From 0ee87bc771246d4d926af297c7d5a80aef35a5de Mon Sep 17 00:00:00 2001 From: 20kdc Date: Mon, 16 May 2022 16:28:37 +0100 Subject: [PATCH] Colour batch reduction (#2809) Co-authored-by: Pieter-Jan Briers --- .../Graphics/Clyde/Clyde.Constants.cs | 13 +- .../Graphics/Clyde/Clyde.GridRendering.cs | 16 +- Robust.Client/Graphics/Clyde/Clyde.Layout.cs | 42 ++++- .../Graphics/Clyde/Clyde.RenderHandle.cs | 125 ++++---------- .../Graphics/Clyde/Clyde.Rendering.cs | 40 +++-- Robust.Client/Graphics/Clyde/Clyde.cs | 31 ++-- .../Clyde/GLObjects/Clyde.ShaderProgram.cs | 3 - .../Graphics/Clyde/Shaders/base-default.frag | 4 +- .../Graphics/Clyde/Shaders/base-default.vert | 4 + .../Graphics/Clyde/Shaders/base-raw.frag | 3 +- .../Graphics/Clyde/Shaders/base-raw.vert | 2 + .../Graphics/Drawing/DrawingHandleBase.cs | 153 +++++++++++++++++- .../UserInterface/UserInterfaceManager.cs | 6 +- Robust.Shared.Maths/Color.cs | 2 + 14 files changed, 270 insertions(+), 174 deletions(-) diff --git a/Robust.Client/Graphics/Clyde/Clyde.Constants.cs b/Robust.Client/Graphics/Clyde/Clyde.Constants.cs index 41c49c0e8..94b1fe3ac 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.Constants.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.Constants.cs @@ -5,20 +5,19 @@ namespace Robust.Client.Graphics.Clyde private static readonly (string, uint)[] BaseShaderAttribLocations = { ("aPos", 0), - ("tCoord", 1) + ("tCoord", 1), + ("modulate", 2) }; private const int UniIModUV = 0; private const int UniIModelMatrix = 1; - private const int UniIModulate = 2; - private const int UniITexturePixelSize = 3; - private const int UniIMainTexture = 4; - private const int UniILightTexture = 5; - private const int UniCount = 6; + private const int UniITexturePixelSize = 2; + private const int UniIMainTexture = 3; + private const int UniILightTexture = 4; + private const int UniCount = 5; private const string UniModUV = "modifyUV"; private const string UniModelMatrix = "modelMatrix"; - private const string UniModulate = "modulate"; private const string UniTexturePixelSize = "TEXTURE_PIXEL_SIZE"; private const string UniMainTexture = "TEXTURE"; private const string UniLightTexture = "lightMap"; diff --git a/Robust.Client/Graphics/Clyde/Clyde.GridRendering.cs b/Robust.Client/Graphics/Clyde/Clyde.GridRendering.cs index b2dd5eab7..e4f3b67c4 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.GridRendering.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.GridRendering.cs @@ -37,7 +37,6 @@ namespace Robust.Client.Graphics.Clyde gridProgram.SetUniformTextureMaybe(UniIMainTexture, TextureUnit.Texture0); gridProgram.SetUniformTextureMaybe(UniILightTexture, TextureUnit.Texture1); gridProgram.SetUniform(UniIModUV, new Vector4(0, 0, 1, 1)); - gridProgram.SetUniform(UniIModulate, Color.White); foreach (var mapGrid in _mapManager.FindGridsIntersecting(mapId, worldBounds)) { @@ -115,10 +114,10 @@ namespace Robust.Client.Graphics.Clyde var gy = y + cScaled.Y; var vIdx = i * 4; - vertexBuffer[vIdx + 0] = new Vertex2D(gx, gy, region.Left, region.Bottom); - vertexBuffer[vIdx + 1] = new Vertex2D(gx + 1, gy, region.Right, region.Bottom); - vertexBuffer[vIdx + 2] = new Vertex2D(gx + 1, gy + 1, region.Right, region.Top); - vertexBuffer[vIdx + 3] = new Vertex2D(gx, gy + 1, region.Left, region.Top); + vertexBuffer[vIdx + 0] = new Vertex2D(gx, gy, region.Left, region.Bottom, Color.White); + vertexBuffer[vIdx + 1] = new Vertex2D(gx + 1, gy, region.Right, region.Bottom, Color.White); + vertexBuffer[vIdx + 2] = new Vertex2D(gx + 1, gy + 1, region.Right, region.Top, Color.White); + vertexBuffer[vIdx + 3] = new Vertex2D(gx, gy + 1, region.Left, region.Top, Color.White); var nIdx = i * GetQuadBatchIndexCount(); var tIdx = (ushort)(i * 4); QuadBatchIndexWrite(indexBuffer, ref nIdx, tIdx); @@ -151,12 +150,7 @@ namespace Robust.Client.Graphics.Clyde eboSize, $"Grid {grid.Index} chunk {chunk.Indices} EBO"); ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, vao, $"Grid {grid.Index} chunk {chunk.Indices} VAO"); - // Vertex Coords - GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 0); - GL.EnableVertexAttribArray(0); - // Texture Coords. - GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 2 * sizeof(float)); - GL.EnableVertexAttribArray(1); + SetupVAOLayout(); CheckGlError(); // Assign VBO and EBO to VAO. diff --git a/Robust.Client/Graphics/Clyde/Clyde.Layout.cs b/Robust.Client/Graphics/Clyde/Clyde.Layout.cs index 8c5c36299..0b9f43004 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.Layout.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.Layout.cs @@ -1,3 +1,4 @@ +using OpenToolkit.Graphics.OpenGL4; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using JetBrains.Annotations; @@ -8,6 +9,26 @@ namespace Robust.Client.Graphics.Clyde // Contains various layout/rendering structs used inside Clyde. internal partial class Clyde { + /// + /// Sets up VAO layout for Vertex2D for base and raw shader types. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetupVAOLayout() + { + // Vertex Coords + GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 0); + GL.EnableVertexAttribArray(0); + // Texture Coords. + GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 2 * sizeof(float)); + GL.EnableVertexAttribArray(1); + // Colour Modulation. + GL.VertexAttribPointer(2, 4, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 4 * sizeof(float)); + GL.EnableVertexAttribArray(2); + } + + // NOTE: This is: + // + Directly cast from DrawVertexUV2DColor!!! + // + GLContextWindow does it's own thing with this for winblit, be careful! [StructLayout(LayoutKind.Sequential)] [PublicAPI] private readonly struct Vertex2D @@ -16,6 +37,8 @@ namespace Robust.Client.Graphics.Clyde public readonly Vector2 Position; public readonly Vector2 TextureCoordinates; + // Note that this color is in linear space. + public readonly Color Modulate; static Vertex2D() { @@ -26,27 +49,34 @@ namespace Robust.Client.Graphics.Clyde } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vertex2D(Vector2 position, Vector2 textureCoordinates) + public Vertex2D(Vector2 position, Vector2 textureCoordinates, Color modulate) { Position = position; TextureCoordinates = textureCoordinates; + Modulate = modulate; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vertex2D(float x, float y, float u, float v) - : this(new Vector2(x, y), new Vector2(u, v)) + public Vertex2D(float x, float y, float u, float v, float r, float g, float b, float a) + : this(new Vector2(x, y), new Vector2(u, v), new Color(r, g, b, a)) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vertex2D(Vector2 position, float u, float v) - : this(position, new Vector2(u, v)) + public Vertex2D(float x, float y, float u, float v, Color modulate) + : this(new Vector2(x, y), new Vector2(u, v), modulate) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Vertex2D(Vector2 position, float u, float v, Color modulate) + : this(position, new Vector2(u, v), modulate) { } public override string ToString() { - return $"Vertex2D: {Position}, {TextureCoordinates}"; + return $"Vertex2D: {Position}, {TextureCoordinates}, {Modulate}"; } } diff --git a/Robust.Client/Graphics/Clyde/Clyde.RenderHandle.cs b/Robust.Client/Graphics/Clyde/Clyde.RenderHandle.cs index 9c4b14f85..b1f588d7f 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.RenderHandle.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.RenderHandle.cs @@ -208,60 +208,34 @@ namespace Robust.Client.Graphics.Clyde _clyde.DrawClear(color); } - public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan vertices, - Color color) - { - // TODO: Maybe don't stackalloc if the data is too large. - Span drawVertices = stackalloc DrawVertexUV2D[vertices.Length]; - PadVertices(vertices, drawVertices); - - DrawPrimitives(primitiveTopology, Texture.White, drawVertices, color); - } - - public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan indices, - ReadOnlySpan vertices, Color color) - { - // TODO: Maybe don't stackalloc if the data is too large. - Span drawVertices = stackalloc DrawVertexUV2D[vertices.Length]; - PadVertices(vertices, drawVertices); - - DrawPrimitives(primitiveTopology, Texture.White, indices, drawVertices, color); - } - public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, - ReadOnlySpan vertices, Color color) + ReadOnlySpan vertices) { if (!(texture is ClydeTexture clydeTexture)) { throw new ArgumentException("Texture must be a basic texture."); } - var castSpan = MemoryMarshal.Cast(vertices); + var castSpan = MemoryMarshal.Cast(vertices); - _clyde.DrawPrimitives(primitiveTopology, clydeTexture.TextureId, castSpan, color); + _clyde.DrawPrimitives(primitiveTopology, clydeTexture.TextureId, castSpan); } public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, ReadOnlySpan indices, - ReadOnlySpan vertices, Color color) + ReadOnlySpan vertices) { if (!(texture is ClydeTexture clydeTexture)) { throw new ArgumentException("Texture must be a basic texture."); } - var castSpan = MemoryMarshal.Cast(vertices); + var castSpan = MemoryMarshal.Cast(vertices); - _clyde.DrawPrimitives(primitiveTopology, clydeTexture.TextureId, indices, castSpan, color); + _clyde.DrawPrimitives(primitiveTopology, clydeTexture.TextureId, indices, castSpan); } - private void PadVertices(ReadOnlySpan input, Span output) - { - for (var i = 0; i < output.Length; i++) - { - output[i] = new DrawVertexUV2D(input[i], (0.5f, 0.5f)); - } - } + // ---- (end) ---- private sealed class DrawingHandleScreenImpl : DrawingHandleScreen { @@ -282,38 +256,16 @@ namespace Robust.Client.Graphics.Clyde _renderHandle.UseShader(shader); } - public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, - ReadOnlySpan vertices, - Color color) + public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, + ReadOnlySpan vertices) { - var realColor = color * Modulate; - - _renderHandle.DrawPrimitives(primitiveTopology, vertices, realColor); - } - - public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, - ReadOnlySpan indices, - ReadOnlySpan vertices, Color color) - { - var realColor = color * Modulate; - - _renderHandle.DrawPrimitives(primitiveTopology, indices, vertices, realColor); + _renderHandle.DrawPrimitives(primitiveTopology, texture, vertices); } public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, - ReadOnlySpan vertices, Color? color = null) + ReadOnlySpan indices, ReadOnlySpan vertices) { - var realColor = (color ?? Color.White) * Modulate; - - _renderHandle.DrawPrimitives(primitiveTopology, texture, vertices, realColor); - } - - public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, - ReadOnlySpan indices, ReadOnlySpan vertices, Color? color = null) - { - var realColor = (color ?? Color.White) * Modulate; - - _renderHandle.DrawPrimitives(primitiveTopology, texture, indices, vertices, realColor); + _renderHandle.DrawPrimitives(primitiveTopology, texture, indices, vertices); } public override void DrawLine(Vector2 from, Vector2 to, Color color) @@ -379,7 +331,16 @@ namespace Robust.Client.Graphics.Clyde int divisions = Math.Max(16,(int)(radius * 16)); float arcLength = MathF.PI * 2 / divisions; - Span filledTriangle = stackalloc Vector2[3]; + var colorReal = color * Modulate; + + if (filled) + { + // Unfilled (using _renderHandle.DrawLine) does the linear conversion internally. + // Filled meanwhile uses DrawPrimitives, so has to do it here. + colorReal = Color.FromSrgb(color); + } + + Span filledTriangle = stackalloc DrawVertexUV2DColor[3]; // Draws a "circle", but its just a polygon with a bunch of sides // this is the GL_LINES version, not GL_LINE_STRIP @@ -389,14 +350,14 @@ namespace Robust.Client.Graphics.Clyde var endPos = new Vector2(MathF.Cos(arcLength * (i+1)) * radius, MathF.Sin(arcLength * (i + 1)) * radius); if(!filled) - _renderHandle.DrawLine(startPos, endPos, color); + _renderHandle.DrawLine(startPos, endPos, colorReal); else { - filledTriangle[0] = startPos + position; - filledTriangle[1] = endPos + position; - filledTriangle[2] = Vector2.Zero + position; + filledTriangle[0] = new DrawVertexUV2DColor(startPos + position, colorReal); + filledTriangle[1] = new DrawVertexUV2DColor(endPos + position, colorReal); + filledTriangle[2] = new DrawVertexUV2DColor(position, colorReal); - _renderHandle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, filledTriangle, color); + _renderHandle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, Texture.White, filledTriangle); } } } @@ -477,38 +438,16 @@ namespace Robust.Client.Graphics.Clyde quad.TopLeft, quad.TopRight, color, in subRegion); } - public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, - ReadOnlySpan vertices, - Color color) + public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, + ReadOnlySpan vertices) { - var realColor = color * Modulate; - - _renderHandle.DrawPrimitives(primitiveTopology, vertices, realColor); - } - - public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, - ReadOnlySpan indices, - ReadOnlySpan vertices, Color color) - { - var realColor = color * Modulate; - - _renderHandle.DrawPrimitives(primitiveTopology, indices, vertices, realColor); + _renderHandle.DrawPrimitives(primitiveTopology, texture, vertices); } public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, - ReadOnlySpan vertices, Color? color = null) + ReadOnlySpan indices, ReadOnlySpan vertices) { - var realColor = (color ?? Color.White) * Modulate; - - _renderHandle.DrawPrimitives(primitiveTopology, texture, vertices, realColor); - } - - public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, - ReadOnlySpan indices, ReadOnlySpan vertices, Color? color = null) - { - var realColor = (color ?? Color.White) * Modulate; - - _renderHandle.DrawPrimitives(primitiveTopology, texture, indices, vertices, realColor); + _renderHandle.DrawPrimitives(primitiveTopology, texture, indices, vertices); } } } diff --git a/Robust.Client/Graphics/Clyde/Clyde.Rendering.cs b/Robust.Client/Graphics/Clyde/Clyde.Rendering.cs index f12dc4911..2b63523e8 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.Rendering.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.Rendering.cs @@ -239,7 +239,6 @@ namespace Robust.Client.Graphics.Clyde // Reset ModUV to ensure it's identity and doesn't touch anything. program.SetUniformMaybe(UniIModUV, new Vector4(0, 0, 1, 1)); - program.SetUniformMaybe(UniIModulate, command.Modulate); program.SetUniformMaybe(UniITexturePixelSize, Vector2.One / loadedTexture.Size); var primitiveType = MapPrimitiveType(command.PrimitiveType); @@ -529,25 +528,27 @@ namespace Robust.Client.Graphics.Clyde /// Bottom right vertex of the quad in object space. /// Top left vertex of the quad in object space. /// Top right vertex of the quad in object space. - /// A color to multiply the texture by when shading. + /// A color to multiply the texture by when shading. Non-linear. /// The four corners of the texture coordinates, matching the four vertices. private void DrawTexture(ClydeHandle texture, Vector2 bl, Vector2 br, Vector2 tl, Vector2 tr, in Color modulate, in Box2 texCoords) { EnsureBatchSpaceAvailable(4, GetQuadBatchIndexCount()); - EnsureBatchState(texture, in modulate, true, GetQuadBatchPrimitiveType(), _queuedShader); + EnsureBatchState(texture, true, GetQuadBatchPrimitiveType(), _queuedShader); bl = _currentMatrixModel.Transform(bl); br = _currentMatrixModel.Transform(br); tr = _currentMatrixModel.Transform(tr); tl = _currentMatrixModel.Transform(tl); + var modulateLinear = Color.FromSrgb(modulate); + // TODO: split batch if necessary. var vIdx = BatchVertexIndex; - BatchVertexData[vIdx + 0] = new Vertex2D(bl, texCoords.BottomLeft); - BatchVertexData[vIdx + 1] = new Vertex2D(br, texCoords.BottomRight); - BatchVertexData[vIdx + 2] = new Vertex2D(tr, texCoords.TopRight); - BatchVertexData[vIdx + 3] = new Vertex2D(tl, texCoords.TopLeft); + BatchVertexData[vIdx + 0] = new Vertex2D(bl, texCoords.BottomLeft, modulateLinear); + BatchVertexData[vIdx + 1] = new Vertex2D(br, texCoords.BottomRight, modulateLinear); + BatchVertexData[vIdx + 2] = new Vertex2D(tr, texCoords.TopRight, modulateLinear); + BatchVertexData[vIdx + 3] = new Vertex2D(tl, texCoords.TopLeft, modulateLinear); BatchVertexIndex += 4; QuadBatchIndexWrite(BatchIndexData, ref BatchIndexIndex, (ushort) vIdx); @@ -555,7 +556,7 @@ namespace Robust.Client.Graphics.Clyde } private void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ClydeHandle textureId, - ReadOnlySpan indices, ReadOnlySpan vertices, in Color color) + ReadOnlySpan indices, ReadOnlySpan vertices) { FinishBatch(); _batchMetaData = null; @@ -585,7 +586,6 @@ namespace Robust.Client.Graphics.Clyde command.DrawBatch.Indexed = true; command.DrawBatch.StartIndex = BatchIndexIndex; command.DrawBatch.PrimitiveType = MapDrawToBatchPrimitiveType(primitiveTopology); - command.DrawBatch.Modulate = color; command.DrawBatch.TextureId = textureId; command.DrawBatch.ShaderInstance = _queuedShader; @@ -598,7 +598,7 @@ namespace Robust.Client.Graphics.Clyde } private void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ClydeHandle textureId, - in ReadOnlySpan vertices, in Color color) + in ReadOnlySpan vertices) { FinishBatch(); _batchMetaData = null; @@ -612,7 +612,6 @@ namespace Robust.Client.Graphics.Clyde command.DrawBatch.Indexed = false; command.DrawBatch.StartIndex = BatchVertexIndex; command.DrawBatch.PrimitiveType = MapDrawToBatchPrimitiveType(primitiveTopology); - command.DrawBatch.Modulate = color; command.DrawBatch.TextureId = textureId; command.DrawBatch.ShaderInstance = _queuedShader; @@ -641,15 +640,17 @@ namespace Robust.Client.Graphics.Clyde private void DrawLine(Vector2 a, Vector2 b, Color color) { EnsureBatchSpaceAvailable(2, 0); - EnsureBatchState(_stockTextureWhite.TextureId, color, false, BatchPrimitiveType.LineList, _queuedShader); + EnsureBatchState(_stockTextureWhite.TextureId, false, BatchPrimitiveType.LineList, _queuedShader); a = _currentMatrixModel.Transform(a); b = _currentMatrixModel.Transform(b); + var colorLinear = Color.FromSrgb(color); + // TODO: split batch if necessary. var vIdx = BatchVertexIndex; - BatchVertexData[vIdx + 0] = new Vertex2D(a, Vector2.Zero); - BatchVertexData[vIdx + 1] = new Vertex2D(b, Vector2.Zero); + BatchVertexData[vIdx + 0] = new Vertex2D(a, Vector2.Zero, colorLinear); + BatchVertexData[vIdx + 1] = new Vertex2D(b, Vector2.Zero, colorLinear); BatchVertexIndex += 2; _debugStats.LastClydeDrawCalls += 1; @@ -716,14 +717,13 @@ namespace Robust.Client.Graphics.Clyde /// Ensures that batching metadata matches the current batch. /// If not, the current batch is finished and a new one is started. /// - private void EnsureBatchState(ClydeHandle textureId, in Color color, bool indexed, + private void EnsureBatchState(ClydeHandle textureId, bool indexed, BatchPrimitiveType primitiveType, ClydeHandle shaderInstance) { if (_batchMetaData.HasValue) { var metaData = _batchMetaData.Value; if (metaData.TextureId == textureId && - StrictColorEquality(metaData.Color, color) && indexed == metaData.Indexed && metaData.PrimitiveType == primitiveType && metaData.ShaderInstance == shaderInstance) @@ -737,7 +737,7 @@ namespace Robust.Client.Graphics.Clyde } // ... and start another. - _batchMetaData = new BatchMetaData(textureId, color, indexed, primitiveType, + _batchMetaData = new BatchMetaData(textureId, indexed, primitiveType, indexed ? BatchIndexIndex : BatchVertexIndex, shaderInstance); /* @@ -769,7 +769,6 @@ namespace Robust.Client.Graphics.Clyde command.DrawBatch.Indexed = indexed; command.DrawBatch.StartIndex = metaData.StartIndex; command.DrawBatch.PrimitiveType = metaData.PrimitiveType; - command.DrawBatch.Modulate = metaData.Color; command.DrawBatch.TextureId = metaData.TextureId; command.DrawBatch.ShaderInstance = metaData.ShaderInstance; @@ -898,7 +897,6 @@ namespace Robust.Client.Graphics.Clyde { public ClydeHandle TextureId; public ClydeHandle ShaderInstance; - public Color Modulate; public int StartIndex; public int Count; @@ -970,17 +968,15 @@ namespace Robust.Client.Graphics.Clyde private readonly struct BatchMetaData { public readonly ClydeHandle TextureId; - public readonly Color Color; public readonly bool Indexed; public readonly BatchPrimitiveType PrimitiveType; public readonly int StartIndex; public readonly ClydeHandle ShaderInstance; - public BatchMetaData(ClydeHandle textureId, in Color color, bool indexed, BatchPrimitiveType primitiveType, + public BatchMetaData(ClydeHandle textureId, bool indexed, BatchPrimitiveType primitiveType, int startIndex, ClydeHandle shaderInstance) { TextureId = textureId; - Color = color; Indexed = indexed; PrimitiveType = primitiveType; StartIndex = startIndex; diff --git a/Robust.Client/Graphics/Clyde/Clyde.cs b/Robust.Client/Graphics/Clyde/Clyde.cs index 89bec6a54..a3a673312 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.cs @@ -14,6 +14,7 @@ using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Log; using Robust.Shared.Map; +using Robust.Shared.Maths; using Robust.Shared.Profiling; using Robust.Shared.Timing; using DependencyAttribute = Robust.Shared.IoC.DependencyAttribute; @@ -275,10 +276,10 @@ namespace Robust.Client.Graphics.Clyde { Span quadVertices = stackalloc[] { - new Vertex2D(1, 0, 1, 1), - new Vertex2D(0, 0, 0, 1), - new Vertex2D(1, 1, 1, 0), - new Vertex2D(0, 1, 0, 0) + new Vertex2D(1, 0, 1, 1, Color.White), + new Vertex2D(0, 0, 0, 1, Color.White), + new Vertex2D(1, 1, 1, 0, Color.White), + new Vertex2D(0, 1, 0, 0, Color.White) }; QuadVBO = new GLBuffer(this, BufferTarget.ArrayBuffer, BufferUsageHint.StaticDraw, @@ -294,10 +295,10 @@ namespace Robust.Client.Graphics.Clyde { Span winVertices = stackalloc[] { - new Vertex2D(-1, 1, 0, 1), - new Vertex2D(-1, -1, 0, 0), - new Vertex2D(1, 1, 1, 1), - new Vertex2D(1, -1, 1, 0), + new Vertex2D(-1, 1, 0, 1, Color.White), + new Vertex2D(-1, -1, 0, 0, Color.White), + new Vertex2D(1, 1, 1, 1, Color.White), + new Vertex2D(1, -1, 1, 0, Color.White), }; WindowVBO = new GLBuffer( @@ -318,12 +319,7 @@ namespace Robust.Client.Graphics.Clyde BatchVAO = new GLHandle(GenVertexArray()); BindVertexArray(BatchVAO.Handle); ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, BatchVAO, nameof(BatchVAO)); - // Vertex Coords - GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 0); - GL.EnableVertexAttribArray(0); - // Texture Coords. - GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 2 * sizeof(float)); - GL.EnableVertexAttribArray(1); + SetupVAOLayout(); CheckGlError(); @@ -347,12 +343,7 @@ namespace Robust.Client.Graphics.Clyde BindVertexArray(vao.Handle); ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, vao, nameof(QuadVAO)); GL.BindBuffer(BufferTarget.ArrayBuffer, QuadVBO.ObjectHandle); - // Vertex Coords - GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 0); - GL.EnableVertexAttribArray(0); - // Texture Coords. - GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 2 * sizeof(float)); - GL.EnableVertexAttribArray(1); + SetupVAOLayout(); return vao; } diff --git a/Robust.Client/Graphics/Clyde/GLObjects/Clyde.ShaderProgram.cs b/Robust.Client/Graphics/Clyde/GLObjects/Clyde.ShaderProgram.cs index 158c6a8f9..8ad90a14b 100644 --- a/Robust.Client/Graphics/Clyde/GLObjects/Clyde.ShaderProgram.cs +++ b/Robust.Client/Graphics/Clyde/GLObjects/Clyde.ShaderProgram.cs @@ -178,9 +178,6 @@ namespace Robust.Client.Graphics.Clyde case UniIModUV: name = UniModUV; break; - case UniIModulate: - name = UniModulate; - break; case UniILightTexture: name = UniLightTexture; break; diff --git a/Robust.Client/Graphics/Clyde/Shaders/base-default.frag b/Robust.Client/Graphics/Clyde/Shaders/base-default.frag index 1c5eda2e4..b50f73bfc 100644 --- a/Robust.Client/Graphics/Clyde/Shaders/base-default.frag +++ b/Robust.Client/Graphics/Clyde/Shaders/base-default.frag @@ -1,8 +1,8 @@ varying highp vec2 UV; varying highp vec2 Pos; +varying highp vec4 VtxModulate; uniform sampler2D lightMap; -uniform highp vec4 modulate; // [SHADER_HEADER_CODE] @@ -16,5 +16,5 @@ void main() lowp vec3 lightSample = texture2D(lightMap, Pos).rgb; - gl_FragColor = zAdjustResult(COLOR * modulate * vec4(lightSample, 1.0)); + gl_FragColor = zAdjustResult(COLOR * VtxModulate * vec4(lightSample, 1.0)); } diff --git a/Robust.Client/Graphics/Clyde/Shaders/base-default.vert b/Robust.Client/Graphics/Clyde/Shaders/base-default.vert index a3d42c6df..2e137329f 100644 --- a/Robust.Client/Graphics/Clyde/Shaders/base-default.vert +++ b/Robust.Client/Graphics/Clyde/Shaders/base-default.vert @@ -2,9 +2,12 @@ /*layout (location = 0)*/ attribute vec2 aPos; // Texture coordinates. /*layout (location = 1)*/ attribute vec2 tCoord; +// Colour modulation. +/*layout (location = 2)*/ attribute vec4 modulate; varying vec2 UV; varying vec2 Pos; +varying vec4 VtxModulate; // Maybe we should merge these CPU side. // idk yet. @@ -33,4 +36,5 @@ void main() gl_Position = vec4(VERTEX, 0.0, 1.0); Pos = (VERTEX + 1.0) / 2.0; UV = mix(modifyUV.xy, modifyUV.zw, tCoord); + VtxModulate = modulate; } diff --git a/Robust.Client/Graphics/Clyde/Shaders/base-raw.frag b/Robust.Client/Graphics/Clyde/Shaders/base-raw.frag index 1340e10b6..e72eaeebb 100644 --- a/Robust.Client/Graphics/Clyde/Shaders/base-raw.frag +++ b/Robust.Client/Graphics/Clyde/Shaders/base-raw.frag @@ -1,7 +1,6 @@ varying highp vec2 UV; uniform sampler2D lightMap; -uniform highp vec4 modulate; // [SHADER_HEADER_CODE] @@ -13,5 +12,7 @@ void main() // [SHADER_CODE] + // NOTE: You may want to add modulation here. Problem: Game doesn't like that. + // In particular, walls disappear. gl_FragColor = zAdjustResult(COLOR); } diff --git a/Robust.Client/Graphics/Clyde/Shaders/base-raw.vert b/Robust.Client/Graphics/Clyde/Shaders/base-raw.vert index 05105ad66..e5b8cf27f 100644 --- a/Robust.Client/Graphics/Clyde/Shaders/base-raw.vert +++ b/Robust.Client/Graphics/Clyde/Shaders/base-raw.vert @@ -2,6 +2,8 @@ /*layout (location = 0)*/ attribute vec2 aPos; // Texture coordinates. /*layout (location = 1)*/ attribute vec2 tCoord; +// Colour modulation. +/*layout (location = 2)*/ attribute vec4 modulate; varying vec2 UV; diff --git a/Robust.Client/Graphics/Drawing/DrawingHandleBase.cs b/Robust.Client/Graphics/Drawing/DrawingHandleBase.cs index 5dd582752..7037cc8f6 100644 --- a/Robust.Client/Graphics/Drawing/DrawingHandleBase.cs +++ b/Robust.Client/Graphics/Drawing/DrawingHandleBase.cs @@ -1,5 +1,7 @@ using System; using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Robust.Shared.Maths; namespace Robust.Client.Graphics @@ -12,6 +14,15 @@ namespace Robust.Client.Graphics //private protected IRenderHandle _renderHandle; private protected readonly int _handleId; public bool Disposed { get; private set; } + /// + /// Drawing commands that do NOT receive per-vertex modulation get modulated by this. + /// Specifically, *DrawPrimitives w/ DrawVertexUV2DColor IS NOT AFFECTED BY THIS*. + /// The only code that should ever be setting this is UserInterfaceManager. + /// It's absolutely evil statefulness. + /// I understand it's existence and operation. + /// I understand that removing it would require rewriting all the UI controls everywhere. + /// I still wish it a prolonged death - it's a performance nightmare. - 20kdc + /// public Color Modulate { get; set; } = Color.White; public void Dispose() @@ -37,14 +48,25 @@ namespace Robust.Client.Graphics public abstract void UseShader(ShaderInstance? shader); + // ---- DrawPrimitives: Vector2 API ---- + /// /// Draws arbitrary geometry primitives with a flat color. /// /// The topology of the primitives to draw. /// The set of vertices to render. /// The color to draw with. - public abstract void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan vertices, - Color color); + public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan vertices, + Color color) + { + var realColor = color * Modulate; + + // TODO: Maybe don't stackalloc if the data is too large. + Span drawVertices = stackalloc DrawVertexUV2DColor[vertices.Length]; + PadVerticesV2(vertices, drawVertices, realColor); + + DrawPrimitives(primitiveTopology, Texture.White, drawVertices); + } /// /// Draws arbitrary indexed geometry primitives with a flat color. @@ -53,8 +75,28 @@ namespace Robust.Client.Graphics /// The indices into to render. /// The set of vertices to render. /// The color to draw with. - public abstract void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan indices, - ReadOnlySpan vertices, Color color); + public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan indices, + ReadOnlySpan vertices, Color color) + { + var realColor = color * Modulate; + + // TODO: Maybe don't stackalloc if the data is too large. + Span drawVertices = stackalloc DrawVertexUV2DColor[vertices.Length]; + PadVerticesV2(vertices, drawVertices, realColor); + + DrawPrimitives(primitiveTopology, Texture.White, indices, drawVertices); + } + + private void PadVerticesV2(ReadOnlySpan input, Span output, Color color) + { + Color colorLinear = Color.FromSrgb(color); + for (var i = 0; i < output.Length; i++) + { + output[i] = new DrawVertexUV2DColor(input[i], (0.5f, 0.5f), colorLinear); + } + } + + // ---- DrawPrimitives: DrawVertexUV2D API ---- /// /// Draws arbitrary geometry primitives with a texture. @@ -63,20 +105,70 @@ namespace Robust.Client.Graphics /// The texture to render with. /// The set of vertices to render. /// The color to draw with. + public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, ReadOnlySpan vertices, + Color? color = null) + { + var realColor = (color ?? Color.White) * Modulate; + + // TODO: Maybe don't stackalloc if the data is too large. + Span drawVertices = stackalloc DrawVertexUV2DColor[vertices.Length]; + PadVerticesUV(vertices, drawVertices, realColor); + + DrawPrimitives(primitiveTopology, texture, drawVertices); + } + + /// + /// Draws arbitrary geometry primitives with a texture. + /// + /// The topology of the primitives to draw. + /// The texture to render with. + /// The set of vertices to render. + /// The indices into to render. + /// The color to draw with. + public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, ReadOnlySpan indices, + ReadOnlySpan vertices, Color? color = null) + { + var realColor = (color ?? Color.White) * Modulate; + + // TODO: Maybe don't stackalloc if the data is too large. + Span drawVertices = stackalloc DrawVertexUV2DColor[vertices.Length]; + PadVerticesUV(vertices, drawVertices, realColor); + + DrawPrimitives(primitiveTopology, texture, indices, drawVertices); + } + + private void PadVerticesUV(ReadOnlySpan input, Span output, Color color) + { + Color colorLinear = Color.FromSrgb(color); + for (var i = 0; i < output.Length; i++) + { + output[i] = new DrawVertexUV2DColor(input[i], colorLinear); + } + } + + // ---- End wrappers ---- + + /// + /// Draws arbitrary geometry primitives with a texture. + /// Be aware that this ignores the Modulate property! Apply it yourself if necessary. + /// + /// The topology of the primitives to draw. + /// The texture to render with. + /// The set of vertices to render. public abstract void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, - ReadOnlySpan vertices, Color? color = null); + ReadOnlySpan vertices); /// /// Draws arbitrary geometry primitives with a flat color. + /// Be aware that this ignores the Modulate property! Apply it yourself if necessary. /// /// The topology of the primitives to draw. /// The texture to render with. /// The indices into to render. /// The set of vertices to render. - /// The color to draw with. public abstract void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, ReadOnlySpan indices, - ReadOnlySpan vertices, Color? color = null); + ReadOnlySpan vertices); [DebuggerStepThrough] protected void CheckDisposed() @@ -108,4 +200,51 @@ namespace Robust.Client.Graphics UV = uv; } } + + /// + /// 2D Vertex that contains position and UV coordinates, and a modulation colour (Linear!!!) + /// NOTE: This is directly cast into Clyde Vertex2D!!!! + /// + [StructLayout(LayoutKind.Sequential)] + public struct DrawVertexUV2DColor + { + public Vector2 Position; + public Vector2 UV; + /// + /// Modulation colour for this vertex. + /// Note that this color is in linear space. + /// + public Color Color; + + /// The location. + /// The texture coordinate. + /// Modulation colour (In linear space, use Color.FromSrgb if needed) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public DrawVertexUV2DColor(Vector2 position, Vector2 uv, Color col) + { + Position = position; + UV = uv; + Color = col; + } + + /// The location. + /// Modulation colour (In linear space, use Color.FromSrgb if needed) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public DrawVertexUV2DColor(Vector2 position, Color col) + { + Position = position; + UV = new Vector2(0.5f, 0.5f); + Color = col; + } + + /// The existing position/UV pair. + /// Modulation colour (In linear space, use Color.FromSrgb if needed) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public DrawVertexUV2DColor(DrawVertexUV2D b, Color col) + { + Position = b.Position; + UV = b.UV; + Color = col; + } + } } diff --git a/Robust.Client/UserInterface/UserInterfaceManager.cs b/Robust.Client/UserInterface/UserInterfaceManager.cs index 46fa9b070..cc5fb60e6 100644 --- a/Robust.Client/UserInterface/UserInterfaceManager.cs +++ b/Robust.Client/UserInterface/UserInterfaceManager.cs @@ -787,7 +787,6 @@ namespace Robust.Client.UserInterface } } - var clip = control.RectClipContent; var scissorRegion = scissorBox; if (clip) @@ -815,11 +814,14 @@ namespace Robust.Client.UserInterface var handle = renderHandle.DrawingHandleScreen; handle.SetTransform(position, Angle.Zero, Vector2.One); modulate *= control.Modulate; - handle.Modulate = modulate * control.ActualModulateSelf; if (_rendering || control.AlwaysRender) { + // Handle modulation with care. + var oldMod = handle.Modulate; + handle.Modulate = modulate * control.ActualModulateSelf; control.DrawInternal(renderHandle); + handle.Modulate = oldMod; handle.UseShader(null); } diff --git a/Robust.Shared.Maths/Color.cs b/Robust.Shared.Maths/Color.cs index 7cce3498d..3dc664a7d 100644 --- a/Robust.Shared.Maths/Color.cs +++ b/Robust.Shared.Maths/Color.cs @@ -28,6 +28,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Xml.Linq; using JetBrains.Annotations; using SysVector3 = System.Numerics.Vector3; @@ -44,6 +45,7 @@ namespace Robust.Shared.Maths /// Represents a color with 4 floating-point components (R, G, B, A). /// [Serializable] + [StructLayout(LayoutKind.Sequential)] public struct Color : IEquatable { ///