| // Copyright 2016 The Chromium 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 "platform/graphics/compositing/PaintArtifactCompositor.h" |
| |
| #include "base/test/test_simple_task_runner.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "cc/layers/layer.h" |
| #include "cc/test/fake_compositor_frame_sink.h" |
| #include "cc/test/geometry_test_utils.h" |
| #include "cc/trees/clip_node.h" |
| #include "cc/trees/effect_node.h" |
| #include "cc/trees/layer_tree_host.h" |
| #include "cc/trees/layer_tree_settings.h" |
| #include "cc/trees/scroll_node.h" |
| #include "cc/trees/transform_node.h" |
| #include "platform/RuntimeEnabledFeatures.h" |
| #include "platform/graphics/paint/EffectPaintPropertyNode.h" |
| #include "platform/graphics/paint/PaintArtifact.h" |
| #include "platform/graphics/paint/ScrollPaintPropertyNode.h" |
| #include "platform/testing/PictureMatchers.h" |
| #include "platform/testing/TestPaintArtifact.h" |
| #include "platform/testing/WebLayerTreeViewImplForTesting.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include <memory> |
| |
| namespace blink { |
| namespace { |
| |
| using ::testing::Pointee; |
| |
| gfx::Transform translation(SkMScalar x, SkMScalar y) { |
| gfx::Transform transform; |
| transform.Translate(x, y); |
| return transform; |
| } |
| |
| EffectPaintPropertyNode* dummyRootEffect() { |
| DEFINE_STATIC_REF(EffectPaintPropertyNode, node, |
| EffectPaintPropertyNode::create(nullptr, 1.0)); |
| return node; |
| } |
| |
| class WebLayerTreeViewWithCompositorFrameSink |
| : public WebLayerTreeViewImplForTesting { |
| public: |
| WebLayerTreeViewWithCompositorFrameSink(const cc::LayerTreeSettings& settings) |
| : WebLayerTreeViewImplForTesting(settings) {} |
| |
| // cc::LayerTreeHostClient |
| void RequestNewCompositorFrameSink() override { |
| layerTreeHost()->SetCompositorFrameSink( |
| cc::FakeCompositorFrameSink::Create3d()); |
| } |
| }; |
| |
| class PaintArtifactCompositorTestWithPropertyTrees : public ::testing::Test { |
| protected: |
| PaintArtifactCompositorTestWithPropertyTrees() |
| : m_taskRunner(new base::TestSimpleTaskRunner), |
| m_taskRunnerHandle(m_taskRunner) {} |
| |
| void SetUp() override { |
| RuntimeEnabledFeatures::setSlimmingPaintV2Enabled(true); |
| |
| // Delay constructing the compositor until after the feature is set. |
| m_paintArtifactCompositor = PaintArtifactCompositor::create(); |
| m_paintArtifactCompositor->enableExtraDataForTesting(); |
| |
| cc::LayerTreeSettings settings = |
| WebLayerTreeViewImplForTesting::defaultLayerTreeSettings(); |
| settings.single_thread_proxy_scheduler = false; |
| settings.use_layer_lists = true; |
| m_webLayerTreeView = |
| wrapUnique(new WebLayerTreeViewWithCompositorFrameSink(settings)); |
| m_webLayerTreeView->setRootLayer(*m_paintArtifactCompositor->getWebLayer()); |
| } |
| |
| void TearDown() override { m_featuresBackup.restore(); } |
| |
| const cc::PropertyTrees& propertyTrees() { |
| return *m_webLayerTreeView->layerTreeHost() |
| ->GetLayerTree() |
| ->property_trees(); |
| } |
| |
| const cc::TransformNode& transformNode(const cc::Layer* layer) { |
| return *propertyTrees().transform_tree.Node(layer->transform_tree_index()); |
| } |
| |
| void update(const PaintArtifact& artifact) { |
| m_paintArtifactCompositor->update(artifact, nullptr); |
| m_webLayerTreeView->layerTreeHost()->LayoutAndUpdateLayers(); |
| } |
| |
| cc::Layer* rootLayer() { return m_paintArtifactCompositor->rootLayer(); } |
| |
| size_t contentLayerCount() { |
| return m_paintArtifactCompositor->getExtraDataForTesting() |
| ->contentLayers.size(); |
| } |
| |
| cc::Layer* contentLayerAt(unsigned index) { |
| return m_paintArtifactCompositor->getExtraDataForTesting() |
| ->contentLayers[index] |
| .get(); |
| } |
| |
| private: |
| RuntimeEnabledFeatures::Backup m_featuresBackup; |
| std::unique_ptr<PaintArtifactCompositor> m_paintArtifactCompositor; |
| scoped_refptr<base::TestSimpleTaskRunner> m_taskRunner; |
| base::ThreadTaskRunnerHandle m_taskRunnerHandle; |
| std::unique_ptr<WebLayerTreeViewWithCompositorFrameSink> m_webLayerTreeView; |
| }; |
| |
| TEST_F(PaintArtifactCompositorTestWithPropertyTrees, EmptyPaintArtifact) { |
| PaintArtifact emptyArtifact; |
| update(emptyArtifact); |
| EXPECT_TRUE(rootLayer()->children().empty()); |
| } |
| |
| TEST_F(PaintArtifactCompositorTestWithPropertyTrees, OneChunkWithAnOffset) { |
| TestPaintArtifact artifact; |
| artifact.chunk(PaintChunkProperties()) |
| .rectDrawing(FloatRect(50, -50, 100, 100), Color::white); |
| update(artifact.build()); |
| |
| ASSERT_EQ(1u, contentLayerCount()); |
| const cc::Layer* child = contentLayerAt(0); |
| EXPECT_THAT(child->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 100, 100), Color::white))); |
| EXPECT_EQ(translation(50, -50), child->screen_space_transform()); |
| EXPECT_EQ(gfx::Size(100, 100), child->bounds()); |
| } |
| |
| TEST_F(PaintArtifactCompositorTestWithPropertyTrees, OneTransform) { |
| // A 90 degree clockwise rotation about (100, 100). |
| RefPtr<TransformPaintPropertyNode> transform = |
| TransformPaintPropertyNode::create(nullptr, |
| TransformationMatrix().rotate(90), |
| FloatPoint3D(100, 100, 0)); |
| |
| TestPaintArtifact artifact; |
| artifact.chunk(transform, nullptr, dummyRootEffect()) |
| .rectDrawing(FloatRect(0, 0, 100, 100), Color::white); |
| artifact.chunk(nullptr, nullptr, dummyRootEffect()) |
| .rectDrawing(FloatRect(0, 0, 100, 100), Color::gray); |
| artifact.chunk(transform, nullptr, dummyRootEffect()) |
| .rectDrawing(FloatRect(100, 100, 200, 100), Color::black); |
| update(artifact.build()); |
| |
| ASSERT_EQ(3u, contentLayerCount()); |
| { |
| const cc::Layer* layer = contentLayerAt(0); |
| EXPECT_THAT( |
| layer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 100, 100), Color::white))); |
| gfx::RectF mappedRect(0, 0, 100, 100); |
| layer->screen_space_transform().TransformRect(&mappedRect); |
| EXPECT_EQ(gfx::RectF(100, 0, 100, 100), mappedRect); |
| } |
| { |
| const cc::Layer* layer = contentLayerAt(1); |
| EXPECT_THAT( |
| layer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 100, 100), Color::gray))); |
| EXPECT_EQ(gfx::Transform(), layer->screen_space_transform()); |
| } |
| { |
| const cc::Layer* layer = contentLayerAt(2); |
| EXPECT_THAT( |
| layer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 200, 100), Color::black))); |
| gfx::RectF mappedRect(0, 0, 200, 100); |
| layer->screen_space_transform().TransformRect(&mappedRect); |
| EXPECT_EQ(gfx::RectF(0, 100, 100, 200), mappedRect); |
| } |
| } |
| |
| TEST_F(PaintArtifactCompositorTestWithPropertyTrees, TransformCombining) { |
| // A translation by (5, 5) within a 2x scale about (10, 10). |
| RefPtr<TransformPaintPropertyNode> transform1 = |
| TransformPaintPropertyNode::create( |
| nullptr, TransformationMatrix().scale(2), FloatPoint3D(10, 10, 0)); |
| RefPtr<TransformPaintPropertyNode> transform2 = |
| TransformPaintPropertyNode::create( |
| transform1, TransformationMatrix().translate(5, 5), FloatPoint3D()); |
| |
| TestPaintArtifact artifact; |
| artifact.chunk(transform1, nullptr, dummyRootEffect()) |
| .rectDrawing(FloatRect(0, 0, 300, 200), Color::white); |
| artifact.chunk(transform2, nullptr, dummyRootEffect()) |
| .rectDrawing(FloatRect(0, 0, 300, 200), Color::black); |
| update(artifact.build()); |
| |
| ASSERT_EQ(2u, contentLayerCount()); |
| { |
| const cc::Layer* layer = contentLayerAt(0); |
| EXPECT_THAT( |
| layer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 300, 200), Color::white))); |
| gfx::RectF mappedRect(0, 0, 300, 200); |
| layer->screen_space_transform().TransformRect(&mappedRect); |
| EXPECT_EQ(gfx::RectF(-10, -10, 600, 400), mappedRect); |
| } |
| { |
| const cc::Layer* layer = contentLayerAt(1); |
| EXPECT_THAT( |
| layer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 300, 200), Color::black))); |
| gfx::RectF mappedRect(0, 0, 300, 200); |
| layer->screen_space_transform().TransformRect(&mappedRect); |
| EXPECT_EQ(gfx::RectF(0, 0, 600, 400), mappedRect); |
| } |
| EXPECT_NE(contentLayerAt(0)->transform_tree_index(), |
| contentLayerAt(1)->transform_tree_index()); |
| } |
| |
| TEST_F(PaintArtifactCompositorTestWithPropertyTrees, |
| FlattensInheritedTransform) { |
| for (bool transformIsFlattened : {true, false}) { |
| SCOPED_TRACE(transformIsFlattened); |
| |
| // The flattens_inherited_transform bit corresponds to whether the _parent_ |
| // transform node flattens the transform. This is because Blink's notion of |
| // flattening determines whether content within the node's local transform |
| // is flattened, while cc's notion applies in the parent's coordinate space. |
| RefPtr<TransformPaintPropertyNode> transform1 = |
| TransformPaintPropertyNode::create(nullptr, TransformationMatrix(), |
| FloatPoint3D()); |
| RefPtr<TransformPaintPropertyNode> transform2 = |
| TransformPaintPropertyNode::create( |
| transform1, TransformationMatrix().rotate3d(0, 45, 0), |
| FloatPoint3D()); |
| RefPtr<TransformPaintPropertyNode> transform3 = |
| TransformPaintPropertyNode::create( |
| transform2, TransformationMatrix().rotate3d(0, 45, 0), |
| FloatPoint3D(), transformIsFlattened); |
| |
| TestPaintArtifact artifact; |
| artifact.chunk(transform3, nullptr, nullptr) |
| .rectDrawing(FloatRect(0, 0, 300, 200), Color::white); |
| update(artifact.build()); |
| |
| ASSERT_EQ(1u, contentLayerCount()); |
| const cc::Layer* layer = contentLayerAt(0); |
| EXPECT_THAT( |
| layer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 300, 200), Color::white))); |
| |
| // The leaf transform node should flatten its inherited transform node |
| // if and only if the intermediate rotation transform in the Blink tree |
| // flattens. |
| const cc::TransformNode* transformNode3 = |
| propertyTrees().transform_tree.Node(layer->transform_tree_index()); |
| EXPECT_EQ(transformIsFlattened, |
| transformNode3->flattens_inherited_transform); |
| |
| // Given this, we should expect the correct screen space transform for |
| // each case. If the transform was flattened, we should see it getting |
| // an effective horizontal scale of 1/sqrt(2) each time, thus it gets |
| // half as wide. If the transform was not flattened, we should see an |
| // empty rectangle (as the total 90 degree rotation makes it |
| // perpendicular to the viewport). |
| gfx::RectF rect(0, 0, 100, 100); |
| layer->screen_space_transform().TransformRect(&rect); |
| if (transformIsFlattened) |
| EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 50, 100), rect); |
| else |
| EXPECT_TRUE(rect.IsEmpty()); |
| } |
| } |
| |
| TEST_F(PaintArtifactCompositorTestWithPropertyTrees, SortingContextID) { |
| // Has no 3D rendering context. |
| RefPtr<TransformPaintPropertyNode> transform1 = |
| TransformPaintPropertyNode::create(nullptr, TransformationMatrix(), |
| FloatPoint3D()); |
| // Establishes a 3D rendering context. |
| RefPtr<TransformPaintPropertyNode> transform2 = |
| TransformPaintPropertyNode::create(transform1, TransformationMatrix(), |
| FloatPoint3D(), false, 1); |
| // Extends the 3D rendering context of transform2. |
| RefPtr<TransformPaintPropertyNode> transform3 = |
| TransformPaintPropertyNode::create(transform2, TransformationMatrix(), |
| FloatPoint3D(), false, 1); |
| // Establishes a 3D rendering context distinct from transform2. |
| RefPtr<TransformPaintPropertyNode> transform4 = |
| TransformPaintPropertyNode::create(transform2, TransformationMatrix(), |
| FloatPoint3D(), false, 2); |
| |
| TestPaintArtifact artifact; |
| artifact.chunk(transform1, nullptr, dummyRootEffect()) |
| .rectDrawing(FloatRect(0, 0, 300, 200), Color::white); |
| artifact.chunk(transform2, nullptr, dummyRootEffect()) |
| .rectDrawing(FloatRect(0, 0, 300, 200), Color::lightGray); |
| artifact.chunk(transform3, nullptr, dummyRootEffect()) |
| .rectDrawing(FloatRect(0, 0, 300, 200), Color::darkGray); |
| artifact.chunk(transform4, nullptr, dummyRootEffect()) |
| .rectDrawing(FloatRect(0, 0, 300, 200), Color::black); |
| update(artifact.build()); |
| |
| ASSERT_EQ(4u, contentLayerCount()); |
| |
| // The white layer is not 3D sorted. |
| const cc::Layer* whiteLayer = contentLayerAt(0); |
| EXPECT_THAT(whiteLayer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 300, 200), Color::white))); |
| int whiteSortingContextId = transformNode(whiteLayer).sorting_context_id; |
| EXPECT_EQ(whiteLayer->sorting_context_id(), whiteSortingContextId); |
| EXPECT_EQ(0, whiteSortingContextId); |
| |
| // The light gray layer is 3D sorted. |
| const cc::Layer* lightGrayLayer = contentLayerAt(1); |
| EXPECT_THAT( |
| lightGrayLayer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 300, 200), Color::lightGray))); |
| int lightGraySortingContextId = |
| transformNode(lightGrayLayer).sorting_context_id; |
| EXPECT_EQ(lightGrayLayer->sorting_context_id(), lightGraySortingContextId); |
| EXPECT_NE(0, lightGraySortingContextId); |
| |
| // The dark gray layer is 3D sorted with the light gray layer, but has a |
| // separate transform node. |
| const cc::Layer* darkGrayLayer = contentLayerAt(2); |
| EXPECT_THAT( |
| darkGrayLayer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 300, 200), Color::darkGray))); |
| int darkGraySortingContextId = |
| transformNode(darkGrayLayer).sorting_context_id; |
| EXPECT_EQ(darkGrayLayer->sorting_context_id(), darkGraySortingContextId); |
| EXPECT_EQ(lightGraySortingContextId, darkGraySortingContextId); |
| EXPECT_NE(lightGrayLayer->transform_tree_index(), |
| darkGrayLayer->transform_tree_index()); |
| |
| // The black layer is 3D sorted, but in a separate context from the previous |
| // layers. |
| const cc::Layer* blackLayer = contentLayerAt(3); |
| EXPECT_THAT(blackLayer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 300, 200), Color::black))); |
| int blackSortingContextId = transformNode(blackLayer).sorting_context_id; |
| EXPECT_EQ(blackLayer->sorting_context_id(), blackSortingContextId); |
| EXPECT_NE(0, blackSortingContextId); |
| EXPECT_NE(lightGraySortingContextId, blackSortingContextId); |
| } |
| |
| TEST_F(PaintArtifactCompositorTestWithPropertyTrees, OneClip) { |
| RefPtr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::create( |
| nullptr, nullptr, FloatRoundedRect(100, 100, 300, 200)); |
| |
| TestPaintArtifact artifact; |
| artifact.chunk(nullptr, clip, nullptr) |
| .rectDrawing(FloatRect(220, 80, 300, 200), Color::black); |
| update(artifact.build()); |
| |
| ASSERT_EQ(1u, contentLayerCount()); |
| const cc::Layer* layer = contentLayerAt(0); |
| EXPECT_THAT(layer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 300, 200), Color::black))); |
| EXPECT_EQ(translation(220, 80), layer->screen_space_transform()); |
| |
| const cc::ClipNode* clipNode = |
| propertyTrees().clip_tree.Node(layer->clip_tree_index()); |
| EXPECT_TRUE(clipNode->applies_local_clip); |
| EXPECT_TRUE(clipNode->layers_are_clipped); |
| EXPECT_EQ(gfx::RectF(100, 100, 300, 200), clipNode->clip); |
| } |
| |
| TEST_F(PaintArtifactCompositorTestWithPropertyTrees, NestedClips) { |
| RefPtr<ClipPaintPropertyNode> clip1 = ClipPaintPropertyNode::create( |
| nullptr, nullptr, FloatRoundedRect(100, 100, 700, 700)); |
| RefPtr<ClipPaintPropertyNode> clip2 = ClipPaintPropertyNode::create( |
| clip1, nullptr, FloatRoundedRect(200, 200, 700, 100)); |
| |
| TestPaintArtifact artifact; |
| artifact.chunk(nullptr, clip1, dummyRootEffect()) |
| .rectDrawing(FloatRect(300, 350, 100, 100), Color::white); |
| artifact.chunk(nullptr, clip2, dummyRootEffect()) |
| .rectDrawing(FloatRect(300, 350, 100, 100), Color::lightGray); |
| artifact.chunk(nullptr, clip1, dummyRootEffect()) |
| .rectDrawing(FloatRect(300, 350, 100, 100), Color::darkGray); |
| artifact.chunk(nullptr, clip2, dummyRootEffect()) |
| .rectDrawing(FloatRect(300, 350, 100, 100), Color::black); |
| update(artifact.build()); |
| |
| ASSERT_EQ(4u, contentLayerCount()); |
| |
| const cc::Layer* whiteLayer = contentLayerAt(0); |
| EXPECT_THAT(whiteLayer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 100, 100), Color::white))); |
| EXPECT_EQ(translation(300, 350), whiteLayer->screen_space_transform()); |
| |
| const cc::Layer* lightGrayLayer = contentLayerAt(1); |
| EXPECT_THAT( |
| lightGrayLayer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 100, 100), Color::lightGray))); |
| EXPECT_EQ(translation(300, 350), lightGrayLayer->screen_space_transform()); |
| |
| const cc::Layer* darkGrayLayer = contentLayerAt(2); |
| EXPECT_THAT( |
| darkGrayLayer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 100, 100), Color::darkGray))); |
| EXPECT_EQ(translation(300, 350), darkGrayLayer->screen_space_transform()); |
| |
| const cc::Layer* blackLayer = contentLayerAt(3); |
| EXPECT_THAT(blackLayer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 100, 100), Color::black))); |
| EXPECT_EQ(translation(300, 350), blackLayer->screen_space_transform()); |
| |
| EXPECT_EQ(whiteLayer->clip_tree_index(), darkGrayLayer->clip_tree_index()); |
| const cc::ClipNode* outerClip = |
| propertyTrees().clip_tree.Node(whiteLayer->clip_tree_index()); |
| EXPECT_TRUE(outerClip->applies_local_clip); |
| EXPECT_TRUE(outerClip->layers_are_clipped); |
| EXPECT_EQ(gfx::RectF(100, 100, 700, 700), outerClip->clip); |
| |
| EXPECT_EQ(lightGrayLayer->clip_tree_index(), blackLayer->clip_tree_index()); |
| const cc::ClipNode* innerClip = |
| propertyTrees().clip_tree.Node(blackLayer->clip_tree_index()); |
| EXPECT_TRUE(innerClip->applies_local_clip); |
| EXPECT_TRUE(innerClip->layers_are_clipped); |
| EXPECT_EQ(gfx::RectF(200, 200, 700, 100), innerClip->clip); |
| EXPECT_EQ(outerClip->id, innerClip->parent_id); |
| } |
| |
| TEST_F(PaintArtifactCompositorTestWithPropertyTrees, DeeplyNestedClips) { |
| Vector<RefPtr<ClipPaintPropertyNode>> clips; |
| for (unsigned i = 1; i <= 10; i++) { |
| clips.append(ClipPaintPropertyNode::create( |
| clips.isEmpty() ? nullptr : clips.last(), nullptr, |
| FloatRoundedRect(5 * i, 0, 100, 200 - 10 * i))); |
| } |
| |
| TestPaintArtifact artifact; |
| artifact.chunk(nullptr, clips.last(), dummyRootEffect()) |
| .rectDrawing(FloatRect(0, 0, 200, 200), Color::white); |
| update(artifact.build()); |
| |
| // Check the drawing layer. |
| ASSERT_EQ(1u, contentLayerCount()); |
| const cc::Layer* drawingLayer = contentLayerAt(0); |
| EXPECT_THAT(drawingLayer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 200, 200), Color::white))); |
| EXPECT_EQ(gfx::Transform(), drawingLayer->screen_space_transform()); |
| |
| // Check the clip nodes. |
| const cc::ClipNode* clipNode = |
| propertyTrees().clip_tree.Node(drawingLayer->clip_tree_index()); |
| for (auto it = clips.rbegin(); it != clips.rend(); ++it) { |
| const ClipPaintPropertyNode* paintClipNode = it->get(); |
| EXPECT_TRUE(clipNode->applies_local_clip); |
| EXPECT_TRUE(clipNode->layers_are_clipped); |
| EXPECT_EQ(paintClipNode->clipRect().rect(), clipNode->clip); |
| clipNode = propertyTrees().clip_tree.Node(clipNode->parent_id); |
| } |
| } |
| |
| TEST_F(PaintArtifactCompositorTestWithPropertyTrees, SiblingClips) { |
| RefPtr<ClipPaintPropertyNode> commonClip = ClipPaintPropertyNode::create( |
| nullptr, nullptr, FloatRoundedRect(0, 0, 800, 600)); |
| RefPtr<ClipPaintPropertyNode> clip1 = ClipPaintPropertyNode::create( |
| commonClip, nullptr, FloatRoundedRect(0, 0, 400, 600)); |
| RefPtr<ClipPaintPropertyNode> clip2 = ClipPaintPropertyNode::create( |
| commonClip, nullptr, FloatRoundedRect(400, 0, 400, 600)); |
| |
| TestPaintArtifact artifact; |
| artifact.chunk(nullptr, clip1, dummyRootEffect()) |
| .rectDrawing(FloatRect(0, 0, 640, 480), Color::white); |
| artifact.chunk(nullptr, clip2, dummyRootEffect()) |
| .rectDrawing(FloatRect(0, 0, 640, 480), Color::black); |
| update(artifact.build()); |
| |
| ASSERT_EQ(2u, contentLayerCount()); |
| |
| const cc::Layer* whiteLayer = contentLayerAt(0); |
| EXPECT_THAT(whiteLayer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 640, 480), Color::white))); |
| EXPECT_EQ(gfx::Transform(), whiteLayer->screen_space_transform()); |
| const cc::ClipNode* whiteClip = |
| propertyTrees().clip_tree.Node(whiteLayer->clip_tree_index()); |
| EXPECT_TRUE(whiteClip->applies_local_clip); |
| EXPECT_TRUE(whiteClip->layers_are_clipped); |
| ASSERT_EQ(gfx::RectF(0, 0, 400, 600), whiteClip->clip); |
| |
| const cc::Layer* blackLayer = contentLayerAt(1); |
| EXPECT_THAT(blackLayer->GetPicture(), |
| Pointee(drawsRectangle(FloatRect(0, 0, 640, 480), Color::black))); |
| EXPECT_EQ(gfx::Transform(), blackLayer->screen_space_transform()); |
| const cc::ClipNode* blackClip = |
| propertyTrees().clip_tree.Node(blackLayer->clip_tree_index()); |
| EXPECT_TRUE(blackClip->applies_local_clip); |
| EXPECT_TRUE(blackClip->layers_are_clipped); |
| ASSERT_EQ(gfx::RectF(400, 0, 400, 600), blackClip->clip); |
| |
| EXPECT_EQ(whiteClip->parent_id, blackClip->parent_id); |
| const cc::ClipNode* commonClipNode = |
| propertyTrees().clip_tree.Node(whiteClip->parent_id); |
| EXPECT_TRUE(commonClipNode->applies_local_clip); |
| EXPECT_TRUE(commonClipNode->layers_are_clipped); |
| ASSERT_EQ(gfx::RectF(0, 0, 800, 600), commonClipNode->clip); |
| } |
| |
| TEST_F(PaintArtifactCompositorTestWithPropertyTrees, |
| ForeignLayerPassesThrough) { |
| scoped_refptr<cc::Layer> layer = cc::Layer::Create(); |
| |
| TestPaintArtifact artifact; |
| artifact.chunk(PaintChunkProperties()) |
| .foreignLayer(FloatPoint(50, 100), IntSize(400, 300), layer); |
| update(artifact.build()); |
| |
| ASSERT_EQ(1u, contentLayerCount()); |
| EXPECT_EQ(layer, contentLayerAt(0)); |
| EXPECT_EQ(gfx::Size(400, 300), layer->bounds()); |
| EXPECT_EQ(translation(50, 100), layer->screen_space_transform()); |
| } |
| |
| TEST_F(PaintArtifactCompositorTestWithPropertyTrees, EffectTreeConversion) { |
| RefPtr<EffectPaintPropertyNode> effect1 = |
| EffectPaintPropertyNode::create(dummyRootEffect(), 0.5); |
| RefPtr<EffectPaintPropertyNode> effect2 = |
| EffectPaintPropertyNode::create(effect1, 0.3); |
| RefPtr<EffectPaintPropertyNode> effect3 = |
| EffectPaintPropertyNode::create(dummyRootEffect(), 0.2); |
| |
| TestPaintArtifact artifact; |
| artifact.chunk(nullptr, nullptr, effect2.get()) |
| .rectDrawing(FloatRect(0, 0, 100, 100), Color::white); |
| artifact.chunk(nullptr, nullptr, effect1.get()) |
| .rectDrawing(FloatRect(0, 0, 100, 100), Color::white); |
| artifact.chunk(nullptr, nullptr, effect3.get()) |
| .rectDrawing(FloatRect(0, 0, 100, 100), Color::white); |
| update(artifact.build()); |
| |
| ASSERT_EQ(3u, contentLayerCount()); |
| |
| const cc::EffectTree& effectTree = propertyTrees().effect_tree; |
| // Node #0 reserved for null; #1 for root render surface; #2 for |
| // dummyRootEffect, plus 3 nodes for those created by this test. |
| ASSERT_EQ(6u, effectTree.size()); |
| |
| const cc::EffectNode& convertedDummyRootEffect = *effectTree.Node(2); |
| EXPECT_EQ(1, convertedDummyRootEffect.parent_id); |
| |
| const cc::EffectNode& convertedEffect1 = *effectTree.Node(3); |
| EXPECT_EQ(convertedDummyRootEffect.id, convertedEffect1.parent_id); |
| EXPECT_FLOAT_EQ(0.5, convertedEffect1.opacity); |
| |
| const cc::EffectNode& convertedEffect2 = *effectTree.Node(4); |
| EXPECT_EQ(convertedEffect1.id, convertedEffect2.parent_id); |
| EXPECT_FLOAT_EQ(0.3, convertedEffect2.opacity); |
| |
| const cc::EffectNode& convertedEffect3 = *effectTree.Node(5); |
| EXPECT_EQ(convertedDummyRootEffect.id, convertedEffect3.parent_id); |
| EXPECT_FLOAT_EQ(0.2, convertedEffect3.opacity); |
| |
| EXPECT_EQ(convertedEffect2.id, contentLayerAt(0)->effect_tree_index()); |
| EXPECT_EQ(convertedEffect1.id, contentLayerAt(1)->effect_tree_index()); |
| EXPECT_EQ(convertedEffect3.id, contentLayerAt(2)->effect_tree_index()); |
| } |
| |
| TEST_F(PaintArtifactCompositorTestWithPropertyTrees, OneScrollNode) { |
| RefPtr<TransformPaintPropertyNode> scrollTranslation = |
| TransformPaintPropertyNode::create( |
| nullptr, TransformationMatrix().translate(7, 9), FloatPoint3D()); |
| RefPtr<ScrollPaintPropertyNode> scroll = ScrollPaintPropertyNode::create( |
| nullptr, scrollTranslation, IntSize(11, 13), IntSize(27, 31), true, |
| false); |
| |
| TestPaintArtifact artifact; |
| artifact.chunk(scrollTranslation, nullptr, nullptr, scroll) |
| .rectDrawing(FloatRect(11, 13, 17, 19), Color::white); |
| update(artifact.build()); |
| |
| const cc::ScrollTree& scrollTree = propertyTrees().scroll_tree; |
| // Node #0 reserved for null; #1 for root render surface. |
| ASSERT_EQ(3u, scrollTree.size()); |
| const cc::ScrollNode& scrollNode = *scrollTree.Node(2); |
| EXPECT_EQ(gfx::Size(11, 13), scrollNode.scroll_clip_layer_bounds); |
| EXPECT_EQ(gfx::Size(27, 31), scrollNode.bounds); |
| EXPECT_TRUE(scrollNode.user_scrollable_horizontal); |
| EXPECT_FALSE(scrollNode.user_scrollable_vertical); |
| EXPECT_EQ(1, scrollNode.parent_id); |
| |
| const cc::TransformTree& transformTree = propertyTrees().transform_tree; |
| const cc::TransformNode& transformNode = |
| *transformTree.Node(scrollNode.transform_id); |
| EXPECT_TRUE(transformNode.local.IsIdentity()); |
| |
| EXPECT_EQ(gfx::ScrollOffset(-7, -9), |
| scrollTree.current_scroll_offset(contentLayerAt(0)->id())); |
| |
| EXPECT_EQ(MainThreadScrollingReason::kNotScrollingOnMain, |
| scrollNode.main_thread_scrolling_reasons); |
| } |
| |
| TEST_F(PaintArtifactCompositorTestWithPropertyTrees, NestedScrollNodes) { |
| RefPtr<EffectPaintPropertyNode> effect = |
| EffectPaintPropertyNode::create(dummyRootEffect(), 0.5); |
| |
| RefPtr<TransformPaintPropertyNode> scrollTranslationA = |
| TransformPaintPropertyNode::create( |
| nullptr, TransformationMatrix().translate(11, 13), FloatPoint3D()); |
| RefPtr<ScrollPaintPropertyNode> scrollA = ScrollPaintPropertyNode::create( |
| nullptr, scrollTranslationA, IntSize(2, 3), IntSize(5, 7), false, true); |
| scrollA->addMainThreadScrollingReasons( |
| MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects); |
| RefPtr<TransformPaintPropertyNode> scrollTranslationB = |
| TransformPaintPropertyNode::create( |
| scrollTranslationA, TransformationMatrix().translate(37, 41), |
| FloatPoint3D()); |
| RefPtr<ScrollPaintPropertyNode> scrollB = ScrollPaintPropertyNode::create( |
| scrollA, scrollTranslationB, IntSize(19, 23), IntSize(29, 31), true, |
| false); |
| TestPaintArtifact artifact; |
| artifact.chunk(scrollTranslationA, nullptr, effect, scrollA) |
| .rectDrawing(FloatRect(7, 11, 13, 17), Color::white); |
| artifact.chunk(scrollTranslationB, nullptr, effect, scrollB) |
| .rectDrawing(FloatRect(1, 2, 3, 5), Color::white); |
| update(artifact.build()); |
| |
| const cc::ScrollTree& scrollTree = propertyTrees().scroll_tree; |
| // Node #0 reserved for null; #1 for root render surface. |
| ASSERT_EQ(4u, scrollTree.size()); |
| const cc::ScrollNode& scrollNodeA = *scrollTree.Node(2); |
| EXPECT_EQ(gfx::Size(2, 3), scrollNodeA.scroll_clip_layer_bounds); |
| EXPECT_EQ(gfx::Size(5, 7), scrollNodeA.bounds); |
| EXPECT_FALSE(scrollNodeA.user_scrollable_horizontal); |
| EXPECT_TRUE(scrollNodeA.user_scrollable_vertical); |
| EXPECT_EQ(1, scrollNodeA.parent_id); |
| const cc::ScrollNode& scrollNodeB = *scrollTree.Node(3); |
| EXPECT_EQ(gfx::Size(19, 23), scrollNodeB.scroll_clip_layer_bounds); |
| EXPECT_EQ(gfx::Size(29, 31), scrollNodeB.bounds); |
| EXPECT_TRUE(scrollNodeB.user_scrollable_horizontal); |
| EXPECT_FALSE(scrollNodeB.user_scrollable_vertical); |
| EXPECT_EQ(scrollNodeA.id, scrollNodeB.parent_id); |
| |
| const cc::TransformTree& transformTree = propertyTrees().transform_tree; |
| const cc::TransformNode& transformNodeA = |
| *transformTree.Node(scrollNodeA.transform_id); |
| EXPECT_TRUE(transformNodeA.local.IsIdentity()); |
| const cc::TransformNode& transformNodeB = |
| *transformTree.Node(scrollNodeB.transform_id); |
| EXPECT_TRUE(transformNodeB.local.IsIdentity()); |
| |
| EXPECT_EQ(gfx::ScrollOffset(-11, -13), |
| scrollTree.current_scroll_offset(contentLayerAt(0)->id())); |
| EXPECT_EQ(gfx::ScrollOffset(-37, -41), |
| scrollTree.current_scroll_offset(contentLayerAt(1)->id())); |
| |
| EXPECT_TRUE(scrollNodeA.main_thread_scrolling_reasons & |
| MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects); |
| EXPECT_FALSE(scrollNodeB.main_thread_scrolling_reasons & |
| MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects); |
| } |
| |
| } // namespace |
| } // namespace blink |