diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp index 465e90936..28468fb39 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp @@ -613,19 +613,16 @@ bool VulkanPipelineStateViewer::setViewDetails(RDTreeWidgetItem *node, const bin bool VulkanPipelineStateViewer::showNode(bool usedSlot, bool filledSlot) { - const bool showUnused = ui->showUnused->isChecked(); - const bool showEmpty = ui->showEmpty->isChecked(); - // show if it's referenced by the shader - regardless of empty or not if(usedSlot) return true; // it's not referenced, but if it's bound and we have "show unused" then show it - if(showUnused && filledSlot) + if(m_ShowUnused && filledSlot) return true; // it's empty, and we have "show empty" - if(showEmpty && !filledSlot) + if(m_ShowEmpty && !filledSlot) return true; return false; @@ -1019,16 +1016,20 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails, } const rdcarray *slotBinds = NULL; + int32_t firstUsedBind = 0; + int32_t lastUsedBind = 0; BindType bindType = BindType::Unknown; ShaderStageMask stageBits = ShaderStageMask::Unknown; bool pushDescriptor = false; - uint32_t dynamicallyUsedCount = 1; + uint32_t dynamicallyUsedCount = ~0U; if(bindset < pipe.descriptorSets.count() && bind < pipe.descriptorSets[bindset].bindings.count()) { pushDescriptor = pipe.descriptorSets[bindset].pushDescriptor; dynamicallyUsedCount = pipe.descriptorSets[bindset].bindings[bind].dynamicallyUsedCount; slotBinds = &pipe.descriptorSets[bindset].bindings[bind].binds; + firstUsedBind = pipe.descriptorSets[bindset].bindings[bind].firstUsedIndex; + lastUsedBind = pipe.descriptorSets[bindset].bindings[bind].lastUsedIndex; bindType = pipe.descriptorSets[bindset].bindings[bind].type; stageBits = pipe.descriptorSets[bindset].bindings[bind].stageFlags; } @@ -1042,6 +1043,12 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails, bindType = isrw ? BindType::ReadWriteImage : BindType::ReadOnlyImage; } + if(m_ShowUnused) + { + firstUsedBind = 0; + lastUsedBind = INT_MAX; + } + bool usedSlot = bindMap != NULL && bindMap->used && dynamicallyUsedCount > 0; bool stageBitsIncluded = bool(stageBits & MaskForStage(stage.stage)); @@ -1056,7 +1063,8 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails, // consider it filled if any array element is filled bool filledSlot = false; - for(int idx = 0; slotBinds != NULL && idx < slotBinds->count(); idx++) + for(int32_t idx = firstUsedBind; + slotBinds != NULL && !filledSlot && idx <= lastUsedBind && idx < slotBinds->count(); idx++) { filledSlot |= (*slotBinds)[idx].resourceResourceId != ResourceId(); if(bindType == BindType::Sampler || bindType == BindType::ImageSampler) @@ -1114,7 +1122,7 @@ void VulkanPipelineStateViewer::addResourceRow(ShaderReflection *shaderDetails, parentNode = node; } - for(int idx = 0; idx < arrayLength; idx++) + for(int idx = firstUsedBind; idx <= lastUsedBind && idx < arrayLength; idx++) { const VKPipe::BindingElement *descriptorBind = NULL; if(slotBinds != NULL) @@ -1441,7 +1449,9 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta const rdcarray *slotBinds = NULL; BindType bindType = BindType::ConstantBuffer; ShaderStageMask stageBits = ShaderStageMask::Unknown; - uint32_t dynamicallyUsedCount = 1; + uint32_t dynamicallyUsedCount = ~0U; + int32_t firstUsedBind = 0; + int32_t lastUsedBind = 0; bool pushDescriptor = false; @@ -1450,10 +1460,18 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta pushDescriptor = pipe.descriptorSets[bindset].pushDescriptor; dynamicallyUsedCount = pipe.descriptorSets[bindset].bindings[bind].dynamicallyUsedCount; slotBinds = &pipe.descriptorSets[bindset].bindings[bind].binds; + firstUsedBind = pipe.descriptorSets[bindset].bindings[bind].firstUsedIndex; + lastUsedBind = pipe.descriptorSets[bindset].bindings[bind].lastUsedIndex; bindType = pipe.descriptorSets[bindset].bindings[bind].type; stageBits = pipe.descriptorSets[bindset].bindings[bind].stageFlags; } + if(m_ShowUnused) + { + firstUsedBind = 0; + lastUsedBind = INT_MAX; + } + bool usedSlot = bindMap != NULL && bindMap->used && dynamicallyUsedCount > 0; bool stageBitsIncluded = bool(stageBits & MaskForStage(stage.stage)); @@ -1466,9 +1484,12 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta // consider it filled if any array element is filled (or it's push constants) bool filledSlot = cblock != NULL && !cblock->bufferBacked; - for(int idx = 0; slotBinds != NULL && idx < slotBinds->count(); idx++) + for(int32_t idx = firstUsedBind; + slotBinds != NULL && !filledSlot && idx <= lastUsedBind && idx < slotBinds->count(); idx++) + { filledSlot |= (*slotBinds)[idx].resourceResourceId != ResourceId() || (*slotBinds)[idx].inlineBlock; + } bool containsResource = filledSlot; @@ -1517,7 +1538,7 @@ void VulkanPipelineStateViewer::addConstantBlockRow(ShaderReflection *shaderDeta ubos->showColumn(0); } - for(int idx = 0; idx < arrayLength; idx++) + for(int32_t idx = firstUsedBind; idx <= lastUsedBind && idx < arrayLength; idx++) { const VKPipe::BindingElement *descriptorBind = NULL; if(slotBinds != NULL) @@ -1816,6 +1837,10 @@ void VulkanPipelineStateViewer::setState() return; } + // cache latest state of these checkboxes + m_ShowUnused = ui->showUnused->isChecked(); + m_ShowEmpty = ui->showEmpty->isChecked(); + m_CombinedImageSamplers.clear(); const VKPipe::State &state = *m_Ctx.CurVulkanPipelineState(); diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h index 5aebd11ae..68ffcfa76 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h @@ -122,6 +122,9 @@ private: bool showNode(bool usedSlot, bool filledSlot); + bool m_ShowUnused = false; + bool m_ShowEmpty = false; + void exportHTML(QXmlStreamWriter &xml, const VKPipe::VertexInput &vi); void exportHTML(QXmlStreamWriter &xml, const VKPipe::InputAssembly &ia); void exportHTML(QXmlStreamWriter &xml, const VKPipe::Shader &sh); diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index 4095d76a5..be2f5c0cb 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -350,8 +350,8 @@ void ShaderViewer::editShader(ResourceId id, ShaderStage stage, const QString &e void ShaderViewer::cacheResources() { - m_ReadOnlyResources = m_Ctx.CurPipelineState().GetReadOnlyResources(m_Stage); - m_ReadWriteResources = m_Ctx.CurPipelineState().GetReadWriteResources(m_Stage); + m_ReadOnlyResources = m_Ctx.CurPipelineState().GetReadOnlyResources(m_Stage, false); + m_ReadWriteResources = m_Ctx.CurPipelineState().GetReadWriteResources(m_Stage, false); } void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderReflection *shader, diff --git a/qrenderdoc/Windows/TextureViewer.cpp b/qrenderdoc/Windows/TextureViewer.cpp index 9a37cbba6..a63556a41 100644 --- a/qrenderdoc/Windows/TextureViewer.cpp +++ b/qrenderdoc/Windows/TextureViewer.cpp @@ -166,7 +166,12 @@ BoundResource Following::GetBoundResource(ICaptureContext &ctx, int arrayIdx) int residx = rw.indexOf(key); if(residx >= 0) - ret = rw[residx].resources[arrayIdx]; + { + const BoundResourceArray &resArray = rw[residx]; + if(arrayIdx >= resArray.firstIndex && + arrayIdx - resArray.firstIndex < resArray.resources.count()) + ret = resArray.resources[arrayIdx - resArray.firstIndex]; + } } } else if(Type == FollowType::ReadOnly) @@ -181,7 +186,12 @@ BoundResource Following::GetBoundResource(ICaptureContext &ctx, int arrayIdx) int residx = ro.indexOf(key); if(residx >= 0) - ret = ro[residx].resources[arrayIdx]; + { + const BoundResourceArray &resArray = ro[residx]; + if(arrayIdx >= resArray.firstIndex && + arrayIdx - resArray.firstIndex < resArray.resources.count()) + ret = resArray.resources[arrayIdx - resArray.firstIndex]; + } } } @@ -233,7 +243,8 @@ BoundResource Following::GetDepthTarget(ICaptureContext &ctx) return ctx.CurPipelineState().GetDepthTarget(); } -rdcarray Following::GetReadWriteResources(ICaptureContext &ctx, ShaderStage stage) +rdcarray Following::GetReadWriteResources(ICaptureContext &ctx, + ShaderStage stage, bool onlyUsed) { bool copy = false, clear = false, compute = false; GetDrawContext(ctx, copy, clear, compute); @@ -246,22 +257,18 @@ rdcarray Following::GetReadWriteResources(ICaptureContext &c { // only return compute resources for one stage if(stage == ShaderStage::Pixel || stage == ShaderStage::Compute) - return ctx.CurPipelineState().GetReadWriteResources(ShaderStage::Compute); + return ctx.CurPipelineState().GetReadWriteResources(ShaderStage::Compute, onlyUsed); else return rdcarray(); } else { - return ctx.CurPipelineState().GetReadWriteResources(stage); + return ctx.CurPipelineState().GetReadWriteResources(stage, onlyUsed); } } -rdcarray Following::GetReadWriteResources(ICaptureContext &ctx) -{ - return GetReadWriteResources(ctx, Stage); -} - -rdcarray Following::GetReadOnlyResources(ICaptureContext &ctx, ShaderStage stage) +rdcarray Following::GetReadOnlyResources(ICaptureContext &ctx, + ShaderStage stage, bool onlyUsed) { const DrawcallDescription *curDraw = ctx.CurDrawcall(); bool copy = false, clear = false, compute = false; @@ -282,21 +289,16 @@ rdcarray Following::GetReadOnlyResources(ICaptureContext &ct { // only return compute resources for one stage if(stage == ShaderStage::Pixel || stage == ShaderStage::Compute) - return ctx.CurPipelineState().GetReadOnlyResources(ShaderStage::Compute); + return ctx.CurPipelineState().GetReadOnlyResources(ShaderStage::Compute, onlyUsed); else return rdcarray(); } else { - return ctx.CurPipelineState().GetReadOnlyResources(stage); + return ctx.CurPipelineState().GetReadOnlyResources(stage, onlyUsed); } } -rdcarray Following::GetReadOnlyResources(ICaptureContext &ctx) -{ - return GetReadOnlyResources(ctx, Stage); -} - const ShaderReflection *Following::GetReflection(ICaptureContext &ctx, ShaderStage stage) { bool copy = false, clear = false, compute = false; @@ -2342,65 +2344,40 @@ void TextureViewer::InitStageResourcePreviews(ShaderStage stage, const rdcarray *resArray = NULL; uint32_t dynamicallyUsedResCount = 1; + int32_t firstIndex = 0; int residx = ResList.indexOf(key); if(residx >= 0) { resArray = &ResList[residx].resources; dynamicallyUsedResCount = ResList[residx].dynamicallyUsedCount; + firstIndex = ResList[residx].firstIndex; } int arrayLen = resArray != NULL ? resArray->count() : 1; - const bool collapseArray = - (dynamicallyUsedResCount > 20) || (dynamicallyUsedResCount == 0 && arrayLen > 8); + const bool collapseArray = arrayLen > 8 && (dynamicallyUsedResCount > 20 || m_ShowUnused); - for(int arrayIdx = 0; arrayIdx < arrayLen; arrayIdx++) + for(int i = 0; i < arrayLen; i++) { - if(resArray && !resArray->at(arrayIdx).dynamicallyUsed) + int arrayIdx = firstIndex + i; + + if(resArray && i >= resArray->count()) + break; + + if(resArray && !resArray->at(i).dynamicallyUsed) continue; BoundResource res = {}; if(resArray) - res = resArray->at(arrayIdx); - - bool used = key.used; - - QString bindName; - - for(const ShaderResource &bind : resourceDetails) - { - if(bind.bindPoint == idx) - { - bindName = bind.name; - break; - } - } - - if(copy) - { - used = true; - bindName = tr("Source"); - } + res = resArray->at(i); Following follow(*this, rw ? FollowType::ReadWrite : FollowType::ReadOnly, stage, idx, arrayIdx); - QString slotName = QFormatStr("%1 %2%3") - .arg(m_Ctx.CurPipelineState().Abbrev(stage)) - .arg(rw ? lit("RW ") : lit("")) - .arg(idx); - - if(collapseArray) - slotName += QFormatStr(" Arr[%1]").arg(arrayLen); - else - slotName += QFormatStr("[%1]").arg(arrayIdx); - - if(copy) - slotName = tr("SRC"); // show if it's referenced by the shader - regardless of empty or not - bool show = used; + bool show = key.used || copy; // it's bound, but not referenced, and we have "show disabled" show = show || (m_ShowUnused && res.resourceId != ResourceId()); @@ -2432,6 +2409,33 @@ void TextureViewer::InitStageResourcePreviews(ShaderStage stage, prevIndex++; + QString slotName = QFormatStr("%1 %2%3") + .arg(m_Ctx.CurPipelineState().Abbrev(stage)) + .arg(rw ? lit("RW ") : lit("")) + .arg(idx); + + if(collapseArray) + slotName += QFormatStr(" Arr[%1]").arg(arrayLen); + else + slotName += QFormatStr("[%1]").arg(arrayIdx); + + if(copy) + slotName = tr("SRC"); + + QString bindName; + + for(const ShaderResource &bind : resourceDetails) + { + if(bind.bindPoint == idx) + { + bindName = bind.name; + break; + } + } + + if(copy) + bindName = tr("Source"); + InitResourcePreview(prev, show ? res : BoundResource(), show, follow, bindName, slotName); if(collapseArray) @@ -3073,8 +3077,10 @@ void TextureViewer::OnEventChanged(uint32_t eventId) { ShaderStage stage = stages[i]; - m_ReadWriteResources[(uint32_t)stage] = Following::GetReadWriteResources(m_Ctx, stage); - m_ReadOnlyResources[(uint32_t)stage] = Following::GetReadOnlyResources(m_Ctx, stage); + m_ReadWriteResources[(uint32_t)stage] = + Following::GetReadWriteResources(m_Ctx, stage, !m_ShowUnused); + m_ReadOnlyResources[(uint32_t)stage] = + Following::GetReadOnlyResources(m_Ctx, stage, !m_ShowUnused); const ShaderReflection *details = Following::GetReflection(m_Ctx, stage); const ShaderBindpointMapping &mapping = Following::GetMapping(m_Ctx, stage); diff --git a/qrenderdoc/Windows/TextureViewer.h b/qrenderdoc/Windows/TextureViewer.h index 6699c4048..15e78d125 100644 --- a/qrenderdoc/Windows/TextureViewer.h +++ b/qrenderdoc/Windows/TextureViewer.h @@ -79,14 +79,10 @@ struct Following static rdcarray GetOutputTargets(ICaptureContext &ctx); static BoundResource GetDepthTarget(ICaptureContext &ctx); - - rdcarray GetReadWriteResources(ICaptureContext &ctx); - - static rdcarray GetReadWriteResources(ICaptureContext &ctx, ShaderStage stage); - - rdcarray GetReadOnlyResources(ICaptureContext &ctx); - - static rdcarray GetReadOnlyResources(ICaptureContext &ctx, ShaderStage stage); + static rdcarray GetReadWriteResources(ICaptureContext &ctx, ShaderStage stage, + bool onlyUsed); + static rdcarray GetReadOnlyResources(ICaptureContext &ctx, ShaderStage stage, + bool onlyUsed); const ShaderReflection *GetReflection(ICaptureContext &ctx); static const ShaderReflection *GetReflection(ICaptureContext &ctx, ShaderStage stage); diff --git a/renderdoc/api/replay/common_pipestate.h b/renderdoc/api/replay/common_pipestate.h index b7f812c7c..650e28c33 100644 --- a/renderdoc/api/replay/common_pipestate.h +++ b/renderdoc/api/replay/common_pipestate.h @@ -315,6 +315,7 @@ struct BoundResourceArray BoundResourceArray(Bindpoint b, const rdcarray &r) : bindPoint(b), resources(r) { dynamicallyUsedCount = (uint32_t)r.size(); + firstIndex = 0; } // for convenience for searching the array, we compare only using the BindPoint bool operator==(const BoundResourceArray &o) const { return bindPoint == o.bindPoint; } @@ -330,8 +331,17 @@ struct BoundResourceArray Some APIs provide fine-grained usage based on dynamic shader feedback, to support 'bindless' scenarios where only a small sparse subset of bound resources are actually used. + +If this information isn't present this will be set to a large number. )"); - uint32_t dynamicallyUsedCount = 0; + uint32_t dynamicallyUsedCount = ~0U; + DOCUMENT(R"(Gives the array index of the first binding in :data:`resource`. If only a small subset +of the resources are used by the shader then the array may be rebased such that the first element is +not array index 0. + +For more information see :data:`VKBindingElement.dynamicallyUsed`. +)"); + int32_t firstIndex = 0; }; DECLARE_REFLECTION_STRUCT(BoundResourceArray); diff --git a/renderdoc/api/replay/pipestate.h b/renderdoc/api/replay/pipestate.h index 742924780..39ed46b1b 100644 --- a/renderdoc/api/replay/pipestate.h +++ b/renderdoc/api/replay/pipestate.h @@ -305,10 +305,12 @@ For some APIs that don't distinguish by entry point, this may be empty. DOCUMENT(R"(Retrieves the read-only resources bound to a particular shader stage. :param ShaderStage stage: The shader stage to fetch from. +:param bool onlyUsed: Return only a subset of resources containing those actually used by the + shader. :return: The currently bound read-only resources. :rtype: ``list`` of :class:`BoundResourceArray` entries )"); - rdcarray GetReadOnlyResources(ShaderStage stage) const; + rdcarray GetReadOnlyResources(ShaderStage stage, bool onlyUsed = false) const; DOCUMENT(R"(Retrieves the samplers bound to a particular shader stage. @@ -321,10 +323,12 @@ For some APIs that don't distinguish by entry point, this may be empty. DOCUMENT(R"(Retrieves the read/write resources bound to a particular shader stage. :param ShaderStage stage: The shader stage to fetch from. +:param bool onlyUsed: Return only a subset of resources containing those actually used by the + shader. :return: The currently bound read/write resources. :rtype: ``list`` of :class:`BoundResourceArray` entries )"); - rdcarray GetReadWriteResources(ShaderStage stage) const; + rdcarray GetReadWriteResources(ShaderStage stage, bool onlyUsed = false) const; DOCUMENT(R"(Retrieves the read/write resources bound to the depth-stencil output. diff --git a/renderdoc/api/replay/pipestate.inl b/renderdoc/api/replay/pipestate.inl index 7e2c1fad2..9432ec576 100644 --- a/renderdoc/api/replay/pipestate.inl +++ b/renderdoc/api/replay/pipestate.inl @@ -1132,7 +1132,7 @@ rdcarray PipeState::GetSamplers(ShaderStage stage) const return ret; } -rdcarray PipeState::GetReadOnlyResources(ShaderStage stage) const +rdcarray PipeState::GetReadOnlyResources(ShaderStage stage, bool onlyUsed) const { rdcarray ret; @@ -1251,18 +1251,33 @@ rdcarray PipeState::GetReadOnlyResources(ShaderStage stage) ret.push_back(BoundResourceArray()); ret.back().bindPoint = Bindpoint(set, slot); - rdcarray &val = ret.back().resources; - val.resize(bind.descriptorCount); + uint32_t count = bind.descriptorCount; + uint32_t firstIdx = 0; + if(onlyUsed) + { + firstIdx = (uint32_t)bind.firstUsedIndex; + if(bind.dynamicallyUsedCount < count) + count = bind.dynamicallyUsedCount; + if((uint32_t)bind.lastUsedIndex < count) + count = uint32_t(bind.lastUsedIndex - bind.firstUsedIndex + 1); + } + + rdcarray &val = ret.back().resources; + val.reserve(count); + + ret.back().firstIndex = (int32_t)firstIdx; ret.back().dynamicallyUsedCount = bind.dynamicallyUsedCount; - for(uint32_t i = 0; i < bind.descriptorCount; i++) + BoundResource res; + for(uint32_t i = firstIdx; i < firstIdx + count; i++) { - val[i].resourceId = bind.binds[i].resourceResourceId; - val[i].dynamicallyUsed = bind.binds[i].dynamicallyUsed; - val[i].firstMip = (int)bind.binds[i].firstMip; - val[i].firstSlice = (int)bind.binds[i].firstSlice; - val[i].typeCast = bind.binds[i].viewFormat.compType; + res.resourceId = bind.binds[i].resourceResourceId; + res.dynamicallyUsed = bind.binds[i].dynamicallyUsed; + res.firstMip = (int)bind.binds[i].firstMip; + res.firstSlice = (int)bind.binds[i].firstSlice; + res.typeCast = bind.binds[i].viewFormat.compType; + val.push_back(res); } } } @@ -1275,7 +1290,7 @@ rdcarray PipeState::GetReadOnlyResources(ShaderStage stage) return ret; } -rdcarray PipeState::GetReadWriteResources(ShaderStage stage) const +rdcarray PipeState::GetReadWriteResources(ShaderStage stage, bool onlyUsed) const { rdcarray ret; @@ -1417,18 +1432,33 @@ rdcarray PipeState::GetReadWriteResources(ShaderStage stage) ret.push_back(BoundResourceArray()); ret.back().bindPoint = Bindpoint(set, slot); - rdcarray &val = ret.back().resources; - val.resize(bind.descriptorCount); + uint32_t count = bind.descriptorCount; + uint32_t firstIdx = 0; + if(onlyUsed) + { + firstIdx = (uint32_t)bind.firstUsedIndex; + if(bind.dynamicallyUsedCount < count) + count = bind.dynamicallyUsedCount; + if((uint32_t)bind.lastUsedIndex < count) + count = uint32_t(bind.lastUsedIndex - bind.firstUsedIndex + 1); + } + + rdcarray &val = ret.back().resources; + val.reserve(count); + + ret.back().firstIndex = (int32_t)firstIdx; ret.back().dynamicallyUsedCount = bind.dynamicallyUsedCount; - for(uint32_t i = 0; i < bind.descriptorCount; i++) + BoundResource res; + for(uint32_t i = firstIdx; i < firstIdx + count; i++) { - val[i].resourceId = bind.binds[i].resourceResourceId; - val[i].dynamicallyUsed = bind.binds[i].dynamicallyUsed; - val[i].firstMip = (int)bind.binds[i].firstMip; - val[i].firstSlice = (int)bind.binds[i].firstSlice; - val[i].typeCast = bind.binds[i].viewFormat.compType; + res.resourceId = bind.binds[i].resourceResourceId; + res.dynamicallyUsed = bind.binds[i].dynamicallyUsed; + res.firstMip = (int)bind.binds[i].firstMip; + res.firstSlice = (int)bind.binds[i].firstSlice; + res.typeCast = bind.binds[i].viewFormat.compType; + val.push_back(res); } } } diff --git a/renderdoc/api/replay/vk_pipestate.h b/renderdoc/api/replay/vk_pipestate.h index 583f39c3b..22832f860 100644 --- a/renderdoc/api/replay/vk_pipestate.h +++ b/renderdoc/api/replay/vk_pipestate.h @@ -246,7 +246,23 @@ redundant iteration to determine whether any bindings are present. For more information see :data:`VKBindingElement.dynamicallyUsed`. )"); - uint32_t dynamicallyUsedCount = 0; + uint32_t dynamicallyUsedCount = ~0U; + DOCUMENT(R"(Gives the index of the first binding in :data:`binds` that is dynamically used. Useful +to avoid redundant iteration in very large descriptor arrays with a small subset that are used. + +For more information see :data:`VKBindingElement.dynamicallyUsed`. +)"); + int32_t firstUsedIndex = 0; + DOCUMENT(R"(Gives the index of the first binding in :data:`binds` that is dynamically used. Useful +to avoid redundant iteration in very large descriptor arrays with a small subset that are used. + +.. note:: + This may be set to a higher value than the number of bindings, if no dynamic use information is + available. Ensure that this is an additional check on the bind and the count is still respected. + +For more information see :data:`VKBindingElement.dynamicallyUsed`. +)"); + int32_t lastUsedIndex = 0x7fffffff; DOCUMENT("The :class:`BindType` of this binding."); BindType type = BindType::Unknown; DOCUMENT("The :class:`ShaderStageMask` where this binding is visible."); diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 70041b4ce..8254b43c7 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -1036,7 +1036,20 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) VkMarkerRegion::End(); - m_VulkanPipelineState = VKPipe::State(); + { + // reset the pipeline state, but keep the descriptor set arrays. This prevents needless + // reallocations, we'll ensure that descriptors are fully overwritten below. + rdcarray graphicsDescriptors; + rdcarray computeDescriptors; + + m_VulkanPipelineState.graphics.descriptorSets.swap(graphicsDescriptors); + m_VulkanPipelineState.compute.descriptorSets.swap(computeDescriptors); + + m_VulkanPipelineState = VKPipe::State(); + + m_VulkanPipelineState.graphics.descriptorSets.swap(graphicsDescriptors); + m_VulkanPipelineState.compute.descriptorSets.swap(computeDescriptors); + } m_VulkanPipelineState.pushconsts.resize(state.pushConstSize); memcpy(m_VulkanPipelineState.pushconsts.data(), state.pushconsts, state.pushConstSize); @@ -1668,11 +1681,18 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) default: dst.bindings[b].type = BindType::Unknown; RDCERR("Unexpected descriptor type"); } + dst.bindings[b].firstUsedIndex = -1; + dst.bindings[b].lastUsedIndex = -1; + dst.bindings[b].dynamicallyUsedCount = 0; + dst.bindings[b].binds.resize(dst.bindings[b].descriptorCount); for(uint32_t a = 0; a < dst.bindings[b].descriptorCount; a++) { VKPipe::BindingElement &dstel = dst.bindings[b].binds[a]; + // clear it so we don't have to manually reset all elements back to normal + memset(&dstel, 0, sizeof(dstel)); + curBind.arrayIndex = a; // if we have a list of used binds, and this is an array descriptor (so would be @@ -1697,22 +1717,34 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) } // the next used bind is equal to this one. Mark it as dynamically used, and consume - if(curBind == *usedBindsData) + if(usedBindsSize > 0 && curBind == *usedBindsData) { dstel.dynamicallyUsed = true; usedBindsData++; usedBindsSize--; } // the next used bind is after the current one, this is not used. - else if(curBind < *usedBindsData) + else if(usedBindsSize > 0 && curBind < *usedBindsData) { dstel.dynamicallyUsed = false; } } } + else + { + dstel.dynamicallyUsed = true; + } if(dstel.dynamicallyUsed) + { dst.bindings[b].dynamicallyUsedCount++; + // we iterate in forward order, so we can unconditinoally set the last bind to the + // current one, and only set the first bind if we haven't encountered one before + dst.bindings[b].lastUsedIndex = a; + + if(dst.bindings[b].firstUsedIndex < 0) + dst.bindings[b].firstUsedIndex = a; + } // first handle the sampler separately because it might be in a combined descriptor if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || @@ -1893,6 +1925,13 @@ void VulkanReplay::SavePipelineState(uint32_t eventId) dst.bindings[b].binds[a].byteSize = info[a].bufferInfo.range; } } + + // if no bindings were set these will still be negative. Set them to something sensible. + if(dst.bindings[b].firstUsedIndex < 0) + { + dst.bindings[b].firstUsedIndex = 0; + dst.bindings[b].lastUsedIndex = 0x7fffffff; + } } } } diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index 42c376115..cf7bf880f 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -1896,12 +1896,14 @@ void DoSerialise(SerialiserType &ser, VKPipe::DescriptorBinding &el) { SERIALISE_MEMBER(descriptorCount); SERIALISE_MEMBER(dynamicallyUsedCount); + SERIALISE_MEMBER(firstUsedIndex); + SERIALISE_MEMBER(lastUsedIndex); SERIALISE_MEMBER(type); SERIALISE_MEMBER(stageFlags); SERIALISE_MEMBER(binds); - SIZE_CHECK(40); + SIZE_CHECK(48); } template