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_UTIL_H_
6#define FLUTTER_FLOW_RASTER_CACHE_UTIL_H_
7
8#include "flutter/fml/logging.h"
9#include "include/core/SkM44.h"
10#include "include/core/SkMatrix.h"
11#include "include/core/SkRect.h"
12
13namespace flutter {
14
15struct RasterCacheUtil {
16 // The default max number of picture and display list raster caches to be
17 // generated per frame. Generating too many caches in one frame may cause jank
18 // on that frame. This limit allows us to throttle the cache and distribute
19 // the work across multiple frames.
20 static constexpr int kDefaultPictureAndDisplayListCacheLimitPerFrame = 3;
21
22 // The ImageFilterLayer might cache the filtered output of this layer
23 // if the layer remains stable (if it is not animating for instance).
24 // If the ImageFilterLayer is not the same between rendered frames,
25 // though, it will cache its children instead and filter their cached
26 // output on the fly.
27 // Caching just the children saves the time to render them and also
28 // avoids a rendering surface switch to draw them.
29 // Caching the layer itself avoids all of that and additionally avoids
30 // the cost of applying the filter, but can be worse than caching the
31 // children if the filter itself is not stable from frame to frame.
32 // This constant controls how many times we will Preroll and Paint this
33 // same ImageFilterLayer before we consider the layer and filter to be
34 // stable enough to switch from caching the children to caching the
35 // filtered output of this layer.
36 static constexpr int kMinimumRendersBeforeCachingFilterLayer = 3;
37
38 static bool CanRasterizeRect(const SkRect& cull_rect) {
39 if (cull_rect.isEmpty()) {
40 // No point in ever rasterizing an empty display list.
41 return false;
42 }
43
44 if (!cull_rect.isFinite()) {
45 // Cannot attempt to rasterize into an infinitely large surface.
46 FML_LOG(INFO) << "Attempted to raster cache non-finite display list";
47 return false;
48 }
49
50 return true;
51 }
52
53 static SkRect GetDeviceBounds(const SkRect& rect, const SkMatrix& ctm) {
54 SkRect device_rect;
55 ctm.mapRect(dst: &device_rect, src: rect);
56 return device_rect;
57 }
58
59 static SkRect GetRoundedOutDeviceBounds(const SkRect& rect,
60 const SkMatrix& ctm) {
61 SkRect device_rect;
62 ctm.mapRect(dst: &device_rect, src: rect);
63 device_rect.roundOut(dst: &device_rect);
64 return device_rect;
65 }
66
67 /**
68 * @brief Snap the translation components of the matrix to integers.
69 *
70 * The snapping will only happen if the matrix only has scale and translation
71 * transformations. This is used, along with GetRoundedOutDeviceBounds, to
72 * ensure that the textures drawn by the raster cache are exactly aligned to
73 * physical pixels. Any layers that participate in raster caching must align
74 * themselves to physical pixels even when not cached to prevent a change in
75 * apparent location if caching is later applied.
76 *
77 * @param ctm the current transformation matrix.
78 * @return SkMatrix the snapped transformation matrix.
79 */
80 static SkMatrix GetIntegralTransCTM(const SkMatrix& ctm) {
81 // Avoid integral snapping if the matrix has complex transformation to avoid
82 // the artifact observed in https://github.com/flutter/flutter/issues/41654.
83 if (!ctm.isScaleTranslate()) {
84 return ctm;
85 }
86 SkMatrix result = ctm;
87 result[SkMatrix::kMTransX] = SkScalarRoundToScalar(ctm.getTranslateX());
88 result[SkMatrix::kMTransY] = SkScalarRoundToScalar(ctm.getTranslateY());
89 return result;
90 }
91
92 /**
93 * @brief Snap the translation components of the matrix to integers.
94 *
95 * The snapping will only happen if the matrix only has scale and translation
96 * transformations. This is used, along with GetRoundedOutDeviceBounds, to
97 * ensure that the textures drawn by the raster cache are exactly aligned to
98 * physical pixels. Any layers that participate in raster caching must align
99 * themselves to physical pixels even when not cached to prevent a change in
100 * apparent location if caching is later applied.
101 *
102 * @param ctm the current transformation matrix.
103 * @return SkM44 the snapped transformation matrix.
104 */
105 static SkM44 GetIntegralTransCTM(const SkM44& ctm) {
106 // Avoid integral snapping if the matrix has complex transformation to avoid
107 // the artifact observed in https://github.com/flutter/flutter/issues/41654.
108 if (ctm.rc(r: 0, c: 1) != 0 || ctm.rc(r: 0, c: 2) != 0) {
109 // X multiplied by either Y or Z
110 return ctm;
111 }
112 if (ctm.rc(r: 1, c: 0) != 0 || ctm.rc(r: 1, c: 2) != 0) {
113 // Y multiplied by either X or Z
114 return ctm;
115 }
116 // We do not need to worry about the Z row unless the W row
117 // has perspective entries...
118 if (ctm.rc(r: 3, c: 0) != 0 || ctm.rc(r: 3, c: 1) != 0 || ctm.rc(r: 3, c: 2) != 0 ||
119 ctm.rc(r: 3, c: 3) != 1) {
120 // W not identity row, therefore perspective is applied
121 return ctm;
122 }
123
124 SkM44 result = ctm;
125 result.setRC(r: 0, c: 3, SkScalarRoundToScalar(ctm.rc(0, 3)));
126 result.setRC(r: 1, c: 3, SkScalarRoundToScalar(ctm.rc(1, 3)));
127 // No need to worry about Z translation because it has no effect
128 // without perspective entries...
129 return result;
130 }
131};
132
133} // namespace flutter
134
135#endif // FLUTTER_FLOW_RASTER_CACHE_UTIL_H_
136

source code of flutter_engine/flutter/flow/raster_cache_util.h