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

[SPv175+] Reuse transform state between chunks with the same property tree state

This saves a pair of RestoreOp and SaveOp between chunks with the same
property tree state.

https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/wangxianzhu-20180405050948/html/index.html
showed that this CL can reduce paint_op_count by 3%.

Bug: 803867
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2
Change-Id: I979b946e3642773152e19b8648a6e9c3e7268257
Reviewed-on: https://chromium-review.googlesource.com/997175
Commit-Queue: Xianzhu Wang <wangxianzhu@chromium.org>
Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#548802}
parent 547833ae
...@@ -30,6 +30,7 @@ class ConversionContext { ...@@ -30,6 +30,7 @@ class ConversionContext {
current_transform_(layer_state.Transform()), current_transform_(layer_state.Transform()),
current_clip_(layer_state.Clip()), current_clip_(layer_state.Clip()),
current_effect_(layer_state.Effect()), current_effect_(layer_state.Effect()),
chunk_to_layer_mapper_(layer_state_, layer_offset_),
cc_list_(cc_list) {} cc_list_(cc_list) {}
~ConversionContext(); ~ConversionContext();
...@@ -82,6 +83,15 @@ class ConversionContext { ...@@ -82,6 +83,15 @@ class ConversionContext {
void Convert(const Vector<const PaintChunk*>&, const DisplayItemList&); void Convert(const Vector<const PaintChunk*>&, const DisplayItemList&);
private: private:
// Adjust the translation of the whole display list relative to layer offset.
// It's only called if we actually paint anything.
void TranslateForLayerOffsetOnce();
// Switch the current property tree state to the chunk's state. It's only
// called if we actually paint anything, and should execute for a chunk
// only once.
void SwitchToChunkState(const PaintChunk&);
// Switch the current clip to the target state, staying in the same effect. // Switch the current clip to the target state, staying in the same effect.
// It is no-op if the context is already in the target state. // It is no-op if the context is already in the target state.
// Otherwise zero or more clips will be popped from or pushed onto the // Otherwise zero or more clips will be popped from or pushed onto the
...@@ -109,6 +119,13 @@ class ConversionContext { ...@@ -109,6 +119,13 @@ class ConversionContext {
// The current effect will change to the target effect. // The current effect will change to the target effect.
void SwitchToEffect(const EffectPaintPropertyNode*); void SwitchToEffect(const EffectPaintPropertyNode*);
// Switch the current transform to the target state.
void SwitchToTransform(const TransformPaintPropertyNode*);
// End the transform state that is estalished by SwitchToTransform().
// Called when the next chunk has different property tree state and when we
// have processed all chunks.
void EndTransform();
// Applies combined transform from |current_transform_| to |target_transform| // Applies combined transform from |current_transform_| to |target_transform|
// This function doesn't change |current_transform_|. // This function doesn't change |current_transform_|.
void ApplyTransform(const TransformPaintPropertyNode* target_transform) { void ApplyTransform(const TransformPaintPropertyNode* target_transform) {
...@@ -160,11 +177,17 @@ class ConversionContext { ...@@ -160,11 +177,17 @@ class ConversionContext {
const PropertyTreeState& layer_state_; const PropertyTreeState& layer_state_;
gfx::Vector2dF layer_offset_; gfx::Vector2dF layer_offset_;
bool translated_for_layer_offset_ = false;
const TransformPaintPropertyNode* current_transform_; const TransformPaintPropertyNode* current_transform_;
const ClipPaintPropertyNode* current_clip_; const ClipPaintPropertyNode* current_clip_;
const EffectPaintPropertyNode* current_effect_; const EffectPaintPropertyNode* current_effect_;
// The previous transform state before SwitchToTransform(). When the next
// chunk's state is different from the current state we should restore to
// this transform.
const TransformPaintPropertyNode* previous_transform_ = nullptr;
// State stack. // State stack.
// The size of the stack is the number of nested paired items that are // The size of the stack is the number of nested paired items that are
// currently nested. Note that this is a "restore stack", i.e. the top // currently nested. Note that this is a "restore stack", i.e. the top
...@@ -200,17 +223,46 @@ class ConversionContext { ...@@ -200,17 +223,46 @@ class ConversionContext {
}; };
Vector<EffectBoundsInfo> effect_bounds_stack_; Vector<EffectBoundsInfo> effect_bounds_stack_;
ChunkToLayerMapper chunk_to_layer_mapper_;
cc::DisplayItemList& cc_list_; cc::DisplayItemList& cc_list_;
}; };
ConversionContext::~ConversionContext() { ConversionContext::~ConversionContext() {
// End all states. // End all states.
EndTransform();
while (state_stack_.size()) { while (state_stack_.size()) {
if (state_stack_.back().type == StateEntry::kEffect) if (state_stack_.back().type == StateEntry::kEffect)
EndEffect(); EndEffect();
else else
EndClip(); EndClip();
} }
if (translated_for_layer_offset_)
AppendRestore(1);
}
void ConversionContext::TranslateForLayerOffsetOnce() {
if (translated_for_layer_offset_ || layer_offset_.IsZero())
return;
cc_list_.StartPaint();
cc_list_.push<cc::SaveOp>();
cc_list_.push<cc::TranslateOp>(-layer_offset_.x(), -layer_offset_.y());
cc_list_.EndPaintOfPairedBegin();
translated_for_layer_offset_ = true;
}
void ConversionContext::SwitchToChunkState(const PaintChunk& chunk) {
chunk_to_layer_mapper_.SwitchToChunk(chunk);
const auto& chunk_state = chunk.properties.property_tree_state;
if (chunk_state.Effect() != current_effect_ ||
chunk_state.Clip() != current_clip_ ||
chunk_state.Transform() != current_transform_)
EndTransform();
SwitchToEffect(chunk_state.Effect());
SwitchToClip(chunk_state.Clip());
SwitchToTransform(chunk_state.Transform());
} }
void ConversionContext::SwitchToClip(const ClipPaintPropertyNode* target_clip) { void ConversionContext::SwitchToClip(const ClipPaintPropertyNode* target_clip) {
...@@ -529,51 +581,38 @@ void ConversionContext::EndClip() { ...@@ -529,51 +581,38 @@ void ConversionContext::EndClip() {
state_stack_.pop_back(); state_stack_.pop_back();
} }
void ConversionContext::Convert(const Vector<const PaintChunk*>& paint_chunks, void ConversionContext::SwitchToTransform(
const DisplayItemList& display_items) { const TransformPaintPropertyNode* target_transform) {
bool translated = false; if (target_transform == current_transform_)
bool need_translate = !layer_offset_.IsZero(); return;
// This functor adjust the translation of the whole display list relative to
// layer offset. It's only called if we actually paint anything. DCHECK_EQ(nullptr, previous_transform_);
auto translate_once = [this, &translated, need_translate] { cc_list_.StartPaint();
if (translated || !need_translate) cc_list_.push<cc::SaveOp>();
return; ApplyTransform(target_transform);
cc_list_.StartPaint(); cc_list_.EndPaintOfPairedBegin();
cc_list_.push<cc::SaveOp>(); previous_transform_ = current_transform_;
cc_list_.push<cc::TranslateOp>(-layer_offset_.x(), -layer_offset_.y()); current_transform_ = target_transform;
cc_list_.EndPaintOfPairedBegin(); }
translated = true;
};
ChunkToLayerMapper mapper(layer_state_, layer_offset_); void ConversionContext::EndTransform() {
if (!previous_transform_)
return;
cc_list_.StartPaint();
cc_list_.push<cc::RestoreOp>();
cc_list_.EndPaintOfPairedEnd();
current_transform_ = previous_transform_;
previous_transform_ = nullptr;
}
void ConversionContext::Convert(const Vector<const PaintChunk*>& paint_chunks,
const DisplayItemList& display_items) {
for (auto chunk_it = paint_chunks.begin(); chunk_it != paint_chunks.end(); for (auto chunk_it = paint_chunks.begin(); chunk_it != paint_chunks.end();
chunk_it++) { chunk_it++) {
const PaintChunk& chunk = **chunk_it; const PaintChunk& chunk = **chunk_it;
const PropertyTreeState& chunk_state = const auto& chunk_state = chunk.properties.property_tree_state;
chunk.properties.property_tree_state.GetPropertyTreeState(); bool switched_to_chunk_state = false;
bool transformed = false;
bool properties_adjusted = false;
// This functor adjusts the properties for the current effect and clip once.
// It's called if a DrawingDisplayItem draws content or is under an effect
// that may draw content.
auto adjust_properties_once = [this, &chunk_state, &transformed,
&properties_adjusted] {
if (properties_adjusted)
return;
SwitchToEffect(chunk_state.Effect());
SwitchToClip(chunk_state.Clip());
if (chunk_state.Transform() != current_transform_) {
transformed = true;
cc_list_.StartPaint();
cc_list_.push<cc::SaveOp>();
ApplyTransform(chunk_state.Transform());
cc_list_.EndPaintOfPairedBegin();
}
properties_adjusted = true;
};
mapper.SwitchToChunk(chunk);
for (const auto& item : display_items.ItemsInPaintChunk(chunk)) { for (const auto& item : display_items.ItemsInPaintChunk(chunk)) {
DCHECK(item.IsDrawing()); DCHECK(item.IsDrawing());
...@@ -589,19 +628,20 @@ void ConversionContext::Convert(const Vector<const PaintChunk*>& paint_chunks, ...@@ -589,19 +628,20 @@ void ConversionContext::Convert(const Vector<const PaintChunk*>& paint_chunks,
continue; continue;
} }
translate_once(); TranslateForLayerOffsetOnce();
adjust_properties_once(); if (!switched_to_chunk_state) {
SwitchToChunkState(chunk);
switched_to_chunk_state = true;
}
cc_list_.StartPaint(); cc_list_.StartPaint();
if (record && record->size() != 0) if (record && record->size() != 0)
cc_list_.push<cc::DrawRecordOp>(std::move(record)); cc_list_.push<cc::DrawRecordOp>(std::move(record));
cc_list_.EndPaintOfUnpaired(mapper.MapVisualRect(item.VisualRect())); cc_list_.EndPaintOfUnpaired(
chunk_to_layer_mapper_.MapVisualRect(item.VisualRect()));
} }
if (transformed)
AppendRestore(1);
UpdateEffectBounds(chunk.bounds, chunk_state.Transform()); UpdateEffectBounds(chunk.bounds, chunk_state.Transform());
} }
if (translated)
AppendRestore(1);
} }
} // unnamed namespace } // unnamed namespace
......
...@@ -824,5 +824,47 @@ TEST_F(PaintChunksToCcLayerTest, CombineClips) { ...@@ -824,5 +824,47 @@ TEST_F(PaintChunksToCcLayerTest, CombineClips) {
cc::PaintOpType::Restore})); // </c1+c2> cc::PaintOpType::Restore})); // </c1+c2>
} }
TEST_F(PaintChunksToCcLayerTest, ChunksSamePropertyTreeState) {
auto t1 = TransformPaintPropertyNode::Create(
t0(), TransformationMatrix().Scale(2.f), FloatPoint3D());
auto t2 = TransformPaintPropertyNode::Create(
t1.get(), TransformationMatrix().Scale(3.f), FloatPoint3D());
auto c1 = ClipPaintPropertyNode::Create(c0(), t1.get(),
FloatRoundedRect(0, 0, 100, 100));
TestChunks chunks;
chunks.AddChunk(t0(), c0(), e0());
chunks.AddChunk(t1.get(), c0(), e0());
chunks.AddChunk(t1.get(), c0(), e0());
chunks.AddChunk(t1.get(), c1.get(), e0());
chunks.AddChunk(t1.get(), c1.get(), e0());
chunks.AddChunk(t2.get(), c1.get(), e0());
chunks.AddChunk(t2.get(), c1.get(), e0());
sk_sp<PaintRecord> output =
PaintChunksToCcLayer::Convert(
chunks.GetChunkList(), PropertyTreeState(t0(), c0(), e0()),
gfx::Vector2dF(), chunks.items,
cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
->ReleaseAsRecord();
EXPECT_THAT(*output,
PaintRecordMatcher::Make(
{cc::PaintOpType::DrawRecord, // <p0/>
cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1>
cc::PaintOpType::DrawRecord, // <p1/>
cc::PaintOpType::DrawRecord, // <p2/>
cc::PaintOpType::Restore, // </t1>
cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t1>
cc::PaintOpType::ClipRect, // <c1>
cc::PaintOpType::DrawRecord, // <p3/>
cc::PaintOpType::DrawRecord, // <p4/>
cc::PaintOpType::Save, cc::PaintOpType::Concat, // <t2>
cc::PaintOpType::DrawRecord, // <p5/>
cc::PaintOpType::DrawRecord, // <p6/>
cc::PaintOpType::Restore, // </t2>
cc::PaintOpType::Restore})); // </c1></t1>
}
} // namespace } // namespace
} // namespace blink } // namespace blink
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