| /* |
| * Copyright © 2019 Google, LLC |
| * |
| * 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 "anv_magma.h" |
| #include "anv_private.h" |
| #include "isl/isl.h" |
| #include "magma_sysmem.h" |
| #include "vk_util.h" |
| |
| #if VK_USE_PLATFORM_FUCHSIA |
| |
| struct anv_buffer_collection { |
| magma_buffer_collection_t buffer_collection; |
| }; |
| |
| ANV_DEFINE_HANDLE_CASTS(anv_buffer_collection, VkBufferCollectionFUCHSIA) |
| |
| VkResult anv_CreateBufferCollectionFUCHSIA(VkDevice vk_device, |
| const VkBufferCollectionCreateInfoFUCHSIA* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, |
| VkBufferCollectionFUCHSIA* pCollection) |
| { |
| ANV_FROM_HANDLE(anv_device, device, vk_device); |
| |
| magma_sysmem_connection_t sysmem_connection; |
| magma_status_t status = AnvMagmaGetSysmemConnection(device->connection, &sysmem_connection); |
| if (status != MAGMA_STATUS_OK) |
| return ANV_MAGMA_DRET(VK_ERROR_DEVICE_LOST); |
| |
| magma_buffer_collection_t magma_buffer_collection; |
| status = magma_buffer_collection_import(sysmem_connection, pCreateInfo->collectionToken, |
| &magma_buffer_collection); |
| if (status != MAGMA_STATUS_OK) |
| return ANV_MAGMA_DRET(VK_ERROR_INVALID_EXTERNAL_HANDLE); |
| |
| struct anv_buffer_collection* buffer_collection = |
| vk_alloc2(&device->alloc, pAllocator, sizeof(*buffer_collection), 8, |
| VK_SYSTEM_ALLOCATION_SCOPE_DEVICE); |
| buffer_collection->buffer_collection = magma_buffer_collection; |
| *pCollection = anv_buffer_collection_to_handle(buffer_collection); |
| |
| return VK_SUCCESS; |
| } |
| |
| void anv_DestroyBufferCollectionFUCHSIA(VkDevice vk_device, VkBufferCollectionFUCHSIA vk_collection, |
| const VkAllocationCallbacks* pAllocator) |
| { |
| ANV_FROM_HANDLE(anv_device, device, vk_device); |
| ANV_FROM_HANDLE(anv_buffer_collection, buffer_collection, vk_collection); |
| |
| magma_sysmem_connection_t sysmem_connection; |
| magma_status_t status = AnvMagmaGetSysmemConnection(device->connection, &sysmem_connection); |
| if (status != MAGMA_STATUS_OK) |
| return; |
| |
| magma_buffer_collection_release(sysmem_connection, buffer_collection->buffer_collection); |
| vk_free2(&device->alloc, pAllocator, buffer_collection); |
| } |
| |
| static VkResult get_image_format_constraints( |
| VkDevice vk_device, VkFormat format, const VkImageCreateInfo* pImageInfo, |
| magma_image_format_constraints_t* image_constraints_out, isl_tiling_flags_t isl_tiling_flags) |
| { |
| ANV_FROM_HANDLE(anv_device, device, vk_device); |
| |
| const struct anv_format_plane plane_format = |
| anv_get_format_plane(&device->info, format, VK_IMAGE_ASPECT_COLOR_BIT, pImageInfo->tiling); |
| |
| const isl_surf_usage_flags_t isl_surf_usage = |
| choose_isl_surf_usage(pImageInfo->flags, // vk_create_flags |
| pImageInfo->usage, // vk_usage |
| 0, // isl_extra_usage |
| VK_IMAGE_ASPECT_COLOR_BIT); |
| enum isl_surf_dim dim; |
| switch (pImageInfo->imageType) { |
| case VK_IMAGE_TYPE_1D: |
| dim = ISL_SURF_DIM_1D; |
| break; |
| case VK_IMAGE_TYPE_2D: |
| dim = ISL_SURF_DIM_2D; |
| break; |
| default: |
| return ANV_MAGMA_DRET(VK_ERROR_FORMAT_NOT_SUPPORTED); |
| } |
| |
| struct isl_surf_init_info isl_surf_init_info = { |
| .dim = dim, |
| .format = plane_format.isl_format, |
| .width = pImageInfo->extent.width / plane_format.denominator_scales[0], |
| .height = pImageInfo->extent.height / plane_format.denominator_scales[1], |
| .depth = pImageInfo->extent.depth, |
| .levels = pImageInfo->mipLevels, |
| .array_len = pImageInfo->arrayLayers, |
| .samples = pImageInfo->samples, |
| .min_alignment_B = 0, |
| .row_pitch_B = 0, |
| .usage = isl_surf_usage, |
| .tiling_flags = isl_tiling_flags}; |
| |
| struct isl_surf isl_surf; |
| if (!isl_surf_init_s(&device->isl_dev, &isl_surf, &isl_surf_init_info)) |
| return ANV_MAGMA_DRET(VK_ERROR_FORMAT_NOT_SUPPORTED); |
| |
| assert(pImageInfo->extent.width); |
| magma_image_format_constraints_t image_constraints = {.width = pImageInfo->extent.width, |
| .height = pImageInfo->extent.height, |
| .layers = 1, |
| .bytes_per_row_divisor = 1, |
| .min_bytes_per_row = isl_surf.row_pitch_B}; |
| |
| switch (isl_surf.tiling) { |
| case ISL_TILING_LINEAR: |
| image_constraints.has_format_modifier = false; |
| break; |
| case ISL_TILING_X: |
| image_constraints.has_format_modifier = true; |
| image_constraints.format_modifier = MAGMA_FORMAT_MODIFIER_INTEL_X_TILED; |
| break; |
| case ISL_TILING_Y0: |
| image_constraints.has_format_modifier = true; |
| image_constraints.format_modifier = MAGMA_FORMAT_MODIFIER_INTEL_Y_TILED; |
| break; |
| case ISL_TILING_Yf: |
| image_constraints.has_format_modifier = true; |
| image_constraints.format_modifier = MAGMA_FORMAT_MODIFIER_INTEL_YF_TILED; |
| break; |
| default: |
| return ANV_MAGMA_DRET(VK_ERROR_FORMAT_NOT_SUPPORTED); |
| } |
| |
| switch (format) { |
| case VK_FORMAT_B8G8R8A8_SINT: |
| case VK_FORMAT_B8G8R8A8_UNORM: |
| case VK_FORMAT_B8G8R8A8_SRGB: |
| case VK_FORMAT_B8G8R8A8_SNORM: |
| case VK_FORMAT_B8G8R8A8_SSCALED: |
| case VK_FORMAT_B8G8R8A8_USCALED: |
| image_constraints.image_format = MAGMA_FORMAT_BGRA32; |
| break; |
| case VK_FORMAT_R8G8B8A8_SINT: |
| case VK_FORMAT_R8G8B8A8_UNORM: |
| case VK_FORMAT_R8G8B8A8_SRGB: |
| case VK_FORMAT_R8G8B8A8_SNORM: |
| case VK_FORMAT_R8G8B8A8_SSCALED: |
| case VK_FORMAT_R8G8B8A8_USCALED: |
| image_constraints.image_format = MAGMA_FORMAT_R8G8B8A8; |
| break; |
| case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM: |
| image_constraints.image_format = MAGMA_FORMAT_NV12; |
| break; |
| case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM: |
| image_constraints.image_format = MAGMA_FORMAT_I420; |
| break; |
| default: |
| return ANV_MAGMA_DRET(VK_ERROR_FORMAT_NOT_SUPPORTED); |
| } |
| |
| *image_constraints_out = image_constraints; |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult anv_SetBufferCollectionConstraintsFUCHSIA(VkDevice vk_device, |
| VkBufferCollectionFUCHSIA vk_collection, |
| const VkImageCreateInfo* pImageInfo) |
| { |
| ANV_FROM_HANDLE(anv_device, device, vk_device); |
| ANV_FROM_HANDLE(anv_buffer_collection, buffer_collection, vk_collection); |
| |
| magma_sysmem_connection_t sysmem_connection; |
| magma_status_t status = AnvMagmaGetSysmemConnection(device->connection, &sysmem_connection); |
| if (status != MAGMA_STATUS_OK) |
| return ANV_MAGMA_DRET(VK_ERROR_DEVICE_LOST); |
| |
| uint32_t slot_count = 0; |
| VkResult result; |
| |
| const VkFormat kDefaultFormatList[] = {VK_FORMAT_B8G8R8A8_UNORM, |
| VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, |
| VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM}; |
| magma_image_format_constraints_t image_constraints[2 * ARRAY_SIZE(kDefaultFormatList)]; |
| |
| // Sysmem is currently limited to a maximum of 32 image constraints. |
| assert(ARRAY_SIZE(image_constraints) <= 32); |
| |
| const VkFormat* format_list_to_try = &pImageInfo->format; |
| uint32_t num_formats_to_try = 1; |
| if (pImageInfo->format == VK_FORMAT_UNDEFINED) { |
| format_list_to_try = kDefaultFormatList; |
| num_formats_to_try = ARRAY_SIZE(kDefaultFormatList); |
| } |
| |
| for (uint32_t i = 0; i < num_formats_to_try; ++i) { |
| VkFormat format = format_list_to_try[i]; |
| assert(slot_count < ARRAY_SIZE(image_constraints)); |
| switch (pImageInfo->tiling) { |
| case VK_IMAGE_TILING_OPTIMAL: { |
| // We always support X tiled for scanout but there may be a more optimal tiling format. |
| result = get_image_format_constraints(vk_device, format, pImageInfo, |
| &image_constraints[slot_count], ISL_TILING_X_BIT); |
| if (result != VK_SUCCESS) { |
| break; |
| } |
| |
| if (image_constraints[slot_count].image_format == MAGMA_FORMAT_NV12 || |
| image_constraints[slot_count].image_format == MAGMA_FORMAT_I420) { |
| // Sysmem can't handle tiled YUV. |
| result = |
| get_image_format_constraints(vk_device, format, pImageInfo, |
| &image_constraints[slot_count], ISL_TILING_LINEAR_BIT); |
| if (result == VK_SUCCESS) { |
| slot_count++; |
| } |
| } else { |
| assert(image_constraints[slot_count].has_format_modifier); |
| slot_count++; |
| assert(slot_count < ARRAY_SIZE(image_constraints)); |
| result = get_image_format_constraints( |
| vk_device, format, pImageInfo, &image_constraints[slot_count], ISL_TILING_ANY_MASK); |
| if (result == VK_SUCCESS) { |
| assert(image_constraints[slot_count].has_format_modifier); |
| if (image_constraints[slot_count].format_modifier != |
| image_constraints[slot_count - 1].format_modifier) { |
| slot_count++; |
| } |
| } |
| } |
| break; |
| } |
| case VK_IMAGE_TILING_LINEAR: { |
| result = get_image_format_constraints( |
| vk_device, format, pImageInfo, &image_constraints[slot_count], ISL_TILING_LINEAR_BIT); |
| if (result == VK_SUCCESS) { |
| assert(!image_constraints[slot_count].has_format_modifier); |
| slot_count++; |
| } |
| break; |
| } |
| default: |
| return ANV_MAGMA_DRET(VK_ERROR_FORMAT_NOT_SUPPORTED); |
| } |
| |
| if (result != VK_SUCCESS) |
| return result; |
| } |
| |
| if (slot_count == 0) |
| return ANV_MAGMA_DRET(VK_ERROR_FORMAT_NOT_SUPPORTED); |
| |
| magma_buffer_format_constraints_t format_constraints = {.count = 1, |
| .usage = 0, |
| .secure_permitted = false, |
| .secure_required = false, |
| .ram_domain_supported = true, |
| .cpu_domain_supported = true, |
| .min_size_bytes = 0}; |
| |
| magma_sysmem_buffer_constraints_t constraints; |
| status = magma_buffer_constraints_create(sysmem_connection, &format_constraints, &constraints); |
| if (status != MAGMA_STATUS_OK) |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| |
| for (uint32_t slot = 0; slot < slot_count; slot++) { |
| assert(slot < sizeof(image_constraints) / sizeof(image_constraints[0])); |
| status = magma_buffer_constraints_set_format(sysmem_connection, constraints, slot, |
| &image_constraints[slot]); |
| if (status != MAGMA_STATUS_OK) { |
| break; |
| } |
| } |
| |
| if (status == MAGMA_STATUS_OK) { |
| status = magma_buffer_collection_set_constraints( |
| sysmem_connection, buffer_collection->buffer_collection, constraints); |
| } |
| |
| magma_buffer_constraints_release(sysmem_connection, constraints); |
| |
| if (status != MAGMA_STATUS_OK) |
| return VK_ERROR_FORMAT_NOT_SUPPORTED; |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult |
| anv_SetBufferCollectionBufferConstraintsFUCHSIA(VkDevice vk_device, |
| VkBufferCollectionFUCHSIA vk_collection, |
| const VkBufferConstraintsInfoFUCHSIA* pConstraints) |
| { |
| ANV_FROM_HANDLE(anv_device, device, vk_device); |
| ANV_FROM_HANDLE(anv_buffer_collection, buffer_collection, vk_collection); |
| |
| magma_sysmem_connection_t sysmem_connection; |
| magma_status_t status = AnvMagmaGetSysmemConnection(device->connection, &sysmem_connection); |
| if (status != MAGMA_STATUS_OK) |
| return ANV_MAGMA_DRET(VK_ERROR_DEVICE_LOST); |
| |
| magma_buffer_format_constraints_t format_constraints = { |
| .count = pConstraints->minCount, |
| .usage = 0, |
| .secure_permitted = false, |
| .secure_required = false, |
| .ram_domain_supported = true, |
| .cpu_domain_supported = true, |
| .min_size_bytes = pConstraints->pBufferCreateInfo->size}; |
| |
| magma_sysmem_buffer_constraints_t constraints; |
| status = magma_buffer_constraints_create(sysmem_connection, &format_constraints, &constraints); |
| if (status != MAGMA_STATUS_OK) |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| |
| status = magma_buffer_collection_set_constraints( |
| sysmem_connection, buffer_collection->buffer_collection, constraints); |
| |
| magma_buffer_constraints_release(sysmem_connection, constraints); |
| |
| if (status != MAGMA_STATUS_OK) |
| return VK_ERROR_FORMAT_NOT_SUPPORTED; |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult anv_GetBufferCollectionPropertiesFUCHSIA(VkDevice vk_device, |
| VkBufferCollectionFUCHSIA vk_collection, |
| VkBufferCollectionPropertiesFUCHSIA* pProperties) |
| { |
| ANV_FROM_HANDLE(anv_device, device, vk_device); |
| ANV_FROM_HANDLE(anv_buffer_collection, buffer_collection, vk_collection); |
| |
| magma_sysmem_connection_t sysmem_connection; |
| magma_status_t status = AnvMagmaGetSysmemConnection(device->connection, &sysmem_connection); |
| if (status != MAGMA_STATUS_OK) |
| return ANV_MAGMA_DRET(VK_ERROR_DEVICE_LOST); |
| magma_buffer_format_description_t description; |
| status = magma_sysmem_get_description_from_collection( |
| sysmem_connection, buffer_collection->buffer_collection, &description); |
| if (status != MAGMA_STATUS_OK) |
| return ANV_MAGMA_DRET(VK_ERROR_DEVICE_LOST); |
| |
| status = magma_get_buffer_count(description, &pProperties->count); |
| magma_buffer_format_description_release(description); |
| |
| if (status != MAGMA_STATUS_OK) |
| return ANV_MAGMA_DRET(VK_ERROR_DEVICE_LOST); |
| |
| struct anv_physical_device* pdevice = &device->instance->physicalDevice; |
| // All memory types supported. |
| pProperties->memoryTypeBits = (1ull << pdevice->memory.type_count) - 1; |
| return VK_SUCCESS; |
| } |
| |
| // Takes ownership of the buffer format description. |
| static VkResult anv_image_params_from_description( |
| magma_buffer_format_description_t description, uint32_t width, uint32_t height, |
| struct anv_fuchsia_image_plane_params params_out[MAGMA_MAX_IMAGE_PLANES], |
| isl_tiling_flags_t* tiling_flags_out, bool* not_cache_coherent_out) |
| { |
| magma_bool_t has_format_modifier; |
| uint64_t format_modifier; |
| magma_status_t status = MAGMA_STATUS_OK; |
| if (params_out) { |
| magma_image_plane_t planes[MAGMA_MAX_IMAGE_PLANES]; |
| |
| status = magma_get_buffer_format_plane_info_with_size(description, width, height, planes); |
| if (status == MAGMA_STATUS_OK) { |
| for (uint32_t i = 0; i < MAGMA_MAX_IMAGE_PLANES; i++) { |
| params_out[i].bytes_per_row = planes[i].bytes_per_row; |
| params_out[i].byte_offset = planes[i].byte_offset; |
| } |
| } |
| } |
| uint32_t coherency_domain; |
| if (status == MAGMA_STATUS_OK) { |
| status = magma_get_buffer_coherency_domain(description, &coherency_domain); |
| } |
| if (status == MAGMA_STATUS_OK) { |
| status = |
| magma_get_buffer_format_modifier(description, &has_format_modifier, &format_modifier); |
| } |
| |
| magma_buffer_format_description_release(description); |
| |
| if (status != MAGMA_STATUS_OK) |
| return ANV_MAGMA_DRET(VK_ERROR_FORMAT_NOT_SUPPORTED); |
| |
| if (not_cache_coherent_out) { |
| *not_cache_coherent_out = coherency_domain == MAGMA_COHERENCY_DOMAIN_RAM; |
| } |
| |
| if (tiling_flags_out) { |
| *tiling_flags_out = ISL_TILING_LINEAR_BIT; |
| |
| if (has_format_modifier) { |
| switch (format_modifier) { |
| case MAGMA_FORMAT_MODIFIER_INTEL_X_TILED: |
| *tiling_flags_out = ISL_TILING_X_BIT; |
| break; |
| case MAGMA_FORMAT_MODIFIER_INTEL_Y_TILED: |
| *tiling_flags_out = ISL_TILING_Y0_BIT; |
| break; |
| case MAGMA_FORMAT_MODIFIER_INTEL_YF_TILED: |
| *tiling_flags_out = ISL_TILING_Yf_BIT; |
| break; |
| case MAGMA_FORMAT_MODIFIER_LINEAR: |
| break; |
| default: |
| assert(false); |
| } |
| } |
| } |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult anv_image_params_from_fuchsia_image( |
| VkDevice vk_device, const VkImageCreateInfo* pCreateInfo, |
| struct anv_fuchsia_image_plane_params params_out[MAGMA_MAX_IMAGE_PLANES], |
| isl_tiling_flags_t* tiling_flags_out, bool* not_cache_coherent_out) |
| { |
| assert(pCreateInfo->arrayLayers == 1); |
| assert(pCreateInfo->extent.depth == 1); |
| |
| const struct VkFuchsiaImageFormatFUCHSIA* image_format_fuchsia = |
| vk_find_struct_const(pCreateInfo->pNext, FUCHSIA_IMAGE_FORMAT_FUCHSIA); |
| assert(image_format_fuchsia); |
| |
| magma_buffer_format_description_t description; |
| magma_status_t status; |
| status = magma_get_buffer_format_description( |
| image_format_fuchsia->imageFormat, image_format_fuchsia->imageFormatSize, &description); |
| if (status != MAGMA_STATUS_OK) |
| return ANV_MAGMA_DRET(VK_ERROR_FORMAT_NOT_SUPPORTED); |
| |
| return anv_image_params_from_description(description, pCreateInfo->extent.width, |
| pCreateInfo->extent.height, params_out, |
| tiling_flags_out, not_cache_coherent_out); |
| } |
| |
| VkResult anv_image_params_from_buffer_collection( |
| VkDevice vk_device, VkBufferCollectionFUCHSIA vk_collection, const VkExtent3D* extent, |
| struct anv_fuchsia_image_plane_params params_out[MAGMA_MAX_IMAGE_PLANES], |
| isl_tiling_flags_t* tiling_flags_out, bool* not_cache_coherent_out) |
| { |
| ANV_FROM_HANDLE(anv_device, device, vk_device); |
| ANV_FROM_HANDLE(anv_buffer_collection, buffer_collection, vk_collection); |
| |
| magma_sysmem_connection_t sysmem_connection; |
| magma_status_t status = AnvMagmaGetSysmemConnection(device->connection, &sysmem_connection); |
| if (status != MAGMA_STATUS_OK) |
| return ANV_MAGMA_DRET(VK_ERROR_DEVICE_LOST); |
| magma_buffer_format_description_t description; |
| status = magma_sysmem_get_description_from_collection( |
| sysmem_connection, buffer_collection->buffer_collection, &description); |
| if (status != MAGMA_STATUS_OK) |
| return ANV_MAGMA_DRET(VK_ERROR_DEVICE_LOST); |
| uint32_t width = extent ? extent->width : 0u; |
| uint32_t height = extent ? extent->height : 0; |
| |
| return anv_image_params_from_description(description, width, height, params_out, |
| tiling_flags_out, not_cache_coherent_out); |
| } |
| |
| VkResult anv_get_buffer_collection_handle(struct anv_device* device, |
| VkBufferCollectionFUCHSIA vk_collection, uint32_t index, |
| uint32_t* handle_out, uint32_t* offset_out) |
| { |
| ANV_FROM_HANDLE(anv_buffer_collection, buffer_collection, vk_collection); |
| |
| magma_sysmem_connection_t sysmem_connection; |
| magma_status_t status = AnvMagmaGetSysmemConnection(device->connection, &sysmem_connection); |
| if (status != MAGMA_STATUS_OK) |
| return ANV_MAGMA_DRET(VK_ERROR_DEVICE_LOST); |
| if (magma_sysmem_get_buffer_handle_from_collection(sysmem_connection, |
| buffer_collection->buffer_collection, index, |
| handle_out, offset_out) != MAGMA_STATUS_OK) { |
| return ANV_MAGMA_DRET(VK_ERROR_DEVICE_LOST); |
| } |
| return VK_SUCCESS; |
| } |
| |
| #endif // VK_USE_PLATFORM_FUCHSIA |