[go: nahoru, domu]

PowerMode: Track main thread animations separately

Adds a separate PowerModeVoter to cc::Scheduler to identify when the
main thread is producing frames.

This helps us better identify no-op animations that involve the main
thread (e.g. rAF), while still distinguishing (valid) main-thread frame
production that takes a long time and eventually produces updates.

The prior mechanism that would look at the FrameSkippedReason to
identify whether the main thread was active could not detect no-op
main thread animations where the BeginMainFrame exceeded the BeginFrame
deadline.

Bug: b:187392644
Bug: 1166695
Change-Id: Ia3feb109f0ae498dd378cc7c063790433f21e04d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2877048
Commit-Queue: Eric Seckler <eseckler@chromium.org>
Reviewed-by: Bo <boliu@chromium.org>
Reviewed-by: Sunny Sachanandani <sunnyps@chromium.org>
Reviewed-by: Sami Kyöstilä <skyostil@chromium.org>
Cr-Commit-Position: refs/heads/master@{#881990}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index a0848a9..eaa675b 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -436,6 +436,7 @@
     "//base",
     "//base/third_party/dynamic_annotations",
     "//build:chromeos_buildflags",
+    "//components/power_scheduler",
     "//components/viz/client",
     "//device/base/synchronization",
     "//gpu",
@@ -829,6 +830,7 @@
     "//build:chromeos_buildflags",
     "//cc/mojo_embedder",
     "//cc/paint",
+    "//components/power_scheduler",
     "//components/ukm:test_support",
     "//components/viz/client",
     "//components/viz/common",
diff --git a/cc/DEPS b/cc/DEPS
index 85414ab..b694c05 100644
--- a/cc/DEPS
+++ b/cc/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/power_scheduler",
   "+components/ukm/test_ukm_recorder.h",
   "+components/viz/client",
   "+components/viz/common",
diff --git a/cc/mojo_embedder/async_layer_tree_frame_sink.cc b/cc/mojo_embedder/async_layer_tree_frame_sink.cc
index e8c3476d..c22f450 100644
--- a/cc/mojo_embedder/async_layer_tree_frame_sink.cc
+++ b/cc/mojo_embedder/async_layer_tree_frame_sink.cc
@@ -215,7 +215,8 @@
                          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
                          "step", "DidNotProduceFrame", "reason", reason);
   bool frame_completed = reason == FrameSkippedReason::kNoDamage;
-  power_mode_voter_.OnFrameSkipped(frame_completed);
+  bool waiting_on_main = reason == FrameSkippedReason::kWaitingOnMain;
+  power_mode_voter_.OnFrameSkipped(frame_completed, waiting_on_main);
   compositor_frame_sink_ptr_->DidNotProduceFrame(ack);
 }
 
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index e0ba0fb..ccb984d 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -19,6 +19,7 @@
 #include "cc/metrics/begin_main_frame_metrics.h"
 #include "cc/metrics/compositor_frame_reporting_controller.h"
 #include "cc/metrics/compositor_timing_history.h"
+#include "components/power_scheduler/power_mode_arbiter.h"
 #include "components/viz/common/frame_sinks/delay_based_time_source.h"
 #include "services/tracing/public/cpp/perfetto/macros.h"
 #include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pbzero.h"
@@ -40,7 +41,8 @@
     std::unique_ptr<CompositorTimingHistory> compositor_timing_history,
     gfx::RenderingPipeline* main_thread_pipeline,
     gfx::RenderingPipeline* compositor_thread_pipeline,
-    CompositorFrameReportingController* compositor_frame_reporting_controller)
+    CompositorFrameReportingController* compositor_frame_reporting_controller,
+    power_scheduler::PowerModeArbiter* power_mode_arbiter)
     : settings_(settings),
       client_(client),
       layer_tree_host_id_(layer_tree_host_id),
@@ -51,7 +53,9 @@
       begin_impl_frame_tracker_(FROM_HERE),
       state_machine_(settings),
       main_thread_pipeline_(main_thread_pipeline),
