Flutter Engine
vulkan::VulkanSwapchain Class Reference

#include <vulkan_swapchain.h>

Public Types

enum  AcquireStatus {
  AcquireStatus::Success,
  AcquireStatus::ErrorSurfaceLost,
  AcquireStatus::ErrorSurfaceOutOfDate
}
 
using AcquireResult = std::pair< AcquireStatus, sk_sp< SkSurface > >
 

Public Member Functions

 VulkanSwapchain (const VulkanProcTable &vk, const VulkanDevice &device, const VulkanSurface &surface, GrDirectContext *skia_context, std::unique_ptr< VulkanSwapchain > old_swapchain, uint32_t queue_family_index)
 
 ~VulkanSwapchain ()
 
bool IsValid () const
 
AcquireResult AcquireSurface ()
 
bool Submit ()
 
SkISize GetSize () const
 
 FML_DISALLOW_COPY_AND_ASSIGN (VulkanSwapchain)
 

Detailed Description

Definition at line 26 of file vulkan_swapchain.h.

Member Typedef Documentation

◆ AcquireResult

using vulkan::VulkanSwapchain::AcquireResult = std::pair<AcquireStatus, sk_sp<SkSurface> >

Definition at line 50 of file vulkan_swapchain.h.

Member Enumeration Documentation

◆ AcquireStatus

Enumerator
Success 

A valid SkSurface was acquired successfully from the swapchain.

ErrorSurfaceLost 

The underlying surface of the swapchain was permanently lost. This is an unrecoverable error. The entire surface must be recreated along with the swapchain.

ErrorSurfaceOutOfDate 

The swapchain surface is out-of-date with the underlying surface. The swapchain must be recreated.

Definition at line 39 of file vulkan_swapchain.h.

39  {
40  /// A valid SkSurface was acquired successfully from the swapchain.
41  Success,
42  /// The underlying surface of the swapchain was permanently lost. This is an
43  /// unrecoverable error. The entire surface must be recreated along with the
44  /// swapchain.
45  ErrorSurfaceLost,
46  /// The swapchain surface is out-of-date with the underlying surface. The
47  /// swapchain must be recreated.
48  ErrorSurfaceOutOfDate,
49  };

Constructor & Destructor Documentation

◆ VulkanSwapchain()

vulkan::VulkanSwapchain::VulkanSwapchain ( const VulkanProcTable vk,
const VulkanDevice device,
const VulkanSurface surface,
GrDirectContext *  skia_context,
std::unique_ptr< VulkanSwapchain old_swapchain,
uint32_t  queue_family_index 
)

Definition at line 39 of file vulkan_swapchain.cc.

References color_space_, color_type_, vulkan::DesiredFormatInfos(), FML_ALLOW_UNUSED_LOCAL, FML_DLOG, vulkan::VulkanSurface::Handle(), vulkan::VulkanSurface::IsValid(), VK_CALL_LOG_ERROR, and ~VulkanSwapchain().

