Flutter Engine
The Flutter Engine
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 = 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.
26static constexpr size_t kPollFramesForOrientation = 1u;
27
29 vk::UniqueFence acquire;
30 vk::UniqueSemaphore render_ready;
31 vk::UniqueSemaphore present_ready;
32 std::shared_ptr<CommandBuffer> final_cmd_buffer;
33 bool is_valid = false;
34
35 explicit KHRFrameSynchronizerVK(const vk::Device& device) {
36 auto acquire_res = device.createFenceUnique(
37 vk::FenceCreateInfo{vk::FenceCreateFlagBits::eSignaled});
38 auto render_res = device.createSemaphoreUnique({});
39 auto present_res = device.createSemaphoreUnique({});
40 if (acquire_res.result != vk::Result::eSuccess ||
41 render_res.result != vk::Result::eSuccess ||
42 present_res.result != vk::Result::eSuccess) {
43 VALIDATION_LOG << "Could not create synchronizer.";
44 return;
45 }
46 acquire = std::move(acquire_res.value);
47 render_ready = std::move(render_res.value);
48 present_ready = std::move(present_res.value);
49 is_valid = true;
50 }
51
53
54 bool WaitForFence(const vk::Device& device) {
55 if (auto result = device.waitForFences(
56 *acquire, // fence
57 true, // wait all
59 );
60 result != vk::Result::eSuccess) {
61 VALIDATION_LOG << "Fence wait failed: " << vk::to_string(result);
62 return false;
63 }
64 if (auto result = device.resetFences(*acquire);
65 result != vk::Result::eSuccess) {
66 VALIDATION_LOG << "Could not reset fence: " << vk::to_string(result);
67 return false;
68 }
69 return true;
70 }
71};
72
73static bool ContainsFormat(const std::vector<vk::SurfaceFormatKHR>& formats,
74 vk::SurfaceFormatKHR format) {
75 return std::find(formats.begin(), formats.end(), format) != formats.end();
76}
77
78static std::optional<vk::SurfaceFormatKHR> ChooseSurfaceFormat(
79 const std::vector<vk::SurfaceFormatKHR>& formats,
80 PixelFormat preference) {
81 const auto colorspace = vk::ColorSpaceKHR::eSrgbNonlinear;
82 const auto vk_preference =
83 vk::SurfaceFormatKHR{ToVKImageFormat(preference), colorspace};
84 if (ContainsFormat(formats, vk_preference)) {
85 return vk_preference;
86 }
87
88 std::vector<vk::SurfaceFormatKHR> options = {
89 {vk::Format::eB8G8R8A8Unorm, colorspace},
90 {vk::Format::eR8G8B8A8Unorm, colorspace}};
91 for (const auto& format : options) {
92 if (ContainsFormat(formats, format)) {
93 return format;
94 }
95 }
96
97 return std::nullopt;
98}
99
100static std::optional<vk::CompositeAlphaFlagBitsKHR> ChooseAlphaCompositionMode(
101 vk::CompositeAlphaFlagsKHR flags) {
102 if (flags & vk::CompositeAlphaFlagBitsKHR::eInherit) {
103 return vk::CompositeAlphaFlagBitsKHR::eInherit;
104 }
105 if (flags & vk::CompositeAlphaFlagBitsKHR::ePreMultiplied) {
106 return vk::CompositeAlphaFlagBitsKHR::ePreMultiplied;
107 }
108 if (flags & vk::CompositeAlphaFlagBitsKHR::ePostMultiplied) {
109 return vk::CompositeAlphaFlagBitsKHR::ePostMultiplied;
110 }
111 if (flags & vk::CompositeAlphaFlagBitsKHR::eOpaque) {
112 return vk::CompositeAlphaFlagBitsKHR::eOpaque;
113 }
114
115 return std::nullopt;
116}
117
118std::shared_ptr<KHRSwapchainImplVK> KHRSwapchainImplVK::Create(
119 const std::shared_ptr<Context>& context,
120 vk::UniqueSurfaceKHR surface,
121 const ISize& size,
122 bool enable_msaa,
123 vk::SwapchainKHR old_swapchain) {
124 return std::shared_ptr<KHRSwapchainImplVK>(new KHRSwapchainImplVK(
125 context, std::move(surface), size, enable_msaa, old_swapchain));
126}
127
128KHRSwapchainImplVK::KHRSwapchainImplVK(const std::shared_ptr<Context>& context,
129 vk::UniqueSurfaceKHR surface,
130 const ISize& size,
131 bool enable_msaa,
132 vk::SwapchainKHR old_swapchain) {
133 if (!context) {
134 VALIDATION_LOG << "Cannot create a swapchain without a context.";
135 return;
136 }
137
138 auto& vk_context = ContextVK::Cast(*context);
139
140 const auto [caps_result, surface_caps] =
141 vk_context.GetPhysicalDevice().getSurfaceCapabilitiesKHR(*surface);
142 if (caps_result != vk::Result::eSuccess) {
143 VALIDATION_LOG << "Could not get surface capabilities: "
144 << vk::to_string(caps_result);
145 return;
146 }
147
148 auto [formats_result, formats] =
149 vk_context.GetPhysicalDevice().getSurfaceFormatsKHR(*surface);
150 if (formats_result != vk::Result::eSuccess) {
151 VALIDATION_LOG << "Could not get surface formats: "
152 << vk::to_string(formats_result);
153 return;
154 }
155
156 const auto format = ChooseSurfaceFormat(
157 formats, vk_context.GetCapabilities()->GetDefaultColorFormat());
158 if (!format.has_value()) {
159 VALIDATION_LOG << "Swapchain has no supported formats.";
160 return;
161 }
162 vk_context.SetOffscreenFormat(ToPixelFormat(format.value().format));
163
164 const auto composite =
165 ChooseAlphaCompositionMode(surface_caps.supportedCompositeAlpha);
166 if (!composite.has_value()) {
167 VALIDATION_LOG << "No composition mode supported.";
168 return;
169 }
170
171 vk::SwapchainCreateInfoKHR swapchain_info;
172 swapchain_info.surface = *surface;
173 swapchain_info.imageFormat = format.value().format;
174 swapchain_info.imageColorSpace = format.value().colorSpace;
175 swapchain_info.presentMode = vk::PresentModeKHR::eFifo;
176 swapchain_info.imageExtent = vk::Extent2D{
177 std::clamp(static_cast<uint32_t>(size.width),
178 surface_caps.minImageExtent.width,
179 surface_caps.maxImageExtent.width),
180 std::clamp(static_cast<uint32_t>(size.height),
181 surface_caps.minImageExtent.height,
182 surface_caps.maxImageExtent.height),
183 };
184 swapchain_info.minImageCount =
185 std::clamp(surface_caps.minImageCount + 1u, // preferred image count
186 surface_caps.minImageCount, // min count cannot be zero
187 surface_caps.maxImageCount == 0u
188 ? surface_caps.minImageCount + 1u
189 : surface_caps.maxImageCount // max zero means no limit
190 );
191 swapchain_info.imageArrayLayers = 1u;
192 // Swapchain images are primarily used as color attachments (via resolve),
193 // blit targets, or input attachments.
194 swapchain_info.imageUsage = vk::ImageUsageFlagBits::eColorAttachment |
195 vk::ImageUsageFlagBits::eTransferDst |
196 vk::ImageUsageFlagBits::eInputAttachment;
197 swapchain_info.preTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity;
198 swapchain_info.compositeAlpha = composite.value();
199 // If we set the clipped value to true, Vulkan expects we will never read back
200 // from the buffer. This is analogous to [CAMetalLayer framebufferOnly] in
201 // Metal.
202 swapchain_info.clipped = true;
203 // Setting queue family indices is irrelevant since the present mode is
204 // exclusive.
205 swapchain_info.imageSharingMode = vk::SharingMode::eExclusive;
206 swapchain_info.oldSwapchain = old_swapchain;
207
208 auto [swapchain_result, swapchain] =
209 vk_context.GetDevice().createSwapchainKHRUnique(swapchain_info);
210 if (swapchain_result != vk::Result::eSuccess) {
211 VALIDATION_LOG << "Could not create swapchain: "
212 << vk::to_string(swapchain_result);
213 return;
214 }
215
216 auto [images_result, images] =
217 vk_context.GetDevice().getSwapchainImagesKHR(*swapchain);
218 if (images_result != vk::Result::eSuccess) {
219 VALIDATION_LOG << "Could not get swapchain images.";
220 return;
221 }
222
223 TextureDescriptor texture_desc;
224 texture_desc.usage = TextureUsage::kRenderTarget;
225 texture_desc.storage_mode = StorageMode::kDevicePrivate;
226 texture_desc.format = ToPixelFormat(swapchain_info.imageFormat);
227 texture_desc.size = ISize::MakeWH(swapchain_info.imageExtent.width,
228 swapchain_info.imageExtent.height);
229
230 std::vector<std::shared_ptr<KHRSwapchainImageVK>> swapchain_images;
231 for (const auto& image : images) {
232 auto swapchain_image = std::make_shared<KHRSwapchainImageVK>(
233 texture_desc, // texture descriptor
234 vk_context.GetDevice(), // device
235 image // image
236 );
237 if (!swapchain_image->IsValid()) {
238 VALIDATION_LOG << "Could not create swapchain image.";
239 return;
240 }
242 vk_context.GetDevice(), swapchain_image->GetImage(),
243 "SwapchainImage" + std::to_string(swapchain_images.size()));
245 vk_context.GetDevice(), swapchain_image->GetImageView(),
246 "SwapchainImageView" + std::to_string(swapchain_images.size()));
247
248 swapchain_images.emplace_back(swapchain_image);
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 current_frame_ = synchronizers_.size() - 1u;
272 size_ = size;
273 enable_msaa_ = enable_msaa;
274 is_valid_ = true;
275}
276
279}
280
282 return size_;
283}
284
286 return is_valid_;
287}
288
289void KHRSwapchainImplVK::WaitIdle() const {
290 if (auto context = context_.lock()) {
291 [[maybe_unused]] auto result =
292 ContextVK::Cast(*context).GetDevice().waitIdle();
293 }
294}
295
296std::pair<vk::UniqueSurfaceKHR, vk::UniqueSwapchainKHR>
298 WaitIdle();
299 is_valid_ = false;
300 synchronizers_.clear();
301 images_.clear();
302 context_.reset();
303 return {std::move(surface_), std::move(swapchain_)};
304}
305
307 return surface_format_;
308}
309
310std::shared_ptr<Context> KHRSwapchainImplVK::GetContext() const {
311 return context_.lock();
312}
313
315 auto context_strong = context_.lock();
316 if (!context_strong) {
318 }
319
320 const auto& context = ContextVK::Cast(*context_strong);
321
322 current_frame_ = (current_frame_ + 1u) % synchronizers_.size();
323
324 const auto& sync = synchronizers_[current_frame_];
325
326 //----------------------------------------------------------------------------
327 /// Wait on the host for the synchronizer fence.
328 ///
329 if (!sync->WaitForFence(context.GetDevice())) {
330 VALIDATION_LOG << "Could not wait for fence.";
332 }
333
334 //----------------------------------------------------------------------------
335 /// Get the next image index.
336 ///
337 auto [acq_result, index] = context.GetDevice().acquireNextImageKHR(
338 *swapchain_, // swapchain
339 1'000'000'000, // timeout (ns) 1000ms
340 *sync->render_ready, // signal semaphore
341 nullptr // fence
342 );
343
344 switch (acq_result) {
345 case vk::Result::eSuccess:
346 // Keep going.
347 break;
348 case vk::Result::eSuboptimalKHR:
349 case vk::Result::eErrorOutOfDateKHR:
350 // A recoverable error. Just say we are out of date.
351 return AcquireResult{true /* out of date */};
352 break;
353 default:
354 // An unrecoverable error.
355 VALIDATION_LOG << "Could not acquire next swapchain image: "
356 << vk::to_string(acq_result);
357 return AcquireResult{false /* out of date */};
358 }
359
360 if (index >= images_.size()) {
361 VALIDATION_LOG << "Swapchain returned an invalid image index.";
363 }
364
365 /// Record all subsequent cmd buffers as part of the current frame.
366 context.GetGPUTracer()->MarkFrameStart();
367
368 auto image = images_[index % images_.size()];
369 uint32_t image_index = index;
371 transients_, // transients
372 image, // swapchain image
373 [weak_swapchain = weak_from_this(), image, image_index]() -> bool {
374 auto swapchain = weak_swapchain.lock();
375 if (!swapchain) {
376 return false;
377 }
378 return swapchain->Present(image, image_index);
379 } // swap callback
380 )};
381}
382
383bool KHRSwapchainImplVK::Present(
384 const std::shared_ptr<KHRSwapchainImageVK>& image,
385 uint32_t index) {
386 auto context_strong = context_.lock();
387 if (!context_strong) {
388 return false;
389 }
390
391 const auto& context = ContextVK::Cast(*context_strong);
392 const auto& sync = synchronizers_[current_frame_];
393 context.GetGPUTracer()->MarkFrameEnd();
394
395 //----------------------------------------------------------------------------
396 /// Transition the image to color-attachment-optimal.
397 ///
398 sync->final_cmd_buffer = context.CreateCommandBuffer();
399 if (!sync->final_cmd_buffer) {
400 return false;
401 }
402
403 auto vk_final_cmd_buffer = CommandBufferVK::Cast(*sync->final_cmd_buffer)
404 .GetEncoder()
405 ->GetCommandBuffer();
406 {
407 BarrierVK barrier;
408 barrier.new_layout = vk::ImageLayout::ePresentSrcKHR;
409 barrier.cmd_buffer = vk_final_cmd_buffer;
410 barrier.src_access = vk::AccessFlagBits::eColorAttachmentWrite;
411 barrier.src_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
412 barrier.dst_access = {};
413 barrier.dst_stage = vk::PipelineStageFlagBits::eBottomOfPipe;
414
415 if (!image->SetLayout(barrier).ok()) {
416 return false;
417 }
418
419 if (vk_final_cmd_buffer.end() != vk::Result::eSuccess) {
420 return false;
421 }
422 }
423
424 //----------------------------------------------------------------------------
425 /// Signal that the presentation semaphore is ready.
426 ///
427 {
428 vk::SubmitInfo submit_info;
429 vk::PipelineStageFlags wait_stage =
430 vk::PipelineStageFlagBits::eColorAttachmentOutput;
431 submit_info.setWaitDstStageMask(wait_stage);
432 submit_info.setWaitSemaphores(*sync->render_ready);
433 submit_info.setSignalSemaphores(*sync->present_ready);
434 submit_info.setCommandBuffers(vk_final_cmd_buffer);
435 auto result =
436 context.GetGraphicsQueue()->Submit(submit_info, *sync->acquire);
437 if (result != vk::Result::eSuccess) {
438 VALIDATION_LOG << "Could not wait on render semaphore: "
440 return false;
441 }
442 }
443
444 //----------------------------------------------------------------------------
445 /// Present the image.
446 ///
447 uint32_t indices[] = {static_cast<uint32_t>(index)};
448
449 vk::PresentInfoKHR present_info;
450 present_info.setSwapchains(*swapchain_);
451 present_info.setImageIndices(indices);
452 present_info.setWaitSemaphores(*sync->present_ready);
453
454 auto result = context.GetGraphicsQueue()->Present(present_info);
455
456 switch (result) {
457 case vk::Result::eErrorOutOfDateKHR:
458 // Caller will recreate the impl on acquisition, not submission.
459 [[fallthrough]];
460 case vk::Result::eErrorSurfaceLostKHR:
461 // Vulkan guarantees that the set of queue operations will still
462 // complete successfully.
463 [[fallthrough]];
464 case vk::Result::eSuboptimalKHR:
465 // Even though we're handling rotation changes via polling, we
466 // still need to handle the case where the swapchain signals that
467 // it's suboptimal (i.e. every frame when we are rotated given we
468 // aren't doing Vulkan pre-rotation).
469 [[fallthrough]];
470 case vk::Result::eSuccess:
471 break;
472 default:
473 VALIDATION_LOG << "Could not present queue: " << vk::to_string(result);
474 break;
475 }
476
477 return true;
478}
479
480} // namespace impeller
const char * options
static unsigned clamp(SkFixed fx, int max)
int find(T *array, int N, T item)
static ContextVK & Cast(Context &base)
Definition: backend_cast.h:13
const std::shared_ptr< CommandEncoderVK > & GetEncoder()
bool SetDebugName(T handle, std::string_view label) const
Definition: context_vk.h:108
const vk::Device & GetDevice() const
Definition: context_vk.cc:513
An instance of a swapchain that does NOT adapt to going out of date with the underlying surface....
std::shared_ptr< Context > GetContext() const
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::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
VkSwapchainKHR swapchain
Definition: main.cc:64
VkDevice device
Definition: main.cc:53
std::vector< VkImage > swapchain_images
Definition: main.cc:65
VkSurfaceKHR surface
Definition: main.cc:49
FlutterSemanticsFlag flags
GAsyncResult * result
uint32_t uint32_t * format
#define FML_DCHECK(condition)
Definition: logging.h:103
static float max(float r, float g, float b)
Definition: hsl.cpp:49
std::array< MockImage, 3 > images
Definition: mock_vulkan.cc:41
sk_sp< const SkImage > image
Definition: SkRecords.h:269
it will be possible to load the file into Perfetto s trace viewer 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
Definition: switches.h:259
static std::optional< vk::SurfaceFormatKHR > ChooseSurfaceFormat(const std::vector< vk::SurfaceFormatKHR > &formats, PixelFormat preference)
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
static constexpr size_t kPollFramesForOrientation
constexpr vk::Format ToVKImageFormat(PixelFormat format)
Definition: formats_vk.h:133
static PixelFormat ToPixelFormat(AHardwareBuffer_Format format)
def Format(template, **parameters)
Definition: emitter.py:13
static SkString to_string(int n)
Definition: nanobench.cpp:119
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:34
#define VALIDATION_LOG
Definition: validation.h:73