| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 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 "qquickrendercontrol.h" |
| #include "qquickrendercontrol_p.h" |
| |
| #include <QtCore/QCoreApplication> |
| #include <QtCore/QTime> |
| #include <QtQuick/private/qquickanimatorcontroller_p.h> |
| |
| #if QT_CONFIG(opengl) |
| # include <QtGui/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 <QtQml/private/qqmlglobal_p.h> |
| |
| #include <QtQuick/QQuickWindow> |
| #include <QtQuick/private/qquickwindow_p.h> |
| #include <QtQuick/private/qsgsoftwarerenderer_p.h> |
| #include <QtCore/private/qobject_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 |
| |
| \brief The QQuickRenderControl class provides a mechanism for rendering the Qt |
| Quick scenegraph onto an offscreen render target in a fully |
| application-controlled manner. |
| |
| \since 5.4 |
| |
| QQuickWindow and QQuickView and their associated internal render loops render |
| the Qt Quick scene onto a native window. In some cases, for example when |
| integrating with 3rd party OpenGL renderers, it might be beneficial to get the |
| scene into a texture that can then be used in arbitrary ways by the external |
| rendering engine. QQuickRenderControl makes this possible in a hardware |
| accelerated manner, unlike the performance-wise limited alternative of using |
| QQuickWindow::grabWindow() |
| |
| When using a QQuickRenderControl, the QQuickWindow does not have to be shown |
| or even created at all. This means there will not be an underlying native |
| window for it. Instead, the QQuickWindow instance is associated with the |
| render control, using the overload of the QQuickWindow constructor, and an |
| OpenGL framebuffer object by calling QQuickWindow::setRenderTarget(). |
| |
| Management of the context and framebuffer object is up to the application. The |
| context that will be used by Qt Quick must be created before calling |
| initialize(). The creation of the framebuffer object can be deferred, see |
| below. Qt 5.4 introduces the ability for QOpenGLContext to adopt existing |
| native contexts. Together with QQuickRenderControl this makes it possible to |
| create a QOpenGLContext that shares with an external rendering engine's |
| existing context. This new QOpenGLContext can then be used to render the Qt |
| Quick scene into a texture that is accessible by the other engine's context |
| too. |
| |
| Loading and instantiation of the QML components happen by using a |
| QQmlEngine. Once the root object is created, it will need to be parented to |
| the QQuickWindow's contentItem(). |
| |
| Applications will usually have to connect to 4 important signals: |
| |
| \list |
| |
| \li QQuickWindow::sceneGraphInitialized() Emitted at some point after calling |
| QQuickRenderControl::initialize(). Upon this signal, the application is |
| expected to create its framebuffer object and associate it with the |
| QQuickWindow. |
| |
| \li QQuickWindow::sceneGraphInvalidated() When the scenegraph resources are |
| released, the framebuffer object can be destroyed too. |
| |
| \li QQuickRenderControl::renderRequested() Indicates that the scene has to be |
| rendered by calling render(). After making the context current, applications |
| are expected to call render(). |
| |
| \li QQuickRenderControl::sceneChanged() Indicates that the scene has changed |
| meaning that, before rendering, polishing and synchronizing is also necessary. |
| |
| \endlist |
| |
| To send events, for example mouse or keyboard events, to the scene, use |
| QCoreApplication::sendEvent() with the QQuickWindow instance as the receiver. |
| |
| \note In general QQuickRenderControl is supported in combination with all Qt |
| Quick backends. However, some functionality, in particular grab(), may not be |
| available in all cases. |
| |
| \inmodule QtQuick |
| */ |
| |
| QSGContext *QQuickRenderControlPrivate::sg = nullptr; |
| |
| QQuickRenderControlPrivate::QQuickRenderControlPrivate() |
| : initialized(0), |
| window(nullptr) |
| { |
| if (!sg) { |
| qAddPostRoutine(cleanup); |
| sg = QSGContext::createDefaultContext(); |
| } |
| rc = sg->createRenderContext(); |
| } |
| |
| void QQuickRenderControlPrivate::cleanup() |
| { |
| delete sg; |
| sg = nullptr; |
| } |
| |
| /*! |
| Constructs a QQuickRenderControl object, with parent |
| object \a parent. |
| */ |
| QQuickRenderControl::QQuickRenderControl(QObject *parent) |
| : QObject(*(new QQuickRenderControlPrivate), parent) |
| { |
| } |
| |
| /*! |
| Destroys the instance. Releases all scenegraph resources. |
| |
| \sa invalidate() |
| */ |
| QQuickRenderControl::~QQuickRenderControl() |
| { |
| Q_D(QQuickRenderControl); |
| |
| invalidate(); |
| |
| if (d->window) |
| QQuickWindowPrivate::get(d->window)->renderControl = nullptr; |
| |
| // It is likely that the cleanup in windowDestroyed() is not called since |
| // the standard pattern is to destroy the rendercontrol before the QQuickWindow. |
| // Do it here. |
| d->windowDestroyed(); |
| |
| delete d->rc; |
| } |
| |
| void QQuickRenderControlPrivate::windowDestroyed() |
| { |
| if (window) { |
| rc->invalidate(); |
| |
| QQuickWindowPrivate::get(window)->animationController.reset(); |
| |
| #if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl) |
| if (QOpenGLContext::currentContext()) |
| QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); |
| #endif |
| |
| window = nullptr; |
| } |
| } |
| |
| /*! |
| Prepares rendering the Qt Quick scene outside the gui thread. |
| |
| \a targetThread specifies the thread on which synchronization and |
| rendering will happen. There is no need to call this function in a |
| single threaded scenario. |
| */ |
| void QQuickRenderControl::prepareThread(QThread *targetThread) |
| { |
| Q_D(QQuickRenderControl); |
| d->rc->moveToThread(targetThread); |
| QQuickWindowPrivate::get(d->window)->animationController->moveToThread(targetThread); |
| } |
| |
| /*! |
| 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. |
| |
| \note Qt Quick does not take ownership of the context. It is up to the |
| application to destroy it after a call to invalidate() or after the |
| QQuickRenderControl instance is destroyed. |
| */ |
| void QQuickRenderControl::initialize(QOpenGLContext *gl) |
| { |
| |
| Q_D(QQuickRenderControl); |
| #if QT_CONFIG(opengl) |
| if (!d->window) { |
| qWarning("QQuickRenderControl::initialize called with no associated window"); |
| return; |
| } |
| |
| if (QOpenGLContext::currentContext() != gl) { |
| qWarning("QQuickRenderControl::initialize called with incorrect current context"); |
| return; |
| } |
| |
| // It is the caller's responsiblity to make a context/surface current. |
| // It cannot be done here since the surface to use may not be the |
| // surface belonging to window. In fact window may not have a native |
| // window/surface at all. |
| QSGDefaultRenderContext *rc = qobject_cast<QSGDefaultRenderContext *>(d->rc); |
| if (rc) { |
| QSGDefaultRenderContext::InitParams params; |
| params.sampleCount = qMax(1, gl->format().samples()); |
| params.openGLContext = gl; |
| params.initialSurfacePixelSize = d->window->size() * d->window->effectiveDevicePixelRatio(); |
| params.maybeSurface = d->window; |
| rc->initialize(¶ms); |
| } else { |
| // can this happen? |
| d->rc->initialize(nullptr); |
| } |
| #else |
| Q_UNUSED(gl) |
| #endif |
| d->initialized = true; |
| } |
| |
| /*! |
| This function should be called as late as possible before |
| sync(). In a threaded scenario, rendering can happen in parallel |
| with this function. |
| */ |
| void QQuickRenderControl::polishItems() |
| { |
| Q_D(QQuickRenderControl); |
| if (!d->window) |
| return; |
| |
| QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window); |
| cd->flushFrameSynchronousEvents(); |
| if (!d->window) |
| return; |
| cd->polishItems(); |
| emit d->window->afterAnimating(); |
| } |
| |
| /*! |
| This function is used to synchronize the QML scene with the rendering scene |
| graph. |
| |
| If a dedicated render thread is used, the GUI thread should be blocked for the |
| duration of this call. |
| |
| \return \e true if the synchronization changed the scene graph. |
| */ |
| bool QQuickRenderControl::sync() |
| { |
| Q_D(QQuickRenderControl); |
| if (!d->window) |
| return false; |
| |
| QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window); |
| 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. |
| |
| 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. |
| |
| Once invalidate() has been called, it is possible to reuse the |
| QQuickRenderControl instance by calling initialize() again. |
| |
| \note This function does not take |
| QQuickWindow::persistentSceneGraph() or |
| QQuickWindow::persistentOpenGLContext() into account. This means |
| that context-specific resources are always released. |
| */ |
| void QQuickRenderControl::invalidate() |
| { |
| Q_D(QQuickRenderControl); |
| if (!d->window) |
| return; |
| |
| QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window); |
| cd->fireAboutToStop(); |
| cd->cleanupNodesOnShutdown(); |
| |
| if (!d->initialized) |
| return; |
| |
| // We must invalidate since the context can potentially be destroyed by the |
| // application right after returning from this function. Invalidating is |
| // also essential to allow a subsequent initialize() to succeed. |
| d->rc->invalidate(); |
| |
| d->initialized = false; |
| } |
| |
| /*! |
| Renders the scenegraph using the current context. |
| */ |
| void QQuickRenderControl::render() |
| { |
| Q_D(QQuickRenderControl); |
| if (!d->window) |
| return; |
| |
| QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window); |
| cd->renderSceneGraph(d->window->size()); |
| } |
| |
| /*! |
| \fn void QQuickRenderControl::renderRequested() |
| |
| This signal is emitted when the scene graph needs to be rendered. It is not necessary to call sync(). |
| |
| \note Avoid triggering rendering directly when this signal is |
| emitted. Instead, prefer deferring it by using a timer for example. This |
| will lead to better performance. |
| */ |
| |
| /*! |
| \fn void QQuickRenderControl::sceneChanged() |
| |
| This signal is emitted when the scene graph is updated, meaning that |
| polishItems() and sync() needs to be called. If sync() returns |
| true, then render() needs to be called. |
| |
| \note Avoid triggering polishing, synchronization and rendering directly |
| when this signal is emitted. Instead, prefer deferring it by using a timer |
| 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() |
| { |
| Q_D(QQuickRenderControl); |
| if (!d->window) |
| return QImage(); |
| |
| QImage grabContent; |
| |
| if (d->window->rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL) { |
| #if QT_CONFIG(opengl) |
| QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->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()); |
| } |
| #endif |
| #if QT_CONFIG(thread) |
| } else if (d->window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) { |
| QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->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; |
| grabContent = QImage(imageSize, QImage::Format_ARGB32_Premultiplied); |
| grabContent.setDevicePixelRatio(dpr); |
| QPaintDevice *prevDev = softwareRenderer->currentPaintDevice(); |
| softwareRenderer->setCurrentPaintDevice(&grabContent); |
| softwareRenderer->markDirty(); |
| d->rc->endSync(); |
| render(); |
| softwareRenderer->setCurrentPaintDevice(prevDev); |
| } |
| #endif |
| } else { |
| qWarning("QQuickRenderControl: grabs are not supported with the current Qt Quick backend"); |
| } |
| |
| return grabContent; |
| } |
| |
| void QQuickRenderControlPrivate::update() |
| { |
| Q_Q(QQuickRenderControl); |
| emit q->renderRequested(); |
| } |
| |
| void QQuickRenderControlPrivate::maybeUpdate() |
| { |
| Q_Q(QQuickRenderControl); |
| emit q->sceneChanged(); |
| } |
| |
| /*! |
| \fn QWindow *QQuickRenderControl::renderWindow(QPoint *offset) |
| |
| Reimplemented in subclasses to return the real window this render control |
| is rendering into. |
| |
| If \a offset in non-null, it is set to the offset of the control |
| inside the window. |
| |
| \note While not mandatory, reimplementing this function becomes essential for |
| supporting multiple screens with different device pixel ratios and properly positioning |
| popup windows opened from QML. Therefore providing it in subclasses is highly |
| recommended. |
| */ |
| |
| /*! |
| Returns the real window that \a win is being rendered to, if any. |
| |
| If \a offset in non-null, it is set to the offset of the rendering |
| inside its window. |
| |
| */ |
| QWindow *QQuickRenderControl::renderWindowFor(QQuickWindow *win, QPoint *offset) |
| { |
| if (!win) |
| return nullptr; |
| QQuickRenderControl *rc = QQuickWindowPrivate::get(win)->renderControl; |
| if (rc) |
| return rc->renderWindow(offset); |
| return nullptr; |
| } |
| |
| QT_END_NAMESPACE |
| |
| #include "moc_qquickrendercontrol.cpp" |