aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2020-01-15 09:08:24 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2020-04-11 18:23:55 +0200
commit400d176760de84626500787674b1ece387b25893 (patch)
treedbb8b5fd50440e6c01d6b940fe1692ba8e6a1f5c /src/quick
parent654959e2e2b74855036f949a9c23c9d9c7a614ac (diff)
Allow redirecting QRhi-based rendering via QQuickRenderControl
Implement the Qt 6 TODO for using an externally-provided render target when rendering the scene via QRhi. And say hello to QQuickRenderTarget. This class exists to allow potentially extending later what a "render target" consists of. Instead of hard-coding taking a single void * in the setRenderTarget() function, it takes a (implicitly shared, d-pointered) QQuickRenderTarget, which in turn can be created via static factory functions - of which new ones can be added later on. The new version of QQuickWindow::setRenderTarget() takes a QQuickRenderTarget. QQuickRenderControl gets a new initialize() variant, and a few extra functions (beginFrame(), endFrame()). This allows it to, by using QSGRhiSupport internally, create a QRhi under the hood. As a bonus, this also fixes an existing scenegraph resource leak when destroying the QQuickRenderControl. The qquickrendercontrol autotest is extended, with a QRhi-based test case that is executed for all of the QRhi backends that succeed to initialize. This is the internal verification. In addition, there is a Vulkan-based one that creates its own VkDevice, VkImage, and friends, and then uses Qt Quick with the same Vulkan device, targeting the VkImage. This test verifies the typical application use case. (sadly, life is too short to waste it on writing Vulkan boilerplate for an on-screen version of this, but we have the D3D11 example instead) What QQuickRenderControl loses, when used in combination with QRhi, is the grab() function. This never made much sense as a public API: QQuickWindow::grabWindow() call this when the window is associated with a rendercontrol, so as a public API QQuickRenderControl::grab() is redundant, because one gets the same result via the standard QQuickWindow API. It is now made private. More importantly, reading back the content is no longer supported, unless the 'software' backend is in use. The reasoning here is that, if the client of the API manages and provides the render target (as abstracted by QQuickRenderTarget), it is then expected to be capable of reading back the content in whatever way it sees fit, because it owns and manages the resource (e.g. the texture) in the first place. Providing fragile convenience functions for this is not reasonable anymore, and was questionable even with OpenGL, given that it is not future proof - what if the target is suddenly a floating point texture, for instance? The software backend case makes sense because that relies on private APIs - and has no render target concept either - so there the same cannot be achieved by applications by relying on public APIs only. Another new class is QQuickGraphicsDevice. This is very similar to QQuickRenderTarget, it is a simple container capable of holding a set of of native objects, mostly in the form of void*s, with future extensibility thanks to the static factory functions. (examples of native object sets would be a ID3D11Device + ID3D11DeviceContext, or a QOpenGLContext, or a MTLDevice + MTLCommandQueue, or a number of Vulkan device-related objects, etc.) This allows one to specify that the QRhi created under the hood (either by QQuickRenderControl or by the render loop) should use an existing graphics device (i.e. it is basically a public wrapper for values that go into a QRhi*InitParams under the hood). QQuickRenderTarget and QQuickGraphicsDevice are both demonstrated in a new example: rendercontrol_d3d11. We choose D3D11 because it is reasonably simple to set up a renderer with a window, and, because there is known user demand for Qt Quick - external D3D engine interop. Passing in the custom engine's own ID3D11Device and ID3D11DeviceContext is essential: the texture (ID3D11Texture2D) Qt Quick is targeting would not be usable if Qt Quick's QRhi was using a different ID3D11Device. Task-number: QTBUG-78595 Change-Id: I5dfe7f6cf1540daffc2f11136be114a08e87202b Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/quick')
-rw-r--r--src/quick/CMakeLists.txt2
-rw-r--r--src/quick/items/items.pri10
-rw-r--r--src/quick/items/qquickgraphicsdevice.cpp216
-rw-r--r--src/quick/items/qquickgraphicsdevice.h73
-rw-r--r--src/quick/items/qquickgraphicsdevice_p.h105
-rw-r--r--src/quick/items/qquickrendercontrol.cpp316
-rw-r--r--src/quick/items/qquickrendercontrol.h19
-rw-r--r--src/quick/items/qquickrendercontrol_p.h18
-rw-r--r--src/quick/items/qquickrendertarget.cpp293
-rw-r--r--src/quick/items/qquickrendertarget.h78
-rw-r--r--src/quick/items/qquickrendertarget_p.h90
-rw-r--r--src/quick/items/qquickwindow.cpp295
-rw-r--r--src/quick/items/qquickwindow.h21
-rw-r--r--src/quick/items/qquickwindow_p.h40
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp4
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp2
-rw-r--r--src/quick/scenegraph/qsgrhisupport.cpp68
-rw-r--r--src/quick/scenegraph/qsgrhisupport_p.h10
18 files changed, 1544 insertions, 116 deletions
diff --git a/src/quick/CMakeLists.txt b/src/quick/CMakeLists.txt
index 88ddc4e257..466c9aedb0 100644
--- a/src/quick/CMakeLists.txt
+++ b/src/quick/CMakeLists.txt
@@ -37,6 +37,7 @@ qt_add_module(Quick
items/qquickflickable_p_p.h
items/qquickflickablebehavior_p.h
items/qquickfocusscope.cpp items/qquickfocusscope_p.h
+ items/qquickgraphicsdevice.cpp items/qquickgraphicsdevice.h items/qquickgraphicsdevice_p.h
items/qquickgraphicsinfo.cpp items/qquickgraphicsinfo_p.h
items/qquickimage.cpp items/qquickimage_p.h
items/qquickimage_p_p.h
@@ -64,6 +65,7 @@ qt_add_module(Quick
items/qquickrectangle.cpp items/qquickrectangle_p.h
items/qquickrectangle_p_p.h
items/qquickrendercontrol.cpp items/qquickrendercontrol.h items/qquickrendercontrol_p.h
+ items/qquickrendertarget.cpp items/qquickrendertarget.h items/qquickrendertarget_p.h
items/qquickscalegrid.cpp
items/qquickscalegrid_p_p.h
items/qquickscreen.cpp items/qquickscreen_p.h
diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri
index c2078b0e04..2d232a0b0a 100644
--- a/src/quick/items/items.pri
+++ b/src/quick/items/items.pri
@@ -65,7 +65,11 @@ HEADERS += \
$$PWD/qquickpalette_p.h \
$$PWD/qquickcolorgroup_p.h \
$$PWD/qquickpalettecolorprovider_p.h \
- $$PWD/qquickpaletteproviderprivatebase_p.h
+ $$PWD/qquickpaletteproviderprivatebase_p.h \
+ $$PWD/qquickrendertarget.h \
+ $$PWD/qquickrendertarget_p.h \
+ $$PWD/qquickgraphicsdevice.h \
+ $$PWD/qquickgraphicsdevice_p.h
SOURCES += \
$$PWD/qquickevents.cpp \
@@ -108,7 +112,9 @@ SOURCES += \
$$PWD/qquickitemgrabresult.cpp \
$$PWD/qquickpalettecolorprovider.cpp \
$$PWD/qquickcolorgroup.cpp \
- $$PWD/qquickpalette.cpp
+ $$PWD/qquickpalette.cpp \
+ $$PWD/qquickrendertarget.cpp \
+ $$PWD/qquickgraphicsdevice.cpp
qtConfig(quick-draganddrop) {
HEADERS += \
diff --git a/src/quick/items/qquickgraphicsdevice.cpp b/src/quick/items/qquickgraphicsdevice.cpp
new file mode 100644
index 0000000000..029546a5f8
--- /dev/null
+++ b/src/quick/items/qquickgraphicsdevice.cpp
@@ -0,0 +1,216 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickgraphicsdevice_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QQuickGraphicsDevice
+ \since 6.0
+ \inmodule QtQuick
+
+ \brief The QQuickGraphicsDevice class provides an opaque container for
+ native graphics objects representing graphics devices or contexts.
+
+ \sa QQuickWindow::setGraphicsDevice(), QQuickRenderTarget
+*/
+
+/*!
+ Constructs a default QQuickGraphicsDEvice that does not reference any native
+ objects.
+ */
+QQuickGraphicsDevice::QQuickGraphicsDevice()
+ : d(new QQuickGraphicsDevicePrivate)
+{
+}
+
+/*!
+ \internal
+ */
+void QQuickGraphicsDevice::detach()
+{
+ qAtomicDetach(d);
+}
+
+/*!
+ \internal
+ */
+QQuickGraphicsDevice::QQuickGraphicsDevice(const QQuickGraphicsDevice &other)
+ : d(other.d)
+{
+ d->ref.ref();
+}
+
+/*!
+ \internal
+ */
+QQuickGraphicsDevice &QQuickGraphicsDevice::operator=(const QQuickGraphicsDevice &other)
+{
+ qAtomicAssign(d, other.d);
+ return *this;
+}
+
+/*!
+ Destructor.
+ */
+QQuickGraphicsDevice::~QQuickGraphicsDevice()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+/*!
+ Constructs a default QQuickRenderTarget that does not reference any native
+ objects.
+ */
+bool QQuickGraphicsDevice::isNull() const
+{
+ return d->type == QQuickGraphicsDevicePrivate::Type::Null;
+}
+
+/*!
+ \return a new QQuickGraphicsDevice referencing an existing QOpenGLContext.
+
+ This factory function is suitable for OpenGL.
+ */
+QQuickGraphicsDevice QQuickGraphicsDevice::fromOpenGLContext(QOpenGLContext *context)
+{
+ QQuickGraphicsDevice dev;
+ QQuickGraphicsDevicePrivate *d = QQuickGraphicsDevicePrivate::get(&dev);
+ d->type = QQuickGraphicsDevicePrivate::Type::OpenGLContext;
+ d->u.context = context;
+ return dev;
+}
+
+/*!
+ \return a new QQuickGraphicsDevice referencing a native device and context
+ object.
+
+ This factory function is suitable for:
+
+ \list
+
+ \li Direct3D11 - \a device is expected to be a \c{ID3D11Device*}, \a
+ context is expected to be a \c{ID3D11DeviceContext*}.
+
+ \endlist
+
+ \note the resulting QQuickGraphicsDevice does not own any native resources,
+ it merely contains references. It is the caller's responsibility to ensure
+ that the native resource exists as long as necessary.
+
+ */
+QQuickGraphicsDevice QQuickGraphicsDevice::fromDeviceAndContext(void *device, void *context)
+{
+ QQuickGraphicsDevice dev;
+ QQuickGraphicsDevicePrivate *d = QQuickGraphicsDevicePrivate::get(&dev);
+ d->type = QQuickGraphicsDevicePrivate::Type::DeviceAndContext;
+ d->u.deviceAndContext = { device, context };
+ return dev;
+}
+
+/*!
+ \return a new QQuickGraphicsDevice referencing a native device and command
+ queue object.
+
+ This factory function is suitable for:
+
+ \list
+
+ \li Metal - \a device is expected to be a \c{MTLDevice*}, \a cmdQueue is
+ expected to be a \c{MTLCommandQueue*}.
+
+ \endlist
+
+ \note the resulting QQuickGraphicsDevice does not own any native resources,
+ it merely contains references. It is the caller's responsibility to ensure
+ that the native resource exists as long as necessary.
+
+ */
+QQuickGraphicsDevice QQuickGraphicsDevice::fromDeviceAndCommandQueue(void *device, void *cmdQueue)
+{
+ QQuickGraphicsDevice dev;
+ QQuickGraphicsDevicePrivate *d = QQuickGraphicsDevicePrivate::get(&dev);
+ d->type = QQuickGraphicsDevicePrivate::Type::DeviceAndCommandQueue;
+ d->u.deviceAndCommandQueue = { device, cmdQueue };
+ return dev;
+}
+
+/*!
+ \return a new QQuickGraphicsDevice referencing a native device and related
+ objects.
+
+ This factory function is suitable for:
+
+ \list
+
+ \li Vulkan - \a physicalDevice is expected to be \c VkPhysicalDevice, \a
+ device is expected to be a \a VkDevice, while \a queueFamilyIndex is the
+ index of the graphics queue family on the device.
+
+ \endlist
+
+ \note the resulting QQuickGraphicsDevice does not own any native resources,
+ it merely contains references. It is the caller's responsibility to ensure
+ that the native resource exists as long as necessary.
+
+ */
+QQuickGraphicsDevice QQuickGraphicsDevice::fromDeviceObjects(void *physicalDevice, void *device, int queueFamilyIndex)
+{
+ QQuickGraphicsDevice dev;
+ QQuickGraphicsDevicePrivate *d = QQuickGraphicsDevicePrivate::get(&dev);
+ d->type = QQuickGraphicsDevicePrivate::Type::DeviceObjects;
+ d->u.deviceObjects = { physicalDevice, device, queueFamilyIndex };
+ return dev;
+}
+
+QQuickGraphicsDevicePrivate::QQuickGraphicsDevicePrivate()
+ : ref(1)
+{
+}
+
+QQuickGraphicsDevicePrivate::QQuickGraphicsDevicePrivate(const QQuickGraphicsDevicePrivate *other)
+ : ref(1),
+ type(other->type),
+ u(other->u)
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickgraphicsdevice.h b/src/quick/items/qquickgraphicsdevice.h
new file mode 100644
index 0000000000..811d510a5f
--- /dev/null
+++ b/src/quick/items/qquickgraphicsdevice.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKGRAPHICSDEVICE_H
+#define QQUICKGRAPHICSDEVICE_H
+
+#include <QtQuick/qtquickglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickGraphicsDevicePrivate;
+class QOpenGLContext;
+
+class Q_QUICK_EXPORT QQuickGraphicsDevice
+{
+public:
+ QQuickGraphicsDevice();
+ ~QQuickGraphicsDevice();
+ QQuickGraphicsDevice(const QQuickGraphicsDevice &other);
+ QQuickGraphicsDevice &operator=(const QQuickGraphicsDevice &other);
+
+ bool isNull() const;
+
+ static QQuickGraphicsDevice fromOpenGLContext(QOpenGLContext *context);
+ static QQuickGraphicsDevice fromDeviceAndContext(void *device, void *context);
+ static QQuickGraphicsDevice fromDeviceAndCommandQueue(void *device, void *cmdQueue);
+ static QQuickGraphicsDevice fromDeviceObjects(void *physicalDevice, void *device, int queueFamilyIndex);
+
+private:
+ void detach();
+ QQuickGraphicsDevicePrivate *d;
+ friend class QQuickGraphicsDevicePrivate;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKGRAPHICSDEVICE_H
diff --git a/src/quick/items/qquickgraphicsdevice_p.h b/src/quick/items/qquickgraphicsdevice_p.h
new file mode 100644
index 0000000000..0a206725a6
--- /dev/null
+++ b/src/quick/items/qquickgraphicsdevice_p.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKGRAPHICSDEVICE_P_H
+#define QQUICKGRAPHICSDEVICE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/private/qtquickglobal_p.h>
+#include <QAtomicInt>
+#include "qquickgraphicsdevice.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QQuickGraphicsDevicePrivate
+{
+public:
+ static QQuickGraphicsDevicePrivate *get(QQuickGraphicsDevice *p) { return p->d; }
+ static const QQuickGraphicsDevicePrivate *get(const QQuickGraphicsDevice *p) { return p->d; }
+ QQuickGraphicsDevicePrivate();
+ QQuickGraphicsDevicePrivate(const QQuickGraphicsDevicePrivate *other);
+
+ enum class Type {
+ Null,
+ OpenGLContext,
+ DeviceAndContext,
+ DeviceAndCommandQueue,
+ DeviceObjects
+ };
+
+ QAtomicInt ref;
+ Type type = Type::Null;
+
+ struct DeviceAndContext {
+ void *device;
+ void *context;
+ };
+
+ struct DeviceAndCommandQueue {
+ void *device;
+ void *cmdQueue;
+ };
+
+ struct DeviceObjects {
+ void *physicalDevice;
+ void *device;
+ int queueFamilyIndex;
+ };
+
+ union {
+ QOpenGLContext *context;
+ DeviceAndContext deviceAndContext;
+ DeviceAndCommandQueue deviceAndCommandQueue;
+ DeviceObjects deviceObjects;
+ } u;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKGRAPHICSDEVICE_P_H
diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp
index 190e0942ab..b14a3f35f7 100644
--- a/src/quick/items/qquickrendercontrol.cpp
+++ b/src/quick/items/qquickrendercontrol.cpp
@@ -43,29 +43,36 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QTime>
#include <QtQuick/private/qquickanimatorcontroller_p.h>
+#include <QtQuick/private/qsgdefaultrendercontext_p.h>
+#include <QtQuick/private/qsgrhisupport_p.h>
#if QT_CONFIG(opengl)
# include <QOpenGLContext>
-# include <QtQuick/private/qsgdefaultrendercontext_p.h>
#if QT_CONFIG(quick_shadereffect)
# include <QtQuick/private/qquickopenglshadereffectnode_p.h>
#endif
#endif
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
+#include <QtGui/qoffscreensurface.h>
#include <QtQml/private/qqmlglobal_p.h>
#include <QtQuick/QQuickWindow>
+#include <QtQuick/QQuickRenderTarget>
#include <QtQuick/private/qquickwindow_p.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qsgsoftwarerenderer_p.h>
#include <QtCore/private/qobject_p.h>
+#include <QtQuick/private/qquickwindow_p.h>
+#include <QtGui/private/qrhi_p.h>
+
QT_BEGIN_NAMESPACE
#if QT_CONFIG(opengl)
extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
#endif
+
/*!
\class QQuickRenderControl
@@ -136,9 +143,14 @@ extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_
QSGContext *QQuickRenderControlPrivate::sg = nullptr;
-QQuickRenderControlPrivate::QQuickRenderControlPrivate()
- : initialized(0),
- window(nullptr)
+QQuickRenderControlPrivate::QQuickRenderControlPrivate(QQuickRenderControl *renderControl)
+ : q(renderControl),
+ initialized(false),
+ window(nullptr),
+ rhi(nullptr),
+ cb(nullptr),
+ offscreenSurface(nullptr),
+ sampleCount(1)
{
if (!sg) {
qAddPostRoutine(cleanup);
@@ -158,7 +170,7 @@ void QQuickRenderControlPrivate::cleanup()
object \a parent.
*/
QQuickRenderControl::QQuickRenderControl(QObject *parent)
- : QObject(*(new QQuickRenderControlPrivate), parent)
+ : QObject(*(new QQuickRenderControlPrivate(this)), parent)
{
}
@@ -182,11 +194,16 @@ QQuickRenderControl::~QQuickRenderControl()
d->windowDestroyed();
delete d->rc;
+
+ d->resetRhi();
}
void QQuickRenderControlPrivate::windowDestroyed()
{
if (window) {
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
+ cd->cleanupNodesOnShutdown();
+
rc->invalidate();
QQuickWindowPrivate::get(window)->animationController.reset();
@@ -215,6 +232,90 @@ void QQuickRenderControl::prepareThread(QThread *targetThread)
}
/*!
+ Sets the number of samples to use for multisampling. When \a sampleCount is
+ 0 or 1, multisampling is disabled.
+
+ \note This function is always used in combination with a multisample render
+ target, which means \a sampleCount must match the sample count passed to
+ QQuickRenderTarget::fromNativeTexture(), which in turn must match the
+ sample count of the native texture.
+
+ \since 6.0
+
+ \sa initialize(), QQuickRenderTarget
+ */
+void QQuickRenderControl::setSamples(int sampleCount)
+{
+ Q_D(QQuickRenderControl);
+ d->sampleCount = qMax(1, sampleCount);
+}
+
+/*!
+ \return the current sample count. 1 or 0 means no multisampling.
+
+ \since 6.0
+ */
+int QQuickRenderControl::samples() const
+{
+ Q_D(const QQuickRenderControl);
+ return d->sampleCount;
+}
+
+/*!
+ Initializes the scene graph resources. When using a graphics API, such as
+ Vulkan, Metal, OpenGL, or Direct3D, for Qt Quick rendering,
+ QQuickRenderControl will set up an appropriate rendering engine when this
+ function is called. This rendering infrastructure exists as long as the
+ QQuickRenderControl exists.
+
+ To control what graphics API Qt Quick uses, call
+ QQuickWindow::setSceneGraphBackend() with one of the
+ QSGRendererInterface:GraphicsApi constants. That must be done before
+ calling this function.
+
+ To prevent the scenegraph from creating its own device and context objects,
+ specify an appropriate QQuickGraphicsDevice, wrapping existing graphics
+ objects, by calling QQuickWindow::setGraphicsDevice().
+
+ \note This function does not need to be, and must not be, called when using
+ the \c software adaptation of Qt Quick.
+
+ \since 6.0
+
+ \sa QQuickRenderTarget, QQuickGraphicsDevice
+ */
+bool QQuickRenderControl::initialize()
+{
+ Q_D(QQuickRenderControl);
+
+ if (!d->window) {
+ qWarning("QQuickRenderControl::initialize called with no associated window");
+ return false;
+ }
+
+ if (!d->initRhi())
+ return false;
+
+ QQuickWindowPrivate *wd = QQuickWindowPrivate::get(d->window);
+ wd->rhi = d->rhi;
+
+ QSGDefaultRenderContext *renderContext = qobject_cast<QSGDefaultRenderContext *>(d->rc);
+ if (renderContext) {
+ QSGDefaultRenderContext::InitParams params;
+ params.rhi = d->rhi;
+ params.sampleCount = d->sampleCount;
+ params.initialSurfacePixelSize = d->window->size() * d->window->effectiveDevicePixelRatio();
+ params.maybeSurface = d->window;
+ renderContext->initialize(&params);
+ } else {
+ qWarning("QRhi is only compatible with default adaptation");
+ return false;
+ }
+
+ return true;
+}
+
+/*!
Initializes the scene graph resources. The context \a gl has to be the
current OpenGL context or null if it is not relevant because a Qt Quick
backend other than OpenGL is in use.
@@ -295,24 +396,33 @@ bool QQuickRenderControl::sync()
return false;
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window);
+ // we may not have a d->rhi (software backend) hence the check is important
+ if (d->rhi) {
+ if (!d->rhi->isRecordingFrame()) {
+ qWarning("QQuickRenderControl can only sync when beginFrame() has been called");
+ return false;
+ }
+ if (!d->cb) {
+ qWarning("QQuickRenderControl cannot be used with QRhi when no QRhiCommandBuffer is provided");
+ return false;
+ }
+ cd->setCustomCommandBuffer(d->cb);
+ }
+
cd->syncSceneGraph();
d->rc->endSync();
- // TODO: find out if the sync actually caused a scenegraph update.
return true;
}
/*!
- Stop rendering and release resources. Requires a current context.
+ Stop rendering and release resources.
This is the equivalent of the cleanup operations that happen with a
real QQuickWindow when the window becomes hidden.
This function is called from the destructor. Therefore there will
- typically be no need to call it directly. Pay attention however to
- the fact that this requires the context, that was passed to
- initialize(), to be the current one at the time of destroying the
- QQuickRenderControl instance.
+ typically be no need to call it directly.
Once invalidate() has been called, it is possible to reuse the
QQuickRenderControl instance by calling initialize() again.
@@ -353,6 +463,19 @@ void QQuickRenderControl::render()
return;
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window);
+ // we may not have a d->rhi (software backend) hence the check is important
+ if (d->rhi) {
+ if (!d->rhi->isRecordingFrame()) {
+ qWarning("QQuickRenderControl can only render when beginFrame() has been called");
+ return;
+ }
+ if (!d->cb) {
+ qWarning("QQuickRenderControl cannot be used with QRhi when no QRhiCommandBuffer is provided");
+ return;
+ }
+ cd->setCustomCommandBuffer(d->cb);
+ }
+
cd->renderSceneGraph(d->window->size());
}
@@ -378,48 +501,50 @@ void QQuickRenderControl::render()
for example. This will lead to better performance.
*/
-/*!
- Grabs the contents of the scene and returns it as an image.
-
- \note Requires the context to be current.
- */
-QImage QQuickRenderControl::grab()
+QImage QQuickRenderControlPrivate::grab()
{
- Q_D(QQuickRenderControl);
- if (!d->window)
+ if (!window)
return QImage();
QImage grabContent;
- if (d->window->rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL) {
+ if (rhi) {
+
+ // As documented by QQuickWindow::grabWindow(): Nothing to do here, we
+ // do not support "grabbing" with an application-provided render target
+ // in Qt 6. (with the exception of the software backend because that
+ // does not support custom render targets, so the grab implementation
+ // here is still valuable)
+
+ } else if (window->rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL) {
#if QT_CONFIG(opengl)
- QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window);
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
cd->polishItems();
cd->syncSceneGraph();
- d->rc->endSync();
- render();
- const bool alpha = d->window->format().alphaBufferSize() > 0 && d->window->color().alpha() < 255;
- grabContent = qt_gl_read_framebuffer(d->window->size() * d->window->effectiveDevicePixelRatio(), alpha, alpha);
- if (QQuickRenderControl::renderWindowFor(d->window)) {
- grabContent.setDevicePixelRatio(d->window->effectiveDevicePixelRatio());
+ rc->endSync();
+ q->render();
+ const bool alpha = window->format().alphaBufferSize() > 0 && window->color().alpha() < 255;
+ grabContent = qt_gl_read_framebuffer(window->size() * window->effectiveDevicePixelRatio(), alpha, alpha);
+ if (QQuickRenderControl::renderWindowFor(window)) {
+ grabContent.setDevicePixelRatio(window->effectiveDevicePixelRatio());
}
#endif
#if QT_CONFIG(thread)
- } else if (d->window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) {
- QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window);
+ } else if (window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) {
+ QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
cd->polishItems();
cd->syncSceneGraph();
QSGSoftwareRenderer *softwareRenderer = static_cast<QSGSoftwareRenderer *>(cd->renderer);
if (softwareRenderer) {
- const qreal dpr = d->window->effectiveDevicePixelRatio();
- const QSize imageSize = d->window->size() * dpr;
+ const qreal dpr = window->effectiveDevicePixelRatio();
+ const QSize imageSize = window->size() * dpr;
grabContent = QImage(imageSize, QImage::Format_ARGB32_Premultiplied);
grabContent.setDevicePixelRatio(dpr);
QPaintDevice *prevDev = softwareRenderer->currentPaintDevice();
softwareRenderer->setCurrentPaintDevice(&grabContent);
softwareRenderer->markDirty();
- d->rc->endSync();
- render();
+ rc->endSync();
+ q->render();
softwareRenderer->setCurrentPaintDevice(prevDev);
}
#endif
@@ -474,6 +599,131 @@ QWindow *QQuickRenderControl::renderWindowFor(QQuickWindow *win, QPoint *offset)
return nullptr;
}
+/*!
+ \return the QQuickWindow this QQuickRenderControl is associated with.
+
+ \note A QQuickRenderControl gets associated with a QQuickWindow when
+ constructing the QQuickWindow. The return value from this function is null
+ before that point.
+
+ \since 6.0
+ */
+QQuickWindow *QQuickRenderControl::window() const
+{
+ Q_D(const QQuickRenderControl);
+ return d->window;
+}
+
+/*!
+ Specifies the start of a graphics frame. Calls to sync() or render() must
+ be enclosed by calls to beginFrame() and endFrame().
+
+ Unlike the earlier OpenGL-only world of Qt 5, rendering with other graphics
+ APIs requires more well-defined points of starting and ending a frame. When
+ manually driving the rendering loop via QQuickRenderControl, it now falls
+ to the user of QQuickRenderControl to specify these points.
+
+ A typical update step, including initialization of rendering into an
+ existing texture, could like like the following. The example snippet
+ assumes Direct3D 11 but the same concepts apply other graphics APIs as
+ well.
+
+ \badcode
+ if (!m_quickInitialized) {
+ m_quickWindow->setGraphicsDevice(QQuickGraphicsDevice::fromDeviceAndContext(m_engine->device(), m_engine->context()));
+
+ if (!m_renderControl->initialize())
+ qWarning("Failed to initialize redirected Qt Quick rendering");
+
+ m_quickWindow->setRenderTarget(QQuickRenderTarget::fromNativeTexture({ &m_res.texture, 0 },
+ QSize(QML_WIDTH, QML_HEIGHT),
+ SAMPLE_COUNT));
+
+ m_quickInitialized = true;
+ }
+
+ m_renderControl->polishItems();
+
+ m_renderControl->beginFrame();
+ m_renderControl->sync();
+ m_renderControl->render();
+ m_renderControl->endFrame(); // Qt Quick's rendering commands are submitted to the device context here
+ \endcode
+
+ \since 6.0
+
+ \sa endFrame(), initialize(), sync(), render(), QQuickGraphicsDevice, QQuickRenderTarget
+ */
+void QQuickRenderControl::beginFrame()
+{
+ Q_D(QQuickRenderControl);
+ if (!d->rhi || d->rhi->isRecordingFrame())
+ return;
+
+ d->rhi->beginOffscreenFrame(&d->cb, QRhi::ExternalContentsInPass); // must specify ExternalContents since we do not know in advance
+}
+
+/*!
+ Specifies the end of a graphics frame. Calls to sync() or render() must be
+ enclosed by calls to beginFrame() and endFrame().
+
+ When this function is called, any graphics commands enqueued by the
+ scenegraph are submitted to the context or command queue, whichever is
+ applicable.
+
+ \since 6.0
+
+ \sa beginFrame(), initialize(), sync(), render(), QQuickGraphicsDevice, QQuickRenderTarget
+ */
+void QQuickRenderControl::endFrame()
+{
+ Q_D(QQuickRenderControl);
+ if (!d->rhi || !d->rhi->isRecordingFrame())
+ return;
+
+ d->rhi->endOffscreenFrame();
+ d->cb = nullptr;
+}
+
+bool QQuickRenderControlPrivate::initRhi()
+{
+ // initialize() - invalidate() - initialize() uses the QRhi the first
+ // initialize() created, so if already exists, we are done
+ if (rhi)
+ return true;
+
+ QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
+
+ // sanity check for Vulkan
+#if QT_CONFIG(vulkan)
+ if (rhiSupport->rhiBackend() == QRhi::Vulkan && !window->vulkanInstance()) {
+ qWarning("QQuickRenderControl: No QVulkanInstance set for QQuickWindow, cannot initialize");
+ return false;
+ }
+#endif
+
+ // for OpenGL
+ if (!offscreenSurface)
+ offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window);
+
+ rhi = rhiSupport->createRhi(window, offscreenSurface);
+ if (!rhi) {
+ qWarning("QQuickRenderControl: Failed to initialize QRhi");
+ return false;
+ }
+
+ return true;
+}
+
+void QQuickRenderControlPrivate::resetRhi()
+{
+ delete rhi;
+ rhi = nullptr;
+
+ delete offscreenSurface;
+ offscreenSurface = nullptr;
+}
+
QT_END_NAMESPACE
#include "moc_qquickrendercontrol.cpp"
diff --git a/src/quick/items/qquickrendercontrol.h b/src/quick/items/qquickrendercontrol.h
index 8ec9b8aafc..cdcad589f6 100644
--- a/src/quick/items/qquickrendercontrol.h
+++ b/src/quick/items/qquickrendercontrol.h
@@ -41,7 +41,7 @@
#define QQUICKRENDERCONTROL_H
#include <QtQuick/qtquickglobal.h>
-#include <QtGui/QImage>
+#include <QtGui/qimage.h>
QT_BEGIN_NAMESPACE
@@ -59,18 +59,27 @@ public:
~QQuickRenderControl() override;
void prepareThread(QThread *targetThread);
- void initialize(QOpenGLContext *gl);
+
+ void setSamples(int sampleCount);
+ int samples() const;
+
+ bool initialize();
+ void initialize(QOpenGLContext *gl); // ### Qt 6 remove
+
void invalidate();
+ void beginFrame();
+ void endFrame();
+
void polishItems();
- void render();
bool sync();
-
- QImage grab();
+ void render();
static QWindow *renderWindowFor(QQuickWindow *win, QPoint *offset = nullptr);
virtual QWindow *renderWindow(QPoint *offset) { Q_UNUSED(offset); return nullptr; }
+ QQuickWindow *window() const;
+
Q_SIGNALS:
void renderRequested();
void sceneChanged();
diff --git a/src/quick/items/qquickrendercontrol_p.h b/src/quick/items/qquickrendercontrol_p.h
index 487af45db5..c24ea9b8c6 100644
--- a/src/quick/items/qquickrendercontrol_p.h
+++ b/src/quick/items/qquickrendercontrol_p.h
@@ -56,12 +56,16 @@
QT_BEGIN_NAMESPACE
-class QQuickRenderControlPrivate : public QObjectPrivate
+class QRhi;
+class QRhiCommandBuffer;
+class QOffscreenSurface;
+
+class Q_AUTOTEST_EXPORT QQuickRenderControlPrivate : public QObjectPrivate
{
public:
Q_DECLARE_PUBLIC(QQuickRenderControl)
- QQuickRenderControlPrivate();
+ QQuickRenderControlPrivate(QQuickRenderControl *renderControl);
static QQuickRenderControlPrivate *get(QQuickRenderControl *renderControl) {
return renderControl->d_func();
@@ -74,10 +78,20 @@ public:
void update();
void maybeUpdate();
+ bool initRhi();
+ void resetRhi();
+
+ QImage grab();
+
+ QQuickRenderControl *q;
bool initialized;
QQuickWindow *window;
static QSGContext *sg;
QSGRenderContext *rc;
+ QRhi *rhi;
+ QRhiCommandBuffer *cb;
+ QOffscreenSurface *offscreenSurface;
+ int sampleCount;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickrendertarget.cpp b/src/quick/items/qquickrendertarget.cpp
new file mode 100644
index 0000000000..924824e456
--- /dev/null
+++ b/src/quick/items/qquickrendertarget.cpp
@@ -0,0 +1,293 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickrendertarget_p.h"
+#include <QtGui/private/qrhi_p.h>
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickwindow_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QQuickRenderTarget
+ \since 6.0
+ \inmodule QtQuick
+
+ \brief The QQuickRenderTarget class provides an opaque container for native
+ graphics resources specifying a render target, and associated metadata.
+
+ \sa QQuickWindow::setRenderTarget(), QQuickGraphicsDevice
+*/
+
+QQuickRenderTargetPrivate::QQuickRenderTargetPrivate()
+ : ref(1)
+{
+}
+
+QQuickRenderTargetPrivate::QQuickRenderTargetPrivate(const QQuickRenderTargetPrivate *other)
+ : ref(1),
+ type(other->type),
+ pixelSize(other->pixelSize),
+ sampleCount(other->sampleCount),
+ u(other->u)
+{
+}
+
+/*!
+ Constructs a default QQuickRenderTarget that does not reference any native
+ objects.
+ */
+QQuickRenderTarget::QQuickRenderTarget()
+ : d(new QQuickRenderTargetPrivate)
+{
+}
+
+/*!
+ \internal
+ */
+void QQuickRenderTarget::detach()
+{
+ qAtomicDetach(d);
+}
+
+/*!
+ \internal
+ */
+QQuickRenderTarget::QQuickRenderTarget(const QQuickRenderTarget &other)
+ : d(other.d)
+{
+ d->ref.ref();
+}
+
+/*!
+ \internal
+ */
+QQuickRenderTarget &QQuickRenderTarget::operator=(const QQuickRenderTarget &other)
+{
+ qAtomicAssign(d, other.d);
+ return *this;
+}
+
+/*!
+ Destructor.
+ */
+QQuickRenderTarget::~QQuickRenderTarget()
+{
+ if (!d->ref.deref())
+ delete d;
+}
+
+/*!
+ \return true if this QQuickRenderTarget is default constructed, referencing
+ no native objects.
+ */
+bool QQuickRenderTarget::isNull() const
+{
+ return d->type == QQuickRenderTargetPrivate::Type::Null;
+}
+
+/*!
+ \return a new QQuickRenderTarget referencing a native texture or image
+ object.
+
+ \a nativeTexture references a native resource, for example, a \c VkImage,
+ \c ID3D11Texture2D*, c MTLTexture*, or \c GLuint. Where applicable, this
+ must be complemented by the current layout of the image.
+
+ \note the \c object field in \a nativeTexture must always be a pointer to
+ the native object, even if the type is already a pointer.
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures are supported.
+
+ \a sampleCount specific the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture.
+
+ \note the resulting QQuickRenderTarget does not own any native resources,
+ it merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl
+ */
+QQuickRenderTarget QQuickRenderTarget::fromNativeTexture(const QSGTexture::NativeTexture &nativeTexture,
+ const QSize &pixelSize,
+ int sampleCount)
+{
+ QQuickRenderTarget rt;
+ QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
+
+ if (!nativeTexture.object) {
+ qWarning("QQuickRenderTarget: nativeTexture.object is null");
+ return rt;
+ }
+
+ if (pixelSize.isEmpty()) {
+ qWarning("QQuickRenderTarget: Cannot create with empty size");
+ return rt;
+ }
+
+ d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
+ d->pixelSize = pixelSize;
+ d->sampleCount = qMax(1, sampleCount);
+ d->u.nativeTexture = nativeTexture;
+
+ return rt;
+}
+
+/*!
+ \internal
+ */
+QQuickRenderTarget QQuickRenderTarget::fromRhiRenderTarget(QRhiRenderTarget *renderTarget)
+{
+ QQuickRenderTarget rt;
+ QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
+
+ if (!renderTarget) {
+ qWarning("QQuickRenderTarget: Needs a valid QRhiRenderTarget");
+ return rt;
+ }
+
+ d->type = QQuickRenderTargetPrivate::Type::RhiRenderTarget;
+ d->pixelSize = renderTarget->pixelSize();
+ d->sampleCount = renderTarget->sampleCount();
+ d->u.rhiRt = renderTarget;
+
+ return rt;
+}
+
+/*!
+ \return true if \a a and \a b refer to the same set of native objects and
+ have matching associated data (size, sample count).
+ */
+bool operator==(const QQuickRenderTarget &a, const QQuickRenderTarget &b) Q_DECL_NOTHROW
+{
+ const QQuickRenderTargetPrivate *da = QQuickRenderTargetPrivate::get(&a);
+ const QQuickRenderTargetPrivate *db = QQuickRenderTargetPrivate::get(&b);
+ if (da->type != db->type
+ || da->pixelSize != db->pixelSize
+ || da->sampleCount != db->sampleCount)
+ {
+ return false;
+ }
+
+ switch (da->type) {
+ case QQuickRenderTargetPrivate::Type::Null:
+ break;
+ case QQuickRenderTargetPrivate::Type::NativeTexture:
+ if (da->u.nativeTexture.object != db->u.nativeTexture.object
+ || da->u.nativeTexture.layout != db->u.nativeTexture.layout)
+ return false;
+ break;
+ case QQuickRenderTargetPrivate::Type::RhiRenderTarget:
+ if (da->u.rhiRt != db->u.rhiRt)
+ return false;
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+/*!
+ \return true if \a a and \a b refer to a different set of native objects,
+ or the associated data (size, sample count) does not match.
+ */
+bool operator!=(const QQuickRenderTarget &a, const QQuickRenderTarget &b) Q_DECL_NOTHROW
+{
+ return !(a == b);
+}
+
+bool QQuickRenderTargetPrivate::resolve(QRhi *rhi, QQuickWindowRenderTarget *dst)
+{
+ switch (type) {
+ case Type::Null:
+ dst->renderTarget = nullptr;
+ dst->owns = false;
+ return true;
+
+ case Type::NativeTexture:
+ {
+ QRhiTexture *texture = rhi->newTexture(QRhiTexture::RGBA8, pixelSize, sampleCount, QRhiTexture::RenderTarget);
+ if (!texture->buildFrom({ u.nativeTexture.object, u.nativeTexture.layout })) {
+ qWarning("Failed to build texture for QQuickRenderTarget");
+ return false;
+ }
+ QRhiRenderBuffer *depthStencil = rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, sampleCount);
+ if (!depthStencil->build()) {
+ qWarning("Failed to build depth-stencil buffer for QQuickRenderTarget");
+ delete texture;
+ return false;
+ }
+
+ QRhiColorAttachment att(texture);
+ QRhiTextureRenderTargetDescription rtDesc(att);
+ rtDesc.setDepthStencilBuffer(depthStencil);
+ QRhiTextureRenderTarget *rt = rhi->newTextureRenderTarget(rtDesc);
+ QRhiRenderPassDescriptor *rp = rt->newCompatibleRenderPassDescriptor();
+ rt->setRenderPassDescriptor(rp);
+ if (!rt->build()) {
+ qWarning("Failed to build texture render target for QQuickRenderTarget");
+ delete rp;
+ delete depthStencil;
+ delete texture;
+ return false;
+ }
+ dst->renderTarget = rt;
+ dst->rpDesc = rp;
+ dst->texture = texture;
+ dst->depthStencil = depthStencil;
+ dst->owns = true; // ownership of the native resource itself is not transferred but the QRhi objects are on us now
+ }
+ return true;
+
+ case Type::RhiRenderTarget:
+ dst->renderTarget = u.rhiRt;
+ dst->owns = false;
+ return true;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquickrendertarget.h b/src/quick/items/qquickrendertarget.h
new file mode 100644
index 0000000000..5139046ad4
--- /dev/null
+++ b/src/quick/items/qquickrendertarget.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKRENDERTARGET_H
+#define QQUICKRENDERTARGET_H
+
+#include <QtQuick/qtquickglobal.h>
+#include <QtQuick/qsgtexture.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickRenderTargetPrivate;
+class QRhiRenderTarget;
+
+class Q_QUICK_EXPORT QQuickRenderTarget
+{
+public:
+ QQuickRenderTarget();
+ ~QQuickRenderTarget();
+ QQuickRenderTarget(const QQuickRenderTarget &other);
+ QQuickRenderTarget &operator=(const QQuickRenderTarget &other);
+
+ bool isNull() const;
+
+ static QQuickRenderTarget fromNativeTexture(const QSGTexture::NativeTexture &nativeTexture,
+ const QSize &pixelSize,
+ int sampleCount = 1);
+
+ static QQuickRenderTarget fromRhiRenderTarget(QRhiRenderTarget *renderTarget);
+
+private:
+ void detach();
+ QQuickRenderTargetPrivate *d;
+ friend class QQuickRenderTargetPrivate;
+};
+
+Q_QUICK_EXPORT bool operator==(const QQuickRenderTarget &a, const QQuickRenderTarget &b) Q_DECL_NOTHROW;
+Q_QUICK_EXPORT bool operator!=(const QQuickRenderTarget &a, const QQuickRenderTarget &b) Q_DECL_NOTHROW;
+
+QT_END_NAMESPACE
+
+#endif // QQUICKRENDERTARGET_H
diff --git a/src/quick/items/qquickrendertarget_p.h b/src/quick/items/qquickrendertarget_p.h
new file mode 100644
index 0000000000..628e5c277b
--- /dev/null
+++ b/src/quick/items/qquickrendertarget_p.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKRENDERTARGET_P_H
+#define QQUICKRENDERTARGET_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/private/qtquickglobal_p.h>
+#include "qquickrendertarget.h"
+#include <QAtomicInt>
+
+QT_BEGIN_NAMESPACE
+
+class QRhi;
+class QQuickWindowRenderTarget;
+
+class Q_QUICK_PRIVATE_EXPORT QQuickRenderTargetPrivate
+{
+public:
+ static QQuickRenderTargetPrivate *get(QQuickRenderTarget *rt) { return rt->d; }
+ static const QQuickRenderTargetPrivate *get(const QQuickRenderTarget *rt) { return rt->d; }
+ QQuickRenderTargetPrivate();
+ QQuickRenderTargetPrivate(const QQuickRenderTargetPrivate *other);
+ bool resolve(QRhi *rhi, QQuickWindowRenderTarget *dst);
+
+ enum class Type {
+ Null,
+ NativeTexture,
+ RhiRenderTarget
+ };
+
+ QAtomicInt ref;
+ Type type = Type::Null;
+ QSize pixelSize;
+ int sampleCount = 1;
+ union {
+ QSGTexture::NativeTexture nativeTexture;
+ QRhiRenderTarget *rhiRt;
+ } u;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKRENDERTARGET_P_H
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 4874af44ed..bb3aff0cd6 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -43,6 +43,7 @@
#include "qquickitem.h"
#include "qquickitem_p.h"
#include "qquickevents_p_p.h"
+#include "qquickgraphicsdevice_p.h"
#if QT_CONFIG(quick_draganddrop)
#include <private/qquickdrag_p.h>
@@ -424,16 +425,66 @@ void forceUpdate(QQuickItem *item)
forceUpdate(items.at(i));
}
+void QQuickWindowRenderTarget::reset(QRhi *rhi)
+{
+ if (rhi && owns) {
+ delete renderTarget;
+ delete rpDesc;
+ delete texture;
+ delete depthStencil;
+ }
+
+ renderTarget = nullptr;
+ rpDesc = nullptr;
+ texture = nullptr;
+ depthStencil = nullptr;
+ owns = false;
+}
+
+void QQuickWindowPrivate::ensureCustomRenderTarget()
+{
+ // resolve() can be expensive when importing an existing native texture, so
+ // it is important to only do it when the QQuickRenderTarget* was really changed
+ if (!redirect.renderTargetDirty || !rhi)
+ return;
+
+ redirect.renderTargetDirty = false;
+
+ redirect.rt.reset(rhi);
+
+ // a default constructed QQuickRenderTarget means no redirection
+ if (customRenderTarget.isNull())
+ return;
+
+ QQuickRenderTargetPrivate::get(&customRenderTarget)->resolve(rhi, &redirect.rt);
+}
+
+void QQuickWindowPrivate::setCustomCommandBuffer(QRhiCommandBuffer *cb)
+{
+ // ownership not transferred
+ redirect.commandBuffer = cb;
+}
+
void QQuickWindowPrivate::syncSceneGraph()
{
Q_Q(QQuickWindow);
+ ensureCustomRenderTarget();
+
// Calculate the dpr the same way renderSceneGraph() will.
qreal devicePixelRatio = q->effectiveDevicePixelRatio();
- if (renderTargetId && !QQuickRenderControl::renderWindowFor(q))
+ const bool hasCustomRenderTarget = customRenderTargetGl.renderTargetId || redirect.rt.renderTarget;
+ if (hasCustomRenderTarget && !QQuickRenderControl::renderWindowFor(q))
devicePixelRatio = 1;
- context->prepareSync(devicePixelRatio, rhi ? swapchain->currentFrameCommandBuffer() : nullptr);
+ QRhiCommandBuffer *cb = nullptr;
+ if (rhi) {
+ if (redirect.commandBuffer)
+ cb = redirect.commandBuffer;
+ else
+ cb = swapchain->currentFrameCommandBuffer();
+ }
+ context->prepareSync(devicePixelRatio, cb);
animationController->beforeNodeSync();
@@ -484,11 +535,32 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfa
return;
if (rhi) {
- // ### no offscreen ("renderTargetId") support yet
- context->beginNextRhiFrame(renderer,
- swapchain->currentFrameRenderTarget(),
- rpDescForSwapchain,
- swapchain->currentFrameCommandBuffer(),
+ ensureCustomRenderTarget();
+ QRhiRenderTarget *rt;
+ QRhiRenderPassDescriptor *rp;
+ QRhiCommandBuffer *cb;
+ if (redirect.rt.renderTarget) {
+ rt = redirect.rt.renderTarget;
+ rp = rt->renderPassDescriptor();
+ if (!rp) {
+ qWarning("Custom render target is set but no renderpass descriptor has been provided.");
+ return;
+ }
+ cb = redirect.commandBuffer;
+ if (!cb) {
+ qWarning("Custom render target is set but no command buffer has been provided.");
+ return;
+ }
+ } else {
+ if (!swapchain) {
+ qWarning("QQuickWindow: No render target (neither swapchain nor custom target was provided)");
+ return;
+ }
+ rt = swapchain->currentFrameRenderTarget();
+ rp = rpDescForSwapchain;
+ cb = swapchain->currentFrameCommandBuffer();
+ }
+ context->beginNextRhiFrame(renderer, rt, rp, cb,
emitBeforeRenderPassRecording,
emitAfterRenderPassRecording,
q);
@@ -503,18 +575,28 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfa
emit q->beforeRendering();
runAndClearJobs(&beforeRenderingJobs);
if (!customRenderStage || !customRenderStage->render()) {
+ QSGAbstractRenderer::MatrixTransformFlags matrixFlags;
+ const bool flipY = rhi ? !rhi->isYUpInNDC() : false;
+ if (flipY)
+ matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
int fboId = 0;
const qreal devicePixelRatio = q->effectiveDevicePixelRatio();
- if (renderTargetId) {
- QRect rect(QPoint(0, 0), renderTargetSize);
- fboId = renderTargetId;
+ const bool hasCustomRenderTarget = customRenderTargetGl.renderTargetId || redirect.rt.renderTarget;
+ if (hasCustomRenderTarget) {
+ QRect rect;
+ if (redirect.rt.renderTarget) {
+ rect = QRect(QPoint(0, 0), redirect.rt.renderTarget->pixelSize());
+ } else {
+ fboId = customRenderTargetGl.renderTargetId;
+ rect = QRect(QPoint(0, 0), customRenderTargetGl.renderTargetSize);
+ }
renderer->setDeviceRect(rect);
renderer->setViewportRect(rect);
if (QQuickRenderControl::renderWindowFor(q)) {
- renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), size));
+ renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), size), matrixFlags);
renderer->setDevicePixelRatio(devicePixelRatio);
} else {
- renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), rect.size()));
+ renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), rect.size()), matrixFlags);
renderer->setDevicePixelRatio(1);
}
} else {
@@ -530,10 +612,6 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size, const QSize &surfa
QRect rect(QPoint(0, 0), pixelSize);
renderer->setDeviceRect(rect);
renderer->setViewportRect(rect);
- const bool flipY = rhi ? !rhi->isYUpInNDC() : false;
- QSGAbstractRenderer::MatrixTransformFlags matrixFlags;
- if (flipY)
- matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
renderer->setProjectionMatrixToRect(QRectF(QPoint(0, 0), logicalSize), matrixFlags);
renderer->setDevicePixelRatio(devicePixelRatio);
}
@@ -589,8 +667,6 @@ QQuickWindowPrivate::QQuickWindowPrivate()
, allowChildEventFiltering(true)
, allowDoubleClick(true)
, lastFocusReason(Qt::OtherFocusReason)
- , renderTarget(nullptr)
- , renderTargetId(0)
, vaoHelper(nullptr)
, incubationController(nullptr)
, hasActiveSwapchain(false)
@@ -604,6 +680,7 @@ QQuickWindowPrivate::QQuickWindowPrivate()
QQuickWindowPrivate::~QQuickWindowPrivate()
{
+ redirect.rt.reset(rhi);
delete customRenderStage;
if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>())
service->removeWindow(q_func());
@@ -652,7 +729,7 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
q->setSurfaceType(windowManager ? windowManager->windowSurfaceType() : QSurface::OpenGLSurface);
q->setFormat(sg->defaultSurfaceFormat());
#if QT_CONFIG(vulkan)
- if (q->surfaceType() == QSurface::VulkanSurface)
+ if (!renderControl && q->surfaceType() == QSurface::VulkanSurface)
q->setVulkanInstance(QSGRhiSupport::vulkanInstance());
#endif
@@ -4028,13 +4105,13 @@ void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo)
return;
}
- d->renderTarget = fbo;
+ d->customRenderTargetGl.renderTarget = fbo;
if (fbo) {
- d->renderTargetId = fbo->handle();
- d->renderTargetSize = fbo->size();
+ d->customRenderTargetGl.renderTargetId = fbo->handle();
+ d->customRenderTargetGl.renderTargetSize = fbo->size();
} else {
- d->renderTargetId = 0;
- d->renderTargetSize = QSize();
+ d->customRenderTargetGl.renderTargetId = 0;
+ d->customRenderTargetGl.renderTargetSize = QSize();
}
}
#endif
@@ -4069,11 +4146,11 @@ void QQuickWindow::setRenderTarget(uint fboId, const QSize &size)
return;
}
- d->renderTargetId = fboId;
- d->renderTargetSize = size;
+ d->customRenderTargetGl.renderTargetId = fboId;
+ d->customRenderTargetGl.renderTargetSize = size;
// Unset any previously set instance...
- d->renderTarget = nullptr;
+ d->customRenderTargetGl.renderTarget = nullptr;
}
@@ -4083,7 +4160,7 @@ void QQuickWindow::setRenderTarget(uint fboId, const QSize &size)
uint QQuickWindow::renderTargetId() const
{
Q_D(const QQuickWindow);
- return d->renderTargetId;
+ return d->customRenderTargetGl.renderTargetId;
}
/*!
@@ -4092,11 +4169,9 @@ uint QQuickWindow::renderTargetId() const
QSize QQuickWindow::renderTargetSize() const
{
Q_D(const QQuickWindow);
- return d->renderTargetSize;
+ return d->customRenderTargetGl.renderTargetSize;
}
-
-
#if QT_CONFIG(opengl)
/*!
Returns the render target for this window.
@@ -4113,11 +4188,84 @@ QSize QQuickWindow::renderTargetSize() const
QOpenGLFramebufferObject *QQuickWindow::renderTarget() const
{
Q_D(const QQuickWindow);
- return d->renderTarget;
+ return d->customRenderTargetGl.renderTarget;
}
#endif
/*!
+ Sets the render target for this window to be \a target.
+
+ A QQuickRenderTarget serves as an opaque handle for a renderable native
+ object, most commonly a 2D texture, and associated metadata, such as the
+ size in pixels.
+
+ A default constructed QQuickRenderTarget means no redirection. A valid
+ \a target, created via one of the static QQuickRenderTarget factory functions,
+ on the other hand, enables redirection of the rendering of the Qt Quick
+ scene: it will no longer target the color buffers for the surface
+ associated with the window, but rather the textures or other graphics
+ objects specified in \a target.
+
+ For example, assuming the scenegraph is using Vulkan to render, one can
+ redirect its output into a \c VkImage. For graphics APIs like Vulkan, the
+ image layout must be provided as well. QQuickRenderTarget instances are
+ implicitly shared and are copyable and can be passed by value. They do not
+ own the associated native objects (such as, the VkImage in the example),
+ however.
+
+ \badcode
+ QQuickRenderTarget rt = QQuickRenderTarget::fromNativeTexture({ &vulkanImage, VK_IMAGE_LAYOUT_PREINITIALIZED }, pixelSize);
+ quickWindow->setRenderTarget(rt);
+ \endcode
+
+ This function is very often used in combination with QQuickRenderControl
+ and an invisible QQuickWindow, in order to render Qt Quick content into a
+ texture, without creating an on-screen native window for this QQuickWindow.
+
+ When the desired target, or associated data, such as the size, changes,
+ call this function with a new QQuickRenderTarget. Constructing
+ QQuickRenderTarget instances and calling this function is cheap, but be
+ aware that setting a new \a target with a different native object or other
+ data may lead to potentially expensive initialization steps when the
+ scenegraph is about to render the next frame. Therefore change the target
+ only when necessary.
+
+ \note This function should not be used when using the \c software backend.
+ Instead, use grabWindow() to render the content into a QImage.
+
+ \note The window does not take ownership of any native objects referenced
+ in \a target.
+
+ \note It is the caller's responsibility to ensure the native objects
+ referred to in \a target are valid for the scenegraph renderer too. For
+ instance, with Vulkan, Metal, and Direct3D this implies that the texture or
+ image is created on the same graphics device that is used by the scenegraph
+ internally. This is often achieved by using this function in combination
+ with setGraphicsDevice().
+
+ \note With graphics APIs where relevant, the application must pay attention
+ to image layout transitions performed by the scenegraph. For example, once
+ a VkImage is associated with the scenegraph by calling this function, its
+ layout will transition to \c VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL when
+ rendering a frame.
+
+ \warning This function can only be called from the thread doing the
+ rendering.
+
+ \since 6.0
+
+ \sa QQuickRenderControl, setGraphicsDevice(), setSceneGraphBackend()
+ */
+void QQuickWindow::setRenderTarget(const QQuickRenderTarget &target)
+{
+ Q_D(QQuickWindow);
+ if (target != d->customRenderTarget) {
+ d->customRenderTarget = target;
+ d->redirect.renderTargetDirty = true;
+ }
+}
+
+/*!
Grabs the contents of the window and returns it as an image.
It is possible to call the grabWindow() function when the window is not
@@ -4125,6 +4273,14 @@ QOpenGLFramebufferObject *QQuickWindow::renderTarget() const
and has a valid size and that no other QQuickWindow instances are rendering
in the same process.
+ \note When using this window in combination with QQuickRenderControl, the
+ result of this function is an empty image, unless the \c software backend
+ is in use. This is because when redirecting the output to an
+ application-managed graphics resource (such as, a texture) by using
+ QQuickRenderControl and setRenderTarget(), the application is better suited
+ for managing and executing an eventual read back operation, since it is in
+ full control of the resource to begin with.
+
\warning Calling this function will cause performance problems.
\warning This function can only be called from the GUI thread.
@@ -4141,12 +4297,8 @@ QImage QQuickWindow::grabWindow()
if (!isVisible() && !d->renderControl) {
if (d->rhi) {
- // ### we may need a full offscreen round when non-exposed...
-
- if (d->renderControl)
- return d->renderControl->grab();
- else if (d->windowManager)
- return d->windowManager->grab(this);
+ if (d->windowManager)
+ return d->windowManager->grab(this); // ### we may need a full offscreen round when non-exposed?
return QImage();
}
@@ -4184,7 +4336,7 @@ QImage QQuickWindow::grabWindow()
}
if (d->renderControl)
- return d->renderControl->grab();
+ return QQuickRenderControlPrivate::get(d->renderControl)->grab();
else if (d->windowManager)
return d->windowManager->grab(this);
return QImage();
@@ -5648,7 +5800,11 @@ QSGRendererInterface *QQuickWindow::rendererInterface() const
loaded plugins.
\note The call to the function must happen before constructing the first
- QQuickWindow in the application. It cannot be changed afterwards.
+ QQuickWindow in the application. The graphics API cannot be changed
+ afterwards. When used in combination with QQuickRenderControl, this rule is
+ relaxed: it is possible to change the graphics API, but only when all
+ existing QQuickRenderControl and QQuickWindow instances have been
+ destroyed.
If the selected backend is invalid or an error occurs, the default backend
(OpenGL or software, depending on the Qt configuration) is used.
@@ -5709,6 +5865,65 @@ QString QQuickWindow::sceneGraphBackend()
}
/*!
+ Sets the graphics device objects for this window. The scenegraph will use
+ existing device, physical device, and other objects specified by \a device
+ instead of creating new ones.
+
+ This function is very often used in combination with QQuickRenderControl
+ and setRenderTarget(), in order to redirect Qt Quick rendering into a
+ texture.
+
+ A default constructed QQuickGraphicsDevice does not change the default
+ behavior in any way. Once a \a device created via one of the
+ QQuickGraphicsDevice factory functions, such as,
+ QQuickGraphicsDevice::fromDeviceObjects(), is passed in, and the scenegraph
+ uses a matching graphics API (with the example of fromDeviceObjects(), that
+ would be Vulkan), the scenegraph will use the existing device objects (such
+ as, the \c VkPhysicalDevice, \c VkDevice, and graphics queue family index,
+ in case of Vulkan) encapsulated by the QQuickGraphicsDevice. This allows
+ using the same device, and so sharing resources, such as buffers and
+ textures, between Qt Quick and native rendering engines.
+
+ \warning This function can only be called before initializing the
+ scenegraph and will have no effect if called afterwards. In practice this
+ typically means calling it right before QQuickRenderControl::initialize().
+
+ As an example, this time with Direct3D, the typical usage is expected to be
+ the following:
+
+ \badcode
+ // native graphics resources set up by a custom D3D rendering engine
+ ID3D11Device *device;
+ ID3D11DeviceContext *context;
+ ID3D11Texture2D *texture;
+ ...
+ // now to redirect Qt Quick content into 'texture' we could do the following:
+ QQuickRenderControl *renderControl = new QQuickRenderControl;
+ QQuickWindow *window = new QQuickWindow(renderControl); // this window will never be shown on-screen
+ ...
+ window->setGraphicsDevice(QQuickGraphicsDevice::fromDeviceAndContext(device, context));
+ renderControl->initialize();
+ window->setRenderTarget(QQuickRenderTarget::fromNativeTexture({ &texture, 0 }, textureSize);
+ ...
+ \endcode
+
+ The key aspect of using this function is to ensure that resources or
+ handles to resources, such as \c texture in the above example, are visible
+ to and usable by both the external rendering engine and the scenegraph
+ renderer. This requires using the same graphics device (or with OpenGL,
+ OpenGL context).
+
+ \since 6.0
+
+ \sa QQuickRenderControl, setRenderTarget(), setSceneGraphBackend()
+ */
+void QQuickWindow::setGraphicsDevice(const QQuickGraphicsDevice &device)
+{
+ Q_D(QQuickWindow);
+ d->customDeviceObjects = device;
+}
+
+/*!
Creates a simple rectangle node. When the scenegraph is not initialized, the return value is null.
This is cross-backend alternative to constructing a QSGSimpleRectNode directly.
diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h
index 1331d036e1..a2278a8df0 100644
--- a/src/quick/items/qquickwindow.h
+++ b/src/quick/items/qquickwindow.h
@@ -66,7 +66,8 @@ class QQuickRenderControl;
class QSGRectangleNode;
class QSGImageNode;
class QSGNinePatchNode;
-class QRhi;
+class QQuickRenderTarget;
+class QQuickGraphicsDevice;
class Q_QUICK_EXPORT QQuickWindow : public QWindow
{
@@ -131,6 +132,8 @@ public:
#endif
QImage grabWindow();
+
+ // ### Qt 6 remove all these 5 functions. Replaced by setRenderTarget(QQuickRenderTarget*).
#if QT_CONFIG(opengl)
void setRenderTarget(QOpenGLFramebufferObject *fbo);
QOpenGLFramebufferObject *renderTarget() const;
@@ -138,8 +141,11 @@ public:
void setRenderTarget(uint fboId, const QSize &size);
uint renderTargetId() const;
QSize renderTargetSize() const;
+
+ void setRenderTarget(const QQuickRenderTarget &target);
+
#if QT_CONFIG(opengl)
- void resetOpenGLState();
+ void resetOpenGLState(); // ### Qt 6 remove
#endif
struct GraphicsStateInfo {
int currentFrameSlot;
@@ -178,13 +184,14 @@ public:
static bool hasDefaultAlphaBuffer();
static void setDefaultAlphaBuffer(bool useAlpha);
- void setPersistentOpenGLContext(bool persistent);
+ void setPersistentOpenGLContext(bool persistent); // ### Qt 6 is this relevant / usable anymore?
bool isPersistentOpenGLContext() const;
- void setPersistentSceneGraph(bool persistent);
+ void setPersistentSceneGraph(bool persistent); // ### Qt 6 is this relevant / usable anymore?
bool isPersistentSceneGraph() const;
- QOpenGLContext *openglContext() const;
+ QOpenGLContext *openglContext() const; // ### Qt 6 consider if this is kept or not
+
bool isSceneGraphInitialized() const;
void scheduleRenderJob(QRunnable *job, RenderStage schedule);
@@ -197,6 +204,8 @@ public:
static void setSceneGraphBackend(const QString &backend);
static QString sceneGraphBackend();
+ void setGraphicsDevice(const QQuickGraphicsDevice &device);
+
QSGRectangleNode *createRectangleNode() const;
QSGImageNode *createImageNode() const;
QSGNinePatchNode *createNinePatchNode() const;
@@ -206,7 +215,7 @@ public:
Q_SIGNALS:
void frameSwapped();
- Q_REVISION(2, 2) void openglContextCreated(QOpenGLContext *context);
+ Q_REVISION(2, 2) void openglContextCreated(QOpenGLContext *context); // ### Qt 6 remove
void sceneGraphInitialized();
void sceneGraphInvalidated();
void beforeSynchronizing();
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index a77ce30a89..ba8691cba3 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -57,6 +57,8 @@
#include <QtQuick/private/qsgcontext_p.h>
#include <QtQuick/private/qquickpaletteproviderprivatebase_p.h>
+#include <QtQuick/private/qquickrendertarget_p.h>
+#include <QtQuick/private/qquickgraphicsdevice_p.h>
#include <QtCore/qthread.h>
#include <QtCore/qmutex.h>
@@ -84,6 +86,7 @@ class QRhi;
class QRhiSwapChain;
class QRhiRenderBuffer;
class QRhiRenderPassDescriptor;
+class QRhiTexture;
//Make it easy to identify and customize the root item if needed
class Q_QUICK_PRIVATE_EXPORT QQuickRootItem : public QQuickItem
@@ -104,6 +107,17 @@ public:
virtual bool swap() = 0;
};
+class QQuickWindowRenderTarget
+{
+public:
+ void reset(QRhi *rhi);
+ QRhiRenderTarget *renderTarget = nullptr;
+ QRhiRenderPassDescriptor *rpDesc = nullptr;
+ QRhiTexture *texture = nullptr;
+ QRhiRenderBuffer *depthStencil = nullptr;
+ bool owns = false;
+};
+
class Q_QUICK_PRIVATE_EXPORT QQuickWindowPrivate
: public QWindowPrivate
, public QQuickPaletteProviderPrivateBase<QQuickWindow, QQuickWindowPrivate>
@@ -218,6 +232,9 @@ public:
void dirtyItem(QQuickItem *);
void cleanup(QSGNode *);
+ void ensureCustomRenderTarget();
+ void setCustomCommandBuffer(QRhiCommandBuffer *cb);
+
void polishItems();
void forcePolish();
void syncSceneGraph();
@@ -277,11 +294,26 @@ public:
Qt::FocusReason lastFocusReason;
- QOpenGLFramebufferObject *renderTarget;
- uint renderTargetId;
- QSize renderTargetSize;
+ // Storage for setRenderTarget(QQuickRenderTarget).
+ // Gets baked into redirect.renderTarget by ensureCustomRenderTarget() when rendering the next frame.
+ QQuickRenderTarget customRenderTarget;
+
+ struct Redirect {
+ QRhiCommandBuffer *commandBuffer = nullptr;
+ QQuickWindowRenderTarget rt;
+ bool renderTargetDirty = false;
+ } redirect;
+
+ QQuickGraphicsDevice customDeviceObjects;
+
+ // ### Qt 6 remove
+ struct {
+ QOpenGLFramebufferObject *renderTarget = nullptr;
+ uint renderTargetId = 0;
+ QSize renderTargetSize;
+ } customRenderTargetGl;
- QOpenGLVertexArrayObjectHelper *vaoHelper;
+ QOpenGLVertexArrayObjectHelper *vaoHelper; // ### Qt 6 remove
mutable QQuickWindowIncubationController *incubationController;
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index 5a281cdd4a..5dac796f2a 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -4116,6 +4116,8 @@ void Renderer::renderBatches()
if (m_renderPassRecordingCallbacks.start)
m_renderPassRecordingCallbacks.start(m_renderPassRecordingCallbacks.userData);
+ cb->debugMarkBegin(QByteArrayLiteral("Qt Quick scene render"));
+
for (int i = 0, ie = opaqueRenderBatches.count(); i != ie; ++i) {
PreparedRenderBatch *renderBatch = &opaqueRenderBatches[i];
if (renderBatch->batch->merged)
@@ -4137,6 +4139,8 @@ void Renderer::renderBatches()
if (m_currentShader)
setActiveRhiShader(nullptr, nullptr);
+ cb->debugMarkEnd();
+
if (m_renderPassRecordingCallbacks.end)
m_renderPassRecordingCallbacks.end(m_renderPassRecordingCallbacks.userData);
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index 424d1b9ea8..a49f4a2263 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -126,7 +126,7 @@ void QSGRenderLoop::cleanup()
s_instance = nullptr;
#ifdef ENABLE_DEFAULT_BACKEND
- QSGRhiSupport::instance()->cleanup();
+ QSGRhiSupport::cleanupVulkanInstance();
QSGRhiProfileConnection::instance()->cleanup();
#endif
}
diff --git a/src/quick/scenegraph/qsgrhisupport.cpp b/src/quick/scenegraph/qsgrhisupport.cpp
index 29671f8a10..b9a81789e0 100644
--- a/src/quick/scenegraph/qsgrhisupport.cpp
+++ b/src/quick/scenegraph/qsgrhisupport.cpp
@@ -39,7 +39,8 @@
#include "qsgrhisupport_p.h"
#include "qsgdefaultrendercontext_p.h"
-#include <QtGui/qwindow.h>
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickwindow_p.h>
#if QT_CONFIG(vulkan)
#include <QtGui/qvulkaninstance.h>
@@ -88,7 +89,7 @@ QVulkanInstance *QSGRhiSupport::vulkanInstance()
#endif
}
-void QSGRhiSupport::cleanup()
+void QSGRhiSupport::cleanupVulkanInstance()
{
#if QT_CONFIG(vulkan)
delete s_vulkanInstance;
@@ -97,7 +98,7 @@ void QSGRhiSupport::cleanup()
}
QSGRhiSupport::QSGRhiSupport()
- : m_set(false),
+ : m_settingsApplied(false),
m_enableRhi(false),
m_debugLayer(false),
m_profile(false),
@@ -108,7 +109,7 @@ QSGRhiSupport::QSGRhiSupport()
void QSGRhiSupport::applySettings()
{
- m_set = true;
+ m_settingsApplied = true;
// This is also done when creating the renderloop but we may be before that
// in case we get here due to a setScenegraphBackend() -> configure() early
@@ -214,10 +215,6 @@ void QSGRhiSupport::configure(QSGRendererInterface::GraphicsApi api)
{
Q_ASSERT(QSGRendererInterface::isApiRhiBased(api));
QSGRhiSupport *inst = staticInst();
- if (inst->m_set) {
- qWarning("QRhi is already configured, request ignored");
- return;
- }
inst->m_requested.valid = true;
inst->m_requested.api = api;
inst->m_requested.rhi = true;
@@ -227,7 +224,7 @@ void QSGRhiSupport::configure(QSGRendererInterface::GraphicsApi api)
QSGRhiSupport *QSGRhiSupport::instance()
{
QSGRhiSupport *inst = staticInst();
- if (!inst->m_set)
+ if (!inst->m_settingsApplied)
inst->applySettings();
return inst;
}
@@ -467,11 +464,10 @@ QOffscreenSurface *QSGRhiSupport::maybeCreateOffscreenSurface(QWindow *window)
}
// must be called on the render thread
-QRhi *QSGRhiSupport::createRhi(QWindow *window, QOffscreenSurface *offscreenSurface)
+QRhi *QSGRhiSupport::createRhi(QQuickWindow *window, QOffscreenSurface *offscreenSurface)
{
-#if !QT_CONFIG(opengl) && !QT_CONFIG(vulkan)
- Q_UNUSED(window);
-#endif
+ const QQuickGraphicsDevice &customDev(QQuickWindowPrivate::get(window)->customDeviceObjects);
+ const QQuickGraphicsDevicePrivate *customDevD = QQuickGraphicsDevicePrivate::get(&customDev);
QRhi *rhi = nullptr;
@@ -493,7 +489,14 @@ QRhi *QSGRhiSupport::createRhi(QWindow *window, QOffscreenSurface *offscreenSurf
rhiParams.format = format;
rhiParams.fallbackSurface = offscreenSurface;
rhiParams.window = window;
- rhi = QRhi::create(backend, &rhiParams, flags);
+ if (customDevD->type == QQuickGraphicsDevicePrivate::Type::OpenGLContext) {
+ QRhiGles2NativeHandles importDev;
+ importDev.context = customDevD->u.context;
+ qCDebug(QSG_LOG_INFO, "Using existing QOpenGLContext %p", importDev.context);
+ rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
+ } else {
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ }
}
#else
Q_UNUSED(offscreenSurface);
@@ -504,8 +507,19 @@ QRhi *QSGRhiSupport::createRhi(QWindow *window, QOffscreenSurface *offscreenSurf
rhiParams.inst = window->vulkanInstance();
if (!rhiParams.inst)
qWarning("No QVulkanInstance set for QQuickWindow, this is wrong.");
- rhiParams.window = window;
- rhi = QRhi::create(backend, &rhiParams, flags);
+ if (window->handle()) // only used for vkGetPhysicalDeviceSurfaceSupportKHR and that implies having a valid native window
+ rhiParams.window = window;
+ if (customDevD->type == QQuickGraphicsDevicePrivate::Type::DeviceObjects) {
+ QRhiVulkanNativeHandles importDev;
+ importDev.physDev = reinterpret_cast<VkPhysicalDevice>(customDevD->u.deviceObjects.physicalDevice);
+ importDev.dev = reinterpret_cast<VkDevice>(customDevD->u.deviceObjects.device);
+ importDev.gfxQueueFamilyIdx = customDevD->u.deviceObjects.queueFamilyIndex;
+ qCDebug(QSG_LOG_INFO, "Using existing native Vulkan physical device %p device %p graphics queue family index %d",
+ importDev.physDev, importDev.dev, importDev.gfxQueueFamilyIdx);
+ rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
+ } else {
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ }
}
#endif
#ifdef Q_OS_WIN
@@ -516,13 +530,31 @@ QRhi *QSGRhiSupport::createRhi(QWindow *window, QOffscreenSurface *offscreenSurf
rhiParams.framesUntilKillingDeviceViaTdr = m_killDeviceFrameCount;
rhiParams.repeatDeviceKill = true;
}
- rhi = QRhi::create(backend, &rhiParams, flags);
+ if (customDevD->type == QQuickGraphicsDevicePrivate::Type::DeviceAndContext) {
+ QRhiD3D11NativeHandles importDev;
+ importDev.dev = customDevD->u.deviceAndContext.device;
+ importDev.context = customDevD->u.deviceAndContext.context;
+ qCDebug(QSG_LOG_INFO, "Using existing native D3D11 device %p and context %p",
+ importDev.dev, importDev.context);
+ rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
+ } else {
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ }
}
#endif
#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
if (backend == QRhi::Metal) {
QRhiMetalInitParams rhiParams;
- rhi = QRhi::create(backend, &rhiParams, flags);
+ if (customDevD->type == QQuickGraphicsDevicePrivate::Type::DeviceAndCommandQueue) {
+ QRhiMetalNativeHandles importDev;
+ importDev.dev = customDevD->u.deviceAndCommandQueue.device;
+ importDev.cmdQueue = customDevD->u.deviceAndCommandQueue.cmdQueue;
+ qCDebug(QSG_LOG_INFO, "Using existing native Metal device %p and command queue %p",
+ importDev.dev, importDev.cmdQueue);
+ rhi = QRhi::create(backend, &rhiParams, flags, &importDev);
+ } else {
+ rhi = QRhi::create(backend, &rhiParams, flags);
+ }
}
#endif
diff --git a/src/quick/scenegraph/qsgrhisupport_p.h b/src/quick/scenegraph/qsgrhisupport_p.h
index 0a95a09ad2..601dd07895 100644
--- a/src/quick/scenegraph/qsgrhisupport_p.h
+++ b/src/quick/scenegraph/qsgrhisupport_p.h
@@ -91,7 +91,7 @@ class QOffscreenSurface;
// Opting in/out of QRhi and choosing the default/requested backend is managed
// by this singleton. This is because this information may be needed before
// creating a render loop. A well-written render loop sets up its QRhi and
-// related machinery based on the settings queriable from here.
+// related machinery using the helper functions in here.
//
// cleanup() must be called to perform global (not per thread) cleanup, such
// as, destroying the QVulkanInstance (if one was created in vulkanInstance()).
@@ -99,13 +99,13 @@ class QOffscreenSurface;
// In addition, the class provides handy conversion and query stuff for the
// renderloop and the QSGRendererInterface implementations.
//
-class QSGRhiSupport
+class Q_QUICK_PRIVATE_EXPORT QSGRhiSupport
{
public:
static void configure(QSGRendererInterface::GraphicsApi api);
static QSGRhiSupport *instance();
static QVulkanInstance *vulkanInstance();
- void cleanup();
+ static void cleanupVulkanInstance();
bool isRhiEnabled() const { return m_enableRhi; }
QRhi::Implementation rhiBackend() const { return m_rhiBackend; }
@@ -124,7 +124,7 @@ public:
int chooseSampleCountForWindowWithRhi(QWindow *window, QRhi *rhi);
QOffscreenSurface *maybeCreateOffscreenSurface(QWindow *window);
- QRhi *createRhi(QWindow *window, QOffscreenSurface *offscreenSurface);
+ QRhi *createRhi(QQuickWindow *window, QOffscreenSurface *offscreenSurface);
QImage grabAndBlockInCurrentFrame(QRhi *rhi, QRhiSwapChain *swapchain);
@@ -141,7 +141,7 @@ private:
} m_requested;
QRhi::Implementation m_rhiBackend = QRhi::Null;
int m_killDeviceFrameCount;
- uint m_set : 1;
+ uint m_settingsApplied : 1;
uint m_enableRhi : 1;
uint m_debugLayer : 1;
uint m_profile : 1;