-      compositor_thread_pipeline_(compositor_thread_pipeline) {
+      compositor_thread_pipeline_(compositor_thread_pipeline),
+      power_mode_voter_(
+          power_mode_arbiter->NewVoter("PowerModeVoter.MainThreadAnimation")) {
   TRACE_EVENT1("cc", "Scheduler::Scheduler", "settings", settings_.AsValue());
   DCHECK(client_);
   DCHECK(!state_machine_.BeginFrameNeeded());
@@ -948,6 +952,7 @@
 
   PostPendingBeginFrameTask();
   StartOrStopBeginFrames();
+  UpdatePowerModeVote();
 }
 
 void Scheduler::AsProtozeroInto(
@@ -1095,4 +1100,34 @@
   ProcessScheduledActions();
 }
 
+void Scheduler::UpdatePowerModeVote() {
+  // After three aborted BeginMainFrames, consider the main thread's involvement
+  // in frame production unimportant. PowerMode detection for compositor-driven
+  // animation or no-op animation relies on the voter in the frame sink in this
+  // case.
+  constexpr int kMaxAbortedBeginMainFrames = 2;
+
+  bool main_thread_animation =
+      observing_begin_frame_source_ &&
+      (state_machine_.needs_begin_main_frame() ||
+       state_machine_.CommitPending() || state_machine_.has_pending_tree()) &&
+      state_machine_.aborted_begin_main_frame_count() <=
+          kMaxAbortedBeginMainFrames;
+
+  power_scheduler::PowerMode vote =
+      main_thread_animation ? power_scheduler::PowerMode::kMainThreadAnimation
+                            : power_scheduler::PowerMode::kIdle;
+
+  if (last_power_mode_vote_ == vote)
+    return;
+
+  last_power_mode_vote_ = vote;
+  if (vote == power_scheduler::PowerMode::kIdle) {
+    power_mode_voter_->ResetVoteAfterTimeout(
+        power_scheduler::PowerModeVoter::kAnimationTimeout);
+  } else {
+    power_mode_voter_->VoteFor(vote);
+  }
+}
+
 }  // namespace cc
diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h
index 69026a1..fa51f7f6 100644
--- a/cc/scheduler/scheduler.h
+++ b/cc/scheduler/scheduler.h
@@ -19,6 +19,7 @@
 #include "cc/scheduler/scheduler_settings.h"
 #include "cc/scheduler/scheduler_state_machine.h"
 #include "cc/tiles/tile_priority.h"
+#include "components/power_scheduler/power_mode_voter.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/frame_sinks/begin_frame_source.h"
 #include "components/viz/common/frame_sinks/delay_based_time_source.h"
@@ -95,15 +96,16 @@
 
 class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase {
  public:
-  Scheduler(SchedulerClient* client,
-            const SchedulerSettings& scheduler_settings,
-            int layer_tree_host_id,
-            base::SingleThreadTaskRunner* task_runner,
-            std::unique_ptr<CompositorTimingHistory> compositor_timing_history,
-            gfx::RenderingPipeline* main_thread_pipeline,
-            gfx::RenderingPipeline* compositor_thread_pipeline,
-            CompositorFrameReportingController*
-                compositor_frame_reporting_controller);
+  Scheduler(
+      SchedulerClient* client,
+      const SchedulerSettings& scheduler_settings,
+      int layer_tree_host_id,
+      base::SingleThreadTaskRunner* task_runner,
+      std::unique_ptr<CompositorTimingHistory> compositor_timing_history,
+      gfx::RenderingPipeline* main_thread_pipeline,
+      gfx::RenderingPipeline* compositor_thread_pipeline,
+      CompositorFrameReportingController* compositor_frame_reporting_controller,
+      power_scheduler::PowerModeArbiter* power_mode_arbiter);
   Scheduler(const Scheduler&) = delete;
   ~Scheduler() override;
 
@@ -348,6 +350,10 @@
   base::Optional<gfx::RenderingPipeline::ScopedPipelineActive>
       compositor_thread_pipeline_active_;
 
+  std::unique_ptr<power_scheduler::PowerModeVoter> power_mode_voter_;
+  power_scheduler::PowerMode last_power_mode_vote_ =
+      power_scheduler::PowerMode::kIdle;
+
  private:
   // Posts the deadline task if needed by checking
   // SchedulerStateMachine::CurrentBeginImplFrameDeadlineMode(). This only
@@ -403,6 +409,8 @@
   bool IsInsideAction(SchedulerStateMachine::Action action) {
     return inside_action_ == action;
   }
+
+  void UpdatePowerModeVote();
 };
 
 }  // namespace cc
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc
index 84aa13ae..8fc7ba9 100644
--- a/cc/scheduler/scheduler_state_machine.cc
+++ b/cc/scheduler/scheduler_state_machine.cc
@@ -879,6 +879,10 @@
       // need first draw to come through).
       active_tree_is_ready_to_draw_ = false;
     }
