Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
vulkan_swapchain.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "vulkan_swapchain.h"
6
7#include "flutter/vulkan/procs/vulkan_proc_table.h"
8
16
17#include "vulkan_backbuffer.h"
18#include "vulkan_device.h"
19#include "vulkan_image.h"
20#include "vulkan_surface.h"
21
22namespace vulkan {
23
24namespace {
25struct FormatInfo {
29};
30} // namespace
31
44
46 const VulkanDevice& device,
48 GrDirectContext* skia_context,
49 std::unique_ptr<VulkanSwapchain> old_swapchain,
50 uint32_t queue_family_index)
51 : vk(p_vk),
52 device_(device),
53 capabilities_(),
54 surface_format_(),
55 current_pipeline_stage_(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
56 current_backbuffer_index_(0),
57 current_image_index_(0),
58 valid_(false) {
59 if (!device_.IsValid() || !surface.IsValid() || skia_context == nullptr) {
60 FML_DLOG(INFO) << "Device or surface is invalid.";
61 return;
62 }
63
64 if (!device_.GetSurfaceCapabilities(surface, &capabilities_)) {
65 FML_DLOG(INFO) << "Could not find surface capabilities.";
66 return;
67 }
68
69 const auto format_infos = DesiredFormatInfos();
70 std::vector<VkFormat> desired_formats(format_infos.size());
71 for (size_t i = 0; i < format_infos.size(); ++i) {
72 if (skia_context->colorTypeSupportedAsSurface(
73 format_infos[i].color_type_)) {
74 desired_formats[i] = format_infos[i].format_;
75 } else {
76 desired_formats[i] = VK_FORMAT_UNDEFINED;
77 }
78 }
79
80 int format_index =
81 device_.ChooseSurfaceFormat(surface, desired_formats, &surface_format_);
82 if (format_index < 0) {
83 FML_DLOG(INFO) << "Could not choose surface format.";
84 return;
85 }
86
88 if (!device_.ChoosePresentMode(surface, &present_mode)) {
89 FML_DLOG(INFO) << "Could not choose present mode.";
90 return;
91 }
92
93 // Check if the surface can present.
94
95 VkBool32 supported = VK_FALSE;
96 if (VK_CALL_LOG_ERROR(vk.GetPhysicalDeviceSurfaceSupportKHR(
97 device_.GetPhysicalDeviceHandle(), // physical device
98 queue_family_index, // queue family
99 surface.Handle(), // surface to test
100 &supported)) != VK_SUCCESS) {
101 FML_DLOG(INFO) << "Could not get physical device surface support.";
102 return;
103 }
104
105 if (supported != VK_TRUE) {
106 FML_DLOG(INFO) << "Surface was not supported by the physical device.";
107 return;
108 }
109
110 // Construct the Swapchain
111
112 VkSwapchainKHR old_swapchain_handle = VK_NULL_HANDLE;
113
114 if (old_swapchain != nullptr && old_swapchain->IsValid()) {
115 old_swapchain_handle = old_swapchain->swapchain_;
116 // The unique pointer to the swapchain will go out of scope here
117 // and its handle collected after the appropriate device wait.
118 }
119
120 VkSurfaceKHR surface_handle = surface.Handle();
121
125
126 const VkSwapchainCreateInfoKHR create_info = {
128 .pNext = nullptr,
129 .flags = 0,
130 .surface = surface_handle,
131 .minImageCount = capabilities_.minImageCount,
132 .imageFormat = surface_format_.format,
133 .imageColorSpace = surface_format_.colorSpace,
134 .imageExtent = capabilities_.currentExtent,
135 .imageArrayLayers = 1,
136 .imageUsage = usage_flags,
137 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
138 .queueFamilyIndexCount = 0, // Because of the exclusive sharing mode.
139 .pQueueFamilyIndices = nullptr,
141 .compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
142 .presentMode = present_mode,
143 .clipped = VK_FALSE,
144 .oldSwapchain = old_swapchain_handle,
145 };
146
147 VkSwapchainKHR swapchain = VK_NULL_HANDLE;
148
149 if (VK_CALL_LOG_ERROR(vk.CreateSwapchainKHR(device_.GetHandle(), &create_info,
150 nullptr, &swapchain)) !=
151 VK_SUCCESS) {
152 FML_DLOG(INFO) << "Could not create the swapchain.";
153 return;
154 }
155
156 swapchain_ = VulkanHandle<VkSwapchainKHR>{
157 swapchain, [this](VkSwapchainKHR swapchain) {
158 FML_ALLOW_UNUSED_LOCAL(device_.WaitIdle());
159 vk.DestroySwapchainKHR(device_.GetHandle(), swapchain, nullptr);
160 }};
161
162 if (!CreateSwapchainImages(
163 skia_context, format_infos[format_index].color_type_,
164 format_infos[format_index].color_space_, usage_flags)) {
165 FML_DLOG(INFO) << "Could not create swapchain images.";
166 return;
167 }
168
169 valid_ = true;
170}
171
173
175 return valid_;
176}
177
178std::vector<VkImage> VulkanSwapchain::GetImages() const {
179 uint32_t count = 0;
180 if (VK_CALL_LOG_ERROR(vk.GetSwapchainImagesKHR(
181 device_.GetHandle(), swapchain_, &count, nullptr)) != VK_SUCCESS) {
182 return {};
183 }
184
185 if (count == 0) {
186 return {};
187 }
188
189 std::vector<VkImage> images;
190
191 images.resize(count);
192
193 if (VK_CALL_LOG_ERROR(vk.GetSwapchainImagesKHR(
194 device_.GetHandle(), swapchain_, &count, images.data())) !=
195 VK_SUCCESS) {
196 return {};
197 }
198
199 return images;
200}
201
203 VkExtent2D extents = capabilities_.currentExtent;
204
205 if (extents.width < capabilities_.minImageExtent.width) {
206 extents.width = capabilities_.minImageExtent.width;
207 } else if (extents.width > capabilities_.maxImageExtent.width) {
208 extents.width = capabilities_.maxImageExtent.width;
209 }
210
211 if (extents.height < capabilities_.minImageExtent.height) {
212 extents.height = capabilities_.minImageExtent.height;
213 } else if (extents.height > capabilities_.maxImageExtent.height) {
214 extents.height = capabilities_.maxImageExtent.height;
215 }
216
217 return SkISize::Make(extents.width, extents.height);
218}
219
220sk_sp<SkSurface> VulkanSwapchain::CreateSkiaSurface(
221 GrDirectContext* gr_context,
222 VkImage image,
223 VkImageUsageFlags usage_flags,
224 const SkISize& size,
226 sk_sp<SkColorSpace> color_space) const {
227 if (gr_context == nullptr) {
228 return nullptr;
229 }
230
232 // Unexpected Vulkan format.
233 return nullptr;
234 }
235
236 GrVkImageInfo image_info;
237 image_info.fImage = image;
240 image_info.fFormat = surface_format_.format;
241 image_info.fImageUsageFlags = usage_flags;
242 image_info.fSampleCount = 1;
243 image_info.fLevelCount = 1;
244
245 // TODO(chinmaygarde): Setup the stencil buffer and the sampleCnt.
246 auto backend_render_target =
247 GrBackendRenderTargets::MakeVk(size.fWidth, size.fHeight, image_info);
249
251 gr_context, // context
252 backend_render_target, // backend render target
253 kTopLeft_GrSurfaceOrigin, // origin
254 color_type, // color type
255 std::move(color_space), // color space
256 &props // surface properties
257 );
258}
259
260bool VulkanSwapchain::CreateSwapchainImages(
261 GrDirectContext* skia_context,
263 const sk_sp<SkColorSpace>& color_space,
264 VkImageUsageFlags usage_flags) {
265 std::vector<VkImage> images = GetImages();
266
267 if (images.empty()) {
268 return false;
269 }
270
271 const SkISize surface_size = GetSize();
272
273 for (const VkImage& image : images) {
274 // Populate the backbuffer.
275 auto backbuffer = std::make_unique<VulkanBackbuffer>(
276 vk, device_.GetHandle(), device_.GetCommandPool());
277
278 if (!backbuffer->IsValid()) {
279 return false;
280 }
281
282 backbuffers_.emplace_back(std::move(backbuffer));
283
284 // Populate the image.
285 VulkanHandle<VkImage> image_handle = VulkanHandle<VkImage>{
286 image, [this](VkImage image) {
287 vk.DestroyImage(device_.GetHandle(), image, nullptr);
288 }};
289 auto vulkan_image = std::make_unique<VulkanImage>(std::move(image_handle));
290
291 if (!vulkan_image->IsValid()) {
292 return false;
293 }
294
295 images_.emplace_back(std::move(vulkan_image));
296
297 // Populate the surface.
298 auto surface = CreateSkiaSurface(skia_context, image, usage_flags,
299 surface_size, color_type, color_space);
300
301 if (surface == nullptr) {
302 return false;
303 }
304
305 surfaces_.emplace_back(std::move(surface));
306 }
307
308 FML_DCHECK(backbuffers_.size() == images_.size());
309 FML_DCHECK(images_.size() == surfaces_.size());
310
311 return true;
312}
313
314VulkanBackbuffer* VulkanSwapchain::GetNextBackbuffer() {
315 auto available_backbuffers = backbuffers_.size();
316
317 if (available_backbuffers == 0) {
318 return nullptr;
319 }
320
321 auto next_backbuffer_index =
322 (current_backbuffer_index_ + 1) % backbuffers_.size();
323
324 auto& backbuffer = backbuffers_[next_backbuffer_index];
325
326 if (!backbuffer->IsValid()) {
327 return nullptr;
328 }
329
330 current_backbuffer_index_ = next_backbuffer_index;
331 return backbuffer.get();
332}
333
336
337 if (!IsValid()) {
338 FML_DLOG(INFO) << "Swapchain was invalid.";
339 return error;
340 }
341
342 // ---------------------------------------------------------------------------
343 // Step 0:
344 // Acquire the next available backbuffer.
345 // ---------------------------------------------------------------------------
346 auto backbuffer = GetNextBackbuffer();
347
348 if (backbuffer == nullptr) {
349 FML_DLOG(INFO) << "Could not get the next backbuffer.";
350 return error;
351 }
352
353 // ---------------------------------------------------------------------------
354 // Step 1:
355 // Wait for use readiness.
356 // ---------------------------------------------------------------------------
357 if (!backbuffer->WaitFences()) {
358 FML_DLOG(INFO) << "Failed waiting on fences.";
359 return error;
360 }
361
362 // ---------------------------------------------------------------------------
363 // Step 2:
364 // Put fences in an unsignaled state.
365 // ---------------------------------------------------------------------------
366 if (!backbuffer->ResetFences()) {
367 FML_DLOG(INFO) << "Could not reset fences.";
368 return error;
369 }
370
371 // ---------------------------------------------------------------------------
372 // Step 3:
373 // Acquire the next image index.
374 // ---------------------------------------------------------------------------
375 uint32_t next_image_index = 0;
376
377 VkResult acquire_result = VK_CALL_LOG_ERROR(
378 vk.AcquireNextImageKHR(device_.GetHandle(), //
379 swapchain_, //
380 std::numeric_limits<uint64_t>::max(), //
381 backbuffer->GetUsageSemaphore(), //
383 &next_image_index));
384
385 switch (acquire_result) {
386 case VK_SUCCESS:
387 break;
389 return {AcquireStatus::ErrorSurfaceOutOfDate, nullptr};
391 return {AcquireStatus::ErrorSurfaceLost, nullptr};
392 default:
393 FML_LOG(INFO) << "Unexpected result from AcquireNextImageKHR: "
394 << acquire_result;
395 return {AcquireStatus::ErrorSurfaceLost, nullptr};
396 }
397
398 // Simple sanity checking of image index.
399 if (next_image_index >= images_.size()) {
400 FML_DLOG(INFO) << "Image index returned was out-of-bounds.";
401 return error;
402 }
403
404 auto& image = images_[next_image_index];
405 if (!image->IsValid()) {
406 FML_DLOG(INFO) << "Image at index was invalid.";
407 return error;
408 }
409
410 // ---------------------------------------------------------------------------
411 // Step 4:
412 // Start recording to the command buffer.
413 // ---------------------------------------------------------------------------
414 if (!backbuffer->GetUsageCommandBuffer().Begin()) {
415 FML_DLOG(INFO) << "Could not begin recording to the command buffer.";
416 return error;
417 }
418
419 // ---------------------------------------------------------------------------
420 // Step 5:
421 // Set image layout to color attachment mode.
422 // ---------------------------------------------------------------------------
423 VkPipelineStageFlagBits destination_pipeline_stage =
425 VkImageLayout destination_image_layout =
427
428 if (!image->InsertImageMemoryBarrier(
429 backbuffer->GetUsageCommandBuffer(), // command buffer
430 current_pipeline_stage_, // src_pipeline_bits
431 destination_pipeline_stage, // dest_pipeline_bits
432 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // dest_access_flags
433 destination_image_layout // dest_layout
434 )) {
435 FML_DLOG(INFO) << "Could not insert image memory barrier.";
436 return error;
437 } else {
438 current_pipeline_stage_ = destination_pipeline_stage;
439 }
440
441 // ---------------------------------------------------------------------------
442 // Step 6:
443 // End recording to the command buffer.
444 // ---------------------------------------------------------------------------
445 if (!backbuffer->GetUsageCommandBuffer().End()) {
446 FML_DLOG(INFO) << "Could not end recording to the command buffer.";
447 return error;
448 }
449
450 // ---------------------------------------------------------------------------
451 // Step 7:
452 // Submit the command buffer to the device queue.
453 // ---------------------------------------------------------------------------
454 std::vector<VkSemaphore> wait_semaphores = {backbuffer->GetUsageSemaphore()};
455 std::vector<VkSemaphore> signal_semaphores = {};
456 std::vector<VkCommandBuffer> command_buffers = {
457 backbuffer->GetUsageCommandBuffer().Handle()};
458
459 if (!device_.QueueSubmit(
460 {destination_pipeline_stage}, // wait_dest_pipeline_stages
461 wait_semaphores, // wait_semaphores
462 signal_semaphores, // signal_semaphores
463 command_buffers, // command_buffers
464 backbuffer->GetUsageFence() // fence
465 )) {
466 FML_DLOG(INFO) << "Could not submit to the device queue.";
467 return error;
468 }
469
470 // ---------------------------------------------------------------------------
471 // Step 8:
472 // Tell Skia about the updated image layout.
473 // ---------------------------------------------------------------------------
474 sk_sp<SkSurface> surface = surfaces_[next_image_index];
475
476 if (surface == nullptr) {
477 FML_DLOG(INFO) << "Could not access surface at the image index.";
478 return error;
479 }
480
482 surface.get(), SkSurfaces::BackendHandleAccess::kFlushRead);
483 if (!backendRT.isValid()) {
484 FML_DLOG(INFO) << "Could not get backend render target.";
485 return error;
486 }
488 destination_image_layout);
489
490 current_image_index_ = next_image_index;
491
493}
494
496 if (!IsValid()) {
497 FML_DLOG(INFO) << "Swapchain was invalid.";
498 return false;
499 }
500
501 sk_sp<SkSurface> surface = surfaces_[current_image_index_];
502 const std::unique_ptr<VulkanImage>& image = images_[current_image_index_];
503 auto backbuffer = backbuffers_[current_backbuffer_index_].get();
504
505 // ---------------------------------------------------------------------------
506 // Step 0:
507 // Make sure Skia has flushed all work for the surface to the gpu.
508 // ---------------------------------------------------------------------------
510
511 // ---------------------------------------------------------------------------
512 // Step 1:
513 // Start recording to the command buffer.
514 // ---------------------------------------------------------------------------
515 if (!backbuffer->GetRenderCommandBuffer().Begin()) {
516 FML_DLOG(INFO) << "Could not start recording to the command buffer.";
517 return false;
518 }
519
520 // ---------------------------------------------------------------------------
521 // Step 2:
522 // Set image layout to present mode.
523 // ---------------------------------------------------------------------------
524 VkPipelineStageFlagBits destination_pipeline_stage =
526 VkImageLayout destination_image_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
527
528 if (!image->InsertImageMemoryBarrier(
529 backbuffer->GetRenderCommandBuffer(), // command buffer
530 current_pipeline_stage_, // src_pipeline_bits
531 destination_pipeline_stage, // dest_pipeline_bits
532 VK_ACCESS_MEMORY_READ_BIT, // dest_access_flags
533 destination_image_layout // dest_layout
534 )) {
535 FML_DLOG(INFO) << "Could not insert memory barrier.";
536 return false;
537 } else {
538 current_pipeline_stage_ = destination_pipeline_stage;
539 }
540
541 // ---------------------------------------------------------------------------
542 // Step 3:
543 // End recording to the command buffer.
544 // ---------------------------------------------------------------------------
545 if (!backbuffer->GetRenderCommandBuffer().End()) {
546 FML_DLOG(INFO) << "Could not end recording to the command buffer.";
547 return false;
548 }
549
550 // ---------------------------------------------------------------------------
551 // Step 4:
552 // Submit the command buffer to the device queue. Tell it to signal the render
553 // semaphore.
554 // ---------------------------------------------------------------------------
555 std::vector<VkSemaphore> wait_semaphores = {};
556 std::vector<VkSemaphore> queue_signal_semaphores = {
557 backbuffer->GetRenderSemaphore()};
558 std::vector<VkCommandBuffer> command_buffers = {
559 backbuffer->GetRenderCommandBuffer().Handle()};
560
561 if (!device_.QueueSubmit(
562 {/* Empty. No wait semaphores. */}, // wait_dest_pipeline_stages
563 wait_semaphores, // wait_semaphores
564 queue_signal_semaphores, // signal_semaphores
565 command_buffers, // command_buffers
566 backbuffer->GetRenderFence() // fence
567 )) {
568 FML_DLOG(INFO) << "Could not submit to the device queue.";
569 return false;
570 }
571
572 // ---------------------------------------------------------------------------
573 // Step 5:
574 // Submit the present operation and wait on the render semaphore.
575 // ---------------------------------------------------------------------------
576 VkSwapchainKHR swapchain = swapchain_;
577 uint32_t present_image_index = static_cast<uint32_t>(current_image_index_);
578 const VkPresentInfoKHR present_info = {
580 .pNext = nullptr,
581 .waitSemaphoreCount =
582 static_cast<uint32_t>(queue_signal_semaphores.size()),
583 .pWaitSemaphores = queue_signal_semaphores.data(),
584 .swapchainCount = 1,
585 .pSwapchains = &swapchain,
586 .pImageIndices = &present_image_index,
587 .pResults = nullptr,
588 };
589
590 if (VK_CALL_LOG_ERROR(vk.QueuePresentKHR(device_.GetQueueHandle(),
591 &present_info)) != VK_SUCCESS) {
592 FML_DLOG(INFO) << "Could not submit the present operation.";
593 return false;
594 }
595
596 return true;
597}
598
599} // namespace vulkan
int count
@ kTopLeft_GrSurfaceOrigin
Definition GrTypes.h:148
SkColorType
Definition SkColorType.h:19
@ kRGBA_F16_SkColorType
pixel with half floats for red, green, blue, alpha;
Definition SkColorType.h:38
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition SkColorType.h:24
@ kUnknown_SkColorType
uninitialized
Definition SkColorType.h:20
@ kUnknown_SkPixelGeometry
SK_API bool colorTypeSupportedAsSurface(SkColorType colorType) const
static sk_sp< SkColorSpace > MakeSRGB()
static sk_sp< SkColorSpace > MakeSRGBLinear()
T * get() const
Definition SkRefCnt.h:303
std::pair< AcquireStatus, sk_sp< SkSurface > > AcquireResult
@ Success
A valid SkSurface was acquired successfully from the swapchain.
VulkanSwapchain(const VulkanProcTable &vk, const VulkanDevice &device, const VulkanSurface &surface, GrDirectContext *skia_context, std::unique_ptr< VulkanSwapchain > old_swapchain, uint32_t queue_family_index)
VkSwapchainKHR swapchain
Definition main.cc:64
VkDevice device
Definition main.cc:53
uint32_t queue_family_index
Definition main.cc:54
VkSurfaceKHR surface
Definition main.cc:49
sk_sp< SkImage > image
Definition examples.cpp:29
const uint8_t uint32_t uint32_t GError ** error
#define FML_ALLOW_UNUSED_LOCAL(x)
#define FML_DLOG(severity)
Definition logging.h:102
#define FML_LOG(severity)
Definition logging.h:82
#define FML_DCHECK(condition)
Definition logging.h:103
std::array< MockImage, 3 > images
SK_API void SetVkImageLayout(GrBackendRenderTarget *, VkImageLayout)
SK_API GrBackendRenderTarget MakeVk(int width, int height, const GrVkImageInfo &)
SK_API sk_sp< SkSurface > WrapBackendRenderTarget(GrRecordingContext *context, const GrBackendRenderTarget &backendRenderTarget, GrSurfaceOrigin origin, SkColorType colorType, sk_sp< SkColorSpace > colorSpace, const SkSurfaceProps *surfaceProps, RenderTargetReleaseProc releaseProc=nullptr, ReleaseContext releaseContext=nullptr)
SK_API GrBackendRenderTarget GetBackendRenderTarget(SkSurface *, BackendHandleAccess)
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition switches.h:259
SK_API void FlushAndSubmit(sk_sp< SkSurface >)
static std::vector< FormatInfo > DesiredFormatInfos()
uint32_t color_type
VkImage fImage
Definition GrVkTypes.h:26
VkFormat fFormat
Definition GrVkTypes.h:30
uint32_t fSampleCount
Definition GrVkTypes.h:32
uint32_t fLevelCount
Definition GrVkTypes.h:33
VkImageLayout fImageLayout
Definition GrVkTypes.h:29
VkImageUsageFlags fImageUsageFlags
Definition GrVkTypes.h:31
VkImageTiling fImageTiling
Definition GrVkTypes.h:28
static constexpr SkISize Make(int32_t w, int32_t h)
Definition SkSize.h:20
uint32_t width
uint32_t height
VkStructureType sType
VkImageLayout
@ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
@ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
@ VK_IMAGE_LAYOUT_UNDEFINED
@ VK_SHARING_MODE_EXCLUSIVE
#define VK_TRUE
VkFlags VkImageUsageFlags
@ VK_IMAGE_TILING_OPTIMAL
@ VK_IMAGE_USAGE_TRANSFER_DST_BIT
@ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
@ VK_IMAGE_USAGE_TRANSFER_SRC_BIT
@ VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
#define VK_FALSE
VkResult
@ VK_SUCCESS
@ VK_ERROR_OUT_OF_DATE_KHR
@ VK_ERROR_SURFACE_LOST_KHR
@ VK_ACCESS_MEMORY_READ_BIT
@ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
@ VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
VkPresentModeKHR
@ VK_PRESENT_MODE_FIFO_KHR
#define VK_NULL_HANDLE
Definition vulkan_core.h:46
VkFormat
@ VK_FORMAT_R8G8B8A8_SRGB
@ VK_FORMAT_B8G8R8A8_UNORM
@ VK_FORMAT_B8G8R8A8_SRGB
@ VK_FORMAT_R8G8B8A8_UNORM
@ VK_FORMAT_UNDEFINED
@ VK_FORMAT_R16G16B16A16_SFLOAT
uint32_t VkBool32
Definition vulkan_core.h:94
VkPipelineStageFlagBits
@ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT
@ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
@ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
@ VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR
@ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR
#define VK_CALL_LOG_ERROR(expression)
SkColorType color_type_
sk_sp< SkColorSpace > color_space_
VkFormat format_