Commit 00982d27 authored by yjliu's avatar yjliu Committed by Commit Bot

Frame throttling for Android apps in ChromeOS.

Throttling Android apps from ash through wayland protocol. This is done
by manipulating the vsync updates intervals in exo. Because vsync is
global for all android apps, we don't apply throttle if at least one
android app shouldn't be throttled.

Bug: 1107201
Change-Id: I39f761f9460120928687058732942739bb26c88b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2389326Reviewed-by: default avatarkylechar <kylechar@chromium.org>
Reviewed-by: default avatarMitsuru Oshima <oshima@chromium.org>
Commit-Queue: Jun Liu <yjliu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#816831}
parent 91b5b55a
......@@ -8,6 +8,8 @@
#include "ash/public/cpp/app_types.h"
#include "ash/public/cpp/ash_switches.h"
#include "ash/shell.h"
#include "ash/wm/mru_window_tracker.h"
#include "base/command_line.h"
#include "base/strings/string_number_conversions.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
......@@ -64,32 +66,90 @@ void FrameThrottlingController::StartThrottling(
if (windows_throttled_)
EndThrottling();
windows_throttled_ = true;
std::vector<viz::FrameSinkId> frame_sink_ids;
frame_sink_ids.reserve(windows.size());
CollectBrowserFrameSinkIds(windows, &frame_sink_ids);
if (!frame_sink_ids.empty())
StartThrottling(frame_sink_ids, throttled_fps_);
auto all_windows =
Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk);
std::vector<aura::Window*> all_arc_windows;
std::copy_if(all_windows.begin(), all_windows.end(),
std::back_inserter(all_arc_windows), [](aura::Window* window) {
return ash::AppType::ARC_APP ==
static_cast<ash::AppType>(
window->GetProperty(aura::client::kAppType));
});
std::vector<aura::Window*> browser_windows;
browser_windows.reserve(windows.size());
std::vector<aura::Window*> arc_windows;
arc_windows.reserve(windows.size());
for (auto* window : windows) {
ash::AppType type =
static_cast<ash::AppType>(window->GetProperty(aura::client::kAppType));
switch (type) {
case ash::AppType::BROWSER:
browser_windows.push_back(window);
break;
case ash::AppType::ARC_APP:
arc_windows.push_back(window);
break;
default:
break;
}
}
for (auto& observer : observers_) {
observer.OnThrottlingStarted(windows, throttled_fps_);
if (!browser_windows.empty()) {
std::vector<viz::FrameSinkId> frame_sink_ids;
frame_sink_ids.reserve(browser_windows.size());
CollectBrowserFrameSinkIds(browser_windows, &frame_sink_ids);
if (!frame_sink_ids.empty())
StartThrottlingFrameSinks(frame_sink_ids);
}
std::vector<aura::Window*> all_windows_to_throttle(browser_windows);
// Do not throttle arc if at least one arc window should not be throttled.
if (!arc_windows.empty() && (arc_windows.size() == all_arc_windows.size())) {
StartThrottlingArc(arc_windows);
all_windows_to_throttle.insert(all_windows_to_throttle.end(),
arc_windows.begin(), arc_windows.end());
}
if (!all_windows_to_throttle.empty()) {
windows_throttled_ = true;
for (auto& observer : observers_)
observer.OnThrottlingStarted(all_windows_to_throttle, throttled_fps_);
}
}
void FrameThrottlingController::StartThrottling(
const std::vector<viz::FrameSinkId>& frame_sink_ids,
uint8_t fps) {
DCHECK_GT(fps, 0);
void FrameThrottlingController::StartThrottlingFrameSinks(
const std::vector<viz::FrameSinkId>& frame_sink_ids) {
DCHECK(!frame_sink_ids.empty());
if (context_factory_) {
context_factory_->GetHostFrameSinkManager()->StartThrottling(
frame_sink_ids, base::TimeDelta::FromSeconds(1) / fps);
frame_sink_ids, base::TimeDelta::FromSeconds(1) / throttled_fps_);
}
}
void FrameThrottlingController::EndThrottling() {
void FrameThrottlingController::StartThrottlingArc(
const std::vector<aura::Window*>& arc_windows) {
for (auto& arc_observer : arc_observers_) {
arc_observer.OnThrottlingStarted(arc_windows, throttled_fps_);
}
}
void FrameThrottlingController::EndThrottlingFrameSinks() {
if (context_factory_)
context_factory_->GetHostFrameSinkManager()->EndThrottling();
}
void FrameThrottlingController::EndThrottlingArc() {
for (auto& arc_observer : arc_observers_) {
arc_observer.OnThrottlingEnded();
}
}
void FrameThrottlingController::EndThrottling() {
EndThrottlingFrameSinks();
EndThrottlingArc();
for (auto& observer : observers_) {
observer.OnThrottlingEnded();
......@@ -106,4 +166,14 @@ void FrameThrottlingController::RemoveObserver(
observers_.RemoveObserver(observer);
}
void FrameThrottlingController::AddArcObserver(
FrameThrottlingObserver* observer) {
arc_observers_.AddObserver(observer);
}
void FrameThrottlingController::RemoveArcObserver(
FrameThrottlingObserver* observer) {
arc_observers_.RemoveObserver(observer);
}
} // namespace ash
......@@ -43,14 +43,22 @@ class ASH_EXPORT FrameThrottlingController {
void AddObserver(FrameThrottlingObserver* observer);
void RemoveObserver(FrameThrottlingObserver* observer);
void AddArcObserver(FrameThrottlingObserver* observer);
void RemoveArcObserver(FrameThrottlingObserver* observer);
uint8_t throttled_fps() const { return throttled_fps_; }
private:
void StartThrottling(const std::vector<viz::FrameSinkId>& frame_sink_ids,
uint8_t fps);
void StartThrottlingFrameSinks(
const std::vector<viz::FrameSinkId>& frame_sink_ids);
void StartThrottlingArc(const std::vector<aura::Window*>& windows);
void EndThrottlingFrameSinks();
void EndThrottlingArc();
ui::ContextFactory* context_factory_ = nullptr;
base::ObserverList<FrameThrottlingObserver> observers_;
base::ObserverList<FrameThrottlingObserver> arc_observers_;
// The fps used for throttling.
uint8_t throttled_fps_ = kDefaultThrottleFps;
bool windows_throttled_ = false;
......
......@@ -686,11 +686,15 @@ TEST_F(OverviewControllerTest, FrameThrottling) {
FrameThrottlingController* frame_throttling_controller =
Shell::Get()->frame_throttling_controller();
frame_throttling_controller->AddObserver(&observer);
const int window_count = 5;
std::unique_ptr<aura::Window> created_windows[window_count];
std::vector<aura::Window*> windows(window_count, nullptr);
for (int i = 0; i < window_count; ++i) {
created_windows[i] = CreateAppWindow(gfx::Rect(), AppType::BROWSER);
const int browser_window_count = 3;
const int arc_window_count = 2;
const int total_window_count = browser_window_count + arc_window_count;
std::unique_ptr<aura::Window> created_windows[total_window_count];
std::vector<aura::Window*> windows(total_window_count, nullptr);
for (int i = 0; i < total_window_count; ++i) {
created_windows[i] = CreateAppWindow(gfx::Rect(), i < browser_window_count
? AppType::BROWSER
: AppType::ARC_APP);
windows[i] = created_windows[i].get();
}
......
......@@ -324,9 +324,52 @@ TEST_F(OverviewGridTest, FrameThrottling) {
aura::Window* window = windows[0];
windows.erase(windows.begin());
EXPECT_CALL(observer, OnThrottlingEnded());
EXPECT_CALL(observer,
OnThrottlingStarted(testing::UnorderedElementsAreArray(windows),
throttled_fps));
if (!windows.empty()) {
EXPECT_CALL(observer, OnThrottlingStarted(
testing::UnorderedElementsAreArray(windows),
throttled_fps));
}
OverviewItem* item = grid()->GetOverviewItemContaining(window);
grid()->RemoveItem(item, /*item_destroying=*/false, /*reposition=*/false);
}
frame_throttling_controller->RemoveObserver(&observer);
}
TEST_F(OverviewGridTest, FrameThrottlingArc) {
testing::NiceMock<MockFrameThrottlingObserver> observer;
FrameThrottlingController* frame_throttling_controller =
Shell::Get()->frame_throttling_controller();
uint8_t throttled_fps = frame_throttling_controller->throttled_fps();
frame_throttling_controller->AddObserver(&observer);
const int window_count = 5;
std::unique_ptr<aura::Window> created_windows[window_count];
std::vector<aura::Window*> windows(window_count, nullptr);
for (int i = 0; i < window_count; ++i) {
created_windows[i] = CreateAppWindow(gfx::Rect(), AppType::ARC_APP);
windows[i] = created_windows[i].get();
}
InitializeGrid(windows);
frame_throttling_controller->StartThrottling(windows);
// Add a new window to overview.
std::unique_ptr<aura::Window> new_window(
CreateAppWindow(gfx::Rect(), AppType::ARC_APP));
windows.push_back(new_window.get());
EXPECT_CALL(observer, OnThrottlingEnded());
EXPECT_CALL(observer,
OnThrottlingStarted(testing::UnorderedElementsAreArray(windows),
throttled_fps));
grid()->AppendItem(new_window.get(), /*reposition=*/false, /*animate=*/false,
/*use_spawn_animation=*/false);
// Remove windows one by one. Once one window is out of the overview grid, no
// more windows will be throttled.
for (int i = 0; i < window_count; ++i) {
aura::Window* window = windows[0];
windows.erase(windows.begin());
if (i == 0)
EXPECT_CALL(observer, OnThrottlingEnded());
EXPECT_CALL(observer, OnThrottlingStarted(testing::_, testing::_)).Times(0);
OverviewItem* item = grid()->GetOverviewItemContaining(window);
grid()->RemoveItem(item, /*item_destroying=*/false, /*reposition=*/false);
}
......
......@@ -189,6 +189,8 @@ source_set("test_support") {
testonly = true
sources = [
"mock_vsync_timing_observer.cc",
"mock_vsync_timing_observer.h",
"test/exo_test_base_views.cc",
"test/exo_test_base_views.h",
"test/exo_test_suite_aura.cc",
......@@ -294,6 +296,7 @@ source_set("unit_tests") {
"toast_surface_unittest.cc",
"touch_unittest.cc",
"ui_lock_controller_unittest.cc",
"wm_helper_chromeos_unittest.cc",
"xdg_shell_surface_unittest.cc",
]
......
// Copyright 2020 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 "components/exo/mock_vsync_timing_observer.h"
namespace exo {
MockVSyncTimingObserver::MockVSyncTimingObserver() = default;
MockVSyncTimingObserver::~MockVSyncTimingObserver() = default;
} // namespace exo
// Copyright 2020 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 COMPONENTS_EXO_MOCK_VSYNC_TIMING_OBSERVER_H_
#define COMPONENTS_EXO_MOCK_VSYNC_TIMING_OBSERVER_H_
#include "components/exo/vsync_timing_manager.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace exo {
class MockVSyncTimingObserver : public VSyncTimingManager::Observer {
public:
MockVSyncTimingObserver();
~MockVSyncTimingObserver() override;
MOCK_METHOD(void,
OnUpdateVSyncParameters,
(base::TimeTicks timebase, base::TimeDelta interval),
(override));
};
} // namespace exo
#endif // COMPONENTS_EXO_MOCK_VSYNC_TIMING_OBSERVER_H_
......@@ -63,6 +63,7 @@ class ExoTestBase : public ash::AshTestBase {
ShellSurface* parent);
ExoTestHelper* exo_test_helper() { return &exo_test_helper_; }
WMHelper* wm_helper() { return wm_helper_.get(); }
private:
ExoTestHelper exo_test_helper_;
......
......@@ -6,11 +6,13 @@
#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
namespace exo {
VSyncTimingManager::VSyncTimingManager(Delegate* delegate)
: delegate_(delegate) {}
: last_interval_(viz::BeginFrameArgs::DefaultInterval()),
delegate_(delegate) {}
VSyncTimingManager::~VSyncTimingManager() = default;
......@@ -36,8 +38,25 @@ void VSyncTimingManager::RemoveObserver(Observer* obs) {
void VSyncTimingManager::OnUpdateVSyncParameters(base::TimeTicks timebase,
base::TimeDelta interval) {
for (auto* observer : observers_)
observer->OnUpdateVSyncParameters(timebase, interval);
for (auto* observer : observers_) {
observer->OnUpdateVSyncParameters(timebase, throttled_interval_.is_zero()
? interval
: throttled_interval_);
}
last_timebase_ = timebase;
last_interval_ = interval;
}
void VSyncTimingManager::OnThrottlingStarted(
const std::vector<aura::Window*>& windows,
uint8_t fps) {
throttled_interval_ = base::TimeDelta::FromSeconds(1) / fps;
OnUpdateVSyncParameters(last_timebase_, last_interval_);
}
void VSyncTimingManager::OnThrottlingEnded() {
throttled_interval_ = base::TimeDelta();
OnUpdateVSyncParameters(last_timebase_, last_interval_);
}
void VSyncTimingManager::InitializeConnection() {
......
......@@ -7,6 +7,7 @@
#include <vector>
#include "ash/frame_throttler/frame_throttling_observer.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
......@@ -18,7 +19,8 @@ namespace exo {
// Multiplexes vsync parameter updates from the display compositor to exo
// clients using the zcr_vsync_feedback_v1 protocol. Will maintain an IPC
// connection to the display compositor only when necessary.
class VSyncTimingManager : public viz::mojom::VSyncParameterObserver {
class VSyncTimingManager : public viz::mojom::VSyncParameterObserver,
public ash::FrameThrottlingObserver {
public:
// Will be notified about changes in vsync parameters.
class Observer {
......@@ -44,15 +46,26 @@ class VSyncTimingManager : public viz::mojom::VSyncParameterObserver {
void AddObserver(Observer* obs);
void RemoveObserver(Observer* obs);
base::TimeDelta throttled_interval() const { return throttled_interval_; }
private:
// Overridden from viz::mojom::VSyncParameterObserver:
void OnUpdateVSyncParameters(base::TimeTicks timebase,
base::TimeDelta interval) override;
// Overridden from ash::FrameThrottlingObserver
void OnThrottlingStarted(const std::vector<aura::Window*>& windows,
uint8_t fps) override;
void OnThrottlingEnded() override;
void InitializeConnection();
void MaybeInitializeConnection();
void OnConnectionError();
base::TimeDelta throttled_interval_;
base::TimeDelta last_interval_;
base::TimeTicks last_timebase_;
Delegate* const delegate_;
std::vector<Observer*> observers_;
......
......@@ -839,6 +839,7 @@ class WaylandRemoteShell : public ash::TabletModeObserver,
helper->AddTabletModeObserver(this);
helper->AddActivationObserver(this);
display::Screen::GetScreen()->AddObserver(this);
helper->AddFrameThrottlingObserver();
layout_mode_ = helper->InTabletMode()
? ZCR_REMOTE_SHELL_V1_LAYOUT_MODE_TABLET
......@@ -864,6 +865,7 @@ class WaylandRemoteShell : public ash::TabletModeObserver,
helper->RemoveTabletModeObserver(this);
helper->RemoveActivationObserver(this);
display::Screen::GetScreen()->RemoveObserver(this);
helper->RemoveFrameThrottlingObserver();
}
std::unique_ptr<ClientControlledShellSurface> CreateShellSurface(
......
......@@ -5,6 +5,7 @@
#include "components/exo/wm_helper_chromeos.h"
#include "components/exo/wm_helper.h"
#include "ash/frame_throttler/frame_throttling_controller.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/shell.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
......@@ -61,6 +62,16 @@ void WMHelperChromeOS::RemoveDisplayConfigurationObserver(
ash::Shell::Get()->window_tree_host_manager()->RemoveObserver(observer);
}
void WMHelperChromeOS::AddFrameThrottlingObserver() {
ash::Shell::Get()->frame_throttling_controller()->AddArcObserver(
&vsync_timing_manager_);
}
void WMHelperChromeOS::RemoveFrameThrottlingObserver() {
ash::Shell::Get()->frame_throttling_controller()->RemoveArcObserver(
&vsync_timing_manager_);
}
void WMHelperChromeOS::AddActivationObserver(
wm::ActivationChangeObserver* observer) {
ash::Shell::Get()->activation_client()->AddObserver(observer);
......
......@@ -60,6 +60,8 @@ class WMHelperChromeOS : public WMHelper, public VSyncTimingManager::Delegate {
ash::WindowTreeHostManager::Observer* observer);
void RemoveDisplayConfigurationObserver(
ash::WindowTreeHostManager::Observer* observer);
void AddFrameThrottlingObserver();
void RemoveFrameThrottlingObserver();
// Overridden from WMHelper
void AddActivationObserver(wm::ActivationChangeObserver* observer) override;
......
// Copyright 2020 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 "components/exo/wm_helper_chromeos.h"
#include "ash/frame_throttler/frame_throttling_controller.h"
#include "ash/shell.h"
#include "components/exo/mock_vsync_timing_observer.h"
#include "components/exo/test/exo_test_base.h"
namespace exo {
using WMHelperChromeOSTest = test::ExoTestBase;
TEST_F(WMHelperChromeOSTest, FrameThrottling) {
WMHelperChromeOS* wm_helper_chromeos =
static_cast<WMHelperChromeOS*>(wm_helper());
wm_helper_chromeos->AddFrameThrottlingObserver();
VSyncTimingManager& vsync_timing_manager =
wm_helper_chromeos->GetVSyncTimingManager();
MockVSyncTimingObserver observer;
vsync_timing_manager.AddObserver(&observer);
ash::FrameThrottlingController* ftc =
ash::Shell::Get()->frame_throttling_controller();
// Throttling should be off by default.
EXPECT_EQ(vsync_timing_manager.throttled_interval(), base::TimeDelta());
// Create two arc windows.
std::unique_ptr<aura::Window> arc_window_1 =
CreateAppWindow(gfx::Rect(), ash::AppType::ARC_APP);
std::unique_ptr<aura::Window> arc_window_2 =
CreateAppWindow(gfx::Rect(), ash::AppType::ARC_APP);
// Starting throttling on one of the two arc windows will have no effect on
// vsync time.
EXPECT_CALL(observer, OnUpdateVSyncParameters(testing::_, testing::_))
.Times(0);
ftc->StartThrottling({arc_window_1.get()});
EXPECT_EQ(vsync_timing_manager.throttled_interval(), base::TimeDelta());
// Both windows are to be throttled, vsync timing will be adjusted.
base::TimeDelta throttled_interval =
base::TimeDelta::FromSeconds(1) / ftc->throttled_fps();
EXPECT_CALL(observer,
OnUpdateVSyncParameters(testing::_, throttled_interval));
ftc->StartThrottling({arc_window_1.get(), arc_window_2.get()});
EXPECT_EQ(vsync_timing_manager.throttled_interval(), throttled_interval);
EXPECT_CALL(observer,
OnUpdateVSyncParameters(testing::_,
viz::BeginFrameArgs::DefaultInterval()));
ftc->EndThrottling();
EXPECT_EQ(vsync_timing_manager.throttled_interval(), base::TimeDelta());
vsync_timing_manager.RemoveObserver(&observer);
wm_helper_chromeos->RemoveFrameThrottlingObserver();
}
} // namespace exo
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