From 07b7975d09e8dfbdfc5a9942b4f0c9d054a5cd11 Mon Sep 17 00:00:00 2001 From: Giovanni Mascellani Date: Sat, 8 Feb 2025 21:46:58 +0100 Subject: [PATCH] vkd3d: Put all root descriptors in a single Vulkan descriptor set when using Vulkan heaps. Since 4a94bfc2f6c1ebfe4cac6c26ccf567339c71078f we segregate different D3D12 descriptor types in different Vulkan descriptor sets. This change was introduced to reduce descriptor wasting when allocating a new descriptor pool; that can be very useful when using virtual heaps, which have to often cycle through many descriptors, but it is expected to have limited impact for Vulkan heaps, given that in that case most descriptors are allocated through the descriptor heap rather than through the command allocator. Instead, it has a rather detrimental effect with Vulkan heaps, because it tends to use many more Vulkan descriptor sets than necessary, often with just a handful of descriptors each. This causes a regression on some Vulkan implementations that support too few descriptor sets. With this change we revert to a situation similar to before, stuffing all the descriptors that do not live in a root descriptor table in as few descriptor sets as possible (at most one or two, depending on whether push descriptors are used). --- libs/vkd3d/command.c | 46 +++++++++++++++++++++++++++-------- libs/vkd3d/state.c | 58 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 92 insertions(+), 12 deletions(-) diff --git a/libs/vkd3d/command.c b/libs/vkd3d/command.c index ecdf6dbf..1ff58f97 100644 --- a/libs/vkd3d/command.c +++ b/libs/vkd3d/command.c @@ -1499,7 +1499,7 @@ static VkDescriptorPool d3d12_command_allocator_allocate_descriptor_pool( const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; struct VkDescriptorPoolCreateInfo pool_desc; VkDevice vk_device = device->vk_device; - VkDescriptorPoolSize vk_pool_sizes[2]; + VkDescriptorPoolSize vk_pool_sizes[4]; unsigned int pool_size, pool_limit; VkDescriptorPool vk_pool; VkResult vr; @@ -1530,21 +1530,43 @@ static VkDescriptorPool d3d12_command_allocator_allocate_descriptor_pool( } descriptor_count = pool_size; - vk_pool_sizes[0].type = vk_descriptor_type_from_vkd3d_descriptor_type(descriptor_type, true); - vk_pool_sizes[0].descriptorCount = descriptor_count; - - vk_pool_sizes[1].type = vk_descriptor_type_from_vkd3d_descriptor_type(descriptor_type, false); - vk_pool_sizes[1].descriptorCount = descriptor_count; - pool_desc.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; pool_desc.pNext = NULL; pool_desc.flags = 0; pool_desc.maxSets = 512; - pool_desc.poolSizeCount = 1; - if (vk_pool_sizes[1].type != vk_pool_sizes[0].type) - ++pool_desc.poolSizeCount; pool_desc.pPoolSizes = vk_pool_sizes; + if (allocator->device->use_vk_heaps) + { + /* SRV root descriptors. */ + vk_pool_sizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + vk_pool_sizes[0].descriptorCount = descriptor_count; + + /* UAV root descriptors and UAV counters. */ + vk_pool_sizes[1].type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; + vk_pool_sizes[1].descriptorCount = descriptor_count; + + /* CBV root descriptors. */ + vk_pool_sizes[2].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + vk_pool_sizes[2].descriptorCount = descriptor_count; + + /* Static samplers. */ + vk_pool_sizes[3].type = VK_DESCRIPTOR_TYPE_SAMPLER; + vk_pool_sizes[3].descriptorCount = descriptor_count; + + pool_desc.poolSizeCount = 4; + } + else + { + vk_pool_sizes[0].type = vk_descriptor_type_from_vkd3d_descriptor_type(descriptor_type, true); + vk_pool_sizes[0].descriptorCount = descriptor_count; + + vk_pool_sizes[1].type = vk_descriptor_type_from_vkd3d_descriptor_type(descriptor_type, false); + vk_pool_sizes[1].descriptorCount = descriptor_count; + + pool_desc.poolSizeCount = 1 + (vk_pool_sizes[0].type != vk_pool_sizes[1].type); + } + if ((vr = VK_CALL(vkCreateDescriptorPool(vk_device, &pool_desc, NULL, &vk_pool))) < 0) { ERR("Failed to create descriptor pool, vr %d.\n", vr); @@ -1578,6 +1600,10 @@ static VkDescriptorSet d3d12_command_allocator_allocate_descriptor_set(struct d3 VkDescriptorSet vk_descriptor_set; VkResult vr; + /* With Vulkan heaps we use just one descriptor pool. */ + if (device->use_vk_heaps) + descriptor_type = 0; + if (!allocator->vk_descriptor_pools[descriptor_type]) allocator->vk_descriptor_pools[descriptor_type] = d3d12_command_allocator_allocate_descriptor_pool(allocator, descriptor_type, descriptor_count, unbounded); diff --git a/libs/vkd3d/state.c b/libs/vkd3d/state.c index 86541b5d..2246f49b 100644 --- a/libs/vkd3d/state.c +++ b/libs/vkd3d/state.c @@ -755,7 +755,10 @@ struct vkd3d_descriptor_set_context unsigned int push_constant_index; struct vk_binding_array *root_descriptor_set; + struct vk_binding_array *static_samplers_descriptor_set; bool push_descriptor; + bool static_samplers; + bool use_vk_heaps; }; static void descriptor_set_context_cleanup(struct vkd3d_descriptor_set_context *context) @@ -806,15 +809,61 @@ static struct vk_binding_array *d3d12_root_signature_vk_binding_array_for_type( { struct vk_binding_array *array, **current; + /* There are a few different ways we can reach this point: + * * If we are using virtual heaps we want to allocate descriptors to sets + * depending on their descriptor type, in order to minimize waste when + * recycling descriptor pools. + * + With the exception of root descriptors when we are using push + * descriptors: the push descriptors must be in a separate set, so we + * keep one specifically for them. + * * If we are using Vulkan heaps then all the root table descriptors don't + * even reach here, because they are managed by the D3D12 descriptor + * heap. Thus we only have to deal with root descriptors and static + * samplers. + * + If we're using push descriptors then again we have to dedicate a set + * for them, so static samplers will and up in their own set too. + * + If we're not using push descriptors then we can use the same set and + * save one. In this case we don't care too much about minimizing + * wasted descriptors, because few descriptors can end up here anyway. + */ + if (context->push_descriptor) { + /* The descriptor type is irrelevant here, it will never be used. */ if (!context->root_descriptor_set) context->root_descriptor_set = d3d12_root_signature_append_vk_binding_array(root_signature, - descriptor_type, VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR, context); + 0, VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR, context); return context->root_descriptor_set; } + if (context->use_vk_heaps) + { + if (context->static_samplers) + { + if (!context->static_samplers_descriptor_set) + { + if (!context->push_descriptor && context->root_descriptor_set) + context->static_samplers_descriptor_set = context->root_descriptor_set; + else + /* The descriptor type is irrelevant here, it will never be used. */ + context->static_samplers_descriptor_set = d3d12_root_signature_append_vk_binding_array( + root_signature, 0, 0, context); + } + + return context->static_samplers_descriptor_set; + } + else + { + /* The descriptor type is irrelevant here, it will never be used. */ + if (!context->root_descriptor_set) + context->root_descriptor_set = d3d12_root_signature_append_vk_binding_array( + root_signature, 0, 0, context); + + return context->root_descriptor_set; + } + } + current = context->current_binding_array; if (!(array = current[descriptor_type])) { @@ -1638,17 +1687,22 @@ static HRESULT d3d12_root_signature_init(struct d3d12_root_signature *root_signa sizeof(*root_signature->static_samplers)))) goto fail; + context.use_vk_heaps = use_vk_heaps; context.push_descriptor = vk_info->KHR_push_descriptor; if (FAILED(hr = d3d12_root_signature_init_root_descriptors(root_signature, desc, &context))) goto fail; - root_signature->main_set = !!context.root_descriptor_set; + root_signature->main_set = context.root_descriptor_set && context.push_descriptor; context.push_descriptor = false; if (FAILED(hr = d3d12_root_signature_init_push_constants(root_signature, desc, root_signature->push_constant_ranges, &root_signature->push_constant_range_count))) goto fail; + + context.static_samplers = true; if (FAILED(hr = d3d12_root_signature_init_static_samplers(root_signature, device, desc, &context))) goto fail; + context.static_samplers = false; + context.push_constant_index = 0; if (FAILED(hr = d3d12_root_signature_init_root_descriptor_tables(root_signature, desc, &info, &context))) goto fail;