Dispose of graphics resources deterministically.

Textures, FrameBuffers and VertexBuffers allocated by the Sdl2 Renderer were only being released via finalizers. This could lead to OpenGL out of memory errors since resources may not be cleaned up in a timely manner. To avoid this, IDisposable has been implemented and transitively applied to classes that use these resources.

As a side-effect some static state is no longer static, particularly in Renderer, in order to facilitate this change and just for nicer design in general.

Also dispose some bitmaps.
This commit is contained in:
RoosterDragon
2014-10-24 18:21:30 +01:00
committed by RoosterDragon
parent 38b579a081
commit f0f02dff5c
31 changed files with 371 additions and 128 deletions

View File

@@ -140,7 +140,10 @@ namespace OpenRA
orderManager.World = new World(map, orderManager, isShellmap);
orderManager.World.Timestep = Timestep;
}
if (worldRenderer != null)
worldRenderer.Dispose();
worldRenderer = new WorldRenderer(orderManager.World);
using (new PerfTimer("LoadComplete"))
orderManager.World.LoadComplete(worldRenderer);
@@ -215,7 +218,7 @@ namespace OpenRA
Settings.Graphics.Renderer = r;
try
{
Renderer.Initialize(Settings.Graphics.Mode);
Renderer = new Renderer(Settings.Graphics, Settings.Server);
break;
}
catch (Exception e)
@@ -225,8 +228,6 @@ namespace OpenRA
}
}
Renderer = new Renderer();
try
{
Sound.Create(Settings.Sound.Engine);
@@ -257,12 +258,18 @@ namespace OpenRA
BeforeGameStart = () => { };
Ui.ResetAll();
if (worldRenderer != null)
worldRenderer.Dispose();
worldRenderer = null;
if (server != null)
server.Shutdown();
if (orderManager != null)
orderManager.Dispose();
if (modData != null)
modData.Dispose();
modData = null;
// Fall back to default if the mod doesn't exist
if (!ModMetadata.AllMods.ContainsKey(mod))
mod = new GameSettings().Mod;
@@ -274,7 +281,7 @@ namespace OpenRA
Sound.StopVideo();
Sound.Initialize();
modData = new ModData(mod);
modData = new ModData(mod, true);
Renderer.InitializeFonts(modData.Manifest);
modData.InitializeLoaders();
using (new PerfTimer("LoadMaps"))
@@ -632,8 +639,12 @@ namespace OpenRA
if (orderManager != null)
orderManager.Dispose();
}
Renderer.Device.Dispose();
if (worldRenderer != null)
worldRenderer.Dispose();
modData.Dispose();
ChromeProvider.Deinitialize();
Renderer.Dispose();
OnQuit();

View File

@@ -17,7 +17,7 @@ using OpenRA.Support;
namespace OpenRA
{
public class RulesetCache
public sealed class RulesetCache : IDisposable
{
readonly ModData modData;
@@ -137,5 +137,12 @@ namespace OpenRA
return items;
}
public void Dispose()
{
foreach (var cache in sequenceCaches.Values)
cache.Dispose();
sequenceCaches.Clear();
}
}
}

View File

@@ -27,6 +27,8 @@ namespace OpenRA.Graphics
public static void Initialize(IEnumerable<string> chromeFiles)
{
Deinitialize();
collections = new Dictionary<string, Collection>();
cachedSheets = new Dictionary<string, Sheet>();
cachedSprites = new Dictionary<string, Dictionary<string, Sprite>>();
@@ -37,6 +39,17 @@ namespace OpenRA.Graphics
LoadCollection(c.Key, c.Value);
}
public static void Deinitialize()
{
if (cachedSheets != null)
foreach (var sheet in cachedSheets.Values)
sheet.Dispose();
collections = null;
cachedSheets = null;
cachedSprites = null;
}
public static void Save(string file)
{
var root = new List<MiniYamlNode>();

View File

@@ -16,11 +16,12 @@ using OpenRA.Primitives;
namespace OpenRA.Graphics
{
public class CursorProvider
public sealed class CursorProvider : IDisposable
{
HardwarePalette palette;
Dictionary<string, CursorSequence> cursors;
Cache<string, PaletteReference> palettes;
readonly SheetBuilder sheetBuilder;
public static bool CursorViewportZoomed { get { return Game.Settings.Graphics.CursorDouble && Game.Settings.Graphics.PixelDouble; } }
@@ -45,10 +46,11 @@ namespace OpenRA.Graphics
foreach (var p in nodesDict["Palettes"].Nodes)
palette.AddPalette(p.Key, new ImmutablePalette(GlobalFileSystem.Open(p.Value.Value), shadowIndex), false);
var spriteCache = new SpriteCache(modData.SpriteLoaders, new string[0], new SheetBuilder(SheetType.Indexed));
sheetBuilder = new SheetBuilder(SheetType.Indexed);
var spriteCache = new SpriteCache(modData.SpriteLoaders, new string[0], sheetBuilder);
foreach (var s in nodesDict["Cursors"].Nodes)
LoadSequencesForCursor(spriteCache, s.Key, s.Value);
spriteCache.SheetBuilder.Current.ReleaseBuffer();
sheetBuilder.Current.ReleaseBuffer();
palette.Initialize();
}
@@ -95,5 +97,11 @@ namespace OpenRA.Graphics
throw new InvalidOperationException("Cursor does not have a sequence `{0}`".F(cursor));
}
}
public void Dispose()
{
palette.Dispose();
sheetBuilder.Dispose();
}
}
}

