| // 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 "cc/scheduler/scheduler.h" |
| |
| #include <algorithm> |
| #include "base/auto_reset.h" |
| #include "base/debug/trace_event.h" |
| #include "base/logging.h" |
| #include "cc/debug/traced_value.h" |
| |
| namespace cc { |
| |
| Scheduler::Scheduler(SchedulerClient* client, |
| const SchedulerSettings& scheduler_settings) |
| : settings_(scheduler_settings), |
| client_(client), |
| last_set_needs_begin_frame_(false), |
| state_machine_(scheduler_settings), |
| inside_process_scheduled_actions_(false), |
| inside_action_(SchedulerStateMachine::ACTION_NONE), |
| weak_factory_(this) { |
| DCHECK(client_); |
| DCHECK(!state_machine_.BeginFrameNeededByImplThread()); |
| } |
| |
| Scheduler::~Scheduler() {} |
| |
| void Scheduler::SetCanStart() { |
| state_machine_.SetCanStart(); |
| ProcessScheduledActions(); |
| } |
| |
| void Scheduler::SetVisible(bool visible) { |
| state_machine_.SetVisible(visible); |
| ProcessScheduledActions(); |
| } |
| |
| void Scheduler::SetCanDraw(bool can_draw) { |
| state_machine_.SetCanDraw(can_draw); |
| ProcessScheduledActions(); |
| } |
| |
| void Scheduler::NotifyReadyToActivate() { |
| state_machine_.NotifyReadyToActivate(); |
| ProcessScheduledActions(); |
| } |
| |
| void Scheduler::ActivatePendingTree() { |
| client_->ScheduledActionActivatePendingTree(); |
| if (state_machine_.ShouldTriggerBeginFrameDeadlineEarly()) |
| PostBeginFrameDeadline(base::TimeTicks()); |
| } |
| |
| void Scheduler::SetNeedsCommit() { |
| state_machine_.SetNeedsCommit(); |
| ProcessScheduledActions(); |
| } |
| |
| void Scheduler::SetNeedsForcedCommitForReadback() { |
| state_machine_.SetNeedsCommit(); |
| state_machine_.SetNeedsForcedCommitForReadback(); |
| ProcessScheduledActions(); |
| } |
| |
| void Scheduler::SetNeedsRedraw() { |
| state_machine_.SetNeedsRedraw(); |
| ProcessScheduledActions(); |
| } |
| |
| void Scheduler::SetNeedsManageTiles() { |
| DCHECK(!IsInsideAction(SchedulerStateMachine::ACTION_MANAGE_TILES)); |
| state_machine_.SetNeedsManageTiles(); |
| ProcessScheduledActions(); |
| } |
| |
| void Scheduler::SetSwapUsedIncompleteTile(bool used_incomplete_tile) { |
| state_machine_.SetSwapUsedIncompleteTile(used_incomplete_tile); |
| ProcessScheduledActions(); |
| } |
| |
| void Scheduler::SetMainThreadNeedsLayerTextures() { |
| state_machine_.SetMainThreadNeedsLayerTextures(); |
| ProcessScheduledActions(); |
| } |
| |
| void Scheduler::FinishCommit() { |
| TRACE_EVENT0("cc", "Scheduler::FinishCommit"); |
| state_machine_.FinishCommit(); |
| ProcessScheduledActions(); |
| |
| if (state_machine_.ShouldTriggerBeginFrameDeadlineEarly()) |
| PostBeginFrameDeadline(base::TimeTicks()); |
| } |
| |
| void Scheduler::BeginFrameAbortedByMainThread(bool did_handle) { |
| TRACE_EVENT0("cc", "Scheduler::BeginFrameAbortedByMainThread"); |
| state_machine_.BeginFrameAbortedByMainThread(did_handle); |
| ProcessScheduledActions(); |
| } |
| |
| void Scheduler::DidLoseOutputSurface() { |
| TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface"); |
| last_set_needs_begin_frame_ = false; |
| begin_frame_deadline_closure_.Cancel(); |
| state_machine_.DidLoseOutputSurface(); |
| ProcessScheduledActions(); |
| } |
| |
| void Scheduler::DidCreateAndInitializeOutputSurface() { |
| TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface"); |
| DCHECK(!last_set_needs_begin_frame_); |
| DCHECK(begin_frame_deadline_closure_.IsCancelled()); |
| state_machine_.DidCreateAndInitializeOutputSurface(); |
| ProcessScheduledActions(); |
| } |
| |
| base::TimeTicks Scheduler::AnticipatedDrawTime() { |
| TRACE_EVENT0("cc", "Scheduler::AnticipatedDrawTime"); |
| |
| if (!last_set_needs_begin_frame_ || |
| last_begin_frame_args_.interval <= base::TimeDelta()) |
| return base::TimeTicks(); |
| |
| base::TimeTicks now = base::TimeTicks::Now(); |
| base::TimeTicks timebase = std::max(last_begin_frame_args_.frame_time, |
| last_begin_frame_args_.deadline); |
| int64 intervals = 1 + ((now - timebase) / last_begin_frame_args_.interval); |
| return timebase + (last_begin_frame_args_.interval * intervals); |
| } |
| |
| base::TimeTicks Scheduler::LastBeginFrameOnImplThreadTime() { |
| return last_begin_frame_args_.frame_time; |
| } |
| |
| void Scheduler::SetupNextBeginFrameIfNeeded() { |
| bool needs_begin_frame = |
| state_machine_.BeginFrameNeededByImplThread(); |
| |
| bool at_end_of_deadline = |
| state_machine_.begin_frame_state() == |
| SchedulerStateMachine::BEGIN_FRAME_STATE_INSIDE_DEADLINE; |
| |
| bool should_call_set_needs_begin_frame = |
| // Always request the BeginFrame immediately if it wasn't needed before. |
| (needs_begin_frame && !last_set_needs_begin_frame_) || |
| // We always need to explicitly request our next BeginFrame. |
| at_end_of_deadline; |
| |
| if (should_call_set_needs_begin_frame) { |
| client_->SetNeedsBeginFrameOnImplThread(needs_begin_frame); |
| last_set_needs_begin_frame_ = needs_begin_frame; |
| } |
| |
| // Setup PollForAnticipatedDrawTriggers if we need to monitor state but |
| // aren't expecting any more BeginFrames. This should only be needed by the |
| // synchronous compositor when BeginFrameNeededByImplThread is false. |
| if (state_machine_.ShouldPollForAnticipatedDrawTriggers()) { |
| DCHECK(!state_machine_.SupportsProactiveBeginFrame()); |
| DCHECK(!needs_begin_frame); |
| if (poll_for_draw_triggers_closure_.IsCancelled()) { |
| poll_for_draw_triggers_closure_.Reset( |
| base::Bind(&Scheduler::PollForAnticipatedDrawTriggers, |
| weak_factory_.GetWeakPtr())); |
| base::MessageLoop::current()->PostDelayedTask( |
| FROM_HERE, |
| poll_for_draw_triggers_closure_.callback(), |
| last_begin_frame_args_.interval); |
| } |
| } else { |
| poll_for_draw_triggers_closure_.Cancel(); |
| } |
| } |
| |
| void Scheduler::BeginFrame(const BeginFrameArgs& args) { |
| TRACE_EVENT0("cc", "Scheduler::BeginFrame"); |
| DCHECK(state_machine_.begin_frame_state() == |
| SchedulerStateMachine::BEGIN_FRAME_STATE_IDLE); |
| DCHECK(state_machine_.HasInitializedOutputSurface()); |
| last_begin_frame_args_ = args; |
| last_begin_frame_args_.deadline -= client_->DrawDurationEstimate(); |
| state_machine_.OnBeginFrame(last_begin_frame_args_); |
| ProcessScheduledActions(); |
| |
| if (!state_machine_.HasInitializedOutputSurface()) |
| return; |
| |
| state_machine_.OnBeginFrameDeadlinePending(); |
| |
| if (settings_.using_synchronous_renderer_compositor) { |
| // The synchronous renderer compositor has to make its GL calls |
| // within this call to BeginFrame. |
| // TODO(brianderson): Have the OutputSurface initiate the deadline tasks |
| // so the sychronous renderer compoistor can take advantage of splitting |
| // up the BeginFrame and deadline as well. |
| OnBeginFrameDeadline(); |
| } else if (!settings_.deadline_scheduling_enabled) { |
| // We emulate the old non-deadline scheduler here by posting the |
| // deadline task without any delay. |
| PostBeginFrameDeadline(base::TimeTicks()); |
| } else if (state_machine_.ShouldTriggerBeginFrameDeadlineEarly()) { |
| // We are ready to draw a new active tree immediately. |
| PostBeginFrameDeadline(base::TimeTicks()); |
| } else if (state_machine_.needs_redraw()) { |
| // We have an animation or fast input path on the impl thread that wants |
| // to draw, so don't wait too long for a new active tree. |
| PostBeginFrameDeadline(last_begin_frame_args_.deadline); |
| } else { |
| // The impl thread doesn't have anything it wants to draw and we are just |
| // waiting for a new active tree, so post the deadline for the next |
| // expected BeginFrame start. This allows us to draw immediately when |
| // there is a new active tree, instead of waiting for the next BeginFrame. |
| // TODO(brianderson): Handle long deadlines (that are past the next frame's |
| // frame time) properly instead of using this hack. |
| PostBeginFrameDeadline(last_begin_frame_args_.frame_time + |
| last_begin_frame_args_.interval); |
| } |
| } |
| |
| void Scheduler::PostBeginFrameDeadline(base::TimeTicks deadline) { |
| begin_frame_deadline_closure_.Cancel(); |
| begin_frame_deadline_closure_.Reset( |
| base::Bind(&Scheduler::OnBeginFrameDeadline, weak_factory_.GetWeakPtr())); |
| client_->PostBeginFrameDeadline(begin_frame_deadline_closure_.callback(), |
| deadline); |
| } |
| |
| void Scheduler::OnBeginFrameDeadline() { |
| TRACE_EVENT0("cc", "Scheduler::OnBeginFrameDeadline"); |
| DCHECK(state_machine_.HasInitializedOutputSurface()); |
| begin_frame_deadline_closure_.Cancel(); |
| state_machine_.OnBeginFrameDeadline(); |
| ProcessScheduledActions(); |
| |
| if (state_machine_.HasInitializedOutputSurface()) { |
| // We only transition out of BEGIN_FRAME_STATE_INSIDE_DEADLINE when all |
| // actions that occur back-to-back in response to entering |
| // BEGIN_FRAME_STATE_INSIDE_DEADLINE have completed. This is important |
| // because sending the BeginFrame to the main thread will not occur if |
| // we transition to BEGIN_FRAME_STATE_IDLE too early. |
| state_machine_.OnBeginFrameIdle(); |
| } |
| |
| client_->DidBeginFrameDeadlineOnImplThread(); |
| } |
| |
| void Scheduler::PollForAnticipatedDrawTriggers() { |
| TRACE_EVENT0("cc", "Scheduler::PollForAnticipatedDrawTriggers"); |
| state_machine_.DidEnterPollForAnticipatedDrawTriggers(); |
| ProcessScheduledActions(); |
| state_machine_.DidLeavePollForAnticipatedDrawTriggers(); |
| |
| poll_for_draw_triggers_closure_.Cancel(); |
| } |
| |
| void Scheduler::DrawAndSwapIfPossible() { |
| DrawSwapReadbackResult result = |
| client_->ScheduledActionDrawAndSwapIfPossible(); |
| state_machine_.DidDrawIfPossibleCompleted(result.did_draw); |
| } |
| |
| void Scheduler::DrawAndSwapForced() { |
| client_->ScheduledActionDrawAndSwapForced(); |
| } |
| |
| void Scheduler::DrawAndReadback() { |
| DrawSwapReadbackResult result = client_->ScheduledActionDrawAndReadback(); |
| DCHECK(!result.did_swap); |
| } |
| |
| void Scheduler::ProcessScheduledActions() { |
| // We do not allow ProcessScheduledActions to be recursive. |
| // The top-level call will iteratively execute the next action for us anyway. |
| if (inside_process_scheduled_actions_) |
| return; |
| |
| base::AutoReset<bool> mark_inside(&inside_process_scheduled_actions_, true); |
| |
| SchedulerStateMachine::Action action; |
| do { |
| state_machine_.CheckInvariants(); |
| action = state_machine_.NextAction(); |
| TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler"), |
| "SchedulerStateMachine", |
| "state", |
| TracedValue::FromValue(state_machine_.AsValue().release())); |
| state_machine_.UpdateState(action); |
| base::AutoReset<SchedulerStateMachine::Action> |
| mark_inside_action(&inside_action_, action); |
| switch (action) { |
| case SchedulerStateMachine::ACTION_NONE: |
| break; |
| case SchedulerStateMachine::ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD: |
| client_->ScheduledActionSendBeginFrameToMainThread(); |
| break; |
| case SchedulerStateMachine::ACTION_COMMIT: |
| client_->ScheduledActionCommit(); |
| break; |
| case SchedulerStateMachine::ACTION_UPDATE_VISIBLE_TILES: |
| client_->ScheduledActionUpdateVisibleTiles(); |
| break; |
| case SchedulerStateMachine::ACTION_ACTIVATE_PENDING_TREE: |
| ActivatePendingTree(); |
| break; |
| case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_IF_POSSIBLE: |
| DrawAndSwapIfPossible(); |
| break; |
| case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_FORCED: |
| DrawAndSwapForced(); |
| break; |
| case SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT: |
| // No action is actually performed, but this allows the state machine to |
| // advance out of its waiting to draw state without actually drawing. |
| break; |
| case SchedulerStateMachine::ACTION_DRAW_AND_READBACK: |
| DrawAndReadback(); |
| break; |
| case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION: |
| client_->ScheduledActionBeginOutputSurfaceCreation(); |
| break; |
| case SchedulerStateMachine::ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD: |
| client_->ScheduledActionAcquireLayerTexturesForMainThread(); |
| break; |
| case SchedulerStateMachine::ACTION_MANAGE_TILES: |
| client_->ScheduledActionManageTiles(); |
| break; |
| } |
| } while (action != SchedulerStateMachine::ACTION_NONE); |
| |
| SetupNextBeginFrameIfNeeded(); |
| client_->DidAnticipatedDrawTimeChange(AnticipatedDrawTime()); |
| } |
| |
| bool Scheduler::WillDrawIfNeeded() const { |
| return !state_machine_.PendingDrawsShouldBeAborted(); |
| } |
| |
| } // namespace cc |