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