Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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 {}
106 void setStrokeJoin(DlStrokeJoin join) 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 setPathEffect(const DlPathEffect* effect) override {}
114 void setMaskFilter(const DlMaskFilter* filter) override {}
115
116 void save() override {}
117 // We accumulate the cost of restoring a saveLayer() in saveLayer()
118 void restore() override {}
119
120 void setAntiAlias(bool aa) override { current_paint_.setAntiAlias(aa); }
121
122 void setDrawStyle(DlDrawStyle style) override {
123 current_paint_.setDrawStyle(style);
124 }
125
127 current_paint_.setStrokeWidth(width);
128 }
129
131 if (IsComplex()) {
132 return;
133 }
134 // Placeholder value here. This is a relatively cheap operation.
136 }
137
138 void drawPaint() override {
139 if (IsComplex()) {
140 return;
141 }
142 // Placeholder value here. This can be cheap (e.g. effectively a drawColor),
143 // or expensive (e.g. a bitmap shader with an image filter)
145 }
146
148 const sk_sp<DlImage> image,
149 const SkRect& src,
150 const SkRect& dst,
151 DlImageSampling sampling,
152 bool render_with_attributes,
153 SrcRectConstraint constraint = SrcRectConstraint::kFast) override {
154 if (IsComplex()) {
155 return;
156 }
158 render_with_attributes, constraint == SrcRectConstraint::kStrict);
159 }
160
161 void drawAtlas(const sk_sp<DlImage> atlas,
162 const SkRSXform xform[],
163 const SkRect tex[],
164 const DlColor colors[],
165 int count,
167 DlImageSampling sampling,
168 const SkRect* cull_rect,
169 bool render_with_attributes) override {
170 if (IsComplex()) {
171 return;
172 }
173 // This API just does a series of drawImage calls from the atlas
174 // This is equivalent to calling drawImageRect lots of times
175 for (int i = 0; i < count; i++) {
176 ImageRect(SkISize::Make(tex[i].width(), tex[i].height()), true,
177 render_with_attributes, true);
178 }
179 }
180
181 // This method finalizes the complexity score calculation and returns it
182 unsigned int ComplexityScore() {
183 // We hit our ceiling, so return that
184 if (IsComplex()) {
185 return Ceiling();
186 }
187
188 // Calculate the impact of any draw ops where the complexity is dependent
189 // on the number of calls made.
190 unsigned int batched_complexity = BatchedComplexity();
191
192 // Check for overflow
193 if (Ceiling() - complexity_score_ < batched_complexity) {
194 return Ceiling();
195 }
196
197 return complexity_score_ + batched_complexity;
198 }
199
200 protected:
201 void AccumulateComplexity(unsigned int complexity) {
202 // Check to see if we will overflow by accumulating this complexity score
203 if (ceiling_ - complexity_score_ < complexity) {
204 is_complex_ = true;
205 return;
206 }
207
208 complexity_score_ += complexity;
209 }
210
211 inline bool IsAntiAliased() { return current_paint_.isAntiAlias(); }
212 inline bool IsHairline() { return current_paint_.getStrokeWidth() == 0.0f; }
213 inline DlDrawStyle DrawStyle() { return current_paint_.getDrawStyle(); }
214 inline bool IsComplex() { return is_complex_; }
215 inline unsigned int Ceiling() { return ceiling_; }
216 inline unsigned int CurrentComplexityScore() { return complexity_score_; }
217
219 unsigned int line_verb_cost,
220 unsigned int quad_verb_cost,
221 unsigned int conic_verb_cost,
222 unsigned int cubic_verb_cost) {
223 int verb_count = path.countVerbs();
224 std::vector<uint8_t> verbs(verb_count);
225 path.getVerbs(verbs.data(), verbs.size());
226
227 unsigned int complexity = 0;
228 for (int i = 0; i < verb_count; i++) {
229 switch (verbs[i]) {
231 complexity += line_verb_cost;
232 break;
234 complexity += quad_verb_cost;
235 break;
237 complexity += conic_verb_cost;
238 break;
240 complexity += cubic_verb_cost;
241 break;
242 }
243 }
244 return complexity;
245 }
246
247 virtual void ImageRect(const SkISize& size,
248 bool texture_backed,
249 bool render_with_attributes,
250 bool enforce_src_edges) = 0;
251
252 // This calculates and returns the cost of draw calls which are batched and
253 // thus have a time cost proportional to the number of draw calls made, such
254 // as saveLayer and drawTextBlob.
255 virtual unsigned int BatchedComplexity() = 0;
256
257 private:
258 DlPaint current_paint_;
259
260 // If we exceed the ceiling (defaults to the largest number representable
261 // by unsigned int), then set the is_complex_ bool and we no longer
262 // accumulate.
263 bool is_complex_ = false;
264 unsigned int ceiling_;
265
266 unsigned int complexity_score_ = 0;
267};
268
269} // namespace flutter
270
271#endif // FLUTTER_DISPLAY_LIST_BENCHMARKING_DL_COMPLEXITY_HELPER_H_
int count
SkColor4f color
SkISize dimensions() const
Definition SkImage.h:297
virtual bool isTextureBacked() const =0
@ kConic_Verb
Definition SkPath.h:1461
@ kCubic_Verb
Definition SkPath.h:1462
@ kQuad_Verb
Definition SkPath.h:1460
@ kLine_Verb
Definition SkPath.h:1459
void setPathEffect(const DlPathEffect *effect) override
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:58
DlPaint & setAntiAlias(bool isAntiAlias)
Definition dl_paint.h:59
DlPaint & setStrokeWidth(float width)
Definition dl_paint.h:117
DlDrawStyle getDrawStyle() const
Definition dl_paint.h:92
float getStrokeWidth() const
Definition dl_paint.h:116
DlPaint & setDrawStyle(DlDrawStyle style)
Definition dl_paint.h:95
sk_sp< SkImage > image
Definition examples.cpp:29
SkBitmap source
Definition examples.cpp:28
float SkScalar
Definition extension.cpp:12
gboolean invert
DlStrokeJoin
Definition dl_paint.h:38
DlStrokeCap
Definition dl_paint.h:29
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:20
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
int32_t height
int32_t width
static constexpr SkISize Make(int32_t w, int32_t h)
Definition SkSize.h:20