Commit c7dbb870 authored by Toni Barzic's avatar Toni Barzic Committed by Commit Bot

Wait for animated wallpaper changes to finish before suspending displays

PowerEventObserver delays display suspend until lock screen UI is shown
and compositors submit enough frames to know the UI changes have reached
displays.
This does not account for wallpaper changes due to screen lock:
  * On screen lock, the active wallpaper will be changed with its blurred
    version.
  * If screen lock changes the active user (e.g. if the screen is locked
    from the secondary user), the wallpaper will change to the new
    active user's blurred wallpaper.
Given that PowerEventObserver does not ensure wallpaper switch is done
before it stops drawing to the display, the first frame shown on the
device resume might still contain the UI from before wallpaper change.

This changes PowerEventObserver to additionally wait for any active
wallpaper changes (which can be detected by checking
WallpaperWidgetController::IsAnimating - set while wallpaper widget is
being changed) to finish before it starts waiting for compositor frames
to get composited, thus ensuring the correct wallpaper is set when the
display/compositing is suspended.

Adds the following methods to WallpaperWidgetController:
  * AddPendingAnimationEndCallback - to provide a way for
    PowerEventObserver to get notified when the pending wallpaper change
    completes
  * EndPendingAnimation which can be used to force immediate wallpaper
    change when device starts suspending (and prevents suspend flow from
    being blocked on unnecessarily waiting for new wallpaper animation)

BUG=820436,803762