45  : vk(p_vk),
46  device_(device),
47  capabilities_(),
48  surface_format_(),
49  current_pipeline_stage_(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT),
50  current_backbuffer_index_(0),
51  current_image_index_(0),
52  valid_(false) {
53  if (!device_.IsValid() || !surface.IsValid() || skia_context == nullptr) {
54  FML_DLOG(INFO) << "Device or surface is invalid.";
55  return;
56  }
57 
58  if (!device_.GetSurfaceCapabilities(surface, &capabilities_)) {
59  FML_DLOG(INFO) << "Could not find surface capabilities.";
60  return;
61  }
62 
63  const auto format_infos = DesiredFormatInfos();
64  std::vector<VkFormat> desired_formats(format_infos.size());
65  for (size_t i = 0; i < format_infos.size(); ++i) {
66  if (skia_context->colorTypeSupportedAsSurface(
67  format_infos[i].color_type_)) {
68  desired_formats[i] = format_infos[i].format_;
69  } else {
70  desired_formats[i] = VK_FORMAT_UNDEFINED;
71  }
72  }
73 
74  int format_index =
75  device_.ChooseSurfaceFormat(surface, desired_formats, &surface_format_);
76  if (format_index < 0) {
77  FML_DLOG(INFO) << "Could not choose surface format.";
78  return;
79  }
80 
81  VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR;
82  if (!device_.ChoosePresentMode(surface, &present_mode)) {
83  FML_DLOG(INFO) << "Could not choose present mode.";
84  return;
85  }
86 
87  // Check if the surface can present.
88 
89  VkBool32 supported = VK_FALSE;
90  if (VK_CALL_LOG_ERROR(vk.GetPhysicalDeviceSurfaceSupportKHR(
91  device_.GetPhysicalDeviceHandle(), // physical device
92  queue_family_index, // queue family
93  surface.Handle(), // surface to test
94  &supported)) != VK_SUCCESS) {
95  FML_DLOG(INFO) << "Could not get physical device surface support.";
96  return;
97  }
98 
99  if (supported != VK_TRUE) {
100  FML_DLOG(INFO) << "Surface was not supported by the physical device.";
101  return;
102  }
103 
104  // Construct the Swapchain
105 
106  VkSwapchainKHR old_swapchain_handle = VK_NULL_HANDLE;
107 
108  if (old_swapchain != nullptr && old_swapchain->IsValid()) {
109  old_swapchain_handle = old_swapchain->swapchain_;
110  // The unique pointer to the swapchain will go out of scope here
111  // and its handle collected after the appropriate device wait.
112  }
113 
114  VkSurfaceKHR surface_handle = surface.Handle();
115 
116  VkImageUsageFlags usage_flags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
117  VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
118  VK_IMAGE_USAGE_TRANSFER_DST_BIT;
119 
120  const VkSwapchainCreateInfoKHR create_info = {
121  .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
122  .pNext = nullptr,
123  .flags = 0,
124  .surface = surface_handle,
125  .minImageCount = capabilities_.minImageCount,
126  .imageFormat = surface_format_.format,
127  .imageColorSpace = surface_format_.colorSpace,
128  .imageExtent = capabilities_.currentExtent,
129  .imageArrayLayers = 1,
130  .imageUsage = usage_flags,
131  .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
132  .queueFamilyIndexCount = 0, // Because of the exclusive sharing mode.
133  .pQueueFamilyIndices = nullptr,
134  .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
135  .compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
136  .presentMode = present_mode,
137  .clipped = VK_FALSE,
138  .oldSwapchain = old_swapchain_handle,
139  };
140 
141  VkSwapchainKHR swapchain = VK_NULL_HANDLE;
142 
143  if (VK_CALL_LOG_ERROR(vk.CreateSwapchainKHR(device_.GetHandle(), &create_info,
144  nullptr, &swapchain)) !=
145  VK_SUCCESS) {
146  FML_DLOG(INFO) << "Could not create the swapchain.";
147  return;
148  }
149 
150  swapchain_ = {swapchain, [this](VkSwapchainKHR swapchain) {
151  FML_ALLOW_UNUSED_LOCAL(device_.WaitIdle());
152  vk.DestroySwapchainKHR(device_.GetHandle(), swapchain,
153  nullptr);
154  }};
155 
156  if (!CreateSwapchainImages(
157  skia_context, format_infos[format_index].color_type_,
158  format_infos[format_index].color_space_, usage_flags)) {
159  FML_DLOG(INFO) << "Could not create swapchain images.";
160  return;
161  }
162 
163  valid_ = true;
164 }
#define FML_ALLOW_UNUSED_LOCAL(x)
sk_sp< SkColorSpace > color_space_
#define VK_CALL_LOG_ERROR(expression)
SkColorType color_type_
#define FML_DLOG(severity)
Definition: logging.h:85
static std::vector< FormatInfo > DesiredFormatInfos()

◆ ~VulkanSwapchain()

vulkan::VulkanSwapchain::~VulkanSwapchain ( )
default

Referenced by VulkanSwapchain().

Member Function Documentation

◆ AcquireSurface()

VulkanSwapchain::AcquireResult vulkan::VulkanSwapchain::AcquireSurface ( )

Acquire an SkSurface from the swapchain for the caller to render into for later submission via |Submit|. There must not be consecutive calls to |AcquireFrame| without and interleaving |Submit|.

