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

@@ -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();

View File

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

View File

@@ -17,8 +17,8 @@ namespace OpenRA.Graphics
{
struct Collection
{
public string src;
public Dictionary<string, MappedImage> regions;
public string Src;
public Dictionary<string, MappedImage> Regions;
}
static Dictionary<string, Collection> collections;
@@ -27,6 +27,8 @@ namespace OpenRA.Graphics
public static void Initialize(IEnumerable<string> chromeFiles)
{
Deinitialize();
collections = new Dictionary<string, Collection>();
cachedSheets = new Dictionary<string, Sheet>();
cachedSprites = new Dictionary<string, Dictionary<string, Sprite>>();
@@ -37,6 +39,17 @@ namespace OpenRA.Graphics
LoadCollection(c.Key, c.Value);
}
public static void Deinitialize()
{
if (cachedSheets != null)
foreach (var sheet in cachedSheets.Values)
sheet.Dispose();
collections = null;
cachedSheets = null;
cachedSprites = null;
}
public static void Save(string file)
{
var root = new List<MiniYamlNode>();
@@ -49,10 +62,10 @@ namespace OpenRA.Graphics
static MiniYaml SaveCollection(Collection collection)
{
var root = new List<MiniYamlNode>();
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<string, Sprite>();
cachedSprites.Add(collectionName, cachedCollection);
}
var image = mi.GetImage(sheet);
cachedCollection.Add(imageName, image);

View File

@@ -16,11 +16,12 @@ using OpenRA.Primitives;
namespace OpenRA.Graphics
{
public class CursorProvider
public sealed class CursorProvider : IDisposable
{
HardwarePalette palette;
Dictionary<string, CursorSequence> cursors;
Cache<string, PaletteReference> palettes;
readonly HardwarePalette palette = new HardwarePalette();
readonly Dictionary<string, CursorSequence> cursors = new Dictionary<string, CursorSequence>();
readonly Cache<string, PaletteReference> 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<string, CursorSequence>();
palettes = new Cache<string, PaletteReference>(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();
}
}
}

View File

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

View File

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

View File

@@ -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);
}
}

View File

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

View File

@@ -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);
}
}

View File

@@ -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<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;
Stack<Rectangle> scissorState;
readonly Queue<IVertexBuffer<Vertex>> tempBuffers = new Queue<IVertexBuffer<Vertex>>();
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;
TempBufferCount = Game.Settings.Graphics.NumTempBuffers;
SheetSize = Game.Settings.Graphics.SheetSize;
scissorState = new Stack<Rectangle>();
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<T>(IVertexBuffer<T> 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<Vertex> 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();
}
}
}

View File

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

View File

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

View File

@@ -9,7 +9,9 @@
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace OpenRA.Graphics
{
@@ -27,28 +29,34 @@ namespace OpenRA.Graphics
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;
TextureChannel channel;
SheetType type;
int rowHeight = 0;
Point p;
Func<Sheet> allocateSheet;
public static Sheet AllocateSheet()
public static Sheet AllocateSheet(int sheetSize)
{
return new Sheet(new Size(Renderer.SheetSize, Renderer.SheetSize));
return new Sheet(new Size(sheetSize, sheetSize));
}
public SheetBuilder(SheetType t)
: this(t, AllocateSheet) { }
: this(t, Game.Settings.Graphics.SheetSize) { }
public SheetBuilder(SheetType t, int sheetSize)
: this(t, () => AllocateSheet(sheetSize)) { }
public SheetBuilder(SheetType t, Func<Sheet> allocateSheet)
{
channel = TextureChannel.Red;
type = t;
current = allocateSheet();
sheets.Add(current);
this.allocateSheet = allocateSheet;
}
@@ -111,6 +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();
}
}
}

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -8,16 +8,16 @@
*/
#endregion
using System;
using OpenRA.Traits;
namespace OpenRA.Graphics
{
class TerrainRenderer
sealed class TerrainRenderer : IDisposable
{
IVertexBuffer<Vertex> vertexBuffer;
World world;
Map map;
readonly IVertexBuffer<Vertex> 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<IRenderOverlay>())
r.Render(wr);
}
public void Dispose()
{
vertexBuffer.Dispose();
}
}
}

View File

@@ -16,11 +16,11 @@ using OpenRA.FileSystem;
namespace OpenRA.Graphics
{
public class Theater
public sealed class Theater : IDisposable
{
SheetBuilder sheetBuilder;
Dictionary<ushort, Sprite[]> templates;
Sprite missingTile;
readonly Dictionary<ushort, Sprite[]> templates = new Dictionary<ushort, Sprite[]>();
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();
}
}
}

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;
List<Vertex[]> 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<Pair<string,string>, Voxel>(LoadFile);
voxels = new Cache<Pair<string, string>, Voxel>(LoadFile);
vertices = new List<Vertex[]>();
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<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 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<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)
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<string,string> files)
Voxel LoadFile(Pair<string, string> 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();
}
}
}

View File

@@ -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<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;
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)
{
this.renderer = renderer;
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)
@@ -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<Sheet, Action>(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();
}
}
}

View File

@@ -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<string, PaletteReference> palettes = new Dictionary<string, PaletteReference>();
readonly TerrainRenderer terrainRenderer;
readonly HardwarePalette palette;
readonly Dictionary<string, PaletteReference> palettes;
readonly Lazy<DeveloperMode> devTrait;
internal WorldRenderer(World world)
{
this.world = world;
Viewport = new Viewport(this, world.Map);
palette = new HardwarePalette();
palettes = new Dictionary<string, PaletteReference>();
foreach (var pal in world.traitDict.ActorsWithTrait<ILoadsPalettes>())
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();
}
}
}

View File

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

View File

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