| Flutter Impeller |

aiks_dl_unittests.cc
Go to the documentation of this file.
1
2 // Copyright 2013 The Flutter Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5
6 #include <vector>
7 #include "display_list/dl_sampling_options.h"
8 #include "display_list/dl_tile_mode.h"
9 #include "display_list/effects/dl_color_filter.h"
10 #include "display_list/effects/dl_color_source.h"
11 #include "display_list/effects/dl_image_filter.h"
12 #include "display_list/geometry/dl_geometry_types.h"
13 #include "display_list/geometry/dl_path.h"
14 #include "display_list/image/dl_image.h"
15 #include "flutter/impeller/display_list/aiks_unittests.h"
16
17 #include "flutter/display_list/dl_blend_mode.h"
18 #include "flutter/display_list/dl_builder.h"
19 #include "flutter/display_list/dl_color.h"
20 #include "flutter/display_list/dl_paint.h"
21 #include "flutter/testing/testing.h"
22 #include "imgui.h"
23 #include "impeller/display_list/dl_dispatcher.h"
24 #include "impeller/display_list/dl_image_impeller.h"
25 #include "impeller/geometry/scalar.h"
26 #include "include/core/SkCanvas.h"
27 #include "include/core/SkMatrix.h"
28 #include "include/core/SkPath.h"
29 #include "include/core/SkRSXform.h"
30 #include "include/core/SkRefCnt.h"
31
32 namespace impeller {
33 namespace testing {
34
35 using namespace flutter;
36
37 namespace {
38 SkRect GetCullRect(ISize window_size) {
39 return SkRect::MakeSize(SkSize::Make(window_size.width, window_size.height));
40 }
41 } // namespace
42
43 TEST_P(AiksTest, CollapsedDrawPaintInSubpass) {
44 DisplayListBuilder builder;
45
46 DlPaint paint;
47 paint.setColor(DlColor::kYellow());
48 paint.setBlendMode(DlBlendMode::kSrc);
49 builder.DrawPaint(paint);
50
51 DlPaint save_paint;
52 save_paint.setBlendMode(DlBlendMode::kMultiply);
53 builder.SaveLayer(nullptr, &save_paint);
54
55 DlPaint draw_paint;
56 draw_paint.setColor(DlColor::kCornflowerBlue().modulateOpacity(0.75f));
57 builder.DrawPaint(draw_paint);
58
59 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
60 }
61
62 TEST_P(AiksTest, CollapsedDrawPaintInSubpassBackdropFilter) {
63 // Bug: https://github.com/flutter/flutter/issues/131576
64 DisplayListBuilder builder;
65
66 DlPaint paint;
67 paint.setColor(DlColor::kYellow());
68 paint.setBlendMode(DlBlendMode::kSrc);
69 builder.DrawPaint(paint);
70
71 auto filter = DlBlurImageFilter::Make(20.0, 20.0, DlTileMode::kDecal);
72 builder.SaveLayer(nullptr, nullptr, filter.get());
73
74 DlPaint draw_paint;
75 draw_paint.setColor(DlColor::kCornflowerBlue());
76 builder.DrawPaint(draw_paint);
77
78 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
79 }
80
81 TEST_P(AiksTest, ColorMatrixFilterSubpassCollapseOptimization) {
82 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
83
84 const float matrix[20] = {
85 -1.0, 0, 0, 1.0, 0, //
86 0, -1.0, 0, 1.0, 0, //
87 0, 0, -1.0, 1.0, 0, //
88 1.0, 1.0, 1.0, 1.0, 0 //
89 };
90 auto filter = DlMatrixColorFilter::Make(matrix);
91
92 DlPaint paint;
93 paint.setColorFilter(filter);
94 builder.SaveLayer(nullptr, &paint);
95
96 builder.Translate(500, 300);
97 builder.Rotate(120); // 120 deg
98
99 DlPaint draw_paint;
100 draw_paint.setColor(DlColor::kBlue());
101 builder.DrawRect(SkRect::MakeXYWH(100, 100, 200, 200), draw_paint);
102
103 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
104 }
105
106 TEST_P(AiksTest, LinearToSrgbFilterSubpassCollapseOptimization) {
107 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
108
109 DlPaint paint;
110 paint.setColorFilter(DlLinearToSrgbGammaColorFilter::kInstance);
111 builder.SaveLayer(nullptr, &paint);
112
113 builder.Translate(500, 300);
114 builder.Rotate(120); // 120 deg.
115
116 DlPaint draw_paint;
117 draw_paint.setColor(DlColor::kBlue());
118 builder.DrawRect(SkRect::MakeXYWH(100, 100, 200, 200), draw_paint);
119
120 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
121 }
122
123 TEST_P(AiksTest, SrgbToLinearFilterSubpassCollapseOptimization) {
124 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
125
126 DlPaint paint;
127 paint.setColorFilter(DlLinearToSrgbGammaColorFilter::kInstance);
128 builder.SaveLayer(nullptr, &paint);
129
130 builder.Translate(500, 300);
131 builder.Rotate(120); // 120 deg
132
133 DlPaint draw_paint;
134 draw_paint.setColor(DlColor::kBlue());
135 builder.DrawRect(SkRect::MakeXYWH(100, 100, 200, 200), draw_paint);
136
137 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
138 }
139
140 TEST_P(AiksTest, TranslucentSaveLayerDrawsCorrectly) {
141 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
142
143 DlPaint paint;
144 paint.setColor(DlColor::kBlue());
145 builder.DrawRect(SkRect::MakeXYWH(100, 100, 300, 300), paint);
146
147 DlPaint save_paint;
148 save_paint.setColor(DlColor::kBlack().withAlpha(128));
149 builder.SaveLayer(nullptr, &save_paint);
150 builder.DrawRect(SkRect::MakeXYWH(100, 500, 300, 300), paint);
151 builder.Restore();
152
153 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
154 }
155
156 TEST_P(AiksTest, TranslucentSaveLayerWithBlendColorFilterDrawsCorrectly) {
157 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
158
159 DlPaint paint;
160 paint.setColor(DlColor::kBlue());
161 builder.DrawRect(SkRect::MakeXYWH(100, 100, 300, 300), paint);
162
163 DlPaint save_paint;
164 paint.setColor(DlColor::kBlack().withAlpha(128));
165 paint.setColorFilter(
166 DlBlendColorFilter::Make(DlColor::kRed(), DlBlendMode::kDstOver));
167 builder.SaveLayer(nullptr, &paint);
168
169 DlPaint draw_paint;
170 draw_paint.setColor(DlColor::kBlue());
171 builder.DrawRect(SkRect::MakeXYWH(100, 500, 300, 300), draw_paint);
172 builder.Restore();
173
174 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
175 }
176
177 TEST_P(AiksTest, TranslucentSaveLayerWithBlendImageFilterDrawsCorrectly) {
178 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
179
180 DlPaint paint;
181 paint.setColor(DlColor::kBlue());
182 builder.DrawRect(SkRect::MakeXYWH(100, 100, 300, 300), paint);
183
184 DlPaint save_paint;
185 save_paint.setColor(DlColor::kBlack().withAlpha(128));
186 save_paint.setImageFilter(DlColorFilterImageFilter::Make(
187 DlBlendColorFilter::Make(DlColor::kRed(), DlBlendMode::kDstOver)));
188
189 builder.SaveLayer(nullptr, &save_paint);
190
191 DlPaint draw_paint;
192 draw_paint.setColor(DlColor::kBlue());
193 builder.DrawRect(SkRect::MakeXYWH(100, 500, 300, 300), draw_paint);
194 builder.Restore();
195
196 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
197 }
198
199 TEST_P(AiksTest, TranslucentSaveLayerWithColorAndImageFilterDrawsCorrectly) {
200 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
201
202 DlPaint paint;
203 paint.setColor(DlColor::kBlue());
204 builder.DrawRect(SkRect::MakeXYWH(100, 100, 300, 300), paint);
205
206 DlPaint save_paint;
207 save_paint.setColor(DlColor::kBlack().withAlpha(128));
208 save_paint.setColorFilter(
209 DlBlendColorFilter::Make(DlColor::kRed(), DlBlendMode::kDstOver));
210 builder.SaveLayer(nullptr, &save_paint);
211
212 DlPaint draw_paint;
213 draw_paint.setColor(DlColor::kBlue());
214 builder.DrawRect(SkRect::MakeXYWH(100, 500, 300, 300), draw_paint);
215 builder.Restore();
216
217 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
218 }
219
220 TEST_P(AiksTest, ImageFilteredUnboundedSaveLayerWithUnboundedContents) {
221 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
222 builder.Scale(GetContentScale().x, GetContentScale().y);
223
224 DlPaint save_paint;
225 save_paint.setImageFilter(
226 DlBlurImageFilter::Make(10.0, 10.0, DlTileMode::kDecal));
227 builder.SaveLayer(nullptr, &save_paint);
228
229 {
230 // DrawPaint to verify correct behavior when the contents are unbounded.
231 DlPaint draw_paint;
232 draw_paint.setColor(DlColor::kYellow());
233 builder.DrawPaint(draw_paint);
234
235 // Contrasting rectangle to see interior blurring
236 DlPaint draw_rect;
237 draw_rect.setColor(DlColor::kBlue());
238 builder.DrawRect(SkRect::MakeLTRB(125, 125, 175, 175), draw_rect);
239 }
240 builder.Restore();
241
242 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
243 }
244
245 TEST_P(AiksTest, TranslucentSaveLayerImageDrawsCorrectly) {
246 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
247
248 auto image = DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg"));
249 builder.DrawImage(image, {100, 100}, DlImageSampling::kMipmapLinear);
250
251 DlPaint paint;
252 paint.setColor(DlColor::kBlack().withAlpha(128));
253 builder.SaveLayer(nullptr, &paint);
254 builder.DrawImage(image, {100, 500}, DlImageSampling::kMipmapLinear);
255 builder.Restore();
256
257 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
258 }
259
260 TEST_P(AiksTest, TranslucentSaveLayerWithColorMatrixColorFilterDrawsCorrectly) {
261 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
262
263 auto image = DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg"));
264 builder.DrawImage(image, {100, 100}, {});
265
266 const float matrix[20] = {
267 1, 0, 0, 0, 0, //
268 0, 1, 0, 0, 0, //
269 0, 0, 1, 0, 0, //
270 0, 0, 0, 2, 0 //
271 };
272 DlPaint paint;
273 paint.setColor(DlColor::kBlack().withAlpha(128));
274 paint.setColorFilter(DlMatrixColorFilter::Make(matrix));
275 builder.SaveLayer(nullptr, &paint);
276 builder.DrawImage(image, {100, 500}, {});
277 builder.Restore();
278
279 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
280 }
281
282 TEST_P(AiksTest, TranslucentSaveLayerWithColorMatrixImageFilterDrawsCorrectly) {
283 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
284
285 auto image = DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg"));
286 builder.DrawImage(image, {100, 100}, {});
287
288 const float matrix[20] = {
289 1, 0, 0, 0, 0, //
290 0, 1, 0, 0, 0, //
291 0, 0, 1, 0, 0, //
292 0, 0, 0, 2, 0 //
293 };
294 DlPaint paint;
295 paint.setColor(DlColor::kBlack().withAlpha(128));
296 paint.setColorFilter(DlMatrixColorFilter::Make(matrix));
297 builder.SaveLayer(nullptr, &paint);
298 builder.DrawImage(image, {100, 500}, {});
299 builder.Restore();
300
301 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
302 }
303
304 TEST_P(AiksTest,
305 TranslucentSaveLayerWithColorFilterAndImageFilterDrawsCorrectly) {
306 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
307
308 auto image = DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg"));
309 builder.DrawImage(image, {100, 100}, {});
310
311 const float matrix[20] = {
312 1, 0, 0, 0, 0, //
313 0, 1, 0, 0, 0, //
314 0, 0.2, 1, 0, 0, //
315 0, 0, 0, 0.5, 0 //
316 };
317 DlPaint paint;
318 paint.setColor(DlColor::kBlack().withAlpha(128));
319 paint.setImageFilter(
320 DlColorFilterImageFilter::Make(DlMatrixColorFilter::Make(matrix)));
321 paint.setColorFilter(
322 DlBlendColorFilter::Make(DlColor::kGreen(), DlBlendMode::kModulate));
323 builder.SaveLayer(nullptr, &paint);
324 builder.DrawImage(image, {100, 500}, {});
325 builder.Restore();
326
327 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
328 }
329
330 TEST_P(AiksTest, TranslucentSaveLayerWithAdvancedBlendModeDrawsCorrectly) {
331 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
332
333 DlPaint paint;
334 paint.setColor(DlColor::kRed());
335 builder.DrawRect(SkRect::MakeXYWH(0, 0, 400, 400), paint);
336
337 DlPaint save_paint;
338 save_paint.setAlpha(128);
339 save_paint.setBlendMode(DlBlendMode::kLighten);
340 builder.SaveLayer(nullptr, &save_paint);
341
342 DlPaint draw_paint;
343 draw_paint.setColor(DlColor::kGreen());
344 builder.DrawCircle({200, 200}, 100, draw_paint);
345 builder.Restore();
346
347 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
348 }
349
350 /// This is a regression check for https://github.com/flutter/engine/pull/41129
351 /// The entire screen is green if successful. If failing, no frames will render,
352 /// or the entire screen will be transparent black.
353 TEST_P(AiksTest, CanRenderTinyOverlappingSubpasses) {
354 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
355
356 DlPaint paint;
357 paint.setColor(DlColor::kRed());
358 builder.DrawPaint(paint);
359
360 // Draw two overlapping subpixel circles.
361 builder.SaveLayer({});
362
363 DlPaint yellow_paint;
364 yellow_paint.setColor(DlColor::kYellow());
365 builder.DrawCircle({100, 100}, 0.1, yellow_paint);
366 builder.Restore();
367 builder.SaveLayer({});
368 builder.DrawCircle({100, 100}, 0.1, yellow_paint);
369 builder.Restore();
370
371 DlPaint draw_paint;
372 draw_paint.setColor(DlColor::kGreen());
373 builder.DrawPaint(draw_paint);
374
375 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
376 }
377
378 TEST_P(AiksTest, CanRenderDestructiveSaveLayer) {
379 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
380
381 DlPaint paint;
382 paint.setColor(DlColor::kRed());
383 builder.DrawPaint(paint);
384 // Draw an empty savelayer with a destructive blend mode, which will replace
385 // the entire red screen with fully transparent black, except for the green
386 // circle drawn within the layer.
387
388 DlPaint save_paint;
389 save_paint.setBlendMode(DlBlendMode::kSrc);
390 builder.SaveLayer(nullptr, &save_paint);
391
392 DlPaint draw_paint;
393 draw_paint.setColor(DlColor::kGreen());
394 builder.DrawCircle({300, 300}, 100, draw_paint);
395 builder.Restore();
396
397 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
398 }
399
400 TEST_P(AiksTest, CanDrawPoints) {
401 std::vector<SkPoint> points = {
402 {0, 0}, //
403 {100, 100}, //
404 {100, 0}, //
405 {0, 100}, //
406 {0, 0}, //
407 {48, 48}, //
408 {52, 52}, //
409 };
410 DlPaint paint_round;
411 paint_round.setColor(DlColor::kYellow().withAlpha(128));
412 paint_round.setStrokeCap(DlStrokeCap::kRound);
413 paint_round.setStrokeWidth(20);
414
415 DlPaint paint_square;
416 paint_square.setColor(DlColor::kYellow().withAlpha(128));
417 paint_square.setStrokeCap(DlStrokeCap::kSquare);
418 paint_square.setStrokeWidth(20);
419
420 DlPaint background;
421 background.setColor(DlColor::kBlack());
422
423 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
424 builder.DrawPaint(background);
425 builder.Translate(200, 200);
426
427 builder.DrawPoints(DlCanvas::PointMode::kPoints, points.size(), points.data(),
428 paint_round);
429 builder.Translate(150, 0);
430 builder.DrawPoints(DlCanvas::PointMode::kPoints, points.size(), points.data(),
431 paint_square);
432
433 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
434 }
435
436 TEST_P(AiksTest, CanDrawPointsWithTextureMap) {
437 auto texture = DlImageImpeller::Make(
438 CreateTextureForFixture("table_mountain_nx.png",
439 /*enable_mipmapping=*/true));
440
441 std::vector<SkPoint> points = {
442 {0, 0}, //
443 {100, 100}, //
444 {100, 0}, //
445 {0, 100}, //
446 {0, 0}, //
447 {48, 48}, //
448 {52, 52}, //
449 };
450
451 auto image_src = std::make_shared<DlImageColorSource>(
452 texture, DlTileMode::kClamp, DlTileMode::kClamp);
453
454 DlPaint paint_round;
455 paint_round.setStrokeCap(DlStrokeCap::kRound);
456 paint_round.setColorSource(image_src);
457 paint_round.setStrokeWidth(200);
458
459 DlPaint paint_square;
460 paint_square.setStrokeCap(DlStrokeCap::kSquare);
461 paint_square.setColorSource(image_src);
462 paint_square.setStrokeWidth(200);
463
464 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
465 builder.Translate(200, 200);
466
467 builder.DrawPoints(DlCanvas::PointMode::kPoints, points.size(), points.data(),
468 paint_round);
469 builder.Translate(150, 0);
470 builder.DrawPoints(DlCanvas::PointMode::kPoints, points.size(), points.data(),
471 paint_square);
472
473 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
474 }
475
476 TEST_P(AiksTest, MipmapGenerationWorksCorrectly) {
477 TextureDescriptor texture_descriptor;
478 texture_descriptor.size = ISize{1024, 1024};
479 texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt;
480 texture_descriptor.storage_mode = StorageMode::kHostVisible;
481 texture_descriptor.mip_count = texture_descriptor.size.MipCount();
482
483 std::vector<uint8_t> bytes(4194304);
484 bool alternate = false;
485 for (auto i = 0u; i < 4194304; i += 4) {
486 if (alternate) {
487 bytes[i] = 255;
488 bytes[i + 1] = 0;
489 bytes[i + 2] = 0;
490 bytes[i + 3] = 255;
491 } else {
492 bytes[i] = 0;
493 bytes[i + 1] = 255;
494 bytes[i + 2] = 0;
495 bytes[i + 3] = 255;
496 }
497 alternate = !alternate;
498 }
499
500 ASSERT_EQ(texture_descriptor.GetByteSizeOfBaseMipLevel(), bytes.size());
501 auto mapping = std::make_shared<fml::NonOwnedMapping>(
502 bytes.data(), // data
503 texture_descriptor.GetByteSizeOfBaseMipLevel() // size
504 );
505 auto texture =
506 GetContext()->GetResourceAllocator()->CreateTexture(texture_descriptor);
507
508 auto device_buffer =
509 GetContext()->GetResourceAllocator()->CreateBufferWithCopy(*mapping);
510 auto command_buffer = GetContext()->CreateCommandBuffer();
511 auto blit_pass = command_buffer->CreateBlitPass();
512
513 blit_pass->AddCopy(DeviceBuffer::AsBufferView(std::move(device_buffer)),
514 texture);
515 blit_pass->GenerateMipmap(texture);
516 EXPECT_TRUE(blit_pass->EncodeCommands(GetContext()->GetResourceAllocator()));
517 EXPECT_TRUE(GetContext()->GetCommandQueue()->Submit({command_buffer}).ok());
518
519 auto image = DlImageImpeller::Make(texture);
520
521 DisplayListBuilder builder;
522 builder.DrawImageRect(
523 image,
524 SkRect::MakeSize(
525 SkSize::Make(texture->GetSize().width, texture->GetSize().height)),
526 SkRect::MakeLTRB(0, 0, 100, 100), DlImageSampling::kMipmapLinear);
527
528 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
529 }
530
531 // https://github.com/flutter/flutter/issues/146648
532 TEST_P(AiksTest, StrokedPathWithMoveToThenCloseDrawnCorrectly) {
533 SkPath path;
534 path.moveTo(0, 400)
535 .lineTo(0, 0)
536 .lineTo(400, 0)
537 // MoveTo implicitly adds a contour, ensure that close doesn't
538 // add another nearly-empty contour.
539 .moveTo(0, 400)
540 .close();
541
542 DisplayListBuilder builder;
543 builder.Translate(50, 50);
544
545 DlPaint paint;
546 paint.setColor(DlColor::kBlue());
547 paint.setStrokeCap(DlStrokeCap::kRound);
548 paint.setStrokeWidth(10);
549 paint.setDrawStyle(DlDrawStyle::kStroke);
550 builder.DrawPath(path, paint);
551
552 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
553 }
554
555 TEST_P(AiksTest, SetContentsWithRegion) {
556 auto bridge = CreateTextureForFixture("bay_bridge.jpg");
557
558 // Replace part of the texture with a red rectangle.
559 std::vector<uint8_t> bytes(100 * 100 * 4);
560 for (auto i = 0u; i < bytes.size(); i += 4) {
561 bytes[i] = 255;
562 bytes[i + 1] = 0;
563 bytes[i + 2] = 0;
564 bytes[i + 3] = 255;
565 }
566 auto mapping =
567 std::make_shared<fml::NonOwnedMapping>(bytes.data(), bytes.size());
568 auto device_buffer =
569 GetContext()->GetResourceAllocator()->CreateBufferWithCopy(*mapping);
570 auto cmd_buffer = GetContext()->CreateCommandBuffer();
571 auto blit_pass = cmd_buffer->CreateBlitPass();
572 blit_pass->AddCopy(DeviceBuffer::AsBufferView(device_buffer), bridge,
573 IRect::MakeLTRB(50, 50, 150, 150));
574
575 auto did_submit =
576 blit_pass->EncodeCommands(GetContext()->GetResourceAllocator()) &&
577 GetContext()->GetCommandQueue()->Submit({std::move(cmd_buffer)}).ok();
578 ASSERT_TRUE(did_submit);
579
580 auto image = DlImageImpeller::Make(bridge);
581
582 DisplayListBuilder builder;
583 builder.DrawImage(image, {0, 0}, {});
584
585 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
586 }
587
588 // Regression test for https://github.com/flutter/flutter/issues/134678.
589 TEST_P(AiksTest, ReleasesTextureOnTeardown) {
590 auto context = MakeContext();
591 std::weak_ptr<Texture> weak_texture;
592
593 {
594 auto texture = CreateTextureForFixture("table_mountain_nx.png");
595 weak_texture = texture;
596
597 DisplayListBuilder builder;
598 builder.Scale(GetContentScale().x, GetContentScale().y);
599 builder.Translate(100.0f, 100.0f);
600
601 DlPaint paint;
602 paint.setColorSource(std::make_shared<DlImageColorSource>(
603 DlImageImpeller::Make(texture), DlTileMode::kClamp, DlTileMode::kClamp,
604 DlImageSampling::kLinear, nullptr));
605
606 builder.DrawRect(SkRect::MakeXYWH(0, 0, 600, 600), paint);
607
608 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
609 }
610
611 // See https://github.com/flutter/flutter/issues/134751.
612 //
613 // If the fence waiter was working this may not be released by the end of the
614 // scope above. Adding a manual shutdown so that future changes to the fence
615 // waiter will not flake this test.
616 context->Shutdown();
617
618 // The texture should be released by now.
619 ASSERT_TRUE(weak_texture.expired()) << "When the texture is no longer in use "
620 "by the backend, it should be "
621 "released.";
622 }
623
624 TEST_P(AiksTest, MatrixImageFilterMagnify) {
625 Scalar scale = 2.0;
626 auto callback = [&]() -> sk_sp<DisplayList> {
627 if (AiksTest::ImGuiBegin("Controls", nullptr,
628 ImGuiWindowFlags_AlwaysAutoResize)) {
629 ImGui::SliderFloat("Scale", &scale, 1, 2);
630 ImGui::End();
631 }
632 DisplayListBuilder builder;
633 builder.Scale(GetContentScale().x, GetContentScale().y);
634 auto image = DlImageImpeller::Make(CreateTextureForFixture("airplane.jpg"));
635
636 builder.Translate(600, -200);
637
638 SkMatrix matrix = SkMatrix::Scale(scale, scale);
639 DlPaint paint;
640 paint.setImageFilter(
641 DlMatrixImageFilter::Make(matrix, DlImageSampling::kLinear));
642 builder.SaveLayer(nullptr, &paint);
643
644 DlPaint rect_paint;
645 rect_paint.setAlpha(0.5 * 255);
646 builder.DrawImage(image, {0, 0}, DlImageSampling::kLinear, &rect_paint);
647 builder.Restore();
648
649 return builder.Build();
650 };
651
652 ASSERT_TRUE(OpenPlaygroundHere(callback));
653 }
654
655 TEST_P(AiksTest, ImageFilteredSaveLayerWithUnboundedContents) {
656 DisplayListBuilder builder;
657 builder.Scale(GetContentScale().x, GetContentScale().y);
658
659 auto test = [&builder](const std::shared_ptr<const DlImageFilter>& filter) {
660 auto DrawLine = [&builder](const SkPoint& p0, const SkPoint& p1,
661 const DlPaint& p) {
662 DlPaint paint = p;
663 paint.setDrawStyle(DlDrawStyle::kStroke);
664 builder.DrawPath(SkPath::Line(p0, p1), paint);
665 };
666 // Registration marks for the edge of the SaveLayer
667 DlPaint paint;
668 paint.setColor(DlColor::kWhite());
669 DrawLine(SkPoint::Make(75, 100), SkPoint::Make(225, 100), paint);
670 DrawLine(SkPoint::Make(75, 200), SkPoint::Make(225, 200), paint);
671 DrawLine(SkPoint::Make(100, 75), SkPoint::Make(100, 225), paint);
672 DrawLine(SkPoint::Make(200, 75), SkPoint::Make(200, 225), paint);
673
674 DlPaint save_paint;
675 save_paint.setImageFilter(filter);
676 SkRect bounds = SkRect::MakeLTRB(100, 100, 200, 200);
677 builder.SaveLayer(&bounds, &save_paint);
678
679 {
680 // DrawPaint to verify correct behavior when the contents are unbounded.
681 DlPaint paint;
682 paint.setColor(DlColor::kYellow());
683 builder.DrawPaint(paint);
684
685 // Contrasting rectangle to see interior blurring
686 paint.setColor(DlColor::kBlue());
687 builder.DrawRect(SkRect::MakeLTRB(125, 125, 175, 175), paint);
688 }
689 builder.Restore();
690 };
691
692 test(std::make_shared<DlBlurImageFilter>(10.0, 10.0, DlTileMode::kDecal));
693
694 builder.Translate(200.0, 0.0);
695
696 test(std::make_shared<DlDilateImageFilter>(10.0, 10.0));
697
698 builder.Translate(200.0, 0.0);
699
700 test(std::make_shared<DlErodeImageFilter>(10.0, 10.0));
701
702 builder.Translate(-400.0, 200.0);
703
704 SkMatrix sk_matrix = SkMatrix::RotateDeg(10);
705
706 auto rotate_filter = std::make_shared<DlMatrixImageFilter>(
707 sk_matrix, DlImageSampling::kLinear);
708 test(rotate_filter);
709
710 builder.Translate(200.0, 0.0);
711
712 const float m[20] = {
713 0, 1, 0, 0, 0, //
714 0, 0, 1, 0, 0, //
715 1, 0, 0, 0, 0, //
716 0, 0, 0, 1, 0 //
717 };
718 auto rgb_swap_filter = std::make_shared<DlColorFilterImageFilter>(
719 std::make_shared<DlMatrixColorFilter>(m));
720 test(rgb_swap_filter);
721
722 builder.Translate(200.0, 0.0);
723
724 test(DlComposeImageFilter::Make(rotate_filter, rgb_swap_filter));
725
726 builder.Translate(-400.0, 200.0);
727
728 test(std::make_shared<DlLocalMatrixImageFilter>(
729 SkMatrix::Translate(25.0, 25.0), rotate_filter));
730
731 builder.Translate(200.0, 0.0);
732
733 test(std::make_shared<DlLocalMatrixImageFilter>(
734 SkMatrix::Translate(25.0, 25.0), rgb_swap_filter));
735
736 builder.Translate(200.0, 0.0);
737
738 test(std::make_shared<DlLocalMatrixImageFilter>(
739 SkMatrix::Translate(25.0, 25.0),
740 DlComposeImageFilter::Make(rotate_filter, rgb_swap_filter)));
741
742 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
743 }
744
745 TEST_P(AiksTest, MatrixBackdropFilter) {
746 DisplayListBuilder builder;
747
748 DlPaint paint;
749 paint.setColor(DlColor::kBlack());
750 builder.DrawPaint(paint);
751 builder.SaveLayer(nullptr, nullptr);
752 {
753 DlPaint paint;
754 paint.setColor(DlColor::kGreen().withAlpha(0.5 * 255));
755 paint.setBlendMode(DlBlendMode::kPlus);
756
757 DlPaint rect_paint;
758 rect_paint.setColor(DlColor::kRed());
759 rect_paint.setStrokeWidth(4);
760 rect_paint.setDrawStyle(DlDrawStyle::kStroke);
761 builder.DrawRect(SkRect::MakeLTRB(0, 0, 300, 300), rect_paint);
762 builder.DrawCircle(SkPoint::Make(200, 200), 100, paint);
763 // Should render a second circle, centered on the bottom-right-most edge of
764 // the circle.
765 SkMatrix matrix = SkMatrix::Translate((100 + 100 * k1OverSqrt2),
766 (100 + 100 * k1OverSqrt2)) *
767 SkMatrix::Scale(0.5, 0.5) *
768 SkMatrix::Translate(-100, -100);
769 auto backdrop_filter =
770 DlMatrixImageFilter::Make(matrix, DlImageSampling::kLinear);
771 builder.SaveLayer(nullptr, nullptr, backdrop_filter.get());
772 builder.Restore();
773 }
774 builder.Restore();
775
776 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
777 }
778
779 TEST_P(AiksTest, MatrixSaveLayerFilter) {
780 DisplayListBuilder builder;
781
782 DlPaint paint;
783 paint.setColor(DlColor::kBlack());
784 builder.DrawPaint(paint);
785 builder.SaveLayer(nullptr, nullptr);
786 {
787 paint.setColor(DlColor::kGreen().withAlpha(255 * 0.5));
788 paint.setBlendMode(DlBlendMode::kPlus);
789 builder.DrawCircle({200, 200}, 100, paint);
790 // Should render a second circle, centered on the bottom-right-most edge of
791 // the circle.
792
793 SkMatrix matrix = SkMatrix::Translate((200 + 100 * k1OverSqrt2),
794 (200 + 100 * k1OverSqrt2)) *
795 SkMatrix::Scale(0.5, 0.5) *
796 SkMatrix::Translate(-200, -200);
797 DlPaint save_paint;
798 save_paint.setImageFilter(
799 DlMatrixImageFilter::Make(matrix, DlImageSampling::kLinear));
800
801 builder.SaveLayer(nullptr, &save_paint);
802
803 DlPaint circle_paint;
804 circle_paint.setColor(DlColor::kGreen().withAlpha(255 * 0.5));
805 circle_paint.setBlendMode(DlBlendMode::kPlus);
806 builder.DrawCircle({200, 200}, 100, circle_paint);
807 builder.Restore();
808 }
809 builder.Restore();
810
811 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
812 }
813
814 // Regression test for flutter/flutter#152780
815 TEST_P(AiksTest, CanDrawScaledPointsSmallScaleLargeRadius) {
816 std::vector<SkPoint> point = {
817 {0, 0}, //
818 };
819
820 DlPaint paint;
821 paint.setStrokeCap(DlStrokeCap::kRound);
822 paint.setColor(DlColor::kRed());
823 paint.setStrokeWidth(100 * 1000000);
824
825 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
826 builder.Translate(200, 200);
827 builder.Scale(0.000001, 0.000001);
828
829 builder.DrawPoints(DlCanvas::PointMode::kPoints, point.size(), point.data(),
830 paint);
831
832 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
833 }
834
835 // Regression test for flutter/flutter#152780
836 TEST_P(AiksTest, CanDrawScaledPointsLargeScaleSmallRadius) {
837 std::vector<SkPoint> point = {
838 {0, 0}, //
839 };
840
841 DlPaint paint;
842 paint.setStrokeCap(DlStrokeCap::kRound);
843 paint.setColor(DlColor::kRed());
844 paint.setStrokeWidth(100 * 0.000001);
845
846 DisplayListBuilder builder(GetCullRect(GetWindowSize()));
847 builder.Translate(200, 200);
848 builder.Scale(1000000, 1000000);
849
850 builder.DrawPoints(DlCanvas::PointMode::kPoints, point.size(), point.data(),
851 paint);
852 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
853 }
854
855 TEST_P(AiksTest, TransparentShadowProducesCorrectColor) {
856 DisplayListBuilder builder;
857 builder.Save();
858 builder.Scale(1.618, 1.618);
859 SkPath path = SkPath{}.addRect(SkRect::MakeXYWH(0, 0, 200, 100));
860
861 builder.DrawShadow(path, flutter::DlColor::kTransparent(), 15, false, 1);
862 builder.Restore();
863
864 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
865 }
866
867 // Regression test for https://github.com/flutter/flutter/issues/130613
868 TEST_P(AiksTest, DispatcherDoesNotCullPerspectiveTransformedChildDisplayLists) {
869 flutter::DisplayListBuilder sub_builder(true);
870 sub_builder.DrawRect(SkRect::MakeXYWH(0, 0, 50, 50),
871 flutter::DlPaint(flutter::DlColor::kRed()));
872 auto display_list = sub_builder.Build();
873
874 AiksContext context(GetContext(), nullptr);
875 RenderTarget render_target =
876 context.GetContentContext().GetRenderTargetCache()->CreateOffscreen(
877 *context.GetContext(), {2400, 1800}, 1);
878
879 DisplayListBuilder builder;
880
881 builder.Scale(2.0, 2.0);
882 builder.Translate(-93.0, 0.0);
883
884 // clang-format off
885 builder.TransformFullPerspective(
886 0.8, -0.2, -0.1, -0.0,
887 0.0, 1.0, 0.0, 0.0,
888 1.4, 1.3, 1.0, 0.0,
889 63.2, 65.3, 48.6, 1.1
890 );
891 // clang-format on
892 builder.Translate(35.0, 75.0);
893 builder.DrawDisplayList(display_list, 1.0f);
894
895 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
896 }
897
898 // Results in a 100x100 green square. If any red is drawn, there is a bug.
899 TEST_P(AiksTest, BackdropRestoreUsesCorrectCoverageForFirstRestoredClip) {
900 DisplayListBuilder builder;
901
902 DlPaint paint;
903 // Add a difference clip that cuts out the bottom right corner
904 builder.ClipRect(SkRect::MakeLTRB(50, 50, 100, 100),
905 DlCanvas::ClipOp::kDifference);
906
907 // Draw a red rectangle that's going to be completely covered by green later.
908 paint.setColor(DlColor::kRed());
909 builder.DrawRect(SkRect::MakeLTRB(0, 0, 100, 100), paint);
910
911 // Add a clip restricting the backdrop filter to the top right corner.
912 auto count = builder.GetSaveCount();
913 builder.Save();
914 {
915 builder.ClipRect(SkRect::MakeLTRB(0, 0, 100, 100));
916 {
917 // Create a save layer with a backdrop blur filter.
918 auto backdrop_filter =
919 DlBlurImageFilter::Make(10.0, 10.0, DlTileMode::kDecal);
920 builder.SaveLayer(nullptr, nullptr, backdrop_filter.get());
921 }
922 }
923 builder.RestoreToCount(count);
924
925 // Finally, overwrite all the previous stuff with green.
926 paint.setColor(DlColor::kGreen());
927 builder.DrawRect(SkRect::MakeLTRB(0, 0, 100, 100), paint);
928
929 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
930 }
931
932 TEST_P(AiksTest, CanPictureConvertToImage) {
933 DisplayListBuilder recorder_canvas;
934 DlPaint paint;
935 paint.setColor(DlColor::RGBA(0.9568, 0.2627, 0.2118, 1.0));
936 recorder_canvas.DrawRect(SkRect::MakeXYWH(100.0, 100.0, 600, 600), paint);
937 paint.setColor(DlColor::RGBA(0.1294, 0.5882, 0.9529, 1.0));
938 recorder_canvas.DrawRect(SkRect::MakeXYWH(200.0, 200.0, 600, 600), paint);
939
940 DisplayListBuilder canvas;
941 AiksContext renderer(GetContext(), nullptr);
942 paint.setColor(DlColor::kTransparent());
943 canvas.DrawPaint(paint);
944
945 auto image =
946 DisplayListToTexture(recorder_canvas.Build(), {1000, 1000}, renderer);
947 if (image) {
948 canvas.DrawImage(DlImageImpeller::Make(image), {}, {});
949 paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 0.2));
950 canvas.DrawRect(SkRect::MakeSize({1000, 1000}), paint);
951 }
952
953 ASSERT_TRUE(OpenPlaygroundHere(canvas.Build()));
954 }
955
956 // Regression test for https://github.com/flutter/flutter/issues/142358 .
957 // Without a change to force render pass construction the image is left in an
958 // undefined layout and triggers a validation error.
959 TEST_P(AiksTest, CanEmptyPictureConvertToImage) {
960 DisplayListBuilder recorder_builder;
961
962 DisplayListBuilder builder;
963 AiksContext renderer(GetContext(), nullptr);
964
965 DlPaint paint;
966 paint.setColor(DlColor::kTransparent());
967 builder.DrawPaint(paint);
968
969 auto result_image =
970 DisplayListToTexture(builder.Build(), ISize{1000, 1000}, renderer);
971 if (result_image) {
972 recorder_builder.DrawImage(DlImageImpeller::Make(result_image), {}, {});
973
974 paint.setColor(DlColor::RGBA(0.1, 0.1, 0.1, 0.2));
975 recorder_builder.DrawRect(SkRect::MakeSize({1000, 1000}), paint);
976 }
977
978 ASSERT_TRUE(OpenPlaygroundHere(recorder_builder.Build()));
979 }
980
981 TEST_P(AiksTest, DepthValuesForLineMode) {
982 // Ensures that the additional draws created by line/polygon mode all
983 // have the same depth values.
984 DisplayListBuilder builder;
985
986 SkPath path = SkPath::Circle(100, 100, 100);
987
988 builder.DrawPath(path, DlPaint()
989 .setColor(DlColor::kRed())
990 .setDrawStyle(DlDrawStyle::kStroke)
991 .setStrokeWidth(5));
992 builder.Save();
993 builder.ClipPath(path);
994
995 std::vector<SkPoint> points = {
996 SkPoint::Make(0, -200), SkPoint::Make(400, 200), SkPoint::Make(0, -100),
997 SkPoint::Make(400, 300), SkPoint::Make(0, 0), SkPoint::Make(400, 400),
998 SkPoint::Make(0, 100), SkPoint::Make(400, 500), SkPoint::Make(0, 150),
999 SkPoint::Make(400, 600)};
1000
1001 builder.DrawPoints(DisplayListBuilder::PointMode::kLines, points.size(),
1002 points.data(),
1003 DlPaint().setColor(DlColor::kBlue()).setStrokeWidth(10));
1004 builder.Restore();
1005
1006 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1007 }
1008
1009 TEST_P(AiksTest, DepthValuesForPolygonMode) {
1010 // Ensures that the additional draws created by line/polygon mode all
1011 // have the same depth values.
1012 DisplayListBuilder builder;
1013
1014 SkPath path = SkPath::Circle(100, 100, 100);
1015
1016 builder.DrawPath(path, DlPaint()
1017 .setColor(DlColor::kRed())
1018 .setDrawStyle(DlDrawStyle::kStroke)
1019 .setStrokeWidth(5));
1020 builder.Save();
1021 builder.ClipPath(path);
1022
1023 std::vector<SkPoint> points = {
1024 SkPoint::Make(0, -200), SkPoint::Make(400, 200), SkPoint::Make(0, -100),
1025 SkPoint::Make(400, 300), SkPoint::Make(0, 0), SkPoint::Make(400, 400),
1026 SkPoint::Make(0, 100), SkPoint::Make(400, 500), SkPoint::Make(0, 150),
1027 SkPoint::Make(400, 600)};
1028
1029 builder.DrawPoints(DisplayListBuilder::PointMode::kPolygon, points.size(),
1030 points.data(),
1031 DlPaint().setColor(DlColor::kBlue()).setStrokeWidth(10));
1032 builder.Restore();
1033
1034 ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
1035 }
1036
1037 } // namespace testing
1038 } // namespace impeller
impeller::DeviceBuffer::AsBufferView
static BufferView AsBufferView(std::shared_ptr< DeviceBuffer > buffer)
Create a buffer view of this entire buffer.
Definition: device_buffer.cc:18
impeller::ISize
ISize64 ISize
Definition: size.h:140
impeller::AiksPlayground
Definition: aiks_playground.h:16
impeller::k1OverSqrt2
constexpr float k1OverSqrt2
Definition: constants.h:50
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::AiksContext
Definition: aiks_context.h:19
impeller::AiksContext::GetContentContext
ContentContext & GetContentContext() const
Definition: aiks_context.cc:42
aiks_unittests.h
impeller::TextureDescriptor::format
PixelFormat format
Definition: texture_descriptor.h:41
impeller::PixelFormat::kR8G8B8A8UNormInt
@ kR8G8B8A8UNormInt
dl_dispatcher.h
impeller::TextureDescriptor::mip_count
size_t mip_count
Definition: texture_descriptor.h:43
impeller::StorageMode::kHostVisible
@ kHostVisible
impeller::DlImageImpeller::Make
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
Definition: dl_image_impeller.cc:23
impeller::TSize
Definition: size.h:19
flutter
Definition: dl_golden_blur_unittests.cc:15
impeller::RenderTarget
Definition: render_target.h:38
impeller::testing::TEST_P
TEST_P(AiksTest, DrawAtlasNoColor)
Definition: aiks_dl_atlas_unittests.cc:78
scalar.h
impeller::AiksContext::GetContext
std::shared_ptr< Context > GetContext() const
Definition: aiks_context.cc:38
impeller::TextureDescriptor::size
ISize size
Definition: texture_descriptor.h:42
impeller::ContentContext::GetRenderTargetCache
const std::shared_ptr< RenderTargetAllocator > & GetRenderTargetCache() const
Definition: content_context.h:725
impeller::TextureDescriptor::GetByteSizeOfBaseMipLevel
constexpr size_t GetByteSizeOfBaseMipLevel() const
Definition: texture_descriptor.h:48
scale
const Scalar scale
Definition: stroke_path_geometry.cc:301
impeller::AiksPlayground::ImGuiBegin
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
Definition: aiks_playground.cc:30
impeller::DisplayListToTexture
std::shared_ptr< Texture > DisplayListToTexture(const sk_sp< flutter::DisplayList > &display_list, ISize size, AiksContext &context, bool reset_host_buffer, bool generate_mips)
Render the provided display list to a texture with the given size.
Definition: dl_dispatcher.cc:1195
impeller::TextureDescriptor::storage_mode
StorageMode storage_mode
Definition: texture_descriptor.h:39
impeller::TRect::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
impeller::TextureDescriptor
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
Definition: texture_descriptor.h:38
impeller::TSize::MipCount
constexpr size_t MipCount() const
Definition: size.h:115
impeller
Definition: allocation.cc:12
dl_image_impeller.h