diff --git a/contrib/src/qt/0001-d3d-Drive-window-updates-from-a-vblank-watcher-threa.patch b/contrib/src/qt/0001-d3d-Drive-window-updates-from-a-vblank-watcher-threa.patch new file mode 100644 index 0000000000000000000000000000000000000000..735a1147991b815409ddb4e16019dbdcdc4f1e5b --- /dev/null +++ b/contrib/src/qt/0001-d3d-Drive-window-updates-from-a-vblank-watcher-threa.patch @@ -0,0 +1,829 @@ +From e14f26656b5a77231dbdb6e58ec3859c11342c2c Mon Sep 17 00:00:00 2001 +From: Laszlo Agocs <laszlo.agocs@qt.io> +Date: Thu, 8 Aug 2024 13:54:42 +0200 +Subject: [PATCH] d3d: Drive window updates from a vblank watcher thread + +Have dedicated threads that just do IDXGIOutput::WaitForVBlank() and +fire off notifications. I.e., create something conceptually similar to +macOS' CVDisplayLink. Then hook this up to QWindow::requestUpdate(). + +For windows that are not D3D and not used with an active QRhi, or when +the used D3D adapter is WARP that reports no DXGI outputs, this has no +effect, and everything works like before, with the standard +requestUpdate behavior of using a 5 ms (or less) timer. + +For development purposes one can always disable this by setting the +environment variable QT_D3D_NO_VBLANK_THREAD to a non-zero value. To +have some debug output, enable the category qt.qpa.screen.updates. + +Multiple refresh rates are supported by using dedicated a notifier +thread per output. This way we do not bake in assumptions about the +composition and presentation behavior, which is good since it is +reported to have changed around Win 11 23H2 (DWM not anymore limiting to +the primary display's refresh rate). E.g., just having a single thread +ticking based on the primary screen would be quite wrong. + +The result is a very visibly reduced display lag e.g. with the classic +test case of dragging an item with the mouse around in a Qt Quick scene, +less time spent on throttling to the presentation rate (and more time in +the event loop, presumably), and, for some reason, a noticeable +reduction in the reported CPU load as well. + +This complements the 6.8 changes that added a blocking wait on the frame +latency waitable object (with max latency set to 2) in beginFrame(). +Those changes provided some improvements for the lag-when-dragging case +in particular, effectively bringing the out of the box Qt 6 experience +with D3D on par with the Qt 5 results with OpenGL (results vary with +diferent vendors; all testing here has been done with NVIDIA graphics). +Whereas this more complex approach on top provides further improvements, +which were not possible to achieve before with any configuration of 3D +APIs or settings. + +[ChangeLog][Windows] By default, windows that get rendered to with QRhi +using either the D3D11 or D3D12 backend base their update request +deliveries on notifications from dedicated vblank waiting threads. This +affects both direct users of QWindow::requestUpdate() and also Qt Quick, +since QQuickWindow uses the same mechanism internally. This is expected +to provide smoother presentation, in particular a reduced display - +mouse cursor lag when dragging items in a Qt Quick scene for example, +and potentially reduced CPU load. Note that this is not applicable when +using an adapter such as WARP. For development and testing purposes, it +is possible to request the old behavior by setting the environment +variable QT_D3D_NO_VBLANK_THREAD to a non-zero value. + +Task-number: QTBUG-127267 +Change-Id: I6e35e776f495bc34cf164164da8ce7f9e74f45b6 +Reviewed-by: Oliver Wolff <oliver.wolff@qt.io> +--- + src/gui/CMakeLists.txt | 1 + + src/gui/rhi/qdxgivsyncservice.cpp | 440 ++++++++++++++++++ + src/gui/rhi/qdxgivsyncservice_p.h | 109 +++++ + src/gui/rhi/qrhid3d11.cpp | 10 + + src/gui/rhi/qrhid3d12.cpp | 10 + + src/gui/rhi/qrhid3dhelpers_p.h | 2 + + .../platforms/windows/qwindowswindow.cpp | 27 ++ + .../platforms/windows/qwindowswindow.h | 5 + + 8 files changed, 604 insertions(+) + create mode 100644 src/gui/rhi/qdxgivsyncservice.cpp + create mode 100644 src/gui/rhi/qdxgivsyncservice_p.h + +diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt +index 2cc72625131..3b976ec08a3 100644 +--- a/src/gui/CMakeLists.txt ++++ b/src/gui/CMakeLists.txt +@@ -464,6 +464,7 @@ qt_internal_extend_target(Gui CONDITION WIN32 + rhi/cs_mipmap_p.h + ../3rdparty/D3D12MemoryAllocator/D3D12MemAlloc.h + ../3rdparty/D3D12MemoryAllocator/D3D12MemAlloc.cpp ++ rhi/qdxgivsyncservice_p.h rhi/qdxgivsyncservice.cpp + text/windows/qwindowsfontdatabase.cpp text/windows/qwindowsfontdatabase_p.h + text/windows/qwindowsfontdatabasebase.cpp text/windows/qwindowsfontdatabasebase_p.h + text/windows/qwindowsfontengine.cpp text/windows/qwindowsfontengine_p.h +diff --git a/src/gui/rhi/qdxgivsyncservice.cpp b/src/gui/rhi/qdxgivsyncservice.cpp +new file mode 100644 +index 00000000000..3b7c6fde9a7 +--- /dev/null ++++ b/src/gui/rhi/qdxgivsyncservice.cpp +@@ -0,0 +1,440 @@ ++// Copyright (C) 2024 The Qt Company Ltd. ++// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only ++ ++#include "qdxgivsyncservice_p.h" ++#include <QThread> ++#include <QWaitCondition> ++#include <QElapsedTimer> ++#include <QCoreApplication> ++#include <QLoggingCategory> ++#include <QScreen> ++#include <QtCore/private/qsystemerror_p.h> ++ ++QT_BEGIN_NAMESPACE ++ ++Q_LOGGING_CATEGORY(lcQpaScreenUpdates, "qt.qpa.screen.updates", QtCriticalMsg); ++ ++class QDxgiVSyncThread : public QThread ++{ ++public: ++ // the HMONITOR is unique (i.e. identifies the output), the IDXGIOutput (the pointer/object itself) is not ++ using Callback = std::function<void(IDXGIOutput *, HMONITOR, qint64)>; ++ QDxgiVSyncThread(IDXGIOutput *output, float vsyncIntervalMsReportedForScreen, Callback callback); ++ void stop(); // to be called from a thread that's not this thread ++ void run() override; ++ ++private: ++ IDXGIOutput *output; ++ float vsyncIntervalMsReportedForScreen; ++ Callback callback; ++ HMONITOR monitor; ++ QAtomicInt quit; ++ QMutex mutex; ++ QWaitCondition cond; ++}; ++ ++QDxgiVSyncThread::QDxgiVSyncThread(IDXGIOutput *output, float vsyncIntervalMsReportedForScreen, Callback callback) ++ : output(output), ++ vsyncIntervalMsReportedForScreen(vsyncIntervalMsReportedForScreen), ++ callback(callback) ++{ ++ DXGI_OUTPUT_DESC desc; ++ output->GetDesc(&desc); ++ monitor = desc.Monitor; ++} ++ ++void QDxgiVSyncThread::run() ++{ ++ qCDebug(lcQpaScreenUpdates) << "QDxgiVSyncThread" << this << "for output" << output << "monitor" << monitor << "entered run()"; ++ QElapsedTimer timestamp; ++ QElapsedTimer elapsed; ++ timestamp.start(); ++ while (!quit.loadAcquire()) { ++ elapsed.start(); ++ HRESULT hr = output->WaitForVBlank(); ++ if (FAILED(hr) || elapsed.nsecsElapsed() <= 1000000) { ++ // 1 ms minimum; if less than that was spent in WaitForVBlank ++ // (reportedly can happen e.g. when a screen gets powered on/off?), ++ // or it reported an error, do a sleep; spinning unthrottled is ++ // never acceptable ++ QThread::msleep((unsigned long) vsyncIntervalMsReportedForScreen); ++ } else { ++ callback(output, monitor, timestamp.nsecsElapsed()); ++ } ++ } ++ qCDebug(lcQpaScreenUpdates) << "QDxgiVSyncThread" << this << "is stopping"; ++ mutex.lock(); ++ cond.wakeOne(); ++ mutex.unlock(); ++ qCDebug(lcQpaScreenUpdates) << "QDxgiVSyncThread" << this << "run() out"; ++} ++ ++void QDxgiVSyncThread::stop() ++{ ++ mutex.lock(); ++ qCDebug(lcQpaScreenUpdates) << "Requesting QDxgiVSyncThread stop from thread" << QThread::currentThread() << "on" << this; ++ if (isRunning() && !quit.loadAcquire()) { ++ quit.storeRelease(1); ++ cond.wait(&mutex); ++ } ++ wait(); ++ mutex.unlock(); ++} ++ ++QDxgiVSyncService *QDxgiVSyncService::instance() ++{ ++ static QDxgiVSyncService service; ++ return &service; ++} ++ ++QDxgiVSyncService::QDxgiVSyncService() ++{ ++ qCDebug(lcQpaScreenUpdates) << "New QDxgiVSyncService" << this; ++ ++ disableService = qEnvironmentVariableIntValue("QT_D3D_NO_VBLANK_THREAD"); ++ if (disableService) { ++ qCDebug(lcQpaScreenUpdates) << "QDxgiVSyncService disabled by environment"; ++ return; ++ } ++} ++ ++QDxgiVSyncService::~QDxgiVSyncService() ++{ ++ qCDebug(lcQpaScreenUpdates) << "~QDxgiVSyncService" << this; ++ ++ // Deadlock is almost guaranteed if we try to clean up here, when the global static is being destructed. ++ // Must have been done earlier. ++ if (dxgiFactory) ++ qWarning("QDxgiVSyncService not destroyed in time"); ++} ++ ++void QDxgiVSyncService::global_destroy() ++{ ++ QDxgiVSyncService *inst = QDxgiVSyncService::instance(); ++ inst->cleanupRegistered = false; ++ inst->destroy(); ++} ++ ++void QDxgiVSyncService::destroy() ++{ ++ qCDebug(lcQpaScreenUpdates) << "QDxgiVSyncService::destroy()"; ++ ++ if (disableService) ++ return; ++ ++ for (auto it = windows.begin(), end = windows.end(); it != end; ++it) ++ cleanupWindowData(&*it); ++ windows.clear(); ++ ++ teardownDxgi(); ++} ++ ++void QDxgiVSyncService::teardownDxgi() ++{ ++ for (auto it = adapters.begin(), end = adapters.end(); it != end; ++it) ++ cleanupAdapterData(&*it); ++ adapters.clear(); ++ ++ if (dxgiFactory) { ++ dxgiFactory->Release(); ++ dxgiFactory = nullptr; ++ } ++ ++ qCDebug(lcQpaScreenUpdates) << "QDxgiVSyncService DXGI teardown complete"; ++} ++ ++void QDxgiVSyncService::beginFrame(LUID) ++{ ++ QMutexLocker lock(&mutex); ++ if (disableService) ++ return; ++ ++ // Handle "the possible need to re-create the factory and re-enumerate ++ // adapters". At the time of writing the QRhi D3D11 and D3D12 backends do ++ // not handle this at all (and rendering does not actually break or stop ++ // just because the factory says !IsCurrent), whereas here it makes more ++ // sense to act since we may want to get rid of threads that are no longer ++ // needed. Keep the adapter IDs and the registered windows, drop everything ++ // else, then start from scratch. ++ ++ if (dxgiFactory && !dxgiFactory->IsCurrent()) { ++ qWarning("QDxgiVSyncService: DXGI Factory is no longer Current"); ++ QVarLengthArray<LUID, 8> luids; ++ for (auto it = adapters.begin(), end = adapters.end(); it != end; ++it) ++ luids.append(it->luid); ++ for (auto it = windows.begin(), end = windows.end(); it != end; ++it) ++ cleanupWindowData(&*it); ++ lock.unlock(); ++ teardownDxgi(); ++ for (LUID luid : luids) ++ refAdapter(luid); ++ lock.relock(); ++ for (auto it = windows.begin(), end = windows.end(); it != end; ++it) ++ updateWindowData(it.key(), &*it); ++ } ++} ++ ++void QDxgiVSyncService::refAdapter(LUID luid) ++{ ++ QMutexLocker lock(&mutex); ++ if (disableService) ++ return; ++ ++ if (!dxgiFactory) { ++ HRESULT hr = CreateDXGIFactory2(0, __uuidof(IDXGIFactory2), reinterpret_cast<void **>(&dxgiFactory)); ++ if (FAILED(hr)) { ++ disableService = true; ++ qWarning("QDxgiVSyncService: CreateDXGIFactory2 failed: %s", qPrintable(QSystemError::windowsComString(hr))); ++ return; ++ } ++ if (!cleanupRegistered) { ++ qAddPostRoutine(QDxgiVSyncService::global_destroy); ++ cleanupRegistered = true; ++ } ++ } ++ ++ for (AdapterData &a : adapters) { ++ if (a.luid.LowPart == luid.LowPart && a.luid.HighPart == luid.HighPart) { ++ a.ref += 1; ++ return; ++ } ++ } ++ ++ AdapterData a; ++ a.ref = 1; ++ a.luid = luid; ++ a.adapter = nullptr; ++ ++ IDXGIAdapter1 *ad; ++ for (int adapterIndex = 0; dxgiFactory->EnumAdapters1(UINT(adapterIndex), &ad) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) { ++ DXGI_ADAPTER_DESC1 desc; ++ ad->GetDesc1(&desc); ++ if (desc.AdapterLuid.LowPart == luid.LowPart && desc.AdapterLuid.HighPart == luid.HighPart) { ++ a.adapter = ad; ++ break; ++ } ++ ad->Release(); ++ } ++ ++ if (!a.adapter) { ++ qWarning("VSyncService: Failed to find adapter (via EnumAdapters1), skipping"); ++ return; ++ } ++ ++ adapters.append(a); ++ ++ qCDebug(lcQpaScreenUpdates) << "QDxgiVSyncService refAdapter for not yet seen adapter" << luid.LowPart << luid.HighPart; ++ ++ // windows may have been registered before any adapters ++ for (auto it = windows.begin(), end = windows.end(); it != end; ++it) ++ updateWindowData(it.key(), &*it); ++} ++ ++void QDxgiVSyncService::derefAdapter(LUID luid) ++{ ++ QVarLengthArray<AdapterData, 4> cleanupList; ++ ++ { ++ QMutexLocker lock(&mutex); ++ if (disableService) ++ return; ++ ++ for (qsizetype i = 0; i < adapters.count(); ++i) { ++ AdapterData &a(adapters[i]); ++ if (a.luid.LowPart == luid.LowPart && a.luid.HighPart == luid.HighPart) { ++ if (!--a.ref) { ++ cleanupList.append(a); ++ adapters.removeAt(i); ++ } ++ break; ++ } ++ } ++ } ++ ++ // the lock must *not* be held when triggering cleanup ++ for (AdapterData &a : cleanupList) ++ cleanupAdapterData(&a); ++} ++ ++void QDxgiVSyncService::cleanupAdapterData(AdapterData *a) ++{ ++ for (auto it = a->notifiers.begin(), end = a->notifiers.end(); it != end; ++it) { ++ qCDebug(lcQpaScreenUpdates) << "QDxgiVSyncService::cleanupAdapterData(): about to call stop()"; ++ it->thread->stop(); ++ qCDebug(lcQpaScreenUpdates) << "QDxgiVSyncService::cleanupAdapterData(): stop() called"; ++ delete it->thread; ++ it->output->Release(); ++ } ++ a->notifiers.clear(); ++ ++ a->adapter->Release(); ++ a->adapter = nullptr; ++} ++ ++void QDxgiVSyncService::cleanupWindowData(WindowData *w) ++{ ++ if (w->output) { ++ w->output->Release(); ++ w->output = nullptr; ++ } ++} ++ ++static IDXGIOutput *outputForWindow(QWindow *w, IDXGIAdapter *adapter) ++{ ++ // Generic canonical solution as per ++ // https://learn.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgiswapchain-getcontainingoutput ++ // and ++ // https://learn.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range ++ ++ QRect wr = w->geometry(); ++ wr = QRect(wr.topLeft() * w->devicePixelRatio(), wr.size() * w->devicePixelRatio()); ++ const QPoint center = wr.center(); ++ IDXGIOutput *currentOutput = nullptr; ++ IDXGIOutput *output = nullptr; ++ for (UINT i = 0; adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND; ++i) { ++ DXGI_OUTPUT_DESC desc; ++ output->GetDesc(&desc); ++ const RECT r = desc.DesktopCoordinates; ++ const QRect dr(QPoint(r.left, r.top), QPoint(r.right - 1, r.bottom - 1)); ++ if (dr.contains(center)) { ++ currentOutput = output; ++ break; ++ } else { ++ output->Release(); ++ } ++ } ++ return currentOutput; // has a ref on it, will need Release by caller ++} ++ ++void QDxgiVSyncService::updateWindowData(QWindow *window, WindowData *wd) ++{ ++ for (auto it = adapters.begin(), end = adapters.end(); it != end; ++it) { ++ IDXGIOutput *output = outputForWindow(window, it->adapter); ++ if (!output) ++ continue; ++ ++ // Two windows on the same screen may well return two different ++ // IDXGIOutput pointers due to enumerating outputs every time; always ++ // compare the HMONITOR, not the pointer itself. ++ ++ DXGI_OUTPUT_DESC desc; ++ output->GetDesc(&desc); ++ ++ if (wd->output && wd->output != output) { ++ if (desc.Monitor == wd->monitor) { ++ output->Release(); ++ return; ++ } ++ wd->output->Release(); ++ } ++ ++ wd->output = output; ++ wd->monitor = desc.Monitor; ++ ++ QScreen *screen = window->screen(); ++ const qreal refresh = screen ? screen->refreshRate() : 60; ++ wd->reportedRefreshIntervalMs = refresh > 0 ? 1000.0f / float(refresh) : 1000.f / 60.0f; ++ ++ qCDebug(lcQpaScreenUpdates) << "QDxgiVSyncService: Output for window" << window ++ << "on the actively used adapters is now" << output ++ << "HMONITOR" << wd->monitor ++ << "refresh" << wd->reportedRefreshIntervalMs; ++ ++ if (!it->notifiers.contains(wd->monitor)) { ++ output->AddRef(); ++ QDxgiVSyncThread *t = new QDxgiVSyncThread(output, wd->reportedRefreshIntervalMs, ++ [this](IDXGIOutput *, HMONITOR monitor, qint64 timestampNs) { ++ CallbackWindowList w; ++ QMutexLocker lock(&mutex); ++ for (auto it = windows.cbegin(), end = windows.cend(); it != end; ++it) { ++ if (it->output && it->monitor == monitor) ++ w.append(it.key()); ++ } ++ if (!w.isEmpty()) { ++#if 0 ++ qDebug() << "vsync thread" << QThread::currentThread() << monitor << "window list" << w << timestampNs; ++#endif ++ for (const Callback &cb : std::as_const(callbacks)) { ++ if (cb) ++ cb(w, timestampNs); ++ } ++ } ++ }); ++ t->start(QThread::TimeCriticalPriority); ++ it->notifiers.insert(wd->monitor, { wd->output, t }); ++ } ++ return; ++ } ++ ++ // If we get here, there is no IDXGIOutput and supportsWindow() will return false for this window. ++ // This is perfectly normal when using an adapter such as WARP. ++} ++ ++void QDxgiVSyncService::registerWindow(QWindow *window) ++{ ++ QMutexLocker lock(&mutex); ++ if (disableService || windows.contains(window)) ++ return; ++ ++ qCDebug(lcQpaScreenUpdates) << "QDxgiVSyncService: adding window" << window; ++ ++ WindowData wd; ++ wd.output = nullptr; ++ updateWindowData(window, &wd); ++ windows.insert(window, wd); ++ ++ QObject::connect(window, &QWindow::screenChanged, window, [this, window](QScreen *screen) { ++ qCDebug(lcQpaScreenUpdates) << "QDxgiVSyncService: screen changed for window:" << window << screen; ++ QMutexLocker lock(&mutex); ++ auto it = windows.find(window); ++ if (it != windows.end()) ++ updateWindowData(window, &*it); ++ }, Qt::QueuedConnection); // intentionally using Queued ++ // It has been observed that with DirectConnection _sometimes_ we do not ++ // find any IDXGIOutput for the window when moving it to a different screen. ++ // Add a delay by going through the event loop. ++} ++ ++void QDxgiVSyncService::unregisterWindow(QWindow *window) ++{ ++ QMutexLocker lock(&mutex); ++ auto it = windows.find(window); ++ if (it == windows.end()) ++ return; ++ ++ qCDebug(lcQpaScreenUpdates) << "QDxgiVSyncService: removing window" << window; ++ ++ cleanupWindowData(&*it); ++ ++ windows.remove(window); ++} ++ ++bool QDxgiVSyncService::supportsWindow(QWindow *window) ++{ ++ QMutexLocker lock(&mutex); ++ auto it = windows.constFind(window); ++ return it != windows.cend() ? (it->output != nullptr) : false; ++} ++ ++qsizetype QDxgiVSyncService::registerCallback(Callback cb) ++{ ++ QMutexLocker lock(&mutex); ++ for (qsizetype i = 0; i < callbacks.count(); ++i) { ++ if (!callbacks[i]) { ++ callbacks[i] = cb; ++ return i + 1; ++ } ++ } ++ callbacks.append(cb); ++ return callbacks.count(); ++} ++ ++void QDxgiVSyncService::unregisterCallback(qsizetype id) ++{ ++ QMutexLocker lock(&mutex); ++ const qsizetype index = id - 1; ++ if (index >= 0 && index < callbacks.count()) ++ callbacks[index] = nullptr; ++} ++ ++QT_END_NAMESPACE +diff --git a/src/gui/rhi/qdxgivsyncservice_p.h b/src/gui/rhi/qdxgivsyncservice_p.h +new file mode 100644 +index 00000000000..e9b4145afb1 +--- /dev/null ++++ b/src/gui/rhi/qdxgivsyncservice_p.h +@@ -0,0 +1,109 @@ ++// Copyright (C) 2024 The Qt Company Ltd. ++// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only ++ ++#ifndef QDXGIVSYNCSERVICE_P_H ++#define QDXGIVSYNCSERVICE_P_H ++ ++// ++// W A R N I N G ++// ------------- ++// ++// This file is not part of the Qt API. It exists purely as an ++// implementation detail. This header file may change from version to ++// version without notice, or even be removed. ++// ++// We mean it. ++// ++ ++#include <QWindow> ++#include <QMutex> ++ ++#include <dxgi1_6.h> ++ ++QT_BEGIN_NAMESPACE ++ ++class QDxgiVSyncThread; ++ ++class Q_GUI_EXPORT QDxgiVSyncService ++{ ++public: ++ // The public functions can be called on any thread. (but not at all from inside a callback) ++ ++ static QDxgiVSyncService *instance(); ++ ++ // We want to know what adapters are in use by all the active QRhi instances ++ // at any time. This is to avoid blind EnumAdapters calls to enumerate the ++ // world every time a window is created. To be called by create()/destroy() ++ // of the QRhi D3D11/12 backends. ++ void refAdapter(LUID luid); ++ void derefAdapter(LUID luid); ++ ++ // To be called from QRhi's beginFrame. Used to check if the factory is stale. ++ void beginFrame(LUID luid); ++ ++ // Registering the windows can be done either by the QRhi swapchain in the ++ // backends, or e.g. in the platformwindow implementation based on the ++ // surfaceType. ++ void registerWindow(QWindow *window); ++ void unregisterWindow(QWindow *window); ++ ++ // The catch is that in some cases (using an adapter such as WARP) there are ++ // no IDXGIOutputs at all, by design (even though there is very definitely a ++ // QScreen etc. from our perspective), so none of this VSyncService is ++ // functional. Hence this can never be the only way to drive updates for ++ // D3D-based windows. ++ // ++ // A requestUpdate implementation can call this function on every request in ++ // order to decide if it can rely on the callback from this service, or it ++ // should use the traditional timer-based approach for a particular window. ++ // For windows not registered the result is always false. ++ bool supportsWindow(QWindow *window); ++ ++ // Callbacks are invoked on a vsync watcher thread. Whatever is done in ++ // there, it must be fast and cheap. Ideally fire a notification for the ++ // main thread somehow, and nothing else. ++ using CallbackWindowList = QVarLengthArray<QWindow *, 16>; ++ using Callback = std::function<void(const CallbackWindowList &windowList, qint64 timestampNs)>; ++ qsizetype registerCallback(Callback cb); // result is an id, always >= 1 ++ void unregisterCallback(qsizetype id); ++ ++private: ++ QDxgiVSyncService(); ++ ~QDxgiVSyncService(); ++ void destroy(); ++ void teardownDxgi(); ++ static void global_destroy(); ++ ++ struct AdapterData { ++ int ref; ++ LUID luid; ++ IDXGIAdapter *adapter; ++ struct NotifierData { ++ IDXGIOutput *output; ++ QDxgiVSyncThread *thread; ++ }; ++ QHash<HMONITOR, NotifierData> notifiers; ++ }; ++ ++ struct WindowData { ++ IDXGIOutput *output; ++ HMONITOR monitor; ++ float reportedRefreshIntervalMs; ++ }; ++ ++ void cleanupAdapterData(AdapterData *a); ++ void cleanupWindowData(WindowData *w); ++ void updateWindowData(QWindow *window, WindowData *w); ++ ++ bool disableService = false; ++ bool cleanupRegistered = false; ++ IDXGIFactory2 *dxgiFactory = nullptr; ++ QMutex mutex; ++ QVector<AdapterData> adapters; ++ QHash<QWindow *, WindowData> windows; ++ QVector<Callback> callbacks; ++}; ++ ++QT_END_NAMESPACE ++ ++#endif +diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp +index 9e2e0c4e81c..1ebb16e8ab6 100644 +--- a/src/gui/rhi/qrhid3d11.cpp ++++ b/src/gui/rhi/qrhid3d11.cpp +@@ -394,6 +394,8 @@ bool QRhiD3D11::create(QRhi::Flags flags) + qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev); + } + ++ QDxgiVSyncService::instance()->refAdapter(adapterLuid); ++ + if (FAILED(context->QueryInterface(__uuidof(ID3DUserDefinedAnnotation), reinterpret_cast<void **>(&annotations)))) + annotations = nullptr; + +@@ -463,6 +465,8 @@ void QRhiD3D11::destroy() + dxgiFactory->Release(); + dxgiFactory = nullptr; + } ++ ++ QDxgiVSyncService::instance()->derefAdapter(adapterLuid); + } + + void QRhiD3D11::reportLiveObjects(ID3D11Device *device) +@@ -1382,6 +1386,8 @@ QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF + cmd.args.beginFrame.tsDisjointQuery = recordTimestamps ? tsDisjoint : nullptr; + cmd.args.beginFrame.swapchainData = rtData(&swapChainD->rt); + ++ QDxgiVSyncService::instance()->beginFrame(adapterLuid); ++ + return QRhi::FrameOpSuccess; + } + +@@ -4976,6 +4982,8 @@ void QD3D11SwapChain::destroy() + frameLatencyWaitableObject = nullptr; + } + ++ QDxgiVSyncService::instance()->unregisterWindow(window); ++ + QRHI_RES_RHI(QRhiD3D11); + if (rhiD) { + rhiD->unregisterResource(this); +@@ -5421,6 +5429,8 @@ bool QD3D11SwapChain::createOrResize() + // timestamp queries are optional so we can go on even if they failed + } + ++ QDxgiVSyncService::instance()->registerWindow(window); ++ + if (needsRegistration) + rhiD->registerResource(this); + +diff --git a/src/gui/rhi/qrhid3d12.cpp b/src/gui/rhi/qrhid3d12.cpp +index f663c006346..10028bd0d89 100644 +--- a/src/gui/rhi/qrhid3d12.cpp ++++ b/src/gui/rhi/qrhid3d12.cpp +@@ -354,6 +354,8 @@ bool QRhiD3D12::create(QRhi::Flags flags) + qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev); + } + ++ QDxgiVSyncService::instance()->refAdapter(adapterLuid); ++ + if (debugLayer) { + ID3D12InfoQueue *infoQueue; + if (SUCCEEDED(dev->QueryInterface(__uuidof(ID3D12InfoQueue), reinterpret_cast<void **>(&infoQueue)))) { +@@ -604,6 +606,8 @@ void QRhiD3D12::destroy() + dxgiFactory->Release(); + dxgiFactory = nullptr; + } ++ ++ QDxgiVSyncService::instance()->derefAdapter(adapterLuid); + } + + QList<int> QRhiD3D12::supportedSampleCounts() const +@@ -1611,6 +1615,8 @@ QRhi::FrameOpResult QRhiD3D12::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF + timestampPairStartIndex); + } + ++ QDxgiVSyncService::instance()->beginFrame(adapterLuid); ++ + return QRhi::FrameOpSuccess; + } + +@@ -6162,6 +6168,8 @@ void QD3D12SwapChain::destroy() + frameLatencyWaitableObject = nullptr; + } + ++ QDxgiVSyncService::instance()->unregisterWindow(window); ++ + QRHI_RES_RHI(QRhiD3D12); + if (rhiD) { + rhiD->swapchains.remove(this); +@@ -6608,6 +6616,8 @@ bool QD3D12SwapChain::createOrResize() + rtDr->d.colorAttCount = 1; + rtDr->d.dsAttCount = m_depthStencil ? 1 : 0; + ++ QDxgiVSyncService::instance()->registerWindow(window); ++ + if (needsRegistration) { + rhiD->swapchains.insert(this); + rhiD->registerResource(this); +diff --git a/src/gui/rhi/qrhid3dhelpers_p.h b/src/gui/rhi/qrhid3dhelpers_p.h +index f31cdc8d11a..814824d774c 100644 +--- a/src/gui/rhi/qrhid3dhelpers_p.h ++++ b/src/gui/rhi/qrhid3dhelpers_p.h +@@ -28,6 +28,8 @@ + #define QRHI_D3D12_HAS_DXC + #endif + ++#include "qdxgivsyncservice_p.h" ++ + QT_BEGIN_NAMESPACE + + namespace QRhiD3D { +diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp +index f44a28f5636..9222e9494c9 100644 +--- a/src/plugins/platforms/windows/qwindowswindow.cpp ++++ b/src/plugins/platforms/windows/qwindowswindow.cpp +@@ -46,6 +46,8 @@ + + #include <shellscalingapi.h> + ++#include <private/qdxgivsyncservice_p.h> ++ + QT_BEGIN_NAMESPACE + + using QWindowCreationContextPtr = QSharedPointer<QWindowCreationContext>; +@@ -1558,6 +1560,8 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data) + + QWindowsWindow::~QWindowsWindow() + { ++ if (m_vsyncServiceCallbackId != 0) ++ QDxgiVSyncService::instance()->unregisterCallback(m_vsyncServiceCallbackId); + setFlag(WithinDestroy); + QWindowsThemeCache::clearThemeCache(m_data.hwnd); + if (testFlag(TouchRegistered)) +@@ -3573,4 +3577,27 @@ QString QWindowsWindow::formatWindowTitle(const QString &title) + return QPlatformWindow::formatWindowTitle(title, QStringLiteral(" - ")); + } + ++void QWindowsWindow::requestUpdate() ++{ ++ QWindow *w = window(); ++ QDxgiVSyncService *vs = QDxgiVSyncService::instance(); ++ if (vs->supportsWindow(w)) { ++ if (m_vsyncServiceCallbackId == 0) { ++ m_vsyncServiceCallbackId = vs->registerCallback([this, w](const QDxgiVSyncService::CallbackWindowList &windowList, qint64) { ++ if (windowList.contains(w)) { ++ if (m_vsyncUpdatePending.testAndSetAcquire(1, 0)) { ++ QMetaObject::invokeMethod(w, [this, w] { ++ if (w->handle() == this) ++ deliverUpdateRequest(); ++ }); ++ } ++ } ++ }); ++ } ++ m_vsyncUpdatePending.storeRelease(1); ++ } else { ++ QPlatformWindow::requestUpdate(); ++ } ++} ++ + QT_END_NAMESPACE +diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h +index bebd068f1a5..e92a573d1d5 100644 +--- a/src/plugins/platforms/windows/qwindowswindow.h ++++ b/src/plugins/platforms/windows/qwindowswindow.h +@@ -353,6 +353,8 @@ public: + + bool isFrameless() const { return m_data.flags.testFlag(Qt::FramelessWindowHint); } + ++ void requestUpdate() override; ++ + private: + inline void show_sys() const; + inline QWindowsWindowData setWindowFlags_sys(Qt::WindowFlags wt, unsigned flags = 0) const; +@@ -397,6 +399,9 @@ private: + #endif + static bool m_borderInFullScreenDefault; + static bool m_inSetgeometry; ++ ++ qsizetype m_vsyncServiceCallbackId = 0; ++ QAtomicInt m_vsyncUpdatePending; + }; + + #ifndef QT_NO_DEBUG_STREAM +-- +2.49.0 + diff --git a/contrib/src/qt/0002-Satisfy-Windows-7-compatibility.patch b/contrib/src/qt/0002-Satisfy-Windows-7-compatibility.patch index 6afbf889f77003cf0ed91e2108701201a7c5a92c..a54e1395e0ac804376156a53f44b21ab800230ba 100644 --- a/contrib/src/qt/0002-Satisfy-Windows-7-compatibility.patch +++ b/contrib/src/qt/0002-Satisfy-Windows-7-compatibility.patch @@ -1,4 +1,4 @@ -From 5ceab2c7433f7c9aff207b1e7d2f1a4fc9680a0d Mon Sep 17 00:00:00 2001 +From f05d6ea6d1584f2ce097254b7de79639101dd0e0 Mon Sep 17 00:00:00 2001 From: Fatih Uzunoglu <fuzun54@outlook.com> Date: Mon, 22 Jan 2024 21:37:39 +0200 Subject: [PATCH] Satisfy Windows 7 compatibility @@ -14,6 +14,7 @@ Subject: [PATCH] Satisfy Windows 7 compatibility src/corelib/thread/qmutex_win.cpp | 4 +- src/corelib/thread/qthread_win.cpp | 25 ++-- src/gui/CMakeLists.txt | 1 - + src/gui/rhi/qdxgivsyncservice.cpp | 4 +- src/gui/rhi/qrhid3d11.cpp | 5 +- src/gui/rhi/qrhid3d12.cpp | 87 ++++++++++--- .../text/windows/qwindowsfontdatabasebase.cpp | 14 +- @@ -32,7 +33,7 @@ Subject: [PATCH] Satisfy Windows 7 compatibility .../platforms/windows/qwindowstheme.cpp | 6 +- .../platforms/windows/qwindowswindow.cpp | 48 +++++-- src/widgets/styles/qwindowsstyle.cpp | 26 +++- - 28 files changed, 535 insertions(+), 114 deletions(-) + 29 files changed, 537 insertions(+), 116 deletions(-) diff --git a/cmake/QtBaseConfigureTests.cmake b/cmake/QtBaseConfigureTests.cmake index d41fac2e18e..9490de8fa12 100644 @@ -61,10 +62,10 @@ index d41fac2e18e..9490de8fa12 100644 CACHE STRING "Qt platform specific pre-processor defines" FORCE) endif() diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt -index e8158f73950..d7432c35e3c 100644 +index cb76dca3b02..db57eed2b05 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt -@@ -536,17 +536,17 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_animation +@@ -538,17 +538,17 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_animation animation/qvariantanimation.cpp animation/qvariantanimation.h animation/qvariantanimation_p.h ) @@ -88,7 +89,7 @@ index e8158f73950..d7432c35e3c 100644 ) qt_internal_extend_target(Core CONDITION WIN32 -@@ -860,10 +860,10 @@ qt_internal_extend_target(Core CONDITION WASM +@@ -867,10 +867,10 @@ qt_internal_extend_target(Core CONDITION WASM ) # On MS-Win, clang has two flavors, one of which immitates MSVC (so claims to be it) @@ -339,10 +340,10 @@ index 88ba6dc8f62..2d17f1b4dee 100644 emit thr->started(QThread::QPrivateSignal()); QThread::setTerminationEnabled(true); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt -index 2cc72625131..16c8086decc 100644 +index 3b976ec08a3..83c4b695d27 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt -@@ -479,7 +479,6 @@ qt_internal_extend_target(Gui CONDITION WIN32 +@@ -480,7 +480,6 @@ qt_internal_extend_target(Gui CONDITION WIN32 d3d11 dxgi dxguid @@ -350,8 +351,25 @@ index 2cc72625131..16c8086decc 100644 ATTRIBUTION_FILE_DIR_PATHS ../3rdparty/D3D12MemoryAllocator ) +diff --git a/src/gui/rhi/qdxgivsyncservice.cpp b/src/gui/rhi/qdxgivsyncservice.cpp +index 3b7c6fde9a7..00122898d6f 100644 +--- a/src/gui/rhi/qdxgivsyncservice.cpp ++++ b/src/gui/rhi/qdxgivsyncservice.cpp +@@ -181,10 +181,10 @@ void QDxgiVSyncService::refAdapter(LUID luid) + return; + + if (!dxgiFactory) { +- HRESULT hr = CreateDXGIFactory2(0, __uuidof(IDXGIFactory2), reinterpret_cast<void **>(&dxgiFactory)); ++ HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory2), reinterpret_cast<void **>(&dxgiFactory)); + if (FAILED(hr)) { + disableService = true; +- qWarning("QDxgiVSyncService: CreateDXGIFactory2 failed: %s", qPrintable(QSystemError::windowsComString(hr))); ++ qWarning("QDxgiVSyncService: CreateDXGIFactory1 failed: %s", qPrintable(QSystemError::windowsComString(hr))); + return; + } + if (!cleanupRegistered) { diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp -index 3faae39a42a..336b8fbe059 100644 +index 1ebb16e8ab6..2766e6b9fee 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -8,6 +8,7 @@ @@ -375,7 +393,7 @@ index 3faae39a42a..336b8fbe059 100644 result = nullptr; } diff --git a/src/gui/rhi/qrhid3d12.cpp b/src/gui/rhi/qrhid3d12.cpp -index 3ce8b967e0f..cae5c0e9ebe 100644 +index 10028bd0d89..f1f9dd83c89 100644 --- a/src/gui/rhi/qrhid3d12.cpp +++ b/src/gui/rhi/qrhid3d12.cpp @@ -5,6 +5,7 @@ @@ -490,7 +508,7 @@ index 3ce8b967e0f..cae5c0e9ebe 100644 minimumFeatureLevel, __uuidof(ID3D12Device2), reinterpret_cast<void **>(&dev)); -@@ -2818,7 +2866,12 @@ bool QD3D12MipmapGenerator::create(QRhiD3D12 *rhiD) +@@ -2825,7 +2873,12 @@ bool QD3D12MipmapGenerator::create(QRhiD3D12 *rhiD) rsDesc.Desc_1_1.pStaticSamplers = &samplerDesc; ID3DBlob *signature = nullptr; @@ -504,7 +522,7 @@ index 3ce8b967e0f..cae5c0e9ebe 100644 if (FAILED(hr)) { qWarning("Failed to serialize root signature: %s", qPrintable(QSystemError::windowsComString(hr))); return false; -@@ -5095,7 +5148,11 @@ QD3D12ObjectHandle QD3D12ShaderResourceBindings::createRootSignature(const QD3D1 +@@ -5102,7 +5155,11 @@ QD3D12ObjectHandle QD3D12ShaderResourceBindings::createRootSignature(const QD3D1 rsDesc.Desc_1_1.Flags = D3D12_ROOT_SIGNATURE_FLAGS(rsFlags); ID3DBlob *signature = nullptr; @@ -1193,10 +1211,10 @@ index 6c86c47caac..14d10bef072 100644 case ORIENTATION_PREFERENCE_NONE: break; diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp -index 35445c7a3c9..2d5a20134e4 100644 +index 97b2dd339be..72f78c114fa 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp -@@ -737,7 +737,11 @@ void QWindowsTheme::refreshFonts() +@@ -738,7 +738,11 @@ void QWindowsTheme::refreshFonts() fixedFont.setStyleHint(QFont::TypeWriter); LOGFONT lfIconTitleFont; @@ -1210,10 +1228,10 @@ index 35445c7a3c9..2d5a20134e4 100644 m_fonts[SystemFont] = new QFont(QWindowsFontDatabase::systemDefaultFont()); diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp -index f44a28f5636..06d7c7c5ad0 100644 +index 9222e9494c9..c7bda7d07c7 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp -@@ -150,7 +150,7 @@ static QByteArray debugWinExStyle(DWORD exStyle) +@@ -152,7 +152,7 @@ static QByteArray debugWinExStyle(DWORD exStyle) rc += " WS_EX_NOACTIVATE"; if (exStyle & WS_EX_NOPARENTNOTIFY) rc += " WS_EX_NOPARENTNOTIFY"; @@ -1222,7 +1240,7 @@ index f44a28f5636..06d7c7c5ad0 100644 rc += " WS_EX_NOREDIRECTIONBITMAP"; if (exStyle & WS_EX_RIGHT) rc += " WS_EX_RIGHT"; -@@ -525,8 +525,11 @@ static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::Windo +@@ -527,8 +527,11 @@ static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::Windo // The width of the padded border will always be 0 if DWM composition is // disabled, but since it will always be enabled and can't be programtically // disabled from Windows 8, we are safe to go. @@ -1236,7 +1254,7 @@ index f44a28f5636..06d7c7c5ad0 100644 } /*! -@@ -536,11 +539,14 @@ static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::Windo +@@ -538,11 +541,14 @@ static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::Windo static QMargins invisibleMargins(QPoint screenPoint) { @@ -1252,7 +1270,7 @@ index f44a28f5636..06d7c7c5ad0 100644 const int gap = getResizeBorderThickness(dpiX); return QMargins(gap, 0, gap, gap); } -@@ -550,7 +556,12 @@ static QMargins invisibleMargins(QPoint screenPoint) +@@ -552,7 +558,12 @@ static QMargins invisibleMargins(QPoint screenPoint) [[nodiscard]] static inline QMargins invisibleMargins(const HWND hwnd) { @@ -1266,7 +1284,7 @@ index f44a28f5636..06d7c7c5ad0 100644 const int gap = getResizeBorderThickness(dpi); return QMargins(gap, 0, gap, gap); } -@@ -843,7 +854,7 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag +@@ -845,7 +856,7 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag // Currently only compatible with D3D surfaces, use it with care. if (qEnvironmentVariableIntValue("QT_QPA_DISABLE_REDIRECTION_SURFACE")) @@ -1275,7 +1293,7 @@ index f44a28f5636..06d7c7c5ad0 100644 } } -@@ -1071,7 +1082,14 @@ QMargins QWindowsGeometryHint::frame(const QWindow *w, DWORD style, DWORD exStyl +@@ -1073,7 +1084,14 @@ QMargins QWindowsGeometryHint::frame(const QWindow *w, DWORD style, DWORD exStyl return {}; RECT rect = {0,0,0,0}; style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs. @@ -1291,7 +1309,7 @@ index f44a28f5636..06d7c7c5ad0 100644 qErrnoWarning("%s: AdjustWindowRectExForDpi failed", __FUNCTION__); } const QMargins result(qAbs(rect.left), qAbs(rect.top), -@@ -1588,7 +1606,14 @@ void QWindowsWindow::initialize() +@@ -1592,7 +1610,14 @@ void QWindowsWindow::initialize() QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(w, obtainedGeometry); } } @@ -1307,7 +1325,7 @@ index f44a28f5636..06d7c7c5ad0 100644 } QSurfaceFormat QWindowsWindow::format() const -@@ -2083,7 +2108,12 @@ void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam) +@@ -2087,7 +2112,12 @@ void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam) void QWindowsWindow::handleDpiChangedAfterParent(HWND hwnd) { @@ -1322,7 +1340,7 @@ index f44a28f5636..06d7c7c5ad0 100644 setSavedDpi(dpi); diff --git a/src/widgets/styles/qwindowsstyle.cpp b/src/widgets/styles/qwindowsstyle.cpp -index bae182b4676..ab4793007c8 100644 +index a8407add019..11a6dd0ea1d 100644 --- a/src/widgets/styles/qwindowsstyle.cpp +++ b/src/widgets/styles/qwindowsstyle.cpp @@ -62,6 +62,7 @@ @@ -1383,5 +1401,5 @@ index bae182b4676..ab4793007c8 100644 default: break; -- -2.47.1 +2.49.0 diff --git a/contrib/src/qt/0002-qdxgivsyncservice_p-Add-missing-includes.patch b/contrib/src/qt/0002-qdxgivsyncservice_p-Add-missing-includes.patch new file mode 100644 index 0000000000000000000000000000000000000000..46ae87c8eaac8370ce4f78b20f9e47ec39f5fd0f --- /dev/null +++ b/contrib/src/qt/0002-qdxgivsyncservice_p-Add-missing-includes.patch @@ -0,0 +1,42 @@ +From 09799cc76356f44c41ea9f815a22accaa075fc8b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= <marten.nordheim@qt.io> +Date: Wed, 16 Oct 2024 15:01:18 +0200 +Subject: [PATCH] qdxgivsyncservice_p: Add missing includes + +MSVC locally complains about usage of undefined class QHash and QVarLengthArray + +Change-Id: I9ab0067f3f7dc832da879e283f920546aefb7e47 +Reviewed-by: Christian Ehrlicher <ch.ehrlicher@gmx.de> +Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io> +--- + src/gui/rhi/qdxgivsyncservice.cpp | 1 + + src/gui/rhi/qdxgivsyncservice_p.h | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/src/gui/rhi/qdxgivsyncservice.cpp b/src/gui/rhi/qdxgivsyncservice.cpp +index 2d366c41205..ac7e0ed239d 100644 +--- a/src/gui/rhi/qdxgivsyncservice.cpp ++++ b/src/gui/rhi/qdxgivsyncservice.cpp +@@ -8,6 +8,7 @@ + #include <QCoreApplication> + #include <QLoggingCategory> + #include <QScreen> ++#include <QVarLengthArray> + #include <QtCore/private/qsystemerror_p.h> + + QT_BEGIN_NAMESPACE +diff --git a/src/gui/rhi/qdxgivsyncservice_p.h b/src/gui/rhi/qdxgivsyncservice_p.h +index e9b4145afb1..6de729eec5b 100644 +--- a/src/gui/rhi/qdxgivsyncservice_p.h ++++ b/src/gui/rhi/qdxgivsyncservice_p.h +@@ -17,6 +17,7 @@ + + #include <QWindow> + #include <QMutex> ++#include <QHash> + + #include <dxgi1_6.h> + +-- +2.49.0 + diff --git a/contrib/src/qt/rules.mak b/contrib/src/qt/rules.mak index f7a574039e0cb639e4eb32af7f49bdd6df76e0a6..65f1cc296760cd22b9de603e119557cf997f5dab 100644 --- a/contrib/src/qt/rules.mak +++ b/contrib/src/qt/rules.mak @@ -50,6 +50,8 @@ qt: qtbase-everywhere-src-$(QTBASE_VERSION_FULL).tar.xz .sum-qt $(APPLY) $(SRC)/qt/0001-Expose-QRhiImplementation-in-QRhi.patch $(APPLY) $(SRC)/qt/0001-Do-not-include-D3D12MemAlloc.h-in-header-file.patch $(APPLY) $(SRC)/qt/0001-Try-DCompositionCreateDevice3-first-if-available.patch + $(APPLY) $(SRC)/qt/0001-d3d-Drive-window-updates-from-a-vblank-watcher-threa.patch + $(APPLY) $(SRC)/qt/0002-qdxgivsyncservice_p-Add-missing-includes.patch $(APPLY) $(SRC)/qt/0002-Satisfy-Windows-7-compatibility.patch $(APPLY) $(SRC)/qt/0001-disable-precompiled-headers-when-forcing-WINVER-inte.patch $(APPLY) $(SRC)/qt/0001-Use-DirectWrite-font-database-only-with-Windows-10-a.patch