OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "services/gfx/compositor/backend/vsync_scheduler.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/location.h" |
| 9 #include "base/logging.h" |
| 10 #include "base/time/time.h" |
| 11 |
| 12 namespace compositor { |
| 13 |
| 14 VsyncScheduler::VsyncScheduler( |
| 15 const scoped_refptr<base::TaskRunner>& task_runner, |
| 16 const SchedulerCallbacks& callbacks) |
| 17 : VsyncScheduler(task_runner, callbacks, base::Bind(&MojoGetTimeTicksNow)) { |
| 18 } |
| 19 |
| 20 VsyncScheduler::VsyncScheduler( |
| 21 const scoped_refptr<base::TaskRunner>& task_runner, |
| 22 const SchedulerCallbacks& callbacks, |
| 23 const Clock& clock) |
| 24 : state_(std::make_shared<State>(task_runner, callbacks, clock)) {} |
| 25 |
| 26 VsyncScheduler::~VsyncScheduler() {} |
| 27 |
| 28 void VsyncScheduler::ScheduleFrame(SchedulingMode scheduling_mode) { |
| 29 state_->ScheduleFrame(scheduling_mode); |
| 30 } |
| 31 |
| 32 VsyncScheduler::State::State(const scoped_refptr<base::TaskRunner>& task_runner, |
| 33 const SchedulerCallbacks& callbacks, |
| 34 const Clock& clock) |
| 35 : task_runner_(task_runner), callbacks_(callbacks), clock_(clock) {} |
| 36 |
| 37 VsyncScheduler::State::~State() {} |
| 38 |
| 39 bool VsyncScheduler::State::Start(int64_t vsync_timebase, |
| 40 int64_t vsync_interval, |
| 41 int64_t update_phase, |
| 42 int64_t snapshot_phase, |
| 43 int64_t presentation_phase) { |
| 44 // Be slightly paranoid. Timing glitches are hard to find and the |
| 45 // vsync parameters will typically come from other services. |
| 46 // Ensure vsync timing is anchored on actual observations from the past. |
| 47 MojoTimeTicks now = GetTimeTicksNow(); |
| 48 if (vsync_timebase > now) { |
| 49 DVLOG(1) << "Vsync timebase is in the future: vsync_timebase=" |
| 50 << vsync_timebase << ", now=" << now; |
| 51 return false; |
| 52 } |
| 53 if (vsync_interval < kMinVsyncInterval || |
| 54 vsync_interval > kMaxVsyncInterval) { |
| 55 DVLOG(1) << "Vsync interval is invalid: vsync_interval=" << vsync_interval |
| 56 << ", min=" << kMinVsyncInterval << ", max=" << kMaxVsyncInterval; |
| 57 return false; |
| 58 } |
| 59 if (snapshot_phase < update_phase || |
| 60 snapshot_phase > update_phase + vsync_interval || |
| 61 presentation_phase < snapshot_phase) { |
| 62 // Updating and snapshotting must happen within the same frame interval |
| 63 // to avoid having multiple updates in progress simultanteously (which |
| 64 // doesn't make much sense if we're already compute bound). |
| 65 DVLOG(1) << "Vsync scheduling phases are invalid: update_phase=" |
| 66 << update_phase << ", snapshot_phase=" << snapshot_phase |
| 67 << ", presentation_phase=" << presentation_phase; |
| 68 return false; |
| 69 } |
| 70 |
| 71 { |
| 72 std::lock_guard<std::mutex> lock(mutex_); |
| 73 |
| 74 // Suppress spurious updates. |
| 75 if (running_ && vsync_timebase_ == vsync_timebase && |
| 76 vsync_interval_ == vsync_interval && update_phase_ == update_phase && |
| 77 snapshot_phase_ == snapshot_phase && |
| 78 presentation_phase_ == presentation_phase) |
| 79 return true; |
| 80 |
| 81 // Get running with these new parameters. |
| 82 // Note that |last_delivered_update_time_| is preserved. |
| 83 running_ = true; |
| 84 generation_++; // cancels pending undelivered callbacks |
| 85 vsync_timebase_ = vsync_timebase; |
| 86 vsync_interval_ = vsync_interval; |
| 87 update_phase_ = update_phase; |
| 88 snapshot_phase_ = snapshot_phase; |
| 89 presentation_phase_ = presentation_phase; |
| 90 need_update_ = true; |
| 91 pending_dispatch_ = false; |
| 92 ScheduleLocked(now); |
| 93 return true; |
| 94 } |
| 95 } |
| 96 |
| 97 void VsyncScheduler::State::Stop() { |
| 98 std::lock_guard<std::mutex> lock(mutex_); |
| 99 running_ = false; |
| 100 } |
| 101 |
| 102 void VsyncScheduler::State::ScheduleFrame(SchedulingMode scheduling_mode) { |
| 103 MojoTimeTicks now = GetTimeTicksNow(); |
| 104 |
| 105 { |
| 106 std::lock_guard<std::mutex> lock(mutex_); |
| 107 if (running_) { |
| 108 if (scheduling_mode == SchedulingMode::kUpdateAndSnapshot) |
| 109 need_update_ = true; |
| 110 ScheduleLocked(now); |
| 111 } |
| 112 } |
| 113 } |
| 114 |
| 115 void VsyncScheduler::State::ScheduleLocked(MojoTimeTicks now) { |
| 116 DCHECK(running_); |
| 117 DCHECK(now >= vsync_timebase_); |
| 118 DVLOG(2) << "schedule: now=" << now << ", need_update_=" << need_update_ |
| 119 << ", last_delivered_update_time_=" << last_delivered_update_time_; |
| 120 if (pending_dispatch_) |
| 121 return; |
| 122 |
| 123 // Determine the time of the earliest achievable frame snapshot in |
| 124 // the near future. |
| 125 int64_t snapshot_timebase = vsync_timebase_ + snapshot_phase_; |
| 126 uint64_t snapshot_offset = (now - snapshot_timebase) % vsync_interval_; |
| 127 int64_t snapshot_time = now - snapshot_offset + vsync_interval_; |
| 128 DCHECK(snapshot_time >= now); |
| 129 |
| 130 // Determine when the update that produced this snapshot must have begun. |
| 131 // This time may be in the past. |
| 132 int64_t update_time = snapshot_time - snapshot_phase_ + update_phase_; |
| 133 DCHECK(update_time <= snapshot_time); |
| 134 |
| 135 // When changing vsync parameters, it's possible for the next update time |
| 136 // to regress. Prevent applications from observing that. |
| 137 if (update_time <= last_delivered_update_time_) { |
| 138 int64_t frames = |
| 139 (last_delivered_update_time_ - update_time) / vsync_interval_ + 1; |
| 140 int64_t adjustment = frames * vsync_interval_; |
| 141 update_time += adjustment; |
| 142 snapshot_time += adjustment; |
| 143 } |
| 144 |
| 145 // Schedule dispatching at that time. |
| 146 if (update_time >= now) { |
| 147 PostDispatchLocked(now, update_time, Action::kUpdate, update_time); |
| 148 } else { |
| 149 PostDispatchLocked(now, snapshot_time, Action::kEarlySnapshot, update_time); |
| 150 } |
| 151 |
| 152 pending_dispatch_ = true; |
| 153 } |
| 154 |
| 155 void VsyncScheduler::State::PostDispatchLocked(int64_t now, |
| 156 int64_t delivery_time, |
| 157 Action action, |
| 158 int64_t update_time) { |
| 159 DVLOG(2) << "post: now=" << now << ", delivery_time=" << delivery_time |
| 160 << ", action=" << static_cast<int>(action) |
| 161 << ", update_time=" << update_time; |
| 162 |
| 163 task_runner_->PostDelayedTask( |
| 164 FROM_HERE, |
| 165 base::Bind(&VsyncScheduler::State::DispatchThunk, shared_from_this(), |
| 166 generation_, action, update_time), |
| 167 base::TimeDelta::FromMicroseconds(delivery_time - now)); |
| 168 } |
| 169 |
| 170 void VsyncScheduler::State::DispatchThunk( |
| 171 const std::weak_ptr<State>& state_weak, |
| 172 int32_t generation, |
| 173 Action action, |
| 174 int64_t update_time) { |
| 175 std::shared_ptr<State> state = state_weak.lock(); |
| 176 if (state) |
| 177 state->Dispatch(generation, action, update_time); |
| 178 } |
| 179 |
| 180 void VsyncScheduler::State::Dispatch(int32_t generation, |
| 181 Action action, |
| 182 int64_t update_time) { |
| 183 MojoTimeTicks now = GetTimeTicksNow(); |
| 184 DCHECK(update_time <= now); |
| 185 |
| 186 // Time may have passed since the callback was originally scheduled and |
| 187 // it's possible that we completely missed the deadline we were aiming for. |
| 188 // Reevaluate the schedule and jump ahead if necessary. |
| 189 mojo::gfx::composition::FrameInfo frame_info; |
| 190 { |
| 191 std::lock_guard<std::mutex> lock(mutex_); |
| 192 if (!running_ || generation_ != generation) |
| 193 return; |
| 194 |
| 195 DCHECK(pending_dispatch_); |
| 196 |
| 197 // Check whether we missed any deadlines. |
| 198 bool missed_deadline = false; |
| 199 if (action == Action::kUpdate) { |
| 200 int64_t update_deadline = update_time - update_phase_ + snapshot_phase_; |
| 201 if (now > update_deadline) { |
| 202 LOG(WARNING) << "Compositor missed update deadline by " |
| 203 << (now - update_deadline) << " us"; |
| 204 missed_deadline = true; |
| 205 } |
| 206 } else { |
| 207 int64_t snapshot_deadline = update_time + vsync_interval_; |
| 208 if (now > snapshot_deadline) { |
| 209 LOG(WARNING) << "Compositor missed snapshot deadline by " |
| 210 << (now - snapshot_deadline) << " us"; |
| 211 missed_deadline = true; |
| 212 } |
| 213 } |
| 214 if (missed_deadline) { |
| 215 uint64_t offset = (now - update_time) % vsync_interval_; |
| 216 update_time = now - offset; |
| 217 DCHECK(update_time > now - vsync_interval_ && update_time <= now); |
| 218 } |
| 219 |
| 220 DVLOG(2) << "dispatch: now=" << now |
| 221 << ", action=" << static_cast<int>(action) |
| 222 << ", update_time=" << update_time; |
| 223 |
| 224 // Schedule the corresponding snapshot for the update. |
| 225 if (action == Action::kUpdate) { |
| 226 int64_t snapshot_time = update_time - update_phase_ + snapshot_phase_; |
| 227 PostDispatchLocked(now, snapshot_time, Action::kLateSnapshot, |
| 228 update_time); |
| 229 need_update_ = false; |
| 230 } else if (need_update_) { |
| 231 int64_t next_update_time = update_time + vsync_interval_; |
| 232 PostDispatchLocked(now, next_update_time, Action::kUpdate, |
| 233 next_update_time); |
| 234 |
| 235 // If we missed the deadline on an early snapshot, then just skip it |
| 236 // and wait for the following update instead. |
| 237 if (action == Action::kEarlySnapshot && missed_deadline) { |
| 238 DVLOG(2) << "skip early snapshot"; |
| 239 return; |
| 240 } |
| 241 } else { |
| 242 pending_dispatch_ = false; |
| 243 } |
| 244 |
| 245 SetFrameInfoLocked(&frame_info, update_time); |
| 246 last_delivered_update_time_ = update_time; |
| 247 } |
| 248 |
| 249 if (action == Action::kUpdate) { |
| 250 callbacks_.update_callback.Run(frame_info); |
| 251 } else { |
| 252 callbacks_.snapshot_callback.Run(frame_info); |
| 253 } |
| 254 } |
| 255 |
| 256 void VsyncScheduler::State::SetFrameInfoLocked( |
| 257 mojo::gfx::composition::FrameInfo* frame_info, |
| 258 int64_t update_time) { |
| 259 DCHECK(frame_info); |
| 260 frame_info->frame_time = update_time; |
| 261 frame_info->frame_interval = vsync_interval_; |
| 262 frame_info->frame_deadline = update_time - update_phase_ + snapshot_phase_; |
| 263 frame_info->presentation_time = |
| 264 update_time - update_phase_ + presentation_phase_; |
| 265 } |
| 266 |
| 267 } // namespace compositor |
OLD | NEW |