diff --git a/src/broadcom/vulkan/meson.build b/src/broadcom/vulkan/meson.build index bfa7e9f29f2..f86598ee95e 100644 --- a/src/broadcom/vulkan/meson.build +++ b/src/broadcom/vulkan/meson.build @@ -98,6 +98,12 @@ if with_platform_wayland libv3dv_files += [wayland_drm_client_protocol_h, wayland_drm_protocol_c] endif +if with_platform_android + v3dv_deps += dep_android + v3dv_flags += '-DVK_USE_PLATFORM_ANDROID_KHR' + libv3dv_files += files('v3dv_android.c') +endif + per_version_libs = [] foreach ver : v3d_versions per_version_libs += static_library( diff --git a/src/broadcom/vulkan/v3dv_android.c b/src/broadcom/vulkan/v3dv_android.c new file mode 100644 index 00000000000..7ebc5a01adc --- /dev/null +++ b/src/broadcom/vulkan/v3dv_android.c @@ -0,0 +1,539 @@ +/* + * Copyright © 2017, Google Inc. + * + * 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 "v3dv_private.h" +#include + +#if ANDROID_API_LEVEL >= 26 +#include +#endif + +#include "drm-uapi/drm_fourcc.h" +#include +#include + +#include +#include + +#include "util/libsync.h" +#include "util/log.h" +#include "util/os_file.h" + +static int +v3dv_hal_open(const struct hw_module_t *mod, + const char *id, + struct hw_device_t **dev); +static int +v3dv_hal_close(struct hw_device_t *dev); + +static void UNUSED +static_asserts(void) +{ + STATIC_ASSERT(HWVULKAN_DISPATCH_MAGIC == ICD_LOADER_MAGIC); +} + +PUBLIC struct hwvulkan_module_t HAL_MODULE_INFO_SYM = { + .common = + { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = HWVULKAN_MODULE_API_VERSION_0_1, + .hal_api_version = HARDWARE_MAKE_API_VERSION(1, 0), + .id = HWVULKAN_HARDWARE_MODULE_ID, + .name = "Broadcom Vulkan HAL", + .author = "Mesa3D", + .methods = + &(hw_module_methods_t) { + .open = v3dv_hal_open, + }, + }, +}; + +/* If any bits in test_mask are set, then unset them and return true. */ +static inline bool +unmask32(uint32_t *inout_mask, uint32_t test_mask) +{ + uint32_t orig_mask = *inout_mask; + *inout_mask &= ~test_mask; + return *inout_mask != orig_mask; +} + +static int +v3dv_hal_open(const struct hw_module_t *mod, + const char *id, + struct hw_device_t **dev) +{ + assert(mod == &HAL_MODULE_INFO_SYM.common); + assert(strcmp(id, HWVULKAN_DEVICE_0) == 0); + + hwvulkan_device_t *hal_dev = malloc(sizeof(*hal_dev)); + if (!hal_dev) + return -1; + + *hal_dev = (hwvulkan_device_t){ + .common = + { + .tag = HARDWARE_DEVICE_TAG, + .version = HWVULKAN_DEVICE_API_VERSION_0_1, + .module = &HAL_MODULE_INFO_SYM.common, + .close = v3dv_hal_close, + }, + .EnumerateInstanceExtensionProperties = + v3dv_EnumerateInstanceExtensionProperties, + .CreateInstance = v3dv_CreateInstance, + .GetInstanceProcAddr = v3dv_GetInstanceProcAddr, + }; + + mesa_logi("v3dv: Warning: Android Vulkan implementation is experimental"); + + *dev = &hal_dev->common; + return 0; +} + +static int +v3dv_hal_close(struct hw_device_t *dev) +{ + /* hwvulkan.h claims that hw_device_t::close() is never called. */ + return -1; +} + +static int +get_format_bpp(int native) +{ + int bpp; + + switch (native) { + case HAL_PIXEL_FORMAT_RGBA_FP16: + bpp = 8; + break; + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + case HAL_PIXEL_FORMAT_RGBA_1010102: + bpp = 4; + break; + case HAL_PIXEL_FORMAT_RGB_565: + bpp = 2; + break; + default: + bpp = 0; + break; + } + + return bpp; +} + +/* get buffer info from VkNativeBufferANDROID */ +static VkResult +v3dv_gralloc_info_other(struct v3dv_device *device, + const VkNativeBufferANDROID *native_buffer, + int *out_stride, + uint64_t *out_modifier) +{ + *out_stride = native_buffer->stride /*in pixels*/ * + get_format_bpp(native_buffer->format); + *out_modifier = DRM_FORMAT_MOD_LINEAR; + return VK_SUCCESS; +} + +static const char cros_gralloc_module_name[] = "CrOS Gralloc"; + +#define CROS_GRALLOC_DRM_GET_BUFFER_INFO 4 + +struct cros_gralloc0_buffer_info +{ + uint32_t drm_fourcc; + int num_fds; + int fds[4]; + uint64_t modifier; + int offset[4]; + int stride[4]; +}; + +static VkResult +v3dv_gralloc_info_cros(struct v3dv_device *device, + const VkNativeBufferANDROID *native_buffer, + int *out_stride, + uint64_t *out_modifier) +{ + const gralloc_module_t *gralloc = device->gralloc; + struct cros_gralloc0_buffer_info info; + int ret; + + ret = gralloc->perform(gralloc, CROS_GRALLOC_DRM_GET_BUFFER_INFO, + native_buffer->handle, &info); + if (ret) + return VK_ERROR_INVALID_EXTERNAL_HANDLE; + + *out_stride = info.stride[0]; + *out_modifier = info.modifier; + + return VK_SUCCESS; +} + +VkResult +v3dv_gralloc_info(struct v3dv_device *device, + const VkNativeBufferANDROID *native_buffer, + int *out_dmabuf, + int *out_stride, + int *out_size, + uint64_t *out_modifier) +{ + if (device->gralloc_type == V3DV_GRALLOC_UNKNOWN) { + /* get gralloc module for gralloc buffer info query */ + int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, + (const hw_module_t **) &device->gralloc); + + device->gralloc_type = V3DV_GRALLOC_OTHER; + + if (err == 0) { + const gralloc_module_t *gralloc = device->gralloc; + mesa_logi("opened gralloc module name: %s", gralloc->common.name); + + if (strcmp(gralloc->common.name, cros_gralloc_module_name) == 0 && + gralloc->perform) { + device->gralloc_type = V3DV_GRALLOC_CROS; + } + } + } + + *out_dmabuf = native_buffer->handle->data[0]; + *out_size = lseek(*out_dmabuf, 0, SEEK_END); + + if (device->gralloc_type == V3DV_GRALLOC_CROS) { + return v3dv_gralloc_info_cros(device, native_buffer, out_stride, + out_modifier); + } else { + return v3dv_gralloc_info_other(device, native_buffer, out_stride, + out_modifier); + } +} + +VkResult +v3dv_import_native_buffer_fd(VkDevice device_h, + int native_buffer_fd, + const VkAllocationCallbacks *alloc, + VkImage image_h) +{ + struct v3dv_image *image = NULL; + VkResult result; + + image = v3dv_image_from_handle(image_h); + + VkDeviceMemory memory_h; + + const VkMemoryDedicatedAllocateInfo ded_alloc = { + .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, + .pNext = NULL, + .buffer = VK_NULL_HANDLE, + .image = image_h + }; + + const VkImportMemoryFdInfoKHR import_info = { + .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, + .pNext = &ded_alloc, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, + .fd = os_dupfd_cloexec(native_buffer_fd), + }; + + result = + v3dv_AllocateMemory(device_h, + &(VkMemoryAllocateInfo) { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = &import_info, + .allocationSize = image->size, + .memoryTypeIndex = 0, + }, + alloc, &memory_h); + + if (result != VK_SUCCESS) + goto fail_create_image; + + VkBindImageMemoryInfo bind_info = { + .sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO, + .image = image_h, + .memory = memory_h, + .memoryOffset = 0, + }; + v3dv_BindImageMemory2(device_h, 1, &bind_info); + + image->is_native_buffer_memory = true; + + return VK_SUCCESS; + +fail_create_image: + close(import_info.fd); + + return result; +} + +static VkResult +format_supported_with_usage(VkDevice device_h, + VkFormat format, + VkImageUsageFlags imageUsage) +{ + V3DV_FROM_HANDLE(v3dv_device, device, device_h); + struct v3dv_physical_device *phys_dev = &device->instance->physicalDevice; + VkPhysicalDevice phys_dev_h = v3dv_physical_device_to_handle(phys_dev); + VkResult result; + + const VkPhysicalDeviceImageFormatInfo2 image_format_info = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, + .format = format, + .type = VK_IMAGE_TYPE_2D, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = imageUsage, + }; + + VkImageFormatProperties2 image_format_props = { + .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, + }; + + /* Check that requested format and usage are supported. */ + result = v3dv_GetPhysicalDeviceImageFormatProperties2( + phys_dev_h, &image_format_info, &image_format_props); + if (result != VK_SUCCESS) { + return vk_errorf(device, result, + "v3dv_GetPhysicalDeviceImageFormatProperties2 failed " + "inside %s", + __func__); + } + + return VK_SUCCESS; +} + +static VkResult +setup_gralloc0_usage(struct v3dv_device *device, + VkFormat format, + VkImageUsageFlags imageUsage, + int *grallocUsage) +{ + if (unmask32(&imageUsage, VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) + *grallocUsage |= GRALLOC_USAGE_HW_RENDER; + + if (unmask32(&imageUsage, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_STORAGE_BIT | + VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)) + *grallocUsage |= GRALLOC_USAGE_HW_TEXTURE; + + /* All VkImageUsageFlags not explicitly checked here are unsupported for + * gralloc swapchains. + */ + if (imageUsage != 0) { + return vk_errorf(device, VK_ERROR_FORMAT_NOT_SUPPORTED, + "unsupported VkImageUsageFlags(0x%x) for gralloc " + "swapchain", + imageUsage); + } + + /* Swapchain assumes direct displaying, therefore enable COMPOSER flag, + * In case format is not supported by display controller, gralloc will + * drop this flag and still allocate the buffer in VRAM + */ + *grallocUsage |= GRALLOC_USAGE_HW_COMPOSER; + + if (*grallocUsage == 0) + return VK_ERROR_FORMAT_NOT_SUPPORTED; + + return VK_SUCCESS; +} + +VKAPI_ATTR VkResult VKAPI_CALL +v3dv_GetSwapchainGrallocUsageANDROID(VkDevice device_h, + VkFormat format, + VkImageUsageFlags imageUsage, + int *grallocUsage) +{ + V3DV_FROM_HANDLE(v3dv_device, device, device_h); + VkResult result; + + result = format_supported_with_usage(device_h, format, imageUsage); + if (result != VK_SUCCESS) + return result; + + *grallocUsage = 0; + return setup_gralloc0_usage(device, format, imageUsage, grallocUsage); +} + +#if ANDROID_API_LEVEL >= 26 +VKAPI_ATTR VkResult VKAPI_CALL +v3dv_GetSwapchainGrallocUsage2ANDROID( + VkDevice device_h, + VkFormat format, + VkImageUsageFlags imageUsage, + VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, + uint64_t *grallocConsumerUsage, + uint64_t *grallocProducerUsage) +{ + V3DV_FROM_HANDLE(v3dv_device, device, device_h); + VkResult result; + + *grallocConsumerUsage = 0; + *grallocProducerUsage = 0; + mesa_logd("%s: format=%d, usage=0x%x", __func__, format, imageUsage); + + result = format_supported_with_usage(device_h, format, imageUsage); + if (result != VK_SUCCESS) + return result; + + int32_t grallocUsage = 0; + result = setup_gralloc0_usage(device, format, imageUsage, &grallocUsage); + if (result != VK_SUCCESS) + return result; + + /* Setup gralloc1 usage flags from gralloc0 flags. */ + + if (grallocUsage & GRALLOC_USAGE_HW_RENDER) { + *grallocProducerUsage |= GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET; + } + + if (grallocUsage & GRALLOC_USAGE_HW_TEXTURE) { + *grallocConsumerUsage |= GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE; + } + + if (grallocUsage & GRALLOC_USAGE_HW_COMPOSER) { + /* GPU composing case */ + *grallocConsumerUsage |= GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE; + /* Hardware composing case */ + *grallocConsumerUsage |= GRALLOC1_CONSUMER_USAGE_HWCOMPOSER; + } + + return VK_SUCCESS; +} +#endif + +VKAPI_ATTR VkResult VKAPI_CALL +v3dv_AcquireImageANDROID(VkDevice device_h, + VkImage image_h, + int nativeFenceFd, + VkSemaphore semaphore_h, + VkFence fence_h) +{ + V3DV_FROM_HANDLE(v3dv_device, device, device_h); + VkResult result = VK_SUCCESS; + + /* From https://source.android.com/devices/graphics/implement-vulkan : + * + * "The driver takes ownership of the fence file descriptor and closes + * the fence file descriptor when no longer needed. The driver must do + * so even if neither a semaphore or fence object is provided, or even + * if vkAcquireImageANDROID fails and returns an error." + * + * The Vulkan spec for VkImportFence/SemaphoreFdKHR(), however, requires + * the file descriptor to be left alone on failure. + */ + int semaphore_fd = -1, fence_fd = -1; + if (nativeFenceFd >= 0) { + if (semaphore_h != VK_NULL_HANDLE && fence_h != VK_NULL_HANDLE) { + /* We have both so we have to import the sync file twice. One of + * them needs to be a dup. + */ + semaphore_fd = nativeFenceFd; + fence_fd = os_dupfd_cloexec(nativeFenceFd); + if (fence_fd < 0) { + VkResult err = (errno == EMFILE) ? VK_ERROR_TOO_MANY_OBJECTS + : VK_ERROR_OUT_OF_HOST_MEMORY; + close(nativeFenceFd); + return vk_error(device, err); + } + } else if (semaphore_h != VK_NULL_HANDLE) { + semaphore_fd = nativeFenceFd; + } else if (fence_h != VK_NULL_HANDLE) { + fence_fd = nativeFenceFd; + } else { + /* Nothing to import into so we have to close the file */ + close(nativeFenceFd); + } + } + + if (semaphore_h != VK_NULL_HANDLE) { + const VkImportSemaphoreFdInfoKHR info = { + .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR, + .semaphore = semaphore_h, + .flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT, + .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + .fd = semaphore_fd, + }; + result = v3dv_ImportSemaphoreFdKHR(device_h, &info); + if (result == VK_SUCCESS) + semaphore_fd = -1; /* VK took ownership */ + } + + if (result == VK_SUCCESS && fence_h != VK_NULL_HANDLE) { + const VkImportFenceFdInfoKHR info = { + .sType = VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR, + .fence = fence_h, + .flags = VK_FENCE_IMPORT_TEMPORARY_BIT, + .handleType = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT, + .fd = fence_fd, + }; + result = v3dv_ImportFenceFdKHR(device_h, &info); + if (result == VK_SUCCESS) + fence_fd = -1; /* VK took ownership */ + } + + if (semaphore_fd >= 0) + close(semaphore_fd); + if (fence_fd >= 0) + close(fence_fd); + + return result; +} + +VkResult +v3dv_QueueSignalReleaseImageANDROID(VkQueue queue, + uint32_t waitSemaphoreCount, + const VkSemaphore *pWaitSemaphores, + VkImage image, + int *pNativeFenceFd) +{ + VkResult result; + + if (waitSemaphoreCount == 0) + goto done; + + result = v3dv_QueueSubmit( + queue, 1, + &(VkSubmitInfo) { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .waitSemaphoreCount = 1, + .pWaitSemaphores = pWaitSemaphores, + .pWaitDstStageMask = + &(VkPipelineStageFlags) { VK_PIPELINE_STAGE_ALL_COMMANDS_BIT }, + }, + (VkFence) VK_NULL_HANDLE); + if (result != VK_SUCCESS) + return result; + +done: + if (pNativeFenceFd) { + /* We can rely implicit on sync because above we submitted all + * semaphores to the queue. + */ + *pNativeFenceFd = -1; + } + + return VK_SUCCESS; +} diff --git a/src/broadcom/vulkan/v3dv_device.c b/src/broadcom/vulkan/v3dv_device.c index 2ab2ccb90ac..8d17320c652 100644 --- a/src/broadcom/vulkan/v3dv_device.c +++ b/src/broadcom/vulkan/v3dv_device.c @@ -155,6 +155,9 @@ get_device_extensions(const struct v3dv_physical_device *device, .EXT_private_data = true, .EXT_provoking_vertex = true, .EXT_vertex_attribute_divisor = true, +#ifdef ANDROID + .ANDROID_native_buffer = true, +#endif }; } diff --git a/src/broadcom/vulkan/v3dv_image.c b/src/broadcom/vulkan/v3dv_image.c index 06ecb2bfa12..395a7f467fa 100644 --- a/src/broadcom/vulkan/v3dv_image.c +++ b/src/broadcom/vulkan/v3dv_image.c @@ -296,6 +296,27 @@ create_image(struct v3dv_device *device, tiling = VK_IMAGE_TILING_LINEAR; } +#ifdef ANDROID + const VkNativeBufferANDROID *native_buffer = + vk_find_struct_const(pCreateInfo->pNext, NATIVE_BUFFER_ANDROID); + + int native_buf_fd = -1; + int native_buf_stride = 0; + int native_buf_size = 0; + + if (native_buffer != NULL) { + VkResult result = v3dv_gralloc_info(device, native_buffer, &native_buf_fd, + &native_buf_stride, &native_buf_size, &modifier); + if (result != VK_SUCCESS) { + vk_image_destroy(&device->vk, pAllocator, &image->vk); + return result; + } + + if (modifier != DRM_FORMAT_MOD_BROADCOM_UIF) + tiling = VK_IMAGE_TILING_LINEAR; + } +#endif + const struct v3dv_format *format = v3dv_X(device, get_format)(pCreateInfo->format); v3dv_assert(format != NULL && format->supported); @@ -320,6 +341,21 @@ create_image(struct v3dv_device *device, v3d_setup_slices(image); +#ifdef ANDROID + if (native_buffer != NULL) { + image->slices[0].stride = native_buf_stride; + image->slices[0].size = image->size = native_buf_size; + + VkResult result = v3dv_import_native_buffer_fd(v3dv_device_to_handle(device), + native_buf_fd, pAllocator, + v3dv_image_to_handle(image)); + if (result != VK_SUCCESS) { + vk_object_free(&device->vk, pAllocator, image); + return result; + } + } +#endif + *pImage = v3dv_image_to_handle(image); return VK_SUCCESS; @@ -435,6 +471,11 @@ v3dv_DestroyImage(VkDevice _device, if (image == NULL) return; +#ifdef ANDROID + if (image->is_native_buffer_memory) + v3dv_FreeMemory(_device, v3dv_device_memory_to_handle(image->mem), pAllocator); +#endif + vk_image_destroy(&device->vk, pAllocator, &image->vk); } diff --git a/src/broadcom/vulkan/v3dv_private.h b/src/broadcom/vulkan/v3dv_private.h index 9636c3f2c47..71068d52253 100644 --- a/src/broadcom/vulkan/v3dv_private.h +++ b/src/broadcom/vulkan/v3dv_private.h @@ -471,6 +471,15 @@ struct v3dv_device { */ struct v3dv_bo *default_attribute_float; VkPhysicalDeviceFeatures features; + +#ifdef ANDROID + const void *gralloc; + enum { + V3DV_GRALLOC_UNKNOWN, + V3DV_GRALLOC_CROS, + V3DV_GRALLOC_OTHER, + } gralloc_type; +#endif }; struct v3dv_device_memory { @@ -536,6 +545,11 @@ struct v3dv_image { struct v3dv_device_memory *mem; VkDeviceSize mem_offset; uint32_t alignment; + +#ifdef ANDROID + /* Image is backed by VK_ANDROID_native_buffer, */ + bool is_native_buffer_memory; +#endif }; VkImageViewType v3dv_image_type_to_view_type(VkImageType type); @@ -2157,4 +2171,20 @@ u64_compare(const void *key1, const void *key2) # undef v3dX #endif +#ifdef ANDROID +VkResult +v3dv_gralloc_info(struct v3dv_device *device, + const VkNativeBufferANDROID *gralloc_info, + int *out_dmabuf, + int *out_stride, + int *out_size, + uint64_t *out_modifier); + +VkResult +v3dv_import_native_buffer_fd(VkDevice device_h, + int dma_buf, + const VkAllocationCallbacks *alloc, + VkImage image_h); +#endif /* ANDROID */ + #endif /* V3DV_PRIVATE_H */