+
+    aborted_begin_main_frame_count_ = 0;
+  } else {
+    aborted_begin_main_frame_count_++;
   }
 
   // Update state related to forced draws.
diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h
index 8fa08af31..0f07052 100644
--- a/cc/scheduler/scheduler_state_machine.h
+++ b/cc/scheduler/scheduler_state_machine.h
@@ -349,6 +349,10 @@
   }
   bool did_commit_during_frame() const { return did_commit_during_frame_; }
 
+  int aborted_begin_main_frame_count() const {
+    return aborted_begin_main_frame_count_;
+  }
+
  protected:
   bool BeginFrameRequiredForAction() const;
   bool BeginFrameNeededForVideo() const;
@@ -480,6 +484,9 @@
   // If set to true, the pending tree must be drawn at least once after
   // activation before a new tree can be activated.
   bool pending_tree_needs_first_draw_on_activation_ = false;
+
+  // Number of consecutive BeginMainFrames that were aborted without updates.
+  int aborted_begin_main_frame_count_ = 0;
 };
 
 }  // namespace cc
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index 3074f14..950f1ed6 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -20,11 +20,13 @@
 #include "base/run_loop.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/time/time.h"
+#include "base/time/time_override.h"
 #include "base/trace_event/trace_event.h"
 #include "cc/metrics/begin_main_frame_metrics.h"
 #include "cc/metrics/event_metrics.h"
 #include "cc/test/fake_compositor_frame_reporting_controller.h"
 #include "cc/test/scheduler_test_common.h"
+#include "components/power_scheduler/power_mode_arbiter.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/test/begin_frame_args_test.h"
 #include "components/viz/test/fake_delay_based_time_source.h"
@@ -46,6 +48,10 @@
 namespace cc {
 namespace {
 
+using power_scheduler::PowerMode;
+using power_scheduler::PowerModeArbiter;
+using power_scheduler::PowerModeVoter;
+
 base::TimeDelta kSlowDuration = base::TimeDelta::FromSeconds(1);
 base::TimeDelta kFastDuration = base::TimeDelta::FromMilliseconds(1);
 
@@ -404,7 +410,7 @@
     scheduler_ = std::make_unique<TestScheduler>(
         task_runner_->GetMockTickClock(), client_.get(), scheduler_settings_, 0,
         task_runner_.get(), std::move(fake_compositor_timing_history),
-        reporting_controller.get());
+        reporting_controller.get(), &power_mode_arbiter_);
     client_->set_scheduler(scheduler_.get());
     scheduler_->SetBeginFrameSource(frame_source);
 
@@ -591,6 +597,7 @@
   std::unique_ptr<viz::SyntheticBeginFrameSource> unthrottled_frame_source_;
   SchedulerSettings scheduler_settings_;
   std::unique_ptr<FakeSchedulerClient> client_;
+  PowerModeArbiter power_mode_arbiter_;
   std::unique_ptr<TestScheduler> scheduler_;
   FakeCompositorTimingHistory* fake_compositor_timing_history_;
   DroppedFrameCounter dropped_counter;
@@ -4435,5 +4442,160 @@
   EXPECT_ACTIONS("WillBeginImplFrame");
 }
 
