From 805dbb52630f470e98b0b961b8a3c67e9fe7498f Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Thu, 13 Aug 2026 13:34:35 +0100 Subject: [PATCH 01/16] Added helper function VulkanEventNode::AddResourceUsage --- renderdoc/driver/vulkan/vk_core.cpp | 64 ++++---- renderdoc/driver/vulkan/vk_core.h | 4 + .../driver/vulkan/wrappers/vk_cmd_funcs.cpp | 88 +++++------ .../driver/vulkan/wrappers/vk_draw_funcs.cpp | 147 ++++++++---------- .../driver/vulkan/wrappers/vk_misc_funcs.cpp | 13 +- .../vulkan/wrappers/vk_resource_funcs.cpp | 6 +- .../driver/vulkan/wrappers/vk_sync_funcs.cpp | 6 +- 7 files changed, 146 insertions(+), 182 deletions(-) diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 9414394b432..ff5c898c225 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -6312,25 +6312,19 @@ void WrappedVulkan::AddUsage(VulkanEventNode &eventNode) if(action.flags & ActionFlags::Drawcall) { if(action.flags & ActionFlags::Indexed && state.ibuffer.buf != ResourceId()) - eventNode.resourceUsage.push_back(make_rdcpair(state.ibuffer.buf, ResourceUsage::IndexBuffer)); + eventNode.AddResourceUsage(state.ibuffer.buf, ResourceUsage::IndexBuffer); for(size_t i = 0; i < state.vbuffers.size(); i++) { if(state.vbuffers[i].buf != ResourceId()) - { - eventNode.resourceUsage.push_back( - make_rdcpair(state.vbuffers[i].buf, ResourceUsage::VertexBuffer)); - } + eventNode.AddResourceUsage(state.vbuffers[i].buf, ResourceUsage::VertexBuffer); } for(uint32_t i = state.firstxfbcounter; i < state.firstxfbcounter + state.xfbcounters.size() && i < state.xfbbuffers.size(); i++) { if(state.xfbbuffers[i].buf != ResourceId()) - { - eventNode.resourceUsage.push_back( - make_rdcpair(state.xfbbuffers[i].buf, ResourceUsage::StreamOut)); - } + eventNode.AddResourceUsage(state.xfbbuffers[i].buf, ResourceUsage::StreamOut); } } @@ -6415,7 +6409,6 @@ void WrappedVulkan::AddUsageForDescriptorBuffers(VulkanEventNode &eventNode, if(sh.module == ResourceId()) continue; - ResourceId origPipe = pipe; ResourceId origShad = sh.module; for(const ConstantBlock &constantBlock : sh.refl->constantBlocks) @@ -6547,7 +6540,6 @@ void WrappedVulkan::AddUsageForDescriptorSets(VulkanEventNode &eventNode) if(sh.module == ResourceId()) continue; - ResourceId origPipe = pipe; ResourceId origShad = sh.module; for(const ConstantBlock &constantBlock : sh.refl->constantBlocks) @@ -6706,7 +6698,7 @@ void WrappedVulkan::AddUsageForDescriptor(VulkanEventNode &eventNode, const Desc } if(id != ResourceId()) - eventNode.resourceUsage.push_back(make_rdcpair(id, usage)); + eventNode.AddResourceUsage(id, usage); } void WrappedVulkan::AddFramebufferUsage(VulkanEventNode &eventNode, @@ -6743,8 +6735,8 @@ void WrappedVulkan::AddFramebufferUsage(VulkanEventNode &eventNode, uint32_t att = sub.inputAttachments[i]; if(att == VK_ATTACHMENT_UNUSED) continue; - eventNode.resourceUsage.push_back( - make_rdcpair(c.m_ImageView[fbattachments[att]].image, ResourceUsage::InputTarget)); + eventNode.AddResourceUsage(c.m_ImageView[fbattachments[att]].image, + ResourceUsage::InputTarget); } for(size_t i = 0; i < sub.colorAttachments.size(); i++) @@ -6752,16 +6744,16 @@ void WrappedVulkan::AddFramebufferUsage(VulkanEventNode &eventNode, uint32_t att = sub.colorAttachments[i]; if(att == VK_ATTACHMENT_UNUSED) continue; - eventNode.resourceUsage.push_back(make_rdcpair( + eventNode.AddResourceUsage( c.m_ImageView[fbattachments[att]].image, - sub.customResolve ? ResourceUsage::ResolveDst : ResourceUsage::ColorTarget)); + sub.customResolve ? ResourceUsage::ResolveDst : ResourceUsage::ColorTarget); } if(sub.depthstencilAttachment >= 0) { int32_t att = sub.depthstencilAttachment; - eventNode.resourceUsage.push_back(make_rdcpair(c.m_ImageView[fbattachments[att]].image, - ResourceUsage::DepthStencilTarget)); + eventNode.AddResourceUsage(c.m_ImageView[fbattachments[att]].image, + ResourceUsage::DepthStencilTarget); } } } @@ -6778,15 +6770,15 @@ void WrappedVulkan::AddFramebufferUsage(VulkanEventNode &eventNode, (dyn.color[i].resolveMode & VK_RESOLVE_MODE_CUSTOM_BIT_EXT); if(!isCustomResolve) { - eventNode.resourceUsage.push_back(make_rdcpair( - c.m_ImageView[GetResID(dyn.color[i].imageView)].image, ResourceUsage::ColorTarget)); + eventNode.AddResourceUsage(c.m_ImageView[GetResID(dyn.color[i].imageView)].image, + ResourceUsage::ColorTarget); } else { - eventNode.resourceUsage.push_back(make_rdcpair( - c.m_ImageView[GetResID(dyn.color[i].imageView)].image, ResourceUsage::InputTarget)); - eventNode.resourceUsage.push_back(make_rdcpair( - c.m_ImageView[GetResID(dyn.color[i].resolveImageView)].image, ResourceUsage::ResolveDst)); + eventNode.AddResourceUsage(c.m_ImageView[GetResID(dyn.color[i].imageView)].image, + ResourceUsage::InputTarget); + eventNode.AddResourceUsage(c.m_ImageView[GetResID(dyn.color[i].resolveImageView)].image, + ResourceUsage::ResolveDst); } } @@ -6796,15 +6788,15 @@ void WrappedVulkan::AddFramebufferUsage(VulkanEventNode &eventNode, (dyn.depth.resolveMode & VK_RESOLVE_MODE_CUSTOM_BIT_EXT); if(!isCustomResolve) { - eventNode.resourceUsage.push_back(make_rdcpair( - c.m_ImageView[GetResID(dyn.depth.imageView)].image, ResourceUsage::DepthStencilTarget)); + eventNode.AddResourceUsage(c.m_ImageView[GetResID(dyn.depth.imageView)].image, + ResourceUsage::DepthStencilTarget); } else { - eventNode.resourceUsage.push_back(make_rdcpair( - c.m_ImageView[GetResID(dyn.depth.imageView)].image, ResourceUsage::InputTarget)); - eventNode.resourceUsage.push_back(make_rdcpair( - c.m_ImageView[GetResID(dyn.depth.resolveImageView)].image, ResourceUsage::ResolveDst)); + eventNode.AddResourceUsage(c.m_ImageView[GetResID(dyn.depth.imageView)].image, + ResourceUsage::InputTarget); + eventNode.AddResourceUsage(c.m_ImageView[GetResID(dyn.depth.resolveImageView)].image, + ResourceUsage::ResolveDst); } } @@ -6815,15 +6807,15 @@ void WrappedVulkan::AddFramebufferUsage(VulkanEventNode &eventNode, if(!isCustomResolve) { - eventNode.resourceUsage.push_back(make_rdcpair( - c.m_ImageView[GetResID(dyn.stencil.imageView)].image, ResourceUsage::DepthStencilTarget)); + eventNode.AddResourceUsage(c.m_ImageView[GetResID(dyn.stencil.imageView)].image, + ResourceUsage::DepthStencilTarget); } else { - eventNode.resourceUsage.push_back(make_rdcpair( - c.m_ImageView[GetResID(dyn.stencil.imageView)].image, ResourceUsage::InputTarget)); - eventNode.resourceUsage.push_back(make_rdcpair( - c.m_ImageView[GetResID(dyn.stencil.resolveImageView)].image, ResourceUsage::ResolveDst)); + eventNode.AddResourceUsage(c.m_ImageView[GetResID(dyn.stencil.imageView)].image, + ResourceUsage::InputTarget); + eventNode.AddResourceUsage(c.m_ImageView[GetResID(dyn.stencil.resolveImageView)].image, + ResourceUsage::ResolveDst); } } } diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index 942551a355b..19ce3ef76b3 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -134,6 +134,10 @@ struct VulkanEventNode { VulkanEventNode() = default; ~VulkanEventNode() = default; + void AddResourceUsage(ResourceId id, ResourceUsage usage) + { + resourceUsage.push_back(make_rdcpair(id, usage)); + } // eventId is not used APIEvent event; diff --git a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp index e516e8090d0..5d4dd080f81 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp @@ -426,13 +426,13 @@ void WrappedVulkan::AddImplicitResolveResourceUsage(uint32_t subpass) if(attIdx == VK_ATTACHMENT_UNUSED) continue; ResourceId image = m_CreationInfo.m_ImageView[fbattachments[attIdx]].image; - m_LoadingEventNode.resourceUsage.push_back(make_rdcpair(image, ResourceUsage::ResolveDst)); + m_LoadingEventNode.AddResourceUsage(image, ResourceUsage::ResolveDst); attIdx = rpinfo.subpasses[subpass].colorAttachments[i]; if(attIdx == VK_ATTACHMENT_UNUSED) continue; image = m_CreationInfo.m_ImageView[fbattachments[attIdx]].image; - m_LoadingEventNode.resourceUsage.push_back(make_rdcpair(image, ResourceUsage::ResolveSrc)); + m_LoadingEventNode.AddResourceUsage(image, ResourceUsage::ResolveSrc); } // also add any discards on the final subpass @@ -443,7 +443,7 @@ void WrappedVulkan::AddImplicitResolveResourceUsage(uint32_t subpass) if(rpinfo.attachments[i].storeOp == VK_ATTACHMENT_STORE_OP_DONT_CARE) { ResourceId image = m_CreationInfo.m_ImageView[fbattachments[i]].image; - m_LoadingEventNode.resourceUsage.push_back(make_rdcpair(image, ResourceUsage::Discard)); + m_LoadingEventNode.AddResourceUsage(image, ResourceUsage::Discard); } } } @@ -2311,10 +2311,10 @@ bool WrappedVulkan::Serialise_vkCmdBeginRenderPass(SerialiserType &ser, VkComman rpinfo.attachments[i].loadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE) { ResourceId image = m_CreationInfo.m_ImageView[fbattachments[i]].image; - m_LoadingEventNode.resourceUsage.push_back( - make_rdcpair(image, rpinfo.attachments[i].loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR - ? ResourceUsage::Clear - : ResourceUsage::Discard)); + m_LoadingEventNode.AddResourceUsage( + image, rpinfo.attachments[i].loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR + ? ResourceUsage::Clear + : ResourceUsage::Discard); } } @@ -2931,10 +2931,10 @@ bool WrappedVulkan::Serialise_vkCmdBeginRenderPass2(SerialiserType &ser, rpinfo.attachments[i].loadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE) { ResourceId image = m_CreationInfo.m_ImageView[fbattachments[i]].image; - m_LoadingEventNode.resourceUsage.push_back( - make_rdcpair(image, rpinfo.attachments[i].loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR - ? ResourceUsage::Clear - : ResourceUsage::Discard)); + m_LoadingEventNode.AddResourceUsage( + image, rpinfo.attachments[i].loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR + ? ResourceUsage::Clear + : ResourceUsage::Discard); } } @@ -4598,8 +4598,8 @@ bool WrappedVulkan::Serialise_vkCmdPipelineBarrier( if(IsLoading(m_State)) { - m_LoadingEventNode.resourceUsage.push_back( - make_rdcpair(GetResID(pBufferMemoryBarriers[i].buffer), ResourceUsage::Barrier)); + m_LoadingEventNode.AddResourceUsage(GetResID(pBufferMemoryBarriers[i].buffer), + ResourceUsage::Barrier); } } } @@ -4616,8 +4616,8 @@ bool WrappedVulkan::Serialise_vkCmdPipelineBarrier( if(IsLoading(m_State)) { - m_LoadingEventNode.resourceUsage.push_back( - make_rdcpair(GetResID(pImageMemoryBarriers[i].image), ResourceUsage::Barrier)); + m_LoadingEventNode.AddResourceUsage(GetResID(pImageMemoryBarriers[i].image), + ResourceUsage::Barrier); } } } @@ -4639,8 +4639,7 @@ bool WrappedVulkan::Serialise_vkCmdPipelineBarrier( VulkanCreationInfo::Image &imgInfo = m_CreationInfo.m_Image[GetResID(b.image)]; if(!imgInfo.external) { - m_LoadingEventNode.resourceUsage.push_back( - make_rdcpair(GetResID(b.image), ResourceUsage::Discard)); + m_LoadingEventNode.AddResourceUsage(GetResID(b.image), ResourceUsage::Discard); } } } @@ -4846,8 +4845,8 @@ bool WrappedVulkan::Serialise_vkCmdPipelineBarrier2(SerialiserType &ser, if(IsLoading(m_State)) { - m_LoadingEventNode.resourceUsage.push_back(make_rdcpair( - GetResID(DependencyInfo.pBufferMemoryBarriers[i].buffer), ResourceUsage::Barrier)); + m_LoadingEventNode.AddResourceUsage( + GetResID(DependencyInfo.pBufferMemoryBarriers[i].buffer), ResourceUsage::Barrier); } } } @@ -4864,8 +4863,8 @@ bool WrappedVulkan::Serialise_vkCmdPipelineBarrier2(SerialiserType &ser, if(IsLoading(m_State)) { - m_LoadingEventNode.resourceUsage.push_back(make_rdcpair( - GetResID(DependencyInfo.pImageMemoryBarriers[i].image), ResourceUsage::Barrier)); + m_LoadingEventNode.AddResourceUsage( + GetResID(DependencyInfo.pImageMemoryBarriers[i].image), ResourceUsage::Barrier); } } } @@ -4895,8 +4894,7 @@ bool WrappedVulkan::Serialise_vkCmdPipelineBarrier2(SerialiserType &ser, VulkanCreationInfo::Image &imgInfo = m_CreationInfo.m_Image[GetResID(b.image)]; if(!imgInfo.external) { - m_LoadingEventNode.resourceUsage.push_back( - make_rdcpair(GetResID(b.image), ResourceUsage::Discard)); + m_LoadingEventNode.AddResourceUsage(GetResID(b.image), ResourceUsage::Discard); } } } @@ -7890,9 +7888,9 @@ bool WrappedVulkan::Serialise_vkCmdBeginRendering(SerialiserType &ser, VkCommand att->loadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE) { ResourceId image = m_CreationInfo.m_ImageView[GetResID(att->imageView)].image; - m_LoadingEventNode.resourceUsage.push_back(make_rdcpair( - image, att->loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ? ResourceUsage::Clear - : ResourceUsage::Discard)); + m_LoadingEventNode.AddResourceUsage(image, att->loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR + ? ResourceUsage::Clear + : ResourceUsage::Discard); } } @@ -8191,8 +8189,6 @@ bool WrappedVulkan::Serialise_vkCmdEndRendering(SerialiserType &ser, VkCommandBu VulkanRenderState &state = m_BakedCmdBufferInfo[m_LastCmdBufferID].state; - rdcarray> &usage = m_LoadingEventNode.resourceUsage; - VulkanRenderState::DynamicRendering &dyn = state.dynamicRendering; bool suspending = (dyn.flags & VK_RENDERING_SUSPENDING_BIT) != 0; @@ -8211,19 +8207,21 @@ bool WrappedVulkan::Serialise_vkCmdEndRendering(SerialiserType &ser, VkCommandBu if((dynAtts[i].resolveMode && !(dynAtts[i].resolveMode & VK_RESOLVE_MODE_CUSTOM_BIT_EXT)) && dynAtts[i].imageView != VK_NULL_HANDLE && dynAtts[i].resolveImageView != VK_NULL_HANDLE) { - usage.push_back(make_rdcpair(m_CreationInfo.m_ImageView[GetResID(dynAtts[i].imageView)].image, - ResourceUsage::ResolveSrc)); + m_LoadingEventNode.AddResourceUsage( + m_CreationInfo.m_ImageView[GetResID(dynAtts[i].imageView)].image, + ResourceUsage::ResolveSrc); - usage.push_back( - make_rdcpair(m_CreationInfo.m_ImageView[GetResID(dynAtts[i].resolveImageView)].image, - ResourceUsage::ResolveDst)); + m_LoadingEventNode.AddResourceUsage( + m_CreationInfo.m_ImageView[GetResID(dynAtts[i].resolveImageView)].image, + ResourceUsage::ResolveDst); } // also add any discards if(dynAtts[i].storeOp == VK_ATTACHMENT_STORE_OP_DONT_CARE) { - usage.push_back(make_rdcpair(m_CreationInfo.m_ImageView[GetResID(dynAtts[i].imageView)].image, - ResourceUsage::Discard)); + m_LoadingEventNode.AddResourceUsage( + m_CreationInfo.m_ImageView[GetResID(dynAtts[i].imageView)].image, + ResourceUsage::Discard); } } @@ -8477,8 +8475,6 @@ bool WrappedVulkan::Serialise_vkCmdEndRendering2EXT(SerialiserType &ser, VulkanRenderState &state = m_BakedCmdBufferInfo[m_LastCmdBufferID].state; - rdcarray> &usage = m_LoadingEventNode.resourceUsage; - VulkanRenderState::DynamicRendering &dyn = state.dynamicRendering; bool suspending = (dyn.flags & VK_RENDERING_SUSPENDING_BIT) != 0; @@ -8497,19 +8493,21 @@ bool WrappedVulkan::Serialise_vkCmdEndRendering2EXT(SerialiserType &ser, if((dynAtts[i].resolveMode && !(dynAtts[i].resolveMode & VK_RESOLVE_MODE_CUSTOM_BIT_EXT)) && dynAtts[i].imageView != VK_NULL_HANDLE && dynAtts[i].resolveImageView != VK_NULL_HANDLE) { - usage.push_back(make_rdcpair(m_CreationInfo.m_ImageView[GetResID(dynAtts[i].imageView)].image, - ResourceUsage::ResolveSrc)); + m_LoadingEventNode.AddResourceUsage( + m_CreationInfo.m_ImageView[GetResID(dynAtts[i].imageView)].image, + ResourceUsage::ResolveSrc); - usage.push_back( - make_rdcpair(m_CreationInfo.m_ImageView[GetResID(dynAtts[i].resolveImageView)].image, - ResourceUsage::ResolveDst)); + m_LoadingEventNode.AddResourceUsage( + m_CreationInfo.m_ImageView[GetResID(dynAtts[i].resolveImageView)].image, + ResourceUsage::ResolveDst); } // also add any discards if(dynAtts[i].storeOp == VK_ATTACHMENT_STORE_OP_DONT_CARE) { - usage.push_back(make_rdcpair(m_CreationInfo.m_ImageView[GetResID(dynAtts[i].imageView)].image, - ResourceUsage::Discard)); + m_LoadingEventNode.AddResourceUsage( + m_CreationInfo.m_ImageView[GetResID(dynAtts[i].imageView)].image, + ResourceUsage::Discard); } } @@ -10372,7 +10370,7 @@ bool WrappedVulkan::Serialise_vkCmdBeginCustomResolveEXT( renderstate.dynamicRendering.color[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE; ResourceId image = m_CreationInfo.m_ImageView[resolveImageView].image; - m_LoadingEventNode.resourceUsage.push_back(make_rdcpair(image, ResourceUsage::Discard)); + m_LoadingEventNode.AddResourceUsage(image, ResourceUsage::Discard); } else { diff --git a/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp index d9c6ce9fac6..196980b94f2 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp @@ -747,7 +747,7 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndirect(SerialiserType &ser, VkCommandBu VulkanEventNode &eventNode = GetLastEventNode(); eventNode.indirectPatch = indirectPatch; - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(buffer), ResourceUsage::Indirect)); + eventNode.AddResourceUsage(GetResID(buffer), ResourceUsage::Indirect); return true; } @@ -767,7 +767,7 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndirect(SerialiserType &ser, VkCommandBu VulkanEventNode &eventNode = GetLastEventNode(); eventNode.indirectPatch = indirectPatch; - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(buffer), ResourceUsage::Indirect)); + eventNode.AddResourceUsage(GetResID(buffer), ResourceUsage::Indirect); for(uint32_t i = 0; i < count; i++) { @@ -1106,7 +1106,7 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndexedIndirect(SerialiserType &ser, VulkanEventNode &eventNode = GetLastEventNode(); eventNode.indirectPatch = indirectPatch; - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(buffer), ResourceUsage::Indirect)); + eventNode.AddResourceUsage(GetResID(buffer), ResourceUsage::Indirect); return true; } @@ -1127,7 +1127,7 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndexedIndirect(SerialiserType &ser, VulkanEventNode &eventNode = GetLastEventNode(); eventNode.indirectPatch = indirectPatch; - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(buffer), ResourceUsage::Indirect)); + eventNode.AddResourceUsage(GetResID(buffer), ResourceUsage::Indirect); for(uint32_t i = 0; i < count; i++) { @@ -1335,7 +1335,7 @@ bool WrappedVulkan::Serialise_vkCmdDispatchIndirect(SerialiserType &ser, VulkanEventNode &eventNode = GetLastEventNode(); eventNode.indirectPatch = indirectPatch; - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(buffer), ResourceUsage::Indirect)); + eventNode.AddResourceUsage(GetResID(buffer), ResourceUsage::Indirect); } } } @@ -1445,14 +1445,12 @@ bool WrappedVulkan::Serialise_vkCmdBlitImage(SerialiserType &ser, VkCommandBuffe VulkanEventNode &eventNode = GetLastEventNode(); if(srcImage == destImage) { - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(srcImage), ResourceUsage::Resolve)); + eventNode.AddResourceUsage(GetResID(srcImage), ResourceUsage::Resolve); } else { - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(srcImage), ResourceUsage::ResolveSrc)); - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(destImage), ResourceUsage::ResolveDst)); + eventNode.AddResourceUsage(GetResID(srcImage), ResourceUsage::ResolveSrc); + eventNode.AddResourceUsage(GetResID(destImage), ResourceUsage::ResolveDst); } } } @@ -1590,14 +1588,12 @@ bool WrappedVulkan::Serialise_vkCmdResolveImage(SerialiserType &ser, VkCommandBu VulkanEventNode &eventNode = GetLastEventNode(); if(srcImage == destImage) { - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(srcImage), ResourceUsage::Resolve)); + eventNode.AddResourceUsage(GetResID(srcImage), ResourceUsage::Resolve); } else { - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(srcImage), ResourceUsage::ResolveSrc)); - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(destImage), ResourceUsage::ResolveDst)); + eventNode.AddResourceUsage(GetResID(srcImage), ResourceUsage::ResolveSrc); + eventNode.AddResourceUsage(GetResID(destImage), ResourceUsage::ResolveDst); } } } @@ -1725,12 +1721,12 @@ bool WrappedVulkan::Serialise_vkCmdCopyImage(SerialiserType &ser, VkCommandBuffe VulkanEventNode &eventNode = GetLastEventNode(); if(srcImage == destImage) { - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(srcImage), ResourceUsage::Copy)); + eventNode.AddResourceUsage(GetResID(srcImage), ResourceUsage::Copy); } else { - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(srcImage), ResourceUsage::CopySrc)); - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(destImage), ResourceUsage::CopyDst)); + eventNode.AddResourceUsage(GetResID(srcImage), ResourceUsage::CopySrc); + eventNode.AddResourceUsage(GetResID(destImage), ResourceUsage::CopyDst); } } } @@ -1848,8 +1844,8 @@ bool WrappedVulkan::Serialise_vkCmdCopyBufferToImage( AddAction(action); VulkanEventNode &eventNode = GetLastEventNode(); - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(srcBuffer), ResourceUsage::CopySrc)); - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(destImage), ResourceUsage::CopyDst)); + eventNode.AddResourceUsage(GetResID(srcBuffer), ResourceUsage::CopySrc); + eventNode.AddResourceUsage(GetResID(destImage), ResourceUsage::CopyDst); } } } @@ -1956,8 +1952,8 @@ bool WrappedVulkan::Serialise_vkCmdCopyImageToBuffer(SerialiserType &ser, AddAction(action); VulkanEventNode &eventNode = GetLastEventNode(); - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(srcImage), ResourceUsage::CopySrc)); - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(destBuffer), ResourceUsage::CopyDst)); + eventNode.AddResourceUsage(GetResID(srcImage), ResourceUsage::CopySrc); + eventNode.AddResourceUsage(GetResID(destBuffer), ResourceUsage::CopyDst); } } } @@ -2060,13 +2056,12 @@ bool WrappedVulkan::Serialise_vkCmdCopyBuffer(SerialiserType &ser, VkCommandBuff VulkanEventNode &eventNode = GetLastEventNode(); if(srcBuffer == destBuffer) { - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(srcBuffer), ResourceUsage::Copy)); + eventNode.AddResourceUsage(GetResID(srcBuffer), ResourceUsage::Copy); } else { - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(srcBuffer), ResourceUsage::CopySrc)); - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(destBuffer), ResourceUsage::CopyDst)); + eventNode.AddResourceUsage(GetResID(srcBuffer), ResourceUsage::CopySrc); + eventNode.AddResourceUsage(GetResID(destBuffer), ResourceUsage::CopyDst); } } } @@ -2167,7 +2162,7 @@ bool WrappedVulkan::Serialise_vkCmdUpdateBuffer(SerialiserType &ser, VkCommandBu AddAction(action); VulkanEventNode &eventNode = GetLastEventNode(); - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(destBuffer), ResourceUsage::CopyDst)); + eventNode.AddResourceUsage(GetResID(destBuffer), ResourceUsage::CopyDst); } } } @@ -2259,7 +2254,7 @@ bool WrappedVulkan::Serialise_vkCmdFillBuffer(SerialiserType &ser, VkCommandBuff AddAction(action); VulkanEventNode &eventNode = GetLastEventNode(); - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(destBuffer), ResourceUsage::Clear)); + eventNode.AddResourceUsage(GetResID(destBuffer), ResourceUsage::Clear); } } } @@ -2361,7 +2356,7 @@ bool WrappedVulkan::Serialise_vkCmdClearColorImage(SerialiserType &ser, VkComman AddAction(action); VulkanEventNode &eventNode = GetLastEventNode(); - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(image), ResourceUsage::Clear)); + eventNode.AddResourceUsage(GetResID(image), ResourceUsage::Clear); } } } @@ -2471,7 +2466,7 @@ bool WrappedVulkan::Serialise_vkCmdClearDepthStencilImage( AddAction(action); VulkanEventNode &eventNode = GetLastEventNode(); - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(image), ResourceUsage::Clear)); + eventNode.AddResourceUsage(GetResID(image), ResourceUsage::Clear); } } } @@ -2602,9 +2597,9 @@ bool WrappedVulkan::Serialise_vkCmdClearAttachments(SerialiserType &ser, att = rp.subpasses[state.subpass].colorAttachments[att]; if(att < (uint32_t)state.GetFramebufferAttachments().size()) { - eventNode.resourceUsage.push_back(make_rdcpair( + eventNode.AddResourceUsage( m_CreationInfo.m_ImageView[state.GetFramebufferAttachments()[att]].image, - ResourceUsage::Clear)); + ResourceUsage::Clear); } } } @@ -2614,9 +2609,9 @@ bool WrappedVulkan::Serialise_vkCmdClearAttachments(SerialiserType &ser, if(rp.subpasses[state.subpass].depthstencilAttachment >= 0) { att = (uint32_t)rp.subpasses[state.subpass].depthstencilAttachment; - eventNode.resourceUsage.push_back(make_rdcpair( + eventNode.AddResourceUsage( m_CreationInfo.m_ImageView[state.GetFramebufferAttachments()[att]].image, - ResourceUsage::Clear)); + ResourceUsage::Clear); } } } @@ -2627,23 +2622,22 @@ bool WrappedVulkan::Serialise_vkCmdClearAttachments(SerialiserType &ser, for(size_t a = 0; a < dyn.color.size(); a++) { - eventNode.resourceUsage.push_back( - make_rdcpair(m_CreationInfo.m_ImageView[GetResID(dyn.color[a].imageView)].image, - ResourceUsage::Clear)); + eventNode.AddResourceUsage( + m_CreationInfo.m_ImageView[GetResID(dyn.color[a].imageView)].image, + ResourceUsage::Clear); } if(dyn.depth.imageView != VK_NULL_HANDLE) { - eventNode.resourceUsage.push_back( - make_rdcpair(m_CreationInfo.m_ImageView[GetResID(dyn.depth.imageView)].image, - ResourceUsage::Clear)); + eventNode.AddResourceUsage(m_CreationInfo.m_ImageView[GetResID(dyn.depth.imageView)].image, + ResourceUsage::Clear); } if(dyn.stencil.imageView != VK_NULL_HANDLE && dyn.depth.imageView != dyn.stencil.imageView) { - eventNode.resourceUsage.push_back( - make_rdcpair(m_CreationInfo.m_ImageView[GetResID(dyn.stencil.imageView)].image, - ResourceUsage::Clear)); + eventNode.AddResourceUsage( + m_CreationInfo.m_ImageView[GetResID(dyn.stencil.imageView)].image, + ResourceUsage::Clear); } } } @@ -3003,8 +2997,8 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndirectCount(SerialiserType &ser, VulkanEventNode &eventNode = GetLastEventNode(); eventNode.indirectPatch = indirectPatch; - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(buffer), ResourceUsage::Indirect)); - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(countBuffer), ResourceUsage::Indirect)); + eventNode.AddResourceUsage(GetResID(buffer), ResourceUsage::Indirect); + eventNode.AddResourceUsage(GetResID(countBuffer), ResourceUsage::Indirect); if(maxDrawCount > 0) eventNode.action.flags = flags; @@ -3310,8 +3304,8 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndexedIndirectCount( VulkanEventNode &eventNode = GetLastEventNode(); eventNode.indirectPatch = indirectPatch; - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(buffer), ResourceUsage::Indirect)); - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(countBuffer), ResourceUsage::Indirect)); + eventNode.AddResourceUsage(GetResID(buffer), ResourceUsage::Indirect); + eventNode.AddResourceUsage(GetResID(countBuffer), ResourceUsage::Indirect); if(maxDrawCount > 0) eventNode.action.flags = flags; @@ -3461,8 +3455,7 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndirectByteCountEXT( VulkanEventNode &eventNode = GetLastEventNode(); eventNode.indirectPatch = indirectPatch; - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(counterBuffer), ResourceUsage::Indirect)); + eventNode.AddResourceUsage(GetResID(counterBuffer), ResourceUsage::Indirect); return true; } @@ -3568,15 +3561,12 @@ bool WrappedVulkan::Serialise_vkCmdCopyBuffer2(SerialiserType &ser, VkCommandBuf VulkanEventNode &eventNode = GetLastEventNode(); if(srcid == dstid) { - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(CopyInfo.srcBuffer), ResourceUsage::Copy)); + eventNode.AddResourceUsage(GetResID(CopyInfo.srcBuffer), ResourceUsage::Copy); } else { - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(CopyInfo.srcBuffer), ResourceUsage::CopySrc)); - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(CopyInfo.dstBuffer), ResourceUsage::CopyDst)); + eventNode.AddResourceUsage(GetResID(CopyInfo.srcBuffer), ResourceUsage::CopySrc); + eventNode.AddResourceUsage(GetResID(CopyInfo.dstBuffer), ResourceUsage::CopyDst); } } } @@ -3688,15 +3678,12 @@ bool WrappedVulkan::Serialise_vkCmdCopyImage2(SerialiserType &ser, VkCommandBuff VulkanEventNode &eventNode = GetLastEventNode(); if(srcid == dstid) { - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(CopyInfo.srcImage), ResourceUsage::Copy)); + eventNode.AddResourceUsage(GetResID(CopyInfo.srcImage), ResourceUsage::Copy); } else { - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(CopyInfo.srcImage), ResourceUsage::CopySrc)); - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(CopyInfo.dstImage), ResourceUsage::CopyDst)); + eventNode.AddResourceUsage(GetResID(CopyInfo.srcImage), ResourceUsage::CopySrc); + eventNode.AddResourceUsage(GetResID(CopyInfo.dstImage), ResourceUsage::CopyDst); } } } @@ -3819,10 +3806,8 @@ bool WrappedVulkan::Serialise_vkCmdCopyBufferToImage2( AddAction(action); VulkanEventNode &eventNode = GetLastEventNode(); - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(CopyInfo.srcBuffer), ResourceUsage::CopySrc)); - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(CopyInfo.dstImage), ResourceUsage::CopyDst)); + eventNode.AddResourceUsage(GetResID(CopyInfo.srcBuffer), ResourceUsage::CopySrc); + eventNode.AddResourceUsage(GetResID(CopyInfo.dstImage), ResourceUsage::CopyDst); } } } @@ -3948,10 +3933,8 @@ bool WrappedVulkan::Serialise_vkCmdCopyImageToBuffer2( AddAction(action); VulkanEventNode &eventNode = GetLastEventNode(); - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(CopyInfo.srcImage), ResourceUsage::CopySrc)); - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(CopyInfo.dstBuffer), ResourceUsage::CopyDst)); + eventNode.AddResourceUsage(GetResID(CopyInfo.srcImage), ResourceUsage::CopySrc); + eventNode.AddResourceUsage(GetResID(CopyInfo.dstBuffer), ResourceUsage::CopyDst); } } } @@ -4073,15 +4056,12 @@ bool WrappedVulkan::Serialise_vkCmdBlitImage2(SerialiserType &ser, VkCommandBuff VulkanEventNode &eventNode = GetLastEventNode(); if(srcid == dstid) { - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(BlitInfo.srcImage), ResourceUsage::Resolve)); + eventNode.AddResourceUsage(GetResID(BlitInfo.srcImage), ResourceUsage::Resolve); } else { - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(BlitInfo.srcImage), ResourceUsage::ResolveSrc)); - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(BlitInfo.dstImage), ResourceUsage::ResolveDst)); + eventNode.AddResourceUsage(GetResID(BlitInfo.srcImage), ResourceUsage::ResolveSrc); + eventNode.AddResourceUsage(GetResID(BlitInfo.dstImage), ResourceUsage::ResolveDst); } } } @@ -4210,15 +4190,12 @@ bool WrappedVulkan::Serialise_vkCmdResolveImage2(SerialiserType &ser, VkCommandB VulkanEventNode &eventNode = GetLastEventNode(); if(srcid == dstid) { - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(ResolveInfo.srcImage), ResourceUsage::Resolve)); + eventNode.AddResourceUsage(GetResID(ResolveInfo.srcImage), ResourceUsage::Resolve); } else { - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(ResolveInfo.srcImage), ResourceUsage::ResolveSrc)); - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(ResolveInfo.dstImage), ResourceUsage::ResolveDst)); + eventNode.AddResourceUsage(GetResID(ResolveInfo.srcImage), ResourceUsage::ResolveSrc); + eventNode.AddResourceUsage(GetResID(ResolveInfo.dstImage), ResourceUsage::ResolveDst); } } } @@ -4621,7 +4598,7 @@ bool WrappedVulkan::Serialise_vkCmdDrawMeshTasksIndirectEXT(SerialiserType &ser, VulkanEventNode &eventNode = GetLastEventNode(); eventNode.indirectPatch = indirectPatch; - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(buffer), ResourceUsage::Indirect)); + eventNode.AddResourceUsage(GetResID(buffer), ResourceUsage::Indirect); return true; } @@ -4641,7 +4618,7 @@ bool WrappedVulkan::Serialise_vkCmdDrawMeshTasksIndirectEXT(SerialiserType &ser, VulkanEventNode &eventNode = GetLastEventNode(); eventNode.indirectPatch = indirectPatch; - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(buffer), ResourceUsage::Indirect)); + eventNode.AddResourceUsage(GetResID(buffer), ResourceUsage::Indirect); for(uint32_t i = 0; i < drawCount; i++) { @@ -4943,8 +4920,8 @@ bool WrappedVulkan::Serialise_vkCmdDrawMeshTasksIndirectCountEXT( VulkanEventNode &eventNode = GetLastEventNode(); eventNode.indirectPatch = indirectPatch; - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(buffer), ResourceUsage::Indirect)); - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(countBuffer), ResourceUsage::Indirect)); + eventNode.AddResourceUsage(GetResID(buffer), ResourceUsage::Indirect); + eventNode.AddResourceUsage(GetResID(countBuffer), ResourceUsage::Indirect); // only allocate up to one indirect sub-command to avoid pessimistic allocation if // maxDrawCount is very high but the actual action count is low. diff --git a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp index 7ca31f75c1d..e003442056b 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp @@ -1941,15 +1941,12 @@ bool WrappedVulkan::Serialise_vkCopyImageToImage(SerialiserType &ser, VkDevice d VulkanEventNode &eventNode = GetLastEventNode(); if(CopyImageToImageInfo.srcImage == CopyImageToImageInfo.dstImage) { - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(CopyImageToImageInfo.srcImage), ResourceUsage::Copy)); + eventNode.AddResourceUsage(GetResID(CopyImageToImageInfo.srcImage), ResourceUsage::Copy); } else { - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(CopyImageToImageInfo.srcImage), ResourceUsage::CopySrc)); - eventNode.resourceUsage.push_back( - make_rdcpair(GetResID(CopyImageToImageInfo.dstImage), ResourceUsage::CopyDst)); + eventNode.AddResourceUsage(GetResID(CopyImageToImageInfo.srcImage), ResourceUsage::CopySrc); + eventNode.AddResourceUsage(GetResID(CopyImageToImageInfo.dstImage), ResourceUsage::CopyDst); } } } @@ -2030,7 +2027,7 @@ bool WrappedVulkan::Serialise_vkCopyImageToMemory(SerialiserType &ser, VkDevice AddAction(action); VulkanEventNode &eventNode = GetLastEventNode(); - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(srcImage), ResourceUsage::CopySrc)); + eventNode.AddResourceUsage(GetResID(srcImage), ResourceUsage::CopySrc); } } @@ -2115,7 +2112,7 @@ bool WrappedVulkan::Serialise_vkCopyMemoryToImage(SerialiserType &ser, VkDevice AddAction(action); VulkanEventNode &eventNode = GetLastEventNode(); - eventNode.resourceUsage.push_back(make_rdcpair(GetResID(dstImage), ResourceUsage::CopyDst)); + eventNode.AddResourceUsage(GetResID(dstImage), ResourceUsage::CopyDst); } } diff --git a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp index 0fa857def3c..72d9bc90b35 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp @@ -1106,8 +1106,7 @@ bool WrappedVulkan::SerialiseUnmap(SerialiserType &ser, VkDeviceMemory memory, u } if(IsLoading(m_State)) - m_LoadingEventNode.resourceUsage.push_back( - make_rdcpair(GetResID(memory), ResourceUsage::CPUWrite)); + m_LoadingEventNode.AddResourceUsage(GetResID(memory), ResourceUsage::CPUWrite); const Intervals &bindings = m_CreationInfo.m_Memory[GetResID(memory)].bindings; @@ -1463,8 +1462,7 @@ bool WrappedVulkan::Serialise_vkFlushMappedMemoryRanges(SerialiserType &ser, VkD if(IsReplayingAndReading() && MemRange.memory != VK_NULL_HANDLE && MemRange.size > 0) { if(IsLoading(m_State)) - m_LoadingEventNode.resourceUsage.push_back( - make_rdcpair(GetResID(MemRange.memory), ResourceUsage::CPUWrite)); + m_LoadingEventNode.AddResourceUsage(GetResID(MemRange.memory), ResourceUsage::CPUWrite); VkResult ret = ObjDisp(device)->MapMemory(Unwrap(device), Unwrap(MemRange.memory), MemRange.offset, diff --git a/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp index 91f23f207e8..ca0e7e985d9 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_sync_funcs.cpp @@ -810,8 +810,7 @@ bool WrappedVulkan::Serialise_vkCmdWaitEvents( const VkImageMemoryBarrier &b = pImageMemoryBarriers[i]; if(b.image != VK_NULL_HANDLE && b.oldLayout == VK_IMAGE_LAYOUT_UNDEFINED) { - m_LoadingEventNode.resourceUsage.push_back( - make_rdcpair(GetResID(b.image), ResourceUsage::Discard)); + m_LoadingEventNode.AddResourceUsage(GetResID(b.image), ResourceUsage::Discard); } } } @@ -1322,8 +1321,7 @@ bool WrappedVulkan::Serialise_vkCmdWaitEvents2(SerialiserType &ser, VkCommandBuf if(b.image != VK_NULL_HANDLE && b.oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && b.newLayout != VK_IMAGE_LAYOUT_UNDEFINED) { - m_LoadingEventNode.resourceUsage.push_back( - make_rdcpair(GetResID(b.image), ResourceUsage::Discard)); + m_LoadingEventNode.AddResourceUsage(GetResID(b.image), ResourceUsage::Discard); } } } From 6272c1795ef7318cb315062795af3c19ae020ef3 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Sat, 22 Aug 2026 12:51:12 +0100 Subject: [PATCH 02/16] Added helper function VulkanEventNode::AddUniqueResourceUsage --- renderdoc/driver/vulkan/vk_core.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index 19ce3ef76b3..63a1148b50d 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -138,6 +138,13 @@ struct VulkanEventNode { resourceUsage.push_back(make_rdcpair(id, usage)); } + void AddUniqueResourceUsage(ResourceId id, ResourceUsage usage) + { + RDCASSERTNOTEQUAL(id, ResourceId()); + if(resourceUsage.contains(make_rdcpair(id, usage))) + return; + resourceUsage.push_back(make_rdcpair(id, usage)); + } // eventId is not used APIEvent event; From 1c8fd44c3da884014723229a2486418489ab176e Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Tue, 15 Sep 2026 13:09:58 +0100 Subject: [PATCH 03/16] VK_Resource_Usage improvements - add secondary commands buffers to Submit() calls so they get put back into the command buffer pool - fix validation error about renderEndSemaphore - fix sync validation errors - add more names to objects - fix some incorrect names - move the pipeline bind to after marker "DrawIndirect: Single" - add marker to start and end of EIDS which should be tracked for resource usage - add a per frame update descriptorset - bind compute descriptor sets before binding compute pipelines --- util/test/demos/vk/vk_resource_usage.cpp | 74 ++++++++---- util/test/tests/Vulkan/VK_Resource_Usage.py | 124 ++++++++++---------- 2 files changed, 114 insertions(+), 84 deletions(-) diff --git a/util/test/demos/vk/vk_resource_usage.cpp b/util/test/demos/vk/vk_resource_usage.cpp index 524fc25d175..db8d11d05e5 100644 --- a/util/test/demos/vk/vk_resource_usage.cpp +++ b/util/test/demos/vk/vk_resource_usage.cpp @@ -542,6 +542,7 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) VkPipelineLayout meshShaderLayout = createPipelineLayout( vkh::PipelineLayoutCreateInfo({}, {vkh::PushConstantRange(VK_SHADER_STAGE_ALL, 0, 8)})); + setName(meshShaderLayout, "Mesh Shader Pipeline Layout"); VkPipeline meshShaderPipe = VK_NULL_HANDLE; if(meshShader) @@ -563,6 +564,7 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) vkMeshShaderPipeCreateInfo->pInputAssemblyState = NULL; meshShaderPipe = createGraphicsPipeline(vkMeshShaderPipeCreateInfo); + setName(meshShaderPipe, "Mesh Shader Pipeline"); } VkDescriptorSetLayout compDescSetLayout = @@ -574,7 +576,7 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) VkPipelineLayout compDescSetPipeLayout = createPipelineLayout(vkh::PipelineLayoutCreateInfo({compDescSetLayout})); - setName(compDescSetPipeLayout, "Compute Pipeline Layout"); + setName(compDescSetPipeLayout, "Compute Descriptor Set Pipeline Layout"); vkh::ComputePipelineCreateInfo compDescSetPipeCreateInfo( compDescSetPipeLayout, @@ -607,7 +609,7 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) if(descBuffer) { compDescBuffPipeLayout = createPipelineLayout(vkh::PipelineLayoutCreateInfo({descBuffLayout})); - setName(compDescSetPipeLayout, "Compute Descriptor Buffer Pipeline Layout"); + setName(compDescBuffPipeLayout, "Compute Descriptor Buffer Pipeline Layout"); vkh::ComputePipelineCreateInfo compDescBuffPipeCreateInfo( compDescBuffPipeLayout, @@ -836,6 +838,19 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) { viewPort = {0.0f, 0.0f, sqSize, sqSize, 0.0f, 1.0f}; setName(mainWindow->GetFB(), "Main Framebuffer"); + std::vector secondaries; + + vkh::updateDescriptorSets( + device, + { + vkh::WriteDescriptorSet(compWriteDataDescSet, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + {vkh::DescriptorBufferInfo(indirectData.buffer)}), + vkh::WriteDescriptorSet(compDescSet, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(compBufIn.buffer)}), + vkh::WriteDescriptorSet( + descSet, 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + {vkh::DescriptorImageInfo(offimgRTV, VK_IMAGE_LAYOUT_GENERAL, linearSampler)}), + }); VkCommandBuffer barrierSecCmd = GetCommandBuffer(VK_COMMAND_BUFFER_LEVEL_SECONDARY); vkBeginCommandBuffer(barrierSecCmd, vkh::CommandBufferBeginInfo( @@ -846,7 +861,7 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) {vkh::BufferMemoryBarrier(VK_ACCESS_NONE, VK_ACCESS_NONE, barrier2Buffer.buffer)}); vkEndCommandBuffer(barrierSecCmd); - VkCommandBuffer secCmdBuffers[3]; + std::vector secCmdBuffers; for(size_t i = 0; i < 2; i++) { VkCommandBuffer secCmd = GetCommandBuffer(VK_COMMAND_BUFFER_LEVEL_SECONDARY); @@ -876,7 +891,7 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) popMarker(secCmd); vkEndCommandBuffer(secCmd); - secCmdBuffers[i] = secCmd; + secCmdBuffers.push_back(secCmd); } { VkCommandBuffer emptySecCmd = GetCommandBuffer(VK_COMMAND_BUFFER_LEVEL_SECONDARY); @@ -885,7 +900,7 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, vkh::CommandBufferInheritanceInfo(renderPass, 0))); vkEndCommandBuffer(emptySecCmd); - secCmdBuffers[2] = emptySecCmd; + secCmdBuffers.push_back(emptySecCmd); } VkCommandBuffer nestedCmd = GetCommandBuffer(VK_COMMAND_BUFFER_LEVEL_SECONDARY); @@ -949,6 +964,7 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) VkCommandBuffer cmd = GetCommandBuffer(); vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()); + setMarker(cmd, "Resource Usage: Start"); VkImage swapimg = StartUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); @@ -999,9 +1015,9 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) {vkh::BufferMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, indirectData.buffer)}); - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, compWriteDataPipe); vkh::cmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, compWriteDataPipeLayout, 0, {compWriteDataDescSet}, {}); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, compWriteDataPipe); uint32_t mode = 0; vkCmdPushConstants(cmd, compWriteDataPipeLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, 4, &mode); @@ -1068,7 +1084,8 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) { vkCmdBeginRenderPass(cmd, mainWindow->beginRP(), VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); - vkCmdExecuteCommands(cmd, 3, secCmdBuffers); + vkCmdExecuteCommands(cmd, (uint32_t)secCmdBuffers.size(), secCmdBuffers.data()); + secondaries.insert(secondaries.end(), secCmdBuffers.begin(), secCmdBuffers.end()); vkCmdEndRenderPass(cmd); } popMarker(cmd); @@ -1092,6 +1109,7 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) pushMarker(cmd, "Secondary Command Buffer"); { vkCmdExecuteCommands(cmd, 1, &compSecCmd); + secondaries.push_back(compSecCmd); } popMarker(cmd); } @@ -1101,9 +1119,9 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) { pushMarker(cmd, "Indirect Dispatch Write IndirectDraw Data"); { - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, compWriteDataPipe); vkh::cmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, compWriteDataPipeLayout, 0, {compWriteDataDescSet}, {}); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, compWriteDataPipe); uint32_t mode = 1; vkCmdPushConstants(cmd, compWriteDataPipeLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, 4, &mode); @@ -1187,9 +1205,9 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) {vkh::BufferMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, indirectData.buffer)}); - vkCmdBindPipeline(indirectCompSecCmd, VK_PIPELINE_BIND_POINT_COMPUTE, compWriteDataPipe); vkh::cmdBindDescriptorSets(indirectCompSecCmd, VK_PIPELINE_BIND_POINT_COMPUTE, compWriteDataPipeLayout, 0, {compWriteDataDescSet}, {}); + vkCmdBindPipeline(indirectCompSecCmd, VK_PIPELINE_BIND_POINT_COMPUTE, compWriteDataPipe); uint32_t mode = 0; vkCmdPushConstants(indirectCompSecCmd, compWriteDataPipeLayout, @@ -1201,10 +1219,11 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) vkCmdPushConstants(indirectCompSecCmd, compWriteDataPipeLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, 4, &mode); - vkh::cmdPipelineBarrier( - indirectCompSecCmd, {}, - {vkh::BufferMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT, indirectData.buffer)}); + vkh::cmdPipelineBarrier(indirectCompSecCmd, {}, + {vkh::BufferMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT | + VK_ACCESS_INDIRECT_COMMAND_READ_BIT, + indirectData.buffer)}); setMarker(indirectCompSecCmd, "DispatchIndirect"); vkCmdDispatchIndirect(indirectCompSecCmd, indirectData.buffer, 0); @@ -1212,8 +1231,10 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) vkh::cmdPipelineBarrier( indirectCompSecCmd, {}, - {vkh::BufferMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT, indirectData.buffer)}); + {vkh::BufferMemoryBarrier( + VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT, + indirectData.buffer)}); vkCmdDispatchIndirect(indirectCompSecCmd, indirectData.buffer, 0); @@ -1226,6 +1247,7 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) } vkCmdExecuteCommands(cmd, 1, &indirectCompSecCmd); + secondaries.push_back(indirectCompSecCmd); vkCmdBeginRenderPass(cmd, mainWindow->beginRP(), VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); @@ -1246,10 +1268,9 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) descSetPipeLayout, 0, {descSet}, {}); vkh::cmdBindVertexBuffers(indirectDrawSecCmd, 0, {vb.buffer}, {0}); vkCmdBindIndexBuffer(indirectDrawSecCmd, ib.buffer, 0, VK_INDEX_TYPE_UINT32); - vkCmdBindPipeline(indirectDrawSecCmd, VK_PIPELINE_BIND_POINT_GRAPHICS, descSetPipe); setMarker(indirectDrawSecCmd, "DrawIndirect: Single"); - + vkCmdBindPipeline(indirectDrawSecCmd, VK_PIPELINE_BIND_POINT_GRAPHICS, descSetPipe); vkCmdSetViewport(indirectDrawSecCmd, 0, 1, &viewPort); vkCmdDrawIndirect(indirectDrawSecCmd, indirectData.buffer, offset, 1, strideDraw); NextTest(); @@ -1272,6 +1293,7 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) vkEndCommandBuffer(indirectDrawSecCmd); } vkCmdExecuteCommands(cmd, 1, &indirectDrawSecCmd); + secondaries.push_back(indirectDrawSecCmd); vkCmdEndRenderPass(cmd); } @@ -1292,9 +1314,9 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) {descSet}, {}); vkh::cmdBindVertexBuffers(cmd, 0, {vb.buffer}, {0}); vkCmdBindIndexBuffer(cmd, ib.buffer, 0, VK_INDEX_TYPE_UINT32); - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, descSetPipe); setMarker(cmd, "DrawIndirect: Single"); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, descSetPipe); vkCmdSetViewport(cmd, 0, 1, &viewPort); size_t drawIndirectOffset = offset; vkCmdDrawIndirect(cmd, indirectData.buffer, drawIndirectOffset, 1, strideDraw); @@ -1379,7 +1401,8 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) vkEndCommandBuffer(cmd); - Submit(0, 3, {cmd}); + Submit(0, 2, {cmd}, secondaries); + secondaries.clear(); cmd = GetCommandBuffer(); @@ -1397,10 +1420,12 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) vkCmdBeginRenderPass(cmd, mainWindow->beginRP(), VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); vkCmdExecuteCommands(cmd, 1, &nestedCmd); + secondaries.push_back(nestedCmd); vkCmdEndRenderPass(cmd); setMarker(cmd, "Dispatch"); vkCmdExecuteCommands(cmd, 1, &compNestedSecCmd); + secondaries.push_back(compNestedSecCmd); popMarker(cmd); } @@ -1442,7 +1467,7 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) vkh::cmdPipelineBarrier( restoreDescBufCmd, {}, { - vkh::BufferMemoryBarrier(VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT, + vkh::BufferMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, descBackupBuf.buffer), vkh::BufferMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, descBuf.buffer), @@ -1549,7 +1574,8 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) vkEndCommandBuffer(cmd); cmds.push_back(cmd); - Submit(1, 3, cmds); + Submit(1, 2, cmds, secondaries); + secondaries.clear(); std::vector cmds2; cmds2.push_back(barrierCmd); @@ -1575,9 +1601,13 @@ RD_TEST(VK_Resource_Usage, VulkanGraphicsTest) popMarker(cmd); } + setMarker(cmd, "Resource Usage: End"); vkEndCommandBuffer(cmd); - Submit(2, 3, {cmd}); + { + VkSubmitInfo finalSubmit = vkh::SubmitInfo({cmd}); + CHECK_VKR(vkQueueSubmit(queue, 1, &finalSubmit, VK_NULL_HANDLE)); + } Present(); } diff --git a/util/test/tests/Vulkan/VK_Resource_Usage.py b/util/test/tests/Vulkan/VK_Resource_Usage.py index 9f4144c1b49..6e9f94b6bb7 100644 --- a/util/test/tests/Vulkan/VK_Resource_Usage.py +++ b/util/test/tests/Vulkan/VK_Resource_Usage.py @@ -176,16 +176,16 @@ def check_capture(self): # the swap chain image has usage, anything else does not if res.resourceId == swapImage: expectedUsage = [ - (6,rd.ResourceUsage.Barrier), - (6,rd.ResourceUsage.Discard), - (7,rd.ResourceUsage.Clear), - (8,rd.ResourceUsage.Barrier)] + (7,rd.ResourceUsage.Barrier), + (7,rd.ResourceUsage.Discard), + (8,rd.ResourceUsage.Clear), + (9,rd.ResourceUsage.Barrier)] for eid in drawEIDs: expectedUsage.append((eid,rd.ResourceUsage.ColorTarget)) for eid in meshDispatchEIDs: expectedUsage.append((eid,rd.ResourceUsage.ColorTarget)) expectedUsage += [ - (235+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier)] + (236+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier)] else: expectedUsage = [] elif res.type == rd.ResourceType.RenderPass: @@ -226,39 +226,39 @@ def check_capture(self): elif nestedSecondaries and eid > markerNestedSecondaryCommandBuffer: expectedUsage.append((eid,rd.ResourceUsage.CS_RWResource)) if res.name == "Indirect Data": - expectedUsage += [(14,rd.ResourceUsage.Barrier), - (15,rd.ResourceUsage.Clear), - (16,rd.ResourceUsage.Barrier), - (20,rd.ResourceUsage.CS_RWResource), - (21,rd.ResourceUsage.Barrier), - (97,rd.ResourceUsage.Barrier), - (109,rd.ResourceUsage.Barrier), - (120,rd.ResourceUsage.Indirect), - (124,rd.ResourceUsage.Indirect), - (142,rd.ResourceUsage.Indirect), - (144,rd.ResourceUsage.Barrier), - (149,rd.ResourceUsage.Barrier), - (150,rd.ResourceUsage.Clear), - (151,rd.ResourceUsage.Barrier), - (155,rd.ResourceUsage.CS_RWResource), - (157,rd.ResourceUsage.Barrier), - (161,rd.ResourceUsage.Barrier), - (163,rd.ResourceUsage.Barrier), - (177,rd.ResourceUsage.Indirect), - (208,rd.ResourceUsage.Indirect)] + expectedUsage += [(15,rd.ResourceUsage.Barrier), + (16,rd.ResourceUsage.Clear), + (17,rd.ResourceUsage.Barrier), + (21,rd.ResourceUsage.CS_RWResource), + (22,rd.ResourceUsage.Barrier), + (98,rd.ResourceUsage.Barrier), + (110,rd.ResourceUsage.Barrier), + (121,rd.ResourceUsage.Indirect), + (125,rd.ResourceUsage.Indirect), + (143,rd.ResourceUsage.Indirect), + (145,rd.ResourceUsage.Barrier), + (150,rd.ResourceUsage.Barrier), + (151,rd.ResourceUsage.Clear), + (152,rd.ResourceUsage.Barrier), + (156,rd.ResourceUsage.CS_RWResource), + (158,rd.ResourceUsage.Barrier), + (162,rd.ResourceUsage.Barrier), + (164,rd.ResourceUsage.Barrier), + (178,rd.ResourceUsage.Indirect), + (209,rd.ResourceUsage.Indirect)] if drawIndirectCount: expectedUsage += [ - (247,rd.ResourceUsage.Indirect), - (254,rd.ResourceUsage.Indirect)] - expectedUsage += [(231+countDrawIndirectCount,rd.ResourceUsage.Barrier)] + (248,rd.ResourceUsage.Indirect), + (255,rd.ResourceUsage.Indirect)] + expectedUsage += [(232+countDrawIndirectCount,rd.ResourceUsage.Barrier)] if nestedSecondaries: expectedUsage += [ - (268+countDrawIndirectCount+countDescBufferCopy,rd.ResourceUsage.Barrier)] + (269+countDrawIndirectCount+countDescBufferCopy,rd.ResourceUsage.Barrier)] if meshShader: expectedUsage += [ - (249+countDrawIndirectCount+countNested+countDescBuffer,rd.ResourceUsage.Indirect), - (252+countDrawIndirectCount+countNested+countDescBuffer,rd.ResourceUsage.Indirect), - (255+countDrawIndirectCount+countNested+countDescBuffer,rd.ResourceUsage.Indirect)] + (250+countDrawIndirectCount+countNested+countDescBuffer,rd.ResourceUsage.Indirect), + (253+countDrawIndirectCount+countNested+countDescBuffer,rd.ResourceUsage.Indirect), + (256+countDrawIndirectCount+countNested+countDescBuffer,rd.ResourceUsage.Indirect)] for eid in indirectEIDs: # Dispatch + Indirect if eid in dispatchEIDs: @@ -272,44 +272,44 @@ def check_capture(self): expectedUsage.append((eid,rd.ResourceUsage.IndexBuffer)) expectedUsage.append((eid,rd.ResourceUsage.Indirect)) if res.name == "Barrier Buffer": - expectedUsage = [(243+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (251+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (259+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (267+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (275+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (283+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (291+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (299+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (307+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (315+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier)] + expectedUsage = [(244+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), + (252+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), + (260+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), + (268+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), + (276+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), + (284+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), + (292+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), + (300+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), + (308+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), + (316+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier)] if res.name == "Barrier2 Buffer": - expectedUsage = [(323+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (328+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (333+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (338+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier)] + expectedUsage = [(324+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), + (329+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), + (334+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), + (339+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier)] if res.name == "Descriptor Buffer": if descBuffer: - expectedUsage = [(235+countDrawIndirectCount,rd.ResourceUsage.Barrier), - (236+countDrawIndirectCount,rd.ResourceUsage.CopySrc), - (237+countDrawIndirectCount,rd.ResourceUsage.Barrier), - (238+countDrawIndirectCount,rd.ResourceUsage.Clear), - (241+countDrawIndirectCount,rd.ResourceUsage.Barrier), - (242+countDrawIndirectCount,rd.ResourceUsage.CopyDst)] + expectedUsage = [(236+countDrawIndirectCount,rd.ResourceUsage.Barrier), + (237+countDrawIndirectCount,rd.ResourceUsage.CopySrc), + (238+countDrawIndirectCount,rd.ResourceUsage.Barrier), + (239+countDrawIndirectCount,rd.ResourceUsage.Clear), + (242+countDrawIndirectCount,rd.ResourceUsage.Barrier), + (243+countDrawIndirectCount,rd.ResourceUsage.CopyDst)] if res.name == "Descriptor Backup Buffer": if descBuffer: - expectedUsage = [(235+countDrawIndirectCount,rd.ResourceUsage.Barrier), - (236+countDrawIndirectCount,rd.ResourceUsage.CopyDst), - (241+countDrawIndirectCount,rd.ResourceUsage.Barrier), - (242+countDrawIndirectCount,rd.ResourceUsage.CopySrc)] + expectedUsage = [(236+countDrawIndirectCount,rd.ResourceUsage.Barrier), + (237+countDrawIndirectCount,rd.ResourceUsage.CopyDst), + (242+countDrawIndirectCount,rd.ResourceUsage.Barrier), + (243+countDrawIndirectCount,rd.ResourceUsage.CopySrc)] elif res.type == rd.ResourceType.Texture: if res.name == "Offscreen MSAA Image": - expectedUsage = [(11,rd.ResourceUsage.Barrier), - (11,rd.ResourceUsage.Discard), - (12,rd.ResourceUsage.Clear)] + expectedUsage = [(12,rd.ResourceUsage.Barrier), + (12,rd.ResourceUsage.Discard), + (13,rd.ResourceUsage.Clear)] if res.name == "Offscreen Image": - expectedUsage = [(9,rd.ResourceUsage.Barrier), - (9,rd.ResourceUsage.Discard), - (10,rd.ResourceUsage.Clear)] + expectedUsage = [(10,rd.ResourceUsage.Barrier), + (10,rd.ResourceUsage.Discard), + (11,rd.ResourceUsage.Clear)] for eid in descSetDrawEIDs: expectedUsage.append((eid,rd.ResourceUsage.PS_Resource)) for eid in descBufferDrawEIDs: From a08ead94ff7775f4c7dc0955bf1eb97c80966af9 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Tue, 15 Sep 2026 18:23:04 +0100 Subject: [PATCH 04/16] Remove hard coded EIDs from VK_Resource_Usage python --- util/test/tests/Vulkan/VK_Resource_Usage.py | 312 ++++++++++++-------- 1 file changed, 182 insertions(+), 130 deletions(-) diff --git a/util/test/tests/Vulkan/VK_Resource_Usage.py b/util/test/tests/Vulkan/VK_Resource_Usage.py index 6e9f94b6bb7..f1ca704d5cd 100644 --- a/util/test/tests/Vulkan/VK_Resource_Usage.py +++ b/util/test/tests/Vulkan/VK_Resource_Usage.py @@ -7,6 +7,8 @@ class VK_Resource_Usage(rdtest.TestCase): demos_test_name = 'VK_Resource_Usage' resourceUsages: Dict[rd.ResourceId, List[rd.EventUsage]] = {} eids: List[int] = [] + markerStart = 0 + markerEnd = 0 def add_action(self, action: rd.ActionDescription): self.eids.append(action.eventId) @@ -17,14 +19,21 @@ def add_action(self, action: rd.ActionDescription): def check_resource_usage(self, res: rd.ResourceDescription, expectedUsages: List[Tuple[int,rd.ResourceUsage]]): usages = self.resourceUsages[res.resourceId] - if len(usages) != len(expectedUsages): - for u in usages: - rdtest.log.print(f"Resource '{res.name}' {res.resourceId} usage EID:{u.eventId} usage:{u.usage.name}") + if len(usages) == 1 and usages[0].eventId == 0 and usages[0].usage == rd.ResourceUsage.Unused: + resUsages = usages + else: + resUsages = [u for u in usages if u.eventId >= self.markerStart and u.eventId <= self.markerEnd] + rdtest.log.print(f"Resource '{res.name}' type:{res.type.name} {res.resourceId} usages:{len(resUsages)} expectedUsages:{len(expectedUsages)}") + if len(resUsages) != len(expectedUsages): + rdtest.log.print(f"Usages for Resource '{res.name}' {res.resourceId}") + for u in resUsages: + rdtest.log.print(f"EID:{u.eventId} usage:{u.usage.name}") + rdtest.log.print(f"Expected Usages for Resource '{res.name}' {res.resourceId}") for u in expectedUsages: eid, usage = u - rdtest.log.print(f"Resource '{res.name}' {res.resourceId} usage EID:{eid} usage:{usage.name}") - raise rdtest.TestFailureException(f"'{res.name}' {res.resourceId} Incorrect resource usages count expected:{len(expectedUsages)} actual:{len(usages)}") - for i, u in enumerate(usages): + rdtest.log.print(f"EID:{eid} usage:{usage.name}") + raise rdtest.TestFailureException(f"'{res.name}' {res.resourceId} Incorrect resource usages count expected:{len(expectedUsages)} actual:{len(resUsages)}") + for i, u in enumerate(resUsages): eid, usage = expectedUsages[i] if u.usage != usage: raise rdtest.TestFailureException(f"'{res.name}' {res.resourceId} EID:{u.eventId} Incorrect resource usage expected:{usage.name} actual:{u.usage.name}") @@ -50,13 +59,6 @@ def check_capture(self): meshShader = self.find_action("Mesh Shader") is not None rdtest.log.print(f"Has Mesh Shader: {'Yes' if meshShader else 'No'}") - countDrawIndirectCount = 30 if drawIndirectCount else 0 - countNested = 39 if nestedSecondaries else 0 - countDescBufferCopy = 10 if descBuffer else 0 - countDescBuffer = 21 if descBuffer else 0 - countDescBuffer += countDescBufferCopy - countMeshShader = 33 if meshShader else 0 - # Walk the actions finding all the Dispatch, Draw, DrawIndexed, DrawIndirect, DrawIndirectCount sdfile = self.controller.GetStructuredFile() actions = self.controller.GetRootActions().copy() @@ -74,23 +76,53 @@ def check_capture(self): resetFencesEIDs: List[int] = [] descSetDrawEIDs: List[int] = [] descBufferDrawEIDs: List[int] = [] + cmdDrawIndirectEIDs: List[int] = [] + cmdMeshDispatchIndirectEIDs: List[int] = [] + endRenderPassEIDs : List[int] = [] + fillBufferEIDs: List[int] = [] + copyBufferEIDs: List[int] = [] + markerGraphics = 0 + markerGraphicsNoDescriptorSet = 0 markerGraphicsDescriptorSet = 0 markerGraphicsSecondaryCommandBuffer = 0 markerIndirect = 0 + markerIndirectWriteIndirectDispatchData = 0 + markerIndirectDispatchWriteIndirectDrawData = 0 + markerIndirectIndirectDraws = 0 + markerIndirectSecondaryCommandBuffer = 0 + markerIndirectSecondaryCommandBufferDispatchIndirect = 0 markerIndirectEnd = 0 markerNestedSecondaryCommandBuffer = 0 + markerNestedSecondaryCommandBufferDispatch = 0 markerDescriptorBuffer = 0 markerBarrierCommandSubmitFence = 0 markerCompute = 0 markerComputeDescriptorSet = 0 + markerComputeSecondaryCommandBuffer = 0 + markerLooseEventsAfterIndirectDrawsDrawIndirectSingle = 0 + markerMultipleCommandBufferSubmits = 0 + markerMultipleSecondaryCommandBufferExecutes = 0 + self.markerStart = self.find_action("Resource Usage: Start").eventId + self.markerEnd = self.find_action("Resource Usage: End").eventId + markerIndirectWriteIndirectDispatchData = self.find_action("Indirect Write IndirectDispatch Data").eventId + markerGraphics = self.find_action("Graphics").eventId + markerGraphicsNoDescriptorSet = self.find_action("No Descriptor Set", markerGraphics).eventId + markerGraphicsDescriptorSet = self.find_action("Descriptor Set", markerGraphicsNoDescriptorSet+1).eventId markerGraphicsSecondaryCommandBuffer = self.find_action("Secondary Command Buffer").eventId if nestedSecondaries: markerNestedSecondaryCommandBuffer = self.find_action("Nested Secondary Command Buffer").eventId + markerNestedSecondaryCommandBufferDispatch = self.find_action("Dispatch", markerNestedSecondaryCommandBuffer).eventId if descBuffer: markerDescriptorBuffer = self.find_action("Descriptor Buffer").eventId markerBarrierCommandSubmitFence = self.find_action("Barrier Command Submit Fence").eventId + markerCompute = self.find_action("Compute").eventId + markerComputeSecondaryCommandBuffer = self.find_action("Secondary Command Buffer", markerCompute).eventId + markerLooseEventsAfterIndirectDraws = self.find_action("Loose Events After Indirect Draws").eventId + markerLooseEventsAfterIndirectDrawsDrawIndirectSingle = self.find_action("DrawIndirect: Single", markerLooseEventsAfterIndirectDraws).eventId + markerMultipleCommandBufferSubmits = self.find_action("Multiple Command Buffer Submits").eventId + markerMultipleSecondaryCommandBufferExecutes = self.find_action("Multiple Secondary Command Buffer Executes").eventId while len(actions) > 0: action = actions.pop(0) @@ -115,19 +147,23 @@ def check_capture(self): meshDispatchEIDs.append(action.eventId) if flags & rd.ActionFlags.MultiAction: multiEIDs.append(action.eventId) + if "Count" in action.customName: + if not action.eventId in indirectCountEIDs: + indirectCountEIDs.append(action.eventId) if flags & rd.ActionFlags.PushMarker: parentName = action.parent.customName if action.parent is not None else None markerPath = parentName + "." + action.customName if parentName is not None else action.customName - if not markerGraphicsDescriptorSet and markerPath == "Graphics.Descriptor Set": - rdtest.log.print(f"{action.parent.customName} {action.parent.eventId} {action.customName} {action.eventId}") - markerGraphicsDescriptorSet = action.eventId if not markerIndirect and markerPath == "Indirect": markerIndirect = action.eventId markerIndirectEnd = action.children[-1].eventId - if not markerCompute and markerPath == "Compute": - markerCompute = action.eventId if not markerComputeDescriptorSet and markerPath == "Compute.Descriptor Set": markerComputeDescriptorSet = action.eventId + if "vkCmdDrawIndirect(" in action.customName: + cmdDrawIndirectEIDs.append(action.eventId) + if "vkCmdDrawIndexedIndirect(" in action.customName: + cmdDrawIndirectEIDs.append(action.eventId) + if "vkCmdDrawMeshTasksIndirectEXT(" in action.customName: + cmdMeshDispatchIndirectEIDs.append(action.eventId) for e in action.events: eventName = sdfile.chunks[e.chunkIndex].name if "vkQueueSubmit" in eventName: @@ -136,28 +172,41 @@ def check_capture(self): waitFencesEIDs.append(e.eventId) if "vkResetFences" in eventName: resetFencesEIDs.append(e.eventId) + if "vkCmdCopyBuffer" in eventName: + copyBufferEIDs.append(e.eventId) + if "vkCmdFillBuffer" in eventName: + fillBufferEIDs.append(e.eventId) + if "vkCmdEndRenderPass" in eventName: + endRenderPassEIDs.append(e.eventId) - for eid in drawEIDs: - if eid > markerGraphicsDescriptorSet and eid < markerGraphicsSecondaryCommandBuffer: - descSetDrawEIDs.append(eid) - elif eid > markerIndirect and (not nestedSecondaries or eid < markerNestedSecondaryCommandBuffer): - descSetDrawEIDs.append(eid) - elif descBuffer and eid > markerDescriptorBuffer: - descBufferDrawEIDs.append(eid) + markerIndirectDispatchWriteIndirectDrawData = self.find_action("Indirect Dispatch Write IndirectDraw Data", markerIndirect).eventId + markerIndirectIndirectDraws = self.find_action("Indirect Draws", markerIndirectDispatchWriteIndirectDrawData).eventId + markerIndirectSecondaryCommandBuffer = self.find_action("Secondary Command Buffer", markerIndirect).eventId + markerIndirectSecondaryCommandBufferDispatchIndirect = self.find_action("DispatchIndirect", markerIndirectSecondaryCommandBuffer).eventId + + descSetDrawEIDs += [eid for eid in drawEIDs + if (eid > markerGraphicsDescriptorSet and eid < markerGraphicsSecondaryCommandBuffer) or + (eid > markerIndirect and (not nestedSecondaries or eid < markerNestedSecondaryCommandBuffer))] + descBufferDrawEIDs += [eid for eid in drawEIDs if descBuffer and eid > markerDescriptorBuffer] drawEIDs.sort() indexedEIDs.sort() indexedSpecialEIDs.sort() dispatchEIDs.sort() + multiEIDs.sort() indirectEIDs.sort() meshDispatchEIDs.sort() - multiEIDs.sort() indirectCountEIDs.sort() submitEIDs.sort() waitFencesEIDs.sort() resetFencesEIDs.sort() descSetDrawEIDs.sort() descBufferDrawEIDs.sort() + fillBufferEIDs.sort() + copyBufferEIDs.sort() + endRenderPassEIDs.sort() + cmdDrawIndirectEIDs.sort() + cmdMeshDispatchIndirectEIDs.sort() action = self.find_action("Draw") self.set_event(action.eventId, False) @@ -176,16 +225,13 @@ def check_capture(self): # the swap chain image has usage, anything else does not if res.resourceId == swapImage: expectedUsage = [ - (7,rd.ResourceUsage.Barrier), - (7,rd.ResourceUsage.Discard), - (8,rd.ResourceUsage.Clear), - (9,rd.ResourceUsage.Barrier)] - for eid in drawEIDs: - expectedUsage.append((eid,rd.ResourceUsage.ColorTarget)) - for eid in meshDispatchEIDs: - expectedUsage.append((eid,rd.ResourceUsage.ColorTarget)) - expectedUsage += [ - (236+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier)] + (self.markerStart + 1,rd.ResourceUsage.Barrier), + (self.markerStart + 1,rd.ResourceUsage.Discard), + (self.markerStart + 2,rd.ResourceUsage.Clear), + (self.markerStart + 3,rd.ResourceUsage.Barrier)] + expectedUsage += [(eid,rd.ResourceUsage.ColorTarget) for eid in drawEIDs] + expectedUsage += [(eid,rd.ResourceUsage.ColorTarget) for eid in meshDispatchEIDs] + expectedUsage += [(markerBarrierCommandSubmitFence - 1, rd.ResourceUsage.Barrier)] else: expectedUsage = [] elif res.type == rd.ResourceType.RenderPass: @@ -204,116 +250,123 @@ def check_capture(self): expectedUsage = [(0,rd.ResourceUsage.Unused)] elif res.type == rd.ResourceType.Buffer: if res.name == "Vertex Buffer": - for eid in drawEIDs: - expectedUsage.append((eid,rd.ResourceUsage.VertexBuffer)) + expectedUsage += [(eid,rd.ResourceUsage.VertexBuffer) for eid in drawEIDs] if res.name == "Index Buffer": - for eid in indexedEIDs: - expectedUsage.append((eid,rd.ResourceUsage.IndexBuffer)) + expectedUsage += [(eid,rd.ResourceUsage.IndexBuffer) for eid in indexedEIDs] if res.name == "Compute Buffer In": # All dispatches within "Compute" region # All dispatches within "Nested Secondary Command Buffer" region - for eid in dispatchEIDs: - if eid > markerComputeDescriptorSet and eid < markerIndirect: - expectedUsage.append((eid,rd.ResourceUsage.CS_Constants)) - elif nestedSecondaries and eid > markerNestedSecondaryCommandBuffer: - expectedUsage.append((eid,rd.ResourceUsage.CS_Constants)) + expectedUsage += [(eid,rd.ResourceUsage.CS_Constants) for eid in dispatchEIDs + if (eid > markerComputeDescriptorSet and eid < markerIndirect) or + (nestedSecondaries and eid > markerNestedSecondaryCommandBuffer)] if res.name == "Compute Buffer Out": # All dispatches within "Compute" region # All dispatches within "Nested Secondary Command Buffer" region - for eid in dispatchEIDs: - if eid > markerComputeDescriptorSet and eid < markerIndirect: - expectedUsage.append((eid,rd.ResourceUsage.CS_RWResource)) - elif nestedSecondaries and eid > markerNestedSecondaryCommandBuffer: - expectedUsage.append((eid,rd.ResourceUsage.CS_RWResource)) + expectedUsage += [(eid,rd.ResourceUsage.CS_RWResource) for eid in dispatchEIDs + if (eid > markerComputeDescriptorSet and eid < markerIndirect) or + (nestedSecondaries and eid > markerNestedSecondaryCommandBuffer)] if res.name == "Indirect Data": - expectedUsage += [(15,rd.ResourceUsage.Barrier), - (16,rd.ResourceUsage.Clear), - (17,rd.ResourceUsage.Barrier), - (21,rd.ResourceUsage.CS_RWResource), - (22,rd.ResourceUsage.Barrier), - (98,rd.ResourceUsage.Barrier), - (110,rd.ResourceUsage.Barrier), - (121,rd.ResourceUsage.Indirect), - (125,rd.ResourceUsage.Indirect), - (143,rd.ResourceUsage.Indirect), - (145,rd.ResourceUsage.Barrier), - (150,rd.ResourceUsage.Barrier), - (151,rd.ResourceUsage.Clear), - (152,rd.ResourceUsage.Barrier), - (156,rd.ResourceUsage.CS_RWResource), - (158,rd.ResourceUsage.Barrier), - (162,rd.ResourceUsage.Barrier), - (164,rd.ResourceUsage.Barrier), - (178,rd.ResourceUsage.Indirect), - (209,rd.ResourceUsage.Indirect)] - if drawIndirectCount: - expectedUsage += [ - (248,rd.ResourceUsage.Indirect), - (255,rd.ResourceUsage.Indirect)] - expectedUsage += [(232+countDrawIndirectCount,rd.ResourceUsage.Barrier)] - if nestedSecondaries: - expectedUsage += [ - (269+countDrawIndirectCount+countDescBufferCopy,rd.ResourceUsage.Barrier)] - if meshShader: - expectedUsage += [ - (250+countDrawIndirectCount+countNested+countDescBuffer,rd.ResourceUsage.Indirect), - (253+countDrawIndirectCount+countNested+countDescBuffer,rd.ResourceUsage.Indirect), - (256+countDrawIndirectCount+countNested+countDescBuffer,rd.ResourceUsage.Indirect)] - for eid in indirectEIDs: - # Dispatch + Indirect - if eid in dispatchEIDs: + expectedUsage += [(markerIndirectWriteIndirectDispatchData + 1,rd.ResourceUsage.Barrier), + (markerIndirectWriteIndirectDispatchData + 2,rd.ResourceUsage.Clear), + (markerIndirectWriteIndirectDispatchData + 3,rd.ResourceUsage.Barrier)] + + expectedUsage += [(markerIndirectSecondaryCommandBuffer - 2,rd.ResourceUsage.Barrier), + (markerIndirectSecondaryCommandBuffer + 3,rd.ResourceUsage.Barrier), + (markerIndirectSecondaryCommandBuffer + 5,rd.ResourceUsage.Barrier)] + expectedUsage += [(markerIndirectIndirectDraws - 2,rd.ResourceUsage.Barrier)] + + expectedUsage += [(markerIndirectSecondaryCommandBufferDispatchIndirect - 1,rd.ResourceUsage.Barrier), + (markerIndirectSecondaryCommandBufferDispatchIndirect + 3,rd.ResourceUsage.Barrier), + (markerIndirectSecondaryCommandBufferDispatchIndirect + 5,rd.ResourceUsage.Barrier)] + + for eid in dispatchEIDs: + if eid in indirectEIDs: + continue + if eid in indirectCountEIDs: + continue + if eid in multiEIDs: + continue + if eid in indexedSpecialEIDs: + continue + if eid > markerIndirectWriteIndirectDispatchData and eid < markerGraphics: expectedUsage.append((eid,rd.ResourceUsage.CS_RWResource)) + expectedUsage.append((eid+1,rd.ResourceUsage.Barrier)) + if eid > markerComputeSecondaryCommandBuffer and eid < markerIndirect: + expectedUsage.append((eid+1,rd.ResourceUsage.Barrier)) + if eid > markerIndirectSecondaryCommandBuffer and eid < markerIndirectEnd: + expectedUsage.append((eid,rd.ResourceUsage.CS_RWResource)) + for eid in cmdDrawIndirectEIDs: + if eid in indirectCountEIDs: + continue + if eid in multiEIDs: + continue + if eid in indexedSpecialEIDs: + continue + if eid > markerIndirectIndirectDraws and eid < markerIndirectEnd: + expectedUsage.append((eid,rd.ResourceUsage.Indirect)) + if eid > markerLooseEventsAfterIndirectDrawsDrawIndirectSingle and eid < markerDescriptorBuffer: expectedUsage.append((eid,rd.ResourceUsage.Indirect)) - for eid in multiEIDs: - expectedUsage.append((eid,rd.ResourceUsage.Indirect)) - for eid in indirectCountEIDs: - expectedUsage.append((eid,rd.ResourceUsage.Indirect)) - for eid in indexedSpecialEIDs: - expectedUsage.append((eid,rd.ResourceUsage.IndexBuffer)) - expectedUsage.append((eid,rd.ResourceUsage.Indirect)) + expectedUsage += [(eid,rd.ResourceUsage.Clear) for eid in fillBufferEIDs + if eid > markerIndirect and eid < markerIndirectEnd] + expectedUsage += [(eid,rd.ResourceUsage.CS_RWResource) for eid in indirectEIDs if eid in dispatchEIDs] + expectedUsage += [(eid,rd.ResourceUsage.Indirect) for eid in indirectEIDs if eid in dispatchEIDs] + expectedUsage += [(eid,rd.ResourceUsage.Indirect) for eid in multiEIDs if not eid in indirectCountEIDs] + expectedUsage += [(eid,rd.ResourceUsage.Indirect) for eid in indirectCountEIDs] + expectedUsage += [(eid,rd.ResourceUsage.Indirect) for eid in indirectCountEIDs] + expectedUsage += [(eid,rd.ResourceUsage.IndexBuffer) for eid in indexedSpecialEIDs if not eid in indirectCountEIDs] + expectedUsage += [(eid,rd.ResourceUsage.Indirect) for eid in indexedSpecialEIDs if not eid in indirectCountEIDs] + expectedUsage += [(eid,rd.ResourceUsage.Indirect) for eid in cmdMeshDispatchIndirectEIDs if not eid in multiEIDs] + for eid in endRenderPassEIDs: + if eid > markerLooseEventsAfterIndirectDraws: + expectedUsage.append((eid+2,rd.ResourceUsage.Barrier)) + break + for eid in dispatchEIDs: + if nestedSecondaries and eid > markerNestedSecondaryCommandBufferDispatch: + expectedUsage.append((eid + 1,rd.ResourceUsage.Barrier)) + break if res.name == "Barrier Buffer": - expectedUsage = [(244+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (252+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (260+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (268+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (276+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (284+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (292+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (300+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (308+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (316+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier)] + expectedUsage += [(eid,rd.ResourceUsage.Barrier) + for eid in range(markerMultipleCommandBufferSubmits + 1, markerMultipleCommandBufferSubmits + 1 + 10*8, 8)] if res.name == "Barrier2 Buffer": - expectedUsage = [(324+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (329+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (334+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier), - (339+countDrawIndirectCount+countNested+countDescBuffer+countMeshShader,rd.ResourceUsage.Barrier)] + expectedUsage += [(eid,rd.ResourceUsage.Barrier) + for eid in range(markerMultipleSecondaryCommandBufferExecutes + 3, markerMultipleSecondaryCommandBufferExecutes + 3 + 4*5, 5)] if res.name == "Descriptor Buffer": - if descBuffer: - expectedUsage = [(236+countDrawIndirectCount,rd.ResourceUsage.Barrier), - (237+countDrawIndirectCount,rd.ResourceUsage.CopySrc), - (238+countDrawIndirectCount,rd.ResourceUsage.Barrier), - (239+countDrawIndirectCount,rd.ResourceUsage.Clear), - (242+countDrawIndirectCount,rd.ResourceUsage.Barrier), - (243+countDrawIndirectCount,rd.ResourceUsage.CopyDst)] + first = True + for eid in copyBufferEIDs: + if eid > markerLooseEventsAfterIndirectDraws: + expectedUsage.append((eid-1,rd.ResourceUsage.Barrier)) + if first: + expectedUsage.append((eid,rd.ResourceUsage.CopySrc)) + first = False + else: + expectedUsage.append((eid,rd.ResourceUsage.CopyDst)) + for eid in fillBufferEIDs: + if eid > markerLooseEventsAfterIndirectDraws: + expectedUsage.append((eid-1,rd.ResourceUsage.Barrier)) + expectedUsage.append((eid,rd.ResourceUsage.Clear)) + break if res.name == "Descriptor Backup Buffer": - if descBuffer: - expectedUsage = [(236+countDrawIndirectCount,rd.ResourceUsage.Barrier), - (237+countDrawIndirectCount,rd.ResourceUsage.CopyDst), - (242+countDrawIndirectCount,rd.ResourceUsage.Barrier), - (243+countDrawIndirectCount,rd.ResourceUsage.CopySrc)] + first = True + for eid in copyBufferEIDs: + if eid > markerLooseEventsAfterIndirectDraws: + expectedUsage.append((eid-1,rd.ResourceUsage.Barrier)) + if first: + expectedUsage.append((eid,rd.ResourceUsage.CopyDst)) + first = False + else: + expectedUsage.append((eid,rd.ResourceUsage.CopySrc)) + break elif res.type == rd.ResourceType.Texture: if res.name == "Offscreen MSAA Image": - expectedUsage = [(12,rd.ResourceUsage.Barrier), - (12,rd.ResourceUsage.Discard), - (13,rd.ResourceUsage.Clear)] + expectedUsage = [(self.markerStart + 6,rd.ResourceUsage.Barrier), + (self.markerStart + 6,rd.ResourceUsage.Discard), + (self.markerStart + 7,rd.ResourceUsage.Clear)] if res.name == "Offscreen Image": - expectedUsage = [(10,rd.ResourceUsage.Barrier), - (10,rd.ResourceUsage.Discard), - (11,rd.ResourceUsage.Clear)] - for eid in descSetDrawEIDs: - expectedUsage.append((eid,rd.ResourceUsage.PS_Resource)) - for eid in descBufferDrawEIDs: - expectedUsage.append((eid,rd.ResourceUsage.PS_Resource)) + expectedUsage = [(self.markerStart + 4,rd.ResourceUsage.Barrier), + (self.markerStart + 4,rd.ResourceUsage.Discard), + (self.markerStart + 5,rd.ResourceUsage.Clear)] + expectedUsage += [(eid,rd.ResourceUsage.PS_Resource) for eid in descSetDrawEIDs] + expectedUsage += [(eid,rd.ResourceUsage.PS_Resource) for eid in descBufferDrawEIDs] elif res.type == rd.ResourceType.CommandBuffer: expectedUsage = [(0,rd.ResourceUsage.Unused)] elif res.type == rd.ResourceType.DescriptorStore: @@ -322,7 +375,6 @@ def check_capture(self): expectedUsage = [(0,rd.ResourceUsage.Unused)] else: raise rdtest.TestFailureException(f"'{res.name}' {res.resourceId} Unexpected resource type {res.type.name}") - rdtest.log.print(f"Resource '{res.name}' type:{res.type.name} {res.resourceId} usages:{len(self.resourceUsages[res.resourceId])} expectedUsages:{len(expectedUsage)}") expectedUsage.sort(key=lambda x: x[0]) self.check_resource_usage(res, expectedUsage) From 969bb3fc35d7dc45f7128537f226549d43e20ad3 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Thu, 8 Oct 2026 10:05:39 +0100 Subject: [PATCH 05/16] HACK HACK FORCE SINGLE THREADED SHADER DEBUGGING --- renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp b/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp index da78e168739..2f4b857162d 100644 --- a/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp @@ -2140,7 +2140,7 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s ret->inputs = active.inputs; mtSimulation = apiWrapper->SimulateThreaded(); - if(threadsInWorkgroup < 4) + if(threadsInWorkgroup < 1024) mtSimulation = false; AtomicStore(&atomic_simulationFinished, 0); From f0029b1c14d973624756997e694d3d4faf1bb72a Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Sat, 3 Oct 2026 16:40:42 +0100 Subject: [PATCH 06/16] Extend VK_Shader_Debug_Zoo tests for arrayed resources - bounded Uniform array - unbounded array of Uniform, Buffer and Tex2D --- util/test/demos/vk/vk_shader_debug_zoo.cpp | 166 ++++++++++++++++++++- 1 file changed, 163 insertions(+), 3 deletions(-) diff --git a/util/test/demos/vk/vk_shader_debug_zoo.cpp b/util/test/demos/vk/vk_shader_debug_zoo.cpp index bbe90353b60..09bd5cb356e 100644 --- a/util/test/demos/vk/vk_shader_debug_zoo.cpp +++ b/util/test/demos/vk/vk_shader_debug_zoo.cpp @@ -214,6 +214,12 @@ layout(set = 1, binding = 9) uniform sampler shadowSamplers[14]; layout(set = 1, binding = 20) uniform sampler2DArray queryTests[14]; layout(set = 1, binding = 21) uniform sampler2DMSArray queryTestsMS[14]; +layout(set = 1, binding = 22) uniform ubostype { + vec4 zeroes[16]; + vec4 first; + ivec4 index; + vec4 padding[14]; +} ubos[14]; layout(set = 2, binding = 0) uniform sampler1D zoo_1D; layout(set = 2, binding = 1) uniform sampler2D zoo_2D; @@ -281,6 +287,19 @@ layout(set = 2, rgba32i, binding = 56) uniform iimageCubeArray storezoo_iCubeArr //layout(set = 2, rgba32i, binding = 58) uniform iimage2DMSArray storezoo_i2DMSArray; layout(set = 2, rgba32i, binding = 59) uniform iimageBuffer storezoo_iBuffer; +layout(set = 3, binding = 22) uniform unboundedUniformType { + vec4 zeroes[16]; + vec4 first; + ivec4 index; + vec4 padding[14]; +} unbounded_ubos[]; + +layout(set = 3, binding = 23) uniform sampler2D unbounded_tex2ds[]; + +layout(set = 3, binding = 24, std430) buffer unboundedBufferType { + vec4 colour; +} unbounded_buffers[]; + #endif layout(push_constant) uniform PushData { @@ -1697,6 +1716,47 @@ void main() Color = imageLoad(storeTexBuffers[zeroi+10], 7); break; } + case 190: + { + Color.x = float(ubos[zerou+1].index.x); + Color.y = float(ubos[zerou+2].index.y); + Color.z = float(ubos[zerou+3].index.z); + Color.w = float(ubos[zerou+4].index.w); + break; + } + case 191: + { + uint index = ubos[zerou+9].index.y; + Color = texture(linearSampledImages[index], inpos.xy); + break; + } + case 192: + { + uint index = ubos[zerou+7].index.z; + storebufs[index].x = vec4(3.1f, 4.1f, 5.9f, 2.6f); + Color = storebufs[index].x; + break; + } + case 193: + { + Color.x = float(unbounded_ubos[zerou+1].index.x); + Color.y = float(unbounded_ubos[zerou+2].index.y); + Color.z = float(unbounded_ubos[zerou+3].index.z); + Color.w = float(unbounded_ubos[zerou+4].index.w); + break; + } + case 194: + { + uint index = unbounded_ubos[zerou+10].index.y; + Color = texture(unbounded_tex2ds[index], inpos.xy); + break; + } + case 195: + { + uint index = unbounded_ubos[zerou+8].index.z; + Color = unbounded_buffers[index].colour; + break; + } #endif default: break; } @@ -4792,6 +4852,7 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource // non-uniformly access each of these VkDescriptorSetLayout setlayout1 = VK_NULL_HANDLE; VkDescriptorSetLayout setlayout2 = VK_NULL_HANDLE; + VkDescriptorSetLayout setlayout3 = VK_NULL_HANDLE; if(descIndexing) { @@ -4807,6 +4868,7 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource {9, VK_DESCRIPTOR_TYPE_SAMPLER, 14, VK_SHADER_STAGE_FRAGMENT_BIT}, {20, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 14, VK_SHADER_STAGE_FRAGMENT_BIT}, {21, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 14, VK_SHADER_STAGE_FRAGMENT_BIT}, + {22, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 14, VK_SHADER_STAGE_FRAGMENT_BIT}, })); setlayout2 = createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo({ @@ -4877,8 +4939,15 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource {59, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT}, })); + setlayout3 = createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo({ + {22, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 14, VK_SHADER_STAGE_FRAGMENT_BIT}, + {23, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 14, VK_SHADER_STAGE_FRAGMENT_BIT}, + {24, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 14, VK_SHADER_STAGE_FRAGMENT_BIT}, + })); + setLayouts.push_back(setlayout1); setLayouts.push_back(setlayout2); + setLayouts.push_back(setlayout3); } VkPipelineLayout layout = createPipelineLayout(vkh::PipelineLayoutCreateInfo( @@ -5329,11 +5398,13 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource VkDescriptorSet descset0 = allocateDescriptorSet(setlayout0); VkDescriptorSet descset1 = VK_NULL_HANDLE; VkDescriptorSet descset2 = VK_NULL_HANDLE; + VkDescriptorSet descset3 = VK_NULL_HANDLE; if(descIndexing) { descset1 = allocateDescriptorSet(setlayout1); descset2 = allocateDescriptorSet(setlayout2); + descset3 = allocateDescriptorSet(setlayout3); } vkh::updateDescriptorSets( @@ -5353,7 +5424,7 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource {vkh::DescriptorImageInfo(shadowview, VK_IMAGE_LAYOUT_GENERAL, VK_NULL_HANDLE)}), }); - Vec4f cbufferdata[64] = {}; + Vec4f cbufferdata[16 * 16] = {}; AllocatedBuffer cb( this, @@ -5379,7 +5450,13 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource cbufferdata[11] = Vec4f(98.125f, 76.375f, 54.5625f, 32.78125f); uint32_t index = 4; - memcpy(&cbufferdata[1], &index, sizeof(index)); + memcpy(&cbufferdata[1].x, &index, sizeof(index)); + index = 2; + memcpy(&cbufferdata[1].y, &index, sizeof(index)); + index = 3; + memcpy(&cbufferdata[1].z, &index, sizeof(index)); + index = 5; + memcpy(&cbufferdata[1].w, &index, sizeof(index)); Vec4u unpack = {}; @@ -5401,8 +5478,29 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource memmove(&cbufferdata[16], &cbufferdata[0], sizeof(Vec4f) * 16); memset(&cbufferdata[0], 0, sizeof(Vec4f) * 16); + for(size_t i = 2; i < 14; ++i) + { + size_t cbufIdx = i * 16; + memcpy(&cbufferdata[cbufIdx], &cbufferdata[16], sizeof(Vec4f) * 16); + + index = (i + 0) % 14; + memcpy(&cbufferdata[cbufIdx + 1].x, &index, sizeof(index)); + index = (i + 2) % 14; + memcpy(&cbufferdata[cbufIdx + 1].y, &index, sizeof(index)); + index = (i + 3) % 14; + memcpy(&cbufferdata[cbufIdx + 1].z, &index, sizeof(index)); + index = (i + 5) % 14; + memcpy(&cbufferdata[cbufIdx + 1].w, &index, sizeof(index)); + } + cb.upload(cbufferdata); + AllocatedBuffer bad_cb( + this, + vkh::BufferCreateInfo(1 * sizeof(Vec4f), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT), + VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU})); + AllocatedBuffer bad_texbuffer( this, vkh::BufferCreateInfo(1 * sizeof(Vec4f), VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | @@ -5599,6 +5697,7 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource setName(bad_store_buffer.buffer, "bad_store_buffer"); setName(bad_image.image, "bad_image"); setName(bad_sampler, "bad_sampler"); + setName(bad_cb.buffer, "bad_cb"); setName(bad_texbuffer.buffer, "bad_texbuffer"); setName(bad_bufview, "bad_bufview"); setName(bad_store_bufview, "bad_store_bufview"); @@ -5733,6 +5832,8 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource descset1, 21, i, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, {vkh::DescriptorImageInfo(bad_queryTestMSView, VK_IMAGE_LAYOUT_GENERAL, bad_sampler)}), + vkh::WriteDescriptorSet(descset1, 22, i, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(bad_cb.buffer)}), }); } // Update specific entries to valid entries @@ -5791,9 +5892,11 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource descset1, 4, 12, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, {vkh::DescriptorImageInfo(smileyview, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, linearsampler)}), - // storebufs: 7 + // storebufs: 7, 11 vkh::WriteDescriptorSet(descset1, 5, 7, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, {vkh::DescriptorBufferInfo(store_buffer.buffer)}), + vkh::WriteDescriptorSet(descset1, 5, 11, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + {vkh::DescriptorBufferInfo(store_buffer.buffer)}), // storeImages: 7 vkh::WriteDescriptorSet( descset1, 6, 7, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, @@ -5828,7 +5931,63 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource vkh::WriteDescriptorSet( descset1, 21, 5, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, {vkh::DescriptorImageInfo(queryTestMSView, VK_IMAGE_LAYOUT_GENERAL, mipsampler)}), + // ubos: 1, 2, 3, 4, 7, 9 + vkh::WriteDescriptorSet(descset1, 22, 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(cb.buffer, 1 * sizeof(Vec4f) * 16)}), + vkh::WriteDescriptorSet(descset1, 22, 2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(cb.buffer, 2 * sizeof(Vec4f) * 16)}), + vkh::WriteDescriptorSet(descset1, 22, 3, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(cb.buffer, 3 * sizeof(Vec4f) * 16)}), + vkh::WriteDescriptorSet(descset1, 22, 4, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(cb.buffer, 4 * sizeof(Vec4f) * 16)}), + vkh::WriteDescriptorSet(descset1, 22, 7, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(cb.buffer, 7 * sizeof(Vec4f) * 16)}), + vkh::WriteDescriptorSet(descset1, 22, 9, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(cb.buffer, 9 * sizeof(Vec4f) * 16)}), + }); + // Set all entries to bad entries + for(uint32_t i = 0; i < 14; i++) + { + vkh::updateDescriptorSets( + device, + { + vkh::WriteDescriptorSet(descset3, 22, i, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(bad_cb.buffer)}), + vkh::WriteDescriptorSet( + descset3, 23, i, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + {vkh::DescriptorImageInfo(bad_imgview, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + bad_sampler)}), + vkh::WriteDescriptorSet(descset3, 24, i, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + {vkh::DescriptorBufferInfo(bad_store_buffer.buffer)}), + }); + } + // Update specific entries to valid entries + vkh::updateDescriptorSets( + device, + { + // unbounded_ubos: 1 2 3 4 8 10 + vkh::WriteDescriptorSet(descset3, 22, 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(cb.buffer, 1 * sizeof(Vec4f) * 16)}), + vkh::WriteDescriptorSet(descset3, 22, 2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(cb.buffer, 2 * sizeof(Vec4f) * 16)}), + vkh::WriteDescriptorSet(descset3, 22, 3, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(cb.buffer, 3 * sizeof(Vec4f) * 16)}), + vkh::WriteDescriptorSet(descset3, 22, 4, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(cb.buffer, 4 * sizeof(Vec4f) * 16)}), + vkh::WriteDescriptorSet(descset3, 22, 8, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(cb.buffer, 8 * sizeof(Vec4f) * 16)}), + vkh::WriteDescriptorSet(descset3, 22, 10, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + {vkh::DescriptorBufferInfo(cb.buffer, 10 * sizeof(Vec4f) * 16)}), + // unbounded_tex2ds: 13 + vkh::WriteDescriptorSet( + descset3, 23, 13, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + {vkh::DescriptorImageInfo(smileyview, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + linearsampler)}), + // unbounded_buffers: 12 + vkh::WriteDescriptorSet( + descset3, 24, 12, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + {vkh::DescriptorBufferInfo(store_buffer.buffer, 13 * sizeof(Vec4f) * 16)}), }); } @@ -5968,6 +6127,7 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource { descSets.push_back(descset1); descSets.push_back(descset2); + descSets.push_back(descset3); } vkh::cmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descSets, From 42f9a5a42a4118e5ce0d43473760fda87800fcf4 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Thu, 8 Oct 2026 10:02:57 +0100 Subject: [PATCH 07/16] Vulkan Spirv debugger support for arrays of cbuffers - bounded and unbounded --- .../shaders/spirv/spirv_debug_setup.cpp | 79 +++++++------------ 1 file changed, 27 insertions(+), 52 deletions(-) diff --git a/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp b/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp index 2f4b857162d..c6500a81f71 100644 --- a/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp @@ -974,38 +974,6 @@ void Reflector::CheckDebuggable(bool &debuggable, rdcstr &debugStatus) const debugStatus += StringFormat::Fmt("Unsupported extended instruction set: '%s'\n", setname.c_str()); } - // we don't currently support debugging unbounded arrays of cbuffers - for(const Variable &v : globals) - { - if(v.storage == StorageClass::Uniform) - { - const DataType &type = dataTypes[v.type]; - - // global variables should all be pointers into opaque storage - RDCASSERT(type.type == DataType::PointerType); - - const DataType *innertype = &dataTypes[type.InnerType()]; - - if(innertype->type == DataType::ArrayType) - { - if(innertype->length == Id()) - { - // unbounded SSBO is supported - const DataType *elementType = &dataTypes[innertype->InnerType()]; - if(decorations[elementType->id].flags & Decorations::BufferBlock) - continue; - - debuggable = false; - rdcstr name = strings[v.id]; - if(name.empty()) - name = GetRawName(v.id); - debugStatus += - StringFormat::Fmt("Unsupported unbounded uniform buffer array: '%s'\n", name.c_str()); - } - } - } - } - debugStatus.trim(); } @@ -1547,29 +1515,35 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s } }; - if(isArray) + bool unbounded = isArray && (arraySize == ~0U); + if(!unbounded) { - if(arraySize == ~0U) + if(isArray) { - RDCERR("Unsupported runtime array of UBOs"); - arraySize = 1; - } + var.members.reserve(arraySize); - var.members.reserve(arraySize); - - for(uint32_t a = 0; a < arraySize; a++) + for(uint32_t a = 0; a < arraySize; a++) + { + binding.arrayElement = a; + var.members.push_back(ShaderVariable()); + var.members.back().name = StringFormat::Fmt("[%u]", a); + WalkVariable(decorations[v.id], *innertype, 0U, false, + var.members.back(), rdcstr(), cbufferCallback); + } + } + else { - binding.arrayElement = a; - var.members.push_back(ShaderVariable()); - var.members.back().name = StringFormat::Fmt("[%u]", a); - WalkVariable(decorations[v.id], *innertype, 0U, false, - var.members.back(), rdcstr(), cbufferCallback); + WalkVariable(decorations[v.id], *innertype, 0U, false, var, + rdcstr(), cbufferCallback); } } else { - WalkVariable(decorations[v.id], *innertype, 0U, false, var, - rdcstr(), cbufferCallback); + enablePointerFlags(var, PointerFlags::GlobalArrayBinding); + var.SetBindIndex(binding); + var.rows = 1; + var.columns = 1; + var.type = VarType::ConstantBlock; } sourceVar.type = VarType::ConstantBlock; @@ -3338,7 +3312,7 @@ ShaderVariable Debugger::MakeCompositePointer(const ShaderVariable &base, Id id, // We only take this if this is the FIRST dereference from the global pointer. // If the SPIR-V does something like structType *_1234 = if((leaf->type == VarType::ReadWriteResource || leaf->type == VarType::ReadOnlyResource || - leaf->type == VarType::Sampler) && + leaf->type == VarType::Sampler || leaf->type == VarType::ConstantBlock) && checkPointerFlags(*leaf, PointerFlags::GlobalArrayBinding) && getBufferTypeId(base) == rdcspv::Id()) { @@ -3346,7 +3320,7 @@ ShaderVariable Debugger::MakeCompositePointer(const ShaderVariable &base, Id id, } if((leaf->type == VarType::ReadWriteResource && checkPointerFlags(*leaf, PointerFlags::SSBO)) || - physicalPointer) + physicalPointer || leaf->type == VarType::ConstantBlock) { ShaderVariable ret; uint64_t byteOffset = 0; @@ -3670,7 +3644,8 @@ DeviceOpResult Debugger::ReadFromPointer(const ShaderVariable &ptr, ShaderVariab ret = ShaderVariable(ptr.name, 0, 0, 0, 0); return DeviceOpResult::Succeeded; } - if(inner->type == VarType::ReadWriteResource && checkPointerFlags(*inner, PointerFlags::SSBO)) + if((inner->type == VarType::ReadWriteResource && checkPointerFlags(*inner, PointerFlags::SSBO)) || + (inner->type == VarType::ConstantBlock)) { typeId = getBufferTypeId(ptr); byteOffset = getByteOffset(ptr); @@ -3799,7 +3774,7 @@ DeviceOpResult Debugger::ReadFromPointer(const ShaderVariable &ptr, ShaderVariab ret.name = ptr.name; if(inner->type == VarType::ReadOnlyResource || inner->type == VarType::ReadWriteResource || - inner->type == VarType::Sampler) + inner->type == VarType::Sampler || inner->type == VarType::ConstantBlock) { bind = ret.GetBindIndex(); bind.arrayElement = getBindArrayIndex(ptr); @@ -3880,7 +3855,7 @@ bool Debugger::IsOpaquePointer(const ShaderVariable &ptr) const const ShaderVariable *inner = getPointer(ptr); return inner->type == VarType::ReadOnlyResource || inner->type == VarType::Sampler || - inner->type == VarType::ReadWriteResource; + inner->type == VarType::ReadWriteResource || inner->type == VarType::ConstantBlock; } bool Debugger::IsPhysicalPointer(const ShaderVariable &ptr) const From 3685a3a501d2e7886e48e37e8089c9b481b0c0c9 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Thu, 8 Oct 2026 10:03:16 +0100 Subject: [PATCH 08/16] ShaderViewer support for arrays of cbuffers --- qrenderdoc/Windows/ShaderViewer.cpp | 288 ++++++++++++++++++++++------ qrenderdoc/Windows/ShaderViewer.h | 1 + 2 files changed, 232 insertions(+), 57 deletions(-) diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index c70239340d5..238a2c70fbe 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -528,6 +528,7 @@ void ShaderViewer::cacheResources() m_ReadOnlyResources = m_Ctx.CurPipelineState().GetReadOnlyResources(m_Stage, false); m_ReadWriteResources = m_Ctx.CurPipelineState().GetReadWriteResources(m_Stage, false); m_Samplers = m_Ctx.CurPipelineState().GetSamplers(m_Stage, false); + m_Constants = m_Ctx.CurPipelineState().GetConstantBlocks(m_Stage, false); } void ShaderViewer::debugShader(const ShaderReflection *shader, ResourceId pipeline, @@ -2915,6 +2916,27 @@ QString ShaderViewer::stringRep(const ShaderVariable &var, uint32_t row) if(type == VarType::Unknown) type = ui->intView->isChecked() ? VarType::SInt : VarType::Float; + if(type == VarType::ConstantBlock) + { + rdcarray &resList = m_Constants; + int32_t bindIdx = -1; + if(var.IsDirectAccess()) + { + ShaderDirectAccess access = var.GetDirectAccess(); + bindIdx = resList.indexOf(access); + } + else + { + ShaderBindIndex varBind = var.GetBindIndex(); + bindIdx = resList.indexOf(varBind); + } + if(bindIdx >= 0) + { + const UsedDescriptor &res = resList[bindIdx]; + return ToQStr(res.descriptor.resource); + } + } + if(type == VarType::ReadOnlyResource || type == VarType::ReadWriteResource || type == VarType::Sampler) { @@ -3243,8 +3265,8 @@ QString ShaderViewer::getRegNames(const RDTreeWidgetItem *item, uint32_t swizzle return ret; } - if(mapping.type == VarType::Sampler || mapping.type == VarType::ReadOnlyResource || - mapping.type == VarType::ReadWriteResource) + if(mapping.type == VarType::ConstantBlock || mapping.type == VarType::Sampler || + mapping.type == VarType::ReadOnlyResource || mapping.type == VarType::ReadWriteResource) { const ShaderVariable *reg = GetDebugVariable(mapping.variables[0]); @@ -3254,38 +3276,28 @@ QString ShaderViewer::getRegNames(const RDTreeWidgetItem *item, uint32_t swizzle ret = reg->name; - if(mapping.type == VarType::Sampler) + ShaderBindIndex bind = reg->GetBindIndex(); + + if(!reg->IsDirectAccess()) { - if(!reg->IsDirectAccess()) + if(mapping.type == VarType::ConstantBlock) { - ShaderBindIndex bind = reg->GetBindIndex(); + if(bind.category != DescriptorCategory::ConstantBlock) + return QString(); + if(bind.index >= m_ShaderDetails->constantBlocks.size()) + return QString(); + } + else if(mapping.type == VarType::Sampler) + { if(bind.category != DescriptorCategory::Sampler) return QString(); if(bind.index >= m_ShaderDetails->samplers.size()) return QString(); - - const ShaderSampler &samp = m_ShaderDetails->samplers[bind.index]; - - uint32_t arrayIdx = child < (uint32_t)item->childCount() - ? item->child(child)->tag().value() - : child; - if(arrayIdx == ~0U) - arrayIdx = child; - - if(arrayIdx != ~0U) - return QFormatStr("%1[%2]").arg(ret).arg(arrayIdx); } - - return ret; - } - else if(mapping.type == VarType::ReadOnlyResource || mapping.type == VarType::ReadWriteResource) - { - if(!reg->IsDirectAccess()) + else if(mapping.type == VarType::ReadOnlyResource || mapping.type == VarType::ReadWriteResource) { - ShaderBindIndex bind = reg->GetBindIndex(); - if((mapping.type == VarType::ReadOnlyResource && bind.category != DescriptorCategory::ReadOnlyResource) || (mapping.type == VarType::ReadWriteResource && @@ -3297,24 +3309,16 @@ QString ShaderViewer::getRegNames(const RDTreeWidgetItem *item, uint32_t swizzle (mapping.type == VarType::ReadWriteResource && bind.index >= m_ShaderDetails->readWriteResources.size())) return QString(); - - const ShaderResource &res = mapping.type == VarType::ReadOnlyResource - ? m_ShaderDetails->readOnlyResources[bind.index] - : m_ShaderDetails->readWriteResources[bind.index]; - - uint32_t arrayIdx = child < (uint32_t)item->childCount() - ? item->child(child)->tag().value() - : child; - if(arrayIdx == ~0U) - arrayIdx = child; - - if(arrayIdx != ~0U) - return QFormatStr("%1[%2]").arg(ret).arg(arrayIdx); } - return ret; - } + uint32_t arrayIdx = + child < (uint32_t)item->childCount() ? item->child(child)->tag().value() : child; + if(arrayIdx == ~0U) + arrayIdx = child; + if(arrayIdx != ~0U) + return QFormatStr("%1[%2]").arg(ret).arg(arrayIdx); + } return ret; } @@ -4131,23 +4135,55 @@ void ShaderViewer::updateDebugState() // sure we add any remainders here. Constants might be un-touched by reflection info for(int i = 0; i < m_Trace->constantBlocks.count(); i++) { - rdcstr name = m_Trace->constantBlocks[i].name; + const ShaderVariable &cb = m_Trace->constantBlocks[i]; + const rdcstr &name = cb.name; if(varsMapped.contains(name)) continue; + ShaderBindIndex bind = cb.GetBindIndex(); + const ConstantBlock &res = m_ShaderDetails->constantBlocks[bind.index]; + + if(res.bindArraySize == ~0U) + { + RDTreeWidgetItem *node = + new RDTreeWidgetItem({res.name, name, lit("[unbounded]"), QString()}); + node->setTag(QVariant::fromValue(VariableTag(DebugVariableType::Constant, name))); + ui->constants->addTopLevelItem(node); + continue; + } + + // Special case for arrays of constant blocks which might be of the form [0], [1] + size_t toFind = cb.members.size(); + if(toFind > 0) + { + for(uint32_t j = 0; j < cb.members.size(); ++j) + { + if(!cb.members[i].name.isEmpty()) + { + if(cb.members[j].name[0] == '[') + { + rdcstr childname = name + cb.members[j].name; + if(varsMapped.contains(childname)) + --toFind; + } + } + } + if(toFind == 0) + continue; + } + RDTreeWidgetItem *node = new RDTreeWidgetItem({name, name, lit("Constant"), QString()}); node->setTag(QVariant::fromValue(VariableTag(DebugVariableType::Constant, name))); - for(int j = 0; j < m_Trace->constantBlocks[i].members.count(); j++) + for(int j = 0; j < cb.members.count(); j++) { - if(m_Trace->constantBlocks[i].members[j].rows > 0 || - m_Trace->constantBlocks[i].members[j].columns > 0) + if(cb.members[j].rows > 0 || cb.members[j].columns > 0) { - rdcstr childname = name + "." + m_Trace->constantBlocks[i].members[j].name; + rdcstr childname = name + "." + cb.members[j].name; if(!varsMapped.contains(name)) { - RDTreeWidgetItem *child = new RDTreeWidgetItem( - {name, name, lit("Constant"), stringRep(m_Trace->constantBlocks[i].members[j])}); + RDTreeWidgetItem *child = + new RDTreeWidgetItem({name, name, lit("Constant"), stringRep(cb.members[j])}); child->setTag(QVariant::fromValue(VariableTag(DebugVariableType::Constant, childname))); node->addChild(child); } @@ -4155,16 +4191,14 @@ void ShaderViewer::updateDebugState() else { // Check if this is a constant buffer array - int arrayCount = m_Trace->constantBlocks[i].members[j].members.count(); + int arrayCount = cb.members[j].members.count(); for(int k = 0; k < arrayCount; k++) { - if(m_Trace->constantBlocks[i].members[j].members[k].rows > 0 || - m_Trace->constantBlocks[i].members[j].members[k].columns > 0) + if(cb.members[j].members[k].rows > 0 || cb.members[j].members[k].columns > 0) { - rdcstr childname = name + "." + m_Trace->constantBlocks[i].members[j].members[k].name; - RDTreeWidgetItem *child = - new RDTreeWidgetItem({name, name, lit("Constant"), - stringRep(m_Trace->constantBlocks[i].members[j].members[k])}); + rdcstr childname = name + "." + cb.members[j].members[k].name; + RDTreeWidgetItem *child = new RDTreeWidgetItem( + {name, name, lit("Constant"), stringRep(cb.members[j].members[k])}); node->setTag(QVariant::fromValue(VariableTag(DebugVariableType::Constant, childname))); node->addChild(child); @@ -4199,6 +4233,74 @@ void ShaderViewer::updateDebugState() } } + const rdcarray &cbBinds = m_Constants; + + for(int i = 0; i < m_Trace->constantBlocks.count(); i++) + { + const ShaderVariable &cb = m_Trace->constantBlocks[i]; + + if(varsMapped.contains(cb.name)) + continue; + + if(cb.IsDirectAccess()) + continue; + + // find all descriptors in this bind's array + ShaderBindIndex bind = cb.GetBindIndex(); + bind.arrayElement = 0; + + rdcarray descriptors; + for(const UsedDescriptor &a : cbBinds) + if(CategoryForDescriptorType(a.access.type) == bind.category && a.access.index == bind.index) + descriptors.push_back(a); + + if(descriptors.empty()) + continue; + + const ConstantBlock &res = m_ShaderDetails->constantBlocks[bind.index]; + + if(res.bindArraySize == 1) + { + RDTreeWidgetItem *node = new RDTreeWidgetItem( + {res.name, cb.name, lit("Constant"), ToQStr(descriptors[0].descriptor.resource)}); + node->setTag(QVariant::fromValue(VariableTag(DebugVariableType::ReadOnlyResource, cb.name))); + ui->constants->addTopLevelItem(node); + } + else + { + QString sizeName; + uint32_t count; + if(res.bindArraySize == ~0U) + { + sizeName = lit("[unbounded]"); + count = (uint32_t)descriptors.size(); + } + else + { + sizeName = QFormatStr("[%1]").arg(res.bindArraySize); + count = qMin(res.bindArraySize, (uint32_t)descriptors.size()); + } + RDTreeWidgetItem *node = new RDTreeWidgetItem({res.name, cb.name, sizeName, QString()}); + node->setTag(QVariant::fromValue(VariableTag(DebugVariableType::Constant, cb.name))); + + for(uint32_t a = 0; a < count; a++) + { + uint32_t arrayIndex = descriptors[a].access.arrayElement; + QString childName = QFormatStr("%1[%2]").arg(cb.name).arg(arrayIndex); + RDTreeWidgetItem *child = new RDTreeWidgetItem({ + QFormatStr("%1[%2]").arg(res.name).arg(a), + childName, + lit("Constant"), + ToQStr(descriptors[a].descriptor.resource), + }); + child->setTag(QVariant::fromValue(VariableTag(DebugVariableType::Constant, childName))); + node->addChild(child); + } + + ui->constants->addTopLevelItem(node); + } + } + rdcarray &roBinds = m_ReadOnlyResources; for(int i = 0; i < m_Trace->readOnlyResources.count(); i++) @@ -5007,6 +5109,7 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi for(size_t i = 0; i < l.variables.size(); i++) { + bool processed = false; const DebugVariableReference &r = l.variables[i]; baseTag.modified |= HasChanged(r.name); @@ -5026,13 +5129,13 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi if(reg == NULL) continue; + if(reg->IsDirectAccess()) + continue; + typeName = lit("Sampler"); const rdcarray &samplers = m_Samplers; - if(reg->IsDirectAccess()) - continue; - ShaderBindIndex bind = reg->GetBindIndex(); int32_t bindIdx = samplers.indexOf(bind); @@ -5089,6 +5192,7 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi value = QString(); } } + processed = true; } else if(r.type == DebugVariableType::ReadOnlyResource || r.type == DebugVariableType::ReadWriteResource) @@ -5165,8 +5269,78 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi value = QString(); } } + processed = true; } - else + else if(r.type == DebugVariableType::Constant) + { + const ShaderVariable *reg = GetDebugVariable(r); + if(reg == NULL) + continue; + + if(reg->IsDirectAccess()) + continue; + + typeName = lit("Constant"); + + const rdcarray &cbBinds = m_Constants; + + // find all descriptors in this bind's array + ShaderBindIndex bind = reg->GetBindIndex(); + bind.arrayElement = 0; + + rdcarray descriptors; + for(const UsedDescriptor &a : cbBinds) + if(CategoryForDescriptorType(a.access.type) == bind.category && a.access.index == bind.index) + descriptors.push_back(a); + + if(descriptors.empty()) + { + processed = false; + } + else + { + processed = true; + const ConstantBlock &cb = m_ShaderDetails->constantBlocks[bind.index]; + if(cb.bindArraySize == 1) + { + value = ToQStr(descriptors[0].descriptor.resource); + } + else + { + QString childTypeName = typeName; + uint32_t count; + if(cb.bindArraySize == ~0U) + { + typeName = lit("[unbounded]"); + count = (uint32_t)descriptors.size(); + } + else + { + typeName = QFormatStr("[%1]").arg(cb.bindArraySize); + count = qMin(cb.bindArraySize, (uint32_t)descriptors.size()); + } + + for(uint32_t a = 0; a < count; a++) + { + uint32_t arrayIndex = descriptors[a].access.arrayElement; + QString childName = QFormatStr("%1[%2]").arg(localName).arg(arrayIndex); + RDTreeWidgetItem *child = new RDTreeWidgetItem({ + childName, + QString(), + childTypeName, + ToQStr(descriptors[a].descriptor.resource), + }); + child->setTag(QVariant::fromValue(arrayIndex)); + children.push_back(child); + } + + childCount += count; + + value = QString(); + } + } + } + if(!processed) { const ShaderVariable *reg = GetDebugVariable(r); diff --git a/qrenderdoc/Windows/ShaderViewer.h b/qrenderdoc/Windows/ShaderViewer.h index a8d8816772e..5f1bfafed59 100644 --- a/qrenderdoc/Windows/ShaderViewer.h +++ b/qrenderdoc/Windows/ShaderViewer.h @@ -327,6 +327,7 @@ private slots: rdcarray m_ReadOnlyResources; rdcarray m_ReadWriteResources; rdcarray m_Samplers; + rdcarray m_Constants; QSet> m_Breakpoints; bool m_TempBreakpoint = false; From 9c542054074a07df815934adb880691f6641e3be Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Wed, 7 Oct 2026 11:45:20 +0100 Subject: [PATCH 09/16] Extend GL_Shader_Debug_Zoo tests for arrayed resources - bounded UBO array - bounded SSBO array --- util/test/demos/gl/gl_shader_debug_zoo.cpp | 42 +++++++++++++++++----- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/util/test/demos/gl/gl_shader_debug_zoo.cpp b/util/test/demos/gl/gl_shader_debug_zoo.cpp index 8a0b1439eb5..7fe6b299aef 100644 --- a/util/test/demos/gl/gl_shader_debug_zoo.cpp +++ b/util/test/demos/gl/gl_shader_debug_zoo.cpp @@ -227,15 +227,27 @@ layout(binding = 0, std140) uniform ubo_test vec4 data; } ubo; +layout(binding = 1, std140) uniform uboArray_test +{ + vec4 data1; + vec4 data2; +} ubos[5]; + layout(binding = 0, std430) buffer ssbo_test { vec4 data; } ssbo; +layout(binding = 1, std430) buffer ssboArray_test +{ + vec4 data1; + vec4 data2; +} ssbos[7]; + layout(binding = 0) uniform sampler2D tex2d_test; layout(binding = 1) uniform samplerBuffer texBuf_test; layout(binding = 2) uniform sampler2D bias_test; -layout(binding = 3) uniform sampler2D resArray_test[2]; +layout(binding = 3) uniform sampler2D resArray_test[5]; layout(location = 1) in vec4 v2fColor; layout(location = 2) in vec2 v2fUV; @@ -258,8 +270,12 @@ void main() col += texture(tex2d_test, v2fUV); col += texelFetch(texBuf_test, int(v2fUV.x*10)); col += texture(bias_test, v2fUV, -0.8f); - col += texture(resArray_test[0], v2fUV); col += texture(resArray_test[1], v2fUV); + col += texture(resArray_test[3], v2fUV); + col += ubos[1].data1; + col += ubos[2].data2; + col += ssbos[3].data1; + col += ssbos[4].data2; outColor = col; } @@ -557,12 +573,20 @@ void main() glBufferStorage(GL_UNIFORM_BUFFER, sizeof(cbufferdata), cbufferdata, GL_MAP_WRITE_BIT); glBindBufferRange(GL_UNIFORM_BUFFER, 0, cb, bindOffset * sizeof(Vec4f), 16 * sizeof(Vec4f)); + // ubos[1], ubos[2] + glBindBufferRange(GL_UNIFORM_BUFFER, 2, cb, 2 * bindOffset * sizeof(Vec4f), 16 * sizeof(Vec4f)); + glBindBufferRange(GL_UNIFORM_BUFFER, 3, cb, 3 * bindOffset * sizeof(Vec4f), 16 * sizeof(Vec4f)); GLuint ssbo = MakeBuffer(); glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); glBufferStorage(GL_SHADER_STORAGE_BUFFER, sizeof(cbufferdata), cbufferdata, GL_MAP_WRITE_BIT); - glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, ssbo, bindOffset * sizeof(Vec4f) * 2, + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, ssbo, 4 * bindOffset * sizeof(Vec4f), + 16 * sizeof(Vec4f)); + // ssbos[3], ssbos[4] + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 4, ssbo, 5 * bindOffset * sizeof(Vec4f), + 16 * sizeof(Vec4f)); + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 5, ssbo, 6 * bindOffset * sizeof(Vec4f), 16 * sizeof(Vec4f)); GLuint tbuf_store = MakeBuffer(); @@ -622,9 +646,9 @@ void main() pixels.push_back(rgba8.data[srcIdx]); --srcIdx; } - GLuint resArray_tex0 = MakeTexture(); - glActiveTexture(GL_TEXTURE3); - glBindTexture(GL_TEXTURE_2D, resArray_tex0); + GLuint resArray_tex1 = MakeTexture(); + glActiveTexture(GL_TEXTURE4); + glBindTexture(GL_TEXTURE_2D, resArray_tex1); glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, rgba8.width, rgba8.height); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rgba8.width, rgba8.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); @@ -638,9 +662,9 @@ void main() pixels.push_back(rgba8.data[srcIdx % countPixels]); srcIdx -= 2; } - GLuint resArray_tex1 = MakeTexture(); - glActiveTexture(GL_TEXTURE4); - glBindTexture(GL_TEXTURE_2D, resArray_tex1); + GLuint resArray_tex3 = MakeTexture(); + glActiveTexture(GL_TEXTURE6); + glBindTexture(GL_TEXTURE_2D, resArray_tex3); glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, rgba8.width, rgba8.height); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rgba8.width, rgba8.height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); From 7ab4390904c971f92fd7d25431c164f9988652bd Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Wed, 7 Oct 2026 17:17:02 +0100 Subject: [PATCH 10/16] Fix GL Shader Reflection for UBO arrays - use the array basename to match for ubo variables --- renderdoc/driver/gl/gl_shader_refl.cpp | 30 +++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/renderdoc/driver/gl/gl_shader_refl.cpp b/renderdoc/driver/gl/gl_shader_refl.cpp index 5d5d1da0213..438ab8cece5 100644 --- a/renderdoc/driver/gl/gl_shader_refl.cpp +++ b/renderdoc/driver/gl/gl_shader_refl.cpp @@ -2015,24 +2015,44 @@ void MakeShaderReflection(GLenum shadType, GLuint sepProg, ShaderReflection &ref { for(int i = 0; i < numUBOs; i++) { - if(!ubos[i].empty()) + int idx = i; + int arrOffs = uboNames[i].indexOf('['); + // Try to match arrays + // TODO JAKE : EXPLAIN THIS WITH A PROPER COMMENT + if(ubos[i].empty() && arrOffs > 0) + { + rdcstr basename = uboNames[i]; + basename.erase(arrOffs, basename.size()); + + for(int j = 0; j < numUBOs; ++j) + { + if(!ubos[j].empty()) + { + if(ubos[j][0].name == basename) + { + idx = j; + break; + } + } + } + } + if(!ubos[idx].empty()) { ConstantBlock cblock; cblock.name = uboNames[i]; cblock.bufferBacked = true; + cblock.variables = ubos[idx]; GLenum bufSize = eGL_BUFFER_DATA_SIZE; GL.glGetProgramResourceiv(sepProg, eGL_UNIFORM_BLOCK, i, 1, &bufSize, 1, NULL, (GLint *)&cblock.byteSize); - sort(ubos[i]); + sort(cblock.variables); // can't make perfect guesses of struct alignment but assume std140 for ubos - for(ShaderConstant &member : ubos[i]) + for(ShaderConstant &member : cblock.variables) FixupStructOffsetsAndSize(true, member); - std::swap(cblock.variables, ubos[i]); - refl.constantBlocks.push_back(cblock); } } From 8f8f1a8c8698ac78ee6e772a97387f16fb7746ee Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Wed, 7 Oct 2026 11:46:23 +0100 Subject: [PATCH 11/16] Extend GL reflection matching to SPIRV reflection for UBO, SSBO arrays GL resource arrays are expanded in the GL reflection i.e. texture[0], texture[1] but they are collapsed in the SPIRV reflection. For SPIRV resource arrays if matching the full name fails then try to match by the expanded name i.e. texture[] --- .../driver/gl/wrappers/gl_shader_funcs.cpp | 114 ++++++++---------- 1 file changed, 52 insertions(+), 62 deletions(-) diff --git a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp index e67636800b5..dbbcfa57fe8 100644 --- a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp @@ -381,10 +381,16 @@ void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &drv, ResourceI for(size_t i = 0; i < reflection->constantBlocks.size(); i++) { rdcstr name = reflection->constantBlocks[i].name; + // Only match on base name remove any array indexes + int arrOffs = name.indexOf('['); + if(arrOffs > 0) + name.erase(arrOffs, name.size()); + bool found = false; for(size_t j = 0; j < spvReflection.constantBlocks.size(); j++) { - if(name == spvReflection.constantBlocks[j].name) + const rdcstr &matchName = spvReflection.constantBlocks[j].name; + if(name == matchName) { convertedPatchData.cblockInterface.resize_for_index(i); convertedPatchData.cblockInterface[i] = spvPatchData.cblockInterface[j]; @@ -404,11 +410,17 @@ void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &drv, ResourceI const rdcspv::DataType &outerType = spirv.GetDataType(spirv.GetIDType(spvPatchData.cblockInterface[j])); - rdcstr typeName = outerType.name; + rdcstr matchName = outerType.name; if(outerType.type == rdcspv::DataType::PointerType) - typeName = spirv.GetDataType(outerType.InnerType()).name; + { + const rdcspv::DataType &innerType = spirv.GetDataType(outerType.InnerType()); + if(innerType.type == rdcspv::DataType::ArrayType) + matchName = spirv.GetDataType(innerType.InnerType()).name; + else + matchName = innerType.name; + } - if(name == typeName) + if(name == matchName) { convertedPatchData.cblockInterface.resize_for_index(i); convertedPatchData.cblockInterface[i] = spvPatchData.cblockInterface[j]; @@ -426,7 +438,8 @@ void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &drv, ResourceI name += "_var"; for(size_t j = 0; j < spvReflection.constantBlocks.size(); j++) { - if(name == spvReflection.constantBlocks[j].name) + const rdcstr &matchName = spvReflection.constantBlocks[j].name; + if(name == matchName) { convertedPatchData.cblockInterface.resize_for_index(i); convertedPatchData.cblockInterface[i] = spvPatchData.cblockInterface[j]; @@ -456,32 +469,22 @@ void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &drv, ResourceI for(size_t i = 0; i < reflection->readOnlyResources.size(); i++) { + rdcstr name = reflection->readOnlyResources[i].name; + // Only match on base name remove any array indexes + int arrOffs = name.indexOf('['); + if(arrOffs > 0) + name.erase(arrOffs, name.size()); + bool found = false; for(size_t j = 0; j < spvReflection.readOnlyResources.size(); j++) { - if(reflection->readOnlyResources[i].name == spvReflection.readOnlyResources[j].name) - { - found = true; - } - else if((spvReflection.readOnlyResources[j].bindArraySize > 1)) - { - rdcstr &baseName = spvReflection.readOnlyResources[j].name; - for(uint32_t idx = 0; idx < spvReflection.readOnlyResources[j].bindArraySize; idx++) - { - rdcstr resName = StringFormat::Fmt("%s[%u]", baseName.c_str(), idx); - if(reflection->readOnlyResources[i].name == resName) - { - found = true; - break; - } - } - } - if(found) + if(name == spvReflection.readOnlyResources[j].name) { convertedPatchData.roInterface.resize_for_index(i); convertedPatchData.roInterface[i] = spvPatchData.roInterface[j]; convertedRefl.readOnlyResources.resize_for_index(i); convertedRefl.readOnlyResources[i] = spvReflection.readOnlyResources[j]; + found = true; break; } } @@ -500,32 +503,22 @@ void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &drv, ResourceI for(size_t i = 0; i < reflection->readWriteResources.size(); i++) { rdcstr name = reflection->readWriteResources[i].name; + // Only match on base name remove any array indexes + int arrOffs = name.indexOf('['); + if(arrOffs > 0) + name.erase(arrOffs, name.size()); + bool found = false; for(size_t j = 0; j < spvReflection.readWriteResources.size(); j++) { - if(reflection->readWriteResources[i].name == spvReflection.readWriteResources[j].name) - { - found = true; - } - else if((spvReflection.readWriteResources[j].bindArraySize > 1)) - { - rdcstr &baseName = spvReflection.readWriteResources[j].name; - for(uint32_t idx = 0; idx < spvReflection.readWriteResources[j].bindArraySize; idx++) - { - rdcstr resName = StringFormat::Fmt("%s[%u]", baseName.c_str(), idx); - if(reflection->readWriteResources[i].name == resName) - { - found = true; - break; - } - } - } - if(found) + const rdcstr &matchName = spvReflection.readWriteResources[j].name; + if(name == matchName) { convertedPatchData.rwInterface.resize_for_index(i); convertedPatchData.rwInterface[i] = spvPatchData.rwInterface[j]; convertedRefl.readWriteResources.resize_for_index(i); convertedRefl.readWriteResources[i] = spvReflection.readWriteResources[j]; + found = true; break; } } @@ -539,11 +532,17 @@ void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &drv, ResourceI const rdcspv::DataType &outerType = spirv.GetDataType(spirv.GetIDType(spvPatchData.rwInterface[j])); - rdcstr typeName = outerType.name; + rdcstr matchName = outerType.name; if(outerType.type == rdcspv::DataType::PointerType) - typeName = spirv.GetDataType(outerType.InnerType()).name; + { + const rdcspv::DataType &innerType = spirv.GetDataType(outerType.InnerType()); + if(innerType.type == rdcspv::DataType::ArrayType) + matchName = spirv.GetDataType(innerType.InnerType()).name; + else + matchName = innerType.name; + } - if(name == typeName) + if(name == matchName) { convertedPatchData.rwInterface.resize_for_index(i); convertedPatchData.rwInterface[i] = spvPatchData.rwInterface[j]; @@ -561,7 +560,8 @@ void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &drv, ResourceI name += "_var"; for(size_t j = 0; j < spvReflection.readWriteResources.size(); j++) { - if(name == spvReflection.readWriteResources[j].name) + const rdcstr &matchName = spvReflection.readWriteResources[j].name; + if(name == matchName) { convertedPatchData.rwInterface.resize_for_index(i); convertedPatchData.rwInterface[i] = spvPatchData.rwInterface[j]; @@ -586,32 +586,22 @@ void WrappedOpenGL::ShaderData::ProcessCompilation(WrappedOpenGL &drv, ResourceI for(size_t i = 0; i < reflection->samplers.size(); i++) { + rdcstr name = spvReflection.samplers[i].name; + // Only match on base name remove any array indexes + int arrOffs = name.indexOf('['); + if(arrOffs > 0) + name.erase(arrOffs, name.size()); + bool found = false; for(size_t j = 0; j < spvReflection.samplers.size(); j++) { if(reflection->samplers[i].name == spvReflection.samplers[j].name) - { - found = true; - } - else if((spvReflection.samplers[j].bindArraySize > 1)) - { - rdcstr &baseName = spvReflection.samplers[j].name; - for(uint32_t idx = 0; idx < spvReflection.samplers[j].bindArraySize; idx++) - { - rdcstr resName = StringFormat::Fmt("%s[%u]", baseName.c_str(), idx); - if(reflection->samplers[i].name == resName) - { - found = true; - break; - } - } - } - if(found) { convertedPatchData.samplerInterface.resize_for_index(i); convertedPatchData.samplerInterface[i] = spvPatchData.samplerInterface[j]; convertedRefl.samplers.resize_for_index(i); convertedRefl.samplers[i] = spvReflection.samplers[j]; + found = true; break; } } From c2785c9b1348f27951a44d92f0164756bbc70722 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Wed, 7 Oct 2026 11:46:55 +0100 Subject: [PATCH 12/16] GL Spirv debugger support for arrays of UBOs, SSBOs - bounded arrays only --- .../shaders/spirv/spirv_debug_setup.cpp | 141 ++++++++++++++---- 1 file changed, 112 insertions(+), 29 deletions(-) diff --git a/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp b/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp index c6500a81f71..07e43782c42 100644 --- a/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp @@ -49,6 +49,7 @@ enum class PointerFlags SSBO = 0x2, GlobalArrayBinding = 0x4, DereferencedPhysical = 0x8, + StructArrayBinding = 0x10, }; BITMASK_OPERATORS(PointerFlags); @@ -1407,9 +1408,51 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s var.rows = 1; var.columns = 1; var.type = VarType::ReadWriteResource; + enablePointerFlags(var, PointerFlags::SSBO); int32_t idx = patchData.rwInterface.indexOf(v.id); + // GL SSBO Arrays + if((api->GetGraphicsAPI() == GraphicsAPI::OpenGL) && isArray) + { + enablePointerFlags(var, PointerFlags::StructArrayBinding); + for(uint32_t i = 0; i < arraySize; ++i) + { + ShaderVariable member; + + member.rows = 1; + member.columns = 1; + member.name = StringFormat::Fmt("[%u]", i); + member.type = VarType::ReadWriteResource; + enablePointerFlags(member, PointerFlags::SSBO); + + // Bind to index "idx+i" because GL resource arrays are expanded in element order + if(idx >= 0) + member.SetBindIndex( + ShaderBindIndex(DescriptorCategory::ReadWriteResource, idx + i, 0)); + else + member.SetBindIndex(ShaderBindIndex()); + + var.members.push_back(member); + + // Source mapping per array element because GL resource arrays are expanded + SourceVariableMapping srcVar; + srcVar.name = StringFormat::Fmt("%s[%u]", sourceName.c_str(), i); + srcVar.type = var.members[i].type; + srcVar.rows = 1; + srcVar.columns = 1; + srcVar.offset = 0; + srcVar.variables.push_back( + DebugVariableReference(DebugVariableType::ReadWriteResource, + StringFormat::Fmt("%s[%u]", var.name.c_str(), i))); + ret->sourceVars.push_back(srcVar); + } + + global.readWriteResources.push_back(var); + pointerIDs.push_back(GLOBAL_POINTER(v.id, readWriteResources)); + continue; + } + // on GL we may have buffers which are dead-code eliminated but remain part of the simulated // code. Because we base our interfaces off the GLSL reflected data it may not be present if(idx >= 0) @@ -1439,15 +1482,17 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s ShaderBindIndex binding; binding.category = DescriptorCategory::ConstantBlock; - binding.index = patchData.cblockInterface.indexOf(v.id); + int32_t idx = patchData.cblockInterface.indexOf(v.id); // on GL we may have buffers which are dead-code eliminated but remain part of the simulated // code. Because we base our interfaces off the GLSL reflected data it may not be present - if(binding.index == ~0U) + if(idx >= 0) + binding.index = idx; + else binding = ShaderBindIndex(); if(api->GetGraphicsAPI() == GraphicsAPI::Vulkan) - RDCASSERT(binding.index != ~0U); + RDCASSERT(idx >= 0); auto cbufferCallback = [this, &binding]( ShaderVariable &var, const Decorations &curDecorations, @@ -1515,35 +1560,56 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s } }; - bool unbounded = isArray && (arraySize == ~0U); - if(!unbounded) + if(!isArray) { - if(isArray) - { - var.members.reserve(arraySize); - - for(uint32_t a = 0; a < arraySize; a++) - { - binding.arrayElement = a; - var.members.push_back(ShaderVariable()); - var.members.back().name = StringFormat::Fmt("[%u]", a); - WalkVariable(decorations[v.id], *innertype, 0U, false, - var.members.back(), rdcstr(), cbufferCallback); - } - } - else - { - WalkVariable(decorations[v.id], *innertype, 0U, false, var, - rdcstr(), cbufferCallback); - } + WalkVariable(decorations[v.id], *innertype, 0U, false, var, + rdcstr(), cbufferCallback); } else { - enablePointerFlags(var, PointerFlags::GlobalArrayBinding); - var.SetBindIndex(binding); var.rows = 1; var.columns = 1; var.type = VarType::ConstantBlock; + + // GL UBO Arrays + if((api->GetGraphicsAPI() == GraphicsAPI::OpenGL)) + { + enablePointerFlags(var, PointerFlags::StructArrayBinding); + for(uint32_t i = 0; i < arraySize; ++i) + { + ShaderVariable member; + + member.rows = 1; + member.columns = 1; + member.name = StringFormat::Fmt("[%u]", i); + member.type = VarType::ConstantBlock; + + // Bind to index "idx+i" because GL resource arrays are expanded in element order + if(idx >= 0) + member.SetBindIndex(ShaderBindIndex(DescriptorCategory::ConstantBlock, idx + i, 0)); + else + member.SetBindIndex(ShaderBindIndex()); + + var.members.push_back(member); + + // Source mapping per array element because GL resource arrays are expanded + SourceVariableMapping srcVar; + srcVar.name = StringFormat::Fmt("%s[%u]", sourceName.c_str(), i); + srcVar.type = var.members[i].type; + srcVar.rows = 1; + srcVar.columns = 1; + srcVar.offset = 0; + srcVar.variables.push_back(DebugVariableReference( + DebugVariableType::Constant, StringFormat::Fmt("%s[%u]", var.name.c_str(), i))); + ret->sourceVars.push_back(srcVar); + } + + global.constantBlocks.push_back(var); + pointerIDs.push_back(GLOBAL_POINTER(v.id, constantBlocks)); + continue; + } + enablePointerFlags(var, PointerFlags::GlobalArrayBinding); + var.SetBindIndex(binding); } sourceVar.type = VarType::ConstantBlock; @@ -1680,7 +1746,7 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s else if(memberType == VarType::ReadWriteResource) idx = patchData.rwInterface.indexOf(v.id); - uint32_t len = uintComp(GetActiveLane().ids[innertype->length], 0); + uint32_t len = EvaluateConstant(innertype->length, specInfo).value.u32v[0]; for(uint32_t i = 0; i < len; ++i) { ShaderVariable member; @@ -3326,6 +3392,8 @@ ShaderVariable Debugger::MakeCompositePointer(const ShaderVariable &base, Id id, uint64_t byteOffset = 0; const DataType *type = NULL; + bool isStructArray = checkPointerFlags(*leaf, PointerFlags::StructArrayBinding); + if(physicalPointer) { // work purely with the pointer itself. All we're going to do effectively is move the address @@ -3343,13 +3411,24 @@ ShaderVariable Debugger::MakeCompositePointer(const ShaderVariable &base, Id id, } else { - ret = MakePointerVariable(id, leaf); - - byteOffset = getByteOffset(base); type = &dataTypes[idTypes[id]]; RDCASSERT(type->type == DataType::PointerType); type = &dataTypes[type->InnerType()]; + + if(isStructArray) + { + // TODO JAKE : DEFENSIVE CODE FOR INDEX 0 + // First index is the member index into the struct_array + leaf = &leaf->members[indices[0]]; + + RDCASSERT(type->type == DataType::ArrayType); + type = &dataTypes[type->InnerType()]; + id = type->id; + } + ret = MakePointerVariable(id, leaf); + + byteOffset = getByteOffset(base); } setMatrixStride(ret, getMatrixStride(base)); @@ -3369,6 +3448,10 @@ ShaderVariable Debugger::MakeCompositePointer(const ShaderVariable &base, Id id, setBindArrayIndex(ret, indices[i++]); type = &dataTypes[type->InnerType()]; } + else if(isStructArray) + { + i++; + } else { setBindArrayIndex(ret, getBindArrayIndex(base)); From 12cb34850c691178019db1111f6293b1b20830b9 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Thu, 8 Oct 2026 11:37:47 +0100 Subject: [PATCH 13/16] Extend D3D12_Shader_Debug_Zoo tests for arrayed resources - bounded array of srv, uav, sampler, cbuffer - unbounded array of srv, uav, sampler, cbuffer --- .../demos/d3d12/d3d12_shader_debug_zoo.cpp | 126 +++++++++++++++++- 1 file changed, 120 insertions(+), 6 deletions(-) diff --git a/util/test/demos/d3d12/d3d12_shader_debug_zoo.cpp b/util/test/demos/d3d12/d3d12_shader_debug_zoo.cpp index 17ce6738fb3..3daae61730d 100644 --- a/util/test/demos/d3d12/d3d12_shader_debug_zoo.cpp +++ b/util/test/demos/d3d12/d3d12_shader_debug_zoo.cpp @@ -199,6 +199,12 @@ v2f main(consts IN, uint tri : SV_InstanceID) // we want to do this on purpose #pragma warning( disable : 3556 ) +#if (SM_5_1 || SM_6_0 || SM_6_2 || SM_6_6) +#define ARRAYED_RESOURCES 1 +#else +#define ARRAYED_RESOURCES 0 +#endif + struct InnerStruct { float a; @@ -257,6 +263,19 @@ StructuredBuffer int16srv : register(t42); Buffer int16srv : register(t43); #endif +#if ARRAYED_RESOURCES +Texture2D tex2ds[14] : register(t0, space1); +SamplerState samplers[14] : register(s0, space1); +RWStructuredBuffer buffers[14] : register(u0, space1); +struct Data +{ + float4 zeroes; + uint4 index; + float4 padding[14]; +}; +ConstantBuffer cbuffers[14] : register(b0, space1); +#endif + static const int gConstInt = 10; static const int gConstIntArray[6] = { 1, 2, 3, 4, 5, 6 }; static int gInt = 3; @@ -274,6 +293,7 @@ float4 main(v2f IN) : SV_Target0 float tiny = IN.tinyVal; int intval = IN.intval; + uint zerou = uint(zero); if(IN.tri == 0) return float4(log(negone), log(zero), log(posone), 1.0f); @@ -1162,6 +1182,46 @@ float4 main(v2f IN) : SV_Target0 byterwtest2.GetDimensions(c); return float4(float(a), float(b), float(c), 0.0f); } +#if ARRAYED_RESOURCES + if(IN.tri == 114) + { + float2 uv = posone * float2(0.55f, 0.48f); + return tex2ds[zerou+9].Sample(linearclamp, uv, int2(4, 3)); + } + if(IN.tri == 115) + { + float2 uv = posone * float2(1.55f, 1.48f); + return smiley.Sample(samplers[zerou+2], uv, int2(4, 3)); + } + if(IN.tri == 116) + { + uint z = intval - IN.tri - 7; + + MyStruct write = (MyStruct)0; + + write.a = zero+1.0f; + write.c = zero+2.0f; + write.e = zero+3.0f; + write.b = float4(zero+4.0f, zero+5.0f, zero+6.0f, zero+7.0f); + write.d.a = zero+8.0f; + write.d.b[0] = zero+9.0f; + write.d.b[1] = zero+10.0f; + write.d.c = zero+11.0f; + + buffers[zerou+3][z+2] = write; + MyStruct read = buffers[z+3][zerou+2]; + return float4(read.b.xyz, read.c); + } + if(IN.tri == 117) + { + float4 Color = float4(0,0,0,0); + Color.x = float(cbuffers[zerou+1].index.x); + Color.y = float(cbuffers[zerou+2].index.y); + Color.z = float(cbuffers[zerou+3].index.z); + Color.w = float(cbuffers[zerou+4].index.w); + return Color; + } +#endif return float4(0.4f, 0.4f, 0.4f, 0.4f); } )EOSHADER"; @@ -2103,6 +2163,20 @@ void main(uint3 inDTID : SV_DispatchThreadID, uint3 inGID : SV_GroupThreadID, ui multiRangeParam.DescriptorTable.NumDescriptorRanges = ARRAY_COUNT(multiRanges); multiRangeParam.DescriptorTable.pDescriptorRanges = multiRanges; + ID3D12RootSignaturePtr sig_5_0 = MakeSig( + { + tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 0, 8, 0), + tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 0, 1, 3, 10), + tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 100, 5, 20), + tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 0, 4, 5, 30), + multiRangeParam, + uavParam(D3D12_SHADER_VISIBILITY_PIXEL, 0, 21), + srvParam(D3D12_SHADER_VISIBILITY_PIXEL, 0, 20), + tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 0, 9, 3, 100), + srvParam(D3D12_SHADER_VISIBILITY_PIXEL, 0, 21), + }, + D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT, 1, &staticSamp); + ID3D12RootSignaturePtr sig = MakeSig( { tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 0, 8, 0), @@ -2114,6 +2188,14 @@ void main(uint3 inDTID : SV_DispatchThreadID, uint3 inGID : SV_GroupThreadID, ui srvParam(D3D12_SHADER_VISIBILITY_PIXEL, 0, 20), tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 0, 9, 3, 100), srvParam(D3D12_SHADER_VISIBILITY_PIXEL, 0, 21), + // Texture2D tex2ds[14] : register(t0, space1); + tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 14, 150), + // SamplerState samplers[14] : register(s0, space1); + tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0, 14, 0), + // RWStructuredBuffer buffers[14] : register(u0, space1); + tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0, 14, 250), + // ConstantBuffer cbuffers[14] : register(b0, space1); + tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0, 14, 300), }, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT, 1, &staticSamp); @@ -2123,7 +2205,7 @@ void main(uint3 inDTID : SV_DispatchThreadID, uint3 inGID : SV_GroupThreadID, ui ID3DBlobPtr vs5blob = Compile(common + vertex, "main", "vs_5_0"); psos[0] = MakePSO() - .RootSig(sig) + .RootSig(sig_5_0) .InputLayout(inputLayout) .VS(vs5blob) .PS(Compile(common + shaderDefines + pixel, "main", "ps_5_0", @@ -2132,7 +2214,7 @@ void main(uint3 inDTID : SV_DispatchThreadID, uint3 inGID : SV_GroupThreadID, ui psos[0]->SetName(L"ps_5_0"); psos[1] = MakePSO() - .RootSig(sig) + .RootSig(sig_5_0) .InputLayout(inputLayout) .VS(vs5blob) .PS(Compile(common + shaderDefines + pixel, "main", "ps_5_0", CompileOptionFlags::None)) @@ -2224,7 +2306,7 @@ void main(uint3 inDTID : SV_DispatchThreadID, uint3 inGID : SV_GroupThreadID, ui ID3D12PipelineStatePtr noResPSOs[numShaderModels * 2] = {}; noResPSOs[0] = MakePSO() - .RootSig(sig) + .RootSig(sig_5_0) .InputLayout(inputLayout) .VS(vs5blob) .PS(Compile(common + noResourcesPixel, "main", "ps_5_0", @@ -2233,7 +2315,7 @@ void main(uint3 inDTID : SV_DispatchThreadID, uint3 inGID : SV_GroupThreadID, ui noResPSOs[0]->SetName(L"ps_5_0"); noResPSOs[1] = MakePSO() - .RootSig(sig) + .RootSig(sig_5_0) .InputLayout(inputLayout) .VS(vs5blob) .PS(Compile(common + noResourcesPixel, "main", "ps_5_0", CompileOptionFlags::None)) @@ -2578,6 +2660,24 @@ void main(uint3 inDTID : SV_DispatchThreadID, uint3 inGID : SV_GroupThreadID, ui uav2cpu = uavView2.CreateClearCPU(9); uav2gpu = uavView2.CreateGPU(9); + // tex2ds[14] GPU Base = 150 Used Indices: 9 + MakeSRV(smiley).Format(DXGI_FORMAT_R8G8B8A8_UNORM).CreateGPU(150 + 9); + + // samplers[14] GPU Base = 0 Used Indices: 2 + D3D12_SAMPLER_DESC pointSamplerDesc = {}; + pointSamplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; + pointSamplerDesc.AddressU = pointSamplerDesc.AddressV = pointSamplerDesc.AddressW = + D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + UINT increment = dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + D3D12_CPU_DESCRIPTOR_HANDLE samplerStart = m_Sampler->GetCPUDescriptorHandleForHeapStart(); + dev->CreateSampler(&pointSamplerDesc, {samplerStart.ptr + increment * 2}); + // for(int i = 0; i < 128; ++i) + // dev->CreateSampler(&pointSamplerDesc, {samplerStart.ptr + increment * i}); + + // buffers[14] GPU Base = 250 Used Indices: 3 + uavView2.CreateGPU(250 + 3); + // cbuffers[14] GPU Base = 300 Used Indices: 1, 2, 3, 4 + // Create resources for MSAA draw ID3DBlobPtr vsmsaablob = Compile(D3DDefaultVertex, "main", "vs_5_0"); ID3DBlobPtr psmsaablob = Compile(msaaPixel, "main", "ps_5_0"); @@ -2922,8 +3022,13 @@ void main(uint3 inDTID : SV_DispatchThreadID, uint3 inGID : SV_GroupThreadID, ui IASetVertexBuffer(cmd, vb, sizeof(ConstsA2V), 0); cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - cmd->SetGraphicsRootSignature(sig); - cmd->SetDescriptorHeaps(1, &m_CBVUAVSRV.GetInterfacePtr()); + if(i < 2) + cmd->SetGraphicsRootSignature(sig_5_0); + else + cmd->SetGraphicsRootSignature(sig); + ID3D12DescriptorHeap *heaps[] = {m_CBVUAVSRV.GetInterfacePtr(), + m_Sampler.GetInterfacePtr()}; + cmd->SetDescriptorHeaps(2, heaps); cmd->SetGraphicsRootDescriptorTable(0, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); cmd->SetGraphicsRootDescriptorTable(1, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); cmd->SetGraphicsRootDescriptorTable(2, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); @@ -2935,6 +3040,15 @@ void main(uint3 inDTID : SV_DispatchThreadID, uint3 inGID : SV_GroupThreadID, ui cmd->SetGraphicsRootDescriptorTable(7, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); cmd->SetGraphicsRootShaderResourceView( 8, rootbytesrv->GetGPUVirtualAddress() + renderDataSize); + if(i >= 2) + { + cmd->SetGraphicsRootDescriptorTable(9, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + cmd->SetGraphicsRootDescriptorTable(10, m_Sampler->GetGPUDescriptorHandleForHeapStart()); + // cmd->SetGraphicsRootDescriptorTable(11, + // m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + // cmd->SetGraphicsRootDescriptorTable(12, + // m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + } // Add a marker so we can easily locate this draw std::string markerName = markers[i]; From 167629a51489447eb0d4976be83c6b3894d29b30 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Fri, 9 Oct 2026 17:25:04 +0100 Subject: [PATCH 14/16] DXIL Debugger improved support for arrays of resources - simulation support for arrays of Samplers - add arrayed SRVs, UAVs, Samplers, cbuffers to the global state and UI --- renderdoc/driver/shaders/dxil/dxil_debug.cpp | 70 ++++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/renderdoc/driver/shaders/dxil/dxil_debug.cpp b/renderdoc/driver/shaders/dxil/dxil_debug.cpp index 5194270aec4..97493eea4b5 100644 --- a/renderdoc/driver/shaders/dxil/dxil_debug.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_debug.cpp @@ -2510,32 +2510,35 @@ bool ThreadState::ExecuteInstruction(const rdcarray &workgroup) const ResourceReference *resRef = m_Program.GetResourceReference(resultId); if(resRef) { - const rdcarray *list = NULL; - // a static known handle which should be in the global resources container - switch(resRef->resourceBase.resClass) - { - case ResourceClass::CBuffer: list = &m_GlobalState.constantBlocks; break; - case ResourceClass::SRV: list = &m_GlobalState.readOnlyResources; break; - case ResourceClass::UAV: list = &m_GlobalState.readWriteResources; break; - case ResourceClass::Sampler: list = &m_GlobalState.samplers; break; - default: - RDCERR("Invalid ResourceClass %u", (uint32_t)resRef->resourceBase.resClass); - break; - }; - RDCASSERT(list); - rdcstr resName = Debugger::GetResourceBaseName(&m_Program, resRef->resourceBase); - - const rdcarray &resources = *list; - result.name.clear(); size_t constantBlockIndex = ~0U; - for(uint32_t i = 0; i < resources.size(); ++i) + result.name.clear(); + + // Non-arrayed resource : a static known handle which should be in the global resources container + if(resRef->resourceBase.regCount == 1) { - if(resources[i].name == resName) + const rdcarray *list = NULL; + switch(resRef->resourceBase.resClass) { - constantBlockIndex = i; - result = resources[i]; - break; + case ResourceClass::CBuffer: list = &m_GlobalState.constantBlocks; break; + case ResourceClass::SRV: list = &m_GlobalState.readOnlyResources; break; + case ResourceClass::UAV: list = &m_GlobalState.readWriteResources; break; + case ResourceClass::Sampler: list = &m_GlobalState.samplers; break; + default: + RDCERR("Invalid ResourceClass %u", (uint32_t)resRef->resourceBase.resClass); + break; + }; + RDCASSERT(list); + + const rdcarray &resources = *list; + for(uint32_t i = 0; i < resources.size(); ++i) + { + if(resources[i].name == resName) + { + constantBlockIndex = i; + result = resources[i]; + break; + } } } if(result.name.isEmpty()) @@ -2553,11 +2556,23 @@ bool ThreadState::ExecuteInstruction(const rdcarray &workgroup) uint32_t arrayIndex = arg.value.u32v[0]; RDCASSERT(arrayIndex >= resRef->resourceBase.regBase); arrayIndex -= resRef->resourceBase.regBase; - bool isSRV = (resRef->resourceBase.resClass == ResourceClass::SRV); - DescriptorCategory category = isSRV ? DescriptorCategory::ReadOnlyResource - : DescriptorCategory::ReadWriteResource; + DescriptorCategory category = DescriptorCategory::Unknown; + if(resRef->resourceBase.resClass == ResourceClass::SRV) + { + category = DescriptorCategory::ReadOnlyResource; + result.type = VarType::ReadOnlyResource; + } + else if(resRef->resourceBase.resClass == ResourceClass::UAV) + { + category = DescriptorCategory::ReadWriteResource; + result.type = VarType::ReadWriteResource; + } + else if(resRef->resourceBase.resClass == ResourceClass::Sampler) + { + category = DescriptorCategory::Sampler; + result.type = VarType::Sampler; + } result.SetBindIndex(ShaderBindIndex(category, resRef->resourceIndex, arrayIndex)); - result.type = isSRV ? VarType::ReadOnlyResource : VarType::ReadWriteResource; // Default to unannotated handle ClearAnnotatedHandle(result); } @@ -9316,9 +9331,6 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *apiWrapper, uint32_t eve for(uint32_t i = 0; i < list.resources.size(); i++) { const ShaderResource &res = list.resources[i]; - // Ignore arrays the debugger execution will mark specific array elements used - if(res.bindArraySize > 1) - continue; // Fetch the resource name BindingSlot slot(res.fixedBindNumber, res.fixedBindSetOrSpace); From f009ec830e7ccf9411a9d45354fe7966934c5fc2 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Fri, 9 Oct 2026 17:25:24 +0100 Subject: [PATCH 15/16] DXBC Debugger support for arrays of Samplers (SM5.1) --- renderdoc/driver/shaders/dxbc/dxbc_debug.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp index b0da7ac4dd3..1b4caafc8e2 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp @@ -4008,6 +4008,10 @@ void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper, { samplerMode = decl.samplerMode; samplerBinding = GetBindingSlotForDeclaration(*program, decl); + + // With SM5.1 sampler arrays need to offset the shader register by the array index + if(program->IsShaderModel51()) + samplerBinding.shaderRegister = srcOpers[2].value.u32v[1]; } if(op.operation == OPCODE_LD && decl.declaration == OPCODE_DCL_RESOURCE && decl.resource.dim == RESOURCE_DIMENSION_BUFFER && From 5eaa879f3f4a8f29ff081b51d5703b98e9dd5d99 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Fri, 9 Oct 2026 17:25:44 +0100 Subject: [PATCH 16/16] ShaderViewer support for arrays of Samplers --- qrenderdoc/Windows/ShaderViewer.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index 238a2c70fbe..5d5d3ddf3c9 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -5139,19 +5139,18 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi ShaderBindIndex bind = reg->GetBindIndex(); int32_t bindIdx = samplers.indexOf(bind); - if(bindIdx < 0) + Descriptor desc = samplers[bindIdx].descriptor; + 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()) { value = ToQStr(ResourceId()); } else { - Descriptor desc = samplers[bindIdx].descriptor; - rdcarray descriptors; - for(const UsedDescriptor &a : samplers) - if(CategoryForDescriptorType(a.access.type) == bind.category && - a.access.index == bind.index) - descriptors.push_back(a); - const ShaderSampler &samp = m_ShaderDetails->samplers[bind.index]; if(samp.bindArraySize == 1)