OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/gl/vsync_provider_win.h" | 5 #include "ui/gl/vsync_provider_win.h" |
6 | 6 |
7 #include <dwmapi.h> | 7 #include <dwmapi.h> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/trace_event/trace_event.h" | 10 #include "base/trace_event/trace_event.h" |
11 #include "base/win/windows_version.h" | |
12 #include "ui/gfx/native_widget_types.h" | 11 #include "ui/gfx/native_widget_types.h" |
13 | 12 |
14 namespace gl { | 13 namespace gl { |
15 | 14 |
16 namespace { | |
17 bool g_use_dwm_vsync; | |
18 } // namespace | |
19 | |
20 VSyncProviderWin::VSyncProviderWin(gfx::AcceleratedWidget window) | 15 VSyncProviderWin::VSyncProviderWin(gfx::AcceleratedWidget window) |
21 : window_(window) { | 16 : window_(window) { |
22 } | 17 } |
23 | 18 |
24 VSyncProviderWin::~VSyncProviderWin() {} | 19 VSyncProviderWin::~VSyncProviderWin() {} |
25 | 20 |
26 // static | 21 // static |
27 void VSyncProviderWin::InitializeOneOff() { | 22 void VSyncProviderWin::InitializeOneOff() { |
28 static bool initialized = false; | 23 static bool initialized = false; |
29 if (initialized) | 24 if (initialized) |
30 return; | 25 return; |
31 initialized = true; | 26 initialized = true; |
32 g_use_dwm_vsync = (base::win::GetVersion() >= base::win::VERSION_WIN7); | |
33 | 27 |
34 if (g_use_dwm_vsync) { | 28 // Prewarm sandbox |
35 // Prewarm sandbox | 29 ::LoadLibrary(L"dwmapi.dll"); |
36 ::LoadLibrary(L"dwmapi.dll"); | |
37 } | |
38 } | 30 } |
39 | 31 |
40 void VSyncProviderWin::GetVSyncParameters(const UpdateVSyncCallback& callback) { | 32 void VSyncProviderWin::GetVSyncParameters(const UpdateVSyncCallback& callback) { |
41 TRACE_EVENT0("gpu", "WinVSyncProvider::GetVSyncParameters"); | 33 TRACE_EVENT0("gpu", "WinVSyncProvider::GetVSyncParameters"); |
42 | 34 |
43 base::TimeTicks timebase; | 35 base::TimeTicks timebase; |
44 base::TimeDelta interval; | 36 base::TimeDelta interval; |
45 bool dwm_active = false; | |
46 | 37 |
47 // Query the DWM timing info first if available. This will provide the most | 38 // Query the DWM timing info first if available. This will provide the most |
48 // precise values. | 39 // precise values. |
49 if (g_use_dwm_vsync) { | 40 DWM_TIMING_INFO timing_info; |
50 DWM_TIMING_INFO timing_info; | 41 timing_info.cbSize = sizeof(timing_info); |
51 timing_info.cbSize = sizeof(timing_info); | 42 HRESULT result = DwmGetCompositionTimingInfo(NULL, &timing_info); |
52 HRESULT result = DwmGetCompositionTimingInfo(NULL, &timing_info); | 43 if (result == S_OK) { |
53 if (result == S_OK) { | 44 // Calculate an interval value using the rateRefresh numerator and |
54 dwm_active = true; | 45 // denominator. |
| 46 base::TimeDelta rate_interval; |
| 47 if (timing_info.rateRefresh.uiDenominator > 0 && |
| 48 timing_info.rateRefresh.uiNumerator > 0) { |
| 49 // Swap the numerator/denominator to convert frequency to period. |
| 50 rate_interval = base::TimeDelta::FromMicroseconds( |
| 51 timing_info.rateRefresh.uiDenominator * |
| 52 base::Time::kMicrosecondsPerSecond / |
| 53 timing_info.rateRefresh.uiNumerator); |
| 54 } |
55 | 55 |
56 // Calculate an interval value using the rateRefresh numerator and | 56 if (base::TimeTicks::IsHighResolution()) { |
57 // denominator. | 57 // qpcRefreshPeriod is very accurate but noisy, and must be used with |
58 base::TimeDelta rate_interval; | 58 // a high resolution timebase to avoid frequently missing Vsync. |
59 if (timing_info.rateRefresh.uiDenominator > 0 && | 59 timebase = base::TimeTicks::FromQPCValue( |
60 timing_info.rateRefresh.uiNumerator > 0) { | 60 static_cast<LONGLONG>(timing_info.qpcVBlank)); |
61 // Swap the numerator/denominator to convert frequency to period. | 61 interval = base::TimeDelta::FromQPCValue( |
62 rate_interval = base::TimeDelta::FromMicroseconds( | 62 static_cast<LONGLONG>(timing_info.qpcRefreshPeriod)); |
63 timing_info.rateRefresh.uiDenominator * | 63 // Check for interval values that are impossibly low. A 29 microsecond |
64 base::Time::kMicrosecondsPerSecond / | 64 // interval was seen (from a qpcRefreshPeriod of 60). |
65 timing_info.rateRefresh.uiNumerator); | 65 if (interval < base::TimeDelta::FromMilliseconds(1)) { |
66 } | |
67 | |
68 if (base::TimeTicks::IsHighResolution()) { | |
69 // qpcRefreshPeriod is very accurate but noisy, and must be used with | |
70 // a high resolution timebase to avoid frequently missing Vsync. | |
71 timebase = base::TimeTicks::FromQPCValue( | |
72 static_cast<LONGLONG>(timing_info.qpcVBlank)); | |
73 interval = base::TimeDelta::FromQPCValue( | |
74 static_cast<LONGLONG>(timing_info.qpcRefreshPeriod)); | |
75 // Check for interval values that are impossibly low. A 29 microsecond | |
76 // interval was seen (from a qpcRefreshPeriod of 60). | |
77 if (interval < base::TimeDelta::FromMilliseconds(1)) { | |
78 interval = rate_interval; | |
79 } | |
80 // Check for the qpcRefreshPeriod interval being improbably small | |
81 // compared to the rateRefresh calculated interval, as another | |
82 // attempt at detecting driver bugs. | |
83 if (!rate_interval.is_zero() && interval < rate_interval / 2) { | |
84 interval = rate_interval; | |
85 } | |
86 } else { | |
87 // If FrameTime is not high resolution, we do not want to translate | |
88 // the QPC value provided by DWM into the low-resolution timebase, | |
89 // which would be error prone and jittery. As a fallback, we assume | |
90 // the timebase is zero and use rateRefresh, which may be rounded but | |
91 // isn't noisy like qpcRefreshPeriod, instead. The fact that we don't | |
92 // have a timebase here may lead to brief periods of jank when our | |
93 // scheduling becomes offset from the hardware vsync. | |
94 interval = rate_interval; | 66 interval = rate_interval; |
95 } | 67 } |
| 68 // Check for the qpcRefreshPeriod interval being improbably small |
| 69 // compared to the rateRefresh calculated interval, as another |
| 70 // attempt at detecting driver bugs. |
| 71 if (!rate_interval.is_zero() && interval < rate_interval / 2) { |
| 72 interval = rate_interval; |
| 73 } |
| 74 } else { |
| 75 // If FrameTime is not high resolution, we do not want to translate |
| 76 // the QPC value provided by DWM into the low-resolution timebase, |
| 77 // which would be error prone and jittery. As a fallback, we assume |
| 78 // the timebase is zero and use rateRefresh, which may be rounded but |
| 79 // isn't noisy like qpcRefreshPeriod, instead. The fact that we don't |
| 80 // have a timebase here may lead to brief periods of jank when our |
| 81 // scheduling becomes offset from the hardware vsync. |
| 82 interval = rate_interval; |
96 } | 83 } |
97 } | 84 } else { |
98 | |
99 if (!dwm_active) { | |
100 // When DWM compositing is active all displays are normalized to the | 85 // When DWM compositing is active all displays are normalized to the |
101 // refresh rate of the primary display, and won't composite any faster. | 86 // refresh rate of the primary display, and won't composite any faster. |
102 // If DWM compositing is disabled, though, we can use the refresh rates | 87 // If DWM compositing is disabled, though, we can use the refresh rates |
103 // reported by each display, which will help systems that have mis-matched | 88 // reported by each display, which will help systems that have mis-matched |
104 // displays that run at different frequencies. | 89 // displays that run at different frequencies. |
105 HMONITOR monitor = MonitorFromWindow(window_, MONITOR_DEFAULTTONEAREST); | 90 HMONITOR monitor = MonitorFromWindow(window_, MONITOR_DEFAULTTONEAREST); |
106 MONITORINFOEX monitor_info; | 91 MONITORINFOEX monitor_info; |
107 monitor_info.cbSize = sizeof(MONITORINFOEX); | 92 monitor_info.cbSize = sizeof(MONITORINFOEX); |
108 BOOL result = GetMonitorInfo(monitor, &monitor_info); | 93 BOOL result = GetMonitorInfo(monitor, &monitor_info); |
109 if (result) { | 94 if (result) { |
110 DEVMODE display_info; | 95 DEVMODE display_info; |
111 display_info.dmSize = sizeof(DEVMODE); | 96 display_info.dmSize = sizeof(DEVMODE); |
112 display_info.dmDriverExtra = 0; | 97 display_info.dmDriverExtra = 0; |
113 result = EnumDisplaySettings(monitor_info.szDevice, ENUM_CURRENT_SETTINGS, | 98 result = EnumDisplaySettings(monitor_info.szDevice, ENUM_CURRENT_SETTINGS, |
114 &display_info); | 99 &display_info); |
115 if (result && display_info.dmDisplayFrequency > 1) { | 100 if (result && display_info.dmDisplayFrequency > 1) { |
116 interval = base::TimeDelta::FromMicroseconds( | 101 interval = base::TimeDelta::FromMicroseconds( |
117 (1.0 / static_cast<double>(display_info.dmDisplayFrequency)) * | 102 (1.0 / static_cast<double>(display_info.dmDisplayFrequency)) * |
118 base::Time::kMicrosecondsPerSecond); | 103 base::Time::kMicrosecondsPerSecond); |
119 } | 104 } |
120 } | 105 } |
121 } | 106 } |
122 | 107 |
123 if (interval.ToInternalValue() != 0) { | 108 if (interval.ToInternalValue() != 0) { |
124 callback.Run(timebase, interval); | 109 callback.Run(timebase, interval); |
125 } | 110 } |
126 } | 111 } |
127 | 112 |
128 } // namespace gl | 113 } // namespace gl |
OLD | NEW |