WIP direct rendering backend.
This commit is contained in:
@@ -0,0 +1,210 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace Dashboard.Drawing
|
||||||
|
{
|
||||||
|
public enum MeshPrimitive
|
||||||
|
{
|
||||||
|
Point,
|
||||||
|
Line,
|
||||||
|
Triangle,
|
||||||
|
TriangleFan,
|
||||||
|
TriangleStrip,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum WindingOrder
|
||||||
|
{
|
||||||
|
Clockwise,
|
||||||
|
Counterclockwise,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum FaceCulling
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Front,
|
||||||
|
Back,
|
||||||
|
Both,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum VertexAttributeType
|
||||||
|
{
|
||||||
|
UnsignedByte,
|
||||||
|
UnsignedShort,
|
||||||
|
UnsignedInt,
|
||||||
|
UnsignedLong,
|
||||||
|
Byte,
|
||||||
|
Short,
|
||||||
|
Int,
|
||||||
|
Long,
|
||||||
|
Half,
|
||||||
|
Float,
|
||||||
|
Double,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum IndexType
|
||||||
|
{
|
||||||
|
UnsignedShort,
|
||||||
|
UnsignedInt,
|
||||||
|
UnsignedLong,
|
||||||
|
Short,
|
||||||
|
Int,
|
||||||
|
Long,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BlendFunction
|
||||||
|
{
|
||||||
|
One,
|
||||||
|
Zero,
|
||||||
|
SourceAlpha,
|
||||||
|
OneMinusSourceAlpha,
|
||||||
|
DestinationAlpha,
|
||||||
|
OneMinusDestinationAlpha,
|
||||||
|
SourceColor,
|
||||||
|
OneMinusSourceColor,
|
||||||
|
DestinationColor,
|
||||||
|
OneMinusDestinationColor,
|
||||||
|
ConstantColor,
|
||||||
|
OneMinusConstantColor,
|
||||||
|
ConstantAlpha,
|
||||||
|
OneMinusConstantAlpha,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BlendEquation
|
||||||
|
{
|
||||||
|
Add,
|
||||||
|
Subtract,
|
||||||
|
ReverseSubtract,
|
||||||
|
Min,
|
||||||
|
Max,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TestFunction
|
||||||
|
{
|
||||||
|
Never,
|
||||||
|
LessThan,
|
||||||
|
LessThanOrEqual,
|
||||||
|
Equal,
|
||||||
|
NotEqual,
|
||||||
|
GreaterThanOrEqual,
|
||||||
|
Greater,
|
||||||
|
Always,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum StencilOperation
|
||||||
|
{
|
||||||
|
Keep,
|
||||||
|
Zero,
|
||||||
|
Replace,
|
||||||
|
Increment,
|
||||||
|
UncheckedIncrement,
|
||||||
|
Decrement,
|
||||||
|
UncheckedDecrement,
|
||||||
|
Invert,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum UniformType
|
||||||
|
{
|
||||||
|
U1,
|
||||||
|
U2,
|
||||||
|
U3,
|
||||||
|
U4,
|
||||||
|
I1,
|
||||||
|
I2,
|
||||||
|
I3,
|
||||||
|
I4,
|
||||||
|
F1,
|
||||||
|
F2,
|
||||||
|
F3,
|
||||||
|
F4,
|
||||||
|
Mat2,
|
||||||
|
Mat3,
|
||||||
|
Mat4,
|
||||||
|
Buffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct DepthMode(bool Test, bool Mask, TestFunction Function)
|
||||||
|
{
|
||||||
|
public static readonly DepthMode Off = new DepthMode(false, false, TestFunction.Always);
|
||||||
|
public static readonly DepthMode WriteOnly = new DepthMode(false, true, TestFunction.LessThan);
|
||||||
|
public static readonly DepthMode Equal = new DepthMode(true, false, TestFunction.Equal);
|
||||||
|
public static readonly DepthMode Enabled = new DepthMode(true, true, TestFunction.LessThan);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct BlendFactor(BlendFunction Source, BlendFunction Destination)
|
||||||
|
{
|
||||||
|
public static readonly BlendFactor Default =
|
||||||
|
new BlendFactor(BlendFunction.SourceAlpha, BlendFunction.OneMinusSourceAlpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct BlendMode(bool Enabled, BlendFactor Color, BlendFactor Alpha)
|
||||||
|
{
|
||||||
|
[MemberNotNullWhen(true, nameof(Unified))]
|
||||||
|
public bool IsSeparate => Color != Alpha;
|
||||||
|
public BlendFactor? Unified => IsSeparate ? null : Color;
|
||||||
|
public BlendEquation Equation { get; init; } = BlendEquation.Add;
|
||||||
|
public Color Constant { get; init; } = System.Drawing.Color.Black;
|
||||||
|
|
||||||
|
public BlendMode(BlendFunction source, BlendFunction destination)
|
||||||
|
: this(true, new BlendFactor(source, destination), new BlendFactor(source, destination))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly BlendMode Off = new BlendMode(false, BlendFactor.Default, BlendFactor.Default);
|
||||||
|
|
||||||
|
public static readonly BlendMode Normal =
|
||||||
|
new BlendMode(true, BlendFactor.Default, BlendFactor.Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly record struct StencilMode(bool Enabled, TestFunction Function)
|
||||||
|
{
|
||||||
|
public int Reference { get; init; } = 0;
|
||||||
|
public int Mask { get; init; } = ~0;
|
||||||
|
public StencilOperation Pass { get; init; } = StencilOperation.Keep;
|
||||||
|
public StencilOperation DepthFail { get; init; } = StencilOperation.Keep;
|
||||||
|
public StencilOperation Fail { get; init; } = StencilOperation.Keep;
|
||||||
|
|
||||||
|
public static readonly StencilMode Off = new StencilMode(false, TestFunction.Always);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record struct VertexAttribute(
|
||||||
|
int Location,
|
||||||
|
int Components,
|
||||||
|
VertexAttributeType Type,
|
||||||
|
long Offset,
|
||||||
|
long Stride)
|
||||||
|
{
|
||||||
|
public int Divisor { get; init; } = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public record struct UniformDescriptor(UniformType Type, long Offset, long Size);
|
||||||
|
|
||||||
|
public struct DrawCall(MeshPrimitive primitive, IBuffer vertexBuffer)
|
||||||
|
{
|
||||||
|
public IShader? ShaderPipeline { get; init; }
|
||||||
|
public Box2d ViewportRegion { get; init; }
|
||||||
|
public Box2d ScissorRegion { get; init; }
|
||||||
|
public WindingOrder FrontFace { get; init; } = WindingOrder.Counterclockwise;
|
||||||
|
public FaceCulling CullMode { get; init; } = FaceCulling.Back;
|
||||||
|
public BlendMode BlendMode { get; init; } = BlendMode.Normal;
|
||||||
|
public DepthMode DepthMode { get; init; } = DepthMode.Enabled;
|
||||||
|
public StencilMode StencilMode { get; init; } = StencilMode.Off;
|
||||||
|
public bool EnableRestart { get; init; } = false;
|
||||||
|
public long RestartIndex { get; init; } = -1;
|
||||||
|
public bool RedMask { get; init; } = true;
|
||||||
|
public bool GreenMask { get; init; } = true;
|
||||||
|
public bool BlueMask { get; init; } = true;
|
||||||
|
public bool AlphaMask { get; init; } = true;
|
||||||
|
public float PointSize { get; init; }= 1.0f;
|
||||||
|
public float LineWidth { get; init; }= 1.0f;
|
||||||
|
|
||||||
|
public MeshPrimitive Primitive { get; init; } = primitive;
|
||||||
|
public List<ITexture> Textures { get; init; } = [];
|
||||||
|
public List<VertexAttribute> Attributes { get; init; } = [];
|
||||||
|
public List<UniformDescriptor> Uniforms { get; init; } = [];
|
||||||
|
public IndexType IndexType { get; init; } = IndexType.UnsignedShort;
|
||||||
|
|
||||||
|
public IBuffer VertexBuffer { get; init; } = vertexBuffer;
|
||||||
|
public IBuffer? ElementBuffer { get; init; }
|
||||||
|
public IBuffer? UniformBuffer { get; init; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
using Dashboard.Pal;
|
||||||
|
|
||||||
|
namespace Dashboard.Drawing
|
||||||
|
{
|
||||||
|
public interface IShader : IDisposable
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base interface for creating shader pipelines.
|
||||||
|
/// </summary>
|
||||||
|
public interface IShaderCreateInfo
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BufferAccessPattern
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No hint given. Not recommended.
|
||||||
|
/// </summary>
|
||||||
|
DontCare,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For buffers which have a long lifetime, but the contents are initialized once.
|
||||||
|
/// </summary>
|
||||||
|
Static,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For buffers which have a long lifetime, but the contents can be updated every frame.
|
||||||
|
/// </summary>
|
||||||
|
Stream,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For buffers which have a short lifetime, used for streaming resources to the graphics hardware.
|
||||||
|
/// </summary>
|
||||||
|
Upload,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For buffers which have a short lifetime, used for streaming resources from the graphics hardware.
|
||||||
|
/// </summary>
|
||||||
|
Download,
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IBuffer : IDisposable
|
||||||
|
{
|
||||||
|
public BufferAccessPattern AccessPattern { get; }
|
||||||
|
public long Size { get; }
|
||||||
|
|
||||||
|
void Reallocate(long newSize);
|
||||||
|
Span<T> Map<T>(long offset, int count = -1) where T : unmanaged;
|
||||||
|
void Unmap();
|
||||||
|
void Read<T>(long offset, Span<T> span) where T : unmanaged;
|
||||||
|
void Write<T>(long offset, ReadOnlySpan<T> span) where T : unmanaged;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This extension provides direct access to the backend rendering pipeline without writing with a higher level
|
||||||
|
/// abstraction than the rendering backend.
|
||||||
|
/// </summary>
|
||||||
|
public interface IDirectRendering : IDeviceContextExtension
|
||||||
|
{
|
||||||
|
public IShader CreatePipeline<T>(T createInfo) where T : IShaderCreateInfo;
|
||||||
|
public IBuffer CreateBuffer(BufferAccessPattern pattern, long size);
|
||||||
|
|
||||||
|
void Draw(DrawCall drawCall);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
namespace Dashboard.Drawing
|
||||||
|
{
|
||||||
|
public record GlslShaderCreateInfo() : IShaderCreateInfo
|
||||||
|
{
|
||||||
|
public required string VertexShader { get; init; }
|
||||||
|
public string? GeometryShader { get; init; } = null;
|
||||||
|
public string? TesselationControl { get; init; } = null;
|
||||||
|
public string? TesselationEvaluation { get; init; } = null;
|
||||||
|
public string? FragmentShader { get; init; } = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public record GlslComputeShaderCreateInfo(string Source) : IShaderCreateInfo
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public record SpirvShaderCreateInfo(byte[] Binary) : IShaderCreateInfo
|
||||||
|
{
|
||||||
|
public int Format { get; init; } = Spirv;
|
||||||
|
|
||||||
|
public SpirvShaderCreateInfo(Stream stream) : this(GetBinary(stream)) { }
|
||||||
|
|
||||||
|
private static byte[] GetBinary(Stream stream)
|
||||||
|
{
|
||||||
|
//FIXME: this might actually be copying twice.
|
||||||
|
if (stream is MemoryStream ms)
|
||||||
|
{
|
||||||
|
return ms.ToArray();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using MemoryStream memory = new MemoryStream();
|
||||||
|
stream.CopyTo(memory);
|
||||||
|
return memory.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const int Spirv = 38225;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
namespace Dashboard.Drawing
|
||||||
|
{
|
||||||
|
public enum ShaderStage
|
||||||
|
{
|
||||||
|
Vertex,
|
||||||
|
Geometry,
|
||||||
|
TesselationEvaluation,
|
||||||
|
TesselationControl,
|
||||||
|
Fragment,
|
||||||
|
Compute,
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class ShaderBuilder(string language)
|
||||||
|
{
|
||||||
|
public string Language { get; } = language;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
using Dashboard.Drawing;
|
||||||
|
using Dashboard.Pal;
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
|
||||||
|
namespace Dashboard.OpenGL.Drawing
|
||||||
|
{
|
||||||
|
public class DirectRendering : IDirectRendering
|
||||||
|
{
|
||||||
|
public DeviceContext Context { get; private set; } = null!;
|
||||||
|
public string DriverName { get; } = "Dashboard OpenGL";
|
||||||
|
public string DriverVendor { get; } = "Dashboard";
|
||||||
|
public Version DriverVersion { get; } = new Version(0, 1);
|
||||||
|
IContextBase IContextExtensionBase.Context => Context;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Require(DeviceContext context) => Context = context;
|
||||||
|
public void Require(IContextBase context) => Require((DeviceContext)context);
|
||||||
|
|
||||||
|
public void Begin()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void End()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public IShader CreatePipeline<T>(T createInfo) where T : IShaderCreateInfo
|
||||||
|
{
|
||||||
|
int program = createInfo switch
|
||||||
|
{
|
||||||
|
GlslShaderCreateInfo glsl => glsl.CreateProgram(),
|
||||||
|
GlslComputeShaderCreateInfo compute => compute.CreateProgram(),
|
||||||
|
SpirvShaderCreateInfo spirv => spirv.CreateProgram(),
|
||||||
|
_ => throw new Exception($"Unsupported shader pipeline type {createInfo.GetType()}."),
|
||||||
|
};
|
||||||
|
|
||||||
|
return new GLShader((GLDeviceContext)Context, program);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBuffer CreateBuffer(BufferAccessPattern pattern, long size)
|
||||||
|
{
|
||||||
|
return GLBuffer.Create((GLDeviceContext)Context, pattern, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Draw(DrawCall drawCall)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using Dashboard.Drawing;
|
||||||
|
|
||||||
|
namespace Dashboard.OpenGL.Drawing
|
||||||
|
{
|
||||||
|
public class GLShader(GLDeviceContext context, int handle) : IShader
|
||||||
|
{
|
||||||
|
public GLDeviceContext Context { get; } = context;
|
||||||
|
public int Handle { get; } = handle;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Context.Collector.DeleteProgram(Handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Dashboard.Drawing;
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
|
||||||
|
namespace Dashboard.OpenGL.Drawing
|
||||||
|
{
|
||||||
|
public static class KhronosShaderHelper
|
||||||
|
{
|
||||||
|
public static int CompileStage(ShaderType type, string source, out string? log)
|
||||||
|
{
|
||||||
|
log = null;
|
||||||
|
int shader = GL.CreateShader(type);
|
||||||
|
|
||||||
|
GL.ShaderSource(shader, source);
|
||||||
|
GL.CompileShader(shader);
|
||||||
|
|
||||||
|
GL.GetShaderi(shader, ShaderParameterName.CompileStatus, out int flag);
|
||||||
|
|
||||||
|
if (flag != 0)
|
||||||
|
return shader;
|
||||||
|
|
||||||
|
GL.GetShaderInfoLog(shader, out log);
|
||||||
|
GL.DeleteShader(shader);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int LinkStages(ReadOnlySpan<int> stages, out string? log)
|
||||||
|
{
|
||||||
|
log = null;
|
||||||
|
int program = GL.CreateProgram();
|
||||||
|
|
||||||
|
foreach (int stage in stages)
|
||||||
|
{
|
||||||
|
GL.AttachShader(program, stage);
|
||||||
|
}
|
||||||
|
|
||||||
|
GL.LinkProgram(program);
|
||||||
|
GL.GetProgrami(program, ProgramProperty.LinkStatus, out int flag);
|
||||||
|
|
||||||
|
if (flag == 0)
|
||||||
|
return program;
|
||||||
|
|
||||||
|
GL.GetProgramInfoLog(program, out log);
|
||||||
|
GL.DeleteProgram(program);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int CreateProgram(this GlslShaderCreateInfo glsl)
|
||||||
|
{
|
||||||
|
Span<int> stages = stackalloc int[5];
|
||||||
|
int failed = 0;
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
Compile(ShaderType.VertexShader, glsl.VertexShader, "Vertex", stages);
|
||||||
|
Compile(ShaderType.FragmentShader, glsl.FragmentShader, "Fragment", stages);
|
||||||
|
Compile(ShaderType.GeometryShader, glsl.GeometryShader, "Geometry", stages);
|
||||||
|
Compile(ShaderType.TessControlShader, glsl.TesselationControl, "Tesselation Control", stages);
|
||||||
|
Compile(ShaderType.TessEvaluationShader, glsl.TesselationEvaluation, "Tesselation Evaluation", stages);
|
||||||
|
|
||||||
|
if (failed > 0)
|
||||||
|
{
|
||||||
|
throw new Exception($"{failed} shader stage(s) failed to compile. See the exception data for details.")
|
||||||
|
{
|
||||||
|
Data =
|
||||||
|
{
|
||||||
|
["Log"] = builder.ToString(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
int program = KhronosShaderHelper.LinkStages(stages[..count], out string? log);
|
||||||
|
if (program == 0)
|
||||||
|
{
|
||||||
|
throw new Exception($"Shader program failed to link. See the exception data for details.")
|
||||||
|
{
|
||||||
|
Data =
|
||||||
|
{
|
||||||
|
["Log"] = log,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return program;
|
||||||
|
|
||||||
|
void Compile(ShaderType type, string? source, string name, Span<int> stages)
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int stage = CompileStage(type, source, out string? log);
|
||||||
|
if (stage == 0)
|
||||||
|
{
|
||||||
|
failed++;
|
||||||
|
builder.Append($"=== {name} Shader Compile Log Begin ===\n{log}\n=== {name} Shader Compile Log End ===\n\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stages[count++] = stage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int CreateProgram(this GlslComputeShaderCreateInfo glsl)
|
||||||
|
{
|
||||||
|
int compute = CompileStage(ShaderType.ComputeShader, glsl.Source, out string? log);
|
||||||
|
if (compute == 0)
|
||||||
|
{
|
||||||
|
throw new Exception("Failed to compile compute shader. See the exception data for details.")
|
||||||
|
{
|
||||||
|
Data =
|
||||||
|
{
|
||||||
|
["Log"] = log,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
int program = KhronosShaderHelper.LinkStages([compute], out log);
|
||||||
|
|
||||||
|
if (program != 0)
|
||||||
|
return program;
|
||||||
|
|
||||||
|
throw new Exception("Failed to link compute shader. See the exception data for details.")
|
||||||
|
{
|
||||||
|
Data =
|
||||||
|
{
|
||||||
|
["Log"] = log,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int CreateProgram(this SpirvShaderCreateInfo spirv)
|
||||||
|
{
|
||||||
|
int program = GL.CreateProgram();
|
||||||
|
GL.ProgramBinary(program, (All)spirv.Format, spirv.Binary, spirv.Binary.Length);
|
||||||
|
|
||||||
|
GL.GetProgrami(program, ProgramProperty.LinkStatus, out int flag);
|
||||||
|
if (flag == 0)
|
||||||
|
return program;
|
||||||
|
|
||||||
|
GL.GetProgramInfoLog(program, out string? log);
|
||||||
|
GL.DeleteProgram(program);
|
||||||
|
|
||||||
|
throw new Exception("Failed to load program binary. See the exception data for details.")
|
||||||
|
{
|
||||||
|
Data =
|
||||||
|
{
|
||||||
|
["Log"] = log,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Dashboard.Drawing;
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
|
||||||
|
namespace Dashboard.OpenGL
|
||||||
|
{
|
||||||
|
public class GLBuffer(GLDeviceContext dc, BufferAccessPattern pattern, int handle, long size, long offset) : IBuffer
|
||||||
|
{
|
||||||
|
// TODO: At the moment, this class does no pooling. Convert it to a pool for better API usage.
|
||||||
|
|
||||||
|
public BufferAccessPattern AccessPattern { get; } = pattern;
|
||||||
|
public long Size { get; private set; } = size;
|
||||||
|
public long Offset { get; private set; } = offset;
|
||||||
|
public int Handle { get; private set; }
|
||||||
|
|
||||||
|
public void Reallocate(long newSize)
|
||||||
|
{
|
||||||
|
GL.CreateBuffer(out int handle);
|
||||||
|
GL.BindBuffer(BufferTarget.ArrayBuffer, handle);
|
||||||
|
GL.BufferData(BufferTarget.ArrayBuffer, (nint)newSize, (nint)0, AccessPattern switch
|
||||||
|
{
|
||||||
|
BufferAccessPattern.Stream => BufferUsage.StreamDraw,
|
||||||
|
BufferAccessPattern.Static => BufferUsage.StaticDraw,
|
||||||
|
BufferAccessPattern.Download => BufferUsage.DynamicCopy,
|
||||||
|
BufferAccessPattern.Upload => BufferUsage.DynamicCopy,
|
||||||
|
_ => BufferUsage.DynamicDraw,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Handle != -1)
|
||||||
|
GL.DeleteBuffer(Handle);
|
||||||
|
|
||||||
|
Handle = handle;
|
||||||
|
Size = newSize;
|
||||||
|
Offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe Span<T> Map<T>(long offset, int count = -1)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
ArgumentOutOfRangeException.ThrowIfNegative(offset);
|
||||||
|
|
||||||
|
if (count < 0)
|
||||||
|
count = (int)(Size / Unsafe.SizeOf<T>());
|
||||||
|
|
||||||
|
long absOffset = Offset + offset;
|
||||||
|
long absCount = count * Unsafe.SizeOf<T>();
|
||||||
|
ArgumentOutOfRangeException.ThrowIfGreaterThan(offset + absCount, Size);
|
||||||
|
|
||||||
|
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
|
||||||
|
nint ptr = (nint)GL.MapBuffer(BufferTarget.ArrayBuffer, BufferAccess.ReadWrite);
|
||||||
|
|
||||||
|
return new Span<T>((T*)(ptr + absOffset), count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unmap()
|
||||||
|
{
|
||||||
|
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
|
||||||
|
GL.UnmapBuffer(BufferTarget.ArrayBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Read<T>(long offset, Span<T> span) where T : unmanaged
|
||||||
|
{
|
||||||
|
long absCount = span.Length * Unsafe.SizeOf<T>();
|
||||||
|
ArgumentOutOfRangeException.ThrowIfGreaterThan(offset + absCount, Size);
|
||||||
|
|
||||||
|
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
|
||||||
|
GL.GetBufferSubData(BufferTarget.ArrayBuffer, (nint)(Offset + offset), (nint)absCount, span);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write<T>(long offset, ReadOnlySpan<T> span) where T : unmanaged
|
||||||
|
{
|
||||||
|
long absCount = span.Length * Unsafe.SizeOf<T>();
|
||||||
|
ArgumentOutOfRangeException.ThrowIfGreaterThan(offset + absCount, Size);
|
||||||
|
|
||||||
|
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
|
||||||
|
GL.BufferSubData(BufferTarget.ArrayBuffer, (nint)(Offset + offset), (nint)absCount, span);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Handle == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dc.Collector.DeleteBufffer(Handle);
|
||||||
|
Handle = -1;
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GLBuffer Create(GLDeviceContext dc, BufferAccessPattern pattern, long size)
|
||||||
|
{
|
||||||
|
GLBuffer buffer = new GLBuffer(dc, pattern, -1, 0, 0);
|
||||||
|
buffer.Reallocate(size);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -74,6 +74,7 @@ namespace Dashboard.OpenGL
|
|||||||
ExtensionPreload<DeviceContextBase>();
|
ExtensionPreload<DeviceContextBase>();
|
||||||
ExtensionPreload<GLTextureExtension>();
|
ExtensionPreload<GLTextureExtension>();
|
||||||
ExtensionPreload<ImmediateMode>();
|
ExtensionPreload<ImmediateMode>();
|
||||||
|
ExtensionPreload<DirectRendering>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsGLExtensionAvailable(string name)
|
public bool IsGLExtensionAvailable(string name)
|
||||||
@@ -154,12 +155,12 @@ namespace Dashboard.OpenGL
|
|||||||
|
|
||||||
public override void End()
|
public override void End()
|
||||||
{
|
{
|
||||||
base.End();
|
|
||||||
|
|
||||||
while (_afterDrawActions.TryDequeue(out Task? action))
|
while (_afterDrawActions.TryDequeue(out Task? action))
|
||||||
{
|
{
|
||||||
action.RunSynchronously();
|
action.RunSynchronously();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
base.End();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Dashboard.BlurgText.OpenGL;
|
using Dashboard.BlurgText.OpenGL;
|
||||||
using Dashboard.Controls;
|
using Dashboard.Controls;
|
||||||
using Dashboard.Drawing;
|
using Dashboard.Drawing;
|
||||||
|
using Dashboard.Events;
|
||||||
using Dashboard.OpenGL;
|
using Dashboard.OpenGL;
|
||||||
using Dashboard.OpenTK.PAL2;
|
using Dashboard.OpenTK.PAL2;
|
||||||
using Dashboard.Pal;
|
using Dashboard.Pal;
|
||||||
@@ -71,6 +72,34 @@ TK.Window.SetMode(window.WindowHandle, WindowMode.Normal);
|
|||||||
|
|
||||||
window.DeviceContext.ExtensionRequire<IDeviceContextBase>().ScaleOverride = 1.5f;
|
window.DeviceContext.ExtensionRequire<IDeviceContextBase>().ScaleOverride = 1.5f;
|
||||||
|
|
||||||
|
IDirectRendering direct = window.DeviceContext.ExtensionRequire<IDirectRendering>();
|
||||||
|
IBuffer vertex = direct.CreateBuffer(BufferAccessPattern.Static, 1024);
|
||||||
|
float[] vertices = new float[]
|
||||||
|
{ // x, y, z, _, r, g, b, a
|
||||||
|
-0.5f, -0.5f, 0.0f, 0.0f, 1, 0, 0, 1,
|
||||||
|
+0.5f, -0.5f, 0.0f, 0.0f, 0, 1, 0, 1,
|
||||||
|
+0.0f, +0.5f, 0.0f, 0.0f, 0, 0, 1, 1,
|
||||||
|
};
|
||||||
|
vertex.Write(0, vertices);
|
||||||
|
|
||||||
|
DrawCall call = new DrawCall(MeshPrimitive.Triangle, vertex)
|
||||||
|
{
|
||||||
|
ShaderPipeline = null,
|
||||||
|
Attributes =
|
||||||
|
[
|
||||||
|
new VertexAttribute(0, 3, VertexAttributeType.Float, 0, 8 * sizeof(float)),
|
||||||
|
new VertexAttribute(1, 4, VertexAttributeType.Float, 4 * sizeof(float), 8 * sizeof(float))
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
window.EventRaised += (sender, eventArgs) =>
|
||||||
|
{
|
||||||
|
if (eventArgs is not PaintEventArgs paint)
|
||||||
|
return;
|
||||||
|
|
||||||
|
paint.DeviceContext.ExtensionRequire<IDirectRendering>().Draw(call);
|
||||||
|
};
|
||||||
|
|
||||||
app.Run(true, source.Token);
|
app.Run(true, source.Token);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user