| Flutter Impeller |

khr_swapchain_impl_vk.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 "impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.h"
6
7 #include "fml/synchronization/semaphore.h"
8 #include "impeller/base/validation.h"
9 #include "impeller/core/formats.h"
10 #include "impeller/renderer/backend/vulkan/command_buffer_vk.h"
11 #include "impeller/renderer/backend/vulkan/context_vk.h"
12 #include "impeller/renderer/backend/vulkan/formats_vk.h"
13 #include "impeller/renderer/backend/vulkan/gpu_tracer_vk.h"
14 #include "impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_image_vk.h"
15 #include "impeller/renderer/backend/vulkan/swapchain/surface_vk.h"
16 #include "impeller/renderer/context.h"
17
18 namespace impeller {
19
20 static constexpr size_t kMaxFramesInFlight = 3u;
21
22 struct KHRFrameSynchronizerVK {
23 vk::UniqueFence acquire;
24 vk::UniqueSemaphore render_ready;
25 vk::UniqueSemaphore present_ready;
26 std::shared_ptr<CommandBuffer> final_cmd_buffer;
27 bool is_valid = false;
28
29 explicit KHRFrameSynchronizerVK(const vk::Device& device) {
30 auto acquire_res = device.createFenceUnique(
31 vk::FenceCreateInfo{vk::FenceCreateFlagBits::eSignaled});
32 auto render_res = device.createSemaphoreUnique({});
33 auto present_res = device.createSemaphoreUnique({});
34 if (acquire_res.result != vk::Result::eSuccess ||
35 render_res.result != vk::Result::eSuccess ||
36 present_res.result != vk::Result::eSuccess) {
37 VALIDATION_LOG << "Could not create synchronizer.";
38 return;
39 }
40 acquire = std::move(acquire_res.value);
41 render_ready = std::move(render_res.value);
42 present_ready = std::move(present_res.value);
43 is_valid = true;
44 }
45
46 ~KHRFrameSynchronizerVK() = default;
47
48 bool WaitForFence(const vk::Device& device) {
49 if (auto result = device.waitForFences(
50 *acquire, // fence
51 true, // wait all
52 std::numeric_limits<uint64_t>::max() // timeout (ns)
53 );
54 result != vk::Result::eSuccess) {
55 VALIDATION_LOG << "Fence wait failed: " << vk::to_string(result);
56 return false;
57 }
58 if (auto result = device.resetFences(*acquire);
59 result != vk::Result::eSuccess) {
60 VALIDATION_LOG << "Could not reset fence: " << vk::to_string(result);
61 return false;
62 }
63 return true;
64 }
65 };
66
67 static bool ContainsFormat(const std::vector<vk::SurfaceFormatKHR>& formats,
68 vk::SurfaceFormatKHR format) {
69 return std::find(formats.begin(), formats.end(), format) != formats.end();
70 }
71
72 static std::optional<vk::SurfaceFormatKHR> ChooseSurfaceFormat(
73 const std::vector<vk::SurfaceFormatKHR>& formats,
74 PixelFormat preference) {
75 const auto colorspace = vk::ColorSpaceKHR::eSrgbNonlinear;
76 const auto vk_preference =
77 vk::SurfaceFormatKHR{ToVKImageFormat(preference), colorspace};
78 if (ContainsFormat(formats, vk_preference)) {
79 return vk_preference;
80 }
81
82 std::vector<vk::SurfaceFormatKHR> options = {
83 {vk::Format::eB8G8R8A8Unorm, colorspace},
84 {vk::Format::eR8G8B8A8Unorm, colorspace}};
85 for (const auto& format : options) {
86 if (ContainsFormat(formats, format)) {
87 return format;
88 }
89 }
90
91 return std::nullopt;
92 }
93
94 static std::optional<vk::CompositeAlphaFlagBitsKHR> ChooseAlphaCompositionMode(
95 vk::CompositeAlphaFlagsKHR flags) {
96 if (flags & vk::CompositeAlphaFlagBitsKHR::eInherit) {
97 return vk::CompositeAlphaFlagBitsKHR::eInherit;
98 }
99 if (flags & vk::CompositeAlphaFlagBitsKHR::ePreMultiplied) {
100 return vk::CompositeAlphaFlagBitsKHR::ePreMultiplied;
101 }
102 if (flags & vk::CompositeAlphaFlagBitsKHR::ePostMultiplied) {
103 return vk::CompositeAlphaFlagBitsKHR::ePostMultiplied;
104 }
105 if (flags & vk::CompositeAlphaFlagBitsKHR::eOpaque) {
106 return vk::CompositeAlphaFlagBitsKHR::eOpaque;
107 }
108
109 return std::nullopt;
110 }
111
112 std::shared_ptr<KHRSwapchainImplVK> KHRSwapchainImplVK::Create(
113 const std::shared_ptr<Context>& context,
114 vk::UniqueSurfaceKHR surface,
115 const ISize& size,
116 bool enable_msaa,
117 vk::SwapchainKHR old_swapchain) {
118 return std::shared_ptr<KHRSwapchainImplVK>(new KHRSwapchainImplVK(
119 context, std::move(surface), size, enable_msaa, old_swapchain));
120 }
121
122 KHRSwapchainImplVK::KHRSwapchainImplVK(const std::shared_ptr<Context>& context,
123 vk::UniqueSurfaceKHR surface,
124 const ISize& size,
125 bool enable_msaa,
126 vk::SwapchainKHR old_swapchain) {
127 if (!context) {
128 VALIDATION_LOG << "Cannot create a swapchain without a context.";
129 return;
130 }
131
132 auto& vk_context = ContextVK::Cast(*context);
133
134 const auto [caps_result, surface_caps] =
135 vk_context.GetPhysicalDevice().getSurfaceCapabilitiesKHR(*surface);
136 if (caps_result != vk::Result::eSuccess) {
137 VALIDATION_LOG << "Could not get surface capabilities: "
138 << vk::to_string(caps_result);
139 return;
140 }
141
142 auto [formats_result, formats] =
143 vk_context.GetPhysicalDevice().getSurfaceFormatsKHR(*surface);
144 if (formats_result != vk::Result::eSuccess) {
145 VALIDATION_LOG << "Could not get surface formats: "
146 << vk::to_string(formats_result);
147 return;
148 }
149
150 const auto format = ChooseSurfaceFormat(
151 formats, vk_context.GetCapabilities()->GetDefaultColorFormat());
152 if (!format.has_value()) {
153 VALIDATION_LOG << "Swapchain has no supported formats.";
154 return;
155 }
156 vk_context.SetOffscreenFormat(ToPixelFormat(format.value().format));
157
158 const auto composite =
159 ChooseAlphaCompositionMode(surface_caps.supportedCompositeAlpha);
160 if (!composite.has_value()) {
161 VALIDATION_LOG << "No composition mode supported.";
162 return;
163 }
164
165 vk::SwapchainCreateInfoKHR swapchain_info;
166 swapchain_info.surface = *surface;
167 swapchain_info.imageFormat = format.value().format;
168 swapchain_info.imageColorSpace = format.value().colorSpace;
169 swapchain_info.presentMode = vk::PresentModeKHR::eFifo;
170 swapchain_info.imageExtent = vk::Extent2D{
171 std::clamp(static_cast<uint32_t>(size.width),
172 surface_caps.minImageExtent.width,
173 surface_caps.maxImageExtent.width),
174 std::clamp(static_cast<uint32_t>(size.height),
175 surface_caps.minImageExtent.height,
176 surface_caps.maxImageExtent.height),
177 };
178 swapchain_info.minImageCount =
179 std::clamp(surface_caps.minImageCount + 1u, // preferred image count
180 surface_caps.minImageCount, // min count cannot be zero
181 surface_caps.maxImageCount == 0u
182 ? surface_caps.minImageCount + 1u
183 : surface_caps.maxImageCount // max zero means no limit
184 );
185 swapchain_info.imageArrayLayers = 1u;
186 // Swapchain images are primarily used as color attachments (via resolve),
187 // blit targets, or input attachments.
188 swapchain_info.imageUsage = vk::ImageUsageFlagBits::eColorAttachment |
189 vk::ImageUsageFlagBits::eTransferDst |
190 vk::ImageUsageFlagBits::eInputAttachment;
191 swapchain_info.preTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity;
192 swapchain_info.compositeAlpha = composite.value();
193 // If we set the clipped value to true, Vulkan expects we will never read back
194 // from the buffer. This is analogous to [CAMetalLayer framebufferOnly] in
195 // Metal.
196 swapchain_info.clipped = true;
197 // Setting queue family indices is irrelevant since the present mode is
198 // exclusive.
199 swapchain_info.imageSharingMode = vk::SharingMode::eExclusive;
200 swapchain_info.oldSwapchain = old_swapchain;
201
202 auto [swapchain_result, swapchain] =
203 vk_context.GetDevice().createSwapchainKHRUnique(swapchain_info);
204 if (swapchain_result != vk::Result::eSuccess) {
205 VALIDATION_LOG << "Could not create swapchain: "
206 << vk::to_string(swapchain_result);
207 return;
208 }
209
210 auto [images_result, images] =
211 vk_context.GetDevice().getSwapchainImagesKHR(*swapchain);
212 if (images_result != vk::Result::eSuccess) {
213 VALIDATION_LOG << "Could not get swapchain images.";
214 return;
215 }
216
217 TextureDescriptor texture_desc;
218 texture_desc.usage = TextureUsage::kRenderTarget;
219 texture_desc.storage_mode = StorageMode::kDevicePrivate;
220 texture_desc.format = ToPixelFormat(swapchain_info.imageFormat);
221 texture_desc.size = ISize::MakeWH(swapchain_info.imageExtent.width,
222 swapchain_info.imageExtent.height);
223
224 std::vector<std::shared_ptr<KHRSwapchainImageVK>> swapchain_images;
225 for (const auto& image : images) {
226 auto swapchain_image = std::make_shared<KHRSwapchainImageVK>(
227 texture_desc, // texture descriptor
228 vk_context.GetDevice(), // device
229 image // image
230 );
231 if (!swapchain_image->IsValid()) {
232 VALIDATION_LOG << "Could not create swapchain image.";
233 return;
234 }
235 ContextVK::SetDebugName(
236 vk_context.GetDevice(), swapchain_image->GetImage(),
237 "SwapchainImage" + std::to_string(swapchain_images.size()));
238 ContextVK::SetDebugName(
239 vk_context.GetDevice(), swapchain_image->GetImageView(),
240 "SwapchainImageView" + std::to_string(swapchain_images.size()));
241
242 swapchain_images.emplace_back(swapchain_image);
243 }
244
245 std::vector<std::unique_ptr<KHRFrameSynchronizerVK>> synchronizers;
246 for (size_t i = 0u; i < kMaxFramesInFlight; i++) {
247 auto sync =
248 std::make_unique<KHRFrameSynchronizerVK>(vk_context.GetDevice());
249 if (!sync->is_valid) {
250 VALIDATION_LOG << "Could not create frame synchronizers.";
251 return;
252 }
253 synchronizers.emplace_back(std::move(sync));
254 }
255 FML_DCHECK(!synchronizers.empty());
256
257 context_ = context;
258 surface_ = std::move(surface);
259 surface_format_ = swapchain_info.imageFormat;
260 swapchain_ = std::move(swapchain);
261 transients_ = std::make_shared<SwapchainTransientsVK>(context, texture_desc,
262 enable_msaa);
263 images_ = std::move(swapchain_images);
264 synchronizers_ = std::move(synchronizers);
265 current_frame_ = synchronizers_.size() - 1u;
266 size_ = size;
267 enable_msaa_ = enable_msaa;
268 is_valid_ = true;
269 }
270
271 KHRSwapchainImplVK::~KHRSwapchainImplVK() {
272 DestroySwapchain();
273 }
274
275 const ISize& KHRSwapchainImplVK::GetSize() const {
276 return size_;
277 }
278
279 bool KHRSwapchainImplVK::IsValid() const {
280 return is_valid_;
281 }
282
283 void KHRSwapchainImplVK::WaitIdle() const {
284 if (auto context = context_.lock()) {
285 [[maybe_unused]] auto result =
286 ContextVK::Cast(*context).GetDevice().waitIdle();
287 }
288 }
289
290 std::pair<vk::UniqueSurfaceKHR, vk::UniqueSwapchainKHR>
291 KHRSwapchainImplVK::DestroySwapchain() {
292 WaitIdle();
293 is_valid_ = false;
294 synchronizers_.clear();
295 images_.clear();
296 context_.reset();
297 return {std::move(surface_), std::move(swapchain_)};
298 }
299
300 vk::Format KHRSwapchainImplVK::GetSurfaceFormat() const {
301 return surface_format_;
302 }
303
304 std::shared_ptr<Context> KHRSwapchainImplVK::GetContext() const {
305 return context_.lock();
306 }
307
308 KHRSwapchainImplVK::AcquireResult KHRSwapchainImplVK::AcquireNextDrawable() {
309 auto context_strong = context_.lock();
310 if (!context_strong) {
311 return KHRSwapchainImplVK::AcquireResult{};
312 }
313
314 const auto& context = ContextVK::Cast(*context_strong);
315
316 current_frame_ = (current_frame_ + 1u) % synchronizers_.size();
317
318 const auto& sync = synchronizers_[current_frame_];
319
320 //----------------------------------------------------------------------------
321 /// Wait on the host for the synchronizer fence.
322 ///
323 if (!sync->WaitForFence(context.GetDevice())) {
324 VALIDATION_LOG << "Could not wait for fence.";
325 return KHRSwapchainImplVK::AcquireResult{};
326 }
327
328 //----------------------------------------------------------------------------
329 /// Get the next image index.
330 ///
331 /// @bug Non-infinite timeouts are not supported on some older Android
332 /// devices and the only indication we get is log spam which serves to
333 /// add confusion. Just use an infinite timeout instead of being
334 /// defensive.
335 auto [acq_result, index] = context.GetDevice().acquireNextImageKHR(
336 *swapchain_, // swapchain
337 std::numeric_limits<uint64_t>::max(), // timeout (ns)
338 *sync->render_ready, // signal semaphore
339 nullptr // fence
340 );
341
342 switch (acq_result) {
343 case vk::Result::eSuccess:
344 // Keep going.
345 break;
346 case vk::Result::eSuboptimalKHR:
347 case vk::Result::eErrorOutOfDateKHR:
348 // A recoverable error. Just say we are out of date.
349 return AcquireResult{true /* out of date */};
350 break;
351 default:
352 // An unrecoverable error.
353 VALIDATION_LOG << "Could not acquire next swapchain image: "
354 << vk::to_string(acq_result);
355 return AcquireResult{false /* out of date */};
356 }
357
358 if (index >= images_.size()) {
359 VALIDATION_LOG << "Swapchain returned an invalid image index.";
360 return KHRSwapchainImplVK::AcquireResult{};
361 }
362
363 /// Record all subsequent cmd buffers as part of the current frame.
364 context.GetGPUTracer()->MarkFrameStart();
365
366 auto image = images_[index % images_.size()];
367 uint32_t image_index = index;
368 return AcquireResult{SurfaceVK::WrapSwapchainImage(
369 transients_, // transients
370 image, // swapchain image
371 [weak_swapchain = weak_from_this(), image, image_index]() -> bool {
372 auto swapchain = weak_swapchain.lock();
373 if (!swapchain) {
374 return false;
375 }
376 return swapchain->Present(image, image_index);
377 } // swap callback
378 )};
379 }
380
381 bool KHRSwapchainImplVK::Present(
382 const std::shared_ptr<KHRSwapchainImageVK>& image,
383 uint32_t index) {
384 auto context_strong = context_.lock();
385 if (!context_strong) {
386 return false;
387 }
388
389 const auto& context = ContextVK::Cast(*context_strong);
390 const auto& sync = synchronizers_[current_frame_];
391 context.GetGPUTracer()->MarkFrameEnd();
392
393 //----------------------------------------------------------------------------
394 /// Transition the image to color-attachment-optimal.
395 ///
396 sync->final_cmd_buffer = context.CreateCommandBuffer();
397 if (!sync->final_cmd_buffer) {
398 return false;
399 }
400
401 auto vk_final_cmd_buffer =
402 CommandBufferVK::Cast(*sync->final_cmd_buffer).GetCommandBuffer();
403 {
404 BarrierVK barrier;
405 barrier.new_layout = vk::ImageLayout::ePresentSrcKHR;
406 barrier.cmd_buffer = vk_final_cmd_buffer;
407 barrier.src_access = vk::AccessFlagBits::eColorAttachmentWrite;
408 barrier.src_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
409 barrier.dst_access = {};
410 barrier.dst_stage = vk::PipelineStageFlagBits::eBottomOfPipe;
411
412 if (!image->SetLayout(barrier).ok()) {
413 return false;
414 }
415
416 if (vk_final_cmd_buffer.end() != vk::Result::eSuccess) {
417 return false;
418 }
419 }
420
421 //----------------------------------------------------------------------------
422 /// Signal that the presentation semaphore is ready.
423 ///
424 {
425 vk::SubmitInfo submit_info;
426 vk::PipelineStageFlags wait_stage =
427 vk::PipelineStageFlagBits::eColorAttachmentOutput;
428 submit_info.setWaitDstStageMask(wait_stage);
429 submit_info.setWaitSemaphores(*sync->render_ready);
430 submit_info.setSignalSemaphores(*sync->present_ready);
431 submit_info.setCommandBuffers(vk_final_cmd_buffer);
432 auto result =
433 context.GetGraphicsQueue()->Submit(submit_info, *sync->acquire);
434 if (result != vk::Result::eSuccess) {
435 VALIDATION_LOG << "Could not wait on render semaphore: "
436 << vk::to_string(result);
437 return false;
438 }
439 }
440
441 //----------------------------------------------------------------------------
442 /// Present the image.
443 ///
444 uint32_t indices[] = {static_cast<uint32_t>(index)};
445
446 vk::PresentInfoKHR present_info;
447 present_info.setSwapchains(*swapchain_);
448 present_info.setImageIndices(indices);
449 present_info.setWaitSemaphores(*sync->present_ready);
450
451 auto result = context.GetGraphicsQueue()->Present(present_info);
452
453 switch (result) {
454 case vk::Result::eErrorOutOfDateKHR:
455 // Caller will recreate the impl on acquisition, not submission.
456 [[fallthrough]];
457 case vk::Result::eErrorSurfaceLostKHR:
458 // Vulkan guarantees that the set of queue operations will still
459 // complete successfully.
460 [[fallthrough]];
461 case vk::Result::eSuboptimalKHR:
462 // Even though we're handling rotation changes via polling, we
463 // still need to handle the case where the swapchain signals that
464 // it's suboptimal (i.e. every frame when we are rotated given we
465 // aren't doing Vulkan pre-rotation).
466 [[fallthrough]];
467 case vk::Result::eSuccess:
468 break;
469 default:
470 VALIDATION_LOG << "Could not present queue: " << vk::to_string(result);
471 break;
472 }
473
474 return true;
475 }
476
477 } // namespace impeller
impeller::SurfaceVK::WrapSwapchainImage
static std::unique_ptr< SurfaceVK > WrapSwapchainImage(const std::shared_ptr< SwapchainTransientsVK > &transients, const std::shared_ptr< TextureSourceVK > &swapchain_image, SwapCallback swap_callback)
Wrap the swapchain image in a Surface, which provides the additional configuration required for usage...
Definition: surface_vk.cc:13
gpu_tracer_vk.h
khr_swapchain_impl_vk.h
impeller::KHRSwapchainImplVK::GetSurfaceFormat
vk::Format GetSurfaceFormat() const
Definition: khr_swapchain_impl_vk.cc:300
surface_vk.h
impeller::KHRFrameSynchronizerVK::~KHRFrameSynchronizerVK
~KHRFrameSynchronizerVK()=default
impeller::KHRSwapchainImplVK::GetSize
const ISize & GetSize() const
Definition: khr_swapchain_impl_vk.cc:275
impeller::KHRSwapchainImplVK
An instance of a swapchain that does NOT adapt to going out of date with the underlying surface....
Definition: khr_swapchain_impl_vk.h:31
impeller::KHRSwapchainImplVK::AcquireNextDrawable
AcquireResult AcquireNextDrawable()
Definition: khr_swapchain_impl_vk.cc:308
formats.h
impeller::TextureUsage::kRenderTarget
@ kRenderTarget
formats_vk.h
impeller::kMaxFramesInFlight
static constexpr size_t kMaxFramesInFlight
Definition: khr_swapchain_impl_vk.cc:20
khr_swapchain_image_vk.h
validation.h
impeller::KHRSwapchainImplVK::DestroySwapchain
std::pair< vk::UniqueSurfaceKHR, vk::UniqueSwapchainKHR > DestroySwapchain()
Definition: khr_swapchain_impl_vk.cc:291
impeller::PixelFormat
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:99
impeller::CommandBufferVK::GetCommandBuffer
vk::CommandBuffer GetCommandBuffer() const
Retrieve the native command buffer from this object.
Definition: command_buffer_vk.cc:112
impeller::KHRSwapchainImplVK::AcquireResult
Definition: khr_swapchain_impl_vk.h:45
command_buffer_vk.h
impeller::TSize
Definition: size.h:19
impeller::ToVKImageFormat
constexpr vk::Format ToVKImageFormat(PixelFormat format)
Definition: formats_vk.h:133
impeller::KHRFrameSynchronizerVK::WaitForFence
bool WaitForFence(const vk::Device &device)
Definition: khr_swapchain_impl_vk.cc:48
impeller::ContainsFormat
static bool ContainsFormat(const std::vector< vk::SurfaceFormatKHR > &formats, vk::SurfaceFormatKHR format)
Definition: khr_swapchain_impl_vk.cc:67
impeller::StorageMode::kDevicePrivate
@ kDevicePrivate
impeller::KHRSwapchainImplVK::GetContext
std::shared_ptr< Context > GetContext() const
Definition: khr_swapchain_impl_vk.cc:304
impeller::KHRSwapchainImplVK::~KHRSwapchainImplVK
~KHRSwapchainImplVK()
Definition: khr_swapchain_impl_vk.cc:271
impeller::KHRFrameSynchronizerVK::acquire
vk::UniqueFence acquire
Definition: khr_swapchain_impl_vk.cc:23
impeller::KHRFrameSynchronizerVK::render_ready
vk::UniqueSemaphore render_ready
Definition: khr_swapchain_impl_vk.cc:24
impeller::ChooseAlphaCompositionMode
static std::optional< vk::CompositeAlphaFlagBitsKHR > ChooseAlphaCompositionMode(vk::CompositeAlphaFlagsKHR flags)
Definition: khr_swapchain_impl_vk.cc:94
impeller::KHRFrameSynchronizerVK
Definition: khr_swapchain_impl_vk.cc:22
impeller::ChooseSurfaceFormat
static std::optional< vk::SurfaceFormatKHR > ChooseSurfaceFormat(const std::vector< vk::SurfaceFormatKHR > &formats, PixelFormat preference)
Definition: khr_swapchain_impl_vk.cc:72
impeller::ContextVK::SetDebugName
bool SetDebugName(T handle, std::string_view label) const
Definition: context_vk.h:109
impeller::TSize::width
Type width
Definition: size.h:22
impeller::KHRFrameSynchronizerVK::final_cmd_buffer
std::shared_ptr< CommandBuffer > final_cmd_buffer
Definition: khr_swapchain_impl_vk.cc:26
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91
impeller::KHRSwapchainImplVK::IsValid
bool IsValid() const
Definition: khr_swapchain_impl_vk.cc:279
impeller::KHRSwapchainImplVK::Create
static std::shared_ptr< KHRSwapchainImplVK > Create(const std::shared_ptr< Context > &context, vk::UniqueSurfaceKHR surface, const ISize &size, bool enable_msaa=true, vk::SwapchainKHR old_swapchain=VK_NULL_HANDLE)
Definition: khr_swapchain_impl_vk.cc:112
impeller::ContextVK::GetDevice
const vk::Device & GetDevice() const
Definition: context_vk.cc:530
impeller::BackendCast< ContextVK, Context >::Cast
static ContextVK & Cast(Context &base)
Definition: backend_cast.h:13
impeller::KHRFrameSynchronizerVK::present_ready
vk::UniqueSemaphore present_ready
Definition: khr_swapchain_impl_vk.cc:25
impeller::KHRFrameSynchronizerVK::KHRFrameSynchronizerVK
KHRFrameSynchronizerVK(const vk::Device &device)
Definition: khr_swapchain_impl_vk.cc:29
context.h
impeller::TSize::height
Type height
Definition: size.h:23
context_vk.h
impeller::KHRFrameSynchronizerVK::is_valid
bool is_valid
Definition: khr_swapchain_impl_vk.cc:27
impeller::TSize::MakeWH
static constexpr TSize MakeWH(Type width, Type height)
Definition: size.h:34
impeller
Definition: allocation.cc:12
impeller::ToPixelFormat
static PixelFormat ToPixelFormat(AHardwareBuffer_Format format)
Definition: ahb_texture_source_vk.cc:221