From 104626cc1d91d0a3568b8bc17ab782ebf062b858 Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Wed, 6 Oct 2021 18:43:30 +0200 Subject: [PATCH] vkd3d: Create and write descriptor sets for root signature unbounded ranges. Signed-off-by: Conor McCarthy Signed-off-by: Henri Verbeet Signed-off-by: Alexandre Julliard --- libs/vkd3d/command.c | 120 +++++++++++++++++++----- libs/vkd3d/device.c | 114 +++++++++++++++++++++++ libs/vkd3d/resource.c | 9 ++ libs/vkd3d/state.c | 181 ++++++++++++++++++++++++++++++++----- libs/vkd3d/vkd3d_private.h | 40 +++++++- tests/d3d12.c | 18 ++-- 6 files changed, 424 insertions(+), 58 deletions(-) diff --git a/libs/vkd3d/command.c b/libs/vkd3d/command.c index 9f4a4149..118fc4bb 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, + unsigned int 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; @@ -2562,7 +2573,12 @@ static void d3d12_command_list_prepare_descriptors(struct d3d12_command_list *li enum vkd3d_pipeline_bind_point bind_point) { struct vkd3d_pipeline_bindings *bindings = &list->pipeline_bindings[bind_point]; + unsigned int variable_binding_size, unbounded_offset, table_index, heap_size, i; const struct d3d12_root_signature *root_signature = bindings->root_signature; + const struct d3d12_descriptor_set_layout *layout; + struct d3d12_device *device = list->device; + const struct d3d12_desc *base_descriptor; + VkDescriptorSet vk_descriptor_set; if (bindings->descriptor_set_count && !bindings->in_use) return; @@ -2579,9 +2595,33 @@ 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) + { + layout = &root_signature->descriptor_set_layouts[i]; + unbounded_offset = layout->unbounded_offset; + table_index = layout->table_index; + variable_binding_size = 0; + + if (unbounded_offset != UINT_MAX + /* Descriptors may not be set, eg. WoW. */ + && (base_descriptor = d3d12_desc_from_gpu_handle(bindings->descriptor_tables[table_index]))) + { + heap_size = vkd3d_gpu_descriptor_allocator_range_size_from_descriptor( + &device->gpu_descriptor_allocator, base_descriptor); + + if (heap_size < unbounded_offset) + 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_offset, table_index, i); + else + variable_binding_size = heap_size - unbounded_offset; + } + + vk_descriptor_set = d3d12_command_allocator_allocate_descriptor_set(list->allocator, + layout->vk_layout, variable_binding_size, unbounded_offset != UINT_MAX); + bindings->descriptor_sets[bindings->descriptor_set_count++] = vk_descriptor_set; + } + bindings->in_use = false; bindings->descriptor_table_dirty_mask |= bindings->descriptor_table_active_mask & root_signature->descriptor_table_mask; @@ -2596,13 +2636,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; @@ -2620,12 +2661,26 @@ 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) @@ -2673,19 +2728,38 @@ 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; + bool unbounded = false; 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) { range = &descriptor_table->ranges[i]; + /* The first unbounded range of each type is written until the heap end is reached. Do not repeat. */ + if (unbounded && i && range->type == descriptor_table->ranges[i - 1].type) + continue; + descriptor = base_descriptor + range->offset; - for (j = 0; j < range->descriptor_count; ++j, ++descriptor) + descriptor_count = range->descriptor_count; + if ((unbounded = descriptor_count == UINT_MAX)) + { + descriptor_count = vkd3d_gpu_descriptor_allocator_range_size_from_descriptor( + &list->device->gpu_descriptor_allocator, 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; @@ -2707,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, @@ -2850,8 +2928,8 @@ static void d3d12_command_list_update_uav_counter_descriptors(struct d3d12_comma uav_counter_count = state->uav_counters.binding_count; 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))) + if (!(vk_descriptor_set = d3d12_command_allocator_allocate_descriptor_set( + list->allocator, state->uav_counters.vk_set_layout, 0, false))) goto done; for (i = 0; i < uav_counter_count; ++i) @@ -4946,7 +5024,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..320a759a 100644 --- a/libs/vkd3d/device.c +++ b/libs/vkd3d/device.c @@ -2173,6 +2173,118 @@ static void vkd3d_gpu_va_allocator_cleanup(struct vkd3d_gpu_va_allocator *alloca pthread_mutex_destroy(&allocator->mutex); } +bool vkd3d_gpu_descriptor_allocator_register_range(struct vkd3d_gpu_descriptor_allocator *allocator, + const struct d3d12_desc *base, size_t count) +{ + struct vkd3d_gpu_descriptor_allocation *allocation; + int rc; + + if ((rc = pthread_mutex_lock(&allocator->mutex))) + { + ERR("Failed to lock mutex, error %d.\n", rc); + return false; + } + + if (!vkd3d_array_reserve((void **)&allocator->allocations, &allocator->allocations_size, + allocator->allocation_count + 1, sizeof(*allocator->allocations))) + { + pthread_mutex_unlock(&allocator->mutex); + return false; + } + + allocation = &allocator->allocations[allocator->allocation_count++]; + allocation->base = base; + allocation->count = count; + + pthread_mutex_unlock(&allocator->mutex); + + return true; +} + +bool vkd3d_gpu_descriptor_allocator_unregister_range( + struct vkd3d_gpu_descriptor_allocator *allocator, const struct d3d12_desc *base) +{ + bool found; + size_t i; + int rc; + + if ((rc = pthread_mutex_lock(&allocator->mutex))) + { + ERR("Failed to lock mutex, error %d.\n", rc); + return false; + } + + for (i = 0, found = false; i < allocator->allocation_count; ++i) + { + if (allocator->allocations[i].base != base) + continue; + + if (i != --allocator->allocation_count) + allocator->allocations[i] = allocator->allocations[allocator->allocation_count]; + + found = true; + break; + } + + pthread_mutex_unlock(&allocator->mutex); + + return found; +} + +/* Return the available size from the specified descriptor to the heap end. */ +size_t vkd3d_gpu_descriptor_allocator_range_size_from_descriptor( + struct vkd3d_gpu_descriptor_allocator *allocator, const struct d3d12_desc *desc) +{ + struct vkd3d_gpu_descriptor_allocation *allocation; + size_t remaining, offset, i; + int rc; + + if ((rc = pthread_mutex_lock(&allocator->mutex))) + { + ERR("Failed to lock mutex, error %d.\n", rc); + return 0; + } + + for (i = 0, remaining = 0; i < allocator->allocation_count; ++i) + { + allocation = &allocator->allocations[i]; + + if (desc < allocation->base) + continue; + + offset = desc - allocation->base; + if (offset >= allocation->count) + continue; + + remaining = allocation->count - offset; + break; + } + + pthread_mutex_unlock(&allocator->mutex); + + return remaining; +} + +static bool vkd3d_gpu_descriptor_allocator_init(struct vkd3d_gpu_descriptor_allocator *allocator) +{ + int rc; + + memset(allocator, 0, sizeof(*allocator)); + if ((rc = pthread_mutex_init(&allocator->mutex, NULL))) + { + ERR("Failed to initialise mutex, error %d.\n", rc); + return false; + } + + return true; +} + +static void vkd3d_gpu_descriptor_allocator_cleanup(struct vkd3d_gpu_descriptor_allocator *allocator) +{ + vkd3d_free(allocator->allocations); + pthread_mutex_destroy(&allocator->mutex); +} + /* ID3D12Device */ static inline struct d3d12_device *impl_from_ID3D12Device(ID3D12Device *iface) { @@ -2227,6 +2339,7 @@ static ULONG STDMETHODCALLTYPE d3d12_device_Release(ID3D12Device *iface) vkd3d_uav_clear_state_cleanup(&device->uav_clear_state, device); vkd3d_destroy_null_resources(&device->null_resources, device); vkd3d_gpu_va_allocator_cleanup(&device->gpu_va_allocator); + vkd3d_gpu_descriptor_allocator_cleanup(&device->gpu_descriptor_allocator); vkd3d_render_pass_cache_cleanup(&device->render_pass_cache, device); vkd3d_fence_worker_stop(&device->fence_worker, device); d3d12_device_destroy_pipeline_cache(device); @@ -3729,6 +3842,7 @@ static HRESULT d3d12_device_init(struct d3d12_device *device, goto out_destroy_null_resources; vkd3d_render_pass_cache_init(&device->render_pass_cache); + vkd3d_gpu_descriptor_allocator_init(&device->gpu_descriptor_allocator); vkd3d_gpu_va_allocator_init(&device->gpu_va_allocator); for (i = 0; i < ARRAY_SIZE(device->desc_mutex); ++i) diff --git a/libs/vkd3d/resource.c b/libs/vkd3d/resource.c index 1ca23a90..a51f5795 100644 --- a/libs/vkd3d/resource.c +++ b/libs/vkd3d/resource.c @@ -3351,6 +3351,10 @@ static ULONG STDMETHODCALLTYPE d3d12_descriptor_heap_Release(ID3D12DescriptorHea { d3d12_desc_destroy(&descriptors[i], device); } + + if (device->vk_info.EXT_descriptor_indexing && !vkd3d_gpu_descriptor_allocator_unregister_range( + &device->gpu_descriptor_allocator, descriptors)) + ERR("Failed to unregister descriptor range.\n"); break; } @@ -3547,6 +3551,11 @@ 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) + && device->vk_info.EXT_descriptor_indexing && !vkd3d_gpu_descriptor_allocator_register_range( + &device->gpu_descriptor_allocator, (struct d3d12_desc *)object->descriptors, desc->NumDescriptors)) + ERR("Failed to register descriptor range.\n"); + TRACE("Created descriptor heap %p.\n", object); *descriptor_heap = object; diff --git a/libs/vkd3d/state.c b/libs/vkd3d/state.c index 990b49b2..78b0c717 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,14 @@ static ULONG STDMETHODCALLTYPE d3d12_root_signature_AddRef(ID3D12RootSignature * return refcount; } +static void d3d12_descriptor_set_layout_cleanup( + struct d3d12_descriptor_set_layout *layout, struct d3d12_device *device) +{ + const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs; + + VK_CALL(vkDestroyDescriptorSetLayout(device->vk_device, layout->vk_layout, NULL)); +} + static void d3d12_root_signature_cleanup(struct d3d12_root_signature *root_signature, struct d3d12_device *device) { @@ -66,7 +75,9 @@ 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)); + { + d3d12_descriptor_set_layout_cleanup(&root_signature->descriptor_set_layouts[i], device); + } if (root_signature->parameters) { @@ -370,10 +381,10 @@ static HRESULT d3d12_root_signature_info_count_descriptors(struct d3d12_root_sig info->binding_count += binding_count; } - if (unbounded) + if (unbounded && !use_array) { - FIXME("Unhandled unbounded descriptor range.\n"); - return E_NOTIMPL; + FIXME("The device does not support unbounded descriptor ranges.\n"); + return E_FAIL; } return S_OK; @@ -526,6 +537,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; }; @@ -544,24 +557,30 @@ 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 d3d12_descriptor_set_layout *layout; + 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)) + index = root_signature->vk_set_count; + layout = &root_signature->descriptor_set_layouts[index]; + + 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]))) + if (FAILED(hr = vkd3d_create_descriptor_set_layout(root_signature->device, flags, context->descriptor_binding, + context->unbounded_offset != UINT_MAX, context->first_binding, &layout->vk_layout))) return hr; + layout->table_index = context->table_index; + layout->unbounded_offset = context->unbounded_offset; ++root_signature->vk_set_count; context->current_binding = context->first_binding; @@ -586,6 +605,9 @@ static void d3d12_root_signature_append_vk_binding(struct d3d12_root_signature * mapping->binding.set = root_signature->vk_set_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, @@ -632,6 +654,16 @@ 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) @@ -640,28 +672,62 @@ 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_register_range(const void *a, const void *b) { const struct d3d12_root_descriptor_table_range *range_a = a, *range_b = b; @@ -676,6 +742,20 @@ static int compare_register_range(const void *a, const void *b) return range_a->base_register_idx - range_b->base_register_idx; } +static int compare_descriptor_range(const void *a, const void *b) +{ + const struct d3d12_root_descriptor_table_range *range_a = a, *range_b = b; + int ret; + + if ((ret = range_a->type - range_b->type)) + return ret; + + if ((ret = range_a->offset - range_b->offset)) + return ret; + + return (range_a->descriptor_count == UINT_MAX) - (range_b->descriptor_count == UINT_MAX); +} + static HRESULT validate_descriptor_register_ranges(const struct d3d12_root_descriptor_table_range *ranges, unsigned int count) { @@ -707,6 +787,7 @@ static HRESULT d3d12_root_signature_init_root_descriptor_tables(struct d3d12_roo for (i = 0; i < desc->NumParameters; ++i) { + const struct d3d12_root_descriptor_table_range *base_range = NULL; const D3D12_ROOT_PARAMETER *p = &desc->pParameters[i]; enum vkd3d_shader_visibility shader_visibility; unsigned int offset = 0; @@ -725,6 +806,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]; @@ -732,7 +815,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; @@ -745,6 +828,7 @@ static HRESULT d3d12_root_signature_init_root_descriptor_tables(struct d3d12_roo TRACE("Descriptor table %u, range %u, offset %u, type %#x, count %u.\n", i, j, offset, range->RangeType, range->NumDescriptors); + /* If NumDescriptors == UINT_MAX, validation during counting ensures this offset is not used. */ offset += range->NumDescriptors; } @@ -752,15 +836,49 @@ static HRESULT d3d12_root_signature_init_root_descriptor_tables(struct d3d12_roo if (FAILED(hr = validate_descriptor_register_ranges(table->ranges, range_count))) return hr; + qsort(table->ranges, range_count, sizeof(*table->ranges), compare_descriptor_range); + for (j = 0; j < range_count; ++j) { struct d3d12_root_descriptor_table_range *range; VkDescriptorSetLayoutBinding *cur_binding; + range = &table->ranges[j]; + range->set = root_signature->vk_set_count - root_signature->main_set; + if (root_signature->use_descriptor_arrays) { + if (j && range->type != table->ranges[j - 1].type) + base_range = NULL; + + /* Bounded and unbounded ranges can follow unbounded ones, + * so map them all into the first unbounded range. */ + if (base_range) + { + unsigned int rel_offset = range->offset - base_range->offset; + + if (rel_offset >= base_range->vk_binding_count) + { + ERR("Available binding size of %u is insufficient for an offset of %u.\n", + base_range->vk_binding_count, rel_offset); + continue; + } + + range->set = base_range->set; + range->binding = base_range->binding; + range->vk_binding_count = base_range->vk_binding_count - rel_offset; + d3d12_root_signature_map_descriptor_unbounded_binding(root_signature, range, + shader_visibility, context); + continue; + } + else if (range->descriptor_count == UINT_MAX) + { + base_range = range; + } + 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))) @@ -800,6 +918,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; @@ -897,7 +1016,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; @@ -916,7 +1035,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) @@ -928,6 +1047,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; @@ -957,6 +1080,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; @@ -977,13 +1103,16 @@ static HRESULT vkd3d_create_pipeline_layout(struct d3d12_device *device, static HRESULT d3d12_root_signature_init(struct d3d12_root_signature *root_signature, struct d3d12_device *device, const D3D12_ROOT_SIGNATURE_DESC *desc) { + VkDescriptorSetLayout vk_layouts[VKD3D_MAX_DESCRIPTOR_SETS]; const struct vkd3d_vulkan_info *vk_info = &device->vk_info; struct vkd3d_descriptor_set_context context; VkDescriptorSetLayoutBinding *binding_desc; struct d3d12_root_signature_info info; + unsigned int i; HRESULT hr; memset(&context, 0, sizeof(context)); + context.unbounded_offset = UINT_MAX; binding_desc = NULL; root_signature->ID3D12RootSignature_iface.lpVtbl = &d3d12_root_signature_vtbl; @@ -1047,6 +1176,8 @@ static HRESULT d3d12_root_signature_init(struct d3d12_root_signature *root_signa goto fail; } + root_signature->main_set = root_signature->vk_set_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; @@ -1055,16 +1186,19 @@ 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; + for (i = 0; i < root_signature->vk_set_count; ++i) + { + vk_layouts[i] = root_signature->descriptor_set_layouts[i].vk_layout; + } + if (FAILED(hr = vkd3d_create_pipeline_layout(device, root_signature->vk_set_count, - root_signature->vk_set_layouts, root_signature->push_constant_range_count, + vk_layouts, root_signature->push_constant_range_count, root_signature->push_constant_ranges, &root_signature->vk_pipeline_layout))) goto fail; @@ -1670,7 +1804,7 @@ static HRESULT d3d12_pipeline_state_init_uav_counters(struct d3d12_pipeline_stat 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]; + set_layouts[set_index] = root_signature->descriptor_set_layouts[set_index].vk_layout; for (i = 0, j = 0; i < shader_info->descriptor_count; ++i) { @@ -1699,8 +1833,8 @@ 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); + hr = vkd3d_create_descriptor_set_layout(device, 0, descriptor_binding, + false, binding_desc, &state->uav_counters.vk_set_layout); vkd3d_free(binding_desc); if (FAILED(hr)) { @@ -3196,7 +3330,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..f8474caa 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; @@ -234,6 +234,28 @@ D3D12_GPU_VIRTUAL_ADDRESS vkd3d_gpu_va_allocator_allocate(struct vkd3d_gpu_va_al void *vkd3d_gpu_va_allocator_dereference(struct vkd3d_gpu_va_allocator *allocator, D3D12_GPU_VIRTUAL_ADDRESS address); void vkd3d_gpu_va_allocator_free(struct vkd3d_gpu_va_allocator *allocator, D3D12_GPU_VIRTUAL_ADDRESS address); +struct vkd3d_gpu_descriptor_allocation +{ + const struct d3d12_desc *base; + size_t count; +}; + +struct vkd3d_gpu_descriptor_allocator +{ + pthread_mutex_t mutex; + + struct vkd3d_gpu_descriptor_allocation *allocations; + size_t allocations_size; + size_t allocation_count; +}; + +size_t vkd3d_gpu_descriptor_allocator_range_size_from_descriptor( + struct vkd3d_gpu_descriptor_allocator *allocator, const struct d3d12_desc *desc); +bool vkd3d_gpu_descriptor_allocator_register_range(struct vkd3d_gpu_descriptor_allocator *allocator, + const struct d3d12_desc *base, size_t count); +bool vkd3d_gpu_descriptor_allocator_unregister_range( + struct vkd3d_gpu_descriptor_allocator *allocator, const struct d3d12_desc *base); + struct vkd3d_render_pass_key { unsigned int attachment_count; @@ -647,6 +669,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 +707,13 @@ struct d3d12_root_parameter } u; }; +struct d3d12_descriptor_set_layout +{ + VkDescriptorSetLayout vk_layout; + unsigned int unbounded_offset; + unsigned int table_index; +}; + /* ID3D12RootSignature */ struct d3d12_root_signature { @@ -690,8 +721,8 @@ struct d3d12_root_signature LONG refcount; VkPipelineLayout vk_pipeline_layout; + struct d3d12_descriptor_set_layout descriptor_set_layouts[VKD3D_MAX_DESCRIPTOR_SETS]; uint32_t vk_set_count; - VkDescriptorSetLayout vk_set_layouts[VKD3D_MAX_DESCRIPTOR_SETS]; bool use_descriptor_arrays; struct d3d12_root_parameter *parameters; @@ -912,8 +943,10 @@ 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]; @@ -1126,6 +1159,7 @@ struct d3d12_device PFN_vkd3d_signal_event signal_event; size_t wchar_size; + struct vkd3d_gpu_descriptor_allocator gpu_descriptor_allocator; struct vkd3d_gpu_va_allocator gpu_va_allocator; struct vkd3d_fence_worker fence_worker; diff --git a/tests/d3d12.c b/tests/d3d12.c index 57c39d27..2a8bdcf3 100644 --- a/tests/d3d12.c +++ b/tests/d3d12.c @@ -2799,7 +2799,7 @@ static void test_create_root_signature(void) root_signature_desc.pStaticSamplers = NULL; root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE; hr = create_root_signature(device, &root_signature_desc, &root_signature); - todo ok(hr == S_OK || (binding_tier == D3D12_RESOURCE_BINDING_TIER_1 && (hr == E_FAIL || hr == E_INVALIDARG)), + ok(hr == S_OK || (binding_tier == D3D12_RESOURCE_BINDING_TIER_1 && (hr == E_FAIL || hr == E_INVALIDARG)), "Got unexpected hr %#x.\n", hr); if (SUCCEEDED(hr)) { @@ -2819,13 +2819,13 @@ static void test_create_root_signature(void) descriptor_ranges[1].RegisterSpace = 0; descriptor_ranges[1].OffsetInDescriptorsFromTableStart = 16; hr = create_root_signature(device, &root_signature_desc, &root_signature); - todo ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); + ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); /* A bounded range overlapping an unbounded one, mapped to the same * register space, but a different type. */ descriptor_ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV; hr = create_root_signature(device, &root_signature_desc, &root_signature); - todo ok(hr == S_OK || (binding_tier <= D3D12_RESOURCE_BINDING_TIER_2 && (hr == E_FAIL || hr == E_INVALIDARG)), + ok(hr == S_OK || (binding_tier <= D3D12_RESOURCE_BINDING_TIER_2 && (hr == E_FAIL || hr == E_INVALIDARG)), "Got unexpected hr %#x.\n", hr); if (SUCCEEDED(hr)) { @@ -2838,9 +2838,7 @@ static void test_create_root_signature(void) descriptor_ranges[1].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; descriptor_ranges[1].NumDescriptors = UINT_MAX; hr = create_root_signature(device, &root_signature_desc, &root_signature); - todo ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); - if (SUCCEEDED(hr)) - ID3D12RootSignature_Release(root_signature); + ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); /* And unbounded range overlapping a bounded one, mapped to the same * register space and type. */ @@ -2848,9 +2846,7 @@ static void test_create_root_signature(void) descriptor_ranges[1].BaseShaderRegister = 0; descriptor_ranges[1].OffsetInDescriptorsFromTableStart = 15; hr = create_root_signature(device, &root_signature_desc, &root_signature); - todo ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); - if (SUCCEEDED(hr)) - ID3D12RootSignature_Release(root_signature); + ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); refcount = ID3D12Device_Release(device); ok(!refcount, "ID3D12Device has %u references left.\n", (unsigned int)refcount); @@ -34925,7 +34921,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; @@ -34999,6 +34994,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); @@ -35088,7 +35084,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; @@ -35146,6 +35141,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);