blob: 5d7f07dda8416143b2142eef34861eefcca1bcfd [file] [log] [blame]
// Copyright 2021 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_POWER_SCHEDULER_POWER_MODE_VOTER_H_
#define COMPONENTS_POWER_SCHEDULER_POWER_MODE_VOTER_H_
#include <array>
#include <limits>
#include <memory>
#include "base/component_export.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "components/power_scheduler/power_mode.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/geometry/rect.h"
namespace power_scheduler {
class PowerModeArbiter;
// PowerModeVoters should be instantiated at instrumentation points in Chromium
// via PowerModeArbiter::GetInstance()->NewVoter("MyVoter") to vote on a
// process's PowerMode.
class COMPONENT_EXPORT(POWER_SCHEDULER) PowerModeVoter {
public:
class Delegate {
public:
virtual ~Delegate();
virtual void OnVoterDestroyed(PowerModeVoter*) = 0;
virtual void SetVote(PowerModeVoter*, PowerMode) = 0;
virtual void ResetVoteAfterTimeout(PowerModeVoter*,
base::TimeDelta timeout) = 0;
};
// Consider an initial response to a single input to last 100ms.
static constexpr base::TimeDelta kResponseTimeout = base::Milliseconds(100);
// Animations often have brief idle periods where no frames are produced. This
// timeout is applied before resetting animation votes to avoid frequent vote
// reversals.
static constexpr base::TimeDelta kAnimationTimeout = base::Milliseconds(100);
static constexpr base::TimeDelta kVideoTimeout = kAnimationTimeout;
// Give frames an extra second to draw & settle after load completion.
static constexpr base::TimeDelta kLoadingTimeout = base::Seconds(1);
// Avoid getting stuck in loading stage forever. More than 99.9% of
// navigations load (to largest contentful paint) in less than a minute.
static constexpr base::TimeDelta kStuckLoadingTimeout = base::Seconds(60);
// This timeout is applied before resetting script execution votes to avoid
// frequent vote reversals.
static constexpr base::TimeDelta kScriptExecutionTimeout =
base::Milliseconds(50);
~PowerModeVoter();
PowerModeVoter(const PowerModeVoter&) = delete;
PowerModeVoter& operator=(const PowerModeVoter&) = delete;
// Set the voter's vote to the provided PowerMode. Cancels any previously
// scheduled ResetVoteAfterTimeout().
void VoteFor(PowerMode);
// Resets the vote to the PowerMode::kIdle after |timeout|, provided VoteFor()
// is not called again before then.
void ResetVoteAfterTimeout(base::TimeDelta timeout);
private:
friend class PowerModeArbiter;
explicit PowerModeVoter(Delegate* delegate);
raw_ptr<Delegate> delegate_;
};
// Tracks the BeginFrame signal as well as produced and skipped frames to vote
// either for the kAnimation, kNopAnimation, or kIdle modes.
class COMPONENT_EXPORT(POWER_SCHEDULER) FrameProductionPowerModeVoter {
public:
explicit FrameProductionPowerModeVoter(const char* name);
~FrameProductionPowerModeVoter();
FrameProductionPowerModeVoter(const FrameProductionPowerModeVoter&) = delete;
FrameProductionPowerModeVoter& operator=(
const FrameProductionPowerModeVoter&) = delete;
// Should be called when starting or stoping observing BeginFrames.
void OnNeedsBeginFramesChanged(bool needs_begin_frames);
// Should be called when a frame is produced.
void OnFrameProduced(const gfx::Rect& damage_rect, float device_scale_factor);
// Should be called when a frame is skipped. |frame_completed| should be true
// if the frame production resulted in no visible updates and was completed on
// time. In other cases (e.g. if the deadline was missed and frame production
// continues for the next vsync), it should be false. |waiting_on_main| should
// be true if the frame was not completed because the main thread's frame
// production was not finished on time for the deadline.
void OnFrameSkipped(bool frame_completed, bool waiting_on_main);
// Should be called when BeginFrame was not followed by a draw within a set
// timeframe.
void OnFrameTimeout();
private:
friend class PowerModeArbiterFrameProductionTest;
// For testing.
FrameProductionPowerModeVoter(const char* name, PowerModeArbiter*);
// 10 Frames: 166ms on 60fps, 111ms on 90fps, 83ms on 120fps. This should be a
// reasonable compromise to avoid frequent flip-flopping between different
// animation modes.
static constexpr int kNumDamageAreas = 10;
static constexpr int kMinFramesSkippedForIdleAnimation = 10;
std::unique_ptr<PowerModeVoter> voter_;
bool in_nop_animation_ = false;
int consecutive_frames_skipped_ = 0;
base::TimeTicks last_frame_produced_timestamp_;
bool needs_begin_frames_ = false;
std::array<int, kNumDamageAreas> last_damage_areas_ = {
std::numeric_limits<int>::max(), std::numeric_limits<int>::max()};
};
// PowerModeVoter that requires two consecutive votes for the same PowerMode
// within a given timeout to move out of idle.
class COMPONENT_EXPORT(POWER_SCHEDULER) DebouncedPowerModeVoter {
public:
DebouncedPowerModeVoter(const char* name, base::TimeDelta timeout);
~DebouncedPowerModeVoter();
void VoteFor(PowerMode vote);
void ResetVoteAfterTimeout() { voter_->ResetVoteAfterTimeout(timeout_); }
private:
std::unique_ptr<PowerModeVoter> voter_;
const base::TimeDelta timeout_;
absl::optional<PowerMode> last_vote_;
base::TimeTicks last_vote_timestamp_;
};
} // namespace power_scheduler
#endif // COMPONENTS_POWER_SCHEDULER_POWER_MODE_VOTER_H_