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

[SPv175] Delay translate/clip/effect conversion (to cc display item list).

This patch delays the recording of the translate/clip/effect until we
are confident that the operations will affect anything meaningful. This
eliminates the cases of save/clip/restore for example, since it skips
recording these operations.

Note that it also adds an early out to BoxPainterBase when we're not
painting either image or color (ie transparent). Without the early out
the record which is generated has a clip due to scroll adjustment, and
it's hard to say with a non-empty record whether it actually draws
anything.

Specifically from the bug, this eliminates the sequence
- save
-  translate
-  save
-   clip rect
-  restore
- restore

However, there is another sequence of
- save
- translate
- restore
which isn't coming from this code which still remains.

R=wangxianzhu@chromium.org, chrishtr@chromium.org

Bug: 795017
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2
Change-Id: Id750ead924deec5d208cc9d52b8bb873d448d3e0
Reviewed-on: https://chromium-review.googlesource.com/900264Reviewed-by: default avatarXianzhu Wang <wangxianzhu@chromium.org>
Commit-Queue: vmpstr <vmpstr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#534513}
parent 26aafb4c
...@@ -530,6 +530,9 @@ void BoxPainterBase::PaintFillLayer(const PaintInfo& paint_info, ...@@ -530,6 +530,9 @@ void BoxPainterBase::PaintFillLayer(const PaintInfo& paint_info,
return; return;
const FillLayerInfo info = GetFillLayerInfo(color, bg_layer, bleed_avoidance); const FillLayerInfo info = GetFillLayerInfo(color, bg_layer, bleed_avoidance);
// If we're not actually going to paint anything, abort early.
if (!info.should_paint_image && !info.should_paint_color)
return;
GraphicsContextStateSaver clip_with_scrolling_state_saver( GraphicsContextStateSaver clip_with_scrolling_state_saver(
context, info.is_clipped_with_local_scrolling); context, info.is_clipped_with_local_scrolling);
......
...@@ -19,22 +19,6 @@ namespace blink { ...@@ -19,22 +19,6 @@ namespace blink {
namespace { namespace {
void AppendDisplayItemToCcDisplayItemList(const DisplayItem& display_item,
cc::DisplayItemList& list,
const IntRect& visual_rect_in_layer) {
DCHECK(display_item.IsDrawing());
list.StartPaint();
if (auto record =
static_cast<const DrawingDisplayItem&>(display_item).GetPaintRecord())
list.push<cc::DrawRecordOp>(std::move(record));
// StartPaint() and EndPaintOfUnpaired() are called regardless of whether the
// record is null to ensure we'll set correct visual rects for the enclosing
// paired display items. This is especially important for filters that draw
// content by themselves but don't enclose any non-empty DrawingDisplayItem.
list.EndPaintOfUnpaired(visual_rect_in_layer);
}
void AppendRestore(cc::DisplayItemList& list, size_t n) { void AppendRestore(cc::DisplayItemList& list, size_t n) {
list.StartPaint(); list.StartPaint();
while (n--) while (n--)
...@@ -352,33 +336,76 @@ void ConversionContext::SwitchToEffect( ...@@ -352,33 +336,76 @@ void ConversionContext::SwitchToEffect(
void ConversionContext::Convert(const Vector<const PaintChunk*>& paint_chunks, void ConversionContext::Convert(const Vector<const PaintChunk*>& paint_chunks,
const DisplayItemList& display_items) { const DisplayItemList& display_items) {
bool translated = false;
bool need_translate = !layer_offset_.IsZero();
// This functor adjust the translation of the whole display list relative to
// layer offset. It's only called if we actually paint anything.
auto translate_once = [this, &translated, need_translate] {
if (translated || !need_translate)
return;
cc_list_.StartPaint();
cc_list_.push<cc::SaveOp>();
cc_list_.push<cc::TranslateOp>(-layer_offset_.x(), -layer_offset_.y());
cc_list_.EndPaintOfPairedBegin();
translated = true;
};
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 PropertyTreeState& chunk_state =
chunk.properties.property_tree_state.GetPropertyTreeState(); chunk.properties.property_tree_state.GetPropertyTreeState();
SwitchToEffect(chunk_state.Effect()); bool transformed = false;
SwitchToClip(chunk_state.Clip()); bool properties_adjusted = false;
bool transformed = chunk_state.Transform() != current_transform_; // This functor adjusts the properties for the current effect and clip once.
if (transformed) { // It's called if a DrawingDisplayItem draws content or is under an effect
cc_list_.StartPaint(); // that may draw content.
cc_list_.push<cc::SaveOp>(); auto adjust_properties_once = [this, &chunk_state, &transformed,
cc_list_.push<cc::ConcatOp>( &properties_adjusted] {
static_cast<SkMatrix>(TransformationMatrix::ToSkMatrix44( if (properties_adjusted)
GeometryMapper::SourceToDestinationProjection( return;
chunk_state.Transform(), current_transform_)))); SwitchToEffect(chunk_state.Effect());
cc_list_.EndPaintOfPairedBegin(); SwitchToClip(chunk_state.Clip());
} if (chunk_state.Transform() != current_transform_) {
transformed = true;
cc_list_.StartPaint();
cc_list_.push<cc::SaveOp>();
cc_list_.push<cc::ConcatOp>(
static_cast<SkMatrix>(TransformationMatrix::ToSkMatrix44(
GeometryMapper::SourceToDestinationProjection(
chunk_state.Transform(), current_transform_))));
cc_list_.EndPaintOfPairedBegin();
}
properties_adjusted = true;
};
for (const auto& item : display_items.ItemsInPaintChunk(chunk)) { for (const auto& item : display_items.ItemsInPaintChunk(chunk)) {
AppendDisplayItemToCcDisplayItemList( DCHECK(item.IsDrawing());
item, cc_list_, auto record =
PaintChunksToCcLayer::MapRectFromChunkToLayer( static_cast<const DrawingDisplayItem&>(item).GetPaintRecord();
FloatRect(item.VisualRect()), chunk, layer_state_, // If we have an empty paint record, then we would prefer not to draw it.
layer_offset_)); // However, if we also have a non-root effect, it means that the filter
// applied might draw something even if the record is empty. We need to
// "draw" this record in order to ensure that the effect has correct
// visual rects.
if ((!record || record->size() == 0) &&
chunk_state.Effect() == EffectPaintPropertyNode::Root()) {
continue;
}
translate_once();
adjust_properties_once();
cc_list_.StartPaint();
if (record && record->size() != 0)
cc_list_.push<cc::DrawRecordOp>(std::move(record));
cc_list_.EndPaintOfUnpaired(PaintChunksToCcLayer::MapRectFromChunkToLayer(
FloatRect(item.VisualRect()), chunk, layer_state_, layer_offset_));
} }
if (transformed) if (transformed)
AppendRestore(cc_list_, 1); AppendRestore(cc_list_, 1);
} }
if (translated)
AppendRestore(cc_list_, 1);
} }
} // unnamed namespace } // unnamed namespace
...@@ -389,19 +416,8 @@ void PaintChunksToCcLayer::ConvertInto( ...@@ -389,19 +416,8 @@ void PaintChunksToCcLayer::ConvertInto(
const gfx::Vector2dF& layer_offset, const gfx::Vector2dF& layer_offset,
const DisplayItemList& display_items, const DisplayItemList& display_items,
cc::DisplayItemList& cc_list) { cc::DisplayItemList& cc_list) {
bool need_translate = !layer_offset.IsZero();
if (need_translate) {
cc_list.StartPaint();
cc_list.push<cc::SaveOp>();
cc_list.push<cc::TranslateOp>(-layer_offset.x(), -layer_offset.y());
cc_list.EndPaintOfPairedBegin();
}
ConversionContext(layer_state, layer_offset, cc_list) ConversionContext(layer_state, layer_offset, cc_list)
.Convert(paint_chunks, display_items); .Convert(paint_chunks, display_items);
if (need_translate)
AppendRestore(cc_list, 1);
} }
scoped_refptr<cc::DisplayItemList> PaintChunksToCcLayer::Convert( scoped_refptr<cc::DisplayItemList> PaintChunksToCcLayer::Convert(
......
...@@ -129,13 +129,22 @@ struct TestChunks { ...@@ -129,13 +129,22 @@ struct TestChunks {
Vector<PaintChunk> chunks; Vector<PaintChunk> chunks;
DisplayItemList items = DisplayItemList(0); DisplayItemList items = DisplayItemList(0);
// Add a paint chunk with a non-empty paint record and given property nodes.
void AddChunk(const TransformPaintPropertyNode* t, void AddChunk(const TransformPaintPropertyNode* t,
const ClipPaintPropertyNode* c, const ClipPaintPropertyNode* c,
const EffectPaintPropertyNode* e) { const EffectPaintPropertyNode* e) {
size_t i = items.size();
auto record = sk_make_sp<PaintRecord>(); auto record = sk_make_sp<PaintRecord>();
record->push<cc::DrawRectOp>(SkRect::MakeXYWH(0, 0, 100, 100), record->push<cc::DrawRectOp>(SkRect::MakeXYWH(0, 0, 100, 100),
cc::PaintFlags()); cc::PaintFlags());
AddChunk(std::move(record), t, c, e);
}
// Add a paint chunk with a given paint record and property nodes.
void AddChunk(sk_sp<PaintRecord> record,
const TransformPaintPropertyNode* t,
const ClipPaintPropertyNode* c,
const EffectPaintPropertyNode* e) {
size_t i = items.size();
items.AllocateAndConstruct<DrawingDisplayItem>( items.AllocateAndConstruct<DrawingDisplayItem>(
DefaultId().client, DefaultId().type, std::move(record)); DefaultId().client, DefaultId().type, std::move(record));
chunks.emplace_back(i, i + 1, DefaultId(), PropertyTreeState(t, c, e)); chunks.emplace_back(i, i + 1, DefaultId(), PropertyTreeState(t, c, e));
...@@ -548,5 +557,103 @@ TEST_F(PaintChunksToCcLayerTest, NoncompositedClipPath) { ...@@ -548,5 +557,103 @@ TEST_F(PaintChunksToCcLayerTest, NoncompositedClipPath) {
cc::PaintOpType::Restore}))); // </clip_path> cc::PaintOpType::Restore}))); // </clip_path>
} }
TEST_F(PaintChunksToCcLayerTest, EmptyClipsAreElided) {
scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
c0(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
scoped_refptr<ClipPaintPropertyNode> c1c2 = ClipPaintPropertyNode::Create(
c1.get(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
scoped_refptr<ClipPaintPropertyNode> c2 = ClipPaintPropertyNode::Create(
c0(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
TestChunks chunks;
chunks.AddChunk(nullptr, t0(), c1.get(), e0());
chunks.AddChunk(nullptr, t0(), c1c2.get(), e0());
chunks.AddChunk(nullptr, t0(), c1c2.get(), e0());
chunks.AddChunk(nullptr, t0(), c1c2.get(), e0());
chunks.AddChunk(nullptr, t0(), c1.get(), e0());
// D1
chunks.AddChunk(t0(), c2.get(), e0());
sk_sp<PaintRecord> output =
PaintChunksToCcLayer::Convert(
chunks.GetChunkList(), PropertyTreeState(t0(), c0(), e0()),
gfx::Vector2dF(), chunks.items,
cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
->ReleaseAsRecord();
// Note that c1 and c1c2 are elided.
EXPECT_THAT(output, Pointee(PaintRecordMatcher::Make({
cc::PaintOpType::Save, //
cc::PaintOpType::ClipRect, // <c2>
cc::PaintOpType::DrawRecord, // D1
cc::PaintOpType::Restore, // </c2>
})));
}
TEST_F(PaintChunksToCcLayerTest, NonEmptyClipsAreStored) {
scoped_refptr<ClipPaintPropertyNode> c1 = ClipPaintPropertyNode::Create(
c0(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
scoped_refptr<ClipPaintPropertyNode> c1c2 = ClipPaintPropertyNode::Create(
c1.get(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
scoped_refptr<ClipPaintPropertyNode> c2 = ClipPaintPropertyNode::Create(
c0(), t0(), FloatRoundedRect(0.f, 0.f, 1.f, 1.f));
TestChunks chunks;
chunks.AddChunk(nullptr, t0(), c1.get(), e0());
chunks.AddChunk(nullptr, t0(), c1c2.get(), e0());
chunks.AddChunk(nullptr, t0(), c1c2.get(), e0());
// D1
chunks.AddChunk(t0(), c1c2.get(), e0());
chunks.AddChunk(nullptr, t0(), c1.get(), e0());
// D2
chunks.AddChunk(t0(), c2.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, Pointee(PaintRecordMatcher::Make({
cc::PaintOpType::Save, //
cc::PaintOpType::ClipRect, // <c1>
cc::PaintOpType::Save, //
cc::PaintOpType::ClipRect, // <c2>
cc::PaintOpType::DrawRecord, // D1
cc::PaintOpType::Restore, // </c2>
cc::PaintOpType::Restore, // </c1>
cc::PaintOpType::Save, //
cc::PaintOpType::ClipRect, // <c2>
cc::PaintOpType::DrawRecord, // D2
cc::PaintOpType::Restore, // </c2>
})));
}
TEST_F(PaintChunksToCcLayerTest, EmptyEffectsAreStored) {
scoped_refptr<EffectPaintPropertyNode> e1 = EffectPaintPropertyNode::Create(
e0(), t0(), c0(), kColorFilterNone, CompositorFilterOperations(), 0.5,
SkBlendMode::kSrcOver);
TestChunks chunks;
chunks.AddChunk(nullptr, t0(), c0(), e0());
chunks.AddChunk(nullptr, t0(), c0(), e1.get());
sk_sp<PaintRecord> output =
PaintChunksToCcLayer::Convert(
chunks.GetChunkList(), PropertyTreeState(t0(), c0(), e0()),
gfx::Vector2dF(), chunks.items,
cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer)
->ReleaseAsRecord();
// TODO(vmpstr): Ideally this would change to be one save layer if possible.
EXPECT_THAT(output, Pointee(PaintRecordMatcher::Make({
cc::PaintOpType::SaveLayer, // <e1>
cc::PaintOpType::SaveLayer, //
cc::PaintOpType::Restore, //
cc::PaintOpType::Restore, // </e1>
})));
}
} // 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