From e2dcd902cdd83c68cfe4ed7a8b32cc99be88f815 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 17 May 2018 13:35:58 +0100 Subject: [PATCH] Gather and display callstack information during shader debugging --- qrenderdoc/Windows/ShaderViewer.cpp | 16 +++++++++- qrenderdoc/Windows/ShaderViewer.ui | 16 ++++++++++ renderdoc/api/replay/shader_types.h | 10 ++++-- renderdoc/driver/d3d11/d3d11_shaderdebug.cpp | 27 +++++++++++++++- .../driver/shaders/dxbc/dxbc_disassemble.cpp | 22 ++++++++++--- renderdoc/driver/shaders/dxbc/dxbc_inspect.h | 5 +-- renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp | 10 ++++-- renderdoc/driver/shaders/dxbc/dxbc_sdbg.h | 4 ++- renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp | 32 ++++++++++++------- renderdoc/driver/shaders/dxbc/dxbc_spdb.h | 4 ++- renderdoc/replay/renderdoc_serialise.inl | 1 + 11 files changed, 121 insertions(+), 26 deletions(-) diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index d8385c593..d9bdbfdce 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -69,6 +69,7 @@ ShaderViewer::ShaderViewer(ICaptureContext &ctx, QWidget *parent) ui->watch->setFont(Formatter::PreferredFont()); ui->inputSig->setFont(Formatter::PreferredFont()); ui->outputSig->setFont(Formatter::PreferredFont()); + ui->callstack->setFont(Formatter::PreferredFont()); // we create this up front so its state stays persistent as much as possible. m_FindReplace = new FindReplace(this); @@ -204,10 +205,11 @@ void ShaderViewer::editShader(bool customShader, const QString &entryPoint, cons m_DisassemblyView = NULL; - // hide watch, constants, variables + // hide debugging windows ui->watch->hide(); ui->variables->hide(); ui->constants->hide(); + ui->callstack->hide(); ui->snippets->setVisible(customShader); @@ -460,6 +462,13 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR ui->docking->setToolWindowProperties( ui->constants, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); + ui->callstack->setWindowTitle(tr("Callstack")); + ui->docking->addToolWindow( + ui->callstack, ToolWindowManager::AreaReference(ToolWindowManager::RightOf, + ui->docking->areaOf(ui->variables), 0.2f)); + ui->docking->setToolWindowProperties( + ui->callstack, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); + m_DisassemblyView->setMarginWidthN(1, 20.0 * devicePixelRatioF()); // display current line in margin 2, distinct from breakpoint in margin 1 @@ -1227,6 +1236,11 @@ void ShaderViewer::updateDebugging() } } + ui->callstack->clear(); + + for(const rdcstr &s : state.callstack) + ui->callstack->insertItem(0, s); + if(ui->constants->topLevelItemCount() == 0) { for(int i = 0; i < m_Trace->constantBlocks.count(); i++) diff --git a/qrenderdoc/Windows/ShaderViewer.ui b/qrenderdoc/Windows/ShaderViewer.ui index a3ebf59a7..d33a54788 100644 --- a/qrenderdoc/Windows/ShaderViewer.ui +++ b/qrenderdoc/Windows/ShaderViewer.ui @@ -538,6 +538,22 @@ + + + + 20 + 80 + 256 + 192 + + + + QFrame::NoFrame + + + QAbstractItemView::NoSelection + + diff --git a/renderdoc/api/replay/shader_types.h b/renderdoc/api/replay/shader_types.h index 66223d319..afd4e8c6a 100644 --- a/renderdoc/api/replay/shader_types.h +++ b/renderdoc/api/replay/shader_types.h @@ -235,7 +235,7 @@ struct ShaderDebugState bool operator==(const ShaderDebugState &o) const { return registers == o.registers && outputs == o.outputs && indexableTemps == o.indexableTemps && - nextInstruction == o.nextInstruction && flags == o.flags; + nextInstruction == o.nextInstruction && flags == o.flags && callstack == o.callstack; } bool operator<(const ShaderDebugState &o) const { @@ -249,6 +249,8 @@ struct ShaderDebugState return nextInstruction < o.nextInstruction; if(!(flags == o.flags)) return flags < o.flags; + if(!(callstack == o.callstack)) + return callstack < o.callstack; return false; } DOCUMENT("The temporary variables for this shader as a list of :class:`ShaderValue`."); @@ -260,8 +262,12 @@ struct ShaderDebugState "Indexable temporary variables for this shader as a list of :class:`ShaderValue` lists."); rdcarray indexableTemps; + DOCUMENT("An optional callstack listing function calls at the present instruction"); + rdcarray callstack; + DOCUMENT(R"(The next instruction to be executed after this state. The initial state before any -shader execution happened will have ``nextInstruction == 0``.)"); +shader execution happened will have ``nextInstruction == 0``. +)"); uint32_t nextInstruction; DOCUMENT("A set of :class:`ShaderEvents` flags that indicate what events happened on this step."); diff --git a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp index 2d0630b71..06b6bb4de 100644 --- a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp +++ b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp @@ -940,6 +940,8 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin vector states; + dxbc->m_DebugInfo->GetStack(0, dxbc->GetInstruction(0).offset, initialState.callstack); + states.push_back((State)initialState); D3D11MarkerRegion simloop("Simulation Loop"); @@ -951,6 +953,11 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin initialState = initialState.GetNext(global, NULL); + { + const ASMOperation &op = dxbc->GetInstruction((size_t)initialState.nextInstruction); + dxbc->m_DebugInfo->GetStack(initialState.nextInstruction, op.offset, initialState.callstack); + } + states.push_back((State)initialState); if(cycleCounter == SHADER_DEBUG_WARN_THRESHOLD) @@ -1810,6 +1817,8 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t vector states; + dxbc->m_DebugInfo->GetStack(0, dxbc->GetInstruction(0).offset, quad[destIdx].callstack); + states.push_back((State)quad[destIdx]); // ping pong between so that we can have 'current' quad to update into new one @@ -1843,7 +1852,16 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t // if our destination quad is paused don't record multiple identical states. if(activeMask[destIdx]) - states.push_back((State)curquad[destIdx]); + { + State &s = curquad[destIdx]; + + { + const ASMOperation &op = dxbc->GetInstruction((size_t)s.nextInstruction); + dxbc->m_DebugInfo->GetStack(s.nextInstruction, op.offset, s.callstack); + } + + states.push_back(s); + } // we need to make sure that control flow which converges stays in lockstep so that // derivatives are still valid. While diverged, we don't have to keep threads in lockstep @@ -1992,6 +2010,8 @@ ShaderDebugTrace D3D11Replay::DebugThread(uint32_t eventId, const uint32_t group vector states; + dxbc->m_DebugInfo->GetStack(0, dxbc->GetInstruction(0).offset, initialState.callstack); + states.push_back((State)initialState); for(int cycleCounter = 0;; cycleCounter++) @@ -2001,6 +2021,11 @@ ShaderDebugTrace D3D11Replay::DebugThread(uint32_t eventId, const uint32_t group initialState = initialState.GetNext(global, NULL); + { + const ASMOperation &op = dxbc->GetInstruction((size_t)initialState.nextInstruction); + dxbc->m_DebugInfo->GetStack(initialState.nextInstruction, op.offset, initialState.callstack); + } + states.push_back((State)initialState); if(cycleCounter == SHADER_DEBUG_WARN_THRESHOLD) diff --git a/renderdoc/driver/shaders/dxbc/dxbc_disassemble.cpp b/renderdoc/driver/shaders/dxbc/dxbc_disassemble.cpp index 4e017b1ec..22f1ccac6 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_disassemble.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_disassemble.cpp @@ -667,6 +667,7 @@ void DXBCFile::MakeDisassemblyString() int32_t prevFile = -1; int32_t prevLine = -1; + std::string prevFunc; size_t debugInst = 0; @@ -695,8 +696,9 @@ void DXBCFile::MakeDisassemblyString() { int32_t fileID = prevFile; int32_t lineNum = prevLine; + std::string func = prevFunc; - m_DebugInfo->GetFileLine(debugInst, m_Instructions[i].offset, fileID, lineNum); + m_DebugInfo->GetLineInfo(debugInst, m_Instructions[i].offset, fileID, lineNum, func); if(fileID >= 0 && lineNum >= 0 && (fileID != prevFile || lineNum != prevLine)) { @@ -722,13 +724,24 @@ void DXBCFile::MakeDisassemblyString() m_Disassembly += "\n"; - if((fileID != prevFile && fileID < (int32_t)fileLines.size()) || line == "") + if(((fileID != prevFile || func != prevFunc) && fileID < (int32_t)fileLines.size()) || + line == "") { m_Disassembly += " "; // "0000: " for(int in = 0; in < indent; in++) m_Disassembly += " "; - m_Disassembly += - StringFormat::Fmt("%s:%d\n", m_DebugInfo->Files[fileID].first.c_str(), lineNum + 1); + + if(!func.empty()) + { + m_Disassembly += + StringFormat::Fmt("%s:%d - %s()\n", m_DebugInfo->Files[fileID].first.c_str(), + lineNum + 1, func.c_str()); + } + else + { + m_Disassembly += + StringFormat::Fmt("%s:%d\n", m_DebugInfo->Files[fileID].first.c_str(), lineNum + 1); + } } if(line != "") @@ -742,6 +755,7 @@ void DXBCFile::MakeDisassemblyString() prevFile = fileID; prevLine = lineNum; + prevFunc = func; } char buf[64] = {0}; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_inspect.h b/renderdoc/driver/shaders/dxbc/dxbc_inspect.h index 0689933a8..271501192 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_inspect.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_inspect.h @@ -330,8 +330,9 @@ public: vector > Files; // - virtual void GetFileLine(size_t instruction, uintptr_t offset, int32_t &fileIdx, - int32_t &lineNum) const = 0; + virtual void GetLineInfo(size_t instruction, uintptr_t offset, int32_t &fileIdx, int32_t &lineNum, + std::string &funcName) const = 0; + virtual void GetStack(size_t instruction, uintptr_t offset, rdcarray &stack) const = 0; }; uint32_t DecodeFlags(const ShaderCompileFlags &compileFlags); diff --git a/renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp b/renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp index 0bc1a4fc4..f8c4916c9 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp @@ -98,8 +98,8 @@ SDBGChunk::SDBGChunk(void *data) m_HasDebugInfo = true; } -void SDBGChunk::GetFileLine(size_t instruction, uintptr_t offset, int32_t &fileIdx, - int32_t &lineNum) const +void SDBGChunk::GetLineInfo(size_t instruction, uintptr_t offset, int32_t &fileIdx, + int32_t &lineNum, std::string &func) const { if(instruction < m_Instructions.size()) { @@ -110,10 +110,16 @@ void SDBGChunk::GetFileLine(size_t instruction, uintptr_t offset, int32_t &fileI fileIdx = sym.fileID; lineNum = sym.lineNum - 1; + func = m_Entry; } } } +void SDBGChunk::GetStack(size_t instruction, uintptr_t offset, rdcarray &stack) const +{ + stack = {"Stack not available"}; +} + string SDBGChunk::GetSymbolName(int symbolID) { RDCASSERT(symbolID >= 0 && symbolID < (int)m_SymbolTable.size()); diff --git a/renderdoc/driver/shaders/dxbc/dxbc_sdbg.h b/renderdoc/driver/shaders/dxbc/dxbc_sdbg.h index f6405952d..e4e4f6181 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_sdbg.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_sdbg.h @@ -260,7 +260,9 @@ public: string GetEntryFunction() const { return m_Entry; } string GetShaderProfile() const { return m_Profile; } uint32_t GetShaderCompileFlags() const { return m_ShaderFlags; } - void GetFileLine(size_t instruction, uintptr_t offset, int32_t &fileIdx, int32_t &lineNum) const; + void GetLineInfo(size_t instruction, uintptr_t offset, int32_t &fileIdx, int32_t &lineNum, + std::string &func) const; + void GetStack(size_t instruction, uintptr_t offset, rdcarray &stack) const; private: SDBGChunk(); diff --git a/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp b/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp index 444204f33..d1fc7efd9 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp @@ -1044,20 +1044,28 @@ SPDBChunk::SPDBChunk(void *chunk) m_HasDebugInfo = true; } -void SPDBChunk::GetFileLine(size_t instruction, uintptr_t offset, int32_t &fileIdx, - int32_t &lineNum) const +void SPDBChunk::GetLineInfo(size_t instruction, uintptr_t offset, int32_t &fileIdx, + int32_t &lineNum, std::string &func) const { - for(auto it = m_Lines.begin(); it != m_Lines.end(); ++it) + auto it = m_Lines.lower_bound((uint32_t)offset); + + if(it != m_Lines.end() && (uintptr_t)it->first <= offset) { - if((uintptr_t)it->first <= offset) - { - fileIdx = it->second.fileIndex; - lineNum = it->second.lineStart - 1; // 0-indexed - } - else - { - return; - } + fileIdx = it->second.fileIndex; + lineNum = it->second.lineStart - 1; // 0-indexed + func = it->second.stack.back(); + } +} + +void SPDBChunk::GetStack(size_t instruction, uintptr_t offset, rdcarray &stack) const +{ + auto it = m_Lines.lower_bound((uint32_t)offset); + + if(it != m_Lines.end() && (uintptr_t)it->first <= offset) + { + stack.resize(it->second.stack.size()); + for(size_t i = 0; i < stack.size(); i++) + stack[i] = it->second.stack[i]; } } diff --git a/renderdoc/driver/shaders/dxbc/dxbc_spdb.h b/renderdoc/driver/shaders/dxbc/dxbc_spdb.h index 02a6f6ca4..9f4fd6e68 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_spdb.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_spdb.h @@ -258,7 +258,9 @@ public: std::string GetEntryFunction() const { return m_Entry; } std::string GetShaderProfile() const { return m_Profile; } uint32_t GetShaderCompileFlags() const { return m_ShaderFlags; } - void GetFileLine(size_t instruction, uintptr_t offset, int32_t &fileIdx, int32_t &lineNum) const; + void GetLineInfo(size_t instruction, uintptr_t offset, int32_t &fileIdx, int32_t &lineNum, + std::string &func) const; + void GetStack(size_t instruction, uintptr_t offset, rdcarray &stack) const; private: SPDBChunk(const SPDBChunk &); diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index c77fe00be..6b47cbba8 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -349,6 +349,7 @@ void DoSerialise(SerialiserType &ser, ShaderDebugState &el) SERIALISE_MEMBER(indexableTemps); SERIALISE_MEMBER(nextInstruction); SERIALISE_MEMBER(flags); + SERIALISE_MEMBER(callstack); SIZE_CHECK(56); }