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/flow/layers/offscreen_surface.h"
14#include "flutter/fml/time/time_delta.h"
15#include "flutter/fml/time/time_point.h"
16#include "flutter/shell/common/serialization_callbacks.h"
17#include "fml/make_copyable.h"
18#include "third_party/skia/include/core/SkColorSpace.h"
19#include "third_party/skia/include/core/SkData.h"
20#include "third_party/skia/include/core/SkImage.h"
21#include "third_party/skia/include/core/SkImageInfo.h"
22#include "third_party/skia/include/core/SkMatrix.h"
23#include "third_party/skia/include/core/SkPictureRecorder.h"
24#include "third_party/skia/include/core/SkRect.h"
25#include "third_party/skia/include/core/SkSerialProcs.h"
26#include "third_party/skia/include/core/SkSize.h"
27#include "third_party/skia/include/core/SkSurface.h"
28#include "third_party/skia/include/gpu/GrBackendSurface.h"
29#include "third_party/skia/include/gpu/GrDirectContext.h"
30#include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
31#include "third_party/skia/include/utils/SkBase64.h"
32
33namespace flutter {
34
35// The rasterizer will tell Skia to purge cached resources that have not been
36// used within this interval.
37static constexpr std::chrono::milliseconds kSkiaCleanupExpiration(15000);
38
39Rasterizer::Rasterizer(Delegate& delegate,
40 MakeGpuImageBehavior gpu_image_behavior)
41 : delegate_(delegate),
42 gpu_image_behavior_(gpu_image_behavior),
43 compositor_context_(std::make_unique<flutter::CompositorContext>(args&: *this)),
44 user_override_resource_cache_bytes_(false),
45 snapshot_controller_(
46 SnapshotController::Make(delegate: *this, settings: delegate.GetSettings())),
47 weak_factory_(this) {
48 FML_DCHECK(compositor_context_);
49}
50
51Rasterizer::~Rasterizer() = default;
52
53fml::TaskRunnerAffineWeakPtr<Rasterizer> Rasterizer::GetWeakPtr() const {
54 return weak_factory_.GetWeakPtr();
55}
56
57fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> Rasterizer::GetSnapshotDelegate()
58 const {
59 return weak_factory_.GetWeakPtr();
60}
61
62void Rasterizer::SetImpellerContext(
63 std::weak_ptr<impeller::Context> impeller_context) {
64 impeller_context_ = std::move(impeller_context);
65}
66
67void Rasterizer::Setup(std::unique_ptr<Surface> surface) {
68 surface_ = std::move(surface);
69
70 if (max_cache_bytes_.has_value()) {
71 SetResourceCacheMaxBytes(max_bytes: max_cache_bytes_.value(),
72 from_user: user_override_resource_cache_bytes_);
73 }
74
75 auto context_switch = surface_->MakeRenderContextCurrent();
76 if (context_switch->GetResult()) {
77 compositor_context_->OnGrContextCreated();
78 }
79
80 if (external_view_embedder_ &&
81 external_view_embedder_->SupportsDynamicThreadMerging() &&
82 !raster_thread_merger_) {
83 const auto platform_id =
84 delegate_.GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId();
85 const auto gpu_id =
86 delegate_.GetTaskRunners().GetRasterTaskRunner()->GetTaskQueueId();
87 raster_thread_merger_ = fml::RasterThreadMerger::CreateOrShareThreadMerger(
88 parent_merger: delegate_.GetParentRasterThreadMerger(), platform_id, raster_id: gpu_id);
89 }
90 if (raster_thread_merger_) {
91 raster_thread_merger_->SetMergeUnmergeCallback([this]() {
92 // Clear the GL context after the thread configuration has changed.
93 if (surface_) {
94 surface_->ClearRenderContext();
95 }
96 });
97 }
98}
99
100void Rasterizer::TeardownExternalViewEmbedder() {
101 if (external_view_embedder_) {
102 external_view_embedder_->Teardown();
103 }
104}
105
106void Rasterizer::Teardown() {
107 if (surface_) {
108 auto context_switch = surface_->MakeRenderContextCurrent();
109 if (context_switch->GetResult()) {
110 compositor_context_->OnGrContextDestroyed();
111 if (auto* context = surface_->GetContext()) {
112 context->purgeUnlockedResources(/*scratchResourcesOnly=*/false);
113 }
114 }
115 surface_.reset();
116 }
117
118 last_layer_tree_.reset();
119
120 if (raster_thread_merger_.get() != nullptr &&
121 raster_thread_merger_.get()->IsMerged()) {
122 FML_DCHECK(raster_thread_merger_->IsEnabled());
123 raster_thread_merger_->UnMergeNowIfLastOne();
124 raster_thread_merger_->SetMergeUnmergeCallback(nullptr);
125 }
126}
127
128void Rasterizer::EnableThreadMergerIfNeeded() {
129 if (raster_thread_merger_) {
130 raster_thread_merger_->Enable();
131 }
132}
133
134void Rasterizer::DisableThreadMergerIfNeeded() {
135 if (raster_thread_merger_) {
136 raster_thread_merger_->Disable();
137 }
138}
139
140void Rasterizer::NotifyLowMemoryWarning() const {
141 if (!surface_) {
142 FML_DLOG(INFO)
143 << "Rasterizer::NotifyLowMemoryWarning called with no surface.";
144 return;
145 }
146 auto context = surface_->GetContext();
147 if (!context) {
148 FML_DLOG(INFO)
149 << "Rasterizer::NotifyLowMemoryWarning called with no GrContext.";
150 return;
151 }
152 auto context_switch = surface_->MakeRenderContextCurrent();
153 if (!context_switch->GetResult()) {
154 return;
155 }
156 context->performDeferredCleanup(msNotUsed: std::chrono::milliseconds(0));
157}
158
159std::shared_ptr<flutter::TextureRegistry> Rasterizer::GetTextureRegistry() {
160 return compositor_context_->texture_registry();
161}
162
163GrDirectContext* Rasterizer::GetGrContext() {
164 return surface_ ? surface_->GetContext() : nullptr;
165}
166
167flutter::LayerTree* Rasterizer::GetLastLayerTree() {
168 return last_layer_tree_.get();
169}
170
171void Rasterizer::DrawLastLayerTree(
172 std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
173 if (!last_layer_tree_ || !surface_) {
174 return;
175 }
176 RasterStatus raster_status = DrawToSurface(
177 frame_timings_recorder&: *frame_timings_recorder, layer_tree&: *last_layer_tree_, device_pixel_ratio: last_device_pixel_ratio_);
178
179 // EndFrame should perform cleanups for the external_view_embedder.
180 if (external_view_embedder_ && external_view_embedder_->GetUsedThisFrame()) {
181 bool should_resubmit_frame = ShouldResubmitFrame(raster_status);
182 external_view_embedder_->SetUsedThisFrame(false);
183 external_view_embedder_->EndFrame(should_resubmit_frame,
184 raster_thread_merger: raster_thread_merger_);
185 }
186}
187
188RasterStatus Rasterizer::Draw(
189 const std::shared_ptr<LayerTreePipeline>& pipeline,
190 LayerTreeDiscardCallback discard_callback) {
191 TRACE_EVENT0("flutter", "GPURasterizer::Draw");
192 if (raster_thread_merger_ &&
193 !raster_thread_merger_->IsOnRasterizingThread()) {
194 // we yield and let this frame be serviced on the right thread.
195 return RasterStatus::kYielded;
196 }
197 FML_DCHECK(delegate_.GetTaskRunners()
198 .GetRasterTaskRunner()
199 ->RunsTasksOnCurrentThread());
200
201 RasterStatus raster_status = RasterStatus::kFailed;
202 LayerTreePipeline::Consumer consumer =
203 [&](std::unique_ptr<LayerTreeItem> item) {
204 std::unique_ptr<LayerTree> layer_tree = std::move(item->layer_tree);
205 std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder =
206 std::move(item->frame_timings_recorder);
207 float device_pixel_ratio = item->device_pixel_ratio;
208 if (discard_callback(*layer_tree.get())) {
209 raster_status = RasterStatus::kDiscarded;
210 } else {
211 raster_status = DoDraw(frame_timings_recorder: std::move(frame_timings_recorder),
212 layer_tree: std::move(layer_tree), device_pixel_ratio);
213 }
214 };
215
216 PipelineConsumeResult consume_result = pipeline->Consume(consumer);
217 if (consume_result == PipelineConsumeResult::NoneAvailable) {
218 return RasterStatus::kFailed;
219 }
220 // if the raster status is to resubmit the frame, we push the frame to the
221 // front of the queue and also change the consume status to more available.
222
223 bool should_resubmit_frame = ShouldResubmitFrame(raster_status);
224 if (should_resubmit_frame) {
225 auto resubmitted_layer_tree_item = std::make_unique<LayerTreeItem>(
226 args: std::move(resubmitted_layer_tree_), args: std::move(resubmitted_recorder_),
227 args&: resubmitted_pixel_ratio_);
228 auto front_continuation = pipeline->ProduceIfEmpty();
229 PipelineProduceResult result =
230 front_continuation.Complete(resource: std::move(resubmitted_layer_tree_item));
231 if (result.success) {
232 consume_result = PipelineConsumeResult::MoreAvailable;
233 }
234 } else if (raster_status == RasterStatus::kEnqueuePipeline) {
235 consume_result = PipelineConsumeResult::MoreAvailable;
236 }
237
238 // EndFrame should perform cleanups for the external_view_embedder.
239 if (external_view_embedder_ && external_view_embedder_->GetUsedThisFrame()) {
240 external_view_embedder_->SetUsedThisFrame(false);
241 external_view_embedder_->EndFrame(should_resubmit_frame,
242 raster_thread_merger: raster_thread_merger_);
243 }
244
245 // Consume as many pipeline items as possible. But yield the event loop
246 // between successive tries.
247 switch (consume_result) {
248 case PipelineConsumeResult::MoreAvailable: {
249 delegate_.GetTaskRunners().GetRasterTaskRunner()->PostTask(
250 task: fml::MakeCopyable(
251 lambda: [weak_this = weak_factory_.GetWeakPtr(), pipeline,
252 discard_callback = std::move(discard_callback)]() mutable {
253 if (weak_this) {
254 weak_this->Draw(pipeline, discard_callback: std::move(discard_callback));
255 }
256 }));
257 break;
258 }
259 default:
260 break;
261 }
262
263 return raster_status;
264}
265
266bool Rasterizer::ShouldResubmitFrame(const RasterStatus& raster_status) {
267 return raster_status == RasterStatus::kResubmit ||
268 raster_status == RasterStatus::kSkipAndRetry;
269}
270
271namespace {
272std::unique_ptr<SnapshotDelegate::GpuImageResult> MakeBitmapImage(
273 const sk_sp<DisplayList>& display_list,
274 const SkImageInfo& image_info) {
275 FML_DCHECK(display_list);
276 // Use 16384 as a proxy for the maximum texture size for a GPU image.
277 // This is meant to be large enough to avoid false positives in test contexts,
278 // but not so artificially large to be completely unrealistic on any platform.
279 // This limit is taken from the Metal specification. D3D, Vulkan, and GL
280 // generally have lower limits.
281 if (image_info.width() > 16384 || image_info.height() > 16384) {
282 return std::make_unique<SnapshotDelegate::GpuImageResult>(
283 args: GrBackendTexture(), args: nullptr, args: nullptr,
284 args: "unable to create bitmap render target at specified size " +
285 std::to_string(val: image_info.width()) + "x" +
286 std::to_string(val: image_info.height()));
287 };
288
289 sk_sp<SkSurface> surface = SkSurfaces::Raster(imageInfo: image_info);
290 auto canvas = DlSkCanvasAdapter(surface->getCanvas());
291 canvas.Clear(color: DlColor::kTransparent());
292 canvas.DrawDisplayList(display_list);
293
294 sk_sp<SkImage> image = surface->makeImageSnapshot();
295 return std::make_unique<SnapshotDelegate::GpuImageResult>(
296 args: GrBackendTexture(), args: nullptr, args&: image,
297 args: image ? "" : "Unable to create image");
298}
299} // namespace
300
301std::unique_ptr<Rasterizer::GpuImageResult> Rasterizer::MakeSkiaGpuImage(
302 sk_sp<DisplayList> display_list,
303 const SkImageInfo& image_info) {
304 TRACE_EVENT0("flutter", "Rasterizer::MakeGpuImage");
305 FML_DCHECK(display_list);
306
307 std::unique_ptr<SnapshotDelegate::GpuImageResult> result;
308 delegate_.GetIsGpuDisabledSyncSwitch()->Execute(
309 handlers: fml::SyncSwitch::Handlers()
310 .SetIfTrue([&result, &image_info, &display_list] {
311 // TODO(dnfield): This isn't safe if display_list contains any GPU
312 // resources like an SkImage_gpu.
313 result = MakeBitmapImage(display_list, image_info);
314 })
315 .SetIfFalse([&result, &image_info, &display_list,
316 surface = surface_.get(),
317 gpu_image_behavior = gpu_image_behavior_] {
318 if (!surface ||
319 gpu_image_behavior == MakeGpuImageBehavior::kBitmap) {
320 // TODO(dnfield): This isn't safe if display_list contains any GPU
321 // resources like an SkImage_gpu.
322 result = MakeBitmapImage(display_list, image_info);
323 return;
324 }
325
326 auto context_switch = surface->MakeRenderContextCurrent();
327 if (!context_switch->GetResult()) {
328 result = MakeBitmapImage(display_list, image_info);
329 return;
330 }
331
332 auto* context = surface->GetContext();
333 if (!context) {
334 result = MakeBitmapImage(display_list, image_info);
335 return;
336 }
337
338 GrBackendTexture texture = context->createBackendTexture(
339 width: image_info.width(), height: image_info.height(), image_info.colorType(),
340 GrMipmapped::kNo, GrRenderable::kYes);
341 if (!texture.isValid()) {
342 result = std::make_unique<SnapshotDelegate::GpuImageResult>(
343 args: GrBackendTexture(), args: nullptr, args: nullptr,
344 args: "unable to create texture render target at specified size " +
345 std::to_string(val: image_info.width()) + "x" +
346 std::to_string(val: image_info.height()));
347 return;
348 }
349
350 sk_sp<SkSurface> sk_surface = SkSurfaces::WrapBackendTexture(
351 context, backendTexture: texture, origin: kTopLeft_GrSurfaceOrigin, /*sampleCnt=*/0,
352 colorType: image_info.colorType(), colorSpace: image_info.refColorSpace(), surfaceProps: nullptr);
353 if (!sk_surface) {
354 result = std::make_unique<SnapshotDelegate::GpuImageResult>(
355 args: GrBackendTexture(), args: nullptr, args: nullptr,
356 args: "unable to create rendering surface for image");
357 return;
358 }
359
360 auto canvas = DlSkCanvasAdapter(sk_surface->getCanvas());
361 canvas.Clear(color: DlColor::kTransparent());
362 canvas.DrawDisplayList(display_list);
363
364 result = std::make_unique<SnapshotDelegate::GpuImageResult>(
365 args&: texture, args: sk_ref_sp(obj: context), args: nullptr, args: "");
366 }));
367 return result;
368}
369
370sk_sp<DlImage> Rasterizer::MakeRasterSnapshot(sk_sp<DisplayList> display_list,
371 SkISize picture_size) {
372 return snapshot_controller_->MakeRasterSnapshot(display_list, size: picture_size);
373}
374
375sk_sp<SkImage> Rasterizer::ConvertToRasterImage(sk_sp<SkImage> image) {
376 TRACE_EVENT0("flutter", __FUNCTION__);
377 return snapshot_controller_->ConvertToRasterImage(image);
378}
379
380fml::Milliseconds Rasterizer::GetFrameBudget() const {
381 return delegate_.GetFrameBudget();
382};
383
384RasterStatus Rasterizer::DoDraw(
385 std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder,
386 std::unique_ptr<flutter::LayerTree> layer_tree,
387 float device_pixel_ratio) {
388 TRACE_EVENT_WITH_FRAME_NUMBER(frame_timings_recorder, "flutter",
389 "Rasterizer::DoDraw", /*flow_id_count=*/0,
390 /*flow_ids=*/nullptr);
391 FML_DCHECK(delegate_.GetTaskRunners()
392 .GetRasterTaskRunner()
393 ->RunsTasksOnCurrentThread());
394
395 if (!layer_tree || !surface_) {
396 return RasterStatus::kFailed;
397 }
398
399 PersistentCache* persistent_cache = PersistentCache::GetCacheForProcess();
400 persistent_cache->ResetStoredNewShaders();
401
402 RasterStatus raster_status =
403 DrawToSurface(frame_timings_recorder&: *frame_timings_recorder, layer_tree&: *layer_tree, device_pixel_ratio);
404 if (raster_status == RasterStatus::kSuccess) {
405 last_layer_tree_ = std::move(layer_tree);
406 last_device_pixel_ratio_ = device_pixel_ratio;
407 } else if (ShouldResubmitFrame(raster_status)) {
408 resubmitted_pixel_ratio_ = device_pixel_ratio;
409 resubmitted_layer_tree_ = std::move(layer_tree);
410 resubmitted_recorder_ = frame_timings_recorder->CloneUntil(
411 state: FrameTimingsRecorder::State::kBuildEnd);
412 return raster_status;
413 } else if (raster_status == RasterStatus::kDiscarded) {
414 return raster_status;
415 }
416
417 if (persistent_cache->IsDumpingSkp() &&
418 persistent_cache->StoredNewShaders()) {
419 auto screenshot =
420 ScreenshotLastLayerTree(type: ScreenshotType::SkiaPicture, base64_encode: false);
421 persistent_cache->DumpSkp(data: *screenshot.data);
422 }
423
424 // TODO(liyuqian): in Fuchsia, the rasterization doesn't finish when
425 // Rasterizer::DoDraw finishes. Future work is needed to adapt the timestamp
426 // for Fuchsia to capture SceneUpdateContext::ExecutePaintTasks.
427 delegate_.OnFrameRasterized(frame_timing: frame_timings_recorder->GetRecordedTime());
428
429// SceneDisplayLag events are disabled on Fuchsia.
430// see: https://github.com/flutter/flutter/issues/56598
431#if !defined(OS_FUCHSIA)
432 const fml::TimePoint raster_finish_time =
433 frame_timings_recorder->GetRasterEndTime();
434 fml::TimePoint frame_target_time =
435 frame_timings_recorder->GetVsyncTargetTime();
436 if (raster_finish_time > frame_target_time) {
437 fml::TimePoint latest_frame_target_time =
438 delegate_.GetLatestFrameTargetTime();
439 const auto frame_budget_millis = delegate_.GetFrameBudget().count();
440 if (latest_frame_target_time < raster_finish_time) {
441 latest_frame_target_time =
442 latest_frame_target_time +
443 fml::TimeDelta::FromMillisecondsF(millis: frame_budget_millis);
444 }
445 const auto frame_lag =
446 (latest_frame_target_time - frame_target_time).ToMillisecondsF();
447 const int vsync_transitions_missed = round(x: frame_lag / frame_budget_millis);
448 fml::tracing::TraceEventAsyncComplete(
449 category_group: "flutter", // category
450 name: "SceneDisplayLag", // name
451 begin: raster_finish_time, // begin_time
452 end: latest_frame_target_time, // end_time
453 args: "frame_target_time", // arg_key_1
454 args: frame_target_time, // arg_val_1
455 args: "current_frame_target_time", // arg_key_2
456 args: latest_frame_target_time, // arg_val_2
457 args: "vsync_transitions_missed", // arg_key_3
458 args: vsync_transitions_missed // arg_val_3
459 );
460 }
461#endif
462
463 // Pipeline pressure is applied from a couple of places:
464 // rasterizer: When there are more items as of the time of Consume.
465 // animator (via shell): Frame gets produces every vsync.
466 // Enqueing here is to account for the following scenario:
467 // T = 1
468 // - one item (A) in the pipeline
469 // - rasterizer starts (and merges the threads)
470 // - pipeline consume result says no items to process
471 // T = 2
472 // - animator produces (B) to the pipeline
473 // - applies pipeline pressure via platform thread.
474 // T = 3
475 // - rasterizes finished (and un-merges the threads)
476 // - |Draw| for B yields as its on the wrong thread.
477 // This enqueue ensures that we attempt to consume from the right
478 // thread one more time after un-merge.
479 if (raster_thread_merger_) {
480 if (raster_thread_merger_->DecrementLease() ==
481 fml::RasterThreadStatus::kUnmergedNow) {
482 return RasterStatus::kEnqueuePipeline;
483 }
484 }
485
486 return raster_status;
487}
488
489RasterStatus Rasterizer::DrawToSurface(
490 FrameTimingsRecorder& frame_timings_recorder,
491 flutter::LayerTree& layer_tree,
492 float device_pixel_ratio) {
493 TRACE_EVENT0("flutter", "Rasterizer::DrawToSurface");
494 FML_DCHECK(surface_);
495
496 RasterStatus raster_status;
497 if (surface_->AllowsDrawingWhenGpuDisabled()) {
498 raster_status = DrawToSurfaceUnsafe(frame_timings_recorder, layer_tree,
499 device_pixel_ratio);
500 } else {
501 delegate_.GetIsGpuDisabledSyncSwitch()->Execute(
502 handlers: fml::SyncSwitch::Handlers()
503 .SetIfTrue([&] { raster_status = RasterStatus::kDiscarded; })
504 .SetIfFalse([&] {
505 raster_status = DrawToSurfaceUnsafe(
506 frame_timings_recorder, layer_tree, device_pixel_ratio);
507 }));
508 }
509
510 return raster_status;
511}
512
513/// Unsafe because it assumes we have access to the GPU which isn't the case
514/// when iOS is backgrounded, for example.
515/// \see Rasterizer::DrawToSurface
516RasterStatus Rasterizer::DrawToSurfaceUnsafe(
517 FrameTimingsRecorder& frame_timings_recorder,
518 flutter::LayerTree& layer_tree,
519 float device_pixel_ratio) {
520 FML_DCHECK(surface_);
521
522 compositor_context_->ui_time().SetLapTime(
523 frame_timings_recorder.GetBuildDuration());
524
525 DlCanvas* embedder_root_canvas = nullptr;
526 if (external_view_embedder_) {
527 FML_DCHECK(!external_view_embedder_->GetUsedThisFrame());
528 external_view_embedder_->SetUsedThisFrame(true);
529 external_view_embedder_->BeginFrame(
530 frame_size: layer_tree.frame_size(), context: surface_->GetContext(), device_pixel_ratio,
531 raster_thread_merger: raster_thread_merger_);
532 embedder_root_canvas = external_view_embedder_->GetRootCanvas();
533 }
534
535 frame_timings_recorder.RecordRasterStart(raster_start: fml::TimePoint::Now());
536
537 // On Android, the external view embedder deletes surfaces in `BeginFrame`.
538 //
539 // Deleting a surface also clears the GL context. Therefore, acquire the
540 // frame after calling `BeginFrame` as this operation resets the GL context.
541 auto frame = surface_->AcquireFrame(size: layer_tree.frame_size());
542 if (frame == nullptr) {
543 frame_timings_recorder.RecordRasterEnd(
544 cache: &compositor_context_->raster_cache());
545 return RasterStatus::kFailed;
546 }
547
548 // If the external view embedder has specified an optional root surface, the
549 // root surface transformation is set by the embedder instead of
550 // having to apply it here.
551 SkMatrix root_surface_transformation =
552 embedder_root_canvas ? SkMatrix{} : surface_->GetRootTransformation();
553
554 auto root_surface_canvas =
555 embedder_root_canvas ? embedder_root_canvas : frame->Canvas();
556 auto compositor_frame = compositor_context_->AcquireFrame(
557 gr_context: surface_->GetContext(), // skia GrContext
558 canvas: root_surface_canvas, // root surface canvas
559 view_embedder: external_view_embedder_.get(), // external view embedder
560 root_surface_transformation, // root surface transformation
561 instrumentation_enabled: true, // instrumentation enabled
562 surface_supports_readback: frame->framebuffer_info()
563 .supports_readback, // surface supports pixel reads
564 raster_thread_merger: raster_thread_merger_, // thread merger
565 aiks_context: surface_->GetAiksContext().get() // aiks context
566 );
567 if (compositor_frame) {
568 compositor_context_->raster_cache().BeginFrame();
569
570 std::unique_ptr<FrameDamage> damage;
571 // when leaf layer tracing is enabled we wish to repaint the whole frame
572 // for accurate performance metrics.
573 if (frame->framebuffer_info().supports_partial_repaint &&
574 !layer_tree.is_leaf_layer_tracing_enabled()) {
575 // Disable partial repaint if external_view_embedder_ SubmitFrame is
576 // involved - ExternalViewEmbedder unconditionally clears the entire
577 // surface and also partial repaint with platform view present is
578 // something that still need to be figured out.
579 bool force_full_repaint =
580 external_view_embedder_ &&
581 (!raster_thread_merger_ || raster_thread_merger_->IsMerged());
582
583 damage = std::make_unique<FrameDamage>();
584 auto existing_damage = frame->framebuffer_info().existing_damage;
585 if (existing_damage.has_value() && !force_full_repaint) {
586 damage->SetPreviousLayerTree(last_layer_tree_.get());
587 damage->AddAdditionalDamage(damage: existing_damage.value());
588 damage->SetClipAlignment(
589 horizontal: frame->framebuffer_info().horizontal_clip_alignment,
590 vertical: frame->framebuffer_info().vertical_clip_alignment);
591 }
592 }
593
594 bool ignore_raster_cache = true;
595 if (surface_->EnableRasterCache() &&
596 !layer_tree.is_leaf_layer_tracing_enabled()) {
597 ignore_raster_cache = false;
598 }
599
600 RasterStatus raster_status =
601 compositor_frame->Raster(layer_tree, // layer tree
602 ignore_raster_cache, // ignore raster cache
603 frame_damage: damage.get() // frame damage
604 );
605 if (raster_status == RasterStatus::kFailed ||
606 raster_status == RasterStatus::kSkipAndRetry) {
607 return raster_status;
608 }
609
610 SurfaceFrame::SubmitInfo submit_info;
611 // TODO (https://github.com/flutter/flutter/issues/105596): this can be in
612 // the past and might need to get snapped to future as this frame could
613 // have been resubmitted. `presentation_time` on `submit_info` is not set
614 // in this case.
615 const auto presentation_time = frame_timings_recorder.GetVsyncTargetTime();
616 if (presentation_time > fml::TimePoint::Now()) {
617 submit_info.presentation_time = presentation_time;
618 }
619 if (damage) {
620 submit_info.frame_damage = damage->GetFrameDamage();
621 submit_info.buffer_damage = damage->GetBufferDamage();
622 }
623
624 frame->set_submit_info(submit_info);
625
626 if (external_view_embedder_ &&
627 (!raster_thread_merger_ || raster_thread_merger_->IsMerged())) {
628 FML_DCHECK(!frame->IsSubmitted());
629 external_view_embedder_->SubmitFrame(
630 context: surface_->GetContext(), aiks_context: surface_->GetAiksContext(), frame: std::move(frame));
631 } else {
632 frame->Submit();
633 }
634
635 // Do not update raster cache metrics for kResubmit because that status
636 // indicates that the frame was not actually painted.
637 if (raster_status != RasterStatus::kResubmit) {
638 compositor_context_->raster_cache().EndFrame();
639 }
640
641 frame_timings_recorder.RecordRasterEnd(
642 cache: &compositor_context_->raster_cache());
643 FireNextFrameCallbackIfPresent();
644
645 if (surface_->GetContext()) {
646 surface_->GetContext()->performDeferredCleanup(msNotUsed: kSkiaCleanupExpiration);
647 }
648
649 return raster_status;
650 }
651
652 return RasterStatus::kFailed;
653}
654
655static sk_sp<SkData> ScreenshotLayerTreeAsPicture(
656 flutter::LayerTree* tree,
657 flutter::CompositorContext& compositor_context) {
658 FML_DCHECK(tree != nullptr);
659 SkPictureRecorder recorder;
660 recorder.beginRecording(
661 bounds: SkRect::MakeWH(w: tree->frame_size().width(), h: tree->frame_size().height()));
662
663 SkMatrix root_surface_transformation;
664 root_surface_transformation.reset();
665 DlSkCanvasAdapter canvas(recorder.getRecordingCanvas());
666
667 // TODO(amirh): figure out how to take a screenshot with embedded UIView.
668 // https://github.com/flutter/flutter/issues/23435
669 auto frame = compositor_context.AcquireFrame(gr_context: nullptr, canvas: &canvas, view_embedder: nullptr,
670 root_surface_transformation,
671 instrumentation_enabled: false, surface_supports_readback: true, raster_thread_merger: nullptr, aiks_context: nullptr);
672 frame->Raster(layer_tree&: *tree, ignore_raster_cache: true, frame_damage: nullptr);
673
674#if defined(OS_FUCHSIA)
675 SkSerialProcs procs = {0};
676 procs.fImageProc = SerializeImageWithoutData;
677 procs.fTypefaceProc = SerializeTypefaceWithoutData;
678#else
679 SkSerialProcs procs = {.fPictureProc: 0};
680 procs.fTypefaceProc = SerializeTypefaceWithData;
681#endif
682
683 return recorder.finishRecordingAsPicture()->serialize(procs: &procs);
684}
685
686sk_sp<SkData> Rasterizer::ScreenshotLayerTreeAsImage(
687 flutter::LayerTree* tree,
688 flutter::CompositorContext& compositor_context,
689 GrDirectContext* surface_context,
690 bool compressed) {
691 // Attempt to create a snapshot surface depending on whether we have access
692 // to a valid GPU rendering context.
693 std::unique_ptr<OffscreenSurface> snapshot_surface =
694 std::make_unique<OffscreenSurface>(args&: surface_context, args: tree->frame_size());
695
696 if (!snapshot_surface->IsValid()) {
697 FML_LOG(ERROR) << "Screenshot: unable to create snapshot surface";
698 return nullptr;
699 }
700
701 // Draw the current layer tree into the snapshot surface.
702 auto* canvas = snapshot_surface->GetCanvas();
703
704 // There is no root surface transformation for the screenshot layer. Reset
705 // the matrix to identity.
706 SkMatrix root_surface_transformation;
707 root_surface_transformation.reset();
708
709 // snapshot_surface->makeImageSnapshot needs the GL context to be set if the
710 // render context is GL. frame->Raster() pops the gl context in platforms
711 // that gl context switching are used. (For example, older iOS that uses GL)
712 // We reset the GL context using the context switch.
713 auto context_switch = surface_->MakeRenderContextCurrent();
714 if (!context_switch->GetResult()) {
715 FML_LOG(ERROR) << "Screenshot: unable to make image screenshot";
716 return nullptr;
717 }
718
719 auto frame = compositor_context.AcquireFrame(
720 gr_context: surface_context, // skia context
721 canvas, // canvas
722 view_embedder: nullptr, // view embedder
723 root_surface_transformation, // root surface transformation
724 instrumentation_enabled: false, // instrumentation enabled
725 surface_supports_readback: true, // render buffer readback supported
726 raster_thread_merger: nullptr, // thread merger
727 aiks_context: nullptr // aiks context
728 );
729 canvas->Clear(color: DlColor::kTransparent());
730 frame->Raster(layer_tree&: *tree, ignore_raster_cache: true, frame_damage: nullptr);
731 canvas->Flush();
732
733 return snapshot_surface->GetRasterData(compressed);
734}
735
736Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree(
737 Rasterizer::ScreenshotType type,
738 bool base64_encode) {
739 auto* layer_tree = GetLastLayerTree();
740 if (layer_tree == nullptr) {
741 FML_LOG(ERROR) << "Last layer tree was null when screenshotting.";
742 return {};
743 }
744
745 sk_sp<SkData> data = nullptr;
746 std::string format;
747
748 GrDirectContext* surface_context =
749 surface_ ? surface_->GetContext() : nullptr;
750
751 switch (type) {
752 case ScreenshotType::SkiaPicture:
753 format = "ScreenshotType::SkiaPicture";
754 data = ScreenshotLayerTreeAsPicture(tree: layer_tree, compositor_context&: *compositor_context_);
755 break;
756 case ScreenshotType::UncompressedImage:
757 format = "ScreenshotType::UncompressedImage";
758 data = ScreenshotLayerTreeAsImage(tree: layer_tree, compositor_context&: *compositor_context_,
759 surface_context, compressed: false);
760 break;
761 case ScreenshotType::CompressedImage:
762 format = "ScreenshotType::CompressedImage";
763 data = ScreenshotLayerTreeAsImage(tree: layer_tree, compositor_context&: *compositor_context_,
764 surface_context, compressed: true);
765 break;
766 case ScreenshotType::SurfaceData: {
767 Surface::SurfaceData surface_data = surface_->GetSurfaceData();
768 format = surface_data.pixel_format;
769 data = surface_data.data;
770 break;
771 }
772 }
773
774 if (data == nullptr) {
775 FML_LOG(ERROR) << "Screenshot data was null.";
776 return {};
777 }
778
779 if (base64_encode) {
780 size_t b64_size = SkBase64::Encode(src: data->data(), length: data->size(), dst: nullptr);
781 auto b64_data = SkData::MakeUninitialized(length: b64_size);
782 SkBase64::Encode(src: data->data(), length: data->size(), dst: b64_data->writable_data());
783 return Rasterizer::Screenshot{b64_data, layer_tree->frame_size(), format};
784 }
785
786 return Rasterizer::Screenshot{data, layer_tree->frame_size(), format};
787}
788
789void Rasterizer::SetNextFrameCallback(const fml::closure& callback) {
790 next_frame_callback_ = callback;
791}
792
793void Rasterizer::SetExternalViewEmbedder(
794 const std::shared_ptr<ExternalViewEmbedder>& view_embedder) {
795 external_view_embedder_ = view_embedder;
796}
797
798void Rasterizer::SetSnapshotSurfaceProducer(
799 std::unique_ptr<SnapshotSurfaceProducer> producer) {
800 snapshot_surface_producer_ = std::move(producer);
801}
802
803fml::RefPtr<fml::RasterThreadMerger> Rasterizer::GetRasterThreadMerger() {
804 return raster_thread_merger_;
805}
806
807void Rasterizer::FireNextFrameCallbackIfPresent() {
808 if (!next_frame_callback_) {
809 return;
810 }
811 // It is safe for the callback to set a new callback.
812 auto callback = next_frame_callback_;
813 next_frame_callback_ = nullptr;
814 callback();
815}
816
817void Rasterizer::SetResourceCacheMaxBytes(size_t max_bytes, bool from_user) {
818 user_override_resource_cache_bytes_ |= from_user;
819
820 if (!from_user && user_override_resource_cache_bytes_) {
821 // We should not update the setting here if a user has explicitly set a
822 // value for this over the flutter/skia channel.
823 return;
824 }
825
826 max_cache_bytes_ = max_bytes;
827 if (!surface_) {
828 return;
829 }
830
831 GrDirectContext* context = surface_->GetContext();
832 if (context) {
833 auto context_switch = surface_->MakeRenderContextCurrent();
834 if (!context_switch->GetResult()) {
835 return;
836 }
837
838 context->setResourceCacheLimit(max_bytes);
839 }
840}
841
842std::optional<size_t> Rasterizer::GetResourceCacheMaxBytes() const {
843 if (!surface_) {
844 return std::nullopt;
845 }
846 GrDirectContext* context = surface_->GetContext();
847 if (context) {
848 return context->getResourceCacheLimit();
849 }
850 return std::nullopt;
851}
852
853Rasterizer::Screenshot::Screenshot() {}
854
855Rasterizer::Screenshot::Screenshot(sk_sp<SkData> p_data,
856 SkISize p_size,
857 const std::string& p_format)
858 : data(std::move(p_data)), frame_size(p_size), format(p_format) {}
859
860Rasterizer::Screenshot::Screenshot(const Screenshot& other) = default;
861
862Rasterizer::Screenshot::~Screenshot() = default;
863
864} // namespace flutter
865

source code of flutter_engine/flutter/shell/common/rasterizer.cc