diff options
Diffstat (limited to 'src/quick/items')
-rw-r--r-- | src/quick/items/items.pri | 10 | ||||
-rw-r--r-- | src/quick/items/qquickgraphicsdevice.cpp | 216 | ||||
-rw-r--r-- | src/quick/items/qquickgraphicsdevice.h | 73 | ||||
-rw-r--r-- | src/quick/items/qquickgraphicsdevice_p.h | 105 | ||||
-rw-r--r-- | src/quick/items/qquickrendercontrol.cpp | 316 | ||||
-rw-r--r-- | src/quick/items/qquickrendercontrol.h | 19 | ||||
-rw-r--r-- | src/quick/items/qquickrendercontrol_p.h | 18 | ||||
-rw-r--r-- | src/quick/items/qquickrendertarget.cpp | 293 | ||||
-rw-r--r-- | src/quick/items/qquickrendertarget.h | 78 | ||||
-rw-r--r-- | src/quick/items/qquickrendertarget_p.h | 90 | ||||
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 295 | ||||
-rw-r--r-- | src/quick/items/qquickwindow.h | 21 | ||||
-rw-r--r-- | src/quick/items/qquickwindow_p.h | 40 |
13 files changed, 1482 insertions, 92 deletions
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(¶ms); + } 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; |