From 661ee35f302f676211e58f49599da67d076f07d5 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 6 Feb 2020 11:06:28 +0000 Subject: [PATCH] Refactor ShaderDebugTrace to not return a single list of complete states * The ShaderDebugTrace now only sets up the initial state of an opaque ShaderDebugger handle. * This handle can then be passed to a new function - ContinueDebug - to iteratively return N more states. The number of states is implementation defined and may be a fixed number or it may run for a fixed time. * The states themselves no longer contain a complete snapshot of all variables, but instead only the changed variables for that iteration. The changes are stored as before and after value to make it easier to step forwards and backwards (only the ShaderDebugState is needed to move forward or backwards, you don't have to search back for the last set value of a variable to 'undo' a change). --- qrenderdoc/Code/Interface/QRDInterface.h | 4 +- qrenderdoc/Code/pyrenderdoc/renderdoc.i | 1 + qrenderdoc/Windows/BufferViewer.cpp | 2 +- .../D3D11PipelineStateViewer.cpp | 2 +- qrenderdoc/Windows/PixelHistoryView.cpp | 2 +- qrenderdoc/Windows/ShaderViewer.cpp | 537 +++--- qrenderdoc/Windows/ShaderViewer.h | 20 +- qrenderdoc/Windows/TextureViewer.cpp | 2 +- renderdoc/api/replay/renderdoc_replay.h | 14 + renderdoc/api/replay/shader_types.h | 101 +- renderdoc/core/image_viewer.cpp | 25 +- renderdoc/core/replay_proxy.cpp | 87 +- renderdoc/core/replay_proxy.h | 11 +- renderdoc/driver/d3d11/d3d11_debug.h | 6 - renderdoc/driver/d3d11/d3d11_replay.h | 14 +- renderdoc/driver/d3d11/d3d11_shaderdebug.cpp | 502 ++---- renderdoc/driver/d3d12/d3d12_debug.h | 6 - renderdoc/driver/d3d12/d3d12_replay.h | 14 +- renderdoc/driver/d3d12/d3d12_shaderdebug.cpp | 370 ++-- renderdoc/driver/gl/gl_replay.cpp | 24 +- renderdoc/driver/gl/gl_replay.h | 13 +- renderdoc/driver/shaders/dxbc/dxbc_debug.cpp | 1569 +++++++++-------- renderdoc/driver/shaders/dxbc/dxbc_debug.h | 73 +- renderdoc/driver/vulkan/vk_replay.cpp | 24 +- renderdoc/driver/vulkan/vk_replay.h | 14 +- renderdoc/replay/renderdoc_serialise.inl | 30 +- renderdoc/replay/replay_controller.cpp | 28 +- renderdoc/replay/replay_controller.h | 1 + renderdoc/replay/replay_driver.h | 13 +- 29 files changed, 1804 insertions(+), 1705 deletions(-) diff --git a/qrenderdoc/Code/Interface/QRDInterface.h b/qrenderdoc/Code/Interface/QRDInterface.h index 4aa928901..e748ab0dd 100644 --- a/qrenderdoc/Code/Interface/QRDInterface.h +++ b/qrenderdoc/Code/Interface/QRDInterface.h @@ -588,13 +588,13 @@ struct IShaderViewer :return: The current step. :rtype: ``int`` )"); - virtual int CurrentStep() = 0; + virtual uint32_t CurrentStep() = 0; DOCUMENT(R"(Sets the current step in the debugging. :param int step: The current step to jump to. )"); - virtual void SetCurrentStep(int step) = 0; + virtual void SetCurrentStep(uint32_t step) = 0; DOCUMENT(R"(Toggles a breakpoint at a given instruction. diff --git a/qrenderdoc/Code/pyrenderdoc/renderdoc.i b/qrenderdoc/Code/pyrenderdoc/renderdoc.i index f7ab806ba..a554e9f6c 100644 --- a/qrenderdoc/Code/pyrenderdoc/renderdoc.i +++ b/qrenderdoc/Code/pyrenderdoc/renderdoc.i @@ -320,6 +320,7 @@ TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderSampler) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderSourceFile) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderVariable) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderEncoding) +TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderVariableChange) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, DebugVariableReference) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, SourceVariableMapping) TEMPLATE_ARRAY_INSTANTIATE(rdcarray, SigParameter) diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index c58389cdc..3fa36d0c3 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -3810,7 +3810,7 @@ void BufferViewer::debugVertex() trace = r->DebugVertex(vertid, m_Config.curInstance, index, m_Ctx.CurDrawcall()->instanceOffset, m_Ctx.CurDrawcall()->vertexOffset); - if(trace->states.isEmpty()) + if(trace->debugger == NULL) { r->FreeTrace(trace); trace = NULL; diff --git a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp index e8e4d43f2..7e7cbd09e 100644 --- a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp @@ -3138,7 +3138,7 @@ void D3D11PipelineStateViewer::on_debugThread_clicked() m_Ctx.Replay().AsyncInvoke([&trace, &done, thread](IReplayController *r) { trace = r->DebugThread(thread.g, thread.t); - if(trace->states.isEmpty()) + if(trace->debugger == NULL) { r->FreeTrace(trace); trace = NULL; diff --git a/qrenderdoc/Windows/PixelHistoryView.cpp b/qrenderdoc/Windows/PixelHistoryView.cpp index 3afe5246d..76f9263a3 100644 --- a/qrenderdoc/Windows/PixelHistoryView.cpp +++ b/qrenderdoc/Windows/PixelHistoryView.cpp @@ -720,7 +720,7 @@ void PixelHistoryView::startDebug(EventTag tag) trace = r->DebugPixel((uint32_t)m_Pixel.x(), (uint32_t)m_Pixel.y(), m_Display.subresource.sample, tag.primitive); - if(trace->states.isEmpty()) + if(trace->debugger == NULL) { r->FreeTrace(trace); trace = NULL; diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index 51f7a18af..8b7ece6bd 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -45,16 +45,12 @@ namespace struct VariableTag { VariableTag() : offset(0), globalSourceVar(-1), localSourceVar(-1) {} - VariableTag(QString name, uint32_t offs, int32_t globalVar, int32_t localVar) - : fullname(name), offset(offs), globalSourceVar(globalVar), localSourceVar(localVar) + VariableTag(rdcstr name, uint32_t offs, int32_t globalVar, int32_t localVar) + : offset(offs), globalSourceVar(globalVar), localSourceVar(localVar) { + debugVar.name = name; } - VariableTag(QString name, DebugVariableReference var) : fullname(name), offset(0), debugVar(var) - { - } - - QString fullname; - + VariableTag(DebugVariableReference var) : offset(0), debugVar(var) {} uint32_t offset; int32_t globalSourceVar; int32_t localSourceVar; @@ -392,27 +388,6 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR m_DisassemblyView->setReadOnly(false); SetTextAndUpdateMargin0(m_DisassemblyView, disasm); m_DisassemblyView->setReadOnly(true); - - bool preferSourceDebug = false; - - for(const ShaderCompileFlag &flag : m_ShaderDetails->debugInfo.compileFlags.flags) - { - if(flag.name == "preferSourceDebug") - { - preferSourceDebug = true; - break; - } - } - - updateDebugging(); - - // we do updateDebugging() again because the first call finds the scintilla for the current - // source file, the second time jumps to it. - if(preferSourceDebug) - { - gotoSourceDebugging(); - updateDebugging(); - } }); }); } @@ -641,7 +616,47 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR ui->debugVars->installEventFilter(this); ui->watch->installEventFilter(this); - SetCurrentStep(0); + m_Ctx.Replay().AsyncInvoke([this](IReplayController *r) { + rdcarray states = r->ContinueDebug(m_Trace->debugger); + + bool finished = false; + do + { + rdcarray nextStates = r->ContinueDebug(m_Trace->debugger); + states.append(nextStates); + finished = nextStates.empty(); + } while(!finished); + + m_States = states; + + for(const ShaderVariableChange &c : GetCurrentState().changes) + m_Variables.push_back(c.after); + + GUIInvoke::call(this, [this]() { + bool preferSourceDebug = false; + + for(const ShaderCompileFlag &flag : m_ShaderDetails->debugInfo.compileFlags.flags) + { + if(flag.name == "preferSourceDebug") + { + preferSourceDebug = true; + break; + } + } + + updateDebugState(); + + // we do updateDebugging() again because the first call finds the scintilla for the current + // source file, the second time jumps to it. + if(preferSourceDebug) + { + gotoSourceDebugging(); + updateDebugState(); + } + }); + }); + + m_CurrentStateIdx = 0; QObject::connect(ui->watch, &RDTableWidget::keyPress, this, &ShaderViewer::watch_keyPress); @@ -873,7 +888,7 @@ void ShaderViewer::OnCaptureClosed() void ShaderViewer::OnEventChanged(uint32_t eventId) { - updateDebugging(); + updateDebugState(); updateWindowTitle(); } @@ -1000,7 +1015,7 @@ void ShaderViewer::debug_contextMenu(const QPoint &pos) else gotoDisassemblyDebugging(); - updateDebugging(); + updateDebugState(); }); QAction intDisplay(tr("Integer register display"), this); @@ -1129,7 +1144,7 @@ void ShaderViewer::variables_contextMenu(const QPoint &pos) QObject::connect(&addWatch, &QAction::triggered, [this, tree] { if(tree == ui->sourceVars) - AddWatch(tree->selectedItem()->tag().value().fullname); + AddWatch(tree->selectedItem()->tag().value().debugVar.name); else AddWatch(tree->selectedItem()->text(0)); }); @@ -1304,7 +1319,7 @@ void ShaderViewer::on_watch_itemChanged(QTableWidgetItem *item) recurse = false; - updateDebugging(); + updateDebugState(); } bool ShaderViewer::stepBack() @@ -1312,39 +1327,50 @@ bool ShaderViewer::stepBack() if(!m_Trace) return false; - if(CurrentStep() == 0) + if(IsFirstState()) return false; if(isSourceDebugging()) { - const ShaderDebugState &oldstate = m_Trace->states[CurrentStep()]; + LineColumnInfo oldLine = m_Trace->lineInfo[GetCurrentState().nextInstruction]; - LineColumnInfo oldLine = - m_Trace->lineInfo[qMin(m_Trace->lineInfo.size() - 1, (size_t)oldstate.nextInstruction)]; - - while(CurrentStep() < m_Trace->states.count()) + // first step to the next instruction in a backwards direction that's on a different line from + // the current one + do { - m_CurrentStep--; + applyBackwardsChange(); - const ShaderDebugState &state = m_Trace->states[m_CurrentStep]; - - if(m_Breakpoints.contains((int)state.nextInstruction)) + if(m_Breakpoints.contains((int)GetCurrentState().nextInstruction)) break; - if(m_CurrentStep == 0) + if(IsFirstState()) break; - if(m_Trace->lineInfo[state.nextInstruction].sourceEqual(oldLine)) + if(m_Trace->lineInfo[GetCurrentState().nextInstruction].sourceEqual(oldLine)) continue; break; + } while(true); + + oldLine = m_Trace->lineInfo[GetCurrentState().nextInstruction]; + + // now since a line can have multiple instructions, keep stepping (looking forward) until we + // reach the first instruction with an identical line info + while(!IsFirstState() && + m_Trace->lineInfo[GetPreviousState().nextInstruction].sourceEqual(oldLine)) + { + applyBackwardsChange(); + + if(m_Breakpoints.contains((int)GetCurrentState().nextInstruction)) + break; } - SetCurrentStep(CurrentStep()); + updateDebugState(); } else { - SetCurrentStep(CurrentStep() - 1); + applyBackwardsChange(); + updateDebugState(); } return true; @@ -1355,38 +1381,35 @@ bool ShaderViewer::stepNext() if(!m_Trace) return false; - if(CurrentStep() + 1 >= m_Trace->states.count()) + if(IsLastState()) return false; if(isSourceDebugging()) { - const ShaderDebugState &oldstate = m_Trace->states[CurrentStep()]; + LineColumnInfo oldLine = m_Trace->lineInfo[GetCurrentState().nextInstruction]; - LineColumnInfo oldLine = m_Trace->lineInfo[oldstate.nextInstruction]; - - while(CurrentStep() < m_Trace->states.count()) + do { - m_CurrentStep++; + applyForwardsChange(); - const ShaderDebugState &state = m_Trace->states[m_CurrentStep]; - - if(m_Breakpoints.contains((int)state.nextInstruction)) + if(m_Breakpoints.contains((int)GetCurrentState().nextInstruction)) break; - if(m_CurrentStep + 1 >= m_Trace->states.count()) + if(IsLastState()) break; - if(m_Trace->lineInfo[state.nextInstruction].sourceEqual(oldLine)) + if(m_Trace->lineInfo[GetCurrentState().nextInstruction].sourceEqual(oldLine)) continue; break; - } + } while(true); - SetCurrentStep(CurrentStep()); + updateDebugState(); } else { - SetCurrentStep(CurrentStep() + 1); + applyForwardsChange(); + updateDebugState(); } return true; @@ -1450,6 +1473,40 @@ int ShaderViewer::instructionForDisassemblyLine(sptr_t line) return -1; } +bool ShaderViewer::IsFirstState() const +{ + return m_CurrentStateIdx == 0; +} + +bool ShaderViewer::IsLastState() const +{ + return m_CurrentStateIdx == m_States.size() - 1; +} + +const ShaderDebugState &ShaderViewer::GetPreviousState() const +{ + if(m_CurrentStateIdx > 0) + return m_States[m_CurrentStateIdx - 1]; + + return m_States.front(); +} + +const ShaderDebugState &ShaderViewer::GetCurrentState() const +{ + if(m_CurrentStateIdx < m_States.size()) + return m_States[m_CurrentStateIdx]; + + return m_States.back(); +} + +const ShaderDebugState &ShaderViewer::GetNextState() const +{ + if(m_CurrentStateIdx + 1 < m_States.size()) + return m_States[m_CurrentStateIdx + 1]; + + return m_States.back(); +} + void ShaderViewer::runToSample() { runTo({}, true, ShaderEvents::SampleLoadGather); @@ -1475,33 +1532,125 @@ void ShaderViewer::runTo(QVector runToInstruction, bool forward, ShaderE if(!m_Trace) return; - int step = CurrentStep(); - - int inc = forward ? 1 : -1; - bool firstStep = true; - while(step < m_Trace->states.count()) + // this is effectively infinite as we break out before moving to next/previous state if that would + // be first/last + while((forward && !IsLastState()) || (!forward && !IsFirstState())) { - if(runToInstruction.contains(m_Trace->states[step].nextInstruction)) + // break immediately even on the very first step if it's the one we want to go to + if(runToInstruction.contains(GetCurrentState().nextInstruction)) break; - if(!firstStep && (step + inc >= 0) && (step + inc < m_Trace->states.count()) && - (m_Trace->states[step + inc].flags & condition)) + // after the first step, break on condition + if(!firstStep && (GetCurrentState().flags & condition)) break; - if(!firstStep && m_Breakpoints.contains((int)m_Trace->states[step].nextInstruction)) + // or breakpoint + if(!firstStep && m_Breakpoints.contains((int)GetCurrentState().nextInstruction)) break; firstStep = false; - if(step + inc < 0 || step + inc >= m_Trace->states.count()) - break; - - step += inc; + if(forward) + { + if(IsLastState()) + break; + applyForwardsChange(); + } + else + { + if(IsFirstState()) + break; + applyBackwardsChange(); + } } - SetCurrentStep(step); + updateDebugState(); +} + +void ShaderViewer::applyBackwardsChange() +{ + if(!IsFirstState()) + { + for(const ShaderVariableChange &c : GetCurrentState().changes) + { + // if the before name is empty, this is a variable that came into scope/was created + if(c.before.name.empty()) + { + // delete the matching variable (should only be one) + for(size_t i = 0; i < m_Variables.size(); i++) + { + if(c.after.name == m_Variables[i].name) + { + m_Variables.erase(i); + break; + } + } + } + else + { + ShaderVariable *v = NULL; + for(size_t i = 0; i < m_Variables.size(); i++) + { + if(c.before.name == m_Variables[i].name) + { + v = &m_Variables[i]; + break; + } + } + + if(v) + *v = c.before; + else + m_Variables.push_back(c.before); + } + } + + m_CurrentStateIdx--; + } +} + +void ShaderViewer::applyForwardsChange() +{ + if(!IsLastState()) + { + m_CurrentStateIdx++; + + for(const ShaderVariableChange &c : GetCurrentState().changes) + { + // if the after name is empty, this is a variable going out of scope/being deleted + if(c.after.name.empty()) + { + // delete the matching variable (should only be one) + for(size_t i = 0; i < m_Variables.size(); i++) + { + if(c.before.name == m_Variables[i].name) + { + m_Variables.erase(i); + break; + } + } + } + else + { + ShaderVariable *v = NULL; + for(size_t i = 0; i < m_Variables.size(); i++) + { + if(c.after.name == m_Variables[i].name) + { + v = &m_Variables[i]; + break; + } + } + + if(v) + *v = c.after; + else + m_Variables.push_back(c.after); + } + } + } } QString ShaderViewer::stringRep(const ShaderVariable &var) @@ -1672,7 +1821,7 @@ RDTreeWidgetItem *ShaderViewer::findVarInTree(RDTreeWidgetItem *root, QString na { if(fullmatch) { - if(root->tag().value().fullname == name) + if(root->tag().value().debugVar.name == rdcstr(name)) return root; } else @@ -1760,7 +1909,7 @@ bool ShaderViewer::getVar(RDTreeWidgetItem *item, ShaderVariable *var, QString * if(reg) { *var = *reg; - var->name = tag.fullname; + var->name = tag.debugVar.name; if(regNames) *regNames = reg->name; @@ -1780,9 +1929,8 @@ bool ShaderViewer::getVar(RDTreeWidgetItem *item, ShaderVariable *var, QString * if(tag.globalSourceVar >= 0 && tag.globalSourceVar < m_Trace->sourceVars.count()) mapping = m_Trace->sourceVars[tag.globalSourceVar]; - else if(tag.localSourceVar >= 0 && - tag.localSourceVar < m_Trace->states[m_CurrentStep].sourceVars.count()) - mapping = m_Trace->states[m_CurrentStep].sourceVars[tag.localSourceVar]; + else if(tag.localSourceVar >= 0 && tag.localSourceVar < GetCurrentState().sourceVars.count()) + mapping = GetCurrentState().sourceVars[tag.localSourceVar]; else qCritical() << "Couldn't find expected source variable!" << tag.globalSourceVar << tag.localSourceVar; @@ -1801,7 +1949,7 @@ bool ShaderViewer::getVar(RDTreeWidgetItem *item, ShaderVariable *var, QString * return true; ShaderVariable &ret = *var; - ret.name = tag.fullname; + ret.name = tag.debugVar.name; ret.rowMajor = true; ret.rows = mapping.rows; ret.columns = mapping.columns; @@ -1862,7 +2010,7 @@ void ShaderViewer::highlightMatchingVars(RDTreeWidgetItem *root, const QString v for(int i = 0; i < root->childCount(); i++) { RDTreeWidgetItem *item = root->child(i); - if(item->tag().value().fullname == varName) + if(item->tag().value().debugVar.name == rdcstr(varName)) item->setBackgroundColor(highlightColor); else item->setBackground(QBrush()); @@ -1871,9 +2019,9 @@ void ShaderViewer::highlightMatchingVars(RDTreeWidgetItem *root, const QString v } } -void ShaderViewer::updateDebugging() +void ShaderViewer::updateDebugState() { - if(!m_Trace || m_CurrentStep < 0 || m_CurrentStep >= m_Trace->states.count()) + if(!m_Trace || m_States.empty()) return; if(ui->debugToggle->isEnabled()) @@ -1884,16 +2032,9 @@ void ShaderViewer::updateDebugging() ui->debugToggle->setText(tr("Debug in Source")); } - const ShaderDebugState &state = m_Trace->states[m_CurrentStep]; + const ShaderDebugState &state = GetCurrentState(); - uint32_t nextInst = state.nextInstruction; - bool done = false; - - if(m_CurrentStep == m_Trace->states.count() - 1) - { - nextInst--; - done = true; - } + const bool done = IsLastState(); // add current instruction marker m_DisassemblyView->markerDeleteAll(CURRENT_MARKER); @@ -2027,7 +2168,7 @@ void ShaderViewer::updateDebugging() RDTreeWidgetItem *node = new RDTreeWidgetItem({name, name, lit("Constant"), QString()}); node->setTag(QVariant::fromValue( - VariableTag(name, DebugVariableReference(DebugVariableType::Constant, name)))); + VariableTag(DebugVariableReference(DebugVariableType::Constant, name)))); for(int j = 0; j < m_Trace->constantBlocks[i].members.count(); j++) { @@ -2040,7 +2181,7 @@ void ShaderViewer::updateDebugging() RDTreeWidgetItem *child = new RDTreeWidgetItem( {name, name, lit("Constant"), stringRep(m_Trace->constantBlocks[i].members[j])}); child->setTag(QVariant::fromValue( - VariableTag(name, DebugVariableReference(DebugVariableType::Constant, name)))); + VariableTag(DebugVariableReference(DebugVariableType::Constant, name)))); node->addChild(child); } } @@ -2067,7 +2208,7 @@ void ShaderViewer::updateDebugging() RDTreeWidgetItem *node = new RDTreeWidgetItem({input.name, input.name, lit("Input"), stringRep(input)}); node->setTag(QVariant::fromValue( - VariableTag(input.name, DebugVariableReference(DebugVariableType::Input, input.name)))); + VariableTag(DebugVariableReference(DebugVariableType::Input, input.name)))); ui->constants->addTopLevelItem(node); } @@ -2104,8 +2245,8 @@ void ShaderViewer::updateDebugging() RDTreeWidgetItem *node = new RDTreeWidgetItem({m_ShaderDetails->readOnlyResources[i].name, ro.name, lit("Resource"), ToQStr(roBind.resources[0].resourceId)}); - node->setTag(QVariant::fromValue(VariableTag( - ro.name, DebugVariableReference(DebugVariableType::ReadOnlyResource, ro.name)))); + node->setTag(QVariant::fromValue( + VariableTag(DebugVariableReference(DebugVariableType::ReadOnlyResource, ro.name)))); if(node) ui->constants->addTopLevelItem(node); } @@ -2114,8 +2255,8 @@ void ShaderViewer::updateDebugging() RDTreeWidgetItem *node = new RDTreeWidgetItem({m_ShaderDetails->readOnlyResources[i].name, ro.name, QFormatStr("[%1]").arg(bind.arraySize), QString()}); - node->setTag(QVariant::fromValue(VariableTag( - ro.name, DebugVariableReference(DebugVariableType::ReadOnlyResource, ro.name)))); + node->setTag(QVariant::fromValue( + VariableTag(DebugVariableReference(DebugVariableType::ReadOnlyResource, ro.name)))); for(uint32_t a = 0; a < bind.arraySize; a++) { @@ -2124,8 +2265,8 @@ void ShaderViewer::updateDebugging() QFormatStr("%1[%2]").arg(m_ShaderDetails->readOnlyResources[i].name).arg(a), childName, lit("Resource"), ToQStr(roBind.resources[a].resourceId), }); - child->setTag(QVariant::fromValue(VariableTag( - childName, DebugVariableReference(DebugVariableType::ReadOnlyResource, childName)))); + child->setTag(QVariant::fromValue( + VariableTag(DebugVariableReference(DebugVariableType::ReadOnlyResource, childName)))); node->addChild(child); } @@ -2164,8 +2305,8 @@ void ShaderViewer::updateDebugging() RDTreeWidgetItem *node = new RDTreeWidgetItem({m_ShaderDetails->readWriteResources[i].name, rw.name, lit("Resource"), ToQStr(rwBind.resources[0].resourceId)}); - node->setTag(QVariant::fromValue(VariableTag( - rw.name, DebugVariableReference(DebugVariableType::ReadOnlyResource, rw.name)))); + node->setTag(QVariant::fromValue( + VariableTag(DebugVariableReference(DebugVariableType::ReadOnlyResource, rw.name)))); if(node) ui->constants->addTopLevelItem(node); } @@ -2174,8 +2315,8 @@ void ShaderViewer::updateDebugging() RDTreeWidgetItem *node = new RDTreeWidgetItem({m_ShaderDetails->readWriteResources[i].name, rw.name, QFormatStr("[%1]").arg(bind.arraySize), QString()}); - node->setTag(QVariant::fromValue(VariableTag( - rw.name, DebugVariableReference(DebugVariableType::ReadOnlyResource, rw.name)))); + node->setTag(QVariant::fromValue( + VariableTag(DebugVariableReference(DebugVariableType::ReadOnlyResource, rw.name)))); for(uint32_t a = 0; a < bind.arraySize; a++) { @@ -2184,8 +2325,8 @@ void ShaderViewer::updateDebugging() QFormatStr("%1[%2]").arg(m_ShaderDetails->readWriteResources[i].name).arg(a), childName, lit("RW Resource"), ToQStr(rwBind.resources[a].resourceId), }); - child->setTag(QVariant::fromValue(VariableTag( - childName, DebugVariableReference(DebugVariableType::ReadWriteResource, childName)))); + child->setTag(QVariant::fromValue( + VariableTag(DebugVariableReference(DebugVariableType::ReadWriteResource, childName)))); node->addChild(child); } @@ -2199,6 +2340,8 @@ void ShaderViewer::updateDebugging() RDTreeViewExpansionState expansion; ui->sourceVars->saveExpansion(expansion, 0); + ui->sourceVars->beginUpdate(); + ui->sourceVars->clear(); RDTreeWidgetItem fakeroot; @@ -2214,13 +2357,13 @@ void ShaderViewer::updateDebugging() bool modified = false; - if(!l.variables.empty() && l.variables[0].type == DebugVariableType::Variable) + if(l.variables[0].type == DebugVariableType::Variable) { - for(const DebugVariableReference &r : l.variables) + for(const DebugVariableReference &v : l.variables) { - for(const DebugVariableReference &mr : state.modified) + for(const ShaderVariableChange &c : GetCurrentState().changes) { - if(mr.name == r.name) + if(c.before.name == v.name || c.after.name == v.name) { modified = true; break; @@ -2246,74 +2389,40 @@ void ShaderViewer::updateDebugging() while(fakeroot.childCount() > 0) ui->sourceVars->addTopLevelItem(fakeroot.takeChild(0)); + ui->sourceVars->endUpdate(); + ui->sourceVars->applyExpansion(expansion, 0); } - if(ui->debugVars->topLevelItemCount() == 0) { - for(int i = 0; i < state.variables.count(); i++) + RDTreeViewExpansionState expansion; + ui->debugVars->saveExpansion(expansion, 0); + + ui->debugVars->beginUpdate(); + + ui->debugVars->clear(); + + for(int i = 0; i < m_Variables.count(); i++) { - RDTreeWidgetItem *node = new RDTreeWidgetItem({state.variables[i].name, QString()}); - node->setTag(QVariant::fromValue(VariableTag( - state.variables[i].name, - DebugVariableReference(DebugVariableType::Variable, state.variables[i].name)))); - for(int t = 0; t < state.variables[i].members.count(); t++) + bool modified = false; + + for(const ShaderVariableChange &c : GetCurrentState().changes) { - RDTreeWidgetItem *child = - new RDTreeWidgetItem({state.variables[i].members[t].name, QString()}); - child->setTag(QVariant::fromValue(VariableTag( - state.variables[i].members[t].name, - DebugVariableReference(DebugVariableType::Variable, state.variables[i].members[t].name)))); - node->addChild(child); + if(c.before.name == m_Variables[i].name || c.after.name == m_Variables[i].name) + { + modified = true; + break; + } } - ui->debugVars->addTopLevelItem(node); + + ui->debugVars->addTopLevelItem(makeDebugVariableNode(m_Variables[i], "", modified)); } + + ui->debugVars->endUpdate(); + + ui->sourceVars->applyExpansion(expansion, 0); } - ui->debugVars->beginUpdate(); - - for(int i = 0; i < state.variables.count(); i++) - { - RDTreeWidgetItem *node = ui->debugVars->topLevelItem(i); - - bool modified = false; - - for(const DebugVariableReference &mr : state.modified) - { - if(mr.name == state.variables[i].name) - { - modified = true; - break; - } - } - - if(state.variables[i].members.empty()) - { - node->setText(1, stringRep(state.variables[i])); - - if(modified) - node->setForegroundColor(QColor(Qt::red)); - else - node->setForeground(QBrush()); - } - else - { - for(int t = 0; t < state.variables[i].members.count(); t++) - { - RDTreeWidgetItem *child = node->child(t); - - if(modified) - child->setForegroundColor(QColor(Qt::red)); - else - child->setForeground(QBrush()); - - child->setText(1, stringRep(state.variables[i].members[t])); - } - } - } - - ui->debugVars->endUpdate(); - updateWatchVariables(); ui->debugVars->resizeColumnToContents(0); @@ -2324,8 +2433,6 @@ void ShaderViewer::updateDebugging() void ShaderViewer::updateWatchVariables() { - const ShaderDebugState &state = m_Trace->states[m_CurrentStep]; - ui->watch->setUpdatesEnabled(false); for(int i = 0; i < ui->watch->rowCount() - 1; i++) @@ -2554,8 +2661,6 @@ void ShaderViewer::updateWatchVariables() RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMapping &l, int globalVarIdx, int localVarIdx) { - const ShaderDebugState &state = m_Trace->states[m_CurrentStep]; - const QString xyzw = lit("xyzw"); QString localName = l.name; @@ -2712,6 +2817,27 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi return node; } +RDTreeWidgetItem *ShaderViewer::makeDebugVariableNode(const ShaderVariable &v, rdcstr prefix, + bool modified) +{ + rdcstr basename = prefix + v.name; + RDTreeWidgetItem *node = new RDTreeWidgetItem({basename, stringRep(v)}); + node->setTag(QVariant::fromValue( + VariableTag(DebugVariableReference(DebugVariableType::Variable, basename)))); + for(const ShaderVariable &m : v.members) + { + rdcstr childprefix = basename + "."; + if(m.name.beginsWith(basename + "[")) + childprefix = prefix; + node->addChild(makeDebugVariableNode(m, childprefix, modified)); + } + + if(modified) + node->setForegroundColor(QColor(Qt::red)); + + return node; +} + const ShaderVariable *ShaderViewer::GetRegisterVariable(const DebugVariableReference &r) { if(r.type == DebugVariableType::Input) @@ -2756,17 +2882,15 @@ const ShaderVariable *ShaderViewer::GetRegisterVariable(const DebugVariableRefer } else if(r.type == DebugVariableType::Variable) { - const ShaderDebugState &state = m_Trace->states[m_CurrentStep]; - - for(int i = 0; i < state.variables.count(); i++) + for(int i = 0; i < m_Variables.count(); i++) { - if(state.variables[i].name == r.name) - return &state.variables[i]; + if(m_Variables[i].name == r.name) + return &m_Variables[i]; - for(int j = 0; j < state.variables[i].members.count(); j++) + for(int j = 0; j < m_Variables[i].members.count(); j++) { - if(state.variables[i].members[j].name == r.name) - return &state.variables[i].members[j]; + if(m_Variables[i].members[j].name == r.name) + return &m_Variables[i].members[j]; } } @@ -2785,19 +2909,34 @@ void ShaderViewer::ensureLineScrolled(ScintillaEdit *s, int line) s->setFirstVisibleLine(qMax(0, line - linesVisible / 2)); } -int ShaderViewer::CurrentStep() +uint32_t ShaderViewer::CurrentStep() { - return m_CurrentStep; + return (uint32_t)m_CurrentStateIdx; } -void ShaderViewer::SetCurrentStep(int step) +void ShaderViewer::SetCurrentStep(uint32_t step) { - if(m_Trace && !m_Trace->states.empty()) - m_CurrentStep = qBound(0, step, m_Trace->states.count() - 1); - else - m_CurrentStep = 0; + while(GetCurrentState().stepIndex != step) + { + if(GetCurrentState().stepIndex < step) + { + // move forward if possible + if(!IsLastState()) + applyForwardsChange(); + else + break; + } + else + { + // move backward if possible + if(!IsFirstState()) + applyBackwardsChange(); + else + break; + } + } - updateDebugging(); + updateDebugState(); } void ShaderViewer::ToggleBreakpoint(int instruction) @@ -3361,7 +3500,7 @@ bool ShaderViewer::eventFilter(QObject *watched, QEvent *event) if(item) { VariableTag tag = item->tag().value(); - showVariableTooltip(tag.fullname); + showVariableTooltip(tag.debugVar.name); } } @@ -3373,7 +3512,7 @@ bool ShaderViewer::eventFilter(QObject *watched, QEvent *event) { item = table->item(item->row(), 2); VariableTag tag = item->data(Qt::UserRole).value(); - showVariableTooltip(tag.fullname); + showVariableTooltip(tag.debugVar.name); } } } @@ -3388,7 +3527,7 @@ bool ShaderViewer::eventFilter(QObject *watched, QEvent *event) void ShaderViewer::disasm_tooltipShow(int x, int y) { // do nothing if there's no trace - if(!m_Trace || m_CurrentStep < 0 || m_CurrentStep >= m_Trace->states.count()) + if(!m_Trace || m_States.empty()) return; ScintillaEdit *sc = qobject_cast(QObject::sender()); @@ -3442,11 +3581,9 @@ void ShaderViewer::showVariableTooltip(QString name) void ShaderViewer::updateVariableTooltip() { - if(!m_Trace || m_CurrentStep < 0 || m_CurrentStep >= m_Trace->states.count()) + if(!m_Trace || m_States.empty()) return; - const ShaderDebugState &state = m_Trace->states[m_CurrentStep]; - ShaderVariable var; if(!findVar(m_TooltipName, &var)) @@ -3803,7 +3940,7 @@ void ShaderViewer::on_intView_clicked() ui->intView->setChecked(true); ui->floatView->setChecked(false); - updateDebugging(); + updateDebugState(); } void ShaderViewer::on_floatView_clicked() @@ -3811,7 +3948,7 @@ void ShaderViewer::on_floatView_clicked() ui->floatView->setChecked(true); ui->intView->setChecked(false); - updateDebugging(); + updateDebugState(); } void ShaderViewer::on_debugToggle_clicked() @@ -3821,7 +3958,7 @@ void ShaderViewer::on_debugToggle_clicked() else gotoSourceDebugging(); - updateDebugging(); + updateDebugState(); } ScintillaEdit *ShaderViewer::currentScintilla() diff --git a/qrenderdoc/Windows/ShaderViewer.h b/qrenderdoc/Windows/ShaderViewer.h index c9e347862..19dc9f280 100644 --- a/qrenderdoc/Windows/ShaderViewer.h +++ b/qrenderdoc/Windows/ShaderViewer.h @@ -94,8 +94,8 @@ public: // IShaderViewer virtual QWidget *Widget() override { return this; } - virtual int CurrentStep() override; - virtual void SetCurrentStep(int step) override; + virtual uint32_t CurrentStep() override; + virtual void SetCurrentStep(uint32_t step) override; virtual void ToggleBreakpoint(int instruction = -1) override; @@ -232,7 +232,9 @@ private: CloseCallback m_CloseCallback; ShaderDebugTrace *m_Trace = NULL; - int m_CurrentStep; + rdcarray m_States; + size_t m_CurrentStateIdx = 0; + rdcarray m_Variables; QList m_Breakpoints; static const int CURRENT_MARKER = 0; @@ -262,11 +264,18 @@ private: int instructionForDisassemblyLine(sptr_t line); - void updateDebugging(); + bool IsFirstState() const; + bool IsLastState() const; + const ShaderDebugState &GetPreviousState() const; + const ShaderDebugState &GetCurrentState() const; + const ShaderDebugState &GetNextState() const; + + void updateDebugState(); void updateWatchVariables(); RDTreeWidgetItem *makeSourceVariableNode(const SourceVariableMapping &l, int globalVarIdx, int localVarIdx); + RDTreeWidgetItem *makeDebugVariableNode(const ShaderVariable &v, rdcstr prefix, bool modified); const ShaderVariable *GetRegisterVariable(const DebugVariableReference &r); @@ -277,6 +286,9 @@ private: void runTo(QVector runToInstructions, bool forward, ShaderEvents condition = ShaderEvents::NoEvent); + void applyBackwardsChange(); + void applyForwardsChange(); + QString stringRep(const ShaderVariable &var); void combineStructures(RDTreeWidgetItem *root, int skipPrefixLength = 0); RDTreeWidgetItem *findVarInTree(RDTreeWidgetItem *root, QString name, bool fullmatch, int maxDepth); diff --git a/qrenderdoc/Windows/TextureViewer.cpp b/qrenderdoc/Windows/TextureViewer.cpp index b136d72d2..aa9d6312c 100644 --- a/qrenderdoc/Windows/TextureViewer.cpp +++ b/qrenderdoc/Windows/TextureViewer.cpp @@ -3758,7 +3758,7 @@ void TextureViewer::on_debugPixelContext_clicked() m_Ctx.Replay().AsyncInvoke([this, &trace, &done, x, y](IReplayController *r) { trace = r->DebugPixel((uint32_t)x, (uint32_t)y, m_TexDisplay.subresource.sample, ~0U); - if(trace->states.isEmpty()) + if(trace->debugger == NULL) { r->FreeTrace(trace); trace = NULL; diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index 5cfca9271..bd994ffcc 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -874,6 +874,20 @@ bucket when the pixel values are divided between ``minval`` and ``maxval``. )"); virtual ShaderDebugTrace *DebugThread(const uint32_t groupid[3], const uint32_t threadid[3]) = 0; + DOCUMENT(R"(Continue a shader's debugging with a given shader debugger instance. This will run an +implementation defined number of steps and then return those steps in a list. This may be a fixed +number of steps or it may run for a fixed length of time and return as many steps as can be +calculated in that time. + +This will always perform at least one step. If the list is empty, the debugging process has +completed, further calls will return an empty list. + +:param ShaderDebugger debugger: The shader debugger to continue running. +:return: A number of subsequent states. +:rtype: ``list`` of :class:`ShaderDebugTrace` +)"); + virtual rdcarray ContinueDebug(ShaderDebugger *debugger) = 0; + DOCUMENT(R"(Free a debugging trace from running a shader invocation debug. :param ShaderDebugTrace trace: The shader debugging trace to free. diff --git a/renderdoc/api/replay/shader_types.h b/renderdoc/api/replay/shader_types.h index 5779d024c..38a2427a0 100644 --- a/renderdoc/api/replay/shader_types.h +++ b/renderdoc/api/replay/shader_types.h @@ -494,6 +494,39 @@ treated as covering the code. }; DECLARE_REFLECTION_STRUCT(LineColumnInfo); +DOCUMENT("This stores the before and after state of a :class:`ShaderVariable`."); +struct ShaderVariableChange +{ + DOCUMENT(""); + ShaderVariableChange() = default; + ShaderVariableChange(const ShaderVariableChange &) = default; + ShaderVariableChange &operator=(const ShaderVariableChange &) = default; + + bool operator==(const ShaderVariableChange &o) const + { + return before == o.before && after == o.after; + } + bool operator<(const ShaderVariableChange &o) const + { + if(!(before == o.before)) + return before < o.before; + if(!(after == o.after)) + return after < o.after; + return false; + } + + DOCUMENT(R"(The value of the variable before the change. If this variable is uninitialised that +means the variable came into existance on this step. +)"); + ShaderVariable before; + + DOCUMENT(R"(The value of the variable after the change. If this variable is uninitialised that +means the variable stopped existing on this step. +)"); + ShaderVariable after; +}; +DECLARE_REFLECTION_STRUCT(ShaderVariableChange); + DOCUMENT(R"(This stores the current state of shader debugging at one particular step in the shader, with all mutable variable contents. )"); @@ -506,24 +539,42 @@ struct ShaderDebugState bool operator==(const ShaderDebugState &o) const { - return variables == o.variables && sourceVars == o.sourceVars && - nextInstruction == o.nextInstruction && flags == o.flags; + return nextInstruction == o.nextInstruction && flags == o.flags && changes == o.changes && + sourceVars == o.sourceVars && stepIndex == o.stepIndex; } bool operator<(const ShaderDebugState &o) const { - if(!(variables == o.variables)) - return variables < o.variables; - if(!(sourceVars == o.sourceVars)) - return sourceVars < o.sourceVars; if(!(nextInstruction == o.nextInstruction)) return nextInstruction < o.nextInstruction; if(!(flags == o.flags)) return flags < o.flags; + if(!(stepIndex == o.stepIndex)) + return stepIndex < o.stepIndex; + if(!(changes == o.changes)) + return changes < o.changes; + if(!(sourceVars == o.sourceVars)) + return sourceVars < o.sourceVars; return false; } - DOCUMENT("The mutable variables for this shader as a list of :class:`ShaderVariable`."); - rdcarray variables; + DOCUMENT(R"(The next instruction to be executed after this state. The initial state before any +shader execution happened will have ``nextInstruction == 0``. +)"); + uint32_t nextInstruction = 0; + + DOCUMENT(R"(The program counter within the debug trace. The initial state will be index 0, and it +will increment linearly after that regardless of loops or branching. +)"); + uint32_t stepIndex = 0; + + DOCUMENT("A set of :class:`ShaderEvents` flags that indicate what events happened on this step."); + ShaderEvents flags = ShaderEvents::NoEvent; + + DOCUMENT(R"(The changes in mutable variables for this shader as a list of +:class:`ShaderVariableChange`. The change documents the bidirectional change of variables, so that +a single state can be updated either forwards or backwards using the information. +)"); + rdcarray changes; DOCUMENT(R"(An optional list of :class:`SourceVariableMapping` indicating which high-level source variables map to which debug variables and including extra type information. @@ -534,17 +585,6 @@ a different debug variable. )"); rdcarray sourceVars; - DOCUMENT("A list of ranges in :data:`variables` that were modified."); - rdcarray modified; - - DOCUMENT(R"(The next instruction to be executed after this state. The initial state before any -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."); - ShaderEvents flags; - 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. @@ -554,6 +594,21 @@ The oldest/outer function is first in the list, the newest/inner function is las DECLARE_REFLECTION_STRUCT(ShaderDebugState); +DOCUMENT("An opaque structure that has internal state for shader debugging"); +struct ShaderDebugger +{ +protected: + DOCUMENT(""); + ShaderDebugger() = default; + ShaderDebugger(const ShaderDebugger &) = default; + ShaderDebugger &operator=(const ShaderDebugger &) = default; + +public: + virtual ~ShaderDebugger() = default; +}; + +DECLARE_REFLECTION_STRUCT(ShaderDebugger); + DOCUMENT(R"(This stores the whole state of a shader's execution from start to finish, with each individual debugging step along the way, as well as the immutable global constant values that do not change with shader execution. @@ -602,10 +657,12 @@ be empty if there is no source variable mapping that extends to the life of the )"); rdcarray sourceVars; - DOCUMENT(R"(A list of :class:`ShaderDebugState` states representing the state after each -instruction was executed + DOCUMENT(R"(An opaque handle of :class:`ShaderDebugger` identifying by the undelying debugger, +which is used to simulate the shader and generate new debug states. + +If this is ``None`` then the trace is invalid. )"); - rdcarray states; + ShaderDebugger *debugger = NULL; DOCUMENT("A flag indicating whether this trace has source-variable mapping information"); bool hasSourceMapping = false; diff --git a/renderdoc/core/image_viewer.cpp b/renderdoc/core/image_viewer.cpp index f89829a2f..7140788d4 100644 --- a/renderdoc/core/image_viewer.cpp +++ b/renderdoc/core/image_viewer.cpp @@ -257,27 +257,22 @@ public: { return rdcarray(); } - ShaderDebugTrace DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, uint32_t idx, - uint32_t instOffset, uint32_t vertOffset) + ShaderDebugTrace *DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, uint32_t idx, + uint32_t instOffset, uint32_t vertOffset) { - ShaderDebugTrace ret; - RDCEraseEl(ret); - return ret; + return new ShaderDebugTrace(); } - ShaderDebugTrace DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, - uint32_t primitive) + ShaderDebugTrace *DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, + uint32_t primitive) { - ShaderDebugTrace ret; - RDCEraseEl(ret); - return ret; + return new ShaderDebugTrace(); } - ShaderDebugTrace DebugThread(uint32_t eventId, const uint32_t groupid[3], - const uint32_t threadid[3]) + ShaderDebugTrace *DebugThread(uint32_t eventId, const uint32_t groupid[3], + const uint32_t threadid[3]) { - ShaderDebugTrace ret; - RDCEraseEl(ret); - return ret; + return new ShaderDebugTrace(); } + rdcarray ContinueDebug(ShaderDebugger *debugger) { return {}; } void BuildTargetShader(ShaderEncoding sourceEncoding, const bytebuf &source, const rdcstr &entry, const ShaderCompileFlags &compileFlags, ShaderStage type, ResourceId &id, rdcstr &errors) diff --git a/renderdoc/core/replay_proxy.cpp b/renderdoc/core/replay_proxy.cpp index 17c86f9b3..434a3bd2a 100644 --- a/renderdoc/core/replay_proxy.cpp +++ b/renderdoc/core/replay_proxy.cpp @@ -93,6 +93,8 @@ rdcstr DoStringise(const ReplayProxyPacket &el) STRINGISE_ENUM_NAMED(eReplayProxy_GetTargetShaderEncodings, "GetTargetShaderEncodings"); STRINGISE_ENUM_NAMED(eReplayProxy_GetDriverInfo, "GetDriverInfo"); + + STRINGISE_ENUM_NAMED(eReplayProxy_ContinueDebug, "ContinueDebug"); } END_ENUM_STRINGISE(); } @@ -1443,14 +1445,14 @@ rdcarray ReplayProxy::PixelHistory(rdcarray event } template -ShaderDebugTrace ReplayProxy::Proxied_DebugVertex(ParamSerialiser ¶mser, - ReturnSerialiser &retser, uint32_t eventId, - uint32_t vertid, uint32_t instid, uint32_t idx, - uint32_t instOffset, uint32_t vertOffset) +ShaderDebugTrace *ReplayProxy::Proxied_DebugVertex(ParamSerialiser ¶mser, + ReturnSerialiser &retser, uint32_t eventId, + uint32_t vertid, uint32_t instid, uint32_t idx, + uint32_t instOffset, uint32_t vertOffset) { const ReplayProxyPacket expectedPacket = eReplayProxy_DebugVertex; ReplayProxyPacket packet = eReplayProxy_DebugVertex; - ShaderDebugTrace ret; + ShaderDebugTrace *ret; { BEGIN_PARAMS(); @@ -1467,27 +1469,29 @@ ShaderDebugTrace ReplayProxy::Proxied_DebugVertex(ParamSerialiser ¶mser, REMOTE_EXECUTION(); if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored) ret = m_Remote->DebugVertex(eventId, vertid, instid, idx, instOffset, vertOffset); + else + ret = new ShaderDebugTrace; } - SERIALISE_RETURN(ret); + SERIALISE_RETURN(*ret); return ret; } -ShaderDebugTrace ReplayProxy::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, - uint32_t idx, uint32_t instOffset, uint32_t vertOffset) +ShaderDebugTrace *ReplayProxy::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, + uint32_t idx, uint32_t instOffset, uint32_t vertOffset) { PROXY_FUNCTION(DebugVertex, eventId, vertid, instid, idx, instOffset, vertOffset); } template -ShaderDebugTrace ReplayProxy::Proxied_DebugPixel(ParamSerialiser ¶mser, ReturnSerialiser &retser, - uint32_t eventId, uint32_t x, uint32_t y, - uint32_t sample, uint32_t primitive) +ShaderDebugTrace *ReplayProxy::Proxied_DebugPixel(ParamSerialiser ¶mser, ReturnSerialiser &retser, + uint32_t eventId, uint32_t x, uint32_t y, + uint32_t sample, uint32_t primitive) { const ReplayProxyPacket expectedPacket = eReplayProxy_DebugPixel; ReplayProxyPacket packet = eReplayProxy_DebugPixel; - ShaderDebugTrace ret; + ShaderDebugTrace *ret; { BEGIN_PARAMS(); @@ -1503,27 +1507,30 @@ ShaderDebugTrace ReplayProxy::Proxied_DebugPixel(ParamSerialiser ¶mser, Retu REMOTE_EXECUTION(); if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored) ret = m_Remote->DebugPixel(eventId, x, y, sample, primitive); + else + ret = new ShaderDebugTrace; } - SERIALISE_RETURN(ret); + SERIALISE_RETURN(*ret); return ret; } -ShaderDebugTrace ReplayProxy::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, - uint32_t primitive) +ShaderDebugTrace *ReplayProxy::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, + uint32_t primitive) { PROXY_FUNCTION(DebugPixel, eventId, x, y, sample, primitive); } template -ShaderDebugTrace ReplayProxy::Proxied_DebugThread(ParamSerialiser ¶mser, ReturnSerialiser &retser, - uint32_t eventId, const uint32_t groupid[3], - const uint32_t threadid[3]) +ShaderDebugTrace *ReplayProxy::Proxied_DebugThread(ParamSerialiser ¶mser, + ReturnSerialiser &retser, uint32_t eventId, + const uint32_t groupid[3], + const uint32_t threadid[3]) { const ReplayProxyPacket expectedPacket = eReplayProxy_DebugThread; ReplayProxyPacket packet = eReplayProxy_DebugThread; - ShaderDebugTrace ret; + ShaderDebugTrace *ret; uint32_t GroupID[3] = {groupid[0], groupid[1], groupid[2]}; uint32_t ThreadID[3] = {threadid[0], threadid[1], threadid[2]}; @@ -1540,6 +1547,42 @@ ShaderDebugTrace ReplayProxy::Proxied_DebugThread(ParamSerialiser ¶mser, Ret REMOTE_EXECUTION(); if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored) ret = m_Remote->DebugThread(eventId, GroupID, ThreadID); + else + ret = new ShaderDebugTrace; + } + + SERIALISE_RETURN(*ret); + + return ret; +} + +ShaderDebugTrace *ReplayProxy::DebugThread(uint32_t eventId, const uint32_t groupid[3], + const uint32_t threadid[3]) +{ + PROXY_FUNCTION(DebugThread, eventId, groupid, threadid); +} + +template +rdcarray ReplayProxy::Proxied_ContinueDebug(ParamSerialiser ¶mser, + ReturnSerialiser &retser, + ShaderDebugger *debugger) +{ + const ReplayProxyPacket expectedPacket = eReplayProxy_GetDisassemblyTargets; + ReplayProxyPacket packet = eReplayProxy_GetDisassemblyTargets; + rdcarray ret; + + { + BEGIN_PARAMS(); + uint64_t debugger_ptr = (uint64_t)(uintptr_t)debugger; + SERIALISE_ELEMENT(debugger_ptr); + debugger = (ShaderDebugger *)(uintptr_t)debugger_ptr; + END_PARAMS(); + } + + { + REMOTE_EXECUTION(); + if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored) + ret = m_Remote->ContinueDebug(debugger); } SERIALISE_RETURN(ret); @@ -1547,10 +1590,9 @@ ShaderDebugTrace ReplayProxy::Proxied_DebugThread(ParamSerialiser ¶mser, Ret return ret; } -ShaderDebugTrace ReplayProxy::DebugThread(uint32_t eventId, const uint32_t groupid[3], - const uint32_t threadid[3]) +rdcarray ReplayProxy::ContinueDebug(ShaderDebugger *debugger) { - PROXY_FUNCTION(DebugThread, eventId, groupid, threadid); + PROXY_FUNCTION(ContinueDebug, debugger); } template @@ -2738,6 +2780,7 @@ bool ReplayProxy::Tick(int type) DebugThread(0, dummy1, dummy2); break; } + case eReplayProxy_ContinueDebug: ContinueDebug(NULL); break; case eReplayProxy_RenderOverlay: RenderOverlay(ResourceId(), CompType::Typeless, FloatVector(), DebugOverlay::NoOverlay, 0, rdcarray()); diff --git a/renderdoc/core/replay_proxy.h b/renderdoc/core/replay_proxy.h index a9fa8943a..92cd88f69 100644 --- a/renderdoc/core/replay_proxy.h +++ b/renderdoc/core/replay_proxy.h @@ -100,6 +100,8 @@ enum ReplayProxyPacket eReplayProxy_GetDriverInfo, eReplayProxy_GetAvailableGPUs, + + eReplayProxy_ContinueDebug, }; DECLARE_REFLECTION_ENUM(ReplayProxyPacket); @@ -520,12 +522,13 @@ public: IMPLEMENT_FUNCTION_PROXIED(rdcarray, PixelHistory, rdcarray events, ResourceId target, uint32_t x, uint32_t y, const Subresource &sub, CompType typeCast); - IMPLEMENT_FUNCTION_PROXIED(ShaderDebugTrace, DebugVertex, uint32_t eventId, uint32_t vertid, + IMPLEMENT_FUNCTION_PROXIED(ShaderDebugTrace *, DebugVertex, uint32_t eventId, uint32_t vertid, uint32_t instid, uint32_t idx, uint32_t instOffset, uint32_t vertOffset); - IMPLEMENT_FUNCTION_PROXIED(ShaderDebugTrace, DebugPixel, uint32_t eventId, uint32_t x, uint32_t y, - uint32_t sample, uint32_t primitive); - IMPLEMENT_FUNCTION_PROXIED(ShaderDebugTrace, DebugThread, uint32_t eventId, + IMPLEMENT_FUNCTION_PROXIED(ShaderDebugTrace *, DebugPixel, uint32_t eventId, uint32_t x, + uint32_t y, uint32_t sample, uint32_t primitive); + IMPLEMENT_FUNCTION_PROXIED(ShaderDebugTrace *, DebugThread, uint32_t eventId, const uint32_t groupid[3], const uint32_t threadid[3]); + IMPLEMENT_FUNCTION_PROXIED(rdcarray, ContinueDebug, ShaderDebugger *debugger); IMPLEMENT_FUNCTION_PROXIED(rdcarray, GetTargetShaderEncodings); IMPLEMENT_FUNCTION_PROXIED(void, BuildTargetShader, ShaderEncoding sourceEncoding, diff --git a/renderdoc/driver/d3d11/d3d11_debug.h b/renderdoc/driver/d3d11/d3d11_debug.h index 2891a5572..af281cee5 100644 --- a/renderdoc/driver/d3d11/d3d11_debug.h +++ b/renderdoc/driver/d3d11/d3d11_debug.h @@ -40,12 +40,6 @@ class D3D11ResourceManager; struct CopyPixelParams; -namespace DXBCDebug -{ -struct GlobalState; -class State; -} - namespace DXBC { class DXBCContainer; diff --git a/renderdoc/driver/d3d11/d3d11_replay.h b/renderdoc/driver/d3d11/d3d11_replay.h index b059c552c..9340179d1 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.h +++ b/renderdoc/driver/d3d11/d3d11_replay.h @@ -231,12 +231,14 @@ public: rdcarray PixelHistory(rdcarray events, ResourceId target, uint32_t x, uint32_t y, const Subresource &sub, CompType typeCast); - ShaderDebugTrace DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, uint32_t idx, - uint32_t instOffset, uint32_t vertOffset); - ShaderDebugTrace DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, - uint32_t primitive); - ShaderDebugTrace DebugThread(uint32_t eventId, const uint32_t groupid[3], - const uint32_t threadid[3]); + ShaderDebugTrace *DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, uint32_t idx, + uint32_t instOffset, uint32_t vertOffset); + ShaderDebugTrace *DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, + uint32_t primitive); + ShaderDebugTrace *DebugThread(uint32_t eventId, const uint32_t groupid[3], + const uint32_t threadid[3]); + rdcarray ContinueDebug(ShaderDebugger *debugger); + uint32_t PickVertex(uint32_t eventId, int32_t width, int32_t height, const MeshDisplay &cfg, uint32_t x, uint32_t y); diff --git a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp index 6c3d0b291..9fec8c4a9 100644 --- a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp +++ b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp @@ -53,7 +53,7 @@ struct DebugHit class D3D11DebugAPIWrapper : public DXBCDebug::DebugAPIWrapper { public: - D3D11DebugAPIWrapper(WrappedID3D11Device *device, DXBC::DXBCContainer *dxbc, + D3D11DebugAPIWrapper(WrappedID3D11Device *device, const DXBC::DXBCContainer *dxbc, DXBCDebug::GlobalState &globalState); void SetCurrentInstruction(uint32_t instruction) { m_instruction = instruction; } @@ -82,12 +82,13 @@ public: private: DXBC::ShaderType GetShaderType() { return m_dxbc ? m_dxbc->m_Type : DXBC::ShaderType::Pixel; } WrappedID3D11Device *m_pDevice; - DXBC::DXBCContainer *m_dxbc; + const DXBC::DXBCContainer *m_dxbc; DXBCDebug::GlobalState &m_globalState; uint32_t m_instruction; }; -D3D11DebugAPIWrapper::D3D11DebugAPIWrapper(WrappedID3D11Device *device, DXBC::DXBCContainer *dxbc, +D3D11DebugAPIWrapper::D3D11DebugAPIWrapper(WrappedID3D11Device *device, + const DXBC::DXBCContainer *dxbc, DXBCDebug::GlobalState &globalState) : m_pDevice(device), m_dxbc(dxbc), m_globalState(globalState), m_instruction(0) { @@ -1698,9 +1699,11 @@ bool D3D11DebugAPIWrapper::CalculateMathIntrinsic(DXBCBytecode::OpcodeType opcod return true; } -void AddCBuffersToDebugTrace(const DXBCBytecode::Program &program, D3D11DebugManager &debugManager, - ShaderDebugTrace &trace, const D3D11RenderState::Shader &shader, - const ShaderReflection &refl, const ShaderBindpointMapping &mapping) +void AddCBuffersToGlobalState(const DXBCBytecode::Program &program, D3D11DebugManager &debugManager, + DXBCDebug::GlobalState &global, + rdcarray &sourceVars, + const D3D11RenderState::Shader &shader, const ShaderReflection &refl, + const ShaderBindpointMapping &mapping) { bytebuf cbufData; for(int i = 0; i < D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT; i++) @@ -1712,22 +1715,20 @@ void AddCBuffersToDebugTrace(const DXBCBytecode::Program &program, D3D11DebugMan debugManager.GetBufferData(shader.ConstantBuffers[i], shader.CBOffsets[i] * sizeof(Vec4f), shader.CBCounts[i] * sizeof(Vec4f), cbufData); - AddCBufferToDebugTrace(program, trace, refl, mapping, slot, cbufData); + AddCBufferToGlobalState(program, global, sourceVars, refl, mapping, slot, cbufData); } } } -ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, - uint32_t idx, uint32_t instOffset, uint32_t vertOffset) +ShaderDebugTrace *D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, + uint32_t idx, uint32_t instOffset, uint32_t vertOffset) { using namespace DXBCBytecode; using namespace DXBCDebug; - D3D11MarkerRegion debugpixRegion( + D3D11MarkerRegion region( StringFormat::Fmt("DebugVertex @ %u of (%u,%u,%u)", eventId, vertid, instid, idx)); - ShaderDebugTrace empty; - const DrawcallDescription *draw = m_pDevice->GetDrawcall(eventId); D3D11RenderStateTracker tracker(m_pImmediateContext); @@ -1740,13 +1741,13 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin SAFE_RELEASE(stateVS); if(!vs) - return empty; + return new ShaderDebugTrace; DXBC::DXBCContainer *dxbc = vs->GetDXBC(); const ShaderReflection &refl = vs->GetDetails(); if(!dxbc) - return empty; + return new ShaderDebugTrace; dxbc->GetDisassembly(); @@ -1811,19 +1812,15 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin } } - ShaderDebugTrace ret; + InterpretDebugger *interpreter = new InterpretDebugger; + ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, vs->GetMapping(), 0); + GlobalState &global = interpreter->global; + ThreadState &state = interpreter->activeLane(); - GlobalState global; - global.PopulateGroupshared(dxbc->GetDXBCByteCode()); - State initialState(-1, &global, dxbc); - CreateShaderDebugStateAndTrace(initialState, ret, dxbc, refl, vs->GetMapping()); + AddCBuffersToGlobalState(*dxbc->GetDXBCByteCode(), *GetDebugManager(), global, ret->sourceVars, + rs->VS, refl, vs->GetMapping()); - AddCBuffersToDebugTrace(*dxbc->GetDXBCByteCode(), *GetDebugManager(), ret, rs->VS, refl, - vs->GetMapping()); - - global.constantBlocks = ret.constantBlocks; - - for(size_t i = 0; i < ret.inputs.size(); i++) + for(size_t i = 0; i < state.inputs.size(); i++) { if(dxbc->GetReflection()->InputSig[i].systemValue == ShaderBuiltin::Undefined || dxbc->GetReflection()->InputSig[i].systemValue == @@ -1894,17 +1891,17 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin // more data needed than is provided if(dxbc->GetReflection()->InputSig[i].compCount > fmt.compCount) { - ret.inputs[i].value.u.w = 1; + state.inputs[i].value.u.w = 1; if(fmt.compType == CompType::Float) - ret.inputs[i].value.f.w = 1.0f; + state.inputs[i].value.f.w = 1.0f; } // interpret resource format types if(fmt.Special()) { - Vec3f *v3 = (Vec3f *)ret.inputs[i].value.fv; - Vec4f *v4 = (Vec4f *)ret.inputs[i].value.fv; + Vec3f *v3 = (Vec3f *)state.inputs[i].value.fv; + Vec4f *v4 = (Vec4f *)state.inputs[i].value.fv; // only pull in all or nothing from these, // if there's only e.g. 3 bytes remaining don't read and unpack some of @@ -1916,8 +1913,8 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin if(srcData == NULL || packedsize > dataSize) { - ret.inputs[i].value.u.x = ret.inputs[i].value.u.y = ret.inputs[i].value.u.z = - ret.inputs[i].value.u.w = 0; + state.inputs[i].value.u.x = state.inputs[i].value.u.y = state.inputs[i].value.u.z = + state.inputs[i].value.u.w = 0; } else if(fmt.type == ResourceFormatType::R5G5B5A1) { @@ -1943,10 +1940,10 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin if(fmt.compType == CompType::UInt) { - ret.inputs[i].value.u.z = (packed >> 0) & 0x3ff; - ret.inputs[i].value.u.y = (packed >> 10) & 0x3ff; - ret.inputs[i].value.u.x = (packed >> 20) & 0x3ff; - ret.inputs[i].value.u.w = (packed >> 30) & 0x003; + state.inputs[i].value.u.z = (packed >> 0) & 0x3ff; + state.inputs[i].value.u.y = (packed >> 10) & 0x3ff; + state.inputs[i].value.u.x = (packed >> 20) & 0x3ff; + state.inputs[i].value.u.w = (packed >> 30) & 0x003; } else { @@ -1965,7 +1962,7 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin { if(srcData == NULL || fmt.compByteWidth > dataSize) { - ret.inputs[i].value.uv[c] = 0; + state.inputs[i].value.uv[c] = 0; continue; } @@ -1976,20 +1973,20 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin byte *src = srcData + c * fmt.compByteWidth; if(fmt.compType == CompType::UInt) - ret.inputs[i].value.uv[c] = *src; + state.inputs[i].value.uv[c] = *src; else if(fmt.compType == CompType::SInt) - ret.inputs[i].value.iv[c] = *((int8_t *)src); + state.inputs[i].value.iv[c] = *((int8_t *)src); else if(fmt.compType == CompType::UNorm || fmt.compType == CompType::UNormSRGB) - ret.inputs[i].value.fv[c] = float(*src) / 255.0f; + state.inputs[i].value.fv[c] = float(*src) / 255.0f; else if(fmt.compType == CompType::SNorm) { signed char *schar = (signed char *)src; // -128 is mapped to -1, then -127 to -127 are mapped to -1 to 1 if(*schar == -128) - ret.inputs[i].value.fv[c] = -1.0f; + state.inputs[i].value.fv[c] = -1.0f; else - ret.inputs[i].value.fv[c] = float(*schar) / 127.0f; + state.inputs[i].value.fv[c] = float(*schar) / 127.0f; } else RDCERR("Unexpected component type"); @@ -1999,22 +1996,22 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin uint16_t *src = (uint16_t *)(srcData + c * fmt.compByteWidth); if(fmt.compType == CompType::Float) - ret.inputs[i].value.fv[c] = ConvertFromHalf(*src); + state.inputs[i].value.fv[c] = ConvertFromHalf(*src); else if(fmt.compType == CompType::UInt) - ret.inputs[i].value.uv[c] = *src; + state.inputs[i].value.uv[c] = *src; else if(fmt.compType == CompType::SInt) - ret.inputs[i].value.iv[c] = *((int16_t *)src); + state.inputs[i].value.iv[c] = *((int16_t *)src); else if(fmt.compType == CompType::UNorm || fmt.compType == CompType::UNormSRGB) - ret.inputs[i].value.fv[c] = float(*src) / float(UINT16_MAX); + state.inputs[i].value.fv[c] = float(*src) / float(UINT16_MAX); else if(fmt.compType == CompType::SNorm) { int16_t *sint = (int16_t *)src; // -32768 is mapped to -1, then -32767 to -32767 are mapped to -1 to 1 if(*sint == -32768) - ret.inputs[i].value.fv[c] = -1.0f; + state.inputs[i].value.fv[c] = -1.0f; else - ret.inputs[i].value.fv[c] = float(*sint) / 32767.0f; + state.inputs[i].value.fv[c] = float(*sint) / 32767.0f; } else RDCERR("Unexpected component type"); @@ -2025,7 +2022,7 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin if(fmt.compType == CompType::Float || fmt.compType == CompType::UInt || fmt.compType == CompType::SInt) - memcpy(&ret.inputs[i].value.uv[c], src, 4); + memcpy(&state.inputs[i].value.uv[c], src, 4); else RDCERR("Unexpected component type"); } @@ -2034,7 +2031,7 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin if(fmt.BGRAOrder()) { RDCASSERT(fmt.compCount == 4); - std::swap(ret.inputs[i].value.fv[2], ret.inputs[i].value.fv[0]); + std::swap(state.inputs[i].value.fv[2], state.inputs[i].value.fv[0]); } } } @@ -2046,20 +2043,20 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin sv_vertid = idx; if(dxbc->GetReflection()->InputSig[i].compType == CompType::Float) - ret.inputs[i].value.f.x = ret.inputs[i].value.f.y = ret.inputs[i].value.f.z = - ret.inputs[i].value.f.w = (float)sv_vertid; + state.inputs[i].value.f.x = state.inputs[i].value.f.y = state.inputs[i].value.f.z = + state.inputs[i].value.f.w = (float)sv_vertid; else - ret.inputs[i].value.u.x = ret.inputs[i].value.u.y = ret.inputs[i].value.u.z = - ret.inputs[i].value.u.w = sv_vertid; + state.inputs[i].value.u.x = state.inputs[i].value.u.y = state.inputs[i].value.u.z = + state.inputs[i].value.u.w = sv_vertid; } else if(dxbc->GetReflection()->InputSig[i].systemValue == ShaderBuiltin::InstanceIndex) { if(dxbc->GetReflection()->InputSig[i].compType == CompType::Float) - ret.inputs[i].value.f.x = ret.inputs[i].value.f.y = ret.inputs[i].value.f.z = - ret.inputs[i].value.f.w = (float)instid; + state.inputs[i].value.f.x = state.inputs[i].value.f.y = state.inputs[i].value.f.z = + state.inputs[i].value.f.w = (float)instid; else - ret.inputs[i].value.u.x = ret.inputs[i].value.u.y = ret.inputs[i].value.u.z = - ret.inputs[i].value.u.w = instid; + state.inputs[i].value.u.x = state.inputs[i].value.u.y = state.inputs[i].value.u.z = + state.inputs[i].value.u.w = instid; } else { @@ -2067,56 +2064,26 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin } } + ret->constantBlocks = global.constantBlocks; + ret->inputs = state.inputs; + ret->hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); + delete[] instData; - rdcarray states; - - dxbc->FillStateInstructionInfo(initialState); - - states.push_back((State)initialState); - - D3D11MarkerRegion simloop("Simulation Loop"); - - D3D11DebugAPIWrapper apiWrapper(m_pDevice, dxbc, global); - - for(int cycleCounter = 0;; cycleCounter++) - { - if(initialState.Finished()) - break; - - initialState = initialState.GetNext(&apiWrapper, NULL); - - dxbc->FillStateInstructionInfo(initialState); - - states.push_back((State)initialState); - - if(cycleCounter == SHADER_DEBUG_WARN_THRESHOLD) - { - if(PromptDebugTimeout(cycleCounter)) - break; - } - } - - ret.states = states; - - ret.hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); - - dxbc->FillTraceLineInfo(ret); + dxbc->FillTraceLineInfo(*ret); return ret; } -ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, - uint32_t primitive) +ShaderDebugTrace *D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, + uint32_t primitive) { using namespace DXBCBytecode; using namespace DXBCDebug; - D3D11MarkerRegion debugpixRegion( + D3D11MarkerRegion region( StringFormat::Fmt("DebugPixel @ %u of (%u,%u) %u / %u", eventId, x, y, sample, primitive)); - ShaderDebugTrace empty; - D3D11RenderStateTracker tracker(m_pImmediateContext); ID3D11PixelShader *statePS = NULL; @@ -2149,7 +2116,7 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t SAFE_RELEASE(stateVS); if(!ps) - return empty; + return new ShaderDebugTrace; D3D11RenderState *rs = m_pImmediateContext->GetCurrentPipelineState(); @@ -2157,7 +2124,7 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t const ShaderReflection &refl = ps->GetDetails(); if(!dxbc) - return empty; + return new ShaderDebugTrace; dxbc->GetDisassembly(); @@ -2464,7 +2431,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim if(FAILED(hr)) { RDCERR("Failed to create buffer HRESULT: %s", ToStr(hr).c_str()); - return empty; + return new ShaderDebugTrace; } ID3D11Buffer *evalBuf = NULL; @@ -2479,7 +2446,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim if(FAILED(hr)) { RDCERR("Failed to create buffer HRESULT: %s", ToStr(hr).c_str()); - return empty; + return new ShaderDebugTrace; } } @@ -2496,7 +2463,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim if(FAILED(hr)) { RDCERR("Failed to create buffer HRESULT: %s", ToStr(hr).c_str()); - return empty; + return new ShaderDebugTrace; } uint32_t evalStructStride = uint32_t(evalSampleCacheData.size() * sizeof(Vec4f)); @@ -2511,7 +2478,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim if(FAILED(hr)) { RDCERR("Failed to create buffer HRESULT: %s", ToStr(hr).c_str()); - return empty; + return new ShaderDebugTrace; } } @@ -2528,7 +2495,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim if(FAILED(hr)) { RDCERR("Failed to create buffer HRESULT: %s", ToStr(hr).c_str()); - return empty; + return new ShaderDebugTrace; } ID3D11UnorderedAccessView *evalUAV = NULL; @@ -2541,7 +2508,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim if(FAILED(hr)) { RDCERR("Failed to create buffer HRESULT: %s", ToStr(hr).c_str()); - return empty; + return new ShaderDebugTrace; } } @@ -2576,7 +2543,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim if(FAILED(hr)) { RDCERR("Failed to map stage buff HRESULT: %s", ToStr(hr).c_str()); - return empty; + return new ShaderDebugTrace; } byte *initialData = new byte[structStride * (overdrawLevels + 1)]; @@ -2594,7 +2561,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim { RDCERR("Failed to map stage buff HRESULT: %s", ToStr(hr).c_str()); SAFE_DELETE_ARRAY(initialData); - return empty; + return new ShaderDebugTrace; } evalData = new byte[evalStructStride * (overdrawLevels + 1)]; @@ -2622,7 +2589,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim RDCLOG("No hit for this event"); SAFE_DELETE_ARRAY(initialData); SAFE_DELETE_ARRAY(evalData); - return empty; + return new ShaderDebugTrace; } // if we encounter multiple hits at our destination pixel co-ord (or any other) we @@ -2702,47 +2669,33 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim RDCLOG("Couldn't find any pixels that passed depth test at target co-ordinates"); SAFE_DELETE_ARRAY(initialData); SAFE_DELETE_ARRAY(evalData); - return empty; + return new ShaderDebugTrace; } - ShaderDebugTrace ret; - tracker.State().ApplyState(m_pImmediateContext); - GlobalState global; - global.PopulateGroupshared(dxbc->GetDXBCByteCode()); + InterpretDebugger *interpreter = new InterpretDebugger; + ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, ps->GetMapping(), destIdx); + GlobalState &global = interpreter->global; + ThreadState &state = interpreter->activeLane(); + + AddCBuffersToGlobalState(*dxbc->GetDXBCByteCode(), *GetDebugManager(), global, ret->sourceVars, + rs->PS, refl, ps->GetMapping()); + global.sampleEvalRegisterMask = sampleEvalRegisterMask; - State quad[4] = { - // top-left - State(0, &global, dxbc), - // top-right - State(1, &global, dxbc), - // bottom-left - State(2, &global, dxbc), - // bottom-right - State(3, &global, dxbc), - }; - - CreateShaderDebugStateAndTrace(quad[destIdx], ret, dxbc, refl, ps->GetMapping()); - - AddCBuffersToDebugTrace(*dxbc->GetDXBCByteCode(), *GetDebugManager(), ret, rs->PS, refl, - ps->GetMapping()); - - global.constantBlocks = ret.constantBlocks; - { DebugHit *hit = winner; - rdcarray &ins = quad[destIdx].inputs; + rdcarray &ins = state.inputs; if(!ins.empty() && ins.back().name == dxbc->GetDXBCByteCode()->GetRegisterName(DXBCBytecode::TYPE_INPUT_COVERAGE_MASK, 0)) ins.back().value.u.x = hit->coverage; - quad[destIdx].semantics.coverage = hit->coverage; - quad[destIdx].semantics.primID = hit->primitive; - quad[destIdx].semantics.isFrontFace = hit->isFrontFace; + state.semantics.coverage = hit->coverage; + state.semantics.primID = hit->primitive; + state.semantics.isFrontFace = hit->isFrontFace; uint32_t *data = &hit->rawdata; @@ -2754,7 +2707,9 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim RDCERR("Derivatives invalid"); SAFE_DELETE_ARRAY(initialData); SAFE_DELETE_ARRAY(evalData); - return empty; + delete interpreter; + delete ret; + return new ShaderDebugTrace; } data++; @@ -2799,10 +2754,10 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim { if(i != destIdx) { - quad[i].inputs = quad[destIdx].inputs; - quad[i].semantics = quad[destIdx].semantics; - quad[i].variables = quad[destIdx].variables; - quad[i].SetHelper(); + interpreter->workgroup[i].inputs = state.inputs; + interpreter->workgroup[i].semantics = state.semantics; + interpreter->workgroup[i].variables = state.variables; + interpreter->workgroup[i].SetHelper(); } } @@ -2810,7 +2765,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim for(const GlobalState::SampleEvalCacheKey &key : evalSampleCacheData) { // start with the basic input value - ShaderVariable var = ret.inputs[key.inputRegisterIndex]; + ShaderVariable var = state.inputs[key.inputRegisterIndex]; // copy over the value into the variable memcpy(var.value.fv, evalSampleCache, var.columns * sizeof(float)); @@ -2827,172 +2782,30 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim evalSampleCache += 4; } - ApplyAllDerivatives(global, quad, destIdx, initialValues, (float *)data); + ApplyAllDerivatives(global, interpreter->workgroup, destIdx, initialValues, (float *)data); } - ret.inputs = quad[destIdx].inputs; + ret->inputs = state.inputs; + ret->constantBlocks = global.constantBlocks; + ret->hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); SAFE_DELETE_ARRAY(initialData); SAFE_DELETE_ARRAY(evalData); - rdcarray states; - - dxbc->FillStateInstructionInfo(quad[destIdx]); - - states.push_back((State)quad[destIdx]); - - // ping pong between so that we can have 'current' quad to update into new one - State quad2[4] = { - State(0, &global, dxbc), State(1, &global, dxbc), State(2, &global, dxbc), - State(3, &global, dxbc), - }; - - State *curquad = quad; - State *newquad = quad2; - - // marks any threads stalled waiting for others to catch up - bool activeMask[4] = {true, true, true, true}; - - int cycleCounter = 0; - - D3D11MarkerRegion simloop("Simulation Loop"); - - D3D11DebugAPIWrapper apiWrapper(m_pDevice, dxbc, global); - - // simulate lockstep until all threads are finished - bool finished = true; - do - { - for(size_t i = 0; i < 4; i++) - { - if(activeMask[i]) - newquad[i] = curquad[i].GetNext(&apiWrapper, curquad); - else - newquad[i] = curquad[i]; - } - - State *a = curquad; - curquad = newquad; - newquad = a; - - // if our destination quad is paused don't record multiple identical states. - if(activeMask[destIdx]) - { - State &s = curquad[destIdx]; - - dxbc->FillStateInstructionInfo(s); - - 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 - // since using derivatives is invalid. - - // Threads diverge either in ifs, loops, or switches. Due to the nature of the bytecode, - // all threads *must* pass through the same exit instruction for each, there's no jumping - // around with gotos. Note also for the same reason, the only time threads are on earlier - // instructions is if they are still catching up to a thread that has exited the control - // flow. - - // So the scheme is as follows: - // * If all threads have the same nextInstruction, just continue we are still in lockstep. - // * If threads are out of lockstep, find any thread which has nextInstruction pointing - // immediately *after* an ENDIF, ENDLOOP or ENDSWITCH. Pointing directly at one is not - // an indication the thread is done, as the next step for an ENDLOOP will jump back to - // the matching LOOP and continue iterating. - // * Pause any thread matching the above until all threads are pointing to the same - // instruction. By the assumption above, all threads will eventually pass through this - // terminating instruction so we just pause any other threads and don't do anything - // until the control flow has converged and we can continue stepping in lockstep. - - // mark all threads as active again. - // if we've converged, or we were never diverged, this keeps everything ticking - activeMask[0] = activeMask[1] = activeMask[2] = activeMask[3] = true; - - if(curquad[0].nextInstruction != curquad[1].nextInstruction || - curquad[0].nextInstruction != curquad[2].nextInstruction || - curquad[0].nextInstruction != curquad[3].nextInstruction) - { - // this isn't *perfect* but it will still eventually continue. We look for the most - // advanced thread, and check to see if it's just finished a control flow. If it has - // then we assume it's at the convergence point and wait for every other thread to - // catch up, pausing any threads that reach the convergence point before others. - - // Note this might mean we don't have any threads paused even within divergent flow. - // This is fine and all we care about is pausing to make sure threads don't run ahead - // into code that should be lockstep. We don't care at all about what they do within - // the code that is divergent. - - // The reason this isn't perfect is that the most advanced thread could be on an - // inner loop or inner if, not the convergence point, and we could be pausing it - // fruitlessly. Worse still - it could be on a branch none of the other threads will - // take so they will never reach that exact instruction. - // But we know that all threads will eventually go through the convergence point, so - // even in that worst case if we didn't pick the right waiting point, another thread - // will overtake and become the new most advanced thread and the previous waiting - // thread will resume. So in this case we caused a thread to wait more than it should - // have but that's not a big deal as it's within divergent flow so they don't have to - // stay in lockstep. Also if all threads will eventually pass that point we picked, - // we just waited to converge even in technically divergent code which is also - // harmless. - - // Phew! - - uint32_t convergencePoint = 0; - - // find which thread is most advanced - for(size_t i = 0; i < 4; i++) - if(curquad[i].nextInstruction > convergencePoint) - convergencePoint = curquad[i].nextInstruction; - - if(convergencePoint > 0) - { - OpcodeType op = dxbc->GetDXBCByteCode()->GetInstruction(convergencePoint - 1).operation; - - // if the most advnaced thread hasn't just finished control flow, then all - // threads are still running, so don't converge - if(op != OPCODE_ENDIF && op != OPCODE_ENDLOOP && op != OPCODE_ENDSWITCH) - convergencePoint = 0; - } - - // pause any threads at that instruction (could be none) - for(size_t i = 0; i < 4; i++) - if(curquad[i].nextInstruction == convergencePoint) - activeMask[i] = false; - } - - finished = curquad[destIdx].Finished(); - - cycleCounter++; - - if(cycleCounter == SHADER_DEBUG_WARN_THRESHOLD) - { - if(PromptDebugTimeout(cycleCounter)) - break; - } - } while(!finished); - - ret.states = states; - - ret.hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); - - dxbc->FillTraceLineInfo(ret); + dxbc->FillTraceLineInfo(*ret); return ret; } -ShaderDebugTrace D3D11Replay::DebugThread(uint32_t eventId, const uint32_t groupid[3], - const uint32_t threadid[3]) +ShaderDebugTrace *D3D11Replay::DebugThread(uint32_t eventId, const uint32_t groupid[3], + const uint32_t threadid[3]) { using namespace DXBCBytecode; using namespace DXBCDebug; - D3D11MarkerRegion simloop(StringFormat::Fmt("DebugThread @ %u: [%u, %u, %u] (%u, %u, %u)", - eventId, groupid[0], groupid[1], groupid[2], - threadid[0], threadid[1], threadid[2])); - - ShaderDebugTrace empty; + D3D11MarkerRegion region(StringFormat::Fmt("DebugThread @ %u: [%u, %u, %u] (%u, %u, %u)", eventId, + groupid[0], groupid[1], groupid[2], threadid[0], + threadid[1], threadid[2])); D3D11RenderStateTracker tracker(m_pImmediateContext); @@ -3004,68 +2817,38 @@ ShaderDebugTrace D3D11Replay::DebugThread(uint32_t eventId, const uint32_t group SAFE_RELEASE(stateCS); if(!cs) - return empty; + return new ShaderDebugTrace; DXBC::DXBCContainer *dxbc = cs->GetDXBC(); const ShaderReflection &refl = cs->GetDetails(); if(!dxbc) - return empty; + return new ShaderDebugTrace; dxbc->GetDisassembly(); D3D11RenderState *rs = m_pImmediateContext->GetCurrentPipelineState(); - ShaderDebugTrace ret; + InterpretDebugger *interpreter = new InterpretDebugger; + ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, cs->GetMapping(), 0); + GlobalState &global = interpreter->global; + ThreadState &state = interpreter->activeLane(); - GlobalState global; - global.PopulateGroupshared(dxbc->GetDXBCByteCode()); - State initialState(-1, &global, dxbc); - CreateShaderDebugStateAndTrace(initialState, ret, dxbc, refl, cs->GetMapping()); - - AddCBuffersToDebugTrace(*dxbc->GetDXBCByteCode(), *GetDebugManager(), ret, rs->CS, refl, - cs->GetMapping()); - - global.constantBlocks = ret.constantBlocks; + AddCBuffersToGlobalState(*dxbc->GetDXBCByteCode(), *GetDebugManager(), global, ret->sourceVars, + rs->PS, refl, cs->GetMapping()); for(int i = 0; i < 3; i++) { - initialState.semantics.GroupID[i] = groupid[i]; - initialState.semantics.ThreadID[i] = threadid[i]; + state.semantics.GroupID[i] = groupid[i]; + state.semantics.ThreadID[i] = threadid[i]; } - rdcarray states; + ret->constantBlocks = global.constantBlocks; + ret->hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); - dxbc->FillStateInstructionInfo(initialState); - - states.push_back((State)initialState); - - D3D11DebugAPIWrapper apiWrapper(m_pDevice, dxbc, global); - - for(int cycleCounter = 0;; cycleCounter++) - { - if(initialState.Finished()) - break; - - initialState = initialState.GetNext(&apiWrapper, NULL); - - dxbc->FillStateInstructionInfo(initialState); - - states.push_back((State)initialState); - - if(cycleCounter == SHADER_DEBUG_WARN_THRESHOLD) - { - if(PromptDebugTimeout(cycleCounter)) - break; - } - } - - ret.states = states; - - ret.hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); - - dxbc->FillTraceLineInfo(ret); + dxbc->FillTraceLineInfo(*ret); + // add fake inputs for semantics for(size_t i = 0; i < dxbc->GetDXBCByteCode()->GetNumDeclarations(); i++) { const DXBCBytecode::Declaration &decl = dxbc->GetDXBCByteCode()->GetDeclaration(i); @@ -3084,40 +2867,53 @@ ShaderDebugTrace D3D11Replay::DebugThread(uint32_t eventId, const uint32_t group switch(decl.operand.type) { case TYPE_INPUT_THREAD_GROUP_ID: - memcpy(v.value.uv, initialState.semantics.GroupID, sizeof(uint32_t) * 3); + memcpy(v.value.uv, state.semantics.GroupID, sizeof(uint32_t) * 3); v.columns = 3; break; case TYPE_INPUT_THREAD_ID_IN_GROUP: - memcpy(v.value.uv, initialState.semantics.ThreadID, sizeof(uint32_t) * 3); + memcpy(v.value.uv, state.semantics.ThreadID, sizeof(uint32_t) * 3); v.columns = 3; break; case TYPE_INPUT_THREAD_ID: - v.value.u.x = initialState.semantics.GroupID[0] * - dxbc->GetReflection()->DispatchThreadsDimension[0] + - initialState.semantics.ThreadID[0]; - v.value.u.y = initialState.semantics.GroupID[1] * - dxbc->GetReflection()->DispatchThreadsDimension[1] + - initialState.semantics.ThreadID[1]; - v.value.u.z = initialState.semantics.GroupID[2] * - dxbc->GetReflection()->DispatchThreadsDimension[2] + - initialState.semantics.ThreadID[2]; + v.value.u.x = + state.semantics.GroupID[0] * dxbc->GetReflection()->DispatchThreadsDimension[0] + + state.semantics.ThreadID[0]; + v.value.u.y = + state.semantics.GroupID[1] * dxbc->GetReflection()->DispatchThreadsDimension[1] + + state.semantics.ThreadID[1]; + v.value.u.z = + state.semantics.GroupID[2] * dxbc->GetReflection()->DispatchThreadsDimension[2] + + state.semantics.ThreadID[2]; v.columns = 3; break; case TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED: - v.value.u.x = initialState.semantics.ThreadID[2] * - dxbc->GetReflection()->DispatchThreadsDimension[0] * - dxbc->GetReflection()->DispatchThreadsDimension[1] + - initialState.semantics.ThreadID[1] * - dxbc->GetReflection()->DispatchThreadsDimension[0] + - initialState.semantics.ThreadID[0]; + v.value.u.x = + state.semantics.ThreadID[2] * dxbc->GetReflection()->DispatchThreadsDimension[0] * + dxbc->GetReflection()->DispatchThreadsDimension[1] + + state.semantics.ThreadID[1] * dxbc->GetReflection()->DispatchThreadsDimension[0] + + state.semantics.ThreadID[0]; v.columns = 1; break; default: v.columns = 4; break; } - ret.inputs.push_back(v); + ret->inputs.push_back(v); } } return ret; } + +rdcarray D3D11Replay::ContinueDebug(ShaderDebugger *debugger) +{ + DXBCDebug::InterpretDebugger *interpreter = (DXBCDebug::InterpretDebugger *)debugger; + + if(!interpreter) + return NULL; + + D3D11DebugAPIWrapper apiWrapper(m_pDevice, interpreter->dxbc, interpreter->global); + + D3D11MarkerRegion region("ContinueDebug Simulation Loop"); + + return interpreter->ContinueDebug(&apiWrapper); +} diff --git a/renderdoc/driver/d3d12/d3d12_debug.h b/renderdoc/driver/d3d12/d3d12_debug.h index 7236a11ff..915da2ad9 100644 --- a/renderdoc/driver/d3d12/d3d12_debug.h +++ b/renderdoc/driver/d3d12/d3d12_debug.h @@ -38,12 +38,6 @@ namespace DXBC class DXBCContainer; }; -namespace DXBCDebug -{ -struct GlobalState; -class State; -} - #define D3D12_MSAA_SAMPLECOUNT 4 // baked indices in descriptor heaps diff --git a/renderdoc/driver/d3d12/d3d12_replay.h b/renderdoc/driver/d3d12/d3d12_replay.h index fae9ab350..e79f4393e 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.h +++ b/renderdoc/driver/d3d12/d3d12_replay.h @@ -186,12 +186,14 @@ public: rdcarray PixelHistory(rdcarray events, ResourceId target, uint32_t x, uint32_t y, const Subresource &sub, CompType typeCast); - ShaderDebugTrace DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, uint32_t idx, - uint32_t instOffset, uint32_t vertOffset); - ShaderDebugTrace DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, - uint32_t primitive); - ShaderDebugTrace DebugThread(uint32_t eventId, const uint32_t groupid[3], - const uint32_t threadid[3]); + ShaderDebugTrace *DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, uint32_t idx, + uint32_t instOffset, uint32_t vertOffset); + ShaderDebugTrace *DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, + uint32_t primitive); + ShaderDebugTrace *DebugThread(uint32_t eventId, const uint32_t groupid[3], + const uint32_t threadid[3]); + rdcarray ContinueDebug(ShaderDebugger *debugger); + uint32_t PickVertex(uint32_t eventId, int32_t width, int32_t height, const MeshDisplay &cfg, uint32_t x, uint32_t y); diff --git a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp index 9c4612702..7f2bf7510 100644 --- a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp +++ b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp @@ -64,7 +64,7 @@ static bool IsShaderParameterVisible(DXBC::ShaderType shaderType, class D3D12DebugAPIWrapper : public DXBCDebug::DebugAPIWrapper { public: - D3D12DebugAPIWrapper(WrappedID3D12Device *device, DXBC::DXBCContainer *dxbc, + D3D12DebugAPIWrapper(WrappedID3D12Device *device, const DXBC::DXBCContainer *dxbc, DXBCDebug::GlobalState &globalState); void SetCurrentInstruction(uint32_t instruction) { m_instruction = instruction; } @@ -94,12 +94,13 @@ public: private: DXBC::ShaderType GetShaderType() { return m_dxbc ? m_dxbc->m_Type : DXBC::ShaderType::Pixel; } WrappedID3D12Device *m_pDevice; - DXBC::DXBCContainer *m_dxbc; + const DXBC::DXBCContainer *m_dxbc; DXBCDebug::GlobalState &m_globalState; uint32_t m_instruction; }; -D3D12DebugAPIWrapper::D3D12DebugAPIWrapper(WrappedID3D12Device *device, DXBC::DXBCContainer *dxbc, +D3D12DebugAPIWrapper::D3D12DebugAPIWrapper(WrappedID3D12Device *device, + const DXBC::DXBCContainer *dxbc, DXBCDebug::GlobalState &globalState) : m_pDevice(device), m_dxbc(dxbc), m_globalState(globalState), m_instruction(0) { @@ -1038,7 +1039,8 @@ bool D3D12DebugAPIWrapper::CalculateSampleGather( void GatherConstantBuffers(WrappedID3D12Device *pDevice, const DXBCBytecode::Program &program, const D3D12RenderState::RootSignature &rootsig, const ShaderReflection &refl, const ShaderBindpointMapping &mapping, - ShaderDebugTrace &debugTrace) + DXBCDebug::GlobalState &global, + rdcarray &sourceVars) { WrappedID3D12RootSignature *pD3D12RootSig = pDevice->GetResourceManager()->GetCurrentAs(rootsig.rootsig); @@ -1058,7 +1060,7 @@ void GatherConstantBuffers(WrappedID3D12Device *pDevice, const DXBCBytecode::Pro UINT sizeBytes = sizeof(uint32_t) * RDCMIN(rootSigParam.Constants.Num32BitValues, (UINT)element.constants.size()); bytebuf cbufData((const byte *)element.constants.data(), sizeBytes); - AddCBufferToDebugTrace(program, debugTrace, refl, mapping, slot, cbufData); + AddCBufferToGlobalState(program, global, sourceVars, refl, mapping, slot, cbufData); } else if(rootSigParam.ParameterType == D3D12_ROOT_PARAMETER_TYPE_CBV && element.type == eRootCBV) { @@ -1067,7 +1069,7 @@ void GatherConstantBuffers(WrappedID3D12Device *pDevice, const DXBCBytecode::Pro ID3D12Resource *cbv = pDevice->GetResourceManager()->GetCurrentAs(element.id); bytebuf cbufData; pDevice->GetDebugManager()->GetBufferData(cbv, element.offset, 0, cbufData); - AddCBufferToDebugTrace(program, debugTrace, refl, mapping, slot, cbufData); + AddCBufferToGlobalState(program, global, sourceVars, refl, mapping, slot, cbufData); } else if(rootSigParam.ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE && element.type == eRootTable) @@ -1122,7 +1124,7 @@ void GatherConstantBuffers(WrappedID3D12Device *pDevice, const DXBCBytecode::Pro pDevice->GetResourceManager()->GetCurrentAs(resId); cbufData.clear(); pDevice->GetDebugManager()->GetBufferData(pCbvResource, element.offset, 0, cbufData); - AddCBufferToDebugTrace(program, debugTrace, refl, mapping, slot, cbufData); + AddCBufferToGlobalState(program, global, sourceVars, refl, mapping, slot, cbufData); } } } @@ -1131,28 +1133,26 @@ void GatherConstantBuffers(WrappedID3D12Device *pDevice, const DXBCBytecode::Pro } } -ShaderDebugTrace D3D12Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, - uint32_t idx, uint32_t instOffset, uint32_t vertOffset) +ShaderDebugTrace *D3D12Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, + uint32_t idx, uint32_t instOffset, uint32_t vertOffset) { RDCUNIMPLEMENTED("Vertex debugging not yet implemented for D3D12"); - ShaderDebugTrace ret; - return ret; + return new ShaderDebugTrace(); } #if D3D12SHADERDEBUG_PIXEL == 0 -ShaderDebugTrace D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, - uint32_t primitive) +ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, + uint32_t primitive) { RDCUNIMPLEMENTED("Pixel debugging not yet implemented for D3D12"); - ShaderDebugTrace ret; - return ret; + return new ShaderDebugTrace(); } #else -ShaderDebugTrace D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, - uint32_t primitive) +ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, + uint32_t primitive) { using namespace DXBC; using namespace DXBCBytecode; @@ -1164,20 +1164,18 @@ ShaderDebugTrace D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t const D3D12Pipe::State *pipelineState = GetD3D12PipelineState(); - ShaderDebugTrace empty; - // Fetch the disassembly info from the pixel shader const D3D12Pipe::Shader &pixelShader = pipelineState->pixelShader; WrappedID3D12Shader *ps = m_pDevice->GetResourceManager()->GetCurrentAs(pixelShader.resourceId); if(!ps) - return empty; + return new ShaderDebugTrace; DXBCContainer *dxbc = ps->GetDXBC(); const ShaderReflection &refl = ps->GetDetails(); if(!dxbc) - return empty; + return new ShaderDebugTrace; dxbc->GetDisassembly(); @@ -1230,7 +1228,7 @@ ShaderDebugTrace D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t if(outputSampleCount > 1) { RDCUNIMPLEMENTED("MSAA debugging not yet implemented for D3D12"); - return empty; + return new ShaderDebugTrace; } extractHlsl += R"( @@ -1298,7 +1296,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim "ps_5_0", &psBlob) != "") { RDCERR("Failed to create shader to extract inputs"); - return empty; + return new ShaderDebugTrace; } uint32_t structStride = sizeof(uint32_t) // uint hit; @@ -1343,7 +1341,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim { RDCERR("Failed to create buffer for pixel shader debugging HRESULT: %s", ToStr(hr).c_str()); SAFE_RELEASE(psBlob); - return empty; + return new ShaderDebugTrace; } // Create UAV of initial values buffer @@ -1410,7 +1408,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim SAFE_RELEASE(root); SAFE_RELEASE(psBlob); SAFE_RELEASE(pInitialValuesBuffer); - return empty; + return new ShaderDebugTrace; } SAFE_RELEASE(root); @@ -1435,7 +1433,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim SAFE_RELEASE(psBlob); SAFE_RELEASE(pInitialValuesBuffer); SAFE_RELEASE(pRootSignature); - return empty; + return new ShaderDebugTrace; } // Add the descriptor for our UAV, then clear it @@ -1459,7 +1457,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim SAFE_RELEASE(pInitialValuesBuffer); SAFE_RELEASE(pRootSignature); SAFE_RELEASE(initialPso); - return empty; + return new ShaderDebugTrace; } { @@ -1500,7 +1498,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim if(buf[0].numHits == 0) { RDCLOG("No hit for this event"); - return empty; + return new ShaderDebugTrace; } // if we encounter multiple hits at our destination pixel co-ord (or any other) we @@ -1567,43 +1565,28 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim if(pWinnerHit == NULL) { RDCLOG("Couldn't find any pixels that passed depth test at target coordinates"); - return empty; + return new ShaderDebugTrace; } - ShaderDebugTrace ret; - - GlobalState global; - global.PopulateGroupshared(dxbc->GetDXBCByteCode()); - - State quad[4] = { - // top-left - State(0, &global, dxbc), - // top-right - State(1, &global, dxbc), - // bottom-left - State(2, &global, dxbc), - // bottom-right - State(3, &global, dxbc), - }; - - CreateShaderDebugStateAndTrace(quad[destIdx], ret, dxbc, refl, origPSO->PS()->GetMapping()); + InterpretDebugger *interpreter = new InterpretDebugger; + ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, origPSO->PS()->GetMapping(), destIdx); + GlobalState &global = interpreter->global; + ThreadState &state = interpreter->activeLane(); // Fetch constant buffer data from root signature GatherConstantBuffers(m_pDevice, *dxbc->GetDXBCByteCode(), rs.graphics, refl, - origPSO->PS()->GetMapping(), ret); - - global.constantBlocks = ret.constantBlocks; + origPSO->PS()->GetMapping(), global, ret->sourceVars); { DebugHit *pHit = pWinnerHit; - rdcarray &ins = quad[destIdx].inputs; + rdcarray &ins = state.inputs; if(!ins.empty() && ins.back().name == "vCoverage") ins.back().value.u.x = pHit->coverage; - quad[destIdx].semantics.coverage = pHit->coverage; - quad[destIdx].semantics.primID = pHit->primitive; - quad[destIdx].semantics.isFrontFace = pHit->isFrontFace; + state.semantics.coverage = pHit->coverage; + state.semantics.primID = pHit->primitive; + state.semantics.isFrontFace = pHit->isFrontFace; uint32_t *data = &pHit->rawdata; @@ -1613,7 +1596,9 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim if(*pos_ddx != 1.0f) { RDCERR("Derivatives invalid"); - return empty; + delete interpreter; + delete ret; + return new ShaderDebugTrace; } data++; @@ -1656,162 +1641,26 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim for(int i = 0; i < 4; i++) { - quad[i].inputs = quad[destIdx].inputs; - quad[i].semantics = quad[destIdx].semantics; - quad[i].variables = quad[destIdx].variables; - quad[i].SetHelper(); + if(i != destIdx) + { + interpreter->workgroup[i].inputs = state.inputs; + interpreter->workgroup[i].semantics = state.semantics; + interpreter->workgroup[i].variables = state.variables; + interpreter->workgroup[i].SetHelper(); + } } // TODO: Handle inputs that were evaluated at sample granularity (MSAA) - ApplyAllDerivatives(global, quad, destIdx, initialValues, (float *)data); + ApplyAllDerivatives(global, interpreter->workgroup, destIdx, initialValues, (float *)data); } - ret.inputs = quad[destIdx].inputs; + ret->constantBlocks = global.constantBlocks; + ret->inputs = state.inputs; - rdcarray states; + ret->hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); - dxbc->FillStateInstructionInfo(quad[destIdx]); - - states.push_back(quad[destIdx]); - - // ping pong between so that we can have 'current' quad to update into new one - State quad2[4] = { - State(0, &global, dxbc), State(1, &global, dxbc), State(2, &global, dxbc), - State(3, &global, dxbc), - }; - - State *curquad = quad; - State *newquad = quad2; - - // marks any threads stalled waiting for others to catch up - bool activeMask[4] = {true, true, true, true}; - - int cycleCounter = 0; - - D3D12MarkerRegion simloop(m_pDevice->GetQueue()->GetReal(), "Simulation Loop"); - - D3D12DebugAPIWrapper apiWrapper(m_pDevice, dxbc, global); - - // simulate lockstep until all threads are finished - bool finished = true; - do - { - for(size_t i = 0; i < 4; i++) - { - if(activeMask[i]) - newquad[i] = curquad[i].GetNext(&apiWrapper, curquad); - else - newquad[i] = curquad[i]; - } - - State *a = curquad; - curquad = newquad; - newquad = a; - - // if our destination quad is paused don't record multiple identical states. - if(activeMask[destIdx]) - { - State &s = curquad[destIdx]; - - dxbc->FillStateInstructionInfo(s); - - 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 - // since using derivatives is invalid. - - // Threads diverge either in ifs, loops, or switches. Due to the nature of the bytecode, - // all threads *must* pass through the same exit instruction for each, there's no jumping - // around with gotos. Note also for the same reason, the only time threads are on earlier - // instructions is if they are still catching up to a thread that has exited the control - // flow. - - // So the scheme is as follows: - // * If all threads have the same nextInstruction, just continue we are still in lockstep. - // * If threads are out of lockstep, find any thread which has nextInstruction pointing - // immediately *after* an ENDIF, ENDLOOP or ENDSWITCH. Pointing directly at one is not - // an indication the thread is done, as the next step for an ENDLOOP will jump back to - // the matching LOOP and continue iterating. - // * Pause any thread matching the above until all threads are pointing to the same - // instruction. By the assumption above, all threads will eventually pass through this - // terminating instruction so we just pause any other threads and don't do anything - // until the control flow has converged and we can continue stepping in lockstep. - - // mark all threads as active again. - // if we've converged, or we were never diverged, this keeps everything ticking - activeMask[0] = activeMask[1] = activeMask[2] = activeMask[3] = true; - - if(curquad[0].nextInstruction != curquad[1].nextInstruction || - curquad[0].nextInstruction != curquad[2].nextInstruction || - curquad[0].nextInstruction != curquad[3].nextInstruction) - { - // this isn't *perfect* but it will still eventually continue. We look for the most - // advanced thread, and check to see if it's just finished a control flow. If it has - // then we assume it's at the convergence point and wait for every other thread to - // catch up, pausing any threads that reach the convergence point before others. - - // Note this might mean we don't have any threads paused even within divergent flow. - // This is fine and all we care about is pausing to make sure threads don't run ahead - // into code that should be lockstep. We don't care at all about what they do within - // the code that is divergent. - - // The reason this isn't perfect is that the most advanced thread could be on an - // inner loop or inner if, not the convergence point, and we could be pausing it - // fruitlessly. Worse still - it could be on a branch none of the other threads will - // take so they will never reach that exact instruction. - // But we know that all threads will eventually go through the convergence point, so - // even in that worst case if we didn't pick the right waiting point, another thread - // will overtake and become the new most advanced thread and the previous waiting - // thread will resume. So in this case we caused a thread to wait more than it should - // have but that's not a big deal as it's within divergent flow so they don't have to - // stay in lockstep. Also if all threads will eventually pass that point we picked, - // we just waited to converge even in technically divergent code which is also - // harmless. - - // Phew! - - uint32_t convergencePoint = 0; - - // find which thread is most advanced - for(size_t i = 0; i < 4; i++) - if(curquad[i].nextInstruction > convergencePoint) - convergencePoint = curquad[i].nextInstruction; - - if(convergencePoint > 0) - { - OpcodeType op = dxbc->GetDXBCByteCode()->GetInstruction(convergencePoint - 1).operation; - - // if the most advnaced thread hasn't just finished control flow, then all - // threads are still running, so don't converge - if(op != OPCODE_ENDIF && op != OPCODE_ENDLOOP && op != OPCODE_ENDSWITCH) - convergencePoint = 0; - } - - // pause any threads at that instruction (could be none) - for(size_t i = 0; i < 4; i++) - if(curquad[i].nextInstruction == convergencePoint) - activeMask[i] = false; - } - - finished = curquad[destIdx].Finished(); - - cycleCounter++; - - if(cycleCounter == SHADER_DEBUG_WARN_THRESHOLD) - { - if(PromptDebugTimeout(cycleCounter)) - break; - } - } while(!finished); - - ret.states = states; - - ret.hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); - - dxbc->FillTraceLineInfo(ret); + dxbc->FillTraceLineInfo(*ret); return ret; } @@ -1820,18 +1669,17 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim #if D3D12SHADERDEBUG_THREAD == 0 -ShaderDebugTrace D3D12Replay::DebugThread(uint32_t eventId, const uint32_t groupid[3], - const uint32_t threadid[3]) +ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId, const uint32_t groupid[3], + const uint32_t threadid[3]) { RDCUNIMPLEMENTED("Compute shader debugging not yet implemented for D3D12"); - ShaderDebugTrace ret; - return ret; + return new ShaderDebugTrace(); } #else -ShaderDebugTrace D3D12Replay::DebugThread(uint32_t eventId, const uint32_t groupid[3], - const uint32_t threadid[3]) +ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId, const uint32_t groupid[3], + const uint32_t threadid[3]) { using namespace DXBCBytecode; using namespace DXBCDebug; @@ -1841,78 +1689,47 @@ ShaderDebugTrace D3D12Replay::DebugThread(uint32_t eventId, const uint32_t group StringFormat::Fmt("DebugThread @ %u: [%u, %u, %u] (%u, %u, %u)", eventId, groupid[0], groupid[1], groupid[2], threadid[0], threadid[1], threadid[2])); - ShaderDebugTrace empty; - const D3D12Pipe::State *pipelineState = GetD3D12PipelineState(); const D3D12Pipe::Shader &computeShader = pipelineState->computeShader; WrappedID3D12Shader *cs = m_pDevice->GetResourceManager()->GetCurrentAs(computeShader.resourceId); if(!cs) - return empty; + return new ShaderDebugTrace; DXBC::DXBCContainer *dxbc = cs->GetDXBC(); const ShaderReflection &refl = cs->GetDetails(); if(!dxbc) - return empty; + return new ShaderDebugTrace; dxbc->GetDisassembly(); D3D12RenderState &rs = m_pDevice->GetQueue()->GetCommandData()->m_RenderState; - ShaderDebugTrace ret; - WrappedID3D12PipelineState *pso = m_pDevice->GetResourceManager()->GetCurrentAs(rs.pipe); - GlobalState global; - global.PopulateGroupshared(dxbc->GetDXBCByteCode()); - State initialState(-1, &global, dxbc); - CreateShaderDebugStateAndTrace(initialState, ret, dxbc, refl, pso->CS()->GetMapping()); + InterpretDebugger *interpreter = new InterpretDebugger; + ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, pso->CS()->GetMapping(), 0); + GlobalState &global = interpreter->global; + ThreadState &state = interpreter->activeLane(); GatherConstantBuffers(m_pDevice, *dxbc->GetDXBCByteCode(), rs.compute, refl, - pso->CS()->GetMapping(), ret); - - global.constantBlocks = ret.constantBlocks; + pso->CS()->GetMapping(), global, ret->sourceVars); for(int i = 0; i < 3; i++) { - initialState.semantics.GroupID[i] = groupid[i]; - initialState.semantics.ThreadID[i] = threadid[i]; + state.semantics.GroupID[i] = groupid[i]; + state.semantics.ThreadID[i] = threadid[i]; } - rdcarray states; + ret->constantBlocks = global.constantBlocks; - dxbc->FillStateInstructionInfo(initialState); + ret->hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); - states.push_back((State)initialState); - - D3D12DebugAPIWrapper apiWrapper(m_pDevice, dxbc, global); - - for(int cycleCounter = 0;; cycleCounter++) - { - if(initialState.Finished()) - break; - - initialState = initialState.GetNext(&apiWrapper, NULL); - - dxbc->FillStateInstructionInfo(initialState); - - states.push_back((State)initialState); - - if(cycleCounter == SHADER_DEBUG_WARN_THRESHOLD) - { - if(PromptDebugTimeout(cycleCounter)) - break; - } - } - - ret.states = states; - - ret.hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); - - dxbc->FillTraceLineInfo(ret); + dxbc->FillTraceLineInfo(*ret); + // add fake inputs for semantics for(size_t i = 0; i < dxbc->GetDXBCByteCode()->GetNumDeclarations(); i++) { const DXBCBytecode::Declaration &decl = dxbc->GetDXBCByteCode()->GetDeclaration(i); @@ -1931,38 +1748,37 @@ ShaderDebugTrace D3D12Replay::DebugThread(uint32_t eventId, const uint32_t group switch(decl.operand.type) { case TYPE_INPUT_THREAD_GROUP_ID: - memcpy(v.value.uv, initialState.semantics.GroupID, sizeof(uint32_t) * 3); + memcpy(v.value.uv, state.semantics.GroupID, sizeof(uint32_t) * 3); v.columns = 3; break; case TYPE_INPUT_THREAD_ID_IN_GROUP: - memcpy(v.value.uv, initialState.semantics.ThreadID, sizeof(uint32_t) * 3); + memcpy(v.value.uv, state.semantics.ThreadID, sizeof(uint32_t) * 3); v.columns = 3; break; case TYPE_INPUT_THREAD_ID: - v.value.u.x = initialState.semantics.GroupID[0] * - dxbc->GetReflection()->DispatchThreadsDimension[0] + - initialState.semantics.ThreadID[0]; - v.value.u.y = initialState.semantics.GroupID[1] * - dxbc->GetReflection()->DispatchThreadsDimension[1] + - initialState.semantics.ThreadID[1]; - v.value.u.z = initialState.semantics.GroupID[2] * - dxbc->GetReflection()->DispatchThreadsDimension[2] + - initialState.semantics.ThreadID[2]; + v.value.u.x = + state.semantics.GroupID[0] * dxbc->GetReflection()->DispatchThreadsDimension[0] + + state.semantics.ThreadID[0]; + v.value.u.y = + state.semantics.GroupID[1] * dxbc->GetReflection()->DispatchThreadsDimension[1] + + state.semantics.ThreadID[1]; + v.value.u.z = + state.semantics.GroupID[2] * dxbc->GetReflection()->DispatchThreadsDimension[2] + + state.semantics.ThreadID[2]; v.columns = 3; break; case TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED: - v.value.u.x = initialState.semantics.ThreadID[2] * - dxbc->GetReflection()->DispatchThreadsDimension[0] * - dxbc->GetReflection()->DispatchThreadsDimension[1] + - initialState.semantics.ThreadID[1] * - dxbc->GetReflection()->DispatchThreadsDimension[0] + - initialState.semantics.ThreadID[0]; + v.value.u.x = + state.semantics.ThreadID[2] * dxbc->GetReflection()->DispatchThreadsDimension[0] * + dxbc->GetReflection()->DispatchThreadsDimension[1] + + state.semantics.ThreadID[1] * dxbc->GetReflection()->DispatchThreadsDimension[0] + + state.semantics.ThreadID[0]; v.columns = 1; break; default: v.columns = 4; break; } - ret.inputs.push_back(v); + ret->inputs.push_back(v); } } @@ -1970,3 +1786,17 @@ ShaderDebugTrace D3D12Replay::DebugThread(uint32_t eventId, const uint32_t group } #endif // D3D12SHADERDEBUG_THREAD + +rdcarray D3D12Replay::ContinueDebug(ShaderDebugger *debugger) +{ + DXBCDebug::InterpretDebugger *interpreter = (DXBCDebug::InterpretDebugger *)debugger; + + if(!interpreter) + return NULL; + + D3D12DebugAPIWrapper apiWrapper(m_pDevice, interpreter->dxbc, interpreter->global); + + D3D12MarkerRegion region(m_pDevice->GetQueue()->GetReal(), "ContinueDebug Simulation Loop"); + + return interpreter->ContinueDebug(&apiWrapper); +} diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index e5b4b7ea8..daa75a516 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -3453,25 +3453,31 @@ rdcarray GLReplay::PixelHistory(rdcarray events, return {}; } -ShaderDebugTrace GLReplay::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, - uint32_t idx, uint32_t instOffset, uint32_t vertOffset) +ShaderDebugTrace *GLReplay::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, + uint32_t idx, uint32_t instOffset, uint32_t vertOffset) { GLNOTIMP("DebugVertex"); - return ShaderDebugTrace(); + return new ShaderDebugTrace(); } -ShaderDebugTrace GLReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, - uint32_t primitive) +ShaderDebugTrace *GLReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, + uint32_t primitive) { GLNOTIMP("DebugPixel"); - return ShaderDebugTrace(); + return new ShaderDebugTrace(); } -ShaderDebugTrace GLReplay::DebugThread(uint32_t eventId, const uint32_t groupid[3], - const uint32_t threadid[3]) +ShaderDebugTrace *GLReplay::DebugThread(uint32_t eventId, const uint32_t groupid[3], + const uint32_t threadid[3]) { GLNOTIMP("DebugThread"); - return ShaderDebugTrace(); + return new ShaderDebugTrace(); +} + +rdcarray GLReplay::ContinueDebug(ShaderDebugger *debugger) +{ + GLNOTIMP("ContinueDebug"); + return {}; } void GLReplay::MakeCurrentReplayContext(GLWindowingData *ctx) diff --git a/renderdoc/driver/gl/gl_replay.h b/renderdoc/driver/gl/gl_replay.h index 062a59126..023fe8a7e 100644 --- a/renderdoc/driver/gl/gl_replay.h +++ b/renderdoc/driver/gl/gl_replay.h @@ -217,12 +217,13 @@ public: rdcarray PixelHistory(rdcarray events, ResourceId target, uint32_t x, uint32_t y, const Subresource &sub, CompType typeCast); - ShaderDebugTrace DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, uint32_t idx, - uint32_t instOffset, uint32_t vertOffset); - ShaderDebugTrace DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, - uint32_t primitive); - ShaderDebugTrace DebugThread(uint32_t eventId, const uint32_t groupid[3], - const uint32_t threadid[3]); + ShaderDebugTrace *DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, uint32_t idx, + uint32_t instOffset, uint32_t vertOffset); + ShaderDebugTrace *DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, + uint32_t primitive); + ShaderDebugTrace *DebugThread(uint32_t eventId, const uint32_t groupid[3], + const uint32_t threadid[3]); + rdcarray ContinueDebug(ShaderDebugger *debugger); uint32_t PickVertex(uint32_t eventId, int32_t width, int32_t height, const MeshDisplay &cfg, uint32_t x, uint32_t y); diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp index e7e17b498..ea576ded6 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp @@ -62,7 +62,7 @@ static float flush_denorm(const float f) return ret; } -VarType State::OperationType(const DXBCBytecode::OpcodeType &op) const +VarType OperationType(const DXBCBytecode::OpcodeType &op) { switch(op) { @@ -238,7 +238,7 @@ VarType State::OperationType(const DXBCBytecode::OpcodeType &op) const } } -bool State::OperationFlushing(const DXBCBytecode::OpcodeType &op) const +bool OperationFlushing(const DXBCBytecode::OpcodeType &op) { switch(op) { @@ -1079,26 +1079,29 @@ ShaderVariable sub(const ShaderVariable &a, const ShaderVariable &b, const VarTy return add(a, neg(b, type), type); } -State::State(int quadIdx, GlobalState *globalState, const DXBC::DXBCContainer *dxbc) +ThreadState::ThreadState(int workgroupIdx, GlobalState &globalState, const DXBC::DXBCContainer *dxbc) : global(globalState) { - quadIndex = quadIdx; - nextInstruction = 0; - flags = ShaderEvents::NoEvent; + workgroupIndex = workgroupIdx; done = false; + nextInstruction = 0; reflection = dxbc->GetReflection(); program = dxbc->GetDXBCByteCode(); RDCEraseEl(semantics); + + program->SetupRegisterFile(variables); } -bool State::Finished() const +bool ThreadState::Finished() const { return program && (done || nextInstruction >= (int)program->GetNumInstructions()); } -bool State::AssignValue(ShaderVariable &dst, uint32_t dstIndex, const ShaderVariable &src, - uint32_t srcIndex, bool flushDenorm) +ShaderEvents ThreadState::AssignValue(ShaderVariable &dst, uint32_t dstIndex, + const ShaderVariable &src, uint32_t srcIndex, bool flushDenorm) { + ShaderEvents flags = ShaderEvents::NoEvent; + if(src.type == VarType::Float) { float ft = src.value.fv[srcIndex]; @@ -1112,17 +1115,16 @@ bool State::AssignValue(ShaderVariable &dst, uint32_t dstIndex, const ShaderVari flags |= ShaderEvents::GeneratedNanOrInf; } - bool ret = (dst.value.uv[dstIndex] != src.value.uv[srcIndex]); - dst.value.uv[dstIndex] = src.value.uv[srcIndex]; if(flushDenorm && src.type == VarType::Float) dst.value.fv[dstIndex] = flush_denorm(dst.value.fv[dstIndex]); - return ret; + return flags; } -void State::SetDst(const Operand &dstoper, const Operation &op, const ShaderVariable &val) +void ThreadState::SetDst(ShaderDebugState *state, const Operand &dstoper, const Operation &op, + const ShaderVariable &val) { ShaderVariable *v = NULL; @@ -1145,10 +1147,6 @@ void State::SetDst(const Operand &dstoper, const Operation &op, const ShaderVari } } - DebugVariableReference range; - if(v) - range.name = v->name; - switch(dstoper.type) { case TYPE_TEMP: @@ -1162,8 +1160,6 @@ void State::SetDst(const Operand &dstoper, const Operation &op, const ShaderVari { uint32_t idx = program->GetRegisterIndex(dstoper.type, indices[0]); - range.type = DebugVariableType::Variable; - if(idx < variables.size()) v = &variables[idx]; break; @@ -1189,6 +1185,8 @@ void State::SetDst(const Operand &dstoper, const Operation &op, const ShaderVari } } + ShaderVariable *changeVar = v; + if(dstoper.type == TYPE_INDEXABLE_TEMP) { RDCASSERTEQUAL(dstoper.indices.size(), 2); @@ -1224,18 +1222,16 @@ void State::SetDst(const Operand &dstoper, const Operation &op, const ShaderVari if(op.saturate) right = sat(right, OperationType(op.operation)); + ShaderVariableChange change = {*changeVar}; + + ShaderEvents flags = ShaderEvents::NoEvent; + if(dstoper.comps[0] != 0xff && dstoper.comps[1] == 0xff && dstoper.comps[2] == 0xff && dstoper.comps[3] == 0xff) { RDCASSERT(dstoper.comps[0] != 0xff); - bool changed = AssignValue(*v, dstoper.comps[0], right, 0, flushDenorm); - - if(changed && !range.name.empty()) - { - range.component = dstoper.comps[0]; - modified.push_back(range); - } + flags |= AssignValue(*v, dstoper.comps[0], right, 0, flushDenorm); } else { @@ -1246,37 +1242,33 @@ void State::SetDst(const Operand &dstoper, const Operation &op, const ShaderVari if(dstoper.comps[i] != 0xff) { RDCASSERT(dstoper.comps[i] < v->columns); - bool changed = AssignValue(*v, dstoper.comps[i], right, dstoper.comps[i], flushDenorm); + flags |= AssignValue(*v, dstoper.comps[i], right, dstoper.comps[i], flushDenorm); compsWritten++; - - if(changed && !range.name.empty()) - { - range.component = dstoper.comps[i]; - modified.push_back(range); - } } } if(compsWritten == 0) - { - bool changed = AssignValue(*v, 0, right, 0, flushDenorm); + flags |= AssignValue(*v, 0, right, 0, flushDenorm); + } - if(changed && !range.name.empty()) - { - range.component = 0; - modified.push_back(range); - } - } + if(state) + { + state->flags |= flags; + change.after = *changeVar; + state->changes.push_back(change); } } -ShaderVariable State::DDX(bool fine, State quad[4], const DXBCBytecode::Operand &oper, - const DXBCBytecode::Operation &op) const +ShaderVariable ThreadState::DDX(bool fine, const rdcarray &quad, + const DXBCBytecode::Operand &oper, + const DXBCBytecode::Operation &op) const { ShaderVariable ret; VarType optype = OperationType(op.operation); + int quadIndex = workgroupIndex; + if(!fine) { // use top-left pixel's neighbours @@ -1295,13 +1287,16 @@ ShaderVariable State::DDX(bool fine, State quad[4], const DXBCBytecode::Operand return ret; } -ShaderVariable State::DDY(bool fine, State quad[4], const DXBCBytecode::Operand &oper, - const DXBCBytecode::Operation &op) const +ShaderVariable ThreadState::DDY(bool fine, const rdcarray &quad, + const DXBCBytecode::Operand &oper, + const DXBCBytecode::Operation &op) const { ShaderVariable ret; VarType optype = OperationType(op.operation); + int quadIndex = workgroupIndex; + if(!fine) { // use top-left pixel's neighbours @@ -1320,7 +1315,7 @@ ShaderVariable State::DDY(bool fine, State quad[4], const DXBCBytecode::Operand return ret; } -ShaderVariable State::GetSrc(const Operand &oper, const Operation &op, bool allowFlushing) const +ShaderVariable ThreadState::GetSrc(const Operand &oper, const Operation &op, bool allowFlushing) const { ShaderVariable v, s; @@ -1468,17 +1463,17 @@ ShaderVariable State::GetSrc(const Operand &oper, const Operation &op, bool allo } } - RDCASSERTMSG("Invalid cbuffer lookup", cb != -1 && cb < global->constantBlocks.count(), cb, - global->constantBlocks.count()); + RDCASSERTMSG("Invalid cbuffer lookup", cb != -1 && cb < global.constantBlocks.count(), cb, + global.constantBlocks.count()); - if(cb >= 0 && cb < global->constantBlocks.count()) + if(cb >= 0 && cb < global.constantBlocks.count()) { RDCASSERTMSG("Out of bounds cbuffer lookup", - cbLookup < (uint32_t)global->constantBlocks[cb].members.count(), cbLookup, - global->constantBlocks[cb].members.count()); + cbLookup < (uint32_t)global.constantBlocks[cb].members.count(), cbLookup, + global.constantBlocks[cb].members.count()); - if(cbLookup < (uint32_t)global->constantBlocks[cb].members.count()) - v = s = global->constantBlocks[cb].members[cbLookup]; + if(cbLookup < (uint32_t)global.constantBlocks[cb].members.count()) + v = s = global.constantBlocks[cb].members[cbLookup]; else v = s = ShaderVariable("", 0U, 0U, 0U, 0U); } @@ -1790,20 +1785,22 @@ void FlattenVariables(const rdcstr &cbname, const rdcarray &cons } } -State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const +void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper, + const rdcarray &prevWorkgroup) { - State s = *this; + if(nextInstruction >= program->GetNumInstructions()) + return; - s.modified.clear(); + const Operation &op = program->GetInstruction((size_t)nextInstruction); - if(s.nextInstruction >= s.program->GetNumInstructions()) - return s; + apiWrapper->SetCurrentInstruction(nextInstruction); + nextInstruction++; - const Operation &op = s.program->GetInstruction((size_t)s.nextInstruction); + if(nextInstruction >= program->GetNumInstructions()) + nextInstruction--; - apiWrapper->SetCurrentInstruction(s.nextInstruction); - s.nextInstruction++; - s.flags = ShaderEvents::NoEvent; + if(state) + state->nextInstruction = nextInstruction; rdcarray srcOpers; @@ -1819,9 +1816,13 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const case OPCODE_DADD: case OPCODE_IADD: - case OPCODE_ADD: s.SetDst(op.operands[0], op, add(srcOpers[0], srcOpers[1], optype)); break; + case OPCODE_ADD: + SetDst(state, op.operands[0], op, add(srcOpers[0], srcOpers[1], optype)); + break; case OPCODE_DDIV: - case OPCODE_DIV: s.SetDst(op.operands[0], op, div(srcOpers[0], srcOpers[1], optype)); break; + case OPCODE_DIV: + SetDst(state, op.operands[0], op, div(srcOpers[0], srcOpers[1], optype)); + break; case OPCODE_UDIV: { ShaderVariable quot("", (uint32_t)0xffffffff, (uint32_t)0xffffffff, (uint32_t)0xffffffff, @@ -1840,11 +1841,11 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const if(op.operands[0].type != TYPE_NULL) { - s.SetDst(op.operands[0], op, quot); + SetDst(state, op.operands[0], op, quot); } if(op.operands[1].type != TYPE_NULL) { - s.SetDst(op.operands[1], op, rem); + SetDst(state, op.operands[1], op, rem); } break; } @@ -1857,7 +1858,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const ret.value.uv[i] = BitwiseReverseLSB16(srcOpers[0].value.uv[i]); } - s.SetDst(op.operands[0], op, ret); + SetDst(state, op.operands[0], op, ret); break; } @@ -1870,7 +1871,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const ret.value.uv[i] = PopCount(srcOpers[0].value.uv[i]); } - s.SetDst(op.operands[0], op, ret); + SetDst(state, op.operands[0], op, ret); break; } case OPCODE_FIRSTBIT_HI: @@ -1892,7 +1893,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const } } - s.SetDst(op.operands[0], op, ret); + SetDst(state, op.operands[0], op, ret); break; } case OPCODE_FIRSTBIT_LO: @@ -1906,7 +1907,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const ret.value.uv[i] = ~0U; } - s.SetDst(op.operands[0], op, ret); + SetDst(state, op.operands[0], op, ret); break; } case OPCODE_FIRSTBIT_SHI: @@ -1933,7 +1934,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const } } - s.SetDst(op.operands[0], op, ret); + SetDst(state, op.operands[0], op, ret); break; } case OPCODE_IMUL: @@ -1962,16 +1963,18 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const if(op.operands[0].type != TYPE_NULL) { - s.SetDst(op.operands[0], op, hi); + SetDst(state, op.operands[0], op, hi); } if(op.operands[1].type != TYPE_NULL) { - s.SetDst(op.operands[1], op, lo); + SetDst(state, op.operands[1], op, lo); } break; } case OPCODE_DMUL: - case OPCODE_MUL: s.SetDst(op.operands[0], op, mul(srcOpers[0], srcOpers[1], optype)); break; + case OPCODE_MUL: + SetDst(state, op.operands[0], op, mul(srcOpers[0], srcOpers[1], optype)); + break; case OPCODE_UADDC: { uint64_t src[4]; @@ -1986,13 +1989,13 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const for(int i = 0; i < 4; i++) dst[i] = (uint32_t)(src[i] & 0xffffffff); - s.SetDst(op.operands[0], op, ShaderVariable("", dst[0], dst[1], dst[2], dst[3])); + SetDst(state, op.operands[0], op, ShaderVariable("", dst[0], dst[1], dst[2], dst[3])); // if not null, set the carry bits if(op.operands[1].type != TYPE_NULL) - s.SetDst(op.operands[1], op, - ShaderVariable("", src[0] > 0xffffffff ? 1U : 0U, src[1] > 0xffffffff ? 1U : 0U, - src[2] > 0xffffffff ? 1U : 0U, src[3] > 0xffffffff ? 1U : 0U)); + SetDst(state, op.operands[1], op, + ShaderVariable("", src[0] > 0xffffffff ? 1U : 0U, src[1] > 0xffffffff ? 1U : 0U, + src[2] > 0xffffffff ? 1U : 0U, src[3] > 0xffffffff ? 1U : 0U)); break; } @@ -2016,12 +2019,12 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const for(int i = 0; i < 4; i++) dst[i] = (uint32_t)(result[0] & 0xffffffff); - s.SetDst(op.operands[0], op, ShaderVariable("", dst[0], dst[1], dst[2], dst[3])); + SetDst(state, op.operands[0], op, ShaderVariable("", dst[0], dst[1], dst[2], dst[3])); // if not null, mark where the borrow bits were used if(op.operands[1].type != TYPE_NULL) - s.SetDst( - op.operands[1], op, + SetDst( + state, op.operands[1], op, ShaderVariable("", result[0] <= 0xffffffff ? 1U : 0U, result[1] <= 0xffffffff ? 1U : 0U, result[2] <= 0xffffffff ? 1U : 0U, result[3] <= 0xffffffff ? 1U : 0U)); @@ -2031,7 +2034,8 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const case OPCODE_UMAD: case OPCODE_MAD: case OPCODE_DFMA: - s.SetDst(op.operands[0], op, add(mul(srcOpers[0], srcOpers[1], optype), srcOpers[2], optype)); + SetDst(state, op.operands[0], op, + add(mul(srcOpers[0], srcOpers[1], optype), srcOpers[2], optype)); break; case OPCODE_DP2: case OPCODE_DP3: @@ -2046,88 +2050,86 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const if(op.operation >= OPCODE_DP4) sum += dot.value.f.w; - s.SetDst(op.operands[0], op, ShaderVariable("", sum, sum, sum, sum)); + SetDst(state, op.operands[0], op, ShaderVariable("", sum, sum, sum, sum)); break; } case OPCODE_F16TOF32: { - s.SetDst(op.operands[0], op, - ShaderVariable("", flush_denorm(ConvertFromHalf(srcOpers[0].value.u.x & 0xffff)), - flush_denorm(ConvertFromHalf(srcOpers[0].value.u.y & 0xffff)), - flush_denorm(ConvertFromHalf(srcOpers[0].value.u.z & 0xffff)), - flush_denorm(ConvertFromHalf(srcOpers[0].value.u.w & 0xffff)))); + SetDst(state, op.operands[0], op, + ShaderVariable("", flush_denorm(ConvertFromHalf(srcOpers[0].value.u.x & 0xffff)), + flush_denorm(ConvertFromHalf(srcOpers[0].value.u.y & 0xffff)), + flush_denorm(ConvertFromHalf(srcOpers[0].value.u.z & 0xffff)), + flush_denorm(ConvertFromHalf(srcOpers[0].value.u.w & 0xffff)))); break; } case OPCODE_F32TOF16: { - s.SetDst(op.operands[0], op, - ShaderVariable("", (uint32_t)ConvertToHalf(flush_denorm(srcOpers[0].value.f.x)), - (uint32_t)ConvertToHalf(flush_denorm(srcOpers[0].value.f.y)), - (uint32_t)ConvertToHalf(flush_denorm(srcOpers[0].value.f.z)), - (uint32_t)ConvertToHalf(flush_denorm(srcOpers[0].value.f.w)))); + SetDst(state, op.operands[0], op, + ShaderVariable("", (uint32_t)ConvertToHalf(flush_denorm(srcOpers[0].value.f.x)), + (uint32_t)ConvertToHalf(flush_denorm(srcOpers[0].value.f.y)), + (uint32_t)ConvertToHalf(flush_denorm(srcOpers[0].value.f.z)), + (uint32_t)ConvertToHalf(flush_denorm(srcOpers[0].value.f.w)))); break; } case OPCODE_FRC: - s.SetDst(op.operands[0], op, - ShaderVariable("", srcOpers[0].value.f.x - floorf(srcOpers[0].value.f.x), - srcOpers[0].value.f.y - floorf(srcOpers[0].value.f.y), - srcOpers[0].value.f.z - floorf(srcOpers[0].value.f.z), - srcOpers[0].value.f.w - floorf(srcOpers[0].value.f.w))); + SetDst(state, op.operands[0], op, + ShaderVariable("", srcOpers[0].value.f.x - floorf(srcOpers[0].value.f.x), + srcOpers[0].value.f.y - floorf(srcOpers[0].value.f.y), + srcOpers[0].value.f.z - floorf(srcOpers[0].value.f.z), + srcOpers[0].value.f.w - floorf(srcOpers[0].value.f.w))); break; // positive infinity case OPCODE_ROUND_PI: - s.SetDst(op.operands[0], op, - ShaderVariable("", ceilf(srcOpers[0].value.f.x), ceilf(srcOpers[0].value.f.y), - ceilf(srcOpers[0].value.f.z), ceilf(srcOpers[0].value.f.w))); + SetDst(state, op.operands[0], op, + ShaderVariable("", ceilf(srcOpers[0].value.f.x), ceilf(srcOpers[0].value.f.y), + ceilf(srcOpers[0].value.f.z), ceilf(srcOpers[0].value.f.w))); break; // negative infinity case OPCODE_ROUND_NI: - s.SetDst(op.operands[0], op, - ShaderVariable("", floorf(srcOpers[0].value.f.x), floorf(srcOpers[0].value.f.y), - floorf(srcOpers[0].value.f.z), floorf(srcOpers[0].value.f.w))); + SetDst(state, op.operands[0], op, + ShaderVariable("", floorf(srcOpers[0].value.f.x), floorf(srcOpers[0].value.f.y), + floorf(srcOpers[0].value.f.z), floorf(srcOpers[0].value.f.w))); break; // towards zero case OPCODE_ROUND_Z: - s.SetDst(op.operands[0], op, - ShaderVariable("", srcOpers[0].value.f.x < 0 ? ceilf(srcOpers[0].value.f.x) - : floorf(srcOpers[0].value.f.x), - srcOpers[0].value.f.y < 0 ? ceilf(srcOpers[0].value.f.y) - : floorf(srcOpers[0].value.f.y), - srcOpers[0].value.f.z < 0 ? ceilf(srcOpers[0].value.f.z) - : floorf(srcOpers[0].value.f.z), - srcOpers[0].value.f.w < 0 ? ceilf(srcOpers[0].value.f.w) - : floorf(srcOpers[0].value.f.w))); + SetDst(state, op.operands[0], op, + ShaderVariable("", srcOpers[0].value.f.x < 0 ? ceilf(srcOpers[0].value.f.x) + : floorf(srcOpers[0].value.f.x), + srcOpers[0].value.f.y < 0 ? ceilf(srcOpers[0].value.f.y) + : floorf(srcOpers[0].value.f.y), + srcOpers[0].value.f.z < 0 ? ceilf(srcOpers[0].value.f.z) + : floorf(srcOpers[0].value.f.z), + srcOpers[0].value.f.w < 0 ? ceilf(srcOpers[0].value.f.w) + : floorf(srcOpers[0].value.f.w))); break; // to nearest even int (banker's rounding) case OPCODE_ROUND_NE: - s.SetDst(op.operands[0], op, - ShaderVariable("", round_ne(srcOpers[0].value.f.x), round_ne(srcOpers[0].value.f.y), - round_ne(srcOpers[0].value.f.z), round_ne(srcOpers[0].value.f.w))); + SetDst(state, op.operands[0], op, + ShaderVariable("", round_ne(srcOpers[0].value.f.x), round_ne(srcOpers[0].value.f.y), + round_ne(srcOpers[0].value.f.z), round_ne(srcOpers[0].value.f.w))); break; - case OPCODE_INEG: s.SetDst(op.operands[0], op, neg(srcOpers[0], optype)); break; + case OPCODE_INEG: SetDst(state, op.operands[0], op, neg(srcOpers[0], optype)); break; case OPCODE_IMIN: - s.SetDst( - op.operands[0], op, - ShaderVariable("", srcOpers[0].value.i.x < srcOpers[1].value.i.x ? srcOpers[0].value.i.x - : srcOpers[1].value.i.x, - srcOpers[0].value.i.y < srcOpers[1].value.i.y ? srcOpers[0].value.i.y - : srcOpers[1].value.i.y, - srcOpers[0].value.i.z < srcOpers[1].value.i.z ? srcOpers[0].value.i.z - : srcOpers[1].value.i.z, - srcOpers[0].value.i.w < srcOpers[1].value.i.w ? srcOpers[0].value.i.w - : srcOpers[1].value.i.w)); + SetDst(state, op.operands[0], op, + ShaderVariable("", srcOpers[0].value.i.x < srcOpers[1].value.i.x ? srcOpers[0].value.i.x + : srcOpers[1].value.i.x, + srcOpers[0].value.i.y < srcOpers[1].value.i.y ? srcOpers[0].value.i.y + : srcOpers[1].value.i.y, + srcOpers[0].value.i.z < srcOpers[1].value.i.z ? srcOpers[0].value.i.z + : srcOpers[1].value.i.z, + srcOpers[0].value.i.w < srcOpers[1].value.i.w ? srcOpers[0].value.i.w + : srcOpers[1].value.i.w)); break; case OPCODE_UMIN: - s.SetDst( - op.operands[0], op, - ShaderVariable("", srcOpers[0].value.u.x < srcOpers[1].value.u.x ? srcOpers[0].value.u.x - : srcOpers[1].value.u.x, - srcOpers[0].value.u.y < srcOpers[1].value.u.y ? srcOpers[0].value.u.y - : srcOpers[1].value.u.y, - srcOpers[0].value.u.z < srcOpers[1].value.u.z ? srcOpers[0].value.u.z - : srcOpers[1].value.u.z, - srcOpers[0].value.u.w < srcOpers[1].value.u.w ? srcOpers[0].value.u.w - : srcOpers[1].value.u.w)); + SetDst(state, op.operands[0], op, + ShaderVariable("", srcOpers[0].value.u.x < srcOpers[1].value.u.x ? srcOpers[0].value.u.x + : srcOpers[1].value.u.x, + srcOpers[0].value.u.y < srcOpers[1].value.u.y ? srcOpers[0].value.u.y + : srcOpers[1].value.u.y, + srcOpers[0].value.u.z < srcOpers[1].value.u.z ? srcOpers[0].value.u.z + : srcOpers[1].value.u.z, + srcOpers[0].value.u.w < srcOpers[1].value.u.w ? srcOpers[0].value.u.w + : srcOpers[1].value.u.w)); break; case OPCODE_DMIN: { @@ -2142,19 +2144,19 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const ShaderVariable r("", 0U, 0U, 0U, 0U); DoubleSet(r, dst); - s.SetDst(op.operands[0], op, r); + SetDst(state, op.operands[0], op, r); break; } case OPCODE_MIN: - s.SetDst(op.operands[0], op, - ShaderVariable("", dxbc_min(srcOpers[0].value.f.x, srcOpers[1].value.f.x), - dxbc_min(srcOpers[0].value.f.y, srcOpers[1].value.f.y), - dxbc_min(srcOpers[0].value.f.z, srcOpers[1].value.f.z), - dxbc_min(srcOpers[0].value.f.w, srcOpers[1].value.f.w))); + SetDst(state, op.operands[0], op, + ShaderVariable("", dxbc_min(srcOpers[0].value.f.x, srcOpers[1].value.f.x), + dxbc_min(srcOpers[0].value.f.y, srcOpers[1].value.f.y), + dxbc_min(srcOpers[0].value.f.z, srcOpers[1].value.f.z), + dxbc_min(srcOpers[0].value.f.w, srcOpers[1].value.f.w))); break; case OPCODE_UMAX: - s.SetDst( - op.operands[0], op, + SetDst( + state, op.operands[0], op, ShaderVariable("", srcOpers[0].value.u.x >= srcOpers[1].value.u.x ? srcOpers[0].value.u.x : srcOpers[1].value.u.x, srcOpers[0].value.u.y >= srcOpers[1].value.u.y ? srcOpers[0].value.u.y @@ -2165,8 +2167,8 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const : srcOpers[1].value.u.w)); break; case OPCODE_IMAX: - s.SetDst( - op.operands[0], op, + SetDst( + state, op.operands[0], op, ShaderVariable("", srcOpers[0].value.i.x >= srcOpers[1].value.i.x ? srcOpers[0].value.i.x : srcOpers[1].value.i.x, srcOpers[0].value.i.y >= srcOpers[1].value.i.y ? srcOpers[0].value.i.y @@ -2189,20 +2191,20 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const ShaderVariable r("", 0U, 0U, 0U, 0U); DoubleSet(r, dst); - s.SetDst(op.operands[0], op, r); + SetDst(state, op.operands[0], op, r); break; } case OPCODE_MAX: - s.SetDst(op.operands[0], op, - ShaderVariable("", dxbc_max(srcOpers[0].value.f.x, srcOpers[1].value.f.x), - dxbc_max(srcOpers[0].value.f.y, srcOpers[1].value.f.y), - dxbc_max(srcOpers[0].value.f.z, srcOpers[1].value.f.z), - dxbc_max(srcOpers[0].value.f.w, srcOpers[1].value.f.w))); + SetDst(state, op.operands[0], op, + ShaderVariable("", dxbc_max(srcOpers[0].value.f.x, srcOpers[1].value.f.x), + dxbc_max(srcOpers[0].value.f.y, srcOpers[1].value.f.y), + dxbc_max(srcOpers[0].value.f.z, srcOpers[1].value.f.z), + dxbc_max(srcOpers[0].value.f.w, srcOpers[1].value.f.w))); break; case OPCODE_SQRT: - s.SetDst(op.operands[0], op, - ShaderVariable("", sqrtf(srcOpers[0].value.f.x), sqrtf(srcOpers[0].value.f.y), - sqrtf(srcOpers[0].value.f.z), sqrtf(srcOpers[0].value.f.w))); + SetDst(state, op.operands[0], op, + ShaderVariable("", sqrtf(srcOpers[0].value.f.x), sqrtf(srcOpers[0].value.f.y), + sqrtf(srcOpers[0].value.f.z), sqrtf(srcOpers[0].value.f.w))); break; case OPCODE_DRCP: { @@ -2214,7 +2216,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const ShaderVariable r("", 0U, 0U, 0U, 0U); DoubleSet(r, ds); - s.SetDst(op.operands[0], op, r); + SetDst(state, op.operands[0], op, r); break; } @@ -2248,7 +2250,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const } } - s.SetDst(op.operands[0], op, dest); + SetDst(state, op.operands[0], op, dest); break; } case OPCODE_UBFE: @@ -2281,7 +2283,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const } } - s.SetDst(op.operands[0], op, dest); + SetDst(state, op.operands[0], op, dest); break; } case OPCODE_BFI: @@ -2304,7 +2306,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const (srcOpers[3].value.uv[comp] & ~bitmask)); } - s.SetDst(op.operands[0], op, dest); + SetDst(state, op.operands[0], op, dest); break; } case OPCODE_ISHL: @@ -2320,8 +2322,8 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const (op.operands[2].comps[2] < 4 && op.operands[2].comps[2] == 0xff)) shifts[3] = shifts[2] = shifts[1] = shifts[0]; - s.SetDst( - op.operands[0], op, + SetDst( + state, op.operands[0], op, ShaderVariable("", srcOpers[0].value.i.x << shifts[0], srcOpers[0].value.i.y << shifts[1], srcOpers[0].value.i.z << shifts[2], srcOpers[0].value.i.w << shifts[3])); break; @@ -2339,8 +2341,8 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const (op.operands[2].comps[2] < 4 && op.operands[2].comps[2] == 0xff)) shifts[3] = shifts[2] = shifts[1] = shifts[0]; - s.SetDst( - op.operands[0], op, + SetDst( + state, op.operands[0], op, ShaderVariable("", srcOpers[0].value.u.x >> shifts[0], srcOpers[0].value.u.y >> shifts[1], srcOpers[0].value.u.z >> shifts[2], srcOpers[0].value.u.w >> shifts[3])); break; @@ -2358,33 +2360,37 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const (op.operands[2].comps[2] < 4 && op.operands[2].comps[2] == 0xff)) shifts[3] = shifts[2] = shifts[1] = shifts[0]; - s.SetDst( - op.operands[0], op, + SetDst( + state, op.operands[0], op, ShaderVariable("", srcOpers[0].value.i.x >> shifts[0], srcOpers[0].value.i.y >> shifts[1], srcOpers[0].value.i.z >> shifts[2], srcOpers[0].value.i.w >> shifts[3])); break; } case OPCODE_AND: - s.SetDst(op.operands[0], op, ShaderVariable("", srcOpers[0].value.i.x & srcOpers[1].value.i.x, - srcOpers[0].value.i.y & srcOpers[1].value.i.y, - srcOpers[0].value.i.z & srcOpers[1].value.i.z, - srcOpers[0].value.i.w & srcOpers[1].value.i.w)); + SetDst(state, op.operands[0], op, + ShaderVariable("", srcOpers[0].value.i.x & srcOpers[1].value.i.x, + srcOpers[0].value.i.y & srcOpers[1].value.i.y, + srcOpers[0].value.i.z & srcOpers[1].value.i.z, + srcOpers[0].value.i.w & srcOpers[1].value.i.w)); break; case OPCODE_OR: - s.SetDst(op.operands[0], op, ShaderVariable("", srcOpers[0].value.i.x | srcOpers[1].value.i.x, - srcOpers[0].value.i.y | srcOpers[1].value.i.y, - srcOpers[0].value.i.z | srcOpers[1].value.i.z, - srcOpers[0].value.i.w | srcOpers[1].value.i.w)); + SetDst(state, op.operands[0], op, + ShaderVariable("", srcOpers[0].value.i.x | srcOpers[1].value.i.x, + srcOpers[0].value.i.y | srcOpers[1].value.i.y, + srcOpers[0].value.i.z | srcOpers[1].value.i.z, + srcOpers[0].value.i.w | srcOpers[1].value.i.w)); break; case OPCODE_XOR: - s.SetDst(op.operands[0], op, ShaderVariable("", srcOpers[0].value.u.x ^ srcOpers[1].value.u.x, - srcOpers[0].value.u.y ^ srcOpers[1].value.u.y, - srcOpers[0].value.u.z ^ srcOpers[1].value.u.z, - srcOpers[0].value.u.w ^ srcOpers[1].value.u.w)); + SetDst(state, op.operands[0], op, + ShaderVariable("", srcOpers[0].value.u.x ^ srcOpers[1].value.u.x, + srcOpers[0].value.u.y ^ srcOpers[1].value.u.y, + srcOpers[0].value.u.z ^ srcOpers[1].value.u.z, + srcOpers[0].value.u.w ^ srcOpers[1].value.u.w)); break; case OPCODE_NOT: - s.SetDst(op.operands[0], op, ShaderVariable("", ~srcOpers[0].value.u.x, ~srcOpers[0].value.u.y, - ~srcOpers[0].value.u.z, ~srcOpers[0].value.u.w)); + SetDst(state, op.operands[0], op, + ShaderVariable("", ~srcOpers[0].value.u.x, ~srcOpers[0].value.u.y, + ~srcOpers[0].value.u.z, ~srcOpers[0].value.u.w)); break; ///////////////////////////////////////////////////////////////////////////////////////////////////// @@ -2400,11 +2406,11 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const ShaderVariable calcResultB("calcB", 0.0f, 0.0f, 0.0f, 0.0f); if(apiWrapper->CalculateMathIntrinsic(op.operation, srcOpers[0], calcResultA, calcResultB)) { - s.SetDst(op.operands[0], op, calcResultA); + SetDst(state, op.operands[0], op, calcResultA); } else { - return s; + return; } break; } @@ -2415,13 +2421,13 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const if(apiWrapper->CalculateMathIntrinsic(OPCODE_SINCOS, srcOpers[1], calcResultA, calcResultB)) { if(op.operands[0].type != TYPE_NULL) - s.SetDst(op.operands[0], op, calcResultA); + SetDst(state, op.operands[0], op, calcResultA); if(op.operands[1].type != TYPE_NULL) - s.SetDst(op.operands[1], op, calcResultB); + SetDst(state, op.operands[1], op, calcResultB); } else { - return s; + return; } break; } @@ -2434,57 +2440,53 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const case OPCODE_SYNC: // might never need to implement this. Who knows! break; case OPCODE_DMOV: - case OPCODE_MOV: s.SetDst(op.operands[0], op, srcOpers[0]); break; + case OPCODE_MOV: SetDst(state, op.operands[0], op, srcOpers[0]); break; case OPCODE_DMOVC: - s.SetDst( - op.operands[0], op, - ShaderVariable("", srcOpers[0].value.u.x ? srcOpers[1].value.u.x : srcOpers[2].value.u.x, - srcOpers[0].value.u.x ? srcOpers[1].value.u.y : srcOpers[2].value.u.y, - srcOpers[0].value.u.y ? srcOpers[1].value.u.z : srcOpers[2].value.u.z, - srcOpers[0].value.u.y ? srcOpers[1].value.u.w : srcOpers[2].value.u.w)); + SetDst(state, op.operands[0], op, + ShaderVariable("", srcOpers[0].value.u.x ? srcOpers[1].value.u.x : srcOpers[2].value.u.x, + srcOpers[0].value.u.x ? srcOpers[1].value.u.y : srcOpers[2].value.u.y, + srcOpers[0].value.u.y ? srcOpers[1].value.u.z : srcOpers[2].value.u.z, + srcOpers[0].value.u.y ? srcOpers[1].value.u.w : srcOpers[2].value.u.w)); break; case OPCODE_MOVC: - s.SetDst( - op.operands[0], op, - ShaderVariable("", srcOpers[0].value.i.x ? srcOpers[1].value.i.x : srcOpers[2].value.i.x, - srcOpers[0].value.i.y ? srcOpers[1].value.i.y : srcOpers[2].value.i.y, - srcOpers[0].value.i.z ? srcOpers[1].value.i.z : srcOpers[2].value.i.z, - srcOpers[0].value.i.w ? srcOpers[1].value.i.w : srcOpers[2].value.i.w)); + SetDst(state, op.operands[0], op, + ShaderVariable("", srcOpers[0].value.i.x ? srcOpers[1].value.i.x : srcOpers[2].value.i.x, + srcOpers[0].value.i.y ? srcOpers[1].value.i.y : srcOpers[2].value.i.y, + srcOpers[0].value.i.z ? srcOpers[1].value.i.z : srcOpers[2].value.i.z, + srcOpers[0].value.i.w ? srcOpers[1].value.i.w : srcOpers[2].value.i.w)); break; case OPCODE_SWAPC: - s.SetDst( - op.operands[0], op, - ShaderVariable("", srcOpers[1].value.i.x ? srcOpers[3].value.i.x : srcOpers[2].value.i.x, - srcOpers[1].value.i.y ? srcOpers[3].value.i.y : srcOpers[2].value.i.y, - srcOpers[1].value.i.z ? srcOpers[3].value.i.z : srcOpers[2].value.i.z, - srcOpers[1].value.i.w ? srcOpers[3].value.i.w : srcOpers[2].value.i.w)); + SetDst(state, op.operands[0], op, + ShaderVariable("", srcOpers[1].value.i.x ? srcOpers[3].value.i.x : srcOpers[2].value.i.x, + srcOpers[1].value.i.y ? srcOpers[3].value.i.y : srcOpers[2].value.i.y, + srcOpers[1].value.i.z ? srcOpers[3].value.i.z : srcOpers[2].value.i.z, + srcOpers[1].value.i.w ? srcOpers[3].value.i.w : srcOpers[2].value.i.w)); - s.SetDst( - op.operands[1], op, - ShaderVariable("", srcOpers[1].value.i.x ? srcOpers[2].value.i.x : srcOpers[3].value.i.x, - srcOpers[1].value.i.y ? srcOpers[2].value.i.y : srcOpers[3].value.i.y, - srcOpers[1].value.i.z ? srcOpers[2].value.i.z : srcOpers[3].value.i.z, - srcOpers[1].value.i.w ? srcOpers[2].value.i.w : srcOpers[3].value.i.w)); + SetDst(state, op.operands[1], op, + ShaderVariable("", srcOpers[1].value.i.x ? srcOpers[2].value.i.x : srcOpers[3].value.i.x, + srcOpers[1].value.i.y ? srcOpers[2].value.i.y : srcOpers[3].value.i.y, + srcOpers[1].value.i.z ? srcOpers[2].value.i.z : srcOpers[3].value.i.z, + srcOpers[1].value.i.w ? srcOpers[2].value.i.w : srcOpers[3].value.i.w)); break; case OPCODE_ITOF: - s.SetDst(op.operands[0], op, - ShaderVariable("", (float)srcOpers[0].value.i.x, (float)srcOpers[0].value.i.y, - (float)srcOpers[0].value.i.z, (float)srcOpers[0].value.i.w)); + SetDst(state, op.operands[0], op, + ShaderVariable("", (float)srcOpers[0].value.i.x, (float)srcOpers[0].value.i.y, + (float)srcOpers[0].value.i.z, (float)srcOpers[0].value.i.w)); break; case OPCODE_UTOF: - s.SetDst(op.operands[0], op, - ShaderVariable("", (float)srcOpers[0].value.u.x, (float)srcOpers[0].value.u.y, - (float)srcOpers[0].value.u.z, (float)srcOpers[0].value.u.w)); + SetDst(state, op.operands[0], op, + ShaderVariable("", (float)srcOpers[0].value.u.x, (float)srcOpers[0].value.u.y, + (float)srcOpers[0].value.u.z, (float)srcOpers[0].value.u.w)); break; case OPCODE_FTOI: - s.SetDst(op.operands[0], op, - ShaderVariable("", (int)srcOpers[0].value.f.x, (int)srcOpers[0].value.f.y, - (int)srcOpers[0].value.f.z, (int)srcOpers[0].value.f.w)); + SetDst(state, op.operands[0], op, + ShaderVariable("", (int)srcOpers[0].value.f.x, (int)srcOpers[0].value.f.y, + (int)srcOpers[0].value.f.z, (int)srcOpers[0].value.f.w)); break; case OPCODE_FTOU: - s.SetDst(op.operands[0], op, - ShaderVariable("", (uint32_t)srcOpers[0].value.f.x, (uint32_t)srcOpers[0].value.f.y, - (uint32_t)srcOpers[0].value.f.z, (uint32_t)srcOpers[0].value.f.w)); + SetDst(state, op.operands[0], op, + ShaderVariable("", (uint32_t)srcOpers[0].value.f.x, (uint32_t)srcOpers[0].value.f.y, + (uint32_t)srcOpers[0].value.f.z, (uint32_t)srcOpers[0].value.f.w)); break; case OPCODE_ITOD: case OPCODE_UTOD: @@ -2517,7 +2519,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const ShaderVariable r("", 0U, 0U, 0U, 0U); DoubleSet(r, res); - s.SetDst(op.operands[0], op, r); + SetDst(state, op.operands[0], op, r); break; } case OPCODE_DTOI: @@ -2570,7 +2572,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const } } - s.SetDst(op.operands[0], op, r); + SetDst(state, op.operands[0], op, r); break; } @@ -2578,32 +2580,32 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const // Comparison case OPCODE_EQ: - s.SetDst(op.operands[0], op, - ShaderVariable("", (srcOpers[0].value.f.x == srcOpers[1].value.f.x ? ~0l : 0l), - (srcOpers[0].value.f.y == srcOpers[1].value.f.y ? ~0l : 0l), - (srcOpers[0].value.f.z == srcOpers[1].value.f.z ? ~0l : 0l), - (srcOpers[0].value.f.w == srcOpers[1].value.f.w ? ~0l : 0l))); + SetDst(state, op.operands[0], op, + ShaderVariable("", (srcOpers[0].value.f.x == srcOpers[1].value.f.x ? ~0l : 0l), + (srcOpers[0].value.f.y == srcOpers[1].value.f.y ? ~0l : 0l), + (srcOpers[0].value.f.z == srcOpers[1].value.f.z ? ~0l : 0l), + (srcOpers[0].value.f.w == srcOpers[1].value.f.w ? ~0l : 0l))); break; case OPCODE_NE: - s.SetDst(op.operands[0], op, - ShaderVariable("", (srcOpers[0].value.f.x != srcOpers[1].value.f.x ? ~0l : 0l), - (srcOpers[0].value.f.y != srcOpers[1].value.f.y ? ~0l : 0l), - (srcOpers[0].value.f.z != srcOpers[1].value.f.z ? ~0l : 0l), - (srcOpers[0].value.f.w != srcOpers[1].value.f.w ? ~0l : 0l))); + SetDst(state, op.operands[0], op, + ShaderVariable("", (srcOpers[0].value.f.x != srcOpers[1].value.f.x ? ~0l : 0l), + (srcOpers[0].value.f.y != srcOpers[1].value.f.y ? ~0l : 0l), + (srcOpers[0].value.f.z != srcOpers[1].value.f.z ? ~0l : 0l), + (srcOpers[0].value.f.w != srcOpers[1].value.f.w ? ~0l : 0l))); break; case OPCODE_LT: - s.SetDst(op.operands[0], op, - ShaderVariable("", (srcOpers[0].value.f.x < srcOpers[1].value.f.x ? ~0l : 0l), - (srcOpers[0].value.f.y < srcOpers[1].value.f.y ? ~0l : 0l), - (srcOpers[0].value.f.z < srcOpers[1].value.f.z ? ~0l : 0l), - (srcOpers[0].value.f.w < srcOpers[1].value.f.w ? ~0l : 0l))); + SetDst(state, op.operands[0], op, + ShaderVariable("", (srcOpers[0].value.f.x < srcOpers[1].value.f.x ? ~0l : 0l), + (srcOpers[0].value.f.y < srcOpers[1].value.f.y ? ~0l : 0l), + (srcOpers[0].value.f.z < srcOpers[1].value.f.z ? ~0l : 0l), + (srcOpers[0].value.f.w < srcOpers[1].value.f.w ? ~0l : 0l))); break; case OPCODE_GE: - s.SetDst(op.operands[0], op, - ShaderVariable("", (srcOpers[0].value.f.x >= srcOpers[1].value.f.x ? ~0l : 0l), - (srcOpers[0].value.f.y >= srcOpers[1].value.f.y ? ~0l : 0l), - (srcOpers[0].value.f.z >= srcOpers[1].value.f.z ? ~0l : 0l), - (srcOpers[0].value.f.w >= srcOpers[1].value.f.w ? ~0l : 0l))); + SetDst(state, op.operands[0], op, + ShaderVariable("", (srcOpers[0].value.f.x >= srcOpers[1].value.f.x ? ~0l : 0l), + (srcOpers[0].value.f.y >= srcOpers[1].value.f.y ? ~0l : 0l), + (srcOpers[0].value.f.z >= srcOpers[1].value.f.z ? ~0l : 0l), + (srcOpers[0].value.f.w >= srcOpers[1].value.f.w ? ~0l : 0l))); break; case OPCODE_DEQ: case OPCODE_DNE: @@ -2655,50 +2657,50 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const r.value.uv[op.operands[0].comps[1]] = cmp2; } - s.SetDst(op.operands[0], op, r); + SetDst(state, op.operands[0], op, r); break; } case OPCODE_IEQ: - s.SetDst(op.operands[0], op, - ShaderVariable("", (srcOpers[0].value.i.x == srcOpers[1].value.i.x ? ~0l : 0l), - (srcOpers[0].value.i.y == srcOpers[1].value.i.y ? ~0l : 0l), - (srcOpers[0].value.i.z == srcOpers[1].value.i.z ? ~0l : 0l), - (srcOpers[0].value.i.w == srcOpers[1].value.i.w ? ~0l : 0l))); + SetDst(state, op.operands[0], op, + ShaderVariable("", (srcOpers[0].value.i.x == srcOpers[1].value.i.x ? ~0l : 0l), + (srcOpers[0].value.i.y == srcOpers[1].value.i.y ? ~0l : 0l), + (srcOpers[0].value.i.z == srcOpers[1].value.i.z ? ~0l : 0l), + (srcOpers[0].value.i.w == srcOpers[1].value.i.w ? ~0l : 0l))); break; case OPCODE_INE: - s.SetDst(op.operands[0], op, - ShaderVariable("", (srcOpers[0].value.i.x != srcOpers[1].value.i.x ? ~0l : 0l), - (srcOpers[0].value.i.y != srcOpers[1].value.i.y ? ~0l : 0l), - (srcOpers[0].value.i.z != srcOpers[1].value.i.z ? ~0l : 0l), - (srcOpers[0].value.i.w != srcOpers[1].value.i.w ? ~0l : 0l))); + SetDst(state, op.operands[0], op, + ShaderVariable("", (srcOpers[0].value.i.x != srcOpers[1].value.i.x ? ~0l : 0l), + (srcOpers[0].value.i.y != srcOpers[1].value.i.y ? ~0l : 0l), + (srcOpers[0].value.i.z != srcOpers[1].value.i.z ? ~0l : 0l), + (srcOpers[0].value.i.w != srcOpers[1].value.i.w ? ~0l : 0l))); break; case OPCODE_IGE: - s.SetDst(op.operands[0], op, - ShaderVariable("", (srcOpers[0].value.i.x >= srcOpers[1].value.i.x ? ~0l : 0l), - (srcOpers[0].value.i.y >= srcOpers[1].value.i.y ? ~0l : 0l), - (srcOpers[0].value.i.z >= srcOpers[1].value.i.z ? ~0l : 0l), - (srcOpers[0].value.i.w >= srcOpers[1].value.i.w ? ~0l : 0l))); + SetDst(state, op.operands[0], op, + ShaderVariable("", (srcOpers[0].value.i.x >= srcOpers[1].value.i.x ? ~0l : 0l), + (srcOpers[0].value.i.y >= srcOpers[1].value.i.y ? ~0l : 0l), + (srcOpers[0].value.i.z >= srcOpers[1].value.i.z ? ~0l : 0l), + (srcOpers[0].value.i.w >= srcOpers[1].value.i.w ? ~0l : 0l))); break; case OPCODE_ILT: - s.SetDst(op.operands[0], op, - ShaderVariable("", (srcOpers[0].value.i.x < srcOpers[1].value.i.x ? ~0l : 0l), - (srcOpers[0].value.i.y < srcOpers[1].value.i.y ? ~0l : 0l), - (srcOpers[0].value.i.z < srcOpers[1].value.i.z ? ~0l : 0l), - (srcOpers[0].value.i.w < srcOpers[1].value.i.w ? ~0l : 0l))); + SetDst(state, op.operands[0], op, + ShaderVariable("", (srcOpers[0].value.i.x < srcOpers[1].value.i.x ? ~0l : 0l), + (srcOpers[0].value.i.y < srcOpers[1].value.i.y ? ~0l : 0l), + (srcOpers[0].value.i.z < srcOpers[1].value.i.z ? ~0l : 0l), + (srcOpers[0].value.i.w < srcOpers[1].value.i.w ? ~0l : 0l))); break; case OPCODE_ULT: - s.SetDst(op.operands[0], op, - ShaderVariable("", (srcOpers[0].value.u.x < srcOpers[1].value.u.x ? ~0l : 0l), - (srcOpers[0].value.u.y < srcOpers[1].value.u.y ? ~0l : 0l), - (srcOpers[0].value.u.z < srcOpers[1].value.u.z ? ~0l : 0l), - (srcOpers[0].value.u.w < srcOpers[1].value.u.w ? ~0l : 0l))); + SetDst(state, op.operands[0], op, + ShaderVariable("", (srcOpers[0].value.u.x < srcOpers[1].value.u.x ? ~0l : 0l), + (srcOpers[0].value.u.y < srcOpers[1].value.u.y ? ~0l : 0l), + (srcOpers[0].value.u.z < srcOpers[1].value.u.z ? ~0l : 0l), + (srcOpers[0].value.u.w < srcOpers[1].value.u.w ? ~0l : 0l))); break; case OPCODE_UGE: - s.SetDst(op.operands[0], op, - ShaderVariable("", (srcOpers[0].value.u.x >= srcOpers[1].value.u.x ? ~0l : 0l), - (srcOpers[0].value.u.y >= srcOpers[1].value.u.y ? ~0l : 0l), - (srcOpers[0].value.u.z >= srcOpers[1].value.u.z ? ~0l : 0l), - (srcOpers[0].value.u.w >= srcOpers[1].value.u.w ? ~0l : 0l))); + SetDst(state, op.operands[0], op, + ShaderVariable("", (srcOpers[0].value.u.x >= srcOpers[1].value.u.x ? ~0l : 0l), + (srcOpers[0].value.u.y >= srcOpers[1].value.u.y ? ~0l : 0l), + (srcOpers[0].value.u.z >= srcOpers[1].value.u.z ? ~0l : 0l), + (srcOpers[0].value.u.w >= srcOpers[1].value.u.w ? ~0l : 0l))); break; ///////////////////////////////////////////////////////////////////////////////////////////////////// @@ -2708,19 +2710,19 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const { BindingSlot slot = GetBindingSlotForIdentifier(*program, TYPE_UNORDERED_ACCESS_VIEW, srcOpers[0].value.u.x); - GlobalState::UAVIterator uav = global->uavs.find(slot); - if(uav == global->uavs.end()) + GlobalState::UAVIterator uav = global.uavs.find(slot); + if(uav == global.uavs.end()) { if(!apiWrapper->FetchUAV(slot)) { RDCERR("Invalid UAV reg=%u, space=%u", slot.shaderRegister, slot.registerSpace); - return s; + return; } - uav = global->uavs.find(slot); + uav = global.uavs.find(slot); } uint32_t count = uav->second.hiddenCounter++; - s.SetDst(op.operands[0], op, ShaderVariable("", count, count, count, count)); + SetDst(state, op.operands[0], op, ShaderVariable("", count, count, count, count)); break; } @@ -2728,19 +2730,19 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const { BindingSlot slot = GetBindingSlotForIdentifier(*program, TYPE_UNORDERED_ACCESS_VIEW, srcOpers[0].value.u.x); - GlobalState::UAVIterator uav = global->uavs.find(slot); - if(uav == global->uavs.end()) + GlobalState::UAVIterator uav = global.uavs.find(slot); + if(uav == global.uavs.end()) { if(!apiWrapper->FetchUAV(slot)) { RDCERR("Invalid UAV reg=%u, space=%u", slot.shaderRegister, slot.registerSpace); - return s; + return; } - uav = global->uavs.find(slot); + uav = global.uavs.find(slot); } uint32_t count = --uav->second.hiddenCounter; - s.SetDst(op.operands[0], op, ShaderVariable("", count, count, count, count)); + SetDst(state, op.operands[0], op, ShaderVariable("", count, count, count, count)); break; } @@ -2751,24 +2753,24 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const case OPCODE_DERIV_RTX: case OPCODE_DERIV_RTX_COARSE: case OPCODE_DERIV_RTX_FINE: - if(quad == NULL) + if(program->GetShaderType() != DXBC::ShaderType::Pixel || prevWorkgroup.size() != 4) RDCERR( "Attempt to use derivative instruction not in pixel shader. Undefined results will " "occur!"); else - s.SetDst(op.operands[0], op, - s.DDX(op.operation == OPCODE_DERIV_RTX_FINE, quad, op.operands[1], op)); + SetDst(state, op.operands[0], op, + DDX(op.operation == OPCODE_DERIV_RTX_FINE, prevWorkgroup, op.operands[1], op)); break; case OPCODE_DERIV_RTY: case OPCODE_DERIV_RTY_COARSE: case OPCODE_DERIV_RTY_FINE: - if(quad == NULL) + if(program->GetShaderType() != DXBC::ShaderType::Pixel || prevWorkgroup.size() != 4) RDCERR( "Attempt to use derivative instruction not in pixel shader. Undefined results will " "occur!"); else - s.SetDst(op.operands[0], op, - s.DDY(op.operation == OPCODE_DERIV_RTY_FINE, quad, op.operands[1], op)); + SetDst(state, op.operands[0], op, + DDY(op.operation == OPCODE_DERIV_RTY_FINE, prevWorkgroup, op.operands[1], op)); break; ///////////////////////////////////////////////////////////////////////////////////////////////////// @@ -2837,7 +2839,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const if(gsm) { offset = 0; - if(resIndex > global->groupshared.size()) + if(resIndex > global.groupshared.size()) { numElems = 0; stride = 4; @@ -2845,25 +2847,25 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const } else { - numElems = global->groupshared[resIndex].count; - stride = global->groupshared[resIndex].bytestride; - data = &global->groupshared[resIndex].data[0]; - structured = global->groupshared[resIndex].structured; + numElems = global.groupshared[resIndex].count; + stride = global.groupshared[resIndex].bytestride; + data = &global.groupshared[resIndex].data[0]; + structured = global.groupshared[resIndex].structured; } } else { BindingSlot slot = GetBindingSlotForIdentifier(*program, TYPE_UNORDERED_ACCESS_VIEW, resIndex); - GlobalState::UAVIterator uav = global->uavs.find(slot); - if(uav == global->uavs.end()) + GlobalState::UAVIterator uav = global.uavs.find(slot); + if(uav == global.uavs.end()) { if(!apiWrapper->FetchUAV(slot)) { RDCERR("Invalid UAV reg=%u, space=%u", slot.shaderRegister, slot.registerSpace); - return s; + return; } - uav = global->uavs.find(slot); + uav = global.uavs.find(slot); } offset = uav->second.firstElement; @@ -2915,7 +2917,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const if(beforeResult.type != TYPE_NULL) { - s.SetDst(beforeResult, op, ShaderVariable("", *udst, *udst, *udst, *udst)); + SetDst(state, beforeResult, op, ShaderVariable("", *udst, *udst, *udst, *udst)); } // not verified below since by definition the operations that expect usrc1 will have it @@ -2985,8 +2987,8 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const load = false; } - if(load) - s.flags = ShaderEvents::SampleLoadGather; + if(load && state) + state->flags |= ShaderEvents::SampleLoadGather; if(op.operation == OPCODE_LD_STRUCTURED || op.operation == OPCODE_STORE_STRUCTURED) { @@ -3008,9 +3010,9 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const if(stride == 0) { - if(gsm && resIndex < global->groupshared.size()) + if(gsm && resIndex < global.groupshared.size()) { - stride = global->groupshared[resIndex].bytestride; + stride = global.groupshared[resIndex].bytestride; } else if(!gsm) { @@ -3084,7 +3086,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const if(gsm) { offset = 0; - if(resIndex > global->groupshared.size()) + if(resIndex > global.groupshared.size()) { numElems = 0; stride = 4; @@ -3092,13 +3094,13 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const } else { - numElems = global->groupshared[resIndex].count; - stride = global->groupshared[resIndex].bytestride; - data = global->groupshared[resIndex].data.data(); - dataSize = global->groupshared[resIndex].data.size(); + numElems = global.groupshared[resIndex].count; + stride = global.groupshared[resIndex].bytestride; + data = global.groupshared[resIndex].data.data(); + dataSize = global.groupshared[resIndex].data.size(); fmt.fmt = CompType::UInt; fmt.byteWidth = 4; - fmt.numComps = global->groupshared[resIndex].bytestride / 4; + fmt.numComps = global.groupshared[resIndex].bytestride / 4; fmt.stride = 0; } texData = false; @@ -3110,15 +3112,15 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const if(srv) { - GlobalState::SRVIterator srvIter = global->srvs.find(slot); - if(srvIter == global->srvs.end()) + GlobalState::SRVIterator srvIter = global.srvs.find(slot); + if(srvIter == global.srvs.end()) { if(!apiWrapper->FetchSRV(slot)) { RDCERR("Invalid SRV reg=%u, space=%u", slot.shaderRegister, slot.registerSpace); - return s; + return; } - srvIter = global->srvs.find(slot); + srvIter = global.srvs.find(slot); } data = srvIter->second.data.data(); @@ -3128,15 +3130,15 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const } else { - GlobalState::UAVIterator uavIter = global->uavs.find(slot); - if(uavIter == global->uavs.end()) + GlobalState::UAVIterator uavIter = global.uavs.find(slot); + if(uavIter == global.uavs.end()) { if(!apiWrapper->FetchUAV(slot)) { RDCERR("Invalid UAV reg=%u, space=%u", slot.shaderRegister, slot.registerSpace); - return s; + return; } - uavIter = global->uavs.find(slot); + uavIter = global.uavs.find(slot); } data = uavIter->second.data.data(); @@ -3172,7 +3174,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const if(!data || (!texData && elemIdx >= numElems) || (texData && texOffset >= dataSize)) { if(load) - s.SetDst(op.operands[0], op, ShaderVariable("", 0U, 0U, 0U, 0U)); + SetDst(state, op.operands[0], op, ShaderVariable("", 0U, 0U, 0U, 0U)); } else { @@ -3243,7 +3245,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const fetch.value.uv[0] = fetch.value.uv[op.operands[0].comps[0]]; } - s.SetDst(op.operands[0], op, fetch); + SetDst(state, op.operands[0], op, fetch); } else if(!Finished()) // helper/inactive pixels can't modify UAVs { @@ -3271,7 +3273,9 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const GlobalState::SampleEvalCacheKey key; - key.quadIndex = quadIndex; + RDCASSERT(program->GetShaderType() == DXBC::ShaderType::Pixel); + + key.quadIndex = workgroupIndex; // if this is TYPE_INPUT we can look up the index directly key.inputRegisterIndex = (int32_t)op.operands[1].indices[0].index; @@ -3301,8 +3305,8 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const } // look up this combination in the cache, if we get a hit then return that value. - auto it = global->sampleEvalCache.find(key); - if(it != global->sampleEvalCache.end()) + auto it = global.sampleEvalCache.find(key); + if(it != global.sampleEvalCache.end()) { // perform source operand swizzling ShaderVariable var = it->second; @@ -3311,7 +3315,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const if(op.operands[1].comps[i] < 4) var.value.uv[i] = it->second.value.uv[op.operands[1].comps[i]]; - s.SetDst(op.operands[0], op, var); + SetDst(state, op.operands[0], op, var); } else { @@ -3319,17 +3323,17 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const // just return the interpolant, or something went wrong and the item we want isn't cached so // the best we can do is return the interpolant. - if(!global->sampleEvalCache.empty()) + if(!global.sampleEvalCache.empty()) { apiWrapper->AddDebugMessage( MessageCategory::Shaders, MessageSeverity::Medium, MessageSource::RuntimeWarning, StringFormat::Fmt( "Shader debugging %d: %s\n" "No sample evaluate found in cache. Possible out-of-bounds sample index", - s.nextInstruction - 1, op.str.c_str())); + nextInstruction - 1, op.str.c_str())); } - s.SetDst(op.operands[0], op, srcOpers[0]); + SetDst(state, op.operands[0], op, srcOpers[0]); } break; @@ -3377,7 +3381,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const MessageCategory::Shaders, MessageSeverity::Medium, MessageSource::RuntimeWarning, StringFormat::Fmt( "Shader debugging %d: %s\nNon-multisampled texture being passed to sample_pos", - s.nextInstruction - 1, op.str.c_str())); + nextInstruction - 1, op.str.c_str())); sample_pattern = NULL; } @@ -3481,7 +3485,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const op.operands[0].comps[2] == 0xff && op.operands[0].comps[3] == 0xff) result.value.uv[0] = result.value.uv[op.operands[0].comps[0]]; - s.SetDst(op.operands[0], op, result); + SetDst(state, op.operands[0], op, result); break; } @@ -3515,12 +3519,12 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const op.operands[0].comps[2] == 0xff && op.operands[0].comps[3] == 0xff) result.value.uv[0] = result.value.uv[op.operands[0].comps[0]]; - s.SetDst(op.operands[0], op, result); + SetDst(state, op.operands[0], op, result); } else { RDCERR("Unexpected relative addressing"); - s.SetDst(op.operands[0], op, ShaderVariable("", 0.0f, 0.0f, 0.0f, 0.0f)); + SetDst(state, op.operands[0], op, ShaderVariable("", 0.0f, 0.0f, 0.0f, 0.0f)); } break; @@ -3622,12 +3626,12 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const op.operands[0].comps[2] == 0xff && op.operands[0].comps[3] == 0xff) result.value.uv[0] = result.value.uv[op.operands[0].comps[0]]; - s.SetDst(op.operands[0], op, result); + SetDst(state, op.operands[0], op, result); } else { RDCERR("Unexpected relative addressing"); - s.SetDst(op.operands[0], op, ShaderVariable("", 0.0f, 0.0f, 0.0f, 0.0f)); + SetDst(state, op.operands[0], op, ShaderVariable("", 0.0f, 0.0f, 0.0f, 0.0f)); } break; @@ -3646,8 +3650,8 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const case OPCODE_GATHER4_PO_C: case OPCODE_LOD: { - if(op.operation != OPCODE_LOD) - s.flags = ShaderEvents::SampleLoadGather; + if(op.operation != OPCODE_LOD && state) + state->flags |= ShaderEvents::SampleLoadGather; SamplerMode samplerMode = NUM_SAMPLERS; ResourceDimension resourceDim = RESOURCE_DIMENSION_UNKNOWN; @@ -3675,16 +3679,16 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const resourceDim = decl.dim; resourceBinding = GetBindingSlotForDeclaration(*program, decl); - GlobalState::SRVIterator srv = global->srvs.find(resourceBinding); - if(srv == global->srvs.end()) + GlobalState::SRVIterator srv = global.srvs.find(resourceBinding); + if(srv == global.srvs.end()) { if(!apiWrapper->FetchSRV(resourceBinding)) { RDCERR("Invalid SRV reg=%u, space=%u", resourceBinding.shaderRegister, resourceBinding.registerSpace); - return s; + return; } - srv = global->srvs.find(resourceBinding); + srv = global.srvs.find(resourceBinding); } const byte *data = &srv->second.data[0]; @@ -3722,9 +3726,9 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const op.operands[0].comps[2] == 0xff && op.operands[0].comps[3] == 0xff) fetch.value.uv[0] = fetch.value.uv[op.operands[0].comps[0]]; - s.SetDst(op.operands[0], op, fetch); + SetDst(state, op.operands[0], op, fetch); - return s; + return; } if(decl.declaration == OPCODE_DCL_RESOURCE && decl.operand.type == TYPE_RESOURCE && decl.operand.indices.size() > 0 && decl.operand.indices[0] == op.operands[2].indices[0]) @@ -3756,7 +3760,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const { ShaderVariable invalidResult("tex", 0.0f, 0.0f, 0.0f, 0.0f); - s.SetDst(op.operands[0], op, invalidResult); + SetDst(state, op.operands[0], op, invalidResult); break; } @@ -3768,7 +3772,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const if(op.operation == OPCODE_SAMPLE || op.operation == OPCODE_SAMPLE_B || op.operation == OPCODE_SAMPLE_C || op.operation == OPCODE_LOD) { - if(quad == NULL) + if(program->GetShaderType() != DXBC::ShaderType::Pixel || prevWorkgroup.size() != 4) { RDCERR( "Attempt to use derivative instruction not in pixel shader. Undefined results will " @@ -3777,8 +3781,8 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const else { // texture samples use coarse derivatives - ddxCalc = s.DDX(false, quad, op.operands[1], op); - ddyCalc = s.DDY(false, quad, op.operands[1], op); + ddxCalc = DDX(false, prevWorkgroup, op.operands[1], op); + ddyCalc = DDY(false, prevWorkgroup, op.operands[1], op); } } else if(op.operation == OPCODE_SAMPLE_D) @@ -3834,11 +3838,11 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const if(op.operands[0].comps[1] == 0xff) lookupResult.value.iv[0] = lookupResult.value.iv[op.operands[0].comps[0]]; - s.SetDst(op.operands[0], op, lookupResult); + SetDst(state, op.operands[0], op, lookupResult); } else { - return s; + return; } break; } @@ -3854,11 +3858,11 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const uint32_t jumpLocation = 0; - uint32_t search = s.nextInstruction; + uint32_t search = nextInstruction; for(; search < (uint32_t)program->GetNumInstructions(); search++) { - const Operation &nextOp = s.program->GetInstruction((size_t)search); + const Operation &nextOp = program->GetInstruction((size_t)search); // track nested switch statements to ensure we don't accidentally pick the case from a // different switch @@ -3909,13 +3913,13 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const // at the next excutable instruction (which might be a break if we're doing nothing) for(; jumpLocation < (uint32_t)program->GetNumInstructions(); jumpLocation++) { - const Operation &nextOp = s.program->GetInstruction(jumpLocation); + const Operation &nextOp = program->GetInstruction(jumpLocation); if(nextOp.operation != OPCODE_CASE && nextOp.operation != OPCODE_DEFAULT) break; } - s.nextInstruction = jumpLocation; + nextInstruction = jumpLocation; } break; @@ -3943,13 +3947,13 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const op.operation == OPCODE_CONTINUE || op.operation == OPCODE_ENDLOOP) { // skip back one to the endloop that we're processing - s.nextInstruction--; + nextInstruction--; - for(; s.nextInstruction >= 0; s.nextInstruction--) + for(; nextInstruction >= 0; nextInstruction--) { - if(s.program->GetInstruction(s.nextInstruction).operation == OPCODE_ENDLOOP) + if(program->GetInstruction(nextInstruction).operation == OPCODE_ENDLOOP) depth++; - if(s.program->GetInstruction(s.nextInstruction).operation == OPCODE_LOOP) + if(program->GetInstruction(nextInstruction).operation == OPCODE_LOOP) depth--; if(depth == 0) @@ -3958,7 +3962,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const } } - RDCASSERT(s.nextInstruction >= 0); + RDCASSERT(nextInstruction >= 0); } break; @@ -3973,13 +3977,13 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const // break out (jump to next endloop/endswitch) int depth = 1; - for(; s.nextInstruction < (int)program->GetNumInstructions(); s.nextInstruction++) + for(; nextInstruction < (int)program->GetNumInstructions(); nextInstruction++) { - if(s.program->GetInstruction(s.nextInstruction).operation == OPCODE_LOOP || - s.program->GetInstruction(s.nextInstruction).operation == OPCODE_SWITCH) + if(program->GetInstruction(nextInstruction).operation == OPCODE_LOOP || + program->GetInstruction(nextInstruction).operation == OPCODE_SWITCH) depth++; - if(s.program->GetInstruction(s.nextInstruction).operation == OPCODE_ENDLOOP || - s.program->GetInstruction(s.nextInstruction).operation == OPCODE_ENDSWITCH) + if(program->GetInstruction(nextInstruction).operation == OPCODE_ENDLOOP || + program->GetInstruction(nextInstruction).operation == OPCODE_ENDSWITCH) depth--; if(depth == 0) @@ -3988,11 +3992,11 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const } } - RDCASSERT(s.program->GetInstruction(s.nextInstruction).operation == OPCODE_ENDLOOP || - s.program->GetInstruction(s.nextInstruction).operation == OPCODE_ENDSWITCH); + RDCASSERT(program->GetInstruction(nextInstruction).operation == OPCODE_ENDLOOP || + program->GetInstruction(nextInstruction).operation == OPCODE_ENDSWITCH); // don't want to process the endloop and jump again! - s.nextInstruction++; + nextInstruction++; } break; @@ -4011,16 +4015,16 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const int depth = 0; // skip back one to the if that we're processing - s.nextInstruction--; + nextInstruction--; - for(; s.nextInstruction < (int)program->GetNumInstructions(); s.nextInstruction++) + for(; nextInstruction < (int)program->GetNumInstructions(); nextInstruction++) { - if(s.program->GetInstruction(s.nextInstruction).operation == OPCODE_IF) + if(program->GetInstruction(nextInstruction).operation == OPCODE_IF) depth++; // only step out on an else if it's the matching depth to our starting if (depth == 1) - if(depth == 1 && s.program->GetInstruction(s.nextInstruction).operation == OPCODE_ELSE) + if(depth == 1 && program->GetInstruction(nextInstruction).operation == OPCODE_ELSE) depth--; - if(s.program->GetInstruction(s.nextInstruction).operation == OPCODE_ENDIF) + if(program->GetInstruction(nextInstruction).operation == OPCODE_ENDIF) depth--; if(depth == 0) @@ -4029,11 +4033,11 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const } } - RDCASSERT(s.program->GetInstruction(s.nextInstruction).operation == OPCODE_ELSE || - s.program->GetInstruction(s.nextInstruction).operation == OPCODE_ENDIF); + RDCASSERT(program->GetInstruction(nextInstruction).operation == OPCODE_ELSE || + program->GetInstruction(nextInstruction).operation == OPCODE_ENDIF); // step to next instruction after the else/endif (processing an else would skip that block) - s.nextInstruction++; + nextInstruction++; } break; @@ -4044,11 +4048,11 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const // next endif) int depth = 1; - for(; s.nextInstruction < (int)program->GetNumInstructions(); s.nextInstruction++) + for(; nextInstruction < (int)program->GetNumInstructions(); nextInstruction++) { - if(s.program->GetInstruction(s.nextInstruction).operation == OPCODE_IF) + if(program->GetInstruction(nextInstruction).operation == OPCODE_IF) depth++; - if(s.program->GetInstruction(s.nextInstruction).operation == OPCODE_ENDIF) + if(program->GetInstruction(nextInstruction).operation == OPCODE_ENDIF) depth--; if(depth == 0) @@ -4057,11 +4061,11 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const } } - RDCASSERT(s.program->GetInstruction(s.nextInstruction).operation == OPCODE_ENDIF); + RDCASSERT(program->GetInstruction(nextInstruction).operation == OPCODE_ENDIF); // step to next instruction after the else/endif (for consistency with handling in the if // block) - s.nextInstruction++; + nextInstruction++; break; } @@ -4076,7 +4080,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const } // discarding. - s.done = true; + done = true; break; } case OPCODE_RET: @@ -4087,7 +4091,7 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const if((test == 0 && !op.nonzero) || (test != 0 && op.nonzero) || op.operation == OPCODE_RET) { // assumes not in a function call - s.done = true; + done = true; } break; } @@ -4097,8 +4101,6 @@ State State::GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const break; } } - - return s; } BindingSlot GetBindingSlotForDeclaration(const Program &program, const DXBCBytecode::Declaration &decl) @@ -4174,298 +4176,10 @@ void GlobalState::PopulateGroupshared(const DXBCBytecode::Program *pBytecode) } } -void CreateShaderDebugStateAndTrace(State &initialState, ShaderDebugTrace &trace, - DXBC::DXBCContainer *dxbc, const ShaderReflection &refl, - const ShaderBindpointMapping &mapping) -{ - bool hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); - - dxbc->GetDXBCByteCode()->SetupRegisterFile(initialState.variables); - - int32_t maxReg = -1; - for(const SigParameter &sig : dxbc->GetReflection()->InputSig) - { - if(sig.regIndex != ~0U) - maxReg = RDCMAX(maxReg, (int32_t)sig.regIndex); - } - - const bool inputCoverage = dxbc->GetDXBCByteCode()->HasCoverageInput(); - - // Add inputs to the shader trace - if(maxReg >= 0 || inputCoverage) - { - trace.inputs.resize(maxReg + 1 + (inputCoverage ? 1 : 0)); - for(const SigParameter &sig : dxbc->GetReflection()->InputSig) - { - ShaderVariable v; - - v.name = dxbc->GetDXBCByteCode()->GetRegisterName(DXBCBytecode::TYPE_INPUT, sig.regIndex); - v.rows = 1; - v.columns = sig.regChannelMask & 0x8 ? 4 : sig.regChannelMask & 0x4 - ? 3 - : sig.regChannelMask & 0x2 - ? 2 - : sig.regChannelMask & 0x1 ? 1 : 0; - - if(sig.compType == CompType::UInt) - v.type = VarType::UInt; - else if(sig.compType == CompType::SInt) - v.type = VarType::SInt; - else - v.type = VarType::Float; - - ShaderVariable &dst = trace.inputs[sig.regIndex]; - - // if the variable hasn't been initialised, just assign. If it has, we're in a situation where - // two input parameters are assigned to the same variable overlapping, so just update the - // number of columns to the max of both. The source mapping (either from debug info or our own - // below) will handle distinguishing better. - if(dst.name.empty()) - dst = v; - else - dst.columns = RDCMAX(dst.columns, v.columns); - - { - SourceVariableMapping sourcemap; - sourcemap.name = sig.semanticIdxName; - if(sourcemap.name.empty() && sig.systemValue != ShaderBuiltin::Undefined) - sourcemap.name = ToStr(sig.systemValue); - sourcemap.type = v.type; - sourcemap.rows = 1; - sourcemap.columns = sig.compCount; - sourcemap.variables.reserve(sig.compCount); - - for(uint16_t c = 0; c < 4; c++) - { - if(sig.regChannelMask & (1 << c)) - { - DebugVariableReference ref; - ref.name = v.name; - ref.type = DebugVariableType::Input; - ref.component = c; - sourcemap.variables.push_back(ref); - } - } - - trace.sourceVars.push_back(sourcemap); - } - } - - // Put the coverage mask at the end - if(inputCoverage) - { - trace.inputs.back() = ShaderVariable( - dxbc->GetDXBCByteCode()->GetRegisterName(DXBCBytecode::TYPE_INPUT_COVERAGE_MASK, 0), 0U, - 0U, 0U, 0U); - trace.inputs.back().columns = 1; - - { - SourceVariableMapping sourcemap; - sourcemap.name = "SV_Coverage"; - sourcemap.type = VarType::UInt; - sourcemap.rows = 1; - sourcemap.columns = 1; - DebugVariableReference ref; - ref.type = DebugVariableType::Input; - ref.name = trace.inputs.back().name; - sourcemap.variables.push_back(ref); - - trace.sourceVars.push_back(sourcemap); - } - } - } - - // Set up outputs in the shader state - for(const SigParameter &sig : dxbc->GetReflection()->OutputSig) - { - DXBCBytecode::OperandType type = DXBCBytecode::TYPE_OUTPUT; - - if(sig.systemValue == ShaderBuiltin::DepthOutput) - type = DXBCBytecode::TYPE_OUTPUT_DEPTH; - else if(sig.systemValue == ShaderBuiltin::DepthOutputLessEqual) - type = DXBCBytecode::TYPE_OUTPUT_DEPTH_LESS_EQUAL; - else if(sig.systemValue == ShaderBuiltin::DepthOutputGreaterEqual) - type = DXBCBytecode::TYPE_OUTPUT_DEPTH_GREATER_EQUAL; - else if(sig.systemValue == ShaderBuiltin::MSAACoverage) - type = DXBCBytecode::TYPE_OUTPUT_COVERAGE_MASK; - else if(sig.systemValue == ShaderBuiltin::StencilReference) - type = DXBCBytecode::TYPE_OUTPUT_STENCIL_REF; - - if(type == DXBCBytecode::TYPE_OUTPUT && sig.regIndex == ~0U) - { - RDCERR("Unhandled output: %s (%s)", sig.semanticName.c_str(), ToStr(sig.systemValue).c_str()); - continue; - } - - uint32_t idx = dxbc->GetDXBCByteCode()->GetRegisterIndex(type, sig.regIndex); - - if(idx >= initialState.variables.size()) - continue; - - ShaderVariable v; - - v.name = dxbc->GetDXBCByteCode()->GetRegisterName(type, sig.regIndex); - v.rows = 1; - v.columns = sig.regChannelMask & 0x8 ? 4 : sig.regChannelMask & 0x4 - ? 3 - : sig.regChannelMask & 0x2 - ? 2 - : sig.regChannelMask & 0x1 ? 1 : 0; - - if(sig.compType == CompType::UInt) - v.type = VarType::UInt; - else if(sig.compType == CompType::SInt) - v.type = VarType::SInt; - else - v.type = VarType::Float; - - ShaderVariable &dst = initialState.variables[idx]; - - // if the variable hasn't been initialised, just assign. If it has, we're in a situation where - // two input parameters are assigned to the same variable overlapping, so just update the - // number of columns to the max of both. The source mapping (either from debug info or our own - // below) will handle distinguishing better. - if(dst.name.empty()) - dst = v; - else - dst.columns = RDCMAX(dst.columns, v.columns); - - // if we don't have any debug info we can at least map to the semantic to give a better name - // than the raw register from the reflection info, at least for normal outputs - if(!hasSourceMapping) - { - if(type == DXBCBytecode::TYPE_OUTPUT) - { - SourceVariableMapping sourcemap; - sourcemap.name = sig.semanticIdxName; - if(sourcemap.name.empty() && sig.systemValue != ShaderBuiltin::Undefined) - sourcemap.name = ToStr(sig.systemValue); - sourcemap.type = v.type; - sourcemap.rows = 1; - sourcemap.columns = sig.compCount; - sourcemap.variables.reserve(sig.compCount); - - for(uint16_t c = 0; c < 4; c++) - { - if(sig.regChannelMask & (1 << c)) - { - DebugVariableReference ref; - ref.type = DebugVariableType::Variable; - ref.name = v.name; - ref.component = c; - sourcemap.variables.push_back(ref); - } - } - - trace.sourceVars.push_back(sourcemap); - } - else - { - SourceVariableMapping sourcemap; - - if(sig.systemValue == ShaderBuiltin::DepthOutput) - { - sourcemap.name = "SV_Depth"; - sourcemap.type = VarType::Float; - } - else if(sig.systemValue == ShaderBuiltin::DepthOutputLessEqual) - { - sourcemap.name = "SV_DepthLessEqual"; - sourcemap.type = VarType::Float; - } - else if(sig.systemValue == ShaderBuiltin::DepthOutputGreaterEqual) - { - sourcemap.name = "SV_DepthGreaterEqual"; - sourcemap.type = VarType::Float; - } - else if(sig.systemValue == ShaderBuiltin::MSAACoverage) - { - sourcemap.name = "SV_Coverage"; - sourcemap.type = VarType::UInt; - } - else if(sig.systemValue == ShaderBuiltin::StencilReference) - { - sourcemap.name = "SV_StencilRef"; - sourcemap.type = VarType::UInt; - } - - // all these variables are 1 scalar component - sourcemap.rows = 1; - sourcemap.columns = 1; - DebugVariableReference ref; - ref.type = DebugVariableType::Variable; - ref.name = v.name; - sourcemap.variables.push_back(ref); - - trace.sourceVars.push_back(sourcemap); - } - } - } - - // Set the number of constant buffers in the trace, but assignment happens later - trace.constantBlocks.resize(refl.constantBlocks.size()); - - struct ResList - { - DebugVariableType varType; - const rdcarray &binds; - const rdcarray &resources; - const char *regChars; - rdcarray &dst; - }; - - ResList lists[2] = { - { - DebugVariableType::ReadOnlyResource, mapping.readOnlyResources, refl.readOnlyResources, - "tT", trace.readOnlyResources, - }, - { - DebugVariableType::ReadWriteResource, mapping.readWriteResources, refl.readWriteResources, - "uU", trace.readWriteResources, - }, - }; - - for(ResList &list : lists) - { - // add the registers for the resources that are used - list.dst.reserve(list.binds.size()); - for(size_t i = 0; i < list.binds.size(); i++) - { - const Bindpoint &b = list.binds[i]; - const ShaderResource &r = list.resources[i]; - - if(!b.used) - continue; - - rdcstr identifier; - - if(dxbc->GetDXBCByteCode()->IsShaderModel51()) - identifier = StringFormat::Fmt("%c%zu", list.regChars[1], i); - else - identifier = StringFormat::Fmt("%c%u", list.regChars[0], b.bind); - - ShaderVariable reg(identifier, (uint32_t)i, 0U, 0U, 0U); - reg.columns = 1; - - SourceVariableMapping sourcemap; - sourcemap.name = r.name; - sourcemap.type = r.variableType.descriptor.type; - sourcemap.rows = r.variableType.descriptor.rows; - sourcemap.columns = r.variableType.descriptor.columns; - DebugVariableReference ref; - ref.type = list.varType; - ref.name = reg.name; - sourcemap.variables.push_back(ref); - - trace.sourceVars.push_back(sourcemap); - list.dst.push_back(reg); - } - } -} - -void AddCBufferToDebugTrace(const DXBCBytecode::Program &program, ShaderDebugTrace &trace, - const ShaderReflection &refl, const ShaderBindpointMapping &mapping, - const BindingSlot &slot, bytebuf &cbufData) +void AddCBufferToGlobalState(const DXBCBytecode::Program &program, GlobalState &global, + rdcarray &sourceVars, + const ShaderReflection &refl, const ShaderBindpointMapping &mapping, + const BindingSlot &slot, bytebuf &cbufData) { // Find the identifier size_t numCBs = mapping.constantBlocks.size(); @@ -4474,11 +4188,11 @@ void AddCBufferToDebugTrace(const DXBCBytecode::Program &program, ShaderDebugTra if((uint32_t)mapping.constantBlocks[i].bindset == slot.registerSpace && (uint32_t)mapping.constantBlocks[i].bind == slot.shaderRegister) { - RDCASSERTMSG("Reassigning previously filled cbuffer", trace.constantBlocks[i].members.empty()); + RDCASSERTMSG("Reassigning previously filled cbuffer", global.constantBlocks[i].members.empty()); uint32_t cbufferIndex = program.IsShaderModel51() ? (uint32_t)i : slot.shaderRegister; - trace.constantBlocks[i].name = + global.constantBlocks[i].name = program.GetRegisterName(DXBCBytecode::TYPE_CONSTANT_BUFFER, cbufferIndex); SourceVariableMapping cbSourceMapping; @@ -4489,14 +4203,14 @@ void AddCBufferToDebugTrace(const DXBCBytecode::Program &program, ShaderDebugTra rdcarray vars; StandardFillCBufferVariables(refl.resourceId, refl.constantBlocks[i].variables, vars, cbufData); - FlattenVariables(trace.constantBlocks[i].name, refl.constantBlocks[i].variables, vars, - trace.constantBlocks[i].members, refl.constantBlocks[i].name + ".", 0, - trace.sourceVars); + FlattenVariables(global.constantBlocks[i].name, refl.constantBlocks[i].variables, vars, + global.constantBlocks[i].members, refl.constantBlocks[i].name + ".", 0, + sourceVars); - for(size_t c = 0; c < trace.constantBlocks[i].members.size(); c++) + for(size_t c = 0; c < global.constantBlocks[i].members.size(); c++) { - trace.constantBlocks[i].members[c].name = - StringFormat::Fmt("%s[%u]", trace.constantBlocks[i].name.c_str(), (uint32_t)c); + global.constantBlocks[i].members[c].name = + StringFormat::Fmt("%s[%u]", global.constantBlocks[i].name.c_str(), (uint32_t)c); } return; @@ -4504,27 +4218,8 @@ void AddCBufferToDebugTrace(const DXBCBytecode::Program &program, ShaderDebugTra } } -bool PromptDebugTimeout(uint32_t cycleCounter) -{ - rdcstr msg = StringFormat::Fmt( - "RenderDoc's shader debugging has been running for over %u cycles, which indicates either a " - "very long-running loop, or possibly an infinite loop. Continuing could lead to extreme " - "memory allocations, slow UI or even crashes. Would you like to abort debugging to see what " - "has run so far?\n\n" - "Hit yes to abort debugging. Note that loading the resulting trace could take several " - "minutes.", - cycleCounter); - - int ret = MessageBoxA(NULL, msg.c_str(), "Shader debugging timeout", MB_YESNO | MB_ICONWARNING); - - if(ret == IDYES) - return true; - - return false; -} - -void ApplyDerivatives(GlobalState &global, State quad[4], int reg, int element, int numWords, - float *data, float signmul, int32_t quadIdxA, int32_t quadIdxB) +void ApplyDerivatives(GlobalState &global, rdcarray &quad, int reg, int element, + int numWords, float *data, float signmul, int32_t quadIdxA, int32_t quadIdxB) { for(int w = 0; w < numWords; w++) { @@ -4549,7 +4244,7 @@ void ApplyDerivatives(GlobalState &global, State quad[4], int reg, int element, } } -void ApplyAllDerivatives(GlobalState &global, State quad[4], int destIdx, +void ApplyAllDerivatives(GlobalState &global, rdcarray &quad, int destIdx, const rdcarray &initialValues, float *data) { // We make the assumption that the coarse derivatives are generated from (0,0) in the quad, and @@ -5041,6 +4736,472 @@ void GatherPSInputDataForInitialValues(const DXBC::Reflection &psDxbc, psInputDefinition += "};\n\n"; } +ShaderDebugTrace *InterpretDebugger::BeginDebug(const DXBC::DXBCContainer *dxbcContainer, + const ShaderReflection &refl, + const ShaderBindpointMapping &mapping, + int activeIndex) +{ + ShaderDebugTrace *ret = new ShaderDebugTrace; + ret->debugger = this; + + this->dxbc = dxbcContainer; + this->activeLaneIndex = activeIndex; + + int workgroupSize = dxbc->m_Type == DXBC::ShaderType::Pixel ? 4 : 1; + for(int i = 0; i < workgroupSize; i++) + workgroup.push_back(ThreadState(i, global, dxbc)); + + if(dxbc->m_Type == DXBC::ShaderType::Compute) + global.PopulateGroupshared(dxbc->GetDXBCByteCode()); + + ThreadState &state = activeLane(); + + bool hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); + + int32_t maxReg = -1; + for(const SigParameter &sig : dxbc->GetReflection()->InputSig) + { + if(sig.regIndex != ~0U) + maxReg = RDCMAX(maxReg, (int32_t)sig.regIndex); + } + + const bool inputCoverage = dxbc->GetDXBCByteCode()->HasCoverageInput(); + + // Add inputs to the shader trace + if(maxReg >= 0 || inputCoverage) + { + state.inputs.resize(maxReg + 1 + (inputCoverage ? 1 : 0)); + for(const SigParameter &sig : dxbc->GetReflection()->InputSig) + { + ShaderVariable v; + + v.name = dxbc->GetDXBCByteCode()->GetRegisterName(DXBCBytecode::TYPE_INPUT, sig.regIndex); + v.rows = 1; + v.columns = sig.regChannelMask & 0x8 ? 4 : sig.regChannelMask & 0x4 + ? 3 + : sig.regChannelMask & 0x2 + ? 2 + : sig.regChannelMask & 0x1 ? 1 : 0; + + if(sig.compType == CompType::UInt) + v.type = VarType::UInt; + else if(sig.compType == CompType::SInt) + v.type = VarType::SInt; + else + v.type = VarType::Float; + + ShaderVariable &dst = state.inputs[sig.regIndex]; + + // if the variable hasn't been initialised, just assign. If it has, we're in a situation where + // two input parameters are assigned to the same variable overlapping, so just update the + // number of columns to the max of both. The source mapping (either from debug info or our own + // below) will handle distinguishing better. + if(dst.name.empty()) + dst = v; + else + dst.columns = RDCMAX(dst.columns, v.columns); + + { + SourceVariableMapping sourcemap; + sourcemap.name = sig.semanticIdxName; + if(sourcemap.name.empty() && sig.systemValue != ShaderBuiltin::Undefined) + sourcemap.name = ToStr(sig.systemValue); + sourcemap.type = v.type; + sourcemap.rows = 1; + sourcemap.columns = sig.compCount; + sourcemap.variables.reserve(sig.compCount); + + for(uint16_t c = 0; c < 4; c++) + { + if(sig.regChannelMask & (1 << c)) + { + DebugVariableReference ref; + ref.name = v.name; + ref.type = DebugVariableType::Input; + ref.component = c; + sourcemap.variables.push_back(ref); + } + } + + ret->sourceVars.push_back(sourcemap); + } + } + + // Put the coverage mask at the end + if(inputCoverage) + { + state.inputs.back() = ShaderVariable( + dxbc->GetDXBCByteCode()->GetRegisterName(DXBCBytecode::TYPE_INPUT_COVERAGE_MASK, 0), 0U, + 0U, 0U, 0U); + state.inputs.back().columns = 1; + + { + SourceVariableMapping sourcemap; + sourcemap.name = "SV_Coverage"; + sourcemap.type = VarType::UInt; + sourcemap.rows = 1; + sourcemap.columns = 1; + DebugVariableReference ref; + ref.type = DebugVariableType::Input; + ref.name = state.inputs.back().name; + sourcemap.variables.push_back(ref); + + ret->sourceVars.push_back(sourcemap); + } + } + } + + // Set up outputs in the shader state + for(const SigParameter &sig : dxbc->GetReflection()->OutputSig) + { + DXBCBytecode::OperandType type = DXBCBytecode::TYPE_OUTPUT; + + if(sig.systemValue == ShaderBuiltin::DepthOutput) + type = DXBCBytecode::TYPE_OUTPUT_DEPTH; + else if(sig.systemValue == ShaderBuiltin::DepthOutputLessEqual) + type = DXBCBytecode::TYPE_OUTPUT_DEPTH_LESS_EQUAL; + else if(sig.systemValue == ShaderBuiltin::DepthOutputGreaterEqual) + type = DXBCBytecode::TYPE_OUTPUT_DEPTH_GREATER_EQUAL; + else if(sig.systemValue == ShaderBuiltin::MSAACoverage) + type = DXBCBytecode::TYPE_OUTPUT_COVERAGE_MASK; + else if(sig.systemValue == ShaderBuiltin::StencilReference) + type = DXBCBytecode::TYPE_OUTPUT_STENCIL_REF; + + if(type == DXBCBytecode::TYPE_OUTPUT && sig.regIndex == ~0U) + { + RDCERR("Unhandled output: %s (%s)", sig.semanticName.c_str(), ToStr(sig.systemValue).c_str()); + continue; + } + + uint32_t idx = dxbc->GetDXBCByteCode()->GetRegisterIndex(type, sig.regIndex); + + if(idx >= state.variables.size()) + continue; + + ShaderVariable v; + + v.name = dxbc->GetDXBCByteCode()->GetRegisterName(type, sig.regIndex); + v.rows = 1; + v.columns = sig.regChannelMask & 0x8 ? 4 : sig.regChannelMask & 0x4 + ? 3 + : sig.regChannelMask & 0x2 + ? 2 + : sig.regChannelMask & 0x1 ? 1 : 0; + + if(sig.compType == CompType::UInt) + v.type = VarType::UInt; + else if(sig.compType == CompType::SInt) + v.type = VarType::SInt; + else + v.type = VarType::Float; + + ShaderVariable &dst = state.variables[idx]; + + // if the variable hasn't been initialised, just assign. If it has, we're in a situation where + // two input parameters are assigned to the same variable overlapping, so just update the + // number of columns to the max of both. The source mapping (either from debug info or our own + // below) will handle distinguishing better. + if(dst.name.empty()) + dst = v; + else + dst.columns = RDCMAX(dst.columns, v.columns); + + // if we don't have any debug info we can at least map to the semantic to give a better name + // than the raw register from the reflection info, at least for normal outputs + if(!hasSourceMapping) + { + if(type == DXBCBytecode::TYPE_OUTPUT) + { + SourceVariableMapping sourcemap; + sourcemap.name = sig.semanticIdxName; + if(sourcemap.name.empty() && sig.systemValue != ShaderBuiltin::Undefined) + sourcemap.name = ToStr(sig.systemValue); + sourcemap.type = v.type; + sourcemap.rows = 1; + sourcemap.columns = sig.compCount; + sourcemap.variables.reserve(sig.compCount); + + for(uint16_t c = 0; c < 4; c++) + { + if(sig.regChannelMask & (1 << c)) + { + DebugVariableReference ref; + ref.type = DebugVariableType::Variable; + ref.name = v.name; + ref.component = c; + sourcemap.variables.push_back(ref); + } + } + + ret->sourceVars.push_back(sourcemap); + } + else + { + SourceVariableMapping sourcemap; + + if(sig.systemValue == ShaderBuiltin::DepthOutput) + { + sourcemap.name = "SV_Depth"; + sourcemap.type = VarType::Float; + } + else if(sig.systemValue == ShaderBuiltin::DepthOutputLessEqual) + { + sourcemap.name = "SV_DepthLessEqual"; + sourcemap.type = VarType::Float; + } + else if(sig.systemValue == ShaderBuiltin::DepthOutputGreaterEqual) + { + sourcemap.name = "SV_DepthGreaterEqual"; + sourcemap.type = VarType::Float; + } + else if(sig.systemValue == ShaderBuiltin::MSAACoverage) + { + sourcemap.name = "SV_Coverage"; + sourcemap.type = VarType::UInt; + } + else if(sig.systemValue == ShaderBuiltin::StencilReference) + { + sourcemap.name = "SV_StencilRef"; + sourcemap.type = VarType::UInt; + } + + // all these variables are 1 scalar component + sourcemap.rows = 1; + sourcemap.columns = 1; + DebugVariableReference ref; + ref.type = DebugVariableType::Variable; + ref.name = v.name; + sourcemap.variables.push_back(ref); + + ret->sourceVars.push_back(sourcemap); + } + } + } + + // Set the number of constant buffers in the trace, but assignment happens later + global.constantBlocks.resize(refl.constantBlocks.size()); + + struct ResList + { + DebugVariableType varType; + const rdcarray &binds; + const rdcarray &resources; + const char *regChars; + rdcarray &dst; + }; + + ResList lists[2] = { + { + DebugVariableType::ReadOnlyResource, mapping.readOnlyResources, refl.readOnlyResources, + "tT", ret->readOnlyResources, + }, + { + DebugVariableType::ReadWriteResource, mapping.readWriteResources, refl.readWriteResources, + "uU", ret->readWriteResources, + }, + }; + + for(ResList &list : lists) + { + // add the registers for the resources that are used + list.dst.reserve(list.binds.size()); + for(size_t i = 0; i < list.binds.size(); i++) + { + const Bindpoint &b = list.binds[i]; + const ShaderResource &r = list.resources[i]; + + if(!b.used) + continue; + + rdcstr identifier; + + if(dxbc->GetDXBCByteCode()->IsShaderModel51()) + identifier = StringFormat::Fmt("%c%zu", list.regChars[1], i); + else + identifier = StringFormat::Fmt("%c%u", list.regChars[0], b.bind); + + ShaderVariable reg(identifier, (uint32_t)i, 0U, 0U, 0U); + reg.columns = 1; + + SourceVariableMapping sourcemap; + sourcemap.name = r.name; + sourcemap.type = r.variableType.descriptor.type; + sourcemap.rows = r.variableType.descriptor.rows; + sourcemap.columns = r.variableType.descriptor.columns; + DebugVariableReference ref; + ref.type = list.varType; + ref.name = reg.name; + sourcemap.variables.push_back(ref); + + ret->sourceVars.push_back(sourcemap); + list.dst.push_back(reg); + } + } + + return ret; +} + +void InterpretDebugger::CalcActiveMask(rdcarray &activeMask) +{ + // one bool per workgroup thread + activeMask.resize(workgroup.size()); + + // start as active, then if necessary turn off threads that are running diverged + for(bool &active : activeMask) + active = true; + + // only pixel shaders automatically converge workgroups, compute shaders need explicit sync + if(dxbc->m_Type != DXBC::ShaderType::Pixel) + return; + + // otherwise we need to make sure that control flow which converges stays in lockstep so that + // derivatives etc are still valid. While diverged, we don't have to keep threads in lockstep + // since using derivatives is invalid. + // + // Threads diverge either in ifs, loops, or switches. Due to the nature of the bytecode, all + // threads *must* pass through the same exit instruction for each, there's no jumping around with + // gotos. Note also for the same reason, the only time threads are on earlier instructions is if + // they are still catching up to a thread that has exited the control flow. + // + // So the scheme is as follows: + // * If all threads have the same nextInstruction, just continue we are still in lockstep. + // * If threads are out of lockstep, find any thread which has nextInstruction pointing + // immediately *after* an ENDIF, ENDLOOP or ENDSWITCH. Pointing directly at one is not an + // indication the thread is done, as the next step for an ENDLOOP will jump back to the matching + // LOOP and continue iterating. + // * Pause any thread matching the above until all threads are pointing to the same instruction. + // By the assumption above, all threads will eventually pass through this terminating + // instruction so we just pause any other threads and don't do anything until the control flow + // has converged and we can continue stepping in lockstep. + + // all threads as active. + // if we've converged, or we were never diverged, this keeps everything ticking + + // see if we've diverged + bool differentNext = false; + for(size_t i = 1; i < workgroup.size(); i++) + differentNext |= (workgroup[0].nextInstruction != workgroup[i].nextInstruction); + + if(differentNext) + { + // this isn't *perfect* but it will still eventually continue. We look for the most advanced + // thread, and check to see if it's just finished a control flow. If it has then we assume it's + // at the convergence point and wait for every other thread to catch up, pausing any threads + // that reach the convergence point before others. + + // Note this might mean we don't have any threads paused even within divergent flow. This is + // fine and all we care about is pausing to make sure threads don't run ahead into code that + // should be lockstep. We don't care at all about what they do within the code that is + // divergent. + + // The reason this isn't perfect is that the most advanced thread could be on an inner loop or + // inner if, not the convergence point, and we could be pausing it fruitlessly. Worse still - it + // could be on a branch none of the other threads will take so they will never reach that exact + // instruction. + // But we know that all threads will eventually go through the convergence point, so even in + // that worst case if we didn't pick the right waiting point, another thread will overtake and + // become the new most advanced thread and the previous waiting thread will resume. So in this + // case we caused a thread to wait more than it should have but that's not a big deal as it's + // within divergent flow so they don't have to stay in lockstep. Also if all threads will + // eventually pass that point we picked, we just waited to converge even in technically + // divergent code which is also harmless. + + // Phew! + + uint32_t convergencePoint = 0; + + // find which thread is most advanced + for(size_t i = 0; i < workgroup.size(); i++) + if(workgroup[i].nextInstruction > convergencePoint) + convergencePoint = workgroup[i].nextInstruction; + + if(convergencePoint > 0) + { + DXBCBytecode::OpcodeType op = + dxbc->GetDXBCByteCode()->GetInstruction(convergencePoint - 1).operation; + + // if the most advnaced thread hasn't just finished control flow, then all + // threads are still running, so don't converge + if(op != OPCODE_ENDIF && op != OPCODE_ENDLOOP && op != OPCODE_ENDSWITCH) + convergencePoint = 0; + } + + // pause any threads at that instruction (could be none) + for(size_t i = 0; workgroup.size(); i++) + if(workgroup[i].nextInstruction == convergencePoint) + activeMask[i] = false; + } +} + +rdcarray InterpretDebugger::ContinueDebug(DXBCDebug::DebugAPIWrapper *apiWrapper) +{ + DXBCDebug::ThreadState &active = activeLane(); + + rdcarray ret; + + // if we've finished, return an empty set to signify that + if(active.Finished()) + return ret; + + // initialise a blank set of shader variable changes in the first ShaderDebugState + if(steps == 0) + { + ShaderDebugState initial; + + for(const ShaderVariable &v : active.variables) + initial.changes.push_back({ShaderVariable(), v}); + dxbc->FillStateInstructionInfo(initial); + + ret.push_back(initial); + + steps++; + } + + rdcarray oldworkgroup = workgroup; + + rdcarray activeMask; + + // do 100 in a chunk + for(int cycleCounter = 0; cycleCounter < 100; cycleCounter++) + { + if(active.Finished()) + break; + + // set up the old workgroup so that cross-workgroup/cross-quad operations (e.g. DDX/DDY) get + // consistent results even when we step the quad out of order. Otherwise if an operation reads + // and writes from the same register we'd trash data needed for other workgroup elements. + for(size_t i = 0; i < oldworkgroup.size(); i++) + oldworkgroup[i].variables = workgroup[i].variables; + + // calculate the current mask of which threads are active + CalcActiveMask(activeMask); + + // step all active members of the workgroup + for(int i = 0; i < workgroup.count(); i++) + { + if(activeMask[i]) + { + if(i == activeLaneIndex) + { + ShaderDebugState state; + workgroup[i].StepNext(&state, apiWrapper, oldworkgroup); + dxbc->FillStateInstructionInfo(state); + state.stepIndex = steps; + ret.push_back(state); + } + else + { + workgroup[i].StepNext(NULL, apiWrapper, oldworkgroup); + } + } + } + + steps++; + } + + return ret; +} + }; // namespace ShaderDebug #if ENABLED(ENABLE_UNIT_TESTS) diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.h b/renderdoc/driver/shaders/dxbc/dxbc_debug.h index 4368148f4..521ec56f1 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.h @@ -179,9 +179,6 @@ public: rdcarray constantBlocks; }; -#define SHADER_DEBUG_WARN_THRESHOLD 100000 -bool PromptDebugTimeout(uint32_t cycleCounter); - struct PSInputElement { PSInputElement(int regster, int element, int numWords, ShaderBuiltin attr, bool inc) @@ -272,10 +269,10 @@ public: ShaderVariable &output) = 0; }; -class State : public ShaderDebugState +class ThreadState { public: - State(int quadIdx, GlobalState *globalState, const DXBC::DXBCContainer *dxbc); + ThreadState(int workgroupIdx, GlobalState &globalState, const DXBC::DXBCContainer *dxbc); void SetHelper() { done = true; } struct @@ -287,26 +284,28 @@ public: uint32_t isFrontFace; } semantics; + uint32_t nextInstruction; + GlobalState &global; + rdcarray inputs; + rdcarray variables; + bool Finished() const; - State GetNext(DebugAPIWrapper *apiWrapper, State quad[4]) const; - - rdcarray inputs; - GlobalState *global; + void StepNext(ShaderDebugState *prevState, DebugAPIWrapper *apiWrapper, + const rdcarray &prevWorkgroup); private: // index in the pixel quad - int quadIndex; - + int workgroupIndex; bool done; // validates assignment for generation of non-normal values - bool AssignValue(ShaderVariable &dst, uint32_t dstIndex, const ShaderVariable &src, - uint32_t srcIndex, bool flushDenorm); + ShaderEvents AssignValue(ShaderVariable &dst, uint32_t dstIndex, const ShaderVariable &src, + uint32_t srcIndex, bool flushDenorm); // sets the destination operand by looking up in the register // file and applying any masking or swizzling - void SetDst(const DXBCBytecode::Operand &dstoper, const DXBCBytecode::Operation &op, - const ShaderVariable &val); + void SetDst(ShaderDebugState *state, const DXBCBytecode::Operand &dstoper, + const DXBCBytecode::Operation &op, const ShaderVariable &val); // retrieves the value of the operand, by looking up // in the register file and performing any swizzling and @@ -314,26 +313,42 @@ private: ShaderVariable GetSrc(const DXBCBytecode::Operand &oper, const DXBCBytecode::Operation &op, bool allowFlushing = true) const; - ShaderVariable DDX(bool fine, State quad[4], const DXBCBytecode::Operand &oper, - const DXBCBytecode::Operation &op) const; - ShaderVariable DDY(bool fine, State quad[4], const DXBCBytecode::Operand &oper, - const DXBCBytecode::Operation &op) const; - - VarType OperationType(const DXBCBytecode::OpcodeType &op) const; - bool OperationFlushing(const DXBCBytecode::OpcodeType &op) const; + ShaderVariable DDX(bool fine, const rdcarray &quad, + const DXBCBytecode::Operand &oper, const DXBCBytecode::Operation &op) const; + ShaderVariable DDY(bool fine, const rdcarray &quad, + const DXBCBytecode::Operand &oper, const DXBCBytecode::Operation &op) const; const DXBC::Reflection *reflection; const DXBCBytecode::Program *program; }; -void ApplyAllDerivatives(GlobalState &global, State quad[4], int destIdx, +struct InterpretDebugger : public ShaderDebugger +{ + ShaderDebugTrace *BeginDebug(const DXBC::DXBCContainer *dxbcContainer, const ShaderReflection &refl, + const ShaderBindpointMapping &mapping, int activeIndex); + + GlobalState global; + + rdcarray workgroup; + + // convenience for access to active lane + ThreadState &activeLane() { return workgroup[activeLaneIndex]; } + int activeLaneIndex = 0; + + int steps = 0; + + const DXBC::DXBCContainer *dxbc; + + void CalcActiveMask(rdcarray &activeMask); + rdcarray ContinueDebug(DebugAPIWrapper *apiWrapper); +}; + +void ApplyAllDerivatives(GlobalState &global, rdcarray &quad, int destIdx, const rdcarray &initialValues, float *data); -void CreateShaderDebugStateAndTrace(State &initialState, ShaderDebugTrace &trace, - DXBC::DXBCContainer *dxbc, const ShaderReflection &refl, - const ShaderBindpointMapping &mapping); -void AddCBufferToDebugTrace(const DXBCBytecode::Program &program, ShaderDebugTrace &trace, - const ShaderReflection &refl, const ShaderBindpointMapping &mapping, - const BindingSlot &slot, bytebuf &cbufData); +void AddCBufferToGlobalState(const DXBCBytecode::Program &program, GlobalState &global, + rdcarray &sourceVars, + const ShaderReflection &refl, const ShaderBindpointMapping &mapping, + const BindingSlot &slot, bytebuf &cbufData); }; // namespace ShaderDebug diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 9228a8c40..f30c4a916 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -3871,25 +3871,31 @@ void VulkanReplay::RefreshDerivedReplacements() m_pDriver->vkDestroyPipeline(dev, pipe, NULL); } -ShaderDebugTrace VulkanReplay::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, - uint32_t idx, uint32_t instOffset, uint32_t vertOffset) +ShaderDebugTrace *VulkanReplay::DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, + uint32_t idx, uint32_t instOffset, uint32_t vertOffset) { VULKANNOTIMP("DebugVertex"); - return ShaderDebugTrace(); + return new ShaderDebugTrace(); } -ShaderDebugTrace VulkanReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, - uint32_t primitive) +ShaderDebugTrace *VulkanReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, + uint32_t sample, uint32_t primitive) { VULKANNOTIMP("DebugPixel"); - return ShaderDebugTrace(); + return new ShaderDebugTrace(); } -ShaderDebugTrace VulkanReplay::DebugThread(uint32_t eventId, const uint32_t groupid[3], - const uint32_t threadid[3]) +ShaderDebugTrace *VulkanReplay::DebugThread(uint32_t eventId, const uint32_t groupid[3], + const uint32_t threadid[3]) { VULKANNOTIMP("DebugThread"); - return ShaderDebugTrace(); + return new ShaderDebugTrace(); +} + +rdcarray VulkanReplay::ContinueDebug(ShaderDebugger *debugger) +{ + VULKANNOTIMP("ContinueDebug"); + return {}; } ResourceId VulkanReplay::CreateProxyTexture(const TextureDescription &templateTex) diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index d9b9a2620..35ea8127c 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -367,12 +367,14 @@ public: rdcarray PixelHistory(rdcarray events, ResourceId target, uint32_t x, uint32_t y, const Subresource &sub, CompType typeCast); - ShaderDebugTrace DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, uint32_t idx, - uint32_t instOffset, uint32_t vertOffset); - ShaderDebugTrace DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, - uint32_t primitive); - ShaderDebugTrace DebugThread(uint32_t eventId, const uint32_t groupid[3], - const uint32_t threadid[3]); + ShaderDebugTrace *DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, uint32_t idx, + uint32_t instOffset, uint32_t vertOffset); + ShaderDebugTrace *DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, + uint32_t primitive); + ShaderDebugTrace *DebugThread(uint32_t eventId, const uint32_t groupid[3], + const uint32_t threadid[3]); + rdcarray ContinueDebug(ShaderDebugger *debugger); + uint32_t PickVertex(uint32_t eventId, int32_t width, int32_t height, const MeshDisplay &cfg, uint32_t x, uint32_t y); diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index bae0e6aff..c7a036d71 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -375,17 +375,26 @@ void DoSerialise(SerialiserType &ser, LineColumnInfo &el) SIZE_CHECK(24); } +template +void DoSerialise(SerialiserType &ser, ShaderVariableChange &el) +{ + SERIALISE_MEMBER(before); + SERIALISE_MEMBER(after); + + SIZE_CHECK(384); +} + template void DoSerialise(SerialiserType &ser, ShaderDebugState &el) { - SERIALISE_MEMBER(variables); - SERIALISE_MEMBER(sourceVars); - SERIALISE_MEMBER(modified); SERIALISE_MEMBER(nextInstruction); + SERIALISE_MEMBER(stepIndex); SERIALISE_MEMBER(flags); + SERIALISE_MEMBER(changes); + SERIALISE_MEMBER(sourceVars); SERIALISE_MEMBER(callstack); - SIZE_CHECK(104); + SIZE_CHECK(88); } template @@ -396,11 +405,18 @@ void DoSerialise(SerialiserType &ser, ShaderDebugTrace &el) SERIALISE_MEMBER(readOnlyResources); SERIALISE_MEMBER(readWriteResources); SERIALISE_MEMBER(sourceVars); - SERIALISE_MEMBER(states); - SERIALISE_MEMBER(hasSourceMapping); SERIALISE_MEMBER(lineInfo); + SERIALISE_MEMBER(hasSourceMapping); - SIZE_CHECK(176); + // serialise the debugger pointer entirely opaquely, this is only used for replay proxying + uint64_t debugger = 0; + if(ser.IsWriting()) + debugger = (uint64_t)(uintptr_t)el.debugger; + SERIALISE_ELEMENT(debugger); + if(ser.IsReading()) + el.debugger = (ShaderDebugger *)debugger; + + SIZE_CHECK(160); } template diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index 180d49c39..662cf165f 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -1626,9 +1626,8 @@ ShaderDebugTrace *ReplayController::DebugVertex(uint32_t vertid, uint32_t instid { CHECK_REPLAY_THREAD(); - ShaderDebugTrace *ret = new ShaderDebugTrace; - - *ret = m_pDevice->DebugVertex(m_EventID, vertid, instid, idx, instOffset, vertOffset); + ShaderDebugTrace *ret = + m_pDevice->DebugVertex(m_EventID, vertid, instid, idx, instOffset, vertOffset); SetFrameEvent(m_EventID, true); @@ -1640,9 +1639,7 @@ ShaderDebugTrace *ReplayController::DebugPixel(uint32_t x, uint32_t y, uint32_t { CHECK_REPLAY_THREAD(); - ShaderDebugTrace *ret = new ShaderDebugTrace; - - *ret = m_pDevice->DebugPixel(m_EventID, x, y, sample, primitive); + ShaderDebugTrace *ret = m_pDevice->DebugPixel(m_EventID, x, y, sample, primitive); SetFrameEvent(m_EventID, true); @@ -1653,9 +1650,18 @@ ShaderDebugTrace *ReplayController::DebugThread(const uint32_t groupid[3], const { CHECK_REPLAY_THREAD(); - ShaderDebugTrace *ret = new ShaderDebugTrace; + ShaderDebugTrace *ret = m_pDevice->DebugThread(m_EventID, groupid, threadid); - *ret = m_pDevice->DebugThread(m_EventID, groupid, threadid); + SetFrameEvent(m_EventID, true); + + return ret; +} + +rdcarray ReplayController::ContinueDebug(ShaderDebugger *debugger) +{ + CHECK_REPLAY_THREAD(); + + rdcarray ret = m_pDevice->ContinueDebug(debugger); SetFrameEvent(m_EventID, true); @@ -1666,7 +1672,11 @@ void ReplayController::FreeTrace(ShaderDebugTrace *trace) { CHECK_REPLAY_THREAD(); - delete trace; + if(trace) + { + SAFE_DELETE(trace->debugger); + delete trace; + } } rdcarray ReplayController::GetCBufferVariableContents( diff --git a/renderdoc/replay/replay_controller.h b/renderdoc/replay/replay_controller.h index 33e3c9008..12f404241 100644 --- a/renderdoc/replay/replay_controller.h +++ b/renderdoc/replay/replay_controller.h @@ -189,6 +189,7 @@ public: uint32_t vertOffset); ShaderDebugTrace *DebugPixel(uint32_t x, uint32_t y, uint32_t sample, uint32_t primitive); ShaderDebugTrace *DebugThread(const uint32_t groupid[3], const uint32_t threadid[3]); + rdcarray ContinueDebug(ShaderDebugger *debugger); void FreeTrace(ShaderDebugTrace *trace); MeshFormat GetPostVSData(uint32_t instID, uint32_t viewID, MeshDataStage stage); diff --git a/renderdoc/replay/replay_driver.h b/renderdoc/replay/replay_driver.h index b7132067e..542a3e83a 100644 --- a/renderdoc/replay/replay_driver.h +++ b/renderdoc/replay/replay_driver.h @@ -179,12 +179,13 @@ public: virtual rdcarray PixelHistory(rdcarray events, ResourceId target, uint32_t x, uint32_t y, const Subresource &sub, CompType typeCast) = 0; - virtual ShaderDebugTrace DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, - uint32_t idx, uint32_t instOffset, uint32_t vertOffset) = 0; - virtual ShaderDebugTrace DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, - uint32_t primitive) = 0; - virtual ShaderDebugTrace DebugThread(uint32_t eventId, const uint32_t groupid[3], - const uint32_t threadid[3]) = 0; + virtual ShaderDebugTrace *DebugVertex(uint32_t eventId, uint32_t vertid, uint32_t instid, + uint32_t idx, uint32_t instOffset, uint32_t vertOffset) = 0; + virtual ShaderDebugTrace *DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, uint32_t sample, + uint32_t primitive) = 0; + virtual ShaderDebugTrace *DebugThread(uint32_t eventId, const uint32_t groupid[3], + const uint32_t threadid[3]) = 0; + virtual rdcarray ContinueDebug(ShaderDebugger *debugger) = 0; virtual ResourceId RenderOverlay(ResourceId texid, CompType typeCast, FloatVector clearCol, DebugOverlay overlay, uint32_t eventId,