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