+namespace {
+class FakePowerModeObserver : public PowerModeArbiter::Observer {
+ public:
+  void OnPowerModeChanged(PowerMode old_mode, PowerMode new_mode) override {}
+};
+
+class SchedulerTestForPowerMode : public SchedulerTest {
+ public:
+  SchedulerTestForPowerMode()
+      : time_overrides_(
+            /*time_override=*/nullptr,
+            &SchedulerTestForPowerMode::TimeTicksNow,
+            /*thread_ticks_override=*/nullptr) {
+    DCHECK_EQ(nullptr, current_test_);
+    current_test_ = this;
+
+    // Clear the arbiter's initial kCharging vote.
+    power_mode_arbiter_.SetOnBatteryPowerForTesting(/*on_battery_power=*/true);
+    power_mode_arbiter_.SetTaskRunnerForTesting(task_runner_);
+
+    // Add a fake observer so that reset tasks are executed.
+    power_mode_arbiter_.AddObserver(&observer_);
+  }
+
+  ~SchedulerTestForPowerMode() override {
+    DCHECK_EQ(this, current_test_);
+    current_test_ = nullptr;
+  }
+
+  void AdvanceToArbiterSnapAfter(base::TimeDelta delay) {
+    // Align the mock clock with the phase of the arbiter's reset tasks.
+    base::TimeTicks target_time =
+        (task_runner_->NowTicks() + delay)
+            .SnappedToNextTick(base::TimeTicks(),
+                               PowerModeArbiter::kResetVoteTimeResolution);
+    task_runner_->RunUntilTime(target_time);
+  }
+
+  static base::TimeTicks TimeTicksNow() {
+    DCHECK_NE(nullptr, current_test_);
+    return current_test_->task_runner_->NowTicks();
+  }
+
+ private:
+  // The ScopedTimeClockOverrides below require a function pointer (as opposed
+  // to a bound callback). We store the current test instance in this static
+  // variable to access its members from the static TimeTicksNow() method above.
+  static SchedulerTestForPowerMode* current_test_;
+
+  // The arbiter uses base::TimeTicks::Now(), which needs to be overridden by
+  // the test's task runner. Ideally we'd be using base::test::TaskEnvironment
+  // for scheduler unittests, which would do this for us.
+  base::subtle::ScopedTimeClockOverrides time_overrides_;
+
+  FakePowerModeObserver observer_;
+};
+
+// static
+SchedulerTestForPowerMode* SchedulerTestForPowerMode::current_test_;
+}  // namespace
+
+TEST_F(SchedulerTestForPowerMode, BeginMainFramePowerModeVoter) {
+  // Arbiter should start out in idle mode.
+  EXPECT_EQ(power_mode_arbiter_.GetActiveModeForTesting(), PowerMode::kIdle);
+
+  // SetUpScheduler will cause a BeginMainFrame and commit, which should change
+  // the PowerMode vote.
+  SetUpScheduler(EXTERNAL_BFS);
+  EXPECT_EQ(power_mode_arbiter_.GetActiveModeForTesting(),
+            PowerMode::kMainThreadAnimation);
+
+  // The scheduler should now be idle, so the PowerMode vote should be reset
+  // after kAnimationTimeout.
+  AdvanceToArbiterSnapAfter(PowerModeVoter::kAnimationTimeout);
+  EXPECT_EQ(power_mode_arbiter_.GetActiveModeForTesting(), PowerMode::kIdle);
+
+  scheduler_->SetNeedsBeginMainFrame();
+  EXPECT_EQ(power_mode_arbiter_.GetActiveModeForTesting(),
+            PowerMode::kMainThreadAnimation);
+
+  // While BeginMainFrame is needed, the vote is not reset
+  AdvanceToArbiterSnapAfter(PowerModeVoter::kAnimationTimeout);
+  EXPECT_EQ(power_mode_arbiter_.GetActiveModeForTesting(),
+            PowerMode::kMainThreadAnimation);
+
+  client_->Reset();
+  EXPECT_SCOPED(AdvanceFrame());
+  EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame");
+
+  client_->Reset();
+  scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks());
+
+  // While BeginMainFrame is active, the vote is not reset
+  AdvanceToArbiterSnapAfter(PowerModeVoter::kAnimationTimeout);
+  EXPECT_EQ(power_mode_arbiter_.GetActiveModeForTesting(),
+            PowerMode::kMainThreadAnimation);
+
+  scheduler_->BeginMainFrameAborted(CommitEarlyOutReason::FINISHED_NO_UPDATES);
+
+  // After aborting, the vote is reset.
+  AdvanceToArbiterSnapAfter(PowerModeVoter::kAnimationTimeout);
+  EXPECT_EQ(power_mode_arbiter_.GetActiveModeForTesting(), PowerMode::kIdle);
+
+  // Go through another two BeginMainFrames that are aborted.
+  scheduler_->SetNeedsBeginMainFrame();
+  EXPECT_EQ(power_mode_arbiter_.GetActiveModeForTesting(),
+            PowerMode::kMainThreadAnimation);
+  client_->Reset();
+  EXPECT_SCOPED(AdvanceFrame());
+  EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame");
+  client_->Reset();
+  scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks());
+  scheduler_->BeginMainFrameAborted(CommitEarlyOutReason::FINISHED_NO_UPDATES);
+
+  scheduler_->SetNeedsBeginMainFrame();
+  client_->Reset();
+  EXPECT_SCOPED(AdvanceFrame());
+  EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame");
+  client_->Reset();
+  scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks());
+  scheduler_->BeginMainFrameAborted(CommitEarlyOutReason::FINISHED_NO_UPDATES);
+
+  // Still in animation mode, but a reset is pending.
+  EXPECT_EQ(power_mode_arbiter_.GetActiveModeForTesting(),
+            PowerMode::kMainThreadAnimation);
+  AdvanceToArbiterSnapAfter(PowerModeVoter::kAnimationTimeout);
+  EXPECT_EQ(power_mode_arbiter_.GetActiveModeForTesting(), PowerMode::kIdle);
+
+  // Further BeginMainFrame will be ignored, because the main-thread animation
+  // is considered no-op after three consecutive aborted BeginMainFrames.
+  scheduler_->SetNeedsBeginMainFrame();
+  EXPECT_EQ(power_mode_arbiter_.GetActiveModeForTesting(), PowerMode::kIdle);
+  client_->Reset();
+  EXPECT_SCOPED(AdvanceFrame());
+  EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame");
+  client_->Reset();
+  scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks());
+  scheduler_->BeginMainFrameAborted(CommitEarlyOutReason::FINISHED_NO_UPDATES);
+  EXPECT_EQ(power_mode_arbiter_.GetActiveModeForTesting(), PowerMode::kIdle);
+
+  // But once the BeginMainFrame produces updates, we vote for animation again.
+  scheduler_->SetNeedsBeginMainFrame();
+  EXPECT_EQ(power_mode_arbiter_.GetActiveModeForTesting(), PowerMode::kIdle);
+  client_->Reset();
+  EXPECT_SCOPED(AdvanceFrame());
+  EXPECT_ACTIONS("WillBeginImplFrame", "ScheduledActionSendBeginMainFrame");
+  client_->Reset();
+  scheduler_->NotifyBeginMainFrameStarted(task_runner_->NowTicks());
+  scheduler_->NotifyReadyToCommit(nullptr);
+  EXPECT_ACTIONS("ScheduledActionCommit");
+  EXPECT_EQ(power_mode_arbiter_.GetActiveModeForTesting(),
+            PowerMode::kMainThreadAnimation);
+  client_->Reset();
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/test/scheduler_test_common.cc b/cc/test/scheduler_test_common.cc
index 68250233..827627d 100644
--- a/cc/test/scheduler_test_common.cc
+++ b/cc/test/scheduler_test_common.cc
@@ -141,7 +141,8 @@
     int layer_tree_host_id,
     base::SingleThreadTaskRunner* task_runner,
     std::unique_ptr<CompositorTimingHistory> compositor_timing_history,
