diff --git a/util/test/demos/CMakeLists.txt b/util/test/demos/CMakeLists.txt
index 90290dff5..87af43c06 100644
--- a/util/test/demos/CMakeLists.txt
+++ b/util/test/demos/CMakeLists.txt
@@ -112,6 +112,7 @@ set(VULKAN_SRC
vk/vk_descriptor_index.cpp
vk/vk_descriptor_reuse.cpp
vk/vk_descriptor_variable_count.cpp
+ vk/vk_descriptor_buffer_analyse.cpp
vk/vk_discard_rects.cpp
vk/vk_discard_zoo.cpp
vk/vk_draw_zoo.cpp
diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj
index 2be76bbe6..3606a9cd8 100644
--- a/util/test/demos/demos.vcxproj
+++ b/util/test/demos/demos.vcxproj
@@ -318,6 +318,7 @@
+
diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters
index 3806e14da..0fc18faca 100644
--- a/util/test/demos/demos.vcxproj.filters
+++ b/util/test/demos/demos.vcxproj.filters
@@ -730,6 +730,9 @@
D3D11\demos
+
+ Vulkan\demos
+
diff --git a/util/test/demos/vk/vk_descriptor_buffer_analyse.cpp b/util/test/demos/vk/vk_descriptor_buffer_analyse.cpp
new file mode 100644
index 000000000..1838ee128
--- /dev/null
+++ b/util/test/demos/vk/vk_descriptor_buffer_analyse.cpp
@@ -0,0 +1,2348 @@
+/******************************************************************************
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2025 Baldur Karlsson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ ******************************************************************************/
+
+#include "3rdparty/fmt/core.h"
+#include "vk_test.h"
+
+RD_TEST(VK_Descriptor_Buffer_Analyse, VulkanGraphicsTest)
+{
+ static constexpr const char *Description =
+ "Analyses the descriptors in VK_EXT_descriptor_buffer to test that they can be identified in "
+ "known patterns.";
+
+ VkPhysicalDeviceDescriptorBufferFeaturesEXT descBufFeatures = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT,
+ };
+ VkPhysicalDeviceDescriptorBufferPropertiesEXT descBufProps = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT,
+ };
+ VkPhysicalDeviceTexelBufferAlignmentProperties texelAlignProps = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES,
+ };
+ void Prepare(int argc, char **argv)
+ {
+ devExts.push_back(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME);
+ devExts.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME);
+ devExts.push_back(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
+ devExts.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
+ devExts.push_back(VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME);
+ devExts.push_back(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
+ devExts.push_back(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME);
+ optDevExts.push_back(VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME);
+ optDevExts.push_back(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME);
+ optDevExts.push_back(VK_EXT_TEXEL_BUFFER_ALIGNMENT_EXTENSION_NAME);
+
+ optFeatures.sparseBinding = VK_TRUE;
+ optFeatures.sparseResidencyBuffer = VK_TRUE;
+ optFeatures.sparseResidencyImage2D = VK_TRUE;
+
+ // require RBA as we always turn it on
+ features.robustBufferAccess = VK_TRUE;
+
+ VulkanGraphicsTest::Prepare(argc, argv);
+
+ if(!Avail.empty())
+ return;
+
+ getPhysFeatures2(&descBufFeatures);
+ getPhysProperties2(&descBufProps);
+
+ static VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT texAlignFeats = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT,
+ NULL,
+ true,
+ };
+
+ texelAlignProps.storageTexelBufferOffsetAlignmentBytes =
+ physProperties.limits.minTexelBufferOffsetAlignment;
+ texelAlignProps.uniformTexelBufferOffsetAlignmentBytes =
+ physProperties.limits.minTexelBufferOffsetAlignment;
+ if(hasExt(VK_EXT_TEXEL_BUFFER_ALIGNMENT_EXTENSION_NAME) || devVersion >= VK_MAKE_VERSION(1, 3, 0))
+ {
+ getPhysProperties2(&texelAlignProps);
+
+ if(devVersion < VK_MAKE_VERSION(1, 3, 0))
+ {
+ texAlignFeats.pNext = (void *)devInfoNext;
+ devInfoNext = &texAlignFeats;
+ }
+ }
+
+ if(!descBufFeatures.descriptorBuffer)
+ Avail = "Feature 'descriptorBuffer' not available";
+
+ if(!descBufFeatures.descriptorBufferCaptureReplay)
+ Avail = "Feature 'descriptorBufferCaptureReplay' not available";
+
+ descBufFeatures.pNext = (void *)devInfoNext;
+ devInfoNext = &descBufFeatures;
+
+ static VkPhysicalDeviceBufferDeviceAddressFeaturesKHR bufaddrFeatures = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_KHR,
+ };
+
+ getPhysFeatures2(&bufaddrFeatures);
+
+ if(!bufaddrFeatures.bufferDeviceAddress)
+ Avail = "feature 'bufferDeviceAddress' not available";
+
+ if(!bufaddrFeatures.bufferDeviceAddressCaptureReplay)
+ Avail = "feature 'bufferDeviceAddressCaptureReplay' not available";
+
+ bufaddrFeatures.bufferDeviceAddressCaptureReplay = 1;
+ bufaddrFeatures.bufferDeviceAddressMultiDevice = 0;
+
+ bufaddrFeatures.pNext = (void *)devInfoNext;
+ devInfoNext = &bufaddrFeatures;
+
+ static VkPhysicalDeviceSamplerYcbcrConversionFeatures ycbcrFeats = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES,
+ };
+
+ getPhysFeatures2(&ycbcrFeats);
+
+ if(!ycbcrFeats.samplerYcbcrConversion)
+ Avail = "feature 'samplerYcbcrConversion' not available";
+
+ ycbcrFeats.pNext = (void *)devInfoNext;
+ devInfoNext = &ycbcrFeats;
+
+ static VkPhysicalDeviceAccelerationStructureFeaturesKHR accelFeats = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR,
+ };
+
+ if(hasExt(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME))
+ {
+ getPhysFeatures2(&accelFeats);
+
+ if(!accelFeats.accelerationStructure)
+ Avail = "feature 'accelerationStructure' not available";
+
+ accelFeats.pNext = (void *)devInfoNext;
+ devInfoNext = &accelFeats;
+ }
+ }
+
+ struct SizedBytes
+ {
+ byte bytes[512];
+ size_t sz;
+
+ bool operator==(const SizedBytes &o) const
+ {
+ return sz == o.sz && memcmp(bytes, o.bytes, sz) == 0;
+ }
+ };
+
+ struct Image
+ {
+ std::string name;
+ VkImageCreateInfo info;
+ VkDeviceSize offset;
+ VkDeviceSize alignment;
+ VkDeviceSize size;
+
+ VkImage img;
+ SizedBytes imgCapData;
+
+ VkImageView view;
+ SizedBytes viewCapData;
+ };
+
+ bool makeImage(Image & img, VkDeviceMemory memory, uint32_t memType, uint32_t offset,
+ std::string name, VkImageCreateInfo imageCreateInfo, VkImageAspectFlags aspect,
+ VkImageViewType viewType, VkFormat viewFormat = VK_FORMAT_UNDEFINED)
+ {
+ VkMemoryRequirements mrq;
+
+ img.name = name;
+ img.info = imageCreateInfo;
+
+ imageCreateInfo.flags |= VK_IMAGE_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT;
+
+ vkCreateImage(device, &imageCreateInfo, NULL, &img.img);
+
+ vkGetImageMemoryRequirements(device, img.img, &mrq);
+
+ if(mrq.memoryTypeBits & (1U << memType))
+ {
+ VkImageCaptureDescriptorDataInfoEXT imgCapInfo = {
+ VK_STRUCTURE_TYPE_IMAGE_CAPTURE_DESCRIPTOR_DATA_INFO_EXT,
+ };
+
+ VkImageViewCaptureDescriptorDataInfoEXT viewCapInfo = {
+ VK_STRUCTURE_TYPE_IMAGE_VIEW_CAPTURE_DESCRIPTOR_DATA_INFO_EXT,
+ };
+
+ img.offset = AlignUp((VkDeviceSize)offset, mrq.alignment);
+ img.alignment = mrq.alignment;
+ img.size = mrq.size;
+ vkBindImageMemory(device, img.img, memory, img.offset);
+
+ if(viewFormat == VK_FORMAT_UNDEFINED)
+ viewFormat = imageCreateInfo.format;
+
+ vkh::ImageViewCreateInfo viewInfo(
+ img.img, viewType, viewFormat, {},
+ {aspect, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS});
+ viewInfo.flags = VK_IMAGE_VIEW_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT;
+
+ img.view = createImageView(viewInfo);
+
+ imgCapInfo.image = img.img;
+ vkGetImageOpaqueCaptureDescriptorDataEXT(device, &imgCapInfo, img.imgCapData.bytes);
+ img.imgCapData.sz = descBufProps.imageCaptureReplayDescriptorDataSize;
+
+ viewCapInfo.imageView = img.view;
+ vkGetImageViewOpaqueCaptureDescriptorDataEXT(device, &viewCapInfo, img.viewCapData.bytes);
+ img.viewCapData.sz = descBufProps.imageViewCaptureReplayDescriptorDataSize;
+
+ return true;
+ }
+
+ vkDestroyImage(device, img.img, NULL);
+
+ return false;
+ }
+
+ VkDeviceSize GetElemSize(VkFormat fmt)
+ {
+ if(fmt == VK_FORMAT_R32G32B32A32_SFLOAT || fmt == VK_FORMAT_R32G32B32A32_UINT ||
+ fmt == VK_FORMAT_R32G32B32A32_SINT)
+ return 16;
+ if(fmt == VK_FORMAT_R32G32B32_SFLOAT || fmt == VK_FORMAT_R32G32B32_UINT ||
+ fmt == VK_FORMAT_R32G32B32_SINT)
+ return 12;
+ if(fmt == VK_FORMAT_R32G32_SFLOAT || fmt == VK_FORMAT_R32G32_UINT || fmt == VK_FORMAT_R32G32_SINT)
+ return 8;
+ if(fmt == VK_FORMAT_R32_SFLOAT || fmt == VK_FORMAT_R32_UINT || fmt == VK_FORMAT_R32_SINT)
+ return 4;
+ if(fmt == VK_FORMAT_R16_SFLOAT || fmt == VK_FORMAT_R16_UINT || fmt == VK_FORMAT_R16_SINT)
+ return 2;
+ if(fmt == VK_FORMAT_R8_UNORM || fmt == VK_FORMAT_R8_UINT || fmt == VK_FORMAT_R8_SINT)
+ return 1;
+ return 0;
+ }
+
+ std::string FormatStr(VkFormat fmt)
+ {
+ switch(fmt)
+ {
+ default: return "?";
+
+ case VK_FORMAT_R32G32B32A32_SFLOAT: return "VK_FORMAT_R32G32B32A32_SFLOAT";
+ case VK_FORMAT_R32G32B32A32_SINT: return "VK_FORMAT_R32G32B32A32_SINT";
+ case VK_FORMAT_R32G32B32A32_UINT: return "VK_FORMAT_R32G32B32A32_UINT";
+ case VK_FORMAT_R32G32B32_SFLOAT: return "VK_FORMAT_R32G32B32_SFLOAT";
+ case VK_FORMAT_R32G32B32_SINT: return "VK_FORMAT_R32G32B32_SINT";
+ case VK_FORMAT_R32G32B32_UINT: return "VK_FORMAT_R32G32B32_UINT";
+ case VK_FORMAT_R32G32_SFLOAT: return "VK_FORMAT_R32G32_SFLOAT";
+ case VK_FORMAT_R32G32_SINT: return "VK_FORMAT_R32G32_SINT";
+ case VK_FORMAT_R32G32_UINT: return "VK_FORMAT_R32G32_UINT";
+ case VK_FORMAT_R32_SFLOAT: return "VK_FORMAT_R32_SFLOAT";
+ case VK_FORMAT_R32_SINT: return "VK_FORMAT_R32_SINT";
+ case VK_FORMAT_R32_UINT: return "VK_FORMAT_R32_UINT";
+ case VK_FORMAT_R16_SFLOAT: return "VK_FORMAT_R16_SFLOAT";
+ case VK_FORMAT_R16_SINT: return "VK_FORMAT_R16_SINT";
+ case VK_FORMAT_R16_UINT: return "VK_FORMAT_R16_UINT";
+ case VK_FORMAT_R8_UNORM: return "VK_FORMAT_R8_UNORM";
+ case VK_FORMAT_R8_SINT: return "VK_FORMAT_R8_SINT";
+ case VK_FORMAT_R8_UINT: return "VK_FORMAT_R8_UINT";
+ }
+ }
+
+ SizedBytes GetDescriptor(VkDescriptorType type, VkDeviceAddress addr, VkDeviceSize range,
+ VkFormat fmt, VkImageLayout layout, VkSampler sampler, VkImageView view)
+ {
+ VkDescriptorGetInfoEXT desc = {VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT};
+ desc.type = type;
+
+ VkDescriptorAddressInfoEXT bufdesc = {VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT};
+ bufdesc.address = addr;
+ bufdesc.range = range;
+ bufdesc.format = fmt;
+
+ VkDescriptorImageInfo imgdesc = {};
+ imgdesc.imageLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL;
+ imgdesc.sampler = sampler;
+ imgdesc.imageView = view;
+
+ SizedBytes descriptorBytes;
+ switch(desc.type)
+ {
+ case VK_DESCRIPTOR_TYPE_SAMPLER:
+ descriptorBytes.sz = descBufProps.samplerDescriptorSize;
+ desc.data.pSampler = &imgdesc.sampler;
+ break;
+ case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+ descriptorBytes.sz = descBufProps.combinedImageSamplerDescriptorSize;
+ desc.data.pCombinedImageSampler = &imgdesc;
+ break;
+ case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
+ descriptorBytes.sz = descBufProps.inputAttachmentDescriptorSize;
+ desc.data.pCombinedImageSampler = &imgdesc;
+ break;
+ case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
+ descriptorBytes.sz = descBufProps.sampledImageDescriptorSize;
+ desc.data.pCombinedImageSampler = &imgdesc;
+ break;
+ case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
+ descriptorBytes.sz = descBufProps.storageImageDescriptorSize;
+ desc.data.pCombinedImageSampler = &imgdesc;
+ break;
+ case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
+ descriptorBytes.sz = descBufProps.uniformTexelBufferDescriptorSize;
+ desc.data.pUniformBuffer = &bufdesc;
+ break;
+ case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
+ descriptorBytes.sz = descBufProps.robustStorageBufferDescriptorSize;
+ desc.data.pUniformBuffer = &bufdesc;
+ break;
+ case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
+ descriptorBytes.sz = descBufProps.robustUniformBufferDescriptorSize;
+ desc.data.pUniformBuffer = &bufdesc;
+ break;
+ case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
+ descriptorBytes.sz = descBufProps.robustStorageBufferDescriptorSize;
+ desc.data.pUniformBuffer = &bufdesc;
+ break;
+ case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR:
+ descriptorBytes.sz = descBufProps.accelerationStructureDescriptorSize;
+ desc.data.accelerationStructure = addr;
+ break;
+ default: break;
+ }
+
+ vkGetDescriptorEXT(device, &desc, descriptorBytes.sz, descriptorBytes.bytes);
+
+ return descriptorBytes;
+ }
+
+ SizedBytes GetDescriptor(VkDescriptorType type, VkDeviceAddress addr, VkDeviceSize range = 0,
+ VkFormat fmt = VK_FORMAT_UNDEFINED)
+ {
+ return GetDescriptor(type, addr, range, fmt, VK_IMAGE_LAYOUT_UNDEFINED, VK_NULL_HANDLE,
+ VK_NULL_HANDLE);
+ }
+
+ SizedBytes GetDescriptor(VkImageLayout layout, VkSampler sampler, VkImageView view)
+ {
+ return GetDescriptor(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, 0, VK_FORMAT_UNDEFINED,
+ layout, sampler, view);
+ }
+
+ SizedBytes GetDescriptor(VkDescriptorType type, VkImageLayout layout, VkImageView view)
+ {
+ return GetDescriptor(type, 0, 0, VK_FORMAT_UNDEFINED, layout, VK_NULL_HANDLE, view);
+ }
+
+ SizedBytes GetDescriptor(VkSampler sampler)
+ {
+ return GetDescriptor(VK_DESCRIPTOR_TYPE_SAMPLER, 0, 0, VK_FORMAT_UNDEFINED,
+ VK_IMAGE_LAYOUT_UNDEFINED, sampler, VK_NULL_HANDLE);
+ }
+
+ enum class SamplerDescriptorFormat
+ {
+ PalettedNV,
+ AMD_SGPR,
+ AMD_SGPR_Fat,
+ Intel_Res,
+ Intel_BMage_Res,
+ ARM_Res,
+ QC_Res,
+ QC_ResPadded,
+ Count,
+ };
+
+ enum class BufferDescriptorFormat
+ {
+ SizeOffset,
+ Pointer,
+ // this doubles as offset+size assuming 48-bit address and 32-bit size
+ AMD_SGPR,
+ AMD_AS,
+ ARM_AS,
+ Intel_Res,
+ Intel_BMage_Res,
+ ARM_Res,
+ // QC has potentially multiple descriptors for a buffer with different strides depending on
+ // capabilities, we will need to detect these via probing.
+ // we assume no device needs all 3 - either only one is needed or at most two because devices
+ // that support 8-bit storage can all use the 16-bit descriptor to access 32-bit
+ QC_Res32,
+ QC_Res16,
+ QC_Res32_16,
+ QC_Res16_32,
+ QC_Res16_8,
+ QC_Res8_16,
+ NVTexel,
+ Packed48_16,
+ Packed45_19_Align256,
+ Packed45_19,
+ Count,
+ };
+
+ template
+ const std::vector &enumerate()
+ {
+ static std::vector ret;
+ if(ret.empty())
+ {
+ for(uint32_t i = 0; i < (uint32_t)T::Count; i++)
+ {
+ ret.push_back(T(i));
+ }
+ }
+
+ return ret;
+ }
+
+ std::string name(SamplerDescriptorFormat t)
+ {
+ static std::string names[(uint32_t)SamplerDescriptorFormat::Count + 1] = {
+ "PalettedNV", "AMD_SGPR", "AMD_SGPR_Fat", "Intel_Res", "Intel_BMage_Res",
+ "ARM_Res", "QC_Res", "QC_ResPadded", "",
+ };
+
+ return names[(uint32_t)t];
+ }
+
+ std::string name(BufferDescriptorFormat t)
+ {
+ static std::string names[(uint32_t)BufferDescriptorFormat::Count + 1] = {
+ "SizeOffset",
+ "Pointer",
+ "AMD_SGPR",
+ "AMD_AS",
+ "ARM_AS",
+ "Intel_Res",
+ "Intel_BMage_Res",
+ "ARM_Res",
+ "QC_Res32",
+ "QC_Res16",
+ "QC_Res32_16",
+ "QC_Res16_32",
+ "QC_Res16_8",
+ "QC_Res8_16",
+ "NVTexel",
+ "Packed48_16",
+ "Packed45_19_Align256",
+ "Packed45_19",
+ "",
+
+ };
+
+ return names[(uint32_t)t];
+ }
+
+ SizedBytes PredictDescriptor(SamplerDescriptorFormat fmt, const SizedBytes &sampCapData)
+ {
+ SizedBytes ret = {};
+
+ if(fmt == SamplerDescriptorFormat::PalettedNV)
+ {
+ if(sampCapData.sz == 4)
+ {
+ uint32_t idx = *(uint32_t *)sampCapData.bytes;
+ ret.sz = 4;
+ *((uint32_t *)ret.bytes) = idx << 20U;
+ }
+ }
+ // all others just encode the sampler directly so we can't decode and will need to hash lookup
+
+ return ret;
+ }
+
+#define MASK_NBITS(n) ((1ULL << n) - 1)
+
+ SizedBytes PredictDescriptor(SamplerDescriptorFormat fmt, VkDescriptorType type,
+ VkDeviceAddress baseAddr, const SizedBytes &sampCapData,
+ const SizedBytes &imgCapData, const SizedBytes &viewCapData)
+ {
+ SizedBytes ret = {};
+
+ if(fmt == SamplerDescriptorFormat::PalettedNV)
+ {
+ if(sampCapData.sz == 4 && viewCapData.sz == 12)
+ {
+ uint32_t sampIdx = *(uint32_t *)sampCapData.bytes;
+ uint32_t *viewIdxs = (uint32_t *)viewCapData.bytes;
+ uint32_t viewIdx = type == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT ? viewIdxs[2]
+ : type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE ? viewIdxs[1]
+ : viewIdxs[0];
+ ret.sz = 4;
+ *((uint32_t *)ret.bytes) = (sampIdx << 20U) | (viewIdx & ((1 << 20U) - 1));
+ }
+ else if(viewCapData.sz == 12)
+ {
+ uint32_t *viewIdxs = (uint32_t *)viewCapData.bytes;
+ uint32_t viewIdx = type == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT ? viewIdxs[2]
+ : type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE ? viewIdxs[1]
+ : viewIdxs[0];
+ ret.sz = 4;
+ *((uint32_t *)ret.bytes) = (viewIdx & ((1 << 20U) - 1));
+ }
+ }
+ else if(fmt == SamplerDescriptorFormat::AMD_SGPR)
+ {
+ // we expect samplers appended to views on AMD
+ // samplers are not reconstructable, we will have to do lookups
+ if(type != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+ ret.sz = 32;
+ else
+ ret.sz = 48;
+
+ uint64_t *out = (uint64_t *)ret.bytes;
+ out[0] = baseAddr >> 8;
+ }
+ else if(fmt == SamplerDescriptorFormat::AMD_SGPR_Fat)
+ {
+ // we expect samplers appended to views on AMD
+ // samplers are not reconstructable, we will have to do lookups
+ if(type != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+ ret.sz = 64;
+ else
+ ret.sz = 96;
+
+ uint64_t *out = (uint64_t *)ret.bytes;
+ out[0] = baseAddr >> 8;
+ }
+ else if(fmt == SamplerDescriptorFormat::Intel_Res)
+ {
+ if(type != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+ ret.sz = 64;
+ else
+ ret.sz = 128;
+ uint64_t *out = (uint64_t *)ret.bytes;
+ out[4] = baseAddr;
+ }
+ else if(fmt == SamplerDescriptorFormat::Intel_BMage_Res)
+ {
+ // battlemage only uses 96 for combined
+ if(type != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+ ret.sz = 64;
+ else
+ ret.sz = 96;
+ uint64_t *out = (uint64_t *)ret.bytes;
+ out[4] = baseAddr & MASK_NBITS(48);
+ }
+ else if(fmt == SamplerDescriptorFormat::ARM_Res)
+ {
+ if(viewCapData.sz == 16)
+ {
+ uint64_t *viewBases = (uint64_t *)viewCapData.bytes;
+ if(type != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+ ret.sz = 32;
+ else
+ ret.sz = 64;
+
+ uint64_t *out = (uint64_t *)ret.bytes;
+ out[2] = type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE ? viewBases[1] : viewBases[0];
+ }
+ }
+ else if(fmt == SamplerDescriptorFormat::QC_Res || fmt == SamplerDescriptorFormat::QC_ResPadded)
+ {
+ if(type != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+ ret.sz = 64;
+ else
+ ret.sz = 80; // 16 bytes of sampler after
+
+ // padded to alignment of base descriptor
+ if(fmt == SamplerDescriptorFormat::QC_ResPadded && ret.sz == 80)
+ ret.sz = 128;
+
+ uint64_t *out = (uint64_t *)ret.bytes;
+ out[2] = baseAddr;
+ }
+
+ return ret;
+ }
+
+ SizedBytes PredictDescriptor(BufferDescriptorFormat fmt, bool storage, VkDeviceAddress ptr,
+ VkDeviceSize range, VkFormat texelFormat)
+ {
+ SizedBytes ret = {};
+
+ if(fmt == BufferDescriptorFormat::AMD_SGPR)
+ {
+ ret.sz = 16;
+ uint64_t *out = (uint64_t *)ret.bytes;
+ out[0] = ptr;
+ if(texelFormat == VK_FORMAT_UNDEFINED)
+ out[1] = range;
+ else
+ out[1] = range / GetElemSize(texelFormat);
+ }
+ else if(fmt == BufferDescriptorFormat::AMD_AS)
+ {
+ ret.sz = 16;
+ uint64_t *out = (uint64_t *)ret.bytes;
+ out[0] = ptr;
+ }
+ else if(fmt == BufferDescriptorFormat::ARM_AS)
+ {
+ ret.sz = 32;
+ uint64_t *out = (uint64_t *)ret.bytes;
+ out[1] = ptr;
+ }
+ else if(fmt == BufferDescriptorFormat::Intel_Res)
+ {
+ ret.sz = 64;
+ uint64_t *out = (uint64_t *)ret.bytes;
+ out[4] = ptr;
+ if(texelFormat == VK_FORMAT_UNDEFINED && !storage)
+ out[5] = AlignUp(range, (VkDeviceSize)64) << 32ULL;
+ else
+ out[5] = range << 32ULL;
+ }
+ else if(fmt == BufferDescriptorFormat::Intel_BMage_Res)
+ {
+ ret.sz = 64;
+ uint64_t *out = (uint64_t *)ret.bytes;
+ out[4] = ptr;
+
+ // Intel battlemage stores size-1 where size is in bytes/texels
+ uint64_t num = range - 1;
+ if(texelFormat != VK_FORMAT_UNDEFINED)
+ num = (range / GetElemSize(texelFormat)) - 1;
+
+ // bottom 4 bits are swizzled for 1 byte texel formats or plain buffers
+ if(texelFormat == VK_FORMAT_UNDEFINED || GetElemSize(texelFormat) == 1)
+ {
+ uint8_t x = num & 0xff;
+ num = (num & ~0xff) + ((x & 0xfc) + 6 - (x & 0x3));
+ }
+ else if(GetElemSize(texelFormat) == 2)
+ {
+ // 2 byte formats have a different swizzling
+ uint8_t x = num & 0xff;
+ num = (num & ~0xff) + ((x & 0xfe) + 2 - (x & 0x1));
+ }
+ // 4 byte and up just encode elems-1
+
+ // bits are then scattered around
+ out[1] = ((num & 0x00007f) << 0) | ((num & 0x1fff80) << 9) | ((num >> 21) << 53);
+ }
+ else if(fmt == BufferDescriptorFormat::ARM_Res)
+ {
+ ret.sz = 32;
+ uint64_t *out = (uint64_t *)ret.bytes;
+ out[0] = range << 32ULL;
+ out[1] = ptr;
+ }
+ else if(fmt == BufferDescriptorFormat::SizeOffset)
+ {
+ ret.sz = 16;
+ uint64_t *out = (uint64_t *)ret.bytes;
+ out[0] = range;
+ out[1] = ptr;
+ }
+ else if(fmt == BufferDescriptorFormat::Pointer)
+ {
+ ret.sz = 8;
+ uint64_t *out = (uint64_t *)ret.bytes;
+ out[0] = ptr;
+ }
+ else if(fmt == BufferDescriptorFormat::QC_Res32 || fmt == BufferDescriptorFormat::QC_Res16 ||
+ fmt == BufferDescriptorFormat::QC_Res32_16 || fmt == BufferDescriptorFormat::QC_Res16_32 ||
+ fmt == BufferDescriptorFormat::QC_Res16_8 || fmt == BufferDescriptorFormat::QC_Res8_16)
+ {
+ uint64_t numDescriptors =
+ (fmt == BufferDescriptorFormat::QC_Res32 || fmt == BufferDescriptorFormat::QC_Res16) ? 1
+ : 2;
+
+ uint64_t strides[2] = {0, 0};
+
+ switch(fmt)
+ {
+ case BufferDescriptorFormat::QC_Res32: strides[0] = 4; break;
+ case BufferDescriptorFormat::QC_Res16: strides[0] = 2; break;
+ case BufferDescriptorFormat::QC_Res32_16:
+ strides[0] = 4;
+ strides[1] = 2;
+ break;
+ case BufferDescriptorFormat::QC_Res16_32:
+ strides[0] = 2;
+ strides[1] = 4;
+ break;
+ case BufferDescriptorFormat::QC_Res16_8:
+ strides[0] = 2;
+ strides[1] = 1;
+ break;
+ case BufferDescriptorFormat::QC_Res8_16:
+ strides[0] = 1;
+ strides[1] = 2;
+ break;
+ default: break;
+ }
+
+ if(texelFormat != VK_FORMAT_UNDEFINED)
+ {
+ // texel buffers are treated as texture since they're formatted
+ ret = PredictDescriptor(SamplerDescriptorFormat::QC_Res,
+ storage ? VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER
+ : VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,
+ ptr, {}, {}, {});
+
+ // add the range
+ uint64_t *out = (uint64_t *)ret.bytes;
+ out[0] = (range / GetElemSize(texelFormat)) << 32;
+ }
+ else
+ {
+ uint64_t offset = ptr & 0x3f;
+ uint64_t alignedPtr = ptr & ~offset;
+
+ ret.sz = 64 * numDescriptors;
+ uint64_t *out = (uint64_t *)ret.bytes;
+ out[0] = (AlignUp(range, strides[0]) >> strides[0]) << 32;
+ out[1] = (offset >> strides[0]) << 16;
+ out[2] = alignedPtr;
+ if(numDescriptors == 2)
+ {
+ out[8 + 0] = (AlignUp(range, strides[1]) >> strides[1]) << 32;
+ out[8 + 1] = (offset >> strides[1]) << 16;
+ out[8 + 2] = alignedPtr;
+ }
+ }
+ }
+ else if(fmt == BufferDescriptorFormat::NVTexel && texelFormat != VK_FORMAT_UNDEFINED)
+ {
+ ret.sz = 16;
+ uint64_t *out = (uint64_t *)ret.bytes;
+ switch(GetElemSize(texelFormat))
+ {
+ case 16: out[0] = ptr >> 4; break;
+ case 8: out[0] = ptr >> 3; break;
+ case 4: out[0] = ptr >> 2; break;
+ case 2: out[0] = ptr >> 1; break;
+ case 1: out[0] = ptr >> 0; break;
+ case 12:
+ {
+ uint64_t a = ptr;
+ if(ptr % 3)
+ a -= (3 - (ptr % 3)) << 7;
+ out[0] = a / 12;
+ break;
+ }
+ }
+ out[1] = range / GetElemSize(texelFormat);
+ }
+ else if(fmt == BufferDescriptorFormat::Packed45_19)
+ {
+ // pointer must be aligned, we can just clip lower bits
+ ptr >>= 4;
+ // range we must round up
+ range = (range + 15) >> 4;
+
+ if((ptr & MASK_NBITS(45)) == ptr && (range & MASK_NBITS(19)) == range)
+ {
+ ret.sz = 8;
+ *((uint64_t *)ret.bytes) = ptr | (range << 45);
+ }
+ }
+ else if(fmt == BufferDescriptorFormat::Packed45_19_Align256)
+ {
+ // pointer must be aligned, we can just clip lower bits
+ ptr >>= 4;
+ // range we must round up
+ range = AlignUp(range, (uint64_t)256) >> 4;
+
+ if((ptr & MASK_NBITS(45)) == ptr && (range & MASK_NBITS(19)) == range)
+ {
+ ret.sz = 8;
+ *((uint64_t *)ret.bytes) = ptr | (range << 45);
+ }
+ }
+ else if(fmt == BufferDescriptorFormat::Packed48_16)
+ {
+ // range we must round up
+ range = (range + 15) >> 4;
+
+ if((ptr & MASK_NBITS(48)) == ptr && (range & MASK_NBITS(16)) == range)
+ {
+ ret.sz = 8;
+ *((uint64_t *)ret.bytes) = ptr | (range << 48);
+ }
+ }
+
+ return ret;
+ }
+
+ bool MatchPrediction(SamplerDescriptorFormat fmt, const SizedBytes &descriptor,
+ const SizedBytes &prediction)
+ {
+ if(prediction.sz != descriptor.sz)
+ return false;
+
+ // only compare base address
+ if(fmt == SamplerDescriptorFormat::AMD_SGPR || fmt == SamplerDescriptorFormat::AMD_SGPR_Fat)
+ {
+ uint64_t *a = (uint64_t *)descriptor.bytes;
+ uint64_t *b = (uint64_t *)prediction.bytes;
+
+ if((a[0] & MASK_NBITS(40)) == (b[0] & MASK_NBITS(40)))
+ return true;
+
+ // hack for planes with base offsets
+ if((a[0] & MASK_NBITS(40)) == (b[0] & MASK_NBITS(40)) + 256)
+ return true;
+ if((a[0] & MASK_NBITS(40)) == (b[0] & MASK_NBITS(40)) + 512)
+ return true;
+ // don't know why amdvlk/radv differ on this but it won't matter
+ if((a[0] & MASK_NBITS(40)) == (b[0] & MASK_NBITS(40)) + 16)
+ return true;
+ if((a[0] & MASK_NBITS(40)) == (b[0] & MASK_NBITS(40)) + 32)
+ return true;
+
+ return false;
+ }
+ else if(fmt == SamplerDescriptorFormat::Intel_Res ||
+ fmt == SamplerDescriptorFormat::Intel_BMage_Res)
+ {
+ uint64_t *a = (uint64_t *)descriptor.bytes;
+ uint64_t *b = (uint64_t *)prediction.bytes;
+
+ if(a[4] == b[4])
+ return true;
+ // hack for planes with base offsets
+ if(a[4] == b[4] + 0x2000)
+ return true;
+ if(a[4] == b[4] + 0x3000)
+ return true;
+ if(a[4] == b[4] + 0x10000)
+ return true;
+ if(a[4] == b[4] + 0x11000)
+ return true;
+
+ return false;
+ }
+ else if(fmt == SamplerDescriptorFormat::ARM_Res)
+ {
+ uint64_t *a = (uint64_t *)descriptor.bytes;
+ uint64_t *b = (uint64_t *)prediction.bytes;
+
+ if(a[2] != b[2])
+ return false;
+
+ return true;
+ }
+ else if(fmt == SamplerDescriptorFormat::QC_Res || fmt == SamplerDescriptorFormat::QC_ResPadded)
+ {
+ uint64_t *a = (uint64_t *)descriptor.bytes;
+ uint64_t *b = (uint64_t *)prediction.bytes;
+
+ if((a[2] & MASK_NBITS(48)) != (b[2] & MASK_NBITS(48)))
+ return false;
+
+ return true;
+ }
+
+ return descriptor == prediction;
+ }
+
+ bool MatchPrediction(BufferDescriptorFormat fmt, const SizedBytes &descriptor,
+ const SizedBytes &prediction)
+ {
+ if(prediction.sz != descriptor.sz)
+ return false;
+
+ if(fmt == BufferDescriptorFormat::AMD_SGPR)
+ {
+ uint64_t *a = (uint64_t *)descriptor.bytes;
+ uint64_t *b = (uint64_t *)prediction.bytes;
+
+ if((a[0] & MASK_NBITS(48)) != (b[0] & MASK_NBITS(48)))
+ return false;
+
+ // allow 4-byte alignment
+ if(AlignUp(a[1] & MASK_NBITS(32), 4ULL) != AlignUp(b[1] & MASK_NBITS(32), 4ULL))
+ return false;
+
+ return true;
+ }
+ else if(fmt == BufferDescriptorFormat::AMD_AS)
+ {
+ uint64_t *a = (uint64_t *)descriptor.bytes;
+ uint64_t *b = (uint64_t *)prediction.bytes;
+
+ if((a[0] & MASK_NBITS(48)) != (b[0] & MASK_NBITS(48)))
+ return false;
+
+ return true;
+ }
+ else if(fmt == BufferDescriptorFormat::ARM_AS)
+ {
+ uint64_t *a = (uint64_t *)descriptor.bytes;
+ uint64_t *b = (uint64_t *)prediction.bytes;
+
+ if(a[1] != b[1])
+ return false;
+
+ return true;
+ }
+ else if(fmt == BufferDescriptorFormat::Intel_Res)
+ {
+ // only compare address and size
+ uint64_t *a = (uint64_t *)descriptor.bytes;
+ uint64_t *b = (uint64_t *)prediction.bytes;
+
+ if(a[4] != b[4])
+ return false;
+
+ if(a[5] >> 32ULL != b[5] >> 32ULL)
+ return false;
+
+ return true;
+ }
+ else if(fmt == BufferDescriptorFormat::Intel_BMage_Res)
+ {
+ // only compare address and size
+ uint64_t *a = (uint64_t *)descriptor.bytes;
+ uint64_t *b = (uint64_t *)prediction.bytes;
+
+ if(a[4] != b[4])
+ return false;
+
+ if((a[1] & 0xFFF00000001FFFFF) != (b[1] & 0xFFF00000001FFFFF))
+ return false;
+
+ return true;
+ }
+ else if(fmt == BufferDescriptorFormat::ARM_Res)
+ {
+ // only compare address and size
+ uint64_t *a = (uint64_t *)descriptor.bytes;
+ uint64_t *b = (uint64_t *)prediction.bytes;
+
+ if(a[1] != b[1])
+ return false;
+
+ if(a[0] >> 32ULL != b[0] >> 32ULL)
+ return false;
+
+ return true;
+ }
+ else if(fmt == BufferDescriptorFormat::QC_Res32 || fmt == BufferDescriptorFormat::QC_Res16 ||
+ fmt == BufferDescriptorFormat::QC_Res32_16 || fmt == BufferDescriptorFormat::QC_Res16_32 ||
+ fmt == BufferDescriptorFormat::QC_Res16_8 || fmt == BufferDescriptorFormat::QC_Res8_16)
+ {
+ uint64_t *a = (uint64_t *)descriptor.bytes;
+ uint64_t *b = (uint64_t *)prediction.bytes;
+
+ // size
+ if(((a[0] >> 32) & 0x7fffffff) != ((b[0] >> 32) & 0x7fffffff))
+ return false;
+
+ // aligned offset
+ if((a[1] & 0x3f0000) != (b[1] & 0x3f0000))
+ return false;
+
+ // aligned pointer
+ if(a[2] != b[2])
+ return false;
+
+ // if there's a second descriptor, check it
+ if(descriptor.sz == 128)
+ {
+ if(((a[8 + 0] >> 32) & 0x7fffffff) != ((b[8 + 0] >> 32) & 0x7fffffff))
+ return false;
+
+ if((a[8 + 1] & 0x3f0000) != (b[8 + 1] & 0x3f0000))
+ return false;
+
+ if(a[8 + 2] != b[8 + 2])
+ return false;
+ }
+
+ return true;
+ }
+ else if(fmt == BufferDescriptorFormat::NVTexel)
+ {
+ uint64_t *a = (uint64_t *)descriptor.bytes;
+ uint64_t *b = (uint64_t *)prediction.bytes;
+
+ if(a[0] != b[0])
+ return false;
+
+ if((a[1] & 0xffffffffULL) != (b[1] & 0xffffffffULL))
+ return false;
+
+ return true;
+ }
+
+ return descriptor == prediction;
+ }
+
+ void DumpData(const std::string &name, const SizedBytes &data)
+ {
+ TEST_LOG(" %s is %u bytes:", name.c_str(), (uint32_t)data.sz);
+
+ if(data.sz == 0)
+ {
+ TEST_LOG(" ---");
+ return;
+ }
+
+ std::string dump;
+
+ const byte *cur = &data.bytes[0];
+
+ uint32_t i = 0;
+ if(data.sz == 1)
+ {
+ dump += fmt::format(" {:#04x}", *cur);
+ cur++;
+ i++;
+ }
+ for(; i + 8 <= data.sz; i += 8)
+ {
+ dump += fmt::format(" {:#018x}", *(uint64_t *)cur);
+ cur += 8;
+ }
+ for(; i + 4 <= data.sz; i += 4)
+ dump += fmt::format(" {:#010x}", *(uint32_t *)cur);
+
+ TEST_ASSERT(i == data.sz, "Expected 4-byte aligned capture data");
+
+ dump.erase(0, 1);
+ TEST_LOG(" %s", dump.c_str());
+
+ dump.clear();
+ cur = &data.bytes[0];
+
+ i = 0;
+ if(data.sz == 1)
+ {
+ dump += fmt::format(" {:#010b}", *cur);
+ cur++;
+ i++;
+ }
+ for(; i + 8 <= data.sz; i += 8)
+ {
+ dump += fmt::format(" {:#066b} ", *(uint64_t *)cur);
+ cur += 8;
+ }
+ for(; i + 4 <= data.sz; i += 4)
+ dump += fmt::format(" {:#034b} ", *(uint32_t *)cur);
+
+ TEST_ASSERT(i == data.sz, "Expected 4-byte aligned capture data");
+
+ dump.erase(0, 1);
+ TEST_LOG(" %s", dump.c_str());
+ }
+
+ int main()
+ {
+ // initialise, create window, create context, etc
+ if(!Init())
+ return 3;
+
+ // allocate 20MB of BDA-able memory in every device local memory type with a buffer
+
+ static const uint64_t bufferSize = 20 * 1000 * 1000;
+ static const uint64_t blasSize = 9999;
+ static const uint64_t blasOffset = 512;
+
+ vkh::BufferCreateInfo bda_buffer_info(
+ bufferSize,
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
+ VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT |
+ VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT,
+ VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT |
+ VK_BUFFER_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT);
+
+ if(hasExt(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME))
+ bda_buffer_info.usage |= VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR;
+
+ vkh::BufferCreateInfo sparse_buffer_info = bda_buffer_info;
+ sparse_buffer_info.flags |=
+ VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT;
+
+ VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
+ VkMemoryAllocateFlagsInfo memAllocFlags = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO};
+ VkBufferDeviceAddressInfoKHR bda_info = {VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_KHR};
+
+ memAllocFlags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR |
+ VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT;
+ memAllocInfo.pNext = &memAllocFlags;
+
+ const VkPhysicalDeviceMemoryProperties *memProps = NULL;
+ vmaGetMemoryProperties(allocator, &memProps);
+
+ VkDeviceMemory memory[16] = {};
+
+ VkBuffer buffer[17] = {};
+ VkDeviceAddress ptrs[17] = {};
+ SizedBytes bufCapData[17] = {};
+
+ VkBuffer sparseBuffer = {};
+ Image sparseImage = {};
+
+ VkAccelerationStructureKHR blas[16];
+ VkDeviceAddress blasAddr[16] = {};
+ SizedBytes blasCapData[16] = {};
+
+ std::vector images[16] = {};
+
+ VkSamplerYcbcrConversionCreateInfo createInfo = {
+ VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
+ };
+ {
+ createInfo.chromaFilter = VK_FILTER_LINEAR;
+ createInfo.xChromaOffset = VK_CHROMA_LOCATION_MIDPOINT;
+ createInfo.yChromaOffset = VK_CHROMA_LOCATION_MIDPOINT;
+
+ createInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020;
+ createInfo.ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
+ }
+
+ for(uint32_t i = 0; i < memProps->memoryTypeCount; i++)
+ {
+ if(memProps->memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
+ {
+ vkCreateBuffer(device, bda_buffer_info, NULL, &buffer[i]);
+
+ memAllocInfo.memoryTypeIndex = i;
+ memAllocInfo.allocationSize = bda_buffer_info.size;
+ vkAllocateMemory(device, &memAllocInfo, NULL, &memory[i]);
+ vkBindBufferMemory(device, buffer[i], memory[i], 0);
+
+ bda_info.buffer = buffer[i];
+ ptrs[i] = vkGetBufferDeviceAddressKHR(device, &bda_info);
+
+ VkBufferCaptureDescriptorDataInfoEXT bufCapInfo = {
+ VK_STRUCTURE_TYPE_BUFFER_CAPTURE_DESCRIPTOR_DATA_INFO_EXT,
+ NULL,
+ buffer[i],
+ };
+
+ vkGetBufferOpaqueCaptureDescriptorDataEXT(device, &bufCapInfo, bufCapData[i].bytes);
+ bufCapData[i].sz = descBufProps.bufferCaptureReplayDescriptorDataSize;
+
+ VkAccelerationStructureCreateInfoKHR blasCreateInfo = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR,
+ };
+ blasCreateInfo.buffer = buffer[i];
+ blasCreateInfo.size = blasSize;
+ blasCreateInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
+ blasCreateInfo.offset = blasOffset;
+ blasCreateInfo.createFlags =
+ VK_ACCELERATION_STRUCTURE_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT |
+ VK_ACCELERATION_STRUCTURE_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT_KHR;
+
+ if(hasExt(VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME))
+ {
+ CHECK_VKR(
+ vkCreateAccelerationStructureKHR(device, &blasCreateInfo, VK_NULL_HANDLE, &blas[i]))
+
+ VkAccelerationStructureDeviceAddressInfoKHR blasDeviceAddressInfo = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR,
+ NULL,
+ blas[i],
+ };
+ blasAddr[i] = vkGetAccelerationStructureDeviceAddressKHR(device, &blasDeviceAddressInfo);
+
+ VkAccelerationStructureCaptureDescriptorDataInfoEXT blasCapInfo = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CAPTURE_DESCRIPTOR_DATA_INFO_EXT,
+ NULL,
+ blas[i],
+ };
+
+ vkGetAccelerationStructureOpaqueCaptureDescriptorDataEXT(device, &blasCapInfo,
+ &blasCapData[i].bytes);
+ blasCapData[i].sz = descBufProps.accelerationStructureCaptureReplayDescriptorDataSize;
+ }
+
+ Image img;
+
+ for(uint32_t offset : {0, 1, 4, 16, 32, 128, 256, 512, 1024, 3000, 4000, 8000, 10000})
+ {
+ if(makeImage(img, memory[i], i, offset, "sampled",
+ vkh::ImageCreateInfo(54, 55, 0, VK_FORMAT_R8G8B8A8_UNORM,
+ VK_IMAGE_USAGE_SAMPLED_BIT, 1, 1, VK_SAMPLE_COUNT_1_BIT),
+ VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_VIEW_TYPE_2D))
+ images[i].push_back(img);
+
+ if(makeImage(img, memory[i], i, offset, "yuv3_plane0",
+ vkh::ImageCreateInfo(64, 64, 0, VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM,
+ VK_IMAGE_USAGE_SAMPLED_BIT, 1, 1, VK_SAMPLE_COUNT_1_BIT,
+ VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT),
+ VK_IMAGE_ASPECT_PLANE_0_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R8_UNORM))
+ {
+ images[i].push_back(img);
+
+ VkSamplerYcbcrConversionInfo ycbcrChain = {
+ VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO};
+
+ createInfo.format = VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM;
+ vkCreateSamplerYcbcrConversionKHR(device, &createInfo, NULL, &ycbcrChain.conversion);
+
+ vkh::ImageViewCreateInfo viewCreateInfo(
+ img.img, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, {},
+ vkh::ImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT));
+
+ viewCreateInfo.pNext = &ycbcrChain;
+
+ img.name = "yuv3_combined";
+ img.view = createImageView(viewCreateInfo);
+ }
+
+ if(makeImage(img, memory[i], i, offset, "yuv3_plane1",
+ vkh::ImageCreateInfo(64, 64, 0, VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM,
+ VK_IMAGE_USAGE_SAMPLED_BIT, 1, 1, VK_SAMPLE_COUNT_1_BIT,
+ VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT),
+ VK_IMAGE_ASPECT_PLANE_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R8_UNORM))
+ images[i].push_back(img);
+
+ if(makeImage(img, memory[i], i, offset, "yuv3_plane2",
+ vkh::ImageCreateInfo(64, 64, 0, VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM,
+ VK_IMAGE_USAGE_SAMPLED_BIT, 1, 1, VK_SAMPLE_COUNT_1_BIT,
+ VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT),
+ VK_IMAGE_ASPECT_PLANE_2_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R8_UNORM))
+ images[i].push_back(img);
+
+ if(makeImage(img, memory[i], i, offset, "yuv2_plane0",
+ vkh::ImageCreateInfo(64, 64, 0, VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
+ VK_IMAGE_USAGE_SAMPLED_BIT, 1, 1, VK_SAMPLE_COUNT_1_BIT,
+ VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT),
+ VK_IMAGE_ASPECT_PLANE_0_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R8_UNORM))
+ {
+ images[i].push_back(img);
+
+ VkSamplerYcbcrConversionInfo ycbcrChain = {
+ VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO};
+
+ createInfo.format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
+ vkCreateSamplerYcbcrConversionKHR(device, &createInfo, NULL, &ycbcrChain.conversion);
+
+ vkh::ImageViewCreateInfo viewCreateInfo(
+ img.img, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, {},
+ vkh::ImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT));
+
+ viewCreateInfo.pNext = &ycbcrChain;
+
+ img.name = "yuv2_combined";
+ img.view = createImageView(viewCreateInfo);
+ }
+
+ if(makeImage(img, memory[i], i, offset, "yuv2_plane1",
+ vkh::ImageCreateInfo(64, 64, 0, VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
+ VK_IMAGE_USAGE_SAMPLED_BIT, 1, 1, VK_SAMPLE_COUNT_1_BIT,
+ VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT),
+ VK_IMAGE_ASPECT_PLANE_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R8G8_UNORM))
+ images[i].push_back(img);
+
+ if(makeImage(img, memory[i], i, offset, "storage",
+ vkh::ImageCreateInfo(54, 55, 0, VK_FORMAT_R8G8B8A8_UNORM,
+ VK_IMAGE_USAGE_STORAGE_BIT, 1, 1, VK_SAMPLE_COUNT_1_BIT),
+ VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_VIEW_TYPE_2D))
+ images[i].push_back(img);
+
+ if(makeImage(img, memory[i], i, offset, "sampled_storage",
+ vkh::ImageCreateInfo(54, 55, 0, VK_FORMAT_R8G8B8A8_UNORM,
+ VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT,
+ 1, 1, VK_SAMPLE_COUNT_1_BIT),
+ VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_VIEW_TYPE_2D))
+ images[i].push_back(img);
+
+ if(makeImage(img, memory[i], i, offset, "sampled_colatt",
+ vkh::ImageCreateInfo(
+ 54, 55, 0, VK_FORMAT_R8G8B8A8_UNORM,
+ VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 1, 1,
+ VK_SAMPLE_COUNT_1_BIT),
+ VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_VIEW_TYPE_2D))
+ images[i].push_back(img);
+
+ if(makeImage(img, memory[i], i, offset, "msaa",
+ vkh::ImageCreateInfo(
+ 54, 55, 0, VK_FORMAT_R8G8B8A8_UNORM,
+ VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 1, 1,
+ VK_SAMPLE_COUNT_4_BIT),
+ VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_VIEW_TYPE_2D))
+ images[i].push_back(img);
+
+ if(makeImage(img, memory[i], i, offset, "storage_colatt",
+ vkh::ImageCreateInfo(
+ 54, 55, 0, VK_FORMAT_R8G8B8A8_UNORM,
+ VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 1, 1,
+ VK_SAMPLE_COUNT_1_BIT),
+ VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_VIEW_TYPE_2D))
+ images[i].push_back(img);
+
+ if(makeImage(img, memory[i], i, offset, "sampled_dsatt_depth",
+ vkh::ImageCreateInfo(
+ 54, 55, 0, VK_FORMAT_D32_SFLOAT_S8_UINT,
+ VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
+ 1, 1, VK_SAMPLE_COUNT_1_BIT),
+ VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_VIEW_TYPE_2D))
+ images[i].push_back(img);
+
+ if(makeImage(img, memory[i], i, offset, "sampled_dsatt_stencil",
+ vkh::ImageCreateInfo(
+ 54, 55, 0, VK_FORMAT_D32_SFLOAT_S8_UINT,
+ VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
+ 1, 1, VK_SAMPLE_COUNT_1_BIT),
+ VK_IMAGE_ASPECT_STENCIL_BIT, VK_IMAGE_VIEW_TYPE_2D))
+ images[i].push_back(img);
+
+ if(makeImage(img, memory[i], i, offset, "input",
+ vkh::ImageCreateInfo(
+ 54, 55, 0, VK_FORMAT_R8G8B8A8_UNORM,
+ VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, 1, 1,
+ VK_SAMPLE_COUNT_1_BIT),
+ VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_VIEW_TYPE_2D))
+ images[i].push_back(img);
+ }
+ }
+ }
+
+ {
+ uint32_t i = memProps->memoryTypeCount;
+
+ if(sparseBuffer == VK_NULL_HANDLE && features.sparseBinding && features.sparseResidencyBuffer)
+ {
+ vkCreateBuffer(device, sparse_buffer_info, NULL, &sparseBuffer);
+
+ bda_info.buffer = sparseBuffer;
+ ptrs[i] = vkGetBufferDeviceAddressKHR(device, &bda_info);
+
+ VkBufferCaptureDescriptorDataInfoEXT bufCapInfo = {
+ VK_STRUCTURE_TYPE_BUFFER_CAPTURE_DESCRIPTOR_DATA_INFO_EXT,
+ NULL,
+ sparseBuffer,
+ };
+
+ vkGetBufferOpaqueCaptureDescriptorDataEXT(device, &bufCapInfo, bufCapData[i].bytes);
+ bufCapData[i].sz = descBufProps.bufferCaptureReplayDescriptorDataSize;
+ }
+ if(sparseImage.img == VK_NULL_HANDLE && features.sparseBinding &&
+ features.sparseResidencyImage2D)
+ {
+ vkh::ImageCreateInfo imageCreateInfo(
+ 54, 55, 0, VK_FORMAT_R32G32_SFLOAT, VK_IMAGE_USAGE_SAMPLED_BIT, 1, 1,
+ VK_SAMPLE_COUNT_1_BIT,
+ VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT | VK_IMAGE_CREATE_SPARSE_BINDING_BIT |
+ VK_IMAGE_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT);
+
+ vkCreateImage(device, imageCreateInfo, NULL, &sparseImage.img);
+
+ VkImageCaptureDescriptorDataInfoEXT imgCapInfo = {
+ VK_STRUCTURE_TYPE_IMAGE_CAPTURE_DESCRIPTOR_DATA_INFO_EXT,
+ };
+
+ VkImageViewCaptureDescriptorDataInfoEXT viewCapInfo = {
+ VK_STRUCTURE_TYPE_IMAGE_VIEW_CAPTURE_DESCRIPTOR_DATA_INFO_EXT,
+ };
+
+ VkMemoryRequirements mrq;
+
+ vkGetImageMemoryRequirements(device, sparseImage.img, &mrq);
+
+ sparseImage.name = "SparseImage";
+ sparseImage.info = imageCreateInfo;
+
+ sparseImage.offset = 0;
+ sparseImage.alignment = mrq.alignment;
+ sparseImage.size = mrq.size;
+
+ vkh::ImageViewCreateInfo viewInfo(
+ sparseImage.img, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R32G32_SFLOAT, {},
+ {VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS});
+ viewInfo.flags = VK_IMAGE_VIEW_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT;
+
+ sparseImage.view = createImageView(viewInfo);
+
+ imgCapInfo.image = sparseImage.img;
+ vkGetImageOpaqueCaptureDescriptorDataEXT(device, &imgCapInfo, sparseImage.imgCapData.bytes);
+ sparseImage.imgCapData.sz = descBufProps.imageCaptureReplayDescriptorDataSize;
+
+ viewCapInfo.imageView = sparseImage.view;
+ vkGetImageViewOpaqueCaptureDescriptorDataEXT(device, &viewCapInfo,
+ sparseImage.viewCapData.bytes);
+ sparseImage.viewCapData.sz = descBufProps.imageViewCaptureReplayDescriptorDataSize;
+ }
+ }
+
+ // vkGetAccelerationStructureOpaqueCaptureDescriptorDataEXT;
+
+ VkSampler samps[] = {
+ createSampler(vkh::SamplerCreateInfo(
+ VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_NEAREST,
+ VK_SAMPLER_ADDRESS_MODE_REPEAT, VK_SAMPLER_ADDRESS_MODE_REPEAT,
+ VK_SAMPLER_ADDRESS_MODE_REPEAT, 0.0f, VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, 0.0f,
+ 0.0f, 0.0f, VK_COMPARE_OP_NEVER, VK_FALSE,
+ VK_SAMPLER_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT)),
+ createSampler(vkh::SamplerCreateInfo(
+ VK_FILTER_NEAREST, VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST,
+ VK_SAMPLER_ADDRESS_MODE_REPEAT, VK_SAMPLER_ADDRESS_MODE_REPEAT,
+ VK_SAMPLER_ADDRESS_MODE_REPEAT, 0.0f, VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, 0.0f,
+ 0.0f, 0.0f, VK_COMPARE_OP_NEVER, VK_FALSE,
+ VK_SAMPLER_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT)),
+ createSampler(vkh::SamplerCreateInfo(
+ VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_LINEAR,
+ VK_SAMPLER_ADDRESS_MODE_REPEAT, VK_SAMPLER_ADDRESS_MODE_REPEAT,
+ VK_SAMPLER_ADDRESS_MODE_REPEAT, 0.0f, VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, 0.0f,
+ 0.0f, 0.0f, VK_COMPARE_OP_NEVER, VK_FALSE,
+ VK_SAMPLER_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT)),
+ createSampler(vkh::SamplerCreateInfo(
+ VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_NEAREST,
+ VK_SAMPLER_ADDRESS_MODE_REPEAT, VK_SAMPLER_ADDRESS_MODE_REPEAT,
+ VK_SAMPLER_ADDRESS_MODE_REPEAT, 0.0f, VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, 0.0f,
+ 0.0f, 0.0f, VK_COMPARE_OP_NEVER, VK_FALSE,
+ VK_SAMPLER_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT)),
+ };
+
+ SizedBytes samplerCapData[ARRAY_COUNT(samps)] = {};
+
+ std::vector samplerFormats;
+ std::vector asFormats;
+ std::vector uboFormats;
+ std::vector ssboFormats;
+ std::vector uniTexelFormats;
+ std::vector storTexelFormats;
+ SamplerDescriptorFormat combined = SamplerDescriptorFormat::Count,
+ sampled = SamplerDescriptorFormat::Count,
+ storage = SamplerDescriptorFormat::Count,
+ input = SamplerDescriptorFormat::Count;
+ uint32_t imperfectDetection = 0;
+ uint32_t failedDetections = 0;
+
+ for(uint32_t i = 0; i < ARRAY_COUNT(samps); i++)
+ {
+ VkSamplerCaptureDescriptorDataInfoEXT sampCapInfo = {
+ VK_STRUCTURE_TYPE_SAMPLER_CAPTURE_DESCRIPTOR_DATA_INFO_EXT,
+ NULL,
+ samps[i],
+ };
+
+ vkGetSamplerOpaqueCaptureDescriptorDataEXT(device, &sampCapInfo, samplerCapData[i].bytes);
+ samplerCapData[i].sz = descBufProps.samplerCaptureReplayDescriptorDataSize;
+
+ SizedBytes descriptor = GetDescriptor(samps[i]);
+
+ for(SamplerDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction = PredictDescriptor(fmt, samplerCapData[i]);
+ if(MatchPrediction(fmt, descriptor, prediction))
+ {
+ if(std::find(samplerFormats.begin(), samplerFormats.end(), fmt) == samplerFormats.end())
+ samplerFormats.push_back(fmt);
+ break;
+ }
+ }
+
+ // don't try to require a sampler format. There may not be one if it's just pure sampler encoded
+ }
+
+ for(uint32_t i = 0; i < memProps->memoryTypeCount + 1; i++)
+ {
+ if(ptrs[i] == 0)
+ continue;
+
+ VkDeviceSize uboAlign = physProperties.limits.minUniformBufferOffsetAlignment;
+ VkDeviceSize uboMax = physProperties.limits.maxUniformBufferRange;
+
+ for(VkDeviceSize offset : {(VkDeviceSize)0, uboAlign, uboAlign * 10, uboAlign * 16})
+ {
+ for(VkDeviceSize size : {(VkDeviceSize)1ULL,
+ (VkDeviceSize)2ULL,
+ (VkDeviceSize)3ULL,
+ (VkDeviceSize)4ULL,
+ (VkDeviceSize)5ULL,
+ (VkDeviceSize)6ULL,
+ (VkDeviceSize)7ULL,
+ (VkDeviceSize)8ULL,
+ (VkDeviceSize)9ULL,
+ (VkDeviceSize)10ULL,
+ (VkDeviceSize)11ULL,
+ (VkDeviceSize)12ULL,
+ (VkDeviceSize)13ULL,
+ (VkDeviceSize)14ULL,
+ (VkDeviceSize)15ULL,
+ (VkDeviceSize)16ULL,
+ (VkDeviceSize)17ULL,
+ (VkDeviceSize)18ULL,
+ (VkDeviceSize)19ULL,
+ (VkDeviceSize)20ULL,
+ (VkDeviceSize)21ULL,
+ (VkDeviceSize)22ULL,
+ (VkDeviceSize)23ULL,
+ (VkDeviceSize)24ULL,
+ (VkDeviceSize)25ULL,
+ (VkDeviceSize)100ULL,
+ (VkDeviceSize)128ULL,
+ (VkDeviceSize)236ULL,
+ (VkDeviceSize)237ULL,
+ (VkDeviceSize)238ULL,
+ (VkDeviceSize)239ULL,
+ (VkDeviceSize)240ULL,
+ (VkDeviceSize)241ULL,
+ (VkDeviceSize)242ULL,
+ (VkDeviceSize)243ULL,
+ (VkDeviceSize)244ULL,
+ (VkDeviceSize)245ULL,
+ (VkDeviceSize)246ULL,
+ (VkDeviceSize)247ULL,
+ (VkDeviceSize)248ULL,
+ (VkDeviceSize)249ULL,
+ (VkDeviceSize)250ULL,
+ (VkDeviceSize)251ULL,
+ (VkDeviceSize)252ULL,
+ (VkDeviceSize)253ULL,
+ (VkDeviceSize)254ULL,
+ (VkDeviceSize)255ULL,
+ (VkDeviceSize)256ULL,
+ (VkDeviceSize)257ULL,
+ (VkDeviceSize)258ULL,
+
+ VkDeviceSize(0x111111) + 1,
+ VkDeviceSize(0x222222) + 1,
+ VkDeviceSize(0x444444) + 1,
+ VkDeviceSize(0x888888) + 1,
+ VkDeviceSize(0xffffff) + 1,
+ VkDeviceSize(0xfffffe) + 1,
+ VkDeviceSize(0xfffffd) + 1,
+ VkDeviceSize(0xfffffc) + 1,
+ VkDeviceSize(0xfffffb) + 1,
+ VkDeviceSize(0xfffffa) + 1,
+ VkDeviceSize(0x1f1f1f) + 1,
+ VkDeviceSize(0xf1f1f1) + 1,
+ VkDeviceSize(0x2f2f2f) + 1,
+ VkDeviceSize(0xf2f2f2) + 1,
+ VkDeviceSize(0x4f4f4f) + 1,
+ VkDeviceSize(0xf4f4f4) + 1,
+ VkDeviceSize(0x8f8f8f) + 1,
+ VkDeviceSize(0xf8f8f8) + 1,
+ uboMax})
+ {
+ size = std::min(size, bda_buffer_info.size - offset);
+ size = std::min(size, uboMax);
+
+ SizedBytes descriptor =
+ GetDescriptor(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, ptrs[i] + offset, size);
+
+ std::vector predicted;
+
+ for(BufferDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction =
+ PredictDescriptor(fmt, false, ptrs[i] + offset, size, VK_FORMAT_UNDEFINED);
+ if(MatchPrediction(fmt, descriptor, prediction))
+ predicted.push_back(fmt);
+ }
+ if(predicted.empty())
+ {
+ failedDetections++;
+ TEST_WARN("!!! Couldn't detect buffer format");
+ DumpData("bufferCapData", bufCapData[i]);
+ TEST_LOG("Base pointer is %p", ptrs[i]);
+
+ DumpData(fmt::format("UBO descriptor with offs {} size {}", offset, size), descriptor);
+ for(BufferDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction =
+ PredictDescriptor(fmt, false, ptrs[i] + offset, size, VK_FORMAT_UNDEFINED);
+ DumpData(fmt::format("{} prediction", name(fmt)), prediction);
+ }
+ }
+
+ if(!uboFormats.empty())
+ {
+ // ensure that at least one previously predicted format is still predicted
+ bool oneKnown = false;
+ for(BufferDescriptorFormat fmt : predicted)
+ {
+ if(std::find(uboFormats.begin(), uboFormats.end(), fmt) != uboFormats.end())
+ oneKnown = true;
+ }
+
+ if(!oneKnown)
+ {
+ imperfectDetection++;
+ TEST_WARN("No commonly predicted formats. Previous:");
+ for(BufferDescriptorFormat fmt : uboFormats)
+ TEST_WARN("%s", name(fmt).c_str());
+ TEST_WARN("Predicted:");
+ for(BufferDescriptorFormat fmt : predicted)
+ TEST_WARN("%s", name(fmt).c_str());
+ }
+
+ if(predicted.size() < uboFormats.size())
+ uboFormats = predicted;
+ }
+ else
+ {
+ uboFormats = predicted;
+ }
+ }
+ }
+
+ VkDeviceSize ssboAlign = physProperties.limits.minStorageBufferOffsetAlignment;
+ VkDeviceSize ssboMax = physProperties.limits.maxStorageBufferRange;
+
+ for(VkDeviceSize offset : {(VkDeviceSize)0ULL, ssboAlign, ssboAlign * 10, ssboAlign * 16})
+ {
+ for(VkDeviceSize size : {(VkDeviceSize)1ULL,
+ (VkDeviceSize)2ULL,
+ (VkDeviceSize)3ULL,
+ (VkDeviceSize)4ULL,
+ (VkDeviceSize)5ULL,
+ (VkDeviceSize)6ULL,
+ (VkDeviceSize)7ULL,
+ (VkDeviceSize)8ULL,
+ (VkDeviceSize)9ULL,
+ (VkDeviceSize)10ULL,
+ (VkDeviceSize)11ULL,
+ (VkDeviceSize)12ULL,
+ (VkDeviceSize)13ULL,
+ (VkDeviceSize)14ULL,
+ (VkDeviceSize)15ULL,
+ (VkDeviceSize)16ULL,
+ (VkDeviceSize)17ULL,
+ (VkDeviceSize)18ULL,
+ (VkDeviceSize)100ULL,
+ (VkDeviceSize)128ULL,
+ (VkDeviceSize)236ULL,
+ (VkDeviceSize)237ULL,
+ (VkDeviceSize)238ULL,
+ (VkDeviceSize)239ULL,
+ (VkDeviceSize)240ULL,
+ (VkDeviceSize)241ULL,
+ (VkDeviceSize)242ULL,
+ (VkDeviceSize)243ULL,
+ (VkDeviceSize)244ULL,
+ (VkDeviceSize)245ULL,
+ (VkDeviceSize)246ULL,
+ (VkDeviceSize)247ULL,
+ (VkDeviceSize)248ULL,
+ (VkDeviceSize)249ULL,
+ (VkDeviceSize)250ULL,
+ (VkDeviceSize)251ULL,
+ (VkDeviceSize)252ULL,
+ (VkDeviceSize)253ULL,
+ (VkDeviceSize)254ULL,
+ (VkDeviceSize)255ULL,
+ (VkDeviceSize)256ULL,
+ (VkDeviceSize)257ULL,
+ (VkDeviceSize)258ULL,
+
+ VkDeviceSize(0x111111) + 1,
+ VkDeviceSize(0x222222) + 1,
+ VkDeviceSize(0x444444) + 1,
+ VkDeviceSize(0x888888) + 1,
+ VkDeviceSize(0xffffff) + 1,
+ VkDeviceSize(0xfffffe) + 1,
+ VkDeviceSize(0xfffffd) + 1,
+ VkDeviceSize(0xfffffc) + 1,
+ VkDeviceSize(0xfffffb) + 1,
+ VkDeviceSize(0xfffffa) + 1,
+ VkDeviceSize(0x1f1f1f) + 1,
+ VkDeviceSize(0xf1f1f1) + 1,
+ VkDeviceSize(0x2f2f2f) + 1,
+ VkDeviceSize(0xf2f2f2) + 1,
+ VkDeviceSize(0x4f4f4f) + 1,
+ VkDeviceSize(0xf4f4f4) + 1,
+ VkDeviceSize(0x8f8f8f) + 1,
+ VkDeviceSize(0xf8f8f8) + 1,
+ ssboMax})
+ {
+ size = std::min(size, bda_buffer_info.size - offset);
+ size = std::min(size, ssboMax);
+
+ SizedBytes descriptor =
+ GetDescriptor(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, ptrs[i] + offset, size);
+
+ std::vector predicted;
+
+ for(BufferDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction =
+ PredictDescriptor(fmt, true, ptrs[i] + offset, size, VK_FORMAT_UNDEFINED);
+ if(MatchPrediction(fmt, descriptor, prediction))
+ predicted.push_back(fmt);
+ }
+ if(predicted.empty())
+ {
+ failedDetections++;
+ TEST_WARN("!!! Couldn't detect buffer format");
+ DumpData("bufferCapData", bufCapData[i]);
+ TEST_LOG("Base pointer is %p", ptrs[i]);
+
+ DumpData(fmt::format("SSBO descriptor with offs {} size {}", offset, size), descriptor);
+ for(BufferDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction =
+ PredictDescriptor(fmt, true, ptrs[i] + offset, size, VK_FORMAT_UNDEFINED);
+ DumpData(fmt::format("{} prediction", name(fmt)), prediction);
+ }
+ }
+
+ if(!ssboFormats.empty())
+ {
+ // ensure that at least one previously predicted format is still predicted
+ bool oneKnown = false;
+ for(BufferDescriptorFormat fmt : predicted)
+ {
+ if(std::find(ssboFormats.begin(), ssboFormats.end(), fmt) != ssboFormats.end())
+ oneKnown = true;
+ }
+
+ if(!oneKnown)
+ {
+ imperfectDetection++;
+ TEST_WARN("No commonly predicted formats. Previous:");
+ for(BufferDescriptorFormat fmt : ssboFormats)
+ TEST_WARN("%s", name(fmt).c_str());
+ TEST_WARN("Predicted:");
+ for(BufferDescriptorFormat fmt : predicted)
+ TEST_WARN("%s", name(fmt).c_str());
+ }
+
+ if(predicted.size() < ssboFormats.size())
+ ssboFormats = predicted;
+ }
+ else
+ {
+ ssboFormats = predicted;
+ }
+ }
+ }
+
+ VkDeviceSize texelAlign = texelAlignProps.uniformTexelBufferOffsetAlignmentBytes;
+ VkDeviceSize texelMax = physProperties.limits.maxTexelBufferElements;
+
+ for(VkFormat texelFmt : {
+ VK_FORMAT_R8_UNORM,
+ VK_FORMAT_R16_SFLOAT,
+ VK_FORMAT_R32_SFLOAT,
+ VK_FORMAT_R32G32_SFLOAT,
+ VK_FORMAT_R32G32B32_SFLOAT,
+ VK_FORMAT_R32G32B32A32_SFLOAT,
+ VK_FORMAT_R8_UINT,
+ VK_FORMAT_R16_UINT,
+ VK_FORMAT_R32_UINT,
+ VK_FORMAT_R32G32_UINT,
+ VK_FORMAT_R32G32B32_UINT,
+ VK_FORMAT_R32G32B32A32_UINT,
+ })
+ {
+ VkDeviceSize elemSize = GetElemSize(texelFmt);
+
+ VkDeviceSize alignStep = texelAlign;
+
+ // If the single texel alignment property is VK_TRUE, then the buffer view’s offset must be
+ // aligned to the lesser of the corresponding byte alignment value or the size of a single
+ // texel, based on VkBufferViewCreateInfo::format
+ if(texelAlignProps.uniformTexelBufferOffsetSingleTexelAlignment)
+ {
+ alignStep = elemSize;
+
+ // If the size of a single texel is a multiple of three bytes, then the size of a single
+ // component of the format is used instead.
+ if(elemSize == 12)
+ alignStep = 4;
+ }
+
+ for(const VkDeviceSize size : {
+ (VkDeviceSize)1ULL, (VkDeviceSize)2ULL,
+ (VkDeviceSize)3ULL, (VkDeviceSize)4ULL,
+ (VkDeviceSize)5ULL, (VkDeviceSize)6ULL,
+ (VkDeviceSize)7ULL, (VkDeviceSize)8ULL,
+ (VkDeviceSize)9ULL, (VkDeviceSize)10ULL,
+ (VkDeviceSize)11ULL, (VkDeviceSize)12ULL,
+ (VkDeviceSize)13ULL, (VkDeviceSize)14ULL,
+ (VkDeviceSize)15ULL, (VkDeviceSize)16ULL,
+ (VkDeviceSize)17ULL, (VkDeviceSize)18ULL,
+ (VkDeviceSize)100ULL, (VkDeviceSize)128ULL,
+ (VkDeviceSize)236ULL, (VkDeviceSize)237ULL,
+ (VkDeviceSize)238ULL, (VkDeviceSize)239ULL,
+ (VkDeviceSize)240ULL, (VkDeviceSize)241ULL,
+ (VkDeviceSize)242ULL, (VkDeviceSize)243ULL,
+ (VkDeviceSize)244ULL, (VkDeviceSize)245ULL,
+ (VkDeviceSize)246ULL, (VkDeviceSize)247ULL,
+ (VkDeviceSize)248ULL, (VkDeviceSize)249ULL,
+ (VkDeviceSize)250ULL, (VkDeviceSize)251ULL,
+ (VkDeviceSize)252ULL, (VkDeviceSize)253ULL,
+ (VkDeviceSize)254ULL, (VkDeviceSize)255ULL,
+ (VkDeviceSize)256ULL, (VkDeviceSize)257ULL,
+ (VkDeviceSize)258ULL, texelMax,
+ })
+ {
+ for(const VkDeviceSize offset : {
+ (VkDeviceSize)0ULL, alignStep, alignStep * 2, alignStep * 3,
+ alignStep * 4, alignStep * 5, alignStep * 6, alignStep * 7,
+ alignStep * 8, alignStep * 9, alignStep * 10, alignStep * 11,
+ alignStep * 12, alignStep * 13, alignStep * 14, alignStep * 15,
+ alignStep * 16, alignStep * 32, alignStep * 60, alignStep * 61,
+ alignStep * 62, alignStep * 63, alignStep * 64,
+ })
+ {
+ VkDeviceSize alignedByteSize = std::min(size * elemSize, bda_buffer_info.size - offset);
+
+ alignedByteSize -= (alignedByteSize % elemSize);
+
+ SizedBytes descriptor = GetDescriptor(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,
+ ptrs[i] + offset, alignedByteSize, texelFmt);
+
+ std::vector predicted;
+
+ for(BufferDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction =
+ PredictDescriptor(fmt, false, ptrs[i] + offset, alignedByteSize, texelFmt);
+ if(MatchPrediction(fmt, descriptor, prediction))
+ predicted.push_back(fmt);
+ }
+ if(predicted.empty())
+ {
+ failedDetections++;
+ TEST_WARN("!!! Couldn't detect buffer format");
+ DumpData("bufferCapData", bufCapData[i]);
+ TEST_LOG("Base pointer is %p", ptrs[i]);
+
+ DumpData(fmt::format("Uniform Texel descriptor with offs {} size {} fmt {}", offset,
+ alignedByteSize, FormatStr(texelFmt).c_str()),
+ descriptor);
+ for(BufferDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction =
+ PredictDescriptor(fmt, false, ptrs[i] + offset, alignedByteSize, texelFmt);
+ DumpData(fmt::format("{} prediction", name(fmt)), prediction);
+ }
+ }
+
+ if(!uniTexelFormats.empty())
+ {
+ // ensure that at least one previously predicted format is still predicted
+ bool oneKnown = false;
+ for(BufferDescriptorFormat fmt : predicted)
+ {
+ if(std::find(uniTexelFormats.begin(), uniTexelFormats.end(), fmt) !=
+ uniTexelFormats.end())
+ oneKnown = true;
+ }
+
+ if(!oneKnown)
+ {
+ imperfectDetection++;
+ TEST_WARN("No commonly predicted formats. Previous:");
+ for(BufferDescriptorFormat fmt : uniTexelFormats)
+ TEST_WARN("%s", name(fmt).c_str());
+ TEST_WARN("Predicted:");
+ for(BufferDescriptorFormat fmt : predicted)
+ TEST_WARN("%s", name(fmt).c_str());
+ }
+
+ if(predicted.size() < uniTexelFormats.size())
+ uniTexelFormats = predicted;
+ }
+ else
+ {
+ uniTexelFormats = predicted;
+ }
+ }
+ }
+ }
+
+ texelAlign = texelAlignProps.storageTexelBufferOffsetAlignmentBytes;
+
+ for(VkFormat texelFmt : {
+ VK_FORMAT_R8_UNORM,
+ VK_FORMAT_R16_SFLOAT,
+ VK_FORMAT_R32_SFLOAT,
+ VK_FORMAT_R32G32_SFLOAT,
+ VK_FORMAT_R32G32B32_SFLOAT,
+ VK_FORMAT_R32G32B32A32_SFLOAT,
+ VK_FORMAT_R8_UINT,
+ VK_FORMAT_R16_UINT,
+ VK_FORMAT_R32_UINT,
+ VK_FORMAT_R32G32_UINT,
+ VK_FORMAT_R32G32B32_UINT,
+ VK_FORMAT_R32G32B32A32_UINT,
+ VK_FORMAT_R32G32B32_SINT,
+ })
+ {
+ VkDeviceSize elemSize = GetElemSize(texelFmt);
+
+ VkDeviceSize alignStep = texelAlign;
+
+ // If the single texel alignment property is VK_TRUE, then the buffer view’s offset must be
+ // aligned to the lesser of the corresponding byte alignment value or the size of a single
+ // texel, based on VkBufferViewCreateInfo::format
+ if(texelAlignProps.storageTexelBufferOffsetSingleTexelAlignment)
+ {
+ alignStep = elemSize;
+
+ // If the size of a single texel is a multiple of three bytes, then the size of a single
+ // component of the format is used instead.
+ if(elemSize == 12)
+ alignStep = 4;
+ }
+
+ for(const VkDeviceSize size : {
+ (VkDeviceSize)1ULL, (VkDeviceSize)2ULL,
+ (VkDeviceSize)3ULL, (VkDeviceSize)4ULL,
+ (VkDeviceSize)5ULL, (VkDeviceSize)6ULL,
+ (VkDeviceSize)7ULL, (VkDeviceSize)8ULL,
+ (VkDeviceSize)9ULL, (VkDeviceSize)10ULL,
+ (VkDeviceSize)11ULL, (VkDeviceSize)12ULL,
+ (VkDeviceSize)13ULL, (VkDeviceSize)14ULL,
+ (VkDeviceSize)15ULL, (VkDeviceSize)16ULL,
+ (VkDeviceSize)17ULL, (VkDeviceSize)18ULL,
+ (VkDeviceSize)100ULL, (VkDeviceSize)128ULL,
+ (VkDeviceSize)236ULL, (VkDeviceSize)237ULL,
+ (VkDeviceSize)238ULL, (VkDeviceSize)239ULL,
+ (VkDeviceSize)240ULL, (VkDeviceSize)241ULL,
+ (VkDeviceSize)242ULL, (VkDeviceSize)243ULL,
+ (VkDeviceSize)244ULL, (VkDeviceSize)245ULL,
+ (VkDeviceSize)246ULL, (VkDeviceSize)247ULL,
+ (VkDeviceSize)248ULL, (VkDeviceSize)249ULL,
+ (VkDeviceSize)250ULL, (VkDeviceSize)251ULL,
+ (VkDeviceSize)252ULL, (VkDeviceSize)253ULL,
+ (VkDeviceSize)254ULL, (VkDeviceSize)255ULL,
+ (VkDeviceSize)256ULL, (VkDeviceSize)257ULL,
+ (VkDeviceSize)258ULL, texelMax,
+ })
+ {
+ for(const VkDeviceSize offset : {
+ (VkDeviceSize)0ULL, alignStep, alignStep * 2, alignStep * 3,
+ alignStep * 4, alignStep * 5, alignStep * 6, alignStep * 7,
+ alignStep * 8, alignStep * 9, alignStep * 10, alignStep * 11,
+ alignStep * 12, alignStep * 13, alignStep * 14, alignStep * 15,
+ alignStep * 16, alignStep * 32, alignStep * 60, alignStep * 61,
+ alignStep * 62, alignStep * 63, alignStep * 64,
+ })
+ {
+ VkDeviceSize alignedByteSize = std::min(size * elemSize, bda_buffer_info.size - offset);
+
+ alignedByteSize -= (alignedByteSize % elemSize);
+
+ SizedBytes descriptor = GetDescriptor(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,
+ ptrs[i] + offset, alignedByteSize, texelFmt);
+ std::vector predicted;
+
+ for(BufferDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction =
+ PredictDescriptor(fmt, true, ptrs[i] + offset, alignedByteSize, texelFmt);
+ if(MatchPrediction(fmt, descriptor, prediction))
+ predicted.push_back(fmt);
+ }
+ if(predicted.empty())
+ {
+ failedDetections++;
+ TEST_WARN("!!! Couldn't detect buffer format");
+ DumpData("bufferCapData", bufCapData[i]);
+ TEST_LOG("Base pointer is %p", ptrs[i]);
+
+ DumpData(fmt::format("Storage Texel descriptor with offs {} size {} fmt {}", offset,
+ alignedByteSize, FormatStr(texelFmt).c_str()),
+ descriptor);
+ for(BufferDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction =
+ PredictDescriptor(fmt, true, ptrs[i] + offset, alignedByteSize, texelFmt);
+ DumpData(fmt::format("{} prediction", name(fmt)), prediction);
+ }
+ }
+
+ if(!storTexelFormats.empty())
+ {
+ // ensure that at least one previously predicted format is still predicted
+ bool oneKnown = false;
+ for(BufferDescriptorFormat fmt : predicted)
+ {
+ if(std::find(storTexelFormats.begin(), storTexelFormats.end(), fmt) !=
+ storTexelFormats.end())
+ oneKnown = true;
+ }
+
+ if(!oneKnown)
+ {
+ imperfectDetection++;
+ TEST_WARN("No commonly predicted formats. Previous:");
+ for(BufferDescriptorFormat fmt : storTexelFormats)
+ TEST_WARN("%s", name(fmt).c_str());
+ TEST_WARN("Predicted:");
+ for(BufferDescriptorFormat fmt : predicted)
+ TEST_WARN("%s", name(fmt).c_str());
+ }
+
+ if(predicted.size() < storTexelFormats.size())
+ storTexelFormats = predicted;
+ }
+ else
+ {
+ storTexelFormats = predicted;
+ }
+ }
+ }
+ }
+
+ if(blasAddr[i])
+ {
+ SizedBytes descriptor =
+ GetDescriptor(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, blasAddr[i], blasSize);
+
+ std::vector predicted;
+
+ for(BufferDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction =
+ PredictDescriptor(fmt, true, blasAddr[i], blasSize, VK_FORMAT_UNDEFINED);
+ if(MatchPrediction(fmt, descriptor, prediction))
+ predicted.push_back(fmt);
+ }
+ if(predicted.empty())
+ {
+ failedDetections++;
+ TEST_WARN("!!! Couldn't detect buffer format");
+ DumpData("blasCapData", blasCapData[i]);
+ TEST_LOG("Base pointer is %p", blasAddr[i]);
+ DumpData("asDescriptor", descriptor);
+ for(BufferDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction =
+ PredictDescriptor(fmt, true, blasAddr[i], blasSize, VK_FORMAT_UNDEFINED);
+ DumpData(fmt::format("{} prediction", name(fmt)), prediction);
+ }
+ }
+
+ if(!asFormats.empty())
+ {
+ // ensure that at least one previously predicted format is still predicted
+ bool oneKnown = false;
+ for(BufferDescriptorFormat fmt : predicted)
+ {
+ if(std::find(asFormats.begin(), asFormats.end(), fmt) != asFormats.end())
+ oneKnown = true;
+ }
+
+ if(!oneKnown)
+ {
+ imperfectDetection++;
+ TEST_WARN("No commonly predicted formats. Previous:");
+ for(BufferDescriptorFormat fmt : asFormats)
+ TEST_WARN("%s", name(fmt).c_str());
+ TEST_WARN("Predicted:");
+ for(BufferDescriptorFormat fmt : predicted)
+ TEST_WARN("%s", name(fmt).c_str());
+ }
+
+ if(predicted.size() < asFormats.size())
+ asFormats = predicted;
+ }
+ else
+ {
+ asFormats = predicted;
+ }
+ }
+ }
+
+ for(uint32_t i = 0; i < memProps->memoryTypeCount; i++)
+ {
+ if(images[i].empty())
+ continue;
+
+ for(uint32_t s = 0; s < ARRAY_COUNT(samps); s++)
+ {
+ SizedBytes descriptor =
+ GetDescriptor(VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL, samps[s], images[i][0].view);
+
+ VkDeviceAddress baseAddr = ptrs[i] + images[i][0].offset;
+
+ bool matched = false;
+
+ for(SamplerDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction = PredictDescriptor(
+ fmt, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, baseAddr, samplerCapData[s],
+ images[i][0].imgCapData, images[i][0].viewCapData);
+ if(MatchPrediction(fmt, descriptor, prediction))
+ {
+ matched = true;
+ if(combined == SamplerDescriptorFormat::Count)
+ combined = fmt;
+ if(combined != fmt)
+ {
+ imperfectDetection++;
+ TEST_WARN("Duplicate/inconsistent format detection between %s and %s",
+ name(combined).c_str(), name(fmt).c_str());
+ }
+ }
+ }
+
+ if(!matched)
+ {
+ TEST_WARN("Couldn't match descriptor");
+
+ DumpData(images[i][0].name + " imgCapData", images[i][0].imgCapData);
+ DumpData(images[i][0].name + " viewCapData", images[i][0].viewCapData);
+ DumpData(fmt::format("samplerCapData {}", s), samplerCapData[s]);
+ DumpData(fmt::format("{} + samp{} combined descriptor", images[i][0].name, s), descriptor);
+
+ for(SamplerDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction = PredictDescriptor(
+ fmt, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, baseAddr, samplerCapData[s],
+ images[i][0].imgCapData, images[i][0].viewCapData);
+ DumpData(fmt::format("{} prediction", name(fmt)), prediction);
+ }
+ }
+ }
+
+ for(uint32_t v = 0; v < images[i].size(); v++)
+ {
+ const Image &img = images[i][v];
+
+ VkDeviceAddress baseAddr = ptrs[i] + img.offset;
+
+ if(img.info.usage & VK_IMAGE_USAGE_SAMPLED_BIT)
+ {
+ bool matched = false;
+
+ SizedBytes descriptor = GetDescriptor(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
+ VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL, img.view);
+
+ for(SamplerDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction =
+ PredictDescriptor(fmt, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, baseAddr, SizedBytes(),
+ img.imgCapData, img.viewCapData);
+ if(MatchPrediction(fmt, descriptor, prediction))
+ {
+ matched = true;
+ if(sampled == SamplerDescriptorFormat::Count)
+ sampled = fmt;
+ if(sampled != fmt)
+ {
+ imperfectDetection++;
+ TEST_WARN("Duplicate/inconsistent format detection between %s and %s",
+ name(sampled).c_str(), name(fmt).c_str());
+ }
+ }
+ }
+
+ if(!matched)
+ {
+ TEST_WARN("Couldn't match descriptor");
+
+ TEST_LOG("at %llu offset image pointer range is 0x%016llx-0x%016llx", img.offset,
+ baseAddr, baseAddr + img.size);
+ DumpData(img.name + " imgCapData", img.imgCapData);
+ DumpData(img.name + " viewCapData", img.viewCapData);
+ DumpData(img.name + " sampled descriptor", descriptor);
+
+ for(SamplerDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction =
+ PredictDescriptor(fmt, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, baseAddr, SizedBytes(),
+ img.imgCapData, img.viewCapData);
+ DumpData(fmt::format("{} sampled prediction", name(fmt)), prediction);
+ }
+ }
+ }
+
+ if(img.info.usage & VK_IMAGE_USAGE_STORAGE_BIT)
+ {
+ bool matched = false;
+
+ SizedBytes descriptor = GetDescriptor(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+ VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL, img.view);
+
+ for(SamplerDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction =
+ PredictDescriptor(fmt, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, baseAddr, SizedBytes(),
+ img.imgCapData, img.viewCapData);
+ if(MatchPrediction(fmt, descriptor, prediction))
+ {
+ matched = true;
+ if(storage == SamplerDescriptorFormat::Count)
+ storage = fmt;
+ if(storage != fmt)
+ {
+ imperfectDetection++;
+ TEST_WARN("Duplicate/inconsistent format detection between %s and %s",
+ name(storage).c_str(), name(fmt).c_str());
+ }
+ }
+ }
+
+ if(!matched)
+ {
+ TEST_WARN("Couldn't match descriptor");
+
+ TEST_LOG("at %llu offset image pointer range is 0x%016llx-0x%016llx", img.offset,
+ baseAddr, baseAddr + img.size);
+ DumpData(img.name + " imgCapData", img.imgCapData);
+ DumpData(img.name + " viewCapData", img.viewCapData);
+ DumpData(img.name + " storage descriptor", descriptor);
+
+ for(SamplerDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction =
+ PredictDescriptor(fmt, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, baseAddr, SizedBytes(),
+ img.imgCapData, img.viewCapData);
+ DumpData(fmt::format("{} storage prediction", name(fmt)), prediction);
+ }
+ }
+ }
+
+ if(img.info.usage & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)
+ {
+ bool matched = false;
+
+ SizedBytes descriptor = GetDescriptor(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
+ VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL, img.view);
+
+ for(SamplerDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction =
+ PredictDescriptor(fmt, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, baseAddr, SizedBytes(),
+ img.imgCapData, img.viewCapData);
+ if(MatchPrediction(fmt, descriptor, prediction))
+ {
+ matched = true;
+ if(input == SamplerDescriptorFormat::Count)
+ input = fmt;
+ if(input != fmt)
+ {
+ imperfectDetection++;
+ TEST_WARN("Duplicate/inconsistent format detection between %s and %s",
+ name(input).c_str(), name(fmt).c_str());
+ }
+ }
+ }
+
+ if(!matched)
+ {
+ TEST_WARN("Couldn't match descriptor");
+
+ TEST_LOG("at %llu offset image pointer range is 0x%016llx-0x%016llx", img.offset,
+ baseAddr, baseAddr + img.size);
+ DumpData(img.name + " imgCapData", img.imgCapData);
+ DumpData(img.name + " viewCapData", img.viewCapData);
+ DumpData(img.name + " input descriptor", descriptor);
+
+ for(SamplerDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction =
+ PredictDescriptor(fmt, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, baseAddr,
+ SizedBytes(), img.imgCapData, img.viewCapData);
+ DumpData(fmt::format("{} input prediction", name(fmt)), prediction);
+ }
+ }
+ }
+ }
+ }
+
+ if(sparseImage.img != VK_NULL_HANDLE)
+ {
+ const Image &img = sparseImage;
+
+ VkDeviceAddress baseAddr = 0xdeadbeefdeadbeefULL;
+
+ if(img.info.usage & VK_IMAGE_USAGE_SAMPLED_BIT)
+ {
+ bool matched = false;
+
+ SizedBytes descriptor = GetDescriptor(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
+ VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL, img.view);
+
+ for(SamplerDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction = PredictDescriptor(fmt, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, baseAddr,
+ SizedBytes(), img.imgCapData, img.viewCapData);
+ if(MatchPrediction(fmt, descriptor, prediction))
+ {
+ matched = true;
+ if(sampled == SamplerDescriptorFormat::Count)
+ sampled = fmt;
+ if(sampled != fmt)
+ {
+ imperfectDetection++;
+ TEST_WARN("Duplicate/inconsistent format detection between %s and %s",
+ name(sampled).c_str(), name(fmt).c_str());
+ }
+ }
+ }
+
+ // if(!matched)
+ {
+ TEST_WARN("Couldn't match descriptor");
+
+ TEST_LOG("at %llu offset image pointer range is 0x%016llx-0x%016llx", img.offset,
+ baseAddr, baseAddr + img.size);
+ DumpData(img.name + " imgCapData", img.imgCapData);
+ DumpData(img.name + " viewCapData", img.viewCapData);
+ DumpData(img.name + " sampled descriptor", descriptor);
+
+ for(SamplerDescriptorFormat fmt : enumerate())
+ {
+ SizedBytes prediction =
+ PredictDescriptor(fmt, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, baseAddr, SizedBytes(),
+ img.imgCapData, img.viewCapData);
+ DumpData(fmt::format("{} sampled prediction", name(fmt)), prediction);
+ }
+ }
+ }
+ }
+
+ TEST_LOG("=============================================");
+ TEST_LOG("=============== Summary ===============");
+ TEST_LOG("");
+ TEST_LOG("%s", physProperties.deviceName);
+ TEST_LOG("");
+ TEST_LOG("%u failed detections", failedDetections);
+ TEST_LOG("%u imperfect detections", imperfectDetection);
+ TEST_LOG("");
+ TEST_LOG("Buffers:");
+ TEST_LOG(" UBO (%u): %s", (uint32_t)uboFormats.size(),
+ uboFormats.empty() ? "-" : name(uboFormats[0]).c_str());
+ TEST_LOG(" SSBO (%u): %s", (uint32_t)ssboFormats.size(),
+ ssboFormats.empty() ? "-" : name(ssboFormats[0]).c_str());
+ TEST_LOG(" UniTexel (%u): %s", (uint32_t)uniTexelFormats.size(),
+ uniTexelFormats.empty() ? "-" : name(uniTexelFormats[0]).c_str());
+ TEST_LOG(" StorTexel (%u): %s", (uint32_t)storTexelFormats.size(),
+ storTexelFormats.empty() ? "-" : name(storTexelFormats[0]).c_str());
+ TEST_LOG("");
+ TEST_LOG("AS (%u): %s", (uint32_t)asFormats.size(),
+ asFormats.empty() ? "-" : name(asFormats[0]).c_str());
+ TEST_LOG("");
+ TEST_LOG("Images:");
+ TEST_LOG(" Sampler (%u): %s", (uint32_t)samplerFormats.size(),
+ samplerFormats.empty() ? "-" : name(samplerFormats[0]).c_str());
+ TEST_LOG(" Combined: %s", name(combined).c_str());
+ TEST_LOG(" Sampled: %s", name(sampled).c_str());
+ TEST_LOG(" Storage: %s", name(storage).c_str());
+ TEST_LOG(" Input: %s", name(input).c_str());
+
+ return 0;
+ }
+};
+
+REGISTER_TEST();