Core - Made graphics context providable by UI

Windows - Implemented Vulkan graphics context
Splash screen - Tweaked design
This commit is contained in:
Robert committed 2022-05-06 18:19:02 +02:00
1 parent b2987b5190
commit 34407aaeec
18 files changed
+346 -25

No files matched your search

@@ -33,6 +33,7 @@
<PackageReference Include="Microsoft.Win32" Version="2.0.1" />
<PackageReference Include="RawInput.Sharp" Version="0.0.4" />
<PackageReference Include="ReactiveUI" Version="17.1.50" />
<PackageReference Include="SkiaSharp.Vulkan.SharpVk" Version="2.80.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
@@ -1,4 +1,5 @@
using Artemis.UI.Shared.Providers;
using Artemis.Core.Services;
using Artemis.UI.Shared.Providers;
using Artemis.UI.Windows.Providers;
using Ninject.Modules;
@@ -12,6 +13,7 @@ public class WindowsModule : NinjectModule
public override void Load()
{
Kernel!.Bind<ICursorProvider>().To<CursorProvider>().InSingletonScope();
Kernel!.Bind<IGraphicsContextProvider>().To<GraphicsContextProvider>().InSingletonScope();
}
#endregion
@@ -0,0 +1,26 @@
using System.Collections.Generic;
using Artemis.Core.Services;
using Artemis.Core.SkiaSharp;
using Artemis.UI.Windows.SkiaSharp;
namespace Artemis.UI.Windows.Providers;
public class GraphicsContextProvider : IGraphicsContextProvider
{
private VulkanContext? _vulkanContext;
/// <inheritdoc />
public IReadOnlyCollection<string> GraphicsContextNames => new List<string> {"Vulkan"}.AsReadOnly();
/// <inheritdoc />
public IManagedGraphicsContext? GetGraphicsContext(string name)
{
if (name == "Vulkan")
{
_vulkanContext ??= new VulkanContext();
return _vulkanContext;
}
return null;
}
}
@@ -0,0 +1,21 @@
using System;
using System.Runtime.InteropServices;
namespace Artemis.UI.Windows.SkiaSharp.Vulkan;
internal class Kernel32
{
private const string kernel32 = "kernel32.dll";
static Kernel32()
{
CurrentModuleHandle = GetModuleHandle(null);
if (CurrentModuleHandle == IntPtr.Zero)
throw new Exception("Could not get module handle.");
}
public static IntPtr CurrentModuleHandle { get; }
[DllImport(kernel32, CallingConvention = CallingConvention.Winapi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
public static extern IntPtr GetModuleHandle([MarshalAs(UnmanagedType.LPTStr)] string lpModuleName);
}
@@ -0,0 +1,34 @@
using System;
using SharpVk;
using SharpVk.Khronos;
using SkiaSharp;
namespace Artemis.UI.Windows.SkiaSharp.Vulkan;
internal class VkContext : IDisposable
{
public virtual Instance Instance { get; protected set; }
public virtual PhysicalDevice PhysicalDevice { get; protected set; }
public virtual Surface Surface { get; protected set; }
public virtual Device Device { get; protected set; }
public virtual Queue GraphicsQueue { get; protected set; }
public virtual Queue PresentQueue { get; protected set; }
public virtual uint GraphicsFamily { get; protected set; }
public virtual uint PresentFamily { get; protected set; }
public virtual GRVkGetProcedureAddressDelegate GetProc { get; protected set; }
public virtual GRSharpVkGetProcedureAddressDelegate SharpVkGetProc { get; protected set; }
public virtual void Dispose()
{
Instance?.Dispose();
}
}
@@ -0,0 +1,87 @@
using System;
using System.Linq;
using Avalonia.Win32;
using SharpVk;
using SharpVk.Khronos;
namespace Artemis.UI.Windows.SkiaSharp.Vulkan;
internal sealed class Win32VkContext : VkContext
{
public Win32VkContext()
{
Window = new WindowImpl();
Instance = Instance.Create(null, new[] {"VK_KHR_surface", "VK_KHR_win32_surface"});
PhysicalDevice = Instance.EnumeratePhysicalDevices().First();
Surface = Instance.CreateWin32Surface(Kernel32.CurrentModuleHandle, Window.Handle.Handle);
(GraphicsFamily, PresentFamily) = FindQueueFamilies();
DeviceQueueCreateInfo[] queueInfos =
{
new() {QueueFamilyIndex = GraphicsFamily, QueuePriorities = new[] {1f}},
new() {QueueFamilyIndex = PresentFamily, QueuePriorities = new[] {1f}}
};
Device = PhysicalDevice.CreateDevice(queueInfos, null, null);
GraphicsQueue = Device.GetQueue(GraphicsFamily, 0);
PresentQueue = Device.GetQueue(PresentFamily, 0);
GetProc = Proc;
SharpVkGetProc = (name, instance, device) =>
{
if (device != null)
return device.GetProcedureAddress(name);
if (instance != null)
return instance.GetProcedureAddress(name);
// SharpVk includes the static functions on Instance, but this is not actually correct
// since the functions are static, they are not tied to an instance. For example,
// VkCreateInstance is not found on an instance, it is creating said instance.
// Other libraries, such as VulkanCore, use another type to do this.
return Instance.GetProcedureAddress(name);
};
}
public WindowImpl Window { get; }
public override void Dispose()
{
base.Dispose();
Window.Dispose();
}
private IntPtr Proc(string name, IntPtr instanceHandle, IntPtr deviceHandle)
{
if (deviceHandle != IntPtr.Zero)
return Device.GetProcedureAddress(name);
return Instance.GetProcedureAddress(name);
}
private (uint, uint) FindQueueFamilies()
{
QueueFamilyProperties[] queueFamilyProperties = PhysicalDevice.GetQueueFamilyProperties();
var graphicsFamily = queueFamilyProperties
.Select((properties, index) => new {properties, index})
.SkipWhile(pair => !pair.properties.QueueFlags.HasFlag(QueueFlags.Graphics))
.FirstOrDefault();
if (graphicsFamily == null)
throw new Exception("Unable to find graphics queue");
uint? presentFamily = default;
for (uint i = 0; i < queueFamilyProperties.Length; ++i)
{
if (PhysicalDevice.GetSurfaceSupport(i, Surface))
presentFamily = i;
}
if (!presentFamily.HasValue)
throw new Exception("Unable to find present queue");
return ((uint) graphicsFamily.index, presentFamily.Value);
}
}
@@ -0,0 +1,64 @@
using System;
using Artemis.Core.SkiaSharp;
using Artemis.UI.Exceptions;
using Artemis.UI.Windows.SkiaSharp.Vulkan;
using SkiaSharp;
namespace Artemis.UI.Windows.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);
}
GraphicsContext.Flush();
}
/// <inheritdoc />
public void Dispose()
{
}
public GRContext GraphicsContext { get; }
}
}
+19
View File
@@ -90,6 +90,16 @@
"Splat": "14.1.45"
}
},
"SkiaSharp.Vulkan.SharpVk": {
"type": "Direct",
"requested": "[2.80.3, )",
"resolved": "2.80.3",
"contentHash": "IeR9oOHBsJUqpuVs23XgZXnrFV6WuOTaLpFhLVlXt2XILWIRrlrqx1PILgJm5bLqesceJLYZyMxVb/Ow7/uReA==",
"dependencies": {
"SharpVk": "0.4.2",
"SkiaSharp": "2.80.3"
}
},
"Avalonia.Angle.Windows.Natives": {
"type": "Transitive",
"resolved": "2.1.0.2020091801",
@@ -738,6 +748,15 @@
"Serilog": "2.10.0"
}
},
"SharpVk": {
"type": "Transitive",
"resolved": "0.4.2",
"contentHash": "0CzZJWKw6CTmxKOXzCCyTKCD7tZB6g2+tm2VSSCXWTHlIMHxlRzbH5BaqkYCGo9Y23wp0hPuz2U3NifMH1VI6w==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "4.4.0",
"System.ValueTuple": "4.4.0"
}
},
"ShimSkiaSharp": {
"type": "Transitive",
"resolved": "0.5.12",