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 | |
17 | namespace flutter { |
18 | |
19 | class 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 | |
40 | struct PrerollContext; |
41 | |
42 | class 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 | |