1// Copyright 2013 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 "ui/gl/sync_control_vsync_provider.h"
6
7#include <math.h>
8
9#include "base/logging.h"
10#include "base/time/time.h"
11
12#if defined(OS_LINUX)
13// These constants define a reasonable range for a calculated refresh interval.
14// Calculating refreshes out of this range will be considered a fatal error.
15const int64 kMinVsyncIntervalUs = base::Time::kMicrosecondsPerSecond / 400;
16const int64 kMaxVsyncIntervalUs = base::Time::kMicrosecondsPerSecond / 10;
17
18// How much noise we'll tolerate between successive computed intervals before
19// we think the latest computed interval is invalid (noisey due to
20// monitor configuration change, moving a window between monitors, etc.).
21const double kRelativeIntervalDifferenceThreshold = 0.05;
22#endif
23
24namespace gfx {
25
26SyncControlVSyncProvider::SyncControlVSyncProvider()
27    : VSyncProvider(), last_media_stream_counter_(0), invalid_msc_(false) {
28  // On platforms where we can't get an accurate reading on the refresh
29  // rate we fall back to the assumption that we're displaying 60 frames
30  // per second.
31  last_good_interval_ = base::TimeDelta::FromSeconds(1) / 60;
32}
33
34SyncControlVSyncProvider::~SyncControlVSyncProvider() {}
35
36void SyncControlVSyncProvider::GetVSyncParameters(
37    const UpdateVSyncCallback& callback) {
38#if defined(OS_LINUX)
39  base::TimeTicks timebase;
40
41  // The actual clock used for the system time returned by glXGetSyncValuesOML
42  // is unspecified. In practice, the clock used is likely to be either
43  // CLOCK_REALTIME or CLOCK_MONOTONIC, so we compare the returned time to the
44  // current time according to both clocks, and assume that the returned time
45  // was produced by the clock whose current time is closest to it, subject
46  // to the restriction that the returned time must not be in the future
47  // (since it is the time of a vblank that has already occurred).
48  int64 system_time;
49  int64 media_stream_counter;
50  int64 swap_buffer_counter;
51  if (!GetSyncValues(&system_time, &media_stream_counter, &swap_buffer_counter))
52    return;
53
54  // Both Intel and Mali drivers will return TRUE for GetSyncValues
55  // but a value of 0 for MSC if they cannot access the CRTC data structure
56  // associated with the surface. crbug.com/231945
57  bool prev_invalid_msc = invalid_msc_;
58  invalid_msc_ = (media_stream_counter == 0);
59  if (invalid_msc_) {
60    LOG_IF(ERROR, !prev_invalid_msc) << "glXGetSyncValuesOML "
61        "should not return TRUE with a media stream counter of 0.";
62    return;
63  }
64
65  struct timespec real_time;
66  struct timespec monotonic_time;
67  clock_gettime(CLOCK_REALTIME, &real_time);
68  clock_gettime(CLOCK_MONOTONIC, &monotonic_time);
69
70  int64 real_time_in_microseconds =
71      real_time.tv_sec * base::Time::kMicrosecondsPerSecond +
72      real_time.tv_nsec / base::Time::kNanosecondsPerMicrosecond;
73  int64 monotonic_time_in_microseconds =
74      monotonic_time.tv_sec * base::Time::kMicrosecondsPerSecond +
75      monotonic_time.tv_nsec / base::Time::kNanosecondsPerMicrosecond;
76
77  // We need the time according to CLOCK_MONOTONIC, so if we've been given
78  // a time from CLOCK_REALTIME, we need to convert.
79  bool time_conversion_needed =
80      llabs(system_time - real_time_in_microseconds) <
81      llabs(system_time - monotonic_time_in_microseconds);
82
83  if (time_conversion_needed)
84    system_time += monotonic_time_in_microseconds - real_time_in_microseconds;
85
86  // Return if |system_time| is more than 1 frames in the future.
87  int64 interval_in_microseconds = last_good_interval_.InMicroseconds();
88  if (system_time > monotonic_time_in_microseconds + interval_in_microseconds)
89    return;
90
91  // If |system_time| is slightly in the future, adjust it to the previous
92  // frame and use the last frame counter to prevent issues in the callback.
93  if (system_time > monotonic_time_in_microseconds) {
94    system_time -= interval_in_microseconds;
95    media_stream_counter--;
96  }
97  if (monotonic_time_in_microseconds - system_time >
98      base::Time::kMicrosecondsPerSecond)
99    return;
100
101  timebase = base::TimeTicks::FromInternalValue(system_time);
102
103  // Only need the previous calculated interval for our filtering.
104  while (last_computed_intervals_.size() > 1)
105    last_computed_intervals_.pop();
106
107  int32 numerator, denominator;
108  if (GetMscRate(&numerator, &denominator)) {
109    last_computed_intervals_.push(base::TimeDelta::FromSeconds(denominator) /
110                                  numerator);
111  } else if (!last_timebase_.is_null()) {
112    base::TimeDelta timebase_diff = timebase - last_timebase_;
113    int64 counter_diff = media_stream_counter - last_media_stream_counter_;
114    if (counter_diff > 0 && timebase > last_timebase_)
115      last_computed_intervals_.push(timebase_diff / counter_diff);
116  }
117
118  if (last_computed_intervals_.size() == 2) {
119    const base::TimeDelta& old_interval = last_computed_intervals_.front();
120    const base::TimeDelta& new_interval = last_computed_intervals_.back();
121
122    double relative_change =
123        fabs(old_interval.InMillisecondsF() - new_interval.InMillisecondsF()) /
124        new_interval.InMillisecondsF();
125    if (relative_change < kRelativeIntervalDifferenceThreshold) {
126      if (new_interval.InMicroseconds() < kMinVsyncIntervalUs ||
127          new_interval.InMicroseconds() > kMaxVsyncIntervalUs) {
128#if defined(USE_ASH)
129        // On ash platforms (ChromeOS essentially), the real refresh interval is
130        // queried from XRandR, regardless of the value calculated here, and
131        // this value is overriden by ui::CompositorVSyncManager.  The log
132        // should not be fatal in this case. Reconsider all this when XRandR
133        // support is added to non-ash platforms.
134        // http://crbug.com/340851
135        LOG(ERROR)
136#else
137        LOG(FATAL)
138#endif  // USE_ASH
139            << "Calculated bogus refresh interval="
140            << new_interval.InMicroseconds()
141            << " us., last_timebase_=" << last_timebase_.ToInternalValue()
142            << " us., timebase=" << timebase.ToInternalValue()
143            << " us., last_media_stream_counter_=" << last_media_stream_counter_
144            << ", media_stream_counter=" << media_stream_counter;
145      } else {
146        last_good_interval_ = new_interval;
147      }
148    }
149  }
150
151  last_timebase_ = timebase;
152  last_media_stream_counter_ = media_stream_counter;
153  callback.Run(timebase, last_good_interval_);
154#endif  // defined(OS_LINUX)
155}
156
157}  // namespace gfx
158