[PATCH vkd3d v2 3/9] vkd3d: Create and write descriptor sets for root signature unbounded ranges.

Conor McCarthy cmccarthy at codeweavers.com
Mon Aug 23 10:23:22 CDT 2021


Signed-off-by: Conor McCarthy <cmccarthy at codeweavers.com>
---
 libs/vkd3d/command.c       | 118 ++++++++++++++++++++-----
 libs/vkd3d/device.c        |  99 +++++++++++++++++++++
 libs/vkd3d/resource.c      |  18 ++++
 libs/vkd3d/state.c         | 175 ++++++++++++++++++++++++++++++-------
 libs/vkd3d/vkd3d_private.h |  32 ++++++-
 tests/d3d12.c              |   4 +-
 6 files changed, 389 insertions(+), 57 deletions(-)

diff --git a/libs/vkd3d/command.c b/libs/vkd3d/command.c
index 964f5cd4..07fdecb9 100644
--- a/libs/vkd3d/command.c
+++ b/libs/vkd3d/command.c
@@ -1,6 +1,7 @@
 /*
  * Copyright 2016 Józef Kucia for CodeWeavers
  * Copyright 2016 Henri Verbeet for CodeWeavers
+ * Copyright 2021 Conor McCarthy for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -1363,10 +1364,12 @@ static VkDescriptorPool d3d12_command_allocator_allocate_descriptor_pool(
 }
 
 static VkDescriptorSet d3d12_command_allocator_allocate_descriptor_set(
-        struct d3d12_command_allocator *allocator, VkDescriptorSetLayout vk_set_layout)
+        struct d3d12_command_allocator *allocator, VkDescriptorSetLayout vk_set_layout,
+        uint32_t variable_binding_size, bool unbounded)
 {
     struct d3d12_device *device = allocator->device;
     const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+    VkDescriptorSetVariableDescriptorCountAllocateInfoEXT set_size;
     struct VkDescriptorSetAllocateInfo set_desc;
     VkDevice vk_device = device->vk_device;
     VkDescriptorSet vk_descriptor_set;
@@ -1382,6 +1385,14 @@ static VkDescriptorSet d3d12_command_allocator_allocate_descriptor_set(
     set_desc.descriptorPool = allocator->vk_descriptor_pool;
     set_desc.descriptorSetCount = 1;
     set_desc.pSetLayouts = &vk_set_layout;
+    if (unbounded)
+    {
+        set_desc.pNext = &set_size;
+        set_size.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT;
+        set_size.pNext = NULL;
+        set_size.descriptorSetCount = 1;
+        set_size.pDescriptorCounts = &variable_binding_size;
+    }
     if ((vr = VK_CALL(vkAllocateDescriptorSets(vk_device, &set_desc, &vk_descriptor_set))) >= 0)
         return vk_descriptor_set;
 
@@ -2565,6 +2576,8 @@ static void d3d12_command_list_prepare_descriptors(struct d3d12_command_list *li
 {
     struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[bind_point];
     const struct d3d12_root_signature *root_signature = bindings->root_signature;
+    struct d3d12_device *device = list->device;
+    unsigned int i;
 
     if (bindings->descriptor_set_count && !bindings->in_use)
         return;
@@ -2581,9 +2594,36 @@ static void d3d12_command_list_prepare_descriptors(struct d3d12_command_list *li
      *   by an update command, or freed) between when the command is recorded
      *   and when the command completes executing on the queue."
      */
-    bindings->descriptor_sets[0] = d3d12_command_allocator_allocate_descriptor_set(list->allocator,
-            root_signature->vk_set_layouts[root_signature->main_set]);
-    bindings->descriptor_set_count = 1;
+    bindings->descriptor_set_count = 0;
+    for (i = root_signature->main_set; i < root_signature->vk_set_layouts.count; ++i)
+    {
+        unsigned int unbounded_range_offset = root_signature->vk_set_layouts.unbounded_offsets[i];
+        unsigned int unbounded_table = root_signature->vk_set_layouts.table_indices[i];
+        unsigned int variable_binding_size = 0;
+
+        if (unbounded_range_offset != UINT_MAX)
+        {
+            const struct d3d12_desc *base_descriptor
+                    = d3d12_desc_from_gpu_handle(bindings->descriptor_tables[unbounded_table]);
+            /* Descriptors may not be set, eg. WoW. */
+            if (base_descriptor)
+            {
+                unsigned int heap_size = d3d12_device_descriptor_heap_size_from_descriptor(device, base_descriptor);
+
+                if (heap_size >= unbounded_range_offset)
+                    variable_binding_size = heap_size - unbounded_range_offset;
+                else
+                    WARN("Descriptor heap size %u is less than the offset %u of an unbounded range in table %u, "
+                            "vk set %u.\n", heap_size, unbounded_range_offset, unbounded_table, i);
+            }
+        }
+
+        bindings->descriptor_sets[bindings->descriptor_set_count] =
+            d3d12_command_allocator_allocate_descriptor_set(list->allocator, root_signature->vk_set_layouts.layouts[i],
+                    variable_binding_size, unbounded_range_offset != UINT_MAX);
+        ++bindings->descriptor_set_count;
+    }
+
     bindings->in_use = false;
 
     bindings->descriptor_table_dirty_mask |= bindings->descriptor_table_active_mask & root_signature->descriptor_table_mask;
