Merge pull request #6756 from RoosterDragon/release-more-buffers

Release more sheet buffers
This commit is contained in:
obrakmann
2014-11-29 17:47:06 +01:00
13 changed files with 115 additions and 65 deletions

View File

@@ -62,8 +62,10 @@ namespace OpenRA.Graphics
public void Preload() public void Preload()
{ {
SpriteCache.SheetBuilder.Current.CreateBuffer();
foreach (var unitSeq in sequences.Value.Values) foreach (var unitSeq in sequences.Value.Values)
foreach (var seq in unitSeq.Value.Values) { } foreach (var seq in unitSeq.Value.Values) { }
SpriteCache.SheetBuilder.Current.ReleaseBuffer();
} }
} }

View File

@@ -25,14 +25,16 @@ namespace OpenRA.Graphics
byte[] data; byte[] data;
public readonly Size Size; public readonly Size Size;
public byte[] Data { get { return data ?? texture.GetData(); } } public byte[] GetData()
public bool Buffered { get { return data != null; } } {
CreateBuffer();
return data;
}
public bool Buffered { get { return data != null || texture == null; } }
public Sheet(Size size, bool buffered) public Sheet(Size size)
{ {
Size = size; Size = size;
if (buffered)
data = new byte[4 * Size.Width * Size.Height];
} }
public Sheet(ITexture texture) public Sheet(ITexture texture)
@@ -61,29 +63,27 @@ namespace OpenRA.Graphics
ReleaseBuffer(); ReleaseBuffer();
} }
public ITexture Texture public ITexture GetTexture()
{ {
// This is only called from the main thread but 'dirty' // This is only called from the main thread but 'dirty'
// is set from other threads too via CommitData(). // is set from other threads too via CommitData().
get GenerateTexture();
{ return texture;
GenerateTexture();
return texture;
}
} }
void GenerateTexture() void GenerateTexture()
{ {
if (texture == null) lock (textureLock)
{ {
texture = Game.Renderer.Device.CreateTexture(); if (texture == null)
dirty = true;
}
if (Buffered)
{
lock (textureLock)
{ {
texture = Game.Renderer.Device.CreateTexture();
dirty = true;
}
if (data != null)
{
if (dirty) if (dirty)
{ {
texture.SetData(data, Size.Width, Size.Height); texture.SetData(data, Size.Width, Size.Height);
@@ -97,7 +97,7 @@ namespace OpenRA.Graphics
public Bitmap AsBitmap() public Bitmap AsBitmap()
{ {
var d = Data; var d = GetData();
var dataStride = 4 * Size.Width; var dataStride = 4 * Size.Width;
var bitmap = new Bitmap(Size.Width, Size.Height); var bitmap = new Bitmap(Size.Width, Size.Height);
@@ -112,7 +112,7 @@ namespace OpenRA.Graphics
public Bitmap AsBitmap(TextureChannel channel, IPalette pal) public Bitmap AsBitmap(TextureChannel channel, IPalette pal)
{ {
var d = Data; var d = GetData();
var dataStride = 4 * Size.Width; var dataStride = 4 * Size.Width;
var bitmap = new Bitmap(Size.Width, Size.Height); var bitmap = new Bitmap(Size.Width, Size.Height);
var channelOffset = (int)channel; var channelOffset = (int)channel;
@@ -139,22 +139,44 @@ namespace OpenRA.Graphics
return bitmap; return bitmap;
} }
public void CreateBuffer()
{
lock (textureLock)
{
if (data != null)
return;
if (texture == null)
data = new byte[4 * Size.Width * Size.Height];
else
data = texture.GetData();
releaseBufferOnCommit = false;
}
}
public void CommitData() public void CommitData()
{ {
if (!Buffered) CommitData(false);
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 (textureLock)
dirty = true;
} }
public void ReleaseBuffer() public void ReleaseBuffer()
{
CommitData(true);
}
void CommitData(bool releaseBuffer)
{ {
lock (textureLock) lock (textureLock)
releaseBufferOnCommit = true; {
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.");
dirty = true;
if (releaseBuffer)
releaseBufferOnCommit = true;
}
} }
} }
} }

View File

@@ -38,7 +38,7 @@ namespace OpenRA.Graphics
public static Sheet AllocateSheet() public static Sheet AllocateSheet()
{ {
return new Sheet(new Size(Renderer.SheetSize, Renderer.SheetSize), true); return new Sheet(new Size(Renderer.SheetSize, Renderer.SheetSize));
} }
public SheetBuilder(SheetType t) public SheetBuilder(SheetType t)

View File

@@ -118,7 +118,7 @@ namespace OpenRA.Graphics
unsafe unsafe
{ {
var p = (byte*)bitmap.Buffer; var p = (byte*)bitmap.Buffer;
var dest = s.sheet.Data; var dest = s.sheet.GetData();
var destStride = s.sheet.Size.Width * 4; var destStride = s.sheet.Size.Width * 4;
for (var j = 0; j < s.size.Y; j++) for (var j = 0; j < s.size.Y; j++)

View File

@@ -32,7 +32,7 @@ namespace OpenRA.Graphics
{ {
if (nv > 0) if (nv > 0)
{ {
shader.SetTexture("DiffuseTexture", currentSheet.Texture); shader.SetTexture("DiffuseTexture", currentSheet.GetTexture());
renderer.Device.SetBlendMode(currentBlend); renderer.Device.SetBlendMode(currentBlend);
shader.Render(() => shader.Render(() =>
@@ -109,7 +109,7 @@ namespace OpenRA.Graphics
public void DrawVertexBuffer(IVertexBuffer<Vertex> buffer, int start, int length, PrimitiveType type, Sheet sheet) public void DrawVertexBuffer(IVertexBuffer<Vertex> buffer, int start, int length, PrimitiveType type, Sheet sheet)
{ {
shader.SetTexture("DiffuseTexture", sheet.Texture); shader.SetTexture("DiffuseTexture", sheet.GetTexture());
renderer.Device.SetBlendMode(BlendMode.Alpha); renderer.Device.SetBlendMode(BlendMode.Alpha);
shader.Render(() => renderer.DrawBatch(buffer, start, length, type)); shader.Render(() => renderer.DrawBatch(buffer, start, length, type));
renderer.Device.SetBlendMode(BlendMode.None); renderer.Device.SetBlendMode(BlendMode.None);

View File

@@ -33,7 +33,7 @@ namespace OpenRA.Graphics
throw new SheetOverflowException("Terrain sheet overflow. Try increasing the tileset SheetSize parameter."); throw new SheetOverflowException("Terrain sheet overflow. Try increasing the tileset SheetSize parameter.");
allocated = true; allocated = true;
return new Sheet(new Size(tileset.SheetSize, tileset.SheetSize), true); return new Sheet(new Size(tileset.SheetSize, tileset.SheetSize));
}; };
sheetBuilder = new SheetBuilder(SheetType.Indexed, allocate); sheetBuilder = new SheetBuilder(SheetType.Indexed, allocate);

View File

@@ -43,7 +43,7 @@ namespace OpenRA.Graphics
public static void FastCopyIntoChannel(Sprite dest, byte[] src) { FastCopyIntoChannel(dest, 0, src); } public static void FastCopyIntoChannel(Sprite dest, byte[] src) { FastCopyIntoChannel(dest, 0, src); }
public static void FastCopyIntoChannel(Sprite dest, int channelOffset, byte[] src) public static void FastCopyIntoChannel(Sprite dest, int channelOffset, byte[] src)
{ {
var data = dest.sheet.Data; var data = dest.sheet.GetData();
var srcStride = dest.bounds.Width; var srcStride = dest.bounds.Width;
var destStride = dest.sheet.Size.Width * 4; var destStride = dest.sheet.Size.Width * 4;
var destOffset = destStride * dest.bounds.Top + dest.bounds.Left * 4 + channelMasks[(int)dest.channel + channelOffset]; var destOffset = destStride * dest.bounds.Top + dest.bounds.Left * 4 + channelMasks[(int)dest.channel + channelOffset];
@@ -64,7 +64,7 @@ namespace OpenRA.Graphics
public static void FastCopyIntoSprite(Sprite dest, Bitmap src) public static void FastCopyIntoSprite(Sprite dest, Bitmap src)
{ {
var data = dest.sheet.Data; var data = dest.sheet.GetData();
var dataStride = dest.sheet.Size.Width * 4; var dataStride = dest.sheet.Size.Width * 4;
var x = dest.bounds.Left * 4; var x = dest.bounds.Left * 4;
var width = dest.bounds.Width * 4; var width = dest.bounds.Width * 4;

View File

@@ -257,7 +257,7 @@ namespace OpenRA.Graphics
float[] ambientLight, float[] diffuseLight, float[] ambientLight, float[] diffuseLight,
int colorPalette, int normalsPalette) int colorPalette, int normalsPalette)
{ {
shader.SetTexture("DiffuseTexture", renderData.Sheet.Texture); shader.SetTexture("DiffuseTexture", renderData.Sheet.GetTexture());
shader.SetVec("PaletteRows", (colorPalette + 0.5f) / HardwarePalette.MaxPalettes, shader.SetVec("PaletteRows", (colorPalette + 0.5f) / HardwarePalette.MaxPalettes,
(normalsPalette + 0.5f) / HardwarePalette.MaxPalettes); (normalsPalette + 0.5f) / HardwarePalette.MaxPalettes);
shader.SetMatrix("TransformMatrix", t); shader.SetMatrix("TransformMatrix", t);

View File

@@ -28,6 +28,7 @@ namespace OpenRA
readonly ModData modData; readonly ModData modData;
readonly SheetBuilder sheetBuilder; readonly SheetBuilder sheetBuilder;
Thread previewLoaderThread; Thread previewLoaderThread;
bool previewLoaderThreadShutDown = true;
object syncRoot = new object(); object syncRoot = new object();
Queue<MapPreview> generateMinimap = new Queue<MapPreview>(); Queue<MapPreview> generateMinimap = new Queue<MapPreview>();
@@ -135,13 +136,20 @@ namespace OpenRA
var maxKeepAlive = 5000 / emptyDelay; var maxKeepAlive = 5000 / emptyDelay;
var keepAlive = maxKeepAlive; var keepAlive = maxKeepAlive;
while (keepAlive-- > 0) for (;;)
{ {
List<MapPreview> todo; List<MapPreview> todo;
lock (syncRoot) lock (syncRoot)
{ {
todo = generateMinimap.Where(p => p.GetMinimap() == null).ToList(); todo = generateMinimap.Where(p => p.GetMinimap() == null).ToList();
generateMinimap.Clear(); generateMinimap.Clear();
if (keepAlive > 0)
keepAlive--;
if (keepAlive == 0 && todo.Count == 0)
{
previewLoaderThreadShutDown = true;
break;
}
} }
if (todo.Count == 0) if (todo.Count == 0)
{ {
@@ -170,20 +178,38 @@ namespace OpenRA
Thread.Sleep(Environment.ProcessorCount == 1 ? 25 : 5); Thread.Sleep(Environment.ProcessorCount == 1 ? 25 : 5);
} }
} }
sheetBuilder.Current.ReleaseBuffer();
// The buffer is not fully reclaimed until changes are written out to the texture.
// We will access the texture in order to force changes to be written out, allowing the buffer to be freed.
Game.RunAfterTick(() => sheetBuilder.Current.GetTexture());
Log.Write("debug", "MapCache.LoadAsyncInternal ended"); Log.Write("debug", "MapCache.LoadAsyncInternal ended");
} }
public void CacheMinimap(MapPreview preview) public void CacheMinimap(MapPreview preview)
{ {
bool launchPreviewLoaderThread;
lock (syncRoot) lock (syncRoot)
generateMinimap.Enqueue(preview);
if (previewLoaderThread == null || !previewLoaderThread.IsAlive)
{ {
previewLoaderThread = new Thread(LoadAsyncInternal); generateMinimap.Enqueue(preview);
previewLoaderThread.IsBackground = true; launchPreviewLoaderThread = previewLoaderThreadShutDown;
previewLoaderThread.Start(); previewLoaderThreadShutDown = false;
} }
if (launchPreviewLoaderThread)
Game.RunAfterTick(() =>
{
// Wait for any existing thread to exit before starting a new one.
if (previewLoaderThread != null)
previewLoaderThread.Join();
sheetBuilder.Current.CreateBuffer();
previewLoaderThread = new Thread(LoadAsyncInternal)
{
Name = "Map Preview Loader",
IsBackground = true
};
previewLoaderThread.Start();
});
} }
public MapPreview this[string key] public MapPreview this[string key]

View File

@@ -61,10 +61,10 @@ namespace OpenRA.Widgets
var size = Math.Max(video.Width, video.Height); var size = Math.Max(video.Width, video.Height);
var textureSize = Exts.NextPowerOf2(size); var textureSize = Exts.NextPowerOf2(size);
var videoSheet = new Sheet(new Size(textureSize, textureSize), false); var videoSheet = new Sheet(new Size(textureSize, textureSize));
videoSheet.Texture.ScaleFilter = TextureScaleFilter.Linear; videoSheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear;
videoSheet.Texture.SetData(video.FrameData); videoSheet.GetTexture().SetData(video.FrameData);
videoSprite = new Sprite(videoSheet, videoSprite = new Sprite(videoSheet,
new Rectangle( new Rectangle(
@@ -89,8 +89,8 @@ namespace OpenRA.Widgets
for (var y = 0; y < scaledHeight; y += 2) for (var y = 0; y < scaledHeight; y += 2)
overlay[y, 0] = black; overlay[y, 0] = black;
var overlaySheet = new Sheet(new Size(1, Exts.NextPowerOf2(scaledHeight)), false); var overlaySheet = new Sheet(new Size(1, Exts.NextPowerOf2(scaledHeight)));
overlaySheet.Texture.SetData(overlay); overlaySheet.GetTexture().SetData(overlay);
overlaySprite = new Sprite(overlaySheet, new Rectangle(0, 0, 1, scaledHeight), TextureChannel.Alpha); overlaySprite = new Sprite(overlaySheet, new Rectangle(0, 0, 1, scaledHeight), TextureChannel.Alpha);
} }
@@ -117,7 +117,7 @@ namespace OpenRA.Widgets
while (nextFrame > video.CurrentFrame) while (nextFrame > video.CurrentFrame)
{ {
video.AdvanceFrame(); video.AdvanceFrame();
videoSprite.sheet.Texture.SetData(video.FrameData); videoSprite.sheet.GetTexture().SetData(video.FrameData);
skippedFrames++; skippedFrames++;
} }
@@ -185,7 +185,7 @@ namespace OpenRA.Widgets
paused = true; paused = true;
Sound.StopVideo(); Sound.StopVideo();
video.Reset(); video.Reset();
videoSprite.sheet.Texture.SetData(video.FrameData); videoSprite.sheet.GetTexture().SetData(video.FrameData);
world.AddFrameEndTask(_ => onComplete()); world.AddFrameEndTask(_ => onComplete());
} }
} }

View File

@@ -51,8 +51,8 @@ namespace OpenRA.Mods.Common.Widgets
back = new byte[4*256*256]; back = new byte[4*256*256];
var rect = new Rectangle((int)(255*SRange[0]), (int)(255*(1 - VRange[1])), (int)(255*(SRange[1] - SRange[0]))+1, (int)(255*(VRange[1] - VRange[0])) + 1); var rect = new Rectangle((int)(255*SRange[0]), (int)(255*(1 - VRange[1])), (int)(255*(SRange[1] - SRange[0]))+1, (int)(255*(VRange[1] - VRange[0])) + 1);
var mixerSheet = new Sheet(new Size(256, 256), false); var mixerSheet = new Sheet(new Size(256, 256));
mixerSheet.Texture.SetData(front, 256, 256); mixerSheet.GetTexture().SetData(front, 256, 256);
mixerSprite = new Sprite(mixerSheet, rect, TextureChannel.Alpha); mixerSprite = new Sprite(mixerSheet, rect, TextureChannel.Alpha);
} }
@@ -123,7 +123,7 @@ namespace OpenRA.Mods.Common.Widgets
{ {
try try
{ {
mixerSprite.sheet.Texture.SetData(front, 256, 256); mixerSprite.sheet.GetTexture().SetData(front, 256, 256);
} }
finally finally
{ {

View File

@@ -28,7 +28,7 @@ namespace OpenRA.Mods.Common.Widgets
using (var hueBitmap = new Bitmap(256, 256)) using (var hueBitmap = new Bitmap(256, 256))
{ {
var hueSheet = new Sheet(new Size(256, 256), false); var hueSheet = new Sheet(new Size(256, 256));
hueSprite = new Sprite(hueSheet, new Rectangle(0, 0, 256, 1), TextureChannel.Alpha); hueSprite = new Sprite(hueSheet, new Rectangle(0, 0, 256, 1), TextureChannel.Alpha);
var bitmapData = hueBitmap.LockBits(hueBitmap.Bounds(), var bitmapData = hueBitmap.LockBits(hueBitmap.Bounds(),
@@ -40,7 +40,7 @@ namespace OpenRA.Mods.Common.Widgets
*(c + h) = HSLColor.FromHSV(h / 255f, 1, 1).RGB.ToArgb(); *(c + h) = HSLColor.FromHSV(h / 255f, 1, 1).RGB.ToArgb();
} }
hueBitmap.UnlockBits(bitmapData); hueBitmap.UnlockBits(bitmapData);
hueSheet.Texture.SetData(hueBitmap); hueSheet.GetTexture().SetData(hueBitmap);
} }
} }

View File

@@ -73,14 +73,14 @@ namespace OpenRA.Mods.Common.Widgets
{ {
var r = new Rectangle(0, 0, width, height); var r = new Rectangle(0, 0, width, height);
var s = new Size(terrainBitmap.Width, terrainBitmap.Height); var s = new Size(terrainBitmap.Width, terrainBitmap.Height);
var terrainSheet = new Sheet(s, false); var terrainSheet = new Sheet(s);
terrainSheet.Texture.SetData(terrainBitmap); terrainSheet.GetTexture().SetData(terrainBitmap);
terrainSprite = new Sprite(terrainSheet, r, TextureChannel.Alpha); terrainSprite = new Sprite(terrainSheet, r, TextureChannel.Alpha);
// Data is set in Tick() // Data is set in Tick()
customTerrainSprite = new Sprite(new Sheet(s, false), r, TextureChannel.Alpha); customTerrainSprite = new Sprite(new Sheet(s), r, TextureChannel.Alpha);
actorSprite = new Sprite(new Sheet(s, false), r, TextureChannel.Alpha); actorSprite = new Sprite(new Sheet(s), r, TextureChannel.Alpha);
shroudSprite = new Sprite(new Sheet(s, false), r, TextureChannel.Alpha); shroudSprite = new Sprite(new Sheet(s), r, TextureChannel.Alpha);
} }
} }
@@ -203,16 +203,16 @@ namespace OpenRA.Mods.Common.Widgets
{ {
updateTicks = 12; updateTicks = 12;
using (var bitmap = Minimap.CustomTerrainBitmap(world)) using (var bitmap = Minimap.CustomTerrainBitmap(world))
customTerrainSprite.sheet.Texture.SetData(bitmap); customTerrainSprite.sheet.GetTexture().SetData(bitmap);
} }
if (updateTicks == 8) if (updateTicks == 8)
using (var bitmap = Minimap.ActorsBitmap(world)) using (var bitmap = Minimap.ActorsBitmap(world))
actorSprite.sheet.Texture.SetData(bitmap); actorSprite.sheet.GetTexture().SetData(bitmap);
if (updateTicks == 4) if (updateTicks == 4)
using (var bitmap = Minimap.ShroudBitmap(world)) using (var bitmap = Minimap.ShroudBitmap(world))
shroudSprite.sheet.Texture.SetData(bitmap); shroudSprite.sheet.GetTexture().SetData(bitmap);
// Enable/Disable the radar // Enable/Disable the radar
var enabled = IsEnabled(); var enabled = IsEnabled();