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