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#ifndef FLUTTER_FLOW_RASTER_CACHE_H_
6#define FLUTTER_FLOW_RASTER_CACHE_H_
7
8#include <memory>
9#include <unordered_map>
10
11#include "flutter/flow/raster_cache_key.h"
12#include "flutter/fml/macros.h"
13#include "flutter/fml/memory/weak_ptr.h"
14#include "third_party/skia/include/core/SkImage.h"
15#include "third_party/skia/include/core/SkSize.h"
16
17namespace flutter {
18
19class RasterCacheResult {
20 public:
21 RasterCacheResult(sk_sp<SkImage> image, const SkRect& logical_rect);
22
23 virtual ~RasterCacheResult() = default;
24
25 virtual void draw(SkCanvas& canvas, const SkPaint* paint) const;
26
27 virtual SkISize image_dimensions() const {
28 return image_ ? image_->dimensions() : SkISize::Make(0, 0);
29 };
30
31 virtual int64_t image_bytes() const {
32 return image_ ? image_->imageInfo().computeMinByteSize() : 0;
33 };
34
35 private:
36 sk_sp<SkImage> image_;
37 SkRect logical_rect_;
38};
39
40struct PrerollContext;
41
42class RasterCache {
43 public:
44 // The default max number of picture raster caches to be generated per frame.
45 // Generating too many caches in one frame may cause jank on that frame. This
46 // limit allows us to throttle the cache and distribute the work across
47 // multiple frames.
48 static constexpr int kDefaultPictureCacheLimitPerFrame = 3;
49
50 explicit RasterCache(
51 size_t access_threshold = 3,
52 size_t picture_cache_limit_per_frame = kDefaultPictureCacheLimitPerFrame);
53
54 virtual ~RasterCache() = default;
55
56 /**
57 * @brief Rasterize a picture object and produce a RasterCacheResult
58 * to be stored in the cache.
59 *
60 * @param picture the SkPicture object to be cached.
61 * @param context the GrDirectContext used for rendering.
62 * @param ctm the transformation matrix used for rendering.
63 * @param dst_color_space the destination color space that the cached
64 * rendering will be drawn into
65 * @param checkerboard a flag indicating whether or not a checkerboard
66 * pattern should be rendered into the cached image for debug
67 * analysis
68 * @return a RasterCacheResult that can draw the rendered picture into
69 * the destination using a simple image blit
70 */
71 virtual std::unique_ptr<RasterCacheResult> RasterizePicture(
72 SkPicture* picture,
73 GrDirectContext* context,
74 const SkMatrix& ctm,
75 SkColorSpace* dst_color_space,
76 bool checkerboard) const;
77
78 /**
79 * @brief Rasterize an engine Layer and produce a RasterCacheResult
80 * to be stored in the cache.
81 *
82 * @param context the PrerollContext containing important information
83 * needed for rendering a layer.
84 * @param layer the Layer object to be cached.
85 * @param ctm the transformation matrix used for rendering.
86 * @param checkerboard a flag indicating whether or not a checkerboard
87 * pattern should be rendered into the cached image for debug
88 * analysis
89 * @return a RasterCacheResult that can draw the rendered layer into
90 * the destination using a simple image blit
91 */
92 virtual std::unique_ptr<RasterCacheResult> RasterizeLayer(
93 PrerollContext* context,
94 Layer* layer,
95 const SkMatrix& ctm,
96 bool checkerboard) const;
97
98 static SkIRect GetDeviceBounds(const SkRect& rect, const SkMatrix& ctm) {
99 SkRect device_rect;
100 ctm.mapRect(&device_rect, rect);
101 SkIRect bounds;
102 device_rect.roundOut(&bounds);
103 return bounds;
104 }
105
106 /**
107 * @brief Snap the translation components of the matrix to integers.
108 *
109 * The snapping will only happen if the matrix only has scale and translation
110 * transformations.
111 *
112 * @param ctm the current transformation matrix.
113 * @return SkMatrix the snapped transformation matrix.
114 */
115 static SkMatrix GetIntegralTransCTM(const SkMatrix& ctm) {
116 // Avoid integral snapping if the matrix has complex transformation to avoid
117 // the artifact observed in https://github.com/flutter/flutter/issues/41654.
118 if (!ctm.isScaleTranslate()) {
119 return ctm;
120 }
121 SkMatrix result = ctm;
122 result[SkMatrix::kMTransX] = SkScalarRoundToScalar(ctm.getTranslateX());
123 result[SkMatrix::kMTransY] = SkScalarRoundToScalar(ctm.getTranslateY());
124 return result;
125 }
126
127 // Return true if the cache is generated.
128 //
129 // We may return false and not generate the cache if
130 // 1. The picture is not worth rasterizing
131 // 2. The matrix is singular
132 // 3. The picture is accessed too few times
133 // 4. There are too many pictures to be cached in the current frame.
134 // (See also kDefaultPictureCacheLimitPerFrame.)
135 bool Prepare(GrDirectContext* context,
136 SkPicture* picture,
137 const SkMatrix& transformation_matrix,
138 SkColorSpace* dst_color_space,
139 bool is_complex,
140 bool will_change);
141
142 void Prepare(PrerollContext* context, Layer* layer, const SkMatrix& ctm);
143
144 // Find the raster cache for the picture and draw it to the canvas.
145 //
146 // Return true if it's found and drawn.
147 bool Draw(const SkPicture& picture, SkCanvas& canvas) const;
148
149 // Find the raster cache for the layer and draw it to the canvas.
150 //
151 // Addional paint can be given to change how the raster cache is drawn (e.g.,
152 // draw the raster cache with some opacity).
153 //
154 // Return true if the layer raster cache is found and drawn.
155 bool Draw(const Layer* layer,
156 SkCanvas& canvas,
157 SkPaint* paint = nullptr) const;
158
159 void SweepAfterFrame();
160
161 void Clear();
162
163 void SetCheckboardCacheImages(bool checkerboard);
164
165 size_t GetCachedEntriesCount() const;
166
167 size_t GetLayerCachedEntriesCount() const;
168
169 size_t GetPictureCachedEntriesCount() const;
170
171 /**
172 * @brief Estimate how much memory is used by picture raster cache entries in
173 * bytes.
174 *
175 * Only SkImage's memory usage is counted as other objects are often much
176 * smaller compared to SkImage. SkImageInfo::computeMinByteSize is used to
177 * estimate the SkImage memory usage.
178 */
179 size_t EstimatePictureCacheByteSize() const;
180
181 /**
182 * @brief Estimate how much memory is used by layer raster cache entries in
183 * bytes.
184 *
185 * Only SkImage's memory usage is counted as other objects are often much
186 * smaller compared to SkImage. SkImageInfo::computeMinByteSize is used to
187 * estimate the SkImage memory usage.
188 */
189 size_t EstimateLayerCacheByteSize() const;
190
191 private:
192 struct Entry {
193 bool used_this_frame = false;
194 size_t access_count = 0;
195 std::unique_ptr<RasterCacheResult> image;
196 };
197
198 template <class Cache>
199 static void SweepOneCacheAfterFrame(Cache& cache) {
200 std::vector<typename Cache::iterator> dead;
201
202 for (auto it = cache.begin(); it != cache.end(); ++it) {
203 Entry& entry = it->second;
204 if (!entry.used_this_frame) {
205 dead.push_back(it);
206 }
207 entry.used_this_frame = false;
208 }
209
210 for (auto it : dead) {
211 cache.erase(it);
212 }
213 }
214
215 const size_t access_threshold_;
216 const size_t picture_cache_limit_per_frame_;
217 size_t picture_cached_this_frame_ = 0;
218 mutable PictureRasterCacheKey::Map<Entry> picture_cache_;
219 mutable LayerRasterCacheKey::Map<Entry> layer_cache_;
220 bool checkerboard_images_;
221
222 void TraceStatsToTimeline() const;
223
224 FML_DISALLOW_COPY_AND_ASSIGN(RasterCache);
225};
226
227} // namespace flutter
228
229#endif // FLUTTER_FLOW_RASTER_CACHE_H_
230