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,