Colour batch reduction (#2809)

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
This commit is contained in:
20kdc
2022-05-16 16:28:37 +01:00
committed by GitHub
parent 6e35c89740
commit 0ee87bc771
14 changed files with 270 additions and 174 deletions

View File

@@ -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";

View File

@@ -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.

View File

@@ -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
{
/// <summary>
/// Sets up VAO layout for Vertex2D for base and raw shader types.
/// </summary>
[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}";
}
}

View File

@@ -208,60 +208,34 @@ namespace Robust.Client.Graphics.Clyde
_clyde.DrawClear(color);
}
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan<Vector2> vertices,
Color color)
{
// TODO: Maybe don't stackalloc if the data is too large.
Span<DrawVertexUV2D> drawVertices = stackalloc DrawVertexUV2D[vertices.Length];
PadVertices(vertices, drawVertices);
DrawPrimitives(primitiveTopology, Texture.White, drawVertices, color);
}
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan<ushort> indices,
ReadOnlySpan<Vector2> vertices, Color color)
{
// TODO: Maybe don't stackalloc if the data is too large.
Span<DrawVertexUV2D> drawVertices = stackalloc DrawVertexUV2D[vertices.Length];
PadVertices(vertices, drawVertices);
DrawPrimitives(primitiveTopology, Texture.White, indices, drawVertices, color);
}
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
ReadOnlySpan<DrawVertexUV2D> vertices, Color color)
ReadOnlySpan<DrawVertexUV2DColor> vertices)
{
if (!(texture is ClydeTexture clydeTexture))
{
throw new ArgumentException("Texture must be a basic texture.");
}
var castSpan = MemoryMarshal.Cast<DrawVertexUV2D, Vertex2D>(vertices);
var castSpan = MemoryMarshal.Cast<DrawVertexUV2DColor, Vertex2D>(vertices);
_clyde.DrawPrimitives(primitiveTopology, clydeTexture.TextureId, castSpan, color);
_clyde.DrawPrimitives(primitiveTopology, clydeTexture.TextureId, castSpan);
}
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
ReadOnlySpan<ushort> indices,
ReadOnlySpan<DrawVertexUV2D> vertices, Color color)
ReadOnlySpan<DrawVertexUV2DColor> vertices)
{
if (!(texture is ClydeTexture clydeTexture))
{
throw new ArgumentException("Texture must be a basic texture.");
}
var castSpan = MemoryMarshal.Cast<DrawVertexUV2D, Vertex2D>(vertices);
var castSpan = MemoryMarshal.Cast<DrawVertexUV2DColor, Vertex2D>(vertices);
_clyde.DrawPrimitives(primitiveTopology, clydeTexture.TextureId, indices, castSpan, color);
_clyde.DrawPrimitives(primitiveTopology, clydeTexture.TextureId, indices, castSpan);
}
private void PadVertices(ReadOnlySpan<Vector2> input, Span<DrawVertexUV2D> 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<Vector2> vertices,
Color color)
public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
ReadOnlySpan<DrawVertexUV2DColor> vertices)
{
var realColor = color * Modulate;
_renderHandle.DrawPrimitives(primitiveTopology, vertices, realColor);
}
public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology,
ReadOnlySpan<ushort> indices,
ReadOnlySpan<Vector2> 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<DrawVertexUV2D> vertices, Color? color = null)
ReadOnlySpan<ushort> indices, ReadOnlySpan<DrawVertexUV2DColor> vertices)
{
var realColor = (color ?? Color.White) * Modulate;
_renderHandle.DrawPrimitives(primitiveTopology, texture, vertices, realColor);
}
public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
ReadOnlySpan<ushort> indices, ReadOnlySpan<DrawVertexUV2D> 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<Vector2> 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<DrawVertexUV2DColor> 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<Vector2> vertices,
Color color)
public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
ReadOnlySpan<DrawVertexUV2DColor> vertices)
{
var realColor = color * Modulate;
_renderHandle.DrawPrimitives(primitiveTopology, vertices, realColor);
}
public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology,
ReadOnlySpan<ushort> indices,
ReadOnlySpan<Vector2> 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<DrawVertexUV2D> vertices, Color? color = null)
ReadOnlySpan<ushort> indices, ReadOnlySpan<DrawVertexUV2DColor> vertices)
{
var realColor = (color ?? Color.White) * Modulate;
_renderHandle.DrawPrimitives(primitiveTopology, texture, vertices, realColor);
}
public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
ReadOnlySpan<ushort> indices, ReadOnlySpan<DrawVertexUV2D> vertices, Color? color = null)
{
var realColor = (color ?? Color.White) * Modulate;
_renderHandle.DrawPrimitives(primitiveTopology, texture, indices, vertices, realColor);
_renderHandle.DrawPrimitives(primitiveTopology, texture, indices, vertices);
}
}
}

