1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquickrendercontrol.h"
41#include "qquickrendercontrol_p.h"
42
43#include <QtCore/QCoreApplication>
44#include <QtCore/QTime>
45#include <QtQuick/private/qquickanimatorcontroller_p.h>
46
47#if QT_CONFIG(opengl)
48# include <QtGui/QOpenGLContext>
49# include <QtQuick/private/qsgdefaultrendercontext_p.h>
50#if QT_CONFIG(quick_shadereffect)
51# include <QtQuick/private/qquickopenglshadereffectnode_p.h>
52#endif
53#endif
54#include <QtGui/private/qguiapplication_p.h>
55#include <qpa/qplatformintegration.h>
56
57#include <QtQml/private/qqmlglobal_p.h>
58
59#include <QtQuick/QQuickWindow>
60#include <QtQuick/private/qquickwindow_p.h>
61#include <QtQuick/private/qsgsoftwarerenderer_p.h>
62#include <QtCore/private/qobject_p.h>
63
64QT_BEGIN_NAMESPACE
65#if QT_CONFIG(opengl)
66extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
67#endif
68/*!
69 \class QQuickRenderControl
70
71 \brief The QQuickRenderControl class provides a mechanism for rendering the Qt
72 Quick scenegraph onto an offscreen render target in a fully
73 application-controlled manner.
74
75 \since 5.4
76
77 QQuickWindow and QQuickView and their associated internal render loops render
78 the Qt Quick scene onto a native window. In some cases, for example when
79 integrating with 3rd party OpenGL renderers, it might be beneficial to get the
80 scene into a texture that can then be used in arbitrary ways by the external
81 rendering engine. QQuickRenderControl makes this possible in a hardware
82 accelerated manner, unlike the performance-wise limited alternative of using
83 QQuickWindow::grabWindow()
84
85 When using a QQuickRenderControl, the QQuickWindow does not have to be shown
86 or even created at all. This means there will not be an underlying native
87 window for it. Instead, the QQuickWindow instance is associated with the
88 render control, using the overload of the QQuickWindow constructor, and an
89 OpenGL framebuffer object by calling QQuickWindow::setRenderTarget().
90
91 Management of the context and framebuffer object is up to the application. The
92 context that will be used by Qt Quick must be created before calling
93 initialize(). The creation of the framebuffer object can be deferred, see
94 below. Qt 5.4 introduces the ability for QOpenGLContext to adopt existing
95 native contexts. Together with QQuickRenderControl this makes it possible to
96 create a QOpenGLContext that shares with an external rendering engine's
97 existing context. This new QOpenGLContext can then be used to render the Qt
98 Quick scene into a texture that is accessible by the other engine's context
99 too.
100
101 Loading and instantiation of the QML components happen by using a
102 QQmlEngine. Once the root object is created, it will need to be parented to
103 the QQuickWindow's contentItem().
104
105 Applications will usually have to connect to 4 important signals:
106
107 \list
108
109 \li QQuickWindow::sceneGraphInitialized() Emitted at some point after calling
110 QQuickRenderControl::initialize(). Upon this signal, the application is
111 expected to create its framebuffer object and associate it with the
112 QQuickWindow.
113
114 \li QQuickWindow::sceneGraphInvalidated() When the scenegraph resources are
115 released, the framebuffer object can be destroyed too.
116
117 \li QQuickRenderControl::renderRequested() Indicates that the scene has to be
118 rendered by calling render(). After making the context current, applications
119 are expected to call render().
120
121 \li QQuickRenderControl::sceneChanged() Indicates that the scene has changed
122 meaning that, before rendering, polishing and synchronizing is also necessary.
123
124 \endlist
125
126 To send events, for example mouse or keyboard events, to the scene, use
127 QCoreApplication::sendEvent() with the QQuickWindow instance as the receiver.
128
129 \note In general QQuickRenderControl is supported in combination with all Qt
130 Quick backends. However, some functionality, in particular grab(), may not be
131 available in all cases.
132
133 \inmodule QtQuick
134*/
135
136QSGContext *QQuickRenderControlPrivate::sg = nullptr;
137
138QQuickRenderControlPrivate::QQuickRenderControlPrivate()
139 : initialized(0),
140 window(nullptr)
141{
142 if (!sg) {
143 qAddPostRoutine(cleanup);
144 sg = QSGContext::createDefaultContext();
145 }
146 rc = sg->createRenderContext();
147}
148
149void QQuickRenderControlPrivate::cleanup()
150{
151 delete sg;
152 sg = nullptr;
153}
154
155/*!
156 Constructs a QQuickRenderControl object, with parent
157 object \a parent.
158*/
159QQuickRenderControl::QQuickRenderControl(QObject *parent)
160 : QObject(*(new QQuickRenderControlPrivate), parent)
161{
162}
163
164/*!
165 Destroys the instance. Releases all scenegraph resources.
166
167 \sa invalidate()
168 */
169QQuickRenderControl::~QQuickRenderControl()
170{
171 Q_D(QQuickRenderControl);
172
173 invalidate();
174
175 if (d->window)
176 QQuickWindowPrivate::get(c: d->window)->renderControl = nullptr;
177
178 // It is likely that the cleanup in windowDestroyed() is not called since
179 // the standard pattern is to destroy the rendercontrol before the QQuickWindow.
180 // Do it here.
181 d->windowDestroyed();
182
183 delete d->rc;
184}
185
186void QQuickRenderControlPrivate::windowDestroyed()
187{
188 if (window) {
189 rc->invalidate();
190
191 QQuickWindowPrivate::get(c: window)->animationController.reset();
192
193#if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl)
194 if (QOpenGLContext::currentContext())
195 QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache();
196#endif
197
198 window = nullptr;
199 }
200}
201
202/*!
203 Prepares rendering the Qt Quick scene outside the GUI thread.
204
205 \a targetThread specifies the thread on which synchronization and
206 rendering will happen. There is no need to call this function in a
207 single threaded scenario.
208 */
209void QQuickRenderControl::prepareThread(QThread *targetThread)
210{
211 Q_D(QQuickRenderControl);
212 d->rc->moveToThread(thread: targetThread);
213 QQuickWindowPrivate::get(c: d->window)->animationController->moveToThread(thread: targetThread);
214}
215
216/*!
217 Initializes the scene graph resources. The context \a gl has to be the
218 current OpenGL context or null if it is not relevant because a Qt Quick
219 backend other than OpenGL is in use.
220
221 \note Qt Quick does not take ownership of the context. It is up to the
222 application to destroy it after a call to invalidate() or after the
223 QQuickRenderControl instance is destroyed.
224 */
225void QQuickRenderControl::initialize(QOpenGLContext *gl)
226{
227
228 Q_D(QQuickRenderControl);
229#if QT_CONFIG(opengl)
230 if (!d->window) {
231 qWarning(msg: "QQuickRenderControl::initialize called with no associated window");
232 return;
233 }
234
235 if (QOpenGLContext::currentContext() != gl) {
236 qWarning(msg: "QQuickRenderControl::initialize called with incorrect current context");
237 return;
238 }
239
240 // It is the caller's responsiblity to make a context/surface current.
241 // It cannot be done here since the surface to use may not be the
242 // surface belonging to window. In fact window may not have a native
243 // window/surface at all.
244 QSGDefaultRenderContext *rc = qobject_cast<QSGDefaultRenderContext *>(object: d->rc);
245 if (rc) {
246 QSGDefaultRenderContext::InitParams params;
247 params.sampleCount = qMax(a: 1, b: gl->format().samples());
248 params.openGLContext = gl;
249 params.initialSurfacePixelSize = d->window->size() * d->window->effectiveDevicePixelRatio();
250 params.maybeSurface = d->window;
251 rc->initialize(params: &params);
252 } else {
253 // can this happen?
254 d->rc->initialize(params: nullptr);
255 }
256#else
257 Q_UNUSED(gl)
258#endif
259 d->initialized = true;
260}
261
262/*!
263 This function should be called as late as possible before
264 sync(). In a threaded scenario, rendering can happen in parallel
265 with this function.
266 */
267void QQuickRenderControl::polishItems()
268{
269 Q_D(QQuickRenderControl);
270 if (!d->window)
271 return;
272
273 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window);
274 cd->flushFrameSynchronousEvents();
275 if (!d->window)
276 return;
277 cd->polishItems();
278 emit d->window->afterAnimating();
279}
280
281/*!
282 This function is used to synchronize the QML scene with the rendering scene
283 graph.
284
285 If a dedicated render thread is used, the GUI thread should be blocked for the
286 duration of this call.
287
288 \return \e true if the synchronization changed the scene graph.
289 */
290bool QQuickRenderControl::sync()
291{
292 Q_D(QQuickRenderControl);
293 if (!d->window)
294 return false;
295
296 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window);
297 cd->syncSceneGraph();
298 d->rc->endSync();
299
300 // TODO: find out if the sync actually caused a scenegraph update.
301 return true;
302}
303
304/*!
305 Stop rendering and release resources. Requires a current context.
306
307 This is the equivalent of the cleanup operations that happen with a
308 real QQuickWindow when the window becomes hidden.
309
310 This function is called from the destructor. Therefore there will
311 typically be no need to call it directly. Pay attention however to
312 the fact that this requires the context, that was passed to
313 initialize(), to be the current one at the time of destroying the
314 QQuickRenderControl instance.
315
316 Once invalidate() has been called, it is possible to reuse the
317 QQuickRenderControl instance by calling initialize() again.
318
319 \note This function does not take
320 QQuickWindow::persistentSceneGraph() or
321 QQuickWindow::persistentOpenGLContext() into account. This means
322 that context-specific resources are always released.
323 */
324void QQuickRenderControl::invalidate()
325{
326 Q_D(QQuickRenderControl);
327 if (!d->window)
328 return;
329
330 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window);
331 cd->fireAboutToStop();
332 cd->cleanupNodesOnShutdown();
333
334 if (!d->initialized)
335 return;
336
337 // We must invalidate since the context can potentially be destroyed by the
338 // application right after returning from this function. Invalidating is
339 // also essential to allow a subsequent initialize() to succeed.
340 d->rc->invalidate();
341
342 d->initialized = false;
343}
344
345/*!
346 Renders the scenegraph using the current context.
347 */
348void QQuickRenderControl::render()
349{
350 Q_D(QQuickRenderControl);
351 if (!d->window)
352 return;
353
354 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window);
355 cd->renderSceneGraph(size: d->window->size());
356}
357
358/*!
359 \fn void QQuickRenderControl::renderRequested()
360
361 This signal is emitted when the scene graph needs to be rendered. It is not necessary to call sync().
362
363 \note Avoid triggering rendering directly when this signal is
364 emitted. Instead, prefer deferring it by using a timer for example. This
365 will lead to better performance.
366*/
367
368/*!
369 \fn void QQuickRenderControl::sceneChanged()
370
371 This signal is emitted when the scene graph is updated, meaning that
372 polishItems() and sync() needs to be called. If sync() returns
373 true, then render() needs to be called.
374
375 \note Avoid triggering polishing, synchronization and rendering directly
376 when this signal is emitted. Instead, prefer deferring it by using a timer
377 for example. This will lead to better performance.
378*/
379
380/*!
381 Grabs the contents of the scene and returns it as an image.
382
383 \note Requires the context to be current.
384 */
385QImage QQuickRenderControl::grab()
386{
387 Q_D(QQuickRenderControl);
388 if (!d->window)
389 return QImage();
390
391 QImage grabContent;
392
393 if (d->window->rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL) {
394#if QT_CONFIG(opengl)
395 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window);
396 cd->polishItems();
397 cd->syncSceneGraph();
398 d->rc->endSync();
399 render();
400 const bool alpha = d->window->format().alphaBufferSize() > 0 && d->window->color().alpha() < 255;
401 grabContent = qt_gl_read_framebuffer(size: d->window->size() * d->window->effectiveDevicePixelRatio(), alpha_format: alpha, include_alpha: alpha);
402 if (QQuickRenderControl::renderWindowFor(win: d->window)) {
403 grabContent.setDevicePixelRatio(d->window->effectiveDevicePixelRatio());
404 }
405#endif
406#if QT_CONFIG(thread)
407 } else if (d->window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) {
408 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window);
409 cd->polishItems();
410 cd->syncSceneGraph();
411 QSGSoftwareRenderer *softwareRenderer = static_cast<QSGSoftwareRenderer *>(cd->renderer);
412 if (softwareRenderer) {
413 const qreal dpr = d->window->effectiveDevicePixelRatio();
414 const QSize imageSize = d->window->size() * dpr;
415 grabContent = QImage(imageSize, QImage::Format_ARGB32_Premultiplied);
416 grabContent.setDevicePixelRatio(dpr);
417 QPaintDevice *prevDev = softwareRenderer->currentPaintDevice();
418 softwareRenderer->setCurrentPaintDevice(&grabContent);
419 softwareRenderer->markDirty();
420 d->rc->endSync();
421 render();
422 softwareRenderer->setCurrentPaintDevice(prevDev);
423 }
424#endif
425 } else {
426 qWarning(msg: "QQuickRenderControl: grabs are not supported with the current Qt Quick backend");
427 }
428
429 return grabContent;
430}
431
432void QQuickRenderControlPrivate::update()
433{
434 Q_Q(QQuickRenderControl);
435 emit q->renderRequested();
436}
437
438void QQuickRenderControlPrivate::maybeUpdate()
439{
440 Q_Q(QQuickRenderControl);
441 emit q->sceneChanged();
442}
443
444/*!
445 \fn QWindow *QQuickRenderControl::renderWindow(QPoint *offset)
446
447 Reimplemented in subclasses to return the real window this render control
448 is rendering into.
449
450 If \a offset in non-null, it is set to the offset of the control
451 inside the window.
452
453 \note While not mandatory, reimplementing this function becomes essential for
454 supporting multiple screens with different device pixel ratios and properly positioning
455 popup windows opened from QML. Therefore providing it in subclasses is highly
456 recommended.
457*/
458
459/*!
460 Returns the real window that \a win is being rendered to, if any.
461
462 If \a offset in non-null, it is set to the offset of the rendering
463 inside its window.
464
465 */
466QWindow *QQuickRenderControl::renderWindowFor(QQuickWindow *win, QPoint *offset)
467{
468 if (!win)
469 return nullptr;
470 QQuickRenderControl *rc = QQuickWindowPrivate::get(c: win)->renderControl;
471 if (rc)
472 return rc->renderWindow(offset);
473 return nullptr;
474}
475
476QT_END_NAMESPACE
477
478#include "moc_qquickrendercontrol.cpp"
479

source code of qtdeclarative/src/quick/items/qquickrendercontrol.cpp