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#endif
68
69#if QT_CONFIG(opengl)
70 if (!m_rhi && canUseRhiImpl(QRhi::OpenGLES2, backend)) {
71 if (openGLCapsSupported(*qpa)) {
72
73 m_fallbackSurface.reset(QRhiGles2InitParams::newFallbackSurface());
74 QRhiGles2InitParams params;
75 params.fallbackSurface = m_fallbackSurface.get();
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#if QT_CONFIG(vulkan)
100 if (!m_rhi && canUseRhiImpl(QRhi::Vulkan, backend)) {
101 auto instance = std::make_unique<QVulkanInstance>();
102 instance->setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
103 if (instance->create()) {
104 QRhiVulkanInitParams params;
105 params.inst = instance.get();
106 m_rhi.reset(QRhi::create(QRhi::Vulkan, &params));
107 } else {
108 qCDebug(qLcThreadLocalRhi) << "Failed to create QVulkanInstance";
109 }
110
111 if (m_rhi)
112 m_vulkanInstance = std::move(instance);
113 }
114#endif
115 }
116
117 if (!m_rhi) {
118 m_cpuOnly = true;
119 qCWarning(qLcThreadLocalRhi)
120 << Q_FUNC_INFO << ": No RHI backend. Using CPU conversion.";
121 }
122
123 return m_rhi.get();
124 }
125
126 void resetRhi()
127 {
128 m_rhi.reset();
129#if QT_CONFIG(opengl)
130 m_fallbackSurface.reset();
131#endif
132#if QT_CONFIG(vulkan)
133 m_vulkanInstance.reset();
134#endif
135 m_cpuOnly = false;
136 }
137
138 bool canUseRhiImpl(const QRhi::Implementation implementation,
139 const QRhi::Implementation reference)
140 {
141 // First priority goes to reference backend
142 if (reference != QRhi::Null)
143 return implementation == reference;
144
145 // If no reference, but preference exists, compare to that
146 if (s_preferredBackend != QRhi::Null)
147 return implementation == s_preferredBackend;
148
149 // Can use (assuming platform and configuration allow)
150 return true;
151 }
152
153private:
154 std::unique_ptr<QRhi> m_rhi;
155#if QT_CONFIG(opengl)
157#endif
158#if QT_CONFIG(vulkan)
160#endif
161 bool m_cpuOnly = false;
162#if defined(Q_OS_ANDROID)
164 // we keep and check QMetaObject::Connection because the sender, qApp,
165 // can be recreated and the connection invalidated.
167#endif
168};
169
171
172}
173
174QRhi *qEnsureThreadLocalRhi(QRhi::Implementation backend)
175{
176 return g_threadLocalRhiHolder.localData().ensureRhi(backend);
177}
178
179void qSetPreferredThreadLocalRhiBackend(QRhi::Implementation backend)
180{
181 if (s_preferredBackend != backend) {
182 s_preferredBackend = backend;
183 g_threadLocalRhiHolder.localData().resetRhi();
184 }
185}
186
187QT_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)