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 
6 
7 #include "fml/synchronization/count_down_latch.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 
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 
270  vk_context.GetDevice(), swapchain_image->GetImage(),
271  "SwapchainImage" + std::to_string(swapchain_images.size()));
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 
304 }
305 
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>
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 
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 
340  auto context_strong = context_.lock();
341  if (!context_strong) {
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.";
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);
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