Vulkan: Always embed shader source code when RenderDoc, Nsight or other frame debuggers are attached (#1733)

This commit is contained in:
Crementif 2025-11-27 17:46:42 +01:00 committed by GitHub
parent bb3fb81fb6
commit 51c1e80ba7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 38 additions and 34 deletions

View file

@ -87,7 +87,7 @@ LatteTextureVk::LatteTextureVk(class VulkanRenderer* vkRenderer, Latte::E_DIM di
if (vkCreateImage(m_vkr->GetLogicalDevice(), &imageInfo, nullptr, &vkObjTex->m_image) != VK_SUCCESS) if (vkCreateImage(m_vkr->GetLogicalDevice(), &imageInfo, nullptr, &vkObjTex->m_image) != VK_SUCCESS)
m_vkr->UnrecoverableError("Failed to create texture image"); m_vkr->UnrecoverableError("Failed to create texture image");
if (m_vkr->IsDebugUtilsEnabled() && vkSetDebugUtilsObjectNameEXT) if (m_vkr->IsDebugMarkersEnabled())
{ {
VkDebugUtilsObjectNameInfoEXT objName{}; VkDebugUtilsObjectNameInfoEXT objName{};
objName.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; objName.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;

View file

@ -271,7 +271,7 @@ void RendererShaderVk::CreateVkShaderModule(std::span<uint32> spirvBuffer)
} }
// set debug name // set debug name
if (vkr->IsDebugUtilsEnabled() && vkSetDebugUtilsObjectNameEXT) if (vkr->IsDebugMarkersEnabled())
{ {
VkDebugUtilsObjectNameInfoEXT objName{}; VkDebugUtilsObjectNameInfoEXT objName{};
objName.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; objName.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
@ -292,8 +292,10 @@ void RendererShaderVk::FinishCompilation()
void RendererShaderVk::CompileInternal(bool isRenderThread) void RendererShaderVk::CompileInternal(bool isRenderThread)
{ {
const bool compileWithDebugInfo = ((VulkanRenderer*)g_renderer.get())->IsTracingToolEnabled();
// try to retrieve SPIR-V module from cache // try to retrieve SPIR-V module from cache
if (s_isLoadingShadersVk && (m_isGameShader && !m_isGfxPackShader) && s_spirvCache) if (s_isLoadingShadersVk && (m_isGameShader && !m_isGfxPackShader) && s_spirvCache && !compileWithDebugInfo)
{ {
cemu_assert_debug(m_baseHash != 0); cemu_assert_debug(m_baseHash != 0);
uint64 h1, h2; uint64 h1, h2;
@ -329,21 +331,12 @@ void RendererShaderVk::CompileInternal(bool isRenderThread)
Shader.setStrings(&cstr, 1); Shader.setStrings(&cstr, 1);
Shader.setEnvInput(glslang::EShSourceGlsl, state, glslang::EShClientVulkan, 100); Shader.setEnvInput(glslang::EShSourceGlsl, state, glslang::EShClientVulkan, 100);
Shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetClientVersion::EShTargetVulkan_1_1); Shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetClientVersion::EShTargetVulkan_1_1);
Shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetLanguageVersion::EShTargetSpv_1_3); Shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetLanguageVersion::EShTargetSpv_1_3);
TBuiltInResource Resources = GetDefaultBuiltInResource();
std::string PreprocessedGLSL; std::string PreprocessedGLSL;
VulkanRenderer* vkr = (VulkanRenderer*)g_renderer.get();
EShMessages messagesPreprocess;
if (vkr->IsDebugUtilsEnabled() && vkSetDebugUtilsObjectNameEXT)
messagesPreprocess = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules | EShMsgDebugInfo);
else
messagesPreprocess = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
glslang::TShader::ForbidIncluder Includer; glslang::TShader::ForbidIncluder Includer;
TBuiltInResource Resources = GetDefaultBuiltInResource();
EShMessages messagesPreprocess = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
if (!Shader.preprocess(&Resources, 450, ENoProfile, false, false, messagesPreprocess, &PreprocessedGLSL, Includer)) if (!Shader.preprocess(&Resources, 450, ENoProfile, false, false, messagesPreprocess, &PreprocessedGLSL, Includer))
{ {
cemuLog_log(LogType::Force, fmt::format("GLSL Preprocessing Failed For {:016x}_{:016x}: \"{}\"", m_baseHash, m_auxHash, Shader.getInfoLog())); cemuLog_log(LogType::Force, fmt::format("GLSL Preprocessing Failed For {:016x}_{:016x}: \"{}\"", m_baseHash, m_auxHash, Shader.getInfoLog()));
@ -351,14 +344,9 @@ void RendererShaderVk::CompileInternal(bool isRenderThread)
return; return;
} }
EShMessages messagesParseLink;
if (vkr->IsDebugUtilsEnabled() && vkSetDebugUtilsObjectNameEXT)
messagesParseLink = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules | EShMsgDebugInfo);
else
messagesParseLink = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
const char* PreprocessedCStr = PreprocessedGLSL.c_str(); const char* PreprocessedCStr = PreprocessedGLSL.c_str();
Shader.setStrings(&PreprocessedCStr, 1); Shader.setStrings(&PreprocessedCStr, 1);
EShMessages messagesParseLink = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
if (!Shader.parse(&Resources, 100, false, messagesParseLink)) if (!Shader.parse(&Resources, 100, false, messagesParseLink))
{ {
cemuLog_log(LogType::Force, fmt::format("GLSL parsing failed for {:016x}_{:016x}: \"{}\"", m_baseHash, m_auxHash, Shader.getInfoLog())); cemuLog_log(LogType::Force, fmt::format("GLSL parsing failed for {:016x}_{:016x}: \"{}\"", m_baseHash, m_auxHash, Shader.getInfoLog()));
@ -392,9 +380,17 @@ void RendererShaderVk::CompileInternal(bool isRenderThread)
glslang::SpvOptions spvOptions; glslang::SpvOptions spvOptions;
spvOptions.disableOptimizer = false; spvOptions.disableOptimizer = false;
spvOptions.generateDebugInfo = (vkr->IsDebugUtilsEnabled() && vkSetDebugUtilsObjectNameEXT);
spvOptions.validate = false; spvOptions.validate = false;
spvOptions.optimizeSize = true; spvOptions.optimizeSize = true;
if (compileWithDebugInfo)
{
spvOptions.generateDebugInfo = true;
spvOptions.emitNonSemanticShaderDebugInfo = true;
spvOptions.emitNonSemanticShaderDebugSource = true;
Shader.addSourceText(m_glslCode.c_str(), (uint32)m_glslCode.size());
Shader.setSourceFile(fmt::format("shader_{:016x}_{:016x}.glsl", m_baseHash, m_auxHash).c_str());
}
//auto beginTime = benchmarkTimer_start(); //auto beginTime = benchmarkTimer_start();
@ -402,8 +398,9 @@ void RendererShaderVk::CompileInternal(bool isRenderThread)
//double timeDur = benchmarkTimer_stop(beginTime); //double timeDur = benchmarkTimer_stop(beginTime);
//forceLogRemoveMe_printf("Shader GLSL-to-SPIRV compilation took %lfms Size %08x", timeDur, spirvBuffer.size()*4); //forceLogRemoveMe_printf("Shader GLSL-to-SPIRV compilation took %lfms Size %08x", timeDur, spirvBuffer.size()*4);
if (s_spirvCache && m_isGameShader && m_isGfxPackShader == false) // store in cache, unless it got compiled with debug info or is a modified shader from a gfx pack
if (s_spirvCache && m_isGameShader && m_isGfxPackShader == false && !compileWithDebugInfo)
{ {
uint64 h1, h2; uint64 h1, h2;
GenerateShaderPrecompiledCacheFilename(m_type, m_baseHash, m_auxHash, h1, h2); GenerateShaderPrecompiledCacheFilename(m_type, m_baseHash, m_auxHash, h1, h2);

View file

@ -446,8 +446,6 @@ VulkanRenderer::VulkanRenderer()
} }
CheckDeviceExtensionSupport(m_physicalDevice, m_featureControl); // todo - merge this with GetDeviceFeatures and separate from IsDeviceSuitable? CheckDeviceExtensionSupport(m_physicalDevice, m_featureControl); // todo - merge this with GetDeviceFeatures and separate from IsDeviceSuitable?
if (m_featureControl.debugMarkersSupported)
cemuLog_log(LogType::Force, "Debug: Frame debugger attached, will use vkDebugMarkerSetObjectNameEXT");
DetermineVendor(); DetermineVendor();
GetDeviceFeatures(); GetDeviceFeatures();
@ -582,10 +580,14 @@ VulkanRenderer::VulkanRenderer()
debugCallback.pfnUserCallback = &DebugUtilsCallback; debugCallback.pfnUserCallback = &DebugUtilsCallback;
vkCreateDebugUtilsMessengerEXT(m_instance, &debugCallback, nullptr, &m_debugCallback); vkCreateDebugUtilsMessengerEXT(m_instance, &debugCallback, nullptr, &m_debugCallback);
cemuLog_log(LogType::Force, "Debug: Vulkan validation layer enabled, vkCreateDebugUtilsMessengerEXT will be used to log validation errors");
} }
if (m_featureControl.instanceExtensions.debug_utils) if (this->IsTracingToolEnabled())
cemuLog_log(LogType::Force, "Using available debug function: vkCreateDebugUtilsMessengerEXT()"); cemuLog_log(LogType::Force, "Debug: Tracing tool detected, will recompile all shaders with debug info enabled. This disables the SPIR-V cache.");
if (this->IsDebugMarkersEnabled())
cemuLog_log(LogType::Force, "Debug: Detected tool capable of using debug markers, will use vkDebugMarkerSetObjectNameEXT to identify Vulkan objects");
// set initial viewport and scissor box size // set initial viewport and scissor box size
m_state.currentViewport.width = 4; m_state.currentViewport.width = 4;
@ -1256,8 +1258,9 @@ bool VulkanRenderer::CheckDeviceExtensionSupport(const VkPhysicalDevice device,
// dynamic rendering doesn't provide any benefits for us right now. Driver implementations are very unoptimized as of Feb 2022 // dynamic rendering doesn't provide any benefits for us right now. Driver implementations are very unoptimized as of Feb 2022
info.deviceExtensions.present_wait = isExtensionAvailable(VK_KHR_PRESENT_WAIT_EXTENSION_NAME) && isExtensionAvailable(VK_KHR_PRESENT_ID_EXTENSION_NAME); info.deviceExtensions.present_wait = isExtensionAvailable(VK_KHR_PRESENT_WAIT_EXTENSION_NAME) && isExtensionAvailable(VK_KHR_PRESENT_ID_EXTENSION_NAME);
// check for framedebuggers // check for validation layers and frame debuggers
info.debugMarkersSupported = false; info.usingDebugMarkerTool = false;
info.usingTracingTool = false;
if (info.deviceExtensions.tooling_info && vkGetPhysicalDeviceToolPropertiesEXT) if (info.deviceExtensions.tooling_info && vkGetPhysicalDeviceToolPropertiesEXT)
{ {
uint32_t toolCount = 0; uint32_t toolCount = 0;
@ -1268,8 +1271,10 @@ bool VulkanRenderer::CheckDeviceExtensionSupport(const VkPhysicalDevice device,
{ {
for (auto& itr : toolProperties) for (auto& itr : toolProperties)
{ {
if ((itr.purposes & VK_TOOL_PURPOSE_DEBUG_MARKERS_BIT_EXT) != 0) if ((itr.purposes & VK_TOOL_PURPOSE_DEBUG_MARKERS_BIT_EXT) != 0 && info.instanceExtensions.debug_utils && vkSetDebugUtilsObjectNameEXT)
info.debugMarkersSupported = true; info.usingDebugMarkerTool = true;
if ((itr.purposes & VK_TOOL_PURPOSE_TRACING_BIT) != 0)
info.usingTracingTool = true;
} }
} }
} }

View file

@ -482,7 +482,8 @@ private:
uint32 nonCoherentAtomSize = 256; uint32 nonCoherentAtomSize = 256;
}limits; }limits;
bool debugMarkersSupported{ false }; // frame debugger is attached bool usingDebugMarkerTool{ false }; // validation layer or other tool capable of handling debug markers is used
bool usingTracingTool{ false }; // frame debugger or other API replaying tool is used
bool disableMultithreadedCompilation{ false }; // for old nvidia drivers bool disableMultithreadedCompilation{ false }; // for old nvidia drivers
}m_featureControl{}; }m_featureControl{};
@ -937,7 +938,8 @@ public:
bool GetDisableMultithreadedCompilation() const { return m_featureControl.disableMultithreadedCompilation; } bool GetDisableMultithreadedCompilation() const { return m_featureControl.disableMultithreadedCompilation; }
bool UseTFViaSSBO() const { return m_featureControl.mode.useTFEmulationViaSSBO; } bool UseTFViaSSBO() const { return m_featureControl.mode.useTFEmulationViaSSBO; }
bool HasSPRIVRoundingModeRTE32() const { return m_featureControl.shaderFloatControls.shaderRoundingModeRTEFloat32; } bool HasSPRIVRoundingModeRTE32() const { return m_featureControl.shaderFloatControls.shaderRoundingModeRTEFloat32; }
bool IsDebugUtilsEnabled() const { return m_featureControl.debugMarkersSupported && m_featureControl.instanceExtensions.debug_utils; } bool IsDebugMarkersEnabled() const { return m_featureControl.usingDebugMarkerTool; }
bool IsTracingToolEnabled() const { return m_featureControl.usingTracingTool; }
private: private:

View file

@ -245,7 +245,7 @@ void compilePipelineThread_queue(PipelineCompiler* v)
bool VulkanRenderer::IsAsyncPipelineAllowed(uint32 numIndices) bool VulkanRenderer::IsAsyncPipelineAllowed(uint32 numIndices)
{ {
// frame debuggers dont handle async well (as of 2020) // frame debuggers dont handle async well (as of 2020)
if (IsDebugUtilsEnabled() && vkSetDebugUtilsObjectNameEXT) if (IsTracingToolEnabled())
return false; return false;
CachedFBOVk* currentFBO = m_state.activeFBO; CachedFBOVk* currentFBO = m_state.activeFBO;