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