@@ -2598,13 +2638,14 @@ static bool vk_write_descriptor_set_from_d3d12_desc(VkWriteDescriptorSet *vk_des
     uint32_t descriptor_range_magic = range->descriptor_magic;
     const struct vkd3d_view *view = descriptor->u.view;
     uint32_t vk_binding = range->binding;
+    uint32_t set = range->set;
 
     if (descriptor->magic != descriptor_range_magic)
         return false;
 
     vk_descriptor_write->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
     vk_descriptor_write->pNext = NULL;
-    vk_descriptor_write->dstSet = vk_descriptor_sets[0];
+    vk_descriptor_write->dstSet = vk_descriptor_sets[set];
     vk_descriptor_write->dstBinding = use_array ? vk_binding : vk_binding + index;
     vk_descriptor_write->dstArrayElement = use_array ? index : 0;
     vk_descriptor_write->descriptorCount = 1;
@@ -2622,12 +2663,25 @@ static bool vk_write_descriptor_set_from_d3d12_desc(VkWriteDescriptorSet *vk_des
         case VKD3D_DESCRIPTOR_MAGIC_SRV:
         case VKD3D_DESCRIPTOR_MAGIC_UAV:
             /* We use separate bindings for buffer and texture SRVs/UAVs.
-             * See d3d12_root_signature_init(). */
-            if (!use_array)
-                vk_descriptor_write->dstBinding = vk_binding + 2 * index;
-            if (descriptor->vk_descriptor_type != VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER
-                    && descriptor->vk_descriptor_type != VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER)
-                ++vk_descriptor_write->dstBinding;
+             * See d3d12_root_signature_init(). For unbounded ranges the descriptors exist
+             * in two consecutive sets, otherwise they occur in pairs in one set. */
+            if (range->descriptor_count == UINT_MAX)
+            {
+                if (descriptor->vk_descriptor_type != VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER
+                        && descriptor->vk_descriptor_type != VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER)
+                {
+                    vk_descriptor_write->dstSet = vk_descriptor_sets[set + 1];
+                    vk_descriptor_write->dstBinding = 0;
+                }
+            }
+            else
+            {
+                if (!use_array)
+                    vk_descriptor_write->dstBinding = vk_binding + 2 * index;
+                if (descriptor->vk_descriptor_type != VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER
+                        && descriptor->vk_descriptor_type != VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER)
+                    ++vk_descriptor_write->dstBinding;
+            }
 
             if (descriptor->vk_descriptor_type == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER
                     || descriptor->vk_descriptor_type == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER)
@@ -2675,10 +2729,10 @@ static void d3d12_command_list_update_descriptor_table(struct d3d12_command_list
     VkDevice vk_device = list->device->vk_device;
     unsigned int i, j, k, descriptor_count;
     struct d3d12_desc *descriptor;
+    unsigned int write_count = 0;
 
     descriptor_table = root_signature_get_descriptor_table(root_signature, index);
 
-    descriptor_count = 0;
     current_descriptor_write = descriptor_writes;
     current_image_info = image_infos;
     for (i = 0; i < descriptor_table->range_count; ++i)
@@ -2687,7 +2741,25 @@ static void d3d12_command_list_update_descriptor_table(struct d3d12_command_list
 
         descriptor = base_descriptor + range->offset;
 
-        for (j = 0; j < range->descriptor_count; ++j, ++descriptor)
+        descriptor_count = range->descriptor_count;
+        if (descriptor_count == UINT_MAX)
+        {
+            /* The first unbounded range of each type is written until the heap end is reached. Do not repeat. */
+            if (i && descriptor_table->ranges[i - 1].descriptor_magic == range->descriptor_magic
+                    && descriptor_table->ranges[i - 1].descriptor_count == UINT_MAX)
+                continue;
+
+            descriptor_count = d3d12_device_descriptor_heap_size_from_descriptor(list->device, descriptor);
+
+            if (descriptor_count > range->vk_binding_count)
+            {
+                ERR("Heap descriptor count %u exceeds maximum Vulkan count %u. Reducing to the Vulkan maximum.\n",
+                        descriptor_count, range->vk_binding_count);
+                descriptor_count = range->vk_binding_count;
+            }
+        }
+
+        for (j = 0; j < descriptor_count; ++j, ++descriptor)
         {
             unsigned int register_idx = range->base_register_idx + j;
 
@@ -2709,25 +2781,29 @@ static void d3d12_command_list_update_descriptor_table(struct d3d12_command_list
                 }
             }
 
+            /* Not all descriptors are necessarily populated if the range is unbounded. */
+            if (descriptor->magic == VKD3D_DESCRIPTOR_MAGIC_FREE)
+                continue;
+
             if (!vk_write_descriptor_set_from_d3d12_desc(current_descriptor_write, current_image_info,
                     descriptor, range, bindings->descriptor_sets, j, root_signature->use_descriptor_arrays))
                 continue;
 
-            ++descriptor_count;
+            ++write_count;
             ++current_descriptor_write;
             ++current_image_info;
 
-            if (descriptor_count == ARRAY_SIZE(descriptor_writes))
+            if (write_count == ARRAY_SIZE(descriptor_writes))
             {
-                VK_CALL(vkUpdateDescriptorSets(vk_device, descriptor_count, descriptor_writes, 0, NULL));
-                descriptor_count = 0;
+                VK_CALL(vkUpdateDescriptorSets(vk_device, write_count, descriptor_writes, 0, NULL));
+                write_count = 0;
                 current_descriptor_write = descriptor_writes;
                 current_image_info = image_infos;
             }
         }
     }
 
-    VK_CALL(vkUpdateDescriptorSets(vk_device, descriptor_count, descriptor_writes, 0, NULL));
+    VK_CALL(vkUpdateDescriptorSets(vk_device, write_count, descriptor_writes, 0, NULL));
 }
 
 static bool vk_write_descriptor_set_from_root_descriptor(VkWriteDescriptorSet *vk_descriptor_write,
@@ -2853,7 +2929,7 @@ static void d3d12_command_list_update_uav_counter_descriptors(struct d3d12_comma
     if (!(vk_descriptor_writes = vkd3d_calloc(uav_counter_count, sizeof(*vk_descriptor_writes))))
         return;
     if (!(vk_descriptor_set = d3d12_command_allocator_allocate_descriptor_set(list->allocator,
-            state->uav_counters.vk_set_layout)))
+            state->uav_counters.vk_set_layout, 0, false)))
         goto done;
 
     for (i = 0; i < uav_counter_count; ++i)
