| 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"
12 #include "flutter/impeller/display_list/aiks_unittests.h"
13 #include "flutter/impeller/display_list/dl_image_impeller.h"
14 #include "flutter/impeller/display_list/dl_runtime_effect_impeller.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
aiks_unittests.h
flutter::DlRuntimeEffectImpeller::Make
static sk_sp< DlRuntimeEffect > Make(std::shared_ptr< impeller::RuntimeStage > runtime_stage)
Definition: dl_runtime_effect_impeller.cc:14
impeller::AiksPlayground
Definition: aiks_playground.h:16
impeller::AiksPlayground::ImGuiBegin
static bool ImGuiBegin(const char *name, bool *p_open, ImGuiWindowFlags flags)
Definition: aiks_playground.cc:30
impeller::DlImageImpeller::Make
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
Definition: dl_image_impeller.cc:23
x
int32_t x
Definition: dl_golden_unittests.cc:458
dl_image_impeller.h
dl_runtime_effect_impeller.h
flutter
Definition: dl_golden_blur_unittests.cc:16
impeller::testing::AiksTest
AiksPlayground AiksTest
Definition: aiks_unittests.h:17
impeller::testing::TEST_P
TEST_P(AiksTest, DrawAtlasNoColor)
Definition: aiks_dl_atlas_unittests.cc:59
impeller
Definition: allocation.cc:12
impeller::Vector2
Point Vector2
Definition: point.h:331
impeller::Scalar
float Scalar
Definition: scalar.h:19
impeller::PlaygroundBackendToRuntimeStageBackend
constexpr RuntimeStageBackend PlaygroundBackendToRuntimeStageBackend(PlaygroundBackend backend)
Definition: playground.h:33
impeller::DlPoint
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:24
impeller::ScopedValidationDisable
Definition: validation.h:56
impeller::TPoint< Scalar >
impeller::TSize< Scalar >
impeller::TSize< Scalar >::MakeWH
static constexpr TSize MakeWH(Type width, Type height)
Definition: size.h:43