View File

@@ -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
/// <param name="br">Bottom right vertex of the quad in object space.</param>
/// <param name="tl">Top left vertex of the quad in object space.</param>
/// <param name="tr">Top right vertex of the quad in object space.</param>
/// <param name="modulate">A color to multiply the texture by when shading.</param>
/// <param name="modulate">A color to multiply the texture by when shading. Non-linear.</param>
/// <param name="texCoords">The four corners of the texture coordinates, matching the four vertices.</param>
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<ushort> indices, ReadOnlySpan<Vertex2D> vertices, in Color color)
ReadOnlySpan<ushort> indices, ReadOnlySpan<Vertex2D> 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<Vertex2D> vertices, in Color color)
in ReadOnlySpan<Vertex2D> 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.
/// </summary>
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;

View File

@@ -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<Vertex2D> 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<Vertex2D>(this, BufferTarget.ArrayBuffer, BufferUsageHint.StaticDraw,
@@ -294,10 +295,10 @@ namespace Robust.Client.Graphics.Clyde
{
Span<Vertex2D> 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<Vertex2D>(
@@ -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;
}

View File

@@ -178,9 +178,6 @@ namespace Robust.Client.Graphics.Clyde
case UniIModUV:
name = UniModUV;
break;
case UniIModulate:
name = UniModulate;
break;
case UniILightTexture:
name = UniLightTexture;
break;

View File

@@ -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));
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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; }
/// <summary>
/// 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
/// </summary>
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 ----
/// <summary>
/// Draws arbitrary geometry primitives with a flat color.
/// </summary>
/// <param name="primitiveTopology">The topology of the primitives to draw.</param>
/// <param name="vertices">The set of vertices to render.</param>
/// <param name="color">The color to draw with.</param>
public abstract void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan<Vector2> vertices,
Color color);
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan<Vector2> vertices,
Color color)
{
var realColor = color * Modulate;
// TODO: Maybe don't stackalloc if the data is too large.
Span<DrawVertexUV2DColor> drawVertices = stackalloc DrawVertexUV2DColor[vertices.Length];
PadVerticesV2(vertices, drawVertices, realColor);
DrawPrimitives(primitiveTopology, Texture.White, drawVertices);
}
/// <summary>
/// Draws arbitrary indexed geometry primitives with a flat color.
@@ -53,8 +75,28 @@ namespace Robust.Client.Graphics
/// <param name="indices">The indices into <paramref name="vertices"/> to render.</param>
/// <param name="vertices">The set of vertices to render.</param>
/// <param name="color">The color to draw with.</param>
public abstract void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan<ushort> indices,
ReadOnlySpan<Vector2> vertices, Color color);
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan<ushort> indices,
ReadOnlySpan<Vector2> vertices, Color color)
{
var realColor = color * Modulate;
// TODO: Maybe don't stackalloc if the data is too large.
Span<DrawVertexUV2DColor> drawVertices = stackalloc DrawVertexUV2DColor[vertices.Length];
PadVerticesV2(vertices, drawVertices, realColor);
DrawPrimitives(primitiveTopology, Texture.White, indices, drawVertices);
}
private void PadVerticesV2(ReadOnlySpan<Vector2> input, Span<DrawVertexUV2DColor> 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 ----
/// <summary>
/// Draws arbitrary geometry primitives with a texture.
@@ -63,20 +105,70 @@ namespace Robust.Client.Graphics
/// <param name="texture">The texture to render with.</param>
/// <param name="vertices">The set of vertices to render.</param>
/// <param name="color">The color to draw with.</param>
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, ReadOnlySpan<DrawVertexUV2D> vertices,
Color? color = null)
{
var realColor = (color ?? Color.White) * Modulate;
// TODO: Maybe don't stackalloc if the data is too large.
Span<DrawVertexUV2DColor> drawVertices = stackalloc DrawVertexUV2DColor[vertices.Length];
PadVerticesUV(vertices, drawVertices, realColor);
DrawPrimitives(primitiveTopology, texture, drawVertices);
}
/// <summary>
/// Draws arbitrary geometry primitives with a texture.
/// </summary>
/// <param name="primitiveTopology">The topology of the primitives to draw.</param>
/// <param name="texture">The texture to render with.</param>
/// <param name="vertices">The set of vertices to render.</param>
/// <param name="indices">The indices into <paramref name="vertices"/> to render.</param>
/// <param name="color">The color to draw with.</param>
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, ReadOnlySpan<ushort> indices,
ReadOnlySpan<DrawVertexUV2D> vertices, Color? color = null)
{
var realColor = (color ?? Color.White) * Modulate;
// TODO: Maybe don't stackalloc if the data is too large.
Span<DrawVertexUV2DColor> drawVertices = stackalloc DrawVertexUV2DColor[vertices.Length];
PadVerticesUV(vertices, drawVertices, realColor);
DrawPrimitives(primitiveTopology, texture, indices, drawVertices);
}
private void PadVerticesUV(ReadOnlySpan<DrawVertexUV2D> input, Span<DrawVertexUV2DColor> 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 ----
/// <summary>
/// Draws arbitrary geometry primitives with a texture.
/// Be aware that this ignores the Modulate property! Apply it yourself if necessary.
/// </summary>
/// <param name="primitiveTopology">The topology of the primitives to draw.</param>
/// <param name="texture">The texture to render with.</param>
/// <param name="vertices">The set of vertices to render.</param>
public abstract void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
ReadOnlySpan<DrawVertexUV2D> vertices, Color? color = null);
ReadOnlySpan<DrawVertexUV2DColor> vertices);
/// <summary>
/// Draws arbitrary geometry primitives with a flat color.
/// Be aware that this ignores the Modulate property! Apply it yourself if necessary.
/// </summary>
/// <param name="primitiveTopology">The topology of the primitives to draw.</param>
/// <param name="texture">The texture to render with.</param>
/// <param name="indices">The indices into <paramref name="vertices"/> to render.</param>
/// <param name="vertices">The set of vertices to render.</param>
/// <param name="color">The color to draw with.</param>
public abstract void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
ReadOnlySpan<ushort> indices,
ReadOnlySpan<DrawVertexUV2D> vertices, Color? color = null);
ReadOnlySpan<DrawVertexUV2DColor> vertices);
[DebuggerStepThrough]
protected void CheckDisposed()
@@ -108,4 +200,51 @@ namespace Robust.Client.Graphics
UV = uv;
}
}
/// <summary>
/// 2D Vertex that contains position and UV coordinates, and a modulation colour (Linear!!!)
/// NOTE: This is directly cast into Clyde Vertex2D!!!!
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct DrawVertexUV2DColor
{
public Vector2 Position;
public Vector2 UV;
/// <summary>
/// Modulation colour for this vertex.
/// Note that this color is in linear space.
/// </summary>
public Color Color;
/// <param name="position">The location.</param>
/// <param name="uv">The texture coordinate.</param>
/// <param name="col">Modulation colour (In linear space, use Color.FromSrgb if needed)</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public DrawVertexUV2DColor(Vector2 position, Vector2 uv, Color col)
{
Position = position;
UV = uv;
Color = col;
}
/// <param name="position">The location.</param>
/// <param name="col">Modulation colour (In linear space, use Color.FromSrgb if needed)</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public DrawVertexUV2DColor(Vector2 position, Color col)
{
Position = position;
UV = new Vector2(0.5f, 0.5f);
Color = col;
}
/// <param name="b">The existing position/UV pair.</param>
/// <param name="col">Modulation colour (In linear space, use Color.FromSrgb if needed)</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public DrawVertexUV2DColor(DrawVertexUV2D b, Color col)
{
Position = b.Position;
UV = b.UV;
Color = col;
}
}
}

View File

@@ -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);
}

View File

@@ -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).
/// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct Color : IEquatable<Color>
{
/// <summary>