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}")