Merge pull request #6830 from RoosterDragon/dispose-renderer-resources

Closes #5116
This commit is contained in:
Matthias Mailänder
2014-12-22 19:59:41 +01:00
34 changed files with 575 additions and 343 deletions

View File

@@ -46,6 +46,7 @@ namespace OpenRA.Editor
miniMapBox.Image = null; miniMapBox.Image = null;
currentMod = toolStripComboBox1.SelectedItem as string; currentMod = toolStripComboBox1.SelectedItem as string;
Game.InitializeSettings(Arguments.Empty);
Game.modData = new ModData(currentMod); Game.modData = new ModData(currentMod);
GlobalFileSystem.LoadFromManifest(Game.modData.Manifest); GlobalFileSystem.LoadFromManifest(Game.modData.Manifest);
Program.Rules = Game.modData.RulesetCache.LoadDefaultRules(); Program.Rules = Game.modData.RulesetCache.LoadDefaultRules();
@@ -203,10 +204,10 @@ namespace OpenRA.Editor
Height = bitmap.Height / 2, Height = bitmap.Height / 2,
SizeMode = PictureBoxSizeMode.StretchImage SizeMode = PictureBoxSizeMode.StretchImage
}; };
var brushTemplate = new BrushTemplate { Bitmap = bitmap, N = t.Key }; var brushTemplate = new BrushTemplate { Bitmap = bitmap, N = t.Key };
ibox.Click += (_, e) => surface1.SetTool(new BrushTool(brushTemplate)); ibox.Click += (_, e) => surface1.SetTool(new BrushTool(brushTemplate));
var template = t.Value; var template = t.Value;
tilePalette.Controls.Add(ibox); tilePalette.Controls.Add(ibox);
tt.SetToolTip(ibox, "{1}:{0} ({2}x{3})".F(template.Image, template.Id, template.Size.X, template.Size.Y)); 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) void ExportMinimap(object sender, EventArgs e)
{ {
using (var sfd = new SaveFileDialog() using (var sfd = new SaveFileDialog()
{ {
InitialDirectory = Path.Combine(Environment.CurrentDirectory, "maps"), InitialDirectory = Path.Combine(Environment.CurrentDirectory, "maps"),
DefaultExt = "*.png", DefaultExt = "*.png",
Filter = "PNG Image (*.png)|*.png", Filter = "PNG Image (*.png)|*.png",
@@ -471,9 +472,8 @@ namespace OpenRA.Editor
FileName = Path.ChangeExtension(loadedMapName, ".png"), FileName = Path.ChangeExtension(loadedMapName, ".png"),
RestoreDirectory = true RestoreDirectory = true
}) })
if (DialogResult.OK == sfd.ShowDialog())
if (DialogResult.OK == sfd.ShowDialog()) miniMapBox.Image.Save(sfd.FileName);
miniMapBox.Image.Save(sfd.FileName);
} }
void ShowActorNamesClicked(object sender, EventArgs e) void ShowActorNamesClicked(object sender, EventArgs e)
@@ -637,7 +637,7 @@ namespace OpenRA.Editor
{ {
ShowGridClicked(sender, e); ShowGridClicked(sender, e);
} }
public int CalculateTotalResource() public int CalculateTotalResource()
{ {
var totalResource = 0; var totalResource = 0;
@@ -651,7 +651,7 @@ namespace OpenRA.Editor
return totalResource; return totalResource;
} }
int GetAdjecentCellsWith(int resourceType, int x, int y) int GetAdjecentCellsWith(int resourceType, int x, int y)
{ {
var sum = 0; var sum = 0;

View File

@@ -140,7 +140,10 @@ namespace OpenRA
orderManager.World = new World(map, orderManager, isShellmap); orderManager.World = new World(map, orderManager, isShellmap);
orderManager.World.Timestep = Timestep; orderManager.World.Timestep = Timestep;
} }
if (worldRenderer != null)
worldRenderer.Dispose();
worldRenderer = new WorldRenderer(orderManager.World); worldRenderer = new WorldRenderer(orderManager.World);
using (new PerfTimer("LoadComplete")) using (new PerfTimer("LoadComplete"))
orderManager.World.LoadComplete(worldRenderer); orderManager.World.LoadComplete(worldRenderer);
@@ -172,13 +175,18 @@ namespace OpenRA
public static Modifiers GetModifierKeys() { return modifiers; } public static Modifiers GetModifierKeys() { return modifiers; }
internal static void HandleModifierKeys(Modifiers mods) { modifiers = mods; } 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) internal static void Initialize(Arguments args)
{ {
Console.WriteLine("Platform is {0}", Platform.CurrentPlatform); Console.WriteLine("Platform is {0}", Platform.CurrentPlatform);
AppDomain.CurrentDomain.AssemblyResolve += GlobalFileSystem.ResolveAssembly; AppDomain.CurrentDomain.AssemblyResolve += GlobalFileSystem.ResolveAssembly;
Settings = new Settings(Platform.ResolvePath("^", "settings.yaml"), args); InitializeSettings(args);
Log.AddChannel("perf", "perf.log"); Log.AddChannel("perf", "perf.log");
Log.AddChannel("debug", "debug.log"); Log.AddChannel("debug", "debug.log");
@@ -215,7 +223,7 @@ namespace OpenRA
Settings.Graphics.Renderer = r; Settings.Graphics.Renderer = r;
try try
{ {
Renderer.Initialize(Settings.Graphics.Mode); Renderer = new Renderer(Settings.Graphics, Settings.Server);
break; break;
} }
catch (Exception e) catch (Exception e)
@@ -225,8 +233,6 @@ namespace OpenRA
} }
} }
Renderer = new Renderer();
try try
{ {
Sound.Create(Settings.Sound.Engine); Sound.Create(Settings.Sound.Engine);
@@ -257,12 +263,18 @@ namespace OpenRA
BeforeGameStart = () => { }; BeforeGameStart = () => { };
Ui.ResetAll(); Ui.ResetAll();
if (worldRenderer != null)
worldRenderer.Dispose();
worldRenderer = null; worldRenderer = null;
if (server != null) if (server != null)
server.Shutdown(); server.Shutdown();
if (orderManager != null) if (orderManager != null)
orderManager.Dispose(); orderManager.Dispose();
if (modData != null)
modData.Dispose();
modData = null;
// Fall back to default if the mod doesn't exist // Fall back to default if the mod doesn't exist
if (!ModMetadata.AllMods.ContainsKey(mod)) if (!ModMetadata.AllMods.ContainsKey(mod))
mod = new GameSettings().Mod; mod = new GameSettings().Mod;
@@ -274,7 +286,7 @@ namespace OpenRA
Sound.StopVideo(); Sound.StopVideo();
Sound.Initialize(); Sound.Initialize();
modData = new ModData(mod); modData = new ModData(mod, true);
Renderer.InitializeFonts(modData.Manifest); Renderer.InitializeFonts(modData.Manifest);
modData.InitializeLoaders(); modData.InitializeLoaders();
using (new PerfTimer("LoadMaps")) using (new PerfTimer("LoadMaps"))
@@ -632,8 +644,12 @@ namespace OpenRA
if (orderManager != null) if (orderManager != null)
orderManager.Dispose(); orderManager.Dispose();
} }
Renderer.Device.Dispose(); if (worldRenderer != null)
worldRenderer.Dispose();
modData.Dispose();
ChromeProvider.Deinitialize();
Renderer.Dispose();
OnQuit(); OnQuit();

View File

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

View File

