From 11870b184aa57a8cb3f2d4c4ecb736c097e79fea Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Wed, 4 Dec 2024 06:07:54 +0000 Subject: [PATCH] ShaderViewer support for ShaderDirectAccess shader resources i.e. D3D12 SM6.6 HLSL Dynamic resources which index directly into the resource/sampler heap and not via shader bindings. --- qrenderdoc/Windows/ShaderViewer.cpp | 253 ++++++++++++++++++++++------ qrenderdoc/Windows/ShaderViewer.h | 17 +- 2 files changed, 216 insertions(+), 54 deletions(-) diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index 9bfe7a8b0c..a5addd4bff 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -45,25 +45,66 @@ #include "toolwindowmanager/ToolWindowManagerArea.h" #include "ui_ShaderViewer.h" +static bool ResourceReferencesMatch(const ShaderVariable &a, const ShaderVariable &b) +{ + if(a.IsDirectAccess() == b.IsDirectAccess()) + { + if(a.IsDirectAccess()) + { + if(a.GetDirectAccess() == b.GetDirectAccess()) + return true; + } + else + { + if(a.GetBindIndex() == b.GetBindIndex()) + return true; + } + } + return false; +} + struct AccessedResourceTag { AccessedResourceTag() : type(VarType::Unknown), step(0) { - bind.category = DescriptorCategory::Unknown; + category = DescriptorCategory::Unknown; } AccessedResourceTag(uint32_t s) : type(VarType::Unknown), step(s) { - bind.category = DescriptorCategory::Unknown; + category = DescriptorCategory::Unknown; + } + AccessedResourceTag(ShaderBindIndex bp, VarType t) + : resRef(bp), type(t), step(0), category(bp.category) + { + } + AccessedResourceTag(ShaderDirectAccess acc, VarType t) + : resRef(acc), type(t), step(0), category(acc.category) + { } - AccessedResourceTag(ShaderBindIndex bp, VarType t) : bind(bp), type(t), step(0) {} AccessedResourceTag(ShaderVariable var) : step(0), type(var.type) { if(var.type == VarType::ReadOnlyResource || var.type == VarType::ReadWriteResource) - bind = var.GetBindIndex(); + { + if(var.IsDirectAccess()) + { + resRef.directAccess = true; + resRef.access = var.GetDirectAccess(); + category = resRef.access.category; + } + else + { + resRef.directAccess = false; + resRef.bind = var.GetBindIndex(); + category = resRef.bind.category; + } + } else - bind.category = DescriptorCategory::Unknown; + { + category = DescriptorCategory::Unknown; + } } - ShaderBindIndex bind; + ResourceReference resRef; + DescriptorCategory category; VarType type; uint32_t step; }; @@ -1971,9 +2012,9 @@ void ShaderViewer::accessedResources_contextMenu(const QPoint &pos) contextMenu.addAction(&nextAccess); QObject::connect(&prevAccess, &QAction::triggered, - [this, tag] { runToResourceAccess(false, tag.type, tag.bind); }); + [this, tag] { runToResourceAccess(false, tag.type, tag.resRef); }); QObject::connect(&nextAccess, &QAction::triggered, - [this, tag] { runToResourceAccess(true, tag.type, tag.bind); }); + [this, tag] { runToResourceAccess(true, tag.type, tag.resRef); }); RDDialog::show(&contextMenu, w->viewport()->mapToGlobal(pos)); } @@ -2522,7 +2563,7 @@ void ShaderViewer::runTo(const rdcarray &runToInstructions, bool forwa updateDebugState(); } -void ShaderViewer::runToResourceAccess(bool forward, VarType type, const ShaderBindIndex &resource) +void ShaderViewer::runToResourceAccess(bool forward, VarType type, const ResourceReference &resRef) { if(!m_Trace || m_States.empty()) return; @@ -2550,10 +2591,19 @@ void ShaderViewer::runToResourceAccess(bool forward, VarType type, const ShaderB bool foundResource = false; for(const ShaderVariableChange &c : GetCurrentState().changes) { - if(c.after.type == type && c.after.GetBindIndex() == resource) + if((c.after.type == type) && (c.after.IsDirectAccess() == resRef.directAccess)) { - foundResource = true; - break; + if(resRef.directAccess) + { + if(c.after.GetDirectAccess() == resRef.access) + foundResource = true; + break; + } + else if(c.after.GetBindIndex() == resRef.bind) + { + foundResource = true; + break; + } } } @@ -2672,7 +2722,7 @@ void ShaderViewer::applyForwardsChange() bool found = false; for(size_t i = 0; i < m_AccessedResources.size(); i++) { - if(c.after.GetBindIndex() == m_AccessedResources[i].resource.GetBindIndex()) + if(ResourceReferencesMatch(c.after, m_AccessedResources[i].resource)) { found = true; if(m_AccessedResources[i].steps.indexOf(m_CurrentStateIdx) < 0) @@ -2702,8 +2752,6 @@ QString ShaderViewer::stringRep(const ShaderVariable &var, uint32_t row) if(type == VarType::ReadOnlyResource || type == VarType::ReadWriteResource || type == VarType::Sampler) { - ShaderBindIndex varBind = var.GetBindIndex(); - rdcarray resList; if(type == VarType::ReadOnlyResource) @@ -2713,7 +2761,18 @@ QString ShaderViewer::stringRep(const ShaderVariable &var, uint32_t row) else if(type == VarType::Sampler) resList = m_Ctx.CurPipelineState().GetSamplers(m_Stage); - int32_t bindIdx = resList.indexOf(varBind); + ShaderBindIndex varBind; + int32_t bindIdx = -1; + if(var.IsDirectAccess()) + { + ShaderDirectAccess access = var.GetDirectAccess(); + bindIdx = resList.indexOf(access); + } + else + { + varBind = var.GetBindIndex(); + bindIdx = resList.indexOf(varBind); + } if(bindIdx < 0) return QString(); @@ -2721,8 +2780,47 @@ QString ShaderViewer::stringRep(const ShaderVariable &var, uint32_t row) const UsedDescriptor &res = resList[bindIdx]; if(type == VarType::Sampler) - return samplerRep(m_ShaderDetails->samplers[varBind.index], varBind.arrayElement, - res.descriptor.resource); + { + if(!var.IsDirectAccess()) + { + return samplerRep(m_ShaderDetails->samplers[varBind.index], varBind.arrayElement, + res.descriptor.resource); + } + else + { + ShaderDirectAccess access = res.access; + if(access.descriptorStore != ResourceId()) + { + auto it = m_LogicalBindNames.find(access); + if(it == m_LogicalBindNames.end()) + { + m_Ctx.Replay().AsyncInvoke([access, this](IReplayController *r) { + rdcarray ranges; + ranges.resize(1); + ranges[0].count = 1; + ranges[0].descriptorSize = access.byteSize; + ranges[0].offset = access.byteOffset; + + rdcarray locations = + r->GetDescriptorLocations(access.descriptorStore, ranges); + if(locations.size() > 0) + { + rdcstr logicalBindName = locations[0].logicalBindName; + GUIInvoke::call(this, [access, logicalBindName, this]() { + m_LogicalBindNames[access] = logicalBindName; + updateDebugState(); + }); + } + }); + } + else + { + return it->second; + } + } + return QString(); + } + } return ToQStr(res.descriptor.resource); } @@ -2985,49 +3083,55 @@ QString ShaderViewer::getRegNames(const RDTreeWidgetItem *item, uint32_t swizzle if(mapping.type == VarType::Sampler) { - ShaderBindIndex bind = reg->GetBindIndex(); + if(!reg->IsDirectAccess()) + { + ShaderBindIndex bind = reg->GetBindIndex(); - if(bind.category != DescriptorCategory::Sampler) - return QString(); + if(bind.category != DescriptorCategory::Sampler) + return QString(); - if(bind.index >= m_ShaderDetails->samplers.size()) - return QString(); + if(bind.index >= m_ShaderDetails->samplers.size()) + return QString(); - const ShaderSampler &samp = m_ShaderDetails->samplers[bind.index]; + const ShaderSampler &samp = m_ShaderDetails->samplers[bind.index]; - if(samp.bindArraySize == ~0U) - return ret + lit("[unbounded]"); + if(samp.bindArraySize == ~0U) + return ret + lit("[unbounded]"); - if(samp.bindArraySize > 1 && child != ~0U) - return QFormatStr("%1[%2]").arg(ret).arg(child); + if(samp.bindArraySize > 1 && child != ~0U) + return QFormatStr("%1[%2]").arg(ret).arg(child); + } return ret; } else if(mapping.type == VarType::ReadOnlyResource || mapping.type == VarType::ReadWriteResource) { - ShaderBindIndex bind = reg->GetBindIndex(); + if(!reg->IsDirectAccess()) + { + ShaderBindIndex bind = reg->GetBindIndex(); - if((mapping.type == VarType::ReadOnlyResource && - bind.category != DescriptorCategory::ReadOnlyResource) || - (mapping.type == VarType::ReadWriteResource && - bind.category != DescriptorCategory::ReadWriteResource)) - return QString(); + if((mapping.type == VarType::ReadOnlyResource && + bind.category != DescriptorCategory::ReadOnlyResource) || + (mapping.type == VarType::ReadWriteResource && + bind.category != DescriptorCategory::ReadWriteResource)) + return QString(); - if((mapping.type == VarType::ReadOnlyResource && - bind.index >= m_ShaderDetails->readOnlyResources.size()) || - (mapping.type == VarType::ReadWriteResource && - bind.index >= m_ShaderDetails->readWriteResources.size())) - return QString(); + if((mapping.type == VarType::ReadOnlyResource && + bind.index >= m_ShaderDetails->readOnlyResources.size()) || + (mapping.type == VarType::ReadWriteResource && + bind.index >= m_ShaderDetails->readWriteResources.size())) + return QString(); - const ShaderResource &res = mapping.type == VarType::ReadOnlyResource - ? m_ShaderDetails->readOnlyResources[bind.index] - : m_ShaderDetails->readWriteResources[bind.index]; + const ShaderResource &res = mapping.type == VarType::ReadOnlyResource + ? m_ShaderDetails->readOnlyResources[bind.index] + : m_ShaderDetails->readWriteResources[bind.index]; - if(res.bindArraySize == ~0U) - return ret + lit("[unbounded]"); + if(res.bindArraySize == ~0U) + return ret + lit("[unbounded]"); - if(res.bindArraySize > 1 && child != ~0U) - return QFormatStr("%1[%2]").arg(ret).arg(child); + if(res.bindArraySize > 1 && child != ~0U) + return QFormatStr("%1[%2]").arg(ret).arg(child); + } return ret; } @@ -3905,6 +4009,9 @@ void ShaderViewer::updateDebugState() if(varsMapped.contains(ro.name)) continue; + if(ro.IsDirectAccess()) + continue; + const rdcarray &resList = m_ReadOnlyResources; // find all descriptors in this bind's array @@ -3969,6 +4076,9 @@ void ShaderViewer::updateDebugState() if(varsMapped.contains(rw.name)) continue; + if(rw.IsDirectAccess()) + continue; + const rdcarray &resList = m_ReadWriteResources; // find all descriptors in this bind's array @@ -4033,6 +4143,9 @@ void ShaderViewer::updateDebugState() if(varsMapped.contains(s.name)) continue; + if(s.IsDirectAccess()) + continue; + // find all descriptors in this bind's array ShaderBindIndex bind = s.GetBindIndex(); bind.arrayElement = 0; @@ -4707,6 +4820,9 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi rdcarray samplers = m_Ctx.CurPipelineState().GetSamplers(m_Stage); + if(reg->IsDirectAccess()) + continue; + ShaderBindIndex bind = reg->GetBindIndex(); int32_t bindIdx = samplers.indexOf(bind); @@ -4751,6 +4867,9 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi if(reg == NULL) continue; + if(reg->IsDirectAccess()) + continue; + typeName = isReadOnlyResource ? lit("Resource") : lit("RW Resource"); const rdcarray &resList = @@ -4936,30 +5055,58 @@ RDTreeWidgetItem *ShaderViewer::makeDebugVariableNode(const ShaderVariable &v, r RDTreeWidgetItem *ShaderViewer::makeAccessedResourceNode(const ShaderVariable &v) { ShaderBindIndex bp = v.GetBindIndex(); + ShaderDirectAccess acc = v.GetDirectAccess(); ResourceId resId; QString typeName; if(v.type == VarType::ReadOnlyResource) { - if(bp.category != DescriptorCategory::ReadOnlyResource) - qCritical() << "Mismatch between variable type and descriptor category"; + DescriptorCategory category = DescriptorCategory::Unknown; + int32_t bindIdx = -1; typeName = lit("Resource"); - int32_t bindIdx = m_ReadOnlyResources.indexOf(bp); + if(!v.IsDirectAccess()) + { + category = bp.category; + bindIdx = m_ReadOnlyResources.indexOf(bp); + } + else + { + category = acc.category; + bindIdx = m_ReadOnlyResources.indexOf(acc); + } + if(category != DescriptorCategory::ReadOnlyResource) + qCritical() << "Mismatch between variable type and descriptor category"; if(bindIdx >= 0) resId = m_ReadOnlyResources[bindIdx].descriptor.resource; } else if(v.type == VarType::ReadWriteResource) { - if(bp.category != DescriptorCategory::ReadWriteResource) - qCritical() << "Mismatch between variable type and descriptor category"; + DescriptorCategory category = DescriptorCategory::Unknown; + int32_t bindIdx = -1; typeName = lit("RW Resource"); - int32_t bindIdx = m_ReadWriteResources.indexOf(bp); + if(!v.IsDirectAccess()) + { + category = bp.category; + bindIdx = m_ReadWriteResources.indexOf(bp); + } + else + { + category = acc.category; + bindIdx = m_ReadWriteResources.indexOf(acc); + } + if(category != DescriptorCategory::ReadWriteResource) + qCritical() << "Mismatch between variable type and descriptor category"; if(bindIdx >= 0) resId = m_ReadWriteResources[bindIdx].descriptor.resource; } RDTreeWidgetItem *node = new RDTreeWidgetItem({v.name, typeName, ToQStr(resId)}); if(resId != ResourceId()) - node->setTag(QVariant::fromValue(AccessedResourceTag(bp, v.type))); + { + if(!v.IsDirectAccess()) + node->setTag(QVariant::fromValue(AccessedResourceTag(bp, v.type))); + else + node->setTag(QVariant::fromValue(AccessedResourceTag(acc, v.type))); + } if(HasChanged(v.name)) node->setForegroundColor(QColor(Qt::red)); diff --git a/qrenderdoc/Windows/ShaderViewer.h b/qrenderdoc/Windows/ShaderViewer.h index 78792ac062..6f700ab04a 100644 --- a/qrenderdoc/Windows/ShaderViewer.h +++ b/qrenderdoc/Windows/ShaderViewer.h @@ -101,6 +101,19 @@ struct VariableTag rdcstr absoluteRefPath; }; +struct ResourceReference +{ + ResourceReference() {} + ResourceReference(ShaderBindIndex bp) : bind(bp), directAccess(false) {} + ResourceReference(ShaderDirectAccess acc) : access(acc), directAccess(true) {} + union + { + ShaderBindIndex bind; + ShaderDirectAccess access; + }; + bool directAccess; +}; + class ShaderViewer : public QFrame, public IShaderViewer, public ICaptureViewer { Q_OBJECT @@ -336,6 +349,8 @@ private slots: QSet> m_Breakpoints; bool m_TempBreakpoint = false; + std::map m_LogicalBindNames; + QList> m_FindAllResults; static const int BOOKMARK_MAX_MENU_ENTRY_LENGTH = 40; // max length of bookmark names in menu @@ -423,7 +438,7 @@ private slots: void runTo(const rdcarray &runToInstructions, bool forward, ShaderEvents condition); void runTo(uint32_t runToInstruction, bool forward, ShaderEvents condition = ShaderEvents::NoEvent); - void runToResourceAccess(bool forward, VarType type, const ShaderBindIndex &resource); + void runToResourceAccess(bool forward, VarType type, const ResourceReference &resRef); void applyBackwardsChange(); void applyForwardsChange();