Flutter Impeller
aiks_dl_runtime_effect_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <memory>
6 
7 #include "flutter/display_list/dl_builder.h"
8 #include "flutter/display_list/dl_paint.h"
9 #include "flutter/display_list/effects/dl_color_source.h"
10 #include "flutter/display_list/effects/dl_image_filter.h"
11 #include "flutter/display_list/effects/dl_runtime_effect.h"
15 
16 namespace impeller {
17 namespace testing {
18 
19 using namespace flutter;
20 
21 namespace {
22 std::shared_ptr<DlColorSource> MakeRuntimeEffect(
23  AiksTest* test,
24  std::string_view name,
25  const std::shared_ptr<std::vector<uint8_t>>& uniform_data = {},
26  const std::vector<std::shared_ptr<DlColorSource>>& samplers = {}) {
27  auto runtime_stages = test->OpenAssetAsRuntimeStage(name.data());
28  auto runtime_stage = runtime_stages[PlaygroundBackendToRuntimeStageBackend(
29  test->GetBackend())];
30  FML_CHECK(runtime_stage);
31  FML_CHECK(runtime_stage->IsDirty());
32 
33  auto dl_runtime_effect = DlRuntimeEffectImpeller::Make(runtime_stage);
34 
35  return DlColorSource::MakeRuntimeEffect(dl_runtime_effect, samplers,
36  uniform_data);
37 }
38 } // namespace
39 
40 // Regression test for https://github.com/flutter/flutter/issues/126701 .
41 TEST_P(AiksTest, CanRenderClippedRuntimeEffects) {
42  struct FragUniforms {
43  Vector2 iResolution;
44  Scalar iTime;
45  } frag_uniforms = {.iResolution = Vector2(400, 400), .iTime = 100.0};
46  auto uniform_data = std::make_shared<std::vector<uint8_t>>();
47  uniform_data->resize(sizeof(FragUniforms));
48  memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms));
49 
50  DlPaint paint;
51  paint.setColorSource(
52  MakeRuntimeEffect(this, "runtime_stage_example.frag.iplr", uniform_data));
53 
54  DisplayListBuilder builder;
55  builder.Save();
56  builder.ClipRoundRect(
57  DlRoundRect::MakeRectXY(DlRect::MakeXYWH(0, 0, 400, 400), 10.0, 10.0),
58  DlClipOp::kIntersect);
59  builder.DrawRect(DlRect::MakeXYWH(0, 0, 400, 400), paint);
60  builder.Restore();
61 
62  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
63 }
64 
65 TEST_P(AiksTest, DrawPaintTransformsBounds) {
66  struct FragUniforms {
67  Size size;
68  } frag_uniforms = {.size = Size::MakeWH(400, 400)};
69  auto uniform_data = std::make_shared<std::vector<uint8_t>>();
70  uniform_data->resize(sizeof(FragUniforms));
71  memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms));
72 
73  DlPaint paint;
74  paint.setColorSource(
75  MakeRuntimeEffect(this, "gradient.frag.iplr", uniform_data));
76 
77  DisplayListBuilder builder;
78  builder.Save();
79  builder.Scale(GetContentScale().x, GetContentScale().y);
80  builder.DrawPaint(paint);
81  builder.Restore();
82 
83  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
84 }
85 
86 TEST_P(AiksTest, CanRenderRuntimeEffectFilter) {
87  auto runtime_stages =
88  OpenAssetAsRuntimeStage("runtime_stage_filter_example.frag.iplr");
89 
90  std::shared_ptr<RuntimeStage> runtime_stage =
91  runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
92  ASSERT_TRUE(runtime_stage);
93  ASSERT_TRUE(runtime_stage->IsDirty());
94 
95  std::vector<std::shared_ptr<DlColorSource>> sampler_inputs = {
96  nullptr,
97  };
98  auto uniform_data = std::make_shared<std::vector<uint8_t>>();
99  uniform_data->resize(sizeof(Vector2));
100 
101  DlPaint paint;
102  paint.setColor(DlColor::kAqua());
103  paint.setImageFilter(DlImageFilter::MakeRuntimeEffect(
104  DlRuntimeEffectImpeller::Make(runtime_stage), sampler_inputs,
105  uniform_data));
106 
107  DisplayListBuilder builder;
108  builder.DrawRect(DlRect::MakeXYWH(0, 0, 400, 400), paint);
109 
110  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
111 }
112 
113 TEST_P(AiksTest, RuntimeEffectWithInvalidSamplerDoesNotCrash) {
114  ScopedValidationDisable disable_validation;
115 
116  // Create a sampler that is not usable as an input to the runtime effect.
117  std::vector<flutter::DlColor> colors = {flutter::DlColor::kBlue(),
118  flutter::DlColor::kRed()};
119  const float stops[2] = {0.0, 1.0};
120  auto linear = flutter::DlColorSource::MakeLinear({0.0, 0.0}, {300.0, 300.0},
121  2, colors.data(), stops,
122  flutter::DlTileMode::kClamp);
123  std::vector<std::shared_ptr<DlColorSource>> sampler_inputs = {
124  linear,
125  };
126 
127  auto uniform_data = std::make_shared<std::vector<uint8_t>>();
128  uniform_data->resize(sizeof(Vector2));
129 
130  DlPaint paint;
131  paint.setColorSource(
132  MakeRuntimeEffect(this, "runtime_stage_filter_example.frag.iplr",
133  uniform_data, sampler_inputs));
134 
135  DisplayListBuilder builder;
136  builder.DrawPaint(paint);
137 
138  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
139 }
140 
141 TEST_P(AiksTest, ComposePaintRuntimeOuter) {
142  DisplayListBuilder builder;
143  DlPaint background;
144  background.setColor(DlColor(1.0, 0.1, 0.1, 0.1, DlColorSpace::kSRGB));
145  builder.DrawPaint(background);
146 
147  DlPaint paint;
148  paint.setColor(DlColor::kGreen());
149  float matrix[] = {
150  0, 1, 0, 0, 0, //
151  1, 0, 0, 0, 0, //
152  0, 0, 1, 0, 0, //
153  0, 0, 0, 1, 0 //
154  };
155  std::shared_ptr<DlImageFilter> color_filter =
156  DlImageFilter::MakeColorFilter(DlColorFilter::MakeMatrix(matrix));
157 
158  auto runtime_stages =
159  OpenAssetAsRuntimeStage("runtime_stage_filter_warp.frag.iplr");
160 
161  std::shared_ptr<RuntimeStage> runtime_stage =
162  runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
163  ASSERT_TRUE(runtime_stage);
164  ASSERT_TRUE(runtime_stage->IsDirty());
165 
166  std::vector<std::shared_ptr<DlColorSource>> sampler_inputs = {
167  nullptr,
168  };
169  auto uniform_data = std::make_shared<std::vector<uint8_t>>();
170  uniform_data->resize(sizeof(Vector2));
171 
172  auto runtime_filter = DlImageFilter::MakeRuntimeEffect(
173  DlRuntimeEffectImpeller::Make(runtime_stage), sampler_inputs,
174  uniform_data);
175 
176  builder.Translate(50, 50);
177  builder.Scale(0.7, 0.7);
178 
179  paint.setImageFilter(
180  DlImageFilter::MakeCompose(runtime_filter, color_filter));
181  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
182  builder.DrawImage(image, DlPoint(100.0, 100.0),
183  DlImageSampling::kNearestNeighbor, &paint);
184 
185  DlPaint green;
186  green.setColor(DlColor::kGreen());
187  builder.DrawLine({100, 100}, {200, 100}, green);
188  builder.DrawLine({100, 100}, {100, 200}, green);
189 
190  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
191 }
192 
193 TEST_P(AiksTest, ComposePaintRuntimeInner) {
194  auto runtime_stages =
195  OpenAssetAsRuntimeStage("runtime_stage_filter_warp.frag.iplr");
196 
197  std::shared_ptr<RuntimeStage> runtime_stage =
198  runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
199  ASSERT_TRUE(runtime_stage);
200  ASSERT_TRUE(runtime_stage->IsDirty());
201  Scalar xoffset = 50;
202  Scalar yoffset = 50;
203  Scalar xscale = 0.7;
204  Scalar yscale = 0.7;
205  bool compare = false;
206 
207  auto callback = [&]() -> sk_sp<DisplayList> {
208  if (AiksTest::ImGuiBegin("Controls", nullptr,
209  ImGuiWindowFlags_AlwaysAutoResize)) {
210  ImGui::SliderFloat("xoffset", &xoffset, -50, 50);
211  ImGui::SliderFloat("yoffset", &yoffset, -50, 50);
212  ImGui::SliderFloat("xscale", &xscale, 0, 1);
213  ImGui::SliderFloat("yscale", &yscale, 0, 1);
214  ImGui::Checkbox("compare", &compare);
215  ImGui::End();
216  }
217  DisplayListBuilder builder;
218  DlPaint background;
219  background.setColor(DlColor(1.0, 0.1, 0.1, 0.1, DlColorSpace::kSRGB));
220  builder.DrawPaint(background);
221 
222  DlPaint paint;
223  paint.setColor(DlColor::kGreen());
224  float matrix[] = {
225  0, 1, 0, 0, 0, //
226  1, 0, 0, 0, 0, //
227  0, 0, 1, 0, 0, //
228  0, 0, 0, 1, 0 //
229  };
230  std::shared_ptr<DlImageFilter> color_filter =
231  DlImageFilter::MakeColorFilter(DlColorFilter::MakeMatrix(matrix));
232 
233  std::vector<std::shared_ptr<DlColorSource>> sampler_inputs = {
234  nullptr,
235  };
236  auto uniform_data = std::make_shared<std::vector<uint8_t>>();
237  uniform_data->resize(sizeof(Vector2));
238 
239  auto runtime_filter = DlImageFilter::MakeRuntimeEffect(
240  DlRuntimeEffectImpeller::Make(runtime_stage), sampler_inputs,
241  uniform_data);
242 
243  builder.Translate(xoffset, yoffset);
244  builder.Scale(xscale, yscale);
245 
246  paint.setImageFilter(
247  DlImageFilter::MakeCompose(color_filter, runtime_filter));
248  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
249  builder.DrawImage(image, DlPoint(100.0, 100.0),
250  DlImageSampling::kNearestNeighbor, &paint);
251 
252  if (compare) {
253  paint.setImageFilter(
254  DlImageFilter::MakeCompose(runtime_filter, color_filter));
255  builder.DrawImage(image, DlPoint(800.0, 100.0),
256  DlImageSampling::kNearestNeighbor, &paint);
257 
258  paint.setImageFilter(runtime_filter);
259  builder.DrawImage(image, DlPoint(100.0, 800.0),
260  DlImageSampling::kNearestNeighbor, &paint);
261  }
262 
263  DlPaint green;
264  green.setColor(DlColor::kGreen());
265  builder.DrawLine({100, 100}, {200, 100}, green);
266  builder.DrawLine({100, 100}, {100, 200}, green);
267  if (compare) {
268  builder.DrawLine({800, 100}, {900, 100}, green);
269  builder.DrawLine({800, 100}, {800, 200}, green);
270  builder.DrawLine({100, 800}, {200, 800}, green);
271  builder.DrawLine({100, 800}, {100, 900}, green);
272  }
273 
274  return builder.Build();
275  };
276 
277  ASSERT_TRUE(OpenPlaygroundHere(callback));
278 }
279 
280 TEST_P(AiksTest, ComposeBackdropRuntimeOuterBlurInner) {
281  auto runtime_stages =
282  OpenAssetAsRuntimeStage("runtime_stage_filter_circle.frag.iplr");
283 
284  std::shared_ptr<RuntimeStage> runtime_stage =
285  runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
286  ASSERT_TRUE(runtime_stage);
287  ASSERT_TRUE(runtime_stage->IsDirty());
288  Scalar sigma = 20.0;
289 
290  auto callback = [&]() -> sk_sp<DisplayList> {
291  if (AiksTest::ImGuiBegin("Controls", nullptr,
292  ImGuiWindowFlags_AlwaysAutoResize)) {
293  ImGui::SliderFloat("sigma", &sigma, 0, 20);
294  ImGui::End();
295  }
296  DisplayListBuilder builder;
297  DlPaint background;
298  background.setColor(DlColor(1.0, 0.1, 0.1, 0.1, DlColorSpace::kSRGB));
299  builder.DrawPaint(background);
300 
301  auto blur_filter =
302  DlImageFilter::MakeBlur(sigma, sigma, DlTileMode::kClamp);
303 
304  std::vector<std::shared_ptr<DlColorSource>> sampler_inputs = {
305  nullptr,
306  };
307  auto uniform_data = std::make_shared<std::vector<uint8_t>>();
308  uniform_data->resize(sizeof(Vector2));
309 
310  auto runtime_filter = DlImageFilter::MakeRuntimeEffect(
311  DlRuntimeEffectImpeller::Make(runtime_stage), sampler_inputs,
312  uniform_data);
313 
314  auto backdrop_filter = DlImageFilter::MakeCompose(/*outer=*/runtime_filter,
315  /*inner=*/blur_filter);
316 
317  DlPaint paint;
318  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
319  builder.DrawImage(image, DlPoint(100.0, 100.0),
320  DlImageSampling::kNearestNeighbor, &paint);
321 
322  DlPaint save_paint;
323  save_paint.setBlendMode(DlBlendMode::kSrc);
324  builder.SaveLayer(std::nullopt, &save_paint, backdrop_filter.get());
325  builder.Restore();
326 
327  DlPaint green;
328  green.setColor(DlColor::kGreen());
329  builder.DrawLine({100, 100}, {200, 100}, green);
330  builder.DrawLine({100, 100}, {100, 200}, green);
331 
332  return builder.Build();
333  };
334 
335  ASSERT_TRUE(OpenPlaygroundHere(callback));
336 }
337 
338 TEST_P(AiksTest, ComposeBackdropRuntimeOuterBlurInnerSmallSigma) {
339  auto runtime_stages =
340  OpenAssetAsRuntimeStage("runtime_stage_filter_circle.frag.iplr");
341 
342  std::shared_ptr<RuntimeStage> runtime_stage =
343  runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
344  ASSERT_TRUE(runtime_stage);
345  ASSERT_TRUE(runtime_stage->IsDirty());
346  Scalar sigma = 5.0;
347 
348  auto callback = [&]() -> sk_sp<DisplayList> {
349  if (AiksTest::ImGuiBegin("Controls", nullptr,
350  ImGuiWindowFlags_AlwaysAutoResize)) {
351  ImGui::SliderFloat("sigma", &sigma, 0, 20);
352  ImGui::End();
353  }
354  DisplayListBuilder builder;
355  DlPaint background;
356  background.setColor(DlColor(1.0, 0.1, 0.1, 0.1, DlColorSpace::kSRGB));
357  builder.DrawPaint(background);
358 
359  auto blur_filter =
360  DlImageFilter::MakeBlur(sigma, sigma, DlTileMode::kClamp);
361 
362  std::vector<std::shared_ptr<DlColorSource>> sampler_inputs = {
363  nullptr,
364  };
365  auto uniform_data = std::make_shared<std::vector<uint8_t>>();
366  uniform_data->resize(sizeof(Vector2));
367 
368  auto runtime_filter = DlImageFilter::MakeRuntimeEffect(
369  DlRuntimeEffectImpeller::Make(runtime_stage), sampler_inputs,
370  uniform_data);
371 
372  auto backdrop_filter = DlImageFilter::MakeCompose(/*outer=*/runtime_filter,
373  /*inner=*/blur_filter);
374 
375  DlPaint paint;
376  auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg"));
377  builder.DrawImage(image, DlPoint(100.0, 100.0),
378  DlImageSampling::kNearestNeighbor, &paint);
379 
380  DlPaint save_paint;
381  save_paint.setBlendMode(DlBlendMode::kSrc);
382  builder.SaveLayer(std::nullopt, &save_paint, backdrop_filter.get());
383  builder.Restore();
384 
385  DlPaint green;
386  green.setColor(DlColor::kGreen());
387  builder.DrawLine({100, 100}, {200, 100}, green);
388  builder.DrawLine({100, 100}, {100, 200}, green);
389 
390  return builder.Build();
391  };
392 
393  ASSERT_TRUE(OpenPlaygroundHere(callback));
394 }
395 
396 TEST_P(AiksTest, ClippedBackdropFilterWithShader) {
397  struct FragUniforms {
398  Vector2 uSize;
399  } frag_uniforms = {.uSize = Vector2(400, 400)};
400  auto uniform_data = std::make_shared<std::vector<uint8_t>>();
401  uniform_data->resize(sizeof(FragUniforms));
402  memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms));
403 
404  auto runtime_stages =
405  OpenAssetAsRuntimeStage("runtime_stage_border.frag.iplr");
406  auto runtime_stage =
407  runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
408  ASSERT_TRUE(runtime_stage);
409  ASSERT_TRUE(runtime_stage->IsDirty());
410 
411  std::vector<std::shared_ptr<DlColorSource>> sampler_inputs = {
412  nullptr,
413  };
414 
415  auto runtime_filter = DlImageFilter::MakeRuntimeEffect(
416  DlRuntimeEffectImpeller::Make(runtime_stage), sampler_inputs,
417  uniform_data);
418 
419  DisplayListBuilder builder;
420 
421  // Draw a background so the backdrop filter has something to affect
422  DlPaint background_paint;
423  background_paint.setColor(DlColor::kWhite());
424  builder.DrawPaint(background_paint);
425 
426  // Draw some pattern to verify the filter effect
427  DlPaint pattern_paint;
428  pattern_paint.setColor(DlColor::kRed());
429  builder.DrawRect(DlRect::MakeXYWH(0, 0, 200, 200), pattern_paint);
430  pattern_paint.setColor(DlColor::kBlue());
431  builder.DrawRect(DlRect::MakeXYWH(200, 200, 200, 200), pattern_paint);
432 
433  builder.Save();
434 
435  // Replicate the clip rect (inset by 66)
436  // Assuming a 400x400 screen, inset 66 gives roughly 66, 66, 268, 268
437  builder.ClipRect(DlRect::MakeXYWH(66, 66, 268, 268));
438 
439  DlPaint save_paint;
440  // The Flutter code uses a backdrop filter layer.
441  // In DisplayList, this corresponds to SaveLayer with a backdrop filter.
442  builder.SaveLayer(std::nullopt, &save_paint, runtime_filter.get());
443 
444  // The child was empty in the Flutter example, so we don't draw anything
445  // inside the SaveLayer
446 
447  builder.Restore(); // Restore SaveLayer
448  builder.Restore(); // Restore Save (Clip)
449 
450  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
451 }
452 
453 } // namespace testing
454 } // namespace impeller
static sk_sp< DlRuntimeEffect > Make(std::shared_ptr< impeller::RuntimeStage > runtime_stage)
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
int32_t x
AiksPlayground AiksTest
TEST_P(AiksTest, DrawAtlasNoColor)
Point Vector2
Definition: point.h:331
float Scalar
Definition: scalar.h:19
constexpr RuntimeStageBackend PlaygroundBackendToRuntimeStageBackend(PlaygroundBackend backend)
Definition: playground.h:33
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:24
static constexpr TSize MakeWH(Type width, Type height)
Definition: size.h:43