| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/frame_throttler/frame_throttling_controller.h" |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "ash/constants/app_types.h" |
| #include "ash/test/ash_test_base.h" |
| #include "ash/test/ash_test_helper.h" |
| #include "base/functional/callback.h" |
| #include "base/run_loop.h" |
| #include "base/time/time.h" |
| #include "components/viz/common/surfaces/frame_sink_id.h" |
| #include "components/viz/host/host_frame_sink_manager.h" |
| #include "components/viz/test/test_frame_sink_manager.h" |
| #include "mojo/public/cpp/bindings/pending_receiver.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "services/viz/privileged/mojom/compositing/frame_sink_manager.mojom.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/window.h" |
| #include "ui/aura/window_tree_host.h" |
| #include "ui/gfx/geometry/rect.h" |
| |
| namespace ash { |
| namespace { |
| |
| using ::testing::Eq; |
| using ::testing::IsEmpty; |
| using ::testing::UnorderedElementsAre; |
| |
| class FakeFrameSinkManagerImpl : public viz::TestFrameSinkManagerImpl { |
| public: |
| FakeFrameSinkManagerImpl() = default; |
| FakeFrameSinkManagerImpl(const FakeFrameSinkManagerImpl&) = delete; |
| FakeFrameSinkManagerImpl& operator=(const FakeFrameSinkManagerImpl&) = delete; |
| ~FakeFrameSinkManagerImpl() override = default; |
| |
| // mojom::FrameSinkManager implementation: |
| void Throttle(const std::vector<viz::FrameSinkId>& ids, |
| base::TimeDelta interval) override { |
| throttled_interval_ = interval; |
| throttled_frame_sink_ids_ = ids; |
| } |
| |
| base::TimeDelta throttled_interval() const { return throttled_interval_; } |
| const std::vector<viz::FrameSinkId>& throttled_frame_sink_ids() const { |
| return throttled_frame_sink_ids_; |
| } |
| |
| private: |
| base::TimeDelta throttled_interval_; |
| std::vector<viz::FrameSinkId> throttled_frame_sink_ids_; |
| }; |
| |
| class FrameThrottlingControllerTest : public AshTestBase { |
| protected: |
| FrameThrottlingControllerTest() : controller_(&host_frame_sink_manager_) { |
| mojo::PendingRemote<viz::mojom::FrameSinkManager> frame_sink_manager; |
| mojo::PendingReceiver<viz::mojom::FrameSinkManager> |
| frame_sink_manager_receiver = |
| frame_sink_manager.InitWithNewPipeAndPassReceiver(); |
| mojo::PendingRemote<viz::mojom::FrameSinkManagerClient> |
| frame_sink_manager_client; |
| mojo::PendingReceiver<viz::mojom::FrameSinkManagerClient> |
| frame_sink_manager_client_receiver = |
| frame_sink_manager_client.InitWithNewPipeAndPassReceiver(); |
| |
| host_frame_sink_manager_.BindAndSetManager( |
| std::move(frame_sink_manager_client_receiver), |
| task_environment()->GetMainThreadTaskRunner(), |
| std::move(frame_sink_manager)); |
| frame_sink_manager_impl_.BindReceiver( |
| std::move(frame_sink_manager_receiver), |
| std::move(frame_sink_manager_client)); |
| } |
| |
| void SetUp() override { |
| AshTestBase::SetUp(); |
| controller_.OnWindowTreeHostCreated(ash_test_helper()->GetHost()); |
| } |
| |
| std::unique_ptr<aura::Window> CreateTestBrowserWindow( |
| const viz::FrameSinkId frame_sink_id) { |
| std::unique_ptr<aura::Window> browser_window = |
| CreateAppWindow(gfx::Rect(100, 100), AppType::BROWSER); |
| browser_window->SetEmbedFrameSinkId(frame_sink_id); |
| return browser_window; |
| } |
| |
| FakeFrameSinkManagerImpl frame_sink_manager_impl_; |
| viz::HostFrameSinkManager host_frame_sink_manager_; |
| FrameThrottlingController controller_; |
| }; |
| |
| TEST_F(FrameThrottlingControllerTest, ManualThrottling) { |
| std::unique_ptr<aura::Window> window_1 = CreateTestWindow(); |
| window_1->SetEmbedFrameSinkId({1, 1}); |
| std::unique_ptr<aura::Window> window_2 = CreateTestWindow(); |
| window_2->SetEmbedFrameSinkId({2, 2}); |
| |
| // 10 Hz is lower than the default, but there are no other windows actively |
| // being throttled, so 10 Hz should be used. |
| controller_.StartThrottling({window_1.get(), window_2.get()}, |
| base::Hertz(10)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_THAT(frame_sink_manager_impl_.throttled_interval(), |
| Eq(base::Hertz(10))); |
| EXPECT_THAT( |
| frame_sink_manager_impl_.throttled_frame_sink_ids(), |
| UnorderedElementsAre(viz::FrameSinkId({1, 1}), viz::FrameSinkId({2, 2}))); |
| |
| controller_.StartThrottling({window_1.get()}); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_THAT(frame_sink_manager_impl_.throttled_interval(), |
| Eq(base::Hertz(kDefaultThrottleFps))); |
| EXPECT_THAT(frame_sink_manager_impl_.throttled_frame_sink_ids(), |
| UnorderedElementsAre(viz::FrameSinkId({1, 1}))); |
| |
| controller_.EndThrottling(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_THAT(frame_sink_manager_impl_.throttled_frame_sink_ids(), IsEmpty()); |
| } |
| |
| TEST_F(FrameThrottlingControllerTest, CompositingBasedThrottling) { |
| constexpr viz::FrameSinkId kBrowserWindowFrameSinkId = {99, 99}; |
| |
| std::unique_ptr<aura::Window> browser_window = |
| CreateTestBrowserWindow(kBrowserWindowFrameSinkId); |
| |
| controller_.OnCompositingFrameSinksToThrottleUpdated( |
| ash_test_helper()->GetHost(), {kBrowserWindowFrameSinkId}); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_THAT(frame_sink_manager_impl_.throttled_interval(), |
| Eq(base::Hertz(kDefaultThrottleFps))); |
| EXPECT_THAT(frame_sink_manager_impl_.throttled_frame_sink_ids(), |
| UnorderedElementsAre(kBrowserWindowFrameSinkId)); |
| } |
| |
| TEST_F(FrameThrottlingControllerTest, ManualAndCompositingBasedThrottling) { |
| constexpr viz::FrameSinkId kBrowserWindowFrameSinkId = {99, 99}; |
| constexpr viz::FrameSinkId kManualWindowFrameSinkId = {100, 100}; |
| |
| std::unique_ptr<aura::Window> browser_window = |
| CreateTestBrowserWindow(kBrowserWindowFrameSinkId); |
| |
| controller_.OnCompositingFrameSinksToThrottleUpdated( |
| ash_test_helper()->GetHost(), {kBrowserWindowFrameSinkId}); |
| base::RunLoop().RunUntilIdle(); |
| |
| std::unique_ptr<aura::Window> manual_window = CreateTestWindow(); |
| manual_window->SetEmbedFrameSinkId(kManualWindowFrameSinkId); |
| |
| // 10 Hz is lower than the default frame rate, so it should be rejected. |
| controller_.StartThrottling({manual_window.get()}, base::Hertz(10)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_THAT(frame_sink_manager_impl_.throttled_interval(), |
| Eq(base::Hertz(kDefaultThrottleFps))); |
| EXPECT_THAT(frame_sink_manager_impl_.throttled_frame_sink_ids(), |
| UnorderedElementsAre(kBrowserWindowFrameSinkId, |
| kManualWindowFrameSinkId)); |
| |
| // 30 Hz is higher than the default frame rate, so it should be respected. |
| controller_.StartThrottling({manual_window.get()}, base::Hertz(30)); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_THAT(frame_sink_manager_impl_.throttled_interval(), |
| Eq(base::Hertz(30))); |
| EXPECT_THAT(frame_sink_manager_impl_.throttled_frame_sink_ids(), |
| UnorderedElementsAre(kBrowserWindowFrameSinkId, |
| kManualWindowFrameSinkId)); |
| |
| // The frame rate should return to default after manual window is removed. |
| controller_.EndThrottling(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_THAT(frame_sink_manager_impl_.throttled_interval(), |
| Eq(base::Hertz(kDefaultThrottleFps))); |
| EXPECT_THAT(frame_sink_manager_impl_.throttled_frame_sink_ids(), |
| UnorderedElementsAre(kBrowserWindowFrameSinkId)); |
| } |
| |
| } // namespace |
| } // namespace ash |