| 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 |