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
4#include "qthreadlocalrhi_p.h"
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#include <QtGui/rhi/qrhi.h>
12
13#if defined(Q_OS_ANDROID)
14# include <QtCore/qmetaobject.h>
15#endif
16
17QT_BEGIN_NAMESPACE
18
19namespace {
20
21class ThreadLocalRhiHolder
22{
23public:
24 ThreadLocalRhiHolder();
25 ~ThreadLocalRhiHolder() { resetRhi(); }
26
27 QRhi *ensureRhi(QRhi *referenceRhi)
28 {
29 if (m_rhi || m_cpuOnly)
30 return m_rhi.get();
31
32 QRhi::Implementation referenceBackend = referenceRhi ? referenceRhi->backend() : QRhi::Null;
33 const QPlatformIntegration *qpa = QGuiApplicationPrivate::platformIntegration();
34
35 if (qpa && qpa->hasCapability(cap: QPlatformIntegration::RhiBasedRendering)) {
36
37#if QT_CONFIG(metal)
38 if (referenceBackend == QRhi::Metal || referenceBackend == QRhi::Null) {
39 QRhiMetalInitParams params;
40 m_rhi.reset(QRhi::create(QRhi::Metal, &params));
41 }
42#endif
43
44#if defined(Q_OS_WIN)
45 if (referenceBackend == QRhi::D3D11 || referenceBackend == QRhi::Null) {
46 QRhiD3D11InitParams params;
47 m_rhi.reset(QRhi::create(QRhi::D3D11, &params));
48 }
49#endif
50
51#if QT_CONFIG(opengl)
52 if (!m_rhi && (referenceBackend == QRhi::OpenGLES2 || referenceBackend == QRhi::Null)) {
53 if (qpa->hasCapability(cap: QPlatformIntegration::OpenGL)
54 && qpa->hasCapability(cap: QPlatformIntegration::RasterGLSurface)
55 && !QCoreApplication::testAttribute(attribute: Qt::AA_ForceRasterWidgets)) {
56
57 m_fallbackSurface.reset(p: QRhiGles2InitParams::newFallbackSurface());
58 QRhiGles2InitParams params;
59 params.fallbackSurface = m_fallbackSurface.get();
60 if (referenceBackend == QRhi::OpenGLES2)
61 params.shareContext = static_cast<const QRhiGles2NativeHandles *>(
62 referenceRhi->nativeHandles())
63 ->context;
64 m_rhi.reset(p: QRhi::create(impl: QRhi::OpenGLES2, params: &params));
65
66# if defined(Q_OS_ANDROID)
67 // reset RHI state on application suspension, as this will be invalid after
68 // resuming
69 if (!m_appStateChangedConnection) {
70 if (!m_eventsReceiver)
71 m_eventsReceiver = std::make_unique<QObject>();
72
73 auto onStateChanged = [this](auto state) {
74 if (state == Qt::ApplicationSuspended)
75 resetRhi();
76 };
77
78 m_appStateChangedConnection =
79 QObject::connect(qApp, &QGuiApplication::applicationStateChanged,
80 m_eventsReceiver.get(), onStateChanged);
81 }
82# endif
83 }
84 }
85#endif
86 }
87
88 if (!m_rhi) {
89 m_cpuOnly = true;
90 qWarning() << Q_FUNC_INFO << ": No RHI backend. Using CPU conversion.";
91 }
92
93 return m_rhi.get();
94 }
95
96private:
97 void resetRhi()
98 {
99 m_rhi.reset();
100#if QT_CONFIG(opengl)
101 m_fallbackSurface.reset();
102#endif
103 m_cpuOnly = false;
104 }
105
106private:
107 std::unique_ptr<QRhi> m_rhi;
108#if QT_CONFIG(opengl)
109 std::unique_ptr<QOffscreenSurface> m_fallbackSurface;
110#endif
111 bool m_cpuOnly = false;
112#if defined(Q_OS_ANDROID)
113 std::unique_ptr<QObject> m_eventsReceiver;
114 // we keep and check QMetaObject::Connection because the sender, qApp,
115 // can be recreated and the connection invalidated.
116 QMetaObject::Connection m_appStateChangedConnection;
117#endif
118};
119
120Q_CONSTINIT thread_local std::optional<ThreadLocalRhiHolder> g_threadLocalRhiHolder;
121
122ThreadLocalRhiHolder::ThreadLocalRhiHolder()
123{
124 if (QThread::isMainThread()) {
125 // ensure cleanup in qApp dtor
126 qAddPostRoutine([] {
127 g_threadLocalRhiHolder.reset();
128 });
129 }
130}
131
132} // namespace
133
134QRhi *qEnsureThreadLocalRhi(QRhi *referenceRhi)
135{
136 if (!g_threadLocalRhiHolder)
137 g_threadLocalRhiHolder.emplace();
138
139 return g_threadLocalRhiHolder->ensureRhi(referenceRhi);
140}
141
142QT_END_NAMESPACE
143

source code of qtmultimedia/src/multimedia/qthreadlocalrhi.cpp