Skip to content

Commit 8ffb18f

Browse files
Critsium-xyclaude
andcommitted
Vulkan: support runtime-sized descriptor arrays
The path tracer reaches its materials through a bindless texture array declared as `layout(set = 1, binding = 0) uniform texture2D bindless_textures[]`, a runtime-sized descriptor array. The reflection already carried an `unbounded` flag for such bindings all the way through the shader container, but the Vulkan driver ignored it, so the descriptor set layout reserved a single descriptor and every sample returned zero. Adds the missing driver support: - Query and enable the descriptor indexing features (shaderSampledImageArrayNonUniformIndexing, descriptorBindingPartiallyBound, descriptorBindingVariableDescriptorCount, runtimeDescriptorArray), following the per-feature struct style already used for buffer device address and the memory model rather than switching this driver to a unified VkPhysicalDeviceVulkan12Features. - Reserve a large maximum descriptor count for an unbounded binding in the set layout, and mark it partially bound and variable count. - Pin the real descriptor count with VkDescriptorSetVariableDescriptorCountAllocateInfo when the set is allocated. With this the path tracer renders. Measured on an RTX 4070 Laptop with a scene of a floor and eight boxes at 640x360, mean image intensity: the albedo debug view goes from 0.0000 to 0.3312, and the final path traced image from 0.0000 to 0.4860 against 0.5446 for the raster path. Ray traversal, BLAS/TLAS and the geometry attributes were already correct; only material evaluation was affected, because textures were the only thing reached through the bindless set. Verified at one and eight samples per pixel, and with the denoiser off. With a sky background the path traced and rasterized images cover the frame equally (99.9% versus 100% of pixels lit). A flat background colour still renders black under path tracing: the miss shader only handles sky, which is a gap in the ported renderer rather than in this change. DLSS is unaffected: frame to frame difference stays at 0.001788 on Vulkan and 0.001795 on D3D12, and reconstruction error against a native reference at 0.042, matching the values measured before the path tracer was ported. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WgxxgYZUnriDqgepZHiKpY
1 parent 32f35f3 commit 8ffb18f

2 files changed

Lines changed: 73 additions & 2 deletions

File tree

