power_mode_voter.cc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. // Copyright 2021 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/power_scheduler/power_mode_voter.h"
  5. #include <limits>
  6. #include "base/command_line.h"
  7. #include "base/metrics/histogram_macros.h"
  8. #include "components/power_scheduler/power_mode_arbiter.h"
  9. #include "ui/gfx/geometry/rect.h"
  10. #include "ui/gfx/geometry/vector2d.h"
  11. namespace power_scheduler {
  12. namespace {
  13. bool IsBrowserProcess() {
  14. return base::CommandLine::ForCurrentProcess()
  15. ->GetSwitchValueASCII("type")
  16. .empty();
  17. }
  18. const char* GetFrameDamageAreaHistogramName() {
  19. return IsBrowserProcess() ? "Power.FrameDamageAreaInDip.Browser"
  20. : "Power.FrameDamageAreaInDip.Renderer";
  21. }
  22. const char* GetFrameDamageDiagonalHistogramName() {
  23. return IsBrowserProcess() ? "Power.FrameDamageDiagonalInDip.Browser"
  24. : "Power.FrameDamageDiagonalInDip.Renderer";
  25. }
  26. } // namespace
  27. PowerModeVoter::Delegate::~Delegate() = default;
  28. PowerModeVoter::PowerModeVoter(Delegate* delegate) : delegate_(delegate) {}
  29. // static
  30. constexpr base::TimeDelta PowerModeVoter::kResponseTimeout;
  31. // static
  32. constexpr base::TimeDelta PowerModeVoter::kAnimationTimeout;
  33. // static
  34. constexpr base::TimeDelta PowerModeVoter::kVideoTimeout;
  35. // static
  36. constexpr base::TimeDelta PowerModeVoter::kLoadingTimeout;
  37. // static
  38. constexpr base::TimeDelta PowerModeVoter::kStuckLoadingTimeout;
  39. // static
  40. constexpr base::TimeDelta PowerModeVoter::kScriptExecutionTimeout;
  41. PowerModeVoter::~PowerModeVoter() {
  42. delegate_->OnVoterDestroyed(this);
  43. }
  44. void PowerModeVoter::VoteFor(PowerMode mode) {
  45. delegate_->SetVote(this, mode);
  46. }
  47. void PowerModeVoter::ResetVoteAfterTimeout(base::TimeDelta timeout) {
  48. delegate_->ResetVoteAfterTimeout(this, timeout);
  49. }
  50. // static
  51. constexpr int FrameProductionPowerModeVoter::kMinFramesSkippedForIdleAnimation;
  52. FrameProductionPowerModeVoter::FrameProductionPowerModeVoter(const char* name)
  53. : FrameProductionPowerModeVoter(name, PowerModeArbiter::GetInstance()) {}
  54. FrameProductionPowerModeVoter::FrameProductionPowerModeVoter(
  55. const char* name,
  56. PowerModeArbiter* arbiter)
  57. : voter_(arbiter->NewVoter(name)) {}
  58. FrameProductionPowerModeVoter::~FrameProductionPowerModeVoter() = default;
  59. void FrameProductionPowerModeVoter::OnNeedsBeginFramesChanged(
  60. bool needs_begin_frames) {
  61. needs_begin_frames_ = needs_begin_frames;
  62. if (needs_begin_frames) {
  63. in_nop_animation_ = false;
  64. consecutive_frames_skipped_ = 0;
  65. // Start out with a 2 x max frame damage history. Thus, we will require
  66. // |kNumDamageAreas-1| frames with lower damage area before voting for
  67. // small/medium animation modes.
  68. last_damage_areas_ = {std::numeric_limits<int>::max(),
  69. std::numeric_limits<int>::max()};
  70. voter_->VoteFor(PowerMode::kAnimation);
  71. } else {
  72. voter_->ResetVoteAfterTimeout(PowerModeVoter::kAnimationTimeout);
  73. }
  74. }
  75. void FrameProductionPowerModeVoter::OnFrameProduced(
  76. const gfx::Rect& damage_rect,
  77. float device_scale_factor) {
  78. // Only consider produced frames when we still need BeginFrames.
  79. if (!needs_begin_frames_)
  80. return;
  81. // In DIP. Assuming 160 dip/inch, one square inch is 160*160 = 25600 dip. A
  82. // typical 6 inch screen has about 13.5 square inch area. We'll define
  83. // animations up to 3 square inch as "small" animation and 6 as "medium".
  84. static constexpr int kMaxDamageAreaForSmallAnimation = 3 * 160 * 160;
  85. static constexpr int kMaxDamageAreaForMediumAnimation = 6 * 160 * 160;
  86. PowerMode vote = PowerMode::kAnimation;
  87. gfx::Size damage_rect_in_dip;
  88. if (device_scale_factor > 0) {
  89. float inverse_dsf = 1 / device_scale_factor;
  90. damage_rect_in_dip =
  91. gfx::ScaleToCeiledSize(damage_rect.size(), inverse_dsf);
  92. }
  93. if (!damage_rect_in_dip.IsEmpty()) {
  94. int damage_area_in_dip = damage_rect_in_dip.GetCheckedArea().ValueOrDefault(
  95. std::numeric_limits<int>::max());
  96. int damage_diagonal_in_dip = std::ceil(
  97. gfx::Vector2d(damage_rect_in_dip.width(), damage_rect_in_dip.height())
  98. .Length());
  99. UMA_HISTOGRAM_COUNTS_10M(GetFrameDamageAreaHistogramName(),
  100. damage_area_in_dip);
  101. UMA_HISTOGRAM_COUNTS_10000(GetFrameDamageDiagonalHistogramName(),
  102. damage_diagonal_in_dip);
  103. // Record the area into last_damage_areas_ after shifting existing values.
  104. for (int i = kNumDamageAreas - 1; i >= 1; i--)
  105. last_damage_areas_[i] = last_damage_areas_[i - 1];
  106. last_damage_areas_[0] = damage_area_in_dip;
  107. // Determine the second largest area damaged over the last kNumDamageAreas
  108. // frames, ignoring the maximum. This way, we ignore one-off frames that
  109. // damage a large area but are submitted in between a sequence of smaller
  110. // animation frames.
  111. int max_damage_area = 0;
  112. int second_max_damage_area = 0;
  113. for (int damage_area : last_damage_areas_) {
  114. if (damage_area > max_damage_area) {
  115. second_max_damage_area = max_damage_area;
  116. max_damage_area = damage_area;
  117. } else if (damage_area > second_max_damage_area) {
  118. second_max_damage_area = damage_area;
  119. }
  120. }
  121. if (second_max_damage_area <= kMaxDamageAreaForSmallAnimation) {
  122. vote = PowerMode::kSmallAnimation;
  123. } else if (second_max_damage_area <= kMaxDamageAreaForMediumAnimation) {
  124. vote = PowerMode::kMediumAnimation;
  125. }
  126. }
  127. consecutive_frames_skipped_ = 0;
  128. // If we were in no-op mode, only go back into an animation mode if there were
  129. // at least two frames produced within kAnimationTimeout.
  130. base::TimeTicks now = base::TimeTicks::Now();
  131. if (in_nop_animation_ &&
  132. last_frame_produced_timestamp_ + PowerModeVoter::kAnimationTimeout <
  133. now) {
  134. last_frame_produced_timestamp_ = now;
  135. return;
  136. }
  137. in_nop_animation_ = false;
  138. last_frame_produced_timestamp_ = now;
  139. voter_->VoteFor(vote);
  140. }
  141. void FrameProductionPowerModeVoter::OnFrameSkipped(bool frame_completed,
  142. bool waiting_on_main) {
  143. // Only consider skipped frames when we still need BeginFrames.
  144. if (!needs_begin_frames_)
  145. return;
  146. // Ignore frames that are skipped in an incomplete state, e.g. because frame
  147. // production took too long and the deadline was missed. Such frames should
  148. // not count as "no-op", because frame production may still be in progress.
  149. // However, if we were only waiting on the main thread, we will treat this as
  150. // no-op here, because we cannot distinguish aborted BeginMainFrame sequences
  151. // from "long" content-producing BeginMainFrames here. Instead, a separate
  152. // PowerModeVoter tracks BeginMainFrame production in cc::Scheduler.
  153. if (!frame_completed && !waiting_on_main)
  154. return;
  155. if (consecutive_frames_skipped_ < kMinFramesSkippedForIdleAnimation - 1) {
  156. consecutive_frames_skipped_++;
  157. return;
  158. }
  159. in_nop_animation_ = true;
  160. last_frame_produced_timestamp_ = base::TimeTicks();
  161. voter_->VoteFor(PowerMode::kNopAnimation);
  162. }
  163. void FrameProductionPowerModeVoter::OnFrameTimeout() {
  164. in_nop_animation_ = true;
  165. voter_->VoteFor(PowerMode::kNopAnimation);
  166. }
  167. DebouncedPowerModeVoter::DebouncedPowerModeVoter(const char* name,
  168. base::TimeDelta timeout)
  169. : voter_(PowerModeArbiter::GetInstance()->NewVoter(name)),
  170. timeout_(timeout) {}
  171. DebouncedPowerModeVoter::~DebouncedPowerModeVoter() = default;
  172. void DebouncedPowerModeVoter::VoteFor(PowerMode vote) {
  173. base::TimeTicks now = base::TimeTicks::Now();
  174. if (!last_vote_ || vote != *last_vote_ ||
  175. last_vote_timestamp_ + timeout_ < now) {
  176. last_vote_ = vote;
  177. last_vote_timestamp_ = now;
  178. return;
  179. }
  180. DCHECK_EQ(*last_vote_, vote);
  181. last_vote_timestamp_ = now;
  182. voter_->VoteFor(vote);
  183. }
  184. } // namespace power_scheduler