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