Flutter Engine
 
Loading...
Searching...
No Matches
dl_complexity_gl.cc
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
6
7// The numbers and weightings used in this file stem from taking the
8// data from the DisplayListBenchmarks suite run on an Pixel 4 and
9// applying very rough analysis on them to identify the approximate
10// trends.
11//
12// See the comments in display_list_complexity_helper.h for details on the
13// process and rationale behind coming up with these numbers.
14
15namespace flutter {
16
17DisplayListGLComplexityCalculator*
18 DisplayListGLComplexityCalculator::instance_ = nullptr;
19
20DisplayListGLComplexityCalculator*
22 if (instance_ == nullptr) {
23 instance_ = new DisplayListGLComplexityCalculator();
24 }
25 return instance_;
26}
27
28unsigned int DisplayListGLComplexityCalculator::GLHelper::BatchedComplexity() {
29 // Calculate the impact of saveLayer.
30 unsigned int save_layer_complexity;
31 if (save_layer_count_ == 0) {
32 save_layer_complexity = 0;
33 } else {
34 // m = 1/5
35 // c = 10
36 save_layer_complexity = (save_layer_count_ + 50) * 40000;
37 }
38
39 unsigned int draw_text_blob_complexity;
40 if (draw_text_count_ == 0) {
41 draw_text_blob_complexity = 0;
42 } else {
43 // m = 1/240
44 // c = 0.25
45 draw_text_blob_complexity = (draw_text_count_ + 60) * 2500 / 3;
46 }
47
48 return save_layer_complexity + draw_text_blob_complexity;
49}
50
51void DisplayListGLComplexityCalculator::GLHelper::saveLayer(
52 const DlRect& bounds,
53 const SaveLayerOptions options,
54 const DlImageFilter* backdrop,
55 std::optional<int64_t> backdrop_id) {
56 if (IsComplex()) {
57 return;
58 }
59 if (backdrop) {
60 // Flutter does not offer this operation so this value can only ever be
61 // non-null for a frame-wide builder which is not currently evaluated for
62 // complexity.
63 AccumulateComplexity(Ceiling());
64 }
65 save_layer_count_++;
66}
67
68void DisplayListGLComplexityCalculator::GLHelper::drawLine(const DlPoint& p0,
69 const DlPoint& p1) {
70 if (IsComplex()) {
71 return;
72 }
73
74 // There is a relatively high fixed overhead cost for drawLine on OpenGL.
75 // Further, there is a strange bump where the cost of drawing a line of
76 // length ~500px is actually more costly than drawing a line of length
77 // ~1000px. The calculations here will be for a linear graph that
78 // approximate the overall trend.
79
80 float non_hairline_penalty = 1.0f;
81 unsigned int aa_penalty = 1;
82
83 // The non-hairline penalty is insignificant when AA is on.
84 if (!IsHairline() && !IsAntiAliased()) {
85 non_hairline_penalty = 1.15f;
86 }
87 if (IsAntiAliased()) {
88 aa_penalty = 2;
89 }
90
91 // Use an approximation for the distance to avoid floating point or
92 // sqrt() calls.
93 DlScalar distance = abs(p0.x - p1.x) + abs(p0.y - p1.y);
94
95 // The baseline complexity is for a hairline stroke with no AA.
96 // m = 1/40
97 // c = 13
98 unsigned int complexity =
99 ((distance + 520) / 2) * non_hairline_penalty * aa_penalty;
100
101 AccumulateComplexity(complexity);
102}
103
104void DisplayListGLComplexityCalculator::GLHelper::drawDashedLine(
105 const DlPoint& p0,
106 const DlPoint& p1,
107 DlScalar on_length,
108 DlScalar off_length) {
109 // Dashing is slightly more complex than a regular drawLine, but this
110 // op is so rare it is not worth measuring the difference.
111 drawLine(p0, p1);
112}
113
114void DisplayListGLComplexityCalculator::GLHelper::drawRect(const DlRect& rect) {
115 if (IsComplex()) {
116 return;
117 }
118
119 unsigned int complexity;
120
121 // If stroked, cost scales linearly with the rectangle width/height.
122 // If filled, it scales with the area.
123 //
124 // Hairline stroke vs non hairline has no significant penalty.
125 //
126 // There is also a kStrokeAndFill_Style that Skia exposes, but we do not
127 // currently use it anywhere in Flutter.
128 if (DrawStyle() == DlDrawStyle::kFill) {
129 // No real difference for AA with filled styles
130 unsigned int area = rect.GetWidth() * rect.GetHeight();
131
132 // m = 1/3500
133 // c = 0
134 complexity = area * 2 / 175;
135 } else {
136 // Take the average of the width and height.
137 unsigned int length = (rect.GetWidth() + rect.GetHeight()) / 2;
138
139 if (IsAntiAliased()) {
140 // m = 1/30
141 // c = 0
142 complexity = length * 4 / 3;
143 } else {
144 // If AA is disabled, the data shows that at larger sizes the overall
145 // cost comes down, peaking at around 1000px. As we don't anticipate
146 // rasterising rects with AA disabled to be all that frequent, just treat
147 // it as a straight line that peaks at 1000px, beyond which it stays
148 // constant. The rationale here is that it makes more sense to
149 // overestimate than to start decreasing the cost as the length goes up.
150 //
151 // This should be a reasonable approximation as it doesn't decrease by
152 // much from 1000px to 2000px.
153 //
154 // m = 1/20
155 // c = 0
156 complexity = std::min(length, 1000u) * 2;
157 }
158 }
159
160 AccumulateComplexity(complexity);
161}
162
163void DisplayListGLComplexityCalculator::GLHelper::drawOval(
164 const DlRect& bounds) {
165 if (IsComplex()) {
166 return;
167 }
168 // DrawOval scales very roughly linearly with the bounding box width/height
169 // (not area) for stroked styles without AA.
170 //
171 // Filled styles and stroked styles with AA scale linearly with the bounding
172 // box area.
173 unsigned int area = bounds.GetWidth() * bounds.GetHeight();
174
175 unsigned int complexity;
176
177 // There is also a kStrokeAndFill_Style that Skia exposes, but we do not
178 // currently use it anywhere in Flutter.
179 if (DrawStyle() == DlDrawStyle::kFill) {
180 // With filled styles, there is no significant AA penalty.
181 // m = 1/6000
182 // c = 0
183 complexity = area / 30;
184 } else {
185 if (IsAntiAliased()) {
186 // m = 1/4000
187 // c = 0
188 complexity = area / 20;
189 } else {
190 // Take the average of the width and height.
191 unsigned int length = (bounds.GetWidth() + bounds.GetHeight()) / 2;
192
193 // m = 1/75
194 // c = 0
195 complexity = length * 8 / 3;
196 }
197 }
198
199 AccumulateComplexity(complexity);
200}
201
202void DisplayListGLComplexityCalculator::GLHelper::drawCircle(
203 const DlPoint& center,
204 DlScalar radius) {
205 if (IsComplex()) {
206 return;
207 }
208
209 unsigned int complexity;
210
211 // There is also a kStrokeAndFill_Style that Skia exposes, but we do not
212 // currently use it anywhere in Flutter.
213 if (DrawStyle() == DlDrawStyle::kFill) {
214 // We can ignore pi here
215 unsigned int area = radius * radius;
216 // m = 1/525
217 // c = 50
218 complexity = (area + 26250) * 8 / 105;
219
220 // Penalty of around 8% when AA is disabled.
221 if (!IsAntiAliased()) {
222 complexity *= 1.08f;
223 }
224 } else {
225 // Hairline vs non-hairline has no significant performance difference.
226 if (IsAntiAliased()) {
227 // m = 1/3
228 // c = 10
229 complexity = (radius + 30) * 40 / 3;
230 } else {
231 // m = 1/10
232 // c = 20
233 complexity = (radius + 200) * 4;
234 }
235 }
236
237 AccumulateComplexity(complexity);
238}
239
240void DisplayListGLComplexityCalculator::GLHelper::drawRoundRect(
241 const DlRoundRect& rrect) {
242 if (IsComplex()) {
243 return;
244 }
245
246 // Drawing RRects is split into three performance tiers:
247 //
248 // 1) All stroked styles without AA *except* simple/symmetric RRects.
249 // 2) All filled styles and symmetric stroked styles w/AA.
250 // 3) Remaining stroked styles with AA.
251 //
252 // 1) and 3) scale linearly with length, 2) scales with area.
253
254 unsigned int complexity;
255
256 // These values were worked out by creating a straight line graph (y=mx+c)
257 // approximately matching the measured data, normalising the data so that
258 // 0.0005ms resulted in a score of 100 then simplifying down the formula.
259 if (DrawStyle() == DlDrawStyle::kFill ||
260 ((rrect.GetRadii().AreAllCornersSame()) && IsAntiAliased())) {
261 unsigned int area = rrect.GetBounds().Area();
262 // m = 1/3200
263 // c = 0.5
264 complexity = (area + 1600) / 80;
265 } else {
266 // Take the average of the width and height.
267 unsigned int length =
268 (rrect.GetBounds().GetWidth() + rrect.GetBounds().GetHeight()) / 2;
269
270 // There is some difference between hairline and non-hairline performance
271 // but the spread is relatively inconsistent and it's pretty much a wash.
272 if (IsAntiAliased()) {
273 // m = 1/25
274 // c = 1
275 complexity = (length + 25) * 8 / 5;
276 } else {
277 // m = 1/50
278 // c = 0.75
279 complexity = ((length * 2) + 75) * 2 / 5;
280 }
281 }
282
283 AccumulateComplexity(complexity);
284}
285
286void DisplayListGLComplexityCalculator::GLHelper::drawDiffRoundRect(
287 const DlRoundRect& outer,
288 const DlRoundRect& inner) {
289 if (IsComplex()) {
290 return;
291 }
292 // There are roughly four classes here:
293 // a) Filled style.
294 // b) Complex RRect type with AA enabled and filled style.
295 // c) Stroked style with AA enabled.
296 // d) Stroked style with AA disabled.
297 //
298 // a) and b) scale linearly with the area, c) and d) scale linearly with
299 // a single dimension (length). In all cases, the dimensions refer to
300 // the outer RRect.
301
302 unsigned int complexity;
303
304 // These values were worked out by creating a straight line graph (y=mx+c)
305 // approximately matching the measured data, normalising the data so that
306 // 0.0005ms resulted in a score of 100 then simplifying down the formula.
307 //
308 // There is also a kStrokeAndFill_Style that Skia exposes, but we do not
309 // currently use it anywhere in Flutter.
310 if (DrawStyle() == DlDrawStyle::kFill) {
311 unsigned int area = outer.GetBounds().Area();
312 if (!outer.GetRadii().AreAllCornersSame()) {
313 // m = 1/500
314 // c = 0.5
315 complexity = (area + 250) / 5;
316 } else {
317 // m = 1/1600
318 // c = 2
319 complexity = (area + 3200) / 16;
320 }
321 } else {
322 unsigned int length =
323 (outer.GetBounds().GetWidth() + outer.GetBounds().GetHeight()) / 2;
324 if (IsAntiAliased()) {
325 // m = 1/15
326 // c = 1
327 complexity = (length + 15) * 20 / 3;
328 } else {
329 // m = 1/27
330 // c = 0.5
331 complexity = ((length * 2) + 27) * 50 / 27;
332 }
333 }
334
335 AccumulateComplexity(complexity);
336}
337
338void DisplayListGLComplexityCalculator::GLHelper::drawRoundSuperellipse(
339 const DlRoundSuperellipse& rse) {
340 // Drawing RSEs on Skia falls back to RRect.
341 drawRoundRect(rse.ToApproximateRoundRect());
342}
343
344void DisplayListGLComplexityCalculator::GLHelper::drawPath(const DlPath& path) {
345 if (IsComplex()) {
346 return;
347 }
348 // There is negligible effect on the performance for hairline vs. non-hairline
349 // stroke widths.
350 //
351 // The data for filled styles is currently suspicious, so for now we are going
352 // to assign scores based on stroked styles.
353
354 unsigned int line_verb_cost, quad_verb_cost, conic_verb_cost, cubic_verb_cost;
355 unsigned int complexity;
356
357 if (IsAntiAliased()) {
358 // There seems to be a fixed cost of around 1ms for calling drawPath with
359 // AA.
360 complexity = 200000;
361
362 line_verb_cost = 235;
363 quad_verb_cost = 365;
364 conic_verb_cost = 365;
365 cubic_verb_cost = 725;
366 } else {
367 // There seems to be a fixed cost of around 0.25ms for calling drawPath.
368 // without AA
369 complexity = 50000;
370
371 line_verb_cost = 135;
372 quad_verb_cost = 150;
373 conic_verb_cost = 200;
374 cubic_verb_cost = 235;
375 }
376
377 complexity += CalculatePathComplexity(path, line_verb_cost, quad_verb_cost,
378 conic_verb_cost, cubic_verb_cost);
379
380 AccumulateComplexity(complexity);
381}
382
383void DisplayListGLComplexityCalculator::GLHelper::drawArc(
384 const DlRect& oval_bounds,
385 DlScalar start_degrees,
386 DlScalar sweep_degrees,
387 bool use_center) {
388 if (IsComplex()) {
389 return;
390 }
391 // Hairline vs non-hairline makes no difference to the performance.
392 // Stroked styles without AA scale linearly with the log of the diameter.
393 // Stroked styles with AA scale linearly with the area.
394 // Filled styles scale lienarly with the area.
395 unsigned int area = oval_bounds.GetWidth() * oval_bounds.GetHeight();
396 unsigned int complexity;
397
398 // These values were worked out by creating a straight line graph (y=mx+c)
399 // approximately matching the measured data, normalising the data so that
400 // 0.0005ms resulted in a score of 100 then simplifying down the formula.
401 //
402 // There is also a kStrokeAndFill_Style that Skia exposes, but we do not
403 // currently use it anywhere in Flutter.
404 if (DrawStyle() == DlDrawStyle::kStroke) {
405 if (IsAntiAliased()) {
406 // m = 1/3800
407 // c = 12
408 complexity = (area + 45600) / 171;
409 } else {
410 unsigned int diameter =
411 (oval_bounds.GetWidth() + oval_bounds.GetHeight()) / 2;
412 // m = 15
413 // c = -100
414 // This should never go negative though, so use std::max to ensure
415 // c is never larger than 15*log_diameter.
416 //
417 // Pre-multiply by 15 here so we get a little bit more precision.
418 unsigned int log_diameter = 15 * log(diameter);
419 complexity = (log_diameter - std::max(log_diameter, 100u)) * 200 / 9;
420 }
421 } else {
422 if (IsAntiAliased()) {
423 // m = 1/1000
424 // c = 10
425 complexity = (area + 10000) / 45;
426 } else {
427 // m = 1/6500
428 // c = 12
429 complexity = (area + 52000) * 2 / 585;
430 }
431 }
432
433 AccumulateComplexity(complexity);
434}
435
436void DisplayListGLComplexityCalculator::GLHelper::drawPoints(
437 DlPointMode mode,
438 uint32_t count,
439 const DlPoint points[]) {
440 if (IsComplex()) {
441 return;
442 }
443 unsigned int complexity;
444
445 if (IsAntiAliased()) {
446 if (mode == DlPointMode::kPoints) {
447 if (IsHairline()) {
448 // This is a special case, it triggers an extremely fast path.
449 // m = 1/4500
450 // c = 0
451 complexity = count * 400 / 9;
452 } else {
453 // m = 1/500
454 // c = 0
455 complexity = count * 400;
456 }
457 } else if (mode == DlPointMode::kLines) {
458 if (IsHairline()) {
459 // m = 1/750
460 // c = 0
461 complexity = count * 800 / 3;
462 } else {
463 // m = 1/500
464 // c = 0
465 complexity = count * 400;
466 }
467 } else {
468 if (IsHairline()) {
469 // m = 1/350
470 // c = 0
471 complexity = count * 4000 / 7;
472 } else {
473 // m = 1/250
474 // c = 0
475 complexity = count * 800;
476 }
477 }
478 } else {
479 if (mode == DlPointMode::kPoints) {
480 // Hairline vs non hairline makes no difference for points without AA.
481 // m = 1/18000
482 // c = 0.25
483 complexity = (count + 4500) * 100 / 9;
484 } else if (mode == DlPointMode::kLines) {
485 if (IsHairline()) {
486 // m = 1/8500
487 // c = 0.25
488 complexity = (count + 2125) * 400 / 17;
489 } else {
490 // m = 1/9000
491 // c = 0.25
492 complexity = (count + 2250) * 200 / 9;
493 }
494 } else {
495 // Polygon only really diverges for hairline vs non hairline at large
496 // point counts, and only by a few %.
497 // m = 1/7500
498 // c = 0.25
499 complexity = (count + 1875) * 80 / 3;
500 }
501 }
502
503 AccumulateComplexity(complexity);
504}
505
506void DisplayListGLComplexityCalculator::GLHelper::drawVertices(
507 const std::shared_ptr<DlVertices>& vertices,
508 DlBlendMode mode) {
509 // There is currently no way for us to get the VertexMode from the SkVertices
510 // object, but for future reference:
511 //
512 // TriangleStrip is roughly 25% more expensive than TriangleFan.
513 // TriangleFan is roughly 5% more expensive than Triangles.
514
515 // For the baseline, it's hard to identify the trend. It might be O(n^1/2)
516 // For now, treat it as linear as an approximation.
517 //
518 // m = 1/1600
519 // c = 1
520 unsigned int complexity = (vertices->vertex_count() + 1600) * 250 / 2;
521
522 AccumulateComplexity(complexity);
523}
524
525void DisplayListGLComplexityCalculator::GLHelper::drawImage(
526 const sk_sp<DlImage> image,
527 const DlPoint& point,
528 DlImageSampling sampling,
529 bool render_with_attributes) {
530 if (IsComplex()) {
531 return;
532 }
533 // AA vs non-AA has a cost but it's dwarfed by the overall cost of the
534 // drawImage call.
535 //
536 // The main difference is if the image is backed by a texture already or not
537 // If we don't need to upload, then the cost scales linearly with the
538 // length of the image. If it needs uploading, the cost scales linearly
539 // with the square of the area (!!!).
540 DlISize dimensions = image->GetSize();
541 unsigned int length = (dimensions.width + dimensions.height) / 2;
542 unsigned int area = dimensions.Area();
543
544 // m = 1/13
545 // c = 0
546 unsigned int complexity = length * 400 / 13;
547
548 if (!image->isTextureBacked()) {
549 // We can't square the area here as we'll overflow, so let's approximate
550 // by taking the calculated complexity score and applying a multiplier to
551 // it.
552 //
553 // (complexity * area / 60000) + 4000 gives a reasonable approximation with
554 // AA (complexity * area / 19000) gives a reasonable approximation without
555 // AA.
556 float multiplier;
557 if (IsAntiAliased()) {
558 multiplier = area / 60000.0f;
559 complexity = complexity * multiplier + 4000;
560 } else {
561 multiplier = area / 19000.0f;
562 complexity = complexity * multiplier;
563 }
564 }
565
566 AccumulateComplexity(complexity);
567}
568
569void DisplayListGLComplexityCalculator::GLHelper::ImageRect(
570 const DlISize& size,
571 bool texture_backed,
572 bool render_with_attributes,
573 bool enforce_src_edges) {
574 if (IsComplex()) {
575 return;
576 }
577 // Two main groups here - texture-backed and non-texture-backed images.
578 //
579 // Within each group, they all perform within a few % of each other *except*
580 // when we have a strict constraint and anti-aliasing enabled.
581
582 // These values were worked out by creating a straight line graph (y=mx+c)
583 // approximately matching the measured data, normalising the data so that
584 // 0.0005ms resulted in a score of 100 then simplifying down the formula.
585 unsigned int complexity;
586 if (!texture_backed || (texture_backed && render_with_attributes &&
587 enforce_src_edges && IsAntiAliased())) {
588 unsigned int area = size.Area();
589 // m = 1/4000
590 // c = 5
591 complexity = (area + 20000) / 10;
592 } else {
593 unsigned int length = (size.width + size.height) / 2;
594 // There's a little bit of spread here but the numbers are pretty large
595 // anyway.
596 //
597 // m = 1/22
598 // c = 0
599 complexity = length * 200 / 11;
600 }
601
602 AccumulateComplexity(complexity);
603}
604
605void DisplayListGLComplexityCalculator::GLHelper::drawImageNine(
606 const sk_sp<DlImage> image,
607 const DlIRect& center,
608 const DlRect& dst,
609 DlFilterMode filter,
610 bool render_with_attributes) {
611 if (IsComplex()) {
612 return;
613 }
614
615 DlISize dimensions = image->GetSize();
616 unsigned int area = dimensions.Area();
617
618 // m = 1/3600
619 // c = 3
620 unsigned int complexity = (area + 10800) / 9;
621
622 // Uploading incurs about a 40% performance penalty.
623 if (!image->isTextureBacked()) {
624 complexity *= 1.4f;
625 }
626
627 AccumulateComplexity(complexity);
628}
629
630void DisplayListGLComplexityCalculator::GLHelper::drawDisplayList(
631 const sk_sp<DisplayList> display_list,
632 DlScalar opacity) {
633 if (IsComplex()) {
634 return;
635 }
636 GLHelper helper(Ceiling() - CurrentComplexityScore());
637 if (opacity < SK_Scalar1 && !display_list->can_apply_group_opacity()) {
638 auto bounds = display_list->GetBounds();
639 helper.saveLayer(bounds, SaveLayerOptions::kWithAttributes, nullptr,
640 /*backdrop_id=*/-1);
641 }
642 display_list->Dispatch(helper);
643 AccumulateComplexity(helper.ComplexityScore());
644}
645
646void DisplayListGLComplexityCalculator::GLHelper::drawText(
647 const std::shared_ptr<DlText>& text,
648 DlScalar x,
649 DlScalar y) {
650 if (IsComplex()) {
651 return;
652 }
653
654 // DrawTextBlob has a high fixed cost, but if we call it multiple times
655 // per frame, that fixed cost is greatly reduced per subsequent call. This
656 // is likely because there is batching being done in SkCanvas.
657
658 // Increment draw_text_count_ and calculate the cost at the end.
659 draw_text_count_++;
660}
661
662void DisplayListGLComplexityCalculator::GLHelper::drawShadow(
663 const DlPath& path,
664 const DlColor color,
665 const DlScalar elevation,
666 bool transparent_occluder,
667 DlScalar dpr) {
668 if (IsComplex()) {
669 return;
670 }
671
672 // Elevation has no significant effect on the timings. Whether the shadow
673 // is cast by a transparent occluder or not has a small impact of around 5%.
674 //
675 // The path verbs do have an effect but only if the verb type is cubic; line,
676 // quad and conic all perform similarly.
677 float occluder_penalty = 1.0f;
678 if (transparent_occluder) {
679 occluder_penalty = 1.20f;
680 }
681
682 // The benchmark uses a test path of around 10 path elements. This is likely
683 // to be similar to what we see in real world usage, but we should benchmark
684 // different path lengths to see how much impact there is from varying the
685 // path length.
686 //
687 // For now, we will assume that there is no fixed overhead and that the time
688 // spent rendering the shadow for a path is split evenly amongst all the verbs
689 // enumerated.
690 unsigned int line_verb_cost = 17000; // 0.085ms
691 unsigned int quad_verb_cost = 20000; // 0.1ms
692 unsigned int conic_verb_cost = 20000; // 0.1ms
693 unsigned int cubic_verb_cost = 120000; // 0.6ms
694
695 unsigned int complexity = CalculatePathComplexity(
696 path, line_verb_cost, quad_verb_cost, conic_verb_cost, cubic_verb_cost);
697
698 AccumulateComplexity(complexity * occluder_penalty);
699}
700
701} // namespace flutter
static DisplayListGLComplexityCalculator * GetInstance()
static const SaveLayerOptions kWithAttributes
int32_t x
FlutterVulkanImage * image
size_t length
std::u16string text
double y
impeller::Scalar DlScalar
impeller::RoundRect DlRoundRect
impeller::Rect DlRect
impeller::ISize32 DlISize
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
impeller::RoundSuperellipse DlRoundSuperellipse
DlPointMode
Definition dl_types.h:15
@ kLines
draw each separate pair of points as a line segment
@ kPoints
draw each point separately
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 switch_defs.h:52
@ kStroke
strokes boundary of shapes
@ kFill
fills interior of shapes
impeller::IRect32 DlIRect
impeller::BlendMode DlBlendMode
impeller::Point DlPoint
std::vector< Point > points