Commit c7384786 authored by Vladimir Levin's avatar Vladimir Levin Committed by Commit Bot

[PE] Use a vector of PrePaintTreeWalkContexts instead of alloc'ed members

This patch replaces the heap allocated members of the PrePaintTreeWalk
context with value members, and instead of creating these contexts
on the stack, instead uses a vector storage that gets passed around
as recursion happens.

There are some subtleties with using this approach (I added comments
for those). In local testing on a micro benchmark (not included here),
this shows about ~10% improvement on the prepaint tree walk when everything
is invalidated via SetSubtreeNeedsPaintPropertyUpdate.

One thing that is important is that contexts can store references to
its parent contexts, both top level PrePaintTreeWalkContext and
PaintInvalidatorContext. However since a vector may reallocate, it will
invalidate these parent references. For the former (PrePaintTreeWalkContext),
we simply have an accessor that uses an index to look up the context in
the context storage. Similar thing is done for PaintInvalidatorContext.
However, in the latter, because it's implemented and accessed in different
files, we instead put in a ParentContextAccessor which hides the underlying
access to the context storage on the PrePaintTreeWalk object.

R=chrishtr@chromium.org

Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2
Change-Id: Id69fae9db5ef76d64342cf3082d81762ee18dd15
Reviewed-on: https://chromium-review.googlesource.com/910085
Commit-Queue: vmpstr <vmpstr@chromium.org>
Reviewed-by: default avatarXianzhu Wang <wangxianzhu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#536768}
parent 93c80a93
......@@ -3322,7 +3322,7 @@ void LocalFrameView::PrePaint() {
{
SCOPED_UMA_AND_UKM_TIMER("Blink.PrePaint.UpdateTime",
UkmMetricNames::kPrePaint);
PrePaintTreeWalk().Walk(*this);
PrePaintTreeWalk().WalkTree(*this);
}
ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
......
......@@ -395,7 +395,7 @@ void ObjectPaintInvalidatorWithContext::FullyInvalidatePaint(
}
bool ObjectPaintInvalidatorWithContext::ParentFullyInvalidatedOnSameBacking() {
if (!object_.Parent() || !context_.parent_context)
if (!object_.Parent() || !context_.ParentContext())
return false;
if (!IsImmediateFullPaintInvalidationReason(
......@@ -403,7 +403,7 @@ bool ObjectPaintInvalidatorWithContext::ParentFullyInvalidatedOnSameBacking() {
return false;
// Parent and child should have the same paint invalidation container.
if (context_.parent_context->paint_invalidation_container !=
if (context_.ParentContext()->paint_invalidation_container !=
context_.paint_invalidation_container)
return false;
......@@ -428,7 +428,7 @@ void ObjectPaintInvalidatorWithContext::InvalidatePaintRectangleWithContext(
// If the parent has fully invalidated and its visual rect covers this object
// on the same backing, skip the invalidation.
if (ParentFullyInvalidatedOnSameBacking() &&
(context_.parent_context->old_visual_rect.Contains(rect) ||
(context_.ParentContext()->old_visual_rect.Contains(rect) ||
object_.Parent()->FirstFragment().VisualRect().Contains(rect))) {
if (!object_.GetFrameView()->IsTrackingPaintInvalidations())
return;
......
......@@ -19,6 +19,7 @@
#include "core/paint/ObjectPaintProperties.h"
#include "core/paint/PaintLayer.h"
#include "core/paint/PaintLayerScrollableArea.h"
#include "core/paint/PrePaintTreeWalk.h"
#include "core/paint/ng/ng_paint_fragment.h"
#include "platform/PlatformChromeClient.h"
#include "platform/graphics/paint/GeometryMapper.h"
......@@ -186,6 +187,13 @@ void PaintInvalidatorContext::MapLocalRectToVisualRectInBacking(
object, rect, *this);
}
const PaintInvalidatorContext*
PaintInvalidatorContext::ParentContextAccessor::ParentContext() const {
return tree_walk_ ? &tree_walk_->ContextAt(parent_context_index_)
.paint_invalidator_context
: nullptr;
}
LayoutRect PaintInvalidator::ComputeVisualRectInBacking(
const LayoutObject& object,
const PaintInvalidatorContext& context) {
......
......@@ -12,20 +12,34 @@
namespace blink {
class PrePaintTreeWalk;
struct CORE_EXPORT PaintInvalidatorContext {
USING_FAST_MALLOC(PaintInvalidatorContext);
DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
public:
PaintInvalidatorContext() : parent_context(nullptr) {}
class ParentContextAccessor {
public:
ParentContextAccessor() = default;
ParentContextAccessor(PrePaintTreeWalk* tree_walk,
size_t parent_context_index)
: tree_walk_(tree_walk), parent_context_index_(parent_context_index) {}
const PaintInvalidatorContext* ParentContext() const;
private:
PrePaintTreeWalk* tree_walk_ = nullptr;
size_t parent_context_index_ = 0u;
};
PaintInvalidatorContext() = default;
PaintInvalidatorContext(const PaintInvalidatorContext& parent_context)
: parent_context(&parent_context),
subtree_flags(parent_context.subtree_flags),
PaintInvalidatorContext(const ParentContextAccessor& parent_context_accessor)
: parent_context_accessor_(parent_context_accessor),
subtree_flags(ParentContext()->subtree_flags),
paint_invalidation_container(
parent_context.paint_invalidation_container),
ParentContext()->paint_invalidation_container),
paint_invalidation_container_for_stacked_contents(
parent_context.paint_invalidation_container_for_stacked_contents),
painting_layer(parent_context.painting_layer) {}
ParentContext()->paint_invalidation_container_for_stacked_contents),
painting_layer(ParentContext()->painting_layer) {}
void MapLocalRectToVisualRectInBacking(const LayoutObject&,
LayoutRect&) const;
......@@ -39,8 +53,16 @@ struct CORE_EXPORT PaintInvalidatorContext {
(subtree_flags & PaintInvalidatorContext::kSubtreeVisualRectUpdate);
}
const PaintInvalidatorContext* parent_context;
const PaintInvalidatorContext* ParentContext() const {
return parent_context_accessor_.ParentContext();
}
private:
// Parent context accessor has to be initialized first, so inject the private
// access block here for that reason.
ParentContextAccessor parent_context_accessor_;
public:
enum SubtreeFlag {
kSubtreeInvalidationChecking = 1 << 0,
kSubtreeVisualRectUpdate = 1 << 1,
......@@ -104,6 +126,7 @@ struct CORE_EXPORT PaintInvalidatorContext {
private:
friend class PaintInvalidator;
const PaintPropertyTreeBuilderFragmentContext* tree_builder_context_ =
nullptr;
......
......@@ -109,7 +109,7 @@ struct PaintPropertyTreeBuilderFragmentContext {
};
struct PaintPropertyTreeBuilderContext {
USING_FAST_MALLOC(PaintPropertyTreeBuilderContext);
DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
public:
PaintPropertyTreeBuilderContext() = default;
......
......@@ -13,7 +13,6 @@ namespace blink {
class LayoutObject;
class LocalFrameView;
struct PrePaintTreeWalkContext;
// This class walks the whole layout tree, beginning from the root
// LocalFrameView, across frame boundaries. Helper classes are called for each
......@@ -22,10 +21,48 @@ struct PrePaintTreeWalkContext;
class CORE_EXPORT PrePaintTreeWalk {
public:
PrePaintTreeWalk() = default;
void Walk(LocalFrameView& root_frame);
void WalkTree(LocalFrameView& root_frame);
private:
void Walk(LocalFrameView&, const PrePaintTreeWalkContext&);
friend PaintInvalidatorContext::ParentContextAccessor;
// PrePaintTreewalkContext is large and can lead to stack overflows
// when recursion is deep so these context objects are allocated on the heap.
// See: https://crbug.com/698653.
struct PrePaintTreeWalkContext {
PrePaintTreeWalkContext() { tree_builder_context.emplace(); }
PrePaintTreeWalkContext(
const PrePaintTreeWalkContext& parent_context,
const PaintInvalidatorContext::ParentContextAccessor&
parent_context_accessor,
bool needs_tree_builder_context)
: paint_invalidator_context(parent_context_accessor),
ancestor_overflow_paint_layer(
parent_context.ancestor_overflow_paint_layer) {
if (needs_tree_builder_context || DCHECK_IS_ON())
tree_builder_context.emplace(*parent_context.tree_builder_context);
#if DCHECK_IS_ON()
if (needs_tree_builder_context)
DCHECK(parent_context.tree_builder_context->is_actually_needed);
tree_builder_context->is_actually_needed = needs_tree_builder_context;
#endif
}
WTF::Optional<PaintPropertyTreeBuilderContext> tree_builder_context;
PaintInvalidatorContext paint_invalidator_context;
// The ancestor in the PaintLayer tree which has overflow clip, or
// is the root layer. Note that it is tree ancestor, not containing
// block or stacking ancestor.
PaintLayer* ancestor_overflow_paint_layer = nullptr;
};
const PrePaintTreeWalkContext& ContextAt(size_t index) {
DCHECK_LT(index, context_storage_.size());
return context_storage_[index];
}
void Walk(LocalFrameView&);
// This is to minimize stack frame usage during recursion. Modern compilers
// (MSVC in particular) can inline across compilation units, resulting in
......@@ -33,7 +70,7 @@ class CORE_EXPORT PrePaintTreeWalk {
// makes sure the stack frame is freed prior to making a recursive call.
// See https://crbug.com/781301 .
NOINLINE void WalkInternal(const LayoutObject&, PrePaintTreeWalkContext&);
void Walk(const LayoutObject&, const PrePaintTreeWalkContext&);
void Walk(const LayoutObject&);
// Invalidates paint-layer painting optimizations, such as subsequence caching
// and empty paint phase optimizations if clips from the context have changed.
......@@ -44,8 +81,13 @@ class CORE_EXPORT PrePaintTreeWalk {
const PrePaintTreeWalkContext&);
bool NeedsTreeBuilderContextUpdate(const LayoutObject&,
const PrePaintTreeWalkContext&);
void UpdateAuxiliaryObjectProperties(const LayoutObject&,
PrePaintTreeWalkContext&);
void ResizeContextStorageIfNeeded();
PaintInvalidator paint_invalidator_;
Vector<PrePaintTreeWalkContext> context_storage_;
FRIEND_TEST_ALL_PREFIXES(PrePaintTreeWalkTest, ClipRects);
};
......
......@@ -54,14 +54,14 @@ PaintInvalidationReason TableCellPaintInvalidator::InvalidatePaint() {
(row.StyleRef().HasBackground() ||
(table.HasCollapsedBorders() &&
LIKELY(!table.ShouldPaintAllCollapsedBorders())))) {
InvalidateContainerForCellGeometryChange(row, *context_.parent_context);
InvalidateContainerForCellGeometryChange(row, *context_.ParentContext());
}
if (UNLIKELY(table.ShouldPaintAllCollapsedBorders()) &&
!IsFullPaintInvalidationReason(table.GetPaintInvalidationReason())) {
DCHECK(table.HasCollapsedBorders());
InvalidateContainerForCellGeometryChange(
table, *context_.parent_context->parent_context->parent_context);
table, *context_.ParentContext()->ParentContext()->ParentContext());
}
if (!IsFullPaintInvalidationReason(section.GetPaintInvalidationReason())) {
......@@ -77,7 +77,7 @@ PaintInvalidationReason TableCellPaintInvalidator::InvalidatePaint() {
}
if (section_paints_background) {
InvalidateContainerForCellGeometryChange(
section, *context_.parent_context->parent_context);
section, *context_.ParentContext()->ParentContext());
}
}
}
......
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