mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-04 17:10:47 +00:00
Gather and display callstack information during shader debugging
This commit is contained in:
@@ -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++)
|
||||
|
||||
@@ -538,6 +538,22 @@
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
<widget class="QListWidget" name="callstack">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>80</y>
|
||||
<width>256</width>
|
||||
<height>192</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
||||
@@ -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<ShaderVariable> indexableTemps;
|
||||
|
||||
DOCUMENT("An optional callstack listing function calls at the present instruction");
|
||||
rdcarray<rdcstr> 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.");
|
||||
|
||||
@@ -940,6 +940,8 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin
|
||||
|
||||
vector<ShaderDebugState> 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<ShaderDebugState> 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<ShaderDebugState> 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)
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -330,8 +330,9 @@ public:
|
||||
|
||||
vector<pair<string, string> > Files; // <filename, source>
|
||||
|
||||
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<rdcstr> &stack) const = 0;
|
||||
};
|
||||
|
||||
uint32_t DecodeFlags(const ShaderCompileFlags &compileFlags);
|
||||
|
||||
@@ -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<rdcstr> &stack) const
|
||||
{
|
||||
stack = {"Stack not available"};
|
||||
}
|
||||
|
||||
string SDBGChunk::GetSymbolName(int symbolID)
|
||||
{
|
||||
RDCASSERT(symbolID >= 0 && symbolID < (int)m_SymbolTable.size());
|
||||
|
||||
@@ -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<rdcstr> &stack) const;
|
||||
|
||||
private:
|
||||
SDBGChunk();
|
||||
|
||||
@@ -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<rdcstr> &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];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<rdcstr> &stack) const;
|
||||
|
||||
private:
|
||||
SPDBChunk(const SPDBChunk &);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user