Revision control

Copy as Markdown

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include "qsgcontextplugin_p.h"
#include <QtQuick/private/qsgcontext_p.h>
#include <QtGui/qguiapplication.h>
#include <QtCore/private/qfactoryloader_p.h>
#include <QtCore/qlibraryinfo.h>
// Built-in adaptations
#include <QtQuick/private/qsgsoftwareadaptation_p.h>
#include <QtQuick/private/qsgdefaultcontext_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatformintegration.h>
QT_BEGIN_NAMESPACE
QSGContextPlugin::QSGContextPlugin(QObject *parent)
: QObject(parent)
{
}
QSGContextPlugin::~QSGContextPlugin()
{
}
#if QT_CONFIG(library)
Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
(QSGContextFactoryInterface_iid, QLatin1String("/scenegraph")))
#endif
struct QSGAdaptationBackendData
{
QSGAdaptationBackendData();
~QSGAdaptationBackendData();
Q_DISABLE_COPY(QSGAdaptationBackendData)
bool tried = false;
QSGContextFactoryInterface *factory = nullptr;
QString name;
QSGContextFactoryInterface::Flags flags;
QList<QSGContextFactoryInterface *> builtIns;
QString quickWindowBackendRequest;
};
QSGAdaptationBackendData::QSGAdaptationBackendData()
{
// Fill in the table with the built-in adaptations.
builtIns.append(new QSGSoftwareAdaptation);
}
QSGAdaptationBackendData::~QSGAdaptationBackendData()
{
qDeleteAll(builtIns);
}
Q_GLOBAL_STATIC(QSGAdaptationBackendData, qsg_adaptation_data)
// This only works when the backend is loaded (contextFactory() was called),
// otherwise the return value is 0.
//
// Note that the default (OpenGL) implementation always results in 0, custom flags
// can only be returned from the other (either compiled-in or plugin-based) backends.
QSGContextFactoryInterface::Flags qsg_backend_flags()
{
return qsg_adaptation_data()->flags;
}
QSGAdaptationBackendData *contextFactory()
{
QSGAdaptationBackendData *backendData = qsg_adaptation_data();
if (!backendData->tried) {
backendData->tried = true;
const QStringList args = QGuiApplication::arguments();
QString requestedBackend = backendData->quickWindowBackendRequest; // empty or set via QQuickWindow::setSceneGraphBackend()
for (int index = 0; index < args.size(); ++index) {
if (args.at(index).startsWith(QLatin1String("--device="))) {
requestedBackend = args.at(index).mid(9);
break;
}
}
if (requestedBackend.isEmpty())
requestedBackend = qEnvironmentVariable("QMLSCENE_DEVICE");
// A modern alternative. Scenegraph adaptations can represent backends
// for different graphics APIs as well, instead of being specific to
// some device or platform.
if (requestedBackend.isEmpty())
requestedBackend = qEnvironmentVariable("QT_QUICK_BACKEND");
// If this platform does not support OpenGL, Vulkan, D3D11, or Metal, and no
// backend has been set, default to the software renderer. We rely on the
// static, build time flags only. This is to prevent the inevitable confusion
// caused by run time hocus pocus. If one wants to use the software backend
// in a GL or Vulkan capable Qt build (or on Windows or Apple platforms), it
// has to be requested explicitly.
#if !QT_CONFIG(opengl) && !QT_CONFIG(vulkan) && !QT_CONFIG(metal) && !defined(Q_OS_WIN)
if (requestedBackend.isEmpty())
requestedBackend = QLatin1String("software");
#endif
// As an exception to the above, play nice with platform plugins like
// vnc or linuxfb: Trying to initialize a QRhi is futile on these, and
// Qt 5 had an explicit fallback to the software backend, based on the
// OpenGL capability. Replicate that behavior using the new
// RhiBasedRendering capability flag, which, on certain platforms,
// indicates that we should not even bother trying to initialize a QRhi
// as no 3D API can be expected work.
if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::RhiBasedRendering)) {
if (requestedBackend.isEmpty())
requestedBackend = QLatin1String("software");
}
// This is handy if some of the logic above goes wrong and we select
// e.g. the software backend when it is not desired.
if (requestedBackend == QLatin1String("rhi"))
requestedBackend.clear(); // empty = no custom backend to load
if (!requestedBackend.isEmpty()) {
qCDebug(QSG_LOG_INFO, "Loading backend %s", qUtf8Printable(requestedBackend));
// First look for a built-in adaptation.
for (QSGContextFactoryInterface *builtInBackend : std::as_const(backendData->builtIns)) {
if (builtInBackend->keys().contains(requestedBackend)) {
backendData->factory = builtInBackend;
backendData->name = requestedBackend;
backendData->flags = backendData->factory->flags(requestedBackend);
break;
}
}
#if QT_CONFIG(library)
// Then try the plugins.
if (!backendData->factory) {
const int index = loader()->indexOf(requestedBackend);
if (index != -1)
backendData->factory = qobject_cast<QSGContextFactoryInterface*>(loader()->instance(index));
if (backendData->factory) {
backendData->name = requestedBackend;
backendData->flags = backendData->factory->flags(requestedBackend);
}
if (!backendData->factory) {
qWarning("Could not create scene graph context for backend '%s'"
" - check that plugins are installed correctly in %s",
qPrintable(requestedBackend),
qPrintable(QLibraryInfo::paths(QLibraryInfo::PluginsPath).value(0)));
}
}
#endif // library
}
}
return backendData;
}
/*!
\fn QSGContext *QSGContext::createDefaultContext()
Creates a default scene graph context for the current hardware.
This may load a device-specific plugin.
*/
QSGContext *QSGContext::createDefaultContext()
{
QSGAdaptationBackendData *backendData = contextFactory();
if (backendData->factory)
return backendData->factory->create(backendData->name);
return new QSGDefaultContext();
}
/*!
Calls into the scene graph adaptation if available and creates a texture
factory. The primary purpose of this function is to reimplement hardware
specific asynchronous texture frameskip-less uploads that can happen on
the image providers thread.
*/
QQuickTextureFactory *QSGContext::createTextureFactoryFromImage(const QImage &image)
{
QSGAdaptationBackendData *backendData = contextFactory();
if (backendData->factory)
return backendData->factory->createTextureFactoryFromImage(image);
return nullptr;
}
/*!
Calls into the scene graph adaptation if available and creates a hardware
specific window manager.
*/
QSGRenderLoop *QSGContext::createWindowManager()
{
QSGAdaptationBackendData *backendData = contextFactory();
if (backendData->factory)
return backendData->factory->createWindowManager();
return nullptr;
}
void QSGContext::setBackend(const QString &backend)
{
QSGAdaptationBackendData *backendData = qsg_adaptation_data();
if (backendData->tried)
qWarning("Scenegraph already initialized, setBackend() request ignored");
backendData->quickWindowBackendRequest = backend;
}
QString QSGContext::backend()
{
QSGAdaptationBackendData *backendData = qsg_adaptation_data();
if (backendData->tried)
return backendData->name;
return backendData->quickWindowBackendRequest;
}
QT_END_NAMESPACE
#include "moc_qsgcontextplugin_p.cpp"