| Flutter Impeller |

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