diff --git a/src/Artemis.Core/Constants.cs b/src/Artemis.Core/Constants.cs
index 068524a1d..21c2ca973 100644
--- a/src/Artemis.Core/Constants.cs
+++ b/src/Artemis.Core/Constants.cs
@@ -2,9 +2,10 @@
using System.Collections.Generic;
using System.IO;
using Artemis.Core.JsonConverters;
+using Artemis.Core.Services;
using Artemis.Core.Services.Core;
+using Artemis.Core.SkiaSharp;
using Newtonsoft.Json;
-using SkiaSharp;
namespace Artemis.Core
{
@@ -118,20 +119,10 @@ namespace Artemis.Core
typeof(decimal)
};
- private static GRContext? _skiaGraphicsContext;
-
///
- /// Gets or sets the graphics context to be used for rendering by SkiaSharp
+ /// Gets the graphics context to be used for rendering by SkiaSharp. Can be set via
+ /// .
///
- public static GRContext? SkiaGraphicsContext
- {
- get => _skiaGraphicsContext;
- set
- {
- if (_skiaGraphicsContext != null)
- throw new ArtemisCoreException($"{nameof(SkiaGraphicsContext)} can only be set once.");
- _skiaGraphicsContext = value;
- }
- }
+ public static IManagedGraphicsContext? ManagedGraphicsContext { get; internal set; }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Exceptions/ArtemisGraphicsContextException.cs b/src/Artemis.Core/Exceptions/ArtemisGraphicsContextException.cs
new file mode 100644
index 000000000..975dd8c04
--- /dev/null
+++ b/src/Artemis.Core/Exceptions/ArtemisGraphicsContextException.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Artemis.Core
+{
+ ///
+ /// Represents SkiaSharp graphics-context related errors
+ ///
+ public class ArtemisGraphicsContextException : Exception
+ {
+ ///
+ public ArtemisGraphicsContextException()
+ {
+ }
+
+ ///
+ public ArtemisGraphicsContextException(string message) : base(message)
+ {
+ }
+
+ ///
+ public ArtemisGraphicsContextException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Renderer.cs b/src/Artemis.Core/Models/Profile/Renderer.cs
index fee081904..05c2fd8e1 100644
--- a/src/Artemis.Core/Models/Profile/Renderer.cs
+++ b/src/Artemis.Core/Models/Profile/Renderer.cs
@@ -36,11 +36,11 @@ namespace Artemis.Core
int width = (int) pathBounds.Width;
int height = (int) pathBounds.Height;
- SKImageInfo imageInfo = new SKImageInfo(width, height);
- if (Constants.SkiaGraphicsContext == null)
+ SKImageInfo imageInfo = new(width, height);
+ if (Constants.ManagedGraphicsContext?.GraphicsContext == null)
Surface = SKSurface.Create(imageInfo);
else
- Surface = SKSurface.Create(Constants.SkiaGraphicsContext, true, imageInfo);
+ Surface = SKSurface.Create(Constants.ManagedGraphicsContext.GraphicsContext, true, imageInfo);
Path = new SKPath(path);
Path.Transform(SKMatrix.CreateTranslation(pathBounds.Left * -1, pathBounds.Top * -1));
diff --git a/src/Artemis.Core/RGB.NET/SKTexture.cs b/src/Artemis.Core/RGB.NET/SKTexture.cs
index 20f80b066..cc0ebc4c9 100644
--- a/src/Artemis.Core/RGB.NET/SKTexture.cs
+++ b/src/Artemis.Core/RGB.NET/SKTexture.cs
@@ -1,6 +1,5 @@
using System;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
+using Artemis.Core.SkiaSharp;
using RGB.NET.Core;
using RGB.NET.Presets.Textures.Sampler;
using SkiaSharp;
@@ -17,14 +16,14 @@ namespace Artemis.Core
#region Constructors
- internal SKTexture(int width, int height, float renderScale)
+ internal SKTexture(IManagedGraphicsContext? managedGraphicsContext, int width, int height, float renderScale)
: base(width, height, 4, new AverageByteSampler())
{
ImageInfo = new SKImageInfo(width, height);
- if (Constants.SkiaGraphicsContext == null)
+ if (managedGraphicsContext == null)
Surface = SKSurface.Create(ImageInfo);
else
- Surface = SKSurface.Create(Constants.SkiaGraphicsContext, true, ImageInfo);
+ Surface = SKSurface.Create(managedGraphicsContext.GraphicsContext, true, ImageInfo);
RenderScale = renderScale;
}
diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs
index eac279fe1..17961a9e2 100644
--- a/src/Artemis.Core/Services/CoreService.cs
+++ b/src/Artemis.Core/Services/CoreService.cs
@@ -59,6 +59,7 @@ namespace Artemis.Core.Services
UpdatePluginCache();
+ _rgbService.IsRenderPaused = true;
_rgbService.Surface.Updating += SurfaceOnUpdating;
_loggingLevel.SettingChanged += (sender, args) => ApplyLoggingLevel();
@@ -242,6 +243,7 @@ namespace Artemis.Core.Services
IsElevated
);
+ _rgbService.IsRenderPaused = false;
OnInitialized();
}
diff --git a/src/Artemis.Core/Services/Interfaces/IRgbService.cs b/src/Artemis.Core/Services/Interfaces/IRgbService.cs
index 099b4bb3f..00c5f85d0 100644
--- a/src/Artemis.Core/Services/Interfaces/IRgbService.cs
+++ b/src/Artemis.Core/Services/Interfaces/IRgbService.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using Artemis.Core.SkiaSharp;
using RGB.NET.Core;
namespace Artemis.Core.Services
@@ -50,6 +51,16 @@ namespace Artemis.Core.Services
///
void CloseRender();
+ ///
+ /// Updates the graphics context to the provided .
+ /// Note: The old graphics context will be used until the next frame starts rendering and is disposed afterwards.
+ ///
+ ///
+ /// The new managed graphics context. If , software rendering
+ /// is used.
+ ///
+ void UpdateGraphicsContext(IManagedGraphicsContext? managedGraphicsContext);
+
///
/// Adds the given device provider to the
///
diff --git a/src/Artemis.Core/Services/RgbService.cs b/src/Artemis.Core/Services/RgbService.cs
index 2e34d27dd..7be1f8c17 100644
--- a/src/Artemis.Core/Services/RgbService.cs
+++ b/src/Artemis.Core/Services/RgbService.cs
@@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
using System.Linq;
using Artemis.Core.DeviceProviders;
using Artemis.Core.Services.Models;
+using Artemis.Core.SkiaSharp;
using Artemis.Storage.Entities.Surface;
using Artemis.Storage.Repositories.Interfaces;
using RGB.NET.Core;
@@ -51,7 +52,7 @@ namespace Artemis.Core.Services
UpdateTrigger = new TimerUpdateTrigger {UpdateFrequency = 1.0 / _targetFrameRateSetting.Value};
Surface.RegisterUpdateTrigger(UpdateTrigger);
}
-
+
public TimerUpdateTrigger UpdateTrigger { get; }
protected virtual void OnDeviceRemoved(DeviceEventArgs e)
@@ -118,9 +119,7 @@ namespace Artemis.Core.Services
public IReadOnlyCollection Devices => _devices.AsReadOnly();
public IReadOnlyDictionary LedMap => new ReadOnlyDictionary(_ledMap);
- ///
public RGBSurface Surface { get; set; }
-
public bool IsRenderPaused { get; set; }
public bool RenderOpen { get; private set; }
@@ -215,6 +214,8 @@ namespace Artemis.Core.Services
#region Rendering
+ private IManagedGraphicsContext? _newGraphicsContext;
+
public SKTexture OpenRender()
{
if (RenderOpen)
@@ -231,7 +232,7 @@ namespace Artemis.Core.Services
{
if (!RenderOpen)
throw new ArtemisCoreException("Render pipeline is already closed");
-
+
RenderOpen = false;
_texture?.CopyPixelData();
}
@@ -241,15 +242,38 @@ namespace Artemis.Core.Services
if (RenderOpen)
throw new ArtemisCoreException("Cannot update the texture while rendering");
- SKTexture? oldTexture = _texture;
+ IManagedGraphicsContext? graphicsContext = Constants.ManagedGraphicsContext = _newGraphicsContext;
+ if (!ReferenceEquals(graphicsContext, _newGraphicsContext))
+ graphicsContext = _newGraphicsContext;
+
+ if (graphicsContext != null)
+ _logger.Debug("Creating SKTexture with graphics context {graphicsContext}", graphicsContext.GetType().Name);
+ else
+ _logger.Debug("Creating SKTexture with software-based graphics context");
float renderScale = (float) _renderScaleSetting.Value;
int width = Math.Max(1, MathF.Min(Surface.Boundary.Size.Width * renderScale, 4096).RoundToInt());
int height = Math.Max(1, MathF.Min(Surface.Boundary.Size.Height * renderScale, 4096).RoundToInt());
- _texture = new SKTexture(width, height, renderScale);
+ _texture?.Dispose();
+ _texture = new SKTexture(graphicsContext, width, height, renderScale);
_textureBrush.Texture = _texture;
- oldTexture?.Dispose();
+
+ if (!ReferenceEquals(_newGraphicsContext, Constants.ManagedGraphicsContext = _newGraphicsContext))
+ {
+ Constants.ManagedGraphicsContext?.Dispose();
+ Constants.ManagedGraphicsContext = _newGraphicsContext;
+ _newGraphicsContext = null;
+ }
+ }
+
+ public void UpdateGraphicsContext(IManagedGraphicsContext? managedGraphicsContext)
+ {
+ if (ReferenceEquals(managedGraphicsContext, Constants.ManagedGraphicsContext))
+ return;
+
+ _newGraphicsContext = managedGraphicsContext;
+ _texture?.Invalidate();
}
#endregion
diff --git a/src/Artemis.Core/SkiaSharp/IManagedGraphicsContext.cs b/src/Artemis.Core/SkiaSharp/IManagedGraphicsContext.cs
new file mode 100644
index 000000000..d2e7ff03b
--- /dev/null
+++ b/src/Artemis.Core/SkiaSharp/IManagedGraphicsContext.cs
@@ -0,0 +1,16 @@
+using System;
+using SkiaSharp;
+
+namespace Artemis.Core.SkiaSharp
+{
+ ///
+ /// Represents a managed wrapper around a SkiaSharp context
+ ///
+ public interface IManagedGraphicsContext : IDisposable
+ {
+ ///
+ /// Gets the graphics context created by this wrapper
+ ///
+ GRContext GraphicsContext { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Bootstrapper.cs b/src/Artemis.UI/Bootstrapper.cs
index 29383b8fe..888851231 100644
--- a/src/Artemis.UI/Bootstrapper.cs
+++ b/src/Artemis.UI/Bootstrapper.cs
@@ -68,12 +68,10 @@ namespace Artemis.UI
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
// Create and bind the root view, this is a tray icon so don't show it with the window manager
- Execute.OnUIThread(() =>
+ Execute.OnUIThreadSync(() =>
{
UIElement view = viewManager.CreateAndBindViewForModelIfNecessary(RootViewModel);
((TrayViewModel) RootViewModel).SetTaskbarIcon(view);
-
- CreateGraphicsContext();
});
// Initialize the core async so the UI can show the progress
@@ -96,6 +94,11 @@ namespace Artemis.UI
registrationService.RegisterInputProvider();
registrationService.RegisterControllers();
+ Execute.OnUIThreadSync(() =>
+ {
+ registrationService.ApplyPreferredGraphicsContext();
+ });
+
// Initialize background services
Kernel.Get();
}
@@ -133,22 +136,6 @@ namespace Artemis.UI
e.Handled = true;
}
- private void CreateGraphicsContext()
- {
- Win32VkContext vulkanContext = new();
- GRVkBackendContext vulkanBackendContext = new()
- {
- VkInstance = (IntPtr) vulkanContext.Instance.RawHandle.ToUInt64(),
- VkPhysicalDevice = (IntPtr) vulkanContext.PhysicalDevice.RawHandle.ToUInt64(),
- VkDevice = (IntPtr) vulkanContext.Device.RawHandle.ToUInt64(),
- VkQueue = (IntPtr) vulkanContext.GraphicsQueue.RawHandle.ToUInt64(),
- GraphicsQueueIndex = vulkanContext.GraphicsFamily,
- GetProcedureAddress = vulkanContext.GetProc
- };
-
- Constants.SkiaGraphicsContext = GRContext.CreateVulkan(vulkanBackendContext);
- }
-
private void HandleFatalException(Exception e, ILogger logger)
{
logger.Fatal(e, "Fatal exception during initialization, shutting down.");
diff --git a/src/Artemis.UI/Exceptions/ArtemisGraphicsContextException.cs b/src/Artemis.UI/Exceptions/ArtemisGraphicsContextException.cs
new file mode 100644
index 000000000..653d65ef4
--- /dev/null
+++ b/src/Artemis.UI/Exceptions/ArtemisGraphicsContextException.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace Artemis.UI.Exceptions
+{
+ public class ArtemisGraphicsContextException : Exception
+ {
+ ///
+ public ArtemisGraphicsContextException()
+ {
+ }
+
+ ///
+ public ArtemisGraphicsContextException(string message) : base(message)
+ {
+ }
+
+ ///
+ public ArtemisGraphicsContextException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabView.xaml b/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabView.xaml
index 6aef820f3..ecd070137 100644
--- a/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabView.xaml
+++ b/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabView.xaml
@@ -7,6 +7,7 @@
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:dataTemplateSelectors="clr-namespace:Artemis.UI.DataTemplateSelectors"
+ xmlns:system="clr-namespace:System;assembly=System.Runtime"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:GeneralSettingsTabViewModel}">
@@ -307,6 +308,30 @@
Rendering
+
+
+
+
+
+
+
+
+
+
+ Preferred render method
+
+ Software-based rendering is done purely on the CPU while Vulkan uses GPU-acceleration
+
+
+
+
+ Software
+ Vulkan
+
+
+
+
+
diff --git a/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabViewModel.cs
index 86e8fdb7b..9193ba8ff 100644
--- a/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabViewModel.cs
+++ b/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabViewModel.cs
@@ -3,15 +3,10 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
-using System.Reflection.Metadata;
-using System.Security.Principal;
using System.Threading.Tasks;
-using System.Xml.Linq;
using Artemis.Core;
using Artemis.Core.LayerBrushes;
using Artemis.Core.Services;
-using Artemis.Core.Services.Core;
-using Artemis.UI.Properties;
using Artemis.UI.Screens.StartupWizard;
using Artemis.UI.Services;
using Artemis.UI.Shared;
@@ -30,6 +25,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
private readonly IDialogService _dialogService;
private readonly IKernel _kernel;
private readonly IMessageService _messageService;
+ private readonly IRegistrationService _registrationService;
private readonly ISettingsService _settingsService;
private readonly IUpdateService _updateService;
private readonly IWindowManager _windowManager;
@@ -45,7 +41,8 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
ISettingsService settingsService,
IUpdateService updateService,
IPluginManagementService pluginManagementService,
- IMessageService messageService)
+ IMessageService messageService,
+ IRegistrationService registrationService)
{
DisplayName = "GENERAL";
@@ -56,6 +53,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
_settingsService = settingsService;
_updateService = updateService;
_messageService = messageService;
+ _registrationService = registrationService;
LogLevels = new BindableCollection(EnumUtilities.GetAllValuesAndDescriptions(typeof(LogEventLevel)));
ColorSchemes = new BindableCollection(EnumUtilities.GetAllValuesAndDescriptions(typeof(ApplicationColorScheme)));
@@ -209,6 +207,17 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
}
}
+ public string PreferredGraphicsContext
+ {
+ get => _settingsService.GetSetting("Core.PreferredGraphicsContext", "Vulkan").Value;
+ set
+ {
+ _settingsService.GetSetting("Core.PreferredGraphicsContext", "Vulkan").Value = value;
+ _settingsService.GetSetting("Core.PreferredGraphicsContext", "Vulkan").Save();
+ _registrationService.ApplyPreferredGraphicsContext();
+ }
+ }
+
public double RenderScale
{
get => _settingsService.GetSetting("Core.RenderScale", 0.5).Value;
@@ -316,10 +325,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
try
{
bool taskCreated = false;
- if (!recreate)
- {
- taskCreated = SettingsUtilities.IsAutoRunTaskCreated();
- }
+ if (!recreate) taskCreated = SettingsUtilities.IsAutoRunTaskCreated();
if (StartWithWindows && !taskCreated)
SettingsUtilities.CreateAutoRunTask(TimeSpan.FromSeconds(AutoRunDelay));
@@ -335,7 +341,6 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
}
-
public enum ApplicationColorScheme
{
Light,
diff --git a/src/Artemis.UI/Services/RegistrationService.cs b/src/Artemis.UI/Services/RegistrationService.cs
index 0d51ce376..6da26b430 100644
--- a/src/Artemis.UI/Services/RegistrationService.cs
+++ b/src/Artemis.UI/Services/RegistrationService.cs
@@ -1,4 +1,5 @@
-using System.Linq;
+using System;
+using System.Linq;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Controllers;
@@ -8,6 +9,7 @@ using Artemis.UI.DefaultTypes.PropertyInput;
using Artemis.UI.InputProviders;
using Artemis.UI.Ninject;
using Artemis.UI.Shared.Services;
+using Artemis.UI.SkiaSharp;
using Serilog;
namespace Artemis.UI.Services
@@ -15,28 +17,37 @@ namespace Artemis.UI.Services
public class RegistrationService : IRegistrationService
{
private readonly ILogger _logger;
+ private readonly ICoreService _coreService;
private readonly IDataModelUIService _dataModelUIService;
private readonly IProfileEditorService _profileEditorService;
private readonly IPluginManagementService _pluginManagementService;
private readonly IInputService _inputService;
private readonly IWebServerService _webServerService;
+ private readonly IRgbService _rgbService;
+ private readonly ISettingsService _settingsService;
private bool _registeredBuiltInDataModelDisplays;
private bool _registeredBuiltInDataModelInputs;
private bool _registeredBuiltInPropertyEditors;
public RegistrationService(ILogger logger,
+ ICoreService coreService,
IDataModelUIService dataModelUIService,
IProfileEditorService profileEditorService,
IPluginManagementService pluginManagementService,
IInputService inputService,
- IWebServerService webServerService)
+ IWebServerService webServerService,
+ IRgbService rgbService,
+ ISettingsService settingsService)
{
_logger = logger;
+ _coreService = coreService;
_dataModelUIService = dataModelUIService;
_profileEditorService = profileEditorService;
_pluginManagementService = pluginManagementService;
_inputService = inputService;
_webServerService = webServerService;
+ _rgbService = rgbService;
+ _settingsService = settingsService;
LoadPluginModules();
pluginManagementService.PluginEnabling += PluginServiceOnPluginEnabling;
@@ -97,6 +108,39 @@ namespace Artemis.UI.Services
_webServerService.AddController();
}
+ ///
+ public void ApplyPreferredGraphicsContext()
+ {
+ if (_coreService.StartupArguments.Contains("--force-software-render"))
+ {
+ _logger.Warning("Startup argument '--force-software-render' is applied, forcing software rendering.");
+ _rgbService.UpdateGraphicsContext(null);
+ return;
+ }
+
+ PluginSetting preferredGraphicsContext = _settingsService.GetSetting("Core.PreferredGraphicsContext", "Vulkan");
+
+ try
+ {
+ switch (preferredGraphicsContext.Value)
+ {
+ case "Software":
+ _rgbService.UpdateGraphicsContext(null);
+ break;
+ case "Vulkan":
+ _rgbService.UpdateGraphicsContext(new VulkanContext());
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.Warning(e, "Failed to apply preferred graphics context {preferred}", preferredGraphicsContext.Value);
+ _rgbService.UpdateGraphicsContext(null);
+ }
+ }
+
private void PluginServiceOnPluginEnabling(object sender, PluginEventArgs e)
{
e.Plugin.Kernel.Load(new[] {new PluginUIModule(e.Plugin)});
@@ -116,5 +160,6 @@ namespace Artemis.UI.Services
void RegisterBuiltInPropertyEditors();
void RegisterInputProvider();
void RegisterControllers();
+ void ApplyPreferredGraphicsContext();
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/SkiaSharp/User32.cs b/src/Artemis.UI/SkiaSharp/User32.cs
deleted file mode 100644
index a53212d5f..000000000
--- a/src/Artemis.UI/SkiaSharp/User32.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using System.Text;
-
-namespace Artemis.UI.SkiaSharp
-{
- internal class User32
- {
- private const string user32 = "user32.dll";
-
- public const uint IDC_ARROW = 32512;
-
- public const uint IDI_APPLICATION = 32512;
- public const uint IDI_WINLOGO = 32517;
-
- public const int SW_HIDE = 0;
-
- public const uint CS_VREDRAW = 0x1;
- public const uint CS_HREDRAW = 0x2;
- public const uint CS_OWNDC = 0x20;
-
- public const uint WS_EX_CLIENTEDGE = 0x00000200;
-
- [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
- public static extern ushort RegisterClass(ref Win32Window.WNDCLASS lpWndClass);
-
- [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
- public static extern ushort UnregisterClass([MarshalAs(UnmanagedType.LPTStr)] string lpClassName, IntPtr hInstance);
-
- [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
- public static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
-
- [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
- public static extern IntPtr LoadIcon(IntPtr hInstance, IntPtr lpIconName);
-
- [DllImport(user32, CallingConvention = CallingConvention.Winapi)]
- public static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
-
- [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
- public static extern IntPtr CreateWindowEx(uint dwExStyle, [MarshalAs(UnmanagedType.LPTStr)] string lpClassName, [MarshalAs(UnmanagedType.LPTStr)] string lpWindowName, WindowStyles dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);
-
- public static IntPtr CreateWindow(string lpClassName, string lpWindowName, WindowStyles dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam)
- {
- return CreateWindowEx(0, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
- }
-
- [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
- public static extern IntPtr GetDC(IntPtr hWnd);
-
- [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
-
- [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool DestroyWindow(IntPtr hWnd);
-
- [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool IsWindow(IntPtr hWnd);
-
- [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
- public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
-
- [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool AdjustWindowRectEx(ref RECT lpRect, WindowStyles dwStyle, bool bMenu, uint dwExStyle);
-
- [DllImport(user32, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);
-
- [StructLayout(LayoutKind.Sequential)]
- public struct RECT
- {
- public int left;
- public int top;
- public int right;
- public int bottom;
- }
-
- [Flags]
- public enum WindowStyles : uint
- {
- WS_BORDER = 0x800000,
- WS_CAPTION = 0xc00000,
- WS_CHILD = 0x40000000,
- WS_CLIPCHILDREN = 0x2000000,
- WS_CLIPSIBLINGS = 0x4000000,
- WS_DISABLED = 0x8000000,
- WS_DLGFRAME = 0x400000,
- WS_GROUP = 0x20000,
- WS_HSCROLL = 0x100000,
- WS_MAXIMIZE = 0x1000000,
- WS_MAXIMIZEBOX = 0x10000,
- WS_MINIMIZE = 0x20000000,
- WS_MINIMIZEBOX = 0x20000,
- WS_OVERLAPPED = 0x0,
- WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_SIZEFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
- WS_POPUP = 0x80000000u,
- WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU,
- WS_SIZEFRAME = 0x40000,
- WS_SYSMENU = 0x80000,
- WS_TABSTOP = 0x10000,
- WS_VISIBLE = 0x10000000,
- WS_VSCROLL = 0x200000
- }
- }
-}
diff --git a/src/Artemis.UI/SkiaSharp/Kernel32.cs b/src/Artemis.UI/SkiaSharp/Vulkan/Kernel32.cs
similarity index 94%
rename from src/Artemis.UI/SkiaSharp/Kernel32.cs
rename to src/Artemis.UI/SkiaSharp/Vulkan/Kernel32.cs
index 6fd86aab0..8f094b4ca 100644
--- a/src/Artemis.UI/SkiaSharp/Kernel32.cs
+++ b/src/Artemis.UI/SkiaSharp/Vulkan/Kernel32.cs
@@ -1,7 +1,7 @@
using System;
using System.Runtime.InteropServices;
-namespace Artemis.UI.SkiaSharp
+namespace Artemis.UI.SkiaSharp.Vulkan
{
internal class Kernel32
{
diff --git a/src/Artemis.UI/SkiaSharp/VkContext.cs b/src/Artemis.UI/SkiaSharp/Vulkan/VkContext.cs
similarity index 96%
rename from src/Artemis.UI/SkiaSharp/VkContext.cs
rename to src/Artemis.UI/SkiaSharp/Vulkan/VkContext.cs
index 48937cf46..c7cb8de7f 100644
--- a/src/Artemis.UI/SkiaSharp/VkContext.cs
+++ b/src/Artemis.UI/SkiaSharp/Vulkan/VkContext.cs
@@ -6,7 +6,7 @@ using Instance = SharpVk.Instance;
using PhysicalDevice = SharpVk.PhysicalDevice;
using Queue = SharpVk.Queue;
-namespace Artemis.UI.SkiaSharp
+namespace Artemis.UI.SkiaSharp.Vulkan
{
internal class VkContext : IDisposable
{
diff --git a/src/Artemis.UI/SkiaSharp/Win32VkContext.cs b/src/Artemis.UI/SkiaSharp/Vulkan/Win32VkContext.cs
similarity index 98%
rename from src/Artemis.UI/SkiaSharp/Win32VkContext.cs
rename to src/Artemis.UI/SkiaSharp/Vulkan/Win32VkContext.cs
index 1ddc7c539..eff84d8ac 100644
--- a/src/Artemis.UI/SkiaSharp/Win32VkContext.cs
+++ b/src/Artemis.UI/SkiaSharp/Vulkan/Win32VkContext.cs
@@ -4,7 +4,7 @@ using System.Windows.Forms;
using SharpVk;
using SharpVk.Khronos;
-namespace Artemis.UI.SkiaSharp
+namespace Artemis.UI.SkiaSharp.Vulkan
{
internal sealed class Win32VkContext : VkContext
{
diff --git a/src/Artemis.UI/SkiaSharp/VulkanContext.cs b/src/Artemis.UI/SkiaSharp/VulkanContext.cs
new file mode 100644
index 000000000..a9aee5dd0
--- /dev/null
+++ b/src/Artemis.UI/SkiaSharp/VulkanContext.cs
@@ -0,0 +1,65 @@
+using System;
+using Artemis.Core.SkiaSharp;
+using Artemis.UI.Exceptions;
+using Artemis.UI.SkiaSharp.Vulkan;
+using SkiaSharp;
+
+namespace Artemis.UI.SkiaSharp
+{
+ public class VulkanContext : IManagedGraphicsContext
+ {
+ private readonly GRVkBackendContext _vulkanBackendContext;
+ private readonly Win32VkContext _vulkanContext;
+
+ public VulkanContext()
+ {
+ // Try everything in separate try-catch blocks to provide some accuracy in error reporting
+ try
+ {
+ _vulkanContext = new Win32VkContext();
+ }
+ catch (Exception e)
+ {
+ throw new ArtemisGraphicsContextException("Failed to create Vulkan context", e);
+ }
+
+ try
+ {
+ _vulkanBackendContext = new GRVkBackendContext
+ {
+ VkInstance = (IntPtr) _vulkanContext.Instance.RawHandle.ToUInt64(),
+ VkPhysicalDevice = (IntPtr) _vulkanContext.PhysicalDevice.RawHandle.ToUInt64(),
+ VkDevice = (IntPtr) _vulkanContext.Device.RawHandle.ToUInt64(),
+ VkQueue = (IntPtr) _vulkanContext.GraphicsQueue.RawHandle.ToUInt64(),
+ GraphicsQueueIndex = _vulkanContext.GraphicsFamily,
+ GetProcedureAddress = _vulkanContext.GetProc
+ };
+ }
+ catch (Exception e)
+ {
+ throw new ArtemisGraphicsContextException("Failed to create Vulkan backend context", e);
+ }
+
+ try
+ {
+ GraphicsContext = GRContext.CreateVulkan(_vulkanBackendContext);
+ if (GraphicsContext == null)
+ throw new ArtemisGraphicsContextException("GRContext.CreateVulkan returned null");
+ }
+ catch (Exception e)
+ {
+ throw new ArtemisGraphicsContextException("Failed to create Vulkan graphics context", e);
+ }
+ }
+
+ ///
+ public void Dispose()
+ {
+ _vulkanBackendContext?.Dispose();
+ _vulkanContext?.Dispose();
+ GraphicsContext?.Dispose();
+ }
+
+ public GRContext GraphicsContext { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/SkiaSharp/Win32Window.cs b/src/Artemis.UI/SkiaSharp/Win32Window.cs
deleted file mode 100644
index c7dd0c48c..000000000
--- a/src/Artemis.UI/SkiaSharp/Win32Window.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-
-namespace Artemis.UI.SkiaSharp
-{
- internal class Win32Window : IDisposable
- {
- private ushort classRegistration;
-
- public string WindowClassName { get; }
-
- public IntPtr WindowHandle { get; private set; }
-
- public IntPtr DeviceContextHandle { get; private set; }
-
- public Win32Window(string className)
- {
- WindowClassName = className;
-
- var wc = new WNDCLASS
- {
- cbClsExtra = 0,
- cbWndExtra = 0,
- hbrBackground = IntPtr.Zero,
- hCursor = User32.LoadCursor(IntPtr.Zero, (int)User32.IDC_ARROW),
- hIcon = User32.LoadIcon(IntPtr.Zero, (IntPtr)User32.IDI_APPLICATION),
- hInstance = Kernel32.CurrentModuleHandle,
- lpfnWndProc = (WNDPROC)User32.DefWindowProc,
- lpszClassName = WindowClassName,
- lpszMenuName = null,
- style = User32.CS_HREDRAW | User32.CS_VREDRAW | User32.CS_OWNDC
- };
-
- classRegistration = User32.RegisterClass(ref wc);
- if (classRegistration == 0)
- throw new Exception($"Could not register window class: {className}");
-
- WindowHandle = User32.CreateWindow(
- WindowClassName,
- $"The Invisible Man ({className})",
- User32.WindowStyles.WS_OVERLAPPEDWINDOW,
- 0, 0,
- 1, 1,
- IntPtr.Zero, IntPtr.Zero, Kernel32.CurrentModuleHandle, IntPtr.Zero);
- if (WindowHandle == IntPtr.Zero)
- throw new Exception($"Could not create window: {className}");
-
- DeviceContextHandle = User32.GetDC(WindowHandle);
- if (DeviceContextHandle == IntPtr.Zero)
- {
- Dispose();
- throw new Exception($"Could not get device context: {className}");
- }
- }
-
- public void Dispose()
- {
- if (WindowHandle != IntPtr.Zero)
- {
- if (DeviceContextHandle != IntPtr.Zero)
- {
- User32.ReleaseDC(WindowHandle, DeviceContextHandle);
- DeviceContextHandle = IntPtr.Zero;
- }
-
- User32.DestroyWindow(WindowHandle);
- WindowHandle = IntPtr.Zero;
- }
-
- if (classRegistration != 0)
- {
- User32.UnregisterClass(WindowClassName, Kernel32.CurrentModuleHandle);
- classRegistration = 0;
- }
- }
-
- public delegate IntPtr WNDPROC(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
-
- [StructLayout(LayoutKind.Sequential)]
- public struct WNDCLASS
- {
- public uint style;
- [MarshalAs(UnmanagedType.FunctionPtr)]
- public WNDPROC lpfnWndProc;
- public int cbClsExtra;
- public int cbWndExtra;
- public IntPtr hInstance;
- public IntPtr hIcon;
- public IntPtr hCursor;
- public IntPtr hbrBackground;
- [MarshalAs(UnmanagedType.LPTStr)]
- public string lpszMenuName;
- [MarshalAs(UnmanagedType.LPTStr)]
- public string lpszClassName;
- }
- }
-}