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, paint);
36 }
37 
38 RasterCache::RasterCache(size_t access_threshold,
39  size_t picture_cache_limit_per_frame)
40  : access_threshold_(access_threshold),
41  picture_cache_limit_per_frame_(picture_cache_limit_per_frame),
42  checkerboard_images_(false) {}
43 
44 static bool CanRasterizePicture(SkPicture* picture) {
45  if (picture == nullptr) {
46  return false;
47  }
48 
49  const SkRect cull_rect = picture->cullRect();
50 
51  if (cull_rect.isEmpty()) {
52  // No point in ever rasterizing an empty picture.
53  return false;
54  }
55 
56  if (!cull_rect.isFinite()) {
57  // Cannot attempt to rasterize into an infinitely large surface.
58  return false;
59  }
60 
61  return true;
62 }
63 
64 static bool IsPictureWorthRasterizing(SkPicture* picture,
65  bool will_change,
66  bool is_complex) {
67  if (will_change) {
68  // If the picture is going to change in the future, there is no point in
69  // doing to extra work to rasterize.
70  return false;
71  }
72 
73  if (!CanRasterizePicture(picture)) {
74  // No point in deciding whether the picture is worth rasterizing if it
75  // cannot be rasterized at all.
76  return false;
77  }
78 
79  if (is_complex) {
80  // The caller seems to have extra information about the picture and thinks
81  // the picture is always worth rasterizing.
82  return true;
83  }
84 
85  // TODO(abarth): We should find a better heuristic here that lets us avoid
86  // wasting memory on trivial layers that are easy to re-rasterize every frame.
87  return picture->approximateOpCount() > 5;
88 }
89 
90 /// @note Procedure doesn't copy all closures.
91 static std::unique_ptr<RasterCacheResult> Rasterize(
92  GrDirectContext* context,
93  const SkMatrix& ctm,
94  SkColorSpace* dst_color_space,
95  bool checkerboard,
96  const SkRect& logical_rect,
97  const std::function<void(SkCanvas*)>& draw_function) {
98  TRACE_EVENT0("flutter", "RasterCachePopulate");
99  SkIRect cache_rect = RasterCache::GetDeviceBounds(logical_rect, ctm);
100 
101  const SkImageInfo image_info = SkImageInfo::MakeN32Premul(
102  cache_rect.width(), cache_rect.height(), sk_ref_sp(dst_color_space));
103 
104  sk_sp<SkSurface> surface =
105  context
106  ? SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, image_info)
107  : SkSurface::MakeRaster(image_info);
108 
109  if (!surface) {
110  return nullptr;
111  }
112 
113  SkCanvas* canvas = surface->getCanvas();
114  canvas->clear(SK_ColorTRANSPARENT);
115  canvas->translate(-cache_rect.left(), -cache_rect.top());
116  canvas->concat(ctm);
117  draw_function(canvas);
118 
119  if (checkerboard) {
120  DrawCheckerboard(canvas, logical_rect);
121  }
122 
123  return std::make_unique<RasterCacheResult>(surface->makeImageSnapshot(),
124  logical_rect);
125 }
126 
127 std::unique_ptr<RasterCacheResult> RasterCache::RasterizePicture(
128  SkPicture* picture,
129  GrDirectContext* context,
130  const SkMatrix& ctm,
131  SkColorSpace* dst_color_space,
132  bool checkerboard) const {
133  return Rasterize(context, ctm, dst_color_space, checkerboard,
134  picture->cullRect(),
135  [=](SkCanvas* canvas) { canvas->drawPicture(picture); });
136 }
137 
139  Layer* layer,
140  const SkMatrix& ctm) {
141  LayerRasterCacheKey cache_key(layer->unique_id(), ctm);
142  Entry& entry = layer_cache_[cache_key];
143  entry.access_count++;
144  entry.used_this_frame = true;
145  if (!entry.image) {
146  entry.image = RasterizeLayer(context, layer, ctm, checkerboard_images_);
147  }
148 }
149 
150 std::unique_ptr<RasterCacheResult> RasterCache::RasterizeLayer(
151  PrerollContext* context,
152  Layer* layer,
153  const SkMatrix& ctm,
154  bool checkerboard) const {
155  return Rasterize(
156  context->gr_context, ctm, context->dst_color_space, checkerboard,
157  layer->paint_bounds(), [layer, context](SkCanvas* canvas) {
158  SkISize canvas_size = canvas->getBaseLayerSize();
159  SkNWayCanvas internal_nodes_canvas(canvas_size.width(),
160  canvas_size.height());
161  internal_nodes_canvas.addCanvas(canvas);
162  Layer::PaintContext paintContext = {
163  /* internal_nodes_canvas= */ static_cast<SkCanvas*>(
164  &internal_nodes_canvas),
165  /* leaf_nodes_canvas= */ canvas,
166  /* gr_context= */ context->gr_context,
167  /* view_embedder= */ nullptr,
168  context->raster_time,
169  context->ui_time,
170  context->texture_registry,
171  context->has_platform_view ? nullptr : context->raster_cache,
173  context->frame_device_pixel_ratio};
174  if (layer->needs_painting()) {
175  layer->Paint(paintContext);
176  }
177  });
178 }
179 
180 bool RasterCache::Prepare(GrDirectContext* context,
181  SkPicture* picture,
182  const SkMatrix& transformation_matrix,
183  SkColorSpace* dst_color_space,
184  bool is_complex,
185  bool will_change) {
186  // Disabling caching when access_threshold is zero is historic behavior.
187  if (access_threshold_ == 0) {
188  return false;
189  }
190  if (picture_cached_this_frame_ >= picture_cache_limit_per_frame_) {
191  return false;
192  }
193  if (!IsPictureWorthRasterizing(picture, will_change, is_complex)) {
194  // We only deal with pictures that are worthy of rasterization.
195  return false;
196  }
197 
198  // Decompose the matrix (once) for all subsequent operations. We want to make
199  // sure to avoid volumetric distortions while accounting for scaling.
200  const MatrixDecomposition matrix(transformation_matrix);
201 
202  if (!matrix.IsValid()) {
203  // The matrix was singular. No point in going further.
204  return false;
205  }
206 
207  PictureRasterCacheKey cache_key(picture->uniqueID(), transformation_matrix);
208 
209  // Creates an entry, if not present prior.
210  Entry& entry = picture_cache_[cache_key];
211  if (entry.access_count < access_threshold_) {
212  // Frame threshold has not yet been reached.
213  return false;
214  }
215 
216  if (!entry.image) {
217  entry.image = RasterizePicture(picture, context, transformation_matrix,
218  dst_color_space, checkerboard_images_);
219  picture_cached_this_frame_++;
220  }
221  return true;
222 }
223 
224 bool RasterCache::Draw(const SkPicture& picture, SkCanvas& canvas) const {
225  PictureRasterCacheKey cache_key(picture.uniqueID(), canvas.getTotalMatrix());
226  auto it = picture_cache_.find(cache_key);
227  if (it == picture_cache_.end()) {
228  return false;
229  }
230 
231  Entry& entry = it->second;
232  entry.access_count++;
233  entry.used_this_frame = true;
234 
235  if (entry.image) {
236  entry.image->draw(canvas, nullptr);
237  return true;
238  }
239 
240  return false;
241 }
242 
243 bool RasterCache::Draw(const Layer* layer,
244  SkCanvas& canvas,
245  SkPaint* paint) const {
246  LayerRasterCacheKey cache_key(layer->unique_id(), canvas.getTotalMatrix());
247  auto it = layer_cache_.find(cache_key);
248  if (it == layer_cache_.end()) {
249  return false;
250  }
251 
252  Entry& entry = it->second;
253  entry.access_count++;
254  entry.used_this_frame = true;
255 
256  if (entry.image) {
257  entry.image->draw(canvas, paint);
258  return true;
259  }
260 
261  return false;
262 }
263 
265  SweepOneCacheAfterFrame(picture_cache_);
266  SweepOneCacheAfterFrame(layer_cache_);
267  picture_cached_this_frame_ = 0;
268  TraceStatsToTimeline();
269 }
270 
272  picture_cache_.clear();
273  layer_cache_.clear();
274 }
275 
277  return layer_cache_.size() + picture_cache_.size();
278 }
279 
281  return layer_cache_.size();
282 }
283 
285  return picture_cache_.size();
286 }
287 
288 void RasterCache::SetCheckboardCacheImages(bool checkerboard) {
289  if (checkerboard_images_ == checkerboard) {
290  return;
291  }
292 
293  checkerboard_images_ = checkerboard;
294 
295  // Clear all existing entries so previously rasterized items (with or without
296  // a checkerboard) will be refreshed in subsequent passes.
297  Clear();
298 }
299 
300 void RasterCache::TraceStatsToTimeline() const {
301 #if !FLUTTER_RELEASE
302  FML_TRACE_COUNTER("flutter", "RasterCache", reinterpret_cast<int64_t>(this),
303  "LayerCount", layer_cache_.size(), "LayerMBytes",
305  "PictureCount", picture_cache_.size(), "PictureMBytes",
307 
308 #endif // !FLUTTER_RELEASE
309 }
310 
312  size_t layer_cache_bytes = 0;
313  for (const auto& item : layer_cache_) {
314  if (item.second.image) {
315  layer_cache_bytes += item.second.image->image_bytes();
316  }
317  }
318  return layer_cache_bytes;
319 }
320 
322  size_t picture_cache_bytes = 0;
323  for (const auto& item : picture_cache_) {
324  if (item.second.image) {
325  picture_cache_bytes += item.second.image->image_bytes();
326  }
327  }
328  return picture_cache_bytes;
329 }
330 
331 } // namespace flutter
const Stopwatch & ui_time
Definition: layer.h:54
size_t GetPictureCachedEntriesCount() const
static SkIRect GetDeviceBounds(const SkRect &rect, const SkMatrix &ctm)
Definition: raster_cache.h:98
GrDirectContext * gr_context
Definition: layer.h:45
constexpr double kMegaByteSizeInBytes
Definition: constants.h:6
size_t GetCachedEntriesCount() const
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:75
#define FML_DCHECK(condition)
Definition: logging.h:86
#define FML_TRACE_COUNTER(category_group, name, counter_id, arg1,...)
Definition: trace_event.h:67
size_t GetLayerCachedEntriesCount() const
void SetCheckboardCacheImages(bool checkerboard)
const Stopwatch & raster_time
Definition: layer.h:53
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)
Definition: raster_cache.cc:91
RasterCacheResult(sk_sp< SkImage > image, const SkRect &logical_rect)
Definition: raster_cache.cc:22
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.
RasterCache(size_t access_threshold=3, size_t picture_cache_limit_per_frame=kDefaultPictureCacheLimitPerFrame)
Definition: raster_cache.cc:38
static bool IsPictureWorthRasterizing(SkPicture *picture, bool will_change, bool is_complex)
Definition: raster_cache.cc:64
bool Prepare(GrDirectContext *context, SkPicture *picture, const SkMatrix &transformation_matrix, SkColorSpace *dst_color_space, bool is_complex, bool will_change)
size_t EstimateLayerCacheByteSize() const
Estimate how much memory is used by layer raster cache entries in bytes.
TextureRegistry & texture_registry
Definition: layer.h:55
const bool checkerboard_offscreen_layers
Definition: layer.h:56
size_t EstimatePictureCacheByteSize() const
Estimate how much memory is used by picture raster cache entries in bytes.
bool needs_painting() const
Definition: layer.h:174
virtual void Paint(PaintContext &context) const =0
SkColorSpace * dst_color_space
Definition: layer.h:48
bool Draw(const SkPicture &picture, SkCanvas &canvas) const
const SkRect & paint_bounds() const
Definition: layer.h:166
const float frame_device_pixel_ratio
Definition: layer.h:57
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.
uint64_t unique_id() const
Definition: layer.h:176
RasterCache * raster_cache
Definition: layer.h:44
static bool CanRasterizePicture(SkPicture *picture)
Definition: raster_cache.cc:44
virtual void draw(SkCanvas &canvas, const SkPaint *paint) const
Definition: raster_cache.cc:26
void DrawCheckerboard(SkCanvas *canvas, SkColor c1, SkColor c2, int size)
Definition: paint_utils.cc:28