Chromium Code Reviews| Index: cc/scheduler/scheduler.cc |
| diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc |
| index 5bbd1a2dc425aabbb5b0fd76a9aea78676a130fe..11113074f758ff014ed97844a46cc0d91c3981dd 100644 |
| --- a/cc/scheduler/scheduler.cc |
| +++ b/cc/scheduler/scheduler.cc |
| @@ -17,69 +17,67 @@ |
| namespace cc { |
| -Scheduler::SyntheticBeginFrameSource::SyntheticBeginFrameSource( |
| - Scheduler* scheduler, |
| - scoped_refptr<DelayBasedTimeSource> time_source) |
| - : scheduler_(scheduler), time_source_(time_source) { |
| - time_source_->SetClient(this); |
| -} |
| - |
| -Scheduler::SyntheticBeginFrameSource::~SyntheticBeginFrameSource() { |
| -} |
| - |
| -void Scheduler::SyntheticBeginFrameSource::CommitVSyncParameters( |
| - base::TimeTicks timebase, |
| - base::TimeDelta interval) { |
| - time_source_->SetTimebaseAndInterval(timebase, interval); |
| -} |
| - |
| -void Scheduler::SyntheticBeginFrameSource::SetNeedsBeginFrame( |
| - bool needs_begin_frame, |
| - std::deque<BeginFrameArgs>* begin_retro_frame_args) { |
| - DCHECK(begin_retro_frame_args); |
| - base::TimeTicks missed_tick_time = |
| - time_source_->SetActive(needs_begin_frame); |
| - if (!missed_tick_time.is_null()) { |
| - begin_retro_frame_args->push_back( |
| - CreateSyntheticBeginFrameArgs(missed_tick_time)); |
| +BeginFrameSource* SchedulerFrameSourcesConstructor::ConstructPrimaryFrameSource( |
| + Scheduler* scheduler) { |
| + if (!scheduler->settings_.throttle_frame_production) { |
| + TRACE_EVENT1("cc", |
| + "Scheduler::Scheduler()", |
| + "PrimaryFrameSource", |
| + "BackToBackBeginFrameSource"); |
| + DCHECK(!scheduler->primary_frame_source_internal_); |
| + scheduler->primary_frame_source_internal_ = |
| + BackToBackBeginFrameSource::Create(scheduler->task_runner_.get()); |
| + return scheduler->primary_frame_source_internal_.get(); |
| + } else if (scheduler->settings_.begin_frame_scheduling_enabled) { |
| + TRACE_EVENT1("cc", |
| + "Scheduler::Scheduler()", |
| + "PrimaryFrameSource", |
| + "SchedulerClient"); |
| + return scheduler->client_->ExternalBeginFrameSource(); |
| + } else { |
| + TRACE_EVENT1("cc", |
| + "Scheduler::Scheduler()", |
| + "PrimaryFrameSource", |
| + "SyntheticBeginFrameSource"); |
| + scoped_ptr<SyntheticBeginFrameSource> synthetic_source = |
| + SyntheticBeginFrameSource::Create(scheduler->task_runner_.get(), |
| + scheduler->Now(), |
| + BeginFrameArgs::DefaultInterval()); |
| + |
| + DCHECK(!scheduler->vsync_observer_); |
| + scheduler->vsync_observer_ = synthetic_source.get(); |
| + |
| + DCHECK(!scheduler->primary_frame_source_internal_); |
| + scheduler->primary_frame_source_internal_ = synthetic_source.Pass(); |
| + return scheduler->primary_frame_source_internal_.get(); |
| } |
| } |
| -bool Scheduler::SyntheticBeginFrameSource::IsActive() const { |
| - return time_source_->Active(); |
| -} |
| - |
| -void Scheduler::SyntheticBeginFrameSource::OnTimerTick() { |
| - BeginFrameArgs begin_frame_args( |
| - CreateSyntheticBeginFrameArgs(time_source_->LastTickTime())); |
| - scheduler_->BeginFrame(begin_frame_args); |
| -} |
| - |
| -void Scheduler::SyntheticBeginFrameSource::AsValueInto( |
| - base::debug::TracedValue* state) const { |
| - time_source_->AsValueInto(state); |
| -} |
| - |
| -BeginFrameArgs |
| -Scheduler::SyntheticBeginFrameSource::CreateSyntheticBeginFrameArgs( |
| - base::TimeTicks frame_time) { |
| - base::TimeTicks deadline = time_source_->NextTickTime(); |
| - return BeginFrameArgs::Create( |
| - frame_time, deadline, scheduler_->VSyncInterval()); |
| +BeginFrameSource* |
| +SchedulerFrameSourcesConstructor::ConstructBackgroundFrameSource( |
| + Scheduler* scheduler) { |
| + TRACE_EVENT1("cc", |
| + "Scheduler::Scheduler()", |
| + "BackgroundFrameSource", |
| + "SyntheticBeginFrameSource"); |
| + DCHECK(!(scheduler->background_frame_source_internal_)); |
| + scheduler->background_frame_source_internal_ = |
| + SyntheticBeginFrameSource::Create(scheduler->task_runner_.get(), |
| + scheduler->Now(), |
| + base::TimeDelta::FromSeconds(1)); |
| + return scheduler->background_frame_source_internal_.get(); |
| } |
| Scheduler::Scheduler( |
| SchedulerClient* client, |
| const SchedulerSettings& scheduler_settings, |
| int layer_tree_host_id, |
| - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) |
| + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| + SchedulerFrameSourcesConstructor frame_sources_constructor) |
| : settings_(scheduler_settings), |
| client_(client), |
| layer_tree_host_id_(layer_tree_host_id), |
| task_runner_(task_runner), |
| - vsync_interval_(BeginFrameArgs::DefaultInterval()), |
| - last_set_needs_begin_frame_(false), |
| - begin_unthrottled_frame_posted_(false), |
| begin_retro_frame_posted_(false), |
| state_machine_(scheduler_settings), |
| inside_process_scheduled_actions_(false), |
| @@ -97,8 +95,6 @@ Scheduler::Scheduler( |
| begin_retro_frame_closure_ = |
| base::Bind(&Scheduler::BeginRetroFrame, weak_factory_.GetWeakPtr()); |
| - begin_unthrottled_frame_closure_ = |
| - base::Bind(&Scheduler::BeginUnthrottledFrame, weak_factory_.GetWeakPtr()); |
| begin_impl_frame_deadline_closure_ = base::Bind( |
| &Scheduler::OnBeginImplFrameDeadline, weak_factory_.GetWeakPtr()); |
| poll_for_draw_triggers_closure_ = base::Bind( |
| @@ -106,30 +102,21 @@ Scheduler::Scheduler( |
| advance_commit_state_closure_ = base::Bind( |
| &Scheduler::PollToAdvanceCommitState, weak_factory_.GetWeakPtr()); |
| - if (!settings_.begin_frame_scheduling_enabled) { |
| - SetupSyntheticBeginFrames(); |
| - } |
| -} |
| + frame_source_ = BeginFrameSourceMultiplexer::Create(); |
| + frame_source_->AddObserver(this); |
| -Scheduler::~Scheduler() { |
| - if (synthetic_begin_frame_source_) { |
| - synthetic_begin_frame_source_->SetNeedsBeginFrame(false, |
| - &begin_retro_frame_args_); |
| - } |
| + // Primary frame source |
| + primary_frame_source_ = |
| + frame_sources_constructor.ConstructPrimaryFrameSource(this); |
| + frame_source_->AddSource(primary_frame_source_); |
| + |
| + // Background ticking frame source |
| + background_frame_source_ = |
| + frame_sources_constructor.ConstructBackgroundFrameSource(this); |
| + frame_source_->AddSource(background_frame_source_); |
| } |
| -void Scheduler::SetupSyntheticBeginFrames() { |
| - scoped_refptr<DelayBasedTimeSource> time_source; |
| - if (gfx::FrameTime::TimestampsAreHighRes()) { |
| - time_source = DelayBasedTimeSourceHighRes::Create(VSyncInterval(), |
| - task_runner_.get()); |
| - } else { |
| - time_source = |
| - DelayBasedTimeSource::Create(VSyncInterval(), task_runner_.get()); |
| - } |
| - DCHECK(!synthetic_begin_frame_source_); |
| - synthetic_begin_frame_source_.reset( |
| - new SyntheticBeginFrameSource(this, time_source)); |
| +Scheduler::~Scheduler() { |
| } |
| base::TimeTicks Scheduler::Now() const { |
| @@ -141,9 +128,9 @@ void Scheduler::CommitVSyncParameters(base::TimeTicks timebase, |
| // TODO(brianderson): We should not be receiving 0 intervals. |
| if (interval == base::TimeDelta()) |
| interval = BeginFrameArgs::DefaultInterval(); |
| - vsync_interval_ = interval; |
| - if (!settings_.begin_frame_scheduling_enabled) |
| - synthetic_begin_frame_source_->CommitVSyncParameters(timebase, interval); |
| + |
| + if (vsync_observer_) |
| + vsync_observer_->OnUpdateVSyncParameters(timebase, interval); |
| } |
| void Scheduler::SetEstimatedParentDrawTime(base::TimeDelta draw_time) { |
| @@ -158,6 +145,11 @@ void Scheduler::SetCanStart() { |
| void Scheduler::SetVisible(bool visible) { |
| state_machine_.SetVisible(visible); |
| + if (visible) { |
| + frame_source_->SetActiveSource(primary_frame_source_); |
| + } else { |
| + frame_source_->SetActiveSource(background_frame_source_); |
| + } |
| ProcessScheduledActions(); |
| } |
| @@ -240,18 +232,15 @@ void Scheduler::DidManageTiles() { |
| void Scheduler::DidLoseOutputSurface() { |
| TRACE_EVENT0("cc", "Scheduler::DidLoseOutputSurface"); |
| state_machine_.DidLoseOutputSurface(); |
| - last_set_needs_begin_frame_ = false; |
| - if (!settings_.begin_frame_scheduling_enabled) { |
| - synthetic_begin_frame_source_->SetNeedsBeginFrame(false, |
| - &begin_retro_frame_args_); |
| - } |
| + if (frame_source_->NeedsBeginFrames()) |
| + frame_source_->SetNeedsBeginFrames(false); |
| begin_retro_frame_args_.clear(); |
| ProcessScheduledActions(); |
| } |
| void Scheduler::DidCreateAndInitializeOutputSurface() { |
| TRACE_EVENT0("cc", "Scheduler::DidCreateAndInitializeOutputSurface"); |
| - DCHECK(!last_set_needs_begin_frame_); |
| + DCHECK(!frame_source_->NeedsBeginFrames()); |
| DCHECK(begin_impl_frame_deadline_task_.IsCancelled()); |
| state_machine_.DidCreateAndInitializeOutputSurface(); |
| ProcessScheduledActions(); |
| @@ -263,7 +252,7 @@ void Scheduler::NotifyBeginMainFrameStarted() { |
| } |
| base::TimeTicks Scheduler::AnticipatedDrawTime() const { |
| - if (!last_set_needs_begin_frame_ || |
| + if (!frame_source_->NeedsBeginFrames() || |
| begin_impl_frame_args_.interval <= base::TimeDelta()) |
| return base::TimeTicks(); |
| @@ -284,75 +273,27 @@ void Scheduler::SetupNextBeginFrameIfNeeded() { |
| bool needs_begin_frame = state_machine_.BeginFrameNeeded(); |
| - if (settings_.throttle_frame_production) { |
| - SetupNextBeginFrameWhenVSyncThrottlingEnabled(needs_begin_frame); |
| - } else { |
| - SetupNextBeginFrameWhenVSyncThrottlingDisabled(needs_begin_frame); |
| - } |
| - SetupPollingMechanisms(needs_begin_frame); |
| -} |
| - |
| -// When we are throttling frame production, we request BeginFrames |
| -// from the OutputSurface. |
| -void Scheduler::SetupNextBeginFrameWhenVSyncThrottlingEnabled( |
| - bool needs_begin_frame) { |
| bool at_end_of_deadline = |
| - state_machine_.begin_impl_frame_state() == |
| - SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE; |
| + (state_machine_.begin_impl_frame_state() == |
| + SchedulerStateMachine::BEGIN_IMPL_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_) || |
| + (needs_begin_frame && !frame_source_->NeedsBeginFrames()) || |
| // Only stop requesting BeginFrames after a deadline. |
| - (!needs_begin_frame && last_set_needs_begin_frame_ && at_end_of_deadline); |
| + (!needs_begin_frame && frame_source_->NeedsBeginFrames() && |
| + at_end_of_deadline); |
| if (should_call_set_needs_begin_frame) { |
| - if (settings_.begin_frame_scheduling_enabled) { |
| - client_->SetNeedsBeginFrame(needs_begin_frame); |
| - } else { |
| - synthetic_begin_frame_source_->SetNeedsBeginFrame( |
| - needs_begin_frame, &begin_retro_frame_args_); |
| - } |
| - last_set_needs_begin_frame_ = needs_begin_frame; |
| + frame_source_->SetNeedsBeginFrames(needs_begin_frame); |
| } |
| - PostBeginRetroFrameIfNeeded(); |
| -} |
| - |
| -// When we aren't throttling frame production, we initiate a BeginFrame |
| -// as soon as one is needed. |
| -void Scheduler::SetupNextBeginFrameWhenVSyncThrottlingDisabled( |
| - bool needs_begin_frame) { |
| - last_set_needs_begin_frame_ = needs_begin_frame; |
| - |
| - if (!needs_begin_frame || begin_unthrottled_frame_posted_) |
| - return; |
| - |
| - if (state_machine_.begin_impl_frame_state() != |
| - SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE && |
| - state_machine_.begin_impl_frame_state() != |
| - SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE) { |
| - return; |
| + if (at_end_of_deadline) { |
| + frame_source_->DidFinishFrame(begin_retro_frame_args_.size()); |
| } |
| - begin_unthrottled_frame_posted_ = true; |
| - task_runner_->PostTask(FROM_HERE, begin_unthrottled_frame_closure_); |
| -} |
| - |
| -// BeginUnthrottledFrame is used when we aren't throttling frame production. |
| -// This will usually be because VSync is disabled. |
| -void Scheduler::BeginUnthrottledFrame() { |
| - DCHECK(!settings_.throttle_frame_production); |
| - DCHECK(begin_retro_frame_args_.empty()); |
| - |
| - base::TimeTicks now = Now(); |
| - base::TimeTicks deadline = now + vsync_interval_; |
| - |
| - BeginFrameArgs begin_frame_args = |
| - BeginFrameArgs::Create(now, deadline, vsync_interval_); |
| - BeginImplFrame(begin_frame_args); |
| - |
| - begin_unthrottled_frame_posted_ = false; |
| + PostBeginRetroFrameIfNeeded(); |
| + SetupPollingMechanisms(needs_begin_frame); |
| } |
| // We may need to poll when we can't rely on BeginFrame to advance certain |
| @@ -408,9 +349,8 @@ void Scheduler::SetupPollingMechanisms(bool needs_begin_frame) { |
| // making a frame. Usually this means that user input for the frame is complete. |
| // If the scheduler is busy, we queue the BeginFrame to be handled later as |
| // a BeginRetroFrame. |
| -void Scheduler::BeginFrame(const BeginFrameArgs& args) { |
| +bool Scheduler::OnBeginFrameImpl(const BeginFrameArgs& args) { |
|
brianderson
2014/09/23 01:31:06
Why did this become OnBeginFrameImpl? A little con
mithro-old
2014/09/23 12:43:54
Fixed. Should be significantly clearer now.
|
| TRACE_EVENT1("cc", "Scheduler::BeginFrame", "args", args.AsValue()); |
| - DCHECK(settings_.throttle_frame_production); |
| BeginFrameArgs adjusted_args(args); |
| adjusted_args.deadline -= EstimatedParentDrawTime(); |
| @@ -421,7 +361,7 @@ void Scheduler::BeginFrame(const BeginFrameArgs& args) { |
| } else { |
| should_defer_begin_frame = |
| !begin_retro_frame_args_.empty() || begin_retro_frame_posted_ || |
| - !last_set_needs_begin_frame_ || |
| + !frame_source_->NeedsBeginFrames() || |
| (state_machine_.begin_impl_frame_state() != |
| SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE); |
| } |
| @@ -430,10 +370,21 @@ void Scheduler::BeginFrame(const BeginFrameArgs& args) { |
| begin_retro_frame_args_.push_back(adjusted_args); |
| TRACE_EVENT_INSTANT0( |
| "cc", "Scheduler::BeginFrame deferred", TRACE_EVENT_SCOPE_THREAD); |
| - return; |
| + // Queuing the frame counts as "using it", so we need to return true. |
| + return true; |
| } |
| BeginImplFrame(adjusted_args); |
| + return true; |
| +} |
| + |
| +// We have just called SetNeedsBeginFrame(true) and the BeginFrameSource has |
| +// sent us the last BeginFrame we have missed. As we might not be able to |
| +// actually make rendering for this call, handle it like a "retro frame". |
| +// TODO(brainderson): Add a test for this functionality ASAP! |
| +void Scheduler::OnMissedBeginFrame(const BeginFrameArgs& args) { |
| + begin_retro_frame_args_.push_back(args); |
| + PostBeginRetroFrameIfNeeded(); |
| } |
| // BeginRetroFrame is called for BeginFrames that we've deferred because |
| @@ -458,18 +409,24 @@ void Scheduler::BeginRetroFrame() { |
| // up, our fallback should be to lower our frame rate. |
| base::TimeTicks now = Now(); |
| base::TimeDelta draw_duration_estimate = client_->DrawDurationEstimate(); |
| - while (!begin_retro_frame_args_.empty() && |
| - now > AdjustedBeginImplFrameDeadline(begin_retro_frame_args_.front(), |
| - draw_duration_estimate)) { |
| - TRACE_EVENT1("cc", |
| - "Scheduler::BeginRetroFrame discarding", |
| - "frame_time", |
| - begin_retro_frame_args_.front().frame_time); |
| + while (!begin_retro_frame_args_.empty()) { |
| + base::TimeTicks adjusted_deadline = AdjustedBeginImplFrameDeadline( |
| + begin_retro_frame_args_.front(), draw_duration_estimate); |
| + if (now <= adjusted_deadline) |
| + break; |
| + |
| + TRACE_EVENT_INSTANT2("cc", |
| + "Scheduler::BeginRetroFrame discarding", |
| + TRACE_EVENT_SCOPE_THREAD, |
| + "deadline - now", |
| + (adjusted_deadline - now).InMicroseconds(), |
| + "BeginFrameArgs", |
| + begin_retro_frame_args_.front().AsValue()); |
| begin_retro_frame_args_.pop_front(); |
| + frame_source_->DidFinishFrame(begin_retro_frame_args_.size()); |
| } |
| if (begin_retro_frame_args_.empty()) { |
| - DCHECK(settings_.throttle_frame_production); |
| TRACE_EVENT_INSTANT0("cc", |
| "Scheduler::BeginRetroFrames all expired", |
| TRACE_EVENT_SCOPE_THREAD); |
| @@ -488,7 +445,7 @@ void Scheduler::PostBeginRetroFrameIfNeeded() { |
| "Scheduler::PostBeginRetroFrameIfNeeded", |
| "state", |
| AsValue()); |
| - if (!last_set_needs_begin_frame_) |
| + if (!frame_source_->NeedsBeginFrames()) |
| return; |
| if (begin_retro_frame_args_.empty() || begin_retro_frame_posted_) |
| @@ -706,21 +663,18 @@ void Scheduler::AsValueInto(base::debug::TracedValue* state) const { |
| state->BeginDictionary("state_machine"); |
| state_machine_.AsValueInto(state, Now()); |
| state->EndDictionary(); |
| - if (synthetic_begin_frame_source_) { |
| - state->BeginDictionary("synthetic_begin_frame_source_"); |
| - synthetic_begin_frame_source_->AsValueInto(state); |
| - state->EndDictionary(); |
| - } |
| + |
| + state->BeginDictionary("frame_source_"); |
| + frame_source_->AsValueInto(state); |
| + state->EndDictionary(); |
| state->BeginDictionary("scheduler_state"); |
| state->SetDouble("time_until_anticipated_draw_time_ms", |
| (AnticipatedDrawTime() - Now()).InMillisecondsF()); |
| - state->SetDouble("vsync_interval_ms", vsync_interval_.InMillisecondsF()); |
| state->SetDouble("estimated_parent_draw_time_ms", |
| estimated_parent_draw_time_.InMillisecondsF()); |
| - state->SetBoolean("last_set_needs_begin_frame_", last_set_needs_begin_frame_); |
| - state->SetBoolean("begin_unthrottled_frame_posted_", |
| - begin_unthrottled_frame_posted_); |
| + state->SetBoolean("last_set_needs_begin_frame_", |
| + frame_source_->NeedsBeginFrames()); |
| state->SetBoolean("begin_retro_frame_posted_", begin_retro_frame_posted_); |
| state->SetInteger("begin_retro_frame_args_", begin_retro_frame_args_.size()); |
| state->SetBoolean("begin_impl_frame_deadline_task_", |