From 02136dd6b86ebd3b084a196ecb61280783fbee3c Mon Sep 17 00:00:00 2001 From: James Sumihiro Date: Thu, 1 Oct 2026 13:31:12 -0700 Subject: [PATCH] Add support for VK_EXT_shader_object to FetchShaderFeedback --- renderdoc/driver/vulkan/vk_debug.cpp | 152 +++++++++++------ renderdoc/driver/vulkan/vk_replay.h | 2 +- .../driver/vulkan/vk_shader_feedback.cpp | 160 +++++++++++------- 3 files changed, 200 insertions(+), 114 deletions(-) diff --git a/renderdoc/driver/vulkan/vk_debug.cpp b/renderdoc/driver/vulkan/vk_debug.cpp index 5f6bfdcf788..1f53c45dbb9 100644 --- a/renderdoc/driver/vulkan/vk_debug.cpp +++ b/renderdoc/driver/vulkan/vk_debug.cpp @@ -2742,8 +2742,9 @@ void VulkanDebugManager::UnlockReadbackBuffer() } void VulkanReplay::AllocAndAddReservedDescriptors( - const VulkanStatePipeline &pipe, AddedDescriptorData &patchedBufferData, - bool vertexPatchedToCompute, const rdcarray &newBindings) + const VulkanRenderState &state, const VulkanStatePipeline &pipe, + AddedDescriptorData &patchedBufferData, bool vertexPatchedToCompute, + const rdcarray &newBindings) { VkDevice dev = m_Device; VulkanCreationInfo &creationInfo = m_pDriver->m_CreationInfo; @@ -2802,11 +2803,33 @@ void VulkanReplay::AllocAndAddReservedDescriptors( // bitmask's type list is. rdcarray> mutableBitmaskArrayRange; + const bool compute = pipe.bindPoint == VK_PIPELINE_BIND_POINT_COMPUTE; + + size_t descSetLayoutsSize = 0; + + if(pipe.shaderObject) + { + for(uint32_t stage = 0; stage < NumShaderStages; stage++) + { + if(compute && (ShaderStage)stage != ShaderStage::Compute) + continue; + if(!compute && (ShaderStage)stage == ShaderStage::Compute) + continue; + + descSetLayoutsSize = + RDCMAX(descSetLayoutsSize, + creationInfo.m_ShaderObject[state.shaderObjects[stage]].descSetLayouts.size()); + } + } + else + { + descSetLayoutsSize = pipeInfo.descSetLayouts.size(); + } + // populate mutable bitmasks. This loop is the same as the one below which is more commented - for(size_t i = 0; i < setLayouts.size(); i++) + for(size_t i = 0; i < RDCMIN(descSetLayoutsSize, pipe.descSets.size()); i++) { - if(i < pipeInfo.descSetLayouts.size() && i < pipe.descSets.size() && - pipe.descSets[i].pipeLayout != ResourceId()) + if(pipe.descSets[i].pipeLayout != ResourceId()) { const VulkanCreationInfo::PipelineLayout &pipelineLayoutInfo = creationInfo.m_PipelineLayout[pipe.descSets[i].pipeLayout]; @@ -2819,6 +2842,9 @@ void VulkanReplay::AllocAndAddReservedDescriptors( for(size_t b = 0; b < origLayout.bindings.size(); b++) { + if(origLayout.bindings[b].layoutDescType != VK_DESCRIPTOR_TYPE_MUTABLE_EXT) + continue; + uint64_t mutableBitmask = origLayout.mutableBitmasks[b]; int bitmaskIdx = mutablePoolsizeBitmask.indexOf(mutableBitmask); @@ -2873,7 +2899,7 @@ void VulkanReplay::AllocAndAddReservedDescriptors( // if there are fewer sets bound than were declared in the pipeline layout, only process the // bound sets (as otherwise we'd fail to copy from them). Assume the application knew what it // was doing and the other sets are statically unused. - setLayouts.resize(RDCMIN(pipe.descSets.size(), pipeInfo.descSetLayouts.size())); + setLayouts.resize(RDCMIN(pipe.descSets.size(), descSetLayoutsSize)); size_t boundDescs = setLayouts.size(); @@ -3037,7 +3063,7 @@ void VulkanReplay::AllocAndAddReservedDescriptors( // if the shader had no descriptor sets at all, i will be invalid, so just skip and add a set // with only our own bindings. - if(i < pipeInfo.descSetLayouts.size() && i < pipe.descSets.size() && + if(i < descSetLayoutsSize && i < pipe.descSets.size() && pipe.descSets[i].pipeLayout != ResourceId()) { const VulkanCreationInfo::PipelineLayout &pipelineLayoutInfo = @@ -3423,7 +3449,7 @@ VulkanReplay::AddedDescriptorData VulkanReplay::PrepareExtraBufferDescriptor( { // create a duplicate set of descriptor sets, all visible to compute, with bindings shifted to // account for new ones we need. This also copies the existing bindings into the new sets - AllocAndAddReservedDescriptors(srcPipeState, ret, vertexPatchedToCompute, newBindings); + AllocAndAddReservedDescriptors(state, srcPipeState, ret, vertexPatchedToCompute, newBindings); // if the pool failed due to limits, it will be NULL so bail now if(ret.descpool == VK_NULL_HANDLE) @@ -3550,6 +3576,13 @@ VulkanReplay::AddedDescriptorData VulkanReplay::PrepareExtraBufferDescriptor( dstPipeState.descSets[i].pipeLayout = GetResID(ret.pipeLayout); dstPipeState.descSets[i].descSet = GetResID(ret.descSets[i]); } + + // when no pipeline is bound, we rely on the pipeline layout used by last bound descriptor set + // to reconstruct correct descriptor set bindings (see BindDescriptorSetsWithoutPipeline and + // BindDescriptorSetsForShaders). Here our descriptor sets have been patched and all bound sets + // share a pipeline layout. + if(dstPipeState.shaderObject && !vertexPatchedToCompute) + dstPipeState.lastBoundDescSet = 0; } else if(ret.pipeLayout != VK_NULL_HANDLE) { @@ -3597,6 +3630,7 @@ void VulkanReplay::PrepareStateForPatchedShader( } VkPipeline pipe = VK_NULL_HANDLE; + rdcarray shaderObjs = {}; if(pipelineId != ResourceId() && compute) { const rdcarray &origSpirv = @@ -3700,64 +3734,71 @@ void VulkanReplay::PrepareStateForPatchedShader( modifiedstate.graphics.pipeline = GetResID(pipe); } - - rdcarray shaderObjs; - - for(uint32_t i = 0; i < NumShaderStages; i++) + else { - ResourceId shadId = modifiedstate.shaderObjects[i]; - if(shadId == ResourceId()) - continue; + for(uint32_t i = 0; i < NumShaderStages; i++) + { + if(compute && (ShaderStage)i != ShaderStage::Compute) + continue; + else if(!compute && (ShaderStage)i == ShaderStage::Compute) + continue; - const rdcarray &origSpirv = c.m_ShaderModule[shadId].spirv.GetSPIRV(); - rdcarray modSpirv; + ResourceId shadId = modifiedstate.shaderObjects[i]; + if(shadId == ResourceId()) + continue; - VkShaderEXT shad = VK_NULL_HANDLE; - VkShaderCreateInfoEXT shadCreateinfo = {}; - m_pDriver->GetShaderCache()->MakeShaderObjectInfo(shadCreateinfo, shadId); + const rdcarray &origSpirv = c.m_ShaderModule[shadId].spirv.GetSPIRV(); + rdcarray modSpirv; - bool patched = stagePatchCallback(patchedBufferdata, shadCreateinfo.stage, shadCreateinfo.pName, - origSpirv, modSpirv, shadCreateinfo.pSpecializationInfo); + VkShaderEXT shad = VK_NULL_HANDLE; + VkShaderCreateInfoEXT shadCreateinfo = {}; + m_pDriver->GetShaderCache()->MakeShaderObjectInfo(shadCreateinfo, shadId); - if(patched) - { - shadCreateinfo.pCode = modSpirv.data(); - shadCreateinfo.codeSize = modSpirv.size() * sizeof(uint32_t); - } - else if(IsBinding(m_StorageMode)) - { - modSpirv = origSpirv; + bool patched = stagePatchCallback(patchedBufferdata, shadCreateinfo.stage, shadCreateinfo.pName, + origSpirv, modSpirv, shadCreateinfo.pSpecializationInfo); - // if we're stealing a binding point, we need to patch all other shaders - rdcspv::Editor editor(modSpirv); + if(patched) + { + shadCreateinfo.pCode = modSpirv.data(); + shadCreateinfo.codeSize = modSpirv.size() * sizeof(uint32_t); + } + else if(IsBinding(m_StorageMode)) + { + modSpirv = origSpirv; - editor.Prepare(); - editor.SetBufferStorageMode(m_StorageMode); + // if we're stealing a binding point, we need to patch all other shaders + { + rdcspv::Editor editor(modSpirv); - editor.OffsetBindingsToMatchReservation(patchedBufferdata.numNewBindings); + editor.Prepare(); + editor.SetBufferStorageMode(m_StorageMode); - shadCreateinfo.pCode = modSpirv.data(); - shadCreateinfo.codeSize = modSpirv.size() * sizeof(uint32_t); - } - else - { - shadCreateinfo.pCode = origSpirv.data(); - shadCreateinfo.codeSize = origSpirv.size() * sizeof(uint32_t); - } + editor.OffsetBindingsToMatchReservation(patchedBufferdata.numNewBindings); + } - // if we're stealing a binding point, all shaders need updated descriptor sets - if(IsBinding(m_StorageMode)) - { - shadCreateinfo.setLayoutCount = (uint32_t)patchedBufferdata.setLayouts.size(); - shadCreateinfo.pSetLayouts = patchedBufferdata.setLayouts.data(); - } + shadCreateinfo.pCode = modSpirv.data(); + shadCreateinfo.codeSize = modSpirv.size() * sizeof(uint32_t); + } + else + { + shadCreateinfo.pCode = origSpirv.data(); + shadCreateinfo.codeSize = origSpirv.size() * sizeof(uint32_t); + } - vkr = m_pDriver->vkCreateShadersEXT(dev, 1, &shadCreateinfo, NULL, &shad); - CHECK_VKR(m_pDriver, vkr); + // if we're stealing a binding point, all shaders need updated descriptor sets + if(IsBinding(m_StorageMode)) + { + shadCreateinfo.setLayoutCount = (uint32_t)patchedBufferdata.setLayouts.size(); + shadCreateinfo.pSetLayouts = patchedBufferdata.setLayouts.data(); + } - shaderObjs.push_back(shad); + vkr = m_pDriver->vkCreateShadersEXT(dev, 1, &shadCreateinfo, NULL, &shad); + CHECK_VKR(m_pDriver, vkr); - modifiedstate.shaderObjects[i] = GetResID(shad); + shaderObjs.push_back(shad); + + modifiedstate.shaderObjects[i] = GetResID(shad); + } } modifiedstate.subpassContents = VK_SUBPASS_CONTENTS_INLINE; @@ -3813,7 +3854,10 @@ bool VulkanReplay::RunFeedbackAction(VkDeviceSize bufferSize, const ActionDescri if(action->flags & ActionFlags::Dispatch) { - modifiedstate.BindPipeline(m_pDriver, cmd, VulkanRenderState::BindCompute, true); + if(modifiedstate.compute.shaderObject) + modifiedstate.BindShaderObjects(m_pDriver, cmd, VulkanRenderState::BindCompute); + else + modifiedstate.BindPipeline(m_pDriver, cmd, VulkanRenderState::BindCompute, true); ObjDisp(cmd)->CmdDispatch(Unwrap(cmd), action->dispatchDimension[0], action->dispatchDimension[1], action->dispatchDimension[2]); diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index 70ed46041b9..95e34e33021 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -555,7 +555,7 @@ class VulkanReplay : public IReplayDriver const rdcfixedarray &groupid, const rdcfixedarray &threadid); - void AllocAndAddReservedDescriptors(const VulkanStatePipeline &pipe, + void AllocAndAddReservedDescriptors(const VulkanRenderState &state, const VulkanStatePipeline &pipe, AddedDescriptorData &patchedBufferData, bool vertexPatchedToCompute, const rdcarray &newBindings); diff --git a/renderdoc/driver/vulkan/vk_shader_feedback.cpp b/renderdoc/driver/vulkan/vk_shader_feedback.cpp index 55d2dd44334..7a3217315f1 100644 --- a/renderdoc/driver/vulkan/vk_shader_feedback.cpp +++ b/renderdoc/driver/vulkan/vk_shader_feedback.cpp @@ -1518,8 +1518,9 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId) result.compute = bool(action->flags & ActionFlags::Dispatch); const VulkanStatePipeline &pipe = result.compute ? state.compute : state.graphics; + const bool shaderObject = pipe.shaderObject; - if(pipe.pipeline == ResourceId()) + if(pipe.pipeline == ResourceId() && !shaderObject) { result.valid = true; return false; @@ -1527,24 +1528,40 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId) const VulkanCreationInfo::Pipeline &pipeInfo = creationInfo.m_Pipeline[pipe.pipeline]; - bool usesPrintf = false; + const VulkanCreationInfo::ShaderEntry *shaderEntries[NumShaderStages] = {}; for(uint32_t i = 0; i < NumShaderStages; i++) { - if(pipeInfo.shaders[i].stage == ShaderStage::Count) + if(result.compute && (ShaderStage)i != ShaderStage::Compute) + continue; + if(!result.compute && (ShaderStage)i == ShaderStage::Compute) + continue; + if(shaderObject && state.shaderObjects[i] == ResourceId()) + continue; + if(!shaderObject && pipeInfo.shaders[i].stage == ShaderStage::Count) continue; - usesPrintf |= pipeInfo.shaders[i].patchData->usesPrintf; + shaderEntries[i] = shaderObject ? &creationInfo.m_ShaderObject[state.shaderObjects[i]].shad + : &pipeInfo.shaders[i]; + } + + bool usesPrintf = false; + + for(uint32_t i = 0; i < NumShaderStages; i++) + { + if(shaderEntries[i]) + usesPrintf |= shaderEntries[i]->patchData->usesPrintf; } - const bool hasGeomOrMesh = pipeInfo.shaders[(size_t)ShaderStage::Geometry].module != ResourceId() || - pipeInfo.shaders[(size_t)ShaderStage::Mesh].module != ResourceId(); + const bool hasGeomOrMesh = + shaderEntries[(size_t)ShaderStage::Geometry] || shaderEntries[(size_t)ShaderStage::Mesh]; const bool usePrimitiveID = !hasGeomOrMesh && m_pDriver->GetDeviceEnabledFeatures().geometryShader != VK_FALSE; const bool usesMultiview = - state.GetRenderPass() != ResourceId() + shaderObject ? state.dynamicRendering.viewMask != 0 + : state.GetRenderPass() != ResourceId() ? creationInfo.m_RenderPass[state.GetRenderPass()].subpasses[state.subpass].multiviews.size() > 1 : pipeInfo.viewMask != 0; @@ -1558,32 +1575,55 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId) feedbackData.feedbackStorageSize += 16 + Vulkan_Debug_PrintfBufferSize() + 1024; } - const ShaderReflection *stageRefls[NumShaderStages] = {}; - { const rdcarray &descBufs = state.descBufs; const rdcarray &descSets = (result.compute ? state.compute.descSets : state.graphics.descSets); rdcarray descLayouts; - for(size_t set = 0; set < pipeInfo.descSetLayouts.size(); set++) - descLayouts.push_back(&creationInfo.m_DescSetLayout[pipeInfo.descSetLayouts[set]]); + rdcarray stageDescLayouts[NumShaderStages]; + + if(shaderObject) + { + for(uint32_t stage = 0; stage < NumShaderStages; stage++) + { + if(!shaderEntries[stage]) + continue; - auto processBinding = [this, &descLayouts, &descBufs, &descSets, &feedbackData]( + for(ResourceId layoutid : + creationInfo.m_ShaderObject[state.shaderObjects[stage]].descSetLayouts) + { + stageDescLayouts[stage].push_back(&creationInfo.m_DescSetLayout[layoutid]); + } + } + } + else + { + for(size_t set = 0; set < pipeInfo.descSetLayouts.size(); set++) + { + descLayouts.push_back(&creationInfo.m_DescSetLayout[pipeInfo.descSetLayouts[set]]); + } + } + + auto processBinding = [this, &shaderObject, &descLayouts, &stageDescLayouts, &descBufs, + &descSets, &feedbackData]( ShaderStage stage, DescriptorType type, bool inputAttachment, uint16_t index, uint32_t bindset, uint32_t bind, uint32_t arraySize) { // only process array bindings if(arraySize <= 1) return; + rdcarray &descSetLayouts = + shaderObject ? stageDescLayouts[(uint32_t)stage] : descLayouts; + BindKey key; key.stage = stage; key.index.category = CategoryForDescriptorType(type); key.index.index = index; key.index.arrayElement = 0; - if(bindset >= descLayouts.size() || !descLayouts[bindset] || bindset >= descSets.size() || - !descSets[bindset].IsBound()) + if(bindset >= descSetLayouts.size() || !descSetLayouts[bindset] || + bindset >= descSets.size() || !descSets[bindset].IsBound()) { RDCERR("Invalid set %u referenced by %s shader", bindset, ToStr(key.stage).c_str()); return; @@ -1592,13 +1632,13 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId) // declared resources may not exist in the descriptor set layout, if they do not then we // assume they are not statically used ("A reference in the entry point's interface list does // not constitute a static use") - if(bind >= descLayouts[bindset]->bindings.size()) + if(bind >= descSetLayouts[bindset]->bindings.size()) { return; } // VkShaderStageFlagBits and ShaderStageMask are identical bit-for-bit. - if((descLayouts[bindset]->bindings[bind].stageFlags & + if((descSetLayouts[bindset]->bindings[bind].stageFlags & (VkShaderStageFlags)MaskForStage(key.stage)) == 0) { // this might be deliberate if the binding is never actually used dynamically, only @@ -1618,12 +1658,12 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId) m_pDriver->GetResIDFromAddr(descBufs[descSets[bindset].descBufferIdx].address, id, offs); access.descriptorStore = id; access.byteOffset += uint32_t(offs + descSets[bindset].descBufferOffset) + - descLayouts[bindset]->bindings[bind].elemOffset; - access.byteSize = - GetDescriptorSizeOfBind(m_pDriver->GetResourceManager(), descLayouts[bindset]->bindings, - descLayouts[bindset]->mutableBitmasks, bind); + descSetLayouts[bindset]->bindings[bind].elemOffset; + access.byteSize = GetDescriptorSizeOfBind(m_pDriver->GetResourceManager(), + descSetLayouts[bindset]->bindings, + descSetLayouts[bindset]->mutableBitmasks, bind); - if(descLayouts[bindset]->bindings[bind].variableSize || arraySize == ~0U) + if(descSetLayouts[bindset]->bindings[bind].variableSize || arraySize == ~0U) { arraySize = uint32_t((m_pDriver->m_CreationInfo.m_Buffer[id].size - access.byteOffset) / access.byteSize); @@ -1633,14 +1673,14 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId) { ResourceId descSet = descSets[bindset].descSet; - if(bind >= descLayouts[bindset]->bindings.size()) + if(bind >= descSetLayouts[bindset]->bindings.size()) { RDCERR("Invalid binding %u in set %u referenced by %s shader", bind, bindset, ToStr(key.stage).c_str()); return; } - if(descLayouts[bindset]->bindings[bind].variableSize) + if(descSetLayouts[bindset]->bindings[bind].variableSize) { auto it = m_pDriver->m_DescriptorSetState.find(descSet); if(it != m_pDriver->m_DescriptorSetState.end()) @@ -1649,12 +1689,12 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId) else if(arraySize == ~0U) { // if the array was unbounded, clamp it to the size of the descriptor set - arraySize = descLayouts[bindset]->bindings[bind].descriptorCount; + arraySize = descSetLayouts[bindset]->bindings[bind].descriptorCount; } access.descriptorStore = descSet; - access.byteOffset = - descLayouts[bindset]->bindings[bind].elemOffset + descLayouts[bindset]->inlineByteSize; + access.byteOffset = descSetLayouts[bindset]->bindings[bind].elemOffset + + descSetLayouts[bindset]->inlineByteSize; access.byteSize = 1; } @@ -1663,36 +1703,39 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId) feedbackData.feedbackStorageSize += arraySize * sizeof(uint32_t); }; - for(const VulkanCreationInfo::ShaderEntry &sh : pipeInfo.shaders) + for(const VulkanCreationInfo::ShaderEntry *entry : shaderEntries) { - if(!sh.refl) + if(!entry) + continue; + if(result.compute && entry->stage != ShaderStage::Compute) + continue; + if(!result.compute && entry->stage == ShaderStage::Compute) continue; - stageRefls[(uint32_t)sh.refl->stage] = sh.refl; - - for(uint32_t i = 0; i < sh.refl->constantBlocks.size(); i++) - processBinding(sh.refl->stage, DescriptorType::ConstantBuffer, false, i & 0xffff, - sh.refl->constantBlocks[i].fixedBindSetOrSpace, - sh.refl->constantBlocks[i].fixedBindNumber, - sh.refl->constantBlocks[i].bindArraySize); - - for(uint32_t i = 0; i < sh.refl->samplers.size(); i++) - processBinding(sh.refl->stage, DescriptorType::Sampler, false, i & 0xffff, - sh.refl->samplers[i].fixedBindSetOrSpace, - sh.refl->samplers[i].fixedBindNumber, sh.refl->samplers[i].bindArraySize); - - for(uint32_t i = 0; i < sh.refl->readOnlyResources.size(); i++) - processBinding(sh.refl->stage, sh.refl->readOnlyResources[i].descriptorType, - sh.refl->readOnlyResources[i].isInputAttachment, i & 0xffff, - sh.refl->readOnlyResources[i].fixedBindSetOrSpace, - sh.refl->readOnlyResources[i].fixedBindNumber, - sh.refl->readOnlyResources[i].bindArraySize); - - for(uint32_t i = 0; i < sh.refl->readWriteResources.size(); i++) - processBinding(sh.refl->stage, sh.refl->readWriteResources[i].descriptorType, false, - i & 0xffff, sh.refl->readWriteResources[i].fixedBindSetOrSpace, - sh.refl->readWriteResources[i].fixedBindNumber, - sh.refl->readWriteResources[i].bindArraySize); + for(uint32_t i = 0; i < entry->refl->constantBlocks.size(); i++) + processBinding(entry->refl->stage, DescriptorType::ConstantBuffer, false, i & 0xffff, + entry->refl->constantBlocks[i].fixedBindSetOrSpace, + entry->refl->constantBlocks[i].fixedBindNumber, + entry->refl->constantBlocks[i].bindArraySize); + + for(uint32_t i = 0; i < entry->refl->samplers.size(); i++) + processBinding(entry->refl->stage, DescriptorType::Sampler, false, i & 0xffff, + entry->refl->samplers[i].fixedBindSetOrSpace, + entry->refl->samplers[i].fixedBindNumber, + entry->refl->samplers[i].bindArraySize); + + for(uint32_t i = 0; i < entry->refl->readOnlyResources.size(); i++) + processBinding(entry->refl->stage, entry->refl->readOnlyResources[i].descriptorType, + entry->refl->readOnlyResources[i].isInputAttachment, i & 0xffff, + entry->refl->readOnlyResources[i].fixedBindSetOrSpace, + entry->refl->readOnlyResources[i].fixedBindNumber, + entry->refl->readOnlyResources[i].bindArraySize); + + for(uint32_t i = 0; i < entry->refl->readWriteResources.size(); i++) + processBinding(entry->refl->stage, entry->refl->readWriteResources[i].descriptorType, false, + i & 0xffff, entry->refl->readWriteResources[i].fixedBindSetOrSpace, + entry->refl->readWriteResources[i].fixedBindNumber, + entry->refl->readWriteResources[i].bindArraySize); } } @@ -1757,7 +1800,7 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId) std::map printfData[NumShaderStages]; - auto patchCallback = [this, &printfData, &feedbackData, pipeInfo, maxSlot, usePrimitiveID, + auto patchCallback = [this, &printfData, &feedbackData, &shaderEntries, maxSlot, usePrimitiveID, usesMultiview]( const AddedDescriptorData &patchedBufferdata, VkShaderStageFlagBits stage, const char *entryName, const rdcarray &origSpirv, @@ -1791,14 +1834,14 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId) if(m_pDriver->GetDeviceEnabledFeatures().shaderInt64) { - AnnotateShader(*pipeInfo.shaders[idx].refl, *pipeInfo.shaders[idx].patchData, + AnnotateShader(*shaderEntries[idx]->refl, *shaderEntries[idx]->patchData, ShaderStage(idx), entryName, feedbackData.offsetMap, maxSlot, usePrimitiveID, m_PatchedShaderFeedback.FeedbackBuffer.Address(), m_StorageMode, usesMultiview, modSpirv, printfData[idx]); } else { - AnnotateShader(*pipeInfo.shaders[idx].refl, *pipeInfo.shaders[idx].patchData, + AnnotateShader(*shaderEntries[idx]->refl, *shaderEntries[idx]->patchData, ShaderStage(idx), entryName, feedbackData.offsetMap, maxSlot, usePrimitiveID, m_PatchedShaderFeedback.FeedbackBuffer.Address(), m_StorageMode, usesMultiview, modSpirv, printfData[idx]); @@ -1888,7 +1931,7 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId) msg.stage = stage; - const VulkanCreationInfo::ShaderEntry &sh = pipeInfo.shaders[(uint32_t)stage]; + const VulkanCreationInfo::ShaderEntry &sh = *shaderEntries[(uint32_t)stage]; { VulkanCreationInfo::ShaderModule &mod = creationInfo.m_ShaderModule[sh.module]; @@ -1954,8 +1997,7 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId) msg.location.mesh.thread[2] = meshThread / (sh.refl->dispatchThreadsDimension[0] * sh.refl->dispatchThreadsDimension[1]); - const VulkanCreationInfo::ShaderEntry &tasksh = - pipeInfo.shaders[(uint32_t)ShaderStage::Task]; + const VulkanCreationInfo::ShaderEntry &tasksh = *shaderEntries[(uint32_t)ShaderStage::Task]; if(tasksh.module == ResourceId()) {