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