drivers/vulkan/rendering_device_driver_vulkan.cpp

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,11 @@ Error RenderingDeviceDriverVulkan::_check_device_capabilities() {
10411041
vulkan_memory_model_support = device_features_vk_1_2.vulkanMemoryModel;
10421042
vulkan_memory_model_device_scope_support = device_features_vk_1_2.vulkanMemoryModelDeviceScope;
10431043
}
1044+
// Descriptor indexing, required for the raytracing bindless texture array.
1045+
descriptor_indexing_capabilities.shader_sampled_image_array_non_uniform_indexing = device_features_vk_1_2.shaderSampledImageArrayNonUniformIndexing;
1046+
descriptor_indexing_capabilities.descriptor_binding_partially_bound = device_features_vk_1_2.descriptorBindingPartiallyBound;
1047+
descriptor_indexing_capabilities.descriptor_binding_variable_descriptor_count = device_features_vk_1_2.descriptorBindingVariableDescriptorCount;
1048+
descriptor_indexing_capabilities.runtime_descriptor_array = device_features_vk_1_2.runtimeDescriptorArray;
10441049
} else {
10451050
if (enabled_device_extension_names.has(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)) {
10461051
shader_capabilities.shader_float16_is_supported = shader_features.shaderFloat16;
@@ -1371,6 +1376,18 @@ Error RenderingDeviceDriverVulkan::_initialize_device(const LocalVector<VkDevice
13711376
shader_features.shaderInt8 = shader_capabilities.shader_int8_is_supported;
13721377
create_info_next = &shader_features;
13731378

1379+
// Runtime-sized descriptor arrays, used by the raytracing bindless texture set.
1380+
VkPhysicalDeviceDescriptorIndexingFeatures descriptor_indexing_features = {};
1381+
if (descriptor_indexing_capabilities.runtime_descriptor_array) {
1382+
descriptor_indexing_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES;
1383+
descriptor_indexing_features.pNext = create_info_next;
1384+
descriptor_indexing_features.shaderSampledImageArrayNonUniformIndexing = descriptor_indexing_capabilities.shader_sampled_image_array_non_uniform_indexing;
1385+
descriptor_indexing_features.descriptorBindingPartiallyBound = descriptor_indexing_capabilities.descriptor_binding_partially_bound;
1386+
descriptor_indexing_features.descriptorBindingVariableDescriptorCount = descriptor_indexing_capabilities.descriptor_binding_variable_descriptor_count;
1387+
descriptor_indexing_features.runtimeDescriptorArray = descriptor_indexing_capabilities.runtime_descriptor_array;
1388+
create_info_next = &descriptor_indexing_features;
1389+
}
1390+
13741391
VkPhysicalDeviceBufferDeviceAddressFeaturesKHR buffer_device_address_features = {};
13751392
if (buffer_device_address_support) {
13761393
buffer_device_address_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_KHR;
@@ -4328,6 +4345,10 @@ static VkShaderStageFlagBits RD_STAGE_TO_VK_SHADER_STAGE_BITS[RDD::SHADER_STAGE_
43284345
VK_SHADER_STAGE_INTERSECTION_BIT_KHR,
43294346
};
43304347

4348+
// A runtime-sized descriptor array declares no size in the shader, so the set layout
4349+
// reserves this many slots and the real count is pinned when the set is allocated.
4350+
static constexpr uint32_t VK_UNBOUNDED_DESCRIPTOR_COUNT = 128000;
4351+
43314352
RDD::ShaderID RenderingDeviceDriverVulkan::shader_create_from_container(const Ref<RenderingShaderContainer> &p_shader_container, const Vector<ImmutableSampler> &p_immutable_samplers) {
43324353
ShaderReflection shader_refl = p_shader_container->get_shader_reflection();
43334354
ShaderInfo shader_info;
@@ -4374,11 +4395,11 @@ RDD::ShaderID RenderingDeviceDriverVulkan::shader_create_from_container(const Re
43744395
} break;
43754396
case UNIFORM_TYPE_SAMPLER_WITH_TEXTURE: {
43764397
layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
4377-
layout_binding.descriptorCount = uniform.length;
4398+
layout_binding.descriptorCount = uniform.unbounded ? VK_UNBOUNDED_DESCRIPTOR_COUNT : uniform.length;
43784399
} break;
43794400
case UNIFORM_TYPE_TEXTURE: {
43804401
layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
4381-
layout_binding.descriptorCount = uniform.length;
4402+
layout_binding.descriptorCount = uniform.unbounded ? VK_UNBOUNDED_DESCRIPTOR_COUNT : uniform.length;
43824403
} break;
43834404
case UNIFORM_TYPE_IMAGE: {
43844405
layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
@@ -4531,11 +4552,37 @@ RDD::ShaderID RenderingDeviceDriverVulkan::shader_create_from_container(const Re
45314552
placeholder_binding.stageFlags = VK_SHADER_STAGE_ALL;
45324553

45334554
for (uint32_t i = 0; i < shader_refl.uniform_sets.size(); i++) {
4555+
bool has_unbounded = false;
4556+
for (uint32_t j = 0; j < shader_refl.uniform_sets[i].size(); j++) {
4557+
if (shader_refl.uniform_sets[i][j].unbounded) {
4558+
has_unbounded = true;
4559+
break;
4560+
}
4561+
}
4562+
4563+
const bool use_binding_flags = has_unbounded && descriptor_indexing_capabilities.descriptor_binding_partially_bound;
4564+
LocalVector<VkDescriptorBindingFlags> binding_flags;
4565+
VkDescriptorSetLayoutBindingFlagsCreateInfo binding_flags_info = {};
4566+
if (use_binding_flags) {
4567+
binding_flags.resize(vk_set_bindings[i].size());
4568+
for (uint32_t j = 0; j < binding_flags.size(); j++) {
4569+
binding_flags[j] = (j < shader_refl.uniform_sets[i].size() && shader_refl.uniform_sets[i][j].unbounded)
4570+
? (VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT)
4571+
: 0;
4572+
}
4573+
binding_flags_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO;
4574+
binding_flags_info.bindingCount = binding_flags.size();
4575+
binding_flags_info.pBindingFlags = binding_flags.ptr();
4576+
}
4577+
45344578
// Empty ones are fine if they were not used according to spec (binding count will be 0).
45354579
VkDescriptorSetLayoutCreateInfo layout_create_info = {};
45364580
layout_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
45374581
layout_create_info.bindingCount = vk_set_bindings[i].size();
45384582
layout_create_info.pBindings = vk_set_bindings[i].ptr();
4583+
if (use_binding_flags) {
4584+
layout_create_info.pNext = &binding_flags_info;
4585+
}
45394586

45404587
// ...not so fine on Adreno 5XX.
45414588
if (adreno_5xx_empty_descriptor_set_layout_workaround && layout_create_info.bindingCount == 0) {
@@ -4767,6 +4814,7 @@ RDD::UniformSetID RenderingDeviceDriverVulkan::uniform_set_create(VectorView<Bou
47674814
// Immutable samplers will be skipped so we need to track the number of vk_writes used.
47684815
VkWriteDescriptorSet *vk_writes = ALLOCA_ARRAY(VkWriteDescriptorSet, p_uniforms.size());
47694816
uint32_t writes_amount = 0;
4817+
uint32_t variable_descriptor_count = 0;
47704818
for (uint32_t i = 0; i < p_uniforms.size(); i++) {
47714819
const BoundUniform &uniform = p_uniforms[i];
47724820

@@ -4985,6 +5033,10 @@ RDD::UniformSetID RenderingDeviceDriverVulkan::uniform_set_create(VectorView<Bou
49855033
if (add_write) {
49865034
vk_writes[writes_amount].dstBinding = uniform.binding;
49875035
vk_writes[writes_amount].descriptorCount = num_descriptors;
5036+
if (uniform.variable_count) {
5037+
ERR_FAIL_COND_V_MSG(variable_descriptor_count > 0, UniformSetID(), "Trying to create a uniform set with multiple variable bindings. Only the last binding can be variable.");
5038+
variable_descriptor_count = num_descriptors;
5039+
}
49885040
writes_amount++;
49895041
}
49905042

@@ -5008,6 +5060,16 @@ RDD::UniformSetID RenderingDeviceDriverVulkan::uniform_set_create(VectorView<Bou
50085060
const ShaderInfo *shader_info = (const ShaderInfo *)p_shader.id;
50095061
descriptor_set_allocate_info.pSetLayouts = &shader_info->vk_descriptor_set_layouts[p_set_index];
50105062

5063+
// A runtime-sized binding reserves a large maximum in the layout; tell Vulkan how
5064+
// many descriptors this particular set actually holds.
5065+
VkDescriptorSetVariableDescriptorCountAllocateInfo variable_count_info = {};
5066+
if (variable_descriptor_count > 0) {
5067+
variable_count_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO;
5068+
variable_count_info.descriptorSetCount = 1;
5069+
variable_count_info.pDescriptorCounts = &variable_descriptor_count;
5070+
descriptor_set_allocate_info.pNext = &variable_count_info;
5071+
}
5072+
50115073
VkDescriptorSet vk_descriptor_set = VK_NULL_HANDLE;
50125074
for (KeyValue<VkDescriptorPool, uint32_t> &E : pool_sets_it->value) {
50135075
if (E.value < max_descriptor_sets_per_pool) {

drivers/vulkan/rendering_device_driver_vulkan.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,14 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver {
109109
bool validation = false;
110110
};
111111

112+
// Runtime-sized descriptor arrays, needed by the raytracing bindless texture set.
113+
struct DescriptorIndexingCapabilities {
114+
bool shader_sampled_image_array_non_uniform_indexing = false;
115+
bool descriptor_binding_partially_bound = false;
116+
bool descriptor_binding_variable_descriptor_count = false;
117+
bool runtime_descriptor_array = false;
118+
};
119+
112120
struct DeviceFunctions {
113121
PFN_vkCreateSwapchainKHR CreateSwapchainKHR = nullptr;
114122
PFN_vkDestroySwapchainKHR DestroySwapchainKHR = nullptr;
@@ -165,6 +173,7 @@ class RenderingDeviceDriverVulkan : public RenderingDeviceDriver {
165173
AccelerationStructureCapabilities acceleration_structure_capabilities;
166174
bool ray_query_support = false;
167175
RaytracingCapabilities raytracing_capabilities;
176+
DescriptorIndexingCapabilities descriptor_indexing_capabilities;
168177
bool pipeline_cache_control_support = false;
169178
bool device_fault_support = false;
170179
bool framebuffer_depth_resolve = false;

0 commit comments

Comments
 (0)