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