// Copyright 2014 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/begin_frame_source.h" #include "base/auto_reset.h" #include "base/location.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" #include "cc/scheduler/delay_based_time_source.h" #include "cc/scheduler/scheduler.h" #include "ui/gfx/frame_time.h" #ifdef NDEBUG #define DEBUG_FRAMES(...) #else #define DEBUG_FRAMES(name, arg1_name, arg1_val, arg2_name, arg2_val) \ TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("cc.debug.scheduler.frames"), \ name, \ arg1_name, \ arg1_val, \ arg2_name, \ arg2_val); #endif namespace cc { // BeginFrameObserverMixIn ----------------------------------------------- BeginFrameObserverMixIn::BeginFrameObserverMixIn() : last_begin_frame_args_(), dropped_begin_frame_args_(0) { } const BeginFrameArgs BeginFrameObserverMixIn::LastUsedBeginFrameArgs() const { return last_begin_frame_args_; } void BeginFrameObserverMixIn::OnBeginFrame(const BeginFrameArgs& args) { DEBUG_FRAMES("BeginFrameObserverMixIn::OnBeginFrame", "last args", last_begin_frame_args_.AsValue(), "new args", args.AsValue()); DCHECK(args.IsValid()); DCHECK(args.frame_time >= last_begin_frame_args_.frame_time); bool used = OnBeginFrameMixInDelegate(args); if (used) { last_begin_frame_args_ = args; } else { ++dropped_begin_frame_args_; } } void BeginFrameObserverMixIn::AsValueInto( base::trace_event::TracedValue* dict) const { dict->BeginDictionary("last_begin_frame_args_"); last_begin_frame_args_.AsValueInto(dict); dict->EndDictionary(); dict->SetInteger("dropped_begin_frame_args_", dropped_begin_frame_args_); } // BeginFrameSourceMixIn ------------------------------------------------------ BeginFrameSourceMixIn::BeginFrameSourceMixIn() : observer_(NULL), needs_begin_frames_(false), inside_as_value_into_(false) { DCHECK(!observer_); DCHECK_EQ(inside_as_value_into_, false); } bool BeginFrameSourceMixIn::NeedsBeginFrames() const { return needs_begin_frames_; } void BeginFrameSourceMixIn::SetNeedsBeginFrames(bool needs_begin_frames) { DEBUG_FRAMES("BeginFrameSourceMixIn::SetNeedsBeginFrames", "current state", needs_begin_frames_, "new state", needs_begin_frames); if (needs_begin_frames_ != needs_begin_frames) { needs_begin_frames_ = needs_begin_frames; OnNeedsBeginFramesChange(needs_begin_frames); } } void BeginFrameSourceMixIn::AddObserver(BeginFrameObserver* obs) { DEBUG_FRAMES("BeginFrameSourceMixIn::AddObserver", "current observer", observer_, "to add observer", obs); DCHECK(!observer_); observer_ = obs; } void BeginFrameSourceMixIn::RemoveObserver(BeginFrameObserver* obs) { DEBUG_FRAMES("BeginFrameSourceMixIn::RemoveObserver", "current observer", observer_, "to remove observer", obs); DCHECK_EQ(observer_, obs); observer_ = NULL; } void BeginFrameSourceMixIn::CallOnBeginFrame(const BeginFrameArgs& args) { DEBUG_FRAMES("BeginFrameSourceMixIn::CallOnBeginFrame", "current observer", observer_, "args", args.AsValue()); if (observer_) { return observer_->OnBeginFrame(args); } } // Tracing support void BeginFrameSourceMixIn::AsValueInto( base::trace_event::TracedValue* dict) const { // As the observer might try to trace the source, prevent an infinte loop // from occuring. if (inside_as_value_into_) { dict->SetString("observer", ""); return; } if (observer_) { base::AutoReset prevent_loops( const_cast(&inside_as_value_into_), true); dict->BeginDictionary("observer"); observer_->AsValueInto(dict); dict->EndDictionary(); } else { dict->SetString("observer", "NULL"); } dict->SetBoolean("needs_begin_frames", NeedsBeginFrames()); } // BackToBackBeginFrameSourceMixIn -------------------------------------------- scoped_ptr BackToBackBeginFrameSource::Create( base::SingleThreadTaskRunner* task_runner) { return make_scoped_ptr(new BackToBackBeginFrameSource(task_runner)); } BackToBackBeginFrameSource::BackToBackBeginFrameSource( base::SingleThreadTaskRunner* task_runner) : BeginFrameSourceMixIn(), task_runner_(task_runner), send_begin_frame_posted_(false), weak_factory_(this) { DCHECK(task_runner); DCHECK_EQ(needs_begin_frames_, false); DCHECK_EQ(send_begin_frame_posted_, false); } BackToBackBeginFrameSource::~BackToBackBeginFrameSource() { } base::TimeTicks BackToBackBeginFrameSource::Now() { return gfx::FrameTime::Now(); } void BackToBackBeginFrameSource::OnNeedsBeginFramesChange( bool needs_begin_frames) { if (!needs_begin_frames) return; if (send_begin_frame_posted_) return; send_begin_frame_posted_ = true; task_runner_->PostTask(FROM_HERE, base::Bind(&BackToBackBeginFrameSource::BeginFrame, weak_factory_.GetWeakPtr())); } void BackToBackBeginFrameSource::BeginFrame() { send_begin_frame_posted_ = false; if (!needs_begin_frames_) return; base::TimeTicks now = Now(); BeginFrameArgs args = BeginFrameArgs::Create( BEGINFRAME_FROM_HERE, now, now + BeginFrameArgs::DefaultInterval(), BeginFrameArgs::DefaultInterval(), BeginFrameArgs::NORMAL); CallOnBeginFrame(args); } // BeginFrameSource support void BackToBackBeginFrameSource::DidFinishFrame(size_t remaining_frames) { if (remaining_frames == 0) { OnNeedsBeginFramesChange(NeedsBeginFrames()); } } // Tracing support void BackToBackBeginFrameSource::AsValueInto( base::trace_event::TracedValue* dict) const { dict->SetString("type", "BackToBackBeginFrameSource"); BeginFrameSourceMixIn::AsValueInto(dict); dict->SetBoolean("send_begin_frame_posted_", send_begin_frame_posted_); } // SyntheticBeginFrameSource --------------------------------------------- scoped_ptr SyntheticBeginFrameSource::Create( base::SingleThreadTaskRunner* task_runner, base::TimeTicks initial_vsync_timebase, base::TimeDelta initial_vsync_interval) { scoped_refptr time_source; if (gfx::FrameTime::TimestampsAreHighRes()) { time_source = DelayBasedTimeSourceHighRes::Create(initial_vsync_interval, task_runner); } else { time_source = DelayBasedTimeSource::Create(initial_vsync_interval, task_runner); } return make_scoped_ptr(new SyntheticBeginFrameSource(time_source)); } SyntheticBeginFrameSource::SyntheticBeginFrameSource( scoped_refptr time_source) : BeginFrameSourceMixIn(), time_source_(time_source) { time_source_->SetActive(false); time_source_->SetClient(this); } SyntheticBeginFrameSource::~SyntheticBeginFrameSource() { if (NeedsBeginFrames()) time_source_->SetActive(false); } void SyntheticBeginFrameSource::OnUpdateVSyncParameters( base::TimeTicks new_vsync_timebase, base::TimeDelta new_vsync_interval) { time_source_->SetTimebaseAndInterval(new_vsync_timebase, new_vsync_interval); } BeginFrameArgs SyntheticBeginFrameSource::CreateBeginFrameArgs( base::TimeTicks frame_time, BeginFrameArgs::BeginFrameArgsType type) { base::TimeTicks deadline = time_source_->NextTickTime(); return BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline, time_source_->Interval(), type); } // TimeSourceClient support void SyntheticBeginFrameSource::OnTimerTick() { CallOnBeginFrame(CreateBeginFrameArgs(time_source_->LastTickTime(), BeginFrameArgs::NORMAL)); } // BeginFrameSourceMixIn support void SyntheticBeginFrameSource::OnNeedsBeginFramesChange( bool needs_begin_frames) { base::TimeTicks missed_tick_time = time_source_->SetActive(needs_begin_frames); if (!missed_tick_time.is_null()) { CallOnBeginFrame( CreateBeginFrameArgs(missed_tick_time, BeginFrameArgs::MISSED)); } } // Tracing support void SyntheticBeginFrameSource::AsValueInto( base::trace_event::TracedValue* dict) const { dict->SetString("type", "SyntheticBeginFrameSource"); BeginFrameSourceMixIn::AsValueInto(dict); dict->BeginDictionary("time_source"); time_source_->AsValueInto(dict); dict->EndDictionary(); } // BeginFrameSourceMultiplexer ------------------------------------------- scoped_ptr BeginFrameSourceMultiplexer::Create() { return make_scoped_ptr(new BeginFrameSourceMultiplexer()); } BeginFrameSourceMultiplexer::BeginFrameSourceMultiplexer() : BeginFrameSourceMixIn(), minimum_interval_(base::TimeDelta()), active_source_(NULL), source_list_() { } BeginFrameSourceMultiplexer::BeginFrameSourceMultiplexer( base::TimeDelta minimum_interval) : BeginFrameSourceMixIn(), minimum_interval_(minimum_interval), active_source_(NULL), source_list_() { } BeginFrameSourceMultiplexer::~BeginFrameSourceMultiplexer() { } void BeginFrameSourceMultiplexer::SetMinimumInterval( base::TimeDelta new_minimum_interval) { DEBUG_FRAMES("BeginFrameSourceMultiplexer::SetMinimumInterval", "current minimum (us)", minimum_interval_.InMicroseconds(), "new minimum (us)", new_minimum_interval.InMicroseconds()); DCHECK_GE(new_minimum_interval.ToInternalValue(), 0); minimum_interval_ = new_minimum_interval; } void BeginFrameSourceMultiplexer::AddSource(BeginFrameSource* new_source) { DEBUG_FRAMES("BeginFrameSourceMultiplexer::AddSource", "current active", active_source_, "source to remove", new_source); DCHECK(new_source); DCHECK(!HasSource(new_source)); source_list_.insert(new_source); // If there is no active source, set the new one as the active one. if (!active_source_) SetActiveSource(new_source); } void BeginFrameSourceMultiplexer::RemoveSource( BeginFrameSource* existing_source) { DEBUG_FRAMES("BeginFrameSourceMultiplexer::RemoveSource", "current active", active_source_, "source to remove", existing_source); DCHECK(existing_source); DCHECK(HasSource(existing_source)); DCHECK_NE(existing_source, active_source_); source_list_.erase(existing_source); } void BeginFrameSourceMultiplexer::SetActiveSource( BeginFrameSource* new_source) { DEBUG_FRAMES("BeginFrameSourceMultiplexer::SetActiveSource", "current active", active_source_, "to become active", new_source); DCHECK(HasSource(new_source) || new_source == NULL); bool needs_begin_frames = NeedsBeginFrames(); if (active_source_) { if (needs_begin_frames) SetNeedsBeginFrames(false); // Technically we shouldn't need to remove observation, but this prevents // the case where SetNeedsBeginFrames message gets to the source after a // message has already been sent. active_source_->RemoveObserver(this); active_source_ = NULL; } DCHECK(!active_source_); active_source_ = new_source; if (active_source_) { active_source_->AddObserver(this); if (needs_begin_frames) { SetNeedsBeginFrames(true); } } } const BeginFrameSource* BeginFrameSourceMultiplexer::ActiveSource() { return active_source_; } // BeginFrameObserver support void BeginFrameSourceMultiplexer::OnBeginFrame(const BeginFrameArgs& args) { if (!IsIncreasing(args)) { DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnBeginFrame", "action", "discarding", "new args", args.AsValue()); return; } DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnBeginFrame", "action", "using", "new args", args.AsValue()); CallOnBeginFrame(args); } const BeginFrameArgs BeginFrameSourceMultiplexer::LastUsedBeginFrameArgs() const { if (observer_) return observer_->LastUsedBeginFrameArgs(); else return BeginFrameArgs(); } // BeginFrameSource support void BeginFrameSourceMultiplexer::OnNeedsBeginFramesChange( bool needs_begin_frames) { DEBUG_FRAMES("BeginFrameSourceMultiplexer::OnNeedsBeginFramesChange", "active_source", active_source_, "needs_begin_frames", needs_begin_frames); if (active_source_) { active_source_->SetNeedsBeginFrames(needs_begin_frames); } else { DCHECK(!needs_begin_frames); } } void BeginFrameSourceMultiplexer::DidFinishFrame(size_t remaining_frames) { DEBUG_FRAMES("BeginFrameSourceMultiplexer::DidFinishFrame", "active_source", active_source_, "remaining_frames", remaining_frames); if (active_source_) { active_source_->DidFinishFrame(remaining_frames); } } // Tracing support void BeginFrameSourceMultiplexer::AsValueInto( base::trace_event::TracedValue* dict) const { dict->SetString("type", "BeginFrameSourceMultiplexer"); dict->SetInteger("minimum_interval_us", minimum_interval_.InMicroseconds()); if (observer_) { dict->BeginDictionary("last_begin_frame_args"); observer_->LastUsedBeginFrameArgs().AsValueInto(dict); dict->EndDictionary(); } if (active_source_) { dict->BeginDictionary("active_source"); active_source_->AsValueInto(dict); dict->EndDictionary(); } else { dict->SetString("active_source", "NULL"); } dict->BeginArray("sources"); for (std::set::const_iterator it = source_list_.begin(); it != source_list_.end(); ++it) { dict->BeginDictionary(); (*it)->AsValueInto(dict); dict->EndDictionary(); } dict->EndArray(); } // protected methods bool BeginFrameSourceMultiplexer::HasSource(BeginFrameSource* source) { return (source_list_.find(source) != source_list_.end()); } bool BeginFrameSourceMultiplexer::IsIncreasing(const BeginFrameArgs& args) { DCHECK(args.IsValid()); if (!observer_) return false; // If the last begin frame is invalid, then any new begin frame is valid. if (!observer_->LastUsedBeginFrameArgs().IsValid()) return true; // Only allow new args have a *strictly bigger* frame_time value and statisfy // minimum interval requirement. return (args.frame_time >= observer_->LastUsedBeginFrameArgs().frame_time + minimum_interval_); } } // namespace cc