| Index: gpu/ipc/service/gpu_vsync_provider_win.cc
 | 
| diff --git a/gpu/ipc/service/gpu_vsync_provider_win.cc b/gpu/ipc/service/gpu_vsync_provider_win.cc
 | 
| index 47287ce7b8b044f1ba356976e2427f2b591a1088..093f5e0eb96a55196018733cf8f7fcba1a648a0b 100644
 | 
| --- a/gpu/ipc/service/gpu_vsync_provider_win.cc
 | 
| +++ b/gpu/ipc/service/gpu_vsync_provider_win.cc
 | 
| @@ -4,6 +4,9 @@
 | 
|  
 | 
|  #include "gpu/ipc/service/gpu_vsync_provider_win.h"
 | 
|  
 | 
| +#include <dwmapi.h>
 | 
| +#include <windows.h>
 | 
| +
 | 
|  #include <string>
 | 
|  
 | 
|  #include "base/atomicops.h"
 | 
| @@ -14,15 +17,12 @@
 | 
|  #include "gpu/ipc/common/gpu_messages.h"
 | 
|  #include "ipc/ipc_message_macros.h"
 | 
|  #include "ipc/message_filter.h"
 | 
| -#include "ui/gl/vsync_provider_win.h"
 | 
| -
 | 
| -#include <windows.h>
 | 
|  
 | 
