diff --git a/renderdoc/driver/vulkan/vk_common.h b/renderdoc/driver/vulkan/vk_common.h index 750064a90..41e260af5 100644 --- a/renderdoc/driver/vulkan/vk_common.h +++ b/renderdoc/driver/vulkan/vk_common.h @@ -917,6 +917,9 @@ DECLARE_REFLECTION_STRUCT(DescriptorSetSlot); constexpr uint64_t FixedOpaqueDescriptorCaptureSize = 16; constexpr uint64_t MaxDescriptorSize = 256; +// used for calculating how much address space to reserve if it's small and we're worried about +// expanding descriptor buffers +constexpr uint32_t ExpectedMaxNumDescriptorBuffers = 100; uint32_t DescriptorDataSize(const VkPhysicalDeviceDescriptorBufferPropertiesEXT &descSizes, VkDescriptorType type); diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 1d393808a..74a50cd35 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -2187,8 +2187,9 @@ VkResult WrappedVulkan::FilterDeviceExtensionProperties(VkPhysicalDevice physDev CHECK_PROP_SIZE(inputAttachmentDescriptorSize, MaxDescriptorSize); CHECK_PROP_SIZE(accelerationStructureDescriptorSize, MaxDescriptorSize); - // we don't expect any world where descriptor buffer is available but descriptor indexing - // doesn't support robust update after bind, but require it anyway as we force robustness on + // we don't expect any world where descriptor buffer is available but descriptor + // indexing doesn't support robust update after bind, but require it anyway as we + // force robustness on VkPhysicalDeviceDescriptorIndexingProperties descIndexingProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES, }; @@ -2206,6 +2207,75 @@ VkResult WrappedVulkan::FilterDeviceExtensionProperties(VkPhysicalDevice physDev return true; } + // calculate the maximum descriptor size according to the spec + size_t maxResourceDescriptorSize = 0; +#define CALC_MAX_SIZE(prop) \ + maxResourceDescriptorSize = RDCMAX(maxResourceDescriptorSize, descProps.prop); + + CALC_MAX_SIZE(storageImageDescriptorSize); + CALC_MAX_SIZE(sampledImageDescriptorSize); + CALC_MAX_SIZE(robustUniformTexelBufferDescriptorSize); + CALC_MAX_SIZE(robustStorageTexelBufferDescriptorSize); + CALC_MAX_SIZE(robustUniformBufferDescriptorSize); + CALC_MAX_SIZE(robustStorageBufferDescriptorSize); + CALC_MAX_SIZE(inputAttachmentDescriptorSize); + CALC_MAX_SIZE(accelerationStructureDescriptorSize); + + // guess worst-case size of a descriptor set with 2 descriptors + VkDeviceSize reservedDescriptorSize = + AlignUp(maxResourceDescriptorSize * 2, descProps.descriptorBufferOffsetAlignment); + + // finally we need to ensure we have enough room to hopefully expand every resource + // descriptor buffer a bit without blowing up available address space. we assume that + // making room for a certain number of buffers is more than enough - anyone making more + // than that is hopefully making buffers that are a much smaller fraction of the address space + + // if the range is so small that we can't shrink the limit to give ourselves room and + // remain legal, that's a problem. We need to be able to expand each buffer by a bit + if(descProps.maxResourceDescriptorBufferRange - reservedDescriptorSize < + ((1 << 20) - (1 << 15)) * maxResourceDescriptorSize) + { + if(!filterWarned) + { + RDCWARN( + "VkPhysicalDeviceDescriptorIndexingProperties buffer range of %llx is too " + "small for maxResourceDescriptorSize %zu, can't support capture of " + "VK_EXT_descriptor_buffer", + descProps.maxResourceDescriptorBufferRange, maxResourceDescriptorSize); + } + return true; + } + + const VkDeviceSize addrSpaceSize = + RDCMIN(descProps.descriptorBufferAddressSpaceSize, + descProps.resourceDescriptorBufferAddressSpaceSize); + + // an example set of close-to-problem limits here would be: 128MB (0x8000000) addr space + // with 64MB resource buffer range (0x4000000) + + // the spec requires that the resource buffer range must be at least enough for ~1 + // million (2^20-2^15 = 1015808) descriptors so as long as that's still satisfied if we + // reduce the max range by a bit, we're fine. In practice most implementations have + // plenty of address space and those that are more constrained have a max range that's + // power-of-two rather than the minimum ~1 million so we have plenty scope to remove. + + // if the address space can't be shrunk by enough for 100 buffers to each have a couple + // of descriptors that's also a problem - this is a heuristic, and it could break if the + // user perfectly subdivided a shrunken address space into 101 buffers as then our + // expansion would cause things to explode. We don't expect that to be a problem though. + if(addrSpaceSize - ExpectedMaxNumDescriptorBuffers * reservedDescriptorSize < (1 << 27)) + { + if(!filterWarned) + { + RDCWARN( + "VkPhysicalDeviceDescriptorIndexingProperties resource address space size of " + "%llx is too small for maxResourceDescriptorSize %zu, can't support capture of " + "VK_EXT_descriptor_buffer", + addrSpaceSize, maxResourceDescriptorSize); + } + return true; + } + // supported and all descriptor sizes are sensible return false; } diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index 92ad02819..8025f7318 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -528,6 +528,7 @@ private: bool m_NULLDescriptorPatternSaved = false; bool m_IgnoreLayoutForDescriptors = false; + uint32_t m_ResourceDescriptorBufferReserveSize = 0; std::unordered_map m_InlineBuffers; bool m_SeparateDepthStencil = false; diff --git a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp index 9ce2ba1dd..4809b6fb6 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp @@ -4341,6 +4341,24 @@ bool WrappedVulkan::Serialise_vkCreateDevice(SerialiserType &ser, VkPhysicalDevi RDCASSERT(m_DescriptorBufferProperties.samplerCaptureReplayDescriptorDataSize <= FixedOpaqueDescriptorCaptureSize); + uint32_t maxResourceDescriptorSize = 0; +#define CALC_MAX_SIZE(prop) \ + maxResourceDescriptorSize = \ + RDCMAX(maxResourceDescriptorSize, (uint32_t)m_DescriptorBufferProperties.prop); + + CALC_MAX_SIZE(storageImageDescriptorSize); + CALC_MAX_SIZE(sampledImageDescriptorSize); + CALC_MAX_SIZE(robustUniformTexelBufferDescriptorSize); + CALC_MAX_SIZE(robustStorageTexelBufferDescriptorSize); + CALC_MAX_SIZE(robustUniformBufferDescriptorSize); + CALC_MAX_SIZE(robustStorageBufferDescriptorSize); + CALC_MAX_SIZE(inputAttachmentDescriptorSize); + CALC_MAX_SIZE(accelerationStructureDescriptorSize); + + m_ResourceDescriptorBufferReserveSize = + AlignUp(maxResourceDescriptorSize * 2, + (uint32_t)m_DescriptorBufferProperties.descriptorBufferOffsetAlignment); + m_IgnoreLayoutForDescriptors = descBufFeats->descriptorBufferImageLayoutIgnored != VK_FALSE; } @@ -5008,6 +5026,24 @@ VkResult WrappedVulkan::vkCreateDevice(VkPhysicalDevice physicalDevice, RDCASSERT(m_DescriptorBufferProperties.samplerCaptureReplayDescriptorDataSize <= FixedOpaqueDescriptorCaptureSize); + uint32_t maxResourceDescriptorSize = 0; +#define CALC_MAX_SIZE(prop) \ + maxResourceDescriptorSize = \ + RDCMAX(maxResourceDescriptorSize, (uint32_t)m_DescriptorBufferProperties.prop); + + CALC_MAX_SIZE(storageImageDescriptorSize); + CALC_MAX_SIZE(sampledImageDescriptorSize); + CALC_MAX_SIZE(robustUniformTexelBufferDescriptorSize); + CALC_MAX_SIZE(robustStorageTexelBufferDescriptorSize); + CALC_MAX_SIZE(robustUniformBufferDescriptorSize); + CALC_MAX_SIZE(robustStorageBufferDescriptorSize); + CALC_MAX_SIZE(inputAttachmentDescriptorSize); + CALC_MAX_SIZE(accelerationStructureDescriptorSize); + + m_ResourceDescriptorBufferReserveSize = + AlignUp(maxResourceDescriptorSize * 2, + (uint32_t)m_DescriptorBufferProperties.descriptorBufferOffsetAlignment); + m_IgnoreLayoutForDescriptors = descBufFeatures->descriptorBufferImageLayoutIgnored != VK_FALSE; VkPhysicalDeviceRobustness2FeaturesKHR *robustness2 = diff --git a/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp index 0befecde4..bb7fdd316 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_get_funcs.cpp @@ -897,6 +897,33 @@ void WrappedVulkan::vkGetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevi descBufferProperties->robustUniformTexelBufferDescriptorSize; descBufferProperties->storageTexelBufferDescriptorSize = descBufferProperties->robustStorageTexelBufferDescriptorSize; + + // we also may need to shrink some range/address space limits to allow us to expand buffers. We + // checked that this should be valid at extension filter time + + // calculate the maximum descriptor size according to the spec + size_t maxResourceDescriptorSize = 0; +#define CALC_MAX_SIZE(prop) \ + maxResourceDescriptorSize = RDCMAX(maxResourceDescriptorSize, descBufferProperties->prop); + + CALC_MAX_SIZE(storageImageDescriptorSize); + CALC_MAX_SIZE(sampledImageDescriptorSize); + CALC_MAX_SIZE(robustUniformTexelBufferDescriptorSize); + CALC_MAX_SIZE(robustStorageTexelBufferDescriptorSize); + CALC_MAX_SIZE(robustUniformBufferDescriptorSize); + CALC_MAX_SIZE(robustStorageBufferDescriptorSize); + CALC_MAX_SIZE(inputAttachmentDescriptorSize); + CALC_MAX_SIZE(accelerationStructureDescriptorSize); + + VkDeviceSize reservedDescriptorSize = AlignUp( + maxResourceDescriptorSize * 2, descBufferProperties->descriptorBufferOffsetAlignment); + + descBufferProperties->maxResourceDescriptorBufferRange -= reservedDescriptorSize; + + descBufferProperties->descriptorBufferAddressSpaceSize -= + ExpectedMaxNumDescriptorBuffers * reservedDescriptorSize; + descBufferProperties->resourceDescriptorBufferAddressSpaceSize -= + ExpectedMaxNumDescriptorBuffers * reservedDescriptorSize; } } diff --git a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp index 4575dbe54..c9a2cc16c 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp @@ -1893,6 +1893,13 @@ bool WrappedVulkan::Serialise_vkCreateBuffer(SerialiserType &ser, VkDevice devic VkBufferCreateInfo patched = CreateInfo; + // inflate all resource descriptor buffers by 2 descriptors, so that we have room for internal + // descriptors wherever they are bound + if(CreateInfo.usage & VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT) + { + patched.size += m_ResourceDescriptorBufferReserveSize; + } + byte *tempMem = GetTempMemory(GetNextPatchSize(patched.pNext)); UnwrapNextChain(m_State, "VkBufferCreateInfo", tempMem, (VkBaseInStructure *)&patched); @@ -1977,6 +1984,13 @@ VkResult WrappedVulkan::vkCreateBuffer(VkDevice device, const VkBufferCreateInfo if(IsCaptureMode(m_State)) adjusted_info.flags |= DefaultBufferCreateFlags(); + // inflate all resource descriptor buffers by 2 descriptors, so that we have room for internal + // descriptors wherever they are bound + if(adjusted_info.usage & VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT) + { + adjusted_info.size += m_ResourceDescriptorBufferReserveSize; + } + SetBufferUsageFlags(&adjusted_info, adjusted_usage); byte *tempMem = GetTempMemory(GetNextPatchSize(adjusted_info.pNext));