Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qthreadlocalrhi.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QtCore/qcoreapplication.h>
7#include <QtCore/qthreadstorage.h>
8#include <QtGui/private/qguiapplication_p.h>
9#include <QtGui/qoffscreensurface.h>
10#include <QtGui/qpa/qplatformintegration.h>
11
12#if defined(Q_OS_ANDROID)
13# include <QtCore/qmetaobject.h>
14#endif
15
17
18namespace {
19
20static thread_local QRhi::Implementation s_preferredBackend = QRhi::Null;
21
22#if QT_CONFIG(opengl)
23
24bool openGLCapsSupported(const QPlatformIntegration &qpa)
25{
26 return qpa.hasCapability(QPlatformIntegration::OpenGL) &&
27 !QCoreApplication::testAttribute(Qt::AA_ForceRasterWidgets) &&
28 (QThread::isMainThread() ||
29 (qpa.hasCapability(QPlatformIntegration::ThreadedOpenGL) &&
30 qpa.hasCapability(QPlatformIntegration::OffscreenSurface)));
31}
32#endif
33
35{
36public:
39
40 QRhi *ensureRhi(QRhi *referenceRhi)
41 {
42 if (m_rhi || m_cpuOnly)
43 return m_rhi.get();
44
45 [[maybe_unused]] QRhi::Implementation referenceBackend =
46 referenceRhi ? referenceRhi->backend() : QRhi::Null;
47 const QPlatformIntegration *qpa = QGuiApplicationPrivate::platformIntegration();
48
49 if (qpa && qpa->hasCapability(QPlatformIntegration::RhiBasedRendering)) {
50
51#if QT_CONFIG(metal)
52 if (canUseRhiImpl(QRhi::Metal, referenceBackend)) {
53 QRhiMetalInitParams params;
54 m_rhi.reset(QRhi::create(QRhi::Metal, &params));
55 }
56#endif
57
58#if defined(Q_OS_WIN)
59 if (!m_rhi && canUseRhiImpl(QRhi::D3D11, referenceBackend)) {
60 QRhiD3D11InitParams params;
61 m_rhi.reset(QRhi::create(QRhi::D3D11, &params));
62 }
63#endif
64
65#if QT_CONFIG(opengl)
66 if (!m_rhi && canUseRhiImpl(QRhi::OpenGLES2, referenceBackend)) {
67 if (openGLCapsSupported(*qpa)) {
68
69 m_fallbackSurface.reset(QRhiGles2InitParams::newFallbackSurface());
70 QRhiGles2InitParams params;
71 params.fallbackSurface = m_fallbackSurface.get();
72 if (referenceBackend == QRhi::OpenGLES2)
73 params.shareContext = static_cast<const QRhiGles2NativeHandles *>(
74 referenceRhi->nativeHandles())
75 ->context;
76 m_rhi.reset(QRhi::create(QRhi::OpenGLES2, &params));
77
78# if defined(Q_OS_ANDROID)
79 // reset RHI state on application suspension, as this will be invalid after
80 // resuming
81 if (!m_appStateChangedConnection) {
82 if (!m_eventsReceiver)
83 m_eventsReceiver = std::make_unique<QObject>();
84
85 auto onStateChanged = [this](auto state) {
86 if (state == Qt::ApplicationSuspended)
87 resetRhi();
88 };
89
90 m_appStateChangedConnection =
91 QObject::connect(qApp, &QGuiApplication::applicationStateChanged,
92 m_eventsReceiver.get(), onStateChanged);
93 }
94# endif
95 }
96 }
97#endif
98 }
99
100 if (!m_rhi) {
101 m_cpuOnly = true;
102 qWarning() << Q_FUNC_INFO << ": No RHI backend. Using CPU conversion.";
103 }
104
105 return m_rhi.get();
106 }
107
108 void resetRhi()
109 {
110 m_rhi.reset();
111#if QT_CONFIG(opengl)
112 m_fallbackSurface.reset();
113#endif
114 m_cpuOnly = false;
115 }
116
117 bool canUseRhiImpl(const QRhi::Implementation implementation,
118 const QRhi::Implementation reference)
119 {
120 // First priority goes to reference backend
121 if (reference != QRhi::Null)
122 return implementation == reference;
123
124 // If no reference, but preference exists, compare to that
125 if (s_preferredBackend != QRhi::Null)
126 return implementation == s_preferredBackend;
127
128 // Can use (assuming platform and configuration allow)
129 return true;
130 }
131
132private:
133 std::unique_ptr<QRhi> m_rhi;
134#if QT_CONFIG(opengl)
136#endif
137 bool m_cpuOnly = false;
138#if defined(Q_OS_ANDROID)
140 // we keep and check QMetaObject::Connection because the sender, qApp,
141 // can be recreated and the connection invalidated.
143#endif
144};
145
146Q_CONSTINIT thread_local std::optional<ThreadLocalRhiHolder> g_threadLocalRhiHolder;
147
149{
150 if (QThread::isMainThread()) {
151 // ensure cleanup in qApp dtor
152 qAddPostRoutine([] {
153 g_threadLocalRhiHolder.reset();
154 });
155 }
156}
157
158} // namespace
159
160QRhi *qEnsureThreadLocalRhi(QRhi *referenceRhi)
161{
162 if (!g_threadLocalRhiHolder)
163 g_threadLocalRhiHolder.emplace();
164
165 return g_threadLocalRhiHolder->ensureRhi(referenceRhi);
166}
167
168void qSetPreferredThreadLocalRhiBackend(QRhi::Implementation backend)
169{
170 s_preferredBackend = backend;
171 if (g_threadLocalRhiHolder)
172 g_threadLocalRhiHolder->resetRhi();
173}
174
175QT_END_NAMESPACE
bool canUseRhiImpl(const QRhi::Implementation implementation, const QRhi::Implementation reference)
Combined button and popup list for selecting options.
static QRhi::Implementation s_preferredBackend
QRhi * qEnsureThreadLocalRhi(QRhi *referenceRhi)
void qSetPreferredThreadLocalRhiBackend(QRhi::Implementation backend)