From 8ed7c14556145d2f904238802113aa4a7e80e677 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 19 Nov 2025 13:40:20 +0000 Subject: [PATCH] Try to compile GLSL shaders to SPIR-V to enable debugging --- renderdoc/driver/gl/gl_driver.h | 7 +++ renderdoc/driver/gl/gl_replay.cpp | 3 +- renderdoc/driver/gl/gl_shader_refl.cpp | 4 +- renderdoc/driver/gl/gl_shaderdebug.cpp | 31 ++++++++---- .../driver/gl/wrappers/gl_shader_funcs.cpp | 47 ++++++++++++++++--- .../driver/shaders/spirv/spirv_compile.cpp | 12 +++++ .../driver/shaders/spirv/spirv_compile.h | 2 + 7 files changed, 88 insertions(+), 18 deletions(-) diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index 85c62baf8..d43864695 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -732,6 +732,13 @@ public: rdcarray spirvWords; SPIRVPatchData patchData; + // used if the application uploaded GLSL but we were able to compile to SPIR-V + bool convertedSPIRV = false; + bool convertedAutomapped = false; + rdcarray convertedSpirvWords; + SPIRVPatchData convertedPatchData; + ShaderReflection convertedRefl; + // the parameters passed to glSpecializeShader rdcstr entryPoint; rdcarray specIDs; diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index 9144d4a48..6f394acd5 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -819,7 +819,8 @@ rdcstr GLReplay::DisassembleShader(ResourceId pipeline, const ShaderReflection * ResourceId liveId = m_pDriver->GetResourceManager()->GetLiveID(refl->resourceId); const WrappedOpenGL::ShaderData &shaderDetails = m_pDriver->GetShader(liveId); - if(shaderDetails.sources.empty() && shaderDetails.spirvWords.empty()) + if(shaderDetails.sources.empty() && shaderDetails.spirvWords.empty() && + shaderDetails.convertedSpirvWords.empty()) return "; Invalid Shader Specified"; if(target == SPIRVDisassemblyTarget || target.empty()) diff --git a/renderdoc/driver/gl/gl_shader_refl.cpp b/renderdoc/driver/gl/gl_shader_refl.cpp index d4a0c66a0..14a00e1d1 100644 --- a/renderdoc/driver/gl/gl_shader_refl.cpp +++ b/renderdoc/driver/gl/gl_shader_refl.cpp @@ -1253,7 +1253,9 @@ void MakeShaderReflection(GLenum shadType, GLuint sepProg, ShaderReflection &ref refl.debugInfo.compiler = KnownShaderTool::Unknown; refl.debugInfo.encoding = ShaderEncoding::GLSL; refl.debugInfo.debuggable = false; - refl.debugInfo.debugStatus = "Shader debugging not supported for GLSL shaders"; + refl.debugInfo.debugStatus = + "Shader debugging not supported for legacy GLSL shaders.\n" + "Only modern GLSL compatible with SPIR-V compilation can be debugged."; if(shadType == eGL_COMPUTE_SHADER) { diff --git a/renderdoc/driver/gl/gl_shaderdebug.cpp b/renderdoc/driver/gl/gl_shaderdebug.cpp index 4df798e04..4fcbb7aeb 100644 --- a/renderdoc/driver/gl/gl_shaderdebug.cpp +++ b/renderdoc/driver/gl/gl_shaderdebug.cpp @@ -2033,8 +2033,12 @@ void SetInputs(out Inputs inputs) {} inputDecl += "struct Inputs {\n"; inputFetch += "void SetInputs(out Inputs inputs) {\n"; - const ShaderReflection *refl = shadDetails.GetReflection(); - const SPIRVPatchData &patchData = shadDetails.patchData; + // use the converted SPIR-V compiled reflection as it's more likely to have accurate data - + // driver reflection can omit inputs even if they're declared and needed for matching + const ShaderReflection *refl = + shadDetails.convertedSPIRV ? &shadDetails.convertedRefl : shadDetails.GetReflection(); + const SPIRVPatchData &patchData = + shadDetails.convertedSPIRV ? shadDetails.convertedPatchData : shadDetails.patchData; rdcarray> blockVarsToDeclare; @@ -2850,7 +2854,10 @@ ShaderDebugTrace *GLReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, for(size_t i = 0; i < shadDetails.specIDs.size() && i < shadDetails.specValues.size(); i++) spec.push_back(SpecConstant(shadDetails.specIDs[i], shadDetails.specValues[i], 4)); - const ShaderReflection *refl = shadDetails.GetReflection(); + // use converted reflection unless we had native SPIR-V reflection because the input fetcher will + // use it as well and we might have extra inputs (that were stripped from the driver's GL reflection) + const ShaderReflection *refl = + shadDetails.convertedSPIRV ? &shadDetails.convertedRefl : shadDetails.GetReflection(); if(!refl->debugInfo.debuggable) { @@ -2865,7 +2872,10 @@ ShaderDebugTrace *GLReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, SubgroupSupport subgroupSupport = SubgroupSupport::None; uint32_t numThreads = 4; - if(shadDetails.patchData.threadScope & rdcspv::ThreadScope::Subgroup) + const SPIRVPatchData &patchData = + shadDetails.convertedSPIRV ? shadDetails.convertedPatchData : shadDetails.patchData; + + if(patchData.threadScope & rdcspv::ThreadScope::Subgroup) { uint32_t maxSubgroupSize = 1; CalculateSubgroupProperties(maxSubgroupSize, subgroupSupport); @@ -2926,7 +2936,7 @@ ShaderDebugTrace *GLReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, } uint32_t paramAlign, structStride; - rdctie(paramAlign, structStride) = GetAlignAndOutputSize(refl, shadDetails.patchData); + rdctie(paramAlign, structStride) = GetAlignAndOutputSize(refl, patchData); // struct size is ResultDataBase header plus Nx structStride for the number of threads uint32_t structSize = sizeof(rdcspv::ResultDataBase) + structStride * numThreads; @@ -3112,7 +3122,8 @@ ShaderDebugTrace *GLReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, if(winner) { rdcspv::Debugger *debugger = new rdcspv::Debugger; - debugger->Parse(shadDetails.spirvWords); + debugger->Parse(shadDetails.convertedSPIRV ? shadDetails.convertedSpirvWords + : shadDetails.spirvWords); // the per-thread data immediately follows the rdcspv::ResultDataBase header. Every piece of // data is uniformly aligned, either 16-byte by default or 32-byte if larger components exist. @@ -3121,7 +3132,7 @@ ShaderDebugTrace *GLReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, numThreads = 4; - if(shadDetails.patchData.threadScope & rdcspv::ThreadScope::Subgroup) + if(patchData.threadScope & rdcspv::ThreadScope::Subgroup) { RDCASSERTNOTEQUAL(winner->subgroupSize, 0); numThreads = RDCMAX(numThreads, winner->subgroupSize); @@ -3135,7 +3146,7 @@ ShaderDebugTrace *GLReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, { byte *value = LaneData + t * structStride; - if(shadDetails.patchData.threadScope & rdcspv::ThreadScope::Subgroup) + if(patchData.threadScope & rdcspv::ThreadScope::Subgroup) { rdcspv::SubgroupLaneData *subgroupData = (rdcspv::SubgroupLaneData *)value; apiWrapper->thread_props[t][(size_t)rdcspv::ThreadProperty::Active] = subgroupData->isActive; @@ -3232,8 +3243,8 @@ ShaderDebugTrace *GLReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, ShaderVariable(rdcstr(), numThreads, 0U, 0U, 0U); ret = debugger->BeginDebug(apiWrapper, ShaderStage::Pixel, entryPoint, spec, - shadDetails.spirvInstructionLines, shadDetails.patchData, - winner->laneIndex, numThreads, numThreads); + shadDetails.spirvInstructionLines, patchData, winner->laneIndex, + numThreads, numThreads); apiWrapper->ResetReplay(); } else diff --git a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp index c01844c7a..ce48bc481 100644 --- a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp @@ -282,18 +282,53 @@ void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &drv, ResourceI if(reflected) { - rdcarray spirvwords; - rdcspv::CompilationSettings settings(rdcspv::InputLanguage::OpenGLGLSL, rdcspv::ShaderStage(ShaderIdx(type))); settings.gles = IsGLES; + settings.debugInfo = true; - rdcstr s = rdcspv::Compile(settings, sources, spirvwords); - if(!spirvwords.empty()) - spirv.Parse(spirvwords); + rdcstr err = rdcspv::Compile(settings, sources, convertedSpirvWords); + if(!convertedSpirvWords.empty()) + { + convertedSPIRV = true; + spirv.Parse(convertedSpirvWords); + + spirv.MakeReflection(GraphicsAPI::OpenGL, ShaderStage(ShaderIdx(type)), "main", {}, + convertedRefl, convertedPatchData); + } else - disassembly = "Disassembly to SPIR-V failed:\n\n" + s; + { + // enable automapping and try again + settings.autoMapBindings = true; + settings.autoMapLocations = true; + err = rdcspv::Compile(settings, sources, convertedSpirvWords); + + if(!convertedSpirvWords.empty()) + { + convertedSPIRV = true; + convertedAutomapped = true; + spirv.Parse(convertedSpirvWords); + + spirv.MakeReflection(GraphicsAPI::OpenGL, ShaderStage(ShaderIdx(type)), "main", {}, + convertedRefl, convertedPatchData); + } + else + { + disassembly = "Disassembly to SPIR-V failed:\n\n" + err; + } + } + + if(convertedSPIRV) + { + // we could assert here that convertedRefl looks like the real reflection + reflection->debugInfo.debuggable = convertedRefl.debugInfo.debuggable; + reflection->debugInfo.debugStatus = convertedRefl.debugInfo.debugStatus; + reflection->debugInfo.sourceDebugInformation = + convertedRefl.debugInfo.sourceDebugInformation; + if(reflection->debugInfo.sourceDebugInformation) + reflection->debugInfo.compileFlags.flags.push_back({"preferSourceDebug", "1"}); + } reflection->resourceId = id; diff --git a/renderdoc/driver/shaders/spirv/spirv_compile.cpp b/renderdoc/driver/shaders/spirv/spirv_compile.cpp index 238727842..f3fac162e 100644 --- a/renderdoc/driver/shaders/spirv/spirv_compile.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_compile.cpp @@ -70,6 +70,15 @@ rdcstr rdcspv::Compile(const rdcspv::CompilationSettings &settings, const rdcarr glslang::TShader *shader = new glslang::TShader(lang); + shader->setAutoMapBindings(settings.autoMapBindings); + shader->setAutoMapLocations(settings.autoMapLocations); + // glslang internally allows up to 4095 for locations - this gives plenty space for I/O where + // only 128 components are allowed max + shader->setIOLocationBase(3900); + // don't set a limit for uniforms, as we don't know a reasonable value and can't make use of + // this information + // shader->setUniformLocationBase(0); + shader->setStringsWithLengthsAndNames(strs, NULL, names, (int)sources.size()); if(!settings.entryPoint.empty()) @@ -102,6 +111,9 @@ rdcstr rdcspv::Compile(const rdcspv::CompilationSettings &settings, const rdcarr success = program->link(EShMsgDefault); + if(success && (settings.autoMapBindings || settings.autoMapLocations)) + success = program->mapIO(); + if(!success) { errors = "Program failed to link:\n\n"; diff --git a/renderdoc/driver/shaders/spirv/spirv_compile.h b/renderdoc/driver/shaders/spirv/spirv_compile.h index 11591e12f..b8a97c85f 100644 --- a/renderdoc/driver/shaders/spirv/spirv_compile.h +++ b/renderdoc/driver/shaders/spirv/spirv_compile.h @@ -60,6 +60,8 @@ struct CompilationSettings InputLanguage lang = InputLanguage::Unknown; bool debugInfo = false; bool gles = false; + bool autoMapBindings = false; + bool autoMapLocations = false; rdcstr entryPoint; };