• 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 "vulkan_surface_producer.h"
6 
7 #include <lib/async/cpp/task.h>
8 #include <lib/async/default.h>
9 
10 #include <memory>
11 #include <string>
12 #include <vector>
13 
14 #include "flutter/fml/trace_event.h"
15 #include "third_party/skia/include/gpu/GrBackendSemaphore.h"
16 #include "third_party/skia/include/gpu/GrBackendSurface.h"
17 #include "third_party/skia/include/gpu/GrContext.h"
18 #include "third_party/skia/include/gpu/vk/GrVkBackendContext.h"
19 #include "third_party/skia/include/gpu/vk/GrVkTypes.h"
20 
21 namespace flutter_runner {
22 
23 namespace {
24 
25 // TODO: Properly tune these values. See FL-153.
26 constexpr int kGrCacheMaxCount = 8192;
27 constexpr size_t kGrCacheMaxByteSize = 8 * (1 << 20);
28 
29 }  // namespace
30 
VulkanSurfaceProducer(scenic::Session * scenic_session)31 VulkanSurfaceProducer::VulkanSurfaceProducer(scenic::Session* scenic_session) {
32   valid_ = Initialize(scenic_session);
33 
34   if (valid_) {
35     FML_DLOG(INFO)
36         << "Flutter engine: Vulkan surface producer initialization: Successful";
37   } else {
38     FML_LOG(ERROR)
39         << "Flutter engine: Vulkan surface producer initialization: Failed";
40   }
41 }
42 
~VulkanSurfaceProducer()43 VulkanSurfaceProducer::~VulkanSurfaceProducer() {
44   // Make sure queue is idle before we start destroying surfaces
45   if (valid_) {
46     VkResult wait_result = VK_CALL_LOG_ERROR(
47         vk_->QueueWaitIdle(logical_device_->GetQueueHandle()));
48     FML_DCHECK(wait_result == VK_SUCCESS);
49   }
50 };
51 
Initialize(scenic::Session * scenic_session)52 bool VulkanSurfaceProducer::Initialize(scenic::Session* scenic_session) {
53   vk_ = fml::MakeRefCounted<vulkan::VulkanProcTable>();
54 
55   std::vector<std::string> extensions = {
56       VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME,
57   };
58   application_ = std::make_unique<vulkan::VulkanApplication>(
59       *vk_, "FlutterRunner", std::move(extensions), VK_MAKE_VERSION(1, 0, 0),
60       VK_MAKE_VERSION(1, 1, 0));
61 
62   if (!application_->IsValid() || !vk_->AreInstanceProcsSetup()) {
63     // Make certain the application instance was created and it setup the
64     // instance proc table entries.
65     FML_LOG(ERROR) << "Instance proc addresses have not been setup.";
66     return false;
67   }
68 
69   // Create the device.
70 
71   logical_device_ = application_->AcquireFirstCompatibleLogicalDevice();
72 
73   if (logical_device_ == nullptr || !logical_device_->IsValid() ||
74       !vk_->AreDeviceProcsSetup()) {
75     // Make certain the device was created and it setup the device proc table
76     // entries.
77     FML_LOG(ERROR) << "Device proc addresses have not been setup.";
78     return false;
79   }
80 
81   if (!vk_->HasAcquiredMandatoryProcAddresses()) {
82     FML_LOG(ERROR) << "Failed to acquire mandatory proc addresses.";
83     return false;
84   }
85 
86   if (!vk_->IsValid()) {
87     FML_LOG(ERROR) << "VulkanProcTable invalid";
88     return false;
89   }
90 
91   auto getProc = vk_->CreateSkiaGetProc();
92 
93   if (getProc == nullptr) {
94     FML_LOG(ERROR) << "Failed to create skia getProc.";
95     return false;
96   }
97 
98   uint32_t skia_features = 0;
99   if (!logical_device_->GetPhysicalDeviceFeaturesSkia(&skia_features)) {
100     FML_LOG(ERROR) << "Failed to get physical device features.";
101 
102     return false;
103   }
104 
105   GrVkBackendContext backend_context;
106   backend_context.fInstance = application_->GetInstance();
107   backend_context.fPhysicalDevice = logical_device_->GetPhysicalDeviceHandle();
108   backend_context.fDevice = logical_device_->GetHandle();
109   backend_context.fQueue = logical_device_->GetQueueHandle();
110   backend_context.fGraphicsQueueIndex =
111       logical_device_->GetGraphicsQueueIndex();
112   backend_context.fMinAPIVersion = application_->GetAPIVersion();
113   backend_context.fFeatures = skia_features;
114   backend_context.fGetProc = std::move(getProc);
115   backend_context.fOwnsInstanceAndDevice = false;
116 
117   context_ = GrContext::MakeVulkan(backend_context);
118 
119   // Use local limits specified in this file above instead of flutter defaults.
120   context_->setResourceCacheLimits(kGrCacheMaxCount, kGrCacheMaxByteSize);
121 
122   surface_pool_ =
123       std::make_unique<VulkanSurfacePool>(*this, context_, scenic_session);
124 
125   return true;
126 }
127 
OnSurfacesPresented(std::vector<std::unique_ptr<flutter::SceneUpdateContext::SurfaceProducerSurface>> surfaces)128 void VulkanSurfaceProducer::OnSurfacesPresented(
129     std::vector<
130         std::unique_ptr<flutter::SceneUpdateContext::SurfaceProducerSurface>>
131         surfaces) {
132   TRACE_EVENT0("flutter", "VulkanSurfaceProducer::OnSurfacesPresented");
133 
134   // Do a single flush for all canvases derived from the context.
135   {
136     TRACE_EVENT0("flutter", "GrContext::flushAndSignalSemaphores");
137     context_->flush();
138   }
139 
140   if (!TransitionSurfacesToExternal(surfaces))
141     FML_LOG(ERROR) << "TransitionSurfacesToExternal failed";
142 
143   // Submit surface
144   for (auto& surface : surfaces) {
145     SubmitSurface(std::move(surface));
146   }
147 
148   // Buffer management.
149   surface_pool_->AgeAndCollectOldBuffers();
150 
151   // If no further surface production has taken place for 10 frames (TODO:
152   // Don't hardcode refresh rate here), then shrink our surface pool to fit.
153   constexpr auto kShouldShrinkThreshold = zx::msec(10 * 16.67);
154   async::PostDelayedTask(
155       async_get_default_dispatcher(),
156       [self = weak_factory_.GetWeakPtr(), kShouldShrinkThreshold] {
157         if (!self) {
158           return;
159         }
160         auto time_since_last_produce =
161             async::Now(async_get_default_dispatcher()) -
162             self->last_produce_time_;
163         if (time_since_last_produce >= kShouldShrinkThreshold) {
164           self->surface_pool_->ShrinkToFit();
165         }
166       },
167       kShouldShrinkThreshold);
168 }
169 
TransitionSurfacesToExternal(const std::vector<std::unique_ptr<flutter::SceneUpdateContext::SurfaceProducerSurface>> & surfaces)170 bool VulkanSurfaceProducer::TransitionSurfacesToExternal(
171     const std::vector<
172         std::unique_ptr<flutter::SceneUpdateContext::SurfaceProducerSurface>>&
173         surfaces) {
174   for (auto& surface : surfaces) {
175     auto vk_surface = static_cast<VulkanSurface*>(surface.get());
176 
177     vulkan::VulkanCommandBuffer* command_buffer =
178         vk_surface->GetCommandBuffer(logical_device_->GetCommandPool());
179     if (!command_buffer->Begin())
180       return false;
181 
182     GrBackendRenderTarget backendRT =
183         vk_surface->GetSkiaSurface()->getBackendRenderTarget(
184             SkSurface::kFlushRead_BackendHandleAccess);
185     if (!backendRT.isValid()) {
186       return false;
187     }
188     GrVkImageInfo imageInfo;
189     if (!backendRT.getVkImageInfo(&imageInfo)) {
190       return false;
191     }
192 
193     VkImageMemoryBarrier image_barrier = {
194         .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
195         .pNext = nullptr,
196         .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
197         .dstAccessMask = 0,
198         .oldLayout = imageInfo.fImageLayout,
199         .newLayout = VK_IMAGE_LAYOUT_GENERAL,
200         .srcQueueFamilyIndex = 0,
201         .dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR,
202         .image = vk_surface->GetVkImage(),
203         .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
204 
205     if (!command_buffer->InsertPipelineBarrier(
206             VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
207             VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
208             0,           // dependencyFlags
209             0, nullptr,  // memory barriers
210             0, nullptr,  // buffer barriers
211             1, &image_barrier))
212       return false;
213 
214     backendRT.setVkImageLayout(image_barrier.newLayout);
215 
216     if (!command_buffer->End())
217       return false;
218 
219     if (!logical_device_->QueueSubmit(
220             {}, {}, {vk_surface->GetAcquireVkSemaphore()},
221             {command_buffer->Handle()}, vk_surface->GetCommandBufferFence()))
222       return false;
223   }
224   return true;
225 }
226 
227 std::unique_ptr<flutter::SceneUpdateContext::SurfaceProducerSurface>
ProduceSurface(const SkISize & size,const flutter::LayerRasterCacheKey & layer_key,std::unique_ptr<scenic::EntityNode> entity_node)228 VulkanSurfaceProducer::ProduceSurface(
229     const SkISize& size,
230     const flutter::LayerRasterCacheKey& layer_key,
231     std::unique_ptr<scenic::EntityNode> entity_node) {
232   FML_DCHECK(valid_);
233   last_produce_time_ = async::Now(async_get_default_dispatcher());
234   auto surface = surface_pool_->AcquireSurface(size);
235   surface->SetRetainedInfo(layer_key, std::move(entity_node));
236   return surface;
237 }
238 
SubmitSurface(std::unique_ptr<flutter::SceneUpdateContext::SurfaceProducerSurface> surface)239 void VulkanSurfaceProducer::SubmitSurface(
240     std::unique_ptr<flutter::SceneUpdateContext::SurfaceProducerSurface>
241         surface) {
242   FML_DCHECK(valid_ && surface != nullptr);
243   surface_pool_->SubmitSurface(std::move(surface));
244 }
245 
246 }  // namespace flutter_runner
247