diff --git a/OpenRA.Game/Graphics/Sheet.cs b/OpenRA.Game/Graphics/Sheet.cs index 2df6a0dd2b..2de2d1ce81 100644 --- a/OpenRA.Game/Graphics/Sheet.cs +++ b/OpenRA.Game/Graphics/Sheet.cs @@ -132,6 +132,7 @@ namespace OpenRA.Graphics { if (!Buffered) return; + dirty = true; releaseBufferOnCommit = true; @@ -140,6 +141,25 @@ namespace OpenRA.Graphics GetTexture(); } + public bool ReleaseBufferAndTryTransferTo(Sheet destination) + { + if (Size != destination.Size) + throw new ArgumentException("Destination sheet does not have the same size", nameof(destination)); + + var buffer = data; + ReleaseBuffer(); + + // Only transfer if the destination has no data that would be lost by overwriting. + if (buffer != null && destination.data == null && destination.texture == null) + { + Array.Clear(buffer, 0, buffer.Length); + destination.data = buffer; + return true; + } + + return false; + } + public void Dispose() { texture?.Dispose(); diff --git a/OpenRA.Game/Graphics/SheetBuilder.cs b/OpenRA.Game/Graphics/SheetBuilder.cs index 78dfb5f8e4..1237972726 100644 --- a/OpenRA.Game/Graphics/SheetBuilder.cs +++ b/OpenRA.Game/Graphics/SheetBuilder.cs @@ -130,8 +130,13 @@ namespace OpenRA.Graphics var next = NextChannel(CurrentChannel); if (next == null) { - Current.ReleaseBuffer(); + var previous = Current; Current = allocateSheet(); + + // Reuse the backing buffer between sheets where possible. + // This avoids allocating additional buffers which the GC must clean up. + previous.ReleaseBufferAndTryTransferTo(Current); + sheets.Add(Current); CurrentChannel = Type == SheetType.Indexed ? TextureChannel.Red : TextureChannel.RGBA; }