Try to compile GLSL shaders to SPIR-V to enable debugging

This commit is contained in:
baldurk
2025-12-02 12:17:35 +00:00
parent c4f71f6223
commit 8ed7c14556
7 changed files with 88 additions and 18 deletions
+7
View File
@@ -732,6 +732,13 @@ public:
rdcarray<uint32_t> 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<uint32_t> convertedSpirvWords;
SPIRVPatchData convertedPatchData;
ShaderReflection convertedRefl;
// the parameters passed to glSpecializeShader
rdcstr entryPoint;
rdcarray<uint32_t> specIDs;
+2 -1
View File
@@ -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())
+3 -1
View File
@@ -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)
{
+21 -10
View File
@@ -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<rdcpair<rdcstr, size_t>> 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
@@ -282,18 +282,53 @@ void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &drv, ResourceI
if(reflected)
{
rdcarray<uint32_t> 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;
@@ -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";
@@ -60,6 +60,8 @@ struct CompilationSettings
InputLanguage lang = InputLanguage::Unknown;
bool debugInfo = false;
bool gles = false;
bool autoMapBindings = false;
bool autoMapLocations = false;
rdcstr entryPoint;
};