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

Unified 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: fix android build 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 side-by-side diff with in-line comments
Download patch
Index: services/gfx/compositor/backend/vsync_scheduler.cc
diff --git a/services/gfx/compositor/backend/vsync_scheduler.cc b/services/gfx/compositor/backend/vsync_scheduler.cc
new file mode 100644
index 0000000000000000000000000000000000000000..71be4f33a5f840fb8a8404a1643eae74b473a3d2
--- /dev/null
+++ b/services/gfx/compositor/backend/vsync_scheduler.cc
@@ -0,0 +1,273 @@
+// Copyright 2015 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 "services/gfx/compositor/backend/vsync_scheduler.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+
+namespace compositor {
+
+constexpr int64_t VsyncScheduler::kMinVsyncInterval;
+constexpr int64_t VsyncScheduler::kMaxVsyncInterval;
+
+VsyncScheduler::VsyncScheduler(
+ const scoped_refptr<base::TaskRunner>& task_runner,
+ const SchedulerCallbacks& callbacks)
+ : VsyncScheduler(task_runner, callbacks, base::Bind(&MojoGetTimeTicksNow)) {
+}
+
+VsyncScheduler::VsyncScheduler(
+ const scoped_refptr<base::TaskRunner>& task_runner,
+ const SchedulerCallbacks& callbacks,
+ const Clock& clock)
+ : state_(std::make_shared<State>(task_runner, callbacks, clock)) {}
+
+VsyncScheduler::~VsyncScheduler() {}
+
+void VsyncScheduler::ScheduleFrame(SchedulingMode scheduling_mode) {
+ state_->ScheduleFrame(scheduling_mode);
+}
+
+VsyncScheduler::State::State(const scoped_refptr<base::TaskRunner>& task_runner,
+ const SchedulerCallbacks& callbacks,
+ const Clock& clock)
+ : task_runner_(task_runner), callbacks_(callbacks), clock_(clock) {}
+
+VsyncScheduler::State::~State() {}
+
+bool VsyncScheduler::State::Start(int64_t vsync_timebase,
+ int64_t vsync_interval,
+ int64_t update_phase,
+ int64_t snapshot_phase,
+ int64_t presentation_phase) {
+ // Be slightly paranoid. Timing glitches are hard to find and the
+ // vsync parameters will typically come from other services.
+ // Ensure vsync timing is anchored on actual observations from the past.
+ MojoTimeTicks now = GetTimeTicksNow();
+ if (vsync_timebase > now) {
+ DVLOG(1) << "Vsync timebase is in the future: vsync_timebase="
+ << vsync_timebase << ", now=" << now;
+ return false;
+ }
+ if (vsync_interval < kMinVsyncInterval ||
+ vsync_interval > kMaxVsyncInterval) {
+ DVLOG(1) << "Vsync interval is invalid: vsync_interval=" << vsync_interval
+ << ", min=" << kMinVsyncInterval << ", max=" << kMaxVsyncInterval;
+ return false;
+ }
+ if (snapshot_phase < update_phase ||
+ snapshot_phase > update_phase + vsync_interval ||
+ presentation_phase < snapshot_phase) {
+ // Updating and snapshotting must happen within the same frame interval
+ // to avoid having multiple updates in progress simultanteously (which
+ // doesn't make much sense if we're already compute bound).
+ DVLOG(1) << "Vsync scheduling phases are invalid: update_phase="
+ << update_phase << ", snapshot_phase=" << snapshot_phase
+ << ", presentation_phase=" << presentation_phase;
+ return false;
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+
+ // Suppress spurious updates.
+ if (running_ && vsync_timebase_ == vsync_timebase &&
+ vsync_interval_ == vsync_interval && update_phase_ == update_phase &&
+ snapshot_phase_ == snapshot_phase &&
+ presentation_phase_ == presentation_phase)
+ return true;
+
+ // Get running with these new parameters.
+ // Note that |last_delivered_update_time_| is preserved.
+ running_ = true;
+ generation_++; // cancels pending undelivered callbacks
+ vsync_timebase_ = vsync_timebase;
+ vsync_interval_ = vsync_interval;
+ update_phase_ = update_phase;
+ snapshot_phase_ = snapshot_phase;
+ presentation_phase_ = presentation_phase;
+ need_update_ = true;
+ pending_dispatch_ = false;
+ ScheduleLocked(now);
+ return true;
+ }
+}
+
+void VsyncScheduler::State::Stop() {
+ std::lock_guard<std::mutex> lock(mutex_);
+ running_ = false;
+}
+
+void VsyncScheduler::State::ScheduleFrame(SchedulingMode scheduling_mode) {
+ MojoTimeTicks now = GetTimeTicksNow();
+
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ if (running_) {
+ if (scheduling_mode == SchedulingMode::kUpdateAndSnapshot)
+ need_update_ = true;
+ ScheduleLocked(now);
+ }
+ }
+}
+
+void VsyncScheduler::State::ScheduleLocked(MojoTimeTicks now) {
+ DCHECK(running_);
+ DCHECK(now >= vsync_timebase_);
+ DVLOG(2) << "schedule: now=" << now << ", need_update_=" << need_update_
+ << ", last_delivered_update_time_=" << last_delivered_update_time_;
+ if (pending_dispatch_)
+ return;
+
+ // Determine the time of the earliest achievable frame snapshot in
+ // the near future.
+ int64_t snapshot_timebase = vsync_timebase_ + snapshot_phase_;
+ uint64_t snapshot_offset = (now - snapshot_timebase) % vsync_interval_;
+ int64_t snapshot_time = now - snapshot_offset + vsync_interval_;
+ DCHECK(snapshot_time >= now);
+
+ // Determine when the update that produced this snapshot must have begun.
+ // This time may be in the past.
+ int64_t update_time = snapshot_time - snapshot_phase_ + update_phase_;
+ DCHECK(update_time <= snapshot_time);
+
+ // When changing vsync parameters, it's possible for the next update time
+ // to regress. Prevent applications from observing that.
+ if (update_time <= last_delivered_update_time_) {
+ int64_t frames =
+ (last_delivered_update_time_ - update_time) / vsync_interval_ + 1;
+ int64_t adjustment = frames * vsync_interval_;
+ update_time += adjustment;
+ snapshot_time += adjustment;
+ }
+
+ // Schedule dispatching at that time.
+ if (update_time >= now) {
+ PostDispatchLocked(now, update_time, Action::kUpdate, update_time);
+ } else {
+ PostDispatchLocked(now, snapshot_time, Action::kEarlySnapshot, update_time);
+ }
+
+ pending_dispatch_ = true;
+}
+
+void VsyncScheduler::State::PostDispatchLocked(int64_t now,
+ int64_t delivery_time,
+ Action action,
+ int64_t update_time) {
+ DVLOG(2) << "post: now=" << now << ", delivery_time=" << delivery_time
+ << ", action=" << static_cast<int>(action)
+ << ", update_time=" << update_time;
+
+ task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&VsyncScheduler::State::DispatchThunk, shared_from_this(),
+ generation_, action, update_time),
+ base::TimeDelta::FromMicroseconds(
+ std::max(delivery_time - now, static_cast<int64_t>(0))));
+}
+
+void VsyncScheduler::State::DispatchThunk(
+ const std::weak_ptr<State>& state_weak,
+ int32_t generation,
+ Action action,
+ int64_t update_time) {
+ std::shared_ptr<State> state = state_weak.lock();
+ if (state)
+ state->Dispatch(generation, action, update_time);
+}
+
+void VsyncScheduler::State::Dispatch(int32_t generation,
+ Action action,
+ int64_t update_time) {
+ MojoTimeTicks now = GetTimeTicksNow();
+ DCHECK(update_time <= now);
+
+ // Time may have passed since the callback was originally scheduled and
+ // it's possible that we completely missed the deadline we were aiming for.
+ // Reevaluate the schedule and jump ahead if necessary.
+ mojo::gfx::composition::FrameInfo frame_info;
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ if (!running_ || generation_ != generation)
+ return;
+
+ DCHECK(pending_dispatch_);
+
+ // Check whether we missed any deadlines.
+ bool missed_deadline = false;
+ if (action == Action::kUpdate) {
+ int64_t update_deadline = update_time - update_phase_ + snapshot_phase_;
+ if (now > update_deadline) {
+ LOG(WARNING) << "Compositor missed update deadline by "
+ << (now - update_deadline) << " us";
+ missed_deadline = true;
+ }
+ } else {
+ int64_t snapshot_deadline = update_time + vsync_interval_;
+ if (now > snapshot_deadline) {
+ LOG(WARNING) << "Compositor missed snapshot deadline by "
+ << (now - snapshot_deadline) << " us";
+ missed_deadline = true;
+ }
+ }
+ if (missed_deadline) {
+ uint64_t offset = (now - update_time) % vsync_interval_;
+ update_time = now - offset;
+ DCHECK(update_time > now - vsync_interval_ && update_time <= now);
+ }
+
+ DVLOG(2) << "dispatch: now=" << now
+ << ", action=" << static_cast<int>(action)
+ << ", update_time=" << update_time;
+
+ // Schedule the corresponding snapshot for the update.
+ if (action == Action::kUpdate) {
+ int64_t snapshot_time = update_time - update_phase_ + snapshot_phase_;
+ PostDispatchLocked(now, snapshot_time, Action::kLateSnapshot,
+ update_time);
+ need_update_ = false;
+ } else if (need_update_) {
+ int64_t next_update_time = update_time + vsync_interval_;
+ PostDispatchLocked(now, next_update_time, Action::kUpdate,
+ next_update_time);
+
+ // If we missed the deadline on an early snapshot, then just skip it
+ // and wait for the following update instead.
+ if (action == Action::kEarlySnapshot && missed_deadline) {
+ DVLOG(2) << "skip early snapshot";
+ return;
+ }
+ } else {
+ pending_dispatch_ = false;
+ }
+
+ SetFrameInfoLocked(&frame_info, update_time);
+ last_delivered_update_time_ = update_time;
+ }
+
+ if (action == Action::kUpdate) {
+ callbacks_.update_callback.Run(frame_info);
+ } else {
+ callbacks_.snapshot_callback.Run(frame_info);
+ }
+}
+
+void VsyncScheduler::State::SetFrameInfoLocked(
+ mojo::gfx::composition::FrameInfo* frame_info,
+ int64_t update_time) {
+ DCHECK(frame_info);
+ frame_info->frame_time = update_time;
+ frame_info->frame_interval = vsync_interval_;
+ frame_info->frame_deadline = update_time - update_phase_ + snapshot_phase_;
+ frame_info->presentation_time =
+ update_time - update_phase_ + presentation_phase_;
+}
+
+} // namespace compositor
« no previous file with comments | « services/gfx/compositor/backend/vsync_scheduler.h ('k') | services/gfx/compositor/backend/vsync_scheduler_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698