From 0e0ff8714c2980f1a7a5e19ce5cb6f3bbb35e974 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 29 Jan 2020 14:10:21 +0000 Subject: [PATCH] Add fake source-mapping information for constants in debug traces * cbuffer and input registers no longer have any names elided into them, they are 'raw' registers. The source mapping provides the information of where they come from as best as possible. * Since we're now using source-mapping for constants, fix some source mapping issues with arrays, nested structs/arrays, and matrix major-ness. --- qrenderdoc/Windows/ShaderViewer.cpp | 565 +++++++++--------- qrenderdoc/Windows/ShaderViewer.h | 8 +- qrenderdoc/Windows/ShaderViewer.ui | 13 +- renderdoc/api/replay/shader_types.h | 16 +- renderdoc/driver/d3d11/d3d11_shaderdebug.cpp | 6 +- renderdoc/driver/d3d12/d3d12_shaderdebug.cpp | 4 +- .../driver/shaders/dxbc/dxbc_container.h | 2 +- renderdoc/driver/shaders/dxbc/dxbc_debug.cpp | 395 ++++++++---- renderdoc/driver/shaders/dxbc/dxbc_debug.h | 5 - renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp | 2 +- renderdoc/driver/shaders/dxbc/dxbc_sdbg.h | 2 +- renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp | 148 +++-- renderdoc/driver/shaders/dxbc/dxbc_spdb.h | 3 +- renderdoc/replay/renderdoc_serialise.inl | 5 +- 14 files changed, 697 insertions(+), 477 deletions(-) diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index 9e113d03e..307869cdc 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -55,9 +55,17 @@ struct VariableTag return cat == o.cat && index == o.index && member == o.member; } }; + +struct SourceVariableTag +{ + SourceVariableTag(QString name = QString(), uint32_t offs = 0) : fullname(name), offset(offs) {} + QString fullname; + uint32_t offset; +}; }; Q_DECLARE_METATYPE(VariableTag); +Q_DECLARE_METATYPE(SourceVariableTag); ShaderViewer::ShaderViewer(ICaptureContext &ctx, QWidget *parent) : QFrame(parent), ui(new Ui::ShaderViewer), m_Ctx(ctx) @@ -65,8 +73,8 @@ ShaderViewer::ShaderViewer(ICaptureContext &ctx, QWidget *parent) ui->setupUi(this); ui->constants->setFont(Formatter::PreferredFont()); - ui->registers->setFont(Formatter::PreferredFont()); - ui->locals->setFont(Formatter::PreferredFont()); + ui->debugVars->setFont(Formatter::PreferredFont()); + ui->sourceVars->setFont(Formatter::PreferredFont()); ui->watch->setFont(Formatter::PreferredFont()); ui->inputSig->setFont(Formatter::PreferredFont()); ui->outputSig->setFont(Formatter::PreferredFont()); @@ -224,10 +232,10 @@ void ShaderViewer::editShader(bool customShader, ShaderStage stage, const QStrin // hide debugging windows ui->watch->hide(); - ui->registers->hide(); + ui->debugVars->hide(); ui->constants->hide(); ui->callstack->hide(); - ui->locals->hide(); + ui->sourceVars->hide(); ui->snippets->setVisible(customShader); @@ -493,23 +501,25 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR ui->debugToggle->setText(tr("HLSL Unavailable")); } - ui->registers->setColumns({tr("Name"), tr("Type"), tr("Value")}); - ui->registers->header()->setSectionResizeMode(0, QHeaderView::Interactive); - ui->registers->header()->setSectionResizeMode(1, QHeaderView::Interactive); - ui->registers->header()->setSectionResizeMode(2, QHeaderView::Stretch); + ui->debugVars->setColumns({tr("Name"), tr("Value")}); + ui->debugVars->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + ui->debugVars->header()->setSectionResizeMode(1, QHeaderView::Interactive); - ui->locals->setColumns({tr("Name"), tr("Register(s)"), tr("Type"), tr("Value")}); - ui->locals->header()->setSectionResizeMode(0, QHeaderView::Interactive); - ui->locals->header()->setSectionResizeMode(1, QHeaderView::Interactive); - ui->locals->header()->setSectionResizeMode(2, QHeaderView::Interactive); - ui->locals->header()->setSectionResizeMode(3, QHeaderView::Stretch); + ui->sourceVars->setColumns({tr("Name"), tr("Register(s)"), tr("Type"), tr("Value")}); + ui->sourceVars->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + ui->sourceVars->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + ui->sourceVars->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); + ui->sourceVars->header()->setSectionResizeMode(3, QHeaderView::Interactive); - ui->constants->setColumns({tr("Name"), tr("Type"), tr("Value")}); - ui->constants->header()->setSectionResizeMode(0, QHeaderView::Interactive); - ui->constants->header()->setSectionResizeMode(1, QHeaderView::Interactive); - ui->constants->header()->setSectionResizeMode(2, QHeaderView::Stretch); + ui->constants->setColumns({tr("Name"), tr("Register(s)"), tr("Type"), tr("Value")}); + ui->constants->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + ui->constants->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + ui->constants->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); + ui->constants->header()->setSectionResizeMode(3, QHeaderView::Interactive); - ui->registers->setTooltipElidedItems(false); + ui->constants->header()->resizeSection(0, 80); + + ui->debugVars->setTooltipElidedItems(false); ui->constants->setTooltipElidedItems(false); ui->watch->setWindowTitle(tr("Watch")); @@ -519,39 +529,41 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR ui->docking->setToolWindowProperties( ui->watch, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); - ui->registers->setWindowTitle(tr("Registers")); + ui->debugVars->setWindowTitle(tr("Variable Values")); ui->docking->addToolWindow( - ui->registers, + ui->debugVars, ToolWindowManager::AreaReference(ToolWindowManager::AddTo, ui->docking->areaOf(ui->watch))); ui->docking->setToolWindowProperties( - ui->registers, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); + ui->debugVars, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); ui->constants->setWindowTitle(tr("Constants && Resources")); ui->docking->addToolWindow( ui->constants, ToolWindowManager::AreaReference(ToolWindowManager::LeftOf, - ui->docking->areaOf(ui->registers), 0.5f)); + ui->docking->areaOf(ui->debugVars), 0.5f)); ui->docking->setToolWindowProperties( ui->constants, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); - ui->callstack->setWindowTitle(tr("Callstack")); - ui->docking->addToolWindow( - ui->callstack, ToolWindowManager::AreaReference(ToolWindowManager::RightOf, - ui->docking->areaOf(ui->registers), 0.2f)); - ui->docking->setToolWindowProperties( - ui->callstack, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); - if(m_Trace->hasSourceMapping) { - ui->locals->setWindowTitle(tr("Local Variables")); + ui->callstack->setWindowTitle(tr("Callstack")); ui->docking->addToolWindow( - ui->locals, ToolWindowManager::AreaReference(ToolWindowManager::AddTo, - ui->docking->areaOf(ui->registers))); + ui->callstack, ToolWindowManager::AreaReference( + ToolWindowManager::RightOf, ui->docking->areaOf(ui->debugVars), 0.2f)); ui->docking->setToolWindowProperties( - ui->locals, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); + ui->callstack, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); + + ui->sourceVars->setWindowTitle(tr("High-level Variables")); + ui->docking->addToolWindow( + ui->sourceVars, ToolWindowManager::AreaReference(ToolWindowManager::AddTo, + ui->docking->areaOf(ui->debugVars))); + ui->docking->setToolWindowProperties( + ui->sourceVars, + ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); } else { - ui->locals->hide(); + ui->callstack->hide(); + ui->sourceVars->hide(); } m_Line2Insts.resize(m_ShaderDetails->debugInfo.files.count()); @@ -628,7 +640,7 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR // event filter to pick up tooltip events ui->constants->installEventFilter(this); - ui->registers->installEventFilter(this); + ui->debugVars->installEventFilter(this); ui->watch->installEventFilter(this); SetCurrentStep(0); @@ -638,11 +650,11 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR ui->watch->setContextMenuPolicy(Qt::CustomContextMenu); QObject::connect(ui->watch, &RDTableWidget::customContextMenuRequested, this, &ShaderViewer::variables_contextMenu); - ui->registers->setContextMenuPolicy(Qt::CustomContextMenu); - QObject::connect(ui->registers, &RDTreeWidget::customContextMenuRequested, this, + ui->debugVars->setContextMenuPolicy(Qt::CustomContextMenu); + QObject::connect(ui->debugVars, &RDTreeWidget::customContextMenuRequested, this, &ShaderViewer::variables_contextMenu); - ui->locals->setContextMenuPolicy(Qt::CustomContextMenu); - QObject::connect(ui->locals, &RDTreeWidget::customContextMenuRequested, this, + ui->sourceVars->setContextMenuPolicy(Qt::CustomContextMenu); + QObject::connect(ui->sourceVars, &RDTreeWidget::customContextMenuRequested, this, &ShaderViewer::variables_contextMenu); ui->watch->insertRow(0); @@ -663,9 +675,9 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR { // hide watch, constants, variables ui->watch->hide(); - ui->registers->hide(); + ui->debugVars->hide(); ui->constants->hide(); - ui->locals->hide(); + ui->sourceVars->hide(); ui->callstack->hide(); // hide debugging toolbar buttons @@ -1118,8 +1130,8 @@ void ShaderViewer::variables_contextMenu(const QPoint &pos) addWatch.setEnabled(tree->selectedItem() != NULL); QObject::connect(&addWatch, &QAction::triggered, [this, tree] { - if(tree == ui->locals) - AddWatch(tree->selectedItem()->tag().toString()); + if(tree == ui->sourceVars) + AddWatch(tree->selectedItem()->tag().value().fullname); else AddWatch(tree->selectedItem()->text(0)); }); @@ -1168,9 +1180,9 @@ void ShaderViewer::disassembly_buttonReleased(QMouseEvent *event) start = 0; end = m_DisassemblyView->length(); - for(int i = 0; i < ui->registers->topLevelItemCount(); i++) + for(int i = 0; i < ui->debugVars->topLevelItemCount(); i++) { - RDTreeWidgetItem *item = ui->registers->topLevelItem(i); + RDTreeWidgetItem *item = ui->debugVars->topLevelItem(i); if(item->tag().value() == tag) item->setBackgroundColor(QColor::fromHslF( 0.333f, 1.0f, qBound(0.25, palette().color(QPalette::Base).lightnessF(), 0.85))); @@ -1522,60 +1534,6 @@ QString ShaderViewer::stringRep(const ShaderVariable &var, bool useType) return RowString(var, 0, VarType::Float); } -RDTreeWidgetItem *ShaderViewer::makeResourceRegister(const Bindpoint &bind, uint32_t idx, - const BoundResource &bound, - const ShaderResource &res) -{ - QString name = QFormatStr(" (%1)").arg(res.name); - - const TextureDescription *tex = m_Ctx.GetTexture(bound.resourceId); - const BufferDescription *buf = m_Ctx.GetBuffer(bound.resourceId); - - QChar regChar(QLatin1Char('u')); - - if(res.isReadOnly) - regChar = QLatin1Char('t'); - - QString regname; - - if(m_Ctx.APIProps().pipelineType == GraphicsAPI::D3D12) - { - if(bind.arraySize == 1) - regname = QFormatStr("%1%2:%3").arg(regChar).arg(bind.bindset).arg(bind.bind); - else - regname = QFormatStr("%1%2:%3[%4]").arg(regChar).arg(bind.bindset).arg(bind.bind).arg(idx); - } - else - { - regname = QFormatStr("%1%2").arg(regChar).arg(bind.bind); - } - - if(tex) - { - QString type = QFormatStr("%1x%2x%3[%4] @ %5 - %6") - .arg(tex->width) - .arg(tex->height) - .arg(tex->depth > 1 ? tex->depth : tex->arraysize) - .arg(tex->mips) - .arg(tex->format.Name()) - .arg(m_Ctx.GetResourceName(bound.resourceId)); - - return new RDTreeWidgetItem({regname + name, lit("Texture"), type}); - } - else if(buf) - { - QString type = - QFormatStr("%1 - %2").arg(buf->length).arg(m_Ctx.GetResourceName(bound.resourceId)); - - return new RDTreeWidgetItem({regname + name, lit("Buffer"), type}); - } - else - { - return new RDTreeWidgetItem( - {regname + name, lit("Resource"), m_Ctx.GetResourceName(bound.resourceId)}); - } -} - QString ShaderViewer::targetName(const ShaderProcessingTool &disasm) { return lit("%1 (%2)").arg(ToQStr(disasm.output)).arg(disasm.name); @@ -1609,7 +1567,7 @@ void ShaderViewer::addFileList() list, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); } -void ShaderViewer::combineStructures(RDTreeWidgetItem *root) +void ShaderViewer::combineStructures(RDTreeWidgetItem *root, int skipPrefixLength) { RDTreeWidgetItem temp; @@ -1630,8 +1588,8 @@ void ShaderViewer::combineStructures(RDTreeWidgetItem *root) QString name = child->text(0); - int dotIndex = name.indexOf(QLatin1Char('.')); - int arrIndex = name.indexOf(QLatin1Char('[')); + int dotIndex = name.indexOf(QLatin1Char('.'), skipPrefixLength); + int arrIndex = name.indexOf(QLatin1Char('['), skipPrefixLength); // if this node doesn't have any segments, just move it across. if(dotIndex < 0 && arrIndex < 0) @@ -1642,12 +1600,9 @@ void ShaderViewer::combineStructures(RDTreeWidgetItem *root) // store the index of the first separator int sepIndex = dotIndex; - bool isArray = false; + bool isLeafArray = (sepIndex == -1); if(sepIndex == -1 || (arrIndex > 0 && arrIndex < sepIndex)) - { sepIndex = arrIndex; - isArray = true; - } // we have a valid node to match against, record the prefix (including separator character) QString prefix = name.mid(0, sepIndex + 1); @@ -1681,9 +1636,13 @@ void ShaderViewer::combineStructures(RDTreeWidgetItem *root) continue; } - // sort the children by name + // sort the children by offset, then name (in case all offsets are empty) std::sort(matches.begin(), matches.end(), [](const RDTreeWidgetItem *a, const RDTreeWidgetItem *b) { + SourceVariableTag at = a->tag().value(); + SourceVariableTag bt = b->tag().value(); + if(at.offset != bt.offset) + return at.offset < bt.offset; return a->text(0) < b->text(0); }); @@ -1696,7 +1655,7 @@ void ShaderViewer::combineStructures(RDTreeWidgetItem *root) // add all the children (stripping the prefix from their name) for(RDTreeWidgetItem *item : matches) { - if(!isArray) + if(sepIndex == dotIndex) item->setText(0, item->text(0).mid(sepIndex + 1)); parent->addChild(item); @@ -1707,8 +1666,13 @@ void ShaderViewer::combineStructures(RDTreeWidgetItem *root) } // recurse and combine members of this object if a struct - if(!isArray) - combineStructures(parent); + if(!isLeafArray) + { + if(sepIndex != dotIndex) + combineStructures(parent, sepIndex + 1); + else + combineStructures(parent); + } // now add to the list temp.insertChild(0, parent); @@ -1724,7 +1688,7 @@ void ShaderViewer::combineStructures(RDTreeWidgetItem *root) RDTreeWidgetItem *ShaderViewer::findLocal(RDTreeWidgetItem *root, QString name) { - if(root->tag().toString() == name) + if(root->tag().value().fullname == name) return root; for(int i = 0; i < root->childCount(); i++) @@ -1858,31 +1822,67 @@ void ShaderViewer::updateDebugging() if(ui->constants->topLevelItemCount() == 0) { + // track all debug variables that have been mapped by source vars + QSet varsMapped; + + RDTreeWidgetItem fakeroot; + + for(const SourceVariableMapping &sourceVar : m_Trace->sourceVars) + { + for(const DebugVariableReference &r : sourceVar.variables) + varsMapped.insert(r.name); + + fakeroot.addChild(makeSourceVariableNode(sourceVar)); + } + + // recursively combine nodes with the same prefix together + combineStructures(&fakeroot); + + while(fakeroot.childCount() > 0) + ui->constants->addTopLevelItem(fakeroot.takeChild(0)); + + // add any raw registers that weren't mapped with something better. We assume for inputs that + // everything has a source mapping, even if it's faked from reflection info, but just to be sure + // we add any remainders here. Constants might be un-touched by reflection info for(int i = 0; i < m_Trace->constantBlocks.count(); i++) { + rdcstr name = m_Trace->constantBlocks[i].name; + RDTreeWidgetItem *node = new RDTreeWidgetItem({name, name, lit("Constant"), QString()}); + for(int j = 0; j < m_Trace->constantBlocks[i].members.count(); j++) { if(m_Trace->constantBlocks[i].members[j].rows > 0 || m_Trace->constantBlocks[i].members[j].columns > 0) { - RDTreeWidgetItem *node = - new RDTreeWidgetItem({m_Trace->constantBlocks[i].members[j].name, lit("cbuffer"), - stringRep(m_Trace->constantBlocks[i].members[j], false)}); - node->setTag(QVariant::fromValue(VariableTag(VariableCategory::Constants, j, i))); - - ui->constants->addTopLevelItem(node); + name = m_Trace->constantBlocks[i].members[j].name; + if(!varsMapped.contains(name)) + node->addChild( + new RDTreeWidgetItem({name, name, lit("Constant"), + stringRep(m_Trace->constantBlocks[i].members[j], false)})); } } + + if(node->childCount() == 0) + { + delete node; + continue; + } + + node->setTag(QVariant::fromValue(VariableTag(VariableCategory::Constants, i))); + ui->constants->addTopLevelItem(node); } for(int i = 0; i < m_Trace->inputs.count(); i++) { const ShaderVariable &input = m_Trace->inputs[i]; + if(varsMapped.contains(input.name)) + continue; + if(input.rows > 0 || input.columns > 0) { - RDTreeWidgetItem *node = new RDTreeWidgetItem( - {input.name, ToQStr(input.type) + lit(" input"), stringRep(input, true)}); + RDTreeWidgetItem *node = + new RDTreeWidgetItem({input.name, input.name, lit("Input"), stringRep(input, false)}); node->setTag(QVariant::fromValue(VariableTag(VariableCategory::Inputs, i))); ui->constants->addTopLevelItem(node); @@ -1908,22 +1908,26 @@ void ShaderViewer::updateDebugging() if(idx < 0 || rw[idx].resources.isEmpty()) continue; + QString bindname = makeBindName(bind, false); + if(bind.arraySize == 1) { - RDTreeWidgetItem *node = makeResourceRegister(bind, 0, rw[idx].resources[0], - m_ShaderDetails->readWriteResources[i]); + RDTreeWidgetItem *node = + new RDTreeWidgetItem({bindname, m_ShaderDetails->readWriteResources[i].name, + lit("RW Resource"), ToQStr(rw[idx].resources[0].resourceId)}); if(node) ui->constants->addTopLevelItem(node); } else { RDTreeWidgetItem *node = - new RDTreeWidgetItem({m_ShaderDetails->readWriteResources[i].name, + new RDTreeWidgetItem({bindname, m_ShaderDetails->readWriteResources[i].name, QFormatStr("[%1]").arg(bind.arraySize), QString()}); for(uint32_t a = 0; a < bind.arraySize; a++) - node->addChild(makeResourceRegister(bind, a, rw[idx].resources[a], - m_ShaderDetails->readWriteResources[i])); + node->addChild(new RDTreeWidgetItem( + {QFormatStr("%1[%2]").arg(bindname).arg(a), m_ShaderDetails->readWriteResources[i].name, + lit("Resource"), ToQStr(rw[idx].resources[a].resourceId)})); tree = true; @@ -1945,183 +1949,76 @@ void ShaderViewer::updateDebugging() if(idx < 0 || ro[idx].resources.isEmpty()) continue; + QString bindname = makeBindName(bind, true); + if(bind.arraySize == 1) { - RDTreeWidgetItem *node = makeResourceRegister(bind, 0, ro[idx].resources[0], - m_ShaderDetails->readOnlyResources[i]); + RDTreeWidgetItem *node = + new RDTreeWidgetItem({bindname, m_ShaderDetails->readOnlyResources[i].name, + lit("Resource"), ToQStr(ro[idx].resources[0].resourceId)}); if(node) ui->constants->addTopLevelItem(node); } else { RDTreeWidgetItem *node = - new RDTreeWidgetItem({m_ShaderDetails->readOnlyResources[i].name, + new RDTreeWidgetItem({bindname, m_ShaderDetails->readOnlyResources[i].name, + m_ShaderDetails->readOnlyResources[i].name, QFormatStr("[%1]").arg(bind.arraySize), QString()}); for(uint32_t a = 0; a < bind.arraySize; a++) - node->addChild(makeResourceRegister(bind, a, ro[idx].resources[a], - m_ShaderDetails->readOnlyResources[i])); + node->addChild(new RDTreeWidgetItem( + {QFormatStr("%1[%2]").arg(bindname).arg(a), m_ShaderDetails->readOnlyResources[i].name, + lit("Resource"), ToQStr(ro[idx].resources[a].resourceId)})); tree = true; ui->constants->addTopLevelItem(node); } } - - if(tree) - { - ui->constants->setIndentation(20); - ui->constants->setRootIsDecorated(true); - } } if(m_Trace->hasSourceMapping) { RDTreeViewExpansionState expansion; - ui->locals->saveExpansion(expansion, 0); + ui->sourceVars->saveExpansion(expansion, 0); - ui->locals->clear(); - - const QString xyzw = lit("xyzw"); + ui->sourceVars->clear(); RDTreeWidgetItem fakeroot; - for(size_t lidx = 0; lidx < state.sourceVars.size(); lidx++) + const rdcarray &sourceVars = state.sourceVars; + + for(size_t lidx = 0; lidx < sourceVars.size(); lidx++) { // iterate in reverse order, so newest locals tend to end up on top - const SourceVariableMapping &l = state.sourceVars[state.sourceVars.size() - 1 - lidx]; - - QString localName = l.name; - QString regNames, typeName; - QString value; + const SourceVariableMapping &l = sourceVars[sourceVars.size() - 1 - lidx]; bool modified = false; - if(l.type == VarType::UInt) - typeName = lit("uint"); - else if(l.type == VarType::SInt) - typeName = lit("int"); - else if(l.type == VarType::Float) - typeName = lit("float"); - else if(l.type == VarType::Double) - typeName = lit("double"); - - const ShaderVariable *variable = NULL; - if(!l.variables.empty() && l.variables[0].type == DebugVariableType::Variable) { - for(const ShaderVariable &v : state.variables) + for(const DebugVariableReference &r : l.variables) { - if(v.name == l.variables[0].name) + for(const DebugVariableReference &mr : state.modified) { - variable = &v; + if(mr.name == r.name) + { + modified = true; + break; + } + } + + if(modified) break; - } } } - if(variable && !variable->members.empty()) - { - typeName += lit("[]"); - - regNames = l.variables[0].name; - - for(const DebugVariableReference &mr : state.modified) - { - if(mr.name == l.variables[0].name) - { - modified = true; - break; - } - } - } - else - { - if(l.rows > 1) - typeName += QFormatStr("%1x%2").arg(l.rows).arg(l.columns); - else - typeName += QString::number(l.columns); - - for(size_t i = 0; i < l.variables.size(); i++) - { - const DebugVariableReference &r = l.variables[i]; - - if(l.variables[0].type == DebugVariableType::Variable) - { - for(const DebugVariableReference &mr : state.modified) - { - if(mr.name == r.name) - { - modified = true; - break; - } - } - } - - if(!value.isEmpty()) - value += lit(", "); - if(!regNames.isEmpty()) - regNames += lit(", "); - - if(r.name.empty()) - { - regNames += lit("-"); - value += lit("?"); - continue; - } - - const ShaderVariable *reg = GetRegisterVariable(r); - - if(reg) - { - // if the previous register was the same, just append our component - if(i > 0 && r.name == l.variables[i - 1].name) - { - // remove the auto-appended ", " - there must be one because this isn't the first - // register - regNames.chop(2); - regNames += xyzw[r.component]; - } - else - { - regNames += QFormatStr("%1.%2").arg(reg->name).arg(xyzw[r.component]); - } - - if(l.type == VarType::UInt) - value += Formatter::Format(reg->value.uv[r.component]); - else if(l.type == VarType::SInt) - value += Formatter::Format(reg->value.iv[r.component]); - else if(l.type == VarType::Float) - value += Formatter::Format(reg->value.fv[r.component]); - else if(l.type == VarType::Double) - value += Formatter::Format(reg->value.dv[r.component]); - } - else - { - regNames += lit(""); - value += lit(""); - } - } - } - - RDTreeWidgetItem *node = new RDTreeWidgetItem({localName, regNames, typeName, value}); - - node->setTag(localName); + RDTreeWidgetItem *node = makeSourceVariableNode(l); if(modified) node->setForegroundColor(QColor(Qt::red)); - if(variable) - { - for(int t = 0; t < variable->members.count(); t++) - { - node->addChild(new RDTreeWidgetItem({ - QFormatStr("%1[%2]").arg(localName).arg(t), QFormatStr("%1[%2]").arg(regNames).arg(t), - typeName, RowString(variable->members[t], 0, l.type), - })); - } - } - fakeroot.addChild(node); } @@ -2129,29 +2026,27 @@ void ShaderViewer::updateDebugging() combineStructures(&fakeroot); while(fakeroot.childCount() > 0) - ui->locals->addTopLevelItem(fakeroot.takeChild(0)); + ui->sourceVars->addTopLevelItem(fakeroot.takeChild(0)); - ui->locals->applyExpansion(expansion, 0); + ui->sourceVars->applyExpansion(expansion, 0); } - if(ui->registers->topLevelItemCount() == 0) + if(ui->debugVars->topLevelItemCount() == 0) { for(int i = 0; i < state.variables.count(); i++) { - RDTreeWidgetItem *node = - new RDTreeWidgetItem({state.variables[i].name, lit("variable"), QString()}); + RDTreeWidgetItem *node = new RDTreeWidgetItem({state.variables[i].name, QString()}); for(int t = 0; t < state.variables[i].members.count(); t++) - node->addChild( - new RDTreeWidgetItem({state.variables[i].members[t].name, lit("variable"), QString()})); - ui->registers->addTopLevelItem(node); + node->addChild(new RDTreeWidgetItem({state.variables[i].members[t].name, QString()})); + ui->debugVars->addTopLevelItem(node); } } - ui->registers->beginUpdate(); + ui->debugVars->beginUpdate(); for(int i = 0; i < state.variables.count(); i++) { - RDTreeWidgetItem *node = ui->registers->topLevelItem(i); + RDTreeWidgetItem *node = ui->debugVars->topLevelItem(i); bool modified = false; @@ -2166,7 +2061,7 @@ void ShaderViewer::updateDebugging() if(state.variables[i].members.empty()) { - node->setText(2, stringRep(state.variables[i], false)); + node->setText(1, stringRep(state.variables[i], false)); node->setTag(QVariant::fromValue(VariableTag(VariableCategory::Variables, i))); if(modified) @@ -2185,13 +2080,13 @@ void ShaderViewer::updateDebugging() else child->setForeground(QBrush()); - child->setText(2, stringRep(state.variables[i].members[t], false)); + child->setText(1, stringRep(state.variables[i].members[t], false)); child->setTag(QVariant::fromValue(VariableTag(VariableCategory::Variables, i, t))); } } } - ui->registers->endUpdate(); + ui->debugVars->endUpdate(); ui->watch->setUpdatesEnabled(false); @@ -2361,7 +2256,7 @@ void ShaderViewer::updateDebugging() { QString variablename = match.captured(1); - RDTreeWidgetItem *local = findLocal(ui->locals->invisibleRootItem(), match.captured(1)); + RDTreeWidgetItem *local = findLocal(ui->sourceVars->invisibleRootItem(), match.captured(1)); if(local) { @@ -2405,14 +2300,132 @@ void ShaderViewer::updateDebugging() ui->watch->setUpdatesEnabled(true); - ui->constants->resizeColumnToContents(0); - ui->registers->resizeColumnToContents(0); - ui->constants->resizeColumnToContents(1); - ui->registers->resizeColumnToContents(1); + ui->debugVars->resizeColumnToContents(0); + ui->debugVars->resizeColumnToContents(1); updateVariableTooltip(); } +QString ShaderViewer::makeBindName(Bindpoint &bind, bool readOnly) +{ + QChar regChar(QLatin1Char('u')); + + if(readOnly) + regChar = QLatin1Char('t'); + + if(m_Ctx.APIProps().pipelineType == GraphicsAPI::D3D12) + return QFormatStr("%1%2:%3").arg(regChar).arg(bind.bindset).arg(bind.bind); + else + return QFormatStr("%1%2").arg(regChar).arg(bind.bind); +} + +RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMapping &l) +{ + const ShaderDebugState &state = m_Trace->states[m_CurrentStep]; + + const QString xyzw = lit("xyzw"); + + QString localName = l.name; + QString regNames, typeName; + QString value; + + if(l.type == VarType::UInt) + typeName = lit("uint"); + else if(l.type == VarType::SInt) + typeName = lit("int"); + else if(l.type == VarType::Float) + typeName = lit("float"); + else if(l.type == VarType::Double) + typeName = lit("double"); + + QList children; + + { + QString childType = typeName; + + if(l.rows > 1) + { + childType += QString::number(l.columns); + typeName += QFormatStr("%1x%2").arg(l.rows).arg(l.columns); + } + else + { + typeName += QString::number(l.columns); + } + + for(size_t i = 0; i < l.variables.size(); i++) + { + const DebugVariableReference &r = l.variables[i]; + + if(!value.isEmpty()) + value += lit(", "); + if(!regNames.isEmpty()) + regNames += lit(", "); + + if(r.name.empty()) + { + regNames += lit("-"); + value += lit("?"); + } + else + { + const ShaderVariable *reg = GetRegisterVariable(r); + + if(reg) + { + // if the previous register was the same, just append our component + if(i > 0 && r.name == l.variables[i - 1].name) + { + // remove the auto-appended ", " - there must be one because this isn't the first + // register + regNames.chop(2); + regNames += xyzw[r.component]; + } + else + { + regNames += QFormatStr("%1.%2").arg(reg->name).arg(xyzw[r.component]); + } + + if(l.type == VarType::UInt) + value += Formatter::Format(reg->value.uv[r.component]); + else if(l.type == VarType::SInt) + value += Formatter::Format(reg->value.iv[r.component]); + else if(l.type == VarType::Float) + value += Formatter::Format(reg->value.fv[r.component]); + else if(l.type == VarType::Double) + value += Formatter::Format(reg->value.dv[r.component]); + } + else + { + regNames += lit(""); + value += lit(""); + } + } + + if(l.rows > 1 && l.variables.size() > l.columns) + { + if(((i + 1) % l.columns) == 0) + { + uint32_t row = (uint32_t)i / l.columns; + children.push_back(new RDTreeWidgetItem( + {QFormatStr("%1.row%2").arg(localName).arg(row), regNames, typeName, value})); + regNames = QString(); + value = QString(); + } + } + } + } + + RDTreeWidgetItem *node = new RDTreeWidgetItem({localName, regNames, typeName, value}); + + for(RDTreeWidgetItem *c : children) + node->addChild(c); + + node->setTag(QVariant::fromValue(SourceVariableTag(localName, l.offset))); + + return node; +} + const ShaderVariable *ShaderViewer::GetRegisterVariable(const DebugVariableReference &r) { if(r.type == DebugVariableType::Input) diff --git a/qrenderdoc/Windows/ShaderViewer.h b/qrenderdoc/Windows/ShaderViewer.h index a9e35c57c..e1aa4a901 100644 --- a/qrenderdoc/Windows/ShaderViewer.h +++ b/qrenderdoc/Windows/ShaderViewer.h @@ -266,6 +266,10 @@ private: void updateDebugging(); + QString makeBindName(Bindpoint &bind, bool readOnly); + + RDTreeWidgetItem *makeSourceVariableNode(const SourceVariableMapping &l); + const ShaderVariable *GetRegisterVariable(const DebugVariableReference &r); const ShaderVariable *GetRegisterVariable(VariableCategory category, int index, int member); @@ -277,8 +281,6 @@ private: ShaderEvents condition = ShaderEvents::NoEvent); QString stringRep(const ShaderVariable &var, bool useType); - RDTreeWidgetItem *makeResourceRegister(const Bindpoint &bind, uint32_t idx, - const BoundResource &ro, const ShaderResource &resources); - void combineStructures(RDTreeWidgetItem *root); + void combineStructures(RDTreeWidgetItem *root, int skipPrefixLength = 0); RDTreeWidgetItem *findLocal(RDTreeWidgetItem *root, QString name); }; diff --git a/qrenderdoc/Windows/ShaderViewer.ui b/qrenderdoc/Windows/ShaderViewer.ui index a33acfb0a..907e30c59 100644 --- a/qrenderdoc/Windows/ShaderViewer.ui +++ b/qrenderdoc/Windows/ShaderViewer.ui @@ -112,20 +112,11 @@ QFrame::NoFrame - - 0 - - - false - - - false - true - + 310 @@ -576,7 +567,7 @@ QAbstractItemView::NoSelection - + 290 diff --git a/renderdoc/api/replay/shader_types.h b/renderdoc/api/replay/shader_types.h index b8ca87a3d..4f9a99a34 100644 --- a/renderdoc/api/replay/shader_types.h +++ b/renderdoc/api/replay/shader_types.h @@ -383,7 +383,7 @@ struct SourceVariableMapping bool operator==(const SourceVariableMapping &o) const { return name == o.name && type == o.type && rows == o.rows && columns == o.columns && - elements == o.elements && variables == o.variables; + variables == o.variables; } bool operator<(const SourceVariableMapping &o) const { @@ -395,8 +395,6 @@ struct SourceVariableMapping return rows < o.rows; if(!(columns == o.columns)) return columns < o.columns; - if(!(elements == o.elements)) - return elements < o.elements; if(!(variables == o.variables)) return variables < o.variables; return false; @@ -414,8 +412,8 @@ struct SourceVariableMapping DOCUMENT("The number of columns in this variable."); uint32_t columns; - DOCUMENT("The number of array elements in this variable."); - uint32_t elements; + DOCUMENT("The offset in the parent source variable, for struct members. Useful for sorting."); + uint32_t offset; DOCUMENT(R"(The debug variables that the components of this high level variable map to. Multiple ranges could refer to the same variable if a contiguous range is mapped to - the mapping is @@ -577,6 +575,14 @@ it may have been vectorised and flattened. )"); rdcarray constantBlocks; + DOCUMENT(R"(An optional list of :class:`SourceVariableMapping` indicating which high-level source +variables map to which debug variables and includes extra type information. + +This list contains source variable mapping that is valid for the lifetime of a debug trace. It may +be empty if there is no source variable mapping that extends to the life of the debug trace. +)"); + rdcarray sourceVars; + DOCUMENT(R"(A list of :class:`ShaderDebugState` states representing the state after each instruction was executed )"); diff --git a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp index 4c8e0e480..e28fde42b 100644 --- a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp +++ b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp @@ -2100,7 +2100,7 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin ret.states = states; - ret.hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasLocals(); + ret.hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); dxbc->FillTraceLineInfo(ret); @@ -2961,7 +2961,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim traces[destIdx].states = states; - traces[destIdx].hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasLocals(); + traces[destIdx].hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); dxbc->FillTraceLineInfo(traces[destIdx]); @@ -3046,7 +3046,7 @@ ShaderDebugTrace D3D11Replay::DebugThread(uint32_t eventId, const uint32_t group ret.states = states; - ret.hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasLocals(); + ret.hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); dxbc->FillTraceLineInfo(ret); diff --git a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp index 1c0f529f6..e8c5eacd3 100644 --- a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp +++ b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp @@ -1797,7 +1797,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim traces[destIdx].states = states; - traces[destIdx].hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasLocals(); + traces[destIdx].hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); dxbc->FillTraceLineInfo(traces[destIdx]); @@ -1894,7 +1894,7 @@ ShaderDebugTrace D3D12Replay::DebugThread(uint32_t eventId, const uint32_t group ret.states = states; - ret.hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasLocals(); + ret.hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); dxbc->FillTraceLineInfo(ret); diff --git a/renderdoc/driver/shaders/dxbc/dxbc_container.h b/renderdoc/driver/shaders/dxbc/dxbc_container.h index 072a500e5..56b1a910b 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_container.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_container.h @@ -127,7 +127,7 @@ public: virtual void GetCallstack(size_t instruction, uintptr_t offset, rdcarray &callstack) const = 0; - virtual bool HasLocals() const = 0; + virtual bool HasSourceMapping() const = 0; virtual void GetLocals(DXBCBytecode::Program *program, size_t instruction, uintptr_t offset, rdcarray &locals) const = 0; }; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp index dc7d5f203..ad1b35fb2 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp @@ -1639,6 +1639,145 @@ static uint32_t PopCount(uint32_t x) return (((x + (x >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; } +void FlattenSingleVariable(const rdcstr &cbname, uint32_t byteOffset, const rdcstr &basename, + const ShaderVariable &v, rdcarray &outvars, + rdcarray &sourcevars) +{ + size_t outIdx = byteOffset / 16; + size_t outComp = (byteOffset % 16) / 4; + + if(v.rowMajor) + outvars.resize(RDCMAX(outIdx + v.rows, outvars.size())); + else + outvars.resize(RDCMAX(outIdx + v.columns, outvars.size())); + + if(outvars[outIdx].columns > 0) + { + // if we already have a variable in this slot, just copy the data for this variable and add the + // source mapping. + // We should not overlap into the next register as that's not allowed. + memcpy(&outvars[outIdx].value.uv[outComp], &v.value.uv[0], sizeof(uint32_t) * v.columns); + + SourceVariableMapping mapping; + mapping.name = basename; + mapping.type = v.type; + mapping.rows = v.rows; + mapping.columns = v.columns; + mapping.offset = byteOffset; + mapping.variables.resize(v.columns); + + for(int i = 0; i < v.columns; i++) + { + mapping.variables[i].type = DebugVariableType::Constant; + mapping.variables[i].name = StringFormat::Fmt("%s[%u]", cbname.c_str(), outIdx); + mapping.variables[i].component = uint16_t(outComp + i); + } + + sourcevars.push_back(mapping); + } + else + { + const uint32_t numRegisters = v.rowMajor ? v.rows : v.columns; + for(uint32_t reg = 0; reg < numRegisters; reg++) + { + outvars[outIdx + reg].rows = 1; + outvars[outIdx + reg].type = v.type; + outvars[outIdx + reg].isStruct = false; + outvars[outIdx + reg].columns = v.columns; + outvars[outIdx + reg].rowMajor = v.rowMajor; + } + + if(v.rowMajor) + { + for(size_t ri = 0; ri < v.rows; ri++) + memcpy(&outvars[outIdx + ri].value.uv[0], &v.value.uv[ri * v.columns], + sizeof(uint32_t) * v.columns); + } + else + { + // if we have a matrix stored in column major order, we need to transpose it back so we can + // unroll it into vectors. + for(size_t ci = 0; ci < v.columns; ci++) + for(size_t ri = 0; ri < v.rows; ri++) + outvars[outIdx + ci].value.uv[ri] = v.value.uv[ri * v.columns + ci]; + } + + SourceVariableMapping mapping; + mapping.name = basename; + mapping.type = v.type; + mapping.rows = v.rows; + mapping.columns = v.columns; + mapping.offset = byteOffset; + mapping.variables.resize(v.rows * v.columns); + + for(size_t i = 0; i < mapping.variables.size(); i++) + { + mapping.variables[i].type = DebugVariableType::Constant; + mapping.variables[i].name = + StringFormat::Fmt("%s[%u]", cbname.c_str(), uint32_t(outIdx + (outComp + i) / 4)); + mapping.variables[i].component = uint16_t((outComp + i) % 4); + } + + sourcevars.push_back(mapping); + } +} + +void FlattenVariables(const rdcstr &cbname, const rdcarray &constants, + const rdcarray &invars, rdcarray &outvars, + const rdcstr &prefix, uint32_t baseOffset, + rdcarray &sourceVars) +{ + RDCASSERTEQUAL(constants.size(), invars.size()); + + for(size_t i = 0; i < constants.size(); i++) + { + const ShaderConstant &c = constants[i]; + const ShaderVariable &v = invars[i]; + + uint32_t byteOffset = baseOffset + c.byteOffset; + + rdcstr basename = prefix + rdcstr(v.name); + + if(!v.members.empty()) + { + if(v.isStruct) + { + FlattenVariables(cbname, c.type.members, v.members, outvars, basename + ".", byteOffset, + sourceVars); + } + else + { + if(c.type.members.empty()) + { + // if there are no members in this type, it means it's a basic array - unroll directly + + for(int m = 0; m < v.members.count(); m++) + { + FlattenSingleVariable(cbname, byteOffset + m * c.type.descriptor.arrayByteStride, + StringFormat::Fmt("%s[%zu]", basename.c_str(), m), v.members[m], + outvars, sourceVars); + } + } + else + { + // otherwise we recurse into each member and flatten + + for(int m = 0; m < v.members.count(); m++) + { + FlattenVariables(cbname, c.type.members, v.members[m].members, outvars, + StringFormat::Fmt("%s[%zu].", basename.c_str(), m), + byteOffset + m * c.type.descriptor.arrayByteStride, sourceVars); + } + } + } + + continue; + } + + FlattenSingleVariable(cbname, byteOffset, basename, v, outvars, sourceVars); + } +} + State State::GetNext(GlobalState &global, DebugAPIWrapper *apiWrapper, State quad[4]) const { State s = *this; @@ -4030,6 +4169,8 @@ void CreateShaderDebugStateAndTrace(ShaderDebug::State &initialState, ShaderDebu { initialState = ShaderDebug::State(quadIdx, &trace, dxbc->GetReflection(), dxbc->GetDXBCByteCode()); + bool hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping(); + dxbc->GetDXBCByteCode()->SetupRegisterFile(initialState.variables); int32_t maxReg = -1; @@ -4074,15 +4215,54 @@ void CreateShaderDebugStateAndTrace(ShaderDebug::State &initialState, ShaderDebu dst = v; else dst.columns = RDCMAX(dst.columns, v.columns); + + { + SourceVariableMapping mapping; + mapping.name = sig.semanticIdxName; + if(mapping.name.empty() && sig.systemValue != ShaderBuiltin::Undefined) + mapping.name = ToStr(sig.systemValue); + mapping.type = v.type; + mapping.rows = 1; + mapping.columns = sig.compCount; + mapping.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; + mapping.variables.push_back(ref); + } + } + + trace.sourceVars.push_back(mapping); + } } // Put the coverage mask at the end if(inputCoverage) { - trace.inputs[maxReg + 1] = ShaderVariable( + trace.inputs.back() = ShaderVariable( dxbc->GetDXBCByteCode()->GetRegisterName(DXBCBytecode::TYPE_INPUT_COVERAGE_MASK, 0), 0U, 0U, 0U, 0U); - trace.inputs[maxReg + 1].columns = 1; + trace.inputs.back().columns = 1; + + { + SourceVariableMapping mapping; + mapping.name = "SV_Coverage"; + mapping.type = VarType::UInt; + mapping.rows = 1; + mapping.columns = 1; + DebugVariableReference ref; + ref.type = DebugVariableType::Input; + ref.name = trace.inputs.back().name; + mapping.variables.push_back(ref); + + trace.sourceVars.push_back(mapping); + } } } @@ -4140,6 +4320,77 @@ void CreateShaderDebugStateAndTrace(ShaderDebug::State &initialState, ShaderDebu 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 mapping; + mapping.name = sig.semanticIdxName; + if(mapping.name.empty() && sig.systemValue != ShaderBuiltin::Undefined) + mapping.name = ToStr(sig.systemValue); + mapping.type = v.type; + mapping.rows = 1; + mapping.columns = sig.compCount; + mapping.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; + mapping.variables.push_back(ref); + } + } + + trace.sourceVars.push_back(mapping); + } + else + { + SourceVariableMapping mapping; + + if(sig.systemValue == ShaderBuiltin::DepthOutput) + { + mapping.name = "SV_Depth"; + mapping.type = VarType::Float; + } + else if(sig.systemValue == ShaderBuiltin::DepthOutputLessEqual) + { + mapping.name = "SV_DepthLessEqual"; + mapping.type = VarType::Float; + } + else if(sig.systemValue == ShaderBuiltin::DepthOutputGreaterEqual) + { + mapping.name = "SV_DepthGreaterEqual"; + mapping.type = VarType::Float; + } + else if(sig.systemValue == ShaderBuiltin::MSAACoverage) + { + mapping.name = "SV_Coverage"; + mapping.type = VarType::UInt; + } + else if(sig.systemValue == ShaderBuiltin::StencilReference) + { + mapping.name = "SV_StencilRef"; + mapping.type = VarType::UInt; + } + + // all these variables are 1 scalar component + mapping.rows = 1; + mapping.columns = 1; + DebugVariableReference ref; + ref.type = DebugVariableType::Variable; + ref.name = v.name; + mapping.variables.push_back(ref); + + trace.sourceVars.push_back(mapping); + } + } } // Set the number of constant buffers in the trace, but assignment happens later @@ -4160,17 +4411,29 @@ void AddCBufferToDebugTrace(const DXBCBytecode::Program &program, ShaderDebugTra { RDCASSERTMSG("Reassigning previously filled cbuffer", trace.constantBlocks[i].members.empty()); + uint32_t cbufferIndex = program.IsShaderModel51() ? (uint32_t)i : slot.shaderRegister; + + trace.constantBlocks[i].name = + program.GetRegisterName(DXBCBytecode::TYPE_CONSTANT_BUFFER, cbufferIndex); + + SourceVariableMapping cbSourceMapping; + cbSourceMapping.name = refl.constantBlocks[i].name; + cbSourceMapping.variables.push_back( + DebugVariableReference(DebugVariableType::Constant, global.constantBlocks[i].name)); + sourceVars.push_back(cbSourceMapping); + rdcarray vars; StandardFillCBufferVariables(refl.resourceId, refl.constantBlocks[i].variables, vars, cbufData); - FlattenVariables(refl.constantBlocks[i].variables, vars, trace.constantBlocks[i].members); + FlattenVariables(trace.constantBlocks[i].name, refl.constantBlocks[i].variables, vars, + trace.constantBlocks[i].members, refl.constantBlocks[i].name + ".", 0, + trace.sourceVars); - const char *format = program.IsShaderModel51() ? "CB%u[%u] (%s)" : "cb%u[%u] (%s)"; - uint32_t regSlot = program.IsShaderModel51() ? (uint32_t)i : slot.shaderRegister; for(size_t c = 0; c < trace.constantBlocks[i].members.size(); c++) { - trace.constantBlocks[i].members[c].name = StringFormat::Fmt( - format, regSlot, (uint32_t)c, trace.constantBlocks[i].members[c].name.c_str()); + trace.constantBlocks[i].members[c].name = + StringFormat::Fmt("%s[%u]", trace.constantBlocks[i].name.c_str(), (uint32_t)c); } + return; } } @@ -4364,124 +4627,6 @@ void ApplyAllDerivatives(ShaderDebug::GlobalState &global, ShaderDebugTrace trac } } -void FlattenSingleVariable(uint32_t byteOffset, const rdcstr &basename, const ShaderVariable &v, - rdcarray &outvars) -{ - size_t outIdx = byteOffset / 16; - size_t outComp = (byteOffset % 16) / 4; - - if(v.rowMajor) - outvars.resize(RDCMAX(outIdx + v.rows, outvars.size())); - else - outvars.resize(RDCMAX(outIdx + v.columns, outvars.size())); - - if(!outvars[outIdx].name.empty()) - { - // if we already have a variable in this slot, just append this variable to it. We should not - // overlap into the next register as that's not allowed. - outvars[outIdx].name = rdcstr(outvars[outIdx].name) + ", " + basename; - outvars[outIdx].rows = 1; - outvars[outIdx].isStruct = false; - outvars[outIdx].columns += v.columns; - - RDCASSERT(outvars[outIdx].columns <= 4, outvars[outIdx].columns); - - memcpy(&outvars[outIdx].value.uv[outComp], &v.value.uv[0], sizeof(uint32_t) * v.columns); - } - else - { - const uint32_t numRegisters = v.rowMajor ? v.rows : v.columns; - const char *regName = v.rowMajor ? "row" : "col"; - for(uint32_t reg = 0; reg < numRegisters; reg++) - { - if(numRegisters > 1) - outvars[outIdx + reg].name = StringFormat::Fmt("%s.%s%u", basename.c_str(), regName, reg); - else - outvars[outIdx + reg].name = basename; - outvars[outIdx + reg].rows = 1; - outvars[outIdx + reg].type = v.type; - outvars[outIdx + reg].isStruct = false; - outvars[outIdx + reg].columns = v.columns; - outvars[outIdx + reg].rowMajor = v.rowMajor; - } - - if(v.rowMajor) - { - for(size_t ri = 0; ri < v.rows; ri++) - memcpy(&outvars[outIdx + ri].value.uv[0], &v.value.uv[ri * v.columns], - sizeof(uint32_t) * v.columns); - } - else - { - // if we have a matrix stored in column major order, we need to transpose it back so we can - // unroll it into vectors. - for(size_t ci = 0; ci < v.columns; ci++) - for(size_t ri = 0; ri < v.rows; ri++) - outvars[outIdx + ci].value.uv[ri] = v.value.uv[ri * v.columns + ci]; - } - } -} - -void FlattenVariables(const rdcarray &constants, - const rdcarray &invars, rdcarray &outvars, - const rdcstr &prefix, uint32_t baseOffset) -{ - RDCASSERTEQUAL(constants.size(), invars.size()); - - for(size_t i = 0; i < constants.size(); i++) - { - const ShaderConstant &c = constants[i]; - const ShaderVariable &v = invars[i]; - - uint32_t byteOffset = baseOffset + c.byteOffset; - - rdcstr basename = prefix + rdcstr(v.name); - - if(!v.members.empty()) - { - if(v.isStruct) - { - FlattenVariables(c.type.members, v.members, outvars, basename + ".", byteOffset); - } - else - { - if(c.type.members.empty()) - { - // if there are no members in this type, it means it's a basic array - unroll directly - - for(int m = 0; m < v.members.count(); m++) - { - FlattenSingleVariable(byteOffset + m * c.type.descriptor.arrayByteStride, - StringFormat::Fmt("%s[%zu]", basename.c_str(), m), v.members[m], - outvars); - } - } - else - { - // otherwise we recurse into each member and flatten - - for(int m = 0; m < v.members.count(); m++) - { - FlattenVariables(c.type.members, v.members[m].members, outvars, - StringFormat::Fmt("%s[%zu].", basename.c_str(), m), - byteOffset + m * c.type.descriptor.arrayByteStride); - } - } - } - - continue; - } - - FlattenSingleVariable(byteOffset, basename, v, outvars); - } -} - -void FlattenVariables(const rdcarray &constants, - const rdcarray &invars, rdcarray &outvars) -{ - FlattenVariables(constants, invars, outvars, "", 0); -} - void FillViewFmt(DXGI_FORMAT format, GlobalState::ViewFmt &viewFmt) { if(format != DXGI_FORMAT_UNKNOWN) diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.h b/renderdoc/driver/shaders/dxbc/dxbc_debug.h index 9d7f6fd06..aca76e47b 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.h @@ -209,11 +209,6 @@ void ApplyAllDerivatives(ShaderDebug::GlobalState &global, ShaderDebugTrace trac void FlattenSingleVariable(uint32_t byteOffset, const rdcstr &basename, const ShaderVariable &v, rdcarray &outvars); -void FlattenVariables(const rdcarray &constants, - const rdcarray &invars, rdcarray &outvars, - const rdcstr &prefix, uint32_t baseOffset); -void FlattenVariables(const rdcarray &constants, - const rdcarray &invars, rdcarray &outvars); void FillViewFmt(DXGI_FORMAT format, GlobalState::ViewFmt &viewFmt); diff --git a/renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp b/renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp index b50bf1ea8..81981d21e 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_sdbg.cpp @@ -122,7 +122,7 @@ void SDBGChunk::GetCallstack(size_t instruction, uintptr_t offset, rdcarray &callstack) const; - bool HasLocals() const; + bool HasSourceMapping() const; void GetLocals(DXBCBytecode::Program *program, size_t instruction, uintptr_t offset, rdcarray &locals) const; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp b/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp index 4633a295a..09d0672d6 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp @@ -189,7 +189,8 @@ SPDBChunk::SPDBChunk(void *chunk) VarType baseType; uint32_t byteSize; uint16_t vecSize; - uint16_t matArrayStride; + uint16_t matArrayStride : 15; + uint16_t colMajorMatrix : 1; LEAF_ENUM_e leafType; rdcarray members; }; @@ -199,24 +200,24 @@ SPDBChunk::SPDBChunk(void *chunk) // prepopulate with basic types // for now we stick to full-precision 32-bit VarTypes. It's not clear if HLSL even emits the other // types - typeInfo[T_INT4] = {"int32_t", VarType::SInt, 4, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_INT2] = {"int16_t", VarType::SInt, 2, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_INT1] = {"int8_t", VarType::SInt, 1, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_LONG] = {"int32_t", VarType::SInt, 4, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_SHORT] = {"int16_t", VarType::SInt, 2, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_CHAR] = {"char", VarType::SInt, 1, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_BOOL32FF] = {"bool", VarType::UInt, 4, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_UINT4] = {"uint32_t", VarType::UInt, 4, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_UINT2] = {"uint16_t", VarType::UInt, 2, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_UINT1] = {"uint8_t", VarType::UInt, 1, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_ULONG] = {"uint32_t", VarType::UInt, 4, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_USHORT] = {"uint16_t", VarType::UInt, 2, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_UCHAR] = {"unsigned char", VarType::UInt, 1, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_REAL16] = {"half", VarType::Float, 2, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_REAL32] = {"float", VarType::Float, 4, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_INT4] = {"int32_t", VarType::SInt, 4, 1, 0, 0, LF_NUMERIC, {}}; + typeInfo[T_INT2] = {"int16_t", VarType::SInt, 2, 1, 0, 0, LF_NUMERIC, {}}; + typeInfo[T_INT1] = {"int8_t", VarType::SInt, 1, 1, 0, 0, LF_NUMERIC, {}}; + typeInfo[T_LONG] = {"int32_t", VarType::SInt, 4, 1, 0, 0, LF_NUMERIC, {}}; + typeInfo[T_SHORT] = {"int16_t", VarType::SInt, 2, 1, 0, 0, LF_NUMERIC, {}}; + typeInfo[T_CHAR] = {"char", VarType::SInt, 1, 1, 0, 0, LF_NUMERIC, {}}; + typeInfo[T_BOOL32FF] = {"bool", VarType::UInt, 4, 1, 0, 0, LF_NUMERIC, {}}; + typeInfo[T_UINT4] = {"uint32_t", VarType::UInt, 4, 1, 0, 0, LF_NUMERIC, {}}; + typeInfo[T_UINT2] = {"uint16_t", VarType::UInt, 2, 1, 0, 0, LF_NUMERIC, {}}; + typeInfo[T_UINT1] = {"uint8_t", VarType::UInt, 1, 1, 0, 0, LF_NUMERIC, {}}; + typeInfo[T_ULONG] = {"uint32_t", VarType::UInt, 4, 1, 0, 0, LF_NUMERIC, {}}; + typeInfo[T_USHORT] = {"uint16_t", VarType::UInt, 2, 1, 0, 0, LF_NUMERIC, {}}; + typeInfo[T_UCHAR] = {"unsigned char", VarType::UInt, 1, 1, 0, 0, LF_NUMERIC, {}}; + typeInfo[T_REAL16] = {"half", VarType::Float, 2, 1, 0, 0, LF_NUMERIC, {}}; + typeInfo[T_REAL32] = {"float", VarType::Float, 4, 1, 0, 0, LF_NUMERIC, {}}; // modern HLSL fake half - typeInfo[T_REAL32PP] = {"half", VarType::Float, 4, 1, 0, LF_NUMERIC, {}}; - typeInfo[T_REAL64] = {"double", VarType::Double, 8, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_REAL32PP] = {"half", VarType::Float, 4, 1, 0, 0, LF_NUMERIC, {}}; + typeInfo[T_REAL64] = {"double", VarType::Double, 8, 1, 0, 0, LF_NUMERIC, {}}; if(streams.size() >= 3) { @@ -275,8 +276,8 @@ SPDBChunk::SPDBChunk(void *chunk) typeInfo[id] = { name, typeInfo[vector->elemtype].baseType, *bytelength, (uint16_t)vector->count, - 0, type, - {}, + 0, 0, + type, {}, }; break; @@ -293,14 +294,22 @@ SPDBChunk::SPDBChunk(void *chunk) id, name, matrix->elemtype, matrix->rows, matrix->cols, *bytelength, matrix->majorStride, matrix->matattr.row_major ? "row" : "column"); + uint16_t vecSize = 0, matStride = 0; + + if(matrix->matattr.row_major) + { + vecSize = uint16_t(matrix->cols); + matStride = uint16_t(*bytelength / matrix->rows); + } + else + { + vecSize = uint16_t(matrix->rows); + matStride = uint16_t(*bytelength / matrix->cols); + } + typeInfo[id] = { - name, - typeInfo[matrix->elemtype].baseType, - *bytelength, - uint16_t(matrix->rows), - uint16_t(*bytelength / matrix->cols), - type, - {}, + name, typeInfo[matrix->elemtype].baseType, *bytelength, vecSize, + matStride, matrix->matattr.row_major == 0, type, {}, }; break; @@ -547,7 +556,7 @@ SPDBChunk::SPDBChunk(void *chunk) structType = "class"; typeInfo[id] = { - name, VarType::Float, *bytelength, 1, 0, type, typeInfo[structure->field].members, + name, VarType::Float, *bytelength, 1, 0, 0, type, typeInfo[structure->field].members, }; SPDBLOG( @@ -583,6 +592,7 @@ SPDBChunk::SPDBChunk(void *chunk) *bytelength, 1, uint16_t(stridedArray->stride), + 0, type, {}, }; @@ -1159,6 +1169,8 @@ SPDBChunk::SPDBChunk(void *chunk) uint32_t varOffset = defrange->offsetParent; uint32_t varLen = defrange->sizeInParent; + mapping.varOffset = varOffset; + const TypeDesc *vartype = &typeInfo[localType]; RDCASSERT(varOffset + varLen <= vartype->byteSize); @@ -1218,23 +1230,59 @@ SPDBChunk::SPDBChunk(void *chunk) mapping.var.type = vartype->baseType; mapping.var.rows = 1; mapping.var.columns = uint8_t(vartype->vecSize); + mapping.var.elements = 1; // if it's an array or matrix, figure out the index if(vartype->matArrayStride) { + // number of rows is the number of vectors in the matrix's total byte size (each vector is + // a row) + mapping.var.rows = uint8_t(vartype->byteSize / vartype->matArrayStride); + + // unless this is a column major matrix, in which case each vector is a column so swap the + // rows/columns (the number of ROWS is the vector size, when each vector is a column) + if(vartype->colMajorMatrix) + std::swap(mapping.var.rows, mapping.var.columns); + + // calculate which vector we're on, and which component uint32_t idx = varOffset / vartype->matArrayStride; - - mapping.var.name += StringFormat::Fmt("[%u]", idx); - mapping.var.rows = (uint8_t)RDCMAX( - 1U, (vartype->byteSize + (vartype->matArrayStride - 1)) / vartype->matArrayStride); - varOffset -= vartype->matArrayStride * idx; - } + uint32_t comp = (varOffset % 16) / 4; - if(vartype->leafType != LF_MATRIX) - { - mapping.var.elements = mapping.var.rows; - mapping.var.rows = 1; + // should now be down to a vector, so the remaining offset is the component + RDCASSERT(varOffset < 16); + + if(vartype->leafType == LF_MATRIX) + { + // if this is a matrix, start with the index as row, and component as column + uint32_t row = idx; + uint32_t col = comp; + + // flip them if this is column major + if(vartype->colMajorMatrix) + std::swap(row, col); + + // add the row to the name since we want our mapping row-major for better display + mapping.var.name += StringFormat::Fmt(".row%u", row); + + // and set the component after flipping + comp = col; + } + else + { + // the number of rows is actually the number of elements (and number of rows is 1) + mapping.var.elements = mapping.var.rows; + mapping.var.rows = 1; + + // if this is an array, the index is just the array index. However if we're mapping the + // whole array, don't add the index as the mapping will do that for us + if(varLen < mapping.var.elements * vartype->matArrayStride) + mapping.var.name += StringFormat::Fmt("[%u]", idx); + } + + // set the offset explicitly to the component within the final vector we chose (whatever + // it is) + varOffset = comp * 4; } RDCASSERT(mapping.var.rows <= 4 && mapping.var.columns <= 4); @@ -1623,7 +1671,7 @@ void SPDBChunk::GetCallstack(size_t instruction, uintptr_t offset, rdcarraysecond.callstack; } -bool SPDBChunk::HasLocals() const +bool SPDBChunk::HasSourceMapping() const { return true; } @@ -1676,6 +1724,9 @@ void SPDBChunk::GetLocals(DXBCBytecode::Program *program, size_t instruction, ui else range.type = DebugVariableType::Variable; + // we don't handle more than float4 at a time (unless in an array) + RDCASSERT(it->numComps <= 4); + // we apply each matching local over the top. Where there is an overlap (e.g. two variables with // the same name) we take the last mapping as authoratitive. This is a good solution for the // case where one function with a parameter/variable name calls an inner function with the same @@ -1696,6 +1747,8 @@ void SPDBChunk::GetLocals(DXBCBytecode::Program *program, size_t instruction, ui a.variables[it->varFirstComp + i].component += (uint8_t)i; } + RDCASSERT(it->var.elements == 1); + // we've processed this, no need to add a new entry added = true; break; @@ -1710,7 +1763,7 @@ void SPDBChunk::GetLocals(DXBCBytecode::Program *program, size_t instruction, ui a.type = it->var.type; a.rows = it->var.rows; a.columns = it->var.columns; - a.elements = it->var.elements; + a.offset = it->varOffset; a.variables.resize(it->varFirstComp + it->numComps); @@ -1720,7 +1773,20 @@ void SPDBChunk::GetLocals(DXBCBytecode::Program *program, size_t instruction, ui a.variables[it->varFirstComp + i].component += (uint8_t)i; } - locals.push_back(a); + for(uint32_t e = 0; e < it->var.elements; e++) + { + if(it->var.elements > 1) + { + a.name = StringFormat::Fmt("%s[%u]", it->var.name.c_str(), e); + for(uint32_t i = 0; i < it->numComps; i++) + { + a.variables[it->varFirstComp + i].name = + StringFormat::Fmt("%s[%u]", range.name.c_str(), e); + } + } + + locals.push_back(a); + } } } } diff --git a/renderdoc/driver/shaders/dxbc/dxbc_spdb.h b/renderdoc/driver/shaders/dxbc/dxbc_spdb.h index c6b212ee3..904ff20bc 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_spdb.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_spdb.h @@ -249,6 +249,7 @@ struct LocalMapping LocalRange range; uint8_t regFirstComp; uint32_t varFirstComp; + uint32_t varOffset; uint32_t numComps; rdcarray gaps; @@ -274,7 +275,7 @@ public: void GetLineInfo(size_t instruction, uintptr_t offset, LineColumnInfo &lineInfo) const; void GetCallstack(size_t instruction, uintptr_t offset, rdcarray &callstack) const; - bool HasLocals() const; + bool HasSourceMapping() const; void GetLocals(DXBCBytecode::Program *program, size_t instruction, uintptr_t offset, rdcarray &locals) const; diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index 4087726e4..e1dd1ff95 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -356,7 +356,7 @@ void DoSerialise(SerialiserType &ser, SourceVariableMapping &el) SERIALISE_MEMBER(type); SERIALISE_MEMBER(rows); SERIALISE_MEMBER(columns); - SERIALISE_MEMBER(elements); + SERIALISE_MEMBER(offset); SERIALISE_MEMBER(variables); SIZE_CHECK(64); @@ -393,11 +393,12 @@ void DoSerialise(SerialiserType &ser, ShaderDebugTrace &el) { SERIALISE_MEMBER(inputs); SERIALISE_MEMBER(constantBlocks); + SERIALISE_MEMBER(sourceVars); SERIALISE_MEMBER(states); SERIALISE_MEMBER(hasSourceMapping); SERIALISE_MEMBER(lineInfo); - SIZE_CHECK(104); + SIZE_CHECK(128); } template