Flutter Engine
 
Loading...
Searching...
No Matches
dl_benchmarks.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
12
13#include "third_party/skia/include/core/SkBitmap.h"
14#include "third_party/skia/include/core/SkImage.h"
15#include "third_party/skia/include/core/SkSurface.h"
16#include "third_party/skia/include/core/SkTextBlob.h"
17
18namespace flutter {
19namespace testing {
20
22 public:
23 void MoveTo(const DlPoint& p2, bool will_be_closed) override {
24 verb_count_++;
25 }
26 void LineTo(const DlPoint& p2) override { verb_count_++; }
27 void QuadTo(const DlPoint& cp, const DlPoint& p2) override { verb_count_++; }
28 bool ConicTo(const DlPoint& cp, const DlPoint& p2, DlScalar weight) override {
29 verb_count_++;
30 return false;
31 }
32 void CubicTo(const DlPoint& cp1,
33 const DlPoint& cp2,
34 const DlPoint& p2) override {
35 verb_count_++;
36 }
37 void Close() override { verb_count_++; }
38
39 uint32_t GetVerbCount() const { return verb_count_; }
40
41 private:
42 uint32_t verb_count_ = 0u;
43};
44
45DlPaint GetPaintForRun(unsigned attributes) {
46 DlPaint paint;
47
48 if (attributes & kStrokedStyle && attributes & kFilledStyle) {
49 // Not currently exposed by Flutter, but we can probably benchmark this in
50 // the future
52 } else if (attributes & kStrokedStyle) {
54 } else if (attributes & kFilledStyle) {
56 }
57
58 if (attributes & kHairlineStroke) {
59 paint.setStrokeWidth(0.0f);
60 } else {
61 paint.setStrokeWidth(1.0f);
62 }
63
64 paint.setAntiAlias(attributes & kAntiAliasing);
65 return paint;
66}
67
68void AnnotateAttributes(unsigned attributes,
69 benchmark::State& state,
70 const DisplayListAttributeFlags flags) {
71 if (flags.always_stroked()) {
72 state.counters["HairlineStroke"] = attributes & kHairlineStroke ? 1 : 0;
73 }
74 if (flags.applies_style()) {
75 state.counters["HairlineStroke"] = attributes & kHairlineStroke ? 1 : 0;
76 state.counters["StrokedStyle"] = attributes & kStrokedStyle ? 1 : 0;
77 state.counters["FilledStyle"] = attributes & kFilledStyle ? 1 : 0;
78 }
79 if (flags.applies_anti_alias()) {
80 state.counters["AntiAliasing"] = attributes & kAntiAliasing ? 1 : 0;
81 }
82}
83
84// Constants chosen to produce benchmark results in the region of 1-50ms
85constexpr size_t kLinesToDraw = 10000;
86constexpr size_t kRectsToDraw = 5000;
87constexpr size_t kOvalsToDraw = 1000;
88constexpr size_t kCirclesToDraw = 5000;
89constexpr size_t kRRectsToDraw = 5000;
90constexpr size_t kDRRectsToDraw = 2000;
91constexpr size_t kArcSweepSetsToDraw = 1000;
92constexpr size_t kImagesToDraw = 500;
93constexpr size_t kFixedCanvasSize = 1024;
94
95// Draw a series of diagonal lines across a square canvas of width/height of
96// the length requested. The lines will start from the top left corner to the
97// bottom right corner, and move from left to right (at the top) and from right
98// to left (at the bottom) until 10,000 lines are drawn.
99//
100// The resulting image will be an hourglass shape.
101void BM_DrawLine(benchmark::State& state,
102 BackendType backend_type,
103 unsigned attributes) {
104 auto surface_provider = DlSurfaceProvider::Create(backend_type);
105 DisplayListBuilder builder;
106 DlPaint paint = GetPaintForRun(attributes);
107
109
110 size_t length = state.range(0);
111
112 surface_provider->InitializeSurface(length, length);
113 auto surface = surface_provider->GetPrimarySurface();
114 auto canvas = surface->GetCanvas();
115
116 state.counters["DrawCallCount"] = kLinesToDraw;
117 for (size_t i = 0; i < kLinesToDraw; i++) {
118 builder.DrawLine(DlPoint(i % length, 0),
119 DlPoint(length - i % length, length), paint);
120 }
121
122 auto display_list = builder.Build();
123
124 // We only want to time the actual rasterization.
125 for ([[maybe_unused]] auto _ : state) {
126 canvas->DrawDisplayList(display_list);
127 surface->FlushSubmitCpuSync();
128 }
129
130 auto filename = surface_provider->backend_name() + "-DrawLine-" +
131 std::to_string(state.range(0)) + ".png";
132 surface_provider->Snapshot(filename);
133}
134
135// Draws a series of square rects of the requested width across
136// the canvas and repeats until `kRectsToDraw` rects have been drawn.
137//
138// Half the drawn rects will not have an integral offset.
139void BM_DrawRect(benchmark::State& state,
140 BackendType backend_type,
141 unsigned attributes) {
142 auto surface_provider = DlSurfaceProvider::Create(backend_type);
143 DisplayListBuilder builder;
144 DlPaint paint = GetPaintForRun(attributes);
145
147
148 size_t length = state.range(0);
149 size_t canvas_size = length * 2;
150 surface_provider->InitializeSurface(canvas_size, canvas_size);
151 auto surface = surface_provider->GetPrimarySurface();
152 auto canvas = surface->GetCanvas();
153
154 // As rects have DlScalar dimensions, we want to ensure that we also
155 // draw rects with non-integer position and size
156 const DlPoint offset(0.5f, 0.5f);
157 DlPoint origin;
159
160 state.counters["DrawCallCount"] = kRectsToDraw;
161 for (size_t i = 0; i < kRectsToDraw; i++) {
162 DlRect rect = DlRect::MakeOriginSize(origin, size);
163 builder.DrawRect(rect, paint);
164 origin += offset;
165 if (origin.x + size.width > canvas_size) {
166 origin.x -= canvas_size;
167 }
168 if (rect.GetBottom() > canvas_size) {
169 origin.y -= canvas_size;
170 }
171 }
172
173 auto display_list = builder.Build();
174
175 // We only want to time the actual rasterization.
176 for ([[maybe_unused]] auto _ : state) {
177 canvas->DrawDisplayList(display_list);
178 surface->FlushSubmitCpuSync();
179 }
180
181 auto filename = surface_provider->backend_name() + "-DrawRect-" +
182 std::to_string(state.range(0)) + ".png";
183 surface_provider->Snapshot(filename);
184}
185
186// Draws a series of ovals of the requested height with aspect ratio 3:2 across
187// the canvas and repeats until `kOvalsToDraw` ovals have been drawn.
188//
189// Half the drawn ovals will not have an integral offset.
190void BM_DrawOval(benchmark::State& state,
191 BackendType backend_type,
192 unsigned attributes) {
193 auto surface_provider = DlSurfaceProvider::Create(backend_type);
194 DisplayListBuilder builder;
195 DlPaint paint = GetPaintForRun(attributes);
196
198
199 size_t length = state.range(0);
200 size_t canvas_size = length * 2;
201 surface_provider->InitializeSurface(canvas_size, canvas_size);
202 auto surface = surface_provider->GetPrimarySurface();
203 auto canvas = surface->GetCanvas();
204
205 const DlPoint offset(0.5f, 0.5f);
206 DlPoint origin;
207 DlSize size(length * 1.5f, length);
208
209 state.counters["DrawCallCount"] = kOvalsToDraw;
210 for (size_t i = 0; i < kOvalsToDraw; i++) {
211 DlRect rect = DlRect::MakeOriginSize(origin, size);
212 builder.DrawOval(rect, paint);
213 origin += offset;
214 if (origin.x + size.width > canvas_size) {
215 origin.x -= canvas_size;
216 }
217 if (origin.y + size.height > canvas_size) {
218 origin.y -= canvas_size;
219 }
220 }
221 auto display_list = builder.Build();
222
223 // We only want to time the actual rasterization.
224 for ([[maybe_unused]] auto _ : state) {
225 canvas->DrawDisplayList(display_list);
226 surface->FlushSubmitCpuSync();
227 }
228
229 auto filename = surface_provider->backend_name() + "-DrawOval-" +
230 std::to_string(state.range(0)) + ".png";
231 surface_provider->Snapshot(filename);
232}
233
234// Draws a series of circles of the requested radius across
235// the canvas and repeats until `kCirclesToDraw` circles have been drawn.
236//
237// Half the drawn circles will not have an integral center point.
238void BM_DrawCircle(benchmark::State& state,
239 BackendType backend_type,
240 unsigned attributes) {
241 auto surface_provider = DlSurfaceProvider::Create(backend_type);
242 DisplayListBuilder builder;
243 DlPaint paint = GetPaintForRun(attributes);
244
246
247 size_t length = state.range(0);
248 size_t canvas_size = length * 2;
249 surface_provider->InitializeSurface(canvas_size, canvas_size);
250 auto surface = surface_provider->GetPrimarySurface();
251 auto canvas = surface->GetCanvas();
252
253 DlScalar radius = length / 2.0f;
254 const DlPoint offset(0.5f, 0.5f);
255
256 DlPoint center = DlPoint(radius, radius);
257
258 state.counters["DrawCallCount"] = kCirclesToDraw;
259 for (size_t i = 0; i < kCirclesToDraw; i++) {
260 builder.DrawCircle(center, radius, paint);
261 center += offset;
262 if (center.x + radius > canvas_size) {
263 center.x = radius;
264 }
265 if (center.y + radius > canvas_size) {
266 center.y = radius;
267 }
268 }
269 auto display_list = builder.Build();
270
271 // We only want to time the actual rasterization.
272 for ([[maybe_unused]] auto _ : state) {
273 canvas->DrawDisplayList(display_list);
274 surface->FlushSubmitCpuSync();
275 }
276
277 auto filename = surface_provider->backend_name() + "-DrawCircle-" +
278 std::to_string(state.range(0)) + ".png";
279 surface_provider->Snapshot(filename);
280}
281
282// Draws a series of rounded rects of the requested width across
283// the canvas and repeats until `kRRectsToDraw` rects have been drawn.
284//
285// Half the drawn rounded rects will not have an integral offset.
286void BM_DrawRRect(benchmark::State& state,
287 BackendType backend_type,
288 unsigned attributes,
289 RRectType type) {
290 auto surface_provider = DlSurfaceProvider::Create(backend_type);
291 DisplayListBuilder builder;
292 DlPaint paint = GetPaintForRun(attributes);
293
295
296 size_t length = state.range(0);
297 size_t canvas_size = length * 2;
298 surface_provider->InitializeSurface(canvas_size, canvas_size);
299 auto surface = surface_provider->GetPrimarySurface();
300 auto canvas = surface->GetCanvas();
301
302 DlRoundingRadii radii;
303 switch (type) {
305 radii.top_left = DlSize(5.0f, 5.0f);
306 radii.top_right = DlSize(5.0f, 5.0f);
307 radii.bottom_right = DlSize(5.0f, 5.0f);
308 radii.bottom_left = DlSize(5.0f, 5.0f);
309 break;
311 radii.top_left = DlSize(5.0f, 2.0f);
312 radii.top_right = DlSize(3.0f, 2.0f);
313 radii.bottom_right = DlSize(3.0f, 4.0f);
314 radii.bottom_left = DlSize(5.0f, 4.0f);
315 break;
317 radii.top_left = DlSize(5.0f, 4.0f);
318 radii.top_right = DlSize(4.0f, 5.0f);
319 radii.bottom_right = DlSize(3.0f, 6.0f);
320 radii.bottom_left = DlSize(2.0f, 7.0f);
321 break;
322 default:
324 }
325
326 const DlScalar offset = 0.5f;
327 const DlScalar multiplier = length / 16.0f;
328
330 DlRect::MakeLTRB(0, 0, length, length), radii * multiplier);
331
332 state.counters["DrawCallCount"] = kRRectsToDraw;
333 for (size_t i = 0; i < kRRectsToDraw; i++) {
334 builder.DrawRoundRect(rrect, paint);
335 rrect = rrect.Shift(offset, offset);
336 if (rrect.GetBounds().GetRight() > canvas_size) {
337 rrect = rrect.Shift(-canvas_size, 0);
338 }
339 if (rrect.GetBounds().GetBottom() > canvas_size) {
340 rrect = rrect.Shift(0, -canvas_size);
341 }
342 }
343 auto display_list = builder.Build();
344
345 // We only want to time the actual rasterization.
346 for ([[maybe_unused]] auto _ : state) {
347 canvas->DrawDisplayList(display_list);
348 surface->FlushSubmitCpuSync();
349 }
350
351 auto filename = surface_provider->backend_name() + "-DrawRRect-" +
352 std::to_string(state.range(0)) + ".png";
353 surface_provider->Snapshot(filename);
354}
355
356// Draws a series of "DR" rects of the requested width across
357// the canvas and repeats until `kRRectsToDraw` rects have been drawn.
358//
359// A "DR" rect is a shape consisting of the difference between two
360// rounded rects.
361//
362// Half the drawn DR rects will not have an integral offset.
363void BM_DrawDRRect(benchmark::State& state,
364 BackendType backend_type,
365 unsigned attributes,
366 RRectType type) {
367 auto surface_provider = DlSurfaceProvider::Create(backend_type);
368 DisplayListBuilder builder;
369 DlPaint paint = GetPaintForRun(attributes);
370
372
373 size_t length = state.range(0);
374 size_t canvas_size = length * 2;
375 surface_provider->InitializeSurface(canvas_size, canvas_size);
376 auto surface = surface_provider->GetPrimarySurface();
377 auto canvas = surface->GetCanvas();
378
379 DlRoundingRadii radii;
380 switch (type) {
382 radii.top_left = DlSize(5.0f, 5.0f);
383 radii.top_right = DlSize(5.0f, 5.0f);
384 radii.bottom_right = DlSize(5.0f, 5.0f);
385 radii.bottom_left = DlSize(5.0f, 5.0f);
386 break;
388 radii.top_left = DlSize(5.0f, 7.0f);
389 radii.top_right = DlSize(3.0f, 7.0f);
390 radii.bottom_right = DlSize(3.0f, 4.0f);
391 radii.bottom_left = DlSize(5.0f, 4.0f);
392 break;
394 radii.top_left = DlSize(5.0f, 4.0f);
395 radii.top_right = DlSize(4.0f, 5.0f);
396 radii.bottom_right = DlSize(3.0f, 6.0f);
397 radii.bottom_left = DlSize(8.0f, 7.0f);
398 break;
399 default:
401 }
402
403 const DlScalar offset = 0.5f;
404 const DlScalar multiplier = length / 16.0f;
405
407 DlRect::MakeLTRB(0, 0, length, length), radii * multiplier);
409 DlRect::MakeLTRB(0, 0, length, length).Expand(-0.1f * length),
410 radii * multiplier);
411
412 state.counters["DrawCallCount"] = kDRRectsToDraw;
413 for (size_t i = 0; i < kDRRectsToDraw; i++) {
414 builder.DrawDiffRoundRect(rrect, rrect_2, paint);
415 rrect = rrect.Shift(offset, offset);
416 rrect_2 = rrect_2.Shift(offset, offset);
417 if (rrect.GetBounds().GetRight() > canvas_size) {
418 rrect = rrect.Shift(-canvas_size, 0);
419 rrect_2 = rrect_2.Shift(-canvas_size, 0);
420 }
421 if (rrect.GetBounds().GetBottom() > canvas_size) {
422 rrect = rrect.Shift(0, -canvas_size);
423 rrect_2 = rrect_2.Shift(0, -canvas_size);
424 }
425 }
426 auto display_list = builder.Build();
427
428 // We only want to time the actual rasterization.
429 for ([[maybe_unused]] auto _ : state) {
430 canvas->DrawDisplayList(display_list);
431 surface->FlushSubmitCpuSync();
432 }
433
434 auto filename = surface_provider->backend_name() + "-DrawDRRect-" +
435 std::to_string(state.range(0)) + ".png";
436 surface_provider->Snapshot(filename);
437}
438
439void BM_DrawArc(benchmark::State& state,
440 BackendType backend_type,
441 unsigned attributes) {
442 auto surface_provider = DlSurfaceProvider::Create(backend_type);
443 DisplayListBuilder builder;
444 DlPaint paint = GetPaintForRun(attributes);
445
446 AnnotateAttributes(attributes, state,
448
449 size_t length = state.range(0);
450 size_t canvas_size = length * 2;
451 surface_provider->InitializeSurface(canvas_size, canvas_size);
452 auto surface = surface_provider->GetPrimarySurface();
453 auto canvas = surface->GetCanvas();
454
455 DlScalar starting_angle = 0.0f;
456 DlPoint offset(0.5f, 0.5f);
457
458 // Just some random sweeps that will mostly circumnavigate the circle
459 std::vector<DlScalar> segment_sweeps = {5.5f, -10.0f, 42.0f, 71.7f, 90.0f,
460 37.5f, 17.9f, 32.0f, 379.4f};
461
462 DlPoint origin;
464
465 state.counters["DrawCallCount"] = kArcSweepSetsToDraw * segment_sweeps.size();
466 for (size_t i = 0; i < kArcSweepSetsToDraw; i++) {
467 DlRect bounds = DlRect::MakeOriginSize(origin, size);
468 for (DlScalar sweep : segment_sweeps) {
469 builder.DrawArc(bounds, starting_angle, sweep, false, paint);
470 starting_angle += sweep + 5.0f;
471 }
472 origin += offset;
473 if (origin.x + size.width > canvas_size) {
474 origin.x -= canvas_size;
475 }
476 if (origin.y + size.height > canvas_size) {
477 origin.y -= canvas_size;
478 }
479 }
480
481 auto display_list = builder.Build();
482
483 // We only want to time the actual rasterization.
484 for ([[maybe_unused]] auto _ : state) {
485 canvas->DrawDisplayList(display_list);
486 surface->FlushSubmitCpuSync();
487 }
488
489 auto filename = surface_provider->backend_name() + "-DrawArc-" +
490 std::to_string(state.range(0)) + ".png";
491 surface_provider->Snapshot(filename);
492}
493
494// Returns a list of DlPoints that represent `n` points equally spaced out
495// along the circumference of a circle with radius `r` and centered on `center`.
496std::vector<DlPoint> GetPolygonPoints(size_t n, DlPoint center, DlScalar r) {
497 std::vector<DlPoint> points;
498 DlScalar x, y;
499 float angle;
500 float full_circle = 2.0f * M_PI;
501 for (size_t i = 0; i < n; i++) {
502 angle = (full_circle / static_cast<float>(n)) * static_cast<float>(i);
503 x = center.x + r * std::cosf(angle);
504 y = center.y + r * std::sinf(angle);
505 points.emplace_back(x, y);
506 }
507 return points;
508}
509
510// Creates a path that represents a regular polygon with `sides` sides,
511// centered on `center` with a radius of `radius`. The control points are
512// equally spaced out along the circumference of the circle described by
513// `radius` and `center`.
514//
515// The path segment connecting each control point is a line segment.
516void GetLinesPath(DlPathBuilder& path_builder,
517 size_t sides,
518 DlPoint center,
519 float radius) {
520 std::vector<DlPoint> points = GetPolygonPoints(sides, center, radius);
521 path_builder.MoveTo(points[0]);
522 for (size_t i = 1; i < sides; i++) {
523 path_builder.LineTo(points[i]);
524 }
525 path_builder.LineTo(points[0]);
526 path_builder.Close();
527}
528
529// Creates a path that represents a regular polygon with `sides` sides,
530// centered on `center` with a radius of `radius`. The control points are
531// equally spaced out along the circumference of the circle described by
532// `radius` and `center`.
533//
534// The path segment connecting each control point is a quad bezier, with the
535// bezier control point being on a circle with 80% of `radius` and with the
536// control point angle half way between the start and end point angles for the
537// polygon segment.
538void GetQuadsPath(DlPathBuilder& path_builder,
539 size_t sides,
540 DlPoint center,
541 float radius) {
542 std::vector<DlPoint> points = GetPolygonPoints(sides, center, radius);
543 std::vector<DlPoint> control_points =
544 GetPolygonPoints(sides * 2, center, radius * 0.8f);
545
546 path_builder.MoveTo(points[0]);
547 for (size_t i = 1; i < sides; i++) {
548 path_builder.QuadraticCurveTo(control_points[2 * i - 1], points[i]);
549 }
550 path_builder.QuadraticCurveTo(control_points[2 * sides - 1], points[0]);
551 path_builder.Close();
552}
553
554// Creates a path that represents a regular polygon with `sides` sides,
555// centered on `center` with a radius of `radius`. The control points are
556// equally spaced out along the circumference of the circle described by
557// `radius` and `center`.
558//
559// The path segment connecting each control point is a conic, with the
560// control point being on a circle with 80% of `radius` and with the
561// control point angle half way between the start and end point angles for the
562// polygon segment, and the conic weight set to 3.7f.
563void GetConicsPath(DlPathBuilder& path_builder,
564 size_t sides,
565 DlPoint center,
566 float radius) {
567 std::vector<DlPoint> points = GetPolygonPoints(sides, center, radius);
568 std::vector<DlPoint> control_points =
569 GetPolygonPoints(sides * 2, center, radius * 0.8f);
570
571 path_builder.MoveTo(points[0]);
572 for (size_t i = 1; i < sides; i++) {
573 path_builder.ConicCurveTo(control_points[2 * i - 1], points[i], 3.7f);
574 }
575 path_builder.ConicCurveTo(control_points[2 * sides - 1], points[0], 3.7f);
576 path_builder.Close();
577}
578
579// Creates a path that represents a regular polygon with `sides` sides,
580// centered on `center` with a radius of `radius`. The control points are
581// equally spaced out along the circumference of the circle described by
582// `radius` and `center`.
583//
584// The path segment connecting each control point is a cubic, with the first
585// control point being on a circle with 80% of `radius` and with the second
586// control point being on a circle with 120% of `radius`. The first
587// control point is 1/3, and the second control point is 2/3, of the angle
588// between the start and end point angles for the polygon segment.
589void GetCubicsPath(DlPathBuilder& path_builder,
590 size_t sides,
591 DlPoint center,
592 float radius) {
593 std::vector<DlPoint> points = GetPolygonPoints(sides, center, radius);
594 std::vector<DlPoint> inner_control_points =
595 GetPolygonPoints(sides * 3, center, radius * 0.8f);
596 std::vector<DlPoint> outer_control_points =
597 GetPolygonPoints(sides * 3, center, radius * 1.2f);
598
599 path_builder.MoveTo(points[0]);
600 for (size_t i = 1; i < sides; i++) {
601 path_builder.CubicCurveTo(inner_control_points[3 * i - 2],
602 outer_control_points[3 * i - 1], points[i]);
603 }
604 path_builder.CubicCurveTo(inner_control_points[3 * sides - 2],
605 outer_control_points[3 * sides - 1], points[0]);
606 path_builder.Close();
607}
608
609// Returns a path generated by one of the above path generators
610// which is multiplied `number` times centered on each of the `number` control
611// points along the circumference of a circle centered on `center` with radius
612// `radius`.
613//
614// Each of the polygons will have `sides` sides, and the resulting path will be
615// bounded by a circle with radius of 150% of `radius` (or another 20% on top of
616// that for cubics)
617void MultiplyPath(DlPathBuilder& path_builder,
619 DlPoint center,
620 size_t sides,
621 size_t number,
622 float radius) {
623 std::vector<DlPoint> center_points =
624 GetPolygonPoints(number, center, radius / 2.0f);
625
626 for (DlPoint p : center_points) {
627 switch (type) {
628 case PathVerb::kLine:
629 GetLinesPath(path_builder, sides, p, radius);
630 break;
631 case PathVerb::kQuad:
632 GetQuadsPath(path_builder, sides, p, radius);
633 break;
634 case PathVerb::kConic:
635 GetConicsPath(path_builder, sides, p, radius);
636 break;
637 case PathVerb::kCubic:
638 GetCubicsPath(path_builder, sides, p, radius);
639 break;
640 default:
641 break;
642 }
643 }
644}
645
647 switch (type) {
648 case PathVerb::kLine:
649 return "Lines";
650 case PathVerb::kQuad:
651 return "Quads";
652 case PathVerb::kConic:
653 return "Conics";
654 case PathVerb::kCubic:
655 return "Cubics";
656 default:
657 return "Unknown";
658 }
659}
660
661// Draws a series of overlapping 20-sided polygons where the path segment
662// between each point is one of the verb types defined in PathVerb.
663//
664// The number of polygons drawn will be varied to get an overall path
665// with approximately 20*N verbs, so we can get an idea of the fixed
666// cost of using drawPath as well as an idea of how the cost varies according
667// to the verb count.
668void BM_DrawPath(benchmark::State& state,
669 BackendType backend_type,
670 unsigned attributes,
671 PathVerb type) {
672 auto surface_provider = DlSurfaceProvider::Create(backend_type);
673 DisplayListBuilder builder;
674 DlPaint paint = GetPaintForRun(attributes);
675
677
678 size_t length = kFixedCanvasSize;
679 surface_provider->InitializeSurface(length, length);
680 auto surface = surface_provider->GetPrimarySurface();
681 auto canvas = surface->GetCanvas();
682
683 DlPathBuilder path_builder;
684
685 std::string label = VerbToString(type);
686 DlPoint center = DlPoint(length / 2.0f, length / 2.0f);
687 float radius = length * 0.25f;
688 state.SetComplexityN(state.range(0));
689
690 MultiplyPath(path_builder, type, center, 20, state.range(0), radius);
691 DlPath path = path_builder.TakePath();
692
693 DlPathVerbCounter counter;
694 path.Dispatch(counter);
695 state.counters["VerbCount"] = counter.GetVerbCount();
696 state.counters["DrawCallCount"] = 1;
697
698 builder.DrawPath(path, paint);
699 auto display_list = builder.Build();
700
701 // Prime any path conversions
702 canvas->DrawDisplayList(display_list);
703 surface->FlushSubmitCpuSync();
704
705 // We only want to time the actual rasterization.
706 for ([[maybe_unused]] auto _ : state) {
707 canvas->DrawDisplayList(display_list);
708 surface->FlushSubmitCpuSync();
709 }
710
711 auto filename = surface_provider->backend_name() + "-DrawPath-" + label +
712 "-" + std::to_string(state.range(0)) + ".png";
713 surface_provider->Snapshot(filename);
714}
715
716// Returns a set of vertices that describe a circle that has a
717// radius of `radius` and outer vertex count of approximately
718// `vertex_count`. The final number of vertices will differ as we
719// need to ensure the correct usage of vertices to ensure we do not
720// request degenerate triangles be drawn. This final count is output
721// through `final_vertex_count`.
722//
723// The resulting vertices will describe a disc consisting of a series
724// of triangles with two vertices on the circumference of the disc,
725// and the final vertex being the center point of the disc.
726//
727// Each vertex colour will alternate through Red, Green, Blue and Cyan.
728std::shared_ptr<DlVertices> GetTestVertices(DlPoint center,
729 float radius,
730 size_t vertex_count,
731 DlVertexMode mode,
732 size_t& final_vertex_count) {
733 size_t outer_vertex_count = vertex_count / 2;
734 std::vector<DlPoint> outer_points =
735 GetPolygonPoints(outer_vertex_count, center, radius);
736
737 std::vector<DlPoint> vertices;
738 std::vector<DlColor> colors;
739
740 switch (mode) {
742 // Calling the points on the outer circle O_0, O_1, O_2, ..., and
743 // the center point C, this should create a triangle fan with vertices
744 // C, O_0, O_1, O_2, O_3, ...
745 vertices.push_back(center);
746 colors.push_back(DlColor(SK_ColorCYAN));
747 for (size_t i = 0; i <= outer_points.size(); i++) {
748 vertices.push_back(outer_points[i % outer_points.size()]);
749 if (i % 3 == 0) {
750 colors.push_back(DlColor(SK_ColorRED));
751 } else if (i % 3 == 1) {
752 colors.push_back(DlColor(SK_ColorGREEN));
753 } else {
754 colors.push_back(DlColor(SK_ColorBLUE));
755 }
756 }
757 break;
759 // Calling the points on the outer circle O_0, O_1, O_2, ..., and
760 // the center point C, this should create a series of triangles with
761 // vertices O_0, O_1, C, O_1, O_2, C, O_2, O_3, C, ...
762 for (size_t i = 0; i < outer_vertex_count; i++) {
763 vertices.push_back(outer_points[i % outer_points.size()]);
764 colors.push_back(DlColor(SK_ColorRED));
765 vertices.push_back(outer_points[(i + 1) % outer_points.size()]);
766 colors.push_back(DlColor(SK_ColorGREEN));
767 vertices.push_back(center);
768 colors.push_back(DlColor(SK_ColorBLUE));
769 }
770 break;
772 // Calling the points on the outer circle O_0, O_1, O_2, ..., and
773 // the center point C, this should create a strip with vertices
774 // O_0, O_1, C, O_2, O_3, C, O_4, O_5, C, ...
775 for (size_t i = 0; i <= outer_vertex_count; i++) {
776 vertices.push_back(outer_points[i % outer_points.size()]);
777 colors.push_back(i % 2 ? DlColor(SK_ColorRED) : DlColor(SK_ColorGREEN));
778 if (i % 2 == 1) {
779 vertices.push_back(center);
780 colors.push_back(DlColor(SK_ColorBLUE));
781 }
782 }
783 break;
784 default:
785 break;
786 }
787
788 final_vertex_count = vertices.size();
789 return DlVertices::Make(mode, vertices.size(), vertices.data(), nullptr,
790 colors.data());
791}
792
794 switch (mode) {
796 return "TriangleStrip";
798 return "TriangleFan";
800 return "Triangles";
801 }
802 return "Unknown";
803}
804
805// Draws a series of discs generated by `GetTestVertices()` with
806// 50 vertices in each disc. The number of discs drawn will vary according
807// to the benchmark input, and the benchmark will automatically calculate
808// the Big-O complexity of `DrawVertices` with N being the number of vertices
809// being drawn.
810//
811// The discs drawn will be centered on points along a circle with radius of 25%
812// of the canvas width/height, with each point being equally spaced out.
813void BM_DrawVertices(benchmark::State& state,
814 BackendType backend_type,
815 unsigned attributes,
816 DlVertexMode mode) {
817 auto surface_provider = DlSurfaceProvider::Create(backend_type);
818 DisplayListBuilder builder;
819 DlPaint paint = GetPaintForRun(attributes);
820
822
823 size_t length = kFixedCanvasSize;
824 surface_provider->InitializeSurface(length, length);
825 auto surface = surface_provider->GetPrimarySurface();
826 auto canvas = surface->GetCanvas();
827
828 DlPoint center = DlPoint(length / 2.0f, length / 2.0f);
829
830 float radius = length / 4.0f;
831
832 size_t vertex_count, total_vertex_count = 0;
833 size_t disc_count = state.range(0);
834
835 std::vector<DlPoint> center_points =
836 GetPolygonPoints(disc_count, center, radius / 4.0f);
837
838 state.counters["DrawCallCount"] = center_points.size();
839 for (DlPoint p : center_points) {
840 std::shared_ptr<DlVertices> vertices =
841 GetTestVertices(p, radius, 50, mode, vertex_count);
842 total_vertex_count += vertex_count;
843 builder.DrawVertices(vertices, DlBlendMode::kSrc, paint);
844 }
845
846 state.counters["VertexCount"] = total_vertex_count;
847 state.SetComplexityN(total_vertex_count);
848
849 auto display_list = builder.Build();
850
851 // We only want to time the actual rasterization.
852 for ([[maybe_unused]] auto _ : state) {
853 canvas->DrawDisplayList(display_list);
854 surface->FlushSubmitCpuSync();
855 }
856
857 auto filename = surface_provider->backend_name() + "-DrawVertices-" +
858 std::to_string(disc_count) + "-" + VertexModeToString(mode) +
859 ".png";
860 surface_provider->Snapshot(filename);
861}
862
863// Generate `count` test points.
864//
865// The points are distributed using some fixed constant offsets that were
866// chosen to appear somewhat random.
867//
868// The points generated will wrap in x and y for the bounds of `canvas_size`.
869std::vector<DlPoint> GetTestPoints(size_t count, DlISize canvas_size) {
870 std::vector<DlPoint> points;
871
872 // Some arbitrary offsets to use when building the list of points
873 std::vector<DlScalar> delta_x = {10.0f, 6.3f, 15.0f, 3.5f, 22.6f, 4.7f};
874 std::vector<DlScalar> delta_y = {9.3f, -5.4f, 8.5f, -12.0f, 19.2f, -19.6f};
875
876 DlPoint current;
877 for (size_t i = 0; i < count; i++) {
878 points.push_back(current);
879 current +=
880 DlPoint(delta_x[i % delta_x.size()], delta_y[i % delta_y.size()]);
881 if (current.x > canvas_size.width) {
882 current += DlPoint(-canvas_size.width, 25.0f);
883 }
884 if (current.y > canvas_size.height) {
885 current += DlPoint(0.0f, -canvas_size.height);
886 }
887 }
888
889 return points;
890}
891
893 switch (mode) {
895 return "Lines";
897 return "Polygon";
899 default:
900 return "Points";
901 }
902}
903
904// Draws a series of points generated by `GetTestPoints()` above to
905// a fixed-size canvas. The benchmark will vary the number of points drawn,
906// and they can be drawn in one of three modes - Lines, Polygon or Points mode.
907//
908// This benchmark will automatically calculate the Big-O complexity of
909// `DrawPoints` with N being the number of points being drawn.
910void BM_DrawPoints(benchmark::State& state,
911 BackendType backend_type,
912 unsigned attributes,
913 DlPointMode mode) {
914 auto surface_provider = DlSurfaceProvider::Create(backend_type);
915 DisplayListBuilder builder;
916 DlPaint paint = GetPaintForRun(attributes);
917
918 switch (mode) {
920 AnnotateAttributes(attributes, state,
922 break;
924 AnnotateAttributes(attributes, state,
926 break;
928 AnnotateAttributes(attributes, state,
930 break;
931 }
932
933 size_t length = kFixedCanvasSize;
934 surface_provider->InitializeSurface(length, length);
935 auto surface = surface_provider->GetPrimarySurface();
936 auto canvas = surface->GetCanvas();
937
938 size_t point_count = state.range(0);
939 state.SetComplexityN(point_count);
940 state.counters["PointCount"] = point_count;
941 state.counters["DrawCallCount"] = 1;
942
943 std::vector<DlPoint> points =
944 GetTestPoints(point_count, DlISize(length, length));
945 builder.DrawPoints(mode, points.size(), points.data(), paint);
946
947 auto display_list = builder.Build();
948
949 for ([[maybe_unused]] auto _ : state) {
950 canvas->DrawDisplayList(display_list);
951 surface->FlushSubmitCpuSync();
952 }
953
954 auto filename = surface_provider->backend_name() + "-DrawPoints-" +
955 PointModeToString(mode) + "-" + std::to_string(point_count) +
956 ".png";
957 surface_provider->Snapshot(filename);
958}
959
960sk_sp<SkImage> ImageFromBitmapWithNewID(const SkBitmap& bitmap) {
961 // If we create an SkPixmap with a ref to the SkBitmap's pixel data,
962 // then create an SkImage from that, we always get a new generation ID,
963 // so we will avoid hitting the cache.
964 SkPixmap pixmap;
965 bitmap.peekPixels(&pixmap);
966 return SkImages::RasterFromPixmap(pixmap, nullptr, nullptr);
967}
968
969// Draws `kImagesToDraw` bitmaps to a canvas, either with texture-backed
970// bitmaps or bitmaps that need to be uploaded to the GPU first.
971void BM_DrawImage(benchmark::State& state,
972 BackendType backend_type,
973 unsigned attributes,
974 DlImageSampling options,
975 bool upload_bitmap) {
976 auto surface_provider = DlSurfaceProvider::Create(backend_type);
977 DisplayListBuilder builder;
978 DlPaint paint = GetPaintForRun(attributes);
979
980 AnnotateAttributes(attributes, state,
982
983 size_t bitmap_size = state.range(0);
984 size_t canvas_size = 2 * bitmap_size;
985 surface_provider->InitializeSurface(canvas_size, canvas_size);
986 auto surface = surface_provider->GetPrimarySurface();
987 auto canvas = surface->GetCanvas();
988
989 sk_sp<SkImage> image;
990 std::shared_ptr<DlSurfaceInstance> offscreen_instance;
991 sk_sp<SkSurface> offscreen;
992 SkBitmap bitmap;
993
994 if (upload_bitmap) {
995 SkImageInfo info = SkImageInfo::Make(bitmap_size, bitmap_size,
996 SkColorType::kRGBA_8888_SkColorType,
997 SkAlphaType::kPremul_SkAlphaType);
998 bitmap.allocPixels(info, 0);
999 bitmap.eraseColor(SK_ColorBLUE);
1000 } else {
1001 offscreen_instance =
1002 surface_provider->MakeOffscreenSurface(bitmap_size, bitmap_size);
1003 offscreen = offscreen_instance->sk_surface();
1004 offscreen->getCanvas()->clear(SK_ColorRED);
1005 }
1006
1007 const DlPoint offset(0.5f, 0.5f);
1008 DlPoint dst;
1009
1010 state.counters["DrawCallCount"] = kImagesToDraw;
1011 for (size_t i = 0; i < kImagesToDraw; i++) {
1012 image = upload_bitmap ? ImageFromBitmapWithNewID(bitmap)
1013 : offscreen->makeImageSnapshot();
1014 builder.DrawImage(DlImage::Make(image), dst, options, &paint);
1015
1016 dst += offset;
1017 if (dst.x + bitmap_size > canvas_size) {
1018 dst.x = 0;
1019 }
1020 if (dst.y + bitmap_size > canvas_size) {
1021 dst.y = 0;
1022 }
1023 }
1024
1025 auto display_list = builder.Build();
1026
1027 for ([[maybe_unused]] auto _ : state) {
1028 canvas->DrawDisplayList(display_list);
1029 surface->FlushSubmitCpuSync();
1030 }
1031
1032 auto filename = surface_provider->backend_name() + "-DrawImage-" +
1033 (upload_bitmap ? "Upload-" : "Texture-") +
1034 std::to_string(bitmap_size) + ".png";
1035 surface_provider->Snapshot(filename);
1036}
1037
1039 switch (constraint) {
1041 return "Strict";
1043 return "Fast";
1044 default:
1045 return "Unknown";
1046 }
1047}
1048
1049// Draws `kImagesToDraw` bitmaps to a canvas, either with texture-backed
1050// bitmaps or bitmaps that need to be uploaded to the GPU first.
1051//
1052// The bitmaps are shrunk down to 75% of their size when rendered to the canvas.
1053void BM_DrawImageRect(benchmark::State& state,
1054 BackendType backend_type,
1055 unsigned attributes,
1056 DlImageSampling options,
1057 DlSrcRectConstraint constraint,
1058 bool upload_bitmap) {
1059 auto surface_provider = DlSurfaceProvider::Create(backend_type);
1060 DisplayListBuilder builder;
1061 DlPaint paint = GetPaintForRun(attributes);
1062
1063 AnnotateAttributes(attributes, state,
1065
1066 size_t bitmap_size = state.range(0);
1067 size_t canvas_size = 2 * bitmap_size;
1068 surface_provider->InitializeSurface(canvas_size, canvas_size);
1069 auto surface = surface_provider->GetPrimarySurface();
1070 auto canvas = surface->GetCanvas();
1071
1072 sk_sp<SkImage> image;
1073 std::shared_ptr<DlSurfaceInstance> offscreen_instance;
1074 sk_sp<SkSurface> offscreen;
1075 SkBitmap bitmap;
1076
1077 if (upload_bitmap) {
1078 SkImageInfo info = SkImageInfo::Make(bitmap_size, bitmap_size,
1079 SkColorType::kRGBA_8888_SkColorType,
1080 SkAlphaType::kPremul_SkAlphaType);
1081 bitmap.allocPixels(info, 0);
1082 bitmap.eraseColor(SK_ColorBLUE);
1083 } else {
1084 offscreen_instance =
1085 surface_provider->MakeOffscreenSurface(bitmap_size, bitmap_size);
1086 offscreen = offscreen_instance->sk_surface();
1087 offscreen->getCanvas()->clear(SK_ColorRED);
1088 }
1089
1090 const DlPoint offset(0.5f, 0.5f);
1091 DlRect src = DlRect::MakeXYWH(bitmap_size / 4.0f, bitmap_size / 4.0f,
1092 bitmap_size / 2.0f, bitmap_size / 2.0f);
1093 DlPoint origin;
1094 DlSize size(bitmap_size * 0.75f, bitmap_size * 0.75f);
1095
1096 state.counters["DrawCallCount"] = kImagesToDraw;
1097 for (size_t i = 0; i < kImagesToDraw; i++) {
1098 image = upload_bitmap ? ImageFromBitmapWithNewID(bitmap)
1099 : offscreen->makeImageSnapshot();
1100 DlRect dst = DlRect::MakeOriginSize(origin, size);
1101 builder.DrawImageRect(DlImage::Make(image), src, dst, options, &paint,
1102 constraint);
1103 origin += offset;
1104 if (origin.x + size.width > canvas_size) {
1105 origin.x = 0.0f;
1106 }
1107 if (origin.y + size.height > canvas_size) {
1108 origin.y = 0.0f;
1109 }
1110 }
1111
1112 auto display_list = builder.Build();
1113
1114 for ([[maybe_unused]] auto _ : state) {
1115 canvas->DrawDisplayList(display_list);
1116 surface->FlushSubmitCpuSync();
1117 }
1118
1119 auto filename = surface_provider->backend_name() + "-DrawImageRect-" +
1120 (upload_bitmap ? "Upload-" : "Texture-") +
1121 ConstraintToString(constraint) + "-" +
1122 std::to_string(bitmap_size) + ".png";
1123 surface_provider->Snapshot(filename);
1124}
1125
1126std::string FilterModeToString(const DlFilterMode mode) {
1127 switch (mode) {
1129 return "Nearest";
1131 return "Linear";
1132 default:
1133 return "Unknown";
1134 }
1135}
1136
1137// Draws `kImagesToDraw` bitmaps to a canvas, either with texture-backed
1138// bitmaps or bitmaps that need to be uploaded to the GPU first.
1139//
1140// The image is split into 9 sub-rects and stretched proportionally for final
1141// rendering.
1142void BM_DrawImageNine(benchmark::State& state,
1143 BackendType backend_type,
1144 unsigned attributes,
1145 const DlFilterMode filter,
1146 bool upload_bitmap) {
1147 auto surface_provider = DlSurfaceProvider::Create(backend_type);
1148 DisplayListBuilder builder;
1149 DlPaint paint = GetPaintForRun(attributes);
1150
1151 AnnotateAttributes(attributes, state,
1153
1154 size_t bitmap_size = state.range(0);
1155 size_t canvas_size = 2 * bitmap_size;
1156 surface_provider->InitializeSurface(canvas_size, canvas_size);
1157 auto surface = surface_provider->GetPrimarySurface();
1158 auto canvas = surface->GetCanvas();
1159
1160 DlIRect center = DlIRect::MakeXYWH(bitmap_size / 4, bitmap_size / 4,
1161 bitmap_size / 2, bitmap_size / 2);
1162
1163 sk_sp<SkImage> image;
1164 std::shared_ptr<DlSurfaceInstance> offscreen_instance;
1165 sk_sp<SkSurface> offscreen;
1166 SkBitmap bitmap;
1167
1168 if (upload_bitmap) {
1169 SkImageInfo info = SkImageInfo::Make(bitmap_size, bitmap_size,
1170 SkColorType::kRGBA_8888_SkColorType,
1171 SkAlphaType::kPremul_SkAlphaType);
1172 bitmap.allocPixels(info, 0);
1173 bitmap.eraseColor(SK_ColorBLUE);
1174 } else {
1175 offscreen_instance =
1176 surface_provider->MakeOffscreenSurface(bitmap_size, bitmap_size);
1177 offscreen = offscreen_instance->sk_surface();
1178 offscreen->getCanvas()->clear(SK_ColorRED);
1179 }
1180
1181 const DlPoint offset(0.5f, 0.5f);
1182 DlPoint origin;
1183 DlSize size(bitmap_size * 0.75f, bitmap_size * 0.75f);
1184
1185 state.counters["DrawCallCount"] = kImagesToDraw;
1186 for (size_t i = 0; i < kImagesToDraw; i++) {
1187 image = upload_bitmap ? ImageFromBitmapWithNewID(bitmap)
1188 : offscreen->makeImageSnapshot();
1189 DlRect dst = DlRect::MakeOriginSize(origin, size);
1190 builder.DrawImageNine(DlImage::Make(image), center, dst, filter, &paint);
1191 origin += offset;
1192 if (origin.x + size.width > canvas_size) {
1193 origin.x = 0.0f;
1194 }
1195 if (origin.y + size.height > canvas_size) {
1196 origin.y = 0.0f;
1197 }
1198 }
1199
1200 auto display_list = builder.Build();
1201
1202 for ([[maybe_unused]] auto _ : state) {
1203 canvas->DrawDisplayList(display_list);
1204 surface->FlushSubmitCpuSync();
1205 }
1206
1207 auto filename = surface_provider->backend_name() + "-DrawImageNine-" +
1208 (upload_bitmap ? "Upload-" : "Texture-") +
1209 FilterModeToString(filter) + "-" +
1210 std::to_string(bitmap_size) + ".png";
1211 surface_provider->Snapshot(filename);
1212}
1213
1214// Draws a series of glyph runs with 32 glyphs in each run. The number of runs
1215// may vary according to the benchmark parameters. The text will start in the
1216// upper left corner of the canvas and advance from left to right and wrap at
1217// the canvas boundaries in both x and y.
1218//
1219// This benchmark will automatically calculate the Big-O complexity of
1220// `DrawTextBlob` with N being the number of glyphs being drawn.
1221void BM_DrawTextBlob(benchmark::State& state,
1222 BackendType backend_type,
1223 unsigned attributes) {
1224 auto surface_provider = DlSurfaceProvider::Create(backend_type);
1225 DisplayListBuilder builder;
1226 DlPaint paint = GetPaintForRun(attributes);
1227
1229
1230 size_t draw_calls = state.range(0);
1231 size_t canvas_size = kFixedCanvasSize;
1232 surface_provider->InitializeSurface(canvas_size, canvas_size);
1233 auto surface = surface_provider->GetPrimarySurface();
1234 auto canvas = surface->GetCanvas();
1235
1236 state.SetComplexityN(draw_calls);
1237 state.counters["DrawCallCount_Varies"] = draw_calls;
1238 state.counters["GlyphCount"] = draw_calls;
1239 char character[2] = {'A', '\0'};
1240
1241 for (size_t i = 0; i < draw_calls; i++) {
1242 character[0] = 'A' + (i % 26);
1243 auto blob = SkTextBlob::MakeFromString(character, CreateTestFontOfSize(20));
1244 builder.DrawText(DlTextSkia::Make(blob), 50.0f, 50.0f, paint);
1245 }
1246
1247 auto display_list = builder.Build();
1248
1249 for ([[maybe_unused]] auto _ : state) {
1250 canvas->DrawDisplayList(display_list);
1251 surface->FlushSubmitCpuSync();
1252 }
1253
1254 auto filename = surface_provider->backend_name() + "-DrawTextBlob-" +
1255 std::to_string(draw_calls) + ".png";
1256 surface_provider->Snapshot(filename);
1257}
1258
1259// Draw the shadow for a 10-sided regular polygon where the polygon's
1260// sides are denoted by one of a Line, Quad, Conic or Cubic path segment.
1261//
1262// The elevation of the light source will vary according to the benchmark
1263// paremeters.
1264//
1265// The benchmark can be run with either a transparent occluder or an opaque
1266// occluder.
1267void BM_DrawShadow(benchmark::State& state,
1268 BackendType backend_type,
1269 unsigned attributes,
1270 bool transparent_occluder,
1271 PathVerb type) {
1272 auto surface_provider = DlSurfaceProvider::Create(backend_type);
1273 DisplayListBuilder builder;
1274 DlPaint paint = GetPaintForRun(attributes);
1275
1277
1278 size_t length = kFixedCanvasSize;
1279 surface_provider->InitializeSurface(length, length);
1280 auto surface = surface_provider->GetPrimarySurface();
1281 auto canvas = surface->GetCanvas();
1282
1283 DlPathBuilder path_builder;
1284
1285 DlPoint center = DlPoint(length / 2.0f, length / 2.0f);
1286 float radius = length * 0.25f;
1287
1288 switch (type) {
1289 case PathVerb::kLine:
1290 GetLinesPath(path_builder, 10, center, radius);
1291 break;
1292 case PathVerb::kQuad:
1293 GetQuadsPath(path_builder, 10, center, radius);
1294 break;
1295 case PathVerb::kConic:
1296 GetConicsPath(path_builder, 10, center, radius);
1297 break;
1298 case PathVerb::kCubic:
1299 GetCubicsPath(path_builder, 10, center, radius);
1300 break;
1301 default:
1302 break;
1303 }
1304
1305 float elevation = state.range(0);
1306 state.counters["DrawCallCount"] = 1;
1307
1308 DlPath path = path_builder.TakePath();
1309
1310 // We can hardcode dpr to 1.0f as we're varying elevation, and dpr is only
1311 // ever used in conjunction with elevation.
1312 builder.DrawShadow(path, DlColor(SK_ColorBLUE), elevation,
1313 transparent_occluder, 1.0f);
1314 auto display_list = builder.Build();
1315
1316 // Prime the path conversion.
1317 canvas->DrawDisplayList(display_list);
1318 surface->FlushSubmitCpuSync();
1319
1320 // We only want to time the actual rasterization.
1321 for ([[maybe_unused]] auto _ : state) {
1322 canvas->DrawDisplayList(display_list);
1323 surface->FlushSubmitCpuSync();
1324 }
1325
1326 auto filename = surface_provider->backend_name() + "-DrawShadow-" +
1327 VerbToString(type) + "-" +
1328 (transparent_occluder ? "Transparent-" : "Opaque-") +
1329 std::to_string(elevation) + "-" + ".png";
1330 surface_provider->Snapshot(filename);
1331}
1332
1333// Calls saveLayer N times from the root canvas layer, and optionally calls
1334// saveLayer a further M times nested inside that top saveLayer call.
1335//
1336// The total number of saveLayer calls will be N * (M+1).
1337//
1338// In each saveLayer call, simply draw the colour red with no clip rect.
1339void BM_SaveLayer(benchmark::State& state,
1340 BackendType backend_type,
1341 unsigned attributes,
1342 size_t save_depth) {
1343 auto surface_provider = DlSurfaceProvider::Create(backend_type);
1344 DisplayListBuilder builder;
1345 DlPaint paint = GetPaintForRun(attributes);
1346
1348
1349 size_t length = kFixedCanvasSize;
1350 surface_provider->InitializeSurface(length, length);
1351 auto surface = surface_provider->GetPrimarySurface();
1352 auto canvas = surface->GetCanvas();
1353
1354 size_t save_layer_calls = state.range(0);
1355
1356 // Ensure we draw two overlapping rects to avoid any peephole optimisations
1357 DlRect rect1 = DlRect::MakeLTRB(0, 0, 0.75f * length, 0.75f * length);
1358 DlRect rect2 =
1359 DlRect::MakeLTRB(0.25f * length, 0.25f * length, length, length);
1360
1361 state.counters["DrawCallCount_Varies"] = save_layer_calls * save_depth;
1362 for (size_t i = 0; i < save_layer_calls; i++) {
1363 for (size_t j = 0; j < save_depth; j++) {
1364 builder.SaveLayer(std::nullopt, nullptr);
1365 builder.DrawRect(rect1, paint);
1366 builder.DrawRect(rect2, paint);
1367 }
1368 for (size_t j = 0; j < save_depth; j++) {
1369 builder.Restore();
1370 }
1371 }
1372 auto display_list = builder.Build();
1373
1374 // We only want to time the actual rasterization.
1375 for ([[maybe_unused]] auto _ : state) {
1376 canvas->DrawDisplayList(display_list);
1377 surface->FlushSubmitCpuSync();
1378 }
1379
1380 auto filename = surface_provider->backend_name() + "-SaveLayer-" +
1381 std::to_string(save_depth) + "-" +
1382 std::to_string(save_layer_calls) + ".png";
1383 surface_provider->Snapshot(filename);
1384}
1385
1386#ifdef ENABLE_SOFTWARE_BENCHMARKS
1388#endif
1389
1390#ifdef ENABLE_OPENGL_BENCHMARKS
1392#endif
1393
1394#ifdef ENABLE_METAL_BENCHMARKS
1396#endif
1397
1398} // namespace testing
1399} // namespace flutter
GLenum type
constexpr bool applies_anti_alias() const
constexpr bool always_stroked() const
constexpr bool applies_style() const
void DrawOval(const DlRect &bounds, const DlPaint &paint) override
void DrawImageRect(const sk_sp< DlImage > &image, const DlRect &src, const DlRect &dst, DlImageSampling sampling, const DlPaint *paint=nullptr, DlSrcRectConstraint constraint=DlSrcRectConstraint::kFast) override
void DrawVertices(const std::shared_ptr< DlVertices > &vertices, DlBlendMode mode, const DlPaint &paint) override
void DrawImageNine(const sk_sp< DlImage > &image, const DlIRect &center, const DlRect &dst, DlFilterMode filter, const DlPaint *paint=nullptr) override
void DrawRoundRect(const DlRoundRect &rrect, const DlPaint &paint) override
void DrawArc(const DlRect &bounds, DlScalar start, DlScalar sweep, bool useCenter, const DlPaint &paint) override
void DrawShadow(const DlPath &path, const DlColor color, const DlScalar elevation, bool transparent_occluder, DlScalar dpr) override
Draws the shadow of the given |path| rendered in the provided |color| (which is only consulted for it...
void DrawImage(const sk_sp< DlImage > &image, const DlPoint &point, DlImageSampling sampling, const DlPaint *paint=nullptr) override
void DrawCircle(const DlPoint &center, DlScalar radius, const DlPaint &paint) override
void SaveLayer(const std::optional< DlRect > &bounds, const DlPaint *paint=nullptr, const DlImageFilter *backdrop=nullptr, std::optional< int64_t > backdrop_id=std::nullopt) override
void DrawLine(const DlPoint &p0, const DlPoint &p1, const DlPaint &paint) override
void DrawText(const std::shared_ptr< DlText > &text, DlScalar x, DlScalar y, const DlPaint &paint) override
sk_sp< DisplayList > Build()
Definition dl_builder.cc:66
void DrawPath(const DlPath &path, const DlPaint &paint) override
void DrawPoints(DlPointMode mode, uint32_t count, const DlPoint pts[], const DlPaint &paint) override
void DrawDiffRoundRect(const DlRoundRect &outer, const DlRoundRect &inner, const DlPaint &paint) override
void DrawRect(const DlRect &rect, const DlPaint &paint) override
static constexpr DisplayListAttributeFlags kDrawVerticesFlags
static constexpr DisplayListAttributeFlags kDrawArcNoCenterFlags
static constexpr DisplayListAttributeFlags kDrawImageRectWithPaintFlags
static constexpr DisplayListAttributeFlags kSaveLayerFlags
static constexpr DisplayListAttributeFlags kDrawOvalFlags
static constexpr DisplayListAttributeFlags kDrawPointsAsLinesFlags
static constexpr DisplayListAttributeFlags kDrawPointsAsPolygonFlags
static constexpr DisplayListAttributeFlags kDrawCircleFlags
static constexpr DisplayListAttributeFlags kDrawPathFlags
static constexpr DisplayListAttributeFlags kDrawLineFlags
static constexpr DisplayListAttributeFlags kDrawPointsAsPointsFlags
static constexpr DisplayListAttributeFlags kDrawImageWithPaintFlags
static constexpr DisplayListAttributeFlags kDrawImageNineWithPaintFlags
static constexpr DisplayListAttributeFlags kDrawTextFlags
static constexpr DisplayListAttributeFlags kDrawShadowFlags
static constexpr DisplayListAttributeFlags kDrawRRectFlags
static constexpr DisplayListAttributeFlags kDrawDRRectFlags
static constexpr DisplayListAttributeFlags kDrawRectFlags
static sk_sp< DlImage > Make(const SkImage *image)
Definition dl_image.cc:11
DlPaint & setAntiAlias(bool isAntiAlias)
Definition dl_paint.h:58
DlPaint & setStrokeWidth(float width)
Definition dl_paint.h:115
DlPaint & setDrawStyle(DlDrawStyle style)
Definition dl_paint.h:93
DlPathBuilder & LineTo(DlPoint p2)
Draw a line from the current point to the indicated point p2.
DlPathBuilder & MoveTo(DlPoint p2)
Start a new contour that will originate at the indicated point p2.
const DlPath TakePath()
Returns the path constructed by this path builder and resets its internal state to the default state ...
DlPathBuilder & ConicCurveTo(DlPoint cp, DlPoint p2, DlScalar weight)
Draw a conic curve (a rational quadratic bezier curve) from the current point to the indicated point ...
DlPathBuilder & QuadraticCurveTo(DlPoint cp, DlPoint p2)
Draw a quadratic bezier curve from the current point to the indicated point p2, using the indicated p...
DlPathBuilder & Close()
The path is closed back to the location of the most recent MoveTo call. Contours that are filled are ...
DlPathBuilder & CubicCurveTo(DlPoint cp1, DlPoint cp2, DlPoint p2)
Draw a cubic bezier curve from the current point to the indicated point p2, using the indicated point...
static std::shared_ptr< DlTextSkia > Make(const sk_sp< SkTextBlob > &blob)
static std::shared_ptr< DlVertices > Make(DlVertexMode mode, int vertex_count, const DlPoint vertices[], const DlPoint texture_coordinates[], const DlColor colors[], int index_count=0, const uint16_t indices[]=nullptr, const DlRect *bounds=nullptr)
Constructs a DlVector with compact inline storage for all of its required and optional lists of data.
bool ConicTo(const DlPoint &cp, const DlPoint &p2, DlScalar weight) override
void CubicTo(const DlPoint &cp1, const DlPoint &cp2, const DlPoint &p2) override
void MoveTo(const DlPoint &p2, bool will_be_closed) override
void LineTo(const DlPoint &p2) override
void QuadTo(const DlPoint &cp, const DlPoint &p2) override
static std::unique_ptr< DlSurfaceProvider > Create(BackendType backend_type)
Collection of functions to receive path segments from the underlying path representation via the DlPa...
Definition path_source.h:42
#define RUN_DISPLAYLIST_BENCHMARKS(BACKEND)
int32_t x
FlutterVulkanImage * image
VkSurfaceKHR surface
Definition main.cc:65
#define FML_UNREACHABLE()
Definition logging.h:128
size_t length
double y
std::string VertexModeToString(DlVertexMode mode)
void BM_DrawVertices(benchmark::State &state, BackendType backend_type, unsigned attributes, DlVertexMode mode)
std::shared_ptr< DlVertices > GetTestVertices(DlPoint center, float radius, size_t vertex_count, DlVertexMode mode, size_t &final_vertex_count)
void BM_DrawPath(benchmark::State &state, BackendType backend_type, unsigned attributes, PathVerb type)
constexpr size_t kRectsToDraw
SkFont CreateTestFontOfSize(DlScalar scalar)
void BM_DrawPoints(benchmark::State &state, BackendType backend_type, unsigned attributes, DlPointMode mode)
void BM_DrawOval(benchmark::State &state, BackendType backend_type, unsigned attributes)
void GetLinesPath(DlPathBuilder &path_builder, size_t sides, DlPoint center, float radius)
void BM_DrawShadow(benchmark::State &state, BackendType backend_type, unsigned attributes, bool transparent_occluder, PathVerb type)
void AnnotateAttributes(unsigned attributes, benchmark::State &state, const DisplayListAttributeFlags flags)
constexpr size_t kFixedCanvasSize
constexpr size_t kImagesToDraw
std::string ConstraintToString(DlSrcRectConstraint constraint)
std::string PointModeToString(DlPointMode mode)
void BM_DrawImage(benchmark::State &state, BackendType backend_type, unsigned attributes, DlImageSampling options, bool upload_bitmap)
void BM_DrawLine(benchmark::State &state, BackendType backend_type, unsigned attributes)
void BM_DrawCircle(benchmark::State &state, BackendType backend_type, unsigned attributes)
void BM_DrawRRect(benchmark::State &state, BackendType backend_type, unsigned attributes, RRectType type)
void GetConicsPath(DlPathBuilder &path_builder, size_t sides, DlPoint center, float radius)
void GetQuadsPath(DlPathBuilder &path_builder, size_t sides, DlPoint center, float radius)
void BM_DrawRect(benchmark::State &state, BackendType backend_type, unsigned attributes)
constexpr size_t kDRRectsToDraw
std::string FilterModeToString(const DlFilterMode mode)
sk_sp< SkImage > ImageFromBitmapWithNewID(const SkBitmap &bitmap)
std::vector< DlPoint > GetPolygonPoints(size_t n, DlPoint center, DlScalar r)
std::string VerbToString(PathVerb type)
void BM_DrawDRRect(benchmark::State &state, BackendType backend_type, unsigned attributes, RRectType type)
void BM_DrawTextBlob(benchmark::State &state, BackendType backend_type, unsigned attributes)
std::vector< DlPoint > GetTestPoints(size_t count, DlISize canvas_size)
void BM_DrawArc(benchmark::State &state, BackendType backend_type, unsigned attributes)
constexpr size_t kRRectsToDraw
void MultiplyPath(DlPathBuilder &path_builder, PathVerb type, DlPoint center, size_t sides, size_t number, float radius)
constexpr size_t kArcSweepSetsToDraw
void BM_DrawImageNine(benchmark::State &state, BackendType backend_type, unsigned attributes, const DlFilterMode filter, bool upload_bitmap)
void BM_SaveLayer(benchmark::State &state, BackendType backend_type, unsigned attributes, size_t save_depth)
constexpr size_t kCirclesToDraw
void GetCubicsPath(DlPathBuilder &path_builder, size_t sides, DlPoint center, float radius)
constexpr size_t kLinesToDraw
constexpr size_t kOvalsToDraw
void BM_DrawImageRect(benchmark::State &state, BackendType backend_type, unsigned attributes, DlImageSampling options, DlSrcRectConstraint constraint, bool upload_bitmap)
DlPaint GetPaintForRun(unsigned attributes)
impeller::Scalar DlScalar
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
DlPointMode
Definition dl_types.h:15
@ kLines
draw each separate pair of points as a line segment
@ kPolygon
draw each pair of overlapping points as a line segment
@ kPoints
draw each point separately
impeller::Size DlSize
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
DlVertexMode
Defines the way in which the vertices of a DlVertices object are separated into triangles into which ...
Definition dl_vertices.h:18
@ kTriangles
The vertices are taken 3 at a time to form a triangle.
@ kStrokeAndFill
both strokes and fills shapes
@ kStroke
strokes boundary of shapes
@ kFill
fills interior of shapes
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 mode
impeller::Point DlPoint
DlSrcRectConstraint
Definition dl_types.h:21
static RoundRect MakeRectRadii(const Rect &rect, const RoundingRadii &radii)
Definition round_rect.cc:9
constexpr const Rect & GetBounds() const
Definition round_rect.h:53
RoundRect Shift(Scalar dx, Scalar dy) const
Returns a new round rectangle translated by the given offset.
Definition round_rect.h:89
constexpr auto GetBottom() const
Definition rect.h:357
static constexpr TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition rect.h:144
constexpr auto GetRight() const
Definition rect.h:355
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129
Type height
Definition size.h:29
Type width
Definition size.h:28
std::vector< Point > points