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

source code of flutter_engine/flutter/vulkan/vulkan_window.cc