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
13
14namespace flutter {
15
16// The Metal and OpenGL complexity calculators use benchmark data gathered
17// using the display_list_benchmarks test suite on real devices.
18//
19// Constants of proportionality and trends were chosen to better match
20// larger numbers rather than smaller ones. This may turn out to be the
21// wrong decision, but the rationale here is that with the smaller numbers,
22// we have:
23//
24// a) More noise.
25// b) Less absolute difference. If the constant we've chosen is out by 50%
26// on a measurement that is 0.001ms, that's less of an issue than if
27// the measurement is 15ms.
28// c) Smaller numbers affect the caching decision negligibly; the caching
29// decision is likely to be driven by slower ops rather than faster ones.
30//
31// In some cases, a cost penalty is used to figure out the cost of an
32// attribute such as anti-aliasing or fill style. In some of these, the
33// penalty is proportional to some other value such as the radius of
34// a circle. In these cases, we ensure that the penalty is never smaller
35// than 1.0f.
36//
37// The error bars in measurement are likely quite large and will
38// vary from device to device, so the goal here is not to provide a
39// perfectly accurate representation of a given DisplayList's
40// complexity but rather a very rough estimate that improves upon our
41// previous cache admission policy (op_count > 5).
42//
43// There will likely be future work that will improve upon the figures
44// in here. Notably, we do not take matrices or clips into account yet.
45//
46// The scoring is based around a baseline score of 100 being roughly
47// equivalent to 0.0005ms of time. With a 32-bit unsigned integer, this
48// would set the maximum time estimate associated with the complexity score
49// at about 21 seconds, which far exceeds what we would ever expect a
50// DisplayList to take rasterising.
51//
52// Finally, care has been taken to keep the computations as cheap as possible.
53// We need to be able to calculate the complexity as quickly as possible
54// so that we don't end up wasting too much time figuring out if something
55// should be cached or not and eat into the time we could have just spent
56// rasterising the DisplayList.
57//
58// In order to keep the computations cheap, the following tradeoffs were made:
59//
60// a) Limit all computations to simple division, multiplication, addition
61// and subtraction.
62// b) Try and use integer arithmetic as much as possible.
63// c) If a specific draw op is logarithmic in complexity, do a best fit
64// onto a linear equation within the range we expect to see the variables
65// fall within.
66//
67// As an example of the above, let's say we have some data that looks like the
68// complexity is something like O(n^1/3). We would like to avoid anything too
69// expensive to calculate, so taking the cube root of the value to try and
70// calculate the time cost should be avoided.
71//
72// In this case, the approach would be to take a straight line approximation
73// that maps closely in the range of n where we feel it is most likely to occur.
74// For example, if this were drawLine, and n were the line length, and we
75// expected the line lengths to typically be between 50 and 100, then we would
76// figure out the equation of the straight line graph that approximates the
77// n^1/3 curve, and if possible try and choose an approximation that is more
78// representative in the range of [50, 100] for n.
79//
80// Once that's done, we can develop the formula as follows:
81//
82// Take y = mx + c (straight line graph chosen as per guidelines above).
83// Divide by however many ops the benchmark ran for a single pass.
84// Multiply by 200,000 to normalise 0.0005ms = 100.
85// Simplify down the formula.
86//
87// So if we had m = 1/5 and c = 0, and the drawLines benchmark ran 10,000
88// drawLine calls per iteration:
89//
90// y (time taken) = x (line length) / 5
91// y = x / 5 * 200,000 / 10,000
92// y = x / 5 * 20
93// y = 4x
94
96 : public virtual DlOpReceiver,
97 public virtual IgnoreClipDispatchHelper,
98 public virtual IgnoreTransformDispatchHelper {
99 public:
100 explicit ComplexityCalculatorHelper(unsigned int ceiling)
101 : ceiling_(ceiling) {}
102
103 virtual ~ComplexityCalculatorHelper() = default;
104
105 void setInvertColors(bool invert) override {}
106 void setStrokeCap(DlStrokeCap cap) override {}
107 void setStrokeJoin(DlStrokeJoin join) override {}
108 void setStrokeMiter(DlScalar limit) override {}
109 void setColor(DlColor color) override {}
110 void setBlendMode(DlBlendMode mode) override {}
111 void setColorSource(const DlColorSource* source) override {}
112 void setImageFilter(const DlImageFilter* filter) override {}
113 void setColorFilter(const DlColorFilter* filter) 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
130 void drawColor(DlColor color, DlBlendMode mode) override {
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 DlRect& src,
150 const DlRect& dst,
151 DlImageSampling sampling,
152 bool render_with_attributes,
153 DlSrcRectConstraint constraint = DlSrcRectConstraint::kFast) override {
154 if (IsComplex()) {
155 return;
156 }
157 ImageRect(image->GetBounds().GetSize(), image->isTextureBacked(),
158 render_with_attributes,
159 constraint == DlSrcRectConstraint::kStrict);
160 }
161
162 void drawAtlas(const sk_sp<DlImage> atlas,
163 const DlRSTransform xform[],
164 const DlRect tex[],
165 const DlColor colors[],
166 int count,
167 DlBlendMode mode,
168 DlImageSampling sampling,
169 const DlRect* cull_rect,
170 bool render_with_attributes) override {
171 if (IsComplex()) {
172 return;
173 }
174 // This API just does a series of drawImage calls from the atlas
175 // This is equivalent to calling drawImageRect lots of times
176 for (int i = 0; i < count; i++) {
177 ImageRect(DlIRect::RoundOut(tex[i]).GetSize(), true,
178 render_with_attributes, true);
179 }
180 }
181
182 // This method finalizes the complexity score calculation and returns it
183 unsigned int ComplexityScore() {
184 // We hit our ceiling, so return that
185 if (IsComplex()) {
186 return Ceiling();
187 }
188
189 // Calculate the impact of any draw ops where the complexity is dependent
190 // on the number of calls made.
191 unsigned int batched_complexity = BatchedComplexity();
192
193 // Check for overflow
194 if (Ceiling() - complexity_score_ < batched_complexity) {
195 return Ceiling();
196 }
197
198 return complexity_score_ + batched_complexity;
199 }
200
201 protected:
202 void AccumulateComplexity(unsigned int complexity) {
203 // Check to see if we will overflow by accumulating this complexity score
204 if (ceiling_ - complexity_score_ < complexity) {
205 is_complex_ = true;
206 return;
207 }
208
209 complexity_score_ += complexity;
210 }
211
212 inline bool IsAntiAliased() { return current_paint_.isAntiAlias(); }
213 inline bool IsHairline() { return current_paint_.getStrokeWidth() == 0.0f; }
214 inline DlDrawStyle DrawStyle() { return current_paint_.getDrawStyle(); }
215 inline bool IsComplex() { return is_complex_; }
216 inline unsigned int Ceiling() { return ceiling_; }
217 inline unsigned int CurrentComplexityScore() { return complexity_score_; }
218
219 unsigned int CalculatePathComplexity(const DlPath& dl_path,
220 unsigned int line_verb_cost,
221 unsigned int quad_verb_cost,
222 unsigned int conic_verb_cost,
223 unsigned int cubic_verb_cost);
224
225 virtual void ImageRect(const DlISize& size,
226 bool texture_backed,
227 bool render_with_attributes,
228 bool enforce_src_edges) = 0;
229
230 // This calculates and returns the cost of draw calls which are batched and
231 // thus have a time cost proportional to the number of draw calls made, such
232 // as saveLayer and drawTextBlob.
233 virtual unsigned int BatchedComplexity() = 0;
234
235 private:
236 DlPaint current_paint_;
237
238 // If we exceed the ceiling (defaults to the largest number representable
239 // by unsigned int), then set the is_complex_ bool and we no longer
240 // accumulate.
241 bool is_complex_ = false;
242 unsigned int ceiling_;
243
244 unsigned int complexity_score_ = 0;
245};
246
247} // namespace flutter
248
249#endif // FLUTTER_DISPLAY_LIST_BENCHMARKING_DL_COMPLEXITY_HELPER_H_
void setInvertColors(bool invert) override
virtual void ImageRect(const DlISize &size, bool texture_backed, bool render_with_attributes, bool enforce_src_edges)=0
void setColorSource(const DlColorSource *source) override
void setStrokeWidth(DlScalar width) override
void setColor(DlColor color) override
virtual ~ComplexityCalculatorHelper()=default
void AccumulateComplexity(unsigned int complexity)
void drawImageRect(const sk_sp< DlImage > image, const DlRect &src, const DlRect &dst, DlImageSampling sampling, bool render_with_attributes, DlSrcRectConstraint constraint=DlSrcRectConstraint::kFast) override
void setMaskFilter(const DlMaskFilter *filter) override
void drawColor(DlColor color, DlBlendMode mode) 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 drawAtlas(const sk_sp< DlImage > atlas, const DlRSTransform xform[], const DlRect tex[], const DlColor colors[], int count, DlBlendMode mode, DlImageSampling sampling, const DlRect *cull_rect, bool render_with_attributes) override
unsigned int CalculatePathComplexity(const DlPath &dl_path, unsigned int line_verb_cost, unsigned int quad_verb_cost, unsigned int conic_verb_cost, unsigned int cubic_verb_cost)
void setBlendMode(DlBlendMode mode) override
void setStrokeMiter(DlScalar limit) override
void setDrawStyle(DlDrawStyle style) override
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:115
DlDrawStyle getDrawStyle() const
Definition dl_paint.h:90
float getStrokeWidth() const
Definition dl_paint.h:114
DlPaint & setDrawStyle(DlDrawStyle style)
Definition dl_paint.h:93
FlutterVulkanImage * image
impeller::Scalar DlScalar
DlStrokeJoin
Definition dl_paint.h:37
DlStrokeCap
Definition dl_paint.h:28
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all 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
DlDrawStyle
Definition dl_paint.h:19
DlSrcRectConstraint
Definition dl_types.h:21
BlendMode
Definition color.h:58
int32_t width
RoundOut(const TRect< U > &r)
Definition rect.h:679