Definition at line 323 of file vulkan_swapchain.cc.

References error, ErrorSurfaceLost, ErrorSurfaceOutOfDate, FML_DLOG, FML_LOG, IsValid(), Success, and VK_CALL_LOG_ERROR.

323  {
325 
326  if (!IsValid()) {
327  FML_DLOG(INFO) << "Swapchain was invalid.";
328  return error;
329  }
330 
331  // ---------------------------------------------------------------------------
332  // Step 0:
333  // Acquire the next available backbuffer.
334  // ---------------------------------------------------------------------------
335  auto backbuffer = GetNextBackbuffer();
336 
337  if (backbuffer == nullptr) {
338  FML_DLOG(INFO) << "Could not get the next backbuffer.";
339  return error;
340  }
341 
342  // ---------------------------------------------------------------------------
343  // Step 1:
344  // Wait for use readiness.
345  // ---------------------------------------------------------------------------
346  if (!backbuffer->WaitFences()) {
347  FML_DLOG(INFO) << "Failed waiting on fences.";
348  return error;
349  }
350 
351  // ---------------------------------------------------------------------------
352  // Step 2:
353  // Put semaphores in unsignaled state.
354  // ---------------------------------------------------------------------------
355  if (!backbuffer->ResetFences()) {
356  FML_DLOG(INFO) << "Could not reset fences.";
357  return error;
358  }
359 
360  // ---------------------------------------------------------------------------
361  // Step 3:
362  // Acquire the next image index.
363  // ---------------------------------------------------------------------------
364  uint32_t next_image_index = 0;
365 
366  VkResult acquire_result = VK_CALL_LOG_ERROR(
367  vk.AcquireNextImageKHR(device_.GetHandle(), //
368  swapchain_, //
369  std::numeric_limits<uint64_t>::max(), //
370  backbuffer->GetUsageSemaphore(), //
371  VK_NULL_HANDLE, //
372  &next_image_index));
373 
374  switch (acquire_result) {
375  case VK_SUCCESS:
376  break;
377  case VK_ERROR_OUT_OF_DATE_KHR:
378  return {AcquireStatus::ErrorSurfaceOutOfDate, nullptr};
379  case VK_ERROR_SURFACE_LOST_KHR:
380  return {AcquireStatus::ErrorSurfaceLost, nullptr};
381  default:
382  FML_LOG(INFO) << "Unexpected result from AcquireNextImageKHR: "
383  << acquire_result;
384  return {AcquireStatus::ErrorSurfaceLost, nullptr};
385  }
386 
387  // Simple sanity checking of image index.
388  if (next_image_index >= images_.size()) {
389  FML_DLOG(INFO) << "Image index returned was out-of-bounds.";
390  return error;
391  }
392 
393  auto& image = images_[next_image_index];
394  if (!image->IsValid()) {
395  FML_DLOG(INFO) << "Image at index was invalid.";
396  return error;
397  }
398 
399  // ---------------------------------------------------------------------------
400  // Step 4:
401  // Start recording to the command buffer.
402  // ---------------------------------------------------------------------------
403  if (!backbuffer->GetUsageCommandBuffer().Begin()) {
404  FML_DLOG(INFO) << "Could not begin recording to the command buffer.";
405  return error;
406  }
407 
408  // ---------------------------------------------------------------------------
409  // Step 5:
410  // Set image layout to color attachment mode.
411  // ---------------------------------------------------------------------------
412  VkPipelineStageFlagBits destination_pipeline_stage =
413  VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
414  VkImageLayout destination_image_layout =
415  VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
416 
417  if (!image->InsertImageMemoryBarrier(
418  backbuffer->GetUsageCommandBuffer(), // command buffer
419  current_pipeline_stage_, // src_pipeline_bits
420  destination_pipeline_stage, // dest_pipeline_bits
421  VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // dest_access_flags
422  destination_image_layout // dest_layout
423  )) {
424  FML_DLOG(INFO) << "Could not insert image memory barrier.";
425  return error;
426  } else {
427  current_pipeline_stage_ = destination_pipeline_stage;
428  }
429 
430  // ---------------------------------------------------------------------------
431  // Step 6:
432  // End recording to the command buffer.
433  // ---------------------------------------------------------------------------
434  if (!backbuffer->GetUsageCommandBuffer().End()) {
435  FML_DLOG(INFO) << "Could not end recording to the command buffer.";
436  return error;
437  }
438 
439  // ---------------------------------------------------------------------------
440  // Step 7:
441  // Submit the command buffer to the device queue.
442  // ---------------------------------------------------------------------------
443  std::vector<VkSemaphore> wait_semaphores = {backbuffer->GetUsageSemaphore()};
444  std::vector<VkSemaphore> signal_semaphores = {};
445  std::vector<VkCommandBuffer> command_buffers = {
446  backbuffer->GetUsageCommandBuffer().Handle()};
447 
448  if (!device_.QueueSubmit(
449  {destination_pipeline_stage}, // wait_dest_pipeline_stages
450  wait_semaphores, // wait_semaphores
451  signal_semaphores, // signal_semaphores
452  command_buffers, // command_buffers
453  backbuffer->GetUsageFence() // fence
454  )) {
455  FML_DLOG(INFO) << "Could not submit to the device queue.";
456  return error;
457  }
458 
459  // ---------------------------------------------------------------------------
460  // Step 8:
461  // Tell Skia about the updated image layout.
462  // ---------------------------------------------------------------------------
463  sk_sp<SkSurface> surface = surfaces_[next_image_index];
464 
465  if (surface == nullptr) {
466  FML_DLOG(INFO) << "Could not access surface at the image index.";
467  return error;
468  }
469 
470  GrBackendRenderTarget backendRT = surface->getBackendRenderTarget(
471  SkSurface::kFlushRead_BackendHandleAccess);
472  if (!backendRT.isValid()) {
473  FML_DLOG(INFO) << "Could not get backend render target.";
474  return error;
475  }
476  backendRT.setVkImageLayout(destination_image_layout);
477 
478  current_image_index_ = next_image_index;
479 
480  return {AcquireStatus::Success, surface};
481 }
FlMethodResponse GError ** error
#define FML_LOG(severity)
Definition: logging.h:65
std::pair< AcquireStatus, sk_sp< SkSurface > > AcquireResult
#define VK_CALL_LOG_ERROR(expression)
#define FML_DLOG(severity)
Definition: logging.h:85
A valid SkSurface was acquired successfully from the swapchain.

