diff --git a/OpenRA.Game/Graphics/PlatformInterfaces.cs b/OpenRA.Game/Graphics/PlatformInterfaces.cs index 693b2be872..08d227c939 100644 --- a/OpenRA.Game/Graphics/PlatformInterfaces.cs +++ b/OpenRA.Game/Graphics/PlatformInterfaces.cs @@ -84,6 +84,7 @@ namespace OpenRA public interface IGraphicsContext : IDisposable { IVertexBuffer CreateVertexBuffer(int size); + IIndexBuffer CreateIndexBuffer(uint[] indices); Vertex[] CreateVertices(int size); ITexture CreateTexture(); IFrameBuffer CreateFrameBuffer(Size s); @@ -114,6 +115,11 @@ namespace OpenRA void SetData(T[] vertices, int offset, int start, int length); } + public interface IIndexBuffer : IDisposable + { + void Bind(); + } + public interface IShader { void SetBool(string name, bool value); diff --git a/OpenRA.Game/Graphics/Util.cs b/OpenRA.Game/Graphics/Util.cs index 7e376cce70..81f4449faa 100644 --- a/OpenRA.Game/Graphics/Util.cs +++ b/OpenRA.Game/Graphics/Util.cs @@ -20,6 +20,16 @@ namespace OpenRA.Graphics // yes, our channel order is nuts. static readonly int[] ChannelMasks = { 2, 1, 0, 3 }; + public static uint[] CreateQuadIndices(int quads) + { + var indices = new uint[quads * 6]; + ReadOnlySpan cornerVertexMap = stackalloc uint[] { 0, 1, 2, 2, 3, 0 }; + for (var i = 0; i < indices.Length; i++) + indices[i] = cornerVertexMap[i % 6] + (uint)(4 * (i / 6)); + + return indices; + } + public static void FastCreateQuad(Vertex[] vertices, in float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv, in float3 size, in float3 tint, float alpha, float rotation = 0f) { diff --git a/OpenRA.Platforms.Default/OpenGL.cs b/OpenRA.Platforms.Default/OpenGL.cs index 523dbf335a..4a282677e8 100644 --- a/OpenRA.Platforms.Default/OpenGL.cs +++ b/OpenRA.Platforms.Default/OpenGL.cs @@ -131,6 +131,8 @@ namespace OpenRA.Platforms.Default public const int GL_TEXTURE_MAX_LEVEL = 0x813D; public const int GL_ARRAY_BUFFER = 0x8892; + public const int GL_ELEMENT_ARRAY_BUFFER = 0x8893; + public const int GL_STATIC_DRAW = 0x88E4; public const int GL_DYNAMIC_DRAW = 0x88E8; public const int GL_TEXTURE0 = 0x84C0; diff --git a/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs b/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs index 67ea0ad6fc..3a13ee8ec7 100644 --- a/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs +++ b/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs @@ -68,6 +68,12 @@ namespace OpenRA.Platforms.Default return new VertexBuffer(size); } + public IIndexBuffer CreateIndexBuffer(uint[] indices) + { + VerifyThreadAffinity(); + return new StaticIndexBuffer(indices); + } + public Vertex[] CreateVertices(int size) { VerifyThreadAffinity(); diff --git a/OpenRA.Platforms.Default/StaticIndexBuffer.cs b/OpenRA.Platforms.Default/StaticIndexBuffer.cs new file mode 100644 index 0000000000..1384751669 --- /dev/null +++ b/OpenRA.Platforms.Default/StaticIndexBuffer.cs @@ -0,0 +1,62 @@ +#region Copyright & License Information +/* + * Copyright (c) The OpenRA Developers and Contributors + * 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, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; +using System.Runtime.InteropServices; + +namespace OpenRA.Platforms.Default +{ + sealed class StaticIndexBuffer : ThreadAffine, IDisposable, IIndexBuffer + { + const int UintSize = 4; + uint buffer; + bool disposed; + + public StaticIndexBuffer(uint[] indices) + { + OpenGL.glGenBuffers(1, out buffer); + OpenGL.CheckGLError(); + Bind(); + + var ptr = GCHandle.Alloc(indices, GCHandleType.Pinned); + try + { + OpenGL.glBufferData(OpenGL.GL_ELEMENT_ARRAY_BUFFER, + new IntPtr(UintSize * indices.Length), + ptr.AddrOfPinnedObject(), + OpenGL.GL_STATIC_DRAW); + } + finally + { + ptr.Free(); + } + + OpenGL.CheckGLError(); + } + + public void Bind() + { + VerifyThreadAffinity(); + OpenGL.glBindBuffer(OpenGL.GL_ELEMENT_ARRAY_BUFFER, buffer); + OpenGL.CheckGLError(); + } + + public void Dispose() + { + if (disposed) + return; + + disposed = true; + OpenGL.glDeleteBuffers(1, ref buffer); + OpenGL.CheckGLError(); + } + } +} diff --git a/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs b/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs index 0f90049c1c..2400505634 100644 --- a/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs +++ b/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs @@ -46,6 +46,7 @@ namespace OpenRA.Platforms.Default Func getCreateFrameBuffer; Func getCreateShader; Func> getCreateVertexBuffer; + Func getCreateIndexBuffer; Action doDrawPrimitives; Action doEnableScissor; Action doSetBlendMode; @@ -94,6 +95,7 @@ namespace OpenRA.Platforms.Default }; getCreateShader = name => new ThreadedShader(this, context.CreateShader((string)name)); getCreateVertexBuffer = length => new ThreadedVertexBuffer(this, context.CreateVertexBuffer((int)length)); + getCreateIndexBuffer = indices => new ThreadedIndexBuffer(this, context.CreateIndexBuffer((uint[])indices)); doDrawPrimitives = tuple => { @@ -404,6 +406,11 @@ namespace OpenRA.Platforms.Default return Send(getCreateVertexBuffer, length); } + public IIndexBuffer CreateIndexBuffer(uint[] indices) + { + return Send(getCreateIndexBuffer, indices); + } + public Vertex[] CreateVertices(int size) { return GetVertices(size); @@ -563,6 +570,30 @@ namespace OpenRA.Platforms.Default } } + sealed class ThreadedIndexBuffer : IIndexBuffer + { + readonly ThreadedGraphicsContext device; + readonly Action bind; + readonly Action dispose; + + public ThreadedIndexBuffer(ThreadedGraphicsContext device, IIndexBuffer indexBuffer) + { + this.device = device; + bind = indexBuffer.Bind; + dispose = indexBuffer.Dispose; + } + + public void Bind() + { + device.Post(bind); + } + + public void Dispose() + { + device.Post(dispose); + } + } + sealed class ThreadedTexture : ITextureInternal { readonly ThreadedGraphicsContext device;