Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(90)

Side by Side Diff: services/gfx/compositor/backend/vsync_scheduler.cc

Issue 1552963002: Initial checkin of the new Mozart compositor. (Closed) Base URL: git@github.com:domokit/mojo.git@moz-11
Patch Set: Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698