• 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 #ifndef GPU_DISABLED
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 #ifndef GPU_DISABLED
269     if (surface_->GetContext()) {
270       surface_->GetContext()->performDeferredCleanup(kSkiaCleanupExpiration);
271     }
272 #endif
273     return raster_status;
274   }
275 
276   return RasterStatus::kFailed;
277 }
278 
SerializeTypeface(SkTypeface * typeface,void * ctx)279 static sk_sp<SkData> SerializeTypeface(SkTypeface* typeface, void* ctx) {
280   return typeface->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
281 }
282 
ScreenshotLayerTreeAsPicture(flutter::LayerTree * tree,flutter::CompositorContext & compositor_context)283 static sk_sp<SkData> ScreenshotLayerTreeAsPicture(
284     flutter::LayerTree* tree,
285     flutter::CompositorContext& compositor_context) {
286   FML_DCHECK(tree != nullptr);
287   SkPictureRecorder recorder;
288   recorder.beginRecording(
289       SkRect::MakeWH(tree->frame_size().width(), tree->frame_size().height()));
290 
291   SkMatrix root_surface_transformation;
292   root_surface_transformation.reset();
293 
294   // TODO(amirh): figure out how to take a screenshot with embedded UIView.
295   // https://github.com/flutter/flutter/issues/23435
296   auto frame = compositor_context.AcquireFrame(
297       nullptr, recorder.getRecordingCanvas(), nullptr,
298       root_surface_transformation, false, nullptr);
299 
300   frame->Raster(*tree, true);
301 
302   SkSerialProcs procs = {0};
303   procs.fTypefaceProc = SerializeTypeface;
304 
305   return recorder.finishRecordingAsPicture()->serialize(&procs);
306 }
307 
CreateSnapshotSurface(GrContext * surface_context,const SkISize & size)308 static sk_sp<SkSurface> CreateSnapshotSurface(GrContext* surface_context,
309                                               const SkISize& size) {
310   const auto image_info = SkImageInfo::MakeN32Premul(
311       size.width(), size.height(), SkColorSpace::MakeSRGB());
312   if (surface_context) {
313     // There is a rendering surface that may contain textures that are going to
314     // be referenced in the layer tree about to be drawn.
315     return SkSurface::MakeRenderTarget(surface_context,  //
316                                        SkBudgeted::kNo,  //
317                                        image_info        //
318     );
319   }
320 
321   // There is no rendering surface, assume no GPU textures are present and
322   // create a raster surface.
323   return SkSurface::MakeRaster(image_info);
324 }
325 
ScreenshotLayerTreeAsImage(flutter::LayerTree * tree,flutter::CompositorContext & compositor_context,GrContext * surface_context,bool compressed)326 static sk_sp<SkData> ScreenshotLayerTreeAsImage(
327     flutter::LayerTree* tree,
328     flutter::CompositorContext& compositor_context,
329     GrContext* surface_context,
330     bool compressed) {
331   // Attempt to create a snapshot surface depending on whether we have access to
332   // a valid GPU rendering context.
333   auto snapshot_surface =
334       CreateSnapshotSurface(surface_context, tree->frame_size());
335   if (snapshot_surface == nullptr) {
336     FML_LOG(ERROR) << "Screenshot: unable to create snapshot surface";
337     return nullptr;
338   }
339 
340   // Draw the current layer tree into the snapshot surface.
341   auto* canvas = snapshot_surface->getCanvas();
342 
343   // There is no root surface transformation for the screenshot layer. Reset the
344   // matrix to identity.
345   SkMatrix root_surface_transformation;
346   root_surface_transformation.reset();
347 
348   auto frame = compositor_context.AcquireFrame(surface_context, canvas, nullptr,
349                                                root_surface_transformation,
350                                                false, nullptr);
351   canvas->clear(SK_ColorTRANSPARENT);
352   frame->Raster(*tree, true);
353   canvas->flush();
354 
355   // Prepare an image from the surface, this image may potentially be on th GPU.
356   auto potentially_gpu_snapshot = snapshot_surface->makeImageSnapshot();
357   if (!potentially_gpu_snapshot) {
358     FML_LOG(ERROR) << "Screenshot: unable to make image screenshot";
359     return nullptr;
360   }
361 
362   // Copy the GPU image snapshot into CPU memory.
363   auto cpu_snapshot = potentially_gpu_snapshot->makeRasterImage();
364   if (!cpu_snapshot) {
365     FML_LOG(ERROR) << "Screenshot: unable to make raster image";
366     return nullptr;
367   }
368 
369   // If the caller want the pixels to be compressed, there is a Skia utility to
370   // compress to PNG. Use that.
371   if (compressed) {
372     return cpu_snapshot->encodeToData();
373   }
374 
375   // Copy it into a bitmap and return the same.
376   SkPixmap pixmap;
377   if (!cpu_snapshot->peekPixels(&pixmap)) {
378     FML_LOG(ERROR) << "Screenshot: unable to obtain bitmap pixels";
379     return nullptr;
380   }
381 
382   return SkData::MakeWithCopy(pixmap.addr32(), pixmap.computeByteSize());
383 }
384 
ScreenshotLastLayerTree(Rasterizer::ScreenshotType type,bool base64_encode)385 Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree(
386     Rasterizer::ScreenshotType type,
387     bool base64_encode) {
388   auto* layer_tree = GetLastLayerTree();
389   if (layer_tree == nullptr) {
390     FML_LOG(ERROR) << "Last layer tree was null when screenshotting.";
391     return {};
392   }
393 
394   sk_sp<SkData> data = nullptr;
395 
396   GrContext* surface_context = surface_ ? surface_->GetContext() : nullptr;
397 
398   switch (type) {
399     case ScreenshotType::SkiaPicture:
400       data = ScreenshotLayerTreeAsPicture(layer_tree, *compositor_context_);
401       break;
402     case ScreenshotType::UncompressedImage:
403       data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_,
404                                         surface_context, false);
405       break;
406     case ScreenshotType::CompressedImage:
407       data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_,
408                                         surface_context, true);
409       break;
410   }
411 
412   if (data == nullptr) {
413     FML_LOG(ERROR) << "Screenshot data was null.";
414     return {};
415   }
416 
417   if (base64_encode) {
418     size_t b64_size = SkBase64::Encode(data->data(), data->size(), nullptr);
419     auto b64_data = SkData::MakeUninitialized(b64_size);
420     SkBase64::Encode(data->data(), data->size(), b64_data->writable_data());
421     return Rasterizer::Screenshot{b64_data, layer_tree->frame_size()};
422   }
423 
424   return Rasterizer::Screenshot{data, layer_tree->frame_size()};
425 }
426 
SetNextFrameCallback(fml::closure callback)427 void Rasterizer::SetNextFrameCallback(fml::closure callback) {
428   next_frame_callback_ = callback;
429 }
430 
FireNextFrameCallbackIfPresent()431 void Rasterizer::FireNextFrameCallbackIfPresent() {
432   if (!next_frame_callback_) {
433     return;
434   }
435   // It is safe for the callback to set a new callback.
436   auto callback = next_frame_callback_;
437   next_frame_callback_ = nullptr;
438   callback();
439 }
440 
SetResourceCacheMaxBytes(size_t max_bytes,bool from_user)441 void Rasterizer::SetResourceCacheMaxBytes(size_t max_bytes, bool from_user) {
442   user_override_resource_cache_bytes_ |= from_user;
443 
444   if (!from_user && user_override_resource_cache_bytes_) {
445     // We should not update the setting here if a user has explicitly set a
446     // value for this over the flutter/skia channel.
447     return;
448   }
449 
450   max_cache_bytes_ = max_bytes;
451   if (!surface_) {
452     return;
453   }
454 #ifndef GPU_DISABLED
455   GrContext* context = surface_->GetContext();
456   if (context) {
457     int max_resources;
458     context->getResourceCacheLimits(&max_resources, nullptr);
459     context->setResourceCacheLimits(max_resources, max_bytes);
460   }
461 #endif
462 }
463 
GetResourceCacheMaxBytes() const464 std::optional<size_t> Rasterizer::GetResourceCacheMaxBytes() const {
465   if (!surface_) {
466     return std::nullopt;
467   }
468 #ifndef GPU_DISABLED
469   GrContext* context = surface_->GetContext();
470   if (context) {
471     size_t max_bytes;
472     context->getResourceCacheLimits(nullptr, &max_bytes);
473     return max_bytes;
474   }
475 #endif
476   return std::nullopt;
477 }
478 
Screenshot()479 Rasterizer::Screenshot::Screenshot() {}
480 
Screenshot(sk_sp<SkData> p_data,SkISize p_size)481 Rasterizer::Screenshot::Screenshot(sk_sp<SkData> p_data, SkISize p_size)
482     : data(std::move(p_data)), frame_size(p_size) {}
483 
484 Rasterizer::Screenshot::Screenshot(const Screenshot& other) = default;
485 
486 Rasterizer::Screenshot::~Screenshot() = default;
487 
488 }  // namespace flutter
489