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