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:
parent
d7e302fb23
commit
f888fb5697
@ -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; }
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
16
src/Artemis.Core/SkiaSharp/IManagedGraphicsContext.cs
Normal file
16
src/Artemis.Core/SkiaSharp/IManagedGraphicsContext.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
@ -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.");
|
||||
|
||||
22
src/Artemis.UI/Exceptions/ArtemisGraphicsContextException.cs
Normal file
22
src/Artemis.UI/Exceptions/ArtemisGraphicsContextException.cs
Normal 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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 />
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Artemis.UI.SkiaSharp
|
||||
namespace Artemis.UI.SkiaSharp.Vulkan
|
||||
{
|
||||
internal class Kernel32
|
||||
{
|
||||
@ -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
|
||||
{
|
||||
@ -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
|
||||
{
|
||||
65
src/Artemis.UI/SkiaSharp/VulkanContext.cs
Normal file
65
src/Artemis.UI/SkiaSharp/VulkanContext.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user