1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Core - Added the ability to change graphics context at runtime

Settings - Added setting for switching between software-based and Vulkan-based rendering
This commit is contained in:
Robert 2021-03-22 19:29:53 +01:00
parent d7e302fb23
commit f888fb5697
19 changed files with 281 additions and 270 deletions

View File

@ -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;
/// <summary>
/// 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
/// <see cref="IRgbService.UpdateGraphicsContext" />.
/// </summary>
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; }
}
}

View File

@ -0,0 +1,25 @@
using System;
namespace Artemis.Core
{
/// <summary>
/// Represents SkiaSharp graphics-context related errors
/// </summary>
public class ArtemisGraphicsContextException : Exception
{
/// <inheritdoc />
public ArtemisGraphicsContextException()
{
}
/// <inheritdoc />
public ArtemisGraphicsContextException(string message) : base(message)
{
}
/// <inheritdoc />
public ArtemisGraphicsContextException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

@ -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));

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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
/// </summary>
void CloseRender();
/// <summary>
/// Updates the graphics context to the provided <paramref name="managedGraphicsContext"></paramref>.
/// <para>Note: The old graphics context will be used until the next frame starts rendering and is disposed afterwards.</para>
/// </summary>
/// <param name="managedGraphicsContext">
/// The new managed graphics context. If <see langword="null" />, software rendering
/// is used.
/// </param>
void UpdateGraphicsContext(IManagedGraphicsContext? managedGraphicsContext);
/// <summary>
/// Adds the given device provider to the <see cref="Surface" />
/// </summary>

View File

@ -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;
@ -118,9 +119,7 @@ namespace Artemis.Core.Services
public IReadOnlyCollection<ArtemisDevice> Devices => _devices.AsReadOnly();
public IReadOnlyDictionary<Led, ArtemisLed> LedMap => new ReadOnlyDictionary<Led, ArtemisLed>(_ledMap);
/// <inheritdoc />
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)
@ -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

View File

@ -0,0 +1,16 @@
using System;
using SkiaSharp;
namespace Artemis.Core.SkiaSharp
{
/// <summary>
/// Represents a managed wrapper around a SkiaSharp context
/// </summary>
public interface IManagedGraphicsContext : IDisposable
{
/// <summary>
/// Gets the graphics context created by this wrapper
/// </summary>
GRContext GraphicsContext { get; }
}
}

View File

@ -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<IDeviceLayoutService>();
}
@ -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.");

View File

@ -0,0 +1,22 @@
using System;
namespace Artemis.UI.Exceptions
{
public class ArtemisGraphicsContextException : Exception
{
/// <inheritdoc />
public ArtemisGraphicsContextException()
{
}
/// <inheritdoc />
public ArtemisGraphicsContextException(string message) : base(message)
{
}
/// <inheritdoc />
public ArtemisGraphicsContextException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

@ -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 @@
<TextBlock Style="{StaticResource MaterialDesignHeadline5TextBlock}" Margin="0 15">Rendering</TextBlock>
<materialDesign:Card materialDesign:ShadowAssist.ShadowDepth="Depth1" VerticalAlignment="Stretch" Margin="0,0,5,0">
<StackPanel Margin="15">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Style="{StaticResource MaterialDesignTextBlock}">Preferred render method</TextBlock>
<TextBlock Style="{StaticResource MaterialDesignTextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
Software-based rendering is done purely on the CPU while Vulkan uses GPU-acceleration
</TextBlock>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
<ComboBox Width="80" SelectedItem="{Binding PreferredGraphicsContext}" >
<system:String>Software</system:String>
<system:String>Vulkan</system:String>
</ComboBox>
</StackPanel>
</Grid>
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition />

View File

@ -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<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(LogEventLevel)));
ColorSchemes = new BindableCollection<ValueDescription>(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,

View File

@ -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<RemoteController>();
}
/// <inheritdoc />
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<string> 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();
}
}

View File

@ -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
}
}
}

View File

@ -1,7 +1,7 @@
using System;
using System.Runtime.InteropServices;
namespace Artemis.UI.SkiaSharp
namespace Artemis.UI.SkiaSharp.Vulkan
{
internal class Kernel32
{

View File

@ -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
{

View File

@ -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
{

View File

@ -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);
}
}
/// <inheritdoc />
public void Dispose()
{
_vulkanBackendContext?.Dispose();
_vulkanContext?.Dispose();
GraphicsContext?.Dispose();
}
public GRContext GraphicsContext { get; }
}
}

View File

@ -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;
}
}
}