Flutter Engine
vulkan_window.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 // FLUTTER_NOLINT: https://github.com/flutter/flutter/issues/68331
6 
7 #include "vulkan_window.h"
8 
9 #include <memory>
10 #include <string>
11 
12 #include "third_party/skia/include/gpu/GrDirectContext.h"
13 #include "vulkan_application.h"
14 #include "vulkan_device.h"
15 #include "vulkan_native_surface.h"
16 #include "vulkan_surface.h"
17 #include "vulkan_swapchain.h"
18 
19 namespace vulkan {
20 
22  std::unique_ptr<VulkanNativeSurface> native_surface,
23  bool render_to_surface)
24  : valid_(false), vk(std::move(proc_table)) {
25  if (!vk || !vk->HasAcquiredMandatoryProcAddresses()) {
26  FML_DLOG(INFO) << "Proc table has not acquired mandatory proc addresses.";
27  return;
28  }
29 
30  if (native_surface == nullptr || !native_surface->IsValid()) {
31  FML_DLOG(INFO) << "Native surface is invalid.";
32  return;
33  }
34 
35  // Create the application instance.
36 
37  std::vector<std::string> extensions = {
38  VK_KHR_SURFACE_EXTENSION_NAME, // parent extension
39  native_surface->GetExtensionName() // child extension
40  };
41 
42  application_ = std::make_unique<VulkanApplication>(*vk, "Flutter",
43  std::move(extensions));
44 
45  if (!application_->IsValid() || !vk->AreInstanceProcsSetup()) {
46  // Make certain the application instance was created and it setup the
47  // instance proc table entries.
48  FML_DLOG(INFO) << "Instance proc addresses have not been setup.";
49  return;
50  }
51 
52  // Create the device.
53 
54  logical_device_ = application_->AcquireFirstCompatibleLogicalDevice();
55 
56  if (logical_device_ == nullptr || !logical_device_->IsValid() ||
57  !vk->AreDeviceProcsSetup()) {
58  // Make certain the device was created and it setup the device proc table
59  // entries.
60  FML_DLOG(INFO) << "Device proc addresses have not been setup.";
61  return;
62  }
63 
64  // TODO(38466): Refactor GPU surface APIs take into account the fact that an
65  // external view embedder may want to render to the root surface.
66  if (!render_to_surface) {
67  return;
68  }
69 
70  // Create the logical surface from the native platform surface.
71  surface_ = std::make_unique<VulkanSurface>(*vk, *application_,
72  std::move(native_surface));
73 
74  if (!surface_->IsValid()) {
75  FML_DLOG(INFO) << "Vulkan surface is invalid.";
76  return;
77  }
78 
79  // Create the Skia GrDirectContext.
80 
81  if (!CreateSkiaGrContext()) {
82  FML_DLOG(INFO) << "Could not create Skia context.";
83  return;
84  }
85 
86  // Create the swapchain.
87 
88  if (!RecreateSwapchain()) {
89  FML_DLOG(INFO) << "Could not setup the swapchain initially.";
90  return;
91  }
92 
93  valid_ = true;
94 }
95 
96 VulkanWindow::~VulkanWindow() = default;
97 
98 bool VulkanWindow::IsValid() const {
99  return valid_;
100 }
101 
102 GrDirectContext* VulkanWindow::GetSkiaGrContext() {
103  return skia_gr_context_.get();
104 }
105 
106 bool VulkanWindow::CreateSkiaGrContext() {
107  GrVkBackendContext backend_context;
108 
109  if (!CreateSkiaBackendContext(&backend_context)) {
110  return false;
111  }
112 
113  sk_sp<GrDirectContext> context = GrDirectContext::MakeVulkan(backend_context);
114 
115  if (context == nullptr) {
116  return false;
117  }
118 
119  context->setResourceCacheLimits(kGrCacheMaxCount, kGrCacheMaxByteSize);
120 
121  skia_gr_context_ = context;
122 
123  return true;
124 }
125 
126 bool VulkanWindow::CreateSkiaBackendContext(GrVkBackendContext* context) {
127  auto getProc = vk->CreateSkiaGetProc();
128 
129  if (getProc == nullptr) {
130  return false;
131  }
132 
133  uint32_t skia_features = 0;
134  if (!logical_device_->GetPhysicalDeviceFeaturesSkia(&skia_features)) {
135  return false;
136  }
137 
138  context->fInstance = application_->GetInstance();
139  context->fPhysicalDevice = logical_device_->GetPhysicalDeviceHandle();
140  context->fDevice = logical_device_->GetHandle();
141  context->fQueue = logical_device_->GetQueueHandle();
142  context->fGraphicsQueueIndex = logical_device_->GetGraphicsQueueIndex();
143  context->fMinAPIVersion = application_->GetAPIVersion();
144  context->fExtensions = kKHR_surface_GrVkExtensionFlag |
145  kKHR_swapchain_GrVkExtensionFlag |
146  surface_->GetNativeSurface().GetSkiaExtensionName();
147  context->fFeatures = skia_features;
148  context->fGetProc = std::move(getProc);
149  context->fOwnsInstanceAndDevice = false;
150  return true;
151 }
152 
153 sk_sp<SkSurface> VulkanWindow::AcquireSurface() {
154  if (!IsValid()) {
155  FML_DLOG(INFO) << "Surface is invalid.";
156  return nullptr;
157  }
158 
159  auto surface_size = surface_->GetSize();
160 
161  // This check is theoretically unnecessary as the swapchain should report that
162  // the surface is out-of-date and perform swapchain recreation at the new
163  // configuration. However, on Android, the swapchain never reports that it is
164  // of date. Hence this extra check. Platforms that don't have this issue, or,
165  // cant report this information (which is optional anyway), report a zero
166  // size.
167  if (surface_size != SkISize::Make(0, 0) &&
168  surface_size != swapchain_->GetSize()) {
169  FML_DLOG(INFO) << "Swapchain and surface sizes are out of sync. Recreating "
170  "swapchain.";
171  if (!RecreateSwapchain()) {
172  FML_DLOG(INFO) << "Could not recreate swapchain.";
173  valid_ = false;
174  return nullptr;
175  }
176  }
177 
178  while (true) {
179  sk_sp<SkSurface> surface;
181 
182  std::tie(acquire_result, surface) = swapchain_->AcquireSurface();
183 
184  if (acquire_result == VulkanSwapchain::AcquireStatus::Success) {
185  // Successfully acquired a surface from the swapchain. Nothing more to do.
186  return surface;
187  }
188 
189  if (acquire_result == VulkanSwapchain::AcquireStatus::ErrorSurfaceLost) {
190  // Surface is lost. This is an unrecoverable error.
191  FML_DLOG(INFO) << "Swapchain reported surface was lost.";
192  return nullptr;
193  }
194 
195  if (acquire_result ==
197  // Surface out of date. Recreate the swapchain at the new configuration.
198  if (RecreateSwapchain()) {
199  // Swapchain was recreated, try surface acquisition again.
200  continue;
201  } else {
202  // Could not recreate the swapchain at the new configuration.
203  FML_DLOG(INFO) << "Swapchain reported surface was out of date but "
204  "could not recreate the swapchain at the new "
205  "configuration.";
206  valid_ = false;
207  return nullptr;
208  }
209  }
210 
211  break;
212  }
213 
214  FML_DCHECK(false) << "Unhandled VulkanSwapchain::AcquireResult";
215  return nullptr;
216 }
217 
219  if (!IsValid()) {
220  FML_DLOG(INFO) << "Window was invalid.";
221  return false;
222  }
223 
224  return swapchain_->Submit();
225 }
226 
227 bool VulkanWindow::RecreateSwapchain() {
228  // This way, we always lose our reference to the old swapchain. Even if we
229  // cannot create a new one to replace it.
230  auto old_swapchain = std::move(swapchain_);
231 
232  if (!vk->IsValid()) {
233  return false;
234  }
235 
236  if (logical_device_ == nullptr || !logical_device_->IsValid()) {
237  return false;
238  }
239 
240  if (surface_ == nullptr || !surface_->IsValid()) {
241  return false;
242  }
243 
244  if (skia_gr_context_ == nullptr) {
245  return false;
246  }
247 
248  auto swapchain = std::make_unique<VulkanSwapchain>(
249  *vk, *logical_device_, *surface_, skia_gr_context_.get(),
250  std::move(old_swapchain), logical_device_->GetGraphicsQueueIndex());
251 
252  if (!swapchain->IsValid()) {
253  return false;
254  }
255 
256  swapchain_ = std::move(swapchain);
257  return true;
258 }
259 
260 } // namespace vulkan
static const size_t kGrCacheMaxByteSize
#define FML_DCHECK(condition)
Definition: logging.h:86
Definition: ref_ptr.h:252
GrDirectContext * GetSkiaGrContext()
VulkanWindow(fml::RefPtr< VulkanProcTable > proc_table, std::unique_ptr< VulkanNativeSurface > native_surface, bool render_to_surface)
sk_sp< SkSurface > AcquireSurface()
static const int kGrCacheMaxCount
bool IsValid() const
#define FML_DLOG(severity)
Definition: logging.h:85
A valid SkSurface was acquired successfully from the swapchain.