-    CompositorFrameReportingController* compositor_frame_reporting_controller)
+    CompositorFrameReportingController* compositor_frame_reporting_controller,
+    power_scheduler::PowerModeArbiter* power_mode_arbiter)
     : Scheduler(client,
                 scheduler_settings,
                 layer_tree_host_id,
@@ -149,7 +150,8 @@
                 std::move(compositor_timing_history),
                 nullptr,
                 nullptr,
-                compositor_frame_reporting_controller),
+                compositor_frame_reporting_controller,
+                power_mode_arbiter),
       now_src_(now_src) {}
 
 base::TimeTicks TestScheduler::Now() const {
diff --git a/cc/test/scheduler_test_common.h b/cc/test/scheduler_test_common.h
index 48fff79..b989e63 100644
--- a/cc/test/scheduler_test_common.h
+++ b/cc/test/scheduler_test_common.h
@@ -86,8 +86,8 @@
       int layer_tree_host_id,
       base::SingleThreadTaskRunner* task_runner,
       std::unique_ptr<CompositorTimingHistory> compositor_timing_history,
-      CompositorFrameReportingController*
-          compositor_frame_reporting_controller);
+      CompositorFrameReportingController* compositor_frame_reporting_controller,
+      power_scheduler::PowerModeArbiter* arbiter);
   TestScheduler(const TestScheduler&) = delete;
 
   TestScheduler& operator=(const TestScheduler&) = delete;
