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);
}