@@ -2895,7 +2971,7 @@ static void d3d12_command_list_update_descriptors(struct d3d12_command_list *lis
     struct d3d12_desc *base_descriptor;
     unsigned int i;
 
-    if (!rs || !rs->vk_set_count)
+    if (!rs || !rs->vk_set_layouts.count)
         return;
 
     if (bindings->descriptor_table_dirty_mask || bindings->push_descriptor_dirty_mask)
@@ -4954,7 +5030,7 @@ static void d3d12_command_list_clear_uav(struct d3d12_command_list *list,
     }
 
     if (!(write_set.dstSet = d3d12_command_allocator_allocate_descriptor_set(
-            list->allocator, pipeline.vk_set_layout)))
+            list->allocator, pipeline.vk_set_layout, 0, false)))
     {
         ERR("Failed to allocate descriptor set.\n");
         return;
diff --git a/libs/vkd3d/device.c b/libs/vkd3d/device.c
index 0fadb521..8bad8ab3 100644
--- a/libs/vkd3d/device.c
+++ b/libs/vkd3d/device.c
@@ -2173,6 +2173,19 @@ static void vkd3d_gpu_va_allocator_cleanup(struct vkd3d_gpu_va_allocator *alloca
     pthread_mutex_destroy(&allocator->mutex);
 }
 
+static void vkd3d_descriptor_heap_array_init(struct vkd3d_descriptor_heap_array *descriptor_heaps)
+{
+    descriptor_heaps->heaps = NULL;
+    descriptor_heaps->capacity = 0;
+    descriptor_heaps->count = 0;
+}
+
+static void vkd3d_descriptor_heap_array_cleanup(struct vkd3d_descriptor_heap_array *descriptor_heaps)
+{
+    vkd3d_free(descriptor_heaps->heaps);
+    descriptor_heaps->heaps = NULL;
+}
+
 /* ID3D12Device */
 static inline struct d3d12_device *impl_from_ID3D12Device(ID3D12Device *iface)
 {
@@ -2231,6 +2244,7 @@ static ULONG STDMETHODCALLTYPE d3d12_device_Release(ID3D12Device *iface)
         vkd3d_fence_worker_stop(&device->fence_worker, device);
         d3d12_device_destroy_pipeline_cache(device);
         d3d12_device_destroy_vkd3d_queues(device);
+        vkd3d_descriptor_heap_array_cleanup(&device->descriptor_heaps);
         for (i = 0; i < ARRAY_SIZE(device->desc_mutex); ++i)
             pthread_mutex_destroy(&device->desc_mutex[i]);
         VK_CALL(vkDestroyDevice(device->vk_device, NULL));
@@ -3737,6 +3751,8 @@ static HRESULT d3d12_device_init(struct d3d12_device *device,
     if ((device->parent = create_info->parent))
         IUnknown_AddRef(device->parent);
 
+    vkd3d_descriptor_heap_array_init(&device->descriptor_heaps);
+
     return S_OK;
 
 out_destroy_null_resources:
@@ -3792,6 +3808,89 @@ void d3d12_device_mark_as_removed(struct d3d12_device *device, HRESULT reason,
     device->removed_reason = reason;
 }
 
+void d3d12_device_track_descriptor_heap(struct d3d12_device *device,
+        const struct d3d12_descriptor_heap *heap)
+{
+    struct vkd3d_descriptor_heap_array *descriptor_heaps;
+
+    if (!device->vk_info.EXT_descriptor_indexing)
+        return;
+
+    descriptor_heaps = &device->descriptor_heaps;
+
+    if (!vkd3d_array_reserve((void **)&descriptor_heaps->heaps, &descriptor_heaps->capacity,
+            descriptor_heaps->count + 1, sizeof(*descriptor_heaps->heaps)))
+    {
+        ERR("Out of memory. Cannot track descriptor heap for unbounded arrays.\n");
+        return;
+    }
+
+    descriptor_heaps->heaps[descriptor_heaps->count++] = heap;
+    /* Do not increment the heap reference count. This reference is deleted on heap destruction. */
+}
+
+void d3d12_device_untrack_descriptor_heap(struct d3d12_device *device, const struct d3d12_descriptor_heap *heap)
+{
+    struct vkd3d_descriptor_heap_array *descriptor_heaps;
+    size_t i;
+
+    if (!device->vk_info.EXT_descriptor_indexing)
+        return;
+
+    descriptor_heaps = &device->descriptor_heaps;
+
+    for (i = 0; i < descriptor_heaps->count; ++i)
+    {
+        if (descriptor_heaps->heaps[i] != heap)
+            continue;
+
+        descriptor_heaps->heaps[i] = descriptor_heaps->heaps[descriptor_heaps->count - 1];
+        --descriptor_heaps->count;
+
+        return;
+    }
+
+    ERR("Attempted to untrack an already untracked heap.\n");
+}
+
+/* Return the available size from the specified descriptor to the heap end. */
+uint32_t d3d12_device_descriptor_heap_size_from_descriptor(struct d3d12_device *device,
+        const struct d3d12_desc *desc)
+{
+    int rc = pthread_mutex_lock(&device->mutex);
+    size_t i;
+
+    if (rc)
+        ERR("Failed to lock mutex, error %d.\n", rc);
+
+    for (i = 0; i < device->descriptor_heaps.count; ++i)
+    {
+        const struct d3d12_descriptor_heap *heap = device->descriptor_heaps.heaps[i];
+        size_t offset;
+
+        assert(heap->desc.Type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV
+                || heap->desc.Type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
+
+        if ((struct d3d12_desc *)heap->descriptors > desc)
+            continue;
+
+        offset = desc - (struct d3d12_desc *)heap->descriptors;
+        if (heap->desc.NumDescriptors <= offset)
+            continue;
+
+        if (!rc)
+            pthread_mutex_unlock(&device->mutex);
+
+        return heap->desc.NumDescriptors - (uint32_t)offset;
+    }
+
+    if (!rc)
+        pthread_mutex_unlock(&device->mutex);
+
+    ERR("Failed to find descriptor heap size from descriptor pointer.\n");
+    return 0;
+}
+
 HRESULT vkd3d_create_thread(struct vkd3d_instance *instance,
         PFN_vkd3d_thread thread_main, void *data, union vkd3d_thread_handle *thread)
 {
diff --git a/libs/vkd3d/resource.c b/libs/vkd3d/resource.c
index 1ca23a90..0fab258d 100644
--- a/libs/vkd3d/resource.c
+++ b/libs/vkd3d/resource.c
@@ -3346,11 +3346,19 @@ static ULONG STDMETHODCALLTYPE d3d12_descriptor_heap_Release(ID3D12DescriptorHea
             case D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER:
             {
                 struct d3d12_desc *descriptors = (struct d3d12_desc *)heap->descriptors;
+                int rc;
 
                 for (i = 0; i < heap->desc.NumDescriptors; ++i)
                 {
                     d3d12_desc_destroy(&descriptors[i], device);
                 }
+
+                if ((rc = pthread_mutex_lock(&device->mutex)))
+                    ERR("Failed to lock mutex, error %d.\n", rc);
+                d3d12_device_untrack_descriptor_heap(device, heap);
+                if (!rc)
+                    pthread_mutex_unlock(&device->mutex);
+
                 break;
             }
 
@@ -3547,6 +3555,16 @@ HRESULT d3d12_descriptor_heap_create(struct d3d12_device *device,
 
     memset(object->descriptors, 0, descriptor_size * desc->NumDescriptors);
 
+    if (desc->Type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV || desc->Type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER)
+    {
+        int rc = pthread_mutex_lock(&device->mutex);
+        if (rc)
+            ERR("Failed to lock mutex, error %d.\n", rc);
+        d3d12_device_track_descriptor_heap(device, object);
+        if (!rc)
+            pthread_mutex_unlock(&device->mutex);
+    }
+
     TRACE("Created descriptor heap %p.\n", object);
 
     *descriptor_heap = object;
diff --git a/libs/vkd3d/state.c b/libs/vkd3d/state.c
index 58463e59..4cb2c387 100644
--- a/libs/vkd3d/state.c
+++ b/libs/vkd3d/state.c
@@ -1,6 +1,7 @@
 /*
  * Copyright 2016 Józef Kucia for CodeWeavers
  * Copyright 2016 Henri Verbeet for CodeWeavers
+ * Copyright 2021 Conor McCarthy for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -57,6 +58,15 @@ static ULONG STDMETHODCALLTYPE d3d12_root_signature_AddRef(ID3D12RootSignature *
     return refcount;
 }
 
+static void vk_set_layouts_cleanup(struct vk_set_layouts_state *vk_set_layouts, struct d3d12_device *device)
+{
+    const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
+    unsigned int i;
+
+    for (i = 0; i < vk_set_layouts->count; ++i)
+        VK_CALL(vkDestroyDescriptorSetLayout(device->vk_device, vk_set_layouts->layouts[i], NULL));
+}
+
 static void d3d12_root_signature_cleanup(struct d3d12_root_signature *root_signature,
         struct d3d12_device *device)
 {
@@ -65,8 +75,7 @@ static void d3d12_root_signature_cleanup(struct d3d12_root_signature *root_signa
 
     if (root_signature->vk_pipeline_layout)
         VK_CALL(vkDestroyPipelineLayout(device->vk_device, root_signature->vk_pipeline_layout, NULL));
-    for (i = 0; i < root_signature->vk_set_count; ++i)
-        VK_CALL(vkDestroyDescriptorSetLayout(device->vk_device, root_signature->vk_set_layouts[i], NULL));
+    vk_set_layouts_cleanup(&root_signature->vk_set_layouts, device);
 
     if (root_signature->parameters)
     {
@@ -332,12 +341,6 @@ static HRESULT d3d12_root_signature_info_count_descriptors(struct d3d12_root_sig
         const D3D12_DESCRIPTOR_RANGE *range = &table->pDescriptorRanges[i];
         unsigned int binding_count;
 
-        if (range->NumDescriptors == 0xffffffff)
-        {
-            FIXME("Unhandled unbound descriptor range.\n");
-            return E_NOTIMPL;
-        }
-
         binding_count = use_array ? 1 : range->NumDescriptors;
 
         switch (range->RangeType)
@@ -515,6 +518,8 @@ struct vkd3d_descriptor_set_context
 {
     VkDescriptorSetLayoutBinding *current_binding;
     VkDescriptorSetLayoutBinding *first_binding;
+    unsigned int table_index;
+    unsigned int unbounded_offset;
     unsigned int descriptor_index;
     uint32_t descriptor_binding;
 };
@@ -533,25 +538,32 @@ static bool vkd3d_validate_descriptor_set_count(struct d3d12_device *device, uns
 }
 
 static HRESULT vkd3d_create_descriptor_set_layout(struct d3d12_device *device,
-        VkDescriptorSetLayoutCreateFlags flags, unsigned int binding_count,
+        VkDescriptorSetLayoutCreateFlags flags, unsigned int binding_count, bool unbounded,
         const VkDescriptorSetLayoutBinding *bindings, VkDescriptorSetLayout *set_layout);
 
 static HRESULT d3d12_root_signature_append_descriptor_set_layout(struct d3d12_root_signature *root_signature,
     struct vkd3d_descriptor_set_context *context, VkDescriptorSetLayoutCreateFlags flags)
 {
+    struct vk_set_layouts_state *vk_set_layouts;
+    unsigned int index;
     HRESULT hr;
 
     if (!context->descriptor_binding)
         return S_OK;
 
-    if (!vkd3d_validate_descriptor_set_count(root_signature->device, root_signature->vk_set_count + 1))
+    vk_set_layouts = &root_signature->vk_set_layouts;
+    index = vk_set_layouts->count;
+
+    if (!vkd3d_validate_descriptor_set_count(root_signature->device, index + 1))
         return E_INVALIDARG;
 
     if (FAILED(hr = vkd3d_create_descriptor_set_layout(root_signature->device,
-            flags, context->descriptor_binding, context->first_binding,
-            &root_signature->vk_set_layouts[root_signature->vk_set_count])))
+            flags, context->descriptor_binding, context->unbounded_offset != UINT_MAX, context->first_binding,
+            &vk_set_layouts->layouts[index])))
         return hr;
-    ++root_signature->vk_set_count;
+    vk_set_layouts->table_indices[index] = context->table_index;
+    vk_set_layouts->unbounded_offsets[index] = context->unbounded_offset;
+    ++vk_set_layouts->count;
 
     context->current_binding = context->first_binding;
     context->descriptor_binding = 0;
@@ -572,9 +584,12 @@ static void d3d12_root_signature_append_vk_binding(struct d3d12_root_signature *
     mapping->register_index = register_idx;
     mapping->shader_visibility = shader_visibility;
     mapping->flags = buffer_descriptor ? VKD3D_SHADER_BINDING_FLAG_BUFFER : VKD3D_SHADER_BINDING_FLAG_IMAGE;
-    mapping->binding.set = root_signature->vk_set_count;
+    mapping->binding.set = root_signature->vk_set_layouts.count;
     mapping->binding.binding = context->descriptor_binding++;
     mapping->binding.count = descriptor_count;
+
+    if (context->unbounded_offset != UINT_MAX)
+        d3d12_root_signature_append_descriptor_set_layout(root_signature, context, 0);
 }
 
 static uint32_t d3d12_root_signature_assign_vk_bindings(struct d3d12_root_signature *root_signature,
@@ -621,6 +636,15 @@ static uint32_t vkd3d_descriptor_magic_from_d3d12(D3D12_DESCRIPTOR_RANGE_TYPE ty
     }
 }
 
+static unsigned int vk_binding_count_from_descriptor_range(const struct d3d12_root_descriptor_table_range *range)
+{
+    if (range->descriptor_count != UINT_MAX)
+        return range->descriptor_count;
+
+    /* TODO: Calculate an upper bound from unbounded set counts and Vulkan device limits. */
+    return 1024;
+}
+
 static HRESULT d3d12_root_signature_init_descriptor_array_binding(struct d3d12_root_signature *root_signature,
         const struct d3d12_root_descriptor_table_range *range, D3D12_SHADER_VISIBILITY visibility,
         struct vkd3d_descriptor_set_context *context)
@@ -629,28 +653,81 @@ static HRESULT d3d12_root_signature_init_descriptor_array_binding(struct d3d12_r
     bool is_buffer = range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_CBV;
     enum vkd3d_shader_descriptor_type descriptor_type = range->type;
 
+    if (range->descriptor_count == UINT_MAX)
+        context->unbounded_offset = range->offset;
+
     if (descriptor_type == VKD3D_SHADER_DESCRIPTOR_TYPE_SRV || descriptor_type == VKD3D_SHADER_DESCRIPTOR_TYPE_UAV)
     {
         if (!vk_binding_from_d3d12_descriptor_range(context->current_binding,
-                descriptor_type, visibility, true, context->descriptor_binding, range->descriptor_count))
+                descriptor_type, visibility, true, context->descriptor_binding, range->vk_binding_count))
             return E_NOTIMPL;
         ++context->current_binding;
 
         d3d12_root_signature_append_vk_binding(root_signature, descriptor_type, range->register_space,
-                range->base_register_idx, true, shader_visibility, range->descriptor_count, context);
+                range->base_register_idx, true, shader_visibility, range->vk_binding_count, context);
     }
 
     if (!vk_binding_from_d3d12_descriptor_range(context->current_binding,
-            descriptor_type, visibility, is_buffer, context->descriptor_binding, range->descriptor_count))
+            descriptor_type, visibility, is_buffer, context->descriptor_binding, range->vk_binding_count))
         return E_NOTIMPL;
     ++context->current_binding;
 
     d3d12_root_signature_append_vk_binding(root_signature, descriptor_type, range->register_space,
-            range->base_register_idx, is_buffer, shader_visibility, range->descriptor_count, context);
+            range->base_register_idx, is_buffer, shader_visibility, range->vk_binding_count, context);
+
+    context->unbounded_offset = UINT_MAX;
 
     return S_OK;
 }
 