View File

@@ -14,7 +14,7 @@ using OpenRA.Traits;
namespace OpenRA.Graphics
{
public class HardwarePalette
public sealed class HardwarePalette : IDisposable
{
public const int MaxPalettes = 256;
@@ -111,5 +111,10 @@ namespace OpenRA.Graphics
modifiedPalette.SetFromPalette(originalPalette);
}
}
public void Dispose()
{
Texture.Dispose();
}
}
}

View File

@@ -63,7 +63,7 @@ namespace OpenRA
void ReleaseWindowMouseFocus();
}
public interface IVertexBuffer<T>
public interface IVertexBuffer<T> : IDisposable
{
void Bind();
void SetData(T[] vertices, int length);
@@ -80,7 +80,8 @@ namespace OpenRA
}
public enum TextureScaleFilter { Nearest, Linear }
public interface ITexture
public interface ITexture : IDisposable
{
void SetData(Bitmap bitmap);
void SetData(uint[,] colors);
@@ -90,7 +91,7 @@ namespace OpenRA
TextureScaleFilter ScaleFilter { get; set; }
}
public interface IFrameBuffer
public interface IFrameBuffer : IDisposable
{
void Bind();
void Unbind();

View File

@@ -20,13 +20,14 @@ namespace OpenRA.Graphics
Renderer renderer;
IShader shader;
Vertex[] vertices = new Vertex[Renderer.TempBufferSize];
readonly Vertex[] vertices;
int nv = 0;
public LineRenderer(Renderer renderer, IShader shader)
{
this.renderer = renderer;
this.shader = shader;
vertices = new Vertex[renderer.TempBufferSize];
}
@@ -71,9 +72,9 @@ namespace OpenRA.Graphics
public void DrawLine(float2 start, float2 end, Color startColor, Color endColor)
{
Renderer.CurrentBatchRenderer = this;
renderer.CurrentBatchRenderer = this;
if (nv + 2 > Renderer.TempBufferSize)
if (nv + 2 > renderer.TempBufferSize)
Flush();
vertices[nv++] = new Vertex(start + offset,

View File

@@ -203,8 +203,8 @@ namespace OpenRA.Graphics
public static Bitmap RenderMapPreview(TileSet tileset, Map map, Ruleset resourceRules, bool actualSize)
{
var terrain = TerrainBitmap(tileset, map, actualSize);
return AddStaticResources(tileset, map, resourceRules, terrain);
using (var terrain = TerrainBitmap(tileset, map, actualSize))
return AddStaticResources(tileset, map, resourceRules, terrain);
}
}
}

View File

@@ -17,13 +17,14 @@ namespace OpenRA.Graphics
Renderer renderer;
IShader shader;
Vertex[] vertices = new Vertex[Renderer.TempBufferSize];
readonly Vertex[] vertices;
int nv = 0;
public QuadRenderer(Renderer renderer, IShader shader)
{
this.renderer = renderer;
this.shader = shader;
vertices = new Vertex[renderer.TempBufferSize];
}
public void Flush()
@@ -45,9 +46,9 @@ namespace OpenRA.Graphics
public void FillRect(RectangleF rect, Color color)
{
Renderer.CurrentBatchRenderer = this;
renderer.CurrentBatchRenderer = this;
if (nv + 4 > Renderer.TempBufferSize)
if (nv + 4 > renderer.TempBufferSize)
Flush();
var r = color.R / 255.0f;

View File

@@ -18,11 +18,11 @@ using OpenRA.Support;
namespace OpenRA.Graphics
{
public class Renderer
public sealed class Renderer : IDisposable
{
internal static int SheetSize;
internal static int TempBufferSize;
internal static int TempBufferCount;
internal int SheetSize { get; private set; }
internal int TempBufferSize { get; private set; }
internal int TempBufferCount { get; private set; }
public SpriteRenderer WorldSpriteRenderer { get; private set; }
public SpriteRenderer WorldRgbaSpriteRenderer { get; private set; }
@@ -38,11 +38,13 @@ namespace OpenRA.Graphics
public Dictionary<string, SpriteFont> Fonts;
Stack<Rectangle> scissorState;
public Renderer()
public Renderer(GraphicSettings graphicSettings, ServerSettings serverSettings)
{
TempBufferSize = Game.Settings.Graphics.BatchSize;
TempBufferCount = Game.Settings.Graphics.NumTempBuffers;
SheetSize = Game.Settings.Graphics.SheetSize;
Initialize(graphicSettings, serverSettings);
TempBufferSize = graphicSettings.BatchSize;
TempBufferCount = graphicSettings.NumTempBuffers;
SheetSize = graphicSettings.SheetSize;
scissorState = new Stack<Rectangle>();
WorldSpriteRenderer = new SpriteRenderer(this, device.CreateShader("shp"));
@@ -142,21 +144,21 @@ namespace OpenRA.Graphics
public Size Resolution { get { return device.WindowSize; } }
internal static void Initialize(WindowMode windowMode)
void Initialize(GraphicSettings graphicSettings, ServerSettings serverSettings)
{
var resolution = GetResolution(windowMode);
var resolution = GetResolution(graphicSettings);
var renderer = Game.Settings.Server.Dedicated ? "Null" : Game.Settings.Graphics.Renderer;
var rendererPath = Platform.ResolvePath(".", "OpenRA.Renderer." + renderer + ".dll");
var rendererName = serverSettings.Dedicated ? "Null" : graphicSettings.Renderer;
var rendererPath = Platform.ResolvePath(".", "OpenRA.Renderer." + rendererName + ".dll");
device = CreateDevice(Assembly.LoadFile(rendererPath), resolution.Width, resolution.Height, windowMode);
device = CreateDevice(Assembly.LoadFile(rendererPath), resolution.Width, resolution.Height, graphicSettings.Mode);
}
static Size GetResolution(WindowMode windowmode)
static Size GetResolution(GraphicSettings graphicsSettings)
{
var size = (windowmode == WindowMode.Windowed)
? Game.Settings.Graphics.WindowedSize
: Game.Settings.Graphics.FullscreenSize;
var size = (graphicsSettings.Mode == WindowMode.Windowed)
? graphicsSettings.WindowedSize
: graphicsSettings.FullscreenSize;
return new Size(size.X, size.Y);
}
@@ -180,8 +182,8 @@ namespace OpenRA.Graphics
public interface IBatchRenderer { void Flush(); }
static IBatchRenderer currentBatchRenderer;
public static IBatchRenderer CurrentBatchRenderer
IBatchRenderer currentBatchRenderer;
public IBatchRenderer CurrentBatchRenderer
{
get { return currentBatchRenderer; }
set
@@ -240,5 +242,14 @@ namespace OpenRA.Graphics
{
device.ReleaseWindowMouseFocus();
}
public void Dispose()
{
Device.Dispose();
WorldVoxelRenderer.Dispose();
foreach (var buffer in tempBuffers)
buffer.Dispose();
tempBuffers.Clear();
}
}
}

View File

@@ -69,7 +69,7 @@ namespace OpenRA.Graphics
}
}
public class SequenceCache
public sealed class SequenceCache : IDisposable
{
readonly ModData modData;
readonly Lazy<SpriteCache> spriteCache;
@@ -141,5 +141,11 @@ namespace OpenRA.Graphics
return new ReadOnlyDictionary<string, Sequence>(unitSequences);
}
public void Dispose()
{
if (spriteCache.IsValueCreated)
spriteCache.Value.SheetBuilder.Dispose();
}
}
}

