Flutter Engine
vulkan_swapchain.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 
5 #include "vulkan_swapchain.h"
6 
7 #include "third_party/skia/include/gpu/GrBackendSurface.h"
8 #include "third_party/skia/include/gpu/GrDirectContext.h"
9 #include "third_party/skia/include/gpu/vk/GrVkTypes.h"
10 #include "vulkan_backbuffer.h"
11 #include "vulkan_device.h"
12 #include "vulkan_image.h"
13 #include "vulkan_proc_table.h"
14 #include "vulkan_surface.h"
15 
16 namespace vulkan {
17 
18 namespace {
19 struct FormatInfo {
20  VkFormat format_;
21  SkColorType color_type_;
22  sk_sp<SkColorSpace> color_space_;
23 };
24 } // namespace
25 
26 static std::vector<FormatInfo> DesiredFormatInfos() {
27  return {{VK_FORMAT_R8G8B8A8_SRGB, kRGBA_8888_SkColorType,
28  SkColorSpace::MakeSRGB()},
29  {VK_FORMAT_B8G8R8A8_SRGB, kRGBA_8888_SkColorType,
30  SkColorSpace::MakeSRGB()},
31  {VK_FORMAT_R16G16B16A16_SFLOAT, kRGBA_F16_SkColorType,
32  SkColorSpace::MakeSRGBLinear()},
33  {VK_FORMAT_R8G8B8A8_UNORM, kRGBA_8888_SkColorType,
34  SkColorSpace::MakeSRGB()},
35  {VK_FORMAT_B8G8R8A8_UNORM, kRGBA_8888_SkColorType,
36  SkColorSpace::MakeSRGB()}};
37 }
38 
40  const VulkanDevice& device,
41  const VulkanSurface& surface,
42  GrDirectContext* skia_context,
43  std::unique_ptr<VulkanSwapchain> old_swapchain,
44  uint32_t queue_family_index)
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(skia_context,
157  format_infos[format_index].color_type_,
158  format_infos[format_index].color_space_,
159  usage_flags)) {
160  FML_DLOG(INFO) << "Could not create swapchain images.";
161  return;
162  }
163 
164  valid_ = true;
165 }
166 
168 
170  return valid_;
171 }
172 
173 std::vector<VkImage> VulkanSwapchain::GetImages() const {
174  uint32_t count = 0;
175  if (VK_CALL_LOG_ERROR(vk.GetSwapchainImagesKHR(
176  device_.GetHandle(), swapchain_, &count, nullptr)) != VK_SUCCESS) {
177  return {};
178  }
179 
180  if (count == 0) {
181  return {};
182  }
183 
184  std::vector<VkImage> images;
185 
186  images.resize(count);
187 
188  if (VK_CALL_LOG_ERROR(vk.GetSwapchainImagesKHR(
189  device_.GetHandle(), swapchain_, &count, images.data())) !=
190  VK_SUCCESS) {
191  return {};
192  }
193 
194  return images;
195 }
196 
197 SkISize VulkanSwapchain::GetSize() const {
198  VkExtent2D extents = capabilities_.currentExtent;
199 
200  if (extents.width < capabilities_.minImageExtent.width) {
201  extents.width = capabilities_.minImageExtent.width;
202  } else if (extents.width > capabilities_.maxImageExtent.width) {
203  extents.width = capabilities_.maxImageExtent.width;
204  }
205 
206  if (extents.height < capabilities_.minImageExtent.height) {
207  extents.height = capabilities_.minImageExtent.height;
208  } else if (extents.height > capabilities_.maxImageExtent.height) {
209  extents.height = capabilities_.maxImageExtent.height;
210  }
211 
212  return SkISize::Make(extents.width, extents.height);
213 }
214 
215 sk_sp<SkSurface> VulkanSwapchain::CreateSkiaSurface(
216  GrDirectContext* gr_context,
217  VkImage image,
218  VkImageUsageFlags usage_flags,
219  const SkISize& size,
220  SkColorType color_type,
221  sk_sp<SkColorSpace> color_space) const {
222  if (gr_context == nullptr) {
223  return nullptr;
224  }
225 
226  if (color_type == kUnknown_SkColorType) {
227  // Unexpected Vulkan format.
228  return nullptr;
229  }
230 
231  GrVkImageInfo image_info;
232  image_info.fImage = image;
233  image_info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
234  image_info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
235  image_info.fFormat = surface_format_.format;
236  image_info.fImageUsageFlags = usage_flags;
237  image_info.fSampleCount = 1;
238  image_info.fLevelCount = 1;
239 
240  // TODO(chinmaygarde): Setup the stencil buffer and the sampleCnt.
241  GrBackendRenderTarget backend_render_target(size.fWidth, size.fHeight, 0,
242  image_info);
243  SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
244 
245  return SkSurface::MakeFromBackendRenderTarget(
246  gr_context, // context
247  backend_render_target, // backend render target
248  kTopLeft_GrSurfaceOrigin, // origin
249  color_type, // color type
250  std::move(color_space), // color space
251  &props // surface properties
252  );
253 }
254 
255 bool VulkanSwapchain::CreateSwapchainImages(GrDirectContext* skia_context,
256  SkColorType color_type,
257  sk_sp<SkColorSpace> color_space,
258  VkImageUsageFlags usage_flags) {
259  std::vector<VkImage> images = GetImages();
260 
261  if (images.size() == 0) {
262  return false;
263  }
264 
265  const SkISize surface_size = GetSize();
266 
267  for (const VkImage& image : images) {
268  // Populate the backbuffer.
269  auto backbuffer = std::make_unique<VulkanBackbuffer>(
270  vk, device_.GetHandle(), device_.GetCommandPool());
271 
272  if (!backbuffer->IsValid()) {
273  return false;
274  }
275 
276  backbuffers_.emplace_back(std::move(backbuffer));
277 
278  // Populate the image.
279  auto vulkan_image = std::make_unique<VulkanImage>(image);
280 
281  if (!vulkan_image->IsValid()) {
282  return false;
283  }
284 
285  images_.emplace_back(std::move(vulkan_image));
286 
287  // Populate the surface.
288  auto surface = CreateSkiaSurface(skia_context, image, usage_flags,
289  surface_size, color_type, color_space);
290 
291  if (surface == nullptr) {
292  return false;
293  }
294 
295  surfaces_.emplace_back(std::move(surface));
296  }
297 
298  FML_DCHECK(backbuffers_.size() == images_.size());
299  FML_DCHECK(images_.size() == surfaces_.size());
300 
301  return true;
302 }
303 
304 VulkanBackbuffer* VulkanSwapchain::GetNextBackbuffer() {
305  auto available_backbuffers = backbuffers_.size();
306 
307  if (available_backbuffers == 0) {
308  return nullptr;
309  }
310 
311  auto next_backbuffer_index =
312  (current_backbuffer_index_ + 1) % backbuffers_.size();
313 
314  auto& backbuffer = backbuffers_[next_backbuffer_index];
315 
316  if (!backbuffer->IsValid()) {
317  return nullptr;
318  }
319 
320  current_backbuffer_index_ = next_backbuffer_index;
321  return backbuffer.get();
322 }
323 
326 
327  if (!IsValid()) {
328  FML_DLOG(INFO) << "Swapchain was invalid.";
329  return error;
330  }
331 
332  // ---------------------------------------------------------------------------
333  // Step 0:
334  // Acquire the next available backbuffer.
335  // ---------------------------------------------------------------------------
336  auto backbuffer = GetNextBackbuffer();
337 
338  if (backbuffer == nullptr) {
339  FML_DLOG(INFO) << "Could not get the next backbuffer.";
340  return error;
341  }
342 
343  // ---------------------------------------------------------------------------
344  // Step 1:
345  // Wait for use readiness.
346  // ---------------------------------------------------------------------------
347  if (!backbuffer->WaitFences()) {
348  FML_DLOG(INFO) << "Failed waiting on fences.";
349  return error;
350  }
351 
352  // ---------------------------------------------------------------------------
353  // Step 2:
354  // Put semaphores in unsignaled state.
355  // ---------------------------------------------------------------------------
356  if (!backbuffer->ResetFences()) {
357  FML_DLOG(INFO) << "Could not reset fences.";
358  return error;
359  }
360 
361  // ---------------------------------------------------------------------------
362  // Step 3:
363  // Acquire the next image index.
364  // ---------------------------------------------------------------------------
365  uint32_t next_image_index = 0;
366 
367  VkResult acquire_result = VK_CALL_LOG_ERROR(
368  vk.AcquireNextImageKHR(device_.GetHandle(), //
369  swapchain_, //
370  std::numeric_limits<uint64_t>::max(), //
371  backbuffer->GetUsageSemaphore(), //
372  VK_NULL_HANDLE, //
373  &next_image_index));
374 
375  switch (acquire_result) {
376  case VK_SUCCESS:
377  break;
378  case VK_ERROR_OUT_OF_DATE_KHR:
379  return {AcquireStatus::ErrorSurfaceOutOfDate, nullptr};
380  case VK_ERROR_SURFACE_LOST_KHR:
381  return {AcquireStatus::ErrorSurfaceLost, nullptr};
382  default:
383  FML_LOG(INFO) << "Unexpected result from AcquireNextImageKHR: "
384  << acquire_result;
385  return {AcquireStatus::ErrorSurfaceLost, nullptr};
386  }
387 
388  // Simple sanity checking of image index.
389  if (next_image_index >= images_.size()) {
390  FML_DLOG(INFO) << "Image index returned was out-of-bounds.";
391  return error;
392  }
393 
394  auto& image = images_[next_image_index];
395  if (!image->IsValid()) {
396  FML_DLOG(INFO) << "Image at index was invalid.";
397  return error;
398  }
399 
400  // ---------------------------------------------------------------------------
401  // Step 4:
402  // Start recording to the command buffer.
403  // ---------------------------------------------------------------------------
404  if (!backbuffer->GetUsageCommandBuffer().Begin()) {
405  FML_DLOG(INFO) << "Could not begin recording to the command buffer.";
406  return error;
407  }
408 
409  // ---------------------------------------------------------------------------
410  // Step 5:
411  // Set image layout to color attachment mode.
412  // ---------------------------------------------------------------------------
413  VkPipelineStageFlagBits destination_pipeline_stage =
414  VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
415  VkImageLayout destination_image_layout =
416  VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
417 
418  if (!image->InsertImageMemoryBarrier(
419  backbuffer->GetUsageCommandBuffer(), // command buffer
420  current_pipeline_stage_, // src_pipeline_bits
421  destination_pipeline_stage, // dest_pipeline_bits
422  VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // dest_access_flags
423  destination_image_layout // dest_layout
424  )) {
425  FML_DLOG(INFO) << "Could not insert image memory barrier.";
426  return error;
427  } else {
428  current_pipeline_stage_ = destination_pipeline_stage;
429  }
430 
431  // ---------------------------------------------------------------------------
432  // Step 6:
433  // End recording to the command buffer.
434  // ---------------------------------------------------------------------------
435  if (!backbuffer->GetUsageCommandBuffer().End()) {
436  FML_DLOG(INFO) << "Could not end recording to the command buffer.";
437  return error;
438  }
439 
440  // ---------------------------------------------------------------------------
441  // Step 7:
442  // Submit the command buffer to the device queue.
443  // ---------------------------------------------------------------------------
444  std::vector<VkSemaphore> wait_semaphores = {backbuffer->GetUsageSemaphore()};
445  std::vector<VkSemaphore> signal_semaphores = {};
446  std::vector<VkCommandBuffer> command_buffers = {
447  backbuffer->GetUsageCommandBuffer().Handle()};
448 
449  if (!device_.QueueSubmit(
450  {destination_pipeline_stage}, // wait_dest_pipeline_stages
451  wait_semaphores, // wait_semaphores
452  signal_semaphores, // signal_semaphores
453  command_buffers, // command_buffers
454  backbuffer->GetUsageFence() // fence
455  )) {
456  FML_DLOG(INFO) << "Could not submit to the device queue.";
457  return error;
458  }
459 
460  // ---------------------------------------------------------------------------
461  // Step 8:
462  // Tell Skia about the updated image layout.
463  // ---------------------------------------------------------------------------
464  sk_sp<SkSurface> surface = surfaces_[next_image_index];
465 
466  if (surface == nullptr) {
467  FML_DLOG(INFO) << "Could not access surface at the image index.";
468  return error;
469  }
470 
471  GrBackendRenderTarget backendRT = surface->getBackendRenderTarget(
472  SkSurface::kFlushRead_BackendHandleAccess);
473  if (!backendRT.isValid()) {
474  FML_DLOG(INFO) << "Could not get backend render target.";
475  return error;
476  }
477  backendRT.setVkImageLayout(destination_image_layout);
478 
479  current_image_index_ = next_image_index;
480 
481  return {AcquireStatus::Success, surface};
482 }
483 
485  if (!IsValid()) {
486  FML_DLOG(INFO) << "Swapchain was invalid.";
487  return false;
488  }
489 
490  sk_sp<SkSurface> surface = surfaces_[current_image_index_];
491  const std::unique_ptr<VulkanImage>& image = images_[current_image_index_];
492  auto backbuffer = backbuffers_[current_backbuffer_index_].get();
493 
494  // ---------------------------------------------------------------------------
495  // Step 0:
496  // Make sure Skia has flushed all work for the surface to the gpu.
497  // ---------------------------------------------------------------------------
498  surface->flushAndSubmit();
499 
500  // ---------------------------------------------------------------------------
501  // Step 1:
502  // Start recording to the command buffer.
503  // ---------------------------------------------------------------------------
504  if (!backbuffer->GetRenderCommandBuffer().Begin()) {
505  FML_DLOG(INFO) << "Could not start recording to the command buffer.";
506  return false;
507  }
508 
509  // ---------------------------------------------------------------------------
510  // Step 2:
511  // Set image layout to present mode.
512  // ---------------------------------------------------------------------------
513  VkPipelineStageFlagBits destination_pipeline_stage =
514  VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
515  VkImageLayout destination_image_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
516 
517  if (!image->InsertImageMemoryBarrier(
518  backbuffer->GetRenderCommandBuffer(), // command buffer
519  current_pipeline_stage_, // src_pipeline_bits
520  destination_pipeline_stage, // dest_pipeline_bits
521  VK_ACCESS_MEMORY_READ_BIT, // dest_access_flags
522  destination_image_layout // dest_layout
523  )) {
524  FML_DLOG(INFO) << "Could not insert memory barrier.";
525  return false;
526  } else {
527  current_pipeline_stage_ = destination_pipeline_stage;
528  }
529 
530  // ---------------------------------------------------------------------------
531  // Step 3:
532  // End recording to the command buffer.
533  // ---------------------------------------------------------------------------
534  if (!backbuffer->GetRenderCommandBuffer().End()) {
535  FML_DLOG(INFO) << "Could not end recording to the command buffer.";
536  return false;
537  }
538 
539  // ---------------------------------------------------------------------------
540  // Step 4:
541  // Submit the command buffer to the device queue. Tell it to signal the render
542  // semaphore.
543  // ---------------------------------------------------------------------------
544  std::vector<VkSemaphore> wait_semaphores = {};
545  std::vector<VkSemaphore> queue_signal_semaphores = {
546  backbuffer->GetRenderSemaphore()};
547  std::vector<VkCommandBuffer> command_buffers = {
548  backbuffer->GetRenderCommandBuffer().Handle()};
549 
550  if (!device_.QueueSubmit(
551  {/* Empty. No wait semaphores. */}, // wait_dest_pipeline_stages
552  wait_semaphores, // wait_semaphores
553  queue_signal_semaphores, // signal_semaphores
554  command_buffers, // command_buffers
555  backbuffer->GetRenderFence() // fence
556  )) {
557  FML_DLOG(INFO) << "Could not submit to the device queue.";
558  return false;
559  }
560 
561  // ---------------------------------------------------------------------------
562  // Step 5:
563  // Submit the present operation and wait on the render semaphore.
564  // ---------------------------------------------------------------------------
565  VkSwapchainKHR swapchain = swapchain_;
566  uint32_t present_image_index = static_cast<uint32_t>(current_image_index_);
567  const VkPresentInfoKHR present_info = {
568  .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
569  .pNext = nullptr,
570  .waitSemaphoreCount =
571  static_cast<uint32_t>(queue_signal_semaphores.size()),
572  .pWaitSemaphores = queue_signal_semaphores.data(),
573  .swapchainCount = 1,
574  .pSwapchains = &swapchain,
575  .pImageIndices = &present_image_index,
576  .pResults = nullptr,
577  };
578 
579  if (VK_CALL_LOG_ERROR(vk.QueuePresentKHR(device_.GetQueueHandle(),
580  &present_info)) != VK_SUCCESS) {
581  FML_DLOG(INFO) << "Could not submit the present operation.";
582  return false;
583  }
584 
585  return true;
586 }
587 
588 } // namespace vulkan
#define FML_ALLOW_UNUSED_LOCAL(x)
#define FML_DCHECK(condition)
Definition: logging.h:86
FlMethodResponse GError ** error
uint32_t color_type
const VulkanHandle< VkSurfaceKHR > & Handle() const
sk_sp< SkColorSpace > color_space_
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
#define FML_LOG(severity)
Definition: logging.h:65
std::pair< AcquireStatus, sk_sp< SkSurface > > AcquireResult
VkFormat format_
VulkanSwapchain(const VulkanProcTable &vk, const VulkanDevice &device, const VulkanSurface &surface, GrDirectContext *skia_context, std::unique_ptr< VulkanSwapchain > old_swapchain, uint32_t queue_family_index)
#define VK_CALL_LOG_ERROR(expression)
SkColorType color_type_
AcquireResult AcquireSurface()
#define FML_DLOG(severity)
Definition: logging.h:85
static std::vector< FormatInfo > DesiredFormatInfos()
A valid SkSurface was acquired successfully from the swapchain.