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