View File

@@ -16,7 +16,7 @@ using OpenRA.FileSystem;
namespace OpenRA.Graphics
{
public class Sheet
public sealed class Sheet : IDisposable
{
readonly object textureLock = new object();
bool dirty;
@@ -178,5 +178,11 @@ namespace OpenRA.Graphics
releaseBufferOnCommit = true;
}
}
public void Dispose()
{
if (texture != null)
texture.Dispose();
}
}
}

View File

@@ -9,7 +9,9 @@
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace OpenRA.Graphics
{
@@ -27,8 +29,9 @@ namespace OpenRA.Graphics
BGRA = 4,
}
public class SheetBuilder
public sealed class SheetBuilder : IDisposable
{
readonly List<Sheet> sheets = new List<Sheet>();
Sheet current;
TextureChannel channel;
SheetType type;
@@ -36,19 +39,23 @@ namespace OpenRA.Graphics
Point p;
Func<Sheet> allocateSheet;
public static Sheet AllocateSheet()
public static Sheet AllocateSheet(int sheetSize)
{
return new Sheet(new Size(Renderer.SheetSize, Renderer.SheetSize));
return new Sheet(new Size(sheetSize, sheetSize));
}
public SheetBuilder(SheetType t)
: this(t, AllocateSheet) { }
: this(t, Game.Renderer.SheetSize) { }
public SheetBuilder(SheetType t, int sheetSize)
: this(t, () => AllocateSheet(sheetSize)) { }
public SheetBuilder(SheetType t, Func<Sheet> allocateSheet)
{
channel = TextureChannel.Red;
type = t;
current = allocateSheet();
sheets.Add(current);
this.allocateSheet = allocateSheet;
}
@@ -111,6 +118,7 @@ namespace OpenRA.Graphics
{
current.ReleaseBuffer();
current = allocateSheet();
sheets.Add(current);
channel = TextureChannel.Red;
}
else
@@ -127,5 +135,12 @@ namespace OpenRA.Graphics
}
public Sheet Current { get { return current; } }
public void Dispose()
{
foreach (var sheet in sheets)
sheet.Dispose();
sheets.Clear();
}
}
}

