From e1cbc7a1f19a97c7c00ba32df17b30d4cf32c630 Mon Sep 17 00:00:00 2001 From: Yuhang Zhao <zhaoyuhang@rankyee.com> Date: ćšć, 07 12æ 2023 17:36:56 +0800 Subject: [PATCH] port sync dwm code --- src/core/contexts/win32windowcontext.cpp | 65 +++++++++++++++++++++++++++++++- 1 files changed, 62 insertions(+), 3 deletions(-) diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index 27e5347..1201277 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -23,6 +23,7 @@ #include <shellscalingapi.h> #include <dwmapi.h> +#include <timeapi.h> Q_DECLARE_METATYPE(QMargins) @@ -45,9 +46,13 @@ struct DynamicApis { decltype(&::DwmFlush) pDwmFlush = nullptr; decltype(&::DwmIsCompositionEnabled) pDwmIsCompositionEnabled = nullptr; + decltype(&::DwmGetCompositionTimingInfo) pDwmGetCompositionTimingInfo = nullptr; decltype(&::GetDpiForWindow) pGetDpiForWindow = nullptr; decltype(&::GetSystemMetricsForDpi) pGetSystemMetricsForDpi = nullptr; decltype(&::GetDpiForMonitor) pGetDpiForMonitor = nullptr; + decltype(&::timeGetDevCaps) ptimeGetDevCaps = nullptr; + decltype(&::timeBeginPeriod) ptimeBeginPeriod = nullptr; + decltype(&::timeEndPeriod) ptimeEndPeriod = nullptr; DynamicApis() { QSystemLibrary user32(QStringLiteral("user32.dll")); @@ -64,6 +69,12 @@ pDwmFlush = reinterpret_cast<decltype(pDwmFlush)>(dwmapi.resolve("DwmFlush")); pDwmIsCompositionEnabled = reinterpret_cast<decltype(pDwmIsCompositionEnabled)>( dwmapi.resolve("DwmIsCompositionEnabled")); + pDwmGetCompositionTimingInfo = reinterpret_cast<decltype(pDwmGetCompositionTimingInfo)>(dwmapi.resolve("DwmGetCompositionTimingInfo")); + + QSystemLibrary winmm(QStringLiteral("winmm.dll")); + ptimeGetDevCaps = reinterpret_cast<decltype(ptimeGetDevCaps)>(winmm.resolve("timeGetDevCaps")); + ptimeBeginPeriod = reinterpret_cast<decltype(ptimeBeginPeriod)>(winmm.resolve("timeBeginPeriod")); + ptimeEndPeriod = reinterpret_cast<decltype(ptimeEndPeriod)>(winmm.resolve("timeEndPeriod")); } ~DynamicApis() = default; @@ -310,6 +321,53 @@ #endif } + static inline void syncPaintEventWithDwm() { + // No need to sync with DWM if DWM composition is disabled. + if (!isDwmCompositionEnabled()) { + return; + } + const DynamicApis &apis = DynamicApis::instance(); + // Dirty hack to workaround the resize flicker caused by DWM. + LARGE_INTEGER freq{}; + ::QueryPerformanceFrequency(&freq); + TIMECAPS tc{}; + apis.ptimeGetDevCaps(&tc, sizeof(tc)); + const UINT ms_granularity = tc.wPeriodMin; + apis.ptimeBeginPeriod(ms_granularity); + LARGE_INTEGER now0{}; + ::QueryPerformanceCounter(&now0); + // ask DWM where the vertical blank falls + DWM_TIMING_INFO dti{}; + dti.cbSize = sizeof(dti); + apis.pDwmGetCompositionTimingInfo(nullptr, &dti); + LARGE_INTEGER now1{}; + ::QueryPerformanceCounter(&now1); + // - DWM told us about SOME vertical blank + // - past or future, possibly many frames away + // - convert that into the NEXT vertical blank + const auto period = qreal(dti.qpcRefreshPeriod); + const auto dt = qreal(dti.qpcVBlank - now1.QuadPart); + const qreal ratio = (dt / period); + auto w = qreal(0); + auto m = qreal(0); + if ((dt > qreal(0)) || qFuzzyIsNull(dt)) { + w = ratio; + } else { + // reach back to previous period + // - so m represents consistent position within phase + w = (ratio - qreal(1)); + } + m = (dt - (period * w)); + //Q_ASSERT((m > qreal(0)) || qFuzzyIsNull(m)); + //Q_ASSERT(m < period); + if ((m < qreal(0)) || qFuzzyCompare(m, period) || (m > period)) { + return; + } + const qreal m_ms = (qreal(1000) * m / qreal(freq.QuadPart)); + ::Sleep(static_cast<DWORD>(std::round(m_ms))); + apis.ptimeEndPeriod(ms_granularity); + } + static inline QPoint fromNativeLocalPosition(const QWindow *window, const QPoint &point) { Q_ASSERT(window); if (!window) { @@ -408,7 +466,7 @@ // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/plugins/platforms/windows/qwindowscontext.cpp#L1546 // Qt needs to refer to the WM_NCCALCSIZE message data that hasn't been processed, so we // have to process it after Qt acquired the initial data. - auto msg = reinterpret_cast<const MSG *>(message); + auto msg = static_cast<const MSG *>(message); if (msg->message == WM_NCCALCSIZE && orgLastMessageContext) { LRESULT res; if (Win32WindowContext::nonClientCalcSizeHandler(msg->hwnd, msg->message, @@ -1455,8 +1513,9 @@ } } } - // ### TODO: std::ignore = Utils::syncWmPaintWithDwm(); // This should be executed - // at the very last. By returning WVR_REDRAW we can make the window resizing look + // We should call this function only before the function returns. + syncPaintEventWithDwm(); + // By returning WVR_REDRAW we can make the window resizing look // less broken. But we must return 0 if wParam is FALSE, according to Microsoft // Docs. // **IMPORTANT NOTE**: -- Gitblit v1.9.1