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