diff options
author | danakj <danakj@chromium.org> | 2015-04-25 10:53:17 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-25 17:53:12 +0000 |
commit | 93f5730af51af283713ad19dd5e08c3b3ea34eaa (patch) | |
tree | 38e1eb1974410d8503476bb9d0d25218f3ec17c9 | |
parent | ba5b67e27dc64c3c6793be8f9c991066c956c33f (diff) | |
download | chromium_src-93f5730af51af283713ad19dd5e08c3b3ea34eaa.zip chromium_src-93f5730af51af283713ad19dd5e08c3b3ea34eaa.tar.gz chromium_src-93f5730af51af283713ad19dd5e08c3b3ea34eaa.tar.bz2 |
ui: Cache the output of View::OnPaint when the View isn't invalid.
Store the output of View::OnPaint as a PaintCache object, and when the
view has not been invalidated, use that cache output instead of doing
work to build a recording.
Performance data as follows when the loading spinner is going:
Before impl-side (TOT): 0.13ms / paint
With impl-side (no slimming paint): 0.22ms / paint
With impl-side and slimming paint (this patch): 0.17ms / paint
So this gets us some of the way there. I need to investigate why it's
not doing more.
R=piman@chromium.org, sky
BUG=466426
Committed: https://crrev.com/7f686cdcff81d6779b962e98e529a7360be2809c
Cr-Commit-Position: refs/heads/master@{#326592}
Review URL: https://codereview.chromium.org/1101783002
Cr-Commit-Position: refs/heads/master@{#326976}
-rw-r--r-- | cc/resources/drawing_display_item.cc | 4 | ||||
-rw-r--r-- | cc/resources/drawing_display_item.h | 2 | ||||
-rw-r--r-- | ui/compositor/BUILD.gn | 2 | ||||
-rw-r--r-- | ui/compositor/compositor.gyp | 2 | ||||
-rw-r--r-- | ui/compositor/layer.cc | 9 | ||||
-rw-r--r-- | ui/compositor/paint_cache.cc | 31 | ||||
-rw-r--r-- | ui/compositor/paint_cache.h | 45 | ||||
-rw-r--r-- | ui/compositor/paint_context.h | 16 | ||||
-rw-r--r-- | ui/compositor/paint_recorder.cc | 21 | ||||
-rw-r--r-- | ui/compositor/paint_recorder.h | 5 | ||||
-rw-r--r-- | ui/views/view.cc | 13 | ||||
-rw-r--r-- | ui/views/view.h | 4 |
12 files changed, 134 insertions, 20 deletions
diff --git a/cc/resources/drawing_display_item.cc b/cc/resources/drawing_display_item.cc index 91ab3fb..a37729f 100644 --- a/cc/resources/drawing_display_item.cc +++ b/cc/resources/drawing_display_item.cc @@ -62,4 +62,8 @@ void DrawingDisplayItem::AsValueInto( array->EndDictionary(); } +scoped_ptr<DrawingDisplayItem> DrawingDisplayItem::Clone() { + return Create(picture_); +} + } // namespace cc diff --git a/cc/resources/drawing_display_item.h b/cc/resources/drawing_display_item.h index a3eef77..da302d8 100644 --- a/cc/resources/drawing_display_item.h +++ b/cc/resources/drawing_display_item.h @@ -33,6 +33,8 @@ class CC_EXPORT DrawingDisplayItem : public DisplayItem { size_t PictureMemoryUsage() const override; void AsValueInto(base::trace_event::TracedValue* array) const override; + scoped_ptr<DrawingDisplayItem> Clone(); + protected: explicit DrawingDisplayItem(skia::RefPtr<SkPicture> picture); diff --git a/ui/compositor/BUILD.gn b/ui/compositor/BUILD.gn index 590cb73..a0944fe 100644 --- a/ui/compositor/BUILD.gn +++ b/ui/compositor/BUILD.gn @@ -47,6 +47,8 @@ component("compositor") { "layer_tree_owner.cc", "layer_tree_owner.h", "layer_type.h", + "paint_cache.cc", + "paint_cache.h", "paint_context.cc", "paint_context.h", "paint_recorder.cc", diff --git a/ui/compositor/compositor.gyp b/ui/compositor/compositor.gyp index 11d6723..92f5ef4 100644 --- a/ui/compositor/compositor.gyp +++ b/ui/compositor/compositor.gyp @@ -65,6 +65,8 @@ 'layer_tree_owner.cc', 'layer_tree_owner.h', 'layer_type.h', + 'paint_cache.cc', + 'paint_cache.h', 'paint_context.cc', 'paint_context.h', 'paint_recorder.cc', diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc index 914e000..2b6f921 100644 --- a/ui/compositor/layer.cc +++ b/ui/compositor/layer.cc @@ -750,14 +750,13 @@ void Layer::PaintContentsToDisplayList( const gfx::Rect& clip, ContentLayerClient::PaintingControlSetting painting_control) { TRACE_EVENT1("ui", "Layer::PaintContentsToDisplayList", "name", name_); + gfx::Rect local_bounds(bounds().size()); + gfx::Rect invalidation( + gfx::IntersectRects(damaged_region_.bounds(), local_bounds)); + DCHECK(clip.Contains(invalidation)); ClearDamagedRects(); if (!delegate_) return; - // TODO(danakj): Save the invalidation on the layer and pass that down - // instead of the |clip| here. That will break everything until View - // early-outs emit cached display items instead of nothing. - gfx::Rect invalidation = clip; - DCHECK(clip.Contains(invalidation)); delegate_->OnPaintLayer( PaintContext(display_list, device_scale_factor_, clip, invalidation)); } diff --git a/ui/compositor/paint_cache.cc b/ui/compositor/paint_cache.cc new file mode 100644 index 0000000..ef0e5a7 --- /dev/null +++ b/ui/compositor/paint_cache.cc @@ -0,0 +1,31 @@ +// Copyright 2015 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 "ui/compositor/paint_cache.h" + +#include "cc/resources/display_item_list.h" +#include "cc/resources/drawing_display_item.h" +#include "ui/compositor/paint_context.h" + +namespace ui { + +PaintCache::PaintCache() { +} + +PaintCache::~PaintCache() { +} + +bool PaintCache::UseCache(const PaintContext& context) { + if (!display_item_) + return false; + DCHECK(context.list_); + context.list_->AppendItem(display_item_->Clone()); + return true; +} + +void PaintCache::SetCache(scoped_ptr<cc::DrawingDisplayItem> item) { + display_item_ = item.Pass(); +} + +} // namespace ui diff --git a/ui/compositor/paint_cache.h b/ui/compositor/paint_cache.h new file mode 100644 index 0000000..599a8ac --- /dev/null +++ b/ui/compositor/paint_cache.h @@ -0,0 +1,45 @@ +// Copyright 2015 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 UI_COMPOSITOR_PAINT_CACHE_H_ +#define UI_COMPOSITOR_PAINT_CACHE_H_ + +#include "base/memory/scoped_ptr.h" +#include "ui/compositor/compositor_export.h" + +namespace cc { +class DrawingDisplayItem; +} + +namespace ui { +class PaintContext; +class PaintRecorder; + +// A class that holds the output of a PaintRecorder to be reused when the +// object that created the PaintRecorder has not been changed/invalidated. +class COMPOSITOR_EXPORT PaintCache { + public: + PaintCache(); + ~PaintCache(); + + // Returns true if the PaintCache was able to insert a previously-saved + // painting output into the PaintContext. If it returns false, the caller + // needs to do the work of painting, which can be stored into the PaintCache + // to be used next time. + bool UseCache(const PaintContext& context); + + private: + // Only PaintRecorder can modify these. + friend PaintRecorder; + + void SetCache(scoped_ptr<cc::DrawingDisplayItem> item); + + scoped_ptr<cc::DrawingDisplayItem> display_item_; + + DISALLOW_COPY_AND_ASSIGN(PaintCache); +}; + +} // namespace ui + +#endif // UI_COMPOSITOR_PAINT_CACHE_H_ diff --git a/ui/compositor/paint_context.h b/ui/compositor/paint_context.h index d63f11f..a754ee4 100644 --- a/ui/compositor/paint_context.h +++ b/ui/compositor/paint_context.h @@ -56,15 +56,20 @@ class COMPOSITOR_EXPORT PaintContext { return PaintContext(canvas_); } - // When true, IsRectInvalidated() can be called, otherwise its result would be + // When true, IsRectInvalid() can be called, otherwise its result would be // invalid. - bool CanCheckInvalidated() const { return !invalidation_.IsEmpty(); } + bool CanCheckInvalid() const { return !invalidation_.IsEmpty(); } + + // When true, if a thing is not invalidated it does not need to paint itself. + // When false, everything should provide an output when painting regardless of + // being invalidated in order to remain visible. + bool ShouldEarlyOutOfPaintingWhenValid() const { return !!canvas_; } // When true, the |bounds| touches an invalidated area, so should be // re-painted. When false, re-painting can be skipped. Bounds should be in // the local space with offsets up to the painting root in the PaintContext. - bool IsRectInvalidated(const gfx::Rect& bounds) const { - DCHECK(CanCheckInvalidated()); + bool IsRectInvalid(const gfx::Rect& bounds) const { + DCHECK(CanCheckInvalid()); return invalidation_.Intersects(bounds + offset_); } @@ -86,6 +91,9 @@ class COMPOSITOR_EXPORT PaintContext { friend class ClipTransformRecorder; friend class CompositingRecorder; friend class PaintRecorder; + // The Cache class also needs to access the DisplayItemList to append its + // cache contents. + friend class PaintCache; PaintContext& operator=(const PaintContext& other) = delete; diff --git a/ui/compositor/paint_recorder.cc b/ui/compositor/paint_recorder.cc index 15aa0fb..80fb616 100644 --- a/ui/compositor/paint_recorder.cc +++ b/ui/compositor/paint_recorder.cc @@ -7,14 +7,15 @@ #include "cc/resources/display_item_list.h" #include "cc/resources/drawing_display_item.h" #include "third_party/skia/include/core/SkPictureRecorder.h" +#include "ui/compositor/paint_cache.h" #include "ui/compositor/paint_context.h" #include "ui/gfx/canvas.h" #include "ui/gfx/skia_util.h" namespace ui { -PaintRecorder::PaintRecorder(const PaintContext& context) - : context_(context), canvas_(context.canvas_) { +PaintRecorder::PaintRecorder(const PaintContext& context, PaintCache* cache) + : context_(context), canvas_(context.canvas_), cache_(cache) { #if DCHECK_IS_ON() DCHECK(!context.inside_paint_recorder_); context.inside_paint_recorder_ = true; @@ -34,15 +35,23 @@ PaintRecorder::PaintRecorder(const PaintContext& context) } } +PaintRecorder::PaintRecorder(const PaintContext& context) + : PaintRecorder(context, nullptr) { +} + PaintRecorder::~PaintRecorder() { #if DCHECK_IS_ON() context_.inside_paint_recorder_ = false; #endif - if (context_.list_) { - context_.list_->AppendItem(cc::DrawingDisplayItem::Create( - skia::AdoptRef(context_.recorder_->endRecordingAsPicture()))); - } + if (!context_.list_) + return; + + scoped_ptr<cc::DrawingDisplayItem> item = cc::DrawingDisplayItem::Create( + skia::AdoptRef(context_.recorder_->endRecordingAsPicture())); + if (cache_) + cache_->SetCache(item->Clone()); + context_.list_->AppendItem(item.Pass()); } } // namespace ui diff --git a/ui/compositor/paint_recorder.h b/ui/compositor/paint_recorder.h index 7c3df82..9cac7a8 100644 --- a/ui/compositor/paint_recorder.h +++ b/ui/compositor/paint_recorder.h @@ -22,6 +22,7 @@ class SkCanvas; class SkPictureRecorder; namespace ui { +class PaintCache; class PaintContext; // A class to hide the complexity behind setting up a recording into a @@ -30,6 +31,9 @@ class PaintContext; // recording is complete and can be cached. class COMPOSITOR_EXPORT PaintRecorder { public: + // The |cache| is owned by the caller and must be kept alive while + // PaintRecorder is in use. + PaintRecorder(const PaintContext& context, PaintCache* cache); explicit PaintRecorder(const PaintContext& context); ~PaintRecorder(); @@ -40,6 +44,7 @@ class COMPOSITOR_EXPORT PaintRecorder { const PaintContext& context_; gfx::Canvas* canvas_; scoped_ptr<gfx::Canvas> owned_canvas_; + PaintCache* cache_; DISALLOW_COPY_AND_ASSIGN(PaintRecorder); }; diff --git a/ui/views/view.cc b/ui/views/view.cc index 78659ca..c79cc6c 100644 --- a/ui/views/view.cc +++ b/ui/views/view.cc @@ -748,7 +748,8 @@ void View::Paint(const ui::PaintContext& parent_context) { ui::PaintContext context = parent_context.CloneWithPaintOffset(offset_to_parent); - if (context.CanCheckInvalidated()) { + bool is_invalidated = true; + if (context.CanCheckInvalid()) { #if DCHECK_IS_ON() gfx::Vector2d offset; context.Visited(this); @@ -770,10 +771,12 @@ void View::Paint(const ui::PaintContext& parent_context) { // If the View wasn't invalidated, don't waste time painting it, the output // would be culled. - if (!context.IsRectInvalidated(GetLocalBounds())) - return; + is_invalidated = context.IsRectInvalid(GetLocalBounds()); } + if (!is_invalidated && context.ShouldEarlyOutOfPaintingWhenValid()) + return; + TRACE_EVENT1("views", "View::Paint", "class", GetClassName()); // If the view is backed by a layer, it should paint with itself as the origin @@ -803,8 +806,8 @@ void View::Paint(const ui::PaintContext& parent_context) { clip_transform_recorder->Transform(transform_from_parent); } - { - ui::PaintRecorder recorder(context); + if (is_invalidated || !paint_cache_.UseCache(context)) { + ui::PaintRecorder recorder(context, &paint_cache_); gfx::Canvas* canvas = recorder.canvas(); gfx::ScopedCanvas scoped_canvas(canvas); diff --git a/ui/views/view.h b/ui/views/view.h index 00813e0..1a56db5 100644 --- a/ui/views/view.h +++ b/ui/views/view.h @@ -24,6 +24,7 @@ #include "ui/base/ui_base_types.h" #include "ui/compositor/layer_delegate.h" #include "ui/compositor/layer_owner.h" +#include "ui/compositor/paint_cache.h" #include "ui/events/event.h" #include "ui/events/event_target.h" #include "ui/gfx/geometry/insets.h" @@ -1509,6 +1510,9 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Border. scoped_ptr<Border> border_; + // Cached output of painting to be reused in future frames until invalidated. + ui::PaintCache paint_cache_; + // RTL painting -------------------------------------------------------------- // Indicates whether or not the gfx::Canvas object passed to View::Paint() |