Avi Drissman | 8ba1bad | 2022-09-13 19:22:36 | [diff] [blame^] | 1 | // Copyright 2019 The Chromium Authors |
Xiyuan Xia | d7e4d94 | 2019-06-12 22:32:52 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "components/viz/client/frame_eviction_manager.h" |
| 6 | |
| 7 | #include <algorithm> |
| 8 | #include <vector> |
| 9 | |
Benoit Lize | 6325f04 | 2022-09-12 10:11:35 | [diff] [blame] | 10 | #include "base/memory/memory_pressure_listener.h" |
| 11 | #include "base/test/scoped_feature_list.h" |
| 12 | #include "base/test/test_mock_time_task_runner.h" |
| 13 | #include "components/viz/common/features.h" |
Xiyuan Xia | d7e4d94 | 2019-06-12 22:32:52 | [diff] [blame] | 14 | #include "testing/gtest/include/gtest/gtest.h" |
| 15 | |
| 16 | namespace viz { |
| 17 | |
| 18 | namespace { |
| 19 | |
| 20 | class TestFrameEvictionManagerClient : public FrameEvictionManagerClient { |
| 21 | public: |
| 22 | TestFrameEvictionManagerClient() = default; |
Benoit Lize | 6325f04 | 2022-09-12 10:11:35 | [diff] [blame] | 23 | explicit TestFrameEvictionManagerClient(FrameEvictionManager* manager) |
| 24 | : manager_(manager) {} |
Peter Boström | 09c0182 | 2021-09-20 22:43:27 | [diff] [blame] | 25 | |
| 26 | TestFrameEvictionManagerClient(const TestFrameEvictionManagerClient&) = |
| 27 | delete; |
| 28 | TestFrameEvictionManagerClient& operator=( |
| 29 | const TestFrameEvictionManagerClient&) = delete; |
| 30 | |
Benoit Lize | 6325f04 | 2022-09-12 10:11:35 | [diff] [blame] | 31 | ~TestFrameEvictionManagerClient() override { |
| 32 | if (has_frame_) |
| 33 | manager_->RemoveFrame(this); |
| 34 | } |
Xiyuan Xia | d7e4d94 | 2019-06-12 22:32:52 | [diff] [blame] | 35 | |
| 36 | // FrameEvictionManagerClient: |
| 37 | void EvictCurrentFrame() override { |
Benoit Lize | 6325f04 | 2022-09-12 10:11:35 | [diff] [blame] | 38 | manager_->RemoveFrame(this); |
Xiyuan Xia | d7e4d94 | 2019-06-12 22:32:52 | [diff] [blame] | 39 | has_frame_ = false; |
| 40 | } |
| 41 | |
| 42 | bool has_frame() const { return has_frame_; } |
| 43 | |
| 44 | private: |
Benoit Lize | 6325f04 | 2022-09-12 10:11:35 | [diff] [blame] | 45 | FrameEvictionManager* manager_ = FrameEvictionManager::GetInstance(); |
Xiyuan Xia | d7e4d94 | 2019-06-12 22:32:52 | [diff] [blame] | 46 | bool has_frame_ = true; |
Xiyuan Xia | d7e4d94 | 2019-06-12 22:32:52 | [diff] [blame] | 47 | }; |
| 48 | |
| 49 | } // namespace |
| 50 | |
Benoit Lize | 6325f04 | 2022-09-12 10:11:35 | [diff] [blame] | 51 | class FrameEvictionManagerTest : public testing::Test {}; |
Xiyuan Xia | d7e4d94 | 2019-06-12 22:32:52 | [diff] [blame] | 52 | |
| 53 | TEST_F(FrameEvictionManagerTest, ScopedPause) { |
| 54 | constexpr int kMaxSavedFrames = 1; |
| 55 | constexpr int kFrames = 2; |
| 56 | |
| 57 | FrameEvictionManager* manager = FrameEvictionManager::GetInstance(); |
| 58 | manager->set_max_number_of_saved_frames(kMaxSavedFrames); |
| 59 | |
| 60 | std::vector<TestFrameEvictionManagerClient> frames(kFrames); |
| 61 | { |
| 62 | FrameEvictionManager::ScopedPause scoped_pause; |
| 63 | |
| 64 | for (auto& frame : frames) |
| 65 | manager->AddFrame(&frame, /*locked=*/false); |
| 66 | |
| 67 | // All frames stays because |scoped_pause| holds off frame eviction. |
| 68 | EXPECT_EQ(kFrames, |
| 69 | std::count_if(frames.begin(), frames.end(), |
| 70 | [](const TestFrameEvictionManagerClient& frame) { |
| 71 | return frame.has_frame(); |
| 72 | })); |
| 73 | } |
| 74 | |
| 75 | // Frame eviction happens when |scoped_pause| goes out of scope. |
| 76 | EXPECT_EQ(kMaxSavedFrames, |
| 77 | std::count_if(frames.begin(), frames.end(), |
| 78 | [](const TestFrameEvictionManagerClient& frame) { |
| 79 | return frame.has_frame(); |
| 80 | })); |
| 81 | } |
| 82 | |
Benoit Lize | 6325f04 | 2022-09-12 10:11:35 | [diff] [blame] | 83 | TEST_F(FrameEvictionManagerTest, PeriodicCulling) { |
| 84 | base::test::ScopedFeatureList feature_list{features::kAggressiveFrameCulling}; |
| 85 | // Cannot use a TaskEnvironment as there is already one which is not using |
| 86 | // MOCK_TIME. |
| 87 | auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(); |
| 88 | FrameEvictionManager manager; |
| 89 | manager.set_max_number_of_saved_frames(5); |
| 90 | manager.SetOverridesForTesting(task_runner, task_runner->GetMockTickClock()); |
| 91 | |
| 92 | TestFrameEvictionManagerClient frame1{&manager}, frame2{&manager}, |
| 93 | frame3{&manager}; |
| 94 | manager.AddFrame(&frame1, false); |
| 95 | task_runner->FastForwardBy(FrameEvictionManager::kPeriodicCullingDelay / 10); |
| 96 | manager.AddFrame(&frame2, true); |
| 97 | manager.AddFrame(&frame3, false); |
| 98 | |
| 99 | task_runner->FastForwardBy(FrameEvictionManager::kPeriodicCullingDelay); |
| 100 | EXPECT_FALSE(frame1.has_frame()); |
| 101 | EXPECT_TRUE(frame2.has_frame()); |
| 102 | EXPECT_TRUE(frame3.has_frame()); // Too early for this one. |
| 103 | task_runner->FastForwardBy(FrameEvictionManager::kPeriodicCullingDelay); |
| 104 | EXPECT_FALSE(frame3.has_frame()); |
| 105 | |
| 106 | task_runner->FastForwardBy(FrameEvictionManager::kPeriodicCullingDelay / 2); |
| 107 | manager.UnlockFrame(&frame2); |
| 108 | EXPECT_TRUE(frame2.has_frame()); |
| 109 | |
| 110 | // Pause prevents eviction, but not rescheduling the task. Not using |
| 111 | // ScopedPause because it impacts the singleton. |
| 112 | manager.Pause(); |
| 113 | task_runner->FastForwardBy(FrameEvictionManager::kPeriodicCullingDelay / 2); |
| 114 | EXPECT_TRUE(frame2.has_frame()); |
| 115 | manager.Unpause(); |
| 116 | |
| 117 | task_runner->FastForwardBy(FrameEvictionManager::kPeriodicCullingDelay); |
| 118 | EXPECT_FALSE(frame2.has_frame()); |
| 119 | } |
| 120 | |
| 121 | TEST_F(FrameEvictionManagerTest, MemoryPressure) { |
| 122 | FrameEvictionManager* manager = FrameEvictionManager::GetInstance(); |
| 123 | |
| 124 | manager->set_max_number_of_saved_frames(5); |
| 125 | TestFrameEvictionManagerClient frame1, frame2; |
| 126 | manager->AddFrame(&frame1, false); |
| 127 | manager->AddFrame(&frame2, false); |
| 128 | |
| 129 | // We keep one frame around, no matter how many times we get a memory pressure |
| 130 | // notification. |
| 131 | manager->OnMemoryPressure( |
| 132 | base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); |
| 133 | EXPECT_FALSE(frame1.has_frame()); |
| 134 | EXPECT_TRUE(frame2.has_frame()); |
| 135 | |
| 136 | manager->OnMemoryPressure( |
| 137 | base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); |
| 138 | EXPECT_FALSE(frame1.has_frame()); |
| 139 | EXPECT_TRUE(frame2.has_frame()); |
| 140 | |
| 141 | // Unless aggressive frame culling is enabled. |
| 142 | base::test::ScopedFeatureList feature_list{features::kAggressiveFrameCulling}; |
| 143 | manager->OnMemoryPressure( |
| 144 | base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); |
| 145 | EXPECT_FALSE(frame1.has_frame()); |
| 146 | EXPECT_FALSE(frame2.has_frame()); |
| 147 | } |
| 148 | |
Xiyuan Xia | d7e4d94 | 2019-06-12 22:32:52 | [diff] [blame] | 149 | } // namespace viz |