[PATCH vkd3d 5/7] vkd3d: Create and write descriptor sets for root signature unbounded ranges.

Henri Verbeet hverbeet at gmail.com
Thu Aug 19 10:00:58 CDT 2021


On Fri, 13 Aug 2021 at 16:57, Conor McCarthy <cmccarthy at codeweavers.com> wrote:
> @@ -2581,9 +2594,44 @@ 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_count; ++i)
> +    {
> +        unsigned int unbounded_range_offset = root_signature->vk_set_layout_unbounded_offsets[i];
> +        unsigned int unbounded_table = root_signature->vk_set_layout_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;
> +                int rc;
> +
> +                rc = pthread_mutex_lock(&device->mutex);
> +                if (rc)
> +                    ERR("Failed to lock mutex, error %d.\n", rc);
> +                heap_size = d3d12_device_descriptor_heap_size_from_descriptor(device, base_descriptor);
> +                if (!rc)
> +                    pthread_mutex_unlock(&device->mutex);
> +
Would it make sense to do the locking for
d3d12_device_descriptor_heap_size_from_descriptor() inside that
function? Doing it outside the function is perhaps a little more
flexible, but it doesn't seem to gain us much here.

> @@ -2687,7 +2749,31 @@ 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)
> +        {
> +            int rc;
> +
> +            /* The first unbounded range of each type is written until the heap end is reached. Do not repeat. */
> +            if (i && range[-1].descriptor_magic == range->descriptor_magic && range[-1].descriptor_count == UINT_MAX)
> +                continue;
> +
I get that "range[-1]" is convenient, but I don't like it much. There
are a few more instances of this in this patch.

> +void d3d12_device_track_descriptor_heap(struct d3d12_device *device,
> +        const struct d3d12_descriptor_heap *heap)
> +{
> +    if (!device->vk_info.EXT_descriptor_indexing)
> +        return;
> +
> +    if (!vkd3d_array_reserve((void **)&device->descriptor_heaps, &device->descriptor_heap_capacity,
> +            device->descriptor_heap_count + 1, sizeof(*device->descriptor_heaps)))
> +    {
> +        ERR("Out of memory. Cannot track descriptor heap for unbounded arrays.\n");
> +        return;
> +    }
> +
> +    device->descriptor_heaps[device->descriptor_heap_count++] = heap;
> +    /* Do not increment the heap reference count. This reference is deleted on heap destruction. */
> +}
> +
It seems like it would make sense to group the "descriptor_heaps",
"descriptor_heap_capacity", and "descriptor_heap_count" fields
together in their own structure, similar to how that's done for e.g.
struct vkd3d_render_pass_cache.

> +void d3d12_device_untrack_descriptor_heap(struct d3d12_device *device,
> +        const struct d3d12_descriptor_heap *heap)
> +{
> +    size_t i;
> +
> +    if (!device->vk_info.EXT_descriptor_indexing)
> +        return;
> +
> +    for (i = 0; i < device->descriptor_heap_count; ++i)
> +    {
> +        if (device->descriptor_heaps[i] != heap)
> +            continue;
> +
> +        memmove(&device->descriptor_heaps[i], &device->descriptor_heaps[i + 1],
> +                (device->descriptor_heap_count - i - 1) * sizeof(*device->descriptor_heaps));
> +        --device->descriptor_heap_count;
> +
> +        return;
> +    }
> +
> +    ERR("Attempted to untrack an already untracked heap.\n");
> +}
> +
If we don't need to maintain the order in which heaps were inserted,
we don't really need to use memmove() here; we'd only need to move the
last element to the index of the element that's to be removed.

> +/* 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)
> +{
> +    size_t i;
> +
> +    for (i = 0; i < device->descriptor_heap_count; ++i)
> +    {
> +        unsigned int size;
> +        size_t offset;
> +
> +        if (device->descriptor_heaps[i]->descriptors > (const BYTE*)desc)
> +            continue;
> +
> +        size = d3d12_device_get_descriptor_handle_increment_size(device, device->descriptor_heaps[i]->desc.Type);
> +        offset = ((const BYTE*)desc - device->descriptor_heaps[i]->descriptors) / size;
> +        if (device->descriptor_heaps[i]->desc.NumDescriptors <= offset)
> +            continue;
> +
> +        return device->descriptor_heaps[i]->desc.NumDescriptors - (uint32_t)offset;
> +    }
> +
> +    ERR("Failed to find descriptor heap size from descriptor pointer.\n");
> +    return 0;
> +}
> +
Presumably we could just skip checking heaps that don't have the
correct type, and then "size" would always be "sizeof(*desc)".

> @@ -3547,6 +3555,12 @@ HRESULT d3d12_descriptor_heap_create(struct d3d12_device *device,
>
>      memset(object->descriptors, 0, descriptor_size * desc->NumDescriptors);
>
> +    if ((rc = pthread_mutex_lock(&device->mutex)))
> +        ERR("Failed to lock mutex, error %d.\n", rc);
> +    d3d12_device_track_descriptor_heap(device, object);
> +    if (!rc)
> +        pthread_mutex_unlock(&device->mutex);
> +
Do we need to track RTV and DSV heaps?

> @@ -332,14 +334,33 @@ 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;
> +        if (unbounded)
> +         {
Some stray whitespace above.

> +            if (range->NumDescriptors != UINT_MAX)
> +            {
> +                ERR("Static range occurs after unbounded range.\n");
> +                return E_INVALIDARG;
> +            }
> +            if (range->OffsetInDescriptorsFromTableStart == D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND)
> +            {
> +                ERR("Unbounded range with offset D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND occurs after "
> +                        "another unbounded range.\n");
> +                return E_INVALIDARG;
> +            }
>          }
>
>          binding_count = use_array ? 1 : range->NumDescriptors;
>
> +        if (range->NumDescriptors == UINT_MAX)
> +        {
> +            if (!use_array)
> +            {
> +                FIXME("The device does not support unbounded descriptor ranges.\n");
> +                return E_NOTIMPL;
> +            }
> +            unbounded = true;
> +        }
> +
The validation changes above could perhaps be in a separate patch.

> @@ -692,6 +694,8 @@ struct d3d12_root_signature
>      VkPipelineLayout vk_pipeline_layout;
>      uint32_t vk_set_count;
>      VkDescriptorSetLayout vk_set_layouts[VKD3D_MAX_DESCRIPTOR_SETS];
> +    unsigned int vk_set_layout_unbounded_offsets[VKD3D_MAX_DESCRIPTOR_SETS];
> +    unsigned int vk_set_layout_table_indices[VKD3D_MAX_DESCRIPTOR_SETS];
>      bool use_descriptor_arrays;
>
Like the descriptor heap tracking fields mentioned earlier, it seems
like it would make sense to group the "vk_set_layouts",
"vk_set_layout_unbounded_offsets", and "vk_set_layout_table_indices"
together in their own structure.



More information about the wine-devel mailing list