Commit 2175fae8 authored by vollick's avatar vollick Committed by Commit bot

Reconcile property tree and CDP layer skipping logic

BUG=477721

Review URL: https://codereview.chromium.org/1056793009

Cr-Commit-Position: refs/heads/master@{#327124}
parent 04bfc5ea
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "cc/base/math_util.h" #include "cc/base/math_util.h"
#include "cc/layers/layer.h" #include "cc/layers/layer.h"
#include "cc/layers/layer_impl.h" #include "cc/layers/layer_impl.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/property_tree.h" #include "cc/trees/property_tree.h"
#include "cc/trees/property_tree_builder.h" #include "cc/trees/property_tree_builder.h"
#include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/rect_conversions.h"
...@@ -157,72 +158,154 @@ static bool TransformToScreenIsKnown(LayerType* layer, ...@@ -157,72 +158,154 @@ static bool TransformToScreenIsKnown(LayerType* layer,
} }
template <typename LayerType> template <typename LayerType>
static bool IsLayerBackFaceExposed(LayerType* layer, static bool HasSingularTransform(LayerType* layer, const TransformTree& tree) {
const TransformTree& tree) { const TransformNode* node = tree.Node(layer->transform_tree_index());
if (!TransformToScreenIsKnown(layer, tree)) return !node->data.is_invertible || !node->data.ancestors_are_invertible;
return false;
if (LayerIsInExisting3DRenderingContext(layer))
return DrawTransformFromPropertyTrees(layer, tree).IsBackFaceVisible();
return layer->transform().IsBackFaceVisible();
} }
template <typename LayerType> template <typename LayerType>
static bool IsSurfaceBackFaceExposed(LayerType* layer, static bool IsLayerBackFaceVisible(LayerType* layer,
const TransformTree& tree) { const TransformTree& tree) {
if (!TransformToScreenIsKnown(layer, tree)) // The current W3C spec on CSS transforms says that backface visibility should
return false; // be determined differently depending on whether the layer is in a "3d
// rendering context" or not. For Chromium code, we can determine whether we
// are in a 3d rendering context by checking if the parent preserves 3d.
if (LayerIsInExisting3DRenderingContext(layer)) if (LayerIsInExisting3DRenderingContext(layer))
return DrawTransformFromPropertyTrees(layer, tree).IsBackFaceVisible(); return DrawTransformFromPropertyTrees(layer, tree).IsBackFaceVisible();
if (IsRootLayerOfNewRenderingContext(layer)) // In this case, either the layer establishes a new 3d rendering context, or
// is not in a 3d rendering context at all.
return layer->transform().IsBackFaceVisible(); return layer->transform().IsBackFaceVisible();
// If the render_surface is not part of a new or existing rendering context,
// then the layers that contribute to this surface will decide back-face
// visibility for themselves.
return false;
} }
template <typename LayerType> template <typename LayerType>
static bool HasSingularTransform(LayerType* layer, const TransformTree& tree) { static bool IsAnimatingTransformToScreen(LayerType* layer,
const TransformTree& tree) {
const TransformNode* node = tree.Node(layer->transform_tree_index()); const TransformNode* node = tree.Node(layer->transform_tree_index());
return !node->data.is_invertible || !node->data.ancestors_are_invertible; return node->data.to_screen_is_animated;
} }
template <typename LayerType> static inline bool TransformToScreenIsKnown(Layer* layer,
static bool IsBackFaceInvisible(LayerType* layer, const TransformTree& tree) { const TransformTree& tree) {
LayerType* backface_test_layer = layer; return !IsAnimatingTransformToScreen(layer, tree);
if (layer->use_parent_backface_visibility()) {
DCHECK(layer->parent());
DCHECK(!layer->parent()->use_parent_backface_visibility());
backface_test_layer = layer->parent();
}
return !backface_test_layer->double_sided() &&
IsLayerBackFaceExposed(backface_test_layer, tree);
} }
template <typename LayerType> static inline bool TransformToScreenIsKnown(LayerImpl* layer,
static bool IsAnimatingTransformToScreen(LayerType* layer,
const TransformTree& tree) { const TransformTree& tree) {
const TransformNode* node = tree.Node(layer->transform_tree_index()); return true;
return node->data.to_screen_is_animated;
} }
template <typename LayerType> template <typename LayerType>
static bool IsInvisibleDueToTransform(LayerType* layer, static bool HasInvertibleOrAnimatedTransform(LayerType* layer) {
const TransformTree& tree) { return layer->transform_is_invertible() || layer->TransformIsAnimating();
if (IsAnimatingTransformToScreen(layer, tree)) }
static inline bool SubtreeShouldBeSkipped(LayerImpl* layer,
bool layer_is_drawn) {
// If the layer transform is not invertible, it should not be drawn.
// TODO(ajuma): Correctly process subtrees with singular transform for the
// case where we may animate to a non-singular transform and wish to
// pre-raster.
if (!HasInvertibleOrAnimatedTransform(layer))
return true;
// When we need to do a readback/copy of a layer's output, we can not skip
// it or any of its ancestors.
if (layer->draw_properties().layer_or_descendant_has_copy_request)
return false;
// We cannot skip the the subtree if a descendant has a wheel or touch handler
// or the hit testing code will break (it requires fresh transforms, etc).
if (layer->draw_properties().layer_or_descendant_has_input_handler)
return false;
// If the layer is not drawn, then skip it and its subtree.
if (!layer_is_drawn)
return true;
// If layer is on the pending tree and opacity is being animated then
// this subtree can't be skipped as we need to create, prioritize and
// include tiles for this layer when deciding if tree can be activated.
if (layer->layer_tree_impl()->IsPendingTree() && layer->OpacityIsAnimating())
return false; return false;
return HasSingularTransform(layer, tree) || IsBackFaceInvisible(layer, tree);
// The opacity of a layer always applies to its children (either implicitly
// via a render surface or explicitly if the parent preserves 3D), so the
// entire subtree can be skipped if this layer is fully transparent.
return !layer->opacity();
} }
bool LayerIsInvisible(const Layer* layer) { static inline bool SubtreeShouldBeSkipped(Layer* layer, bool layer_is_drawn) {
// If the layer transform is not invertible, it should not be drawn.
if (!layer->transform_is_invertible() && !layer->TransformIsAnimating())
return true;
// When we need to do a readback/copy of a layer's output, we can not skip
// it or any of its ancestors.
if (layer->draw_properties().layer_or_descendant_has_copy_request)
return false;
// We cannot skip the the subtree if a descendant has a wheel or touch handler
// or the hit testing code will break (it requires fresh transforms, etc).
if (layer->draw_properties().layer_or_descendant_has_input_handler)
return false;
// If the layer is not drawn, then skip it and its subtree.
if (!layer_is_drawn)
return true;
// If the opacity is being animated then the opacity on the main thread is
// unreliable (since the impl thread may be using a different opacity), so it
// should not be trusted.
// In particular, it should not cause the subtree to be skipped.
// Similarly, for layers that might animate opacity using an impl-only
// animation, their subtree should also not be skipped.
return !layer->opacity() && !layer->OpacityIsAnimating() && return !layer->opacity() && !layer->OpacityIsAnimating() &&
!layer->OpacityCanAnimateOnImplThread(); !layer->OpacityCanAnimateOnImplThread();
} }
bool LayerIsInvisible(const LayerImpl* layer) { template <typename LayerType>
return !layer->opacity() && !layer->OpacityIsAnimating(); static bool LayerShouldBeSkipped(LayerType* layer,
bool layer_is_drawn,
const TransformTree& tree) {
// Layers can be skipped if any of these conditions are met.
// - is not drawn due to it or one of its ancestors being hidden (or having
// no copy requests).
// - does not draw content.
// - is transparent.
// - has empty bounds
// - the layer is not double-sided, but its back face is visible.
//
// Some additional conditions need to be computed at a later point after the
// recursion is finished.
// - the intersection of render_surface content and layer clip_rect is empty
// - the visible_content_rect is empty
//
// Note, if the layer should not have been drawn due to being fully
// transparent, we would have skipped the entire subtree and never made it
// into this function, so it is safe to omit this check here.
if (!layer_is_drawn)
return true;
if (!layer->DrawsContent() || layer->bounds().IsEmpty())
return true;
LayerType* backface_test_layer = layer;
if (layer->use_parent_backface_visibility()) {
DCHECK(layer->parent());
DCHECK(!layer->parent()->use_parent_backface_visibility());
backface_test_layer = layer->parent();
}
// The layer should not be drawn if (1) it is not double-sided and (2) the
// back of the layer is known to be facing the screen.
if (!backface_test_layer->double_sided() &&
TransformToScreenIsKnown(backface_test_layer, tree) &&
IsLayerBackFaceVisible(backface_test_layer, tree))
return true;
return false;
} }
template <typename LayerType> template <typename LayerType>
...@@ -230,24 +313,15 @@ void FindLayersThatNeedVisibleRects(LayerType* layer, ...@@ -230,24 +313,15 @@ void FindLayersThatNeedVisibleRects(LayerType* layer,
const TransformTree& tree, const TransformTree& tree,
bool subtree_is_visible_from_ancestor, bool subtree_is_visible_from_ancestor,
std::vector<LayerType*>* layers_to_update) { std::vector<LayerType*>* layers_to_update) {
const bool layer_is_invisible = LayerIsInvisible(layer);
const bool layer_is_backfacing =
(layer->has_render_surface() && !layer->double_sided() &&
IsSurfaceBackFaceExposed(layer, tree));
const bool subtree_is_invisble = layer_is_invisible || layer_is_backfacing;
if (subtree_is_invisble)
return;
bool layer_is_drawn = bool layer_is_drawn =
layer->HasCopyRequest() || layer->HasCopyRequest() ||
(subtree_is_visible_from_ancestor && !layer->hide_layer_and_subtree()); (subtree_is_visible_from_ancestor && !layer->hide_layer_and_subtree());
if (layer_is_drawn && layer->DrawsContent()) { if (layer->parent() && SubtreeShouldBeSkipped(layer, layer_is_drawn))
const bool visible = !IsInvisibleDueToTransform(layer, tree); return;
if (visible)
if (!LayerShouldBeSkipped(layer, layer_is_drawn, tree))
layers_to_update->push_back(layer); layers_to_update->push_back(layer);
}
for (size_t i = 0; i < layer->children().size(); ++i) { for (size_t i = 0; i < layer->children().size(); ++i) {
FindLayersThatNeedVisibleRects(layer->child_at(i), tree, layer_is_drawn, FindLayersThatNeedVisibleRects(layer->child_at(i), tree, layer_is_drawn,
......
...@@ -21,6 +21,9 @@ ...@@ -21,6 +21,9 @@
#include "cc/output/copy_output_request.h" #include "cc/output/copy_output_request.h"
#include "cc/output/copy_output_result.h" #include "cc/output/copy_output_result.h"
#include "cc/test/animation_test_common.h" #include "cc/test/animation_test_common.h"
#include "cc/test/fake_content_layer.h"
#include "cc/test/fake_content_layer_client.h"
#include "cc/test/fake_content_layer_impl.h"
#include "cc/test/fake_impl_proxy.h" #include "cc/test/fake_impl_proxy.h"
#include "cc/test/fake_layer_tree_host.h" #include "cc/test/fake_layer_tree_host.h"
#include "cc/test/fake_layer_tree_host_impl.h" #include "cc/test/fake_layer_tree_host_impl.h"
...@@ -9258,5 +9261,213 @@ TEST_F(LayerTreeHostCommonTest, UpdateScrollChildPosition) { ...@@ -9258,5 +9261,213 @@ TEST_F(LayerTreeHostCommonTest, UpdateScrollChildPosition) {
scroll_child->visible_rect_from_property_trees()); scroll_child->visible_rect_from_property_trees());
} }
static void CopyOutputCallback(scoped_ptr<CopyOutputResult> result) {
}
TEST_F(LayerTreeHostCommonTest, SkippingSubtreeMain) {
gfx::Transform identity;
FakeContentLayerClient client;
scoped_refptr<Layer> root = Layer::Create();
scoped_refptr<LayerWithForcedDrawsContent> child =
make_scoped_refptr(new LayerWithForcedDrawsContent());
scoped_refptr<LayerWithForcedDrawsContent> grandchild =
make_scoped_refptr(new LayerWithForcedDrawsContent());
scoped_refptr<FakeContentLayer> greatgrandchild(
FakeContentLayer::Create(&client));
SetLayerPropertiesForTesting(root.get(), identity, gfx::Point3F(),
gfx::PointF(), gfx::Size(100, 100), true, false);
SetLayerPropertiesForTesting(child.get(), identity, gfx::Point3F(),
gfx::PointF(), gfx::Size(10, 10), true, false);
SetLayerPropertiesForTesting(grandchild.get(), identity, gfx::Point3F(),
gfx::PointF(), gfx::Size(10, 10), true, false);
SetLayerPropertiesForTesting(greatgrandchild.get(), identity, gfx::Point3F(),
gfx::PointF(), gfx::Size(10, 10), true, false);
root->AddChild(child);
child->AddChild(grandchild);
grandchild->AddChild(greatgrandchild);
scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
host->SetRootLayer(root);
// Check the non-skipped case.
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(10, 10), grandchild->visible_rect_from_property_trees());
// Now we will reset the visible rect from property trees for the grandchild,
// and we will configure |child| in several ways that should force the subtree
// to be skipped. The visible content rect for |grandchild| should, therefore,
// remain empty.
grandchild->set_visible_rect_from_property_trees(gfx::Rect());
gfx::Transform singular;
singular.matrix().set(0, 0, 0);
child->SetTransform(singular);
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(0, 0), grandchild->visible_rect_from_property_trees());
child->SetTransform(identity);
child->SetHideLayerAndSubtree(true);
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(0, 0), grandchild->visible_rect_from_property_trees());
child->SetHideLayerAndSubtree(false);
child->SetOpacity(0.f);
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(0, 0), grandchild->visible_rect_from_property_trees());
// Now, even though child has zero opacity, we will configure |grandchild| and
// |greatgrandchild| in several ways that should force the subtree to be
// processed anyhow.
grandchild->SetTouchEventHandlerRegion(Region(gfx::Rect(0, 0, 10, 10)));
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(10, 10), grandchild->visible_rect_from_property_trees());
grandchild->set_visible_rect_from_property_trees(gfx::Rect());
grandchild->SetTouchEventHandlerRegion(Region());
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(0, 0), grandchild->visible_rect_from_property_trees());
grandchild->set_visible_rect_from_property_trees(gfx::Rect());
greatgrandchild->RequestCopyOfOutput(
CopyOutputRequest::CreateBitmapRequest(base::Bind(&CopyOutputCallback)));
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(10, 10), grandchild->visible_rect_from_property_trees());
}
TEST_F(LayerTreeHostCommonTest, SkippingSubtreeImpl) {
FakeImplProxy proxy;
TestSharedBitmapManager shared_bitmap_manager;
FakeLayerTreeHostImpl host_impl(&proxy, &shared_bitmap_manager, nullptr);
gfx::Transform identity;
scoped_ptr<LayerImpl> root = LayerImpl::Create(host_impl.active_tree(), 1);
scoped_ptr<LayerImpl> child = LayerImpl::Create(host_impl.active_tree(), 2);
scoped_ptr<LayerImpl> grandchild =
LayerImpl::Create(host_impl.active_tree(), 3);
scoped_ptr<FakeContentLayerImpl> greatgrandchild(
FakeContentLayerImpl::Create(host_impl.active_tree(), 4));
child->SetDrawsContent(true);
grandchild->SetDrawsContent(true);
greatgrandchild->SetDrawsContent(true);
SetLayerPropertiesForTesting(root.get(), identity, gfx::Point3F(),
gfx::PointF(), gfx::Size(100, 100), true, false,
true);
SetLayerPropertiesForTesting(child.get(), identity, gfx::Point3F(),
gfx::PointF(), gfx::Size(10, 10), true, false,
false);
SetLayerPropertiesForTesting(grandchild.get(), identity, gfx::Point3F(),
gfx::PointF(), gfx::Size(10, 10), true, false,
false);
SetLayerPropertiesForTesting(greatgrandchild.get(), identity, gfx::Point3F(),
gfx::PointF(), gfx::Size(10, 10), true, false,
true);
LayerImpl* child_ptr = child.get();
LayerImpl* grandchild_ptr = grandchild.get();
LayerImpl* greatgrandchild_ptr = greatgrandchild.get();
grandchild->AddChild(greatgrandchild.Pass());
child->AddChild(grandchild.Pass());
root->AddChild(child.Pass());
// Check the non-skipped case.
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(10, 10),
grandchild_ptr->visible_rect_from_property_trees());
// Now we will reset the visible rect from property trees for the grandchild,
// and we will configure |child| in several ways that should force the subtree
// to be skipped. The visible content rect for |grandchild| should, therefore,
// remain empty.
grandchild_ptr->set_visible_rect_from_property_trees(gfx::Rect());
gfx::Transform singular;
singular.matrix().set(0, 0, 0);
child_ptr->SetTransform(singular);
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(0, 0),
grandchild_ptr->visible_rect_from_property_trees());
child_ptr->SetTransform(identity);
child_ptr->SetHideLayerAndSubtree(true);
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(0, 0),
grandchild_ptr->visible_rect_from_property_trees());
child_ptr->SetHideLayerAndSubtree(false);
child_ptr->SetOpacity(0.f);
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(0, 0),
grandchild_ptr->visible_rect_from_property_trees());
// Now, even though child has zero opacity, we will configure |grandchild| and
// |greatgrandchild| in several ways that should force the subtree to be
// processed anyhow.
grandchild_ptr->SetTouchEventHandlerRegion(Region(gfx::Rect(0, 0, 10, 10)));
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(10, 10),
grandchild_ptr->visible_rect_from_property_trees());
grandchild_ptr->set_visible_rect_from_property_trees(gfx::Rect());
grandchild_ptr->SetTouchEventHandlerRegion(Region());
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(0, 0),
grandchild_ptr->visible_rect_from_property_trees());
grandchild_ptr->set_visible_rect_from_property_trees(gfx::Rect());
ScopedPtrVector<CopyOutputRequest> requests;
requests.push_back(CopyOutputRequest::CreateEmptyRequest());
greatgrandchild_ptr->PassCopyRequests(&requests);
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(10, 10),
grandchild_ptr->visible_rect_from_property_trees());
}
TEST_F(LayerTreeHostCommonTest, SkippingLayer) {
gfx::Transform identity;
FakeContentLayerClient client;
scoped_refptr<Layer> root = Layer::Create();
scoped_refptr<LayerWithForcedDrawsContent> child =
make_scoped_refptr(new LayerWithForcedDrawsContent());
SetLayerPropertiesForTesting(root.get(), identity, gfx::Point3F(),
gfx::PointF(), gfx::Size(100, 100), true, false);
SetLayerPropertiesForTesting(child.get(), identity, gfx::Point3F(),
gfx::PointF(), gfx::Size(10, 10), true, false);
root->AddChild(child);
scoped_ptr<FakeLayerTreeHost> host(CreateFakeLayerTreeHost());
host->SetRootLayer(root);
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(10, 10), child->visible_rect_from_property_trees());
child->set_visible_rect_from_property_trees(gfx::Rect());
child->SetHideLayerAndSubtree(true);
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(0, 0), child->visible_rect_from_property_trees());
child->SetHideLayerAndSubtree(false);
child->SetBounds(gfx::Size());
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(0, 0), child->visible_rect_from_property_trees());
child->SetBounds(gfx::Size(10, 10));
gfx::Transform rotate;
child->SetDoubleSided(false);
rotate.RotateAboutXAxis(180.f);
child->SetTransform(rotate);
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(0, 0), child->visible_rect_from_property_trees());
child->SetDoubleSided(true);
child->SetTransform(identity);
child->SetOpacity(0.f);
ExecuteCalculateDrawProperties(root.get());
EXPECT_EQ(gfx::Rect(0, 0), child->visible_rect_from_property_trees());
}
} // namespace } // namespace
} // namespace cc } // namespace cc
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment