diff --git a/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs b/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs index b623dca6..52f473d0 100644 --- a/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs +++ b/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs @@ -4041,6 +4041,10 @@ internal static unsafe class VulkanVideoPresenter "vkCreateDebugUtilsMessengerEXT"); } + + [ThreadStatic] + private static string? _pendingShaderModuleDumpPath; + private static unsafe uint DebugCallback( DebugUtilsMessageSeverityFlagsEXT severity, DebugUtilsMessageTypeFlagsEXT type, @@ -4055,6 +4059,24 @@ internal static unsafe class VulkanVideoPresenter _ => "[VULKAN][INFO]", }; Console.Error.WriteLine($"{prefix} {message}"); + + + if (severity == DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt && + message is not null && + message.Contains("vkCreateShaderModule", StringComparison.Ordinal)) + { + var dumpPath = _pendingShaderModuleDumpPath; + var dumpHint = dumpPath is null + ? "Set SHARPEMU_SHADER_SPIRV_DUMP_DIR to a directory to " + + "capture every module's .spv bytes for offline " + + "spirv-dis/spirv-val analysis." + : $"Dumped module for this failure: {dumpPath}"; + + Console.Error.WriteLine( + "[SHARPEMU][ERROR] A guest shader compiled to invalid SPIR-V." + + "The shader module was created without an API-level error. {dumpHint}"); + } + return Vk.False; } private void CreateSurface() @@ -6844,20 +6866,43 @@ internal static unsafe class VulkanVideoPresenter } } + private static int _shaderModuleDumpSequence; + private ShaderModule CreateShaderModule(byte[] code) { - fixed (byte* codePointer = code) + string? dumpPath = null; + var dumpDirectory = Environment.GetEnvironmentVariable("SHARPEMU_SHADER_SPIRV_DUMP_DIR"); + if (!string.IsNullOrWhiteSpace(dumpDirectory)) { - var createInfo = new ShaderModuleCreateInfo + Directory.CreateDirectory(dumpDirectory); + + var sequence = Interlocked.Increment(ref _shaderModuleDumpSequence); + dumpPath = Path.Combine(dumpDirectory, $"{sequence:D4}.spv"); + File.WriteAllBytes(dumpPath, code); + + _pendingShaderModuleDumpPath = dumpPath; + } + + + try + { + fixed (byte* codePointer = code) { - SType = StructureType.ShaderModuleCreateInfo, - CodeSize = (nuint)code.Length, - PCode = (uint*)codePointer, - }; - Check( - _vk.CreateShaderModule(_device, &createInfo, null, out var module), - "vkCreateShaderModule"); - return module; + var createInfo = new ShaderModuleCreateInfo + { + SType = StructureType.ShaderModuleCreateInfo, + CodeSize = (nuint)code.Length, + PCode = (uint*)codePointer, + }; + Check( + _vk.CreateShaderModule(_device, &createInfo, null, out var module), + "vkCreateShaderModule"); + return module; + } + } + finally + { + _pendingShaderModuleDumpPath = null; } } diff --git a/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.cs b/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.cs index d7a12dae..2daeac52 100644 --- a/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.cs +++ b/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.cs @@ -712,6 +712,8 @@ public static partial class Gen5SpirvTranslator if (UsesSubgroupOperations()) { _module.AddCapability(SpirvCapability.GroupNonUniform); + _module.AddCapability(SpirvCapability.GroupNonUniformBallot); + if (UsesSubgroupShuffle()) { _module.AddCapability(SpirvCapability.GroupNonUniformShuffle); @@ -722,10 +724,6 @@ public static partial class Gen5SpirvTranslator _module.AddCapability(SpirvCapability.GroupNonUniformVote); } - if (UsesSubgroupBroadcast() || UsesWaveControl()) - { - _module.AddCapability(SpirvCapability.GroupNonUniformBallot); - } } _glsl = _module.ImportExtInst("GLSL.std.450"); @@ -1803,13 +1801,16 @@ public static partial class Gen5SpirvTranslator if (instruction.Opcode == "SBarrier") { - var workgroup = UInt(2); - var semantics = UInt(0x108); - _module.AddStatement( - SpirvOp.ControlBarrier, - workgroup, - workgroup, - semantics); + if (_stage == Gen5SpirvStage.Compute) + { + var workgroup = UInt(2); + var semantics = UInt(0x108); + _module.AddStatement( + SpirvOp.ControlBarrier, + workgroup, + workgroup, + semantics); + } return true; }