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/qloggingcategory.h>
8#include <QtCore/qthreadstorage.h>
9#include <QtGui/private/qguiapplication_p.h>
10#include <QtGui/qoffscreensurface.h>
11#include <QtGui/qpa/qplatformintegration.h>
12
13#if QT_CONFIG(vulkan)
14# include <QtGui/qvulkaninstance.h>
15#endif
16
17#if defined(Q_OS_ANDROID)
18# include <QtCore/qmetaobject.h>
19#endif
20
22
23namespace {
24
25Q_STATIC_LOGGING_CATEGORY(qLcThreadLocalRhi, "qt.multimedia.threadlocalrhi")
26
27static thread_local QRhi::Implementation s_preferredBackend = QRhi::Null;
28
29#if QT_CONFIG(opengl)
30
31bool openGLCapsSupported(const QPlatformIntegration &qpa)
32{
33 return qpa.hasCapability(QPlatformIntegration::OpenGL) &&
34 !QCoreApplication::testAttribute(Qt::AA_ForceRasterWidgets) &&
35 (QThread::isMainThread() ||
36 (qpa.hasCapability(QPlatformIntegration::ThreadedOpenGL) &&
37 qpa.hasCapability(QPlatformIntegration::OffscreenSurface)));
38}
39#endif
40
42{
43public:
45
46 QRhi *ensureRhi([[maybe_unused]] QRhi::Implementation backend)
47 {
48 if (m_rhi || m_cpuOnly)
49 return m_rhi.get();
50
51 const QPlatformIntegration *qpa = QGuiApplicationPrivate::platformIntegration();
52
53 if (qpa && qpa->hasCapability(QPlatformIntegration::RhiBasedRendering)) {
54
55#if QT_CONFIG(metal)
56 if (canUseRhiImpl(QRhi::Metal, backend)) {
57 QRhiMetalInitParams params;
58 m_rhi.reset(QRhi::create(QRhi::Metal, &params));
59 }
60#endif
61
62#if defined(Q_OS_WIN)
63 if (!m_rhi && canUseRhiImpl(QRhi::D3D11, backend)) {
64 QRhiD3D11InitParams params;
65 m_rhi.reset(QRhi::create(QRhi::D3D11, &params));
66 }
67
68 if (!m_rhi && canUseRhiImpl(QRhi::D3D12, backend)) {
69 QRhiD3D12InitParams params;
70 m_rhi.reset(QRhi::create(QRhi::D3D12, &params));
71 }
72#endif
73
74#if QT_CONFIG(opengl)
75 if (!m_rhi && canUseRhiImpl(QRhi::OpenGLES2, backend)) {
76 if (openGLCapsSupported(*qpa)) {
77
78 m_fallbackSurface.reset(QRhiGles2InitParams::newFallbackSurface());
79 QRhiGles2InitParams params;
80 params.fallbackSurface = m_fallbackSurface.get();
81 m_rhi.reset(QRhi::create(QRhi::OpenGLES2, &params));
82
83# if defined(Q_OS_ANDROID)
84 // reset RHI state on application suspension, as this will be invalid after
85 // resuming
86 if (!m_appStateChangedConnection) {
87 if (!m_eventsReceiver)
88 m_eventsReceiver = std::make_unique<QObject>();
89
90 auto onStateChanged = [this](auto state) {
91 if (state == Qt::ApplicationSuspended)
92 resetRhi();
93 };
94
95 m_appStateChangedConnection =
96 QObject::connect(qApp, &QGuiApplication::applicationStateChanged,
97 m_eventsReceiver.get(), onStateChanged);
98 }
99# endif
100 }
101 }
102#endif
103
104#if QT_CONFIG(vulkan)
105 if (!m_rhi && canUseRhiImpl(QRhi::Vulkan, backend)) {
106 auto instance = std::make_unique<QVulkanInstance>();
107 instance->setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
108 if (instance->create()) {
109 QRhiVulkanInitParams params;
110 params.inst = instance.get();
111 m_rhi.reset(QRhi::create(QRhi::Vulkan, &params));
112 } else {
113 qCDebug(qLcThreadLocalRhi) << "Failed to create QVulkanInstance";
114 }
115
116 if (m_rhi)
117 m_vulkanInstance = std::move(instance);
118 }
119#endif
120 }
121
122 if (!m_rhi) {
123 m_cpuOnly = true;
124 qCWarning(qLcThreadLocalRhi)
125 << Q_FUNC_INFO << ": No RHI backend. Using CPU conversion.";
126 }
127
128 return m_rhi.get();
129 }
130
131 void resetRhi()
132 {
133 m_rhi.reset();
134#if QT_CONFIG(opengl)
135 m_fallbackSurface.reset();
136#endif
137#if QT_CONFIG(vulkan)
138 m_vulkanInstance.reset();
139#endif
140 m_cpuOnly = false;
141 }
142
143 bool canUseRhiImpl(const QRhi::Implementation implementation,
144 const QRhi::Implementation reference)
145 {
146 // First priority goes to reference backend
147 if (reference != QRhi::Null)
148 return implementation == reference;
149
150 // If no reference, but preference exists, compare to that
151 if (s_preferredBackend != QRhi::Null)
152 return implementation == s_preferredBackend;
153
154 // Can use (assuming platform and configuration allow)
155 return true;
156 }
157
158private:
159 std::unique_ptr<QRhi> m_rhi;
160#if QT_CONFIG(opengl)
162#endif
163#if QT_CONFIG(vulkan)
165#endif
166 bool m_cpuOnly = false;
167#if defined(Q_OS_ANDROID)
169 // we keep and check QMetaObject::Connection because the sender, qApp,
170 // can be recreated and the connection invalidated.
172#endif
173};
174
176
177}
178
179QRhi *qEnsureThreadLocalRhi(QRhi::Implementation backend)
180{
181 return g_threadLocalRhiHolder.localData().ensureRhi(backend);
182}
183
184void qSetPreferredThreadLocalRhiBackend(QRhi::Implementation backend)
185{
186 if (s_preferredBackend != backend) {
187 s_preferredBackend = backend;
188 g_threadLocalRhiHolder.localData().resetRhi();
189 }
190}
191
192QT_END_NAMESPACE
QRhi * ensureRhi(QRhi::Implementation backend)
bool canUseRhiImpl(const QRhi::Implementation implementation, const QRhi::Implementation reference)
Combined button and popup list for selecting options.
static QRhi::Implementation s_preferredBackend
QThreadStorage< ThreadLocalRhiHolder > g_threadLocalRhiHolder
QRhi * qEnsureThreadLocalRhi(QRhi::Implementation backend)
void qSetPreferredThreadLocalRhiBackend(QRhi::Implementation backend)