1// Copyright (C) 2016 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 "qsgcontextplugin_p.h"
5#include <QtQuick/private/qsgcontext_p.h>
6#include <QtGui/qguiapplication.h>
7#include <QtCore/private/qfactoryloader_p.h>
8#include <QtCore/qlibraryinfo.h>
9
10// Built-in adaptations
11#include <QtQuick/private/qsgsoftwareadaptation_p.h>
12#include <QtQuick/private/qsgdefaultcontext_p.h>
13
14#include <QtGui/private/qguiapplication_p.h>
15#include <QtGui/qpa/qplatformintegration.h>
16
17QT_BEGIN_NAMESPACE
18
19Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_INFO)
20
21QSGContextPlugin::QSGContextPlugin(QObject *parent)
22 : QObject(parent)
23{
24}
25
26QSGContextPlugin::~QSGContextPlugin()
27{
28}
29
30#if QT_CONFIG(library)
31Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
32 (QSGContextFactoryInterface_iid, QLatin1String("/scenegraph")))
33#endif
34
35struct QSGAdaptationBackendData
36{
37 QSGAdaptationBackendData();
38 ~QSGAdaptationBackendData();
39 Q_DISABLE_COPY(QSGAdaptationBackendData)
40
41 bool tried = false;
42 QSGContextFactoryInterface *factory = nullptr;
43 QString name;
44 QSGContextFactoryInterface::Flags flags;
45
46 QVector<QSGContextFactoryInterface *> builtIns;
47
48 QString quickWindowBackendRequest;
49};
50
51QSGAdaptationBackendData::QSGAdaptationBackendData()
52{
53 // Fill in the table with the built-in adaptations.
54 builtIns.append(t: new QSGSoftwareAdaptation);
55}
56
57QSGAdaptationBackendData::~QSGAdaptationBackendData()
58{
59 qDeleteAll(c: builtIns);
60}
61
62Q_GLOBAL_STATIC(QSGAdaptationBackendData, qsg_adaptation_data)
63
64// This only works when the backend is loaded (contextFactory() was called),
65// otherwise the return value is 0.
66//
67// Note that the default (OpenGL) implementation always results in 0, custom flags
68// can only be returned from the other (either compiled-in or plugin-based) backends.
69QSGContextFactoryInterface::Flags qsg_backend_flags()
70{
71 return qsg_adaptation_data()->flags;
72}
73
74QSGAdaptationBackendData *contextFactory()
75{
76 QSGAdaptationBackendData *backendData = qsg_adaptation_data();
77
78 if (!backendData->tried) {
79 backendData->tried = true;
80
81 const QStringList args = QGuiApplication::arguments();
82 QString requestedBackend = backendData->quickWindowBackendRequest; // empty or set via QQuickWindow::setSceneGraphBackend()
83
84 for (int index = 0; index < args.size(); ++index) {
85 if (args.at(i: index).startsWith(s: QLatin1String("--device="))) {
86 requestedBackend = args.at(i: index).mid(position: 9);
87 break;
88 }
89 }
90
91 if (requestedBackend.isEmpty())
92 requestedBackend = qEnvironmentVariable(varName: "QMLSCENE_DEVICE");
93
94 // A modern alternative. Scenegraph adaptations can represent backends
95 // for different graphics APIs as well, instead of being specific to
96 // some device or platform.
97 if (requestedBackend.isEmpty())
98 requestedBackend = qEnvironmentVariable(varName: "QT_QUICK_BACKEND");
99
100 // If this platform does not support OpenGL, Vulkan, D3D11, or Metal, and no
101 // backend has been set, default to the software renderer. We rely on the
102 // static, build time flags only. This is to prevent the inevitable confusion
103 // caused by run time hocus pocus. If one wants to use the software backend
104 // in a GL or Vulkan capable Qt build (or on Windows or Apple platforms), it
105 // has to be requested explicitly.
106#if !QT_CONFIG(opengl) && !QT_CONFIG(vulkan) && !defined(Q_OS_WIN) && !defined(Q_OS_MACOS) && !defined(Q_OS_IOS)
107 if (requestedBackend.isEmpty())
108 requestedBackend = QLatin1String("software");
109#endif
110
111 // As an exception to the above, play nice with platform plugins like
112 // vnc or linuxfb: Trying to initialize a QRhi is futile on these, and
113 // Qt 5 had an explicit fallback to the software backend, based on the
114 // OpenGL capability. Replicate that behavior using the new
115 // RhiBasedRendering capability flag, which, on certain platforms,
116 // indicates that we should not even bother trying to initialize a QRhi
117 // as no 3D API can be expected work.
118 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::RhiBasedRendering)) {
119 if (requestedBackend.isEmpty())
120 requestedBackend = QLatin1String("software");
121 }
122
123 // This is handy if some of the logic above goes wrong and we select
124 // e.g. the software backend when it is not desired.
125 if (requestedBackend == QLatin1String("rhi"))
126 requestedBackend.clear(); // empty = no custom backend to load
127
128 if (!requestedBackend.isEmpty()) {
129 qCDebug(QSG_LOG_INFO, "Loading backend %s", qUtf8Printable(requestedBackend));
130
131 // First look for a built-in adaptation.
132 for (QSGContextFactoryInterface *builtInBackend : std::as_const(t&: backendData->builtIns)) {
133 if (builtInBackend->keys().contains(str: requestedBackend)) {
134 backendData->factory = builtInBackend;
135 backendData->name = requestedBackend;
136 backendData->flags = backendData->factory->flags(key: requestedBackend);
137 break;
138 }
139 }
140
141#if QT_CONFIG(library)
142 // Then try the plugins.
143 if (!backendData->factory) {
144 const int index = loader()->indexOf(needle: requestedBackend);
145 if (index != -1)
146 backendData->factory = qobject_cast<QSGContextFactoryInterface*>(object: loader()->instance(index));
147 if (backendData->factory) {
148 backendData->name = requestedBackend;
149 backendData->flags = backendData->factory->flags(key: requestedBackend);
150 }
151 if (!backendData->factory) {
152 qWarning(msg: "Could not create scene graph context for backend '%s'"
153 " - check that plugins are installed correctly in %s",
154 qPrintable(requestedBackend),
155 qPrintable(QLibraryInfo::path(QLibraryInfo::PluginsPath)));
156 }
157 }
158#endif // library
159 }
160 }
161
162 return backendData;
163}
164
165
166
167/*!
168 \fn QSGContext *QSGContext::createDefaultContext()
169
170 Creates a default scene graph context for the current hardware.
171 This may load a device-specific plugin.
172*/
173QSGContext *QSGContext::createDefaultContext()
174{
175 QSGAdaptationBackendData *backendData = contextFactory();
176 if (backendData->factory)
177 return backendData->factory->create(key: backendData->name);
178 return new QSGDefaultContext();
179}
180
181
182
183/*!
184 Calls into the scene graph adaptation if available and creates a texture
185 factory. The primary purpose of this function is to reimplement hardware
186 specific asynchronous texture frameskip-less uploads that can happen on
187 the image providers thread.
188 */
189
190QQuickTextureFactory *QSGContext::createTextureFactoryFromImage(const QImage &image)
191{
192 QSGAdaptationBackendData *backendData = contextFactory();
193 if (backendData->factory)
194 return backendData->factory->createTextureFactoryFromImage(image);
195 return nullptr;
196}
197
198
199/*!
200 Calls into the scene graph adaptation if available and creates a hardware
201 specific window manager.
202 */
203
204QSGRenderLoop *QSGContext::createWindowManager()
205{
206 QSGAdaptationBackendData *backendData = contextFactory();
207 if (backendData->factory)
208 return backendData->factory->createWindowManager();
209 return nullptr;
210}
211
212void QSGContext::setBackend(const QString &backend)
213{
214 QSGAdaptationBackendData *backendData = qsg_adaptation_data();
215 if (backendData->tried)
216 qWarning(msg: "Scenegraph already initialized, setBackend() request ignored");
217
218 backendData->quickWindowBackendRequest = backend;
219}
220
221QString QSGContext::backend()
222{
223 QSGAdaptationBackendData *backendData = qsg_adaptation_data();
224 if (backendData->tried)
225 return backendData->name;
226
227 return backendData->quickWindowBackendRequest;
228}
229
230QT_END_NAMESPACE
231
232#include "moc_qsgcontextplugin_p.cpp"
233

source code of qtdeclarative/src/quick/scenegraph/qsgcontextplugin.cpp