diff --git a/renderdoc/driver/vulkan/vk_bindless_feedback.cpp b/renderdoc/driver/vulkan/vk_bindless_feedback.cpp index 62d1a2d7c..f96b01fec 100644 --- a/renderdoc/driver/vulkan/vk_bindless_feedback.cpp +++ b/renderdoc/driver/vulkan/vk_bindless_feedback.cpp @@ -42,8 +42,8 @@ struct feedbackData }; void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName, - const std::map &offsetMap, VkDeviceAddress addr, - bool bufferAddressKHR, rdcarray &modSpirv) + const std::map &offsetMap, uint32_t maxSlot, + VkDeviceAddress addr, bool bufferAddressKHR, rdcarray &modSpirv) { rdcspv::Editor editor(modSpirv); @@ -51,6 +51,12 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName, const bool useBufferAddress = (addr != 0); + const uint32_t targetIndexWidth = useBufferAddress ? 64 : 32; + + // store the maximum slot we can use, for clamping outputs to avoid writing out of bounds + rdcspv::Id maxSlotID = useBufferAddress ? editor.AddConstantImmediate(maxSlot) + : editor.AddConstantImmediate(maxSlot); + rdcspv::Id uint32ID = editor.DeclareType(rdcspv::scalar()); rdcspv::Id int32ID = editor.DeclareType(rdcspv::scalar()); rdcspv::Id uint64ID, int64ID; @@ -409,7 +415,6 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName, it++; // upcast the index to uint32 or uint64 depending on which path we're taking - uint32_t targetIndexWidth = useBufferAddress ? 64 : 32; { rdcspv::Id indexType = editor.GetIDType(index); @@ -457,6 +462,27 @@ void AnnotateShader(const SPIRVPatchData &patchData, const char *entryName, } } + // clamp the index to the maximum slot. If the user is reading out of bounds, don't write + // out of bounds. + { + rdcspv::Id glsl450 = editor.ImportExtInst("GLSL.std.450"); + + rdcspv::Id clampedtype = + editor.DeclareType(rdcspv::Scalar(rdcspv::Op::TypeInt, targetIndexWidth, false)); + rdcspv::Id clampedindex = editor.MakeId(); + + editor.AddOperation( + it, rdcspv::Operation( + rdcspv::Op::ExtInst, + { + clampedtype.value(), clampedindex.value(), glsl450.value(), + (uint32_t)rdcspv::GLSLstd450::UMin, index.value(), maxSlotID.value(), + })); + it++; + + index = clampedindex; + } + rdcspv::Id bufptr; if(useBufferAddress) @@ -563,6 +589,10 @@ void VulkanReplay::FetchShaderFeedback(uint32_t eventId) std::map offsetMap; + // reserve some space at the start for a general counter indicating that successful data was + // written. + feedbackStorageSize += 16; + { const rdcarray &descSetLayoutIds = creationInfo.m_PipelineLayout[pipeInfo.layout].descSetLayouts; @@ -596,6 +626,11 @@ void VulkanReplay::FetchShaderFeedback(uint32_t eventId) } } + uint32_t maxSlot = uint32_t(feedbackStorageSize / sizeof(uint32_t)); + + // add some extra padding just in case of out-of-bounds writes + feedbackStorageSize += 128; + // if we don't have any array descriptors to feedback then just return now if(offsetMap.empty()) return; @@ -723,7 +758,7 @@ void VulkanReplay::FetchShaderFeedback(uint32_t eventId) rdcarray modSpirv = moduleInfo.spirv.GetSPIRV(); - AnnotateShader(*pipeInfo.shaders[5].patchData, stage.pName, offsetMap, bufferAddress, + AnnotateShader(*pipeInfo.shaders[5].patchData, stage.pName, offsetMap, maxSlot, bufferAddress, useBufferAddressKHR, modSpirv); moduleCreateInfo.pCode = modSpirv.data(); @@ -748,8 +783,8 @@ void VulkanReplay::FetchShaderFeedback(uint32_t eventId) rdcarray modSpirv = moduleInfo.spirv.GetSPIRV(); - AnnotateShader(*pipeInfo.shaders[idx].patchData, stage.pName, offsetMap, bufferAddress, - useBufferAddressKHR, modSpirv); + AnnotateShader(*pipeInfo.shaders[idx].patchData, stage.pName, offsetMap, maxSlot, + bufferAddress, useBufferAddressKHR, modSpirv); moduleCreateInfo.pCode = modSpirv.data(); moduleCreateInfo.codeSize = modSpirv.size() * sizeof(uint32_t);