Flutter Engine
The Flutter Engine
dl_complexity_helper.h
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#ifndef FLUTTER_DISPLAY_LIST_BENCHMARKING_DL_COMPLEXITY_HELPER_H_
6#define FLUTTER_DISPLAY_LIST_BENCHMARKING_DL_COMPLEXITY_HELPER_H_
7
8#include "flutter/display_list/benchmarking/dl_complexity.h"
9#include "flutter/display_list/dl_blend_mode.h"
10#include "flutter/display_list/dl_op_receiver.h"
11#include "flutter/display_list/utils/dl_receiver_utils.h"
12
13namespace flutter {
14
15// The Metal and OpenGL complexity calculators use benchmark data gathered
16// using the display_list_benchmarks test suite on real devices.
17//
18// Constants of proportionality and trends were chosen to better match
19// larger numbers rather than smaller ones. This may turn out to be the
20// wrong decision, but the rationale here is that with the smaller numbers,
21// we have:
22//
23// a) More noise.
24// b) Less absolute difference. If the constant we've chosen is out by 50%
25// on a measurement that is 0.001ms, that's less of an issue than if
26// the measurement is 15ms.
27// c) Smaller numbers affect the caching decision negligibly; the caching
28// decision is likely to be driven by slower ops rather than faster ones.
29//
30// In some cases, a cost penalty is used to figure out the cost of an
31// attribute such as anti-aliasing or fill style. In some of these, the
32// penalty is proportional to some other value such as the radius of
33// a circle. In these cases, we ensure that the penalty is never smaller
34// than 1.0f.
35//
36// The error bars in measurement are likely quite large and will
37// vary from device to device, so the goal here is not to provide a
38// perfectly accurate representation of a given DisplayList's
39// complexity but rather a very rough estimate that improves upon our
40// previous cache admission policy (op_count > 5).
41//
42// There will likely be future work that will improve upon the figures
43// in here. Notably, we do not take matrices or clips into account yet.
44//
45// The scoring is based around a baseline score of 100 being roughly
46// equivalent to 0.0005ms of time. With a 32-bit unsigned integer, this
47// would set the maximum time estimate associated with the complexity score
48// at about 21 seconds, which far exceeds what we would ever expect a
49// DisplayList to take rasterising.
50//
51// Finally, care has been taken to keep the computations as cheap as possible.
52// We need to be able to calculate the complexity as quickly as possible
53// so that we don't end up wasting too much time figuring out if something
54// should be cached or not and eat into the time we could have just spent
55// rasterising the DisplayList.
56//
57// In order to keep the computations cheap, the following tradeoffs were made:
58//
59// a) Limit all computations to simple division, multiplication, addition
60// and subtraction.
61// b) Try and use integer arithmetic as much as possible.
62// c) If a specific draw op is logarithmic in complexity, do a best fit
63// onto a linear equation within the range we expect to see the variables
64// fall within.
65//
66// As an example of the above, let's say we have some data that looks like the
67// complexity is something like O(n^1/3). We would like to avoid anything too
68// expensive to calculate, so taking the cube root of the value to try and
69// calculate the time cost should be avoided.
70//
71// In this case, the approach would be to take a straight line approximation
72// that maps closely in the range of n where we feel it is most likely to occur.
73// For example, if this were drawLine, and n were the line length, and we
74// expected the line lengths to typically be between 50 and 100, then we would
75// figure out the equation of the straight line graph that approximates the
76// n^1/3 curve, and if possible try and choose an approximation that is more
77// representative in the range of [50, 100] for n.
78//
79// Once that's done, we can develop the formula as follows:
80//
81// Take y = mx + c (straight line graph chosen as per guidelines above).
82// Divide by however many ops the benchmark ran for a single pass.
83// Multiply by 200,000 to normalise 0.0005ms = 100.
84// Simplify down the formula.
85//
86// So if we had m = 1/5 and c = 0, and the drawLines benchmark ran 10,000
87// drawLine calls per iteration:
88//
89// y (time taken) = x (line length) / 5
90// y = x / 5 * 200,000 / 10,000
91// y = x / 5 * 20
92// y = 4x
93
95 : public virtual DlOpReceiver,
96 public virtual IgnoreClipDispatchHelper,
97 public virtual IgnoreTransformDispatchHelper {
98 public:
99 explicit ComplexityCalculatorHelper(unsigned int ceiling)
100 : ceiling_(ceiling) {}
101
102 virtual ~ComplexityCalculatorHelper() = default;
103
104 void setInvertColors(bool invert) override {}
105 void setStrokeCap(DlStrokeCap cap) override {}
107 void setStrokeMiter(SkScalar limit) override {}
108 void setColor(DlColor color) override {}
109 void setBlendMode(DlBlendMode mode) override {}
110 void setColorSource(const DlColorSource* source) override {}
111 void setImageFilter(const DlImageFilter* filter) override {}
112 void setColorFilter(const DlColorFilter* filter) override {}
113 void setMaskFilter(const DlMaskFilter* filter) override {}
114
115 void save() override {}
116 // We accumulate the cost of restoring a saveLayer() in saveLayer()
117 void restore() override {}
118
119 void setAntiAlias(bool aa) override { current_paint_.setAntiAlias(aa); }
120
121 void setDrawStyle(DlDrawStyle style) override {
122 current_paint_.setDrawStyle(style);
123 }
124
126 current_paint_.setStrokeWidth(width);
127 }
128
130 if (IsComplex()) {
131 return;
132 }
133 // Placeholder value here. This is a relatively cheap operation.
135 }
136
137 void drawPaint() override {
138 if (IsComplex()) {
139 return;
140 }
141 // Placeholder value here. This can be cheap (e.g. effectively a drawColor),
142 // or expensive (e.g. a bitmap shader with an image filter)
144 }
145
147 const sk_sp<DlImage> image,
148 const SkRect& src,
149 const SkRect& dst,
151 bool render_with_attributes,
152 SrcRectConstraint constraint = SrcRectConstraint::kFast) override {
153 if (IsComplex()) {
154 return;
155 }
157 render_with_attributes, constraint == SrcRectConstraint::kStrict);
158 }
159
161 const SkRSXform xform[],
162 const SkRect tex[],
163 const DlColor colors[],
164 int count,
167 const SkRect* cull_rect,
168 bool render_with_attributes) override {
169 if (IsComplex()) {
170 return;
171 }
172 // This API just does a series of drawImage calls from the atlas
173 // This is equivalent to calling drawImageRect lots of times
174 for (int i = 0; i < count; i++) {
175 ImageRect(SkISize::Make(tex[i].width(), tex[i].height()), true,
176 render_with_attributes, true);
177 }
178 }
179
180 // This method finalizes the complexity score calculation and returns it
181 unsigned int ComplexityScore() {
182 // We hit our ceiling, so return that
183 if (IsComplex()) {
184 return Ceiling();
185 }
186
187 // Calculate the impact of any draw ops where the complexity is dependent
188 // on the number of calls made.
189 unsigned int batched_complexity = BatchedComplexity();
190
191 // Check for overflow
192 if (Ceiling() - complexity_score_ < batched_complexity) {
193 return Ceiling();
194 }
195
196 return complexity_score_ + batched_complexity;
197 }
198
199 protected:
200 void AccumulateComplexity(unsigned int complexity) {
201 // Check to see if we will overflow by accumulating this complexity score
202 if (ceiling_ - complexity_score_ < complexity) {
203 is_complex_ = true;
204 return;
205 }
206
207 complexity_score_ += complexity;
208 }
209
210 inline bool IsAntiAliased() { return current_paint_.isAntiAlias(); }
211 inline bool IsHairline() { return current_paint_.getStrokeWidth() == 0.0f; }
212 inline DlDrawStyle DrawStyle() { return current_paint_.getDrawStyle(); }
213 inline bool IsComplex() { return is_complex_; }
214 inline unsigned int Ceiling() { return ceiling_; }
215 inline unsigned int CurrentComplexityScore() { return complexity_score_; }
216
218 unsigned int line_verb_cost,
219 unsigned int quad_verb_cost,
220 unsigned int conic_verb_cost,
221 unsigned int cubic_verb_cost) {
222 int verb_count = path.countVerbs();
223 std::vector<uint8_t> verbs(verb_count);
224 path.getVerbs(verbs.data(), verbs.size());
225
226 unsigned int complexity = 0;
227 for (int i = 0; i < verb_count; i++) {
228 switch (verbs[i]) {
229 case SkPath::Verb::kLine_Verb:
230 complexity += line_verb_cost;
231 break;
232 case SkPath::Verb::kQuad_Verb:
233 complexity += quad_verb_cost;
234 break;
235 case SkPath::Verb::kConic_Verb:
236 complexity += conic_verb_cost;
237 break;
238 case SkPath::Verb::kCubic_Verb:
239 complexity += cubic_verb_cost;
240 break;
241 }
242 }
243 return complexity;
244 }
245
246 virtual void ImageRect(const SkISize& size,
247 bool texture_backed,
248 bool render_with_attributes,
249 bool enforce_src_edges) = 0;
250
251 // This calculates and returns the cost of draw calls which are batched and
252 // thus have a time cost proportional to the number of draw calls made, such
253 // as saveLayer and drawTextBlob.
254 virtual unsigned int BatchedComplexity() = 0;
255
256 private:
257 DlPaint current_paint_;
258
259 // If we exceed the ceiling (defaults to the largest number representable
260 // by unsigned int), then set the is_complex_ bool and we no longer
261 // accumulate.
262 bool is_complex_ = false;
263 unsigned int ceiling_;
264
265 unsigned int complexity_score_ = 0;
266};
267
268} // namespace flutter
269
270#endif // FLUTTER_DISPLAY_LIST_BENCHMARKING_DL_COMPLEXITY_HELPER_H_
int count
Definition: FontMgrTest.cpp:50
SkISize dimensions() const
Definition: SkImage.h:297
virtual bool isTextureBacked() const =0
Definition: SkPath.h:59
void setStrokeMiter(SkScalar limit) override
virtual void ImageRect(const SkISize &size, bool texture_backed, bool render_with_attributes, bool enforce_src_edges)=0
void setInvertColors(bool invert) override
void setStrokeWidth(SkScalar width) override
void setColorSource(const DlColorSource *source) override
void setColor(DlColor color) override
virtual ~ComplexityCalculatorHelper()=default
void AccumulateComplexity(unsigned int complexity)
ComplexityCalculatorHelper(unsigned int ceiling)
void setMaskFilter(const DlMaskFilter *filter) override
void drawColor(DlColor color, DlBlendMode mode) override
void drawAtlas(const sk_sp< DlImage > atlas, const SkRSXform xform[], const SkRect tex[], const DlColor colors[], int count, DlBlendMode mode, DlImageSampling sampling, const SkRect *cull_rect, bool render_with_attributes) override
void setStrokeCap(DlStrokeCap cap) override
void setColorFilter(const DlColorFilter *filter) override
void setStrokeJoin(DlStrokeJoin join) override
virtual unsigned int BatchedComplexity()=0
void setImageFilter(const DlImageFilter *filter) override
void setBlendMode(DlBlendMode mode) override
void setDrawStyle(DlDrawStyle style) override
void drawImageRect(const sk_sp< DlImage > image, const SkRect &src, const SkRect &dst, DlImageSampling sampling, bool render_with_attributes, SrcRectConstraint constraint=SrcRectConstraint::kFast) override
unsigned int CalculatePathComplexity(const SkPath &path, unsigned int line_verb_cost, unsigned int quad_verb_cost, unsigned int conic_verb_cost, unsigned int cubic_verb_cost)
Internal API for rendering recorded display lists to backends.
bool isAntiAlias() const
Definition: dl_paint.h:57
DlPaint & setAntiAlias(bool isAntiAlias)
Definition: dl_paint.h:58
DlPaint & setStrokeWidth(float width)
Definition: dl_paint.h:116
DlDrawStyle getDrawStyle() const
Definition: dl_paint.h:91
float getStrokeWidth() const
Definition: dl_paint.h:115
DlPaint & setDrawStyle(DlDrawStyle style)
Definition: dl_paint.h:94
DlColor color
SkBitmap source
Definition: examples.cpp:28
float SkScalar
Definition: extension.cpp:12
gboolean invert
sk_sp< const SkImage > atlas
Definition: SkRecords.h:331
sk_sp< const SkImage > image
Definition: SkRecords.h:269
PODArray< SkColor > colors
Definition: SkRecords.h:276
SkSamplingOptions sampling
Definition: SkRecords.h:337
DlStrokeJoin
Definition: dl_paint.h:37
DlStrokeCap
Definition: dl_paint.h:28
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DlDrawStyle
Definition: dl_paint.h:19
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition: switches.h:228
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
dst
Definition: cp.py:12
int32_t height
int32_t width
static SkString join(const CommandLineFlags::StringArray &)
Definition: skpbench.cpp:741
Definition: SkSize.h:16
static constexpr SkISize Make(int32_t w, int32_t h)
Definition: SkSize.h:20