Reduce working set by releasing buffers for sheets.
Sheets carry a managed buffer of data that allows updates to be made without having to constantly fetch and set data to the texture memory of the video card. This is useful for things like SheetBuilder which make small progressive changes to sheets. However these buffers are often large and are kept alive because sheets are referenced by the sprites that use them. If this buffer is explicitly null'ed when it is no longer needed then the GC can reclaim it. Sometimes a buffer need not even be created because the object using the sheet only works on the texture directly anyway. In practise, this reduced memory consumed by such buffers from ~165 MiB to ~112 MiB (at the start of a new RA skirmish mission).
This commit is contained in:
@@ -46,6 +46,7 @@ namespace OpenRA.Graphics
|
||||
var spriteLoader = new SpriteLoader(new string[0], new SheetBuilder(SheetType.Indexed));
|
||||
foreach (var s in nodesDict["Cursors"].Nodes)
|
||||
LoadSequencesForCursor(spriteLoader, s.Key, s.Value);
|
||||
spriteLoader.SheetBuilder.Current.ReleaseBuffer();
|
||||
|
||||
palette.Initialize();
|
||||
}
|
||||
|
||||
@@ -18,18 +18,21 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
public class Sheet
|
||||
{
|
||||
ITexture texture;
|
||||
readonly object textureLock = new object();
|
||||
bool dirty;
|
||||
readonly byte[] data;
|
||||
readonly object dirtyLock = new object();
|
||||
bool releaseBufferOnCommit;
|
||||
ITexture texture;
|
||||
byte[] data;
|
||||
|
||||
public readonly Size Size;
|
||||
public byte[] Data { get { return data ?? texture.GetData(); } }
|
||||
public bool Buffered { get { return data != null; } }
|
||||
|
||||
public Sheet(Size size)
|
||||
public Sheet(Size size, bool buffered)
|
||||
{
|
||||
Size = size;
|
||||
data = new byte[4 * Size.Width * Size.Height];
|
||||
if (buffered)
|
||||
data = new byte[4 * Size.Width * Size.Height];
|
||||
}
|
||||
|
||||
public Sheet(ITexture texture)
|
||||
@@ -54,6 +57,8 @@ namespace OpenRA.Graphics
|
||||
Marshal.Copy(IntPtr.Add(bd.Scan0, y * bd.Stride), data, y * dataStride, dataStride);
|
||||
bitmap.UnlockBits(bd);
|
||||
}
|
||||
|
||||
ReleaseBuffer();
|
||||
}
|
||||
|
||||
public ITexture Texture
|
||||
@@ -62,22 +67,31 @@ namespace OpenRA.Graphics
|
||||
// is set from other threads too via CommitData().
|
||||
get
|
||||
{
|
||||
if (texture == null)
|
||||
{
|
||||
texture = Game.Renderer.Device.CreateTexture();
|
||||
dirty = true;
|
||||
}
|
||||
GenerateTexture();
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
|
||||
lock (dirtyLock)
|
||||
void GenerateTexture()
|
||||
{
|
||||
if (texture == null)
|
||||
{
|
||||
texture = Game.Renderer.Device.CreateTexture();
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if (Buffered)
|
||||
{
|
||||
lock (textureLock)
|
||||
{
|
||||
if (dirty)
|
||||
{
|
||||
texture.SetData(data, Size.Width, Size.Height);
|
||||
dirty = false;
|
||||
if (releaseBufferOnCommit)
|
||||
data = null;
|
||||
}
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +133,7 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitmap.UnlockBits(bd);
|
||||
|
||||
return bitmap;
|
||||
@@ -126,11 +141,20 @@ namespace OpenRA.Graphics
|
||||
|
||||
public void CommitData()
|
||||
{
|
||||
if (data == null)
|
||||
throw new InvalidOperationException("Texture-wrappers are read-only");
|
||||
if (!Buffered)
|
||||
throw new InvalidOperationException(
|
||||
"This sheet is unbuffered. You cannot call CommitData on an unbuffered sheet. " +
|
||||
"If you need to completely replace the texture data you should set data into the texture directly. " +
|
||||
"If you need to make only small changes to the texture data consider creating a buffered sheet instead.");
|
||||
|
||||
lock (dirtyLock)
|
||||
lock (textureLock)
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public void ReleaseBuffer()
|
||||
{
|
||||
lock (textureLock)
|
||||
releaseBufferOnCommit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
@@ -17,7 +17,7 @@ namespace OpenRA.Graphics
|
||||
public class SheetOverflowException : Exception
|
||||
{
|
||||
public SheetOverflowException(string message)
|
||||
: base(message) {}
|
||||
: base(message) { }
|
||||
}
|
||||
|
||||
public enum SheetType
|
||||
@@ -38,11 +38,11 @@ namespace OpenRA.Graphics
|
||||
|
||||
public static Sheet AllocateSheet()
|
||||
{
|
||||
return new Sheet(new Size(Renderer.SheetSize, Renderer.SheetSize));
|
||||
return new Sheet(new Size(Renderer.SheetSize, Renderer.SheetSize), true);
|
||||
}
|
||||
|
||||
public SheetBuilder(SheetType t)
|
||||
: this(t, AllocateSheet) {}
|
||||
: this(t, AllocateSheet) { }
|
||||
|
||||
public SheetBuilder(SheetType t, Func<Sheet> allocateSheet)
|
||||
{
|
||||
@@ -109,6 +109,7 @@ namespace OpenRA.Graphics
|
||||
var next = NextChannel(channel);
|
||||
if (next == null)
|
||||
{
|
||||
current.ReleaseBuffer();
|
||||
current = allocateSheet();
|
||||
channel = TextureChannel.Red;
|
||||
}
|
||||
@@ -116,7 +117,7 @@ namespace OpenRA.Graphics
|
||||
channel = next.Value;
|
||||
|
||||
rowHeight = imageSize.Height;
|
||||
p = new Point(0,0);
|
||||
p = new Point(0, 0);
|
||||
}
|
||||
|
||||
var rect = new Sprite(current, new Rectangle(p, imageSize), spriteOffset, channel, BlendMode.Alpha);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
|
||||
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
|
||||
* This file is part of OpenRA, which is free software. It is made
|
||||
* available to you under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation. For more information,
|
||||
@@ -58,7 +58,7 @@ namespace OpenRA.Graphics
|
||||
throw new SheetOverflowException("Terrain sheet overflow. Try increasing the tileset SheetSize parameter.");
|
||||
allocated = true;
|
||||
|
||||
return new Sheet(new Size(tileset.SheetSize, tileset.SheetSize));
|
||||
return new Sheet(new Size(tileset.SheetSize, tileset.SheetSize), true);
|
||||
};
|
||||
|
||||
var sourceCache = new Dictionary<string, ISpriteSource>();
|
||||
@@ -69,6 +69,8 @@ namespace OpenRA.Graphics
|
||||
|
||||
// 1x1px transparent tile
|
||||
missingTile = sheetBuilder.Add(new byte[1], new Size(1, 1));
|
||||
|
||||
Sheet.ReleaseBuffer();
|
||||
}
|
||||
|
||||
public Sprite TileSprite(TerrainTile r)
|
||||
|
||||
@@ -179,6 +179,7 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
// Sheet overflow - allocate a new sheet and try once more
|
||||
Log.Write("debug", "Voxel sheet overflow! Generating new sheet");
|
||||
sheetBuilder.Current.ReleaseBuffer();
|
||||
sheetBuilder = CreateSheetBuilder();
|
||||
v = GenerateSlicePlanes(l).SelectMany(x => x).ToArray();
|
||||
}
|
||||
@@ -223,5 +224,10 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
return voxels[Pair.New(vxl, hva)];
|
||||
}
|
||||
|
||||
public void Finish()
|
||||
{
|
||||
sheetBuilder.Current.ReleaseBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user