diff --git a/OpenRA.Game/Graphics/PlatformInterfaces.cs b/OpenRA.Game/Graphics/PlatformInterfaces.cs index f8c897ac6e..6178d5625b 100644 --- a/OpenRA.Game/Graphics/PlatformInterfaces.cs +++ b/OpenRA.Game/Graphics/PlatformInterfaces.cs @@ -10,7 +10,6 @@ #endregion using System; -using OpenRA.Graphics; using OpenRA.Primitives; namespace OpenRA @@ -83,9 +82,9 @@ namespace OpenRA public interface IGraphicsContext : IDisposable { - IVertexBuffer CreateVertexBuffer(int size); + IVertexBuffer CreateVertexBuffer(int size) where T : struct; + T[] CreateVertices(int size) where T : struct; IIndexBuffer CreateIndexBuffer(uint[] indices); - Vertex[] CreateVertices(int size); ITexture CreateTexture(); IFrameBuffer CreateFrameBuffer(Size s); IFrameBuffer CreateFrameBuffer(Size s, Color clearColor); @@ -104,7 +103,7 @@ namespace OpenRA string GLVersion { get; } } - public interface IVertexBuffer : IDisposable + public interface IVertexBuffer : IDisposable where T : struct { void Bind(); void SetData(T[] vertices, int length); diff --git a/OpenRA.Game/Graphics/SpriteRenderer.cs b/OpenRA.Game/Graphics/SpriteRenderer.cs index cf5b10f9c7..d0b985de62 100644 --- a/OpenRA.Game/Graphics/SpriteRenderer.cs +++ b/OpenRA.Game/Graphics/SpriteRenderer.cs @@ -36,7 +36,7 @@ namespace OpenRA.Graphics { this.renderer = renderer; this.shader = shader; - vertices = renderer.Context.CreateVertices(renderer.TempVertexBufferSize); + vertices = renderer.Context.CreateVertices(renderer.TempVertexBufferSize); } public void Flush() diff --git a/OpenRA.Game/Graphics/TerrainSpriteLayer.cs b/OpenRA.Game/Graphics/TerrainSpriteLayer.cs index eef2f71967..28c8e46a76 100644 --- a/OpenRA.Game/Graphics/TerrainSpriteLayer.cs +++ b/OpenRA.Game/Graphics/TerrainSpriteLayer.cs @@ -51,7 +51,7 @@ namespace OpenRA.Graphics vertexRowStride = 4 * map.MapSize.X; vertices = new Vertex[vertexRowStride * map.MapSize.Y]; - vertexBuffer = Game.Renderer.Context.CreateVertexBuffer(vertices.Length); + vertexBuffer = Game.Renderer.Context.CreateVertexBuffer(vertices.Length); indexRowStride = 6 * map.MapSize.X; lock (IndexBuffers) diff --git a/OpenRA.Game/Renderer.cs b/OpenRA.Game/Renderer.cs index 019d7fe0e9..2ef7c09cab 100644 --- a/OpenRA.Game/Renderer.cs +++ b/OpenRA.Game/Renderer.cs @@ -96,7 +96,7 @@ namespace OpenRA RgbaSpriteRenderer = new RgbaSpriteRenderer(SpriteRenderer); RgbaColorRenderer = new RgbaColorRenderer(SpriteRenderer); - tempVertexBuffer = Context.CreateVertexBuffer(TempVertexBufferSize); + tempVertexBuffer = Context.CreateVertexBuffer(TempVertexBufferSize); quadIndexBuffer = Context.CreateIndexBuffer(Util.CreateQuadIndices(TempIndexBufferSize / 6)); } @@ -382,9 +382,9 @@ namespace OpenRA } } - public IVertexBuffer CreateVertexBuffer(int length) + public IVertexBuffer CreateVertexBuffer(int length) where T : struct { - return Context.CreateVertexBuffer(length); + return Context.CreateVertexBuffer(length); } public void EnableScissor(Rectangle rect) diff --git a/OpenRA.Mods.Cnc/Graphics/VoxelLoader.cs b/OpenRA.Mods.Cnc/Graphics/VoxelLoader.cs index b47d97a7aa..ebf1861392 100644 --- a/OpenRA.Mods.Cnc/Graphics/VoxelLoader.cs +++ b/OpenRA.Mods.Cnc/Graphics/VoxelLoader.cs @@ -195,7 +195,7 @@ namespace OpenRA.Mods.Cnc.Graphics public void RefreshBuffer() { vertexBuffer?.Dispose(); - vertexBuffer = Game.Renderer.CreateVertexBuffer(totalVertexCount); + vertexBuffer = Game.Renderer.CreateVertexBuffer(totalVertexCount); vertexBuffer.SetData(vertices.SelectMany(v => v).ToArray(), totalVertexCount); cachedVertexCount = totalVertexCount; } diff --git a/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs b/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs index e9f60e3a74..7aa1d7ce5d 100644 --- a/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs +++ b/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs @@ -10,7 +10,6 @@ #endregion using System; -using OpenRA.Graphics; using OpenRA.Primitives; using SDL2; @@ -62,10 +61,10 @@ namespace OpenRA.Platforms.Default OpenGL.CheckGLError(); } - public IVertexBuffer CreateVertexBuffer(int size) + public IVertexBuffer CreateVertexBuffer(int size) where T : struct { VerifyThreadAffinity(); - return new VertexBuffer(size); + return new VertexBuffer(size); } public IIndexBuffer CreateIndexBuffer(uint[] indices) @@ -74,10 +73,10 @@ namespace OpenRA.Platforms.Default return new StaticIndexBuffer(indices); } - public Vertex[] CreateVertices(int size) + public T[] CreateVertices(int size) where T : struct { VerifyThreadAffinity(); - return new Vertex[size]; + return new T[size]; } public ITexture CreateTexture() diff --git a/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs b/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs index 6d45a7a1ef..ed686adae8 100644 --- a/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs +++ b/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs @@ -13,7 +13,6 @@ using System; using System.Collections.Generic; using System.Runtime.ExceptionServices; using System.Threading; -using OpenRA.Graphics; using OpenRA.Primitives; namespace OpenRA.Platforms.Default @@ -25,7 +24,7 @@ namespace OpenRA.Platforms.Default sealed class ThreadedGraphicsContext : IGraphicsContext { // PERF: Maintain several object pools to reduce allocations. - readonly Stack verticesPool = new(); + readonly Dictionary vertexBufferPools = new(); readonly Stack messagePool = new(); readonly Queue messages = new(); @@ -46,7 +45,7 @@ namespace OpenRA.Platforms.Default Func getCreateTexture; Func getCreateFrameBuffer; Func getCreateShader; - Func> getCreateVertexBuffer; + Func getCreateVertexBuffer; Func getCreateIndexBuffer; Action doDrawPrimitives; Action doDrawElements; @@ -97,7 +96,13 @@ namespace OpenRA.Platforms.Default context.CreateFrameBuffer(t.Item1, (ITextureInternal)CreateTexture(), t.Item2)); }; getCreateShader = name => new ThreadedShader(this, context.CreateShader((string)name)); - getCreateVertexBuffer = length => new ThreadedVertexBuffer(this, context.CreateVertexBuffer((int)length)); + getCreateVertexBuffer = + tuple => + { + (object t, var type) = ((int, Type))tuple; + var vertexBuffer = context.GetType().GetMethod(nameof(CreateVertexBuffer)).MakeGenericMethod(type).Invoke(context, new[] { t }); + return typeof(ThreadedVertexBuffer<>).MakeGenericType(type).GetConstructors()[0].Invoke(new[] { this, vertexBuffer }); + }; getCreateIndexBuffer = indices => new ThreadedIndexBuffer(this, context.CreateIndexBuffer((uint[])indices)); doDrawPrimitives = tuple => @@ -150,20 +155,31 @@ namespace OpenRA.Platforms.Default } } - internal Vertex[] GetVertices(int size) + internal T[] GetVertices(int size) { - lock (verticesPool) - if (size <= VertexBatchSize && verticesPool.Count > 0) - return verticesPool.Pop(); + lock (vertexBufferPools) + { + Stack pool; + if (!vertexBufferPools.TryGetValue(typeof(T), out var poolObject)) + { + pool = new Stack(); + vertexBufferPools.Add(typeof(T), pool); + } + else + pool = (Stack)poolObject; - return new Vertex[size < VertexBatchSize ? VertexBatchSize : size]; + if (size <= VertexBatchSize && pool.Count > 0) + return pool.Pop(); + } + + return new T[size < VertexBatchSize ? VertexBatchSize : size]; } - internal void ReturnVertices(Vertex[] vertices) + internal void ReturnVertices(T[] vertices) { if (vertices.Length == VertexBatchSize) - lock (verticesPool) - verticesPool.Push(vertices); + lock (vertexBufferPools) + ((Stack)vertexBufferPools[typeof(T)]).Push(vertices); } sealed class Message @@ -410,9 +426,9 @@ namespace OpenRA.Platforms.Default return Send(getCreateTexture); } - public IVertexBuffer CreateVertexBuffer(int length) + public IVertexBuffer CreateVertexBuffer(int size) where T : struct { - return Send(getCreateVertexBuffer, length); + return (IVertexBuffer)Send(getCreateVertexBuffer, (size, typeof(T))); } public IIndexBuffer CreateIndexBuffer(uint[] indices) @@ -420,9 +436,9 @@ namespace OpenRA.Platforms.Default return Send(getCreateIndexBuffer, indices); } - public Vertex[] CreateVertices(int size) + public T[] CreateVertices(int size) where T : struct { - return GetVertices(size); + return GetVertices(size); } public void DisableDepthBuffer() @@ -521,7 +537,7 @@ namespace OpenRA.Platforms.Default } } - sealed class ThreadedVertexBuffer : IVertexBuffer + sealed class ThreadedVertexBuffer : IVertexBuffer where T : struct { readonly ThreadedGraphicsContext device; readonly Action bind; @@ -530,12 +546,24 @@ namespace OpenRA.Platforms.Default readonly Func setData3; readonly Action dispose; - public ThreadedVertexBuffer(ThreadedGraphicsContext device, IVertexBuffer vertexBuffer) + public ThreadedVertexBuffer(ThreadedGraphicsContext device, IVertexBuffer vertexBuffer) { this.device = device; bind = vertexBuffer.Bind; - setData1 = tuple => { var t = ((Vertex[], int))tuple; vertexBuffer.SetData(t.Item1, t.Item2); device.ReturnVertices(t.Item1); }; - setData2 = tuple => { var t = ((Vertex[], int, int, int))tuple; vertexBuffer.SetData(t.Item1, t.Item2, t.Item3, t.Item4); device.ReturnVertices(t.Item1); }; + setData1 = tuple => + { + var t = ((T[], int))tuple; + vertexBuffer.SetData(t.Item1, t.Item2); + device.ReturnVertices(t.Item1); + }; + + setData2 = tuple => + { + var t = ((T[], int, int, int))tuple; + vertexBuffer.SetData(t.Item1, t.Item2, t.Item3, t.Item4); + device.ReturnVertices(t.Item1); + }; + setData3 = tuple => { setData2(tuple); return null; }; dispose = vertexBuffer.Dispose; } @@ -545,9 +573,9 @@ namespace OpenRA.Platforms.Default device.Post(bind); } - public void SetData(Vertex[] vertices, int length) + public void SetData(T[] vertices, int length) { - var buffer = device.GetVertices(length); + var buffer = device.GetVertices(length); Array.Copy(vertices, buffer, length); device.Post(setData1, (buffer, length)); } @@ -556,18 +584,18 @@ namespace OpenRA.Platforms.Default /// PERF: The vertices array is passed without copying to the render thread. Upon return `vertices` may reference another /// array object of at least the same size - containing random values. /// - public void SetData(ref Vertex[] vertices, int length) + public void SetData(ref T[] vertices, int length) { device.Post(setData1, (vertices, length)); - vertices = device.GetVertices(vertices.Length); + vertices = device.GetVertices(vertices.Length); } - public void SetData(Vertex[] vertices, int offset, int start, int length) + public void SetData(T[] vertices, int offset, int start, int length) { if (length <= device.VertexBatchSize) { // If we are able to use a buffer without allocation, post a message to avoid blocking. - var buffer = device.GetVertices(length); + var buffer = device.GetVertices(length); Array.Copy(vertices, offset, buffer, 0, length); device.Post(setData2, (buffer, 0, start, length)); }