• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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