Files
OpenRA/OpenRA.Game/Graphics/SequenceProvider.cs
RoosterDragon d671e1de01 Create a separate FrameCache for caching sprite frames.
We split the caching SpriteLoader into a SpriteCache and FrameCache. SpriteLoader instead becomes a holder for static loading methods.

Only a few classes loaded sprite frames, and they all use it with a transient cache. By moving this method into a new class, we can lose the now redundant frame cache, saving on memory significantly since the frame data array can be reclaimed by the GC. This saves ~58 MiB on frames and ~4 MiB on the caching dictionary in simple tests.
2014-10-14 22:06:11 +01:00

144 lines
4.0 KiB
C#

#region Copyright & License Information
/*
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace OpenRA.Graphics
{
using Sequences = IReadOnlyDictionary<string, Lazy<IReadOnlyDictionary<string, Sequence>>>;
using UnitSequences = Lazy<IReadOnlyDictionary<string, Sequence>>;
public class SequenceProvider
{
readonly Lazy<Sequences> sequences;
public readonly SpriteCache SpriteCache;
public SequenceProvider(SequenceCache cache, Map map)
{
this.sequences = Exts.Lazy(() => cache.LoadSequences(map));
this.SpriteCache = cache.SpriteCache;
}
public Sequence GetSequence(string unitName, string sequenceName)
{
UnitSequences unitSeq;
if (!sequences.Value.TryGetValue(unitName, out unitSeq))
throw new InvalidOperationException("Unit `{0}` does not have any sequences defined.".F(unitName));
Sequence seq;
if (!unitSeq.Value.TryGetValue(sequenceName, out seq))
throw new InvalidOperationException("Unit `{0}` does not have a sequence named `{1}`".F(unitName, sequenceName));
return seq;
}
public bool HasSequence(string unitName, string sequenceName)
{
UnitSequences unitSeq;
if (!sequences.Value.TryGetValue(unitName, out unitSeq))
throw new InvalidOperationException("Unit `{0}` does not have any sequences defined.".F(unitName));
return unitSeq.Value.ContainsKey(sequenceName);
}
public IEnumerable<string> Sequences(string unitName)
{
UnitSequences unitSeq;
if (!sequences.Value.TryGetValue(unitName, out unitSeq))
throw new InvalidOperationException("Unit `{0}` does not have any sequences defined.".F(unitName));
return unitSeq.Value.Keys;
}
public void Preload()
{
foreach (var unitSeq in sequences.Value.Values)
foreach (var seq in unitSeq.Value.Values) { }
}
}
public class SequenceCache
{
readonly ModData modData;
readonly Lazy<SpriteCache> spriteCache;
public SpriteCache SpriteCache { get { return spriteCache.Value; } }
readonly Dictionary<string, UnitSequences> sequenceCache = new Dictionary<string, UnitSequences>();
public SequenceCache(ModData modData, TileSet tileSet)
{
this.modData = modData;
spriteCache = Exts.Lazy(() => new SpriteCache(modData.SpriteLoaders, tileSet.Extensions, new SheetBuilder(SheetType.Indexed)));
}
public Sequences LoadSequences(Map map)
{
using (new Support.PerfTimer("LoadSequences"))
return Load(map.SequenceDefinitions);
}
Sequences Load(List<MiniYamlNode> sequenceNodes)
{
var sequenceFiles = modData.Manifest.Sequences;
var nodes = sequenceFiles
.Select(s => MiniYaml.FromFile(s))
.Aggregate(sequenceNodes, MiniYaml.MergeLiberal);
var items = new Dictionary<string, UnitSequences>();
foreach (var n in nodes)
{
// Work around the loop closure issue in older versions of C#
var node = n;
var key = node.Value.ToLines(node.Key).JoinWith("|");
UnitSequences t;
if (sequenceCache.TryGetValue(key, out t))
items.Add(node.Key, t);
else
{
t = Exts.Lazy(() => CreateUnitSequences(node));
sequenceCache.Add(key, t);
items.Add(node.Key, t);
}
}
return new ReadOnlyDictionary<string, UnitSequences>(items);
}
IReadOnlyDictionary<string, Sequence> CreateUnitSequences(MiniYamlNode node)
{
var unitSequences = new Dictionary<string, Sequence>();
foreach (var kvp in node.Value.ToDictionary())
{
using (new Support.PerfTimer("new Sequence(\"{0}\")".F(node.Key), 20))
{
try
{
unitSequences.Add(kvp.Key, new Sequence(spriteCache.Value, node.Key, kvp.Key, kvp.Value));
}
catch (FileNotFoundException ex)
{
Log.Write("debug", ex.Message);
}
}
}
return new ReadOnlyDictionary<string, Sequence>(unitSequences);
}
}
}