Browse Source
* Ported the old Vulkan PR * chop-chop * Support for external objects in vulkan backend * Fixed structure type * Removed debug code * sln fix * Don't force vulkan on windowspull/15312/head
committed by
GitHub
57 changed files with 6238 additions and 49 deletions
@ -0,0 +1,17 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks> |
||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" /> |
||||
|
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<Import Project="..\..\build\DevAnalyzers.props" /> |
||||
|
<Import Project="..\..\build\SourceGenerators.props" /> |
||||
|
<Import Project="..\..\build\TrimmingEnable.props" /> |
||||
|
<Import Project="..\..\build\NullableEnable.props" /> |
||||
|
</Project> |
||||
@ -0,0 +1,33 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Metadata; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Rendering.Composition; |
||||
|
|
||||
|
namespace Avalonia.Vulkan; |
||||
|
|
||||
|
[Unstable] |
||||
|
public interface IVulkanContextExternalObjectsFeature |
||||
|
{ |
||||
|
IReadOnlyList<string> SupportedImageHandleTypes { get; } |
||||
|
IReadOnlyList<string> SupportedSemaphoreTypes { get; } |
||||
|
byte[]? DeviceUuid { get; } |
||||
|
byte[]? DeviceLuid { get; } |
||||
|
CompositionGpuImportedImageSynchronizationCapabilities GetSynchronizationCapabilities(string imageHandleType); |
||||
|
IVulkanExternalImage ImportImage(IPlatformHandle handle, PlatformGraphicsExternalImageProperties properties); |
||||
|
IVulkanExternalSemaphore ImportSemaphore(IPlatformHandle handle); |
||||
|
} |
||||
|
|
||||
|
[Unstable] |
||||
|
public interface IVulkanExternalSemaphore : IDisposable |
||||
|
{ |
||||
|
ulong Handle { get; } |
||||
|
void SubmitWaitSemaphore(); |
||||
|
void SubmitSignalSemaphore(); |
||||
|
} |
||||
|
|
||||
|
[Unstable] |
||||
|
public interface IVulkanExternalImage : IDisposable |
||||
|
{ |
||||
|
VulkanImageInfo Info { get; } |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Metadata; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan; |
||||
|
public interface IVulkanDevice : IDisposable, IOptionalFeatureProvider |
||||
|
{ |
||||
|
public IntPtr Handle { get; } |
||||
|
public IntPtr PhysicalDeviceHandle { get; } |
||||
|
public IntPtr MainQueueHandle { get; } |
||||
|
public uint GraphicsQueueFamilyIndex { get; } |
||||
|
public IVulkanInstance Instance { get; } |
||||
|
bool IsLost { get; } |
||||
|
public IDisposable Lock(); |
||||
|
public IEnumerable<string> EnabledExtensions { get; } |
||||
|
} |
||||
|
|
||||
|
public interface IVulkanInstance |
||||
|
{ |
||||
|
public IntPtr Handle { get; } |
||||
|
public IntPtr GetInstanceProcAddress(IntPtr instance, string name); |
||||
|
public IntPtr GetDeviceProcAddress(IntPtr device, string name); |
||||
|
public IEnumerable<string> EnabledExtensions { get; } |
||||
|
} |
||||
|
|
||||
|
[NotClientImplementable] |
||||
|
public interface IVulkanPlatformGraphicsContext : IPlatformGraphicsContext |
||||
|
{ |
||||
|
IVulkanDevice Device { get; } |
||||
|
IVulkanInstance Instance { get; } |
||||
|
internal VulkanInstanceApi InstanceApi { get; } |
||||
|
internal VulkanDeviceApi DeviceApi { get; } |
||||
|
internal VkDevice DeviceHandle { get; } |
||||
|
internal VkPhysicalDevice PhysicalDeviceHandle { get; } |
||||
|
internal VkInstance InstanceHandle { get; } |
||||
|
internal VkQueue MainQueueHandle { get; } |
||||
|
internal uint GraphicsQueueFamilyIndex { get; } |
||||
|
IVulkanRenderTarget CreateRenderTarget(IEnumerable<object> surfaces); |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Vulkan; |
||||
|
public interface IVulkanKhrSurfacePlatformSurface : IDisposable |
||||
|
{ |
||||
|
double Scaling { get; } |
||||
|
PixelSize Size { get; } |
||||
|
ulong CreateSurface(IVulkanPlatformGraphicsContext context); |
||||
|
} |
||||
|
|
||||
|
public interface IVulkanKhrSurfacePlatformSurfaceFactory |
||||
|
{ |
||||
|
bool CanRenderToSurface(IVulkanPlatformGraphicsContext context, object surface); |
||||
|
IVulkanKhrSurfacePlatformSurface CreateSurface(IVulkanPlatformGraphicsContext context, object surface); |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Metadata; |
||||
|
|
||||
|
namespace Avalonia.Vulkan; |
||||
|
|
||||
|
[NotClientImplementable] |
||||
|
public interface IVulkanRenderTarget : IDisposable |
||||
|
{ |
||||
|
IVulkanRenderSession BeginDraw(); |
||||
|
} |
||||
|
|
||||
|
[NotClientImplementable] |
||||
|
public interface IVulkanRenderSession : IDisposable |
||||
|
{ |
||||
|
double Scaling { get; } |
||||
|
PixelSize Size { get; } |
||||
|
public bool IsYFlipped { get; } |
||||
|
VulkanImageInfo ImageInfo { get; } |
||||
|
bool IsRgba { get; } |
||||
|
} |
||||
@ -0,0 +1,107 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan.Interop; |
||||
|
|
||||
|
internal class VulkanCommandBuffer : IDisposable |
||||
|
{ |
||||
|
private readonly VulkanCommandBufferPool _pool; |
||||
|
private VkCommandBuffer _handle; |
||||
|
private readonly IVulkanPlatformGraphicsContext _context; |
||||
|
private VulkanFence _fence; |
||||
|
private bool _hasEnded; |
||||
|
private bool _hasStarted; |
||||
|
public VkCommandBuffer Handle => _handle; |
||||
|
|
||||
|
public VulkanCommandBuffer(VulkanCommandBufferPool pool, VkCommandBuffer handle, IVulkanPlatformGraphicsContext context) |
||||
|
{ |
||||
|
_pool = pool; |
||||
|
_handle = handle; |
||||
|
_context = context; |
||||
|
_fence = new VulkanFence(context, VkFenceCreateFlags.VK_FENCE_CREATE_SIGNALED_BIT); |
||||
|
} |
||||
|
|
||||
|
public unsafe void Dispose() |
||||
|
{ |
||||
|
if (_fence.Handle.Handle != 0) |
||||
|
_fence.Wait(); |
||||
|
_fence.Dispose(); |
||||
|
if (_handle.Handle != IntPtr.Zero) |
||||
|
{ |
||||
|
VkCommandBuffer buf = _handle; |
||||
|
_context.DeviceApi.FreeCommandBuffers(_context.DeviceHandle, _pool.Handle, 1, &buf); |
||||
|
_handle = default; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public bool IsFinished => _fence.IsSignaled; |
||||
|
|
||||
|
public void BeginRecording() |
||||
|
{ |
||||
|
if (_hasStarted) |
||||
|
return; |
||||
|
|
||||
|
var beginInfo = new VkCommandBufferBeginInfo |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, |
||||
|
flags = VkCommandBufferUsageFlags.VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT |
||||
|
}; |
||||
|
|
||||
|
_context.DeviceApi.BeginCommandBuffer(_handle, ref beginInfo).ThrowOnError("vkBeginCommandBuffer"); |
||||
|
_hasStarted = true; |
||||
|
} |
||||
|
|
||||
|
public void EndRecording() |
||||
|
{ |
||||
|
if (_hasStarted && !_hasEnded) |
||||
|
{ |
||||
|
_context.DeviceApi.EndCommandBuffer(_handle).ThrowOnError("vkEndCommandBuffer"); |
||||
|
_hasEnded = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Submit() |
||||
|
{ |
||||
|
Submit(null, null, null); |
||||
|
} |
||||
|
|
||||
|
public unsafe void Submit( |
||||
|
ReadOnlySpan<VulkanSemaphore> waitSemaphores, |
||||
|
ReadOnlySpan<VkPipelineStageFlags> waitDstStageMask, |
||||
|
ReadOnlySpan<VulkanSemaphore> signalSemaphores, |
||||
|
VulkanFence? fence = null) |
||||
|
{ |
||||
|
|
||||
|
EndRecording(); |
||||
|
VkFence fenceHandle = (fence ?? _fence).Handle; |
||||
|
_context.DeviceApi.ResetFences(_context.DeviceHandle, 1, &fenceHandle) |
||||
|
.ThrowOnError("vkResetFences"); |
||||
|
|
||||
|
var pWaitSempaphores = stackalloc VkSemaphore[waitSemaphores.Length]; |
||||
|
for (var c = 0; c < waitSemaphores.Length; c++) |
||||
|
pWaitSempaphores[c] = waitSemaphores[c].Handle; |
||||
|
|
||||
|
var pSignalSemaphores = stackalloc VkSemaphore[signalSemaphores.Length]; |
||||
|
for (var c = 0; c < signalSemaphores.Length; c++) |
||||
|
pSignalSemaphores[c] = signalSemaphores[c].Handle; |
||||
|
|
||||
|
VkCommandBuffer commandBuffer = _handle; |
||||
|
fixed (VkPipelineStageFlags* flags = waitDstStageMask) |
||||
|
{ |
||||
|
var submitInfo = new VkSubmitInfo |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_SUBMIT_INFO, |
||||
|
waitSemaphoreCount = (uint)waitSemaphores.Length, |
||||
|
pWaitSemaphores = pWaitSempaphores, |
||||
|
signalSemaphoreCount = (uint)signalSemaphores.Length, |
||||
|
pSignalSemaphores = pSignalSemaphores, |
||||
|
commandBufferCount = 1, |
||||
|
pCommandBuffers = &commandBuffer, |
||||
|
pWaitDstStageMask = flags |
||||
|
}; |
||||
|
_context.DeviceApi.QueueSubmit(_context.MainQueueHandle, 1, &submitInfo, fenceHandle) |
||||
|
.ThrowOnError("vkQueueSubmit"); |
||||
|
} |
||||
|
_pool.AddSubmittedCommandBuffer(this); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,71 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan.Interop; |
||||
|
|
||||
|
internal class VulkanCommandBufferPool : IDisposable |
||||
|
{ |
||||
|
private readonly IVulkanPlatformGraphicsContext _context; |
||||
|
private readonly Queue<VulkanCommandBuffer> _commandBuffers = new(); |
||||
|
private VkCommandPool _handle; |
||||
|
public VkCommandPool Handle => _handle; |
||||
|
|
||||
|
public VulkanCommandBufferPool(IVulkanPlatformGraphicsContext context) |
||||
|
{ |
||||
|
_context = context; |
||||
|
var createInfo = new VkCommandPoolCreateInfo |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, |
||||
|
flags = VkCommandPoolCreateFlags.VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, |
||||
|
queueFamilyIndex = context.GraphicsQueueFamilyIndex |
||||
|
}; |
||||
|
_context.DeviceApi.CreateCommandPool(_context.DeviceHandle, ref createInfo, IntPtr.Zero, out _handle) |
||||
|
.ThrowOnError("vkCreateCommandPool"); |
||||
|
} |
||||
|
|
||||
|
public void FreeUsedCommandBuffers() |
||||
|
{ |
||||
|
while (_commandBuffers.Count > 0) |
||||
|
_commandBuffers.Dequeue().Dispose(); |
||||
|
} |
||||
|
|
||||
|
public void FreeFinishedCommandBuffers() |
||||
|
{ |
||||
|
while (_commandBuffers.Count > 0) |
||||
|
{ |
||||
|
var next = _commandBuffers.Peek(); |
||||
|
if(!next.IsFinished) |
||||
|
return; |
||||
|
_commandBuffers.Dequeue(); |
||||
|
next.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
FreeUsedCommandBuffers(); |
||||
|
|
||||
|
if (_handle.Handle != 0) |
||||
|
_context.DeviceApi.DestroyCommandPool(_context.DeviceHandle, _handle, IntPtr.Zero); |
||||
|
_handle = default; |
||||
|
} |
||||
|
|
||||
|
public unsafe VulkanCommandBuffer CreateCommandBuffer() |
||||
|
{ |
||||
|
var commandBufferAllocateInfo = new VkCommandBufferAllocateInfo |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, |
||||
|
commandPool = _handle, |
||||
|
commandBufferCount = 1, |
||||
|
level = VkCommandBufferLevel.VK_COMMAND_BUFFER_LEVEL_PRIMARY |
||||
|
}; |
||||
|
VkCommandBuffer bufferHandle = default; |
||||
|
_context.DeviceApi.AllocateCommandBuffers(_context.DeviceHandle, ref commandBufferAllocateInfo, |
||||
|
&bufferHandle).ThrowOnError("vkAllocateCommandBuffers"); |
||||
|
|
||||
|
return new VulkanCommandBuffer(this, bufferHandle, _context); |
||||
|
} |
||||
|
|
||||
|
public void AddSubmittedCommandBuffer(VulkanCommandBuffer buffer) => _commandBuffers.Enqueue(buffer); |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
using System; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using Avalonia.Logging; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan.Interop; |
||||
|
|
||||
|
unsafe static class VulkanDebugLogger |
||||
|
{ |
||||
|
private static VkDebugUtilsMessengerCallbackEXTDelegate s_Delegate = WriteLogEvent; |
||||
|
public static IntPtr CallbackPtr { get; } = Marshal.GetFunctionPointerForDelegate(s_Delegate); |
||||
|
|
||||
|
private static uint WriteLogEvent(VkDebugUtilsMessageSeverityFlagsEXT messageSeverity, |
||||
|
VkDebugUtilsMessageTypeFlagsEXT messagetypes, |
||||
|
VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, |
||||
|
void* puserdata) |
||||
|
{ |
||||
|
var level = messageSeverity switch |
||||
|
{ |
||||
|
VkDebugUtilsMessageSeverityFlagsEXT.VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT => LogEventLevel.Error, |
||||
|
VkDebugUtilsMessageSeverityFlagsEXT.VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT => |
||||
|
LogEventLevel.Warning, |
||||
|
VkDebugUtilsMessageSeverityFlagsEXT.VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT => |
||||
|
LogEventLevel.Verbose, |
||||
|
VkDebugUtilsMessageSeverityFlagsEXT.VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT => LogEventLevel |
||||
|
.Information, |
||||
|
_ => LogEventLevel.Information |
||||
|
}; |
||||
|
if (Logger.TryGet(level, "Vulkan", out var logger)) |
||||
|
{ |
||||
|
var msg =Marshal.PtrToStringAnsi((nint)pCallbackData->pMessage); |
||||
|
logger.Log("Vulkan", "Vulkan: {0}", msg); |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,166 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan.Interop; |
||||
|
|
||||
|
internal unsafe partial class VulkanDevice |
||||
|
{ |
||||
|
public static IVulkanDevice Create(IVulkanInstance instance, |
||||
|
VulkanDeviceCreationOptions options, VulkanPlatformSpecificOptions platformOptions) |
||||
|
{ |
||||
|
uint deviceCount = 0; |
||||
|
var api = new VulkanInstanceApi(instance); |
||||
|
var vkInstance = new VkInstance(instance.Handle); |
||||
|
api.EnumeratePhysicalDevices(vkInstance, ref deviceCount, null) |
||||
|
.ThrowOnError("vkEnumeratePhysicalDevices"); |
||||
|
|
||||
|
if (deviceCount == 0) |
||||
|
throw new VulkanException("No devices found"); |
||||
|
|
||||
|
var devices = stackalloc VkPhysicalDevice[(int)deviceCount]; |
||||
|
api.EnumeratePhysicalDevices(vkInstance, ref deviceCount, devices) |
||||
|
.ThrowOnError("vkEnumeratePhysicalDevices"); |
||||
|
|
||||
|
var surfaceForProbePtr = platformOptions.DeviceCheckSurfaceFactory?.Invoke(api.Instance); |
||||
|
var surfaceForProbe = surfaceForProbePtr.HasValue && surfaceForProbePtr.Value != 0 |
||||
|
? new VkSurfaceKHR(surfaceForProbePtr.Value) |
||||
|
: (VkSurfaceKHR?)null; |
||||
|
|
||||
|
DeviceInfo? compatibleDevice = null, discreteDevice = null; |
||||
|
|
||||
|
for (var c = 0; c < deviceCount; c++) |
||||
|
{ |
||||
|
var info = CheckDevice(api, devices[c], options, surfaceForProbe); |
||||
|
if (info != null) |
||||
|
{ |
||||
|
compatibleDevice ??= info; |
||||
|
if (info.Value.Type == VkPhysicalDeviceType.VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) |
||||
|
discreteDevice ??= info; |
||||
|
} |
||||
|
|
||||
|
if (compatibleDevice != null && (discreteDevice != null || !options.PreferDiscreteGpu)) |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
if (options.PreferDiscreteGpu && discreteDevice != null) |
||||
|
compatibleDevice = discreteDevice; |
||||
|
|
||||
|
if (compatibleDevice == null) |
||||
|
throw new VulkanException("No compatible devices found"); |
||||
|
|
||||
|
var dev = compatibleDevice.Value; |
||||
|
|
||||
|
var queuePriorities = stackalloc float[(int)dev.QueueCount]; |
||||
|
for (var c = 0; c < dev.QueueCount; c++) |
||||
|
queuePriorities[c] = 1f; |
||||
|
|
||||
|
var queueCreateInfo = new VkDeviceQueueCreateInfo |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, |
||||
|
queueFamilyIndex = dev.QueueFamilyIndex, |
||||
|
queueCount = dev.QueueCount, |
||||
|
pQueuePriorities = queuePriorities, |
||||
|
}; |
||||
|
|
||||
|
var enableExtensions = |
||||
|
new HashSet<string>(options.DeviceExtensions.Concat(VulkanExternalObjectsFeature.RequiredDeviceExtensions)); |
||||
|
|
||||
|
var enabledExtensions = enableExtensions |
||||
|
.Intersect(dev.Extensions).Append(VK_KHR_swapchain).Distinct().ToArray(); |
||||
|
|
||||
|
using var pEnabledExtensions = new Utf8BufferArray(enabledExtensions); |
||||
|
|
||||
|
var createInfo = new VkDeviceCreateInfo |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, |
||||
|
queueCreateInfoCount = 1, |
||||
|
pQueueCreateInfos = &queueCreateInfo, |
||||
|
ppEnabledExtensionNames = pEnabledExtensions, |
||||
|
enabledExtensionCount = pEnabledExtensions.UCount, |
||||
|
}; |
||||
|
|
||||
|
api.CreateDevice(dev.PhysicalDevice, ref createInfo, IntPtr.Zero, out var createdDevice) |
||||
|
.ThrowOnError("vkCreateDevice"); |
||||
|
|
||||
|
api.GetDeviceQueue(createdDevice, dev.QueueFamilyIndex, 0, out var createdQueue); |
||||
|
|
||||
|
return new VulkanDevice(api.Instance, createdDevice, dev.PhysicalDevice, createdQueue, |
||||
|
dev.QueueFamilyIndex, enabledExtensions); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
struct DeviceInfo |
||||
|
{ |
||||
|
public VkPhysicalDevice PhysicalDevice; |
||||
|
public uint QueueFamilyIndex; |
||||
|
public VkPhysicalDeviceType Type; |
||||
|
public List<string> Extensions; |
||||
|
public uint QueueCount; |
||||
|
} |
||||
|
|
||||
|
static List<string> GetDeviceExtensions(VulkanInstanceApi instance, VkPhysicalDevice physicalDevice) |
||||
|
{ |
||||
|
uint propertyCount = 0; |
||||
|
instance.EnumerateDeviceExtensionProperties(physicalDevice, null, ref propertyCount, null); |
||||
|
var extensionProps = new VkExtensionProperties[propertyCount]; |
||||
|
var extensions = new List<string>((int)propertyCount); |
||||
|
if (propertyCount != 0) |
||||
|
fixed (VkExtensionProperties* ptr = extensionProps) |
||||
|
{ |
||||
|
instance.EnumerateDeviceExtensionProperties(physicalDevice, null, ref propertyCount, ptr); |
||||
|
|
||||
|
for (var c = 0; c < propertyCount; c++) |
||||
|
extensions.Add(Marshal.PtrToStringAnsi(new IntPtr(ptr[c].extensionName))!); |
||||
|
} |
||||
|
|
||||
|
return extensions; |
||||
|
} |
||||
|
|
||||
|
private const string VK_KHR_swapchain = "VK_KHR_swapchain"; |
||||
|
|
||||
|
static unsafe DeviceInfo? CheckDevice(VulkanInstanceApi instance, VkPhysicalDevice physicalDevice, |
||||
|
VulkanDeviceCreationOptions options, VkSurfaceKHR? surface) |
||||
|
{ |
||||
|
instance.GetPhysicalDeviceProperties(physicalDevice, out var properties); |
||||
|
|
||||
|
var supportedExtensions = GetDeviceExtensions(instance, physicalDevice); |
||||
|
if (!supportedExtensions.Contains(VK_KHR_swapchain)) |
||||
|
return null; |
||||
|
|
||||
|
uint familyCount = 0; |
||||
|
instance.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, ref familyCount, null); |
||||
|
var familyProperties = stackalloc VkQueueFamilyProperties[(int)familyCount]; |
||||
|
instance.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, ref familyCount, familyProperties); |
||||
|
var requredFlags = VkQueueFlags.VK_QUEUE_GRAPHICS_BIT; |
||||
|
if (options.RequireComputeBit) |
||||
|
requredFlags |= VkQueueFlags.VK_QUEUE_COMPUTE_BIT; |
||||
|
|
||||
|
for (var c = 0; c < familyCount; c++) |
||||
|
{ |
||||
|
if ((familyProperties[c].queueFlags & requredFlags) != requredFlags) |
||||
|
continue; |
||||
|
if (surface.HasValue) |
||||
|
{ |
||||
|
instance.GetPhysicalDeviceSurfaceSupportKHR(physicalDevice, (uint)c, surface.Value, out var supported) |
||||
|
.ThrowOnError("vkGetPhysicalDeviceSurfaceSupportKHR"); |
||||
|
if (supported == 0) |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
return new DeviceInfo |
||||
|
{ |
||||
|
PhysicalDevice = physicalDevice, |
||||
|
Extensions = supportedExtensions, |
||||
|
Type = properties.deviceType, |
||||
|
QueueFamilyIndex = (uint)c, |
||||
|
QueueCount = familyProperties[c].queueCount |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,66 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Reactive; |
||||
|
using System.Threading; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Vulkan.Interop; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
namespace Avalonia.Vulkan.Interop; |
||||
|
|
||||
|
internal partial class VulkanDevice : IVulkanDevice |
||||
|
{ |
||||
|
private readonly VkDevice _handle; |
||||
|
private readonly VkPhysicalDevice _physicalDeviceHandle; |
||||
|
private readonly VkQueue _mainQueue; |
||||
|
private readonly uint _graphicsQueueIndex; |
||||
|
private readonly object _lock = new(); |
||||
|
private Thread? _lockedByThread; |
||||
|
private int _lockCount; |
||||
|
|
||||
|
private VulkanDevice(IVulkanInstance instance, VkDevice handle, VkPhysicalDevice physicalDeviceHandle, |
||||
|
VkQueue mainQueue, uint graphicsQueueIndex, string[] enabledExtensions) |
||||
|
{ |
||||
|
_handle = handle; |
||||
|
_physicalDeviceHandle = physicalDeviceHandle; |
||||
|
_mainQueue = mainQueue; |
||||
|
_graphicsQueueIndex = graphicsQueueIndex; |
||||
|
Instance = instance; |
||||
|
EnabledExtensions = enabledExtensions; |
||||
|
} |
||||
|
|
||||
|
T CheckAccess<T>(T f) |
||||
|
{ |
||||
|
if (_lockedByThread != Thread.CurrentThread) |
||||
|
throw new InvalidOperationException("This class is only usable when locked"); |
||||
|
return f; |
||||
|
} |
||||
|
|
||||
|
public IDisposable Lock() |
||||
|
{ |
||||
|
Monitor.Enter(_lock); |
||||
|
_lockCount++; |
||||
|
_lockedByThread = Thread.CurrentThread; |
||||
|
return Disposable.Create(() => |
||||
|
{ |
||||
|
_lockCount--; |
||||
|
if (_lockCount == 0) |
||||
|
_lockedByThread = null; |
||||
|
Monitor.Exit(_lock); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public IEnumerable<string> EnabledExtensions { get; } |
||||
|
|
||||
|
public bool IsLost => false; |
||||
|
public IntPtr Handle => _handle.Handle; |
||||
|
public IntPtr PhysicalDeviceHandle => _physicalDeviceHandle.Handle; |
||||
|
public IntPtr MainQueueHandle => CheckAccess(_mainQueue).Handle; |
||||
|
public uint GraphicsQueueFamilyIndex => _graphicsQueueIndex; |
||||
|
public IVulkanInstance Instance { get; } |
||||
|
public void Dispose() |
||||
|
{ |
||||
|
// TODO
|
||||
|
} |
||||
|
|
||||
|
public object? TryGetFeature(Type featureType) => null; |
||||
|
} |
||||
@ -0,0 +1,328 @@ |
|||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using System.Threading; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
// ReSharper disable FieldCanBeMadeReadOnly.Local
|
||||
|
// ReSharper disable IdentifierTypo
|
||||
|
// ReSharper disable StringLiteralTypo
|
||||
|
|
||||
|
namespace Avalonia.Vulkan.Interop; |
||||
|
|
||||
|
internal class VulkanDisplay : IDisposable |
||||
|
{ |
||||
|
private IVulkanPlatformGraphicsContext _context; |
||||
|
private VulkanSemaphorePair _semaphorePair; |
||||
|
private uint _nextImage; |
||||
|
private readonly VulkanKhrSurface _surface; |
||||
|
private VkSurfaceFormatKHR _surfaceFormat; |
||||
|
private VkSwapchainKHR _swapchain; |
||||
|
private VkExtent2D _swapchainExtent; |
||||
|
private VkImage[] _swapchainImages = Array.Empty<VkImage>(); |
||||
|
private VkImageView[] _swapchainImageViews = Array.Empty<VkImageView>(); |
||||
|
public VulkanCommandBufferPool CommandBufferPool { get; private set; } |
||||
|
public PixelSize Size { get; private set; } |
||||
|
|
||||
|
private VulkanDisplay(IVulkanPlatformGraphicsContext context, VulkanKhrSurface surface, VkSwapchainKHR swapchain, |
||||
|
VkExtent2D swapchainExtent) |
||||
|
{ |
||||
|
_context = context; |
||||
|
_surface = surface; |
||||
|
_swapchain = swapchain; |
||||
|
_swapchainExtent = swapchainExtent; |
||||
|
_semaphorePair = new VulkanSemaphorePair(_context); |
||||
|
CommandBufferPool = new VulkanCommandBufferPool(_context); |
||||
|
CreateSwapchainImages(); |
||||
|
} |
||||
|
|
||||
|
internal VkSurfaceFormatKHR SurfaceFormat |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (_surfaceFormat.format == VkFormat.VK_FORMAT_UNDEFINED) |
||||
|
_surfaceFormat = _surface.GetSurfaceFormat(); |
||||
|
return _surfaceFormat; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static unsafe VkSwapchainKHR CreateSwapchain(IVulkanPlatformGraphicsContext context, |
||||
|
VulkanKhrSurface surface, out VkExtent2D swapchainExtent, VulkanDisplay? oldDisplay = null) |
||||
|
{ |
||||
|
while (!surface.CanSurfacePresent()) |
||||
|
Thread.Sleep(16); |
||||
|
context.InstanceApi.GetPhysicalDeviceSurfaceCapabilitiesKHR(context.PhysicalDeviceHandle, |
||||
|
surface.Handle, out var capabilities) |
||||
|
.ThrowOnError("vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); |
||||
|
uint presentModesCount = 0; |
||||
|
context.InstanceApi.GetPhysicalDeviceSurfacePresentModesKHR(context.PhysicalDeviceHandle, |
||||
|
surface.Handle, ref presentModesCount, null) |
||||
|
.ThrowOnError("vkGetPhysicalDeviceSurfacePresentModesKHR"); |
||||
|
|
||||
|
var modes = new VkPresentModeKHR[(int)presentModesCount]; |
||||
|
fixed (VkPresentModeKHR* pModes = modes) |
||||
|
context.InstanceApi.GetPhysicalDeviceSurfacePresentModesKHR(context.PhysicalDeviceHandle, |
||||
|
surface.Handle, ref presentModesCount, pModes) |
||||
|
.ThrowOnError("vkGetPhysicalDeviceSurfacePresentModesKHR"); |
||||
|
|
||||
|
var imageCount = capabilities.minImageCount + 1; |
||||
|
if (capabilities.maxImageCount > 0 && imageCount > capabilities.maxImageCount) |
||||
|
imageCount = capabilities.maxImageCount; |
||||
|
|
||||
|
var surfaceFormat = surface.GetSurfaceFormat(); |
||||
|
|
||||
|
bool supportsIdentityTransform = capabilities.supportedTransforms.HasAllFlags( |
||||
|
VkSurfaceTransformFlagsKHR.VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR); |
||||
|
|
||||
|
bool isRotated = |
||||
|
capabilities.currentTransform.HasAllFlags(VkSurfaceTransformFlagsKHR.VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR) |
||||
|
|| capabilities.currentTransform.HasAllFlags(VkSurfaceTransformFlagsKHR |
||||
|
.VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR); |
||||
|
|
||||
|
if (capabilities.currentExtent.width != uint.MaxValue) |
||||
|
swapchainExtent = capabilities.currentExtent; |
||||
|
else |
||||
|
{ |
||||
|
var surfaceSize = surface.Size; |
||||
|
|
||||
|
var width = Math.Max(capabilities.minImageExtent.width, |
||||
|
Math.Min(capabilities.maxImageExtent.width, (uint)surfaceSize.Width)); |
||||
|
var height = Math.Max(capabilities.minImageExtent.height, |
||||
|
Math.Min(capabilities.maxImageExtent.height, (uint)surfaceSize.Height)); |
||||
|
|
||||
|
swapchainExtent = new VkExtent2D |
||||
|
{ |
||||
|
width = width, |
||||
|
height = height |
||||
|
}; |
||||
|
} |
||||
|
VkPresentModeKHR presentMode; |
||||
|
if (modes.Contains(VkPresentModeKHR.VK_PRESENT_MODE_MAILBOX_KHR)) |
||||
|
presentMode = VkPresentModeKHR.VK_PRESENT_MODE_MAILBOX_KHR; |
||||
|
else if (modes.Contains(VkPresentModeKHR.VK_PRESENT_MODE_FIFO_KHR)) |
||||
|
presentMode = VkPresentModeKHR.VK_PRESENT_MODE_FIFO_KHR; |
||||
|
else |
||||
|
presentMode = VkPresentModeKHR.VK_PRESENT_MODE_IMMEDIATE_KHR; |
||||
|
|
||||
|
var swapchainCreateInfo = new VkSwapchainCreateInfoKHR |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, |
||||
|
surface = surface.Handle, |
||||
|
minImageCount = imageCount, |
||||
|
imageFormat = surfaceFormat.format, |
||||
|
imageColorSpace = surfaceFormat.colorSpace, |
||||
|
imageExtent = swapchainExtent, |
||||
|
imageUsage = VkImageUsageFlags.VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | |
||||
|
VkImageUsageFlags.VK_IMAGE_USAGE_TRANSFER_DST_BIT, |
||||
|
imageSharingMode = VkSharingMode.VK_SHARING_MODE_EXCLUSIVE, |
||||
|
imageArrayLayers = 1, |
||||
|
preTransform = supportsIdentityTransform && isRotated |
||||
|
? VkSurfaceTransformFlagsKHR.VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR |
||||
|
: capabilities.currentTransform, |
||||
|
compositeAlpha = VkCompositeAlphaFlagsKHR.VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, |
||||
|
presentMode = presentMode, |
||||
|
clipped = 1, |
||||
|
oldSwapchain = oldDisplay?._swapchain ?? default |
||||
|
}; |
||||
|
context.DeviceApi.CreateSwapchainKHR(context.DeviceHandle, ref swapchainCreateInfo, IntPtr.Zero, |
||||
|
out var swapchain).ThrowOnError("vkCreateSwapchainKHR"); |
||||
|
oldDisplay?.DestroySwapchain(); |
||||
|
return swapchain; |
||||
|
} |
||||
|
|
||||
|
private void DestroySwapchain() |
||||
|
{ |
||||
|
if(_swapchain.Handle != 0) |
||||
|
_context.DeviceApi.DestroySwapchainKHR(_context.DeviceHandle, _swapchain, IntPtr.Zero); |
||||
|
_swapchain = default; |
||||
|
} |
||||
|
|
||||
|
internal static VulkanDisplay CreateDisplay(IVulkanPlatformGraphicsContext context, VulkanKhrSurface surface) |
||||
|
{ |
||||
|
var swapchain = CreateSwapchain(context, surface, out var extent); |
||||
|
return new VulkanDisplay(context, surface, swapchain, extent); |
||||
|
} |
||||
|
|
||||
|
private void DestroyCurrentImageViews() |
||||
|
{ |
||||
|
if (_swapchainImageViews.Length <= 0) |
||||
|
return; |
||||
|
foreach (var imageView in _swapchainImageViews) |
||||
|
_context.DeviceApi.DestroyImageView(_context.DeviceHandle, imageView, IntPtr.Zero); |
||||
|
|
||||
|
_swapchainImageViews = Array.Empty<VkImageView>(); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
private unsafe void CreateSwapchainImages() |
||||
|
{ |
||||
|
DestroyCurrentImageViews(); |
||||
|
Size = new PixelSize((int)_swapchainExtent.width, (int)_swapchainExtent.height); |
||||
|
uint imageCount = 0; |
||||
|
_context.DeviceApi.GetSwapchainImagesKHR(_context.DeviceHandle, _swapchain, ref imageCount, null) |
||||
|
.ThrowOnError("vkGetSwapchainImagesKHR"); |
||||
|
_swapchainImages = new VkImage[imageCount]; |
||||
|
fixed (VkImage* pImages = _swapchainImages) |
||||
|
_context.DeviceApi.GetSwapchainImagesKHR(_context.DeviceHandle, _swapchain, ref imageCount, |
||||
|
pImages).ThrowOnError("vkGetSwapchainImagesKHR"); |
||||
|
_swapchainImageViews = new VkImageView[imageCount]; |
||||
|
for (var c = 0; c < imageCount; c++) |
||||
|
_swapchainImageViews[c] = CreateSwapchainImageView(_swapchainImages[c], SurfaceFormat.format); |
||||
|
} |
||||
|
|
||||
|
private VkImageView CreateSwapchainImageView(VkImage swapchainImage, VkFormat format) |
||||
|
{ |
||||
|
var imageViewCreateInfo = new VkImageViewCreateInfo |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, |
||||
|
subresourceRange = |
||||
|
{ |
||||
|
aspectMask = VkImageAspectFlags.VK_IMAGE_ASPECT_COLOR_BIT, |
||||
|
levelCount = 1, |
||||
|
layerCount = 1 |
||||
|
}, |
||||
|
format = format, |
||||
|
image = swapchainImage, |
||||
|
viewType = VkImageViewType.VK_IMAGE_VIEW_TYPE_2D, |
||||
|
}; |
||||
|
_context.DeviceApi.CreateImageView(_context.DeviceHandle, ref imageViewCreateInfo, |
||||
|
IntPtr.Zero, out var imageView).ThrowOnError("vkCreateImageView"); |
||||
|
return imageView; |
||||
|
} |
||||
|
|
||||
|
private void Recreate() |
||||
|
{ |
||||
|
_context.DeviceApi.DeviceWaitIdle(_context.DeviceHandle); |
||||
|
_swapchain = CreateSwapchain(_context, _surface, out var extent, this); |
||||
|
_swapchainExtent = extent; |
||||
|
CreateSwapchainImages(); |
||||
|
} |
||||
|
|
||||
|
public bool EnsureSwapchainAvailable() |
||||
|
{ |
||||
|
if (Size != _surface.Size) |
||||
|
{ |
||||
|
Recreate(); |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public VulkanCommandBuffer StartPresentation() |
||||
|
{ |
||||
|
_nextImage = 0; |
||||
|
while (true) |
||||
|
{ |
||||
|
var acquireResult = _context.DeviceApi.AcquireNextImageKHR( |
||||
|
_context.DeviceHandle, |
||||
|
_swapchain, |
||||
|
ulong.MaxValue, |
||||
|
_semaphorePair.ImageAvailableSemaphore.Handle, |
||||
|
default, out _nextImage); |
||||
|
if (acquireResult is VkResult.VK_ERROR_OUT_OF_DATE_KHR or VkResult.VK_SUBOPTIMAL_KHR) |
||||
|
Recreate(); |
||||
|
else |
||||
|
{ |
||||
|
acquireResult.ThrowOnError("vkAcquireNextImageKHR"); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var commandBuffer = CommandBufferPool.CreateCommandBuffer(); |
||||
|
commandBuffer.BeginRecording(); |
||||
|
VulkanMemoryHelper.TransitionLayout(_context, commandBuffer, |
||||
|
_swapchainImages[_nextImage], VkImageLayout.VK_IMAGE_LAYOUT_UNDEFINED, |
||||
|
VkAccessFlags.VK_ACCESS_NONE, VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, |
||||
|
VkAccessFlags.VK_ACCESS_TRANSFER_WRITE_BIT, 1); |
||||
|
return commandBuffer; |
||||
|
} |
||||
|
|
||||
|
internal unsafe void BlitImageToCurrentImage(VulkanCommandBuffer commandBuffer, VulkanImage image) |
||||
|
{ |
||||
|
VulkanMemoryHelper.TransitionLayout(_context, commandBuffer, |
||||
|
image.Handle, image.CurrentLayout, VkAccessFlags.VK_ACCESS_NONE, |
||||
|
VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, |
||||
|
VkAccessFlags.VK_ACCESS_TRANSFER_READ_BIT, |
||||
|
image.MipLevels); |
||||
|
|
||||
|
var srcBlitRegion = new VkImageBlit |
||||
|
{ |
||||
|
srcOffsets2 = |
||||
|
{ |
||||
|
x = image.Size.Width, |
||||
|
y = image.Size.Height, |
||||
|
z = 1 |
||||
|
}, |
||||
|
dstOffsets2 = |
||||
|
{ |
||||
|
x = Size.Width, |
||||
|
y = Size.Height, |
||||
|
z = 1 |
||||
|
}, |
||||
|
srcSubresource = |
||||
|
{ |
||||
|
aspectMask = VkImageAspectFlags.VK_IMAGE_ASPECT_COLOR_BIT, |
||||
|
layerCount = 1 |
||||
|
}, |
||||
|
dstSubresource = |
||||
|
{ |
||||
|
aspectMask = VkImageAspectFlags.VK_IMAGE_ASPECT_COLOR_BIT, |
||||
|
layerCount = 1 |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
_context.DeviceApi.CmdBlitImage(commandBuffer.Handle, image.Handle, |
||||
|
VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, |
||||
|
_swapchainImages[_nextImage], |
||||
|
VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, |
||||
|
1, &srcBlitRegion, VkFilter.VK_FILTER_LINEAR); |
||||
|
|
||||
|
VulkanMemoryHelper.TransitionLayout(_context, commandBuffer, |
||||
|
image.Handle, VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, |
||||
|
VkAccessFlags.VK_ACCESS_TRANSFER_READ_BIT, |
||||
|
image.CurrentLayout, VkAccessFlags.VK_ACCESS_NONE, image.MipLevels); |
||||
|
} |
||||
|
|
||||
|
internal unsafe void EndPresentation(VulkanCommandBuffer commandBuffer) |
||||
|
{ |
||||
|
VulkanMemoryHelper.TransitionLayout(_context, commandBuffer, |
||||
|
_swapchainImages[_nextImage], |
||||
|
VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, |
||||
|
VkAccessFlags.VK_ACCESS_NONE, |
||||
|
VkImageLayout.VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, |
||||
|
VkAccessFlags.VK_ACCESS_NONE, |
||||
|
1); |
||||
|
commandBuffer.Submit(new[] { _semaphorePair.ImageAvailableSemaphore }, |
||||
|
new[] { VkPipelineStageFlags.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }, |
||||
|
new[] { _semaphorePair.RenderFinishedSemaphore }); |
||||
|
|
||||
|
var semaphore = _semaphorePair.RenderFinishedSemaphore.Handle; |
||||
|
var swapchain = _swapchain; |
||||
|
var nextImage = _nextImage; |
||||
|
|
||||
|
VkResult result; |
||||
|
var presentInfo = new VkPresentInfoKHR |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, |
||||
|
waitSemaphoreCount = 1, |
||||
|
pWaitSemaphores = &semaphore, |
||||
|
swapchainCount = 1, |
||||
|
pSwapchains = &swapchain, |
||||
|
pImageIndices = &nextImage, |
||||
|
pResults = &result |
||||
|
}; |
||||
|
|
||||
|
_context.DeviceApi.vkQueuePresentKHR(_context.MainQueueHandle, ref presentInfo) |
||||
|
.ThrowOnError("vkQueuePresentKHR"); |
||||
|
result.ThrowOnError("vkQueuePresentKHR"); |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_context.DeviceApi.DeviceWaitIdle(_context.DeviceHandle); |
||||
|
_semaphorePair?.Dispose(); |
||||
|
DestroyCurrentImageViews(); |
||||
|
DestroySwapchain(); |
||||
|
CommandBufferPool?.Dispose(); |
||||
|
CommandBufferPool = null!; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Vulkan.Interop; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan; |
||||
|
|
||||
|
internal struct VulkanFence : IDisposable |
||||
|
{ |
||||
|
private readonly IVulkanPlatformGraphicsContext _context; |
||||
|
private VkFence _handle; |
||||
|
|
||||
|
public VulkanFence(IVulkanPlatformGraphicsContext context, VkFenceCreateFlags flags) |
||||
|
{ |
||||
|
_context = context; |
||||
|
var fenceCreateInfo = new VkFenceCreateInfo |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, |
||||
|
flags = flags |
||||
|
}; |
||||
|
|
||||
|
_context.DeviceApi.CreateFence(_context.DeviceHandle, ref fenceCreateInfo, IntPtr.Zero, out _handle) |
||||
|
.ThrowOnError("vkCreateFence"); |
||||
|
} |
||||
|
|
||||
|
public VkFence Handle => _handle; |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_context.DeviceApi.DestroyFence(_context.DeviceHandle, _handle, IntPtr.Zero); |
||||
|
_handle = default; |
||||
|
} |
||||
|
|
||||
|
public bool IsSignaled => _context.DeviceApi.GetFenceStatus(_context.DeviceHandle, _handle) == VkResult.VK_SUCCESS; |
||||
|
|
||||
|
public unsafe void Wait(ulong timeout = ulong.MaxValue) |
||||
|
{ |
||||
|
VkFence fence = _handle; |
||||
|
_context.DeviceApi.WaitForFences(_context.DeviceHandle, 1, &fence, 1, timeout).ThrowOnError("vkWaitForFences"); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,197 @@ |
|||||
|
using System; |
||||
|
using System.Diagnostics; |
||||
|
using Avalonia.Vulkan.Interop; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan; |
||||
|
|
||||
|
internal class VulkanImageBase : IDisposable |
||||
|
{ |
||||
|
private IVulkanPlatformGraphicsContext _context; |
||||
|
|
||||
|
private VkAccessFlags _currentAccessFlags; |
||||
|
public VkImageUsageFlags UsageFlags { get; } |
||||
|
private VkImageView _imageView; |
||||
|
private VkDeviceMemory _imageMemory; |
||||
|
private VkImage _handle; |
||||
|
private VulkanCommandBufferPool _commandBufferPool; |
||||
|
internal VkImage Handle => _handle; |
||||
|
internal VkFormat Format { get; } |
||||
|
internal VkImageAspectFlags AspectFlags { get; private set; } |
||||
|
public uint MipLevels { get; private set; } |
||||
|
public PixelSize Size { get; } |
||||
|
public ulong MemorySize { get; private set; } |
||||
|
public VkImageLayout CurrentLayout { get; protected set; } |
||||
|
public VkDeviceMemory MemoryHandle => _imageMemory; |
||||
|
|
||||
|
public VkImageTiling Tiling => VkImageTiling.VK_IMAGE_TILING_OPTIMAL; |
||||
|
|
||||
|
public VulkanImageInfo ImageInfo => new() |
||||
|
{ |
||||
|
Handle = Handle.Handle, |
||||
|
PixelSize = Size, |
||||
|
Format = (uint)Format, |
||||
|
MemoryHandle = MemoryHandle.Handle, |
||||
|
MemorySize = MemorySize, |
||||
|
ViewHandle = _imageView.Handle, |
||||
|
UsageFlags = (uint)UsageFlags, |
||||
|
Layout = (uint)CurrentLayout, |
||||
|
Tiling = (uint)Tiling, |
||||
|
LevelCount = MipLevels, |
||||
|
SampleCount = 1, |
||||
|
IsProtected = false |
||||
|
}; |
||||
|
|
||||
|
public struct MemoryImportInfo |
||||
|
{ |
||||
|
public IntPtr Next; |
||||
|
public ulong MemorySize; |
||||
|
public ulong MemoryOffset; |
||||
|
} |
||||
|
|
||||
|
public VulkanImageBase(IVulkanPlatformGraphicsContext context, |
||||
|
VulkanCommandBufferPool commandBufferPool, |
||||
|
VkFormat format, PixelSize size, uint mipLevels = 0) |
||||
|
{ |
||||
|
Format = format; |
||||
|
Size = size; |
||||
|
MipLevels = mipLevels; |
||||
|
_context = context; |
||||
|
_commandBufferPool = commandBufferPool; |
||||
|
UsageFlags = VkImageUsageFlags.VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
||||
|
| VkImageUsageFlags.VK_IMAGE_USAGE_TRANSFER_DST_BIT |
||||
|
| VkImageUsageFlags.VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
||||
|
| VkImageUsageFlags.VK_IMAGE_USAGE_SAMPLED_BIT; |
||||
|
} |
||||
|
|
||||
|
protected virtual VkDeviceMemory CreateMemory(VkImage image, ulong size, uint memoryTypeBits) |
||||
|
{ |
||||
|
var memoryAllocateInfo = new VkMemoryAllocateInfo |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, |
||||
|
allocationSize = size, |
||||
|
memoryTypeIndex = (uint)VulkanMemoryHelper.FindSuitableMemoryTypeIndex(_context, |
||||
|
memoryTypeBits, |
||||
|
VkMemoryPropertyFlags.VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT), |
||||
|
}; |
||||
|
|
||||
|
_context.DeviceApi.AllocateMemory(_context.DeviceHandle, ref memoryAllocateInfo, IntPtr.Zero, |
||||
|
out var imageMemory).ThrowOnError("vkAllocateMemory"); |
||||
|
return imageMemory; |
||||
|
} |
||||
|
|
||||
|
public unsafe void Initialize(void* pNext) |
||||
|
{ |
||||
|
if (Handle.Handle != 0) |
||||
|
return; |
||||
|
MipLevels = MipLevels != 0 ? MipLevels : (uint)Math.Floor(Math.Log(Math.Max(Size.Width, Size.Height), 2)); |
||||
|
var createInfo = new VkImageCreateInfo |
||||
|
{ |
||||
|
pNext = pNext, |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, |
||||
|
imageType = VkImageType.VK_IMAGE_TYPE_2D, |
||||
|
format = Format, |
||||
|
extent = new VkExtent3D |
||||
|
{ |
||||
|
depth = 1, |
||||
|
width = (uint)Size.Width, |
||||
|
height = (uint)Size.Height |
||||
|
}, |
||||
|
mipLevels = MipLevels, |
||||
|
arrayLayers = 1, |
||||
|
samples = VkSampleCountFlags.VK_SAMPLE_COUNT_1_BIT, |
||||
|
tiling = Tiling, |
||||
|
usage = UsageFlags, |
||||
|
sharingMode = VkSharingMode.VK_SHARING_MODE_EXCLUSIVE, |
||||
|
initialLayout = VkImageLayout.VK_IMAGE_LAYOUT_UNDEFINED, |
||||
|
flags = VkImageCreateFlags.VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT |
||||
|
}; |
||||
|
|
||||
|
_context.DeviceApi.CreateImage(_context.DeviceHandle, ref createInfo, IntPtr.Zero, out _handle) |
||||
|
.ThrowOnError("vkCreateImage"); |
||||
|
|
||||
|
_context.DeviceApi.GetImageMemoryRequirements(_context.DeviceHandle, _handle, out var memoryRequirements); |
||||
|
try |
||||
|
{ |
||||
|
_imageMemory = CreateMemory(_handle, memoryRequirements.size, memoryRequirements.memoryTypeBits); |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
_context.DeviceApi.DestroyImage(_context.DeviceHandle, _handle, IntPtr.Zero); |
||||
|
throw; |
||||
|
} |
||||
|
|
||||
|
_context.DeviceApi.BindImageMemory(_context.DeviceHandle, _handle, _imageMemory, 0) |
||||
|
.ThrowOnError("vkBindImageMemory"); |
||||
|
|
||||
|
MemorySize = memoryRequirements.size; |
||||
|
AspectFlags = VkImageAspectFlags.VK_IMAGE_ASPECT_COLOR_BIT; |
||||
|
|
||||
|
var imageViewCreateInfo = new VkImageViewCreateInfo |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, |
||||
|
components = new(), |
||||
|
subresourceRange = new() |
||||
|
{ |
||||
|
aspectMask = AspectFlags, |
||||
|
levelCount = MipLevels, |
||||
|
layerCount = 1 |
||||
|
}, |
||||
|
format = Format, |
||||
|
image = _handle, |
||||
|
viewType = VkImageViewType.VK_IMAGE_VIEW_TYPE_2D |
||||
|
}; |
||||
|
_context.DeviceApi.CreateImageView(_context.DeviceHandle, ref imageViewCreateInfo, |
||||
|
IntPtr.Zero, out _imageView).ThrowOnError("vkCreateImageView"); |
||||
|
CurrentLayout = VkImageLayout.VK_IMAGE_LAYOUT_UNDEFINED; |
||||
|
} |
||||
|
|
||||
|
internal void TransitionLayout(VkImageLayout destinationLayout, VkAccessFlags destinationAccessFlags) |
||||
|
{ |
||||
|
var commandBuffer = _commandBufferPool!.CreateCommandBuffer(); |
||||
|
commandBuffer.BeginRecording(); |
||||
|
VulkanMemoryHelper.TransitionLayout(_context, commandBuffer, Handle, |
||||
|
CurrentLayout, _currentAccessFlags, destinationLayout, destinationAccessFlags, |
||||
|
MipLevels); |
||||
|
commandBuffer.EndRecording(); |
||||
|
commandBuffer.Submit(); |
||||
|
CurrentLayout = destinationLayout; |
||||
|
_currentAccessFlags = destinationAccessFlags; |
||||
|
} |
||||
|
|
||||
|
public void TransitionLayout(uint destinationLayout, uint destinationAccessFlags) |
||||
|
{ |
||||
|
TransitionLayout((VkImageLayout)destinationLayout, (VkAccessFlags)destinationAccessFlags); |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
var api = _context.DeviceApi; |
||||
|
var d = _context.DeviceHandle; |
||||
|
if (_imageView.Handle != 0) |
||||
|
{ |
||||
|
api.DestroyImageView(d, _imageView, IntPtr.Zero); |
||||
|
_imageView = default; |
||||
|
} |
||||
|
if (_handle.Handle != 0) |
||||
|
{ |
||||
|
api.DestroyImage(d, _handle, IntPtr.Zero); |
||||
|
_handle = default; |
||||
|
} |
||||
|
if (_imageMemory.Handle != 0) |
||||
|
{ |
||||
|
api.FreeMemory(d, _imageMemory, IntPtr.Zero); |
||||
|
_imageMemory = default; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
unsafe class VulkanImage : VulkanImageBase |
||||
|
{ |
||||
|
public VulkanImage(IVulkanPlatformGraphicsContext context, VulkanCommandBufferPool commandBufferPool, |
||||
|
VkFormat format, PixelSize size, uint mipLevels = 0) : base(context, commandBufferPool, format, size, mipLevels) |
||||
|
{ |
||||
|
Initialize(null); |
||||
|
TransitionLayout(VkImageLayout.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VkAccessFlags.VK_ACCESS_NONE_KHR); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,179 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using Avalonia.Reactive; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using Avalonia.Platform.Interop; |
||||
|
using Avalonia.Vulkan.Interop; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan; |
||||
|
internal class VulkanInstance : IVulkanInstance |
||||
|
{ |
||||
|
private readonly VkGetInstanceProcAddressDelegate _getProcAddress; |
||||
|
private readonly VulkanInstanceApi _api; |
||||
|
|
||||
|
public VulkanInstance(VkInstance handle, VkGetInstanceProcAddressDelegate getProcAddress, string[] enabledExtensions) |
||||
|
{ |
||||
|
Handle = handle; |
||||
|
_getProcAddress = getProcAddress; |
||||
|
_api = new VulkanInstanceApi(this); |
||||
|
EnabledExtensions = enabledExtensions; |
||||
|
} |
||||
|
|
||||
|
internal static unsafe IVulkanInstance Create( |
||||
|
VulkanInstanceCreationOptions options, |
||||
|
VulkanPlatformSpecificOptions platformOptions) |
||||
|
{ |
||||
|
var getProcAddress = options.CustomGetProcAddressDelegate ?? |
||||
|
platformOptions.GetProcAddressDelegate ?? |
||||
|
throw new ArgumentException("No VkGetInstanceProcAddr provided"); |
||||
|
|
||||
|
|
||||
|
using var name = new Utf8Buffer(options.ApplicationName ?? "AvaloniaUI"); |
||||
|
using var engineName = new Utf8Buffer("AvaloniaUI"); |
||||
|
|
||||
|
var gapi = new UnmanagedInterop.VulkanGlobalApi(getProcAddress); |
||||
|
|
||||
|
var supportedExtensions = GetSupportedExtensions(gapi); |
||||
|
var appInfo = new VkApplicationInfo() |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_APPLICATION_INFO, |
||||
|
apiVersion = VulkanHelpers.MakeVersion(options.VulkanVersion), |
||||
|
applicationVersion = VulkanHelpers.MakeVersion(1, 0, 0), |
||||
|
engineVersion = VulkanHelpers.MakeVersion(1, 0, 0), |
||||
|
pApplicationName = name, |
||||
|
pEngineName = engineName, |
||||
|
}; |
||||
|
|
||||
|
var enabledExtensions = new HashSet<string>(options.InstanceExtensions |
||||
|
.Concat(platformOptions.RequiredInstanceExtensions) |
||||
|
.Append("VK_KHR_surface")); |
||||
|
|
||||
|
var enabledLayers = options.EnabledLayers.ToList(); |
||||
|
|
||||
|
void AddExtensionsIfSupported(params string[] names) |
||||
|
{ |
||||
|
if(names.All(n=>supportedExtensions.Contains(n))) |
||||
|
foreach (var n in names) |
||||
|
enabledExtensions.Add(n); |
||||
|
} |
||||
|
|
||||
|
if (options.UseDebug) |
||||
|
{ |
||||
|
AddExtensionsIfSupported("VK_EXT_debug_utils"); |
||||
|
if (IsLayerAvailable(gapi, "VK_LAYER_KHRONOS_validation")) |
||||
|
enabledLayers.Add("VK_LAYER_KHRONOS_validation"); |
||||
|
} |
||||
|
|
||||
|
AddExtensionsIfSupported(VulkanExternalObjectsFeature.RequiredInstanceExtensions); |
||||
|
|
||||
|
using var enabledExtensionBuffers = new Utf8BufferArray( |
||||
|
enabledExtensions |
||||
|
.Where(x => !string.IsNullOrWhiteSpace(x)) |
||||
|
.Distinct()); |
||||
|
|
||||
|
using var enabledLayersBuffers = new Utf8BufferArray(enabledLayers |
||||
|
.Where(x => !string.IsNullOrWhiteSpace(x)) |
||||
|
.Distinct()); |
||||
|
|
||||
|
|
||||
|
var createInfo = new VkInstanceCreateInfo() |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, |
||||
|
pApplicationInfo = &appInfo, |
||||
|
ppEnabledLayerNames = enabledLayersBuffers, |
||||
|
enabledLayerCount = enabledLayersBuffers.UCount, |
||||
|
ppEnabledExtensionNames = enabledExtensionBuffers, |
||||
|
enabledExtensionCount = enabledExtensionBuffers.UCount |
||||
|
}; |
||||
|
|
||||
|
gapi.vkCreateInstance(ref createInfo, IntPtr.Zero, out var pInstance) |
||||
|
.ThrowOnError(nameof(gapi.vkCreateInstance)); |
||||
|
|
||||
|
var instance = new VulkanInstance(pInstance, getProcAddress, enabledExtensions.ToArray()); |
||||
|
var instanceApi = new VulkanInstanceApi(instance); |
||||
|
|
||||
|
if (options.UseDebug) |
||||
|
{ |
||||
|
var debugCreateInfo = new VkDebugUtilsMessengerCreateInfoEXT |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, |
||||
|
messageSeverity = VkDebugUtilsMessageSeverityFlagsEXT.VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
||||
|
|VkDebugUtilsMessageSeverityFlagsEXT.VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
||||
|
|VkDebugUtilsMessageSeverityFlagsEXT.VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
||||
|
|VkDebugUtilsMessageSeverityFlagsEXT.VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, |
||||
|
messageType = VkDebugUtilsMessageTypeFlagsEXT.VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
||||
|
|VkDebugUtilsMessageTypeFlagsEXT.VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
||||
|
|VkDebugUtilsMessageTypeFlagsEXT.VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, |
||||
|
pfnUserCallback = VulkanDebugLogger.CallbackPtr |
||||
|
}; |
||||
|
|
||||
|
instanceApi.CreateDebugUtilsMessengerEXT(pInstance, ref debugCreateInfo, IntPtr.Zero, out var messenger); |
||||
|
} |
||||
|
|
||||
|
return instance; |
||||
|
} |
||||
|
|
||||
|
private static unsafe bool IsLayerAvailable(VulkanGlobalApi api, string layerName) |
||||
|
{ |
||||
|
uint layerPropertiesCount = 0; |
||||
|
|
||||
|
api.EnumerateInstanceLayerProperties(ref layerPropertiesCount, null) |
||||
|
.ThrowOnError("vkEnumerateInstanceLayerProperties"); |
||||
|
|
||||
|
var layerProperties = new VkLayerProperties[layerPropertiesCount]; |
||||
|
|
||||
|
fixed (VkLayerProperties* pLayerProperties = layerProperties) |
||||
|
{ |
||||
|
api.EnumerateInstanceLayerProperties(ref layerPropertiesCount, pLayerProperties) |
||||
|
.ThrowOnError("vkEnumerateInstanceLayerProperties"); |
||||
|
|
||||
|
for (var i = 0; i < layerPropertiesCount; i++) |
||||
|
{ |
||||
|
var currentLayerName = Marshal.PtrToStringAnsi((IntPtr)pLayerProperties[i].layerName); |
||||
|
if (currentLayerName == layerName) return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
private static unsafe HashSet<string> GetSupportedExtensions(VulkanGlobalApi api) |
||||
|
{ |
||||
|
var supportedExtensions = new HashSet<string>(); |
||||
|
uint supportedExtensionCount = 0; |
||||
|
api.vkEnumerateInstanceExtensionProperties(IntPtr.Zero, &supportedExtensionCount, null); |
||||
|
if (supportedExtensionCount > 0) |
||||
|
{ |
||||
|
var ptr = (VkExtensionProperties*)Marshal.AllocHGlobal(Unsafe.SizeOf<VkExtensionProperties>() * |
||||
|
(int)supportedExtensionCount); |
||||
|
try |
||||
|
{ |
||||
|
api.vkEnumerateInstanceExtensionProperties(IntPtr.Zero, &supportedExtensionCount, ptr) |
||||
|
.ThrowOnError("vkEnumerateInstanceExtensionProperties"); |
||||
|
for (var c = 0; c < supportedExtensionCount; c++) |
||||
|
supportedExtensions.Add(Marshal.PtrToStringAnsi((IntPtr)ptr[c].extensionName) ?? ""); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
Marshal.FreeHGlobal((IntPtr)ptr); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return supportedExtensions; |
||||
|
} |
||||
|
|
||||
|
public VkInstance Handle { get; } |
||||
|
IntPtr IVulkanInstance.Handle => Handle.Handle; |
||||
|
|
||||
|
public IntPtr GetInstanceProcAddress(IntPtr instance, string name) => _getProcAddress(instance, name); |
||||
|
public IntPtr GetDeviceProcAddress(IntPtr device, string name) |
||||
|
{ |
||||
|
using var buf = new Utf8Buffer(name); |
||||
|
return _api.GetDeviceProcAddr(new(device), buf); |
||||
|
} |
||||
|
|
||||
|
public IEnumerable<string> EnabledExtensions { get; } |
||||
|
} |
||||
@ -0,0 +1,69 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan.Interop; |
||||
|
|
||||
|
|
||||
|
internal class VulkanKhrSurface : IDisposable |
||||
|
{ |
||||
|
private readonly IVulkanPlatformGraphicsContext _context; |
||||
|
private readonly IVulkanKhrSurfacePlatformSurface _surfaceInfo; |
||||
|
private VkSurfaceKHR _handle; |
||||
|
public VkSurfaceKHR Handle => _handle; |
||||
|
|
||||
|
public VulkanKhrSurface(IVulkanPlatformGraphicsContext context, IVulkanKhrSurfacePlatformSurface surfaceInfo) |
||||
|
{ |
||||
|
_context = context; |
||||
|
_surfaceInfo = surfaceInfo; |
||||
|
_handle = new VkSurfaceKHR(surfaceInfo.CreateSurface(context)); |
||||
|
} |
||||
|
|
||||
|
internal bool CanSurfacePresent() |
||||
|
{ |
||||
|
_context.InstanceApi.GetPhysicalDeviceSurfaceSupportKHR(_context.PhysicalDeviceHandle, |
||||
|
_context.GraphicsQueueFamilyIndex, _handle, out var isSupported) |
||||
|
.ThrowOnError("vkGetPhysicalDeviceSurfaceSupportKHR"); |
||||
|
return isSupported != 0; |
||||
|
} |
||||
|
|
||||
|
internal unsafe VkSurfaceFormatKHR GetSurfaceFormat() |
||||
|
{ |
||||
|
uint surfaceFormatsCount = 0; |
||||
|
_context.InstanceApi.GetPhysicalDeviceSurfaceFormatsKHR(_context.PhysicalDeviceHandle, |
||||
|
_handle, ref surfaceFormatsCount, null) |
||||
|
.ThrowOnError("vkGetPhysicalDeviceSurfaceFormatsKHR"); |
||||
|
|
||||
|
if (surfaceFormatsCount == 0) |
||||
|
throw new VulkanException("vkGetPhysicalDeviceSurfaceFormatsKHR returned 0 formats"); |
||||
|
|
||||
|
var surfaceFormats = stackalloc VkSurfaceFormatKHR[(int)surfaceFormatsCount]; |
||||
|
_context.InstanceApi.GetPhysicalDeviceSurfaceFormatsKHR(_context.PhysicalDeviceHandle, |
||||
|
_handle, ref surfaceFormatsCount, surfaceFormats) |
||||
|
.ThrowOnError("vkGetPhysicalDeviceSurfaceFormatsKHR"); |
||||
|
|
||||
|
|
||||
|
if (surfaceFormatsCount == 1 && surfaceFormats[0].format == VkFormat.VK_FORMAT_UNDEFINED) |
||||
|
return new VkSurfaceFormatKHR |
||||
|
{ |
||||
|
format = VkFormat.VK_FORMAT_B8G8R8A8_UNORM, |
||||
|
colorSpace = VkColorSpaceKHR.VK_COLOR_SPACE_SRGB_NONLINEAR_KHR |
||||
|
}; |
||||
|
for (var c = 0; c < surfaceFormatsCount; c++) |
||||
|
{ |
||||
|
if (surfaceFormats[c].format == VkFormat.VK_FORMAT_B8G8R8A8_UNORM |
||||
|
&& surfaceFormats[c].colorSpace == VkColorSpaceKHR.VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) |
||||
|
return surfaceFormats[c]; |
||||
|
} |
||||
|
|
||||
|
return surfaceFormats[0]; |
||||
|
} |
||||
|
|
||||
|
public PixelSize Size => _surfaceInfo.Size; |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
if (_handle.Handle != 0) |
||||
|
_context.InstanceApi.DestroySurfaceKHR(_context.InstanceHandle, _handle, IntPtr.Zero); |
||||
|
_handle = default; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,67 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan.Interop; |
||||
|
|
||||
|
internal static class VulkanMemoryHelper |
||||
|
{ |
||||
|
internal static int FindSuitableMemoryTypeIndex(IVulkanPlatformGraphicsContext context, uint memoryTypeBits, |
||||
|
VkMemoryPropertyFlags flags) |
||||
|
{ |
||||
|
context.InstanceApi.GetPhysicalDeviceMemoryProperties(context.PhysicalDeviceHandle, |
||||
|
out var properties); |
||||
|
for (var i = 0; i < properties.memoryTypeCount; i++) |
||||
|
{ |
||||
|
var type = properties.memoryTypes[i]; |
||||
|
|
||||
|
if ((memoryTypeBits & (1 << i)) != 0 && type.propertyFlags.HasAllFlags(flags)) |
||||
|
return i; |
||||
|
} |
||||
|
|
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
internal static unsafe void TransitionLayout(IVulkanPlatformGraphicsContext context, |
||||
|
VulkanCommandBuffer commandBuffer, |
||||
|
VkImage image, |
||||
|
VkImageLayout sourceLayout, |
||||
|
VkAccessFlags sourceAccessMask, |
||||
|
VkImageLayout destinationLayout, |
||||
|
VkAccessFlags destinationAccessMask, |
||||
|
uint mipLevels) |
||||
|
{ |
||||
|
var subresourceRange = new VkImageSubresourceRange |
||||
|
{ |
||||
|
aspectMask = VkImageAspectFlags.VK_IMAGE_ASPECT_COLOR_BIT, |
||||
|
levelCount = mipLevels, |
||||
|
layerCount = 1 |
||||
|
}; |
||||
|
|
||||
|
var barrier = new VkImageMemoryBarrier |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, |
||||
|
srcAccessMask = sourceAccessMask, |
||||
|
dstAccessMask = destinationAccessMask, |
||||
|
oldLayout = sourceLayout, |
||||
|
newLayout = destinationLayout, |
||||
|
srcQueueFamilyIndex = VulkanHelpers.QueueFamilyIgnored, |
||||
|
dstQueueFamilyIndex = VulkanHelpers.QueueFamilyIgnored, |
||||
|
image = image, |
||||
|
subresourceRange = subresourceRange |
||||
|
}; |
||||
|
|
||||
|
context.DeviceApi.CmdPipelineBarrier( |
||||
|
commandBuffer.Handle, |
||||
|
VkPipelineStageFlags.VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, |
||||
|
VkPipelineStageFlags.VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, |
||||
|
0, |
||||
|
0, |
||||
|
IntPtr.Zero, |
||||
|
0, |
||||
|
IntPtr.Zero, |
||||
|
1, |
||||
|
&barrier); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,58 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Vulkan.Interop; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan; |
||||
|
|
||||
|
class VulkanSemaphore : IDisposable |
||||
|
{ |
||||
|
private readonly IVulkanPlatformGraphicsContext _context; |
||||
|
|
||||
|
private VkSemaphore _handle; |
||||
|
public VkSemaphore Handle => _handle; |
||||
|
|
||||
|
public VulkanSemaphore(IVulkanPlatformGraphicsContext context) |
||||
|
{ |
||||
|
_context = context; |
||||
|
var info = new VkSemaphoreCreateInfo |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO |
||||
|
}; |
||||
|
_context.DeviceApi.CreateSemaphore(context.DeviceHandle, ref info, IntPtr.Zero, out _handle) |
||||
|
.ThrowOnError("vkCreateSemaphore"); |
||||
|
} |
||||
|
|
||||
|
public VulkanSemaphore(IVulkanPlatformGraphicsContext context, VkSemaphore handle) |
||||
|
{ |
||||
|
_context = context; |
||||
|
_handle = handle; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
if (_handle.Handle != 0) |
||||
|
{ |
||||
|
_context.DeviceApi.DestroySemaphore(_context.DeviceHandle, _handle, IntPtr.Zero); |
||||
|
_handle = default; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal class VulkanSemaphorePair : IDisposable |
||||
|
{ |
||||
|
|
||||
|
public unsafe VulkanSemaphorePair(IVulkanPlatformGraphicsContext context) |
||||
|
{ |
||||
|
ImageAvailableSemaphore = new VulkanSemaphore(context); |
||||
|
RenderFinishedSemaphore = new VulkanSemaphore(context); |
||||
|
} |
||||
|
|
||||
|
internal VulkanSemaphore ImageAvailableSemaphore { get; } |
||||
|
internal VulkanSemaphore RenderFinishedSemaphore { get; } |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
ImageAvailableSemaphore.Dispose(); |
||||
|
RenderFinishedSemaphore.Dispose(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using Avalonia.Platform.Interop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan.Interop; |
||||
|
|
||||
|
internal unsafe class Utf8BufferArray : IDisposable |
||||
|
{ |
||||
|
private readonly List<Utf8Buffer> _buffers; |
||||
|
private byte** _bufferArray; |
||||
|
|
||||
|
public Utf8BufferArray(IEnumerable<string> strings) |
||||
|
{ |
||||
|
_buffers = strings.Select(x => new Utf8Buffer(x)).ToList(); |
||||
|
_bufferArray = (byte**)Marshal.AllocHGlobal(_buffers.Count * IntPtr.Size); |
||||
|
for (var c = 0; c < _buffers.Count; c++) |
||||
|
_bufferArray[c] = _buffers[c]; |
||||
|
} |
||||
|
|
||||
|
public static unsafe implicit operator byte**(Utf8BufferArray a) => a._bufferArray; |
||||
|
|
||||
|
public int Count => _buffers.Count; |
||||
|
public uint UCount => (uint)Count; |
||||
|
|
||||
|
public void Dispose() => Dispose(true); |
||||
|
void Dispose(bool disposing) |
||||
|
{ |
||||
|
if (_bufferArray != null) |
||||
|
Marshal.FreeHGlobal(new IntPtr(_bufferArray)); |
||||
|
_bufferArray = null; |
||||
|
if (disposing) |
||||
|
{ |
||||
|
foreach (var b in _buffers) |
||||
|
b.Dispose(); |
||||
|
_buffers.Clear(); |
||||
|
GC.SuppressFinalize(this); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
~Utf8BufferArray() |
||||
|
{ |
||||
|
Dispose(false); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,154 @@ |
|||||
|
using System; |
||||
|
using Avalonia.SourceGenerator; |
||||
|
using Avalonia.Vulkan.Interop; |
||||
|
using uint32_t = System.UInt32; |
||||
|
using uint64_t = System.UInt64; |
||||
|
using VkBool32 = System.UInt32; |
||||
|
using VkDeviceSize = System.UInt64; |
||||
|
|
||||
|
namespace Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
|
||||
|
internal unsafe partial class VulkanDeviceApi |
||||
|
{ |
||||
|
public VulkanDeviceApi(IVulkanDevice device) |
||||
|
{ |
||||
|
Initialize(name => |
||||
|
{ |
||||
|
var addr = device.Instance.GetDeviceProcAddress(device.Handle, name); |
||||
|
if (addr != IntPtr.Zero) |
||||
|
return addr; |
||||
|
return device.Instance.GetInstanceProcAddress(device.Instance.Handle, name); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
[GetProcAddress("vkCreateFence")] |
||||
|
public partial VkResult CreateFence(VkDevice device, |
||||
|
ref VkFenceCreateInfo pCreateInfo, |
||||
|
IntPtr pAllocator, |
||||
|
out VkFence pFence); |
||||
|
|
||||
|
[GetProcAddress("vkDestroyFence")] |
||||
|
public partial void DestroyFence(VkDevice device, VkFence fence, IntPtr pAllocator); |
||||
|
|
||||
|
[GetProcAddress("vkCreateCommandPool")] |
||||
|
public partial VkResult CreateCommandPool(VkDevice device, ref VkCommandPoolCreateInfo pCreateInfo, |
||||
|
IntPtr pAllocator, out VkCommandPool pCommandPool); |
||||
|
|
||||
|
[GetProcAddress("vkDestroyCommandPool")] |
||||
|
public partial void DestroyCommandPool(VkDevice device, VkCommandPool pool, IntPtr pAllocator); |
||||
|
|
||||
|
[GetProcAddress("vkAllocateCommandBuffers")] |
||||
|
public partial VkResult AllocateCommandBuffers(VkDevice device, |
||||
|
ref VkCommandBufferAllocateInfo pAllocateInfo, VkCommandBuffer* pCommandBuffers); |
||||
|
|
||||
|
[GetProcAddress("vkFreeCommandBuffers")] |
||||
|
public partial void FreeCommandBuffers(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, |
||||
|
VkCommandBuffer* pCommandBuffers); |
||||
|
|
||||
|
[GetProcAddress("vkWaitForFences")] |
||||
|
public partial VkResult WaitForFences(VkDevice device, uint32_t fenceCount, VkFence* pFences, VkBool32 waitAll, |
||||
|
uint64_t timeout); |
||||
|
|
||||
|
[GetProcAddress("vkGetFenceStatus")] |
||||
|
public partial VkResult GetFenceStatus(VkDevice device, VkFence fence); |
||||
|
|
||||
|
[GetProcAddress("vkBeginCommandBuffer")] |
||||
|
public partial VkResult BeginCommandBuffer(VkCommandBuffer commandBuffer, ref VkCommandBufferBeginInfo pBeginInfo); |
||||
|
|
||||
|
[GetProcAddress("vkEndCommandBuffer")] |
||||
|
public partial VkResult EndCommandBuffer(VkCommandBuffer commandBuffer); |
||||
|
|
||||
|
[GetProcAddress("vkCreateSemaphore")] |
||||
|
public partial VkResult CreateSemaphore(VkDevice device, ref VkSemaphoreCreateInfo pCreateInfo, |
||||
|
IntPtr pAllocator, out VkSemaphore pSemaphore); |
||||
|
|
||||
|
[GetProcAddress("vkDestroySemaphore")] |
||||
|
public partial void DestroySemaphore(VkDevice device, VkSemaphore semaphore, IntPtr pAllocator); |
||||
|
|
||||
|
[GetProcAddress("vkResetFences")] |
||||
|
public partial VkResult ResetFences(VkDevice device, uint32_t fenceCount, VkFence* pFences); |
||||
|
|
||||
|
[GetProcAddress("vkQueueSubmit")] |
||||
|
public partial VkResult QueueSubmit(VkQueue queue, uint32_t submitCount, VkSubmitInfo* pSubmits, |
||||
|
VkFence fence); |
||||
|
|
||||
|
[GetProcAddress("vkCreateImage")] |
||||
|
public partial VkResult CreateImage(VkDevice device, ref VkImageCreateInfo pCreateInfo, IntPtr pAllocator, |
||||
|
out VkImage pImage); |
||||
|
|
||||
|
[GetProcAddress("vkDestroyImage")] |
||||
|
public partial void DestroyImage(VkDevice device, VkImage image, IntPtr pAllocator); |
||||
|
|
||||
|
[GetProcAddress("vkGetImageMemoryRequirements")] |
||||
|
public partial void GetImageMemoryRequirements(VkDevice device, VkImage image, |
||||
|
out VkMemoryRequirements pMemoryRequirements); |
||||
|
|
||||
|
[GetProcAddress("vkAllocateMemory")] |
||||
|
public partial VkResult AllocateMemory(VkDevice device, ref VkMemoryAllocateInfo pAllocateInfo, IntPtr pAllocator, |
||||
|
out VkDeviceMemory pMemory); |
||||
|
|
||||
|
[GetProcAddress("vkFreeMemory")] |
||||
|
public partial void FreeMemory(VkDevice device, VkDeviceMemory memory, IntPtr pAllocator); |
||||
|
|
||||
|
[GetProcAddress("vkBindImageMemory")] |
||||
|
public partial VkResult BindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory, |
||||
|
VkDeviceSize memoryOffset); |
||||
|
|
||||
|
[GetProcAddress("vkCreateImageView")] |
||||
|
public partial VkResult CreateImageView(VkDevice device, ref VkImageViewCreateInfo pCreateInfo, IntPtr pAllocator, |
||||
|
out VkImageView pView); |
||||
|
|
||||
|
[GetProcAddress("vkDestroyImageView")] |
||||
|
public partial void DestroyImageView(VkDevice device, VkImageView imageView, IntPtr pAllocator); |
||||
|
|
||||
|
|
||||
|
[GetProcAddress("vkCmdPipelineBarrier")] |
||||
|
public partial void CmdPipelineBarrier(VkCommandBuffer commandBuffer, |
||||
|
VkPipelineStageFlags srcStageMask, |
||||
|
VkPipelineStageFlags dstStageMask, |
||||
|
VkDependencyFlags dependencyFlags, |
||||
|
uint32_t memoryBarrierCount, |
||||
|
IntPtr pMemoryBarriers, |
||||
|
uint32_t bufferMemoryBarrierCount, |
||||
|
IntPtr pBufferMemoryBarriers, |
||||
|
uint32_t imageMemoryBarrierCount, |
||||
|
VkImageMemoryBarrier* pImageMemoryBarriers); |
||||
|
|
||||
|
[GetProcAddress("vkCreateSwapchainKHR")] |
||||
|
public partial VkResult CreateSwapchainKHR(VkDevice device, ref VkSwapchainCreateInfoKHR pCreateInfo, |
||||
|
IntPtr pAllocator, out VkSwapchainKHR pSwapchain); |
||||
|
|
||||
|
[GetProcAddress("vkDestroySwapchainKHR")] |
||||
|
public partial void DestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, IntPtr pAllocator); |
||||
|
|
||||
|
[GetProcAddress("vkGetSwapchainImagesKHR")] |
||||
|
public partial VkResult GetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, ref uint32_t pSwapchainImageCount, |
||||
|
VkImage* pSwapchainImages); |
||||
|
|
||||
|
[GetProcAddress("vkDeviceWaitIdle")] |
||||
|
public partial VkResult DeviceWaitIdle(VkDevice device); |
||||
|
|
||||
|
[GetProcAddress("vkQueueWaitIdle")] |
||||
|
public partial VkResult QueueWaitIdle(VkQueue queue); |
||||
|
|
||||
|
[GetProcAddress("vkAcquireNextImageKHR")] |
||||
|
public partial VkResult AcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, |
||||
|
VkSemaphore semaphore, VkFence fence, out uint32_t pImageIndex); |
||||
|
|
||||
|
[GetProcAddress("vkCmdBlitImage")] |
||||
|
public partial void CmdBlitImage(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, |
||||
|
VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, VkImageBlit* pRegions, VkFilter filter); |
||||
|
|
||||
|
[GetProcAddress("vkQueuePresentKHR")] |
||||
|
public partial VkResult vkQueuePresentKHR(VkQueue queue, ref VkPresentInfoKHR pPresentInfo); |
||||
|
|
||||
|
[GetProcAddress("vkImportSemaphoreFdKHR", true)] |
||||
|
public partial VkResult ImportSemaphoreFdKHR(VkDevice device, VkImportSemaphoreFdInfoKHR* pImportSemaphoreFdInfo); |
||||
|
|
||||
|
[GetProcAddress("vkImportSemaphoreWin32HandleKHR", true)] |
||||
|
public partial VkResult ImportSemaphoreWin32HandleKHR(VkDevice device, |
||||
|
VkImportSemaphoreWin32HandleInfoKHR* pImportSemaphoreWin32HandleInfo); |
||||
|
|
||||
|
|
||||
|
} |
||||
File diff suppressed because it is too large
@ -0,0 +1,29 @@ |
|||||
|
using System; |
||||
|
using Avalonia.SourceGenerator; |
||||
|
using Avalonia.Vulkan.Interop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
internal unsafe partial class VulkanGlobalApi |
||||
|
{ |
||||
|
private readonly VkGetInstanceProcAddressDelegate _vkGetProcAddress; |
||||
|
|
||||
|
public VulkanGlobalApi(VkGetInstanceProcAddressDelegate vkGetProcAddress) |
||||
|
{ |
||||
|
_vkGetProcAddress = vkGetProcAddress; |
||||
|
Initialize(name => vkGetProcAddress(IntPtr.Zero, name)); |
||||
|
} |
||||
|
|
||||
|
public IntPtr GetProcAddress(VkInstance instance, string name) => _vkGetProcAddress(instance.Handle, name); |
||||
|
|
||||
|
|
||||
|
[GetProcAddress("vkEnumerateInstanceLayerProperties")] |
||||
|
public partial VkResult EnumerateInstanceLayerProperties(ref uint pPropertyCount, VkLayerProperties* pProperties); |
||||
|
|
||||
|
[GetProcAddress("vkCreateInstance")] |
||||
|
public partial VkResult vkCreateInstance(ref VkInstanceCreateInfo pCreateInfo, IntPtr pAllocator, out VkInstance pInstance); |
||||
|
|
||||
|
[GetProcAddress("vkEnumerateInstanceExtensionProperties")] |
||||
|
public partial VkResult vkEnumerateInstanceExtensionProperties(IntPtr pLayerName, uint* pPropertyCount, |
||||
|
VkExtensionProperties* pProperties); |
||||
|
} |
||||
@ -0,0 +1,82 @@ |
|||||
|
using System; |
||||
|
using Avalonia.SourceGenerator; |
||||
|
using Avalonia.Vulkan.Interop; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
using uint32_t = System.UInt32; |
||||
|
using VkBool32 = System.UInt32; |
||||
|
namespace Avalonia.Vulkan; |
||||
|
|
||||
|
internal unsafe partial class VulkanInstanceApi |
||||
|
{ |
||||
|
public IVulkanInstance Instance { get; } |
||||
|
|
||||
|
public VulkanInstanceApi(IVulkanInstance instance) |
||||
|
{ |
||||
|
Instance = instance; |
||||
|
Initialize(name => instance.GetInstanceProcAddress(instance.Handle, name)); |
||||
|
} |
||||
|
|
||||
|
[GetProcAddress("vkCreateDebugUtilsMessengerEXT", true)] |
||||
|
public partial VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, |
||||
|
ref VkDebugUtilsMessengerCreateInfoEXT pCreateInfo, IntPtr pAllocator, out VkDebugUtilsMessengerEXT pMessenger); |
||||
|
|
||||
|
[GetProcAddress("vkEnumeratePhysicalDevices")] |
||||
|
public partial VkResult EnumeratePhysicalDevices(VkInstance instance, ref uint32_t pPhysicalDeviceCount, |
||||
|
VkPhysicalDevice* pPhysicalDevices); |
||||
|
|
||||
|
[GetProcAddress("vkGetPhysicalDeviceProperties")] |
||||
|
public partial void GetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, |
||||
|
out VkPhysicalDeviceProperties pProperties); |
||||
|
|
||||
|
[GetProcAddress("vkEnumerateDeviceExtensionProperties")] |
||||
|
public partial VkResult EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, byte* pLayerName, |
||||
|
ref uint32_t pPropertyCount, VkExtensionProperties* pProperties); |
||||
|
|
||||
|
[GetProcAddress("vkGetPhysicalDeviceSurfaceSupportKHR")] |
||||
|
public partial VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, |
||||
|
uint32_t queueFamilyIndex, |
||||
|
VkSurfaceKHR surface, out VkBool32 pSupported); |
||||
|
|
||||
|
|
||||
|
[GetProcAddress("vkGetPhysicalDeviceQueueFamilyProperties")] |
||||
|
public partial void GetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice physicalDevice, |
||||
|
ref uint32_t pQueueFamilyPropertyCount, VkQueueFamilyProperties* pQueueFamilyProperties); |
||||
|
|
||||
|
[GetProcAddress("vkCreateDevice")] |
||||
|
public partial VkResult CreateDevice(VkPhysicalDevice physicalDevice, ref VkDeviceCreateInfo pCreateInfo, |
||||
|
IntPtr pAllocator, out VkDevice pDevice); |
||||
|
|
||||
|
[GetProcAddress("vkGetDeviceQueue")] |
||||
|
public partial void GetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, |
||||
|
out VkQueue pQueue); |
||||
|
|
||||
|
[GetProcAddress("vkGetDeviceProcAddr")] |
||||
|
public partial IntPtr GetDeviceProcAddr(VkDevice device, IntPtr pName); |
||||
|
|
||||
|
[GetProcAddress("vkDestroySurfaceKHR")] |
||||
|
public partial void DestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, IntPtr pAllocator); |
||||
|
|
||||
|
[GetProcAddress("vkGetPhysicalDeviceSurfaceFormatsKHR")] |
||||
|
public partial VkResult GetPhysicalDeviceSurfaceFormatsKHR( |
||||
|
VkPhysicalDevice physicalDevice, |
||||
|
VkSurfaceKHR surface, |
||||
|
ref uint32_t pSurfaceFormatCount, |
||||
|
VkSurfaceFormatKHR* pSurfaceFormats); |
||||
|
|
||||
|
[GetProcAddress("vkGetPhysicalDeviceMemoryProperties")] |
||||
|
public partial void GetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice, |
||||
|
out VkPhysicalDeviceMemoryProperties pMemoryProperties); |
||||
|
|
||||
|
[GetProcAddress("vkGetPhysicalDeviceSurfaceCapabilitiesKHR")] |
||||
|
public partial VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, |
||||
|
out VkSurfaceCapabilitiesKHR pSurfaceCapabilities); |
||||
|
|
||||
|
[GetProcAddress("vkGetPhysicalDeviceSurfacePresentModesKHR")] |
||||
|
public partial VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, |
||||
|
ref uint32_t pPresentModeCount, VkPresentModeKHR* pPresentModes); |
||||
|
|
||||
|
[GetProcAddress("vkGetPhysicalDeviceProperties2", true)] |
||||
|
public partial void GetPhysicalDeviceProperties2( |
||||
|
VkPhysicalDevice physicalDevice, |
||||
|
VkPhysicalDeviceProperties2* pProperties); |
||||
|
} |
||||
@ -0,0 +1,767 @@ |
|||||
|
// ReSharper disable IdentifierTypo
|
||||
|
|
||||
|
// ReSharper disable InconsistentNaming
|
||||
|
// ReSharper disable NotAccessedField.Global
|
||||
|
#pragma warning disable CS0649
|
||||
|
#pragma warning disable CS0169
|
||||
|
#pragma warning disable CA1823
|
||||
|
using System; |
||||
|
using uint32_t = System.UInt32; |
||||
|
using VkSampleCountFlags = System.UInt32; |
||||
|
using int32_t = System.Int32; |
||||
|
using VkBool32 = System.UInt32; |
||||
|
using uint8_t = System.Byte; |
||||
|
using size_t = System.IntPtr; |
||||
|
using VkDeviceSize = System.UInt64; |
||||
|
// ReSharper disable RedundantUnsafeContext
|
||||
|
|
||||
|
namespace Avalonia.Vulkan.UnmanagedInterop |
||||
|
{ |
||||
|
struct VkInstance |
||||
|
{ |
||||
|
public IntPtr Handle; |
||||
|
public VkInstance(IntPtr handle) |
||||
|
{ |
||||
|
Handle = handle; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
struct VkPhysicalDevice |
||||
|
{ |
||||
|
public IntPtr Handle; |
||||
|
public VkPhysicalDevice(IntPtr handle) |
||||
|
{ |
||||
|
Handle = handle; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
struct VkDevice |
||||
|
{ |
||||
|
public IntPtr Handle; |
||||
|
|
||||
|
public VkDevice(IntPtr handle) |
||||
|
{ |
||||
|
Handle = handle; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
struct VkSwapchainKHR |
||||
|
{ |
||||
|
public ulong Handle; |
||||
|
} |
||||
|
|
||||
|
struct VkSemaphore |
||||
|
{ |
||||
|
public ulong Handle; |
||||
|
} |
||||
|
|
||||
|
struct VkFence |
||||
|
{ |
||||
|
public ulong Handle; |
||||
|
} |
||||
|
|
||||
|
struct VkImage |
||||
|
{ |
||||
|
public ulong Handle; |
||||
|
} |
||||
|
|
||||
|
struct VkImageView |
||||
|
{ |
||||
|
public ulong Handle; |
||||
|
} |
||||
|
|
||||
|
struct VkDeviceMemory |
||||
|
{ |
||||
|
public ulong Handle; |
||||
|
} |
||||
|
|
||||
|
struct VkQueue |
||||
|
{ |
||||
|
public IntPtr Handle; |
||||
|
public VkQueue(IntPtr handle) |
||||
|
{ |
||||
|
Handle = handle; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
struct VkCommandPool |
||||
|
{ |
||||
|
public ulong Handle; |
||||
|
} |
||||
|
|
||||
|
struct VkCommandBuffer |
||||
|
{ |
||||
|
public IntPtr Handle; |
||||
|
} |
||||
|
|
||||
|
struct VkSurfaceKHR |
||||
|
{ |
||||
|
public ulong Handle; |
||||
|
|
||||
|
public VkSurfaceKHR(ulong handle) |
||||
|
{ |
||||
|
Handle = handle; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
struct VkDebugUtilsMessengerEXT |
||||
|
{ |
||||
|
public IntPtr Handle; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkLayerProperties |
||||
|
{ |
||||
|
public fixed byte layerName[256]; |
||||
|
public uint32_t specVersion; |
||||
|
public uint32_t implementationVersion; |
||||
|
public fixed byte description[256]; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkDebugUtilsLabelEXT |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public IntPtr pNext; |
||||
|
public IntPtr pLabelName; |
||||
|
public fixed float color[4]; |
||||
|
} |
||||
|
|
||||
|
struct VkDebugUtilsObjectNameInfoEXT |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public IntPtr pNext; |
||||
|
public VkObjectType objectType; |
||||
|
public ulong objectHandle; |
||||
|
public IntPtr pObjectName; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkDebugUtilsMessengerCallbackDataEXT |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public IntPtr pNext; |
||||
|
public uint flags; |
||||
|
public IntPtr pMessageIdName; |
||||
|
public int32_t messageIdNumber; |
||||
|
public IntPtr pMessage; |
||||
|
public uint32_t queueLabelCount; |
||||
|
public VkDebugUtilsLabelEXT* pQueueLabels; |
||||
|
public uint32_t cmdBufLabelCount; |
||||
|
public VkDebugUtilsLabelEXT* pCmdBufLabels; |
||||
|
public uint32_t objectCount; |
||||
|
public VkDebugUtilsObjectNameInfoEXT* pObjects; |
||||
|
} |
||||
|
|
||||
|
unsafe delegate VkBool32 VkDebugUtilsMessengerCallbackEXTDelegate( |
||||
|
VkDebugUtilsMessageSeverityFlagsEXT messageSeverity, |
||||
|
VkDebugUtilsMessageTypeFlagsEXT messageTypes, |
||||
|
VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, |
||||
|
void* pUserData); |
||||
|
|
||||
|
struct VkDebugUtilsMessengerCreateInfoEXT |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public IntPtr pNext; |
||||
|
public uint flags; |
||||
|
public VkDebugUtilsMessageSeverityFlagsEXT messageSeverity; |
||||
|
public VkDebugUtilsMessageTypeFlagsEXT messageType; |
||||
|
public IntPtr pfnUserCallback; |
||||
|
public IntPtr pUserData; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkInstanceCreateInfo |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public IntPtr pNext; |
||||
|
public int flags; |
||||
|
public VkApplicationInfo* pApplicationInfo; |
||||
|
public uint32_t enabledLayerCount; |
||||
|
public byte** ppEnabledLayerNames; |
||||
|
public uint32_t enabledExtensionCount; |
||||
|
public byte** ppEnabledExtensionNames; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkApplicationInfo |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public void* pNext; |
||||
|
public byte* pApplicationName; |
||||
|
public uint32_t applicationVersion; |
||||
|
public byte* pEngineName; |
||||
|
public uint32_t engineVersion; |
||||
|
public uint32_t apiVersion; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkPhysicalDeviceSparseProperties |
||||
|
{ |
||||
|
public VkBool32 residencyStandard2DBlockShape; |
||||
|
public VkBool32 residencyStandard2DMultisampleBlockShape; |
||||
|
public VkBool32 residencyStandard3DBlockShape; |
||||
|
public VkBool32 residencyAlignedMipSize; |
||||
|
public VkBool32 residencyNonResidentStrict; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkPhysicalDeviceProperties |
||||
|
{ |
||||
|
public uint32_t apiVersion; |
||||
|
public uint32_t driverVersion; |
||||
|
public uint32_t vendorID; |
||||
|
public uint32_t deviceID; |
||||
|
public VkPhysicalDeviceType deviceType; |
||||
|
public fixed byte deviceName[256]; |
||||
|
public fixed uint8_t pipelineCacheUUID[16]; |
||||
|
public VkPhysicalDeviceLimits limits; |
||||
|
public VkPhysicalDeviceSparseProperties sparseProperties; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkPhysicalDeviceProperties2 { |
||||
|
public VkStructureType sType; |
||||
|
public void* pNext; |
||||
|
public VkPhysicalDeviceProperties properties; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkPhysicalDeviceIDProperties { |
||||
|
public VkStructureType sType; |
||||
|
public void* pNext; |
||||
|
public fixed uint8_t deviceUUID[16]; |
||||
|
public fixed uint8_t driverUUID[16]; |
||||
|
public fixed uint8_t deviceLUID[8]; |
||||
|
public uint32_t deviceNodeMask; |
||||
|
public VkBool32 deviceLUIDValid; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkPhysicalDeviceLimits |
||||
|
{ |
||||
|
public uint32_t maxImageDimension1D; |
||||
|
public uint32_t maxImageDimension2D; |
||||
|
public uint32_t maxImageDimension3D; |
||||
|
public uint32_t maxImageDimensionCube; |
||||
|
public uint32_t maxImageArrayLayers; |
||||
|
public uint32_t maxTexelBufferElements; |
||||
|
public uint32_t maxUniformBufferRange; |
||||
|
public uint32_t maxStorageBufferRange; |
||||
|
public uint32_t maxPushConstantsSize; |
||||
|
public uint32_t maxMemoryAllocationCount; |
||||
|
public uint32_t maxSamplerAllocationCount; |
||||
|
public VkDeviceSize bufferImageGranularity; |
||||
|
public VkDeviceSize sparseAddressSpaceSize; |
||||
|
public uint32_t maxBoundDescriptorSets; |
||||
|
public uint32_t maxPerStageDescriptorSamplers; |
||||
|
public uint32_t maxPerStageDescriptorUniformBuffers; |
||||
|
public uint32_t maxPerStageDescriptorStorageBuffers; |
||||
|
public uint32_t maxPerStageDescriptorSampledImages; |
||||
|
public uint32_t maxPerStageDescriptorStorageImages; |
||||
|
public uint32_t maxPerStageDescriptorInputAttachments; |
||||
|
public uint32_t maxPerStageResources; |
||||
|
public uint32_t maxDescriptorSetSamplers; |
||||
|
public uint32_t maxDescriptorSetUniformBuffers; |
||||
|
public uint32_t maxDescriptorSetUniformBuffersDynamic; |
||||
|
public uint32_t maxDescriptorSetStorageBuffers; |
||||
|
public uint32_t maxDescriptorSetStorageBuffersDynamic; |
||||
|
public uint32_t maxDescriptorSetSampledImages; |
||||
|
public uint32_t maxDescriptorSetStorageImages; |
||||
|
public uint32_t maxDescriptorSetInputAttachments; |
||||
|
public uint32_t maxVertexInputAttributes; |
||||
|
public uint32_t maxVertexInputBindings; |
||||
|
public uint32_t maxVertexInputAttributeOffset; |
||||
|
public uint32_t maxVertexInputBindingStride; |
||||
|
public uint32_t maxVertexOutputComponents; |
||||
|
public uint32_t maxTessellationGenerationLevel; |
||||
|
public uint32_t maxTessellationPatchSize; |
||||
|
public uint32_t maxTessellationControlPerVertexInputComponents; |
||||
|
public uint32_t maxTessellationControlPerVertexOutputComponents; |
||||
|
public uint32_t maxTessellationControlPerPatchOutputComponents; |
||||
|
public uint32_t maxTessellationControlTotalOutputComponents; |
||||
|
public uint32_t maxTessellationEvaluationInputComponents; |
||||
|
public uint32_t maxTessellationEvaluationOutputComponents; |
||||
|
public uint32_t maxGeometryShaderInvocations; |
||||
|
public uint32_t maxGeometryInputComponents; |
||||
|
public uint32_t maxGeometryOutputComponents; |
||||
|
public uint32_t maxGeometryOutputVertices; |
||||
|
public uint32_t maxGeometryTotalOutputComponents; |
||||
|
public uint32_t maxFragmentInputComponents; |
||||
|
public uint32_t maxFragmentOutputAttachments; |
||||
|
public uint32_t maxFragmentDualSrcAttachments; |
||||
|
public uint32_t maxFragmentCombinedOutputResources; |
||||
|
public uint32_t maxComputeSharedMemorySize; |
||||
|
public fixed uint32_t maxComputeWorkGroupCount[3]; |
||||
|
public uint32_t maxComputeWorkGroupInvocations; |
||||
|
public fixed uint32_t maxComputeWorkGroupSize[3]; |
||||
|
public uint32_t subPixelPrecisionBits; |
||||
|
public uint32_t subTexelPrecisionBits; |
||||
|
public uint32_t mipmapPrecisionBits; |
||||
|
public uint32_t maxDrawIndexedIndexValue; |
||||
|
public uint32_t maxDrawIndirectCount; |
||||
|
public float maxSamplerLodBias; |
||||
|
public float maxSamplerAnisotropy; |
||||
|
public uint32_t maxViewports; |
||||
|
public fixed uint32_t maxViewportDimensions[2]; |
||||
|
public fixed float viewportBoundsRange[2]; |
||||
|
public uint32_t viewportSubPixelBits; |
||||
|
public size_t minMemoryMapAlignment; |
||||
|
public VkDeviceSize minTexelBufferOffsetAlignment; |
||||
|
public VkDeviceSize minUniformBufferOffsetAlignment; |
||||
|
public VkDeviceSize minStorageBufferOffsetAlignment; |
||||
|
public int32_t minTexelOffset; |
||||
|
public uint32_t maxTexelOffset; |
||||
|
public int32_t minTexelGatherOffset; |
||||
|
public uint32_t maxTexelGatherOffset; |
||||
|
public float minInterpolationOffset; |
||||
|
public float maxInterpolationOffset; |
||||
|
public uint32_t subPixelInterpolationOffsetBits; |
||||
|
public uint32_t maxFramebufferWidth; |
||||
|
public uint32_t maxFramebufferHeight; |
||||
|
public uint32_t maxFramebufferLayers; |
||||
|
public VkSampleCountFlags framebufferColorSampleCounts; |
||||
|
public VkSampleCountFlags framebufferDepthSampleCounts; |
||||
|
public VkSampleCountFlags framebufferStencilSampleCounts; |
||||
|
public VkSampleCountFlags framebufferNoAttachmentsSampleCounts; |
||||
|
public uint32_t maxColorAttachments; |
||||
|
public VkSampleCountFlags sampledImageColorSampleCounts; |
||||
|
public VkSampleCountFlags sampledImageIntegerSampleCounts; |
||||
|
public VkSampleCountFlags sampledImageDepthSampleCounts; |
||||
|
public VkSampleCountFlags sampledImageStencilSampleCounts; |
||||
|
public VkSampleCountFlags storageImageSampleCounts; |
||||
|
public uint32_t maxSampleMaskWords; |
||||
|
public VkBool32 timestampComputeAndGraphics; |
||||
|
public float timestampPeriod; |
||||
|
public uint32_t maxClipDistances; |
||||
|
public uint32_t maxCullDistances; |
||||
|
public uint32_t maxCombinedClipAndCullDistances; |
||||
|
public uint32_t discreteQueuePriorities; |
||||
|
public fixed float pointSizeRange[2]; |
||||
|
public fixed float lineWidthRange[2]; |
||||
|
public float pointSizeGranularity; |
||||
|
public float lineWidthGranularity; |
||||
|
public VkBool32 strictLines; |
||||
|
public VkBool32 standardSampleLocations; |
||||
|
public VkDeviceSize optimalBufferCopyOffsetAlignment; |
||||
|
public VkDeviceSize optimalBufferCopyRowPitchAlignment; |
||||
|
public VkDeviceSize nonCoherentAtomSize; |
||||
|
} |
||||
|
|
||||
|
internal unsafe struct VkExtensionProperties |
||||
|
{ |
||||
|
public fixed byte extensionName[256]; |
||||
|
public uint32_t specVersion; |
||||
|
} |
||||
|
|
||||
|
struct VkQueueFamilyProperties |
||||
|
{ |
||||
|
public VkQueueFlags queueFlags; |
||||
|
public uint32_t queueCount; |
||||
|
public uint32_t timestampValidBits; |
||||
|
public VkExtent3D minImageTransferGranularity; |
||||
|
} |
||||
|
|
||||
|
struct VkExtent2D |
||||
|
{ |
||||
|
public uint32_t width; |
||||
|
public uint32_t height; |
||||
|
} |
||||
|
|
||||
|
struct VkExtent3D |
||||
|
{ |
||||
|
public uint32_t width; |
||||
|
public uint32_t height; |
||||
|
public uint32_t depth; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkDeviceQueueCreateInfo |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public IntPtr pNext; |
||||
|
public VkDeviceQueueCreateFlags flags; |
||||
|
public uint32_t queueFamilyIndex; |
||||
|
public uint32_t queueCount; |
||||
|
public float* pQueuePriorities; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkDeviceCreateInfo |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public IntPtr pNext; |
||||
|
public uint flags; |
||||
|
public uint32_t queueCreateInfoCount; |
||||
|
public VkDeviceQueueCreateInfo* pQueueCreateInfos; |
||||
|
public uint32_t enabledLayerCount; |
||||
|
public byte** ppEnabledLayerNames; |
||||
|
public uint32_t enabledExtensionCount; |
||||
|
public byte** ppEnabledExtensionNames; |
||||
|
public IntPtr pEnabledFeatures; |
||||
|
} |
||||
|
|
||||
|
struct VkFenceCreateInfo |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
IntPtr pNext; |
||||
|
public VkFenceCreateFlags flags; |
||||
|
} |
||||
|
|
||||
|
struct VkCommandPoolCreateInfo |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
IntPtr pNext; |
||||
|
public VkCommandPoolCreateFlags flags; |
||||
|
public uint32_t queueFamilyIndex; |
||||
|
} |
||||
|
|
||||
|
struct VkCommandBufferAllocateInfo |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
IntPtr pNext; |
||||
|
public VkCommandPool commandPool; |
||||
|
public VkCommandBufferLevel level; |
||||
|
public uint32_t commandBufferCount; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
struct VkCommandBufferBeginInfo |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
IntPtr pNext; |
||||
|
public VkCommandBufferUsageFlags flags; |
||||
|
IntPtr pInheritanceInfo; |
||||
|
} |
||||
|
|
||||
|
struct VkSemaphoreCreateInfo |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
IntPtr pNext; |
||||
|
uint flags; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkSubmitInfo |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public IntPtr pNext; |
||||
|
public uint32_t waitSemaphoreCount; |
||||
|
public VkSemaphore* pWaitSemaphores; |
||||
|
public VkPipelineStageFlags* pWaitDstStageMask; |
||||
|
public uint32_t commandBufferCount; |
||||
|
public VkCommandBuffer* pCommandBuffers; |
||||
|
public uint32_t signalSemaphoreCount; |
||||
|
public VkSemaphore* pSignalSemaphores; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
struct VkSurfaceFormatKHR |
||||
|
{ |
||||
|
public VkFormat format; |
||||
|
public VkColorSpaceKHR colorSpace; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
struct VkMemoryType |
||||
|
{ |
||||
|
public VkMemoryPropertyFlags propertyFlags; |
||||
|
public uint32_t heapIndex; |
||||
|
} |
||||
|
|
||||
|
struct VkMemoryHeap |
||||
|
{ |
||||
|
public VkDeviceSize size; |
||||
|
public VkMemoryHeapFlags flags; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkPhysicalDeviceMemoryProperties |
||||
|
{ |
||||
|
public uint32_t memoryTypeCount; |
||||
|
public VkMemoryTypesBuffer memoryTypes; |
||||
|
public uint32_t memoryHeapCount; |
||||
|
public VkMemoryHeapsBuffer memoryHeaps; |
||||
|
|
||||
|
public struct VkMemoryTypesBuffer |
||||
|
{ |
||||
|
public VkMemoryType Element0; |
||||
|
public VkMemoryType Element1; |
||||
|
public VkMemoryType Element2; |
||||
|
public VkMemoryType Element3; |
||||
|
public VkMemoryType Element4; |
||||
|
public VkMemoryType Element5; |
||||
|
public VkMemoryType Element6; |
||||
|
public VkMemoryType Element7; |
||||
|
public VkMemoryType Element8; |
||||
|
public VkMemoryType Element9; |
||||
|
public VkMemoryType Element10; |
||||
|
public VkMemoryType Element11; |
||||
|
public VkMemoryType Element12; |
||||
|
public VkMemoryType Element13; |
||||
|
public VkMemoryType Element14; |
||||
|
public VkMemoryType Element15; |
||||
|
public VkMemoryType Element16; |
||||
|
public VkMemoryType Element17; |
||||
|
public VkMemoryType Element18; |
||||
|
public VkMemoryType Element19; |
||||
|
public VkMemoryType Element20; |
||||
|
public VkMemoryType Element21; |
||||
|
public VkMemoryType Element22; |
||||
|
public VkMemoryType Element23; |
||||
|
public VkMemoryType Element24; |
||||
|
public VkMemoryType Element25; |
||||
|
public VkMemoryType Element26; |
||||
|
public VkMemoryType Element27; |
||||
|
public VkMemoryType Element28; |
||||
|
public VkMemoryType Element29; |
||||
|
public VkMemoryType Element30; |
||||
|
public VkMemoryType Element31; |
||||
|
|
||||
|
public ref VkMemoryType this[int index] |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (index > 31 || index < 0) |
||||
|
{ |
||||
|
throw new ArgumentOutOfRangeException(nameof(index)); |
||||
|
} |
||||
|
|
||||
|
fixed (VkMemoryType* ptr = &Element0) |
||||
|
{ |
||||
|
return ref ptr[index]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public struct VkMemoryHeapsBuffer |
||||
|
{ |
||||
|
public VkMemoryHeap Element0; |
||||
|
public VkMemoryHeap Element1; |
||||
|
public VkMemoryHeap Element2; |
||||
|
public VkMemoryHeap Element3; |
||||
|
public VkMemoryHeap Element4; |
||||
|
public VkMemoryHeap Element5; |
||||
|
public VkMemoryHeap Element6; |
||||
|
public VkMemoryHeap Element7; |
||||
|
public VkMemoryHeap Element8; |
||||
|
public VkMemoryHeap Element9; |
||||
|
public VkMemoryHeap Element10; |
||||
|
public VkMemoryHeap Element11; |
||||
|
public VkMemoryHeap Element12; |
||||
|
public VkMemoryHeap Element13; |
||||
|
public VkMemoryHeap Element14; |
||||
|
public VkMemoryHeap Element15; |
||||
|
|
||||
|
public ref VkMemoryHeap this[int index] |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (index > 15 || index < 0) |
||||
|
{ |
||||
|
throw new ArgumentOutOfRangeException(nameof(index)); |
||||
|
} |
||||
|
|
||||
|
fixed (VkMemoryHeap* ptr = &Element0) |
||||
|
{ |
||||
|
return ref ptr[index]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkImageCreateInfo |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public void* pNext; |
||||
|
public VkImageCreateFlags flags; |
||||
|
public VkImageType imageType; |
||||
|
public VkFormat format; |
||||
|
public VkExtent3D extent; |
||||
|
public uint32_t mipLevels; |
||||
|
public uint32_t arrayLayers; |
||||
|
public VkSampleCountFlags samples; |
||||
|
public VkImageTiling tiling; |
||||
|
public VkImageUsageFlags usage; |
||||
|
public VkSharingMode sharingMode; |
||||
|
public uint32_t queueFamilyIndexCount; |
||||
|
public uint32_t* pQueueFamilyIndices; |
||||
|
public VkImageLayout initialLayout; |
||||
|
} |
||||
|
|
||||
|
struct VkMemoryRequirements |
||||
|
{ |
||||
|
public VkDeviceSize size; |
||||
|
public VkDeviceSize alignment; |
||||
|
public uint32_t memoryTypeBits; |
||||
|
} |
||||
|
|
||||
|
struct VkMemoryAllocateInfo |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public IntPtr pNext; |
||||
|
public VkDeviceSize allocationSize; |
||||
|
public uint32_t memoryTypeIndex; |
||||
|
} |
||||
|
|
||||
|
struct VkComponentMapping |
||||
|
{ |
||||
|
public VkComponentSwizzle r; |
||||
|
public VkComponentSwizzle g; |
||||
|
public VkComponentSwizzle b; |
||||
|
public VkComponentSwizzle a; |
||||
|
} |
||||
|
|
||||
|
struct VkImageSubresourceRange |
||||
|
{ |
||||
|
public VkImageAspectFlags aspectMask; |
||||
|
public uint32_t baseMipLevel; |
||||
|
public uint32_t levelCount; |
||||
|
public uint32_t baseArrayLayer; |
||||
|
public uint32_t layerCount; |
||||
|
} |
||||
|
|
||||
|
struct VkImageViewCreateInfo |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public IntPtr pNext; |
||||
|
public VkImageViewCreateFlags flags; |
||||
|
public VkImage image; |
||||
|
public VkImageViewType viewType; |
||||
|
public VkFormat format; |
||||
|
public VkComponentMapping components; |
||||
|
public VkImageSubresourceRange subresourceRange; |
||||
|
} |
||||
|
|
||||
|
struct VkImageMemoryBarrier |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
IntPtr pNext; |
||||
|
public VkAccessFlags srcAccessMask; |
||||
|
public VkAccessFlags dstAccessMask; |
||||
|
public VkImageLayout oldLayout; |
||||
|
public VkImageLayout newLayout; |
||||
|
public uint32_t srcQueueFamilyIndex; |
||||
|
public uint32_t dstQueueFamilyIndex; |
||||
|
public VkImage image; |
||||
|
public VkImageSubresourceRange subresourceRange; |
||||
|
} |
||||
|
|
||||
|
struct VkSurfaceCapabilitiesKHR |
||||
|
{ |
||||
|
public uint32_t minImageCount; |
||||
|
public uint32_t maxImageCount; |
||||
|
public VkExtent2D currentExtent; |
||||
|
public VkExtent2D minImageExtent; |
||||
|
public VkExtent2D maxImageExtent; |
||||
|
public uint32_t maxImageArrayLayers; |
||||
|
public VkSurfaceTransformFlagsKHR supportedTransforms; |
||||
|
public VkSurfaceTransformFlagsKHR currentTransform; |
||||
|
public VkCompositeAlphaFlagsKHR supportedCompositeAlpha; |
||||
|
public VkImageUsageFlags supportedUsageFlags; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkSwapchainCreateInfoKHR |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public IntPtr pNext; |
||||
|
public VkSwapchainCreateFlagsKHR flags; |
||||
|
public VkSurfaceKHR surface; |
||||
|
public uint32_t minImageCount; |
||||
|
public VkFormat imageFormat; |
||||
|
public VkColorSpaceKHR imageColorSpace; |
||||
|
public VkExtent2D imageExtent; |
||||
|
public uint32_t imageArrayLayers; |
||||
|
public VkImageUsageFlags imageUsage; |
||||
|
public VkSharingMode imageSharingMode; |
||||
|
public uint32_t queueFamilyIndexCount; |
||||
|
public uint32_t* pQueueFamilyIndices; |
||||
|
public VkSurfaceTransformFlagsKHR preTransform; |
||||
|
public VkCompositeAlphaFlagsKHR compositeAlpha; |
||||
|
public VkPresentModeKHR presentMode; |
||||
|
public VkBool32 clipped; |
||||
|
public VkSwapchainKHR oldSwapchain; |
||||
|
} |
||||
|
|
||||
|
struct VkOffset3D |
||||
|
{ |
||||
|
public int32_t x; |
||||
|
public int32_t y; |
||||
|
public int32_t z; |
||||
|
} |
||||
|
|
||||
|
struct VkImageSubresourceLayers |
||||
|
{ |
||||
|
public VkImageAspectFlags aspectMask; |
||||
|
public uint32_t mipLevel; |
||||
|
public uint32_t baseArrayLayer; |
||||
|
public uint32_t layerCount; |
||||
|
} |
||||
|
|
||||
|
struct VkImageBlit |
||||
|
{ |
||||
|
public VkImageSubresourceLayers srcSubresource; |
||||
|
public VkOffset3D srcOffsets1; |
||||
|
public VkOffset3D srcOffsets2; |
||||
|
public VkImageSubresourceLayers dstSubresource; |
||||
|
public VkOffset3D dstOffsets1; |
||||
|
public VkOffset3D dstOffsets2; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkPresentInfoKHR |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public IntPtr pNext; |
||||
|
public uint32_t waitSemaphoreCount; |
||||
|
public VkSemaphore* pWaitSemaphores; |
||||
|
public uint32_t swapchainCount; |
||||
|
public VkSwapchainKHR* pSwapchains; |
||||
|
public uint32_t* pImageIndices; |
||||
|
public VkResult* pResults; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkImportSemaphoreFdInfoKHR |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public void* pNext; |
||||
|
public VkSemaphore semaphore; |
||||
|
public VkSemaphoreImportFlags flags; |
||||
|
public VkExternalSemaphoreHandleTypeFlags handleType; |
||||
|
public int fd; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkImportSemaphoreWin32HandleInfoKHR |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public void* pNext; |
||||
|
public VkSemaphore semaphore; |
||||
|
public VkSemaphoreImportFlags flags; |
||||
|
public VkExternalSemaphoreHandleTypeFlags handleType; |
||||
|
public IntPtr handle; |
||||
|
public IntPtr name; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkImportMemoryFdInfoKHR |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public void* pNext; |
||||
|
public VkExternalMemoryHandleTypeFlagBits handleType; |
||||
|
public int fd; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkImportMemoryWin32HandleInfoKHR |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public void* pNext; |
||||
|
public VkExternalMemoryHandleTypeFlagBits handleType; |
||||
|
public IntPtr handle; |
||||
|
public IntPtr name; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkMemoryDedicatedAllocateInfo |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public void* pNext; |
||||
|
public VkImage image; |
||||
|
public IntPtr buffer; |
||||
|
} |
||||
|
|
||||
|
unsafe struct VkExternalMemoryImageCreateInfo |
||||
|
{ |
||||
|
public VkStructureType sType; |
||||
|
public void* pNext; |
||||
|
public VkExternalMemoryHandleTypeFlagBits handleTypes; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using Avalonia.Vulkan.Interop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan; |
||||
|
|
||||
|
|
||||
|
static class VulkanHelpers |
||||
|
{ |
||||
|
public static uint MakeVersion(Version v) => MakeVersion(v.Major, v.Minor, v.Build); |
||||
|
|
||||
|
public static uint MakeVersion(int major, int minor, int patch) |
||||
|
{ |
||||
|
return (uint)((major << 22) | (minor << 12) | patch); |
||||
|
} |
||||
|
|
||||
|
public const uint QueueFamilyIgnored = 4294967295; |
||||
|
} |
||||
@ -0,0 +1,73 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan; |
||||
|
|
||||
|
internal class VulkanContext : IVulkanPlatformGraphicsContext |
||||
|
{ |
||||
|
private readonly IVulkanKhrSurfacePlatformSurfaceFactory? _surfaceFactory; |
||||
|
private readonly VulkanExternalObjectsFeature? _externalObjectsFeature; |
||||
|
public IVulkanDevice Device { get; } |
||||
|
public IVulkanInstance Instance => Device.Instance; |
||||
|
|
||||
|
public VulkanContext(IVulkanDevice device, Dictionary<Type, object> platformFeatures) |
||||
|
{ |
||||
|
Device = device; |
||||
|
using (device.Lock()) |
||||
|
{ |
||||
|
InstanceApi = new VulkanInstanceApi(device.Instance); |
||||
|
DeviceApi = new VulkanDeviceApi(device); |
||||
|
if (platformFeatures.TryGetValue(typeof(IVulkanKhrSurfacePlatformSurfaceFactory), out var factory)) |
||||
|
_surfaceFactory = (IVulkanKhrSurfacePlatformSurfaceFactory)factory; |
||||
|
} |
||||
|
|
||||
|
if ( |
||||
|
VulkanExternalObjectsFeature.RequiredInstanceExtensions.All(ext => Instance.EnabledExtensions.Contains(ext)) |
||||
|
&& VulkanExternalObjectsFeature.RequiredDeviceExtensions.All(ext => Device.EnabledExtensions.Contains(ext))) |
||||
|
{ |
||||
|
_externalObjectsFeature = new VulkanExternalObjectsFeature(this); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public object? TryGetFeature(Type featureType) |
||||
|
{ |
||||
|
if (featureType == typeof(IVulkanContextExternalObjectsFeature)) |
||||
|
return _externalObjectsFeature; |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public bool IsLost => Device.IsLost; |
||||
|
public IDisposable EnsureCurrent() => Device.Lock(); |
||||
|
|
||||
|
public VkDevice DeviceHandle => new (Device.Handle); |
||||
|
public VkPhysicalDevice PhysicalDeviceHandle => new (Device.PhysicalDeviceHandle); |
||||
|
public VkInstance InstanceHandle => new(Instance.Handle); |
||||
|
public VkQueue MainQueueHandle => new(Device.MainQueueHandle); |
||||
|
public uint GraphicsQueueFamilyIndex => Device.GraphicsQueueFamilyIndex; |
||||
|
|
||||
|
public VulkanInstanceApi InstanceApi { get; } |
||||
|
public VulkanDeviceApi DeviceApi { get; } |
||||
|
public IVulkanRenderTarget CreateRenderTarget(IEnumerable<object> surfaces) |
||||
|
{ |
||||
|
foreach (var surf in surfaces) |
||||
|
{ |
||||
|
IVulkanKhrSurfacePlatformSurface khrSurface; |
||||
|
if (surf is IVulkanKhrSurfacePlatformSurface khr) |
||||
|
khrSurface = khr; |
||||
|
else if (_surfaceFactory?.CanRenderToSurface(this, surf) == true) |
||||
|
khrSurface = _surfaceFactory.CreateSurface(this, surf); |
||||
|
else |
||||
|
continue; |
||||
|
return new VulkanKhrRenderTarget(khrSurface, this); |
||||
|
} |
||||
|
|
||||
|
throw new VulkanException("Unable to find a suitable platform surface"); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Vulkan.Interop; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan; |
||||
|
|
||||
|
public class VulkanException : Exception |
||||
|
{ |
||||
|
public VulkanException(string message) : base(message) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public VulkanException(string funcName, int res) : this(funcName, (VkResult)res) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
internal VulkanException(string funcName, VkResult res) : base($"{funcName} returned {res}") |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public static void ThrowOnError(string funcName, int res) => ((VkResult)res).ThrowOnError(funcName); |
||||
|
} |
||||
|
|
||||
|
internal static class VulkanExceptionExtensions |
||||
|
{ |
||||
|
public static void ThrowOnError(this VkResult res, string funcName) |
||||
|
{ |
||||
|
if (res != VkResult.VK_SUCCESS) |
||||
|
throw new VulkanException(funcName, res); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,311 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Rendering.Composition; |
||||
|
using Avalonia.Vulkan.Interop; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan; |
||||
|
|
||||
|
internal unsafe class VulkanExternalObjectsFeature : IVulkanContextExternalObjectsFeature |
||||
|
{ |
||||
|
public static string[] RequiredInstanceExtensions = { |
||||
|
"VK_KHR_get_physical_device_properties2", |
||||
|
"VK_KHR_external_memory_capabilities", |
||||
|
"VK_KHR_external_semaphore_capabilities" |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
private static string[] s_requiredCommonDeviceExtensions = |
||||
|
{ |
||||
|
"VK_KHR_external_memory", |
||||
|
"VK_KHR_external_semaphore", |
||||
|
"VK_KHR_dedicated_allocation", |
||||
|
}; |
||||
|
|
||||
|
private static string[] s_requiredLinuxDeviceExtensions = |
||||
|
s_requiredCommonDeviceExtensions.Concat(new[] |
||||
|
{ |
||||
|
"VK_KHR_external_semaphore_fd", |
||||
|
"VK_KHR_external_memory_fd" |
||||
|
}).ToArray(); |
||||
|
|
||||
|
private static string[] s_requiredWin32DeviceExtensions = s_requiredCommonDeviceExtensions.Concat(new[] |
||||
|
{ |
||||
|
"VK_KHR_external_semaphore_win32", |
||||
|
"VK_KHR_external_memory_win32" |
||||
|
}).ToArray(); |
||||
|
|
||||
|
public static string[] RequiredDeviceExtensions = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) |
||||
|
? s_requiredWin32DeviceExtensions |
||||
|
: s_requiredLinuxDeviceExtensions; |
||||
|
|
||||
|
private readonly VulkanContext _context; |
||||
|
private readonly VulkanCommandBufferPool _pool; |
||||
|
|
||||
|
public VulkanExternalObjectsFeature(VulkanContext context) |
||||
|
{ |
||||
|
_context = context; |
||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
||||
|
{ |
||||
|
//TODO: keyed muted
|
||||
|
SupportedImageHandleTypes = new[] |
||||
|
{ |
||||
|
KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaqueNtHandle, |
||||
|
KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaqueKmtHandle, |
||||
|
}; |
||||
|
SupportedSemaphoreTypes = new[] |
||||
|
{ |
||||
|
KnownPlatformGraphicsExternalSemaphoreHandleTypes.VulkanOpaqueNtHandle, |
||||
|
KnownPlatformGraphicsExternalSemaphoreHandleTypes.VulkanOpaqueKmtHandle |
||||
|
}; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
SupportedImageHandleTypes = new[] |
||||
|
{ |
||||
|
KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaquePosixFileDescriptor |
||||
|
}; |
||||
|
SupportedSemaphoreTypes = new[] |
||||
|
{ |
||||
|
KnownPlatformGraphicsExternalSemaphoreHandleTypes.VulkanOpaquePosixFileDescriptor |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
var physicalDeviceIDProperties = new VkPhysicalDeviceIDProperties() |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES |
||||
|
}; |
||||
|
|
||||
|
var physicalDeviceProperties2 = new VkPhysicalDeviceProperties2() |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, |
||||
|
pNext = &physicalDeviceIDProperties |
||||
|
}; |
||||
|
_context.InstanceApi.GetPhysicalDeviceProperties2(_context.PhysicalDeviceHandle, &physicalDeviceProperties2); |
||||
|
|
||||
|
var luid = new Span<byte>(physicalDeviceIDProperties.deviceLUID, 8).ToArray(); |
||||
|
if (luid.Any(b => b != 0)) |
||||
|
DeviceLuid = luid; |
||||
|
var uuid = new Span<byte>(physicalDeviceIDProperties.deviceUUID, 16).ToArray(); |
||||
|
if (uuid.Any(b => b != 0)) |
||||
|
DeviceUuid = uuid; |
||||
|
_pool = new VulkanCommandBufferPool(_context); |
||||
|
} |
||||
|
|
||||
|
public IReadOnlyList<string> SupportedImageHandleTypes { get; } |
||||
|
public IReadOnlyList<string> SupportedSemaphoreTypes { get; } |
||||
|
public byte[]? DeviceUuid { get; } |
||||
|
public byte[]? DeviceLuid { get; } |
||||
|
|
||||
|
|
||||
|
public CompositionGpuImportedImageSynchronizationCapabilities GetSynchronizationCapabilities(string imageHandleType) |
||||
|
{ |
||||
|
if (!SupportedImageHandleTypes.Contains(imageHandleType)) |
||||
|
throw new ArgumentException(); |
||||
|
//TODO: keyed muted
|
||||
|
return CompositionGpuImportedImageSynchronizationCapabilities.Semaphores; |
||||
|
} |
||||
|
|
||||
|
public IVulkanExternalImage ImportImage(IPlatformHandle handle, PlatformGraphicsExternalImageProperties properties) |
||||
|
{ |
||||
|
_pool.FreeFinishedCommandBuffers(); |
||||
|
if (!SupportedImageHandleTypes.Contains(handle.HandleDescriptor)) |
||||
|
throw new NotSupportedException(); |
||||
|
|
||||
|
|
||||
|
return new ImportedImage(_context, _pool, handle, properties); |
||||
|
} |
||||
|
|
||||
|
public IVulkanExternalSemaphore ImportSemaphore(IPlatformHandle handle) |
||||
|
{ |
||||
|
if (!SupportedSemaphoreTypes.Contains(handle.HandleDescriptor)) |
||||
|
throw new NotSupportedException(); |
||||
|
|
||||
|
var typeBit = handle.HandleDescriptor switch |
||||
|
{ |
||||
|
KnownPlatformGraphicsExternalSemaphoreHandleTypes.VulkanOpaquePosixFileDescriptor => |
||||
|
VkExternalSemaphoreHandleTypeFlags.VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, |
||||
|
KnownPlatformGraphicsExternalSemaphoreHandleTypes.VulkanOpaqueKmtHandle => |
||||
|
VkExternalSemaphoreHandleTypeFlags.VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT, |
||||
|
KnownPlatformGraphicsExternalSemaphoreHandleTypes.VulkanOpaqueNtHandle => |
||||
|
VkExternalSemaphoreHandleTypeFlags.VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT, |
||||
|
_ => throw new NotSupportedException() |
||||
|
}; |
||||
|
|
||||
|
var semaphore = new VulkanSemaphore(_context); |
||||
|
if (handle.HandleDescriptor == |
||||
|
KnownPlatformGraphicsExternalSemaphoreHandleTypes.VulkanOpaquePosixFileDescriptor) |
||||
|
{ |
||||
|
var info = new VkImportSemaphoreFdInfoKHR |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR, |
||||
|
fd = handle.Handle.ToInt32(), |
||||
|
handleType = typeBit, |
||||
|
semaphore = semaphore.Handle |
||||
|
}; |
||||
|
var addr = _context.Instance.GetDeviceProcAddress(_context.Device.Handle, "vkImportSemaphoreFdKHR"); |
||||
|
if (addr == IntPtr.Zero) |
||||
|
addr = _context.Instance.GetInstanceProcAddress(_context.Instance.Handle, "vkImportSemaphoreFdKHR"); |
||||
|
_context.DeviceApi.ImportSemaphoreFdKHR(_context.DeviceHandle, &info) |
||||
|
.ThrowOnError("vkImportSemaphoreFdKHR"); |
||||
|
return new ImportedSemaphore(_context, _pool, semaphore); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var info = new VkImportSemaphoreWin32HandleInfoKHR() |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR, |
||||
|
handle = handle.Handle, |
||||
|
handleType = typeBit, |
||||
|
semaphore = semaphore.Handle |
||||
|
}; |
||||
|
_context.DeviceApi.ImportSemaphoreWin32HandleKHR(_context.DeviceHandle, &info) |
||||
|
.ThrowOnError("vkImportSemaphoreWin32HandleKHR"); |
||||
|
return new ImportedSemaphore(_context, _pool, semaphore); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class ImportedSemaphore : IVulkanExternalSemaphore |
||||
|
{ |
||||
|
private readonly VulkanContext _context; |
||||
|
private readonly VulkanCommandBufferPool _pool; |
||||
|
private VulkanSemaphore? _sem; |
||||
|
private VulkanSemaphore Sem => _sem ?? throw new ObjectDisposedException(nameof(ImportedSemaphore)); |
||||
|
|
||||
|
public ImportedSemaphore(VulkanContext context, VulkanCommandBufferPool pool, VulkanSemaphore sem) |
||||
|
{ |
||||
|
_context = context; |
||||
|
_pool = pool; |
||||
|
_sem = sem; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_sem?.Dispose(); |
||||
|
_sem = null; |
||||
|
} |
||||
|
|
||||
|
public ulong Handle => Sem.Handle.Handle; |
||||
|
|
||||
|
void SubmitSemaphore(VulkanSemaphore? wait, VulkanSemaphore? signal) |
||||
|
{ |
||||
|
var buf = _pool.CreateCommandBuffer(); |
||||
|
buf.BeginRecording(); |
||||
|
_context.DeviceApi.CmdPipelineBarrier( |
||||
|
buf.Handle, |
||||
|
VkPipelineStageFlags.VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, |
||||
|
VkPipelineStageFlags.VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, |
||||
|
0, |
||||
|
0, |
||||
|
IntPtr.Zero, |
||||
|
0, |
||||
|
IntPtr.Zero, |
||||
|
0, |
||||
|
null); |
||||
|
|
||||
|
buf.EndRecording(); |
||||
|
buf.Submit(wait != null ? new[] { wait }.AsSpan() : default, |
||||
|
new[] { VkPipelineStageFlags.VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT }, |
||||
|
signal != null ? new[] { signal }.AsSpan() : default); |
||||
|
} |
||||
|
public void SubmitWaitSemaphore() |
||||
|
{ |
||||
|
SubmitSemaphore(_sem, null); |
||||
|
} |
||||
|
|
||||
|
public void SubmitSignalSemaphore() |
||||
|
{ |
||||
|
SubmitSemaphore(null, _sem); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class ImportedImage : VulkanImageBase, IVulkanExternalImage |
||||
|
{ |
||||
|
private readonly IVulkanPlatformGraphicsContext _context; |
||||
|
private readonly IPlatformHandle _importHandle; |
||||
|
private readonly PlatformGraphicsExternalImageProperties _properties; |
||||
|
private readonly VkExternalMemoryHandleTypeFlagBits _typeBit; |
||||
|
public VulkanImageInfo Info => ImageInfo; |
||||
|
|
||||
|
public ImportedImage(IVulkanPlatformGraphicsContext context, VulkanCommandBufferPool commandBufferPool, |
||||
|
IPlatformHandle importHandle, |
||||
|
PlatformGraphicsExternalImageProperties properties) : base(context, commandBufferPool, |
||||
|
properties.Format switch |
||||
|
{ |
||||
|
PlatformGraphicsExternalImageFormat.B8G8R8A8UNorm => VkFormat.VK_FORMAT_B8G8R8A8_UNORM, |
||||
|
PlatformGraphicsExternalImageFormat.R8G8B8A8UNorm => VkFormat.VK_FORMAT_R8G8B8A8_UNORM, |
||||
|
_ => throw new ArgumentException($"Format {properties.Format} is not supported") |
||||
|
}, new PixelSize(properties.Width, properties.Height), 1) |
||||
|
{ |
||||
|
_context = context; |
||||
|
_importHandle = importHandle; |
||||
|
_properties = properties; |
||||
|
_typeBit = importHandle.HandleDescriptor switch |
||||
|
{ |
||||
|
KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaquePosixFileDescriptor => |
||||
|
VkExternalMemoryHandleTypeFlagBits.VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, |
||||
|
KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaqueKmtHandle => |
||||
|
VkExternalMemoryHandleTypeFlagBits.VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT, |
||||
|
KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaqueNtHandle => |
||||
|
VkExternalMemoryHandleTypeFlagBits.VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT, |
||||
|
_ => throw new NotSupportedException() |
||||
|
}; |
||||
|
var externalAlloc = new VkExternalMemoryImageCreateInfo |
||||
|
{ |
||||
|
handleTypes = _typeBit, |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO |
||||
|
}; |
||||
|
Initialize(&externalAlloc); |
||||
|
TransitionLayout(VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VkAccessFlags.VK_ACCESS_TRANSFER_READ_BIT); |
||||
|
this.CurrentLayout = VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; |
||||
|
} |
||||
|
|
||||
|
protected override VkDeviceMemory CreateMemory(VkImage image, ulong size, uint memoryTypeBits) |
||||
|
{ |
||||
|
var handle = _importHandle; |
||||
|
|
||||
|
if (_properties.MemoryOffset != 0 || _properties.MemorySize != size) |
||||
|
throw new Exception("Invalid memory size"); |
||||
|
|
||||
|
var dedicated = new VkMemoryDedicatedAllocateInfo() |
||||
|
{ |
||||
|
image = image, |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, |
||||
|
}; |
||||
|
|
||||
|
var isPosixHandle = handle.HandleDescriptor == |
||||
|
KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaquePosixFileDescriptor; |
||||
|
var win32Info = new VkImportMemoryWin32HandleInfoKHR |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR, |
||||
|
handle = handle.Handle, |
||||
|
handleType = _typeBit, |
||||
|
pNext = &dedicated |
||||
|
}; |
||||
|
var posixInfo = new VkImportMemoryFdInfoKHR() |
||||
|
{ |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, |
||||
|
handleType = _typeBit, |
||||
|
fd = isPosixHandle ? handle.Handle.ToInt32() : 0, |
||||
|
pNext = &dedicated |
||||
|
}; |
||||
|
|
||||
|
var memoryAllocateInfo = new VkMemoryAllocateInfo |
||||
|
{ |
||||
|
pNext = new IntPtr(isPosixHandle ? &posixInfo : &win32Info), |
||||
|
sType = VkStructureType.VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, |
||||
|
allocationSize = size, |
||||
|
memoryTypeIndex = (uint)VulkanMemoryHelper.FindSuitableMemoryTypeIndex(_context, |
||||
|
memoryTypeBits, |
||||
|
VkMemoryPropertyFlags.VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT), |
||||
|
}; |
||||
|
|
||||
|
_context.DeviceApi.AllocateMemory(_context.DeviceHandle, ref memoryAllocateInfo, IntPtr.Zero, |
||||
|
out var imageMemory).ThrowOnError("vkAllocateMemory"); |
||||
|
return imageMemory; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Vulkan; |
||||
|
|
||||
|
public struct VulkanImageInfo |
||||
|
{ |
||||
|
public uint Format { get; set; } |
||||
|
public PixelSize PixelSize { get; set; } |
||||
|
public ulong Handle { get; set; } |
||||
|
public uint Layout { get; set; } |
||||
|
public uint Tiling { get; set; } |
||||
|
public uint UsageFlags { get; set; } |
||||
|
public uint LevelCount { get; set; } |
||||
|
public uint SampleCount { get; set; } |
||||
|
public ulong MemoryHandle { get; set; } |
||||
|
public ulong ViewHandle { get; set; } |
||||
|
public ulong MemorySize { get; set; } |
||||
|
public bool IsProtected { get; set; } |
||||
|
} |
||||
@ -0,0 +1,107 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Vulkan.Interop; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
namespace Avalonia.Vulkan; |
||||
|
|
||||
|
|
||||
|
internal class VulkanKhrRenderTarget : IVulkanRenderTarget |
||||
|
{ |
||||
|
private VulkanKhrSurface _khrSurface; |
||||
|
private readonly IVulkanPlatformGraphicsContext _context; |
||||
|
private VulkanDisplay _display; |
||||
|
private VulkanImage? _image; |
||||
|
private readonly IVulkanKhrSurfacePlatformSurface _platformSurface; |
||||
|
public VkFormat Format { get; } |
||||
|
public bool IsRgba { get; } |
||||
|
|
||||
|
public VulkanKhrRenderTarget(IVulkanKhrSurfacePlatformSurface surface, IVulkanPlatformGraphicsContext context) |
||||
|
{ |
||||
|
_platformSurface = surface; |
||||
|
_khrSurface = new(context, surface); |
||||
|
_display = VulkanDisplay.CreateDisplay(context, _khrSurface); |
||||
|
_context = context; |
||||
|
IsRgba = _display.SurfaceFormat.format >= VkFormat.VK_FORMAT_R8G8B8A8_UNORM && |
||||
|
_display.SurfaceFormat.format <= VkFormat.VK_FORMAT_R8G8B8A8_SRGB; |
||||
|
|
||||
|
// Skia seems to only create surfaces from images with unorm format
|
||||
|
Format = IsRgba ? VkFormat.VK_FORMAT_R8G8B8A8_UNORM : VkFormat.VK_FORMAT_B8G8R8A8_UNORM; |
||||
|
} |
||||
|
|
||||
|
private void CreateImage() |
||||
|
{ |
||||
|
_image = new VulkanImage(_context, _display.CommandBufferPool, Format, _display.Size); |
||||
|
} |
||||
|
|
||||
|
private void DestroyImage() |
||||
|
{ |
||||
|
_context.DeviceApi.DeviceWaitIdle(_context.DeviceHandle); |
||||
|
_image?.Dispose(); |
||||
|
_image = null; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_context.DeviceApi.DeviceWaitIdle(_context.DeviceHandle); |
||||
|
DestroyImage(); |
||||
|
_display?.Dispose(); |
||||
|
_display = null!; |
||||
|
_khrSurface?.Dispose(); |
||||
|
_khrSurface = null!; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public IVulkanRenderSession BeginDraw() |
||||
|
{ |
||||
|
var l = _context.EnsureCurrent(); |
||||
|
_display.CommandBufferPool.FreeUsedCommandBuffers(); |
||||
|
if (_display.EnsureSwapchainAvailable() || _image == null) |
||||
|
{ |
||||
|
DestroyImage(); |
||||
|
CreateImage(); |
||||
|
} |
||||
|
else |
||||
|
_image.TransitionLayout(VkImageLayout.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, |
||||
|
VkAccessFlags.VK_ACCESS_NONE); |
||||
|
|
||||
|
return new RenderingSession(_display, _image!, IsRgba, _platformSurface.Scaling, l); |
||||
|
} |
||||
|
|
||||
|
public class RenderingSession : IVulkanRenderSession |
||||
|
{ |
||||
|
private readonly VulkanImage _image; |
||||
|
private readonly IDisposable _dispose; |
||||
|
|
||||
|
public RenderingSession(VulkanDisplay display, VulkanImage image, bool isRgba, double scaling, |
||||
|
IDisposable dispose) |
||||
|
{ |
||||
|
_image = image; |
||||
|
_dispose = dispose; |
||||
|
Display = display; |
||||
|
IsRgba = isRgba; |
||||
|
Scaling = scaling; |
||||
|
} |
||||
|
|
||||
|
public VulkanDisplay Display { get; } |
||||
|
public PixelSize Size => _image.Size; |
||||
|
public double Scaling { get; } |
||||
|
public bool IsYFlipped => true; |
||||
|
public VulkanImageInfo ImageInfo => _image.ImageInfo; |
||||
|
|
||||
|
public bool IsRgba { get; } |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var commandBuffer = Display.StartPresentation(); |
||||
|
Display.BlitImageToCurrentImage(commandBuffer, _image); |
||||
|
Display.EndPresentation(commandBuffer); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
_dispose.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,73 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Avalonia.Vulkan; |
||||
|
|
||||
|
public class VulkanOptions |
||||
|
{ |
||||
|
public VulkanInstanceCreationOptions VulkanInstanceCreationOptions { get; set; } = new(); |
||||
|
public VulkanDeviceCreationOptions VulkanDeviceCreationOptions { get; set; } = new(); |
||||
|
public IVulkanDevice? CustomSharedDevice { get; set; } |
||||
|
} |
||||
|
public class VulkanInstanceCreationOptions |
||||
|
{ |
||||
|
public VkGetInstanceProcAddressDelegate? CustomGetProcAddressDelegate { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets the application name of the vulkan instance
|
||||
|
/// </summary>
|
||||
|
public string? ApplicationName { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Specifies the vulkan api version to use
|
||||
|
/// </summary>
|
||||
|
public Version VulkanVersion{ get; set; } = new Version(1, 1, 0); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Specifies additional extensions to enable if available on the instance
|
||||
|
/// </summary>
|
||||
|
public IList<string> InstanceExtensions { get; set; } = new List<string>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Specifies layers to enable if available on the instance
|
||||
|
/// </summary>
|
||||
|
public IList<string> EnabledLayers { get; set; } = new List<string>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Enables the debug layer
|
||||
|
/// </summary>
|
||||
|
public bool UseDebug { get; set; } |
||||
|
|
||||
|
/* |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Sets the presentation mode the swapchain uses if available.
|
||||
|
/// </summary>
|
||||
|
//public PresentMode PresentMode { get; set; } = PresentMode.Mailbox;*/
|
||||
|
} |
||||
|
|
||||
|
public class VulkanDeviceCreationOptions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Specifies extensions to enable if available on the logical device
|
||||
|
/// </summary>
|
||||
|
public IList<string> DeviceExtensions { get; set; } = new List<string>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Selects the first suitable discrete gpu available
|
||||
|
/// </summary>
|
||||
|
public bool PreferDiscreteGpu { get; set; } |
||||
|
|
||||
|
public bool RequireComputeBit { get; set; } |
||||
|
} |
||||
|
|
||||
|
public class VulkanPlatformSpecificOptions |
||||
|
{ |
||||
|
public IList<string> RequiredInstanceExtensions { get; set; } = new List<string>(); |
||||
|
public VkGetInstanceProcAddressDelegate? GetProcAddressDelegate { get; set; } |
||||
|
public Func<IVulkanInstance, ulong>? DeviceCheckSurfaceFactory { get; set; } |
||||
|
public Dictionary<Type, object> PlatformFeatures { get; set; } = new(); |
||||
|
} |
||||
|
|
||||
|
public delegate IntPtr VkGetInstanceProcAddressDelegate(IntPtr instance, string name); |
||||
@ -0,0 +1,103 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Logging; |
||||
|
using Avalonia.Platform; |
||||
|
|
||||
|
namespace Avalonia.Vulkan; |
||||
|
|
||||
|
public class VulkanPlatformGraphics : IPlatformGraphics |
||||
|
{ |
||||
|
private readonly IVulkanDeviceFactory _factory; |
||||
|
private IVulkanPlatformGraphicsContext? _currentSharedContext; |
||||
|
private readonly VulkanPlatformSpecificOptions _platformOptions; |
||||
|
public VulkanPlatformGraphics(IVulkanDeviceFactory factory, VulkanPlatformSpecificOptions platformOptions) |
||||
|
{ |
||||
|
_factory = factory; |
||||
|
_platformOptions = platformOptions; |
||||
|
} |
||||
|
|
||||
|
public IPlatformGraphicsContext CreateContext() => |
||||
|
new VulkanContext(_factory.CreateDevice(_platformOptions), _platformOptions.PlatformFeatures); |
||||
|
|
||||
|
public IPlatformGraphicsContext GetSharedContext() |
||||
|
{ |
||||
|
if (_currentSharedContext?.IsLost == true) |
||||
|
_currentSharedContext = null; |
||||
|
return _currentSharedContext = |
||||
|
new VulkanContext(_factory.GetSharedDevice(_platformOptions), _platformOptions.PlatformFeatures); |
||||
|
} |
||||
|
|
||||
|
public bool UsesSharedContext => _factory.UsesShadedDevice; |
||||
|
|
||||
|
|
||||
|
public interface IVulkanDeviceFactory |
||||
|
{ |
||||
|
bool UsesShadedDevice { get; } |
||||
|
IVulkanDevice GetSharedDevice(VulkanPlatformSpecificOptions platformOptions); |
||||
|
IVulkanDevice CreateDevice(VulkanPlatformSpecificOptions platformOptions); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
class DefaultDeviceFactory : IVulkanDeviceFactory |
||||
|
{ |
||||
|
private readonly IVulkanInstance _instance; |
||||
|
private readonly VulkanDeviceCreationOptions _deviceOptions; |
||||
|
|
||||
|
public DefaultDeviceFactory(IVulkanInstance instance, VulkanDeviceCreationOptions deviceOptions) |
||||
|
{ |
||||
|
_instance = instance; |
||||
|
_deviceOptions = deviceOptions; |
||||
|
} |
||||
|
|
||||
|
public bool UsesShadedDevice => false; |
||||
|
|
||||
|
public IVulkanDevice GetSharedDevice(VulkanPlatformSpecificOptions platformOptions) => throw new NotSupportedException(); |
||||
|
|
||||
|
public IVulkanDevice CreateDevice(VulkanPlatformSpecificOptions platformOptions) |
||||
|
{ |
||||
|
return Interop.VulkanDevice.Create(_instance, _deviceOptions, platformOptions); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class CustomSharedDeviceFactory : IVulkanDeviceFactory |
||||
|
{ |
||||
|
private readonly IVulkanDevice _device; |
||||
|
|
||||
|
public CustomSharedDeviceFactory(IVulkanDevice device) |
||||
|
{ |
||||
|
_device = device; |
||||
|
} |
||||
|
|
||||
|
public bool UsesShadedDevice => true; |
||||
|
public IVulkanDevice GetSharedDevice(VulkanPlatformSpecificOptions platformOptions) => _device; |
||||
|
|
||||
|
public IVulkanDevice CreateDevice(VulkanPlatformSpecificOptions platformOptions) => |
||||
|
throw new NotSupportedException(); |
||||
|
} |
||||
|
|
||||
|
public static VulkanPlatformGraphics? TryCreate(VulkanOptions options, VulkanPlatformSpecificOptions platformOptions) |
||||
|
{ |
||||
|
if (options.CustomSharedDevice != null) |
||||
|
return new(new CustomSharedDeviceFactory(options.CustomSharedDevice), platformOptions); |
||||
|
|
||||
|
IVulkanInstance? instance = null; |
||||
|
try |
||||
|
{ |
||||
|
instance = VulkanInstance.Create(options.VulkanInstanceCreationOptions ?? new(), |
||||
|
platformOptions); |
||||
|
|
||||
|
var devOpts = options.VulkanDeviceCreationOptions ?? new(); |
||||
|
Interop.VulkanDevice.Create(instance, devOpts, platformOptions) |
||||
|
.Dispose(); |
||||
|
|
||||
|
return new VulkanPlatformGraphics(new DefaultDeviceFactory(instance, devOpts), platformOptions); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
//instance?.Dispose();
|
||||
|
Logger.TryGet(LogEventLevel.Error, "Vulkan")?.Log(null, "Unable to initialize Vulkan rendering: {0}", e); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
@ -0,0 +1,29 @@ |
|||||
|
using System; |
||||
|
using Avalonia.SourceGenerator; |
||||
|
using Avalonia.Vulkan; |
||||
|
using Avalonia.Vulkan.Interop; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
namespace Avalonia.X11.Vulkan; |
||||
|
partial class X11VulkanInterface |
||||
|
{ |
||||
|
|
||||
|
public X11VulkanInterface(IVulkanInstance instance) |
||||
|
{ |
||||
|
Initialize(name => instance.GetInstanceProcAddress(instance.Handle, name)); |
||||
|
} |
||||
|
|
||||
|
[GetProcAddress("vkCreateXlibSurfaceKHR")] |
||||
|
public partial int vkCreateXlibSurfaceKHR(IntPtr instance, ref VkXlibSurfaceCreateInfoKHR pCreateInfo, |
||||
|
IntPtr pAllocator, out ulong pSurface); |
||||
|
} |
||||
|
|
||||
|
struct VkXlibSurfaceCreateInfoKHR |
||||
|
{ |
||||
|
public const uint VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000; |
||||
|
public uint sType; |
||||
|
public IntPtr pNext; |
||||
|
public uint flags; |
||||
|
public IntPtr dpy; |
||||
|
public IntPtr window; |
||||
|
} |
||||
@ -0,0 +1,94 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Vulkan; |
||||
|
|
||||
|
namespace Avalonia.X11.Vulkan; |
||||
|
|
||||
|
internal class VulkanSupport |
||||
|
{ |
||||
|
private static IntPtr s_offscreenWindow; |
||||
|
|
||||
|
[DllImport("libvulkan.so.1")] |
||||
|
private static extern IntPtr vkGetInstanceProcAddr(IntPtr instance, string name); |
||||
|
|
||||
|
public static VulkanPlatformGraphics? TryInitialize(X11Info info, VulkanOptions options) |
||||
|
{ |
||||
|
s_offscreenWindow = XLib.XCreateSimpleWindow(info.DeferredDisplay, |
||||
|
XLib.XDefaultRootWindow(info.DeferredDisplay), 0, 0, 1, |
||||
|
1, 1, IntPtr.Zero, IntPtr.Zero); |
||||
|
XLib.XSync(info.DeferredDisplay, true); |
||||
|
|
||||
|
return VulkanPlatformGraphics.TryCreate(options ?? new(), new VulkanPlatformSpecificOptions |
||||
|
{ |
||||
|
RequiredInstanceExtensions = { "VK_KHR_xlib_surface" }, |
||||
|
GetProcAddressDelegate = vkGetInstanceProcAddr, |
||||
|
DeviceCheckSurfaceFactory = instance => CreateDummySurface(info.DeferredDisplay, instance), |
||||
|
PlatformFeatures = new Dictionary<Type, object> |
||||
|
{ |
||||
|
[typeof(IVulkanKhrSurfacePlatformSurfaceFactory)] = new VulkanSurfaceFactory(info.DeferredDisplay) |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
internal class VulkanSurfaceFactory : IVulkanKhrSurfacePlatformSurfaceFactory |
||||
|
{ |
||||
|
private readonly IntPtr _display; |
||||
|
|
||||
|
public VulkanSurfaceFactory(IntPtr display) |
||||
|
{ |
||||
|
_display = display; |
||||
|
} |
||||
|
|
||||
|
public bool CanRenderToSurface(IVulkanPlatformGraphicsContext context, object surface) => |
||||
|
surface is INativePlatformHandleSurface handle && handle.HandleDescriptor == "XID"; |
||||
|
|
||||
|
public IVulkanKhrSurfacePlatformSurface CreateSurface(IVulkanPlatformGraphicsContext context, object handle) => |
||||
|
new XidSurface(_display, (INativePlatformHandleSurface)handle); |
||||
|
} |
||||
|
|
||||
|
class XidSurface : IVulkanKhrSurfacePlatformSurface |
||||
|
{ |
||||
|
private readonly IntPtr _display; |
||||
|
private readonly INativePlatformHandleSurface _handle; |
||||
|
|
||||
|
public XidSurface(IntPtr display, INativePlatformHandleSurface handle) |
||||
|
{ |
||||
|
_display = display; |
||||
|
_handle = handle; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
// No-op
|
||||
|
} |
||||
|
|
||||
|
public double Scaling => _handle.Scaling; |
||||
|
public PixelSize Size => _handle.Size; |
||||
|
public ulong CreateSurface(IVulkanPlatformGraphicsContext context) => |
||||
|
CreateXlibSurface(_display, _handle.Handle, context.Instance); |
||||
|
} |
||||
|
|
||||
|
private static ulong CreateDummySurface(IntPtr display, IVulkanInstance instance) |
||||
|
{ |
||||
|
var surf = CreateXlibSurface(display, s_offscreenWindow, instance); |
||||
|
XLib.XSync(display, true); |
||||
|
return surf; |
||||
|
} |
||||
|
|
||||
|
private static ulong CreateXlibSurface(IntPtr display, IntPtr window, IVulkanInstance instance) |
||||
|
{ |
||||
|
var vulkanXlib = new X11VulkanInterface(instance); |
||||
|
var createInfo = new VkXlibSurfaceCreateInfoKHR |
||||
|
{ |
||||
|
sType = VkXlibSurfaceCreateInfoKHR.VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, |
||||
|
dpy = display, |
||||
|
window = window |
||||
|
}; |
||||
|
VulkanException.ThrowOnError("vkCreateXlibSurfaceKHR", |
||||
|
vulkanXlib.vkCreateXlibSurfaceKHR(instance.Handle, ref createInfo, IntPtr.Zero, out var surface)); |
||||
|
return surface; |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,124 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Rendering.Composition; |
||||
|
using Avalonia.Vulkan; |
||||
|
using SkiaSharp; |
||||
|
|
||||
|
namespace Avalonia.Skia.Vulkan; |
||||
|
|
||||
|
internal class VulkanSkiaExternalObjectsFeature : IExternalObjectsRenderInterfaceContextFeature |
||||
|
{ |
||||
|
private readonly VulkanSkiaGpu _gpu; |
||||
|
private readonly IVulkanPlatformGraphicsContext _context; |
||||
|
private readonly IVulkanContextExternalObjectsFeature _feature; |
||||
|
|
||||
|
public VulkanSkiaExternalObjectsFeature(VulkanSkiaGpu gpu, |
||||
|
IVulkanPlatformGraphicsContext context, IVulkanContextExternalObjectsFeature feature) |
||||
|
{ |
||||
|
_gpu = gpu; |
||||
|
_context = context; |
||||
|
_feature = feature; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public IPlatformRenderInterfaceImportedImage ImportImage(IPlatformHandle handle, |
||||
|
PlatformGraphicsExternalImageProperties properties) => |
||||
|
new Image(_gpu, _feature.ImportImage(handle, properties), properties); |
||||
|
|
||||
|
public IPlatformRenderInterfaceImportedSemaphore ImportSemaphore(IPlatformHandle handle) => new Semaphore(_feature.ImportSemaphore(handle)); |
||||
|
|
||||
|
class Semaphore : IPlatformRenderInterfaceImportedSemaphore |
||||
|
{ |
||||
|
private IVulkanExternalSemaphore? _inner; |
||||
|
|
||||
|
public Semaphore(IVulkanExternalSemaphore inner) |
||||
|
{ |
||||
|
_inner = inner; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_inner?.Dispose(); |
||||
|
_inner = null; |
||||
|
} |
||||
|
|
||||
|
public IVulkanExternalSemaphore Inner => |
||||
|
_inner ?? throw new ObjectDisposedException(nameof(IVulkanExternalSemaphore)); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
class Image : IPlatformRenderInterfaceImportedImage |
||||
|
{ |
||||
|
private readonly VulkanSkiaGpu _gpu; |
||||
|
private IVulkanExternalImage? _inner; |
||||
|
private readonly PlatformGraphicsExternalImageProperties _properties; |
||||
|
|
||||
|
public Image(VulkanSkiaGpu gpu, IVulkanExternalImage inner, PlatformGraphicsExternalImageProperties properties) |
||||
|
{ |
||||
|
_gpu = gpu; |
||||
|
_inner = inner; |
||||
|
_properties = properties; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_inner?.Dispose(); |
||||
|
_inner = null; |
||||
|
} |
||||
|
|
||||
|
public IBitmapImpl SnapshotWithKeyedMutex(uint acquireIndex, uint releaseIndex) => throw new NotSupportedException(); |
||||
|
|
||||
|
public IBitmapImpl SnapshotWithSemaphores(IPlatformRenderInterfaceImportedSemaphore waitForSemaphore, |
||||
|
IPlatformRenderInterfaceImportedSemaphore signalSemaphore) |
||||
|
{ |
||||
|
var info = _inner!.Info; |
||||
|
|
||||
|
_gpu.GrContext.ResetContext(); |
||||
|
((Semaphore)waitForSemaphore).Inner.SubmitWaitSemaphore(); |
||||
|
var imageInfo = new GRVkImageInfo |
||||
|
{ |
||||
|
CurrentQueueFamily = _gpu.Vulkan.Device.GraphicsQueueFamilyIndex, |
||||
|
Format = info.Format, |
||||
|
Image = (ulong)info.Handle, |
||||
|
ImageLayout = info.Layout, |
||||
|
ImageTiling = info.Tiling, |
||||
|
ImageUsageFlags = info.UsageFlags, |
||||
|
LevelCount = info.LevelCount, |
||||
|
SampleCount = info.SampleCount, |
||||
|
Protected = info.IsProtected, |
||||
|
Alloc = new GRVkAlloc |
||||
|
{ |
||||
|
Memory = (ulong)info.MemoryHandle, |
||||
|
Size = info.MemorySize |
||||
|
} |
||||
|
}; |
||||
|
using var renderTarget = new GRBackendRenderTarget(_properties.Width, _properties.Height, 1, imageInfo); |
||||
|
using var surface = SKSurface.Create(_gpu.GrContext, renderTarget, |
||||
|
_properties.TopLeftOrigin ? GRSurfaceOrigin.TopLeft : GRSurfaceOrigin.BottomLeft, |
||||
|
_properties.Format == PlatformGraphicsExternalImageFormat.R8G8B8A8UNorm |
||||
|
? SKColorType.Rgba8888 |
||||
|
: SKColorType.Bgra8888, SKColorSpace.CreateSrgb()); |
||||
|
var image = surface.Snapshot(); |
||||
|
_gpu.GrContext.Flush(); |
||||
|
//surface.Canvas.Flush();
|
||||
|
|
||||
|
((Semaphore)signalSemaphore).Inner.SubmitSignalSemaphore(); |
||||
|
|
||||
|
return new ImmutableBitmap(image); |
||||
|
} |
||||
|
|
||||
|
public IBitmapImpl SnapshotWithAutomaticSync() => throw new NotSupportedException(); |
||||
|
} |
||||
|
|
||||
|
public IPlatformRenderInterfaceImportedImage ImportImage(ICompositionImportableSharedGpuContextImage image) => throw new System.NotSupportedException(); |
||||
|
|
||||
|
public CompositionGpuImportedImageSynchronizationCapabilities |
||||
|
GetSynchronizationCapabilities(string imageHandleType) => _feature.GetSynchronizationCapabilities(imageHandleType); |
||||
|
|
||||
|
public byte[]? DeviceUuid => _feature.DeviceUuid; |
||||
|
public byte[]? DeviceLuid => _feature.DeviceLuid; |
||||
|
|
||||
|
public IReadOnlyList<string> SupportedImageHandleTypes => _feature.SupportedImageHandleTypes; |
||||
|
public IReadOnlyList<string> SupportedSemaphoreTypes => _feature.SupportedSemaphoreTypes; |
||||
|
} |
||||
@ -0,0 +1,86 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Vulkan; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Rendering; |
||||
|
using SkiaSharp; |
||||
|
|
||||
|
namespace Avalonia.Skia.Vulkan; |
||||
|
|
||||
|
internal class VulkanSkiaGpu : ISkiaGpu |
||||
|
{ |
||||
|
private readonly VulkanSkiaExternalObjectsFeature? _externalObjects; |
||||
|
public IVulkanPlatformGraphicsContext Vulkan { get; private set; } |
||||
|
public GRContext GrContext { get; private set; } |
||||
|
|
||||
|
public VulkanSkiaGpu(IVulkanPlatformGraphicsContext vulkan, long? maxResourceBytes) |
||||
|
{ |
||||
|
Vulkan = vulkan; |
||||
|
var device = vulkan.Device; |
||||
|
using (Vulkan.EnsureCurrent()) |
||||
|
{ |
||||
|
IntPtr GetProcAddressWrapper(string name, IntPtr instance, IntPtr device) |
||||
|
{ |
||||
|
if (device != IntPtr.Zero) |
||||
|
{ |
||||
|
var addr = Vulkan.Instance.GetDeviceProcAddress(device, name); |
||||
|
if (addr != IntPtr.Zero) |
||||
|
return addr; |
||||
|
} |
||||
|
|
||||
|
if (instance != IntPtr.Zero) |
||||
|
{ |
||||
|
var addr = Vulkan.Instance.GetInstanceProcAddress(instance, name); |
||||
|
if (addr != IntPtr.Zero) |
||||
|
return addr; |
||||
|
} |
||||
|
|
||||
|
return Vulkan.Instance.GetInstanceProcAddress(IntPtr.Zero, name); |
||||
|
} |
||||
|
|
||||
|
var ctx = new GRVkBackendContext |
||||
|
{ |
||||
|
VkInstance = device.Instance.Handle, |
||||
|
VkPhysicalDevice = device.PhysicalDeviceHandle, |
||||
|
VkDevice = device.Handle, |
||||
|
VkQueue = device.MainQueueHandle, |
||||
|
GraphicsQueueIndex = device.GraphicsQueueFamilyIndex, |
||||
|
GetProcedureAddress = GetProcAddressWrapper |
||||
|
}; |
||||
|
|
||||
|
GrContext = GRContext.CreateVulkan(ctx) ?? |
||||
|
throw new VulkanException("Unable to create GrContext from IVulkanDevice"); |
||||
|
|
||||
|
if (maxResourceBytes.HasValue) |
||||
|
GrContext.SetResourceCacheLimit(maxResourceBytes.Value); |
||||
|
} |
||||
|
|
||||
|
if (vulkan.TryGetFeature<IVulkanContextExternalObjectsFeature>(out var externalObjects)) |
||||
|
_externalObjects = new VulkanSkiaExternalObjectsFeature(this, vulkan, externalObjects); |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
Vulkan.Dispose(); |
||||
|
} |
||||
|
|
||||
|
public object? TryGetFeature(Type featureType) |
||||
|
{ |
||||
|
if (featureType == typeof(IExternalObjectsRenderInterfaceContextFeature)) |
||||
|
return _externalObjects; |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public bool IsLost => Vulkan.IsLost; |
||||
|
public IDisposable EnsureCurrent() => Vulkan.EnsureCurrent(); |
||||
|
|
||||
|
|
||||
|
public ISkiaGpuRenderTarget TryCreateRenderTarget(IEnumerable<object> surfaces) |
||||
|
{ |
||||
|
var target = Vulkan.CreateRenderTarget(surfaces); |
||||
|
return new VulkanSkiaRenderTarget(this, target); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public ISkiaSurface? TryCreateSurface(PixelSize size, ISkiaGpuRenderSession? session) => null; |
||||
|
} |
||||
@ -0,0 +1,105 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Skia.Helpers; |
||||
|
using Avalonia.Vulkan; |
||||
|
using SkiaSharp; |
||||
|
|
||||
|
namespace Avalonia.Skia.Vulkan; |
||||
|
|
||||
|
class VulkanSkiaRenderTarget : ISkiaGpuRenderTarget |
||||
|
{ |
||||
|
private readonly VulkanSkiaGpu _gpu; |
||||
|
private readonly IVulkanRenderTarget _target; |
||||
|
|
||||
|
public VulkanSkiaRenderTarget(VulkanSkiaGpu gpu, IVulkanRenderTarget target) |
||||
|
{ |
||||
|
_gpu = gpu; |
||||
|
_target = target; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_target.Dispose(); |
||||
|
} |
||||
|
|
||||
|
public ISkiaGpuRenderSession BeginRenderingSession() |
||||
|
{ |
||||
|
var session = _target.BeginDraw(); |
||||
|
bool success = false; |
||||
|
try |
||||
|
{ |
||||
|
var size = session.Size; |
||||
|
var scaling = session.Scaling; |
||||
|
if (size.Width <= 0 || size.Height <= 0 || scaling < 0) |
||||
|
{ |
||||
|
session.Dispose(); |
||||
|
throw new InvalidOperationException( |
||||
|
$"Can't create drawing context for surface with {size} size and {scaling} scaling"); |
||||
|
} |
||||
|
_gpu.GrContext.ResetContext(); |
||||
|
var sessionImageInfo = session.ImageInfo; |
||||
|
var imageInfo = new GRVkImageInfo |
||||
|
{ |
||||
|
CurrentQueueFamily = _gpu.Vulkan.Device.GraphicsQueueFamilyIndex, |
||||
|
Format = sessionImageInfo.Format, |
||||
|
Image = (ulong)sessionImageInfo.Handle, |
||||
|
ImageLayout = sessionImageInfo.Layout, |
||||
|
ImageTiling = sessionImageInfo.Tiling, |
||||
|
ImageUsageFlags = sessionImageInfo.UsageFlags, |
||||
|
LevelCount = sessionImageInfo.LevelCount, |
||||
|
SampleCount = sessionImageInfo.SampleCount, |
||||
|
Protected = sessionImageInfo.IsProtected, |
||||
|
Alloc = new GRVkAlloc |
||||
|
{ |
||||
|
Memory = (ulong)sessionImageInfo.MemoryHandle, |
||||
|
Size = sessionImageInfo.MemorySize |
||||
|
} |
||||
|
}; |
||||
|
using var renderTarget = new GRBackendRenderTarget(size.Width, size.Height, 1, imageInfo); |
||||
|
var surface = SKSurface.Create(_gpu.GrContext, renderTarget, |
||||
|
session.IsYFlipped ? GRSurfaceOrigin.TopLeft : GRSurfaceOrigin.BottomLeft, |
||||
|
session.IsRgba ? SKColorType.Rgba8888 : SKColorType.Bgra8888, SKColorSpace.CreateSrgb()); |
||||
|
|
||||
|
if (surface == null) |
||||
|
throw new InvalidOperationException( |
||||
|
$"Surface can't be created with the provided render target"); |
||||
|
success = true; |
||||
|
return new VulkanSkiaRenderSession(_gpu.GrContext, surface, session); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
if(!success) |
||||
|
session.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public bool IsCorrupted => false; |
||||
|
|
||||
|
|
||||
|
internal class VulkanSkiaRenderSession : ISkiaGpuRenderSession |
||||
|
{ |
||||
|
private readonly IVulkanRenderSession _vulkanSession; |
||||
|
|
||||
|
public VulkanSkiaRenderSession(GRContext grContext, |
||||
|
SKSurface surface, |
||||
|
IVulkanRenderSession vulkanSession) |
||||
|
{ |
||||
|
GrContext = grContext; |
||||
|
SkSurface = surface; |
||||
|
_vulkanSession = vulkanSession; |
||||
|
SurfaceOrigin = vulkanSession.IsYFlipped ? GRSurfaceOrigin.TopLeft : GRSurfaceOrigin.BottomLeft; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
SkSurface.Canvas.Flush(); |
||||
|
SkSurface.Dispose(); |
||||
|
GrContext.Flush(); |
||||
|
_vulkanSession.Dispose(); |
||||
|
} |
||||
|
|
||||
|
public GRContext GrContext { get; } |
||||
|
public SKSurface SkSurface { get; } |
||||
|
public double ScaleFactor => _vulkanSession.Scaling; |
||||
|
public GRSurfaceOrigin SurfaceOrigin { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
using System; |
||||
|
using Avalonia.SourceGenerator; |
||||
|
using Avalonia.Vulkan; |
||||
|
using Avalonia.Vulkan.UnmanagedInterop; |
||||
|
|
||||
|
namespace Avalonia.Win32.Vulkan; |
||||
|
partial class Win32VulkanInterface |
||||
|
{ |
||||
|
public Win32VulkanInterface(IVulkanInstance instance) |
||||
|
{ |
||||
|
Initialize(name => instance.GetInstanceProcAddress(instance.Handle, name)); |
||||
|
} |
||||
|
|
||||
|
[GetProcAddress("vkCreateWin32SurfaceKHR")] |
||||
|
public partial int vkCreateWin32SurfaceKHR(IntPtr instance, ref VkWin32SurfaceCreateInfoKHR pCreateInfo, |
||||
|
IntPtr pAllocator, out ulong pSurface); |
||||
|
} |
||||
|
|
||||
|
struct VkWin32SurfaceCreateInfoKHR |
||||
|
{ |
||||
|
public const uint VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000; |
||||
|
public uint sType; |
||||
|
public IntPtr pNext; |
||||
|
public uint flags; |
||||
|
public IntPtr hinstance; |
||||
|
public IntPtr hwnd; |
||||
|
} |
||||
@ -0,0 +1,71 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Vulkan; |
||||
|
using Avalonia.Win32.Interop; |
||||
|
|
||||
|
namespace Avalonia.Win32.Vulkan; |
||||
|
|
||||
|
internal class VulkanSupport |
||||
|
{ |
||||
|
[DllImport("vulkan-1.dll")] |
||||
|
private static extern IntPtr vkGetInstanceProcAddr(IntPtr instance, string name); |
||||
|
|
||||
|
public static VulkanPlatformGraphics? TryInitialize(VulkanOptions options) => |
||||
|
VulkanPlatformGraphics.TryCreate(options ?? new(), new VulkanPlatformSpecificOptions |
||||
|
{ |
||||
|
RequiredInstanceExtensions = { "VK_KHR_win32_surface" }, |
||||
|
GetProcAddressDelegate = vkGetInstanceProcAddr, |
||||
|
DeviceCheckSurfaceFactory = instance => CreateHwndSurface(OffscreenParentWindow.Handle, instance), |
||||
|
PlatformFeatures = new Dictionary<Type, object> |
||||
|
{ |
||||
|
[typeof(IVulkanKhrSurfacePlatformSurfaceFactory)] = new VulkanSurfaceFactory() |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
internal class VulkanSurfaceFactory : IVulkanKhrSurfacePlatformSurfaceFactory |
||||
|
{ |
||||
|
public bool CanRenderToSurface(IVulkanPlatformGraphicsContext context, object surface) => |
||||
|
surface is INativePlatformHandleSurface handle && handle.HandleDescriptor == "HWND"; |
||||
|
|
||||
|
public IVulkanKhrSurfacePlatformSurface CreateSurface(IVulkanPlatformGraphicsContext context, object handle) => |
||||
|
new HwndVulkanSurface((INativePlatformHandleSurface)handle); |
||||
|
} |
||||
|
|
||||
|
class HwndVulkanSurface : IVulkanKhrSurfacePlatformSurface |
||||
|
{ |
||||
|
private readonly INativePlatformHandleSurface _handle; |
||||
|
|
||||
|
public HwndVulkanSurface(INativePlatformHandleSurface handle) |
||||
|
{ |
||||
|
_handle = handle; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
// No-op
|
||||
|
} |
||||
|
|
||||
|
public double Scaling => _handle.Scaling; |
||||
|
public PixelSize Size => _handle.Size; |
||||
|
public ulong CreateSurface(IVulkanPlatformGraphicsContext context) => |
||||
|
CreateHwndSurface(_handle.Handle, context.Instance); |
||||
|
} |
||||
|
|
||||
|
private static ulong CreateHwndSurface(IntPtr window, IVulkanInstance instance) |
||||
|
{ |
||||
|
var vulkanWin32 = new Win32VulkanInterface(instance); |
||||
|
var createInfo = new VkWin32SurfaceCreateInfoKHR() |
||||
|
{ |
||||
|
|
||||
|
sType = VkWin32SurfaceCreateInfoKHR.VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, |
||||
|
hinstance = UnmanagedMethods.GetModuleHandle(null), |
||||
|
hwnd = window |
||||
|
}; |
||||
|
VulkanException.ThrowOnError("vkCreateWin32SurfaceKHR", |
||||
|
vulkanWin32.vkCreateWin32SurfaceKHR(instance.Handle, ref createInfo, IntPtr.Zero, out var surface)); |
||||
|
return surface; |
||||
|
} |
||||
|
} |
||||
|
|
||||
Loading…
Reference in new issue