Flutter Engine
 
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;
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
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
66static 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
71static 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
93static 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
111std::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
121KHRSwapchainImplVK::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 }
235 vk_context.GetDevice(), swapchain_image->GetImage(),
236 "SwapchainImage" + std::to_string(swapchain_images.size()));
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
281
283 return size_;
284}
285
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
319 return is_valid_;
320}
321
322void KHRSwapchainImplVK::WaitIdle() const {
323 if (auto context = context_.lock()) {
324 [[maybe_unused]] auto result =
325 ContextVK::Cast(*context).GetDevice().waitIdle();
326 }
327}
328
329std::pair<vk::UniqueSurfaceKHR, vk::UniqueSwapchainKHR>
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
340 return surface_format_;
341}
342
343std::shared_ptr<Context> KHRSwapchainImplVK::GetContext() const {
344 return context_.lock();
345}
346
348 auto context_strong = context_.lock();
349 if (!context_strong) {
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.";
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.";
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;
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
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
427bool 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
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:183
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