| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "flutter/flow/layers/container_layer.h" |
| |
| #include "flutter/flow/layers/layer.h" |
| #include "flutter/flow/layers/layer_tree.h" |
| #include "flutter/flow/testing/diff_context_test.h" |
| #include "flutter/flow/testing/layer_test.h" |
| #include "flutter/flow/testing/mock_layer.h" |
| #include "flutter/fml/macros.h" |
| #include "flutter/testing/mock_canvas.h" |
| #include "gtest/gtest.h" |
| #include "include/core/SkCanvas.h" |
| #include "include/core/SkMatrix.h" |
| |
| namespace flutter { |
| namespace testing { |
| |
| using ContainerLayerTest = LayerTest; |
| |
| #ifndef NDEBUG |
| TEST_F(ContainerLayerTest, LayerWithParentHasPlatformView) { |
| auto layer = std::make_shared<ContainerLayer>(); |
| |
| preroll_context()->has_platform_view = true; |
| EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context()), |
| "!context->has_platform_view"); |
| } |
| |
| TEST_F(ContainerLayerTest, LayerWithParentHasTextureLayer) { |
| auto layer = std::make_shared<ContainerLayer>(); |
| |
| preroll_context()->has_texture_layer = true; |
| EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context()), |
| "!context->has_texture_layer"); |
| } |
| |
| TEST_F(ContainerLayerTest, PaintingEmptyLayerDies) { |
| auto layer = std::make_shared<ContainerLayer>(); |
| |
| layer->Preroll(preroll_context()); |
| EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); |
| EXPECT_EQ(layer->child_paint_bounds(), SkRect::MakeEmpty()); |
| EXPECT_FALSE(layer->needs_painting(paint_context())); |
| |
| EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), |
| "needs_painting\\(context\\)"); |
| } |
| |
| TEST_F(ContainerLayerTest, PaintBeforePrerollDies) { |
| SkPath child_path; |
| child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); |
| auto mock_layer = std::make_shared<MockLayer>(child_path); |
| auto layer = std::make_shared<ContainerLayer>(); |
| layer->Add(mock_layer); |
| |
| EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); |
| EXPECT_EQ(layer->child_paint_bounds(), SkRect::MakeEmpty()); |
| EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), |
| "needs_painting\\(context\\)"); |
| } |
| #endif |
| |
| TEST_F(ContainerLayerTest, LayerWithParentHasTextureLayerNeedsResetFlag) { |
| SkPath child_path1; |
| child_path1.addRect(5.0f, 6.0f, 20.5f, 21.5f); |
| SkPath child_path2; |
| child_path2.addRect(8.0f, 2.0f, 16.5f, 14.5f); |
| SkPaint child_paint1(SkColors::kGray); |
| SkPaint child_paint2(SkColors::kGreen); |
| |
| auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1); |
| mock_layer1->set_fake_has_texture_layer(true); |
| auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2); |
| |
| auto root = std::make_shared<ContainerLayer>(); |
| auto container_layer1 = std::make_shared<ContainerLayer>(); |
| auto container_layer2 = std::make_shared<ContainerLayer>(); |
| root->Add(container_layer1); |
| root->Add(container_layer2); |
| container_layer1->Add(mock_layer1); |
| container_layer2->Add(mock_layer2); |
| |
| EXPECT_EQ(preroll_context()->has_texture_layer, false); |
| root->Preroll(preroll_context()); |
| EXPECT_EQ(preroll_context()->has_texture_layer, true); |
| // The flag for holding texture layer from parent needs to be clear |
| EXPECT_EQ(mock_layer2->parent_has_texture_layer(), false); |
| } |
| |
| TEST_F(ContainerLayerTest, Simple) { |
| SkPath child_path; |
| child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); |
| SkPaint child_paint(SkColors::kGreen); |
| SkMatrix initial_transform = SkMatrix::Translate(-0.5f, -0.5f); |
| |
| auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint); |
| auto layer = std::make_shared<ContainerLayer>(); |
| layer->Add(mock_layer); |
| |
| preroll_context()->state_stack.set_preroll_delegate(initial_transform); |
| layer->Preroll(preroll_context()); |
| EXPECT_FALSE(preroll_context()->has_platform_view); |
| EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds()); |
| EXPECT_EQ(layer->paint_bounds(), child_path.getBounds()); |
| EXPECT_EQ(layer->child_paint_bounds(), layer->paint_bounds()); |
| EXPECT_TRUE(mock_layer->needs_painting(paint_context())); |
| EXPECT_TRUE(layer->needs_painting(paint_context())); |
| EXPECT_EQ(mock_layer->parent_matrix(), initial_transform); |
| EXPECT_EQ(mock_layer->parent_cull_rect(), kGiantRect); |
| |
| layer->Paint(paint_context()); |
| EXPECT_EQ(mock_canvas().draw_calls(), |
| std::vector({MockCanvas::DrawCall{ |
| 0, MockCanvas::DrawPathData{child_path, child_paint}}})); |
| } |
| |
| TEST_F(ContainerLayerTest, Multiple) { |
| SkPath child_path1; |
| child_path1.addRect(5.0f, 6.0f, 20.5f, 21.5f); |
| SkPath child_path2; |
| child_path2.addRect(8.0f, 2.0f, 16.5f, 14.5f); |
| SkPaint child_paint1(SkColors::kGray); |
| SkPaint child_paint2(SkColors::kGreen); |
| SkMatrix initial_transform = SkMatrix::Translate(-0.5f, -0.5f); |
| |
| auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1); |
| mock_layer1->set_fake_has_platform_view(true); |
| auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2); |
| auto layer = std::make_shared<ContainerLayer>(); |
| layer->Add(mock_layer1); |
| layer->Add(mock_layer2); |
| |
| SkRect expected_total_bounds = child_path1.getBounds(); |
| expected_total_bounds.join(child_path2.getBounds()); |
| preroll_context()->state_stack.set_preroll_delegate(initial_transform); |
| layer->Preroll(preroll_context()); |
| EXPECT_TRUE(preroll_context()->has_platform_view); |
| EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); |
| EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); |
| EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); |
| EXPECT_EQ(layer->child_paint_bounds(), layer->paint_bounds()); |
| EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); |
| EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); |
| EXPECT_TRUE(layer->needs_painting(paint_context())); |
| EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); |
| EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); |
| EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); |
| EXPECT_EQ(mock_layer2->parent_cull_rect(), |
| kGiantRect); // Siblings are independent |
| |
| layer->Paint(paint_context()); |
| EXPECT_EQ( |
| mock_canvas().draw_calls(), |
| std::vector({MockCanvas::DrawCall{ |
| 0, MockCanvas::DrawPathData{child_path1, child_paint1}}, |
| MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ |
| child_path2, child_paint2}}})); |
| } |
| |
| TEST_F(ContainerLayerTest, MultipleWithEmpty) { |
| SkPath child_path1; |
| child_path1.addRect(5.0f, 6.0f, 20.5f, 21.5f); |
| SkPaint child_paint1(SkColors::kGray); |
| SkPaint child_paint2(SkColors::kGreen); |
| SkMatrix initial_transform = SkMatrix::Translate(-0.5f, -0.5f); |
| |
| auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1); |
| auto mock_layer2 = std::make_shared<MockLayer>(SkPath(), child_paint2); |
| auto layer = std::make_shared<ContainerLayer>(); |
| layer->Add(mock_layer1); |
| layer->Add(mock_layer2); |
| |
| preroll_context()->state_stack.set_preroll_delegate(initial_transform); |
| layer->Preroll(preroll_context()); |
| EXPECT_FALSE(preroll_context()->has_platform_view); |
| EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); |
| EXPECT_EQ(mock_layer2->paint_bounds(), SkPath().getBounds()); |
| EXPECT_EQ(layer->paint_bounds(), child_path1.getBounds()); |
| EXPECT_EQ(layer->child_paint_bounds(), layer->paint_bounds()); |
| EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); |
| EXPECT_FALSE(mock_layer2->needs_painting(paint_context())); |
| EXPECT_TRUE(layer->needs_painting(paint_context())); |
| EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); |
| EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); |
| EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); |
| EXPECT_EQ(mock_layer2->parent_cull_rect(), kGiantRect); |
| |
| layer->Paint(paint_context()); |
| EXPECT_EQ(mock_canvas().draw_calls(), |
| std::vector({MockCanvas::DrawCall{ |
| 0, MockCanvas::DrawPathData{child_path1, child_paint1}}})); |
| } |
| |
| TEST_F(ContainerLayerTest, NeedsSystemComposite) { |
| SkPath child_path1; |
| child_path1.addRect(5.0f, 6.0f, 20.5f, 21.5f); |
| SkPath child_path2; |
| child_path2.addRect(8.0f, 2.0f, 16.5f, 14.5f); |
| SkPaint child_paint1(SkColors::kGray); |
| SkPaint child_paint2(SkColors::kGreen); |
| SkMatrix initial_transform = SkMatrix::Translate(-0.5f, -0.5f); |
| |
| auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1); |
| mock_layer1->set_fake_has_platform_view(false); |
| auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2); |
| auto layer = std::make_shared<ContainerLayer>(); |
| layer->Add(mock_layer1); |
| layer->Add(mock_layer2); |
| |
| SkRect expected_total_bounds = child_path1.getBounds(); |
| expected_total_bounds.join(child_path2.getBounds()); |
| preroll_context()->state_stack.set_preroll_delegate(initial_transform); |
| layer->Preroll(preroll_context()); |
| EXPECT_FALSE(preroll_context()->has_platform_view); |
| EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds()); |
| EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds()); |
| EXPECT_EQ(layer->paint_bounds(), expected_total_bounds); |
| EXPECT_EQ(layer->child_paint_bounds(), layer->paint_bounds()); |
| EXPECT_TRUE(mock_layer1->needs_painting(paint_context())); |
| EXPECT_TRUE(mock_layer2->needs_painting(paint_context())); |
| EXPECT_TRUE(layer->needs_painting(paint_context())); |
| EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform); |
| EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform); |
| EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect); |
| EXPECT_EQ(mock_layer2->parent_cull_rect(), kGiantRect); |
| |
| layer->Paint(paint_context()); |
| EXPECT_EQ( |
| mock_canvas().draw_calls(), |
| std::vector({MockCanvas::DrawCall{ |
| 0, MockCanvas::DrawPathData{child_path1, child_paint1}}, |
| MockCanvas::DrawCall{0, MockCanvas::DrawPathData{ |
| child_path2, child_paint2}}})); |
| } |
| |
| TEST_F(ContainerLayerTest, RasterCacheTest) { |
| // LTRB |
| const SkPath child_path1 = SkPath().addRect(5.0f, 6.0f, 20.5f, 21.5f); |
| const SkPath child_path2 = SkPath().addRect(21.0f, 6.0f, 25.5f, 21.5f); |
| const SkPath child_path3 = SkPath().addRect(26.0f, 6.0f, 30.5f, 21.5f); |
| const SkPaint child_paint1(SkColors::kGray); |
| const SkPaint child_paint2(SkColors::kGreen); |
| const SkPaint paint; |
| auto cacheable_container_layer1 = |
| MockCacheableContainerLayer::CacheLayerOrChildren(); |
| auto cacheable_container_layer2 = |
| MockCacheableContainerLayer::CacheLayerOnly(); |
| auto cacheable_container_layer11 = |
| MockCacheableContainerLayer::CacheLayerOrChildren(); |
| |
| auto cacheable_layer111 = |
| std::make_shared<MockCacheableLayer>(child_path3, paint); |
| // if the frame had rendered 2 frames, we will cache the cacheable_layer21 |
| // layer |
| auto cacheable_layer21 = |
| std::make_shared<MockCacheableLayer>(child_path1, paint, 2); |
| |
| // clang-format off |
| // layer |
| // | |
| // ________________________________ ________________________________ |
| // | | | |
| // cacheable_container_layer1 mock_layer2 cacheable_container_layer2 |
| // | | |
| // cacheable_container_layer11 cacheable_layer21 |
| // | |
| // cacheable_layer111 |
| // clang-format on |
| |
| auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1); |
| auto mock_layer2 = std::make_shared<MockLayer>(SkPath(), child_paint2); |
| auto mock_layer3 = std::make_shared<MockLayer>(child_path2, paint); |
| |
| cacheable_container_layer1->Add(mock_layer1); |
| cacheable_container_layer1->Add(mock_layer3); |
| |
| cacheable_container_layer1->Add(cacheable_container_layer11); |
| cacheable_container_layer11->Add(cacheable_layer111); |
| |
| cacheable_container_layer2->Add(cacheable_layer21); |
| auto layer = std::make_shared<ContainerLayer>(); |
| layer->Add(cacheable_container_layer1); |
| layer->Add(mock_layer2); |
| layer->Add(cacheable_container_layer2); |
| |
| SkCanvas cache_canvas; |
| cache_canvas.setMatrix(SkMatrix::I()); |
| |
| // Initial Preroll for check the layer paint bounds |
| layer->Preroll(preroll_context()); |
| |
| EXPECT_EQ(mock_layer1->paint_bounds(), |
| SkRect::MakeLTRB(5.f, 6.f, 20.5f, 21.5f)); |
| EXPECT_EQ(mock_layer3->paint_bounds(), |
| SkRect::MakeLTRB(21.0f, 6.0f, 25.5f, 21.5f)); |
| EXPECT_EQ(cacheable_layer111->paint_bounds(), |
| SkRect::MakeLTRB(26.0f, 6.0f, 30.5f, 21.5f)); |
| EXPECT_EQ(cacheable_container_layer1->paint_bounds(), |
| SkRect::MakeLTRB(5.f, 6.f, 30.5f, 21.5f)); |
| |
| // the preroll context's raster cache is nullptr |
| EXPECT_EQ(preroll_context()->raster_cached_entries->size(), |
| static_cast<unsigned long>(0)); |
| { |
| // frame1 |
| use_mock_raster_cache(); |
| preroll_context()->raster_cache->BeginFrame(); |
| layer->Preroll(preroll_context()); |
| preroll_context()->raster_cache->EvictUnusedCacheEntries(); |
| // Cache the cacheable entries |
| LayerTree::TryToRasterCache(*(preroll_context()->raster_cached_entries), |
| &paint_context()); |
| |
| EXPECT_EQ(preroll_context()->raster_cached_entries->size(), |
| static_cast<unsigned long>(5)); |
| |
| // cacheable_container_layer1 will cache his children |
| EXPECT_EQ(cacheable_container_layer1->raster_cache_item()->cache_state(), |
| RasterCacheItem::CacheState::kChildren); |
| EXPECT_TRUE(raster_cache()->HasEntry( |
| cacheable_container_layer1->raster_cache_item()->GetId().value(), |
| SkMatrix::I())); |
| |
| EXPECT_EQ(cacheable_container_layer11->raster_cache_item()->cache_state(), |
| RasterCacheItem::CacheState::kChildren); |
| EXPECT_TRUE(raster_cache()->HasEntry( |
| cacheable_container_layer11->raster_cache_item()->GetId().value(), |
| SkMatrix::I())); |
| EXPECT_FALSE(raster_cache()->Draw( |
| cacheable_container_layer11->raster_cache_item()->GetId().value(), |
| cache_canvas, &paint)); |
| |
| // The cacheable_layer111 should be cached when rended 3 frames |
| EXPECT_EQ(cacheable_layer111->raster_cache_item()->cache_state(), |
| RasterCacheItem::CacheState::kNone); |
| // render count < 2 don't cache it |
| EXPECT_EQ(cacheable_container_layer2->raster_cache_item()->cache_state(), |
| RasterCacheItem::CacheState::kNone); |
| preroll_context()->raster_cache->EndFrame(); |
| } |
| |
| { |
| // frame2 |
| // new frame the layer tree will create new PrerollContext, so in here we |
| // clear the cached_entries |
| preroll_context()->raster_cached_entries->clear(); |
| preroll_context()->raster_cache->BeginFrame(); |
| layer->Preroll(preroll_context()); |
| preroll_context()->raster_cache->EvictUnusedCacheEntries(); |
| |
| // Cache the cacheable entries |
| LayerTree::TryToRasterCache(*(preroll_context()->raster_cached_entries), |
| &paint_context()); |
| EXPECT_EQ(preroll_context()->raster_cached_entries->size(), |
| static_cast<unsigned long>(5)); |
| EXPECT_EQ(cacheable_container_layer1->raster_cache_item()->cache_state(), |
| RasterCacheItem::CacheState::kChildren); |
| EXPECT_TRUE(raster_cache()->HasEntry( |
| cacheable_container_layer1->raster_cache_item()->GetId().value(), |
| SkMatrix::I())); |
| |
| EXPECT_EQ(cacheable_container_layer11->raster_cache_item()->cache_state(), |
| RasterCacheItem::CacheState::kChildren); |
| EXPECT_TRUE(raster_cache()->HasEntry( |
| cacheable_container_layer11->raster_cache_item()->GetId().value(), |
| SkMatrix::I())); |
| EXPECT_FALSE(raster_cache()->Draw( |
| cacheable_container_layer11->raster_cache_item()->GetId().value(), |
| cache_canvas, &paint)); |
| |
| EXPECT_EQ(cacheable_container_layer2->raster_cache_item()->cache_state(), |
| RasterCacheItem::CacheState::kNone); |
| |
| // render count == 2 cache it |
| EXPECT_EQ(cacheable_layer21->raster_cache_item()->cache_state(), |
| RasterCacheItem::CacheState::kCurrent); |
| EXPECT_TRUE(raster_cache()->HasEntry( |
| cacheable_layer21->raster_cache_item()->GetId().value(), |
| SkMatrix::I())); |
| EXPECT_TRUE(raster_cache()->Draw( |
| cacheable_layer21->raster_cache_item()->GetId().value(), cache_canvas, |
| &paint)); |
| preroll_context()->raster_cache->EndFrame(); |
| } |
| |
| { |
| // frame3 |
| // new frame the layer tree will create new PrerollContext, so in here we |
| // clear the cached_entries |
| preroll_context()->raster_cache->BeginFrame(); |
| preroll_context()->raster_cached_entries->clear(); |
| layer->Preroll(preroll_context()); |
| preroll_context()->raster_cache->EvictUnusedCacheEntries(); |
| // Cache the cacheable entries |
| LayerTree::TryToRasterCache(*(preroll_context()->raster_cached_entries), |
| &paint_context()); |
| EXPECT_EQ(preroll_context()->raster_cached_entries->size(), |
| static_cast<unsigned long>(5)); |
| EXPECT_EQ(cacheable_container_layer1->raster_cache_item()->cache_state(), |
| RasterCacheItem::CacheState::kCurrent); |
| EXPECT_TRUE(raster_cache()->HasEntry( |
| cacheable_container_layer1->raster_cache_item()->GetId().value(), |
| SkMatrix::I())); |
| EXPECT_TRUE(raster_cache()->HasEntry( |
| cacheable_container_layer11->raster_cache_item()->GetId().value(), |
| SkMatrix::I())); |
| EXPECT_FALSE(raster_cache()->Draw( |
| cacheable_container_layer11->raster_cache_item()->GetId().value(), |
| cache_canvas, &paint)); |
| // The 3td frame, we will cache the cacheable_layer111, but his ancestor has |
| // been cached, so cacheable_layer111 Draw is false |
| EXPECT_TRUE(raster_cache()->HasEntry( |
| cacheable_layer111->raster_cache_item()->GetId().value(), |
| SkMatrix::I())); |
| EXPECT_FALSE(raster_cache()->Draw( |
| cacheable_layer111->raster_cache_item()->GetId().value(), cache_canvas, |
| &paint)); |
| |
| // The third frame, we will cache the cacheable_container_layer2 |
| EXPECT_EQ(cacheable_container_layer2->raster_cache_item()->cache_state(), |
| RasterCacheItem::CacheState::kCurrent); |
| |
| EXPECT_TRUE(raster_cache()->HasEntry( |
| cacheable_layer21->raster_cache_item()->GetId().value(), |
| SkMatrix::I())); |
| preroll_context()->raster_cache->EndFrame(); |
| } |
| |
| { |
| preroll_context()->raster_cache->BeginFrame(); |
| // frame4 |
| preroll_context()->raster_cached_entries->clear(); |
| layer->Preroll(preroll_context()); |
| preroll_context()->raster_cache->EvictUnusedCacheEntries(); |
| LayerTree::TryToRasterCache(*(preroll_context()->raster_cached_entries), |
| &paint_context()); |
| preroll_context()->raster_cache->EndFrame(); |
| |
| // frame5 |
| preroll_context()->raster_cache->BeginFrame(); |
| preroll_context()->raster_cached_entries->clear(); |
| layer->Preroll(preroll_context()); |
| LayerTree::TryToRasterCache(*(preroll_context()->raster_cached_entries), |
| &paint_context()); |
| preroll_context()->raster_cache->EndFrame(); |
| |
| // frame6 |
| preroll_context()->raster_cache->BeginFrame(); |
| preroll_context()->raster_cached_entries->clear(); |
| layer->Preroll(preroll_context()); |
| LayerTree::TryToRasterCache(*(preroll_context()->raster_cached_entries), |
| &paint_context()); |
| preroll_context()->raster_cache->EndFrame(); |
| } |
| } |
| |
| TEST_F(ContainerLayerTest, OpacityInheritance) { |
| auto path1 = SkPath().addRect({10, 10, 30, 30}); |
| auto mock1 = MockLayer::MakeOpacityCompatible(path1); |
| auto container1 = std::make_shared<ContainerLayer>(); |
| container1->Add(mock1); |
| |
| // ContainerLayer will pass through compatibility |
| PrerollContext* context = preroll_context(); |
| container1->Preroll(context); |
| EXPECT_EQ(context->renderable_state_flags, |
| LayerStateStack::kCallerCanApplyOpacity); |
| |
| auto path2 = SkPath().addRect({40, 40, 50, 50}); |
| auto mock2 = MockLayer::MakeOpacityCompatible(path2); |
| container1->Add(mock2); |
| |
| // ContainerLayer will pass through compatibility from multiple |
| // non-overlapping compatible children |
| container1->Preroll(context); |
| EXPECT_EQ(context->renderable_state_flags, |
| LayerStateStack::kCallerCanApplyOpacity); |
| |
| auto path3 = SkPath().addRect({20, 20, 40, 40}); |
| auto mock3 = MockLayer::MakeOpacityCompatible(path3); |
| container1->Add(mock3); |
| |
| // ContainerLayer will not pass through compatibility from multiple |
| // overlapping children even if they are individually compatible |
| container1->Preroll(context); |
| EXPECT_EQ(context->renderable_state_flags, 0); |
| |
| auto container2 = std::make_shared<ContainerLayer>(); |
| container2->Add(mock1); |
| container2->Add(mock2); |
| |
| // Double check first two children are compatible and non-overlapping |
| container2->Preroll(context); |
| EXPECT_EQ(context->renderable_state_flags, |
| LayerStateStack::kCallerCanApplyOpacity); |
| |
| auto path4 = SkPath().addRect({60, 60, 70, 70}); |
| auto mock4 = MockLayer::Make(path4); |
| container2->Add(mock4); |
| |
| // The third child is non-overlapping, but not compatible so the |
| // ContainerLayer should end up incompatible |
| container2->Preroll(context); |
| EXPECT_EQ(context->renderable_state_flags, 0); |
| } |
| |
| TEST_F(ContainerLayerTest, CollectionCacheableLayer) { |
| SkPath child_path; |
| child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f); |
| SkPaint child_paint(SkColors::kGreen); |
| SkMatrix initial_transform = SkMatrix::Translate(-0.5f, -0.5f); |
| |
| auto mock_layer1 = std::make_shared<MockLayer>(SkPath(), child_paint); |
| auto mock_cacheable_container_layer1 = |
| std::make_shared<MockCacheableContainerLayer>(); |
| auto mock_container_layer = std::make_shared<ContainerLayer>(); |
| auto mock_cacheable_layer = |
| std::make_shared<MockCacheableLayer>(child_path, child_paint); |
| mock_cacheable_container_layer1->Add(mock_cacheable_layer); |
| |
| // ContainerLayer |
| // |- MockLayer |
| // |- MockCacheableContainerLayer |
| // |- MockCacheableLayer |
| auto layer = std::make_shared<ContainerLayer>(); |
| layer->Add(mock_cacheable_container_layer1); |
| layer->Add(mock_layer1); |
| |
| preroll_context()->state_stack.set_preroll_delegate(initial_transform); |
| layer->Preroll(preroll_context()); |
| // raster cache is null, so no entry |
| ASSERT_EQ(preroll_context()->raster_cached_entries->size(), |
| static_cast<const unsigned long>(0)); |
| |
| use_mock_raster_cache(); |
| // preroll_context()->raster_cache = raster_cache(); |
| layer->Preroll(preroll_context()); |
| ASSERT_EQ(preroll_context()->raster_cached_entries->size(), |
| static_cast<const unsigned long>(2)); |
| } |
| |
| using ContainerLayerDiffTest = DiffContextTest; |
| |
| // Insert PictureLayer amongst container layers |
| TEST_F(ContainerLayerDiffTest, PictureLayerInsertion) { |
| auto pic1 = CreateDisplayList(SkRect::MakeLTRB(0, 0, 50, 50), 1); |
| auto pic2 = CreateDisplayList(SkRect::MakeLTRB(100, 0, 150, 50), 1); |
| auto pic3 = CreateDisplayList(SkRect::MakeLTRB(200, 0, 250, 50), 1); |
| |
| MockLayerTree t1; |
| |
| auto t1_c1 = CreateContainerLayer(CreateDisplayListLayer(pic1)); |
| t1.root()->Add(t1_c1); |
| |
| auto t1_c2 = CreateContainerLayer(CreateDisplayListLayer(pic2)); |
| t1.root()->Add(t1_c2); |
| |
| auto damage = DiffLayerTree(t1, MockLayerTree()); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 150, 50)); |
| |
| // Add in the middle |
| |
| MockLayerTree t2; |
| auto t2_c1 = CreateContainerLayer(CreateDisplayListLayer(pic1)); |
| t2_c1->AssignOldLayer(t1_c1.get()); |
| t2.root()->Add(t2_c1); |
| |
| t2.root()->Add(CreateDisplayListLayer(pic3)); |
| |
| auto t2_c2 = CreateContainerLayer(CreateDisplayListLayer(pic2)); |
| t2_c2->AssignOldLayer(t1_c2.get()); |
| t2.root()->Add(t2_c2); |
| |
| damage = DiffLayerTree(t2, t1); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(200, 0, 250, 50)); |
| |
| // Add in the beginning |
| |
| t2 = MockLayerTree(); |
| t2.root()->Add(CreateDisplayListLayer(pic3)); |
| t2.root()->Add(t2_c1); |
| t2.root()->Add(t2_c2); |
| damage = DiffLayerTree(t2, t1); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(200, 0, 250, 50)); |
| |
| // Add at the end |
| |
| t2 = MockLayerTree(); |
| t2.root()->Add(t2_c1); |
| t2.root()->Add(t2_c2); |
| t2.root()->Add(CreateDisplayListLayer(pic3)); |
| damage = DiffLayerTree(t2, t1); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(200, 0, 250, 50)); |
| } |
| |
| // Insert picture layer amongst other picture layers |
| TEST_F(ContainerLayerDiffTest, PictureInsertion) { |
| auto pic1 = CreateDisplayList(SkRect::MakeLTRB(0, 0, 50, 50), 1); |
| auto pic2 = CreateDisplayList(SkRect::MakeLTRB(100, 0, 150, 50), 1); |
| auto pic3 = CreateDisplayList(SkRect::MakeLTRB(200, 0, 250, 50), 1); |
| |
| MockLayerTree t1; |
| t1.root()->Add(CreateDisplayListLayer(pic1)); |
| t1.root()->Add(CreateDisplayListLayer(pic2)); |
| |
| auto damage = DiffLayerTree(t1, MockLayerTree()); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 150, 50)); |
| |
| MockLayerTree t2; |
| t2.root()->Add(CreateDisplayListLayer(pic3)); |
| t2.root()->Add(CreateDisplayListLayer(pic1)); |
| t2.root()->Add(CreateDisplayListLayer(pic2)); |
| |
| damage = DiffLayerTree(t2, t1); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(200, 0, 250, 50)); |
| |
| MockLayerTree t3; |
| t3.root()->Add(CreateDisplayListLayer(pic1)); |
| t3.root()->Add(CreateDisplayListLayer(pic3)); |
| t3.root()->Add(CreateDisplayListLayer(pic2)); |
| |
| damage = DiffLayerTree(t3, t1); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(200, 0, 250, 50)); |
| |
| MockLayerTree t4; |
| t4.root()->Add(CreateDisplayListLayer(pic1)); |
| t4.root()->Add(CreateDisplayListLayer(pic2)); |
| t4.root()->Add(CreateDisplayListLayer(pic3)); |
| |
| damage = DiffLayerTree(t4, t1); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(200, 0, 250, 50)); |
| } |
| |
| TEST_F(ContainerLayerDiffTest, LayerDeletion) { |
| auto path1 = SkPath().addRect(SkRect::MakeLTRB(0, 0, 50, 50)); |
| auto path2 = SkPath().addRect(SkRect::MakeLTRB(100, 0, 150, 50)); |
| auto path3 = SkPath().addRect(SkRect::MakeLTRB(200, 0, 250, 50)); |
| |
| auto c1 = CreateContainerLayer(std::make_shared<MockLayer>(path1)); |
| auto c2 = CreateContainerLayer(std::make_shared<MockLayer>(path2)); |
| auto c3 = CreateContainerLayer(std::make_shared<MockLayer>(path3)); |
| |
| MockLayerTree t1; |
| t1.root()->Add(c1); |
| t1.root()->Add(c2); |
| t1.root()->Add(c3); |
| |
| auto damage = DiffLayerTree(t1, MockLayerTree()); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 250, 50)); |
| |
| MockLayerTree t2; |
| t2.root()->Add(c2); |
| t2.root()->Add(c3); |
| |
| damage = DiffLayerTree(t2, t1); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 50, 50)); |
| |
| MockLayerTree t3; |
| t3.root()->Add(c1); |
| t3.root()->Add(c3); |
| |
| damage = DiffLayerTree(t3, t1); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(100, 0, 150, 50)); |
| |
| MockLayerTree t4; |
| t4.root()->Add(c1); |
| t4.root()->Add(c2); |
| |
| damage = DiffLayerTree(t4, t1); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(200, 0, 250, 50)); |
| |
| MockLayerTree t5; |
| t5.root()->Add(c1); |
| |
| damage = DiffLayerTree(t5, t1); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(100, 0, 250, 50)); |
| |
| MockLayerTree t6; |
| t6.root()->Add(c2); |
| |
| damage = DiffLayerTree(t6, t1); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 250, 50)); |
| |
| MockLayerTree t7; |
| t7.root()->Add(c3); |
| |
| damage = DiffLayerTree(t7, t1); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 150, 50)); |
| } |
| |
| TEST_F(ContainerLayerDiffTest, ReplaceLayer) { |
| auto path1 = SkPath().addRect(SkRect::MakeLTRB(0, 0, 50, 50)); |
| auto path2 = SkPath().addRect(SkRect::MakeLTRB(100, 0, 150, 50)); |
| auto path3 = SkPath().addRect(SkRect::MakeLTRB(200, 0, 250, 50)); |
| |
| auto path1a = SkPath().addRect(SkRect::MakeLTRB(0, 100, 50, 150)); |
| auto path2a = SkPath().addRect(SkRect::MakeLTRB(100, 100, 150, 150)); |
| auto path3a = SkPath().addRect(SkRect::MakeLTRB(200, 100, 250, 150)); |
| |
| auto c1 = CreateContainerLayer(std::make_shared<MockLayer>(path1)); |
| auto c2 = CreateContainerLayer(std::make_shared<MockLayer>(path2)); |
| auto c3 = CreateContainerLayer(std::make_shared<MockLayer>(path3)); |
| |
| MockLayerTree t1; |
| t1.root()->Add(c1); |
| t1.root()->Add(c2); |
| t1.root()->Add(c3); |
| |
| auto damage = DiffLayerTree(t1, MockLayerTree()); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 250, 50)); |
| |
| MockLayerTree t2; |
| t2.root()->Add(c1); |
| t2.root()->Add(c2); |
| t2.root()->Add(c3); |
| |
| damage = DiffLayerTree(t2, t1); |
| EXPECT_TRUE(damage.frame_damage.isEmpty()); |
| |
| MockLayerTree t3; |
| t3.root()->Add(CreateContainerLayer({std::make_shared<MockLayer>(path1a)})); |
| t3.root()->Add(c2); |
| t3.root()->Add(c3); |
| |
| damage = DiffLayerTree(t3, t1); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(0, 0, 50, 150)); |
| |
| MockLayerTree t4; |
| t4.root()->Add(c1); |
| t4.root()->Add(CreateContainerLayer(std::make_shared<MockLayer>(path2a))); |
| t4.root()->Add(c3); |
| |
| damage = DiffLayerTree(t4, t1); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(100, 0, 150, 150)); |
| |
| MockLayerTree t5; |
| t5.root()->Add(c1); |
| t5.root()->Add(c2); |
| t5.root()->Add(CreateContainerLayer(std::make_shared<MockLayer>(path3a))); |
| |
| damage = DiffLayerTree(t5, t1); |
| EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(200, 0, 250, 150)); |
| } |
| |
| } // namespace testing |
| } // namespace flutter |