• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "flutter/vulkan/vulkan_swapchain.h"
6 
7 #include "flutter/vulkan/vulkan_backbuffer.h"
8 #include "flutter/vulkan/vulkan_device.h"
9 #include "flutter/vulkan/vulkan_image.h"
10 #include "flutter/vulkan/vulkan_proc_table.h"
11 #include "flutter/vulkan/vulkan_surface.h"
12 #include "third_party/skia/include/gpu/GrBackendSurface.h"
13 #include "third_party/skia/include/gpu/GrContext.h"
14 #include "third_party/skia/include/gpu/vk/GrVkTypes.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 
DesiredFormatInfos()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 
VulkanSwapchain(const VulkanProcTable & p_vk,const VulkanDevice & device,const VulkanSurface & surface,GrContext * skia_context,std::unique_ptr<VulkanSwapchain> old_swapchain,uint32_t queue_family_index)39 VulkanSwapchain::VulkanSwapchain(const VulkanProcTable& p_vk,
40                                  const VulkanDevice& device,
41                                  const VulkanSurface& surface,
42                                  GrContext* 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   const VkSwapchainCreateInfoKHR create_info = {
117       .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
118       .pNext = nullptr,
119       .flags = 0,
120       .surface = surface_handle,
121       .minImageCount = capabilities_.minImageCount,
122       .imageFormat = surface_format_.format,
123       .imageColorSpace = surface_format_.colorSpace,
124       .imageExtent = capabilities_.currentExtent,
125       .imageArrayLayers = 1,
126       .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
127       .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
128       .queueFamilyIndexCount = 0,  // Because of the exclusive sharing mode.
129       .pQueueFamilyIndices = nullptr,
130       .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
131       .compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
132       .presentMode = present_mode,
133       .clipped = VK_FALSE,
134       .oldSwapchain = old_swapchain_handle,
135   };
136 
137   VkSwapchainKHR swapchain = VK_NULL_HANDLE;
138 
139   if (VK_CALL_LOG_ERROR(vk.CreateSwapchainKHR(device_.GetHandle(), &create_info,
140                                               nullptr, &swapchain)) !=
141       VK_SUCCESS) {
142     FML_DLOG(INFO) << "Could not create the swapchain.";
143     return;
144   }
145 
146   swapchain_ = {swapchain, [this](VkSwapchainKHR swapchain) {
147                   FML_ALLOW_UNUSED_LOCAL(device_.WaitIdle());
148                   vk.DestroySwapchainKHR(device_.GetHandle(), swapchain,
149                                          nullptr);
150                 }};
151 
152   if (!CreateSwapchainImages(skia_context,
153                              format_infos[format_index].color_type_,
154                              format_infos[format_index].color_space_)) {
155     FML_DLOG(INFO) << "Could not create swapchain images.";
156     return;
157   }
158 
159   valid_ = true;
160 }
161 
162 VulkanSwapchain::~VulkanSwapchain() = default;
163 
IsValid() const164 bool VulkanSwapchain::IsValid() const {
165   return valid_;
166 }
167 
GetImages() const168 std::vector<VkImage> VulkanSwapchain::GetImages() const {
169   uint32_t count = 0;
170   if (VK_CALL_LOG_ERROR(vk.GetSwapchainImagesKHR(
171           device_.GetHandle(), swapchain_, &count, nullptr)) != VK_SUCCESS) {
172     return {};
173   }
174 
175   if (count == 0) {
176     return {};
177   }
178 
179   std::vector<VkImage> images;
180 
181   images.resize(count);
182 
183   if (VK_CALL_LOG_ERROR(vk.GetSwapchainImagesKHR(
184           device_.GetHandle(), swapchain_, &count, images.data())) !=
185       VK_SUCCESS) {
186     return {};
187   }
188 
189   return images;
190 }
191 
GetSize() const192 SkISize VulkanSwapchain::GetSize() const {
193   VkExtent2D extents = capabilities_.currentExtent;
194 
195   if (extents.width < capabilities_.minImageExtent.width) {
196     extents.width = capabilities_.minImageExtent.width;
197   } else if (extents.width > capabilities_.maxImageExtent.width) {
198     extents.width = capabilities_.maxImageExtent.width;
199   }
200 
201   if (extents.height < capabilities_.minImageExtent.height) {
202     extents.height = capabilities_.minImageExtent.height;
203   } else if (extents.height > capabilities_.maxImageExtent.height) {
204     extents.height = capabilities_.maxImageExtent.height;
205   }
206 
207   return SkISize::Make(extents.width, extents.height);
208 }
209 
CreateSkiaSurface(GrContext * gr_context,VkImage image,const SkISize & size,SkColorType color_type,sk_sp<SkColorSpace> color_space) const210 sk_sp<SkSurface> VulkanSwapchain::CreateSkiaSurface(
211     GrContext* gr_context,
212     VkImage image,
213     const SkISize& size,
214     SkColorType color_type,
215     sk_sp<SkColorSpace> color_space) const {
216   if (gr_context == nullptr) {
217     return nullptr;
218   }
219 
220   if (color_type == kUnknown_SkColorType) {
221     // Unexpected Vulkan format.
222     return nullptr;
223   }
224 
225   const GrVkImageInfo image_info = {
226       image,                      // image
227       GrVkAlloc(),                // alloc
228       VK_IMAGE_TILING_OPTIMAL,    // tiling
229       VK_IMAGE_LAYOUT_UNDEFINED,  // layout
230       surface_format_.format,     // format
231       1,                          // level count
232   };
233 
234   // TODO(chinmaygarde): Setup the stencil buffer and the sampleCnt.
235   GrBackendRenderTarget backend_render_target(size.fWidth, size.fHeight, 0,
236                                               image_info);
237   SkSurfaceProps props(SkSurfaceProps::InitType::kLegacyFontHost_InitType);
238 
239   return SkSurface::MakeFromBackendRenderTarget(
240       gr_context,                // context
241       backend_render_target,     // backend render target
242       kTopLeft_GrSurfaceOrigin,  // origin
243       color_type,                // color type
244       std::move(color_space),    // color space
245       &props                     // surface properties
246   );
247 }
248 
CreateSwapchainImages(GrContext * skia_context,SkColorType color_type,sk_sp<SkColorSpace> color_space)249 bool VulkanSwapchain::CreateSwapchainImages(GrContext* skia_context,
250                                             SkColorType color_type,
251                                             sk_sp<SkColorSpace> color_space) {
252   std::vector<VkImage> images = GetImages();
253 
254   if (images.size() == 0) {
255     return false;
256   }
257 
258   const SkISize surface_size = GetSize();
259 
260   for (const VkImage& image : images) {
261     // Populate the backbuffer.
262     auto backbuffer = std::make_unique<VulkanBackbuffer>(
263         vk, device_.GetHandle(), device_.GetCommandPool());
264 
265     if (!backbuffer->IsValid()) {
266       return false;
267     }
268 
269     backbuffers_.emplace_back(std::move(backbuffer));
270 
271     // Populate the image.
272     auto vulkan_image = std::make_unique<VulkanImage>(image);
273 
274     if (!vulkan_image->IsValid()) {
275       return false;
276     }
277 
278     images_.emplace_back(std::move(vulkan_image));
279 
280     // Populate the surface.
281     auto surface = CreateSkiaSurface(skia_context, image, surface_size,
282                                      color_type, color_space);
283 
284     if (surface == nullptr) {
285       return false;
286     }
287 
288     surfaces_.emplace_back(std::move(surface));
289   }
290 
291   FML_DCHECK(backbuffers_.size() == images_.size());
292   FML_DCHECK(images_.size() == surfaces_.size());
293 
294   return true;
295 }
296 
GetNextBackbuffer()297 VulkanBackbuffer* VulkanSwapchain::GetNextBackbuffer() {
298   auto available_backbuffers = backbuffers_.size();
299 
300   if (available_backbuffers == 0) {
301     return nullptr;
302   }
303 
304   auto next_backbuffer_index =
305       (current_backbuffer_index_ + 1) % backbuffers_.size();
306 
307   auto& backbuffer = backbuffers_[next_backbuffer_index];
308 
309   if (!backbuffer->IsValid()) {
310     return nullptr;
311   }
312 
313   current_backbuffer_index_ = next_backbuffer_index;
314   return backbuffer.get();
315 }
316 
AcquireSurface()317 VulkanSwapchain::AcquireResult VulkanSwapchain::AcquireSurface() {
318   AcquireResult error = {AcquireStatus::ErrorSurfaceLost, nullptr};
319 
320   if (!IsValid()) {
321     FML_DLOG(INFO) << "Swapchain was invalid.";
322     return error;
323   }
324 
325   // ---------------------------------------------------------------------------
326   // Step 0:
327   // Acquire the next available backbuffer.
328   // ---------------------------------------------------------------------------
329   auto backbuffer = GetNextBackbuffer();
330 
331   if (backbuffer == nullptr) {
332     FML_DLOG(INFO) << "Could not get the next backbuffer.";
333     return error;
334   }
335 
336   // ---------------------------------------------------------------------------
337   // Step 1:
338   // Wait for use readiness.
339   // ---------------------------------------------------------------------------
340   if (!backbuffer->WaitFences()) {
341     FML_DLOG(INFO) << "Failed waiting on fences.";
342     return error;
343   }
344 
345   // ---------------------------------------------------------------------------
346   // Step 2:
347   // Put semaphores in unsignaled state.
348   // ---------------------------------------------------------------------------
349   if (!backbuffer->ResetFences()) {
350     FML_DLOG(INFO) << "Could not reset fences.";
351     return error;
352   }
353 
354   // ---------------------------------------------------------------------------
355   // Step 3:
356   // Acquire the next image index.
357   // ---------------------------------------------------------------------------
358   uint32_t next_image_index = 0;
359 
360   VkResult acquire_result = VK_CALL_LOG_ERROR(
361       vk.AcquireNextImageKHR(device_.GetHandle(),                   //
362                              swapchain_,                            //
363                              std::numeric_limits<uint64_t>::max(),  //
364                              backbuffer->GetUsageSemaphore(),       //
365                              VK_NULL_HANDLE,                        //
366                              &next_image_index));
367 
368   switch (acquire_result) {
369     case VK_SUCCESS:
370       break;
371     case VK_ERROR_OUT_OF_DATE_KHR:
372       return {AcquireStatus::ErrorSurfaceOutOfDate, nullptr};
373     case VK_ERROR_SURFACE_LOST_KHR:
374       return {AcquireStatus::ErrorSurfaceLost, nullptr};
375     default:
376       FML_LOG(INFO) << "Unexpected result from AcquireNextImageKHR: "
377                     << acquire_result;
378       return {AcquireStatus::ErrorSurfaceLost, nullptr};
379   }
380 
381   // Simple sanity checking of image index.
382   if (next_image_index >= images_.size()) {
383     FML_DLOG(INFO) << "Image index returned was out-of-bounds.";
384     return error;
385   }
386 
387   auto& image = images_[next_image_index];
388   if (!image->IsValid()) {
389     FML_DLOG(INFO) << "Image at index was invalid.";
390     return error;
391   }
392 
393   // ---------------------------------------------------------------------------
394   // Step 4:
395   // Start recording to the command buffer.
396   // ---------------------------------------------------------------------------
397   if (!backbuffer->GetUsageCommandBuffer().Begin()) {
398     FML_DLOG(INFO) << "Could not begin recording to the command buffer.";
399     return error;
400   }
401 
402   // ---------------------------------------------------------------------------
403   // Step 5:
404   // Set image layout to color attachment mode.
405   // ---------------------------------------------------------------------------
406   VkPipelineStageFlagBits destination_pipeline_stage =
407       VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
408   VkImageLayout destination_image_layout =
409       VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
410 
411   if (!image->InsertImageMemoryBarrier(
412           backbuffer->GetUsageCommandBuffer(),   // command buffer
413           current_pipeline_stage_,               // src_pipeline_bits
414           destination_pipeline_stage,            // dest_pipeline_bits
415           VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,  // dest_access_flags
416           destination_image_layout               // dest_layout
417           )) {
418     FML_DLOG(INFO) << "Could not insert image memory barrier.";
419     return error;
420   } else {
421     current_pipeline_stage_ = destination_pipeline_stage;
422   }
423 
424   // ---------------------------------------------------------------------------
425   // Step 6:
426   // End recording to the command buffer.
427   // ---------------------------------------------------------------------------
428   if (!backbuffer->GetUsageCommandBuffer().End()) {
429     FML_DLOG(INFO) << "Could not end recording to the command buffer.";
430     return error;
431   }
432 
433   // ---------------------------------------------------------------------------
434   // Step 7:
435   // Submit the command buffer to the device queue.
436   // ---------------------------------------------------------------------------
437   std::vector<VkSemaphore> wait_semaphores = {backbuffer->GetUsageSemaphore()};
438   std::vector<VkSemaphore> signal_semaphores = {};
439   std::vector<VkCommandBuffer> command_buffers = {
440       backbuffer->GetUsageCommandBuffer().Handle()};
441 
442   if (!device_.QueueSubmit(
443           {destination_pipeline_stage},  // wait_dest_pipeline_stages
444           wait_semaphores,               // wait_semaphores
445           signal_semaphores,             // signal_semaphores
446           command_buffers,               // command_buffers
447           backbuffer->GetUsageFence()    // fence
448           )) {
449     FML_DLOG(INFO) << "Could not submit to the device queue.";
450     return error;
451   }
452 
453   // ---------------------------------------------------------------------------
454   // Step 8:
455   // Tell Skia about the updated image layout.
456   // ---------------------------------------------------------------------------
457   sk_sp<SkSurface> surface = surfaces_[next_image_index];
458 
459   if (surface == nullptr) {
460     FML_DLOG(INFO) << "Could not access surface at the image index.";
461     return error;
462   }
463 
464   GrBackendRenderTarget backendRT = surface->getBackendRenderTarget(
465       SkSurface::kFlushRead_BackendHandleAccess);
466   if (!backendRT.isValid()) {
467     FML_DLOG(INFO) << "Could not get backend render target.";
468     return error;
469   }
470   backendRT.setVkImageLayout(destination_image_layout);
471 
472   current_image_index_ = next_image_index;
473 
474   return {AcquireStatus::Success, surface};
475 }
476 
Submit()477 bool VulkanSwapchain::Submit() {
478   if (!IsValid()) {
479     FML_DLOG(INFO) << "Swapchain was invalid.";
480     return false;
481   }
482 
483   sk_sp<SkSurface> surface = surfaces_[current_image_index_];
484   const std::unique_ptr<VulkanImage>& image = images_[current_image_index_];
485   auto backbuffer = backbuffers_[current_backbuffer_index_].get();
486 
487   // ---------------------------------------------------------------------------
488   // Step 0:
489   // Make sure Skia has flushed all work for the surface to the gpu.
490   // ---------------------------------------------------------------------------
491   surface->flush();
492 
493   // ---------------------------------------------------------------------------
494   // Step 1:
495   // Start recording to the command buffer.
496   // ---------------------------------------------------------------------------
497   if (!backbuffer->GetRenderCommandBuffer().Begin()) {
498     FML_DLOG(INFO) << "Could not start recording to the command buffer.";
499     return false;
500   }
501 
502   // ---------------------------------------------------------------------------
503   // Step 2:
504   // Set image layout to present mode.
505   // ---------------------------------------------------------------------------
506   VkPipelineStageFlagBits destination_pipeline_stage =
507       VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
508   VkImageLayout destination_image_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
509 
510   if (!image->InsertImageMemoryBarrier(
511           backbuffer->GetRenderCommandBuffer(),  // command buffer
512           current_pipeline_stage_,               // src_pipeline_bits
513           destination_pipeline_stage,            // dest_pipeline_bits
514           VK_ACCESS_MEMORY_READ_BIT,             // dest_access_flags
515           destination_image_layout               // dest_layout
516           )) {
517     FML_DLOG(INFO) << "Could not insert memory barrier.";
518     return false;
519   } else {
520     current_pipeline_stage_ = destination_pipeline_stage;
521   }
522 
523   // ---------------------------------------------------------------------------
524   // Step 3:
525   // End recording to the command buffer.
526   // ---------------------------------------------------------------------------
527   if (!backbuffer->GetRenderCommandBuffer().End()) {
528     FML_DLOG(INFO) << "Could not end recording to the command buffer.";
529     return false;
530   }
531 
532   // ---------------------------------------------------------------------------
533   // Step 4:
534   // Submit the command buffer to the device queue. Tell it to signal the render
535   // semaphore.
536   // ---------------------------------------------------------------------------
537   std::vector<VkSemaphore> wait_semaphores = {};
538   std::vector<VkSemaphore> queue_signal_semaphores = {
539       backbuffer->GetRenderSemaphore()};
540   std::vector<VkCommandBuffer> command_buffers = {
541       backbuffer->GetRenderCommandBuffer().Handle()};
542 
543   if (!device_.QueueSubmit(
544           {/* Empty. No wait semaphores. */},  // wait_dest_pipeline_stages
545           wait_semaphores,                     // wait_semaphores
546           queue_signal_semaphores,             // signal_semaphores
547           command_buffers,                     // command_buffers
548           backbuffer->GetRenderFence()         // fence
549           )) {
550     FML_DLOG(INFO) << "Could not submit to the device queue.";
551     return false;
552   }
553 
554   // ---------------------------------------------------------------------------
555   // Step 5:
556   // Submit the present operation and wait on the render semaphore.
557   // ---------------------------------------------------------------------------
558   VkSwapchainKHR swapchain = swapchain_;
559   uint32_t present_image_index = static_cast<uint32_t>(current_image_index_);
560   const VkPresentInfoKHR present_info = {
561       .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
562       .pNext = nullptr,
563       .waitSemaphoreCount =
564           static_cast<uint32_t>(queue_signal_semaphores.size()),
565       .pWaitSemaphores = queue_signal_semaphores.data(),
566       .swapchainCount = 1,
567       .pSwapchains = &swapchain,
568       .pImageIndices = &present_image_index,
569       .pResults = nullptr,
570   };
571 
572   if (VK_CALL_LOG_ERROR(vk.QueuePresentKHR(device_.GetQueueHandle(),
573                                            &present_info)) != VK_SUCCESS) {
574     FML_DLOG(INFO) << "Could not submit the present operation.";
575     return false;
576   }
577 
578   return true;
579 }
580 
581 }  // namespace vulkan
582