Commit d7159f19 authored by Xianzhu Wang's avatar Xianzhu Wang Committed by Commit Bot

[PE] Detect clip and effect node change in their transform space etc.

Previously, if a clip was between two transforms, and the clip result
was not tight, and the combination of the two transforms didn't change,
we missed raster invalidation for the changed effective clip.

Now check for change of LocalTransformSpace for clip and effect nodes.

Bug: 848782
Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_slimming_paint_v2;master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2
Change-Id: I1b60ecc2bc79cba69c667e188053b024de670361
Reviewed-on: https://chromium-review.googlesource.com/1094202
Commit-Queue: Xianzhu Wang <wangxianzhu@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@{#567045}
parent 8a4de1d2
<!DOCTYPE html>
<div style="width: 0; height: 0; transform: rotate(5deg) translateX(200px)">
<div style="width: 200px; height: 200px; overflow: hidden">
<div style="width: 400px; height: 400px; background: blue; transform: translateX(-200px)">
</div>
</div>
</div>
<!DOCTYPE html>
<div id="outer" style="width: 0; height: 0; transform: rotate(5deg) translateX(100px)">
<div id="clip" style="width: 200px; height: 200px; overflow: hidden">
<div id="inner" style="width: 400px; height: 400px; background: blue; transform: translateX(-100px)">
</div>
</div>
</div>
<script src="../../../resources/run-after-layout-and-paint.js"></script>
<script>
runAfterLayoutAndPaint(function() {
// Change both outer and inner transforms, but keep the combined transform
// unchanged. We should invalidate because the effective clip changes
// between the transforms.
outer.style.transform = "rotate(5deg) translateX(200px)";
inner.style.transform = "translateX(-200px)";
}, true);
</script>
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h" #include "third_party/blink/renderer/platform/graphics/paint/clip_paint_property_node.h"
#include "third_party/blink/renderer/platform/geometry/layout_rect.h" #include "third_party/blink/renderer/platform/geometry/layout_rect.h"
#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
namespace blink { namespace blink {
...@@ -17,10 +18,27 @@ const ClipPaintPropertyNode& ClipPaintPropertyNode::Root() { ...@@ -17,10 +18,27 @@ const ClipPaintPropertyNode& ClipPaintPropertyNode::Root() {
return *root; return *root;
} }
bool ClipPaintPropertyNode::Changed(
const PropertyTreeState& relative_to_state,
const TransformPaintPropertyNode* transform_not_to_check) const {
for (const auto* node = this; node && node != relative_to_state.Clip();
node = node->Parent()) {
if (node->NodeChanged())
return true;
if (node->LocalTransformSpace() != transform_not_to_check &&
node->LocalTransformSpace()->Changed(*relative_to_state.Transform()))
return true;
}
return false;
}
std::unique_ptr<JSONObject> ClipPaintPropertyNode::ToJSON() const { std::unique_ptr<JSONObject> ClipPaintPropertyNode::ToJSON() const {
auto json = JSONObject::Create(); auto json = JSONObject::Create();
if (Parent()) if (Parent())
json->SetString("parent", String::Format("%p", Parent())); json->SetString("parent", String::Format("%p", Parent()));
if (NodeChanged())
json->SetBoolean("changed", true);
json->SetString("localTransformSpace", json->SetString("localTransformSpace",
String::Format("%p", state_.local_transform_space.get())); String::Format("%p", state_.local_transform_space.get()));
json->SetString("rect", state_.clip_rect.ToString()); json->SetString("rect", state_.clip_rect.ToString());
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
namespace blink { namespace blink {
class GeometryMapperClipCache; class GeometryMapperClipCache;
class PropertyTreeState;
// A clip rect created by a css property such as "overflow" or "clip". // A clip rect created by a css property such as "overflow" or "clip".
// Along with a reference to the transform space the clip rect is based on, // Along with a reference to the transform space the clip rect is based on,
...@@ -72,6 +73,15 @@ class PLATFORM_EXPORT ClipPaintPropertyNode ...@@ -72,6 +73,15 @@ class PLATFORM_EXPORT ClipPaintPropertyNode
return true; return true;
} }
// Checks if the accumulated clip from |this| to |relative_to_state.Clip()|
// has changed in the space of |relative_to_state.Transform()|. We check for
// changes of not only clip nodes, but also LocalTransformSpace relative to
// |relative_to_state.Transform()| of the clip nodes. |transform_not_to_check|
// specifies a transform node that the caller has checked or will check its
// change in other ways and this function should treat it as unchanged.
bool Changed(const PropertyTreeState& relative_to_state,
const TransformPaintPropertyNode* transform_not_to_check) const;
bool EqualIgnoringHitTestRects(const ClipPaintPropertyNode* parent, bool EqualIgnoringHitTestRects(const ClipPaintPropertyNode* parent,
const State& state) const { const State& state) const {
return parent == Parent() && state_.EqualIgnoringHitTestRects(state); return parent == Parent() && state_.EqualIgnoringHitTestRects(state);
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h" #include "third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h"
#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
namespace blink { namespace blink {
const EffectPaintPropertyNode& EffectPaintPropertyNode::Root() { const EffectPaintPropertyNode& EffectPaintPropertyNode::Root() {
...@@ -22,10 +24,30 @@ FloatRect EffectPaintPropertyNode::MapRect(const FloatRect& input_rect) const { ...@@ -22,10 +24,30 @@ FloatRect EffectPaintPropertyNode::MapRect(const FloatRect& input_rect) const {
return result; return result;
} }
bool EffectPaintPropertyNode::Changed(
const PropertyTreeState& relative_to_state,
const TransformPaintPropertyNode* transform_not_to_check) const {
for (const auto* node = this; node && node != relative_to_state.Effect();
node = node->Parent()) {
if (node->NodeChanged())
return true;
if (node->HasFilterThatMovesPixels() &&
node->LocalTransformSpace() != transform_not_to_check &&
node->LocalTransformSpace()->Changed(*relative_to_state.Transform()))
return true;
// We don't check for change of OutputClip here to avoid N^3 complexity.
// The caller should check for clip change in other ways.
}
return false;
}
std::unique_ptr<JSONObject> EffectPaintPropertyNode::ToJSON() const { std::unique_ptr<JSONObject> EffectPaintPropertyNode::ToJSON() const {
auto json = JSONObject::Create(); auto json = JSONObject::Create();
if (Parent()) if (Parent())
json->SetString("parent", String::Format("%p", Parent())); json->SetString("parent", String::Format("%p", Parent()));
if (NodeChanged())
json->SetBoolean("changed", true);
json->SetString("localTransformSpace", json->SetString("localTransformSpace",
String::Format("%p", state_.local_transform_space.get())); String::Format("%p", state_.local_transform_space.get()));
json->SetString("outputClip", String::Format("%p", state_.output_clip.get())); json->SetString("outputClip", String::Format("%p", state_.output_clip.get()));
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
namespace blink { namespace blink {
class PropertyTreeState;
// Effect nodes are abstraction of isolated groups, along with optional effects // Effect nodes are abstraction of isolated groups, along with optional effects
// that can be applied to the composited output of the group. // that can be applied to the composited output of the group.
// //
...@@ -82,6 +84,17 @@ class PLATFORM_EXPORT EffectPaintPropertyNode ...@@ -82,6 +84,17 @@ class PLATFORM_EXPORT EffectPaintPropertyNode
return true; return true;
} }
// Checks if the accumulated effect from |this| to |relative_to_state
// .Effect()| has changed in the space of |relative_to_state.Transform()|.
// We check for changes of not only effect nodes, but also LocalTransformSpace
// relative to |relative_to_state.Transform()| of the effect nodes having
// filters that move pixels. Change of OutputClip is not checked and the
// caller should check in other ways. |transform_not_to_check| specifies the
// transform node that the caller has checked or will check its change in
// other ways and this function should treat it as unchanged.
bool Changed(const PropertyTreeState& relative_to_state,
const TransformPaintPropertyNode* transform_not_to_check) const;
const TransformPaintPropertyNode* LocalTransformSpace() const { const TransformPaintPropertyNode* LocalTransformSpace() const {
return state_.local_transform_space.get(); return state_.local_transform_space.get();
} }
......
...@@ -69,30 +69,6 @@ class PaintPropertyNode : public RefCounted<NodeType> { ...@@ -69,30 +69,6 @@ class PaintPropertyNode : public RefCounted<NodeType> {
return true; return true;
} }
// TODO(wangxianzhu): Changed() and ClearChangedToRoot() are inefficient
// due to the tree walks. Optimize this if this affects overall performance.
// Returns true if any node (excluding the lowest common ancestor of
// |relative_to_node| and |this|) is marked changed along the shortest path
// from |this| to |relative_to_node|.
bool Changed(const NodeType& relative_to_node) const {
if (this == &relative_to_node)
return false;
bool changed = false;
for (const auto* n = this; n; n = n->Parent()) {
if (n == &relative_to_node)
return changed;
if (n->changed_)
changed = true;
}
// We reach here if |relative_to_node| is not an ancestor of |this|.
const auto& lca = LowestCommonAncestor(static_cast<const NodeType&>(*this),
relative_to_node);
return Changed(lca) || relative_to_node.Changed(lca);
}
void ClearChangedToRoot() const { void ClearChangedToRoot() const {
for (auto* n = this; n; n = n->Parent()) for (auto* n = this; n; n = n->Parent())
n->changed_ = false; n->changed_ = false;
...@@ -129,8 +105,11 @@ class PaintPropertyNode : public RefCounted<NodeType> { ...@@ -129,8 +105,11 @@ class PaintPropertyNode : public RefCounted<NodeType> {
} }
void SetChanged() { changed_ = true; } void SetChanged() { changed_ = true; }
bool NodeChanged() const { return changed_; }
private: private:
friend class PaintPropertyNodeTest;
scoped_refptr<const NodeType> parent_; scoped_refptr<const NodeType> parent_;
mutable bool changed_ = true; mutable bool changed_ = true;
......
...@@ -75,7 +75,8 @@ PaintInvalidationReason RasterInvalidator::ChunkPropertiesChanged( ...@@ -75,7 +75,8 @@ PaintInvalidationReason RasterInvalidator::ChunkPropertiesChanged(
// different, or the effect node's value changed between the layer state and // different, or the effect node's value changed between the layer state and
// the chunk state. // the chunk state.
if (new_chunk_state.Effect() != old_chunk.effect_state || if (new_chunk_state.Effect() != old_chunk.effect_state ||
new_chunk_state.Effect()->Changed(*layer_state.Effect())) new_chunk_state.Effect()->Changed(layer_state,
new_chunk_state.Transform()))
return PaintInvalidationReason::kPaintProperty; return PaintInvalidationReason::kPaintProperty;
// Check for accumulated clip rect change, if the clip rects are tight. // Check for accumulated clip rect change, if the clip rects are tight.
...@@ -94,7 +95,7 @@ PaintInvalidationReason RasterInvalidator::ChunkPropertiesChanged( ...@@ -94,7 +95,7 @@ PaintInvalidationReason RasterInvalidator::ChunkPropertiesChanged(
// different, or the clip node's value changed between the layer state and the // different, or the clip node's value changed between the layer state and the
// chunk state. // chunk state.
if (new_chunk_state.Clip() != old_chunk.clip_state || if (new_chunk_state.Clip() != old_chunk.clip_state ||
new_chunk_state.Clip()->Changed(*layer_state.Clip())) new_chunk_state.Clip()->Changed(layer_state, new_chunk_state.Transform()))
return PaintInvalidationReason::kPaintProperty; return PaintInvalidationReason::kPaintProperty;
return PaintInvalidationReason::kNone; return PaintInvalidationReason::kNone;
......
...@@ -31,10 +31,26 @@ TransformPaintPropertyNode::NearestScrollTranslationNode() const { ...@@ -31,10 +31,26 @@ TransformPaintPropertyNode::NearestScrollTranslationNode() const {
return *transform; return *transform;
} }
bool TransformPaintPropertyNode::Changed(
const TransformPaintPropertyNode& relative_to_node) const {
for (const auto* node = this; node; node = node->Parent()) {
if (node == &relative_to_node)
return false;
if (node->NodeChanged())
return true;
}
// |this| is not a descendant of |relative_to_node|. We have seen no changed
// flag from |this| to the root. Now check |relative_to_node| to the root.
return relative_to_node.Changed(Root());
}
std::unique_ptr<JSONObject> TransformPaintPropertyNode::ToJSON() const { std::unique_ptr<JSONObject> TransformPaintPropertyNode::ToJSON() const {
auto json = JSONObject::Create(); auto json = JSONObject::Create();
if (Parent()) if (Parent())
json->SetString("parent", String::Format("%p", Parent())); json->SetString("parent", String::Format("%p", Parent()));
if (NodeChanged())
json->SetBoolean("changed", true);
if (!state_.matrix.IsIdentity()) if (!state_.matrix.IsIdentity())
json->SetString("matrix", state_.matrix.ToString()); json->SetString("matrix", state_.matrix.ToString());
if (!state_.matrix.IsIdentityOrTranslation()) if (!state_.matrix.IsIdentityOrTranslation())
......
...@@ -83,6 +83,12 @@ class PLATFORM_EXPORT TransformPaintPropertyNode ...@@ -83,6 +83,12 @@ class PLATFORM_EXPORT TransformPaintPropertyNode
return true; return true;
} }
// If |relative_to_node| is an ancestor of |this|, returns true if any node is
// marked changed along the path from |this| to |relative_to_node| (not
// included). Otherwise returns the combined changed status of the paths
// from |this| and |relative_to_node| to the root.
bool Changed(const TransformPaintPropertyNode& relative_to_node) const;
const TransformationMatrix& Matrix() const { return state_.matrix; } const TransformationMatrix& Matrix() const { return state_.matrix; }
const FloatPoint3D& Origin() const { return state_.origin; } const FloatPoint3D& Origin() const { return state_.origin; }
......
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