◆ FML_DISALLOW_COPY_AND_ASSIGN()

vulkan::VulkanSwapchain::FML_DISALLOW_COPY_AND_ASSIGN ( VulkanSwapchain  )

◆ GetSize()

SkISize vulkan::VulkanSwapchain::GetSize ( ) const

Definition at line 196 of file vulkan_swapchain.cc.

References color_type, FML_DCHECK, and fml::size().

196  {
197  VkExtent2D extents = capabilities_.currentExtent;
198 
199  if (extents.width < capabilities_.minImageExtent.width) {
200  extents.width = capabilities_.minImageExtent.width;
201  } else if (extents.width > capabilities_.maxImageExtent.width) {
202  extents.width = capabilities_.maxImageExtent.width;
203  }
204 
205  if (extents.height < capabilities_.minImageExtent.height) {
206  extents.height = capabilities_.minImageExtent.height;
207  } else if (extents.height > capabilities_.maxImageExtent.height) {
208  extents.height = capabilities_.maxImageExtent.height;
209  }
210 
211  return SkISize::Make(extents.width, extents.height);
212 }

◆ IsValid()

bool vulkan::VulkanSwapchain::IsValid ( ) const

Definition at line 168 of file vulkan_swapchain.cc.

References VK_CALL_LOG_ERROR.

Referenced by AcquireSurface(), and Submit().

168  {
169  return valid_;
170 }

◆ Submit()

bool vulkan::VulkanSwapchain::Submit ( )

Submit a previously acquired. There must not be consecutive calls to |Submit| without and interleaving |AcquireFrame|.

Definition at line 483 of file vulkan_swapchain.cc.

References FML_DLOG, IsValid(), and VK_CALL_LOG_ERROR.

