1 //
2 // Copyright 2019 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 
7 // VulkanExternalHelper.cpp : Helper for allocating & managing vulkan external objects.
8 
9 #include "test_utils/VulkanExternalHelper.h"
10 
11 #include <vector>
12 
13 #include "common/bitset_utils.h"
14 #include "common/debug.h"
15 #include "common/system_utils.h"
16 #include "common/vulkan/vulkan_icd.h"
17 
18 namespace angle
19 {
20 
21 namespace
22 {
23 
EnumerateInstanceExtensionProperties(const char * layerName)24 std::vector<VkExtensionProperties> EnumerateInstanceExtensionProperties(const char *layerName)
25 {
26     uint32_t instanceExtensionCount;
27     VkResult result =
28         vkEnumerateInstanceExtensionProperties(layerName, &instanceExtensionCount, nullptr);
29     ASSERT(result == VK_SUCCESS);
30     std::vector<VkExtensionProperties> instanceExtensionProperties(instanceExtensionCount);
31     result = vkEnumerateInstanceExtensionProperties(layerName, &instanceExtensionCount,
32                                                     instanceExtensionProperties.data());
33     ASSERT(result == VK_SUCCESS);
34     return instanceExtensionProperties;
35 }
36 
EnumeratePhysicalDevices(VkInstance instance)37 std::vector<VkPhysicalDevice> EnumeratePhysicalDevices(VkInstance instance)
38 {
39     uint32_t physicalDeviceCount;
40     VkResult result = vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr);
41     ASSERT(result == VK_SUCCESS);
42     std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
43     result = vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices.data());
44     return physicalDevices;
45 }
46 
EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice,const char * layerName)47 std::vector<VkExtensionProperties> EnumerateDeviceExtensionProperties(
48     VkPhysicalDevice physicalDevice,
49     const char *layerName)
50 {
51     uint32_t deviceExtensionCount;
52     VkResult result = vkEnumerateDeviceExtensionProperties(physicalDevice, layerName,
53                                                            &deviceExtensionCount, nullptr);
54     ASSERT(result == VK_SUCCESS);
55     std::vector<VkExtensionProperties> deviceExtensionProperties(deviceExtensionCount);
56     result = vkEnumerateDeviceExtensionProperties(physicalDevice, layerName, &deviceExtensionCount,
57                                                   deviceExtensionProperties.data());
58     ASSERT(result == VK_SUCCESS);
59     return deviceExtensionProperties;
60 }
61 
GetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice physicalDevice)62 std::vector<VkQueueFamilyProperties> GetPhysicalDeviceQueueFamilyProperties(
63     VkPhysicalDevice physicalDevice)
64 {
65     uint32_t queueFamilyPropertyCount;
66     vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyPropertyCount, nullptr);
67     std::vector<VkQueueFamilyProperties> physicalDeviceQueueFamilyProperties(
68         queueFamilyPropertyCount);
69     vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyPropertyCount,
70                                              physicalDeviceQueueFamilyProperties.data());
71     return physicalDeviceQueueFamilyProperties;
72 }
73 
HasExtension(const std::vector<VkExtensionProperties> instanceExtensions,const char * extensionName)74 bool HasExtension(const std::vector<VkExtensionProperties> instanceExtensions,
75                   const char *extensionName)
76 {
77     for (const auto &extensionProperties : instanceExtensions)
78     {
79         if (!strcmp(extensionProperties.extensionName, extensionName))
80             return true;
81     }
82 
83     return false;
84 }
85 
HasExtension(const std::vector<const char * > enabledExtensions,const char * extensionName)86 bool HasExtension(const std::vector<const char *> enabledExtensions, const char *extensionName)
87 {
88     for (const char *enabledExtension : enabledExtensions)
89     {
90         if (!strcmp(enabledExtension, extensionName))
91             return true;
92     }
93 
94     return false;
95 }
96 
FindMemoryType(const VkPhysicalDeviceMemoryProperties & memoryProperties,uint32_t memoryTypeBits,VkMemoryPropertyFlags requiredMemoryPropertyFlags)97 uint32_t FindMemoryType(const VkPhysicalDeviceMemoryProperties &memoryProperties,
98                         uint32_t memoryTypeBits,
99                         VkMemoryPropertyFlags requiredMemoryPropertyFlags)
100 {
101     for (size_t memoryIndex : angle::BitSet32<32>(memoryTypeBits))
102     {
103         ASSERT(memoryIndex < memoryProperties.memoryTypeCount);
104 
105         if ((memoryProperties.memoryTypes[memoryIndex].propertyFlags &
106              requiredMemoryPropertyFlags) == requiredMemoryPropertyFlags)
107         {
108             return static_cast<uint32_t>(memoryIndex);
109         }
110     }
111 
112     return UINT32_MAX;
113 }
114 
ImageMemoryBarrier(VkCommandBuffer commandBuffer,VkImage image,uint32_t srcQueueFamilyIndex,uint32_t dstQueueFamilyIndex,VkImageLayout oldLayout,VkImageLayout newLayout)115 void ImageMemoryBarrier(VkCommandBuffer commandBuffer,
116                         VkImage image,
117                         uint32_t srcQueueFamilyIndex,
118                         uint32_t dstQueueFamilyIndex,
119                         VkImageLayout oldLayout,
120                         VkImageLayout newLayout)
121 {
122     const VkImageMemoryBarrier imageMemoryBarriers[] = {
123         /* [0] = */ {/* .sType = */ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
124                      /* .pNext = */ nullptr,
125                      /* .srcAccessMask = */ VK_ACCESS_MEMORY_WRITE_BIT,
126                      /* .dstAccessMask = */ VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
127                      /* .oldLayout = */ oldLayout,
128                      /* .newLayout = */ newLayout,
129                      /* .srcQueueFamilyIndex = */ srcQueueFamilyIndex,
130                      /* .dstQueueFamilyIndex = */ dstQueueFamilyIndex,
131                      /* .image = */ image,
132                      /* .subresourceRange = */
133                      {
134                          /* .aspectMask = */ VK_IMAGE_ASPECT_COLOR_BIT,
135                          /* .basicMiplevel = */ 0,
136                          /* .levelCount = */ 1,
137                          /* .baseArrayLayer = */ 0,
138                          /* .layerCount = */ 1,
139                      }}};
140     const uint32_t imageMemoryBarrierCount = std::extent<decltype(imageMemoryBarriers)>();
141 
142     constexpr VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
143     constexpr VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
144     const VkDependencyFlags dependencyFlags     = 0;
145 
146     vkCmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, 0, nullptr, 0,
147                          nullptr, imageMemoryBarrierCount, imageMemoryBarriers);
148 }
149 
150 }  // namespace
151 
VulkanExternalHelper()152 VulkanExternalHelper::VulkanExternalHelper() {}
153 
~VulkanExternalHelper()154 VulkanExternalHelper::~VulkanExternalHelper()
155 {
156     if (mDevice != VK_NULL_HANDLE)
157     {
158         vkDeviceWaitIdle(mDevice);
159     }
160 
161     if (mCommandPool != VK_NULL_HANDLE)
162     {
163         vkDestroyCommandPool(mDevice, mCommandPool, nullptr);
164     }
165 
166     if (mDevice != VK_NULL_HANDLE)
167     {
168         vkDestroyDevice(mDevice, nullptr);
169 
170         mDevice        = VK_NULL_HANDLE;
171         mGraphicsQueue = VK_NULL_HANDLE;
172     }
173 
174     if (mInstance != VK_NULL_HANDLE)
175     {
176         vkDestroyInstance(mInstance, nullptr);
177 
178         mInstance = VK_NULL_HANDLE;
179     }
180 }
181 
initialize(bool useSwiftshader,bool enableValidationLayers)182 void VulkanExternalHelper::initialize(bool useSwiftshader, bool enableValidationLayers)
183 {
184     bool enableValidationLayersOverride = enableValidationLayers;
185 #if !defined(ANGLE_ENABLE_VULKAN_VALIDATION_LAYERS)
186     enableValidationLayersOverride = false;
187 #endif
188 
189     vk::ICD icd = useSwiftshader ? vk::ICD::SwiftShader : vk::ICD::Default;
190 
191     vk::ScopedVkLoaderEnvironment scopedEnvironment(enableValidationLayersOverride, icd);
192 
193     ASSERT(mInstance == VK_NULL_HANDLE);
194     VkResult result = VK_SUCCESS;
195 #if ANGLE_SHARED_LIBVULKAN
196     result = volkInitialize();
197     ASSERT(result == VK_SUCCESS);
198 #endif  // ANGLE_SHARED_LIBVULKAN
199     std::vector<VkExtensionProperties> instanceExtensionProperties =
200         EnumerateInstanceExtensionProperties(nullptr);
201 
202     std::vector<const char *> requestedInstanceExtensions = {
203         VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
204         VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,
205         VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME};
206 
207     std::vector<const char *> enabledInstanceExtensions;
208 
209     for (const char *extensionName : requestedInstanceExtensions)
210     {
211         if (HasExtension(instanceExtensionProperties, extensionName))
212         {
213             enabledInstanceExtensions.push_back(extensionName);
214         }
215     }
216 
217     VkApplicationInfo applicationInfo = {
218         /* .sType = */ VK_STRUCTURE_TYPE_APPLICATION_INFO,
219         /* .pNext = */ nullptr,
220         /* .pApplicationName = */ "ANGLE Tests",
221         /* .applicationVersion = */ 1,
222         /* .pEngineName = */ nullptr,
223         /* .engineVersion = */ 0,
224         /* .apiVersion = */ VK_API_VERSION_1_0,
225     };
226 
227     uint32_t enabledInstanceExtensionCount =
228         static_cast<uint32_t>(enabledInstanceExtensions.size());
229 
230     std::vector<const char *> enabledLayerNames;
231     if (enableValidationLayersOverride)
232     {
233         enabledLayerNames.push_back("VK_LAYER_KHRONOS_validation");
234     }
235 
236     VkInstanceCreateInfo instanceCreateInfo = {
237         /* .sType = */ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
238         /* .pNext = */ nullptr,
239         /* .flags = */ 0,
240         /* .pApplicationInfo = */ &applicationInfo,
241         /* .enabledLayerCount = */ static_cast<uint32_t>(enabledLayerNames.size()),
242         /* .ppEnabledLayerNames = */ enabledLayerNames.data(),
243         /* .enabledExtensionCount = */ enabledInstanceExtensionCount,
244         /* .ppEnabledExtensionName = */ enabledInstanceExtensions.data(),
245     };
246 
247     result = vkCreateInstance(&instanceCreateInfo, nullptr, &mInstance);
248     ASSERT(result == VK_SUCCESS);
249     ASSERT(mInstance != VK_NULL_HANDLE);
250 #if ANGLE_SHARED_LIBVULKAN
251     volkLoadInstance(mInstance);
252 #endif  // ANGLE_SHARED_LIBVULKAN
253 
254     std::vector<VkPhysicalDevice> physicalDevices = EnumeratePhysicalDevices(mInstance);
255 
256     ASSERT(physicalDevices.size() > 0);
257 
258     VkPhysicalDeviceProperties physicalDeviceProperties;
259     ChoosePhysicalDevice(physicalDevices, icd, &mPhysicalDevice, &physicalDeviceProperties);
260 
261     vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &mMemoryProperties);
262 
263     std::vector<VkExtensionProperties> deviceExtensionProperties =
264         EnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr);
265 
266     std::vector<const char *> requestedDeviceExtensions = {
267         VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME,  VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
268         VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,     VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
269         VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME, VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME,
270     };
271 
272     std::vector<const char *> enabledDeviceExtensions;
273 
274     for (const char *extensionName : requestedDeviceExtensions)
275     {
276         if (HasExtension(deviceExtensionProperties, extensionName))
277         {
278             enabledDeviceExtensions.push_back(extensionName);
279         }
280     }
281 
282     std::vector<VkQueueFamilyProperties> queueFamilyProperties =
283         GetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice);
284 
285     for (uint32_t i = 0; i < queueFamilyProperties.size(); ++i)
286     {
287         if (queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
288         {
289             mGraphicsQueueFamilyIndex = i;
290         }
291     }
292     ASSERT(mGraphicsQueueFamilyIndex != UINT32_MAX);
293 
294     constexpr uint32_t kQueueCreateInfoCount           = 1;
295     constexpr uint32_t kGraphicsQueueCount             = 1;
296     float graphicsQueuePriorities[kGraphicsQueueCount] = {0.f};
297 
298     VkDeviceQueueCreateInfo queueCreateInfos[kQueueCreateInfoCount] = {
299         /* [0] = */ {
300             /* .sType = */ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
301             /* .pNext = */ nullptr,
302             /* .flags = */ 0,
303             /* .queueFamilyIndex = */ mGraphicsQueueFamilyIndex,
304             /* .queueCount = */ 1,
305             /* .pQueuePriorities = */ graphicsQueuePriorities,
306         },
307     };
308 
309     uint32_t enabledDeviceExtensionCount = static_cast<uint32_t>(enabledDeviceExtensions.size());
310 
311     VkDeviceCreateInfo deviceCreateInfo = {
312         /* .sType = */ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
313         /* .pNext = */ nullptr,
314         /* .flags = */ 0,
315         /* .queueCreateInfoCount = */ kQueueCreateInfoCount,
316         /* .pQueueCreateInfos = */ queueCreateInfos,
317         /* .enabledLayerCount = */ 0,
318         /* .ppEnabledLayerNames = */ nullptr,
319         /* .enabledExtensionCount = */ enabledDeviceExtensionCount,
320         /* .ppEnabledExtensionName = */ enabledDeviceExtensions.data(),
321         /* .pEnabledFeatures = */ nullptr,
322     };
323 
324     result = vkCreateDevice(mPhysicalDevice, &deviceCreateInfo, nullptr, &mDevice);
325     ASSERT(result == VK_SUCCESS);
326     ASSERT(mDevice != VK_NULL_HANDLE);
327 #if ANGLE_SHARED_LIBVULKAN
328     volkLoadDevice(mDevice);
329 #endif  // ANGLE_SHARED_LIBVULKAN
330 
331     constexpr uint32_t kGraphicsQueueIndex = 0;
332     static_assert(kGraphicsQueueIndex < kGraphicsQueueCount, "must be in range");
333     vkGetDeviceQueue(mDevice, mGraphicsQueueFamilyIndex, kGraphicsQueueIndex, &mGraphicsQueue);
334     ASSERT(mGraphicsQueue != VK_NULL_HANDLE);
335 
336     VkCommandPoolCreateInfo commandPoolCreateInfo = {
337         /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
338         /* .pNext = */ nullptr,
339         /* .flags = */ 0,
340         /* .queueFamilyIndex = */ mGraphicsQueueFamilyIndex,
341     };
342     result = vkCreateCommandPool(mDevice, &commandPoolCreateInfo, nullptr, &mCommandPool);
343     ASSERT(result == VK_SUCCESS);
344 
345     mHasExternalMemoryFd =
346         HasExtension(enabledDeviceExtensions, VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
347     mHasExternalSemaphoreFd =
348         HasExtension(enabledDeviceExtensions, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME);
349     mHasExternalMemoryFuchsia =
350         HasExtension(enabledDeviceExtensions, VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME);
351     mHasExternalSemaphoreFuchsia =
352         HasExtension(enabledDeviceExtensions, VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
353 
354     vkGetPhysicalDeviceImageFormatProperties2 =
355         reinterpret_cast<PFN_vkGetPhysicalDeviceImageFormatProperties2>(
356             vkGetInstanceProcAddr(mInstance, "vkGetPhysicalDeviceImageFormatProperties2"));
357     vkGetMemoryFdKHR = reinterpret_cast<PFN_vkGetMemoryFdKHR>(
358         vkGetInstanceProcAddr(mInstance, "vkGetMemoryFdKHR"));
359     ASSERT(!mHasExternalMemoryFd || vkGetMemoryFdKHR);
360     vkGetSemaphoreFdKHR = reinterpret_cast<PFN_vkGetSemaphoreFdKHR>(
361         vkGetInstanceProcAddr(mInstance, "vkGetSemaphoreFdKHR"));
362     ASSERT(!mHasExternalSemaphoreFd || vkGetSemaphoreFdKHR);
363     vkGetPhysicalDeviceExternalSemaphorePropertiesKHR =
364         reinterpret_cast<PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR>(
365             vkGetInstanceProcAddr(mInstance, "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR"));
366     vkGetMemoryZirconHandleFUCHSIA = reinterpret_cast<PFN_vkGetMemoryZirconHandleFUCHSIA>(
367         vkGetInstanceProcAddr(mInstance, "vkGetMemoryZirconHandleFUCHSIA"));
368     ASSERT(!mHasExternalMemoryFuchsia || vkGetMemoryZirconHandleFUCHSIA);
369     vkGetSemaphoreZirconHandleFUCHSIA = reinterpret_cast<PFN_vkGetSemaphoreZirconHandleFUCHSIA>(
370         vkGetInstanceProcAddr(mInstance, "vkGetSemaphoreZirconHandleFUCHSIA"));
371     ASSERT(!mHasExternalSemaphoreFuchsia || vkGetSemaphoreZirconHandleFUCHSIA);
372 }
373 
canCreateImageExternal(VkFormat format,VkImageType type,VkImageTiling tiling,VkImageCreateFlags createFlags,VkImageUsageFlags usageFlags,VkExternalMemoryHandleTypeFlagBits handleType) const374 bool VulkanExternalHelper::canCreateImageExternal(
375     VkFormat format,
376     VkImageType type,
377     VkImageTiling tiling,
378     VkImageCreateFlags createFlags,
379     VkImageUsageFlags usageFlags,
380     VkExternalMemoryHandleTypeFlagBits handleType) const
381 {
382     VkPhysicalDeviceExternalImageFormatInfo externalImageFormatInfo = {
383         /* .sType = */ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
384         /* .pNext = */ nullptr,
385         /* .handleType = */ handleType,
386     };
387 
388     VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = {
389         /* .sType = */ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
390         /* .pNext = */ &externalImageFormatInfo,
391         /* .format = */ format,
392         /* .type = */ type,
393         /* .tiling = */ tiling,
394         /* .usage = */ usageFlags,
395         /* .flags = */ createFlags,
396     };
397 
398     VkExternalImageFormatProperties externalImageFormatProperties = {
399         /* .sType = */ VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES,
400         /* .pNext = */ nullptr,
401     };
402 
403     VkImageFormatProperties2 imageFormatProperties = {
404         /* .sType = */ VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2,
405         /* .pNext = */ &externalImageFormatProperties};
406 
407     VkResult result = vkGetPhysicalDeviceImageFormatProperties2(mPhysicalDevice, &imageFormatInfo,
408                                                                 &imageFormatProperties);
409     if (result == VK_ERROR_FORMAT_NOT_SUPPORTED)
410     {
411         return false;
412     }
413 
414     ASSERT(result == VK_SUCCESS);
415 
416     constexpr VkExternalMemoryFeatureFlags kRequiredFeatures =
417         VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT;
418     if ((externalImageFormatProperties.externalMemoryProperties.externalMemoryFeatures &
419          kRequiredFeatures) != kRequiredFeatures)
420     {
421         return false;
422     }
423 
424     return true;
425 }
426 
createImage2DExternal(VkFormat format,VkImageCreateFlags createFlags,VkImageUsageFlags usageFlags,VkExtent3D extent,VkExternalMemoryHandleTypeFlags handleTypes,VkImage * imageOut,VkDeviceMemory * deviceMemoryOut,VkDeviceSize * deviceMemorySizeOut)427 VkResult VulkanExternalHelper::createImage2DExternal(VkFormat format,
428                                                      VkImageCreateFlags createFlags,
429                                                      VkImageUsageFlags usageFlags,
430                                                      VkExtent3D extent,
431                                                      VkExternalMemoryHandleTypeFlags handleTypes,
432                                                      VkImage *imageOut,
433                                                      VkDeviceMemory *deviceMemoryOut,
434                                                      VkDeviceSize *deviceMemorySizeOut)
435 {
436     VkExternalMemoryImageCreateInfoKHR externalMemoryImageCreateInfo = {
437         /* .sType = */ VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
438         /* .pNext = */ nullptr,
439         /* .handleTypes = */ handleTypes,
440     };
441 
442     VkImageCreateInfo imageCreateInfo = {
443         /* .sType = */ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
444         /* .pNext = */ &externalMemoryImageCreateInfo,
445         /* .flags = */ createFlags,
446         /* .imageType = */ VK_IMAGE_TYPE_2D,
447         /* .format = */ format,
448         /* .extent = */ extent,
449         /* .mipLevels = */ 1,
450         /* .arrayLayers = */ 1,
451         /* .samples = */ VK_SAMPLE_COUNT_1_BIT,
452         /* .tiling = */ VK_IMAGE_TILING_OPTIMAL,
453         /* .usage = */ usageFlags,
454         /* .sharingMode = */ VK_SHARING_MODE_EXCLUSIVE,
455         /* .queueFamilyIndexCount = */ 0,
456         /* .pQueueFamilyIndices = */ nullptr,
457         /* initialLayout = */ VK_IMAGE_LAYOUT_UNDEFINED,
458     };
459 
460     VkImage image   = VK_NULL_HANDLE;
461     VkResult result = vkCreateImage(mDevice, &imageCreateInfo, nullptr, &image);
462     if (result != VK_SUCCESS)
463     {
464         return result;
465     }
466 
467     VkMemoryPropertyFlags requestedMemoryPropertyFlags = 0;
468     VkMemoryRequirements memoryRequirements;
469     vkGetImageMemoryRequirements(mDevice, image, &memoryRequirements);
470     uint32_t memoryTypeIndex = FindMemoryType(mMemoryProperties, memoryRequirements.memoryTypeBits,
471                                               requestedMemoryPropertyFlags);
472     ASSERT(memoryTypeIndex != UINT32_MAX);
473     VkDeviceSize deviceMemorySize = memoryRequirements.size;
474 
475     VkExportMemoryAllocateInfo exportMemoryAllocateInfo = {
476         /* .sType = */ VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO,
477         /* .pNext = */ nullptr,
478         /* .handleTypes = */ handleTypes,
479     };
480     VkMemoryDedicatedAllocateInfoKHR memoryDedicatedAllocateInfo = {
481         /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR,
482         /* .pNext = */ &exportMemoryAllocateInfo,
483         /* .image = */ image,
484     };
485     VkMemoryAllocateInfo memoryAllocateInfo = {
486         /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
487         /* .pNext = */ &memoryDedicatedAllocateInfo,
488         /* .allocationSize = */ deviceMemorySize,
489         /* .memoryTypeIndex = */ memoryTypeIndex,
490     };
491 
492     VkDeviceMemory deviceMemory = VK_NULL_HANDLE;
493     result = vkAllocateMemory(mDevice, &memoryAllocateInfo, nullptr, &deviceMemory);
494     if (result != VK_SUCCESS)
495     {
496         vkDestroyImage(mDevice, image, nullptr);
497         return result;
498     }
499 
500     VkDeviceSize memoryOffset = 0;
501     result                    = vkBindImageMemory(mDevice, image, deviceMemory, memoryOffset);
502     if (result != VK_SUCCESS)
503     {
504         vkFreeMemory(mDevice, deviceMemory, nullptr);
505         vkDestroyImage(mDevice, image, nullptr);
506         return result;
507     }
508 
509     *imageOut            = image;
510     *deviceMemoryOut     = deviceMemory;
511     *deviceMemorySizeOut = deviceMemorySize;
512 
513     return VK_SUCCESS;
514 }
515 
canCreateImageOpaqueFd(VkFormat format,VkImageType type,VkImageTiling tiling,VkImageCreateFlags createFlags,VkImageUsageFlags usageFlags) const516 bool VulkanExternalHelper::canCreateImageOpaqueFd(VkFormat format,
517                                                   VkImageType type,
518                                                   VkImageTiling tiling,
519                                                   VkImageCreateFlags createFlags,
520                                                   VkImageUsageFlags usageFlags) const
521 {
522     if (!mHasExternalMemoryFd)
523     {
524         return false;
525     }
526 
527     return canCreateImageExternal(format, type, tiling, createFlags, usageFlags,
528                                   VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT);
529 }
530 
createImage2DOpaqueFd(VkFormat format,VkImageCreateFlags createFlags,VkImageUsageFlags usageFlags,VkExtent3D extent,VkImage * imageOut,VkDeviceMemory * deviceMemoryOut,VkDeviceSize * deviceMemorySizeOut)531 VkResult VulkanExternalHelper::createImage2DOpaqueFd(VkFormat format,
532                                                      VkImageCreateFlags createFlags,
533                                                      VkImageUsageFlags usageFlags,
534                                                      VkExtent3D extent,
535                                                      VkImage *imageOut,
536                                                      VkDeviceMemory *deviceMemoryOut,
537                                                      VkDeviceSize *deviceMemorySizeOut)
538 {
539     return createImage2DExternal(format, createFlags, usageFlags, extent,
540                                  VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, imageOut,
541                                  deviceMemoryOut, deviceMemorySizeOut);
542 }
543 
exportMemoryOpaqueFd(VkDeviceMemory deviceMemory,int * fd)544 VkResult VulkanExternalHelper::exportMemoryOpaqueFd(VkDeviceMemory deviceMemory, int *fd)
545 {
546     VkMemoryGetFdInfoKHR memoryGetFdInfo = {
547         /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
548         /* .pNext = */ nullptr,
549         /* .memory = */ deviceMemory,
550         /* .handleType = */ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
551     };
552 
553     return vkGetMemoryFdKHR(mDevice, &memoryGetFdInfo, fd);
554 }
555 
canCreateImageZirconVmo(VkFormat format,VkImageType type,VkImageTiling tiling,VkImageCreateFlags createFlags,VkImageUsageFlags usageFlags) const556 bool VulkanExternalHelper::canCreateImageZirconVmo(VkFormat format,
557                                                    VkImageType type,
558                                                    VkImageTiling tiling,
559                                                    VkImageCreateFlags createFlags,
560                                                    VkImageUsageFlags usageFlags) const
561 {
562     if (!mHasExternalMemoryFuchsia)
563     {
564         return false;
565     }
566 
567     return canCreateImageExternal(format, type, tiling, createFlags, usageFlags,
568                                   VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA);
569 }
570 
createImage2DZirconVmo(VkFormat format,VkImageCreateFlags createFlags,VkImageUsageFlags usageFlags,VkExtent3D extent,VkImage * imageOut,VkDeviceMemory * deviceMemoryOut,VkDeviceSize * deviceMemorySizeOut)571 VkResult VulkanExternalHelper::createImage2DZirconVmo(VkFormat format,
572                                                       VkImageCreateFlags createFlags,
573                                                       VkImageUsageFlags usageFlags,
574                                                       VkExtent3D extent,
575                                                       VkImage *imageOut,
576                                                       VkDeviceMemory *deviceMemoryOut,
577                                                       VkDeviceSize *deviceMemorySizeOut)
578 {
579     return createImage2DExternal(format, createFlags, usageFlags, extent,
580                                  VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA, imageOut,
581                                  deviceMemoryOut, deviceMemorySizeOut);
582 }
583 
exportMemoryZirconVmo(VkDeviceMemory deviceMemory,zx_handle_t * vmo)584 VkResult VulkanExternalHelper::exportMemoryZirconVmo(VkDeviceMemory deviceMemory, zx_handle_t *vmo)
585 {
586     VkMemoryGetZirconHandleInfoFUCHSIA memoryGetZirconHandleInfo = {
587         /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_GET_ZIRCON_HANDLE_INFO_FUCHSIA,
588         /* .pNext = */ nullptr,
589         /* .memory = */ deviceMemory,
590         /* .handleType = */ VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA,
591     };
592 
593     return vkGetMemoryZirconHandleFUCHSIA(mDevice, &memoryGetZirconHandleInfo, vmo);
594 }
595 
canCreateSemaphoreOpaqueFd() const596 bool VulkanExternalHelper::canCreateSemaphoreOpaqueFd() const
597 {
598     if (!mHasExternalSemaphoreFd || !vkGetPhysicalDeviceExternalSemaphorePropertiesKHR)
599     {
600         return false;
601     }
602 
603     VkPhysicalDeviceExternalSemaphoreInfo externalSemaphoreInfo = {
604         /* .sType = */ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO,
605         /* .pNext = */ nullptr,
606         /* .handleType = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
607     };
608 
609     VkExternalSemaphoreProperties externalSemaphoreProperties = {
610         /* .sType = */ VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES,
611     };
612     vkGetPhysicalDeviceExternalSemaphorePropertiesKHR(mPhysicalDevice, &externalSemaphoreInfo,
613                                                       &externalSemaphoreProperties);
614 
615     constexpr VkExternalSemaphoreFeatureFlags kRequiredFeatures =
616         VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT;
617 
618     if ((externalSemaphoreProperties.externalSemaphoreFeatures & kRequiredFeatures) !=
619         kRequiredFeatures)
620     {
621         return false;
622     }
623 
624     return true;
625 }
626 
createSemaphoreOpaqueFd(VkSemaphore * semaphore)627 VkResult VulkanExternalHelper::createSemaphoreOpaqueFd(VkSemaphore *semaphore)
628 {
629     VkExportSemaphoreCreateInfo exportSemaphoreCreateInfo = {
630         /* .sType = */ VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO,
631         /* .pNext = */ nullptr,
632         /* .handleTypes = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
633     };
634 
635     VkSemaphoreCreateInfo semaphoreCreateInfo = {
636         /* .sType = */ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
637         /* .pNext = */ &exportSemaphoreCreateInfo,
638         /* .flags = */ 0,
639     };
640 
641     return vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, semaphore);
642 }
643 
exportSemaphoreOpaqueFd(VkSemaphore semaphore,int * fd)644 VkResult VulkanExternalHelper::exportSemaphoreOpaqueFd(VkSemaphore semaphore, int *fd)
645 {
646     VkSemaphoreGetFdInfoKHR semaphoreGetFdInfo = {
647         /* .sType = */ VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
648         /* .pNext = */ nullptr,
649         /* .semaphore = */ semaphore,
650         /* .handleType = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
651     };
652 
653     return vkGetSemaphoreFdKHR(mDevice, &semaphoreGetFdInfo, fd);
654 }
655 
canCreateSemaphoreZirconEvent() const656 bool VulkanExternalHelper::canCreateSemaphoreZirconEvent() const
657 {
658     if (!mHasExternalSemaphoreFuchsia || !vkGetPhysicalDeviceExternalSemaphorePropertiesKHR)
659     {
660         return false;
661     }
662 
663     VkPhysicalDeviceExternalSemaphoreInfo externalSemaphoreInfo = {
664         /* .sType = */ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO,
665         /* .pNext = */ nullptr,
666         /* .handleType = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA,
667     };
668 
669     VkExternalSemaphoreProperties externalSemaphoreProperties = {
670         /* .sType = */ VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES,
671     };
672     vkGetPhysicalDeviceExternalSemaphorePropertiesKHR(mPhysicalDevice, &externalSemaphoreInfo,
673                                                       &externalSemaphoreProperties);
674 
675     constexpr VkExternalSemaphoreFeatureFlags kRequiredFeatures =
676         VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT;
677 
678     if ((externalSemaphoreProperties.externalSemaphoreFeatures & kRequiredFeatures) !=
679         kRequiredFeatures)
680     {
681         return false;
682     }
683 
684     return true;
685 }
686 
createSemaphoreZirconEvent(VkSemaphore * semaphore)687 VkResult VulkanExternalHelper::createSemaphoreZirconEvent(VkSemaphore *semaphore)
688 {
689     VkExportSemaphoreCreateInfo exportSemaphoreCreateInfo = {
690         /* .sType = */ VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO,
691         /* .pNext = */ nullptr,
692         /* .handleTypes = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA,
693     };
694 
695     VkSemaphoreCreateInfo semaphoreCreateInfo = {
696         /* .sType = */ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
697         /* .pNext = */ &exportSemaphoreCreateInfo,
698         /* .flags = */ 0,
699     };
700 
701     return vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, semaphore);
702 }
703 
exportSemaphoreZirconEvent(VkSemaphore semaphore,zx_handle_t * event)704 VkResult VulkanExternalHelper::exportSemaphoreZirconEvent(VkSemaphore semaphore, zx_handle_t *event)
705 {
706     VkSemaphoreGetZirconHandleInfoFUCHSIA semaphoreGetZirconHandleInfo = {
707         /* .sType = */ VK_STRUCTURE_TYPE_SEMAPHORE_GET_ZIRCON_HANDLE_INFO_FUCHSIA,
708         /* .pNext = */ nullptr,
709         /* .semaphore = */ semaphore,
710         /* .handleType = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA,
711     };
712 
713     return vkGetSemaphoreZirconHandleFUCHSIA(mDevice, &semaphoreGetZirconHandleInfo, event);
714 }
715 
releaseImageAndSignalSemaphore(VkImage image,VkImageLayout oldLayout,VkImageLayout newLayout,VkSemaphore semaphore)716 void VulkanExternalHelper::releaseImageAndSignalSemaphore(VkImage image,
717                                                           VkImageLayout oldLayout,
718                                                           VkImageLayout newLayout,
719                                                           VkSemaphore semaphore)
720 {
721     VkResult result;
722 
723     VkCommandBuffer commandBuffers[]                      = {VK_NULL_HANDLE};
724     constexpr uint32_t commandBufferCount                 = std::extent<decltype(commandBuffers)>();
725     VkCommandBufferAllocateInfo commandBufferAllocateInfo = {
726         /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
727         /* .pNext = */ nullptr,
728         /* .commandPool = */ mCommandPool,
729         /* .level = */ VK_COMMAND_BUFFER_LEVEL_PRIMARY,
730         /* .commandBufferCount = */ commandBufferCount,
731     };
732 
733     result = vkAllocateCommandBuffers(mDevice, &commandBufferAllocateInfo, commandBuffers);
734     ASSERT(result == VK_SUCCESS);
735 
736     VkCommandBufferBeginInfo commandBufferBeginInfo = {
737         /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
738         /* .pNext = */ nullptr,
739         /* .flags = */ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
740         /* .pInheritanceInfo = */ nullptr,
741     };
742     result = vkBeginCommandBuffer(commandBuffers[0], &commandBufferBeginInfo);
743     ASSERT(result == VK_SUCCESS);
744 
745     ImageMemoryBarrier(commandBuffers[0], image, mGraphicsQueueFamilyIndex,
746                        VK_QUEUE_FAMILY_EXTERNAL, oldLayout, newLayout);
747 
748     result = vkEndCommandBuffer(commandBuffers[0]);
749     ASSERT(result == VK_SUCCESS);
750 
751     const VkSemaphore signalSemaphores[] = {
752         semaphore,
753     };
754     constexpr uint32_t signalSemaphoreCount = std::extent<decltype(signalSemaphores)>();
755 
756     const VkSubmitInfo submits[] = {
757         /* [0] = */ {
758             /* .sType */ VK_STRUCTURE_TYPE_SUBMIT_INFO,
759             /* .pNext = */ nullptr,
760             /* .waitSemaphoreCount = */ 0,
761             /* .pWaitSemaphores = */ nullptr,
762             /* .pWaitDstStageMask = */ nullptr,
763             /* .commandBufferCount = */ commandBufferCount,
764             /* .pCommandBuffers = */ commandBuffers,
765             /* .signalSemaphoreCount = */ signalSemaphoreCount,
766             /* .pSignalSemaphores = */ signalSemaphores,
767         },
768     };
769     constexpr uint32_t submitCount = std::extent<decltype(submits)>();
770 
771     const VkFence fence = VK_NULL_HANDLE;
772     result              = vkQueueSubmit(mGraphicsQueue, submitCount, submits, fence);
773     ASSERT(result == VK_SUCCESS);
774 }
775 
waitSemaphoreAndAcquireImage(VkImage image,VkImageLayout oldLayout,VkImageLayout newLayout,VkSemaphore semaphore)776 void VulkanExternalHelper::waitSemaphoreAndAcquireImage(VkImage image,
777                                                         VkImageLayout oldLayout,
778                                                         VkImageLayout newLayout,
779                                                         VkSemaphore semaphore)
780 {
781     VkResult result;
782 
783     VkCommandBuffer commandBuffers[]                      = {VK_NULL_HANDLE};
784     constexpr uint32_t commandBufferCount                 = std::extent<decltype(commandBuffers)>();
785     VkCommandBufferAllocateInfo commandBufferAllocateInfo = {
786         /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
787         /* .pNext = */ nullptr,
788         /* .commandPool = */ mCommandPool,
789         /* .level = */ VK_COMMAND_BUFFER_LEVEL_PRIMARY,
790         /* .commandBufferCount = */ commandBufferCount,
791     };
792 
793     result = vkAllocateCommandBuffers(mDevice, &commandBufferAllocateInfo, commandBuffers);
794     ASSERT(result == VK_SUCCESS);
795 
796     VkCommandBufferBeginInfo commandBufferBeginInfo = {
797         /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
798         /* .pNext = */ nullptr,
799         /* .flags = */ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
800         /* .pInheritanceInfo = */ nullptr,
801     };
802     result = vkBeginCommandBuffer(commandBuffers[0], &commandBufferBeginInfo);
803     ASSERT(result == VK_SUCCESS);
804 
805     ImageMemoryBarrier(commandBuffers[0], image, VK_QUEUE_FAMILY_EXTERNAL,
806                        mGraphicsQueueFamilyIndex, oldLayout, newLayout);
807 
808     result = vkEndCommandBuffer(commandBuffers[0]);
809     ASSERT(result == VK_SUCCESS);
810 
811     const VkSemaphore waitSemaphores[] = {
812         semaphore,
813     };
814     const VkPipelineStageFlags waitDstStageMasks[] = {
815         VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
816     };
817     constexpr uint32_t waitSemaphoreCount    = std::extent<decltype(waitSemaphores)>();
818     constexpr uint32_t waitDstStageMaskCount = std::extent<decltype(waitDstStageMasks)>();
819     static_assert(waitSemaphoreCount == waitDstStageMaskCount,
820                   "waitSemaphores and waitDstStageMasks must be the same length");
821 
822     const VkSubmitInfo submits[] = {
823         /* [0] = */ {
824             /* .sType */ VK_STRUCTURE_TYPE_SUBMIT_INFO,
825             /* .pNext = */ nullptr,
826             /* .waitSemaphoreCount = */ waitSemaphoreCount,
827             /* .pWaitSemaphores = */ waitSemaphores,
828             /* .pWaitDstStageMask = */ waitDstStageMasks,
829             /* .commandBufferCount = */ commandBufferCount,
830             /* .pCommandBuffers = */ commandBuffers,
831             /* .signalSemaphoreCount = */ 0,
832             /* .pSignalSemaphores = */ nullptr,
833         },
834     };
835     constexpr uint32_t submitCount = std::extent<decltype(submits)>();
836 
837     const VkFence fence = VK_NULL_HANDLE;
838     result              = vkQueueSubmit(mGraphicsQueue, submitCount, submits, fence);
839     ASSERT(result == VK_SUCCESS);
840 }
841 
readPixels(VkImage srcImage,VkImageLayout srcImageLayout,VkFormat srcImageFormat,VkOffset3D imageOffset,VkExtent3D imageExtent,void * pixels,size_t pixelsSize)842 void VulkanExternalHelper::readPixels(VkImage srcImage,
843                                       VkImageLayout srcImageLayout,
844                                       VkFormat srcImageFormat,
845                                       VkOffset3D imageOffset,
846                                       VkExtent3D imageExtent,
847                                       void *pixels,
848                                       size_t pixelsSize)
849 {
850     ASSERT(srcImageFormat == VK_FORMAT_B8G8R8A8_UNORM ||
851            srcImageFormat == VK_FORMAT_R8G8B8A8_UNORM);
852     ASSERT(imageExtent.depth == 1);
853     ASSERT(pixelsSize == 4 * imageExtent.width * imageExtent.height);
854 
855     VkBufferCreateInfo bufferCreateInfo = {
856         /* .sType = */ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
857         /* .pNext = */ nullptr,
858         /* .flags = */ 0,
859         /* .size = */ pixelsSize,
860         /* .usage = */ VK_BUFFER_USAGE_TRANSFER_DST_BIT,
861         /* .sharingMode = */ VK_SHARING_MODE_EXCLUSIVE,
862         /* .queueFamilyIndexCount = */ 0,
863         /* .pQueueFamilyIndices = */ nullptr,
864     };
865     VkBuffer stagingBuffer = VK_NULL_HANDLE;
866     VkResult result        = vkCreateBuffer(mDevice, &bufferCreateInfo, nullptr, &stagingBuffer);
867     ASSERT(result == VK_SUCCESS);
868 
869     VkMemoryPropertyFlags requestedMemoryPropertyFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
870     VkMemoryRequirements memoryRequirements;
871     vkGetBufferMemoryRequirements(mDevice, stagingBuffer, &memoryRequirements);
872     uint32_t memoryTypeIndex = FindMemoryType(mMemoryProperties, memoryRequirements.memoryTypeBits,
873                                               requestedMemoryPropertyFlags);
874     ASSERT(memoryTypeIndex != UINT32_MAX);
875     VkDeviceSize deviceMemorySize = memoryRequirements.size;
876 
877     VkMemoryDedicatedAllocateInfoKHR memoryDedicatedAllocateInfo = {
878         /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR,
879         /* .pNext = */ nullptr,
880         /* .image = */ VK_NULL_HANDLE,
881         /* .buffer = */ stagingBuffer,
882     };
883     VkMemoryAllocateInfo memoryAllocateInfo = {
884         /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
885         /* .pNext = */ &memoryDedicatedAllocateInfo,
886         /* .allocationSize = */ deviceMemorySize,
887         /* .memoryTypeIndex = */ memoryTypeIndex,
888     };
889 
890     VkDeviceMemory deviceMemory = VK_NULL_HANDLE;
891     result = vkAllocateMemory(mDevice, &memoryAllocateInfo, nullptr, &deviceMemory);
892     ASSERT(result == VK_SUCCESS);
893 
894     result = vkBindBufferMemory(mDevice, stagingBuffer, deviceMemory, 0 /* memoryOffset */);
895     ASSERT(result == VK_SUCCESS);
896 
897     VkCommandBuffer commandBuffers[]                      = {VK_NULL_HANDLE};
898     constexpr uint32_t commandBufferCount                 = std::extent<decltype(commandBuffers)>();
899     VkCommandBufferAllocateInfo commandBufferAllocateInfo = {
900         /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
901         /* .pNext = */ nullptr,
902         /* .commandPool = */ mCommandPool,
903         /* .level = */ VK_COMMAND_BUFFER_LEVEL_PRIMARY,
904         /* .commandBufferCount = */ commandBufferCount,
905     };
906 
907     result = vkAllocateCommandBuffers(mDevice, &commandBufferAllocateInfo, commandBuffers);
908     ASSERT(result == VK_SUCCESS);
909 
910     VkCommandBufferBeginInfo commandBufferBeginInfo = {
911         /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
912         /* .pNext = */ nullptr,
913         /* .flags = */ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
914         /* .pInheritanceInfo = */ nullptr,
915     };
916     result = vkBeginCommandBuffer(commandBuffers[0], &commandBufferBeginInfo);
917     ASSERT(result == VK_SUCCESS);
918 
919     VkBufferImageCopy bufferImageCopies[] = {
920         /* [0] = */ {
921             /* .bufferOffset = */ 0,
922             /* .bufferRowLength = */ 0,
923             /* .bufferImageHeight = */ 0,
924             /* .imageSubresources = */
925             {
926                 /* .aspectMask = */ VK_IMAGE_ASPECT_COLOR_BIT,
927                 /* .mipLevel = */ 0,
928                 /* .baseArrayLayer = */ 0,
929                 /* .layerCount = */ 1,
930             },
931             /* .imageOffset = */ imageOffset,
932             /* .imageExtent = */ imageExtent,
933         },
934     };
935     constexpr uint32_t bufferImageCopyCount = std::extent<decltype(bufferImageCopies)>();
936 
937     vkCmdCopyImageToBuffer(commandBuffers[0], srcImage, srcImageLayout, stagingBuffer,
938                            bufferImageCopyCount, bufferImageCopies);
939 
940     VkMemoryBarrier memoryBarriers[] = {
941         /* [0] = */ {/* .sType = */ VK_STRUCTURE_TYPE_MEMORY_BARRIER,
942                      /* .pNext = */ nullptr,
943                      /* .srcAccessMask = */ VK_ACCESS_MEMORY_WRITE_BIT,
944                      /* .dstAccessMask = */ VK_ACCESS_HOST_READ_BIT},
945     };
946     constexpr uint32_t memoryBarrierCount = std::extent<decltype(memoryBarriers)>();
947     vkCmdPipelineBarrier(commandBuffers[0], VK_PIPELINE_STAGE_TRANSFER_BIT,
948                          VK_PIPELINE_STAGE_HOST_BIT, 0 /* dependencyFlags */, memoryBarrierCount,
949                          memoryBarriers, 0, nullptr, 0, nullptr);
950 
951     result = vkEndCommandBuffer(commandBuffers[0]);
952     ASSERT(result == VK_SUCCESS);
953 
954     const VkSubmitInfo submits[] = {
955         /* [0] = */ {
956             /* .sType */ VK_STRUCTURE_TYPE_SUBMIT_INFO,
957             /* .pNext = */ nullptr,
958             /* .waitSemaphoreCount = */ 0,
959             /* .pWaitSemaphores = */ nullptr,
960             /* .pWaitDstStageMask = */ nullptr,
961             /* .commandBufferCount = */ commandBufferCount,
962             /* .pCommandBuffers = */ commandBuffers,
963             /* .signalSemaphoreCount = */ 0,
964             /* .pSignalSemaphores = */ nullptr,
965         },
966     };
967     constexpr uint32_t submitCount = std::extent<decltype(submits)>();
968 
969     const VkFence fence = VK_NULL_HANDLE;
970     result              = vkQueueSubmit(mGraphicsQueue, submitCount, submits, fence);
971     ASSERT(result == VK_SUCCESS);
972 
973     result = vkQueueWaitIdle(mGraphicsQueue);
974     ASSERT(result == VK_SUCCESS);
975 
976     vkFreeCommandBuffers(mDevice, mCommandPool, commandBufferCount, commandBuffers);
977 
978     void *stagingMemory = nullptr;
979     result = vkMapMemory(mDevice, deviceMemory, 0 /* offset */, pixelsSize, 0 /* flags */,
980                          &stagingMemory);
981     ASSERT(result == VK_SUCCESS);
982 
983     VkMappedMemoryRange memoryRanges[] = {
984         /* [0] = */ {
985             /* .sType = */ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
986             /* .pNext = */ nullptr,
987             /* .memory = */ deviceMemory,
988             /* .offset = */ 0,
989             /* .size = */ pixelsSize,
990         },
991     };
992     constexpr uint32_t memoryRangeCount = std::extent<decltype(memoryRanges)>();
993 
994     result = vkInvalidateMappedMemoryRanges(mDevice, memoryRangeCount, memoryRanges);
995     ASSERT(result == VK_SUCCESS);
996 
997     memcpy(pixels, stagingMemory, pixelsSize);
998 
999     vkUnmapMemory(mDevice, deviceMemory);
1000     vkFreeMemory(mDevice, deviceMemory, nullptr);
1001     vkDestroyBuffer(mDevice, stagingBuffer, nullptr);
1002 }
1003 
1004 }  // namespace angle
1005