mesa/src/virtio/vulkan/vn_image.c

1203 lines
39 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_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;
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) {
result = vn_wsi_create_image_from_swapchain(
dev, pCreateInfo, swapchain_info, alloc, &img);
} 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
/* TODO handle VkNativeBufferANDROID when we bump up
* VN_ANDROID_NATIVE_BUFFER_SPEC_VERSION
*/
unreachable("VkBindImageMemoryInfo with no 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;
info->memory = vn_device_memory_to_handle(mem);
#endif
}
assert(mem && info->memory != VK_NULL_HANDLE);
if (img->wsi.is_wsi)
img->wsi.memory = mem;
}
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++) {
if (pBindInfos[i].memory == VK_NULL_HANDLE)
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);
}