Compare commits
4 Commits
1457e32095
...
fe96499512
| Author | SHA1 | Date | |
|---|---|---|---|
| fe96499512 | |||
| 18cccf1e41 | |||
| 019ac6f4ae | |||
| 449c398f05 |
@@ -0,0 +1,216 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
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,
|
||||||
|
Byte,
|
||||||
|
Short,
|
||||||
|
Int,
|
||||||
|
Half,
|
||||||
|
Float,
|
||||||
|
Double,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum IndexType
|
||||||
|
{
|
||||||
|
UnsignedShort,
|
||||||
|
UnsignedInt,
|
||||||
|
UnsignedLong,
|
||||||
|
Short,
|
||||||
|
Int,
|
||||||
|
}
|
||||||
|
|
||||||
|
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(false, nameof(Unified), nameof(UnifiedEquation))]
|
||||||
|
public bool IsSeparate => Color != Alpha;
|
||||||
|
public BlendFactor? Unified => IsSeparate ? null : Color;
|
||||||
|
public BlendEquation? UnifiedEquation => IsSeparate ? null : ColorEquation;
|
||||||
|
public BlendEquation ColorEquation { get; init; } = BlendEquation.Add;
|
||||||
|
public BlendEquation AlphaEquation { 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(int Location, UniformType Type, long Offset, long Size);
|
||||||
|
|
||||||
|
public struct DrawCall(MeshPrimitive primitive, IBuffer vertexBuffer, int first, int count)
|
||||||
|
{
|
||||||
|
public IShader? ShaderPipeline { get; init; }
|
||||||
|
public Box2d ViewportRegion { get; init; } = new Box2d(Vector2.Zero, Vector2.PositiveInfinity);
|
||||||
|
public Box2d ScissorRegion { get; init; } = new Box2d(Vector2.PositiveInfinity, Vector2.PositiveInfinity);
|
||||||
|
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 int First { get; init; } = first;
|
||||||
|
public int Count { get; init; } = count;
|
||||||
|
public long Offset { get; init; } = 0;
|
||||||
|
public int BaseVertex { get; init; } = 0;
|
||||||
|
|
||||||
|
public IBuffer VertexBuffer { get; init; } = vertexBuffer;
|
||||||
|
public IBuffer? ElementBuffer { get; init; }
|
||||||
|
public ReadOnlyMemory<byte> UniformData { get; init; } = ReadOnlyMemory<byte>.Empty;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,7 +29,7 @@ namespace Dashboard.Events
|
|||||||
|
|
||||||
public enum KeyCode
|
public enum KeyCode
|
||||||
{
|
{
|
||||||
// TODO:
|
// TODO: Keycode table. Keycodes are subject to change upstream. SeeAlso: Extensions.cs@KeyCode ToDashboard(this Key key)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ScanCode
|
public enum ScanCode
|
||||||
@@ -160,10 +160,18 @@ namespace Dashboard.Events
|
|||||||
International4 = 0x79,
|
International4 = 0x79,
|
||||||
International5 = 0x7B,
|
International5 = 0x7B,
|
||||||
International6 = 0x5C,
|
International6 = 0x5C,
|
||||||
|
International7,
|
||||||
|
International8,
|
||||||
|
International9,
|
||||||
Lang1 = 0x72,
|
Lang1 = 0x72,
|
||||||
Lang2 = 0x71,
|
Lang2 = 0x71,
|
||||||
Lang3 = 0x78,
|
Lang3 = 0x78,
|
||||||
Lang4 = 0x77,
|
Lang4 = 0x77,
|
||||||
|
Lang5,
|
||||||
|
Lang6,
|
||||||
|
Lang7,
|
||||||
|
Lang8,
|
||||||
|
Lang9,
|
||||||
LCtrl = 0x1D,
|
LCtrl = 0x1D,
|
||||||
LShift = 0x2A,
|
LShift = 0x2A,
|
||||||
LWin = 0xE05B,
|
LWin = 0xE05B,
|
||||||
@@ -189,6 +197,9 @@ namespace Dashboard.Events
|
|||||||
Halt = 0xE068,
|
Halt = 0xE068,
|
||||||
Refresh = 0xE67,
|
Refresh = 0xE67,
|
||||||
Bookmarks = 0xE066,
|
Bookmarks = 0xE066,
|
||||||
|
|
||||||
|
NonUsSlashBar = 0xFF01,
|
||||||
|
Mute,
|
||||||
}
|
}
|
||||||
|
|
||||||
public class KeyboardButtonEventArgs(KeyCode keyCode, ScanCode scanCode, ModifierKeys modifierKeys, bool up)
|
public class KeyboardButtonEventArgs(KeyCode keyCode, ScanCode scanCode, ModifierKeys modifierKeys, bool up)
|
||||||
|
|||||||
@@ -42,11 +42,23 @@ namespace Dashboard.Pal
|
|||||||
Dispose(false);
|
Dispose(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Begin() { }
|
public virtual void Begin()
|
||||||
|
{
|
||||||
|
foreach (IDeviceContextExtension extension in _extensions)
|
||||||
|
{
|
||||||
|
extension.Begin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// public abstract void Paint(object renderbuffer);
|
// public abstract void Paint(object renderbuffer);
|
||||||
|
|
||||||
public virtual void End() { }
|
public virtual void End()
|
||||||
|
{
|
||||||
|
foreach (IDeviceContextExtension extension in _extensions)
|
||||||
|
{
|
||||||
|
extension.End();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsExtensionAvailable<T>() where T : IDeviceContextExtension
|
public bool IsExtensionAvailable<T>() where T : IDeviceContextExtension
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,5 +2,7 @@ namespace Dashboard.Pal
|
|||||||
{
|
{
|
||||||
public interface IDeviceContextExtension : IContextExtensionBase<DeviceContext>
|
public interface IDeviceContextExtension : IContextExtensionBase<DeviceContext>
|
||||||
{
|
{
|
||||||
|
void Begin() {}
|
||||||
|
void End() {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
using System.Numerics;
|
||||||
|
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;
|
||||||
|
|
||||||
|
private int _vao = -1;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Require(DeviceContext context) => Context = context;
|
||||||
|
public void Require(IContextBase context) => Require((DeviceContext)context);
|
||||||
|
|
||||||
|
public void Begin()
|
||||||
|
{
|
||||||
|
if (_vao == -1)
|
||||||
|
{
|
||||||
|
GL.CreateVertexArray(out _vao);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
Vector2 size = Context.FramebufferSize;
|
||||||
|
drawCall.SetAll(size, _vao, false);
|
||||||
|
|
||||||
|
if (drawCall.ElementBuffer is null)
|
||||||
|
{
|
||||||
|
GL.DrawArrays(drawCall.Primitive.OpenGL, drawCall.First, drawCall.Count);
|
||||||
|
}
|
||||||
|
else if (drawCall.BaseVertex == 0)
|
||||||
|
{
|
||||||
|
GL.DrawElements(drawCall.Primitive.OpenGL, drawCall.Count, drawCall.IndexType.OpenGL, (nint)drawCall.Offset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GL.DrawElementsBaseVertex(drawCall.Primitive.OpenGL, drawCall.Count, drawCall.IndexType.OpenGL, (nint)drawCall.Offset, drawCall.BaseVertex);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,474 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Dashboard.Drawing;
|
||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
using UniformType = Dashboard.Drawing.UniformType;
|
||||||
|
using Vector2 = System.Numerics.Vector2;
|
||||||
|
|
||||||
|
namespace Dashboard.OpenGL.Drawing
|
||||||
|
{
|
||||||
|
public static class GLStateExtensions
|
||||||
|
{
|
||||||
|
extension(VertexAttributeType type)
|
||||||
|
{
|
||||||
|
public All OpenGL => type switch
|
||||||
|
{
|
||||||
|
VertexAttributeType.UnsignedByte => All.UnsignedByte,
|
||||||
|
VertexAttributeType.UnsignedShort => All.UnsignedShort,
|
||||||
|
VertexAttributeType.UnsignedInt => All.UnsignedInt,
|
||||||
|
VertexAttributeType.Byte => All.Byte,
|
||||||
|
VertexAttributeType.Short => All.Short,
|
||||||
|
VertexAttributeType.Int => All.Int,
|
||||||
|
VertexAttributeType.Half => All.HalfFloat,
|
||||||
|
VertexAttributeType.Float => All.Float,
|
||||||
|
VertexAttributeType.Double => All.Double,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(),
|
||||||
|
};
|
||||||
|
|
||||||
|
public VertexAttribType VertexAttribType => (VertexAttribType)type.OpenGL;
|
||||||
|
public VertexAttribPointerType VertexAttribPointerType => (VertexAttribPointerType)type.OpenGL;
|
||||||
|
}
|
||||||
|
|
||||||
|
extension(TextureType type)
|
||||||
|
{
|
||||||
|
public TextureTarget OpenGL => type switch
|
||||||
|
{
|
||||||
|
TextureType.Texture1D => TextureTarget.Texture1D,
|
||||||
|
TextureType.Texture2D => TextureTarget.Texture2D,
|
||||||
|
TextureType.Texture2DArray => TextureTarget.Texture2DArray,
|
||||||
|
TextureType.Texture2DCube => TextureTarget.TextureCubeMap,
|
||||||
|
TextureType.Texture3D => TextureTarget.Texture3D,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
extension(BlendEquation eq)
|
||||||
|
{
|
||||||
|
public BlendEquationMode OpenGL => eq switch
|
||||||
|
{
|
||||||
|
BlendEquation.Add => BlendEquationMode.FuncAdd,
|
||||||
|
BlendEquation.Subtract => BlendEquationMode.FuncSubtract,
|
||||||
|
BlendEquation.Max => BlendEquationMode.Max,
|
||||||
|
BlendEquation.Min => BlendEquationMode.Min,
|
||||||
|
BlendEquation.ReverseSubtract => BlendEquationMode.FuncReverseSubtract,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
extension(BlendFunction mode)
|
||||||
|
{
|
||||||
|
public BlendingFactor OpenGL => mode switch
|
||||||
|
{
|
||||||
|
BlendFunction.One => BlendingFactor.One,
|
||||||
|
BlendFunction.Zero => BlendingFactor.Zero,
|
||||||
|
BlendFunction.SourceAlpha => BlendingFactor.SrcAlpha,
|
||||||
|
BlendFunction.OneMinusSourceAlpha => BlendingFactor.OneMinusSrcAlpha,
|
||||||
|
BlendFunction.DestinationAlpha => BlendingFactor.DstAlpha,
|
||||||
|
BlendFunction.OneMinusDestinationAlpha => BlendingFactor.OneMinusDstAlpha,
|
||||||
|
BlendFunction.SourceColor => BlendingFactor.SrcColor,
|
||||||
|
BlendFunction.OneMinusSourceColor => BlendingFactor.OneMinusSrcColor,
|
||||||
|
BlendFunction.DestinationColor => BlendingFactor.DstColor,
|
||||||
|
BlendFunction.OneMinusDestinationColor => BlendingFactor.OneMinusDstColor,
|
||||||
|
BlendFunction.ConstantColor => BlendingFactor.OneMinusConstantColor,
|
||||||
|
BlendFunction.OneMinusConstantColor => BlendingFactor.OneMinusConstantColor,
|
||||||
|
BlendFunction.ConstantAlpha => BlendingFactor.OneMinusConstantAlpha,
|
||||||
|
BlendFunction.OneMinusConstantAlpha => BlendingFactor.OneMinusConstantAlpha,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
extension(TestFunction test)
|
||||||
|
{
|
||||||
|
public All OpenGL => test switch
|
||||||
|
{
|
||||||
|
TestFunction.Always => All.Always,
|
||||||
|
TestFunction.Equal => All.Equal,
|
||||||
|
TestFunction.Greater => All.Greater,
|
||||||
|
TestFunction.GreaterThanOrEqual => All.Gequal,
|
||||||
|
TestFunction.LessThan => All.Less,
|
||||||
|
TestFunction.LessThanOrEqual => All.Lequal,
|
||||||
|
TestFunction.Never => All.Never,
|
||||||
|
TestFunction.NotEqual => All.Notequal,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(),
|
||||||
|
};
|
||||||
|
|
||||||
|
public DepthFunction DepthFunction => (DepthFunction)test.OpenGL;
|
||||||
|
public StencilFunction StencilFunction => (StencilFunction)test.OpenGL;
|
||||||
|
}
|
||||||
|
|
||||||
|
extension(StencilOperation op)
|
||||||
|
{
|
||||||
|
public StencilOp OpenGL => op switch
|
||||||
|
{
|
||||||
|
StencilOperation.Keep => StencilOp.Keep,
|
||||||
|
StencilOperation.Zero => StencilOp.Zero,
|
||||||
|
StencilOperation.Replace => StencilOp.Replace,
|
||||||
|
StencilOperation.Increment => StencilOp.Incr,
|
||||||
|
StencilOperation.UncheckedIncrement => StencilOp.IncrWrap,
|
||||||
|
StencilOperation.Decrement => StencilOp.Decr,
|
||||||
|
StencilOperation.UncheckedDecrement => StencilOp.DecrWrap,
|
||||||
|
StencilOperation.Invert => StencilOp.Invert,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
extension(MeshPrimitive primitive)
|
||||||
|
{
|
||||||
|
public PrimitiveType OpenGL => primitive switch
|
||||||
|
{
|
||||||
|
MeshPrimitive.Line => PrimitiveType.Lines,
|
||||||
|
MeshPrimitive.Point => PrimitiveType.Points,
|
||||||
|
MeshPrimitive.Triangle => PrimitiveType.Triangles,
|
||||||
|
MeshPrimitive.TriangleFan => PrimitiveType.TriangleFan,
|
||||||
|
MeshPrimitive.TriangleStrip => PrimitiveType.TriangleStrip,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
extension(IndexType index)
|
||||||
|
{
|
||||||
|
public DrawElementsType OpenGL => index switch
|
||||||
|
{
|
||||||
|
IndexType.Int or IndexType.UnsignedInt => DrawElementsType.UnsignedInt,
|
||||||
|
IndexType.Short or IndexType.UnsignedShort => DrawElementsType.UnsignedShort,
|
||||||
|
IndexType.UnsignedLong => throw new NotSupportedException(),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
extension(BlendMode mode)
|
||||||
|
{
|
||||||
|
public void SetEnabled()
|
||||||
|
{
|
||||||
|
if (mode.Enabled)
|
||||||
|
GL.Enable(EnableCap.Blend);
|
||||||
|
else
|
||||||
|
GL.Disable(EnableCap.Blend);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMode()
|
||||||
|
{
|
||||||
|
if (mode.IsSeparate)
|
||||||
|
{
|
||||||
|
GL.BlendEquationSeparate(mode.ColorEquation.OpenGL, mode.AlphaEquation.OpenGL);
|
||||||
|
GL.BlendFuncSeparate(
|
||||||
|
mode.Color.Source.OpenGL, mode.Color.Destination.OpenGL,
|
||||||
|
mode.Alpha.Source.OpenGL, mode.Alpha.Destination.OpenGL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GL.BlendEquation(mode.UnifiedEquation.Value.OpenGL);
|
||||||
|
GL.BlendFunc(mode.Unified.Value.Source.OpenGL, mode.Unified.Value.Destination.OpenGL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetConstant()
|
||||||
|
{
|
||||||
|
GL.BlendColor(mode.Constant.R / 255f, mode.Constant.G / 255f,mode.Constant.B / 255f,mode.Constant.A / 255f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetAll()
|
||||||
|
{
|
||||||
|
mode.SetEnabled();
|
||||||
|
mode.SetConstant();
|
||||||
|
mode.SetMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension(DepthMode mode)
|
||||||
|
{
|
||||||
|
public void SetEnabled()
|
||||||
|
{
|
||||||
|
if (mode.Test)
|
||||||
|
GL.Enable(EnableCap.DepthTest);
|
||||||
|
else
|
||||||
|
GL.Disable(EnableCap.DepthTest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMask() => GL.DepthMask(mode.Mask);
|
||||||
|
|
||||||
|
public void SetFunction() => GL.DepthFunc(mode.Function.DepthFunction);
|
||||||
|
|
||||||
|
public void SetAll()
|
||||||
|
{
|
||||||
|
mode.SetEnabled();
|
||||||
|
mode.SetMask();
|
||||||
|
mode.SetFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension(StencilMode mode)
|
||||||
|
{
|
||||||
|
public void SetEnabled()
|
||||||
|
{
|
||||||
|
if (mode.Enabled)
|
||||||
|
GL.Enable(EnableCap.StencilTest);
|
||||||
|
else
|
||||||
|
GL.Disable(EnableCap.StencilTest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFunction() => GL.StencilFunc(mode.Function.StencilFunction, mode.Reference, (uint)mode.Mask);
|
||||||
|
|
||||||
|
public void SetOperations() => GL.StencilOp(mode.Fail.OpenGL, mode.DepthFail.OpenGL, mode.Pass.OpenGL);
|
||||||
|
|
||||||
|
public void SetAll()
|
||||||
|
{
|
||||||
|
mode.SetEnabled();
|
||||||
|
mode.SetFunction();
|
||||||
|
mode.SetOperations();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension(DrawCall call)
|
||||||
|
{
|
||||||
|
public void UsePipeline() => GL.UseProgram((call.ShaderPipeline as GLShader)?.Handle ?? 0);
|
||||||
|
|
||||||
|
// TODO: clamp to viewport size
|
||||||
|
|
||||||
|
public void SetViewport(Vector2 size)
|
||||||
|
{
|
||||||
|
Vector2 min = Vector2.Max(Vector2.Zero, call.ViewportRegion.Min);
|
||||||
|
Vector2 max = Vector2.Min(size, call.ViewportRegion.Max);
|
||||||
|
|
||||||
|
GL.Viewport(
|
||||||
|
(int)Math.Round(min.X),
|
||||||
|
(int)Math.Round(size.Y - max.Y),
|
||||||
|
(int)Math.Round(max.X - min.X),
|
||||||
|
(int)Math.Round(max.Y - min.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetScissor(Vector2 size)
|
||||||
|
{
|
||||||
|
if (call.ScissorRegion == new Box2d(Vector2.PositiveInfinity, Vector2.PositiveInfinity))
|
||||||
|
{
|
||||||
|
GL.Disable(EnableCap.ScissorTest);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL.Enable(EnableCap.ScissorTest);
|
||||||
|
GL.Scissor(
|
||||||
|
(int)Math.Round(call.ViewportRegion.Left),
|
||||||
|
(int)Math.Round(size.Y - call.ViewportRegion.Top),
|
||||||
|
(int)Math.Round(call.ViewportRegion.Right - call.ViewportRegion.Left),
|
||||||
|
(int)Math.Round(call.ViewportRegion.Bottom - call.ViewportRegion.Top));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFrontFace()
|
||||||
|
{
|
||||||
|
GL.FrontFace(call.FrontFace switch
|
||||||
|
{
|
||||||
|
WindingOrder.Clockwise => FrontFaceDirection.Cw,
|
||||||
|
WindingOrder.Counterclockwise => FrontFaceDirection.Ccw,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCullMode()
|
||||||
|
{
|
||||||
|
if (call.CullMode == FaceCulling.None)
|
||||||
|
{
|
||||||
|
GL.Disable(EnableCap.CullFace);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL.Enable(EnableCap.CullFace);
|
||||||
|
|
||||||
|
GL.CullFace(call.CullMode switch
|
||||||
|
{
|
||||||
|
FaceCulling.Both => TriangleFace.FrontAndBack,
|
||||||
|
FaceCulling.Front => TriangleFace.Front,
|
||||||
|
FaceCulling.Back => TriangleFace.Back,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBlendMode() => call.BlendMode.SetAll();
|
||||||
|
public void SetDepthMode() => call.DepthMode.SetAll();
|
||||||
|
public void SetStencilMode() => call.StencilMode.SetAll();
|
||||||
|
|
||||||
|
public void SetPrimitiveRestart()
|
||||||
|
{
|
||||||
|
if (!call.EnableRestart)
|
||||||
|
{
|
||||||
|
GL.Disable(EnableCap.PrimitiveRestart);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GL.Enable(EnableCap.PrimitiveRestart);
|
||||||
|
GL.PrimitiveRestartIndex((uint)call.RestartIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetColorMask()
|
||||||
|
{
|
||||||
|
GL.ColorMask(call.RedMask, call.GreenMask, call.BlueMask, call.AlphaMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPointSize()
|
||||||
|
{
|
||||||
|
GL.PointSize(call.PointSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLineWidth()
|
||||||
|
{
|
||||||
|
GL.LineWidth(call.LineWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UseTextures()
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
foreach (ITexture? texture in call.Textures)
|
||||||
|
{
|
||||||
|
GL.ActiveTexture((TextureUnit)((int)TextureUnit.Texture0 + i));
|
||||||
|
if (texture is GLTexture glTexture)
|
||||||
|
{
|
||||||
|
GL.BindTexture(glTexture.Type.OpenGL, glTexture.Handle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateVertexArrays(int vao)
|
||||||
|
{
|
||||||
|
GL.BindVertexArray(vao);
|
||||||
|
|
||||||
|
if (call.ElementBuffer != null)
|
||||||
|
{
|
||||||
|
GL.BindBuffer(BufferTarget.ElementArrayBuffer, (call.ElementBuffer as GLBuffer)?.Handle ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
GL.BindBuffer(BufferTarget.ArrayBuffer, (call.VertexBuffer as GLBuffer)?.Handle ?? 0);
|
||||||
|
|
||||||
|
foreach(VertexAttribute attrib in call.Attributes)
|
||||||
|
{
|
||||||
|
GL.VertexAttribPointer(
|
||||||
|
(uint)attrib.Location,
|
||||||
|
attrib.Components,
|
||||||
|
attrib.Type.VertexAttribPointerType,
|
||||||
|
false,
|
||||||
|
(int)attrib.Stride,
|
||||||
|
(nint)attrib.Offset);
|
||||||
|
GL.EnableVertexAttribArray((uint)attrib.Location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateUniforms(bool transpose = false)
|
||||||
|
{
|
||||||
|
int bufferIndex = 0;
|
||||||
|
foreach (UniformDescriptor uniform in call.Uniforms)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<byte> uniformData = call.UniformData.Span;
|
||||||
|
|
||||||
|
switch (uniform.Type)
|
||||||
|
{
|
||||||
|
case UniformType.I1:
|
||||||
|
{
|
||||||
|
ReadOnlySpan<int> span = CastSpan<int>(uniformData, uniform.Offset, uniform.Size);
|
||||||
|
GL.Uniform1i(uniform.Location, span.Length, span);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UniformType.I2:
|
||||||
|
{
|
||||||
|
ReadOnlySpan<Vector2i> span = CastSpan<Vector2i>(uniformData, uniform.Offset, uniform.Size);
|
||||||
|
GL.Uniform2i(uniform.Location, span.Length, span);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UniformType.I3:
|
||||||
|
{
|
||||||
|
ReadOnlySpan<Vector3i> span = CastSpan<Vector3i>(uniformData, uniform.Offset, uniform.Size);
|
||||||
|
GL.Uniform3i(uniform.Location, span.Length, span);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UniformType.I4:
|
||||||
|
{
|
||||||
|
ReadOnlySpan<Vector4i> span = CastSpan<Vector4i>(uniformData, uniform.Offset, uniform.Size);
|
||||||
|
GL.Uniform4i(uniform.Location, span.Length, span);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UniformType.F1:
|
||||||
|
{
|
||||||
|
ReadOnlySpan<float> span = CastSpan<float>(uniformData, uniform.Offset, uniform.Size);
|
||||||
|
GL.Uniform1f(uniform.Location, span.Length, span);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UniformType.F2:
|
||||||
|
{
|
||||||
|
ReadOnlySpan<Vector2> span = CastSpan<Vector2>(uniformData, uniform.Offset, uniform.Size);
|
||||||
|
GL.Uniform2f(uniform.Location, span.Length, span);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UniformType.F3:
|
||||||
|
{
|
||||||
|
ReadOnlySpan<Vector3> span = CastSpan<Vector3>(uniformData, uniform.Offset, uniform.Size);
|
||||||
|
GL.Uniform3f(uniform.Location, span.Length, span);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UniformType.F4:
|
||||||
|
{
|
||||||
|
ReadOnlySpan<Vector4> span = CastSpan<Vector4>(uniformData, uniform.Offset, uniform.Size);
|
||||||
|
GL.Uniform4f(uniform.Location, span.Length, span);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UniformType.Mat2:
|
||||||
|
{
|
||||||
|
ReadOnlySpan<Matrix2> span = CastSpan<Matrix2>(uniformData, uniform.Offset, uniform.Size);
|
||||||
|
GL.UniformMatrix2f(uniform.Location, span.Length, transpose, span);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UniformType.Mat3:
|
||||||
|
{
|
||||||
|
ReadOnlySpan<Matrix3> span = CastSpan<Matrix3>(uniformData, uniform.Offset, uniform.Size);
|
||||||
|
GL.UniformMatrix3f(uniform.Location, span.Length, transpose, span);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UniformType.Mat4:
|
||||||
|
{
|
||||||
|
ReadOnlySpan<Matrix4> span = CastSpan<Matrix4>(uniformData, uniform.Offset, uniform.Size);
|
||||||
|
GL.UniformMatrix4f(uniform.Location, span.Length, transpose, span);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UniformType.Buffer:
|
||||||
|
{
|
||||||
|
int buffer = (call.UniformBuffer as GLBuffer)?.Handle ?? 0;
|
||||||
|
int index = bufferIndex++;
|
||||||
|
GL.BindBufferRange(BufferTarget.UniformBuffer, (uint)index, buffer, (nint)uniform.Offset, (int)uniform.Size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
ReadOnlySpan<T> CastSpan<T>(ReadOnlySpan<byte> bytes, long offset, long size)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
return MemoryMarshal.Cast<byte, T>(bytes.Slice((int)offset, (int)size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetAll(Vector2 size, int vao, bool transpose = false)
|
||||||
|
{
|
||||||
|
call.UsePipeline();
|
||||||
|
call.SetViewport(size); call.SetScissor(size);
|
||||||
|
call.SetFrontFace();
|
||||||
|
call.SetCullMode();
|
||||||
|
call.SetBlendMode();
|
||||||
|
call.SetDepthMode();
|
||||||
|
call.SetStencilMode();
|
||||||
|
call.SetPrimitiveRestart();
|
||||||
|
call.SetColorMask();
|
||||||
|
call.SetDepthMode();
|
||||||
|
call.SetPointSize();
|
||||||
|
call.SetLineWidth();
|
||||||
|
call.UseTextures();
|
||||||
|
call.UpdateUniforms(transpose);
|
||||||
|
call.UpdateVertexArrays(vao);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -0,0 +1,204 @@
|
|||||||
|
using Dashboard.Events;
|
||||||
|
using OpenTK.Platform;
|
||||||
|
|
||||||
|
namespace Dashboard.OpenTK.PAL2
|
||||||
|
{
|
||||||
|
public static class Extensions
|
||||||
|
{
|
||||||
|
public static ModifierKeys ToDashboard(this KeyModifier modifier)
|
||||||
|
{
|
||||||
|
ModifierKeys keys = 0;
|
||||||
|
|
||||||
|
keys |= modifier.HasFlag(KeyModifier.NumLock) ? ModifierKeys.NumLock : 0;
|
||||||
|
keys |= modifier.HasFlag(KeyModifier.CapsLock) ? ModifierKeys.CapsLock : 0;
|
||||||
|
keys |= modifier.HasFlag(KeyModifier.ScrollLock) ? ModifierKeys.ScrollLock : 0;
|
||||||
|
|
||||||
|
keys |= modifier.HasFlag(KeyModifier.LeftShift) ? ModifierKeys.LeftShift : 0;
|
||||||
|
keys |= modifier.HasFlag(KeyModifier.LeftControl) ? ModifierKeys.LeftControl : 0;
|
||||||
|
keys |= modifier.HasFlag(KeyModifier.LeftAlt) ? ModifierKeys.LeftAlt : 0;
|
||||||
|
keys |= modifier.HasFlag(KeyModifier.LeftGUI) ? ModifierKeys.LeftMeta : 0;
|
||||||
|
|
||||||
|
keys |= modifier.HasFlag(KeyModifier.RightShift) ? ModifierKeys.RightShift : 0;
|
||||||
|
keys |= modifier.HasFlag(KeyModifier.RightControl) ? ModifierKeys.RightControl : 0;
|
||||||
|
keys |= modifier.HasFlag(KeyModifier.RightAlt) ? ModifierKeys.RightAlt : 0;
|
||||||
|
keys |= modifier.HasFlag(KeyModifier.RightGUI) ? ModifierKeys.RightMeta : 0;
|
||||||
|
|
||||||
|
keys |= modifier.HasFlag(KeyModifier.Shift) ? ModifierKeys.Shift : 0;
|
||||||
|
keys |= modifier.HasFlag(KeyModifier.Control) ? ModifierKeys.Control : 0;
|
||||||
|
keys |= modifier.HasFlag(KeyModifier.Alt) ? ModifierKeys.Alt : 0;
|
||||||
|
keys |= modifier.HasFlag(KeyModifier.GUI) ? ModifierKeys.Meta : 0;
|
||||||
|
|
||||||
|
// C# makes this cast as annoying as possible.
|
||||||
|
keys |= (ModifierKeys)((((int)keys >> (int)ModifierKeys.RightBitPos) & 0xF) |
|
||||||
|
(((int)keys >> (int)ModifierKeys.LeftBitPos) & 0xF));
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static KeyCode ToDashboard(this Key key) => key switch
|
||||||
|
{
|
||||||
|
// TODO: Keycode table. Keycodes are subject to change upstream.
|
||||||
|
_ => (KeyCode)0,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static ScanCode ToDashboard(this Scancode scanCode) => scanCode switch
|
||||||
|
{
|
||||||
|
// TODO: Revise this array.
|
||||||
|
Scancode.Unknown => ScanCode.Error,
|
||||||
|
|
||||||
|
Scancode.A => ScanCode.A,
|
||||||
|
Scancode.B => ScanCode.B,
|
||||||
|
Scancode.C => ScanCode.C,
|
||||||
|
Scancode.D => ScanCode.D,
|
||||||
|
Scancode.E => ScanCode.E,
|
||||||
|
Scancode.F => ScanCode.F,
|
||||||
|
Scancode.G => ScanCode.G,
|
||||||
|
Scancode.H => ScanCode.H,
|
||||||
|
Scancode.I => ScanCode.I,
|
||||||
|
Scancode.J => ScanCode.J,
|
||||||
|
Scancode.K => ScanCode.K,
|
||||||
|
Scancode.L => ScanCode.L,
|
||||||
|
Scancode.M => ScanCode.M,
|
||||||
|
Scancode.N => ScanCode.N,
|
||||||
|
Scancode.O => ScanCode.O,
|
||||||
|
Scancode.P => ScanCode.P,
|
||||||
|
Scancode.Q => ScanCode.Q,
|
||||||
|
Scancode.R => ScanCode.R,
|
||||||
|
Scancode.S => ScanCode.S,
|
||||||
|
Scancode.T => ScanCode.T,
|
||||||
|
Scancode.U => ScanCode.U,
|
||||||
|
Scancode.V => ScanCode.V,
|
||||||
|
Scancode.W => ScanCode.W,
|
||||||
|
Scancode.X => ScanCode.X,
|
||||||
|
Scancode.Y => ScanCode.Y,
|
||||||
|
Scancode.Z => ScanCode.Z,
|
||||||
|
|
||||||
|
Scancode.D0 => ScanCode.D0,
|
||||||
|
Scancode.D1 => ScanCode.D1,
|
||||||
|
Scancode.D2 => ScanCode.D2,
|
||||||
|
Scancode.D3 => ScanCode.D3,
|
||||||
|
Scancode.D4 => ScanCode.D4,
|
||||||
|
Scancode.D5 => ScanCode.D5,
|
||||||
|
Scancode.D6 => ScanCode.D6,
|
||||||
|
Scancode.D7 => ScanCode.D7,
|
||||||
|
Scancode.D8 => ScanCode.D8,
|
||||||
|
Scancode.D9 => ScanCode.D9,
|
||||||
|
|
||||||
|
Scancode.Return => ScanCode.Return,
|
||||||
|
Scancode.Escape => ScanCode.Esc,
|
||||||
|
Scancode.Backspace => ScanCode.Backspace,
|
||||||
|
Scancode.Tab => ScanCode.Tab,
|
||||||
|
Scancode.Spacebar => ScanCode.Space,
|
||||||
|
Scancode.Dash => ScanCode.Dash,
|
||||||
|
Scancode.Equals => ScanCode.Equals,
|
||||||
|
Scancode.LeftBrace => ScanCode.LBracket,
|
||||||
|
Scancode.RightBrace => ScanCode.RBracket,
|
||||||
|
Scancode.SemiColon => ScanCode.Semicolon,
|
||||||
|
Scancode.LeftApostrophe => ScanCode.Apostrophe,
|
||||||
|
Scancode.GraveAccent => ScanCode.Grave,
|
||||||
|
Scancode.Comma => ScanCode.Comma,
|
||||||
|
Scancode.Period => ScanCode.Period,
|
||||||
|
Scancode.CapsLock => ScanCode.CapsLock,
|
||||||
|
|
||||||
|
Scancode.F1 => ScanCode.F1,
|
||||||
|
Scancode.F2 => ScanCode.F2,
|
||||||
|
Scancode.F3 => ScanCode.F3,
|
||||||
|
Scancode.F4 => ScanCode.F4,
|
||||||
|
Scancode.F5 => ScanCode.F5,
|
||||||
|
Scancode.F6 => ScanCode.F6,
|
||||||
|
Scancode.F7 => ScanCode.F7,
|
||||||
|
Scancode.F8 => ScanCode.F8,
|
||||||
|
Scancode.F9 => ScanCode.F9,
|
||||||
|
Scancode.F10 => ScanCode.F10,
|
||||||
|
Scancode.F11 => ScanCode.F11,
|
||||||
|
Scancode.F12 => ScanCode.F12,
|
||||||
|
Scancode.F13 => ScanCode.F13,
|
||||||
|
Scancode.F14 => ScanCode.F14,
|
||||||
|
Scancode.F15 => ScanCode.F15,
|
||||||
|
Scancode.F16 => ScanCode.F16,
|
||||||
|
Scancode.F17 => ScanCode.F17,
|
||||||
|
Scancode.F18 => ScanCode.F18,
|
||||||
|
Scancode.F19 => ScanCode.F19,
|
||||||
|
Scancode.F20 => ScanCode.F20,
|
||||||
|
Scancode.F21 => ScanCode.F21,
|
||||||
|
Scancode.F22 => ScanCode.F22,
|
||||||
|
Scancode.F23 => ScanCode.F23,
|
||||||
|
Scancode.F24 => ScanCode.F24,
|
||||||
|
|
||||||
|
Scancode.PrintScreen => ScanCode.PrintScr,
|
||||||
|
Scancode.ScrollLock => ScanCode.ScrollLock,
|
||||||
|
Scancode.Pause => ScanCode.Pause,
|
||||||
|
Scancode.Insert => ScanCode.Insert,
|
||||||
|
Scancode.Home => ScanCode.Home,
|
||||||
|
Scancode.PageUp => ScanCode.PageUp,
|
||||||
|
Scancode.Delete => ScanCode.Delete,
|
||||||
|
Scancode.End => ScanCode.End,
|
||||||
|
Scancode.PageDown => ScanCode.PageDown,
|
||||||
|
Scancode.RightArrow => ScanCode.RightArrow,
|
||||||
|
Scancode.LeftArrow => ScanCode.LeftArrow,
|
||||||
|
Scancode.DownArrow => ScanCode.DownArrow,
|
||||||
|
Scancode.UpArrow => ScanCode.UpArrow,
|
||||||
|
|
||||||
|
Scancode.NumLock => ScanCode.NumLock,
|
||||||
|
Scancode.KeypadEnter => ScanCode.NumEnter,
|
||||||
|
Scancode.Keypad1 => ScanCode.Num1,
|
||||||
|
Scancode.Keypad2 => ScanCode.Num2,
|
||||||
|
Scancode.Keypad3 => ScanCode.Num3,
|
||||||
|
Scancode.Keypad4 => ScanCode.Num4,
|
||||||
|
Scancode.Keypad5 => ScanCode.Num5,
|
||||||
|
Scancode.Keypad6 => ScanCode.Num6,
|
||||||
|
Scancode.Keypad7 => ScanCode.Num7,
|
||||||
|
Scancode.Keypad8 => ScanCode.Num8,
|
||||||
|
Scancode.Keypad9 => ScanCode.Num9,
|
||||||
|
Scancode.Keypad0 => ScanCode.Num0,
|
||||||
|
Scancode.KeypadForwardSlash => ScanCode.NumDiv,
|
||||||
|
Scancode.KeypadPeriod => ScanCode.NumDecimal,
|
||||||
|
Scancode.KeypadStar => ScanCode.NumMul,
|
||||||
|
Scancode.KeypadDash => ScanCode.NumSub,
|
||||||
|
Scancode.KeypadPlus => ScanCode.NumAdd,
|
||||||
|
Scancode.KeypadEquals => ScanCode.NumEquals,
|
||||||
|
Scancode.KeypadComma => ScanCode.NumComma,
|
||||||
|
Scancode.Application => ScanCode.Application,
|
||||||
|
|
||||||
|
Scancode.International1 => ScanCode.International1,
|
||||||
|
Scancode.International2 => ScanCode.International2,
|
||||||
|
Scancode.International3 => ScanCode.International3,
|
||||||
|
Scancode.International4 => ScanCode.International4,
|
||||||
|
Scancode.International5 => ScanCode.International5,
|
||||||
|
Scancode.International6 => ScanCode.International6,
|
||||||
|
|
||||||
|
Scancode.LANG1 => ScanCode.Lang1,
|
||||||
|
Scancode.LANG2 => ScanCode.Lang2,
|
||||||
|
Scancode.LANG3 => ScanCode.Lang3,
|
||||||
|
Scancode.LANG4 => ScanCode.Lang4,
|
||||||
|
Scancode.LANG5 => ScanCode.Lang5,
|
||||||
|
|
||||||
|
Scancode.LeftControl => ScanCode.LCtrl,
|
||||||
|
Scancode.LeftShift => ScanCode.LShift,
|
||||||
|
Scancode.LeftAlt => ScanCode.LAlt,
|
||||||
|
Scancode.LeftGUI => ScanCode.LWin,
|
||||||
|
|
||||||
|
Scancode.RightControl => ScanCode.RCtrl,
|
||||||
|
Scancode.RightShift => ScanCode.RShift,
|
||||||
|
Scancode.RightAlt => ScanCode.RAlt,
|
||||||
|
Scancode.RightGUI => ScanCode.RWin,
|
||||||
|
|
||||||
|
Scancode.SystemPowerDown => ScanCode.SystemPowerDown,
|
||||||
|
Scancode.SystemSleep => ScanCode.SystemSleep,
|
||||||
|
Scancode.SystemWakeUp => ScanCode.SystemWakeUp,
|
||||||
|
|
||||||
|
Scancode.ScanNextTrack => ScanCode.NextTrack,
|
||||||
|
Scancode.ScanPreviousTrack => ScanCode.PrevTrack,
|
||||||
|
Scancode.Stop => ScanCode.Stop,
|
||||||
|
Scancode.PlayPause => ScanCode.PlayPause,
|
||||||
|
Scancode.Mute => ScanCode.Mute,
|
||||||
|
Scancode.VolumeIncrement => ScanCode.VolUp,
|
||||||
|
Scancode.VolumeDecrement => ScanCode.VolDown,
|
||||||
|
Scancode.NonUSSlashBar => ScanCode.NonUsSlashBar,
|
||||||
|
Scancode.Pipe => ScanCode.Backslash,
|
||||||
|
Scancode.QuestionMark => ScanCode.ForwardSlash,
|
||||||
|
_ => (ScanCode)0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ namespace Dashboard.OpenTK.PAL2
|
|||||||
public override string DriverName => "Dashboard OpenTK PAL2.0 Driver";
|
public override string DriverName => "Dashboard OpenTK PAL2.0 Driver";
|
||||||
public override string DriverVendor => "Dashboard";
|
public override string DriverVendor => "Dashboard";
|
||||||
public override Version DriverVersion => new Version(0, 1);
|
public override Version DriverVersion => new Version(0, 1);
|
||||||
public GraphicsApiHints GraphicsApiHints { get; set; } = new OpenGLGraphicsApiHints();
|
public GraphicsApiHints GraphicsApiHints { get; init; } = new OpenGLGraphicsApiHints();
|
||||||
|
|
||||||
private readonly List<PhysicalWindow> _windows = new List<PhysicalWindow>();
|
private readonly List<PhysicalWindow> _windows = new List<PhysicalWindow>();
|
||||||
|
|
||||||
@@ -49,9 +49,8 @@ namespace Dashboard.OpenTK.PAL2
|
|||||||
TK.Window.PostUserEvent(new ApplicationQuitEventArgs());
|
TK.Window.PostUserEvent(new ApplicationQuitEventArgs());
|
||||||
});
|
});
|
||||||
|
|
||||||
Toolkit.Event.EventRaised += OnEventRaised;
|
TK.Event.EventRaised += OnEventRaised;
|
||||||
|
TK.Init(options ?? new ToolkitOptions());
|
||||||
Toolkit.Init(options ?? new ToolkitOptions());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RemoveWindow(PhysicalWindow window)
|
internal void RemoveWindow(PhysicalWindow window)
|
||||||
@@ -115,40 +114,36 @@ namespace Dashboard.OpenTK.PAL2
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: fix this.
|
// TODO: fix this.
|
||||||
switch (PlatformEventType.UserMessage)
|
switch (args)
|
||||||
{
|
{
|
||||||
case PlatformEventType.UserMessage:
|
case ApplicationQuitEventArgs:
|
||||||
if (args is ApplicationQuitEventArgs)
|
|
||||||
{
|
{
|
||||||
Quit = true;
|
Quit = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
// Mouse Events
|
// Mouse Events
|
||||||
case PlatformEventType.MouseDown:
|
case MouseButtonDownEventArgs mdown:
|
||||||
{
|
{
|
||||||
MouseButtonDownEventArgs down = (MouseButtonDownEventArgs)args;
|
MouseButtons buttons = (MouseButtons)(1 << (int)mdown.Button);
|
||||||
MouseButtons buttons = (MouseButtons)(1 << (int)down.Button);
|
ModifierKeys modifierKeys = mdown.Modifiers.ToDashboard();
|
||||||
ModifierKeys modifierKeys = GetModifierKeys(down.Modifiers);
|
|
||||||
// TODO: modifier keys
|
// TODO: modifier keys
|
||||||
MouseButtonEventArgs down2 = new MouseButtonEventArgs(info.MousePosition, buttons, modifierKeys, false);
|
MouseButtonEventArgs down2 = new MouseButtonEventArgs(info.MousePosition, buttons, modifierKeys, false);
|
||||||
info.Window.SendEvent(this, down2);
|
info.Window.SendEvent(this, down2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PlatformEventType.MouseUp:
|
case MouseButtonUpEventArgs mup:
|
||||||
{
|
{
|
||||||
MouseButtonUpEventArgs up = (MouseButtonUpEventArgs)args;
|
MouseButtons buttons = (MouseButtons)(1 << (int)mup.Button);
|
||||||
MouseButtons buttons = (MouseButtons)(1 << (int)up.Button);
|
ModifierKeys modifierKeys = mup.Modifiers.ToDashboard();
|
||||||
ModifierKeys modifierKeys = GetModifierKeys(up.Modifiers);
|
|
||||||
// TODO: modifier keys
|
// TODO: modifier keys
|
||||||
MouseButtonEventArgs up2 = new MouseButtonEventArgs(info.MousePosition, buttons, modifierKeys, true);
|
MouseButtonEventArgs up2 = new MouseButtonEventArgs(info.MousePosition, buttons, modifierKeys, true);
|
||||||
info.Window.SendEvent(this, up2);
|
info.Window.SendEvent(this, up2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PlatformEventType.MouseMove:
|
case OPENTK.MouseMoveEventArgs mmove:
|
||||||
{
|
{
|
||||||
OPENTK.MouseMoveEventArgs move = (OPENTK.MouseMoveEventArgs)args;
|
Vector2 position = new Vector2(mmove.ClientPosition.X, mmove.ClientPosition.Y);
|
||||||
Vector2 position = new Vector2(move.ClientPosition.X, move.ClientPosition.Y);
|
|
||||||
DB.MouseMoveEventArgs move2 = new DB.MouseMoveEventArgs(position, position - info.MousePosition);
|
DB.MouseMoveEventArgs move2 = new DB.MouseMoveEventArgs(position, position - info.MousePosition);
|
||||||
|
|
||||||
info.MousePosition = position;
|
info.MousePosition = position;
|
||||||
@@ -156,73 +151,64 @@ namespace Dashboard.OpenTK.PAL2
|
|||||||
info.Window.SendEvent(this, move2);
|
info.Window.SendEvent(this, move2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PlatformEventType.Scroll:
|
case ScrollEventArgs mscroll:
|
||||||
{
|
{
|
||||||
ScrollEventArgs scroll = (ScrollEventArgs)args;
|
Vector2 distance = new Vector2(mscroll.Distance.X, mscroll.Distance.Y);
|
||||||
Vector2 distance = new Vector2(scroll.Distance.X, scroll.Distance.Y);
|
Vector2 delta = new Vector2(mscroll.Delta.X, mscroll.Delta.Y);
|
||||||
Vector2 delta = new Vector2(scroll.Delta.X, scroll.Delta.Y);
|
|
||||||
MouseScrollEventArgs scroll2 = new MouseScrollEventArgs(distance, delta);
|
MouseScrollEventArgs scroll2 = new MouseScrollEventArgs(distance, delta);
|
||||||
info.Window.SendEvent(this, scroll2);
|
info.Window.SendEvent(this, scroll2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keyboard & Text Events
|
// Keyboard & Text Events
|
||||||
case PlatformEventType.KeyDown:
|
case KeyDownEventArgs kdown:
|
||||||
{
|
{
|
||||||
KeyDownEventArgs down = (KeyDownEventArgs)args;
|
ModifierKeys modifierKeys = kdown.Modifiers.ToDashboard();
|
||||||
|
KeyCode keyCode = kdown.Key.ToDashboard();
|
||||||
ModifierKeys modifierKeys = GetModifierKeys(down.Modifiers);
|
ScanCode scanCode = kdown.Scancode.ToDashboard();
|
||||||
KeyCode keyCode = GetKeyCode(down.Key);
|
|
||||||
ScanCode scanCode = GetScanCode(down.Scancode);
|
|
||||||
|
|
||||||
KeyboardButtonEventArgs up2 = new KeyboardButtonEventArgs(keyCode, scanCode, modifierKeys, false);
|
KeyboardButtonEventArgs up2 = new KeyboardButtonEventArgs(keyCode, scanCode, modifierKeys, false);
|
||||||
info.Window.SendEvent(this, up2);
|
info.Window.SendEvent(this, up2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PlatformEventType.KeyUp:
|
case KeyUpEventArgs kup:
|
||||||
{
|
{
|
||||||
KeyUpEventArgs up = (KeyUpEventArgs)args;
|
ModifierKeys modifierKeys = kup.Modifiers.ToDashboard();
|
||||||
|
KeyCode keyCode = kup.Key.ToDashboard();
|
||||||
ModifierKeys modifierKeys = GetModifierKeys(up.Modifiers);
|
ScanCode scanCode = kup.Scancode.ToDashboard();
|
||||||
KeyCode keyCode = GetKeyCode(up.Key);
|
|
||||||
ScanCode scanCode = GetScanCode(up.Scancode);
|
|
||||||
|
|
||||||
KeyboardButtonEventArgs up2 = new KeyboardButtonEventArgs(keyCode, scanCode, modifierKeys, true);
|
KeyboardButtonEventArgs up2 = new KeyboardButtonEventArgs(keyCode, scanCode, modifierKeys, true);
|
||||||
info.Window.SendEvent(this, up2);
|
info.Window.SendEvent(this, up2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case PlatformEventType.TextInput:
|
case OPENTK.TextInputEventArgs textInput:
|
||||||
{
|
{
|
||||||
OPENTK.TextInputEventArgs textInput = (OPENTK.TextInputEventArgs)args;
|
|
||||||
DB.TextInputEventArgs textInput2 = new DB.TextInputEventArgs(textInput.Text);
|
DB.TextInputEventArgs textInput2 = new DB.TextInputEventArgs(textInput.Text);
|
||||||
info.Window.SendEvent(this, textInput2);
|
info.Window.SendEvent(this, textInput2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PlatformEventType.TextEditing:
|
case TextEditingEventArgs textEditing:
|
||||||
{
|
{
|
||||||
TextEditingEventArgs textEditing = (TextEditingEventArgs)args;
|
|
||||||
TextEditEventArgs textEditing2 = new TextEditEventArgs(textEditing.Candidate, textEditing.Cursor, textEditing.Length);
|
TextEditEventArgs textEditing2 = new TextEditEventArgs(textEditing.Candidate, textEditing.Cursor, textEditing.Length);
|
||||||
info.Window.SendEvent(this, textEditing2);
|
info.Window.SendEvent(this, textEditing2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Window/Surface related events.
|
// Window/Surface related events.
|
||||||
case PlatformEventType.Close:
|
case CloseEventArgs:
|
||||||
{
|
{
|
||||||
info.Window.SendEvent(this, new WindowCloseEvent());
|
info.Window.SendEvent(this, new WindowCloseEvent());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PlatformEventType.WindowFramebufferResize:
|
case WindowFramebufferResizeEventArgs:
|
||||||
{
|
{
|
||||||
var resize = (WindowFramebufferResizeEventArgs)args;
|
|
||||||
info.Window.SendEvent(this, new ResizeEventArgs());
|
info.Window.SendEvent(this, new ResizeEventArgs());
|
||||||
info.Window.SendEvent(this, new PaintEventArgs(info.Window.DeviceContext));
|
info.Window.SendEvent(this, new PaintEventArgs(info.Window.DeviceContext));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PlatformEventType.WindowResize:
|
case WindowResizeEventArgs:
|
||||||
{
|
{
|
||||||
var resize = (WindowResizeEventArgs)args;
|
|
||||||
info.Window.SendEvent(this, new ResizeEventArgs());
|
info.Window.SendEvent(this, new ResizeEventArgs());
|
||||||
info.Window.SendEvent(this, new PaintEventArgs(info.Window.DeviceContext));
|
info.Window.SendEvent(this, new PaintEventArgs(info.Window.DeviceContext));
|
||||||
break;
|
break;
|
||||||
@@ -234,96 +220,10 @@ namespace Dashboard.OpenTK.PAL2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ModifierKeys GetModifierKeys(KeyModifier modifier)
|
|
||||||
{
|
|
||||||
ModifierKeys keys = 0;
|
|
||||||
|
|
||||||
keys |= modifier.HasFlag(KeyModifier.NumLock) ? ModifierKeys.NumLock : 0;
|
|
||||||
keys |= modifier.HasFlag(KeyModifier.CapsLock) ? ModifierKeys.CapsLock : 0;
|
|
||||||
keys |= modifier.HasFlag(KeyModifier.ScrollLock) ? ModifierKeys.ScrollLock : 0;
|
|
||||||
|
|
||||||
keys |= modifier.HasFlag(KeyModifier.LeftShift) ? ModifierKeys.LeftShift : 0;
|
|
||||||
keys |= modifier.HasFlag(KeyModifier.LeftControl) ? ModifierKeys.LeftControl : 0;
|
|
||||||
keys |= modifier.HasFlag(KeyModifier.LeftAlt) ? ModifierKeys.LeftAlt : 0;
|
|
||||||
keys |= modifier.HasFlag(KeyModifier.LeftGUI) ? ModifierKeys.LeftMeta : 0;
|
|
||||||
|
|
||||||
keys |= modifier.HasFlag(KeyModifier.RightShift) ? ModifierKeys.RightShift : 0;
|
|
||||||
keys |= modifier.HasFlag(KeyModifier.RightControl) ? ModifierKeys.RightControl : 0;
|
|
||||||
keys |= modifier.HasFlag(KeyModifier.RightAlt) ? ModifierKeys.RightAlt : 0;
|
|
||||||
keys |= modifier.HasFlag(KeyModifier.RightGUI) ? ModifierKeys.RightMeta : 0;
|
|
||||||
|
|
||||||
keys |= modifier.HasFlag(KeyModifier.Shift) ? ModifierKeys.Shift : 0;
|
|
||||||
keys |= modifier.HasFlag(KeyModifier.Control) ? ModifierKeys.Control : 0;
|
|
||||||
keys |= modifier.HasFlag(KeyModifier.Alt) ? ModifierKeys.Alt : 0;
|
|
||||||
keys |= modifier.HasFlag(KeyModifier.GUI) ? ModifierKeys.Meta : 0;
|
|
||||||
|
|
||||||
// C# makes this cast as annoying as possible.
|
|
||||||
keys |= (ModifierKeys)((((int)keys >> (int)ModifierKeys.RightBitPos) & 0xF) |
|
|
||||||
(((int)keys >> (int)ModifierKeys.LeftBitPos) & 0xF));
|
|
||||||
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
private record WindowExtraInfo(PhysicalWindow Window)
|
private record WindowExtraInfo(PhysicalWindow Window)
|
||||||
{
|
{
|
||||||
public Vector2 MousePosition { get; set; } = Vector2.Zero;
|
public Vector2 MousePosition { get; set; } = Vector2.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Keycode and scancode tables.
|
|
||||||
|
|
||||||
private static KeyCode GetKeyCode(Key key) => key switch
|
|
||||||
{
|
|
||||||
_ => (KeyCode)0,
|
|
||||||
};
|
|
||||||
|
|
||||||
private static ScanCode GetScanCode(Scancode scanCode) => scanCode switch
|
|
||||||
{
|
|
||||||
// TODO: Revise this array.
|
|
||||||
Scancode.A => ScanCode.A,
|
|
||||||
Scancode.B => ScanCode.B,
|
|
||||||
Scancode.C => ScanCode.C,
|
|
||||||
Scancode.D => ScanCode.D,
|
|
||||||
Scancode.E => ScanCode.E,
|
|
||||||
Scancode.F => ScanCode.F,
|
|
||||||
Scancode.G => ScanCode.G,
|
|
||||||
Scancode.H => ScanCode.H,
|
|
||||||
Scancode.I => ScanCode.I,
|
|
||||||
Scancode.J => ScanCode.J,
|
|
||||||
Scancode.K => ScanCode.K,
|
|
||||||
Scancode.L => ScanCode.L,
|
|
||||||
Scancode.M => ScanCode.M,
|
|
||||||
Scancode.N => ScanCode.N,
|
|
||||||
Scancode.O => ScanCode.O,
|
|
||||||
Scancode.P => ScanCode.P,
|
|
||||||
Scancode.Q => ScanCode.Q,
|
|
||||||
Scancode.R => ScanCode.R,
|
|
||||||
Scancode.S => ScanCode.S,
|
|
||||||
Scancode.T => ScanCode.T,
|
|
||||||
Scancode.U => ScanCode.U,
|
|
||||||
Scancode.V => ScanCode.V,
|
|
||||||
Scancode.W => ScanCode.W,
|
|
||||||
Scancode.X => ScanCode.X,
|
|
||||||
Scancode.Y => ScanCode.Y,
|
|
||||||
Scancode.Z => ScanCode.Z,
|
|
||||||
Scancode.D0 => ScanCode.D0,
|
|
||||||
Scancode.D1 => ScanCode.D1,
|
|
||||||
Scancode.D2 => ScanCode.D2,
|
|
||||||
Scancode.D3 => ScanCode.D3,
|
|
||||||
Scancode.D4 => ScanCode.D4,
|
|
||||||
Scancode.D5 => ScanCode.D5,
|
|
||||||
Scancode.D6 => ScanCode.D6,
|
|
||||||
Scancode.D7 => ScanCode.D7,
|
|
||||||
Scancode.D8 => ScanCode.D8,
|
|
||||||
Scancode.D9 => ScanCode.D9,
|
|
||||||
Scancode.UpArrow => ScanCode.UpArrow,
|
|
||||||
Scancode.DownArrow => ScanCode.DownArrow,
|
|
||||||
Scancode.LeftArrow => ScanCode.LeftArrow,
|
|
||||||
Scancode.RightArrow => ScanCode.RightArrow,
|
|
||||||
Scancode.Spacebar => ScanCode.Space,
|
|
||||||
Scancode.LeftShift => ScanCode.LShift,
|
|
||||||
Scancode.RightShift => ScanCode.RShift,
|
|
||||||
_ => (ScanCode)0,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class ApplicationQuitEventArgs() : EventArgs
|
internal class ApplicationQuitEventArgs() : EventArgs
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ namespace Dashboard.Controls
|
|||||||
dcb.ResetScissor();
|
dcb.ResetScissor();
|
||||||
dcb.ResetTransforms();
|
dcb.ResetTransforms();
|
||||||
|
|
||||||
|
dcb.ClearDepth();
|
||||||
|
|
||||||
if (Background is SolidColorBrush solidColorBrush)
|
if (Background is SolidColorBrush solidColorBrush)
|
||||||
dcb.ClearColor(solidColorBrush.Color);
|
dcb.ClearColor(solidColorBrush.Color);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -23,7 +24,7 @@ Application app = new Pal2Application(new ToolkitOptions()
|
|||||||
{
|
{
|
||||||
GraphicsApiHints = new OpenGLGraphicsApiHints()
|
GraphicsApiHints = new OpenGLGraphicsApiHints()
|
||||||
{
|
{
|
||||||
Version = new Version(3, 2),
|
Version = new Version(4, 1),
|
||||||
ForwardCompatibleFlag = true,
|
ForwardCompatibleFlag = true,
|
||||||
DebugFlag = true,
|
DebugFlag = true,
|
||||||
Profile = OpenGLProfile.Core,
|
Profile = OpenGLProfile.Core,
|
||||||
@@ -71,6 +72,64 @@ 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);
|
||||||
|
|
||||||
|
IShader shader = direct.CreatePipeline(new GlslShaderCreateInfo()
|
||||||
|
{
|
||||||
|
VertexShader =
|
||||||
|
"""
|
||||||
|
#version 410
|
||||||
|
layout (location = 0) in vec3 apos;
|
||||||
|
layout (location = 1) in vec4 acolor;
|
||||||
|
|
||||||
|
out vec4 vcolor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = vec4(apos, 1);
|
||||||
|
vcolor = acolor;
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
FragmentShader =
|
||||||
|
"""
|
||||||
|
#version 410
|
||||||
|
in vec4 vcolor;
|
||||||
|
out vec4 fcolor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
fcolor = vcolor;
|
||||||
|
}
|
||||||
|
|
||||||
|
""",
|
||||||
|
});
|
||||||
|
|
||||||
|
DrawCall call = new DrawCall(MeshPrimitive.Triangle, vertex, 0, 3)
|
||||||
|
{
|
||||||
|
ShaderPipeline = shader,
|
||||||
|
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