• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
24 constexpr VkImageUsageFlags kRequiredImageUsageFlags =
25     VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
26     VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
27     VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
28 
EnumerateInstanceExtensionProperties(const char * layerName)29 std::vector<VkExtensionProperties> EnumerateInstanceExtensionProperties(const char *layerName)
30 {
31     uint32_t instanceExtensionCount;
32     VkResult result =
33         vkEnumerateInstanceExtensionProperties(layerName, &instanceExtensionCount, nullptr);
34     ASSERT(result == VK_SUCCESS);
35     std::vector<VkExtensionProperties> instanceExtensionProperties(instanceExtensionCount);
36     result = vkEnumerateInstanceExtensionProperties(layerName, &instanceExtensionCount,
37                                                     instanceExtensionProperties.data());
38     ASSERT(result == VK_SUCCESS);
39     return instanceExtensionProperties;
40 }
41 
EnumeratePhysicalDevices(VkInstance instance)42 std::vector<VkPhysicalDevice> EnumeratePhysicalDevices(VkInstance instance)
43 {
44     uint32_t physicalDeviceCount;
45     VkResult result = vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr);
46     ASSERT(result == VK_SUCCESS);
47     std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
48     result = vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices.data());
49     return physicalDevices;
50 }
51 
EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice,const char * layerName)52 std::vector<VkExtensionProperties> EnumerateDeviceExtensionProperties(
53     VkPhysicalDevice physicalDevice,
54     const char *layerName)
55 {
56     uint32_t deviceExtensionCount;
57     VkResult result = vkEnumerateDeviceExtensionProperties(physicalDevice, layerName,
58                                                            &deviceExtensionCount, nullptr);
59     ASSERT(result == VK_SUCCESS);
60     std::vector<VkExtensionProperties> deviceExtensionProperties(deviceExtensionCount);
61     result = vkEnumerateDeviceExtensionProperties(physicalDevice, layerName, &deviceExtensionCount,
62                                                   deviceExtensionProperties.data());
63     ASSERT(result == VK_SUCCESS);
64     return deviceExtensionProperties;
65 }
66 
GetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice physicalDevice)67 std::vector<VkQueueFamilyProperties> GetPhysicalDeviceQueueFamilyProperties(
68     VkPhysicalDevice physicalDevice)
69 {
70     uint32_t queueFamilyPropertyCount;
71     vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyPropertyCount, nullptr);
72     std::vector<VkQueueFamilyProperties> physicalDeviceQueueFamilyProperties(
73         queueFamilyPropertyCount);
74     vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyPropertyCount,
75                                              physicalDeviceQueueFamilyProperties.data());
76     return physicalDeviceQueueFamilyProperties;
77 }
78 
HasExtension(const std::vector<VkExtensionProperties> instanceExtensions,const char * extensionName)79 bool HasExtension(const std::vector<VkExtensionProperties> instanceExtensions,
80                   const char *extensionName)
81 {
82     for (const auto &extensionProperties : instanceExtensions)
83     {
84         if (!strcmp(extensionProperties.extensionName, extensionName))
85             return true;
86     }
87 
88     return false;
89 }
90 
HasExtension(const std::vector<const char * > enabledExtensions,const char * extensionName)91 bool HasExtension(const std::vector<const char *> enabledExtensions, const char *extensionName)
92 {
93     for (const char *enabledExtension : enabledExtensions)
94     {
95         if (!strcmp(enabledExtension, extensionName))
96             return true;
97     }
98 
99     return false;
100 }
101 
FindMemoryType(const VkPhysicalDeviceMemoryProperties & memoryProperties,uint32_t memoryTypeBits,VkMemoryPropertyFlags requiredMemoryPropertyFlags)102 uint32_t FindMemoryType(const VkPhysicalDeviceMemoryProperties &memoryProperties,
103                         uint32_t memoryTypeBits,
104                         VkMemoryPropertyFlags requiredMemoryPropertyFlags)
105 {
106     for (size_t memoryIndex : angle::BitSet32<32>(memoryTypeBits))
107     {
108         ASSERT(memoryIndex < memoryProperties.memoryTypeCount);
109 
110         if ((memoryProperties.memoryTypes[memoryIndex].propertyFlags &
111              requiredMemoryPropertyFlags) == requiredMemoryPropertyFlags)
112         {
113             return static_cast<uint32_t>(memoryIndex);
114         }
115     }
116 
117     return UINT32_MAX;
118 }
119 
ImageMemoryBarrier(VkCommandBuffer commandBuffer,VkImage image,uint32_t srcQueueFamilyIndex,uint32_t dstQueueFamilyIndex,VkImageLayout oldLayout,VkImageLayout newLayout)120 void ImageMemoryBarrier(VkCommandBuffer commandBuffer,
121                         VkImage image,
122                         uint32_t srcQueueFamilyIndex,
123                         uint32_t dstQueueFamilyIndex,
124                         VkImageLayout oldLayout,
125                         VkImageLayout newLayout)
126 {
127     const VkImageMemoryBarrier imageMemoryBarriers[] = {
128         /* [0] = */ {/* .sType = */ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
129                      /* .pNext = */ nullptr,
130                      /* .srcAccessMask = */ VK_ACCESS_MEMORY_WRITE_BIT,
131                      /* .dstAccessMask = */ VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
132                      /* .oldLayout = */ oldLayout,
133                      /* .newLayout = */ newLayout,
134                      /* .srcQueueFamilyIndex = */ srcQueueFamilyIndex,
135                      /* .dstQueueFamilyIndex = */ dstQueueFamilyIndex,
136                      /* .image = */ image,
137                      /* .subresourceRange = */
138                      {
139                          /* .aspectMask = */ VK_IMAGE_ASPECT_COLOR_BIT,
140                          /* .basicMiplevel = */ 0,
141                          /* .levelCount = */ 1,
142                          /* .baseArrayLayer = */ 0,
143                          /* .layerCount = */ 1,
144                      }}};
145     const uint32_t imageMemoryBarrierCount = std::extent<decltype(imageMemoryBarriers)>();
146 
147     constexpr VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
148     constexpr VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
149     const VkDependencyFlags dependencyFlags     = 0;
150 
151     vkCmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, 0, nullptr, 0,
152                          nullptr, imageMemoryBarrierCount, imageMemoryBarriers);
153 }
154 
155 }  // namespace
156 
VulkanExternalHelper()157 VulkanExternalHelper::VulkanExternalHelper() {}
158 
~VulkanExternalHelper()159 VulkanExternalHelper::~VulkanExternalHelper()
160 {
161     if (mDevice != VK_NULL_HANDLE)
162     {
163         vkDeviceWaitIdle(mDevice);
164     }
165 
166     if (mCommandPool != VK_NULL_HANDLE)
167     {
168         vkDestroyCommandPool(mDevice, mCommandPool, nullptr);
169     }
170 
171     if (mDevice != VK_NULL_HANDLE)
172     {
173         vkDestroyDevice(mDevice, nullptr);
174 
175         mDevice        = VK_NULL_HANDLE;
176         mGraphicsQueue = VK_NULL_HANDLE;
177     }
178 
179     if (mInstance != VK_NULL_HANDLE)
180     {
181         vkDestroyInstance(mInstance, nullptr);
182 
183         mInstance = VK_NULL_HANDLE;
184     }
185 }
186 
initialize(bool useSwiftshader,bool enableValidationLayers)187 void VulkanExternalHelper::initialize(bool useSwiftshader, bool enableValidationLayers)
188 {
189     vk::ICD icd = useSwiftshader ? vk::ICD::SwiftShader : vk::ICD::Default;
190 
191     vk::ScopedVkLoaderEnvironment scopedEnvironment(enableValidationLayers, 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 (enableValidationLayers)
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 = */ 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,VkExternalMemoryHandleTypeFlagBits handleType) const374 bool VulkanExternalHelper::canCreateImageExternal(
375     VkFormat format,
376     VkImageType type,
377     VkImageTiling tiling,
378     VkExternalMemoryHandleTypeFlagBits handleType) const
379 {
380     VkPhysicalDeviceExternalImageFormatInfo externalImageFormatInfo = {
381         /* .sType = */ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
382         /* .pNext = */ nullptr,
383         /* .handleType = */ handleType,
384     };
385 
386     VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = {
387         /* .sType = */ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
388         /* .pNext = */ &externalImageFormatInfo,
389         /* .format = */ format,
390         /* .type = */ type,
391         /* .tiling = */ tiling,
392         /* .usage = */ kRequiredImageUsageFlags,
393         /* .flags = */ 0,
394     };
395 
396     VkExternalImageFormatProperties externalImageFormatProperties = {
397         /* .sType = */ VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES,
398         /* .pNext = */ nullptr,
399     };
400 
401     VkImageFormatProperties2 imageFormatProperties = {
402         /* .sType = */ VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2,
403         /* .pNext = */ &externalImageFormatProperties};
404 
405     VkResult result = vkGetPhysicalDeviceImageFormatProperties2(mPhysicalDevice, &imageFormatInfo,
406                                                                 &imageFormatProperties);
407     if (result == VK_ERROR_FORMAT_NOT_SUPPORTED)
408     {
409         return false;
410     }
411 
412     ASSERT(result == VK_SUCCESS);
413 
414     constexpr VkExternalMemoryFeatureFlags kRequiredFeatures =
415         VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT;
416     if ((externalImageFormatProperties.externalMemoryProperties.externalMemoryFeatures &
417          kRequiredFeatures) != kRequiredFeatures)
418     {
419         return false;
420     }
421 
422     return true;
423 }
424 
createImage2DExternal(VkFormat format,VkExtent3D extent,VkExternalMemoryHandleTypeFlags handleTypes,VkImage * imageOut,VkDeviceMemory * deviceMemoryOut,VkDeviceSize * deviceMemorySizeOut)425 VkResult VulkanExternalHelper::createImage2DExternal(VkFormat format,
426                                                      VkExtent3D extent,
427                                                      VkExternalMemoryHandleTypeFlags handleTypes,
428                                                      VkImage *imageOut,
429                                                      VkDeviceMemory *deviceMemoryOut,
430                                                      VkDeviceSize *deviceMemorySizeOut)
431 {
432     VkExternalMemoryImageCreateInfoKHR externalMemoryImageCreateInfo = {
433         /* .sType = */ VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
434         /* .pNext = */ nullptr,
435         /* .handleTypes = */ handleTypes,
436     };
437 
438     VkImageCreateInfo imageCreateInfo = {
439         /* .sType = */ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
440         /* .pNext = */ &externalMemoryImageCreateInfo,
441         /* .flags = */ 0,
442         /* .imageType = */ VK_IMAGE_TYPE_2D,
443         /* .format = */ format,
444         /* .extent = */ extent,
445         /* .mipLevels = */ 1,
446         /* .arrayLayers = */ 1,
447         /* .samples = */ VK_SAMPLE_COUNT_1_BIT,
448         /* .tiling = */ VK_IMAGE_TILING_OPTIMAL,
449         /* .usage = */ kRequiredImageUsageFlags,
450         /* .sharingMode = */ VK_SHARING_MODE_EXCLUSIVE,
451         /* .queueFamilyIndexCount = */ 0,
452         /* .pQueueFamilyIndices = */ nullptr,
453         /* initialLayout = */ VK_IMAGE_LAYOUT_UNDEFINED,
454     };
455 
456     VkImage image   = VK_NULL_HANDLE;
457     VkResult result = vkCreateImage(mDevice, &imageCreateInfo, nullptr, &image);
458     if (result != VK_SUCCESS)
459     {
460         return result;
461     }
462 
463     VkMemoryPropertyFlags requestedMemoryPropertyFlags = 0;
464     VkMemoryRequirements memoryRequirements;
465     vkGetImageMemoryRequirements(mDevice, image, &memoryRequirements);
466     uint32_t memoryTypeIndex = FindMemoryType(mMemoryProperties, memoryRequirements.memoryTypeBits,
467                                               requestedMemoryPropertyFlags);
468     ASSERT(memoryTypeIndex != UINT32_MAX);
469     VkDeviceSize deviceMemorySize = memoryRequirements.size;
470 
471     VkExportMemoryAllocateInfo exportMemoryAllocateInfo = {
472         /* .sType = */ VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO,
473         /* .pNext = */ nullptr,
474         /* .handleTypes = */ handleTypes,
475     };
476     VkMemoryDedicatedAllocateInfoKHR memoryDedicatedAllocateInfo = {
477         /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR,
478         /* .pNext = */ &exportMemoryAllocateInfo,
479         /* .image = */ image,
480     };
481     VkMemoryAllocateInfo memoryAllocateInfo = {
482         /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
483         /* .pNext = */ &memoryDedicatedAllocateInfo,
484         /* .allocationSize = */ deviceMemorySize,
485         /* .memoryTypeIndex = */ memoryTypeIndex,
486     };
487 
488     VkDeviceMemory deviceMemory = VK_NULL_HANDLE;
489     result = vkAllocateMemory(mDevice, &memoryAllocateInfo, nullptr, &deviceMemory);
490     if (result != VK_SUCCESS)
491     {
492         vkDestroyImage(mDevice, image, nullptr);
493         return result;
494     }
495 
496     VkDeviceSize memoryOffset = 0;
497     result                    = vkBindImageMemory(mDevice, image, deviceMemory, memoryOffset);
498     if (result != VK_SUCCESS)
499     {
500         vkFreeMemory(mDevice, deviceMemory, nullptr);
501         vkDestroyImage(mDevice, image, nullptr);
502         return result;
503     }
504 
505     *imageOut            = image;
506     *deviceMemoryOut     = deviceMemory;
507     *deviceMemorySizeOut = deviceMemorySize;
508 
509     return VK_SUCCESS;
510 }
511 
canCreateImageOpaqueFd(VkFormat format,VkImageType type,VkImageTiling tiling) const512 bool VulkanExternalHelper::canCreateImageOpaqueFd(VkFormat format,
513                                                   VkImageType type,
514                                                   VkImageTiling tiling) const
515 {
516     if (!mHasExternalMemoryFd)
517     {
518         return false;
519     }
520 
521     return canCreateImageExternal(format, type, tiling,
522                                   VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT);
523 }
524 
createImage2DOpaqueFd(VkFormat format,VkExtent3D extent,VkImage * imageOut,VkDeviceMemory * deviceMemoryOut,VkDeviceSize * deviceMemorySizeOut)525 VkResult VulkanExternalHelper::createImage2DOpaqueFd(VkFormat format,
526                                                      VkExtent3D extent,
527                                                      VkImage *imageOut,
528                                                      VkDeviceMemory *deviceMemoryOut,
529                                                      VkDeviceSize *deviceMemorySizeOut)
530 {
531     return createImage2DExternal(format, extent, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
532                                  imageOut, deviceMemoryOut, deviceMemorySizeOut);
533 }
534 
exportMemoryOpaqueFd(VkDeviceMemory deviceMemory,int * fd)535 VkResult VulkanExternalHelper::exportMemoryOpaqueFd(VkDeviceMemory deviceMemory, int *fd)
536 {
537     VkMemoryGetFdInfoKHR memoryGetFdInfo = {
538         /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
539         /* .pNext = */ nullptr,
540         /* .memory = */ deviceMemory,
541         /* .handleType = */ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT,
542     };
543 
544     return vkGetMemoryFdKHR(mDevice, &memoryGetFdInfo, fd);
545 }
546 
canCreateImageZirconVmo(VkFormat format,VkImageType type,VkImageTiling tiling) const547 bool VulkanExternalHelper::canCreateImageZirconVmo(VkFormat format,
548                                                    VkImageType type,
549                                                    VkImageTiling tiling) const
550 {
551     if (!mHasExternalMemoryFuchsia)
552     {
553         return false;
554     }
555 
556     return canCreateImageExternal(format, type, tiling,
557                                   VK_EXTERNAL_MEMORY_HANDLE_TYPE_TEMP_ZIRCON_VMO_BIT_FUCHSIA);
558 }
559 
createImage2DZirconVmo(VkFormat format,VkExtent3D extent,VkImage * imageOut,VkDeviceMemory * deviceMemoryOut,VkDeviceSize * deviceMemorySizeOut)560 VkResult VulkanExternalHelper::createImage2DZirconVmo(VkFormat format,
561                                                       VkExtent3D extent,
562                                                       VkImage *imageOut,
563                                                       VkDeviceMemory *deviceMemoryOut,
564                                                       VkDeviceSize *deviceMemorySizeOut)
565 {
566     return createImage2DExternal(format, extent,
567                                  VK_EXTERNAL_MEMORY_HANDLE_TYPE_TEMP_ZIRCON_VMO_BIT_FUCHSIA,
568                                  imageOut, deviceMemoryOut, deviceMemorySizeOut);
569 }
570 
exportMemoryZirconVmo(VkDeviceMemory deviceMemory,zx_handle_t * vmo)571 VkResult VulkanExternalHelper::exportMemoryZirconVmo(VkDeviceMemory deviceMemory, zx_handle_t *vmo)
572 {
573     VkMemoryGetZirconHandleInfoFUCHSIA memoryGetZirconHandleInfo = {
574         /* .sType = */ VK_STRUCTURE_TYPE_TEMP_MEMORY_GET_ZIRCON_HANDLE_INFO_FUCHSIA,
575         /* .pNext = */ nullptr,
576         /* .memory = */ deviceMemory,
577         /* .handleType = */ VK_EXTERNAL_MEMORY_HANDLE_TYPE_TEMP_ZIRCON_VMO_BIT_FUCHSIA,
578     };
579 
580     return vkGetMemoryZirconHandleFUCHSIA(mDevice, &memoryGetZirconHandleInfo, vmo);
581 }
582 
canCreateSemaphoreOpaqueFd() const583 bool VulkanExternalHelper::canCreateSemaphoreOpaqueFd() const
584 {
585     if (!mHasExternalSemaphoreFd || !vkGetPhysicalDeviceExternalSemaphorePropertiesKHR)
586     {
587         return false;
588     }
589 
590     VkPhysicalDeviceExternalSemaphoreInfo externalSemaphoreInfo = {
591         /* .sType = */ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO,
592         /* .pNext = */ nullptr,
593         /* .handleType = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
594     };
595 
596     VkExternalSemaphoreProperties externalSemaphoreProperties = {
597         /* .sType = */ VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES,
598     };
599     vkGetPhysicalDeviceExternalSemaphorePropertiesKHR(mPhysicalDevice, &externalSemaphoreInfo,
600                                                       &externalSemaphoreProperties);
601 
602     constexpr VkExternalSemaphoreFeatureFlags kRequiredFeatures =
603         VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT;
604 
605     if ((externalSemaphoreProperties.externalSemaphoreFeatures & kRequiredFeatures) !=
606         kRequiredFeatures)
607     {
608         return false;
609     }
610 
611     return true;
612 }
613 
createSemaphoreOpaqueFd(VkSemaphore * semaphore)614 VkResult VulkanExternalHelper::createSemaphoreOpaqueFd(VkSemaphore *semaphore)
615 {
616     VkExportSemaphoreCreateInfo exportSemaphoreCreateInfo = {
617         /* .sType = */ VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO,
618         /* .pNext = */ nullptr,
619         /* .handleTypes = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
620     };
621 
622     VkSemaphoreCreateInfo semaphoreCreateInfo = {
623         /* .sType = */ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
624         /* .pNext = */ &exportSemaphoreCreateInfo,
625         /* .flags = */ 0,
626     };
627 
628     return vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, semaphore);
629 }
630 
exportSemaphoreOpaqueFd(VkSemaphore semaphore,int * fd)631 VkResult VulkanExternalHelper::exportSemaphoreOpaqueFd(VkSemaphore semaphore, int *fd)
632 {
633     VkSemaphoreGetFdInfoKHR semaphoreGetFdInfo = {
634         /* .sType = */ VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
635         /* .pNext = */ nullptr,
636         /* .semaphore = */ semaphore,
637         /* .handleType = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
638     };
639 
640     return vkGetSemaphoreFdKHR(mDevice, &semaphoreGetFdInfo, fd);
641 }
642 
canCreateSemaphoreZirconEvent() const643 bool VulkanExternalHelper::canCreateSemaphoreZirconEvent() const
644 {
645     if (!mHasExternalSemaphoreFuchsia || !vkGetPhysicalDeviceExternalSemaphorePropertiesKHR)
646     {
647         return false;
648     }
649 
650     VkPhysicalDeviceExternalSemaphoreInfo externalSemaphoreInfo = {
651         /* .sType = */ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO,
652         /* .pNext = */ nullptr,
653         /* .handleType = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA,
654     };
655 
656     VkExternalSemaphoreProperties externalSemaphoreProperties = {
657         /* .sType = */ VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES,
658     };
659     vkGetPhysicalDeviceExternalSemaphorePropertiesKHR(mPhysicalDevice, &externalSemaphoreInfo,
660                                                       &externalSemaphoreProperties);
661 
662     constexpr VkExternalSemaphoreFeatureFlags kRequiredFeatures =
663         VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT;
664 
665     if ((externalSemaphoreProperties.externalSemaphoreFeatures & kRequiredFeatures) !=
666         kRequiredFeatures)
667     {
668         return false;
669     }
670 
671     return true;
672 }
673 
createSemaphoreZirconEvent(VkSemaphore * semaphore)674 VkResult VulkanExternalHelper::createSemaphoreZirconEvent(VkSemaphore *semaphore)
675 {
676     VkExportSemaphoreCreateInfo exportSemaphoreCreateInfo = {
677         /* .sType = */ VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO,
678         /* .pNext = */ nullptr,
679         /* .handleTypes = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA,
680     };
681 
682     VkSemaphoreCreateInfo semaphoreCreateInfo = {
683         /* .sType = */ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
684         /* .pNext = */ &exportSemaphoreCreateInfo,
685         /* .flags = */ 0,
686     };
687 
688     return vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, semaphore);
689 }
690 
exportSemaphoreZirconEvent(VkSemaphore semaphore,zx_handle_t * event)691 VkResult VulkanExternalHelper::exportSemaphoreZirconEvent(VkSemaphore semaphore, zx_handle_t *event)
692 {
693     VkSemaphoreGetZirconHandleInfoFUCHSIA semaphoreGetZirconHandleInfo = {
694         /* .sType = */ VK_STRUCTURE_TYPE_TEMP_SEMAPHORE_GET_ZIRCON_HANDLE_INFO_FUCHSIA,
695         /* .pNext = */ nullptr,
696         /* .semaphore = */ semaphore,
697         /* .handleType = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA,
698     };
699 
700     return vkGetSemaphoreZirconHandleFUCHSIA(mDevice, &semaphoreGetZirconHandleInfo, event);
701 }
702 
releaseImageAndSignalSemaphore(VkImage image,VkImageLayout oldLayout,VkImageLayout newLayout,VkSemaphore semaphore)703 void VulkanExternalHelper::releaseImageAndSignalSemaphore(VkImage image,
704                                                           VkImageLayout oldLayout,
705                                                           VkImageLayout newLayout,
706                                                           VkSemaphore semaphore)
707 {
708     VkResult result;
709 
710     VkCommandBuffer commandBuffers[]                      = {VK_NULL_HANDLE};
711     constexpr uint32_t commandBufferCount                 = std::extent<decltype(commandBuffers)>();
712     VkCommandBufferAllocateInfo commandBufferAllocateInfo = {
713         /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
714         /* .pNext = */ nullptr,
715         /* .commandPool = */ mCommandPool,
716         /* .level = */ VK_COMMAND_BUFFER_LEVEL_PRIMARY,
717         /* .commandBufferCount = */ commandBufferCount,
718     };
719 
720     result = vkAllocateCommandBuffers(mDevice, &commandBufferAllocateInfo, commandBuffers);
721     ASSERT(result == VK_SUCCESS);
722 
723     VkCommandBufferBeginInfo commandBufferBeginInfo = {
724         /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
725         /* .pNext = */ nullptr,
726         /* .flags = */ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
727         /* .pInheritanceInfo = */ nullptr,
728     };
729     result = vkBeginCommandBuffer(commandBuffers[0], &commandBufferBeginInfo);
730     ASSERT(result == VK_SUCCESS);
731 
732     ImageMemoryBarrier(commandBuffers[0], image, mGraphicsQueueFamilyIndex,
733                        VK_QUEUE_FAMILY_EXTERNAL, oldLayout, newLayout);
734 
735     result = vkEndCommandBuffer(commandBuffers[0]);
736     ASSERT(result == VK_SUCCESS);
737 
738     const VkSemaphore signalSemaphores[] = {
739         semaphore,
740     };
741     constexpr uint32_t signalSemaphoreCount = std::extent<decltype(signalSemaphores)>();
742 
743     const VkSubmitInfo submits[] = {
744         /* [0] = */ {
745             /* .sType */ VK_STRUCTURE_TYPE_SUBMIT_INFO,
746             /* .pNext = */ nullptr,
747             /* .waitSemaphoreCount = */ 0,
748             /* .pWaitSemaphores = */ nullptr,
749             /* .pWaitDstStageMask = */ nullptr,
750             /* .commandBufferCount = */ commandBufferCount,
751             /* .pCommandBuffers = */ commandBuffers,
752             /* .signalSemaphoreCount = */ signalSemaphoreCount,
753             /* .pSignalSemaphores = */ signalSemaphores,
754         },
755     };
756     constexpr uint32_t submitCount = std::extent<decltype(submits)>();
757 
758     const VkFence fence = VK_NULL_HANDLE;
759     result              = vkQueueSubmit(mGraphicsQueue, submitCount, submits, fence);
760     ASSERT(result == VK_SUCCESS);
761 }
762 
waitSemaphoreAndAcquireImage(VkImage image,VkImageLayout oldLayout,VkImageLayout newLayout,VkSemaphore semaphore)763 void VulkanExternalHelper::waitSemaphoreAndAcquireImage(VkImage image,
764                                                         VkImageLayout oldLayout,
765                                                         VkImageLayout newLayout,
766                                                         VkSemaphore semaphore)
767 {
768     VkResult result;
769 
770     VkCommandBuffer commandBuffers[]                      = {VK_NULL_HANDLE};
771     constexpr uint32_t commandBufferCount                 = std::extent<decltype(commandBuffers)>();
772     VkCommandBufferAllocateInfo commandBufferAllocateInfo = {
773         /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
774         /* .pNext = */ nullptr,
775         /* .commandPool = */ mCommandPool,
776         /* .level = */ VK_COMMAND_BUFFER_LEVEL_PRIMARY,
777         /* .commandBufferCount = */ commandBufferCount,
778     };
779 
780     result = vkAllocateCommandBuffers(mDevice, &commandBufferAllocateInfo, commandBuffers);
781     ASSERT(result == VK_SUCCESS);
782 
783     VkCommandBufferBeginInfo commandBufferBeginInfo = {
784         /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
785         /* .pNext = */ nullptr,
786         /* .flags = */ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
787         /* .pInheritanceInfo = */ nullptr,
788     };
789     result = vkBeginCommandBuffer(commandBuffers[0], &commandBufferBeginInfo);
790     ASSERT(result == VK_SUCCESS);
791 
792     ImageMemoryBarrier(commandBuffers[0], image, VK_QUEUE_FAMILY_EXTERNAL,
793                        mGraphicsQueueFamilyIndex, oldLayout, newLayout);
794 
795     result = vkEndCommandBuffer(commandBuffers[0]);
796     ASSERT(result == VK_SUCCESS);
797 
798     const VkSemaphore waitSemaphores[] = {
799         semaphore,
800     };
801     const VkPipelineStageFlags waitDstStageMasks[] = {
802         VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
803     };
804     constexpr uint32_t waitSemaphoreCount    = std::extent<decltype(waitSemaphores)>();
805     constexpr uint32_t waitDstStageMaskCount = std::extent<decltype(waitDstStageMasks)>();
806     static_assert(waitSemaphoreCount == waitDstStageMaskCount,
807                   "waitSemaphores and waitDstStageMasks must be the same length");
808 
809     const VkSubmitInfo submits[] = {
810         /* [0] = */ {
811             /* .sType */ VK_STRUCTURE_TYPE_SUBMIT_INFO,
812             /* .pNext = */ nullptr,
813             /* .waitSemaphoreCount = */ waitSemaphoreCount,
814             /* .pWaitSemaphores = */ waitSemaphores,
815             /* .pWaitDstStageMask = */ waitDstStageMasks,
816             /* .commandBufferCount = */ commandBufferCount,
817             /* .pCommandBuffers = */ commandBuffers,
818             /* .signalSemaphoreCount = */ 0,
819             /* .pSignalSemaphores = */ nullptr,
820         },
821     };
822     constexpr uint32_t submitCount = std::extent<decltype(submits)>();
823 
824     const VkFence fence = VK_NULL_HANDLE;
825     result              = vkQueueSubmit(mGraphicsQueue, submitCount, submits, fence);
826     ASSERT(result == VK_SUCCESS);
827 }
828 
readPixels(VkImage srcImage,VkImageLayout srcImageLayout,VkFormat srcImageFormat,VkOffset3D imageOffset,VkExtent3D imageExtent,void * pixels,size_t pixelsSize)829 void VulkanExternalHelper::readPixels(VkImage srcImage,
830                                       VkImageLayout srcImageLayout,
831                                       VkFormat srcImageFormat,
832                                       VkOffset3D imageOffset,
833                                       VkExtent3D imageExtent,
834                                       void *pixels,
835                                       size_t pixelsSize)
836 {
837     ASSERT(srcImageFormat == VK_FORMAT_B8G8R8A8_UNORM ||
838            srcImageFormat == VK_FORMAT_R8G8B8A8_UNORM);
839     ASSERT(imageExtent.depth == 1);
840     ASSERT(pixelsSize == 4 * imageExtent.width * imageExtent.height);
841 
842     VkBufferCreateInfo bufferCreateInfo = {
843         /* .sType = */ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
844         /* .pNext = */ nullptr,
845         /* .flags = */ 0,
846         /* .size = */ pixelsSize,
847         /* .usage = */ VK_BUFFER_USAGE_TRANSFER_DST_BIT,
848         /* .sharingMode = */ VK_SHARING_MODE_EXCLUSIVE,
849         /* .queueFamilyIndexCount = */ 0,
850         /* .pQueueFamilyIndices = */ nullptr,
851     };
852     VkBuffer stagingBuffer = VK_NULL_HANDLE;
853     VkResult result        = vkCreateBuffer(mDevice, &bufferCreateInfo, nullptr, &stagingBuffer);
854     ASSERT(result == VK_SUCCESS);
855 
856     VkMemoryPropertyFlags requestedMemoryPropertyFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
857     VkMemoryRequirements memoryRequirements;
858     vkGetBufferMemoryRequirements(mDevice, stagingBuffer, &memoryRequirements);
859     uint32_t memoryTypeIndex = FindMemoryType(mMemoryProperties, memoryRequirements.memoryTypeBits,
860                                               requestedMemoryPropertyFlags);
861     ASSERT(memoryTypeIndex != UINT32_MAX);
862     VkDeviceSize deviceMemorySize = memoryRequirements.size;
863 
864     VkMemoryDedicatedAllocateInfoKHR memoryDedicatedAllocateInfo = {
865         /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR,
866         /* .pNext = */ nullptr,
867         /* .image = */ VK_NULL_HANDLE,
868         /* .buffer = */ stagingBuffer,
869     };
870     VkMemoryAllocateInfo memoryAllocateInfo = {
871         /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
872         /* .pNext = */ &memoryDedicatedAllocateInfo,
873         /* .allocationSize = */ deviceMemorySize,
874         /* .memoryTypeIndex = */ memoryTypeIndex,
875     };
876 
877     VkDeviceMemory deviceMemory = VK_NULL_HANDLE;
878     result = vkAllocateMemory(mDevice, &memoryAllocateInfo, nullptr, &deviceMemory);
879     ASSERT(result == VK_SUCCESS);
880 
881     result = vkBindBufferMemory(mDevice, stagingBuffer, deviceMemory, 0 /* memoryOffset */);
882     ASSERT(result == VK_SUCCESS);
883 
884     VkCommandBuffer commandBuffers[]                      = {VK_NULL_HANDLE};
885     constexpr uint32_t commandBufferCount                 = std::extent<decltype(commandBuffers)>();
886     VkCommandBufferAllocateInfo commandBufferAllocateInfo = {
887         /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
888         /* .pNext = */ nullptr,
889         /* .commandPool = */ mCommandPool,
890         /* .level = */ VK_COMMAND_BUFFER_LEVEL_PRIMARY,
891         /* .commandBufferCount = */ commandBufferCount,
892     };
893 
894     result = vkAllocateCommandBuffers(mDevice, &commandBufferAllocateInfo, commandBuffers);
895     ASSERT(result == VK_SUCCESS);
896 
897     VkCommandBufferBeginInfo commandBufferBeginInfo = {
898         /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
899         /* .pNext = */ nullptr,
900         /* .flags = */ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
901         /* .pInheritanceInfo = */ nullptr,
902     };
903     result = vkBeginCommandBuffer(commandBuffers[0], &commandBufferBeginInfo);
904     ASSERT(result == VK_SUCCESS);
905 
906     VkBufferImageCopy bufferImageCopies[] = {
907         /* [0] = */ {
908             /* .bufferOffset = */ 0,
909             /* .bufferRowLength = */ 0,
910             /* .bufferImageHeight = */ 0,
911             /* .imageSubresources = */
912             {
913                 /* .aspectMask = */ VK_IMAGE_ASPECT_COLOR_BIT,
914                 /* .mipLevel = */ 0,
915                 /* .baseArrayLayer = */ 0,
916                 /* .layerCount = */ 1,
917             },
918             /* .imageOffset = */ imageOffset,
919             /* .imageExtent = */ imageExtent,
920         },
921     };
922     constexpr uint32_t bufferImageCopyCount = std::extent<decltype(bufferImageCopies)>();
923 
924     vkCmdCopyImageToBuffer(commandBuffers[0], srcImage, srcImageLayout, stagingBuffer,
925                            bufferImageCopyCount, bufferImageCopies);
926 
927     VkMemoryBarrier memoryBarriers[] = {
928         /* [0] = */ {/* .sType = */ VK_STRUCTURE_TYPE_MEMORY_BARRIER,
929                      /* .pNext = */ nullptr,
930                      /* .srcAccessMask = */ VK_ACCESS_MEMORY_WRITE_BIT,
931                      /* .dstAccessMask = */ VK_ACCESS_HOST_READ_BIT},
932     };
933     constexpr uint32_t memoryBarrierCount = std::extent<decltype(memoryBarriers)>();
934     vkCmdPipelineBarrier(commandBuffers[0], VK_PIPELINE_STAGE_TRANSFER_BIT,
935                          VK_PIPELINE_STAGE_HOST_BIT, 0 /* dependencyFlags */, memoryBarrierCount,
936                          memoryBarriers, 0, nullptr, 0, nullptr);
937 
938     result = vkEndCommandBuffer(commandBuffers[0]);
939     ASSERT(result == VK_SUCCESS);
940 
941     const VkSubmitInfo submits[] = {
942         /* [0] = */ {
943             /* .sType */ VK_STRUCTURE_TYPE_SUBMIT_INFO,
944             /* .pNext = */ nullptr,
945             /* .waitSemaphoreCount = */ 0,
946             /* .pWaitSemaphores = */ nullptr,
947             /* .pWaitDstStageMask = */ nullptr,
948             /* .commandBufferCount = */ commandBufferCount,
949             /* .pCommandBuffers = */ commandBuffers,
950             /* .signalSemaphoreCount = */ 0,
951             /* .pSignalSemaphores = */ nullptr,
952         },
953     };
954     constexpr uint32_t submitCount = std::extent<decltype(submits)>();
955 
956     const VkFence fence = VK_NULL_HANDLE;
957     result              = vkQueueSubmit(mGraphicsQueue, submitCount, submits, fence);
958     ASSERT(result == VK_SUCCESS);
959 
960     result = vkQueueWaitIdle(mGraphicsQueue);
961     ASSERT(result == VK_SUCCESS);
962 
963     vkFreeCommandBuffers(mDevice, mCommandPool, commandBufferCount, commandBuffers);
964 
965     void *stagingMemory = nullptr;
966     result = vkMapMemory(mDevice, deviceMemory, 0 /* offset */, pixelsSize, 0 /* flags */,
967                          &stagingMemory);
968     ASSERT(result == VK_SUCCESS);
969 
970     VkMappedMemoryRange memoryRanges[] = {
971         /* [0] = */ {
972             /* .sType = */ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
973             /* .pNext = */ nullptr,
974             /* .memory = */ deviceMemory,
975             /* .offset = */ 0,
976             /* .size = */ pixelsSize,
977         },
978     };
979     constexpr uint32_t memoryRangeCount = std::extent<decltype(memoryRanges)>();
980 
981     result = vkInvalidateMappedMemoryRanges(mDevice, memoryRangeCount, memoryRanges);
982     ASSERT(result == VK_SUCCESS);
983 
984     memcpy(pixels, stagingMemory, pixelsSize);
985 
986     vkUnmapMemory(mDevice, deviceMemory);
987     vkFreeMemory(mDevice, deviceMemory, nullptr);
988     vkDestroyBuffer(mDevice, stagingBuffer, nullptr);
989 }
990 
991 }  // namespace angle
992