Flutter Engine
 
Loading...
Searching...
No Matches
clip_rrect_layer_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
6
13#include "flutter/fml/macros.h"
14
15// TODO(zanderso): https://github.com/flutter/flutter/issues/127701
16// NOLINTBEGIN(bugprone-unchecked-optional-access)
17
18namespace flutter {
19namespace testing {
20
22
23#ifndef NDEBUG
24TEST_F(ClipRRectLayerTest, ClipNoneBehaviorDies) {
25 const DlRoundRect layer_rrect = DlRoundRect();
26 EXPECT_DEATH_IF_SUPPORTED(
27 auto clip = std::make_shared<ClipRRectLayer>(layer_rrect, Clip::kNone),
28 "clip_behavior != Clip::kNone");
29}
30
31TEST_F(ClipRRectLayerTest, PaintingEmptyLayerDies) {
32 const DlRoundRect layer_rrect = DlRoundRect();
33 auto layer = std::make_shared<ClipRRectLayer>(layer_rrect, Clip::kHardEdge);
34
35 layer->Preroll(preroll_context());
36
37 // Untouched
38 EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), kGiantRect);
39 EXPECT_TRUE(preroll_context()->state_stack.is_empty());
40
41 EXPECT_EQ(layer->paint_bounds(), DlRect());
42 EXPECT_EQ(layer->child_paint_bounds(), DlRect());
43 EXPECT_FALSE(layer->needs_painting(paint_context()));
44
45 EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
46 "needs_painting\\(context\\)");
47}
48
49TEST_F(ClipRRectLayerTest, PaintBeforePrerollDies) {
50 const DlRect layer_bounds = DlRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
51 const DlRoundRect layer_rrect = DlRoundRect::MakeRect(layer_bounds);
52 auto layer = std::make_shared<ClipRRectLayer>(layer_rrect, Clip::kHardEdge);
53 EXPECT_EQ(layer->paint_bounds(), DlRect());
54 EXPECT_EQ(layer->child_paint_bounds(), DlRect());
55 EXPECT_FALSE(layer->needs_painting(paint_context()));
56
57 EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
58 "needs_painting\\(context\\)");
59}
60
61TEST_F(ClipRRectLayerTest, PaintingCulledLayerDies) {
62 const DlMatrix initial_matrix = DlMatrix::MakeTranslation({0.5f, 1.0f});
63 const DlRect child_bounds = DlRect::MakeXYWH(1.0, 2.0, 2.0, 2.0);
64 const DlRect layer_bounds = DlRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
65 const DlRect distant_bounds = DlRect::MakeXYWH(100.0, 100.0, 10.0, 10.0);
66 const DlPath child_path = DlPath::MakeRect(child_bounds);
67 const DlRoundRect layer_rrect =
68 DlRoundRect::MakeRectXY(layer_bounds, 0.1f, 0.1f);
69 const DlPaint child_paint = DlPaint(DlColor::kYellow());
70 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
71 auto layer = std::make_shared<ClipRRectLayer>(layer_rrect, Clip::kHardEdge);
72 layer->Add(mock_layer);
73
74 // Cull these children
75 preroll_context()->state_stack.set_preroll_delegate(distant_bounds,
76 initial_matrix);
77 layer->Preroll(preroll_context());
78
79 // Untouched
80 EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), distant_bounds);
81 EXPECT_TRUE(preroll_context()->state_stack.is_empty());
82
83 EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
84 EXPECT_EQ(layer->paint_bounds(), child_bounds);
85 EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
86 EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
87 EXPECT_TRUE(layer->needs_painting(paint_context()));
88 EXPECT_EQ(mock_layer->parent_cull_rect(), DlRect());
89 EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
90 EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)}));
91
92 auto mutator = paint_context().state_stack.save();
93 mutator.clipRect(distant_bounds, false);
94 EXPECT_FALSE(mock_layer->needs_painting(paint_context()));
95 EXPECT_FALSE(layer->needs_painting(paint_context()));
96 EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
97 "needs_painting\\(context\\)");
98}
99#endif
100
101TEST_F(ClipRRectLayerTest, ChildOutsideBounds) {
102 const DlMatrix initial_matrix = DlMatrix::MakeTranslation({0.5f, 1.0f});
103 const DlRect local_cull_bounds = DlRect::MakeXYWH(0.0, 0.0, 2.0, 4.0);
104 const DlRect device_cull_bounds =
105 local_cull_bounds.TransformAndClipBounds(initial_matrix);
106 const DlRect child_bounds = DlRect::MakeXYWH(2.5, 5.0, 4.5, 4.0);
107 const DlRect clip_bounds = DlRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
108 const DlPath child_path = DlPath::MakeRect(child_bounds);
109 const DlRoundRect clip_rrect =
110 DlRoundRect::MakeRectXY(clip_bounds, 0.1f, 0.1f);
111 const DlPaint child_paint = DlPaint(DlColor::kYellow());
112 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
113 auto layer = std::make_shared<ClipRRectLayer>(clip_rrect, Clip::kHardEdge);
114 layer->Add(mock_layer);
115
116 auto clip_cull_rect = clip_bounds.Intersection(local_cull_bounds);
117 ASSERT_TRUE(clip_cull_rect.has_value());
118 auto clip_layer_bounds = child_bounds.Intersection(clip_bounds);
119 ASSERT_TRUE(clip_layer_bounds.has_value());
120
121 // Set up both contexts to cull clipped child
122 preroll_context()->state_stack.set_preroll_delegate(device_cull_bounds,
123 initial_matrix);
124 paint_context().canvas->ClipRect(device_cull_bounds);
125 paint_context().canvas->Transform(initial_matrix);
126
127 layer->Preroll(preroll_context());
128 // Untouched
129 EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(),
130 device_cull_bounds);
131 EXPECT_EQ(preroll_context()->state_stack.local_cull_rect(),
132 local_cull_bounds);
133 EXPECT_TRUE(preroll_context()->state_stack.is_empty());
134
135 EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
136 EXPECT_EQ(layer->paint_bounds(), clip_layer_bounds.value());
137 EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
138 EXPECT_EQ(mock_layer->parent_cull_rect(), clip_cull_rect.value());
139 EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
140 EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(clip_rrect)}));
141
142 EXPECT_FALSE(mock_layer->needs_painting(paint_context()));
143 ASSERT_FALSE(layer->needs_painting(paint_context()));
144 // Top level layer not visible so calling layer->Paint()
145 // would trip an FML_DCHECK
146}
147
148TEST_F(ClipRRectLayerTest, RectRRectReducesToClipRect) {
149 const DlMatrix initial_matrix = DlMatrix::MakeTranslation({0.5f, 1.0f});
150 const DlRect child_bounds = DlRect::MakeXYWH(1.0, 2.0, 2.0, 2.0);
151 const DlRect layer_bounds = DlRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
152 const DlPath child_path = DlPath::MakeRect(child_bounds) +
153 DlPath::MakeOval(child_bounds.Expand(-0.1f));
154 const DlRoundRect layer_rrect = DlRoundRect::MakeRect(layer_bounds);
155 const DlPaint child_paint = DlPaint(DlColor::kYellow());
156 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
157 auto layer = std::make_shared<ClipRRectLayer>(layer_rrect, Clip::kHardEdge);
158 layer->Add(mock_layer);
159
160 preroll_context()->state_stack.set_preroll_delegate(initial_matrix);
161 layer->Preroll(preroll_context());
162
163 // Untouched
164 EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), kGiantRect);
165 EXPECT_TRUE(preroll_context()->state_stack.is_empty());
166
167 EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
168 EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds());
169 EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
170 EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
171 EXPECT_TRUE(layer->needs_painting(paint_context()));
172 EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds);
173 EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
174 EXPECT_EQ(mock_layer->parent_mutators(),
175 std::vector({Mutator(layer_bounds)}));
176
177 layer->Paint(display_list_paint_context());
178 DisplayListBuilder expected_builder;
179 /* (ClipRRect)layer::Paint */ {
180 expected_builder.Save();
181 {
182 expected_builder.ClipRect(layer_bounds);
183 /* mock_layer::Paint */ {
184 expected_builder.DrawPath(child_path, child_paint);
185 }
186 }
187 expected_builder.Restore();
188 }
189 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
190}
191
192TEST_F(ClipRRectLayerTest, FullyContainedChild) {
193 const DlMatrix initial_matrix = DlMatrix::MakeTranslation({0.5f, 1.0f});
194 const DlRect child_bounds = DlRect::MakeXYWH(1.0, 2.0, 2.0, 2.0);
195 const DlRect layer_bounds = DlRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
196 const DlPath child_path = DlPath::MakeRect(child_bounds) +
197 DlPath::MakeOval(child_bounds.Expand(-0.1f));
198 const DlRoundRect layer_rrect =
199 DlRoundRect::MakeRectXY(layer_bounds, 0.1, 0.1);
200 const DlPaint child_paint = DlPaint(DlColor::kYellow());
201 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
202 auto layer = std::make_shared<ClipRRectLayer>(layer_rrect, Clip::kHardEdge);
203 layer->Add(mock_layer);
204
205 preroll_context()->state_stack.set_preroll_delegate(initial_matrix);
206 layer->Preroll(preroll_context());
207
208 // Untouched
209 EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(), kGiantRect);
210 EXPECT_TRUE(preroll_context()->state_stack.is_empty());
211
212 EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
213 EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds());
214 EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
215 EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
216 EXPECT_TRUE(layer->needs_painting(paint_context()));
217 EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds);
218 EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
219 EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_rrect)}));
220
221 layer->Paint(display_list_paint_context());
222 DisplayListBuilder expected_builder;
223 /* (ClipRRect)layer::Paint */ {
224 expected_builder.Save();
225 {
226 expected_builder.ClipRoundRect(layer_rrect);
227 /* mock_layer::Paint */ {
228 expected_builder.DrawPath(child_path, child_paint);
229 }
230 }
231 expected_builder.Restore();
232 }
233 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
234}
235
236TEST_F(ClipRRectLayerTest, PartiallyContainedChild) {
237 const DlMatrix initial_matrix = DlMatrix::MakeTranslation({0.5f, 1.0f});
238 const DlRect local_cull_bounds = DlRect::MakeXYWH(0.0, 0.0, 4.0, 5.5);
239 const DlRect device_cull_bounds =
240 local_cull_bounds.TransformAndClipBounds(initial_matrix);
241 const DlRect child_bounds = DlRect::MakeXYWH(2.5, 5.0, 4.5, 4.0);
242 const DlRect clip_bounds = DlRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
243 const DlPath child_path = DlPath::MakeRect(child_bounds) +
244 DlPath::MakeOval(child_bounds.Expand(-0.1f));
245 const DlRoundRect clip_rrect = DlRoundRect::MakeRectXY(clip_bounds, 0.1, 0.1);
246 const DlPaint child_paint = DlPaint(DlColor::kYellow());
247 auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
248 auto layer = std::make_shared<ClipRRectLayer>(clip_rrect, Clip::kHardEdge);
249 layer->Add(mock_layer);
250
251 auto clip_cull_rect = clip_bounds.Intersection(local_cull_bounds);
252 ASSERT_TRUE(clip_cull_rect.has_value());
253 auto clip_layer_bounds = child_bounds.Intersection(clip_bounds);
254 ASSERT_TRUE(clip_layer_bounds.has_value());
255
256 preroll_context()->state_stack.set_preroll_delegate(device_cull_bounds,
257 initial_matrix);
258
259 layer->Preroll(preroll_context());
260 // Untouched
261 EXPECT_EQ(preroll_context()->state_stack.device_cull_rect(),
262 device_cull_bounds);
263 EXPECT_EQ(preroll_context()->state_stack.local_cull_rect(),
264 local_cull_bounds);
265 EXPECT_TRUE(preroll_context()->state_stack.is_empty());
266
267 EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
268 EXPECT_EQ(layer->paint_bounds(), clip_layer_bounds.value());
269 EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
270 EXPECT_EQ(mock_layer->parent_cull_rect(), clip_cull_rect.value());
271 EXPECT_EQ(mock_layer->parent_matrix(), initial_matrix);
272 EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(clip_rrect)}));
273
274 layer->Paint(display_list_paint_context());
275 DisplayListBuilder expected_builder;
276 /* (ClipRRect)layer::Paint */ {
277 expected_builder.Save();
278 {
279 expected_builder.ClipRoundRect(clip_rrect);
280 /* mock_layer::Paint */ {
281 expected_builder.DrawPath(child_path, child_paint);
282 }
283 }
284 expected_builder.Restore();
285 }
286 EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
287}
288
289static bool ReadbackResult(PrerollContext* context,
290 Clip clip_behavior,
291 const std::shared_ptr<Layer>& child,
292 bool before) {
293 const DlRect layer_bounds = DlRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
294 const DlRoundRect layer_rrect = DlRoundRect::MakeRect(layer_bounds);
295 auto layer = std::make_shared<ClipRRectLayer>(layer_rrect, clip_behavior);
296 if (child != nullptr) {
297 layer->Add(child);
298 }
299 context->surface_needs_readback = before;
300 layer->Preroll(context);
301 return context->surface_needs_readback;
302}
303
304TEST_F(ClipRRectLayerTest, Readback) {
305 PrerollContext* context = preroll_context();
306 DlPath path;
307 DlPaint paint;
308
309 const Clip hard = Clip::kHardEdge;
310 const Clip soft = Clip::kAntiAlias;
311 const Clip save_layer = Clip::kAntiAliasWithSaveLayer;
312
313 std::shared_ptr<MockLayer> nochild;
314 auto reader = std::make_shared<MockLayer>(path, paint);
315 reader->set_fake_reads_surface(true);
316 auto nonreader = std::make_shared<MockLayer>(path, paint);
317
318 // No children, no prior readback -> no readback after
319 EXPECT_FALSE(ReadbackResult(context, hard, nochild, false));
320 EXPECT_FALSE(ReadbackResult(context, soft, nochild, false));
321 EXPECT_FALSE(ReadbackResult(context, save_layer, nochild, false));
322
323 // No children, prior readback -> readback after
324 EXPECT_TRUE(ReadbackResult(context, hard, nochild, true));
325 EXPECT_TRUE(ReadbackResult(context, soft, nochild, true));
326 EXPECT_TRUE(ReadbackResult(context, save_layer, nochild, true));
327
328 // Non readback child, no prior readback -> no readback after
329 EXPECT_FALSE(ReadbackResult(context, hard, nonreader, false));
330 EXPECT_FALSE(ReadbackResult(context, soft, nonreader, false));
331 EXPECT_FALSE(ReadbackResult(context, save_layer, nonreader, false));
332
333 // Non readback child, prior readback -> readback after
334 EXPECT_TRUE(ReadbackResult(context, hard, nonreader, true));
335 EXPECT_TRUE(ReadbackResult(context, soft, nonreader, true));
336 EXPECT_TRUE(ReadbackResult(context, save_layer, nonreader, true));
337
338 // Readback child, no prior readback -> readback after unless SaveLayer
339 EXPECT_TRUE(ReadbackResult(context, hard, reader, false));
340 EXPECT_TRUE(ReadbackResult(context, soft, reader, false));
341 EXPECT_FALSE(ReadbackResult(context, save_layer, reader, false));
342
343 // Readback child, prior readback -> readback after
344 EXPECT_TRUE(ReadbackResult(context, hard, reader, true));
345 EXPECT_TRUE(ReadbackResult(context, soft, reader, true));
346 EXPECT_TRUE(ReadbackResult(context, save_layer, reader, true));
347}
348
349TEST_F(ClipRRectLayerTest, OpacityInheritance) {
350 auto path1 = DlPath::MakeRectLTRB(10, 10, 30, 30);
351 auto mock1 = MockLayer::MakeOpacityCompatible(path1);
352 DlRect clip_rect = DlRect::MakeWH(500, 500);
353 DlRoundRect clip_rrect = DlRoundRect::MakeRectXY(clip_rect, 20, 20);
354 auto clip_rrect_layer =
355 std::make_shared<ClipRRectLayer>(clip_rrect, Clip::kHardEdge);
356 clip_rrect_layer->Add(mock1);
357
358 // ClipRectLayer will pass through compatibility from a compatible child
359 PrerollContext* context = preroll_context();
360 clip_rrect_layer->Preroll(context);
361 EXPECT_EQ(context->renderable_state_flags,
363
364 auto path2 = DlPath::MakeRectLTRB(40, 40, 50, 50);
365 auto mock2 = MockLayer::MakeOpacityCompatible(path2);
366 clip_rrect_layer->Add(mock2);
367
368 // ClipRectLayer will pass through compatibility from multiple
369 // non-overlapping compatible children
370 clip_rrect_layer->Preroll(context);
371 EXPECT_EQ(context->renderable_state_flags,
373
374 auto path3 = DlPath::MakeRectLTRB(20, 20, 40, 40);
375 auto mock3 = MockLayer::MakeOpacityCompatible(path3);
376 clip_rrect_layer->Add(mock3);
377
378 // ClipRectLayer will not pass through compatibility from multiple
379 // overlapping children even if they are individually compatible
380 clip_rrect_layer->Preroll(context);
381 EXPECT_EQ(context->renderable_state_flags, 0);
382
383 {
384 // ClipRectLayer(aa with saveLayer) will always be compatible
385 auto clip_rrect_savelayer = std::make_shared<ClipRRectLayer>(
387 clip_rrect_savelayer->Add(mock1);
388 clip_rrect_savelayer->Add(mock2);
389
390 // Double check first two children are compatible and non-overlapping
391 clip_rrect_savelayer->Preroll(context);
392 EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags);
393
394 // Now add the overlapping child and test again, should still be compatible
395 clip_rrect_savelayer->Add(mock3);
396 clip_rrect_savelayer->Preroll(context);
397 EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags);
398 }
399
400 // An incompatible, but non-overlapping child for the following tests
401 auto path4 = DlPath::MakeRectLTRB(60, 60, 70, 70);
402 auto mock4 = MockLayer::Make(path4);
403
404 {
405 // ClipRectLayer with incompatible child will not be compatible
406 auto clip_rrect_bad_child =
407 std::make_shared<ClipRRectLayer>(clip_rrect, Clip::kHardEdge);
408 clip_rrect_bad_child->Add(mock1);
409 clip_rrect_bad_child->Add(mock2);
410
411 // Double check first two children are compatible and non-overlapping
412 clip_rrect_bad_child->Preroll(context);
413 EXPECT_EQ(context->renderable_state_flags,
415
416 clip_rrect_bad_child->Add(mock4);
417
418 // The third child is non-overlapping, but not compatible so the
419 // TransformLayer should end up incompatible
420 clip_rrect_bad_child->Preroll(context);
421 EXPECT_EQ(context->renderable_state_flags, 0);
422 }
423
424 {
425 // ClipRectLayer(aa with saveLayer) will always be compatible
426 auto clip_rrect_savelayer_bad_child = std::make_shared<ClipRRectLayer>(
428 clip_rrect_savelayer_bad_child->Add(mock1);
429 clip_rrect_savelayer_bad_child->Add(mock2);
430
431 // Double check first two children are compatible and non-overlapping
432 clip_rrect_savelayer_bad_child->Preroll(context);
433 EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags);
434
435 // Now add the incompatible child and test again, should still be compatible
436 clip_rrect_savelayer_bad_child->Add(mock4);
437 clip_rrect_savelayer_bad_child->Preroll(context);
438 EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags);
439 }
440}
441
442TEST_F(ClipRRectLayerTest, OpacityInheritancePainting) {
443 auto path1 = DlPath::MakeRectLTRB(10, 10, 30, 30);
444 auto mock1 = MockLayer::MakeOpacityCompatible(path1);
445 auto path2 = DlPath::MakeRectLTRB(40, 40, 50, 50);
446 auto mock2 = MockLayer::MakeOpacityCompatible(path2);
447 DlRect clip_rect = DlRect::MakeWH(500, 500);
448 DlRoundRect clip_rrect = DlRoundRect::MakeRectXY(clip_rect, 20, 20);
449 auto clip_rect_layer =
450 std::make_shared<ClipRRectLayer>(clip_rrect, Clip::kAntiAlias);
451 clip_rect_layer->Add(mock1);
452 clip_rect_layer->Add(mock2);
453
454 // ClipRectLayer will pass through compatibility from multiple
455 // non-overlapping compatible children
456 PrerollContext* context = preroll_context();
457 clip_rect_layer->Preroll(context);
458 EXPECT_EQ(context->renderable_state_flags,
460
461 int opacity_alpha = 0x7F;
462 DlPoint offset = DlPoint(10, 10);
463 auto opacity_layer = std::make_shared<OpacityLayer>(opacity_alpha, offset);
464 opacity_layer->Add(clip_rect_layer);
465 opacity_layer->Preroll(context);
466 EXPECT_TRUE(opacity_layer->children_can_accept_opacity());
467
468 DisplayListBuilder expected_builder;
469 /* OpacityLayer::Paint() */ {
470 expected_builder.Save();
471 {
472 expected_builder.Translate(offset.x, offset.y);
473 /* ClipRectLayer::Paint() */ {
474 expected_builder.Save();
475 expected_builder.ClipRoundRect(clip_rrect, DlClipOp::kIntersect, true);
476 /* child layer1 paint */ {
477 expected_builder.DrawPath(path1, DlPaint().setAlpha(opacity_alpha));
478 }
479 /* child layer2 paint */ {
480 expected_builder.DrawPath(path2, DlPaint().setAlpha(opacity_alpha));
481 }
482 expected_builder.Restore();
483 }
484 }
485 expected_builder.Restore();
486 }
487
488 opacity_layer->Paint(display_list_paint_context());
489 EXPECT_TRUE(DisplayListsEQ_Verbose(expected_builder.Build(), display_list()));
490}
491
492TEST_F(ClipRRectLayerTest, OpacityInheritanceSaveLayerPainting) {
493 auto path1 = DlPath::MakeRectLTRB(10, 10, 30, 30);
494 auto mock1 = MockLayer::MakeOpacityCompatible(path1);
495 auto path2 = DlPath::MakeRectLTRB(20, 20, 40, 40);
496 auto mock2 = MockLayer::MakeOpacityCompatible(path2);
497 auto children_bounds = path1.GetBounds().Union(path2.GetBounds());
498 DlRect clip_rect = DlRect::MakeWH(500, 500);
499 DlRoundRect clip_rrect = DlRoundRect::MakeRectXY(clip_rect, 20, 20);
500 auto clip_rrect_layer = std::make_shared<ClipRRectLayer>(
502 clip_rrect_layer->Add(mock1);
503 clip_rrect_layer->Add(mock2);
504
505 // ClipRectLayer will pass through compatibility from multiple
506 // non-overlapping compatible children
507 PrerollContext* context = preroll_context();
508 clip_rrect_layer->Preroll(context);
509 EXPECT_EQ(context->renderable_state_flags, Layer::kSaveLayerRenderFlags);
510
511 int opacity_alpha = 0x7F;
512 DlPoint offset = DlPoint(10, 10);
513 auto opacity_layer = std::make_shared<OpacityLayer>(opacity_alpha, offset);
514 opacity_layer->Add(clip_rrect_layer);
515 opacity_layer->Preroll(context);
516 EXPECT_TRUE(opacity_layer->children_can_accept_opacity());
517
518 DisplayListBuilder expected_builder;
519 /* OpacityLayer::Paint() */ {
520 expected_builder.Save();
521 {
522 expected_builder.Translate(offset.x, offset.y);
523 /* ClipRectLayer::Paint() */ {
524 expected_builder.Save();
525 expected_builder.ClipRoundRect(clip_rrect, DlClipOp::kIntersect, true);
526 expected_builder.SaveLayer(children_bounds,
527 &DlPaint().setAlpha(opacity_alpha));
528 /* child layer1 paint */ {
529 expected_builder.DrawPath(path1, DlPaint());
530 }
531 /* child layer2 paint */ { //
532 expected_builder.DrawPath(path2, DlPaint());
533 }
534 expected_builder.Restore();
535 }
536 }
537 expected_builder.Restore();
538 }
539
540 opacity_layer->Paint(display_list_paint_context());
541 EXPECT_TRUE(DisplayListsEQ_Verbose(expected_builder.Build(), display_list()));
542}
543
544TEST_F(ClipRRectLayerTest, LayerCached) {
545 auto path1 = DlPath::MakeRectLTRB(10, 10, 30, 30);
546 DlPaint paint = DlPaint();
547 auto mock1 = MockLayer::MakeOpacityCompatible(path1);
548 DlRect clip_rect = DlRect::MakeWH(500, 500);
549 DlRoundRect clip_rrect = DlRoundRect::MakeRectXY(clip_rect, 20, 20);
550 auto layer = std::make_shared<ClipRRectLayer>(clip_rrect,
552 layer->Add(mock1);
553
554 auto initial_transform = DlMatrix::MakeTranslation({50.0, 25.5});
555 DlMatrix cache_ctm = initial_transform;
556 DisplayListBuilder cache_canvas;
557 cache_canvas.Transform(cache_ctm);
558
559 use_mock_raster_cache();
560 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
561
562 const auto* clip_cache_item = layer->raster_cache_item();
563
564 layer->Preroll(preroll_context());
565 LayerTree::TryToRasterCache(cacheable_items(), &paint_context());
566
567 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
568 EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone);
569
570 layer->Preroll(preroll_context());
571 LayerTree::TryToRasterCache(cacheable_items(), &paint_context());
572 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
573 EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone);
574
575 layer->Preroll(preroll_context());
576 LayerTree::TryToRasterCache(cacheable_items(), &paint_context());
577 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1);
578 EXPECT_EQ(clip_cache_item->cache_state(),
580 EXPECT_TRUE(raster_cache()->Draw(clip_cache_item->GetId().value(),
581 cache_canvas, &paint));
582}
583
584TEST_F(ClipRRectLayerTest, NoSaveLayerShouldNotCache) {
585 auto path1 = DlPath::MakeRectLTRB(10, 10, 30, 30);
586
587 auto mock1 = MockLayer::MakeOpacityCompatible(path1);
588 DlRect clip_rect = DlRect::MakeWH(500, 500);
589 DlRoundRect clip_rrect = DlRoundRect::MakeRectXY(clip_rect, 20, 20);
590 auto layer = std::make_shared<ClipRRectLayer>(clip_rrect, Clip::kAntiAlias);
591 layer->Add(mock1);
592
593 auto initial_transform = DlMatrix::MakeTranslation({50.0, 25.5});
594
595 use_mock_raster_cache();
596 preroll_context()->state_stack.set_preroll_delegate(initial_transform);
597
598 const auto* clip_cache_item = layer->raster_cache_item();
599
600 layer->Preroll(preroll_context());
601 LayerTree::TryToRasterCache(cacheable_items(), &paint_context());
602
603 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
604 EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone);
605
606 layer->Preroll(preroll_context());
607 LayerTree::TryToRasterCache(cacheable_items(), &paint_context());
608 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
609 EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone);
610
611 layer->Preroll(preroll_context());
612 LayerTree::TryToRasterCache(cacheable_items(), &paint_context());
613 EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
614 EXPECT_EQ(clip_cache_item->cache_state(), RasterCacheItem::CacheState::kNone);
615}
616
617TEST_F(ClipRRectLayerTest, EmptyClipDoesNotCullPlatformView) {
618 const DlPoint view_offset = DlPoint(0.0f, 0.0f);
619 const DlSize view_size = DlSize(8.0f, 8.0f);
620 const int64_t view_id = 42;
621 auto platform_view =
622 std::make_shared<PlatformViewLayer>(view_offset, view_size, view_id);
623
624 DlRoundRect clip_rrect = DlRoundRect::MakeRectXY(DlRect(), 20, 20);
625 auto clip = std::make_shared<ClipRRectLayer>(clip_rrect, Clip::kAntiAlias);
626 clip->Add(platform_view);
627
628 auto embedder = MockViewEmbedder();
629 DisplayListBuilder fake_overlay_builder;
630 embedder.AddCanvas(&fake_overlay_builder);
631 preroll_context()->view_embedder = &embedder;
632 paint_context().view_embedder = &embedder;
633
634 clip->Preroll(preroll_context());
635 EXPECT_EQ(embedder.prerolled_views(), std::vector<int64_t>({view_id}));
636
637 clip->Paint(paint_context());
638 EXPECT_EQ(embedder.painted_views(), std::vector<int64_t>({view_id}));
639}
640
641} // namespace testing
642} // namespace flutter
643
644// NOLINTEND(bugprone-unchecked-optional-access)
std::unique_ptr< flutter::PlatformViewIOS > platform_view
void ClipRect(const DlRect &rect, DlClipOp clip_op=DlClipOp::kIntersect, bool is_aa=false) override
sk_sp< DisplayList > Build()
Definition dl_builder.cc:66
void DrawPath(const DlPath &path, const DlPaint &paint) override
static DlPath MakeRectLTRB(DlScalar left, DlScalar top, DlScalar right, DlScalar bottom)
Definition dl_path.cc:43
static DlPath MakeRect(const DlRect &rect)
Definition dl_path.cc:39
static DlPath MakeOval(const DlRect &bounds)
Definition dl_path.cc:57
static constexpr int kSaveLayerRenderFlags
Definition layer.h:118
static constexpr int kCallerCanApplyOpacity
static void TryToRasterCache(const std::vector< RasterCacheItem * > &raster_cached_entries, const PaintContext *paint_context, bool ignore_raster_cache=false)
Definition layer_tree.cc:67
static std::shared_ptr< MockLayer > Make(const DlPath &path, DlPaint paint=DlPaint())
Definition mock_layer.h:30
static std::shared_ptr< MockLayer > MakeOpacityCompatible(const DlPath &path)
Definition mock_layer.h:35
G_BEGIN_DECLS FlutterViewId view_id
TEST_F(DisplayListTest, Defaults)
LayerTestBase<::testing::Test > LayerTest
Definition layer_test.h:177
bool DisplayListsEQ_Verbose(const DisplayList *a, const DisplayList *b)
static bool ReadbackResult(PrerollContext *context, Clip clip_behavior, const std::shared_ptr< Layer > &child, bool before)
@ kAntiAlias
Definition layer.h:43
@ kAntiAliasWithSaveLayer
Definition layer.h:43
@ kNone
Definition layer.h:43
@ kHardEdge
Definition layer.h:43
impeller::RoundRect DlRoundRect
impeller::Matrix DlMatrix
impeller::Rect DlRect
impeller::Size DlSize
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switch_defs.h:52
static constexpr DlRect kGiantRect
Definition layer.h:40
impeller::Point DlPoint
flutter::DlPath DlPath
flutter::DlPaint DlPaint
static constexpr DlColor kYellow()
Definition dl_color.h:76
bool surface_needs_readback
Definition layer.h:51
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
constexpr Quad Transform(const Quad &quad) const
Definition matrix.h:623
static RoundRect MakeRectXY(const Rect &rect, Scalar x_radius, Scalar y_radius)
Definition round_rect.h:31
static RoundRect MakeRect(const Rect &rect)
Definition round_rect.h:19
static constexpr TRect MakeWH(Type width, Type height)
Definition rect.h:140
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
constexpr TRect TransformAndClipBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle, clipped against the near clippin...
Definition rect.h:438