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