Commit fa9b0108 authored by Mason Freed's avatar Mason Freed Committed by Commit Bot

Reuse pending_layers between frames

From profiling and Pinpoint tests, a decent percentage of
PushPaintArtifactToCompositor() CPU time (possibly up to ~30%) is spent
reallocating Vector<> objects. Specifically in two places: the chunks_
vector in PaintChunker, and the pending_layers vector in
PaintArtifactCompositor. Previous to this CL, on each frame, both
vectors were constructed from scratch, grown up to their final size
(with many reallocations along the way), used, and then destroyed.
This CL tries to reduce a bit of that overhead by keeping the vectors
around, at their prior-frame capacity.

This CL re-uses the pending_layers vector. The PaintChunker CL is at [1].

[1] https://chromium-review.googlesource.com/c/chromium/src/+/1586578

Bug: 954961
Change-Id: I496259f06835989d2e25b7ce1eb73ef2ab9cc788
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1585330
Commit-Queue: Mason Freed <masonfreed@chromium.org>
Commit-Queue: Philip Rogers <pdr@chromium.org>
Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Auto-Submit: Mason Freed <masonfreed@chromium.org>
Cr-Commit-Position: refs/heads/master@{#654686}
parent 053334be
...@@ -168,7 +168,6 @@ found in the LICENSE file. ...@@ -168,7 +168,6 @@ found in the LICENSE file.
subject: getRandomItem(possibleSubjects), subject: getRandomItem(possibleSubjects),
snippet: getRandomItem(possibleSnippets), snippet: getRandomItem(possibleSnippets),
} }
console.log(data[i]);
} }
return data; return data;
} }
......
...@@ -158,7 +158,6 @@ found in the LICENSE file. ...@@ -158,7 +158,6 @@ found in the LICENSE file.
subject: getRandomItem(possibleSubjects), subject: getRandomItem(possibleSubjects),
snippet: getRandomItem(possibleSnippets), snippet: getRandomItem(possibleSnippets),
} }
console.log(data[i]);
} }
return data; return data;
} }
......
...@@ -570,7 +570,6 @@ static bool SkipGroupIfEffectivelyInvisible( ...@@ -570,7 +570,6 @@ static bool SkipGroupIfEffectivelyInvisible(
void PaintArtifactCompositor::LayerizeGroup( void PaintArtifactCompositor::LayerizeGroup(
const PaintArtifact& paint_artifact, const PaintArtifact& paint_artifact,
const Settings& settings, const Settings& settings,
Vector<PendingLayer>& pending_layers,
const EffectPaintPropertyNode& current_group, const EffectPaintPropertyNode& current_group,
Vector<PaintChunk>::const_iterator& chunk_it) { Vector<PaintChunk>::const_iterator& chunk_it) {
// Skip paint chunks that are effectively invisible due to opacity and don't // Skip paint chunks that are effectively invisible due to opacity and don't
...@@ -580,7 +579,7 @@ void PaintArtifactCompositor::LayerizeGroup( ...@@ -580,7 +579,7 @@ void PaintArtifactCompositor::LayerizeGroup(
chunk_it)) chunk_it))
return; return;
wtf_size_t first_layer_in_current_group = pending_layers.size(); wtf_size_t first_layer_in_current_group = pending_layers_.size();
// The worst case time complexity of the algorithm is O(pqd), where // The worst case time complexity of the algorithm is O(pqd), where
// p = the number of paint chunks. // p = the number of paint chunks.
// q = average number of trials to find a squash layer or rejected // q = average number of trials to find a squash layer or rejected
...@@ -613,9 +612,9 @@ void PaintArtifactCompositor::LayerizeGroup( ...@@ -613,9 +612,9 @@ void PaintArtifactCompositor::LayerizeGroup(
// TODO(pdr): This should require a direct // TODO(pdr): This should require a direct
// compositing reason. // compositing reason.
last_display_item.IsScrollHitTest(); last_display_item.IsScrollHitTest();
pending_layers.push_back(PendingLayer( pending_layers_.emplace_back(
*chunk_it, chunk_it - paint_artifact.PaintChunks().begin(), *chunk_it, chunk_it - paint_artifact.PaintChunks().begin(),
requires_own_layer)); requires_own_layer);
chunk_it++; chunk_it++;
if (requires_own_layer) if (requires_own_layer)
continue; continue;
...@@ -628,9 +627,8 @@ void PaintArtifactCompositor::LayerizeGroup( ...@@ -628,9 +627,8 @@ void PaintArtifactCompositor::LayerizeGroup(
break; break;
// Case C: The following chunks belong to a subgroup. Process them by // Case C: The following chunks belong to a subgroup. Process them by
// a recursion call. // a recursion call.
wtf_size_t first_layer_in_subgroup = pending_layers.size(); wtf_size_t first_layer_in_subgroup = pending_layers_.size();
LayerizeGroup(paint_artifact, settings, pending_layers, LayerizeGroup(paint_artifact, settings, *unaliased_subgroup, chunk_it);
*unaliased_subgroup, chunk_it);
// Now the chunk iterator stepped over the subgroup we just saw. // Now the chunk iterator stepped over the subgroup we just saw.
// If the subgroup generated 2 or more layers then the subgroup must be // If the subgroup generated 2 or more layers then the subgroup must be
// composited to satisfy grouping requirement. // composited to satisfy grouping requirement.
...@@ -638,10 +636,10 @@ void PaintArtifactCompositor::LayerizeGroup( ...@@ -638,10 +636,10 @@ void PaintArtifactCompositor::LayerizeGroup(
// for example, Opacity(A+B) != Opacity(A) + Opacity(B), thus an effect // for example, Opacity(A+B) != Opacity(A) + Opacity(B), thus an effect
// either applied 100% within a layer, or not at all applied within layer // either applied 100% within a layer, or not at all applied within layer
// (i.e. applied by compositor render surface instead). // (i.e. applied by compositor render surface instead).
if (pending_layers.size() != first_layer_in_subgroup + 1) if (pending_layers_.size() != first_layer_in_subgroup + 1)
continue; continue;
// Now attempt to "decomposite" subgroup. // Now attempt to "decomposite" subgroup.
PendingLayer& subgroup_layer = pending_layers[first_layer_in_subgroup]; PendingLayer& subgroup_layer = pending_layers_[first_layer_in_subgroup];
if (!CanDecompositeEffect(*unaliased_subgroup, subgroup_layer)) if (!CanDecompositeEffect(*unaliased_subgroup, subgroup_layer))
continue; continue;
subgroup_layer.Upcast( subgroup_layer.Upcast(
...@@ -651,20 +649,21 @@ void PaintArtifactCompositor::LayerizeGroup( ...@@ -651,20 +649,21 @@ void PaintArtifactCompositor::LayerizeGroup(
: subgroup_layer.property_tree_state.Clip(), : subgroup_layer.property_tree_state.Clip(),
unaliased_group)); unaliased_group));
} }
// At this point pending_layers.back() is the either a layer from a // At this point pending_layers_.back() is the either a layer from a
// "decomposited" subgroup or a layer created from a chunk we just // "decomposited" subgroup or a layer created from a chunk we just
// processed. Now determine whether it could be merged into a previous // processed. Now determine whether it could be merged into a previous
// layer. // layer.
const PendingLayer& new_layer = pending_layers.back(); const PendingLayer& new_layer = pending_layers_.back();
DCHECK(!new_layer.requires_own_layer); DCHECK(!new_layer.requires_own_layer);
DCHECK_EQ(&unaliased_group, &new_layer.property_tree_state.Effect()); DCHECK_EQ(&unaliased_group, &new_layer.property_tree_state.Effect());
// This iterates pending_layers[first_layer_in_current_group:-1] in reverse. // This iterates pending_layers_[first_layer_in_current_group:-1] in
for (wtf_size_t candidate_index = pending_layers.size() - 1; // reverse.
for (wtf_size_t candidate_index = pending_layers_.size() - 1;
candidate_index-- > first_layer_in_current_group;) { candidate_index-- > first_layer_in_current_group;) {
PendingLayer& candidate_layer = pending_layers[candidate_index]; PendingLayer& candidate_layer = pending_layers_[candidate_index];
if (candidate_layer.CanMerge(new_layer)) { if (candidate_layer.CanMerge(new_layer)) {
candidate_layer.Merge(new_layer); candidate_layer.Merge(new_layer);
pending_layers.pop_back(); pending_layers_.pop_back();
break; break;
} }
if (MightOverlap(new_layer, candidate_layer)) if (MightOverlap(new_layer, candidate_layer))
...@@ -675,13 +674,15 @@ void PaintArtifactCompositor::LayerizeGroup( ...@@ -675,13 +674,15 @@ void PaintArtifactCompositor::LayerizeGroup(
void PaintArtifactCompositor::CollectPendingLayers( void PaintArtifactCompositor::CollectPendingLayers(
const PaintArtifact& paint_artifact, const PaintArtifact& paint_artifact,
const Settings& settings, const Settings& settings) {
Vector<PendingLayer>& pending_layers) {
Vector<PaintChunk>::const_iterator cursor = Vector<PaintChunk>::const_iterator cursor =
paint_artifact.PaintChunks().begin(); paint_artifact.PaintChunks().begin();
LayerizeGroup(paint_artifact, settings, pending_layers, // Shrink, but do not release the backing. Re-use it from the last frame.
EffectPaintPropertyNode::Root(), cursor); pending_layers_.Shrink(0);
LayerizeGroup(paint_artifact, settings, EffectPaintPropertyNode::Root(),
cursor);
DCHECK_EQ(paint_artifact.PaintChunks().end(), cursor); DCHECK_EQ(paint_artifact.PaintChunks().end(), cursor);
pending_layers_.ShrinkToReasonableCapacity();
} }
void SynthesizedClip::UpdateLayer(bool needs_layer, void SynthesizedClip::UpdateLayer(bool needs_layer,
...@@ -813,15 +814,13 @@ static void UpdateCompositorViewportProperties( ...@@ -813,15 +814,13 @@ static void UpdateCompositorViewportProperties(
// 9. All child transform nodes are also able to be de-composited. // 9. All child transform nodes are also able to be de-composited.
// This algorithm should be O(t+c+e) where t,c,e are the number of transform, // This algorithm should be O(t+c+e) where t,c,e are the number of transform,
// clip, and effect nodes in the full tree. // clip, and effect nodes in the full tree.
void PaintArtifactCompositor::DecompositeTransforms( void PaintArtifactCompositor::DecompositeTransforms() {
Vector<PendingLayer>* pending_layers) const {
DCHECK(pending_layers);
// TODO(masonfreed): CAP is not yet implemented here. // TODO(masonfreed): CAP is not yet implemented here.
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
return; return;
WTF::HashMap<const TransformPaintPropertyNode*, bool> can_be_decomposited; WTF::HashMap<const TransformPaintPropertyNode*, bool> can_be_decomposited;
WTF::HashSet<const void*> clips_and_effects_seen; WTF::HashSet<const void*> clips_and_effects_seen;
for (const auto& pending_layer : *pending_layers) { for (const auto& pending_layer : pending_layers_) {
const auto& property_state = pending_layer.property_tree_state; const auto& property_state = pending_layer.property_tree_state;
// Lambda to handle marking a transform node false, and walking up all true // Lambda to handle marking a transform node false, and walking up all true
...@@ -877,7 +876,7 @@ void PaintArtifactCompositor::DecompositeTransforms( ...@@ -877,7 +876,7 @@ void PaintArtifactCompositor::DecompositeTransforms(
// Now, for any transform nodes that can be de-composited, re-map their // Now, for any transform nodes that can be de-composited, re-map their
// transform to point to the correct parent, and set the // transform to point to the correct parent, and set the
// offset_to_transform_parent. // offset_to_transform_parent.
for (auto& pending_layer : *pending_layers) { for (auto& pending_layer : pending_layers_) {
const auto* transform_node = &pending_layer.property_tree_state.Transform(); const auto* transform_node = &pending_layer.property_tree_state.Transform();
while (transform_node && !transform_node->IsRoot() && while (transform_node && !transform_node->IsRoot() &&
can_be_decomposited.at(transform_node)) { can_be_decomposited.at(transform_node)) {
...@@ -919,14 +918,13 @@ void PaintArtifactCompositor::Update( ...@@ -919,14 +918,13 @@ void PaintArtifactCompositor::Update(
property_tree_manager_.Initialize(host->property_trees(), property_tree_manager_.Initialize(host->property_trees(),
&layer_list_builder); &layer_list_builder);
Vector<PendingLayer, 0> pending_layers; CollectPendingLayers(*paint_artifact, settings);
CollectPendingLayers(*paint_artifact, settings, pending_layers);
UpdateCompositorViewportProperties(viewport_properties, UpdateCompositorViewportProperties(viewport_properties,
property_tree_manager_, host); property_tree_manager_, host);
Vector<std::unique_ptr<ContentLayerClientImpl>> new_content_layer_clients; Vector<std::unique_ptr<ContentLayerClientImpl>> new_content_layer_clients;
new_content_layer_clients.ReserveCapacity(pending_layers.size()); new_content_layer_clients.ReserveCapacity(pending_layers_.size());
Vector<scoped_refptr<cc::Layer>> new_scroll_hit_test_layers; Vector<scoped_refptr<cc::Layer>> new_scroll_hit_test_layers;
// Maps from cc effect id to blink effects. Containing only the effects having // Maps from cc effect id to blink effects. Containing only the effects having
...@@ -937,11 +935,11 @@ void PaintArtifactCompositor::Update( ...@@ -937,11 +935,11 @@ void PaintArtifactCompositor::Update(
entry.in_use = false; entry.in_use = false;
// See if we can de-composite any transforms. // See if we can de-composite any transforms.
DecompositeTransforms(&pending_layers); DecompositeTransforms();
// Clear prior frame ids before inserting new ones. // Clear prior frame ids before inserting new ones.
composited_element_ids.clear(); composited_element_ids.clear();
for (auto& pending_layer : pending_layers) { for (auto& pending_layer : pending_layers_) {
const auto& property_state = pending_layer.property_tree_state; const auto& property_state = pending_layer.property_tree_state;
const auto& transform = property_state.Transform(); const auto& transform = property_state.Transform();
const auto& clip = property_state.Clip(); const auto& clip = property_state.Clip();
......
...@@ -222,13 +222,11 @@ class PLATFORM_EXPORT PaintArtifactCompositor final ...@@ -222,13 +222,11 @@ class PLATFORM_EXPORT PaintArtifactCompositor final
bool requires_own_layer; bool requires_own_layer;
}; };
void DecompositeTransforms(Vector<PendingLayer>* pending_layers) const; void DecompositeTransforms();
// Collects the PaintChunks into groups which will end up in the same // Collects the PaintChunks into groups which will end up in the same
// cc layer. This is the entry point of the layerization algorithm. // cc layer. This is the entry point of the layerization algorithm.
void CollectPendingLayers(const PaintArtifact&, void CollectPendingLayers(const PaintArtifact&, const Settings& settings);
const Settings& settings,
Vector<PendingLayer>& pending_layers);
// This is the internal recursion of collectPendingLayers. This function // This is the internal recursion of collectPendingLayers. This function
// loops over the list of paint chunks, scoped by an isolated group // loops over the list of paint chunks, scoped by an isolated group
...@@ -247,11 +245,10 @@ class PLATFORM_EXPORT PaintArtifactCompositor final ...@@ -247,11 +245,10 @@ class PLATFORM_EXPORT PaintArtifactCompositor final
// recursion, the layerization of the subgroup may be tested for merge & // recursion, the layerization of the subgroup may be tested for merge &
// overlap with other chunks in the parent group, if grouping requirement // overlap with other chunks in the parent group, if grouping requirement
// can be satisfied (and the effect node has no direct reason). // can be satisfied (and the effect node has no direct reason).
static void LayerizeGroup(const PaintArtifact&, void LayerizeGroup(const PaintArtifact&,
const Settings& settings, const Settings& settings,
Vector<PendingLayer>& pending_layers, const EffectPaintPropertyNode&,
const EffectPaintPropertyNode&, Vector<PaintChunk>::const_iterator& chunk_cursor);
Vector<PaintChunk>::const_iterator& chunk_cursor);
static bool MightOverlap(const PendingLayer&, const PendingLayer&); static bool MightOverlap(const PendingLayer&, const PendingLayer&);
static bool CanDecompositeEffect(const EffectPaintPropertyNode&, static bool CanDecompositeEffect(const EffectPaintPropertyNode&,
const PendingLayer&); const PendingLayer&);
...@@ -324,6 +321,8 @@ class PLATFORM_EXPORT PaintArtifactCompositor final ...@@ -324,6 +321,8 @@ class PLATFORM_EXPORT PaintArtifactCompositor final
PropertyTreeManager property_tree_manager_; PropertyTreeManager property_tree_manager_;
Vector<PendingLayer, 0> pending_layers_;
bool extra_data_for_testing_enabled_ = false; bool extra_data_for_testing_enabled_ = false;
std::unique_ptr<ExtraDataForTesting> extra_data_for_testing_; std::unique_ptr<ExtraDataForTesting> extra_data_for_testing_;
......
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