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"
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"
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 
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