Commit 3b0118d7 authored by Vladimir Levin's avatar Vladimir Levin Committed by Commit Bot

Add IsParentAlias as a property on paint property tree nodes.

This patch adds a notion of IsParentAlias to paint property tree
nodes.

In this patch, we make the root transform be an "alias for parent".
Also, this adds some tests for various interactions with these nodes,
as well as code in PaintChunksToCcLayer to ensure we don't emit
unnecessary save/concat/restores for these nodes.

We also need to do this for clips and effects, but this patch can
land on its own.

R=trchen@chromium.org, chrishtr@chromium.org

Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_slimming_paint_v2;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: Ibf1a0cd21754dc226f7c19716028eafad63339c9
Reviewed-on: https://chromium-review.googlesource.com/1200382
Commit-Queue: vmpstr <vmpstr@chromium.org>
Reviewed-by: default avatarTien-Ren Chen <trchen@chromium.org>
Reviewed-by: default avatarChris Harrelson <chrishtr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#588898}
parent 9c0e7ea4
...@@ -377,11 +377,13 @@ void ConversionContext::SwitchToClip(const ClipPaintPropertyNode* target_clip) { ...@@ -377,11 +377,13 @@ void ConversionContext::SwitchToClip(const ClipPaintPropertyNode* target_clip) {
void ConversionContext::StartClip( void ConversionContext::StartClip(
const FloatRoundedRect& combined_clip_rect, const FloatRoundedRect& combined_clip_rect,
const ClipPaintPropertyNode* lowest_combined_clip_node) { const ClipPaintPropertyNode* lowest_combined_clip_node) {
if (lowest_combined_clip_node->LocalTransformSpace() != current_transform_) auto* local_transform =
lowest_combined_clip_node->LocalTransformSpace()->Unalias();
if (local_transform != current_transform_)
EndTransform(); EndTransform();
cc_list_.StartPaint(); cc_list_.StartPaint();
cc_list_.push<cc::SaveOp>(); cc_list_.push<cc::SaveOp>();
ApplyTransform(lowest_combined_clip_node->LocalTransformSpace()); ApplyTransform(local_transform);
const bool antialias = true; const bool antialias = true;
if (combined_clip_rect.IsRounded()) { if (combined_clip_rect.IsRounded()) {
cc_list_.push<cc::ClipRRectOp>(combined_clip_rect, SkClipOp::kIntersect, cc_list_.push<cc::ClipRRectOp>(combined_clip_rect, SkClipOp::kIntersect,
...@@ -399,7 +401,7 @@ void ConversionContext::StartClip( ...@@ -399,7 +401,7 @@ void ConversionContext::StartClip(
PushState(StateEntry::kClip, 1); PushState(StateEntry::kClip, 1);
current_clip_ = lowest_combined_clip_node; current_clip_ = lowest_combined_clip_node;
current_transform_ = lowest_combined_clip_node->LocalTransformSpace(); current_transform_ = local_transform;
} }
void ConversionContext::SwitchToEffect( void ConversionContext::SwitchToEffect(
...@@ -608,6 +610,7 @@ void ConversionContext::PopState() { ...@@ -608,6 +610,7 @@ void ConversionContext::PopState() {
void ConversionContext::SwitchToTransform( void ConversionContext::SwitchToTransform(
const TransformPaintPropertyNode* target_transform) { const TransformPaintPropertyNode* target_transform) {
target_transform = target_transform->Unalias();
if (target_transform == current_transform_) if (target_transform == current_transform_)
return; return;
......
...@@ -963,5 +963,146 @@ TEST_F(PaintChunksToCcLayerTest, NestedEffectsWithSameTransform) { ...@@ -963,5 +963,146 @@ TEST_F(PaintChunksToCcLayerTest, NestedEffectsWithSameTransform) {
cc::PaintOpType::Restore})); // </t1> cc::PaintOpType::Restore})); // </t1>
} }
TEST_F(PaintChunksToCcLayerTest, NoopTransformIsNotEmitted) {
auto t1 = CreateTransform(t0(), TransformationMatrix().Scale(2.f));
auto noop_t2 = TransformPaintPropertyNode::CreateAlias(*t1);
auto noop_t3 = TransformPaintPropertyNode::CreateAlias(*noop_t2);
auto t4 = CreateTransform(*noop_t3, TransformationMatrix().Scale(2.f));
auto noop_t5 = TransformPaintPropertyNode::CreateAlias(*t4);
TestChunks chunks;
chunks.AddChunk(t0(), c0(), e0());
chunks.AddChunk(*t1, c0(), e0());
chunks.AddChunk(*noop_t2, c0(), e0());
chunks.AddChunk(*noop_t3, c0(), e0());
chunks.AddChunk(*noop_t2, c0(), e0());
chunks.AddChunk(*t4, c0(), e0());
chunks.AddChunk(*noop_t5, c0(), e0());
chunks.AddChunk(*t4, c0(), e0());
auto output =
PaintChunksToCcLayer::Convert(
chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(),
chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
->ReleaseAsRecord();
EXPECT_THAT(*output,
PaintRecordMatcher::Make({
cc::PaintOpType::DrawRecord, // draw with t0
cc::PaintOpType::Save, cc::PaintOpType::Concat, // t1
cc::PaintOpType::DrawRecord, // draw with t1
cc::PaintOpType::DrawRecord, // draw with noop_t2
cc::PaintOpType::DrawRecord, // draw with noop_t3
cc::PaintOpType::DrawRecord, // draw with noop_t2
cc::PaintOpType::Restore, // end t1
cc::PaintOpType::Save, cc::PaintOpType::Concat, // t4
cc::PaintOpType::DrawRecord, // draw with t4
cc::PaintOpType::DrawRecord, // draw with noop_t5
cc::PaintOpType::DrawRecord, // draw with t4
cc::PaintOpType::Restore // end t4
}));
}
TEST_F(PaintChunksToCcLayerTest, OnlyNoopTransformIsNotEmitted) {
auto noop_t1 = TransformPaintPropertyNode::CreateAlias(t0());
auto noop_t2 = TransformPaintPropertyNode::CreateAlias(*noop_t1);
TestChunks chunks;
chunks.AddChunk(t0(), c0(), e0());
chunks.AddChunk(*noop_t1, c0(), e0());
chunks.AddChunk(*noop_t2, c0(), e0());
auto output =
PaintChunksToCcLayer::Convert(
chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(),
chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
->ReleaseAsRecord();
EXPECT_THAT(*output, PaintRecordMatcher::Make({cc::PaintOpType::DrawRecord,
cc::PaintOpType::DrawRecord,
cc::PaintOpType::DrawRecord}));
}
TEST_F(PaintChunksToCcLayerTest, NoopTransformFirstThenBackToParent) {
auto t1 = CreateTransform(t0(), TransformationMatrix().Scale(2));
auto noop_t2 = TransformPaintPropertyNode::CreateAlias(*t1);
TestChunks chunks;
chunks.AddChunk(t0(), c0(), e0());
chunks.AddChunk(*noop_t2, c0(), e0());
chunks.AddChunk(*t1, c0(), e0());
auto output =
PaintChunksToCcLayer::Convert(
chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(),
chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
->ReleaseAsRecord();
EXPECT_THAT(*output, PaintRecordMatcher::Make({
cc::PaintOpType::DrawRecord, // t0
cc::PaintOpType::Save,
cc::PaintOpType::Concat, // t1 + noop_t2
cc::PaintOpType::DrawRecord, // draw with above
cc::PaintOpType::DrawRecord, // draw with just t1
cc::PaintOpType::Restore // end t1
}));
}
TEST_F(PaintChunksToCcLayerTest, ClipUndoesNoopTransform) {
auto t1 = CreateTransform(t0(), TransformationMatrix().Scale(2));
auto noop_t2 = TransformPaintPropertyNode::CreateAlias(*t1);
auto c1 = CreateClip(c0(), t1.get(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
TestChunks chunks;
chunks.AddChunk(t0(), c0(), e0());
chunks.AddChunk(*noop_t2, c0(), e0());
// The clip's local transform is t1, which is the parent of noop_t2.
chunks.AddChunk(*noop_t2, *c1, e0());
auto output =
PaintChunksToCcLayer::Convert(
chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(),
chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
->ReleaseAsRecord();
EXPECT_THAT(*output, PaintRecordMatcher::Make({
cc::PaintOpType::DrawRecord, // t0
cc::PaintOpType::Save,
cc::PaintOpType::Concat, // t1 + noop_t2
cc::PaintOpType::DrawRecord, cc::PaintOpType::Save,
cc::PaintOpType::ClipRect, // c1 (with t1 space)
cc::PaintOpType::DrawRecord,
cc::PaintOpType::Restore, // end c1
cc::PaintOpType::Restore // end t1
}));
}
TEST_F(PaintChunksToCcLayerTest, EffectUndoesNoopTransform) {
auto t1 = CreateTransform(t0(), TransformationMatrix().Scale(2));
auto noop_t2 = TransformPaintPropertyNode::CreateAlias(*t1);
auto e1 = CreateOpacityEffect(e0(), t1.get(), &c0(), 0.5);
TestChunks chunks;
chunks.AddChunk(t0(), c0(), e0());
chunks.AddChunk(*noop_t2, c0(), e0());
// The effects's local transform is t1, which is the parent of noop_t2.
chunks.AddChunk(*noop_t2, c0(), *e1);
auto output =
PaintChunksToCcLayer::Convert(
chunks.chunks, PropertyTreeState::Root(), gfx::Vector2dF(),
chunks.items, cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
->ReleaseAsRecord();
EXPECT_THAT(*output, PaintRecordMatcher::Make({
cc::PaintOpType::DrawRecord, // t0
cc::PaintOpType::Save,
cc::PaintOpType::Concat, // t1 + noop_t2
cc::PaintOpType::DrawRecord,
cc::PaintOpType::SaveLayerAlpha, // e1
cc::PaintOpType::DrawRecord,
cc::PaintOpType::Restore, // end e1
cc::PaintOpType::Restore // end t1
}));
}
} // namespace } // namespace
} // namespace blink } // namespace blink
...@@ -41,8 +41,7 @@ class PLATFORM_EXPORT ClipPaintPropertyNode ...@@ -41,8 +41,7 @@ class PLATFORM_EXPORT ClipPaintPropertyNode
// overlay scrollbars which is only used for hit testing. // overlay scrollbars which is only used for hit testing.
bool EqualIgnoringHitTestRects(const State& o) const { bool EqualIgnoringHitTestRects(const State& o) const {
return local_transform_space == o.local_transform_space && return local_transform_space == o.local_transform_space &&
clip_rect == o.clip_rect && clip_rect == o.clip_rect && clip_path == o.clip_path &&
clip_path == o.clip_path &&
direct_compositing_reasons == o.direct_compositing_reasons; direct_compositing_reasons == o.direct_compositing_reasons;
} }
......
...@@ -74,6 +74,21 @@ class PaintPropertyNode : public RefCounted<NodeType> { ...@@ -74,6 +74,21 @@ class PaintPropertyNode : public RefCounted<NodeType> {
n->changed_ = false; n->changed_ = false;
} }
// Returns true if this node is an alias for its parent. A parent alias is a
// node which on its own does not contribute to the rendering output, and only
// exists to enforce a particular structure of the paint property tree. Its
// value is ignored during display item list generation, instead the parent
// value is used. See Unalias().
bool IsParentAlias() const { return is_parent_alias_; }
// Returns the first node up the parent chain that is not an alias; return the
// root node if every node is an alias.
const NodeType* Unalias() const {
const auto* node = static_cast<const NodeType*>(this);
while (node->Parent() && node->IsParentAlias())
node = node->Parent();
return node;
}
String ToString() const { String ToString() const {
auto s = static_cast<const NodeType*>(this)->ToJSON()->ToJSONString(); auto s = static_cast<const NodeType*>(this)->ToJSON()->ToJSONString();
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
...@@ -91,8 +106,10 @@ class PaintPropertyNode : public RefCounted<NodeType> { ...@@ -91,8 +106,10 @@ class PaintPropertyNode : public RefCounted<NodeType> {
#endif #endif
protected: protected:
PaintPropertyNode(const NodeType* parent) PaintPropertyNode(const NodeType* parent, bool is_parent_alias = false)
: parent_(parent), changed_(!!parent) {} : parent_(parent),
is_parent_alias_(is_parent_alias),
changed_(!!parent) {}
bool SetParent(const NodeType* parent) { bool SetParent(const NodeType* parent) {
DCHECK(!IsRoot()); DCHECK(!IsRoot());
...@@ -115,7 +132,11 @@ class PaintPropertyNode : public RefCounted<NodeType> { ...@@ -115,7 +132,11 @@ class PaintPropertyNode : public RefCounted<NodeType> {
friend class PaintPropertyNodeTest; friend class PaintPropertyNodeTest;
scoped_refptr<const NodeType> parent_; scoped_refptr<const NodeType> parent_;
mutable bool changed_; // Indicates whether this node is an alias for its parent. Parent aliases are
// nodes that do not affect rendering and are ignored for the purposes of
// display item list generation.
bool is_parent_alias_ = false;
mutable bool changed_ = true;
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
String debug_name_; String debug_name_;
......
...@@ -15,7 +15,8 @@ const TransformPaintPropertyNode& TransformPaintPropertyNode::Root() { ...@@ -15,7 +15,8 @@ const TransformPaintPropertyNode& TransformPaintPropertyNode::Root() {
nullptr, nullptr,
State{TransformationMatrix(), FloatPoint3D(), false, State{TransformationMatrix(), FloatPoint3D(), false,
BackfaceVisibility::kVisible, 0, CompositingReason::kNone, BackfaceVisibility::kVisible, 0, CompositingReason::kNone,
CompositorElementId(), &ScrollPaintPropertyNode::Root()}))); CompositorElementId(), &ScrollPaintPropertyNode::Root()},
true /* is_parent_alias */)));
return *root; return *root;
} }
......
...@@ -71,8 +71,13 @@ class PLATFORM_EXPORT TransformPaintPropertyNode ...@@ -71,8 +71,13 @@ class PLATFORM_EXPORT TransformPaintPropertyNode
static scoped_refptr<TransformPaintPropertyNode> Create( static scoped_refptr<TransformPaintPropertyNode> Create(
const TransformPaintPropertyNode& parent, const TransformPaintPropertyNode& parent,
State&& state) { State&& state) {
return base::AdoptRef( return base::AdoptRef(new TransformPaintPropertyNode(
new TransformPaintPropertyNode(&parent, std::move(state))); &parent, std::move(state), false /* is_parent_alias */));
}
static scoped_refptr<TransformPaintPropertyNode> CreateAlias(
const TransformPaintPropertyNode& parent) {
return base::AdoptRef(new TransformPaintPropertyNode(
&parent, State{}, true /* is_parent_alias */));
} }
bool Update(const TransformPaintPropertyNode& parent, State&& state) { bool Update(const TransformPaintPropertyNode& parent, State&& state) {
...@@ -80,6 +85,7 @@ class PLATFORM_EXPORT TransformPaintPropertyNode ...@@ -80,6 +85,7 @@ class PLATFORM_EXPORT TransformPaintPropertyNode
if (state == state_) if (state == state_)
return parent_changed; return parent_changed;
DCHECK(!IsParentAlias()) << "Changed the state of an alias node.";
SetChanged(); SetChanged();
state_ = std::move(state); state_ = std::move(state);
Validate(); Validate();
...@@ -145,14 +151,15 @@ class PLATFORM_EXPORT TransformPaintPropertyNode ...@@ -145,14 +151,15 @@ class PLATFORM_EXPORT TransformPaintPropertyNode
// The clone function is used by FindPropertiesNeedingUpdate.h for recording // The clone function is used by FindPropertiesNeedingUpdate.h for recording
// a transform node before it has been updated, to later detect changes. // a transform node before it has been updated, to later detect changes.
scoped_refptr<TransformPaintPropertyNode> Clone() const { scoped_refptr<TransformPaintPropertyNode> Clone() const {
return base::AdoptRef( return base::AdoptRef(new TransformPaintPropertyNode(
new TransformPaintPropertyNode(Parent(), State(state_))); Parent(), State(state_), IsParentAlias()));
} }
// The equality operator is used by FindPropertiesNeedingUpdate.h for checking // The equality operator is used by FindPropertiesNeedingUpdate.h for checking
// if a transform node has changed. // if a transform node has changed.
bool operator==(const TransformPaintPropertyNode& o) const { bool operator==(const TransformPaintPropertyNode& o) const {
return Parent() == o.Parent() && state_ == o.state_; return Parent() == o.Parent() && state_ == o.state_ &&
IsParentAlias() == o.IsParentAlias();
} }
#endif #endif
...@@ -163,8 +170,9 @@ class PLATFORM_EXPORT TransformPaintPropertyNode ...@@ -163,8 +170,9 @@ class PLATFORM_EXPORT TransformPaintPropertyNode
private: private:
TransformPaintPropertyNode(const TransformPaintPropertyNode* parent, TransformPaintPropertyNode(const TransformPaintPropertyNode* parent,
State&& state) State&& state,
: PaintPropertyNode(parent), state_(std::move(state)) { bool is_parent_alias)
: PaintPropertyNode(parent, is_parent_alias), state_(std::move(state)) {
Validate(); Validate();
} }
......
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