|  namespace gpu {
 | 
|  
 | 
|  namespace {
 | 
| -// Default interval used when no v-sync interval comes from DWM.
 | 
| -const int kDefaultTimerBasedInterval = 16666;
 | 
| +// Default v-sync interval used when there is no history of v-sync timestamps.
 | 
| +const int kDefaultInterval = 16666;
 | 
|  
 | 
|  // from <D3dkmthk.h>
 | 
|  typedef LONG NTSTATUS;
 | 
| @@ -79,14 +79,20 @@ class GpuVSyncWorker : public base::Thread,
 | 
|    void CloseAdapter();
 | 
|    NTSTATUS WaitForVBlankEvent();
 | 
|  
 | 
| -  void SendGpuVSyncUpdate(base::TimeTicks now,
 | 
| -                          base::TimeTicks timestamp,
 | 
| -                          base::TimeDelta interval);
 | 
| +  void AddTimestamp(base::TimeTicks timestamp);
 | 
| +  void AddInterval(base::TimeDelta interval);
 | 
| +  base::TimeDelta GetAverageInterval() const;
 | 
| +  void ClearIntervalHistory();
 | 
| +
 | 
| +  bool GetDisplayFrequency(const wchar_t* device_name, DWORD* frequency);
 | 
| +  void UpdateCurrentDisplayFrequency();
 | 
| +  bool GetDwmVBlankTimestamp(base::TimeTicks* timestamp);
 | 
| +
 | 
| +  void SendGpuVSyncUpdate(base::TimeTicks now, bool use_dwm);
 | 
| +
 | 
|    void InvokeCallbackAndReschedule(base::TimeTicks timestamp,
 | 
|                                     base::TimeDelta interval);
 | 
|    void UseDelayBasedVSyncOnError();
 | 
| -  void ScheduleDelayBasedVSync(base::TimeTicks timebase,
 | 
| -                               base::TimeDelta interval);
 | 
|  
 | 
|    // Specifies whether background tasks are running.
 | 
|    // This can be set on background thread only.
 | 
| @@ -99,9 +105,6 @@ class GpuVSyncWorker : public base::Thread,
 | 
|    const gfx::VSyncProvider::UpdateVSyncCallback callback_;
 | 
|    const SurfaceHandle surface_handle_;
 | 
|  
 | 
| -  // The actual timing and interval comes from the nested provider.
 | 
| -  std::unique_ptr<gl::VSyncProviderWin> vsync_provider_;
 | 
| -
 | 
|    PFND3DKMTOPENADAPTERFROMHDC open_adapter_from_hdc_ptr_;
 | 
|    PFND3DKMTCLOSEADAPTER close_adapter_ptr_;
 | 
|    PFND3DKMTWAITFORVERTICALBLANKEVENT wait_for_vertical_blank_event_ptr_;
 | 
| @@ -109,6 +112,30 @@ class GpuVSyncWorker : public base::Thread,
 | 
|    std::wstring current_device_name_;
 | 
|    D3DKMT_HANDLE current_adapter_handle_ = 0;
 | 
|    D3DDDI_VIDEO_PRESENT_SOURCE_ID current_source_id_ = 0;
 | 
| +
 | 
| +  // Last known v-sync timestamp.
 | 
| +  base::TimeTicks last_timestamp_;
 | 
| +
 | 
| +  // Current display refresh frequency in Hz which is used to detect
 | 
| +  // when the frequency changes and and to update the accepted interval
 | 
| +  // range below.
 | 
| +  DWORD current_display_frequency_ = 0;
 | 
| +
 | 
| +  // Range of intervals accepted for the average calculation which is
 | 
| +  // +/-20% from the interval corresponding to the display frequency above.
 | 
| +  // This is used to filter out outliers.
 | 
| +  base::TimeDelta min_accepted_interval_;
 | 
| +  base::TimeDelta max_accepted_interval_;
 | 
| +
 | 
| +  // History of recent deltas between timestamps which is used to calculate the
 | 
| +  // average v-sync interval and organized as a circular buffer.
 | 
| +  static const size_t kIntervalHistorySize = 60;
 | 
| +  base::TimeDelta interval_history_[kIntervalHistorySize];
 | 
| +  size_t history_index_ = 0;
 | 
| +  size_t history_size_ = 0;
 | 
| +
 | 
| +  // Rolling sum of intervals in the circular buffer above.
 | 
| +  base::TimeDelta rolling_interval_sum_;
 | 
|  };
 | 
|  
 | 
|  GpuVSyncWorker::GpuVSyncWorker(
 | 
| @@ -116,8 +143,7 @@ GpuVSyncWorker::GpuVSyncWorker(
 | 
|      SurfaceHandle surface_handle)
 | 
|      : base::Thread(base::StringPrintf("VSync-%d", surface_handle)),
 | 
|        callback_(callback),
 | 
| -      surface_handle_(surface_handle),
 | 
| -      vsync_provider_(new gl::VSyncProviderWin(surface_handle)) {
 | 
| +      surface_handle_(surface_handle) {
 | 
|    HMODULE gdi32 = GetModuleHandle(L"gdi32");
 | 
|    if (!gdi32) {
 | 
|      NOTREACHED() << "Can't open gdi32.dll";
 | 
| @@ -209,6 +235,14 @@ void GpuVSyncWorker::WaitForVSyncOnThread() {
 | 
|      }
 | 
|    }
 | 
|  
 | 
| +  UpdateCurrentDisplayFrequency();
 | 
| +
 | 
| +  // Use DWM timing only when running on the primary monitor which DWM
 | 
| +  // is synchronized with and only if we can get accurate high resulution
 | 
| +  // timestamps.
 | 
| +  bool use_dwm = (monitor_info.dwFlags & MONITORINFOF_PRIMARY) != 0 &&
 | 
| +                 base::TimeTicks::IsHighResolution();
 | 
| +
 | 
|    NTSTATUS wait_result = WaitForVBlankEvent();
 | 
|    if (wait_result != STATUS_SUCCESS) {
 | 
|      if (wait_result == STATUS_GRAPHICS_PRESENT_OCCLUDED) {
 | 
| @@ -221,34 +255,119 @@ void GpuVSyncWorker::WaitForVSyncOnThread() {
 | 
|      }
 | 
|    }
 | 
|  
 | 
| -  vsync_provider_->GetVSyncParameters(
 | 
| -      base::Bind(&GpuVSyncWorker::SendGpuVSyncUpdate, base::Unretained(this),
 | 
| -                 base::TimeTicks::Now()));
 | 
| +  SendGpuVSyncUpdate(base::TimeTicks::Now(), use_dwm);
 | 
| +}
 | 
| +
 | 
| +void GpuVSyncWorker::AddTimestamp(base::TimeTicks timestamp) {
 | 
| +  if (!last_timestamp_.is_null()) {
 | 
| +    AddInterval(timestamp - last_timestamp_);
 | 
| +  }
 | 
| +
 | 
| +  last_timestamp_ = timestamp;
 | 
| +}
 | 
| +
 | 
| +void GpuVSyncWorker::AddInterval(base::TimeDelta interval) {
 | 
| +  if (interval < min_accepted_interval_ || interval > max_accepted_interval_)
 | 
| +    return;
 | 
| +
 | 
| +  if (history_size_ == kIntervalHistorySize) {
 | 
| +    rolling_interval_sum_ -= interval_history_[history_index_];
 | 
| +  } else {
 | 
| +    history_size_++;
 | 
| +  }
 | 
| +
 | 
| +  interval_history_[history_index_] = interval;
 | 
| +  rolling_interval_sum_ += interval;
 | 
| +  history_index_ = (history_index_ + 1) % kIntervalHistorySize;
 | 
| +}
 | 
| +
 | 
| +void GpuVSyncWorker::ClearIntervalHistory() {
 | 
| +  last_timestamp_ = base::TimeTicks();
 | 
| +  rolling_interval_sum_ = base::TimeDelta();
 | 
| +  history_index_ = 0;
 | 
| +  history_size_ = 0;
 | 
| +}
 | 
| +
 | 
| +base::TimeDelta GpuVSyncWorker::GetAverageInterval() const {
 | 
| +  return !rolling_interval_sum_.is_zero()
 | 
| +             ? rolling_interval_sum_ / history_size_
 | 
| +             : base::TimeDelta::FromMicroseconds(kDefaultInterval);
 | 
| +}
 | 
| +
 | 
| +bool GpuVSyncWorker::GetDisplayFrequency(const wchar_t* device_name,
 | 
| +                                         DWORD* frequency) {
 | 
| +  DEVMODE display_info;
 | 
| +  display_info.dmSize = sizeof(DEVMODE);
 | 
| +  display_info.dmDriverExtra = 0;
 | 
| +
 | 
| +  BOOL result =
 | 
| +      EnumDisplaySettings(device_name, ENUM_CURRENT_SETTINGS, &display_info);
 | 
| +  if (result && display_info.dmDisplayFrequency > 1) {
 | 
| +    *frequency = display_info.dmDisplayFrequency;
 | 
| +    return true;
 | 
| +  }
 | 
| +
 | 
| +  return false;
 | 
|  }
 | 
|  
 | 
| -void GpuVSyncWorker::SendGpuVSyncUpdate(base::TimeTicks now,
 | 
| -                                        base::TimeTicks timestamp,
 | 
| -                                        base::TimeDelta interval) {
 | 
| +void GpuVSyncWorker::UpdateCurrentDisplayFrequency() {
 | 
| +  DWORD frequency;
 | 
| +  DCHECK(!current_device_name_.empty());
 | 
| +  if (!GetDisplayFrequency(current_device_name_.c_str(), &frequency)) {
 | 
| +    current_display_frequency_ = 0;
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  if (frequency != current_display_frequency_) {
 | 
| +    current_display_frequency_ = frequency;
 | 
| +    base::TimeDelta interval = base::TimeDelta::FromMicroseconds(
 | 
| +        base::Time::kMicrosecondsPerSecond / static_cast<double>(frequency));
 | 
| +    ClearIntervalHistory();
 | 
| +
 | 
| +    min_accepted_interval_ = interval * 0.8;
 | 
| +    max_accepted_interval_ = interval * 1.2;
 | 
| +    AddInterval(interval);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +bool GpuVSyncWorker::GetDwmVBlankTimestamp(base::TimeTicks* timestamp) {
 | 
| +  DWM_TIMING_INFO timing_info;
 | 
| +  timing_info.cbSize = sizeof(timing_info);
 | 
| +  HRESULT result = DwmGetCompositionTimingInfo(nullptr, &timing_info);
 | 
| +  if (result != S_OK)
 | 
| +    return false;
 | 
| +
 | 
| +  *timestamp = base::TimeTicks::FromQPCValue(
 | 
| +      static_cast<LONGLONG>(timing_info.qpcVBlank));
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +void GpuVSyncWorker::SendGpuVSyncUpdate(base::TimeTicks now, bool use_dwm) {
 | 
| +  base::TimeTicks timestamp;
 | 
|    base::TimeDelta adjustment;
 | 
|  
 | 
| -  if (!(timestamp.is_null() || interval.is_zero())) {
 | 
| +  if (use_dwm && GetDwmVBlankTimestamp(×tamp)) {
 | 
|      // Timestamp comes from DwmGetCompositionTimingInfo and apparently it might
 | 
|      // be up to 2-3 vsync cycles in the past or in the future.
 | 
|      // The adjustment formula was suggested here:
 | 
|      // http://www.vsynctester.com/firefoxisbroken.html
 | 
| +    base::TimeDelta interval = GetAverageInterval();
 | 
|      adjustment =
 | 
|          ((now - timestamp + interval / 8) % interval + interval) % interval -
 | 
|          interval / 8;
 | 
|      timestamp = now - adjustment;
 | 
|    } else {
 | 
| -    // DWM must be disabled.
 | 
| +    // Not using DWM.
 | 
|      timestamp = now;
 | 
|    }
 | 
|  
 | 
| +  AddTimestamp(timestamp);
 | 
| +
 | 
|    TRACE_EVENT1("gpu", "GpuVSyncWorker::SendGpuVSyncUpdate", "adjustment",
 | 
|                 adjustment.ToInternalValue());
 | 
|  
 | 
| -  InvokeCallbackAndReschedule(timestamp, interval);
 | 
| +  DCHECK_GT(GetAverageInterval().InMillisecondsF(), 0);
 | 
| +  InvokeCallbackAndReschedule(timestamp, GetAverageInterval());
 | 
|  }
 | 
|  
 | 
|  void GpuVSyncWorker::InvokeCallbackAndReschedule(base::TimeTicks timestamp,
 | 
| @@ -261,6 +380,8 @@ void GpuVSyncWorker::InvokeCallbackAndReschedule(base::TimeTicks timestamp,
 | 
|                                         base::Unretained(this)));
 | 
|    } else {
 | 
|      running_ = false;
 | 
| +    // Clear last_timestamp_ to avoid a long interval when the worker restarts.
 | 
| +    last_timestamp_ = base::TimeTicks();
 | 
|    }
 | 
|  }
 | 
|  
 | 
| @@ -269,18 +390,11 @@ void GpuVSyncWorker::UseDelayBasedVSyncOnError() {
 | 
|    // Use timer based mechanism as a backup for one v-sync cycle, start with
 | 
|    // getting VSync parameters to determine timebase and interval.
 | 
|    // TODO(stanisc): Consider a slower v-sync rate in this particular case.
 | 
| -  vsync_provider_->GetVSyncParameters(base::Bind(
 | 
| -      &GpuVSyncWorker::ScheduleDelayBasedVSync, base::Unretained(this)));
 | 
| -}
 | 
|  
 | 
| -void GpuVSyncWorker::ScheduleDelayBasedVSync(base::TimeTicks timebase,
 | 
| -                                             base::TimeDelta interval) {
 | 
| -  // This is called only when WaitForVBlankEvent fails due to monitor going to
 | 
| -  // sleep.  Use a delay based v-sync as a back-up.
 | 
| -  if (interval.is_zero()) {
 | 
| -    interval = base::TimeDelta::FromMicroseconds(kDefaultTimerBasedInterval);
 | 
| -  }
 | 
| +  base::TimeTicks timebase;
 | 
| +  GetDwmVBlankTimestamp(&timebase);
 | 
|  
 | 
| +  base::TimeDelta interval = GetAverageInterval();
 | 
|    base::TimeTicks now = base::TimeTicks::Now();
 | 
|    base::TimeTicks next_vsync = now.SnappedToNextTick(timebase, interval);
 | 
|  
 | 
| @@ -327,6 +441,8 @@ void GpuVSyncWorker::CloseAdapter() {
 | 
|  
 | 
|      current_adapter_handle_ = 0;
 | 
|      current_device_name_.clear();
 | 
| +
 | 
| +    ClearIntervalHistory();
 | 
|    }
 | 
|  }
 | 
|  
 | 
| 
 |