Dashboard/Dashboard.Drawing.OpenGL/DrawCallRecorder.cs

228 lines
7.1 KiB
C#

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using OpenTK.Graphics.OpenGL;
using OpenTK.Mathematics;
using Vector2 = System.Numerics.Vector2;
using Vector3 = System.Numerics.Vector3;
namespace Dashboard.Drawing.OpenGL
{
public class DrawCallRecorder : IGLDisposable, IInitializer
{
private int _vao = 0;
private int _vbo = 0;
private readonly List<DrawVertex> _vertices = new List<DrawVertex>();
private readonly List<DrawCall> _calls = new List<DrawCall>();
private int _start = 0;
private int _count = 0;
private int _primitives = 0;
private Vector3 _charCoords;
private int _cmdIndex;
private int _texture0, _texture1, _texture2, _texture3;
private TextureTarget _target0, _target1, _target2, _target3;
private Matrix4 _transforms = Matrix4.Identity;
public int CommandModulus = 64;
public int CommandBuffer = 0;
public int CommandSize = 64;
private int CommandByteSize => CommandModulus * CommandSize;
public int TransformsLocation { get; set; }
public void Transforms(in Matrix4 transforms)
{
_transforms = transforms;
}
public void Begin(PrimitiveType type)
{
if (_primitives != 0)
throw new InvalidOperationException("Attempt to begin new draw call before finishing previous one.");
_primitives = (int)type;
_start = _vertices.Count;
_count = 0;
}
public void TexCoords2(Vector2 texCoords)
{
_charCoords = new Vector3(texCoords, 0);
}
public void CharCoords(Vector3 charCoords)
{
_charCoords = charCoords;
}
public void CommandIndex(int index)
{
_cmdIndex = index;
}
public void Vertex3(Vector3 vertex)
{
_vertices.Add(new DrawVertex()
{
Position = vertex,
CharCoords = _charCoords,
CmdIndex = _cmdIndex % CommandModulus,
});
_count++;
}
public void End()
{
if (_primitives == 0)
throw new InvalidOperationException("Attempt to end draw call before starting one.");
_calls.Add(
new DrawCall()
{
Type = (PrimitiveType)_primitives,
Start = _start,
Count = _count,
CmdIndex = _cmdIndex,
Target0 = _target0,
Target1 = _target1,
Target2 = _target2,
Target3 = _target3,
Texture0 = _texture0,
Texture1 = _texture1,
Texture2 = _texture2,
Texture3 = _texture3,
Transforms = _transforms,
});
_primitives = 0;
}
public void BindTexture(TextureTarget target, int texture) => BindTexture(target, 0, texture);
public void BindTexture(TextureTarget target, int unit, int texture)
{
switch (unit)
{
case 0:
_texture0 = 0;
_target0 = target;
break;
case 1:
_texture1 = 0;
_target1 = target;
break;
case 2:
_texture2 = 0;
_target2 = target;
break;
case 3:
_texture3 = 0;
_target3 = target;
break;
default:
throw new ArgumentOutOfRangeException(nameof(unit), "I did not write support for more than 4 textures.");
}
}
public void DrawArrays(PrimitiveType type, int first, int count)
{
throw new NotImplementedException();
}
public void Execute()
{
GL.BindVertexArray(_vao);
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo);
ReadOnlySpan<DrawVertex> vertices = CollectionsMarshal.AsSpan(_vertices);
GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Count * Unsafe.SizeOf<DrawVertex>(), vertices, BufferUsage.DynamicDraw);
foreach (DrawCall call in _calls)
{
GL.BindBufferRange(BufferTarget.UniformBuffer, 0, CommandBuffer, call.CmdIndex / CommandModulus * CommandByteSize, CommandByteSize);
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(call.Target0, call.Texture0);
GL.ActiveTexture(TextureUnit.Texture1);
GL.BindTexture(call.Target1, call.Texture1);
GL.ActiveTexture(TextureUnit.Texture2);
GL.BindTexture(call.Target2, call.Texture2);
GL.ActiveTexture(TextureUnit.Texture3);
GL.BindTexture(call.Target3, call.Texture3);
Matrix4 transforms = call.Transforms;
GL.UniformMatrix4f(TransformsLocation, 1, true, ref transforms);
GL.DrawArrays(call.Type, call.Start, call.Count);
}
}
public void Clear()
{
_vertices.Clear();
_calls.Clear();
}
public void Dispose()
{
throw new NotImplementedException();
}
public void Dispose(bool safeExit)
{
throw new NotImplementedException();
}
public bool IsInitialized { get; private set; }
public void Initialize()
{
if (IsInitialized)
return;
IsInitialized = true;
_vao = GL.CreateVertexArray();
_vbo = GL.CreateBuffer();
GL.BindVertexArray(_vao);
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo);
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 32, 0);
GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, 32, 16);
GL.VertexAttribIPointer(2, 1, VertexAttribIType.Int, 32, 28);
GL.EnableVertexAttribArray(0);
GL.EnableVertexAttribArray(1);
GL.EnableVertexAttribArray(2);
}
private struct DrawCall
{
public PrimitiveType Type;
public int Start;
public int Count;
public int CmdIndex;
public int Texture0;
public int Texture1;
public int Texture2;
public int Texture3;
public TextureTarget Target0;
public TextureTarget Target1;
public TextureTarget Target2;
public TextureTarget Target3;
public Matrix4 Transforms;
}
[StructLayout(LayoutKind.Explicit, Size = 32)]
private struct DrawVertex
{
[FieldOffset(0)]
public Vector3 Position;
[FieldOffset(16)]
public Vector3 CharCoords;
[FieldOffset(28)]
public int CmdIndex;
}
}
}