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 | |
64 | QT_BEGIN_NAMESPACE |
65 | #if QT_CONFIG(opengl) |
66 | extern 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 | |
136 | QSGContext *QQuickRenderControlPrivate::sg = nullptr; |
137 | |
138 | QQuickRenderControlPrivate::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 | |
149 | void QQuickRenderControlPrivate::cleanup() |
150 | { |
151 | delete sg; |
152 | sg = nullptr; |
153 | } |
154 | |
155 | /*! |
156 | Constructs a QQuickRenderControl object, with parent |
157 | object \a parent. |
158 | */ |
159 | QQuickRenderControl::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 | */ |
169 | QQuickRenderControl::~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 | |
186 | void 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 | */ |
209 | void 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 | */ |
225 | void 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: ¶ms); |
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 | */ |
267 | void 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 | */ |
290 | bool 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 | */ |
324 | void 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 | */ |
348 | void 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 | */ |
385 | QImage 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 | |
432 | void QQuickRenderControlPrivate::update() |
433 | { |
434 | Q_Q(QQuickRenderControl); |
435 | emit q->renderRequested(); |
436 | } |
437 | |
438 | void 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 | */ |
466 | QWindow *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 | |
476 | QT_END_NAMESPACE |
477 | |
478 | #include "moc_qquickrendercontrol.cpp" |
479 | |