From 2c4f2a57a841186238ce512c9dbbcc4c9653b75f Mon Sep 17 00:00:00 2001 From: Steve Karolewics Date: Sun, 12 Apr 2020 12:36:08 -0700 Subject: [PATCH] Add resource tracking to DXBC shader debugging When a resource is accessed, it is now tracked by the debug step. The shader viewer has a new panel to display resources accessed up to the current step, with context menus to go to previous/next access of a specific resource. --- qrenderdoc/Windows/ShaderViewer.cpp | 277 ++++++++++++++++-- qrenderdoc/Windows/ShaderViewer.h | 9 + qrenderdoc/Windows/ShaderViewer.ui | 51 ++++ .../driver/shaders/dxbc/dxbc_bytecode.cpp | 4 +- .../driver/shaders/dxbc/dxbc_container.cpp | 5 + renderdoc/driver/shaders/dxbc/dxbc_debug.cpp | 130 ++++++-- renderdoc/driver/shaders/dxbc/dxbc_debug.h | 10 + .../d3d12/d3d12_resource_mapping_zoo.cpp | 94 +++++- 8 files changed, 526 insertions(+), 54 deletions(-) diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index ca433d7e9..74bab0ae6 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -57,9 +57,26 @@ struct VariableTag DebugVariableReference debugVar; }; + +struct AccessedResourceTag +{ + AccessedResourceTag() : type(VarType::Unknown) { bind.bind = -1; } + AccessedResourceTag(BindpointIndex bp, VarType t) : bind(bp), type(t) {} + AccessedResourceTag(ShaderVariable var) + { + type = var.type; + if(var.type == VarType::ReadOnlyResource || var.type == VarType::ReadWriteResource) + bind = var.GetBinding(); + else + bind.bind = -1; + } + BindpointIndex bind; + VarType type; +}; }; Q_DECLARE_METATYPE(VariableTag); +Q_DECLARE_METATYPE(AccessedResourceTag); ShaderViewer::ShaderViewer(ICaptureContext &ctx, QWidget *parent) : QFrame(parent), ui(new Ui::ShaderViewer), m_Ctx(ctx) @@ -67,6 +84,7 @@ ShaderViewer::ShaderViewer(ICaptureContext &ctx, QWidget *parent) ui->setupUi(this); ui->constants->setFont(Formatter::PreferredFont()); + ui->accessedResources->setFont(Formatter::PreferredFont()); ui->debugVars->setFont(Formatter::PreferredFont()); ui->sourceVars->setFont(Formatter::PreferredFont()); ui->watch->setFont(Formatter::PreferredFont()); @@ -228,6 +246,7 @@ void ShaderViewer::editShader(ResourceId id, ShaderStage stage, const QString &e ui->watch->hide(); ui->debugVars->hide(); ui->constants->hide(); + ui->resourcesPanel->hide(); ui->callstack->hide(); ui->sourceVars->hide(); @@ -328,6 +347,12 @@ void ShaderViewer::editShader(ResourceId id, ShaderStage stage, const QString &e } } +void ShaderViewer::cacheResources() +{ + m_ReadOnlyResources = m_Ctx.CurPipelineState().GetReadOnlyResources(m_Stage); + m_ReadWriteResources = m_Ctx.CurPipelineState().GetReadWriteResources(m_Stage); +} + void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderReflection *shader, ResourceId pipeline, ShaderDebugTrace *trace, const QString &debugContext) @@ -497,43 +522,55 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR ui->constants->header()->resizeSection(0, 80); + ui->accessedResources->setColumns({tr("Register(s)"), tr("Type"), tr("Resource")}); + ui->accessedResources->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + ui->accessedResources->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); + ui->accessedResources->header()->setSectionResizeMode(2, QHeaderView::Interactive); + + ui->accessedResources->header()->resizeSection(0, 80); + ui->debugVars->setTooltipElidedItems(false); ui->constants->setTooltipElidedItems(false); + ui->accessedResources->setTooltipElidedItems(false); + ToolWindowManager::ToolWindowProperty windowProps = + ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow; ui->watch->setWindowTitle(tr("Watch")); ui->docking->addToolWindow( ui->watch, ToolWindowManager::AreaReference(ToolWindowManager::BottomOf, ui->docking->areaOf(m_DisassemblyFrame), 0.25f)); - ui->docking->setToolWindowProperties( - ui->watch, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); + ui->docking->setToolWindowProperties(ui->watch, windowProps); ui->debugVars->setWindowTitle(tr("Variable Values")); ui->docking->addToolWindow( ui->debugVars, ToolWindowManager::AreaReference(ToolWindowManager::AddTo, ui->docking->areaOf(ui->watch))); - ui->docking->setToolWindowProperties( - ui->debugVars, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); + ui->docking->setToolWindowProperties(ui->debugVars, windowProps); ui->constants->setWindowTitle(tr("Constants && Resources")); ui->docking->addToolWindow( ui->constants, ToolWindowManager::AreaReference(ToolWindowManager::LeftOf, ui->docking->areaOf(ui->debugVars), 0.5f)); - ui->docking->setToolWindowProperties( - ui->constants, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); + ui->docking->setToolWindowProperties(ui->constants, windowProps); + + ui->resourcesPanel->setWindowTitle(tr("Accessed Resources")); + ui->docking->addToolWindow( + ui->resourcesPanel, ToolWindowManager::AreaReference(ToolWindowManager::AddTo, + ui->docking->areaOf(ui->constants))); + ui->docking->setToolWindowProperties(ui->resourcesPanel, windowProps); + ui->docking->raiseToolWindow(ui->constants); ui->callstack->setWindowTitle(tr("Callstack")); ui->docking->addToolWindow( ui->callstack, ToolWindowManager::AreaReference(ToolWindowManager::RightOf, ui->docking->areaOf(ui->debugVars), 0.2f)); - ui->docking->setToolWindowProperties( - ui->callstack, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow); + ui->docking->setToolWindowProperties(ui->callstack, windowProps); 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); + ui->docking->setToolWindowProperties(ui->sourceVars, windowProps); m_Line2Insts.resize(m_ShaderDetails->debugInfo.files.count()); @@ -609,9 +646,12 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR // event filter to pick up tooltip events ui->constants->installEventFilter(this); + ui->accessedResources->installEventFilter(this); ui->debugVars->installEventFilter(this); ui->watch->installEventFilter(this); + cacheResources(); + m_Ctx.Replay().AsyncInvoke([this](IReplayController *r) { rdcarray states = r->ContinueDebug(m_Trace->debugger); @@ -670,6 +710,9 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR ui->sourceVars->setContextMenuPolicy(Qt::CustomContextMenu); QObject::connect(ui->sourceVars, &RDTreeWidget::customContextMenuRequested, this, &ShaderViewer::variables_contextMenu); + ui->accessedResources->setContextMenuPolicy(Qt::CustomContextMenu); + QObject::connect(ui->accessedResources, &RDTreeWidget::customContextMenuRequested, this, + &ShaderViewer::accessedResources_contextMenu); ui->watch->insertRow(0); @@ -691,6 +734,7 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR ui->watch->hide(); ui->debugVars->hide(); ui->constants->hide(); + ui->resourcesPanel->hide(); ui->sourceVars->hide(); ui->callstack->hide(); @@ -1154,6 +1198,33 @@ void ShaderViewer::variables_contextMenu(const QPoint &pos) RDDialog::show(&contextMenu, w->viewport()->mapToGlobal(pos)); } +void ShaderViewer::accessedResources_contextMenu(const QPoint &pos) +{ + QAbstractItemView *w = qobject_cast(QObject::sender()); + RDTreeWidget *tree = qobject_cast(w); + if(tree->selectedItem() == NULL) + return; + + QMenu contextMenu(this); + + QAction prevAccess(tr("Run To Previous Access"), this); + QAction nextAccess(tr("Run To Next Access"), this); + + contextMenu.addAction(&prevAccess); + contextMenu.addAction(&nextAccess); + + QObject::connect(&prevAccess, &QAction::triggered, [this, tree] { + const AccessedResourceTag &tag = tree->selectedItem()->tag().value(); + runToResourceAccess(false, tag.type, tag.bind); + }); + QObject::connect(&nextAccess, &QAction::triggered, [this, tree] { + const AccessedResourceTag &tag = tree->selectedItem()->tag().value(); + runToResourceAccess(true, tag.type, tag.bind); + }); + + RDDialog::show(&contextMenu, w->viewport()->mapToGlobal(pos)); +} + void ShaderViewer::disassembly_buttonReleased(QMouseEvent *event) { if(event->button() == Qt::LeftButton) @@ -1195,6 +1266,7 @@ void ShaderViewer::disassembly_buttonReleased(QMouseEvent *event) highlightMatchingVars(ui->debugVars->invisibleRootItem(), text, highlightColor); highlightMatchingVars(ui->constants->invisibleRootItem(), text, highlightColor); + highlightMatchingVars(ui->accessedResources->invisibleRootItem(), text, highlightColor); highlightMatchingVars(ui->sourceVars->invisibleRootItem(), text, highlightColor); m_DisassemblyView->setIndicatorCurrent(INDICATOR_REGHIGHLIGHT); @@ -1570,6 +1642,50 @@ void ShaderViewer::runTo(QVector runToInstruction, bool forward, ShaderE updateDebugState(); } +void ShaderViewer::runToResourceAccess(bool forward, VarType type, const BindpointIndex &resource) +{ + if(!m_Trace || m_States.empty()) + return; + + // this is effectively infinite as we break out before moving to next/previous state if that would + // be first/last + while((forward && !IsLastState()) || (!forward && !IsFirstState())) + { + if(forward) + { + if(IsLastState()) + break; + applyForwardsChange(); + } + else + { + if(IsFirstState()) + break; + applyBackwardsChange(); + } + + // Break if the current state references the specific resource requested + bool foundResource = false; + for(const ShaderVariableChange &c : GetCurrentState().changes) + { + if(c.after.type == type && c.after.GetBinding() == resource) + { + foundResource = true; + break; + } + } + + if(foundResource) + break; + + // or breakpoint + if(m_Breakpoints.contains((int)GetCurrentState().nextInstruction)) + break; + } + + updateDebugState(); +} + void ShaderViewer::applyBackwardsChange() { if(!IsFirstState()) @@ -1590,6 +1706,15 @@ void ShaderViewer::applyBackwardsChange() break; } } + + for(size_t i = 0; i < m_AccessedResources.size(); i++) + { + if(c.after.name == m_AccessedResources[i].name) + { + m_AccessedResources.erase(i); + break; + } + } } else { @@ -1623,6 +1748,7 @@ void ShaderViewer::applyForwardsChange() m_CurrentStateIdx++; rdcarray newVariables; + rdcarray newAccessedResources; for(const ShaderVariableChange &c : GetCurrentState().changes) { @@ -1655,10 +1781,27 @@ void ShaderViewer::applyForwardsChange() *v = c.after; else newVariables.push_back(c.after); + + if(c.after.type == VarType::ReadOnlyResource || c.after.type == VarType::ReadWriteResource) + { + bool found = false; + for(size_t i = 0; i < m_AccessedResources.size(); i++) + { + if(c.after.name == m_AccessedResources[i].name) + { + found = true; + break; + } + } + + if(!found) + newAccessedResources.push_back(c.after); + } } } m_Variables.insert(0, newVariables); + m_AccessedResources.insert(0, newAccessedResources); } } @@ -1677,9 +1820,9 @@ QString ShaderViewer::stringRep(const ShaderVariable &var, uint32_t row) rdcarray resList; if(type == VarType::ReadOnlyResource) - resList = m_Ctx.CurPipelineState().GetReadOnlyResources(m_Stage); + resList = m_ReadOnlyResources; else if(type == VarType::ReadWriteResource) - resList = m_Ctx.CurPipelineState().GetReadWriteResources(m_Stage); + resList = m_ReadWriteResources; else if(type == VarType::Sampler) resList = m_Ctx.CurPipelineState().GetSamplers(m_Stage); @@ -2310,7 +2453,7 @@ void ShaderViewer::updateDebugState() } } - rdcarray roBinds = m_Ctx.CurPipelineState().GetReadOnlyResources(m_Stage); + rdcarray &roBinds = m_ReadOnlyResources; for(int i = 0; i < m_Trace->readOnlyResources.count(); i++) { @@ -2334,7 +2477,7 @@ void ShaderViewer::updateDebugState() if(bindIdx < 0) continue; - BoundResourceArray roBind = roBinds[bindIdx]; + BoundResourceArray &roBind = roBinds[bindIdx]; if(bind.arraySize == 1) { @@ -2361,7 +2504,8 @@ void ShaderViewer::updateDebugState() node->setTag(QVariant::fromValue( VariableTag(DebugVariableReference(DebugVariableType::ReadOnlyResource, ro.name)))); - for(uint32_t a = 0; a < bind.arraySize; a++) + uint32_t count = qMin(bind.arraySize, (uint32_t)roBind.resources.size()); + for(uint32_t a = 0; a < count; a++) { QString childName = QFormatStr("%1[%2]").arg(ro.name).arg(a); RDTreeWidgetItem *child = new RDTreeWidgetItem({ @@ -2377,7 +2521,7 @@ void ShaderViewer::updateDebugState() } } - rdcarray rwBinds = m_Ctx.CurPipelineState().GetReadWriteResources(m_Stage); + rdcarray &rwBinds = m_ReadWriteResources; for(int i = 0; i < m_Trace->readWriteResources.count(); i++) { @@ -2401,7 +2545,7 @@ void ShaderViewer::updateDebugState() if(bindIdx < 0) continue; - BoundResourceArray rwBind = rwBinds[bindIdx]; + BoundResourceArray &rwBind = rwBinds[bindIdx]; if(bind.arraySize == 1) { @@ -2428,7 +2572,8 @@ void ShaderViewer::updateDebugState() node->setTag(QVariant::fromValue( VariableTag(DebugVariableReference(DebugVariableType::ReadWriteResource, rw.name)))); - for(uint32_t a = 0; a < bind.arraySize; a++) + uint32_t count = qMin(bind.arraySize, (uint32_t)rwBind.resources.size()); + for(uint32_t a = 0; a < count; a++) { QString childName = QFormatStr("%1[%2]").arg(rw.name).arg(a); RDTreeWidgetItem *child = new RDTreeWidgetItem({ @@ -2606,6 +2751,31 @@ void ShaderViewer::updateDebugState() ui->debugVars->applyExpansion(expansion, 0); } + { + ui->accessedResources->beginUpdate(); + + ui->accessedResources->clear(); + + for(int i = 0; i < m_AccessedResources.count(); i++) + { + bool modified = false; + + for(const ShaderVariableChange &c : GetCurrentState().changes) + { + if(c.before.name == m_AccessedResources[i].name || c.after.name == m_AccessedResources[i].name) + { + modified = true; + break; + } + } + + ui->accessedResources->addTopLevelItem( + makeAccessedResourceNode(m_AccessedResources[i], modified)); + } + + ui->accessedResources->endUpdate(); + } + updateWatchVariables(); ui->debugVars->resizeColumnToContents(0); @@ -2915,7 +3085,7 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi if(bindIdx < 0) continue; - BoundResourceArray res = samplers[bindIdx]; + BoundResourceArray &res = samplers[bindIdx]; if(bind.arraySize == 1) { @@ -2953,9 +3123,8 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi regNames = r.name; typeName = isReadOnlyResource ? lit("Resource") : lit("RW Resource"); - rdcarray resList = - isReadOnlyResource ? m_Ctx.CurPipelineState().GetReadOnlyResources(m_Stage) - : m_Ctx.CurPipelineState().GetReadWriteResources(m_Stage); + rdcarray &resList = + isReadOnlyResource ? m_ReadOnlyResources : m_ReadWriteResources; int32_t idx = (isReadOnlyResource ? m_Mapping->readOnlyResources : m_Mapping->readWriteResources) @@ -2972,8 +3141,7 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi if(bindIdx < 0) continue; - BoundResourceArray res = resList[bindIdx]; - + BoundResourceArray &res = resList[bindIdx]; if(bind.arraySize == 1) { value = ToQStr(res.resources[0].resourceId); @@ -2986,11 +3154,14 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi } else { - for(uint32_t a = 0; a < bind.arraySize; a++) + uint32_t count = qMin(bind.arraySize, (uint32_t)res.resources.size()); + for(uint32_t a = 0; a < count; a++) + { children.push_back(new RDTreeWidgetItem({ QFormatStr("%1[%2]").arg(localName).arg(a), QFormatStr("%1[%2]").arg(regNames).arg(a), typeName, ToQStr(res.resources[a].resourceId), })); + } regNames = QString(); typeName = QFormatStr("[%1]").arg(bind.arraySize); @@ -3105,6 +3276,62 @@ RDTreeWidgetItem *ShaderViewer::makeDebugVariableNode(const ShaderVariable &v, r return node; } +RDTreeWidgetItem *ShaderViewer::makeAccessedResourceNode(const ShaderVariable &v, bool modified) +{ + BindpointIndex bp = v.GetBinding(); + ResourceId resId; + QString typeName; + if(v.type == VarType::ReadOnlyResource) + { + typeName = lit("Resource"); + int32_t idx = m_Mapping->readOnlyResources.indexOf(Bindpoint(bp)); + if(idx >= 0) + { + Bindpoint bind = m_Mapping->readOnlyResources[idx]; + if(bind.used) + { + int32_t bindIdx = m_ReadOnlyResources.indexOf(bind); + if(bindIdx >= 0) + { + BoundResourceArray &roBind = m_ReadOnlyResources[bindIdx]; + if(bp.arrayIndex < roBind.resources.size()) + resId = roBind.resources[bp.arrayIndex].resourceId; + } + } + } + } + else if(v.type == VarType::ReadWriteResource) + { + typeName = lit("RW Resource"); + int32_t idx = m_Mapping->readWriteResources.indexOf(Bindpoint(bp)); + if(idx >= 0) + { + Bindpoint bind = m_Mapping->readWriteResources[idx]; + if(bind.used) + { + int32_t bindIdx = m_ReadWriteResources.indexOf(bind); + if(bindIdx >= 0) + { + BoundResourceArray &rwBind = m_ReadWriteResources[bindIdx]; + if(bp.arrayIndex < rwBind.resources.size()) + resId = rwBind.resources[bp.arrayIndex].resourceId; + } + } + } + } + + RDTreeWidgetItem *node = NULL; + if(resId != ResourceId()) + { + node = new RDTreeWidgetItem({v.name, typeName, ToQStr(resId)}); + node->setTag(QVariant::fromValue(AccessedResourceTag(bp, v.type))); + if(modified) + node->setForegroundColor(QColor(Qt::red)); + } + + return node; +} + const ShaderVariable *ShaderViewer::GetDebugVariable(const DebugVariableReference &r) { if(r.type == DebugVariableType::ReadOnlyResource) diff --git a/qrenderdoc/Windows/ShaderViewer.h b/qrenderdoc/Windows/ShaderViewer.h index 476ef5082..a453f94c1 100644 --- a/qrenderdoc/Windows/ShaderViewer.h +++ b/qrenderdoc/Windows/ShaderViewer.h @@ -124,6 +124,7 @@ private slots: void editable_keyPressed(QKeyEvent *event); void debug_contextMenu(const QPoint &pos); void variables_contextMenu(const QPoint &pos); + void accessedResources_contextMenu(const QPoint &pos); void disassembly_buttonReleased(QMouseEvent *event); void disassemble_typeChanged(int index); void watch_keyPress(QKeyEvent *event); @@ -176,6 +177,8 @@ private: bool isSourceDebugging(); + void cacheResources(); + ShaderEncoding currentEncoding(); QString m_TooltipName; @@ -235,6 +238,9 @@ private: rdcarray m_States; size_t m_CurrentStateIdx = 0; rdcarray m_Variables; + rdcarray m_AccessedResources; + rdcarray m_ReadOnlyResources; + rdcarray m_ReadWriteResources; QList m_Breakpoints; static const int CURRENT_MARKER = 0; @@ -276,6 +282,7 @@ private: RDTreeWidgetItem *makeSourceVariableNode(const SourceVariableMapping &l, int globalVarIdx, int localVarIdx); RDTreeWidgetItem *makeDebugVariableNode(const ShaderVariable &v, rdcstr prefix, bool modified); + RDTreeWidgetItem *makeAccessedResourceNode(const ShaderVariable &v, bool modified); const ShaderVariable *GetDebugVariable(const DebugVariableReference &r); @@ -286,6 +293,8 @@ private: void runTo(QVector runToInstructions, bool forward, ShaderEvents condition = ShaderEvents::NoEvent); + void runToResourceAccess(bool forward, VarType type, const BindpointIndex &resource); + void applyBackwardsChange(); void applyForwardsChange(); diff --git a/qrenderdoc/Windows/ShaderViewer.ui b/qrenderdoc/Windows/ShaderViewer.ui index 907e30c59..ae87db104 100644 --- a/qrenderdoc/Windows/ShaderViewer.ui +++ b/qrenderdoc/Windows/ShaderViewer.ui @@ -116,6 +116,57 @@ true + + + + 20 + 310 + 256 + 192 + + + + Qt::PreventContextMenu + + + QFrame::NoFrame + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 20 + 310 + 256 + 192 + + + + QFrame::NoFrame + + + true + + + + + diff --git a/renderdoc/driver/shaders/dxbc/dxbc_bytecode.cpp b/renderdoc/driver/shaders/dxbc/dxbc_bytecode.cpp index 115f92aca..70d335d7c 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_bytecode.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_bytecode.cpp @@ -58,9 +58,9 @@ void HandleResourceArrayIndices(const rdcarray &indices, // Start/end registers are inclusive, so one resource will have the same start/end register desc.bindCount = uint32_t(indices[2].index - indices[1].index + 1); - // If it's an unbounded resource array, mark the bind count as 0 + // If it's an unbounded resource array, mark the bind count as ~0U if(indices[2].index == 0xffffffff) - desc.bindCount = 0; + desc.bindCount = ~0U; } } diff --git a/renderdoc/driver/shaders/dxbc/dxbc_container.cpp b/renderdoc/driver/shaders/dxbc/dxbc_container.cpp index fa4cb85c5..7fb5632ed 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_container.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_container.cpp @@ -755,6 +755,11 @@ DXBCContainer::DXBCContainer(const void *ByteCode, size_t ByteCodeLength) desc.dimension = (ShaderInputBind::Dimension)res->dimension; desc.numSamples = res->sampleCount; + // Bindless resources report a bind count of 0 from the shader bytecode, but many other + // places in this codebase assume ~0U means bindless. Patch it up now. + if(h->targetVersion >= 0x501 && desc.bindCount == 0) + desc.bindCount = ~0U; + if(desc.numSamples == ~0 && desc.retType != RETURN_TYPE_MIXED && desc.retType != RETURN_TYPE_UNKNOWN && desc.retType != RETURN_TYPE_CONTINUED) { diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp index 14e70817c..b206c5a8d 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp @@ -1861,6 +1861,71 @@ void FlattenVariables(const rdcstr &cbufferName, const rdcarray } } +void ThreadState::MarkResourceAccess(ShaderDebugState *state, DXBCBytecode::OperandType type, + const BindingSlot &slot) +{ + if(state == NULL) + return; + + if(type != DXBCBytecode::TYPE_RESOURCE && type != DXBCBytecode::TYPE_UNORDERED_ACCESS_VIEW) + return; + + state->changes.push_back(ShaderVariableChange()); + ShaderVariableChange &change = state->changes.back(); + change.after.rows = change.after.columns = 1; + change.after.type = (type == DXBCBytecode::TYPE_RESOURCE) ? VarType::ReadOnlyResource + : VarType::ReadWriteResource; + + uint32_t reg = slot.shaderRegister; + uint32_t arrIdx = 0; + + const rdcarray &shaderBinds = + (type == DXBCBytecode::TYPE_RESOURCE) ? reflection->SRVs : reflection->UAVs; + for(size_t i = 0; i < shaderBinds.size(); ++i) + { + const DXBC::ShaderInputBind &bind = shaderBinds[i]; + if(bind.space == slot.registerSpace && bind.reg <= slot.shaderRegister && + (bind.bindCount == ~0U || slot.shaderRegister < bind.reg + bind.bindCount)) + { + reg = bind.reg; + arrIdx = slot.shaderRegister - bind.reg; + + char prefix = (type == DXBCBytecode::TYPE_RESOURCE) ? 't' : 'u'; + if(program->IsShaderModel51()) + prefix = (char)toupper(prefix); + + uint32_t resIdx = GetLogicalIdentifierForBindingSlot(*program, type, slot); + + if(bind.bindCount == 1) + change.after.name = StringFormat::Fmt("%c%u", prefix, resIdx); + else + change.after.name = StringFormat::Fmt("%c%u[%u]", prefix, resIdx, arrIdx); + + break; + } + } + change.after.SetBinding(slot.registerSpace, reg, arrIdx); + + // Check whether this resource was visited before + bool found = false; + BindpointIndex bp = change.after.GetBinding(); + rdcarray &accessed = + (type == DXBCBytecode::TYPE_RESOURCE) ? m_accessedSRVs : m_accessedUAVs; + for(size_t i = 0; i < accessed.size(); ++i) + { + if(accessed[i] == bp) + { + found = true; + break; + } + } + + if(found) + change.before = change.after; + else + accessed.push_back(bp); +} + void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper, const rdcarray &prevWorkgroup) { @@ -2799,6 +2864,8 @@ void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper, uav = global.uavs.find(slot); } + MarkResourceAccess(state, TYPE_UNORDERED_ACCESS_VIEW, slot); + uint32_t count = uav->second.hiddenCounter++; SetDst(state, op.operands[0], op, ShaderVariable("", count, count, count, count)); break; @@ -2819,6 +2886,8 @@ void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper, uav = global.uavs.find(slot); } + MarkResourceAccess(state, TYPE_UNORDERED_ACCESS_VIEW, slot); + uint32_t count = --uav->second.hiddenCounter; SetDst(state, op.operands[0], op, ShaderVariable("", count, count, count, count)); break; @@ -2946,6 +3015,8 @@ void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper, uav = global.uavs.find(slot); } + MarkResourceAccess(state, TYPE_UNORDERED_ACCESS_VIEW, slot); + offset = uav->second.firstElement; numElems = uav->second.numElements; data = &uav->second.data[0]; @@ -3201,6 +3272,8 @@ void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper, srvIter = global.srvs.find(slot); } + MarkResourceAccess(state, TYPE_RESOURCE, slot); + data = srvIter->second.data.data(); offset = srvIter->second.firstElement; numElems = srvIter->second.numElements; @@ -3219,6 +3292,8 @@ void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper, uavIter = global.uavs.find(slot); } + MarkResourceAccess(state, TYPE_UNORDERED_ACCESS_VIEW, slot); + data = uavIter->second.data.data(); dataSize = uavIter->second.data.size(); texData = uavIter->second.tex; @@ -3429,6 +3504,8 @@ void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper, { UINT identifier = (UINT)(op.operands[1].indices[0].index & 0xffffffff); slot = GetBindingSlotForIdentifier(*program, op.operands[1].type, identifier); + + MarkResourceAccess(state, op.operands[1].type, slot); } ShaderVariable result = apiWrapper->GetSampleInfo(op.operands[1].type, isAbsoluteResource, slot, op.str.c_str()); @@ -3589,6 +3666,8 @@ void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper, BindingSlot slot = GetBindingSlotForIdentifier(*program, op.operands[1].type, identifier); ShaderVariable result = apiWrapper->GetBufferInfo(op.operands[1].type, slot, op.str.c_str()); + MarkResourceAccess(state, op.operands[1].type, slot); + // apply swizzle ShaderVariable swizzled("", 0.0f, 0.0f, 0.0f, 0.0f); @@ -3635,6 +3714,8 @@ void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper, BindingSlot slot = GetBindingSlotForIdentifier(*program, op.operands[2].type, identifier); ShaderVariable result = apiWrapper->GetResourceInfo(op.operands[2].type, slot, mipLevel, dim); + MarkResourceAccess(state, op.operands[2].type, slot); + // need a valid dimension even if the resource was unbound, so // search for the declaration if(dim == 0) @@ -3820,6 +3901,8 @@ void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper, SetDst(state, op.operands[0], op, fetch); + MarkResourceAccess(state, TYPE_RESOURCE, resourceBinding); + return; } if(decl.declaration == OPCODE_DCL_RESOURCE && decl.operand.sameResource(op.operands[2])) @@ -3927,6 +4010,8 @@ void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper, samplerData.binding = samplerBinding; samplerData.bias = samplerBias; + MarkResourceAccess(state, TYPE_RESOURCE, resourceBinding); + ShaderVariable lookupResult("tex", 0.0f, 0.0f, 0.0f, 0.0f); if(apiWrapper->CalculateSampleGather(op.operation, resourceData, samplerData, uv, ddxCalc, ddyCalc, op.texelOffset, multisampleIndex, @@ -4275,6 +4360,30 @@ void GlobalState::PopulateGroupshared(const DXBCBytecode::Program *pBytecode) } } +uint32_t GetLogicalIdentifierForBindingSlot(const DXBCBytecode::Program &program, + OperandType declType, const DXBCDebug::BindingSlot &slot) +{ + uint32_t idx = slot.shaderRegister; + if(program.IsShaderModel51()) + { + // Need to lookup the logical identifier from the declarations + size_t numDeclarations = program.GetNumDeclarations(); + for(size_t d = 0; d < numDeclarations; ++d) + { + const DXBCBytecode::Declaration &decl = program.GetDeclaration(d); + if(decl.operand.type == declType && decl.space == slot.registerSpace && + decl.operand.indices[1].index <= slot.shaderRegister && + decl.operand.indices[2].index >= slot.shaderRegister) + { + idx = (uint32_t)decl.operand.indices[0].index; + break; + } + } + } + + return idx; +} + void AddCBufferToGlobalState(const DXBCBytecode::Program &program, GlobalState &global, rdcarray &sourceVars, const ShaderReflection &refl, const ShaderBindpointMapping &mapping, @@ -4289,29 +4398,14 @@ void AddCBufferToGlobalState(const DXBCBytecode::Program &program, GlobalState & slot.shaderRegister < (uint32_t)(bp.bind + bp.arraySize)) { uint32_t arrayIndex = slot.shaderRegister - bp.bind; + rdcarray &targetVars = bp.arraySize > 1 ? global.constantBlocks[i].members[arrayIndex].members : global.constantBlocks[i].members; RDCASSERTMSG("Reassigning previously filled cbuffer", targetVars.empty()); - uint32_t cbufferIndex = slot.shaderRegister; - if(program.IsShaderModel51()) - { - // Need to lookup the logical identifier from the declarations - size_t numDeclarations = program.GetNumDeclarations(); - for(size_t d = 0; d < numDeclarations; ++d) - { - const DXBCBytecode::Declaration &decl = program.GetDeclaration(d); - if(decl.operand.type == DXBCBytecode::TYPE_CONSTANT_BUFFER && - decl.space == slot.registerSpace && - decl.operand.indices[1].index <= slot.shaderRegister && - decl.operand.indices[2].index >= slot.shaderRegister) - { - cbufferIndex = (uint32_t)decl.operand.indices[0].index; - break; - } - } - } + uint32_t cbufferIndex = + GetLogicalIdentifierForBindingSlot(program, DXBCBytecode::TYPE_CONSTANT_BUFFER, slot); global.constantBlocks[i].name = program.GetRegisterName(DXBCBytecode::TYPE_CONSTANT_BUFFER, cbufferIndex); diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.h b/renderdoc/driver/shaders/dxbc/dxbc_debug.h index 5dfded91e..37b04d969 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.h @@ -307,6 +307,9 @@ private: void SetDst(ShaderDebugState *state, const DXBCBytecode::Operand &dstoper, const DXBCBytecode::Operation &op, const ShaderVariable &val); + void MarkResourceAccess(ShaderDebugState *state, DXBCBytecode::OperandType type, + const BindingSlot &slot); + // retrieves the value of the operand, by looking up // in the register file and performing any swizzling and // negation/abs functions @@ -320,6 +323,9 @@ private: const DXBC::Reflection *reflection; const DXBCBytecode::Program *program; + + rdcarray m_accessedSRVs; + rdcarray m_accessedUAVs; }; struct InterpretDebugger : public ShaderDebugger @@ -343,6 +349,10 @@ struct InterpretDebugger : public ShaderDebugger rdcarray ContinueDebug(DebugAPIWrapper *apiWrapper); }; +uint32_t GetLogicalIdentifierForBindingSlot(const DXBCBytecode::Program &program, + DXBCBytecode::OperandType declType, + const DXBCDebug::BindingSlot &slot); + void ApplyAllDerivatives(GlobalState &global, rdcarray &quad, int destIdx, const rdcarray &initialValues, float *data); diff --git a/util/test/demos/d3d12/d3d12_resource_mapping_zoo.cpp b/util/test/demos/d3d12/d3d12_resource_mapping_zoo.cpp index cf12e60b7..6f242033c 100644 --- a/util/test/demos/d3d12/d3d12_resource_mapping_zoo.cpp +++ b/util/test/demos/d3d12/d3d12_resource_mapping_zoo.cpp @@ -39,12 +39,12 @@ Texture2D res2 : register(t2); cbuffer consts : register(b3) { - float4 test; + uint4 test; }; float4 main() : SV_Target0 { - float4 color = test + float4(0.1f, 0.0f, 0.0f, 0.0f); + float4 color = (float4)test + float4(0.1f, 0.0f, 0.0f, 0.0f); return color + res1[uint2(0, 0)] + res2[uint2(0, 0)]; } @@ -59,7 +59,7 @@ Texture2D res2 : register(t7); cbuffer consts : register(b3) { - float4 test; + uint4 test; }; struct Foo @@ -71,7 +71,7 @@ ConstantBuffer bar[4][3] : register(b4); float4 main() : SV_Target0 { float4 color = bar[1][2].col; - color += test + float4(0.1f, 0.0f, 0.0f, 0.0f); + color += (float4)test + float4(0.1f, 0.0f, 0.0f, 0.0f); return color + res1[uint2(0, 0)] + res2[uint2(0, 0)]; } @@ -83,7 +83,7 @@ Texture2DArray resArray[4] : register(t10, space1); cbuffer consts : register(b3) { - float4 test; + uint4 test; }; float4 main(float4 pos : SV_Position) : SV_Target0 @@ -104,7 +104,7 @@ Texture2DArray resArray[] : register(t0); cbuffer consts : register(b3) { - float4 test; + uint4 test; }; float4 main(float4 pos : SV_Position) : SV_Target0 @@ -117,6 +117,40 @@ float4 main(float4 pos : SV_Position) : SV_Target0 return float4(arrayVal1, arrayVal2, arrayVal3, 1.0f); } +)EOSHADER"; + + std::string pixel_resourceAccess = R"EOSHADER( + +// SRVs +Texture2D srvAccessed : register(t0); +Texture2D srvNotAccessed : register(t1); +Texture2D srvArray[] : register(t0, space1); + +// UAVs +RWTexture2D uavAccessed : register(u0); +RWTexture2D uavNotAccessed : register(u1); + +struct Data +{ + uint4 data; +}; +ConstantBuffer cbvAccessed : register(b0); +ConstantBuffer cbvNotAccessed : register(b1); +ConstantBuffer cbvArray[8] : register(b0, space1); + +float4 main(float4 pos : SV_Position) : SV_Target0 +{ + float srvVal = srvAccessed.Load(uint3(0, 0, 0)); + uint cbvVal = cbvAccessed.data.x; + float srvArrayVal = srvArray[cbvVal].Load(uint3(0, 0, 0)); + float3 cbvArrayVal = (float3)cbvArray[cbvVal].data.yzw; + float3 ret = cbvArrayVal; + uavAccessed[uint2(0, 0)] = srvArrayVal / 100.0f; + ret.x += srvVal; + ret.y += srvArrayVal; + return float4(ret, 1.0f); +} + )EOSHADER"; void UploadTexture(ID3D12ResourcePtr & uploadBuf, ID3D12ResourcePtr & dstTexture, byte * data, @@ -192,11 +226,17 @@ float4 main(float4 pos : SV_Position) : SV_Target0 ID3DBlobPtr psblob_5_1 = Compile(pixel_5_1, "main", "ps_5_1"); ID3DBlobPtr psblob_resArray = Compile(pixel_resArray, "main", "ps_5_1"); ID3DBlobPtr psblob_bindless = Compile(pixel_bindless, "main", "ps_5_1"); + ID3DBlobPtr psblob_resourceAccess = Compile(pixel_resourceAccess, "main", "ps_5_1"); - Vec4f cbufferdata = Vec4f(3.0f, 50.0f, 75.0f, 100.0f); + uint32_t cbufferdata[4] = {3, 50, 75, 100}; ID3D12ResourcePtr vb = MakeBuffer().Data(DefaultTri); - ID3D12ResourcePtr cb = MakeBuffer().Data(&cbufferdata); + ID3D12ResourcePtr cb = MakeBuffer().Data(cbufferdata); + + // Descriptor table entries: + // 0-12: CB array + // 30-33: SRV array + // 56-57: SRVs containing stepped unorm data AlignedCB cbufferarray[4][3]; for(uint32_t x = 0; x < 4; ++x) @@ -211,7 +251,14 @@ float4 main(float4 pos : SV_Position) : SV_Target0 MakeSRV(res1).CreateGPU(56); ID3D12ResourcePtr res2 = MakeTexture(DXGI_FORMAT_R8G8B8A8_UNORM, 2, 2).Mips(1).InitialState(D3D12_RESOURCE_STATE_COPY_DEST); - MakeSRV(res2).CreateGPU(57); + D3D12ViewCreator srvRes2 = MakeSRV(res2); + srvRes2.CreateGPU(57); + + // Create a few unused SRVs so that a bindless descriptor table has a lot of things to report + srvRes2.CreateGPU(500); + srvRes2.CreateGPU(501); + srvRes2.CreateGPU(510); + srvRes2.CreateGPU(625); ID3D12ResourcePtr uploadBuf = MakeBuffer().Size(1024 * 1024).Upload(); @@ -258,6 +305,15 @@ float4 main(float4 pos : SV_Position) : SV_Target0 cbvParam(D3D12_SHADER_VISIBILITY_PIXEL, 0, 3), tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 0, UINT_MAX, 30), }); + ID3D12RootSignaturePtr sig_resourceAccess = MakeSig({ + tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 0, 1, 56), + tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 1, 1, 57), + tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, UINT_MAX, 30), + tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 0, 0, 1, 56), + tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 0, 1, 1, 57), + cbvParam(D3D12_SHADER_VISIBILITY_PIXEL, 0, 0), cbvParam(D3D12_SHADER_VISIBILITY_PIXEL, 0, 1), + tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0, 8, 0), + }); ID3D12PipelineStatePtr pso_5_0 = MakePSO() .RootSig(sig_5_0) @@ -283,6 +339,12 @@ float4 main(float4 pos : SV_Position) : SV_Target0 .VS(vsblob) .PS(psblob_bindless) .RTVs({DXGI_FORMAT_R32G32B32A32_FLOAT}); + ID3D12PipelineStatePtr pso_resourceAccess = MakePSO() + .RootSig(sig_resourceAccess) + .InputLayout() + .VS(vsblob) + .PS(psblob_resourceAccess) + .RTVs({DXGI_FORMAT_R32G32B32A32_FLOAT}); ResourceBarrier(vb, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); ResourceBarrier(cb, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); @@ -351,6 +413,20 @@ float4 main(float4 pos : SV_Position) : SV_Target0 cmd->SetGraphicsRootDescriptorTable(1, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); cmd->DrawInstanced(3, 1, 0, 0); + setMarker(cmd, "ResourceAccess"); + cmd->SetPipelineState(pso_resourceAccess); + cmd->SetGraphicsRootSignature(sig_resourceAccess); + cmd->SetDescriptorHeaps(1, &m_CBVUAVSRV.GetInterfacePtr()); + cmd->SetGraphicsRootDescriptorTable(0, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + cmd->SetGraphicsRootDescriptorTable(1, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + cmd->SetGraphicsRootDescriptorTable(2, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + cmd->SetGraphicsRootDescriptorTable(3, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + cmd->SetGraphicsRootDescriptorTable(4, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + cmd->SetGraphicsRootConstantBufferView(5, cb->GetGPUVirtualAddress()); + cmd->SetGraphicsRootConstantBufferView(6, cb->GetGPUVirtualAddress() + sizeof(AlignedCB)); + cmd->SetGraphicsRootDescriptorTable(7, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + cmd->DrawInstanced(3, 1, 0, 0); + FinishUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET); cmd->Close();