diff --git a/cc/trees/proxy_impl.cc b/cc/trees/proxy_impl.cc
index 6e4e0cc..726d6ff12 100644
--- a/cc/trees/proxy_impl.cc
+++ b/cc/trees/proxy_impl.cc
@@ -31,6 +31,7 @@
 #include "cc/trees/proxy_main.h"
 #include "cc/trees/render_frame_metadata_observer.h"
 #include "cc/trees/task_runner_provider.h"
+#include "components/power_scheduler/power_mode_arbiter.h"
 #include "components/viz/common/frame_sinks/delay_based_time_source.h"
 #include "components/viz/common/frame_timing_details.h"
 #include "components/viz/common/gpu/context_provider.h"
@@ -95,7 +96,8 @@
       task_runner_provider_->ImplThreadTaskRunner(),
       std::move(compositor_timing_history), layer_tree_host->TakeMainPipeline(),
       layer_tree_host->TakeCompositorPipeline(),
-      host_impl_->compositor_frame_reporting_controller());
+      host_impl_->compositor_frame_reporting_controller(),
+      power_scheduler::PowerModeArbiter::GetInstance());
 
   DCHECK_EQ(scheduler_->visible(), host_impl_->visible());
 }
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 3de362e3..a24ce564 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -29,6 +29,7 @@
 #include "cc/trees/mutator_host.h"
 #include "cc/trees/render_frame_metadata_observer.h"
 #include "cc/trees/scoped_abort_remaining_swap_promises.h"
+#include "components/power_scheduler/power_mode_arbiter.h"
 #include "components/viz/common/frame_sinks/delay_based_time_source.h"
 #include "components/viz/common/frame_timing_details.h"
 #include "components/viz/common/gpu/context_provider.h"
@@ -92,7 +93,8 @@
         std::move(compositor_timing_history),
         layer_tree_host_->TakeMainPipeline(),
         layer_tree_host_->TakeCompositorPipeline(),
-        host_impl_->compositor_frame_reporting_controller());
+        host_impl_->compositor_frame_reporting_controller(),
+        power_scheduler::PowerModeArbiter::GetInstance());
   }
 }