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 <cstddef>
8#include <vector>
9
10#include "flutter/common/constants.h"
11#include "flutter/display_list/skia/dl_sk_dispatcher.h"
12#include "flutter/flow/layers/container_layer.h"
13#include "flutter/flow/layers/layer.h"
14#include "flutter/flow/paint_utils.h"
15#include "flutter/flow/raster_cache_util.h"
16#include "flutter/fml/logging.h"
17#include "flutter/fml/trace_event.h"
18#include "third_party/skia/include/core/SkCanvas.h"
19#include "third_party/skia/include/core/SkColorSpace.h"
20#include "third_party/skia/include/core/SkImage.h"
21#include "third_party/skia/include/core/SkSurface.h"
22#include "third_party/skia/include/gpu/GrDirectContext.h"
23#include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
24
25namespace flutter {
26
27RasterCacheResult::RasterCacheResult(sk_sp<DlImage> image,
28 const SkRect& logical_rect,
29 const char* type,
30 sk_sp<const DlRTree> rtree)
31 : image_(std::move(image)),
32 logical_rect_(logical_rect),
33 flow_(type),
34 rtree_(std::move(rtree)) {}
35
36void RasterCacheResult::draw(DlCanvas& canvas,
37 const DlPaint* paint,
38 bool preserve_rtree) const {
39 DlAutoCanvasRestore auto_restore(&canvas, true);
40
41 auto matrix = RasterCacheUtil::GetIntegralTransCTM(ctm: canvas.GetTransform());
42 SkRect bounds =
43 RasterCacheUtil::GetRoundedOutDeviceBounds(rect: logical_rect_, ctm: matrix);
44 FML_DCHECK(std::abs(bounds.width() - image_->dimensions().width()) <= 1 &&
45 std::abs(bounds.height() - image_->dimensions().height()) <= 1);
46 canvas.TransformReset();
47 flow_.Step();
48 if (!preserve_rtree || !rtree_) {
49 canvas.DrawImage(image: image_, point: {.fX: bounds.fLeft, .fY: bounds.fTop},
50 sampling: DlImageSampling::kNearestNeighbor, paint);
51 } else {
52 // On some platforms RTree from overlay layers is used for unobstructed
53 // platform views and hit testing. To preserve the RTree raster cache must
54 // paint individual rects instead of the whole image.
55 auto rects = rtree_->region().getRects(deband: true);
56
57 canvas.Translate(tx: bounds.fLeft, ty: bounds.fTop);
58
59 SkRect rtree_bounds =
60 RasterCacheUtil::GetRoundedOutDeviceBounds(rect: rtree_->bounds(), ctm: matrix);
61 for (auto rect : rects) {
62 SkRect device_rect = RasterCacheUtil::GetRoundedOutDeviceBounds(
63 rect: SkRect::Make(irect: rect), ctm: matrix);
64 device_rect.offset(dx: -rtree_bounds.fLeft, dy: -rtree_bounds.fTop);
65 canvas.DrawImageRect(image: image_, src: device_rect, dst: device_rect,
66 sampling: DlImageSampling::kNearestNeighbor, paint);
67 }
68 }
69}
70
71RasterCache::RasterCache(size_t access_threshold,
72 size_t display_list_cache_limit_per_frame)
73 : access_threshold_(access_threshold),
74 display_list_cache_limit_per_frame_(display_list_cache_limit_per_frame),
75 checkerboard_images_(false) {}
76
77/// @note Procedure doesn't copy all closures.
78std::unique_ptr<RasterCacheResult> RasterCache::Rasterize(
79 const RasterCache::Context& context,
80 sk_sp<const DlRTree> rtree,
81 const std::function<void(DlCanvas*)>& draw_function,
82 const std::function<void(DlCanvas*, const SkRect& rect)>& draw_checkerboard)
83 const {
84 auto matrix = RasterCacheUtil::GetIntegralTransCTM(ctm: context.matrix);
85 SkRect dest_rect =
86 RasterCacheUtil::GetRoundedOutDeviceBounds(rect: context.logical_rect, ctm: matrix);
87
88 const SkImageInfo image_info =
89 SkImageInfo::MakeN32Premul(width: dest_rect.width(), height: dest_rect.height(),
90 cs: sk_ref_sp(obj: context.dst_color_space));
91
92 sk_sp<SkSurface> surface =
93 context.gr_context
94 ? SkSurfaces::RenderTarget(context: context.gr_context, budgeted: skgpu::Budgeted::kYes,
95 imageInfo: image_info)
96 : SkSurfaces::Raster(imageInfo: image_info);
97
98 if (!surface) {
99 return nullptr;
100 }
101
102 DlSkCanvasAdapter canvas(surface->getCanvas());
103 canvas.Clear(color: DlColor::kTransparent());
104
105 canvas.Translate(tx: -dest_rect.left(), ty: -dest_rect.top());
106 canvas.Transform(matrix);
107 draw_function(&canvas);
108
109 if (checkerboard_images_) {
110 draw_checkerboard(&canvas, context.logical_rect);
111 }
112
113 auto image = DlImage::Make(image: surface->makeImageSnapshot());
114 return std::make_unique<RasterCacheResult>(
115 args&: image, args: context.logical_rect, args: context.flow_type, args: std::move(rtree));
116}
117
118bool RasterCache::UpdateCacheEntry(
119 const RasterCacheKeyID& id,
120 const Context& raster_cache_context,
121 const std::function<void(DlCanvas*)>& render_function,
122 sk_sp<const DlRTree> rtree) const {
123 RasterCacheKey key = RasterCacheKey(id, raster_cache_context.matrix);
124 Entry& entry = cache_[key];
125 if (!entry.image) {
126 void (*func)(DlCanvas*, const SkRect& rect) = DrawCheckerboard;
127 entry.image = Rasterize(context: raster_cache_context, rtree: std::move(rtree),
128 draw_function: render_function, draw_checkerboard: func);
129 if (entry.image != nullptr) {
130 switch (id.type()) {
131 case RasterCacheKeyType::kDisplayList: {
132 display_list_cached_this_frame_++;
133 break;
134 }
135 default:
136 break;
137 }
138 return true;
139 }
140 }
141 return entry.image != nullptr;
142}
143
144RasterCache::CacheInfo RasterCache::MarkSeen(const RasterCacheKeyID& id,
145 const SkMatrix& matrix,
146 bool visible) const {
147 RasterCacheKey key = RasterCacheKey(id, matrix);
148 Entry& entry = cache_[key];
149 entry.encountered_this_frame = true;
150 entry.visible_this_frame = visible;
151 if (visible || entry.accesses_since_visible > 0) {
152 entry.accesses_since_visible++;
153 }
154 return {.accesses_since_visible: entry.accesses_since_visible, .has_image: entry.image != nullptr};
155}
156
157int RasterCache::GetAccessCount(const RasterCacheKeyID& id,
158 const SkMatrix& matrix) const {
159 RasterCacheKey key = RasterCacheKey(id, matrix);
160 auto entry = cache_.find(k: key);
161 if (entry != cache_.cend()) {
162 return entry->second.accesses_since_visible;
163 }
164 return -1;
165}
166
167bool RasterCache::HasEntry(const RasterCacheKeyID& id,
168 const SkMatrix& matrix) const {
169 RasterCacheKey key = RasterCacheKey(id, matrix);
170 if (cache_.find(k: key) != cache_.cend()) {
171 return true;
172 }
173 return false;
174}
175
176bool RasterCache::Draw(const RasterCacheKeyID& id,
177 DlCanvas& canvas,
178 const DlPaint* paint,
179 bool preserve_rtree) const {
180 auto it = cache_.find(k: RasterCacheKey(id, canvas.GetTransform()));
181 if (it == cache_.end()) {
182 return false;
183 }
184
185 Entry& entry = it->second;
186
187 if (entry.image) {
188 entry.image->draw(canvas, paint, preserve_rtree);
189 return true;
190 }
191
192 return false;
193}
194
195void RasterCache::BeginFrame() {
196 display_list_cached_this_frame_ = 0;
197 picture_metrics_ = {};
198 layer_metrics_ = {};
199}
200
201void RasterCache::UpdateMetrics() {
202 for (auto it = cache_.begin(); it != cache_.end(); ++it) {
203 Entry& entry = it->second;
204 FML_DCHECK(entry.encountered_this_frame);
205 if (entry.image) {
206 RasterCacheMetrics& metrics = GetMetricsForKind(kind: it->first.kind());
207 metrics.in_use_count++;
208 metrics.in_use_bytes += entry.image->image_bytes();
209 }
210 entry.encountered_this_frame = false;
211 }
212}
213
214void RasterCache::EvictUnusedCacheEntries() {
215 std::vector<RasterCacheKey::Map<Entry>::iterator> dead;
216
217 for (auto it = cache_.begin(); it != cache_.end(); ++it) {
218 Entry& entry = it->second;
219 if (!entry.encountered_this_frame) {
220 dead.push_back(x: it);
221 }
222 }
223
224 for (auto it : dead) {
225 if (it->second.image) {
226 RasterCacheMetrics& metrics = GetMetricsForKind(kind: it->first.kind());
227 metrics.eviction_count++;
228 metrics.eviction_bytes += it->second.image->image_bytes();
229 }
230 cache_.erase(p: it);
231 }
232}
233
234void RasterCache::EndFrame() {
235 UpdateMetrics();
236 TraceStatsToTimeline();
237}
238
239void RasterCache::Clear() {
240 cache_.clear();
241 picture_metrics_ = {};
242 layer_metrics_ = {};
243}
244
245size_t RasterCache::GetCachedEntriesCount() const {
246 return cache_.size();
247}
248
249size_t RasterCache::GetLayerCachedEntriesCount() const {
250 size_t layer_cached_entries_count = 0;
251 for (const auto& item : cache_) {
252 if (item.first.kind() == RasterCacheKeyKind::kLayerMetrics) {
253 layer_cached_entries_count++;
254 }
255 }
256 return layer_cached_entries_count;
257}
258
259size_t RasterCache::GetPictureCachedEntriesCount() const {
260 size_t display_list_cached_entries_count = 0;
261 for (const auto& item : cache_) {
262 if (item.first.kind() == RasterCacheKeyKind::kDisplayListMetrics) {
263 display_list_cached_entries_count++;
264 }
265 }
266 return display_list_cached_entries_count;
267}
268
269void RasterCache::SetCheckboardCacheImages(bool checkerboard) {
270 if (checkerboard_images_ == checkerboard) {
271 return;
272 }
273
274 checkerboard_images_ = checkerboard;
275
276 // Clear all existing entries so previously rasterized items (with or without
277 // a checkerboard) will be refreshed in subsequent passes.
278 Clear();
279}
280
281void RasterCache::TraceStatsToTimeline() const {
282#if !FLUTTER_RELEASE
283 FML_TRACE_COUNTER(
284 "flutter", //
285 "RasterCache", reinterpret_cast<int64_t>(this), //
286 "LayerCount", layer_metrics_.total_count(), //
287 "LayerMBytes", layer_metrics_.total_bytes() / kMegaByteSizeInBytes, //
288 "PictureCount", picture_metrics_.total_count(), //
289 "PictureMBytes", picture_metrics_.total_bytes() / kMegaByteSizeInBytes);
290
291#endif // !FLUTTER_RELEASE
292}
293
294size_t RasterCache::EstimateLayerCacheByteSize() const {
295 size_t layer_cache_bytes = 0;
296 for (const auto& item : cache_) {
297 if (item.first.kind() == RasterCacheKeyKind::kLayerMetrics &&
298 item.second.image) {
299 layer_cache_bytes += item.second.image->image_bytes();
300 }
301 }
302 return layer_cache_bytes;
303}
304
305size_t RasterCache::EstimatePictureCacheByteSize() const {
306 size_t picture_cache_bytes = 0;
307 for (const auto& item : cache_) {
308 if (item.first.kind() == RasterCacheKeyKind::kDisplayListMetrics &&
309 item.second.image) {
310 picture_cache_bytes += item.second.image->image_bytes();
311 }
312 }
313 return picture_cache_bytes;
314}
315
316RasterCacheMetrics& RasterCache::GetMetricsForKind(RasterCacheKeyKind kind) {
317 switch (kind) {
318 case RasterCacheKeyKind::kDisplayListMetrics:
319 return picture_metrics_;
320 case RasterCacheKeyKind::kLayerMetrics:
321 return layer_metrics_;
322 }
323}
324
325} // namespace flutter
326

source code of flutter_engine/flutter/flow/raster_cache.cc