• 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/shell/common/rasterizer.h"
6 
7 #include "flutter/shell/common/persistent_cache.h"
8 
9 #include <utility>
10 
11 #include "third_party/skia/include/core/SkEncodedImageFormat.h"
12 #include "third_party/skia/include/core/SkImageEncoder.h"
13 #include "third_party/skia/include/core/SkPictureRecorder.h"
14 #include "third_party/skia/include/core/SkSerialProcs.h"
15 #include "third_party/skia/include/core/SkSurface.h"
16 #include "third_party/skia/include/core/SkSurfaceCharacterization.h"
17 #include "third_party/skia/include/utils/SkBase64.h"
18 
19 namespace flutter {
20 
21 #ifdef ACE_ENABLE_GPU
22 // The rasterizer will tell Skia to purge cached resources that have not been
23 // used within this interval.
24 static constexpr std::chrono::milliseconds kSkiaCleanupExpiration(15000);
25 #endif
26 
27 // TODO(dnfield): Remove this once internal embedders have caught up.
28 static Rasterizer::DummyDelegate dummy_delegate_;
Rasterizer(TaskRunners task_runners,std::unique_ptr<flutter::CompositorContext> compositor_context)29 Rasterizer::Rasterizer(
30     TaskRunners task_runners,
31     std::unique_ptr<flutter::CompositorContext> compositor_context)
32     : Rasterizer(dummy_delegate_,
33                  std::move(task_runners),
34                  std::move(compositor_context)) {}
35 
Rasterizer(Delegate & delegate,TaskRunners task_runners)36 Rasterizer::Rasterizer(Delegate& delegate, TaskRunners task_runners)
37     : Rasterizer(delegate,
38                  std::move(task_runners),
39                  std::make_unique<flutter::CompositorContext>()) {}
40 
Rasterizer(Delegate & delegate,TaskRunners task_runners,std::unique_ptr<flutter::CompositorContext> compositor_context)41 Rasterizer::Rasterizer(
42     Delegate& delegate,
43     TaskRunners task_runners,
44     std::unique_ptr<flutter::CompositorContext> compositor_context)
45     : delegate_(delegate),
46       task_runners_(std::move(task_runners)),
47       compositor_context_(std::move(compositor_context)),
48       user_override_resource_cache_bytes_(false),
49       weak_factory_(this) {
50   FML_DCHECK(compositor_context_);
51 }
52 
53 Rasterizer::~Rasterizer() = default;
54 
GetWeakPtr() const55 fml::WeakPtr<Rasterizer> Rasterizer::GetWeakPtr() const {
56   return weak_factory_.GetWeakPtr();
57 }
58 
Setup(std::unique_ptr<Surface> surface)59 void Rasterizer::Setup(std::unique_ptr<Surface> surface) {
60   surface_ = std::move(surface);
61   if (max_cache_bytes_.has_value()) {
62     SetResourceCacheMaxBytes(max_cache_bytes_.value(),
63                              user_override_resource_cache_bytes_);
64   }
65   compositor_context_->OnGrContextCreated();
66   if (surface_->GetExternalViewEmbedder()) {
67     const auto platform_id =
68         task_runners_.GetPlatformTaskRunner()->GetTaskQueueId();
69     const auto gpu_id = task_runners_.GetGPUTaskRunner()->GetTaskQueueId();
70     gpu_thread_merger_ =
71         fml::MakeRefCounted<fml::GpuThreadMerger>(platform_id, gpu_id);
72   }
73 }
74 
Teardown()75 void Rasterizer::Teardown() {
76   compositor_context_->OnGrContextDestroyed();
77   surface_.reset();
78   last_layer_tree_.reset();
79 }
80 
NotifyLowMemoryWarning() const81 void Rasterizer::NotifyLowMemoryWarning() const {
82   if (!surface_) {
83     FML_DLOG(INFO) << "Rasterizer::PurgeCaches called with no surface.";
84     return;
85   }
86   auto context = surface_->GetContext();
87   if (!context) {
88     FML_DLOG(INFO) << "Rasterizer::PurgeCaches called with no GrContext.";
89     return;
90   }
91   context->freeGpuResources();
92 }
93 
GetTextureRegistry()94 flutter::TextureRegistry* Rasterizer::GetTextureRegistry() {
95   return &compositor_context_->texture_registry();
96 }
97 
GetLastLayerTree()98 flutter::LayerTree* Rasterizer::GetLastLayerTree() {
99   return last_layer_tree_.get();
100 }
101 
DrawLastLayerTree()102 void Rasterizer::DrawLastLayerTree() {
103   if (!last_layer_tree_ || !surface_) {
104     return;
105   }
106   DrawToSurface(*last_layer_tree_);
107 }
108 
Draw(fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline)109 void Rasterizer::Draw(fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline) {
110   TRACE_EVENT0("flutter", "GPURasterizer::Draw");
111   if (gpu_thread_merger_ && !gpu_thread_merger_->IsOnRasterizingThread()) {
112     // we yield and let this frame be serviced on the right thread.
113     return;
114   }
115   FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread());
116 
117   RasterStatus raster_status = RasterStatus::kFailed;
118   Pipeline<flutter::LayerTree>::Consumer consumer =
119       [&](std::unique_ptr<LayerTree> layer_tree) {
120         raster_status = DoDraw(std::move(layer_tree));
121       };
122 
123   PipelineConsumeResult consume_result = pipeline->Consume(consumer);
124   // if the raster status is to resubmit the frame, we push the frame to the
125   // front of the queue and also change the consume status to more available.
126   if (raster_status == RasterStatus::kResubmit) {
127     auto front_continuation = pipeline->ProduceToFront();
128     front_continuation.Complete(std::move(resubmitted_layer_tree_));
129     consume_result = PipelineConsumeResult::MoreAvailable;
130   } else if (raster_status == RasterStatus::kEnqueuePipeline) {
131     consume_result = PipelineConsumeResult::MoreAvailable;
132   }
133 
134   // Consume as many pipeline items as possible. But yield the event loop
135   // between successive tries.
136   switch (consume_result) {
137     case PipelineConsumeResult::MoreAvailable: {
138       task_runners_.GetGPUTaskRunner()->PostTask(
139           [weak_this = weak_factory_.GetWeakPtr(), pipeline]() {
140             if (weak_this) {
141               weak_this->Draw(pipeline);
142             }
143           });
144       break;
145     }
146     default:
147       break;
148   }
149 }
150 
DoDraw(std::unique_ptr<flutter::LayerTree> layer_tree)151 RasterStatus Rasterizer::DoDraw(
152     std::unique_ptr<flutter::LayerTree> layer_tree) {
153   FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread());
154 
155   if (!layer_tree || !surface_) {
156     return RasterStatus::kFailed;
157   }
158 
159   FrameTiming timing;
160   timing.Set(FrameTiming::kBuildStart, layer_tree->build_start());
161   timing.Set(FrameTiming::kBuildFinish, layer_tree->build_finish());
162   timing.Set(FrameTiming::kRasterStart, fml::TimePoint::Now());
163 
164   PersistentCache* persistent_cache = PersistentCache::GetCacheForProcess();
165   persistent_cache->ResetStoredNewShaders();
166 
167   RasterStatus raster_status = DrawToSurface(*layer_tree);
168   if (raster_status == RasterStatus::kSuccess) {
169     last_layer_tree_ = std::move(layer_tree);
170   } else if (raster_status == RasterStatus::kResubmit) {
171     resubmitted_layer_tree_ = std::move(layer_tree);
172     return raster_status;
173   }
174 
175   if (persistent_cache->IsDumpingSkp() &&
176       persistent_cache->StoredNewShaders()) {
177     auto screenshot =
178         ScreenshotLastLayerTree(ScreenshotType::SkiaPicture, false);
179     persistent_cache->DumpSkp(*screenshot.data);
180   }
181 
182   // TODO(liyuqian): in Fuchsia, the rasterization doesn't finish when
183   // Rasterizer::DoDraw finishes. Future work is needed to adapt the timestamp
184   // for Fuchsia to capture SceneUpdateContext::ExecutePaintTasks.
185   timing.Set(FrameTiming::kRasterFinish, fml::TimePoint::Now());
186   delegate_.OnFrameRasterized(timing);
187 
188   // Pipeline pressure is applied from a couple of places:
189   // rasterizer: When there are more items as of the time of Consume.
190   // animator (via shell): Frame gets produces every vsync.
191   // Enqueing here is to account for the following scenario:
192   // T = 1
193   //  - one item (A) in the pipeline
194   //  - rasterizer starts (and merges the threads)
195   //  - pipeline consume result says no items to process
196   // T = 2
197   //  - animator produces (B) to the pipeline
198   //  - applies pipeline pressure via platform thread.
199   // T = 3
200   //   - rasterizes finished (and un-merges the threads)
201   //   - |Draw| for B yields as its on the wrong thread.
202   // This enqueue ensures that we attempt to consume from the right
203   // thread one more time after un-merge.
204   if (gpu_thread_merger_) {
205     if (gpu_thread_merger_->DecrementLease() ==
206         fml::GpuThreadStatus::kUnmergedNow) {
207       return RasterStatus::kEnqueuePipeline;
208     }
209   }
210 
211   return raster_status;
212 }
213 
DrawToSurface(flutter::LayerTree & layer_tree)214 RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) {
215   FML_DCHECK(surface_);
216 
217   auto frame = surface_->AcquireFrame(layer_tree.frame_size());
218 
219   if (frame == nullptr) {
220     return RasterStatus::kFailed;
221   }
222 
223   // There is no way for the compositor to know how long the layer tree
224   // construction took. Fortunately, the layer tree does. Grab that time
225   // for instrumentation.
226   compositor_context_->ui_time().SetLapTime(layer_tree.build_time());
227 
228   auto* external_view_embedder = surface_->GetExternalViewEmbedder();
229 
230   sk_sp<SkSurface> embedder_root_surface;
231 
232   if (external_view_embedder != nullptr) {
233     external_view_embedder->BeginFrame(layer_tree.frame_size(),
234                                        surface_->GetContext());
235     embedder_root_surface = external_view_embedder->GetRootSurface();
236   }
237 
238   // If the external view embedder has specified an optional root surface, the
239   // root surface transformation is set by the embedder instead of
240   // having to apply it here.
241   SkMatrix root_surface_transformation =
242       embedder_root_surface ? SkMatrix{} : surface_->GetRootTransformation();
243 
244   auto root_surface_canvas = embedder_root_surface
245                                  ? embedder_root_surface->getCanvas()
246                                  : frame->SkiaCanvas();
247 
248   auto compositor_frame = compositor_context_->AcquireFrame(
249       surface_->GetContext(),       // skia GrContext
250       root_surface_canvas,          // root surface canvas
251       external_view_embedder,       // external view embedder
252       root_surface_transformation,  // root surface transformation
253       true,                         // instrumentation enabled
254       gpu_thread_merger_            // thread merger
255   );
256 
257   if (compositor_frame) {
258     RasterStatus raster_status = compositor_frame->Raster(layer_tree, false);
259     if (raster_status == RasterStatus::kFailed) {
260       return raster_status;
261     }
262     frame->Submit();
263     if (external_view_embedder != nullptr) {
264       external_view_embedder->SubmitFrame(surface_->GetContext());
265     }
266 
267     FireNextFrameCallbackIfPresent();
268 #ifdef ACE_ENABLE_GPU
269     if (surface_->GetContext()) {
270         surface_->GetContext()->performDeferredCleanup(kSkiaCleanupExpiration);
271     }
272 #endif
273 
274     return raster_status;
275   }
276 
277   return RasterStatus::kFailed;
278 }
279 
SerializeTypeface(SkTypeface * typeface,void * ctx)280 static sk_sp<SkData> SerializeTypeface(SkTypeface* typeface, void* ctx) {
281   return typeface->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
282 }
283 
ScreenshotLayerTreeAsPicture(flutter::LayerTree * tree,flutter::CompositorContext & compositor_context)284 static sk_sp<SkData> ScreenshotLayerTreeAsPicture(
285     flutter::LayerTree* tree,
286     flutter::CompositorContext& compositor_context) {
287   FML_DCHECK(tree != nullptr);
288   SkPictureRecorder recorder;
289   recorder.beginRecording(
290       SkRect::MakeWH(tree->frame_size().width(), tree->frame_size().height()));
291 
292   SkMatrix root_surface_transformation;
293   root_surface_transformation.reset();
294 
295   // TODO(amirh): figure out how to take a screenshot with embedded UIView.
296   // https://github.com/flutter/flutter/issues/23435
297   auto frame = compositor_context.AcquireFrame(
298       nullptr, recorder.getRecordingCanvas(), nullptr,
299       root_surface_transformation, false, nullptr);
300 
301   frame->Raster(*tree, true);
302 
303   SkSerialProcs procs = {0};
304   procs.fTypefaceProc = SerializeTypeface;
305 
306   return recorder.finishRecordingAsPicture()->serialize(&procs);
307 }
308 
CreateSnapshotSurface(GrContext * surface_context,const SkISize & size)309 static sk_sp<SkSurface> CreateSnapshotSurface(GrContext* surface_context,
310                                               const SkISize& size) {
311   const auto image_info = SkImageInfo::MakeN32Premul(
312       size.width(), size.height(), SkColorSpace::MakeSRGB());
313   if (surface_context) {
314     // There is a rendering surface that may contain textures that are going to
315     // be referenced in the layer tree about to be drawn.
316     return SkSurface::MakeRenderTarget(surface_context,  //
317                                        SkBudgeted::kNo,  //
318                                        image_info        //
319     );
320   }
321 
322   // There is no rendering surface, assume no GPU textures are present and
323   // create a raster surface.
324   return SkSurface::MakeRaster(image_info);
325 }
326 
ScreenshotLayerTreeAsImage(flutter::LayerTree * tree,flutter::CompositorContext & compositor_context,GrContext * surface_context,bool compressed)327 static sk_sp<SkData> ScreenshotLayerTreeAsImage(
328     flutter::LayerTree* tree,
329     flutter::CompositorContext& compositor_context,
330     GrContext* surface_context,
331     bool compressed) {
332   // Attempt to create a snapshot surface depending on whether we have access to
333   // a valid GPU rendering context.
334   auto snapshot_surface =
335       CreateSnapshotSurface(surface_context, tree->frame_size());
336   if (snapshot_surface == nullptr) {
337     FML_LOG(ERROR) << "Screenshot: unable to create snapshot surface";
338     return nullptr;
339   }
340 
341   // Draw the current layer tree into the snapshot surface.
342   auto* canvas = snapshot_surface->getCanvas();
343 
344   // There is no root surface transformation for the screenshot layer. Reset the
345   // matrix to identity.
346   SkMatrix root_surface_transformation;
347   root_surface_transformation.reset();
348 
349   auto frame = compositor_context.AcquireFrame(surface_context, canvas, nullptr,
350                                                root_surface_transformation,
351                                                false, nullptr);
352   canvas->clear(SK_ColorTRANSPARENT);
353   frame->Raster(*tree, true);
354   canvas->flush();
355 
356   // Prepare an image from the surface, this image may potentially be on th GPU.
357   auto potentially_gpu_snapshot = snapshot_surface->makeImageSnapshot();
358   if (!potentially_gpu_snapshot) {
359     FML_LOG(ERROR) << "Screenshot: unable to make image screenshot";
360     return nullptr;
361   }
362 
363   // Copy the GPU image snapshot into CPU memory.
364   auto cpu_snapshot = potentially_gpu_snapshot->makeRasterImage();
365   if (!cpu_snapshot) {
366     FML_LOG(ERROR) << "Screenshot: unable to make raster image";
367     return nullptr;
368   }
369 
370   // If the caller want the pixels to be compressed, there is a Skia utility to
371   // compress to PNG. Use that.
372   if (compressed) {
373     return cpu_snapshot->encodeToData();
374   }
375 
376   // Copy it into a bitmap and return the same.
377   SkPixmap pixmap;
378   if (!cpu_snapshot->peekPixels(&pixmap)) {
379     FML_LOG(ERROR) << "Screenshot: unable to obtain bitmap pixels";
380     return nullptr;
381   }
382 
383   return SkData::MakeWithCopy(pixmap.addr32(), pixmap.computeByteSize());
384 }
385 
ScreenshotLastLayerTree(Rasterizer::ScreenshotType type,bool base64_encode)386 Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree(
387     Rasterizer::ScreenshotType type,
388     bool base64_encode) {
389   auto* layer_tree = GetLastLayerTree();
390   if (layer_tree == nullptr) {
391     FML_LOG(ERROR) << "Last layer tree was null when screenshotting.";
392     return {};
393   }
394 
395   sk_sp<SkData> data = nullptr;
396 
397   GrContext* surface_context = surface_ ? surface_->GetContext() : nullptr;
398 
399   switch (type) {
400     case ScreenshotType::SkiaPicture:
401       data = ScreenshotLayerTreeAsPicture(layer_tree, *compositor_context_);
402       break;
403     case ScreenshotType::UncompressedImage:
404       data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_,
405                                         surface_context, false);
406       break;
407     case ScreenshotType::CompressedImage:
408       data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_,
409                                         surface_context, true);
410       break;
411   }
412 
413   if (data == nullptr) {
414     FML_LOG(ERROR) << "Screenshot data was null.";
415     return {};
416   }
417 
418   if (base64_encode) {
419     size_t b64_size = SkBase64::Encode(data->data(), data->size(), nullptr);
420     auto b64_data = SkData::MakeUninitialized(b64_size);
421     SkBase64::Encode(data->data(), data->size(), b64_data->writable_data());
422     return Rasterizer::Screenshot{b64_data, layer_tree->frame_size()};
423   }
424 
425   return Rasterizer::Screenshot{data, layer_tree->frame_size()};
426 }
427 
SetNextFrameCallback(fml::closure callback)428 void Rasterizer::SetNextFrameCallback(fml::closure callback) {
429   next_frame_callback_ = callback;
430 }
431 
FireNextFrameCallbackIfPresent()432 void Rasterizer::FireNextFrameCallbackIfPresent() {
433   if (!next_frame_callback_) {
434     return;
435   }
436   // It is safe for the callback to set a new callback.
437   auto callback = next_frame_callback_;
438   next_frame_callback_ = nullptr;
439   callback();
440 }
441 
SetResourceCacheMaxBytes(size_t max_bytes,bool from_user)442 void Rasterizer::SetResourceCacheMaxBytes(size_t max_bytes, bool from_user) {
443   user_override_resource_cache_bytes_ |= from_user;
444 
445   if (!from_user && user_override_resource_cache_bytes_) {
446     // We should not update the setting here if a user has explicitly set a
447     // value for this over the flutter/skia channel.
448     return;
449   }
450 
451   max_cache_bytes_ = max_bytes;
452   if (!surface_) {
453     return;
454   }
455 #ifdef ACE_ENABLE_GPU
456     GrContext* context = surface_->GetContext();
457     if (context) {
458         int max_resources;
459         context->getResourceCacheLimits(&max_resources, nullptr);
460         context->setResourceCacheLimits(max_resources, max_bytes);
461     }
462 #endif
463 }
464 
GetResourceCacheMaxBytes() const465 std::optional<size_t> Rasterizer::GetResourceCacheMaxBytes() const {
466     if (!surface_) {
467         return std::nullopt;
468     }
469 #ifdef ACE_ENABLE_GPU
470     GrContext* context = surface_->GetContext();
471     if (context) {
472         size_t max_bytes;
473         context->getResourceCacheLimits(nullptr, &max_bytes);
474         return max_bytes;
475     }
476 #endif
477     return std::nullopt;
478 }
479 
Screenshot()480 Rasterizer::Screenshot::Screenshot() {}
481 
Screenshot(sk_sp<SkData> p_data,SkISize p_size)482 Rasterizer::Screenshot::Screenshot(sk_sp<SkData> p_data, SkISize p_size)
483     : data(std::move(p_data)), frame_size(p_size) {}
484 
485 Rasterizer::Screenshot::Screenshot(const Screenshot& other) = default;
486 
487 Rasterizer::Screenshot::~Screenshot() = default;
488 
489 }  // namespace flutter
490