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

[SPv175+] Improve visual rect mapping performance

Add ChunkToLayerMapper to reduce repeated costs when mapping many rects
from the same chunk state to the same layer state. It also tries to
reuse computed transforms and clips across chunk states to the same
layer state.

Performance:
- cluster telemetry: https://ct.skia.org/results/cluster-telemetry/tasks/chromium_perf_runs/wangxianzhu-20180308192816/html/index.html
 3.5% improvement of record_time
- pinpoint: https://pinpoint-dot-chromeperf.appspot.com/results2/17a3d0fc440000?r=chromium%4054ab408&s=%25%CE%94avg&g=name&c=0
 6% improvement of record_time

Bug: 803867
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2
Change-Id: I8c20ff7525894767e601270e2cdd291828941f05
Reviewed-on: https://chromium-review.googlesource.com/952291
Commit-Queue: Xianzhu Wang <wangxianzhu@chromium.org>
Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#542274}
parent e5913dc4
......@@ -1053,6 +1053,8 @@ jumbo_component("platform") {
"graphics/VideoFrameSubmitter.h",
"graphics/WebGraphicsContext3DProviderWrapper.cpp",
"graphics/WebGraphicsContext3DProviderWrapper.h",
"graphics/compositing/ChunkToLayerMapper.cpp",
"graphics/compositing/ChunkToLayerMapper.h",
"graphics/compositing/CompositedLayerRasterInvalidator.cpp",
"graphics/compositing/CompositedLayerRasterInvalidator.h",
"graphics/compositing/ContentLayerClientImpl.cpp",
......@@ -1856,6 +1858,7 @@ jumbo_source_set("blink_platform_unittests_sources") {
"graphics/PlaceholderImageTest.cpp",
"graphics/StaticBitmapImageTest.cpp",
"graphics/VideoFrameSubmitterTest.cpp",
"graphics/compositing/ChunkToLayerMapperTest.cpp",
"graphics/compositing/CompositedLayerRasterInvalidatorTest.cpp",
"graphics/compositing/PaintArtifactCompositorTest.cpp",
"graphics/compositing/PaintChunksToCcLayerTest.cpp",
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "platform/graphics/compositing/ChunkToLayerMapper.h"
#include "platform/graphics/paint/GeometryMapper.h"
#include "platform/graphics/paint/PaintChunk.h"
namespace blink {
void ChunkToLayerMapper::SwitchToChunk(const PaintChunk& chunk) {
outset_for_raster_effects_ = chunk.outset_for_raster_effects;
const auto& new_chunk_state =
chunk.properties.property_tree_state.GetPropertyTreeState();
if (new_chunk_state == chunk_state_)
return;
if (new_chunk_state == layer_state_) {
has_filter_that_moves_pixels_ = false;
transform_ = TransformationMatrix().Translate(-layer_offset_.x(),
-layer_offset_.y());
clip_rect_ = FloatClipRect();
chunk_state_ = new_chunk_state;
return;
}
if (new_chunk_state.Transform() != chunk_state_.Transform()) {
transform_ = GeometryMapper::SourceToDestinationProjection(
new_chunk_state.Transform(), layer_state_.Transform());
transform_.PostTranslate(-layer_offset_.x(), -layer_offset_.y());
}
bool new_has_filter_that_moves_pixels = has_filter_that_moves_pixels_;
if (new_chunk_state.Effect() != chunk_state_.Effect()) {
new_has_filter_that_moves_pixels = false;
for (const auto* effect = new_chunk_state.Effect();
effect && effect != layer_state_.Effect(); effect = effect->Parent()) {
if (effect->HasFilterThatMovesPixels()) {
new_has_filter_that_moves_pixels = true;
break;
}
}
}
bool needs_clip_recalculation =
new_has_filter_that_moves_pixels != has_filter_that_moves_pixels_ ||
new_chunk_state.Clip() != chunk_state_.Clip();
if (needs_clip_recalculation) {
clip_rect_ =
GeometryMapper::LocalToAncestorClipRect(new_chunk_state, layer_state_);
if (!clip_rect_.IsInfinite())
clip_rect_.MoveBy(FloatPoint(-layer_offset_.x(), -layer_offset_.y()));
}
chunk_state_ = new_chunk_state;
has_filter_that_moves_pixels_ = new_has_filter_that_moves_pixels;
}
IntRect ChunkToLayerMapper::MapVisualRect(const FloatRect& rect) const {
if (rect.IsEmpty())
return IntRect();
if (UNLIKELY(has_filter_that_moves_pixels_))
return MapUsingGeometryMapper(rect);
FloatRect mapped_rect = transform_.MapRect(rect);
if (!mapped_rect.IsEmpty() && !clip_rect_.IsInfinite())
mapped_rect.Intersect(clip_rect_.Rect());
if (mapped_rect.IsEmpty()) {
DCHECK_EQ(IntRect(), MapUsingGeometryMapper(rect));
return IntRect();
}
mapped_rect.Inflate(outset_for_raster_effects_);
auto result = EnclosingIntRect(mapped_rect);
#if DCHECK_IS_ON()
auto slow_result = MapUsingGeometryMapper(rect);
if (result != slow_result) {
// Not a DCHECK because this may result from a floating point error.
LOG(WARNING) << "ChunkToLayerMapper::MapVisualRect: Different results from"
<< "fast path (" << result << ") and slow path ("
<< slow_result << ")";
}
#endif
return result;
}
// This is called when the fast path doesn't apply if there is any filter that
// moves pixels. GeometryMapper::LocalToAncestorVisualRect() will apply the
// visual effects of the filters, though slowly.
IntRect ChunkToLayerMapper::MapUsingGeometryMapper(
const FloatRect& rect) const {
FloatClipRect visual_rect(rect);
GeometryMapper::LocalToAncestorVisualRect(chunk_state_, layer_state_,
visual_rect);
if (visual_rect.Rect().IsEmpty())
return IntRect();
visual_rect.Rect().Move(-layer_offset_.x(), -layer_offset_.y());
visual_rect.Rect().Inflate(outset_for_raster_effects_);
return EnclosingIntRect(visual_rect.Rect());
}
} // namespace blink
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ChunkToLayerMapper_h
#define ChunkToLayerMapper_h
#include "platform/graphics/paint/FloatClipRect.h"
#include "platform/graphics/paint/PropertyTreeState.h"
namespace blink {
struct PaintChunk;
// Maps geometry from PaintChunks to the containing composited layer.
// It provides higher performance than GeometryMapper by reusing computed
// transforms and clips for unchanged states within or across paint chunks.
class PLATFORM_EXPORT ChunkToLayerMapper {
DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
public:
ChunkToLayerMapper(const PropertyTreeState& layer_state,
const gfx::Vector2dF& layer_offset)
: layer_state_(layer_state),
layer_offset_(layer_offset),
chunk_state_(nullptr, nullptr, nullptr) {}
// This class can map from multiple chunks. Before mapping from a chunk, this
// method must be called to prepare for the chunk.
void SwitchToChunk(const PaintChunk&);
// Maps a visual rectangle in the current chunk space into the layer space.
IntRect MapVisualRect(const FloatRect&) const;
// Returns the combined transform from the current chunk to the layer.
const TransformationMatrix& Transform() const { return transform_; }
// Returns the combined clip from the current chunk to the layer if it can
// be calculated (there is no filter that moves pixels), or infinite loose
// clip rect otherwise.
const FloatClipRect& ClipRect() const { return clip_rect_; }
private:
friend class ChunkToLayerMapperTest;
IntRect MapUsingGeometryMapper(const FloatRect&) const;
const PropertyTreeState layer_state_;
const gfx::Vector2dF layer_offset_;
// The following fields are chunk-specific which are updated in
// SwitchToChunk().
PropertyTreeState chunk_state_;
float outset_for_raster_effects_ = 0.f;
TransformationMatrix transform_;
FloatClipRect clip_rect_;
// True if there is any pixel-moving filter between chunk state and layer
// state, and we will call GeometryMapper for each mapping.
bool has_filter_that_moves_pixels_ = false;
};
} // namespace blink
#endif // PaintArtifactCompositor_h
......@@ -30,44 +30,6 @@ void CompositedLayerRasterInvalidator::SetTracksRasterInvalidations(
}
}
IntRect CompositedLayerRasterInvalidator::MapRectFromChunkToLayer(
const FloatRect& r,
const PaintChunk& chunk,
const PropertyTreeState& layer_state) const {
return ClipByLayerBounds(PaintChunksToCcLayer::MapRectFromChunkToLayer(
r, chunk, layer_state, layer_bounds_.OffsetFromOrigin()));
}
TransformationMatrix CompositedLayerRasterInvalidator::ChunkToLayerTransform(
const PaintChunk& chunk,
const PropertyTreeState& layer_state) const {
auto matrix = GeometryMapper::SourceToDestinationProjection(
chunk.properties.property_tree_state.Transform(),
layer_state.Transform());
matrix.Translate(-layer_bounds_.x(), -layer_bounds_.y());
return matrix;
}
// Returns the clip rect when we know it is precise (no radius, no complex
// transform, no pixel moving filter, etc.)
FloatClipRect CompositedLayerRasterInvalidator::ChunkToLayerClip(
const PaintChunk& chunk,
const PropertyTreeState& layer_state) const {
FloatClipRect clip_rect;
if (chunk.properties.property_tree_state.Effect() != layer_state.Effect()) {
// Don't bother GeometryMapper because we don't need the rect when it's not
// tight because of the effect nodes.
clip_rect.ClearIsTight();
} else {
clip_rect = GeometryMapper::LocalToAncestorClipRect(
chunk.properties.property_tree_state.GetPropertyTreeState(),
layer_state);
if (clip_rect.IsTight())
clip_rect.MoveBy(FloatPoint(-layer_bounds_.x(), -layer_bounds_.y()));
}
return clip_rect;
}
size_t CompositedLayerRasterInvalidator::MatchNewChunkToOldChunk(
const PaintChunk& new_chunk,
size_t old_index) {
......@@ -141,15 +103,18 @@ CompositedLayerRasterInvalidator::ChunkPropertiesChanged(
// is slightly larger than O(n).
void CompositedLayerRasterInvalidator::GenerateRasterInvalidations(
const Vector<const PaintChunk*>& new_chunks,
const Vector<PaintChunkInfo>& new_chunks_info,
const PropertyTreeState& layer_state) {
const PropertyTreeState& layer_state,
Vector<PaintChunkInfo>& new_chunks_info) {
ChunkToLayerMapper mapper(layer_state, layer_bounds_.OffsetFromOrigin());
Vector<bool> old_chunks_matched;
old_chunks_matched.resize(paint_chunks_info_.size());
size_t old_index = 0;
size_t max_matched_old_index = 0;
for (size_t new_index = 0; new_index < new_chunks.size(); ++new_index) {
const auto& new_chunk = *new_chunks[new_index];
const auto& new_chunk_info = new_chunks_info[new_index];
mapper.SwitchToChunk(new_chunk);
const auto& new_chunk_info =
new_chunks_info.emplace_back(*this, mapper, new_chunk);
if (!new_chunk.is_cacheable) {
FullyInvalidateNewChunk(new_chunk_info,
......@@ -191,7 +156,7 @@ void CompositedLayerRasterInvalidator::GenerateRasterInvalidations(
IncrementallyInvalidateChunk(old_chunk_info, new_chunk_info);
// Add the raster invalidations found by PaintController within the chunk.
AddDisplayItemRasterInvalidations(new_chunk, layer_state);
AddDisplayItemRasterInvalidations(new_chunk, mapper);
}
old_index = matched_old_index + 1;
......@@ -213,14 +178,17 @@ void CompositedLayerRasterInvalidator::GenerateRasterInvalidations(
void CompositedLayerRasterInvalidator::AddDisplayItemRasterInvalidations(
const PaintChunk& chunk,
const PropertyTreeState& layer_state) {
const ChunkToLayerMapper& mapper) {
DCHECK(chunk.raster_invalidation_tracking.IsEmpty() ||
chunk.raster_invalidation_rects.size() ==
chunk.raster_invalidation_tracking.size());
if (chunk.raster_invalidation_rects.IsEmpty())
return;
for (size_t i = 0; i < chunk.raster_invalidation_rects.size(); ++i) {
auto rect = MapRectFromChunkToLayer(chunk.raster_invalidation_rects[i],
chunk, layer_state);
auto rect = ClipByLayerBounds(
mapper.MapVisualRect(chunk.raster_invalidation_rects[i]));
if (rect.IsEmpty())
continue;
raster_invalidation_function_(rect);
......@@ -300,26 +268,34 @@ void CompositedLayerRasterInvalidator::Generate(
if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled())
EnsureTracking();
if (tracking_info_) {
for (const auto* chunk : paint_chunks) {
tracking_info_->new_client_debug_names.insert(
&chunk->id.client, chunk->id.client.DebugName());
}
}
bool layer_bounds_was_empty = layer_bounds_.IsEmpty();
layer_bounds_ = layer_bounds;
Vector<PaintChunkInfo> new_chunks_info;
new_chunks_info.ReserveCapacity(paint_chunks.size());
for (const auto* chunk : paint_chunks) {
new_chunks_info.push_back(PaintChunkInfo(
MapRectFromChunkToLayer(chunk->bounds, *chunk, layer_state),
ChunkToLayerTransform(*chunk, layer_state),
ChunkToLayerClip(*chunk, layer_state), *chunk));
if (tracking_info_) {
tracking_info_->new_client_debug_names.insert(
&chunk->id.client, chunk->id.client.DebugName());
if (layer_bounds_was_empty || layer_bounds_.IsEmpty()) {
// No raster invalidation is needed if either the old bounds or the new
// bounds is empty, but we still need to update new_chunks_info for the
// next cycle.
ChunkToLayerMapper mapper(layer_state, layer_bounds.OffsetFromOrigin());
for (const auto* chunk : paint_chunks) {
mapper.SwitchToChunk(*chunk);
new_chunks_info.emplace_back(*this, mapper, *chunk);
}
} else {
GenerateRasterInvalidations(paint_chunks, layer_state, new_chunks_info);
}
if (!layer_bounds_was_empty && !layer_bounds_.IsEmpty())
GenerateRasterInvalidations(paint_chunks, new_chunks_info, layer_state);
paint_chunks_info_ = std::move(new_chunks_info);
if (tracking_info_) {
tracking_info_->old_client_debug_names =
std::move(tracking_info_->new_client_debug_names);
......
......@@ -5,6 +5,7 @@
#ifndef CompositedLayerRasterInvalidator_h
#define CompositedLayerRasterInvalidator_h
#include "platform/graphics/compositing/ChunkToLayerMapper.h"
#include "platform/graphics/paint/FloatClipRect.h"
#include "platform/graphics/paint/PaintChunk.h"
#include "platform/graphics/paint/RasterInvalidationTracking.h"
......@@ -48,13 +49,13 @@ class PLATFORM_EXPORT CompositedLayerRasterInvalidator {
friend class CompositedLayerRasterInvalidatorTest;
struct PaintChunkInfo {
PaintChunkInfo(const IntRect& bounds,
const TransformationMatrix& chunk_to_layer_transform,
const FloatClipRect& chunk_to_layer_clip,
PaintChunkInfo(const CompositedLayerRasterInvalidator& invalidator,
const ChunkToLayerMapper& mapper,
const PaintChunk& chunk)
: bounds_in_layer(bounds),
chunk_to_layer_transform(chunk_to_layer_transform),
chunk_to_layer_clip(chunk_to_layer_clip),
: bounds_in_layer(invalidator.ClipByLayerBounds(
mapper.MapVisualRect(chunk.bounds))),
chunk_to_layer_transform(mapper.Transform()),
chunk_to_layer_clip(mapper.ClipRect()),
id(chunk.id),
is_cacheable(chunk.is_cacheable),
properties(chunk.properties) {}
......@@ -71,22 +72,12 @@ class PLATFORM_EXPORT CompositedLayerRasterInvalidator {
PaintChunkProperties properties;
};
IntRect MapRectFromChunkToLayer(const FloatRect&,
const PaintChunk&,
const PropertyTreeState& layer_state) const;
TransformationMatrix ChunkToLayerTransform(
const PaintChunk&,
const PropertyTreeState& layer_state) const;
FloatClipRect ChunkToLayerClip(const PaintChunk&,
const PropertyTreeState& layer_state) const;
void GenerateRasterInvalidations(
const Vector<const PaintChunk*>& new_chunks,
const Vector<PaintChunkInfo>& new_chunks_info,
const PropertyTreeState& layer_state);
void GenerateRasterInvalidations(const Vector<const PaintChunk*>& new_chunks,
const PropertyTreeState& layer_state,
Vector<PaintChunkInfo>& new_chunks_info);
size_t MatchNewChunkToOldChunk(const PaintChunk& new_chunk, size_t old_index);
void AddDisplayItemRasterInvalidations(const PaintChunk&,
const PropertyTreeState& layer_state);
const ChunkToLayerMapper&);
void IncrementallyInvalidateChunk(const PaintChunkInfo& old_chunk,
const PaintChunkInfo& new_chunk);
void FullyInvalidateChunk(const PaintChunkInfo& old_chunk,
......
......@@ -294,10 +294,8 @@ TEST_F(CompositedLayerRasterInvalidatorTest, ClipPropertyChangeRounded) {
EXPECT_TRUE(TrackedRasterInvalidations(invalidator).IsEmpty());
// Change both clip0 and clip2.
LOG(ERROR) << "22222222222222222222222222222222222222222222";
CHUNKS(new_chunks, Chunk(0), Chunk(1), Chunk(2));
FloatRoundedRect new_clip_rect(FloatRect(-2000, -2000, 4000, 4000), radii);
LOG(ERROR) << "new_clip_rect: " << new_clip_rect.ToString();
clip0->Update(clip0->Parent(), clip0->LocalTransformSpace(), new_clip_rect);
clip2->Update(clip2->Parent(), clip2->LocalTransformSpace(), new_clip_rect);
new_chunks_array[0].properties = chunks[0]->properties;
......@@ -314,7 +312,6 @@ TEST_F(CompositedLayerRasterInvalidatorTest, ClipPropertyChangeRounded) {
PaintInvalidationReason::kPaintProperty);
invalidator.SetTracksRasterInvalidations(false);
clip2->ClearChangedToRoot();
LOG(ERROR) << "333333333333333333333333333333333333333333333";
// Change chunk1's properties to use a different property tree state.
CHUNKS(new_chunks1, Chunk(0), Chunk(1), Chunk(2));
......
......@@ -8,6 +8,7 @@
#include "cc/paint/paint_op_buffer.h"
#include "cc/paint/render_surface_filters.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/compositing/ChunkToLayerMapper.h"
#include "platform/graphics/paint/DisplayItemList.h"
#include "platform/graphics/paint/DrawingDisplayItem.h"
#include "platform/graphics/paint/GeometryMapper.h"
......@@ -424,6 +425,8 @@ void ConversionContext::Convert(const Vector<const PaintChunk*>& paint_chunks,
translated = true;
};
ChunkToLayerMapper mapper(layer_state_, layer_offset_);
for (auto chunk_it = paint_chunks.begin(); chunk_it != paint_chunks.end();
chunk_it++) {
const PaintChunk& chunk = **chunk_it;
......@@ -450,6 +453,8 @@ void ConversionContext::Convert(const Vector<const PaintChunk*>& paint_chunks,
properties_adjusted = true;
};
mapper.SwitchToChunk(chunk);
for (const auto& item : display_items.ItemsInPaintChunk(chunk)) {
DCHECK(item.IsDrawing());
auto record =
......@@ -469,8 +474,7 @@ void ConversionContext::Convert(const Vector<const PaintChunk*>& paint_chunks,
cc_list_.StartPaint();
if (record && record->size() != 0)
cc_list_.push<cc::DrawRecordOp>(std::move(record));
cc_list_.EndPaintOfUnpaired(PaintChunksToCcLayer::MapRectFromChunkToLayer(
item.VisualRect(), chunk, layer_state_, layer_offset_));
cc_list_.EndPaintOfUnpaired(mapper.MapVisualRect(item.VisualRect()));
}
if (transformed)
AppendRestore(1);
......@@ -528,23 +532,4 @@ scoped_refptr<cc::DisplayItemList> PaintChunksToCcLayer::Convert(
return cc_list;
}
IntRect PaintChunksToCcLayer::MapRectFromChunkToLayer(
const FloatRect& r,
const PaintChunk& chunk,
const PropertyTreeState& layer_state,
const gfx::Vector2dF& layer_offset) {
FloatClipRect rect(r);
GeometryMapper::LocalToAncestorVisualRect(
chunk.properties.property_tree_state.GetPropertyTreeState(), layer_state,
rect);
if (rect.Rect().IsEmpty())
return IntRect();
// Now rect is in the space of the containing transform node of layer,
// so need to subtract off the layer offset.
rect.Rect().Move(-layer_offset.x(), -layer_offset.y());
rect.Rect().Inflate(chunk.outset_for_raster_effects);
return EnclosingIntRect(rect.Rect());
}
} // namespace blink
......@@ -69,11 +69,6 @@ class PLATFORM_EXPORT PaintChunksToCcLayer {
const DisplayItemList&,
cc::DisplayItemList::UsageHint,
RasterUnderInvalidationCheckingParams* = nullptr);
static IntRect MapRectFromChunkToLayer(const FloatRect&,
const PaintChunk&,
const PropertyTreeState& layer_state,
const gfx::Vector2dF& layer_offset);
};
} // namespace blink
......
......@@ -250,6 +250,11 @@ FloatClipRect GeometryMapper::LocalToAncestorClipRect(
local_state.Clip(), ancestor_state.Clip(), ancestor_state.Transform(),
clip_behavior, success);
DCHECK(success);
// Many effects (e.g. filters, clip-paths) can make a clip rect not tight.
if (local_state.Effect() != ancestor_state.Effect())
result.ClearIsTight();
return result;
}
......
......@@ -649,6 +649,7 @@ TEST_P(GeometryMapperTest, FilterWithClipsAndTransforms) {
expected_visual_rect = FloatClipRect(output);
expected_visual_rect.ClearIsTight();
expected_clip = FloatClipRect(FloatRect(50, 60, 90, 90));
expected_clip.ClearIsTight();
CHECK_MAPPINGS();
}
......
......@@ -6,6 +6,7 @@
#define FakeDisplayItemClient_h
#include "platform/geometry/LayoutRect.h"
#include "platform/graphics/paint/DisplayItemClient.h"
#include "platform/wtf/Forward.h"
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