blob: e0a742e198e0194758a30e68ebd68d01b87b4923 [file] [log] [blame]
/*
* Copyright © 2023 Collabora, Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "vk_device_memory.h"
#include "vk_android.h"
#include "vk_common_entrypoints.h"
#include "vk_util.h"
#if DETECT_OS_ANDROID && ANDROID_API_LEVEL >= 26
#include <vndk/hardware_buffer.h>
#endif
void *
vk_device_memory_create(struct vk_device *device,
const VkMemoryAllocateInfo *pAllocateInfo,
const VkAllocationCallbacks *alloc,
size_t size)
{
struct vk_device_memory *mem =
vk_object_zalloc(device, alloc, size, VK_OBJECT_TYPE_DEVICE_MEMORY);
if (mem == NULL)
return NULL;
assert(pAllocateInfo->sType == VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO);
mem->size = pAllocateInfo->allocationSize;
mem->memory_type_index = pAllocateInfo->memoryTypeIndex;
vk_foreach_struct_const(ext, pAllocateInfo->pNext) {
switch (ext->sType) {
case VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO: {
const VkExportMemoryAllocateInfo *export_info = (void *)ext;
mem->export_handle_types = export_info->handleTypes;
break;
}
case VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID: {
#if DETECT_OS_ANDROID && ANDROID_API_LEVEL >= 26
const VkImportAndroidHardwareBufferInfoANDROID *ahb_info = (void *)ext;
assert(mem->import_handle_type == 0);
mem->import_handle_type =
VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
/* From the Vulkan 1.3.242 spec:
*
* "If the vkAllocateMemory command succeeds, the implementation
* must acquire a reference to the imported hardware buffer, which
* it must release when the device memory object is freed. If the
* command fails, the implementation must not retain a
* reference."
*
* We assume that if the driver fails to create its memory object,
* it will call vk_device_memory_destroy which will delete our
* reference.
*/
AHardwareBuffer_acquire(ahb_info->buffer);
mem->ahardware_buffer = ahb_info->buffer;
break;
#else
unreachable("AHardwareBuffer import requires Android >= 26");
#endif /* DETECT_OS_ANDROID && ANDROID_API_LEVEL >= 26 */
}
case VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR: {
const VkImportMemoryFdInfoKHR *fd_info = (void *)ext;
if (fd_info->handleType) {
assert(fd_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT ||
fd_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT);
assert(mem->import_handle_type == 0);
mem->import_handle_type = fd_info->handleType;
}
break;
}
case VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT: {
const VkImportMemoryHostPointerInfoEXT *host_ptr_info = (void *)ext;
if (host_ptr_info->handleType) {
assert(host_ptr_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT ||
host_ptr_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT);
assert(mem->import_handle_type == 0);
mem->import_handle_type = host_ptr_info->handleType;
mem->host_ptr = host_ptr_info->pHostPointer;
}
break;
}
case VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR: {
#ifdef VK_USE_PLATFORM_WIN32_KHR
const VkImportMemoryWin32HandleInfoKHR *w32h_info = (void *)ext;
if (w32h_info->handleType) {
assert(w32h_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT ||
w32h_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT ||
w32h_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT ||
w32h_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT ||
w32h_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT ||
w32h_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT);
assert(mem->import_handle_type == 0);
mem->import_handle_type = w32h_info->handleType;
}
break;
#else
unreachable("Win32 platform support disabled");
#endif
}
case VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO: {
const VkMemoryAllocateFlagsInfo *flags_info = (void *)ext;
mem->alloc_flags = flags_info->flags;
break;
}
default:
break;
}
}
/* From the Vulkan Specification 1.3.261:
*
* VUID-VkMemoryAllocateInfo-allocationSize-07897
*
* "If the parameters do not define an import or export operation,
* allocationSize must be greater than 0."
*/
if (!mem->import_handle_type && !mem->export_handle_types)
assert(pAllocateInfo->allocationSize > 0);
/* From the Vulkan Specification 1.3.261:
*
* VUID-VkMemoryAllocateInfo-allocationSize-07899
*
* "If the parameters define an export operation and the handle type is
* not VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
* allocationSize must be greater than 0."
*/
if (mem->export_handle_types &&
mem->export_handle_types !=
VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID)
assert(pAllocateInfo->allocationSize > 0);
if ((mem->export_handle_types &
VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID) &&
mem->ahardware_buffer == NULL) {
/* If we need to be able to export an Android hardware buffer but none
* is provided as an import, create a new one.
*/
mem->ahardware_buffer = vk_alloc_ahardware_buffer(pAllocateInfo);
if (mem->ahardware_buffer == NULL) {
vk_device_memory_destroy(device, alloc, mem);
return NULL;
}
}
return mem;
}
void
vk_device_memory_destroy(struct vk_device *device,
const VkAllocationCallbacks *alloc,
struct vk_device_memory *mem)
{
#if DETECT_OS_ANDROID && ANDROID_API_LEVEL >= 26
if (mem->ahardware_buffer)
AHardwareBuffer_release(mem->ahardware_buffer);
#endif /* DETECT_OS_ANDROID && ANDROID_API_LEVEL >= 26 */
vk_object_free(device, alloc, mem);
}
#if DETECT_OS_ANDROID && ANDROID_API_LEVEL >= 26
VkResult
vk_common_GetMemoryAndroidHardwareBufferANDROID(
VkDevice _device,
const VkMemoryGetAndroidHardwareBufferInfoANDROID *pInfo,
struct AHardwareBuffer **pBuffer)
{
VK_FROM_HANDLE(vk_device_memory, mem, pInfo->memory);
/* Some quotes from Vulkan spec:
*
* "If the device memory was created by importing an Android hardware
* buffer, vkGetMemoryAndroidHardwareBufferANDROID must return that same
* Android hardware buffer object."
*
* "VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID
* must have been included in VkExportMemoryAllocateInfo::handleTypes
* when memory was created."
*/
if (mem->ahardware_buffer) {
*pBuffer = mem->ahardware_buffer;
/* Increase refcount. */
AHardwareBuffer_acquire(*pBuffer);
return VK_SUCCESS;
}
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
}
#endif