frame_throttling_controller.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. // Copyright 2020 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 "ash/frame_throttler/frame_throttling_controller.h"
  5. #include <utility>
  6. #include "ash/constants/app_types.h"
  7. #include "ash/constants/ash_switches.h"
  8. #include "ash/public/cpp/window_properties.h"
  9. #include "ash/shell.h"
  10. #include "ash/wm/mru_window_tracker.h"
  11. #include "base/command_line.h"
  12. #include "base/strings/string_number_conversions.h"
  13. #include "components/viz/common/surfaces/frame_sink_id.h"
  14. #include "components/viz/host/host_frame_sink_manager.h"
  15. #include "ui/aura/client/aura_constants.h"
  16. #include "ui/aura/window.h"
  17. #include "ui/aura/window_tree_host.h"
  18. #include "ui/compositor/compositor.h"
  19. namespace ash {
  20. namespace {
  21. void CollectFrameSinkIds(const aura::Window* window,
  22. base::flat_set<viz::FrameSinkId>* frame_sink_ids) {
  23. if (window->GetFrameSinkId().is_valid()) {
  24. frame_sink_ids->insert(window->GetFrameSinkId());
  25. return;
  26. }
  27. for (auto* child : window->children()) {
  28. CollectFrameSinkIds(child, frame_sink_ids);
  29. }
  30. }
  31. // Recursively walks through all descendents of |window| and collects those
  32. // belonging to a browser and with their frame sink ids being a member of |ids|.
  33. // |inside_browser| indicates if the |window| already belongs to a browser.
  34. // |frame_sink_ids| is the output containing the result frame sinks ids.
  35. void CollectBrowserFrameSinkIdsInWindow(
  36. const aura::Window* window,
  37. bool inside_browser,
  38. const base::flat_set<viz::FrameSinkId>& ids,
  39. base::flat_set<viz::FrameSinkId>* frame_sink_ids) {
  40. if (inside_browser || ash::AppType::BROWSER ==
  41. static_cast<ash::AppType>(
  42. window->GetProperty(aura::client::kAppType))) {
  43. const auto& id = window->GetFrameSinkId();
  44. if (id.is_valid() && ids.contains(id))
  45. frame_sink_ids->insert(id);
  46. inside_browser = true;
  47. }
  48. for (auto* child : window->children()) {
  49. CollectBrowserFrameSinkIdsInWindow(child, inside_browser, ids,
  50. frame_sink_ids);
  51. }
  52. }
  53. } // namespace
  54. ThrottleCandidates::ThrottleCandidates() = default;
  55. ThrottleCandidates::~ThrottleCandidates() = default;
  56. ThrottleCandidates::ThrottleCandidates(const ThrottleCandidates&) = default;
  57. ThrottleCandidates& ThrottleCandidates::operator=(const ThrottleCandidates&) =
  58. default;
  59. bool ThrottleCandidates::IsEmpty() const {
  60. return browser_frame_sink_ids.empty() && lacros_candidates.empty();
  61. }
  62. void FrameThrottlingController::ResetThrottleCandidates(
  63. ThrottleCandidates* candidates) {
  64. candidates->browser_frame_sink_ids.clear();
  65. for (auto& lacros_candidate : candidates->lacros_candidates) {
  66. // Reset the window property for frame rate throttling.
  67. lacros_candidate.first->SetProperty(ash::kFrameRateThrottleKey, false);
  68. }
  69. candidates->lacros_candidates.clear();
  70. }
  71. FrameThrottlingController::FrameThrottlingController(
  72. ui::ContextFactory* context_factory)
  73. : context_factory_(context_factory) {
  74. const base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
  75. if (cl->HasSwitch(switches::kFrameThrottleFps)) {
  76. int value;
  77. if (base::StringToInt(cl->GetSwitchValueASCII(switches::kFrameThrottleFps),
  78. &value)) {
  79. throttled_fps_ = value;
  80. }
  81. }
  82. }
  83. FrameThrottlingController::~FrameThrottlingController() {
  84. EndThrottling();
  85. }
  86. void FrameThrottlingController::StartThrottling(
  87. const std::vector<aura::Window*>& windows) {
  88. if (windows_manually_throttled_)
  89. EndThrottling();
  90. if (windows.empty())
  91. return;
  92. auto all_windows =
  93. Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk);
  94. std::vector<aura::Window*> all_arc_windows;
  95. std::copy_if(all_windows.begin(), all_windows.end(),
  96. std::back_inserter(all_arc_windows), [](aura::Window* window) {
  97. return ash::AppType::ARC_APP ==
  98. static_cast<ash::AppType>(
  99. window->GetProperty(aura::client::kAppType));
  100. });
  101. std::vector<aura::Window*> arc_windows;
  102. arc_windows.reserve(windows.size());
  103. for (auto* window : windows) {
  104. ash::AppType type =
  105. static_cast<ash::AppType>(window->GetProperty(aura::client::kAppType));
  106. switch (type) {
  107. case ash::AppType::BROWSER:
  108. CollectFrameSinkIds(
  109. window, &manually_throttled_candidates_.browser_frame_sink_ids);
  110. break;
  111. case ash::AppType::ARC_APP:
  112. arc_windows.push_back(window);
  113. break;
  114. case ash::AppType::LACROS:
  115. CollectLacrosCandidates(
  116. window, &manually_throttled_candidates_.lacros_candidates, window);
  117. break;
  118. default:
  119. break;
  120. }
  121. }
  122. // Throttle browser and lacros windows.
  123. if (!manually_throttled_candidates_.IsEmpty()) {
  124. UpdateThrottlingOnFrameSinks();
  125. for (const auto& lacros_candidate :
  126. manually_throttled_candidates_.lacros_candidates) {
  127. lacros_candidate.first->SetProperty(ash::kFrameRateThrottleKey, true);
  128. }
  129. windows_manually_throttled_ = true;
  130. }
  131. // Do not throttle arc if at least one arc window should not be throttled.
  132. if (!arc_windows.empty() && (arc_windows.size() == all_arc_windows.size())) {
  133. StartThrottlingArc(arc_windows);
  134. windows_manually_throttled_ = true;
  135. }
  136. }
  137. void FrameThrottlingController::StartThrottlingArc(
  138. const std::vector<aura::Window*>& arc_windows) {
  139. for (auto& arc_observer : arc_observers_) {
  140. arc_observer.OnThrottlingStarted(arc_windows, throttled_fps_);
  141. }
  142. }
  143. void FrameThrottlingController::EndThrottlingArc() {
  144. for (auto& arc_observer : arc_observers_) {
  145. arc_observer.OnThrottlingEnded();
  146. }
  147. }
  148. void FrameThrottlingController::EndThrottling() {
  149. if (windows_manually_throttled_) {
  150. ResetThrottleCandidates(&manually_throttled_candidates_);
  151. UpdateThrottlingOnFrameSinks();
  152. EndThrottlingArc();
  153. windows_manually_throttled_ = false;
  154. }
  155. }
  156. void FrameThrottlingController::OnCompositingFrameSinksToThrottleUpdated(
  157. const aura::WindowTreeHost* window_tree_host,
  158. const base::flat_set<viz::FrameSinkId>& ids) {
  159. ThrottleCandidates& candidates = host_to_candidates_map_[window_tree_host];
  160. ResetThrottleCandidates(&candidates);
  161. const aura::Window* window = window_tree_host->window();
  162. CollectBrowserFrameSinkIdsInWindow(window, false, ids,
  163. &candidates.browser_frame_sink_ids);
  164. CollectLacrosWindowsInWindow(const_cast<aura::Window*>(window), false, ids,
  165. &candidates.lacros_candidates);
  166. UpdateThrottlingOnFrameSinks();
  167. for (auto& lacros_candidate : candidates.lacros_candidates)
  168. lacros_candidate.first->SetProperty(ash::kFrameRateThrottleKey, true);
  169. }
  170. void FrameThrottlingController::OnWindowDestroying(aura::Window* window) {
  171. if (window->IsRootWindow()) {
  172. auto it = host_to_candidates_map_.find(window->GetHost());
  173. if (it != host_to_candidates_map_.end()) {
  174. for (auto& lacros_candidate : it->second.lacros_candidates) {
  175. lacros_candidate.first->SetProperty(ash::kFrameRateThrottleKey, false);
  176. lacros_candidate.first->RemoveObserver(this);
  177. }
  178. host_to_candidates_map_.erase(it);
  179. UpdateThrottlingOnFrameSinks();
  180. }
  181. } else {
  182. bool window_removed = false;
  183. for (auto& host_to_candidates : host_to_candidates_map_) {
  184. auto& lacros_candidates = host_to_candidates.second.lacros_candidates;
  185. auto it = lacros_candidates.find(window);
  186. if (it != lacros_candidates.end()) {
  187. window_removed = true;
  188. lacros_candidates.erase(it);
  189. }
  190. }
  191. auto& manually_throttled_lacros_candidates =
  192. manually_throttled_candidates_.lacros_candidates;
  193. auto it = manually_throttled_lacros_candidates.find(window);
  194. if (it != manually_throttled_lacros_candidates.end()) {
  195. window_removed = true;
  196. manually_throttled_lacros_candidates.erase(it);
  197. }
  198. if (window_removed)
  199. UpdateThrottlingOnFrameSinks();
  200. }
  201. window->RemoveObserver(this);
  202. }
  203. void FrameThrottlingController::OnWindowTreeHostCreated(
  204. aura::WindowTreeHost* host) {
  205. host->AddObserver(this);
  206. host->window()->AddObserver(this);
  207. }
  208. std::vector<viz::FrameSinkId>
  209. FrameThrottlingController::GetFrameSinkIdsToThrottle() const {
  210. std::vector<viz::FrameSinkId> ids_to_throttle;
  211. ids_to_throttle.reserve(host_to_candidates_map_.size() * 2);
  212. // Add frame sink ids gathered from compositing.
  213. for (const auto& pair : host_to_candidates_map_) {
  214. ids_to_throttle.insert(ids_to_throttle.end(),
  215. pair.second.browser_frame_sink_ids.begin(),
  216. pair.second.browser_frame_sink_ids.end());
  217. // insert the frame sink ids for lacros windows.
  218. for (const auto& candidate : pair.second.lacros_candidates) {
  219. ids_to_throttle.push_back(candidate.second);
  220. }
  221. }
  222. // Add frame sink ids from special ui modes.
  223. if (!manually_throttled_candidates_.IsEmpty()) {
  224. ids_to_throttle.insert(
  225. ids_to_throttle.end(),
  226. manually_throttled_candidates_.browser_frame_sink_ids.begin(),
  227. manually_throttled_candidates_.browser_frame_sink_ids.end());
  228. for (const auto& lacros_candidate :
  229. manually_throttled_candidates_.lacros_candidates) {
  230. ids_to_throttle.push_back(lacros_candidate.second);
  231. }
  232. }
  233. return ids_to_throttle;
  234. }
  235. void FrameThrottlingController::UpdateThrottlingOnFrameSinks() {
  236. context_factory_->GetHostFrameSinkManager()->Throttle(
  237. GetFrameSinkIdsToThrottle(), base::Hertz(throttled_fps_));
  238. }
  239. void FrameThrottlingController::AddArcObserver(
  240. FrameThrottlingObserver* observer) {
  241. arc_observers_.AddObserver(observer);
  242. }
  243. void FrameThrottlingController::RemoveArcObserver(
  244. FrameThrottlingObserver* observer) {
  245. arc_observers_.RemoveObserver(observer);
  246. }
  247. bool FrameThrottlingController::HasArcObserver(
  248. FrameThrottlingObserver* observer) {
  249. return arc_observers_.HasObserver(observer);
  250. }
  251. void FrameThrottlingController::CollectLacrosWindowsInWindow(
  252. aura::Window* window,
  253. bool inside_lacros,
  254. const base::flat_set<viz::FrameSinkId>& ids,
  255. base::flat_map<aura::Window*, viz::FrameSinkId>* candidates,
  256. aura::Window* lacros_window) {
  257. if (ash::AppType::LACROS ==
  258. static_cast<ash::AppType>(window->GetProperty(aura::client::kAppType))) {
  259. DCHECK(!lacros_window);
  260. lacros_window = window;
  261. inside_lacros = true;
  262. }
  263. if (inside_lacros) {
  264. const auto& id = window->GetFrameSinkId();
  265. if (id.is_valid() && ids.contains(id)) {
  266. DCHECK(lacros_window);
  267. candidates->insert(std::make_pair(lacros_window, id));
  268. if (!lacros_window->HasObserver(this))
  269. lacros_window->AddObserver(this);
  270. return;
  271. }
  272. }
  273. for (auto* child : window->children()) {
  274. CollectLacrosWindowsInWindow(child, inside_lacros, ids, candidates,
  275. lacros_window);
  276. }
  277. }
  278. void FrameThrottlingController::CollectLacrosCandidates(
  279. aura::Window* window,
  280. base::flat_map<aura::Window*, viz::FrameSinkId>* candidates,
  281. aura::Window* lacros_window) {
  282. const auto& id = window->GetFrameSinkId();
  283. if (id.is_valid()) {
  284. DCHECK(lacros_window);
  285. candidates->insert(std::make_pair(lacros_window, id));
  286. if (!lacros_window->HasObserver(this))
  287. lacros_window->AddObserver(this);
  288. return;
  289. }
  290. for (auto* child : window->children())
  291. CollectLacrosCandidates(child, candidates, lacros_window);
  292. }
  293. } // namespace ash