@@ -17,8 +17,8 @@ namespace OpenRA.Graphics
{ {
struct Collection struct Collection
{ {
public string src; public string Src;
public Dictionary<string, MappedImage> regions; public Dictionary<string, MappedImage> Regions;
} }
static Dictionary<string, Collection> collections; static Dictionary<string, Collection> collections;
@@ -27,6 +27,8 @@ namespace OpenRA.Graphics
public static void Initialize(IEnumerable<string> chromeFiles) public static void Initialize(IEnumerable<string> chromeFiles)
{ {
Deinitialize();
collections = new Dictionary<string, Collection>(); collections = new Dictionary<string, Collection>();
cachedSheets = new Dictionary<string, Sheet>(); cachedSheets = new Dictionary<string, Sheet>();
cachedSprites = new Dictionary<string, Dictionary<string, Sprite>>(); cachedSprites = new Dictionary<string, Dictionary<string, Sprite>>();
@@ -37,6 +39,17 @@ namespace OpenRA.Graphics
LoadCollection(c.Key, c.Value); 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) public static void Save(string file)
{ {
var root = new List<MiniYamlNode>(); var root = new List<MiniYamlNode>();
@@ -49,10 +62,10 @@ namespace OpenRA.Graphics
static MiniYaml SaveCollection(Collection collection) static MiniYaml SaveCollection(Collection collection)
{ {
var root = new List<MiniYamlNode>(); var root = new List<MiniYamlNode>();
foreach (var kv in collection.regions) foreach (var kv in collection.Regions)
root.Add(new MiniYamlNode(kv.Key, kv.Value.Save(collection.src))); 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) static void LoadCollection(string name, MiniYaml yaml)
@@ -60,8 +73,8 @@ namespace OpenRA.Graphics
Game.modData.LoadScreen.Display(); Game.modData.LoadScreen.Display();
var collection = new Collection() var collection = new Collection()
{ {
src = yaml.Value, Src = yaml.Value,
regions = yaml.Nodes.ToDictionary(n => n.Key, n => new MappedImage(yaml.Value, n.Value)) Regions = yaml.Nodes.ToDictionary(n => n.Key, n => new MappedImage(yaml.Value, n.Value))
}; };
collections.Add(name, collection); collections.Add(name, collection);
@@ -83,7 +96,7 @@ namespace OpenRA.Graphics
} }
MappedImage mi; MappedImage mi;
if (!collection.regions.TryGetValue(imageName, out mi)) if (!collection.Regions.TryGetValue(imageName, out mi))
return null; return null;
// Cached sheet // Cached sheet
@@ -102,6 +115,7 @@ namespace OpenRA.Graphics
cachedCollection = new Dictionary<string, Sprite>(); cachedCollection = new Dictionary<string, Sprite>();
cachedSprites.Add(collectionName, cachedCollection); cachedSprites.Add(collectionName, cachedCollection);
} }
var image = mi.GetImage(sheet); var image = mi.GetImage(sheet);
cachedCollection.Add(imageName, image); cachedCollection.Add(imageName, image);

View File

@@ -16,11 +16,12 @@ using OpenRA.Primitives;
namespace OpenRA.Graphics namespace OpenRA.Graphics
{ {
public class CursorProvider public sealed class CursorProvider : IDisposable
{ {
HardwarePalette palette; readonly HardwarePalette palette = new HardwarePalette();
Dictionary<string, CursorSequence> cursors; readonly Dictionary<string, CursorSequence> cursors = new Dictionary<string, CursorSequence>();
Cache<string, PaletteReference> palettes; readonly Cache<string, PaletteReference> palettes;
readonly SheetBuilder sheetBuilder;
public static bool CursorViewportZoomed { get { return Game.Settings.Graphics.CursorDouble && Game.Settings.Graphics.PixelDouble; } } 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; var sequenceFiles = modData.Manifest.Cursors;
cursors = new Dictionary<string, CursorSequence>();
palettes = new Cache<string, PaletteReference>(CreatePaletteReference); palettes = new Cache<string, PaletteReference>(CreatePaletteReference);
var sequences = new MiniYaml(null, sequenceFiles.Select(s => MiniYaml.FromFile(s)).Aggregate(MiniYaml.MergeLiberal)); var sequences = new MiniYaml(null, sequenceFiles.Select(s => MiniYaml.FromFile(s)).Aggregate(MiniYaml.MergeLiberal));
var shadowIndex = new int[] { }; var shadowIndex = new int[] { };
@@ -41,14 +41,14 @@ namespace OpenRA.Graphics
out shadowIndex[shadowIndex.Length - 1]); out shadowIndex[shadowIndex.Length - 1]);
} }
palette = new HardwarePalette();
foreach (var p in nodesDict["Palettes"].Nodes) foreach (var p in nodesDict["Palettes"].Nodes)
palette.AddPalette(p.Key, new ImmutablePalette(GlobalFileSystem.Open(p.Value.Value), shadowIndex), false); 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) foreach (var s in nodesDict["Cursors"].Nodes)
LoadSequencesForCursor(spriteCache, s.Key, s.Value); LoadSequencesForCursor(spriteCache, s.Key, s.Value);
spriteCache.SheetBuilder.Current.ReleaseBuffer(); sheetBuilder.Current.ReleaseBuffer();
palette.Initialize(); palette.Initialize();
} }
@@ -95,5 +95,11 @@ namespace OpenRA.Graphics
throw new InvalidOperationException("Cursor does not have a sequence `{0}`".F(cursor)); 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 namespace OpenRA.Graphics
{ {
public class HardwarePalette public sealed class HardwarePalette : IDisposable
{ {
public const int MaxPalettes = 256; public const int MaxPalettes = 256;
@@ -111,5 +111,10 @@ namespace OpenRA.Graphics
modifiedPalette.SetFromPalette(originalPalette); modifiedPalette.SetFromPalette(originalPalette);
} }
} }
public void Dispose()
{
Texture.Dispose();
}
} }
} }

View File

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

View File

