Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
vulkan_surface.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_surface.h"
6
7#include <fuchsia/sysmem/cpp/fidl.h>
8#include <lib/async/default.h>
9
10#include <algorithm>
11
12#include "flutter/fml/logging.h"
13#include "flutter/fml/trace_event.h"
23#include "vulkan/vulkan_core.h"
24#include "vulkan/vulkan_fuchsia.h"
25
26#define LOG_AND_RETURN(cond, msg) \
27 if (cond) { \
28 FML_LOG(ERROR) << msg; \
29 return false; \
30 }
31
32namespace flutter_runner {
33
34namespace {
35
36constexpr SkColorType kSkiaColorType = kRGBA_8888_SkColorType;
37constexpr VkFormat kVulkanFormat = VK_FORMAT_R8G8B8A8_UNORM;
38constexpr VkImageCreateFlags kVulkanImageCreateFlags = 0;
39// TODO: We should only keep usages that are actually required by Skia.
40constexpr VkImageUsageFlags kVkImageUsage =
43const VkSysmemColorSpaceFUCHSIA kSrgbColorSpace = {
45 .pNext = nullptr,
46 .colorSpace = static_cast<uint32_t>(fuchsia::sysmem::ColorSpaceType::SRGB),
47};
48
49} // namespace
50
51bool VulkanSurface::CreateVulkanImage(vulkan::VulkanProvider& vulkan_provider,
52 const SkISize& size,
53 VulkanImage* out_vulkan_image) {
54 TRACE_EVENT0("flutter", "CreateVulkanImage");
55
56 FML_CHECK(!size.isEmpty());
57 FML_CHECK(out_vulkan_image != nullptr);
58
59 out_vulkan_image->vk_collection_image_create_info = {
61 .pNext = nullptr,
62 .collection = collection_,
63 .index = 0,
64 };
65
66 out_vulkan_image->vk_image_create_info = {
68 .pNext = &out_vulkan_image->vk_collection_image_create_info,
69 .flags = kVulkanImageCreateFlags,
70 .imageType = VK_IMAGE_TYPE_2D,
71 .format = kVulkanFormat,
72 .extent = VkExtent3D{static_cast<uint32_t>(size.width()),
73 static_cast<uint32_t>(size.height()), 1},
74 .mipLevels = 1,
75 .arrayLayers = 1,
76 .samples = VK_SAMPLE_COUNT_1_BIT,
78 .usage = kVkImageUsage,
79 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
80 .queueFamilyIndexCount = 0,
81 .pQueueFamilyIndices = nullptr,
82 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
83 };
84
85 const VkImageFormatConstraintsInfoFUCHSIA format_constraints = {
87 .pNext = nullptr,
88 .imageCreateInfo = out_vulkan_image->vk_image_create_info,
89 .requiredFormatFeatures = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT,
90 .flags = {},
91 .sysmemPixelFormat = {},
92 .colorSpaceCount = 1,
93 .pColorSpaces = &kSrgbColorSpace,
94 };
95
96 const VkImageConstraintsInfoFUCHSIA image_constraints = {
98 .pNext = nullptr,
99 .formatConstraintsCount = 1,
100 .pFormatConstraints = &format_constraints,
101 .bufferCollectionConstraints =
102 {
103 .sType =
105 .pNext = nullptr,
106 .minBufferCount = 1,
107 // Using the default value 0 means that we don't have any other
108 // constraints except for the minimum buffer count.
109 .maxBufferCount = 0,
110 .minBufferCountForCamping = 0,
111 .minBufferCountForDedicatedSlack = 0,
112 .minBufferCountForSharedSlack = 0,
113 },
114 .flags = {},
115 };
116
118 vulkan_provider.vk().SetBufferCollectionImageConstraintsFUCHSIA(
119 vulkan_provider.vk_device(), collection_, &image_constraints)) !=
120 VK_SUCCESS) {
121 return false;
122 }
123
124 {
125 VkImage vk_image = VK_NULL_HANDLE;
126 if (VK_CALL_LOG_ERROR(vulkan_provider.vk().CreateImage(
127 vulkan_provider.vk_device(),
128 &out_vulkan_image->vk_image_create_info, nullptr, &vk_image)) !=
129 VK_SUCCESS) {
130 return false;
131 }
132
133 out_vulkan_image->vk_image = vulkan::VulkanHandle<VkImage_T*>{
134 vk_image, [&vulkan_provider = vulkan_provider](VkImage image) {
135 vulkan_provider.vk().DestroyImage(vulkan_provider.vk_device(), image,
136 NULL);
137 }};
138 }
139
140 vulkan_provider.vk().GetImageMemoryRequirements(
141 vulkan_provider.vk_device(), out_vulkan_image->vk_image,
142 &out_vulkan_image->vk_memory_requirements);
143
144 return true;
145}
146
148 vulkan::VulkanProvider& vulkan_provider,
149 fuchsia::sysmem::AllocatorSyncPtr& sysmem_allocator,
150 fuchsia::ui::composition::AllocatorPtr& flatland_allocator,
152 const SkISize& size)
153 : vulkan_provider_(vulkan_provider), wait_(this) {
154 FML_CHECK(flatland_allocator.is_bound());
155 FML_CHECK(context != nullptr);
156
157 if (!AllocateDeviceMemory(sysmem_allocator, flatland_allocator,
158 std::move(context), size)) {
159 FML_LOG(ERROR) << "VulkanSurface: Could not allocate device memory.";
160 return;
161 }
162
163 if (!CreateFences()) {
164 FML_LOG(ERROR) << "VulkanSurface: Could not create signal fences.";
165 return;
166 }
167
168 std::fill(size_history_.begin(), size_history_.end(), SkISize::MakeEmpty());
169
170 wait_.set_object(release_event_.get());
171 wait_.set_trigger(ZX_EVENT_SIGNALED);
172 Reset();
173
174 valid_ = true;
175}
176
178 if (release_image_callback_) {
179 release_image_callback_();
180 }
181 wait_.Cancel();
182 wait_.set_object(ZX_HANDLE_INVALID);
183}
184
186 return valid_;
187}
188
190 if (!valid_) {
191 return SkISize::Make(0, 0);
192 }
193
194 return SkISize::Make(sk_surface_->width(), sk_surface_->height());
195}
196
197vulkan::VulkanHandle<VkSemaphore> VulkanSurface::SemaphoreFromEvent(
198 const zx::event& event) const {
200 VkSemaphore semaphore;
201
202 zx::event semaphore_event;
203 zx_status_t status = event.duplicate(ZX_RIGHT_SAME_RIGHTS, &semaphore_event);
204 if (status != ZX_OK) {
205 FML_LOG(ERROR) << "VulkanSurface: Failed to duplicate semaphore event";
207 }
208
209 VkSemaphoreCreateInfo create_info = {
211 .pNext = nullptr,
212 .flags = 0,
213 };
214
215 result = VK_CALL_LOG_ERROR(vulkan_provider_.vk().CreateSemaphore(
216 vulkan_provider_.vk_device(), &create_info, nullptr, &semaphore));
217 if (result != VK_SUCCESS) {
219 }
220
221 VkImportSemaphoreZirconHandleInfoFUCHSIA import_info = {
223 .pNext = nullptr,
224 .semaphore = semaphore,
226 .zirconHandle = static_cast<uint32_t>(semaphore_event.release())};
227
229 vulkan_provider_.vk().ImportSemaphoreZirconHandleFUCHSIA(
230 vulkan_provider_.vk_device(), &import_info));
231 if (result != VK_SUCCESS) {
233 }
234
236 semaphore, [&vulkan_provider = vulkan_provider_](VkSemaphore semaphore) {
237 vulkan_provider.vk().DestroySemaphore(vulkan_provider.vk_device(),
238 semaphore, nullptr);
239 });
240}
241
242bool VulkanSurface::CreateFences() {
243 if (zx::event::create(0, &acquire_event_) != ZX_OK) {
244 FML_LOG(ERROR) << "VulkanSurface: Failed to create acquire event";
245 return false;
246 }
247
248 acquire_semaphore_ = SemaphoreFromEvent(acquire_event_);
249 if (!acquire_semaphore_) {
250 FML_LOG(ERROR) << "VulkanSurface: Failed to create acquire semaphore";
251 return false;
252 }
253
254 if (zx::event::create(0, &release_event_) != ZX_OK) {
255 FML_LOG(ERROR) << "VulkanSurface: Failed to create release event";
256 return false;
257 }
258
259 command_buffer_fence_ = vulkan_provider_.CreateFence();
260
261 return true;
262}
263
264bool VulkanSurface::AllocateDeviceMemory(
265 fuchsia::sysmem::AllocatorSyncPtr& sysmem_allocator,
266 fuchsia::ui::composition::AllocatorPtr& flatland_allocator,
268 const SkISize& size) {
269 if (size.isEmpty()) {
271 << "VulkanSurface: Failed to allocate surface, size is empty";
272 return false;
273 }
274
275 fuchsia::sysmem::BufferCollectionTokenSyncPtr vulkan_token;
276 zx_status_t status =
277 sysmem_allocator->AllocateSharedCollection(vulkan_token.NewRequest());
278 LOG_AND_RETURN(status != ZX_OK,
279 "VulkanSurface: Failed to allocate collection");
280 fuchsia::sysmem::BufferCollectionTokenSyncPtr scenic_token;
281 status = vulkan_token->Duplicate(std::numeric_limits<uint32_t>::max(),
282 scenic_token.NewRequest());
283 LOG_AND_RETURN(status != ZX_OK,
284 "VulkanSurface: Failed to duplicate collection token");
285 status = vulkan_token->Sync();
286 LOG_AND_RETURN(status != ZX_OK,
287 "VulkanSurface: Failed to sync collection token");
288
289 fuchsia::ui::composition::BufferCollectionExportToken export_token;
290 status = zx::eventpair::create(0, &export_token.value, &import_token_.value);
291
292 fuchsia::ui::composition::RegisterBufferCollectionArgs args;
293 args.set_export_token(std::move(export_token));
294 args.set_buffer_collection_token(std::move(scenic_token));
295 args.set_usage(
296 fuchsia::ui::composition::RegisterBufferCollectionUsage::DEFAULT);
297 flatland_allocator->RegisterBufferCollection(
298 std::move(args),
299 [](fuchsia::ui::composition::Allocator_RegisterBufferCollection_Result
300 result) {
301 if (result.is_err()) {
302 FML_LOG(ERROR)
303 << "RegisterBufferCollection call to Scenic Allocator failed";
304 }
305 });
306
307 VkBufferCollectionCreateInfoFUCHSIA import_info{
309 .pNext = nullptr,
310 .collectionToken = vulkan_token.Unbind().TakeChannel().release(),
311 };
312 VkBufferCollectionFUCHSIA collection;
313 if (VK_CALL_LOG_ERROR(vulkan_provider_.vk().CreateBufferCollectionFUCHSIA(
314 vulkan_provider_.vk_device(), &import_info, nullptr, &collection)) !=
315 VK_SUCCESS) {
316 return false;
317 }
318
320 collection, [&vulkan_provider =
321 vulkan_provider_](VkBufferCollectionFUCHSIA collection) {
322 vulkan_provider.vk().DestroyBufferCollectionFUCHSIA(
323 vulkan_provider.vk_device(), collection, nullptr);
324 }};
325
326 VulkanImage vulkan_image;
327 LOG_AND_RETURN(!CreateVulkanImage(vulkan_provider_, size, &vulkan_image),
328 "VulkanSurface: Failed to create VkImage");
329
330 vulkan_image_ = std::move(vulkan_image);
331 const VkMemoryRequirements& memory_requirements =
332 vulkan_image_.vk_memory_requirements;
333 VkImageCreateInfo& image_create_info = vulkan_image_.vk_image_create_info;
334
335 VkBufferCollectionPropertiesFUCHSIA properties{
338 vulkan_provider_.vk().GetBufferCollectionPropertiesFUCHSIA(
339 vulkan_provider_.vk_device(), collection_, &properties)) !=
340 VK_SUCCESS) {
341 return false;
342 }
343
344 VkImportMemoryBufferCollectionFUCHSIA import_memory_info = {
346 .pNext = nullptr,
347 .collection = collection_,
348 .index = 0,
349 };
350 auto bits = memory_requirements.memoryTypeBits & properties.memoryTypeBits;
351 FML_DCHECK(bits != 0);
352 VkMemoryAllocateInfo allocation_info = {
354 .pNext = &import_memory_info,
355 .allocationSize = memory_requirements.size,
356 .memoryTypeIndex = static_cast<uint32_t>(__builtin_ctz(bits)),
357 };
358
359 {
360 TRACE_EVENT1("flutter", "vkAllocateMemory", "allocationSize",
361 allocation_info.allocationSize);
362 VkDeviceMemory vk_memory = VK_NULL_HANDLE;
363 if (VK_CALL_LOG_ERROR(vulkan_provider_.vk().AllocateMemory(
364 vulkan_provider_.vk_device(), &allocation_info, NULL,
365 &vk_memory)) != VK_SUCCESS) {
366 return false;
367 }
368
370 vk_memory,
371 [&vulkan_provider = vulkan_provider_](VkDeviceMemory memory) {
372 vulkan_provider.vk().FreeMemory(vulkan_provider.vk_device(), memory,
373 NULL);
374 }};
375
376 vk_memory_info_ = allocation_info;
377 }
378
379 // Bind image memory.
380 if (VK_CALL_LOG_ERROR(vulkan_provider_.vk().BindImageMemory(
381 vulkan_provider_.vk_device(), vulkan_image_.vk_image, vk_memory_,
382 0)) != VK_SUCCESS) {
383 return false;
384 }
385
386 return SetupSkiaSurface(std::move(context), size, kSkiaColorType,
387 image_create_info, memory_requirements);
388}
389
390bool VulkanSurface::SetupSkiaSurface(sk_sp<GrDirectContext> context,
391 const SkISize& size,
393 const VkImageCreateInfo& image_create_info,
394 const VkMemoryRequirements& memory_reqs) {
395 FML_CHECK(context != nullptr);
396
397 GrVkAlloc alloc;
398 alloc.fMemory = vk_memory_;
399 alloc.fOffset = 0;
400 alloc.fSize = memory_reqs.size;
401 alloc.fFlags = 0;
402
403 GrVkImageInfo image_info;
404 image_info.fImage = vulkan_image_.vk_image;
405 image_info.fAlloc = alloc;
406 image_info.fImageTiling = image_create_info.tiling;
407 image_info.fImageLayout = image_create_info.initialLayout;
408 image_info.fFormat = image_create_info.format;
409 image_info.fImageUsageFlags = image_create_info.usage;
410 image_info.fSampleCount = 1;
411 image_info.fLevelCount = image_create_info.mipLevels;
412
413 auto sk_render_target =
414 GrBackendRenderTargets::MakeVk(size.width(), size.height(), image_info);
415
416 SkSurfaceProps sk_surface_props(0, kUnknown_SkPixelGeometry);
417
418 auto sk_surface =
420 sk_render_target, //
422 color_type, //
424 &sk_surface_props //
425 );
426
427 if (!sk_surface || sk_surface->getCanvas() == nullptr) {
429 << "VulkanSurface: SkSurfaces::WrapBackendRenderTarget failed";
430 return false;
431 }
432 sk_surface_ = std::move(sk_surface);
433
434 return true;
435}
436
437void VulkanSurface::SetImageId(uint32_t image_id) {
438 FML_CHECK(image_id_ == 0);
439 image_id_ = image_id;
440}
441
443 return image_id_;
444}
445
447 return valid_ ? sk_surface_ : nullptr;
448}
449
450fuchsia::ui::composition::BufferCollectionImportToken
452 fuchsia::ui::composition::BufferCollectionImportToken import_dup;
453 import_token_.value.duplicate(ZX_RIGHT_SAME_RIGHTS, &import_dup.value);
454 return import_dup;
455}
456
458 zx::event fence;
459 acquire_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
460 return fence;
461}
462
464 zx::event fence;
465 release_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence);
466 return fence;
467}
469 ReleaseImageCallback release_image_callback) {
470 release_image_callback_ = release_image_callback;
471}
472
474 size_history_[size_history_index_] = GetSize();
475 size_history_index_ = (size_history_index_ + 1) % kSizeHistorySize;
476 age_++;
477 return age_;
478}
479
481 age_ = 0;
482 return true;
483}
484
486 const std::function<void(void)>& on_writes_committed) {
487 FML_CHECK(on_writes_committed);
488
489 if (!valid_) {
490 on_writes_committed();
491 return;
492 }
493
494 FML_CHECK(pending_on_writes_committed_ == nullptr)
495 << "Attempted to signal a write on the surface when the "
496 "previous write has not yet been acknowledged by the "
497 "compositor.";
498
499 pending_on_writes_committed_ = on_writes_committed;
500}
501
502void VulkanSurface::Reset() {
503 if (acquire_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK ||
504 release_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK) {
505 valid_ = false;
506 FML_LOG(ERROR) << "Could not reset fences. The surface is no longer valid.";
507 }
508
509 VkFence fence = command_buffer_fence_;
510
511 if (command_buffer_) {
512 VK_CALL_LOG_FATAL(vulkan_provider_.vk().WaitForFences(
513 vulkan_provider_.vk_device(), 1, &fence, VK_TRUE, UINT64_MAX));
514 command_buffer_.reset();
515 }
516
517 VK_CALL_LOG_ERROR(vulkan_provider_.vk().ResetFences(
518 vulkan_provider_.vk_device(), 1, &fence));
519
520 // Need to make a new acquire semaphore every frame or else validation layers
521 // get confused about why no one is waiting on it in this VkInstance
522 acquire_semaphore_.Reset();
523 acquire_semaphore_ = SemaphoreFromEvent(acquire_event_);
524 if (!acquire_semaphore_) {
525 FML_LOG(ERROR) << "failed to create acquire semaphore";
526 }
527
528 wait_.Begin(async_get_default_dispatcher());
529
530 // It is safe for the caller to collect the surface in the callback.
531 auto callback = pending_on_writes_committed_;
532 pending_on_writes_committed_ = nullptr;
533 if (callback) {
534 callback();
535 }
536}
537
538void VulkanSurface::OnHandleReady(async_dispatcher_t* dispatcher,
539 async::WaitBase* wait,
540 zx_status_t status,
541 const zx_packet_signal_t* signal) {
542 if (status != ZX_OK)
543 return;
544 FML_DCHECK(signal->observed & ZX_EVENT_SIGNALED);
545 Reset();
546}
547
548} // namespace flutter_runner
@ kTopLeft_GrSurfaceOrigin
Definition GrTypes.h:148
SkColorType
Definition SkColorType.h:19
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition SkColorType.h:24
@ kUnknown_SkPixelGeometry
static sk_sp< SkColorSpace > MakeSRGB()
int width() const
Definition SkSurface.h:178
int height() const
Definition SkSurface.h:184
void SetReleaseImageCallback(ReleaseImageCallback release_image_callback) override
fuchsia::ui::composition::BufferCollectionImportToken GetBufferCollectionImportToken() override
bool IsValid() const override
zx::event GetAcquireFence() override
void SignalWritesFinished(const std::function< void(void)> &on_writes_committed) override
SkISize GetSize() const override
bool FlushSessionAcquireAndReleaseEvents() override
void SetImageId(uint32_t image_id) override
sk_sp< SkSurface > GetSkiaSurface() const override
zx::event GetReleaseFence() override
VulkanSurface(vulkan::VulkanProvider &vulkan_provider, fuchsia::sysmem::AllocatorSyncPtr &sysmem_allocator, fuchsia::ui::composition::AllocatorPtr &flatland_allocator, sk_sp< GrDirectContext > context, const SkISize &size)
T * get() const
Definition SkRefCnt.h:303
vulkan::VulkanHandle< VkFence > CreateFence()
virtual const vulkan::VulkanProcTable & vk()=0
virtual const vulkan::VulkanHandle< VkDevice > & vk_device()=0
sk_sp< SkImage > image
Definition examples.cpp:29
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlKeyEvent * event
GAsyncResult * result
#define FML_LOG(severity)
Definition logging.h:82
#define FML_CHECK(condition)
Definition logging.h:85
#define FML_DCHECK(condition)
Definition logging.h:103
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)
std::function< void()> ReleaseImageCallback
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
uint32_t color_type
#define LOG_AND_RETURN(cond, msg)
VkImage fImage
Definition GrVkTypes.h:26
skgpu::VulkanAlloc fAlloc
Definition GrVkTypes.h:27
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 MakeEmpty()
Definition SkSize.h:22
static constexpr SkISize Make(int32_t w, int32_t h)
Definition SkSize.h:20
VkImageLayout initialLayout
VkImageTiling tiling
VkStructureType sType
VkImageUsageFlags usage
VkStructureType sType
VkDeviceSize allocationSize
VkStructureType sType
VkImageCreateInfo vk_image_create_info
VkMemoryRequirements vk_memory_requirements
vulkan::VulkanHandle< VkImage > vk_image
VkDeviceSize fSize
Definition VulkanTypes.h:40
VkDeviceMemory fMemory
Definition VulkanTypes.h:38
VkDeviceSize fOffset
Definition VulkanTypes.h:39
#define ERROR(message)
#define TRACE_EVENT0(category_group, name)
#define TRACE_EVENT1(category_group, name, arg1_name, arg1_val)
@ VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT
@ 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_SAMPLED_BIT
@ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
@ VK_IMAGE_USAGE_TRANSFER_SRC_BIT
@ VK_SAMPLE_COUNT_1_BIT
@ VK_IMAGE_TYPE_2D
VkFlags VkImageCreateFlags
VkResult
@ VK_SUCCESS
#define VK_NULL_HANDLE
Definition vulkan_core.h:46
VkFormat
@ VK_FORMAT_R8G8B8A8_UNORM
@ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA
@ VK_STRUCTURE_TYPE_IMAGE_CONSTRAINTS_INFO_FUCHSIA
@ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO
@ VK_STRUCTURE_TYPE_BUFFER_COLLECTION_PROPERTIES_FUCHSIA
@ VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CREATE_INFO_FUCHSIA
@ VK_STRUCTURE_TYPE_BUFFER_COLLECTION_IMAGE_CREATE_INFO_FUCHSIA
@ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO
@ VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_ZIRCON_HANDLE_INFO_FUCHSIA
@ VK_STRUCTURE_TYPE_IMPORT_MEMORY_BUFFER_COLLECTION_FUCHSIA
@ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO
@ VK_STRUCTURE_TYPE_SYSMEM_COLOR_SPACE_FUCHSIA
@ VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CONSTRAINTS_INFO_FUCHSIA
@ VK_STRUCTURE_TYPE_IMAGE_FORMAT_CONSTRAINTS_INFO_FUCHSIA
#define VK_CALL_LOG_FATAL(expression)
#define VK_CALL_LOG_ERROR(expression)