mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-16 00:40:59 +00:00
[vulkan] enable required PhysicalDeviceFeatures for translated shaders (#29)
- Enable VertexPipelineStoresAndAtomics/FragmentStoresAndAtomics: fixes vkCreateGraphicsPipelines() rejecting guestBuffers storage descriptor as NonWritable in vertex/fragment stages. - Enable ShaderInt64: fixes vkCreateShaderModule() rejecting SPIR-V using 64-bit integer capability. - Query GetPhysicalDeviceFeatures first and only enable what the GPU actually reports as supported, with a warning fallback otherwise. Also adds optional Vulkan Validation Layers (SHARPEMU_VK_VALIDATION=1) to surface these VUID errors during development instead of silent VK_ERROR_DEVICE_LOST.
This commit is contained in:
@@ -2,7 +2,6 @@
|
|||||||
Copyright (C) 2026 SharpEmu Emulator Project
|
Copyright (C) 2026 SharpEmu Emulator Project
|
||||||
SPDX-License-Identifier: GPL-2.0-or-later
|
SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
@@ -10,7 +9,8 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Iced" Version="1.21.0" />
|
<PackageVersion Include="Iced" Version="1.21.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.23.0" />
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.23.0" />
|
||||||
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.23.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.23.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.23.0" />
|
||||||
<PackageVersion Include="Silk.NET.Windowing" Version="2.23.0" />
|
<PackageVersion Include="Silk.NET.Windowing" Version="2.23.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -81,6 +81,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"SharpEmu.HLE": "[1.0.0, )",
|
"SharpEmu.HLE": "[1.0.0, )",
|
||||||
"Silk.NET.Vulkan": "[2.23.0, )",
|
"Silk.NET.Vulkan": "[2.23.0, )",
|
||||||
|
"Silk.NET.Vulkan.Extensions.EXT": "[2.23.0, )",
|
||||||
"Silk.NET.Vulkan.Extensions.KHR": "[2.23.0, )",
|
"Silk.NET.Vulkan.Extensions.KHR": "[2.23.0, )",
|
||||||
"Silk.NET.Windowing": "[2.23.0, )"
|
"Silk.NET.Windowing": "[2.23.0, )"
|
||||||
}
|
}
|
||||||
@@ -103,6 +104,16 @@
|
|||||||
"Silk.NET.Core": "2.23.0"
|
"Silk.NET.Core": "2.23.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Silk.NET.Vulkan.Extensions.EXT": {
|
||||||
|
"type": "CentralTransitive",
|
||||||
|
"requested": "[2.23.0, )",
|
||||||
|
"resolved": "2.23.0",
|
||||||
|
"contentHash": "+Oth189ksRiL6HvGCwIdnsYHawqrbO8y49u1H61z3wsfcHhQZeVDYe/wF5LD7fk3NcdgDvwFD3mLm1QWhdZySw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Silk.NET.Core": "2.23.0",
|
||||||
|
"Silk.NET.Vulkan": "2.23.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Silk.NET.Vulkan.Extensions.KHR": {
|
"Silk.NET.Vulkan.Extensions.KHR": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.23.0, )",
|
"requested": "[2.23.0, )",
|
||||||
|
|||||||
@@ -72,6 +72,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"SharpEmu.HLE": "[1.0.0, )",
|
"SharpEmu.HLE": "[1.0.0, )",
|
||||||
"Silk.NET.Vulkan": "[2.23.0, )",
|
"Silk.NET.Vulkan": "[2.23.0, )",
|
||||||
|
"Silk.NET.Vulkan.Extensions.EXT": "[2.23.0, )",
|
||||||
"Silk.NET.Vulkan.Extensions.KHR": "[2.23.0, )",
|
"Silk.NET.Vulkan.Extensions.KHR": "[2.23.0, )",
|
||||||
"Silk.NET.Windowing": "[2.23.0, )"
|
"Silk.NET.Windowing": "[2.23.0, )"
|
||||||
}
|
}
|
||||||
@@ -88,6 +89,16 @@
|
|||||||
"Silk.NET.Core": "2.23.0"
|
"Silk.NET.Core": "2.23.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Silk.NET.Vulkan.Extensions.EXT": {
|
||||||
|
"type": "CentralTransitive",
|
||||||
|
"requested": "[2.23.0, )",
|
||||||
|
"resolved": "2.23.0",
|
||||||
|
"contentHash": "+Oth189ksRiL6HvGCwIdnsYHawqrbO8y49u1H61z3wsfcHhQZeVDYe/wF5LD7fk3NcdgDvwFD3mLm1QWhdZySw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Silk.NET.Core": "2.23.0",
|
||||||
|
"Silk.NET.Vulkan": "2.23.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Silk.NET.Vulkan.Extensions.KHR": {
|
"Silk.NET.Vulkan.Extensions.KHR": {
|
||||||
"type": "CentralTransitive",
|
"type": "CentralTransitive",
|
||||||
"requested": "[2.23.0, )",
|
"requested": "[2.23.0, )",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Silk.NET.Vulkan" />
|
<PackageReference Include="Silk.NET.Vulkan" />
|
||||||
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
|
||||||
<PackageReference Include="Silk.NET.Windowing" />
|
<PackageReference Include="Silk.NET.Windowing" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Silk.NET.Maths;
|
|||||||
using SharpEmu.Libs.Agc;
|
using SharpEmu.Libs.Agc;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using Silk.NET.Vulkan.Extensions.KHR;
|
using Silk.NET.Vulkan.Extensions.KHR;
|
||||||
|
using Silk.NET.Vulkan.Extensions.EXT;
|
||||||
using Silk.NET.Windowing;
|
using Silk.NET.Windowing;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
@@ -846,6 +847,8 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
private delegate* unmanaged<CommandBuffer, void> _cmdEndDebugUtilsLabel;
|
private delegate* unmanaged<CommandBuffer, void> _cmdEndDebugUtilsLabel;
|
||||||
private Instance _instance;
|
private Instance _instance;
|
||||||
private SurfaceKHR _surface;
|
private SurfaceKHR _surface;
|
||||||
|
private DebugUtilsMessengerEXT _debugMessenger;
|
||||||
|
private ExtDebugUtils? _debugUtils;
|
||||||
private PhysicalDevice _physicalDevice;
|
private PhysicalDevice _physicalDevice;
|
||||||
private Device _device;
|
private Device _device;
|
||||||
private Queue _queue;
|
private Queue _queue;
|
||||||
@@ -1241,6 +1244,9 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
private void CreateInstance()
|
private void CreateInstance()
|
||||||
{
|
{
|
||||||
var applicationName = (byte*)SilkMarshal.StringToPtr("SharpEmu");
|
var applicationName = (byte*)SilkMarshal.StringToPtr("SharpEmu");
|
||||||
|
var enableValidation = Environment.GetEnvironmentVariable("SHARPEMU_VK_VALIDATION") == "1";
|
||||||
|
byte* validationLayerName = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var applicationInfo = new ApplicationInfo
|
var applicationInfo = new ApplicationInfo
|
||||||
@@ -1268,12 +1274,29 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
enabledExtensions[enabledExtensionCount++] = debugUtilsExtension;
|
enabledExtensions[enabledExtensionCount++] = debugUtilsExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enableValidation && IsInstanceLayerAvailable("VK_LAYER_KHRONOS_validation"))
|
||||||
|
{
|
||||||
|
validationLayerName = (byte*)SilkMarshal.StringToPtr("VK_LAYER_KHRONOS_validation");
|
||||||
|
}
|
||||||
|
else if (enableValidation)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("[LOADER][WARN] SHARPEMU_VK_VALIDATION=1 but VK_LAYER_KHRONOS_validation not found (Vulkan SDK installed?).");
|
||||||
|
}
|
||||||
|
|
||||||
|
var layers = stackalloc byte*[1];
|
||||||
|
if (validationLayerName is not null)
|
||||||
|
{
|
||||||
|
layers[0] = validationLayerName;
|
||||||
|
}
|
||||||
|
|
||||||
var createInfo = new InstanceCreateInfo
|
var createInfo = new InstanceCreateInfo
|
||||||
{
|
{
|
||||||
SType = StructureType.InstanceCreateInfo,
|
SType = StructureType.InstanceCreateInfo,
|
||||||
PApplicationInfo = &applicationInfo,
|
PApplicationInfo = &applicationInfo,
|
||||||
EnabledExtensionCount = (uint)enabledExtensionCount,
|
EnabledExtensionCount = (uint)enabledExtensionCount,
|
||||||
PpEnabledExtensionNames = enabledExtensions,
|
PpEnabledExtensionNames = enabledExtensions,
|
||||||
|
EnabledLayerCount = validationLayerName is not null ? 1u : 0u,
|
||||||
|
PpEnabledLayerNames = validationLayerName is not null ? layers : null,
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -1283,6 +1306,13 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
{
|
{
|
||||||
throw new InvalidOperationException("VK_KHR_surface is unavailable.");
|
throw new InvalidOperationException("VK_KHR_surface is unavailable.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (validationLayerName is not null && _vk.TryGetInstanceExtension(_instance, out ExtDebugUtils debugUtils))
|
||||||
|
{
|
||||||
|
_debugUtils = debugUtils;
|
||||||
|
RegisterDebugMessenger(debugUtils);
|
||||||
|
Console.Error.WriteLine("[LOADER][INFO] Vulkan Validation Layers active (SHARPEMU_VK_VALIDATION=1).");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -1295,9 +1325,74 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
SilkMarshal.Free((nint)applicationName);
|
SilkMarshal.Free((nint)applicationName);
|
||||||
|
if (validationLayerName is not null)
|
||||||
|
{
|
||||||
|
SilkMarshal.Free((nint)validationLayerName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsInstanceLayerAvailable(string layerName)
|
||||||
|
{
|
||||||
|
uint layerCount = 0;
|
||||||
|
if (_vk.EnumerateInstanceLayerProperties(&layerCount, null) != Result.Success || layerCount == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var properties = new LayerProperties[layerCount];
|
||||||
|
fixed (LayerProperties* propertyPointer = properties)
|
||||||
|
{
|
||||||
|
if (_vk.EnumerateInstanceLayerProperties(&layerCount, propertyPointer) != Result.Success)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var expected = Encoding.UTF8.GetBytes(layerName);
|
||||||
|
for (var index = 0; index < layerCount; index++)
|
||||||
|
{
|
||||||
|
if (Utf8NullTerminatedEquals(propertyPointer[index].LayerName, expected))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
private void RegisterDebugMessenger(ExtDebugUtils debugUtils)
|
||||||
|
{
|
||||||
|
var messengerInfo = new DebugUtilsMessengerCreateInfoEXT
|
||||||
|
{
|
||||||
|
SType = StructureType.DebugUtilsMessengerCreateInfoExt,
|
||||||
|
MessageSeverity = DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt
|
||||||
|
| DebugUtilsMessageSeverityFlagsEXT.WarningBitExt,
|
||||||
|
MessageType = DebugUtilsMessageTypeFlagsEXT.ValidationBitExt
|
||||||
|
| DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt
|
||||||
|
| DebugUtilsMessageTypeFlagsEXT.GeneralBitExt,
|
||||||
|
PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(DebugCallback),
|
||||||
|
};
|
||||||
|
|
||||||
|
Check(debugUtils.CreateDebugUtilsMessenger(_instance, &messengerInfo, null, out _debugMessenger),
|
||||||
|
"vkCreateDebugUtilsMessengerEXT");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe uint DebugCallback(
|
||||||
|
DebugUtilsMessageSeverityFlagsEXT severity,
|
||||||
|
DebugUtilsMessageTypeFlagsEXT type,
|
||||||
|
DebugUtilsMessengerCallbackDataEXT* callbackData,
|
||||||
|
void* userData)
|
||||||
|
{
|
||||||
|
var message = SilkMarshal.PtrToString((nint)callbackData->PMessage);
|
||||||
|
var prefix = severity switch
|
||||||
|
{
|
||||||
|
DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt => "[VULKAN][ERROR]",
|
||||||
|
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt => "[VULKAN][WARN]",
|
||||||
|
_ => "[VULKAN][INFO]",
|
||||||
|
};
|
||||||
|
Console.Error.WriteLine($"{prefix} {message}");
|
||||||
|
return Vk.False;
|
||||||
|
}
|
||||||
private void CreateSurface()
|
private void CreateSurface()
|
||||||
{
|
{
|
||||||
var instanceHandle = new VkHandle(_instance.Handle);
|
var instanceHandle = new VkHandle(_instance.Handle);
|
||||||
@@ -1358,7 +1453,28 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
QueueCount = 1,
|
QueueCount = 1,
|
||||||
PQueuePriorities = &priority,
|
PQueuePriorities = &priority,
|
||||||
};
|
};
|
||||||
|
_vk.GetPhysicalDeviceFeatures(_physicalDevice, out var supportedFeatures);
|
||||||
|
var enabledFeatures = new PhysicalDeviceFeatures
|
||||||
|
{
|
||||||
|
VertexPipelineStoresAndAtomics = supportedFeatures.VertexPipelineStoresAndAtomics,
|
||||||
|
FragmentStoresAndAtomics = supportedFeatures.FragmentStoresAndAtomics,
|
||||||
|
ShaderInt64 = supportedFeatures.ShaderInt64,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!supportedFeatures.ShaderInt64)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
"[LOADER][WARN] GPU does not support shaderInt64 " +
|
||||||
|
"translated shaders using 64-bit integers will fail.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!supportedFeatures.VertexPipelineStoresAndAtomics || !supportedFeatures.FragmentStoresAndAtomics)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
"[LOADER][WARN] GPU does not support vertexPipelineStoresAndAtomics/fragmentStoresAndAtomics " +
|
||||||
|
"translated shaders using storage buffers in vertex/fragment stages may fail.");
|
||||||
|
}
|
||||||
|
|
||||||
var swapchainExtension = (byte*)SilkMarshal.StringToPtr("VK_KHR_swapchain");
|
var swapchainExtension = (byte*)SilkMarshal.StringToPtr("VK_KHR_swapchain");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -1369,6 +1485,7 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
PQueueCreateInfos = &queueInfo,
|
PQueueCreateInfos = &queueInfo,
|
||||||
EnabledExtensionCount = 1,
|
EnabledExtensionCount = 1,
|
||||||
PpEnabledExtensionNames = &swapchainExtension,
|
PpEnabledExtensionNames = &swapchainExtension,
|
||||||
|
PEnabledFeatures = &enabledFeatures,
|
||||||
};
|
};
|
||||||
|
|
||||||
Check(_vk.CreateDevice(_physicalDevice, &createInfo, null, out _device), "vkCreateDevice");
|
Check(_vk.CreateDevice(_physicalDevice, &createInfo, null, out _device), "vkCreateDevice");
|
||||||
@@ -5956,6 +6073,10 @@ internal static unsafe class VulkanVideoPresenter
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_debugUtils is not null && _debugMessenger.Handle != 0)
|
||||||
|
{
|
||||||
|
_debugUtils.DestroyDebugUtilsMessenger(_instance, _debugMessenger, null);
|
||||||
|
}
|
||||||
_vulkanReady = false;
|
_vulkanReady = false;
|
||||||
_vk.DeviceWaitIdle(_device);
|
_vk.DeviceWaitIdle(_device);
|
||||||
CollectCompletedGuestSubmissions(waitForOldest: false);
|
CollectCompletedGuestSubmissions(waitForOldest: false);
|
||||||
|
|||||||
@@ -11,6 +11,16 @@
|
|||||||
"Silk.NET.Core": "2.23.0"
|
"Silk.NET.Core": "2.23.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Silk.NET.Vulkan.Extensions.EXT": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[2.23.0, )",
|
||||||
|
"resolved": "2.23.0",
|
||||||
|
"contentHash": "+Oth189ksRiL6HvGCwIdnsYHawqrbO8y49u1H61z3wsfcHhQZeVDYe/wF5LD7fk3NcdgDvwFD3mLm1QWhdZySw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Silk.NET.Core": "2.23.0",
|
||||||
|
"Silk.NET.Vulkan": "2.23.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Silk.NET.Vulkan.Extensions.KHR": {
|
"Silk.NET.Vulkan.Extensions.KHR": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[2.23.0, )",
|
"requested": "[2.23.0, )",
|
||||||
|
|||||||
Reference in New Issue
Block a user