From efd60e575131a14380b4b7bda55fac62eb4a83a2 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 22 May 2025 16:47:09 +0100 Subject: [PATCH] Serialise unique descriptor fetches on a per-resource basis * ASs and samplers can only have one descriptor ever - we automatically fetch this at creation time when descriptor buffers are enabled. We also fetch all possible NULL descriptors at device creation and do not re-serialise them afterwards. * Image views may have either 1 descriptor (if layouts are ignored) or N (one per layout). Buffers have N per offset/size and optionally format. These are tracked for uniqueness in the corresponding resource record to ensure we don't serialise infinite descriptors over time. * For combined image/samplers since they are in a known format we serialise these as-if they are image view descriptors only (with some extra data that we ignore). The whole descriptor must still replay bitwise identical but we will only use the image view part of it. --- renderdoc/driver/vulkan/vk_core.cpp | 31 ++ renderdoc/driver/vulkan/vk_core.h | 6 + renderdoc/driver/vulkan/vk_resources.cpp | 4 + renderdoc/driver/vulkan/vk_resources.h | 70 ++++ .../vulkan/wrappers/vk_descriptor_funcs.cpp | 312 +++++++++++++++++- .../vulkan/wrappers/vk_device_funcs.cpp | 141 ++++++++ .../driver/vulkan/wrappers/vk_misc_funcs.cpp | 12 + .../driver/vulkan/wrappers/vk_queue_funcs.cpp | 4 +- .../vulkan/wrappers/vk_resource_funcs.cpp | 22 +- 9 files changed, 598 insertions(+), 4 deletions(-) diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index b425c5bd2..9437e384f 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -5383,6 +5383,37 @@ WrappedVulkan::CommandBufferNode *WrappedVulkan::GetCommandBufferPartialSubmissi return NULL; } +size_t WrappedVulkan::DescriptorDataSize(VkDescriptorType type) +{ + size_t ret = 0; + + const VkPhysicalDeviceDescriptorBufferPropertiesEXT &descSizes = m_DescriptorBufferProperties; + switch(type) + { + case VK_DESCRIPTOR_TYPE_SAMPLER: ret = descSizes.samplerDescriptorSize; break; + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + ret = descSizes.combinedImageSamplerDescriptorSize; + break; + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: ret = descSizes.inputAttachmentDescriptorSize; break; + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: ret = descSizes.sampledImageDescriptorSize; break; + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: ret = descSizes.storageImageDescriptorSize; break; + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + ret = descSizes.uniformTexelBufferDescriptorSize; + break; + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: + ret = descSizes.storageTexelBufferDescriptorSize; + break; + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: ret = descSizes.uniformBufferDescriptorSize; break; + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: ret = descSizes.storageBufferDescriptorSize; break; + case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: + ret = descSizes.accelerationStructureDescriptorSize; + break; + default: break; + } + + return ret; +} + bool WrappedVulkan::IsPartialRenderPassActive() { for(const CommandBufferNode &cmdNode : m_Partial.partialStack) diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index 707a01fcf..552f00237 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -447,9 +447,15 @@ private: VkQueueFamilyProperties queueProps[16] = {}; }; + VkPhysicalDeviceDescriptorBufferPropertiesEXT m_DescriptorBufferProperties; + size_t DescriptorDataSize(VkDescriptorType type); + Threading::CriticalSection m_ASLookupByAddrLock; rdcflatmap m_ASLookupByAddr; + bool m_NULLDescriptorPatternSaved = false; + bool m_IgnoreLayoutForDescriptors = false; + bool m_SeparateDepthStencil = false; bool m_NULLDescriptorsAllowed = false; bool m_ExtendedDynState = false; diff --git a/renderdoc/driver/vulkan/vk_resources.cpp b/renderdoc/driver/vulkan/vk_resources.cpp index c61f2063f..5d317cab1 100644 --- a/renderdoc/driver/vulkan/vk_resources.cpp +++ b/renderdoc/driver/vulkan/vk_resources.cpp @@ -3973,6 +3973,10 @@ VkResourceRecord::~VkResourceRecord() if(resType == eResBuffer || resType == eResImage) SAFE_DELETE(resInfo); + // for image views with descriptors, they have a duplicated resInfo + if(resType == eResImageView && resInfo && resInfo->parentResInfo != resInfo) + SAFE_DELETE(resInfo); + if(resType == eResInstance || resType == eResDevice || resType == eResPhysicalDevice) SAFE_DELETE(instDevInfo); diff --git a/renderdoc/driver/vulkan/vk_resources.h b/renderdoc/driver/vulkan/vk_resources.h index 9b93ec3fc..16f2844fb 100644 --- a/renderdoc/driver/vulkan/vk_resources.h +++ b/renderdoc/driver/vulkan/vk_resources.h @@ -1017,15 +1017,83 @@ struct AspectSparseTable DECLARE_REFLECTION_STRUCT(AspectSparseTable); +struct DescriptorUniquenessKey +{ + DescriptorUniquenessKey(VkImageLayout layout) + : layout(layout), offset(0), size(0), fmt(VK_FORMAT_UNDEFINED) + { + } + DescriptorUniquenessKey(uint64_t offset, uint64_t size, VkFormat fmt) + : layout(VK_IMAGE_LAYOUT_UNDEFINED), offset(offset), size(size), fmt(fmt) + { + } + + bool operator==(const DescriptorUniquenessKey &key) const + { + return layout == key.layout && offset == key.offset && size == key.size && fmt == key.fmt; + } + + VkImageLayout layout; + uint64_t offset, size; + VkFormat fmt; +}; + +namespace std +{ +template <> +struct hash +{ + std::size_t operator()(const DescriptorUniquenessKey &key) const + { + std::size_t hash = std::hash()(key.layout); + hash ^= std::hash()(key.offset) + 0x9e3779b9 + (hash << 6) + (hash >> 2); + hash ^= std::hash()(key.size) + 0x9e3779b9 + (hash << 6) + (hash >> 2); + hash ^= std::hash()(key.fmt) + 0x9e3779b9 + (hash << 6) + (hash >> 2); + return hash; + } +}; +}; + // these structs are allocated for images and buffers, then pointed to (non-owning) by views struct ResourceInfo { + ResourceInfo() = default; + ResourceInfo(const ResourceInfo &other) + : banDedicated(other.banDedicated), + sparseAspect(other.sparseAspect), + dedicatedMemory(other.dedicatedMemory), + memreqs(other.memreqs), + imageInfo(other.imageInfo) + { + // this constructor is used to duplicate an image's resinfo in an image view when descriptor + // buffers are in use so that each view can have its own descriptor tracking. + + // sparse table info deliberately not copied as it is mutable, and obviously the same for the descriptors + } + // commonly we expect only one aspect (COLOR is vastly likely and METADATA is rare) so have one // directly accessible. If we have others (like separate DEPTH and STENCIL, or anything and // METADATA) we put them in the array. Sparse::PageTable sparseTable; rdcarray altSparseAspects; + // for images this is a self pointer. For image views when we are using descriptor buffers we + // can't alias the resInfo in each view's record because each view needs to track its own + // descriptors - instead we point back to the image's resInfo here for sparse info. blech + ResourceInfo *parentResInfo; + + // for buffers, or for images when layouts can affect descriptors, the set of descriptors which + // have already been serialised + Threading::CriticalSection descriptorLock; + std::unordered_set descriptors; + + // return true if this is a new descriptor, false if we already had it + bool AddDescriptor(DescriptorUniquenessKey key) + { + SCOPED_LOCK(descriptorLock); + return descriptors.insert(key).second; + } + // for external images if we query both external and non-external and the sizes are different, we // can't allow dedicated memory as it is required to precisely match in size. bool banDedicated = false; @@ -2329,6 +2397,8 @@ public: bool storable = false; bool dedicated = false; bool hasBDA = false; + bool hasDescriptorSaved = false; + bool hasNULLDescriptorSaved = false; void MarkMemoryFrameReferenced(ResourceId mem, VkDeviceSize offset, VkDeviceSize size, FrameRefType refType); diff --git a/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp index 7060a5de4..4ac31876f 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp @@ -199,6 +199,83 @@ VkCopyDescriptorSet WrappedVulkan::UnwrapInfo(const VkCopyDescriptorSet *copyDes return ret; } +template <> +VkDescriptorGetInfoEXT WrappedVulkan::UnwrapInfo(const VkDescriptorGetInfoEXT *pDescriptorInfo) +{ + VkDescriptorGetInfoEXT ret = *pDescriptorInfo; + + byte *memory = GetTempMemory(sizeof(VkDescriptorAddressInfoEXT) + GetNextPatchSize(ret.pNext)); + RDCCOMPILE_ASSERT(sizeof(VkDescriptorAddressInfoEXT) >= sizeof(VkDescriptorImageInfo), + "Structure sizes mean not enough space is allocated for write data"); + + if(pDescriptorInfo->data.pUniformBuffer) + ret.data.pUniformBuffer = (VkDescriptorAddressInfoEXT *)memory; + + byte *nextMem = memory + sizeof(VkDescriptorBufferInfo); + + UnwrapNextChain(m_State, "VkDescriptorGetInfoEXT", nextMem, (VkBaseInStructure *)&ret); + + switch(ret.type) + { + case VK_DESCRIPTOR_TYPE_SAMPLER: + { + VkSampler *samp = (VkSampler *)memory; + if(pDescriptorInfo->data.pSampler) + { + *samp = Unwrap(*pDescriptorInfo->data.pSampler); + } + break; + } + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + // ignore the sampler part + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + { + VkDescriptorImageInfo *img = (VkDescriptorImageInfo *)ret.data.pCombinedImageSampler; + if(pDescriptorInfo->data.pCombinedImageSampler) + { + img->imageView = Unwrap(pDescriptorInfo->data.pCombinedImageSampler->imageView); + img->sampler = Unwrap(pDescriptorInfo->data.pCombinedImageSampler->sampler); + img->imageLayout = pDescriptorInfo->data.pCombinedImageSampler->imageLayout; + } + break; + } + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: + { + // no unwrap, just copy + VkDescriptorAddressInfoEXT *buf = (VkDescriptorAddressInfoEXT *)ret.data.pUniformBuffer; + if(pDescriptorInfo->data.pUniformBuffer) + *buf = *pDescriptorInfo->data.pUniformBuffer; + break; + } + case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: + { + // no unwrap, just copy + ret.data.accelerationStructure = pDescriptorInfo->data.accelerationStructure; + break; + } + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK: + case VK_DESCRIPTOR_TYPE_MUTABLE_EXT: + case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV: + case VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM: + case VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM: + case VK_DESCRIPTOR_TYPE_PARTITIONED_ACCELERATION_STRUCTURE_NV: + case VK_DESCRIPTOR_TYPE_MAX_ENUM: + { + RDCERR("Invalid descriptor type %s", ToStr(ret.type).c_str()); + break; + } + } + + return ret; +} + template bool WrappedVulkan::Serialise_vkCreateDescriptorPool(SerialiserType &ser, VkDevice device, const VkDescriptorPoolCreateInfo *pCreateInfo, @@ -1873,14 +1950,247 @@ void WrappedVulkan::vkUpdateDescriptorSetWithTemplate( template bool WrappedVulkan::Serialise_vkGetDescriptorEXT(SerialiserType &ser, VkDevice device, const VkDescriptorGetInfoEXT *pDescriptorInfo, - size_t dataSize, void *pDescriptor) + size_t dataSize_, void *pDescriptor) { + SERIALISE_ELEMENT(device); + SERIALISE_ELEMENT_LOCAL(DescriptorInfo, *pDescriptorInfo).Important(); + SERIALISE_ELEMENT_LOCAL(dataSize, uint64_t(dataSize_)); + SERIALISE_ELEMENT_ARRAY(pDescriptor, dataSize_); + + SERIALISE_CHECK_READ_ERRORS(); + + if(IsReplayingAndReading()) + { + byte *tempMem = GetTempMemory(GetNextPatchSize(&DescriptorInfo)); + VkDescriptorGetInfoEXT *unwrappedInfo = UnwrapStructAndChain(m_State, tempMem, &DescriptorInfo); + + uint64_t curDataSize = DescriptorDataSize(DescriptorInfo.type); + if(dataSize != curDataSize) + { + SET_ERROR_RESULT( + m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Descriptor of type %s changed size, it was %llu bytes during capture but is %llu bytes " + "during replay.\n" + "\n%s", + ToStr(DescriptorInfo.type).c_str(), dataSize, curDataSize, + GetPhysDeviceCompatString(false, false).c_str()); + return false; + } + + bytebuf replayDescriptor; + replayDescriptor.resize(dataSize); + + // verify the descriptor is bitwise identical + ObjDisp(device)->GetDescriptorEXT(Unwrap(device), unwrappedInfo, (size_t)dataSize, + replayDescriptor.data()); + + if(memcmp(replayDescriptor.data(), pDescriptor, dataSize) != 0) + { + rdcstr bitDifferences; + + uint32_t *capU32 = (uint32_t *)pDescriptor; + uint32_t *replayU32 = (uint32_t *)replayDescriptor.data(); + + bitDifferences = "Capture:\n"; + for(uint32_t d = 0; d * 4 < dataSize; d++) + bitDifferences += StringFormat::Fmt("%08llx ", capU32[d]); + bitDifferences += "\n\n"; + bitDifferences += "Replay:\n"; + for(uint32_t d = 0; d * 4 < dataSize; d++) + bitDifferences += StringFormat::Fmt("%08llx ", replayU32[d]); + bitDifferences += "\n"; + + SET_ERROR_RESULT(m_FailedReplayResult, ResultCode::APIHardwareUnsupported, + "Descriptor of type %s changed bit pattern.\n" + "\n%s" + "\n%s", + ToStr(DescriptorInfo.type).c_str(), bitDifferences.c_str(), + GetPhysDeviceCompatString(false, false).c_str()); + return false; + } + } + return true; } void WrappedVulkan::vkGetDescriptorEXT(VkDevice device, const VkDescriptorGetInfoEXT *pDescriptorInfo, size_t dataSize, void *pDescriptor) { + // the user *could* be querying straight into GPU upload memory which would be very bad to read + // back from. To avoid that, we read into our temporary memory then memcpy to user memory but only + // if we're actually going to process this. + + // first determine where this should go, based on the 'primary' resource. For combined + // image/samplers, we pre-populated the sampler one so we treat them as-if they're just images + VkResourceRecord *dstRecord = NULL; + + if(IsCaptureMode(m_State)) + { + switch(pDescriptorInfo->type) + { + case VK_DESCRIPTOR_TYPE_SAMPLER: + { + if(pDescriptorInfo->data.pSampler) + { + dstRecord = GetRecord(*pDescriptorInfo->data.pSampler); + // don't need to worry about the race here, worst case we save an extra descriptor + if(dstRecord->hasDescriptorSaved) + dstRecord = NULL; + else + dstRecord->hasDescriptorSaved = true; + } + else + { + dstRecord = GetRecord(m_Device); + if(m_NULLDescriptorPatternSaved) + dstRecord = NULL; + } + break; + } + case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: + { + // sampled/storage/input attachment are identical in the union. Since the type forms part of + // our this logic can be done in common + if(pDescriptorInfo->data.pSampledImage && + pDescriptorInfo->data.pSampledImage->imageView != VK_NULL_HANDLE) + { + dstRecord = GetRecord(pDescriptorInfo->data.pSampledImage->imageView); + + DescriptorUniquenessKey descKey( + m_IgnoreLayoutForDescriptors + ? VK_IMAGE_LAYOUT_UNDEFINED + : pDescriptorInfo->data.pCombinedImageSampler->imageLayout); + + // this is internally locked + if(!dstRecord->resInfo->AddDescriptor(descKey)) + dstRecord = NULL; + } + else if(pDescriptorInfo->data.pCombinedImageSampler && + pDescriptorInfo->data.pCombinedImageSampler->imageView == VK_NULL_HANDLE && + pDescriptorInfo->data.pCombinedImageSampler->sampler != VK_NULL_HANDLE) + { + dstRecord = GetRecord(pDescriptorInfo->data.pCombinedImageSampler->sampler); + // don't need to worry about the race here, worst case we save an extra descriptor + if(dstRecord->hasNULLDescriptorSaved) + dstRecord = NULL; + else + dstRecord->hasNULLDescriptorSaved = true; + } + else + { + dstRecord = GetRecord(m_Device); + if(m_NULLDescriptorPatternSaved) + dstRecord = NULL; + } + break; + } + case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: + { + // uniform/storage are identical in the union. Since the type forms part of our this + // logic can be done in common + if(pDescriptorInfo->data.pUniformBuffer) + { + ResourceId id; + uint64_t offs = 0; + GetResIDFromAddr(pDescriptorInfo->data.pUniformBuffer->address, id, offs); + + dstRecord = GetResourceManager()->GetResourceRecord(id); + + VkFormat fmt = VK_FORMAT_UNDEFINED; + if(pDescriptorInfo->type == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER || + pDescriptorInfo->type == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) + { + fmt = pDescriptorInfo->data.pUniformTexelBuffer->format; + } + + DescriptorUniquenessKey descKey(offs, pDescriptorInfo->data.pUniformBuffer->range, fmt); + + // this is internally locked + if(!dstRecord->resInfo->AddDescriptor(descKey)) + dstRecord = NULL; + } + else + { + dstRecord = GetRecord(m_Device); + if(m_NULLDescriptorPatternSaved) + dstRecord = NULL; + } + break; + } + case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: + { + if(pDescriptorInfo->data.accelerationStructure) + { + ResourceId id; + { + SCOPED_LOCK(m_ASLookupByAddrLock); + id = m_ASLookupByAddr[pDescriptorInfo->data.accelerationStructure]; + } + + dstRecord = GetResourceManager()->GetResourceRecord(id); + // don't need to worry about the race here, worst case we save an extra descriptor + if(dstRecord->hasDescriptorSaved) + dstRecord = NULL; + else + dstRecord->hasDescriptorSaved = true; + } + else + { + dstRecord = GetRecord(m_Device); + if(m_NULLDescriptorPatternSaved) + dstRecord = NULL; + } + break; + } + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: + case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK: + case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV: + case VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM: + case VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM: + case VK_DESCRIPTOR_TYPE_MUTABLE_EXT: + case VK_DESCRIPTOR_TYPE_PARTITIONED_ACCELERATION_STRUCTURE_NV: + case VK_DESCRIPTOR_TYPE_MAX_ENUM: + RDCERR("Invalid descriptor type passed to vkGetDescriptorEXT"); + break; + } + } + + size_t localSize = 0; + if(dstRecord) + localSize = dataSize; + + byte tempMem[256] = {}; + VkDescriptorGetInfoEXT unwrappedInfo = UnwrapInfo(pDescriptorInfo); + + SERIALISE_TIME_CALL(ObjDisp(device)->GetDescriptorEXT(Unwrap(device), &unwrappedInfo, dataSize, + dstRecord ? tempMem : pDescriptor)); + + // if we needed to serialise this descriptor + if(dstRecord) + { + // copy to the user's memory + memcpy(pDescriptor, tempMem, dataSize); + + Chunk *chunk = NULL; + + { + CACHE_THREAD_SERIALISER(); + + SCOPED_SERIALISE_CHUNK(VulkanChunk::vkGetDescriptorEXT); + Serialise_vkGetDescriptorEXT(ser, device, pDescriptorInfo, dataSize, tempMem); + + chunk = scope.Get(); + } + + dstRecord->AddChunk(chunk); + } } INSTANTIATE_FUNCTION_SERIALISED(VkResult, vkCreateDescriptorSetLayout, VkDevice device, diff --git a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp index 46c88ca5d..1de93a240 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp @@ -4291,6 +4291,53 @@ bool WrappedVulkan::Serialise_vkCreateDevice(SerialiserType &ser, VkPhysicalDevi if(m_EnabledExtensions.ext_EXT_descriptor_buffer && descBufFeats && descBufFeats->descriptorBuffer) { m_DescriptorBuffers = true; + + // if any update after bind feature is enabled, check robustBufferAccessUpdateAfterBind + m_DescriptorBufferProperties = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT, + }; + + VkPhysicalDeviceProperties2 availBase = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2}; + availBase.pNext = &m_DescriptorBufferProperties; + ObjDisp(physicalDevice)->GetPhysicalDeviceProperties2(Unwrap(physicalDevice), &availBase); + + bool robustness = false; + + // VkPhysicalDeviceFeatures2 takes priority + if(enabledFeatures2) + { + robustness = enabledFeatures2->features.robustBufferAccess != VK_FALSE; + } + else if(createInfo.pEnabledFeatures && createInfo.pEnabledFeatures->robustBufferAccess) + { + robustness = true; + } + + // we should have forced on robustness + RDCASSERT(robustness); + + m_DescriptorBufferProperties.uniformBufferDescriptorSize = + m_DescriptorBufferProperties.robustUniformBufferDescriptorSize; + m_DescriptorBufferProperties.storageBufferDescriptorSize = + m_DescriptorBufferProperties.robustStorageBufferDescriptorSize; + m_DescriptorBufferProperties.uniformTexelBufferDescriptorSize = + m_DescriptorBufferProperties.robustUniformTexelBufferDescriptorSize; + m_DescriptorBufferProperties.storageTexelBufferDescriptorSize = + m_DescriptorBufferProperties.robustStorageTexelBufferDescriptorSize; + + // ensure our fixed-size is large enough for this driver. + RDCASSERT(m_DescriptorBufferProperties.accelerationStructureCaptureReplayDescriptorDataSize <= + FixedOpaqueDescriptorCaptureSize); + RDCASSERT(m_DescriptorBufferProperties.bufferCaptureReplayDescriptorDataSize <= + FixedOpaqueDescriptorCaptureSize); + RDCASSERT(m_DescriptorBufferProperties.imageCaptureReplayDescriptorDataSize <= + FixedOpaqueDescriptorCaptureSize); + RDCASSERT(m_DescriptorBufferProperties.imageViewCaptureReplayDescriptorDataSize <= + FixedOpaqueDescriptorCaptureSize); + RDCASSERT(m_DescriptorBufferProperties.samplerCaptureReplayDescriptorDataSize <= + FixedOpaqueDescriptorCaptureSize); + + m_IgnoreLayoutForDescriptors = descBufFeats->descriptorBufferImageLayoutIgnored != VK_FALSE; } // MoltenVK reports 0x3fffffff for this limit so just ignore that value if it comes up @@ -4877,6 +4924,100 @@ VkResult WrappedVulkan::vkCreateDevice(VkPhysicalDevice physicalDevice, descBufFeatures->descriptorBuffer) { m_DescriptorBuffers = true; + + // if any update after bind feature is enabled, check robustBufferAccessUpdateAfterBind + m_DescriptorBufferProperties = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT, + }; + + VkPhysicalDeviceProperties2 availBase = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2}; + availBase.pNext = &m_DescriptorBufferProperties; + ObjDisp(physicalDevice)->GetPhysicalDeviceProperties2(Unwrap(physicalDevice), &availBase); + + bool robustness = false; + + // VkPhysicalDeviceFeatures2 takes priority + if(enabledFeatures2) + { + robustness = enabledFeatures2->features.robustBufferAccess != VK_FALSE; + } + else if(createInfo.pEnabledFeatures && createInfo.pEnabledFeatures->robustBufferAccess) + { + robustness = true; + } + + // we should have forced on robustness + RDCASSERT(robustness); + + m_DescriptorBufferProperties.uniformBufferDescriptorSize = + m_DescriptorBufferProperties.robustUniformBufferDescriptorSize; + m_DescriptorBufferProperties.storageBufferDescriptorSize = + m_DescriptorBufferProperties.robustStorageBufferDescriptorSize; + m_DescriptorBufferProperties.uniformTexelBufferDescriptorSize = + m_DescriptorBufferProperties.robustUniformTexelBufferDescriptorSize; + m_DescriptorBufferProperties.storageTexelBufferDescriptorSize = + m_DescriptorBufferProperties.robustStorageTexelBufferDescriptorSize; + + // ensure our fixed-size is large enough for this driver. + RDCASSERT(m_DescriptorBufferProperties.accelerationStructureCaptureReplayDescriptorDataSize <= + FixedOpaqueDescriptorCaptureSize); + RDCASSERT(m_DescriptorBufferProperties.bufferCaptureReplayDescriptorDataSize <= + FixedOpaqueDescriptorCaptureSize); + RDCASSERT(m_DescriptorBufferProperties.imageCaptureReplayDescriptorDataSize <= + FixedOpaqueDescriptorCaptureSize); + RDCASSERT(m_DescriptorBufferProperties.imageViewCaptureReplayDescriptorDataSize <= + FixedOpaqueDescriptorCaptureSize); + RDCASSERT(m_DescriptorBufferProperties.samplerCaptureReplayDescriptorDataSize <= + FixedOpaqueDescriptorCaptureSize); + + m_IgnoreLayoutForDescriptors = descBufFeatures->descriptorBufferImageLayoutIgnored != VK_FALSE; + + VkPhysicalDeviceRobustness2FeaturesKHR *robustness2 = + (VkPhysicalDeviceRobustness2FeaturesKHR *)FindNextStruct( + &createInfo, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_KHR); + + // record all the NULL descriptors + if(IsCaptureMode(m_State) && robustness2 && robustness2->nullDescriptor) + { + VkResourceRecord *record = GetRecord(*pDevice); + RDCASSERT(record); + + VkDescriptorGetInfoEXT getInfo = { + VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT, + }; + + rdcarray> descriptorTypes = { + {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + m_DescriptorBufferProperties.sampledImageDescriptorSize}, + {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + m_DescriptorBufferProperties.storageImageDescriptorSize}, + {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, + m_DescriptorBufferProperties.robustUniformTexelBufferDescriptorSize}, + {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, + m_DescriptorBufferProperties.robustStorageTexelBufferDescriptorSize}, + {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + m_DescriptorBufferProperties.robustUniformBufferDescriptorSize}, + {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + m_DescriptorBufferProperties.robustStorageBufferDescriptorSize}, + }; + + if(m_AccelerationStructures) + { + descriptorTypes.push_back( + {VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, + m_DescriptorBufferProperties.accelerationStructureDescriptorSize}); + } + + byte dummy[256]; + for(rdcpair typeAndSize : descriptorTypes) + { + getInfo.type = typeAndSize.first; + + vkGetDescriptorEXT(*pDevice, &getInfo, typeAndSize.second, dummy); + } + } + + m_NULLDescriptorPatternSaved = true; } m_PhysicalDeviceData.driverInfo = diff --git a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp index 48c0f5d35..6b201a926 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_misc_funcs.cpp @@ -731,6 +731,18 @@ VkResult WrappedVulkan::vkCreateSampler(VkDevice device, const VkSamplerCreateIn VkResourceRecord *ycbcrRecord = GetRecord(ycbcr->conversion); record->AddParent(ycbcrRecord); } + + if(m_DescriptorBuffers) + { + VkDescriptorGetInfoEXT getDesc = { + VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT, + }; + byte dummy[256]; + getDesc.type = VK_DESCRIPTOR_TYPE_SAMPLER; + getDesc.data.pSampler = pSampler; + vkGetDescriptorEXT(device, &getDesc, m_DescriptorBufferProperties.samplerDescriptorSize, + dummy); + } } else { diff --git a/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp index 9e4bbc803..2602c60aa 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp @@ -1118,7 +1118,9 @@ void WrappedVulkan::CaptureQueueSubmit(VkQueue queue, for(auto refit = refs.sparseRefs.begin(); refit != refs.sparseRefs.end(); ++refit) { - GetResourceManager()->MarkSparseMapReferenced((*refit)->resInfo); + // for these which are added as image views, step up to the image (if it's not an identity + // pointer) - see declaration of parentResInfo + GetResourceManager()->MarkSparseMapReferenced((*refit)->resInfo->parentResInfo); } UpdateImageStates(refs.bindImageStates); diff --git a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp index 74b6437f0..c58bed248 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp @@ -2083,7 +2083,7 @@ VkResult WrappedVulkan::vkCreateBuffer(VkDevice device, const VkBufferCreateInfo record->resInfo = NULL; - if(isSparse || isExternal) + if(isSparse || isExternal || m_DescriptorBuffers) { record->resInfo = new ResourceInfo(); @@ -2686,6 +2686,7 @@ VkResult WrappedVulkan::vkCreateImage(VkDevice device, const VkImageCreateInfo * record->AddChunk(chunk); record->resInfo = new ResourceInfo(); + record->resInfo->parentResInfo = record->resInfo; ResourceInfo &resInfo = *record->resInfo; resInfo.imageInfo = ImageInfo(*pCreateInfo); @@ -3067,7 +3068,15 @@ VkResult WrappedVulkan::vkCreateImageView(VkDevice device, const VkImageViewCrea record->baseResource = imageRecord->GetResourceID(); record->baseResourceMem = imageRecord->baseResourceMem; record->dedicated = imageRecord->dedicated; - record->resInfo = imageRecord->resInfo; + if(m_DescriptorBuffers) + { + record->resInfo = new ResourceInfo(*imageRecord->resInfo); + record->resInfo->parentResInfo = imageRecord->resInfo; + } + else + { + record->resInfo = imageRecord->resInfo; + } record->viewRange = pCreateInfo->subresourceRange; record->viewRange.setViewType(pCreateInfo->viewType); } @@ -3677,6 +3686,15 @@ VkResult WrappedVulkan::vkCreateAccelerationStructureKHR( SCOPED_LOCK(m_ASLookupByAddrLock); m_ASLookupByAddr[addr] = id; } + + VkDescriptorGetInfoEXT getDesc = { + VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT, + }; + byte dummy[256]; + getDesc.type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; + getDesc.data.accelerationStructure = addr; + vkGetDescriptorEXT(device, &getDesc, + m_DescriptorBufferProperties.accelerationStructureDescriptorSize, dummy); } } else