1 // Copyright (C) 2024 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "sample_base.h"
16 
17 #include <string>
18 #include <unordered_map>
19 #include <unordered_set>
20 #include <vector>
21 
22 VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
23 
24 namespace cuttlefish {
25 namespace {
26 
27 constexpr const bool kEnableValidationLayers = false;
28 
VulkanDebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,VkDebugUtilsMessageTypeFlagsEXT,const VkDebugUtilsMessengerCallbackDataEXT * pCallbackData,void *)29 static VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugCallback(
30     VkDebugUtilsMessageSeverityFlagBitsEXT severity,
31     VkDebugUtilsMessageTypeFlagsEXT,
32     const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void*) {
33   if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
34     ALOGV("%s", pCallbackData->pMessage);
35   } else if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
36     ALOGI("%s", pCallbackData->pMessage);
37   } else if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
38     ALOGW("%s", pCallbackData->pMessage);
39   } else if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
40     ALOGE("%s", pCallbackData->pMessage);
41   }
42   return VK_FALSE;
43 }
44 
GetMemoryType(const vkhpp::PhysicalDevice & physical_device,uint32_t memory_type_mask,vkhpp::MemoryPropertyFlags memoryProperties)45 Result<uint32_t> GetMemoryType(const vkhpp::PhysicalDevice& physical_device,
46                                uint32_t memory_type_mask,
47                                vkhpp::MemoryPropertyFlags memoryProperties) {
48   const auto props = physical_device.getMemoryProperties();
49   for (uint32_t i = 0; i < props.memoryTypeCount; i++) {
50     if (!(memory_type_mask & (1 << i))) {
51       continue;
52     }
53     if ((props.memoryTypes[i].propertyFlags & memoryProperties) !=
54         memoryProperties) {
55       continue;
56     }
57     return i;
58   }
59   return Err("Failed to find memory type matching " +
60              vkhpp::to_string(memoryProperties));
61 }
62 
63 }  // namespace
64 
StartUpBase(const std::vector<std::string> & requestedInstanceExtensions,const std::vector<std::string> & requestedInstanceLayers,const std::vector<std::string> & requestedDeviceExtensions)65 Result<Ok> SampleBase::StartUpBase(
66     const std::vector<std::string>& requestedInstanceExtensions,
67     const std::vector<std::string>& requestedInstanceLayers,
68     const std::vector<std::string>& requestedDeviceExtensions) {
69   VULKAN_HPP_DEFAULT_DISPATCHER.init(
70       mLoader.getProcAddress<PFN_vkGetInstanceProcAddr>(
71           "vkGetInstanceProcAddr"));
72 
73   std::vector<const char*> requestedInstanceExtensionsChars;
74   requestedInstanceExtensionsChars.reserve(requestedInstanceExtensions.size());
75   for (const auto& e : requestedInstanceExtensions) {
76     requestedInstanceExtensionsChars.push_back(e.c_str());
77   }
78   if (kEnableValidationLayers) {
79     requestedInstanceExtensionsChars.push_back(
80         VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
81   }
82 
83   std::vector<const char*> requestedInstanceLayersChars;
84   requestedInstanceLayersChars.reserve(requestedInstanceLayers.size());
85   for (const auto& l : requestedInstanceLayers) {
86     requestedInstanceLayersChars.push_back(l.c_str());
87   }
88 
89   const vkhpp::ApplicationInfo applicationInfo = {
90       .pApplicationName = "cuttlefish Sample App",
91       .applicationVersion = 1,
92       .pEngineName = "cuttlefish Sample App",
93       .engineVersion = 1,
94       .apiVersion = VK_API_VERSION_1_2,
95   };
96   const vkhpp::InstanceCreateInfo instanceCreateInfo = {
97       .pApplicationInfo = &applicationInfo,
98       .enabledLayerCount =
99           static_cast<uint32_t>(requestedInstanceLayersChars.size()),
100       .ppEnabledLayerNames = requestedInstanceLayersChars.data(),
101       .enabledExtensionCount =
102           static_cast<uint32_t>(requestedInstanceExtensionsChars.size()),
103       .ppEnabledExtensionNames = requestedInstanceExtensionsChars.data(),
104   };
105   mInstance = VK_EXPECT_RV(vkhpp::createInstanceUnique(instanceCreateInfo));
106 
107   VULKAN_HPP_DEFAULT_DISPATCHER.init(*mInstance);
108 
109   std::optional<vkhpp::UniqueDebugUtilsMessengerEXT> debugMessenger;
110   if (kEnableValidationLayers) {
111     const vkhpp::DebugUtilsMessengerCreateInfoEXT debugCreateInfo = {
112         .messageSeverity =
113             vkhpp::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose |
114             vkhpp::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
115             vkhpp::DebugUtilsMessageSeverityFlagBitsEXT::eError,
116         .messageType = vkhpp::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
117                        vkhpp::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
118                        vkhpp::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
119         .pfnUserCallback = VulkanDebugCallback,
120         .pUserData = nullptr,
121     };
122     debugMessenger = VK_EXPECT_RV(
123         mInstance->createDebugUtilsMessengerEXTUnique(debugCreateInfo));
124   }
125 
126   const auto physicalDevices =
127       VK_EXPECT_RV(mInstance->enumeratePhysicalDevices());
128   mPhysicalDevice = std::move(physicalDevices[0]);
129 
130   std::unordered_set<std::string> availableDeviceExtensions;
131   {
132     const auto exts =
133         VK_EXPECT_RV(mPhysicalDevice.enumerateDeviceExtensionProperties());
134     for (const auto& ext : exts) {
135       availableDeviceExtensions.emplace(ext.extensionName);
136     }
137   }
138   const auto features2 =
139       mPhysicalDevice
140           .getFeatures2<vkhpp::PhysicalDeviceFeatures2,  //
141                         vkhpp::PhysicalDeviceSamplerYcbcrConversionFeatures>();
142 
143   bool ycbcr_conversion_needed = false;
144 
145   std::vector<const char*> requestedDeviceExtensionsChars;
146   requestedDeviceExtensionsChars.reserve(requestedDeviceExtensions.size());
147   for (const auto& e : requestedDeviceExtensions) {
148     if (e == std::string(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME)) {
149       // The interface of VK_KHR_sampler_ycbcr_conversion was promoted to core
150       // in Vulkan 1.1 but the feature/functionality is still optional. Check
151       // here:
152       const auto& sampler_features =
153           features2.get<vkhpp::PhysicalDeviceSamplerYcbcrConversionFeatures>();
154 
155       if (sampler_features.samplerYcbcrConversion == VK_FALSE) {
156         return Err("Physical device doesn't support samplerYcbcrConversion");
157       }
158       ycbcr_conversion_needed = true;
159     } else {
160       if (availableDeviceExtensions.find(e) ==
161           availableDeviceExtensions.end()) {
162         return Err("Physical device doesn't support extension " +
163                    std::string(e));
164       }
165       requestedDeviceExtensionsChars.push_back(e.c_str());
166     }
167   }
168 
169   mQueueFamilyIndex = -1;
170   {
171     const auto props = mPhysicalDevice.getQueueFamilyProperties();
172     for (uint32_t i = 0; i < props.size(); i++) {
173       const auto& prop = props[i];
174       if (prop.queueFlags & vkhpp::QueueFlagBits::eGraphics) {
175         mQueueFamilyIndex = i;
176         break;
177       }
178     }
179   }
180 
181   const float queue_priority = 1.0f;
182   const vkhpp::DeviceQueueCreateInfo device_queue_create_info = {
183       .queueFamilyIndex = mQueueFamilyIndex,
184       .queueCount = 1,
185       .pQueuePriorities = &queue_priority,
186   };
187   const vkhpp::PhysicalDeviceVulkan11Features device_enable_features = {
188       .samplerYcbcrConversion = ycbcr_conversion_needed,
189   };
190   const vkhpp::DeviceCreateInfo deviceCreateInfo = {
191       .pNext = &device_enable_features,
192       .queueCreateInfoCount = 1,
193       .pQueueCreateInfos = &device_queue_create_info,
194       .enabledLayerCount =
195           static_cast<uint32_t>(requestedInstanceLayersChars.size()),
196       .ppEnabledLayerNames = requestedInstanceLayersChars.data(),
197       .enabledExtensionCount =
198           static_cast<uint32_t>(requestedDeviceExtensionsChars.size()),
199       .ppEnabledExtensionNames = requestedDeviceExtensionsChars.data(),
200   };
201   mDevice = VK_EXPECT_RV(mPhysicalDevice.createDeviceUnique(deviceCreateInfo));
202   mQueue = mDevice->getQueue(mQueueFamilyIndex, 0);
203 
204   mStagingBuffer =
205       VK_EXPECT(CreateBuffer(kStagingBufferSize,
206                              vkhpp::BufferUsageFlagBits::eTransferDst |
207                                  vkhpp::BufferUsageFlagBits::eTransferSrc,
208                              vkhpp::MemoryPropertyFlagBits::eHostVisible |
209                                  vkhpp::MemoryPropertyFlagBits::eHostCoherent));
210 
211   const vkhpp::FenceCreateInfo fenceCreateInfo = {
212       .flags = vkhpp::FenceCreateFlagBits::eSignaled,
213   };
214   const vkhpp::SemaphoreCreateInfo semaphoreCreateInfo = {};
215   const vkhpp::CommandPoolCreateInfo commandPoolCreateInfo = {
216       .flags = vkhpp::CommandPoolCreateFlagBits::eResetCommandBuffer,
217       .queueFamilyIndex = mQueueFamilyIndex,
218   };
219   for (uint32_t i = 0; i < kMaxFramesInFlight; i++) {
220     auto fence = VK_EXPECT_RV(mDevice->createFenceUnique(fenceCreateInfo));
221     auto readyForRender =
222         VK_EXPECT_RV(mDevice->createSemaphoreUnique(semaphoreCreateInfo));
223     auto readyForPresent =
224         VK_EXPECT_RV(mDevice->createSemaphoreUnique(semaphoreCreateInfo));
225     auto commandPool =
226         VK_EXPECT_RV(mDevice->createCommandPoolUnique(commandPoolCreateInfo));
227     const vkhpp::CommandBufferAllocateInfo commandBufferAllocateInfo = {
228         .commandPool = *commandPool,
229         .level = vkhpp::CommandBufferLevel::ePrimary,
230         .commandBufferCount = 1,
231     };
232     auto commandBuffers = VK_EXPECT_RV(
233         mDevice->allocateCommandBuffersUnique(commandBufferAllocateInfo));
234     auto commandBuffer = std::move(commandBuffers[0]);
235     mFrameObjects.push_back(PerFrameObjects{
236         .readyFence = std::move(fence),
237         .readyForRender = std::move(readyForRender),
238         .readyForPresent = std::move(readyForPresent),
239         .commandPool = std::move(commandPool),
240         .commandBuffer = std::move(commandBuffer),
241     });
242   }
243 
244   return Ok{};
245 }
246 
CleanUpBase()247 Result<Ok> SampleBase::CleanUpBase() {
248   mDevice->waitIdle();
249 
250   return Ok{};
251 }
252 
CreateBuffer(vkhpp::DeviceSize bufferSize,vkhpp::BufferUsageFlags bufferUsages,vkhpp::MemoryPropertyFlags bufferMemoryProperties)253 Result<SampleBase::BufferWithMemory> SampleBase::CreateBuffer(
254     vkhpp::DeviceSize bufferSize, vkhpp::BufferUsageFlags bufferUsages,
255     vkhpp::MemoryPropertyFlags bufferMemoryProperties) {
256   const vkhpp::BufferCreateInfo bufferCreateInfo = {
257       .size = static_cast<VkDeviceSize>(bufferSize),
258       .usage = bufferUsages,
259       .sharingMode = vkhpp::SharingMode::eExclusive,
260   };
261   auto buffer = VK_EXPECT_RV(mDevice->createBufferUnique(bufferCreateInfo));
262 
263   vkhpp::MemoryRequirements bufferMemoryRequirements{};
264   mDevice->getBufferMemoryRequirements(*buffer, &bufferMemoryRequirements);
265 
266   const auto bufferMemoryType = VK_EXPECT(
267       GetMemoryType(mPhysicalDevice, bufferMemoryRequirements.memoryTypeBits,
268                     bufferMemoryProperties));
269 
270   const vkhpp::MemoryAllocateInfo bufferMemoryAllocateInfo = {
271       .allocationSize = bufferMemoryRequirements.size,
272       .memoryTypeIndex = bufferMemoryType,
273   };
274   auto bufferMemory =
275       VK_EXPECT_RV(mDevice->allocateMemoryUnique(bufferMemoryAllocateInfo));
276 
277   VK_EXPECT_RESULT(mDevice->bindBufferMemory(*buffer, *bufferMemory, 0));
278 
279   return SampleBase::BufferWithMemory{
280       .buffer = std::move(buffer),
281       .bufferMemory = std::move(bufferMemory),
282   };
283 }
284 
CreateBufferWithData(vkhpp::DeviceSize bufferSize,vkhpp::BufferUsageFlags bufferUsages,vkhpp::MemoryPropertyFlags bufferMemoryProperties,const uint8_t * bufferData)285 Result<SampleBase::BufferWithMemory> SampleBase::CreateBufferWithData(
286     vkhpp::DeviceSize bufferSize, vkhpp::BufferUsageFlags bufferUsages,
287     vkhpp::MemoryPropertyFlags bufferMemoryProperties,
288     const uint8_t* bufferData) {
289   auto buffer = VK_EXPECT(CreateBuffer(
290       bufferSize, bufferUsages | vkhpp::BufferUsageFlagBits::eTransferDst,
291       bufferMemoryProperties));
292 
293   void* mapped = VK_EXPECT_RV(
294       mDevice->mapMemory(*mStagingBuffer.bufferMemory, 0, kStagingBufferSize));
295 
296   std::memcpy(mapped, bufferData, bufferSize);
297 
298   mDevice->unmapMemory(*mStagingBuffer.bufferMemory);
299 
300   DoCommandsImmediate([&](vkhpp::UniqueCommandBuffer& cmd) {
301     const std::vector<vkhpp::BufferCopy> regions = {
302         vkhpp::BufferCopy{
303             .srcOffset = 0,
304             .dstOffset = 0,
305             .size = bufferSize,
306         },
307     };
308     cmd->copyBuffer(*mStagingBuffer.buffer, *buffer.buffer, regions);
309     return Ok{};
310   });
311 
312   return std::move(buffer);
313 }
314 
CreateImage(uint32_t width,uint32_t height,vkhpp::Format format,vkhpp::ImageUsageFlags usages,vkhpp::MemoryPropertyFlags memoryProperties,vkhpp::ImageLayout returnedLayout)315 Result<SampleBase::ImageWithMemory> SampleBase::CreateImage(
316     uint32_t width, uint32_t height, vkhpp::Format format,
317     vkhpp::ImageUsageFlags usages, vkhpp::MemoryPropertyFlags memoryProperties,
318     vkhpp::ImageLayout returnedLayout) {
319   const vkhpp::ImageCreateInfo imageCreateInfo = {
320       .imageType = vkhpp::ImageType::e2D,
321       .format = format,
322       .extent =
323           {
324               .width = width,
325               .height = height,
326               .depth = 1,
327           },
328       .mipLevels = 1,
329       .arrayLayers = 1,
330       .samples = vkhpp::SampleCountFlagBits::e1,
331       .tiling = vkhpp::ImageTiling::eOptimal,
332       .usage = usages,
333       .sharingMode = vkhpp::SharingMode::eExclusive,
334       .initialLayout = vkhpp::ImageLayout::eUndefined,
335   };
336   auto image = VK_EXPECT_RV(mDevice->createImageUnique(imageCreateInfo));
337 
338   const auto memoryRequirements = mDevice->getImageMemoryRequirements(*image);
339   const uint32_t memoryIndex = VK_EXPECT(GetMemoryType(
340       mPhysicalDevice, memoryRequirements.memoryTypeBits, memoryProperties));
341 
342   const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
343       .allocationSize = memoryRequirements.size,
344       .memoryTypeIndex = memoryIndex,
345   };
346   auto imageMemory =
347       VK_EXPECT_RV(mDevice->allocateMemoryUnique(imageMemoryAllocateInfo));
348 
349   mDevice->bindImageMemory(*image, *imageMemory, 0);
350 
351   const vkhpp::ImageViewCreateInfo imageViewCreateInfo = {
352       .image = *image,
353       .viewType = vkhpp::ImageViewType::e2D,
354       .format = format,
355       .components =
356           {
357               .r = vkhpp::ComponentSwizzle::eIdentity,
358               .g = vkhpp::ComponentSwizzle::eIdentity,
359               .b = vkhpp::ComponentSwizzle::eIdentity,
360               .a = vkhpp::ComponentSwizzle::eIdentity,
361           },
362       .subresourceRange =
363           {
364               .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
365               .baseMipLevel = 0,
366               .levelCount = 1,
367               .baseArrayLayer = 0,
368               .layerCount = 1,
369           },
370   };
371   auto imageView =
372       VK_EXPECT_RV(mDevice->createImageViewUnique(imageViewCreateInfo));
373 
374   VK_EXPECT(DoCommandsImmediate([&](vkhpp::UniqueCommandBuffer& cmd) {
375     const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
376         vkhpp::ImageMemoryBarrier{
377             .srcAccessMask = {},
378             .dstAccessMask = vkhpp::AccessFlagBits::eTransferWrite,
379             .oldLayout = vkhpp::ImageLayout::eUndefined,
380             .newLayout = returnedLayout,
381             .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
382             .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
383             .image = *image,
384             .subresourceRange =
385                 {
386                     .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
387                     .baseMipLevel = 0,
388                     .levelCount = 1,
389                     .baseArrayLayer = 0,
390                     .layerCount = 1,
391                 },
392         },
393     };
394     cmd->pipelineBarrier(
395         /*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
396         /*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
397         /*dependencyFlags=*/{},
398         /*memoryBarriers=*/{},
399         /*bufferMemoryBarriers=*/{},
400         /*imageMemoryBarriers=*/imageMemoryBarriers);
401 
402     return Ok{};
403   }));
404 
405   return ImageWithMemory{
406       .image = std::move(image),
407       .imageMemory = std::move(imageMemory),
408       .imageView = std::move(imageView),
409   };
410 }
411 
LoadImage(const vkhpp::UniqueImage & image,uint32_t width,uint32_t height,const std::vector<uint8_t> & imageData,vkhpp::ImageLayout currentLayout,vkhpp::ImageLayout returnedLayout)412 Result<Ok> SampleBase::LoadImage(const vkhpp::UniqueImage& image,
413                                  uint32_t width, uint32_t height,
414                                  const std::vector<uint8_t>& imageData,
415                                  vkhpp::ImageLayout currentLayout,
416                                  vkhpp::ImageLayout returnedLayout) {
417   if (imageData.size() > kStagingBufferSize) {
418     return Err("Failed to load image: staging buffer not large enough.");
419   }
420 
421   auto* mapped = reinterpret_cast<uint8_t*>(VK_TRY_RV(
422       mDevice->mapMemory(*mStagingBuffer.bufferMemory, 0, kStagingBufferSize)));
423 
424   std::memcpy(mapped, imageData.data(), imageData.size());
425 
426   mDevice->unmapMemory(*mStagingBuffer.bufferMemory);
427 
428   return DoCommandsImmediate([&](vkhpp::UniqueCommandBuffer& cmd) {
429     if (currentLayout != vkhpp::ImageLayout::eTransferDstOptimal) {
430       const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
431           vkhpp::ImageMemoryBarrier{
432               .srcAccessMask = vkhpp::AccessFlagBits::eMemoryRead |
433                                vkhpp::AccessFlagBits::eMemoryWrite,
434               .dstAccessMask = vkhpp::AccessFlagBits::eTransferWrite,
435               .oldLayout = currentLayout,
436               .newLayout = vkhpp::ImageLayout::eTransferDstOptimal,
437               .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
438               .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
439               .image = *image,
440               .subresourceRange =
441                   {
442                       .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
443                       .baseMipLevel = 0,
444                       .levelCount = 1,
445                       .baseArrayLayer = 0,
446                       .layerCount = 1,
447                   },
448 
449           },
450       };
451       cmd->pipelineBarrier(
452           /*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
453           /*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
454           /*dependencyFlags=*/{},
455           /*memoryBarriers=*/{},
456           /*bufferMemoryBarriers=*/{},
457           /*imageMemoryBarriers=*/imageMemoryBarriers);
458     }
459 
460     const std::vector<vkhpp::BufferImageCopy> imageCopyRegions = {
461         vkhpp::BufferImageCopy{
462             .bufferOffset = 0,
463             .bufferRowLength = 0,
464             .bufferImageHeight = 0,
465             .imageSubresource =
466                 {
467                     .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
468                     .mipLevel = 0,
469                     .baseArrayLayer = 0,
470                     .layerCount = 1,
471                 },
472             .imageOffset =
473                 {
474                     .x = 0,
475                     .y = 0,
476                     .z = 0,
477                 },
478             .imageExtent =
479                 {
480                     .width = width,
481                     .height = height,
482                     .depth = 1,
483                 },
484         },
485     };
486     cmd->copyBufferToImage(*mStagingBuffer.buffer, *image,
487                            vkhpp::ImageLayout::eTransferDstOptimal,
488                            imageCopyRegions);
489 
490     if (returnedLayout != vkhpp::ImageLayout::eTransferDstOptimal) {
491       const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
492           vkhpp::ImageMemoryBarrier{
493               .srcAccessMask = vkhpp::AccessFlagBits::eTransferWrite,
494               .dstAccessMask = vkhpp::AccessFlagBits::eMemoryRead |
495                                vkhpp::AccessFlagBits::eMemoryWrite,
496               .oldLayout = vkhpp::ImageLayout::eTransferDstOptimal,
497               .newLayout = returnedLayout,
498               .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
499               .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
500               .image = *image,
501               .subresourceRange =
502                   {
503                       .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
504                       .baseMipLevel = 0,
505                       .levelCount = 1,
506                       .baseArrayLayer = 0,
507                       .layerCount = 1,
508                   },
509           },
510       };
511       cmd->pipelineBarrier(
512           /*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
513           /*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
514           /*dependencyFlags=*/{},
515           /*memoryBarriers=*/{},
516           /*bufferMemoryBarriers=*/{},
517           /*imageMemoryBarriers=*/imageMemoryBarriers);
518     }
519     return Ok{};
520   });
521 }
522 
DownloadImage(uint32_t width,uint32_t height,const vkhpp::UniqueImage & image,vkhpp::ImageLayout currentLayout,vkhpp::ImageLayout returnedLayout)523 Result<std::vector<uint8_t>> SampleBase::DownloadImage(
524     uint32_t width, uint32_t height, const vkhpp::UniqueImage& image,
525     vkhpp::ImageLayout currentLayout, vkhpp::ImageLayout returnedLayout) {
526   VK_EXPECT(DoCommandsImmediate([&](vkhpp::UniqueCommandBuffer& cmd) {
527     if (currentLayout != vkhpp::ImageLayout::eTransferSrcOptimal) {
528       const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
529           vkhpp::ImageMemoryBarrier{
530               .srcAccessMask = vkhpp::AccessFlagBits::eMemoryRead |
531                                vkhpp::AccessFlagBits::eMemoryWrite,
532               .dstAccessMask = vkhpp::AccessFlagBits::eTransferRead,
533               .oldLayout = currentLayout,
534               .newLayout = vkhpp::ImageLayout::eTransferSrcOptimal,
535               .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
536               .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
537               .image = *image,
538               .subresourceRange =
539                   {
540                       .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
541                       .baseMipLevel = 0,
542                       .levelCount = 1,
543                       .baseArrayLayer = 0,
544                       .layerCount = 1,
545                   },
546           },
547       };
548       cmd->pipelineBarrier(
549           /*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
550           /*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
551           /*dependencyFlags=*/{},
552           /*memoryBarriers=*/{},
553           /*bufferMemoryBarriers=*/{},
554           /*imageMemoryBarriers=*/imageMemoryBarriers);
555     }
556 
557     const std::vector<vkhpp::BufferImageCopy> regions = {
558         vkhpp::BufferImageCopy{
559             .bufferOffset = 0,
560             .bufferRowLength = 0,
561             .bufferImageHeight = 0,
562             .imageSubresource =
563                 {
564                     .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
565                     .mipLevel = 0,
566                     .baseArrayLayer = 0,
567                     .layerCount = 1,
568                 },
569             .imageOffset =
570                 {
571                     .x = 0,
572                     .y = 0,
573                     .z = 0,
574                 },
575             .imageExtent =
576                 {
577                     .width = width,
578                     .height = height,
579                     .depth = 1,
580                 },
581         },
582     };
583     cmd->copyImageToBuffer(*image, vkhpp::ImageLayout::eTransferSrcOptimal,
584                            *mStagingBuffer.buffer, regions);
585 
586     if (returnedLayout != vkhpp::ImageLayout::eTransferSrcOptimal) {
587       const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
588           vkhpp::ImageMemoryBarrier{
589               .srcAccessMask = vkhpp::AccessFlagBits::eTransferRead,
590               .dstAccessMask = vkhpp::AccessFlagBits::eMemoryRead |
591                                vkhpp::AccessFlagBits::eMemoryWrite,
592               .oldLayout = vkhpp::ImageLayout::eTransferSrcOptimal,
593               .newLayout = returnedLayout,
594               .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
595               .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
596               .image = *image,
597               .subresourceRange =
598                   {
599                       .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
600                       .baseMipLevel = 0,
601                       .levelCount = 1,
602                       .baseArrayLayer = 0,
603                       .layerCount = 1,
604                   },
605           },
606       };
607       cmd->pipelineBarrier(
608           /*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
609           /*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
610           /*dependencyFlags=*/{},
611           /*memoryBarriers=*/{},
612           /*bufferMemoryBarriers=*/{},
613           /*imageMemoryBarriers=*/imageMemoryBarriers);
614     }
615 
616     return Ok{};
617   }));
618 
619   auto* mapped = reinterpret_cast<uint8_t*>(VK_EXPECT_RV(
620       mDevice->mapMemory(*mStagingBuffer.bufferMemory, 0, kStagingBufferSize)));
621 
622   std::vector<uint8_t> outPixels;
623   outPixels.resize(width * height * 4);
624 
625   std::memcpy(outPixels.data(), mapped, outPixels.size());
626 
627   mDevice->unmapMemory(*mStagingBuffer.bufferMemory);
628 
629   return outPixels;
630 }
631 
CreateYuvImage(uint32_t width,uint32_t height,vkhpp::ImageUsageFlags usages,vkhpp::MemoryPropertyFlags memoryProperties,vkhpp::ImageLayout layout)632 Result<SampleBase::YuvImageWithMemory> SampleBase::CreateYuvImage(
633     uint32_t width, uint32_t height, vkhpp::ImageUsageFlags usages,
634     vkhpp::MemoryPropertyFlags memoryProperties, vkhpp::ImageLayout layout) {
635   const vkhpp::SamplerYcbcrConversionCreateInfo conversionCreateInfo = {
636       .format = vkhpp::Format::eG8B8R83Plane420Unorm,
637       .ycbcrModel = vkhpp::SamplerYcbcrModelConversion::eYcbcr601,
638       .ycbcrRange = vkhpp::SamplerYcbcrRange::eItuNarrow,
639       .components =
640           {
641               .r = vkhpp::ComponentSwizzle::eIdentity,
642               .g = vkhpp::ComponentSwizzle::eIdentity,
643               .b = vkhpp::ComponentSwizzle::eIdentity,
644               .a = vkhpp::ComponentSwizzle::eIdentity,
645           },
646       .xChromaOffset = vkhpp::ChromaLocation::eMidpoint,
647       .yChromaOffset = vkhpp::ChromaLocation::eMidpoint,
648       .chromaFilter = vkhpp::Filter::eLinear,
649       .forceExplicitReconstruction = VK_FALSE,
650   };
651   auto imageSamplerConversion = VK_EXPECT_RV(
652       mDevice->createSamplerYcbcrConversionUnique(conversionCreateInfo));
653 
654   const vkhpp::SamplerYcbcrConversionInfo samplerConversionInfo = {
655       .conversion = *imageSamplerConversion,
656   };
657   const vkhpp::SamplerCreateInfo samplerCreateInfo = {
658       .pNext = &samplerConversionInfo,
659       .magFilter = vkhpp::Filter::eLinear,
660       .minFilter = vkhpp::Filter::eLinear,
661       .mipmapMode = vkhpp::SamplerMipmapMode::eNearest,
662       .addressModeU = vkhpp::SamplerAddressMode::eClampToEdge,
663       .addressModeV = vkhpp::SamplerAddressMode::eClampToEdge,
664       .addressModeW = vkhpp::SamplerAddressMode::eClampToEdge,
665       .mipLodBias = 0.0f,
666       .anisotropyEnable = VK_FALSE,
667       .maxAnisotropy = 1.0f,
668       .compareEnable = VK_FALSE,
669       .compareOp = vkhpp::CompareOp::eLessOrEqual,
670       .minLod = 0.0f,
671       .maxLod = 0.25f,
672       .borderColor = vkhpp::BorderColor::eIntTransparentBlack,
673       .unnormalizedCoordinates = VK_FALSE,
674   };
675   auto imageSampler =
676       VK_EXPECT_RV(mDevice->createSamplerUnique(samplerCreateInfo));
677 
678   const vkhpp::ImageCreateInfo imageCreateInfo = {
679       .imageType = vkhpp::ImageType::e2D,
680       .format = vkhpp::Format::eG8B8R83Plane420Unorm,
681       .extent =
682           {
683               .width = width,
684               .height = height,
685               .depth = 1,
686           },
687       .mipLevels = 1,
688       .arrayLayers = 1,
689       .samples = vkhpp::SampleCountFlagBits::e1,
690       .tiling = vkhpp::ImageTiling::eOptimal,
691       .usage = usages,
692       .sharingMode = vkhpp::SharingMode::eExclusive,
693       .initialLayout = vkhpp::ImageLayout::eUndefined,
694   };
695   auto image = VK_EXPECT_RV(mDevice->createImageUnique(imageCreateInfo));
696 
697   const auto memoryRequirements = mDevice->getImageMemoryRequirements(*image);
698 
699   const uint32_t memoryIndex = VK_EXPECT(GetMemoryType(
700       mPhysicalDevice, memoryRequirements.memoryTypeBits, memoryProperties));
701 
702   const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
703       .allocationSize = memoryRequirements.size,
704       .memoryTypeIndex = memoryIndex,
705   };
706   auto imageMemory =
707       VK_EXPECT_RV(mDevice->allocateMemoryUnique(imageMemoryAllocateInfo));
708 
709   mDevice->bindImageMemory(*image, *imageMemory, 0);
710 
711   const vkhpp::ImageViewCreateInfo imageViewCreateInfo = {
712       .pNext = &samplerConversionInfo,
713       .image = *image,
714       .viewType = vkhpp::ImageViewType::e2D,
715       .format = vkhpp::Format::eG8B8R83Plane420Unorm,
716       .components =
717           {
718               .r = vkhpp::ComponentSwizzle::eIdentity,
719               .g = vkhpp::ComponentSwizzle::eIdentity,
720               .b = vkhpp::ComponentSwizzle::eIdentity,
721               .a = vkhpp::ComponentSwizzle::eIdentity,
722           },
723       .subresourceRange =
724           {
725               .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
726               .baseMipLevel = 0,
727               .levelCount = 1,
728               .baseArrayLayer = 0,
729               .layerCount = 1,
730           },
731   };
732   auto imageView =
733       VK_EXPECT_RV(mDevice->createImageViewUnique(imageViewCreateInfo));
734 
735   VK_EXPECT(DoCommandsImmediate([&](vkhpp::UniqueCommandBuffer& cmd) {
736     const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
737         vkhpp::ImageMemoryBarrier{
738             .srcAccessMask = {},
739             .dstAccessMask = vkhpp::AccessFlagBits::eTransferWrite,
740             .oldLayout = vkhpp::ImageLayout::eUndefined,
741             .newLayout = layout,
742             .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
743             .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
744             .image = *image,
745             .subresourceRange =
746                 {
747                     .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
748                     .baseMipLevel = 0,
749                     .levelCount = 1,
750                     .baseArrayLayer = 0,
751                     .layerCount = 1,
752                 },
753 
754         },
755     };
756     cmd->pipelineBarrier(
757         /*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
758         /*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
759         /*dependencyFlags=*/{},
760         /*memoryBarriers=*/{},
761         /*bufferMemoryBarriers=*/{},
762         /*imageMemoryBarriers=*/imageMemoryBarriers);
763     return Ok{};
764   }));
765 
766   return YuvImageWithMemory{
767       .imageSamplerConversion = std::move(imageSamplerConversion),
768       .imageSampler = std::move(imageSampler),
769       .imageMemory = std::move(imageMemory),
770       .image = std::move(image),
771       .imageView = std::move(imageView),
772   };
773 }
774 
LoadYuvImage(const vkhpp::UniqueImage & image,uint32_t width,uint32_t height,const std::vector<uint8_t> & imageDataY,const std::vector<uint8_t> & imageDataU,const std::vector<uint8_t> & imageDataV,vkhpp::ImageLayout currentLayout,vkhpp::ImageLayout returnedLayout)775 Result<Ok> SampleBase::LoadYuvImage(const vkhpp::UniqueImage& image,
776                                     uint32_t width, uint32_t height,
777                                     const std::vector<uint8_t>& imageDataY,
778                                     const std::vector<uint8_t>& imageDataU,
779                                     const std::vector<uint8_t>& imageDataV,
780                                     vkhpp::ImageLayout currentLayout,
781                                     vkhpp::ImageLayout returnedLayout) {
782   auto* mapped = reinterpret_cast<uint8_t*>(VK_TRY_RV(
783       mDevice->mapMemory(*mStagingBuffer.bufferMemory, 0, kStagingBufferSize)));
784 
785   const VkDeviceSize yOffset = 0;
786   const VkDeviceSize uOffset = imageDataY.size();
787   const VkDeviceSize vOffset = imageDataY.size() + imageDataU.size();
788   std::memcpy(mapped + yOffset, imageDataY.data(), imageDataY.size());
789   std::memcpy(mapped + uOffset, imageDataU.data(), imageDataU.size());
790   std::memcpy(mapped + vOffset, imageDataV.data(), imageDataV.size());
791   mDevice->unmapMemory(*mStagingBuffer.bufferMemory);
792 
793   return DoCommandsImmediate([&](vkhpp::UniqueCommandBuffer& cmd) {
794     if (currentLayout != vkhpp::ImageLayout::eTransferDstOptimal) {
795       const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
796           vkhpp::ImageMemoryBarrier{
797               .srcAccessMask = vkhpp::AccessFlagBits::eMemoryRead |
798                                vkhpp::AccessFlagBits::eMemoryWrite,
799               .dstAccessMask = vkhpp::AccessFlagBits::eTransferWrite,
800               .oldLayout = currentLayout,
801               .newLayout = vkhpp::ImageLayout::eTransferDstOptimal,
802               .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
803               .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
804               .image = *image,
805               .subresourceRange =
806                   {
807                       .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
808                       .baseMipLevel = 0,
809                       .levelCount = 1,
810                       .baseArrayLayer = 0,
811                       .layerCount = 1,
812                   },
813 
814           },
815       };
816       cmd->pipelineBarrier(
817           /*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
818           /*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
819           /*dependencyFlags=*/{},
820           /*memoryBarriers=*/{},
821           /*bufferMemoryBarriers=*/{},
822           /*imageMemoryBarriers=*/imageMemoryBarriers);
823     }
824 
825     const std::vector<vkhpp::BufferImageCopy> imageCopyRegions = {
826         vkhpp::BufferImageCopy{
827             .bufferOffset = yOffset,
828             .bufferRowLength = 0,
829             .bufferImageHeight = 0,
830             .imageSubresource =
831                 {
832                     .aspectMask = vkhpp::ImageAspectFlagBits::ePlane0,
833                     .mipLevel = 0,
834                     .baseArrayLayer = 0,
835                     .layerCount = 1,
836                 },
837             .imageOffset =
838                 {
839                     .x = 0,
840                     .y = 0,
841                     .z = 0,
842                 },
843             .imageExtent =
844                 {
845                     .width = width,
846                     .height = height,
847                     .depth = 1,
848                 },
849         },
850         vkhpp::BufferImageCopy{
851             .bufferOffset = uOffset,
852             .bufferRowLength = 0,
853             .bufferImageHeight = 0,
854             .imageSubresource =
855                 {
856                     .aspectMask = vkhpp::ImageAspectFlagBits::ePlane1,
857                     .mipLevel = 0,
858                     .baseArrayLayer = 0,
859                     .layerCount = 1,
860                 },
861             .imageOffset =
862                 {
863                     .x = 0,
864                     .y = 0,
865                     .z = 0,
866                 },
867             .imageExtent =
868                 {
869                     .width = width / 2,
870                     .height = height / 2,
871                     .depth = 1,
872                 },
873         },
874         vkhpp::BufferImageCopy{
875             .bufferOffset = vOffset,
876             .bufferRowLength = 0,
877             .bufferImageHeight = 0,
878             .imageSubresource =
879                 {
880                     .aspectMask = vkhpp::ImageAspectFlagBits::ePlane2,
881                     .mipLevel = 0,
882                     .baseArrayLayer = 0,
883                     .layerCount = 1,
884                 },
885             .imageOffset =
886                 {
887                     .x = 0,
888                     .y = 0,
889                     .z = 0,
890                 },
891             .imageExtent =
892                 {
893                     .width = width / 2,
894                     .height = height / 2,
895                     .depth = 1,
896                 },
897         },
898     };
899     cmd->copyBufferToImage(*mStagingBuffer.buffer, *image,
900                            vkhpp::ImageLayout::eTransferDstOptimal,
901                            imageCopyRegions);
902 
903     if (returnedLayout != vkhpp::ImageLayout::eTransferDstOptimal) {
904       const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
905           vkhpp::ImageMemoryBarrier{
906               .srcAccessMask = vkhpp::AccessFlagBits::eTransferWrite,
907               .dstAccessMask = vkhpp::AccessFlagBits::eMemoryRead |
908                                vkhpp::AccessFlagBits::eMemoryWrite,
909               .oldLayout = vkhpp::ImageLayout::eTransferDstOptimal,
910               .newLayout = returnedLayout,
911               .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
912               .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
913               .image = *image,
914               .subresourceRange =
915                   {
916                       .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
917                       .baseMipLevel = 0,
918                       .levelCount = 1,
919                       .baseArrayLayer = 0,
920                       .layerCount = 1,
921                   },
922           },
923       };
924       cmd->pipelineBarrier(
925           /*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
926           /*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
927           /*dependencyFlags=*/{},
928           /*memoryBarriers=*/{},
929           /*bufferMemoryBarriers=*/{},
930           /*imageMemoryBarriers=*/imageMemoryBarriers);
931     }
932     return Ok{};
933   });
934 }
935 
CreateFramebuffer(uint32_t width,uint32_t height,vkhpp::Format color_format,vkhpp::Format depth_format)936 Result<SampleBase::FramebufferWithAttachments> SampleBase::CreateFramebuffer(
937     uint32_t width, uint32_t height, vkhpp::Format color_format,
938     vkhpp::Format depth_format) {
939   std::optional<SampleBase::ImageWithMemory> colorAttachment;
940   if (color_format != vkhpp::Format::eUndefined) {
941     colorAttachment =
942         VK_EXPECT(CreateImage(width, height, color_format,
943                               vkhpp::ImageUsageFlagBits::eColorAttachment |
944                                   vkhpp::ImageUsageFlagBits::eTransferSrc,
945                               vkhpp::MemoryPropertyFlagBits::eDeviceLocal,
946                               vkhpp::ImageLayout::eColorAttachmentOptimal));
947   }
948 
949   std::optional<SampleBase::ImageWithMemory> depthAttachment;
950   if (depth_format != vkhpp::Format::eUndefined) {
951     depthAttachment = VK_EXPECT(
952         CreateImage(width, height, depth_format,
953                     vkhpp::ImageUsageFlagBits::eDepthStencilAttachment |
954                         vkhpp::ImageUsageFlagBits::eTransferSrc,
955                     vkhpp::MemoryPropertyFlagBits::eDeviceLocal,
956                     vkhpp::ImageLayout::eDepthStencilAttachmentOptimal));
957   }
958 
959   std::vector<vkhpp::AttachmentDescription> attachments;
960 
961   std::optional<vkhpp::AttachmentReference> colorAttachment_reference;
962   if (color_format != vkhpp::Format::eUndefined) {
963     attachments.push_back(vkhpp::AttachmentDescription{
964         .format = color_format,
965         .samples = vkhpp::SampleCountFlagBits::e1,
966         .loadOp = vkhpp::AttachmentLoadOp::eClear,
967         .storeOp = vkhpp::AttachmentStoreOp::eStore,
968         .stencilLoadOp = vkhpp::AttachmentLoadOp::eClear,
969         .stencilStoreOp = vkhpp::AttachmentStoreOp::eStore,
970         .initialLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
971         .finalLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
972     });
973 
974     colorAttachment_reference = vkhpp::AttachmentReference{
975         .attachment = static_cast<uint32_t>(attachments.size() - 1),
976         .layout = vkhpp::ImageLayout::eColorAttachmentOptimal,
977     };
978   }
979 
980   std::optional<vkhpp::AttachmentReference> depthAttachment_reference;
981   if (depth_format != vkhpp::Format::eUndefined) {
982     attachments.push_back(vkhpp::AttachmentDescription{
983         .format = depth_format,
984         .samples = vkhpp::SampleCountFlagBits::e1,
985         .loadOp = vkhpp::AttachmentLoadOp::eClear,
986         .storeOp = vkhpp::AttachmentStoreOp::eStore,
987         .stencilLoadOp = vkhpp::AttachmentLoadOp::eClear,
988         .stencilStoreOp = vkhpp::AttachmentStoreOp::eStore,
989         .initialLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
990         .finalLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
991     });
992 
993     depthAttachment_reference = vkhpp::AttachmentReference{
994         .attachment = static_cast<uint32_t>(attachments.size() - 1),
995         .layout = vkhpp::ImageLayout::eDepthStencilAttachmentOptimal,
996     };
997   }
998 
999   vkhpp::SubpassDependency dependency = {
1000       .srcSubpass = 0,
1001       .dstSubpass = 0,
1002       .srcStageMask = {},
1003       .dstStageMask = vkhpp::PipelineStageFlagBits::eFragmentShader,
1004       .srcAccessMask = {},
1005       .dstAccessMask = vkhpp::AccessFlagBits::eInputAttachmentRead,
1006       .dependencyFlags = vkhpp::DependencyFlagBits::eByRegion,
1007   };
1008   if (color_format != vkhpp::Format::eUndefined) {
1009     dependency.srcStageMask |=
1010         vkhpp::PipelineStageFlagBits::eColorAttachmentOutput;
1011     dependency.dstStageMask |=
1012         vkhpp::PipelineStageFlagBits::eColorAttachmentOutput;
1013     dependency.srcAccessMask |= vkhpp::AccessFlagBits::eColorAttachmentWrite;
1014   }
1015   if (depth_format != vkhpp::Format::eUndefined) {
1016     dependency.srcStageMask |=
1017         vkhpp::PipelineStageFlagBits::eColorAttachmentOutput;
1018     dependency.dstStageMask |=
1019         vkhpp::PipelineStageFlagBits::eColorAttachmentOutput;
1020     dependency.srcAccessMask |= vkhpp::AccessFlagBits::eColorAttachmentWrite;
1021   }
1022 
1023   vkhpp::SubpassDescription subpass = {
1024       .pipelineBindPoint = vkhpp::PipelineBindPoint::eGraphics,
1025       .inputAttachmentCount = 0,
1026       .pInputAttachments = nullptr,
1027       .colorAttachmentCount = 0,
1028       .pColorAttachments = nullptr,
1029       .pResolveAttachments = nullptr,
1030       .pDepthStencilAttachment = nullptr,
1031       .pPreserveAttachments = nullptr,
1032   };
1033   if (color_format != vkhpp::Format::eUndefined) {
1034     subpass.colorAttachmentCount = 1;
1035     subpass.pColorAttachments = &*colorAttachment_reference;
1036   }
1037   if (depth_format != vkhpp::Format::eUndefined) {
1038     subpass.pDepthStencilAttachment = &*depthAttachment_reference;
1039   }
1040 
1041   const vkhpp::RenderPassCreateInfo renderpassCreateInfo = {
1042       .attachmentCount = static_cast<uint32_t>(attachments.size()),
1043       .pAttachments = attachments.data(),
1044       .subpassCount = 1,
1045       .pSubpasses = &subpass,
1046       .dependencyCount = 1,
1047       .pDependencies = &dependency,
1048   };
1049   auto renderpass =
1050       VK_EXPECT_RV(mDevice->createRenderPassUnique(renderpassCreateInfo));
1051 
1052   std::vector<vkhpp::ImageView> framebufferAttachments;
1053   if (colorAttachment) {
1054     framebufferAttachments.push_back(*colorAttachment->imageView);
1055   }
1056   if (depthAttachment) {
1057     framebufferAttachments.push_back(*depthAttachment->imageView);
1058   }
1059   const vkhpp::FramebufferCreateInfo framebufferCreateInfo = {
1060       .renderPass = *renderpass,
1061       .attachmentCount = static_cast<uint32_t>(framebufferAttachments.size()),
1062       .pAttachments = framebufferAttachments.data(),
1063       .width = width,
1064       .height = height,
1065       .layers = 1,
1066   };
1067   auto framebuffer =
1068       VK_EXPECT_RV(mDevice->createFramebufferUnique(framebufferCreateInfo));
1069 
1070   return SampleBase::FramebufferWithAttachments{
1071       .colorAttachment = std::move(colorAttachment),
1072       .depthAttachment = std::move(depthAttachment),
1073       .renderpass = std::move(renderpass),
1074       .framebuffer = std::move(framebuffer),
1075   };
1076 }
1077 
DoCommandsImmediate(const std::function<Result<Ok> (vkhpp::UniqueCommandBuffer &)> & func,const std::vector<vkhpp::UniqueSemaphore> & semaphores_wait,const std::vector<vkhpp::UniqueSemaphore> & semaphores_signal)1078 Result<Ok> SampleBase::DoCommandsImmediate(
1079     const std::function<Result<Ok>(vkhpp::UniqueCommandBuffer&)>& func,
1080     const std::vector<vkhpp::UniqueSemaphore>& semaphores_wait,
1081     const std::vector<vkhpp::UniqueSemaphore>& semaphores_signal) {
1082   const vkhpp::CommandPoolCreateInfo commandPoolCreateInfo = {
1083       .queueFamilyIndex = mQueueFamilyIndex,
1084   };
1085   auto commandPool =
1086       VK_EXPECT_RV(mDevice->createCommandPoolUnique(commandPoolCreateInfo));
1087   const vkhpp::CommandBufferAllocateInfo commandBufferAllocateInfo = {
1088       .commandPool = *commandPool,
1089       .level = vkhpp::CommandBufferLevel::ePrimary,
1090       .commandBufferCount = 1,
1091   };
1092   auto commandBuffers = VK_TRY_RV(
1093       mDevice->allocateCommandBuffersUnique(commandBufferAllocateInfo));
1094   auto commandBuffer = std::move(commandBuffers[0]);
1095 
1096   const vkhpp::CommandBufferBeginInfo commandBufferBeginInfo = {
1097       .flags = vkhpp::CommandBufferUsageFlagBits::eOneTimeSubmit,
1098   };
1099   commandBuffer->begin(commandBufferBeginInfo);
1100   VK_EXPECT(func(commandBuffer));
1101   commandBuffer->end();
1102 
1103   std::vector<vkhpp::CommandBuffer> commandBufferHandles;
1104   commandBufferHandles.push_back(*commandBuffer);
1105 
1106   std::vector<vkhpp::Semaphore> semaphoreHandlesWait;
1107   semaphoreHandlesWait.reserve(semaphores_wait.size());
1108   for (const auto& s : semaphores_wait) {
1109     semaphoreHandlesWait.emplace_back(*s);
1110   }
1111 
1112   std::vector<vkhpp::Semaphore> semaphoreHandlesSignal;
1113   semaphoreHandlesSignal.reserve(semaphores_signal.size());
1114   for (const auto& s : semaphores_signal) {
1115     semaphoreHandlesSignal.emplace_back(*s);
1116   }
1117 
1118   vkhpp::SubmitInfo submitInfo = {
1119       .commandBufferCount = static_cast<uint32_t>(commandBufferHandles.size()),
1120       .pCommandBuffers = commandBufferHandles.data(),
1121   };
1122   if (!semaphoreHandlesWait.empty()) {
1123     submitInfo.waitSemaphoreCount =
1124         static_cast<uint32_t>(semaphoreHandlesWait.size());
1125     submitInfo.pWaitSemaphores = semaphoreHandlesWait.data();
1126   }
1127   if (!semaphoreHandlesSignal.empty()) {
1128     submitInfo.signalSemaphoreCount =
1129         static_cast<uint32_t>(semaphoreHandlesSignal.size());
1130     submitInfo.pSignalSemaphores = semaphoreHandlesSignal.data();
1131   }
1132   mQueue.submit(submitInfo);
1133   mQueue.waitIdle();
1134 
1135   return Ok{};
1136 }
1137 
SetWindow(ANativeWindow * window)1138 Result<Ok> SampleBase::SetWindow(ANativeWindow* window) {
1139   mDevice->waitIdle();
1140 
1141   VK_EXPECT(DestroySwapchain());
1142   VK_EXPECT(DestroySurface());
1143 
1144   mWindow = window;
1145 
1146   if (mWindow != nullptr) {
1147     VK_EXPECT(CreateSurface());
1148     VK_EXPECT(CreateSwapchain());
1149   }
1150 
1151   return Ok{};
1152 }
1153 
RecreateSwapchain()1154 Result<Ok> SampleBase::RecreateSwapchain() {
1155   mDevice->waitIdle();
1156 
1157   VK_EXPECT(DestroySwapchain());
1158   VK_EXPECT(CreateSwapchain());
1159   return Ok{};
1160 }
1161 
CreateSurface()1162 Result<Ok> SampleBase::CreateSurface() {
1163   if (mWindow == nullptr) {
1164     return Err("Failed to create VkSurface: no window!");
1165   }
1166 
1167   const vkhpp::AndroidSurfaceCreateInfoKHR surfaceCreateInfo = {
1168       .window = mWindow,
1169   };
1170   mSurface =
1171       VK_EXPECT_RV(mInstance->createAndroidSurfaceKHR(surfaceCreateInfo));
1172 
1173   return Ok{};
1174 }
1175 
DestroySurface()1176 Result<Ok> SampleBase::DestroySurface() {
1177   mSurface.reset();
1178   return Ok{};
1179 }
1180 
CreateSwapchain()1181 Result<Ok> SampleBase::CreateSwapchain() {
1182   if (!mSurface) {
1183     return Err("Failed to CreateSwapchain(): missing VkSurface?");
1184   }
1185 
1186   const auto capabilities =
1187       VK_EXPECT_RV(mPhysicalDevice.getSurfaceCapabilitiesKHR(*mSurface));
1188   const vkhpp::Extent2D swapchainExtent = capabilities.currentExtent;
1189 
1190   const auto formats =
1191       VK_EXPECT_RV(mPhysicalDevice.getSurfaceFormatsKHR(*mSurface));
1192   ALOGI("Supported surface formats:");
1193   for (const auto& format : formats) {
1194     const std::string formatStr = vkhpp::to_string(format.format);
1195     const std::string colorspaceStr = vkhpp::to_string(format.colorSpace);
1196     ALOGI(" - format:%s colorspace:%s", formatStr.c_str(),
1197           colorspaceStr.c_str());
1198   }
1199   // Always supported by Android:
1200   const vkhpp::SurfaceFormatKHR swapchainFormat = vkhpp::SurfaceFormatKHR{
1201       .format = vkhpp::Format::eR8G8B8A8Unorm,
1202       .colorSpace = vkhpp::ColorSpaceKHR::eSrgbNonlinear,
1203   };
1204 
1205   const auto modes =
1206       VK_EXPECT_RV(mPhysicalDevice.getSurfacePresentModesKHR(*mSurface));
1207   ALOGI("Supported surface present modes:");
1208   for (const auto& mode : modes) {
1209     const std::string modeStr = vkhpp::to_string(mode);
1210     ALOGI(" - %s", modeStr.c_str());
1211   }
1212 
1213   uint32_t imageCount = capabilities.minImageCount + 1;
1214   if (capabilities.maxImageCount > 0 &&
1215       imageCount > capabilities.maxImageCount) {
1216     imageCount = capabilities.maxImageCount;
1217   }
1218 
1219   const vkhpp::SwapchainCreateInfoKHR swapchainCreateInfo = {
1220       .surface = *mSurface,
1221       .minImageCount = imageCount,
1222       .imageFormat = swapchainFormat.format,
1223       .imageColorSpace = swapchainFormat.colorSpace,
1224       .imageExtent = swapchainExtent,
1225       .imageArrayLayers = 1,
1226       .imageUsage = vkhpp::ImageUsageFlagBits::eColorAttachment,
1227       .imageSharingMode = vkhpp::SharingMode::eExclusive,
1228       .queueFamilyIndexCount = 0,
1229       .pQueueFamilyIndices = nullptr,
1230       .preTransform = capabilities.currentTransform,
1231       .compositeAlpha = vkhpp::CompositeAlphaFlagBitsKHR::eInherit,
1232       .presentMode = vkhpp::PresentModeKHR::eFifo,
1233       .clipped = VK_TRUE,
1234   };
1235   auto swapchain =
1236       VK_EXPECT_RV(mDevice->createSwapchainKHRUnique(swapchainCreateInfo));
1237 
1238   auto swapchainImages =
1239       VK_EXPECT_RV(mDevice->getSwapchainImagesKHR(*swapchain));
1240 
1241   std::vector<vkhpp::UniqueImageView> swapchainImageViews;  // Owning
1242   std::vector<vkhpp::ImageView> swapchainImageViewHandles;  // Unowning
1243   for (const auto& image : swapchainImages) {
1244     const vkhpp::ImageViewCreateInfo imageViewCreateInfo = {
1245         .image = image,
1246         .viewType = vkhpp::ImageViewType::e2D,
1247         .format = swapchainFormat.format,
1248         .components =
1249             {
1250                 .r = vkhpp::ComponentSwizzle::eIdentity,
1251                 .g = vkhpp::ComponentSwizzle::eIdentity,
1252                 .b = vkhpp::ComponentSwizzle::eIdentity,
1253                 .a = vkhpp::ComponentSwizzle::eIdentity,
1254             },
1255         .subresourceRange =
1256             {
1257                 .aspectMask = vkhpp::ImageAspectFlagBits::eColor,
1258                 .baseMipLevel = 0,
1259                 .levelCount = 1,
1260                 .baseArrayLayer = 0,
1261                 .layerCount = 1,
1262             },
1263     };
1264     auto imageView =
1265         VK_EXPECT_RV(mDevice->createImageViewUnique(imageViewCreateInfo));
1266     swapchainImageViewHandles.push_back(*imageView);
1267     swapchainImageViews.push_back(std::move(imageView));
1268   }
1269 
1270   mSwapchainObjects = SwapchainObjects{
1271       .swapchainFormat = swapchainFormat,
1272       .swapchainExtent = swapchainExtent,
1273       .swapchain = std::move(swapchain),
1274       .swapchainImages = std::move(swapchainImages),
1275       .swapchainImageViews = std::move(swapchainImageViews),
1276   };
1277 
1278   const SwapchainInfo swapchainInfo = {
1279       .swapchainFormat = swapchainFormat.format,
1280       .swapchainExtent = swapchainExtent,
1281       .swapchainImageViews = swapchainImageViewHandles,
1282   };
1283   VK_EXPECT(CreateSwapchainDependents(swapchainInfo));
1284 
1285   return Ok{};
1286 }
1287 
DestroySwapchain()1288 Result<Ok> SampleBase::DestroySwapchain() {
1289   VK_EXPECT(DestroySwapchainDependents());
1290 
1291   mSwapchainObjects.reset();
1292 
1293   return Ok{};
1294 }
1295 
Render()1296 Result<Ok> SampleBase::Render() {
1297   if (!mSwapchainObjects) {
1298     return Ok{};
1299   }
1300 
1301   mCurrentFrame = (mCurrentFrame + 1) % mFrameObjects.size();
1302   PerFrameObjects& perFrame = mFrameObjects[mCurrentFrame];
1303 
1304   VK_EXPECT_RESULT(
1305       mDevice->waitForFences({*perFrame.readyFence}, VK_TRUE, UINT64_MAX));
1306   VK_EXPECT_RESULT(mDevice->resetFences({*perFrame.readyFence}));
1307 
1308   const vkhpp::SwapchainKHR swapchain = *mSwapchainObjects->swapchain;
1309 
1310   uint32_t swapchainImageIndex = -1;
1311   vkhpp::Result result = mDevice->acquireNextImageKHR(swapchain, UINT64_MAX,
1312                                                       *perFrame.readyForRender,
1313                                                       {}, &swapchainImageIndex);
1314   if (result == vkhpp::Result::eErrorOutOfDateKHR) {
1315     return RecreateSwapchain();
1316   } else if (result != vkhpp::Result::eSuccess &&
1317              result != vkhpp::Result::eSuboptimalKHR) {
1318     return Err("Failed to acquire next image: " + vkhpp::to_string(result));
1319   }
1320 
1321   VK_EXPECT_RESULT(perFrame.commandBuffer->reset());
1322   const vkhpp::CommandBufferBeginInfo commandBufferBeginInfo = {
1323       .flags = vkhpp::CommandBufferUsageFlagBits::eOneTimeSubmit,
1324   };
1325   VK_EXPECT_RESULT(perFrame.commandBuffer->begin(commandBufferBeginInfo));
1326   const FrameInfo frameInfo = {
1327       .swapchainImageIndex = swapchainImageIndex,
1328       .commandBuffer = *perFrame.commandBuffer,
1329   };
1330   VK_EXPECT(RecordFrame(frameInfo));
1331   VK_EXPECT_RESULT(perFrame.commandBuffer->end());
1332 
1333   const std::vector<vkhpp::CommandBuffer> commandBufferHandles = {
1334       *perFrame.commandBuffer,
1335   };
1336   const std::vector<vkhpp::Semaphore> renderWaitSemaphores = {
1337       *perFrame.readyForRender,
1338   };
1339   const std::vector<vkhpp::PipelineStageFlags> renderWaitStages = {
1340       vkhpp::PipelineStageFlagBits::eBottomOfPipe,
1341   };
1342   const std::vector<vkhpp::Semaphore> renderSignalSemaphores = {
1343       *perFrame.readyForPresent,
1344   };
1345   const vkhpp::SubmitInfo submitInfo = {
1346       .commandBufferCount = static_cast<uint32_t>(commandBufferHandles.size()),
1347       .pCommandBuffers = commandBufferHandles.data(),
1348       .waitSemaphoreCount = static_cast<uint32_t>(renderWaitSemaphores.size()),
1349       .pWaitSemaphores = renderWaitSemaphores.data(),
1350       .pWaitDstStageMask = renderWaitStages.data(),
1351       .signalSemaphoreCount =
1352           static_cast<uint32_t>(renderSignalSemaphores.size()),
1353       .pSignalSemaphores = renderSignalSemaphores.data(),
1354   };
1355   mQueue.submit(submitInfo, *perFrame.readyFence);
1356 
1357   const std::vector<vkhpp::Semaphore> presentReadySemaphores = {
1358       *perFrame.readyForPresent};
1359   const vkhpp::PresentInfoKHR presentInfo = {
1360       .waitSemaphoreCount =
1361           static_cast<uint32_t>(presentReadySemaphores.size()),
1362       .pWaitSemaphores = presentReadySemaphores.data(),
1363       .swapchainCount = 1,
1364       .pSwapchains = &swapchain,
1365       .pImageIndices = &swapchainImageIndex,
1366   };
1367   result = mQueue.presentKHR(presentInfo);
1368   if (result == vkhpp::Result::eErrorOutOfDateKHR ||
1369       result == vkhpp::Result::eSuboptimalKHR) {
1370     VK_EXPECT(RecreateSwapchain());
1371   } else if (result != vkhpp::Result::eSuccess) {
1372     return Err("Failed to present image: " + vkhpp::to_string(result));
1373   }
1374 
1375   return Ok{};
1376 }
1377 
1378 }  // namespace cuttlefish
1379