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