View File

@@ -17,8 +17,8 @@ namespace OpenRA.Graphics
Renderer renderer;
IShader shader;
Vertex[] vertices = new Vertex[Renderer.TempBufferSize];
Sheet currentSheet = null;
readonly Vertex[] vertices;
Sheet currentSheet;
BlendMode currentBlend = BlendMode.Alpha;
int nv = 0;
@@ -26,6 +26,7 @@ namespace OpenRA.Graphics
{
this.renderer = renderer;
this.shader = shader;
vertices = new Vertex[renderer.TempBufferSize];
}
public void Flush()
@@ -60,7 +61,7 @@ namespace OpenRA.Graphics
void DrawSprite(Sprite s, float2 location, int paletteIndex, float2 size)
{
Renderer.CurrentBatchRenderer = this;
renderer.CurrentBatchRenderer = this;
if (s.sheet != currentSheet)
Flush();
@@ -68,7 +69,7 @@ namespace OpenRA.Graphics
if (s.blendMode != currentBlend)
Flush();
if (nv + 4 > Renderer.TempBufferSize)
if (nv + 4 > renderer.TempBufferSize)
Flush();
currentBlend = s.blendMode;
@@ -90,7 +91,7 @@ namespace OpenRA.Graphics
public void DrawSprite(Sprite s, float2 a, float2 b, float2 c, float2 d)
{
Renderer.CurrentBatchRenderer = this;
renderer.CurrentBatchRenderer = this;
if (s.sheet != currentSheet)
Flush();
@@ -98,7 +99,7 @@ namespace OpenRA.Graphics
if (s.blendMode != currentBlend)
Flush();
if (nv + 4 > Renderer.TempBufferSize)
if (nv + 4 > renderer.TempBufferSize)
Flush();
currentSheet = s.sheet;

View File

@@ -8,11 +8,12 @@
*/
#endregion
using System;
using OpenRA.Traits;
namespace OpenRA.Graphics
{
class TerrainRenderer
sealed class TerrainRenderer : IDisposable
{
IVertexBuffer<Vertex> vertexBuffer;
@@ -58,5 +59,10 @@ namespace OpenRA.Graphics
foreach (var r in world.WorldActor.TraitsImplementing<IRenderOverlay>())
r.Render(wr);
}
public void Dispose()
{
vertexBuffer.Dispose();
}
}
}

View File

@@ -16,7 +16,7 @@ using OpenRA.FileSystem;
namespace OpenRA.Graphics
{
public class Theater
public sealed class Theater : IDisposable
{
SheetBuilder sheetBuilder;
Dictionary<ushort, Sprite[]> templates;
@@ -101,5 +101,10 @@ namespace OpenRA.Graphics
}
public Sheet Sheet { get { return sheetBuilder.Current; } }
public void Dispose()
{
sheetBuilder.Dispose();
}
}
}

