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 @@
#include <utility>
#include "ash/public/cpp/config.h"
#include "ash/root_window_controller.h"
#include "ash/session/session_controller.h"
#include "ash/shell.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_observer.h"
#include "base/bind.h"
......@@ -50,6 +52,10 @@ bool ShouldLockOnSuspend() {
// 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
// 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 {
public:
// |callback| - called when all visible root window compositors complete
......@@ -117,6 +123,7 @@ class CompositorWatcher : public ui::CompositorObserver {
// This enum is used to track this cycle. Compositing goes through the
// following states: DidCommit -> CompositingStarted -> CompositingEnded.
enum class CompositingState {
kWaitingForWallpaperAnimation,
kWaitingForCommit,
kWaitingForStarted,
kWaitingForEnded,
......@@ -142,12 +149,21 @@ class CompositorWatcher : public ui::CompositorObserver {
continue;
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->ScheduleDraw();
compositor_observer_.Add(compositor);
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
......@@ -158,6 +174,22 @@ class CompositorWatcher : public ui::CompositorObserver {
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
// cycle, runs |callback_|.
void RunCallbackIfAllCompositingEnded() {
......@@ -204,6 +236,11 @@ void PowerEventObserver::OnLockAnimationsComplete() {
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
// won't be called after |compositor_watcher_|'s destruction, so
// base::Unretained is safe here.
......@@ -233,6 +270,11 @@ void PowerEventObserver::SuspendImminent(
VLOG(1) << "Requesting screen lock from PowerEventObserver";
lock_state_ = LockState::kLocking;
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() {
}
}
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() {
compositor_watcher_.reset();
lock_state_ = LockState::kLocked;
......
......@@ -28,7 +28,8 @@ namespace ash {
// screen is being locked during suspend - display compositing will not be
// stopped before:
// 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
// This is done to ensure that displays have picked up frames from after the
// screen was locked. Without this, displays might initially show
......@@ -84,6 +85,12 @@ class ASH_EXPORT PowerEventObserver
// have gone through compositing cycle after the screen was locked.
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
// can be stopped for all root windows when device suspends.
void OnCompositorsReadyForSuspend();
......
......@@ -178,6 +178,18 @@ bool WallpaperWidgetController::IsAnimating() const {
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,
float blur_sigma) {
DCHECK(widget);
......@@ -227,8 +239,10 @@ void WallpaperWidgetController::WidgetHandlerReset(WidgetHandler* widget) {
}
void WallpaperWidgetController::WidgetFinishedAnimating(WidgetHandler* widget) {
if (widget == animating_widget_.get())
SetAnimatingWidgetAsActive();
if (widget != animating_widget_.get())
return;
SetAnimatingWidgetAsActive();
}
void WallpaperWidgetController::SetAnimatingWidgetAsActive() {
......@@ -241,7 +255,15 @@ void WallpaperWidgetController::SetAnimatingWidgetAsActive() {
std::move(wallpaper_set_callback_).Run();
// Notify observers that animation finished.
RunPendingAnimationEndCallbacks();
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
......@@ -5,6 +5,7 @@
#ifndef ASH_WALLPAPER_WALLPAPER_WIDGET_CONTROLLER_H_
#define ASH_WALLPAPER_WALLPAPER_WIDGET_CONTROLLER_H_
#include <list>
#include <memory>
#include "ash/ash_export.h"
......@@ -36,6 +37,17 @@ class ASH_EXPORT WallpaperWidgetController {
// |animating_widget_| exists.
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
// immediately. The primary widget will be switched when |widget|'s showing
// animation finishes (during which |widget| will be kept by
......@@ -71,6 +83,9 @@ class ASH_EXPORT WallpaperWidgetController {
// Moves |animated_widget_| to |active_widget_|.
void SetAnimatingWidgetAsActive();
// Runs callbacks in |pending_animation_end_callbacks_|.
void RunPendingAnimationEndCallbacks();
// Callback that will be run when |active_widget_| is first set.
base::OnceClosure wallpaper_set_callback_;
......@@ -81,6 +96,10 @@ class ASH_EXPORT WallpaperWidgetController {
// shown.
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);
};
......
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