From ba117366efac8db8635faf1669bca7055f0915cf Mon Sep 17 00:00:00 2001 From: liyuqian Date: Tue, 18 Dec 2018 09:54:52 -0800 Subject: [PATCH] Compute cull_rect and optimize in Layer::Preroll (#6923) This PR replaces the unused `PrerollContext::child_paint_bounds` with `PrerollContext::cull_rect` so we can prune unnecessary preroll tasks (especially cache) based on clips. This PR fixes https://github.com/flutter/flutter/issues/24712 Performance test has been added (https://github.com/flutter/flutter/pull/25381) to make sure that we won't regress again in the future. Note that the cull_rect here is very similar to those removed in https://github.com/flutter/engine/pull/6352 . We can't compute cull rects in SceneBuilder because of retained layers. But we can still compute and use them to optimize performance in Preroll. --- flow/layers/clip_path_layer.cc | 13 +++++++++---- flow/layers/clip_rect_layer.cc | 12 ++++++++---- flow/layers/clip_rrect_layer.cc | 13 +++++++++---- flow/layers/layer.h | 4 +++- flow/layers/layer_tree.cc | 4 ++-- flow/layers/opacity_layer.cc | 3 ++- flow/layers/transform_layer.cc | 10 ++++++++++ lib/ui/geometry.dart | 2 +- 8 files changed, 44 insertions(+), 17 deletions(-) diff --git a/flow/layers/clip_path_layer.cc b/flow/layers/clip_path_layer.cc index 17fd4db73..c6259de22 100644 --- a/flow/layers/clip_path_layer.cc +++ b/flow/layers/clip_path_layer.cc @@ -18,12 +18,17 @@ ClipPathLayer::ClipPathLayer(Clip clip_behavior) ClipPathLayer::~ClipPathLayer() = default; void ClipPathLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { - SkRect child_paint_bounds = SkRect::MakeEmpty(); - PrerollChildren(context, matrix, &child_paint_bounds); + SkRect previous_cull_rect = context->cull_rect; + SkRect clip_path_bounds = clip_path_.getBounds(); + if (context->cull_rect.intersect(clip_path_bounds)) { + SkRect child_paint_bounds = SkRect::MakeEmpty(); + PrerollChildren(context, matrix, &child_paint_bounds); - if (child_paint_bounds.intersect(clip_path_.getBounds())) { - set_paint_bounds(child_paint_bounds); + if (child_paint_bounds.intersect(clip_path_bounds)) { + set_paint_bounds(child_paint_bounds); + } } + context->cull_rect = previous_cull_rect; } #if defined(OS_FUCHSIA) diff --git a/flow/layers/clip_rect_layer.cc b/flow/layers/clip_rect_layer.cc index cb4ed56da..3f39daca2 100644 --- a/flow/layers/clip_rect_layer.cc +++ b/flow/layers/clip_rect_layer.cc @@ -12,12 +12,16 @@ ClipRectLayer::ClipRectLayer(Clip clip_behavior) ClipRectLayer::~ClipRectLayer() = default; void ClipRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { - SkRect child_paint_bounds = SkRect::MakeEmpty(); - PrerollChildren(context, matrix, &child_paint_bounds); + SkRect previous_cull_rect = context->cull_rect; + if (context->cull_rect.intersect(clip_rect_)) { + SkRect child_paint_bounds = SkRect::MakeEmpty(); + PrerollChildren(context, matrix, &child_paint_bounds); - if (child_paint_bounds.intersect(clip_rect_)) { - set_paint_bounds(child_paint_bounds); + if (child_paint_bounds.intersect(clip_rect_)) { + set_paint_bounds(child_paint_bounds); + } } + context->cull_rect = previous_cull_rect; } #if defined(OS_FUCHSIA) diff --git a/flow/layers/clip_rrect_layer.cc b/flow/layers/clip_rrect_layer.cc index 9016c7f5b..896ec1ae2 100644 --- a/flow/layers/clip_rrect_layer.cc +++ b/flow/layers/clip_rrect_layer.cc @@ -12,12 +12,17 @@ ClipRRectLayer::ClipRRectLayer(Clip clip_behavior) ClipRRectLayer::~ClipRRectLayer() = default; void ClipRRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { - SkRect child_paint_bounds = SkRect::MakeEmpty(); - PrerollChildren(context, matrix, &child_paint_bounds); + SkRect previous_cull_rect = context->cull_rect; + SkRect clip_rrect_bounds = clip_rrect_.getBounds(); + if (context->cull_rect.intersect(clip_rrect_bounds)) { + SkRect child_paint_bounds = SkRect::MakeEmpty(); + PrerollChildren(context, matrix, &child_paint_bounds); - if (child_paint_bounds.intersect(clip_rrect_.getBounds())) { - set_paint_bounds(child_paint_bounds); + if (child_paint_bounds.intersect(clip_rrect_bounds)) { + set_paint_bounds(child_paint_bounds); + } } + context->cull_rect = previous_cull_rect; } #if defined(OS_FUCHSIA) diff --git a/flow/layers/layer.h b/flow/layers/layer.h index 0e53e9281..e846d353e 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -37,6 +37,8 @@ namespace flow { +static constexpr SkRect kGiantRect = SkRect::MakeLTRB(-1E9F, -1E9F, 1E9F, 1E9F); + // This should be an exact copy of the Clip enum in painting.dart. enum Clip { none, hardEdge, antiAlias, antiAliasWithSaveLayer }; @@ -47,7 +49,7 @@ struct PrerollContext { GrContext* gr_context; ExternalViewEmbedder* view_embedder; SkColorSpace* dst_color_space; - SkRect child_paint_bounds; + SkRect cull_rect; // The following allows us to paint in the end of subtree preroll const Stopwatch& frame_time; diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index 37b9b0a23..31c448f04 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -31,7 +31,7 @@ void LayerTree::Preroll(CompositorContext::ScopedFrame& frame, frame.gr_context(), frame.view_embedder(), color_space, - SkRect::MakeEmpty(), + kGiantRect, frame.context().frame_time(), frame.context().engine_time(), frame.context().texture_registry(), @@ -113,7 +113,7 @@ sk_sp LayerTree::Flatten(const SkRect& bounds) { nullptr, // gr_context (used for the raster cache) nullptr, // external view embedder nullptr, // SkColorSpace* dst_color_space - SkRect::MakeEmpty(), // SkRect child_paint_bounds + kGiantRect, // SkRect cull_rect unused_stopwatch, // frame time (dont care) unused_stopwatch, // engine time (dont care) unused_texture_registry, // texture registry (not supported) diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 1a2a3e3e1..c774ab9fb 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -15,7 +15,8 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { child_matrix.postTranslate(offset_.fX, offset_.fY); ContainerLayer::Preroll(context, child_matrix); set_paint_bounds(paint_bounds().makeOffset(offset_.fX, offset_.fY)); - if (context->raster_cache && layers().size() == 1) { + if (context->raster_cache && layers().size() == 1 && + SkRect::Intersects(context->cull_rect, paint_bounds())) { Layer* child = layers()[0].get(); SkMatrix ctm = child_matrix; #ifndef SUPPORT_FRACTIONAL_TRANSLATION diff --git a/flow/layers/transform_layer.cc b/flow/layers/transform_layer.cc index 410204bed..8abb550b9 100644 --- a/flow/layers/transform_layer.cc +++ b/flow/layers/transform_layer.cc @@ -14,11 +14,21 @@ void TransformLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkMatrix child_matrix; child_matrix.setConcat(matrix, transform_); + SkRect previous_cull_rect = context->cull_rect; + SkMatrix inverse_transform_; + if (transform_.invert(&inverse_transform_)) { + inverse_transform_.mapRect(&context->cull_rect); + } else { + context->cull_rect = kGiantRect; + } + SkRect child_paint_bounds = SkRect::MakeEmpty(); PrerollChildren(context, child_matrix, &child_paint_bounds); transform_.mapRect(&child_paint_bounds); set_paint_bounds(child_paint_bounds); + + context->cull_rect = previous_cull_rect; } #if defined(OS_FUCHSIA) diff --git a/lib/ui/geometry.dart b/lib/ui/geometry.dart index 4c3770865..56b6e5575 100644 --- a/lib/ui/geometry.dart +++ b/lib/ui/geometry.dart @@ -693,7 +693,7 @@ class Rect { /// A rectangle with left, top, right, and bottom edges all at zero. static final Rect zero = new Rect._(); - static const double _giantScalar = 1.0E+9; // matches kGiantRect from default_layer_builder.cc + static const double _giantScalar = 1.0E+9; // matches kGiantRect from layer.h /// A rectangle that covers the entire coordinate space. ///