blob: a84943e7ee8feb352e9094902b50594da439ceef [file] [log] [blame]
// Copyright 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <string>
#include "base/cancelable_callback.h"
#include "base/time/time.h"
#include "cc/cc_export.h"
#include "cc/scheduler/begin_frame_tracker.h"
#include "cc/scheduler/draw_result.h"
#include "cc/scheduler/scheduler_settings.h"
#include "cc/scheduler/scheduler_state_machine.h"
#include "cc/tiles/tile_priority.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"
namespace base {
namespace trace_event {
class ConvertableToTraceFormat;
class SingleThreadTaskRunner;
namespace cc {
class CompositorTimingHistory;
class SchedulerClient {
// Returns whether the frame has damage.
virtual bool WillBeginImplFrame(const viz::BeginFrameArgs& args) = 0;
virtual void ScheduledActionSendBeginMainFrame(
const viz::BeginFrameArgs& args) = 0;
virtual DrawResult ScheduledActionDrawIfPossible() = 0;
virtual DrawResult ScheduledActionDrawForced() = 0;
// The Commit step occurs when the client received the BeginFrame from the
// source and we perform at most one commit per BeginFrame. In this step the
// main thread collects all updates then blocks and gives control to the
// compositor thread, which allows Compositor thread to update its layer tree
// to match the state of the layer tree on the main thread.
virtual void ScheduledActionCommit() = 0;
virtual void ScheduledActionActivateSyncTree() = 0;
virtual void ScheduledActionBeginLayerTreeFrameSinkCreation() = 0;
virtual void ScheduledActionPrepareTiles() = 0;
virtual void ScheduledActionInvalidateLayerTreeFrameSink(
bool needs_redraw) = 0;
virtual void ScheduledActionPerformImplSideInvalidation() = 0;
virtual void DidFinishImplFrame() = 0;
virtual void DidNotProduceFrame(const viz::BeginFrameAck& ack) = 0;
virtual void WillNotReceiveBeginFrame() = 0;
virtual void SendBeginMainFrameNotExpectedSoon() = 0;
virtual void ScheduledActionBeginMainFrameNotExpectedUntil(
base::TimeTicks time) = 0;
virtual void FrameIntervalUpdated(base::TimeDelta interval) = 0;
// Functions used for reporting animation targeting UMA,
virtual size_t CompositedAnimationsCount() const = 0;
virtual size_t MainThreadAnimationsCount() const = 0;
virtual bool CurrentFrameHadRAF() const = 0;
virtual bool NextFrameHasPendingRAF() const = 0;
virtual ~SchedulerClient() {}
class CC_EXPORT Scheduler : public viz::BeginFrameObserverBase {
Scheduler(SchedulerClient* client,
const SchedulerSettings& scheduler_settings,
int layer_tree_host_id,
base::SingleThreadTaskRunner* task_runner,
std::unique_ptr<CompositorTimingHistory> compositor_timing_history);
Scheduler(const Scheduler&) = delete;
~Scheduler() override;
Scheduler& operator=(const Scheduler&) = delete;
// This is needed so that the scheduler doesn't perform spurious actions while
// the compositor is being torn down.
void Stop();
// BeginFrameObserverBase
void OnBeginFrameSourcePausedChanged(bool paused) override;
bool OnBeginFrameDerivedImpl(const viz::BeginFrameArgs& args) override;
void OnDrawForLayerTreeFrameSink(bool resourceless_software_draw,
bool skip_draw);
const SchedulerSettings& settings() const { return settings_; }
void SetVisible(bool visible);
bool visible() { return state_machine_.visible(); }
void SetCanDraw(bool can_draw);
// We have 2 copies of the layer trees on the compositor thread: pending_tree
// and active_tree. When we finish asynchronously rastering all tiles on
// pending_tree, call this method to notify that this pending tree is ready to
// be activated, that is to be copied to the active tree.
void NotifyReadyToActivate();
void NotifyReadyToDraw();
void SetBeginFrameSource(viz::BeginFrameSource* source);
using AnimationWorkletState = SchedulerStateMachine::AnimationWorkletState;
using TreeType = SchedulerStateMachine::TreeType;
// Sets whether asynchronous animation worklet mutations are running.
// Mutations on the pending tree should block activiation. Mutations on the
// active tree should delay draw to allow time for the mutations to complete.
void NotifyAnimationWorkletStateChange(AnimationWorkletState state,
TreeType tree);
// Set |needs_begin_main_frame_| to true, which will cause the BeginFrame
// source to be told to send BeginFrames to this client so that this client
// can send a CompositorFrame to the display compositor with appropriate
// timing.
void SetNeedsBeginMainFrame();
// Requests a single impl frame (after the current frame if there is one
// active).
void SetNeedsOneBeginImplFrame();
void SetNeedsRedraw();
void SetNeedsPrepareTiles();
// Requests a pending tree should be created to invalidate content on the impl
// thread, after the current tree is activated, if any. If the request
// necessitates creating a pending tree only for impl-side invalidations, the
// |client_| is informed to perform this action using
// ScheduledActionRunImplSideInvalidation.
// If ScheduledActionCommit is performed, the impl-side invalidations should
// be merged with the main frame and the request is assumed to be completed.
// If |needs_first_draw_on_activation| is set to true, an impl-side pending
// tree creates for this invalidation must be drawn at least once before a
// new tree can be activated.
void SetNeedsImplSideInvalidation(bool needs_first_draw_on_activation);
// Drawing should result in submitting a CompositorFrame to the
// LayerTreeFrameSink and then calling this.
void DidSubmitCompositorFrame();
// The LayerTreeFrameSink acks when it is ready for a new frame which
// should result in this getting called to unblock the next draw.
void DidReceiveCompositorFrameAck();
void SetTreePrioritiesAndScrollState(TreePriority tree_priority,
ScrollHandlerState scroll_handler_state);
// Commit step happens after the main thread has completed updating for a
// BeginMainFrame request from the compositor, and blocks the main thread
// to copy the layer tree to the compositor thread. Call this method when the
// main thread updates are completed to signal it is ready for the commmit.
void NotifyReadyToCommit();
void BeginMainFrameAborted(CommitEarlyOutReason reason);
void DidCommit();
// In the PrepareTiles step, compositor thread divides the layers into tiles
// to reduce cost of raster large layers. Then, each tile is rastered by a
// dedicated thread.
// |WillPrepareTiles| is called before PrepareTiles step to have the scheduler
// track when PrepareTiles starts.
void WillPrepareTiles();
// |DidPrepareTiles| is called after PrepareTiles step to have the scheduler
// track how long PrepareTiles takes.
void DidPrepareTiles();
void DidLoseLayerTreeFrameSink();
void DidCreateAndInitializeLayerTreeFrameSink();
// Tests do not want to shut down until all possible BeginMainFrames have
// occured to prevent flakiness.
bool MainFrameForTestingWillHappen() const {
return state_machine_.CommitPending() ||
bool CommitPending() const { return state_machine_.CommitPending(); }
bool RedrawPending() const { return state_machine_.RedrawPending(); }
bool PrepareTilesPending() const {
return state_machine_.PrepareTilesPending();
bool ImplLatencyTakesPriority() const {
return state_machine_.ImplLatencyTakesPriority();
// Pass in a main_thread_start_time of base::TimeTicks() if it is not
// known or not trustworthy (e.g. blink is running on a remote channel)
// to signal that the start time isn't known and should not be used for
// scheduling or statistics purposes.
void NotifyBeginMainFrameStarted(base::TimeTicks main_thread_start_time);
base::TimeTicks LastBeginImplFrameTime();
// Deferring begin main frame prevents all document lkifecycle updates and
// updates of new layer tree state.
void SetDeferBeginMainFrame(bool defer_begin_main_frame);
// Controls whether the BeginMainFrameNotExpected messages should be sent to
// the main thread by the cc scheduler.
void SetMainThreadWantsBeginMainFrameNotExpected(bool new_state);
std::unique_ptr<base::trace_event::ConvertableToTraceFormat> AsValue() const;
void AsValueInto(base::trace_event::TracedValue* state) const;
void SetVideoNeedsBeginFrames(bool video_needs_begin_frames);
const viz::BeginFrameSource* begin_frame_source() const {
return begin_frame_source_;
viz::BeginFrameAck CurrentBeginFrameAckForActiveTree() const;
void ClearHistory();
// Virtual for testing.
virtual base::TimeTicks Now() const;
const SchedulerSettings settings_;
SchedulerClient* const client_;
const int layer_tree_host_id_;
base::SingleThreadTaskRunner* task_runner_;
viz::BeginFrameSource* begin_frame_source_ = nullptr;
bool observing_begin_frame_source_ = false;
bool skipped_last_frame_missed_exceeded_deadline_ = false;
bool skipped_last_frame_to_reduce_latency_ = false;
std::unique_ptr<CompositorTimingHistory> compositor_timing_history_;
// What the latest deadline was, and when it was scheduled.
base::TimeTicks deadline_;
base::TimeTicks deadline_scheduled_at_;
SchedulerStateMachine::BeginImplFrameDeadlineMode deadline_mode_;
BeginFrameTracker begin_impl_frame_tracker_;
viz::BeginFrameAck last_begin_frame_ack_;
viz::BeginFrameArgs begin_main_frame_args_;
// Task posted for the deadline or drawing phase of the scheduler. This task
// can be rescheduled e.g. when the condition for the deadline is met, it is
// scheduled to run immediately.
// NOTE: Scheduler weak ptrs are not necessary if CancelableCallback is used.
base::CancelableOnceClosure begin_impl_frame_deadline_task_;
// This is used for queueing begin frames while scheduler is waiting for
// previous frame's deadline, or if it's inside ProcessScheduledActions().
// Only one such task is posted at any time, but the args are updated as we
// get new begin frames.
viz::BeginFrameArgs pending_begin_frame_args_;
base::CancelableOnceClosure pending_begin_frame_task_;
SchedulerStateMachine state_machine_;
bool inside_process_scheduled_actions_ = false;
bool inside_scheduled_action_ = false;
SchedulerStateMachine::Action inside_action_ =
bool stopped_ = false;
// Keeps track of the begin frame interval from the last BeginFrameArgs to
// arrive so that |client_| can be informed about changes.
base::TimeDelta last_frame_interval_;
// Posts the deadline task if needed by checking
// SchedulerStateMachine::CurrentBeginImplFrameDeadlineMode(). This only
// happens when the scheduler is processing a begin frame
// (BeginImplFrameState::INSIDE_BEGIN_FRAME).
void ScheduleBeginImplFrameDeadline();
// Starts or stops begin frames as needed by checking
// SchedulerStateMachine::BeginFrameNeeded(). This only happens when the
// scheduler is not processing a begin frame (BeginImplFrameState::IDLE).
void StartOrStopBeginFrames();
// This will only post a task if the args are valid and there's no existing
// task. That implies that we're still expecting begin frames. If begin frames
// aren't needed this will be a nop. This only happens when the scheduler is
// not processing a begin frame (BeginImplFrameState::IDLE).
void PostPendingBeginFrameTask();
// Use |pending_begin_frame_args_| to begin a new frame like it was received
// in OnBeginFrameDerivedImpl().
void HandlePendingBeginFrame();
// Used to drop the pending begin frame before we go idle.
void CancelPendingBeginFrameTask();
void BeginMainFrameNotExpectedUntil(base::TimeTicks time);
void BeginMainFrameNotExpectedSoon();
void DrawIfPossible();
void DrawForced();
void ProcessScheduledActions();
void UpdateCompositorTimingHistoryRecordingEnabled();
bool ShouldDropBeginFrame(const viz::BeginFrameArgs& args) const;
bool ShouldRecoverMainLatency(const viz::BeginFrameArgs& args,
bool can_activate_before_deadline) const;
bool ShouldRecoverImplLatency(const viz::BeginFrameArgs& args,
bool can_activate_before_deadline) const;
bool CanBeginMainFrameAndActivateBeforeDeadline(
const viz::BeginFrameArgs& args,
base::TimeDelta bmf_to_activate_estimate,
base::TimeTicks now) const;
void AdvanceCommitStateIfPossible();
bool IsBeginMainFrameSentOrStarted() const;
void BeginImplFrameWithDeadline(const viz::BeginFrameArgs& args);
void BeginImplFrameSynchronous(const viz::BeginFrameArgs& args);
void BeginImplFrame(const viz::BeginFrameArgs& args, base::TimeTicks now);
void FinishImplFrame();
void SendDidNotProduceFrame(const viz::BeginFrameArgs& args);
void OnBeginImplFrameDeadline();
void PollToAdvanceCommitState();
void BeginMainFrameAnimateAndLayoutOnly(const viz::BeginFrameArgs& args);
bool IsInsideAction(SchedulerStateMachine::Action action) {
return inside_action_ == action;
} // namespace cc