@@ -15,24 +15,29 @@ namespace OpenRA.Graphics
{ {
public class LineRenderer : Renderer.IBatchRenderer public class LineRenderer : Renderer.IBatchRenderer
{ {
static float2 offset = new float2(0.5f, 0.5f); static readonly float2 Offset = new float2(0.5f, 0.5f);
float lineWidth = 1f;
Renderer renderer;
IShader shader;
Vertex[] vertices = new Vertex[Renderer.TempBufferSize]; readonly Renderer renderer;
readonly IShader shader;
readonly Vertex[] vertices;
int nv = 0; int nv = 0;
float lineWidth = 1f;
public LineRenderer(Renderer renderer, IShader shader) public LineRenderer(Renderer renderer, IShader shader)
{ {
this.renderer = renderer; this.renderer = renderer;
this.shader = shader; this.shader = shader;
vertices = new Vertex[renderer.TempBufferSize];
} }
public float LineWidth public float LineWidth
{ {
get { return lineWidth; } get
{
return lineWidth;
}
set set
{ {
if (LineWidth != value) if (LineWidth != value)
@@ -71,16 +76,16 @@ namespace OpenRA.Graphics
public void DrawLine(float2 start, float2 end, Color startColor, Color endColor) 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(); Flush();
vertices[nv++] = new Vertex(start + offset, vertices[nv++] = new Vertex(start + Offset,
startColor.R / 255.0f, startColor.G / 255.0f, startColor.R / 255.0f, startColor.G / 255.0f,
startColor.B / 255.0f, startColor.A / 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.R / 255.0f, endColor.G / 255.0f,
endColor.B / 255.0f, endColor.A / 255.0f); endColor.B / 255.0f, endColor.A / 255.0f);
} }
@@ -99,7 +104,7 @@ namespace OpenRA.Graphics
var yc = (r.Bottom + r.Top) / 2; var yc = (r.Bottom + r.Top) / 2;
for (var y = r.Top; y <= r.Bottom; y++) 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); 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) public void SetViewportParams(Size screen, float zoom, int2 scroll)
{ {
shader.SetVec("Scroll", scroll.X, scroll.Y); 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); shader.SetVec("r2", -1, 1);
} }
} }

View File

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

View File

@@ -14,16 +14,17 @@ namespace OpenRA.Graphics
{ {
public class QuadRenderer : Renderer.IBatchRenderer public class QuadRenderer : Renderer.IBatchRenderer
{ {
Renderer renderer; readonly Renderer renderer;
IShader shader; readonly IShader shader;
Vertex[] vertices = new Vertex[Renderer.TempBufferSize]; readonly Vertex[] vertices;
int nv = 0; int nv = 0;
public QuadRenderer(Renderer renderer, IShader shader) public QuadRenderer(Renderer renderer, IShader shader)
{ {
this.renderer = renderer; this.renderer = renderer;
this.shader = shader; this.shader = shader;
vertices = new Vertex[renderer.TempBufferSize];
} }
public void Flush() public void Flush()
@@ -45,9 +46,9 @@ namespace OpenRA.Graphics
public void FillRect(RectangleF rect, Color color) public void FillRect(RectangleF rect, Color color)
{ {
Renderer.CurrentBatchRenderer = this; renderer.CurrentBatchRenderer = this;
if (nv + 4 > Renderer.TempBufferSize) if (nv + 4 > renderer.TempBufferSize)
Flush(); Flush();
var r = color.R / 255.0f; var r = color.R / 255.0f;
@@ -65,7 +66,7 @@ namespace OpenRA.Graphics
public void SetViewportParams(Size screen, float zoom, int2 scroll) public void SetViewportParams(Size screen, float zoom, int2 scroll)
{ {
shader.SetVec("Scroll", scroll.X, scroll.Y); 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); shader.SetVec("r2", -1, 1);
} }
} }

View File

@@ -11,19 +11,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using OpenRA.Support; using OpenRA.Support;
namespace OpenRA.Graphics 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 WorldSpriteRenderer { get; private set; }
public SpriteRenderer WorldRgbaSpriteRenderer { get; private set; } public SpriteRenderer WorldRgbaSpriteRenderer { get; private set; }
public QuadRenderer WorldQuadRenderer { get; private set; } public QuadRenderer WorldQuadRenderer { get; private set; }
@@ -32,47 +27,77 @@ namespace OpenRA.Graphics
public LineRenderer LineRenderer { get; private set; } public LineRenderer LineRenderer { get; private set; }
public SpriteRenderer RgbaSpriteRenderer { get; private set; } public SpriteRenderer RgbaSpriteRenderer { get; private set; }
public SpriteRenderer SpriteRenderer { get; private set; } public SpriteRenderer SpriteRenderer { get; private set; }
public IReadOnlyDictionary<string, SpriteFont> Fonts;
Queue<IVertexBuffer<Vertex>> tempBuffers = new Queue<IVertexBuffer<Vertex>>(); 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<string, SpriteFont> Fonts; readonly Queue<IVertexBuffer<Vertex>> tempBuffers = new Queue<IVertexBuffer<Vertex>>();
Stack<Rectangle> scissorState; readonly Stack<Rectangle> scissorState = new Stack<Rectangle>();
public Renderer() Size? lastResolution;
int2? lastScroll;
float? lastZoom;
ITexture currentPaletteTexture;
IBatchRenderer currentBatchRenderer;
public Renderer(GraphicSettings graphicSettings, ServerSettings serverSettings)
{ {
TempBufferSize = Game.Settings.Graphics.BatchSize; var resolution = GetResolution(graphicSettings);
TempBufferCount = Game.Settings.Graphics.NumTempBuffers;
SheetSize = Game.Settings.Graphics.SheetSize;
scissorState = new Stack<Rectangle>();
WorldSpriteRenderer = new SpriteRenderer(this, device.CreateShader("shp")); var rendererName = serverSettings.Dedicated ? "Null" : graphicSettings.Renderer;
WorldRgbaSpriteRenderer = new SpriteRenderer(this, device.CreateShader("rgba")); var rendererPath = Platform.ResolvePath(".", "OpenRA.Renderer." + rendererName + ".dll");
WorldLineRenderer = new LineRenderer(this, device.CreateShader("line"));
WorldVoxelRenderer = new VoxelRenderer(this, device.CreateShader("vxl")); Device = CreateDevice(Assembly.LoadFile(rendererPath), resolution.Width, resolution.Height, graphicSettings.Mode);
LineRenderer = new LineRenderer(this, device.CreateShader("line"));
WorldQuadRenderer = new QuadRenderer(this, device.CreateShader("line")); TempBufferSize = graphicSettings.BatchSize;
RgbaSpriteRenderer = new SpriteRenderer(this, device.CreateShader("rgba")); TempBufferCount = graphicSettings.NumTempBuffers;
SpriteRenderer = new SpriteRenderer(this, device.CreateShader("shp")); 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++) 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) public void InitializeFonts(Manifest m)
{ {
using (new Support.PerfTimer("SpriteFonts")) 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) public void BeginFrame(int2 scroll, float zoom)
{ {
device.Clear(); Device.Clear();
var resolutionChanged = lastResolution != Resolution; var resolutionChanged = lastResolution != Resolution;
if (resolutionChanged) if (resolutionChanged)
@@ -96,7 +121,6 @@ namespace OpenRA.Graphics
} }
} }
ITexture currentPaletteTexture;
public void SetPalette(HardwarePalette palette) public void SetPalette(HardwarePalette palette)
{ {
if (palette.Texture == currentPaletteTexture) if (palette.Texture == currentPaletteTexture)
@@ -115,8 +139,8 @@ namespace OpenRA.Graphics
public void EndFrame(IInputHandler inputHandler) public void EndFrame(IInputHandler inputHandler)
{ {
Flush(); Flush();
device.PumpInput(inputHandler); Device.PumpInput(inputHandler);
device.Present(); Device.Present();
} }
public void DrawBatch<T>(IVertexBuffer<T> vertices, public void DrawBatch<T>(IVertexBuffer<T> vertices,
@@ -124,7 +148,7 @@ namespace OpenRA.Graphics
where T : struct where T : struct
{ {
vertices.Bind(); vertices.Bind();
device.DrawPrimitives(type, firstVertex, numVertices); Device.DrawPrimitives(type, firstVertex, numVertices);
PerfHistory.Increment("batches", 1); PerfHistory.Increment("batches", 1);
} }
@@ -135,58 +159,28 @@ namespace OpenRA.Graphics
public void SetLineWidth(float width) public void SetLineWidth(float width)
{ {
device.SetLineWidth(width); Device.SetLineWidth(width);
} }
static IGraphicsDevice device; public Size Resolution { get { return Device.WindowSize; } }
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!");
}
internal IVertexBuffer<Vertex> GetTempVertexBuffer() internal IVertexBuffer<Vertex> GetTempVertexBuffer()
{ {
var ret = tempBuffers.Dequeue(); return tempBuffers.Peek();
tempBuffers.Enqueue(ret);
return ret;
} }
public interface IBatchRenderer { void Flush(); } public interface IBatchRenderer { void Flush(); }
static IBatchRenderer currentBatchRenderer; public IBatchRenderer CurrentBatchRenderer
public static IBatchRenderer CurrentBatchRenderer
{ {
get { return currentBatchRenderer; } get
{
return currentBatchRenderer;
}
set set
{ {
if (currentBatchRenderer == value) return; if (currentBatchRenderer == value)
return;
if (currentBatchRenderer != null) if (currentBatchRenderer != null)
currentBatchRenderer.Flush(); currentBatchRenderer.Flush();
currentBatchRenderer = value; currentBatchRenderer = value;
@@ -233,12 +227,21 @@ namespace OpenRA.Graphics
public void GrabWindowMouseFocus() public void GrabWindowMouseFocus()
{ {
device.GrabWindowMouseFocus(); Device.GrabWindowMouseFocus();
} }
public void ReleaseWindowMouseFocus() public void ReleaseWindowMouseFocus()
{ {
device.ReleaseWindowMouseFocus(); 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 ModData modData;
readonly Lazy<SpriteCache> spriteCache; readonly Lazy<SpriteCache> spriteCache;
@@ -141,5 +141,11 @@ namespace OpenRA.Graphics
return new ReadOnlyDictionary<string, Sequence>(unitSequences); 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 namespace OpenRA.Graphics
{ {
public class Sheet public sealed class Sheet : IDisposable
{ {
readonly object textureLock = new object(); readonly object textureLock = new object();
bool dirty; bool dirty;
@@ -178,5 +178,11 @@ namespace OpenRA.Graphics
releaseBufferOnCommit = true; releaseBufferOnCommit = true;
} }
} }
public void Dispose()
{
if (texture != null)
texture.Dispose();
}
} }
} }

View File

@@ -9,7 +9,9 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Linq;
namespace OpenRA.Graphics namespace OpenRA.Graphics
{ {
@@ -27,28 +29,34 @@ namespace OpenRA.Graphics
BGRA = 4, BGRA = 4,
} }
public class SheetBuilder public sealed class SheetBuilder : IDisposable
{ {
readonly List<Sheet> sheets = new List<Sheet>();
readonly SheetType type;
readonly Func<Sheet> allocateSheet;
Sheet current; Sheet current;
TextureChannel channel; TextureChannel channel;
SheetType type;
int rowHeight = 0; int rowHeight = 0;
Point p; 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) 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<Sheet> allocateSheet) public SheetBuilder(SheetType t, Func<Sheet> allocateSheet)
{ {
channel = TextureChannel.Red; channel = TextureChannel.Red;
type = t; type = t;
current = allocateSheet(); current = allocateSheet();
sheets.Add(current);
this.allocateSheet = allocateSheet; this.allocateSheet = allocateSheet;
} }
@@ -111,6 +119,7 @@ namespace OpenRA.Graphics
{ {
current.ReleaseBuffer(); current.ReleaseBuffer();
current = allocateSheet(); current = allocateSheet();
sheets.Add(current);
channel = TextureChannel.Red; channel = TextureChannel.Red;
} }
else else
@@ -127,5 +136,12 @@ namespace OpenRA.Graphics
} }
public Sheet Current { get { return current; } } public Sheet Current { get { return current; } }
public void Dispose()
{
foreach (var sheet in sheets)
sheet.Dispose();
sheets.Clear();
}
} }
} }

View File

@@ -22,7 +22,7 @@ namespace OpenRA.Graphics
static Library library = new Library(); static Library library = new Library();
static SheetBuilder builder; static SheetBuilder builder;
int size; readonly int size;
string name; string name;
public SpriteFont(string name, int size) 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. // A new bitmap is generated each time this property is accessed, so we do need to dispose it.
using (var bitmap = face.Glyph.Bitmap) using (var bitmap = face.Glyph.Bitmap)
{
unsafe unsafe
{ {
var p = (byte*)bitmap.Buffer; var p = (byte*)bitmap.Buffer;
@@ -136,7 +135,7 @@ namespace OpenRA.Graphics
p += bitmap.Pitch; p += bitmap.Pitch;
} }
} }
}
s.sheet.CommitData(); s.sheet.CommitData();

