Commit fe694a65 authored by Francois Doray's avatar Francois Doray Committed by Commit Bot

Add aura::WindowOcclusionTracker [reland].

This Cl shouldn't have been reverted. See https://crbug.com/784879#c5

aura::WindowOcclusionTracker notifies interested windows
when their occlusion state change.

This will be used to release resources and throttle execution
of occluded tabs.

Time to recompute occlusion on a Samsung Chromebook Pro:
 100 partially occluded windows*: 108us
  50 partially occluded windows*: 50us
  * window 0 at position (x=0;y=0), window 1 at position (x=1;y=1),
    window 2 at position (x=2;y=2), etc.
    Benchmark code:
    https://chromium-review.googlesource.com/c/chromium/src/+/718979

TBR=sadrul@chromium.org

Bug: 738387, 784879
Change-Id: Ie5a8559378223bb78693cd0cd40e4468889e9f79
Reviewed-on: https://chromium-review.googlesource.com/773118Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Commit-Queue: François Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/master@{#517083}
parent c1d60835
......@@ -71,6 +71,7 @@ component("aura") {
"window_delegate.h",
"window_event_dispatcher.h",
"window_observer.h",
"window_occlusion_tracker.h",
"window_port.h",
"window_targeter.h",
"window_tracker.h",
......@@ -128,6 +129,7 @@ component("aura") {
"window.cc",
"window_event_dispatcher.cc",
"window_observer.cc",
"window_occlusion_tracker.cc",
"window_port.cc",
"window_port_for_shutdown.cc",
"window_port_for_shutdown.h",
......@@ -349,6 +351,7 @@ test("aura_unittests") {
"mus/window_tree_host_mus_unittest.cc",
"test/run_all_unittests.cc",
"window_event_dispatcher_unittest.cc",
"window_occlusion_tracker_unittest.cc",
"window_targeter_unittest.cc",
"window_tree_host_unittest.cc",
"window_unittest.cc",
......
......@@ -35,6 +35,7 @@
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_observer.h"
#include "ui/aura/window_occlusion_tracker.h"
#include "ui/aura/window_port.h"
#include "ui/aura/window_tracker.h"
#include "ui/aura/window_tree_host.h"
......@@ -63,6 +64,7 @@ Window::Window(WindowDelegate* delegate,
delegate_(delegate),
parent_(nullptr),
visible_(false),
occlusion_state_(OcclusionState::UNKNOWN),
id_(kInitialId),
transparent_(false),
event_targeting_policy_(
......@@ -75,6 +77,8 @@ Window::Window(WindowDelegate* delegate,
}
Window::~Window() {
WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking;
// See comment in header as to why this is done.
std::unique_ptr<WindowPort> port = std::move(port_owner_);
......@@ -139,6 +143,8 @@ Window::~Window() {
}
void Window::Init(ui::LayerType layer_type) {
WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking;
if (!port_owner_) {
port_owner_ = Env::GetInstance()->CreateWindowPort(this);
port_ = port_owner_.get();
......@@ -262,6 +268,7 @@ gfx::Rect Window::GetBoundsInScreen() const {
}
void Window::SetTransform(const gfx::Transform& transform) {
WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking;
for (WindowObserver& observer : observers_)
observer.OnWindowTargetTransformChanging(this, transform);
gfx::Transform old_transform = layer()->transform();
......@@ -347,6 +354,8 @@ void Window::StackChildBelow(Window* child, Window* target) {
}
void Window::AddChild(Window* child) {
WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking;
DCHECK(layer()) << "Parent has not been Init()ed yet.";
DCHECK(child->layer()) << "Child has not been Init()ed yt.";
WindowObserver::HierarchyChangeParams params;
......@@ -385,6 +394,8 @@ void Window::AddChild(Window* child) {
}
void Window::RemoveChild(Window* child) {
WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking;
WindowObserver::HierarchyChangeParams params;
params.target = child;
params.new_parent = NULL;
......@@ -721,6 +732,8 @@ void Window::SetVisible(bool visible) {
if (visible == layer()->GetTargetVisibility())
return; // No change.
WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking;
for (WindowObserver& observer : observers_)
observer.OnWindowVisibilityChanging(this, visible);
......@@ -742,6 +755,16 @@ void Window::SetVisible(bool visible) {
NotifyWindowVisibilityChanged(this, visible);
}
void Window::SetOccluded(bool occluded) {
OcclusionState occlusion_state =
occluded ? OcclusionState::OCCLUDED : OcclusionState::NOT_OCCLUDED;
if (occlusion_state != occlusion_state_) {
occlusion_state_ = occlusion_state;
if (delegate_)
delegate_->OnWindowOcclusionChanged(occluded);
}
}
void Window::SchedulePaint() {
SchedulePaintInRect(gfx::Rect(0, 0, bounds().width(), bounds().height()));
}
......@@ -849,6 +872,8 @@ void Window::StackChildRelativeTo(Window* child,
DCHECK_EQ(this, child->parent());
DCHECK_EQ(this, target->parent());
WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking;
client::WindowStackingClient* stacking_client =
client::GetWindowStackingClient();
if (stacking_client &&
......@@ -1061,6 +1086,8 @@ void Window::OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) {
void Window::OnLayerBoundsChanged(const gfx::Rect& old_bounds,
ui::PropertyChangeReason reason) {
WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking;
bounds_ = layer()->bounds();
// Use |bounds_| as that is the bounds before any animations, which is what
......@@ -1076,11 +1103,13 @@ void Window::OnLayerBoundsChanged(const gfx::Rect& old_bounds,
}
void Window::OnLayerOpacityChanged(ui::PropertyChangeReason reason) {
WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking;
for (WindowObserver& observer : observers_)
observer.OnWindowOpacityChanged(this, reason);
}
void Window::OnLayerTransformed(ui::PropertyChangeReason reason) {
WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking;
for (WindowObserver& observer : observers_)
observer.OnWindowTransformed(this, reason);
}
......
......@@ -84,6 +84,17 @@ class AURA_EXPORT Window : public ui::LayerDelegate,
STACK_ABOVE,
STACK_BELOW
};
enum class OcclusionState {
// The window's occlusion state isn't tracked
// (WindowOcclusionTracker::Track) or hasn't been computed yet.
UNKNOWN,
// The window is occluded, i.e. one of these conditions is true:
// - The window is hidden (Window::IsVisible() is true).
// - The bounds of the window are completely covered by opaque windows.
OCCLUDED,
// The window is not occluded.
NOT_OCCLUDED,
};
typedef std::vector<Window*> Windows;
......@@ -156,6 +167,11 @@ class AURA_EXPORT Window : public ui::LayerDelegate,
// account the visibility of the layer and ancestors, where as this tracks
// whether Show() without a Hide() has been invoked.
bool TargetVisibility() const { return visible_; }
// Returns the occlusion state of this window. Will be UNKNOWN if the
// occlusion state of this window isn't tracked
// (WindowOcclusionTracker::Track). Will be stale if called within the scope
// of a WindowOcclusionTracker::ScopedPauseOcclusionTracking.
OcclusionState occlusion_state() const { return occlusion_state_; }
// Returns the window's bounds in root window's coordinates.
gfx::Rect GetBoundsInRootWindow() const;
......@@ -369,6 +385,7 @@ class AURA_EXPORT Window : public ui::LayerDelegate,
friend class HitTestDataProviderAura;
friend class LayoutManager;
friend class PropertyConverter;
friend class WindowOcclusionTracker;
friend class WindowPort;
friend class WindowPortForShutdown;
friend class WindowTargeter;
......@@ -388,6 +405,9 @@ class AURA_EXPORT Window : public ui::LayerDelegate,
// specific changes. Called from Show()/Hide().
void SetVisible(bool visible);
// Updates the occlusion state of the window.
void SetOccluded(bool occluded);
// Schedules a paint for the Window's entire bounds.
void SchedulePaint();
......@@ -514,6 +534,9 @@ class AURA_EXPORT Window : public ui::LayerDelegate,
// the window is hidden (e.g. to animate its disappearance).
bool visible_;
// Occlusion state of the window.
OcclusionState occlusion_state_;
int id_;
// Only set when it is embedding another client inside.
......
......@@ -89,6 +89,11 @@ class AURA_EXPORT WindowDelegate : public ui::EventHandler {
// Window::TargetVisibility() for details.
virtual void OnWindowTargetVisibilityChanged(bool visible) = 0;
// Called when the occlusion state of the Window changes while tracked (see
// WindowOcclusionTracker::Track). |is_occluded| indicates whether the Window
// is occluded. Impls must not change any aura::Window.
virtual void OnWindowOcclusionChanged(bool is_occluded) {}
// Called from Window::HitTest to check if the window has a custom hit test
// mask. It works similar to the views counterparts. That is, if the function
// returns true, GetHitTestMask below will be called to get the mask.
......
This diff is collapsed.
// Copyright 2017 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_AURA_WINDOW_OCCLUSION_TRACKER_H_
#define UI_AURA_WINDOW_OCCLUSION_TRACKER_H_
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/macros.h"
#include "ui/aura/aura_export.h"
#include "ui/aura/window_observer.h"
#include "ui/compositor/layer_animation_observer.h"
struct SkIRect;
class SkRegion;
namespace gfx {
class Transform;
}
namespace aura {
class Window;
// Notifies tracked Windows when their occlusion state change.
//
// To start tracking the occlusion state of a Window, call
// WindowOcclusionTracker::Track().
//
// A Window is occluded if its bounds and transform are not animated and one of
// these conditions is true:
// - The Window is hidden (Window::IsVisible() is true).
// - The bounds of the Window are completely covered by opaque and axis-aligned
// Windows whose bounds and transform are not animated.
// Note that an occluded window may be drawn on the screen by window switching
// features such as "Alt-Tab" or "Overview".
class AURA_EXPORT WindowOcclusionTracker : public ui::LayerAnimationObserver,
public WindowObserver {
public:
// Prevents window occlusion state computations within its scope. If an event
// that could cause window occlusion states to change occurs within the scope
// of a ScopedPauseOcclusionTracking, window occlusion state computations are
// delayed until all ScopedPauseOcclusionTracking objects have been destroyed.
class AURA_EXPORT ScopedPauseOcclusionTracking {
public:
ScopedPauseOcclusionTracking();
~ScopedPauseOcclusionTracking();
private:
DISALLOW_COPY_AND_ASSIGN(ScopedPauseOcclusionTracking);
};
// Start tracking the occlusion state of |window|.
static void Track(Window* window);
private:
WindowOcclusionTracker();
~WindowOcclusionTracker() override;
// Recomputes the occlusion state of tracked windows under roots marked as
// dirty in |root_windows_| if there are no active
// ScopedPauseOcclusionTracking instance.
void MaybeRecomputeOcclusion();
// Recomputes the occlusion state of |window| and its descendants.
// |parent_transform_relative_to_root| is the transform of |window->parent()|
// relative to the root window. |clipped_bounds| is an optional mask for the
// bounds of |window| and its descendants. |occluded_region| is a region
// covered by windows which are on top of |window|.
void RecomputeOcclusionImpl(
Window* window,
const gfx::Transform& parent_transform_relative_to_root,
const SkIRect* clipped_bounds,
SkRegion* occluded_region);
// Removes windows whose bounds and transform are not animated from
// |animated_windows_|. Marks the root of those windows as dirty.
void CleanupAnimatedWindows();
// If the bounds or transform of |window| are animated and |window| is not in
// |animated_windows_|, adds |window| to |animated_windows_| and returns true.
bool MaybeObserveAnimatedWindow(Window* window);
// Calls SetOccluded(|is_occluded|) on |window| and its descendants if they
// are in |tracked_windows_|.
void SetWindowAndDescendantsAreOccluded(Window* window, bool is_occluded);
// Calls SetOccluded() on |window| with |occluded| as argument if |window| is
// in |tracked_windows_|.
void SetOccluded(Window* window, bool occluded);
// Returns true if |window| is in |tracked_windows_|.
bool WindowIsTracked(Window* window) const;
// Returns true if |window| is in |animated_windows_|.
bool WindowIsAnimated(Window* window) const;
// If the root of |window| is not dirty and |predicate| is true, marks the
// root of |window| as dirty. Then, calls MaybeRecomputeOcclusion().
// |predicate| is not evaluated if the root of |window| is already dirty when
// this is called.
template <typename Predicate>
void MarkRootWindowAsDirtyAndMaybeRecomputeOcclusionIf(Window* window,
Predicate predicate);
// Returns true if |window| or one of its parents is in |animated_windows_|.
bool WindowOrParentIsAnimated(Window* window) const;
// Returns true if |window| or one of its descendants is in
// |tracked_windows_| and visible.
bool WindowOrDescendantIsTrackedAndVisible(Window* window) const;
// Returns true if |window| or one of its descendants is visible, opaquely
// fills its bounds and is not in |animated_windows_|. If
// |assume_parent_opaque| is true, the function assumes that the combined
// opacity of window->parent() is 1.0f. If |assume_window_opaque|, the
// function assumes that the opacity of |window| is 1.0f.
bool WindowOrDescendantIsOpaque(Window* window,
bool assume_parent_opaque = false,
bool assume_window_opaque = false) const;
// Returns true if changing the transform, bounds or stacking order of
// |window| could affect the occlusion state of a tracked window.
bool WindowMoveMayAffectOcclusionStates(Window* window) const;
// Called when a tracked |window| is added to a root window.
void TrackedWindowAddedToRoot(Window* window);
// Called when a tracked |window| is removed from a root window.
void TrackedWindowRemovedFromRoot(Window* window);
// Removes |this| from the observer list of |window| and its descendants,
// except if they are in |tracked_windows_| or |windows_being_destroyed_|.
void RemoveObserverFromWindowAndDescendants(Window* window);
// Add |this| to the observer list of |window| and its descendants.
void AddObserverToWindowAndDescendants(Window* window);
// ui::LayerAnimationObserver:
void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override;
void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override;
void OnLayerAnimationScheduled(ui::LayerAnimationSequence* sequence) override;
// WindowObserver:
void OnWindowHierarchyChanged(const HierarchyChangeParams& params) override;
void OnWindowAdded(Window* window) override;
void OnWillRemoveWindow(Window* window) override;
void OnWindowVisibilityChanged(Window* window, bool visible) override;
void OnWindowBoundsChanged(Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
ui::PropertyChangeReason reason) override;
void OnWindowOpacityChanged(Window* window,
ui::PropertyChangeReason reason) override;
void OnWindowTransformed(Window* window,
ui::PropertyChangeReason reason) override;
void OnWindowStackingChanged(Window* window) override;
void OnWindowDestroyed(Window* window) override;
void OnWindowAddedToRootWindow(Window* window) override;
void OnWindowRemovingFromRootWindow(Window* window,
Window* new_root) override;
struct RootWindowState {
// Number of Windows whose occlusion state is tracked under this root
// Window.
int num_tracked_windows = 0;
// Whether the occlusion state of tracked Windows under this root is stale.
bool dirty = false;
};
// Windows whose occlusion state is tracked.
base::flat_set<Window*> tracked_windows_;
// Windows whose bounds or transform are animated.
//
// To reduce the overhead of the WindowOcclusionTracker, windows in this set
// and their descendants are considered non-occluded and cannot occlude other
// windows. A window is added to this set the first time that occlusion is
// computed after it was animated. It is removed when the animation ends or is
// aborted.
base::flat_set<Window*> animated_windows_;
// Root Windows of Windows in |tracked_windows_|.
base::flat_map<Window*, RootWindowState> root_windows_;
DISALLOW_COPY_AND_ASSIGN(WindowOcclusionTracker);
};
} // namespace aura
#endif // UI_AURA_WINDOW_OCCLUSION_TRACKER_H_
This diff is collapsed.
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