Remove archaic code from the working tree.
This commit is contained in:
@@ -1,48 +0,0 @@
|
|||||||
using System.Numerics;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing.OpenGL
|
|
||||||
{
|
|
||||||
public enum SimpleDrawCommand : int
|
|
||||||
{
|
|
||||||
Point = 1,
|
|
||||||
Line = 2,
|
|
||||||
Rect = 3,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Make sure your custom commands have values higher than this if you plan on using the default command
|
|
||||||
/// buffer.
|
|
||||||
/// </summary>
|
|
||||||
CustomCommandStart = 4096
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 64)]
|
|
||||||
public struct CommandInfo
|
|
||||||
{
|
|
||||||
[FieldOffset(0)]
|
|
||||||
public SimpleDrawCommand Type;
|
|
||||||
|
|
||||||
[FieldOffset(4)]
|
|
||||||
public int Flags;
|
|
||||||
|
|
||||||
[FieldOffset(8)]
|
|
||||||
public float Arg0;
|
|
||||||
[FieldOffset(12)]
|
|
||||||
public float Arg1;
|
|
||||||
|
|
||||||
[FieldOffset(16)]
|
|
||||||
public int FgGradientIndex;
|
|
||||||
[FieldOffset(20)]
|
|
||||||
public int FgGradientCount;
|
|
||||||
[FieldOffset(24)]
|
|
||||||
public int BgGradientIndex;
|
|
||||||
[FieldOffset(28)]
|
|
||||||
public int BgGradientCount;
|
|
||||||
|
|
||||||
[FieldOffset(32)]
|
|
||||||
public Vector4 FgColor;
|
|
||||||
|
|
||||||
[FieldOffset(48)]
|
|
||||||
public Vector4 BgColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
using System.Drawing;
|
|
||||||
using Dashboard.Drawing.OpenGL.Executors;
|
|
||||||
using Dashboard.OpenGL;
|
|
||||||
using OpenTK.Mathematics;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing.OpenGL
|
|
||||||
{
|
|
||||||
public interface ICommandExecutor
|
|
||||||
{
|
|
||||||
IEnumerable<string> Extensions { get; }
|
|
||||||
IContextExecutor Executor { get; }
|
|
||||||
|
|
||||||
void SetContextExecutor(IContextExecutor executor);
|
|
||||||
|
|
||||||
void BeginFrame();
|
|
||||||
|
|
||||||
void BeginDraw();
|
|
||||||
|
|
||||||
void EndDraw();
|
|
||||||
|
|
||||||
void EndFrame();
|
|
||||||
|
|
||||||
void ProcessCommand(ICommandFrame frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IContextExecutor : IInitializer, IGLDisposable
|
|
||||||
{
|
|
||||||
GLEngine Engine { get; }
|
|
||||||
IGLContext Context { get; }
|
|
||||||
ContextResourcePool ResourcePool { get; }
|
|
||||||
TransformStack TransformStack { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ContextExecutor : IContextExecutor
|
|
||||||
{
|
|
||||||
public GLEngine Engine { get; }
|
|
||||||
public IGLContext Context { get; }
|
|
||||||
public ContextResourcePool ResourcePool { get; }
|
|
||||||
public TransformStack TransformStack { get; } = new TransformStack();
|
|
||||||
protected bool IsDisposed { get; private set; } = false;
|
|
||||||
public bool IsInitialized { get; private set; } = false;
|
|
||||||
|
|
||||||
private readonly List<ICommandExecutor> _executorsList = new List<ICommandExecutor>();
|
|
||||||
|
|
||||||
private readonly Dictionary<string, ICommandExecutor> _executorsMap = new Dictionary<string, ICommandExecutor>();
|
|
||||||
|
|
||||||
public ContextExecutor(GLEngine engine, IGLContext context)
|
|
||||||
{
|
|
||||||
Engine = engine;
|
|
||||||
Context = context;
|
|
||||||
|
|
||||||
ResourcePool = Engine.ResourcePoolManager.Get(context);
|
|
||||||
ResourcePool.IncrementReference();
|
|
||||||
|
|
||||||
AddExecutor(new BaseCommandExecutor());
|
|
||||||
AddExecutor(new TextCommandExecutor());
|
|
||||||
}
|
|
||||||
|
|
||||||
~ContextExecutor()
|
|
||||||
{
|
|
||||||
DisposeInvoker(true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddExecutor(ICommandExecutor executor, bool overwrite = false)
|
|
||||||
{
|
|
||||||
if (IsInitialized)
|
|
||||||
throw new Exception("This context executor is already initialized. Cannot add new command executors.");
|
|
||||||
|
|
||||||
IInitializer? initializer = executor as IInitializer;
|
|
||||||
|
|
||||||
if (initializer?.IsInitialized == true)
|
|
||||||
throw new InvalidOperationException("This command executor has already been initialized, cannot add here.");
|
|
||||||
|
|
||||||
if (!overwrite)
|
|
||||||
{
|
|
||||||
foreach (string extension in executor.Extensions)
|
|
||||||
{
|
|
||||||
if (_executorsMap.ContainsKey(extension))
|
|
||||||
throw new InvalidOperationException("An executor already handles this extension.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string extension in executor.Extensions)
|
|
||||||
{
|
|
||||||
_executorsMap[extension] = executor;
|
|
||||||
}
|
|
||||||
_executorsList.Add(executor);
|
|
||||||
|
|
||||||
executor.SetContextExecutor(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
if (IsInitialized)
|
|
||||||
return;
|
|
||||||
IsInitialized = true;
|
|
||||||
|
|
||||||
foreach (ICommandExecutor executor in _executorsList)
|
|
||||||
{
|
|
||||||
if (executor is IInitializer initializer)
|
|
||||||
initializer.Initialize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void BeginFrame()
|
|
||||||
{
|
|
||||||
foreach (ICommandExecutor executor in _executorsList)
|
|
||||||
executor.BeginFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void BeginDraw()
|
|
||||||
{
|
|
||||||
foreach (ICommandExecutor executor in _executorsList)
|
|
||||||
executor.BeginDraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void EndDraw()
|
|
||||||
{
|
|
||||||
foreach (ICommandExecutor executor in _executorsList)
|
|
||||||
executor.EndDraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void EndFrame()
|
|
||||||
{
|
|
||||||
ResourcePool.Collector.Dispose();
|
|
||||||
TransformStack.Clear();
|
|
||||||
|
|
||||||
foreach (ICommandExecutor executor in _executorsList)
|
|
||||||
executor.EndFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw(DrawQueue drawqueue) => Draw(drawqueue, new RectangleF(new PointF(0f,0f), Context.FramebufferSize));
|
|
||||||
|
|
||||||
public virtual void Draw(DrawQueue drawQueue, RectangleF bounds, float scale = 1.0f)
|
|
||||||
{
|
|
||||||
BeginDraw();
|
|
||||||
|
|
||||||
if (scale != 1.0f)
|
|
||||||
TransformStack.Push(Matrix4.CreateScale(scale, scale, 1));
|
|
||||||
|
|
||||||
foreach (ICommandFrame frame in drawQueue)
|
|
||||||
{
|
|
||||||
if (_executorsMap.TryGetValue(frame.Command.Extension.Name, out ICommandExecutor? executor))
|
|
||||||
executor.ProcessCommand(frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
EndDraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DisposeInvoker(bool safeExit, bool disposing)
|
|
||||||
{
|
|
||||||
if (!IsDisposed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
IsDisposed = true;
|
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
|
|
||||||
Dispose(safeExit, disposing);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void Dispose(bool safeExit, bool disposing)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
foreach (ICommandExecutor executor in _executorsList)
|
|
||||||
{
|
|
||||||
if (executor is IGLDisposable glDisposable)
|
|
||||||
glDisposable.Dispose(safeExit);
|
|
||||||
else if (executor is IDisposable disposable)
|
|
||||||
disposable.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ResourcePool.DecrementReference())
|
|
||||||
Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() => DisposeInvoker(true, true);
|
|
||||||
|
|
||||||
public void Dispose(bool safeExit) => DisposeInvoker(safeExit, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
using Dashboard.OpenGL;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing.OpenGL
|
|
||||||
{
|
|
||||||
public class ContextResourcePoolManager
|
|
||||||
{
|
|
||||||
private readonly Dictionary<IGLContext, ContextResourcePool> _unique = new Dictionary<IGLContext, ContextResourcePool>();
|
|
||||||
private readonly Dictionary<int, ContextResourcePool> _shared = new Dictionary<int, ContextResourcePool>();
|
|
||||||
|
|
||||||
public ContextResourcePool Get(IGLContext context)
|
|
||||||
{
|
|
||||||
if (context.ContextGroup == -1)
|
|
||||||
{
|
|
||||||
if (!_unique.TryGetValue(context, out ContextResourcePool? pool))
|
|
||||||
{
|
|
||||||
pool = new ContextResourcePool(this, context);
|
|
||||||
_unique.Add(context, pool);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pool;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!_shared.TryGetValue(context.ContextGroup, out ContextResourcePool? pool))
|
|
||||||
{
|
|
||||||
pool = new ContextResourcePool(this, context.ContextGroup);
|
|
||||||
_shared.Add(context.ContextGroup, pool);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pool;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Disposed(ContextResourcePool pool)
|
|
||||||
{
|
|
||||||
// TODO:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ContextResourcePool : IGLDisposable, IArc
|
|
||||||
{
|
|
||||||
private int _references = 0;
|
|
||||||
private bool _isDisposed = false;
|
|
||||||
private readonly Dictionary<int, IResourceManager> _managers = new Dictionary<int, IResourceManager>();
|
|
||||||
|
|
||||||
public ContextResourcePoolManager Manager { get; }
|
|
||||||
public IGLContext? Context { get; private set; } = null;
|
|
||||||
public int ContextGroup { get; private set; } = -1;
|
|
||||||
public int References => _references;
|
|
||||||
public ContextCollector Collector { get; } = new ContextCollector();
|
|
||||||
|
|
||||||
internal ContextResourcePool(ContextResourcePoolManager manager, int contextGroup)
|
|
||||||
{
|
|
||||||
Manager = manager;
|
|
||||||
ContextGroup = contextGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal ContextResourcePool(ContextResourcePoolManager manager, IGLContext context)
|
|
||||||
{
|
|
||||||
Manager = manager;
|
|
||||||
Context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T GetResourceManager<T>(bool init = true) where T : IResourceManager, new()
|
|
||||||
{
|
|
||||||
int index = ManagerAtom<T>.Atom;
|
|
||||||
|
|
||||||
if (!_managers.TryGetValue(index, out IResourceManager? resourceClass))
|
|
||||||
{
|
|
||||||
_managers[index] = resourceClass = new T();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (init && resourceClass is IInitializer initializer)
|
|
||||||
{
|
|
||||||
initializer.Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (T)resourceClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
~ContextResourcePool()
|
|
||||||
{
|
|
||||||
Dispose(true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() => Dispose(true, false);
|
|
||||||
|
|
||||||
public void Dispose(bool safeExit) => Dispose(safeExit, true);
|
|
||||||
|
|
||||||
private void Dispose(bool safeExit, bool disposing)
|
|
||||||
{
|
|
||||||
if (_isDisposed)
|
|
||||||
return;
|
|
||||||
_isDisposed = true;
|
|
||||||
|
|
||||||
Manager.Disposed(this);
|
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
foreach ((int _, IResourceManager manager) in _managers)
|
|
||||||
{
|
|
||||||
if (manager is IGLDisposable glDisposable)
|
|
||||||
glDisposable.Dispose(safeExit);
|
|
||||||
else if (manager is IDisposable disposable)
|
|
||||||
disposable.Dispose();
|
|
||||||
}
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void IncrementReference()
|
|
||||||
{
|
|
||||||
Interlocked.Increment(ref _references);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool DecrementReference()
|
|
||||||
{
|
|
||||||
return Interlocked.Decrement(ref _references) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private class ManagerAtom
|
|
||||||
{
|
|
||||||
private static int _counter = -1;
|
|
||||||
|
|
||||||
protected static int Acquire() => Interlocked.Increment(ref _counter);
|
|
||||||
}
|
|
||||||
private class ManagerAtom<T> : ManagerAtom where T : IResourceManager
|
|
||||||
{
|
|
||||||
public static int Atom { get; } = Acquire();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="BlurgText" Version="0.1.0-nightly-19" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Dashboard.Drawing\Dashboard.Drawing.csproj" />
|
|
||||||
<ProjectReference Include="..\Dashboard.OpenGL\Dashboard.OpenGL.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<EmbeddedResource Include="Executors\simple.frag" />
|
|
||||||
<EmbeddedResource Include="Executors\simple.vert" />
|
|
||||||
<EmbeddedResource Include="Executors\text.vert" />
|
|
||||||
<EmbeddedResource Include="Executors\text.frag" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Text\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,468 +0,0 @@
|
|||||||
using System.Diagnostics.Contracts;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Dashboard.OpenGL;
|
|
||||||
using OpenTK.Graphics.OpenGL;
|
|
||||||
using OpenTK.Mathematics;
|
|
||||||
using Vector2 = System.Numerics.Vector2;
|
|
||||||
using Vector3 = System.Numerics.Vector3;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing.OpenGL
|
|
||||||
{
|
|
||||||
public class DrawCallRecorder : IGLDisposable, IInitializer
|
|
||||||
{
|
|
||||||
private int _vao = 0;
|
|
||||||
private int _vbo = 0;
|
|
||||||
private readonly List<DrawVertex> _vertices = new List<DrawVertex>();
|
|
||||||
private readonly List<DrawCall> _calls = new List<DrawCall>();
|
|
||||||
|
|
||||||
private int _start = 0;
|
|
||||||
private int _count = 0;
|
|
||||||
private int _primitives = 0;
|
|
||||||
private Vector3 _charCoords;
|
|
||||||
private int _cmdIndex;
|
|
||||||
private int _texture0, _texture1, _texture2, _texture3;
|
|
||||||
private TextureTarget _target0, _target1, _target2, _target3;
|
|
||||||
private Matrix4 _transforms = Matrix4.Identity;
|
|
||||||
|
|
||||||
public int CommandModulus = 64;
|
|
||||||
public int CommandBuffer = 0;
|
|
||||||
public int CommandSize = 64;
|
|
||||||
|
|
||||||
private int CommandByteSize => CommandModulus * CommandSize;
|
|
||||||
|
|
||||||
public int TransformsLocation { get; set; }
|
|
||||||
|
|
||||||
public void Transforms(in Matrix4 transforms)
|
|
||||||
{
|
|
||||||
_transforms = transforms;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Begin(PrimitiveType type)
|
|
||||||
{
|
|
||||||
if (_primitives != 0)
|
|
||||||
throw new InvalidOperationException("Attempt to begin new draw call before finishing previous one.");
|
|
||||||
|
|
||||||
_primitives = (int)type;
|
|
||||||
_start = _vertices.Count;
|
|
||||||
_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void TexCoords2(Vector2 texCoords)
|
|
||||||
{
|
|
||||||
_charCoords = new Vector3(texCoords, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CharCoords(Vector3 charCoords)
|
|
||||||
{
|
|
||||||
_charCoords = charCoords;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CommandIndex(int index)
|
|
||||||
{
|
|
||||||
_cmdIndex = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Vertex3(Vector3 vertex)
|
|
||||||
{
|
|
||||||
_vertices.Add(new DrawVertex()
|
|
||||||
{
|
|
||||||
Position = vertex,
|
|
||||||
CharCoords = _charCoords,
|
|
||||||
CmdIndex = _cmdIndex % CommandModulus,
|
|
||||||
});
|
|
||||||
_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void End()
|
|
||||||
{
|
|
||||||
if (_primitives == 0)
|
|
||||||
throw new InvalidOperationException("Attempt to end draw call before starting one.");
|
|
||||||
|
|
||||||
_calls.Add(
|
|
||||||
new DrawCall()
|
|
||||||
{
|
|
||||||
Type = (PrimitiveType)_primitives,
|
|
||||||
Start = _start,
|
|
||||||
Count = _count,
|
|
||||||
CmdIndex = _cmdIndex,
|
|
||||||
Target0 = _target0,
|
|
||||||
Target1 = _target1,
|
|
||||||
Target2 = _target2,
|
|
||||||
Target3 = _target3,
|
|
||||||
Texture0 = _texture0,
|
|
||||||
Texture1 = _texture1,
|
|
||||||
Texture2 = _texture2,
|
|
||||||
Texture3 = _texture3,
|
|
||||||
Transforms = _transforms,
|
|
||||||
});
|
|
||||||
|
|
||||||
_primitives = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BindTexture(TextureTarget target, int texture) => BindTexture(target, 0, texture);
|
|
||||||
|
|
||||||
public void BindTexture(TextureTarget target, int unit, int texture)
|
|
||||||
{
|
|
||||||
switch (unit)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
_texture0 = 0;
|
|
||||||
_target0 = target;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
_texture1 = 0;
|
|
||||||
_target1 = target;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
_texture2 = 0;
|
|
||||||
_target2 = target;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
_texture3 = 0;
|
|
||||||
_target3 = target;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(unit), "I did not write support for more than 4 textures.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DrawArrays(PrimitiveType type, int first, int count)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Execute()
|
|
||||||
{
|
|
||||||
GL.BindVertexArray(_vao);
|
|
||||||
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo);
|
|
||||||
|
|
||||||
ReadOnlySpan<DrawVertex> vertices = CollectionsMarshal.AsSpan(_vertices);
|
|
||||||
GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Count * Unsafe.SizeOf<DrawVertex>(), vertices, BufferUsage.DynamicDraw);
|
|
||||||
|
|
||||||
foreach (DrawCall call in _calls)
|
|
||||||
{
|
|
||||||
GL.BindBufferRange(BufferTarget.UniformBuffer, 0, CommandBuffer, call.CmdIndex / CommandModulus * CommandByteSize, CommandByteSize);
|
|
||||||
GL.ActiveTexture(TextureUnit.Texture0);
|
|
||||||
GL.BindTexture(call.Target0, call.Texture0);
|
|
||||||
GL.ActiveTexture(TextureUnit.Texture1);
|
|
||||||
GL.BindTexture(call.Target1, call.Texture1);
|
|
||||||
GL.ActiveTexture(TextureUnit.Texture2);
|
|
||||||
GL.BindTexture(call.Target2, call.Texture2);
|
|
||||||
GL.ActiveTexture(TextureUnit.Texture3);
|
|
||||||
GL.BindTexture(call.Target3, call.Texture3);
|
|
||||||
|
|
||||||
Matrix4 transforms = call.Transforms;
|
|
||||||
GL.UniformMatrix4f(TransformsLocation, 1, true, ref transforms);
|
|
||||||
GL.DrawArrays(call.Type, call.Start, call.Count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
_vertices.Clear();
|
|
||||||
_calls.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose(bool safeExit)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsInitialized { get; private set; }
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
if (IsInitialized)
|
|
||||||
return;
|
|
||||||
IsInitialized = true;
|
|
||||||
|
|
||||||
_vao = GL.CreateVertexArray();
|
|
||||||
_vbo = GL.CreateBuffer();
|
|
||||||
|
|
||||||
GL.BindVertexArray(_vao);
|
|
||||||
GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo);
|
|
||||||
|
|
||||||
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 32, 0);
|
|
||||||
GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, 32, 16);
|
|
||||||
GL.VertexAttribIPointer(2, 1, VertexAttribIType.Int, 32, 28);
|
|
||||||
GL.EnableVertexAttribArray(0);
|
|
||||||
GL.EnableVertexAttribArray(1);
|
|
||||||
GL.EnableVertexAttribArray(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct DrawCall
|
|
||||||
{
|
|
||||||
public PrimitiveType Type;
|
|
||||||
public int Start;
|
|
||||||
public int Count;
|
|
||||||
public int CmdIndex;
|
|
||||||
|
|
||||||
public int Texture0;
|
|
||||||
public int Texture1;
|
|
||||||
public int Texture2;
|
|
||||||
public int Texture3;
|
|
||||||
|
|
||||||
public TextureTarget Target0;
|
|
||||||
public TextureTarget Target1;
|
|
||||||
public TextureTarget Target2;
|
|
||||||
public TextureTarget Target3;
|
|
||||||
|
|
||||||
public Matrix4 Transforms;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 32)]
|
|
||||||
private struct DrawVertex
|
|
||||||
{
|
|
||||||
[FieldOffset(0)]
|
|
||||||
public Vector3 Position;
|
|
||||||
[FieldOffset(16)]
|
|
||||||
public Vector3 CharCoords;
|
|
||||||
[FieldOffset(28)]
|
|
||||||
public int CmdIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A customizable immediate mode draw call queue, for the modern OpenGL user.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TCall">The call info type.</typeparam>
|
|
||||||
/// <typeparam name="TVertex">The vertex structure.</typeparam>
|
|
||||||
public abstract class DrawCallRecorder<TCall, TVertex> : IGLDisposable, IInitializer
|
|
||||||
where TVertex : unmanaged
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The vertex array for this queue.
|
|
||||||
/// </summary>
|
|
||||||
public int Vao { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The vertex buffer for this queue.
|
|
||||||
/// </summary>
|
|
||||||
public int Vbo { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Number of calls recorded in this queue.
|
|
||||||
/// </summary>
|
|
||||||
public int CallCount => Calls.Count;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The number of total vertices recorded.
|
|
||||||
/// </summary>
|
|
||||||
public int TotalVertices => Vertices.Count;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The latest draw call info.
|
|
||||||
/// </summary>
|
|
||||||
public ref TCall CurrentCall => ref _currentCall;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The latest vertex emitted.
|
|
||||||
/// </summary>
|
|
||||||
public ref TVertex CurrentVertex => ref _currentVertex;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// True if currently recording a draw call.
|
|
||||||
/// </summary>
|
|
||||||
public bool InCall => _primitiveMode != 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Size of one vertex.
|
|
||||||
/// </summary>
|
|
||||||
protected int VertexSize => Unsafe.SizeOf<TVertex>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The list of draw calls.
|
|
||||||
/// </summary>
|
|
||||||
protected List<DrawCall> Calls { get; } = new List<DrawCall>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The list of all vertices.
|
|
||||||
/// </summary>
|
|
||||||
protected List<TVertex> Vertices { get; } = new List<TVertex>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The value to write for the draw call info at the start of a call.
|
|
||||||
/// </summary>
|
|
||||||
[Pure] protected virtual TCall DefaultCall => default(TCall);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The value to write for last vertex at the start of a call.
|
|
||||||
/// </summary>
|
|
||||||
[Pure] protected virtual TVertex DefaultVertex => default;
|
|
||||||
|
|
||||||
private int _start = 0;
|
|
||||||
private int _count = 0;
|
|
||||||
private int _primitiveMode = 0;
|
|
||||||
private TCall _currentCall;
|
|
||||||
private TVertex _currentVertex;
|
|
||||||
|
|
||||||
protected DrawCallRecorder()
|
|
||||||
{
|
|
||||||
_currentCall = DefaultCall;
|
|
||||||
_currentVertex = DefaultVertex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Record a draw call directly.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">The primitive type to use.</param>
|
|
||||||
/// <param name="callInfo">The call info structure to use</param>
|
|
||||||
/// <param name="vertices">The list of vertices to use.</param>
|
|
||||||
/// <exception cref="InvalidOperationException">You attempted to use this function during another draw call.</exception>
|
|
||||||
public void DrawArrays(PrimitiveType type, in TCall callInfo, ReadOnlySpan<TVertex> vertices)
|
|
||||||
{
|
|
||||||
if (InCall)
|
|
||||||
throw new InvalidOperationException("Cannot use draw arrays in the middle of an ongoing immediate-mode call.");
|
|
||||||
|
|
||||||
DrawCall call = new DrawCall()
|
|
||||||
{
|
|
||||||
Type = type,
|
|
||||||
Start = Vertices.Count,
|
|
||||||
Count = vertices.Length,
|
|
||||||
CallInfo = callInfo,
|
|
||||||
};
|
|
||||||
|
|
||||||
Vertices.AddRange(vertices);
|
|
||||||
Calls.Add(call);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Start a draw call.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">The primitive type for the call.</param>
|
|
||||||
public void Begin(PrimitiveType type) => Begin(type, DefaultCall);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Start a draw call.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">The primitive type for the call.</param>
|
|
||||||
/// <param name="callInfo">The call info.</param>
|
|
||||||
/// <exception cref="InvalidOperationException">You attempted to create a draw call within a draw call.</exception>
|
|
||||||
public void Begin(PrimitiveType type, TCall callInfo)
|
|
||||||
{
|
|
||||||
if (InCall)
|
|
||||||
throw new InvalidOperationException("Attempt to begin new draw call before finishing previous one.");
|
|
||||||
|
|
||||||
_primitiveMode = (int)type;
|
|
||||||
_start = Vertices.Count;
|
|
||||||
_count = 0;
|
|
||||||
CurrentCall = callInfo;
|
|
||||||
CurrentVertex = DefaultVertex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Emit the latest or modified vertex.
|
|
||||||
/// </summary>
|
|
||||||
public void Vertex()
|
|
||||||
{
|
|
||||||
Vertex(CurrentVertex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Emit a vertex.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="vertex">The vertex to emit.</param>
|
|
||||||
public void Vertex(in TVertex vertex)
|
|
||||||
{
|
|
||||||
Vertices.Add(CurrentVertex = vertex);
|
|
||||||
_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// End the current call.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="InvalidOperationException">You tried to end a call that you didn't begin recording.</exception>
|
|
||||||
public void End()
|
|
||||||
{
|
|
||||||
if (!InCall)
|
|
||||||
throw new InvalidOperationException("Attempt to end draw call before starting one.");
|
|
||||||
|
|
||||||
Calls.Add(new DrawCall()
|
|
||||||
{
|
|
||||||
Start = _start,
|
|
||||||
Count = _count,
|
|
||||||
Type = (PrimitiveType)_primitiveMode,
|
|
||||||
CallInfo = CurrentCall,
|
|
||||||
});
|
|
||||||
|
|
||||||
_primitiveMode = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called by the execution engine before a draw call is executed.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="call">The call to prepare.</param>
|
|
||||||
protected abstract void PrepareCall(in TCall call);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set the vertex format for the <see cref="Vao"/> and <see cref="Vbo"/> created by the recorder.
|
|
||||||
/// </summary>
|
|
||||||
protected abstract void SetVertexFormat();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Execute all the recorded draw calls.
|
|
||||||
/// </summary>
|
|
||||||
public void Execute()
|
|
||||||
{
|
|
||||||
GL.BindVertexArray(Vao);
|
|
||||||
GL.BindBuffer(BufferTarget.ArrayBuffer, Vbo);
|
|
||||||
|
|
||||||
ReadOnlySpan<TVertex> vertices = CollectionsMarshal.AsSpan(Vertices);
|
|
||||||
GL.BufferData(BufferTarget.ArrayBuffer, Vertices.Count * VertexSize, vertices, BufferUsage.DynamicDraw);
|
|
||||||
|
|
||||||
foreach (DrawCall call in Calls)
|
|
||||||
{
|
|
||||||
PrepareCall(call.CallInfo);
|
|
||||||
GL.DrawArrays(call.Type, call.Start, call.Count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clear the draw call queue.
|
|
||||||
/// </summary>
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
Vertices.Clear();
|
|
||||||
Calls.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose(bool safeExit)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsInitialized { get; private set; }
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
if (IsInitialized)
|
|
||||||
return;
|
|
||||||
IsInitialized = true;
|
|
||||||
|
|
||||||
Vao = GL.CreateVertexArray();
|
|
||||||
Vbo = GL.CreateBuffer();
|
|
||||||
|
|
||||||
GL.BindVertexArray(Vao);
|
|
||||||
GL.BindBuffer(BufferTarget.ArrayBuffer, Vbo);
|
|
||||||
|
|
||||||
SetVertexFormat();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected struct DrawCall
|
|
||||||
{
|
|
||||||
public PrimitiveType Type;
|
|
||||||
public int Start;
|
|
||||||
public int Count;
|
|
||||||
public TCall CallInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,333 +0,0 @@
|
|||||||
using System.Drawing;
|
|
||||||
using OpenTK.Graphics.OpenGL;
|
|
||||||
using System.Numerics;
|
|
||||||
using Dashboard.OpenGL;
|
|
||||||
using OTK = OpenTK.Mathematics;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing.OpenGL.Executors
|
|
||||||
{
|
|
||||||
public class BaseCommandExecutor : IInitializer, ICommandExecutor
|
|
||||||
{
|
|
||||||
private int _program = 0;
|
|
||||||
private readonly MappableBumpAllocator<CommandInfo> _commands = new MappableBumpAllocator<CommandInfo>();
|
|
||||||
private readonly DrawCallRecorder _calls = new DrawCallRecorder();
|
|
||||||
|
|
||||||
public bool IsInitialized { get; private set; }
|
|
||||||
public IEnumerable<string> Extensions { get; } = new[] { "DB_base" };
|
|
||||||
public IContextExecutor Executor { get; private set; } = null!;
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
if (IsInitialized) return;
|
|
||||||
|
|
||||||
if (Executor == null)
|
|
||||||
throw new Exception("Executor has not been set.");
|
|
||||||
|
|
||||||
IsInitialized = true;
|
|
||||||
|
|
||||||
LoadShaders();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetContextExecutor(IContextExecutor executor)
|
|
||||||
{
|
|
||||||
Executor = executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BeginFrame()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BeginDraw()
|
|
||||||
{
|
|
||||||
_commands.Initialize();
|
|
||||||
_calls.Initialize();
|
|
||||||
|
|
||||||
Size size = Executor.Context.FramebufferSize;
|
|
||||||
|
|
||||||
Executor.TransformStack.Push(OTK.Matrix4.CreateOrthographicOffCenter(
|
|
||||||
0,
|
|
||||||
size.Width,
|
|
||||||
size.Height,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
-1));
|
|
||||||
|
|
||||||
GL.Viewport(0, 0, size.Width, size.Height);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EndDraw()
|
|
||||||
{
|
|
||||||
_commands.Unmap();
|
|
||||||
GL.UseProgram(_program);
|
|
||||||
_calls.CommandBuffer = _commands.Handle;
|
|
||||||
_calls.Execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EndFrame()
|
|
||||||
{
|
|
||||||
_commands.Clear();
|
|
||||||
_calls.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ProcessCommand(ICommandFrame frame)
|
|
||||||
{
|
|
||||||
switch (frame.Command.Name)
|
|
||||||
{
|
|
||||||
case "Point":
|
|
||||||
DrawBasePoint(frame);
|
|
||||||
break;
|
|
||||||
case "Line":
|
|
||||||
DrawBaseLine(frame);
|
|
||||||
break;
|
|
||||||
case "RectF":
|
|
||||||
case "RectS":
|
|
||||||
case "RectFS":
|
|
||||||
DrawRect(frame);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawBasePoint(ICommandFrame frame)
|
|
||||||
{
|
|
||||||
ref CommandInfo info = ref _commands.Take(out int index);
|
|
||||||
|
|
||||||
PointCommandArgs args = frame.GetParameter<PointCommandArgs>();
|
|
||||||
|
|
||||||
info = new CommandInfo()
|
|
||||||
{
|
|
||||||
Type = SimpleDrawCommand.Point,
|
|
||||||
Arg0 = args.Size,
|
|
||||||
};
|
|
||||||
|
|
||||||
SetCommandCommonBrush(ref info, args.Brush, args.Brush);
|
|
||||||
|
|
||||||
_calls.Transforms(Executor.TransformStack.Top);
|
|
||||||
_calls.Begin(PrimitiveType.Triangles);
|
|
||||||
_calls.CommandIndex(index);
|
|
||||||
DrawPoint(args.Position, args.Depth, args.Size);
|
|
||||||
_calls.End();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawPoint(Vector2 position, float depth, float diameter)
|
|
||||||
{
|
|
||||||
// Draw a point as a isocles triangle.
|
|
||||||
const float adjust = 1.1f;
|
|
||||||
const float cos30 = 0.8660254038f;
|
|
||||||
Vector2 top = adjust * new Vector2(0, -cos30);
|
|
||||||
Vector2 left = adjust * new Vector2(-cos30, 0.5f);
|
|
||||||
Vector2 right = adjust * new Vector2(cos30, 0.5f);
|
|
||||||
|
|
||||||
_calls.TexCoords2(top);
|
|
||||||
_calls.Vertex3(new Vector3(position + top * diameter, depth));
|
|
||||||
_calls.TexCoords2(left);
|
|
||||||
_calls.Vertex3(new Vector3(position + left * diameter, depth));
|
|
||||||
_calls.TexCoords2(right);
|
|
||||||
_calls.Vertex3(new Vector3(position + right * diameter, depth));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawBaseLine(ICommandFrame frame)
|
|
||||||
{
|
|
||||||
ref CommandInfo info = ref _commands.Take(out int index);
|
|
||||||
|
|
||||||
LineCommandArgs args = frame.GetParameter<LineCommandArgs>();
|
|
||||||
|
|
||||||
info = new CommandInfo()
|
|
||||||
{
|
|
||||||
Type = SimpleDrawCommand.Line,
|
|
||||||
Arg0 = 0.5f * args.Size / (args.End - args.Start).Length(),
|
|
||||||
};
|
|
||||||
|
|
||||||
SetCommandCommonBrush(ref info, args.Brush, args.Brush);
|
|
||||||
|
|
||||||
_calls.Transforms(Executor.TransformStack.Top);
|
|
||||||
_calls.Begin(PrimitiveType.Triangles);
|
|
||||||
|
|
||||||
_calls.CommandIndex(index);
|
|
||||||
|
|
||||||
DrawLine(args.Start, args.End, args.Depth, args.Size);
|
|
||||||
|
|
||||||
_calls.End();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawLine(Vector2 start, Vector2 end, float depth, float width)
|
|
||||||
{
|
|
||||||
float radius = 0.5f * width;
|
|
||||||
Vector2 segment = end - start;
|
|
||||||
float length = segment.Length();
|
|
||||||
float ratio = radius / length;
|
|
||||||
Vector2 n = ratio * segment;
|
|
||||||
Vector2 t = new Vector2(-n.Y, n.X);
|
|
||||||
|
|
||||||
Vector2 t00 = new Vector2(-ratio, -ratio);
|
|
||||||
Vector2 t10 = new Vector2(1+ratio, -ratio);
|
|
||||||
Vector2 t01 = new Vector2(-ratio, +ratio);
|
|
||||||
Vector2 t11 = new Vector2(1+ratio, +ratio);
|
|
||||||
|
|
||||||
Vector3 x00 = new Vector3(start - n - t, depth);
|
|
||||||
Vector3 x10 = new Vector3(end + n - t, depth);
|
|
||||||
Vector3 x01 = new Vector3(start - n + t, depth);
|
|
||||||
Vector3 x11 = new Vector3(end + n + t, depth);
|
|
||||||
|
|
||||||
_calls.TexCoords2(t00);
|
|
||||||
_calls.Vertex3(x00);
|
|
||||||
_calls.TexCoords2(t01);
|
|
||||||
_calls.Vertex3(x01);
|
|
||||||
_calls.TexCoords2(t11);
|
|
||||||
_calls.Vertex3(x11);
|
|
||||||
|
|
||||||
_calls.TexCoords2(t00);
|
|
||||||
_calls.Vertex3(x00);
|
|
||||||
_calls.TexCoords2(t11);
|
|
||||||
_calls.Vertex3(x11);
|
|
||||||
_calls.TexCoords2(t10);
|
|
||||||
_calls.Vertex3(x10);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawRect(ICommandFrame frame)
|
|
||||||
{
|
|
||||||
ref CommandInfo info = ref _commands.Take(out int index);
|
|
||||||
|
|
||||||
RectCommandArgs args = frame.GetParameter<RectCommandArgs>();
|
|
||||||
|
|
||||||
Vector2 size = Vector2.Abs(args.End - args.Start);
|
|
||||||
float aspect = size.X / size.Y;
|
|
||||||
float border = args.StrikeSize;
|
|
||||||
float normRad = args.StrikeSize / size.Y;
|
|
||||||
float wideRad = aspect * normRad;
|
|
||||||
|
|
||||||
int flags = 0;
|
|
||||||
|
|
||||||
switch (frame.Command.Name)
|
|
||||||
{
|
|
||||||
case "RectF":
|
|
||||||
flags |= 1;
|
|
||||||
break;
|
|
||||||
case "RectS":
|
|
||||||
flags |= 2;
|
|
||||||
break;
|
|
||||||
case "RectFS":
|
|
||||||
flags |= 3;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (args.BorderKind)
|
|
||||||
{
|
|
||||||
case BorderKind.Inset:
|
|
||||||
flags |= 2 << 2;
|
|
||||||
break;
|
|
||||||
case BorderKind.Outset:
|
|
||||||
flags |= 1 << 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
info = new CommandInfo()
|
|
||||||
{
|
|
||||||
Type = SimpleDrawCommand.Rect,
|
|
||||||
Flags = flags,
|
|
||||||
Arg0 = aspect,
|
|
||||||
Arg1 = normRad,
|
|
||||||
};
|
|
||||||
|
|
||||||
SetCommandCommonBrush(ref info, args.FillBrush, args.StrikeBrush);
|
|
||||||
|
|
||||||
_calls.Transforms(Executor.TransformStack.Top);
|
|
||||||
_calls.Begin(PrimitiveType.Triangles);
|
|
||||||
|
|
||||||
_calls.CommandIndex(index);
|
|
||||||
|
|
||||||
Vector2 t00 = new Vector2(-wideRad, -normRad);
|
|
||||||
Vector2 t10 = new Vector2(1+wideRad, -normRad);
|
|
||||||
Vector2 t01 = new Vector2(-wideRad, 1+normRad);
|
|
||||||
Vector2 t11 = new Vector2(1+wideRad, 1+normRad);
|
|
||||||
|
|
||||||
Vector3 x00 = new Vector3(args.Start.X - border, args.Start.Y - border, args.Depth);
|
|
||||||
Vector3 x10 = new Vector3(args.End.X + border, args.Start.Y - border, args.Depth);
|
|
||||||
Vector3 x01 = new Vector3(args.Start.X - border, args.End.Y + border, args.Depth);
|
|
||||||
Vector3 x11 = new Vector3(args.End.X + border, args.End.Y + border, args.Depth);
|
|
||||||
|
|
||||||
_calls.TexCoords2(t00);
|
|
||||||
_calls.Vertex3(x00);
|
|
||||||
_calls.TexCoords2(t01);
|
|
||||||
_calls.Vertex3(x01);
|
|
||||||
_calls.TexCoords2(t11);
|
|
||||||
_calls.Vertex3(x11);
|
|
||||||
|
|
||||||
_calls.TexCoords2(t00);
|
|
||||||
_calls.Vertex3(x00);
|
|
||||||
_calls.TexCoords2(t11);
|
|
||||||
_calls.Vertex3(x11);
|
|
||||||
_calls.TexCoords2(t10);
|
|
||||||
_calls.Vertex3(x10);
|
|
||||||
|
|
||||||
_calls.End();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void SetCommandCommonBrush(ref CommandInfo info, IBrush? fill, IBrush? border)
|
|
||||||
{
|
|
||||||
switch (fill?.Kind.Name)
|
|
||||||
{
|
|
||||||
case "DB_Brush_solid":
|
|
||||||
SolidBrush solid = (SolidBrush)fill;
|
|
||||||
Vector4 color = new Vector4(solid.Color.R/255f, solid.Color.G/255f, solid.Color.B/255f, solid.Color.A/255f);
|
|
||||||
info.FgColor = color;
|
|
||||||
break;
|
|
||||||
case "DB_Brush_gradient":
|
|
||||||
GradientBrush gradient = (GradientBrush)fill;
|
|
||||||
GradientUniformBuffer gradients = Executor.ResourcePool.GetResourceManager<GradientUniformBuffer>();
|
|
||||||
gradients.Initialize();
|
|
||||||
GradientUniformBuffer.Entry entry = gradients.InternGradient(gradient.Gradient);
|
|
||||||
info.FgGradientIndex = entry.Offset;
|
|
||||||
info.FgGradientCount = entry.Count;
|
|
||||||
break;
|
|
||||||
case null:
|
|
||||||
// Craete a magenta brush for this.
|
|
||||||
info.FgColor = new Vector4(1, 0, 1, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (border?.Kind.Name)
|
|
||||||
{
|
|
||||||
case "DB_Brush_solid":
|
|
||||||
SolidBrush solid = (SolidBrush)border;
|
|
||||||
Vector4 color = new Vector4(solid.Color.R/255f, solid.Color.G/255f, solid.Color.B/255f, solid.Color.A/255f);
|
|
||||||
info.BgColor = color;
|
|
||||||
break;
|
|
||||||
case "DB_Brush_gradient":
|
|
||||||
GradientBrush gradient = (GradientBrush)border;
|
|
||||||
GradientUniformBuffer gradients = Executor.ResourcePool.GetResourceManager<GradientUniformBuffer>();
|
|
||||||
gradients.Initialize();
|
|
||||||
GradientUniformBuffer.Entry entry = gradients.InternGradient(gradient.Gradient);
|
|
||||||
info.BgGradientIndex = entry.Offset;
|
|
||||||
info.BgGradientCount = entry.Count;
|
|
||||||
break;
|
|
||||||
case null:
|
|
||||||
// Craete a magenta brush for this.
|
|
||||||
info.BgColor = new Vector4(1, 0, 1, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadShaders()
|
|
||||||
{
|
|
||||||
using Stream vsource = FetchEmbeddedResource("Dashboard.Drawing.OpenGL.Executors.simple.vert");
|
|
||||||
using Stream fsource = FetchEmbeddedResource("Dashboard.Drawing.OpenGL.Executors.simple.frag");
|
|
||||||
int vs = ShaderUtil.CompileShader(ShaderType.VertexShader, vsource);
|
|
||||||
int fs = ShaderUtil.CompileShader(ShaderType.FragmentShader, fsource);
|
|
||||||
_program = ShaderUtil.LinkProgram(vs, fs, new []
|
|
||||||
{
|
|
||||||
"a_v3Position",
|
|
||||||
"a_v2TexCoords",
|
|
||||||
"a_iCmdIndex",
|
|
||||||
});
|
|
||||||
GL.DeleteShader(vs);
|
|
||||||
GL.DeleteShader(fs);
|
|
||||||
|
|
||||||
GL.UniformBlockBinding(_program, GL.GetUniformBlockIndex(_program, "CommandBlock"), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Stream FetchEmbeddedResource(string name)
|
|
||||||
{
|
|
||||||
return typeof(BaseCommandExecutor).Assembly.GetManifestResourceStream(name)!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,243 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using BlurgText;
|
|
||||||
using Dashboard.OpenGL;
|
|
||||||
using OpenTK.Graphics.OpenGL;
|
|
||||||
using OpenTK.Mathematics;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing.OpenGL.Executors
|
|
||||||
{
|
|
||||||
public class TextCommandExecutor : ICommandExecutor, IInitializer
|
|
||||||
{
|
|
||||||
public IEnumerable<string> Extensions { get; } = new[] { "DB_Text" };
|
|
||||||
public IContextExecutor Executor { get; private set; }
|
|
||||||
// private BlurgEngine Engine => Executor.ResourcePool.GetResourceManager<BlurgEngine>();
|
|
||||||
public bool IsInitialized { get; private set; }
|
|
||||||
|
|
||||||
private DrawCallRecorder _recorder;
|
|
||||||
private int _program = 0;
|
|
||||||
private int _transformsLocation = -1;
|
|
||||||
private int _atlasLocation = -1;
|
|
||||||
private int _borderWidthLocation = -1;
|
|
||||||
private int _borderColorLocation = -1;
|
|
||||||
private int _fillColorLocation = -1;
|
|
||||||
|
|
||||||
public TextCommandExecutor()
|
|
||||||
{
|
|
||||||
Executor = null!;
|
|
||||||
_recorder = new DrawCallRecorder(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
if (IsInitialized)
|
|
||||||
return;
|
|
||||||
IsInitialized = true;
|
|
||||||
|
|
||||||
Assembly self = typeof(TextCommandExecutor).Assembly;
|
|
||||||
|
|
||||||
using Stream vsource = self.GetManifestResourceStream("Dashboard.Drawing.OpenGL.Executors.text.vert")!;
|
|
||||||
using Stream fsource = self.GetManifestResourceStream("Dashboard.Drawing.OpenGL.Executors.text.frag")!;
|
|
||||||
int vs = ShaderUtil.CompileShader(ShaderType.VertexShader, vsource);
|
|
||||||
int fs = ShaderUtil.CompileShader(ShaderType.FragmentShader, fsource);
|
|
||||||
_program = ShaderUtil.LinkProgram(vs, fs, new []
|
|
||||||
{
|
|
||||||
"a_v3Position",
|
|
||||||
"a_v2TexCoords",
|
|
||||||
});
|
|
||||||
GL.DeleteShader(vs);
|
|
||||||
GL.DeleteShader(fs);
|
|
||||||
|
|
||||||
_transformsLocation = GL.GetUniformLocation(_program, "m4Transforms");
|
|
||||||
_atlasLocation = GL.GetUniformLocation(_program, "txAtlas");
|
|
||||||
_borderWidthLocation = GL.GetUniformLocation(_program, "fBorderWidth");
|
|
||||||
_borderColorLocation = GL.GetUniformLocation(_program, "v4BorderColor");
|
|
||||||
_fillColorLocation = GL.GetUniformLocation(_program, "v4FillColor");
|
|
||||||
|
|
||||||
_recorder.Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetContextExecutor(IContextExecutor executor)
|
|
||||||
{
|
|
||||||
Executor = executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BeginFrame()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BeginDraw()
|
|
||||||
{
|
|
||||||
_recorder.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EndDraw()
|
|
||||||
{
|
|
||||||
GL.UseProgram(_program);
|
|
||||||
GL.Enable(EnableCap.Blend);
|
|
||||||
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
|
|
||||||
_recorder.Execute();
|
|
||||||
GL.Disable(EnableCap.Blend);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EndFrame()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ProcessCommand(ICommandFrame frame)
|
|
||||||
{
|
|
||||||
switch (frame.Command.Name)
|
|
||||||
{
|
|
||||||
case "Text":
|
|
||||||
DrawText(frame);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawText(ICommandFrame frame)
|
|
||||||
{
|
|
||||||
TextCommandArgs args = frame.GetParameter<TextCommandArgs>();
|
|
||||||
// DbBlurgFont font = Engine.InternFont(args.Font);
|
|
||||||
|
|
||||||
BlurgColor color;
|
|
||||||
switch (args.TextBrush)
|
|
||||||
{
|
|
||||||
case SolidBrush solid:
|
|
||||||
color = new BlurgColor()
|
|
||||||
{
|
|
||||||
R = solid.Color.R,
|
|
||||||
G = solid.Color.G,
|
|
||||||
B = solid.Color.B,
|
|
||||||
A = solid.Color.A,
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
color = new BlurgColor() { R = 255, G = 0, B = 255, A = 255 };
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
//BlurgResult? result = Engine.Blurg.BuildString(font.Font, font.Size, color, args.Text);
|
|
||||||
|
|
||||||
// if (result == null)
|
|
||||||
// return;
|
|
||||||
//
|
|
||||||
// Vector3 position = new Vector3(args.Position.X, args.Position.Y, args.Position.Z);
|
|
||||||
// ExecuteBlurgResult(result, position);
|
|
||||||
//
|
|
||||||
// result.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ExecuteBlurgResult(BlurgResult result, Vector3 position)
|
|
||||||
{
|
|
||||||
Matrix4 transforms = Executor.TransformStack.Top;
|
|
||||||
|
|
||||||
for (int i = 0; i < result.Count; i++)
|
|
||||||
{
|
|
||||||
BlurgRect rect = result[i];
|
|
||||||
|
|
||||||
int texture = (int)rect.UserData;
|
|
||||||
Vector4 color = new Vector4(rect.Color.R / 255f, rect.Color.G / 255f, rect.Color.B / 255f,
|
|
||||||
rect.Color.A / 255f);
|
|
||||||
|
|
||||||
if (i == 0)
|
|
||||||
{
|
|
||||||
_recorder.Begin(PrimitiveType.Triangles, new Call()
|
|
||||||
{
|
|
||||||
Texture = texture,
|
|
||||||
FillColor = color,
|
|
||||||
Transforms = transforms,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
_recorder.CurrentCall.Texture != texture ||
|
|
||||||
_recorder.CurrentCall.FillColor != color)
|
|
||||||
{
|
|
||||||
_recorder.End();
|
|
||||||
Call call = new Call()
|
|
||||||
{
|
|
||||||
Texture = texture,
|
|
||||||
FillColor = color,
|
|
||||||
Transforms = transforms,
|
|
||||||
};
|
|
||||||
_recorder.Begin(PrimitiveType.Triangles, call);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3 p00 = new Vector3(rect.X, rect.Y, 0) + position;
|
|
||||||
Vector3 p10 = p00 + new Vector3(rect.Width, 0, 0);
|
|
||||||
Vector3 p11 = p00 + new Vector3(rect.Width, rect.Height, 0);
|
|
||||||
Vector3 p01 = p00 + new Vector3(0, rect.Height, 0);
|
|
||||||
|
|
||||||
Vector2 uv00 = new Vector2(rect.U0, rect.V0);
|
|
||||||
Vector2 uv10 = new Vector2(rect.U1, rect.V0);
|
|
||||||
Vector2 uv11 = new Vector2(rect.U1, rect.V1);
|
|
||||||
Vector2 uv01 = new Vector2(rect.U0, rect.V1);
|
|
||||||
|
|
||||||
_recorder.Vertex(p00, uv00);
|
|
||||||
_recorder.Vertex(p10, uv10);
|
|
||||||
_recorder.Vertex(p11, uv11);
|
|
||||||
|
|
||||||
_recorder.Vertex(p00, uv00);
|
|
||||||
_recorder.Vertex(p11, uv11);
|
|
||||||
_recorder.Vertex(p01, uv01);
|
|
||||||
}
|
|
||||||
_recorder.End();
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct Call
|
|
||||||
{
|
|
||||||
public Matrix4 Transforms = Matrix4.Identity;
|
|
||||||
public int Texture = 0;
|
|
||||||
public float BorderWidth = 0f;
|
|
||||||
public Vector4 FillColor = Vector4.One;
|
|
||||||
public Vector4 BorderColor = new Vector4(0,0,0,1);
|
|
||||||
|
|
||||||
public Call()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(float))]
|
|
||||||
private struct Vertex
|
|
||||||
{
|
|
||||||
[FieldOffset(0)]
|
|
||||||
public Vector3 Position;
|
|
||||||
[FieldOffset(4 * sizeof(float))]
|
|
||||||
public Vector2 TexCoords;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DrawCallRecorder : DrawCallRecorder<Call, Vertex>
|
|
||||||
{
|
|
||||||
private TextCommandExecutor Executor { get; }
|
|
||||||
|
|
||||||
public DrawCallRecorder(TextCommandExecutor executor)
|
|
||||||
{
|
|
||||||
Executor = executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Vertex(Vector3 position, Vector2 texCoords)
|
|
||||||
{
|
|
||||||
Vertex(new Vertex(){Position = position, TexCoords = texCoords});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PrepareCall(in Call call)
|
|
||||||
{
|
|
||||||
Matrix4 transforms = call.Transforms;
|
|
||||||
GL.UniformMatrix4f(Executor._transformsLocation, 1, true, ref transforms);
|
|
||||||
GL.Uniform1f(Executor._borderWidthLocation, call.BorderWidth);
|
|
||||||
GL.Uniform4f(Executor._borderColorLocation, 1, in call.BorderColor);
|
|
||||||
GL.Uniform4f(Executor._fillColorLocation, 1, in call.FillColor);
|
|
||||||
GL.Uniform1i(Executor._atlasLocation, 0);
|
|
||||||
GL.ActiveTexture(TextureUnit.Texture0);
|
|
||||||
GL.BindTexture(TextureTarget.Texture2d, call.Texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void SetVertexFormat()
|
|
||||||
{
|
|
||||||
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, VertexSize, 0);
|
|
||||||
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, VertexSize, 4*sizeof(float));
|
|
||||||
GL.EnableVertexAttribArray(0);
|
|
||||||
GL.EnableVertexAttribArray(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
#ifndef _GRADIENT_GLSL_
|
|
||||||
#define _GRADIENT_GLSL_
|
|
||||||
|
|
||||||
#define DB_GRADIENT_MAX 16
|
|
||||||
|
|
||||||
struct Gradient_t {
|
|
||||||
float fPosition;
|
|
||||||
float pad0;
|
|
||||||
float pad1;
|
|
||||||
float pad2;
|
|
||||||
vec4 v4Color;
|
|
||||||
};
|
|
||||||
|
|
||||||
uniform GradientBlock
|
|
||||||
{
|
|
||||||
Gradient_t vstGradientStops[DB_GRADIENT_MAX];
|
|
||||||
};
|
|
||||||
|
|
||||||
vec4 getGradientColor(float position, int index, int count)
|
|
||||||
{
|
|
||||||
position = clamp(position, 0, 1);
|
|
||||||
|
|
||||||
int i0 = 0;
|
|
||||||
float p0 = vstGradientStops[index + i0].fPosition;
|
|
||||||
|
|
||||||
int i1 = count - 1;
|
|
||||||
float p1 = vstGradientStops[index + i1].fPosition;
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
float px = vstGradientStops[index + i].fPosition;
|
|
||||||
|
|
||||||
if (px > p0 && px <= position)
|
|
||||||
{
|
|
||||||
p0 = px;
|
|
||||||
i0 = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (px < p1 && px >= position)
|
|
||||||
{
|
|
||||||
p1 = px;
|
|
||||||
i1 = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 c0 = vstGradientStops[index + i0].v4Color;
|
|
||||||
vec4 c1 = vstGradientStops[index + i1].v4Color;
|
|
||||||
|
|
||||||
float l = p1 - p0;
|
|
||||||
float w = (l > 0) ? (position - p0) / (p1 - p0) : 0;
|
|
||||||
|
|
||||||
return mix(c0, c1, w);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
#version 140
|
|
||||||
|
|
||||||
#define DB_GRADIENT_MAX 16
|
|
||||||
#define DB_COMMAND_MAX 64
|
|
||||||
|
|
||||||
#define CMD_POINT 1
|
|
||||||
#define CMD_LINE 2
|
|
||||||
#define CMD_RECT 3
|
|
||||||
|
|
||||||
#define STRIKE_CENTER 0
|
|
||||||
#define STRIKE_OUTSET 1
|
|
||||||
#define STRIKE_INSET 2
|
|
||||||
|
|
||||||
in vec3 v_v3Position;
|
|
||||||
in vec2 v_v2TexCoords;
|
|
||||||
flat in int v_iCmdIndex;
|
|
||||||
|
|
||||||
out vec4 f_Color;
|
|
||||||
|
|
||||||
uniform sampler2D txForeground;
|
|
||||||
uniform sampler2D txBackground;
|
|
||||||
|
|
||||||
struct Gradient_t {
|
|
||||||
float fPosition;
|
|
||||||
float pad0;
|
|
||||||
float pad1;
|
|
||||||
float pad2;
|
|
||||||
vec4 v4Color;
|
|
||||||
};
|
|
||||||
|
|
||||||
uniform GradientBlock
|
|
||||||
{
|
|
||||||
Gradient_t vstGradientStops[DB_GRADIENT_MAX];
|
|
||||||
};
|
|
||||||
|
|
||||||
vec4 getGradientColor(float position, int index, int count)
|
|
||||||
{
|
|
||||||
position = clamp(position, 0, 1);
|
|
||||||
|
|
||||||
int i0 = 0;
|
|
||||||
float p0 = vstGradientStops[index + i0].fPosition;
|
|
||||||
|
|
||||||
int i1 = count - 1;
|
|
||||||
float p1 = vstGradientStops[index + i1].fPosition;
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
float px = vstGradientStops[index + i].fPosition;
|
|
||||||
|
|
||||||
if (px > p0 && px <= position)
|
|
||||||
{
|
|
||||||
p0 = px;
|
|
||||||
i0 = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (px < p1 && px >= position)
|
|
||||||
{
|
|
||||||
p1 = px;
|
|
||||||
i1 = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 c0 = vstGradientStops[index + i0].v4Color;
|
|
||||||
vec4 c1 = vstGradientStops[index + i1].v4Color;
|
|
||||||
|
|
||||||
float l = p1 - p0;
|
|
||||||
float w = (l > 0) ? (position - p0) / (p1 - p0) : 0;
|
|
||||||
|
|
||||||
return mix(c0, c1, w);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CommandInfo_t {
|
|
||||||
int iCommand;
|
|
||||||
int iFlags;
|
|
||||||
float fArg0;
|
|
||||||
float fArg1;
|
|
||||||
|
|
||||||
int iFgGradientIndex;
|
|
||||||
int iFgGradientCount;
|
|
||||||
int iBgGradientIndex;
|
|
||||||
int iBgGradientCount;
|
|
||||||
|
|
||||||
vec4 v4FgColor;
|
|
||||||
vec4 v4BgColor;
|
|
||||||
};
|
|
||||||
|
|
||||||
uniform CommandBlock
|
|
||||||
{
|
|
||||||
CommandInfo_t vstCommandInfo[DB_COMMAND_MAX];
|
|
||||||
};
|
|
||||||
|
|
||||||
CommandInfo_t getCommandInfo()
|
|
||||||
{
|
|
||||||
return vstCommandInfo[v_iCmdIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 fgColor()
|
|
||||||
{
|
|
||||||
return getCommandInfo().v4FgColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 bgColor()
|
|
||||||
{
|
|
||||||
return getCommandInfo().v4BgColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Point(void)
|
|
||||||
{
|
|
||||||
vec4 fg = fgColor();
|
|
||||||
|
|
||||||
if (dot(v_v2TexCoords, v_v2TexCoords) <= 0.25)
|
|
||||||
f_Color = fg;
|
|
||||||
else
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LINE_NORMALIZED_RADIUS(cmd) cmd.fArg0
|
|
||||||
void Line(void)
|
|
||||||
{
|
|
||||||
vec4 fg = fgColor();
|
|
||||||
CommandInfo_t cmd = getCommandInfo();
|
|
||||||
|
|
||||||
float t = clamp(v_v2TexCoords.x, 0, 1);
|
|
||||||
vec2 dv = v_v2TexCoords - vec2(t, 0);
|
|
||||||
float d = dot(dv, dv);
|
|
||||||
|
|
||||||
float lim = LINE_NORMALIZED_RADIUS(cmd);
|
|
||||||
lim *= lim;
|
|
||||||
|
|
||||||
if (d <= lim)
|
|
||||||
f_Color = fg;
|
|
||||||
else
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define RECT_ASPECT_RATIO(cmd) (cmd.fArg0)
|
|
||||||
#define RECT_BORDER_WIDTH(cmd) (cmd.fArg1)
|
|
||||||
#define RECT_FILL(cmd) ((cmd.iFlags & (1 << 0)) != 0)
|
|
||||||
#define RECT_BORDER(cmd) ((cmd.iFlags & (1 << 1)) != 0)
|
|
||||||
#define RECT_STRIKE_MASK 3
|
|
||||||
#define RECT_STRIKE_SHIFT 2
|
|
||||||
#define RECT_STRIKE_KIND(cmd) ((cmd.iFlags & RECT_STRIKE_MASK) >> RECT_STRIKE_SHIFT)
|
|
||||||
void Rect(void)
|
|
||||||
{
|
|
||||||
vec4 fg = fgColor();
|
|
||||||
vec4 bg = bgColor();
|
|
||||||
|
|
||||||
CommandInfo_t cmd = getCommandInfo();
|
|
||||||
float aspect = RECT_ASPECT_RATIO(cmd);
|
|
||||||
float border = RECT_BORDER_WIDTH(cmd);
|
|
||||||
int strikeKind = RECT_STRIKE_KIND(cmd);
|
|
||||||
|
|
||||||
vec2 p = abs(2*v_v2TexCoords - vec2(1));
|
|
||||||
p.x = p.x/aspect;
|
|
||||||
|
|
||||||
float m0;
|
|
||||||
float m1;
|
|
||||||
if (!RECT_BORDER(cmd))
|
|
||||||
{
|
|
||||||
m0 = 1;
|
|
||||||
m1 = 1;
|
|
||||||
}
|
|
||||||
else if (strikeKind == STRIKE_OUTSET)
|
|
||||||
{
|
|
||||||
m0 = 1;
|
|
||||||
m1 = border;
|
|
||||||
}
|
|
||||||
else if (strikeKind == STRIKE_INSET)
|
|
||||||
{
|
|
||||||
m0 = 1-border;
|
|
||||||
m1 = 1;
|
|
||||||
}
|
|
||||||
else // strikeKind == STRIKE_CENTER
|
|
||||||
{
|
|
||||||
float h = 0.5 * border;
|
|
||||||
m0 = 1-border;
|
|
||||||
m1 = 1+border;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p.x > m1*aspect || p.y > m1)
|
|
||||||
{
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RECT_FILL(cmd))
|
|
||||||
{
|
|
||||||
if (p.x <= 1 && p.y <= 1)
|
|
||||||
{
|
|
||||||
f_Color = fg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RECT_BORDER(cmd))
|
|
||||||
{
|
|
||||||
float x = clamp(p.x, aspect*m0, aspect*m1);
|
|
||||||
float y = clamp(p.y, m0, m1);
|
|
||||||
|
|
||||||
if (p.x == x || p.y == y)
|
|
||||||
{
|
|
||||||
f_Color = bg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void main(void)
|
|
||||||
{
|
|
||||||
switch (getCommandInfo().iCommand)
|
|
||||||
{
|
|
||||||
case CMD_POINT:
|
|
||||||
Point();
|
|
||||||
break;
|
|
||||||
case CMD_LINE:
|
|
||||||
Line();
|
|
||||||
break;
|
|
||||||
case CMD_RECT:
|
|
||||||
Rect();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unimplemented value.
|
|
||||||
f_Color = vec4(1, 0, 1, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
#version 140
|
|
||||||
|
|
||||||
in vec3 a_v3Position;
|
|
||||||
in vec2 a_v2TexCoords;
|
|
||||||
in int a_iCmdIndex;
|
|
||||||
|
|
||||||
out vec3 v_v3Position;
|
|
||||||
out vec2 v_v2TexCoords;
|
|
||||||
flat out int v_iCmdIndex;
|
|
||||||
|
|
||||||
uniform mat4 m4Transforms;
|
|
||||||
|
|
||||||
void main(void)
|
|
||||||
{
|
|
||||||
vec4 position = vec4(a_v3Position, 1) * m4Transforms;
|
|
||||||
gl_Position = position;
|
|
||||||
v_v3Position = position.xyz/position.w;
|
|
||||||
|
|
||||||
v_v2TexCoords = a_v2TexCoords;
|
|
||||||
v_iCmdIndex = a_iCmdIndex;
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
#version 140
|
|
||||||
|
|
||||||
in vec3 v_v3Position;
|
|
||||||
in vec2 v_v2TexCoords;
|
|
||||||
|
|
||||||
out vec4 f_Color;
|
|
||||||
|
|
||||||
uniform sampler2D txAtlas;
|
|
||||||
uniform float fBorderWidth;
|
|
||||||
uniform vec4 v4BorderColor;
|
|
||||||
uniform vec4 v4FillColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
// For now just honor the fill color
|
|
||||||
|
|
||||||
vec4 color = texture(txAtlas, v_v2TexCoords) * v4FillColor;
|
|
||||||
|
|
||||||
if (color.a <= 0.1)
|
|
||||||
discard;
|
|
||||||
f_Color = color;
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#version 140
|
|
||||||
|
|
||||||
in vec3 a_v3Position;
|
|
||||||
in vec2 a_v2TexCoords;
|
|
||||||
|
|
||||||
out vec3 v_v3Position;
|
|
||||||
out vec2 v_v2TexCoords;
|
|
||||||
|
|
||||||
uniform mat4 m4Transforms;
|
|
||||||
|
|
||||||
void main(void)
|
|
||||||
{
|
|
||||||
vec4 position = vec4(a_v3Position, 1) * m4Transforms;
|
|
||||||
gl_Position = position;
|
|
||||||
v_v3Position = position.xyz/position.w;
|
|
||||||
|
|
||||||
v_v2TexCoords = a_v2TexCoords;
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
// using Dashboard.Drawing.OpenGL.Text;
|
|
||||||
using Dashboard.OpenGL;
|
|
||||||
using OpenTK;
|
|
||||||
using OpenTK.Graphics;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing.OpenGL
|
|
||||||
{
|
|
||||||
public class GLEngine
|
|
||||||
{
|
|
||||||
private readonly Dictionary<IGLContext, ContextExecutor> _executors = new Dictionary<IGLContext, ContextExecutor>();
|
|
||||||
|
|
||||||
public bool IsInitialized { get; private set; } = false;
|
|
||||||
public ContextResourcePoolManager ResourcePoolManager { get; private set; } = new ContextResourcePoolManager();
|
|
||||||
|
|
||||||
public void Initialize(IBindingsContext? bindingsContext = null)
|
|
||||||
{
|
|
||||||
if (IsInitialized)
|
|
||||||
return;
|
|
||||||
IsInitialized = true;
|
|
||||||
|
|
||||||
if (bindingsContext != null)
|
|
||||||
GLLoader.LoadBindings(bindingsContext);
|
|
||||||
|
|
||||||
// Typesetter.Backend = BlurgEngine.Global;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContextExecutor GetExecutor(IGLContext glContext)
|
|
||||||
{
|
|
||||||
if (!_executors.TryGetValue(glContext, out ContextExecutor? executor))
|
|
||||||
{
|
|
||||||
executor = new ContextExecutor(this, glContext);
|
|
||||||
executor.Initialize();
|
|
||||||
|
|
||||||
_executors.Add(glContext, executor);
|
|
||||||
}
|
|
||||||
|
|
||||||
return executor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
using System.Runtime.InteropServices;
|
|
||||||
using Dashboard.OpenGL;
|
|
||||||
using OpenTK.Mathematics;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing.OpenGL
|
|
||||||
{
|
|
||||||
public class GradientUniformBuffer : IInitializer, IGLDisposable, IResourceManager
|
|
||||||
{
|
|
||||||
private bool _isDisposed;
|
|
||||||
private int _top = 0;
|
|
||||||
private readonly MappableBuffer<GradientUniformStruct> _buffer = new MappableBuffer<GradientUniformStruct>();
|
|
||||||
private readonly Dictionary<Gradient, Entry> _entries = new Dictionary<Gradient, Entry>();
|
|
||||||
|
|
||||||
public bool IsInitialized { get; private set; } = false;
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
if (IsInitialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
IsInitialized = true;
|
|
||||||
|
|
||||||
_buffer.Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Entry InternGradient(Gradient gradient)
|
|
||||||
{
|
|
||||||
if (_entries.TryGetValue(gradient, out Entry entry))
|
|
||||||
return entry;
|
|
||||||
|
|
||||||
int count = gradient.Count;
|
|
||||||
int offset = _top;
|
|
||||||
_top += count;
|
|
||||||
|
|
||||||
_buffer.EnsureCapacity(_top);
|
|
||||||
_buffer.Map();
|
|
||||||
Span<GradientUniformStruct> span = _buffer.AsSpan()[offset.._top];
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
GradientStop stop = gradient[i];
|
|
||||||
span[i] = new GradientUniformStruct()
|
|
||||||
{
|
|
||||||
Position = stop.Position,
|
|
||||||
Color = new Vector4(
|
|
||||||
stop.Color.R / 255f,
|
|
||||||
stop.Color.G / 255f,
|
|
||||||
stop.Color.B / 255f,
|
|
||||||
stop.Color.A / 255f),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
entry = new Entry(offset, count);
|
|
||||||
_entries.Add(gradient, entry);
|
|
||||||
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
_entries.Clear();
|
|
||||||
_top = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public record struct Entry(int Offset, int Count);
|
|
||||||
|
|
||||||
public void Dispose() => Dispose(true);
|
|
||||||
|
|
||||||
public void Dispose(bool safeExit)
|
|
||||||
{
|
|
||||||
if (_isDisposed)
|
|
||||||
return;
|
|
||||||
_isDisposed = true;
|
|
||||||
|
|
||||||
_buffer.Dispose(safeExit);
|
|
||||||
}
|
|
||||||
|
|
||||||
string IResourceManager.Name { get; } = nameof(GradientUniformBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 8 * sizeof(float))]
|
|
||||||
public struct GradientUniformStruct
|
|
||||||
{
|
|
||||||
[FieldOffset(0 * sizeof(float))]
|
|
||||||
public float Position;
|
|
||||||
|
|
||||||
[FieldOffset(4 * sizeof(float))]
|
|
||||||
public Vector4 Color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
namespace Dashboard.Drawing.OpenGL
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Atomic reference counter.
|
|
||||||
/// </summary>
|
|
||||||
public interface IArc : IDisposable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The number of references to this.
|
|
||||||
/// </summary>
|
|
||||||
int References { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Increment the number of references.
|
|
||||||
/// </summary>
|
|
||||||
void IncrementReference();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Decrement the number of references.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if this was the last reference.</returns>
|
|
||||||
bool DecrementReference();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace Dashboard.Drawing.OpenGL
|
|
||||||
{
|
|
||||||
public interface IInitializer
|
|
||||||
{
|
|
||||||
bool IsInitialized { get; }
|
|
||||||
|
|
||||||
void Initialize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace Dashboard.Drawing.OpenGL
|
|
||||||
{
|
|
||||||
public interface IResourceManager
|
|
||||||
{
|
|
||||||
public string Name { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
using System.Numerics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Dashboard.OpenGL;
|
|
||||||
using OpenTK.Graphics.OpenGL;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing.OpenGL
|
|
||||||
{
|
|
||||||
public class MappableBuffer<T> : IInitializer, IGLDisposable where T : struct
|
|
||||||
{
|
|
||||||
public int Handle { get; private set; } = 0;
|
|
||||||
public int Capacity { get; set; } = BASE_CAPACITY;
|
|
||||||
public IntPtr Pointer { get; private set; } = IntPtr.Zero;
|
|
||||||
|
|
||||||
public bool IsInitialized => Handle != 0;
|
|
||||||
|
|
||||||
private bool _isDisposed = false;
|
|
||||||
private const int BASE_CAPACITY = 4 << 10; // 4 KiB
|
|
||||||
private const int MAX_INCREMENT = 4 << 20; // 4 MiB
|
|
||||||
|
|
||||||
~MappableBuffer()
|
|
||||||
{
|
|
||||||
Dispose(true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
if (IsInitialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Handle = GL.GenBuffer();
|
|
||||||
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
|
|
||||||
GL.BufferData(BufferTarget.ArrayBuffer, Capacity, IntPtr.Zero, BufferUsage.DynamicDraw);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EnsureCapacity(int count)
|
|
||||||
{
|
|
||||||
if (count < 0)
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(count));
|
|
||||||
|
|
||||||
if (Capacity > count)
|
|
||||||
return;
|
|
||||||
|
|
||||||
SetSize(count, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetSize(int count, bool clear = false)
|
|
||||||
{
|
|
||||||
AssertInitialized();
|
|
||||||
Unmap();
|
|
||||||
|
|
||||||
int sz = Unsafe.SizeOf<T>();
|
|
||||||
int oldsize = Capacity * sz;
|
|
||||||
int request = count * sz;
|
|
||||||
int newsize;
|
|
||||||
|
|
||||||
if (request < BASE_CAPACITY)
|
|
||||||
request = BASE_CAPACITY;
|
|
||||||
|
|
||||||
if (request > MAX_INCREMENT)
|
|
||||||
{
|
|
||||||
newsize = ((request + MAX_INCREMENT - 1) / MAX_INCREMENT) * MAX_INCREMENT;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newsize = checked((int)BitOperations.RoundUpToPowerOf2((ulong)request));
|
|
||||||
}
|
|
||||||
|
|
||||||
int dest = GL.GenBuffer();
|
|
||||||
|
|
||||||
if (clear)
|
|
||||||
{
|
|
||||||
GL.BindBuffer(BufferTarget.ArrayBuffer, dest);
|
|
||||||
GL.BufferData(BufferTarget.ArrayBuffer, newsize, IntPtr.Zero, BufferUsage.DynamicDraw);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GL.BindBuffer(BufferTarget.CopyWriteBuffer, dest);
|
|
||||||
GL.BindBuffer(BufferTarget.CopyReadBuffer, Handle);
|
|
||||||
|
|
||||||
GL.BufferData(BufferTarget.CopyWriteBuffer, newsize, IntPtr.Zero, BufferUsage.DynamicDraw);
|
|
||||||
GL.CopyBufferSubData(CopyBufferSubDataTarget.CopyReadBuffer, CopyBufferSubDataTarget.CopyWriteBuffer, 0, 0, Math.Min(newsize, oldsize));
|
|
||||||
}
|
|
||||||
|
|
||||||
GL.DeleteBuffer(Handle);
|
|
||||||
Handle = dest;
|
|
||||||
Capacity = newsize / Unsafe.SizeOf<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe void Map()
|
|
||||||
{
|
|
||||||
if (Pointer != IntPtr.Zero)
|
|
||||||
return;
|
|
||||||
|
|
||||||
AssertInitialized();
|
|
||||||
|
|
||||||
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
|
|
||||||
Pointer = (IntPtr)GL.MapBuffer(BufferTarget.ArrayBuffer, BufferAccess.ReadWrite);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Unmap()
|
|
||||||
{
|
|
||||||
if (Pointer == IntPtr.Zero)
|
|
||||||
return;
|
|
||||||
|
|
||||||
AssertInitialized();
|
|
||||||
|
|
||||||
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
|
|
||||||
GL.UnmapBuffer(BufferTarget.ArrayBuffer);
|
|
||||||
Pointer = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe Span<T> AsSpan()
|
|
||||||
{
|
|
||||||
if (Pointer == IntPtr.Zero)
|
|
||||||
throw new InvalidOperationException("The buffer is not currently mapped.");
|
|
||||||
|
|
||||||
AssertInitialized();
|
|
||||||
|
|
||||||
return new Span<T>(Pointer.ToPointer(), Capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AssertInitialized()
|
|
||||||
{
|
|
||||||
if (Handle == 0)
|
|
||||||
throw new InvalidOperationException("The buffer is not initialized.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Dispose(bool safeExit, bool disposing)
|
|
||||||
{
|
|
||||||
if (_isDisposed)
|
|
||||||
return;
|
|
||||||
_isDisposed = true;
|
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
|
|
||||||
if (safeExit)
|
|
||||||
ContextCollector.Global.DeleteBufffer(Handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() => Dispose(true, true);
|
|
||||||
public void Dispose(bool safeExit) => Dispose(safeExit, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MappableBumpAllocator<T> : MappableBuffer<T> where T : struct
|
|
||||||
{
|
|
||||||
private int _top = 0;
|
|
||||||
private int _previousTop = 0;
|
|
||||||
|
|
||||||
public ref T Take(out int index)
|
|
||||||
{
|
|
||||||
index = _top;
|
|
||||||
EnsureCapacity(++_top);
|
|
||||||
Map();
|
|
||||||
|
|
||||||
return ref AsSpan()[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
public ref T Take() => ref Take(out _);
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
SetSize(0, true);
|
|
||||||
_previousTop = _top;
|
|
||||||
_top = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
using OpenTK.Mathematics;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing.OpenGL
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The current stack of transformations.
|
|
||||||
/// </summary>
|
|
||||||
public class TransformStack
|
|
||||||
{
|
|
||||||
private Matrix4 _top = Matrix4.Identity;
|
|
||||||
private readonly Stack<Matrix4> _stack = new Stack<Matrix4>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The top-most transform matrix.
|
|
||||||
/// </summary>
|
|
||||||
public ref readonly Matrix4 Top => ref _top;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The number of matrices in the stack.
|
|
||||||
/// </summary>
|
|
||||||
public int Count => _stack.Count;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Push a transform.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="transform">The transform to push.</param>
|
|
||||||
public void Push(in Matrix4 transform)
|
|
||||||
{
|
|
||||||
_stack.Push(_top);
|
|
||||||
_top = transform * _top;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pop a transform.
|
|
||||||
/// </summary>
|
|
||||||
public void Pop()
|
|
||||||
{
|
|
||||||
if (!_stack.TryPop(out _top))
|
|
||||||
_top = Matrix4.Identity;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clear the stack of transformations.
|
|
||||||
/// </summary>
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
_stack.Clear();
|
|
||||||
_top = Matrix4.Identity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Drawing;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing
|
|
||||||
{
|
|
||||||
public class BrushExtension : DrawExtension
|
|
||||||
{
|
|
||||||
private BrushExtension() : base("DB_Brush") { }
|
|
||||||
|
|
||||||
public static readonly BrushExtension Instance = new BrushExtension();
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IBrush : IDrawResource
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly struct SolidBrush(Color color) : IBrush
|
|
||||||
{
|
|
||||||
public IDrawExtension Kind { get; } = SolidBrushExtension.Instance;
|
|
||||||
public Color Color { get; } = color;
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return HashCode.Combine(Kind, Color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly struct GradientBrush(Gradient gradient) : IBrush
|
|
||||||
{
|
|
||||||
public IDrawExtension Kind { get; } = GradientBrushExtension.Instance;
|
|
||||||
public Gradient Gradient { get; } = gradient;
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return HashCode.Combine(Kind, Gradient);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SolidBrushExtension : DrawExtension
|
|
||||||
{
|
|
||||||
private SolidBrushExtension() : base("DB_Brush_solid", new[] { BrushExtension.Instance }) { }
|
|
||||||
|
|
||||||
public static readonly SolidBrushExtension Instance = new SolidBrushExtension();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GradientBrushExtension : DrawExtension
|
|
||||||
{
|
|
||||||
private GradientBrushExtension() : base("DB_Brush_gradient", new[] { BrushExtension.Instance }) { }
|
|
||||||
|
|
||||||
public static readonly GradientBrushExtension Instance = new GradientBrushExtension();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<ImplicitUsings>disable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Dashboard.Common\Dashboard.Common.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,292 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing
|
|
||||||
{
|
|
||||||
public class DbBaseCommands : DrawExtension
|
|
||||||
{
|
|
||||||
public DrawCommand<PointCommandArgs> DrawPoint { get; }
|
|
||||||
public DrawCommand<LineCommandArgs> DrawLine { get; }
|
|
||||||
public RectCommand DrawRectF { get; }
|
|
||||||
public RectCommand DrawRectS { get; }
|
|
||||||
public RectCommand DrawRectFS { get; }
|
|
||||||
|
|
||||||
private DbBaseCommands() : base("DB_base",
|
|
||||||
new[]
|
|
||||||
{
|
|
||||||
BrushExtension.Instance,
|
|
||||||
})
|
|
||||||
{
|
|
||||||
AddCommand(DrawPoint = new DrawCommand<PointCommandArgs>("Point", this, PointCommandArgs.CommandSize));
|
|
||||||
AddCommand(DrawLine = new DrawCommand<LineCommandArgs>("Line", this, LineCommandArgs.CommandSize));
|
|
||||||
AddCommand(DrawRectF = new RectCommand(this, RectCommand.Mode.Fill));
|
|
||||||
AddCommand(DrawRectS = new RectCommand(this, RectCommand.Mode.Strike));
|
|
||||||
AddCommand(DrawRectFS = new RectCommand(this, RectCommand.Mode.FillStrike));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly DbBaseCommands Instance = new DbBaseCommands();
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct PointCommandArgs : IParameterSerializer<PointCommandArgs>
|
|
||||||
{
|
|
||||||
public Vector2 Position { get; private set; }
|
|
||||||
public float Depth { get; private set; }
|
|
||||||
public float Size { get; private set; }
|
|
||||||
public IBrush? Brush { get; private set; }
|
|
||||||
|
|
||||||
public PointCommandArgs(Vector2 position, float depth, float size, IBrush brush)
|
|
||||||
{
|
|
||||||
Position = position;
|
|
||||||
Depth = depth;
|
|
||||||
Brush = brush;
|
|
||||||
Size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Serialize(DrawQueue queue, Span<byte> bytes)
|
|
||||||
{
|
|
||||||
if (bytes.Length < CommandSize)
|
|
||||||
return CommandSize;
|
|
||||||
|
|
||||||
Span<Value> value = stackalloc Value[]
|
|
||||||
{
|
|
||||||
new Value(Position, Depth, Size, queue.RequireResource(Brush!))
|
|
||||||
};
|
|
||||||
|
|
||||||
MemoryMarshal.AsBytes(value).CopyTo(bytes);
|
|
||||||
return CommandSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MemberNotNull(nameof(Brush))]
|
|
||||||
public void Deserialize(DrawQueue queue, ReadOnlySpan<byte> bytes)
|
|
||||||
{
|
|
||||||
if (bytes.Length < CommandSize)
|
|
||||||
throw new Exception("Not enough bytes");
|
|
||||||
|
|
||||||
Value value = MemoryMarshal.AsRef<Value>(bytes);
|
|
||||||
|
|
||||||
Position = value.Position;
|
|
||||||
Depth = value.Depth;
|
|
||||||
Size = value.Size;
|
|
||||||
Brush = (IBrush)queue.Resources[value.BrushIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
private record struct Value(Vector2 Position, float Depth, float Size, int BrushIndex);
|
|
||||||
|
|
||||||
public static readonly int CommandSize = Unsafe.SizeOf<Value>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct LineCommandArgs : IParameterSerializer<LineCommandArgs>
|
|
||||||
{
|
|
||||||
public Vector2 Start { get; private set; }
|
|
||||||
public Vector2 End { get; private set; }
|
|
||||||
public float Depth { get; private set; }
|
|
||||||
public float Size { get; private set; }
|
|
||||||
public IBrush? Brush { get; private set; }
|
|
||||||
|
|
||||||
public LineCommandArgs(Vector2 start, Vector2 end, float depth, float size, IBrush brush)
|
|
||||||
{
|
|
||||||
Start = start;
|
|
||||||
End = end;
|
|
||||||
Depth = depth;
|
|
||||||
Size = size;
|
|
||||||
Brush = brush;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Serialize(DrawQueue queue, Span<byte> bytes)
|
|
||||||
{
|
|
||||||
if (bytes.Length < CommandSize)
|
|
||||||
return CommandSize;
|
|
||||||
|
|
||||||
Span<Value> value = stackalloc Value[]
|
|
||||||
{
|
|
||||||
new Value(Start, End, Depth, Size, queue.RequireResource(Brush!))
|
|
||||||
};
|
|
||||||
|
|
||||||
MemoryMarshal.AsBytes(value).CopyTo(bytes);
|
|
||||||
return CommandSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Deserialize(DrawQueue queue, ReadOnlySpan<byte> bytes)
|
|
||||||
{
|
|
||||||
if (bytes.Length < CommandSize)
|
|
||||||
throw new Exception("Not enough bytes");
|
|
||||||
|
|
||||||
Value value = MemoryMarshal.AsRef<Value>(bytes);
|
|
||||||
|
|
||||||
Start = value.Start;
|
|
||||||
End = value.End;
|
|
||||||
Depth = value.Depth;
|
|
||||||
Size = value.Size;
|
|
||||||
Brush = (IBrush)queue.Resources[value.BrushIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
private record struct Value(Vector2 Start, Vector2 End, float Depth, float Size, int BrushIndex);
|
|
||||||
|
|
||||||
public static readonly int CommandSize = Unsafe.SizeOf<Value>();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class RectCommand : IDrawCommand<RectCommandArgs>
|
|
||||||
{
|
|
||||||
private readonly Mode _mode;
|
|
||||||
public string Name { get; }
|
|
||||||
public IDrawExtension Extension { get; }
|
|
||||||
public int Length { get; }
|
|
||||||
|
|
||||||
public RectCommand(IDrawExtension extension, Mode mode)
|
|
||||||
{
|
|
||||||
Extension = extension;
|
|
||||||
_mode = mode;
|
|
||||||
|
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
case Mode.Fill:
|
|
||||||
Name = "RectF";
|
|
||||||
Length = Unsafe.SizeOf<RectF>();
|
|
||||||
break;
|
|
||||||
case Mode.Strike:
|
|
||||||
Name = "RectS";
|
|
||||||
Length = Unsafe.SizeOf<RectS>();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Name = "RectFS";
|
|
||||||
Length = Unsafe.SizeOf<RectFS>();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object? IDrawCommand.GetParams(DrawQueue queue, ReadOnlySpan<byte> param)
|
|
||||||
{
|
|
||||||
return GetParams(queue, param);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RectCommandArgs GetParams(DrawQueue queue, ReadOnlySpan<byte> param)
|
|
||||||
{
|
|
||||||
if (param.Length < Length)
|
|
||||||
throw new Exception("Not enough bytes");
|
|
||||||
|
|
||||||
RectCommandArgs args;
|
|
||||||
|
|
||||||
switch (_mode)
|
|
||||||
{
|
|
||||||
case Mode.Fill:
|
|
||||||
ref readonly RectF f = ref MemoryMarshal.AsRef<RectF>(param);
|
|
||||||
args = new RectCommandArgs(f.Start, f.End, f.Depth, (IBrush)queue.Resources[f.FillBrushIndex]);
|
|
||||||
break;
|
|
||||||
case Mode.Strike:
|
|
||||||
ref readonly RectS s = ref MemoryMarshal.AsRef<RectS>(param);
|
|
||||||
args = new RectCommandArgs(s.Start, s.End, s.Depth, (IBrush)queue.Resources[s.StrikeBrushIndex], s.StrikeSize, s.BorderKind);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ref readonly RectFS fs = ref MemoryMarshal.AsRef<RectFS>(param);
|
|
||||||
args = new RectCommandArgs(fs.Start, fs.End, fs.Depth, (IBrush)queue.Resources[fs.FillBrushIndex],
|
|
||||||
(IBrush)queue.Resources[fs.StrikeBrushIndex], fs.StrikeSize, fs.BorderKind);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int WriteParams(DrawQueue queue, object? obj, Span<byte> param)
|
|
||||||
{
|
|
||||||
return WriteParams(queue, (RectCommandArgs)obj, param);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int WriteParams(DrawQueue queue, RectCommandArgs obj, Span<byte> param)
|
|
||||||
{
|
|
||||||
if (param.Length < Length)
|
|
||||||
return Length;
|
|
||||||
|
|
||||||
switch (_mode)
|
|
||||||
{
|
|
||||||
case Mode.Fill:
|
|
||||||
ref RectF f = ref MemoryMarshal.AsRef<RectF>(param);
|
|
||||||
f.Start = obj.Start;
|
|
||||||
f.End = obj.End;
|
|
||||||
f.Depth = obj.Depth;
|
|
||||||
f.FillBrushIndex = queue.RequireResource(obj.FillBrush!);
|
|
||||||
break;
|
|
||||||
case Mode.Strike:
|
|
||||||
ref RectS s = ref MemoryMarshal.AsRef<RectS>(param);
|
|
||||||
s.Start = obj.Start;
|
|
||||||
s.End = obj.End;
|
|
||||||
s.Depth = obj.Depth;
|
|
||||||
s.StrikeBrushIndex = queue.RequireResource(obj.StrikeBrush!);
|
|
||||||
s.StrikeSize = obj.StrikeSize;
|
|
||||||
s.BorderKind = obj.BorderKind;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ref RectFS fs = ref MemoryMarshal.AsRef<RectFS>(param);
|
|
||||||
fs.Start = obj.Start;
|
|
||||||
fs.End = obj.End;
|
|
||||||
fs.Depth = obj.Depth;
|
|
||||||
fs.FillBrushIndex = queue.RequireResource(obj.FillBrush!);
|
|
||||||
fs.StrikeBrushIndex = queue.RequireResource(obj.StrikeBrush!);
|
|
||||||
fs.StrikeSize = obj.StrikeSize;
|
|
||||||
fs.BorderKind = obj.BorderKind;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
public enum Mode
|
|
||||||
{
|
|
||||||
Fill = 1,
|
|
||||||
Strike = 2,
|
|
||||||
FillStrike = Fill | Strike,
|
|
||||||
}
|
|
||||||
|
|
||||||
private record struct RectF(Vector2 Start, Vector2 End, float Depth, int FillBrushIndex);
|
|
||||||
private record struct RectS(Vector2 Start, Vector2 End, float Depth, int StrikeBrushIndex, float StrikeSize, BorderKind BorderKind);
|
|
||||||
private record struct RectFS(Vector2 Start, Vector2 End, float Depth, int FillBrushIndex, int StrikeBrushIndex, float StrikeSize, BorderKind BorderKind);
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct RectCommandArgs
|
|
||||||
{
|
|
||||||
public Vector2 Start { get; private set; }
|
|
||||||
public Vector2 End { get; private set; }
|
|
||||||
public float Depth { get; private set; }
|
|
||||||
public float StrikeSize { get; private set; } = 0f;
|
|
||||||
public BorderKind BorderKind { get; private set; } = BorderKind.Center;
|
|
||||||
public IBrush? FillBrush { get; private set; } = null;
|
|
||||||
public IBrush? StrikeBrush { get; private set; } = null;
|
|
||||||
public bool IsStruck => StrikeSize != 0;
|
|
||||||
|
|
||||||
public RectCommandArgs(Vector2 start, Vector2 end, float depth, IBrush fillBrush)
|
|
||||||
{
|
|
||||||
Start = start;
|
|
||||||
End = end;
|
|
||||||
Depth = depth;
|
|
||||||
FillBrush = fillBrush;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RectCommandArgs(Vector2 start, Vector2 end, float depth, IBrush strikeBrush, float strikeSize, BorderKind borderKind)
|
|
||||||
{
|
|
||||||
Start = start;
|
|
||||||
End = end;
|
|
||||||
Depth = depth;
|
|
||||||
StrikeBrush = strikeBrush;
|
|
||||||
StrikeSize = strikeSize;
|
|
||||||
BorderKind = borderKind;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RectCommandArgs(Vector2 start, Vector2 end, float depth, IBrush fillBrush, IBrush strikeBrush, float strikeSize,
|
|
||||||
BorderKind borderKind)
|
|
||||||
{
|
|
||||||
Start = start;
|
|
||||||
End = end;
|
|
||||||
Depth = depth;
|
|
||||||
FillBrush = fillBrush;
|
|
||||||
StrikeBrush = strikeBrush;
|
|
||||||
StrikeSize = strikeSize;
|
|
||||||
BorderKind = borderKind;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing
|
|
||||||
{
|
|
||||||
public enum DrawPrimitive
|
|
||||||
{
|
|
||||||
Point,
|
|
||||||
Line,
|
|
||||||
LineStrip,
|
|
||||||
Triangle,
|
|
||||||
TriangleFan,
|
|
||||||
TriangleStrip
|
|
||||||
}
|
|
||||||
|
|
||||||
public record struct DrawVertex(Vector3 Position, Vector3 TextureCoordinate, Vector4 Color);
|
|
||||||
|
|
||||||
public record DrawInfo(DrawPrimitive Primitive, int Count)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DrawBuffer
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing
|
|
||||||
{
|
|
||||||
public interface IDrawCommand
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Name of the command.
|
|
||||||
/// </summary>
|
|
||||||
string Name { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The draw extension that defines this command.
|
|
||||||
/// </summary>
|
|
||||||
IDrawExtension Extension { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The length of the command data segment, in bytes.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Must be 0 for simple commands. For commands that are variadic, the
|
|
||||||
/// value must be less than 0. Any other positive value, otherwise.
|
|
||||||
/// </remarks>
|
|
||||||
int Length { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the parameters object for this command.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="param">The parameter array.</param>
|
|
||||||
/// <returns>The parameters object.</returns>
|
|
||||||
object? GetParams(DrawQueue queue, ReadOnlySpan<byte> param);
|
|
||||||
|
|
||||||
int WriteParams(DrawQueue queue, object? obj, Span<byte> param);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IDrawCommand<T> : IDrawCommand
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Get the parameters object for this command.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="param">The parameter array.</param>
|
|
||||||
/// <returns>The parameters object.</returns>
|
|
||||||
new T? GetParams(DrawQueue queue, ReadOnlySpan<byte> param);
|
|
||||||
|
|
||||||
new int WriteParams(DrawQueue queue, T? obj, Span<byte> param);
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class DrawCommand : IDrawCommand
|
|
||||||
{
|
|
||||||
public string Name { get; }
|
|
||||||
public IDrawExtension Extension { get; }
|
|
||||||
public int Length { get; } = 0;
|
|
||||||
|
|
||||||
public DrawCommand(string name, IDrawExtension extension)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
Extension = extension;
|
|
||||||
}
|
|
||||||
|
|
||||||
public object? GetParams(DrawQueue queue, ReadOnlySpan<byte> param)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int WriteParams(DrawQueue queue, object? obj, Span<byte> param)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class DrawCommand<T> : IDrawCommand<T>
|
|
||||||
where T : IParameterSerializer<T>, new()
|
|
||||||
{
|
|
||||||
public string Name { get; }
|
|
||||||
public IDrawExtension Extension { get; }
|
|
||||||
public int Length { get; }
|
|
||||||
|
|
||||||
public DrawCommand(string name, IDrawExtension extension, int length)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
Extension = extension;
|
|
||||||
Length = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T? GetParams(DrawQueue queue, ReadOnlySpan<byte> param)
|
|
||||||
{
|
|
||||||
T t = new T();
|
|
||||||
t.Deserialize(queue, param);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int WriteParams(DrawQueue queue, T? obj, Span<byte> param)
|
|
||||||
{
|
|
||||||
return obj!.Serialize(queue, param);
|
|
||||||
}
|
|
||||||
|
|
||||||
int IDrawCommand.WriteParams(DrawQueue queue, object? obj, Span<byte> param)
|
|
||||||
{
|
|
||||||
return WriteParams(queue, (T?)obj, param);
|
|
||||||
}
|
|
||||||
|
|
||||||
object? IDrawCommand.GetParams(DrawQueue queue, ReadOnlySpan<byte> param)
|
|
||||||
{
|
|
||||||
return GetParams(queue, param);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Immutable;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interface for all drawing extensions.
|
|
||||||
/// </summary>
|
|
||||||
public interface IDrawExtension
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Name of this extension.
|
|
||||||
/// </summary>
|
|
||||||
public string Name { get; }
|
|
||||||
|
|
||||||
public IReadOnlyList<IDrawExtension> Requires { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The list of commands this extension defines, if any.
|
|
||||||
/// </summary>
|
|
||||||
public IReadOnlyList<IDrawCommand> Commands { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A simple draw extension.
|
|
||||||
/// </summary>
|
|
||||||
public class DrawExtension : IDrawExtension
|
|
||||||
{
|
|
||||||
private readonly List<IDrawCommand> _drawCommands = new List<IDrawCommand>();
|
|
||||||
|
|
||||||
public string Name { get; }
|
|
||||||
|
|
||||||
public IReadOnlyList<IDrawCommand> Commands { get; }
|
|
||||||
|
|
||||||
public IReadOnlyList<IDrawExtension> Requires { get; }
|
|
||||||
|
|
||||||
public DrawExtension(string name, IEnumerable<IDrawExtension>? requires = null)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
Commands = _drawCommands.AsReadOnly();
|
|
||||||
Requires = (requires ?? Enumerable.Empty<IDrawExtension>()).ToImmutableList();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void AddCommand(IDrawCommand command)
|
|
||||||
{
|
|
||||||
_drawCommands.Add(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DrawExtensionClass
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Get the draw controller for the given queue.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="extension">The extension instance.</param>
|
|
||||||
/// <param name="queue">The draw queue.</param>
|
|
||||||
/// <returns>The draw controller for this queue.</returns>
|
|
||||||
public static IDrawController GetController(this IDrawExtension extension, DrawQueue queue)
|
|
||||||
{
|
|
||||||
return queue.GetController(extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Point(this DrawQueue queue, Vector2 position, float depth, float size, IBrush brush)
|
|
||||||
{
|
|
||||||
Vector2 radius = new Vector2(0.5f * size);
|
|
||||||
Box2d bounds = new Box2d(position - radius, position + radius);
|
|
||||||
|
|
||||||
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
|
|
||||||
controller.EnsureBounds(bounds, depth);
|
|
||||||
controller.Write(DbBaseCommands.Instance.DrawPoint, new PointCommandArgs(position, depth, size, brush));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Line(this DrawQueue queue, Vector2 start, Vector2 end, float depth, float size, IBrush brush)
|
|
||||||
{
|
|
||||||
Vector2 radius = new Vector2(size / 2f);
|
|
||||||
Vector2 min = Vector2.Min(start, end) - radius;
|
|
||||||
Vector2 max = Vector2.Max(start, end) + radius;
|
|
||||||
Box2d bounds = new Box2d(min, max);
|
|
||||||
|
|
||||||
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
|
|
||||||
controller.EnsureBounds(bounds, depth);
|
|
||||||
controller.Write(DbBaseCommands.Instance.DrawLine, new LineCommandArgs(start, end, depth, size, brush));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Rect(this DrawQueue queue, Vector2 start, Vector2 end, float depth, IBrush fillBrush)
|
|
||||||
{
|
|
||||||
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
|
|
||||||
Vector2 min = Vector2.Min(start, end);
|
|
||||||
Vector2 max = Vector2.Max(start, end);
|
|
||||||
controller.EnsureBounds(new Box2d(min, max), depth);
|
|
||||||
controller.Write(DbBaseCommands.Instance.DrawRectF, new RectCommandArgs(start, end, depth, fillBrush));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Rect(this DrawQueue queue, Vector2 start, Vector2 end, float depth, IBrush strikeBrush, float strikeSize,
|
|
||||||
BorderKind kind = BorderKind.Center)
|
|
||||||
{
|
|
||||||
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
|
|
||||||
Vector2 min = Vector2.Min(start, end);
|
|
||||||
Vector2 max = Vector2.Max(start, end);
|
|
||||||
controller.EnsureBounds(new Box2d(min, max), depth);
|
|
||||||
controller.Write(DbBaseCommands.Instance.DrawRectS, new RectCommandArgs(start, end, depth, strikeBrush, strikeSize, kind));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Rect(this DrawQueue queue, Vector2 start, Vector2 end, float depth, IBrush fillBrush, IBrush strikeBrush,
|
|
||||||
float strikeSize, BorderKind kind = BorderKind.Center)
|
|
||||||
{
|
|
||||||
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
|
|
||||||
Vector2 min = Vector2.Min(start, end);
|
|
||||||
Vector2 max = Vector2.Max(start, end);
|
|
||||||
controller.EnsureBounds(new Box2d(min, max), depth);
|
|
||||||
controller.Write(DbBaseCommands.Instance.DrawRectFS, new RectCommandArgs(start, end, depth, fillBrush, strikeBrush, strikeSize, kind));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Text(this DrawQueue queue, Vector3 position, IBrush brush, string text, IFont font,
|
|
||||||
Anchor anchor = Anchor.Left)
|
|
||||||
{
|
|
||||||
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
|
|
||||||
SizeF size = Typesetter.MeasureString(font, text);
|
|
||||||
controller.EnsureBounds(new Box2d(position.X, position.Y, position.X + size.Width, position.Y + size.Height), position.Z);
|
|
||||||
controller.Write(TextExtension.Instance.TextCommand, new TextCommandArgs(font, brush, anchor, position, text));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Text(this DrawQueue queue, Vector3 position, IBrush textBrush, IBrush borderBrush,
|
|
||||||
float borderRadius, string text, IFont font, Anchor anchor = Anchor.Left, BorderKind borderKind = BorderKind.Outset)
|
|
||||||
{
|
|
||||||
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
|
|
||||||
SizeF size = Typesetter.MeasureString(font, text);
|
|
||||||
controller.EnsureBounds(new Box2d(position.X, position.Y, position.X + size.Width, position.Y + size.Height), position.Z);
|
|
||||||
controller.Write(TextExtension.Instance.TextCommand, new TextCommandArgs(font, textBrush, anchor, position, text)
|
|
||||||
{
|
|
||||||
BorderBrush = borderBrush,
|
|
||||||
BorderRadius = borderRadius,
|
|
||||||
BorderKind = borderKind,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,368 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing
|
|
||||||
{
|
|
||||||
public class DrawQueue : IEnumerable<ICommandFrame>, IDisposable
|
|
||||||
{
|
|
||||||
private readonly HashList<IDrawExtension> _extensions = new HashList<IDrawExtension>();
|
|
||||||
private readonly HashList<IDrawCommand> _commands = new HashList<IDrawCommand>();
|
|
||||||
private readonly HashList<IDrawResource> _resources = new HashList<IDrawResource>();
|
|
||||||
private readonly DrawController _controller;
|
|
||||||
private readonly MemoryStream _commandStream = new MemoryStream();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The absolute boundary of all graphics objects.
|
|
||||||
/// </summary>
|
|
||||||
public Box3d Bounds { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The extensions required to draw the image.
|
|
||||||
/// </summary>
|
|
||||||
public IReadOnlyList<IDrawExtension> Extensions => _extensions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The resources used by this draw queue.
|
|
||||||
/// </summary>
|
|
||||||
public IReadOnlyList<IDrawResource> Resources => _resources;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The list of commands used by the extension.
|
|
||||||
/// </summary>
|
|
||||||
public IReadOnlyList<IDrawCommand> Command => _commands;
|
|
||||||
|
|
||||||
public DrawQueue()
|
|
||||||
{
|
|
||||||
_controller = new DrawController(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clear the queue.
|
|
||||||
/// </summary>
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
_resources.Clear();
|
|
||||||
_commands.Clear();
|
|
||||||
_extensions.Clear();
|
|
||||||
_commandStream.SetLength(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int RequireExtension(IDrawExtension extension)
|
|
||||||
{
|
|
||||||
foreach (IDrawExtension super in extension.Requires)
|
|
||||||
RequireExtension(super);
|
|
||||||
|
|
||||||
return _extensions.Intern(extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int RequireResource(IDrawResource resource)
|
|
||||||
{
|
|
||||||
RequireExtension(resource.Kind);
|
|
||||||
return _resources.Intern(resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal IDrawController GetController(IDrawExtension extension)
|
|
||||||
{
|
|
||||||
_extensions.Intern(extension);
|
|
||||||
return _controller;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Write(IDrawCommand command)
|
|
||||||
{
|
|
||||||
if (command.Length > 0)
|
|
||||||
throw new InvalidOperationException("This command has a finite length argument.");
|
|
||||||
|
|
||||||
int cmdIndex = _commands.Intern(command);
|
|
||||||
|
|
||||||
Span<byte> cmd = stackalloc byte[6];
|
|
||||||
int sz;
|
|
||||||
|
|
||||||
if (command.Length == 0)
|
|
||||||
{
|
|
||||||
// Write a fixed command.
|
|
||||||
sz = ToVlq(cmdIndex, cmd);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Write a variadic with zero length.
|
|
||||||
sz = ToVlq(cmdIndex, cmd);
|
|
||||||
cmd[sz++] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
_commandStream.Write(cmd[..sz]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Write(IDrawCommand command, ReadOnlySpan<byte> param)
|
|
||||||
{
|
|
||||||
if (command.Length < 0)
|
|
||||||
{
|
|
||||||
Span<byte> cmd = stackalloc byte[10];
|
|
||||||
int cmdIndex = _commands.Intern(command);
|
|
||||||
int sz = ToVlq(cmdIndex, cmd);
|
|
||||||
sz += ToVlq(param.Length, cmd[sz..]);
|
|
||||||
_commandStream.Write(cmd[..sz]);
|
|
||||||
_commandStream.Write(param);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (command.Length != param.Length)
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(param.Length), "Length of the parameter does not match the command.");
|
|
||||||
|
|
||||||
Span<byte> cmd = stackalloc byte[5];
|
|
||||||
int cmdIndex = _commands.Intern(command);
|
|
||||||
int sz = ToVlq(cmdIndex, cmd);
|
|
||||||
|
|
||||||
_commandStream.Write(cmd[..sz]);
|
|
||||||
_commandStream.Write(param);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Enumerator GetEnumerator() => new Enumerator(this);
|
|
||||||
IEnumerator<ICommandFrame> IEnumerable<ICommandFrame>.GetEnumerator() => GetEnumerator();
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
|
||||||
|
|
||||||
private static int ToVlq(int value, Span<byte> bytes)
|
|
||||||
{
|
|
||||||
if (value < 0)
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(value), "Must be a positive integer.");
|
|
||||||
else if (bytes.Length < 5)
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(bytes), "Must at least be five bytes long.");
|
|
||||||
|
|
||||||
if (value == 0)
|
|
||||||
{
|
|
||||||
bytes[0] = 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 5 && value != 0; i++, value >>= 7)
|
|
||||||
{
|
|
||||||
if (i > 0)
|
|
||||||
bytes[i - 1] |= 1 << 7;
|
|
||||||
|
|
||||||
bytes[i] = (byte)(value & 0x7F);
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int FromVlq(ReadOnlySpan<byte> bytes, out int value)
|
|
||||||
{
|
|
||||||
value = 0;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < bytes.Length; i++)
|
|
||||||
{
|
|
||||||
byte b = bytes[i];
|
|
||||||
|
|
||||||
value |= (b & 0x7F) << (7*i);
|
|
||||||
|
|
||||||
if ((b & (1 << 7)) == 0)
|
|
||||||
{
|
|
||||||
i++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DrawController(DrawQueue Queue) : IDrawController
|
|
||||||
{
|
|
||||||
public void EnsureBounds(Box2d bounds, float depth)
|
|
||||||
{
|
|
||||||
Queue.Bounds = Box3d.Union(Queue.Bounds, bounds, depth);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Write(IDrawCommand command)
|
|
||||||
{
|
|
||||||
Queue.Write(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Write(IDrawCommand command, ReadOnlySpan<byte> bytes)
|
|
||||||
{
|
|
||||||
Queue.Write(command, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Write<T>(IDrawCommand command, T param) where T : IParameterSerializer<T>
|
|
||||||
{
|
|
||||||
int length = param.Serialize(Queue, Span<byte>.Empty);
|
|
||||||
Span<byte> bytes = stackalloc byte[length];
|
|
||||||
|
|
||||||
param.Serialize(Queue, bytes);
|
|
||||||
Write(command, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Write<T1, T2>(T2 command, T1 param) where T2 : IDrawCommand<T1>
|
|
||||||
{
|
|
||||||
int length = command.WriteParams(Queue, param, Span<byte>.Empty);
|
|
||||||
Span<byte> bytes = stackalloc byte[length];
|
|
||||||
|
|
||||||
command.WriteParams(Queue, param, bytes);
|
|
||||||
Write(command, bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Enumerator : ICommandFrame, IEnumerator<ICommandFrame>
|
|
||||||
{
|
|
||||||
private readonly DrawQueue _queue;
|
|
||||||
private readonly byte[] _stream;
|
|
||||||
private int _length;
|
|
||||||
private int _index = -1;
|
|
||||||
private int _paramsIndex = -1;
|
|
||||||
private int _paramLength = 0;
|
|
||||||
private IDrawCommand? _current = null;
|
|
||||||
|
|
||||||
public ICommandFrame Current => this;
|
|
||||||
|
|
||||||
object? IEnumerator.Current => Current;
|
|
||||||
|
|
||||||
public IDrawCommand Command => _current ?? throw new InvalidOperationException();
|
|
||||||
|
|
||||||
public bool HasParameters { get; private set; }
|
|
||||||
|
|
||||||
public Enumerator(DrawQueue queue)
|
|
||||||
{
|
|
||||||
_queue = queue;
|
|
||||||
_stream = queue._commandStream.GetBuffer();
|
|
||||||
_length = (int)queue._commandStream.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
if (_index == -1)
|
|
||||||
_index = 0;
|
|
||||||
|
|
||||||
if (_index >= _length)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
|
|
||||||
_index += FromVlq(_stream[_index .. (_index + 5)], out int command);
|
|
||||||
_current = _queue.Command[command];
|
|
||||||
|
|
||||||
HasParameters = _current.Length != 0;
|
|
||||||
|
|
||||||
if (!HasParameters)
|
|
||||||
{
|
|
||||||
_paramsIndex = -1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int length;
|
|
||||||
if (_current.Length < 0)
|
|
||||||
{
|
|
||||||
_index += FromVlq(_stream[_index .. (_index + 5)], out length);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
length = _current.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
_paramsIndex = _index;
|
|
||||||
_paramLength = length;
|
|
||||||
_index += length;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
_index = -1;
|
|
||||||
_current = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public object? GetParameter()
|
|
||||||
{
|
|
||||||
return _current?.GetParams(_queue, _stream.AsSpan(_paramsIndex, _paramLength));
|
|
||||||
}
|
|
||||||
|
|
||||||
public T GetParameter<T>()
|
|
||||||
{
|
|
||||||
if (_current is IDrawCommand<T> command)
|
|
||||||
{
|
|
||||||
return command.GetParams(_queue, _stream.AsSpan(_paramsIndex, _paramLength))!;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetParameter<T>([NotNullWhen(true)] out T? parameter)
|
|
||||||
{
|
|
||||||
if (_current is IDrawCommand<T> command)
|
|
||||||
{
|
|
||||||
parameter = command.GetParams(_queue, _stream.AsSpan(_paramsIndex, _paramLength))!;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
parameter = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ICommandFrame
|
|
||||||
{
|
|
||||||
public IDrawCommand Command { get; }
|
|
||||||
|
|
||||||
public bool HasParameters { get; }
|
|
||||||
|
|
||||||
public object? GetParameter();
|
|
||||||
|
|
||||||
public T GetParameter<T>();
|
|
||||||
|
|
||||||
public bool TryGetParameter<T>([NotNullWhen(true)] out T? parameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public interface IDrawController
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Ensures that the canvas is at least a certain size.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="bounds">The bounding box.</param>
|
|
||||||
void EnsureBounds(Box2d bounds, float depth);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Write into the command stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="command">The command to write.</param>
|
|
||||||
void Write(IDrawCommand command);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Write into the command stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="command">The command to write.</param>
|
|
||||||
/// <param name="param">Any data associated with the command.</param>
|
|
||||||
void Write(IDrawCommand command, ReadOnlySpan<byte> param);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Write into the command stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="command">The command to write.</param>
|
|
||||||
/// <param name="param">Any data associated with the command.</param>
|
|
||||||
void Write<T>(IDrawCommand command, T param) where T : IParameterSerializer<T>;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Write into the command stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="command">The command to write.</param>
|
|
||||||
/// <param name="param">Any data associated with the command.</param>
|
|
||||||
void Write<T1, T2>(T2 command, T1 param) where T2 : IDrawCommand<T1>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using Dashboard.Windowing;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing
|
|
||||||
{
|
|
||||||
public interface IDrawQueuePaintable : IPaintable
|
|
||||||
{
|
|
||||||
DrawQueue DrawQueue { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace Dashboard.Drawing
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interface for draw resources.
|
|
||||||
/// </summary>
|
|
||||||
public interface IDrawResource
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The extension for this kind of resource.
|
|
||||||
/// </summary>
|
|
||||||
IDrawExtension Kind { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing
|
|
||||||
{
|
|
||||||
public interface IParameterSerializer<T>
|
|
||||||
{
|
|
||||||
int Serialize(DrawQueue queue, Span<byte> bytes);
|
|
||||||
void Deserialize(DrawQueue queue, ReadOnlySpan<byte> bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing
|
|
||||||
{
|
|
||||||
public class TextExtension : DrawExtension
|
|
||||||
{
|
|
||||||
public TextCommand TextCommand { get; }
|
|
||||||
|
|
||||||
private TextExtension() : base("DB_Text", new [] { BrushExtension.Instance })
|
|
||||||
{
|
|
||||||
TextCommand = new TextCommand(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly TextExtension Instance = new TextExtension();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TextCommand : IDrawCommand<TextCommandArgs>
|
|
||||||
{
|
|
||||||
public string Name { get; } = "Text";
|
|
||||||
public IDrawExtension Extension { get; }
|
|
||||||
public int Length { get; } = -1;
|
|
||||||
|
|
||||||
public TextCommand(TextExtension ext)
|
|
||||||
{
|
|
||||||
Extension = ext;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int WriteParams(DrawQueue queue, TextCommandArgs obj, Span<byte> param)
|
|
||||||
{
|
|
||||||
int size = Unsafe.SizeOf<Header>() + obj.Text.Length * sizeof(char) + sizeof(char);
|
|
||||||
|
|
||||||
if (param.Length < size)
|
|
||||||
return size;
|
|
||||||
|
|
||||||
ref Header header = ref MemoryMarshal.Cast<byte, Header>(param[0..Unsafe.SizeOf<Header>()])[0];
|
|
||||||
Span<char> text = MemoryMarshal.Cast<byte, char>(param[Unsafe.SizeOf<Header>()..]);
|
|
||||||
|
|
||||||
header = new Header()
|
|
||||||
{
|
|
||||||
// Font = queue.RequireResource(obj.Font),
|
|
||||||
TextBrush = queue.RequireResource(obj.TextBrush),
|
|
||||||
BorderBrush = (obj.BorderBrush != null) ? queue.RequireResource(obj.BorderBrush) : -1,
|
|
||||||
BorderRadius = (obj.BorderBrush != null) ? obj.BorderRadius : 0f,
|
|
||||||
Anchor = obj.Anchor,
|
|
||||||
Position = obj.Position,
|
|
||||||
BorderKind = obj.BorderKind,
|
|
||||||
};
|
|
||||||
obj.Text.CopyTo(text);
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TextCommandArgs GetParams(DrawQueue queue, ReadOnlySpan<byte> param)
|
|
||||||
{
|
|
||||||
Header header = MemoryMarshal.Cast<byte, Header>(param[0..Unsafe.SizeOf<Header>()])[0];
|
|
||||||
ReadOnlySpan<char> text = MemoryMarshal.Cast<byte, char>(param[Unsafe.SizeOf<Header>()..]);
|
|
||||||
|
|
||||||
if (header.BorderBrush != -1 && header.BorderRadius != 0)
|
|
||||||
{
|
|
||||||
return new TextCommandArgs(
|
|
||||||
(IFont)queue.Resources[header.Font],
|
|
||||||
(IBrush)queue.Resources[header.TextBrush],
|
|
||||||
header.Anchor,
|
|
||||||
header.Position,
|
|
||||||
text.ToString())
|
|
||||||
{
|
|
||||||
BorderBrush = (IBrush)queue.Resources[header.BorderBrush],
|
|
||||||
BorderRadius = header.BorderRadius,
|
|
||||||
BorderKind = header.BorderKind,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new TextCommandArgs(
|
|
||||||
(IFont)queue.Resources[header.Font],
|
|
||||||
(IBrush)queue.Resources[header.TextBrush],
|
|
||||||
header.Anchor,
|
|
||||||
header.Position,
|
|
||||||
text.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int IDrawCommand.WriteParams(DrawQueue queue, object? obj, Span<byte> param)
|
|
||||||
{
|
|
||||||
return WriteParams(queue, (TextCommandArgs)obj!, param);
|
|
||||||
}
|
|
||||||
|
|
||||||
object? IDrawCommand.GetParams(DrawQueue queue, ReadOnlySpan<byte> param)
|
|
||||||
{
|
|
||||||
return GetParams(queue, param);
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct Header
|
|
||||||
{
|
|
||||||
private int _flags;
|
|
||||||
public int Font;
|
|
||||||
public int TextBrush;
|
|
||||||
public int BorderBrush;
|
|
||||||
public Vector3 Position;
|
|
||||||
public float BorderRadius;
|
|
||||||
|
|
||||||
public Anchor Anchor
|
|
||||||
{
|
|
||||||
get => (Anchor)(_flags & 0xF);
|
|
||||||
set => _flags = (_flags & ~0xF) | (int)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BorderKind BorderKind
|
|
||||||
{
|
|
||||||
get => (_flags & INSET) switch
|
|
||||||
{
|
|
||||||
OUTSET => BorderKind.Outset,
|
|
||||||
INSET => BorderKind.Inset,
|
|
||||||
_ => BorderKind.Center,
|
|
||||||
};
|
|
||||||
set => _flags = value switch
|
|
||||||
{
|
|
||||||
BorderKind.Outset => (_flags & ~INSET) | OUTSET,
|
|
||||||
BorderKind.Inset => (_flags & ~INSET) | INSET,
|
|
||||||
_ => (_flags & ~INSET) | CENTER,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private const int INSET = 0x30;
|
|
||||||
private const int CENTER = 0x00;
|
|
||||||
private const int OUTSET = 0x10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public record struct TextCommandArgs(IFont Font, IBrush TextBrush, Anchor Anchor, Vector3 Position, string Text)
|
|
||||||
{
|
|
||||||
public IBrush? BorderBrush { get; init; } = null;
|
|
||||||
public float BorderRadius { get; init; } = 0;
|
|
||||||
|
|
||||||
public BorderKind BorderKind { get; init; } = BorderKind.Center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.IO;
|
|
||||||
using System.Reflection.PortableExecutable;
|
|
||||||
|
|
||||||
namespace Dashboard.Drawing
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interface for registered typesetters.
|
|
||||||
/// </summary>
|
|
||||||
public interface ITypeSetter
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Name of the typesetter.
|
|
||||||
/// </summary>
|
|
||||||
string Name { get; }
|
|
||||||
|
|
||||||
SizeF MeasureString(IFont font, string value);
|
|
||||||
|
|
||||||
IFont LoadFont(Stream stream);
|
|
||||||
IFont LoadFont(string path);
|
|
||||||
// IFont LoadFont(NamedFont font);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Class for typesetting related functions.
|
|
||||||
/// </summary>
|
|
||||||
public static class Typesetter
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The typesetting backend for this instance.
|
|
||||||
/// </summary>
|
|
||||||
public static ITypeSetter Backend { get; set; } = new UndefinedTypeSetter();
|
|
||||||
|
|
||||||
public static string Name => Backend.Name;
|
|
||||||
|
|
||||||
public static SizeF MeasureString(IFont font, string value)
|
|
||||||
{
|
|
||||||
return Backend.MeasureString(font, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IFont LoadFont(Stream stream)
|
|
||||||
{
|
|
||||||
return Backend.LoadFont(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IFont LoadFont(string path)
|
|
||||||
{
|
|
||||||
return Backend.LoadFont(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IFont LoadFont(FileInfo file)
|
|
||||||
{
|
|
||||||
return Backend.LoadFont(file.FullName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// public static IFont LoadFont(NamedFont font)
|
|
||||||
// {
|
|
||||||
// return Backend.LoadFont(font);
|
|
||||||
// }
|
|
||||||
|
|
||||||
public static IFont LoadFont(string family, float size, FontWeight weight = FontWeight.Normal,
|
|
||||||
FontSlant slant = FontSlant.Normal, FontStretch stretch = FontStretch.Normal)
|
|
||||||
{
|
|
||||||
// return LoadFont(new NamedFont(family, size, weight, slant, stretch));
|
|
||||||
throw new Exception();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class UndefinedTypeSetter : ITypeSetter
|
|
||||||
{
|
|
||||||
public string Name { get; } = "Undefined";
|
|
||||||
|
|
||||||
[DoesNotReturn]
|
|
||||||
private void Except()
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("No typesetting backend is loaded.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public SizeF MeasureString(IFont font, string value)
|
|
||||||
{
|
|
||||||
Except();
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IFont LoadFont(Stream stream)
|
|
||||||
{
|
|
||||||
Except();
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IFont LoadFont(string path)
|
|
||||||
{
|
|
||||||
Except();
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
// public IFont LoadFont(NamedFont font)
|
|
||||||
// {
|
|
||||||
// Except();
|
|
||||||
// return default;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<ImplicitUsings>disable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,234 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Text;
|
|
||||||
using Dashboard.Drawing;
|
|
||||||
|
|
||||||
namespace Dashboard.ImmediateUI
|
|
||||||
{
|
|
||||||
public class DimUIConfig
|
|
||||||
{
|
|
||||||
public Vector2 Margin = new Vector2(8, 4);
|
|
||||||
public Vector2 Padding = new Vector2(4);
|
|
||||||
public required IFont Font { get; init; }
|
|
||||||
|
|
||||||
public IBrush TextBrush = new SolidBrush(Color.Black);
|
|
||||||
public IBrush DisabledText = new SolidBrush(Color.Gray);
|
|
||||||
|
|
||||||
public float ButtonBorderSize = 2f;
|
|
||||||
public IBrush ButtonBorderBrush = new SolidBrush(Color.SteelBlue);
|
|
||||||
public IBrush ButtonFillBrush = new SolidBrush(Color.SlateGray);
|
|
||||||
public IBrush? ButtonShadowBrush = new SolidBrush(Color.FromArgb(32, Color.LightSteelBlue));
|
|
||||||
public float ButtonShadowOffset = 2f;
|
|
||||||
|
|
||||||
public float InputBorderSize = 2f;
|
|
||||||
public IBrush InputPlaceholderTextBrush = new SolidBrush(Color.SteelBlue);
|
|
||||||
public IBrush InputBorderBrush = new SolidBrush(Color.SlateGray);
|
|
||||||
public IBrush InputFillBrush = new SolidBrush(Color.LightGray);
|
|
||||||
public IBrush? InputShadowBrush = new SolidBrush(Color.FromArgb(32, Color.LightSteelBlue));
|
|
||||||
public float InputShadowOffset = -2f;
|
|
||||||
|
|
||||||
public float MenuBorderSize = 2f;
|
|
||||||
public IBrush MenuBorderBrush = new SolidBrush(Color.SteelBlue);
|
|
||||||
public IBrush MenuFillBrush = new SolidBrush(Color.SlateGray);
|
|
||||||
public IBrush? MenuShadowBrush = new SolidBrush(Color.FromArgb(32, Color.LightSteelBlue));
|
|
||||||
public float MenuShadowOffset = 2f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DimUI
|
|
||||||
{
|
|
||||||
private readonly DimUIConfig _config;
|
|
||||||
private Vector2 _pen;
|
|
||||||
private Box2d _bounds;
|
|
||||||
private bool _firstLine = false;
|
|
||||||
private bool _sameLine = false;
|
|
||||||
private float _z = -1;
|
|
||||||
private float _lineHeight;
|
|
||||||
private DrawQueue _queue;
|
|
||||||
|
|
||||||
public DimUI(DimUIConfig config)
|
|
||||||
{
|
|
||||||
_config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MemberNotNull(nameof(_queue))]
|
|
||||||
public void Begin(Box2d bounds, DrawQueue queue)
|
|
||||||
{
|
|
||||||
_bounds = bounds;
|
|
||||||
_pen = _bounds.Min;
|
|
||||||
_queue = queue;
|
|
||||||
_firstLine = true;
|
|
||||||
_lineHeight = 0;
|
|
||||||
_z = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SameLine()
|
|
||||||
{
|
|
||||||
_sameLine = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Line()
|
|
||||||
{
|
|
||||||
if (!_firstLine && !_sameLine)
|
|
||||||
{
|
|
||||||
_pen = new Vector2(_bounds.Left, _pen.Y + _lineHeight);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_firstLine = false;
|
|
||||||
_sameLine = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_pen.X += _config.Margin.X;
|
|
||||||
_lineHeight = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float Z()
|
|
||||||
{
|
|
||||||
return _z += 0.001f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Text(string text)
|
|
||||||
{
|
|
||||||
Line();
|
|
||||||
|
|
||||||
SizeF sz = Typesetter.MeasureString(_config.Font, text);
|
|
||||||
float z = Z();
|
|
||||||
float h = _config.Margin.Y * 2 + sz.Height;
|
|
||||||
_queue.Text(new Vector3(_pen + new Vector2(0, _config.Margin.X), z), _config.TextBrush, text, _config.Font);
|
|
||||||
|
|
||||||
_lineHeight = Math.Max(_lineHeight, h);
|
|
||||||
_pen.X += sz.Width;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DrawBox(
|
|
||||||
Vector2 position,
|
|
||||||
Vector2 size,
|
|
||||||
IBrush fill,
|
|
||||||
IBrush border, float borderWidth,
|
|
||||||
IBrush? shadow, float offset)
|
|
||||||
{
|
|
||||||
float z = Z();
|
|
||||||
|
|
||||||
if (shadow != null)
|
|
||||||
{
|
|
||||||
if (offset >= 0)
|
|
||||||
{
|
|
||||||
_queue.Rect(position + new Vector2(offset), position + size + new Vector2(offset + borderWidth), z, shadow);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Inset shadows are draw a bit weirdly.
|
|
||||||
_queue.Rect(position, position + new Vector2(offset, size.Y), z, shadow);
|
|
||||||
_queue.Rect(position + new Vector2(offset, 0), position + new Vector2(size.X - offset, offset), z, shadow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_queue.Rect(position, position + size, z, fill, border, borderWidth, BorderKind.Outset);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Button(string label)
|
|
||||||
{
|
|
||||||
Line();
|
|
||||||
|
|
||||||
SizeF sz = Typesetter.MeasureString(_config.Font, label);
|
|
||||||
|
|
||||||
float h = _config.Margin.Y * 2 + _config.Padding.Y * 2 + sz.Height;
|
|
||||||
DrawBox(
|
|
||||||
_pen + new Vector2(0, _config.Margin.Y),
|
|
||||||
new Vector2(sz.Width + 2 * _config.Padding.X, sz.Height + 2 * _config.Padding.Y),
|
|
||||||
_config.ButtonFillBrush,
|
|
||||||
_config.ButtonBorderBrush,
|
|
||||||
_config.ButtonBorderSize,
|
|
||||||
_config.ButtonShadowBrush,
|
|
||||||
_config.ButtonShadowOffset);
|
|
||||||
|
|
||||||
float z = Z();
|
|
||||||
|
|
||||||
_queue.Text(new Vector3(_pen + new Vector2(_config.Padding.X, _config.Margin.Y + _config.Padding.Y), z),
|
|
||||||
_config.TextBrush, label, _config.Font);
|
|
||||||
_lineHeight = Math.Max(_lineHeight, h);
|
|
||||||
|
|
||||||
_pen.X += sz.Width + 2 * _config.Padding.X;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Input(string placeholder, StringBuilder value)
|
|
||||||
{
|
|
||||||
Line();
|
|
||||||
|
|
||||||
IBrush textBrush;
|
|
||||||
string str;
|
|
||||||
if (value.Length == 0)
|
|
||||||
{
|
|
||||||
textBrush = _config.DisabledText;
|
|
||||||
str = placeholder;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
textBrush = _config.TextBrush;
|
|
||||||
str = value.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
SizeF sz = Typesetter.MeasureString(_config.Font, str);
|
|
||||||
|
|
||||||
float h = _config.Margin.Y * 2 + _config.Padding.Y * 2 + sz.Height;
|
|
||||||
DrawBox(
|
|
||||||
_pen + new Vector2(0, _config.Margin.Y),
|
|
||||||
new Vector2(sz.Width + 2 * _config.Padding.X, sz.Height + 2 * _config.Padding.Y),
|
|
||||||
_config.InputFillBrush,
|
|
||||||
_config.InputBorderBrush,
|
|
||||||
_config.InputBorderSize,
|
|
||||||
_config.InputShadowBrush,
|
|
||||||
_config.InputShadowOffset);
|
|
||||||
|
|
||||||
float z = Z();
|
|
||||||
|
|
||||||
_queue.Text(new Vector3(_pen + new Vector2(_config.Padding.X, _config.Margin.Y + _config.Padding.Y), z),
|
|
||||||
textBrush, str, _config.Font);
|
|
||||||
_lineHeight = Math.Max(_lineHeight, h);
|
|
||||||
|
|
||||||
_pen.X += sz.Width + 2 * _config.Padding.X;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BeginMenu()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MenuItem(string name)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EndMenu()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Id(ReadOnlySpan<char> str)
|
|
||||||
{
|
|
||||||
// Uses the FVN-1A algorithm in 32-bit mode.
|
|
||||||
const int PRIME = 0x01000193;
|
|
||||||
const int BASIS = unchecked((int)0x811c9dc5);
|
|
||||||
|
|
||||||
int hash = BASIS;
|
|
||||||
for (int i = 0; i < str.Length; i++)
|
|
||||||
{
|
|
||||||
hash ^= str[i] & 0xFF;
|
|
||||||
hash *= PRIME;
|
|
||||||
hash ^= str[i] >> 8;
|
|
||||||
hash *= PRIME;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Finish()
|
|
||||||
{
|
|
||||||
// TODO:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user