Commit 6f5a1bd0 authored by Philip Rogers's avatar Philip Rogers Committed by Commit Bot

[BlinkGenPropertyTrees] Stop incorrect clearing of property tree changed

PaintPropertyTreeNode::Changed is used for in-layer raster invalidation,
and cc damage for both SPV2 and BGPT. https://crrev.com/606472 failed
to account for the case when non-transient paint controllers in
GraphicsLayers would clear property tree changed values before the
values could be used for damage.

As a reminder, with BlinkGenPropertyTrees, each GraphicsLayer keeps a
non-transient paint controller, and at the end of painting a transient
paint controller is created and each GraphicsLayer's cc::Layer is
inserted as a foreign layer into the transient paint controller.

This patch changes BGPT to clear all property tree changed bits after
damage has been calculated in PaintArtifactCompositor. It is implemented
with a recursive walk of the GraphicsLayer tree.

Bug: 899628
Change-Id: Ibc7d65d6ab8875508b9c3dd18ab6db1230bf1308
Reviewed-on: https://chromium-review.googlesource.com/c/1327804
Commit-Queue: Philip Rogers <pdr@chromium.org>
Reviewed-by: default avatarXianzhu Wang <wangxianzhu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#606874}
parent 1dc99fe0
......@@ -26,7 +26,14 @@
{
"name": "Mask Layer",
"bounds": [800, 300],
"backfaceVisibility": "hidden"
"backfaceVisibility": "hidden",
"paintInvalidations": [
{
"object": "Mask Layer",
"rect": [0, 0, 800, 300],
"reason": "paint property change"
}
]
}
],
"transform": 2
......
......@@ -579,4 +579,62 @@ TEST_P(WebLayerListSimTest, LayerSubtreeClipPropertyChanged) {
EXPECT_FALSE(inner_element_layer->subtree_property_changed());
}
TEST_P(WebLayerListSimTest, LayerSubtreeOverflowClipPropertyChanged) {
// TODO(crbug.com/765003): SPV2 may make different layerization decisions and
// we cannot guarantee that both divs will be composited in this test. When
// SPV2 gets closer to launch, this test should be updated to pass.
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
return;
InitializeWithHTML(R"HTML(
<!DOCTYPE html>
<style>
html { overflow: hidden; }
#outer {
width: 100px;
height: 100px;
will-change: transform;
position: absolute;
overflow: hidden;
}
#inner {
width: 200px;
height: 100px;
will-change: transform;
background: lightblue;
}
</style>
<div id='outer'>
<div id='inner'></div>
</div>
)HTML");
Compositor().BeginFrame();
auto* outer_element = GetElementById("outer");
auto* outer_element_layer = ContentLayerAt(ContentLayerCount() - 2);
auto* inner_element = GetElementById("inner");
auto* inner_element_layer = ContentLayerAt(ContentLayerCount() - 1);
DCHECK_EQ(inner_element_layer->element_id(),
CompositorElementIdFromUniqueObjectId(
inner_element->GetLayoutObject()->UniqueId(),
CompositorElementIdNamespace::kPrimary));
// Initially, no layer should have |subtree_property_changed| set.
EXPECT_FALSE(outer_element_layer->subtree_property_changed());
EXPECT_FALSE(inner_element_layer->subtree_property_changed());
// Modifying the clip width should set |subtree_property_changed| on
// both layers.
outer_element->setAttribute(html_names::kStyleAttr, "width: 200px;");
WebView().MainFrameWidget()->UpdateAllLifecyclePhases();
EXPECT_TRUE(outer_element_layer->subtree_property_changed());
EXPECT_TRUE(inner_element_layer->subtree_property_changed());
// After a frame the |subtree_property_changed| value should be reset.
Compositor().BeginFrame();
EXPECT_FALSE(outer_element_layer->subtree_property_changed());
EXPECT_FALSE(inner_element_layer->subtree_property_changed());
}
} // namespace blink
......@@ -2558,6 +2558,14 @@ bool LocalFrameView::RunPrePaintLifecyclePhase(
return target_state > DocumentLifecycle::kPrePaintClean;
}
template <typename Function>
static void ForAllGraphicsLayers(GraphicsLayer& layer,
const Function& function) {
function(layer);
for (auto* child : layer.Children())
ForAllGraphicsLayers(*child, function);
}
void LocalFrameView::RunPaintLifecyclePhase() {
TRACE_EVENT0("blink,benchmark", "LocalFrameView::RunPaintLifecyclePhase");
// While printing a document, the paint walk is done by the printing
......@@ -2603,8 +2611,18 @@ void LocalFrameView::RunPaintLifecyclePhase() {
// Property tree changed state is typically cleared through
// |PaintController::FinishCycle| but that will be a no-op because
// the paint controller is transient, so force the changed state to be
// updated here.
paint_controller_->ClearPropertyTreeChangedState();
// cleared here.
paint_controller_->ClearPropertyTreeChangedStateTo(
PropertyTreeState::Root());
auto* root = GetLayoutView()->Compositor()->PaintRootGraphicsLayer();
if (root) {
ForAllGraphicsLayers(*root, [](GraphicsLayer& layer) {
if (layer.DrawsContent() && layer.HasLayerState()) {
layer.GetPaintController().ClearPropertyTreeChangedStateTo(
layer.GetPropertyTreeState());
}
});
}
paint_controller_ = nullptr;
}
}
......
......@@ -139,8 +139,11 @@ TEST_P(GraphicsLayerTest, PaintRecursively) {
layers_.graphics_layer_client().SetNeedsRepaint(true);
layers_.graphics_layer().PaintRecursively();
EXPECT_FALSE(transform1->Changed(transform_root));
EXPECT_FALSE(transform2->Changed(transform_root));
// With BlinkGenPropertyTrees, these are not cleared until after paint.
if (!RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) {
EXPECT_FALSE(transform1->Changed(transform_root));
EXPECT_FALSE(transform2->Changed(transform_root));
}
}
TEST_P(GraphicsLayerTest, SetDrawsContentFalse) {
......
......@@ -103,9 +103,15 @@ void PaintArtifact::AppendToDisplayItemList(const FloatSize& visual_rect_offset,
}
void PaintArtifact::FinishCycle() {
// BlinkGenPropertyTrees uses PaintController::ClearPropertyTreeChangedStateTo
// for clearing the property tree changed state at the end of paint instead of
// in FinishCycle. See: LocalFrameView::RunPaintLifecyclePhase.
bool clear_property_tree_changed =
!RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled();
for (auto& chunk : chunks_) {
chunk.client_is_just_created = false;
chunk.properties.ClearChangedToRoot();
if (clear_property_tree_changed)
chunk.properties.ClearChangedToRoot();
}
}
......
......@@ -562,14 +562,17 @@ void PaintController::FinishCycle() {
#endif
}
void PaintController::ClearPropertyTreeChangedState() {
void PaintController::ClearPropertyTreeChangedStateTo(
const PropertyTreeState& to) {
DCHECK(RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled());
DCHECK(usage_ == kTransient);
// Calling |ClearChangedToRoot| for every chunk is O(|property nodes|^2) and
// Calling |ClearChangedTo| for every chunk is O(|property nodes|^2) and
// could be optimized by caching which nodes that have already been cleared.
for (const auto& chunk : current_paint_artifact_->PaintChunks())
chunk.properties.ClearChangedToRoot();
for (const auto& chunk : current_paint_artifact_->PaintChunks()) {
chunk.properties.Transform()->ClearChangedTo(to.Transform());
chunk.properties.Clip()->ClearChangedTo(to.Clip());
chunk.properties.Effect()->ClearChangedTo(to.Effect());
}
}
size_t PaintController::ApproximateUnsharedMemoryUsage() const {
......
......@@ -178,7 +178,7 @@ class PLATFORM_EXPORT PaintController {
// BlinkGenPropertyTrees and this function provides a hook for clearing
// the property tree changed state after paint.
// TODO(pdr): Remove this when BlinkGenPropertyTrees ships.
void ClearPropertyTreeChangedState();
void ClearPropertyTreeChangedStateTo(const PropertyTreeState&);
// Returns the approximate memory usage, excluding memory likely to be
// shared with the embedder after copying to WebPaintController.
......
......@@ -74,8 +74,9 @@ class PaintPropertyNode : public RefCounted<NodeType> {
return true;
}
void ClearChangedToRoot() const {
for (auto* n = this; n; n = n->Parent())
void ClearChangedToRoot() const { ClearChangedTo(nullptr); }
void ClearChangedTo(const NodeType* node) const {
for (auto* n = this; n && n != node; n = n->Parent())
n->changed_ = false;
}
......@@ -143,6 +144,13 @@ class PaintPropertyNode : public RefCounted<NodeType> {
// nodes that do not affect rendering and are ignored for the purposes of
// display item list generation.
bool is_parent_alias_ = false;
// Indicates that the paint property value changed in the last update in the
// prepaint lifecycle step. This is used for raster invalidation and damage
// in the compositor. This value is cleared through |ClearChangedTo*|. With
// BlinkGenPropertyTrees, this is cleared explicitly at the end of paint (see:
// LocalFrameView::RunPaintLifecyclePhase), otherwise this is cleared through
// PaintController::FinishCycle.
mutable bool changed_ = true;
#if DCHECK_IS_ON()
......
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