Flutter Engine
rasterizer.cc
Go to the documentation of this file.
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 <algorithm>
8 #include <memory>
9 #include <utility>
10 
11 #include "flow/frame_timings.h"
12 #include "flutter/common/graphics/persistent_cache.h"
13 #include "flutter/fml/time/time_delta.h"
14 #include "flutter/fml/time/time_point.h"
15 #include "flutter/shell/common/serialization_callbacks.h"
16 #include "fml/make_copyable.h"
17 #include "third_party/skia/include/core/SkEncodedImageFormat.h"
18 #include "third_party/skia/include/core/SkImageEncoder.h"
19 #include "third_party/skia/include/core/SkPictureRecorder.h"
20 #include "third_party/skia/include/core/SkSerialProcs.h"
21 #include "third_party/skia/include/core/SkSurface.h"
22 #include "third_party/skia/include/core/SkSurfaceCharacterization.h"
23 #include "third_party/skia/include/utils/SkBase64.h"
24 
25 namespace flutter {
26 
27 // The rasterizer will tell Skia to purge cached resources that have not been
28 // used within this interval.
29 static constexpr std::chrono::milliseconds kSkiaCleanupExpiration(15000);
30 
32  : delegate_(delegate),
33  compositor_context_(std::make_unique<flutter::CompositorContext>(
34  delegate.GetFrameBudget())),
35  user_override_resource_cache_bytes_(false),
36  weak_factory_(this) {
37  FML_DCHECK(compositor_context_);
38 }
39 
40 Rasterizer::~Rasterizer() = default;
41 
43  return weak_factory_.GetWeakPtr();
44 }
45 
47  const {
48  return weak_factory_.GetWeakPtr();
49 }
50 
51 void Rasterizer::Setup(std::unique_ptr<Surface> surface) {
52  surface_ = std::move(surface);
53 
54  if (max_cache_bytes_.has_value()) {
55  SetResourceCacheMaxBytes(max_cache_bytes_.value(),
56  user_override_resource_cache_bytes_);
57  }
58 
59  auto context_switch = surface_->MakeRenderContextCurrent();
60  if (context_switch->GetResult()) {
61  compositor_context_->OnGrContextCreated();
62  }
63 
64  if (external_view_embedder_ &&
65  external_view_embedder_->SupportsDynamicThreadMerging() &&
66  !raster_thread_merger_) {
67  const auto platform_id =
69  const auto gpu_id =
72  delegate_.GetParentRasterThreadMerger(), platform_id, gpu_id);
73  }
74  if (raster_thread_merger_) {
75  raster_thread_merger_->SetMergeUnmergeCallback([=]() {
76  // Clear the GL context after the thread configuration has changed.
77  if (surface_) {
78  surface_->ClearRenderContext();
79  }
80  });
81  }
82 }
83 
85  auto context_switch =
86  surface_ ? surface_->MakeRenderContextCurrent() : nullptr;
87  if (context_switch && context_switch->GetResult()) {
88  compositor_context_->OnGrContextDestroyed();
89  }
90 
91  surface_.reset();
92  last_layer_tree_.reset();
93 
94  if (raster_thread_merger_.get() != nullptr &&
95  raster_thread_merger_.get()->IsMerged()) {
96  FML_DCHECK(raster_thread_merger_->IsEnabled());
97  raster_thread_merger_->UnMergeNowIfLastOne();
98  raster_thread_merger_->SetMergeUnmergeCallback(nullptr);
99  }
100 
101  if (external_view_embedder_) {
102  external_view_embedder_->Teardown();
103  }
104 }
105 
107  if (raster_thread_merger_) {
108  raster_thread_merger_->Enable();
109  }
110 }
111 
113  if (raster_thread_merger_) {
114  raster_thread_merger_->Disable();
115  }
116 }
117 
119  if (!surface_) {
120  FML_DLOG(INFO)
121  << "Rasterizer::NotifyLowMemoryWarning called with no surface.";
122  return;
123  }
124  auto context = surface_->GetContext();
125  if (!context) {
126  FML_DLOG(INFO)
127  << "Rasterizer::NotifyLowMemoryWarning called with no GrContext.";
128  return;
129  }
130  auto context_switch = surface_->MakeRenderContextCurrent();
131  if (!context_switch->GetResult()) {
132  return;
133  }
134  context->performDeferredCleanup(std::chrono::milliseconds(0));
135 }
136 
138  return &compositor_context_->texture_registry();
139 }
140 
142  return last_layer_tree_.get();
143 }
144 
146  std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
147  if (!last_layer_tree_ || !surface_) {
148  return;
149  }
150  frame_timings_recorder->RecordRasterStart(
151  fml::TimePoint::Now(), &compositor_context_->raster_cache());
152  DrawToSurface(*frame_timings_recorder, *last_layer_tree_);
153 }
154 
156  std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder,
157  std::shared_ptr<Pipeline<flutter::LayerTree>> pipeline,
158  LayerTreeDiscardCallback discard_callback) {
159  TRACE_EVENT_WITH_FRAME_NUMBER(frame_timings_recorder, "flutter",
160  "GPURasterizer::Draw");
161  if (raster_thread_merger_ &&
162  !raster_thread_merger_->IsOnRasterizingThread()) {
163  // we yield and let this frame be serviced on the right thread.
164  return RasterStatus::kYielded;
165  }
166  FML_DCHECK(delegate_.GetTaskRunners()
169 
170  std::unique_ptr<FrameTimingsRecorder> resubmit_recorder =
171  frame_timings_recorder->CloneUntil(
173 
174  RasterStatus raster_status = RasterStatus::kFailed;
176  [&](std::unique_ptr<LayerTree> layer_tree) {
177  if (discard_callback(*layer_tree.get())) {
178  raster_status = RasterStatus::kDiscarded;
179  } else {
180  raster_status =
181  DoDraw(std::move(frame_timings_recorder), std::move(layer_tree));
182  }
183  };
184 
185  PipelineConsumeResult consume_result = pipeline->Consume(consumer);
186  // if the raster status is to resubmit the frame, we push the frame to the
187  // front of the queue and also change the consume status to more available.
188 
189  auto should_resubmit_frame = raster_status == RasterStatus::kResubmit ||
190  raster_status == RasterStatus::kSkipAndRetry;
191  if (should_resubmit_frame) {
192  auto front_continuation = pipeline->ProduceIfEmpty();
193  bool result =
194  front_continuation.Complete(std::move(resubmitted_layer_tree_));
195  if (result) {
196  consume_result = PipelineConsumeResult::MoreAvailable;
197  }
198  } else if (raster_status == RasterStatus::kEnqueuePipeline) {
199  consume_result = PipelineConsumeResult::MoreAvailable;
200  }
201 
202  // EndFrame should perform cleanups for the external_view_embedder.
203  if (surface_ && external_view_embedder_) {
204  external_view_embedder_->EndFrame(should_resubmit_frame,
205  raster_thread_merger_);
206  }
207 
208  // Consume as many pipeline items as possible. But yield the event loop
209  // between successive tries.
210  switch (consume_result) {
214  [weak_this = weak_factory_.GetWeakPtr(), pipeline,
215  resubmit_recorder = std::move(resubmit_recorder),
216  discard_callback = std::move(discard_callback)]() mutable {
217  if (weak_this) {
218  weak_this->Draw(std::move(resubmit_recorder), pipeline,
219  std::move(discard_callback));
220  }
221  }));
222  break;
223  }
224  default:
225  break;
226  }
227 
228  return raster_status;
229 }
230 
231 namespace {
232 sk_sp<SkImage> DrawSnapshot(
233  sk_sp<SkSurface> surface,
234  const std::function<void(SkCanvas*)>& draw_callback) {
235  if (surface == nullptr || surface->getCanvas() == nullptr) {
236  return nullptr;
237  }
238 
239  draw_callback(surface->getCanvas());
240  surface->getCanvas()->flush();
241 
242  sk_sp<SkImage> device_snapshot;
243  {
244  TRACE_EVENT0("flutter", "MakeDeviceSnpashot");
245  device_snapshot = surface->makeImageSnapshot();
246  }
247 
248  if (device_snapshot == nullptr) {
249  return nullptr;
250  }
251 
252  {
253  TRACE_EVENT0("flutter", "DeviceHostTransfer");
254  if (auto raster_image = device_snapshot->makeRasterImage()) {
255  return raster_image;
256  }
257  }
258 
259  return nullptr;
260 }
261 } // namespace
262 
263 sk_sp<SkImage> Rasterizer::DoMakeRasterSnapshot(
264  SkISize size,
265  std::function<void(SkCanvas*)> draw_callback) {
266  TRACE_EVENT0("flutter", __FUNCTION__);
267  sk_sp<SkImage> result;
268  SkImageInfo image_info = SkImageInfo::MakeN32Premul(
269  size.width(), size.height(), SkColorSpace::MakeSRGB());
270 
271  std::unique_ptr<Surface> pbuffer_surface;
272  Surface* snapshot_surface = nullptr;
273  if (surface_ && surface_->GetContext()) {
274  snapshot_surface = surface_.get();
275  } else if (snapshot_surface_producer_) {
276  pbuffer_surface = snapshot_surface_producer_->CreateSnapshotSurface();
277  if (pbuffer_surface && pbuffer_surface->GetContext())
278  snapshot_surface = pbuffer_surface.get();
279  }
280 
281  if (!snapshot_surface) {
282  // Raster surface is fine if there is no on screen surface. This might
283  // happen in case of software rendering.
284  sk_sp<SkSurface> sk_surface = SkSurface::MakeRaster(image_info);
285  result = DrawSnapshot(sk_surface, draw_callback);
286  } else {
287  delegate_.GetIsGpuDisabledSyncSwitch()->Execute(
289  .SetIfTrue([&] {
290  sk_sp<SkSurface> surface = SkSurface::MakeRaster(image_info);
291  result = DrawSnapshot(surface, draw_callback);
292  })
293  .SetIfFalse([&] {
294  FML_DCHECK(snapshot_surface);
295  auto context_switch =
296  snapshot_surface->MakeRenderContextCurrent();
297  if (!context_switch->GetResult()) {
298  return;
299  }
300 
301  GrRecordingContext* context = snapshot_surface->GetContext();
302  auto max_size = context->maxRenderTargetSize();
303  double scale_factor = std::min(
304  1.0, static_cast<double>(max_size) /
305  static_cast<double>(std::max(image_info.width(),
306  image_info.height())));
307 
308  // Scale down the render target size to the max supported by the
309  // GPU if necessary. Exceeding the max would otherwise cause a
310  // null result.
311  if (scale_factor < 1.0) {
312  image_info = image_info.makeWH(
313  static_cast<double>(image_info.width()) * scale_factor,
314  static_cast<double>(image_info.height()) * scale_factor);
315  }
316 
317  // When there is an on screen surface, we need a render target
318  // SkSurface because we want to access texture backed images.
319  sk_sp<SkSurface> sk_surface =
320  SkSurface::MakeRenderTarget(context, // context
321  SkBudgeted::kNo, // budgeted
322  image_info // image info
323  );
324  if (!sk_surface) {
325  FML_LOG(ERROR)
326  << "DoMakeRasterSnapshot can not create GPU render target";
327  return;
328  }
329 
330  sk_surface->getCanvas()->scale(scale_factor, scale_factor);
331  result = DrawSnapshot(sk_surface, draw_callback);
332  }));
333  }
334 
335  return result;
336 }
337 
338 sk_sp<SkImage> Rasterizer::MakeRasterSnapshot(
339  std::function<void(SkCanvas*)> draw_callback,
340  SkISize picture_size) {
341  return DoMakeRasterSnapshot(picture_size, draw_callback);
342 }
343 
344 sk_sp<SkImage> Rasterizer::MakeRasterSnapshot(sk_sp<SkPicture> picture,
345  SkISize picture_size) {
346  return DoMakeRasterSnapshot(picture_size,
347  [picture = std::move(picture)](SkCanvas* canvas) {
348  canvas->drawPicture(picture);
349  });
350 }
351 
352 sk_sp<SkImage> Rasterizer::ConvertToRasterImage(sk_sp<SkImage> image) {
353  TRACE_EVENT0("flutter", __FUNCTION__);
354 
355  // If the rasterizer does not have a surface with a GrContext, then it will
356  // be unable to render a cross-context SkImage. The caller will need to
357  // create the raster image on the IO thread.
358  if (surface_ == nullptr || surface_->GetContext() == nullptr) {
359  return nullptr;
360  }
361 
362  if (image == nullptr) {
363  return nullptr;
364  }
365 
366  SkISize image_size = image->dimensions();
367  return DoMakeRasterSnapshot(image_size,
368  [image = std::move(image)](SkCanvas* canvas) {
369  canvas->drawImage(image, 0, 0);
370  });
371 }
372 
373 RasterStatus Rasterizer::DoDraw(
374  std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder,
375  std::unique_ptr<flutter::LayerTree> layer_tree) {
376  FML_DCHECK(delegate_.GetTaskRunners()
379 
380  if (!layer_tree || !surface_) {
381  return RasterStatus::kFailed;
382  }
383 
384  frame_timings_recorder->RecordRasterStart(
385  fml::TimePoint::Now(), &compositor_context_->raster_cache());
386 
388  persistent_cache->ResetStoredNewShaders();
389 
390  RasterStatus raster_status =
391  DrawToSurface(*frame_timings_recorder, *layer_tree);
392  if (raster_status == RasterStatus::kSuccess) {
393  last_layer_tree_ = std::move(layer_tree);
394  } else if (raster_status == RasterStatus::kResubmit ||
395  raster_status == RasterStatus::kSkipAndRetry) {
396  resubmitted_layer_tree_ = std::move(layer_tree);
397  return raster_status;
398  } else if (raster_status == RasterStatus::kDiscarded) {
399  return raster_status;
400  }
401 
402  if (persistent_cache->IsDumpingSkp() &&
403  persistent_cache->StoredNewShaders()) {
404  auto screenshot =
406  persistent_cache->DumpSkp(*screenshot.data);
407  }
408 
409  // TODO(liyuqian): in Fuchsia, the rasterization doesn't finish when
410  // Rasterizer::DoDraw finishes. Future work is needed to adapt the timestamp
411  // for Fuchsia to capture SceneUpdateContext::ExecutePaintTasks.
412  delegate_.OnFrameRasterized(frame_timings_recorder->GetRecordedTime());
413 
414 // SceneDisplayLag events are disabled on Fuchsia.
415 // see: https://github.com/flutter/flutter/issues/56598
416 #if !defined(OS_FUCHSIA)
417  const fml::TimePoint raster_finish_time =
418  frame_timings_recorder->GetRasterEndTime();
419  fml::TimePoint frame_target_time =
420  frame_timings_recorder->GetVsyncTargetTime();
421  if (raster_finish_time > frame_target_time) {
422  fml::TimePoint latest_frame_target_time =
423  delegate_.GetLatestFrameTargetTime();
424  const auto frame_budget_millis = delegate_.GetFrameBudget().count();
425  if (latest_frame_target_time < raster_finish_time) {
426  latest_frame_target_time =
427  latest_frame_target_time +
428  fml::TimeDelta::FromMillisecondsF(frame_budget_millis);
429  }
430  const auto frame_lag =
431  (latest_frame_target_time - frame_target_time).ToMillisecondsF();
432  const int vsync_transitions_missed = round(frame_lag / frame_budget_millis);
434  "flutter", // category
435  "SceneDisplayLag", // name
436  raster_finish_time, // begin_time
437  latest_frame_target_time, // end_time
438  "frame_target_time", // arg_key_1
439  frame_target_time, // arg_val_1
440  "current_frame_target_time", // arg_key_2
441  latest_frame_target_time, // arg_val_2
442  "vsync_transitions_missed", // arg_key_3
443  vsync_transitions_missed // arg_val_3
444  );
445  }
446 #endif
447 
448  // Pipeline pressure is applied from a couple of places:
449  // rasterizer: When there are more items as of the time of Consume.
450  // animator (via shell): Frame gets produces every vsync.
451  // Enqueing here is to account for the following scenario:
452  // T = 1
453  // - one item (A) in the pipeline
454  // - rasterizer starts (and merges the threads)
455  // - pipeline consume result says no items to process
456  // T = 2
457  // - animator produces (B) to the pipeline
458  // - applies pipeline pressure via platform thread.
459  // T = 3
460  // - rasterizes finished (and un-merges the threads)
461  // - |Draw| for B yields as its on the wrong thread.
462  // This enqueue ensures that we attempt to consume from the right
463  // thread one more time after un-merge.
464  if (raster_thread_merger_) {
465  if (raster_thread_merger_->DecrementLease() ==
468  }
469  }
470 
471  return raster_status;
472 }
473 
474 RasterStatus Rasterizer::DrawToSurface(
475  FrameTimingsRecorder& frame_timings_recorder,
476  flutter::LayerTree& layer_tree) {
477  TRACE_EVENT0("flutter", "Rasterizer::DrawToSurface");
478  FML_DCHECK(surface_);
479 
480  RasterStatus raster_status;
481  if (surface_->AllowsDrawingWhenGpuDisabled()) {
482  raster_status = DrawToSurfaceUnsafe(frame_timings_recorder, layer_tree);
483  } else {
484  delegate_.GetIsGpuDisabledSyncSwitch()->Execute(
486  .SetIfTrue([&] { raster_status = RasterStatus::kDiscarded; })
487  .SetIfFalse([&] {
488  raster_status =
489  DrawToSurfaceUnsafe(frame_timings_recorder, layer_tree);
490  }));
491  }
492 
493  return raster_status;
494 }
495 
496 /// Unsafe because it assumes we have access to the GPU which isn't the case
497 /// when iOS is backgrounded, for example.
498 /// \see Rasterizer::DrawToSurface
499 RasterStatus Rasterizer::DrawToSurfaceUnsafe(
500  FrameTimingsRecorder& frame_timings_recorder,
501  flutter::LayerTree& layer_tree) {
502  TRACE_EVENT0("flutter", "Rasterizer::DrawToSurfaceUnsafe");
503  FML_DCHECK(surface_);
504 
505  compositor_context_->ui_time().SetLapTime(
506  frame_timings_recorder.GetBuildDuration());
507 
508  SkCanvas* embedder_root_canvas = nullptr;
509  if (external_view_embedder_) {
510  external_view_embedder_->BeginFrame(
511  layer_tree.frame_size(), surface_->GetContext(),
512  layer_tree.device_pixel_ratio(), raster_thread_merger_);
513  embedder_root_canvas = external_view_embedder_->GetRootCanvas();
514  }
515 
516  // On Android, the external view embedder deletes surfaces in `BeginFrame`.
517  //
518  // Deleting a surface also clears the GL context. Therefore, acquire the
519  // frame after calling `BeginFrame` as this operation resets the GL context.
520  auto frame = surface_->AcquireFrame(layer_tree.frame_size());
521  if (frame == nullptr) {
522  return RasterStatus::kFailed;
523  }
524 
525  // If the external view embedder has specified an optional root surface, the
526  // root surface transformation is set by the embedder instead of
527  // having to apply it here.
528  SkMatrix root_surface_transformation =
529  embedder_root_canvas ? SkMatrix{} : surface_->GetRootTransformation();
530 
531  auto root_surface_canvas =
532  embedder_root_canvas ? embedder_root_canvas : frame->SkiaCanvas();
533 
534  auto compositor_frame = compositor_context_->AcquireFrame(
535  surface_->GetContext(), // skia GrContext
536  root_surface_canvas, // root surface canvas
537  external_view_embedder_.get(), // external view embedder
538  root_surface_transformation, // root surface transformation
539  true, // instrumentation enabled
540  frame->supports_readback(), // surface supports pixel reads
541  raster_thread_merger_ // thread merger
542  );
543  if (compositor_frame) {
544  RasterStatus raster_status = compositor_frame->Raster(layer_tree, false);
545  if (raster_status == RasterStatus::kFailed ||
546  raster_status == RasterStatus::kSkipAndRetry) {
547  return raster_status;
548  }
549  if (external_view_embedder_ &&
550  (!raster_thread_merger_ || raster_thread_merger_->IsMerged())) {
551  FML_DCHECK(!frame->IsSubmitted());
552  external_view_embedder_->SubmitFrame(surface_->GetContext(),
553  std::move(frame));
554  } else {
555  frame->Submit();
556  }
557 
558  frame_timings_recorder.RecordRasterEnd(
559  &compositor_context_->raster_cache());
560  FireNextFrameCallbackIfPresent();
561 
562  if (surface_->GetContext()) {
563  TRACE_EVENT0("flutter", "PerformDeferredSkiaCleanup");
564  surface_->GetContext()->performDeferredCleanup(kSkiaCleanupExpiration);
565  }
566 
567  return raster_status;
568  }
569 
570  return RasterStatus::kFailed;
571 }
572 
573 static sk_sp<SkData> ScreenshotLayerTreeAsPicture(
574  flutter::LayerTree* tree,
576  FML_DCHECK(tree != nullptr);
577  SkPictureRecorder recorder;
578  recorder.beginRecording(
579  SkRect::MakeWH(tree->frame_size().width(), tree->frame_size().height()));
580 
581  SkMatrix root_surface_transformation;
582  root_surface_transformation.reset();
583 
584  // TODO(amirh): figure out how to take a screenshot with embedded UIView.
585  // https://github.com/flutter/flutter/issues/23435
586  auto frame = compositor_context.AcquireFrame(
587  nullptr, recorder.getRecordingCanvas(), nullptr,
588  root_surface_transformation, false, true, nullptr);
589  frame->Raster(*tree, true);
590 
591 #if defined(OS_FUCHSIA)
592  SkSerialProcs procs = {0};
593  procs.fImageProc = SerializeImageWithoutData;
594  procs.fTypefaceProc = SerializeTypefaceWithoutData;
595 #else
596  SkSerialProcs procs = {0};
597  procs.fTypefaceProc = SerializeTypefaceWithData;
598 #endif
599 
600  return recorder.finishRecordingAsPicture()->serialize(&procs);
601 }
602 
603 static sk_sp<SkSurface> CreateSnapshotSurface(GrDirectContext* surface_context,
604  const SkISize& size) {
605  const auto image_info = SkImageInfo::MakeN32Premul(
606  size.width(), size.height(), SkColorSpace::MakeSRGB());
607  if (surface_context) {
608  // There is a rendering surface that may contain textures that are going to
609  // be referenced in the layer tree about to be drawn.
610  return SkSurface::MakeRenderTarget(surface_context, //
611  SkBudgeted::kNo, //
612  image_info //
613  );
614  }
615 
616  // There is no rendering surface, assume no GPU textures are present and
617  // create a raster surface.
618  return SkSurface::MakeRaster(image_info);
619 }
620 
621 sk_sp<SkData> Rasterizer::ScreenshotLayerTreeAsImage(
622  flutter::LayerTree* tree,
624  GrDirectContext* surface_context,
625  bool compressed) {
626  // Attempt to create a snapshot surface depending on whether we have access to
627  // a valid GPU rendering context.
628  auto snapshot_surface =
629  CreateSnapshotSurface(surface_context, tree->frame_size());
630  if (snapshot_surface == nullptr) {
631  FML_LOG(ERROR) << "Screenshot: unable to create snapshot surface";
632  return nullptr;
633  }
634 
635  // Draw the current layer tree into the snapshot surface.
636  auto* canvas = snapshot_surface->getCanvas();
637 
638  // There is no root surface transformation for the screenshot layer. Reset the
639  // matrix to identity.
640  SkMatrix root_surface_transformation;
641  root_surface_transformation.reset();
642 
643  // snapshot_surface->makeImageSnapshot needs the GL context to be set if the
644  // render context is GL. frame->Raster() pops the gl context in platforms that
645  // gl context switching are used. (For example, older iOS that uses GL) We
646  // reset the GL context using the context switch.
647  auto context_switch = surface_->MakeRenderContextCurrent();
648  if (!context_switch->GetResult()) {
649  FML_LOG(ERROR) << "Screenshot: unable to make image screenshot";
650  return nullptr;
651  }
652 
653  auto frame = compositor_context.AcquireFrame(surface_context, canvas, nullptr,
654  root_surface_transformation,
655  false, true, nullptr);
656  canvas->clear(SK_ColorTRANSPARENT);
657  frame->Raster(*tree, true);
658  canvas->flush();
659 
660  // Prepare an image from the surface, this image may potentially be on th GPU.
661  auto potentially_gpu_snapshot = snapshot_surface->makeImageSnapshot();
662  if (!potentially_gpu_snapshot) {
663  FML_LOG(ERROR) << "Screenshot: unable to make image screenshot";
664  return nullptr;
665  }
666 
667  // Copy the GPU image snapshot into CPU memory.
668  auto cpu_snapshot = potentially_gpu_snapshot->makeRasterImage();
669  if (!cpu_snapshot) {
670  FML_LOG(ERROR) << "Screenshot: unable to make raster image";
671  return nullptr;
672  }
673 
674  // If the caller want the pixels to be compressed, there is a Skia utility to
675  // compress to PNG. Use that.
676  if (compressed) {
677  return cpu_snapshot->encodeToData();
678  }
679 
680  // Copy it into a bitmap and return the same.
681  SkPixmap pixmap;
682  if (!cpu_snapshot->peekPixels(&pixmap)) {
683  FML_LOG(ERROR) << "Screenshot: unable to obtain bitmap pixels";
684  return nullptr;
685  }
686  return SkData::MakeWithCopy(pixmap.addr32(), pixmap.computeByteSize());
687 }
688 
691  bool base64_encode) {
692  auto* layer_tree = GetLastLayerTree();
693  if (layer_tree == nullptr) {
694  FML_LOG(ERROR) << "Last layer tree was null when screenshotting.";
695  return {};
696  }
697 
698  sk_sp<SkData> data = nullptr;
699 
700  GrDirectContext* surface_context =
701  surface_ ? surface_->GetContext() : nullptr;
702 
703  switch (type) {
705  data = ScreenshotLayerTreeAsPicture(layer_tree, *compositor_context_);
706  break;
708  data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_,
709  surface_context, false);
710  break;
712  data = ScreenshotLayerTreeAsImage(layer_tree, *compositor_context_,
713  surface_context, true);
714  break;
715  }
716 
717  if (data == nullptr) {
718  FML_LOG(ERROR) << "Screenshot data was null.";
719  return {};
720  }
721 
722  if (base64_encode) {
723  size_t b64_size = SkBase64::Encode(data->data(), data->size(), nullptr);
724  auto b64_data = SkData::MakeUninitialized(b64_size);
725  SkBase64::Encode(data->data(), data->size(), b64_data->writable_data());
726  return Rasterizer::Screenshot{b64_data, layer_tree->frame_size()};
727  }
728 
729  return Rasterizer::Screenshot{data, layer_tree->frame_size()};
730 }
731 
733  next_frame_callback_ = callback;
734 }
735 
737  const std::shared_ptr<ExternalViewEmbedder>& view_embedder) {
738  external_view_embedder_ = view_embedder;
739 }
740 
742  std::unique_ptr<SnapshotSurfaceProducer> producer) {
743  snapshot_surface_producer_ = std::move(producer);
744 }
745 
747  return raster_thread_merger_;
748 }
749 
750 void Rasterizer::FireNextFrameCallbackIfPresent() {
751  if (!next_frame_callback_) {
752  return;
753  }
754  // It is safe for the callback to set a new callback.
755  auto callback = next_frame_callback_;
756  next_frame_callback_ = nullptr;
757  callback();
758 }
759 
760 void Rasterizer::SetResourceCacheMaxBytes(size_t max_bytes, bool from_user) {
761  user_override_resource_cache_bytes_ |= from_user;
762 
763  if (!from_user && user_override_resource_cache_bytes_) {
764  // We should not update the setting here if a user has explicitly set a
765  // value for this over the flutter/skia channel.
766  return;
767  }
768 
769  max_cache_bytes_ = max_bytes;
770  if (!surface_) {
771  return;
772  }
773 
774  GrDirectContext* context = surface_->GetContext();
775  if (context) {
776  auto context_switch = surface_->MakeRenderContextCurrent();
777  if (!context_switch->GetResult()) {
778  return;
779  }
780 
781  int max_resources;
782  context->getResourceCacheLimits(&max_resources, nullptr);
783  context->setResourceCacheLimits(max_resources, max_bytes);
784  }
785 }
786 
787 std::optional<size_t> Rasterizer::GetResourceCacheMaxBytes() const {
788  if (!surface_) {
789  return std::nullopt;
790  }
791  GrDirectContext* context = surface_->GetContext();
792  if (context) {
793  size_t max_bytes;
794  context->getResourceCacheLimits(nullptr, &max_bytes);
795  return max_bytes;
796  }
797  return std::nullopt;
798 }
799 
801 
802 Rasterizer::Screenshot::Screenshot(sk_sp<SkData> p_data, SkISize p_size)
803  : data(std::move(p_data)), frame_size(p_size) {}
804 
805 Rasterizer::Screenshot::Screenshot(const Screenshot& other) = default;
806 
808 
809 } // namespace flutter
static fml::RefPtr< fml::RasterThreadMerger > CreateOrShareThreadMerger(const fml::RefPtr< fml::RasterThreadMerger > &parent_merger, TaskQueueId platform_id, TaskQueueId raster_id)
PipelineConsumeResult
Definition: pipeline.h:19
KeyCallType type
std::optional< size_t > GetResourceCacheMaxBytes() const
The current value of Skia&#39;s resource cache size, if a surface is present.
Definition: rasterizer.cc:787
virtual TaskQueueId GetTaskQueueId()
Definition: task_runner.cc:38
void Setup(std::unique_ptr< Surface > surface)
Rasterizers may be created well before an on-screen surface is available for rendering. Shells usually create a rasterizer in their constructors. Once an on-screen surface is available however, one may be provided to the rasterizer using this call. No rendering may occur before this call. The surface is held till the balancing call to Rasterizer::Teardown is made. Calling a setup before tearing down the previous surface (if this is not the first time the surface has been set up) is user error.
Definition: rasterizer.cc:51
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:90
void DumpSkp(const SkData &data)
MockDelegate delegate_
#define FML_DCHECK(condition)
Definition: logging.h:86
virtual std::shared_ptr< const fml::SyncSwitch > GetIsGpuDisabledSyncSwitch() const =0
const SkISize & frame_size() const
Definition: layer_tree.h:45
void SetSnapshotSurfaceProducer(std::unique_ptr< SnapshotSurfaceProducer > producer)
Set the snapshot surface producer. This is done on shell initialization. This is non-null on platform...
Definition: rasterizer.cc:741
void SetExternalViewEmbedder(const std::shared_ptr< ExternalViewEmbedder > &view_embedder)
Set the External View Embedder. This is done on shell initialization. This is non-null on platforms t...
Definition: rasterizer.cc:736
fml::TaskRunnerAffineWeakPtr< Rasterizer > GetWeakPtr() const
Gets a weak pointer to the rasterizer. The rasterizer may only be accessed on the raster task runner...
Definition: rasterizer.cc:42
Definition: ref_ptr.h:252
Dart_NativeFunction function
Definition: fuchsia.cc:51
fml::RefPtr< fml::TaskRunner > GetPlatformTaskRunner() const
Definition: task_runners.cc:30
GAsyncResult * result
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
void Teardown()
Releases the previously set up on-screen render surface and collects associated resources. No more rendering may occur till the next call to Rasterizer::Setup with a new render surface. Calling a teardown without a setup is user error.
Definition: rasterizer.cc:84
~Screenshot()
Destroys the screenshot object and releases underlying data.
#define FML_LOG(severity)
Definition: logging.h:65
static constexpr std::chrono::milliseconds kSkiaCleanupExpiration(15000)
sk_sp< SkData > SerializeImageWithoutData(SkImage *image, void *ctx)
Screenshot ScreenshotLastLayerTree(ScreenshotType type, bool base64_encode)
Screenshots the last layer tree to one of the supported screenshot types and optionally Base 64 encod...
Definition: rasterizer.cc:689
fml::RefPtr< fml::TaskRunner > GetRasterTaskRunner() const
Definition: task_runners.cc:42
FlKeyEvent FlKeyResponderAsyncCallback callback
virtual const TaskRunners & GetTaskRunners() const =0
Task runners used by the shell.
sk_sp< SkData > SerializeTypefaceWithData(SkTypeface *typeface, void *ctx)
virtual fml::TimePoint GetLatestFrameTargetTime() const =0
void DisableThreadMergerIfNeeded()
Disables the thread merger if the external view embedder supports dynamic thread merging.
Definition: rasterizer.cc:112
sk_sp< SkData > SerializeTypefaceWithoutData(SkTypeface *typeface, void *ctx)
std::function< void()> closure
Definition: closure.h:14
internal::CopyableLambda< T > MakeCopyable(T lambda)
Definition: make_copyable.h:57
RasterThreadStatus DecrementLease()
void EnableThreadMergerIfNeeded()
Enables the thread merger if the external view embedder supports dynamic thread merging.
Definition: rasterizer.cc:106
A POD type used to return the screenshot data along with the size of the frame.
Definition: rasterizer.h:275
void SetMergeUnmergeCallback(const fml::closure &callback)
fml::TimeDelta GetBuildDuration() const
Duration of the frame build time.
Abstract Base Class that represents where we will be rendering content.
Definition: surface.h:18
ScreenshotType
The type of the screenshot to obtain of the previously rendered layer tree.
Definition: rasterizer.h:245
#define TRACE_EVENT_WITH_FRAME_NUMBER(recorder, category_group, name)
Definition: frame_timings.h:16
void SetNextFrameCallback(const fml::closure &callback)
Sets a callback that will be executed when the next layer tree in rendered to the on-screen surface...
Definition: rasterizer.cc:732
Screenshot()
Creates an empty screenshot.
Definition: rasterizer.cc:800
virtual const fml::RefPtr< fml::RasterThreadMerger > GetParentRasterThreadMerger() const =0
The raster thread merger from parent shell&#39;s rasterizer.
static sk_sp< SkData > ScreenshotLayerTreeAsPicture(flutter::LayerTree *tree, flutter::CompositorContext &compositor_context)
Definition: rasterizer.cc:573
Used to forward events from the rasterizer to interested subsystems. Currently, the shell sets itself...
Definition: rasterizer.h:57
std::function< bool(flutter::LayerTree &)> LayerTreeDiscardCallback
Definition: rasterizer.h:203
virtual std::unique_ptr< GLContextResult > MakeRenderContextCurrent()
Definition: surface.cc:13
fml::RefPtr< fml::RasterThreadMerger > GetRasterThreadMerger()
Returns the raster thread merger used by this rasterizer. This may be nullptr.
Definition: rasterizer.cc:746
std::function< void(ResourcePtr)> Consumer
Definition: pipeline.h:136
T * get() const
Definition: ref_ptr.h:112
static sk_sp< SkSurface > CreateSnapshotSurface(GrDirectContext *surface_context, const SkISize &size)
Definition: rasterizer.cc:603
~Rasterizer()
Destroys the rasterizer. This must happen on the raster task runner. All GPU resources are collected ...
FrameTiming RecordRasterEnd(const RasterCache *cache=nullptr)
flutter::TextureRegistry * GetTextureRegistry()
Gets the registry of external textures currently in use by the rasterizer. These textures may be upda...
Definition: rasterizer.cc:137
float device_pixel_ratio() const
Definition: layer_tree.h:46
fml::TaskRunnerAffineWeakPtr< SnapshotDelegate > GetSnapshotDelegate() const
Definition: rasterizer.cc:46
static PersistentCache * GetCacheForProcess()
virtual fml::Milliseconds GetFrameBudget()=0
RasterStatus Draw(std::unique_ptr< FrameTimingsRecorder > frame_timings_recorder, std::shared_ptr< Pipeline< flutter::LayerTree >> pipeline, LayerTreeDiscardCallback discard_callback=NoDiscard)
Takes the next item from the layer tree pipeline and executes the raster thread frame workload for th...
Definition: rasterizer.cc:155
virtual std::unique_ptr< ScopedFrame > AcquireFrame(GrDirectContext *gr_context, SkCanvas *canvas, ExternalViewEmbedder *view_embedder, const SkMatrix &root_surface_transformation, bool instrumentation_enabled, bool surface_supports_readback, fml::RefPtr< fml::RasterThreadMerger > raster_thread_merger)
Rasterizer(Delegate &delegate)
Creates a new instance of a rasterizer. Rasterizers may only be created on the raster task runner...
Definition: rasterizer.cc:31
virtual bool RunsTasksOnCurrentThread()
Definition: task_runner.cc:43
#define FML_DLOG(severity)
Definition: logging.h:85
void TraceEventAsyncComplete(TraceArg category_group, TraceArg name, TimePoint begin, TimePoint end)
Definition: trace_event.cc:333
void DrawLastLayerTree(std::unique_ptr< FrameTimingsRecorder > frame_timings_recorder)
Draws a last layer tree to the render surface. This may seem entirely redundant at first glance...
Definition: rasterizer.cc:145
flutter::CompositorContext * compositor_context()
Returns a pointer to the compositor context used by this rasterizer. This pointer will never be nullp...
Definition: rasterizer.h:375
flutter::LayerTree * GetLastLayerTree()
Sometimes, it may be necessary to render the same frame again without having to wait for the framewor...
Definition: rasterizer.cc:141
void SetResourceCacheMaxBytes(size_t max_bytes, bool from_user)
Skia has no notion of time. To work around the performance implications of this, it may cache GPU res...
Definition: rasterizer.cc:760
virtual void PostTask(const fml::closure &task) override
Definition: task_runner.cc:24
virtual GrDirectContext * GetContext()=0
virtual void OnFrameRasterized(const FrameTiming &frame_timing)=0
Notifies the delegate that a frame has been rendered. The rasterizer collects profiling information f...
static TimePoint Now()
Definition: time_point.cc:39
static constexpr TimeDelta FromMillisecondsF(double millis)
Definition: time_delta.h:57
void NotifyLowMemoryWarning() const
Notifies the rasterizer that there is a low memory situation and it must purge as many unnecessary re...
Definition: rasterizer.cc:118
Represents the 2 code paths available when calling |SyncSwitch::Execute|.
Definition: sync_switch.h:24