Create a new context system for dashboard.
This commit is contained in:
parent
50eda46b13
commit
edc85c3f24
@ -6,16 +6,31 @@ namespace Dashboard
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pixel format for images.
|
/// Pixel format for images.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
public enum PixelFormat
|
public enum PixelFormat
|
||||||
{
|
{
|
||||||
R8I,
|
None = 0,
|
||||||
Rg8I,
|
|
||||||
Rgb8I,
|
R8I = R | I8,
|
||||||
Rgba8I,
|
Rg8I = Rg | I8,
|
||||||
R16F,
|
Rgb8I = Rgb | I8,
|
||||||
Rg816F,
|
Rgba8I = Rgba | I8,
|
||||||
Rgb16F,
|
R16F = R | F16,
|
||||||
Rgba16F,
|
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>
|
/// <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 System.Drawing;
|
||||||
using Dashboard.Drawing.OpenGL.Executors;
|
using Dashboard.Drawing.OpenGL.Executors;
|
||||||
using Dashboard.OpenGL;
|
using Dashboard.OpenGL;
|
||||||
|
using OpenTK.Mathematics;
|
||||||
|
|
||||||
namespace Dashboard.Drawing.OpenGL
|
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 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();
|
BeginDraw();
|
||||||
|
|
||||||
|
if (scale != 1.0f)
|
||||||
|
TransformStack.Push(Matrix4.CreateScale(scale, scale, 1));
|
||||||
|
|
||||||
foreach (ICommandFrame frame in drawQueue)
|
foreach (ICommandFrame frame in drawQueue)
|
||||||
{
|
{
|
||||||
if (_executorsMap.TryGetValue(frame.Command.Extension.Name, out ICommandExecutor? executor))
|
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.
|
/// Activate this OpenGL Context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void MakeCurrent();
|
void MakeCurrent();
|
||||||
|
|
||||||
|
IntPtr GetProcAddress(string procName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,6 +40,11 @@ namespace Dashboard.OpenTK.PAL2
|
|||||||
TK.OpenGL.SetCurrentContext(ContextHandle);
|
TK.OpenGL.SetCurrentContext(ContextHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IntPtr GetProcAddress(string procName)
|
||||||
|
{
|
||||||
|
return TK.OpenGL.GetProcedureAddress(ContextHandle, procName);
|
||||||
|
}
|
||||||
|
|
||||||
private bool _isDisposed = false;
|
private bool _isDisposed = false;
|
||||||
|
|
||||||
public void Dispose() => Dispose(true);
|
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
|
namespace Dashboard.OpenTK.PAL2
|
||||||
{
|
{
|
||||||
public class PhysicalWindow : IPhysicalWindow, IDrawQueuePaintable, IEventListener
|
public class PhysicalWindow : IPhysicalWindow, IDrawQueuePaintable, IEventListener, IDpiAwareWindow
|
||||||
{
|
{
|
||||||
public DrawQueue DrawQueue { get; } = new DrawQueue();
|
public DrawQueue DrawQueue { get; } = new DrawQueue();
|
||||||
public WindowHandle WindowHandle { get; }
|
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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (_isDisposed) return;
|
if (IsDisposed) return;
|
||||||
_isDisposed = true;
|
IsDisposed = true;
|
||||||
|
|
||||||
RemoveWindow(this);
|
RemoveWindow(this);
|
||||||
|
|
||||||
@ -160,5 +160,16 @@ namespace Dashboard.OpenTK.PAL2
|
|||||||
break;
|
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 Box2d = Dashboard.Box2d;
|
||||||
using TK = OpenTK.Platform.Toolkit;
|
using TK = OpenTK.Platform.Toolkit;
|
||||||
using Dashboard;
|
using Dashboard;
|
||||||
|
using Dashboard.Windowing;
|
||||||
|
using OpenTK.Windowing.GraphicsLibraryFramework;
|
||||||
|
using AppContext = Dashboard.Pal.AppContext;
|
||||||
|
|
||||||
TK.Init(new ToolkitOptions()
|
TK.Init(new ToolkitOptions()
|
||||||
{
|
{
|
||||||
@ -18,11 +21,12 @@ TK.Init(new ToolkitOptions()
|
|||||||
{
|
{
|
||||||
EnableVisualStyles = true,
|
EnableVisualStyles = true,
|
||||||
IsDPIAware = true,
|
IsDPIAware = true,
|
||||||
}
|
},
|
||||||
|
FeatureFlags = ToolkitFlags.EnableOpenGL,
|
||||||
});
|
});
|
||||||
|
|
||||||
Application app = new Application(new Pal2DashboardBackend()
|
AppContext app = new Pal2AppContext()
|
||||||
{
|
{
|
||||||
GraphicsApiHints = new OpenGLGraphicsApiHints()
|
GraphicsApiHints = new OpenGLGraphicsApiHints()
|
||||||
{
|
{
|
||||||
Version = new Version(3, 2),
|
Version = new Version(3, 2),
|
||||||
@ -41,7 +45,7 @@ Application app = new Application(new Pal2DashboardBackend()
|
|||||||
|
|
||||||
SupportTransparentFramebufferX11 = true,
|
SupportTransparentFramebufferX11 = true,
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
PhysicalWindow window;
|
PhysicalWindow window;
|
||||||
SolidBrush fg = new SolidBrush(Color.FromArgb(0, 0, 0, 0));
|
SolidBrush fg = new SolidBrush(Color.FromArgb(0, 0, 0, 0));
|
||||||
@ -56,121 +60,121 @@ List<Vector3> points = new List<Vector3>();
|
|||||||
IFont font;
|
IFont font;
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
app.PostInitializing += (sender, ea) => {
|
app.Initialize();
|
||||||
window = (PhysicalWindow)app.CreatePhysicalWindow();
|
|
||||||
|
|
||||||
window.Title = "DashTerm";
|
window = (PhysicalWindow)app.CreatePhysicalWindow();
|
||||||
TK.Window.SetMinClientSize(window.WindowHandle, 300, 200);
|
|
||||||
TK.Window.SetClientSize(window.WindowHandle, new Vector2i(320, 240));
|
|
||||||
TK.Window.SetBorderStyle(window.WindowHandle, WindowBorderStyle.ResizableBorder);
|
|
||||||
// TK.Window.SetTransparencyMode(wnd, WindowTransparencyMode.TransparentFramebuffer, 0.1f);
|
|
||||||
|
|
||||||
OpenGLDeviceContext context = (OpenGLDeviceContext)window.DeviceContext;
|
window.Title = "DashTerm";
|
||||||
|
TK.Window.SetMinClientSize(window.WindowHandle, 300, 200);
|
||||||
|
TK.Window.SetClientSize(window.WindowHandle, new Vector2i(320, 240));
|
||||||
|
TK.Window.SetBorderStyle(window.WindowHandle, WindowBorderStyle.ResizableBorder);
|
||||||
|
// TK.Window.SetTransparencyMode(wnd, WindowTransparencyMode.TransparentFramebuffer, 0.1f);
|
||||||
|
|
||||||
context.MakeCurrent();
|
OpenGLDeviceContext context = (OpenGLDeviceContext)window.DeviceContext;
|
||||||
context.SwapGroup.SwapInterval = 1;
|
|
||||||
|
|
||||||
engine = new GLEngine();
|
context.MakeCurrent();
|
||||||
engine.Initialize();
|
context.SwapGroup.SwapInterval = 1;
|
||||||
|
|
||||||
executor = engine.GetExecutor(context);
|
|
||||||
|
|
||||||
dimUI = new DimUI(new DimUIConfig()
|
engine = new GLEngine();
|
||||||
|
engine.Initialize();
|
||||||
|
|
||||||
|
executor = engine.GetExecutor(context);
|
||||||
|
|
||||||
|
dimUI = new DimUI(new DimUIConfig()
|
||||||
|
{
|
||||||
|
Font = new NamedFont("Noto Sans", 9f),
|
||||||
|
});
|
||||||
|
|
||||||
|
EventQueue.EventRaised += (handle, type, eventArgs) =>
|
||||||
|
{
|
||||||
|
if (handle != window.WindowHandle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
{
|
{
|
||||||
Font = new NamedFont("Noto Sans", 9f),
|
case PlatformEventType.Close:
|
||||||
});
|
source.Cancel();
|
||||||
|
break;
|
||||||
|
case PlatformEventType.MouseMove:
|
||||||
|
mousePos = ((MouseMoveEventArgs)eventArgs).ClientPosition;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
EventQueue.EventRaised += (handle, type, eventArgs) =>
|
TK.Window.SetMode(window.WindowHandle, WindowMode.Normal);
|
||||||
{
|
font = Typesetter.LoadFont("Nimbus Mono", 12f);
|
||||||
if (handle != window.WindowHandle)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (type)
|
window.Painting += (sender, ea) => {
|
||||||
|
TK.Window.GetSize(context.WindowHandle, out Vector2i size);
|
||||||
|
executor.BeginFrame();
|
||||||
|
|
||||||
|
dimUI.Begin(new Box2d(0, 0, size.X, size.Y), window.DrawQueue);
|
||||||
|
dimUI.Text("Hello World!");
|
||||||
|
dimUI.Button("Cancel"); dimUI.SameLine();
|
||||||
|
dimUI.Button("OK");
|
||||||
|
|
||||||
|
dimUI.Input("type me!", builder);
|
||||||
|
|
||||||
|
dimUI.BeginMenu();
|
||||||
|
|
||||||
|
if (dimUI.MenuItem("File"))
|
||||||
{
|
{
|
||||||
case PlatformEventType.Close:
|
|
||||||
source.Cancel();
|
|
||||||
break;
|
|
||||||
case PlatformEventType.MouseMove:
|
|
||||||
mousePos = ((MouseMoveEventArgs)eventArgs).ClientPosition;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TK.Window.SetMode(window.WindowHandle, WindowMode.Normal);
|
|
||||||
font = Typesetter.LoadFont("Nimbus Mono", 12f);
|
|
||||||
|
|
||||||
window.Painting += (sender, ea) => {
|
|
||||||
TK.Window.GetFramebufferSize(context.WindowHandle, out Vector2i framebufferSize);
|
|
||||||
executor.BeginFrame();
|
|
||||||
|
|
||||||
dimUI.Begin(new Box2d(0, 0, framebufferSize.X, framebufferSize.Y), window.DrawQueue);
|
|
||||||
dimUI.Text("Hello World!");
|
|
||||||
dimUI.Button("Cancel"); dimUI.SameLine();
|
|
||||||
dimUI.Button("OK");
|
|
||||||
|
|
||||||
dimUI.Input("type me!", builder);
|
|
||||||
|
|
||||||
dimUI.BeginMenu();
|
dimUI.BeginMenu();
|
||||||
|
dimUI.MenuItem("New Window");
|
||||||
|
dimUI.MenuItem("Preferences");
|
||||||
|
dimUI.MenuItem("Exit");
|
||||||
|
dimUI.EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
if (dimUI.MenuItem("File"))
|
if (dimUI.MenuItem("Edit"))
|
||||||
|
{
|
||||||
|
dimUI.BeginMenu();
|
||||||
|
dimUI.MenuItem("Cut");
|
||||||
|
dimUI.MenuItem("Copy");
|
||||||
|
dimUI.MenuItem("Paste");
|
||||||
|
|
||||||
|
if (dimUI.MenuItem("Send Char"))
|
||||||
{
|
{
|
||||||
dimUI.BeginMenu();
|
dimUI.BeginMenu();
|
||||||
dimUI.MenuItem("New Window");
|
|
||||||
dimUI.MenuItem("Preferences");
|
|
||||||
dimUI.MenuItem("Exit");
|
|
||||||
dimUI.EndMenu();
|
dimUI.EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dimUI.MenuItem("Edit"))
|
dimUI.EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dimUI.MenuItem("View"))
|
||||||
|
{
|
||||||
|
dimUI.BeginMenu();
|
||||||
|
dimUI.MenuItem("Clear");
|
||||||
|
|
||||||
|
if (dimUI.MenuItem("Set Size"))
|
||||||
{
|
{
|
||||||
dimUI.BeginMenu();
|
dimUI.BeginMenu();
|
||||||
dimUI.MenuItem("Cut");
|
dimUI.MenuItem("24 x 40");
|
||||||
dimUI.MenuItem("Copy");
|
dimUI.MenuItem("25 x 40");
|
||||||
dimUI.MenuItem("Paste");
|
dimUI.MenuItem("24 x 80");
|
||||||
|
dimUI.MenuItem("25 x 80");
|
||||||
if (dimUI.MenuItem("Send Char"))
|
dimUI.MenuItem("25 x 120");
|
||||||
{
|
|
||||||
dimUI.BeginMenu();
|
|
||||||
dimUI.EndMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
dimUI.EndMenu();
|
dimUI.EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dimUI.MenuItem("View"))
|
dimUI.EndMenu();
|
||||||
{
|
}
|
||||||
dimUI.BeginMenu();
|
|
||||||
dimUI.MenuItem("Clear");
|
|
||||||
|
|
||||||
if (dimUI.MenuItem("Set Size"))
|
dimUI.Finish();
|
||||||
{
|
|
||||||
dimUI.BeginMenu();
|
|
||||||
dimUI.MenuItem("24 x 40");
|
|
||||||
dimUI.MenuItem("25 x 40");
|
|
||||||
dimUI.MenuItem("24 x 80");
|
|
||||||
dimUI.MenuItem("25 x 80");
|
|
||||||
dimUI.MenuItem("25 x 120");
|
|
||||||
dimUI.EndMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
dimUI.EndMenu();
|
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);
|
||||||
|
GL.Enable(EnableCap.Blend);
|
||||||
|
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
|
||||||
|
GL.ColorMask(true, true, true, true);
|
||||||
|
|
||||||
dimUI.Finish();
|
executor.Draw(window.DrawQueue, new RectangleF(0, 0, size.X, size.Y), 1.5f /*(window as IDpiAwareWindow)?.Scale ?? 1*/);
|
||||||
|
executor.EndFrame();
|
||||||
|
|
||||||
GL.Viewport(0, 0, framebufferSize.X, framebufferSize.Y);
|
context.SwapGroup.Swap();
|
||||||
GL.ClearColor(0.3f, 0.3f, 0.3f, 1.0f);
|
|
||||||
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
|
||||||
GL.Disable(EnableCap.DepthTest);
|
|
||||||
GL.Enable(EnableCap.Blend);
|
|
||||||
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
|
|
||||||
GL.ColorMask(true, true, true, true);
|
|
||||||
|
|
||||||
executor.Draw(window.DrawQueue);
|
|
||||||
executor.EndFrame();
|
|
||||||
|
|
||||||
context.SwapGroup.Swap();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
app.Run(true, source.Token);
|
app.Run(true, source.Token);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user