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