From f75a39d171d61f725c54cb7853aedee36d3e1dc5 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 5 Jun 2025 17:11:09 +0100 Subject: [PATCH] Implement descriptor buffer binding and track & restore its state --- renderdoc/driver/vulkan/vk_state.cpp | 115 +++++++- renderdoc/driver/vulkan/vk_state.h | 27 ++ .../driver/vulkan/wrappers/vk_cmd_funcs.cpp | 274 ++++++++++++++++++ 3 files changed, 404 insertions(+), 12 deletions(-) diff --git a/renderdoc/driver/vulkan/vk_state.cpp b/renderdoc/driver/vulkan/vk_state.cpp index f0e12f38c..ddb2a7c1f 100644 --- a/renderdoc/driver/vulkan/vk_state.cpp +++ b/renderdoc/driver/vulkan/vk_state.cpp @@ -311,6 +311,52 @@ void VulkanRenderState::BindPipeline(WrappedVulkan *vk, VkCommandBuffer cmd, if(subpass0 && dynamicRendering.active) subpass0 = false; + if(!descBufs.empty()) + { + VkDescriptorBufferBindingPushDescriptorBufferHandleEXT push; + rdcarray usage2; + rdcarray bind; + + uint32_t bufferCount = descBufs.count(); + + usage2.resize(bufferCount); + bind.resize(bufferCount); + + for(uint32_t i = 0; i < bufferCount; i++) + { + bind[i] = {VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT}; + bind[i].address = descBufs[i].address; + + if(descBufs[i].flags2) + { + usage2[i] = { + VK_STRUCTURE_TYPE_BUFFER_USAGE_FLAGS_2_CREATE_INFO, + NULL, + descBufs[i].usage, + }; + + bind[i].pNext = &usage2[i]; + } + else + { + bind[i].usage = VkBufferUsageFlags(descBufs[i].usage); + } + + if(descBufs[i].pushBuffer != ResourceId()) + { + push = { + VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_PUSH_DESCRIPTOR_BUFFER_HANDLE_EXT, + bind[i].pNext, + Unwrap(vk->GetResourceManager()->GetCurrentHandle(descBufs[i].pushBuffer)), + }; + + bind[i].pNext = &push; + } + } + + ObjDisp(cmd)->CmdBindDescriptorBuffersEXT(Unwrap(cmd), bufferCount, bind.data()); + } + if(binding == BindGraphics || binding == BindInitial) { if(graphics.pipeline != ResourceId()) @@ -798,7 +844,7 @@ void VulkanRenderState::BindDescriptorSetsForPipeline(WrappedVulkan *vk, VkComma { const DescSetLayout &descLayout = vk->GetDebugManager()->GetDescSetLayout(descSetLayouts[i]); - if(i < pipe.descSets.size() && pipe.descSets[i].descSet != ResourceId()) + if(i < pipe.descSets.size() && pipe.descSets[i].IsBound()) { // if we come to a descriptor set that isn't compatible, stop setting descriptor sets from // here on. @@ -808,7 +854,19 @@ void VulkanRenderState::BindDescriptorSetsForPipeline(WrappedVulkan *vk, VkComma // by the next action. The remaining sets are invalid, but also unused and this is // explicitly allowed by the spec. We just have to make sure we don't try to actively bind // an incompatible descriptor set. - ResourceId createdDescSetLayoutId = vk->GetDescLayoutForDescSet(pipe.descSets[i].descSet); + ResourceId createdDescSetLayoutId; + + if(pipe.descSets[i].descSet != ResourceId()) + { + createdDescSetLayoutId = vk->GetDescLayoutForDescSet(pipe.descSets[i].descSet); + } + else + { + const VulkanCreationInfo::PipelineLayout &refPipeLayout = + vk->GetDebugManager()->GetPipelineLayoutInfo(pipe.descSets[i].pipeLayout); + + createdDescSetLayoutId = refPipeLayout.descSetLayouts[i]; + } if(descSetLayouts[i] != createdDescSetLayoutId) { @@ -877,7 +935,7 @@ void VulkanRenderState::BindDescriptorSetsWithoutPipeline(WrappedVulkan *vk, VkC for(size_t i = 0; i < pipe.descSets.size(); i++) { - if(pipe.descSets[i].pipeLayout == ResourceId() || pipe.descSets[i].descSet == ResourceId()) + if(pipe.descSets[i].pipeLayout == ResourceId() || !pipe.descSets[i].IsBound()) continue; const VulkanCreationInfo::PipelineLayout &iPipeLayout = @@ -953,7 +1011,7 @@ void VulkanRenderState::BindDescriptorSetsWithoutPipeline(WrappedVulkan *vk, VkC } } - if(pipe.descSets[i].descSet != ResourceId()) + if(pipe.descSets[i].IsBound()) { const DescSetLayout &descLayout = vk->GetDebugManager()->GetDescSetLayout(iPipeLayout.descSetLayouts[i]); @@ -1006,7 +1064,7 @@ void VulkanRenderState::BindDescriptorSetsForShaders(WrappedVulkan *vk, VkComman { const DescSetLayout &descLayout = vk->GetDebugManager()->GetDescSetLayout(descSetLayouts[i]); - if(i < pipe.descSets.size() && pipe.descSets[i].descSet != ResourceId()) + if(i < pipe.descSets.size() && pipe.descSets[i].IsBound()) { // if we come to a descriptor set that isn't compatible, stop setting descriptor sets from // here on. @@ -1016,7 +1074,19 @@ void VulkanRenderState::BindDescriptorSetsForShaders(WrappedVulkan *vk, VkComman // by the next action. The remaining sets are invalid, but also unused and this is // explicitly allowed by the spec. We just have to make sure we don't try to actively bind // an incompatible descriptor set. - ResourceId createdDescSetLayoutId = vk->GetDescLayoutForDescSet(pipe.descSets[i].descSet); + ResourceId createdDescSetLayoutId; + + if(pipe.descSets[i].descSet != ResourceId()) + { + createdDescSetLayoutId = vk->GetDescLayoutForDescSet(pipe.descSets[i].descSet); + } + else + { + const VulkanCreationInfo::PipelineLayout &refPipeLayout = + vk->GetDebugManager()->GetPipelineLayoutInfo(pipe.descSets[i].pipeLayout); + + createdDescSetLayoutId = refPipeLayout.descSetLayouts[i]; + } if(descSetLayouts[i] != createdDescSetLayoutId) { @@ -1069,16 +1139,30 @@ void VulkanRenderState::BindDescriptorSet(WrappedVulkan *vk, const DescSetLayout VkCommandBuffer cmd, VkPipelineBindPoint bindPoint, uint32_t setIndex, uint32_t *dynamicOffsets) { - ResourceId descSet = GetPipeline(bindPoint).descSets[setIndex].descSet; - ResourceId pipeLayout = GetPipeline(bindPoint).descSets[setIndex].pipeLayout; + const VulkanStatePipeline::DescriptorAndOffsets &desc = GetPipeline(bindPoint).descSets[setIndex]; + ResourceId descSet = desc.descSet; + ResourceId pipeLayout = desc.pipeLayout; VkPipelineLayout layout = vk->GetResourceManager()->GetCurrentHandle(pipeLayout); if((descLayout.flags & VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT) == 0) { - ObjDisp(cmd)->CmdBindDescriptorSets( - Unwrap(cmd), bindPoint, Unwrap(layout), (uint32_t)setIndex, 1, - UnwrapPtr(vk->GetResourceManager()->GetCurrentHandle(descSet)), - descLayout.dynamicCount, dynamicOffsets); + if(descSet != ResourceId()) + { + ObjDisp(cmd)->CmdBindDescriptorSets( + Unwrap(cmd), bindPoint, Unwrap(layout), setIndex, 1, + UnwrapPtr(vk->GetResourceManager()->GetCurrentHandle(descSet)), + descLayout.dynamicCount, dynamicOffsets); + } + else if(desc.descBufferEmbeddedSamplers) + { + ObjDisp(cmd)->CmdBindDescriptorBufferEmbeddedSamplersEXT(Unwrap(cmd), bindPoint, + Unwrap(layout), setIndex); + } + else + { + ObjDisp(cmd)->CmdSetDescriptorBufferOffsetsEXT(Unwrap(cmd), bindPoint, Unwrap(layout), setIndex, + 1, &desc.descBufferIdx, &desc.descBufferOffset); + } } else { @@ -1086,6 +1170,13 @@ void VulkanRenderState::BindDescriptorSet(WrappedVulkan *vk, const DescSetLayout // current state. rdcarray writes; + // for descriptor buffer there may be a buffer to bind still + if(desc.descBufferIdx != ~0U) + { + ObjDisp(cmd)->CmdSetDescriptorBufferOffsetsEXT(Unwrap(cmd), bindPoint, Unwrap(layout), setIndex, + 1, &desc.descBufferIdx, &desc.descBufferOffset); + } + // any allocated arrays rdcarray allocImgWrites; rdcarray allocBufWrites; diff --git a/renderdoc/driver/vulkan/vk_state.h b/renderdoc/driver/vulkan/vk_state.h index 99578396f..25e6145e4 100644 --- a/renderdoc/driver/vulkan/vk_state.h +++ b/renderdoc/driver/vulkan/vk_state.h @@ -45,14 +45,32 @@ struct VulkanStatePipeline struct DescriptorAndOffsets { ResourceId pipeLayout; + + // if descriptor set bound ResourceId descSet; rdcarray offsets; + + // if descriptor buffer bound + uint32_t descBufferIdx = ~0U; + VkDeviceSize descBufferOffset = 0; + bool descBufferEmbeddedSamplers = false; + + bool IsBound() const + { + return descSet != ResourceId() || descBufferIdx != ~0U || descBufferEmbeddedSamplers; + } }; rdcarray descSets; // the index of the last set bound. In the case where we are re-binding sets and don't have a // valid pipeline to reference, this can help us resolve which descriptor sets to rebind in the // event that they're not all compatible uint32_t lastBoundSet = 0; + + bool UsingDescBufs() const + { + // all sets must be using buffers or not + return !descSets.empty() && descSets[0].descBufferIdx != ~0U; + } }; struct VulkanRenderState @@ -181,6 +199,15 @@ struct VulkanRenderState return compute; } + struct DescriptorBuffer + { + VkDeviceAddress address; + VkBufferUsageFlags2 usage; + bool flags2; + ResourceId pushBuffer; + }; + rdcarray descBufs; + struct IdxBuffer { ResourceId buf; diff --git a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp index 1597ac214..9b38bc421 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp @@ -3548,6 +3548,13 @@ bool WrappedVulkan::Serialise_vkCmdBindDescriptorSets( VulkanStatePipeline &pipeline = renderstate.GetPipeline(pipelineBindPoint); rdcarray &descsets = pipeline.descSets; + // descriptor buffers and descriptor sets can't co-exist, each invalidates the other + if(pipeline.UsingDescBufs()) + { + renderstate.descBufs.clear(); + descsets.clear(); + } + // expand as necessary if(descsets.size() < firstSet + setCount) descsets.resize(firstSet + setCount); @@ -8672,12 +8679,120 @@ bool WrappedVulkan::Serialise_vkCmdBindDescriptorBuffersEXT( SerialiserType &ser, VkCommandBuffer commandBuffer, uint32_t bufferCount, const VkDescriptorBufferBindingInfoEXT *pBindingInfos) { + SERIALISE_ELEMENT(commandBuffer); + SERIALISE_ELEMENT(bufferCount).Important(); + SERIALISE_ELEMENT_ARRAY(pBindingInfos, bufferCount); + + Serialise_DebugMessages(ser); + + SERIALISE_CHECK_READ_ERRORS(); + + if(IsReplayingAndReading()) + { + m_LastCmdBufferID = GetResourceManager()->GetOriginalID(GetResID(commandBuffer)); + + size_t memSize = 0; + for(uint32_t b = 0; b < bufferCount; b++) + memSize += GetNextPatchSize(&pBindingInfos[b]); + byte *tempMem = GetTempMemory(memSize); + rdcarray unwrappedInfos; + for(uint32_t b = 0; b < bufferCount; b++) + unwrappedInfos.push_back(*UnwrapStructAndChain(m_State, tempMem, &pBindingInfos[b])); + + if(IsActiveReplaying(m_State)) + { + if(InRerecordRange(m_LastCmdBufferID)) + { + commandBuffer = RerecordCmdBuf(m_LastCmdBufferID); + ObjDisp(commandBuffer) + ->CmdBindDescriptorBuffersEXT(Unwrap(commandBuffer), bufferCount, unwrappedInfos.data()); + + { + VulkanRenderState &renderstate = GetCmdRenderState(); + + // 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; + renderstate.descBufs[i].usage = pBindingInfos[i].usage; + renderstate.descBufs[i].flags2 = false; + + const VkBufferUsageFlags2CreateInfo *usage2 = + (const VkBufferUsageFlags2CreateInfo *)FindNextStruct( + &pBindingInfos[i], VK_STRUCTURE_TYPE_BUFFER_USAGE_FLAGS_2_CREATE_INFO); + if(usage2) + { + renderstate.descBufs[i].usage = usage2->usage; + renderstate.descBufs[i].flags2 = true; + } + + const VkDescriptorBufferBindingPushDescriptorBufferHandleEXT *push = + (const VkDescriptorBufferBindingPushDescriptorBufferHandleEXT *)FindNextStruct( + &pBindingInfos[i], + VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_PUSH_DESCRIPTOR_BUFFER_HANDLE_EXT); + if(push) + { + renderstate.descBufs[i].pushBuffer = GetResID(push->buffer); + } + } + + // 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] = {}; + } + } + } + } + else + { + ObjDisp(commandBuffer) + ->CmdBindDescriptorBuffersEXT(Unwrap(commandBuffer), bufferCount, unwrappedInfos.data()); + } + } + return true; } void WrappedVulkan::vkCmdBindDescriptorBuffersEXT(VkCommandBuffer commandBuffer, uint32_t bufferCount, const VkDescriptorBufferBindingInfoEXT *pBindingInfos) { + SCOPED_DBG_SINK(); + + size_t memSize = 0; + for(uint32_t b = 0; b < bufferCount; b++) + memSize += GetNextPatchSize(&pBindingInfos[b]); + byte *tempMem = GetTempMemory(memSize); + rdcarray unwrappedInfos; + for(uint32_t b = 0; b < bufferCount; b++) + unwrappedInfos.push_back(*UnwrapStructAndChain(m_State, tempMem, &pBindingInfos[b])); + + SERIALISE_TIME_CALL( + ObjDisp(commandBuffer) + ->CmdBindDescriptorBuffersEXT(Unwrap(commandBuffer), bufferCount, unwrappedInfos.data())); + + if(IsCaptureMode(m_State)) + { + VkResourceRecord *record = GetRecord(commandBuffer); + + CACHE_THREAD_SERIALISER(); + + SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCmdBindDescriptorBuffersEXT); + Serialise_vkCmdBindDescriptorBuffersEXT(ser, commandBuffer, bufferCount, pBindingInfos); + + record->AddChunk(scope.Get(&record->cmdInfo->alloc)); + // buffers are bound with addresses, they will be forced referenced + } } template @@ -8686,6 +8801,71 @@ bool WrappedVulkan::Serialise_vkCmdSetDescriptorBufferOffsetsEXT( VkPipelineLayout layout, uint32_t firstSet, uint32_t setCount, const uint32_t *pBufferIndices, const VkDeviceSize *pOffsets) { + SERIALISE_ELEMENT(commandBuffer); + SERIALISE_ELEMENT(pipelineBindPoint).Important(); + SERIALISE_ELEMENT(layout); + SERIALISE_ELEMENT(firstSet).Important(); + SERIALISE_ELEMENT(setCount); + SERIALISE_ELEMENT_ARRAY(pBufferIndices, setCount); + SERIALISE_ELEMENT_ARRAY(pOffsets, setCount); + + Serialise_DebugMessages(ser); + + SERIALISE_CHECK_READ_ERRORS(); + + if(IsReplayingAndReading()) + { + m_LastCmdBufferID = GetResourceManager()->GetOriginalID(GetResID(commandBuffer)); + + if(IsActiveReplaying(m_State)) + { + if(InRerecordRange(m_LastCmdBufferID)) + { + commandBuffer = RerecordCmdBuf(m_LastCmdBufferID); + ObjDisp(commandBuffer) + ->CmdSetDescriptorBufferOffsetsEXT(Unwrap(commandBuffer), pipelineBindPoint, + Unwrap(layout), firstSet, setCount, pBufferIndices, + pOffsets); + + { + VulkanRenderState &renderstate = GetCmdRenderState(); + 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] = {}; + } + } + } + } + else + { + ObjDisp(commandBuffer) + ->CmdSetDescriptorBufferOffsetsEXT(Unwrap(commandBuffer), pipelineBindPoint, Unwrap(layout), + firstSet, setCount, pBufferIndices, pOffsets); + } + } + return true; } @@ -8696,6 +8876,26 @@ void WrappedVulkan::vkCmdSetDescriptorBufferOffsetsEXT(VkCommandBuffer commandBu const uint32_t *pBufferIndices, const VkDeviceSize *pOffsets) { + SCOPED_DBG_SINK(); + + SERIALISE_TIME_CALL(ObjDisp(commandBuffer) + ->CmdSetDescriptorBufferOffsetsEXT( + Unwrap(commandBuffer), pipelineBindPoint, Unwrap(layout), firstSet, + setCount, pBufferIndices, pOffsets)); + + if(IsCaptureMode(m_State)) + { + VkResourceRecord *record = GetRecord(commandBuffer); + + CACHE_THREAD_SERIALISER(); + + SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCmdSetDescriptorBufferOffsetsEXT); + Serialise_vkCmdSetDescriptorBufferOffsetsEXT(ser, commandBuffer, pipelineBindPoint, layout, + firstSet, setCount, pBufferIndices, pOffsets); + + record->AddChunk(scope.Get(&record->cmdInfo->alloc)); + record->MarkResourceFrameReferenced(GetResID(layout), eFrameRef_Read); + } } template @@ -8703,6 +8903,61 @@ bool WrappedVulkan::Serialise_vkCmdBindDescriptorBufferEmbeddedSamplersEXT( SerialiserType &ser, VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set) { + SERIALISE_ELEMENT(commandBuffer); + SERIALISE_ELEMENT(pipelineBindPoint).Important(); + SERIALISE_ELEMENT(layout); + SERIALISE_ELEMENT(set).Important(); + + Serialise_DebugMessages(ser); + + SERIALISE_CHECK_READ_ERRORS(); + + if(IsReplayingAndReading()) + { + m_LastCmdBufferID = GetResourceManager()->GetOriginalID(GetResID(commandBuffer)); + + if(IsActiveReplaying(m_State)) + { + if(InRerecordRange(m_LastCmdBufferID)) + { + commandBuffer = RerecordCmdBuf(m_LastCmdBufferID); + ObjDisp(commandBuffer) + ->CmdBindDescriptorBufferEmbeddedSamplersEXT(Unwrap(commandBuffer), pipelineBindPoint, + Unwrap(layout), set); + + { + VulkanRenderState &renderstate = GetCmdRenderState(); + VulkanStatePipeline &pipeline = renderstate.GetPipeline(pipelineBindPoint); + + pipeline.descSets.resize_for_index(set); + + pipeline.descSets[set].pipeLayout = GetResID(layout); + pipeline.descSets[set].descBufferIdx = ~0U; + pipeline.descSets[set].descBufferOffset = 0; + pipeline.descSets[set].descBufferEmbeddedSamplers = true; + + // 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] = {}; + } + } + } + } + else + { + ObjDisp(commandBuffer) + ->CmdBindDescriptorBufferEmbeddedSamplersEXT(Unwrap(commandBuffer), pipelineBindPoint, + Unwrap(layout), set); + } + } + return true; } @@ -8711,6 +8966,25 @@ void WrappedVulkan::vkCmdBindDescriptorBufferEmbeddedSamplersEXT(VkCommandBuffer VkPipelineLayout layout, uint32_t set) { + SCOPED_DBG_SINK(); + + SERIALISE_TIME_CALL(ObjDisp(commandBuffer) + ->CmdBindDescriptorBufferEmbeddedSamplersEXT( + Unwrap(commandBuffer), pipelineBindPoint, Unwrap(layout), set)); + + if(IsCaptureMode(m_State)) + { + VkResourceRecord *record = GetRecord(commandBuffer); + + CACHE_THREAD_SERIALISER(); + + SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCmdBindDescriptorBufferEmbeddedSamplersEXT); + Serialise_vkCmdBindDescriptorBufferEmbeddedSamplersEXT(ser, commandBuffer, pipelineBindPoint, + layout, set); + + record->AddChunk(scope.Get(&record->cmdInfo->alloc)); + record->MarkResourceFrameReferenced(GetResID(layout), eFrameRef_Read); + } } INSTANTIATE_FUNCTION_SERIALISED(VkResult, vkCreateCommandPool, VkDevice device,