Flutter Engine
The 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#include <utility>
12
13#include "flutter/flutter_vma/flutter_skia_vma.h"
14#include "flutter/vulkan/vulkan_skia_proc_table.h"
15#include "vulkan_application.h"
16#include "vulkan_device.h"
18#include "vulkan_surface.h"
19#include "vulkan_swapchain.h"
20
25
26namespace vulkan {
27
29 std::unique_ptr<VulkanNativeSurface> native_surface)
30 : VulkanWindow(/*context/*/ nullptr,
31 std::move(proc_table),
32 std::move(native_surface)) {}
33
34VulkanWindow::VulkanWindow(const sk_sp<GrDirectContext>& context,
36 std::unique_ptr<VulkanNativeSurface> native_surface)
37 : valid_(false), vk_(std::move(proc_table)), skia_gr_context_(context) {
38 if (!vk_ || !vk_->HasAcquiredMandatoryProcAddresses()) {
39 FML_DLOG(INFO) << "Proc table has not acquired mandatory proc addresses.";
40 return;
41 }
42
43 if (native_surface && !native_surface->IsValid()) {
44 FML_DLOG(INFO) << "Native surface is invalid.";
45 return;
46 }
47
48 // Create the application instance.
49
50 std::vector<std::string> extensions = {
51 VK_KHR_SURFACE_EXTENSION_NAME, // parent extension
52 native_surface->GetExtensionName() // child extension
53 };
54
55 application_ = std::make_unique<VulkanApplication>(*vk_, "Flutter",
56 std::move(extensions));
57
58 if (!application_->IsValid() || !vk_->AreInstanceProcsSetup()) {
59 // Make certain the application instance was created and it set up the
60 // instance proc table entries.
61 FML_DLOG(INFO) << "Instance proc addresses have not been set up.";
62 return;
63 }
64
65 // Create the device.
66
67 logical_device_ = application_->AcquireFirstCompatibleLogicalDevice();
68
69 if (logical_device_ == nullptr || !logical_device_->IsValid() ||
70 !vk_->AreDeviceProcsSetup()) {
71 // Make certain the device was created and it set up the device proc table
72 // entries.
73 FML_DLOG(INFO) << "Device proc addresses have not been set up.";
74 return;
75 }
76
77 if (!native_surface) {
78 return;
79 }
80
81 // Create the logical surface from the native platform surface.
82 surface_ = std::make_unique<VulkanSurface>(*vk_, *application_,
83 std::move(native_surface));
84
85 if (!surface_->IsValid()) {
86 FML_DLOG(INFO) << "Vulkan surface is invalid.";
87 return;
88 }
89
90 // Needs to happen before GrDirectContext is created.
92 application_->GetAPIVersion(), application_->GetInstance(),
93 logical_device_->GetPhysicalDeviceHandle(), logical_device_->GetHandle(),
94 vk_, true);
95
96 // Create the Skia GrDirectContext.
97
98 if (!skia_gr_context_ && !CreateSkiaGrContext()) {
99 FML_DLOG(INFO) << "Could not create Skia context.";
100 return;
101 }
102
103 // Create the swapchain.
104
105 if (!RecreateSwapchain()) {
106 FML_DLOG(INFO) << "Could not set up the swapchain initially.";
107 return;
108 }
109
110 valid_ = true;
111}
112
113VulkanWindow::~VulkanWindow() = default;
114
115bool VulkanWindow::IsValid() const {
116 return valid_;
117}
118
119GrDirectContext* VulkanWindow::GetSkiaGrContext() {
120 return skia_gr_context_.get();
121}
122
123bool VulkanWindow::CreateSkiaGrContext() {
124#ifdef SK_VULKAN
125 GrVkBackendContext backend_context;
128
129 if (!this->CreateSkiaBackendContext(&backend_context, &features,
130 &extensions)) {
131 return false;
132 }
133
135 options.fReduceOpsTaskSplitting = GrContextOptions::Enable::kNo;
136 sk_sp<GrDirectContext> context =
137 GrDirectContexts::MakeVulkan(backend_context, options);
138
139 if (context == nullptr) {
140 return false;
141 }
142
144
145 skia_gr_context_ = context;
146
147 return true;
148#else
149 return false;
150#endif // SK_VULKAN
151}
152
153bool VulkanWindow::CreateSkiaBackendContext(
154 GrVkBackendContext* context,
155 VkPhysicalDeviceFeatures* features,
157#ifdef SK_VULKAN
158 FML_CHECK(context);
159 FML_CHECK(features);
161 auto getProc = CreateSkiaGetProc(vk_);
162
163 if (getProc == nullptr) {
164 return false;
165 }
166
167 if (!logical_device_->GetPhysicalDeviceFeatures(features)) {
168 return false;
169 }
170
171 context->fInstance = application_->GetInstance();
172 context->fPhysicalDevice = logical_device_->GetPhysicalDeviceHandle();
173 context->fDevice = logical_device_->GetHandle();
174 context->fQueue = logical_device_->GetQueueHandle();
175 context->fGraphicsQueueIndex = logical_device_->GetGraphicsQueueIndex();
176 context->fMaxAPIVersion = application_->GetAPIVersion();
177 context->fDeviceFeatures = features;
178 context->fGetProc = std::move(getProc);
179 context->fMemoryAllocator = memory_allocator_;
180
181 constexpr uint32_t instance_extension_count = 2;
182 const char* instance_extensions[instance_extension_count] = {
183 // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_surface.html
185 surface_->GetNativeSurface().GetExtensionName(),
186 };
187 constexpr uint32_t device_extension_count = 1;
188 const char* device_extensions[device_extension_count] = {
189 // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_swapchain.html
191 };
192 extensions->init(context->fGetProc, context->fInstance,
193 context->fPhysicalDevice, instance_extension_count,
194 instance_extensions, device_extension_count,
195 device_extensions);
196 context->fVkExtensions = extensions;
197 return true;
198#else
199 return false;
200#endif
201}
202
203sk_sp<SkSurface> VulkanWindow::AcquireSurface() {
204 if (!IsValid()) {
205 FML_DLOG(INFO) << "Surface is invalid.";
206 return nullptr;
207 }
208
209 auto surface_size = surface_->GetSize();
210
211 // This check is theoretically unnecessary as the swapchain should report that
212 // the surface is out-of-date and perform swapchain recreation at the new
213 // configuration. However, on Android, the swapchain never reports that it is
214 // of date. Hence this extra check. Platforms that don't have this issue, or,
215 // cant report this information (which is optional anyway), report a zero
216 // size.
217 if (surface_size != SkISize::Make(0, 0) &&
218 surface_size != swapchain_->GetSize()) {
219 FML_DLOG(INFO) << "Swapchain and surface sizes are out of sync. Recreating "
220 "swapchain.";
221 if (!RecreateSwapchain()) {
222 FML_DLOG(INFO) << "Could not recreate swapchain.";
223 valid_ = false;
224 return nullptr;
225 }
226 }
227
228 while (true) {
230 auto acquire_result = VulkanSwapchain::AcquireStatus::ErrorSurfaceLost;
231
232 std::tie(acquire_result, surface) = swapchain_->AcquireSurface();
233
234 if (acquire_result == VulkanSwapchain::AcquireStatus::Success) {
235 // Successfully acquired a surface from the swapchain. Nothing more to do.
236 return surface;
237 }
238
239 if (acquire_result == VulkanSwapchain::AcquireStatus::ErrorSurfaceLost) {
240 // Surface is lost. This is an unrecoverable error.
241 FML_DLOG(INFO) << "Swapchain reported surface was lost.";
242 return nullptr;
243 }
244
245 if (acquire_result ==
246 VulkanSwapchain::AcquireStatus::ErrorSurfaceOutOfDate) {
247 // Surface out of date. Recreate the swapchain at the new configuration.
248 if (RecreateSwapchain()) {
249 // Swapchain was recreated, try surface acquisition again.
250 continue;
251 } else {
252 // Could not recreate the swapchain at the new configuration.
253 FML_DLOG(INFO) << "Swapchain reported surface was out of date but "
254 "could not recreate the swapchain at the new "
255 "configuration.";
256 valid_ = false;
257 return nullptr;
258 }
259 }
260
261 break;
262 }
263
264 FML_DCHECK(false) << "Unhandled VulkanSwapchain::AcquireResult";
265 return nullptr;
266}
267
268bool VulkanWindow::SwapBuffers() {
269 if (!IsValid()) {
270 FML_DLOG(INFO) << "Window was invalid.";
271 return false;
272 }
273
274 return swapchain_->Submit();
275}
276
277bool VulkanWindow::RecreateSwapchain() {
278 // This way, we always lose our reference to the old swapchain. Even if we
279 // cannot create a new one to replace it.
280 auto old_swapchain = std::move(swapchain_);
281
282 if (!vk_->IsValid()) {
283 return false;
284 }
285
286 if (logical_device_ == nullptr || !logical_device_->IsValid()) {
287 return false;
288 }
289
290 if (surface_ == nullptr || !surface_->IsValid()) {
291 return false;
292 }
293
294 if (skia_gr_context_ == nullptr) {
295 return false;
296 }
297
298 auto swapchain = std::make_unique<VulkanSwapchain>(
299 *vk_, *logical_device_, *surface_, skia_gr_context_.get(),
300 std::move(old_swapchain), logical_device_->GetGraphicsQueueIndex());
301
302 if (!swapchain->IsValid()) {
303 return false;
304 }
305
306 swapchain_ = std::move(swapchain);
307 return true;
308}
309
310} // namespace vulkan
const char * options
void setResourceCacheLimit(size_t maxResourceBytes)
static sk_sp< VulkanMemoryAllocator > Make(uint32_t vulkan_api_version, VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device, const fml::RefPtr< vulkan::VulkanProcTable > &vk, bool mustUseCoherentHostVisibleMemory)
VulkanWindow(fml::RefPtr< VulkanProcTable > proc_table, std::unique_ptr< VulkanNativeSurface > native_surface)
Construct a VulkanWindow. Let it implicitly create a GrDirectContext.
VkSwapchainKHR swapchain
Definition: main.cc:64
VkSurfaceKHR surface
Definition: main.cc:49
#define FML_DLOG(severity)
Definition: logging.h:102
#define FML_CHECK(condition)
Definition: logging.h:85
#define FML_DCHECK(condition)
Definition: logging.h:103
EGLSurface surface_
SK_API sk_sp< GrDirectContext > MakeVulkan(const skgpu::VulkanBackendContext &, const GrContextOptions &)
static const size_t kGrCacheMaxByteSize
Definition: ref_ptr.h:256
GrVkGetProc CreateSkiaGetProc(const fml::RefPtr< vulkan::VulkanProcTable > &vk)
static constexpr SkISize Make(int32_t w, int32_t h)
Definition: SkSize.h:20
sk_sp< VulkanMemoryAllocator > fMemoryAllocator
const VkPhysicalDeviceFeatures * fDeviceFeatures
const skgpu::VulkanExtensions * fVkExtensions
#define VK_KHR_SURFACE_EXTENSION_NAME
Definition: vulkan_core.h:7592
#define VK_KHR_SWAPCHAIN_EXTENSION_NAME
Definition: vulkan_core.h:7707