diff --git a/qrenderdoc/Code/pyrenderdoc/renderdoc.i b/qrenderdoc/Code/pyrenderdoc/renderdoc.i index c1b184c29..e67196f66 100644 --- a/qrenderdoc/Code/pyrenderdoc/renderdoc.i +++ b/qrenderdoc/Code/pyrenderdoc/renderdoc.i @@ -257,6 +257,7 @@ TEMPLATE_ARRAY_INSTANTIATE(rdcarray, PathEntry) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, PixelModification) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ResourceDescription) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ResourceId) +TEMPLATE_ARRAY_INSTANTIATE(rdcarray, LineColumnInfo) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderCompileFlag) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderConstant) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderDebugState) diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index 3ca60ecb2..732a8e6d3 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -105,24 +105,6 @@ ShaderViewer::ShaderViewer(ICaptureContext &ctx, QWidget *parent) QObject::connect(m_DisassemblyView, &ScintillaEdit::keyPressed, this, &ShaderViewer::readonly_keyPressed); - // C# LightCoral - m_DisassemblyView->markerSetBack(CURRENT_MARKER, SCINTILLA_COLOUR(240, 128, 128)); - m_DisassemblyView->markerSetBack(CURRENT_MARKER + 1, SCINTILLA_COLOUR(240, 128, 128)); - m_DisassemblyView->markerDefine(CURRENT_MARKER, SC_MARK_SHORTARROW); - m_DisassemblyView->markerDefine(CURRENT_MARKER + 1, SC_MARK_BACKGROUND); - - // C# LightSlateGray - m_DisassemblyView->markerSetBack(FINISHED_MARKER, SCINTILLA_COLOUR(119, 136, 153)); - m_DisassemblyView->markerSetBack(FINISHED_MARKER + 1, SCINTILLA_COLOUR(119, 136, 153)); - m_DisassemblyView->markerDefine(FINISHED_MARKER, SC_MARK_ROUNDRECT); - m_DisassemblyView->markerDefine(FINISHED_MARKER + 1, SC_MARK_BACKGROUND); - - // C# Red - m_DisassemblyView->markerSetBack(BREAKPOINT_MARKER, SCINTILLA_COLOUR(255, 0, 0)); - m_DisassemblyView->markerSetBack(BREAKPOINT_MARKER + 1, SCINTILLA_COLOUR(255, 0, 0)); - m_DisassemblyView->markerDefine(BREAKPOINT_MARKER, SC_MARK_CIRCLE); - m_DisassemblyView->markerDefine(BREAKPOINT_MARKER + 1, SC_MARK_BACKGROUND); - m_Scintillas.push_back(m_DisassemblyView); m_DisassemblyFrame = new QWidget(this); @@ -355,7 +337,6 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR QObject::connect(m_DisassemblyView, &ScintillaEdit::buttonReleased, this, &ShaderViewer::disassembly_buttonReleased); - // suppress the built-in context menu and hook up our own if(trace) { if(m_Stage == ShaderStage::Vertex) @@ -371,14 +352,8 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR ANALYTIC_SET(ShaderDebug.Compute, true); } - m_DisassemblyView->usePopUp(SC_POPUP_NEVER); - m_DisassemblyFrame->layout()->removeWidget(m_DisassemblyToolbar); - m_DisassemblyView->setContextMenuPolicy(Qt::CustomContextMenu); - QObject::connect(m_DisassemblyView, &ScintillaEdit::customContextMenuRequested, this, - &ShaderViewer::disassembly_contextMenu); - m_DisassemblyView->setMouseDwellTime(500); QObject::connect(m_DisassemblyView, &ScintillaEdit::dwellStart, this, @@ -394,7 +369,7 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR else setWindowTitle(shader->entryPoint); - int fileIdx = 0; + m_FileScintillas.reserve(shader->debugInfo.files.count()); QWidget *sel = NULL; for(const ShaderSourceFile &f : shader->debugInfo.files) @@ -407,7 +382,7 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR if(sel == NULL) sel = scintilla; - fileIdx++; + m_FileScintillas.push_back(scintilla); } if(trace || sel == NULL) @@ -491,14 +466,6 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR ui->locals->hide(); } - m_DisassemblyView->setMarginWidthN(1, 20.0 * devicePixelRatioF()); - - // display current line in margin 2, distinct from breakpoint in margin 1 - sptr_t markMask = (1 << CURRENT_MARKER) | (1 << FINISHED_MARKER); - - m_DisassemblyView->setMarginMaskN(1, m_DisassemblyView->marginMaskN(1) & ~markMask); - m_DisassemblyView->setMarginMaskN(2, m_DisassemblyView->marginMaskN(2) | markMask); - QObject::connect(ui->stepBack, &QToolButton::clicked, this, &ShaderViewer::stepBack); QObject::connect(ui->stepNext, &QToolButton::clicked, this, &ShaderViewer::stepNext); QObject::connect(ui->runBack, &QToolButton::clicked, this, &ShaderViewer::runBack); @@ -507,19 +474,38 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR QObject::connect(ui->runToSample, &QToolButton::clicked, this, &ShaderViewer::runToSample); QObject::connect(ui->runToNaNOrInf, &QToolButton::clicked, this, &ShaderViewer::runToNanOrInf); - QObject::connect(new QShortcut(QKeySequence(Qt::Key_F10), m_DisassemblyView), - &QShortcut::activated, this, &ShaderViewer::stepNext); - QObject::connect(new QShortcut(QKeySequence(Qt::Key_F10 | Qt::ShiftModifier), m_DisassemblyView), + for(ScintillaEdit *edit : m_Scintillas) + { + edit->setMarginWidthN(1, 20.0 * devicePixelRatioF()); + + // display current line in margin 2, distinct from breakpoint in margin 1 + sptr_t markMask = (1 << CURRENT_MARKER) | (1 << FINISHED_MARKER); + + edit->setMarginMaskN(1, edit->marginMaskN(1) & ~markMask); + edit->setMarginMaskN(2, edit->marginMaskN(2) | markMask); + + // suppress the built-in context menu and hook up our own + edit->usePopUp(SC_POPUP_NEVER); + + edit->setContextMenuPolicy(Qt::CustomContextMenu); + QObject::connect(edit, &ScintillaEdit::customContextMenuRequested, this, + &ShaderViewer::debug_contextMenu); + } + + // register the shortcuts globally for this shader viewer so it works regardless of the active + // scintilla + QObject::connect(new QShortcut(QKeySequence(Qt::Key_F10), this), &QShortcut::activated, this, + &ShaderViewer::stepNext); + QObject::connect(new QShortcut(QKeySequence(Qt::Key_F10 | Qt::ShiftModifier), this), &QShortcut::activated, this, &ShaderViewer::stepBack); - QObject::connect( - new QShortcut(QKeySequence(Qt::Key_F10 | Qt::ControlModifier), m_DisassemblyView), - &QShortcut::activated, this, &ShaderViewer::runToCursor); - QObject::connect(new QShortcut(QKeySequence(Qt::Key_F5), m_DisassemblyView), - &QShortcut::activated, this, &ShaderViewer::run); - QObject::connect(new QShortcut(QKeySequence(Qt::Key_F5 | Qt::ShiftModifier), m_DisassemblyView), + QObject::connect(new QShortcut(QKeySequence(Qt::Key_F10 | Qt::ControlModifier), this), + &QShortcut::activated, this, &ShaderViewer::runToCursor); + QObject::connect(new QShortcut(QKeySequence(Qt::Key_F5), this), &QShortcut::activated, this, + &ShaderViewer::run); + QObject::connect(new QShortcut(QKeySequence(Qt::Key_F5 | Qt::ShiftModifier), this), &QShortcut::activated, this, &ShaderViewer::runBack); - QObject::connect(new QShortcut(QKeySequence(Qt::Key_F9), m_DisassemblyView), - &QShortcut::activated, [this]() { ToggleBreakpoint(); }); + QObject::connect(new QShortcut(QKeySequence(Qt::Key_F9), this), &QShortcut::activated, + [this]() { ToggleBreakpoint(); }); // event filter to pick up tooltip events ui->constants->installEventFilter(this); @@ -541,6 +527,8 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR } ui->watch->resizeRowsToContents(); + + ToolWindowManager::raiseToolWindow(m_DisassemblyFrame); } else { @@ -641,6 +629,41 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR ui->docking->setToolWindowProperties( ui->outputSig, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); } + + for(ScintillaEdit *edit : m_Scintillas) + { + // C# LightCoral + edit->markerSetBack(CURRENT_MARKER, SCINTILLA_COLOUR(240, 128, 128)); + edit->markerSetBack(CURRENT_MARKER + 1, SCINTILLA_COLOUR(240, 128, 128)); + edit->markerDefine(CURRENT_MARKER, SC_MARK_SHORTARROW); + edit->markerDefine(CURRENT_MARKER + 1, SC_MARK_BACKGROUND); + edit->indicSetFore(CURRENT_INDICATOR, SCINTILLA_COLOUR(240, 128, 128)); + edit->indicSetAlpha(CURRENT_INDICATOR, 220); + edit->indicSetOutlineAlpha(CURRENT_INDICATOR, 255); + edit->indicSetUnder(CURRENT_INDICATOR, true); + edit->indicSetStyle(CURRENT_INDICATOR, INDIC_STRAIGHTBOX); + edit->indicSetHoverFore(CURRENT_INDICATOR, SCINTILLA_COLOUR(240, 128, 128)); + edit->indicSetHoverStyle(CURRENT_INDICATOR, INDIC_STRAIGHTBOX); + + // C# LightSlateGray + edit->markerSetBack(FINISHED_MARKER, SCINTILLA_COLOUR(119, 136, 153)); + edit->markerSetBack(FINISHED_MARKER + 1, SCINTILLA_COLOUR(119, 136, 153)); + edit->markerDefine(FINISHED_MARKER, SC_MARK_ROUNDRECT); + edit->markerDefine(FINISHED_MARKER + 1, SC_MARK_BACKGROUND); + edit->indicSetFore(FINISHED_INDICATOR, SCINTILLA_COLOUR(119, 136, 153)); + edit->indicSetAlpha(FINISHED_INDICATOR, 220); + edit->indicSetOutlineAlpha(FINISHED_INDICATOR, 255); + edit->indicSetUnder(FINISHED_INDICATOR, true); + edit->indicSetStyle(FINISHED_INDICATOR, INDIC_STRAIGHTBOX); + edit->indicSetHoverFore(FINISHED_INDICATOR, SCINTILLA_COLOUR(119, 136, 153)); + edit->indicSetHoverStyle(FINISHED_INDICATOR, INDIC_STRAIGHTBOX); + + // C# Red + edit->markerSetBack(BREAKPOINT_MARKER, SCINTILLA_COLOUR(255, 0, 0)); + edit->markerSetBack(BREAKPOINT_MARKER + 1, SCINTILLA_COLOUR(255, 0, 0)); + edit->markerDefine(BREAKPOINT_MARKER, SC_MARK_CIRCLE); + edit->markerDefine(BREAKPOINT_MARKER + 1, SC_MARK_BACKGROUND); + } } void ShaderViewer::updateWindowTitle() @@ -793,12 +816,33 @@ void ShaderViewer::editable_keyPressed(QKeyEvent *event) } } -void ShaderViewer::disassembly_contextMenu(const QPoint &pos) +void ShaderViewer::debug_contextMenu(const QPoint &pos) { - int scintillaPos = m_DisassemblyView->positionFromPoint(pos.x(), pos.y()); + ScintillaEdit *edit = qobject_cast(QObject::sender()); + + bool isDisasm = (edit == m_DisassemblyView); + + int scintillaPos = edit->positionFromPoint(pos.x(), pos.y()); QMenu contextMenu(this); + QAction gotoOther(isDisasm ? tr("Go to Source") : tr("Go to Disassembly"), this); + + QObject::connect(&gotoOther, &QAction::triggered, [this, isDisasm]() { + if(isDisasm && m_CurInstructionScintilla) + { + ToolWindowManager::raiseToolWindow(m_CurInstructionScintilla); + m_CurInstructionScintilla->setFocus(Qt::MouseFocusReason); + } + else if(!isDisasm) + { + ToolWindowManager::raiseToolWindow(m_DisassemblyFrame); + m_DisassemblyFrame->setFocus(Qt::MouseFocusReason); + } + + updateDebugging(); + }); + QAction intDisplay(tr("Integer register display"), this); QAction floatDisplay(tr("Float register display"), this); @@ -811,6 +855,12 @@ void ShaderViewer::disassembly_contextMenu(const QPoint &pos) QObject::connect(&intDisplay, &QAction::triggered, this, &ShaderViewer::on_intView_clicked); QObject::connect(&floatDisplay, &QAction::triggered, this, &ShaderViewer::on_floatView_clicked); + if(isDisasm && m_CurInstructionScintilla == NULL) + gotoOther.setEnabled(false); + + contextMenu.addAction(&gotoOther); + contextMenu.addSeparator(); + contextMenu.addAction(&intDisplay); contextMenu.addAction(&floatDisplay); contextMenu.addSeparator(); @@ -831,22 +881,26 @@ void ShaderViewer::disassembly_contextMenu(const QPoint &pos) contextMenu.addAction(&runCursor); contextMenu.addSeparator(); + if(!isDisasm) + { + addBreakpoint.setEnabled(false); + runCursor.setEnabled(false); + } + QAction copyText(tr("Copy"), this); QAction selectAll(tr("Select All"), this); - copyText.setEnabled(!m_DisassemblyView->selectionEmpty()); + copyText.setEnabled(!edit->selectionEmpty()); - QObject::connect(©Text, &QAction::triggered, [this] { - m_DisassemblyView->copyRange(m_DisassemblyView->selectionStart(), - m_DisassemblyView->selectionEnd()); - }); - QObject::connect(&selectAll, &QAction::triggered, [this] { m_DisassemblyView->selectAll(); }); + QObject::connect(©Text, &QAction::triggered, + [this, edit] { edit->copyRange(edit->selectionStart(), edit->selectionEnd()); }); + QObject::connect(&selectAll, &QAction::triggered, [this, edit] { edit->selectAll(); }); contextMenu.addAction(©Text); contextMenu.addAction(&selectAll); contextMenu.addSeparator(); - RDDialog::show(&contextMenu, m_DisassemblyView->viewport()->mapToGlobal(pos)); + RDDialog::show(&contextMenu, edit->viewport()->mapToGlobal(pos)); } void ShaderViewer::disassembly_buttonReleased(QMouseEvent *event) @@ -1015,7 +1069,36 @@ bool ShaderViewer::stepBack() if(CurrentStep() == 0) return false; - SetCurrentStep(CurrentStep() - 1); + if(isSourceDebugging()) + { + const ShaderDebugState &oldstate = m_Trace->states[CurrentStep()]; + + LineColumnInfo oldLine = m_Trace->lineInfo[oldstate.nextInstruction]; + + while(CurrentStep() < m_Trace->states.count()) + { + m_CurrentStep--; + + const ShaderDebugState &state = m_Trace->states[m_CurrentStep]; + + if(m_Breakpoints.contains((int)state.nextInstruction)) + break; + + if(m_CurrentStep == 0) + break; + + if(m_Trace->lineInfo[state.nextInstruction] == oldLine) + continue; + + break; + } + + SetCurrentStep(CurrentStep()); + } + else + { + SetCurrentStep(CurrentStep() - 1); + } return true; } @@ -1028,7 +1111,36 @@ bool ShaderViewer::stepNext() if(CurrentStep() + 1 >= m_Trace->states.count()) return false; - SetCurrentStep(CurrentStep() + 1); + if(isSourceDebugging()) + { + const ShaderDebugState &oldstate = m_Trace->states[CurrentStep()]; + + LineColumnInfo oldLine = m_Trace->lineInfo[oldstate.nextInstruction]; + + while(CurrentStep() < m_Trace->states.count()) + { + m_CurrentStep++; + + const ShaderDebugState &state = m_Trace->states[m_CurrentStep]; + + if(m_Breakpoints.contains((int)state.nextInstruction)) + break; + + if(m_CurrentStep + 1 >= m_Trace->states.count()) + break; + + if(m_Trace->lineInfo[state.nextInstruction] == oldLine) + continue; + + break; + } + + SetCurrentStep(CurrentStep()); + } + else + { + SetCurrentStep(CurrentStep() + 1); + } return true; } @@ -1244,6 +1356,18 @@ void ShaderViewer::updateDebugging() m_DisassemblyView->markerDeleteAll(FINISHED_MARKER); m_DisassemblyView->markerDeleteAll(FINISHED_MARKER + 1); + if(m_CurInstructionScintilla) + { + m_CurInstructionScintilla->markerDeleteAll(CURRENT_MARKER); + m_CurInstructionScintilla->markerDeleteAll(CURRENT_MARKER + 1); + m_CurInstructionScintilla->markerDeleteAll(FINISHED_MARKER); + m_CurInstructionScintilla->markerDeleteAll(FINISHED_MARKER + 1); + + m_CurInstructionScintilla->indicatorClearRange(0, m_CurInstructionScintilla->length()); + + m_CurInstructionScintilla = NULL; + } + for(sptr_t i = 0; i < m_DisassemblyView->lineCount(); i++) { if(QString::fromUtf8(m_DisassemblyView->getLine(i).trimmed()) @@ -1262,8 +1386,64 @@ void ShaderViewer::updateDebugging() ui->callstack->clear(); - for(const rdcstr &s : state.callstack) - ui->callstack->insertItem(0, s); + if(state.nextInstruction < m_Trace->lineInfo.size()) + { + LineColumnInfo &lineInfo = m_Trace->lineInfo[state.nextInstruction]; + + for(const rdcstr &s : lineInfo.callstack) + ui->callstack->insertItem(0, s); + + if(lineInfo.fileIndex >= 0 && lineInfo.fileIndex < m_FileScintillas.count()) + { + m_CurInstructionScintilla = m_FileScintillas[lineInfo.fileIndex]; + + for(sptr_t line = lineInfo.lineStart; line <= lineInfo.lineEnd; line++) + { + if(line == lineInfo.lineEnd) + m_CurInstructionScintilla->markerAdd(line - 1, done ? FINISHED_MARKER : CURRENT_MARKER); + + if(lineInfo.colStart == 0) + { + // with no column info, add a marker on the whole line + m_CurInstructionScintilla->markerAdd(line - 1, + done ? FINISHED_MARKER + 1 : CURRENT_MARKER + 1); + } + else + { + // otherwise add an indicator on the column range. + + // Start from the full position/length for this line + sptr_t pos = m_CurInstructionScintilla->positionFromLine(line - 1); + sptr_t len = m_CurInstructionScintilla->lineEndPosition(line - 1) - pos; + + // if we're on the last line of the range, restrict the length to end on the last column + if(line == lineInfo.lineEnd && lineInfo.colEnd != 0) + len = lineInfo.colEnd; + + // if we're on the start of the range (which may also be the last line above too), shift + // inwards towards the first column + if(line == lineInfo.lineStart) + { + pos += lineInfo.colStart - 1; + len -= lineInfo.colStart - 1; + } + + m_CurInstructionScintilla->setIndicatorCurrent(done ? FINISHED_INDICATOR + : CURRENT_INDICATOR); + m_CurInstructionScintilla->indicatorFillRange(pos, len); + } + } + + if(isSourceDebugging() || + ui->docking->areaOf(m_CurInstructionScintilla) != ui->docking->areaOf(m_DisassemblyFrame)) + ToolWindowManager::raiseToolWindow(m_CurInstructionScintilla); + + int pos = m_CurInstructionScintilla->positionFromLine(lineInfo.lineStart - 1); + m_CurInstructionScintilla->setSelection(pos, pos); + + ensureLineScrolled(m_CurInstructionScintilla, lineInfo.lineStart - 1); + } + } if(ui->constants->topLevelItemCount() == 0) { @@ -1707,7 +1887,7 @@ void ShaderViewer::ensureLineScrolled(ScintillaEdit *s, int line) int linesVisible = s->linesOnScreen(); if(s->isVisible() && (line < firstLine || line > (firstLine + linesVisible))) - s->scrollCaret(); + s->setFirstVisibleLine(qMax(0, line - linesVisible / 2)); } int ShaderViewer::CurrentStep() @@ -2276,6 +2456,11 @@ void ShaderViewer::hideVariableTooltip() m_TooltipVarIdx = -1; } +bool ShaderViewer::isSourceDebugging() +{ + return !m_DisassemblyFrame->isVisible(); +} + void ShaderViewer::on_findReplace_clicked() { if(m_FindReplace->isVisible()) diff --git a/qrenderdoc/Windows/ShaderViewer.h b/qrenderdoc/Windows/ShaderViewer.h index 58aa5fd00..ea4a78862 100644 --- a/qrenderdoc/Windows/ShaderViewer.h +++ b/qrenderdoc/Windows/ShaderViewer.h @@ -117,7 +117,7 @@ private slots: // manual slots void readonly_keyPressed(QKeyEvent *event); void editable_keyPressed(QKeyEvent *event); - void disassembly_contextMenu(const QPoint &pos); + void debug_contextMenu(const QPoint &pos); void disassembly_buttonReleased(QMouseEvent *event); void disassemble_typeChanged(int index); void watch_keyPress(QKeyEvent *event); @@ -162,6 +162,8 @@ private: void updateVariableTooltip(); void hideVariableTooltip(); + bool isSourceDebugging(); + VariableCategory m_TooltipVarCat = VariableCategory::Temporaries; int m_TooltipVarIdx = -1; int m_TooltipArrayIdx = -1; @@ -182,6 +184,9 @@ private: ScintillaEdit *m_FindResults = NULL; QList m_Scintillas; + ScintillaEdit *m_CurInstructionScintilla = NULL; + QList m_FileScintillas; + FindReplace *m_FindReplace; struct FindState @@ -211,6 +216,9 @@ private: static const int BREAKPOINT_MARKER = 2; static const int FINISHED_MARKER = 4; + static const int CURRENT_INDICATOR = 20; + static const int FINISHED_INDICATOR = 21; + static const int INDICATOR_FINDRESULT = 0; static const int INDICATOR_REGHIGHLIGHT = 1; diff --git a/renderdoc/api/replay/shader_types.h b/renderdoc/api/replay/shader_types.h index 324cae2c8..2fa224e0d 100644 --- a/renderdoc/api/replay/shader_types.h +++ b/renderdoc/api/replay/shader_types.h @@ -289,6 +289,61 @@ components are used). This list will have the same number of elements as :data:` }; DECLARE_REFLECTION_STRUCT(LocalVariableMapping); +DOCUMENT("Details the current region of code that an instruction maps to"); +struct LineColumnInfo +{ + DOCUMENT(""); + bool operator==(const LineColumnInfo &o) const + { + return fileIndex == o.fileIndex && lineStart == o.lineStart && lineEnd == o.lineEnd && + colStart == o.colStart && colEnd == o.colEnd && callstack == o.callstack; + } + bool operator<(const LineColumnInfo &o) const + { + if(!(fileIndex == o.fileIndex)) + return fileIndex < o.fileIndex; + if(!(lineStart == o.lineStart)) + return lineStart < o.lineStart; + if(!(lineEnd == o.lineEnd)) + return lineEnd < o.lineEnd; + if(!(colStart == o.colStart)) + return colStart < o.colStart; + if(!(colEnd == o.colEnd)) + return colEnd < o.colEnd; + if(!(callstack == o.callstack)) + return callstack < o.callstack; + return false; + } + + DOCUMENT("The current file, as an index into the list of files for this shader."); + int32_t fileIndex = -1; + + DOCUMENT("The line-number (starting from 1) of the start of the current section of code."); + uint32_t lineStart = 0; + + DOCUMENT("The line-number (starting from 1) of the end of the current section of code."); + uint32_t lineEnd = 0; + + DOCUMENT(R"(The column number (starting from 1) of the start of the code on the line specified by +:data:`lineStart`. If set to 0, no column information is available and the whole lines should be +treated as covering the code. +)"); + uint32_t colStart = 0; + + DOCUMENT(R"(The column number (starting from 1) of the end of the code on the line specified by +:data:`lineEnd`. If set to 0, no column information is available and the whole lines should be +treated as covering the code. +)"); + uint32_t colEnd = 0; + + DOCUMENT(R"(A ``list`` of ``str`` with each function call in the current callstack at this line. + +The oldest/outer function is first in the list, the newest/inner function is last. +)"); + rdcarray callstack; +}; +DECLARE_REFLECTION_STRUCT(LineColumnInfo); + DOCUMENT(R"(This stores the current state of shader debugging at one particular step in the shader, with all mutable variable contents. )"); @@ -298,8 +353,7 @@ struct ShaderDebugState bool operator==(const ShaderDebugState &o) const { return registers == o.registers && outputs == o.outputs && indexableTemps == o.indexableTemps && - locals == o.locals && nextInstruction == o.nextInstruction && flags == o.flags && - callstack == o.callstack; + locals == o.locals && nextInstruction == o.nextInstruction && flags == o.flags; } bool operator<(const ShaderDebugState &o) const { @@ -315,8 +369,6 @@ 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:`ShaderVariable`."); @@ -332,9 +384,6 @@ to which registers, and their type )"); rdcarray locals; - 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``. )"); @@ -368,6 +417,11 @@ instruction was executed DOCUMENT("A flag indicating whether this trace has locals information"); bool hasLocals = false; + + DOCUMENT(R"(A ``list`` of :class:`LineColumnInfo` detailing which source lines each instruction +corresponds to +)"); + rdcarray lineInfo; }; DECLARE_REFLECTION_STRUCT(ShaderDebugTrace); diff --git a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp index 252d4756e..db5a6786d 100644 --- a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp +++ b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp @@ -940,8 +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); - dxbc->m_DebugInfo->GetLocals(0, dxbc->GetInstruction(0).offset, initialState.locals); + if(dxbc->m_DebugInfo) + dxbc->m_DebugInfo->GetLocals(0, dxbc->GetInstruction(0).offset, initialState.locals); states.push_back((State)initialState); @@ -954,9 +954,9 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin initialState = initialState.GetNext(global, NULL); + if(dxbc->m_DebugInfo) { const ASMOperation &op = dxbc->GetInstruction((size_t)initialState.nextInstruction); - dxbc->m_DebugInfo->GetStack(initialState.nextInstruction, op.offset, initialState.callstack); dxbc->m_DebugInfo->GetLocals(initialState.nextInstruction, op.offset, initialState.locals); } @@ -971,7 +971,14 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin ret.states = states; - ret.hasLocals = dxbc->m_DebugInfo->HasLocals(); + ret.hasLocals = dxbc->m_DebugInfo && dxbc->m_DebugInfo->HasLocals(); + + ret.lineInfo.resize(dxbc->GetNumInstructions()); + for(size_t i = 0; i < dxbc->GetNumInstructions(); i++) + { + const ASMOperation &op = dxbc->GetInstruction(i); + dxbc->m_DebugInfo->GetLineInfo(i, op.offset, ret.lineInfo[i]); + } return ret; } @@ -1821,8 +1828,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); - dxbc->m_DebugInfo->GetLocals(0, dxbc->GetInstruction(0).offset, quad[destIdx].locals); + if(dxbc->m_DebugInfo) + dxbc->m_DebugInfo->GetLocals(0, dxbc->GetInstruction(0).offset, quad[destIdx].locals); states.push_back((State)quad[destIdx]); @@ -1860,9 +1867,9 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t { State &s = curquad[destIdx]; + if(dxbc->m_DebugInfo) { const ASMOperation &op = dxbc->GetInstruction((size_t)s.nextInstruction); - dxbc->m_DebugInfo->GetStack(s.nextInstruction, op.offset, s.callstack); dxbc->m_DebugInfo->GetLocals(s.nextInstruction, op.offset, s.locals); } @@ -1959,7 +1966,14 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t traces[destIdx].states = states; - traces[destIdx].hasLocals = dxbc->m_DebugInfo->HasLocals(); + traces[destIdx].hasLocals = dxbc->m_DebugInfo && dxbc->m_DebugInfo->HasLocals(); + + traces[destIdx].lineInfo.resize(dxbc->GetNumInstructions()); + for(size_t i = 0; i < dxbc->GetNumInstructions(); i++) + { + const ASMOperation &op = dxbc->GetInstruction(i); + dxbc->m_DebugInfo->GetLineInfo(i, op.offset, traces[destIdx].lineInfo[i]); + } return traces[destIdx]; } @@ -2018,8 +2032,8 @@ ShaderDebugTrace D3D11Replay::DebugThread(uint32_t eventId, const uint32_t group vector states; - dxbc->m_DebugInfo->GetStack(0, dxbc->GetInstruction(0).offset, initialState.callstack); - dxbc->m_DebugInfo->GetLocals(0, dxbc->GetInstruction(0).offset, initialState.locals); + if(dxbc->m_DebugInfo) + dxbc->m_DebugInfo->GetLocals(0, dxbc->GetInstruction(0).offset, initialState.locals); states.push_back((State)initialState); @@ -2030,9 +2044,9 @@ ShaderDebugTrace D3D11Replay::DebugThread(uint32_t eventId, const uint32_t group initialState = initialState.GetNext(global, NULL); + if(dxbc->m_DebugInfo) { const ASMOperation &op = dxbc->GetInstruction((size_t)initialState.nextInstruction); - dxbc->m_DebugInfo->GetStack(initialState.nextInstruction, op.offset, initialState.callstack); dxbc->m_DebugInfo->GetLocals(initialState.nextInstruction, op.offset, initialState.locals); } @@ -2047,7 +2061,14 @@ ShaderDebugTrace D3D11Replay::DebugThread(uint32_t eventId, const uint32_t group ret.states = states; - ret.hasLocals = dxbc->m_DebugInfo->HasLocals(); + ret.hasLocals = dxbc->m_DebugInfo && dxbc->m_DebugInfo->HasLocals(); + + ret.lineInfo.resize(dxbc->GetNumInstructions()); + for(size_t i = 0; i < dxbc->GetNumInstructions(); i++) + { + const ASMOperation &op = dxbc->GetInstruction(i); + dxbc->m_DebugInfo->GetLineInfo(i, op.offset, ret.lineInfo[i]); + } return ret; } diff --git a/renderdoc/driver/shaders/dxbc/dxbc_disassemble.cpp b/renderdoc/driver/shaders/dxbc/dxbc_disassemble.cpp index 22f1ccac6..27fa2f833 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_disassemble.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_disassemble.cpp @@ -665,9 +665,7 @@ void DXBCFile::MakeDisassemblyString() } } - int32_t prevFile = -1; - int32_t prevLine = -1; - std::string prevFunc; + LineColumnInfo prevLineInfo; size_t debugInst = 0; @@ -694,27 +692,33 @@ void DXBCFile::MakeDisassemblyString() if(m_DebugInfo) { - int32_t fileID = prevFile; - int32_t lineNum = prevLine; - std::string func = prevFunc; + LineColumnInfo lineInfo = prevLineInfo; - m_DebugInfo->GetLineInfo(debugInst, m_Instructions[i].offset, fileID, lineNum, func); + m_DebugInfo->GetLineInfo(debugInst, m_Instructions[i].offset, lineInfo); - if(fileID >= 0 && lineNum >= 0 && (fileID != prevFile || lineNum != prevLine)) + if(lineInfo.fileIndex >= 0 && lineInfo.lineStart >= 0 && + (lineInfo.fileIndex != prevLineInfo.fileIndex || + lineInfo.lineStart != prevLineInfo.lineStart)) { string line = ""; - if(fileID >= (int32_t)fileLines.size()) + if(lineInfo.fileIndex >= (int32_t)fileLines.size()) { line = "Unknown file"; } - else if(fileLines[fileID].empty()) + else if(fileLines[lineInfo.fileIndex].empty()) { line = ""; } else { - int32_t lineIdx = RDCMIN(lineNum, (int32_t)fileLines[fileID].size() - 1); - line = fileLines[fileID][lineIdx]; + std::vector &lines = fileLines[lineInfo.fileIndex]; + + int32_t lineIdx = RDCMIN(lineInfo.lineStart, (uint32_t)lines.size() - 1); + + // line numbers are 1-based but we want a 0-based index + if(lineIdx > 0) + lineIdx--; + line = lines[lineIdx]; } size_t startLine = line.find_first_not_of(" \t"); @@ -724,23 +728,27 @@ void DXBCFile::MakeDisassemblyString() m_Disassembly += "\n"; - if(((fileID != prevFile || func != prevFunc) && fileID < (int32_t)fileLines.size()) || + if(((lineInfo.fileIndex != prevLineInfo.fileIndex || + lineInfo.callstack.back() != prevLineInfo.callstack.back()) && + lineInfo.fileIndex < (int32_t)fileLines.size()) || line == "") { m_Disassembly += " "; // "0000: " for(int in = 0; in < indent; in++) m_Disassembly += " "; + std::string func = lineInfo.callstack.back(); + if(!func.empty()) { - m_Disassembly += - StringFormat::Fmt("%s:%d - %s()\n", m_DebugInfo->Files[fileID].first.c_str(), - lineNum + 1, func.c_str()); + m_Disassembly += StringFormat::Fmt("%s:%d - %s()\n", + m_DebugInfo->Files[lineInfo.fileIndex].first.c_str(), + lineInfo.lineStart, func.c_str()); } else { - m_Disassembly += - StringFormat::Fmt("%s:%d\n", m_DebugInfo->Files[fileID].first.c_str(), lineNum + 1); + m_Disassembly += StringFormat::Fmt( + "%s:%d\n", m_DebugInfo->Files[lineInfo.fileIndex].first.c_str(), lineInfo.lineStart); } } @@ -753,9 +761,7 @@ void DXBCFile::MakeDisassemblyString() } } - prevFile = fileID; - prevLine = lineNum; - prevFunc = func; + prevLineInfo = lineInfo; } char buf[64] = {0}; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_inspect.h b/renderdoc/driver/shaders/dxbc/dxbc_inspect.h index 1f8d65879..5abb66c86 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_inspect.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_inspect.h @@ -330,9 +330,7 @@ public: vector > Files; // - 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; + virtual void GetLineInfo(size_t instruction, uintptr_t offset, LineColumnInfo &lineInfo) const = 0; virtual bool HasLocals() const = 0; virtual void GetLocals(size_t instruction, uintptr_t offset, diff --git a/renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp b/renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp index 6e26384f8..e81c43f1d 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp @@ -98,8 +98,7 @@ SDBGChunk::SDBGChunk(void *data) m_HasDebugInfo = true; } -void SDBGChunk::GetLineInfo(size_t instruction, uintptr_t offset, int32_t &fileIdx, - int32_t &lineNum, std::string &func) const +void SDBGChunk::GetLineInfo(size_t instruction, uintptr_t offset, LineColumnInfo &lineInfo) const { if(instruction < m_Instructions.size()) { @@ -108,18 +107,16 @@ void SDBGChunk::GetLineInfo(size_t instruction, uintptr_t offset, int32_t &fileI { const SDBGSymbol &sym = m_SymbolTable[symID]; - fileIdx = sym.fileID; - lineNum = sym.lineNum - 1; - func = m_Entry; + lineInfo.fileIndex = sym.fileID; + lineInfo.lineStart = sym.lineNum; + lineInfo.lineEnd = sym.lineNum; + lineInfo.colStart = 0; + lineInfo.colEnd = 0; + lineInfo.callstack = {m_Entry}; } } } -void SDBGChunk::GetStack(size_t instruction, uintptr_t offset, rdcarray &stack) const -{ - stack = {"Stack not available"}; -} - bool SDBGChunk::HasLocals() const { return false; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_sdbg.h b/renderdoc/driver/shaders/dxbc/dxbc_sdbg.h index 78040ded9..12beb8706 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_sdbg.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_sdbg.h @@ -260,9 +260,7 @@ public: string GetEntryFunction() const { return m_Entry; } string GetShaderProfile() const { return m_Profile; } uint32_t GetShaderCompileFlags() const { return m_ShaderFlags; } - 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; + void GetLineInfo(size_t instruction, uintptr_t offset, LineColumnInfo &lineInfo) const; bool HasLocals() const; void GetLocals(size_t instruction, uintptr_t offset, rdcarray &locals) const; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp b/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp index 65cc2d620..eacab8af4 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp @@ -1257,7 +1257,6 @@ SPDBChunk::SPDBChunk(void *chunk) lineCol.fileIndex = fileIdx; lineCol.lineStart = line.linenumStart; lineCol.lineEnd = line.linenumStart + line.deltaLineEnd; - lineCol.statement = line.fStatement; if(hasColumns) { @@ -1313,7 +1312,7 @@ SPDBChunk::SPDBChunk(void *chunk) } for(auto it = m_Lines.begin(); it != m_Lines.end(); ++it) - it->second.stack.push_back(m_Functions[0].name); + it->second.callstack.push_back(m_Functions[0].name); SPDBLOG("Applying %zu inline sites", inlines.size()); @@ -1360,12 +1359,11 @@ SPDBChunk::SPDBChunk(void *chunk) loc.lineEnd + inlines[i].baseLineNum, loc.colEnd); it->second.fileIndex = fileIdx; - it->second.funcIndex = inlines[i].id; it->second.lineStart = loc.lineStart + inlines[i].baseLineNum; it->second.lineEnd = loc.lineEnd + inlines[i].baseLineNum; it->second.colStart = loc.colStart; it->second.colEnd = loc.colEnd; - it->second.stack.push_back(m_Functions[inlines[i].id].name); + it->second.callstack.push_back(m_Functions[inlines[i].id].name); nPatched++; } } @@ -1428,29 +1426,12 @@ SPDBChunk::SPDBChunk(void *chunk) m_HasDebugInfo = true; } -void SPDBChunk::GetLineInfo(size_t instruction, uintptr_t offset, int32_t &fileIdx, - int32_t &lineNum, std::string &func) const +void SPDBChunk::GetLineInfo(size_t instruction, uintptr_t offset, LineColumnInfo &lineInfo) const { auto it = m_Lines.lower_bound((uint32_t)offset); if(it != m_Lines.end() && (uintptr_t)it->first <= offset) - { - 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]; - } + lineInfo = it->second; } bool SPDBChunk::HasLocals() const diff --git a/renderdoc/driver/shaders/dxbc/dxbc_spdb.h b/renderdoc/driver/shaders/dxbc/dxbc_spdb.h index 3efd1235b..693bac88d 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_spdb.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_spdb.h @@ -205,17 +205,6 @@ struct FileChecksum uint8_t hashData[1]; }; -struct LineColumnInfo -{ - int32_t fileIndex = -1; - uint32_t funcIndex = 0; - uint32_t lineStart = 0, lineEnd = 0; - uint32_t colStart = 0, colEnd = 0; - bool statement = true; - - std::vector stack; -}; - struct InstructionLocation { bool statement = true; @@ -278,9 +267,7 @@ public: std::string GetEntryFunction() const { return m_Entry; } std::string GetShaderProfile() const { return m_Profile; } uint32_t GetShaderCompileFlags() const { return m_ShaderFlags; } - 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; + void GetLineInfo(size_t instruction, uintptr_t offset, LineColumnInfo &lineInfo) const; bool HasLocals() const; void GetLocals(size_t instruction, uintptr_t offset, rdcarray &locals) const; diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index a52639150..122559259 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -354,6 +354,19 @@ void DoSerialise(SerialiserType &ser, LocalVariableMapping &el) SIZE_CHECK(40); } +template +void DoSerialise(SerialiserType &ser, LineColumnInfo &el) +{ + SERIALISE_MEMBER(fileIndex); + SERIALISE_MEMBER(lineStart); + SERIALISE_MEMBER(lineEnd); + SERIALISE_MEMBER(colStart); + SERIALISE_MEMBER(colEnd); + SERIALISE_MEMBER(callstack); + + SIZE_CHECK(40); +} + template void DoSerialise(SerialiserType &ser, ShaderDebugState &el) { @@ -363,9 +376,8 @@ void DoSerialise(SerialiserType &ser, ShaderDebugState &el) SERIALISE_MEMBER(locals); SERIALISE_MEMBER(nextInstruction); SERIALISE_MEMBER(flags); - SERIALISE_MEMBER(callstack); - SIZE_CHECK(88); + SIZE_CHECK(72); } template @@ -375,8 +387,9 @@ void DoSerialise(SerialiserType &ser, ShaderDebugTrace &el) SERIALISE_MEMBER(constantBlocks); SERIALISE_MEMBER(states); SERIALISE_MEMBER(hasLocals); + SERIALISE_MEMBER(lineInfo); - SIZE_CHECK(56); + SIZE_CHECK(72); } template