| Flutter Impeller |

entity_pass_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/testing/testing.h"
8 #include "gtest/gtest.h"
9 #include "impeller/entity/contents/clip_contents.h"
10 #include "impeller/entity/entity.h"
11 #include "impeller/entity/entity_pass_clip_stack.h"
12
13 namespace impeller {
14 namespace testing {
15
16 TEST(EntityPassClipStackTest, CanPushAndPopEntities) {
17 EntityPassClipStack recorder =
18 EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100));
19
20 EXPECT_TRUE(recorder.GetReplayEntities().empty());
21
22 Entity entity;
23 recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kAppend,
24 Rect::MakeLTRB(0, 0, 100, 100));
25 EXPECT_EQ(recorder.GetReplayEntities().size(), 1u);
26
27 recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kAppend,
28 Rect::MakeLTRB(0, 0, 50, 50));
29 EXPECT_EQ(recorder.GetReplayEntities().size(), 2u);
30 ASSERT_TRUE(recorder.GetReplayEntities()[0].clip_coverage.has_value());
31 ASSERT_TRUE(recorder.GetReplayEntities()[1].clip_coverage.has_value());
32 // NOLINTBEGIN(bugprone-unchecked-optional-access)
33 EXPECT_EQ(recorder.GetReplayEntities()[0].clip_coverage.value(),
34 Rect::MakeLTRB(0, 0, 100, 100));
35 EXPECT_EQ(recorder.GetReplayEntities()[1].clip_coverage.value(),
36 Rect::MakeLTRB(0, 0, 50, 50));
37 // NOLINTEND(bugprone-unchecked-optional-access)
38
39 recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kRestore, Rect());
40 EXPECT_EQ(recorder.GetReplayEntities().size(), 1u);
41
42 recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kRestore, Rect());
43 EXPECT_TRUE(recorder.GetReplayEntities().empty());
44 }
45
46 TEST(EntityPassClipStackTest, CanPopEntitiesSafely) {
47 EntityPassClipStack recorder =
48 EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100));
49
50 EXPECT_TRUE(recorder.GetReplayEntities().empty());
51
52 Entity entity;
53 recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kRestore, Rect());
54 EXPECT_TRUE(recorder.GetReplayEntities().empty());
55 }
56
57 TEST(EntityPassClipStackTest, CanAppendNoChange) {
58 EntityPassClipStack recorder =
59 EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100));
60
61 EXPECT_TRUE(recorder.GetReplayEntities().empty());
62
63 Entity entity;
64 recorder.RecordEntity(entity, Contents::ClipCoverage::Type::kNoChange,
65 Rect());
66 EXPECT_TRUE(recorder.GetReplayEntities().empty());
67 }
68
69 TEST(EntityPassClipStackTest, AppendCoverageNoChange) {
70 EntityPassClipStack recorder =
71 EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100));
72
73 EXPECT_EQ(recorder.GetClipCoverageLayers()[0].coverage,
74 Rect::MakeSize(Size::MakeWH(100, 100)));
75 EXPECT_EQ(recorder.GetClipCoverageLayers()[0].clip_height, 0u);
76
77 Entity entity;
78 EntityPassClipStack::ClipStateResult result = recorder.ApplyClipState(
79 Contents::ClipCoverage{
80 .type = Contents::ClipCoverage::Type::kNoChange,
81 .coverage = std::nullopt,
82 },
83 entity, 0, Point(0, 0));
84 EXPECT_TRUE(result.should_render);
85 EXPECT_FALSE(result.clip_did_change);
86
87 EXPECT_EQ(recorder.GetClipCoverageLayers()[0].coverage,
88 Rect::MakeSize(Size::MakeWH(100, 100)));
89 EXPECT_EQ(recorder.GetClipCoverageLayers()[0].clip_height, 0u);
90 }
91
92 TEST(EntityPassClipStackTest, AppendAndRestoreClipCoverage) {
93 EntityPassClipStack recorder =
94 EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100));
95
96 ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u);
97
98 // Push a clip.
99 Entity entity;
100 EntityPassClipStack::ClipStateResult result = recorder.ApplyClipState(
101 Contents::ClipCoverage{
102 .type = Contents::ClipCoverage::Type::kAppend,
103 .coverage = Rect::MakeLTRB(50, 50, 55, 55),
104 },
105 entity, 0, Point(0, 0));
106 EXPECT_TRUE(result.should_render);
107 EXPECT_TRUE(result.clip_did_change);
108
109 ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 2u);
110 EXPECT_EQ(recorder.GetClipCoverageLayers()[1].coverage,
111 Rect::MakeLTRB(50, 50, 55, 55));
112 EXPECT_EQ(recorder.GetClipCoverageLayers()[1].clip_height, 1u);
113 EXPECT_EQ(recorder.GetReplayEntities().size(), 1u);
114
115 // Restore the clip.
116 auto restore_clip = std::make_shared<ClipRestoreContents>();
117 restore_clip->SetRestoreHeight(0);
118 entity.SetContents(std::move(restore_clip));
119 recorder.ApplyClipState(
120 Contents::ClipCoverage{
121 .type = Contents::ClipCoverage::Type::kRestore,
122 .coverage = Rect::MakeLTRB(50, 50, 55, 55),
123 },
124 entity, 0, Point(0, 0));
125
126 ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u);
127 EXPECT_EQ(recorder.GetClipCoverageLayers()[0].coverage,
128 Rect::MakeSize(Size::MakeWH(100, 100)));
129 EXPECT_EQ(recorder.GetClipCoverageLayers()[0].clip_height, 0u);
130 EXPECT_EQ(recorder.GetReplayEntities().size(), 0u);
131 }
132
133 // Append two clip coverages, the second is larger the first. This
134 // should result in the second clip not requiring any update.
135 TEST(EntityPassClipStackTest, AppendLargerClipCoverage) {
136 EntityPassClipStack recorder =
137 EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100));
138
139 ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u);
140
141 // Push a clip.
142 Entity entity;
143 EntityPassClipStack::ClipStateResult result = recorder.ApplyClipState(
144 Contents::ClipCoverage{
145 .type = Contents::ClipCoverage::Type::kAppend,
146 .coverage = Rect::MakeLTRB(50, 50, 55, 55),
147 },
148 entity, 0, Point(0, 0));
149 EXPECT_TRUE(result.should_render);
150 EXPECT_TRUE(result.clip_did_change);
151
152 // Push a clip with larger coverage than the previous state.
153 result = recorder.ApplyClipState(
154 Contents::ClipCoverage{
155 .type = Contents::ClipCoverage::Type::kAppend,
156 .coverage = Rect::MakeLTRB(0, 0, 100, 100),
157 },
158 entity, 0, Point(0, 0));
159
160 EXPECT_FALSE(result.should_render);
161 EXPECT_FALSE(result.clip_did_change);
162 }
163
164 // Since clip entities return the outer coverage we can only cull axis aligned
165 // rectangles and intersect clips.
166 TEST(EntityPassClipStackTest,
167 AppendLargerClipCoverageWithDifferenceOrNonSquare) {
168 EntityPassClipStack recorder =
169 EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100));
170
171 ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u);
172
173 // Push a clip.
174 Entity entity;
175 EntityPassClipStack::ClipStateResult result = recorder.ApplyClipState(
176 Contents::ClipCoverage{
177 .type = Contents::ClipCoverage::Type::kAppend,
178 .coverage = Rect::MakeLTRB(50, 50, 55, 55),
179 },
180 entity, 0, Point(0, 0));
181 EXPECT_TRUE(result.should_render);
182 EXPECT_TRUE(result.clip_did_change);
183
184 // Push a clip with larger coverage than the previous state.
185 result = recorder.ApplyClipState(
186 Contents::ClipCoverage{
187 .type = Contents::ClipCoverage::Type::kAppend,
188 .is_difference_or_non_square = true,
189 .coverage = Rect::MakeLTRB(0, 0, 100, 100),
190 },
191 entity, 0, Point(0, 0));
192
193 EXPECT_TRUE(result.should_render);
194 EXPECT_TRUE(result.clip_did_change);
195 }
196
197 TEST(EntityPassClipStackTest, AppendDecreasingSizeClipCoverage) {
198 EntityPassClipStack recorder =
199 EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100));
200
201 ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u);
202
203 // Push Clips that shrink in size. All should be applied.
204 Entity entity;
205
206 for (auto i = 1; i < 20; i++) {
207 EntityPassClipStack::ClipStateResult result = recorder.ApplyClipState(
208 Contents::ClipCoverage{
209 .type = Contents::ClipCoverage::Type::kAppend,
210 .coverage = Rect::MakeLTRB(i, i, 100 - i, 100 - i),
211 },
212 entity, 0, Point(0, 0));
213 EXPECT_TRUE(result.should_render);
214 EXPECT_TRUE(result.clip_did_change);
215 EXPECT_EQ(recorder.CurrentClipCoverage(),
216 Rect::MakeLTRB(i, i, 100 - i, 100 - i));
217 }
218 }
219
220 TEST(EntityPassClipStackTest, AppendIncreasingSizeClipCoverage) {
221 EntityPassClipStack recorder =
222 EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100));
223
224 ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u);
225
226 // Push Clips that grow in size. All should be skipped.
227 Entity entity;
228
229 for (auto i = 1; i < 20; i++) {
230 EntityPassClipStack::ClipStateResult result = recorder.ApplyClipState(
231 Contents::ClipCoverage{
232 .type = Contents::ClipCoverage::Type::kAppend,
233 .coverage = Rect::MakeLTRB(0 - i, 0 - i, 100 + i, 100 + i),
234 },
235 entity, 0, Point(0, 0));
236 EXPECT_FALSE(result.should_render);
237 EXPECT_FALSE(result.clip_did_change);
238 EXPECT_EQ(recorder.CurrentClipCoverage(), Rect::MakeLTRB(0, 0, 100, 100));
239 }
240 }
241
242 TEST(EntityPassClipStackTest, UnbalancedRestore) {
243 EntityPassClipStack recorder =
244 EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100));
245
246 ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u);
247
248 // Restore the clip.
249 Entity entity;
250 auto restore_clip = std::make_shared<ClipRestoreContents>();
251 restore_clip->SetRestoreHeight(0);
252 entity.SetContents(std::move(restore_clip));
253 EntityPassClipStack::ClipStateResult result = recorder.ApplyClipState(
254 Contents::ClipCoverage{
255 .type = Contents::ClipCoverage::Type::kRestore,
256 .coverage = Rect::MakeLTRB(50, 50, 55, 55),
257 },
258 entity, 0, Point(0, 0));
259 EXPECT_FALSE(result.should_render);
260 EXPECT_FALSE(result.clip_did_change);
261
262 ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u);
263 EXPECT_EQ(recorder.GetClipCoverageLayers()[0].coverage,
264 Rect::MakeSize(Size::MakeWH(100, 100)));
265 EXPECT_EQ(recorder.GetClipCoverageLayers()[0].clip_height, 0u);
266 EXPECT_EQ(recorder.GetReplayEntities().size(), 0u);
267 }
268
269 TEST(EntityPassClipStackTest, ClipAndRestoreWithSubpasses) {
270 EntityPassClipStack recorder =
271 EntityPassClipStack(Rect::MakeLTRB(0, 0, 100, 100));
272
273 ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u);
274
275 // Push a clip.
276 Entity entity;
277 {
278 EntityPassClipStack::ClipStateResult result = recorder.ApplyClipState(
279 Contents::ClipCoverage{
280 .type = Contents::ClipCoverage::Type::kAppend,
281 .coverage = Rect::MakeLTRB(50, 50, 55, 55),
282 },
283 entity, 0, Point(0, 0));
284 EXPECT_TRUE(result.should_render);
285 EXPECT_TRUE(result.clip_did_change);
286 }
287
288 ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 2u);
289 EXPECT_EQ(recorder.GetClipCoverageLayers()[1].coverage,
290 Rect::MakeLTRB(50, 50, 55, 55));
291 EXPECT_EQ(recorder.GetClipCoverageLayers()[1].clip_height, 1u);
292 EXPECT_EQ(recorder.GetReplayEntities().size(), 1u);
293
294 // Begin a subpass.
295 recorder.PushSubpass(Rect::MakeLTRB(50, 50, 55, 55), 1);
296 ASSERT_EQ(recorder.GetClipCoverageLayers().size(), 1u);
297 EXPECT_EQ(recorder.GetClipCoverageLayers()[0].coverage,
298 Rect::MakeLTRB(50, 50, 55, 55));
299
300 {
301 EntityPassClipStack::ClipStateResult result = recorder.ApplyClipState(
302 Contents::ClipCoverage{
303 .type = Contents::ClipCoverage::Type::kAppend,
304 .coverage = Rect::MakeLTRB(54, 54, 55, 55),
305 },
306 entity, 0, Point(0, 0));
307 EXPECT_TRUE(result.should_render);
308 EXPECT_TRUE(result.clip_did_change);
309 }
310
311 EXPECT_EQ(recorder.GetClipCoverageLayers()[1].coverage,
312 Rect::MakeLTRB(54, 54, 55, 55));
313
314 // End subpass.
315 recorder.PopSubpass();
316
317 EXPECT_EQ(recorder.GetClipCoverageLayers()[1].coverage,
318 Rect::MakeLTRB(50, 50, 55, 55));
319 }
320
321 } // namespace testing
322 } // namespace impeller
impeller::EntityPassClipStack::ClipStateResult::should_render
bool should_render
Definition: entity_pass_clip_stack.h:34
impeller::EntityPassClipStack::PushSubpass
void PushSubpass(std::optional< Rect > subpass_coverage, size_t clip_height)
Definition: entity_pass_clip_stack.cc:33
entity.h
impeller::EntityPassClipStack::GetClipCoverageLayers
const std::vector< ClipCoverageLayer > GetClipCoverageLayers() const
Definition: entity_pass_clip_stack.cc:51
impeller::Contents::ClipCoverage::Type::kRestore
@ kRestore
impeller::Entity::SetContents
void SetContents(std::shared_ptr< Contents > contents)
Definition: entity.cc:83
impeller::Entity
Definition: entity.h:20
impeller::EntityPassClipStack::ApplyClipState
ClipStateResult ApplyClipState(Contents::ClipCoverage global_clip_coverage, Entity &entity, size_t clip_height_floor, Point global_pass_position)
Applies the current clip state to an Entity. If the given Entity is a clip operation,...
Definition: entity_pass_clip_stack.cc:55
impeller::EntityPassClipStack::GetReplayEntities
const std::vector< ReplayResult > & GetReplayEntities() const
Definition: entity_pass_clip_stack.cc:185
impeller::Point
TPoint< Scalar > Point
Definition: point.h:327
impeller::EntityPassClipStack::CurrentClipCoverage
std::optional< Rect > CurrentClipCoverage() const
Definition: entity_pass_clip_stack.cc:25
impeller::Contents::ClipCoverage::type
Type type
Definition: contents.h:40
clip_contents.h
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:776
impeller::EntityPassClipStack::PopSubpass
void PopSubpass()
Definition: entity_pass_clip_stack.cc:45
entity_pass_clip_stack.h
impeller::EntityPassClipStack::RecordEntity
void RecordEntity(const Entity &entity, Contents::ClipCoverage::Type type, std::optional< Rect > clip_coverage)
Definition: entity_pass_clip_stack.cc:150
impeller::EntityPassClipStack::ClipStateResult::clip_did_change
bool clip_did_change
Definition: entity_pass_clip_stack.h:37
impeller::TRect< Scalar >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:150
impeller::Contents::ClipCoverage
Definition: contents.h:37
impeller::Contents::ClipCoverage::Type::kAppend
@ kAppend
impeller::EntityPassClipStack
A class that tracks all clips that have been recorded in the current entity pass stencil.
Definition: entity_pass_clip_stack.h:24
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
impeller::TSize< Scalar >::MakeWH
static constexpr TSize MakeWH(Type width, Type height)
Definition: size.h:34
impeller::EntityPassClipStack::ClipStateResult
Definition: entity_pass_clip_stack.h:31
impeller
Definition: allocation.cc:12
impeller::testing::TEST
TEST(AllocationSizeTest, CanCreateTypedAllocations)
Definition: allocation_size_unittests.cc:10
impeller::Contents::ClipCoverage::Type::kNoChange
@ kNoChange