+static void d3d12_root_signature_map_vk_unbounded_binding(struct d3d12_root_signature *root_signature,
+        const struct d3d12_root_descriptor_table_range *range, bool buffer_descriptor,
+        enum vkd3d_shader_visibility shader_visibility, struct vkd3d_descriptor_set_context *context)
+{
+    struct vkd3d_shader_resource_binding *mapping
+            = &root_signature->descriptor_mapping[context->descriptor_index++];
+
+    mapping->type = range->type;
+    mapping->register_space = range->register_space;
+    mapping->register_index = range->base_register_idx;
+    mapping->shader_visibility = shader_visibility;
+    mapping->flags = buffer_descriptor ? VKD3D_SHADER_BINDING_FLAG_BUFFER : VKD3D_SHADER_BINDING_FLAG_IMAGE;
+    mapping->binding.set = root_signature->main_set + range->set + ((range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_SRV
+            || range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_UAV) && !buffer_descriptor);
+    mapping->binding.binding = range->binding;
+    mapping->binding.count = range->vk_binding_count;
+}
+
+static void d3d12_root_signature_map_descriptor_unbounded_binding(struct d3d12_root_signature *root_signature,
+        const struct d3d12_root_descriptor_table_range *range,
+        enum vkd3d_shader_visibility shader_visibility, struct vkd3d_descriptor_set_context *context)
+{
+    bool is_buffer = range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_CBV;
+
+    if (range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_SRV || range->type == VKD3D_SHADER_DESCRIPTOR_TYPE_UAV)
+        d3d12_root_signature_map_vk_unbounded_binding(root_signature, range,
+                true, shader_visibility, context);
+
+    d3d12_root_signature_map_vk_unbounded_binding(root_signature, range,
+            is_buffer, shader_visibility, context);
+}
+
+static int compare_range(const void *a, const void *b)
+{
+    const struct d3d12_root_descriptor_table_range *range_a = a, *range_b = b;
+    int unbounded_a, unbounded_b;
+
+    if (range_a->descriptor_magic != range_b->descriptor_magic)
+        return range_a->descriptor_magic - range_b->descriptor_magic;
+
+    unbounded_a = range_a->descriptor_count == UINT_MAX;
+    unbounded_b = range_b->descriptor_count == UINT_MAX;
+    if (unbounded_a != unbounded_b)
+        return unbounded_a - unbounded_b;
+
+    return range_a->offset - range_b->offset;
+}
+
 static HRESULT d3d12_root_signature_init_root_descriptor_tables(struct d3d12_root_signature *root_signature,
         const D3D12_ROOT_SIGNATURE_DESC *desc, struct vkd3d_descriptor_set_context *context)
 {
@@ -681,6 +758,8 @@ static HRESULT d3d12_root_signature_init_root_descriptor_tables(struct d3d12_roo
         if (!(table->ranges = vkd3d_calloc(table->range_count, sizeof(*table->ranges))))
             return E_OUTOFMEMORY;
 
+        context->table_index = i;
+
         for (j = 0; j < range_count; ++j)
         {
             const D3D12_DESCRIPTOR_RANGE *range = &p->u.DescriptorTable.pDescriptorRanges[j];
@@ -688,7 +767,7 @@ static HRESULT d3d12_root_signature_init_root_descriptor_tables(struct d3d12_roo
             if (range->OffsetInDescriptorsFromTableStart != D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND)
                 offset = range->OffsetInDescriptorsFromTableStart;
 
-            if (!vkd3d_bound_range(offset, range->NumDescriptors, UINT_MAX))
+            if (range->NumDescriptors != UINT_MAX && !vkd3d_bound_range(offset, range->NumDescriptors, UINT_MAX))
                 return E_INVALIDARG;
 
             table->ranges[j].offset = offset;
@@ -704,15 +783,41 @@ static HRESULT d3d12_root_signature_init_root_descriptor_tables(struct d3d12_roo
             offset += range->NumDescriptors;
         }
 
+        qsort(table->ranges, range_count, sizeof(*table->ranges), compare_range);
+
         for (j = 0; j < range_count; ++j)
         {
+            const struct d3d12_root_descriptor_table_range *prev_range;
             struct d3d12_root_descriptor_table_range *range;
             VkDescriptorSetLayoutBinding *cur_binding;
             range = &table->ranges[j];
 
+            range->set = root_signature->vk_set_layouts.count - root_signature->main_set;
+
             if (root_signature->use_descriptor_arrays)
             {
+                if (j && range->descriptor_magic == (prev_range = &table->ranges[j - 1])->descriptor_magic
+                        && range->descriptor_count == UINT_MAX && prev_range->descriptor_count == UINT_MAX)
+                {
+                    unsigned int rel_offset = range->offset - prev_range->offset;
+
+                    if (rel_offset >= prev_range->vk_binding_count)
+                    {
+                        ERR("Available binding size of %u is insufficient for an offset of %u.\n",
+                                prev_range->vk_binding_count, rel_offset);
+                        continue;
+                    }
+
+                    range->set = prev_range->set;
+                    range->binding = prev_range->binding;
+                    range->vk_binding_count = prev_range->vk_binding_count - rel_offset;
+                    d3d12_root_signature_map_descriptor_unbounded_binding(root_signature, range,
+                            shader_visibility, context);
+                    continue;
+                }
+
                 range->binding = context->descriptor_binding;
+                range->vk_binding_count = vk_binding_count_from_descriptor_range(range);
 
                 if (FAILED(hr = d3d12_root_signature_init_descriptor_array_binding(root_signature,
                         range, p->ShaderVisibility, context)))
@@ -752,6 +857,7 @@ static HRESULT d3d12_root_signature_init_root_descriptor_tables(struct d3d12_roo
                 ++cur_binding;
             }
 
+            table->ranges[j].vk_binding_count = table->ranges[j].descriptor_count;
             table->ranges[j].binding = vk_binding;
 
             context->current_binding = cur_binding;
@@ -849,7 +955,7 @@ static bool vk_binding_uses_partial_binding(const VkDescriptorSetLayoutBinding *
 }
 
 static HRESULT vkd3d_create_descriptor_set_layout(struct d3d12_device *device,
-        VkDescriptorSetLayoutCreateFlags flags, unsigned int binding_count,
+        VkDescriptorSetLayoutCreateFlags flags, unsigned int binding_count, bool unbounded,
         const VkDescriptorSetLayoutBinding *bindings, VkDescriptorSetLayout *set_layout)
 {
     const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
@@ -868,7 +974,7 @@ static HRESULT vkd3d_create_descriptor_set_layout(struct d3d12_device *device,
         unsigned int i;
 
         for (i = 0; i < binding_count; ++i)
-            if (vk_binding_uses_partial_binding(&bindings[i]))
+            if (unbounded || vk_binding_uses_partial_binding(&bindings[i]))
                 break;
 
         if (i < binding_count)
@@ -880,6 +986,10 @@ static HRESULT vkd3d_create_descriptor_set_layout(struct d3d12_device *device,
                 set_flags[i] = vk_binding_uses_partial_binding(&bindings[i])
                         ? VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT : 0;
 
+            if (unbounded)
+                set_flags[binding_count - 1] = VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT
+                        | VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT;
+
             flags_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT;
             flags_info.pNext = NULL;
             flags_info.bindingCount = binding_count;
@@ -909,6 +1019,9 @@ static HRESULT vkd3d_create_pipeline_layout(struct d3d12_device *device,
     struct VkPipelineLayoutCreateInfo pipeline_layout_info;
     VkResult vr;
 
+    if (!vkd3d_validate_descriptor_set_count(device, set_layout_count))
+        return E_INVALIDARG;
+
     pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
     pipeline_layout_info.pNext = NULL;
     pipeline_layout_info.flags = 0;
@@ -936,13 +1049,14 @@ static HRESULT d3d12_root_signature_init(struct d3d12_root_signature *root_signa
     HRESULT hr;
 
     memset(&context, 0, sizeof(context));
+    context.unbounded_offset = UINT_MAX;
     binding_desc = NULL;
 
     root_signature->ID3D12RootSignature_iface.lpVtbl = &d3d12_root_signature_vtbl;
     root_signature->refcount = 1;
 
     root_signature->vk_pipeline_layout = VK_NULL_HANDLE;
-    root_signature->vk_set_count = 0;
+    root_signature->vk_set_layouts.count = 0;
     root_signature->parameters = NULL;
     root_signature->flags = desc->Flags;
     root_signature->descriptor_mapping = NULL;
@@ -999,6 +1113,8 @@ static HRESULT d3d12_root_signature_init(struct d3d12_root_signature *root_signa
             goto fail;
     }
 
+    root_signature->main_set = root_signature->vk_set_layouts.count;
+
     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;
@@ -1007,16 +1123,14 @@ static HRESULT d3d12_root_signature_init(struct d3d12_root_signature *root_signa
     if (FAILED(hr = d3d12_root_signature_init_root_descriptor_tables(root_signature, desc, &context)))
         goto fail;
 
-    root_signature->main_set = root_signature->vk_set_count;
-
     if (FAILED(hr = d3d12_root_signature_append_descriptor_set_layout(root_signature, &context, 0)))
         goto fail;
 
     vkd3d_free(binding_desc);
     binding_desc = NULL;
 
-    if (FAILED(hr = vkd3d_create_pipeline_layout(device, root_signature->vk_set_count,
-            root_signature->vk_set_layouts, root_signature->push_constant_range_count,
+    if (FAILED(hr = vkd3d_create_pipeline_layout(device, root_signature->vk_set_layouts.count,
+            root_signature->vk_set_layouts.layouts, root_signature->push_constant_range_count,
             root_signature->push_constant_ranges, &root_signature->vk_pipeline_layout)))
         goto fail;
 
@@ -1621,8 +1735,8 @@ static HRESULT d3d12_pipeline_state_init_uav_counters(struct d3d12_pipeline_stat
     state->uav_counters.binding_count = uav_counter_count;
 
     descriptor_binding = 0;
-    for (set_index = 0; set_index < root_signature->vk_set_count; ++set_index)
-        set_layouts[set_index] = root_signature->vk_set_layouts[set_index];
+    for (set_index = 0; set_index < root_signature->vk_set_layouts.count; ++set_index)
+        set_layouts[set_index] = root_signature->vk_set_layouts.layouts[set_index];
 
     for (i = 0, j = 0; i < shader_info->descriptor_count; ++i)
     {
@@ -1652,7 +1766,7 @@ static HRESULT d3d12_pipeline_state_init_uav_counters(struct d3d12_pipeline_stat
 
     /* Create a descriptor set layout for UAV counters. */
     hr = vkd3d_create_descriptor_set_layout(device,
-            0, descriptor_binding, binding_desc, &state->uav_counters.vk_set_layout);
+            0, descriptor_binding, false, binding_desc, &state->uav_counters.vk_set_layout);
     vkd3d_free(binding_desc);
     if (FAILED(hr))
     {
@@ -3122,7 +3236,8 @@ HRESULT vkd3d_uav_clear_state_init(struct vkd3d_uav_clear_state *state, struct d
     {
         set_binding.descriptorType = set_layouts[i].descriptor_type;
 
-        if (FAILED(hr = vkd3d_create_descriptor_set_layout(device, 0, 1, &set_binding, set_layouts[i].set_layout)))
+        if (FAILED(hr = vkd3d_create_descriptor_set_layout(device, 0, 1, false,
+                &set_binding, set_layouts[i].set_layout)))
         {
             ERR("Failed to create descriptor set layout %u, hr %#x.\n", i, hr);
             goto fail;
diff --git a/libs/vkd3d/vkd3d_private.h b/libs/vkd3d/vkd3d_private.h
index 0b326b11..b337bb81 100644
--- a/libs/vkd3d/vkd3d_private.h
+++ b/libs/vkd3d/vkd3d_private.h
@@ -54,7 +54,7 @@
 #define VKD3D_MAX_SHADER_EXTENSIONS       2u
 #define VKD3D_MAX_SHADER_STAGES           5u
 #define VKD3D_MAX_VK_SYNC_OBJECTS         4u
-#define VKD3D_MAX_DESCRIPTOR_SETS         2u
+#define VKD3D_MAX_DESCRIPTOR_SETS        64u
 
 struct d3d12_command_list;
 struct d3d12_device;
@@ -647,6 +647,8 @@ struct d3d12_root_descriptor_table_range
 {
     unsigned int offset;
     unsigned int descriptor_count;
+    unsigned int vk_binding_count;
+    uint32_t set;
     uint32_t binding;
 
     enum vkd3d_shader_descriptor_type type;
@@ -683,6 +685,14 @@ struct d3d12_root_parameter
     } u;
 };
 
+struct vk_set_layouts_state
+{
+    VkDescriptorSetLayout layouts[VKD3D_MAX_DESCRIPTOR_SETS];
+    unsigned int unbounded_offsets[VKD3D_MAX_DESCRIPTOR_SETS];
+    unsigned int table_indices[VKD3D_MAX_DESCRIPTOR_SETS];
+    uint32_t count;
+};
+
 /* ID3D12RootSignature */
 struct d3d12_root_signature
 {
@@ -690,8 +700,7 @@ struct d3d12_root_signature
     LONG refcount;
 
     VkPipelineLayout vk_pipeline_layout;
-    uint32_t vk_set_count;
-    VkDescriptorSetLayout vk_set_layouts[VKD3D_MAX_DESCRIPTOR_SETS];
+    struct vk_set_layouts_state vk_set_layouts;
     bool use_descriptor_arrays;
 
     struct d3d12_root_parameter *parameters;
@@ -912,8 +921,9 @@ struct vkd3d_pipeline_bindings
     const struct d3d12_root_signature *root_signature;
 
     VkPipelineBindPoint vk_bind_point;
+    /* All descriptor sets at index > 1 are for unbounded D3D12 ranges. Set 0 or 1 may be unbounded too. */
     size_t descriptor_set_count;
-    VkDescriptorSet descriptor_sets[VKD3D_MAX_DESCRIPTOR_SETS - 1];
+    VkDescriptorSet descriptor_sets[VKD3D_MAX_DESCRIPTOR_SETS];
     bool in_use;
 
     D3D12_GPU_DESCRIPTOR_HANDLE descriptor_tables[D3D12_MAX_ROOT_COST];
@@ -1114,6 +1124,13 @@ struct vkd3d_uav_clear_state
 HRESULT vkd3d_uav_clear_state_init(struct vkd3d_uav_clear_state *state, struct d3d12_device *device);
 void vkd3d_uav_clear_state_cleanup(struct vkd3d_uav_clear_state *state, struct d3d12_device *device);
 
+struct vkd3d_descriptor_heap_array
+{
+    const struct d3d12_descriptor_heap **heaps;
+    size_t capacity;
+    size_t count;
+};
+
 /* ID3D12Device */
 struct d3d12_device
 {
@@ -1165,6 +1182,8 @@ struct d3d12_device
     const struct vkd3d_format_compatibility_list *format_compatibility_lists;
     struct vkd3d_null_resources null_resources;
     struct vkd3d_uav_clear_state uav_clear_state;
+
+    struct vkd3d_descriptor_heap_array descriptor_heaps;
 };
 
 HRESULT d3d12_device_create(struct vkd3d_instance *instance,
@@ -1175,6 +1194,11 @@ void d3d12_device_mark_as_removed(struct d3d12_device *device, HRESULT reason,
         const char *message, ...) VKD3D_PRINTF_FUNC(3, 4);
 struct d3d12_device *unsafe_impl_from_ID3D12Device(ID3D12Device *iface);
 
+void d3d12_device_track_descriptor_heap(struct d3d12_device *device, const struct d3d12_descriptor_heap *heap);
+void d3d12_device_untrack_descriptor_heap(struct d3d12_device *device, const struct d3d12_descriptor_heap *heap);
+uint32_t d3d12_device_descriptor_heap_size_from_descriptor(struct d3d12_device *device,
+        const struct d3d12_desc *desc);
+
 static inline HRESULT d3d12_device_query_interface(struct d3d12_device *device, REFIID iid, void **object)
 {
     return ID3D12Device_QueryInterface(&device->ID3D12Device_iface, iid, object);
diff --git a/tests/d3d12.c b/tests/d3d12.c
index 2b0f6fb2..6948b1a0 100644
--- a/tests/d3d12.c
+++ b/tests/d3d12.c
@@ -34889,7 +34889,6 @@ static void test_unbounded_resource_arrays(void)
     root_signature_desc.NumParameters = ARRAY_SIZE(root_parameters);
     root_signature_desc.pParameters = root_parameters;
     hr = create_root_signature(device, &root_signature_desc, &context.root_signature);
-    todo
     ok(hr == S_OK, "Failed to create root signature, hr %#x.\n", hr);
     if (FAILED(hr))
         goto done;
@@ -34963,6 +34962,7 @@ static void test_unbounded_resource_arrays(void)
                 D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
         get_buffer_readback_with_command_list(output_buffers[i], DXGI_FORMAT_R32_UINT, &rb, queue, command_list);
         /* Buffers at index >= 64 are aliased. */
+        todo_if(i != 10 && i != 74)
         check_readback_data_uint(&rb, NULL, (i < 64 ? 63 - i : 127 - i) ^ 0x35, 0);
         release_resource_readback(&rb);
         reset_command_list(command_list, context.allocator);
@@ -35052,7 +35052,6 @@ static void test_unbounded_samplers(void)
     root_signature_desc.pParameters = root_parameters;
 
     hr = create_root_signature(device, &root_signature_desc, &context.root_signature);
-    todo
     ok(SUCCEEDED(hr), "Failed to create root signature, hr %#x.\n", hr);
     if (FAILED(hr))
         goto done;
@@ -35110,6 +35109,7 @@ static void test_unbounded_samplers(void)
     {
         unsigned int value = get_readback_uint(&rb, i, 0, 0);
         unsigned int expected = (i & 1) ? 100 : 10;
+        todo_if(i & 1)
         ok(value == expected, "Got %u, expected %u at %u.\n", value, expected, i);
     }
     release_resource_readback(&rb);
-- 
2.32.0




More information about the wine-devel mailing list