From 26b6118f50217250a1b9950677a76a334cbfd653 Mon Sep 17 00:00:00 2001 From: Gustas Date: Wed, 13 Sep 2023 12:57:47 +0300 Subject: [PATCH] Extract vertex attributes --- OpenRA.Game/Graphics/ModelRenderer.cs | 2 +- OpenRA.Game/Graphics/PlatformInterfaces.cs | 14 ++++- OpenRA.Game/Graphics/ShaderBindings.cs | 56 +++++++++++++++++++ OpenRA.Game/Graphics/SpriteRenderer.cs | 4 +- OpenRA.Game/Graphics/Vertex.cs | 30 ++++++++++ OpenRA.Game/Renderer.cs | 17 +++--- .../Sdl2GraphicsContext.cs | 13 +---- OpenRA.Platforms.Default/Shader.cs | 48 ++++++++-------- .../ThreadedGraphicsContext.cs | 13 ++++- OpenRA.Platforms.Default/VertexBuffer.cs | 9 --- 10 files changed, 149 insertions(+), 57 deletions(-) create mode 100644 OpenRA.Game/Graphics/ShaderBindings.cs diff --git a/OpenRA.Game/Graphics/ModelRenderer.cs b/OpenRA.Game/Graphics/ModelRenderer.cs index bef51430fb..146d90819b 100644 --- a/OpenRA.Game/Graphics/ModelRenderer.cs +++ b/OpenRA.Game/Graphics/ModelRenderer.cs @@ -277,7 +277,7 @@ namespace OpenRA.Graphics shader.SetVec("DiffuseLight", diffuseLight, 3); shader.PrepareRender(); - renderer.DrawBatch(cache.VertexBuffer, renderData.Start, renderData.Count, PrimitiveType.TriangleList); + renderer.DrawBatch(cache.VertexBuffer, shader, renderData.Start, renderData.Count, PrimitiveType.TriangleList); } public void BeginFrame() diff --git a/OpenRA.Game/Graphics/PlatformInterfaces.cs b/OpenRA.Game/Graphics/PlatformInterfaces.cs index 6178d5625b..70c4c42e24 100644 --- a/OpenRA.Game/Graphics/PlatformInterfaces.cs +++ b/OpenRA.Game/Graphics/PlatformInterfaces.cs @@ -10,6 +10,7 @@ #endregion using System; +using OpenRA.Graphics; using OpenRA.Primitives; namespace OpenRA @@ -88,7 +89,7 @@ namespace OpenRA ITexture CreateTexture(); IFrameBuffer CreateFrameBuffer(Size s); IFrameBuffer CreateFrameBuffer(Size s, Color clearColor); - IShader CreateShader(string name); + IShader CreateShader(IShaderBindings shaderBindings); void EnableScissor(int x, int y, int width, int height); void DisableScissor(); void Present(); @@ -130,6 +131,17 @@ namespace OpenRA void SetTexture(string param, ITexture texture); void SetMatrix(string param, float[] mtx); void PrepareRender(); + void Bind(); + } + + public interface IShaderBindings + { + string VertexShaderName { get; } + string VertexShaderCode { get; } + string FragmentShaderName { get; } + string FragmentShaderCode { get; } + int Stride { get; } + ShaderVertexAttribute[] Attributes { get; } } public enum TextureScaleFilter { Nearest, Linear } diff --git a/OpenRA.Game/Graphics/ShaderBindings.cs b/OpenRA.Game/Graphics/ShaderBindings.cs new file mode 100644 index 0000000000..71adb20666 --- /dev/null +++ b/OpenRA.Game/Graphics/ShaderBindings.cs @@ -0,0 +1,56 @@ +#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.IO; +using System.Linq; + +namespace OpenRA.Graphics +{ + public readonly struct ShaderVertexAttribute + { + public readonly string Name; + public readonly int Components; + public readonly int Offset; + + public ShaderVertexAttribute(string name, int components, int offset) + { + Name = name; + Components = components; + Offset = offset; + } + } + + public abstract class ShaderBindings : IShaderBindings + { + public string VertexShaderName { get; } + public string VertexShaderCode { get; } + public string FragmentShaderName { get; } + public string FragmentShaderCode { get; } + public int Stride { get; } + + public abstract ShaderVertexAttribute[] Attributes { get; } + + protected ShaderBindings(string name) + { + Stride = Attributes.Sum(a => a.Components * 4); + VertexShaderName = name; + VertexShaderCode = GetShaderCode(VertexShaderName + ".vert"); + FragmentShaderName = name; + FragmentShaderCode = GetShaderCode(FragmentShaderName + ".frag"); + } + + public static string GetShaderCode(string filename) + { + var filepath = Path.Combine(Platform.EngineDir, "glsl", filename); + return File.ReadAllText(filepath); + } + } +} diff --git a/OpenRA.Game/Graphics/SpriteRenderer.cs b/OpenRA.Game/Graphics/SpriteRenderer.cs index d0b985de62..c77aae3c04 100644 --- a/OpenRA.Game/Graphics/SpriteRenderer.cs +++ b/OpenRA.Game/Graphics/SpriteRenderer.cs @@ -52,7 +52,7 @@ namespace OpenRA.Graphics renderer.Context.SetBlendMode(currentBlend); shader.PrepareRender(); - renderer.DrawQuadBatch(ref vertices, vertexCount); + renderer.DrawQuadBatch(ref vertices, shader, vertexCount); renderer.Context.SetBlendMode(BlendMode.None); vertexCount = 0; @@ -186,7 +186,7 @@ namespace OpenRA.Graphics renderer.Context.SetBlendMode(blendMode); shader.PrepareRender(); - renderer.DrawQuadBatch(buffer, indices, length, UintSize * start); + renderer.DrawQuadBatch(buffer, indices, shader, length, UintSize * start); renderer.Context.SetBlendMode(BlendMode.None); } diff --git a/OpenRA.Game/Graphics/Vertex.cs b/OpenRA.Game/Graphics/Vertex.cs index 1b295c298d..c880a42296 100644 --- a/OpenRA.Game/Graphics/Vertex.cs +++ b/OpenRA.Game/Graphics/Vertex.cs @@ -46,4 +46,34 @@ namespace OpenRA.Graphics R = r; G = g; B = b; A = a; } } + + public sealed class CombinedShaderBindings : ShaderBindings + { + public CombinedShaderBindings() + : base("combined") + { } + + public override ShaderVertexAttribute[] Attributes { get; } = new[] + { + new ShaderVertexAttribute("aVertexPosition", 3, 0), + new ShaderVertexAttribute("aVertexTexCoord", 4, 12), + new ShaderVertexAttribute("aVertexTexMetadata", 2, 28), + new ShaderVertexAttribute("aVertexTint", 4, 36) + }; + } + + public sealed class ModelShaderBindings : ShaderBindings + { + public ModelShaderBindings() + : base("model") + { } + + public override ShaderVertexAttribute[] Attributes { get; } = new[] + { + new ShaderVertexAttribute("aVertexPosition", 3, 0), + new ShaderVertexAttribute("aVertexTexCoord", 4, 12), + new ShaderVertexAttribute("aVertexTexMetadata", 2, 28), + new ShaderVertexAttribute("aVertexTint", 4, 36) + }; + } } diff --git a/OpenRA.Game/Renderer.cs b/OpenRA.Game/Renderer.cs index 2ef7c09cab..27e0e67e32 100644 --- a/OpenRA.Game/Renderer.cs +++ b/OpenRA.Game/Renderer.cs @@ -88,11 +88,12 @@ namespace OpenRA SheetSize = graphicSettings.SheetSize; - WorldSpriteRenderer = new SpriteRenderer(this, Context.CreateShader("combined")); + var combinedBindings = new CombinedShaderBindings(); + WorldSpriteRenderer = new SpriteRenderer(this, Context.CreateShader(combinedBindings)); WorldRgbaSpriteRenderer = new RgbaSpriteRenderer(WorldSpriteRenderer); WorldRgbaColorRenderer = new RgbaColorRenderer(WorldSpriteRenderer); - WorldModelRenderer = new ModelRenderer(this, Context.CreateShader("model")); - SpriteRenderer = new SpriteRenderer(this, Context.CreateShader("combined")); + WorldModelRenderer = new ModelRenderer(this, Context.CreateShader(new ModelShaderBindings())); + SpriteRenderer = new SpriteRenderer(this, Context.CreateShader(combinedBindings)); RgbaSpriteRenderer = new RgbaSpriteRenderer(SpriteRenderer); RgbaColorRenderer = new RgbaColorRenderer(SpriteRenderer); @@ -331,26 +332,28 @@ namespace OpenRA renderType = RenderType.None; } - public void DrawBatch(IVertexBuffer vertices, + public void DrawBatch(IVertexBuffer vertices, IShader shader, int firstVertex, int numVertices, PrimitiveType type) where T : struct { vertices.Bind(); + shader.Bind(); Context.DrawPrimitives(type, firstVertex, numVertices); PerfHistory.Increment("batches", 1); } - public void DrawQuadBatch(ref Vertex[] vertices, int numVertices) + public void DrawQuadBatch(ref Vertex[] vertices, IShader shader, int numVertices) { tempVertexBuffer.SetData(ref vertices, numVertices); - DrawQuadBatch(tempVertexBuffer, quadIndexBuffer, numVertices / 4 * 6, 0); + DrawQuadBatch(tempVertexBuffer, quadIndexBuffer, shader, numVertices / 4 * 6, 0); } - public void DrawQuadBatch(IVertexBuffer vertices, IIndexBuffer indices, int numIndices, int start) + public void DrawQuadBatch(IVertexBuffer vertices, IIndexBuffer indices, IShader shader, int numIndices, int start) where T : struct { vertices.Bind(); indices.Bind(); + shader.Bind(); Context.DrawElements(numIndices, start); PerfHistory.Increment("batches", 1); } diff --git a/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs b/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs index 7aa1d7ce5d..d2e56c97bd 100644 --- a/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs +++ b/OpenRA.Platforms.Default/Sdl2GraphicsContext.cs @@ -50,15 +50,6 @@ namespace OpenRA.Platforms.Default OpenGL.glBindVertexArray(vao); OpenGL.CheckGLError(); } - - OpenGL.glEnableVertexAttribArray(Shader.VertexPosAttributeIndex); - OpenGL.CheckGLError(); - OpenGL.glEnableVertexAttribArray(Shader.TexCoordAttributeIndex); - OpenGL.CheckGLError(); - OpenGL.glEnableVertexAttribArray(Shader.TexMetadataAttributeIndex); - OpenGL.CheckGLError(); - OpenGL.glEnableVertexAttribArray(Shader.TintAttributeIndex); - OpenGL.CheckGLError(); } public IVertexBuffer CreateVertexBuffer(int size) where T : struct @@ -103,10 +94,10 @@ namespace OpenRA.Platforms.Default return new FrameBuffer(s, texture, clearColor); } - public IShader CreateShader(string name) + public IShader CreateShader(IShaderBindings bindings) { VerifyThreadAffinity(); - return new Shader(name); + return new Shader(bindings); } public void EnableScissor(int x, int y, int width, int height) diff --git a/OpenRA.Platforms.Default/Shader.cs b/OpenRA.Platforms.Default/Shader.cs index 68bb2e89d4..5705597332 100644 --- a/OpenRA.Platforms.Default/Shader.cs +++ b/OpenRA.Platforms.Default/Shader.cs @@ -18,24 +18,16 @@ namespace OpenRA.Platforms.Default { sealed class Shader : ThreadAffine, IShader { - public const int VertexPosAttributeIndex = 0; - public const int TexCoordAttributeIndex = 1; - public const int TexMetadataAttributeIndex = 2; - public const int TintAttributeIndex = 3; - readonly Dictionary samplers = new(); readonly Dictionary legacySizeUniforms = new(); readonly Dictionary uniformCache = new(); readonly Dictionary textures = new(); readonly Queue unbindTextures = new(); + readonly IShaderBindings bindings; readonly uint program; - static uint CompileShaderObject(int type, string name) + static uint CompileShaderObject(int type, string code, string name) { - var ext = type == OpenGL.GL_VERTEX_SHADER ? "vert" : "frag"; - var filename = Path.Combine(Platform.EngineDir, "glsl", name + "." + ext); - var code = File.ReadAllText(filename); - var version = OpenGL.Profile == GLProfile.Embedded ? "300 es" : OpenGL.Profile == GLProfile.Legacy ? "120" : "140"; @@ -61,29 +53,29 @@ namespace OpenRA.Platforms.Default OpenGL.glGetShaderInfoLog(shader, len, out _, log); Log.Write("graphics", $"GL Info Log:\n{log}"); - throw new InvalidProgramException($"Compile error in shader object '{filename}'"); + throw new InvalidProgramException($"Compile error in shader object {name}."); } return shader; } - public Shader(string name) + public Shader(IShaderBindings bindings) { - var vertexShader = CompileShaderObject(OpenGL.GL_VERTEX_SHADER, name); - var fragmentShader = CompileShaderObject(OpenGL.GL_FRAGMENT_SHADER, name); + var vertexShader = CompileShaderObject(OpenGL.GL_VERTEX_SHADER, bindings.VertexShaderCode, bindings.VertexShaderName); + var fragmentShader = CompileShaderObject(OpenGL.GL_FRAGMENT_SHADER, bindings.FragmentShaderCode, bindings.FragmentShaderName); // Assemble program program = OpenGL.glCreateProgram(); OpenGL.CheckGLError(); - OpenGL.glBindAttribLocation(program, VertexPosAttributeIndex, "aVertexPosition"); - OpenGL.CheckGLError(); - OpenGL.glBindAttribLocation(program, TexCoordAttributeIndex, "aVertexTexCoord"); - OpenGL.CheckGLError(); - OpenGL.glBindAttribLocation(program, TexMetadataAttributeIndex, "aVertexTexMetadata"); - OpenGL.CheckGLError(); - OpenGL.glBindAttribLocation(program, TintAttributeIndex, "aVertexTint"); - OpenGL.CheckGLError(); + this.bindings = bindings; + for (ushort i = 0; i < bindings.Attributes.Length; i++) + { + OpenGL.glEnableVertexAttribArray(i); + OpenGL.CheckGLError(); + OpenGL.glBindAttribLocation(program, i, bindings.Attributes[i].Name); + OpenGL.CheckGLError(); + } if (OpenGL.Profile == GLProfile.Modern) { @@ -107,7 +99,7 @@ namespace OpenRA.Platforms.Default var log = new StringBuilder(len); OpenGL.glGetProgramInfoLog(program, len, out _, log); Log.Write("graphics", $"GL Info Log:\n{log}"); - throw new InvalidProgramException($"Link error in shader program '{name}'"); + throw new InvalidProgramException($"Link error in shader program '{bindings.VertexShaderName}' and '{bindings.FragmentShaderName}'"); } OpenGL.glUseProgram(program); @@ -148,6 +140,16 @@ namespace OpenRA.Platforms.Default } } + public void Bind() + { + for (ushort i = 0; i < bindings.Attributes.Length; i++) + { + var attribute = bindings.Attributes[i]; + OpenGL.glVertexAttribPointer(i, attribute.Components, OpenGL.GL_FLOAT, false, bindings.Stride, new IntPtr(attribute.Offset)); + OpenGL.CheckGLError(); + } + } + public void PrepareRender() { VerifyThreadAffinity(); diff --git a/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs b/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs index ed686adae8..cc6c2494ee 100644 --- a/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs +++ b/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs @@ -95,7 +95,7 @@ namespace OpenRA.Platforms.Default return new ThreadedFrameBuffer(this, context.CreateFrameBuffer(t.Item1, (ITextureInternal)CreateTexture(), t.Item2)); }; - getCreateShader = name => new ThreadedShader(this, context.CreateShader((string)name)); + getCreateShader = bindings => new ThreadedShader(this, context.CreateShader((IShaderBindings)bindings)); getCreateVertexBuffer = tuple => { @@ -416,9 +416,9 @@ namespace OpenRA.Platforms.Default return Send(getCreateFrameBuffer, (s, clearColor)); } - public IShader CreateShader(string name) + public IShader CreateShader(IShaderBindings bindings) { - return Send(getCreateShader, name); + return Send(getCreateShader, bindings); } public ITexture CreateTexture() @@ -742,10 +742,12 @@ namespace OpenRA.Platforms.Default readonly Action setVec2; readonly Action setVec3; readonly Action setVec4; + readonly Action bind; public ThreadedShader(ThreadedGraphicsContext device, IShader shader) { this.device = device; + bind = shader.Bind; prepareRender = shader.PrepareRender; setBool = tuple => { var t = ((string, bool))tuple; shader.SetBool(t.Item1, t.Item2); }; setMatrix = tuple => { var t = ((string, float[]))tuple; shader.SetMatrix(t.Item1, t.Item2); }; @@ -756,6 +758,11 @@ namespace OpenRA.Platforms.Default setVec4 = tuple => { var t = ((string, float, float, float))tuple; shader.SetVec(t.Item1, t.Item2, t.Item3, t.Item4); }; } + public void Bind() + { + device.Post(bind); + } + public void PrepareRender() { device.Post(prepareRender); diff --git a/OpenRA.Platforms.Default/VertexBuffer.cs b/OpenRA.Platforms.Default/VertexBuffer.cs index 2d5790f963..16fe21b083 100644 --- a/OpenRA.Platforms.Default/VertexBuffer.cs +++ b/OpenRA.Platforms.Default/VertexBuffer.cs @@ -89,15 +89,6 @@ namespace OpenRA.Platforms.Default { VerifyThreadAffinity(); OpenGL.glBindBuffer(OpenGL.GL_ARRAY_BUFFER, buffer); - OpenGL.CheckGLError(); - OpenGL.glVertexAttribPointer(Shader.VertexPosAttributeIndex, 3, OpenGL.GL_FLOAT, false, VertexSize, IntPtr.Zero); - OpenGL.CheckGLError(); - OpenGL.glVertexAttribPointer(Shader.TexCoordAttributeIndex, 4, OpenGL.GL_FLOAT, false, VertexSize, new IntPtr(12)); - OpenGL.CheckGLError(); - OpenGL.glVertexAttribPointer(Shader.TexMetadataAttributeIndex, 2, OpenGL.GL_FLOAT, false, VertexSize, new IntPtr(28)); - OpenGL.CheckGLError(); - OpenGL.glVertexAttribPointer(Shader.TintAttributeIndex, 4, OpenGL.GL_FLOAT, false, VertexSize, new IntPtr(36)); - OpenGL.CheckGLError(); } public void Dispose()