Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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
6
18
19namespace impeller {
20
21static constexpr size_t kMaxFramesInFlight = 2u;
22
24 vk::UniqueFence acquire;
26 vk::UniqueSemaphore render_ready;
27 std::shared_ptr<CommandBuffer> final_cmd_buffer;
28 bool is_valid = false;
29 // Whether the renderer attached an onscreen command buffer to render to.
30 bool has_onscreen = false;
31
32 explicit KHRFrameSynchronizerVK(const vk::Device& device) {
33 auto acquire_res = device.createFenceUnique({});
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
46
47 bool WaitForFence(const vk::Device& device) {
49 return true;
50 }
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 }
61 if (auto result = device.resetFences(*acquire);
62 result != vk::Result::eSuccess) {
63 VALIDATION_LOG << "Could not reset fence: " << vk::to_string(result);
64 return false;
65 }
66 return true;
67 }
68};
69
70static bool ContainsFormat(const std::vector<vk::SurfaceFormatKHR>& formats,
71 vk::SurfaceFormatKHR format) {
72 return std::find(formats.begin(), formats.end(), format) != formats.end();
73}
74
75static std::optional<vk::SurfaceFormatKHR> ChooseSurfaceFormat(
76 const std::vector<vk::SurfaceFormatKHR>& formats,
77 PixelFormat preference) {
78 const auto colorspace = vk::ColorSpaceKHR::eSrgbNonlinear;
79 const auto vk_preference =
80 vk::SurfaceFormatKHR{ToVKImageFormat(preference), colorspace};
81 if (ContainsFormat(formats, vk_preference)) {
82 return vk_preference;
83 }
84
85 std::vector<vk::SurfaceFormatKHR> options = {
86 {vk::Format::eB8G8R8A8Unorm, colorspace},
87 {vk::Format::eR8G8B8A8Unorm, colorspace}};
88 for (const auto& format : options) {
89 if (ContainsFormat(formats, format)) {
90 return format;
91 }
92 }
93
94 return std::nullopt;
95}
96
97static std::optional<vk::CompositeAlphaFlagBitsKHR> ChooseAlphaCompositionMode(
98 vk::CompositeAlphaFlagsKHR flags) {
99 if (flags & vk::CompositeAlphaFlagBitsKHR::eInherit) {
100 return vk::CompositeAlphaFlagBitsKHR::eInherit;
101 }
102 if (flags & vk::CompositeAlphaFlagBitsKHR::ePreMultiplied) {
103 return vk::CompositeAlphaFlagBitsKHR::ePreMultiplied;
104 }
105 if (flags & vk::CompositeAlphaFlagBitsKHR::ePostMultiplied) {
106 return vk::CompositeAlphaFlagBitsKHR::ePostMultiplied;
107 }
108 if (flags & vk::CompositeAlphaFlagBitsKHR::eOpaque) {
109 return vk::CompositeAlphaFlagBitsKHR::eOpaque;
110 }
111
112 return std::nullopt;
113}
114
115std::shared_ptr<KHRSwapchainImplVK> KHRSwapchainImplVK::Create(
116 const std::shared_ptr<Context>& context,
117 vk::UniqueSurfaceKHR surface,
118 const ISize& size,
119 bool enable_msaa,
120 vk::SwapchainKHR old_swapchain) {
121 return std::shared_ptr<KHRSwapchainImplVK>(new KHRSwapchainImplVK(
122 context, std::move(surface), size, enable_msaa, old_swapchain));
123}
124
125KHRSwapchainImplVK::KHRSwapchainImplVK(const std::shared_ptr<Context>& context,
126 vk::UniqueSurfaceKHR surface,
127 const ISize& size,
128 bool enable_msaa,
129 vk::SwapchainKHR old_swapchain) {
130 if (!context) {
131 VALIDATION_LOG << "Cannot create a swapchain without a context.";
132 return;
133 }
134
135 auto& vk_context = ContextVK::Cast(*context);
136
137 const auto [caps_result, surface_caps] =
138 vk_context.GetPhysicalDevice().getSurfaceCapabilitiesKHR(*surface);
139 if (caps_result != vk::Result::eSuccess) {
140 VALIDATION_LOG << "Could not get surface capabilities: "
141 << vk::to_string(caps_result);
142 return;
143 }
144
145 auto [formats_result, formats] =
146 vk_context.GetPhysicalDevice().getSurfaceFormatsKHR(*surface);
147 if (formats_result != vk::Result::eSuccess) {
148 VALIDATION_LOG << "Could not get surface formats: "
149 << vk::to_string(formats_result);
150 return;
151 }
152
153 const auto format = ChooseSurfaceFormat(
154 formats, vk_context.GetCapabilities()->GetDefaultColorFormat());
155 if (!format.has_value()) {
156 VALIDATION_LOG << "Swapchain has no supported formats.";
157 return;
158 }
159 vk_context.SetOffscreenFormat(ToPixelFormat(format.value().format));
160
161 const auto composite =
162 ChooseAlphaCompositionMode(surface_caps.supportedCompositeAlpha);
163 if (!composite.has_value()) {
164 VALIDATION_LOG << "No composition mode supported.";
165 return;
166 }
167
168 vk::SwapchainCreateInfoKHR swapchain_info;
169 swapchain_info.surface = *surface;
170 swapchain_info.imageFormat = format.value().format;
171 swapchain_info.imageColorSpace = format.value().colorSpace;
172 swapchain_info.presentMode = vk::PresentModeKHR::eFifo;
173 swapchain_info.imageExtent = vk::Extent2D{
174 std::clamp(static_cast<uint32_t>(size.width),
175 surface_caps.minImageExtent.width,
176 surface_caps.maxImageExtent.width),
177 std::clamp(static_cast<uint32_t>(size.height),
178 surface_caps.minImageExtent.height,
179 surface_caps.maxImageExtent.height),
180 };
181 swapchain_info.minImageCount =
182 std::clamp(surface_caps.minImageCount + 1u, // preferred image count
183 surface_caps.minImageCount, // min count cannot be zero
184 surface_caps.maxImageCount == 0u
185 ? surface_caps.minImageCount + 1u
186 : surface_caps.maxImageCount // max zero means no limit
187 );
188 swapchain_info.imageArrayLayers = 1u;
189 // Swapchain images are primarily used as color attachments (via resolve) or
190 // input attachments.
191 swapchain_info.imageUsage = vk::ImageUsageFlagBits::eColorAttachment |
192 vk::ImageUsageFlagBits::eInputAttachment;
193 swapchain_info.preTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity;
194 swapchain_info.compositeAlpha = composite.value();
195 // If we set the clipped value to true, Vulkan expects we will never read back
196 // from the buffer. This is analogous to [CAMetalLayer framebufferOnly] in
197 // Metal.
198 swapchain_info.clipped = true;
199 // Setting queue family indices is irrelevant since the present mode is
200 // exclusive.
201 swapchain_info.imageSharingMode = vk::SharingMode::eExclusive;
202 swapchain_info.oldSwapchain = old_swapchain;
203
204 auto [swapchain_result, swapchain] =
205 vk_context.GetDevice().createSwapchainKHRUnique(swapchain_info);
206 if (swapchain_result != vk::Result::eSuccess) {
207 VALIDATION_LOG << "Could not create swapchain: "
208 << vk::to_string(swapchain_result);
209 return;
210 }
211
212 auto [images_result, images] =
213 vk_context.GetDevice().getSwapchainImagesKHR(*swapchain);
214 if (images_result != vk::Result::eSuccess) {
215 VALIDATION_LOG << "Could not get swapchain images.";
216 return;
217 }
218
219 TextureDescriptor texture_desc;
220 texture_desc.usage = TextureUsage::kRenderTarget;
221 texture_desc.storage_mode = StorageMode::kDevicePrivate;
222 texture_desc.format = ToPixelFormat(swapchain_info.imageFormat);
223 texture_desc.size = ISize::MakeWH(swapchain_info.imageExtent.width,
224 swapchain_info.imageExtent.height);
225
226 std::vector<std::shared_ptr<KHRSwapchainImageVK>> swapchain_images;
227 std::vector<vk::UniqueSemaphore> present_semaphores;
228 for (const auto& image : images) {
229 auto swapchain_image = std::make_shared<KHRSwapchainImageVK>(
230 texture_desc, // texture descriptor
231 vk_context.GetDevice(), // device
232 image // image
233 );
234 if (!swapchain_image->IsValid()) {
235 VALIDATION_LOG << "Could not create swapchain image.";
236 return;
237 }
239 vk_context.GetDevice(), swapchain_image->GetImage(),
240 "SwapchainImage" + std::to_string(swapchain_images.size()));
242 vk_context.GetDevice(), swapchain_image->GetImageView(),
243 "SwapchainImageView" + std::to_string(swapchain_images.size()));
244
245 swapchain_images.emplace_back(swapchain_image);
246
247 auto present_res = vk_context.GetDevice().createSemaphoreUnique({});
248 if (present_res.result != vk::Result::eSuccess) {
249 VALIDATION_LOG << "Could not create presentation semaphore.";
250 return;
251 }
252 present_semaphores.push_back(std::move(present_res.value));
253 }
254
255 std::vector<std::unique_ptr<KHRFrameSynchronizerVK>> synchronizers;
256 for (size_t i = 0u; i < kMaxFramesInFlight; i++) {
257 auto sync =
258 std::make_unique<KHRFrameSynchronizerVK>(vk_context.GetDevice());
259 if (!sync->is_valid) {
260 VALIDATION_LOG << "Could not create frame synchronizers.";
261 return;
262 }
263 synchronizers.emplace_back(std::move(sync));
264 }
265 FML_DCHECK(!synchronizers.empty());
266
267 context_ = context;
268 surface_ = std::move(surface);
269 surface_format_ = swapchain_info.imageFormat;
270 swapchain_ = std::move(swapchain);
271 transients_ = std::make_shared<SwapchainTransientsVK>(context, texture_desc,
272 enable_msaa);
273 images_ = std::move(swapchain_images);
274 synchronizers_ = std::move(synchronizers);
275 present_semaphores_ = std::move(present_semaphores);
276 current_frame_ = synchronizers_.size() - 1u;
277 size_ = size;
278 enable_msaa_ = enable_msaa;
279 is_valid_ = true;
280}
281
285
287 return size_;
288}
289
291 const {
292 if (!IsValid()) {
293 return std::nullopt;
294 }
295
296 auto context = context_.lock();
297 if (!context) {
298 return std::nullopt;
299 }
300
301 auto& vk_context = ContextVK::Cast(*context);
302 const auto [result, surface_caps] =
303 vk_context.GetPhysicalDevice().getSurfaceCapabilitiesKHR(surface_.get());
304 if (result != vk::Result::eSuccess) {
305 return std::nullopt;
306 }
307
308 // From the spec: `currentExtent` is the current width and height of the
309 // surface, or the special value (0xFFFFFFFF, 0xFFFFFFFF) indicating that the
310 // surface size will be determined by the extent of a swapchain targeting the
311 // surface.
312 constexpr uint32_t kCurrentExtentsPlaceholder = 0xFFFFFFFF;
313 if (surface_caps.currentExtent.width == kCurrentExtentsPlaceholder ||
314 surface_caps.currentExtent.height == kCurrentExtentsPlaceholder) {
315 return std::nullopt;
316 }
317
318 return ISize::MakeWH(surface_caps.currentExtent.width,
319 surface_caps.currentExtent.height);
320}
321
323 return is_valid_;
324}
325
326void KHRSwapchainImplVK::WaitIdle() const {
327 if (auto context = context_.lock()) {
328 [[maybe_unused]] auto result =
329 ContextVK::Cast(*context).GetDevice().waitIdle();
330 }
331}
332
333std::pair<vk::UniqueSurfaceKHR, vk::UniqueSwapchainKHR>
335 WaitIdle();
336 is_valid_ = false;
337 synchronizers_.clear();
338 images_.clear();
339 context_.reset();
340 return {std::move(surface_), std::move(swapchain_)};
341}
342
344 return surface_format_;
345}
346
347std::shared_ptr<Context> KHRSwapchainImplVK::GetContext() const {
348 return context_.lock();
349}
350
352 auto context_strong = context_.lock();
353 if (!context_strong) {
355 }
356
357 const auto& context = ContextVK::Cast(*context_strong);
358
359 current_frame_ = (current_frame_ + 1u) % synchronizers_.size();
360
361 const auto& sync = synchronizers_[current_frame_];
362
363 //----------------------------------------------------------------------------
364 /// Wait on the host for the synchronizer fence.
365 ///
366 if (!sync->WaitForFence(context.GetDevice())) {
367 VALIDATION_LOG << "Could not wait for fence.";
369 }
370
371 //----------------------------------------------------------------------------
372 /// Get the next image index.
373 ///
374 /// @bug Non-infinite timeouts are not supported on some older Android
375 /// devices and the only indication we get is log spam which serves to
376 /// add confusion. Just use an infinite timeout instead of being
377 /// defensive.
378 auto [acq_result, index] = context.GetDevice().acquireNextImageKHR(
379 *swapchain_, // swapchain
380 std::numeric_limits<uint64_t>::max(), // timeout (ns)
381 *sync->render_ready, // signal semaphore
382 nullptr // fence
383 );
384
385 switch (acq_result) {
386 case vk::Result::eSuccess:
387 // Keep going.
388 break;
389 case vk::Result::eSuboptimalKHR:
390 case vk::Result::eErrorOutOfDateKHR:
391 // A recoverable error. Just say we are out of date.
392 return AcquireResult{true /* out of date */};
393 break;
394 case vk::Result::eErrorSurfaceLostKHR:
395 // This error code is returned by Android for some situations that are
396 // recoverable but do not require recreating the swapchain.
397 return AcquireResult{false /* out of date */};
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.";
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;
416 transients_, // transients
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
429 std::shared_ptr<CommandBuffer> cmd_buffer) {
430 const auto& sync = synchronizers_[current_frame_];
431 sync->final_cmd_buffer = std::move(cmd_buffer);
432 sync->has_onscreen = true;
433}
434
435bool KHRSwapchainImplVK::Present(
436 const std::shared_ptr<KHRSwapchainImageVK>& image,
437 uint32_t index) {
438 auto context_strong = context_.lock();
439 if (!context_strong) {
440 return false;
441 }
442
443 const auto& context = ContextVK::Cast(*context_strong);
444 const auto& sync = synchronizers_[current_frame_];
445 context.GetGPUTracer()->MarkFrameEnd();
446
447 //----------------------------------------------------------------------------
448 /// Transition the image to color-attachment-optimal.
449 ///
450 if (!sync->has_onscreen) {
451 sync->final_cmd_buffer = context.CreateCommandBuffer();
452 }
453 sync->has_onscreen = false;
454 if (!sync->final_cmd_buffer) {
455 return false;
456 }
457
458 auto vk_final_cmd_buffer =
459 CommandBufferVK::Cast(*sync->final_cmd_buffer).GetCommandBuffer();
460 {
461 BarrierVK barrier;
462 barrier.new_layout = vk::ImageLayout::ePresentSrcKHR;
463 barrier.cmd_buffer = vk_final_cmd_buffer;
464 barrier.src_access = vk::AccessFlagBits::eColorAttachmentWrite;
465 barrier.src_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
466 barrier.dst_access = {};
467 barrier.dst_stage = vk::PipelineStageFlagBits::eBottomOfPipe;
468
469 if (!image->SetLayout(barrier).ok()) {
470 return false;
471 }
472
473 if (vk_final_cmd_buffer.end() != vk::Result::eSuccess) {
474 return false;
475 }
476 }
477
478 //----------------------------------------------------------------------------
479 /// Signal that the presentation semaphore is ready.
480 ///
481 {
482 vk::SubmitInfo submit_info;
483 vk::PipelineStageFlags wait_stage =
484 vk::PipelineStageFlagBits::eColorAttachmentOutput;
485 submit_info.setWaitDstStageMask(wait_stage);
486 submit_info.setWaitSemaphores(*sync->render_ready);
487 submit_info.setSignalSemaphores(*present_semaphores_[index]);
488 submit_info.setCommandBuffers(vk_final_cmd_buffer);
489 auto result =
490 context.GetGraphicsQueue()->Submit(submit_info, *sync->acquire);
491 if (result != vk::Result::eSuccess) {
492 VALIDATION_LOG << "Could not wait on render semaphore: "
493 << vk::to_string(result);
494 return false;
495 }
496 sync->acquire_fence_pending = true;
497 }
498
499 //----------------------------------------------------------------------------
500 /// Present the image.
501 ///
502 uint32_t indices[] = {static_cast<uint32_t>(index)};
503
504 vk::PresentInfoKHR present_info;
505 present_info.setSwapchains(*swapchain_);
506 present_info.setImageIndices(indices);
507 present_info.setWaitSemaphores(*present_semaphores_[index]);
508
509 auto result = context.GetGraphicsQueue()->Present(present_info);
510
511 switch (result) {
512 case vk::Result::eErrorOutOfDateKHR:
513 // Caller will recreate the impl on acquisition, not submission.
514 [[fallthrough]];
515 case vk::Result::eErrorSurfaceLostKHR:
516 // Vulkan guarantees that the set of queue operations will still
517 // complete successfully.
518 [[fallthrough]];
519 case vk::Result::eSuboptimalKHR:
520 // Even though we're handling rotation changes via polling, we
521 // still need to handle the case where the swapchain signals that
522 // it's suboptimal (i.e. every frame when we are rotated given we
523 // aren't doing Vulkan pre-rotation).
524 [[fallthrough]];
525 case vk::Result::eSuccess:
526 break;
527 default:
528 VALIDATION_LOG << "Could not present queue: " << vk::to_string(result);
529 break;
530 }
531
532 return true;
533}
534
535} // namespace impeller
static ContextVK & Cast(Context &base)
vk::CommandBuffer GetCommandBuffer() const
Retrieve the native command buffer from this object.
bool SetDebugName(T handle, std::string_view label) const
Definition context_vk.h:151
const vk::Device & GetDevice() const
An instance of a swapchain that does NOT adapt to going out of date with the underlying surface....
std::shared_ptr< Context > GetContext() const
void AddFinalCommandBuffer(std::shared_ptr< CommandBuffer > cmd_buffer)
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)
std::optional< ISize > GetCurrentUnderlyingSurfaceSize() const
std::pair< vk::UniqueSurfaceKHR, vk::UniqueSwapchainKHR > DestroySwapchain()
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
FlutterVulkanImage * image
VkSwapchainKHR swapchain
Definition main.cc:80
VkDevice device
Definition main.cc:69
std::vector< VkImage > swapchain_images
Definition main.cc:81
VkSurfaceKHR surface
Definition main.cc:65
uint32_t uint32_t * format
#define FML_DCHECK(condition)
Definition logging.h:122
std::array< MockImage, 3 > images
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
static std::optional< vk::SurfaceFormatKHR > ChooseSurfaceFormat(const std::vector< vk::SurfaceFormatKHR > &formats, PixelFormat preference)
constexpr PixelFormat ToPixelFormat(vk::Format format)
Definition formats_vk.h:185
static constexpr size_t kMaxFramesInFlight
static bool ContainsFormat(const std::vector< vk::SurfaceFormatKHR > &formats, vk::SurfaceFormatKHR format)
static std::optional< vk::CompositeAlphaFlagBitsKHR > ChooseAlphaCompositionMode(vk::CompositeAlphaFlagsKHR flags)
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition formats.h:99
constexpr vk::Format ToVKImageFormat(PixelFormat format)
Definition formats_vk.h:146
bool WaitForFence(const vk::Device &device)
KHRFrameSynchronizerVK(const vk::Device &device)
std::shared_ptr< CommandBuffer > final_cmd_buffer
static constexpr TSize MakeWH(Type width, Type height)
Definition size.h:43
#define VALIDATION_LOG
Definition validation.h:91