View File

@@ -14,11 +14,11 @@ namespace OpenRA.Graphics
{ {
public class SpriteRenderer : Renderer.IBatchRenderer public class SpriteRenderer : Renderer.IBatchRenderer
{ {
Renderer renderer; readonly Renderer renderer;
IShader shader; readonly IShader shader;
Vertex[] vertices = new Vertex[Renderer.TempBufferSize]; readonly Vertex[] vertices;
Sheet currentSheet = null; Sheet currentSheet;
BlendMode currentBlend = BlendMode.Alpha; BlendMode currentBlend = BlendMode.Alpha;
int nv = 0; int nv = 0;
@@ -26,6 +26,7 @@ namespace OpenRA.Graphics
{ {
this.renderer = renderer; this.renderer = renderer;
this.shader = shader; this.shader = shader;
vertices = new Vertex[renderer.TempBufferSize];
} }
public void Flush() public void Flush()
@@ -60,7 +61,7 @@ namespace OpenRA.Graphics
void DrawSprite(Sprite s, float2 location, int paletteIndex, float2 size) void DrawSprite(Sprite s, float2 location, int paletteIndex, float2 size)
{ {
Renderer.CurrentBatchRenderer = this; renderer.CurrentBatchRenderer = this;
if (s.sheet != currentSheet) if (s.sheet != currentSheet)
Flush(); Flush();
@@ -68,7 +69,7 @@ namespace OpenRA.Graphics
if (s.blendMode != currentBlend) if (s.blendMode != currentBlend)
Flush(); Flush();
if (nv + 4 > Renderer.TempBufferSize) if (nv + 4 > renderer.TempBufferSize)
Flush(); Flush();
currentBlend = s.blendMode; currentBlend = s.blendMode;
@@ -90,7 +91,7 @@ namespace OpenRA.Graphics
public void DrawSprite(Sprite s, float2 a, float2 b, float2 c, float2 d) public void DrawSprite(Sprite s, float2 a, float2 b, float2 c, float2 d)
{ {
Renderer.CurrentBatchRenderer = this; renderer.CurrentBatchRenderer = this;
if (s.sheet != currentSheet) if (s.sheet != currentSheet)
Flush(); Flush();
@@ -98,7 +99,7 @@ namespace OpenRA.Graphics
if (s.blendMode != currentBlend) if (s.blendMode != currentBlend)
Flush(); Flush();
if (nv + 4 > Renderer.TempBufferSize) if (nv + 4 > renderer.TempBufferSize)
Flush(); Flush();
currentSheet = s.sheet; currentSheet = s.sheet;
@@ -123,7 +124,7 @@ namespace OpenRA.Graphics
public void SetViewportParams(Size screen, float zoom, int2 scroll) public void SetViewportParams(Size screen, float zoom, int2 scroll)
{ {
shader.SetVec("Scroll", scroll.X, scroll.Y); 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); shader.SetVec("r2", -1, 1);
} }
} }

View File

@@ -8,16 +8,16 @@
*/ */
#endregion #endregion
using System;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Graphics namespace OpenRA.Graphics
{ {
class TerrainRenderer sealed class TerrainRenderer : IDisposable
{ {
IVertexBuffer<Vertex> vertexBuffer; readonly IVertexBuffer<Vertex> vertexBuffer;
readonly World world;
World world; readonly Map map;
Map map;
public TerrainRenderer(World world, WorldRenderer wr) public TerrainRenderer(World world, WorldRenderer wr)
{ {
@@ -42,7 +42,7 @@ namespace OpenRA.Graphics
public void Draw(WorldRenderer wr, Viewport viewport) public void Draw(WorldRenderer wr, Viewport viewport)
{ {
var verticesPerRow = 4*map.Bounds.Width; var verticesPerRow = 4 * map.Bounds.Width;
var cells = viewport.VisibleCells; var cells = viewport.VisibleCells;
var shape = wr.world.Map.TileShape; var shape = wr.world.Map.TileShape;
@@ -58,5 +58,10 @@ namespace OpenRA.Graphics
foreach (var r in world.WorldActor.TraitsImplementing<IRenderOverlay>()) foreach (var r in world.WorldActor.TraitsImplementing<IRenderOverlay>())
r.Render(wr); r.Render(wr);
} }
public void Dispose()
{
vertexBuffer.Dispose();
}
} }
} }

View File

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

View File

@@ -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<Pair<string,string>, Voxel> voxels; readonly List<Vertex[]> vertices = new List<Vertex[]>();
readonly Cache<Pair<string, string>, Voxel> voxels;
IVertexBuffer<Vertex> vertexBuffer; IVertexBuffer<Vertex> vertexBuffer;
List<Vertex[]> vertices;
int totalVertexCount; int totalVertexCount;
int cachedVertexCount; int cachedVertexCount;
SheetBuilder sheetBuilder;
static SheetBuilder CreateSheetBuilder() static SheetBuilder CreateSheetBuilder()
{ {
var allocated = false; var allocated = false;
@@ -50,7 +52,7 @@ namespace OpenRA.Graphics
if (allocated) if (allocated)
throw new SheetOverflowException(""); throw new SheetOverflowException("");
allocated = true; allocated = true;
return SheetBuilder.AllocateSheet(); return SheetBuilder.AllocateSheet(Game.Renderer.SheetSize);
}; };
return new SheetBuilder(SheetType.DualIndexed, allocate); return new SheetBuilder(SheetType.DualIndexed, allocate);
@@ -58,7 +60,7 @@ namespace OpenRA.Graphics
public VoxelLoader() public VoxelLoader()
{ {
voxels = new Cache<Pair<string,string>, Voxel>(LoadFile); voxels = new Cache<Pair<string, string>, Voxel>(LoadFile);
vertices = new List<Vertex[]>(); vertices = new List<Vertex[]>();
totalVertexCount = 0; totalVertexCount = 0;
cachedVertexCount = 0; cachedVertexCount = 0;
@@ -66,29 +68,28 @@ namespace OpenRA.Graphics
sheetBuilder = CreateSheetBuilder(); sheetBuilder = CreateSheetBuilder();
} }
static float[] channelSelect = { 0.75f, 0.25f, -0.25f, -0.75f }; Vertex[] GenerateSlicePlane(int su, int sv, Func<int, int, VxlElement> first, Func<int, int, VxlElement> second, Func<int, int, float[]> coord)
Vertex[] GenerateSlicePlane(int su, int sv, Func<int,int,VxlElement> first, Func<int,int,VxlElement> second, Func<int, int, float[]> coord)
{ {
var colors = new byte[su*sv]; var colors = new byte[su * sv];
var normals = new byte[su*sv]; var normals = new byte[su * sv];
var c = 0; var c = 0;
for (var v = 0; v < sv; v++) for (var v = 0; v < sv; v++)
for (var u = 0; u < su; u++) for (var u = 0; u < su; u++)
{ {
var voxel = first(u,v) ?? second(u,v); var voxel = first(u, v) ?? second(u, v);
colors[c] = voxel == null ? (byte)0 : voxel.Color; colors[c] = voxel == null ? (byte)0 : voxel.Color;
normals[c] = voxel == null ? (byte)0 : voxel.Normal; normals[c] = voxel == null ? (byte)0 : voxel.Normal;
c++; c++;
} }
var s = sheetBuilder.Allocate(new Size(su, sv)); var s = sheetBuilder.Allocate(new Size(su, sv));
Util.FastCopyIntoChannel(s, 0, colors); Util.FastCopyIntoChannel(s, 0, colors);
Util.FastCopyIntoChannel(s, 1, normals); Util.FastCopyIntoChannel(s, 1, normals);
s.sheet.CommitData(); s.sheet.CommitData();
var channelP =channelSelect[(int)s.channel]; var channelP = ChannelSelect[(int)s.channel];
var channelC = channelSelect[(int)s.channel + 1]; var channelC = ChannelSelect[(int)s.channel + 1];
return new Vertex[4] return new Vertex[4]
{ {
new Vertex(coord(0, 0), s.left, s.top, channelP, channelC), new Vertex(coord(0, 0), s.left, s.top, channelP, channelC),
@@ -100,7 +101,7 @@ namespace OpenRA.Graphics
IEnumerable<Vertex[]> GenerateSlicePlanes(VxlLimb l) IEnumerable<Vertex[]> GenerateSlicePlanes(VxlLimb l)
{ {
Func<int,int,int,VxlElement> get = (x,y,z) => Func<int, int, int, VxlElement> get = (x, y, z) =>
{ {
if (x < 0 || y < 0 || z < 0) if (x < 0 || y < 0 || z < 0)
return null; return null;
@@ -108,41 +109,41 @@ namespace OpenRA.Graphics
if (x >= l.Size[0] || y >= l.Size[1] || z >= l.Size[2]) if (x >= l.Size[0] || y >= l.Size[1] || z >= l.Size[2])
return null; 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)) if (v == null || !v.ContainsKey((byte)z))
return null; 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 // Cull slices without any visible faces
var xPlanes = new bool[l.Size[0]+1]; var xPlanes = new bool[l.Size[0] + 1];
var yPlanes = new bool[l.Size[1]+1]; var yPlanes = new bool[l.Size[1] + 1];
var zPlanes = new bool[l.Size[2]+1]; var zPlanes = new bool[l.Size[2] + 1];
for (var x = 0; x < l.Size[0]; x++) for (var x = 0; x < l.Size[0]; x++)
{ {
for (var y = 0; y < l.Size[1]; y++) for (var y = 0; y < l.Size[1]; y++)
{ {
for (var z = 0; z < l.Size[2]; z++) for (var z = 0; z < l.Size[2]; z++)
{ {
if (get(x,y,z) == null) if (get(x, y, z) == null)
continue; continue;
// Only generate a plane if it is actually visible // 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; xPlanes[x] = true;
if (!xPlanes[x+1] && get(x+1,y,z) == null) if (!xPlanes[x + 1] && get(x + 1, y, z) == null)
xPlanes[x+1] = true; 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; yPlanes[y] = true;
if (!yPlanes[y+1] && get(x,y+1,z) == null) if (!yPlanes[y + 1] && get(x, y + 1, z) == null)
yPlanes[y+1] = true; 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; zPlanes[z] = true;
if (!zPlanes[z+1] && get(x,y,z+1) == null) if (!zPlanes[z + 1] && get(x, y, z + 1) == null)
zPlanes[z+1] = true; zPlanes[z + 1] = true;
} }
} }
} }
@@ -150,23 +151,23 @@ namespace OpenRA.Graphics
for (var x = 0; x <= l.Size[0]; x++) for (var x = 0; x <= l.Size[0]; x++)
if (xPlanes[x]) if (xPlanes[x])
yield return GenerateSlicePlane(l.Size[1], l.Size[2], yield return GenerateSlicePlane(l.Size[1], l.Size[2],
(u,v) => get(x, u, v), (u, v) => get(x, u, v),
(u,v) => get(x - 1, u, v), (u, v) => get(x - 1, u, v),
(u,v) => new float[] {x, u, v}); (u, v) => new float[] { x, u, v });
for (var y = 0; y <= l.Size[1]; y++) for (var y = 0; y <= l.Size[1]; y++)
if (yPlanes[y]) if (yPlanes[y])
yield return GenerateSlicePlane(l.Size[0], l.Size[2], yield return GenerateSlicePlane(l.Size[0], l.Size[2],
(u,v) => get(u, y, v), (u, v) => get(u, y, v),
(u,v) => get(u, y - 1, v), (u, v) => get(u, y - 1, v),
(u,v) => new float[] {u, y, v}); (u, v) => new float[] { u, y, v });
for (var z = 0; z <= l.Size[2]; z++) for (var z = 0; z <= l.Size[2]; z++)
if (zPlanes[z]) if (zPlanes[z])
yield return GenerateSlicePlane(l.Size[0], l.Size[1], yield return GenerateSlicePlane(l.Size[0], l.Size[1],
(u,v) => get(u, v, z), (u, v) => get(u, v, z),
(u,v) => get(u, v, z - 1), (u, v) => get(u, v, z - 1),
(u,v) => new float[] {u, v, z}); (u, v) => new float[] { u, v, z });
} }
public VoxelRenderData GenerateRenderData(VxlLimb l) public VoxelRenderData GenerateRenderData(VxlLimb l)
@@ -195,6 +196,8 @@ namespace OpenRA.Graphics
public void RefreshBuffer() public void RefreshBuffer()
{ {
if (vertexBuffer != null)
vertexBuffer.Dispose();
vertexBuffer = Game.Renderer.Device.CreateVertexBuffer(totalVertexCount); vertexBuffer = Game.Renderer.Device.CreateVertexBuffer(totalVertexCount);
vertexBuffer.SetData(vertices.SelectMany(v => v).ToArray(), totalVertexCount); vertexBuffer.SetData(vertices.SelectMany(v => v).ToArray(), totalVertexCount);
cachedVertexCount = totalVertexCount; cachedVertexCount = totalVertexCount;
@@ -210,7 +213,7 @@ namespace OpenRA.Graphics
} }
} }
Voxel LoadFile(Pair<string,string> files) Voxel LoadFile(Pair<string, string> files)
{ {
VxlReader vxl; VxlReader vxl;
HvaReader hva; HvaReader hva;
@@ -230,5 +233,12 @@ namespace OpenRA.Graphics
{ {
sheetBuilder.Current.ReleaseBuffer(); sheetBuilder.Current.ReleaseBuffer();
} }
public void Dispose()
{
if (vertexBuffer != null)
vertexBuffer.Dispose();
sheetBuilder.Dispose();
}
} }
} }

View File

@@ -32,33 +32,30 @@ namespace OpenRA.Graphics
} }
} }
public class VoxelRenderer public sealed class VoxelRenderer : IDisposable
{ {
Renderer renderer; // Static constants
IShader shader; 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<Sheet, IFrameBuffer> mappedBuffers = new Dictionary<Sheet, IFrameBuffer>();
readonly Stack<KeyValuePair<Sheet, IFrameBuffer>> unmappedBuffers = new Stack<KeyValuePair<Sheet, IFrameBuffer>>();
readonly List<Pair<Sheet, Action>> doRender = new List<Pair<Sheet, Action>>();
SheetBuilder sheetBuilder; SheetBuilder sheetBuilder;
Dictionary<Sheet, IFrameBuffer> mappedBuffers;
Stack<KeyValuePair<Sheet, IFrameBuffer>> unmappedBuffers;
List<Pair<Sheet, Action>> 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) public VoxelRenderer(Renderer renderer, IShader shader)
{ {
this.renderer = renderer; this.renderer = renderer;
this.shader = shader; this.shader = shader;
mappedBuffers = new Dictionary<Sheet, IFrameBuffer>();
unmappedBuffers = new Stack<KeyValuePair<Sheet, IFrameBuffer>>();
doRender = new List<Pair<Sheet, Action>>();
} }
public void SetPalette(ITexture palette) public void SetPalette(ITexture palette)
@@ -68,12 +65,12 @@ namespace OpenRA.Graphics
public void SetViewportParams(Size screen, float zoom, int2 scroll) public void SetViewportParams(Size screen, float zoom, int2 scroll)
{ {
var a = 2f / Renderer.SheetSize; var a = 2f / renderer.SheetSize;
var view = new float[] var view = new float[]
{ {
a, 0, 0, 0, a, 0, 0, 0,
0, -a, 0, 0, 0, -a, 0, 0,
0, 0, -2*a, 0, 0, 0, -2 * a, 0,
-1, 1, 0, 1 -1, 1, 0, 1
}; };
@@ -111,7 +108,7 @@ namespace OpenRA.Graphics
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]); var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
var worldTransform = v.RotationFunc().Aggregate(Util.IdentityMatrix(), 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(scaleTransform, worldTransform);
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform); worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
@@ -128,18 +125,18 @@ namespace OpenRA.Graphics
} }
// Inflate rects to ensure rendering is within bounds // Inflate rects to ensure rendering is within bounds
tl -= spritePadding; tl -= SpritePadding;
br += spritePadding; br += SpritePadding;
stl -= spritePadding; stl -= SpritePadding;
sbr += spritePadding; sbr += SpritePadding;
// Corners of the shadow quad, in shadow-space // Corners of the shadow quad, in shadow-space
var corners = new float[][] var corners = new float[][]
{ {
new float[] {stl.X, stl.Y, 0, 1}, new[] { stl.X, stl.Y, 0, 1 },
new float[] {sbr.X, sbr.Y, 0, 1}, new[] { sbr.X, sbr.Y, 0, 1 },
new float[] {sbr.X, stl.Y, 0, 1}, new[] { sbr.X, stl.Y, 0, 1 },
new float[] {stl.X, sbr.Y, 0, 1} new[] { stl.X, sbr.Y, 0, 1 }
}; };
var shadowScreenTransform = Util.MatrixMultiply(cameraTransform, invShadowTransform); var shadowScreenTransform = Util.MatrixMultiply(cameraTransform, invShadowTransform);
@@ -148,8 +145,8 @@ namespace OpenRA.Graphics
for (var j = 0; j < 4; j++) for (var j = 0; j < 4; j++)
{ {
// Project to ground plane // Project to ground plane
corners[j][2] = -(corners[j][1]*shadowGroundNormal[1]/shadowGroundNormal[2] + corners[j][2] = -(corners[j][1] * shadowGroundNormal[1] / shadowGroundNormal[2] +
corners[j][0]*shadowGroundNormal[0]/shadowGroundNormal[2]); corners[j][0] * shadowGroundNormal[0] / shadowGroundNormal[2]);
// Rotate to camera-space // Rotate to camera-space
corners[j] = Util.MatrixVectorMultiply(shadowScreenTransform, corners[j]); 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 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 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 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 shadowTranslateMtx = Util.TranslationMatrix(shadowCenter.X - shadowSpriteOffset.X, renderer.SheetSize - (shadowCenter.Y - shadowSpriteOffset.Y), 0);
var correctionTransform = Util.MatrixMultiply(translateMtx, flipMtx); var correctionTransform = Util.MatrixMultiply(translateMtx, FlipMtx);
var shadowCorrectionTransform = Util.MatrixMultiply(shadowTranslateMtx, shadowScaleFlipMtx); var shadowCorrectionTransform = Util.MatrixMultiply(shadowTranslateMtx, ShadowScaleFlipMtx);
doRender.Add(Pair.New<Sheet, Action>(sprite.sheet, () => doRender.Add(Pair.New<Sheet, Action>(sprite.sheet, () =>
{ {
@@ -183,7 +180,7 @@ namespace OpenRA.Graphics
var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]); var offsetTransform = Util.TranslationMatrix(offsetVec[0], offsetVec[1], offsetVec[2]);
var rotations = v.RotationFunc().Aggregate(Util.IdentityMatrix(), 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); var worldTransform = Util.MatrixMultiply(scaleTransform, rotations);
worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform); worldTransform = Util.MatrixMultiply(offsetTransform, worldTransform);
@@ -209,21 +206,21 @@ namespace OpenRA.Graphics
// Disable shadow normals by forcing zero diffuse and identity ambient light // Disable shadow normals by forcing zero diffuse and identity ambient light
Render(rd, Util.MatrixMultiply(shadow, t), lightDirection, 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); 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) static void CalculateSpriteGeometry(float2 tl, float2 br, float scale, out Size size, out int2 offset)
{ {
var width = (int)(scale*(br.X - tl.X)); var width = (int)(scale * (br.X - tl.X));
var height = (int)(scale*(br.Y - tl.Y)); var height = (int)(scale * (br.Y - tl.Y));
offset = (0.5f*scale*(br + tl)).ToInt2(); offset = (0.5f * scale * (br + tl)).ToInt2();
// Width and height must be even to avoid rendering glitches // Width and height must be even to avoid rendering glitches
if ((width & 1) == 1) if ((width & 1) == 1)
@@ -236,14 +233,14 @@ namespace OpenRA.Graphics
static float[] ExtractRotationVector(float[] mtx) static float[] ExtractRotationVector(float[] mtx)
{ {
var tVec = Util.MatrixVectorMultiply(mtx, zVector); var tVec = Util.MatrixVectorMultiply(mtx, ZVector);
var tOrigin = Util.MatrixVectorMultiply(mtx, zeroVector); var tOrigin = Util.MatrixVectorMultiply(mtx, ZeroVector);
tVec[0] -= tOrigin[0]*tVec[3]/tOrigin[3]; tVec[0] -= tOrigin[0] * tVec[3] / tOrigin[3];
tVec[1] -= tOrigin[1]*tVec[3]/tOrigin[3]; tVec[1] -= tOrigin[1] * tVec[3] / tOrigin[3];
tVec[2] -= tOrigin[2]*tVec[3]/tOrigin[3]; tVec[2] -= tOrigin[2] * tVec[3] / tOrigin[3];
// Renormalize // 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[0] /= w;
tVec[1] /= w; tVec[1] /= w;
tVec[2] /= w; tVec[2] /= w;
@@ -330,12 +327,24 @@ namespace OpenRA.Graphics
return kv.Key; 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 framebuffer = renderer.Device.CreateFrameBuffer(size);
var sheet = new Sheet(framebuffer.Texture); var sheet = new Sheet(framebuffer.Texture);
mappedBuffers.Add(sheet, framebuffer); mappedBuffers.Add(sheet, framebuffer);
return sheet; 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,24 +29,22 @@ namespace OpenRA.Graphics
} }
} }
public class WorldRenderer public sealed class WorldRenderer : IDisposable
{ {
public readonly World world; public readonly World world;
public readonly Theater Theater; public readonly Theater Theater;
public Viewport Viewport { get; private set; } public Viewport Viewport { get; private set; }
readonly HardwarePalette palette = new HardwarePalette();
readonly Dictionary<string, PaletteReference> palettes = new Dictionary<string, PaletteReference>();
readonly TerrainRenderer terrainRenderer; readonly TerrainRenderer terrainRenderer;
readonly HardwarePalette palette;
readonly Dictionary<string, PaletteReference> palettes;
readonly Lazy<DeveloperMode> devTrait; readonly Lazy<DeveloperMode> devTrait;
internal WorldRenderer(World world) internal WorldRenderer(World world)
{ {
this.world = world; this.world = world;
Viewport = new Viewport(this, world.Map); Viewport = new Viewport(this, world.Map);
palette = new HardwarePalette();
palettes = new Dictionary<string, PaletteReference>();
foreach (var pal in world.traitDict.ActorsWithTrait<ILoadsPalettes>()) foreach (var pal in world.traitDict.ActorsWithTrait<ILoadsPalettes>())
pal.Trait.LoadPalettes(this); pal.Trait.LoadPalettes(this);
@@ -251,5 +249,12 @@ namespace OpenRA.Graphics
var ts = Game.modData.Manifest.TileSize; var ts = Game.modData.Manifest.TileSize;
return new WPos(1024 * screenPx.X / ts.Width, 1024 * screenPx.Y / ts.Height, 0); 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 namespace OpenRA
{ {
public class MapCache : IEnumerable<MapPreview> public sealed class MapCache : IEnumerable<MapPreview>, IDisposable
{ {
public static readonly MapPreview UnknownMap = new MapPreview(null, null); public static readonly MapPreview UnknownMap = new MapPreview(null, null);
readonly Cache<string, MapPreview> previews; readonly Cache<string, MapPreview> previews;
@@ -163,7 +163,13 @@ namespace OpenRA
foreach (var p in todo) foreach (var p in todo)
{ {
// The rendering is thread safe because it only reads from the passed instances and writes to a new bitmap // 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: // Note: this is not generally thread-safe, but it works here because:
// (a) This worker is the only thread writing to this sheet // (a) This worker is the only thread writing to this sheet
// (b) The main thread is the only thread reading this sheet // (b) The main thread is the only thread reading this sheet
@@ -172,7 +178,15 @@ namespace OpenRA
// the next render cycle. // the next render cycle.
// (d) Any partially written bytes from the next minimap is in an // (d) Any partially written bytes from the next minimap is in an
// unallocated area, and will be committed in the next cycle. // 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. // Yuck... But this helps the UI Jank when opening the map selector significantly.
Thread.Sleep(Environment.ProcessorCount == 1 ? 25 : 5); Thread.Sleep(Environment.ProcessorCount == 1 ? 25 : 5);
@@ -226,5 +240,10 @@ namespace OpenRA
{ {
return GetEnumerator(); return GetEnumerator();
} }
public void Dispose()
{
sheetBuilder.Dispose();
}
} }
} }

View File

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

View File

@@ -55,6 +55,7 @@ namespace OpenRA.Lint
FieldLoader.UnknownFieldAction = (s, f) => EmitError("FieldLoader: Missing field `{0}` on `{1}`".F(s, f.Name)); FieldLoader.UnknownFieldAction = (s, f) => EmitError("FieldLoader: Missing field `{0}` on `{1}`".F(s, f.Name));
AppDomain.CurrentDomain.AssemblyResolve += GlobalFileSystem.ResolveAssembly; AppDomain.CurrentDomain.AssemblyResolve += GlobalFileSystem.ResolveAssembly;
Game.InitializeSettings(Arguments.Empty);
Game.modData = new ModData(mod); Game.modData = new ModData(mod);
IEnumerable<Map> maps; IEnumerable<Map> maps;

View File

@@ -11,24 +11,23 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Widgets; using OpenRA.Widgets;
namespace OpenRA.Mods.Cnc namespace OpenRA.Mods.Cnc
{ {
public class CncLoadScreen : ILoadScreen public sealed class CncLoadScreen : ILoadScreen
{ {
Dictionary<string, string> loadInfo; Dictionary<string, string> loadInfo;
Stopwatch loadTimer = Stopwatch.StartNew(); Stopwatch loadTimer = Stopwatch.StartNew();
Sheet sheet;
Sprite[] ss; Sprite[] ss;
int loadTick; int loadTick;
float2 nodPos, gdiPos, evaPos; float2 nodPos, gdiPos, evaPos;
Sprite nodLogo, gdiLogo, evaLogo, brightBlock, dimBlock; Sprite nodLogo, gdiLogo, evaLogo, brightBlock, dimBlock;
Rectangle bounds; Rectangle bounds;
Renderer r; Renderer r;
NullInputHandler nih = new NullInputHandler(); readonly NullInputHandler nih = new NullInputHandler();
public void Init(Manifest m, Dictionary<string, string> info) public void Init(Manifest m, Dictionary<string, string> info)
{ {
@@ -39,31 +38,31 @@ namespace OpenRA.Mods.Cnc
r = Game.Renderer; r = Game.Renderer;
if (r == null) return; if (r == null) return;
var s = new Sheet(Platform.ResolvePath(loadInfo["Image"])); sheet = new Sheet(Platform.ResolvePath(loadInfo["Image"]));
var res = r.Resolution; var res = r.Resolution;
bounds = new Rectangle(0, 0, res.Width, res.Height); bounds = new Rectangle(0, 0, res.Width, res.Height);
ss = new[] ss = new[]
{ {
new Sprite(s, new Rectangle(161, 128, 62, 33), TextureChannel.Alpha), new Sprite(sheet, new Rectangle(161, 128, 62, 33), TextureChannel.Alpha),
new Sprite(s, new Rectangle(161, 223, 62, 33), TextureChannel.Alpha), new Sprite(sheet, new Rectangle(161, 223, 62, 33), TextureChannel.Alpha),
new Sprite(s, new Rectangle(128, 161, 33, 62), TextureChannel.Alpha), new Sprite(sheet, new Rectangle(128, 161, 33, 62), TextureChannel.Alpha),
new Sprite(s, new Rectangle(223, 161, 33, 62), TextureChannel.Alpha), new Sprite(sheet, new Rectangle(223, 161, 33, 62), TextureChannel.Alpha),
new Sprite(s, new Rectangle(128, 128, 33, 33), TextureChannel.Alpha), new Sprite(sheet, new Rectangle(128, 128, 33, 33), TextureChannel.Alpha),
new Sprite(s, new Rectangle(223, 128, 33, 33), TextureChannel.Alpha), new Sprite(sheet, new Rectangle(223, 128, 33, 33), TextureChannel.Alpha),
new Sprite(s, new Rectangle(128, 223, 33, 33), TextureChannel.Alpha), new Sprite(sheet, new Rectangle(128, 223, 33, 33), TextureChannel.Alpha),
new Sprite(s, new Rectangle(223, 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); nodLogo = new Sprite(sheet, new Rectangle(0, 256, 256, 256), TextureChannel.Alpha);
gdiLogo = new Sprite(s, new Rectangle(256, 256, 256, 256), TextureChannel.Alpha); gdiLogo = new Sprite(sheet, new Rectangle(256, 256, 256, 256), TextureChannel.Alpha);
evaLogo = new Sprite(s, new Rectangle(256, 64, 128, 64), 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); nodPos = new float2(bounds.Width / 2 - 384, bounds.Height / 2 - 128);
gdiPos = new float2(bounds.Width / 2 + 128, bounds.Height / 2 - 128); gdiPos = new float2(bounds.Width / 2 + 128, bounds.Height / 2 - 128);
evaPos = new float2(bounds.Width - 43 - 128, 43); evaPos = new float2(bounds.Width - 43 - 128, 43);
brightBlock = new Sprite(s, new Rectangle(320, 0, 16, 35), TextureChannel.Alpha); brightBlock = new Sprite(sheet, new Rectangle(320, 0, 16, 35), TextureChannel.Alpha);
dimBlock = new Sprite(s, new Rectangle(336, 0, 16, 35), TextureChannel.Alpha); dimBlock = new Sprite(sheet, new Rectangle(336, 0, 16, 35), TextureChannel.Alpha);
versionText = m.Mod.Version; versionText = m.Mod.Version;
} }
@@ -123,5 +122,11 @@ namespace OpenRA.Mods.Cnc
{ {
Game.TestAndContinue(); Game.TestAndContinue();
} }
public void Dispose()
{
if (sheet != null)
sheet.Dispose();
}
} }
} }

View File

@@ -11,20 +11,19 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Widgets; using OpenRA.Widgets;
namespace OpenRA.Mods.Common.LoadScreens namespace OpenRA.Mods.Common.LoadScreens
{ {
public class DefaultLoadScreen : ILoadScreen public sealed class DefaultLoadScreen : ILoadScreen
{ {
Stopwatch lastUpdate = Stopwatch.StartNew(); Stopwatch lastUpdate = Stopwatch.StartNew();
Renderer r; Renderer r;
Rectangle stripeRect; Rectangle stripeRect;
float2 logoPos; float2 logoPos;
Sheet sheet;
Sprite stripe, logo; Sprite stripe, logo;
string[] messages; string[] messages;
@@ -37,9 +36,9 @@ namespace OpenRA.Mods.Common.LoadScreens
return; return;
messages = info["Text"].Split(','); messages = info["Text"].Split(',');
var s = new Sheet(Platform.ResolvePath(info["Image"])); sheet = new Sheet(Platform.ResolvePath(info["Image"]));
logo = new Sprite(s, new Rectangle(0, 0, 256, 256), TextureChannel.Alpha); logo = new Sprite(sheet, new Rectangle(0, 0, 256, 256), TextureChannel.Alpha);
stripe = new Sprite(s, new Rectangle(256, 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); 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); logoPos = new float2(r.Resolution.Width / 2 - 128, r.Resolution.Height / 2 - 128);
} }
@@ -71,5 +70,11 @@ namespace OpenRA.Mods.Common.LoadScreens
{ {
Game.TestAndContinue(); Game.TestAndContinue();
} }
public void Dispose()
{
if (sheet != null)
sheet.Dispose();
}
} }
} }

View File

@@ -15,7 +15,7 @@ using OpenRA.Widgets;
namespace OpenRA.Mods.Common.LoadScreens namespace OpenRA.Mods.Common.LoadScreens
{ {
public class ModChooserLoadScreen : ILoadScreen public sealed class ModChooserLoadScreen : ILoadScreen
{ {
Sprite sprite; Sprite sprite;
Rectangle bounds; Rectangle bounds;
@@ -43,5 +43,11 @@ namespace OpenRA.Mods.Common.LoadScreens
{ {
Ui.LoadWidget("MODCHOOSER", Ui.Root, new WidgetArgs()); Ui.LoadWidget("MODCHOOSER", Ui.Root, new WidgetArgs());
} }
public void Dispose()
{
if (sprite != null)
sprite.sheet.Dispose();
}
} }
} }

View File

@@ -13,7 +13,7 @@ using OpenRA.Widgets;
namespace OpenRA.Mods.Common.LoadScreens namespace OpenRA.Mods.Common.LoadScreens
{ {
public class NullLoadScreen : ILoadScreen public sealed class NullLoadScreen : ILoadScreen
{ {
public void Init(Manifest m, Dictionary<string, string> info) { } public void Init(Manifest m, Dictionary<string, string> info) { }
@@ -31,5 +31,9 @@ namespace OpenRA.Mods.Common.LoadScreens
{ {
Ui.ResetAll(); Ui.ResetAll();
} }
public void Dispose()
{
}
} }
} }

View File

@@ -20,15 +20,16 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
public class ModBrowserLogic public class ModBrowserLogic
{ {
Widget modList; readonly Widget modList;
ButtonWidget modTemplate; readonly ButtonWidget modTemplate;
ModMetadata[] allMods; readonly ModMetadata[] allMods;
readonly Dictionary<string, Sprite> previews = new Dictionary<string, Sprite>();
readonly Dictionary<string, Sprite> logos = new Dictionary<string, Sprite>();
readonly SheetBuilder sheetBuilder;
ModMetadata selectedMod; ModMetadata selectedMod;
string selectedAuthor; string selectedAuthor;
string selectedDescription; string selectedDescription;
int modOffset = 0; int modOffset = 0;
Dictionary<string, Sprite> previews;
Dictionary<string, Sprite> logos;
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
public ModBrowserLogic(Widget widget) public ModBrowserLogic(Widget widget)
@@ -63,9 +64,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return ret; return ret;
}; };
var sheetBuilder = new SheetBuilder(SheetType.BGRA); sheetBuilder = new SheetBuilder(SheetType.BGRA);
previews = new Dictionary<string, Sprite>();
logos = new Dictionary<string, Sprite>();
allMods = ModMetadata.AllMods.Values.Where(m => m.Id != "modchooser") allMods = ModMetadata.AllMods.Values.Where(m => m.Id != "modchooser")
.OrderBy(m => m.Title) .OrderBy(m => m.Title)
.ToArray(); .ToArray();
@@ -75,21 +74,17 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
try try
{ {
var preview = new Bitmap(Platform.ResolvePath(".", "mods", mod.Id, "preview.png")); using (var preview = new Bitmap(Platform.ResolvePath(".", "mods", mod.Id, "preview.png")))
if (preview.Width != 296 || preview.Height != 196) if (preview.Width == 296 && preview.Height == 196)
continue; previews.Add(mod.Id, sheetBuilder.Add(preview));
previews.Add(mod.Id, sheetBuilder.Add(preview));
} }
catch (Exception) { } catch (Exception) { }
try try
{ {
var logo = new Bitmap(Platform.ResolvePath(".", "mods", mod.Id, "logo.png")); using (var logo = new Bitmap(Platform.ResolvePath(".", "mods", mod.Id, "logo.png")))
if (logo.Width != 96 || logo.Height != 96) if (logo.Width == 96 && logo.Height == 96)
continue; logos.Add(mod.Id, sheetBuilder.Add(logo));
logos.Add(mod.Id, sheetBuilder.Add(logo));
} }
catch (Exception) { } catch (Exception) { }
} }
@@ -155,11 +150,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
modOffset = selectedIndex - 4; modOffset = selectedIndex - 4;
} }
static void LoadMod(ModMetadata mod) void LoadMod(ModMetadata mod)
{ {
Game.RunAfterTick(() => Game.RunAfterTick(() =>
{ {
Ui.CloseWindow(); Ui.CloseWindow();
sheetBuilder.Dispose();
Game.InitializeMod(mod.Id, null); Game.InitializeMod(mod.Id, null);
}); });
} }

View File

@@ -78,7 +78,7 @@ namespace OpenRA.Renderer.Null
public void Render(Action a) { } public void Render(Action a) { }
} }
public class NullTexture : ITexture public sealed class NullTexture : ITexture
{ {
public TextureScaleFilter ScaleFilter { get { return TextureScaleFilter.Nearest; } set { } } public TextureScaleFilter ScaleFilter { get { return TextureScaleFilter.Nearest; } set { } }
public void SetData(Bitmap bitmap) { } public void SetData(Bitmap bitmap) { }
@@ -86,18 +86,21 @@ namespace OpenRA.Renderer.Null
public void SetData(byte[] colors, int width, int height) { } public void SetData(byte[] colors, int width, int height) { }
public byte[] GetData() { return new byte[0]; } public byte[] GetData() { return new byte[0]; }
public Size Size { get { return new Size(0, 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 Bind() { }
public void Unbind() { } public void Unbind() { }
public ITexture Texture { get { return new NullTexture(); } } public ITexture Texture { get { return new NullTexture(); } }
public void Dispose() { }
} }
class NullVertexBuffer<T> : IVertexBuffer<T> sealed class NullVertexBuffer<T> : IVertexBuffer<T>
{ {
public void Bind() { } public void Bind() { }
public void SetData(T[] vertices, int length) { } public void SetData(T[] vertices, int length) { }
public void Dispose() { }
} }
} }

View File

@@ -16,11 +16,12 @@ using OpenTK.Graphics.OpenGL;
namespace OpenRA.Renderer.Sdl2 namespace OpenRA.Renderer.Sdl2
{ {
public class FrameBuffer : IFrameBuffer public sealed class FrameBuffer : IFrameBuffer
{ {
Texture texture; Texture texture;
Size size; Size size;
int framebuffer, depth; int framebuffer, depth;
bool disposed;
public FrameBuffer(Size size) public FrameBuffer(Size size)
{ {
@@ -81,16 +82,6 @@ namespace OpenRA.Renderer.Sdl2
return v; 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]; int[] cv = new int[4];
public void Bind() public void Bind()
{ {
@@ -120,5 +111,29 @@ namespace OpenRA.Renderer.Sdl2
} }
public ITexture Texture { get { return texture; } } 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();
}
} }
} }

View File

@@ -16,7 +16,7 @@ using OpenTK.Graphics.OpenGL;
namespace OpenRA.Renderer.Sdl2 namespace OpenRA.Renderer.Sdl2
{ {
public class Texture : ITexture public sealed class Texture : ITexture
{ {
int texture; int texture;
TextureScaleFilter scaleFilter; TextureScaleFilter scaleFilter;
@@ -26,6 +26,8 @@ namespace OpenRA.Renderer.Sdl2
public Size Size { get { return size; } } public Size Size { get { return size; } }
bool disposed;
public TextureScaleFilter ScaleFilter public TextureScaleFilter ScaleFilter
{ {
get get
@@ -55,9 +57,6 @@ namespace OpenRA.Renderer.Sdl2
SetData(bitmap); SetData(bitmap);
} }
void FinalizeInner() { GL.DeleteTextures(1, ref texture); }
~Texture() { Game.RunAfterTick(FinalizeInner); }
void PrepareTexture() void PrepareTexture()
{ {
ErrorHandler.CheckGlError(); ErrorHandler.CheckGlError();
@@ -131,6 +130,7 @@ namespace OpenRA.Renderer.Sdl2
bitmap = new Bitmap(bitmap, bitmap.Size.NextPowerOf2()); bitmap = new Bitmap(bitmap, bitmap.Size.NextPowerOf2());
allocatedBitmap = true; allocatedBitmap = true;
} }
try try
{ {
size = new Size(bitmap.Width, bitmap.Height); 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); 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero);
ErrorHandler.CheckGlError(); 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);
}
} }
} }

View File

@@ -14,11 +14,12 @@ using OpenTK.Graphics.OpenGL;
namespace OpenRA.Renderer.Sdl2 namespace OpenRA.Renderer.Sdl2
{ {
public class VertexBuffer<T> : IVertexBuffer<T> public sealed class VertexBuffer<T> : IVertexBuffer<T>
where T : struct where T : struct
{ {
static readonly int VertexSize = Marshal.SizeOf(typeof(T)); static readonly int VertexSize = Marshal.SizeOf(typeof(T));
int buffer; int buffer;
bool disposed;
public VertexBuffer(int size) public VertexBuffer(int size)
{ {
@@ -52,7 +53,23 @@ namespace OpenRA.Renderer.Sdl2
ErrorHandler.CheckGlError(); ErrorHandler.CheckGlError();
} }
void FinalizeInner() { GL.DeleteBuffers(1, ref buffer); } ~VertexBuffer()
~VertexBuffer() { Game.RunAfterTick(FinalizeInner); } {
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);
}
} }
} }

View File

@@ -38,6 +38,7 @@ namespace OpenRA.Utility
return; return;
} }
Game.InitializeSettings(Arguments.Empty);
var modData = new ModData(modName); var modData = new ModData(modName);
args = args.Skip(1).ToArray(); args = args.Skip(1).ToArray();
var actions = new Dictionary<string, Action<ModData, string[]>>(); var actions = new Dictionary<string, Action<ModData, string[]>>();