1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_VIZ_CLIENT_FRAME_EVICTION_MANAGER_H_
#define COMPONENTS_VIZ_CLIENT_FRAME_EVICTION_MANAGER_H_
#include <stddef.h>
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <utility>
#include "base/gtest_prod_util.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/memory/post_delayed_memory_reduction_task.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/singleton.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/default_tick_clock.h"
#include "base/time/tick_clock.h"
#include "base/timer/timer.h"
#include "base/trace_event/memory_dump_provider.h"
#include "base/trace_event/memory_dump_request_args.h"
#include "components/viz/client/viz_client_export.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/pre_freeze_background_memory_trimmer.h"
#endif
namespace viz {
class FrameEvictionManagerClient {
public:
virtual ~FrameEvictionManagerClient() = default;
virtual void EvictCurrentFrame() = 0;
};
// This class is responsible for globally managing which renderers keep their
// compositor frame when offscreen. We actively discard compositor frames for
// offscreen tabs, but keep a minimum amount, as an LRU cache, to make switching
// between a small set of tabs faster. The limit is a soft limit, because
// clients can lock their frame to prevent it from being discarded, e.g. if the
// tab is visible, or while capturing a screenshot.
class VIZ_CLIENT_EXPORT FrameEvictionManager
: public base::trace_event::MemoryDumpProvider {
public:
// Pauses frame eviction within its scope.
class VIZ_CLIENT_EXPORT ScopedPause {
public:
ScopedPause();
ScopedPause(const ScopedPause&) = delete;
ScopedPause& operator=(const ScopedPause&) = delete;
~ScopedPause();
};
static FrameEvictionManager* GetInstance();
FrameEvictionManager(const FrameEvictionManager&) = delete;
FrameEvictionManager& operator=(const FrameEvictionManager&) = delete;
void AddFrame(FrameEvictionManagerClient*, bool locked);
void RemoveFrame(FrameEvictionManagerClient*);
void LockFrame(FrameEvictionManagerClient*);
void UnlockFrame(FrameEvictionManagerClient*);
size_t GetMaxNumberOfSavedFrames() const;
// For testing only
void set_max_number_of_saved_frames(size_t max_number_of_saved_frames) {
max_number_of_saved_frames_ = max_number_of_saved_frames;
}
// React on memory pressure events to adjust the number of cached frames.
// Please make this private when crbug.com/443824 has been fixed.
void OnMemoryPressure(
base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
// Purges all unlocked frames, allowing us to reclaim resources.
void PurgeAllUnlockedFrames();
// Chosen arbitrarily, didn't show regressions in metrics during a field trial
// in 2023. Should ideally be higher than a common time to switch between
// tabs. The reasoning is that if a tab isn't switched to in this delay, then
// it's unlikeky to soon be.
static constexpr base::TimeDelta kPeriodicCullingDelay = base::Minutes(5);
private:
friend struct base::DefaultSingletonTraits<FrameEvictionManager>;
FRIEND_TEST_ALL_PREFIXES(FrameEvictionManagerTest, PeriodicCulling);
FrameEvictionManager();
~FrameEvictionManager() override;
void StartFrameCullingTimer();
void CullUnlockedFrames(size_t saved_frame_limit);
#if BUILDFLAG(IS_ANDROID)
void CullOldUnlockedFrames(base::MemoryReductionTaskContext task_type);
#else
void CullOldUnlockedFrames();
#endif
void PurgeMemory(int percentage);
// Pauses/unpauses frame eviction.
void Pause();
void Unpause();
void RegisterUnlockedFrame(FrameEvictionManagerClient* frame);
// Inject mock versions for testing.
void SetOverridesForTesting(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
const base::TickClock* clock);
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override;
// Listens for system under pressure notifications and adjusts number of
// cached frames accordingly.
std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
std::map<FrameEvictionManagerClient*, size_t> locked_frames_;
// {FrameEvictionManagerClient, Last Unlock() time}, ordered with the most
// recent first.
std::list<std::pair<FrameEvictionManagerClient*, base::TimeTicks>>
unlocked_frames_;
size_t max_number_of_saved_frames_;
// Counter of the outstanding pauses.
int pause_count_ = 0;
// Argument of the last CullUnlockedFrames call while paused.
std::optional<size_t> pending_unlocked_frame_limit_;
base::OneShotDelayedBackgroundTimer idle_frame_culling_timer_;
raw_ptr<const base::TickClock> clock_ = base::DefaultTickClock::GetInstance();
};
} // namespace viz
#endif // COMPONENTS_VIZ_CLIENT_FRAME_EVICTION_MANAGER_H_
|