1 /*
2  * Copyright © 2023 Collabora, Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23 
24 #include "vk_device_memory.h"
25 
26 #include "vk_android.h"
27 #include "vk_common_entrypoints.h"
28 #include "vk_util.h"
29 
30 #if DETECT_OS_ANDROID && ANDROID_API_LEVEL >= 26
31 #include <vndk/hardware_buffer.h>
32 #endif
33 
34 void *
vk_device_memory_create(struct vk_device * device,const VkMemoryAllocateInfo * pAllocateInfo,const VkAllocationCallbacks * alloc,size_t size)35 vk_device_memory_create(struct vk_device *device,
36                         const VkMemoryAllocateInfo *pAllocateInfo,
37                         const VkAllocationCallbacks *alloc,
38                         size_t size)
39 {
40    struct vk_device_memory *mem =
41       vk_object_zalloc(device, alloc, size, VK_OBJECT_TYPE_DEVICE_MEMORY);
42    if (mem == NULL)
43       return NULL;
44 
45    assert(pAllocateInfo->sType == VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO);
46 
47    mem->size = pAllocateInfo->allocationSize;
48    mem->memory_type_index = pAllocateInfo->memoryTypeIndex;
49 
50    vk_foreach_struct_const(ext, pAllocateInfo->pNext) {
51       switch (ext->sType) {
52       case VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO: {
53          const VkExportMemoryAllocateInfo *export_info = (void *)ext;
54          mem->export_handle_types = export_info->handleTypes;
55          break;
56       }
57 
58       case VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID: {
59 #if DETECT_OS_ANDROID && ANDROID_API_LEVEL >= 26
60          const VkImportAndroidHardwareBufferInfoANDROID *ahb_info = (void *)ext;
61 
62          assert(mem->import_handle_type == 0);
63          mem->import_handle_type =
64             VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
65 
66          /* From the Vulkan 1.3.242 spec:
67           *
68           *    "If the vkAllocateMemory command succeeds, the implementation
69           *    must acquire a reference to the imported hardware buffer, which
70           *    it must release when the device memory object is freed. If the
71           *    command fails, the implementation must not retain a
72           *    reference."
73           *
74           * We assume that if the driver fails to create its memory object,
75           * it will call vk_device_memory_destroy which will delete our
76           * reference.
77           */
78          AHardwareBuffer_acquire(ahb_info->buffer);
79          mem->ahardware_buffer = ahb_info->buffer;
80          break;
81 #else
82          unreachable("AHardwareBuffer import requires Android >= 26");
83 #endif /* ANDROID_API_LEVEL >= 26 */
84       }
85 
86       case VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR: {
87          const VkImportMemoryFdInfoKHR *fd_info = (void *)ext;
88          if (fd_info->handleType) {
89             assert(fd_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT ||
90                    fd_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT);
91             assert(mem->import_handle_type == 0);
92             mem->import_handle_type = fd_info->handleType;
93          }
94          break;
95       }
96 
97       case VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT: {
98          const VkImportMemoryHostPointerInfoEXT *host_ptr_info = (void *)ext;
99          if (host_ptr_info->handleType) {
100             assert(host_ptr_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT ||
101                    host_ptr_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT);
102 
103             assert(mem->import_handle_type == 0);
104             mem->import_handle_type = host_ptr_info->handleType;
105             mem->host_ptr = host_ptr_info->pHostPointer;
106          }
107          break;
108       }
109 
110       case VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR: {
111 #ifdef VK_USE_PLATFORM_WIN32_KHR
112          const VkImportMemoryWin32HandleInfoKHR *w32h_info = (void *)ext;
113          if (w32h_info->handleType) {
114             assert(w32h_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT ||
115                    w32h_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT ||
116                    w32h_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT ||
117                    w32h_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT ||
118                    w32h_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT ||
119                    w32h_info->handleType == VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT);
120             assert(mem->import_handle_type == 0);
121             mem->import_handle_type = w32h_info->handleType;
122          }
123          break;
124 #else
125          unreachable("Win32 platform support disabled");
126 #endif
127       }
128 
129       case VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO: {
130          const VkMemoryAllocateFlagsInfo *flags_info = (void *)ext;
131          mem->alloc_flags = flags_info->flags;
132          break;
133       }
134 
135       default:
136          break;
137       }
138    }
139 
140    /* From the Vulkan Specification 1.3.261:
141     *
142     *    VUID-VkMemoryAllocateInfo-allocationSize-07897
143     *
144     *   "If the parameters do not define an import or export operation,
145     *   allocationSize must be greater than 0."
146     */
147    if (!mem->import_handle_type && !mem->export_handle_types)
148       assert(pAllocateInfo->allocationSize > 0);
149 
150    /* From the Vulkan Specification 1.3.261:
151     *
152     *    VUID-VkMemoryAllocateInfo-allocationSize-07899
153     *
154     *    "If the parameters define an export operation and the handle type is
155     *    not VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
156     *    allocationSize must be greater than 0."
157     */
158     if (mem->export_handle_types &&
159         mem->export_handle_types !=
160           VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID)
161       assert(pAllocateInfo->allocationSize > 0);
162 
163    if ((mem->export_handle_types &
164         VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID) &&
165        mem->ahardware_buffer == NULL) {
166       /* If we need to be able to export an Android hardware buffer but none
167        * is provided as an import, create a new one.
168        */
169       mem->ahardware_buffer = vk_alloc_ahardware_buffer(pAllocateInfo);
170       if (mem->ahardware_buffer == NULL) {
171          vk_device_memory_destroy(device, alloc, mem);
172          return NULL;
173       }
174    }
175 
176    return mem;
177 }
178 
179 void
vk_device_memory_destroy(struct vk_device * device,const VkAllocationCallbacks * alloc,struct vk_device_memory * mem)180 vk_device_memory_destroy(struct vk_device *device,
181                          const VkAllocationCallbacks *alloc,
182                          struct vk_device_memory *mem)
183 {
184 #if DETECT_OS_ANDROID && ANDROID_API_LEVEL >= 26
185    if (mem->ahardware_buffer)
186       AHardwareBuffer_release(mem->ahardware_buffer);
187 #endif /* ANDROID_API_LEVEL >= 26 */
188 
189    vk_object_free(device, alloc, mem);
190 }
191 
192 #if DETECT_OS_ANDROID && ANDROID_API_LEVEL >= 26
193 VkResult
vk_common_GetMemoryAndroidHardwareBufferANDROID(VkDevice _device,const VkMemoryGetAndroidHardwareBufferInfoANDROID * pInfo,struct AHardwareBuffer ** pBuffer)194 vk_common_GetMemoryAndroidHardwareBufferANDROID(
195    VkDevice _device,
196    const VkMemoryGetAndroidHardwareBufferInfoANDROID *pInfo,
197    struct AHardwareBuffer **pBuffer)
198 {
199    VK_FROM_HANDLE(vk_device_memory, mem, pInfo->memory);
200 
201    /* Some quotes from Vulkan spec:
202     *
203     *    "If the device memory was created by importing an Android hardware
204     *    buffer, vkGetMemoryAndroidHardwareBufferANDROID must return that same
205     *    Android hardware buffer object."
206     *
207     *    "VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID
208     *    must have been included in VkExportMemoryAllocateInfo::handleTypes
209     *    when memory was created."
210     */
211    if (mem->ahardware_buffer) {
212       *pBuffer = mem->ahardware_buffer;
213       /* Increase refcount. */
214       AHardwareBuffer_acquire(*pBuffer);
215       return VK_SUCCESS;
216    }
217 
218    return VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR;
219 }
220 #endif
221