483  {
484  if (!IsValid()) {
485  FML_DLOG(INFO) << "Swapchain was invalid.";
486  return false;
487  }
488 
489  sk_sp<SkSurface> surface = surfaces_[current_image_index_];
490  const std::unique_ptr<VulkanImage>& image = images_[current_image_index_];
491  auto backbuffer = backbuffers_[current_backbuffer_index_].get();
492 
493  // ---------------------------------------------------------------------------
494  // Step 0:
495  // Make sure Skia has flushed all work for the surface to the gpu.
496  // ---------------------------------------------------------------------------
497  surface->flushAndSubmit();
498 
499  // ---------------------------------------------------------------------------
500  // Step 1:
501  // Start recording to the command buffer.
502  // ---------------------------------------------------------------------------
503  if (!backbuffer->GetRenderCommandBuffer().Begin()) {
504  FML_DLOG(INFO) << "Could not start recording to the command buffer.";
505  return false;
506  }
507 
508  // ---------------------------------------------------------------------------
509  // Step 2:
510  // Set image layout to present mode.
511  // ---------------------------------------------------------------------------
512  VkPipelineStageFlagBits destination_pipeline_stage =
513  VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
514  VkImageLayout destination_image_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
515 
516  if (!image->InsertImageMemoryBarrier(
517  backbuffer->GetRenderCommandBuffer(), // command buffer
518  current_pipeline_stage_, // src_pipeline_bits
519  destination_pipeline_stage, // dest_pipeline_bits
520  VK_ACCESS_MEMORY_READ_BIT, // dest_access_flags
521  destination_image_layout // dest_layout
522  )) {
523  FML_DLOG(INFO) << "Could not insert memory barrier.";
524  return false;
525  } else {
526  current_pipeline_stage_ = destination_pipeline_stage;
527  }
528 
529  // ---------------------------------------------------------------------------
530  // Step 3:
531  // End recording to the command buffer.
532  // ---------------------------------------------------------------------------
533  if (!backbuffer->GetRenderCommandBuffer().End()) {
534  FML_DLOG(INFO) << "Could not end recording to the command buffer.";
535  return false;
536  }
537 
538  // ---------------------------------------------------------------------------
539  // Step 4:
540  // Submit the command buffer to the device queue. Tell it to signal the render
541  // semaphore.
542  // ---------------------------------------------------------------------------
543  std::vector<VkSemaphore> wait_semaphores = {};
544  std::vector<VkSemaphore> queue_signal_semaphores = {
545  backbuffer->GetRenderSemaphore()};
546  std::vector<VkCommandBuffer> command_buffers = {
547  backbuffer->GetRenderCommandBuffer().Handle()};
548 
549  if (!device_.QueueSubmit(
550  {/* Empty. No wait semaphores. */}, // wait_dest_pipeline_stages
551  wait_semaphores, // wait_semaphores
552  queue_signal_semaphores, // signal_semaphores
553  command_buffers, // command_buffers
554  backbuffer->GetRenderFence() // fence
555  )) {
556  FML_DLOG(INFO) << "Could not submit to the device queue.";
557  return false;
558  }
559 
560  // ---------------------------------------------------------------------------
561  // Step 5:
562  // Submit the present operation and wait on the render semaphore.
563  // ---------------------------------------------------------------------------
564  VkSwapchainKHR swapchain = swapchain_;
565  uint32_t present_image_index = static_cast<uint32_t>(current_image_index_);
566  const VkPresentInfoKHR present_info = {
567  .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
568  .pNext = nullptr,
569  .waitSemaphoreCount =
570  static_cast<uint32_t>(queue_signal_semaphores.size()),
571  .pWaitSemaphores = queue_signal_semaphores.data(),
572  .swapchainCount = 1,
573  .pSwapchains = &swapchain,
574  .pImageIndices = &present_image_index,
575  .pResults = nullptr,
576  };
577 
578  if (VK_CALL_LOG_ERROR(vk.QueuePresentKHR(device_.GetQueueHandle(),
579  &present_info)) != VK_SUCCESS) {
580  FML_DLOG(INFO) << "Could not submit the present operation.";
581  return false;
582  }
583 
584  return true;
585 }
#define VK_CALL_LOG_ERROR(expression)
#define FML_DLOG(severity)
Definition: logging.h:85

The documentation for this class was generated from the following files: