diff --git a/OpenRA.Editor/Form1.cs b/OpenRA.Editor/Form1.cs index c9834e1c8d..e68aac57e9 100644 --- a/OpenRA.Editor/Form1.cs +++ b/OpenRA.Editor/Form1.cs @@ -46,6 +46,7 @@ namespace OpenRA.Editor miniMapBox.Image = null; currentMod = toolStripComboBox1.SelectedItem as string; + Game.InitializeSettings(Arguments.Empty); Game.modData = new ModData(currentMod); GlobalFileSystem.LoadFromManifest(Game.modData.Manifest); Program.Rules = Game.modData.RulesetCache.LoadDefaultRules(); @@ -203,10 +204,10 @@ namespace OpenRA.Editor Height = bitmap.Height / 2, SizeMode = PictureBoxSizeMode.StretchImage }; - + var brushTemplate = new BrushTemplate { Bitmap = bitmap, N = t.Key }; ibox.Click += (_, e) => surface1.SetTool(new BrushTool(brushTemplate)); - + var template = t.Value; tilePalette.Controls.Add(ibox); tt.SetToolTip(ibox, "{1}:{0} ({2}x{3})".F(template.Image, template.Id, template.Size.X, template.Size.Y)); @@ -463,7 +464,7 @@ namespace OpenRA.Editor void ExportMinimap(object sender, EventArgs e) { using (var sfd = new SaveFileDialog() - { + { InitialDirectory = Path.Combine(Environment.CurrentDirectory, "maps"), DefaultExt = "*.png", Filter = "PNG Image (*.png)|*.png", @@ -471,9 +472,8 @@ namespace OpenRA.Editor FileName = Path.ChangeExtension(loadedMapName, ".png"), RestoreDirectory = true }) - - if (DialogResult.OK == sfd.ShowDialog()) - miniMapBox.Image.Save(sfd.FileName); + if (DialogResult.OK == sfd.ShowDialog()) + miniMapBox.Image.Save(sfd.FileName); } void ShowActorNamesClicked(object sender, EventArgs e) @@ -637,7 +637,7 @@ namespace OpenRA.Editor { ShowGridClicked(sender, e); } - + public int CalculateTotalResource() { var totalResource = 0; @@ -651,7 +651,7 @@ namespace OpenRA.Editor return totalResource; } - + int GetAdjecentCellsWith(int resourceType, int x, int y) { var sum = 0; diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 54925f95c4..1fbb6d8683 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -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); @@ -172,13 +175,18 @@ namespace OpenRA public static Modifiers GetModifierKeys() { return modifiers; } internal static void HandleModifierKeys(Modifiers mods) { modifiers = mods; } + public static void InitializeSettings(Arguments args) + { + Settings = new Settings(Platform.ResolvePath("^", "settings.yaml"), args); + } + internal static void Initialize(Arguments args) { Console.WriteLine("Platform is {0}", Platform.CurrentPlatform); AppDomain.CurrentDomain.AssemblyResolve += GlobalFileSystem.ResolveAssembly; - Settings = new Settings(Platform.ResolvePath("^", "settings.yaml"), args); + InitializeSettings(args); Log.AddChannel("perf", "perf.log"); Log.AddChannel("debug", "debug.log"); @@ -215,7 +223,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 +233,6 @@ namespace OpenRA } } - Renderer = new Renderer(); - try { Sound.Create(Settings.Sound.Engine); @@ -257,12 +263,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 +286,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 +644,12 @@ namespace OpenRA if (orderManager != null) orderManager.Dispose(); } - - Renderer.Device.Dispose(); + + if (worldRenderer != null) + worldRenderer.Dispose(); + modData.Dispose(); + ChromeProvider.Deinitialize(); + Renderer.Dispose(); OnQuit(); diff --git a/OpenRA.Game/GameRules/RulesetCache.cs b/OpenRA.Game/GameRules/RulesetCache.cs index 8dccd262b3..0ceecde102 100755 --- a/OpenRA.Game/GameRules/RulesetCache.cs +++ b/OpenRA.Game/GameRules/RulesetCache.cs @@ -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(); + } } } diff --git a/OpenRA.Game/Graphics/ChromeProvider.cs b/OpenRA.Game/Graphics/ChromeProvider.cs index 0d6fbf66db..2031d55099 100644 --- a/OpenRA.Game/Graphics/ChromeProvider.cs +++ b/OpenRA.Game/Graphics/ChromeProvider.cs @@ -17,8 +17,8 @@ namespace OpenRA.Graphics { struct Collection { - public string src; - public Dictionary regions; + public string Src; + public Dictionary Regions; } static Dictionary collections; @@ -27,6 +27,8 @@ namespace OpenRA.Graphics public static void Initialize(IEnumerable chromeFiles) { + Deinitialize(); + collections = new Dictionary(); cachedSheets = new Dictionary(); cachedSprites = new Dictionary>(); @@ -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(); @@ -49,10 +62,10 @@ namespace OpenRA.Graphics static MiniYaml SaveCollection(Collection collection) { var root = new List(); - foreach (var kv in collection.regions) - root.Add(new MiniYamlNode(kv.Key, kv.Value.Save(collection.src))); + foreach (var kv in collection.Regions) + root.Add(new MiniYamlNode(kv.Key, kv.Value.Save(collection.Src))); - return new MiniYaml(collection.src, root); + return new MiniYaml(collection.Src, root); } static void LoadCollection(string name, MiniYaml yaml) @@ -60,8 +73,8 @@ namespace OpenRA.Graphics Game.modData.LoadScreen.Display(); var collection = new Collection() { - src = yaml.Value, - regions = yaml.Nodes.ToDictionary(n => n.Key, n => new MappedImage(yaml.Value, n.Value)) + Src = yaml.Value, + Regions = yaml.Nodes.ToDictionary(n => n.Key, n => new MappedImage(yaml.Value, n.Value)) }; collections.Add(name, collection); @@ -83,7 +96,7 @@ namespace OpenRA.Graphics } MappedImage mi; - if (!collection.regions.TryGetValue(imageName, out mi)) + if (!collection.Regions.TryGetValue(imageName, out mi)) return null; // Cached sheet @@ -102,6 +115,7 @@ namespace OpenRA.Graphics cachedCollection = new Dictionary(); cachedSprites.Add(collectionName, cachedCollection); } + var image = mi.GetImage(sheet); cachedCollection.Add(imageName, image); diff --git a/OpenRA.Game/Graphics/CursorProvider.cs b/OpenRA.Game/Graphics/CursorProvider.cs index 7dd832e4e1..1f1d8bdb79 100644 --- a/OpenRA.Game/Graphics/CursorProvider.cs +++ b/OpenRA.Game/Graphics/CursorProvider.cs @@ -16,11 +16,12 @@ using OpenRA.Primitives; namespace OpenRA.Graphics { - public class CursorProvider + public sealed class CursorProvider : IDisposable { - HardwarePalette palette; - Dictionary cursors; - Cache palettes; + readonly HardwarePalette palette = new HardwarePalette(); + readonly Dictionary cursors = new Dictionary(); + readonly Cache palettes; + readonly SheetBuilder sheetBuilder; public static bool CursorViewportZoomed { get { return Game.Settings.Graphics.CursorDouble && Game.Settings.Graphics.PixelDouble; } } @@ -28,7 +29,6 @@ namespace OpenRA.Graphics { var sequenceFiles = modData.Manifest.Cursors; - cursors = new Dictionary(); palettes = new Cache(CreatePaletteReference); var sequences = new MiniYaml(null, sequenceFiles.Select(s => MiniYaml.FromFile(s)).Aggregate(MiniYaml.MergeLiberal)); var shadowIndex = new int[] { }; @@ -41,14 +41,14 @@ namespace OpenRA.Graphics out shadowIndex[shadowIndex.Length - 1]); } - palette = new HardwarePalette(); 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 +95,11 @@ namespace OpenRA.Graphics throw new InvalidOperationException("Cursor does not have a sequence `{0}`".F(cursor)); } } + + public void Dispose() + { + palette.Dispose(); + sheetBuilder.Dispose(); + } } } diff --git a/OpenRA.Game/Graphics/HardwarePalette.cs b/OpenRA.Game/Graphics/HardwarePalette.cs index 4695d62d04..df10a17b24 100644 --- a/OpenRA.Game/Graphics/HardwarePalette.cs +++ b/OpenRA.Game/Graphics/HardwarePalette.cs @@ -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(); + } } } diff --git a/OpenRA.Game/Graphics/IGraphicsDevice.cs b/OpenRA.Game/Graphics/IGraphicsDevice.cs index 07b78c5085..c3ec766649 100755 --- a/OpenRA.Game/Graphics/IGraphicsDevice.cs +++ b/OpenRA.Game/Graphics/IGraphicsDevice.cs @@ -63,7 +63,7 @@ namespace OpenRA void ReleaseWindowMouseFocus(); } - public interface IVertexBuffer + public interface IVertexBuffer : 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(); diff --git a/OpenRA.Game/Graphics/LineRenderer.cs b/OpenRA.Game/Graphics/LineRenderer.cs index 4c7f240942..f7c3ff79ec 100644 --- a/OpenRA.Game/Graphics/LineRenderer.cs +++ b/OpenRA.Game/Graphics/LineRenderer.cs @@ -15,24 +15,29 @@ namespace OpenRA.Graphics { public class LineRenderer : Renderer.IBatchRenderer { - static float2 offset = new float2(0.5f, 0.5f); - float lineWidth = 1f; - Renderer renderer; - IShader shader; + static readonly float2 Offset = new float2(0.5f, 0.5f); - Vertex[] vertices = new Vertex[Renderer.TempBufferSize]; + readonly Renderer renderer; + readonly IShader shader; + + readonly Vertex[] vertices; int nv = 0; + float lineWidth = 1f; + public LineRenderer(Renderer renderer, IShader shader) { this.renderer = renderer; this.shader = shader; + vertices = new Vertex[renderer.TempBufferSize]; } - public float LineWidth { - get { return lineWidth; } + get + { + return lineWidth; + } set { if (LineWidth != value) @@ -71,16 +76,16 @@ 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, + vertices[nv++] = new Vertex(start + Offset, startColor.R / 255.0f, startColor.G / 255.0f, startColor.B / 255.0f, startColor.A / 255.0f); - vertices[nv++] = new Vertex(end + offset, + vertices[nv++] = new Vertex(end + Offset, endColor.R / 255.0f, endColor.G / 255.0f, endColor.B / 255.0f, endColor.A / 255.0f); } @@ -99,7 +104,7 @@ namespace OpenRA.Graphics var yc = (r.Bottom + r.Top) / 2; for (var y = r.Top; y <= r.Bottom; y++) { - var dx = a * (float)(Math.Sqrt(1 - (y - yc) * (y - yc) / b / b)); + var dx = a * (float)Math.Sqrt(1 - (y - yc) * (y - yc) / b / b); DrawLine(new float2(xc - dx, y), new float2(xc + dx, y), color, color); } } @@ -107,7 +112,7 @@ namespace OpenRA.Graphics public void SetViewportParams(Size screen, float zoom, int2 scroll) { shader.SetVec("Scroll", scroll.X, scroll.Y); - shader.SetVec("r1", zoom*2f/screen.Width, -zoom*2f/screen.Height); + shader.SetVec("r1", zoom * 2f / screen.Width, -zoom * 2f / screen.Height); shader.SetVec("r2", -1, 1); } } diff --git a/OpenRA.Game/Graphics/Minimap.cs b/OpenRA.Game/Graphics/Minimap.cs index 62fdcdaacd..d87eef5a2e 100644 --- a/OpenRA.Game/Graphics/Minimap.cs +++ b/OpenRA.Game/Graphics/Minimap.cs @@ -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); } } } diff --git a/OpenRA.Game/Graphics/QuadRenderer.cs b/OpenRA.Game/Graphics/QuadRenderer.cs index 21a48d3dc0..c3f562a4f7 100644 --- a/OpenRA.Game/Graphics/QuadRenderer.cs +++ b/OpenRA.Game/Graphics/QuadRenderer.cs @@ -14,16 +14,17 @@ namespace OpenRA.Graphics { public class QuadRenderer : Renderer.IBatchRenderer { - Renderer renderer; - IShader shader; + readonly Renderer renderer; + readonly 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; @@ -65,7 +66,7 @@ namespace OpenRA.Graphics public void SetViewportParams(Size screen, float zoom, int2 scroll) { shader.SetVec("Scroll", scroll.X, scroll.Y); - shader.SetVec("r1", zoom*2f/screen.Width, -zoom*2f/screen.Height); + shader.SetVec("r1", zoom * 2f / screen.Width, -zoom * 2f / screen.Height); shader.SetVec("r2", -1, 1); } } diff --git a/OpenRA.Game/Graphics/Renderer.cs b/OpenRA.Game/Graphics/Renderer.cs index 8a99aef2fb..c99cd38d94 100644 --- a/OpenRA.Game/Graphics/Renderer.cs +++ b/OpenRA.Game/Graphics/Renderer.cs @@ -11,19 +11,14 @@ using System; using System.Collections.Generic; using System.Drawing; -using System.IO; using System.Linq; using System.Reflection; 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; - public SpriteRenderer WorldSpriteRenderer { get; private set; } public SpriteRenderer WorldRgbaSpriteRenderer { get; private set; } public QuadRenderer WorldQuadRenderer { get; private set; } @@ -32,47 +27,77 @@ namespace OpenRA.Graphics public LineRenderer LineRenderer { get; private set; } public SpriteRenderer RgbaSpriteRenderer { get; private set; } public SpriteRenderer SpriteRenderer { get; private set; } + public IReadOnlyDictionary Fonts; - Queue> tempBuffers = new Queue>(); + internal IGraphicsDevice Device { get; private set; } + internal int SheetSize { get; private set; } + internal int TempBufferSize { get; private set; } + internal int TempBufferCount { get; private set; } - public Dictionary Fonts; - Stack scissorState; + readonly Queue> tempBuffers = new Queue>(); + readonly Stack scissorState = new Stack(); - public Renderer() + Size? lastResolution; + int2? lastScroll; + float? lastZoom; + ITexture currentPaletteTexture; + IBatchRenderer currentBatchRenderer; + + public Renderer(GraphicSettings graphicSettings, ServerSettings serverSettings) { - TempBufferSize = Game.Settings.Graphics.BatchSize; - TempBufferCount = Game.Settings.Graphics.NumTempBuffers; - SheetSize = Game.Settings.Graphics.SheetSize; - scissorState = new Stack(); + var resolution = GetResolution(graphicSettings); - WorldSpriteRenderer = new SpriteRenderer(this, device.CreateShader("shp")); - WorldRgbaSpriteRenderer = new SpriteRenderer(this, device.CreateShader("rgba")); - WorldLineRenderer = new LineRenderer(this, device.CreateShader("line")); - WorldVoxelRenderer = new VoxelRenderer(this, device.CreateShader("vxl")); - LineRenderer = new LineRenderer(this, device.CreateShader("line")); - WorldQuadRenderer = new QuadRenderer(this, device.CreateShader("line")); - RgbaSpriteRenderer = new SpriteRenderer(this, device.CreateShader("rgba")); - SpriteRenderer = new SpriteRenderer(this, device.CreateShader("shp")); + var rendererName = serverSettings.Dedicated ? "Null" : graphicSettings.Renderer; + var rendererPath = Platform.ResolvePath(".", "OpenRA.Renderer." + rendererName + ".dll"); + + Device = CreateDevice(Assembly.LoadFile(rendererPath), resolution.Width, resolution.Height, graphicSettings.Mode); + + TempBufferSize = graphicSettings.BatchSize; + TempBufferCount = graphicSettings.NumTempBuffers; + SheetSize = graphicSettings.SheetSize; + + WorldSpriteRenderer = new SpriteRenderer(this, Device.CreateShader("shp")); + WorldRgbaSpriteRenderer = new SpriteRenderer(this, Device.CreateShader("rgba")); + WorldLineRenderer = new LineRenderer(this, Device.CreateShader("line")); + WorldVoxelRenderer = new VoxelRenderer(this, Device.CreateShader("vxl")); + LineRenderer = new LineRenderer(this, Device.CreateShader("line")); + WorldQuadRenderer = new QuadRenderer(this, Device.CreateShader("line")); + RgbaSpriteRenderer = new SpriteRenderer(this, Device.CreateShader("rgba")); + SpriteRenderer = new SpriteRenderer(this, Device.CreateShader("shp")); for (var i = 0; i < TempBufferCount; i++) - tempBuffers.Enqueue(device.CreateVertexBuffer(TempBufferSize)); + tempBuffers.Enqueue(Device.CreateVertexBuffer(TempBufferSize)); + } + + static Size GetResolution(GraphicSettings graphicsSettings) + { + var size = (graphicsSettings.Mode == WindowMode.Windowed) + ? graphicsSettings.WindowedSize + : graphicsSettings.FullscreenSize; + return new Size(size.X, size.Y); + } + + static IGraphicsDevice CreateDevice(Assembly rendererDll, int width, int height, WindowMode window) + { + foreach (RendererAttribute r in rendererDll.GetCustomAttributes(typeof(RendererAttribute), false)) + { + var factory = (IDeviceFactory)r.Type.GetConstructor(Type.EmptyTypes).Invoke(null); + return factory.Create(new Size(width, height), window); + } + + throw new InvalidOperationException("Renderer DLL is missing RendererAttribute to tell us what type to use!"); } public void InitializeFonts(Manifest m) { using (new Support.PerfTimer("SpriteFonts")) - Fonts = m.Fonts.ToDictionary(x => x.Key, x => new SpriteFont(Platform.ResolvePath(x.Value.First), x.Value.Second)); + Fonts = m.Fonts.ToDictionary(x => x.Key, + x => new SpriteFont(Platform.ResolvePath(x.Value.First), x.Value.Second)).AsReadOnly(); } - internal IGraphicsDevice Device { get { return device; } } - - Size? lastResolution; - int2? lastScroll; - float? lastZoom; - public void BeginFrame(int2 scroll, float zoom) { - device.Clear(); + Device.Clear(); var resolutionChanged = lastResolution != Resolution; if (resolutionChanged) @@ -96,7 +121,6 @@ namespace OpenRA.Graphics } } - ITexture currentPaletteTexture; public void SetPalette(HardwarePalette palette) { if (palette.Texture == currentPaletteTexture) @@ -115,8 +139,8 @@ namespace OpenRA.Graphics public void EndFrame(IInputHandler inputHandler) { Flush(); - device.PumpInput(inputHandler); - device.Present(); + Device.PumpInput(inputHandler); + Device.Present(); } public void DrawBatch(IVertexBuffer vertices, @@ -124,7 +148,7 @@ namespace OpenRA.Graphics where T : struct { vertices.Bind(); - device.DrawPrimitives(type, firstVertex, numVertices); + Device.DrawPrimitives(type, firstVertex, numVertices); PerfHistory.Increment("batches", 1); } @@ -135,58 +159,28 @@ namespace OpenRA.Graphics public void SetLineWidth(float width) { - device.SetLineWidth(width); + Device.SetLineWidth(width); } - static IGraphicsDevice device; - - public Size Resolution { get { return device.WindowSize; } } - - internal static void Initialize(WindowMode windowMode) - { - var resolution = GetResolution(windowMode); - - var renderer = Game.Settings.Server.Dedicated ? "Null" : Game.Settings.Graphics.Renderer; - var rendererPath = Platform.ResolvePath(".", "OpenRA.Renderer." + renderer + ".dll"); - - device = CreateDevice(Assembly.LoadFile(rendererPath), resolution.Width, resolution.Height, windowMode); - } - - static Size GetResolution(WindowMode windowmode) - { - var size = (windowmode == WindowMode.Windowed) - ? Game.Settings.Graphics.WindowedSize - : Game.Settings.Graphics.FullscreenSize; - return new Size(size.X, size.Y); - } - - static IGraphicsDevice CreateDevice(Assembly rendererDll, int width, int height, WindowMode window) - { - foreach (RendererAttribute r in rendererDll.GetCustomAttributes(typeof(RendererAttribute), false)) - { - var factory = (IDeviceFactory)r.Type.GetConstructor(Type.EmptyTypes).Invoke(null); - return factory.Create(new Size(width, height), window); - } - - throw new InvalidOperationException("Renderer DLL is missing RendererAttribute to tell us what type to use!"); - } + public Size Resolution { get { return Device.WindowSize; } } internal IVertexBuffer GetTempVertexBuffer() { - var ret = tempBuffers.Dequeue(); - tempBuffers.Enqueue(ret); - return ret; + return tempBuffers.Peek(); } - public interface IBatchRenderer { void Flush(); } + public interface IBatchRenderer { void Flush(); } - static IBatchRenderer currentBatchRenderer; - public static IBatchRenderer CurrentBatchRenderer + public IBatchRenderer CurrentBatchRenderer { - get { return currentBatchRenderer; } + get + { + return currentBatchRenderer; + } set { - if (currentBatchRenderer == value) return; + if (currentBatchRenderer == value) + return; if (currentBatchRenderer != null) currentBatchRenderer.Flush(); currentBatchRenderer = value; @@ -233,12 +227,21 @@ namespace OpenRA.Graphics public void GrabWindowMouseFocus() { - device.GrabWindowMouseFocus(); + Device.GrabWindowMouseFocus(); } public void ReleaseWindowMouseFocus() { - device.ReleaseWindowMouseFocus(); + Device.ReleaseWindowMouseFocus(); + } + + public void Dispose() + { + Device.Dispose(); + WorldVoxelRenderer.Dispose(); + foreach (var buffer in tempBuffers) + buffer.Dispose(); + tempBuffers.Clear(); } } } diff --git a/OpenRA.Game/Graphics/SequenceProvider.cs b/OpenRA.Game/Graphics/SequenceProvider.cs index dd250c5fa3..b89559ab52 100644 --- a/OpenRA.Game/Graphics/SequenceProvider.cs +++ b/OpenRA.Game/Graphics/SequenceProvider.cs @@ -69,7 +69,7 @@ namespace OpenRA.Graphics } } - public class SequenceCache + public sealed class SequenceCache : IDisposable { readonly ModData modData; readonly Lazy spriteCache; @@ -141,5 +141,11 @@ namespace OpenRA.Graphics return new ReadOnlyDictionary(unitSequences); } + + public void Dispose() + { + if (spriteCache.IsValueCreated) + spriteCache.Value.SheetBuilder.Dispose(); + } } } diff --git a/OpenRA.Game/Graphics/Sheet.cs b/OpenRA.Game/Graphics/Sheet.cs index 6a69824bc1..a04db289c7 100644 --- a/OpenRA.Game/Graphics/Sheet.cs +++ b/OpenRA.Game/Graphics/Sheet.cs @@ -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(); + } } } diff --git a/OpenRA.Game/Graphics/SheetBuilder.cs b/OpenRA.Game/Graphics/SheetBuilder.cs index eb1049f65f..b79eb2d0cc 100644 --- a/OpenRA.Game/Graphics/SheetBuilder.cs +++ b/OpenRA.Game/Graphics/SheetBuilder.cs @@ -9,7 +9,9 @@ #endregion using System; +using System.Collections.Generic; using System.Drawing; +using System.Linq; namespace OpenRA.Graphics { @@ -27,28 +29,34 @@ namespace OpenRA.Graphics BGRA = 4, } - public class SheetBuilder + public sealed class SheetBuilder : IDisposable { + readonly List sheets = new List(); + readonly SheetType type; + readonly Func allocateSheet; + Sheet current; TextureChannel channel; - SheetType type; int rowHeight = 0; Point p; - Func 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.Settings.Graphics.SheetSize) { } + + public SheetBuilder(SheetType t, int sheetSize) + : this(t, () => AllocateSheet(sheetSize)) { } public SheetBuilder(SheetType t, Func allocateSheet) { channel = TextureChannel.Red; type = t; current = allocateSheet(); + sheets.Add(current); this.allocateSheet = allocateSheet; } @@ -111,6 +119,7 @@ namespace OpenRA.Graphics { current.ReleaseBuffer(); current = allocateSheet(); + sheets.Add(current); channel = TextureChannel.Red; } else @@ -127,5 +136,12 @@ namespace OpenRA.Graphics } public Sheet Current { get { return current; } } + + public void Dispose() + { + foreach (var sheet in sheets) + sheet.Dispose(); + sheets.Clear(); + } } } diff --git a/OpenRA.Game/Graphics/SpriteFont.cs b/OpenRA.Game/Graphics/SpriteFont.cs index 562b302855..cf477d66a3 100644 --- a/OpenRA.Game/Graphics/SpriteFont.cs +++ b/OpenRA.Game/Graphics/SpriteFont.cs @@ -22,7 +22,7 @@ namespace OpenRA.Graphics static Library library = new Library(); static SheetBuilder builder; - int size; + readonly int size; string name; public SpriteFont(string name, int size) @@ -114,7 +114,6 @@ namespace OpenRA.Graphics // A new bitmap is generated each time this property is accessed, so we do need to dispose it. using (var bitmap = face.Glyph.Bitmap) - { unsafe { var p = (byte*)bitmap.Buffer; @@ -136,7 +135,7 @@ namespace OpenRA.Graphics p += bitmap.Pitch; } } - } + s.sheet.CommitData(); diff --git a/OpenRA.Game/Graphics/SpriteRenderer.cs b/OpenRA.Game/Graphics/SpriteRenderer.cs index 0ac6b2e82e..10a0ce6b9b 100644 --- a/OpenRA.Game/Graphics/SpriteRenderer.cs +++ b/OpenRA.Game/Graphics/SpriteRenderer.cs @@ -14,11 +14,11 @@ namespace OpenRA.Graphics { public class SpriteRenderer : Renderer.IBatchRenderer { - Renderer renderer; - IShader shader; + readonly Renderer renderer; + readonly 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; @@ -123,7 +124,7 @@ namespace OpenRA.Graphics public void SetViewportParams(Size screen, float zoom, int2 scroll) { shader.SetVec("Scroll", scroll.X, scroll.Y); - shader.SetVec("r1", zoom*2f/screen.Width, -zoom*2f/screen.Height); + shader.SetVec("r1", zoom * 2f / screen.Width, -zoom * 2f / screen.Height); shader.SetVec("r2", -1, 1); } } diff --git a/OpenRA.Game/Graphics/TerrainRenderer.cs b/OpenRA.Game/Graphics/TerrainRenderer.cs index 4aeb3519db..dffe1bf2c3 100644 --- a/OpenRA.Game/Graphics/TerrainRenderer.cs +++ b/OpenRA.Game/Graphics/TerrainRenderer.cs @@ -8,16 +8,16 @@ */ #endregion +using System; using OpenRA.Traits; namespace OpenRA.Graphics { - class TerrainRenderer + sealed class TerrainRenderer : IDisposable { - IVertexBuffer vertexBuffer; - - World world; - Map map; + readonly IVertexBuffer vertexBuffer; + readonly World world; + readonly Map map; public TerrainRenderer(World world, WorldRenderer wr) { @@ -42,7 +42,7 @@ namespace OpenRA.Graphics public void Draw(WorldRenderer wr, Viewport viewport) { - var verticesPerRow = 4*map.Bounds.Width; + var verticesPerRow = 4 * map.Bounds.Width; var cells = viewport.VisibleCells; var shape = wr.world.Map.TileShape; @@ -58,5 +58,10 @@ namespace OpenRA.Graphics foreach (var r in world.WorldActor.TraitsImplementing()) r.Render(wr); } + + public void Dispose() + { + vertexBuffer.Dispose(); + } } } diff --git a/OpenRA.Game/Graphics/Theater.cs b/OpenRA.Game/Graphics/Theater.cs index f3e20a95cb..3972a6f564 100644 --- a/OpenRA.Game/Graphics/Theater.cs +++ b/OpenRA.Game/Graphics/Theater.cs @@ -16,11 +16,11 @@ using OpenRA.FileSystem; namespace OpenRA.Graphics { - public class Theater + public sealed class Theater : IDisposable { - SheetBuilder sheetBuilder; - Dictionary templates; - Sprite missingTile; + readonly Dictionary templates = new Dictionary(); + readonly SheetBuilder sheetBuilder; + readonly Sprite missingTile; TileSet tileset; public Theater(TileSet tileset) @@ -101,5 +101,10 @@ namespace OpenRA.Graphics } public Sheet Sheet { get { return sheetBuilder.Current; } } + + public void Dispose() + { + sheetBuilder.Dispose(); + } } } diff --git a/OpenRA.Game/Graphics/VoxelLoader.cs b/OpenRA.Game/Graphics/VoxelLoader.cs index 2fc2d2fdf8..506a5070da 100644 --- a/OpenRA.Game/Graphics/VoxelLoader.cs +++ b/OpenRA.Game/Graphics/VoxelLoader.cs @@ -32,16 +32,18 @@ namespace OpenRA.Graphics } } - public class VoxelLoader + public sealed class VoxelLoader : IDisposable { - SheetBuilder sheetBuilder; + static readonly float[] ChannelSelect = { 0.75f, 0.25f, -0.25f, -0.75f }; - Cache, Voxel> voxels; + readonly List vertices = new List(); + readonly Cache, Voxel> voxels; IVertexBuffer vertexBuffer; - List vertices; int totalVertexCount; int cachedVertexCount; + SheetBuilder sheetBuilder; + static SheetBuilder CreateSheetBuilder() { var allocated = false; @@ -50,7 +52,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); @@ -58,7 +60,7 @@ namespace OpenRA.Graphics public VoxelLoader() { - voxels = new Cache, Voxel>(LoadFile); + voxels = new Cache, Voxel>(LoadFile); vertices = new List(); totalVertexCount = 0; cachedVertexCount = 0; @@ -66,29 +68,28 @@ namespace OpenRA.Graphics sheetBuilder = CreateSheetBuilder(); } - static float[] channelSelect = { 0.75f, 0.25f, -0.25f, -0.75f }; - Vertex[] GenerateSlicePlane(int su, int sv, Func first, Func second, Func coord) + Vertex[] GenerateSlicePlane(int su, int sv, Func first, Func second, Func coord) { - var colors = new byte[su*sv]; - var normals = new byte[su*sv]; + var colors = new byte[su * sv]; + var normals = new byte[su * sv]; var c = 0; for (var v = 0; v < sv; v++) for (var u = 0; u < su; u++) - { - var voxel = first(u,v) ?? second(u,v); - colors[c] = voxel == null ? (byte)0 : voxel.Color; - normals[c] = voxel == null ? (byte)0 : voxel.Normal; - c++; - } + { + var voxel = first(u, v) ?? second(u, v); + colors[c] = voxel == null ? (byte)0 : voxel.Color; + normals[c] = voxel == null ? (byte)0 : voxel.Normal; + c++; + } var s = sheetBuilder.Allocate(new Size(su, sv)); Util.FastCopyIntoChannel(s, 0, colors); Util.FastCopyIntoChannel(s, 1, normals); s.sheet.CommitData(); - var channelP =channelSelect[(int)s.channel]; - var channelC = channelSelect[(int)s.channel + 1]; + var channelP = ChannelSelect[(int)s.channel]; + var channelC = ChannelSelect[(int)s.channel + 1]; return new Vertex[4] { new Vertex(coord(0, 0), s.left, s.top, channelP, channelC), @@ -100,7 +101,7 @@ namespace OpenRA.Graphics IEnumerable GenerateSlicePlanes(VxlLimb l) { - Func get = (x,y,z) => + Func get = (x, y, z) => { if (x < 0 || y < 0 || z < 0) return null; @@ -108,41 +109,41 @@ namespace OpenRA.Graphics if (x >= l.Size[0] || y >= l.Size[1] || z >= l.Size[2]) return null; - var v = l.VoxelMap[(byte)x,(byte)y]; + var v = l.VoxelMap[(byte)x, (byte)y]; if (v == null || !v.ContainsKey((byte)z)) return null; - return l.VoxelMap[(byte)x,(byte)y][(byte)z]; + return l.VoxelMap[(byte)x, (byte)y][(byte)z]; }; // Cull slices without any visible faces - var xPlanes = new bool[l.Size[0]+1]; - var yPlanes = new bool[l.Size[1]+1]; - var zPlanes = new bool[l.Size[2]+1]; + var xPlanes = new bool[l.Size[0] + 1]; + var yPlanes = new bool[l.Size[1] + 1]; + var zPlanes = new bool[l.Size[2] + 1]; for (var x = 0; x < l.Size[0]; x++) { for (var y = 0; y < l.Size[1]; y++) { for (var z = 0; z < l.Size[2]; z++) { - if (get(x,y,z) == null) + if (get(x, y, z) == null) continue; // Only generate a plane if it is actually visible - if (!xPlanes[x] && get(x-1,y,z) == null) + if (!xPlanes[x] && get(x - 1, y, z) == null) xPlanes[x] = true; - if (!xPlanes[x+1] && get(x+1,y,z) == null) - xPlanes[x+1] = true; + if (!xPlanes[x + 1] && get(x + 1, y, z) == null) + xPlanes[x + 1] = true; - if (!yPlanes[y] && get(x,y-1,z) == null) + if (!yPlanes[y] && get(x, y - 1, z) == null) yPlanes[y] = true; - if (!yPlanes[y+1] && get(x,y+1,z) == null) - yPlanes[y+1] = true; + if (!yPlanes[y + 1] && get(x, y + 1, z) == null) + yPlanes[y + 1] = true; - if (!zPlanes[z] && get(x,y,z-1) == null) + if (!zPlanes[z] && get(x, y, z - 1) == null) zPlanes[z] = true; - if (!zPlanes[z+1] && get(x,y,z+1) == null) - zPlanes[z+1] = true; + if (!zPlanes[z + 1] && get(x, y, z + 1) == null) + zPlanes[z + 1] = true; } } } @@ -150,23 +151,23 @@ namespace OpenRA.Graphics for (var x = 0; x <= l.Size[0]; x++) if (xPlanes[x]) yield return GenerateSlicePlane(l.Size[1], l.Size[2], - (u,v) => get(x, u, v), - (u,v) => get(x - 1, u, v), - (u,v) => new float[] {x, u, v}); + (u, v) => get(x, u, v), + (u, v) => get(x - 1, u, v), + (u, v) => new float[] { x, u, v }); for (var y = 0; y <= l.Size[1]; y++) if (yPlanes[y]) yield return GenerateSlicePlane(l.Size[0], l.Size[2], - (u,v) => get(u, y, v), - (u,v) => get(u, y - 1, v), - (u,v) => new float[] {u, y, v}); + (u, v) => get(u, y, v), + (u, v) => get(u, y - 1, v), + (u, v) => new float[] { u, y, v }); for (var z = 0; z <= l.Size[2]; z++) if (zPlanes[z]) yield return GenerateSlicePlane(l.Size[0], l.Size[1], - (u,v) => get(u, v, z), - (u,v) => get(u, v, z - 1), - (u,v) => new float[] {u, v, z}); + (u, v) => get(u, v, z), + (u, v) => get(u, v, z - 1), + (u, v) => new float[] { u, v, z }); } public VoxelRenderData GenerateRenderData(VxlLimb l) @@ -195,6 +196,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; @@ -210,7 +213,7 @@ namespace OpenRA.Graphics } } - Voxel LoadFile(Pair files) + Voxel LoadFile(Pair files) { VxlReader vxl; HvaReader hva; @@ -230,5 +233,12 @@ namespace OpenRA.Graphics { sheetBuilder.Current.ReleaseBuffer(); } + + public void Dispose() + { + if (vertexBuffer != null) + vertexBuffer.Dispose(); + sheetBuilder.Dispose(); + } } } diff --git a/OpenRA.Game/Graphics/VoxelRenderer.cs b/OpenRA.Game/Graphics/VoxelRenderer.cs index 3afa4c611b..6b062b1faf 100644 --- a/OpenRA.Game/Graphics/VoxelRenderer.cs +++ b/OpenRA.Game/Graphics/VoxelRenderer.cs @@ -32,33 +32,30 @@ namespace OpenRA.Graphics } } - public class VoxelRenderer + public sealed class VoxelRenderer : IDisposable { - Renderer renderer; - IShader shader; + // Static constants + static readonly float[] ShadowDiffuse = new float[] { 0, 0, 0 }; + static readonly float[] ShadowAmbient = new float[] { 1, 1, 1 }; + static readonly float2 SpritePadding = new float2(2, 2); + static readonly float[] ZeroVector = new float[] { 0, 0, 0, 1 }; + static readonly float[] ZVector = new float[] { 0, 0, 1, 1 }; + static readonly float[] FlipMtx = Util.ScaleMatrix(1, -1, 1); + static readonly float[] ShadowScaleFlipMtx = Util.ScaleMatrix(2, -2, 2); + + readonly Renderer renderer; + readonly IShader shader; + + readonly Dictionary mappedBuffers = new Dictionary(); + readonly Stack> unmappedBuffers = new Stack>(); + readonly List> doRender = new List>(); SheetBuilder sheetBuilder; - Dictionary mappedBuffers; - Stack> unmappedBuffers; - List> doRender; - - // Static constants - static readonly float[] shadowDiffuse = new float[] {0,0,0}; - static readonly float[] shadowAmbient = new float[] {1,1,1}; - static readonly float2 spritePadding = new float2(2, 2); - static readonly float[] zeroVector = new float[] {0,0,0,1}; - static readonly float[] zVector = new float[] {0,0,1,1}; - static readonly float[] flipMtx = Util.ScaleMatrix(1, -1, 1); - static readonly float[] shadowScaleFlipMtx = Util.ScaleMatrix(2, -2, 2); public VoxelRenderer(Renderer renderer, IShader shader) { this.renderer = renderer; this.shader = shader; - - mappedBuffers = new Dictionary(); - unmappedBuffers = new Stack>(); - doRender = new List>(); } public void SetPalette(ITexture palette) @@ -68,12 +65,12 @@ 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, 0, -a, 0, 0, - 0, 0, -2*a, 0, + 0, 0, -2 * a, 0, -1, 1, 0, 1 }; @@ -111,7 +108,7 @@ namespace OpenRA.Graphics var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]); var worldTransform = v.RotationFunc().Aggregate(Util.IdentityMatrix(), - (x,y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x)); + (x, y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x)); worldTransform = Util.MatrixMultiply(scaleTransform, worldTransform); worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform); @@ -128,18 +125,18 @@ namespace OpenRA.Graphics } // Inflate rects to ensure rendering is within bounds - tl -= spritePadding; - br += spritePadding; - stl -= spritePadding; - sbr += spritePadding; + tl -= SpritePadding; + br += SpritePadding; + stl -= SpritePadding; + sbr += SpritePadding; // Corners of the shadow quad, in shadow-space var corners = new float[][] { - new float[] {stl.X, stl.Y, 0, 1}, - new float[] {sbr.X, sbr.Y, 0, 1}, - new float[] {sbr.X, stl.Y, 0, 1}, - new float[] {stl.X, sbr.Y, 0, 1} + new[] { stl.X, stl.Y, 0, 1 }, + new[] { sbr.X, sbr.Y, 0, 1 }, + new[] { sbr.X, stl.Y, 0, 1 }, + new[] { stl.X, sbr.Y, 0, 1 } }; var shadowScreenTransform = Util.MatrixMultiply(cameraTransform, invShadowTransform); @@ -148,8 +145,8 @@ namespace OpenRA.Graphics for (var j = 0; j < 4; j++) { // Project to ground plane - corners[j][2] = -(corners[j][1]*shadowGroundNormal[1]/shadowGroundNormal[2] + - corners[j][0]*shadowGroundNormal[0]/shadowGroundNormal[2]); + corners[j][2] = -(corners[j][1] * shadowGroundNormal[1] / shadowGroundNormal[2] + + corners[j][0] * shadowGroundNormal[0] / shadowGroundNormal[2]); // Rotate to camera-space corners[j] = Util.MatrixVectorMultiply(shadowScreenTransform, corners[j]); @@ -169,10 +166,10 @@ 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 correctionTransform = Util.MatrixMultiply(translateMtx, flipMtx); - var shadowCorrectionTransform = Util.MatrixMultiply(shadowTranslateMtx, shadowScaleFlipMtx); + 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); doRender.Add(Pair.New(sprite.sheet, () => { @@ -183,7 +180,7 @@ namespace OpenRA.Graphics var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]); var rotations = v.RotationFunc().Aggregate(Util.IdentityMatrix(), - (x,y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x)); + (x, y) => Util.MatrixMultiply(Util.MakeFloatMatrix(y.AsMatrix()), x)); var worldTransform = Util.MatrixMultiply(scaleTransform, rotations); worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform); @@ -209,21 +206,21 @@ namespace OpenRA.Graphics // Disable shadow normals by forcing zero diffuse and identity ambient light Render(rd, Util.MatrixMultiply(shadow, t), lightDirection, - shadowAmbient, shadowDiffuse, shadowPalette.Index, normals.Index); + ShadowAmbient, ShadowDiffuse, shadowPalette.Index, normals.Index); } } })); - var screenLightVector = Util.MatrixVectorMultiply(invShadowTransform, zVector); + var screenLightVector = Util.MatrixVectorMultiply(invShadowTransform, ZVector); screenLightVector = Util.MatrixVectorMultiply(cameraTransform, screenLightVector); - return new VoxelRenderProxy(sprite, shadowSprite, screenCorners, -screenLightVector[2]/screenLightVector[1]); + return new VoxelRenderProxy(sprite, shadowSprite, screenCorners, -screenLightVector[2] / screenLightVector[1]); } static void CalculateSpriteGeometry(float2 tl, float2 br, float scale, out Size size, out int2 offset) { - var width = (int)(scale*(br.X - tl.X)); - var height = (int)(scale*(br.Y - tl.Y)); - offset = (0.5f*scale*(br + tl)).ToInt2(); + var width = (int)(scale * (br.X - tl.X)); + var height = (int)(scale * (br.Y - tl.Y)); + offset = (0.5f * scale * (br + tl)).ToInt2(); // Width and height must be even to avoid rendering glitches if ((width & 1) == 1) @@ -236,14 +233,14 @@ namespace OpenRA.Graphics static float[] ExtractRotationVector(float[] mtx) { - var tVec = Util.MatrixVectorMultiply(mtx, zVector); - var tOrigin = Util.MatrixVectorMultiply(mtx, zeroVector); - tVec[0] -= tOrigin[0]*tVec[3]/tOrigin[3]; - tVec[1] -= tOrigin[1]*tVec[3]/tOrigin[3]; - tVec[2] -= tOrigin[2]*tVec[3]/tOrigin[3]; + var tVec = Util.MatrixVectorMultiply(mtx, ZVector); + var tOrigin = Util.MatrixVectorMultiply(mtx, ZeroVector); + tVec[0] -= tOrigin[0] * tVec[3] / tOrigin[3]; + tVec[1] -= tOrigin[1] * tVec[3] / tOrigin[3]; + tVec[2] -= tOrigin[2] * tVec[3] / tOrigin[3]; // Renormalize - var w = (float)Math.Sqrt(tVec[0]*tVec[0] + tVec[1]*tVec[1] + tVec[2]*tVec[2]); + var w = (float)Math.Sqrt(tVec[0] * tVec[0] + tVec[1] * tVec[1] + tVec[2] * tVec[2]); tVec[0] /= w; tVec[1] /= w; tVec[2] /= w; @@ -330,12 +327,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(); + } } } diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index 049d48a859..89140b8406 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -29,24 +29,22 @@ namespace OpenRA.Graphics } } - public class WorldRenderer + public sealed class WorldRenderer : IDisposable { public readonly World world; public readonly Theater Theater; public Viewport Viewport { get; private set; } + readonly HardwarePalette palette = new HardwarePalette(); + readonly Dictionary palettes = new Dictionary(); readonly TerrainRenderer terrainRenderer; - readonly HardwarePalette palette; - readonly Dictionary palettes; readonly Lazy devTrait; internal WorldRenderer(World world) { this.world = world; Viewport = new Viewport(this, world.Map); - palette = new HardwarePalette(); - palettes = new Dictionary(); foreach (var pal in world.traitDict.ActorsWithTrait()) pal.Trait.LoadPalettes(this); @@ -251,5 +249,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(); + } } } diff --git a/OpenRA.Game/Map/MapCache.cs b/OpenRA.Game/Map/MapCache.cs index cf528f1a99..2621808439 100644 --- a/OpenRA.Game/Map/MapCache.cs +++ b/OpenRA.Game/Map/MapCache.cs @@ -21,7 +21,7 @@ using OpenRA.Primitives; namespace OpenRA { - public class MapCache : IEnumerable + public sealed class MapCache : IEnumerable, IDisposable { public static readonly MapPreview UnknownMap = new MapPreview(null, null); readonly Cache 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(); + } } } diff --git a/OpenRA.Game/ModData.cs b/OpenRA.Game/ModData.cs index 288d9ac836..3715a3fbe6 100644 --- a/OpenRA.Game/ModData.cs +++ b/OpenRA.Game/ModData.cs @@ -18,29 +18,32 @@ using OpenRA.Widgets; namespace OpenRA { - public class ModData + public sealed class ModData : IDisposable { public readonly Manifest Manifest; public readonly ObjectCreator ObjectCreator; public readonly WidgetLoader WidgetLoader; public readonly MapCache MapCache; public readonly ISpriteLoader[] SpriteLoaders; - public ILoadScreen LoadScreen = null; - public VoxelLoader VoxelLoader; public readonly RulesetCache RulesetCache; + public ILoadScreen LoadScreen { get; private set; } + public VoxelLoader VoxelLoader { get; private set; } public CursorProvider CursorProvider { get; private set; } Lazy 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(Manifest.LoadScreen.Value); - LoadScreen.Init(Manifest, Manifest.LoadScreen.ToDictionary(my => my.Value)); - LoadScreen.Display(); + if (useLoadScreen) + { + LoadScreen = ObjectCreator.CreateObject(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 info); void Display(); diff --git a/OpenRA.Lint/YamlChecker.cs b/OpenRA.Lint/YamlChecker.cs index a076c082ed..c8686378bb 100644 --- a/OpenRA.Lint/YamlChecker.cs +++ b/OpenRA.Lint/YamlChecker.cs @@ -55,6 +55,7 @@ namespace OpenRA.Lint FieldLoader.UnknownFieldAction = (s, f) => EmitError("FieldLoader: Missing field `{0}` on `{1}`".F(s, f.Name)); AppDomain.CurrentDomain.AssemblyResolve += GlobalFileSystem.ResolveAssembly; + Game.InitializeSettings(Arguments.Empty); Game.modData = new ModData(mod); IEnumerable maps; diff --git a/OpenRA.Mods.Cnc/CncLoadScreen.cs b/OpenRA.Mods.Cnc/CncLoadScreen.cs index b1047a0bde..1f3d424737 100644 --- a/OpenRA.Mods.Cnc/CncLoadScreen.cs +++ b/OpenRA.Mods.Cnc/CncLoadScreen.cs @@ -11,24 +11,23 @@ using System.Collections.Generic; using System.Diagnostics; using System.Drawing; -using System.Linq; -using OpenRA.FileSystem; using OpenRA.Graphics; using OpenRA.Widgets; namespace OpenRA.Mods.Cnc { - public class CncLoadScreen : ILoadScreen + public sealed class CncLoadScreen : ILoadScreen { Dictionary loadInfo; Stopwatch loadTimer = Stopwatch.StartNew(); + Sheet sheet; Sprite[] ss; int loadTick; float2 nodPos, gdiPos, evaPos; Sprite nodLogo, gdiLogo, evaLogo, brightBlock, dimBlock; Rectangle bounds; Renderer r; - NullInputHandler nih = new NullInputHandler(); + readonly NullInputHandler nih = new NullInputHandler(); public void Init(Manifest m, Dictionary info) { @@ -39,31 +38,31 @@ namespace OpenRA.Mods.Cnc r = Game.Renderer; if (r == null) return; - var s = new Sheet(Platform.ResolvePath(loadInfo["Image"])); + sheet = new Sheet(Platform.ResolvePath(loadInfo["Image"])); var res = r.Resolution; bounds = new Rectangle(0, 0, res.Width, res.Height); ss = new[] { - new Sprite(s, new Rectangle(161, 128, 62, 33), TextureChannel.Alpha), - new Sprite(s, new Rectangle(161, 223, 62, 33), TextureChannel.Alpha), - new Sprite(s, new Rectangle(128, 161, 33, 62), TextureChannel.Alpha), - new Sprite(s, new Rectangle(223, 161, 33, 62), TextureChannel.Alpha), - new Sprite(s, new Rectangle(128, 128, 33, 33), TextureChannel.Alpha), - new Sprite(s, new Rectangle(223, 128, 33, 33), TextureChannel.Alpha), - new Sprite(s, new Rectangle(128, 223, 33, 33), TextureChannel.Alpha), - new Sprite(s, new Rectangle(223, 223, 33, 33), TextureChannel.Alpha) + new Sprite(sheet, new Rectangle(161, 128, 62, 33), TextureChannel.Alpha), + new Sprite(sheet, new Rectangle(161, 223, 62, 33), TextureChannel.Alpha), + new Sprite(sheet, new Rectangle(128, 161, 33, 62), TextureChannel.Alpha), + new Sprite(sheet, new Rectangle(223, 161, 33, 62), TextureChannel.Alpha), + new Sprite(sheet, new Rectangle(128, 128, 33, 33), TextureChannel.Alpha), + new Sprite(sheet, new Rectangle(223, 128, 33, 33), TextureChannel.Alpha), + new Sprite(sheet, new Rectangle(128, 223, 33, 33), TextureChannel.Alpha), + new Sprite(sheet, new Rectangle(223, 223, 33, 33), TextureChannel.Alpha) }; - nodLogo = new Sprite(s, new Rectangle(0, 256, 256, 256), TextureChannel.Alpha); - gdiLogo = new Sprite(s, new Rectangle(256, 256, 256, 256), TextureChannel.Alpha); - evaLogo = new Sprite(s, new Rectangle(256, 64, 128, 64), TextureChannel.Alpha); + nodLogo = new Sprite(sheet, new Rectangle(0, 256, 256, 256), TextureChannel.Alpha); + gdiLogo = new Sprite(sheet, new Rectangle(256, 256, 256, 256), TextureChannel.Alpha); + evaLogo = new Sprite(sheet, new Rectangle(256, 64, 128, 64), TextureChannel.Alpha); nodPos = new float2(bounds.Width / 2 - 384, bounds.Height / 2 - 128); gdiPos = new float2(bounds.Width / 2 + 128, bounds.Height / 2 - 128); evaPos = new float2(bounds.Width - 43 - 128, 43); - brightBlock = new Sprite(s, new Rectangle(320, 0, 16, 35), TextureChannel.Alpha); - dimBlock = new Sprite(s, new Rectangle(336, 0, 16, 35), TextureChannel.Alpha); + brightBlock = new Sprite(sheet, new Rectangle(320, 0, 16, 35), TextureChannel.Alpha); + dimBlock = new Sprite(sheet, new Rectangle(336, 0, 16, 35), TextureChannel.Alpha); versionText = m.Mod.Version; } @@ -123,5 +122,11 @@ namespace OpenRA.Mods.Cnc { Game.TestAndContinue(); } + + public void Dispose() + { + if (sheet != null) + sheet.Dispose(); + } } } \ No newline at end of file diff --git a/OpenRA.Mods.Common/LoadScreens/DefaultLoadScreen.cs b/OpenRA.Mods.Common/LoadScreens/DefaultLoadScreen.cs index b10262a0e0..c04fa6738d 100644 --- a/OpenRA.Mods.Common/LoadScreens/DefaultLoadScreen.cs +++ b/OpenRA.Mods.Common/LoadScreens/DefaultLoadScreen.cs @@ -11,20 +11,19 @@ using System.Collections.Generic; using System.Diagnostics; using System.Drawing; -using System.Linq; -using OpenRA.FileSystem; using OpenRA.Graphics; using OpenRA.Widgets; namespace OpenRA.Mods.Common.LoadScreens { - public class DefaultLoadScreen : ILoadScreen + public sealed class DefaultLoadScreen : ILoadScreen { Stopwatch lastUpdate = Stopwatch.StartNew(); Renderer r; Rectangle stripeRect; float2 logoPos; + Sheet sheet; Sprite stripe, logo; string[] messages; @@ -37,9 +36,9 @@ namespace OpenRA.Mods.Common.LoadScreens return; messages = info["Text"].Split(','); - var s = new Sheet(Platform.ResolvePath(info["Image"])); - logo = new Sprite(s, new Rectangle(0, 0, 256, 256), TextureChannel.Alpha); - stripe = new Sprite(s, new Rectangle(256, 0, 256, 256), TextureChannel.Alpha); + sheet = new Sheet(Platform.ResolvePath(info["Image"])); + logo = new Sprite(sheet, new Rectangle(0, 0, 256, 256), TextureChannel.Alpha); + stripe = new Sprite(sheet, new Rectangle(256, 0, 256, 256), TextureChannel.Alpha); stripeRect = new Rectangle(0, r.Resolution.Height / 2 - 128, r.Resolution.Width, 256); logoPos = new float2(r.Resolution.Width / 2 - 128, r.Resolution.Height / 2 - 128); } @@ -71,5 +70,11 @@ namespace OpenRA.Mods.Common.LoadScreens { Game.TestAndContinue(); } + + public void Dispose() + { + if (sheet != null) + sheet.Dispose(); + } } } diff --git a/OpenRA.Mods.Common/LoadScreens/ModChooserLoadScreen.cs b/OpenRA.Mods.Common/LoadScreens/ModChooserLoadScreen.cs index f5ec655a05..ccc18e204e 100644 --- a/OpenRA.Mods.Common/LoadScreens/ModChooserLoadScreen.cs +++ b/OpenRA.Mods.Common/LoadScreens/ModChooserLoadScreen.cs @@ -15,7 +15,7 @@ using OpenRA.Widgets; namespace OpenRA.Mods.Common.LoadScreens { - public class ModChooserLoadScreen : ILoadScreen + public sealed class ModChooserLoadScreen : ILoadScreen { Sprite sprite; Rectangle bounds; @@ -43,5 +43,11 @@ namespace OpenRA.Mods.Common.LoadScreens { Ui.LoadWidget("MODCHOOSER", Ui.Root, new WidgetArgs()); } + + public void Dispose() + { + if (sprite != null) + sprite.sheet.Dispose(); + } } } \ No newline at end of file diff --git a/OpenRA.Mods.Common/LoadScreens/NullLoadScreen.cs b/OpenRA.Mods.Common/LoadScreens/NullLoadScreen.cs index 122a2b1dfe..ce0c7b149c 100644 --- a/OpenRA.Mods.Common/LoadScreens/NullLoadScreen.cs +++ b/OpenRA.Mods.Common/LoadScreens/NullLoadScreen.cs @@ -13,7 +13,7 @@ using OpenRA.Widgets; namespace OpenRA.Mods.Common.LoadScreens { - public class NullLoadScreen : ILoadScreen + public sealed class NullLoadScreen : ILoadScreen { public void Init(Manifest m, Dictionary info) { } @@ -31,5 +31,9 @@ namespace OpenRA.Mods.Common.LoadScreens { Ui.ResetAll(); } + + public void Dispose() + { + } } -} \ No newline at end of file +} diff --git a/OpenRA.Mods.Common/Widgets/Logic/ModBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ModBrowserLogic.cs index 1ea99373f4..8b7e110d3d 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ModBrowserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ModBrowserLogic.cs @@ -20,15 +20,16 @@ namespace OpenRA.Mods.Common.Widgets.Logic { public class ModBrowserLogic { - Widget modList; - ButtonWidget modTemplate; - ModMetadata[] allMods; + readonly Widget modList; + readonly ButtonWidget modTemplate; + readonly ModMetadata[] allMods; + readonly Dictionary previews = new Dictionary(); + readonly Dictionary logos = new Dictionary(); + readonly SheetBuilder sheetBuilder; ModMetadata selectedMod; string selectedAuthor; string selectedDescription; int modOffset = 0; - Dictionary previews; - Dictionary logos; [ObjectCreator.UseCtor] public ModBrowserLogic(Widget widget) @@ -63,9 +64,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic return ret; }; - var sheetBuilder = new SheetBuilder(SheetType.BGRA); - previews = new Dictionary(); - logos = new Dictionary(); + sheetBuilder = new SheetBuilder(SheetType.BGRA); allMods = ModMetadata.AllMods.Values.Where(m => m.Id != "modchooser") .OrderBy(m => m.Title) .ToArray(); @@ -75,21 +74,17 @@ namespace OpenRA.Mods.Common.Widgets.Logic { try { - var preview = new Bitmap(Platform.ResolvePath(".", "mods", mod.Id, "preview.png")); - if (preview.Width != 296 || preview.Height != 196) - continue; - - previews.Add(mod.Id, sheetBuilder.Add(preview)); + using (var preview = new Bitmap(Platform.ResolvePath(".", "mods", mod.Id, "preview.png"))) + if (preview.Width == 296 && preview.Height == 196) + previews.Add(mod.Id, sheetBuilder.Add(preview)); } catch (Exception) { } try { - var logo = new Bitmap(Platform.ResolvePath(".", "mods", mod.Id, "logo.png")); - if (logo.Width != 96 || logo.Height != 96) - continue; - - logos.Add(mod.Id, sheetBuilder.Add(logo)); + using (var logo = new Bitmap(Platform.ResolvePath(".", "mods", mod.Id, "logo.png"))) + if (logo.Width == 96 && logo.Height == 96) + logos.Add(mod.Id, sheetBuilder.Add(logo)); } catch (Exception) { } } @@ -155,11 +150,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic modOffset = selectedIndex - 4; } - static void LoadMod(ModMetadata mod) + void LoadMod(ModMetadata mod) { Game.RunAfterTick(() => { Ui.CloseWindow(); + sheetBuilder.Dispose(); Game.InitializeMod(mod.Id, null); }); } diff --git a/OpenRA.Renderer.Null/NullGraphicsDevice.cs b/OpenRA.Renderer.Null/NullGraphicsDevice.cs index 5e2da9c886..299fa0ddea 100644 --- a/OpenRA.Renderer.Null/NullGraphicsDevice.cs +++ b/OpenRA.Renderer.Null/NullGraphicsDevice.cs @@ -78,7 +78,7 @@ namespace OpenRA.Renderer.Null public void Render(Action a) { } } - public class NullTexture : ITexture + public sealed class NullTexture : ITexture { public TextureScaleFilter ScaleFilter { get { return TextureScaleFilter.Nearest; } set { } } public void SetData(Bitmap bitmap) { } @@ -86,18 +86,21 @@ namespace OpenRA.Renderer.Null public void SetData(byte[] colors, int width, int height) { } public byte[] GetData() { return new byte[0]; } public Size Size { get { return new Size(0, 0); } } + public void Dispose() { } } - public class NullFrameBuffer : IFrameBuffer + public sealed class NullFrameBuffer : IFrameBuffer { public void Bind() { } public void Unbind() { } public ITexture Texture { get { return new NullTexture(); } } + public void Dispose() { } } - class NullVertexBuffer : IVertexBuffer + sealed class NullVertexBuffer : IVertexBuffer { public void Bind() { } public void SetData(T[] vertices, int length) { } + public void Dispose() { } } } diff --git a/OpenRA.Renderer.Sdl2/FrameBuffer.cs b/OpenRA.Renderer.Sdl2/FrameBuffer.cs index a640229fdf..9ef1f4aeb2 100644 --- a/OpenRA.Renderer.Sdl2/FrameBuffer.cs +++ b/OpenRA.Renderer.Sdl2/FrameBuffer.cs @@ -16,11 +16,12 @@ using OpenTK.Graphics.OpenGL; namespace OpenRA.Renderer.Sdl2 { - public class FrameBuffer : IFrameBuffer + public sealed class FrameBuffer : IFrameBuffer { Texture texture; Size size; int framebuffer, depth; + bool disposed; public FrameBuffer(Size size) { @@ -81,16 +82,6 @@ namespace OpenRA.Renderer.Sdl2 return v; } - void FinalizeInner() - { - GL.Ext.DeleteFramebuffers(1, ref framebuffer); - ErrorHandler.CheckGlError(); - GL.Ext.DeleteRenderbuffers(1, ref depth); - ErrorHandler.CheckGlError(); - } - - ~FrameBuffer() { Game.RunAfterTick(FinalizeInner); } - int[] cv = new int[4]; public void Bind() { @@ -120,5 +111,29 @@ namespace OpenRA.Renderer.Sdl2 } public ITexture Texture { get { return texture; } } + + ~FrameBuffer() + { + Game.RunAfterTick(() => Dispose(false)); + } + + public void Dispose() + { + Game.RunAfterTick(() => Dispose(true)); + GC.SuppressFinalize(this); + } + + void Dispose(bool disposing) + { + if (disposed) + return; + disposed = true; + if (disposing) + texture.Dispose(); + GL.Ext.DeleteFramebuffers(1, ref framebuffer); + ErrorHandler.CheckGlError(); + GL.Ext.DeleteRenderbuffers(1, ref depth); + ErrorHandler.CheckGlError(); + } } } diff --git a/OpenRA.Renderer.Sdl2/Texture.cs b/OpenRA.Renderer.Sdl2/Texture.cs index de82a06e8e..3e3ae2bd7a 100644 --- a/OpenRA.Renderer.Sdl2/Texture.cs +++ b/OpenRA.Renderer.Sdl2/Texture.cs @@ -16,7 +16,7 @@ using OpenTK.Graphics.OpenGL; namespace OpenRA.Renderer.Sdl2 { - public class Texture : ITexture + public sealed class Texture : ITexture { int texture; TextureScaleFilter scaleFilter; @@ -26,6 +26,8 @@ namespace OpenRA.Renderer.Sdl2 public Size Size { get { return size; } } + bool disposed; + public TextureScaleFilter ScaleFilter { get @@ -55,9 +57,6 @@ namespace OpenRA.Renderer.Sdl2 SetData(bitmap); } - void FinalizeInner() { GL.DeleteTextures(1, ref texture); } - ~Texture() { Game.RunAfterTick(FinalizeInner); } - void PrepareTexture() { ErrorHandler.CheckGlError(); @@ -131,6 +130,7 @@ namespace OpenRA.Renderer.Sdl2 bitmap = new Bitmap(bitmap, bitmap.Size.NextPowerOf2()); allocatedBitmap = true; } + try { size = new Size(bitmap.Width, bitmap.Height); @@ -180,5 +180,24 @@ namespace OpenRA.Renderer.Sdl2 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero); ErrorHandler.CheckGlError(); } + + ~Texture() + { + Game.RunAfterTick(() => Dispose(false)); + } + + public void Dispose() + { + Game.RunAfterTick(() => Dispose(true)); + GC.SuppressFinalize(this); + } + + void Dispose(bool disposing) + { + if (disposed) + return; + disposed = true; + GL.DeleteTextures(1, ref texture); + } } } diff --git a/OpenRA.Renderer.Sdl2/VertexBuffer.cs b/OpenRA.Renderer.Sdl2/VertexBuffer.cs index a5655e4cd1..c35baa0f7d 100644 --- a/OpenRA.Renderer.Sdl2/VertexBuffer.cs +++ b/OpenRA.Renderer.Sdl2/VertexBuffer.cs @@ -14,11 +14,12 @@ using OpenTK.Graphics.OpenGL; namespace OpenRA.Renderer.Sdl2 { - public class VertexBuffer : IVertexBuffer + public sealed class VertexBuffer : IVertexBuffer where T : struct { static readonly int VertexSize = Marshal.SizeOf(typeof(T)); int buffer; + bool disposed; public VertexBuffer(int size) { @@ -52,7 +53,23 @@ namespace OpenRA.Renderer.Sdl2 ErrorHandler.CheckGlError(); } - void FinalizeInner() { GL.DeleteBuffers(1, ref buffer); } - ~VertexBuffer() { Game.RunAfterTick(FinalizeInner); } + ~VertexBuffer() + { + Game.RunAfterTick(() => Dispose(false)); + } + + public void Dispose() + { + Game.RunAfterTick(() => Dispose(true)); + GC.SuppressFinalize(this); + } + + void Dispose(bool disposing) + { + if (disposed) + return; + disposed = true; + GL.DeleteBuffers(1, ref buffer); + } } } diff --git a/OpenRA.Utility/Program.cs b/OpenRA.Utility/Program.cs index 0a403c2ecc..8b79d8f580 100644 --- a/OpenRA.Utility/Program.cs +++ b/OpenRA.Utility/Program.cs @@ -38,6 +38,7 @@ namespace OpenRA.Utility return; } + Game.InitializeSettings(Arguments.Empty); var modData = new ModData(modName); args = args.Skip(1).ToArray(); var actions = new Dictionary>();