Flutter Engine
raster_cache.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/flow/raster_cache.h"
6 
7 #include <vector>
8 
9 #include "flutter/common/constants.h"
10 #include "flutter/flow/layers/layer.h"
11 #include "flutter/flow/paint_utils.h"
12 #include "flutter/fml/logging.h"
13 #include "flutter/fml/trace_event.h"
14 #include "third_party/skia/include/core/SkCanvas.h"
15 #include "third_party/skia/include/core/SkImage.h"
16 #include "third_party/skia/include/core/SkPicture.h"
17 #include "third_party/skia/include/core/SkSurface.h"
18 #include "third_party/skia/include/gpu/GrDirectContext.h"
19 
20 namespace flutter {
21 
23  const SkRect& logical_rect)
24  : image_(std::move(image)), logical_rect_(logical_rect) {}
25 
26 void RasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const {
27  TRACE_EVENT0("flutter", "RasterCacheResult::draw");
28  SkAutoCanvasRestore auto_restore(&canvas, true);
29  SkIRect bounds =
30  RasterCache::GetDeviceBounds(logical_rect_, canvas.getTotalMatrix());
31  FML_DCHECK(
32  std::abs(bounds.size().width() - image_->dimensions().width()) <= 1 &&
33  std::abs(bounds.size().height() - image_->dimensions().height()) <= 1);
34  canvas.resetMatrix();
35  canvas.drawImage(image_, bounds.fLeft, bounds.fTop, SkSamplingOptions(),
36  paint);
37 }
38 
39 RasterCache::RasterCache(size_t access_threshold,
40  size_t picture_and_display_list_cache_limit_per_frame)
41  : access_threshold_(access_threshold),
42  picture_and_display_list_cache_limit_per_frame_(
43  picture_and_display_list_cache_limit_per_frame),
44  checkerboard_images_(false) {}
45 
46 static bool CanRasterizeRect(const SkRect& cull_rect) {
47  if (cull_rect.isEmpty()) {
48  // No point in ever rasterizing an empty display list.
49  return false;
50  }
51 
52  if (!cull_rect.isFinite()) {
53  // Cannot attempt to rasterize into an infinitely large surface.
54  FML_LOG(INFO) << "Attempted to raster cache non-finite display list";
55  return false;
56  }
57 
58  return true;
59 }
60 
61 static bool IsPictureWorthRasterizing(SkPicture* picture,
62  bool will_change,
63  bool is_complex) {
64  if (will_change) {
65  // If the picture is going to change in the future, there is no point in
66  // doing to extra work to rasterize.
67  return false;
68  }
69 
70  if (picture == nullptr || !CanRasterizeRect(picture->cullRect())) {
71  // No point in deciding whether the picture is worth rasterizing if it
72  // cannot be rasterized at all.
73  return false;
74  }
75 
76  if (is_complex) {
77  // The caller seems to have extra information about the picture and thinks
78  // the picture is always worth rasterizing.
79  return true;
80  }
81 
82  // TODO(abarth): We should find a better heuristic here that lets us avoid
83  // wasting memory on trivial layers that are easy to re-rasterize every frame.
84  return picture->approximateOpCount() > 5;
85 }
86 
87 static bool IsDisplayListWorthRasterizing(DisplayList* display_list,
88  bool will_change,
89  bool is_complex) {
90  if (will_change) {
91  // If the display list is going to change in the future, there is no point
92  // in doing to extra work to rasterize.
93  return false;
94  }
95 
96  if (display_list == nullptr || !CanRasterizeRect(display_list->bounds())) {
97  // No point in deciding whether the display list is worth rasterizing if it
98  // cannot be rasterized at all.
99  return false;
100  }
101 
102  if (is_complex) {
103  // The caller seems to have extra information about the display list and
104  // thinks the display list is always worth rasterizing.
105  return true;
106  }
107 
108  // TODO(abarth): We should find a better heuristic here that lets us avoid
109  // wasting memory on trivial layers that are easy to re-rasterize every frame.
110  return display_list->op_count() > 5;
111 }
112 
113 /// @note Procedure doesn't copy all closures.
114 static std::unique_ptr<RasterCacheResult> Rasterize(
115  GrDirectContext* context,
116  const SkMatrix& ctm,
117  SkColorSpace* dst_color_space,
118  bool checkerboard,
119  const SkRect& logical_rect,
120  const std::function<void(SkCanvas*)>& draw_function) {
121  TRACE_EVENT0("flutter", "RasterCachePopulate");
122  SkIRect cache_rect = RasterCache::GetDeviceBounds(logical_rect, ctm);
123 
124  const SkImageInfo image_info = SkImageInfo::MakeN32Premul(
125  cache_rect.width(), cache_rect.height(), sk_ref_sp(dst_color_space));
126 
127  sk_sp<SkSurface> surface =
128  context
129  ? SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, image_info)
130  : SkSurface::MakeRaster(image_info);
131 
132  if (!surface) {
133  return nullptr;
134  }
135 
136  SkCanvas* canvas = surface->getCanvas();
137  canvas->clear(SK_ColorTRANSPARENT);
138  canvas->translate(-cache_rect.left(), -cache_rect.top());
139  canvas->concat(ctm);
140  draw_function(canvas);
141 
142  if (checkerboard) {
143  DrawCheckerboard(canvas, logical_rect);
144  }
145 
146  return std::make_unique<RasterCacheResult>(surface->makeImageSnapshot(),
147  logical_rect);
148 }
149 
150 std::unique_ptr<RasterCacheResult> RasterCache::RasterizePicture(
151  SkPicture* picture,
152  GrDirectContext* context,
153  const SkMatrix& ctm,
154  SkColorSpace* dst_color_space,
155  bool checkerboard) const {
156  return Rasterize(context, ctm, dst_color_space, checkerboard,
157  picture->cullRect(),
158  [=](SkCanvas* canvas) { canvas->drawPicture(picture); });
159 }
160 
161 std::unique_ptr<RasterCacheResult> RasterCache::RasterizeDisplayList(
162  DisplayList* display_list,
163  GrDirectContext* context,
164  const SkMatrix& ctm,
165  SkColorSpace* dst_color_space,
166  bool checkerboard) const {
167  return Rasterize(context, ctm, dst_color_space, checkerboard,
168  display_list->bounds(),
169  [=](SkCanvas* canvas) { display_list->RenderTo(canvas); });
170 }
171 
173  Layer* layer,
174  const SkMatrix& ctm) {
175  LayerRasterCacheKey cache_key(layer->unique_id(), ctm);
176  Entry& entry = layer_cache_[cache_key];
177  entry.access_count++;
178  entry.used_this_frame = true;
179  if (!entry.image) {
180  entry.image = RasterizeLayer(context, layer, ctm, checkerboard_images_);
181  }
182 }
183 
184 std::unique_ptr<RasterCacheResult> RasterCache::RasterizeLayer(
185  PrerollContext* context,
186  Layer* layer,
187  const SkMatrix& ctm,
188  bool checkerboard) const {
189  return Rasterize(
190  context->gr_context, ctm, context->dst_color_space, checkerboard,
191  layer->paint_bounds(), [layer, context](SkCanvas* canvas) {
192  SkISize canvas_size = canvas->getBaseLayerSize();
193  SkNWayCanvas internal_nodes_canvas(canvas_size.width(),
194  canvas_size.height());
195  internal_nodes_canvas.setMatrix(canvas->getTotalMatrix());
196  internal_nodes_canvas.addCanvas(canvas);
197  Layer::PaintContext paintContext = {
198  /* internal_nodes_canvas= */ static_cast<SkCanvas*>(
199  &internal_nodes_canvas),
200  /* leaf_nodes_canvas= */ canvas,
201  /* gr_context= */ context->gr_context,
202  /* view_embedder= */ nullptr,
203  context->raster_time,
204  context->ui_time,
205  context->texture_registry,
206  context->has_platform_view ? nullptr : context->raster_cache,
208  context->frame_device_pixel_ratio};
209  if (layer->needs_painting(paintContext)) {
210  layer->Paint(paintContext);
211  }
212  });
213 }
214 
216  SkPicture* picture,
217  bool is_complex,
218  bool will_change,
219  const SkMatrix& untranslated_matrix,
220  const SkPoint& offset) {
221  if (!GenerateNewCacheInThisFrame()) {
222  return false;
223  }
224 
225  if (!IsPictureWorthRasterizing(picture, will_change, is_complex)) {
226  // We only deal with pictures that are worthy of rasterization.
227  return false;
228  }
229 
230  SkMatrix transformation_matrix = untranslated_matrix;
231  transformation_matrix.preTranslate(offset.x(), offset.y());
232 
233  // Decompose the matrix (once) for all subsequent operations. We want to make
234  // sure to avoid volumetric distortions while accounting for scaling.
235  const MatrixDecomposition matrix(transformation_matrix);
236 
237  if (!matrix.IsValid()) {
238  // The matrix was singular. No point in going further.
239  return false;
240  }
241 
242  PictureRasterCacheKey cache_key(picture->uniqueID(), transformation_matrix);
243 
244  // Creates an entry, if not present prior.
245  Entry& entry = picture_cache_[cache_key];
246  if (entry.access_count < access_threshold_) {
247  // Frame threshold has not yet been reached.
248  return false;
249  }
250 
251  if (!entry.image) {
252  // GetIntegralTransCTM effect for matrix which only contains scale,
253  // translate, so it won't affect result of matrix decomposition and cache
254  // key.
255 #ifndef SUPPORT_FRACTIONAL_TRANSLATION
256  transformation_matrix = GetIntegralTransCTM(transformation_matrix);
257 #endif
258  entry.image =
259  RasterizePicture(picture, context->gr_context, transformation_matrix,
260  context->dst_color_space, checkerboard_images_);
261  picture_cached_this_frame_++;
262  }
263  return true;
264 }
265 
267  DisplayList* display_list,
268  bool is_complex,
269  bool will_change,
270  const SkMatrix& untranslated_matrix,
271  const SkPoint& offset) {
272  if (!GenerateNewCacheInThisFrame()) {
273  return false;
274  }
275 
276  if (!IsDisplayListWorthRasterizing(display_list, will_change, is_complex)) {
277  // We only deal with display lists that are worthy of rasterization.
278  return false;
279  }
280 
281  SkMatrix transformation_matrix = untranslated_matrix;
282  transformation_matrix.preTranslate(offset.x(), offset.y());
283 
284  // Decompose the matrix (once) for all subsequent operations. We want to make
285  // sure to avoid volumetric distortions while accounting for scaling.
286  const MatrixDecomposition matrix(transformation_matrix);
287 
288  if (!matrix.IsValid()) {
289  // The matrix was singular. No point in going further.
290  return false;
291  }
292 
293  DisplayListRasterCacheKey cache_key(display_list->unique_id(),
294  transformation_matrix);
295 
296  // Creates an entry, if not present prior.
297  Entry& entry = display_list_cache_[cache_key];
298  if (entry.access_count < access_threshold_) {
299  // Frame threshold has not yet been reached.
300  return false;
301  }
302 
303  if (!entry.image) {
304  // GetIntegralTransCTM effect for matrix which only contains scale,
305  // translate, so it won't affect result of matrix decomposition and cache
306  // key.
307 #ifndef SUPPORT_FRACTIONAL_TRANSLATION
308  transformation_matrix = GetIntegralTransCTM(transformation_matrix);
309 #endif
310  entry.image = RasterizeDisplayList(
311  display_list, context->gr_context, transformation_matrix,
312  context->dst_color_space, checkerboard_images_);
313  display_list_cached_this_frame_++;
314  }
315  return true;
316 }
317 
318 bool RasterCache::Draw(const SkPicture& picture, SkCanvas& canvas) const {
319  PictureRasterCacheKey cache_key(picture.uniqueID(), canvas.getTotalMatrix());
320  auto it = picture_cache_.find(cache_key);
321  if (it == picture_cache_.end()) {
322  return false;
323  }
324 
325  Entry& entry = it->second;
326  entry.access_count++;
327  entry.used_this_frame = true;
328 
329  if (entry.image) {
330  entry.image->draw(canvas, nullptr);
331  return true;
332  }
333 
334  return false;
335 }
336 
337 bool RasterCache::Draw(const DisplayList& display_list,
338  SkCanvas& canvas) const {
339  DisplayListRasterCacheKey cache_key(display_list.unique_id(),
340  canvas.getTotalMatrix());
341  auto it = display_list_cache_.find(cache_key);
342  if (it == display_list_cache_.end()) {
343  return false;
344  }
345 
346  Entry& entry = it->second;
347  entry.access_count++;
348  entry.used_this_frame = true;
349 
350  if (entry.image) {
351  entry.image->draw(canvas, nullptr);
352  return true;
353  }
354 
355  return false;
356 }
357 
358 bool RasterCache::Draw(const Layer* layer,
359  SkCanvas& canvas,
360  SkPaint* paint) const {
361  LayerRasterCacheKey cache_key(layer->unique_id(), canvas.getTotalMatrix());
362  auto it = layer_cache_.find(cache_key);
363  if (it == layer_cache_.end()) {
364  return false;
365  }
366 
367  Entry& entry = it->second;
368  entry.access_count++;
369  entry.used_this_frame = true;
370 
371  if (entry.image) {
372  entry.image->draw(canvas, paint);
373  return true;
374  }
375 
376  return false;
377 }
378 
380  TraceStatsToTimeline();
381  SweepOneCacheAfterFrame(picture_cache_);
382  SweepOneCacheAfterFrame(display_list_cache_);
383  SweepOneCacheAfterFrame(layer_cache_);
384  picture_cached_this_frame_ = 0;
385  display_list_cached_this_frame_ = 0;
386  sweep_count_++;
387 }
388 
390  picture_cache_.clear();
391  display_list_cache_.clear();
392  layer_cache_.clear();
393 }
394 
396  return layer_cache_.size() + picture_cache_.size() +
397  display_list_cache_.size();
398 }
399 
401  return layer_cache_.size();
402 }
403 
405  return picture_cache_.size() + display_list_cache_.size();
406 }
407 
408 void RasterCache::SetCheckboardCacheImages(bool checkerboard) {
409  if (checkerboard_images_ == checkerboard) {
410  return;
411  }
412 
413  checkerboard_images_ = checkerboard;
414 
415  // Clear all existing entries so previously rasterized items (with or without
416  // a checkerboard) will be refreshed in subsequent passes.
417  Clear();
418 }
419 
420 void RasterCache::TraceStatsToTimeline() const {
421 #if !FLUTTER_RELEASE
423  "flutter", //
424  "RasterCache", reinterpret_cast<int64_t>(this), //
425  "LayerCount", GetLayerCachedEntriesCount(), //
426  "LayerMBytes", EstimateLayerCacheByteSize() / kMegaByteSizeInBytes, //
427  "PictureCount", GetPictureCachedEntriesCount(), //
429 
430 #endif // !FLUTTER_RELEASE
431 }
432 
434  size_t layer_cache_bytes = 0;
435  for (const auto& item : layer_cache_) {
436  if (item.second.image) {
437  layer_cache_bytes += item.second.image->image_bytes();
438  }
439  }
440  return layer_cache_bytes;
441 }
442 
444  size_t picture_cache_bytes = 0;
445  for (const auto& item : picture_cache_) {
446  if (item.second.image) {
447  picture_cache_bytes += item.second.image->image_bytes();
448  }
449  }
450  for (const auto& item : display_list_cache_) {
451  if (item.second.image) {
452  picture_cache_bytes += item.second.image->image_bytes();
453  }
454  }
455  return picture_cache_bytes;
456 }
457 
458 } // namespace flutter
const Stopwatch & ui_time
Definition: layer.h:53
size_t GetPictureCachedEntriesCount() const
static SkIRect GetDeviceBounds(const SkRect &rect, const SkMatrix &ctm)
Definition: raster_cache.h:105
GrDirectContext * gr_context
Definition: layer.h:44
constexpr double kMegaByteSizeInBytes
Definition: constants.h:6
size_t GetCachedEntriesCount() const
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:90
#define FML_DCHECK(condition)
Definition: logging.h:86
#define FML_TRACE_COUNTER(category_group, name, counter_id, arg1,...)
Definition: trace_event.h:69
size_t GetLayerCachedEntriesCount() const
void SetCheckboardCacheImages(bool checkerboard)
const Stopwatch & raster_time
Definition: layer.h:52
static bool CanRasterizeRect(const SkRect &cull_rect)
Definition: raster_cache.cc:46
static std::unique_ptr< RasterCacheResult > Rasterize(GrDirectContext *context, const SkMatrix &ctm, SkColorSpace *dst_color_space, bool checkerboard, const SkRect &logical_rect, const std::function< void(SkCanvas *)> &draw_function)
RasterCacheResult(sk_sp< SkImage > image, const SkRect &logical_rect)
Definition: raster_cache.cc:22
const SkRect & bounds()
Definition: display_list.h:199
Definition: ref_ptr.h:252
Dart_NativeFunction function
Definition: fuchsia.cc:51
virtual std::unique_ptr< RasterCacheResult > RasterizePicture(SkPicture *picture, GrDirectContext *context, const SkMatrix &ctm, SkColorSpace *dst_color_space, bool checkerboard) const
Rasterize a picture object and produce a RasterCacheResult to be stored in the cache.
#define FML_LOG(severity)
Definition: logging.h:65
bool Prepare(PrerollContext *context, SkPicture *picture, bool is_complex, bool will_change, const SkMatrix &untranslated_matrix, const SkPoint &offset=SkPoint())
static bool IsPictureWorthRasterizing(SkPicture *picture, bool will_change, bool is_complex)
Definition: raster_cache.cc:61
static bool IsDisplayListWorthRasterizing(DisplayList *display_list, bool will_change, bool is_complex)
Definition: raster_cache.cc:87
bool needs_painting(PaintContext &context) const
Definition: layer.h:249
size_t EstimateLayerCacheByteSize() const
Estimate how much memory is used by layer raster cache entries in bytes.
TextureRegistry & texture_registry
Definition: layer.h:54
RasterCache(size_t access_threshold=3, size_t picture_and_display_list_cache_limit_per_frame=kDefaultPictureAndDispLayListCacheLimitPerFrame)
Definition: raster_cache.cc:39
const bool checkerboard_offscreen_layers
Definition: layer.h:55
size_t EstimatePictureCacheByteSize() const
Estimate how much memory is used by picture raster cache entries in bytes, including cache entries in...
virtual void Paint(PaintContext &context) const =0
SkColorSpace * dst_color_space
Definition: layer.h:47
bool Draw(const SkPicture &picture, SkCanvas &canvas) const
const SkRect & paint_bounds() const
Definition: layer.h:227
int op_count(bool nested=false) const
Definition: display_list.h:194
const float frame_device_pixel_ratio
Definition: layer.h:56
virtual std::unique_ptr< RasterCacheResult > RasterizeLayer(PrerollContext *context, Layer *layer, const SkMatrix &ctm, bool checkerboard) const
Rasterize an engine Layer and produce a RasterCacheResult to be stored in the cache.
uint32_t unique_id() const
Definition: display_list.h:197
uint64_t unique_id() const
Definition: layer.h:272
RasterCache * raster_cache
Definition: layer.h:43
static SkMatrix GetIntegralTransCTM(const SkMatrix &ctm)
Snap the translation components of the matrix to integers.
Definition: raster_cache.h:122
virtual void draw(SkCanvas &canvas, const SkPaint *paint) const
Definition: raster_cache.cc:26
virtual std::unique_ptr< RasterCacheResult > RasterizeDisplayList(DisplayList *display_list, GrDirectContext *context, const SkMatrix &ctm, SkColorSpace *dst_color_space, bool checkerboard) const
void DrawCheckerboard(SkCanvas *canvas, SkColor c1, SkColor c2, int size)
Definition: paint_utils.cc:29
void RenderTo(SkCanvas *canvas) const