/* * Copyright © 2022 Imagination Technologies Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include #include #include "hwdef/rogue_hw_utils.h" #include "pvr_bo.h" #include "pvr_private.h" #include "util/compiler.h" #include "util/list.h" #include "util/log.h" #include "util/macros.h" #include "vk_alloc.h" #include "vk_log.h" #include "vk_object.h" #include "vk_util.h" #if defined(DEBUG) static const struct { const char *raw; const char *primary; const char *secondary; const char *primary_dynamic; const char *secondary_dynamic; } stage_names[] = { { "Vertex", "Vertex Primary", "Vertex Secondary", "Vertex Dynamic Primary", "Vertex Dynamic Secondary" }, { "Fragment", "Fragment Primary", "Fragment Secondary", "Fragment Dynamic Primary", "Fragment Dynamic Secondary" }, { "Compute", "Compute Primary", "Compute Secondary", "Compute Dynamic Primary", "Compute Dynamic Secondary" }, }; static const char *descriptor_names[] = { "VK SAMPLER", "VK COMBINED_IMAGE_SAMPLER", "VK SAMPLED_IMAGE", "VK STORAGE_IMAGE", "VK UNIFORM_TEXEL_BUFFER", "VK STORAGE_TEXEL_BUFFER", "VK UNIFORM_BUFFER", "VK STORAGE_BUFFER", "VK UNIFORM_BUFFER_DYNAMIC", "VK STORAGE_BUFFER_DYNAMIC", "VK INPUT_ATTACHMENT" }; #endif static void pvr_descriptor_size_info_init( const struct pvr_device *device, VkDescriptorType type, struct pvr_descriptor_size_info *const size_info_out) { /* UINT_MAX is a place holder. These values will be filled by calling the * init function, and set appropriately based on device features. */ static const struct pvr_descriptor_size_info template_size_infos[] = { /* VK_DESCRIPTOR_TYPE_SAMPLER */ { 4, 0, 4 }, /* VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER */ { 8, UINT_MAX, 4 }, /* VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE */ { 4, UINT_MAX, 4 }, /* VK_DESCRIPTOR_TYPE_STORAGE_IMAGE */ { 4, UINT_MAX, 4 }, /* VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER */ { 4, UINT_MAX, 4 }, /* VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER */ { 4, UINT_MAX, 4 }, /* VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER */ { 2, UINT_MAX, 2 }, /* VK_DESCRIPTOR_TYPE_STORAGE_BUFFER */ { 2, 1, 2 }, /* VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC */ { 2, UINT_MAX, 2 }, /* VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC */ { 2, 1, 2 }, /* VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT */ { 8, UINT_MAX, 4 } }; *size_info_out = template_size_infos[type]; switch (type) { case VK_DESCRIPTOR_TYPE_SAMPLER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: break; case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { const uint32_t image_secondary_offset_arraybase = 0; const uint32_t image_secondary_size_arraybase = 2; const uint32_t image_secondary_size_arraystride = 1; const uint32_t image_secondary_offset_arraystride = image_secondary_offset_arraybase + image_secondary_size_arraybase; const uint32_t image_secondary_offset_arraymaxindex = (PVR_HAS_FEATURE(&device->pdevice->dev_info, tpu_array_textures)) ? 0 : image_secondary_offset_arraystride + image_secondary_size_arraystride; const uint32_t image_secondary_size_arraymaxindex = 1; const uint32_t image_secondary_size_width = 1; const uint32_t image_secondary_size_height = 1; const uint32_t image_secondary_size_depth = 1; const uint32_t image_secondary_offset_width = image_secondary_offset_arraymaxindex + image_secondary_size_arraymaxindex; const uint32_t image_secondary_offset_height = image_secondary_offset_width + image_secondary_size_width; const uint32_t image_secondary_offset_depth = image_secondary_offset_height + image_secondary_size_height; const uint32_t image_secondary_total_size = image_secondary_offset_depth + image_secondary_size_depth; size_info_out->secondary = image_secondary_total_size; break; } case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: size_info_out->secondary = (uint32_t)device->features.robustBufferAccess; break; default: unreachable("Unknown descriptor type"); } } static bool pvr_stage_matches_vk_flags(enum pvr_stage_allocation pvr_stage, VkShaderStageFlags flags) { VkShaderStageFlags flags_per_stage; switch (pvr_stage) { case PVR_STAGE_ALLOCATION_VERTEX_GEOMETRY: flags_per_stage = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT; break; case PVR_STAGE_ALLOCATION_FRAGMENT: flags_per_stage = VK_SHADER_STAGE_FRAGMENT_BIT; break; case PVR_STAGE_ALLOCATION_COMPUTE: flags_per_stage = VK_SHADER_STAGE_COMPUTE_BIT; break; default: unreachable("Unrecognized allocation stage."); } return !!(flags_per_stage & flags); } /* If allocator == NULL, the internal one will be used. */ static struct pvr_descriptor_set_layout * pvr_descriptor_set_layout_allocate(struct pvr_device *device, const VkAllocationCallbacks *allocator, uint32_t binding_count, uint32_t immutable_sampler_count, uint32_t supported_descriptors_count) { struct pvr_descriptor_set_layout_binding *bindings; struct pvr_descriptor_set_layout *layout; __typeof__(layout->per_stage_descriptor_count) counts; const struct pvr_sampler **immutable_samplers; VK_MULTIALLOC(ma); vk_multialloc_add(&ma, &layout, __typeof__(*layout), 1); vk_multialloc_add(&ma, &bindings, __typeof__(*bindings), binding_count); vk_multialloc_add(&ma, &immutable_samplers, __typeof__(*immutable_samplers), immutable_sampler_count); for (uint32_t stage = 0; stage < ARRAY_SIZE(counts); stage++) { vk_multialloc_add(&ma, &counts[stage], __typeof__(*counts[0]), supported_descriptors_count); } /* pvr_CreateDescriptorSetLayout() relies on this being zero allocated. */ if (!vk_multialloc_zalloc2(&ma, &device->vk.alloc, allocator, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT)) { return NULL; } layout->bindings = bindings; layout->immutable_samplers = immutable_samplers; memcpy(&layout->per_stage_descriptor_count, &counts, sizeof(counts)); vk_object_base_init(&device->vk, &layout->base, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT); return layout; } /* If allocator == NULL, the internal one will be used. */ static void pvr_descriptor_set_layout_free(struct pvr_device *device, const VkAllocationCallbacks *allocator, struct pvr_descriptor_set_layout *layout) { vk_object_base_finish(&layout->base); vk_free2(&device->vk.alloc, allocator, layout); } static int pvr_binding_compare(const void *a, const void *b) { uint32_t binding_a = ((VkDescriptorSetLayoutBinding *)a)->binding; uint32_t binding_b = ((VkDescriptorSetLayoutBinding *)b)->binding; if (binding_a < binding_b) return -1; if (binding_a > binding_b) return 1; return 0; } /* If allocator == NULL, the internal one will be used. */ static VkDescriptorSetLayoutBinding * pvr_create_sorted_bindings(struct pvr_device *device, const VkAllocationCallbacks *allocator, const VkDescriptorSetLayoutBinding *bindings, uint32_t binding_count) { VkDescriptorSetLayoutBinding *sorted_bindings = vk_alloc2(&device->vk.alloc, allocator, binding_count * sizeof(*sorted_bindings), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); if (!sorted_bindings) return NULL; memcpy(sorted_bindings, bindings, binding_count * sizeof(*sorted_bindings)); qsort(sorted_bindings, binding_count, sizeof(*sorted_bindings), pvr_binding_compare); return sorted_bindings; } struct pvr_register_usage { uint32_t primary; uint32_t primary_dynamic; uint32_t secondary; uint32_t secondary_dynamic; }; static void pvr_setup_in_memory_layout_sizes( struct pvr_descriptor_set_layout *layout, const struct pvr_register_usage reg_usage[PVR_STAGE_ALLOCATION_COUNT]) { for (uint32_t stage = 0; stage < ARRAY_SIZE(layout->memory_layout_in_dwords_per_stage); stage++) { layout->total_size_in_dwords = ALIGN_POT(layout->total_size_in_dwords, 4); layout->memory_layout_in_dwords_per_stage[stage].primary_offset = layout->total_size_in_dwords; layout->memory_layout_in_dwords_per_stage[stage].primary_size = reg_usage[stage].primary; layout->total_size_in_dwords += reg_usage[stage].primary; layout->total_size_in_dwords = ALIGN_POT(layout->total_size_in_dwords, 4); layout->memory_layout_in_dwords_per_stage[stage].secondary_offset = layout->total_size_in_dwords; layout->memory_layout_in_dwords_per_stage[stage].secondary_size = reg_usage[stage].secondary; layout->total_size_in_dwords += reg_usage[stage].secondary; layout->memory_layout_in_dwords_per_stage[stage].primary_dynamic_size = reg_usage[stage].primary_dynamic; layout->memory_layout_in_dwords_per_stage[stage].secondary_dynamic_size = reg_usage[stage].secondary_dynamic; } } #if defined(DEBUG) static void pvr_dump_in_memory_layout_sizes(const struct pvr_descriptor_set_layout *layout) { mesa_logd("=== SET LAYOUT ==="); mesa_logd("----------------------------------------------"); mesa_logd(" in memory:"); mesa_logd("----------------------------------------------"); for (uint32_t stage = 0; stage < ARRAY_SIZE(layout->memory_layout_in_dwords_per_stage); stage++) { mesa_logd( "| %-18s @ %04u |", stage_names[stage].primary, layout->memory_layout_in_dwords_per_stage[stage].primary_offset); mesa_logd("----------------------------------------------"); /* Print primaries. */ for (uint32_t i = 0; i < layout->binding_count; i++) { const struct pvr_descriptor_set_layout_binding *const binding = &layout->bindings[i]; if (binding->type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || binding->type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) continue; mesa_logd("| %s %04u | %-26s[%3u] |", (binding->shader_stage_mask & (1U << stage)) ? " " : "X", binding->per_stage_offset_in_dwords[stage].primary, descriptor_names[binding->type], binding->descriptor_count); } /* Print dynamic primaries. */ for (uint32_t i = 0; i < layout->binding_count; i++) { const struct pvr_descriptor_set_layout_binding *const binding = &layout->bindings[i]; if (binding->type != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC && binding->type != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) continue; mesa_logd("| * %s %04u | %-26s[%3u] |", (binding->shader_stage_mask & (1U << stage)) ? " " : "X", binding->per_stage_offset_in_dwords[stage].primary, descriptor_names[binding->type], binding->descriptor_count); } mesa_logd("----------------------------------------------"); mesa_logd( "| %-18s @ %04u |", stage_names[stage].secondary, layout->memory_layout_in_dwords_per_stage[stage].secondary_offset); mesa_logd("----------------------------------------------"); /* Print secondaries. */ for (uint32_t i = 0; i < layout->binding_count; i++) { const struct pvr_descriptor_set_layout_binding *const binding = &layout->bindings[i]; if (binding->type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || binding->type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) continue; mesa_logd("| %s %04u | %-26s[%3u] |", (binding->shader_stage_mask & (1U << stage)) ? " " : "X", binding->per_stage_offset_in_dwords[stage].secondary, descriptor_names[binding->type], binding->descriptor_count); } /* Print dynamic secondaries. */ for (uint32_t i = 0; i < layout->binding_count; i++) { const struct pvr_descriptor_set_layout_binding *const binding = &layout->bindings[i]; if (binding->type != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC && binding->type != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) continue; mesa_logd("| * %s %04u | %-26s[%3u] |", (binding->shader_stage_mask & (1U << stage)) ? " " : "X", binding->per_stage_offset_in_dwords[stage].secondary, descriptor_names[binding->type], binding->descriptor_count); } mesa_logd("=============================================="); } } #endif VkResult pvr_CreateDescriptorSetLayout( VkDevice _device, const VkDescriptorSetLayoutCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDescriptorSetLayout *pSetLayout) { /* Used to accumulate sizes and set each descriptor's offsets per stage. */ struct pvr_register_usage reg_usage[PVR_STAGE_ALLOCATION_COUNT] = { 0 }; PVR_FROM_HANDLE(pvr_device, device, _device); struct pvr_descriptor_set_layout *layout; VkDescriptorSetLayoutBinding *bindings; uint32_t immutable_sampler_count; assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO); vk_foreach_struct (ext, pCreateInfo->pNext) { pvr_debug_ignored_stype(ext->sType); } /* TODO: Add support for push descriptors. */ if (pCreateInfo->bindingCount == 0) { layout = pvr_descriptor_set_layout_allocate(device, pAllocator, 0, 0, 0); if (!layout) return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); *pSetLayout = pvr_descriptor_set_layout_to_handle(layout); return VK_SUCCESS; } /* TODO: Instead of sorting, maybe do what anvil does? */ bindings = pvr_create_sorted_bindings(device, pAllocator, pCreateInfo->pBindings, pCreateInfo->bindingCount); if (!bindings) return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); immutable_sampler_count = 0; for (uint32_t i = 0; i < pCreateInfo->bindingCount; i++) { /* From the Vulkan 1.1.97 spec for VkDescriptorSetLayoutBinding: * * "If descriptorType specifies a VK_DESCRIPTOR_TYPE_SAMPLER or * VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER type descriptor, then * pImmutableSamplers can be used to initialize a set of immutable * samplers. [...] If descriptorType is not one of these descriptor * types, then pImmutableSamplers is ignored. * * We need to be careful here and only parse pImmutableSamplers if we * have one of the right descriptor types. */ const VkDescriptorType descriptor_type = bindings[i].descriptorType; if ((descriptor_type == VK_DESCRIPTOR_TYPE_SAMPLER || descriptor_type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) && bindings[i].pImmutableSamplers) immutable_sampler_count += bindings[i].descriptorCount; } /* From the Vulkan 1.2.190 spec for VkDescriptorSetLayoutCreateInfo: * * "The VkDescriptorSetLayoutBinding::binding members of the elements * of the pBindings array must each have different values." * * So we don't worry about duplicates and just allocate for bindingCount * amount of bindings. */ layout = pvr_descriptor_set_layout_allocate( device, pAllocator, pCreateInfo->bindingCount, immutable_sampler_count, PVR_PIPELINE_LAYOUT_SUPPORTED_DESCRIPTOR_TYPE_COUNT); if (!layout) { vk_free2(&device->vk.alloc, pAllocator, bindings); return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); } layout->binding_count = pCreateInfo->bindingCount; for (uint32_t bind_num = 0; bind_num < layout->binding_count; bind_num++) { const VkDescriptorSetLayoutBinding *const binding = &bindings[bind_num]; struct pvr_descriptor_set_layout_binding *const internal_binding = &layout->bindings[bind_num]; VkShaderStageFlags shader_stages = 0; internal_binding->type = binding->descriptorType; /* The binding_numbers can be non-contiguous so we ignore the user * specified binding numbers and make them contiguous ourselves. */ internal_binding->binding_number = bind_num; /* From Vulkan spec 1.2.189: * * "If descriptorCount is zero this binding entry is reserved and the * resource must not be accessed from any stage via this binding" * * So do not use bindings->stageFlags, use shader_stages instead. */ if (binding->descriptorCount) { shader_stages = binding->stageFlags; internal_binding->descriptor_count = binding->descriptorCount; internal_binding->descriptor_index = layout->descriptor_count; layout->descriptor_count += binding->descriptorCount; } switch (binding->descriptorType) { case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: case VK_DESCRIPTOR_TYPE_SAMPLER: if (binding->pImmutableSamplers && binding->descriptorCount > 0) { internal_binding->has_immutable_samplers = true; internal_binding->immutable_samplers_index = layout->immutable_sampler_count; for (uint32_t j = 0; j < binding->descriptorCount; j++) { PVR_FROM_HANDLE(pvr_sampler, sampler, bindings->pImmutableSamplers[j]); const uint32_t next = j + layout->immutable_sampler_count; layout->immutable_samplers[next] = sampler; } layout->immutable_sampler_count += binding->descriptorCount; } break; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: layout->dynamic_buffer_count += binding->descriptorCount; break; case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: break; default: unreachable("Unknown descriptor type"); break; } if (!shader_stages) continue; internal_binding->shader_stages = shader_stages; layout->shader_stages |= shader_stages; for (uint32_t stage = 0; stage < ARRAY_SIZE(layout->bindings[0].per_stage_offset_in_dwords); stage++) { const VkDescriptorType descriptor_type = binding->descriptorType; if (!pvr_stage_matches_vk_flags(stage, shader_stages)) continue; internal_binding->shader_stage_mask |= (1U << stage); /* TODO: Do we have to allocate them at the end? We could speed it * by allocating them here if not. */ /* We allocate dynamics primary and secondaries at the end. */ if (descriptor_type != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC && descriptor_type != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) { struct pvr_descriptor_size_info size_info; pvr_descriptor_size_info_init(device, descriptor_type, &size_info); STATIC_ASSERT( ARRAY_SIZE(reg_usage) == ARRAY_SIZE(layout->bindings[0].per_stage_offset_in_dwords)); reg_usage[stage].primary = ALIGN_POT(reg_usage[stage].primary, size_info.alignment); internal_binding->per_stage_offset_in_dwords[stage].primary = reg_usage[stage].primary; reg_usage[stage].primary += size_info.primary * internal_binding->descriptor_count; internal_binding->per_stage_offset_in_dwords[stage].secondary = reg_usage[stage].secondary; reg_usage[stage].secondary += size_info.secondary * internal_binding->descriptor_count; } STATIC_ASSERT( ARRAY_SIZE(layout->per_stage_descriptor_count) == ARRAY_SIZE(layout->bindings[0].per_stage_offset_in_dwords)); layout->per_stage_descriptor_count[stage][descriptor_type] += internal_binding->descriptor_count; } } for (uint32_t bind_num = 0; bind_num < layout->binding_count; bind_num++) { struct pvr_descriptor_set_layout_binding *const internal_binding = &layout->bindings[bind_num]; const VkDescriptorType descriptor_type = internal_binding->type; if (descriptor_type != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC && descriptor_type != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) continue; for (uint32_t stage = 0; stage < ARRAY_SIZE(layout->bindings[0].per_stage_offset_in_dwords); stage++) { struct pvr_descriptor_size_info size_info; const VkShaderStageFlags shader_stages = internal_binding->shader_stages; if (!pvr_stage_matches_vk_flags(stage, shader_stages)) continue; pvr_descriptor_size_info_init(device, descriptor_type, &size_info); /* TODO: align primary like we did with other descriptors? */ internal_binding->per_stage_offset_in_dwords[stage].primary = reg_usage[stage].primary_dynamic; reg_usage[stage].primary_dynamic += size_info.primary * internal_binding->descriptor_count; internal_binding->per_stage_offset_in_dwords[stage].secondary = reg_usage[stage].secondary_dynamic; reg_usage[stage].secondary_dynamic += size_info.secondary * internal_binding->descriptor_count; } } pvr_setup_in_memory_layout_sizes(layout, reg_usage); #if defined(DEBUG) pvr_dump_in_memory_layout_sizes(layout); #endif vk_free2(&device->vk.alloc, pAllocator, bindings); *pSetLayout = pvr_descriptor_set_layout_to_handle(layout); return VK_SUCCESS; } void pvr_DestroyDescriptorSetLayout(VkDevice _device, VkDescriptorSetLayout _set_layout, const VkAllocationCallbacks *pAllocator) { PVR_FROM_HANDLE(pvr_descriptor_set_layout, layout, _set_layout); PVR_FROM_HANDLE(pvr_device, device, _device); pvr_descriptor_set_layout_free(device, pAllocator, layout); } #if defined(DEBUG) static void pvr_dump_in_register_layout_sizes(const struct pvr_device *device, const struct pvr_pipeline_layout *layout) { mesa_logd("=== SET LAYOUT ==="); mesa_logd("----------------------------------------------------"); mesa_logd(" in registers:"); mesa_logd("----------------------------------------------------"); for (uint32_t stage = 0; stage < ARRAY_SIZE(layout->register_layout_in_dwords_per_stage); stage++) { uint32_t dynamic_offset = 0; mesa_logd("| %-48s |", stage_names[stage].primary_dynamic); mesa_logd("----------------------------------------------------"); /* Print dynamic primaries. */ for (uint32_t set_num = 0; set_num < layout->set_count; set_num++) { const struct pvr_descriptor_set_layout *const set_layout = layout->set_layout[set_num]; for (uint32_t i = 0; i < set_layout->binding_count; i++) { const struct pvr_descriptor_set_layout_binding *const binding = &set_layout->bindings[i]; bool valid = !!(binding->shader_stage_mask & (1U << stage)); if (binding->type != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC && binding->type != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) continue; mesa_logd("| %s %04u | %u:%03u | %-26s[%3u] |", (valid) ? " " : "X", dynamic_offset, set_num, i, descriptor_names[binding->type], binding->descriptor_count); if (valid) { struct pvr_descriptor_size_info size_info; pvr_descriptor_size_info_init(device, binding->type, &size_info); dynamic_offset += size_info.primary; } } } mesa_logd("----------------------------------------------------"); mesa_logd("| %-48s |", stage_names[stage].secondary_dynamic); mesa_logd("----------------------------------------------------"); /* Print dynamic secondaries. */ for (uint32_t set_num = 0; set_num < layout->set_count; set_num++) { const struct pvr_descriptor_set_layout *const set_layout = layout->set_layout[set_num]; for (uint32_t i = 0; i < set_layout->binding_count; i++) { const struct pvr_descriptor_set_layout_binding *const binding = &set_layout->bindings[i]; bool valid = !!(binding->shader_stage_mask & (1U << stage)); if (binding->type != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC && binding->type != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) continue; mesa_logd("| %s %04u | %u:%03u | %-26s[%3u] |", (valid) ? " " : "X", dynamic_offset, set_num, i, descriptor_names[binding->type], binding->descriptor_count); if (valid) { struct pvr_descriptor_size_info size_info; pvr_descriptor_size_info_init(device, binding->type, &size_info); dynamic_offset += size_info.secondary; } } } mesa_logd("----------------------------------------------------"); mesa_logd("| %-48s |", stage_names[stage].primary); mesa_logd("----------------------------------------------------"); /* Print primaries. */ for (uint32_t set_num = 0; set_num < layout->set_count; set_num++) { const struct pvr_descriptor_set_layout *const set_layout = layout->set_layout[set_num]; const uint32_t base = layout->register_layout_in_dwords_per_stage[stage][set_num] .primary_offset; for (uint32_t i = 0; i < set_layout->binding_count; i++) { const struct pvr_descriptor_set_layout_binding *const binding = &set_layout->bindings[i]; if (binding->type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || binding->type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) continue; mesa_logd("| %s %04u | %u:%03u | %-26s[%3u] |", (binding->shader_stage_mask & (1U << stage)) ? " " : "X", base + binding->per_stage_offset_in_dwords[stage].primary, set_num, i, descriptor_names[binding->type], binding->descriptor_count); } } mesa_logd("----------------------------------------------------"); mesa_logd("| %-48s |", stage_names[stage].secondary); mesa_logd("----------------------------------------------------"); /* Print secondaries. */ for (uint32_t set_num = 0; set_num < layout->set_count; set_num++) { const struct pvr_descriptor_set_layout *const set_layout = layout->set_layout[set_num]; const uint32_t base = layout->register_layout_in_dwords_per_stage[stage][set_num] .secondary_offset; for (uint32_t i = 0; i < set_layout->binding_count; i++) { const struct pvr_descriptor_set_layout_binding *const binding = &set_layout->bindings[i]; if (binding->type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || binding->type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) continue; mesa_logd("| %s %04u | %u:%03u | %-26s[%3u] |", (binding->shader_stage_mask & (1U << stage)) ? " " : "X", base + binding->per_stage_offset_in_dwords[stage].secondary, set_num, i, descriptor_names[binding->type], binding->descriptor_count); } } mesa_logd("===================================================="); } } #endif /* Pipeline layouts. These have nothing to do with the pipeline. They are * just multiple descriptor set layouts pasted together. */ VkResult pvr_CreatePipelineLayout(VkDevice _device, const VkPipelineLayoutCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkPipelineLayout *pPipelineLayout) { uint32_t next_free_reg[PVR_STAGE_ALLOCATION_COUNT]; PVR_FROM_HANDLE(pvr_device, device, _device); struct pvr_pipeline_layout *layout; assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO); assert(pCreateInfo->setLayoutCount <= PVR_MAX_DESCRIPTOR_SETS); layout = vk_object_alloc(&device->vk, pAllocator, sizeof(*layout), VK_OBJECT_TYPE_PIPELINE_LAYOUT); if (!layout) return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); layout->set_count = pCreateInfo->setLayoutCount; layout->shader_stages = 0; for (uint32_t stage = 0; stage < PVR_STAGE_ALLOCATION_COUNT; stage++) { uint32_t descriptor_counts [PVR_PIPELINE_LAYOUT_SUPPORTED_DESCRIPTOR_TYPE_COUNT] = { 0 }; struct pvr_pipeline_layout_reg_info *const reg_info = &layout->per_stage_reg_info[stage]; *reg_info = (struct pvr_pipeline_layout_reg_info){ 0 }; layout->per_stage_descriptor_masks[stage] = 0; for (uint32_t set_num = 0; set_num < layout->set_count; set_num++) { /* So we don't write these again and again. Just do it once. */ if (stage == 0) { PVR_FROM_HANDLE(pvr_descriptor_set_layout, set_layout, pCreateInfo->pSetLayouts[set_num]); layout->set_layout[set_num] = set_layout; layout->shader_stages |= set_layout->shader_stages; } const struct pvr_descriptor_set_layout_mem_layout *const mem_layout = &layout->set_layout[set_num] ->memory_layout_in_dwords_per_stage[stage]; /* Allocate registers counts for dynamic descriptors. */ reg_info->primary_dynamic_size_in_dwords += mem_layout->primary_dynamic_size; reg_info->secondary_dynamic_size_in_dwords += mem_layout->secondary_dynamic_size; for (VkDescriptorType type = 0; type < PVR_PIPELINE_LAYOUT_SUPPORTED_DESCRIPTOR_TYPE_COUNT; type++) { uint32_t descriptor_count; layout->descriptor_offsets[set_num][stage][type] = descriptor_counts[type]; descriptor_count = layout->set_layout[set_num] ->per_stage_descriptor_count[stage][type]; if (!descriptor_count) continue; switch (type) { case VK_DESCRIPTOR_TYPE_SAMPLER: case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: layout->per_stage_descriptor_masks[stage] |= 1U << set_num; descriptor_counts[type] += descriptor_count; break; /* We don't need to keep track of the counts or masks for other * descriptor types so there is no assert() here since other * types are not invalid or unsupported. */ /* TODO: Improve the comment above to specify why, when we find * out. */ default: break; } } } next_free_reg[stage] = reg_info->primary_dynamic_size_in_dwords + reg_info->secondary_dynamic_size_in_dwords; } /* Allocate registers counts for primary and secondary descriptors. */ for (uint32_t stage = 0; stage < PVR_STAGE_ALLOCATION_COUNT; stage++) { for (uint32_t set_num = 0; set_num < layout->set_count; set_num++) { const struct pvr_descriptor_set_layout_mem_layout *const mem_layout = &layout->set_layout[set_num] ->memory_layout_in_dwords_per_stage[stage]; struct pvr_descriptor_set_layout_mem_layout *const reg_layout = &layout->register_layout_in_dwords_per_stage[stage][set_num]; next_free_reg[stage] = ALIGN_POT(next_free_reg[stage], 4); reg_layout->primary_offset = next_free_reg[stage]; reg_layout->primary_size = mem_layout->primary_size; next_free_reg[stage] += reg_layout->primary_size; } /* To optimize the total shared layout allocation used by the shader, * secondary descriptors come last since they're less likely to be used. */ for (uint32_t set_num = 0; set_num < layout->set_count; set_num++) { const struct pvr_descriptor_set_layout_mem_layout *const mem_layout = &layout->set_layout[set_num] ->memory_layout_in_dwords_per_stage[stage]; struct pvr_descriptor_set_layout_mem_layout *const reg_layout = &layout->register_layout_in_dwords_per_stage[stage][set_num]; /* Should we be aligning next_free_reg like it's done with the * primary descriptors? */ reg_layout->secondary_offset = next_free_reg[stage]; reg_layout->secondary_size = mem_layout->secondary_size; next_free_reg[stage] += reg_layout->secondary_size; } } layout->push_constants_shader_stages = 0; for (uint32_t i = 0; i < pCreateInfo->pushConstantRangeCount; ++i) { const VkPushConstantRange *range = &pCreateInfo->pPushConstantRanges[i]; layout->push_constants_shader_stages |= range->stageFlags; } #if defined(DEBUG) pvr_dump_in_register_layout_sizes(device, layout); #endif *pPipelineLayout = pvr_pipeline_layout_to_handle(layout); return VK_SUCCESS; } void pvr_DestroyPipelineLayout(VkDevice _device, VkPipelineLayout _pipelineLayout, const VkAllocationCallbacks *pAllocator) { PVR_FROM_HANDLE(pvr_device, device, _device); PVR_FROM_HANDLE(pvr_pipeline_layout, layout, _pipelineLayout); vk_object_free(&device->vk, pAllocator, layout); } VkResult pvr_CreateDescriptorPool(VkDevice _device, const VkDescriptorPoolCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDescriptorPool *pDescriptorPool) { PVR_FROM_HANDLE(pvr_device, device, _device); struct pvr_descriptor_pool *pool; assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO); pool = vk_object_alloc(&device->vk, pAllocator, sizeof(*pool), VK_OBJECT_TYPE_DESCRIPTOR_POOL); if (!pool) return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); if (pAllocator) pool->alloc = *pAllocator; else pool->alloc = device->vk.alloc; pool->max_sets = pCreateInfo->maxSets; list_inithead(&pool->descriptor_sets); pool->total_size_in_dwords = 0; for (uint32_t i = 0; i < pCreateInfo->poolSizeCount; i++) { struct pvr_descriptor_size_info size_info; const uint32_t descriptor_count = pCreateInfo->pPoolSizes[i].descriptorCount; pvr_descriptor_size_info_init(device, pCreateInfo->pPoolSizes[i].type, &size_info); const uint32_t secondary = ALIGN_POT(size_info.secondary, 4); const uint32_t primary = ALIGN_POT(size_info.primary, 4); pool->total_size_in_dwords += descriptor_count * (primary + secondary); } pool->total_size_in_dwords *= PVR_STAGE_ALLOCATION_COUNT; pool->current_size_in_dwords = 0; pvr_finishme("Entry tracker for allocations?"); *pDescriptorPool = pvr_descriptor_pool_to_handle(pool); return VK_SUCCESS; } static void pvr_free_descriptor_set(struct pvr_device *device, struct pvr_descriptor_pool *pool, struct pvr_descriptor_set *set) { list_del(&set->link); pvr_bo_free(device, set->pvr_bo); vk_object_free(&device->vk, &pool->alloc, set); } void pvr_DestroyDescriptorPool(VkDevice _device, VkDescriptorPool _pool, const VkAllocationCallbacks *pAllocator) { PVR_FROM_HANDLE(pvr_device, device, _device); PVR_FROM_HANDLE(pvr_descriptor_pool, pool, _pool); if (!pool) return; list_for_each_entry_safe (struct pvr_descriptor_set, set, &pool->descriptor_sets, link) { pvr_free_descriptor_set(device, pool, set); } vk_object_free(&device->vk, pAllocator, pool); } VkResult pvr_ResetDescriptorPool(VkDevice _device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags) { assert(!"Unimplemented"); return VK_SUCCESS; } static uint16_t pvr_get_descriptor_primary_offset( const struct pvr_device *device, const struct pvr_descriptor_set_layout *layout, const struct pvr_descriptor_set_layout_binding *binding, const uint32_t stage, const uint32_t desc_idx) { struct pvr_descriptor_size_info size_info; uint32_t offset; assert(stage < ARRAY_SIZE(layout->memory_layout_in_dwords_per_stage)); assert(desc_idx < binding->descriptor_count); pvr_descriptor_size_info_init(device, binding->type, &size_info); offset = layout->memory_layout_in_dwords_per_stage[stage].primary_offset; offset += binding->per_stage_offset_in_dwords[stage].primary; offset += (desc_idx * size_info.primary); /* Offset must be less than 16bits. */ assert(offset < UINT16_MAX); return (uint16_t)offset; } static uint16_t pvr_get_descriptor_secondary_offset( const struct pvr_device *device, const struct pvr_descriptor_set_layout *layout, const struct pvr_descriptor_set_layout_binding *binding, const uint32_t stage, const uint32_t desc_idx) { struct pvr_descriptor_size_info size_info; uint32_t offset; assert(stage < ARRAY_SIZE(layout->memory_layout_in_dwords_per_stage)); assert(desc_idx < binding->descriptor_count); pvr_descriptor_size_info_init(device, binding->type, &size_info); offset = layout->memory_layout_in_dwords_per_stage[stage].secondary_offset; offset += binding->per_stage_offset_in_dwords[stage].secondary; offset += (desc_idx * size_info.secondary); /* Offset must be less than 16bits. */ assert(offset < UINT16_MAX); return (uint16_t)offset; } static void pvr_write_sampler_descriptor(uint32_t *primary, const struct pvr_sampler *sampler) { /* TODO: Implement based on WriteSamplerDescriptor. */ pvr_finishme("Implement after vkCreateSampler API."); } #define PVR_MAX_DESCRIPTOR_MEM_SIZE_IN_DWORDS (4 * 1024) static VkResult pvr_descriptor_set_create(struct pvr_device *device, struct pvr_descriptor_pool *pool, const struct pvr_descriptor_set_layout *layout, struct pvr_descriptor_set **const descriptor_set_out) { struct pvr_descriptor_set *set; VkResult result; size_t size; void *map; size = sizeof(*set) + sizeof(set->descriptors[0]) * layout->descriptor_count; /* TODO: Add support to allocate descriptors from descriptor pool, also * check the required descriptors must not exceed max allowed descriptors. */ set = vk_object_zalloc(&device->vk, &pool->alloc, size, VK_OBJECT_TYPE_DESCRIPTOR_SET); if (!set) return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); /* TODO: Add support to allocate device memory from a common pool. Look at * something like anv. Also we can allocate a whole chunk of device memory * for max descriptors supported by pool as done by v3dv. Also check the * possibility if this can be removed from here and done on need basis. */ if (layout->binding_count > 0) { const uint32_t cache_line_size = rogue_get_slc_cache_line_size(&device->pdevice->dev_info); uint64_t bo_size = MIN2(pool->total_size_in_dwords, PVR_MAX_DESCRIPTOR_MEM_SIZE_IN_DWORDS) * sizeof(uint32_t); result = pvr_bo_alloc(device, device->heaps.general_heap, bo_size, cache_line_size, PVR_BO_ALLOC_FLAG_CPU_MAPPED, &set->pvr_bo); if (result != VK_SUCCESS) goto err_free_descriptor_set; } set->layout = layout; set->pool = pool; map = set->pvr_bo->bo->map; for (uint32_t i = 0; i < layout->binding_count; i++) { const struct pvr_descriptor_set_layout_binding *binding = &layout->bindings[i]; if (binding->descriptor_count == 0 || !binding->has_immutable_samplers) continue; for (uint32_t stage = 0; stage < ARRAY_SIZE(binding->per_stage_offset_in_dwords); stage++) { if (!(binding->shader_stage_mask & (1U << stage))) continue; for (uint32_t j = 0; j < binding->descriptor_count; j++) { uint32_t idx = binding->immutable_samplers_index + j; const struct pvr_sampler *sampler = layout->immutable_samplers[idx]; unsigned int offset_in_dwords = pvr_get_descriptor_primary_offset(device, layout, binding, stage, j); if (binding->type == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) offset_in_dwords += 4; pvr_write_sampler_descriptor(map + offset_in_dwords * sizeof(uint32_t), sampler); } } } list_addtail(&set->link, &pool->descriptor_sets); *descriptor_set_out = set; return VK_SUCCESS; err_free_descriptor_set: vk_object_free(&device->vk, &pool->alloc, set); return result; } VkResult pvr_AllocateDescriptorSets(VkDevice _device, const VkDescriptorSetAllocateInfo *pAllocateInfo, VkDescriptorSet *pDescriptorSets) { PVR_FROM_HANDLE(pvr_descriptor_pool, pool, pAllocateInfo->descriptorPool); PVR_FROM_HANDLE(pvr_device, device, _device); VkResult result; uint32_t i; vk_foreach_struct (ext, pAllocateInfo->pNext) { pvr_debug_ignored_stype(ext->sType); } for (i = 0; i < pAllocateInfo->descriptorSetCount; i++) { PVR_FROM_HANDLE(pvr_descriptor_set_layout, layout, pAllocateInfo->pSetLayouts[i]); struct pvr_descriptor_set *set = NULL; result = pvr_descriptor_set_create(device, pool, layout, &set); if (result != VK_SUCCESS) goto err_free_descriptor_sets; pDescriptorSets[i] = pvr_descriptor_set_to_handle(set); } return VK_SUCCESS; err_free_descriptor_sets: pvr_FreeDescriptorSets(_device, pAllocateInfo->descriptorPool, i, pDescriptorSets); for (i = 0; i < pAllocateInfo->descriptorSetCount; i++) pDescriptorSets[i] = VK_NULL_HANDLE; return result; } VkResult pvr_FreeDescriptorSets(VkDevice _device, VkDescriptorPool descriptorPool, uint32_t count, const VkDescriptorSet *pDescriptorSets) { PVR_FROM_HANDLE(pvr_descriptor_pool, pool, descriptorPool); PVR_FROM_HANDLE(pvr_device, device, _device); for (uint32_t i = 0; i < count; i++) { struct pvr_descriptor_set *set; if (!pDescriptorSets[i]) continue; set = pvr_descriptor_set_from_handle(pDescriptorSets[i]); pvr_free_descriptor_set(device, pool, set); } return VK_SUCCESS; } static int pvr_compare_layout_binding(const void *a, const void *b) { uint32_t binding_a; uint32_t binding_b; binding_a = ((struct pvr_descriptor_set_layout_binding *)a)->binding_number; binding_b = ((struct pvr_descriptor_set_layout_binding *)b)->binding_number; if (binding_a < binding_b) return -1; if (binding_a > binding_b) return 1; return 0; } /* This function does not assume that the binding will always exist for a * particular binding_num. Caller should check before using the return pointer. */ static struct pvr_descriptor_set_layout_binding * pvr_get_descriptor_binding(const struct pvr_descriptor_set_layout *layout, const uint32_t binding_num) { struct pvr_descriptor_set_layout_binding binding; binding.binding_number = binding_num; return bsearch(&binding, layout->bindings, layout->binding_count, sizeof(binding), pvr_compare_layout_binding); } static void pvr_descriptor_update_buffer_info(const struct pvr_device *device, const VkWriteDescriptorSet *write_set, struct pvr_descriptor_set *set, uint32_t *mem_ptr, uint32_t start_stage, uint32_t end_stage) { const struct pvr_descriptor_set_layout_binding *binding; struct pvr_descriptor_size_info size_info; bool is_dynamic; binding = pvr_get_descriptor_binding(set->layout, write_set->dstBinding); /* Binding should not be NULL. */ assert(binding); is_dynamic = (binding->type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) || (binding->type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC); pvr_descriptor_size_info_init(device, binding->type, &size_info); /* Only need to update the buffer if it is actually being used. If it was * not present in any stage, then the shader_stage_mask would be 0 and we * can skip this update. */ if (binding->shader_stage_mask == 0) return; for (uint32_t i = 0; i < write_set->descriptorCount; i++) { const VkDescriptorBufferInfo *buffer_info = &write_set->pBufferInfo[i]; PVR_FROM_HANDLE(pvr_buffer, buffer, buffer_info->buffer); const uint32_t desc_idx = binding->descriptor_index + write_set->dstArrayElement + i; uint64_t addr = buffer->dev_addr.addr + buffer_info->offset; uint32_t range = (buffer_info->range == VK_WHOLE_SIZE) ? (buffer->size - buffer_info->offset) : (buffer_info->range); set->descriptors[desc_idx].type = write_set->descriptorType; set->descriptors[desc_idx].buffer_dev_addr.addr = addr; set->descriptors[desc_idx].buffer_create_info_size = buffer->size; set->descriptors[desc_idx].buffer_desc_range = range; if (is_dynamic) continue; /* Update the entries in the descriptor memory for static buffer. */ for (uint32_t j = start_stage; j < end_stage; j++) { uint32_t primary_offset; uint32_t secondary_offset; if (!(binding->shader_stage_mask & (1U << j))) continue; /* Offset calculation functions expect descriptor_index to be * binding relative not layout relative, so we have used * write_set->dstArrayElement + i rather than desc_idx. */ primary_offset = pvr_get_descriptor_primary_offset(device, set->layout, binding, j, write_set->dstArrayElement + i); secondary_offset = pvr_get_descriptor_secondary_offset(device, set->layout, binding, j, write_set->dstArrayElement + i); memcpy(mem_ptr + primary_offset, &addr, size_info.primary << 2); memcpy(mem_ptr + secondary_offset, &range, size_info.secondary << 2); } } } void pvr_UpdateDescriptorSets(VkDevice _device, uint32_t descriptorWriteCount, const VkWriteDescriptorSet *pDescriptorWrites, uint32_t descriptorCopyCount, const VkCopyDescriptorSet *pDescriptorCopies) { PVR_FROM_HANDLE(pvr_device, device, _device); for (uint32_t i = 0; i < descriptorWriteCount; i++) { const VkWriteDescriptorSet *write_set = &pDescriptorWrites[i]; PVR_FROM_HANDLE(pvr_descriptor_set, set, write_set->dstSet); uint32_t *map = set->pvr_bo->bo->map; vk_foreach_struct (ext, write_set->pNext) { pvr_debug_ignored_stype(ext->sType); } switch (write_set->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_UNIFORM_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: pvr_finishme("Update support missing for %d descriptor type\n", write_set->descriptorType); 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: pvr_descriptor_update_buffer_info(device, write_set, set, map, 0, PVR_STAGE_ALLOCATION_COUNT); break; default: unreachable("Unknown descriptor type"); break; } } if (descriptorCopyCount > 0) pvr_finishme("Descriptor copying support missing\n"); }