diff --git a/util/test/demos/CMakeLists.txt b/util/test/demos/CMakeLists.txt
index 840aeb722..90290dff5 100644
--- a/util/test/demos/CMakeLists.txt
+++ b/util/test/demos/CMakeLists.txt
@@ -108,6 +108,7 @@ set(VULKAN_SRC
vk/vk_counters.cpp
vk/vk_custom_border_color.cpp
vk/vk_dedicated_allocation.cpp
+ vk/vk_descriptor_buffer.cpp
vk/vk_descriptor_index.cpp
vk/vk_descriptor_reuse.cpp
vk/vk_descriptor_variable_count.cpp
diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj
index d70b19d8d..c605881a2 100644
--- a/util/test/demos/demos.vcxproj
+++ b/util/test/demos/demos.vcxproj
@@ -312,6 +312,7 @@
+
diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters
index 69a29736e..bdf21c654 100644
--- a/util/test/demos/demos.vcxproj.filters
+++ b/util/test/demos/demos.vcxproj.filters
@@ -712,6 +712,9 @@
D3D11\demos
+
+ Vulkan\demos
+
D3D12\demos
diff --git a/util/test/demos/vk/vk_descriptor_buffer.cpp b/util/test/demos/vk/vk_descriptor_buffer.cpp
new file mode 100644
index 000000000..73a71f6cf
--- /dev/null
+++ b/util/test/demos/vk/vk_descriptor_buffer.cpp
@@ -0,0 +1,1319 @@
+/******************************************************************************
+ * 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 "vk_test.h"
+
+RD_TEST(VK_Descriptor_Buffer, VulkanGraphicsTest)
+{
+ static constexpr const char *Description =
+ "Test of EXT_descriptor_buffer based bindings and different edge cases.";
+
+ VkPhysicalDeviceDescriptorBufferFeaturesEXT descBufFeatures = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT,
+ };
+ VkPhysicalDeviceDescriptorBufferPropertiesEXT descBufProps = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT,
+ };
+
+ std::string header = R"EOSHADER(
+
+#version 460 core
+
+#extension GL_EXT_samplerless_texture_functions : require
+
+)EOSHADER";
+
+ std::string pixel = R"EOSHADER(
+
+layout(push_constant) uniform PushData {
+ vec4 data;
+} push;
+
+layout(location = 0, index = 0) out vec4 Color;
+
+layout(set = 0, binding = 1, std140) uniform aa
+{
+ vec4 data[90];
+} a;
+
+layout(set = 0, binding = 2, std140) buffer bb
+{
+ vec4 data[90];
+} b;
+
+layout(set = 0, binding = 11) uniform samplerBuffer c;
+layout(set = 0, binding = 12, rgba32f) uniform imageBuffer d;
+
+layout(set = 0, binding = 21) uniform texture2D e;
+layout(set = 0, binding = 22, rgba8) uniform image2D f;
+layout(set = 0, binding = 23, input_attachment_index = 0) uniform subpassInput g;
+
+layout(set = 0, binding = 31) uniform sampler h;
+
+layout(set = 0, binding = 41) uniform sampler2D i;
+
+#ifdef RAYS
+layout(set = 0, binding = 51) uniform accelerationStructureEXT j;
+#endif
+
+layout(set = 0, binding = 61, std140) uniform descbuff
+{
+ vec4 data[3];
+} descbuf;
+
+layout(set = 1, binding = 0) uniform sampler l;
+
+layout(set = 3, binding = 1, std140) uniform mm
+{
+ vec4 data[90];
+} m[100];
+
+layout(set = 3, binding = 2) uniform sampler2D n[100];
+
+layout(set = 3, binding = 3, std140) uniform oo
+{
+ vec4 data[90];
+} o[];
+
+layout(set = 4, binding = 1, std140) uniform pp
+{
+ vec4 data[90];
+} p;
+
+layout(set = 4, binding = 2, std140) buffer qq
+{
+ vec4 data[90];
+} q;
+
+layout(set = 5, binding = 0) uniform sampler r;
+
+layout(set = 2, binding = 0) uniform sampler t_samp[100];
+layout(set = 2, binding = 0) uniform texture2D t_tex[100];
+layout(set = 2, binding = 0) uniform sampler2D t_comb[100];
+layout(set = 2, binding = 0, std140) uniform tt_ubo
+{
+ vec4 data[90];
+} t_ubo[];
+layout(set = 2, binding = 0, std140) buffer tt_ssbo
+{
+ vec4 data[90];
+} t_ssbo[];
+#ifdef RAYS
+layout(set = 2, binding = 0) uniform accelerationStructureEXT t_as[100];
+#endif
+
+void main()
+{
+ vec2 uv = vec2(gl_FragCoord.xy - ivec2(push.data.zw))/push.data.xx;
+ ivec2 uvi = ivec2(uv*push.data.yy);
+
+ Color = vec4(uv.xy, 0.0f, 1.0f);
+
+#if defined(RAYS)
+ const vec3 light_origin = vec3(0,0,0);
+
+ const vec3 pos = vec3((uv.xy - 0.5f)*10*vec2(1,-1), 5.0f);
+
+ const float tmin = 0.01, tmax = 1000;
+ const vec3 direction = light_origin - pos;
+
+ rayQueryEXT query;
+ float blue = 0.0f;
+#if RAYS == 1
+ rayQueryInitializeEXT(query, j, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, pos, tmin, direction.xyz, 1.0);
+#elif RAYS == 2
+ blue = 1.0f;
+ rayQueryInitializeEXT(query, t_as[60], gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, pos, tmin, direction.xyz, 1.0);
+#endif
+ rayQueryProceedEXT(query);
+
+ if(rayQueryGetIntersectionTypeEXT(query, true) != gl_RayQueryCommittedIntersectionNoneEXT)
+ Color = vec4(0, 1, blue, 1);
+ else
+ Color = vec4(1, 0, blue, 1);
+#elif TEST == 0
+ Color = a.data[1] + a.data[79];
+#elif TEST == 1
+ Color = b.data[1] + b.data[79];
+#elif TEST == 2
+ Color = texelFetch(c, 1);
+#elif TEST == 3
+ Color = imageLoad(d, 1);
+#elif TEST == 4
+ Color = texelFetch(e, uvi, 0);
+#elif TEST == 5
+ Color = imageLoad(f, uvi);
+#elif TEST == 6
+ Color = subpassLoad(g);
+#elif TEST == 7
+ Color = textureLod(sampler2D(e, h), uv, 0.0);
+#elif TEST == 8
+ Color = texture(i, uv);
+#elif TEST == 9
+ // j - rays
+#elif TEST == 10
+ // inline UBO, named 'descbuf' instead of k to match resource name
+ Color = descbuf.data[1] + descbuf.data[2];
+#elif TEST == 11
+ Color = textureLod(sampler2D(e, l), uv, 0.0);
+#elif TEST == 12
+ Color = m[20].data[1] + m[20].data[79];
+#elif TEST == 13
+ Color = m[31].data[1] + m[31].data[79];
+#elif TEST == 14
+ Color = textureLod(n[20], uv, 0.0);
+#elif TEST == 15
+ Color = textureLod(n[31], uv, 0.0);
+#elif TEST == 16
+ Color = textureLod(n[41], uv, 0.0);
+#elif TEST == 17
+ Color = o[40].data[1] + o[40].data[79];
+#elif TEST == 18
+ Color = o[51].data[1] + o[51].data[79];
+#elif TEST == 19
+ Color = p.data[1] + p.data[79];
+#elif TEST == 20
+ Color = q.data[1] + q.data[79];
+#elif TEST == 21
+ Color = textureLod(sampler2D(e, r), uv, 0.0);
+#elif TEST == 22
+ Color = textureLod(sampler2D(t_tex[20], t_samp[10]), uv, 0.0);
+#elif TEST == 23
+ Color = texture(t_comb[30], uv);
+#elif TEST == 24
+ Color = t_ubo[40].data[1] + t_ubo[40].data[79];
+#elif TEST == 25
+ Color = t_ssbo[50].data[1] + t_ssbo[50].data[79];
+#endif
+}
+
+)EOSHADER";
+
+ static const uint32_t NUM_TESTS = 26;
+
+ 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_EXT_INLINE_UNIFORM_BLOCK_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);
+ devExts.push_back(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
+ devExts.push_back(VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME);
+ devExts.push_back(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
+ devExts.push_back(VK_EXT_MUTABLE_DESCRIPTOR_TYPE_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_KHR_RAY_QUERY_EXTENSION_NAME);
+
+ // Required for ray queries
+ optDevExts.push_back(VK_KHR_SPIRV_1_4_EXTENSION_NAME);
+
+ // Required by VK_KHR_spirv_1_4
+ optDevExts.push_back(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
+
+ optFeatures.sparseBinding = VK_TRUE;
+ optFeatures.sparseResidencyBuffer = VK_TRUE;
+ optFeatures.sparseResidencyImage2D = VK_TRUE;
+
+ features.fragmentStoresAndAtomics = VK_TRUE;
+
+ VulkanGraphicsTest::Prepare(argc, argv);
+
+ if(!Avail.empty())
+ return;
+
+ getPhysFeatures2(&descBufFeatures);
+ getPhysProperties2(&descBufProps);
+
+ if(!descBufFeatures.descriptorBuffer)
+ Avail = "Feature 'descriptorBuffer' 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";
+
+ 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 VkPhysicalDeviceRobustness2FeaturesEXT robustFeats = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT,
+ };
+
+ getPhysFeatures2(&robustFeats);
+
+ if(!robustFeats.nullDescriptor)
+ Avail = "feature 'nullDescriptor' not available";
+
+ robustFeats = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT,
+ };
+ robustFeats.nullDescriptor = VK_TRUE;
+
+ robustFeats.pNext = (void *)devInfoNext;
+ devInfoNext = &robustFeats;
+
+ static VkPhysicalDeviceInlineUniformBlockFeaturesEXT inlineFeatures = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT,
+ };
+
+ getPhysFeatures2(&inlineFeatures);
+
+ if(!inlineFeatures.inlineUniformBlock)
+ Avail = "feature 'inlineUniformBlock' not available";
+
+ inlineFeatures.pNext = (void *)devInfoNext;
+ devInfoNext = &inlineFeatures;
+
+ static VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT mutableFeatures = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_EXT,
+ };
+
+ getPhysFeatures2(&mutableFeatures);
+
+ if(!mutableFeatures.mutableDescriptorType)
+ Avail = "feature 'mutableDescriptorType' not available";
+
+ mutableFeatures.pNext = (void *)devInfoNext;
+ devInfoNext = &mutableFeatures;
+
+ static VkPhysicalDeviceAccelerationStructureFeaturesKHR accelFeats = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR,
+ };
+
+ static VkPhysicalDeviceRayQueryFeaturesKHR rqFeatures = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR,
+ };
+
+ if(hasExt(VK_KHR_RAY_QUERY_EXTENSION_NAME))
+ {
+ getPhysFeatures2(&accelFeats);
+
+ if(!accelFeats.accelerationStructure)
+ Avail = "feature 'accelerationStructure' not available";
+
+ accelFeats.pNext = (void *)devInfoNext;
+ devInfoNext = &accelFeats;
+
+ getPhysFeatures2(&rqFeatures);
+
+ if(!rqFeatures.rayQuery)
+ Avail = "Ray query feature 'rayQuery' not available";
+
+ rqFeatures.pNext = (void *)devInfoNext;
+ devInfoNext = &rqFeatures;
+ }
+
+ static VkPhysicalDeviceDescriptorIndexingFeaturesEXT descIndexingEnable = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT,
+ };
+
+ descIndexingEnable.runtimeDescriptorArray = VK_TRUE;
+ descIndexingEnable.shaderUniformBufferArrayNonUniformIndexing = VK_TRUE;
+ descIndexingEnable.shaderSampledImageArrayNonUniformIndexing = VK_TRUE;
+ descIndexingEnable.descriptorBindingVariableDescriptorCount = VK_TRUE;
+
+ descIndexingEnable.pNext = (void *)devInfoNext;
+ devInfoNext = &descIndexingEnable;
+ }
+
+ byte *descWrite;
+
+ VkDeviceAddress dataAddress;
+ VkDeviceSize setOffset;
+
+ bool mutableSet = false;
+
+ const VkDescriptorType mutableTypes[6] = {
+ VK_DESCRIPTOR_TYPE_SAMPLER,
+ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
+ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
+ };
+
+ size_t DescSize(VkDescriptorType type)
+ {
+ if(type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)
+ return descBufProps.uniformBufferDescriptorSize;
+ else if(type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
+ return descBufProps.storageBufferDescriptorSize;
+ else if(type == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER)
+ return descBufProps.uniformTexelBufferDescriptorSize;
+ else if(type == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER)
+ return descBufProps.storageTexelBufferDescriptorSize;
+ else if(type == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE)
+ return descBufProps.sampledImageDescriptorSize;
+ else if(type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
+ return descBufProps.storageImageDescriptorSize;
+ else if(type == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT)
+ return descBufProps.inputAttachmentDescriptorSize;
+ else if(type == VK_DESCRIPTOR_TYPE_SAMPLER)
+ return descBufProps.samplerDescriptorSize;
+ else if(type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+ return descBufProps.combinedImageSamplerDescriptorSize;
+ else if(type == VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR)
+ return descBufProps.accelerationStructureDescriptorSize;
+
+ return 0;
+ }
+
+ size_t DescStride(VkDescriptorType type)
+ {
+ if(mutableSet)
+ {
+ size_t ret = 0;
+ for(VkDescriptorType t : mutableTypes)
+ ret = std::max(ret, DescSize(t));
+ return ret;
+ }
+
+ return DescSize(type);
+ }
+
+ struct BindRef
+ {
+ uint32_t bind;
+ uint32_t idx;
+
+ BindRef(uint32_t b) : bind(b), idx(0) {}
+ BindRef(std::initializer_list bind_idx)
+ : bind(bind_idx.begin()[0]), idx(bind_idx.begin()[1])
+ {
+ }
+ };
+
+ void FillDescriptor(VkDescriptorSetLayout layout, BindRef bind, VkDescriptorType type,
+ VkDeviceSize offset, VkDeviceSize range, VkFormat format = VK_FORMAT_UNDEFINED)
+ {
+ VkDeviceSize bindOffset;
+ vkGetDescriptorSetLayoutBindingOffsetEXT(device, layout, bind.bind, &bindOffset);
+ void *dst = descWrite + setOffset + bindOffset + DescStride(type) * bind.idx;
+
+ VkDescriptorGetInfoEXT get = {VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT};
+ get.type = type;
+
+ VkDescriptorAddressInfoEXT buf = {VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT};
+ buf.address = dataAddress + offset;
+ buf.range = range;
+ buf.format = format;
+ get.data.pStorageBuffer = &buf;
+
+ vkGetDescriptorEXT(device, &get, DescSize(type), dst);
+ }
+
+ void FillDescriptor(VkDescriptorSetLayout layout, BindRef bind, VkAccelerationStructureKHR as)
+ {
+ VkDeviceSize bindOffset;
+ vkGetDescriptorSetLayoutBindingOffsetEXT(device, layout, bind.bind, &bindOffset);
+ void *dst = descWrite + setOffset + bindOffset +
+ DescStride(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR) * bind.idx;
+
+ VkDescriptorGetInfoEXT get = {VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT};
+ get.type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
+
+ VkAccelerationStructureDeviceAddressInfoKHR info = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR,
+ };
+ info.accelerationStructure = as;
+ get.data.accelerationStructure = vkGetAccelerationStructureDeviceAddressKHR(device, &info);
+
+ vkGetDescriptorEXT(device, &get, DescSize(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR), dst);
+ }
+
+ void FillDescriptor(VkDescriptorSetLayout layout, BindRef bind, VkDescriptorType type,
+ VkSampler sampler, VkImageView view)
+ {
+ VkDeviceSize bindOffset;
+ vkGetDescriptorSetLayoutBindingOffsetEXT(device, layout, bind.bind, &bindOffset);
+ void *dst = descWrite + setOffset + bindOffset + DescStride(type) * bind.idx;
+
+ VkDescriptorGetInfoEXT get = {VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT};
+ get.type = type;
+
+ VkDescriptorImageInfo im;
+ im.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
+ im.imageView = view;
+ im.sampler = sampler;
+
+ if(type == VK_DESCRIPTOR_TYPE_SAMPLER)
+ get.data.pSampler = &sampler;
+ else
+ get.data.pCombinedImageSampler = &im;
+
+ vkGetDescriptorEXT(device, &get, DescSize(type), dst);
+ }
+
+ void FillDescriptor(VkDescriptorSetLayout layout, BindRef bind, VkDescriptorType type)
+ {
+ VkDeviceSize bindOffset;
+ vkGetDescriptorSetLayoutBindingOffsetEXT(device, layout, bind.bind, &bindOffset);
+ void *dst = descWrite + setOffset + bindOffset + DescSize(type) * bind.idx;
+
+ VkDescriptorGetInfoEXT get = {VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT};
+ get.type = type;
+
+ vkGetDescriptorEXT(device, &get, DescSize(type), dst);
+ }
+
+ AllocatedBuffer MakeTestBuffer(const char *name, uint32_t offset, const Vec4f &data)
+ {
+ // use 256 aligned sizes for buffers so we can check this on all drivers, we don't care to test
+ // aliasing caused by different sizes
+ VkDeviceSize size = AlignUp(offset, 0x100U) + 0x2000;
+ AllocatedBuffer ret(this,
+ vkh::BufferCreateInfo(size, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR |
+ VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
+ VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
+ VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT),
+ VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));
+ setName(ret.buffer, name);
+ dataAddress = ret.address;
+ byte *ptr = ret.map();
+ // fill with garbage (that will be a relatively normal float value)
+ memset(ptr, 0x3f, size);
+ memcpy(ptr + offset, &data, sizeof(data));
+ ret.unmap();
+
+ return ret;
+ }
+
+ static const uint32_t texSize = 4;
+
+ VkImageView MakeTestImage(const char *name, const Vec4f &col)
+ {
+ // make images half one colour half black, so we can test samplers that are linear vs point
+ Vec4f pixels[texSize * texSize] = {};
+
+ static AllocatedBuffer uploadBuf(
+ this,
+ vkh::BufferCreateInfo(texSize * texSize * sizeof(Vec4f), VK_BUFFER_USAGE_TRANSFER_SRC_BIT),
+ VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));
+
+ AllocatedImage tex(
+ this,
+ vkh::ImageCreateInfo(texSize, texSize, 0, VK_FORMAT_R32G32B32A32_SFLOAT,
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
+ VK_IMAGE_USAGE_STORAGE_BIT),
+ VmaAllocationCreateInfo(
+ {VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_GPU_ONLY}));
+ setName(tex.image, name);
+
+ for(int i = 0; i < texSize * texSize / 2; i++)
+ pixels[i] = col;
+ uploadBuf.upload(pixels);
+ uploadBufferToImage(tex.image, {texSize, texSize, 1}, uploadBuf.buffer, VK_IMAGE_LAYOUT_GENERAL);
+
+ return createImageView(
+ vkh::ImageViewCreateInfo(tex.image, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R32G32B32A32_SFLOAT));
+ }
+
+ int main()
+ {
+ vmaBDA = true;
+
+ // initialise, create window, create context, etc
+ if(!Init())
+ return 3;
+
+ bool rays = hasExt(VK_KHR_RAY_QUERY_EXTENSION_NAME);
+
+ VkDescriptorSetLayout singlesetlayout = createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo(
+ {
+ {1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+ {2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+
+ {11, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+ {12, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+
+ {21, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+ {22, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+ {23, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+
+ {31, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+
+ {41, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+
+ {51, rays ? VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+
+ {61, VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK, 32, VK_SHADER_STAGE_FRAGMENT_BIT},
+ },
+ VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT));
+
+ VkDescriptorSetLayout samplayout = createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo(
+ {
+ {0, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+ },
+ VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT));
+
+ VkDescriptorBindingFlagsEXT bindFlags[] = {
+ 0,
+ 0,
+ VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT,
+ };
+
+ VkDescriptorSetLayoutBindingFlagsCreateInfoEXT descFlags = {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT,
+ };
+ descFlags.bindingCount = ARRAY_COUNT(bindFlags);
+ descFlags.pBindingFlags = bindFlags;
+
+ VkDescriptorSetLayout arraysetlayout = createDescriptorSetLayout(
+ vkh::DescriptorSetLayoutCreateInfo(
+ {
+ {1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 100, VK_SHADER_STAGE_FRAGMENT_BIT},
+ {2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 100, VK_SHADER_STAGE_FRAGMENT_BIT},
+ {3, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000000, VK_SHADER_STAGE_FRAGMENT_BIT},
+ },
+ VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT)
+ .next(&descFlags));
+
+ VkMutableDescriptorTypeListEXT mutableList = {
+ ARRAY_COUNT(mutableTypes),
+ mutableTypes,
+ };
+ VkMutableDescriptorTypeCreateInfoEXT mutableTypeInfo = {
+ VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT,
+ NULL,
+ 1,
+ &mutableList,
+ };
+
+ VkDescriptorSetLayout mutablelayout = createDescriptorSetLayout(
+ vkh::DescriptorSetLayoutCreateInfo(
+ {
+ {0, VK_DESCRIPTOR_TYPE_MUTABLE_EXT, 100, VK_SHADER_STAGE_FRAGMENT_BIT},
+ },
+ VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT)
+ .next(&mutableTypeInfo));
+
+ // we need each sampler to be different in a way that can't be deduplicated and aliased
+ vkh::SamplerCreateInfo sampInfo(VK_FILTER_LINEAR);
+
+ sampInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ sampInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ sampInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+
+ VkSampler h = createSampler(sampInfo);
+ setName(h, "h");
+
+ sampInfo.magFilter = VK_FILTER_NEAREST;
+
+ VkSampler i_samp = createSampler(sampInfo);
+ setName(i_samp, "i_samp");
+
+ sampInfo.minFilter = VK_FILTER_NEAREST;
+
+ VkSampler l = createSampler(sampInfo);
+ setName(l, "l");
+
+ sampInfo.magFilter = VK_FILTER_LINEAR;
+
+ VkSampler n_20_samp = createSampler(sampInfo);
+ setName(n_20_samp, "n_20_samp");
+
+ sampInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+
+ VkSampler n_31_samp = createSampler(sampInfo);
+ setName(n_31_samp, "n_31_samp");
+
+ sampInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+
+ VkSampler n_41_samp = createSampler(sampInfo);
+ setName(n_41_samp, "n_41_samp");
+
+ sampInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+
+ VkSampler r = createSampler(sampInfo);
+ setName(r, "r");
+
+ sampInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
+ sampInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
+ sampInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
+ sampInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
+
+ VkSampler t_samp_10 = createSampler(sampInfo);
+ setName(t_samp_10, "t_samp_10");
+
+ sampInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
+
+ VkSampler t_comb_30_samp = createSampler(sampInfo);
+ setName(t_comb_30_samp, "t_comb_30_samp");
+
+ VkDescriptorSetLayout immutsetlayout =
+ createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo(
+ {
+ {0, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, &r},
+ },
+ VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT |
+ VK_DESCRIPTOR_SET_LAYOUT_CREATE_EMBEDDED_IMMUTABLE_SAMPLERS_BIT_EXT));
+
+ VkDescriptorSetLayout pushlayout = createDescriptorSetLayout(vkh::DescriptorSetLayoutCreateInfo(
+ {
+ {1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+ {2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
+ },
+ VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR |
+ VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT));
+
+ VkPipelineLayout layout = createPipelineLayout(vkh::PipelineLayoutCreateInfo(
+ {singlesetlayout, samplayout, mutablelayout, arraysetlayout, pushlayout, immutsetlayout},
+ {
+ vkh::PushConstantRange(VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(Vec4i)),
+ }));
+
+ // because some devices don't support more than one sampler heap, and we definitely want to test
+ // combined image/samplers, we just test with one descriptor buffer by default and just add a
+ // stupid sampler-only heap to test multiple heaps
+ AllocatedBuffer descbuf(
+ this,
+ vkh::BufferCreateInfo(0x100000, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR |
+ VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT |
+ VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT),
+ VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));
+ setName(descbuf.buffer, "descbuf");
+
+ AllocatedBuffer sampbuf;
+ if(descBufProps.maxSamplerDescriptorBufferBindings > 1)
+ {
+ sampbuf = AllocatedBuffer(
+ this,
+ vkh::BufferCreateInfo(0x100000, VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR |
+ VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT),
+ VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));
+ setName(sampbuf.buffer, "sampbuf");
+ }
+
+ AllocatedBuffer pushbuf;
+ if(descBufProps.bufferlessPushDescriptors == VK_FALSE)
+ {
+ pushbuf = AllocatedBuffer(
+ this,
+ vkh::BufferCreateInfo(0x100000,
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR |
+ VK_BUFFER_USAGE_PUSH_DESCRIPTORS_DESCRIPTOR_BUFFER_BIT_EXT |
+ VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT),
+ VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));
+ setName(pushbuf.buffer, "pushbuf");
+ }
+
+ byte *descs = descbuf.map();
+ // ensure that we never read 0s except from a NULL descriptor
+ memset(descs, 0xcc, 0x100000);
+
+ AllocatedImage input(
+ this,
+ vkh::ImageCreateInfo(screenWidth, screenHeight, 0, VK_FORMAT_R32G32B32A32_SFLOAT,
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
+ VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT),
+ VmaAllocationCreateInfo(
+ {VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_GPU_ONLY}));
+ setName(input.image, "g");
+
+ VkImageView g = createImageView(vkh::ImageViewCreateInfo(input.image, VK_IMAGE_VIEW_TYPE_2D,
+ VK_FORMAT_R32G32B32A32_SFLOAT));
+
+ AllocatedImage colatt(
+ this,
+ vkh::ImageCreateInfo(screenWidth, screenHeight, 0, VK_FORMAT_R32G32B32A32_SFLOAT,
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+ VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT),
+ VmaAllocationCreateInfo(
+ {VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_GPU_ONLY}));
+ setName(colatt.image, "colatt");
+
+ VkImageView colview = createImageView(vkh::ImageViewCreateInfo(
+ colatt.image, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R32G32B32A32_SFLOAT));
+
+ vkh::RenderPassCreator renderPassCreateInfo;
+
+ renderPassCreateInfo.attachments.push_back(
+ vkh::AttachmentDescription(VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_GENERAL, VK_ATTACHMENT_LOAD_OP_CLEAR));
+ renderPassCreateInfo.attachments.push_back(
+ vkh::AttachmentDescription(VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_GENERAL,
+ VK_IMAGE_LAYOUT_GENERAL, VK_ATTACHMENT_LOAD_OP_LOAD));
+
+ renderPassCreateInfo.addSubpass({VkAttachmentReference({0, VK_IMAGE_LAYOUT_GENERAL})},
+ VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_UNDEFINED, {},
+ {VkAttachmentReference({1, VK_IMAGE_LAYOUT_GENERAL})});
+
+ VkRenderPass renderPass = createRenderPass(renderPassCreateInfo);
+
+ VkFramebuffer framebuffer = createFramebuffer(
+ vkh::FramebufferCreateInfo(renderPass, {colview, g}, mainWindow->scissor.extent));
+
+ vkh::GraphicsPipelineCreateInfo pipeCreateInfo;
+
+ pipeCreateInfo.layout = layout;
+ pipeCreateInfo.renderPass = renderPass;
+
+ pipeCreateInfo.inputAssemblyState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
+
+ pipeCreateInfo.flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT;
+
+ std::vector tests;
+
+ pipeCreateInfo.stages.resize(2);
+ pipeCreateInfo.stages[0] =
+ CompileShaderModule(VKFullscreenQuadVertex, ShaderLang::glsl, ShaderStage::vert, "main");
+
+ for(uint32_t i = 0; i < NUM_TESTS; i++)
+ {
+ pipeCreateInfo.stages[1] =
+ CompileShaderModule(header + "#define TEST " + std::to_string(i) + pixel,
+ ShaderLang::glsl, ShaderStage::frag, "main");
+
+ tests.push_back(createGraphicsPipeline(pipeCreateInfo));
+ }
+
+ if(rays)
+ {
+ pipeCreateInfo.stages[1] = CompileShaderModule(header +
+ "\n"
+ "#extension GL_EXT_ray_query : enable\n"
+ " #define RAYS 1 \n" +
+ pixel,
+ ShaderLang::glsl, ShaderStage::frag, "main");
+
+ tests.push_back(createGraphicsPipeline(pipeCreateInfo));
+
+ pipeCreateInfo.stages[1] = CompileShaderModule(header +
+ "\n"
+ "#extension GL_EXT_ray_query : enable\n"
+ " #define RAYS 2 \n" +
+ pixel,
+ ShaderLang::glsl, ShaderStage::frag, "main");
+
+ tests.push_back(createGraphicsPipeline(pipeCreateInfo));
+ }
+
+ VkImageView e = MakeTestImage("e", Vec4f(1.0f, 0.0f, 0.0f, 1.0f));
+ VkImageView f = MakeTestImage("f", Vec4f(0.0f, 1.0f, 0.0f, 1.0f));
+ VkImageView i_tex = MakeTestImage("i_tex", Vec4f(1.0f, 0.0f, 1.0f, 1.0f));
+ VkImageView n_20_tex = MakeTestImage("n_20_tex", Vec4f(1.0f, 1.0f, 0.0f, 1.0f));
+ VkImageView t_tex_20 = MakeTestImage("t_tex_20", Vec4f(0.0f, 1.0f, 1.0f, 1.0f));
+ VkImageView t_comb_30_tex = MakeTestImage("t_comb_30_tex", Vec4f(0.5f, 0.0f, 0.5f, 1.0f));
+
+ AllocatedBuffer blasBuffer;
+ VkAccelerationStructureKHR blas;
+ AllocatedBuffer tlasBuffer;
+ VkAccelerationStructureKHR j;
+ VkAccelerationStructureKHR t_as_60;
+ if(rays)
+ {
+ Vec3f vertices[] = {
+ // Triangle
+ {0.0f, 0.3f, 0.5f},
+ {-0.3f, -0.3f, 0.5f},
+ {0.3f, -0.3f, 0.5f},
+ };
+
+ uint32_t indices[] = {0, 1, 2};
+
+ uint32_t primitiveCount = (uint32_t)sizeof(indices) / (sizeof(indices[0]) * 3);
+ uint32_t indexCount = (uint32_t)sizeof(indices) / sizeof(indices[0]);
+ uint32_t vertexCount = (uint32_t)sizeof(vertices) / sizeof(vertices[0]);
+
+ VkFormat vertexFormat = VK_FORMAT_R32G32B32_SFLOAT;
+
+ constexpr VkBufferUsageFlags blasInputBufferUsageFlags =
+ VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR |
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR;
+
+ VkTransformMatrixKHR identityTransformMatrix = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
+ 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f};
+
+ VkTransformMatrixKHR blasTransformMatrix = identityTransformMatrix;
+
+ const size_t vertexBufferSize = vertexCount * sizeof(vertices[0]);
+ const size_t indexBufferSize = indexCount * sizeof(indices[0]);
+
+ AllocatedBuffer blasVertexBuffer(
+ this, vkh::BufferCreateInfo(vertexBufferSize, blasInputBufferUsageFlags),
+ VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));
+ AllocatedBuffer blasIndexBuffer(
+ this, vkh::BufferCreateInfo(indexBufferSize, blasInputBufferUsageFlags),
+ VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));
+
+ blasVertexBuffer.upload(vertices, vertexBufferSize);
+ blasIndexBuffer.upload(indices, indexBufferSize);
+
+ /*
+ * Create bottom level acceleration structure
+ */
+ VkAccelerationStructureGeometryKHR blasGeometry = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR};
+ blasGeometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
+ blasGeometry.flags = VK_GEOMETRY_OPAQUE_BIT_KHR;
+ blasGeometry.geometry.triangles.sType =
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR;
+ blasGeometry.geometry.triangles.vertexFormat = vertexFormat;
+ blasGeometry.geometry.triangles.maxVertex = vertexCount - 1;
+ blasGeometry.geometry.triangles.vertexStride = sizeof(Vec3f);
+ blasGeometry.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32;
+ blasGeometry.geometry.triangles.vertexData.deviceAddress = blasVertexBuffer.address;
+ blasGeometry.geometry.triangles.indexData.deviceAddress = blasIndexBuffer.address;
+ blasGeometry.geometry.triangles.transformData.deviceAddress = 0;
+ std::vector blasGeometries = {blasGeometry};
+
+ VkAccelerationStructureBuildRangeInfoKHR buildRangeInfo = {primitiveCount, 0, 0, 0};
+
+ std::vector asBuildRangeInfosVector = {buildRangeInfo};
+ VkAccelerationStructureBuildRangeInfoKHR *asBuildRangeInfos = asBuildRangeInfosVector.data();
+ std::vector primitiveCounts = {primitiveCount};
+
+ VkAccelerationStructureBuildGeometryInfoKHR blasBuildGeometryInfo = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR};
+ blasBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
+ blasBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR |
+ VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR;
+ blasBuildGeometryInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;
+ blasBuildGeometryInfo.geometryCount = (uint32_t)blasGeometries.size();
+ blasBuildGeometryInfo.pGeometries = blasGeometries.data();
+
+ VkAccelerationStructureBuildSizesInfoKHR blasBuildSizesInfo = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR};
+ vkGetAccelerationStructureBuildSizesKHR(
+ device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &blasBuildGeometryInfo,
+ primitiveCounts.data(), &blasBuildSizesInfo);
+
+ blasBuffer = AllocatedBuffer(
+ this,
+ vkh::BufferCreateInfo(blasBuildSizesInfo.accelerationStructureSize,
+ VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR |
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR),
+ VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY}));
+
+ VkAccelerationStructureCreateInfoKHR blasCreateInfo = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR};
+ blasCreateInfo.buffer = blasBuffer.buffer;
+ blasCreateInfo.size = blasBuildSizesInfo.accelerationStructureSize;
+ blasCreateInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
+
+ CHECK_VKR(vkCreateAccelerationStructureKHR(device, &blasCreateInfo, VK_NULL_HANDLE, &blas))
+ setName(blas, "blas");
+
+ VkAccelerationStructureDeviceAddressInfoKHR blasDeviceAddressInfo = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR, NULL, blas};
+ uint64_t blasDeviceAddress =
+ vkGetAccelerationStructureDeviceAddressKHR(device, &blasDeviceAddressInfo);
+
+ AllocatedBuffer blasScratchBuffer(
+ this,
+ vkh::BufferCreateInfo(
+ blasBuildSizesInfo.buildScratchSize,
+ VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR),
+ VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY}));
+
+ blasBuildGeometryInfo.scratchData.deviceAddress = blasScratchBuffer.address;
+ blasBuildGeometryInfo.dstAccelerationStructure = blas;
+
+ {
+ VkCommandBuffer cmd = GetCommandBuffer();
+ CHECK_VKR(vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()));
+ vkCmdBuildAccelerationStructuresKHR(cmd, 1, &blasBuildGeometryInfo, &asBuildRangeInfos);
+ CHECK_VKR(vkEndCommandBuffer(cmd));
+ Submit(99, 99, {cmd});
+ }
+
+ /*
+ * Create top level acceleration structure
+ */
+ VkTransformMatrixKHR tlasTransformMatrix = identityTransformMatrix;
+
+ VkAccelerationStructureInstanceKHR asInstance = {
+ tlasTransformMatrix,
+ 0,
+ 0xFF,
+ 0,
+ VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR,
+ blasDeviceAddress,
+ };
+
+ const size_t asInstanceSize = sizeof(asInstance);
+
+ AllocatedBuffer instancesBuffer(
+ this,
+ vkh::BufferCreateInfo(
+ asInstanceSize, VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR |
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR),
+ VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_CPU_TO_GPU}));
+ instancesBuffer.upload(&asInstance, asInstanceSize);
+
+ VkAccelerationStructureGeometryKHR tlasGeometry = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR};
+ tlasGeometry.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR;
+ tlasGeometry.flags = VK_GEOMETRY_OPAQUE_BIT_KHR;
+ tlasGeometry.geometry.instances.sType =
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR;
+ tlasGeometry.geometry.instances.arrayOfPointers = VK_FALSE;
+ tlasGeometry.geometry.instances.data.deviceAddress = instancesBuffer.address;
+
+ std::vector tlasGeometries = {tlasGeometry};
+
+ VkAccelerationStructureBuildGeometryInfoKHR tlasBuildGeometryInfo = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR};
+ tlasBuildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR;
+ tlasBuildGeometryInfo.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
+ tlasBuildGeometryInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR;
+ tlasBuildGeometryInfo.geometryCount = (uint32_t)tlasGeometries.size();
+ tlasBuildGeometryInfo.pGeometries = tlasGeometries.data();
+
+ VkAccelerationStructureBuildSizesInfoKHR tlasBuildSizesInfo = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR};
+ vkGetAccelerationStructureBuildSizesKHR(
+ device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &tlasBuildGeometryInfo,
+ primitiveCounts.data(), &tlasBuildSizesInfo);
+
+ tlasBuffer = AllocatedBuffer(
+ this,
+ vkh::BufferCreateInfo(tlasBuildSizesInfo.accelerationStructureSize * 2 + 0x1000,
+ VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR |
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR),
+ VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY}));
+
+ VkAccelerationStructureCreateInfoKHR tlasCreateInfo = {
+ VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR};
+ tlasCreateInfo.buffer = tlasBuffer.buffer;
+ tlasCreateInfo.size = tlasBuildSizesInfo.accelerationStructureSize;
+ tlasCreateInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR;
+
+ CHECK_VKR(vkCreateAccelerationStructureKHR(device, &tlasCreateInfo, VK_NULL_HANDLE, &j));
+ setName(j, "j");
+
+ tlasCreateInfo.offset = 0x1000;
+
+ CHECK_VKR(vkCreateAccelerationStructureKHR(device, &tlasCreateInfo, VK_NULL_HANDLE, &t_as_60));
+ setName(t_as_60, "t_as_60");
+
+ AllocatedBuffer tlasScratchBuffer(
+ this,
+ vkh::BufferCreateInfo(
+ tlasBuildSizesInfo.buildScratchSize,
+ VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR),
+ VmaAllocationCreateInfo({0, VMA_MEMORY_USAGE_GPU_ONLY}));
+
+ tlasBuildGeometryInfo.scratchData.deviceAddress = tlasScratchBuffer.address;
+ tlasBuildGeometryInfo.dstAccelerationStructure = j;
+
+ asBuildRangeInfosVector[0].primitiveCount = 1;
+
+ {
+ VkCommandBuffer cmd = GetCommandBuffer();
+ CHECK_VKR(vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo()));
+ vkCmdBuildAccelerationStructuresKHR(cmd, 1, &tlasBuildGeometryInfo, &asBuildRangeInfos);
+ tlasBuildGeometryInfo.dstAccelerationStructure = t_as_60;
+ vkCmdBuildAccelerationStructuresKHR(cmd, 1, &tlasBuildGeometryInfo, &asBuildRangeInfos);
+ CHECK_VKR(vkEndCommandBuffer(cmd));
+ Submit(99, 99, {cmd});
+ }
+ }
+
+ uint32_t bufIdxs[6] = {};
+ VkDeviceSize setOffsets[6];
+
+ {
+ // single set
+ setOffsets[0] = 0;
+
+ VkDeviceSize sz = 0;
+ vkGetDescriptorSetLayoutSizeEXT(device, singlesetlayout, &sz);
+
+ // sampler set
+ setOffsets[1] = setOffsets[0] + std::max(sz, (VkDeviceSize)0x4000ULL);
+
+ vkGetDescriptorSetLayoutSizeEXT(device, samplayout, &sz);
+
+ // mutable set
+ setOffsets[2] = setOffsets[1] + std::max(sz, (VkDeviceSize)0x400ULL);
+
+ vkGetDescriptorSetLayoutSizeEXT(device, mutablelayout, &sz);
+
+ // array set
+ setOffsets[3] = setOffsets[2] + std::max(sz, (VkDeviceSize)0x4000ULL);
+
+ vkGetDescriptorSetLayoutSizeEXT(device, arraysetlayout, &sz);
+ }
+
+ VkDescriptorBufferBindingInfoEXT descBind[3] = {};
+ VkDescriptorBufferBindingPushDescriptorBufferHandleEXT pushbufHandle = {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_PUSH_DESCRIPTOR_BUFFER_HANDLE_EXT,
+ };
+
+ uint32_t numBufs = 1;
+ descBind[0] = {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT,
+ NULL,
+ descbuf.address,
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR |
+ VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT |
+ VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT,
+ };
+ if(sampbuf.address)
+ {
+ bufIdxs[1] = numBufs;
+ setOffsets[1] = 0;
+
+ descBind[numBufs] = {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT,
+ NULL,
+ sampbuf.address,
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR |
+ VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT,
+ };
+ numBufs++;
+ }
+ if(pushbuf.address)
+ {
+ bufIdxs[4] = numBufs;
+
+ descBind[numBufs] = {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT,
+ NULL,
+ sampbuf.address,
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR |
+ VK_BUFFER_USAGE_PUSH_DESCRIPTORS_DESCRIPTOR_BUFFER_BIT_EXT |
+ VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT,
+ };
+ pushbufHandle.buffer = pushbuf.buffer;
+ descBind[numBufs].pNext = &pushbufHandle;
+ numBufs++;
+ }
+
+ ////////////// set 0 ////////////////
+ ////////////// single ////////////////
+ descWrite = descs;
+ setOffset = setOffsets[0];
+
+ MakeTestBuffer("a", 0x310, Vec4f(1.0f, 2.0f, 3.0f, 4.0f));
+ FillDescriptor(singlesetlayout, 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0x300, 256);
+
+ MakeTestBuffer("b", 0x210, Vec4f(5.0f, 6.0f, 7.0f, 8.0f));
+ FillDescriptor(singlesetlayout, 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0x200, 512);
+
+ MakeTestBuffer("c", 0x110, Vec4f(9.0f, 10.0f, 11.0f, 12.0f));
+ FillDescriptor(singlesetlayout, 11, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 0x100, 256,
+ VK_FORMAT_R32G32B32A32_SFLOAT);
+ MakeTestBuffer("d", 0x410, Vec4f(13.0f, 14.0f, 15.0f, 16.0f));
+ FillDescriptor(singlesetlayout, 12, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 0x400, 512,
+ VK_FORMAT_R32G32B32A32_SFLOAT);
+
+ FillDescriptor(singlesetlayout, 21, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_NULL_HANDLE, e);
+ FillDescriptor(singlesetlayout, 22, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_NULL_HANDLE, f);
+
+ FillDescriptor(singlesetlayout, 23, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, VK_NULL_HANDLE, g);
+
+ FillDescriptor(singlesetlayout, 31, VK_DESCRIPTOR_TYPE_SAMPLER, h, VK_NULL_HANDLE);
+
+ FillDescriptor(singlesetlayout, 41, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, i_samp, i_tex);
+
+ FillDescriptor(singlesetlayout, 51, j);
+
+ VkDeviceSize inlineOffset = 0;
+ vkGetDescriptorSetLayoutBindingOffsetEXT(device, singlesetlayout, 61, &inlineOffset);
+ Vec4f inlineData = Vec4f(17.0f, 18.0f, 19.0f, 20.0f);
+ memcpy(descWrite + setOffset + inlineOffset + sizeof(Vec4f), &inlineData, sizeof(inlineData));
+
+ ////////////// set 3 ////////////////
+ ////////////// array ////////////////
+ setOffset = setOffsets[3];
+
+ MakeTestBuffer("m_20", 0x610, Vec4f(21.0f, 22.0f, 23.0f, 24.0f));
+ FillDescriptor(arraysetlayout, {1, 20}, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0x600, 256);
+ FillDescriptor(arraysetlayout, {1, 31}, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
+
+ FillDescriptor(arraysetlayout, {2, 20}, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, n_20_samp,
+ n_20_tex);
+ FillDescriptor(arraysetlayout, {2, 31}, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, n_31_samp,
+ VK_NULL_HANDLE);
+ FillDescriptor(arraysetlayout, {2, 41}, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, n_41_samp,
+ VK_NULL_HANDLE);
+
+ MakeTestBuffer("o_40", 0xf10, Vec4f(25.0f, 26.0f, 27.0f, 28.0f));
+ FillDescriptor(arraysetlayout, {3, 40}, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0xf00, 256);
+ FillDescriptor(arraysetlayout, {3, 51}, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
+
+ ////////////// set 2 ////////////////
+ ////////////// mutable ////////////////
+ setOffset = setOffsets[2];
+ mutableSet = true;
+
+ FillDescriptor(mutablelayout, {0, 10}, VK_DESCRIPTOR_TYPE_SAMPLER, t_samp_10, VK_NULL_HANDLE);
+ FillDescriptor(mutablelayout, {0, 20}, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_NULL_HANDLE,
+ t_tex_20);
+ FillDescriptor(mutablelayout, {0, 30}, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ t_comb_30_samp, t_comb_30_tex);
+ MakeTestBuffer("t_ubo_40", 0x510, Vec4f(29.0f, 30.0f, 31.0f, 32.0f));
+ FillDescriptor(mutablelayout, {0, 40}, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0x500, 768);
+
+ MakeTestBuffer("t_ssbo_50", 0x710, Vec4f(33.0f, 34.0f, 35.0f, 36.0f));
+ FillDescriptor(mutablelayout, {0, 50}, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0x700, 256);
+
+ FillDescriptor(mutablelayout, {0, 60}, t_as_60);
+
+ mutableSet = false;
+
+ ////////////// set 1 ////////////////
+ ////////////// sampler ///////////////
+ setOffset = setOffsets[1];
+ if(sampbuf.address)
+ {
+ descWrite = sampbuf.map();
+ memset(descWrite, 0xcc, 0x100000);
+ }
+
+ FillDescriptor(samplayout, 0, VK_DESCRIPTOR_TYPE_SAMPLER, l, VK_NULL_HANDLE);
+
+ // set 4 is push data, and set 5 is immutable samplers
+
+ AllocatedBuffer pushbuf1 = MakeTestBuffer("p", 0x210, Vec4f(100.0f, 101.0f, 102.0f, 103.0f));
+ AllocatedBuffer pushbuf2 = MakeTestBuffer("q", 0x310, Vec4f(104.0f, 105.0f, 106.0f, 107.0f));
+
+ while(Running())
+ {
+ VkCommandBuffer cmd = GetCommandBuffer();
+
+ vkBeginCommandBuffer(cmd, vkh::CommandBufferBeginInfo());
+
+ vkCmdBindDescriptorBuffersEXT(cmd, numBufs, descBind);
+
+ // if we have a push buffer bind them all, if not bind starting from 1
+ uint32_t numSets = 4;
+ if(pushbuf.address)
+ numSets++;
+
+ vkCmdSetDescriptorBufferOffsetsEXT(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, numSets,
+ bufIdxs, setOffsets);
+ vkCmdBindDescriptorBufferEmbeddedSamplersEXT(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 5);
+
+ std::vector pushBufInfos = {
+ vkh::DescriptorBufferInfo(pushbuf1.buffer, 0x200, 0x100)};
+ vkCmdPushDescriptorSetKHR(
+ cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 4, 1,
+ vkh::WriteDescriptorSet(VK_NULL_HANDLE, 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, pushBufInfos));
+ pushBufInfos = {vkh::DescriptorBufferInfo(pushbuf2.buffer, 0x300, 0x100)};
+ vkCmdPushDescriptorSetKHR(
+ cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 4, 1,
+ vkh::WriteDescriptorSet(VK_NULL_HANDLE, 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, pushBufInfos));
+
+ VkImage swapimg = StartUsingBackbuffer(cmd);
+
+ vkh::cmdPipelineBarrier(
+ cmd, {
+ vkh::ImageMemoryBarrier(0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_GENERAL, input.image),
+ });
+
+ vkh::cmdClearImage(cmd, input.image, vkh::ClearColorValue(1.0f, 0.5f, 0.0f, 1.0f));
+
+ vkh::cmdPipelineBarrier(
+ cmd, {
+ vkh::ImageMemoryBarrier(
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,
+ VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL, input.image),
+ });
+
+ vkCmdBeginRenderPass(cmd,
+ vkh::RenderPassBeginInfo(renderPass, framebuffer, mainWindow->scissor,
+ {vkh::ClearValue(0.2f, 0.2f, 0.2f, 1.0f)}),
+ VK_SUBPASS_CONTENTS_INLINE);
+
+ mainWindow->setViewScissor(cmd);
+
+ float sqSize = float(screenHeight) / ceilf(sqrtf((float)tests.size()));
+
+ float x = 0.0f, y = 0.0f;
+
+ for(size_t t = 0; t < tests.size(); t++)
+ {
+ VkViewport v = {x, y, sqSize, sqSize, 0.0f, 1.0f};
+ vkh::cmdPushConstants(cmd, layout, VK_SHADER_STAGE_FRAGMENT_BIT,
+ Vec4f(sqSize, (float)texSize, x, y));
+ vkCmdSetViewport(cmd, 0, 1, &v);
+ setMarker(cmd, "Test " + std::to_string(t));
+ vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, tests[t]);
+ vkCmdDraw(cmd, 4, 1, 0, 0);
+
+ x += sqSize;
+
+ if(x + sqSize >= (float)screenWidth)
+ {
+ x = 0.0f;
+ y += sqSize;
+ }
+ }
+
+ vkCmdEndRenderPass(cmd);
+
+ vkh::cmdPipelineBarrier(
+ cmd, {
+ vkh::ImageMemoryBarrier(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL,
+ VK_IMAGE_LAYOUT_GENERAL, colatt.image),
+ });
+
+ blitToSwap(cmd, colatt.image, VK_IMAGE_LAYOUT_GENERAL, swapimg, VK_IMAGE_LAYOUT_GENERAL);
+
+ FinishUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL);
+
+ vkEndCommandBuffer(cmd);
+
+ SubmitAndPresent({cmd});
+ }
+
+ vkDestroyAccelerationStructureKHR(device, j, NULL);
+ vkDestroyAccelerationStructureKHR(device, blas, NULL);
+ descbuf.unmap();
+ if(sampbuf.address)
+ sampbuf.unmap();
+
+ return 0;
+ }
+};
+
+REGISTER_TEST();
diff --git a/util/test/demos/vk/vk_test.cpp b/util/test/demos/vk/vk_test.cpp
index 22c482c1e..afe971001 100644
--- a/util/test/demos/vk/vk_test.cpp
+++ b/util/test/demos/vk/vk_test.cpp
@@ -1089,6 +1089,12 @@ void VulkanGraphicsTest::setName(VkFence obj, const std::string &name)
setName(VK_OBJECT_TYPE_FENCE, (uint64_t)obj, name);
}
+template <>
+void VulkanGraphicsTest::setName(VkAccelerationStructureKHR obj, const std::string &name)
+{
+ setName(VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR, (uint64_t)obj, name);
+}
+
void VulkanGraphicsTest::setName(VkObjectType objType, uint64_t obj, const std::string &name)
{
if(vkSetDebugUtilsObjectNameEXT)
diff --git a/util/test/rdtest/testcase.py b/util/test/rdtest/testcase.py
index 121db0593..b41de901a 100644
--- a/util/test/rdtest/testcase.py
+++ b/util/test/rdtest/testcase.py
@@ -861,7 +861,7 @@ class TestCase:
trace = self.controller.DebugPixel(x, y, rd.DebugPixelInputs())
if trace.debugger is None:
self.controller.FreeTrace(trace)
- raise TestFailureException(f"Pixel shader could not be debugged.")
+ raise TestFailureException(f"Pixel shader could not be debugged at {x},{y}.")
_, variables = self.process_trace(trace)
output = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0)
@@ -871,7 +871,7 @@ class TestCase:
try:
self.check_pixel_value(pipe.GetOutputTargets()[0].resource, x, y, debugged.value.f32v[0:4])
except TestFailureException as ex:
- raise TestFailureException(f"Pixel shader did not debug correctly. {ex}")
+ raise TestFailureException(f"Pixel shader did not debug correctly at {x},{y}. {ex}")
log.success(f"Pixel shader debugging at {x},{y} was successful")
diff --git a/util/test/tests/Vulkan/VK_Descriptor_Buffer.py b/util/test/tests/Vulkan/VK_Descriptor_Buffer.py
new file mode 100644
index 000000000..79066f2c9
--- /dev/null
+++ b/util/test/tests/Vulkan/VK_Descriptor_Buffer.py
@@ -0,0 +1,147 @@
+import renderdoc as rd
+import struct
+import rdtest
+
+
+class VK_Descriptor_Buffer(rdtest.TestCase):
+ demos_test_name = 'VK_Descriptor_Buffer'
+
+ def check_capture(self):
+ eid = 0
+
+ stage = rd.ShaderStage.Pixel
+
+ for test in range(1000):
+ draw = self.find_action(f"Test {test}", eid)
+
+ if draw is None:
+ break
+
+ rdtest.log.begin_section(f"Test {test}")
+
+ eid = draw.eventId
+
+ self.controller.SetFrameEvent(draw.next.eventId, False)
+
+ pipe = self.controller.GetPipelineState()
+
+ v = pipe.GetViewport(0)
+
+ x = int(v.x) + int(v.width / 2)
+ y = int(v.y) + int(v.height // 2)
+ offset = int(v.height // 5)
+
+ refl = pipe.GetShaderReflection(stage)
+
+ if refl.debugInfo.debuggable:
+ self.check_debug_pixel(x, y)
+ self.check_debug_pixel(x, y+offset)
+ self.check_debug_pixel(x, y-offset)
+ else:
+ rdtest.log.comment(f'Shader is not debuggable, not verifying')
+
+ cbs = pipe.GetConstantBlocks(stage, True)
+
+ out = pipe.GetOutputTargets()[0].resource
+
+ res_data = [
+ (pipe.GetReadOnlyResources(stage, True), refl.readOnlyResources),
+ (pipe.GetReadWriteResources(stage, True), refl.readWriteResources),
+ ]
+
+ for reslist,refllist in res_data:
+ for res in reslist:
+ expected_name = refllist[res.access.index].name
+
+ if refllist[res.access.index].bindArraySize > 1:
+ if (res.access.arrayElement % 10) == 1:
+ if res.descriptor.resource != rd.ResourceId():
+ raise rdtest.TestFailureException(
+ f"Expected no resource to be bound at {expected_name}[{res.access.arrayElement}")
+ continue
+
+ expected_name = f'{expected_name}_{res.access.arrayElement}'
+
+ if res.descriptor.type == rd.DescriptorType.ImageSampler:
+ sampname = self.get_resource(res.descriptor.secondary).name
+
+ resname = self.get_resource(res.descriptor.resource).name
+
+ expected_samp = f'{expected_name}_samp'
+ expected_name = f'{expected_name}_tex'
+
+ if sampname != expected_samp:
+ raise rdtest.TestFailureException(
+ f"Expected resource {resname} to be named {expected_name}")
+ else:
+ resname = self.get_resource(res.descriptor.resource).name
+
+ if resname != expected_name:
+ raise rdtest.TestFailureException(
+ f"Expected resource {resname} to be named {expected_name}")
+
+ # we don't care about sampler/texture contents, it is enough that we get the right name
+ rdtest.log.success(f"Resource {resname} bound as expected")
+
+ for cb in pipe.GetConstantBlocks(stage, True):
+ expected_name = refl.constantBlocks[cb.access.index].name
+
+ # ignore the push constants
+ if not refl.constantBlocks[cb.access.index].bufferBacked and expected_name == 'push':
+ continue
+
+ if refl.constantBlocks[cb.access.index].bindArraySize > 1:
+ if (cb.access.arrayElement % 10) == 1:
+ if cb.descriptor.resource != rd.ResourceId():
+ raise rdtest.TestFailureException(
+ f"Expected no resource to be bound at {expected_name}[{cb.access.arrayElement}")
+ continue
+
+ expected_name = f'{expected_name}_{cb.access.arrayElement}'
+
+ resname = self.get_resource(cb.descriptor.resource).name
+
+ if resname != expected_name:
+ raise rdtest.TestFailureException(
+ f"Expected resource {resname} to be named {expected_name}")
+
+ data = self.controller.GetBufferData(
+ cb.descriptor.resource, cb.descriptor.byteOffset, cb.descriptor.byteSize)
+
+ floats = struct.unpack_from("8f", data, 0)
+
+ picked = self.controller.PickPixel(
+ out, x, y, rd.Subresource(), rd.CompType.Float)
+
+ output_vec = picked.floatValue[0:4]
+ second_vec = floats[4:8]
+
+ if not rdtest.value_compare(second_vec, output_vec):
+ raise rdtest.TestFailureException(
+ f"Expected buffer data {output_vec}, but got {second_vec}")
+
+ data = self.controller.GetCBufferVariableContents(pipe.GetGraphicsPipelineObject(),
+ pipe.GetShader(
+ stage), stage,
+ pipe.GetShaderEntryPoint(
+ stage),
+ cb.access.index, cb.descriptor.resource,
+ cb.descriptor.byteOffset, cb.descriptor.byteSize)
+
+ if data[0].name != 'data':
+ raise rdtest.TestFailureException(
+ f"Expected 'data' member in cbuffers, got {data[0].name}")
+
+ if len(data[0].members) < 2:
+ raise rdtest.TestFailureException(
+ f"Expected 'data' member in cbuffers to have at least 2 entries, it has {len(data[0].members)}")
+
+ second_vec = data[0].members[1].value.f32v[0:4]
+
+ if not rdtest.value_compare(second_vec, output_vec):
+ raise rdtest.TestFailureException(
+ f"Expected constant data {output_vec}, but got {second_vec}")
+
+ rdtest.log.success(f"CBuffer {resname} bound as expected with correct data")
+
+ rdtest.log.end_section(f"Test {test}")