mesa/src/virtio/vulkan/vn_descriptor_set.c

1058 lines
37 KiB
C

/*
* Copyright 2019 Google LLC
* SPDX-License-Identifier: MIT
*
* based in part on anv and radv which are:
* Copyright © 2015 Intel Corporation
* Copyright © 2016 Red Hat.
* Copyright © 2016 Bas Nieuwenhuizen
*/
#include "vn_descriptor_set.h"
#include "venus-protocol/vn_protocol_driver_descriptor_pool.h"
#include "venus-protocol/vn_protocol_driver_descriptor_set.h"
#include "venus-protocol/vn_protocol_driver_descriptor_set_layout.h"
#include "venus-protocol/vn_protocol_driver_descriptor_update_template.h"
#include "vn_device.h"
static void
vn_descriptor_set_layout_destroy(struct vn_device *dev,
struct vn_descriptor_set_layout *layout)
{
VkDevice dev_handle = vn_device_to_handle(dev);
VkDescriptorSetLayout layout_handle =
vn_descriptor_set_layout_to_handle(layout);
const VkAllocationCallbacks *alloc = &dev->base.base.alloc;
vn_async_vkDestroyDescriptorSetLayout(dev->instance, dev_handle,
layout_handle, NULL);
vn_object_base_fini(&layout->base);
vk_free(alloc, layout);
}
static inline struct vn_descriptor_set_layout *
vn_descriptor_set_layout_ref(struct vn_device *dev,
struct vn_descriptor_set_layout *layout)
{
vn_refcount_inc(&layout->refcount);
return layout;
}
static inline void
vn_descriptor_set_layout_unref(struct vn_device *dev,
struct vn_descriptor_set_layout *layout)
{
if (vn_refcount_dec(&layout->refcount))
vn_descriptor_set_layout_destroy(dev, layout);
}
static void
vn_descriptor_set_destroy(struct vn_device *dev,
struct vn_descriptor_set *set,
const VkAllocationCallbacks *alloc)
{
list_del(&set->head);
vn_descriptor_set_layout_unref(dev, set->layout);
vn_object_base_fini(&set->base);
vk_free(alloc, set);
}
/* Mapping VkDescriptorType to array index */
static enum vn_descriptor_type
vn_descriptor_type_index(VkDescriptorType type)
{
switch (type) {
case VK_DESCRIPTOR_TYPE_SAMPLER:
return VN_DESCRIPTOR_TYPE_SAMPLER;
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
return VN_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
return VN_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
return VN_DESCRIPTOR_TYPE_STORAGE_IMAGE;
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
return VN_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
return VN_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
return VN_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
return VN_DESCRIPTOR_TYPE_STORAGE_BUFFER;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
return VN_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
return VN_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
return VN_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK:
return VN_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK;
default:
break;
}
unreachable("bad VkDescriptorType");
}
/* descriptor set layout commands */
void
vn_GetDescriptorSetLayoutSupport(
VkDevice device,
const VkDescriptorSetLayoutCreateInfo *pCreateInfo,
VkDescriptorSetLayoutSupport *pSupport)
{
struct vn_device *dev = vn_device_from_handle(device);
/* TODO per-device cache */
vn_call_vkGetDescriptorSetLayoutSupport(dev->instance, device, pCreateInfo,
pSupport);
}
static void
vn_descriptor_set_layout_init(
struct vn_device *dev,
const VkDescriptorSetLayoutCreateInfo *create_info,
uint32_t last_binding,
struct vn_descriptor_set_layout *layout)
{
VkDevice dev_handle = vn_device_to_handle(dev);
VkDescriptorSetLayout layout_handle =
vn_descriptor_set_layout_to_handle(layout);
const VkDescriptorSetLayoutBindingFlagsCreateInfo *binding_flags =
vk_find_struct_const(create_info->pNext,
DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO);
/* 14.2.1. Descriptor Set Layout
*
* If bindingCount is zero or if this structure is not included in
* the pNext chain, the VkDescriptorBindingFlags for each descriptor
* set layout binding is considered to be zero.
*/
if (binding_flags && !binding_flags->bindingCount)
binding_flags = NULL;
layout->refcount = VN_REFCOUNT_INIT(1);
layout->last_binding = last_binding;
for (uint32_t i = 0; i < create_info->bindingCount; i++) {
const VkDescriptorSetLayoutBinding *binding_info =
&create_info->pBindings[i];
struct vn_descriptor_set_layout_binding *binding =
&layout->bindings[binding_info->binding];
if (binding_info->binding == last_binding) {
/* 14.2.1. Descriptor Set Layout
*
* VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT must only be
* used for the last binding in the descriptor set layout (i.e. the
* binding with the largest value of binding).
*
* 41. Features
*
* descriptorBindingVariableDescriptorCount indicates whether the
* implementation supports descriptor sets with a variable-sized last
* binding. If this feature is not enabled,
* VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT must not be
* used.
*/
layout->has_variable_descriptor_count =
binding_flags &&
(binding_flags->pBindingFlags[i] &
VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT);
}
binding->type = binding_info->descriptorType;
binding->count = binding_info->descriptorCount;
switch (binding_info->descriptorType) {
case VK_DESCRIPTOR_TYPE_SAMPLER:
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
binding->has_immutable_samplers = binding_info->pImmutableSamplers;
break;
default:
break;
}
}
vn_async_vkCreateDescriptorSetLayout(dev->instance, dev_handle,
create_info, NULL, &layout_handle);
}
VkResult
vn_CreateDescriptorSetLayout(
VkDevice device,
const VkDescriptorSetLayoutCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkDescriptorSetLayout *pSetLayout)
{
struct vn_device *dev = vn_device_from_handle(device);
/* ignore pAllocator as the layout is reference-counted */
const VkAllocationCallbacks *alloc = &dev->base.base.alloc;
uint32_t last_binding = 0;
VkDescriptorSetLayoutBinding *local_bindings = NULL;
VkDescriptorSetLayoutCreateInfo local_create_info;
if (pCreateInfo->bindingCount) {
/* the encoder does not ignore
* VkDescriptorSetLayoutBinding::pImmutableSamplers when it should
*/
const size_t binding_size =
sizeof(*pCreateInfo->pBindings) * pCreateInfo->bindingCount;
local_bindings = vk_alloc(alloc, binding_size, VN_DEFAULT_ALIGN,
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
if (!local_bindings)
return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
memcpy(local_bindings, pCreateInfo->pBindings, binding_size);
for (uint32_t i = 0; i < pCreateInfo->bindingCount; i++) {
VkDescriptorSetLayoutBinding *binding = &local_bindings[i];
if (last_binding < binding->binding)
last_binding = binding->binding;
switch (binding->descriptorType) {
case VK_DESCRIPTOR_TYPE_SAMPLER:
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
break;
default:
binding->pImmutableSamplers = NULL;
break;
}
}
local_create_info = *pCreateInfo;
local_create_info.pBindings = local_bindings;
pCreateInfo = &local_create_info;
}
const size_t layout_size =
offsetof(struct vn_descriptor_set_layout, bindings[last_binding + 1]);
/* allocated with the device scope */
struct vn_descriptor_set_layout *layout =
vk_zalloc(alloc, layout_size, VN_DEFAULT_ALIGN,
VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
if (!layout) {
vk_free(alloc, local_bindings);
return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
}
vn_object_base_init(&layout->base, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT,
&dev->base);
vn_descriptor_set_layout_init(dev, pCreateInfo, last_binding, layout);
vk_free(alloc, local_bindings);
*pSetLayout = vn_descriptor_set_layout_to_handle(layout);
return VK_SUCCESS;
}
void
vn_DestroyDescriptorSetLayout(VkDevice device,
VkDescriptorSetLayout descriptorSetLayout,
const VkAllocationCallbacks *pAllocator)
{
struct vn_device *dev = vn_device_from_handle(device);
struct vn_descriptor_set_layout *layout =
vn_descriptor_set_layout_from_handle(descriptorSetLayout);
if (!layout)
return;
vn_descriptor_set_layout_unref(dev, layout);
}
/* descriptor pool commands */
VkResult
vn_CreateDescriptorPool(VkDevice device,
const VkDescriptorPoolCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkDescriptorPool *pDescriptorPool)
{
VN_TRACE_FUNC();
struct vn_device *dev = vn_device_from_handle(device);
const VkAllocationCallbacks *alloc =
pAllocator ? pAllocator : &dev->base.base.alloc;
const VkDescriptorPoolInlineUniformBlockCreateInfo *iub_info =
vk_find_struct_const(pCreateInfo->pNext,
DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO);
struct vn_descriptor_pool *pool =
vk_zalloc(alloc, sizeof(*pool), VN_DEFAULT_ALIGN,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (!pool)
return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
vn_object_base_init(&pool->base, VK_OBJECT_TYPE_DESCRIPTOR_POOL,
&dev->base);
pool->allocator = *alloc;
/* Without VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, the set
* allocation must not fail due to a fragmented pool per spec. In this
* case, set allocation can be asynchronous with pool resource tracking.
*/
pool->async_set_allocation =
!VN_PERF(NO_ASYNC_SET_ALLOC) &&
!(pCreateInfo->flags &
VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT);
pool->max.set_count = pCreateInfo->maxSets;
if (iub_info)
pool->max.iub_binding_count = iub_info->maxInlineUniformBlockBindings;
for (uint32_t i = 0; i < pCreateInfo->poolSizeCount; i++) {
const VkDescriptorPoolSize *pool_size = &pCreateInfo->pPoolSizes[i];
const uint32_t type_index = vn_descriptor_type_index(pool_size->type);
assert(type_index < VN_NUM_DESCRIPTOR_TYPES);
pool->max.descriptor_counts[type_index] += pool_size->descriptorCount;
assert((pCreateInfo->pPoolSizes[i].type !=
VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) ||
iub_info);
}
list_inithead(&pool->descriptor_sets);
VkDescriptorPool pool_handle = vn_descriptor_pool_to_handle(pool);
vn_async_vkCreateDescriptorPool(dev->instance, device, pCreateInfo, NULL,
&pool_handle);
*pDescriptorPool = pool_handle;
return VK_SUCCESS;
}
void
vn_DestroyDescriptorPool(VkDevice device,
VkDescriptorPool descriptorPool,
const VkAllocationCallbacks *pAllocator)
{
VN_TRACE_FUNC();
struct vn_device *dev = vn_device_from_handle(device);
struct vn_descriptor_pool *pool =
vn_descriptor_pool_from_handle(descriptorPool);
const VkAllocationCallbacks *alloc;
if (!pool)
return;
alloc = pAllocator ? pAllocator : &pool->allocator;
/* We must emit vkDestroyDescriptorPool before freeing the sets in
* pool->descriptor_sets. Otherwise, another thread might reuse their
* object ids while they still refer to the sets in the renderer.
*/
vn_async_vkDestroyDescriptorPool(dev->instance, device, descriptorPool,
NULL);
list_for_each_entry_safe(struct vn_descriptor_set, set,
&pool->descriptor_sets, head)
vn_descriptor_set_destroy(dev, set, alloc);
vn_object_base_fini(&pool->base);
vk_free(alloc, pool);
}
static bool
vn_descriptor_pool_alloc_descriptors(
struct vn_descriptor_pool *pool,
const struct vn_descriptor_set_layout *layout,
uint32_t last_binding_descriptor_count)
{
struct vn_descriptor_pool_state recovery;
if (!pool->async_set_allocation)
return true;
if (pool->used.set_count == pool->max.set_count)
return false;
/* backup current pool state to recovery */
recovery = pool->used;
++pool->used.set_count;
for (uint32_t i = 0; i <= layout->last_binding; i++) {
const VkDescriptorType type = layout->bindings[i].type;
const uint32_t count = i == layout->last_binding
? last_binding_descriptor_count
: layout->bindings[i].count;
/* Allocation may fail if a call to vkAllocateDescriptorSets would cause
* the total number of inline uniform block bindings allocated from the
* pool to exceed the value of
* VkDescriptorPoolInlineUniformBlockCreateInfo::maxInlineUniformBlockBindings
* used to create the descriptor pool.
*/
if (type == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK) {
if (++pool->used.iub_binding_count > pool->max.iub_binding_count) {
/* restore pool state before this allocation */
pool->used = recovery;
return false;
}
}
const uint32_t type_index = vn_descriptor_type_index(type);
pool->used.descriptor_counts[type_index] += count;
if (pool->used.descriptor_counts[type_index] >
pool->max.descriptor_counts[type_index]) {
/* restore pool state before this allocation */
pool->used = recovery;
return false;
}
}
return true;
}
static void
vn_descriptor_pool_free_descriptors(
struct vn_descriptor_pool *pool,
const struct vn_descriptor_set_layout *layout,
uint32_t last_binding_descriptor_count)
{
if (!pool->async_set_allocation)
return;
for (uint32_t i = 0; i <= layout->last_binding; i++) {
const uint32_t count = i == layout->last_binding
? last_binding_descriptor_count
: layout->bindings[i].count;
pool->used.descriptor_counts[vn_descriptor_type_index(
layout->bindings[i].type)] -= count;
if (layout->bindings[i].type == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK)
--pool->used.iub_binding_count;
}
--pool->used.set_count;
}
static void
vn_descriptor_pool_reset_descriptors(struct vn_descriptor_pool *pool)
{
if (!pool->async_set_allocation)
return;
memset(&pool->used, 0, sizeof(pool->used));
}
VkResult
vn_ResetDescriptorPool(VkDevice device,
VkDescriptorPool descriptorPool,
VkDescriptorPoolResetFlags flags)
{
VN_TRACE_FUNC();
struct vn_device *dev = vn_device_from_handle(device);
struct vn_descriptor_pool *pool =
vn_descriptor_pool_from_handle(descriptorPool);
const VkAllocationCallbacks *alloc = &pool->allocator;
vn_async_vkResetDescriptorPool(dev->instance, device, descriptorPool,
flags);
list_for_each_entry_safe(struct vn_descriptor_set, set,
&pool->descriptor_sets, head)
vn_descriptor_set_destroy(dev, set, alloc);
vn_descriptor_pool_reset_descriptors(pool);
return VK_SUCCESS;
}
/* descriptor set commands */
VkResult
vn_AllocateDescriptorSets(VkDevice device,
const VkDescriptorSetAllocateInfo *pAllocateInfo,
VkDescriptorSet *pDescriptorSets)
{
VN_TRACE_FUNC();
struct vn_device *dev = vn_device_from_handle(device);
struct vn_descriptor_pool *pool =
vn_descriptor_pool_from_handle(pAllocateInfo->descriptorPool);
const VkAllocationCallbacks *alloc = &pool->allocator;
const VkDescriptorSetVariableDescriptorCountAllocateInfo *variable_info =
NULL;
VkResult result;
/* 14.2.3. Allocation of Descriptor Sets
*
* If descriptorSetCount is zero or this structure is not included in
* the pNext chain, then the variable lengths are considered to be zero.
*/
variable_info = vk_find_struct_const(
pAllocateInfo->pNext,
DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO);
if (variable_info && !variable_info->descriptorSetCount)
variable_info = NULL;
for (uint32_t i = 0; i < pAllocateInfo->descriptorSetCount; i++) {
struct vn_descriptor_set_layout *layout =
vn_descriptor_set_layout_from_handle(pAllocateInfo->pSetLayouts[i]);
uint32_t last_binding_descriptor_count = 0;
struct vn_descriptor_set *set = NULL;
/* 14.2.3. Allocation of Descriptor Sets
*
* If VkDescriptorSetAllocateInfo::pSetLayouts[i] does not include a
* variable count descriptor binding, then pDescriptorCounts[i] is
* ignored.
*/
if (!layout->has_variable_descriptor_count) {
last_binding_descriptor_count =
layout->bindings[layout->last_binding].count;
} else if (variable_info) {
last_binding_descriptor_count = variable_info->pDescriptorCounts[i];
}
if (!vn_descriptor_pool_alloc_descriptors(
pool, layout, last_binding_descriptor_count)) {
pDescriptorSets[i] = VK_NULL_HANDLE;
result = VK_ERROR_OUT_OF_POOL_MEMORY;
goto fail;
}
set = vk_zalloc(alloc, sizeof(*set), VN_DEFAULT_ALIGN,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (!set) {
vn_descriptor_pool_free_descriptors(pool, layout,
last_binding_descriptor_count);
pDescriptorSets[i] = VK_NULL_HANDLE;
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail;
}
vn_object_base_init(&set->base, VK_OBJECT_TYPE_DESCRIPTOR_SET,
&dev->base);
/* We might reorder vkCmdBindDescriptorSets after
* vkDestroyDescriptorSetLayout due to batching. The spec says
*
* VkDescriptorSetLayout objects may be accessed by commands that
* operate on descriptor sets allocated using that layout, and those
* descriptor sets must not be updated with vkUpdateDescriptorSets
* after the descriptor set layout has been destroyed. Otherwise, a
* VkDescriptorSetLayout object passed as a parameter to create
* another object is not further accessed by that object after the
* duration of the command it is passed into.
*
* It is ambiguous but the reordering is likely invalid. Let's keep the
* layout alive with the set to defer vkDestroyDescriptorSetLayout.
*/
set->layout = vn_descriptor_set_layout_ref(dev, layout);
set->last_binding_descriptor_count = last_binding_descriptor_count;
list_addtail(&set->head, &pool->descriptor_sets);
VkDescriptorSet set_handle = vn_descriptor_set_to_handle(set);
pDescriptorSets[i] = set_handle;
}
if (pool->async_set_allocation) {
vn_async_vkAllocateDescriptorSets(dev->instance, device, pAllocateInfo,
pDescriptorSets);
} else {
result = vn_call_vkAllocateDescriptorSets(
dev->instance, device, pAllocateInfo, pDescriptorSets);
if (result != VK_SUCCESS)
goto fail;
}
return VK_SUCCESS;
fail:
for (uint32_t i = 0; i < pAllocateInfo->descriptorSetCount; i++) {
struct vn_descriptor_set *set =
vn_descriptor_set_from_handle(pDescriptorSets[i]);
if (!set)
break;
vn_descriptor_pool_free_descriptors(pool, set->layout,
set->last_binding_descriptor_count);
vn_descriptor_set_destroy(dev, set, alloc);
}
memset(pDescriptorSets, 0,
sizeof(*pDescriptorSets) * pAllocateInfo->descriptorSetCount);
return vn_error(dev->instance, result);
}
VkResult
vn_FreeDescriptorSets(VkDevice device,
VkDescriptorPool descriptorPool,
uint32_t descriptorSetCount,
const VkDescriptorSet *pDescriptorSets)
{
VN_TRACE_FUNC();
struct vn_device *dev = vn_device_from_handle(device);
struct vn_descriptor_pool *pool =
vn_descriptor_pool_from_handle(descriptorPool);
const VkAllocationCallbacks *alloc = &pool->allocator;
vn_async_vkFreeDescriptorSets(dev->instance, device, descriptorPool,
descriptorSetCount, pDescriptorSets);
for (uint32_t i = 0; i < descriptorSetCount; i++) {
struct vn_descriptor_set *set =
vn_descriptor_set_from_handle(pDescriptorSets[i]);
if (!set)
continue;
vn_descriptor_set_destroy(dev, set, alloc);
}
return VK_SUCCESS;
}
static struct vn_update_descriptor_sets *
vn_update_descriptor_sets_alloc(uint32_t write_count,
uint32_t image_count,
uint32_t buffer_count,
uint32_t view_count,
uint32_t iub_count,
const VkAllocationCallbacks *alloc,
VkSystemAllocationScope scope)
{
const size_t writes_offset = sizeof(struct vn_update_descriptor_sets);
const size_t images_offset =
writes_offset + sizeof(VkWriteDescriptorSet) * write_count;
const size_t buffers_offset =
images_offset + sizeof(VkDescriptorImageInfo) * image_count;
const size_t views_offset =
buffers_offset + sizeof(VkDescriptorBufferInfo) * buffer_count;
const size_t iubs_offset =
views_offset + sizeof(VkBufferView) * view_count;
const size_t alloc_size =
iubs_offset +
sizeof(VkWriteDescriptorSetInlineUniformBlock) * iub_count;
void *storage = vk_alloc(alloc, alloc_size, VN_DEFAULT_ALIGN, scope);
if (!storage)
return NULL;
struct vn_update_descriptor_sets *update = storage;
update->write_count = write_count;
update->writes = storage + writes_offset;
update->images = storage + images_offset;
update->buffers = storage + buffers_offset;
update->views = storage + views_offset;
update->iubs = storage + iubs_offset;
return update;
}
static struct vn_update_descriptor_sets *
vn_update_descriptor_sets_parse_writes(uint32_t write_count,
const VkWriteDescriptorSet *writes,
const VkAllocationCallbacks *alloc)
{
uint32_t img_count = 0;
for (uint32_t i = 0; i < write_count; i++) {
const VkWriteDescriptorSet *write = &writes[i];
switch (write->descriptorType) {
case VK_DESCRIPTOR_TYPE_SAMPLER:
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
img_count += write->descriptorCount;
break;
default:
break;
}
}
struct vn_update_descriptor_sets *update =
vn_update_descriptor_sets_alloc(write_count, img_count, 0, 0, 0, alloc,
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
if (!update)
return NULL;
/* the encoder does not ignore
* VkWriteDescriptorSet::{pImageInfo,pBufferInfo,pTexelBufferView} when it
* should
*
* TODO make the encoder smarter
*/
memcpy(update->writes, writes, sizeof(*writes) * write_count);
img_count = 0;
for (uint32_t i = 0; i < write_count; i++) {
const struct vn_descriptor_set *set =
vn_descriptor_set_from_handle(writes[i].dstSet);
const struct vn_descriptor_set_layout_binding *binding =
&set->layout->bindings[writes[i].dstBinding];
VkWriteDescriptorSet *write = &update->writes[i];
VkDescriptorImageInfo *imgs = &update->images[img_count];
switch (write->descriptorType) {
case VK_DESCRIPTOR_TYPE_SAMPLER:
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
memcpy(imgs, write->pImageInfo,
sizeof(*imgs) * write->descriptorCount);
img_count += write->descriptorCount;
for (uint32_t j = 0; j < write->descriptorCount; j++) {
switch (write->descriptorType) {
case VK_DESCRIPTOR_TYPE_SAMPLER:
imgs[j].imageView = VK_NULL_HANDLE;
break;
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
if (binding->has_immutable_samplers)
imgs[j].sampler = VK_NULL_HANDLE;
break;
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
imgs[j].sampler = VK_NULL_HANDLE;
break;
default:
break;
}
}
write->pImageInfo = imgs;
write->pBufferInfo = NULL;
write->pTexelBufferView = NULL;
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
write->pImageInfo = NULL;
write->pBufferInfo = NULL;
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
write->pImageInfo = NULL;
write->pTexelBufferView = NULL;
break;
case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK:
default:
write->pImageInfo = NULL;
write->pBufferInfo = NULL;
write->pTexelBufferView = NULL;
break;
}
}
return update;
}
void
vn_UpdateDescriptorSets(VkDevice device,
uint32_t descriptorWriteCount,
const VkWriteDescriptorSet *pDescriptorWrites,
uint32_t descriptorCopyCount,
const VkCopyDescriptorSet *pDescriptorCopies)
{
VN_TRACE_FUNC();
struct vn_device *dev = vn_device_from_handle(device);
const VkAllocationCallbacks *alloc = &dev->base.base.alloc;
struct vn_update_descriptor_sets *update =
vn_update_descriptor_sets_parse_writes(descriptorWriteCount,
pDescriptorWrites, alloc);
if (!update) {
/* TODO update one-by-one? */
vn_log(dev->instance, "TODO descriptor set update ignored due to OOM");
return;
}
vn_async_vkUpdateDescriptorSets(dev->instance, device, update->write_count,
update->writes, descriptorCopyCount,
pDescriptorCopies);
vk_free(alloc, update);
}
/* descriptor update template commands */
static struct vn_update_descriptor_sets *
vn_update_descriptor_sets_parse_template(
const VkDescriptorUpdateTemplateCreateInfo *create_info,
const VkAllocationCallbacks *alloc,
struct vn_descriptor_update_template_entry *entries)
{
uint32_t img_count = 0;
uint32_t buf_count = 0;
uint32_t view_count = 0;
uint32_t iub_count = 0;
for (uint32_t i = 0; i < create_info->descriptorUpdateEntryCount; i++) {
const VkDescriptorUpdateTemplateEntry *entry =
&create_info->pDescriptorUpdateEntries[i];
switch (entry->descriptorType) {
case VK_DESCRIPTOR_TYPE_SAMPLER:
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
img_count += entry->descriptorCount;
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
view_count += entry->descriptorCount;
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
buf_count += entry->descriptorCount;
break;
case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK:
iub_count += 1;
break;
default:
unreachable("unhandled descriptor type");
break;
}
}
struct vn_update_descriptor_sets *update = vn_update_descriptor_sets_alloc(
create_info->descriptorUpdateEntryCount, img_count, buf_count,
view_count, iub_count, alloc, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (!update)
return NULL;
img_count = 0;
buf_count = 0;
view_count = 0;
iub_count = 0;
for (uint32_t i = 0; i < create_info->descriptorUpdateEntryCount; i++) {
const VkDescriptorUpdateTemplateEntry *entry =
&create_info->pDescriptorUpdateEntries[i];
VkWriteDescriptorSet *write = &update->writes[i];
write->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write->pNext = NULL;
write->dstBinding = entry->dstBinding;
write->dstArrayElement = entry->dstArrayElement;
write->descriptorCount = entry->descriptorCount;
write->descriptorType = entry->descriptorType;
entries[i].offset = entry->offset;
entries[i].stride = entry->stride;
switch (entry->descriptorType) {
case VK_DESCRIPTOR_TYPE_SAMPLER:
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
write->pImageInfo = &update->images[img_count];
write->pBufferInfo = NULL;
write->pTexelBufferView = NULL;
img_count += entry->descriptorCount;
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
write->pImageInfo = NULL;
write->pBufferInfo = NULL;
write->pTexelBufferView = &update->views[view_count];
view_count += entry->descriptorCount;
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
write->pImageInfo = NULL;
write->pBufferInfo = &update->buffers[buf_count];
write->pTexelBufferView = NULL;
buf_count += entry->descriptorCount;
break;
case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK:
write->pImageInfo = NULL;
write->pBufferInfo = NULL;
write->pTexelBufferView = NULL;
VkWriteDescriptorSetInlineUniformBlock *iub_data =
&update->iubs[iub_count];
iub_data->sType =
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK;
iub_data->pNext = write->pNext;
iub_data->dataSize = entry->descriptorCount;
write->pNext = iub_data;
iub_count += 1;
break;
default:
break;
}
}
return update;
}
VkResult
vn_CreateDescriptorUpdateTemplate(
VkDevice device,
const VkDescriptorUpdateTemplateCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkDescriptorUpdateTemplate *pDescriptorUpdateTemplate)
{
VN_TRACE_FUNC();
struct vn_device *dev = vn_device_from_handle(device);
const VkAllocationCallbacks *alloc =
pAllocator ? pAllocator : &dev->base.base.alloc;
const size_t templ_size =
offsetof(struct vn_descriptor_update_template,
entries[pCreateInfo->descriptorUpdateEntryCount + 1]);
struct vn_descriptor_update_template *templ = vk_zalloc(
alloc, templ_size, VN_DEFAULT_ALIGN, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (!templ)
return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
vn_object_base_init(&templ->base,
VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE, &dev->base);
templ->update = vn_update_descriptor_sets_parse_template(
pCreateInfo, alloc, templ->entries);
if (!templ->update) {
vk_free(alloc, templ);
return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY);
}
mtx_init(&templ->mutex, mtx_plain);
/* no host object */
VkDescriptorUpdateTemplate templ_handle =
vn_descriptor_update_template_to_handle(templ);
*pDescriptorUpdateTemplate = templ_handle;
return VK_SUCCESS;
}
void
vn_DestroyDescriptorUpdateTemplate(
VkDevice device,
VkDescriptorUpdateTemplate descriptorUpdateTemplate,
const VkAllocationCallbacks *pAllocator)
{
VN_TRACE_FUNC();
struct vn_device *dev = vn_device_from_handle(device);
struct vn_descriptor_update_template *templ =
vn_descriptor_update_template_from_handle(descriptorUpdateTemplate);
const VkAllocationCallbacks *alloc =
pAllocator ? pAllocator : &dev->base.base.alloc;
if (!templ)
return;
/* no host object */
vk_free(alloc, templ->update);
mtx_destroy(&templ->mutex);
vn_object_base_fini(&templ->base);
vk_free(alloc, templ);
}
void
vn_UpdateDescriptorSetWithTemplate(
VkDevice device,
VkDescriptorSet descriptorSet,
VkDescriptorUpdateTemplate descriptorUpdateTemplate,
const void *pData)
{
VN_TRACE_FUNC();
struct vn_device *dev = vn_device_from_handle(device);
struct vn_descriptor_set *set =
vn_descriptor_set_from_handle(descriptorSet);
struct vn_descriptor_update_template *templ =
vn_descriptor_update_template_from_handle(descriptorUpdateTemplate);
struct vn_update_descriptor_sets *update = templ->update;
/* duplicate update instead to avoid locking? */
mtx_lock(&templ->mutex);
for (uint32_t i = 0; i < update->write_count; i++) {
const struct vn_descriptor_update_template_entry *entry =
&templ->entries[i];
const struct vn_descriptor_set_layout_binding *binding =
&set->layout->bindings[update->writes[i].dstBinding];
VkWriteDescriptorSet *write = &update->writes[i];
write->dstSet = vn_descriptor_set_to_handle(set);
switch (write->descriptorType) {
case VK_DESCRIPTOR_TYPE_SAMPLER:
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
for (uint32_t j = 0; j < write->descriptorCount; j++) {
const bool need_sampler =
(write->descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER ||
write->descriptorType ==
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) &&
!binding->has_immutable_samplers;
const bool need_view =
write->descriptorType != VK_DESCRIPTOR_TYPE_SAMPLER;
const VkDescriptorImageInfo *src =
pData + entry->offset + entry->stride * j;
VkDescriptorImageInfo *dst =
(VkDescriptorImageInfo *)&write->pImageInfo[j];
dst->sampler = need_sampler ? src->sampler : VK_NULL_HANDLE;
dst->imageView = need_view ? src->imageView : VK_NULL_HANDLE;
dst->imageLayout = src->imageLayout;
}
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
for (uint32_t j = 0; j < write->descriptorCount; j++) {
const VkBufferView *src =
pData + entry->offset + entry->stride * j;
VkBufferView *dst = (VkBufferView *)&write->pTexelBufferView[j];
*dst = *src;
}
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
for (uint32_t j = 0; j < write->descriptorCount; j++) {
const VkDescriptorBufferInfo *src =
pData + entry->offset + entry->stride * j;
VkDescriptorBufferInfo *dst =
(VkDescriptorBufferInfo *)&write->pBufferInfo[j];
*dst = *src;
}
break;
case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK:;
VkWriteDescriptorSetInlineUniformBlock *iub_data =
(VkWriteDescriptorSetInlineUniformBlock *)vk_find_struct_const(
write->pNext, WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK);
iub_data->pData = pData + entry->offset;
break;
default:
unreachable("unhandled descriptor type");
break;
}
}
vn_async_vkUpdateDescriptorSets(dev->instance, device, update->write_count,
update->writes, 0, NULL);
mtx_unlock(&templ->mutex);
}