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() { ...@@ -3322,7 +3322,7 @@ void LocalFrameView::PrePaint() {
{ {
SCOPED_UMA_AND_UKM_TIMER("Blink.PrePaint.UpdateTime", SCOPED_UMA_AND_UKM_TIMER("Blink.PrePaint.UpdateTime",
UkmMetricNames::kPrePaint); UkmMetricNames::kPrePaint);
PrePaintTreeWalk().Walk(*this); PrePaintTreeWalk().WalkTree(*this);
} }
ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) { ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
......
...@@ -395,7 +395,7 @@ void ObjectPaintInvalidatorWithContext::FullyInvalidatePaint( ...@@ -395,7 +395,7 @@ void ObjectPaintInvalidatorWithContext::FullyInvalidatePaint(
} }
bool ObjectPaintInvalidatorWithContext::ParentFullyInvalidatedOnSameBacking() { bool ObjectPaintInvalidatorWithContext::ParentFullyInvalidatedOnSameBacking() {
if (!object_.Parent() || !context_.parent_context) if (!object_.Parent() || !context_.ParentContext())
return false; return false;
if (!IsImmediateFullPaintInvalidationReason( if (!IsImmediateFullPaintInvalidationReason(
...@@ -403,7 +403,7 @@ bool ObjectPaintInvalidatorWithContext::ParentFullyInvalidatedOnSameBacking() { ...@@ -403,7 +403,7 @@ bool ObjectPaintInvalidatorWithContext::ParentFullyInvalidatedOnSameBacking() {
return false; return false;
// Parent and child should have the same paint invalidation container. // 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) context_.paint_invalidation_container)
return false; return false;
...@@ -428,7 +428,7 @@ void ObjectPaintInvalidatorWithContext::InvalidatePaintRectangleWithContext( ...@@ -428,7 +428,7 @@ void ObjectPaintInvalidatorWithContext::InvalidatePaintRectangleWithContext(
// If the parent has fully invalidated and its visual rect covers this object // If the parent has fully invalidated and its visual rect covers this object
// on the same backing, skip the invalidation. // on the same backing, skip the invalidation.
if (ParentFullyInvalidatedOnSameBacking() && if (ParentFullyInvalidatedOnSameBacking() &&
(context_.parent_context->old_visual_rect.Contains(rect) || (context_.ParentContext()->old_visual_rect.Contains(rect) ||
object_.Parent()->FirstFragment().VisualRect().Contains(rect))) { object_.Parent()->FirstFragment().VisualRect().Contains(rect))) {
if (!object_.GetFrameView()->IsTrackingPaintInvalidations()) if (!object_.GetFrameView()->IsTrackingPaintInvalidations())
return; return;
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "core/paint/ObjectPaintProperties.h" #include "core/paint/ObjectPaintProperties.h"
#include "core/paint/PaintLayer.h" #include "core/paint/PaintLayer.h"
#include "core/paint/PaintLayerScrollableArea.h" #include "core/paint/PaintLayerScrollableArea.h"
#include "core/paint/PrePaintTreeWalk.h"
#include "core/paint/ng/ng_paint_fragment.h" #include "core/paint/ng/ng_paint_fragment.h"
#include "platform/PlatformChromeClient.h" #include "platform/PlatformChromeClient.h"
#include "platform/graphics/paint/GeometryMapper.h" #include "platform/graphics/paint/GeometryMapper.h"
...@@ -186,6 +187,13 @@ void PaintInvalidatorContext::MapLocalRectToVisualRectInBacking( ...@@ -186,6 +187,13 @@ void PaintInvalidatorContext::MapLocalRectToVisualRectInBacking(
object, rect, *this); 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( LayoutRect PaintInvalidator::ComputeVisualRectInBacking(
const LayoutObject& object, const LayoutObject& object,
const PaintInvalidatorContext& context) { const PaintInvalidatorContext& context) {
......
...@@ -12,20 +12,34 @@ ...@@ -12,20 +12,34 @@
namespace blink { namespace blink {
class PrePaintTreeWalk;
struct CORE_EXPORT PaintInvalidatorContext { struct CORE_EXPORT PaintInvalidatorContext {
USING_FAST_MALLOC(PaintInvalidatorContext); DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
public: 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) PaintInvalidatorContext(const ParentContextAccessor& parent_context_accessor)
: parent_context(&parent_context), : parent_context_accessor_(parent_context_accessor),
subtree_flags(parent_context.subtree_flags), subtree_flags(ParentContext()->subtree_flags),
paint_invalidation_container( paint_invalidation_container(
parent_context.paint_invalidation_container), ParentContext()->paint_invalidation_container),
paint_invalidation_container_for_stacked_contents( paint_invalidation_container_for_stacked_contents(
parent_context.paint_invalidation_container_for_stacked_contents), ParentContext()->paint_invalidation_container_for_stacked_contents),
painting_layer(parent_context.painting_layer) {} painting_layer(ParentContext()->painting_layer) {}
void MapLocalRectToVisualRectInBacking(const LayoutObject&, void MapLocalRectToVisualRectInBacking(const LayoutObject&,
LayoutRect&) const; LayoutRect&) const;
...@@ -39,8 +53,16 @@ struct CORE_EXPORT PaintInvalidatorContext { ...@@ -39,8 +53,16 @@ struct CORE_EXPORT PaintInvalidatorContext {
(subtree_flags & PaintInvalidatorContext::kSubtreeVisualRectUpdate); (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 { enum SubtreeFlag {
kSubtreeInvalidationChecking = 1 << 0, kSubtreeInvalidationChecking = 1 << 0,
kSubtreeVisualRectUpdate = 1 << 1, kSubtreeVisualRectUpdate = 1 << 1,
...@@ -104,6 +126,7 @@ struct CORE_EXPORT PaintInvalidatorContext { ...@@ -104,6 +126,7 @@ struct CORE_EXPORT PaintInvalidatorContext {
private: private:
friend class PaintInvalidator; friend class PaintInvalidator;
const PaintPropertyTreeBuilderFragmentContext* tree_builder_context_ = const PaintPropertyTreeBuilderFragmentContext* tree_builder_context_ =
nullptr; nullptr;
......
...@@ -109,7 +109,7 @@ struct PaintPropertyTreeBuilderFragmentContext { ...@@ -109,7 +109,7 @@ struct PaintPropertyTreeBuilderFragmentContext {
}; };
struct PaintPropertyTreeBuilderContext { struct PaintPropertyTreeBuilderContext {
USING_FAST_MALLOC(PaintPropertyTreeBuilderContext); DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
public: public:
PaintPropertyTreeBuilderContext() = default; PaintPropertyTreeBuilderContext() = default;
......
...@@ -13,7 +13,6 @@ namespace blink { ...@@ -13,7 +13,6 @@ namespace blink {
class LayoutObject; class LayoutObject;
class LocalFrameView; class LocalFrameView;
struct PrePaintTreeWalkContext;
// This class walks the whole layout tree, beginning from the root // This class walks the whole layout tree, beginning from the root
// LocalFrameView, across frame boundaries. Helper classes are called for each // LocalFrameView, across frame boundaries. Helper classes are called for each
...@@ -22,10 +21,48 @@ struct PrePaintTreeWalkContext; ...@@ -22,10 +21,48 @@ struct PrePaintTreeWalkContext;
class CORE_EXPORT PrePaintTreeWalk { class CORE_EXPORT PrePaintTreeWalk {
public: public:
PrePaintTreeWalk() = default; PrePaintTreeWalk() = default;
void Walk(LocalFrameView& root_frame); void WalkTree(LocalFrameView& root_frame);
private: 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 // This is to minimize stack frame usage during recursion. Modern compilers
// (MSVC in particular) can inline across compilation units, resulting in // (MSVC in particular) can inline across compilation units, resulting in
...@@ -33,7 +70,7 @@ class CORE_EXPORT PrePaintTreeWalk { ...@@ -33,7 +70,7 @@ class CORE_EXPORT PrePaintTreeWalk {
// makes sure the stack frame is freed prior to making a recursive call. // makes sure the stack frame is freed prior to making a recursive call.
// See https://crbug.com/781301 . // See https://crbug.com/781301 .
NOINLINE void WalkInternal(const LayoutObject&, PrePaintTreeWalkContext&); 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 // Invalidates paint-layer painting optimizations, such as subsequence caching
// and empty paint phase optimizations if clips from the context have changed. // and empty paint phase optimizations if clips from the context have changed.
...@@ -44,8 +81,13 @@ class CORE_EXPORT PrePaintTreeWalk { ...@@ -44,8 +81,13 @@ class CORE_EXPORT PrePaintTreeWalk {
const PrePaintTreeWalkContext&); const PrePaintTreeWalkContext&);
bool NeedsTreeBuilderContextUpdate(const LayoutObject&, bool NeedsTreeBuilderContextUpdate(const LayoutObject&,
const PrePaintTreeWalkContext&); const PrePaintTreeWalkContext&);
void UpdateAuxiliaryObjectProperties(const LayoutObject&,
PrePaintTreeWalkContext&);
void ResizeContextStorageIfNeeded();
PaintInvalidator paint_invalidator_; PaintInvalidator paint_invalidator_;
Vector<PrePaintTreeWalkContext> context_storage_;
FRIEND_TEST_ALL_PREFIXES(PrePaintTreeWalkTest, ClipRects); FRIEND_TEST_ALL_PREFIXES(PrePaintTreeWalkTest, ClipRects);
}; };
......
...@@ -54,14 +54,14 @@ PaintInvalidationReason TableCellPaintInvalidator::InvalidatePaint() { ...@@ -54,14 +54,14 @@ PaintInvalidationReason TableCellPaintInvalidator::InvalidatePaint() {
(row.StyleRef().HasBackground() || (row.StyleRef().HasBackground() ||
(table.HasCollapsedBorders() && (table.HasCollapsedBorders() &&
LIKELY(!table.ShouldPaintAllCollapsedBorders())))) { LIKELY(!table.ShouldPaintAllCollapsedBorders())))) {
InvalidateContainerForCellGeometryChange(row, *context_.parent_context); InvalidateContainerForCellGeometryChange(row, *context_.ParentContext());
} }
if (UNLIKELY(table.ShouldPaintAllCollapsedBorders()) && if (UNLIKELY(table.ShouldPaintAllCollapsedBorders()) &&
!IsFullPaintInvalidationReason(table.GetPaintInvalidationReason())) { !IsFullPaintInvalidationReason(table.GetPaintInvalidationReason())) {
DCHECK(table.HasCollapsedBorders()); DCHECK(table.HasCollapsedBorders());
InvalidateContainerForCellGeometryChange( InvalidateContainerForCellGeometryChange(
table, *context_.parent_context->parent_context->parent_context); table, *context_.ParentContext()->ParentContext()->ParentContext());
} }
if (!IsFullPaintInvalidationReason(section.GetPaintInvalidationReason())) { if (!IsFullPaintInvalidationReason(section.GetPaintInvalidationReason())) {
...@@ -77,7 +77,7 @@ PaintInvalidationReason TableCellPaintInvalidator::InvalidatePaint() { ...@@ -77,7 +77,7 @@ PaintInvalidationReason TableCellPaintInvalidator::InvalidatePaint() {
} }
if (section_paints_background) { if (section_paints_background) {
InvalidateContainerForCellGeometryChange( 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