frame_eviction_manager.cc 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/viz/client/frame_eviction_manager.h"
  5. #include <algorithm>
  6. #include "base/bind.h"
  7. #include "base/check_op.h"
  8. #include "base/containers/contains.h"
  9. #include "base/memory/memory_pressure_listener.h"
  10. #include "base/memory/memory_pressure_monitor.h"
  11. #include "base/system/sys_info.h"
  12. #include "build/build_config.h"
  13. namespace viz {
  14. namespace {
  15. const int kModeratePressurePercentage = 50;
  16. const int kCriticalPressurePercentage = 10;
  17. } // namespace
  18. FrameEvictionManager::ScopedPause::ScopedPause() {
  19. FrameEvictionManager::GetInstance()->Pause();
  20. }
  21. FrameEvictionManager::ScopedPause::~ScopedPause() {
  22. FrameEvictionManager::GetInstance()->Unpause();
  23. }
  24. FrameEvictionManager* FrameEvictionManager::GetInstance() {
  25. return base::Singleton<FrameEvictionManager>::get();
  26. }
  27. FrameEvictionManager::~FrameEvictionManager() = default;
  28. void FrameEvictionManager::AddFrame(FrameEvictionManagerClient* frame,
  29. bool locked) {
  30. RemoveFrame(frame);
  31. if (locked)
  32. locked_frames_[frame] = 1;
  33. else
  34. unlocked_frames_.push_front(frame);
  35. CullUnlockedFrames(GetMaxNumberOfSavedFrames());
  36. }
  37. void FrameEvictionManager::RemoveFrame(FrameEvictionManagerClient* frame) {
  38. auto locked_iter = locked_frames_.find(frame);
  39. if (locked_iter != locked_frames_.end())
  40. locked_frames_.erase(locked_iter);
  41. unlocked_frames_.remove(frame);
  42. }
  43. void FrameEvictionManager::LockFrame(FrameEvictionManagerClient* frame) {
  44. if (base::Contains(unlocked_frames_, frame)) {
  45. DCHECK(locked_frames_.find(frame) == locked_frames_.end());
  46. unlocked_frames_.remove(frame);
  47. locked_frames_[frame] = 1;
  48. } else {
  49. DCHECK(locked_frames_.find(frame) != locked_frames_.end());
  50. locked_frames_[frame]++;
  51. }
  52. }
  53. void FrameEvictionManager::UnlockFrame(FrameEvictionManagerClient* frame) {
  54. DCHECK(locked_frames_.find(frame) != locked_frames_.end());
  55. size_t locked_count = locked_frames_[frame];
  56. DCHECK(locked_count);
  57. if (locked_count > 1) {
  58. locked_frames_[frame]--;
  59. } else {
  60. RemoveFrame(frame);
  61. unlocked_frames_.push_front(frame);
  62. CullUnlockedFrames(GetMaxNumberOfSavedFrames());
  63. }
  64. }
  65. size_t FrameEvictionManager::GetMaxNumberOfSavedFrames() const {
  66. int percentage = 100;
  67. base::MemoryPressureMonitor* monitor = base::MemoryPressureMonitor::Get();
  68. if (!monitor)
  69. return max_number_of_saved_frames_;
  70. // Until we have a global OnMemoryPressureChanged event we need to query the
  71. // value from our specific pressure monitor.
  72. switch (monitor->GetCurrentPressureLevel()) {
  73. case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
  74. percentage = 100;
  75. break;
  76. case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
  77. percentage = kModeratePressurePercentage;
  78. break;
  79. case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
  80. percentage = kCriticalPressurePercentage;
  81. break;
  82. }
  83. size_t frames = (max_number_of_saved_frames_ * percentage) / 100;
  84. return std::max(static_cast<size_t>(1), frames);
  85. }
  86. FrameEvictionManager::FrameEvictionManager()
  87. : memory_pressure_listener_(new base::MemoryPressureListener(
  88. FROM_HERE,
  89. base::BindRepeating(&FrameEvictionManager::OnMemoryPressure,
  90. base::Unretained(this)))) {
  91. max_number_of_saved_frames_ =
  92. #if BUILDFLAG(IS_ANDROID)
  93. // If the amount of memory on the device is >= 3.5 GB, save up to 5
  94. // frames.
  95. base::SysInfo::AmountOfPhysicalMemoryMB() < 1024 * 3.5f ? 1 : 5;
  96. #else
  97. std::min(5, 2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256));
  98. #endif
  99. }
  100. void FrameEvictionManager::CullUnlockedFrames(size_t saved_frame_limit) {
  101. if (pause_count_) {
  102. pending_unlocked_frame_limit_ = saved_frame_limit;
  103. return;
  104. }
  105. while (!unlocked_frames_.empty() &&
  106. unlocked_frames_.size() + locked_frames_.size() > saved_frame_limit) {
  107. size_t old_size = unlocked_frames_.size();
  108. // Should remove self from list.
  109. unlocked_frames_.back()->EvictCurrentFrame();
  110. if (unlocked_frames_.size() == old_size)
  111. break;
  112. }
  113. }
  114. void FrameEvictionManager::OnMemoryPressure(
  115. base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
  116. switch (memory_pressure_level) {
  117. case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
  118. PurgeMemory(kModeratePressurePercentage);
  119. break;
  120. case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
  121. PurgeMemory(kCriticalPressurePercentage);
  122. break;
  123. case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
  124. // No need to change anything when there is no pressure.
  125. return;
  126. }
  127. }
  128. void FrameEvictionManager::PurgeMemory(int percentage) {
  129. int saved_frame_limit = max_number_of_saved_frames_;
  130. if (saved_frame_limit <= 1)
  131. return;
  132. CullUnlockedFrames(std::max(1, (saved_frame_limit * percentage) / 100));
  133. }
  134. void FrameEvictionManager::PurgeAllUnlockedFrames() {
  135. CullUnlockedFrames(0);
  136. }
  137. void FrameEvictionManager::Pause() {
  138. ++pause_count_;
  139. }
  140. void FrameEvictionManager::Unpause() {
  141. --pause_count_;
  142. DCHECK_GE(pause_count_, 0);
  143. if (pause_count_ == 0 && pending_unlocked_frame_limit_) {
  144. CullUnlockedFrames(pending_unlocked_frame_limit_.value());
  145. pending_unlocked_frame_limit_.reset();
  146. }
  147. }
  148. } // namespace viz