| // 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 "ui/gfx/native_widget_types.h" |
| |
| namespace gl { |
| |
| VSyncProviderWin::VSyncProviderWin(gfx::AcceleratedWidget window) |
| : window_(window) { |
| } |
| |
| VSyncProviderWin::~VSyncProviderWin() {} |
| |
| // static |
| void VSyncProviderWin::InitializeOneOff() { |
| static bool initialized = false; |
| if (initialized) |
| return; |
| initialized = true; |
| |
| // Prewarm sandbox |
| ::LoadLibrary(L"dwmapi.dll"); |
| } |
| |
| void VSyncProviderWin::GetVSyncParameters(UpdateVSyncCallback callback) { |
| base::TimeTicks timebase; |
| base::TimeDelta interval; |
| if (GetVSyncParametersIfAvailable(&timebase, &interval)) |
| std::move(callback).Run(timebase, interval); |
| } |
| |
| bool VSyncProviderWin::GetVSyncParametersIfAvailable( |
| base::TimeTicks* out_timebase, |
| base::TimeDelta* out_interval) { |
| TRACE_EVENT0("gpu", "WinVSyncProvider::GetVSyncParameters"); |
| |
| base::TimeTicks timebase; |
| base::TimeDelta interval; |
| |
| // Query the DWM timing info first if available. This will provide the most |
| // precise values. |
| DWM_TIMING_INFO timing_info; |
| timing_info.cbSize = sizeof(timing_info); |
| HRESULT result = DwmGetCompositionTimingInfo(NULL, &timing_info); |
| if (result == S_OK) { |
| // 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; |
| } |
| } else { |
| // 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.is_zero()) |
| return false; |
| |
| *out_timebase = timebase; |
| *out_interval = interval; |
| return true; |
| } |
| |
| bool VSyncProviderWin::SupportGetVSyncParametersIfAvailable() const { |
| return true; |
| } |
| |
| bool VSyncProviderWin::IsHWClock() const { |
| return true; |
| } |
| |
| } // namespace gl |