| /* |
| * 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_image.h" |
| |
| #include "venus-protocol/vn_protocol_driver_image.h" |
| #include "venus-protocol/vn_protocol_driver_image_view.h" |
| #include "venus-protocol/vn_protocol_driver_sampler.h" |
| #include "venus-protocol/vn_protocol_driver_sampler_ycbcr_conversion.h" |
| #include "vk_format.h" |
| |
| #include "vn_android.h" |
| #include "vn_device.h" |
| #include "vn_device_memory.h" |
| #include "vn_physical_device.h" |
| #include "vn_wsi.h" |
| |
| #define IMAGE_REQS_CACHE_MAX_ENTRIES 500 |
| |
| /* image commands */ |
| |
| static inline uint32_t |
| vn_image_get_plane_count(const VkImageCreateInfo *create_info) |
| { |
| if (!(create_info->flags & VK_IMAGE_CREATE_DISJOINT_BIT)) |
| return 1; |
| |
| /* TODO VkDrmFormatModifierPropertiesEXT::drmFormatModifierPlaneCount */ |
| assert(create_info->tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT); |
| return vk_format_get_plane_count(create_info->format); |
| } |
| |
| static inline uint32_t |
| vn_image_get_plane(const VkImageAspectFlagBits plane_aspect) |
| { |
| switch (plane_aspect) { |
| case VK_IMAGE_ASPECT_PLANE_1_BIT: |
| return 1; |
| case VK_IMAGE_ASPECT_PLANE_2_BIT: |
| return 2; |
| default: |
| return 0; |
| } |
| } |
| |
| static void |
| vn_image_fill_reqs(const struct vn_image_memory_requirements *req, |
| VkMemoryRequirements2 *out_reqs) |
| { |
| union { |
| VkBaseOutStructure *pnext; |
| VkMemoryRequirements2 *two; |
| VkMemoryDedicatedRequirements *dedicated; |
| } u = { .two = out_reqs }; |
| |
| while (u.pnext) { |
| switch (u.pnext->sType) { |
| case VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2: |
| u.two->memoryRequirements = req->memory.memoryRequirements; |
| break; |
| case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS: |
| u.dedicated->prefersDedicatedAllocation = |
| req->dedicated.prefersDedicatedAllocation; |
| u.dedicated->requiresDedicatedAllocation = |
| req->dedicated.requiresDedicatedAllocation; |
| break; |
| default: |
| break; |
| } |
| u.pnext = u.pnext->pNext; |
| } |
| } |
| |
| static void |
| vn_image_cache_debug_dump(struct vn_image_reqs_cache *cache) |
| { |
| vn_log(NULL, "dumping image reqs cache statistics"); |
| vn_log(NULL, " hit %u\n", cache->debug.cache_hit_count); |
| vn_log(NULL, " miss %u\n", cache->debug.cache_miss_count); |
| vn_log(NULL, " skip %u\n", cache->debug.cache_skip_count); |
| } |
| |
| static bool |
| vn_image_get_image_reqs_key(struct vn_device *dev, |
| const VkImageCreateInfo *create_info, |
| uint8_t *key) |
| { |
| struct mesa_sha1 sha1_ctx; |
| |
| if (!dev->image_reqs_cache.ht) |
| return false; |
| |
| _mesa_sha1_init(&sha1_ctx); |
| |
| /* Hash relevant fields in the pNext chain */ |
| vk_foreach_struct_const(src, create_info->pNext) { |
| switch (src->sType) { |
| case VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO: { |
| struct VkExternalMemoryImageCreateInfo *ext_mem = |
| (struct VkExternalMemoryImageCreateInfo *)src; |
| _mesa_sha1_update(&sha1_ctx, &ext_mem->handleTypes, |
| sizeof(VkExternalMemoryHandleTypeFlags)); |
| break; |
| } |
| case VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO: { |
| struct VkImageFormatListCreateInfo *format_list = |
| (struct VkImageFormatListCreateInfo *)src; |
| _mesa_sha1_update(&sha1_ctx, format_list->pViewFormats, |
| sizeof(VkFormat) * format_list->viewFormatCount); |
| break; |
| } |
| case VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT: { |
| struct VkImageDrmFormatModifierListCreateInfoEXT *format_mod_list = |
| (struct VkImageDrmFormatModifierListCreateInfoEXT *)src; |
| _mesa_sha1_update( |
| &sha1_ctx, format_mod_list->pDrmFormatModifiers, |
| sizeof(uint64_t) * format_mod_list->drmFormatModifierCount); |
| break; |
| } |
| case VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT: { |
| struct VkImageDrmFormatModifierExplicitCreateInfoEXT |
| *format_mod_explicit = |
| (struct VkImageDrmFormatModifierExplicitCreateInfoEXT *)src; |
| _mesa_sha1_update(&sha1_ctx, &format_mod_explicit->drmFormatModifier, |
| sizeof(uint64_t)); |
| _mesa_sha1_update( |
| &sha1_ctx, format_mod_explicit->pPlaneLayouts, |
| sizeof(VkSubresourceLayout) * |
| format_mod_explicit->drmFormatModifierPlaneCount); |
| break; |
| } |
| case VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO: { |
| struct VkImageStencilUsageCreateInfo *stencil_usage = |
| (struct VkImageStencilUsageCreateInfo *)src; |
| _mesa_sha1_update(&sha1_ctx, &stencil_usage->stencilUsage, |
| sizeof(VkImageUsageFlags)); |
| break; |
| } |
| default: |
| /* Skip cache for unsupported pNext */ |
| dev->image_reqs_cache.debug.cache_skip_count++; |
| return false; |
| } |
| } |
| |
| /* Hash contingous block of VkImageCreateInfo starting with |
| * VkImageCreateInfo->flags and ending with VkImageCreateInfo->sharingMode |
| * |
| * There's no padding in involved in this hash block so no concern for C |
| * enum sizes or alignment. |
| */ |
| static const size_t create_image_hash_block_size = |
| offsetof(VkImageCreateInfo, queueFamilyIndexCount) - |
| offsetof(VkImageCreateInfo, flags); |
| |
| _mesa_sha1_update(&sha1_ctx, &create_info->flags, |
| create_image_hash_block_size); |
| |
| /* Follow pointer and hash pQueueFamilyIndices separately. |
| * pQueueFamilyIndices is ignored if sharingMode is not |
| * VK_SHARING_MODE_CONCURRENT |
| */ |
| if (create_info->sharingMode == VK_SHARING_MODE_CONCURRENT) { |
| _mesa_sha1_update( |
| &sha1_ctx, create_info->pQueueFamilyIndices, |
| sizeof(uint32_t) * create_info->queueFamilyIndexCount); |
| } |
| |
| _mesa_sha1_update(&sha1_ctx, &create_info->initialLayout, |
| sizeof(create_info->initialLayout)); |
| _mesa_sha1_final(&sha1_ctx, key); |
| |
| return true; |
| } |
| |
| void |
| vn_image_reqs_cache_init(struct vn_device *dev) |
| { |
| struct vn_image_reqs_cache *cache = &dev->image_reqs_cache; |
| |
| if (VN_PERF(NO_ASYNC_IMAGE_CREATE)) |
| return; |
| |
| cache->ht = _mesa_hash_table_create(NULL, vn_cache_key_hash_function, |
| vn_cache_key_equal_function); |
| if (!cache->ht) |
| return; |
| |
| simple_mtx_init(&cache->mutex, mtx_plain); |
| list_inithead(&dev->image_reqs_cache.lru); |
| } |
| |
| void |
| vn_image_reqs_cache_fini(struct vn_device *dev) |
| { |
| const VkAllocationCallbacks *alloc = &dev->base.base.alloc; |
| struct vn_image_reqs_cache *cache = &dev->image_reqs_cache; |
| |
| if (!cache->ht) |
| return; |
| |
| hash_table_foreach(cache->ht, hash_entry) { |
| struct vn_image_reqs_cache_entry *cache_entry = hash_entry->data; |
| list_del(&cache_entry->head); |
| vk_free(alloc, cache_entry); |
| } |
| assert(list_is_empty(&dev->image_reqs_cache.lru)); |
| |
| _mesa_hash_table_destroy(cache->ht, NULL); |
| |
| simple_mtx_destroy(&cache->mutex); |
| |
| if (VN_DEBUG(CACHE)) |
| vn_image_cache_debug_dump(cache); |
| } |
| |
| static bool |
| vn_image_init_reqs_from_cache(struct vn_device *dev, |
| struct vn_image *img, |
| uint8_t *key) |
| { |
| struct vn_image_reqs_cache *cache = &dev->image_reqs_cache; |
| |
| assert(cache->ht); |
| |
| simple_mtx_lock(&cache->mutex); |
| struct hash_entry *hash_entry = _mesa_hash_table_search(cache->ht, key); |
| if (hash_entry) { |
| struct vn_image_reqs_cache_entry *cache_entry = hash_entry->data; |
| for (uint32_t i = 0; i < cache_entry->plane_count; i++) |
| img->requirements[i] = cache_entry->requirements[i]; |
| list_move_to(&cache_entry->head, &dev->image_reqs_cache.lru); |
| p_atomic_inc(&cache->debug.cache_hit_count); |
| } else { |
| p_atomic_inc(&cache->debug.cache_miss_count); |
| } |
| simple_mtx_unlock(&cache->mutex); |
| |
| return !!hash_entry; |
| } |
| |
| static struct vn_image_memory_requirements * |
| vn_image_get_reqs_from_cache(struct vn_device *dev, |
| uint8_t *key, |
| uint32_t plane) |
| { |
| struct vn_image_memory_requirements *requirements = NULL; |
| struct vn_image_reqs_cache *cache = &dev->image_reqs_cache; |
| |
| assert(cache->ht); |
| |
| simple_mtx_lock(&cache->mutex); |
| struct hash_entry *hash_entry = _mesa_hash_table_search(cache->ht, key); |
| if (hash_entry) { |
| struct vn_image_reqs_cache_entry *cache_entry = hash_entry->data; |
| requirements = &cache_entry->requirements[plane]; |
| list_move_to(&cache_entry->head, &dev->image_reqs_cache.lru); |
| p_atomic_inc(&cache->debug.cache_hit_count); |
| } else { |
| p_atomic_inc(&cache->debug.cache_miss_count); |
| } |
| simple_mtx_unlock(&cache->mutex); |
| |
| return requirements; |
| } |
| |
| static void |
| vn_image_store_reqs_in_cache(struct vn_device *dev, |
| uint8_t *key, |
| uint32_t plane_count, |
| struct vn_image_memory_requirements *requirements) |
| { |
| const VkAllocationCallbacks *alloc = &dev->base.base.alloc; |
| struct vn_image_reqs_cache *cache = &dev->image_reqs_cache; |
| struct vn_image_reqs_cache_entry *cache_entry; |
| |
| assert(cache->ht); |
| |
| simple_mtx_lock(&cache->mutex); |
| |
| /* Check if entry was added before lock */ |
| if (_mesa_hash_table_search(cache->ht, key)) { |
| simple_mtx_unlock(&cache->mutex); |
| return; |
| } |
| |
| if (_mesa_hash_table_num_entries(cache->ht) == |
| IMAGE_REQS_CACHE_MAX_ENTRIES) { |
| /* Evict/use the last entry in the lru list for this new entry */ |
| cache_entry = |
| list_last_entry(&cache->lru, struct vn_image_reqs_cache_entry, head); |
| |
| _mesa_hash_table_remove_key(cache->ht, cache_entry->key); |
| list_del(&cache_entry->head); |
| } else { |
| cache_entry = vk_zalloc(alloc, sizeof(*cache_entry), VN_DEFAULT_ALIGN, |
| VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); |
| if (!cache_entry) { |
| simple_mtx_unlock(&cache->mutex); |
| return; |
| } |
| } |
| |
| for (uint32_t i = 0; i < plane_count; i++) |
| cache_entry->requirements[i] = requirements[i]; |
| |
| memcpy(cache_entry->key, key, SHA1_DIGEST_LENGTH); |
| cache_entry->plane_count = plane_count; |
| |
| _mesa_hash_table_insert(dev->image_reqs_cache.ht, cache_entry->key, |
| cache_entry); |
| list_add(&cache_entry->head, &cache->lru); |
| |
| simple_mtx_unlock(&cache->mutex); |
| } |
| |
| static void |
| vn_image_init_memory_requirements(struct vn_image *img, |
| struct vn_device *dev, |
| uint32_t plane_count) |
| { |
| assert(plane_count <= ARRAY_SIZE(img->requirements)); |
| |
| for (uint32_t i = 0; i < plane_count; i++) { |
| img->requirements[i].memory.sType = |
| VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2; |
| img->requirements[i].memory.pNext = &img->requirements[i].dedicated; |
| img->requirements[i].dedicated.sType = |
| VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS; |
| img->requirements[i].dedicated.pNext = NULL; |
| } |
| |
| VkDevice dev_handle = vn_device_to_handle(dev); |
| VkImage img_handle = vn_image_to_handle(img); |
| if (plane_count == 1) { |
| vn_call_vkGetImageMemoryRequirements2( |
| dev->primary_ring, dev_handle, |
| &(VkImageMemoryRequirementsInfo2){ |
| .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, |
| .image = img_handle, |
| }, |
| &img->requirements[0].memory); |
| |
| /* AHB backed image requires dedicated allocation */ |
| if (img->deferred_info) { |
| img->requirements[0].dedicated.prefersDedicatedAllocation = VK_TRUE; |
| img->requirements[0].dedicated.requiresDedicatedAllocation = VK_TRUE; |
| } |
| } else { |
| for (uint32_t i = 0; i < plane_count; i++) { |
| vn_call_vkGetImageMemoryRequirements2( |
| dev->primary_ring, dev_handle, |
| &(VkImageMemoryRequirementsInfo2){ |
| .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, |
| .pNext = |
| &(VkImagePlaneMemoryRequirementsInfo){ |
| .sType = |
| VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO, |
| .planeAspect = VK_IMAGE_ASPECT_PLANE_0_BIT << i, |
| }, |
| .image = img_handle, |
| }, |
| &img->requirements[i].memory); |
| } |
| } |
| } |
| |
| static VkResult |
| vn_image_deferred_info_init(struct vn_image *img, |
| const VkImageCreateInfo *create_info, |
| const VkAllocationCallbacks *alloc) |
| { |
| struct vn_image_create_deferred_info *info = NULL; |
| VkBaseOutStructure *dst = NULL; |
| |
| info = vk_zalloc(alloc, sizeof(*info), VN_DEFAULT_ALIGN, |
| VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); |
| if (!info) |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| |
| info->create = *create_info; |
| dst = (void *)&info->create; |
| |
| vk_foreach_struct_const(src, create_info->pNext) { |
| void *pnext = NULL; |
| switch (src->sType) { |
| case VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO: { |
| /* 12.3. Images |
| * |
| * If viewFormatCount is zero, pViewFormats is ignored and the image |
| * is created as if the VkImageFormatListCreateInfo structure were |
| * not included in the pNext chain of VkImageCreateInfo. |
| */ |
| if (!((const VkImageFormatListCreateInfo *)src)->viewFormatCount) |
| break; |
| |
| memcpy(&info->list, src, sizeof(info->list)); |
| pnext = &info->list; |
| |
| /* need a deep copy for view formats array */ |
| const size_t size = sizeof(VkFormat) * info->list.viewFormatCount; |
| VkFormat *view_formats = vk_zalloc( |
| alloc, size, VN_DEFAULT_ALIGN, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); |
| if (!view_formats) { |
| vk_free(alloc, info); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| |
| memcpy(view_formats, |
| ((const VkImageFormatListCreateInfo *)src)->pViewFormats, |
| size); |
| info->list.pViewFormats = view_formats; |
| } break; |
| case VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO: |
| memcpy(&info->stencil, src, sizeof(info->stencil)); |
| pnext = &info->stencil; |
| break; |
| case VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID: { |
| const uint32_t drm_format = |
| (uint32_t)((const VkExternalFormatANDROID *)src)->externalFormat; |
| if (drm_format) { |
| info->create.format = |
| vn_android_drm_format_to_vk_format(drm_format); |
| info->from_external_format = true; |
| } |
| } break; |
| case VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHR: |
| img->wsi.is_wsi = true; |
| break; |
| default: |
| break; |
| } |
| |
| if (pnext) { |
| dst->pNext = pnext; |
| dst = pnext; |
| } |
| } |
| dst->pNext = NULL; |
| |
| img->deferred_info = info; |
| |
| return VK_SUCCESS; |
| } |
| |
| static void |
| vn_image_deferred_info_fini(struct vn_image *img, |
| const VkAllocationCallbacks *alloc) |
| { |
| if (!img->deferred_info) |
| return; |
| |
| if (img->deferred_info->list.pViewFormats) |
| vk_free(alloc, (void *)img->deferred_info->list.pViewFormats); |
| |
| vk_free(alloc, img->deferred_info); |
| } |
| |
| static VkResult |
| vn_image_init(struct vn_device *dev, |
| const VkImageCreateInfo *create_info, |
| struct vn_image *img) |
| { |
| VkDevice device = vn_device_to_handle(dev); |
| VkImage image = vn_image_to_handle(img); |
| VkResult result = VK_SUCCESS; |
| |
| img->sharing_mode = create_info->sharingMode; |
| |
| /* Check if mem reqs in cache. If found, make async call */ |
| uint8_t key[SHA1_DIGEST_LENGTH] = { 0 }; |
| const bool cacheable = vn_image_get_image_reqs_key(dev, create_info, key); |
| |
| if (cacheable && vn_image_init_reqs_from_cache(dev, img, key)) { |
| vn_async_vkCreateImage(dev->primary_ring, device, create_info, NULL, |
| &image); |
| return VK_SUCCESS; |
| } |
| |
| result = vn_call_vkCreateImage(dev->primary_ring, device, create_info, |
| NULL, &image); |
| if (result != VK_SUCCESS) |
| return result; |
| |
| const uint32_t plane_count = vn_image_get_plane_count(create_info); |
| vn_image_init_memory_requirements(img, dev, plane_count); |
| |
| if (cacheable) |
| vn_image_store_reqs_in_cache(dev, key, plane_count, img->requirements); |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult |
| vn_image_create(struct vn_device *dev, |
| const VkImageCreateInfo *create_info, |
| const VkAllocationCallbacks *alloc, |
| struct vn_image **out_img) |
| { |
| struct vn_image *img = |
| vk_image_create(&dev->base.base, create_info, alloc, sizeof(*img)); |
| if (!img) |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| |
| vn_object_set_id(img, vn_get_next_obj_id(), VK_OBJECT_TYPE_IMAGE); |
| |
| VkResult result = vn_image_init(dev, create_info, img); |
| if (result != VK_SUCCESS) { |
| vk_image_destroy(&dev->base.base, alloc, &img->base.base); |
| return result; |
| } |
| |
| *out_img = img; |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult |
| vn_image_init_deferred(struct vn_device *dev, |
| const VkImageCreateInfo *create_info, |
| struct vn_image *img) |
| { |
| VkResult result = vn_image_init(dev, create_info, img); |
| img->deferred_info->initialized = result == VK_SUCCESS; |
| return result; |
| } |
| |
| static VkResult |
| vn_image_create_deferred(struct vn_device *dev, |
| const VkImageCreateInfo *create_info, |
| const VkAllocationCallbacks *alloc, |
| struct vn_image **out_img) |
| { |
| struct vn_image *img = |
| vk_image_create(&dev->base.base, create_info, alloc, sizeof(*img)); |
| if (!img) |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| |
| vn_object_set_id(img, vn_get_next_obj_id(), VK_OBJECT_TYPE_IMAGE); |
| |
| VkResult result = vn_image_deferred_info_init(img, create_info, alloc); |
| if (result != VK_SUCCESS) { |
| vk_image_destroy(&dev->base.base, alloc, &img->base.base); |
| return result; |
| } |
| |
| *out_img = img; |
| |
| return VK_SUCCESS; |
| } |
| |
| struct vn_image_create_info { |
| VkImageCreateInfo create; |
| VkExternalMemoryImageCreateInfo external; |
| VkImageFormatListCreateInfo format_list; |
| VkImageStencilUsageCreateInfo stencil; |
| VkImageDrmFormatModifierListCreateInfoEXT modifier_list; |
| VkImageDrmFormatModifierExplicitCreateInfoEXT modifier_explicit; |
| }; |
| |
| static const VkImageCreateInfo * |
| vn_image_fix_create_info( |
| const VkImageCreateInfo *create_info, |
| const VkExternalMemoryHandleTypeFlagBits renderer_handle_type, |
| struct vn_image_create_info *local_info) |
| { |
| local_info->create = *create_info; |
| VkBaseOutStructure *cur = (void *)&local_info->create; |
| |
| vk_foreach_struct_const(src, create_info->pNext) { |
| void *next = NULL; |
| switch (src->sType) { |
| case VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO: |
| memcpy(&local_info->external, src, sizeof(local_info->external)); |
| local_info->external.handleTypes = renderer_handle_type; |
| next = &local_info->external; |
| break; |
| case VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO: |
| memcpy(&local_info->format_list, src, |
| sizeof(local_info->format_list)); |
| next = &local_info->format_list; |
| break; |
| case VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO: |
| memcpy(&local_info->stencil, src, sizeof(local_info->stencil)); |
| next = &local_info->stencil; |
| break; |
| case VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT: |
| memcpy(&local_info->modifier_list, src, |
| sizeof(local_info->modifier_list)); |
| next = &local_info->modifier_list; |
| break; |
| case VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT: |
| memcpy(&local_info->modifier_explicit, src, |
| sizeof(local_info->modifier_explicit)); |
| next = &local_info->modifier_explicit; |
| break; |
| default: |
| break; |
| } |
| |
| if (next) { |
| cur->pNext = next; |
| cur = next; |
| } |
| } |
| |
| cur->pNext = NULL; |
| |
| return &local_info->create; |
| } |
| |
| VkResult |
| vn_CreateImage(VkDevice device, |
| const VkImageCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkImage *pImage) |
| { |
| struct vn_device *dev = vn_device_from_handle(device); |
| const VkAllocationCallbacks *alloc = |
| pAllocator ? pAllocator : &dev->base.base.alloc; |
| const VkExternalMemoryHandleTypeFlagBits renderer_handle_type = |
| dev->physical_device->external_memory.renderer_handle_type; |
| struct vn_image *img; |
| VkResult result; |
| |
| const struct wsi_image_create_info *wsi_info = NULL; |
| const VkNativeBufferANDROID *anb_info = NULL; |
| const VkImageSwapchainCreateInfoKHR *swapchain_info = NULL; |
| const VkExternalMemoryImageCreateInfo *external_info = NULL; |
| bool ahb_info = false; |
| |
| vk_foreach_struct_const(pnext, pCreateInfo->pNext) { |
| switch ((uint32_t)pnext->sType) { |
| case VK_STRUCTURE_TYPE_WSI_IMAGE_CREATE_INFO_MESA: |
| wsi_info = (void *)pnext; |
| break; |
| case VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID: |
| anb_info = (void *)pnext; |
| break; |
| case VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHR: |
| swapchain_info = (void *)pnext; |
| if (!swapchain_info->swapchain) |
| swapchain_info = NULL; |
| break; |
| case VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO: |
| external_info = (void *)pnext; |
| if (!external_info->handleTypes) |
| external_info = NULL; |
| else if ( |
| external_info->handleTypes == |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID) |
| ahb_info = true; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| /* No need to fix external handle type for: |
| * - common wsi image: dma_buf is hard-coded in wsi_configure_native_image |
| * - common wsi image alias: it aligns with wsi_info on external handle |
| * - Android wsi image: VK_ANDROID_native_buffer involves no external info |
| * - AHB external image: deferred creation reconstructs external info |
| * |
| * Must fix the external handle type for: |
| * - non-AHB external image requesting handle types different from renderer |
| * |
| * Will have to fix more when renderer handle type is no longer dma_buf. |
| */ |
| if (wsi_info) { |
| assert(external_info->handleTypes == renderer_handle_type); |
| result = vn_wsi_create_image(dev, pCreateInfo, wsi_info, alloc, &img); |
| } else if (anb_info) { |
| result = |
| vn_android_image_from_anb(dev, pCreateInfo, anb_info, alloc, &img); |
| } else if (ahb_info) { |
| result = vn_image_create_deferred(dev, pCreateInfo, alloc, &img); |
| } else if (swapchain_info) { |
| #if DETECT_OS_ANDROID |
| result = vn_image_create_deferred(dev, pCreateInfo, alloc, &img); |
| #else |
| result = vn_wsi_create_image_from_swapchain( |
| dev, pCreateInfo, swapchain_info, alloc, &img); |
| #endif |
| } else { |
| struct vn_image_create_info local_info; |
| if (external_info && |
| external_info->handleTypes != renderer_handle_type) { |
| pCreateInfo = vn_image_fix_create_info( |
| pCreateInfo, renderer_handle_type, &local_info); |
| } |
| |
| result = vn_image_create(dev, pCreateInfo, alloc, &img); |
| } |
| |
| if (result != VK_SUCCESS) |
| return vn_error(dev->instance, result); |
| |
| *pImage = vn_image_to_handle(img); |
| return VK_SUCCESS; |
| } |
| |
| void |
| vn_DestroyImage(VkDevice device, |
| VkImage image, |
| const VkAllocationCallbacks *pAllocator) |
| { |
| struct vn_device *dev = vn_device_from_handle(device); |
| struct vn_image *img = vn_image_from_handle(image); |
| const VkAllocationCallbacks *alloc = |
| pAllocator ? pAllocator : &dev->base.base.alloc; |
| |
| if (!img) |
| return; |
| |
| if (img->wsi.memory && img->wsi.memory_owned) { |
| VkDeviceMemory mem_handle = vn_device_memory_to_handle(img->wsi.memory); |
| vn_FreeMemory(device, mem_handle, pAllocator); |
| } |
| |
| /* must not ask renderer to destroy uninitialized deferred image */ |
| if (!img->deferred_info || img->deferred_info->initialized) |
| vn_async_vkDestroyImage(dev->primary_ring, device, image, NULL); |
| |
| vn_image_deferred_info_fini(img, alloc); |
| |
| vk_image_destroy(&dev->base.base, alloc, &img->base.base); |
| } |
| |
| void |
| vn_GetImageMemoryRequirements2(VkDevice device, |
| const VkImageMemoryRequirementsInfo2 *pInfo, |
| VkMemoryRequirements2 *pMemoryRequirements) |
| { |
| const struct vn_image *img = vn_image_from_handle(pInfo->image); |
| |
| uint32_t plane = 0; |
| const VkImagePlaneMemoryRequirementsInfo *plane_info = |
| vk_find_struct_const(pInfo->pNext, |
| IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO); |
| if (plane_info) |
| plane = vn_image_get_plane(plane_info->planeAspect); |
| |
| vn_image_fill_reqs(&img->requirements[plane], pMemoryRequirements); |
| } |
| |
| void |
| vn_GetImageSparseMemoryRequirements2( |
| VkDevice device, |
| const VkImageSparseMemoryRequirementsInfo2 *pInfo, |
| uint32_t *pSparseMemoryRequirementCount, |
| VkSparseImageMemoryRequirements2 *pSparseMemoryRequirements) |
| { |
| struct vn_device *dev = vn_device_from_handle(device); |
| |
| /* see vn_GetPhysicalDeviceSparseImageFormatProperties2 */ |
| if (dev->physical_device->sparse_binding_disabled) { |
| *pSparseMemoryRequirementCount = 0; |
| return; |
| } |
| |
| /* TODO local or per-device cache */ |
| vn_call_vkGetImageSparseMemoryRequirements2( |
| dev->primary_ring, device, pInfo, pSparseMemoryRequirementCount, |
| pSparseMemoryRequirements); |
| } |
| |
| static VkResult |
| vn_image_bind_wsi_memory(struct vn_device *dev, |
| uint32_t count, |
| const VkBindImageMemoryInfo *infos) |
| { |
| STACK_ARRAY(VkBindImageMemoryInfo, local_infos, count); |
| typed_memcpy(local_infos, infos, count); |
| |
| for (uint32_t i = 0; i < count; i++) { |
| VkBindImageMemoryInfo *info = &local_infos[i]; |
| struct vn_image *img = vn_image_from_handle(info->image); |
| struct vn_device_memory *mem = |
| vn_device_memory_from_handle(info->memory); |
| |
| if (!mem) { |
| #if DETECT_OS_ANDROID |
| mem = vn_android_get_wsi_memory_from_bind_info(dev, info); |
| if (!mem) { |
| STACK_ARRAY_FINISH(local_infos); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| #else |
| const VkBindImageMemorySwapchainInfoKHR *swapchain_info = |
| vk_find_struct_const(info->pNext, |
| BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHR); |
| assert(img->wsi.is_wsi && swapchain_info); |
| |
| struct vn_image *swapchain_img = |
| vn_image_from_handle(wsi_common_get_image( |
| swapchain_info->swapchain, swapchain_info->imageIndex)); |
| mem = swapchain_img->wsi.memory; |
| #endif |
| info->memory = vn_device_memory_to_handle(mem); |
| } |
| assert(mem && info->memory != VK_NULL_HANDLE); |
| |
| #if DETECT_OS_ANDROID |
| assert(img->wsi.memory); |
| #else |
| assert(!img->wsi.memory); |
| img->wsi.memory = mem; |
| #endif |
| } |
| |
| vn_async_vkBindImageMemory2(dev->primary_ring, vn_device_to_handle(dev), |
| count, local_infos); |
| |
| STACK_ARRAY_FINISH(local_infos); |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult |
| vn_BindImageMemory2(VkDevice device, |
| uint32_t bindInfoCount, |
| const VkBindImageMemoryInfo *pBindInfos) |
| { |
| struct vn_device *dev = vn_device_from_handle(device); |
| |
| for (uint32_t i = 0; i < bindInfoCount; i++) { |
| struct vn_image *img = vn_image_from_handle(pBindInfos[i].image); |
| if (img->wsi.is_wsi) |
| return vn_image_bind_wsi_memory(dev, bindInfoCount, pBindInfos); |
| } |
| |
| vn_async_vkBindImageMemory2(dev->primary_ring, device, bindInfoCount, |
| pBindInfos); |
| return VK_SUCCESS; |
| } |
| |
| VkResult |
| vn_GetImageDrmFormatModifierPropertiesEXT( |
| VkDevice device, |
| VkImage image, |
| VkImageDrmFormatModifierPropertiesEXT *pProperties) |
| { |
| struct vn_device *dev = vn_device_from_handle(device); |
| |
| /* TODO local cache */ |
| return vn_call_vkGetImageDrmFormatModifierPropertiesEXT( |
| dev->primary_ring, device, image, pProperties); |
| } |
| |
| void |
| vn_GetImageSubresourceLayout(VkDevice device, |
| VkImage image, |
| const VkImageSubresource *pSubresource, |
| VkSubresourceLayout *pLayout) |
| { |
| struct vn_device *dev = vn_device_from_handle(device); |
| struct vn_image *img = vn_image_from_handle(image); |
| |
| /* override aspect mask for wsi/ahb images with tiling modifier */ |
| VkImageSubresource local_subresource; |
| if ((img->wsi.is_wsi && img->wsi.tiling_override == |
| VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) || |
| img->deferred_info) { |
| VkImageAspectFlags aspect = pSubresource->aspectMask; |
| switch (aspect) { |
| case VK_IMAGE_ASPECT_COLOR_BIT: |
| case VK_IMAGE_ASPECT_DEPTH_BIT: |
| case VK_IMAGE_ASPECT_STENCIL_BIT: |
| case VK_IMAGE_ASPECT_PLANE_0_BIT: |
| aspect = VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT; |
| break; |
| case VK_IMAGE_ASPECT_PLANE_1_BIT: |
| aspect = VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT; |
| break; |
| case VK_IMAGE_ASPECT_PLANE_2_BIT: |
| aspect = VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT; |
| break; |
| default: |
| break; |
| } |
| |
| /* only handle supported aspect override */ |
| if (aspect != pSubresource->aspectMask) { |
| local_subresource = *pSubresource; |
| local_subresource.aspectMask = aspect; |
| pSubresource = &local_subresource; |
| } |
| } |
| |
| /* TODO local cache */ |
| vn_call_vkGetImageSubresourceLayout(dev->primary_ring, device, image, |
| pSubresource, pLayout); |
| } |
| |
| /* image view commands */ |
| |
| VkResult |
| vn_CreateImageView(VkDevice device, |
| const VkImageViewCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkImageView *pView) |
| { |
| struct vn_device *dev = vn_device_from_handle(device); |
| struct vn_image *img = vn_image_from_handle(pCreateInfo->image); |
| const VkAllocationCallbacks *alloc = |
| pAllocator ? pAllocator : &dev->base.base.alloc; |
| |
| VkImageViewCreateInfo local_info; |
| if (img->deferred_info && img->deferred_info->from_external_format) { |
| assert(pCreateInfo->format == VK_FORMAT_UNDEFINED); |
| |
| local_info = *pCreateInfo; |
| local_info.format = img->deferred_info->create.format; |
| pCreateInfo = &local_info; |
| |
| assert(pCreateInfo->format != VK_FORMAT_UNDEFINED); |
| } |
| |
| struct vn_image_view *view = |
| vk_zalloc(alloc, sizeof(*view), VN_DEFAULT_ALIGN, |
| VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); |
| if (!view) |
| return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY); |
| |
| vn_object_base_init(&view->base, VK_OBJECT_TYPE_IMAGE_VIEW, &dev->base); |
| view->image = img; |
| |
| VkImageView view_handle = vn_image_view_to_handle(view); |
| vn_async_vkCreateImageView(dev->primary_ring, device, pCreateInfo, NULL, |
| &view_handle); |
| |
| *pView = view_handle; |
| |
| return VK_SUCCESS; |
| } |
| |
| void |
| vn_DestroyImageView(VkDevice device, |
| VkImageView imageView, |
| const VkAllocationCallbacks *pAllocator) |
| { |
| struct vn_device *dev = vn_device_from_handle(device); |
| struct vn_image_view *view = vn_image_view_from_handle(imageView); |
| const VkAllocationCallbacks *alloc = |
| pAllocator ? pAllocator : &dev->base.base.alloc; |
| |
| if (!view) |
| return; |
| |
| vn_async_vkDestroyImageView(dev->primary_ring, device, imageView, NULL); |
| |
| vn_object_base_fini(&view->base); |
| vk_free(alloc, view); |
| } |
| |
| /* sampler commands */ |
| |
| VkResult |
| vn_CreateSampler(VkDevice device, |
| const VkSamplerCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkSampler *pSampler) |
| { |
| struct vn_device *dev = vn_device_from_handle(device); |
| const VkAllocationCallbacks *alloc = |
| pAllocator ? pAllocator : &dev->base.base.alloc; |
| |
| struct vn_sampler *sampler = |
| vk_zalloc(alloc, sizeof(*sampler), VN_DEFAULT_ALIGN, |
| VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); |
| if (!sampler) |
| return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY); |
| |
| vn_object_base_init(&sampler->base, VK_OBJECT_TYPE_SAMPLER, &dev->base); |
| |
| VkSampler sampler_handle = vn_sampler_to_handle(sampler); |
| vn_async_vkCreateSampler(dev->primary_ring, device, pCreateInfo, NULL, |
| &sampler_handle); |
| |
| *pSampler = sampler_handle; |
| |
| return VK_SUCCESS; |
| } |
| |
| void |
| vn_DestroySampler(VkDevice device, |
| VkSampler _sampler, |
| const VkAllocationCallbacks *pAllocator) |
| { |
| struct vn_device *dev = vn_device_from_handle(device); |
| struct vn_sampler *sampler = vn_sampler_from_handle(_sampler); |
| const VkAllocationCallbacks *alloc = |
| pAllocator ? pAllocator : &dev->base.base.alloc; |
| |
| if (!sampler) |
| return; |
| |
| vn_async_vkDestroySampler(dev->primary_ring, device, _sampler, NULL); |
| |
| vn_object_base_fini(&sampler->base); |
| vk_free(alloc, sampler); |
| } |
| |
| /* sampler YCbCr conversion commands */ |
| |
| VkResult |
| vn_CreateSamplerYcbcrConversion( |
| VkDevice device, |
| const VkSamplerYcbcrConversionCreateInfo *pCreateInfo, |
| const VkAllocationCallbacks *pAllocator, |
| VkSamplerYcbcrConversion *pYcbcrConversion) |
| { |
| struct vn_device *dev = vn_device_from_handle(device); |
| const VkAllocationCallbacks *alloc = |
| pAllocator ? pAllocator : &dev->base.base.alloc; |
| const VkExternalFormatANDROID *ext_info = |
| vk_find_struct_const(pCreateInfo->pNext, EXTERNAL_FORMAT_ANDROID); |
| |
| VkSamplerYcbcrConversionCreateInfo local_info; |
| if (ext_info && ext_info->externalFormat) { |
| assert(pCreateInfo->format == VK_FORMAT_UNDEFINED); |
| |
| local_info = *pCreateInfo; |
| local_info.format = |
| vn_android_drm_format_to_vk_format(ext_info->externalFormat); |
| local_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; |
| local_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; |
| local_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; |
| local_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; |
| pCreateInfo = &local_info; |
| |
| assert(pCreateInfo->format != VK_FORMAT_UNDEFINED); |
| } |
| |
| struct vn_sampler_ycbcr_conversion *conv = |
| vk_zalloc(alloc, sizeof(*conv), VN_DEFAULT_ALIGN, |
| VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); |
| if (!conv) |
| return vn_error(dev->instance, VK_ERROR_OUT_OF_HOST_MEMORY); |
| |
| vn_object_base_init(&conv->base, VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION, |
| &dev->base); |
| |
| VkSamplerYcbcrConversion conv_handle = |
| vn_sampler_ycbcr_conversion_to_handle(conv); |
| vn_async_vkCreateSamplerYcbcrConversion(dev->primary_ring, device, |
| pCreateInfo, NULL, &conv_handle); |
| |
| *pYcbcrConversion = conv_handle; |
| |
| return VK_SUCCESS; |
| } |
| |
| void |
| vn_DestroySamplerYcbcrConversion(VkDevice device, |
| VkSamplerYcbcrConversion ycbcrConversion, |
| const VkAllocationCallbacks *pAllocator) |
| { |
| struct vn_device *dev = vn_device_from_handle(device); |
| struct vn_sampler_ycbcr_conversion *conv = |
| vn_sampler_ycbcr_conversion_from_handle(ycbcrConversion); |
| const VkAllocationCallbacks *alloc = |
| pAllocator ? pAllocator : &dev->base.base.alloc; |
| |
| if (!conv) |
| return; |
| |
| vn_async_vkDestroySamplerYcbcrConversion(dev->primary_ring, device, |
| ycbcrConversion, NULL); |
| |
| vn_object_base_fini(&conv->base); |
| vk_free(alloc, conv); |
| } |
| |
| void |
| vn_GetDeviceImageMemoryRequirements( |
| VkDevice device, |
| const VkDeviceImageMemoryRequirements *pInfo, |
| VkMemoryRequirements2 *pMemoryRequirements) |
| { |
| struct vn_device *dev = vn_device_from_handle(device); |
| |
| uint8_t key[SHA1_DIGEST_LENGTH] = { 0 }; |
| const bool cacheable = |
| vn_image_get_image_reqs_key(dev, pInfo->pCreateInfo, key); |
| |
| if (cacheable) { |
| uint32_t plane = 0; |
| if (pInfo->pCreateInfo->flags & VK_IMAGE_CREATE_DISJOINT_BIT) |
| vn_image_get_plane(pInfo->planeAspect); |
| |
| const struct vn_image_memory_requirements *cached_reqs = |
| vn_image_get_reqs_from_cache(dev, key, plane); |
| if (cached_reqs) { |
| vn_image_fill_reqs(cached_reqs, pMemoryRequirements); |
| return; |
| } |
| |
| const uint32_t plane_count = |
| vn_image_get_plane_count(pInfo->pCreateInfo); |
| STACK_ARRAY(VkDeviceImageMemoryRequirements, req_info, plane_count); |
| STACK_ARRAY(struct vn_image_memory_requirements, reqs, plane_count); |
| |
| /* Retrieve reqs for all planes so the cache entry is complete */ |
| for (uint32_t i = 0; i < plane_count; i++) { |
| req_info[i].sType = |
| VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS; |
| req_info[i].pNext = NULL; |
| req_info[i].pCreateInfo = pInfo->pCreateInfo; |
| req_info[i].planeAspect = VK_IMAGE_ASPECT_PLANE_0_BIT << i; |
| |
| reqs[i].memory.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2; |
| reqs[i].memory.pNext = &reqs[i].dedicated; |
| reqs[i].dedicated.sType = |
| VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS; |
| reqs[i].dedicated.pNext = NULL; |
| |
| vn_call_vkGetDeviceImageMemoryRequirements( |
| dev->primary_ring, device, &req_info[i], &reqs[i].memory); |
| } |
| vn_image_fill_reqs(&reqs[plane], pMemoryRequirements); |
| vn_image_store_reqs_in_cache(dev, key, plane_count, reqs); |
| |
| STACK_ARRAY_FINISH(req_info); |
| STACK_ARRAY_FINISH(reqs); |
| } else { |
| vn_call_vkGetDeviceImageMemoryRequirements(dev->primary_ring, device, |
| pInfo, pMemoryRequirements); |
| } |
| } |
| |
| void |
| vn_GetDeviceImageSparseMemoryRequirements( |
| VkDevice device, |
| const VkDeviceImageMemoryRequirements *pInfo, |
| uint32_t *pSparseMemoryRequirementCount, |
| VkSparseImageMemoryRequirements2 *pSparseMemoryRequirements) |
| { |
| struct vn_device *dev = vn_device_from_handle(device); |
| |
| /* see vn_GetPhysicalDeviceSparseImageFormatProperties2 */ |
| if (dev->physical_device->sparse_binding_disabled) { |
| *pSparseMemoryRequirementCount = 0; |
| return; |
| } |
| |
| /* TODO per-device cache */ |
| vn_call_vkGetDeviceImageSparseMemoryRequirements( |
| dev->primary_ring, device, pInfo, pSparseMemoryRequirementCount, |
| pSparseMemoryRequirements); |
| } |
| |
| void |
| vn_GetDeviceImageSubresourceLayoutKHR(VkDevice device, |
| const VkDeviceImageSubresourceInfoKHR *pInfo, |
| VkSubresourceLayout2KHR *pLayout) |
| { |
| struct vn_device *dev = vn_device_from_handle(device); |
| |
| /* TODO per-device cache */ |
| vn_call_vkGetDeviceImageSubresourceLayoutKHR( |
| dev->primary_ring, device, pInfo, pLayout); |
| } |
| |
| void |
| vn_GetImageSubresourceLayout2KHR(VkDevice device, |
| VkImage image, |
| const VkImageSubresource2KHR *pSubresource, |
| VkSubresourceLayout2KHR *pLayout) |
| { |
| struct vn_device *dev = vn_device_from_handle(device); |
| struct vn_image *img = vn_image_from_handle(image); |
| |
| /* override aspect mask for wsi/ahb images with tiling modifier */ |
| VkImageSubresource2KHR local_subresource; |
| if ((img->wsi.is_wsi && img->wsi.tiling_override == |
| VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) || |
| img->deferred_info) { |
| VkImageAspectFlags aspect = pSubresource->imageSubresource.aspectMask; |
| switch (aspect) { |
| case VK_IMAGE_ASPECT_COLOR_BIT: |
| case VK_IMAGE_ASPECT_DEPTH_BIT: |
| case VK_IMAGE_ASPECT_STENCIL_BIT: |
| case VK_IMAGE_ASPECT_PLANE_0_BIT: |
| aspect = VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT; |
| break; |
| case VK_IMAGE_ASPECT_PLANE_1_BIT: |
| aspect = VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT; |
| break; |
| case VK_IMAGE_ASPECT_PLANE_2_BIT: |
| aspect = VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT; |
| break; |
| default: |
| break; |
| } |
| |
| /* only handle supported aspect override */ |
| if (aspect != pSubresource->imageSubresource.aspectMask) { |
| local_subresource = *pSubresource; |
| local_subresource.imageSubresource.aspectMask = aspect; |
| pSubresource = &local_subresource; |
| } |
| } |
| |
| vn_call_vkGetImageSubresourceLayout2KHR( |
| dev->primary_ring, device, image, pSubresource, pLayout); |
| } |