summaryrefslogtreecommitdiffstats
path: root/ui/gl/vsync_provider_win.cc
blob: ab76a966c642a19dbbfd098d4d4d3f2d7e3320f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// 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 "ui/gl/vsync_provider_win.h"

#include <dwmapi.h>

#include "base/logging.h"
#include "base/trace_event/trace_event.h"
#include "base/win/windows_version.h"
#include "ui/gfx/native_widget_types.h"

namespace gfx {

namespace {
bool g_use_dwm_vsync;
}  // namespace

VSyncProviderWin::VSyncProviderWin(gfx::AcceleratedWidget window)
    : window_(window) {
}

VSyncProviderWin::~VSyncProviderWin() {}

// static
void VSyncProviderWin::InitializeOneOff() {
  static bool initialized = false;
  if (initialized)
    return;
  initialized = true;
  g_use_dwm_vsync = (base::win::GetVersion() >= base::win::VERSION_WIN7);

  if (g_use_dwm_vsync) {
    // Prewarm sandbox
    ::LoadLibrary(L"dwmapi.dll");
  }
}

void VSyncProviderWin::GetVSyncParameters(const UpdateVSyncCallback& callback) {
  TRACE_EVENT0("gpu", "WinVSyncProvider::GetVSyncParameters");

  base::TimeTicks timebase;
  base::TimeDelta interval;
  bool dwm_active = false;

  // Query the DWM timing info first if available. This will provide the most
  // precise values.
  if (g_use_dwm_vsync) {
    DWM_TIMING_INFO timing_info;
    timing_info.cbSize = sizeof(timing_info);
    HRESULT result = DwmGetCompositionTimingInfo(NULL, &timing_info);
    if (result == S_OK) {
      dwm_active = true;

      // Calculate an interval value using the rateRefresh numerator and
      // denominator.
      base::TimeDelta rate_interval;
      if (timing_info.rateRefresh.uiDenominator > 0 &&
          timing_info.rateRefresh.uiNumerator > 0) {
        // Swap the numerator/denominator to convert frequency to period.
        rate_interval = base::TimeDelta::FromMicroseconds(
            timing_info.rateRefresh.uiDenominator *
            base::Time::kMicrosecondsPerSecond /
            timing_info.rateRefresh.uiNumerator);
      }

      if (base::TimeTicks::IsHighResolution()) {
        // qpcRefreshPeriod is very accurate but noisy, and must be used with
        // a high resolution timebase to avoid frequently missing Vsync.
        timebase = base::TimeTicks::FromQPCValue(
            static_cast<LONGLONG>(timing_info.qpcVBlank));
        interval = base::TimeDelta::FromQPCValue(
            static_cast<LONGLONG>(timing_info.qpcRefreshPeriod));
        // Check for interval values that are impossibly low. A 29 microsecond
        // interval was seen (from a qpcRefreshPeriod of 60).
        if (interval < base::TimeDelta::FromMilliseconds(1)) {
          interval = rate_interval;
        }
        // Check for the qpcRefreshPeriod interval being improbably small
        // compared to the rateRefresh calculated interval, as another
        // attempt at detecting driver bugs.
        if (!rate_interval.is_zero() && interval < rate_interval / 2) {
          interval = rate_interval;
        }
      } else {
        // If FrameTime is not high resolution, we do not want to translate
        // the QPC value provided by DWM into the low-resolution timebase,
        // which would be error prone and jittery. As a fallback, we assume
        // the timebase is zero and use rateRefresh, which may be rounded but
        // isn't noisy like qpcRefreshPeriod, instead. The fact that we don't
        // have a timebase here may lead to brief periods of jank when our
        // scheduling becomes offset from the hardware vsync.
        interval = rate_interval;
      }
    }
  }

  if (!dwm_active) {
    // When DWM compositing is active all displays are normalized to the
    // refresh rate of the primary display, and won't composite any faster.
    // If DWM compositing is disabled, though, we can use the refresh rates
    // reported by each display, which will help systems that have mis-matched
    // displays that run at different frequencies.
    HMONITOR monitor = MonitorFromWindow(window_, MONITOR_DEFAULTTONEAREST);
    MONITORINFOEX monitor_info;
    monitor_info.cbSize = sizeof(MONITORINFOEX);
    BOOL result = GetMonitorInfo(monitor, &monitor_info);
    if (result) {
      DEVMODE display_info;
      display_info.dmSize = sizeof(DEVMODE);
      display_info.dmDriverExtra = 0;
      result = EnumDisplaySettings(monitor_info.szDevice, ENUM_CURRENT_SETTINGS,
                                   &display_info);
      if (result && display_info.dmDisplayFrequency > 1) {
        interval = base::TimeDelta::FromMicroseconds(
            (1.0 / static_cast<double>(display_info.dmDisplayFrequency)) *
            base::Time::kMicrosecondsPerSecond);
      }
    }
  }

  if (interval.ToInternalValue() != 0) {
    callback.Run(timebase, interval);
  }
}

}  // namespace gfx