Create a new context system for dashboard.
This commit is contained in:
parent
50eda46b13
commit
edc85c3f24
@ -6,16 +6,31 @@ namespace Dashboard
|
||||
/// <summary>
|
||||
/// Pixel format for images.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum PixelFormat
|
||||
{
|
||||
R8I,
|
||||
Rg8I,
|
||||
Rgb8I,
|
||||
Rgba8I,
|
||||
R16F,
|
||||
Rg816F,
|
||||
Rgb16F,
|
||||
Rgba16F,
|
||||
None = 0,
|
||||
|
||||
R8I = R | I8,
|
||||
Rg8I = Rg | I8,
|
||||
Rgb8I = Rgb | I8,
|
||||
Rgba8I = Rgba | I8,
|
||||
R16F = R | F16,
|
||||
Rg16F = Rg | F16,
|
||||
Rgb16F = Rgb | F16,
|
||||
Rgba16F = Rgba | F16,
|
||||
|
||||
// Channels
|
||||
R = 0x01,
|
||||
Rg = 0x02,
|
||||
Rgb = 0x03,
|
||||
Rgba = 0x04,
|
||||
A = 0x05,
|
||||
ColorMask = 0x0F,
|
||||
|
||||
I8 = 0x10,
|
||||
F16 = 0x20,
|
||||
TypeMask = 0xF0,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
136
Dashboard.Common/Pal/AppContext.cs
Normal file
136
Dashboard.Common/Pal/AppContext.cs
Normal file
@ -0,0 +1,136 @@
|
||||
using Dashboard.Collections;
|
||||
using Dashboard.Windowing;
|
||||
|
||||
namespace Dashboard.Pal
|
||||
{
|
||||
public abstract class AppContext : IContextBase<AppContext, IAppContextExtension>
|
||||
{
|
||||
public abstract string DriverName { get; }
|
||||
public abstract string DriverVendor { get; }
|
||||
public abstract Version DriverVersion { get; }
|
||||
|
||||
public virtual string AppTitle { get; set; } = "Dashboard Application";
|
||||
|
||||
public bool IsInitialized { get; private set; } = false;
|
||||
public bool IsDisposed { get; private set; } = false;
|
||||
public IContextDebugger? Debugger { get; set; }
|
||||
|
||||
private readonly TypeDictionary<IAppContextExtension> _extensions =
|
||||
new TypeDictionary<IAppContextExtension>(true);
|
||||
private readonly TypeDictionary<IAppContextExtension, Func<IAppContextExtension>> _preloadedExtensions =
|
||||
new TypeDictionary<IAppContextExtension, Func<IAppContextExtension>>(true);
|
||||
|
||||
~AppContext()
|
||||
{
|
||||
InvokeDispose(false);
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
if (IsInitialized)
|
||||
return;
|
||||
|
||||
IsInitialized = true;
|
||||
|
||||
InitializeInternal();
|
||||
}
|
||||
|
||||
protected virtual void InitializeInternal()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void RunEvents(bool wait)
|
||||
{
|
||||
if (!IsInitialized)
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public void Run() => Run(true, CancellationToken.None);
|
||||
|
||||
public void Run(bool wait) => Run(wait, CancellationToken.None);
|
||||
|
||||
public void Run(bool waitForEvents, CancellationToken token)
|
||||
{
|
||||
InitializeInternal();
|
||||
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
RunEvents(waitForEvents);
|
||||
}
|
||||
}
|
||||
|
||||
#region Window API
|
||||
/// <summary>
|
||||
/// Creates a window. It could be a virtual window, or a physical window.
|
||||
/// </summary>
|
||||
/// <returns>A window.</returns>
|
||||
public abstract IWindow CreateWindow();
|
||||
|
||||
/// <summary>
|
||||
/// Always creates a physical window.
|
||||
/// </summary>
|
||||
/// <returns>A physical window.</returns>
|
||||
public abstract IPhysicalWindow CreatePhysicalWindow();
|
||||
#endregion
|
||||
|
||||
public bool IsExtensionAvailable<T>() where T : IAppContextExtension
|
||||
{
|
||||
return _extensions.Contains<T>() || _preloadedExtensions.Contains<T>();
|
||||
}
|
||||
|
||||
public bool ExtensionPreload<T>() where T : IAppContextExtension, new()
|
||||
{
|
||||
return _preloadedExtensions.Add<T>(() => new T());
|
||||
}
|
||||
|
||||
public T ExtensionRequire<T>() where T : IAppContextExtension, new()
|
||||
{
|
||||
T? extension = default;
|
||||
|
||||
if (_extensions.TryGet(out extension))
|
||||
return extension;
|
||||
|
||||
lock (_extensions)
|
||||
{
|
||||
if (_extensions.TryGet(out extension))
|
||||
return extension;
|
||||
|
||||
if (_preloadedExtensions.Remove<T>(out Func<IAppContextExtension>? loader))
|
||||
{
|
||||
extension = (T)loader!();
|
||||
}
|
||||
else
|
||||
{
|
||||
extension = new T();
|
||||
}
|
||||
|
||||
_extensions.Add(extension);
|
||||
extension.Require(this);
|
||||
}
|
||||
|
||||
return extension;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool isDisposing)
|
||||
{
|
||||
if (!isDisposing) return;
|
||||
|
||||
foreach (IAppContextExtension extension in _extensions)
|
||||
{
|
||||
extension.Dispose();
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void InvokeDispose(bool isDisposing)
|
||||
{
|
||||
if (IsDisposed) return;
|
||||
IsDisposed = true;
|
||||
|
||||
Dispose(isDisposing);
|
||||
}
|
||||
|
||||
public void Dispose() => InvokeDispose(true);
|
||||
}
|
||||
}
|
||||
98
Dashboard.Common/Pal/DeviceContext.cs
Normal file
98
Dashboard.Common/Pal/DeviceContext.cs
Normal file
@ -0,0 +1,98 @@
|
||||
using Dashboard.Collections;
|
||||
|
||||
namespace Dashboard.Pal
|
||||
{
|
||||
public abstract class DeviceContext : IContextBase<DeviceContext, IDeviceContextExtension>
|
||||
{
|
||||
private readonly TypeDictionary<IDeviceContextExtension> _extensions =
|
||||
new TypeDictionary<IDeviceContextExtension>(true);
|
||||
private readonly TypeDictionary<IDeviceContextExtension, Func<IDeviceContextExtension>> _preloadedExtensions =
|
||||
new TypeDictionary<IDeviceContextExtension, Func<IDeviceContextExtension>>(true);
|
||||
|
||||
public abstract string DriverName { get; }
|
||||
public abstract string DriverVendor { get; }
|
||||
public abstract Version DriverVersion { get; }
|
||||
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional debugging object for your pleasure.
|
||||
/// </summary>
|
||||
public IContextDebugger? Debugger { get; set; }
|
||||
|
||||
~DeviceContext()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public virtual void Begin() { }
|
||||
|
||||
// public abstract void Paint(object renderbuffer);
|
||||
|
||||
public virtual void End() { }
|
||||
|
||||
public bool IsExtensionAvailable<T>() where T : IDeviceContextExtension
|
||||
{
|
||||
return _extensions.Contains<T>() || _preloadedExtensions.Contains<T>();
|
||||
}
|
||||
|
||||
public bool ExtensionPreload<T>() where T : IDeviceContextExtension, new()
|
||||
{
|
||||
return _preloadedExtensions.Add<T>(() => new T());
|
||||
}
|
||||
|
||||
public T ExtensionRequire<T>() where T : IDeviceContextExtension, new()
|
||||
{
|
||||
T? extension = default;
|
||||
|
||||
if (_extensions.TryGet(out extension))
|
||||
return extension;
|
||||
|
||||
lock (_extensions)
|
||||
{
|
||||
if (_extensions.TryGet(out extension))
|
||||
return extension;
|
||||
|
||||
if (_preloadedExtensions.Remove<T>(out Func<IDeviceContextExtension>? loader))
|
||||
{
|
||||
extension = (T)loader!();
|
||||
}
|
||||
else
|
||||
{
|
||||
extension = new T();
|
||||
}
|
||||
|
||||
_extensions.Add(extension);
|
||||
extension.Require(this);
|
||||
}
|
||||
|
||||
return extension;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implement your dispose in this function.
|
||||
/// </summary>
|
||||
/// <param name="isDisposing">True if disposing, false otherwise.</param>
|
||||
protected virtual void Dispose(bool isDisposing)
|
||||
{
|
||||
if (!isDisposing) return;
|
||||
|
||||
foreach (IDeviceContextExtension extension in _extensions)
|
||||
{
|
||||
extension.Dispose();
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void InvokeDispose(bool isDisposing)
|
||||
{
|
||||
if (IsDisposed) return;
|
||||
IsDisposed = true;
|
||||
|
||||
Dispose(isDisposing);
|
||||
}
|
||||
|
||||
public void Dispose() => InvokeDispose(true);
|
||||
}
|
||||
}
|
||||
7
Dashboard.Common/Pal/IAppContextExtension.cs
Normal file
7
Dashboard.Common/Pal/IAppContextExtension.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Dashboard.Pal
|
||||
{
|
||||
public interface IAppContextExtension : IContextExtensionBase<AppContext>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
103
Dashboard.Common/Pal/IContextBase.cs
Normal file
103
Dashboard.Common/Pal/IContextBase.cs
Normal file
@ -0,0 +1,103 @@
|
||||
namespace Dashboard.Pal
|
||||
{
|
||||
/// <summary>
|
||||
/// Information about this context interface.
|
||||
/// </summary>
|
||||
public interface IContextInterfaceInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of this driver.
|
||||
/// </summary>
|
||||
string DriverName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The vendor for this driver.
|
||||
/// </summary>
|
||||
string DriverVendor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The version of this driver.
|
||||
/// </summary>
|
||||
Version DriverVersion { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The base context interface.
|
||||
/// </summary>
|
||||
public interface IContextBase : IContextInterfaceInfo, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The debugger for this context.
|
||||
/// </summary>
|
||||
IContextDebugger? Debugger { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The base context interface.
|
||||
/// </summary>
|
||||
/// <typeparam name="TContext">The context type.</typeparam>
|
||||
/// <typeparam name="TExtension">The extension type, if used.</typeparam>
|
||||
public interface IContextBase<TContext, in TExtension> : IContextBase
|
||||
where TContext : IContextBase<TContext, TExtension>
|
||||
where TExtension : IContextExtensionBase<TContext>
|
||||
{
|
||||
/// <summary>
|
||||
/// Is such an extension available?
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The extension to check.</typeparam>
|
||||
/// <returns>True if the extension is available.</returns>
|
||||
bool IsExtensionAvailable<T>() where T : TExtension;
|
||||
|
||||
/// <summary>
|
||||
/// Preload extensions, to be lazy loaded when required.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The extension to preload.</typeparam>
|
||||
/// <returns>
|
||||
/// True if the extension was added to the preload set. Otherwise, already loaded or another extension
|
||||
/// exists which provides this.
|
||||
/// </returns>
|
||||
bool ExtensionPreload<T>() where T : TExtension, new();
|
||||
|
||||
/// <summary>
|
||||
/// Require an extension.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The extension to require.</typeparam>
|
||||
/// <returns>The extension instance.</returns>
|
||||
T ExtensionRequire<T>() where T : TExtension, new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base interface for all context extensions.
|
||||
/// </summary>
|
||||
public interface IContextExtensionBase : IContextInterfaceInfo, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The context that loaded this extension.
|
||||
/// </summary>
|
||||
IContextBase Context { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Require this extension.
|
||||
/// </summary>
|
||||
/// <param name="context">The context that required this extension.</param>
|
||||
void Require(IContextBase context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base interface for all context extensions.
|
||||
/// </summary>
|
||||
public interface IContextExtensionBase<TContext> : IContextExtensionBase
|
||||
where TContext : IContextBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The context that loaded this extension.
|
||||
/// </summary>
|
||||
new TContext Context { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Require this extension.
|
||||
/// </summary>
|
||||
/// <param name="context">The context that required this extension.</param>
|
||||
void Require(TContext context);
|
||||
}
|
||||
}
|
||||
10
Dashboard.Common/Pal/IContextDebugger.cs
Normal file
10
Dashboard.Common/Pal/IContextDebugger.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Dashboard.Pal
|
||||
{
|
||||
public interface IContextDebugger : IDisposable
|
||||
{
|
||||
void LogDebug(string message);
|
||||
void LogInfo(string message);
|
||||
void LogWarning(string message);
|
||||
void LogError(string message);
|
||||
}
|
||||
}
|
||||
6
Dashboard.Common/Pal/IDeviceContextExtension.cs
Normal file
6
Dashboard.Common/Pal/IDeviceContextExtension.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace Dashboard.Pal
|
||||
{
|
||||
public interface IDeviceContextExtension : IContextExtensionBase<DeviceContext>
|
||||
{
|
||||
}
|
||||
}
|
||||
83
Dashboard.Common/Pal/ITextureExtension.cs
Normal file
83
Dashboard.Common/Pal/ITextureExtension.cs
Normal file
@ -0,0 +1,83 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace Dashboard.Pal
|
||||
{
|
||||
public interface ITextureExtension : IDeviceContextExtension
|
||||
{
|
||||
ITexture CreateTexture(TextureType type);
|
||||
}
|
||||
|
||||
public enum TextureType
|
||||
{
|
||||
Texture1D,
|
||||
Texture2D,
|
||||
Texture2DArray,
|
||||
Texture2DCube,
|
||||
Texture3D,
|
||||
}
|
||||
|
||||
public enum TextureFilter
|
||||
{
|
||||
Nearest,
|
||||
Linear,
|
||||
NearestMipmapNearest,
|
||||
LinearMipmapNearest,
|
||||
NearestMipmapLinear,
|
||||
LinearMipmapLinear,
|
||||
Anisotropic,
|
||||
}
|
||||
|
||||
public enum TextureRepeat
|
||||
{
|
||||
Repeat,
|
||||
MirroredRepeat,
|
||||
ClampToEdge,
|
||||
ClampToBorder,
|
||||
MirrorClampToEdge,
|
||||
}
|
||||
|
||||
public enum CubeMapFace
|
||||
{
|
||||
PositiveX,
|
||||
PositiveY,
|
||||
PositiveZ,
|
||||
NegativeX,
|
||||
NegativeY,
|
||||
NegativeZ,
|
||||
}
|
||||
|
||||
public interface ITexture : IDisposable
|
||||
{
|
||||
public TextureType Type { get; }
|
||||
public PixelFormat Format { get; }
|
||||
|
||||
public int Width { get; }
|
||||
public int Height { get; }
|
||||
public int Depth { get; }
|
||||
public int Levels { get; }
|
||||
|
||||
public bool Premultiplied { get; set; }
|
||||
|
||||
public ColorSwizzle Swizzle { get; set; }
|
||||
|
||||
public TextureFilter MinifyFilter { get; set; }
|
||||
|
||||
public TextureFilter MagnifyFilter { get; set; }
|
||||
|
||||
public Color BorderColor { get; set; }
|
||||
|
||||
public TextureRepeat RepeatS { get; set; }
|
||||
|
||||
public TextureRepeat RepeatT { get; set; }
|
||||
|
||||
public TextureRepeat RepeatR { get; set; }
|
||||
public int Anisotropy { get; set; }
|
||||
|
||||
void SetStorage(PixelFormat format, int width, int height, int depth, int levels);
|
||||
void Read<T>(Span<T> buffer, int level = 0, int align = 0) where T : unmanaged;
|
||||
void Write<T>(PixelFormat format, ReadOnlySpan<T> buffer, int level = 0, int align = 4) where T : unmanaged;
|
||||
void Premultiply();
|
||||
void Unmultiply();
|
||||
void GenerateMipmaps();
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
using System.Drawing;
|
||||
using Dashboard.Drawing.OpenGL.Executors;
|
||||
using Dashboard.OpenGL;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace Dashboard.Drawing.OpenGL
|
||||
{
|
||||
@ -130,10 +131,13 @@ namespace Dashboard.Drawing.OpenGL
|
||||
|
||||
public void Draw(DrawQueue drawqueue) => Draw(drawqueue, new RectangleF(new PointF(0f,0f), Context.FramebufferSize));
|
||||
|
||||
public virtual void Draw(DrawQueue drawQueue, RectangleF bounds)
|
||||
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))
|
||||
|
||||
160
Dashboard.Drawing.OpenGL/Pal/GLDeviceContext.cs
Normal file
160
Dashboard.Drawing.OpenGL/Pal/GLDeviceContext.cs
Normal file
@ -0,0 +1,160 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Immutable;
|
||||
using Dashboard.OpenGL;
|
||||
using Dashboard.Pal;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
|
||||
namespace Dashboard.Drawing.OpenGL.Pal
|
||||
{
|
||||
internal class GLContextBindingsContext(IGLContext context) : IBindingsContext
|
||||
{
|
||||
public IntPtr GetProcAddress(string procName)
|
||||
{
|
||||
return context.GetProcAddress(procName);
|
||||
}
|
||||
}
|
||||
|
||||
public class GLDeviceContext : DeviceContext
|
||||
{
|
||||
public IGLContext GLContext { get; }
|
||||
|
||||
public override string DriverName => "Dashboard OpenGL Device Context";
|
||||
public override string DriverVendor => "Dashboard";
|
||||
public override Version DriverVersion => new Version(0, 1, 0);
|
||||
|
||||
public Version GLVersion { get; }
|
||||
public string GLRenderer { get; }
|
||||
public string GLVendor { get; }
|
||||
public ImmutableHashSet<string> Extensions { get; }
|
||||
|
||||
public Thread RendererThread { get; } = Thread.CurrentThread;
|
||||
public bool IsRenderThread => RendererThread == Thread.CurrentThread;
|
||||
|
||||
private readonly ConcurrentQueue<Task> _beforeDrawActions = new ConcurrentQueue<Task>();
|
||||
private readonly ConcurrentQueue<Task> _afterDrawActions = new ConcurrentQueue<Task>();
|
||||
|
||||
public GLDeviceContext(IGLContext context)
|
||||
{
|
||||
GLContext = context;
|
||||
context.MakeCurrent();
|
||||
GLLoader.LoadBindings(new GLContextBindingsContext(context));
|
||||
|
||||
context.Disposed += Dispose;
|
||||
|
||||
GL.GetInteger(GetPName.MajorVersion, out int major);
|
||||
GL.GetInteger(GetPName.MinorVersion, out int minor);
|
||||
GLVersion = new Version(major, minor);
|
||||
|
||||
GLRenderer = GL.GetString(StringName.Renderer) ?? string.Empty;
|
||||
GLVendor = GL.GetString(StringName.Vendor) ?? string.Empty;
|
||||
|
||||
HashSet<string> extensions = new HashSet<string>();
|
||||
GL.GetInteger(GetPName.NumExtensions, out int extensionCount);
|
||||
for (uint i = 0; i < extensionCount; i++)
|
||||
{
|
||||
string? ext = GL.GetStringi(StringName.Extensions, i);
|
||||
if (ext != null)
|
||||
extensions.Add(ext);
|
||||
}
|
||||
|
||||
Extensions = extensions.ToImmutableHashSet();
|
||||
|
||||
ExtensionPreload<GLTextureExtension>();
|
||||
}
|
||||
|
||||
public bool IsGLExtensionAvailable(string name)
|
||||
{
|
||||
return Extensions.Contains(name);
|
||||
}
|
||||
|
||||
public void AssertGLExtension(string name)
|
||||
{
|
||||
if (IsGLExtensionAvailable(name))
|
||||
return;
|
||||
|
||||
throw new NotSupportedException($"The OpenGL extension \"{name}\" is not supported by this context.");
|
||||
}
|
||||
|
||||
public void InvokeBeforeDraw(Task task) => _beforeDrawActions.Enqueue(task);
|
||||
|
||||
public void InvokeAfterDraw(Task task) => _afterDrawActions.Enqueue(task);
|
||||
|
||||
public Task InvokeBeforeDraw(Action action)
|
||||
{
|
||||
Task task = new Task(action);
|
||||
_beforeDrawActions.Enqueue(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
public Task InvokeAfterDraw(Action action)
|
||||
{
|
||||
Task task = new Task(action);
|
||||
_afterDrawActions.Enqueue(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
public Task<T> InvokeBeforeDraw<T>(Func<T> function)
|
||||
{
|
||||
Task<T> task = new Task<T>(function);
|
||||
_beforeDrawActions.Enqueue(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
public Task<T> InvokeAfterDraw<T>(Func<T> function)
|
||||
{
|
||||
Task<T> task = new Task<T>(function);
|
||||
_afterDrawActions.Enqueue(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
public Task InvokeOnRenderThread(Action action)
|
||||
{
|
||||
if (IsRenderThread)
|
||||
{
|
||||
action();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
return InvokeBeforeDraw(action);
|
||||
}
|
||||
|
||||
public Task<T> InvokeOnRenderThread<T>(Func<T> function)
|
||||
{
|
||||
return IsRenderThread ? Task.FromResult(function()) : InvokeBeforeDraw(function);
|
||||
}
|
||||
|
||||
public override void Begin()
|
||||
{
|
||||
base.Begin();
|
||||
|
||||
GLContext.MakeCurrent();
|
||||
|
||||
while (_beforeDrawActions.TryDequeue(out Task? action))
|
||||
{
|
||||
action.RunSynchronously();
|
||||
}
|
||||
}
|
||||
|
||||
public override void End()
|
||||
{
|
||||
base.End();
|
||||
|
||||
while (_afterDrawActions.TryDequeue(out Task? action))
|
||||
{
|
||||
action.RunSynchronously();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (isDisposing)
|
||||
{
|
||||
GLContext.Disposed -= Dispose;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
257
Dashboard.Drawing.OpenGL/Pal/GLTextureExtension.cs
Normal file
257
Dashboard.Drawing.OpenGL/Pal/GLTextureExtension.cs
Normal file
@ -0,0 +1,257 @@
|
||||
using System.Drawing;
|
||||
using Dashboard.Pal;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using OGL = OpenTK.Graphics.OpenGL;
|
||||
|
||||
namespace Dashboard.Drawing.OpenGL.Pal
|
||||
{
|
||||
public class GLTextureExtension : ITextureExtension, IContextExtensionBase<GLDeviceContext>
|
||||
{
|
||||
public string DriverName => "Dashboard OpenGL Texture Extension";
|
||||
public string DriverVendor => "Dashboard";
|
||||
public Version DriverVersion => new Version(0, 1, 0);
|
||||
public GLDeviceContext Context { get; private set; } = null!;
|
||||
public bool SupportsArbTextureStorage { get; private set; }
|
||||
public bool SupportsAnisotropy { get; private set; }
|
||||
|
||||
IContextBase IContextExtensionBase.Context => Context;
|
||||
DeviceContext IContextExtensionBase<DeviceContext>.Context => Context;
|
||||
|
||||
private List<GLTexture> _textures = new List<GLTexture>();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
public void Require(GLDeviceContext context)
|
||||
{
|
||||
Context = context;
|
||||
|
||||
SupportsArbTextureStorage = Context.DriverVersion >= new Version(4, 2) ||
|
||||
Context.IsGLExtensionAvailable("GL_ARB_texture_storage");
|
||||
SupportsAnisotropy = Context.DriverVersion >= new Version() ||
|
||||
Context.IsGLExtensionAvailable("GL_EXT_texture_filter_anisotropic") ||
|
||||
Context.IsGLExtensionAvailable("GL_ARB_texture_filter_anisotropic");
|
||||
}
|
||||
public void Require(DeviceContext context) => Require((GLDeviceContext)context);
|
||||
public void Require(IContextBase context) => Require((GLDeviceContext)context);
|
||||
|
||||
|
||||
|
||||
public GLTexture CreateTexture(TextureType type)
|
||||
{
|
||||
GLTexture texture = new GLTexture(this, type);
|
||||
lock (_textures) _textures.Add(texture);
|
||||
return texture;
|
||||
}
|
||||
|
||||
internal void TextureDisposed(GLTexture texture)
|
||||
{
|
||||
lock (_textures) _textures.Remove(texture);
|
||||
}
|
||||
|
||||
ITexture ITextureExtension.CreateTexture(TextureType type) => CreateTexture(type);
|
||||
}
|
||||
|
||||
public class GLTexture(GLTextureExtension extension, TextureType type) : ITexture
|
||||
{
|
||||
public int Handle { get; private set; } = 0;
|
||||
public bool IsValid => Handle != 0;
|
||||
|
||||
public TextureType Type { get; } = type;
|
||||
public PixelFormat Format { get; private set; } = PixelFormat.Rgba8I;
|
||||
public ColorSwizzle Swizzle { get; set; } = ColorSwizzle.Default;
|
||||
public TextureFilter MinifyFilter { get; set; } = TextureFilter.Linear;
|
||||
public TextureFilter MagnifyFilter { get; set; } = TextureFilter.Linear;
|
||||
public Color BorderColor { get; set; } = Color.White;
|
||||
public TextureRepeat RepeatS { get; set; } = TextureRepeat.Repeat;
|
||||
public TextureRepeat RepeatT { get; set; } = TextureRepeat.Repeat;
|
||||
public TextureRepeat RepeatR { get; set; } = TextureRepeat.Repeat;
|
||||
public int Anisotropy { get; set; } = 0;
|
||||
|
||||
public int Width { get; private set; } = 0;
|
||||
public int Height { get; private set; } = 0;
|
||||
public int Depth { get; private set; } = 0;
|
||||
public int Levels { get; private set; } = 0;
|
||||
public bool Premultiplied { get; set; } = false;
|
||||
|
||||
private TextureTarget Target { get; } = type switch
|
||||
{
|
||||
TextureType.Texture1D => TextureTarget.Texture1d,
|
||||
TextureType.Texture2D => TextureTarget.Texture2d,
|
||||
TextureType.Texture3D => TextureTarget.Texture3d,
|
||||
TextureType.Texture2DArray => TextureTarget.Texture2dArray,
|
||||
TextureType.Texture2DCube => TextureTarget.TextureCubeMap,
|
||||
_ => throw new NotSupportedException()
|
||||
};
|
||||
|
||||
private GLTextureExtension Extension { get; } = extension;
|
||||
private GLDeviceContext Context => Extension.Context;
|
||||
|
||||
public void SetStorage(PixelFormat format, int width, int height, int depth, int levels)
|
||||
{
|
||||
if (!Context.IsRenderThread)
|
||||
{
|
||||
Context.InvokeBeforeDraw(() => SetStorage(format, width, height, depth, levels)).Wait();
|
||||
return;
|
||||
}
|
||||
|
||||
Bind();
|
||||
SizedInternalFormat glFormat = GetFormat(format);
|
||||
if (Extension.SupportsArbTextureStorage)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case TextureType.Texture1D:
|
||||
GL.TexStorage1D(Target, levels, glFormat, width);
|
||||
break;
|
||||
case TextureType.Texture2D:
|
||||
GL.TexStorage2D(Target, levels, glFormat, width, height);
|
||||
break;
|
||||
case TextureType.Texture3D:
|
||||
case TextureType.Texture2DArray:
|
||||
case TextureType.Texture2DCube:
|
||||
GL.TexStorage3D(Target, levels, glFormat, width, height, depth);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case TextureType.Texture1D:
|
||||
GL.TexImage1D(Target, 0, (InternalFormat)glFormat, width, 0, (OGL.PixelFormat)glFormat, PixelType.Byte, IntPtr.Zero);
|
||||
break;
|
||||
case TextureType.Texture2D:
|
||||
GL.TexImage2D(Target, 0, (InternalFormat)glFormat, width, height, 0, (OGL.PixelFormat)glFormat, PixelType.Byte, IntPtr.Zero);
|
||||
break;
|
||||
case TextureType.Texture3D:
|
||||
case TextureType.Texture2DArray:
|
||||
case TextureType.Texture2DCube:
|
||||
GL.TexImage3D(Target, 0, (InternalFormat)glFormat, width, height, depth, 0, (OGL.PixelFormat)glFormat, PixelType.Byte, IntPtr.Zero);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Read<T>(Span<T> buffer, int level = 0, int align = 0) where T : unmanaged
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public unsafe void Write<T>(PixelFormat format, ReadOnlySpan<T> buffer, int level = 0, int align = 4) where T : unmanaged
|
||||
{
|
||||
if (!Context.IsRenderThread)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Bind();
|
||||
OGL::PixelFormat glFormat = format switch
|
||||
{
|
||||
PixelFormat.R8I or PixelFormat.R16F => OGL.PixelFormat.Red,
|
||||
PixelFormat.Rg8I or PixelFormat.Rg16F => OGL.PixelFormat.Rg,
|
||||
PixelFormat.Rgb8I or PixelFormat.Rgb16F => OGL.PixelFormat.Rgb,
|
||||
PixelFormat.Rgba8I or PixelFormat.Rgba16F => OGL.PixelFormat.Rgba,
|
||||
_ => throw new NotSupportedException()
|
||||
};
|
||||
|
||||
PixelType glType = format switch
|
||||
{
|
||||
PixelFormat.R8I or PixelFormat.Rg8I or PixelFormat.Rgb8I or PixelFormat.Rgba8I => PixelType.Byte,
|
||||
PixelFormat.R16F or PixelFormat.Rg16F or PixelFormat.Rgb16F or PixelFormat.Rgba16F => PixelType.HalfFloat,
|
||||
_ => throw new NotSupportedException()
|
||||
};
|
||||
|
||||
GL.PixelStorei(PixelStoreParameter.UnpackAlignment, align);
|
||||
fixed (T* ptr = buffer)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case TextureType.Texture1D:
|
||||
GL.TexSubImage1D(Target, level, 0, Width, glFormat, glType, ptr);
|
||||
break;
|
||||
case TextureType.Texture2D:
|
||||
GL.TexSubImage2D(Target, level, 0, 0, Width, Height, glFormat, glType, ptr);
|
||||
break;
|
||||
case TextureType.Texture2DCube:
|
||||
case TextureType.Texture3D:
|
||||
case TextureType.Texture2DArray:
|
||||
GL.TexSubImage3D(Target, level, 0, 0, 0, Width, Height, Depth, glFormat, glType, ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Premultiply()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Unmultiply()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void GenerateMipmaps()
|
||||
{
|
||||
if (!Context.IsRenderThread)
|
||||
{
|
||||
Context.InvokeBeforeDraw(GenerateMipmaps).Wait();
|
||||
return;
|
||||
}
|
||||
|
||||
Bind();
|
||||
GL.GenerateMipmap(Target);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void Bind()
|
||||
{
|
||||
if (Handle == 0)
|
||||
{
|
||||
Handle = GL.GenTexture();
|
||||
}
|
||||
|
||||
GL.BindTexture(Target, Handle);
|
||||
}
|
||||
|
||||
private static SizedInternalFormat GetFormat(PixelFormat format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
PixelFormat.R8I => SizedInternalFormat.R8,
|
||||
PixelFormat.R16F => SizedInternalFormat.R16f,
|
||||
PixelFormat.Rg8I => SizedInternalFormat.Rg8,
|
||||
PixelFormat.Rg16F => SizedInternalFormat.Rg16f,
|
||||
PixelFormat.Rgb8I => SizedInternalFormat.Rgb8,
|
||||
PixelFormat.Rgb16F => SizedInternalFormat.Rgb16f,
|
||||
PixelFormat.Rgba8I => SizedInternalFormat.Rgba8,
|
||||
PixelFormat.Rgba16F => SizedInternalFormat.Rgba16f,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
private static PixelFormat GetFormat(SizedInternalFormat format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
SizedInternalFormat.R8 => PixelFormat.R8I,
|
||||
SizedInternalFormat.R16f => PixelFormat.R16F,
|
||||
SizedInternalFormat.Rg8 => PixelFormat.Rg8I,
|
||||
SizedInternalFormat.Rg16f => PixelFormat.Rg16F,
|
||||
SizedInternalFormat.Rgb8 => PixelFormat.Rgb8I,
|
||||
SizedInternalFormat.Rgb16f => PixelFormat.Rgb16F,
|
||||
SizedInternalFormat.Rgba8 => PixelFormat.Rgba8I,
|
||||
SizedInternalFormat.Rgba16f => PixelFormat.Rgba16F,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -28,5 +28,7 @@ namespace Dashboard.OpenGL
|
||||
/// Activate this OpenGL Context.
|
||||
/// </summary>
|
||||
void MakeCurrent();
|
||||
|
||||
IntPtr GetProcAddress(string procName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,6 +40,11 @@ namespace Dashboard.OpenTK.PAL2
|
||||
TK.OpenGL.SetCurrentContext(ContextHandle);
|
||||
}
|
||||
|
||||
public IntPtr GetProcAddress(string procName)
|
||||
{
|
||||
return TK.OpenGL.GetProcedureAddress(ContextHandle, procName);
|
||||
}
|
||||
|
||||
private bool _isDisposed = false;
|
||||
|
||||
public void Dispose() => Dispose(true);
|
||||
|
||||
56
Dashboard.OpenTK/PAL2/Pal2AppContext.cs
Normal file
56
Dashboard.OpenTK/PAL2/Pal2AppContext.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using Dashboard.Windowing;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Platform;
|
||||
using AppContext = Dashboard.Pal.AppContext;
|
||||
using TK = OpenTK.Platform.Toolkit;
|
||||
|
||||
namespace Dashboard.OpenTK.PAL2
|
||||
{
|
||||
public class Pal2AppContext : AppContext
|
||||
{
|
||||
public override string DriverName => "Dashboard OpenTK PAL2.0 Driver";
|
||||
public override string DriverVendor => "Dashboard";
|
||||
public override Version DriverVersion => new Version(0, 1);
|
||||
public GraphicsApiHints GraphicsApiHints { get; set; } = new OpenGLGraphicsApiHints();
|
||||
public bool OpenGLBindingsInitialized { get; private set; } = false;
|
||||
|
||||
private readonly List<PhysicalWindow> _windows = new List<PhysicalWindow>();
|
||||
|
||||
public override IPhysicalWindow CreatePhysicalWindow()
|
||||
{
|
||||
PhysicalWindow window = new PhysicalWindow(GraphicsApiHints);
|
||||
_windows.Add(window);
|
||||
|
||||
if (!OpenGLBindingsInitialized)
|
||||
{
|
||||
OpenGLBindingsInitialized = true;
|
||||
GLLoader.LoadBindings(
|
||||
new Pal2BindingsContext(TK.OpenGL,
|
||||
((OpenGLDeviceContext)window.DeviceContext).ContextHandle));
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
public override IWindow CreateWindow()
|
||||
{
|
||||
return CreatePhysicalWindow();
|
||||
}
|
||||
|
||||
public override void RunEvents(bool wait)
|
||||
{
|
||||
TK.Window.ProcessEvents(wait);
|
||||
|
||||
for (int i = 0; i < _windows.Count; i++)
|
||||
{
|
||||
if (_windows[i].IsDisposed)
|
||||
{
|
||||
_windows.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
_windows[i].Paint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
using Dashboard.Windowing;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Platform;
|
||||
using TK = OpenTK.Platform.Toolkit;
|
||||
|
||||
namespace Dashboard.OpenTK.PAL2
|
||||
{
|
||||
public class Pal2DashboardBackend : IDashboardBackend
|
||||
{
|
||||
public GraphicsApiHints GraphicsApiHints { get; set; } = new OpenGLGraphicsApiHints();
|
||||
public bool OpenGLBindingsInitialized { get; set; } = false;
|
||||
|
||||
public IPhysicalWindow CreatePhysicalWindow()
|
||||
{
|
||||
PhysicalWindow window = new PhysicalWindow(GraphicsApiHints);
|
||||
|
||||
if (!OpenGLBindingsInitialized)
|
||||
{
|
||||
OpenGLBindingsInitialized = true;
|
||||
GLLoader.LoadBindings(
|
||||
new Pal2BindingsContext(TK.OpenGL,
|
||||
((OpenGLDeviceContext)window.DeviceContext).ContextHandle));
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
public virtual IWindow CreateWindow()
|
||||
{
|
||||
return CreatePhysicalWindow();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
public void Leave()
|
||||
{
|
||||
}
|
||||
|
||||
public void RunEvents(bool wait)
|
||||
{
|
||||
TK.Window.ProcessEvents(wait);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,7 +10,7 @@ using TK = OpenTK.Platform.Toolkit;
|
||||
|
||||
namespace Dashboard.OpenTK.PAL2
|
||||
{
|
||||
public class PhysicalWindow : IPhysicalWindow, IDrawQueuePaintable, IEventListener
|
||||
public class PhysicalWindow : IPhysicalWindow, IDrawQueuePaintable, IEventListener, IDpiAwareWindow
|
||||
{
|
||||
public DrawQueue DrawQueue { get; } = new DrawQueue();
|
||||
public WindowHandle WindowHandle { get; }
|
||||
@ -86,11 +86,11 @@ namespace Dashboard.OpenTK.PAL2
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isDisposed = false;
|
||||
public bool IsDisposed { get; private set; } = false;
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
_isDisposed = true;
|
||||
if (IsDisposed) return;
|
||||
IsDisposed = true;
|
||||
|
||||
RemoveWindow(this);
|
||||
|
||||
@ -160,5 +160,16 @@ namespace Dashboard.OpenTK.PAL2
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public float Dpi => Scale * 96f;
|
||||
|
||||
public float Scale
|
||||
{
|
||||
get
|
||||
{
|
||||
TK.Window.GetScaleFactor(WindowHandle, out float x, out float y);
|
||||
return Math.Max(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,131 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Dashboard.Windowing;
|
||||
|
||||
namespace Dashboard
|
||||
{
|
||||
public class Application : IDisposable
|
||||
{
|
||||
public IDashboardBackend Backend { get; }
|
||||
public IWindowFactory WindowFactory { get; set; }
|
||||
|
||||
public string Name { get; } = "Dashboard Application";
|
||||
|
||||
protected bool IsInitialized { get; private set; } = false;
|
||||
protected bool IsDisposed { get; private set; } = false;
|
||||
|
||||
private readonly List<IPhysicalWindow> _physicalWindows = new List<IPhysicalWindow>();
|
||||
|
||||
public event EventHandler? PreInitializing;
|
||||
public event EventHandler? Initializing;
|
||||
public event EventHandler? PostInitializing;
|
||||
public event EventHandler? Leaving;
|
||||
|
||||
public Application(IDashboardBackend backend)
|
||||
{
|
||||
Backend = backend;
|
||||
WindowFactory = backend;
|
||||
}
|
||||
|
||||
protected virtual void PreInitialize()
|
||||
{
|
||||
PreInitializing?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public virtual void Initialize()
|
||||
{
|
||||
Backend.Initialize();
|
||||
Initializing?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected virtual void PostInitialize()
|
||||
{
|
||||
PostInitializing?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void InitializeInternal()
|
||||
{
|
||||
if (IsInitialized)
|
||||
return;
|
||||
|
||||
IsInitialized = true;
|
||||
|
||||
PreInitialize();
|
||||
Initialize();
|
||||
PostInitialize();
|
||||
}
|
||||
|
||||
public virtual void RunEvents(bool wait)
|
||||
{
|
||||
if (!IsInitialized)
|
||||
throw new InvalidOperationException("The application is not initialized. Cannot run events at this time.");
|
||||
|
||||
Backend.RunEvents(wait);
|
||||
}
|
||||
|
||||
public virtual void Leave()
|
||||
{
|
||||
Backend.Leave();
|
||||
Leaving?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void Run() => Run(true, CancellationToken.None);
|
||||
public void Run(bool wait) => Run(wait, CancellationToken.None);
|
||||
|
||||
public void Run(bool waitForEvents, CancellationToken token)
|
||||
{
|
||||
InitializeInternal();
|
||||
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
RunEvents(waitForEvents);
|
||||
|
||||
foreach (IPhysicalWindow window in _physicalWindows)
|
||||
{
|
||||
window.Paint();
|
||||
}
|
||||
}
|
||||
|
||||
Leave();
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IWindowFactory.CreateWindow"/>
|
||||
public IWindow CreateWindow()
|
||||
{
|
||||
IWindow window = WindowFactory.CreateWindow();
|
||||
|
||||
if (window is IPhysicalWindow physical)
|
||||
_physicalWindows.Add(physical);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IWindowFactory.CreatePhysicalWindow"/>
|
||||
public IPhysicalWindow CreatePhysicalWindow()
|
||||
{
|
||||
IPhysicalWindow window = WindowFactory.CreatePhysicalWindow();
|
||||
_physicalWindows.Add(window);
|
||||
return window;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
}
|
||||
|
||||
protected void InvokeDispose(bool disposing)
|
||||
{
|
||||
if (IsDisposed)
|
||||
return;
|
||||
|
||||
IsDisposed = true;
|
||||
|
||||
Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public void Dispose() => InvokeDispose(true);
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using Dashboard.Windowing;
|
||||
|
||||
namespace Dashboard
|
||||
{
|
||||
public interface IWindowFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a window. It could be a virtual window, or a physical window.
|
||||
/// </summary>
|
||||
/// <returns>A window.</returns>
|
||||
IWindow CreateWindow();
|
||||
|
||||
/// <summary>
|
||||
/// Always creates a physical window.
|
||||
/// </summary>
|
||||
/// <returns>A physical window.</returns>
|
||||
IPhysicalWindow CreatePhysicalWindow();
|
||||
}
|
||||
|
||||
public interface IDashboardBackend : IDisposable, IWindowFactory
|
||||
{
|
||||
void Initialize();
|
||||
|
||||
void RunEvents(bool wait);
|
||||
|
||||
void Leave();
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,9 @@ using OpenTK.Mathematics;
|
||||
using Box2d = Dashboard.Box2d;
|
||||
using TK = OpenTK.Platform.Toolkit;
|
||||
using Dashboard;
|
||||
using Dashboard.Windowing;
|
||||
using OpenTK.Windowing.GraphicsLibraryFramework;
|
||||
using AppContext = Dashboard.Pal.AppContext;
|
||||
|
||||
TK.Init(new ToolkitOptions()
|
||||
{
|
||||
@ -18,10 +21,11 @@ TK.Init(new ToolkitOptions()
|
||||
{
|
||||
EnableVisualStyles = true,
|
||||
IsDPIAware = true,
|
||||
}
|
||||
},
|
||||
FeatureFlags = ToolkitFlags.EnableOpenGL,
|
||||
});
|
||||
|
||||
Application app = new Application(new Pal2DashboardBackend()
|
||||
AppContext app = new Pal2AppContext()
|
||||
{
|
||||
GraphicsApiHints = new OpenGLGraphicsApiHints()
|
||||
{
|
||||
@ -41,7 +45,7 @@ Application app = new Application(new Pal2DashboardBackend()
|
||||
|
||||
SupportTransparentFramebufferX11 = true,
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
PhysicalWindow window;
|
||||
SolidBrush fg = new SolidBrush(Color.FromArgb(0, 0, 0, 0));
|
||||
@ -56,7 +60,8 @@ List<Vector3> points = new List<Vector3>();
|
||||
IFont font;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
app.PostInitializing += (sender, ea) => {
|
||||
app.Initialize();
|
||||
|
||||
window = (PhysicalWindow)app.CreatePhysicalWindow();
|
||||
|
||||
window.Title = "DashTerm";
|
||||
@ -100,10 +105,10 @@ app.PostInitializing += (sender, ea) => {
|
||||
font = Typesetter.LoadFont("Nimbus Mono", 12f);
|
||||
|
||||
window.Painting += (sender, ea) => {
|
||||
TK.Window.GetFramebufferSize(context.WindowHandle, out Vector2i framebufferSize);
|
||||
TK.Window.GetSize(context.WindowHandle, out Vector2i size);
|
||||
executor.BeginFrame();
|
||||
|
||||
dimUI.Begin(new Box2d(0, 0, framebufferSize.X, framebufferSize.Y), window.DrawQueue);
|
||||
dimUI.Begin(new Box2d(0, 0, size.X, size.Y), window.DrawQueue);
|
||||
dimUI.Text("Hello World!");
|
||||
dimUI.Button("Cancel"); dimUI.SameLine();
|
||||
dimUI.Button("OK");
|
||||
@ -158,7 +163,7 @@ app.PostInitializing += (sender, ea) => {
|
||||
|
||||
dimUI.Finish();
|
||||
|
||||
GL.Viewport(0, 0, framebufferSize.X, framebufferSize.Y);
|
||||
GL.Viewport(0, 0, size.X, size.Y);
|
||||
GL.ClearColor(0.3f, 0.3f, 0.3f, 1.0f);
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
||||
GL.Disable(EnableCap.DepthTest);
|
||||
@ -166,11 +171,10 @@ app.PostInitializing += (sender, ea) => {
|
||||
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
|
||||
GL.ColorMask(true, true, true, true);
|
||||
|
||||
executor.Draw(window.DrawQueue);
|
||||
executor.Draw(window.DrawQueue, new RectangleF(0, 0, size.X, size.Y), 1.5f /*(window as IDpiAwareWindow)?.Scale ?? 1*/);
|
||||
executor.EndFrame();
|
||||
|
||||
context.SwapGroup.Swap();
|
||||
};
|
||||
};
|
||||
|
||||
app.Run(true, source.Token);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user