Vulkan backend (#12737)
* 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 windows
This commit is contained in:
@@ -30,6 +30,7 @@
|
|||||||
"src\\Avalonia.MicroCom\\Avalonia.MicroCom.csproj",
|
"src\\Avalonia.MicroCom\\Avalonia.MicroCom.csproj",
|
||||||
"src\\Avalonia.Native\\Avalonia.Native.csproj",
|
"src\\Avalonia.Native\\Avalonia.Native.csproj",
|
||||||
"src\\Avalonia.OpenGL\\Avalonia.OpenGL.csproj",
|
"src\\Avalonia.OpenGL\\Avalonia.OpenGL.csproj",
|
||||||
|
"src\\Avalonia.Vulkan\\Avalonia.Vulkan.csproj",
|
||||||
"src\\Avalonia.ReactiveUI\\Avalonia.ReactiveUI.csproj",
|
"src\\Avalonia.ReactiveUI\\Avalonia.ReactiveUI.csproj",
|
||||||
"src\\Avalonia.Remote.Protocol\\Avalonia.Remote.Protocol.csproj",
|
"src\\Avalonia.Remote.Protocol\\Avalonia.Remote.Protocol.csproj",
|
||||||
"src\\Avalonia.Themes.Fluent\\Avalonia.Themes.Fluent.csproj",
|
"src\\Avalonia.Themes.Fluent\\Avalonia.Themes.Fluent.csproj",
|
||||||
|
|||||||
@@ -298,6 +298,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnloadableAssemblyLoadConte
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnloadableAssemblyLoadContextPlug", "samples\UnloadableAssemblyLoadContext\UnloadableAssemblyLoadContextPlug\UnloadableAssemblyLoadContextPlug.csproj", "{DA5F1FF9-4259-4C54-B443-85CFA226EE6A}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnloadableAssemblyLoadContextPlug", "samples\UnloadableAssemblyLoadContext\UnloadableAssemblyLoadContextPlug\UnloadableAssemblyLoadContextPlug.csproj", "{DA5F1FF9-4259-4C54-B443-85CFA226EE6A}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Vulkan", "src\Avalonia.Vulkan\Avalonia.Vulkan.csproj", "{3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}"
|
||||||
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.RenderTests.WpfCompare", "tests\Avalonia.RenderTests.WpfCompare\Avalonia.RenderTests.WpfCompare.csproj", "{9AE1B827-21AC-4063-AB22-C8804B7F931E}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.RenderTests.WpfCompare", "tests\Avalonia.RenderTests.WpfCompare\Avalonia.RenderTests.WpfCompare.csproj", "{9AE1B827-21AC-4063-AB22-C8804B7F931E}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
@@ -676,6 +678,10 @@ Global
|
|||||||
{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Release|Any CPU.Build.0 = Release|Any CPU
|
{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{3E2DE2B6-13BC-4C27-BCB9-A423B86CAF77}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
|||||||
@@ -39,4 +39,5 @@
|
|||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002EDaemon_002ESettings_002EMigration_002ESwaWarningsModeSettingsMigrate/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002EDaemon_002ESettings_002EMigration_002ESwaWarningsModeSettingsMigrate/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Activatable/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Activatable/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Avalonia/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Avalonia/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Fcitx/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Fcitx/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=swapchain/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj" />
|
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.DesignerSupport/Avalonia.DesignerSupport.csproj" />
|
||||||
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.OpenGL/Avalonia.OpenGL.csproj" />
|
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.OpenGL/Avalonia.OpenGL.csproj" />
|
||||||
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Metal/Avalonia.Metal.csproj" />
|
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Metal/Avalonia.Metal.csproj" />
|
||||||
|
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Vulkan/Avalonia.Vulkan.csproj" />
|
||||||
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Dialogs/Avalonia.Dialogs.csproj" />
|
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Dialogs/Avalonia.Dialogs.csproj" />
|
||||||
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Markup/Avalonia.Markup/Avalonia.Markup.csproj" />
|
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Markup/Avalonia.Markup/Avalonia.Markup.csproj" />
|
||||||
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj" />
|
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj" />
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ using Avalonia.LinuxFramebuffer.Output;
|
|||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
using Avalonia.Rendering.Composition;
|
using Avalonia.Rendering.Composition;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using Avalonia.Vulkan;
|
||||||
using ControlCatalog.Pages;
|
using ControlCatalog.Pages;
|
||||||
|
|
||||||
namespace ControlCatalog.NetCore
|
namespace ControlCatalog.NetCore
|
||||||
@@ -133,7 +133,15 @@ namespace ControlCatalog.NetCore
|
|||||||
{
|
{
|
||||||
EnableMultiTouch = true,
|
EnableMultiTouch = true,
|
||||||
UseDBusMenu = true,
|
UseDBusMenu = true,
|
||||||
EnableIme = true
|
EnableIme = true,
|
||||||
|
})
|
||||||
|
|
||||||
|
.With(new VulkanOptions
|
||||||
|
{
|
||||||
|
VulkanInstanceCreationOptions = new ()
|
||||||
|
{
|
||||||
|
UseDebug = true
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.With(new CompositionOptions()
|
.With(new CompositionOptions()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
global using System.Reactive.Disposables;
|
global using System.Reactive.Disposables;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Logging;
|
||||||
|
using Avalonia.Vulkan;
|
||||||
|
|
||||||
namespace GpuInterop
|
namespace GpuInterop
|
||||||
{
|
{
|
||||||
public class Program
|
public class Program
|
||||||
@@ -10,6 +13,21 @@ namespace GpuInterop
|
|||||||
public static AppBuilder BuildAvaloniaApp() =>
|
public static AppBuilder BuildAvaloniaApp() =>
|
||||||
AppBuilder.Configure<App>()
|
AppBuilder.Configure<App>()
|
||||||
.UsePlatformDetect()
|
.UsePlatformDetect()
|
||||||
.LogToTrace();
|
.With(new Win32PlatformOptions
|
||||||
|
{
|
||||||
|
RenderingMode = new []
|
||||||
|
{
|
||||||
|
Win32RenderingMode.Vulkan
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.With(new X11PlatformOptions(){RenderingMode =new[] { X11RenderingMode.Vulkan } })
|
||||||
|
.With(new VulkanOptions()
|
||||||
|
{
|
||||||
|
VulkanInstanceCreationOptions = new VulkanInstanceCreationOptions()
|
||||||
|
{
|
||||||
|
UseDebug = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.LogToTrace(LogEventLevel.Debug, "Vulkan");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -397,7 +397,7 @@ unsafe class VulkanContent : IDisposable
|
|||||||
Matrix4x4.CreatePerspectiveFieldOfView((float)(Math.PI / 4), (float)size.Width / size.Height,
|
Matrix4x4.CreatePerspectiveFieldOfView((float)(Math.PI / 4), (float)size.Width / size.Height,
|
||||||
0.01f, 1000);
|
0.01f, 1000);
|
||||||
|
|
||||||
_colorAttachment = new VulkanImage(_context, (uint)Format.R8G8B8A8Unorm, size, false);
|
_colorAttachment = new VulkanImage(_context, (uint)Format.R8G8B8A8Unorm, size, false, Array.Empty<string>());
|
||||||
CreateDepthAttachment(size);
|
CreateDepthAttachment(size);
|
||||||
|
|
||||||
var api = _context.Api;
|
var api = _context.Api;
|
||||||
|
|||||||
@@ -101,8 +101,10 @@ public unsafe class VulkanContext : IDisposable
|
|||||||
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
if (!gpuInterop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes
|
if (!(gpuInterop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes
|
||||||
.D3D11TextureGlobalSharedHandle)
|
.D3D11TextureGlobalSharedHandle)
|
||||||
|
|| gpuInterop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes
|
||||||
|
.VulkanOpaqueNtHandle))
|
||||||
)
|
)
|
||||||
return (null, "Image sharing is not supported by the current backend");
|
return (null, "Image sharing is not supported by the current backend");
|
||||||
requireDeviceExtensions.Add(KhrExternalMemoryWin32.ExtensionName);
|
requireDeviceExtensions.Add(KhrExternalMemoryWin32.ExtensionName);
|
||||||
@@ -240,10 +242,13 @@ public unsafe class VulkanContext : IDisposable
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
D3DDevice? d3dDevice = null;
|
D3DDevice? d3dDevice = null;
|
||||||
if (physicalDeviceIDProperties.DeviceLuidvalid &&
|
if (physicalDeviceIDProperties.DeviceLuidvalid &&
|
||||||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
|
||||||
|
!gpuInterop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaqueNtHandle)
|
||||||
|
)
|
||||||
d3dDevice = D3DMemoryHelper.CreateDeviceByLuid(
|
d3dDevice = D3DMemoryHelper.CreateDeviceByLuid(
|
||||||
new Span<byte>(physicalDeviceIDProperties.DeviceLuid, 8));
|
new Span<byte>(physicalDeviceIDProperties.DeviceLuid, 8));
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
@@ -44,7 +46,7 @@ public unsafe class VulkanImage : IDisposable
|
|||||||
public uint CurrentLayout => (uint) _currentLayout;
|
public uint CurrentLayout => (uint) _currentLayout;
|
||||||
|
|
||||||
public VulkanImage(VulkanContext vk, uint format, PixelSize size,
|
public VulkanImage(VulkanContext vk, uint format, PixelSize size,
|
||||||
bool exportable, uint mipLevels = 0)
|
bool exportable, IReadOnlyList<string> supportedHandleTypes)
|
||||||
{
|
{
|
||||||
_vk = vk;
|
_vk = vk;
|
||||||
_instance = vk.Instance;
|
_instance = vk.Instance;
|
||||||
@@ -62,8 +64,12 @@ public unsafe class VulkanImage : IDisposable
|
|||||||
//MipLevels = MipLevels != 0 ? MipLevels : (uint)Math.Floor(Math.Log(Math.Max(Size.Width, Size.Height), 2));
|
//MipLevels = MipLevels != 0 ? MipLevels : (uint)Math.Floor(Math.Log(Math.Max(Size.Width, Size.Height), 2));
|
||||||
|
|
||||||
var handleType = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
|
var handleType = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
|
||||||
ExternalMemoryHandleTypeFlags.D3D11TextureBit :
|
(supportedHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes.D3D11TextureNtHandle)
|
||||||
|
&& !supportedHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaqueNtHandle) ?
|
||||||
|
ExternalMemoryHandleTypeFlags.D3D11TextureBit :
|
||||||
|
ExternalMemoryHandleTypeFlags.OpaqueWin32Bit) :
|
||||||
ExternalMemoryHandleTypeFlags.OpaqueFDBit;
|
ExternalMemoryHandleTypeFlags.OpaqueFDBit;
|
||||||
|
|
||||||
var externalMemoryCreateInfo = new ExternalMemoryImageCreateInfo
|
var externalMemoryCreateInfo = new ExternalMemoryImageCreateInfo
|
||||||
{
|
{
|
||||||
SType = StructureType.ExternalMemoryImageCreateInfo,
|
SType = StructureType.ExternalMemoryImageCreateInfo,
|
||||||
@@ -96,35 +102,37 @@ public unsafe class VulkanImage : IDisposable
|
|||||||
Api.GetImageMemoryRequirements(_device, InternalHandle,
|
Api.GetImageMemoryRequirements(_device, InternalHandle,
|
||||||
out var memoryRequirements);
|
out var memoryRequirements);
|
||||||
|
|
||||||
|
|
||||||
var fdExport = new ExportMemoryAllocateInfo
|
|
||||||
{
|
|
||||||
HandleTypes = handleType, SType = StructureType.ExportMemoryAllocateInfo
|
|
||||||
};
|
|
||||||
var dedicatedAllocation = new MemoryDedicatedAllocateInfoKHR
|
var dedicatedAllocation = new MemoryDedicatedAllocateInfoKHR
|
||||||
{
|
{
|
||||||
SType = StructureType.MemoryDedicatedAllocateInfoKhr,
|
SType = StructureType.MemoryDedicatedAllocateInfoKhr,
|
||||||
Image = image
|
Image = image
|
||||||
};
|
};
|
||||||
ImportMemoryWin32HandleInfoKHR handleImport = default;
|
|
||||||
if (exportable && RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
var fdExport = new ExportMemoryAllocateInfo
|
||||||
{
|
{
|
||||||
_d3dTexture2D = D3DMemoryHelper.CreateMemoryHandle(vk.D3DDevice!, size, Format);
|
HandleTypes = handleType, SType = StructureType.ExportMemoryAllocateInfo,
|
||||||
using var dxgi = _d3dTexture2D.QueryInterface<SharpDX.DXGI.Resource1>();
|
PNext = &dedicatedAllocation
|
||||||
|
};
|
||||||
handleImport = new ImportMemoryWin32HandleInfoKHR
|
|
||||||
{
|
ImportMemoryWin32HandleInfoKHR handleImport = default;
|
||||||
PNext = &dedicatedAllocation,
|
if (handleType == ExternalMemoryHandleTypeFlags.D3D11TextureBit && exportable)
|
||||||
SType = StructureType.ImportMemoryWin32HandleInfoKhr,
|
{
|
||||||
HandleType = ExternalMemoryHandleTypeFlags.D3D11TextureBit,
|
_d3dTexture2D = D3DMemoryHelper.CreateMemoryHandle(vk.D3DDevice, size, Format);
|
||||||
Handle = dxgi.CreateSharedHandle(null, SharedResourceFlags.Read | SharedResourceFlags.Write),
|
using var dxgi = _d3dTexture2D.QueryInterface<SharpDX.DXGI.Resource1>();
|
||||||
};
|
|
||||||
|
handleImport = new ImportMemoryWin32HandleInfoKHR
|
||||||
|
{
|
||||||
|
PNext = &dedicatedAllocation,
|
||||||
|
SType = StructureType.ImportMemoryWin32HandleInfoKhr,
|
||||||
|
HandleType = ExternalMemoryHandleTypeFlags.D3D11TextureBit,
|
||||||
|
Handle = dxgi.CreateSharedHandle(null, SharedResourceFlags.Read | SharedResourceFlags.Write),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var memoryAllocateInfo = new MemoryAllocateInfo
|
var memoryAllocateInfo = new MemoryAllocateInfo
|
||||||
{
|
{
|
||||||
PNext =
|
PNext =
|
||||||
exportable ? RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? &handleImport : &fdExport : null,
|
exportable ? handleImport.Handle != IntPtr.Zero ? &handleImport : &fdExport : null,
|
||||||
SType = StructureType.MemoryAllocateInfo,
|
SType = StructureType.MemoryAllocateInfo,
|
||||||
AllocationSize = memoryRequirements.Size,
|
AllocationSize = memoryRequirements.Size,
|
||||||
MemoryTypeIndex = (uint)VulkanMemoryHelper.FindSuitableMemoryTypeIndex(
|
MemoryTypeIndex = (uint)VulkanMemoryHelper.FindSuitableMemoryTypeIndex(
|
||||||
@@ -187,14 +195,34 @@ public unsafe class VulkanImage : IDisposable
|
|||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IntPtr ExportOpaqueNtHandle()
|
||||||
|
{
|
||||||
|
if (!Api.TryGetDeviceExtension<KhrExternalMemoryWin32>(_instance, _device, out var ext))
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
var info = new MemoryGetWin32HandleInfoKHR()
|
||||||
|
{
|
||||||
|
Memory = _imageMemory,
|
||||||
|
SType = StructureType.MemoryGetWin32HandleInfoKhr,
|
||||||
|
HandleType = ExternalMemoryHandleTypeFlags.OpaqueWin32Bit
|
||||||
|
};
|
||||||
|
ext.GetMemoryWin32Handle(_device, info, out var fd).ThrowOnError();
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
public IPlatformHandle Export()
|
public IPlatformHandle Export()
|
||||||
{
|
{
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
using var dxgi = _d3dTexture2D!.QueryInterface<Resource1>();
|
if (_d3dTexture2D != null)
|
||||||
return new PlatformHandle(
|
{
|
||||||
dxgi.CreateSharedHandle(null, SharedResourceFlags.Read | SharedResourceFlags.Write),
|
using var dxgi = _d3dTexture2D!.QueryInterface<Resource1>();
|
||||||
KnownPlatformGraphicsExternalImageHandleTypes.D3D11TextureNtHandle);
|
return new PlatformHandle(
|
||||||
|
dxgi.CreateSharedHandle(null, SharedResourceFlags.Read | SharedResourceFlags.Write),
|
||||||
|
KnownPlatformGraphicsExternalImageHandleTypes.D3D11TextureNtHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PlatformHandle(ExportOpaqueNtHandle(),
|
||||||
|
KnownPlatformGraphicsExternalImageHandleTypes.VulkanOpaqueNtHandle);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return new PlatformHandle(new IntPtr(ExportFd()),
|
return new PlatformHandle(new IntPtr(ExportFd()),
|
||||||
@@ -203,7 +231,7 @@ public unsafe class VulkanImage : IDisposable
|
|||||||
|
|
||||||
public ImageTiling Tiling => ImageTiling.Optimal;
|
public ImageTiling Tiling => ImageTiling.Optimal;
|
||||||
|
|
||||||
|
public bool IsDirectXBacked => _d3dTexture2D != null;
|
||||||
|
|
||||||
internal void TransitionLayout(CommandBuffer commandBuffer,
|
internal void TransitionLayout(CommandBuffer commandBuffer,
|
||||||
ImageLayout fromLayout, AccessFlags fromAccessFlags,
|
ImageLayout fromLayout, AccessFlags fromAccessFlags,
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Avalonia.Platform;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using Silk.NET.Vulkan.Extensions.KHR;
|
using Silk.NET.Vulkan.Extensions.KHR;
|
||||||
using SilkNetDemo;
|
using SilkNetDemo;
|
||||||
@@ -16,7 +18,9 @@ class VulkanSemaphorePair : IDisposable
|
|||||||
var semaphoreExportInfo = new ExportSemaphoreCreateInfo
|
var semaphoreExportInfo = new ExportSemaphoreCreateInfo
|
||||||
{
|
{
|
||||||
SType = StructureType.ExportSemaphoreCreateInfo,
|
SType = StructureType.ExportSemaphoreCreateInfo,
|
||||||
HandleTypes = ExternalSemaphoreHandleTypeFlags.OpaqueFDBit
|
HandleTypes = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
|
||||||
|
ExternalSemaphoreHandleTypeFlags.OpaqueWin32Bit :
|
||||||
|
ExternalSemaphoreHandleTypeFlags.OpaqueFDBit
|
||||||
};
|
};
|
||||||
|
|
||||||
var semaphoreCreateInfo = new SemaphoreCreateInfo
|
var semaphoreCreateInfo = new SemaphoreCreateInfo
|
||||||
@@ -46,6 +50,30 @@ class VulkanSemaphorePair : IDisposable
|
|||||||
ext.GetSemaphoreF(_resources.Device, info, out var fd).ThrowOnError();
|
ext.GetSemaphoreF(_resources.Device, info, out var fd).ThrowOnError();
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IntPtr ExportWin32(bool renderFinished)
|
||||||
|
{
|
||||||
|
if (!_resources.Api.TryGetDeviceExtension<KhrExternalSemaphoreWin32>(_resources.Instance, _resources.Device,
|
||||||
|
out var ext))
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
var info = new SemaphoreGetWin32HandleInfoKHR()
|
||||||
|
{
|
||||||
|
SType = StructureType.SemaphoreGetWin32HandleInfoKhr,
|
||||||
|
Semaphore = renderFinished ? RenderFinishedSemaphore : ImageAvailableSemaphore,
|
||||||
|
HandleType = ExternalSemaphoreHandleTypeFlags.OpaqueWin32Bit
|
||||||
|
};
|
||||||
|
ext.GetSemaphoreWin32Handle(_resources.Device, info, out var fd).ThrowOnError();
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPlatformHandle Export(bool renderFinished)
|
||||||
|
{
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
return new PlatformHandle(ExportWin32(renderFinished),
|
||||||
|
KnownPlatformGraphicsExternalSemaphoreHandleTypes.VulkanOpaqueNtHandle);
|
||||||
|
return new PlatformHandle(new IntPtr(ExportFd(renderFinished)),
|
||||||
|
KnownPlatformGraphicsExternalSemaphoreHandleTypes.VulkanOpaquePosixFileDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
internal Semaphore ImageAvailableSemaphore { get; }
|
internal Semaphore ImageAvailableSemaphore { get; }
|
||||||
internal Semaphore RenderFinishedSemaphore { get; }
|
internal Semaphore RenderFinishedSemaphore { get; }
|
||||||
@@ -55,4 +83,4 @@ class VulkanSemaphorePair : IDisposable
|
|||||||
_resources.Api.DestroySemaphore(_resources.Device, ImageAvailableSemaphore, null);
|
_resources.Api.DestroySemaphore(_resources.Device, ImageAvailableSemaphore, null);
|
||||||
_resources.Api.DestroySemaphore(_resources.Device, RenderFinishedSemaphore, null);
|
_resources.Api.DestroySemaphore(_resources.Device, RenderFinishedSemaphore, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class VulkanSwapchainImage : ISwapchainImage
|
|||||||
_interop = interop;
|
_interop = interop;
|
||||||
_target = target;
|
_target = target;
|
||||||
Size = size;
|
Size = size;
|
||||||
_image = new VulkanImage(vk, (uint)Format.R8G8B8A8Unorm, size, true);
|
_image = new VulkanImage(vk, (uint)Format.R8G8B8A8Unorm, size, true, interop.SupportedImageHandleTypes);
|
||||||
_semaphorePair = new VulkanSemaphorePair(vk, true);
|
_semaphorePair = new VulkanSemaphorePair(vk, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ class VulkanSwapchainImage : ISwapchainImage
|
|||||||
ImageLayout.Undefined, AccessFlags.None,
|
ImageLayout.Undefined, AccessFlags.None,
|
||||||
ImageLayout.ColorAttachmentOptimal, AccessFlags.ColorAttachmentReadBit);
|
ImageLayout.ColorAttachmentOptimal, AccessFlags.ColorAttachmentReadBit);
|
||||||
|
|
||||||
if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if(_image.IsDirectXBacked)
|
||||||
buffer.Submit(null,null,null, null, new VulkanCommandBufferPool.VulkanCommandBuffer.KeyedMutexSubmitInfo
|
buffer.Submit(null,null,null, null, new VulkanCommandBufferPool.VulkanCommandBuffer.KeyedMutexSubmitInfo
|
||||||
{
|
{
|
||||||
AcquireKey = 0,
|
AcquireKey = 0,
|
||||||
@@ -111,8 +111,7 @@ class VulkanSwapchainImage : ISwapchainImage
|
|||||||
_image.TransitionLayout(buffer.InternalHandle, ImageLayout.TransferSrcOptimal, AccessFlags.TransferWriteBit);
|
_image.TransitionLayout(buffer.InternalHandle, ImageLayout.TransferSrcOptimal, AccessFlags.TransferWriteBit);
|
||||||
|
|
||||||
|
|
||||||
|
if (_image.IsDirectXBacked)
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
{
|
{
|
||||||
buffer.Submit(null, null, null, null,
|
buffer.Submit(null, null, null, null,
|
||||||
new VulkanCommandBufferPool.VulkanCommandBuffer.KeyedMutexSubmitInfo
|
new VulkanCommandBufferPool.VulkanCommandBuffer.KeyedMutexSubmitInfo
|
||||||
@@ -123,15 +122,11 @@ class VulkanSwapchainImage : ISwapchainImage
|
|||||||
else
|
else
|
||||||
buffer.Submit(null, null, new[] { _semaphorePair.RenderFinishedSemaphore });
|
buffer.Submit(null, null, new[] { _semaphorePair.RenderFinishedSemaphore });
|
||||||
|
|
||||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (!_image.IsDirectXBacked)
|
||||||
{
|
{
|
||||||
_availableSemaphore ??= _interop.ImportSemaphore(new PlatformHandle(
|
_availableSemaphore ??= _interop.ImportSemaphore(_semaphorePair.Export(false));
|
||||||
new IntPtr(_semaphorePair.ExportFd(false)),
|
|
||||||
KnownPlatformGraphicsExternalSemaphoreHandleTypes.VulkanOpaquePosixFileDescriptor));
|
|
||||||
|
|
||||||
_renderCompletedSemaphore ??= _interop.ImportSemaphore(new PlatformHandle(
|
_renderCompletedSemaphore ??= _interop.ImportSemaphore(_semaphorePair.Export(true));
|
||||||
new IntPtr(_semaphorePair.ExportFd(true)),
|
|
||||||
KnownPlatformGraphicsExternalSemaphoreHandleTypes.VulkanOpaquePosixFileDescriptor));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_importedImage ??= _interop.ImportImage(_image.Export(),
|
_importedImage ??= _interop.ImportImage(_image.Export(),
|
||||||
@@ -143,7 +138,7 @@ class VulkanSwapchainImage : ISwapchainImage
|
|||||||
MemorySize = _image.MemorySize
|
MemorySize = _image.MemorySize
|
||||||
});
|
});
|
||||||
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (_image.IsDirectXBacked)
|
||||||
_lastPresent = _target.UpdateWithKeyedMutexAsync(_importedImage, 1, 0);
|
_lastPresent = _target.UpdateWithKeyedMutexAsync(_importedImage, 1, 0);
|
||||||
else
|
else
|
||||||
_lastPresent = _target.UpdateWithSemaphoresAsync(_importedImage, _renderCompletedSemaphore!, _availableSemaphore!);
|
_lastPresent = _target.UpdateWithSemaphoresAsync(_importedImage, _renderCompletedSemaphore!, _availableSemaphore!);
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
<InternalsVisibleTo Include="Avalonia.Markup.Xaml, PublicKey=$(AvaloniaPublicKey)" />
|
<InternalsVisibleTo Include="Avalonia.Markup.Xaml, PublicKey=$(AvaloniaPublicKey)" />
|
||||||
<InternalsVisibleTo Include="Avalonia.Markup.Xaml.Loader, PublicKey=$(AvaloniaPublicKey)" />
|
<InternalsVisibleTo Include="Avalonia.Markup.Xaml.Loader, PublicKey=$(AvaloniaPublicKey)" />
|
||||||
<InternalsVisibleTo Include="Avalonia.OpenGL, PublicKey=$(AvaloniaPublicKey)" />
|
<InternalsVisibleTo Include="Avalonia.OpenGL, PublicKey=$(AvaloniaPublicKey)" />
|
||||||
|
<InternalsVisibleTo Include="Avalonia.Vulkan, PublicKey=$(AvaloniaPublicKey)" />
|
||||||
<InternalsVisibleTo Include="Avalonia.Skia, PublicKey=$(AvaloniaPublicKey)" />
|
<InternalsVisibleTo Include="Avalonia.Skia, PublicKey=$(AvaloniaPublicKey)" />
|
||||||
<InternalsVisibleTo Include="Avalonia.Controls.ColorPicker, PublicKey=$(AvaloniaPublicKey)" />
|
<InternalsVisibleTo Include="Avalonia.Controls.ColorPicker, PublicKey=$(AvaloniaPublicKey)" />
|
||||||
<InternalsVisibleTo Include="Avalonia.Controls.DataGrid, PublicKey=$(AvaloniaPublicKey)" />
|
<InternalsVisibleTo Include="Avalonia.Controls.DataGrid, PublicKey=$(AvaloniaPublicKey)" />
|
||||||
|
|||||||
@@ -56,5 +56,8 @@ namespace Avalonia.Platform.Interop
|
|||||||
ArrayPool<byte>.Shared.Return(bytes);
|
ArrayPool<byte>.Shared.Return(bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static implicit operator IntPtr(Utf8Buffer b) => b.handle;
|
||||||
|
public static unsafe implicit operator byte*(Utf8Buffer b) => (byte*)b.handle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,14 @@ public static class KnownPlatformGraphicsExternalImageHandleTypes
|
|||||||
/// A POSIX file descriptor that's exported by Vulkan using VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT or in a compatible way
|
/// A POSIX file descriptor that's exported by Vulkan using VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT or in a compatible way
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string VulkanOpaquePosixFileDescriptor = nameof(VulkanOpaquePosixFileDescriptor);
|
public const string VulkanOpaquePosixFileDescriptor = nameof(VulkanOpaquePosixFileDescriptor);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A NT handle that's been exported by Vulkan using VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT or in a compatible way
|
||||||
|
/// </summary>
|
||||||
|
public const string VulkanOpaqueNtHandle = nameof(VulkanOpaqueNtHandle);
|
||||||
|
|
||||||
|
// A global shared handle that's been exported by Vulkan using VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT or in a compatible way
|
||||||
|
public const string VulkanOpaqueKmtHandle = nameof(VulkanOpaqueKmtHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -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
Load Diff
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
@@ -15,8 +15,10 @@ using Avalonia.Platform;
|
|||||||
using Avalonia.Rendering;
|
using Avalonia.Rendering;
|
||||||
using Avalonia.Rendering.Composition;
|
using Avalonia.Rendering.Composition;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using Avalonia.Vulkan;
|
||||||
using Avalonia.X11;
|
using Avalonia.X11;
|
||||||
using Avalonia.X11.Glx;
|
using Avalonia.X11.Glx;
|
||||||
|
using Avalonia.X11.Vulkan;
|
||||||
using Avalonia.X11.Screens;
|
using Avalonia.X11.Screens;
|
||||||
using static Avalonia.X11.XLib;
|
using static Avalonia.X11.XLib;
|
||||||
|
|
||||||
@@ -214,6 +216,14 @@ namespace Avalonia.X11
|
|||||||
return egl;
|
return egl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (renderingMode == X11RenderingMode.Vulkan)
|
||||||
|
{
|
||||||
|
var vulkan = VulkanSupport.TryInitialize(info,
|
||||||
|
AvaloniaLocator.Current.GetService<VulkanOptions>() ?? new());
|
||||||
|
if (vulkan != null)
|
||||||
|
return vulkan;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException($"{nameof(X11PlatformOptions)}.{nameof(X11PlatformOptions.RenderingMode)} has a value of \"{string.Join(", ", opts.RenderingMode)}\", but no options were applied.");
|
throw new InvalidOperationException($"{nameof(X11PlatformOptions)}.{nameof(X11PlatformOptions.RenderingMode)} has a value of \"{string.Join(", ", opts.RenderingMode)}\", but no options were applied.");
|
||||||
@@ -241,7 +251,12 @@ namespace Avalonia
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enables native Linux EGL rendering.
|
/// Enables native Linux EGL rendering.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Egl = 3
|
Egl = 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables Vulkan rendering
|
||||||
|
/// </summary>
|
||||||
|
Vulkan = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ using Avalonia.Media;
|
|||||||
using Avalonia.OpenGL;
|
using Avalonia.OpenGL;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
using Avalonia.Media.Imaging;
|
using Avalonia.Media.Imaging;
|
||||||
|
using Avalonia.Skia.Vulkan;
|
||||||
|
using Avalonia.Vulkan;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
using Avalonia.Media.TextFormatting;
|
using Avalonia.Media.TextFormatting;
|
||||||
using Avalonia.Metal;
|
using Avalonia.Metal;
|
||||||
@@ -36,6 +38,8 @@ namespace Avalonia.Skia
|
|||||||
return new SkiaContext(new GlSkiaGpu(gl, _maxResourceBytes));
|
return new SkiaContext(new GlSkiaGpu(gl, _maxResourceBytes));
|
||||||
if (graphicsContext is IMetalDevice metal)
|
if (graphicsContext is IMetalDevice metal)
|
||||||
return new SkiaContext(new SkiaMetalGpu(metal, _maxResourceBytes));
|
return new SkiaContext(new SkiaMetalGpu(metal, _maxResourceBytes));
|
||||||
|
if (graphicsContext is IVulkanPlatformGraphicsContext vulkanContext)
|
||||||
|
return new SkiaContext(new VulkanSkiaGpu(vulkanContext, _maxResourceBytes));
|
||||||
throw new ArgumentException("Graphics context of type is not supported");
|
throw new ArgumentException("Graphics context of type is not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -3,10 +3,12 @@ using System.Diagnostics.Tracing;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Avalonia.OpenGL;
|
using Avalonia.OpenGL;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
|
using Avalonia.Vulkan;
|
||||||
using Avalonia.Win32.DComposition;
|
using Avalonia.Win32.DComposition;
|
||||||
using Avalonia.Win32.DirectX;
|
using Avalonia.Win32.DirectX;
|
||||||
using Avalonia.Win32.OpenGl;
|
using Avalonia.Win32.OpenGl;
|
||||||
using Avalonia.Win32.OpenGl.Angle;
|
using Avalonia.Win32.OpenGl.Angle;
|
||||||
|
using Avalonia.Win32.Vulkan;
|
||||||
using Avalonia.Win32.WinRT.Composition;
|
using Avalonia.Win32.WinRT.Composition;
|
||||||
|
|
||||||
namespace Avalonia.Win32;
|
namespace Avalonia.Win32;
|
||||||
@@ -62,6 +64,13 @@ static class Win32GlManager
|
|||||||
return wgl;
|
return wgl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (renderingMode == Win32RenderingMode.Vulkan)
|
||||||
|
{
|
||||||
|
var vulkan = VulkanSupport.TryInitialize(AvaloniaLocator.Current.GetService<VulkanOptions>() ?? new());
|
||||||
|
if (vulkan != null)
|
||||||
|
return vulkan;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException($"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.RenderingMode)} has a value of \"{string.Join(", ", opts.RenderingMode)}\", but no options were applied.");
|
throw new InvalidOperationException($"{nameof(Win32PlatformOptions)}.{nameof(Win32PlatformOptions.RenderingMode)} has a value of \"{string.Join(", ", opts.RenderingMode)}\", but no options were applied.");
|
||||||
|
|||||||
@@ -22,7 +22,11 @@ public enum Win32RenderingMode
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Avalonia would try to use native Widows OpenGL with GPU rendering.
|
/// Avalonia would try to use native Widows OpenGL with GPU rendering.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Wgl = 3
|
Wgl = 3,
|
||||||
|
/// <summary>
|
||||||
|
/// Avalonia would try to use native Widows Vulkan with GPU rendering.
|
||||||
|
/// </summary>
|
||||||
|
Vulkan = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|||||||
Reference in New Issue
Block a user