From 2fe13e8cdf1e08af002421950314d1e6c2742849 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 18 Jun 2025 17:04:58 +0100 Subject: [PATCH] Implement resource usage tracking by versioned readback --- renderdoc/driver/vulkan/vk_common.cpp | 21 ++ renderdoc/driver/vulkan/vk_common.h | 1 + renderdoc/driver/vulkan/vk_core.cpp | 242 ++++++++++++++++-- renderdoc/driver/vulkan/vk_core.h | 29 +++ renderdoc/driver/vulkan/vk_info.cpp | 21 +- .../driver/vulkan/vk_shader_feedback.cpp | 21 -- renderdoc/driver/vulkan/vk_state.h | 1 + .../driver/vulkan/wrappers/vk_cmd_funcs.cpp | 152 ++++++++++- .../driver/vulkan/wrappers/vk_queue_funcs.cpp | 28 +- .../driver/vulkan/wrappers/vk_sync_funcs.cpp | 21 ++ 10 files changed, 460 insertions(+), 77 deletions(-) diff --git a/renderdoc/driver/vulkan/vk_common.cpp b/renderdoc/driver/vulkan/vk_common.cpp index 8d7976e04..1d7da6921 100644 --- a/renderdoc/driver/vulkan/vk_common.cpp +++ b/renderdoc/driver/vulkan/vk_common.cpp @@ -700,6 +700,27 @@ void DoPipelineBarrier(VkCommandBuffer cmd, size_t count, const VkMemoryBarrier 0, NULL); // image memory barriers } +VkDescriptorType MakeVkDescriptorType(DescriptorType type, bool inputAttachment) +{ + switch(type) + { + case DescriptorType::Unknown: return VK_DESCRIPTOR_TYPE_MAX_ENUM; + case DescriptorType::Buffer: return VK_DESCRIPTOR_TYPE_MAX_ENUM; + case DescriptorType::ConstantBuffer: return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + case DescriptorType::Sampler: return VK_DESCRIPTOR_TYPE_SAMPLER; + case DescriptorType::ImageSampler: return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + case DescriptorType::Image: + return inputAttachment ? VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT : VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + case DescriptorType::TypedBuffer: return VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + case DescriptorType::ReadWriteImage: return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + case DescriptorType::ReadWriteTypedBuffer: return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; + case DescriptorType::ReadWriteBuffer: return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + case DescriptorType::AccelerationStructure: + return VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; + } + return VK_DESCRIPTOR_TYPE_MAX_ENUM; +} + Topology MakePrimitiveTopology(VkPrimitiveTopology Topo, uint32_t patchControlPoints) { switch(Topo) diff --git a/renderdoc/driver/vulkan/vk_common.h b/renderdoc/driver/vulkan/vk_common.h index af63a403b..3ada6d2fa 100644 --- a/renderdoc/driver/vulkan/vk_common.h +++ b/renderdoc/driver/vulkan/vk_common.h @@ -106,6 +106,7 @@ typedef VkPhysicalDeviceBufferDeviceAddressFeatures VkPhysicalDeviceBufferDevice ResourceFormat MakeResourceFormat(VkFormat fmt); VkFormat MakeVkFormat(ResourceFormat fmt); +VkDescriptorType MakeVkDescriptorType(DescriptorType type, bool inputAttachment); Topology MakePrimitiveTopology(VkPrimitiveTopology Topo, uint32_t patchControlPoints); VkPrimitiveTopology MakeVkPrimitiveTopology(Topology Topo); AddressMode MakeAddressMode(VkSamplerAddressMode addr); diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 44e2fe14c..f3bfe2787 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -7091,7 +7091,6 @@ void WrappedVulkan::AddUsage(VulkanActionTreeNode &actionNode, rdcarray shaderStages; + if(pipeState.UsingDescBufs()) + { + actionNode.deferredResourceUsage.push_back({}); + + VulkanActionTreeNode::DeferredResourceUsage &def = actionNode.deferredResourceUsage.back(); + + def.descBufVersionIdx = m_BakedCmdBufferInfo[m_LastCmdBufferID].descBufVersionIdx; + def.pipeline = pipeState.shaderObject ? ResourceId() : pipeState.pipeline; + if(pipeState.shaderObject) + memcpy(def.shaderObjects, state.shaderObjects, sizeof(state.shaderObjects)); + def.descSets = pipeState.descSets; + + bool usesPush = false; + + // bake the recorded descriptor buffer offsets in so we don't have to track them separately + for(VulkanStatePipeline::DescriptorAndOffsets &desc : def.descSets) + { + if(desc.push) + { + usesPush = true; + continue; + } + + // gaps in descriptor sets are possible + if(desc.descBufferIdx == ~0U) + continue; + + desc.descBufferOffset += + m_BakedCmdBufferInfo[m_LastCmdBufferID].descBufOffsets[desc.descBufferIdx]; + } + + if(!usesPush) + return; + } + + AddUsageForDescriptorSets(actionNode, debugMessages); +} + +static rdcarray ShaderStagesForAction(ActionDescription &action) +{ if(action.flags & ActionFlags::Dispatch) - { - shaderStages = {5}; - } + return {5}; else if(action.flags & ActionFlags::Drawcall) - { - shaderStages = {0, 1, 2, 3, 4}; - } + return {0, 1, 2, 3, 4}; else if(action.flags & ActionFlags::MeshDispatch) + return {4, 6, 7}; + return {}; +} + +void WrappedVulkan::AddUsageForDescriptorBuffers(VulkanActionTreeNode &actionNode, + rdcarray &debugMessages, + const VulkanActionTreeNode::DeferredResourceUsage &def) +{ + if(def.descBufVersionIdx >= m_DescriptorBufferVersions.size()) { - shaderStages = {4, 6, 7}; + RDCERR("Invalid deferred resource usage buffer reference"); + return; } + ActionDescription &action = actionNode.action; + + VulkanCreationInfo &c = m_CreationInfo; + + rdcarray shaderStages = ShaderStagesForAction(action); + + GPUBuffer &buf = m_DescriptorBufferVersions[def.descBufVersionIdx]; + + byte *descriptorBytes = (byte *)buf.Map(); + for(int shad : shaderStages) { - bool compute = (shad == 5); - ResourceId pipe = (compute ? state.compute.pipeline : state.graphics.pipeline); + ResourceId pipe = def.pipeline; + bool shaderObject = pipe == ResourceId(); - bool shaderObject = (compute ? state.compute.shaderObject : state.graphics.shaderObject); + VulkanCreationInfo::ShaderEntry &sh = shaderObject + ? c.m_ShaderObject[def.shaderObjects[shad]].shad + : c.m_Pipeline[pipe].shaders[shad]; + if(sh.module == ResourceId()) + continue; + + ResourceId origPipe = GetResourceManager()->GetOriginalID(pipe); + ResourceId origShad = GetResourceManager()->GetOriginalID(sh.module); + + for(const ConstantBlock &constantBlock : sh.refl->constantBlocks) + { + // ignore push constants + if(!constantBlock.bufferBacked) + continue; + + AddUsageForDescriptorBufferBind( + actionNode, debugMessages, def, descriptorBytes, + DescriptorDataSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER), DescriptorType::ConstantBuffer, + constantBlock.fixedBindSetOrSpace, constantBlock.fixedBindNumber, + ResourceUsage(uint32_t(ResourceUsage::VS_Constants) + shad)); + } + + for(const ShaderResource &res : sh.refl->readOnlyResources) + { + AddUsageForDescriptorBufferBind( + actionNode, debugMessages, def, descriptorBytes, + DescriptorDataSize(MakeVkDescriptorType(res.descriptorType, res.isInputAttachment)), + res.descriptorType, res.fixedBindSetOrSpace, res.fixedBindNumber, + ResourceUsage(uint32_t(ResourceUsage::VS_Resource) + shad)); + } + + for(const ShaderResource &res : sh.refl->readWriteResources) + { + AddUsageForDescriptorBufferBind( + actionNode, debugMessages, def, descriptorBytes, + DescriptorDataSize(MakeVkDescriptorType(res.descriptorType, false)), res.descriptorType, + res.fixedBindSetOrSpace, res.fixedBindNumber, + ResourceUsage(uint32_t(ResourceUsage::VS_RWResource) + shad)); + } + } + + buf.Unmap(); +} + +void WrappedVulkan::AddUsageForDescriptorBufferBind( + VulkanActionTreeNode &actionNode, rdcarray &debugMessages, + const VulkanActionTreeNode::DeferredResourceUsage &def, byte *descriptorBytes, + size_t descriptorSize, DescriptorType type, uint32_t bindset, uint32_t bind, ResourceUsage usage) +{ + static bool hugeRangeWarned = false; + uint32_t eid = actionNode.action.eventId; + + const rdcarray &descSets = def.descSets; + + VulkanCreationInfo &c = m_CreationInfo; + + DebugMessage msg; + msg.eventId = eid; + msg.category = MessageCategory::Execution; + msg.messageID = 0; + msg.source = MessageSource::IncorrectAPIUse; + msg.severity = MessageSeverity::High; + + if(bindset >= descSets.size() || !descSets[bindset].IsBound()) + { + msg.description = + StringFormat::Fmt("Shader referenced a descriptor set %i that was not bound", bindset); + debugMessages.push_back(msg); + return; + } + + // ignore push sets, these were handled normally + if(descSets[bindset].push) + return; + + const VulkanCreationInfo::PipelineLayout &pipeLayout = + c.m_PipelineLayout[descSets[bindset].pipeLayout]; + const DescSetLayout &layout = c.m_DescSetLayout[pipeLayout.descSetLayouts[bindset]]; + + if(layout.bindings.empty()) + { + msg.description = + StringFormat::Fmt("Shader referenced a descriptor set %i that was not bound", bindset); + debugMessages.push_back(msg); + return; + } + + if(bind >= layout.bindings.size()) + { + msg.description = StringFormat::Fmt( + "Shader referenced a bind %i in descriptor set %i that does not exist. Mismatched " + "descriptor set?", + bind, bindset); + debugMessages.push_back(msg); + return; + } + + // no object to mark for usage with inline blocks + if(layout.bindings[bind].layoutDescType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) + return; + + uint32_t descriptorCount = layout.bindings[bind].descriptorCount; + // completely skip variable size or arrayed bindings as it is too spammy to look up uninitialised + // descriptors and there is a chance of false positives + if(layout.bindings[bind].variableSize || descriptorCount > 1) + return; + + for(uint32_t a = 0; a < descriptorCount; a++) + { + DescriptorSetSlot tmp = {}; + LookupDescriptor(descriptorBytes + descSets[bindset].descBufferOffset + + layout.bindings[bind].elemOffset + descriptorSize * a, + descriptorSize, type, tmp); + + AddUsageForDescriptor(actionNode, tmp, usage); + } +} + +void WrappedVulkan::AddUsageForDescriptorSets(VulkanActionTreeNode &actionNode, + rdcarray &debugMessages) +{ + ActionDescription &action = actionNode.action; + + const VulkanRenderState &state = m_BakedCmdBufferInfo[m_LastCmdBufferID].state; + const VulkanStatePipeline &pipeState = + (action.flags & ActionFlags::Dispatch ? state.compute : state.graphics); + VulkanCreationInfo &c = m_CreationInfo; + + rdcarray shaderStages = ShaderStagesForAction(action); + + for(int shad : shaderStages) + { + ResourceId pipe = pipeState.pipeline; + bool shaderObject = pipeState.shaderObject; VulkanCreationInfo::ShaderEntry &sh = shaderObject ? c.m_ShaderObject[state.shaderObjects[shad]].shad @@ -7185,12 +7382,6 @@ void WrappedVulkan::AddUsage(VulkanActionTreeNode &actionNode, rdcarray= descSets.size() || !descSets[bindset].IsBound()) { - // can't generate usage for descriptor buffers - if(!state.descBufs.empty()) - return; - msg.description = StringFormat::Fmt("Shader referenced a descriptor set %i that was not bound", bindset); debugMessages.push_back(msg); return; } - DescriptorSetInfo &descset = m_DescriptorSetState[descSets[bindset].descSet]; - DescSetLayout &layout = c.m_DescSetLayout[descset.layout]; + // can't generate usage for descriptor buffers + if(descSets[bindset].descBufferIdx != ~0U) + return; - ResourceId layoutId = GetResourceManager()->GetOriginalID(descset.layout); + const DescriptorSetInfo &descset = m_DescriptorSetState[descSets[bindset].descSet]; + const DescSetLayout &layout = c.m_DescSetLayout[descset.layout]; if(layout.bindings.empty()) { @@ -7314,7 +7503,8 @@ void WrappedVulkan::AddUsageForDescriptor(VulkanActionTreeNode &actionNode, break; case DescriptorSlotType::UniformTexelBuffer: case DescriptorSlotType::StorageTexelBuffer: - if(slot.resource != ResourceId()) + id = slot.resource; + if(c.m_BufferView.find(slot.resource) != c.m_BufferView.end()) id = c.m_BufferView[slot.resource].buffer; break; case DescriptorSlotType::UniformBuffer: diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index 14d789c02..7306fbe2f 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -143,6 +143,15 @@ struct VulkanActionTreeNode rdcarray executedCmds; + struct DeferredResourceUsage + { + uint32_t descBufVersionIdx; + ResourceId pipeline; + ResourceId shaderObjects[NumShaderStages]; + rdcarray descSets; + }; + rdcarray deferredResourceUsage; + VulkanActionTreeNode &operator=(const ActionDescription &a) { *this = VulkanActionTreeNode(a); @@ -840,6 +849,11 @@ private: uint32_t eventCount; // how many events are in this cmd buffer, for quick skipping uint32_t curEventID; // current event ID while reading or executing uint32_t actionCount; // similar to above + + // the index in m_DescriptorBufferVersions for the current GPUBuffer containing the descriptor buffer snapshot + uint32_t descBufVersionIdx = ~0U; + // when multiple buffers are bound, the offsets of each in the single GPUBuffer where they are + rdcarray descBufOffsets; }; uint64_t m_FakePushSetID = 0; @@ -1071,6 +1085,9 @@ private: // immutable creation data VulkanCreationInfo m_CreationInfo; + rdcarray m_DescriptorBufferVersions; + void VersionDescriptorBuffers(VkCommandBuffer cmd); + std::map> m_ResourceUses; std::map m_EventFlags; rdcarray m_FeedbackRPs; @@ -1239,9 +1256,21 @@ private: void AddEvent(); void AddUsage(VulkanActionTreeNode &actionNode, rdcarray &debugMessages); + + void AddUsageForDescriptorSets(VulkanActionTreeNode &actionNode, + rdcarray &debugMessages); void AddUsageForDescriptorSetBind(VulkanActionTreeNode &actionNode, rdcarray &debugMessages, uint32_t bindset, uint32_t bind, ResourceUsage usage); + void AddUsageForDescriptorBuffers(VulkanActionTreeNode &actionNode, + rdcarray &debugMessages, + const VulkanActionTreeNode::DeferredResourceUsage &def); + void AddUsageForDescriptorBufferBind(VulkanActionTreeNode &actionNode, + rdcarray &debugMessages, + const VulkanActionTreeNode::DeferredResourceUsage &def, + byte *descriptorBytes, size_t descriptorSize, + DescriptorType type, uint32_t bindset, uint32_t bind, + ResourceUsage usage); void AddUsageForDescriptor(VulkanActionTreeNode &actionNode, const DescriptorSetSlot &slot, ResourceUsage usage); diff --git a/renderdoc/driver/vulkan/vk_info.cpp b/renderdoc/driver/vulkan/vk_info.cpp index 11f8d4afe..d00622019 100644 --- a/renderdoc/driver/vulkan/vk_info.cpp +++ b/renderdoc/driver/vulkan/vk_info.cpp @@ -1016,17 +1016,8 @@ void ProcessStaticDescriptorAccess(VulkanResourceManager *resourceMan, ShaderRef VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT) { access.descriptorStore = VulkanCreationInfo::descriptorBufferStorage[bind.fixedBindSetOrSpace]; - if(bind.isInputAttachment) - access.byteSize = resourceMan->DescriptorDataSize(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT); - else if(bind.hasSampler) - access.byteSize = resourceMan->DescriptorDataSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); - else if(bind.isTexture) - access.byteSize = resourceMan->DescriptorDataSize(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); - else if(bind.descriptorType == DescriptorType::AccelerationStructure) - access.byteSize = - resourceMan->DescriptorDataSize(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR); - else - access.byteSize = resourceMan->DescriptorDataSize(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER); + access.byteSize = resourceMan->DescriptorDataSize( + MakeVkDescriptorType(bind.descriptorType, bind.isInputAttachment)); // we are only handling non-arrays here access.byteOffset = setLayout->bindings[bind.fixedBindNumber].elemOffset; @@ -1073,12 +1064,8 @@ void ProcessStaticDescriptorAccess(VulkanResourceManager *resourceMan, ShaderRef VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT) { access.descriptorStore = VulkanCreationInfo::descriptorBufferStorage[bind.fixedBindSetOrSpace]; - if(bind.isTexture) - access.byteSize = resourceMan->DescriptorDataSize(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE); - else if(bind.descriptorType == DescriptorType::ReadWriteTypedBuffer) - access.byteSize = resourceMan->DescriptorDataSize(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER); - else - access.byteSize = resourceMan->DescriptorDataSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); + access.byteSize = + resourceMan->DescriptorDataSize(MakeVkDescriptorType(bind.descriptorType, false)); // we are only handling non-arrays here access.byteOffset = setLayout->bindings[bind.fixedBindNumber].elemOffset; diff --git a/renderdoc/driver/vulkan/vk_shader_feedback.cpp b/renderdoc/driver/vulkan/vk_shader_feedback.cpp index 61bf4812b..ad034d546 100644 --- a/renderdoc/driver/vulkan/vk_shader_feedback.cpp +++ b/renderdoc/driver/vulkan/vk_shader_feedback.cpp @@ -45,27 +45,6 @@ RDOC_CONFIG(uint32_t, Vulkan_Debug_PrintfBufferSize, 64 * 1024, static const uint32_t ShaderStageHeaderBitShift = 28U; static const uint32_t ShaderFeedbackReservedBindings = 1; -VkDescriptorType MakeVkDescriptorType(DescriptorType type, bool inputAttachment) -{ - switch(type) - { - case DescriptorType::Unknown: return VK_DESCRIPTOR_TYPE_MAX_ENUM; - case DescriptorType::Buffer: return VK_DESCRIPTOR_TYPE_MAX_ENUM; - case DescriptorType::ConstantBuffer: return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - case DescriptorType::Sampler: return VK_DESCRIPTOR_TYPE_SAMPLER; - case DescriptorType::ImageSampler: return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - case DescriptorType::Image: - return inputAttachment ? VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT : VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - case DescriptorType::TypedBuffer: return VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; - case DescriptorType::ReadWriteImage: return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - case DescriptorType::ReadWriteTypedBuffer: return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; - case DescriptorType::ReadWriteBuffer: return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - case DescriptorType::AccelerationStructure: - return VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; - } - return VK_DESCRIPTOR_TYPE_MAX_ENUM; -} - struct BindKey { bool operator<(const BindKey &o) const diff --git a/renderdoc/driver/vulkan/vk_state.h b/renderdoc/driver/vulkan/vk_state.h index 25e6145e4..867276ef7 100644 --- a/renderdoc/driver/vulkan/vk_state.h +++ b/renderdoc/driver/vulkan/vk_state.h @@ -45,6 +45,7 @@ struct VulkanStatePipeline struct DescriptorAndOffsets { ResourceId pipeLayout; + bool push = false; // if descriptor set bound ResourceId descSet; diff --git a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp index c7566740d..884ba0a80 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp @@ -55,6 +55,62 @@ static rdcstr ToHumanStr(const VkAttachmentStoreOp &el) END_ENUM_STRINGISE(); } +void WrappedVulkan::VersionDescriptorBuffers(VkCommandBuffer cmd) +{ + VulkanRenderState &renderstate = m_BakedCmdBufferInfo[m_LastCmdBufferID].state; + uint32_t &version = m_BakedCmdBufferInfo[m_LastCmdBufferID].descBufVersionIdx; + rdcarray &offsets = m_BakedCmdBufferInfo[m_LastCmdBufferID].descBufOffsets; + + uint64_t neededBytes = 0; + + for(uint32_t i = 0; i < renderstate.descBufs.size(); i++) + { + offsets.push_back(neededBytes); + + ResourceId id; + uint64_t offs; + GetResIDFromAddr(renderstate.descBufs[i].address, id, offs); + + neededBytes += m_CreationInfo.m_Buffer[id].size - offs; + } + + uint32_t nextUnusedVersion = (version == ~0U ? 0 : version + 1); + version = ~0U; + for(uint32_t ver = nextUnusedVersion; ver < m_DescriptorBufferVersions.size(); ver++) + { + if(m_DescriptorBufferVersions[ver].TotalSize() >= neededBytes) + { + // use this version and copy into it + version = ver; + break; + } + } + + if(version == ~0U) + { + version = (uint32_t)m_DescriptorBufferVersions.size(); + m_DescriptorBufferVersions.push_back(GPUBuffer()); + m_DescriptorBufferVersions.back().Create(this, m_Device, neededBytes, 1, + GPUBuffer::eGPUBufferReadback); + } + + for(uint32_t i = 0; i < renderstate.descBufs.size(); i++) + { + ResourceId id; + uint64_t offs; + GetResIDFromAddr(renderstate.descBufs[i].address, id, offs); + + const VkBufferCopy region = { + offs, + offsets[i], + m_CreationInfo.m_Buffer[id].size - offs, + }; + ObjDisp(cmd)->CmdCopyBuffer(Unwrap(cmd), + Unwrap(GetResourceManager()->GetCurrentHandle(id)), + m_DescriptorBufferVersions[version].UnwrappedBuffer(), 1, ®ion); + } +} + void WrappedVulkan::AddImplicitResolveResourceUsage(uint32_t subpass) { ResourceId rp = m_BakedCmdBufferInfo[m_LastCmdBufferID].state.GetRenderPass(); @@ -3572,6 +3628,7 @@ bool WrappedVulkan::Serialise_vkCmdBindDescriptorSets( { descsets[firstSet + i].pipeLayout = GetResID(layout); descsets[firstSet + i].descSet = GetResID(pDescriptorSets[i]); + descsets[firstSet + i].push = false; descsets[firstSet + i].offsets.clear(); if(descSetLayouts[firstSet + i] == ResourceId()) @@ -3593,12 +3650,22 @@ bool WrappedVulkan::Serialise_vkCmdBindDescriptorSets( rdcarray &descsets = m_BakedCmdBufferInfo[m_LastCmdBufferID].state.GetPipeline(pipelineBindPoint).descSets; + // descriptor buffers and descriptor sets can't co-exist, each invalidates the other + if(m_BakedCmdBufferInfo[m_LastCmdBufferID].state.GetPipeline(pipelineBindPoint).UsingDescBufs()) + { + m_BakedCmdBufferInfo[m_LastCmdBufferID].state.descBufs.clear(); + descsets.clear(); + } + // expand as necessary if(descsets.size() < firstSet + setCount) descsets.resize(firstSet + setCount); for(uint32_t i = 0; i < setCount; i++) + { descsets[firstSet + i].descSet = GetResID(pDescriptorSets[i]); + descsets[firstSet + i].push = false; + } ObjDisp(commandBuffer) ->CmdBindDescriptorSets(Unwrap(commandBuffer), pipelineBindPoint, Unwrap(layout), @@ -4371,6 +4438,24 @@ bool WrappedVulkan::Serialise_vkCmdPipelineBarrier2(SerialiserType &ser, if(commandBuffer != VK_NULL_HANDLE) { + if(IsLoading(m_State)) + { + bool descBarrier = false; + + for(uint32_t i = 0; i < DependencyInfo.bufferMemoryBarrierCount; i++) + if(DependencyInfo.pBufferMemoryBarriers[i].dstAccessMask & + VK_ACCESS_2_DESCRIPTOR_BUFFER_READ_BIT_EXT) + descBarrier = true; + + for(uint32_t i = 0; i < DependencyInfo.memoryBarrierCount; i++) + if(DependencyInfo.pMemoryBarriers[i].dstAccessMask & + VK_ACCESS_2_DESCRIPTOR_BUFFER_READ_BIT_EXT) + descBarrier = true; + + if(descBarrier) + VersionDescriptorBuffers(commandBuffer); + } + GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[m_LastCmdBufferID].imageStates, FindCommandQueueFamily(m_LastCmdBufferID), (uint32_t)imgBarriers.size(), imgBarriers.data()); @@ -5612,6 +5697,7 @@ bool WrappedVulkan::Serialise_vkCmdPushDescriptorSetKHR(SerialiserType &ser, descsets[set].pipeLayout = GetResID(layout); descsets[set].descSet = setId; + descsets[set].push = true; } // actual replay of the command will happen below @@ -5634,6 +5720,7 @@ bool WrappedVulkan::Serialise_vkCmdPushDescriptorSetKHR(SerialiserType &ser, // we use a 'special' ID for the push descriptor at this index, since there's no actual // allocated object corresponding to it. descsets[set].descSet = setId; + descsets[set].push = true; } if(commandBuffer != VK_NULL_HANDLE) @@ -5985,6 +6072,7 @@ bool WrappedVulkan::Serialise_vkCmdPushDescriptorSetWithTemplateKHR( descsets[set].pipeLayout = GetResID(layout); descsets[set].descSet = setId; + descsets[set].push = true; } // actual replay of the command will happen below @@ -6007,6 +6095,7 @@ bool WrappedVulkan::Serialise_vkCmdPushDescriptorSetWithTemplateKHR( // we use a 'special' ID for the push descriptor at this index, since there's no actual // allocated object corresponding to it. descsets[set].descSet = setId; + descsets[set].push = true; } if(commandBuffer != VK_NULL_HANDLE) @@ -8766,9 +8855,32 @@ bool WrappedVulkan::Serialise_vkCmdBindDescriptorBuffersEXT( } else { - // track while reading, as while we can't track resource usage for descriptor buffers we want - // to know we're using them - m_BakedCmdBufferInfo[m_LastCmdBufferID].state.descBufs.resize(bufferCount); + // track while reading, for resource usage + { + VulkanRenderState &renderstate = m_BakedCmdBufferInfo[m_LastCmdBufferID].state; + + // all descriptor buffers above bufferCount are unbound + renderstate.descBufs.resize(bufferCount); + for(uint32_t i = 0; i < bufferCount; i++) + renderstate.descBufs[i].address = pBindingInfos[i].address; + + // any offsets that refer to these buffers are invalidated, but then also other buffers + // are unbound meaning those are invalid - we can clear all bindings that refer to + // descriptor buffers however normal descriptor sets must remain as they are *not* + // invalidated and could still be used + for(VkPipelineBindPoint bindPoint : + {VK_PIPELINE_BIND_POINT_GRAPHICS, VK_PIPELINE_BIND_POINT_COMPUTE, + VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR}) + { + VulkanStatePipeline &pipe = renderstate.GetPipeline(bindPoint); + + for(uint32_t i = 0; i < pipe.descSets.size(); i++) + if(pipe.descSets[i].descSet == ResourceId()) + pipe.descSets[i] = {}; + } + + VersionDescriptorBuffers(commandBuffer); + } ObjDisp(commandBuffer) ->CmdBindDescriptorBuffersEXT(Unwrap(commandBuffer), bufferCount, unwrappedInfos.data()); @@ -8874,13 +8986,35 @@ bool WrappedVulkan::Serialise_vkCmdSetDescriptorBufferOffsetsEXT( } else { - // track while reading, as while we can't track resource usage for descriptor buffers we want - // to know we're using them - for(VkPipelineBindPoint bindPoint : - {VK_PIPELINE_BIND_POINT_GRAPHICS, VK_PIPELINE_BIND_POINT_COMPUTE, - VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR}) + // track while reading, for resource usage { - m_BakedCmdBufferInfo[m_LastCmdBufferID].state.GetPipeline(bindPoint).descSets.clear(); + VulkanRenderState &renderstate = m_BakedCmdBufferInfo[m_LastCmdBufferID].state; + VulkanStatePipeline &pipeline = renderstate.GetPipeline(pipelineBindPoint); + + pipeline.lastBoundSet = firstSet; + + // descriptor set bindings are overwritten/cleared by descriptor buffer bindings + for(uint32_t set = 0; set < setCount; set++) + { + pipeline.descSets.resize_for_index(firstSet + set); + + pipeline.descSets[firstSet + set].pipeLayout = GetResID(layout); + pipeline.descSets[firstSet + set].descBufferIdx = pBufferIndices[set]; + pipeline.descSets[firstSet + set].descBufferOffset = pOffsets[set]; + pipeline.descSets[firstSet + set].descBufferEmbeddedSamplers = false; + } + + // any normal descriptor set bindings are invalidated + for(VkPipelineBindPoint bindPoint : + {VK_PIPELINE_BIND_POINT_GRAPHICS, VK_PIPELINE_BIND_POINT_COMPUTE, + VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR}) + { + VulkanStatePipeline &pipe = renderstate.GetPipeline(bindPoint); + + for(uint32_t i = 0; i < pipe.descSets.size(); i++) + if(pipe.descSets[i].descSet != ResourceId()) + pipe.descSets[i] = {}; + } } ObjDisp(commandBuffer) diff --git a/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp index 2602c60aa..cb21f2b0d 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp @@ -281,8 +281,6 @@ void WrappedVulkan::ReplayQueueSubmit(VkQueue queue, VkSubmitInfo2 submitInfo, r { if(IsLoading(m_State)) { - DoSubmit(queue, submitInfo); - AddEvent(); // we're adding multiple events, need to increment ourselves @@ -290,6 +288,8 @@ void WrappedVulkan::ReplayQueueSubmit(VkQueue queue, VkSubmitInfo2 submitInfo, r if(submitInfo.commandBufferInfoCount == 0) { + DoSubmit(queue, submitInfo); + rdcstr name = StringFormat::Fmt("=> %s: No Command Buffers", basename.c_str()); ActionDescription action; @@ -304,10 +304,18 @@ void WrappedVulkan::ReplayQueueSubmit(VkQueue queue, VkSubmitInfo2 submitInfo, r m_RootEventID++; } - for(uint32_t c = 0; c < submitInfo.commandBufferInfoCount; c++) + // submit command buffers one by one + uint32_t numCmds = submitInfo.commandBufferInfoCount; + submitInfo.commandBufferInfoCount = 1; + for(uint32_t c = 0; c < numCmds; c++) { + DoSubmit(queue, submitInfo); + FlushQ(); + ResourceId cmd = GetResourceManager()->GetOriginalID( - GetResID(submitInfo.pCommandBufferInfos[c].commandBuffer)); + GetResID(submitInfo.pCommandBufferInfos[0].commandBuffer)); + + submitInfo.pCommandBufferInfos++; BakedCmdBufferInfo &cmdBufInfo = m_BakedCmdBufferInfo[cmd]; @@ -637,6 +645,18 @@ void WrappedVulkan::InsertActionsAndRefreshIDs(BakedCmdBufferInfo &cmdBufInfo) for(size_t i = 0; i < cmdBufNodes.size(); i++) { VulkanActionTreeNode n = cmdBufNodes[i]; + + for(VulkanActionTreeNode::DeferredResourceUsage &def : n.deferredResourceUsage) + { + if(def.descBufVersionIdx >= m_DescriptorBufferVersions.size()) + { + RDCERR("Invalid deferred resource usage buffer reference"); + continue; + } + + AddUsageForDescriptorBuffers(n, cmdBufInfo.debugMessages, def); + } + n.action.eventId += m_RootEventID; n.action.actionId += m_RootActionID; diff --git a/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp index c0ceb9e5c..303f98189 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp @@ -1361,6 +1361,27 @@ bool WrappedVulkan::Serialise_vkCmdWaitEvents2(SerialiserType &ser, VkCommandBuf if(commandBuffer != VK_NULL_HANDLE) { + if(IsLoading(m_State) && evIdx == 0) + { + bool descBarrier = false; + + for(uint32_t ev = 0; ev < eventCount; ev++) + { + for(uint32_t i = 0; i < pDependencyInfos[ev].bufferMemoryBarrierCount; i++) + if(pDependencyInfos[ev].pBufferMemoryBarriers[i].dstAccessMask & + VK_ACCESS_2_DESCRIPTOR_BUFFER_READ_BIT_EXT) + descBarrier = true; + + for(uint32_t i = 0; i < pDependencyInfos[ev].memoryBarrierCount; i++) + if(pDependencyInfos[ev].pMemoryBarriers[i].dstAccessMask & + VK_ACCESS_2_DESCRIPTOR_BUFFER_READ_BIT_EXT) + descBarrier = true; + } + + if(descBarrier) + VersionDescriptorBuffers(commandBuffer); + } + VkEventCreateInfo evInfo = { VK_STRUCTURE_TYPE_EVENT_CREATE_INFO, NULL,