View File

@@ -32,7 +32,7 @@ namespace OpenRA.Graphics
}
}
public class VoxelLoader
public sealed class VoxelLoader : IDisposable
{
SheetBuilder sheetBuilder;
@@ -50,7 +50,7 @@ namespace OpenRA.Graphics
if (allocated)
throw new SheetOverflowException("");
allocated = true;
return SheetBuilder.AllocateSheet();
return SheetBuilder.AllocateSheet(Game.Renderer.SheetSize);
};
return new SheetBuilder(SheetType.DualIndexed, allocate);
@@ -195,6 +195,8 @@ namespace OpenRA.Graphics
public void RefreshBuffer()
{
if (vertexBuffer != null)
vertexBuffer.Dispose();
vertexBuffer = Game.Renderer.Device.CreateVertexBuffer(totalVertexCount);
vertexBuffer.SetData(vertices.SelectMany(v => v).ToArray(), totalVertexCount);
cachedVertexCount = totalVertexCount;
@@ -230,5 +232,12 @@ namespace OpenRA.Graphics
{
sheetBuilder.Current.ReleaseBuffer();
}
public void Dispose()
{
if (vertexBuffer != null)
vertexBuffer.Dispose();
sheetBuilder.Dispose();
}
}
}

View File

@@ -32,7 +32,7 @@ namespace OpenRA.Graphics
}
}
public class VoxelRenderer
public sealed class VoxelRenderer : IDisposable
{
Renderer renderer;
IShader shader;
@@ -68,7 +68,7 @@ namespace OpenRA.Graphics
public void SetViewportParams(Size screen, float zoom, int2 scroll)
{
var a = 2f / Renderer.SheetSize;
var a = 2f / renderer.SheetSize;
var view = new float[]
{
a, 0, 0, 0,
@@ -169,8 +169,8 @@ namespace OpenRA.Graphics
var spriteCenter = new float2(sb.Left + sb.Width / 2, sb.Top + sb.Height / 2);
var shadowCenter = new float2(ssb.Left + ssb.Width / 2, ssb.Top + ssb.Height / 2);
var translateMtx = Util.TranslationMatrix(spriteCenter.X - spriteOffset.X, Renderer.SheetSize - (spriteCenter.Y - spriteOffset.Y), 0);
var shadowTranslateMtx = Util.TranslationMatrix(shadowCenter.X - shadowSpriteOffset.X, Renderer.SheetSize - (shadowCenter.Y - shadowSpriteOffset.Y), 0);
var translateMtx = Util.TranslationMatrix(spriteCenter.X - spriteOffset.X, renderer.SheetSize - (spriteCenter.Y - spriteOffset.Y), 0);
var shadowTranslateMtx = Util.TranslationMatrix(shadowCenter.X - shadowSpriteOffset.X, renderer.SheetSize - (shadowCenter.Y - shadowSpriteOffset.Y), 0);
var correctionTransform = Util.MatrixMultiply(translateMtx, flipMtx);
var shadowCorrectionTransform = Util.MatrixMultiply(shadowTranslateMtx, shadowScaleFlipMtx);
@@ -330,12 +330,24 @@ namespace OpenRA.Graphics
return kv.Key;
}
var size = new Size(Renderer.SheetSize, Renderer.SheetSize);
var size = new Size(renderer.SheetSize, renderer.SheetSize);
var framebuffer = renderer.Device.CreateFrameBuffer(size);
var sheet = new Sheet(framebuffer.Texture);
mappedBuffers.Add(sheet, framebuffer);
return sheet;
}
public void Dispose()
{
foreach (var kvp in mappedBuffers.Concat(unmappedBuffers))
{
kvp.Key.Dispose();
kvp.Value.Dispose();
}
mappedBuffers.Clear();
unmappedBuffers.Clear();
}
}
}

View File

@@ -29,7 +29,7 @@ namespace OpenRA.Graphics
}
}
public class WorldRenderer
public sealed class WorldRenderer : IDisposable
{
public readonly World world;
public readonly Theater Theater;
@@ -251,5 +251,12 @@ namespace OpenRA.Graphics
var ts = Game.modData.Manifest.TileSize;
return new WPos(1024 * screenPx.X / ts.Width, 1024 * screenPx.Y / ts.Height, 0);
}
public void Dispose()
{
palette.Dispose();
Theater.Dispose();
terrainRenderer.Dispose();
}
}
}

View File

