From db28189dbefed999fe945804d44d8cdedf7a826c Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 30 Jul 2025 10:55:01 +0100 Subject: [PATCH] Move descriptor lookup code out of core file --- renderdoc/driver/vulkan/vk_core.cpp | 1342 ----------------- .../vulkan/wrappers/vk_descriptor_funcs.cpp | 1342 +++++++++++++++++ 2 files changed, 1342 insertions(+), 1342 deletions(-) diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 4a40b1467..cad599077 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -46,10 +46,6 @@ RDOC_DEBUG_CONFIG(bool, Vulkan_Debug_SingleSubmitFlushing, false, "Every command buffer is submitted and fully flushed to the GPU, to narrow down " "the source of problems."); -RDOC_CONFIG(bool, Vulkan_Debug_UseFastDescriptorLookup, true, - "Use fast pattern-matching lookup to try to identify descriptors before falling back " - "to trie lookup."); - uint64_t VkInitParams::GetSerialiseSize() { // misc bytes and fixed integer members @@ -5521,1344 +5517,6 @@ ResourceId WrappedVulkan::GetASFromAddr(VkDeviceAddress addr) return m_ASLookupByAddr[addr]; } -uint32_t WrappedVulkan::DescriptorDataSize(VkDescriptorType type) -{ - return ::DescriptorDataSize(m_DescriptorBufferProperties, type); -} - -void WrappedVulkan::EstimateDescriptorFormats() -{ - // we want to differentiate the descriptor in as few tests as possible. We don't necessarily care - // if we falsely identify a descriptor format once we've isolated it down to one since worst case - // we'd have to fall back to nothing. - - // create an image first so we can make a buffer on the same memory type and re-use the memory - // allocation for everything. We make it with a format that should be guaranteed supported by all drivers - VkImageCreateInfo imCreateInfo = { - VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - NULL, - VK_IMAGE_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT, - VK_IMAGE_TYPE_2D, - VK_FORMAT_R32G32B32A32_SFLOAT, - {128, 128, 1}, - 1, - 1, - VK_SAMPLE_COUNT_1_BIT, - VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_STORAGE_BIT, - VK_SHARING_MODE_EXCLUSIVE, - VK_IMAGE_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT, - NULL, - VK_IMAGE_LAYOUT_UNDEFINED, - }; - - VkImage image = VK_NULL_HANDLE; - VkResult vkr = ObjDisp(m_Device)->CreateImage(Unwrap(m_Device), &imCreateInfo, NULL, &image); - CHECK_VKR(this, vkr); - - VkMemoryRequirements imgMrq = {0}; - ObjDisp(m_Device)->GetImageMemoryRequirements(Unwrap(m_Device), image, &imgMrq); - - // we make the memory at least 4MB since that's pretty modest still and gives us enough room to - // try different buffer sizes to determine weird swizzling. - const VkDeviceSize size = 0x400000; - - RDCASSERT(size >= imgMrq.size); - - VkMemoryAllocateFlagsInfo memFlags = { - VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO, 0, - VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT | VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT}; - VkMemoryAllocateInfo allocInfo = { - VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - &memFlags, - size, - GetGPULocalMemoryIndex(imgMrq.memoryTypeBits), - }; - - VkDeviceMemory memory = VK_NULL_HANDLE; - vkr = ObjDisp(m_Device)->AllocateMemory(Unwrap(m_Device), &allocInfo, NULL, &memory); - CHECK_VKR(this, vkr); - - // allocate a buffer onto the memory too. We assume that reasonable usage will not exclude the - // image's memory type - - VkBufferCreateInfo bufInfo = { - VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - NULL, - VK_BUFFER_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT | - VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT, - size, - VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | - VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR, - }; - VkBuffer buffer = VK_NULL_HANDLE; - vkr = ObjDisp(m_Device)->CreateBuffer(Unwrap(m_Device), &bufInfo, NULL, &buffer); - CHECK_VKR(this, vkr); - - VkMemoryRequirements bufMrq; - ObjDisp(m_Device)->GetBufferMemoryRequirements(Unwrap(m_Device), buffer, &bufMrq); - if((bufMrq.memoryTypeBits & (1 << allocInfo.memoryTypeIndex)) == 0) - { - RDCERR("Can't detect descriptor types, image memory type can't bind buffer"); - ObjDisp(m_Device)->FreeMemory(Unwrap(m_Device), memory, NULL); - ObjDisp(m_Device)->DestroyImage(Unwrap(m_Device), image, NULL); - ObjDisp(m_Device)->DestroyBuffer(Unwrap(m_Device), buffer, NULL); - return; - } - - ObjDisp(m_Device)->BindBufferMemory(Unwrap(m_Device), buffer, memory, 0); - ObjDisp(m_Device)->BindImageMemory(Unwrap(m_Device), image, memory, 0); - - // create image view - VkImageView imageView = VK_NULL_HANDLE; - - VkImageViewCreateInfo imgViewInfo = { - VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - NULL, - VK_IMAGE_VIEW_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT, - image, - VK_IMAGE_VIEW_TYPE_2D, - VK_FORMAT_R32G32B32A32_SFLOAT, - {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_IDENTITY}, - { - VK_IMAGE_ASPECT_COLOR_BIT, - 0, - VK_REMAINING_MIP_LEVELS, - 0, - 1, - }, - }; - - vkr = ObjDisp(m_Device)->CreateImageView(Unwrap(m_Device), &imgViewInfo, NULL, &imageView); - CHECK_VKR(this, vkr); - - // make a couple of samplers also to be able to decode combined image/sampler layouts - VkSamplerCreateInfo sampInfo = {VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; - sampInfo.minFilter = sampInfo.magFilter = VK_FILTER_NEAREST; - sampInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; - sampInfo.addressModeU = sampInfo.addressModeV = sampInfo.addressModeW = - VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - sampInfo.mipLodBias = 1.6f; - sampInfo.flags = VK_SAMPLER_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT; - - VkSampler sampler; - vkr = ObjDisp(m_Device)->CreateSampler(Unwrap(m_Device), &sampInfo, NULL, &sampler); - CHECK_VKR(this, vkr); - - VkSampler altSampler; - sampInfo.minFilter = sampInfo.magFilter = VK_FILTER_LINEAR; - sampInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - sampInfo.addressModeU = sampInfo.addressModeV = sampInfo.addressModeW = - VK_SAMPLER_ADDRESS_MODE_REPEAT; - sampInfo.mipLodBias = -1.6f; - vkr = ObjDisp(m_Device)->CreateSampler(Unwrap(m_Device), &sampInfo, NULL, &altSampler); - CHECK_VKR(this, vkr); - - VkBufferDeviceAddressInfo getInfo = { - VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, - NULL, - buffer, - }; - VkDeviceAddress addr = ObjDisp(m_Device)->GetBufferDeviceAddress(Unwrap(m_Device), &getInfo); - - DescriptorTrieNode::rangeToleranceMask = ~0ULL; - - m_DescriptorLookup.uniformBuffer = - EstimateBufferDescriptor(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, addr); - m_DescriptorLookup.storageBuffer = - EstimateBufferDescriptor(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, addr); - // we use a 4-byte format here to distinguish byte size from elem size, but otherwise don't need - // to check multiple formats. There are no possible cases where there's ambiguity between *known* - // descriptor formats. If a descriptor format looks like one of ours for this but not for other - // formats there's not much we can do about that. - // - // we'd slightly prefer a 2-byte format but these aren't required and 4-byte does just as well - m_DescriptorLookup.uniformTexelBuffer = - EstimateBufferDescriptor(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, addr, VK_FORMAT_R32_UINT); - m_DescriptorLookup.storageTexelBuffer = - EstimateBufferDescriptor(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, addr, VK_FORMAT_R32_UINT); - - if(DescriptorDataSize(VK_DESCRIPTOR_TYPE_SAMPLER) == 4 && - DescriptorDataSize(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE) == 4 && - DescriptorDataSize(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) == 4 && - DescriptorDataSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) == 4) - { - // there's only one possible encoding that puts this in 4 bytes - m_DescriptorLookup.storage = m_DescriptorLookup.sampled = ImageDescriptorFormat::Indexed2012; - - m_DescriptorLookup.samplerPalette.resize(0xfff); - m_DescriptorLookup.imageViewPalette.resize(0xfffff); - } - else - { - rdcpair formats[] = { - {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, m_DescriptorLookup.sampled}, - {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, m_DescriptorLookup.storage}, - }; - - VkDescriptorGetInfoEXT info = { - VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT, - NULL, - }; - - VkDescriptorImageInfo imginfo = {}; - info.data.pSampledImage = &imginfo; - imginfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; - imginfo.imageView = imageView; - - uint64_t descData[8] = {}; - - // we only compare the bottom 48 bits of pointers (after shifting appropriately) since some - // descriptors stuff things in the upper bits - const uint64_t ptrMask = ((1ULL << 48) - 1); - - // we allow any pointer within the image to work, since some may be offset (not likely for the - // format we have chosen, but just in case) - VkDeviceAddress imgBase = addr & ptrMask; - VkDeviceAddress imgEnd = imgBase + imgMrq.size; - - for(size_t i = 0; i < ARRAY_COUNT(formats); i++) - { - size_t descSize = DescriptorDataSize(formats[i].first); - - info.type = formats[i].first; - - ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, descData); - - if(descSize == 32 && ((descData[0] << 8) & ptrMask) >= imgBase && - ((descData[0] << 8) & ptrMask) < imgEnd) - { - formats[i].second = ImageDescriptorFormat::PointerShifted_32; - } - else if(descSize == 64 && ((descData[0] << 8) & ptrMask) >= imgBase && - ((descData[0] << 8) & ptrMask) < imgEnd) - { - formats[i].second = ImageDescriptorFormat::PointerShifted_64; - } - else if(descSize == 64 && (descData[2] & ptrMask) >= imgBase && (descData[2] & ptrMask) < imgEnd) - { - formats[i].second = ImageDescriptorFormat::Pointer2_64; - } - else if(descSize == 64 && (descData[4] & ptrMask) >= imgBase && (descData[4] & ptrMask) < imgEnd) - { - formats[i].second = ImageDescriptorFormat::Pointer2_64; - } - else - { - RDCERR("Couldn't determine %s descriptor format for image %llx-%llx", - ToStr(info.type).c_str(), imgBase, imgEnd); - // dump the descriptor - for(uint32_t d = 0; d * 8 < descSize; d++) - RDCLOG("[%u]: %llx", d, descData[d]); - } - } - - size_t combinedSize = m_DescriptorBufferProperties.combinedImageSamplerDescriptorSize; - size_t sampledSize = m_DescriptorBufferProperties.sampledImageDescriptorSize; - size_t samplerSize = m_DescriptorBufferProperties.samplerDescriptorSize; - - if(combinedSize == sampledSize + samplerSize) - { - m_DescriptorLookup.combinedSamplerOffset = (uint32_t)sampledSize; - } - else if(combinedSize >= sampledSize + sampledSize) - { - byte combined1[256] = {}; - byte combined2[256] = {}; - - imginfo.sampler = sampler; - info.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - size_t descSize = DescriptorDataSize(info.type); - - ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, combined1); - - imginfo.sampler = altSampler; - - ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, combined2); - - for(uint32_t i = 0; i < combinedSize; i++) - { - if(combined1[i] != combined2[i]) - { - m_DescriptorLookup.combinedSamplerOffset = i; - break; - } - } - - if(m_DescriptorLookup.combinedSamplerOffset < sampledSize) - { - RDCERR( - "Unexpected descriptor difference at byte %u, less than sampled size %u in combined %u", - m_DescriptorLookup.combinedSamplerOffset, sampledSize, combinedSize); - m_DescriptorLookup.combinedSamplerOffset = (uint32_t)sampledSize; - } - else if(m_DescriptorLookup.combinedSamplerOffset + samplerSize < combinedSize) - { - if(memcmp(combined1 + m_DescriptorLookup.combinedSamplerOffset + samplerSize, - combined2 + m_DescriptorLookup.combinedSamplerOffset + samplerSize, - combinedSize - (m_DescriptorLookup.combinedSamplerOffset + samplerSize)) != 0) - { - RDCERR( - "Unexpected descriptor difference after sampler at offset %u + size %u, before end " - "of combined %u bytes", - m_DescriptorLookup.combinedSamplerOffset, samplerSize, combinedSize); - } - } - - RDCLOG("Combined sampler offset is %u into %u byte combined descriptor", - m_DescriptorLookup.combinedSamplerOffset, combinedSize); - } - else - { - RDCLOG("Unexpected combined size %u with sampled size %u and sampler size %u", combinedSize, - sampledSize, samplerSize); - } - } - - // check for AS descriptors too, and do this last as it stomps the union a bit - if(AccelerationStructures()) - { - VkAccelerationStructureKHR as = VK_NULL_HANDLE; - const VkAccelerationStructureCreateInfoKHR asCreateInfo = { - VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR, - NULL, - VK_ACCELERATION_STRUCTURE_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT, - buffer, - 0, - size, - VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR, - 0x0, - }; - vkr = ObjDisp(m_Device)->CreateAccelerationStructureKHR(Unwrap(m_Device), &asCreateInfo, NULL, - &as); - CHECK_VKR(this, vkr); - - VkAccelerationStructureDeviceAddressInfoKHR asGetInfo = { - VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR, - NULL, - as, - }; - - VkDeviceAddress asAddr = - ObjDisp(m_Device)->GetAccelerationStructureDeviceAddressKHR(Unwrap(m_Device), &asGetInfo); - - m_DescriptorLookup.accelStructure = - EstimateBufferDescriptor(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, asAddr); - - ObjDisp(m_Device)->DestroyAccelerationStructureKHR(Unwrap(m_Device), as, NULL); - } - - // shutdown and destroy the objects we made - ObjDisp(m_Device)->DestroySampler(Unwrap(m_Device), sampler, NULL); - ObjDisp(m_Device)->DestroySampler(Unwrap(m_Device), altSampler, NULL); - ObjDisp(m_Device)->DestroyImageView(Unwrap(m_Device), imageView, NULL); - ObjDisp(m_Device)->DestroyImage(Unwrap(m_Device), image, NULL); - ObjDisp(m_Device)->DestroyBuffer(Unwrap(m_Device), buffer, NULL); - ObjDisp(m_Device)->FreeMemory(Unwrap(m_Device), memory, NULL); - - RDCLOG("Descriptor format estimates:"); - RDCLOG(" Uniform buffers: %s", ToStr(m_DescriptorLookup.uniformBuffer).c_str()); - RDCLOG(" Storage buffers: %s", ToStr(m_DescriptorLookup.storageBuffer).c_str()); - RDCLOG(" Uniform texel buffers: %s", ToStr(m_DescriptorLookup.uniformTexelBuffer).c_str()); - RDCLOG(" Storage texel buffers: %s", ToStr(m_DescriptorLookup.storageTexelBuffer).c_str()); - RDCLOG(" Accel Structs: %s", ToStr(m_DescriptorLookup.accelStructure).c_str()); - RDCLOG(" Sampled images: %s", ToStr(m_DescriptorLookup.sampled).c_str()); - RDCLOG(" Storage images: %s", ToStr(m_DescriptorLookup.storage).c_str()); -} - -BufferDescriptorFormat WrappedVulkan::EstimateBufferDescriptor(VkDescriptorType type, - VkDeviceAddress addr, - VkFormat texelFormat) -{ - BufferDescriptorFormat outFormat = BufferDescriptorFormat::UnknownBufferDescriptor; - - union - { - byte descriptorBytes[256]; - uint64_t descriptorU64[32]; - }; - - VkDescriptorGetInfoEXT info = { - VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT, - }; - - info.type = type; - - const uint64_t texelSize = - texelFormat == VK_FORMAT_UNDEFINED ? 1 : GetByteSize(1, 1, 1, texelFormat, 0); - - // start with a size that will never run afoul of alignment problems but isn't likely to be - // misidentified by a random bit. This also stays under 64k which is the minimum limit for some buffers - VkDeviceAddress byteSize = 0xd300; - VkDeviceAddress elemSize = byteSize / texelSize; - - size_t descSize = DescriptorDataSize(info.type); - - VkDescriptorAddressInfoEXT bufinfo = {VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT}; - - if(info.type != VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR) - { - bufinfo.address = addr; - bufinfo.range = byteSize; - bufinfo.format = texelFormat; - info.data.pUniformBuffer = &bufinfo; - } - else - { - info.data.accelerationStructure = addr; - } - - ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, descriptorBytes); - - // we only compare the bottom 48 bits of pointers (after shifting appropriately) since some - // descriptors stuff things in the upper bits - const uint64_t ptrMask = ((1ULL << 48) - 1); - - addr &= ptrMask; - - if(addr == 0) - { - RDCERR("Invalid address returned"); - } - else if(type == VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR) - { - if(descSize == 8 && (descriptorU64[0] & ptrMask) == addr) - outFormat = BufferDescriptorFormat::Pointer_8; - else if(descSize == 16 && (descriptorU64[0] & ptrMask) == addr) - outFormat = BufferDescriptorFormat::Pointer0_16; - else if(descSize == 32 && (descriptorU64[1] & ptrMask) == addr) - outFormat = BufferDescriptorFormat::Pointer1_32; - else if(descSize == 64 && (descriptorU64[2] & ptrMask) == addr) - outFormat = BufferDescriptorFormat::Pointer2_64; - } - else if(descSize == 8 && descriptorU64[0] == ((bufinfo.address >> 4) | ((byteSize >> 4) << 45))) - { - // check alignment - bufinfo.range = byteSize = 16; - ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, descriptorBytes); - - if((descriptorU64[0] & ptrMask) == ((bufinfo.address >> 4) | ((byteSize >> 4) << 45))) - { - outFormat = BufferDescriptorFormat::Packed_4519_Aligned16_8; - DescriptorTrieNode::rangeToleranceMask = ~0xFULL; - } - else if((descriptorU64[0] & ptrMask) == ((bufinfo.address >> 4) | ((256ULL >> 4) << 45))) - { - outFormat = BufferDescriptorFormat::Packed_4519_Aligned256_8; - DescriptorTrieNode::rangeToleranceMask = ~0xFFULL; - } - else - { - outFormat = BufferDescriptorFormat::UnknownBufferDescriptor; - } - } - else if(descSize == 16) - { - if((descriptorU64[0] & ptrMask) == (bufinfo.address & ptrMask) && - (descriptorU64[1] & 0xffffffff) == elemSize) - outFormat = BufferDescriptorFormat::Pointer_ElemSize_16; - else if((descriptorU64[0] & ptrMask) == ((bufinfo.address & ptrMask) / texelSize) && - (descriptorU64[1] & 0xffffffff) == elemSize) - outFormat = BufferDescriptorFormat::PointerDivided_ElemSize_16; - } - else if(descSize == 32) - { - if((descriptorU64[1] & ptrMask) == (bufinfo.address & ptrMask) && - (descriptorU64[0] >> 32) == byteSize) - outFormat = BufferDescriptorFormat::ByteSize0_Pointer1_32; - if((descriptorU64[4] & ptrMask) == (bufinfo.address & ptrMask) && - (descriptorU64[5] >> 32) == byteSize) - outFormat = BufferDescriptorFormat::Pointer4_ByteSize5_Unaligned_64; - } - else if(descSize == 64) - { - // don't check ElemSize0_Pointer2_64 here due to possible aliasing with Strided*_MultiDescriptor_64 - /* - if((descriptorU64[2] & ptrMask) == bufinfo.address && (descriptorU64[0] >> 32) == elemSize) - { - outFormat = BufferDescriptorFormat::ElemSize0_Pointer2_64; - } - else - */ - - if((descriptorU64[4] & ptrMask) == (bufinfo.address & ptrMask) && - (descriptorU64[5] >> 32) == byteSize) - { - // check alignment - bufinfo.range = 16; - ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, descriptorBytes); - - if((descriptorU64[4] & ptrMask) == (bufinfo.address & ptrMask) && (descriptorU64[5] >> 32) == 64) - { - outFormat = BufferDescriptorFormat::Pointer4_ByteSize5_Aligned_64; - DescriptorTrieNode::rangeToleranceMask = ~0x3FULL; - } - else if((descriptorU64[4] & ptrMask) == (bufinfo.address & ptrMask) && - (descriptorU64[5] >> 32) == 16) - { - outFormat = BufferDescriptorFormat::Pointer4_ByteSize5_Unaligned_64; - } - } - else if((descriptorU64[4] & ptrMask) == (bufinfo.address & ptrMask)) - { - // check for complex scattering, we sized the memory large enough for 3 million specifically to test this - uint64_t inputs[3] = {256, 200, 3000000}; - uint64_t scattered[3] = {descriptorU64[1]}; - - bufinfo.range = inputs[1]; - ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, descriptorBytes); - scattered[1] = descriptorU64[1]; - - bufinfo.range = inputs[2]; - ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, descriptorBytes); - scattered[2] = descriptorU64[1]; - - bool match = true; - for(size_t i = 0; i < ARRAY_COUNT(inputs); i++) - { - uint64_t num = inputs[i] - 1; - if(texelSize > 1) - num = (inputs[i] / texelSize) - 1; - - if(texelSize == 1) - { - uint8_t x = num & 0xff; - num = (num & ~0xff) + ((x & 0xfc) + 6 - (x & 0x3)); - } - else if(texelSize == 2) - { - uint8_t x = num & 0xff; - num = (num & ~0xff) + ((x & 0xfe) + 2 - (x & 0x1)); - } - - uint64_t expected = ((num & 0x00007f) << 0) | ((num & 0x1fff80) << 9) | ((num >> 21) << 53); - if(expected != (scattered[i] & 0xffe00000001fffffULL)) - { - match = false; - break; - } - } - - if(match) - outFormat = BufferDescriptorFormat::ElemSizeScattered1_Pointer4_64; - } - } - - // variable size descriptors - if(outFormat == BufferDescriptorFormat::UnknownBufferDescriptor && - (descSize == 64 || descSize == 128)) - { - // check with non-64byte aligned pointer just to check. 16 bytes is safe for all possible inputs - bufinfo.address += 16; - ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, descriptorBytes); - - VkDeviceAddress remainder = bufinfo.address & 0x3f; - VkDeviceAddress alignedAddr = (bufinfo.address & ptrMask) - remainder; - RDCASSERT(remainder != 0, bufinfo.address); - - BufferDescriptorFormat fmts[] = { - BufferDescriptorFormat::Strided4_MultiDescriptor_64, - BufferDescriptorFormat::Strided2_MultiDescriptor_64, - BufferDescriptorFormat::Strided1_MultiDescriptor_64, - }; - uint32_t strides[] = { - 4, - 2, - 1, - }; - RDCCOMPILE_ASSERT(ARRAY_COUNT(fmts) == ARRAY_COUNT(strides), - "Strides don't match number of descriptor formats"); - - for(size_t i = 0; i < ARRAY_COUNT(fmts); i++) - { - uint32_t stride = strides[i]; - - if((descriptorU64[2] & ptrMask) == alignedAddr && - descriptorU64[0] >> 32 == (byteSize >> stride) && - ((descriptorU64[1] >> 16) & 0x3f) == (remainder >> stride)) - { - outFormat = fmts[i]; - break; - } - } - - if(outFormat == BufferDescriptorFormat::UnknownBufferDescriptor && - (descriptorU64[2] & ptrMask) == (bufinfo.address & ptrMask) && - (descriptorU64[0] >> 32) == elemSize) - { - outFormat = BufferDescriptorFormat::ElemSize0_Pointer2_64; - } - } - - if(outFormat == BufferDescriptorFormat::UnknownBufferDescriptor) - { - RDCERR("Couldn't determine %s descriptor format for address %llx range %llx", - ToStr(info.type).c_str(), addr, bufinfo.range); - // dump the descriptor - for(uint32_t i = 0; i * 8 < descSize; i++) - RDCLOG("[%u]: %llx", i, descriptorU64[i]); - } - - return outFormat; -} - -void WrappedVulkan::LookupDescriptor(byte *descriptorBytes, size_t descriptorSize, - DescriptorType type, DescriptorSetSlot &data) -{ - const size_t combinedSize = m_DescriptorBufferProperties.combinedImageSamplerDescriptorSize; - const size_t sampledSize = m_DescriptorBufferProperties.sampledImageDescriptorSize; - const size_t samplerSize = m_DescriptorBufferProperties.samplerDescriptorSize; - - union - { - VkDescriptorImageInfo imInfo; - VkDescriptorAddressInfoEXT bufinfo; - VkSampler sampler; - }; - - bufinfo = {}; - - VkDescriptorGetInfoEXT info = { - VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT, - }; - - info.data.pUniformBuffer = &bufinfo; - - byte tempMem[256] = {}; - // start with the descriptor bytes in case the driver doesn't initialise them all. If this - // contains random bytes from user memory we want it to match - memcpy(tempMem, descriptorBytes, descriptorSize); - if(Vulkan_Debug_UseFastDescriptorLookup()) - { - switch(type) - { - case DescriptorType::Sampler: - { - ResourceId samp = GetSamplerForDescriptor(descriptorBytes, descriptorSize); - - if(samp != ResourceId()) - { - data = {}; - data.SetSampler(samp); - - // verify that descriptor roundtrips that our detection was correct - info.type = VK_DESCRIPTOR_TYPE_SAMPLER; - sampler = Unwrap(GetResourceManager()->GetCurrentHandle(samp)); - - ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descriptorSize, tempMem); - - if(memcmp(tempMem, descriptorBytes, descriptorSize) == 0) - return; - } - - break; - } - case DescriptorType::ImageSampler: - case DescriptorType::Image: - case DescriptorType::ReadWriteImage: - { - if(type == DescriptorType::ImageSampler) - info.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - else if(type == DescriptorType::Image) - info.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - else if(type == DescriptorType::ReadWriteImage) - info.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - - ResourceId samp; - ResourceId view; - - if(type == DescriptorType::ImageSampler) - { - if(m_DescriptorLookup.sampled == ImageDescriptorFormat::Indexed2012) - { - // for the indexed format, the sampler/view are encoded together - view = GetImageViewForDescriptor(descriptorBytes, descriptorSize, type); - samp = GetSamplerForDescriptor(descriptorBytes, descriptorSize); - } - else if(m_DescriptorLookup.sampled == ImageDescriptorFormat::UnknownImageDescriptor) - { - break; - } - else - { - // all known formats we expect an image view to be followed by a sampler - if(combinedSize == m_DescriptorLookup.combinedSamplerOffset + samplerSize) - { - view = GetImageViewForDescriptor(descriptorBytes, sampledSize, type); - samp = GetSamplerForDescriptor( - descriptorBytes + m_DescriptorLookup.combinedSamplerOffset, samplerSize); - } - else - { - RDCWARN( - "non-indexed combined image/sampler is not (padded) image followed by sampler"); - } - } - - if(samp == ResourceId()) - { - RDCWARN("Fast-detection failed to get sampler for image/sampler descriptor"); - break; - } - } - else - { - view = GetImageViewForDescriptor(descriptorBytes, descriptorSize, type); - } - - // exit silently, may be unknown descriptor format which would spam - if(view == ResourceId()) - { - // check if this is a NULL descriptor, in which case we've identified correctly! - // only works if we don't need to care about the layout - if(m_IgnoreLayoutForDescriptors && - m_DescriptorLookup.nullPatterns[(uint32_t)convert(info.type)] == - bytebuf(descriptorBytes, descriptorSize)) - { - data = {}; - data.SetImageSampler(info.type, ResourceId(), ResourceId(), VK_IMAGE_LAYOUT_GENERAL); - return; - } - break; - } - - rdcarray layouts; - - if(!m_IgnoreLayoutForDescriptors) - { - layouts = m_DescriptorLookup.generalImageLayouts; - - if(m_CreationInfo.m_ImageView[view].isDepthImage) - layouts.append(m_DescriptorLookup.depthImageLayouts); - } - - imInfo.sampler = Unwrap(GetResourceManager()->GetCurrentHandle(samp)); - imInfo.imageView = Unwrap(GetResourceManager()->GetCurrentHandle(view)); - - // always iterate at least once even if the layouts array is empty - for(size_t i = 0; i < layouts.size() || (i == 0 && layouts.empty()); i++) - { - imInfo.imageLayout = i < layouts.size() ? layouts[i] : VK_IMAGE_LAYOUT_GENERAL; - - ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descriptorSize, tempMem); - - if(memcmp(tempMem, descriptorBytes, descriptorSize) == 0) - { - data.SetImageSampler(info.type, view, samp, imInfo.imageLayout); - return; - } - } - - RDCWARN("Fast-detection failed for %s descriptor", ToStr(type).c_str()); - - break; - } - case DescriptorType::TypedBuffer: - case DescriptorType::ReadWriteTypedBuffer: - case DescriptorType::ConstantBuffer: - case DescriptorType::ReadWriteBuffer: - case DescriptorType::AccelerationStructure: - { - if(type == DescriptorType::TypedBuffer) - info.type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; - else if(type == DescriptorType::ReadWriteTypedBuffer) - info.type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; - else if(type == DescriptorType::ConstantBuffer) - info.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - else if(type == DescriptorType::ReadWriteBuffer) - info.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - else if(type == DescriptorType::AccelerationStructure) - info.type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; - - const bool texelBuffer = - type == DescriptorType::TypedBuffer || type == DescriptorType::ReadWriteTypedBuffer; - - VkDeviceAddress address; - VkDeviceSize size; - - GetPointerAndSizeForDescriptor(descriptorBytes, descriptorSize, type, address, size); - - if(address == 0) - { - // exit silently, may be unknown descriptor format which would spam - // check if this is a NULL descriptor, in which case we've identified correctly! - // can't work for texel buffers that may have a format encoded - if(type != DescriptorType::TypedBuffer && type != DescriptorType::ReadWriteTypedBuffer && - m_DescriptorLookup.nullPatterns[(uint32_t)convert(info.type)] == - bytebuf(descriptorBytes, descriptorSize)) - { - data = {}; - if(type == DescriptorType::AccelerationStructure) - data.SetAccelerationStructure(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, - VK_NULL_HANDLE); - else - data.SetBuffer(info.type, ResourceId(), 0, 0, VK_FORMAT_UNDEFINED); - return; - } - break; - } - else - { - if(type == DescriptorType::AccelerationStructure) - { - info.data.accelerationStructure = address; - } - else - { - bufinfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT; - bufinfo.address = address; - bufinfo.range = size; - } - - bufinfo.format = VK_FORMAT_UNDEFINED; - for(size_t i = 0, n = texelBuffer ? m_DescriptorLookup.texelFormats.size() : 1; i < n; i++) - { - if(texelBuffer) - bufinfo.format = m_DescriptorLookup.texelFormats[i]; - - if(type != DescriptorType::AccelerationStructure) - { - // a couple of formats modify the address or size in non-trivial ways that need to be patched - // here. This also handles converting an element size back into a byte size trivially - GetFinalBufferParameters(descriptorBytes, descriptorSize, type, bufinfo.format, - address, size, bufinfo.address, bufinfo.range); - } - - ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descriptorSize, tempMem); - - if(memcmp(tempMem, descriptorBytes, descriptorSize) != 0) - { - // try sign extending if the top bit is set - address |= (0xffffULL << 48); - if(type == DescriptorType::AccelerationStructure) - info.data.accelerationStructure = address; - else - bufinfo.address = address; - - ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descriptorSize, tempMem); - } - - if(memcmp(tempMem, descriptorBytes, descriptorSize) == 0) - { - ResourceId id; - VkDeviceSize offs; - - if(type != DescriptorType::AccelerationStructure) - { - GetResIDFromAddr(bufinfo.address, id, offs); - - if(id == ResourceId() && (bufinfo.address & (1ULL << 47))) - { - // try sign extending if the top bit is set - GetResIDFromAddr(bufinfo.address | (0xffffULL << 48), id, offs); - } - - if(id == ResourceId()) - { - RDCWARN("Unknown buffer at descriptor address %llx", bufinfo.address); - } - else - { - data.SetBuffer(info.type, id, offs, bufinfo.range, bufinfo.format); - - return; - } - } - else - { - id = m_ASLookupByAddr[address]; - - if(id == ResourceId() && (address & (1ULL << 47))) - { - // try sign extending if the top bit is set - id = m_ASLookupByAddr[address | (0xffffULL << 48)]; - } - - if(id == ResourceId()) - { - RDCWARN("Unknown AS at descriptor address %llx", address); - } - else - { - data.SetAccelerationStructure( - VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, - GetResourceManager()->GetCurrentHandle(id)); - - return; - } - } - } - } - - RDCWARN("Fast-detection failed to get match for %s descriptor", ToStr(type).c_str()); - } - - break; - } - case DescriptorType::Buffer: - case DescriptorType::Unknown: RDCERR("Invalid descriptor type being looked up"); break; - } - } - - data = m_DescriptorLookup.fallback.lookup({descriptorBytes, descriptorSize}); - -#if ENABLED(RDOC_DEVEL) - if(!m_DescriptorLookup.fallback.contains({descriptorBytes, descriptorSize})) - { - RDCERR("Trie descriptor lookup failed"); - // dump the descriptor - uint64_t *descriptorU64 = (uint64_t *)descriptorBytes; - for(uint32_t i = 0; i * 8 < descriptorSize; i++) - RDCLOG(" [%u]: %llx", i, descriptorU64[i]); - } -#endif -} - -ResourceId WrappedVulkan::GetSamplerForDescriptor(byte *descriptorBytes, size_t descriptorSize) -{ - if(m_DescriptorLookup.sampled == ImageDescriptorFormat::Indexed2012) - { - if(descriptorSize == sizeof(uint32_t)) - { - uint32_t idx = (*(uint32_t *)descriptorBytes) >> 20; - - if(idx > 0 && idx < m_DescriptorLookup.samplerPalette.size()) - { - return m_DescriptorLookup.samplerPalette[idx]; - } - - RDCWARN("Indexed sampler descriptor index is %u", idx); - return ResourceId(); - } - - RDCWARN("Indexed sampler descriptor is %zu bytes", descriptorSize); - return ResourceId(); - } - - DescriptorTrieNode data = m_DescriptorLookup.samplers.lookup({descriptorBytes, descriptorSize}); - // we should find this - if(data.sampler == ResourceId()) - RDCWARN("Couldn't find solo sampler in sampler lookup trie"); - - return data.sampler; -} - -ResourceId WrappedVulkan::GetImageViewForDescriptor(byte *descriptorBytes, size_t descriptorSize, - DescriptorType type) -{ - ImageDescriptorFormat format = m_DescriptorLookup.sampled; - if(type == DescriptorType::ReadWriteImage) - format = m_DescriptorLookup.storage; - - // we assumed if one image type is indexed, all are - if(format == ImageDescriptorFormat::Indexed2012) - { - if(descriptorSize == sizeof(uint32_t)) - { - uint32_t idx = (*(uint32_t *)descriptorBytes) & 0xfffff; - - if(idx > 0 && idx < m_DescriptorLookup.imageViewPalette.size()) - { - return m_DescriptorLookup.imageViewPalette[idx]; - } - - RDCWARN("Indexed view descriptor index is %u", idx); - return ResourceId(); - } - - RDCWARN("Indexed view descriptor is %zu bytes", descriptorSize); - return ResourceId(); - } - - // other descriptors are recognised by pointer - uint64_t ptr = 0; - - if(format == ImageDescriptorFormat::PointerShifted_32 || - format == ImageDescriptorFormat::PointerShifted_64) - { - if(descriptorSize == 32 || descriptorSize == 64) - { - ptr = (((uint64_t *)descriptorBytes)[0] << 8) & ((1ULL << 48) - 1); - } - else - { - RDCWARN("Unexpected descriptor format for detected format %u: %u", format, descriptorSize); - return ResourceId(); - } - } - else if(format == ImageDescriptorFormat::Pointer2_64) - { - if(descriptorSize == 64) - { - ptr = ((uint64_t *)descriptorBytes)[2] & ((1ULL << 48) - 1); - } - else - { - RDCWARN("Unexpected descriptor format for detected format %u: %u", format, descriptorSize); - return ResourceId(); - } - } - else if(format == ImageDescriptorFormat::Pointer4_64) - { - if(descriptorSize == 64) - { - ptr = ((uint64_t *)descriptorBytes)[4] & ((1ULL << 48) - 1); - } - else - { - RDCWARN("Unexpected descriptor format for detected format %u: %u", format, descriptorSize); - return ResourceId(); - } - } - else - { - return ResourceId(); - } - - ResourceId imageId; - uint64_t unused; - m_DescriptorLookup.imageAddresses.GetResIDFromAddr(ptr, imageId, unused); - - if(imageId == ResourceId() && (ptr & (1ULL << 47))) - { - // try sign extending if the top bit is set - m_DescriptorLookup.imageAddresses.GetResIDFromAddr(ptr | (0xffffULL << 48), imageId, unused); - } - - if(imageId == ResourceId()) - { - RDCWARN("View descriptor gave unrecognised pointer %llx", ptr); - return ResourceId(); - } - - ResourceId viewId = - m_CreationInfo.m_Image[imageId].getViewFromDescriptor(descriptorBytes, descriptorSize); - - if(viewId == ResourceId()) - { - RDCWARN("View descriptor gave pointer %llx for image %s but was unrecognised", ptr, - ToStr(imageId).c_str()); - return ResourceId(); - } - - return viewId; -} - -void WrappedVulkan::GetPointerAndSizeForDescriptor(byte *descriptorBytes, size_t descriptorSize, - DescriptorType type, VkDeviceAddress &address, - VkDeviceSize &size) -{ - address = 0; - size = 0; - - BufferDescriptorFormat format = m_DescriptorLookup.uniformBuffer; - if(type == DescriptorType::ReadWriteBuffer) - format = m_DescriptorLookup.storageBuffer; - else if(type == DescriptorType::TypedBuffer) - format = m_DescriptorLookup.uniformTexelBuffer; - else if(type == DescriptorType::ReadWriteTypedBuffer) - format = m_DescriptorLookup.storageTexelBuffer; - else if(type == DescriptorType::AccelerationStructure) - format = m_DescriptorLookup.accelStructure; - - if(format == BufferDescriptorFormat::Pointer_8 && descriptorSize == sizeof(uint64_t)) - { - uint64_t *packed = (uint64_t *)descriptorBytes; - address = (packed[0] & ((1ULL << 48) - 1)); - } - else if(format == BufferDescriptorFormat::Packed_4519_Aligned16_8 && - descriptorSize == sizeof(uint64_t)) - { - uint64_t packed = *(uint64_t *)descriptorBytes; - address = (packed & ((1ULL << 45) - 1)) << 4; - size = (packed >> 45) << 4; - } - else if(format == BufferDescriptorFormat::Packed_4519_Aligned256_8 && - descriptorSize == sizeof(uint64_t)) - { - uint64_t packed = *(uint64_t *)descriptorBytes; - address = (packed & ((1ULL << 45) - 1)) << 4; - size = (packed >> 45) << 4; - } - else if(format == BufferDescriptorFormat::Pointer_ElemSize_16 && - descriptorSize == sizeof(uint64_t) * 2) - { - uint64_t *packed = (uint64_t *)descriptorBytes; - address = (packed[0] & ((1ULL << 48) - 1)); - size = (packed[1] & 0xFFFFFFFFULL); - } - else if(format == BufferDescriptorFormat::PointerDivided_ElemSize_16 && - descriptorSize == sizeof(uint64_t) * 2) - { - uint64_t *packed = (uint64_t *)descriptorBytes; - address = (packed[0] & ((1ULL << 48) - 1)); - size = (packed[1] & 0xFFFFFFFFULL); - - // address is incomplete, but can't be fixed until we know the texel size - } - else if(format == BufferDescriptorFormat::Pointer0_16 && descriptorSize == sizeof(uint64_t) * 2) - { - uint64_t *packed = (uint64_t *)descriptorBytes; - address = (packed[0] & ((1ULL << 48) - 1)); - } - else if(format == BufferDescriptorFormat::ByteSize0_Pointer1_32 && - descriptorSize == sizeof(uint64_t) * 4) - { - uint64_t *packed = (uint64_t *)descriptorBytes; - address = (packed[1] & ((1ULL << 48) - 1)); - size = (packed[0] >> 32); - } - else if(format == BufferDescriptorFormat::Pointer1_32 && descriptorSize == sizeof(uint64_t) * 4) - { - uint64_t *packed = (uint64_t *)descriptorBytes; - address = (packed[1] & ((1ULL << 48) - 1)); - } - else if(format == BufferDescriptorFormat::Pointer2_64 && descriptorSize == sizeof(uint64_t) * 4) - { - uint64_t *packed = (uint64_t *)descriptorBytes; - address = (packed[2] & ((1ULL << 48) - 1)); - } - else if(format == BufferDescriptorFormat::Pointer4_ByteSize5_Unaligned_64 && - descriptorSize == sizeof(uint64_t) * 8) - { - uint64_t *packed = (uint64_t *)descriptorBytes; - address = (packed[4] & ((1ULL << 48) - 1)); - size = (packed[5] >> 32); - } - else if(format == BufferDescriptorFormat::Pointer4_ByteSize5_Aligned_64 && - descriptorSize == sizeof(uint64_t) * 8) - { - uint64_t *packed = (uint64_t *)descriptorBytes; - address = (packed[4] & ((1ULL << 48) - 1)); - size = (packed[5] >> 32); - } - else if(format == BufferDescriptorFormat::ElemSizeScattered1_Pointer4_64 && - descriptorSize == sizeof(uint64_t) * 8) - { - uint64_t *packed = (uint64_t *)descriptorBytes; - address = (packed[4] & ((1ULL << 48) - 1)); - - uint64_t sizeScattered = packed[1]; - size = (sizeScattered & 0x7f) | ((sizeScattered >> 9) & 0x1fff80) | ((sizeScattered >> 53) << 21); - - // this is still swizzled on the low bits, but we won't know how to decode that until we have the texel format - } - else if((format == BufferDescriptorFormat::Strided4_MultiDescriptor_64 || - format == BufferDescriptorFormat::Strided2_MultiDescriptor_64 || - format == BufferDescriptorFormat::Strided1_MultiDescriptor_64) && - (descriptorSize == sizeof(uint64_t) * 8 * 1 || descriptorSize == sizeof(uint64_t) * 8 * 2)) - { - const uint32_t stride = format == BufferDescriptorFormat::Strided4_MultiDescriptor_64 ? 4 - : format == BufferDescriptorFormat::Strided2_MultiDescriptor_64 ? 2 - : 1; - - uint64_t *packed = (uint64_t *)descriptorBytes; - address = (packed[2] & ((1ULL << 48) - 1)); - - address += ((packed[1] >> 16) & 0x3f) << stride; - - size = (packed[0] >> 32) << stride; - } - else if(format == BufferDescriptorFormat::ElemSize0_Pointer2_64 && - descriptorSize == sizeof(uint64_t) * 8) - { - uint64_t *packed = (uint64_t *)descriptorBytes; - address = (packed[2] & ((1ULL << 48) - 1)); - size = (packed[0] >> 32); - } -} - -void WrappedVulkan::GetFinalBufferParameters(byte *descriptorBytes, size_t descriptorSize, - DescriptorType type, VkFormat texelFormat, - VkDeviceAddress inAddress, VkDeviceSize inSize, - VkDeviceAddress &outAddress, VkDeviceSize &outSize) -{ - BufferDescriptorFormat format = m_DescriptorLookup.uniformBuffer; - - if(type == DescriptorType::ReadWriteBuffer) - format = m_DescriptorLookup.storageBuffer; - else if(type == DescriptorType::TypedBuffer) - format = m_DescriptorLookup.uniformTexelBuffer; - else if(type == DescriptorType::ReadWriteTypedBuffer) - format = m_DescriptorLookup.storageTexelBuffer; - else if(type == DescriptorType::AccelerationStructure) - format = m_DescriptorLookup.accelStructure; - - uint32_t elemSize = 1; - if(type == DescriptorType::TypedBuffer || type == DescriptorType::ReadWriteTypedBuffer) - elemSize = GetByteSize(1, 1, 1, texelFormat, 0) & 0xffff; - - if(format == BufferDescriptorFormat::ElemSize0_Pointer2_64 || - format == BufferDescriptorFormat::Pointer_ElemSize_16) - { - outAddress = inAddress; - outSize = inSize * elemSize; - } - else if(format == BufferDescriptorFormat::PointerDivided_ElemSize_16) - { - outAddress = inAddress * elemSize; - - // for elemSize==12 the address didn't divide evenly so we need to grab the remainder - if(elemSize == 12) - { - uint64_t *packed = (uint64_t *)descriptorBytes; - uint64_t remainder = (packed[1] >> 32) & 0x3f; - // the actual pattern to this is entirely unknown and it's assumed to be some internal base - // offset, but this seems consistent as the 3 remainders (0, 4, 8 bytes) are always 23 apart in these bits - outAddress += 4 * (remainder / 23); - } - - // outAddress += remainder; - outSize = inSize * elemSize; - } - else if(format == BufferDescriptorFormat::ElemSizeScattered1_Pointer4_64) - { - outAddress = inAddress; - - if(elemSize >= 4) - { - outSize = (inSize + 1) * elemSize; - } - else - { - // unswizzle the 2-byte/1-byte size. There's probably a fancier way to express this - // bit-twiddling but it's more readable to have this verbosely specified - // - // the general scheme for encoding is: - // - // lop off the bottom 8 bits, which we call 'x' - // - // swizzle those bits in this formula: - // 1 byte elements: ((x & 0xfc) + 6 - (x & 0x3)) - // 2 byte elements: ((x & 0xfe) + 2 - (x & 0x1)) - // - // note that this means the bottom bits can be swizzled into 9 bits of data and increment into - // the upper bits, so we need to deal with carrying. E.g. for x = 0xfd this produces 0x102 result - - const uint32_t upperMask = elemSize == 1 ? 0xfc : 0xfe; - const uint32_t lowerMask = 0xff - upperMask; - const uint32_t offset = lowerMask << 1; - - // segment the lower swizzled bits. There may be leakage due to the carry bit but we handle that - const uint64_t upperSize = inSize & ~0xff; - const uint32_t lowerSize = inSize & 0xff; - - // need a carry bit for some cases, this will be subtracted later - const uint32_t carry = lowerSize < offset ? 128 : 0; - - // this is (mostly) the result of the (x & 0xfc) - (x & 0x3) subtraction - const uint32_t xsubbed = lowerSize + carry - offset; - - // assuming a lower mask of 0x3 (bottom two bits) if xsubbed ends in 00 then it must have been - // aligned, if it ended in 11 then it must have been 01 subtracted from the value above, etc. - // this will give us the original bottom two bits of x by subtracting and masking - const uint32_t xlow = ((lowerMask + 1) - (xsubbed & lowerMask)) & lowerMask; - - // the upper bits of the size are added on unconditionally, we also undo the +1 to the range here - outSize = upperSize + 1; - - // no bits = no subtraction! x must have been aligned when we did the sum so just the upperMask bits are used - if(xlow == 0) - outSize += xsubbed; - else - // xlow had some bits, so we figure out what it must have been subtracted from and add that to the upper mask - outSize += ((xsubbed & upperMask) + (lowerMask + 1) + xlow); - - // subtract any carry we added now - outSize -= carry; - - // finally convert to bytes - outSize *= elemSize; - } - } - else - { - // byte sizes, no translation needed - outAddress = inAddress; - outSize = inSize; - } -} - -void WrappedVulkan::RegisterDescriptor(const bytebuf &key, const DescriptorSetSlot &data) -{ - m_DescriptorLookup.fallback.insert(key, data); - - // only register NULL patterns for non-sampler types - if(data.type != DescriptorSlotType::Sampler && - data.type != DescriptorSlotType::CombinedImageSampler && data.resource == ResourceId()) - m_DescriptorLookup.nullPatterns[(uint32_t)data.type] = key; - - // store unique texel buffer formats used, expecting this to be small and it will help descriptor lookups - if(data.type == DescriptorSlotType::UniformTexelBuffer || - data.type == DescriptorSlotType::StorageTexelBuffer) - { - VkFormat fmt = VkFormat(data.imageLayoutOrFormat); - if(data.resource != ResourceId() && !m_DescriptorLookup.texelFormats.contains(fmt)) - m_DescriptorLookup.texelFormats.push_back(fmt); - } - else if(data.type == DescriptorSlotType::CombinedImageSampler || - data.type == DescriptorSlotType::SampledImage || - data.type == DescriptorSlotType::StorageImage || - data.type == DescriptorSlotType::InputAttachment) - { - VkImageLayout layout = convert(data.imageLayoutOrFormat); - - if(layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL || - layout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL || - layout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL || - layout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL || - layout == VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL) - { - if(!m_DescriptorLookup.depthImageLayouts.contains(layout)) - m_DescriptorLookup.depthImageLayouts.push_back(layout); - } - else - { - if(!m_DescriptorLookup.generalImageLayouts.contains(layout)) - { - m_DescriptorLookup.generalImageLayouts.push_back(layout); - // keep the list sorted so that rare/niche layouts like feedback loop or local read are tried last - std::sort(m_DescriptorLookup.generalImageLayouts.begin(), - m_DescriptorLookup.generalImageLayouts.end()); - } - } - } - - size_t combinedSize = m_DescriptorBufferProperties.combinedImageSamplerDescriptorSize; - size_t sampledSize = m_DescriptorBufferProperties.sampledImageDescriptorSize; - size_t samplerSize = m_DescriptorBufferProperties.samplerDescriptorSize; - - // if this is just a sampler descriptor, store it directly (unless we're not using indexed) - if(data.type == DescriptorSlotType::Sampler && - m_DescriptorLookup.sampled != ImageDescriptorFormat::Indexed2012) - { - m_DescriptorLookup.samplers.insert(key, data); - } - // if this is a combined descriptor but it looks like it's an image+sampler (which is common) and - // we're not indexed, store the second part as sampler bytes. This may be wrong, but that's fine - // and worst case we pollute the samplers lookup and fail to do a fast lookup of this descriptor - else if(data.type == DescriptorSlotType::CombinedImageSampler && - combinedSize == m_DescriptorLookup.combinedSamplerOffset + samplerSize) - { - DescriptorSetSlot samplerData; - samplerData.SetSampler(data.sampler); - - m_DescriptorLookup.samplers.insert( - {key.data() + m_DescriptorLookup.combinedSamplerOffset, samplerSize}, samplerData); - } - - if((data.type == DescriptorSlotType::InputAttachment || - data.type == DescriptorSlotType::StorageImage || data.type == DescriptorSlotType::SampledImage || - data.type == DescriptorSlotType::CombinedImageSampler) && - m_DescriptorLookup.sampled != ImageDescriptorFormat::Indexed2012) - { - m_CreationInfo.m_Image[m_CreationInfo.m_ImageView[data.resource].image].viewDescriptors.push_back( - {bytebuf(key.data(), sampledSize), data.resource}); - } -} - bool WrappedVulkan::IsPartialRenderPassActive() { for(const CommandBufferNode &cmdNode : m_Partial.partialStack) diff --git a/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp index 8879e4093..f148d4349 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp @@ -33,6 +33,1348 @@ RDOC_CONFIG( uint32_t, Vulkan_Debug_DangerousDescriptorSerialisation, 0, "DANGEROUS, MAY CAUSE CAPTURE FAILURES: Disable serialising plain untyped buffer descriptors."); +RDOC_CONFIG(bool, Vulkan_Debug_UseFastDescriptorLookup, true, + "Use fast pattern-matching lookup to try to identify descriptors before falling back " + "to trie lookup."); + +uint32_t WrappedVulkan::DescriptorDataSize(VkDescriptorType type) +{ + return ::DescriptorDataSize(m_DescriptorBufferProperties, type); +} + +void WrappedVulkan::EstimateDescriptorFormats() +{ + // we want to differentiate the descriptor in as few tests as possible. We don't necessarily care + // if we falsely identify a descriptor format once we've isolated it down to one since worst case + // we'd have to fall back to nothing. + + // create an image first so we can make a buffer on the same memory type and re-use the memory + // allocation for everything. We make it with a format that should be guaranteed supported by all drivers + VkImageCreateInfo imCreateInfo = { + VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + NULL, + VK_IMAGE_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT, + VK_IMAGE_TYPE_2D, + VK_FORMAT_R32G32B32A32_SFLOAT, + {128, 128, 1}, + 1, + 1, + VK_SAMPLE_COUNT_1_BIT, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_STORAGE_BIT, + VK_SHARING_MODE_EXCLUSIVE, + VK_IMAGE_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT, + NULL, + VK_IMAGE_LAYOUT_UNDEFINED, + }; + + VkImage image = VK_NULL_HANDLE; + VkResult vkr = ObjDisp(m_Device)->CreateImage(Unwrap(m_Device), &imCreateInfo, NULL, &image); + CHECK_VKR(this, vkr); + + VkMemoryRequirements imgMrq = {0}; + ObjDisp(m_Device)->GetImageMemoryRequirements(Unwrap(m_Device), image, &imgMrq); + + // we make the memory at least 4MB since that's pretty modest still and gives us enough room to + // try different buffer sizes to determine weird swizzling. + const VkDeviceSize size = 0x400000; + + RDCASSERT(size >= imgMrq.size); + + VkMemoryAllocateFlagsInfo memFlags = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO, 0, + VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT | VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT}; + VkMemoryAllocateInfo allocInfo = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + &memFlags, + size, + GetGPULocalMemoryIndex(imgMrq.memoryTypeBits), + }; + + VkDeviceMemory memory = VK_NULL_HANDLE; + vkr = ObjDisp(m_Device)->AllocateMemory(Unwrap(m_Device), &allocInfo, NULL, &memory); + CHECK_VKR(this, vkr); + + // allocate a buffer onto the memory too. We assume that reasonable usage will not exclude the + // image's memory type + + VkBufferCreateInfo bufInfo = { + VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + NULL, + VK_BUFFER_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT | + VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT, + size, + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | + VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR, + }; + VkBuffer buffer = VK_NULL_HANDLE; + vkr = ObjDisp(m_Device)->CreateBuffer(Unwrap(m_Device), &bufInfo, NULL, &buffer); + CHECK_VKR(this, vkr); + + VkMemoryRequirements bufMrq; + ObjDisp(m_Device)->GetBufferMemoryRequirements(Unwrap(m_Device), buffer, &bufMrq); + if((bufMrq.memoryTypeBits & (1 << allocInfo.memoryTypeIndex)) == 0) + { + RDCERR("Can't detect descriptor types, image memory type can't bind buffer"); + ObjDisp(m_Device)->FreeMemory(Unwrap(m_Device), memory, NULL); + ObjDisp(m_Device)->DestroyImage(Unwrap(m_Device), image, NULL); + ObjDisp(m_Device)->DestroyBuffer(Unwrap(m_Device), buffer, NULL); + return; + } + + ObjDisp(m_Device)->BindBufferMemory(Unwrap(m_Device), buffer, memory, 0); + ObjDisp(m_Device)->BindImageMemory(Unwrap(m_Device), image, memory, 0); + + // create image view + VkImageView imageView = VK_NULL_HANDLE; + + VkImageViewCreateInfo imgViewInfo = { + VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + NULL, + VK_IMAGE_VIEW_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT, + image, + VK_IMAGE_VIEW_TYPE_2D, + VK_FORMAT_R32G32B32A32_SFLOAT, + {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY}, + { + VK_IMAGE_ASPECT_COLOR_BIT, + 0, + VK_REMAINING_MIP_LEVELS, + 0, + 1, + }, + }; + + vkr = ObjDisp(m_Device)->CreateImageView(Unwrap(m_Device), &imgViewInfo, NULL, &imageView); + CHECK_VKR(this, vkr); + + // make a couple of samplers also to be able to decode combined image/sampler layouts + VkSamplerCreateInfo sampInfo = {VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; + sampInfo.minFilter = sampInfo.magFilter = VK_FILTER_NEAREST; + sampInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + sampInfo.addressModeU = sampInfo.addressModeV = sampInfo.addressModeW = + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + sampInfo.mipLodBias = 1.6f; + sampInfo.flags = VK_SAMPLER_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT; + + VkSampler sampler; + vkr = ObjDisp(m_Device)->CreateSampler(Unwrap(m_Device), &sampInfo, NULL, &sampler); + CHECK_VKR(this, vkr); + + VkSampler altSampler; + sampInfo.minFilter = sampInfo.magFilter = VK_FILTER_LINEAR; + sampInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + sampInfo.addressModeU = sampInfo.addressModeV = sampInfo.addressModeW = + VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampInfo.mipLodBias = -1.6f; + vkr = ObjDisp(m_Device)->CreateSampler(Unwrap(m_Device), &sampInfo, NULL, &altSampler); + CHECK_VKR(this, vkr); + + VkBufferDeviceAddressInfo getInfo = { + VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, + NULL, + buffer, + }; + VkDeviceAddress addr = ObjDisp(m_Device)->GetBufferDeviceAddress(Unwrap(m_Device), &getInfo); + + DescriptorTrieNode::rangeToleranceMask = ~0ULL; + + m_DescriptorLookup.uniformBuffer = + EstimateBufferDescriptor(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, addr); + m_DescriptorLookup.storageBuffer = + EstimateBufferDescriptor(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, addr); + // we use a 4-byte format here to distinguish byte size from elem size, but otherwise don't need + // to check multiple formats. There are no possible cases where there's ambiguity between *known* + // descriptor formats. If a descriptor format looks like one of ours for this but not for other + // formats there's not much we can do about that. + // + // we'd slightly prefer a 2-byte format but these aren't required and 4-byte does just as well + m_DescriptorLookup.uniformTexelBuffer = + EstimateBufferDescriptor(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, addr, VK_FORMAT_R32_UINT); + m_DescriptorLookup.storageTexelBuffer = + EstimateBufferDescriptor(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, addr, VK_FORMAT_R32_UINT); + + if(DescriptorDataSize(VK_DESCRIPTOR_TYPE_SAMPLER) == 4 && + DescriptorDataSize(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE) == 4 && + DescriptorDataSize(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) == 4 && + DescriptorDataSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) == 4) + { + // there's only one possible encoding that puts this in 4 bytes + m_DescriptorLookup.storage = m_DescriptorLookup.sampled = ImageDescriptorFormat::Indexed2012; + + m_DescriptorLookup.samplerPalette.resize(0xfff); + m_DescriptorLookup.imageViewPalette.resize(0xfffff); + } + else + { + rdcpair formats[] = { + {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, m_DescriptorLookup.sampled}, + {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, m_DescriptorLookup.storage}, + }; + + VkDescriptorGetInfoEXT info = { + VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT, + NULL, + }; + + VkDescriptorImageInfo imginfo = {}; + info.data.pSampledImage = &imginfo; + imginfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + imginfo.imageView = imageView; + + uint64_t descData[8] = {}; + + // we only compare the bottom 48 bits of pointers (after shifting appropriately) since some + // descriptors stuff things in the upper bits + const uint64_t ptrMask = ((1ULL << 48) - 1); + + // we allow any pointer within the image to work, since some may be offset (not likely for the + // format we have chosen, but just in case) + VkDeviceAddress imgBase = addr & ptrMask; + VkDeviceAddress imgEnd = imgBase + imgMrq.size; + + for(size_t i = 0; i < ARRAY_COUNT(formats); i++) + { + size_t descSize = DescriptorDataSize(formats[i].first); + + info.type = formats[i].first; + + ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, descData); + + if(descSize == 32 && ((descData[0] << 8) & ptrMask) >= imgBase && + ((descData[0] << 8) & ptrMask) < imgEnd) + { + formats[i].second = ImageDescriptorFormat::PointerShifted_32; + } + else if(descSize == 64 && ((descData[0] << 8) & ptrMask) >= imgBase && + ((descData[0] << 8) & ptrMask) < imgEnd) + { + formats[i].second = ImageDescriptorFormat::PointerShifted_64; + } + else if(descSize == 64 && (descData[2] & ptrMask) >= imgBase && (descData[2] & ptrMask) < imgEnd) + { + formats[i].second = ImageDescriptorFormat::Pointer2_64; + } + else if(descSize == 64 && (descData[4] & ptrMask) >= imgBase && (descData[4] & ptrMask) < imgEnd) + { + formats[i].second = ImageDescriptorFormat::Pointer2_64; + } + else + { + RDCERR("Couldn't determine %s descriptor format for image %llx-%llx", + ToStr(info.type).c_str(), imgBase, imgEnd); + // dump the descriptor + for(uint32_t d = 0; d * 8 < descSize; d++) + RDCLOG("[%u]: %llx", d, descData[d]); + } + } + + size_t combinedSize = m_DescriptorBufferProperties.combinedImageSamplerDescriptorSize; + size_t sampledSize = m_DescriptorBufferProperties.sampledImageDescriptorSize; + size_t samplerSize = m_DescriptorBufferProperties.samplerDescriptorSize; + + if(combinedSize == sampledSize + samplerSize) + { + m_DescriptorLookup.combinedSamplerOffset = (uint32_t)sampledSize; + } + else if(combinedSize >= sampledSize + sampledSize) + { + byte combined1[256] = {}; + byte combined2[256] = {}; + + imginfo.sampler = sampler; + info.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + size_t descSize = DescriptorDataSize(info.type); + + ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, combined1); + + imginfo.sampler = altSampler; + + ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, combined2); + + for(uint32_t i = 0; i < combinedSize; i++) + { + if(combined1[i] != combined2[i]) + { + m_DescriptorLookup.combinedSamplerOffset = i; + break; + } + } + + if(m_DescriptorLookup.combinedSamplerOffset < sampledSize) + { + RDCERR( + "Unexpected descriptor difference at byte %u, less than sampled size %u in combined %u", + m_DescriptorLookup.combinedSamplerOffset, sampledSize, combinedSize); + m_DescriptorLookup.combinedSamplerOffset = (uint32_t)sampledSize; + } + else if(m_DescriptorLookup.combinedSamplerOffset + samplerSize < combinedSize) + { + if(memcmp(combined1 + m_DescriptorLookup.combinedSamplerOffset + samplerSize, + combined2 + m_DescriptorLookup.combinedSamplerOffset + samplerSize, + combinedSize - (m_DescriptorLookup.combinedSamplerOffset + samplerSize)) != 0) + { + RDCERR( + "Unexpected descriptor difference after sampler at offset %u + size %u, before end " + "of combined %u bytes", + m_DescriptorLookup.combinedSamplerOffset, samplerSize, combinedSize); + } + } + + RDCLOG("Combined sampler offset is %u into %u byte combined descriptor", + m_DescriptorLookup.combinedSamplerOffset, combinedSize); + } + else + { + RDCLOG("Unexpected combined size %u with sampled size %u and sampler size %u", combinedSize, + sampledSize, samplerSize); + } + } + + // check for AS descriptors too, and do this last as it stomps the union a bit + if(AccelerationStructures()) + { + VkAccelerationStructureKHR as = VK_NULL_HANDLE; + const VkAccelerationStructureCreateInfoKHR asCreateInfo = { + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR, + NULL, + VK_ACCELERATION_STRUCTURE_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT, + buffer, + 0, + size, + VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR, + 0x0, + }; + vkr = ObjDisp(m_Device)->CreateAccelerationStructureKHR(Unwrap(m_Device), &asCreateInfo, NULL, + &as); + CHECK_VKR(this, vkr); + + VkAccelerationStructureDeviceAddressInfoKHR asGetInfo = { + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR, + NULL, + as, + }; + + VkDeviceAddress asAddr = + ObjDisp(m_Device)->GetAccelerationStructureDeviceAddressKHR(Unwrap(m_Device), &asGetInfo); + + m_DescriptorLookup.accelStructure = + EstimateBufferDescriptor(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, asAddr); + + ObjDisp(m_Device)->DestroyAccelerationStructureKHR(Unwrap(m_Device), as, NULL); + } + + // shutdown and destroy the objects we made + ObjDisp(m_Device)->DestroySampler(Unwrap(m_Device), sampler, NULL); + ObjDisp(m_Device)->DestroySampler(Unwrap(m_Device), altSampler, NULL); + ObjDisp(m_Device)->DestroyImageView(Unwrap(m_Device), imageView, NULL); + ObjDisp(m_Device)->DestroyImage(Unwrap(m_Device), image, NULL); + ObjDisp(m_Device)->DestroyBuffer(Unwrap(m_Device), buffer, NULL); + ObjDisp(m_Device)->FreeMemory(Unwrap(m_Device), memory, NULL); + + RDCLOG("Descriptor format estimates:"); + RDCLOG(" Uniform buffers: %s", ToStr(m_DescriptorLookup.uniformBuffer).c_str()); + RDCLOG(" Storage buffers: %s", ToStr(m_DescriptorLookup.storageBuffer).c_str()); + RDCLOG(" Uniform texel buffers: %s", ToStr(m_DescriptorLookup.uniformTexelBuffer).c_str()); + RDCLOG(" Storage texel buffers: %s", ToStr(m_DescriptorLookup.storageTexelBuffer).c_str()); + RDCLOG(" Accel Structs: %s", ToStr(m_DescriptorLookup.accelStructure).c_str()); + RDCLOG(" Sampled images: %s", ToStr(m_DescriptorLookup.sampled).c_str()); + RDCLOG(" Storage images: %s", ToStr(m_DescriptorLookup.storage).c_str()); +} + +BufferDescriptorFormat WrappedVulkan::EstimateBufferDescriptor(VkDescriptorType type, + VkDeviceAddress addr, + VkFormat texelFormat) +{ + BufferDescriptorFormat outFormat = BufferDescriptorFormat::UnknownBufferDescriptor; + + union + { + byte descriptorBytes[256]; + uint64_t descriptorU64[32]; + }; + + VkDescriptorGetInfoEXT info = { + VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT, + }; + + info.type = type; + + const uint64_t texelSize = + texelFormat == VK_FORMAT_UNDEFINED ? 1 : GetByteSize(1, 1, 1, texelFormat, 0); + + // start with a size that will never run afoul of alignment problems but isn't likely to be + // misidentified by a random bit. This also stays under 64k which is the minimum limit for some buffers + VkDeviceAddress byteSize = 0xd300; + VkDeviceAddress elemSize = byteSize / texelSize; + + size_t descSize = DescriptorDataSize(info.type); + + VkDescriptorAddressInfoEXT bufinfo = {VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT}; + + if(info.type != VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR) + { + bufinfo.address = addr; + bufinfo.range = byteSize; + bufinfo.format = texelFormat; + info.data.pUniformBuffer = &bufinfo; + } + else + { + info.data.accelerationStructure = addr; + } + + ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, descriptorBytes); + + // we only compare the bottom 48 bits of pointers (after shifting appropriately) since some + // descriptors stuff things in the upper bits + const uint64_t ptrMask = ((1ULL << 48) - 1); + + addr &= ptrMask; + + if(addr == 0) + { + RDCERR("Invalid address returned"); + } + else if(type == VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR) + { + if(descSize == 8 && (descriptorU64[0] & ptrMask) == addr) + outFormat = BufferDescriptorFormat::Pointer_8; + else if(descSize == 16 && (descriptorU64[0] & ptrMask) == addr) + outFormat = BufferDescriptorFormat::Pointer0_16; + else if(descSize == 32 && (descriptorU64[1] & ptrMask) == addr) + outFormat = BufferDescriptorFormat::Pointer1_32; + else if(descSize == 64 && (descriptorU64[2] & ptrMask) == addr) + outFormat = BufferDescriptorFormat::Pointer2_64; + } + else if(descSize == 8 && descriptorU64[0] == ((bufinfo.address >> 4) | ((byteSize >> 4) << 45))) + { + // check alignment + bufinfo.range = byteSize = 16; + ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, descriptorBytes); + + if((descriptorU64[0] & ptrMask) == ((bufinfo.address >> 4) | ((byteSize >> 4) << 45))) + { + outFormat = BufferDescriptorFormat::Packed_4519_Aligned16_8; + DescriptorTrieNode::rangeToleranceMask &= 0xFULL; + } + else if((descriptorU64[0] & ptrMask) == ((bufinfo.address >> 4) | ((256ULL >> 4) << 45))) + { + outFormat = BufferDescriptorFormat::Packed_4519_Aligned256_8; + DescriptorTrieNode::rangeToleranceMask &= 0xFFULL; + } + else + { + outFormat = BufferDescriptorFormat::UnknownBufferDescriptor; + } + } + else if(descSize == 16) + { + if((descriptorU64[0] & ptrMask) == (bufinfo.address & ptrMask) && + (descriptorU64[1] & 0xffffffff) == elemSize) + outFormat = BufferDescriptorFormat::Pointer_ElemSize_16; + else if((descriptorU64[0] & ptrMask) == ((bufinfo.address & ptrMask) / texelSize) && + (descriptorU64[1] & 0xffffffff) == elemSize) + outFormat = BufferDescriptorFormat::PointerDivided_ElemSize_16; + } + else if(descSize == 32) + { + if((descriptorU64[1] & ptrMask) == (bufinfo.address & ptrMask) && + (descriptorU64[0] >> 32) == byteSize) + outFormat = BufferDescriptorFormat::ByteSize0_Pointer1_32; + if((descriptorU64[4] & ptrMask) == (bufinfo.address & ptrMask) && + (descriptorU64[5] >> 32) == byteSize) + outFormat = BufferDescriptorFormat::Pointer4_ByteSize5_Unaligned_64; + } + else if(descSize == 64) + { + // don't check ElemSize0_Pointer2_64 here due to possible aliasing with Strided*_MultiDescriptor_64 + /* + if((descriptorU64[2] & ptrMask) == bufinfo.address && (descriptorU64[0] >> 32) == elemSize) + { + outFormat = BufferDescriptorFormat::ElemSize0_Pointer2_64; + } + else + */ + + if((descriptorU64[4] & ptrMask) == (bufinfo.address & ptrMask) && + (descriptorU64[5] >> 32) == byteSize) + { + // check alignment + bufinfo.range = 16; + ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, descriptorBytes); + + if((descriptorU64[4] & ptrMask) == (bufinfo.address & ptrMask) && (descriptorU64[5] >> 32) == 64) + { + outFormat = BufferDescriptorFormat::Pointer4_ByteSize5_Aligned_64; + DescriptorTrieNode::rangeToleranceMask &= 0x3FULL; + } + else if((descriptorU64[4] & ptrMask) == (bufinfo.address & ptrMask) && + (descriptorU64[5] >> 32) == 16) + { + outFormat = BufferDescriptorFormat::Pointer4_ByteSize5_Unaligned_64; + } + } + else if((descriptorU64[4] & ptrMask) == (bufinfo.address & ptrMask)) + { + // check for complex scattering, we sized the memory large enough for 3 million specifically to test this + uint64_t inputs[3] = {256, 200, 3000000}; + uint64_t scattered[3] = {descriptorU64[1]}; + + bufinfo.range = inputs[1]; + ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, descriptorBytes); + scattered[1] = descriptorU64[1]; + + bufinfo.range = inputs[2]; + ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, descriptorBytes); + scattered[2] = descriptorU64[1]; + + bool match = true; + for(size_t i = 0; i < ARRAY_COUNT(inputs); i++) + { + uint64_t num = inputs[i] - 1; + if(texelSize > 1) + num = (inputs[i] / texelSize) - 1; + + if(texelSize == 1) + { + uint8_t x = num & 0xff; + num = (num & ~0xff) + ((x & 0xfc) + 6 - (x & 0x3)); + } + else if(texelSize == 2) + { + uint8_t x = num & 0xff; + num = (num & ~0xff) + ((x & 0xfe) + 2 - (x & 0x1)); + } + + uint64_t expected = ((num & 0x00007f) << 0) | ((num & 0x1fff80) << 9) | ((num >> 21) << 53); + if(expected != (scattered[i] & 0xffe00000001fffffULL)) + { + match = false; + break; + } + } + + if(match) + outFormat = BufferDescriptorFormat::ElemSizeScattered1_Pointer4_64; + } + } + + // variable size descriptors + if(outFormat == BufferDescriptorFormat::UnknownBufferDescriptor && + (descSize == 64 || descSize == 128)) + { + // check with non-64byte aligned pointer just to check. 16 bytes is safe for all possible inputs + bufinfo.address += 16; + ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descSize, descriptorBytes); + + VkDeviceAddress remainder = bufinfo.address & 0x3f; + VkDeviceAddress alignedAddr = (bufinfo.address & ptrMask) - remainder; + RDCASSERT(remainder != 0, bufinfo.address); + + BufferDescriptorFormat fmts[] = { + BufferDescriptorFormat::Strided4_MultiDescriptor_64, + BufferDescriptorFormat::Strided2_MultiDescriptor_64, + BufferDescriptorFormat::Strided1_MultiDescriptor_64, + }; + uint32_t strides[] = { + 4, + 2, + 1, + }; + RDCCOMPILE_ASSERT(ARRAY_COUNT(fmts) == ARRAY_COUNT(strides), + "Strides don't match number of descriptor formats"); + + for(size_t i = 0; i < ARRAY_COUNT(fmts); i++) + { + uint32_t stride = strides[i]; + + if((descriptorU64[2] & ptrMask) == alignedAddr && + descriptorU64[0] >> 32 == (byteSize >> stride) && + ((descriptorU64[1] >> 16) & 0x3f) == (remainder >> stride)) + { + outFormat = fmts[i]; + break; + } + } + + if(outFormat == BufferDescriptorFormat::UnknownBufferDescriptor && + (descriptorU64[2] & ptrMask) == (bufinfo.address & ptrMask) && + (descriptorU64[0] >> 32) == elemSize) + { + outFormat = BufferDescriptorFormat::ElemSize0_Pointer2_64; + } + } + + if(outFormat == BufferDescriptorFormat::UnknownBufferDescriptor) + { + RDCERR("Couldn't determine %s descriptor format for address %llx range %llx", + ToStr(info.type).c_str(), addr, bufinfo.range); + // dump the descriptor + for(uint32_t i = 0; i * 8 < descSize; i++) + RDCLOG("[%u]: %llx", i, descriptorU64[i]); + } + + return outFormat; +} + +void WrappedVulkan::LookupDescriptor(byte *descriptorBytes, size_t descriptorSize, + DescriptorType type, DescriptorSetSlot &data) +{ + const size_t combinedSize = m_DescriptorBufferProperties.combinedImageSamplerDescriptorSize; + const size_t sampledSize = m_DescriptorBufferProperties.sampledImageDescriptorSize; + const size_t samplerSize = m_DescriptorBufferProperties.samplerDescriptorSize; + + union + { + VkDescriptorImageInfo imInfo; + VkDescriptorAddressInfoEXT bufinfo; + VkSampler sampler; + }; + + bufinfo = {}; + + VkDescriptorGetInfoEXT info = { + VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT, + }; + + info.data.pUniformBuffer = &bufinfo; + + byte tempMem[256] = {}; + // start with the descriptor bytes in case the driver doesn't initialise them all. If this + // contains random bytes from user memory we want it to match + memcpy(tempMem, descriptorBytes, descriptorSize); + if(Vulkan_Debug_UseFastDescriptorLookup()) + { + switch(type) + { + case DescriptorType::Sampler: + { + ResourceId samp = GetSamplerForDescriptor(descriptorBytes, descriptorSize); + + if(samp != ResourceId()) + { + data = {}; + data.SetSampler(samp); + + // verify that descriptor roundtrips that our detection was correct + info.type = VK_DESCRIPTOR_TYPE_SAMPLER; + sampler = Unwrap(GetResourceManager()->GetCurrentHandle(samp)); + + ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descriptorSize, tempMem); + + if(memcmp(tempMem, descriptorBytes, descriptorSize) == 0) + return; + } + + break; + } + case DescriptorType::ImageSampler: + case DescriptorType::Image: + case DescriptorType::ReadWriteImage: + { + if(type == DescriptorType::ImageSampler) + info.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + else if(type == DescriptorType::Image) + info.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + else if(type == DescriptorType::ReadWriteImage) + info.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + + ResourceId samp; + ResourceId view; + + if(type == DescriptorType::ImageSampler) + { + if(m_DescriptorLookup.sampled == ImageDescriptorFormat::Indexed2012) + { + // for the indexed format, the sampler/view are encoded together + view = GetImageViewForDescriptor(descriptorBytes, descriptorSize, type); + samp = GetSamplerForDescriptor(descriptorBytes, descriptorSize); + } + else if(m_DescriptorLookup.sampled == ImageDescriptorFormat::UnknownImageDescriptor) + { + break; + } + else + { + // all known formats we expect an image view to be followed by a sampler + if(combinedSize == m_DescriptorLookup.combinedSamplerOffset + samplerSize) + { + view = GetImageViewForDescriptor(descriptorBytes, sampledSize, type); + samp = GetSamplerForDescriptor( + descriptorBytes + m_DescriptorLookup.combinedSamplerOffset, samplerSize); + } + else + { + RDCWARN( + "non-indexed combined image/sampler is not (padded) image followed by sampler"); + } + } + + if(samp == ResourceId()) + { + RDCWARN("Fast-detection failed to get sampler for image/sampler descriptor"); + break; + } + } + else + { + view = GetImageViewForDescriptor(descriptorBytes, descriptorSize, type); + } + + // exit silently, may be unknown descriptor format which would spam + if(view == ResourceId()) + { + // check if this is a NULL descriptor, in which case we've identified correctly! + // only works if we don't need to care about the layout + if(m_IgnoreLayoutForDescriptors && + m_DescriptorLookup.nullPatterns[(uint32_t)convert(info.type)] == + bytebuf(descriptorBytes, descriptorSize)) + { + data = {}; + data.SetImageSampler(info.type, ResourceId(), ResourceId(), VK_IMAGE_LAYOUT_GENERAL); + return; + } + break; + } + + rdcarray layouts; + + if(!m_IgnoreLayoutForDescriptors) + { + layouts = m_DescriptorLookup.generalImageLayouts; + + if(m_CreationInfo.m_ImageView[view].isDepthImage) + layouts.append(m_DescriptorLookup.depthImageLayouts); + } + + imInfo.sampler = Unwrap(GetResourceManager()->GetCurrentHandle(samp)); + imInfo.imageView = Unwrap(GetResourceManager()->GetCurrentHandle(view)); + + // always iterate at least once even if the layouts array is empty + for(size_t i = 0; i < layouts.size() || (i == 0 && layouts.empty()); i++) + { + imInfo.imageLayout = i < layouts.size() ? layouts[i] : VK_IMAGE_LAYOUT_GENERAL; + + ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descriptorSize, tempMem); + + if(memcmp(tempMem, descriptorBytes, descriptorSize) == 0) + { + data.SetImageSampler(info.type, view, samp, imInfo.imageLayout); + return; + } + } + + RDCWARN("Fast-detection failed for %s descriptor", ToStr(type).c_str()); + + break; + } + case DescriptorType::TypedBuffer: + case DescriptorType::ReadWriteTypedBuffer: + case DescriptorType::ConstantBuffer: + case DescriptorType::ReadWriteBuffer: + case DescriptorType::AccelerationStructure: + { + if(type == DescriptorType::TypedBuffer) + info.type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + else if(type == DescriptorType::ReadWriteTypedBuffer) + info.type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; + else if(type == DescriptorType::ConstantBuffer) + info.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + else if(type == DescriptorType::ReadWriteBuffer) + info.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + else if(type == DescriptorType::AccelerationStructure) + info.type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; + + const bool texelBuffer = + type == DescriptorType::TypedBuffer || type == DescriptorType::ReadWriteTypedBuffer; + + VkDeviceAddress address; + VkDeviceSize size; + + GetPointerAndSizeForDescriptor(descriptorBytes, descriptorSize, type, address, size); + + if(address == 0) + { + // exit silently, may be unknown descriptor format which would spam + // check if this is a NULL descriptor, in which case we've identified correctly! + // can't work for texel buffers that may have a format encoded + if(type != DescriptorType::TypedBuffer && type != DescriptorType::ReadWriteTypedBuffer && + m_DescriptorLookup.nullPatterns[(uint32_t)convert(info.type)] == + bytebuf(descriptorBytes, descriptorSize)) + { + data = {}; + if(type == DescriptorType::AccelerationStructure) + data.SetAccelerationStructure(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, + VK_NULL_HANDLE); + else + data.SetBuffer(info.type, ResourceId(), 0, 0, VK_FORMAT_UNDEFINED); + return; + } + break; + } + else + { + if(type == DescriptorType::AccelerationStructure) + { + info.data.accelerationStructure = address; + } + else + { + bufinfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT; + bufinfo.address = address; + bufinfo.range = size; + } + + bufinfo.format = VK_FORMAT_UNDEFINED; + for(size_t i = 0, n = texelBuffer ? m_DescriptorLookup.texelFormats.size() : 1; i < n; i++) + { + if(texelBuffer) + bufinfo.format = m_DescriptorLookup.texelFormats[i]; + + if(type != DescriptorType::AccelerationStructure) + { + // a couple of formats modify the address or size in non-trivial ways that need to be patched + // here. This also handles converting an element size back into a byte size trivially + GetFinalBufferParameters(descriptorBytes, descriptorSize, type, bufinfo.format, + address, size, bufinfo.address, bufinfo.range); + } + + ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descriptorSize, tempMem); + + if(memcmp(tempMem, descriptorBytes, descriptorSize) != 0) + { + // try sign extending if the top bit is set + address |= (0xffffULL << 48); + if(type == DescriptorType::AccelerationStructure) + info.data.accelerationStructure = address; + else + bufinfo.address = address; + + ObjDisp(m_Device)->GetDescriptorEXT(Unwrap(m_Device), &info, descriptorSize, tempMem); + } + + if(memcmp(tempMem, descriptorBytes, descriptorSize) == 0) + { + ResourceId id; + VkDeviceSize offs; + + if(type != DescriptorType::AccelerationStructure) + { + GetResIDFromAddr(bufinfo.address, id, offs); + + if(id == ResourceId() && (bufinfo.address & (1ULL << 47))) + { + // try sign extending if the top bit is set + GetResIDFromAddr(bufinfo.address | (0xffffULL << 48), id, offs); + } + + if(id == ResourceId()) + { + RDCWARN("Unknown buffer at descriptor address %llx", bufinfo.address); + } + else + { + data.SetBuffer(info.type, id, offs, bufinfo.range, bufinfo.format); + + return; + } + } + else + { + id = m_ASLookupByAddr[address]; + + if(id == ResourceId() && (address & (1ULL << 47))) + { + // try sign extending if the top bit is set + id = m_ASLookupByAddr[address | (0xffffULL << 48)]; + } + + if(id == ResourceId()) + { + RDCWARN("Unknown AS at descriptor address %llx", address); + } + else + { + data.SetAccelerationStructure( + VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, + GetResourceManager()->GetCurrentHandle(id)); + + return; + } + } + } + } + + RDCWARN("Fast-detection failed to get match for %s descriptor", ToStr(type).c_str()); + } + + break; + } + case DescriptorType::Buffer: + case DescriptorType::Unknown: RDCERR("Invalid descriptor type being looked up"); break; + } + } + + data = m_DescriptorLookup.fallback.lookup({descriptorBytes, descriptorSize}); + +#if ENABLED(RDOC_DEVEL) + if(!m_DescriptorLookup.fallback.contains({descriptorBytes, descriptorSize})) + { + RDCERR("Trie descriptor lookup failed"); + // dump the descriptor + uint64_t *descriptorU64 = (uint64_t *)descriptorBytes; + for(uint32_t i = 0; i * 8 < descriptorSize; i++) + RDCLOG(" [%u]: %llx", i, descriptorU64[i]); + } +#endif +} + +ResourceId WrappedVulkan::GetSamplerForDescriptor(byte *descriptorBytes, size_t descriptorSize) +{ + if(m_DescriptorLookup.sampled == ImageDescriptorFormat::Indexed2012) + { + if(descriptorSize == sizeof(uint32_t)) + { + uint32_t idx = (*(uint32_t *)descriptorBytes) >> 20; + + if(idx > 0 && idx < m_DescriptorLookup.samplerPalette.size()) + { + return m_DescriptorLookup.samplerPalette[idx]; + } + + RDCWARN("Indexed sampler descriptor index is %u", idx); + return ResourceId(); + } + + RDCWARN("Indexed sampler descriptor is %zu bytes", descriptorSize); + return ResourceId(); + } + + DescriptorTrieNode data = m_DescriptorLookup.samplers.lookup({descriptorBytes, descriptorSize}); + // we should find this + if(data.sampler == ResourceId()) + RDCWARN("Couldn't find solo sampler in sampler lookup trie"); + + return data.sampler; +} + +ResourceId WrappedVulkan::GetImageViewForDescriptor(byte *descriptorBytes, size_t descriptorSize, + DescriptorType type) +{ + ImageDescriptorFormat format = m_DescriptorLookup.sampled; + if(type == DescriptorType::ReadWriteImage) + format = m_DescriptorLookup.storage; + + // we assumed if one image type is indexed, all are + if(format == ImageDescriptorFormat::Indexed2012) + { + if(descriptorSize == sizeof(uint32_t)) + { + uint32_t idx = (*(uint32_t *)descriptorBytes) & 0xfffff; + + if(idx > 0 && idx < m_DescriptorLookup.imageViewPalette.size()) + { + return m_DescriptorLookup.imageViewPalette[idx]; + } + + RDCWARN("Indexed view descriptor index is %u", idx); + return ResourceId(); + } + + RDCWARN("Indexed view descriptor is %zu bytes", descriptorSize); + return ResourceId(); + } + + // other descriptors are recognised by pointer + uint64_t ptr = 0; + + if(format == ImageDescriptorFormat::PointerShifted_32 || + format == ImageDescriptorFormat::PointerShifted_64) + { + if(descriptorSize == 32 || descriptorSize == 64) + { + ptr = (((uint64_t *)descriptorBytes)[0] << 8) & ((1ULL << 48) - 1); + } + else + { + RDCWARN("Unexpected descriptor format for detected format %u: %u", format, descriptorSize); + return ResourceId(); + } + } + else if(format == ImageDescriptorFormat::Pointer2_64) + { + if(descriptorSize == 64) + { + ptr = ((uint64_t *)descriptorBytes)[2] & ((1ULL << 48) - 1); + } + else + { + RDCWARN("Unexpected descriptor format for detected format %u: %u", format, descriptorSize); + return ResourceId(); + } + } + else if(format == ImageDescriptorFormat::Pointer4_64) + { + if(descriptorSize == 64) + { + ptr = ((uint64_t *)descriptorBytes)[4] & ((1ULL << 48) - 1); + } + else + { + RDCWARN("Unexpected descriptor format for detected format %u: %u", format, descriptorSize); + return ResourceId(); + } + } + else + { + return ResourceId(); + } + + ResourceId imageId; + uint64_t unused; + m_DescriptorLookup.imageAddresses.GetResIDFromAddr(ptr, imageId, unused); + + if(imageId == ResourceId() && (ptr & (1ULL << 47))) + { + // try sign extending if the top bit is set + m_DescriptorLookup.imageAddresses.GetResIDFromAddr(ptr | (0xffffULL << 48), imageId, unused); + } + + if(imageId == ResourceId()) + { + RDCWARN("View descriptor gave unrecognised pointer %llx", ptr); + return ResourceId(); + } + + ResourceId viewId = + m_CreationInfo.m_Image[imageId].getViewFromDescriptor(descriptorBytes, descriptorSize); + + if(viewId == ResourceId()) + { + RDCWARN("View descriptor gave pointer %llx for image %s but was unrecognised", ptr, + ToStr(imageId).c_str()); + return ResourceId(); + } + + return viewId; +} + +void WrappedVulkan::GetPointerAndSizeForDescriptor(byte *descriptorBytes, size_t descriptorSize, + DescriptorType type, VkDeviceAddress &address, + VkDeviceSize &size) +{ + address = 0; + size = 0; + + BufferDescriptorFormat format = m_DescriptorLookup.uniformBuffer; + if(type == DescriptorType::ReadWriteBuffer) + format = m_DescriptorLookup.storageBuffer; + else if(type == DescriptorType::TypedBuffer) + format = m_DescriptorLookup.uniformTexelBuffer; + else if(type == DescriptorType::ReadWriteTypedBuffer) + format = m_DescriptorLookup.storageTexelBuffer; + else if(type == DescriptorType::AccelerationStructure) + format = m_DescriptorLookup.accelStructure; + + if(format == BufferDescriptorFormat::Pointer_8 && descriptorSize == sizeof(uint64_t)) + { + uint64_t *packed = (uint64_t *)descriptorBytes; + address = (packed[0] & ((1ULL << 48) - 1)); + } + else if(format == BufferDescriptorFormat::Packed_4519_Aligned16_8 && + descriptorSize == sizeof(uint64_t)) + { + uint64_t packed = *(uint64_t *)descriptorBytes; + address = (packed & ((1ULL << 45) - 1)) << 4; + size = (packed >> 45) << 4; + } + else if(format == BufferDescriptorFormat::Packed_4519_Aligned256_8 && + descriptorSize == sizeof(uint64_t)) + { + uint64_t packed = *(uint64_t *)descriptorBytes; + address = (packed & ((1ULL << 45) - 1)) << 4; + size = (packed >> 45) << 4; + } + else if(format == BufferDescriptorFormat::Pointer_ElemSize_16 && + descriptorSize == sizeof(uint64_t) * 2) + { + uint64_t *packed = (uint64_t *)descriptorBytes; + address = (packed[0] & ((1ULL << 48) - 1)); + size = (packed[1] & 0xFFFFFFFFULL); + } + else if(format == BufferDescriptorFormat::PointerDivided_ElemSize_16 && + descriptorSize == sizeof(uint64_t) * 2) + { + uint64_t *packed = (uint64_t *)descriptorBytes; + address = (packed[0] & ((1ULL << 48) - 1)); + size = (packed[1] & 0xFFFFFFFFULL); + + // address is incomplete, but can't be fixed until we know the texel size + } + else if(format == BufferDescriptorFormat::Pointer0_16 && descriptorSize == sizeof(uint64_t) * 2) + { + uint64_t *packed = (uint64_t *)descriptorBytes; + address = (packed[0] & ((1ULL << 48) - 1)); + } + else if(format == BufferDescriptorFormat::ByteSize0_Pointer1_32 && + descriptorSize == sizeof(uint64_t) * 4) + { + uint64_t *packed = (uint64_t *)descriptorBytes; + address = (packed[1] & ((1ULL << 48) - 1)); + size = (packed[0] >> 32); + } + else if(format == BufferDescriptorFormat::Pointer1_32 && descriptorSize == sizeof(uint64_t) * 4) + { + uint64_t *packed = (uint64_t *)descriptorBytes; + address = (packed[1] & ((1ULL << 48) - 1)); + } + else if(format == BufferDescriptorFormat::Pointer2_64 && descriptorSize == sizeof(uint64_t) * 4) + { + uint64_t *packed = (uint64_t *)descriptorBytes; + address = (packed[2] & ((1ULL << 48) - 1)); + } + else if(format == BufferDescriptorFormat::Pointer4_ByteSize5_Unaligned_64 && + descriptorSize == sizeof(uint64_t) * 8) + { + uint64_t *packed = (uint64_t *)descriptorBytes; + address = (packed[4] & ((1ULL << 48) - 1)); + size = (packed[5] >> 32); + } + else if(format == BufferDescriptorFormat::Pointer4_ByteSize5_Aligned_64 && + descriptorSize == sizeof(uint64_t) * 8) + { + uint64_t *packed = (uint64_t *)descriptorBytes; + address = (packed[4] & ((1ULL << 48) - 1)); + size = (packed[5] >> 32); + } + else if(format == BufferDescriptorFormat::ElemSizeScattered1_Pointer4_64 && + descriptorSize == sizeof(uint64_t) * 8) + { + uint64_t *packed = (uint64_t *)descriptorBytes; + address = (packed[4] & ((1ULL << 48) - 1)); + + uint64_t sizeScattered = packed[1]; + size = (sizeScattered & 0x7f) | ((sizeScattered >> 9) & 0x1fff80) | ((sizeScattered >> 53) << 21); + + // this is still swizzled on the low bits, but we won't know how to decode that until we have the texel format + } + else if((format == BufferDescriptorFormat::Strided4_MultiDescriptor_64 || + format == BufferDescriptorFormat::Strided2_MultiDescriptor_64 || + format == BufferDescriptorFormat::Strided1_MultiDescriptor_64) && + (descriptorSize == sizeof(uint64_t) * 8 * 1 || descriptorSize == sizeof(uint64_t) * 8 * 2)) + { + const uint32_t stride = format == BufferDescriptorFormat::Strided4_MultiDescriptor_64 ? 4 + : format == BufferDescriptorFormat::Strided2_MultiDescriptor_64 ? 2 + : 1; + + uint64_t *packed = (uint64_t *)descriptorBytes; + address = (packed[2] & ((1ULL << 48) - 1)); + + address += ((packed[1] >> 16) & 0x3f) << stride; + + size = (packed[0] >> 32) << stride; + } + else if(format == BufferDescriptorFormat::ElemSize0_Pointer2_64 && + descriptorSize == sizeof(uint64_t) * 8) + { + uint64_t *packed = (uint64_t *)descriptorBytes; + address = (packed[2] & ((1ULL << 48) - 1)); + size = (packed[0] >> 32); + } +} + +void WrappedVulkan::GetFinalBufferParameters(byte *descriptorBytes, size_t descriptorSize, + DescriptorType type, VkFormat texelFormat, + VkDeviceAddress inAddress, VkDeviceSize inSize, + VkDeviceAddress &outAddress, VkDeviceSize &outSize) +{ + BufferDescriptorFormat format = m_DescriptorLookup.uniformBuffer; + + if(type == DescriptorType::ReadWriteBuffer) + format = m_DescriptorLookup.storageBuffer; + else if(type == DescriptorType::TypedBuffer) + format = m_DescriptorLookup.uniformTexelBuffer; + else if(type == DescriptorType::ReadWriteTypedBuffer) + format = m_DescriptorLookup.storageTexelBuffer; + else if(type == DescriptorType::AccelerationStructure) + format = m_DescriptorLookup.accelStructure; + + uint32_t elemSize = 1; + if(type == DescriptorType::TypedBuffer || type == DescriptorType::ReadWriteTypedBuffer) + elemSize = GetByteSize(1, 1, 1, texelFormat, 0) & 0xffff; + + if(format == BufferDescriptorFormat::ElemSize0_Pointer2_64 || + format == BufferDescriptorFormat::Pointer_ElemSize_16) + { + outAddress = inAddress; + outSize = inSize * elemSize; + } + else if(format == BufferDescriptorFormat::PointerDivided_ElemSize_16) + { + outAddress = inAddress * elemSize; + + // for elemSize==12 the address didn't divide evenly so we need to grab the remainder + if(elemSize == 12) + { + uint64_t *packed = (uint64_t *)descriptorBytes; + uint64_t remainder = (packed[1] >> 32) & 0x3f; + // the actual pattern to this is entirely unknown and it's assumed to be some internal base + // offset, but this seems consistent as the 3 remainders (0, 4, 8 bytes) are always 23 apart in these bits + outAddress += 4 * (remainder / 23); + } + + // outAddress += remainder; + outSize = inSize * elemSize; + } + else if(format == BufferDescriptorFormat::ElemSizeScattered1_Pointer4_64) + { + outAddress = inAddress; + + if(elemSize >= 4) + { + outSize = (inSize + 1) * elemSize; + } + else + { + // unswizzle the 2-byte/1-byte size. There's probably a fancier way to express this + // bit-twiddling but it's more readable to have this verbosely specified + // + // the general scheme for encoding is: + // + // lop off the bottom 8 bits, which we call 'x' + // + // swizzle those bits in this formula: + // 1 byte elements: ((x & 0xfc) + 6 - (x & 0x3)) + // 2 byte elements: ((x & 0xfe) + 2 - (x & 0x1)) + // + // note that this means the bottom bits can be swizzled into 9 bits of data and increment into + // the upper bits, so we need to deal with carrying. E.g. for x = 0xfd this produces 0x102 result + + const uint32_t upperMask = elemSize == 1 ? 0xfc : 0xfe; + const uint32_t lowerMask = 0xff - upperMask; + const uint32_t offset = lowerMask << 1; + + // segment the lower swizzled bits. There may be leakage due to the carry bit but we handle that + const uint64_t upperSize = inSize & ~0xff; + const uint32_t lowerSize = inSize & 0xff; + + // need a carry bit for some cases, this will be subtracted later + const uint32_t carry = lowerSize < offset ? 128 : 0; + + // this is (mostly) the result of the (x & 0xfc) - (x & 0x3) subtraction + const uint32_t xsubbed = lowerSize + carry - offset; + + // assuming a lower mask of 0x3 (bottom two bits) if xsubbed ends in 00 then it must have been + // aligned, if it ended in 11 then it must have been 01 subtracted from the value above, etc. + // this will give us the original bottom two bits of x by subtracting and masking + const uint32_t xlow = ((lowerMask + 1) - (xsubbed & lowerMask)) & lowerMask; + + // the upper bits of the size are added on unconditionally, we also undo the +1 to the range here + outSize = upperSize + 1; + + // no bits = no subtraction! x must have been aligned when we did the sum so just the upperMask bits are used + if(xlow == 0) + outSize += xsubbed; + else + // xlow had some bits, so we figure out what it must have been subtracted from and add that to the upper mask + outSize += ((xsubbed & upperMask) + (lowerMask + 1) + xlow); + + // subtract any carry we added now + outSize -= carry; + + // finally convert to bytes + outSize *= elemSize; + } + } + else + { + // byte sizes, no translation needed + outAddress = inAddress; + outSize = inSize; + } +} + +void WrappedVulkan::RegisterDescriptor(const bytebuf &key, const DescriptorSetSlot &data) +{ + m_DescriptorLookup.fallback.insert(key, data); + + // only register NULL patterns for non-sampler types + if(data.type != DescriptorSlotType::Sampler && + data.type != DescriptorSlotType::CombinedImageSampler && data.resource == ResourceId()) + m_DescriptorLookup.nullPatterns[(uint32_t)data.type] = key; + + // store unique texel buffer formats used, expecting this to be small and it will help descriptor lookups + if(data.type == DescriptorSlotType::UniformTexelBuffer || + data.type == DescriptorSlotType::StorageTexelBuffer) + { + VkFormat fmt = VkFormat(data.imageLayoutOrFormat); + if(data.resource != ResourceId() && !m_DescriptorLookup.texelFormats.contains(fmt)) + m_DescriptorLookup.texelFormats.push_back(fmt); + } + else if(data.type == DescriptorSlotType::CombinedImageSampler || + data.type == DescriptorSlotType::SampledImage || + data.type == DescriptorSlotType::StorageImage || + data.type == DescriptorSlotType::InputAttachment) + { + VkImageLayout layout = convert(data.imageLayoutOrFormat); + + if(layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL || + layout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL || + layout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL || + layout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL || + layout == VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL) + { + if(!m_DescriptorLookup.depthImageLayouts.contains(layout)) + m_DescriptorLookup.depthImageLayouts.push_back(layout); + } + else + { + if(!m_DescriptorLookup.generalImageLayouts.contains(layout)) + { + m_DescriptorLookup.generalImageLayouts.push_back(layout); + // keep the list sorted so that rare/niche layouts like feedback loop or local read are tried last + std::sort(m_DescriptorLookup.generalImageLayouts.begin(), + m_DescriptorLookup.generalImageLayouts.end()); + } + } + } + + size_t combinedSize = m_DescriptorBufferProperties.combinedImageSamplerDescriptorSize; + size_t sampledSize = m_DescriptorBufferProperties.sampledImageDescriptorSize; + size_t samplerSize = m_DescriptorBufferProperties.samplerDescriptorSize; + + // if this is just a sampler descriptor, store it directly (unless we're not using indexed) + if(data.type == DescriptorSlotType::Sampler && + m_DescriptorLookup.sampled != ImageDescriptorFormat::Indexed2012) + { + m_DescriptorLookup.samplers.insert(key, data); + } + // if this is a combined descriptor but it looks like it's an image+sampler (which is common) and + // we're not indexed, store the second part as sampler bytes. This may be wrong, but that's fine + // and worst case we pollute the samplers lookup and fail to do a fast lookup of this descriptor + else if(data.type == DescriptorSlotType::CombinedImageSampler && + combinedSize == m_DescriptorLookup.combinedSamplerOffset + samplerSize) + { + DescriptorSetSlot samplerData; + samplerData.SetSampler(data.sampler); + + m_DescriptorLookup.samplers.insert( + {key.data() + m_DescriptorLookup.combinedSamplerOffset, samplerSize}, samplerData); + } + + if((data.type == DescriptorSlotType::InputAttachment || + data.type == DescriptorSlotType::StorageImage || data.type == DescriptorSlotType::SampledImage || + data.type == DescriptorSlotType::CombinedImageSampler) && + m_DescriptorLookup.sampled != ImageDescriptorFormat::Indexed2012) + { + m_CreationInfo.m_Image[m_CreationInfo.m_ImageView[data.resource].image].viewDescriptors.push_back( + {bytebuf(key.data(), sampledSize), data.resource}); + } +} + template <> VkDescriptorSetLayoutCreateInfo WrappedVulkan::UnwrapInfo(const VkDescriptorSetLayoutCreateInfo *info) {