Revision control

Copy as Markdown

// 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
// Qt-Security score:significant reason:default
#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 <QHash>
#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