@@ -21,7 +21,7 @@ using OpenRA.Primitives;
namespace OpenRA
{
public class MapCache : IEnumerable<MapPreview>
public sealed class MapCache : IEnumerable<MapPreview>, IDisposable
{
public static readonly MapPreview UnknownMap = new MapPreview(null, null);
readonly Cache<string, MapPreview> previews;
@@ -163,7 +163,13 @@ namespace OpenRA
foreach (var p in todo)
{
// The rendering is thread safe because it only reads from the passed instances and writes to a new bitmap
var bitmap = p.CustomPreview ?? Minimap.RenderMapPreview(modData.DefaultRules.TileSets[p.Map.Tileset], p.Map, modData.DefaultRules, true);
var createdPreview = false;
var bitmap = p.CustomPreview;
if (bitmap == null)
{
createdPreview = true;
bitmap = Minimap.RenderMapPreview(modData.DefaultRules.TileSets[p.Map.Tileset], p.Map, modData.DefaultRules, true);
}
// Note: this is not generally thread-safe, but it works here because:
// (a) This worker is the only thread writing to this sheet
// (b) The main thread is the only thread reading this sheet
@@ -172,7 +178,15 @@ namespace OpenRA
// the next render cycle.
// (d) Any partially written bytes from the next minimap is in an
// unallocated area, and will be committed in the next cycle.
p.SetMinimap(sheetBuilder.Add(bitmap));
try
{
p.SetMinimap(sheetBuilder.Add(bitmap));
}
finally
{
if (createdPreview)
bitmap.Dispose();
}
// Yuck... But this helps the UI Jank when opening the map selector significantly.
Thread.Sleep(Environment.ProcessorCount == 1 ? 25 : 5);
@@ -226,5 +240,10 @@ namespace OpenRA
{
return GetEnumerator();
}
public void Dispose()
{
sheetBuilder.Dispose();
}
}
}

View File

@@ -18,7 +18,7 @@ using OpenRA.Widgets;
namespace OpenRA
{
public class ModData
public sealed class ModData : IDisposable
{
public readonly Manifest Manifest;
public readonly ObjectCreator ObjectCreator;
@@ -33,14 +33,17 @@ namespace OpenRA
Lazy<Ruleset> defaultRules;
public Ruleset DefaultRules { get { return defaultRules.Value; } }
public ModData(string mod)
public ModData(string mod, bool useLoadScreen = false)
{
Languages = new string[0];
Manifest = new Manifest(mod);
ObjectCreator = new ObjectCreator(Manifest);
LoadScreen = ObjectCreator.CreateObject<ILoadScreen>(Manifest.LoadScreen.Value);
LoadScreen.Init(Manifest, Manifest.LoadScreen.ToDictionary(my => my.Value));
LoadScreen.Display();
if (useLoadScreen)
{
LoadScreen = ObjectCreator.CreateObject<ILoadScreen>(Manifest.LoadScreen.Value);
LoadScreen.Init(Manifest, Manifest.LoadScreen.ToDictionary(my => my.Value));
LoadScreen.Display();
}
WidgetLoader = new WidgetLoader(this);
RulesetCache = new RulesetCache(this);
RulesetCache.LoadingProgress += HandleLoadingProgress;
@@ -82,8 +85,13 @@ namespace OpenRA
// horribly when you use ModData in unexpected ways.
ChromeMetrics.Initialize(Manifest.ChromeMetrics);
ChromeProvider.Initialize(Manifest.Chrome);
if (VoxelLoader != null)
VoxelLoader.Dispose();
VoxelLoader = new VoxelLoader();
if (CursorProvider != null)
CursorProvider.Dispose();
CursorProvider = new CursorProvider(this);
}
@@ -130,7 +138,8 @@ namespace OpenRA
public Map PrepareMap(string uid)
{
LoadScreen.Display();
if (LoadScreen != null)
LoadScreen.Display();
if (MapCache[uid].Status != MapStatus.Available)
throw new InvalidDataException("Invalid map uid: {0}".F(uid));
@@ -157,9 +166,21 @@ namespace OpenRA
return map;
}
public void Dispose()
{
if (LoadScreen != null)
LoadScreen.Dispose();
RulesetCache.Dispose();
MapCache.Dispose();
if (VoxelLoader != null)
VoxelLoader.Dispose();
if (CursorProvider != null)
CursorProvider.Dispose();
}
}
public interface ILoadScreen
public interface ILoadScreen : IDisposable
{
void Init(Manifest m, Dictionary<string, string> info);
void Display();