From 2bbe1a8cd9fe6f0f12de4870c04159100fde48bd Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 19 Mar 2024 17:28:28 +0000 Subject: [PATCH] Update shader viewer and debugging with new reporting for shader binds * This shifts from reporting from the old style bindset/bind to the new system of only referencing by shader interface and index (independent of binding model). * The vulkan shader debugger re-uses the replay interface to cache descriptor access and descriptor contents in a fashion friendly to interface-index lookup. --- qrenderdoc/Code/CaptureContext.cpp | 5 +- qrenderdoc/Code/CaptureContext.h | 5 +- qrenderdoc/Code/Interface/QRDInterface.h | 4 +- qrenderdoc/Windows/BufferViewer.cpp | 4 +- .../D3D11PipelineStateViewer.cpp | 7 +- .../D3D12PipelineStateViewer.cpp | 7 +- .../VulkanPipelineStateViewer.cpp | 7 +- qrenderdoc/Windows/PixelHistoryView.cpp | 4 +- qrenderdoc/Windows/PythonShell.cpp | 5 +- qrenderdoc/Windows/ShaderMessageViewer.cpp | 4 +- qrenderdoc/Windows/ShaderViewer.cpp | 400 +++++++-------- qrenderdoc/Windows/ShaderViewer.h | 24 +- qrenderdoc/Windows/TextureViewer.cpp | 4 +- renderdoc/api/replay/common_pipestate.h | 12 + renderdoc/api/replay/shader_types.h | 104 ++++ renderdoc/driver/d3d11/d3d11_shaderdebug.cpp | 17 +- renderdoc/driver/d3d12/d3d12_shaderdebug.cpp | 12 +- renderdoc/driver/shaders/dxbc/dxbc_debug.cpp | 70 ++- renderdoc/driver/shaders/dxbc/dxbc_debug.h | 12 +- .../driver/shaders/spirv/spirv_debug.cpp | 39 +- renderdoc/driver/shaders/spirv/spirv_debug.h | 16 +- .../shaders/spirv/spirv_debug_setup.cpp | 58 ++- .../driver/shaders/spirv/spirv_reflect.cpp | 23 +- .../driver/shaders/spirv/spirv_reflect.h | 6 + renderdoc/driver/vulkan/vk_replay.cpp | 4 +- renderdoc/driver/vulkan/vk_shaderdebug.cpp | 466 +++++++----------- 26 files changed, 645 insertions(+), 674 deletions(-) diff --git a/qrenderdoc/Code/CaptureContext.cpp b/qrenderdoc/Code/CaptureContext.cpp index 95538d4ea..d82c46108 100644 --- a/qrenderdoc/Code/CaptureContext.cpp +++ b/qrenderdoc/Code/CaptureContext.cpp @@ -2568,11 +2568,10 @@ void CaptureContext::RevertShaderEdit(IShaderViewer *viewer, ResourceId id) }); } -IShaderViewer *CaptureContext::DebugShader(const ShaderBindpointMapping *bind, - const ShaderReflection *shader, ResourceId pipeline, +IShaderViewer *CaptureContext::DebugShader(const ShaderReflection *shader, ResourceId pipeline, ShaderDebugTrace *trace, const rdcstr &debugContext) { - return ShaderViewer::DebugShader(*this, bind, shader, pipeline, trace, debugContext, + return ShaderViewer::DebugShader(*this, shader, pipeline, trace, debugContext, m_MainWindow->Widget()); } diff --git a/qrenderdoc/Code/CaptureContext.h b/qrenderdoc/Code/CaptureContext.h index 0409771dc..f9b32cba8 100644 --- a/qrenderdoc/Code/CaptureContext.h +++ b/qrenderdoc/Code/CaptureContext.h @@ -255,9 +255,8 @@ public: const rdcstr &entryFunc, const bytebuf &shaderBytes); void RevertShaderEdit(IShaderViewer *viewer, ResourceId id); - IShaderViewer *DebugShader(const ShaderBindpointMapping *bind, const ShaderReflection *shader, - ResourceId pipeline, ShaderDebugTrace *trace, - const rdcstr &debugContext) override; + IShaderViewer *DebugShader(const ShaderReflection *shader, ResourceId pipeline, + ShaderDebugTrace *trace, const rdcstr &debugContext) override; IShaderViewer *ViewShader(const ShaderReflection *shader, ResourceId pipeline) override; diff --git a/qrenderdoc/Code/Interface/QRDInterface.h b/qrenderdoc/Code/Interface/QRDInterface.h index 3a42a3028..f4d355635 100644 --- a/qrenderdoc/Code/Interface/QRDInterface.h +++ b/qrenderdoc/Code/Interface/QRDInterface.h @@ -2550,7 +2550,6 @@ place if needed. DOCUMENT(R"(Show a new :class:`ShaderViewer` window, showing a read-only view of a debug trace through the execution of a given shader. -:param renderdoc.ShaderBindpointMapping bind: The bindpoint mapping for the shader to view. :param renderdoc.ShaderReflection shader: The reflection data for the shader to view. :param renderdoc.ResourceId pipeline: The pipeline state object, if applicable, that this shader is bound to. @@ -2560,8 +2559,7 @@ through the execution of a given shader. :return: The new :class:`ShaderViewer` window opened, but not shown. :rtype: ShaderViewer )"); - virtual IShaderViewer *DebugShader(const ShaderBindpointMapping *bind, - const ShaderReflection *shader, ResourceId pipeline, + virtual IShaderViewer *DebugShader(const ShaderReflection *shader, ResourceId pipeline, ShaderDebugTrace *trace, const rdcstr &debugContext) = 0; DOCUMENT(R"(Show a new :class:`ShaderViewer` window, showing a read-only view of a given shader. diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index c64a1a7b0..448faf505 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -6616,12 +6616,10 @@ void BufferViewer::debugVertex() const ShaderReflection *shaderDetails = m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Vertex); - const ShaderBindpointMapping &bindMapping = - m_Ctx.CurPipelineState().GetBindpointMapping(ShaderStage::Vertex); ResourceId pipeline = m_Ctx.CurPipelineState().GetGraphicsPipelineObject(); // viewer takes ownership of the trace - IShaderViewer *s = m_Ctx.DebugShader(&bindMapping, shaderDetails, pipeline, trace, debugContext); + IShaderViewer *s = m_Ctx.DebugShader(shaderDetails, pipeline, trace, debugContext); m_Ctx.AddDockWindow(s->Widget(), DockReference::AddTo, this); } diff --git a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp index e98be4d5e..a46d41cb8 100644 --- a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp @@ -3311,8 +3311,6 @@ void D3D11PipelineStateViewer::computeDebugSelector_beginDebug( const ShaderReflection *shaderDetails = m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Compute); - const ShaderBindpointMapping &bindMapping = - m_Ctx.CurPipelineState().GetBindpointMapping(ShaderStage::Compute); if(!shaderDetails) return; @@ -3367,9 +3365,8 @@ void D3D11PipelineStateViewer::computeDebugSelector_beginDebug( } // viewer takes ownership of the trace - IShaderViewer *s = - m_Ctx.DebugShader(&bindMapping, shaderDetails, - m_Ctx.CurPipelineState().GetComputePipelineObject(), trace, debugContext); + IShaderViewer *s = m_Ctx.DebugShader( + shaderDetails, m_Ctx.CurPipelineState().GetComputePipelineObject(), trace, debugContext); m_Ctx.AddDockWindow(s->Widget(), DockReference::AddTo, this); } diff --git a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp index 3efb088c1..530f08c4b 100644 --- a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp @@ -3825,8 +3825,6 @@ void D3D12PipelineStateViewer::computeDebugSelector_beginDebug( const ShaderReflection *shaderDetails = m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Compute); - const ShaderBindpointMapping &bindMapping = - m_Ctx.CurPipelineState().GetBindpointMapping(ShaderStage::Compute); if(!shaderDetails) return; @@ -3881,9 +3879,8 @@ void D3D12PipelineStateViewer::computeDebugSelector_beginDebug( } // viewer takes ownership of the trace - IShaderViewer *s = - m_Ctx.DebugShader(&bindMapping, shaderDetails, - m_Ctx.CurPipelineState().GetComputePipelineObject(), trace, debugContext); + IShaderViewer *s = m_Ctx.DebugShader( + shaderDetails, m_Ctx.CurPipelineState().GetComputePipelineObject(), trace, debugContext); m_Ctx.AddDockWindow(s->Widget(), DockReference::AddTo, this); } diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp index f1136fd5e..0807af0c1 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp @@ -5439,8 +5439,6 @@ void VulkanPipelineStateViewer::computeDebugSelector_beginDebug( const ShaderReflection *shaderDetails = m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Compute); - const ShaderBindpointMapping &bindMapping = - m_Ctx.CurPipelineState().GetBindpointMapping(ShaderStage::Compute); if(!shaderDetails) return; @@ -5495,9 +5493,8 @@ void VulkanPipelineStateViewer::computeDebugSelector_beginDebug( } // viewer takes ownership of the trace - IShaderViewer *s = - m_Ctx.DebugShader(&bindMapping, shaderDetails, - m_Ctx.CurPipelineState().GetComputePipelineObject(), trace, debugContext); + IShaderViewer *s = m_Ctx.DebugShader( + shaderDetails, m_Ctx.CurPipelineState().GetComputePipelineObject(), trace, debugContext); m_Ctx.AddDockWindow(s->Widget(), DockReference::AddTo, this); } diff --git a/qrenderdoc/Windows/PixelHistoryView.cpp b/qrenderdoc/Windows/PixelHistoryView.cpp index 2535c0bdf..5045bc4e3 100644 --- a/qrenderdoc/Windows/PixelHistoryView.cpp +++ b/qrenderdoc/Windows/PixelHistoryView.cpp @@ -822,12 +822,10 @@ void PixelHistoryView::startDebug(EventTag tag) return; } - const ShaderBindpointMapping &bindMapping = - m_Ctx.CurPipelineState().GetBindpointMapping(ShaderStage::Pixel); ResourceId pipeline = m_Ctx.CurPipelineState().GetGraphicsPipelineObject(); // viewer takes ownership of the trace - IShaderViewer *s = m_Ctx.DebugShader(&bindMapping, shaderDetails, pipeline, trace, debugContext); + IShaderViewer *s = m_Ctx.DebugShader(shaderDetails, pipeline, trace, debugContext); m_Ctx.AddDockWindow(s->Widget(), DockReference::MainToolArea, NULL); } diff --git a/qrenderdoc/Windows/PythonShell.cpp b/qrenderdoc/Windows/PythonShell.cpp index 63ce4f8bc..d5837cb68 100644 --- a/qrenderdoc/Windows/PythonShell.cpp +++ b/qrenderdoc/Windows/PythonShell.cpp @@ -814,11 +814,10 @@ struct CaptureContextInvoker : ObjectForwarder revertCallback); } - virtual IShaderViewer *DebugShader(const ShaderBindpointMapping *bind, - const ShaderReflection *shader, ResourceId pipeline, + virtual IShaderViewer *DebugShader(const ShaderReflection *shader, ResourceId pipeline, ShaderDebugTrace *trace, const rdcstr &debugContext) override { - return InvokeRetFunction(&ICaptureContext::DebugShader, bind, shader, pipeline, + return InvokeRetFunction(&ICaptureContext::DebugShader, shader, pipeline, trace, debugContext); } diff --git a/qrenderdoc/Windows/ShaderMessageViewer.cpp b/qrenderdoc/Windows/ShaderMessageViewer.cpp index 9e35f29e9..bac9047fb 100644 --- a/qrenderdoc/Windows/ShaderMessageViewer.cpp +++ b/qrenderdoc/Windows/ShaderMessageViewer.cpp @@ -439,14 +439,12 @@ ShaderMessageViewer::ShaderMessageViewer(ICaptureContext &ctx, ShaderStageMask s return; } - const ShaderBindpointMapping &bindMapping = - m_Ctx.CurPipelineState().GetBindpointMapping(msg.stage); ResourceId pipeline = msg.stage == ShaderStage::Compute ? m_Ctx.CurPipelineState().GetComputePipelineObject() : m_Ctx.CurPipelineState().GetGraphicsPipelineObject(); // viewer takes ownership of the trace - IShaderViewer *s = m_Ctx.DebugShader(&bindMapping, refl, pipeline, trace, debugContext); + IShaderViewer *s = m_Ctx.DebugShader(refl, pipeline, trace, debugContext); if(msg.disassemblyLine >= 0) { diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index be8ca3459..b009702a5 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -47,17 +47,23 @@ struct AccessedResourceTag { - AccessedResourceTag() : type(VarType::Unknown), step(0) { bind.bind = -1; } - AccessedResourceTag(uint32_t s) : type(VarType::Unknown), step(s) { bind.bind = -1; } - AccessedResourceTag(BindpointIndex bp, VarType t) : bind(bp), type(t), step(0) {} + AccessedResourceTag() : type(VarType::Unknown), step(0) + { + bind.category = DescriptorCategory::Unknown; + } + AccessedResourceTag(uint32_t s) : type(VarType::Unknown), step(s) + { + bind.category = DescriptorCategory::Unknown; + } + 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.GetBinding(); + bind = var.GetBindIndex(); else - bind.bind = -1; + bind.category = DescriptorCategory::Unknown; } - BindpointIndex bind; + ShaderBindIndex bind; VarType type; uint32_t step; }; @@ -381,16 +387,13 @@ void ShaderViewer::editShader(ResourceId id, ShaderStage stage, const QString &e void ShaderViewer::cacheResources() { - m_ReadOnlyResources = m_Ctx.CurPipelineState().GetReadOnlyResources(m_Stage, false); - m_ReadWriteResources = m_Ctx.CurPipelineState().GetReadWriteResources(m_Stage, false); + m_ReadOnlyResources = m_Ctx.CurPipelineState().GetReadOnlyDescriptors(m_Stage, false); + m_ReadWriteResources = m_Ctx.CurPipelineState().GetReadWriteDescriptors(m_Stage, false); } -void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderReflection *shader, - ResourceId pipeline, ShaderDebugTrace *trace, - const QString &debugContext) +void ShaderViewer::debugShader(const ShaderReflection *shader, ResourceId pipeline, + ShaderDebugTrace *trace, const QString &debugContext) { - if(bind) - m_Mapping = *bind; m_ShaderDetails = shader; m_Pipeline = pipeline; m_Trace = trace; @@ -403,7 +406,7 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR // no replacing allowed, stay in find mode m_FindReplace->allowUserModeChange(false); - if(!bind || !m_ShaderDetails) + if(!m_ShaderDetails) m_Trace = NULL; if(m_ShaderDetails) @@ -2408,7 +2411,7 @@ void ShaderViewer::runTo(const rdcarray &runToInstructions, bool forwa updateDebugState(); } -void ShaderViewer::runToResourceAccess(bool forward, VarType type, const BindpointIndex &resource) +void ShaderViewer::runToResourceAccess(bool forward, VarType type, const ShaderBindIndex &resource) { if(!m_Trace || m_States.empty()) return; @@ -2436,7 +2439,7 @@ void ShaderViewer::runToResourceAccess(bool forward, VarType type, const Bindpoi bool foundResource = false; for(const ShaderVariableChange &c : GetCurrentState().changes) { - if(c.after.type == type && c.after.GetBinding() == resource) + if(c.after.type == type && c.after.GetBindIndex() == resource) { foundResource = true; break; @@ -2558,7 +2561,7 @@ void ShaderViewer::applyForwardsChange() bool found = false; for(size_t i = 0; i < m_AccessedResources.size(); i++) { - if(c.after.GetBinding() == m_AccessedResources[i].resource.GetBinding()) + if(c.after.GetBindIndex() == m_AccessedResources[i].resource.GetBindIndex()) { found = true; if(m_AccessedResources[i].steps.indexOf(m_CurrentStateIdx) < 0) @@ -2588,52 +2591,50 @@ QString ShaderViewer::stringRep(const ShaderVariable &var, uint32_t row) if(type == VarType::ReadOnlyResource || type == VarType::ReadWriteResource || type == VarType::Sampler) { - BindpointIndex varBind = var.GetBinding(); + ShaderBindIndex varBind = var.GetBindIndex(); - rdcarray resList; + rdcarray resList; if(type == VarType::ReadOnlyResource) resList = m_ReadOnlyResources; else if(type == VarType::ReadWriteResource) resList = m_ReadWriteResources; else if(type == VarType::Sampler) - resList = m_Ctx.CurPipelineState().GetSamplers(m_Stage); + resList = m_Ctx.CurPipelineState().GetSamplerDescriptors(m_Stage); - int32_t bindIdx = resList.indexOf(Bindpoint(varBind)); + int32_t bindIdx = resList.indexOf(varBind); if(bindIdx < 0) return QString(); - BoundResourceArray res = resList[bindIdx]; - - if(varBind.arrayIndex >= res.resources.size()) - return QString(); + const UsedDescriptor &res = resList[bindIdx]; if(type == VarType::Sampler) - return samplerRep(varBind, varBind.arrayIndex, res.resources[varBind.arrayIndex].resourceId); - return ToQStr(res.resources[varBind.arrayIndex].resourceId); + return samplerRep(m_ShaderDetails->samplers[varBind.index], varBind.arrayElement, + res.descriptor.resource); + return ToQStr(res.descriptor.resource); } return RowString(var, row, type); } -QString ShaderViewer::samplerRep(Bindpoint bind, uint32_t arrayIndex, ResourceId id) +QString ShaderViewer::samplerRep(const ShaderSampler &samp, uint32_t arrayElement, ResourceId id) { if(id == ResourceId()) { QString contents; - if(bind.bindset > 0) + if(samp.fixedBindSetOrSpace > 0) { // a bit ugly to do an API-specific switch here but we don't have a better way to refer // by binding contents = IsD3D(m_Ctx.APIProps().pipelineType) ? tr("space%1, ") : tr("Set %1, "); - contents = contents.arg(bind.bindset); + contents = contents.arg(samp.fixedBindSetOrSpace); } - if(arrayIndex == ~0U) - contents += QString::number(bind.bind); + if(arrayElement == ~0U || samp.bindArraySize == 1) + contents += QString::number(samp.fixedBindNumber); else - contents += QFormatStr("%1[%2]").arg(bind.bind).arg(arrayIndex); + contents += QFormatStr("%1[%2]").arg(samp.fixedBindNumber).arg(arrayElement); return contents; } @@ -2873,43 +2874,48 @@ QString ShaderViewer::getRegNames(const RDTreeWidgetItem *item, uint32_t swizzle if(mapping.type == VarType::Sampler) { - rdcarray samplers = m_Ctx.CurPipelineState().GetSamplers(m_Stage); + ShaderBindIndex bind = reg->GetBindIndex(); - int32_t idx = m_Mapping.samplers.indexOf(Bindpoint(reg->GetBinding())); - - if(idx < 0) + if(bind.category != DescriptorCategory::Sampler) return QString(); - Bindpoint bind = m_Mapping.samplers[idx]; + if(bind.index >= m_ShaderDetails->samplers.size()) + return QString(); - if(bind.arraySize == ~0U) + const ShaderSampler &samp = m_ShaderDetails->samplers[bind.index]; + + if(samp.bindArraySize == ~0U) return ret + lit("[unbounded]"); - if(bind.arraySize > 1 && child != ~0U) + if(samp.bindArraySize > 1 && child != ~0U) return QFormatStr("%1[%2]").arg(ret).arg(child); return ret; } - else if(mapping.type == VarType::ReadOnlyResource) + else if(mapping.type == VarType::ReadOnlyResource || mapping.type == VarType::ReadWriteResource) { - const bool isReadOnlyResource = mapping.type == VarType::ReadOnlyResource; + ShaderBindIndex bind = reg->GetBindIndex(); - rdcarray &resList = - isReadOnlyResource ? m_ReadOnlyResources : m_ReadWriteResources; - - int32_t idx = (isReadOnlyResource ? m_Mapping.readOnlyResources : m_Mapping.readWriteResources) - .indexOf(Bindpoint(reg->GetBinding())); - - if(idx < 0) + if((mapping.type == VarType::ReadOnlyResource && + bind.category != DescriptorCategory::ReadOnlyResource) || + (mapping.type == VarType::ReadWriteResource && + bind.category != DescriptorCategory::ReadWriteResource)) return QString(); - Bindpoint bind = - isReadOnlyResource ? m_Mapping.readOnlyResources[idx] : m_Mapping.readWriteResources[idx]; + if((mapping.type == VarType::ReadOnlyResource && + bind.index >= m_ShaderDetails->readOnlyResources.size()) || + (mapping.type == VarType::ReadWriteResource && + bind.index >= m_ShaderDetails->readWriteResources.size())) + return QString(); - if(bind.arraySize == ~0U) + 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(bind.arraySize > 1 && child != ~0U) + if(res.bindArraySize > 1 && child != ~0U) return QFormatStr("%1[%2]").arg(ret).arg(child); return ret; @@ -3779,7 +3785,7 @@ void ShaderViewer::updateDebugState() } } - rdcarray &roBinds = m_ReadOnlyResources; + rdcarray &roBinds = m_ReadOnlyResources; for(int i = 0; i < m_Trace->readOnlyResources.count(); i++) { @@ -3788,57 +3794,51 @@ void ShaderViewer::updateDebugState() if(varsMapped.contains(ro.name)) continue; - int32_t idx = m_Mapping.readOnlyResources.indexOf(Bindpoint(ro.GetBinding())); + const rdcarray &resList = m_ReadOnlyResources; - if(idx < 0) + // find all descriptors in this bind's array + ShaderBindIndex bind = ro.GetBindIndex(); + bind.arrayElement = 0; + + rdcarray descriptors; + for(const UsedDescriptor &a : resList) + if(CategoryForDescriptorType(a.access.type) == bind.category && a.access.index == bind.index) + descriptors.push_back(a); + + if(descriptors.empty()) continue; - Bindpoint bind = m_Mapping.readOnlyResources[idx]; + const ShaderResource &res = m_ShaderDetails->readOnlyResources[bind.index]; - if(!bind.used) - continue; - - int32_t bindIdx = roBinds.indexOf(bind); - - if(bindIdx < 0) - continue; - - BoundResourceArray &roBind = roBinds[bindIdx]; - - if(bind.arraySize == 1) - { - if(!roBind.resources.empty()) - { - RDTreeWidgetItem *node = - new RDTreeWidgetItem({m_ShaderDetails->readOnlyResources[i].name, ro.name, - lit("Resource"), ToQStr(roBind.resources[0].resourceId)}); - node->setTag(QVariant::fromValue(VariableTag(DebugVariableType::ReadOnlyResource, ro.name))); - ui->constants->addTopLevelItem(node); - } - } - else if(bind.arraySize == ~0U) + if(res.bindArraySize == 1) { RDTreeWidgetItem *node = new RDTreeWidgetItem( - {m_ShaderDetails->readOnlyResources[i].name, ro.name, lit("[unbounded]"), QString()}); + {res.name, ro.name, lit("Resource"), ToQStr(descriptors[0].descriptor.resource)}); + node->setTag(QVariant::fromValue(VariableTag(DebugVariableType::ReadOnlyResource, ro.name))); + ui->constants->addTopLevelItem(node); + } + else if(res.bindArraySize == ~0U) + { + RDTreeWidgetItem *node = + new RDTreeWidgetItem({res.name, ro.name, lit("[unbounded]"), QString()}); node->setTag(QVariant::fromValue(VariableTag(DebugVariableType::ReadOnlyResource, ro.name))); ui->constants->addTopLevelItem(node); } else { - RDTreeWidgetItem *node = - new RDTreeWidgetItem({m_ShaderDetails->readOnlyResources[i].name, ro.name, - QFormatStr("[%1]").arg(bind.arraySize), QString()}); + RDTreeWidgetItem *node = new RDTreeWidgetItem( + {res.name, ro.name, QFormatStr("[%1]").arg(res.bindArraySize), QString()}); node->setTag(QVariant::fromValue(VariableTag(DebugVariableType::ReadOnlyResource, ro.name))); - uint32_t count = qMin(bind.arraySize, (uint32_t)roBind.resources.size()); + uint32_t count = qMin(res.bindArraySize, (uint32_t)descriptors.size()); for(uint32_t a = 0; a < count; a++) { QString childName = QFormatStr("%1[%2]").arg(ro.name).arg(a); RDTreeWidgetItem *child = new RDTreeWidgetItem({ - QFormatStr("%1[%2]").arg(m_ShaderDetails->readOnlyResources[i].name).arg(a), + QFormatStr("%1[%2]").arg(res.name).arg(a), childName, lit("Resource"), - ToQStr(roBind.resources[a].resourceId), + ToQStr(descriptors[a].descriptor.resource), }); child->setTag( QVariant::fromValue(VariableTag(DebugVariableType::ReadOnlyResource, childName))); @@ -3849,7 +3849,7 @@ void ShaderViewer::updateDebugState() } } - rdcarray &rwBinds = m_ReadWriteResources; + rdcarray &rwBinds = m_ReadWriteResources; for(int i = 0; i < m_Trace->readWriteResources.count(); i++) { @@ -3858,58 +3858,51 @@ void ShaderViewer::updateDebugState() if(varsMapped.contains(rw.name)) continue; - int32_t idx = m_Mapping.readWriteResources.indexOf(Bindpoint(rw.GetBinding())); + const rdcarray &resList = m_ReadWriteResources; - if(idx < 0) + // find all descriptors in this bind's array + ShaderBindIndex bind = rw.GetBindIndex(); + bind.arrayElement = 0; + + rdcarray descriptors; + for(const UsedDescriptor &a : resList) + if(CategoryForDescriptorType(a.access.type) == bind.category && a.access.index == bind.index) + descriptors.push_back(a); + + if(descriptors.empty()) continue; - Bindpoint bind = m_Mapping.readWriteResources[idx]; + const ShaderResource &res = m_ShaderDetails->readWriteResources[bind.index]; - if(!bind.used) - continue; - - int32_t bindIdx = rwBinds.indexOf(bind); - - if(bindIdx < 0) - continue; - - BoundResourceArray &rwBind = rwBinds[bindIdx]; - - if(bind.arraySize == 1) - { - if(!rwBind.resources.empty()) - { - RDTreeWidgetItem *node = - new RDTreeWidgetItem({m_ShaderDetails->readWriteResources[i].name, rw.name, - lit("Resource"), ToQStr(rwBind.resources[0].resourceId)}); - node->setTag( - QVariant::fromValue(VariableTag(DebugVariableType::ReadWriteResource, rw.name))); - ui->constants->addTopLevelItem(node); - } - } - else if(bind.arraySize == ~0U) + if(res.bindArraySize == 1) { RDTreeWidgetItem *node = new RDTreeWidgetItem( - {m_ShaderDetails->readWriteResources[i].name, rw.name, lit("[unbounded]"), QString()}); + {res.name, rw.name, lit("Resource"), ToQStr(descriptors[0].descriptor.resource)}); + node->setTag(QVariant::fromValue(VariableTag(DebugVariableType::ReadWriteResource, rw.name))); + ui->constants->addTopLevelItem(node); + } + else if(res.bindArraySize == ~0U) + { + RDTreeWidgetItem *node = + new RDTreeWidgetItem({res.name, rw.name, lit("[unbounded]"), QString()}); node->setTag(QVariant::fromValue(VariableTag(DebugVariableType::ReadWriteResource, rw.name))); ui->constants->addTopLevelItem(node); } else { - RDTreeWidgetItem *node = - new RDTreeWidgetItem({m_ShaderDetails->readWriteResources[i].name, rw.name, - QFormatStr("[%1]").arg(bind.arraySize), QString()}); + RDTreeWidgetItem *node = new RDTreeWidgetItem( + {res.name, rw.name, QFormatStr("[%1]").arg(res.bindArraySize), QString()}); node->setTag(QVariant::fromValue(VariableTag(DebugVariableType::ReadWriteResource, rw.name))); - uint32_t count = qMin(bind.arraySize, (uint32_t)rwBind.resources.size()); + uint32_t count = qMin(res.bindArraySize, (uint32_t)descriptors.size()); for(uint32_t a = 0; a < count; a++) { QString childName = QFormatStr("%1[%2]").arg(rw.name).arg(a); RDTreeWidgetItem *child = new RDTreeWidgetItem({ - QFormatStr("%1[%2]").arg(m_ShaderDetails->readWriteResources[i].name).arg(a), + QFormatStr("%1[%2]").arg(res.name).arg(a), childName, lit("RW Resource"), - ToQStr(rwBind.resources[a].resourceId), + ToQStr(descriptors[a].descriptor.resource), }); child->setTag( QVariant::fromValue(VariableTag(DebugVariableType::ReadWriteResource, childName))); @@ -3920,7 +3913,7 @@ void ShaderViewer::updateDebugState() } } - rdcarray samplers = m_Ctx.CurPipelineState().GetSamplers(m_Stage); + rdcarray samplers = m_Ctx.CurPipelineState().GetSamplerDescriptors(m_Stage); for(int i = 0; i < m_Trace->samplers.count(); i++) { @@ -3929,56 +3922,50 @@ void ShaderViewer::updateDebugState() if(varsMapped.contains(s.name)) continue; - int32_t idx = m_Mapping.samplers.indexOf(Bindpoint(s.GetBinding())); + // find all descriptors in this bind's array + ShaderBindIndex bind = s.GetBindIndex(); + bind.arrayElement = 0; - if(idx < 0) + rdcarray descriptors; + for(const UsedDescriptor &a : samplers) + if(CategoryForDescriptorType(a.access.type) == bind.category && a.access.index == bind.index) + descriptors.push_back(a); + + if(descriptors.empty()) continue; - Bindpoint bind = m_Mapping.samplers[idx]; + const ShaderSampler &samp = m_ShaderDetails->samplers[bind.index]; - if(!bind.used) - continue; - - int32_t bindIdx = samplers.indexOf(bind); - - if(bindIdx < 0) - continue; - - BoundResourceArray sampBind = samplers[bindIdx]; - - if(bind.arraySize == 1) + if(samp.bindArraySize == 1) { - if(!sampBind.resources.empty()) - { - RDTreeWidgetItem *node = - new RDTreeWidgetItem({m_ShaderDetails->samplers[i].name, s.name, lit("Sampler"), - samplerRep(bind, ~0U, sampBind.resources[0].resourceId)}); - node->setTag(QVariant::fromValue(VariableTag(DebugVariableType::Sampler, s.name))); - ui->constants->addTopLevelItem(node); - } + RDTreeWidgetItem *node = + new RDTreeWidgetItem({samp.name, s.name, lit("Sampler"), + samplerRep(samp, ~0U, descriptors[0].descriptor.resource)}); + node->setTag(QVariant::fromValue(VariableTag(DebugVariableType::Sampler, s.name))); + ui->constants->addTopLevelItem(node); } - else if(bind.arraySize == ~0U) + else if(samp.bindArraySize == ~0U) { - RDTreeWidgetItem *node = new RDTreeWidgetItem( - {m_ShaderDetails->samplers[i].name, s.name, lit("[unbounded]"), QString()}); + RDTreeWidgetItem *node = + new RDTreeWidgetItem({samp.name, s.name, lit("[unbounded]"), QString()}); node->setTag(QVariant::fromValue(VariableTag(DebugVariableType::Sampler, s.name))); ui->constants->addTopLevelItem(node); } else { - RDTreeWidgetItem *node = - new RDTreeWidgetItem({m_ShaderDetails->samplers[i].name, s.name, - QFormatStr("[%1]").arg(bind.arraySize), QString()}); + RDTreeWidgetItem *node = new RDTreeWidgetItem( + {samp.name, s.name, QFormatStr("[%1]").arg(samp.bindArraySize), QString()}); node->setTag(QVariant::fromValue(VariableTag(DebugVariableType::Sampler, s.name))); - for(uint32_t a = 0; a < bind.arraySize; a++) + uint32_t count = qMin(samp.bindArraySize, (uint32_t)descriptors.size()); + for(uint32_t a = 0; a < count; a++) { QString childName = QFormatStr("%1[%2]").arg(s.name).arg(a); RDTreeWidgetItem *child = new RDTreeWidgetItem({ QFormatStr("%1[%2]").arg(m_ShaderDetails->samplers[i].name).arg(a), childName, lit("Sampler"), - samplerRep(bind, a, sampBind.resources[a].resourceId), + samplerRep(samp, a, descriptors[a].descriptor.resource), }); child->setTag(QVariant::fromValue(VariableTag(DebugVariableType::Sampler, childName))); node->addChild(child); @@ -4607,45 +4594,39 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi typeName = lit("Sampler"); - rdcarray samplers = m_Ctx.CurPipelineState().GetSamplers(m_Stage); - - int32_t idx = m_Mapping.samplers.indexOf(Bindpoint(reg->GetBinding())); - - if(idx < 0) - continue; - - Bindpoint bind = m_Mapping.samplers[idx]; + rdcarray samplers = m_Ctx.CurPipelineState().GetSamplerDescriptors(m_Stage); + ShaderBindIndex bind = reg->GetBindIndex(); int32_t bindIdx = samplers.indexOf(bind); if(bindIdx < 0) continue; - BoundResourceArray &res = samplers[bindIdx]; + Descriptor desc = samplers[bindIdx].descriptor; + const ShaderSampler &samp = m_ShaderDetails->samplers[bind.index]; - if(bind.arraySize == 1) + if(samp.bindArraySize == 1) { - if(!res.resources.empty()) - value = samplerRep(bind, ~0U, res.resources[0].resourceId); + value = samplerRep(samp, ~0U, desc.resource); } - else if(bind.arraySize == ~0U) + else if(samp.bindArraySize == ~0U) { typeName = lit("[unbounded]"); value = QString(); } else { - for(uint32_t a = 0; a < bind.arraySize; a++) + for(uint32_t a = 0; a < samp.bindArraySize; a++) children.push_back(new RDTreeWidgetItem({ QFormatStr("%1[%2]").arg(localName).arg(a), QString(), typeName, - samplerRep(bind, a, res.resources[a].resourceId), + samplerRep(samp, a, desc.resource), })); - childCount += bind.arraySize; + childCount += samp.bindArraySize; - typeName = QFormatStr("[%1]").arg(bind.arraySize); + typeName = QFormatStr("[%1]").arg(samp.bindArraySize); value = QString(); } } @@ -4661,49 +4642,48 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi typeName = isReadOnlyResource ? lit("Resource") : lit("RW Resource"); - rdcarray &resList = + const rdcarray &resList = isReadOnlyResource ? m_ReadOnlyResources : m_ReadWriteResources; - int32_t idx = - (isReadOnlyResource ? m_Mapping.readOnlyResources : m_Mapping.readWriteResources) - .indexOf(Bindpoint(reg->GetBinding())); + // find all descriptors in this bind's array + ShaderBindIndex bind = reg->GetBindIndex(); + bind.arrayElement = 0; - if(idx < 0) + rdcarray descriptors; + for(const UsedDescriptor &a : resList) + if(CategoryForDescriptorType(a.access.type) == bind.category && a.access.index == bind.index) + descriptors.push_back(a); + + if(descriptors.empty()) continue; - Bindpoint bind = isReadOnlyResource ? m_Mapping.readOnlyResources[idx] - : m_Mapping.readWriteResources[idx]; + const ShaderResource &res = isReadOnlyResource + ? m_ShaderDetails->readOnlyResources[bind.index] + : m_ShaderDetails->readWriteResources[bind.index]; - int32_t bindIdx = resList.indexOf(bind); - - if(bindIdx < 0) - continue; - - BoundResourceArray &res = resList[bindIdx]; - if(bind.arraySize == 1) + if(res.bindArraySize == 1) { - if(!res.resources.empty()) - value = ToQStr(res.resources[0].resourceId); + value = ToQStr(descriptors[0].descriptor.resource); } - else if(bind.arraySize == ~0U) + else if(res.bindArraySize == ~0U) { typeName = lit("[unbounded]"); value = QString(); } else { - uint32_t count = qMin(bind.arraySize, (uint32_t)res.resources.size()); + uint32_t count = qMin(res.bindArraySize, (uint32_t)descriptors.size()); for(uint32_t a = 0; a < count; a++) children.push_back(new RDTreeWidgetItem({ QFormatStr("%1[%2]").arg(localName).arg(a), QString(), typeName, - ToQStr(res.resources[a].resourceId), + ToQStr(descriptors[a].descriptor.resource), })); - childCount += bind.arraySize; + childCount += res.bindArraySize; - typeName = QFormatStr("[%1]").arg(bind.arraySize); + typeName = QFormatStr("[%1]").arg(res.bindArraySize); value = QString(); } } @@ -4844,46 +4824,26 @@ RDTreeWidgetItem *ShaderViewer::makeDebugVariableNode(const ShaderVariable &v, r RDTreeWidgetItem *ShaderViewer::makeAccessedResourceNode(const ShaderVariable &v) { - BindpointIndex bp = v.GetBinding(); + ShaderBindIndex bp = v.GetBindIndex(); ResourceId resId; QString typeName; if(v.type == VarType::ReadOnlyResource) { + if(bp.category != DescriptorCategory::ReadOnlyResource) + qCritical() << "Mismatch between variable type and descriptor category"; 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; - } - } - } + int32_t bindIdx = m_ReadOnlyResources.indexOf(bp); + 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"; 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; - } - } - } + int32_t bindIdx = m_ReadWriteResources.indexOf(bp); + if(bindIdx >= 0) + resId = m_ReadWriteResources[bindIdx].descriptor.resource; } RDTreeWidgetItem *node = new RDTreeWidgetItem({v.name, typeName, ToQStr(resId)}); diff --git a/qrenderdoc/Windows/ShaderViewer.h b/qrenderdoc/Windows/ShaderViewer.h index 888917b31..1c34b5449 100644 --- a/qrenderdoc/Windows/ShaderViewer.h +++ b/qrenderdoc/Windows/ShaderViewer.h @@ -129,20 +129,19 @@ public: return ret; } - static IShaderViewer *DebugShader(ICaptureContext &ctx, const ShaderBindpointMapping *bind, - const ShaderReflection *shader, ResourceId pipeline, - ShaderDebugTrace *trace, const QString &debugContext, - QWidget *parent) + static IShaderViewer *DebugShader(ICaptureContext &ctx, const ShaderReflection *shader, + ResourceId pipeline, ShaderDebugTrace *trace, + const QString &debugContext, QWidget *parent) { ShaderViewer *ret = new ShaderViewer(ctx, parent); - ret->debugShader(bind, shader, pipeline, trace, debugContext); + ret->debugShader(shader, pipeline, trace, debugContext); return ret; } static IShaderViewer *ViewShader(ICaptureContext &ctx, const ShaderReflection *shader, ResourceId pipeline, QWidget *parent) { - return DebugShader(ctx, NULL, shader, pipeline, NULL, QString(), parent); + return DebugShader(ctx, shader, pipeline, NULL, QString(), parent); } ~ShaderViewer(); @@ -210,8 +209,8 @@ private: void editShader(ResourceId id, ShaderStage stage, const QString &entryPoint, const rdcstrpairs &files, KnownShaderTool knownTool, ShaderEncoding shaderEncoding, ShaderCompileFlags flags); - void debugShader(const ShaderBindpointMapping *bind, const ShaderReflection *shader, - ResourceId pipeline, ShaderDebugTrace *trace, const QString &debugContext); + void debugShader(const ShaderReflection *shader, ResourceId pipeline, ShaderDebugTrace *trace, + const QString &debugContext); bool eventFilter(QObject *watched, QEvent *event) override; @@ -246,7 +245,6 @@ private: Ui::ShaderViewer *ui; ICaptureContext &m_Ctx; - ShaderBindpointMapping m_Mapping; const ShaderReflection *m_ShaderDetails = NULL; bool m_CustomShader = false; ResourceId m_EditingShader; @@ -321,8 +319,8 @@ private: rdcarray m_AccessedResources; AccessedResourceView m_AccessedResourceView = AccessedResourceView::SortByResource; - rdcarray m_ReadOnlyResources; - rdcarray m_ReadWriteResources; + rdcarray m_ReadOnlyResources; + rdcarray m_ReadWriteResources; QSet> m_Breakpoints; bool m_TempBreakpoint = false; @@ -408,13 +406,13 @@ private: 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 BindpointIndex &resource); + void runToResourceAccess(bool forward, VarType type, const ShaderBindIndex &resource); void applyBackwardsChange(); void applyForwardsChange(); QString stringRep(const ShaderVariable &var, uint32_t row = 0); - QString samplerRep(Bindpoint bind, uint32_t arrayIndex, ResourceId id); + QString samplerRep(const ShaderSampler &samp, uint32_t arrayElement, ResourceId id); void combineStructures(RDTreeWidgetItem *root, int skipPrefixLength = 0); void highlightMatchingVars(RDTreeWidgetItem *root, const QString varName, const QColor highlightColor); diff --git a/qrenderdoc/Windows/TextureViewer.cpp b/qrenderdoc/Windows/TextureViewer.cpp index 2db3da76d..578ce8827 100644 --- a/qrenderdoc/Windows/TextureViewer.cpp +++ b/qrenderdoc/Windows/TextureViewer.cpp @@ -4130,12 +4130,10 @@ void TextureViewer::on_debugPixelContext_clicked() const ShaderReflection *shaderDetails = m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Pixel); - const ShaderBindpointMapping &bindMapping = - m_Ctx.CurPipelineState().GetBindpointMapping(ShaderStage::Pixel); ResourceId pipeline = m_Ctx.CurPipelineState().GetGraphicsPipelineObject(); // viewer takes ownership of the trace - IShaderViewer *s = m_Ctx.DebugShader(&bindMapping, shaderDetails, pipeline, trace, debugContext); + IShaderViewer *s = m_Ctx.DebugShader(shaderDetails, pipeline, trace, debugContext); m_Ctx.AddDockWindow(s->Widget(), DockReference::AddTo, this); } diff --git a/renderdoc/api/replay/common_pipestate.h b/renderdoc/api/replay/common_pipestate.h index 9143af811..cc96521e3 100644 --- a/renderdoc/api/replay/common_pipestate.h +++ b/renderdoc/api/replay/common_pipestate.h @@ -765,6 +765,11 @@ struct DescriptorAccess DescriptorAccess(const DescriptorAccess &) = default; DescriptorAccess &operator=(const DescriptorAccess &) = default; + bool operator==(const ShaderBindIndex &o) const + { + return CategoryForDescriptorType(type) == o.category && index == o.index && + arrayElement == o.arrayElement; + } bool operator==(const DescriptorAccess &o) const { return stage == o.stage && type == o.type && index == o.index && @@ -849,6 +854,11 @@ does not necessarily guarantee that the descriptor was accessed on the GPU durin DECLARE_REFLECTION_STRUCT(DescriptorAccess); +inline ShaderBindIndex::ShaderBindIndex(const DescriptorAccess &access) + : ShaderBindIndex(CategoryForDescriptorType(access.type), access.index, access.arrayElement) +{ +} + DOCUMENT(R"(In many cases there may be a logical location or fixed binding point for a particular descriptor which is not conveyed with a simple byte offset into a descriptor store. This is particularly true for any descriptor stores that are not equivalent to a buffer of bytes @@ -967,6 +977,8 @@ struct UsedDescriptor UsedDescriptor(const UsedDescriptor &) = default; UsedDescriptor &operator=(const UsedDescriptor &) = default; + bool operator==(const DescriptorAccess &o) const { return access == o; } + bool operator==(const ShaderBindIndex &o) const { return access == o; } bool operator==(const UsedDescriptor &o) const { return access == o.access && descriptor == o.descriptor && sampler == o.sampler; diff --git a/renderdoc/api/replay/shader_types.h b/renderdoc/api/replay/shader_types.h index b742061fc..49da7e6db 100644 --- a/renderdoc/api/replay/shader_types.h +++ b/renderdoc/api/replay/shader_types.h @@ -109,6 +109,81 @@ struct BindpointIndex DECLARE_REFLECTION_STRUCT(BindpointIndex); +struct DescriptorAccess; + +DOCUMENT(R"(References a particular individual binding element in a shader interface. + +This is the shader interface side of a :class:`DescriptorAccess` and so can be compared to one to +check if an access refers to a given index or not. + +The context of which shader reflection this index refers to must be provided to properly interpret +this information, as it is relative to a particular :class:`ShaderReflection`. +)"); +struct ShaderBindIndex +{ + DOCUMENT(""); + ShaderBindIndex() + { + category = DescriptorCategory::Unknown; + index = 0; + arrayElement = 0; + } + ShaderBindIndex(const ShaderBindIndex &) = default; + ShaderBindIndex &operator=(const ShaderBindIndex &) = default; + + ShaderBindIndex(DescriptorCategory category, uint32_t index, uint32_t arrayElement) + : category(category), index(index), arrayElement(arrayElement) + { + } + ShaderBindIndex(DescriptorCategory category, uint32_t index) : ShaderBindIndex(category, index, 0) + { + } + ShaderBindIndex(const DescriptorAccess &access); + + bool operator<(const ShaderBindIndex &o) const + { + if(!(category == o.category)) + return category < o.category; + if(!(index == o.index)) + return index < o.index; + return arrayElement < o.arrayElement; + } + bool operator>(const ShaderBindIndex &o) const + { + if(!(category == o.category)) + return category > o.category; + if(!(index == o.index)) + return index > o.index; + return arrayElement > o.arrayElement; + } + bool operator==(const ShaderBindIndex &o) const + { + return category == o.category && index == o.index && arrayElement == o.arrayElement; + } + + DOCUMENT(R"(The type of binding this refers to, with each category referring to a different +shader interface in the :data:`ShaderReflection`. + +:type: DescriptorCategory +)"); + DescriptorCategory category; + + DOCUMENT(R"(The index within the given :data:`category` for the binding. + +:type: int +)"); + uint32_t index; + + DOCUMENT(R"(If the binding identified by :data:`category` and :data:`index` is arrayed, this +identifies the particular array index being referred to. + +:type: int +)"); + uint32_t arrayElement; +}; + +DECLARE_REFLECTION_STRUCT(ShaderBindIndex); + #if !defined(SWIG) // similarly these need to be pre-declared for use in rdhalf extern "C" RENDERDOC_API float RENDERDOC_CC RENDERDOC_HalfToFloat(uint16_t half); @@ -415,6 +490,35 @@ binding is given by the :data:`type` member. { return BindpointIndex(value.s32v[0], value.s32v[1], value.u32v[2]); } + + DOCUMENT(R"(Utility function for setting a reference to a shader binding. + +The :class:`ShaderBindIndex` uniquely refers to a given shader binding in one of the shader interfaces +(constant blocks, samplers, read-only and read-write resources) and if necessary the element within +an arrayed binding. + +:param ShaderBindIndex idx: The index of the bind being referred to. +)"); + inline void SetBindIndex(const ShaderBindIndex &idx) + { + value.u32v[0] = (uint32_t)idx.category; + value.u32v[1] = idx.index; + value.u32v[2] = idx.arrayElement; + } + + DOCUMENT(R"(Utility function for getting a shader binding referenced by this variable. + +.. note:: + + The return value is undefined if this variable is not a binding reference. + +:return: A :class:`ShaderBindIndex` with the binding referenced. +:rtype: ShaderBindIndex +)"); + inline ShaderBindIndex GetBindIndex() const + { + return ShaderBindIndex((DescriptorCategory)value.u32v[0], value.u32v[1], value.u32v[2]); + } }; DECLARE_REFLECTION_STRUCT(ShaderVariable); diff --git a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp index d3681ddef..685d5084b 100644 --- a/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp +++ b/renderdoc/driver/d3d11/d3d11_shaderdebug.cpp @@ -1420,8 +1420,7 @@ bool D3D11DebugAPIWrapper::CalculateMathIntrinsic(DXBCBytecode::OpcodeType opcod void AddCBuffersToGlobalState(const DXBCBytecode::Program &program, D3D11DebugManager &debugManager, DXBCDebug::GlobalState &global, rdcarray &sourceVars, - const D3D11RenderState::Shader &shader, const ShaderReflection &refl, - const ShaderBindpointMapping &mapping) + const D3D11RenderState::Shader &shader, const ShaderReflection &refl) { bytebuf cbufData; for(int i = 0; i < D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT; i++) @@ -1433,7 +1432,7 @@ void AddCBuffersToGlobalState(const DXBCBytecode::Program &program, D3D11DebugMa debugManager.GetBufferData(shader.ConstantBuffers[i], shader.CBOffsets[i] * sizeof(Vec4f), shader.CBCounts[i] * sizeof(Vec4f), cbufData); - AddCBufferToGlobalState(program, global, sourceVars, refl, mapping, slot, cbufData); + AddCBufferToGlobalState(program, global, sourceVars, refl, slot, cbufData); } } } @@ -1533,12 +1532,12 @@ ShaderDebugTrace *D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, ui InterpretDebugger *interpreter = new InterpretDebugger; interpreter->eventId = eventId; - ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, vs->GetMapping(), 0); + ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, 0); GlobalState &global = interpreter->global; ThreadState &state = interpreter->activeLane(); AddCBuffersToGlobalState(*dxbc->GetDXBCByteCode(), *GetDebugManager(), global, ret->sourceVars, - rs->VS, refl, vs->GetMapping()); + rs->VS, refl); for(size_t i = 0; i < state.inputs.size(); i++) { @@ -2451,12 +2450,12 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, InterpretDebugger *interpreter = new InterpretDebugger; interpreter->eventId = eventId; - ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, ps->GetMapping(), destIdx); + ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, destIdx); GlobalState &global = interpreter->global; ThreadState &state = interpreter->activeLane(); AddCBuffersToGlobalState(*dxbc->GetDXBCByteCode(), *GetDebugManager(), global, ret->sourceVars, - rs->PS, refl, ps->GetMapping()); + rs->PS, refl); global.sampleEvalRegisterMask = sampleEvalRegisterMask; @@ -2606,12 +2605,12 @@ ShaderDebugTrace *D3D11Replay::DebugThread(uint32_t eventId, InterpretDebugger *interpreter = new InterpretDebugger; interpreter->eventId = eventId; - ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, cs->GetMapping(), 0); + ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, 0); GlobalState &global = interpreter->global; ThreadState &state = interpreter->activeLane(); AddCBuffersToGlobalState(*dxbc->GetDXBCByteCode(), *GetDebugManager(), global, ret->sourceVars, - rs->CS, refl, cs->GetMapping()); + rs->CS, refl); for(int i = 0; i < 3; i++) { diff --git a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp index 685ffcf83..19e6f91a0 100644 --- a/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp +++ b/renderdoc/driver/d3d12/d3d12_shaderdebug.cpp @@ -1402,7 +1402,7 @@ void GatherConstantBuffers(WrappedID3D12Device *pDevice, const DXBCBytecode::Pro UINT sizeBytes = sizeof(uint32_t) * RDCMIN(rootSigParam.Constants.Num32BitValues, (UINT)element.constants.size()); bytebuf cbufData((const byte *)element.constants.data(), sizeBytes); - AddCBufferToGlobalState(program, global, sourceVars, refl, mapping, slot, cbufData); + AddCBufferToGlobalState(program, global, sourceVars, refl, slot, cbufData); } else if(rootSigParam.ParameterType == D3D12_ROOT_PARAMETER_TYPE_CBV && element.type == eRootCBV) { @@ -1411,7 +1411,7 @@ void GatherConstantBuffers(WrappedID3D12Device *pDevice, const DXBCBytecode::Pro ID3D12Resource *cbv = pDevice->GetResourceManager()->GetCurrentAs(element.id); bytebuf cbufData; pDevice->GetDebugManager()->GetBufferData(cbv, element.offset, 0, cbufData); - AddCBufferToGlobalState(program, global, sourceVars, refl, mapping, slot, cbufData); + AddCBufferToGlobalState(program, global, sourceVars, refl, slot, cbufData); } else if(rootSigParam.ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE && element.type == eRootTable) @@ -1467,7 +1467,7 @@ void GatherConstantBuffers(WrappedID3D12Device *pDevice, const DXBCBytecode::Pro if(cbv.SizeInBytes > 0) pDevice->GetDebugManager()->GetBufferData(pCbvResource, byteOffset, cbv.SizeInBytes, cbufData); - AddCBufferToGlobalState(program, global, sourceVars, refl, mapping, slot, cbufData); + AddCBufferToGlobalState(program, global, sourceVars, refl, slot, cbufData); desc++; } @@ -1593,7 +1593,7 @@ ShaderDebugTrace *D3D12Replay::DebugVertex(uint32_t eventId, uint32_t vertid, ui InterpretDebugger *interpreter = new InterpretDebugger; interpreter->eventId = eventId; - ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, vs->GetMapping(), 0); + ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, 0); GlobalState &global = interpreter->global; ThreadState &state = interpreter->activeLane(); @@ -2570,7 +2570,7 @@ void ExtractInputsPS(PSInput IN, InterpretDebugger *interpreter = new InterpretDebugger; interpreter->eventId = eventId; - ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, origPSO->PS()->GetMapping(), destIdx); + ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, destIdx); GlobalState &global = interpreter->global; ThreadState &state = interpreter->activeLane(); @@ -2730,7 +2730,7 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId, InterpretDebugger *interpreter = new InterpretDebugger; interpreter->eventId = eventId; - ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, pso->CS()->GetMapping(), 0); + ShaderDebugTrace *ret = interpreter->BeginDebug(dxbc, refl, 0); GlobalState &global = interpreter->global; ThreadState &state = interpreter->activeLane(); diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp index fd0c15b85..9d5e63c4e 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp @@ -1892,7 +1892,7 @@ void ThreadState::MarkResourceAccess(ShaderDebugState *state, DXBCBytecode::Oper const rdcarray &shaderBinds = (type == DXBCBytecode::TYPE_RESOURCE) ? reflection->SRVs : reflection->UAVs; - for(size_t i = 0; i < shaderBinds.size(); ++i) + for(uint32_t i = 0; i < shaderBinds.size(); ++i) { const DXBC::ShaderInputBind &bind = shaderBinds[i]; if(bind.space == slot.registerSpace && bind.reg <= slot.shaderRegister && @@ -1912,15 +1912,19 @@ void ThreadState::MarkResourceAccess(ShaderDebugState *state, DXBCBytecode::Oper else change.after.name = StringFormat::Fmt("%c%u[%u]", prefix, resIdx, arrIdx); + change.after.SetBindIndex(ShaderBindIndex((type == DXBCBytecode::TYPE_RESOURCE) + ? DescriptorCategory::ReadOnlyResource + : DescriptorCategory::ReadWriteResource, + i, 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 = + ShaderBindIndex bp = change.after.GetBindIndex(); + rdcarray &accessed = (type == DXBCBytecode::TYPE_RESOURCE) ? m_accessedSRVs : m_accessedUAVs; for(size_t i = 0; i < accessed.size(); ++i) { @@ -4603,22 +4607,22 @@ uint32_t GetLogicalIdentifierForBindingSlot(const DXBCBytecode::Program &program void AddCBufferToGlobalState(const DXBCBytecode::Program &program, GlobalState &global, rdcarray &sourceVars, - const ShaderReflection &refl, const ShaderBindpointMapping &mapping, - const BindingSlot &slot, bytebuf &cbufData) + const ShaderReflection &refl, const BindingSlot &slot, bytebuf &cbufData) { // Find the identifier - size_t numCBs = mapping.constantBlocks.size(); + size_t numCBs = refl.constantBlocks.size(); for(size_t i = 0; i < numCBs; ++i) { - const Bindpoint &bp = mapping.constantBlocks[i]; - if(slot.registerSpace == (uint32_t)bp.bindset && slot.shaderRegister >= (uint32_t)bp.bind && - slot.shaderRegister < (uint32_t)(bp.bind + bp.arraySize)) + const ConstantBlock &cb = refl.constantBlocks[i]; + if(slot.registerSpace == (uint32_t)cb.fixedBindSetOrSpace && + slot.shaderRegister >= (uint32_t)cb.fixedBindNumber && + slot.shaderRegister < (uint32_t)(cb.fixedBindNumber + cb.bindArraySize)) { - uint32_t arrayIndex = slot.shaderRegister - bp.bind; + uint32_t arrayIndex = slot.shaderRegister - cb.fixedBindNumber; rdcarray &targetVars = - bp.arraySize > 1 ? global.constantBlocks[i].members[arrayIndex].members - : global.constantBlocks[i].members; + cb.bindArraySize > 1 ? global.constantBlocks[i].members[arrayIndex].members + : global.constantBlocks[i].members; RDCASSERTMSG("Reassigning previously filled cbuffer", targetVars.empty()); uint32_t cbufferIndex = @@ -4635,7 +4639,7 @@ void AddCBufferToGlobalState(const DXBCBytecode::Program &program, GlobalState & rdcstr identifierPrefix = global.constantBlocks[i].name; rdcstr variablePrefix = refl.constantBlocks[i].name; - if(bp.arraySize > 1) + if(cb.bindArraySize > 1) { identifierPrefix = StringFormat::Fmt("%s[%u]", global.constantBlocks[i].name.c_str(), arrayIndex); @@ -4652,8 +4656,8 @@ void AddCBufferToGlobalState(const DXBCBytecode::Program &program, GlobalState & sourceVars.push_back(cbArrayMapping); } const rdcarray &constants = - (bp.arraySize > 1) ? refl.constantBlocks[i].variables[0].type.members - : refl.constantBlocks[i].variables; + (cb.bindArraySize > 1) ? refl.constantBlocks[i].variables[0].type.members + : refl.constantBlocks[i].variables; rdcarray vars; StandardFillCBufferVariables(refl.resourceId, constants, vars, cbufData); @@ -5230,9 +5234,7 @@ void GatherPSInputDataForInitialValues(const DXBC::DXBCContainer *dxbc, } ShaderDebugTrace *InterpretDebugger::BeginDebug(const DXBC::DXBCContainer *dxbcContainer, - const ShaderReflection &refl, - const ShaderBindpointMapping &mapping, - int activeIndex) + const ShaderReflection &refl, int activeIndex) { ShaderDebugTrace *ret = new ShaderDebugTrace; ret->debugger = this; @@ -5479,7 +5481,7 @@ ShaderDebugTrace *InterpretDebugger::BeginDebug(const DXBC::DXBCContainer *dxbcC { VarType varType; DebugVariableType debugVarType; - const rdcarray &binds; + DescriptorCategory category; const rdcarray &resources; const char *regChars; rdcarray &dst; @@ -5489,7 +5491,7 @@ ShaderDebugTrace *InterpretDebugger::BeginDebug(const DXBC::DXBCContainer *dxbcC { VarType::ReadOnlyResource, DebugVariableType::ReadOnlyResource, - mapping.readOnlyResources, + DescriptorCategory::ReadOnlyResource, refl.readOnlyResources, "tT", ret->readOnlyResources, @@ -5497,7 +5499,7 @@ ShaderDebugTrace *InterpretDebugger::BeginDebug(const DXBC::DXBCContainer *dxbcC { VarType::ReadWriteResource, DebugVariableType::ReadWriteResource, - mapping.readWriteResources, + DescriptorCategory::ReadWriteResource, refl.readWriteResources, "uU", ret->readWriteResources, @@ -5507,27 +5509,23 @@ ShaderDebugTrace *InterpretDebugger::BeginDebug(const DXBC::DXBCContainer *dxbcC for(ResList &list : lists) { // add the registers for the resources that are used - list.dst.reserve(list.binds.size()); - for(size_t i = 0; i < list.binds.size(); i++) + list.dst.reserve(list.resources.size()); + for(uint32_t i = 0; i < list.resources.size(); i++) { - const Bindpoint &b = list.binds[i]; const ShaderResource &r = list.resources[i]; - if(!b.used) - continue; - rdcstr identifier; if(dxbc->GetDXBCByteCode()->IsShaderModel51()) identifier = StringFormat::Fmt("%c%zu", list.regChars[1], i); else - identifier = StringFormat::Fmt("%c%u", list.regChars[0], b.bind); + identifier = StringFormat::Fmt("%c%u", list.regChars[0], r.fixedBindNumber); ShaderVariable reg(identifier, 0U, 0U, 0U, 0U); reg.rows = 1; reg.columns = 1; - reg.SetBinding(b.bindset, b.bind, 0U); + reg.SetBindIndex(ShaderBindIndex(list.category, i, 0)); SourceVariableMapping sourcemap; sourcemap.name = r.name; @@ -5545,27 +5543,23 @@ ShaderDebugTrace *InterpretDebugger::BeginDebug(const DXBC::DXBCContainer *dxbcC } } - ret->samplers.reserve(mapping.samplers.size()); - for(size_t i = 0; i < mapping.samplers.size(); i++) + ret->samplers.reserve(refl.samplers.size()); + for(uint32_t i = 0; i < refl.samplers.size(); i++) { - const Bindpoint &b = mapping.samplers[i]; const ShaderSampler &s = refl.samplers[i]; - if(!b.used) - continue; - rdcstr identifier; if(dxbc->GetDXBCByteCode()->IsShaderModel51()) identifier = StringFormat::Fmt("S%zu", i); else - identifier = StringFormat::Fmt("s%u", b.bind); + identifier = StringFormat::Fmt("s%u", s.fixedBindNumber); ShaderVariable reg(identifier, 0U, 0U, 0U, 0U); reg.rows = 1; reg.columns = 1; - reg.SetBinding(b.bindset, b.bind, 0U); + reg.SetBindIndex(ShaderBindIndex(DescriptorCategory::Sampler, i, 0)); SourceVariableMapping sourcemap; sourcemap.name = s.name; diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.h b/renderdoc/driver/shaders/dxbc/dxbc_debug.h index 756d8b5a1..b44e0ccc9 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.h @@ -326,14 +326,14 @@ private: const DXBCBytecode::Program *program; const DXBC::IDebugInfo *debug; - rdcarray m_accessedSRVs; - rdcarray m_accessedUAVs; + rdcarray m_accessedSRVs; + rdcarray m_accessedUAVs; }; struct InterpretDebugger : public ShaderDebugger { - ShaderDebugTrace *BeginDebug(const DXBC::DXBCContainer *dxbcContainer, const ShaderReflection &refl, - const ShaderBindpointMapping &mapping, int activeIndex); + ShaderDebugTrace *BeginDebug(const DXBC::DXBCContainer *dxbcContainer, + const ShaderReflection &refl, int activeIndex); GlobalState global; uint32_t eventId; @@ -361,7 +361,7 @@ void ApplyAllDerivatives(GlobalState &global, rdcarray &quad, int d void AddCBufferToGlobalState(const DXBCBytecode::Program &program, GlobalState &global, rdcarray &sourceVars, - const ShaderReflection &refl, const ShaderBindpointMapping &mapping, - const BindingSlot &slot, bytebuf &cbufData); + const ShaderReflection &refl, const BindingSlot &slot, + bytebuf &cbufData); }; // namespace ShaderDebug diff --git a/renderdoc/driver/shaders/spirv/spirv_debug.cpp b/renderdoc/driver/shaders/spirv/spirv_debug.cpp index b1350aee7..232974be5 100644 --- a/renderdoc/driver/shaders/spirv/spirv_debug.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_debug.cpp @@ -77,9 +77,6 @@ inline uint64_t CountOnes(uint64_t value) namespace rdcspv { -const BindpointIndex DebugAPIWrapper::invalidBind = BindpointIndex(-12345, -12345, ~0U); -const BindpointIndex DebugAPIWrapper::pointerBind = BindpointIndex(-12345, -67890, ~0U); - ThreadState::ThreadState(uint32_t workgroupIdx, Debugger &debug, const GlobalState &globalState) : debugger(debug), global(globalState) { @@ -749,7 +746,7 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray ShaderVariable result; result.rows = result.columns = 1; - BindpointIndex bind = debugger.GetPointerValue(structPointer).GetBinding(); + ShaderBindIndex bind = debugger.GetPointerValue(structPointer).GetBindIndex(); uint64_t byteLen = debugger.GetAPIWrapper()->GetBufferLength(bind) - offset; @@ -2783,12 +2780,12 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray result.type = resultType.scalar().Type(); - BindpointIndex samplerIndex = DebugAPIWrapper::invalidBind; + ShaderBindIndex samplerIndex; if(sampler.type == VarType::Sampler || sampler.type == VarType::ReadOnlyResource) - samplerIndex = sampler.GetBinding(); + samplerIndex = sampler.GetBindIndex(); if(!debugger.GetAPIWrapper()->CalculateSampleGather( - *this, opdata.op, texType, img.GetBinding(), samplerIndex, uv, ddxCalc, ddyCalc, + *this, opdata.op, texType, img.GetBindIndex(), samplerIndex, uv, ddxCalc, ddyCalc, compare, gather, operands, result)) { // sample failed. Pretend we got 0 columns back @@ -2834,8 +2831,8 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray // on the CPU for no reason (since we can't write to it) if(!debugger.GetAPIWrapper()->CalculateSampleGather( - *this, Op::ImageFetch, texType, img.GetBinding(), DebugAPIWrapper::invalidBind, - coord, ShaderVariable(), ShaderVariable(), ShaderVariable(), GatherChannel::Red, + *this, Op::ImageFetch, texType, img.GetBindIndex(), ShaderBindIndex(), coord, + ShaderVariable(), ShaderVariable(), ShaderVariable(), GatherChannel::Red, ImageOperandsAndParamDatas(), result)) { // sample failed. Pretend we got 0 columns back @@ -2844,7 +2841,7 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray } else { - if(!debugger.GetAPIWrapper()->ReadTexel(img.GetBinding(), coord, + if(!debugger.GetAPIWrapper()->ReadTexel(img.GetBindIndex(), coord, read.imageOperands.flags & ImageOperands::Sample ? uintComp(GetSrc(read.imageOperands.sample), 0) : 0, @@ -2869,7 +2866,7 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray // only the sample operand should be here RDCASSERT((write.imageOperands.flags & ImageOperands::Sample) == write.imageOperands.flags); - debugger.GetAPIWrapper()->WriteTexel(img.GetBinding(), coord, + debugger.GetAPIWrapper()->WriteTexel(img.GetBindIndex(), coord, write.imageOperands.flags & ImageOperands::Sample ? uintComp(GetSrc(write.imageOperands.sample), 0) : 0, @@ -3171,7 +3168,7 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray result.rows = result.columns = 1; result.type = resultType.scalar().Type(); - if(!debugger.GetAPIWrapper()->ReadTexel(ptr.members[0].GetBinding(), ptr.members[1], + if(!debugger.GetAPIWrapper()->ReadTexel(ptr.members[0].GetBindIndex(), ptr.members[1], uintComp(ptr.members[2], 0), result)) { // sample failed. Pretend we got 0 columns back @@ -3199,7 +3196,7 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray } else { - debugger.GetAPIWrapper()->WriteTexel(ptr.members[0].GetBinding(), ptr.members[1], + debugger.GetAPIWrapper()->WriteTexel(ptr.members[0].GetBindIndex(), ptr.members[1], uintComp(ptr.members[2], 0), value); } @@ -3229,14 +3226,14 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray result.rows = result.columns = 1; result.type = resultType.scalar().Type(); - if(!debugger.GetAPIWrapper()->ReadTexel(ptr.members[0].GetBinding(), ptr.members[1], + if(!debugger.GetAPIWrapper()->ReadTexel(ptr.members[0].GetBindIndex(), ptr.members[1], uintComp(ptr.members[2], 0), result)) { // sample failed. Pretend we got 0 columns back RDCEraseEl(result.value); } - debugger.GetAPIWrapper()->WriteTexel(ptr.members[0].GetBinding(), ptr.members[1], + debugger.GetAPIWrapper()->WriteTexel(ptr.members[0].GetBindIndex(), ptr.members[1], uintComp(ptr.members[2], 0), value); } @@ -3269,7 +3266,7 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray result.rows = result.columns = 1; result.type = resultType.scalar().Type(); - if(!debugger.GetAPIWrapper()->ReadTexel(ptr.members[0].GetBinding(), ptr.members[1], + if(!debugger.GetAPIWrapper()->ReadTexel(ptr.members[0].GetBindIndex(), ptr.members[1], uintComp(ptr.members[2], 0), result)) { // sample failed. Pretend we got 0 columns back @@ -3300,7 +3297,7 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray } else { - debugger.GetAPIWrapper()->WriteTexel(ptr.members[0].GetBinding(), ptr.members[1], + debugger.GetAPIWrapper()->WriteTexel(ptr.members[0].GetBindIndex(), ptr.members[1], uintComp(ptr.members[2], 0), value); } } @@ -3329,7 +3326,7 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray result.rows = result.columns = 1; result.type = resultType.scalar().Type(); - if(!debugger.GetAPIWrapper()->ReadTexel(ptr.members[0].GetBinding(), ptr.members[1], + if(!debugger.GetAPIWrapper()->ReadTexel(ptr.members[0].GetBindIndex(), ptr.members[1], uintComp(ptr.members[2], 0), result)) { // sample failed. Pretend we got 0 columns back @@ -3357,7 +3354,7 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray } else { - debugger.GetAPIWrapper()->WriteTexel(ptr.members[0].GetBinding(), ptr.members[1], + debugger.GetAPIWrapper()->WriteTexel(ptr.members[0].GetBindIndex(), ptr.members[1], uintComp(ptr.members[2], 0), result); } break; @@ -3396,7 +3393,7 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray result.rows = result.columns = 1; result.type = resultType.scalar().Type(); - if(!debugger.GetAPIWrapper()->ReadTexel(ptr.members[0].GetBinding(), ptr.members[1], + if(!debugger.GetAPIWrapper()->ReadTexel(ptr.members[0].GetBindIndex(), ptr.members[1], uintComp(ptr.members[2], 0), result)) { // sample failed. Pretend we got 0 columns back @@ -3495,7 +3492,7 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray } else { - debugger.GetAPIWrapper()->WriteTexel(ptr.members[0].GetBinding(), ptr.members[1], + debugger.GetAPIWrapper()->WriteTexel(ptr.members[0].GetBindIndex(), ptr.members[1], uintComp(ptr.members[2], 0), result); } break; diff --git a/renderdoc/driver/shaders/spirv/spirv_debug.h b/renderdoc/driver/shaders/spirv/spirv_debug.h index a2ac466bc..10d67a9bb 100644 --- a/renderdoc/driver/shaders/spirv/spirv_debug.h +++ b/renderdoc/driver/shaders/spirv/spirv_debug.h @@ -54,18 +54,19 @@ public: virtual ResourceId GetShaderID() = 0; - virtual uint64_t GetBufferLength(BindpointIndex bind) = 0; + virtual uint64_t GetBufferLength(ShaderBindIndex bind) = 0; - virtual void ReadBufferValue(BindpointIndex bind, uint64_t offset, uint64_t byteSize, void *dst) = 0; - virtual void WriteBufferValue(BindpointIndex bind, uint64_t offset, uint64_t byteSize, + virtual void ReadBufferValue(ShaderBindIndex bind, uint64_t offset, uint64_t byteSize, + void *dst) = 0; + virtual void WriteBufferValue(ShaderBindIndex bind, uint64_t offset, uint64_t byteSize, const void *src) = 0; virtual void ReadAddress(uint64_t address, uint64_t byteSize, void *dst) = 0; virtual void WriteAddress(uint64_t address, uint64_t byteSize, const void *src) = 0; - virtual bool ReadTexel(BindpointIndex imageBind, const ShaderVariable &coord, uint32_t sample, + virtual bool ReadTexel(ShaderBindIndex imageBind, const ShaderVariable &coord, uint32_t sample, ShaderVariable &output) = 0; - virtual bool WriteTexel(BindpointIndex imageBind, const ShaderVariable &coord, uint32_t sample, + virtual bool WriteTexel(ShaderBindIndex imageBind, const ShaderVariable &coord, uint32_t sample, const ShaderVariable &value) = 0; virtual void FillInputValue(ShaderVariable &var, ShaderBuiltin builtin, uint32_t location, @@ -82,11 +83,8 @@ public: Subpass_Texture = 0x20, }; - static const BindpointIndex invalidBind; - static const BindpointIndex pointerBind; - virtual bool CalculateSampleGather(ThreadState &lane, Op opcode, TextureType texType, - BindpointIndex imageBind, BindpointIndex samplerBind, + ShaderBindIndex imageBind, ShaderBindIndex samplerBind, const ShaderVariable &uv, const ShaderVariable &ddxCalc, const ShaderVariable &ddyCalc, const ShaderVariable &compare, GatherChannel gatherChannel, diff --git a/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp b/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp index 5aa9b00d2..00965821d 100644 --- a/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp @@ -1106,7 +1106,9 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s var.columns = 1; var.type = VarType::ReadWriteResource; - var.SetBinding((int32_t)bindset, (int32_t)bind, 0U); + int32_t idx = patchData.rwInterface.indexOf(v.id); + RDCASSERT(idx >= 0); + var.SetBindIndex(ShaderBindIndex(DescriptorCategory::ReadWriteResource, idx, 0U)); enablePointerFlags(var, PointerFlags::SSBO); @@ -1124,12 +1126,13 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s } else { - BindpointIndex bindpoint; + ShaderBindIndex binding; - bindpoint.bindset = (int32_t)bindset; - bindpoint.bind = (int32_t)bind; + binding.category = DescriptorCategory::ConstantBlock; + binding.index = patchData.cblockInterface.indexOf(v.id); + RDCASSERT(binding.index != ~0U); - auto cbufferCallback = [this, &bindpoint]( + auto cbufferCallback = [this, &binding]( ShaderVariable &var, const Decorations &curDecorations, const DataType &type, uint64_t offset, const rdcstr &) { if(!var.members.empty()) @@ -1138,7 +1141,7 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s // non-matrix case is simple, just read the size of the variable if(var.rows == 1) { - this->apiWrapper->ReadBufferValue(bindpoint, offset, VarByteSize(var), + this->apiWrapper->ReadBufferValue(binding, offset, VarByteSize(var), var.value.u8v.data()); if(type.type == DataType::PointerType) @@ -1166,7 +1169,7 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s for(uint32_t c = 0; c < var.columns; c++) { // read the column - this->apiWrapper->ReadBufferValue(bindpoint, offset + c * matrixStride, colSize, + this->apiWrapper->ReadBufferValue(binding, offset + c * matrixStride, colSize, VarElemPointer(tmp, 0)); // now write it into the appropiate elements in the destination ShaderValue @@ -1182,7 +1185,7 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s { // read the column into the destination ShaderValue, which is tightly packed with // rows - this->apiWrapper->ReadBufferValue(bindpoint, offset + r * matrixStride, rowSize, + this->apiWrapper->ReadBufferValue(binding, offset + r * matrixStride, rowSize, VarElemPointer(var, r * var.columns)); } } @@ -1201,7 +1204,7 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s for(uint32_t a = 0; a < arraySize; a++) { - bindpoint.arrayIndex = a; + binding.arrayElement = a; var.members.push_back(ShaderVariable()); var.members.back().name = StringFormat::Fmt("[%u]", a); WalkVariable(decorations[v.id], *innertype, 0U, @@ -1270,8 +1273,6 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s if(decorations[v.id].flags & Decorations::HasBinding) bind = decorations[v.id].binding; - var.SetBinding((int32_t)set, (int32_t)bind, 0U); - if(innertype->type == DataType::ArrayType) { enablePointerFlags(var, PointerFlags::GlobalArrayBinding); @@ -1283,6 +1284,10 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s var.type = VarType::Sampler; debugType = DebugVariableType::Sampler; + int32_t idx = patchData.samplerInterface.indexOf(v.id); + RDCASSERT(idx >= 0); + var.SetBindIndex(ShaderBindIndex(DescriptorCategory::Sampler, idx, 0U)); + global.samplers.push_back(var); pointerIDs.push_back(GLOBAL_POINTER(v.id, samplers)); } @@ -1323,11 +1328,19 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s var.type = VarType::ReadWriteResource; debugType = DebugVariableType::ReadWriteResource; + int32_t idx = patchData.rwInterface.indexOf(v.id); + RDCASSERT(idx >= 0); + var.SetBindIndex(ShaderBindIndex(DescriptorCategory::ReadWriteResource, idx, 0U)); + global.readWriteResources.push_back(var); pointerIDs.push_back(GLOBAL_POINTER(v.id, readWriteResources)); } else { + int32_t idx = patchData.roInterface.indexOf(v.id); + RDCASSERT(idx >= 0); + var.SetBindIndex(ShaderBindIndex(DescriptorCategory::ReadOnlyResource, idx, 0U)); + global.readOnlyResources.push_back(var); pointerIDs.push_back(GLOBAL_POINTER(v.id, readOnlyResources)); } @@ -2368,8 +2381,9 @@ ShaderVariable Debugger::GetPointerValue(const ShaderVariable &ptr) const ShaderVariable ret = *inner; ret.name = ptr.name; // inherit any array index from the pointer - BindpointIndex bind = ret.GetBinding(); - ret.SetBinding(bind.bindset, bind.bind, getBindArrayIndex(ptr)); + ShaderBindIndex bind = ret.GetBindIndex(); + bind.arrayElement = getBindArrayIndex(ptr); + ret.SetBindIndex(bind); return ret; } // physical pointers which haven't been dereferenced are returned as-is, they're ready for display @@ -2392,7 +2406,7 @@ ShaderVariable Debugger::ReadFromPointer(const ShaderVariable &ptr) const rdcspv::Id typeId; Decorations parentDecorations; uint64_t baseAddress; - BindpointIndex bind; + ShaderBindIndex bind; uint64_t byteOffset = 0; std::function pointerReadCallback; if(IsPhysicalPointer(ptr)) @@ -2429,8 +2443,8 @@ ShaderVariable Debugger::ReadFromPointer(const ShaderVariable &ptr) const { typeId = getBufferTypeId(ptr); byteOffset = getByteOffset(ptr); - bind = inner->GetBinding(); - bind.arrayIndex = getBindArrayIndex(ptr); + bind = inner->GetBindIndex(); + bind.arrayElement = getBindArrayIndex(ptr); uint32_t varMatrixStride = getMatrixStride(ptr); if(varMatrixStride != 0) { @@ -2547,9 +2561,9 @@ ShaderVariable Debugger::ReadFromPointer(const ShaderVariable &ptr) const if(inner->type == VarType::ReadOnlyResource || inner->type == VarType::ReadWriteResource || inner->type == VarType::Sampler) { - bind = ret.GetBinding(); - - ret.SetBinding(bind.bindset, bind.bind, getBindArrayIndex(ptr)); + bind = ret.GetBindIndex(); + bind.arrayElement = getBindArrayIndex(ptr); + ret.SetBindIndex(bind); } // we don't support pointers to scalars since our 'unit' of pointer is a ShaderVariable, so check @@ -2657,7 +2671,7 @@ void Debugger::WriteThroughPointer(ShaderVariable &ptr, const ShaderVariable &va rdcspv::Id typeId; Decorations parentDecorations; uint64_t baseAddress; - BindpointIndex bind; + ShaderBindIndex bind; uint64_t byteOffset = 0; std::function pointerWriteCallback; @@ -2693,8 +2707,8 @@ void Debugger::WriteThroughPointer(ShaderVariable &ptr, const ShaderVariable &va { typeId = getBufferTypeId(ptr); byteOffset = getByteOffset(ptr); - bind = inner->GetBinding(); - bind.arrayIndex = getBindArrayIndex(ptr); + bind = inner->GetBindIndex(); + bind.arrayElement = getBindArrayIndex(ptr); uint32_t varMatrixStride = getMatrixStride(ptr); if(varMatrixStride != 0) { diff --git a/renderdoc/driver/shaders/spirv/spirv_reflect.cpp b/renderdoc/driver/shaders/spirv/spirv_reflect.cpp index 431ac2166..0ff054b1e 100644 --- a/renderdoc/driver/shaders/spirv/spirv_reflect.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_reflect.cpp @@ -263,9 +263,10 @@ struct bindpair { Bindpoint map; T bindres; + rdcspv::Id id; bindpair() = default; - bindpair(const Bindpoint &m, const T &res) : map(m), bindres(res) {} + bindpair(const Bindpoint &m, rdcspv::Id id, const T &res) : map(m), bindres(res) {} bool operator<(const bindpair &o) const { if(map.bindset != o.map.bindset) @@ -1411,7 +1412,7 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st res.fixedBindNumber = GetBinding(decorations[global.id].binding); res.bindArraySize = isArray ? arraySize : 1; - rwresources.push_back(shaderrespair(bindmap, res)); + rwresources.push_back(shaderrespair(bindmap, global.id, res)); } else if(varType->IsOpaqueType()) { @@ -1438,7 +1439,7 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st res.isTexture = false; res.isReadOnly = true; - samplers.push_back(shaderrespair(bindmap, res)); + samplers.push_back(shaderrespair(bindmap, global.id, res)); } else { @@ -1478,9 +1479,9 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st res.variableType.baseType = imageType.retType.Type(); if(res.isReadOnly) - roresources.push_back(shaderrespair(bindmap, res)); + roresources.push_back(shaderrespair(bindmap, global.id, res)); else - rwresources.push_back(shaderrespair(bindmap, res)); + rwresources.push_back(shaderrespair(bindmap, global.id, res)); } } else @@ -1564,7 +1565,7 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st MakeConstantBlockVariables(effectiveStorage, *varType, 0, 0, res.variableType.members, pointerTypes, specInfo); - rwresources.push_back(shaderrespair(bindmap, res)); + rwresources.push_back(shaderrespair(bindmap, global.id, res)); } else { @@ -1588,7 +1589,7 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st else cblock.byteSize = 0; - cblocks.push_back(cblockpair(bindmap, cblock)); + cblocks.push_back(cblockpair(bindmap, global.id, cblock)); } } } @@ -1644,7 +1645,7 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st bindmap.arraySize = 1; bindmap.used = true; - cblocks.push_back(cblockpair(bindmap, specblock)); + cblocks.push_back(cblockpair(bindmap, Id(), specblock)); } if(!globalsblock.variables.empty()) @@ -1663,7 +1664,7 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st bindmap.arraySize = 1; bindmap.used = true; - cblocks.push_back(cblockpair(bindmap, globalsblock)); + cblocks.push_back(cblockpair(bindmap, Id(), globalsblock)); } reflection.taskPayload = taskPayloadBlock; @@ -1807,6 +1808,7 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st for(size_t i = 0; i < cblocks.size(); i++) { mapping.constantBlocks[i] = cblocks[i].map; + patchData.cblockInterface.push_back(cblocks[i].id); // fix up any bind points marked with INVALID_BIND. They were sorted to the end // but from here on we want to just be able to index with the bind point // without any special casing. @@ -1819,6 +1821,7 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st for(size_t i = 0; i < samplers.size(); i++) { mapping.samplers[i] = samplers[i].map; + patchData.samplerInterface.push_back(samplers[i].id); // fix up any bind points marked with INVALID_BIND. They were sorted to the end // but from here on we want to just be able to index with the bind point // without any special casing. @@ -1831,6 +1834,7 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st for(size_t i = 0; i < roresources.size(); i++) { mapping.readOnlyResources[i] = roresources[i].map; + patchData.roInterface.push_back(roresources[i].id); // fix up any bind points marked with INVALID_BIND. They were sorted to the end // but from here on we want to just be able to index with the bind point // without any special casing. @@ -1843,6 +1847,7 @@ void Reflector::MakeReflection(const GraphicsAPI sourceAPI, const ShaderStage st for(size_t i = 0; i < rwresources.size(); i++) { mapping.readWriteResources[i] = rwresources[i].map; + patchData.rwInterface.push_back(rwresources[i].id); // fix up any bind points marked with INVALID_BIND. They were sorted to the end // but from here on we want to just be able to index with the bind point // without any special casing. diff --git a/renderdoc/driver/shaders/spirv/spirv_reflect.h b/renderdoc/driver/shaders/spirv/spirv_reflect.h index 0ff2fa31d..17c66ae7c 100644 --- a/renderdoc/driver/shaders/spirv/spirv_reflect.h +++ b/renderdoc/driver/shaders/spirv/spirv_reflect.h @@ -63,6 +63,12 @@ struct SPIRVPatchData rdcarray inputs; rdcarray outputs; + // store the interface in reflection for lookup when generating binding indices + rdcarray cblockInterface; + rdcarray roInterface; + rdcarray rwInterface; + rdcarray samplerInterface; + // the spec IDs in order - these are the order of constants encountered while parsing, and are // used for byte offsets into the resulting data blob (each constant takes 64-bits). // The shader constants reported already have the write offset, but this allows looking up the diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index abc28c37d..2c92b5a1e 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -2420,7 +2420,7 @@ void VulkanReplay::FillBindingElement(VKPipe::BindingElement &dstel, const Descr if(c.m_ImageView[viewid].viewType == VK_IMAGE_VIEW_TYPE_3D) dstel.firstSlice = dstel.numSlices = 0; - // temporary hack, store image layout enum in byteOffset as it's not used for images + // cheeky hack, store image layout enum in byteOffset as it's not used for images dstel.byteOffset = convert(srcel.imageLayout); dstel.minLOD = c.m_ImageView[viewid].minLOD; @@ -2615,7 +2615,7 @@ void VulkanReplay::FillDescriptor(Descriptor &dstel, const DescriptorSetSlot &sr if(c.m_ImageView[viewid].viewType == VK_IMAGE_VIEW_TYPE_3D) dstel.firstSlice = dstel.numSlices = 0; - // temporary hack, store image layout enum in byteOffset as it's not used for images + // cheeky hack, store image layout enum in byteOffset as it's not used for images dstel.byteOffset = convert(srcel.imageLayout); dstel.minLODClamp = c.m_ImageView[viewid].minLOD; diff --git a/renderdoc/driver/vulkan/vk_shaderdebug.cpp b/renderdoc/driver/vulkan/vk_shaderdebug.cpp index ca604eb35..cb26020f7 100644 --- a/renderdoc/driver/vulkan/vk_shaderdebug.cpp +++ b/renderdoc/driver/vulkan/vk_shaderdebug.cpp @@ -42,37 +42,6 @@ RDOC_CONFIG(bool, Vulkan_Debug_DisableBufferDeviceAddress, false, RDOC_CONFIG(bool, Vulkan_Debug_ShaderDebugLogging, false, "Output verbose debug logging messages when debugging shaders."); -struct DescSetBindingSnapshot -{ - rdcarray imageInfos; - rdcarray buffers; - rdcarray texelBuffers; - - template - const rdcarray &get() const; -}; - -template <> -const rdcarray &DescSetBindingSnapshot::get() const -{ - return imageInfos; -} -template <> -const rdcarray &DescSetBindingSnapshot::get() const -{ - return buffers; -} -template <> -const rdcarray &DescSetBindingSnapshot::get() const -{ - return texelBuffers; -} - -struct DescSetSnapshot -{ - rdcarray bindings; -}; - // should match the descriptor set layout created in ShaderDebugData::Init() enum class ShaderDebugBind { @@ -147,8 +116,6 @@ struct ShaderUniformParameters class VulkanAPIWrapper : public rdcspv::DebugAPIWrapper { - rdcarray m_DescSets; - public: VulkanAPIWrapper(WrappedVulkan *vk, VulkanCreationInfo &creation, VkShaderStageFlagBits stage, uint32_t eid, ResourceId shadId) @@ -162,169 +129,114 @@ public: // when we're first setting up, the state is pristine and no replay is needed m_ResourcesDirty = false; - const VulkanRenderState &state = m_pDriver->GetRenderState(); + VulkanReplay *replay = m_pDriver->GetReplay(); - const bool compute = (stage == VK_SHADER_STAGE_COMPUTE_BIT); + // cache the descriptor access. This should be a superset of all descriptors we need to read from + m_Access = replay->GetDescriptorAccess(eid); - // snapshot descriptor set contents - const rdcarray &descSets = - compute ? state.compute.descSets : state.graphics.descSets; + // fetch all descriptor contents now too + m_Descriptors.reserve(m_Access.size()); + m_SamplerDescriptors.reserve(m_Access.size()); - const VulkanCreationInfo::Pipeline &pipe = - m_Creation.m_Pipeline[compute ? state.compute.pipeline : state.graphics.pipeline]; + // we could collate ranges by descriptor store, but in practice we don't expect descriptors to + // be scattered across multiple stores. So to keep the code simple for now we do a linear sweep + ResourceId store; + rdcarray ranges; + for(const DescriptorAccess &acc : m_Access) { - // don't have to handle separate vert/frag layouts as push constant ranges must be identical - const VulkanCreationInfo::PipelineLayout &pipeLayout = - m_Creation.m_PipelineLayout[compute ? pipe.compLayout : pipe.vertLayout]; - - for(const VkPushConstantRange &range : pipeLayout.pushRanges) + if(acc.descriptorStore != store) { - if(range.stageFlags & stage) + if(store != ResourceId()) { - pushData.resize(RDCMAX((uint32_t)pushData.size(), range.offset + range.size)); - - RDCASSERT(range.offset + range.size < sizeof(state.pushconsts)); - - memcpy(pushData.data() + range.offset, state.pushconsts + range.offset, range.size); + m_Descriptors.append(replay->GetDescriptors(store, ranges)); + m_SamplerDescriptors.append(replay->GetSamplerDescriptors(store, ranges)); } + + store = replay->GetLiveID(acc.descriptorStore); + ranges.clear(); } + + // if the last range is contiguous with this access, append this access as a new range to query + if(!ranges.empty() && ranges.back().descriptorSize == acc.byteSize && + ranges.back().offset + ranges.back().descriptorSize == acc.byteOffset) + { + ranges.back().count++; + continue; + } + + DescriptorRange range; + range.offset = acc.byteOffset; + range.descriptorSize = acc.byteSize; + ranges.push_back(range); } - m_DescSets.resize(RDCMIN(descSets.size(), pipe.descSetLayouts.size())); - for(size_t set = 0; set < m_DescSets.size(); set++) + if(store != ResourceId()) { - uint32_t dynamicOffset = 0; + m_Descriptors.append(replay->GetDescriptors(store, ranges)); + m_SamplerDescriptors.append(replay->GetSamplerDescriptors(store, ranges)); + } - // skip invalid descriptor set binds, we assume these aren't present because they will not be - // accessed statically - if(descSets[set].descSet == ResourceId() || descSets[set].pipeLayout == ResourceId()) - continue; + // apply dynamic offsets to our cached descriptors + // we iterate over descriptors first to find dynamic ones, then iterate over our cached set to + // apply. Neither array should be large but there should be fewer dynamic descriptors in total + { + const VulkanRenderState &state = m_pDriver->GetRenderState(); - const VulkanCreationInfo::PipelineLayout &pipeLayoutInfo = - m_Creation.m_PipelineLayout[descSets[set].pipeLayout]; + const rdcarray *srcs[] = { + &state.graphics.descSets, + &state.compute.descSets, + }; - if(pipeLayoutInfo.descSetLayouts[set] == ResourceId()) - continue; - - DescSetSnapshot &dstSet = m_DescSets[set]; - - const BindingStorage &bindStorage = - m_pDriver->GetCurrentDescSetBindingStorage(descSets[set].descSet); - const rdcarray &curBinds = bindStorage.binds; - const bytebuf &curInline = bindStorage.inlineBytes; - - // use the descriptor set layout from when it was bound. If the pipeline layout declared a - // descriptor set layout for this set, but it's statically unused, it may be complete - // garbage and doesn't match what the shader uses. However the pipeline layout at descriptor - // set bind time must have been compatible and valid so we can use it. If this set *is* used - // then the pipeline layout at bind time must be compatible with the pipeline's pipeline - // layout, so we're fine too. - const DescSetLayout &setLayout = m_Creation.m_DescSetLayout[pipeLayoutInfo.descSetLayouts[set]]; - - for(size_t bind = 0; bind < setLayout.bindings.size(); bind++) + for(size_t p = 0; p < ARRAY_COUNT(srcs); p++) { - const DescSetLayout::Binding &bindLayout = setLayout.bindings[bind]; - - uint32_t descriptorCount = bindLayout.descriptorCount; - - if(bindLayout.variableSize) - descriptorCount = bindStorage.variableDescriptorCount; - - if(descriptorCount == 0) - continue; - - if(bindLayout.stageFlags & stage) + for(size_t i = 0; i < srcs[p]->size(); i++) { - DescriptorSetSlot *curSlots = curBinds[bind]; + const VulkanStatePipeline::DescriptorAndOffsets &srcData = srcs[p]->at(i); + ResourceId sourceSet = srcData.descSet; + const uint32_t *srcOffset = srcData.offsets.begin(); - dstSet.bindings.resize_for_index(bind); + if(sourceSet == ResourceId()) + continue; - DescSetBindingSnapshot &dstBind = dstSet.bindings[bind]; + const VulkanCreationInfo::PipelineLayout &pipeLayoutInfo = + m_Creation.m_PipelineLayout[srcData.pipeLayout]; - if(bindLayout.layoutDescType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + ResourceId setOrig = m_pDriver->GetResourceManager()->GetOriginalID(sourceSet); + + const BindingStorage &bindStorage = + m_pDriver->GetCurrentDescSetBindingStorage(srcData.descSet); + const DescriptorSetSlot *first = bindStorage.binds[0]; + for(size_t b = 0; b < bindStorage.binds.size(); b++) { - // push directly into the buffer cache from the inline data - BindpointIndex idx; - idx.bindset = (int32_t)set; - idx.bind = (int32_t)bind; - idx.arrayIndex = 0; - bufferCache[idx].assign(curInline.data() + curSlots->offset, descriptorCount); - } - else - { - for(uint32_t i = 0; i < descriptorCount; i++) + const DescSetLayout::Binding &layoutBind = + m_Creation.m_DescSetLayout[pipeLayoutInfo.descSetLayouts[i]].bindings[b]; + + if(layoutBind.layoutDescType != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC && + layoutBind.layoutDescType != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) + continue; + + uint64_t descriptorByteOffset = bindStorage.binds[b] - first; + + // inline UBOs aren't dynamic and variable size can't be used with dynamic buffers, so + // the count is what it is at definition time + for(uint32_t a = 0; a < layoutBind.descriptorCount; a++) { - const DescriptorSetSlot &slot = curSlots[i]; + uint32_t dynamicBufferByteOffset = *srcOffset; + srcOffset++; - // When bind layout contains immutable samplers, sampler-only slots always have type - // DescriptorSlotType::Unwritten. Treat them as sampler slots in that case. - DescriptorSlotType slotType = slot.type; - if(bindLayout.immutableSampler && - bindLayout.layoutDescType == VK_DESCRIPTOR_TYPE_SAMPLER) - slotType = DescriptorSlotType::Sampler; - - switch(slotType) + for(size_t accIdx = 0; accIdx < m_Access.size(); accIdx++) { - case DescriptorSlotType::Sampler: - case DescriptorSlotType::CombinedImageSampler: - case DescriptorSlotType::SampledImage: - case DescriptorSlotType::StorageImage: - case DescriptorSlotType::InputAttachment: + if(m_Access[accIdx].descriptorStore == setOrig && + m_Access[accIdx].byteOffset == descriptorByteOffset + a) { - dstBind.imageInfos.resize(descriptorCount); - dstBind.imageInfos[i].imageLayout = convert(slot.imageLayout); - dstBind.imageInfos[i].imageView = - m_pDriver->GetResourceManager()->GetCurrentHandle(slot.resource); - dstBind.imageInfos[i].sampler = - m_pDriver->GetResourceManager()->GetCurrentHandle( - bindLayout.immutableSampler ? bindLayout.immutableSampler[i] - : slot.sampler); + m_Descriptors[accIdx].byteOffset += dynamicBufferByteOffset; break; } - case DescriptorSlotType::UniformTexelBuffer: - case DescriptorSlotType::StorageTexelBuffer: - { - dstBind.texelBuffers.resize(descriptorCount); - dstBind.texelBuffers[i] = - m_pDriver->GetResourceManager()->GetCurrentHandle(slot.resource); - break; - } - case DescriptorSlotType::UniformBuffer: - case DescriptorSlotType::StorageBuffer: - case DescriptorSlotType::UniformBufferDynamic: - case DescriptorSlotType::StorageBufferDynamic: - { - dstBind.buffers.resize(descriptorCount); - dstBind.buffers[i].offset = slot.offset; - dstBind.buffers[i].range = slot.GetRange(); - dstBind.buffers[i].buffer = - m_pDriver->GetResourceManager()->GetCurrentHandle(slot.resource); - - if(slot.type == DescriptorSlotType::UniformBufferDynamic || - slot.type == DescriptorSlotType::StorageBufferDynamic) - dstBind.buffers[i].offset += descSets[set].offsets[dynamicOffset++]; - break; - } - case DescriptorSlotType::Unwritten: break; - default: RDCERR("Unexpected descriptor type"); } } } } - else - { - // still need to skip past dynamic offsets for stages that aren't of interest - // we can use the layout descriptor type here because mutable descriptors aren't allowed - // to be dynamic - - switch(bindLayout.layoutDescType) - { - case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: - case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: dynamicOffset += descriptorCount; break; - default: break; - } - } } } } @@ -360,12 +272,12 @@ public: virtual ResourceId GetShaderID() override { return m_ShaderID; } - virtual uint64_t GetBufferLength(BindpointIndex bind) override + virtual uint64_t GetBufferLength(ShaderBindIndex bind) override { return PopulateBuffer(bind).size(); } - virtual void ReadBufferValue(BindpointIndex bind, uint64_t offset, uint64_t byteSize, + virtual void ReadBufferValue(ShaderBindIndex bind, uint64_t offset, uint64_t byteSize, void *dst) override { const bytebuf &data = PopulateBuffer(bind); @@ -374,7 +286,7 @@ public: memcpy(dst, data.data() + (size_t)offset, (size_t)byteSize); } - virtual void WriteBufferValue(BindpointIndex bind, uint64_t offset, uint64_t byteSize, + virtual void WriteBufferValue(ShaderBindIndex bind, uint64_t offset, uint64_t byteSize, const void *src) override { bytebuf &data = PopulateBuffer(bind); @@ -399,7 +311,7 @@ public: memcpy(data.data() + offset, src, (size_t)byteSize); } - virtual bool ReadTexel(BindpointIndex imageBind, const ShaderVariable &coord, uint32_t sample, + virtual bool ReadTexel(ShaderBindIndex imageBind, const ShaderVariable &coord, uint32_t sample, ShaderVariable &output) override { ImageData &data = PopulateImage(imageBind); @@ -489,7 +401,7 @@ public: return true; } - virtual bool WriteTexel(BindpointIndex imageBind, const ShaderVariable &coord, uint32_t sample, + virtual bool WriteTexel(ShaderBindIndex imageBind, const ShaderVariable &coord, uint32_t sample, const ShaderVariable &input) override { ImageData &data = PopulateImage(imageBind); @@ -662,8 +574,8 @@ public: } bool CalculateSampleGather(rdcspv::ThreadState &lane, rdcspv::Op opcode, - DebugAPIWrapper::TextureType texType, BindpointIndex imageBind, - BindpointIndex samplerBind, const ShaderVariable &uv, + DebugAPIWrapper::TextureType texType, ShaderBindIndex imageBind, + ShaderBindIndex samplerBind, const ShaderVariable &uv, const ShaderVariable &ddxCalc, const ShaderVariable &ddyCalc, const ShaderVariable &compare, rdcspv::GatherChannel gatherChannel, const rdcspv::ImageOperandsAndParamDatas &operands, @@ -679,17 +591,15 @@ public: // fetch the right type of descriptor depending on if we're buffer or not bool valid = true; rdcstr access = StringFormat::Fmt("performing %s operation", ToStr(opcode).c_str()); - const VkDescriptorImageInfo &imageInfo = - buffer ? GetDescriptor(access, invalidBind, valid) - : GetDescriptor(access, imageBind, valid); - const VkBufferView &bufferView = buffer - ? GetDescriptor(access, imageBind, valid) - : GetDescriptor(access, invalidBind, valid); + const Descriptor &imageDescriptor = buffer ? GetDescriptor(access, ShaderBindIndex(), valid) + : GetDescriptor(access, imageBind, valid); + const Descriptor &bufferViewDescriptor = buffer + ? GetDescriptor(access, imageBind, valid) + : GetDescriptor(access, ShaderBindIndex(), valid); // fetch the sampler (if there's no sampler, this will silently return dummy data without // marking invalid - const VkDescriptorImageInfo &samplerInfo = - GetDescriptor(access, samplerBind, valid); + const SamplerDescriptor &samplerDescriptor = GetSamplerDescriptor(access, samplerBind, valid); // if any descriptor lookup failed, return now if(!valid) @@ -697,9 +607,14 @@ public: VkMarkerRegion markerRegion("CalculateSampleGather"); - VkSampler sampler = samplerInfo.sampler; - VkImageView view = imageInfo.imageView; - VkImageLayout layout = imageInfo.imageLayout; + VkBufferView bufferView = + m_pDriver->GetResourceManager()->GetLiveHandle(bufferViewDescriptor.view); + + VkSampler sampler = + m_pDriver->GetResourceManager()->GetLiveHandle(samplerDescriptor.object); + VkImageView view = + m_pDriver->GetResourceManager()->GetLiveHandle(imageDescriptor.view); + VkImageLayout layout = convert((DescriptorSlotImageLayout)imageDescriptor.byteOffset); // promote view to Array view @@ -1576,14 +1491,16 @@ private: uint32_t m_EventID; ResourceId m_ShaderID; + rdcarray m_Access; + rdcarray m_Descriptors; + rdcarray m_SamplerDescriptors; + std::map m_SampleViews; typedef rdcpair SamplerBiasKey; std::map m_BiasSamplers; - bytebuf pushData; - - std::map bufferCache; + std::map bufferCache; struct ImageData { @@ -1605,83 +1522,80 @@ private: } }; - std::map imageCache; + std::map imageCache; - template - const T &GetDescriptor(const rdcstr &access, BindpointIndex index, bool &valid) + const Descriptor &GetDescriptor(const rdcstr &access, ShaderBindIndex index, bool &valid) { - static T dummy = {}; + static Descriptor dummy; - if(index == invalidBind) + if(index.category == DescriptorCategory::Unknown) { // invalid index, return a dummy data but don't mark as invalid return dummy; } - if(index.bindset < 0 || index.bindset >= m_DescSets.count()) - { - m_pDriver->AddDebugMessage( - MessageCategory::Execution, MessageSeverity::High, MessageSource::RuntimeWarning, - StringFormat::Fmt( - "Out of bounds access to unbound descriptor set %u (binding %u) when %s", - index.bindset, index.bind, access.c_str())); - valid = false; - return dummy; - } + int32_t a = m_Access.indexOf(index); - const DescSetSnapshot &setData = m_DescSets[index.bindset]; - - if(index.bind < 0 || index.bind >= setData.bindings.count()) - { - m_pDriver->AddDebugMessage( - MessageCategory::Execution, MessageSeverity::High, MessageSource::RuntimeWarning, - StringFormat::Fmt( - "Out of bounds access to non-existant descriptor set %u binding %u when %s", - index.bindset, index.bind, access.c_str())); - valid = false; - return dummy; - } - - const DescSetBindingSnapshot &bindData = setData.bindings[index.bind]; - - const rdcarray &elemData = bindData.get(); - - if(elemData.empty()) - { - m_pDriver->AddDebugMessage( - MessageCategory::Execution, MessageSeverity::High, MessageSource::RuntimeWarning, - StringFormat::Fmt("descriptor set %u binding %u is not bound, when %s", index.bindset, - index.bind, access.c_str())); - valid = false; - return dummy; - } - - if(index.arrayIndex >= elemData.size()) + // this should not happen unless the debugging references an array element that we didn't + // detect dynamically. We could improve this by retrieving a more conservative access set + // internally so that all descriptors are 'accessed' + if(a < 0) { m_pDriver->AddDebugMessage(MessageCategory::Execution, MessageSeverity::High, MessageSource::RuntimeWarning, - StringFormat::Fmt("descriptor set %u binding %u has %zu " - "descriptors, index %u is out of bounds when %s", - index.bindset, index.bind, elemData.size(), - index.arrayIndex, access.c_str())); + StringFormat::Fmt("Internal error: Binding %s %u[%u] did not " + "exist in calculated descriptor access when %s.", + ToStr(index.category).c_str(), index.index, + index.arrayElement, access.c_str())); valid = false; return dummy; } - return elemData[index.arrayIndex]; + return m_Descriptors[a]; + } + + const SamplerDescriptor &GetSamplerDescriptor(const rdcstr &access, ShaderBindIndex index, + bool &valid) + { + static SamplerDescriptor dummy; + + if(index.category == DescriptorCategory::Unknown) + { + // invalid index, return a dummy data but don't mark as invalid + return dummy; + } + + int32_t a = m_Access.indexOf(index); + + // this should not happen unless the debugging references an array element that we didn't + // detect dynamically. We could improve this by retrieving a more conservative access set + // internally so that all descriptors are 'accessed' + if(a < 0) + { + m_pDriver->AddDebugMessage(MessageCategory::Execution, MessageSeverity::High, + MessageSource::RuntimeWarning, + StringFormat::Fmt("Internal error: Binding %s %u[%u] did not " + "exist in calculated descriptor access when %s.", + ToStr(index.category).c_str(), index.index, + index.arrayElement, access.c_str())); + valid = false; + return dummy; + } + + return m_SamplerDescriptors[a]; } bytebuf &PopulateBuffer(uint64_t address, size_t &offs) { // pick a non-overlapping bind namespace for direct pointer access - BindpointIndex bind = pointerBind; + ShaderBindIndex bind; uint64_t base; uint64_t end; ResourceId id; bool valid = false; if(m_Creation.m_BufferAddresses.empty()) { - bind.arrayIndex = 0; + bind.arrayElement = 0; auto insertIt = bufferCache.insert(std::make_pair(bind, bytebuf())); m_pDriver->AddDebugMessage( MessageCategory::Execution, MessageSeverity::High, MessageSource::RuntimeWarning, @@ -1697,7 +1611,7 @@ private: if(address != it->first && it != m_Creation.m_BufferAddresses.begin()) it--; // use the index in the map as a unique buffer identifier that's not 64-bit - bind.arrayIndex = uint32_t(it - m_Creation.m_BufferAddresses.begin()); + bind.arrayElement = uint32_t(it - m_Creation.m_BufferAddresses.begin()); { base = it->first; id = it->second; @@ -1734,53 +1648,14 @@ private: return data; } - bytebuf &PopulateBuffer(BindpointIndex bind) + bytebuf &PopulateBuffer(ShaderBindIndex bind) { auto insertIt = bufferCache.insert(std::make_pair(bind, bytebuf())); bytebuf &data = insertIt.first->second; if(insertIt.second) - { - if(bind.bindset == PushConstantBindSet) - { - data = pushData; - } - else - { - bool valid = true; - const VkDescriptorBufferInfo &bufData = - GetDescriptor("accessing buffer value", bind, valid); - if(valid) - { - // if the resources might be dirty from side-effects from the action, replay back to right - // before it. - if(m_ResourcesDirty) - { - VkMarkerRegion region("un-dirtying resources"); - m_pDriver->ReplayLog(0, m_EventID, eReplay_WithoutDraw); - m_ResourcesDirty = false; - } - - if(bufData.buffer != VK_NULL_HANDLE) - { - m_pDriver->GetDebugManager()->GetBufferData(GetResID(bufData.buffer), bufData.offset, - bufData.range, data); - } - } - } - } - - return data; - } - - ImageData &PopulateImage(BindpointIndex bind) - { - auto insertIt = imageCache.insert(std::make_pair(bind, ImageData())); - ImageData &data = insertIt.first->second; - if(insertIt.second) { bool valid = true; - const VkDescriptorImageInfo &imgData = - GetDescriptor("performing image load/store", bind, valid); + const Descriptor &bufData = GetDescriptor("accessing buffer value", bind, valid); if(valid) { // if the resources might be dirty from side-effects from the action, replay back to right @@ -1792,10 +1667,41 @@ private: m_ResourcesDirty = false; } - if(imgData.imageView != VK_NULL_HANDLE) + if(bufData.resource != ResourceId()) + { + m_pDriver->GetReplay()->GetBufferData( + m_pDriver->GetResourceManager()->GetLiveID(bufData.resource), bufData.byteOffset, + bufData.byteSize, data); + } + } + } + + return data; + } + + ImageData &PopulateImage(ShaderBindIndex bind) + { + auto insertIt = imageCache.insert(std::make_pair(bind, ImageData())); + ImageData &data = insertIt.first->second; + if(insertIt.second) + { + bool valid = true; + const Descriptor &imgData = GetDescriptor("performing image load/store", bind, valid); + if(valid) + { + // if the resources might be dirty from side-effects from the action, replay back to right + // before it. + if(m_ResourcesDirty) + { + VkMarkerRegion region("un-dirtying resources"); + m_pDriver->ReplayLog(0, m_EventID, eReplay_WithoutDraw); + m_ResourcesDirty = false; + } + + if(imgData.view != ResourceId()) { const VulkanCreationInfo::ImageView &viewProps = - m_Creation.m_ImageView[GetResID(imgData.imageView)]; + m_Creation.m_ImageView[m_pDriver->GetResourceManager()->GetLiveID(imgData.view)]; const VulkanCreationInfo::Image &imageProps = m_Creation.m_Image[viewProps.image]; uint32_t mip = viewProps.range.baseMipLevel;