From fda5c4819a72deea3b39e57d524a476a7a9977eb Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 20 Jul 2022 13:26:21 +0100 Subject: [PATCH] Scroll shader viewer to entry point file & line using debug info --- .../D3D11PipelineStateViewer.cpp | 20 +++++++++---- .../D3D12PipelineStateViewer.cpp | 10 +++++-- .../PipelineState/PipelineStateViewer.cpp | 27 +++++++++++++---- .../VulkanPipelineStateViewer.cpp | 13 ++++++--- qrenderdoc/Windows/ShaderViewer.cpp | 15 ++++++++++ renderdoc/api/replay/shader_types.h | 16 ++++++++++ .../driver/shaders/dxbc/dxbc_reflect.cpp | 2 ++ renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp | 5 +++- renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp | 6 ++++ .../driver/shaders/spirv/spirv_reflect.cpp | 29 +++++++++++++++++++ .../driver/shaders/spirv/spirv_reflect.h | 4 +++ renderdoc/replay/renderdoc_serialise.inl | 6 ++-- 12 files changed, 133 insertions(+), 20 deletions(-) diff --git a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp index 8d5a7c439..f151ba25f 100644 --- a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp @@ -996,9 +996,12 @@ void D3D11PipelineStateViewer::setShaderState(const D3D11Pipe::Shader &stage, RD if(shaderDetails && !shaderDetails->debugInfo.files.empty()) { + const ShaderDebugInfo &dbg = shaderDetails->debugInfo; + int entryFile = qMax(0, dbg.entryLocation.fileIndex); + shText += QFormatStr(": %1() - %2") .arg(shaderDetails->entryPoint) - .arg(QFileInfo(shaderDetails->debugInfo.files[0].filename).fileName()); + .arg(QFileInfo(dbg.files[entryFile].filename).fileName()); } shader->setText(shText); @@ -1258,10 +1261,12 @@ void D3D11PipelineStateViewer::setState() if(state.inputAssembly.bytecode && !state.inputAssembly.bytecode->debugInfo.files.empty()) { - layout += - QFormatStr(": %1() - %2") - .arg(state.inputAssembly.bytecode->entryPoint) - .arg(QFileInfo(state.inputAssembly.bytecode->debugInfo.files[0].filename).fileName()); + const ShaderDebugInfo &dbg = state.inputAssembly.bytecode->debugInfo; + int entryFile = qMax(0, dbg.entryLocation.fileIndex); + + layout += QFormatStr(": %1() - %2") + .arg(state.inputAssembly.bytecode->entryPoint) + .arg(QFileInfo(dbg.files[entryFile].filename).fileName()); } ui->iaBytecode->setText(layout); @@ -2636,9 +2641,12 @@ void D3D11PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D11Pipe if(shaderDetails && !shaderDetails->debugInfo.files.isEmpty()) { + const ShaderDebugInfo &dbg = shaderDetails->debugInfo; + int entryFile = qMax(0, dbg.entryLocation.fileIndex); + shadername = QFormatStr("%1() - %2") .arg(shaderDetails->entryPoint) - .arg(QFileInfo(shaderDetails->debugInfo.files[0].filename).fileName()); + .arg(QFileInfo(dbg.files[entryFile].filename).fileName()); } xml.writeStartElement(lit("p")); diff --git a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp index 3a3deee46..ba6879281 100644 --- a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp @@ -1057,9 +1057,12 @@ void D3D12PipelineStateViewer::setShaderState( if(shaderDetails && !shaderDetails->debugInfo.files.empty()) { + const ShaderDebugInfo &dbg = shaderDetails->debugInfo; + int entryFile = qMax(0, dbg.entryLocation.fileIndex); + shText += QFormatStr(": %1() - %2") .arg(shaderDetails->entryPoint) - .arg(QFileInfo(shaderDetails->debugInfo.files[0].filename).fileName()); + .arg(QFileInfo(dbg.files[entryFile].filename).fileName()); } shader->setText(shText); @@ -2739,9 +2742,12 @@ void D3D12PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D12Pipe if(shaderDetails && !shaderDetails->debugInfo.files.empty()) { + const ShaderDebugInfo &dbg = shaderDetails->debugInfo; + int entryFile = qMax(0, dbg.entryLocation.fileIndex); + shadername = QFormatStr("%1() - %2") .arg(shaderDetails->entryPoint) - .arg(QFileInfo(shaderDetails->debugInfo.files[0].filename).fileName()); + .arg(QFileInfo(dbg.files[entryFile].filename).fileName()); } xml.writeStartElement(lit("p")); diff --git a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp index 2d64e1b01..34b718fe6 100644 --- a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp @@ -994,8 +994,22 @@ IShaderViewer *PipelineStateViewer::EditOriginalShaderSource(ResourceId id, QSet uniqueFiles; rdcstrpairs files; - for(const ShaderSourceFile &s : shaderDetails->debugInfo.files) + // add the entry point file first, if we have one + const int entryFile = shaderDetails->debugInfo.entryLocation.fileIndex; + + for(int i = -1; i < shaderDetails->debugInfo.files.count(); i++) { + int idx = i; + if(idx < 0) + idx = entryFile; + else if(idx == entryFile) + continue; + + if(idx < 0) + continue; + + const ShaderSourceFile &s = shaderDetails->debugInfo.files[idx]; + QString filename = s.filename; uint filenameHash = qHash(filename.toLower()); @@ -1048,13 +1062,16 @@ void PipelineStateViewer::SetupShaderEditButton(QToolButton *button, ResourceId rdcarray accepted = m_Ctx.TargetShaderEncodings(); + const ShaderDebugInfo &dbg = shaderDetails->debugInfo; + // if we have original source and it's in a known format, display it as the first most preferred // option - if(!shaderDetails->debugInfo.files.empty() && - shaderDetails->debugInfo.encoding != ShaderEncoding::Unknown) + if(!dbg.files.empty() && dbg.encoding != ShaderEncoding::Unknown) { - QAction *action = - new QAction(tr("Edit Source - %1").arg(shaderDetails->debugInfo.files[0].filename), menu); + int entryFile = qMax(0, dbg.entryLocation.fileIndex); + if(dbg.editBaseFile >= 0 && dbg.editBaseFile < dbg.files.size()) + entryFile = dbg.editBaseFile; + QAction *action = new QAction(tr("Edit Source - %1").arg(dbg.files[entryFile].filename), menu); action->setIcon(Icons::page_white_edit()); QObject::connect(action, &QAction::triggered, [this, shaderId, shaderDetails]() { diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp index 49d6aa236..3be030624 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp @@ -1819,8 +1819,11 @@ void VulkanPipelineStateViewer::setShaderState(const VKPipe::Shader &stage, if(entryFunc != lit("main")) shText += lit(": ") + entryFunc + lit("()"); - if(!shaderDetails->debugInfo.files.isEmpty()) - shText += lit(" - ") + QFileInfo(shaderDetails->debugInfo.files[0].filename).fileName(); + const ShaderDebugInfo &dbg = shaderDetails->debugInfo; + int entryFile = qMax(0, dbg.entryLocation.fileIndex); + + if(!dbg.files.isEmpty()) + shText += lit(" - ") + QFileInfo(dbg.files[entryFile].filename).fileName(); } shader->setText(shText); @@ -3442,12 +3445,14 @@ void VulkanPipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const VKPipe:: if(shaderDetails) { QString entryFunc = shaderDetails->entryPoint; + const ShaderDebugInfo &dbg = shaderDetails->debugInfo; + int entryFile = qMax(0, dbg.entryLocation.fileIndex); if(entryFunc != lit("main")) shadername = QFormatStr("%1()").arg(entryFunc); - else if(!shaderDetails->debugInfo.files.isEmpty()) + else if(!dbg.files.isEmpty()) shadername = QFormatStr("%1() - %2") .arg(entryFunc) - .arg(QFileInfo(shaderDetails->debugInfo.files[0].filename).fileName()); + .arg(QFileInfo(dbg.files[entryFile].filename).fileName()); } xml.writeStartElement(lit("p")); diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index 3f200320c..b0ddb3553 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -480,8 +480,11 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR m_FileScintillas.reserve(m_ShaderDetails->debugInfo.files.count()); QWidget *sel = NULL; + int32_t entryFile = m_ShaderDetails->debugInfo.entryLocation.fileIndex; + int32_t i = -1; for(const ShaderSourceFile &f : m_ShaderDetails->debugInfo.files) { + i++; if(f.contents.isEmpty()) { m_FileScintillas.push_back(NULL); @@ -496,6 +499,18 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR if(sel == NULL) sel = scintilla; + if(i == entryFile) + { + sel = scintilla; + + if(m_ShaderDetails->debugInfo.entryLocation.lineStart > 0) + { + GUIInvoke::defer(scintilla, [scintilla, this]() { + ensureLineScrolled(scintilla, m_ShaderDetails->debugInfo.entryLocation.lineStart); + }); + } + } + m_FileScintillas.push_back(scintilla); } diff --git a/renderdoc/api/replay/shader_types.h b/renderdoc/api/replay/shader_types.h index 82351a48c..30cb8d234 100644 --- a/renderdoc/api/replay/shader_types.h +++ b/renderdoc/api/replay/shader_types.h @@ -1401,6 +1401,22 @@ The first entry in the list is always the file where the entry point is. )"); rdcarray files; + DOCUMENT(R"(The source location of the first executable line or the entry point. + +.. note:: + + The information is not guaranteed to be available depending on the underlying shader format, so + all of the elements are optional. +)"); + LineColumnInfo entryLocation; + + DOCUMENT(R"(The index of the file which should be used for re-editing this shader's entry point. + +This is an optional value, and if set to ``-1`` you should fall back to using the file specified +in :data:`entryLocation`, and if no file is specified there then use the first file listed. +)"); + int32_t editBaseFile = -1; + DOCUMENT("The :class:`ShaderEncoding` of the source. See :data:`files`."); ShaderEncoding encoding = ShaderEncoding::Unknown; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_reflect.cpp b/renderdoc/driver/shaders/dxbc/dxbc_reflect.cpp index 3b68b36e4..7e519c0c4 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_reflect.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_reflect.cpp @@ -299,6 +299,8 @@ void MakeShaderReflection(DXBC::DXBCContainer *dxbc, ShaderReflection *refl, refl->debugInfo.files = dxbc->GetDebugInfo()->Files; + dxbc->GetDebugInfo()->GetLineInfo(~0U, ~0U, refl->debugInfo.entryLocation); + rdcstr entry = dxbc->GetDebugInfo()->GetEntryFunction(); if(entry.empty()) entry = "main"; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp b/renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp index 8abc624cf..e202327e6 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp @@ -94,8 +94,11 @@ SDBGChunk::SDBGChunk(void *data) m_HasDebugInfo = true; } -void SDBGChunk::GetLineInfo(size_t instruction, uintptr_t offset, LineColumnInfo &lineInfo) const +void SDBGChunk::GetLineInfo(size_t instruction, uintptr_t, LineColumnInfo &lineInfo) const { + if(instruction == ~0U) + instruction = 0; + if(instruction < m_Instructions.size()) { int32_t symID = m_Instructions[instruction].symbol; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp b/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp index c62799401..7dfc1a4d1 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp @@ -1698,6 +1698,12 @@ SPDBChunk::SPDBChunk(byte *data, uint32_t spdblength) void SPDBChunk::GetLineInfo(size_t, uintptr_t offset, LineColumnInfo &lineInfo) const { + if(offset == ~0U && !m_InstructionInfo.empty()) + { + lineInfo = m_InstructionInfo.begin()->second.lineInfo; + return; + } + auto it = m_InstructionInfo.lower_bound((uint32_t)offset); if(it != m_InstructionInfo.end() && (uintptr_t)it->first <= offset) diff --git a/renderdoc/driver/shaders/spirv/spirv_reflect.cpp b/renderdoc/driver/shaders/spirv/spirv_reflect.cpp index 6091944ab..39edcde9b 100644 --- a/renderdoc/driver/shaders/spirv/spirv_reflect.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_reflect.cpp @@ -576,10 +576,27 @@ void Reflector::RegisterOp(Iter it) { sources.back().contents += strings[dbg.arg(0)]; } + else if(dbg.inst == ShaderDbg::Function) + { + LineColumnInfo &info = debugFuncToLocation[dbg.result]; + + info.fileIndex = (int32_t)debugSources[dbg.arg(2)]; + info.lineStart = info.lineEnd = EvaluateConstant(dbg.arg(3), {}).value.u32v[0]; + } + else if(dbg.inst == ShaderDbg::FunctionDefinition) + { + funcToLocation[dbg.arg(1)] = debugFuncToLocation[dbg.arg(0)]; + } else if(dbg.inst == ShaderDbg::CompilationUnit) { sources[debugSources[dbg.arg(2)]].lang = (SourceLanguage)EvaluateConstant(dbg.arg(3), {}).value.u32v[0]; + + compUnitToFileIndex[dbg.result] = debugSources[dbg.arg(2)]; + } + else if(dbg.inst == ShaderDbg::EntryPoint) + { + funcToBaseFile[dbg.arg(0)] = compUnitToFileIndex[dbg.arg(1)]; } else if(dbg.inst == ShaderDbg::GlobalVariable) { @@ -837,6 +854,18 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st reflection.debugInfo.files.push_back({sources[i].name, sources[i].contents}); } + { + auto it = funcToLocation.find(entry->id); + if(it != funcToLocation.end()) + reflection.debugInfo.entryLocation = it->second; + } + + { + auto it = funcToBaseFile.find(entry->id); + if(it != funcToBaseFile.end()) + reflection.debugInfo.editBaseFile = (int32_t)it->second; + } + PreprocessLineDirectives(reflection.debugInfo.files); // we do a mini-preprocess of the files from the debug info to handle #line directives. diff --git a/renderdoc/driver/shaders/spirv/spirv_reflect.h b/renderdoc/driver/shaders/spirv/spirv_reflect.h index 634fccd2e..9a5e5b3b0 100644 --- a/renderdoc/driver/shaders/spirv/spirv_reflect.h +++ b/renderdoc/driver/shaders/spirv/spirv_reflect.h @@ -129,6 +129,10 @@ private: DenseIdMap strings; rdcarray sources; SparseIdMap debugSources; + SparseIdMap compUnitToFileIndex; + SparseIdMap funcToBaseFile; + SparseIdMap debugFuncToLocation; + SparseIdMap funcToLocation; Id curBlock; std::set loopBlocks; diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index 12a9c7188..698a1cac6 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -265,11 +265,13 @@ void DoSerialise(SerialiserType &ser, ShaderDebugInfo &el) { SERIALISE_MEMBER(compileFlags); SERIALISE_MEMBER(files); + SERIALISE_MEMBER(entryLocation); + SERIALISE_MEMBER(editBaseFile); SERIALISE_MEMBER(encoding); SERIALISE_MEMBER(debuggable); SERIALISE_MEMBER(debugStatus); - SIZE_CHECK(80); + SIZE_CHECK(112); } template @@ -301,7 +303,7 @@ void DoSerialise(SerialiserType &ser, ShaderReflection &el) SERIALISE_MEMBER(pointerTypes); - SIZE_CHECK(360); + SIZE_CHECK(392); } template