Change-Id: Iad2074280ebba740b95c50b9d60a028bf5a0d45c
Reviewed-on: https://chromium-review.googlesource.com/956757Reviewed-by: default avatarDan Erat <derat@chromium.org>
Reviewed-by: default avatarWenzhao (Colin) Zang <wzang@chromium.org>
Commit-Queue: Toni Barzic <tbarzic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#542945}
parent 5f30293d
...@@ -8,9 +8,11 @@ ...@@ -8,9 +8,11 @@
#include <utility> #include <utility>
#include "ash/public/cpp/config.h" #include "ash/public/cpp/config.h"
#include "ash/root_window_controller.h"
#include "ash/session/session_controller.h" #include "ash/session/session_controller.h"
#include "ash/shell.h" #include "ash/shell.h"
#include "ash/system/tray/system_tray_notifier.h" #include "ash/system/tray/system_tray_notifier.h"
#include "ash/wallpaper/wallpaper_widget_controller.h"
#include "ash/wm/lock_state_controller.h" #include "ash/wm/lock_state_controller.h"
#include "ash/wm/lock_state_observer.h" #include "ash/wm/lock_state_observer.h"
#include "base/bind.h" #include "base/bind.h"
...@@ -50,6 +52,10 @@ bool ShouldLockOnSuspend() { ...@@ -50,6 +52,10 @@ bool ShouldLockOnSuspend() {
// will not start drawing the next frame before the previous swap happens, when // will not start drawing the next frame before the previous swap happens, when
// the second compositing cycle ends, it should be safe to assume the required // the second compositing cycle ends, it should be safe to assume the required
// buffer swap happened at that point. // buffer swap happened at that point.
// Note that the compositor watcher will wait for any pending wallpaper
// animation for a root window to finish before it starts observing compositor
// cycles, to ensure it picks up wallpaper state from after the animation ends,
// and avoids issues like https://crbug.com/820436.
class CompositorWatcher : public ui::CompositorObserver { class CompositorWatcher : public ui::CompositorObserver {
public: public:
// |callback| - called when all visible root window compositors complete // |callback| - called when all visible root window compositors complete
...@@ -117,6 +123,7 @@ class CompositorWatcher : public ui::CompositorObserver { ...@@ -117,6 +123,7 @@ class CompositorWatcher : public ui::CompositorObserver {
// This enum is used to track this cycle. Compositing goes through the // This enum is used to track this cycle. Compositing goes through the
// following states: DidCommit -> CompositingStarted -> CompositingEnded. // following states: DidCommit -> CompositingStarted -> CompositingEnded.
enum class CompositingState { enum class CompositingState {
kWaitingForWallpaperAnimation,
kWaitingForCommit, kWaitingForCommit,
kWaitingForStarted, kWaitingForStarted,
kWaitingForEnded, kWaitingForEnded,
...@@ -142,12 +149,21 @@ class CompositorWatcher : public ui::CompositorObserver { ...@@ -142,12 +149,21 @@ class CompositorWatcher : public ui::CompositorObserver {
continue; continue;
DCHECK(!pending_compositing_.count(compositor)); DCHECK(!pending_compositing_.count(compositor));
pending_compositing_[compositor].state =
CompositingState::kWaitingForCommit;
compositor_observer_.Add(compositor);
// Schedule a draw to force at least one more compositing cycle. compositor_observer_.Add(compositor);
compositor->ScheduleDraw(); pending_compositing_[compositor].state =
CompositingState::kWaitingForWallpaperAnimation;
WallpaperWidgetController* wallpaper_widget_controller =
RootWindowController::ForWindow(window)
->wallpaper_widget_controller();
if (wallpaper_widget_controller->IsAnimating()) {
wallpaper_widget_controller->AddPendingAnimationEndCallback(
base::BindOnce(&CompositorWatcher::StartObservingCompositing,
weak_ptr_factory_.GetWeakPtr(), compositor));
} else {
StartObservingCompositing(compositor);
}
} }
// Post task to make sure callback is not invoked synchronously as watcher // Post task to make sure callback is not invoked synchronously as watcher
...@@ -158,6 +174,22 @@ class CompositorWatcher : public ui::CompositorObserver { ...@@ -158,6 +174,22 @@ class CompositorWatcher : public ui::CompositorObserver {
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
} }
// Called when the wallpaper animations end for the root window associated
// with the compositor. It starts observing the compositor's compositing
// cycles.
void StartObservingCompositing(ui::Compositor* compositor) {
if (!pending_compositing_.count(compositor) ||
pending_compositing_[compositor].state !=
CompositingState::kWaitingForWallpaperAnimation) {
return;
}
pending_compositing_[compositor].state =
CompositingState::kWaitingForCommit;
// Schedule a draw to force at least one more compositing cycle.
compositor->ScheduleDraw();
}
// If all observed root window compositors have gone through a compositing // If all observed root window compositors have gone through a compositing
// cycle, runs |callback_|. // cycle, runs |callback_|.
void RunCallbackIfAllCompositingEnded() { void RunCallbackIfAllCompositingEnded() {
...@@ -204,6 +236,11 @@ void PowerEventObserver::OnLockAnimationsComplete() { ...@@ -204,6 +236,11 @@ void PowerEventObserver::OnLockAnimationsComplete() {
lock_state_ = LockState::kLockedCompositingPending; lock_state_ = LockState::kLockedCompositingPending;
// If suspending, run pending animations to the end immediately, as there is
// no point in waiting for them to finish given that the device is suspending.
if (displays_suspended_callback_)
EndPendingWallpaperAnimations();
// The |compositor_watcher_| is owned by this, and the callback passed to it // The |compositor_watcher_| is owned by this, and the callback passed to it
// won't be called after |compositor_watcher_|'s destruction, so // won't be called after |compositor_watcher_|'s destruction, so
// base::Unretained is safe here. // base::Unretained is safe here.
...@@ -233,6 +270,11 @@ void PowerEventObserver::SuspendImminent( ...@@ -233,6 +270,11 @@ void PowerEventObserver::SuspendImminent(
VLOG(1) << "Requesting screen lock from PowerEventObserver"; VLOG(1) << "Requesting screen lock from PowerEventObserver";
lock_state_ = LockState::kLocking; lock_state_ = LockState::kLocking;
Shell::Get()->lock_state_controller()->LockWithoutAnimation(); Shell::Get()->lock_state_controller()->LockWithoutAnimation();
} else if (lock_state_ != LockState::kLocking) {
// If the screen is still being locked (i.e. in kLocking state),
// EndPendingWallpaperAnimations() will be called in
// OnLockAnimationsComplete().
EndPendingWallpaperAnimations();
} }
} }
} }
...@@ -316,6 +358,15 @@ void PowerEventObserver::StopCompositingAndSuspendDisplays() { ...@@ -316,6 +358,15 @@ void PowerEventObserver::StopCompositingAndSuspendDisplays() {
} }
} }
void PowerEventObserver::EndPendingWallpaperAnimations() {
for (aura::Window* window : Shell::GetAllRootWindows()) {
WallpaperWidgetController* wallpaper_widget_controller =
RootWindowController::ForWindow(window)->wallpaper_widget_controller();
if (wallpaper_widget_controller->IsAnimating())
wallpaper_widget_controller->EndPendingAnimation();
}
}
void PowerEventObserver::OnCompositorsReadyForSuspend() { void PowerEventObserver::OnCompositorsReadyForSuspend() {
compositor_watcher_.reset(); compositor_watcher_.reset();
lock_state_ = LockState::kLocked; lock_state_ = LockState::kLocked;
......
...@@ -28,7 +28,8 @@ namespace ash { ...@@ -28,7 +28,8 @@ namespace ash {
// screen is being locked during suspend - display compositing will not be // screen is being locked during suspend - display compositing will not be
// stopped before: // stopped before:
// 1. lock screen window is shown // 1. lock screen window is shown
// 2. the compositor goes through at least two compositing cycles after the // 2. wallpaper changes due to screen lock are finished
// 3. the compositor goes through at least two compositing cycles after the
// screen lock // screen lock
// This is done to ensure that displays have picked up frames from after the // This is done to ensure that displays have picked up frames from after the
// screen was locked. Without this, displays might initially show // screen was locked. Without this, displays might initially show
...@@ -84,6 +85,12 @@ class ASH_EXPORT PowerEventObserver ...@@ -84,6 +85,12 @@ class ASH_EXPORT PowerEventObserver
// have gone through compositing cycle after the screen was locked. // have gone through compositing cycle after the screen was locked.
void StopCompositingAndSuspendDisplays(); void StopCompositingAndSuspendDisplays();
// If any of the root windows have pending wallpaper animations, it stops
// them - this is used to stop wallpaper animations during suspend, and thus
// improve the suspend time (given that suspend will be delayed until the
// wallpaper animations finish).
void EndPendingWallpaperAnimations();
// Callback run by |compositor_watcher_| when it detects that composting // Callback run by |compositor_watcher_| when it detects that composting
// can be stopped for all root windows when device suspends. // can be stopped for all root windows when device suspends.
void OnCompositorsReadyForSuspend(); void OnCompositorsReadyForSuspend();
......
...@@ -178,6 +178,18 @@ bool WallpaperWidgetController::IsAnimating() const { ...@@ -178,6 +178,18 @@ bool WallpaperWidgetController::IsAnimating() const {
return animating_widget_.get(); return animating_widget_.get();
} }
void WallpaperWidgetController::EndPendingAnimation() {
if (!IsAnimating())
return;
animating_widget_->StopAnimating();
}
void WallpaperWidgetController::AddPendingAnimationEndCallback(
base::OnceClosure callback) {
DCHECK(IsAnimating());
pending_animation_end_callbacks_.emplace_back(std::move(callback));
}
void WallpaperWidgetController::SetWallpaperWidget(views::Widget* widget, void WallpaperWidgetController::SetWallpaperWidget(views::Widget* widget,
float blur_sigma) { float blur_sigma) {
DCHECK(widget); DCHECK(widget);
...@@ -227,8 +239,10 @@ void WallpaperWidgetController::WidgetHandlerReset(WidgetHandler* widget) { ...@@ -227,8 +239,10 @@ void WallpaperWidgetController::WidgetHandlerReset(WidgetHandler* widget) {
} }
void WallpaperWidgetController::WidgetFinishedAnimating(WidgetHandler* widget) { void WallpaperWidgetController::WidgetFinishedAnimating(WidgetHandler* widget) {
if (widget == animating_widget_.get()) if (widget != animating_widget_.get())
SetAnimatingWidgetAsActive(); return;
SetAnimatingWidgetAsActive();
} }
void WallpaperWidgetController::SetAnimatingWidgetAsActive() { void WallpaperWidgetController::SetAnimatingWidgetAsActive() {
...@@ -241,7 +255,15 @@ void WallpaperWidgetController::SetAnimatingWidgetAsActive() { ...@@ -241,7 +255,15 @@ void WallpaperWidgetController::SetAnimatingWidgetAsActive() {
std::move(wallpaper_set_callback_).Run(); std::move(wallpaper_set_callback_).Run();
// Notify observers that animation finished. // Notify observers that animation finished.
RunPendingAnimationEndCallbacks();
Shell::Get()->wallpaper_controller()->OnWallpaperAnimationFinished(); Shell::Get()->wallpaper_controller()->OnWallpaperAnimationFinished();
} }
void WallpaperWidgetController::RunPendingAnimationEndCallbacks() {
std::list<base::OnceClosure> callbacks;
pending_animation_end_callbacks_.swap(callbacks);
for (auto& callback : callbacks)
std::move(callback).Run();
}
} // namespace ash } // namespace ash
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef ASH_WALLPAPER_WALLPAPER_WIDGET_CONTROLLER_H_ #ifndef ASH_WALLPAPER_WALLPAPER_WIDGET_CONTROLLER_H_
#define ASH_WALLPAPER_WALLPAPER_WIDGET_CONTROLLER_H_ #define ASH_WALLPAPER_WALLPAPER_WIDGET_CONTROLLER_H_
#include <list>
#include <memory> #include <memory>
#include "ash/ash_export.h" #include "ash/ash_export.h"
...@@ -36,6 +37,17 @@ class ASH_EXPORT WallpaperWidgetController { ...@@ -36,6 +37,17 @@ class ASH_EXPORT WallpaperWidgetController {
// |animating_widget_| exists. // |animating_widget_| exists.
bool IsAnimating() const; bool IsAnimating() const;
// If an animating wallpaper change is in progress, it ends the animation and
// changes the wallpaper immediately.
// No-op if IsAnimation() returns false.
void EndPendingAnimation();
// If an animating wallpaper change is in progress, it adds a callback that
// will be run when the pending animation ends.
// The callback will not be run if a wallpaper animation is not in progress -
// in that case the method will return false.
void AddPendingAnimationEndCallback(base::OnceClosure callback);
// Sets a new wallpaper widget - this will not change the primary widget // Sets a new wallpaper widget - this will not change the primary widget
// immediately. The primary widget will be switched when |widget|'s showing // immediately. The primary widget will be switched when |widget|'s showing
// animation finishes (during which |widget| will be kept by // animation finishes (during which |widget| will be kept by
...@@ -71,6 +83,9 @@ class ASH_EXPORT WallpaperWidgetController { ...@@ -71,6 +83,9 @@ class ASH_EXPORT WallpaperWidgetController {
// Moves |animated_widget_| to |active_widget_|. // Moves |animated_widget_| to |active_widget_|.
void SetAnimatingWidgetAsActive(); void SetAnimatingWidgetAsActive();
// Runs callbacks in |pending_animation_end_callbacks_|.
void RunPendingAnimationEndCallbacks();
// Callback that will be run when |active_widget_| is first set. // Callback that will be run when |active_widget_| is first set.
base::OnceClosure wallpaper_set_callback_; base::OnceClosure wallpaper_set_callback_;
...@@ -81,6 +96,10 @@ class ASH_EXPORT WallpaperWidgetController { ...@@ -81,6 +96,10 @@ class ASH_EXPORT WallpaperWidgetController {
// shown. // shown.
std::unique_ptr<WidgetHandler> animating_widget_; std::unique_ptr<WidgetHandler> animating_widget_;
// Callbacks to be run when the |animating_widget_| stops animating and gets
// set as the active widget.
std::list<base::OnceClosure> pending_animation_end_callbacks_;
DISALLOW_COPY_AND_ASSIGN(WallpaperWidgetController); DISALLOW_COPY_AND_ASSIGN(WallpaperWidgetController);
}; };
......
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