1 // Copyright 2015 The Chromium 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 "third_party/blink/renderer/platform/graphics/paint/paint_chunker.h"
6
7 #include "testing/gmock/include/gmock/gmock.h"
8 #include "testing/gtest/include/gtest/gtest.h"
9 #include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
10 #include "third_party/blink/renderer/platform/graphics/paint/foreign_layer_display_item.h"
11 #include "third_party/blink/renderer/platform/graphics/paint/paint_controller_test.h"
12 #include "third_party/blink/renderer/platform/testing/fake_display_item_client.h"
13 #include "third_party/blink/renderer/platform/testing/paint_property_test_helpers.h"
14 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
15
16 using testing::ElementsAre;
17
18 namespace blink {
19
20 namespace {
21
22 class PaintChunkerTest : public testing::Test {
23 protected:
24 FakeDisplayItemClient client_;
25 };
26
DisplayItemType(int offset)27 DisplayItem::Type DisplayItemType(int offset) {
28 auto type =
29 static_cast<DisplayItem::Type>(DisplayItem::kDrawingFirst + offset);
30 DCHECK(DisplayItem::IsDrawingType(type));
31 return type;
32 }
33
34 class TestChunkerDisplayItem : public DrawingDisplayItem {
35 public:
TestChunkerDisplayItem(const DisplayItemClient & client,DisplayItem::Type type=DisplayItem::kDrawingFirst)36 explicit TestChunkerDisplayItem(
37 const DisplayItemClient& client,
38 DisplayItem::Type type = DisplayItem::kDrawingFirst)
39 : DrawingDisplayItem(client, type, nullptr) {}
40 };
41
42 class TestChunkerOpaqueDisplayItem : public DrawingDisplayItem {
43 public:
TestChunkerOpaqueDisplayItem(const DisplayItemClient & client,DisplayItem::Type type=DisplayItem::kDrawingFirst)44 explicit TestChunkerOpaqueDisplayItem(
45 const DisplayItemClient& client,
46 DisplayItem::Type type = DisplayItem::kDrawingFirst)
47 : DrawingDisplayItem(client, type, nullptr) {
48 SetKnownToBeOpaqueForTesting();
49 }
50 };
51
52 class TestDisplayItemRequiringSeparateChunk : public ForeignLayerDisplayItem {
53 public:
TestDisplayItemRequiringSeparateChunk(const DisplayItemClient & client)54 explicit TestDisplayItemRequiringSeparateChunk(
55 const DisplayItemClient& client)
56 : ForeignLayerDisplayItem(client,
57 DisplayItem::kForeignLayerPlugin,
58 cc::Layer::Create(),
59 FloatPoint(),
60 nullptr) {}
61 };
62
TEST_F(PaintChunkerTest,Empty)63 TEST_F(PaintChunkerTest, Empty) {
64 PaintChunker chunker;
65 EXPECT_TRUE(chunker.PaintChunks().IsEmpty());
66
67 auto chunks = chunker.ReleasePaintChunks();
68 EXPECT_TRUE(chunks.IsEmpty());
69 }
70
TEST_F(PaintChunkerTest,SingleNonEmptyRange)71 TEST_F(PaintChunkerTest, SingleNonEmptyRange) {
72 PaintChunker chunker;
73 PaintChunk::Id id(client_, DisplayItemType(1));
74 chunker.UpdateCurrentPaintChunkProperties(&id, DefaultPaintChunkProperties());
75 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
76 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
77
78 const auto& chunks = chunker.PaintChunks();
79 EXPECT_THAT(chunks, ElementsAre(IsPaintChunk(0, 2, id,
80 DefaultPaintChunkProperties())));
81
82 auto chunks1 = chunker.ReleasePaintChunks();
83 EXPECT_TRUE(chunker.PaintChunks().IsEmpty());
84 EXPECT_THAT(chunks1, ElementsAre(IsPaintChunk(
85 0, 2, id, DefaultPaintChunkProperties())));
86 }
87
TEST_F(PaintChunkerTest,SamePropertiesTwiceCombineIntoOneChunk)88 TEST_F(PaintChunkerTest, SamePropertiesTwiceCombineIntoOneChunk) {
89 PaintChunker chunker;
90 PaintChunk::Id id(client_, DisplayItemType(1));
91 chunker.UpdateCurrentPaintChunkProperties(&id, DefaultPaintChunkProperties());
92 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
93 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
94 chunker.UpdateCurrentPaintChunkProperties(&id, DefaultPaintChunkProperties());
95 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
96
97 const auto& chunks = chunker.PaintChunks();
98 EXPECT_THAT(chunks, ElementsAre(IsPaintChunk(0, 3, id,
99 DefaultPaintChunkProperties())));
100
101 auto chunks1 = chunker.ReleasePaintChunks();
102 EXPECT_TRUE(chunker.PaintChunks().IsEmpty());
103 EXPECT_THAT(chunks1, ElementsAre(IsPaintChunk(
104 0, 3, id, DefaultPaintChunkProperties())));
105 }
106
TEST_F(PaintChunkerTest,BuildMultipleChunksWithSinglePropertyChanging)107 TEST_F(PaintChunkerTest, BuildMultipleChunksWithSinglePropertyChanging) {
108 PaintChunker chunker;
109 PaintChunk::Id id1(client_, DisplayItemType(1));
110 chunker.UpdateCurrentPaintChunkProperties(&id1,
111 DefaultPaintChunkProperties());
112 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
113 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
114
115 auto simple_transform_node = CreateTransform(
116 t0(), TransformationMatrix(0, 1, 2, 3, 4, 5), FloatPoint3D(9, 8, 7));
117 auto simple_transform = DefaultPaintChunkProperties();
118 simple_transform.SetTransform(*simple_transform_node);
119
120 PaintChunk::Id id2(client_, DisplayItemType(2));
121 chunker.UpdateCurrentPaintChunkProperties(&id2, simple_transform);
122 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
123
124 auto another_transform_node = CreateTransform(
125 t0(), TransformationMatrix(0, 1, 2, 3, 4, 5), FloatPoint3D(9, 8, 7));
126 auto another_transform = DefaultPaintChunkProperties();
127 another_transform.SetTransform(*another_transform_node);
128 PaintChunk::Id id3(client_, DisplayItemType(3));
129 chunker.UpdateCurrentPaintChunkProperties(&id3, another_transform);
130 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
131
132 EXPECT_THAT(
133 chunker.PaintChunks(),
134 ElementsAre(IsPaintChunk(0, 2, id1, DefaultPaintChunkProperties()),
135 IsPaintChunk(2, 3, id2, simple_transform),
136 IsPaintChunk(3, 4, id3, another_transform)));
137 }
138
TEST_F(PaintChunkerTest,BuildMultipleChunksWithDifferentPropertyChanges)139 TEST_F(PaintChunkerTest, BuildMultipleChunksWithDifferentPropertyChanges) {
140 PaintChunker chunker;
141 PaintChunk::Id id1(client_, DisplayItemType(1));
142 chunker.UpdateCurrentPaintChunkProperties(&id1,
143 DefaultPaintChunkProperties());
144 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
145
146 auto simple_transform_node = CreateTransform(
147 t0(), TransformationMatrix(0, 0, 0, 0, 0, 0), FloatPoint3D(9, 8, 7));
148 auto simple_transform = DefaultPaintChunkProperties();
149 simple_transform.SetTransform(*simple_transform_node);
150 PaintChunk::Id id2(client_, DisplayItemType(2));
151 chunker.UpdateCurrentPaintChunkProperties(&id2, simple_transform);
152 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
153 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
154
155 auto simple_effect_node = CreateOpacityEffect(e0(), 0.5f);
156 auto simple_transform_and_effect = DefaultPaintChunkProperties();
157 simple_transform_and_effect.SetTransform(*simple_transform_node);
158 simple_transform_and_effect.SetEffect(*simple_effect_node);
159 PaintChunk::Id id3(client_, DisplayItemType(3));
160 chunker.UpdateCurrentPaintChunkProperties(&id3, simple_transform_and_effect);
161 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
162 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
163
164 auto new_transform_node = CreateTransform(
165 t0(), TransformationMatrix(1, 1, 0, 0, 0, 0), FloatPoint3D(9, 8, 7));
166 auto simple_transform_and_effect_with_updated_transform =
167 DefaultPaintChunkProperties();
168 auto new_effect_node = CreateOpacityEffect(e0(), 0.5f);
169 simple_transform_and_effect_with_updated_transform.SetTransform(
170 *new_transform_node);
171 simple_transform_and_effect_with_updated_transform.SetEffect(
172 *new_effect_node);
173 PaintChunk::Id id4(client_, DisplayItemType(4));
174 chunker.UpdateCurrentPaintChunkProperties(
175 &id4, simple_transform_and_effect_with_updated_transform);
176 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
177 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
178
179 // Test that going back to a previous chunk property still creates a new
180 // chunk.
181 chunker.UpdateCurrentPaintChunkProperties(nullptr,
182 simple_transform_and_effect);
183 TestChunkerDisplayItem item_after_restore(client_, DisplayItemType(10));
184 chunker.IncrementDisplayItemIndex(item_after_restore);
185 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
186
187 EXPECT_THAT(
188 chunker.PaintChunks(),
189 ElementsAre(
190 IsPaintChunk(0, 1, id1, DefaultPaintChunkProperties()),
191 IsPaintChunk(1, 3, id2, simple_transform),
192 IsPaintChunk(3, 5, id3, simple_transform_and_effect),
193 IsPaintChunk(5, 7, id4,
194 simple_transform_and_effect_with_updated_transform),
195 IsPaintChunk(7, 9, item_after_restore.GetId(),
196 simple_transform_and_effect)));
197 }
198
TEST_F(PaintChunkerTest,BuildChunksFromNestedTransforms)199 TEST_F(PaintChunkerTest, BuildChunksFromNestedTransforms) {
200 // Test that "nested" transforms linearize using the following
201 // sequence of transforms and display items:
202 // <root xform>
203 // <paint>
204 // <a xform>
205 // <paint><paint>
206 // </a xform>
207 // <paint>
208 // </root xform>
209 PaintChunker chunker;
210 PaintChunk::Id id1(client_, DisplayItemType(1));
211 chunker.UpdateCurrentPaintChunkProperties(&id1,
212 DefaultPaintChunkProperties());
213 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
214
215 auto simple_transform_node = CreateTransform(
216 t0(), TransformationMatrix(0, 1, 2, 3, 4, 5), FloatPoint3D(9, 8, 7));
217 auto simple_transform = DefaultPaintChunkProperties();
218 simple_transform.SetTransform(*simple_transform_node);
219 PaintChunk::Id id2(client_, DisplayItemType(2));
220 chunker.UpdateCurrentPaintChunkProperties(&id2, simple_transform);
221 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
222 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
223
224 chunker.UpdateCurrentPaintChunkProperties(nullptr,
225 DefaultPaintChunkProperties());
226 TestChunkerDisplayItem item_after_restore(client_, DisplayItemType(10));
227 chunker.IncrementDisplayItemIndex(item_after_restore);
228
229 EXPECT_THAT(
230 chunker.PaintChunks(),
231 ElementsAre(IsPaintChunk(0, 1, id1, DefaultPaintChunkProperties()),
232 IsPaintChunk(1, 3, id2, simple_transform),
233 IsPaintChunk(3, 4, item_after_restore.GetId(),
234 DefaultPaintChunkProperties())));
235 }
236
TEST_F(PaintChunkerTest,ChangingPropertiesWithoutItems)237 TEST_F(PaintChunkerTest, ChangingPropertiesWithoutItems) {
238 // Test that properties can change without display items being generated.
239 PaintChunker chunker;
240 PaintChunk::Id id1(client_, DisplayItemType(1));
241 chunker.UpdateCurrentPaintChunkProperties(&id1,
242 DefaultPaintChunkProperties());
243 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
244
245 auto first_transform_node = CreateTransform(
246 t0(), TransformationMatrix(0, 1, 2, 3, 4, 5), FloatPoint3D(9, 8, 7));
247 auto first_transform = DefaultPaintChunkProperties();
248 first_transform.SetTransform(*first_transform_node);
249 PaintChunk::Id id2(client_, DisplayItemType(2));
250 chunker.UpdateCurrentPaintChunkProperties(nullptr, first_transform);
251
252 auto second_transform_node = CreateTransform(
253 t0(), TransformationMatrix(9, 8, 7, 6, 5, 4), FloatPoint3D(3, 2, 1));
254 auto second_transform = DefaultPaintChunkProperties();
255 second_transform.SetTransform(*second_transform_node);
256 PaintChunk::Id id3(client_, DisplayItemType(3));
257 chunker.UpdateCurrentPaintChunkProperties(&id3, second_transform);
258
259 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
260 EXPECT_THAT(
261 chunker.PaintChunks(),
262 ElementsAre(IsPaintChunk(0, 1, id1, DefaultPaintChunkProperties()),
263 IsPaintChunk(1, 2, id3, second_transform)));
264 }
265
TEST_F(PaintChunkerTest,CreatesSeparateChunksWhenRequested)266 TEST_F(PaintChunkerTest, CreatesSeparateChunksWhenRequested) {
267 // Tests that the chunker creates a separate chunks for display items which
268 // require it.
269 PaintChunker chunker;
270 FakeDisplayItemClient client1;
271 TestDisplayItemRequiringSeparateChunk i1(client1);
272 FakeDisplayItemClient client2;
273 TestDisplayItemRequiringSeparateChunk i2(client2);
274 FakeDisplayItemClient client3;
275 TestDisplayItemRequiringSeparateChunk i3(client3);
276
277 PaintChunk::Id id0(client_, DisplayItemType(0));
278 chunker.UpdateCurrentPaintChunkProperties(&id0,
279 DefaultPaintChunkProperties());
280 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
281 chunker.IncrementDisplayItemIndex(i1);
282 chunker.IncrementDisplayItemIndex(i2);
283 TestChunkerDisplayItem after_i2(client_, DisplayItemType(10));
284 chunker.IncrementDisplayItemIndex(after_i2);
285 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
286 chunker.UpdateCurrentPaintChunkProperties(&id0,
287 DefaultPaintChunkProperties());
288 chunker.IncrementDisplayItemIndex(i3);
289
290 EXPECT_THAT(
291 chunker.PaintChunks(),
292 ElementsAre(
293 IsPaintChunk(0, 1, id0, DefaultPaintChunkProperties()),
294 IsPaintChunk(1, 2, i1.GetId(), DefaultPaintChunkProperties()),
295 IsPaintChunk(2, 3, i2.GetId(), DefaultPaintChunkProperties()),
296 IsPaintChunk(3, 5, after_i2.GetId(), DefaultPaintChunkProperties()),
297 IsPaintChunk(5, 6, i3.GetId(), DefaultPaintChunkProperties())));
298 }
299
TEST_F(PaintChunkerTest,ForceNewChunkWithNewId)300 TEST_F(PaintChunkerTest, ForceNewChunkWithNewId) {
301 PaintChunker chunker;
302 PaintChunk::Id id0(client_, DisplayItemType(0));
303 chunker.UpdateCurrentPaintChunkProperties(&id0,
304 DefaultPaintChunkProperties());
305 EXPECT_TRUE(chunker.WillForceNewChunk());
306 EXPECT_EQ(0u, chunker.size());
307 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
308 EXPECT_FALSE(chunker.WillForceNewChunk());
309 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
310 EXPECT_EQ(1u, chunker.size());
311
312 chunker.SetForceNewChunk(true);
313 EXPECT_TRUE(chunker.WillForceNewChunk());
314 EXPECT_EQ(1u, chunker.size());
315 PaintChunk::Id id1(client_, DisplayItemType(1));
316 chunker.UpdateCurrentPaintChunkProperties(&id1,
317 DefaultPaintChunkProperties());
318 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
319 EXPECT_EQ(2u, chunker.size());
320 EXPECT_FALSE(chunker.WillForceNewChunk());
321 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
322 EXPECT_EQ(2u, chunker.size());
323
324 chunker.SetForceNewChunk(true);
325 PaintChunk::Id id2(client_, DisplayItemType(2));
326 EXPECT_TRUE(chunker.WillForceNewChunk());
327 chunker.UpdateCurrentPaintChunkProperties(&id2,
328 DefaultPaintChunkProperties());
329 EXPECT_EQ(2u, chunker.size());
330 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
331 EXPECT_EQ(3u, chunker.size());
332 EXPECT_FALSE(chunker.WillForceNewChunk());
333 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
334
335 EXPECT_THAT(
336 chunker.PaintChunks(),
337 ElementsAre(IsPaintChunk(0, 2, id0, DefaultPaintChunkProperties()),
338 IsPaintChunk(2, 4, id1, DefaultPaintChunkProperties()),
339 IsPaintChunk(4, 6, id2, DefaultPaintChunkProperties())));
340 }
341
TEST_F(PaintChunkerTest,ForceNewChunkWithoutNewId)342 TEST_F(PaintChunkerTest, ForceNewChunkWithoutNewId) {
343 PaintChunker chunker;
344 PaintChunk::Id id0(client_, DisplayItemType(0));
345 chunker.UpdateCurrentPaintChunkProperties(nullptr,
346 DefaultPaintChunkProperties());
347 EXPECT_TRUE(chunker.WillForceNewChunk());
348 EXPECT_EQ(0u, chunker.size());
349 chunker.IncrementDisplayItemIndex(
350 TestChunkerDisplayItem(id0.client, id0.type));
351 EXPECT_FALSE(chunker.WillForceNewChunk());
352 EXPECT_EQ(1u, chunker.size());
353 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
354
355 chunker.SetForceNewChunk(true);
356 EXPECT_TRUE(chunker.WillForceNewChunk());
357 EXPECT_EQ(1u, chunker.size());
358 PaintChunk::Id id1(client_, DisplayItemType(1));
359 chunker.IncrementDisplayItemIndex(
360 TestChunkerDisplayItem(id1.client, id1.type));
361 EXPECT_FALSE(chunker.WillForceNewChunk());
362 EXPECT_EQ(2u, chunker.size());
363 chunker.IncrementDisplayItemIndex(
364 TestChunkerDisplayItem(client_, DisplayItemType(2)));
365
366 chunker.SetForceNewChunk(true);
367 EXPECT_TRUE(chunker.WillForceNewChunk());
368 EXPECT_EQ(2u, chunker.size());
369 PaintChunk::Id id2(client_, DisplayItemType(3));
370 chunker.IncrementDisplayItemIndex(
371 TestChunkerDisplayItem(id2.client, id2.type));
372 EXPECT_FALSE(chunker.WillForceNewChunk());
373 EXPECT_EQ(3u, chunker.size());
374 chunker.IncrementDisplayItemIndex(
375 TestChunkerDisplayItem(client_, DisplayItemType(4)));
376
377 EXPECT_THAT(
378 chunker.PaintChunks(),
379 ElementsAre(IsPaintChunk(0, 2, id0, DefaultPaintChunkProperties()),
380 IsPaintChunk(2, 4, id1, DefaultPaintChunkProperties()),
381 IsPaintChunk(4, 6, id2, DefaultPaintChunkProperties())));
382 }
383
TEST_F(PaintChunkerTest,SetAndImmediatelyUnsetForceNewChunk)384 TEST_F(PaintChunkerTest, SetAndImmediatelyUnsetForceNewChunk) {
385 PaintChunker chunker;
386 PaintChunk::Id id0(client_, DisplayItemType(0));
387 chunker.UpdateCurrentPaintChunkProperties(nullptr,
388 DefaultPaintChunkProperties());
389 EXPECT_TRUE(chunker.WillForceNewChunk());
390 EXPECT_EQ(0u, chunker.size());
391 chunker.IncrementDisplayItemIndex(
392 TestChunkerDisplayItem(id0.client, id0.type));
393 EXPECT_FALSE(chunker.WillForceNewChunk());
394 EXPECT_EQ(1u, chunker.size());
395 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
396
397 // This should not force a new chunk. Simulates a ScopedPaintChunkHint
398 // without any painting in the scope.
399 chunker.SetForceNewChunk(true);
400 chunker.SetForceNewChunk(false);
401 EXPECT_FALSE(chunker.WillForceNewChunk());
402 chunker.IncrementDisplayItemIndex(
403 TestChunkerDisplayItem(client_, DisplayItemType(1)));
404 EXPECT_EQ(1u, chunker.size());
405
406 EXPECT_THAT(
407 chunker.PaintChunks(),
408 ElementsAre(IsPaintChunk(0, 3, id0, DefaultPaintChunkProperties())));
409 }
410
TEST_F(PaintChunkerTest,NoNewChunkForSamePropertyDifferentIds)411 TEST_F(PaintChunkerTest, NoNewChunkForSamePropertyDifferentIds) {
412 PaintChunker chunker;
413 PaintChunk::Id id0(client_, DisplayItemType(0));
414 chunker.UpdateCurrentPaintChunkProperties(&id0,
415 DefaultPaintChunkProperties());
416 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
417 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
418
419 PaintChunk::Id id1(client_, DisplayItemType(1));
420 chunker.UpdateCurrentPaintChunkProperties(&id1,
421 DefaultPaintChunkProperties());
422 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
423 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
424
425 chunker.UpdateCurrentPaintChunkProperties(nullptr,
426 DefaultPaintChunkProperties());
427 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
428 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
429
430 EXPECT_THAT(
431 chunker.PaintChunks(),
432 ElementsAre(IsPaintChunk(0, 6, id0, DefaultPaintChunkProperties())));
433 }
434
435 // Ensure that items following a forced chunk begin using the next display
436 // item's id.
TEST_F(PaintChunkerTest,ChunksFollowingForcedChunk)437 TEST_F(PaintChunkerTest, ChunksFollowingForcedChunk) {
438 PaintChunker chunker;
439 FakeDisplayItemClient client;
440 TestChunkerDisplayItem before_forced1(client, DisplayItemType(1));
441 TestChunkerDisplayItem before_forced2(client, DisplayItemType(2));
442 TestDisplayItemRequiringSeparateChunk forced(client);
443 TestChunkerDisplayItem after_forced1(client, DisplayItemType(3));
444 TestChunkerDisplayItem after_forced2(client, DisplayItemType(4));
445
446 PaintChunk::Id id0(client, DisplayItemType(5));
447 chunker.UpdateCurrentPaintChunkProperties(&id0,
448 DefaultPaintChunkProperties());
449 // Both before_forced items should be in a chunk together.
450 chunker.IncrementDisplayItemIndex(before_forced1);
451 chunker.IncrementDisplayItemIndex(before_forced2);
452 // |forced| forces a dedicted paint chunk.
453 chunker.IncrementDisplayItemIndex(forced);
454 // Both after_forced items should be in a chunk together.
455 chunker.IncrementDisplayItemIndex(after_forced1);
456 chunker.IncrementDisplayItemIndex(after_forced2);
457
458 EXPECT_THAT(
459 chunker.PaintChunks(),
460 ElementsAre(
461 IsPaintChunk(0, 2, id0, DefaultPaintChunkProperties()),
462 IsPaintChunk(2, 3, forced.GetId(), DefaultPaintChunkProperties()),
463 IsPaintChunk(3, 5, after_forced1.GetId(),
464 DefaultPaintChunkProperties())));
465 }
466
TEST_F(PaintChunkerTest,ChunkIdsSkippingCache)467 TEST_F(PaintChunkerTest, ChunkIdsSkippingCache) {
468 PaintChunker chunker;
469
470 PaintChunk::Id id1(client_, DisplayItemType(1));
471 chunker.UpdateCurrentPaintChunkProperties(&id1,
472 DefaultPaintChunkProperties());
473 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
474 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
475
476 auto simple_transform_node = CreateTransform(
477 t0(), TransformationMatrix(0, 1, 2, 3, 4, 5), FloatPoint3D(9, 8, 7));
478 auto simple_transform = DefaultPaintChunkProperties();
479 simple_transform.SetTransform(*simple_transform_node);
480
481 FakeDisplayItemClient uncacheable_client;
482 uncacheable_client.Invalidate(PaintInvalidationReason::kUncacheable);
483 PaintChunk::Id id2(uncacheable_client, DisplayItemType(2));
484 chunker.UpdateCurrentPaintChunkProperties(&id2, simple_transform);
485
486 TestChunkerDisplayItem uncacheable_item(uncacheable_client);
487 chunker.IncrementDisplayItemIndex(uncacheable_item);
488 chunker.IncrementDisplayItemIndex(TestChunkerDisplayItem(client_));
489
490 TestDisplayItemRequiringSeparateChunk uncacheable_separate_chunk_item(
491 uncacheable_client);
492 chunker.IncrementDisplayItemIndex(uncacheable_separate_chunk_item);
493
494 TestChunkerDisplayItem after_separate_chunk(client_, DisplayItemType(3));
495 chunker.IncrementDisplayItemIndex(after_separate_chunk);
496
497 chunker.UpdateCurrentPaintChunkProperties(nullptr,
498 DefaultPaintChunkProperties());
499 TestChunkerDisplayItem after_restore(client_, DisplayItemType(4));
500 chunker.IncrementDisplayItemIndex(after_restore);
501
502 const auto& chunks = chunker.PaintChunks();
503 EXPECT_THAT(
504 chunks,
505 ElementsAre(
506 IsPaintChunk(0, 2, id1, DefaultPaintChunkProperties()),
507 IsPaintChunk(2, 4, id2, simple_transform),
508 IsPaintChunk(4, 5, uncacheable_separate_chunk_item.GetId(),
509 simple_transform),
510 IsPaintChunk(5, 6, after_separate_chunk.GetId(), simple_transform),
511 IsPaintChunk(6, 7, after_restore.GetId(),
512 DefaultPaintChunkProperties())));
513 EXPECT_TRUE(chunks[0].is_cacheable);
514 EXPECT_FALSE(chunks[1].is_cacheable);
515 EXPECT_FALSE(chunks[2].is_cacheable);
516 EXPECT_TRUE(chunks[3].is_cacheable);
517 EXPECT_TRUE(chunks[4].is_cacheable);
518 }
519
TEST_F(PaintChunkerTest,AddHitTestDataToCurrentChunk)520 TEST_F(PaintChunkerTest, AddHitTestDataToCurrentChunk) {
521 PaintChunker chunker;
522 client_.SetVisualRect(IntRect(0, 0, 10, 10));
523
524 PaintChunk::Id id1(client_, DisplayItemType(1));
525
526 chunker.UpdateCurrentPaintChunkProperties(&id1,
527 DefaultPaintChunkProperties());
528 chunker.IncrementDisplayItemIndex(
529 TestChunkerDisplayItem(client_, DisplayItemType(2)));
530
531 PaintChunk::Id id2(client_, DisplayItemType(3));
532 auto transform = Create2DTranslation(t0(), 10, 20);
533 PropertyTreeState properties(*transform, c0(), e0());
534 chunker.UpdateCurrentPaintChunkProperties(&id2, properties);
535 // This is not used as id of the chunk because we already have |id2|.
536 PaintChunk::Id hit_test_id(client_, DisplayItem::kHitTest);
537 chunker.AddHitTestDataToCurrentChunk(hit_test_id, IntRect(10, 20, 30, 40),
538 TouchAction::kAuto);
539 chunker.AddHitTestDataToCurrentChunk(hit_test_id, IntRect(20, 30, 40, 50),
540 TouchAction::kPan);
541
542 chunker.SetForceNewChunk(true);
543 PaintChunk::Id id3(client_, DisplayItemType(4));
544 chunker.AddHitTestDataToCurrentChunk(id3, IntRect(40, 50, 60, 70),
545 TouchAction::kAuto);
546 chunker.IncrementDisplayItemIndex(
547 TestChunkerDisplayItem(client_, DisplayItemType(5)));
548
549 HitTestData hit_test_data;
550 hit_test_data.touch_action_rects = {
551 {IntRect(20, 30, 40, 50), TouchAction::kPan}};
552 if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
553 EXPECT_THAT(
554 chunker.PaintChunks(),
555 ElementsAre(IsPaintChunk(0, 1, id1, DefaultPaintChunkProperties(),
556 nullptr, IntRect(0, 0, 10, 10)),
557 IsPaintChunk(1, 1, id2, properties, &hit_test_data,
558 IntRect(10, 20, 50, 60)),
559 IsPaintChunk(1, 2, id3, properties, nullptr,
560 IntRect(0, 0, 100, 120))));
561 } else {
562 EXPECT_THAT(
563 chunker.PaintChunks(),
564 ElementsAre(
565 IsPaintChunk(0, 1, id1, DefaultPaintChunkProperties(), nullptr,
566 IntRect(0, 0, 10, 10)),
567 IsPaintChunk(1, 1, id2, properties, &hit_test_data,
568 IntRect(20, 30, 40, 50)),
569 IsPaintChunk(1, 2, PaintChunk::Id(client_, DisplayItemType(5)),
570 properties, nullptr, IntRect(0, 0, 10, 10))));
571 }
572 }
573
TEST_F(PaintChunkerTest,ChunkBoundsAndKnownToBeOpaqueAllOpaqueItems)574 TEST_F(PaintChunkerTest, ChunkBoundsAndKnownToBeOpaqueAllOpaqueItems) {
575 ScopedCompositeAfterPaintForTest cap(true);
576 PaintChunker chunker;
577 FakeDisplayItemClient client1("client1", IntRect(0, 0, 100, 100));
578 FakeDisplayItemClient client2("client2", IntRect(0, 100, 100, 50));
579 FakeDisplayItemClient client3("client3", IntRect(50, 50, 100, 100));
580
581 auto properties = DefaultPaintChunkProperties();
582 chunker.UpdateCurrentPaintChunkProperties(nullptr, properties);
583 // Single opaque item.
584 chunker.IncrementDisplayItemIndex(
585 TestChunkerOpaqueDisplayItem(client1, DisplayItemType(0)));
586 chunker.SetForceNewChunk(true);
587 // Two opaque items. No empty area in the united bounds.
588 chunker.IncrementDisplayItemIndex(
589 TestChunkerOpaqueDisplayItem(client1, DisplayItemType(1)));
590 chunker.IncrementDisplayItemIndex(
591 TestChunkerOpaqueDisplayItem(client2, DisplayItemType(2)));
592 chunker.SetForceNewChunk(true);
593 // Two opaque items. Has empty area in the united bounds.
594 chunker.IncrementDisplayItemIndex(
595 TestChunkerOpaqueDisplayItem(client1, DisplayItemType(3)));
596 chunker.IncrementDisplayItemIndex(
597 TestChunkerOpaqueDisplayItem(client3, DisplayItemType(4)));
598
599 Vector<PaintChunk> chunks = chunker.ReleasePaintChunks();
600 EXPECT_THAT(
601 chunks,
602 ElementsAre(
603 IsPaintChunk(0, 1, PaintChunk::Id(client1, DisplayItemType(0)),
604 properties, nullptr, IntRect(0, 0, 100, 100)),
605 IsPaintChunk(1, 3, PaintChunk::Id(client1, DisplayItemType(1)),
606 properties, nullptr, IntRect(0, 0, 100, 150)),
607 IsPaintChunk(3, 5, PaintChunk::Id(client1, DisplayItemType(3)),
608 properties, nullptr, IntRect(0, 0, 150, 150))));
609 ASSERT_EQ(3u, chunks.size());
610 EXPECT_TRUE(chunks[0].known_to_be_opaque);
611 EXPECT_TRUE(chunks[1].known_to_be_opaque);
612 EXPECT_FALSE(chunks[2].known_to_be_opaque);
613 }
614
TEST_F(PaintChunkerTest,ChunkBoundsAndKnownToBeOpaqueWithHitTest)615 TEST_F(PaintChunkerTest, ChunkBoundsAndKnownToBeOpaqueWithHitTest) {
616 ScopedCompositeAfterPaintForTest cap(true);
617 PaintChunker chunker;
618 FakeDisplayItemClient client1("client1", IntRect(0, 0, 100, 100));
619 FakeDisplayItemClient client2("client2", IntRect(0, 100, 100, 50));
620 FakeDisplayItemClient client3("client3", IntRect(50, 50, 100, 100));
621
622 auto properties = DefaultPaintChunkProperties();
623 chunker.UpdateCurrentPaintChunkProperties(nullptr, properties);
624 // Hit test rect only.
625 chunker.AddHitTestDataToCurrentChunk(
626 PaintChunk::Id(client1, DisplayItemType(0)), IntRect(10, 20, 30, 40),
627 TouchAction::kAuto);
628 chunker.SetForceNewChunk(true);
629 // Hit test rect is smaller than the opaque item.
630 chunker.IncrementDisplayItemIndex(
631 TestChunkerOpaqueDisplayItem(client1, DisplayItemType(1)));
632 chunker.AddHitTestDataToCurrentChunk(
633 PaintChunk::Id(client1, DisplayItemType(2)), IntRect(0, 0, 50, 100),
634 TouchAction::kAuto);
635 chunker.SetForceNewChunk(true);
636 // Hit test rect is the same as the opaque item.
637 chunker.IncrementDisplayItemIndex(
638 TestChunkerOpaqueDisplayItem(client1, DisplayItemType(3)));
639 chunker.AddHitTestDataToCurrentChunk(
640 PaintChunk::Id(client1, DisplayItemType(4)), IntRect(0, 0, 100, 100),
641 TouchAction::kAuto);
642 chunker.SetForceNewChunk(true);
643 // Hit test rect is bigger than the opaque item.
644 chunker.IncrementDisplayItemIndex(
645 TestChunkerOpaqueDisplayItem(client1, DisplayItemType(5)));
646 chunker.AddHitTestDataToCurrentChunk(
647 PaintChunk::Id(client1, DisplayItemType(6)), IntRect(0, 100, 200, 100),
648 TouchAction::kAuto);
649
650 Vector<PaintChunk> chunks = chunker.ReleasePaintChunks();
651 EXPECT_THAT(
652 chunks,
653 ElementsAre(
654 IsPaintChunk(0, 0, PaintChunk::Id(client1, DisplayItemType(0)),
655 properties, nullptr, IntRect(10, 20, 30, 40)),
656 IsPaintChunk(0, 1, PaintChunk::Id(client1, DisplayItemType(1)),
657 properties, nullptr, IntRect(0, 0, 100, 100)),
658 IsPaintChunk(1, 2, PaintChunk::Id(client1, DisplayItemType(3)),
659 properties, nullptr, IntRect(0, 0, 100, 100)),
660 IsPaintChunk(2, 3, PaintChunk::Id(client1, DisplayItemType(5)),
661 properties, nullptr, IntRect(0, 0, 200, 200))));
662 ASSERT_EQ(4u, chunks.size());
663 EXPECT_FALSE(chunks[0].known_to_be_opaque);
664 EXPECT_TRUE(chunks[1].known_to_be_opaque);
665 EXPECT_TRUE(chunks[2].known_to_be_opaque);
666 EXPECT_FALSE(chunks[3].known_to_be_opaque);
667 }
668
TEST_F(PaintChunkerTest,ChunkBoundsAndKnownToBeOpaqueMixedOpaquenessItems)669 TEST_F(PaintChunkerTest, ChunkBoundsAndKnownToBeOpaqueMixedOpaquenessItems) {
670 ScopedCompositeAfterPaintForTest cap(true);
671 PaintChunker chunker;
672 FakeDisplayItemClient client1("client1", IntRect(0, 0, 100, 100));
673 FakeDisplayItemClient client3("client2", IntRect(50, 50, 50, 50));
674
675 auto properties = DefaultPaintChunkProperties();
676 chunker.UpdateCurrentPaintChunkProperties(nullptr, properties);
677 // Single translucent item .
678 chunker.IncrementDisplayItemIndex(
679 TestChunkerDisplayItem(client1, DisplayItemType(1)));
680 chunker.SetForceNewChunk(true);
681 // Two items, one translucent, one opaque. The opaque item doesn't contain
682 // the translucent item.
683 chunker.IncrementDisplayItemIndex(
684 TestChunkerDisplayItem(client1, DisplayItemType(2)));
685 chunker.IncrementDisplayItemIndex(
686 TestChunkerOpaqueDisplayItem(client3, DisplayItemType(3)));
687 chunker.SetForceNewChunk(true);
688 // Two items, one translucent, one opaque, with the same visual rect.
689 chunker.IncrementDisplayItemIndex(
690 TestChunkerDisplayItem(client1, DisplayItemType(4)));
691 chunker.IncrementDisplayItemIndex(
692 TestChunkerOpaqueDisplayItem(client1, DisplayItemType(5)));
693 chunker.SetForceNewChunk(true);
694 // Two items, one opaque, one translucent. The opaque item contains the
695 // translucent item.
696 chunker.IncrementDisplayItemIndex(
697 TestChunkerOpaqueDisplayItem(client1, DisplayItemType(6)));
698 chunker.IncrementDisplayItemIndex(
699 TestChunkerDisplayItem(client3, DisplayItemType(7)));
700
701 Vector<PaintChunk> chunks = chunker.ReleasePaintChunks();
702 EXPECT_THAT(
703 chunks,
704 ElementsAre(
705 IsPaintChunk(0, 1, PaintChunk::Id(client1, DisplayItemType(1)),
706 properties, nullptr, IntRect(0, 0, 100, 100)),
707 IsPaintChunk(1, 3, PaintChunk::Id(client1, DisplayItemType(2)),
708 properties, nullptr, IntRect(0, 0, 100, 100)),
709 IsPaintChunk(3, 5, PaintChunk::Id(client1, DisplayItemType(4)),
710 properties, nullptr, IntRect(0, 0, 100, 100)),
711 IsPaintChunk(5, 7, PaintChunk::Id(client1, DisplayItemType(6)),
712 properties, nullptr, IntRect(0, 0, 100, 100))));
713 ASSERT_EQ(4u, chunks.size());
714 EXPECT_FALSE(chunks[0].known_to_be_opaque);
715 EXPECT_FALSE(chunks[1].known_to_be_opaque);
716 EXPECT_TRUE(chunks[2].known_to_be_opaque);
717 EXPECT_TRUE(chunks[3].known_to_be_opaque);
718 }
719
720 } // namespace
721 } // namespace blink
722