1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquickrendercontrol.h"
5#include "qquickrendercontrol_p.h"
6
7#include <QtCore/QCoreApplication>
8#include <QtCore/QTime>
9#include <QtQuick/private/qquickanimatorcontroller_p.h>
10#include <QtQuick/private/qsgdefaultrendercontext_p.h>
11#include <QtQuick/private/qsgrhisupport_p.h>
12
13#include <private/qsgrhishadereffectnode_p.h>
14
15#include <QtGui/private/qguiapplication_p.h>
16#include <qpa/qplatformintegration.h>
17#include <QtGui/qoffscreensurface.h>
18
19#include <QtQml/private/qqmlglobal_p.h>
20
21#include <QtQuick/QQuickWindow>
22#include <QtQuick/QQuickRenderTarget>
23#include <QtQuick/private/qquickwindow_p.h>
24#include <QtQuick/private/qquickitem_p.h>
25#include <QtQuick/private/qsgsoftwarerenderer_p.h>
26#include <QtCore/private/qobject_p.h>
27
28#include <QtQuick/private/qquickwindow_p.h>
29#include <rhi/qrhi.h>
30
31QT_BEGIN_NAMESPACE
32
33/*!
34 \class QQuickRenderControl
35
36 \brief The QQuickRenderControl class provides a mechanism for rendering the Qt
37 Quick scenegraph onto an offscreen render target in a fully
38 application-controlled manner.
39
40 \since 5.4
41
42 QQuickWindow and QQuickView and their associated internal render loops render
43 the Qt Quick scene onto a native window. In some cases, for example when
44 integrating with 3rd party OpenGL, Vulkan, Metal, or Direct 3D renderers, it
45 can be useful to get the scene into a texture that can then be used in
46 arbitrary ways by the external rendering engine. Such a mechanism is also
47 essential when integrating with a VR framework. QQuickRenderControl makes this
48 possible in a hardware accelerated manner, unlike the performance-wise limited
49 alternative of using QQuickWindow::grabWindow()
50
51 When using a QQuickRenderControl, the QQuickWindow must not be
52 \l{QWindow::show()}{shown} (it will not be visible on-screen) and there will
53 not be an underlying native window for it. Instead, the QQuickWindow instance
54 is associated with the render control object, using the overload of the
55 QQuickWindow constructor, and a texture or image object specified via
56 QQuickWindow::setRenderTarget(). The QQuickWindow object is still essential,
57 because it represents the Qt Quick scene and provides the bulk of the scene
58 management and event delivery mechanisms. It does not however act as a real
59 on-screen window from the windowing system's perspective.
60
61 Management of the graphics devices, contexts, image and texture objects is up
62 to the application. The device or context that will be used by Qt Quick must
63 be created before calling initialize(). The creation of the texture object
64 can be deferred, see below. Qt 5.4 introduces the ability for QOpenGLContext
65 to adopt existing native contexts. Together with QQuickRenderControl this
66 makes it possible to create a QOpenGLContext that shares with an external
67 rendering engine's existing context. This new QOpenGLContext can then be used
68 to render the Qt Quick scene into a texture that is accessible by the other
69 engine's context too. For Vulkan, Metal, and Direct 3D there are no
70 Qt-provided wrappers for device objects, so existing ones can be passed as-is
71 via QQuickWindow::setGraphicsDevice().
72
73 Loading and instantiation of the QML components happen by using a
74 QQmlEngine. Once the root object is created, it will need to be parented to
75 the QQuickWindow's contentItem().
76
77 Applications will usually have to connect to 4 important signals:
78
79 \list
80
81 \li QQuickWindow::sceneGraphInitialized() Emitted at some point after calling
82 QQuickRenderControl::initialize(). Upon this signal, the application is
83 expected to create its framebuffer object and associate it with the
84 QQuickWindow.
85
86 \li QQuickWindow::sceneGraphInvalidated() When the scenegraph resources are
87 released, the framebuffer object can be destroyed too.
88
89 \li QQuickRenderControl::renderRequested() Indicates that the scene has to be
90 rendered by calling render(). After making the context current, applications
91 are expected to call render().
92
93 \li QQuickRenderControl::sceneChanged() Indicates that the scene has changed
94 meaning that, before rendering, polishing and synchronizing is also necessary.
95
96 \endlist
97
98 To send events, for example mouse or keyboard events, to the scene, use
99 QCoreApplication::sendEvent() with the QQuickWindow instance as the receiver.
100
101 For key events it may be also necessary to set the focus manually on the
102 desired item. In practice this involves calling
103 \l{QQuickItem::forceActiveFocus()}{forceActiveFocus()} on the desired item,
104 for example the scene's root item, once it is associated with the scene (the
105 QQuickWindow).
106
107 \note In general QQuickRenderControl is supported in combination with all Qt
108 Quick backends. However, some functionality, in particular grab(), may not be
109 available in all cases.
110
111 \inmodule QtQuick
112*/
113
114QSGContext *QQuickRenderControlPrivate::sg = nullptr;
115
116QQuickRenderControlPrivate::QQuickRenderControlPrivate(QQuickRenderControl *renderControl)
117 : q(renderControl),
118 initialized(false),
119 window(nullptr),
120 rhi(nullptr),
121 ownRhi(true),
122 cb(nullptr),
123 offscreenSurface(nullptr),
124 sampleCount(1),
125 frameStatus(NotRecordingFrame)
126{
127 if (!sg) {
128 qAddPostRoutine(cleanup);
129 sg = QSGContext::createDefaultContext();
130 }
131 rc = sg->createRenderContext();
132}
133
134void QQuickRenderControlPrivate::cleanup()
135{
136 delete sg;
137 sg = nullptr;
138}
139
140/*!
141 Constructs a QQuickRenderControl object, with parent
142 object \a parent.
143*/
144QQuickRenderControl::QQuickRenderControl(QObject *parent)
145 : QObject(*(new QQuickRenderControlPrivate(this)), parent)
146{
147}
148
149/*!
150 \internal
151*/
152QQuickRenderControl::QQuickRenderControl(QQuickRenderControlPrivate &dd, QObject * parent)
153 : QObject(dd, parent)
154{
155}
156
157/*!
158 Destroys the instance. Releases all scenegraph resources.
159
160 \sa invalidate()
161 */
162QQuickRenderControl::~QQuickRenderControl()
163{
164 Q_D(QQuickRenderControl);
165
166 invalidate();
167
168 QQuickGraphicsConfiguration config;
169 if (d->window) {
170 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(c: d->window);
171 wd->renderControl = nullptr;
172 config = wd->graphicsConfig;
173 }
174
175 // It is likely that the cleanup in windowDestroyed() is not called since
176 // the standard pattern is to destroy the rendercontrol before the QQuickWindow.
177 // Do it here.
178 d->windowDestroyed();
179
180 delete d->rc;
181
182 // Only call rhi related cleanup when we actually got to initialize() and
183 // managed to get a QRhi. The software backend for instance would mean
184 // using the rendercontrol without ever calling initialize() - it is then
185 // important to completely skip calling any QSGRhiSupport functions.
186 if (d->rhi)
187 d->resetRhi(config);
188}
189
190void QQuickRenderControlPrivate::windowDestroyed()
191{
192 if (window) {
193 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: window);
194 cd->cleanupNodesOnShutdown();
195
196 rc->invalidate();
197
198 QQuickWindowPrivate::get(c: window)->animationController.reset();
199
200#if QT_CONFIG(quick_shadereffect)
201 QSGRhiShaderEffectNode::resetMaterialTypeCache(materialTypeCacheKey: window);
202#endif
203
204 window = nullptr;
205 }
206}
207
208/*!
209 Prepares rendering the Qt Quick scene outside the GUI thread.
210
211 \a targetThread specifies the thread on which synchronization and
212 rendering will happen. There is no need to call this function in a
213 single threaded scenario.
214 */
215void QQuickRenderControl::prepareThread(QThread *targetThread)
216{
217 Q_D(QQuickRenderControl);
218 d->rc->moveToThread(thread: targetThread);
219 QQuickWindowPrivate::get(c: d->window)->animationController->moveToThread(thread: targetThread);
220}
221
222/*!
223 Sets the number of samples to use for multisampling. When \a sampleCount is
224 0 or 1, multisampling is disabled.
225
226 \note This function is always used in combination with a multisample render
227 target, which means \a sampleCount must match the sample count passed to
228 QQuickRenderTarget::fromNativeTexture(), which in turn must match the
229 sample count of the native texture.
230
231 \since 6.0
232
233 \sa initialize(), QQuickRenderTarget
234 */
235void QQuickRenderControl::setSamples(int sampleCount)
236{
237 Q_D(QQuickRenderControl);
238 d->sampleCount = qMax(a: 1, b: sampleCount);
239}
240
241/*!
242 \return the current sample count. 1 or 0 means no multisampling.
243
244 \since 6.0
245 */
246int QQuickRenderControl::samples() const
247{
248 Q_D(const QQuickRenderControl);
249 return d->sampleCount;
250}
251
252/*!
253 Initializes the scene graph resources. When using a graphics API, such as
254 Vulkan, Metal, OpenGL, or Direct3D, for Qt Quick rendering,
255 QQuickRenderControl will set up an appropriate rendering engine when this
256 function is called. This rendering infrastructure exists as long as the
257 QQuickRenderControl exists.
258
259 To control what graphics API Qt Quick uses, call
260 QQuickWindow::setGraphicsApi() with one of the
261 QSGRendererInterface:GraphicsApi constants. That must be done before
262 calling this function.
263
264 To prevent the scenegraph from creating its own device and context objects,
265 specify an appropriate QQuickGraphicsDevice, wrapping existing graphics
266 objects, by calling QQuickWindow::setGraphicsDevice().
267
268 To configure which device extensions to enable (for example, for Vulkan),
269 call QQuickWindow::setGraphicsConfiguration() before this function.
270
271 \note When using Vulkan, QQuickRenderControl does not create a QVulkanInstance
272 automatically. Rather, it is the application's responsibility to create a
273 suitable QVulkanInstance and \l{QWindow::setVulkanInstance()}{associate it} with
274 the QQuickWindow. Before initializing the QVulkanInstance, it is strongly
275 encouraged to query the list of Qt Quick's desired instance extensions by calling
276 the static function QQuickGraphicsConfiguration::preferredInstanceExtensions()
277 and to pass the returned list to QVulkanInstance::setExtensions().
278
279 Returns \c true on success, \c false otherwise.
280
281 \note This function does not need to be, and must not be, called when using
282 the \c software adaptation of Qt Quick.
283
284 With the default Qt Quick adaptation this function creates a new \l QRhi
285 object, similarly to what would happen with an on-screen QQuickWindow when
286 QQuickRenderControl was not used. To make this new QRhi object adopt some
287 existing device or context resource (e.g. use an existing QOpenGLContext
288 instead of creating a new one), use QQuickWindow::setGraphicsDevice() as
289 mentioned above. When the application wants to make the Qt Quick rendering
290 use an already existing \l QRhi object, that is possible as well via
291 \l QQuickGraphicsDevice::fromRhi(). When such a QQuickGraphicsDevice,
292 referencing an already existing QRhi, is set, there will be no new,
293 dedicated \l QRhi object created in initialize().
294
295 \since 6.0
296
297 \sa QQuickRenderTarget, QQuickGraphicsDevice, QQuickGraphicsConfiguration::preferredInstanceExtensions()
298 */
299bool QQuickRenderControl::initialize()
300{
301 Q_D(QQuickRenderControl);
302 if (!d->window) {
303 qWarning(msg: "QQuickRenderControl::initialize called with no associated window");
304 return false;
305 }
306
307 if (!d->initRhi())
308 return false;
309
310 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(c: d->window);
311 wd->rhi = d->rhi;
312
313 QSGDefaultRenderContext *renderContext = qobject_cast<QSGDefaultRenderContext *>(object: d->rc);
314 if (renderContext) {
315 QSGDefaultRenderContext::InitParams params;
316 params.rhi = d->rhi;
317 params.sampleCount = d->sampleCount;
318 params.initialSurfacePixelSize = d->window->size() * d->window->effectiveDevicePixelRatio();
319 params.maybeSurface = d->window;
320 renderContext->initialize(params: &params);
321 d->initialized = true;
322 } else {
323 qWarning(msg: "QRhi is only compatible with default adaptation");
324 return false;
325 }
326 return true;
327}
328
329/*!
330 This function should be called as late as possible before
331 sync(). In a threaded scenario, rendering can happen in parallel
332 with this function.
333 */
334void QQuickRenderControl::polishItems()
335{
336 Q_D(QQuickRenderControl);
337 if (!d->window)
338 return;
339
340 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window);
341 cd->deliveryAgentPrivate()->flushFrameSynchronousEvents(win: d->window);
342 if (!d->window)
343 return;
344 cd->polishItems();
345 emit d->window->afterAnimating();
346}
347
348/*!
349 This function is used to synchronize the QML scene with the rendering scene
350 graph.
351
352 If a dedicated render thread is used, the GUI thread should be blocked for the
353 duration of this call.
354
355 \return \e true if the synchronization changed the scene graph.
356 */
357bool QQuickRenderControl::sync()
358{
359 Q_D(QQuickRenderControl);
360 if (!d->window)
361 return false;
362
363 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window);
364 // we may not have a d->rhi (software backend) hence the check is important
365 if (d->rhi) {
366 if (!d->rhi->isRecordingFrame()) {
367 qWarning(msg: "QQuickRenderControl can only sync when beginFrame() has been called");
368 return false;
369 }
370 if (!d->cb) {
371 qWarning(msg: "QQuickRenderControl cannot be used with QRhi when no QRhiCommandBuffer is provided "
372 "(perhaps beginFrame() was not called or it was unsuccessful?)");
373 return false;
374 }
375 cd->setCustomCommandBuffer(d->cb);
376 }
377
378 cd->syncSceneGraph();
379 d->rc->endSync();
380
381 return true;
382}
383
384/*!
385 Stop rendering and release resources.
386
387 This is the equivalent of the cleanup operations that happen with a
388 real QQuickWindow when the window becomes hidden.
389
390 This function is called from the destructor. Therefore there will
391 typically be no need to call it directly.
392
393 Once invalidate() has been called, it is possible to reuse the
394 QQuickRenderControl instance by calling initialize() again.
395
396 \note This function does not take
397 QQuickWindow::persistentSceneGraph() or
398 QQuickWindow::persistentGraphics() into account. This means
399 that context-specific resources are always released.
400 */
401void QQuickRenderControl::invalidate()
402{
403 Q_D(QQuickRenderControl);
404 if (!d->window)
405 return;
406
407 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window);
408 cd->fireAboutToStop();
409 cd->cleanupNodesOnShutdown();
410
411 if (!d->initialized)
412 return;
413
414 // We must invalidate since the context can potentially be destroyed by the
415 // application right after returning from this function. Invalidating is
416 // also essential to allow a subsequent initialize() to succeed.
417 d->rc->invalidate();
418
419 d->frameStatus = QQuickRenderControlPrivate::NotRecordingFrame;
420 d->initialized = false;
421}
422
423/*!
424 Renders the scenegraph using the current context.
425 */
426void QQuickRenderControl::render()
427{
428 Q_D(QQuickRenderControl);
429 if (!d->window)
430 return;
431
432 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window);
433 // we may not have a d->rhi (software backend) hence the check is important
434 if (d->rhi) {
435 if (!d->rhi->isRecordingFrame()) {
436 qWarning(msg: "QQuickRenderControl can only render when beginFrame() has been called");
437 return;
438 }
439 if (!d->cb) {
440 qWarning(msg: "QQuickRenderControl cannot be used with QRhi when no QRhiCommandBuffer is provided");
441 return;
442 }
443 cd->setCustomCommandBuffer(d->cb);
444 }
445
446 cd->renderSceneGraph();
447}
448
449/*!
450 \fn void QQuickRenderControl::renderRequested()
451
452 This signal is emitted when the scene graph needs to be rendered. It is not necessary to call sync().
453
454 \note Avoid triggering rendering directly when this signal is
455 emitted. Instead, prefer deferring it by using a timer for example. This
456 will lead to better performance.
457*/
458
459/*!
460 \fn void QQuickRenderControl::sceneChanged()
461
462 This signal is emitted when the scene graph is updated, meaning that
463 polishItems() and sync() needs to be called. If sync() returns
464 true, then render() needs to be called.
465
466 \note Avoid triggering polishing, synchronization and rendering directly
467 when this signal is emitted. Instead, prefer deferring it by using a timer
468 for example. This will lead to better performance.
469*/
470
471QImage QQuickRenderControlPrivate::grab()
472{
473 if (!window)
474 return QImage();
475
476 QImage grabContent;
477
478 if (rhi) {
479
480 // As documented by QQuickWindow::grabWindow(): Nothing to do here, we
481 // do not support "grabbing" with an application-provided render target
482 // in Qt 6. (with the exception of the software backend because that
483 // does not support custom render targets, so the grab implementation
484 // here is still valuable)
485
486#if QT_CONFIG(thread)
487 } else if (window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) {
488 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: window);
489 cd->polishItems();
490 cd->syncSceneGraph();
491 QSGSoftwareRenderer *softwareRenderer = static_cast<QSGSoftwareRenderer *>(cd->renderer);
492 if (softwareRenderer) {
493 const qreal dpr = window->effectiveDevicePixelRatio();
494 const QSize imageSize = window->size() * dpr;
495 grabContent = QImage(imageSize, QImage::Format_ARGB32_Premultiplied);
496 grabContent.setDevicePixelRatio(dpr);
497 QPaintDevice *prevDev = softwareRenderer->currentPaintDevice();
498 softwareRenderer->setCurrentPaintDevice(&grabContent);
499 softwareRenderer->markDirty();
500 rc->endSync();
501 q->render();
502 softwareRenderer->setCurrentPaintDevice(prevDev);
503 }
504#endif
505 } else {
506 qWarning(msg: "QQuickRenderControl: grabs are not supported with the current Qt Quick backend");
507 }
508
509 return grabContent;
510}
511
512void QQuickRenderControlPrivate::update()
513{
514 Q_Q(QQuickRenderControl);
515 emit q->renderRequested();
516}
517
518void QQuickRenderControlPrivate::maybeUpdate()
519{
520 Q_Q(QQuickRenderControl);
521 emit q->sceneChanged();
522}
523
524/*!
525 \fn QWindow *QQuickRenderControl::renderWindow(QPoint *offset)
526
527 Reimplemented in subclasses to return the real window this render control
528 is rendering into.
529
530 If \a offset is non-null, it is set to the offset of the control
531 inside the window.
532
533 \note While not mandatory, reimplementing this function becomes essential for
534 supporting multiple screens with different device pixel ratios and properly positioning
535 popup windows opened from QML. Therefore providing it in subclasses is highly
536 recommended.
537*/
538
539/*!
540 Returns the real window that \a win is being rendered to, if any.
541
542 If \a offset is non-null, it is set to the offset of the rendering
543 inside its window.
544
545 */
546QWindow *QQuickRenderControl::renderWindowFor(QQuickWindow *win, QPoint *offset)
547{
548 if (!win)
549 return nullptr;
550 QQuickRenderControl *rc = QQuickWindowPrivate::get(c: win)->renderControl;
551 if (rc)
552 return rc->renderWindow(offset);
553 return nullptr;
554}
555
556bool QQuickRenderControlPrivate::isRenderWindowFor(QQuickWindow *quickWin, const QWindow *renderWin)
557{
558 QQuickRenderControl *rc = QQuickWindowPrivate::get(c: quickWin)->renderControl;
559 if (rc)
560 return QQuickRenderControlPrivate::get(renderControl: rc)->isRenderWindow(w: renderWin);
561 return false;
562}
563
564bool QQuickRenderControlPrivate::isRenderWindow(const QWindow *w)
565{
566 Q_Q(QQuickRenderControl);
567
568 if (window && w)
569 return q->renderWindowFor(win: window, offset: nullptr) == w;
570
571 return false;
572}
573
574/*!
575 \return the QQuickWindow this QQuickRenderControl is associated with.
576
577 \note A QQuickRenderControl gets associated with a QQuickWindow when
578 constructing the QQuickWindow. The return value from this function is null
579 before that point.
580
581 \since 6.0
582 */
583QQuickWindow *QQuickRenderControl::window() const
584{
585 Q_D(const QQuickRenderControl);
586 return d->window;
587}
588
589/*!
590 \return the QRhi this QQuickRenderControl is associated with.
591
592 \note The QRhi exists only when initialize() has successfully completed.
593 Before that the return value is null.
594
595 \note This function is not applicable and returns null when using the
596 \c software adaptation of Qt Quick.
597
598 \since 6.6
599
600 \sa commandBuffer(), beginFrame(), endFrame()
601 */
602QRhi *QQuickRenderControl::rhi() const
603{
604 Q_D(const QQuickRenderControl);
605 return d->rhi;
606}
607
608/*!
609 \return the current command buffer.
610
611 Once beginFrame() is called, a QRhiCommandBuffer is set up automatically.
612 That is the command buffer Qt Quick scenegraph uses, but in some cases
613 applications may also want to query it, for example to issue resource
614 updates (for example, a texture readback).
615
616 The command buffer is only valid for use between beginFrame() and
617 endFrame().
618
619 \note This function is not applicable and returns null when using the
620 \c software adaptation of Qt Quick.
621
622 \since 6.6
623
624 \sa rhi(), beginFrame(), endFrame()
625 */
626QRhiCommandBuffer *QQuickRenderControl::commandBuffer() const
627{
628 Q_D(const QQuickRenderControl);
629 return d->cb;
630}
631
632/*!
633 Specifies the start of a graphics frame. Calls to sync() or render() must
634 be enclosed by calls to beginFrame() and endFrame().
635
636 Unlike the earlier OpenGL-only world of Qt 5, rendering with other graphics
637 APIs requires more well-defined points of starting and ending a frame. When
638 manually driving the rendering loop via QQuickRenderControl, it now falls
639 to the user of QQuickRenderControl to specify these points.
640
641 A typical update step, including initialization of rendering into an
642 existing texture, could look like the following. The example snippet
643 assumes Direct3D 11 but the same concepts apply other graphics APIs as
644 well.
645
646 \badcode
647 if (!m_quickInitialized) {
648 m_quickWindow->setGraphicsDevice(QQuickGraphicsDevice::fromDeviceAndContext(m_engine->device(), m_engine->context()));
649
650 if (!m_renderControl->initialize())
651 qWarning("Failed to initialize redirected Qt Quick rendering");
652
653 m_quickWindow->setRenderTarget(QQuickRenderTarget::fromNativeTexture({ quint64(m_res.texture), 0 },
654 QSize(QML_WIDTH, QML_HEIGHT),
655 SAMPLE_COUNT));
656
657 m_quickInitialized = true;
658 }
659
660 m_renderControl->polishItems();
661
662 m_renderControl->beginFrame();
663 m_renderControl->sync();
664 m_renderControl->render();
665 m_renderControl->endFrame(); // Qt Quick's rendering commands are submitted to the device context here
666 \endcode
667
668 \note This function does not need to be, and must not be, called when using
669 the \c software adaptation of Qt Quick.
670
671 \note Internally beginFrame() and endFrame() invoke
672 \l{QRhi::}beginOffscreenFrame() and \l{QRhi::}endOffscreenFrame(),
673 respectively. This implies that there must not be a frame (neither
674 offscreen, nor swapchain-based) being recorded on the QRhi when
675 this function is called.
676
677 \since 6.0
678
679 \sa endFrame(), initialize(), sync(), render(), QQuickGraphicsDevice, QQuickRenderTarget
680 */
681void QQuickRenderControl::beginFrame()
682{
683 Q_D(QQuickRenderControl);
684 if (!d->rhi) {
685 qWarning(msg: "QQuickRenderControl: No QRhi in beginFrame()");
686 return;
687 }
688 if (d->frameStatus == QQuickRenderControlPrivate::RecordingFrame) {
689 qWarning(msg: "QQuickRenderControl: beginFrame() must be followed by a call to endFrame() before calling beginFrame() again");
690 return;
691 }
692 if (d->rhi->isRecordingFrame()) {
693 qWarning(msg: "QQuickRenderControl: Attempted to beginFrame() while the QRhi is already recording a frame");
694 return;
695 }
696
697 emit d->window->beforeFrameBegin();
698
699 QRhi::FrameOpResult result = d->rhi->beginOffscreenFrame(cb: &d->cb);
700
701 switch (result) {
702 case QRhi::FrameOpSuccess:
703 case QRhi::FrameOpSwapChainOutOfDate:
704 d->frameStatus = QQuickRenderControlPrivate::RecordingFrame;
705 break;
706 case QRhi::FrameOpError:
707 d->frameStatus = QQuickRenderControlPrivate::ErrorInBeginFrame;
708 break;
709 case QRhi::FrameOpDeviceLost:
710 d->frameStatus = QQuickRenderControlPrivate::DeviceLostInBeginFrame;
711 break;
712 default:
713 d->frameStatus = QQuickRenderControlPrivate::NotRecordingFrame;
714 break;
715 }
716}
717
718/*!
719 Specifies the end of a graphics frame. Calls to sync() or render() must be
720 enclosed by calls to beginFrame() and endFrame().
721
722 When this function is called, any graphics commands enqueued by the
723 scenegraph are submitted to the context or command queue, whichever is
724 applicable.
725
726 \note This function does not need to be, and must not be, called when using
727 the \c software adaptation of Qt Quick.
728
729 \since 6.0
730
731 \sa beginFrame(), initialize(), sync(), render(), QQuickGraphicsDevice, QQuickRenderTarget
732 */
733void QQuickRenderControl::endFrame()
734{
735 Q_D(QQuickRenderControl);
736 if (!d->rhi) {
737 qWarning(msg: "QQuickRenderControl: No QRhi in endFrame()");
738 return;
739 }
740 if (d->frameStatus != QQuickRenderControlPrivate::RecordingFrame) {
741 qWarning(msg: "QQuickRenderControl: endFrame() must only be called after a successful beginFrame()");
742 return;
743 }
744 if (!d->rhi->isRecordingFrame()) {
745 qWarning(msg: "QQuickRenderControl: Attempted to endFrame() while the QRhi is not recording a frame");
746 return;
747 }
748
749 d->rhi->endOffscreenFrame();
750 d->cb = nullptr;
751 d->frameStatus = QQuickRenderControlPrivate::NotRecordingFrame;
752
753 emit d->window->afterFrameEnd();
754}
755
756bool QQuickRenderControlPrivate::initRhi()
757{
758 // initialize() - invalidate() - initialize() uses the QRhi the first
759 // initialize() created, so if already exists, we are done. Does not apply
760 // when wrapping an externally created QRhi, because we may be associated
761 // with a new one now.
762 if (rhi && ownRhi)
763 return true;
764
765 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
766
767 // sanity check for Vulkan
768#if QT_CONFIG(vulkan)
769 if (rhiSupport->rhiBackend() == QRhi::Vulkan && !window->vulkanInstance()) {
770 qWarning(msg: "QQuickRenderControl: No QVulkanInstance set for QQuickWindow, cannot initialize");
771 return false;
772 }
773#endif
774
775 // for OpenGL
776 if (!offscreenSurface)
777 offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window);
778
779 QSGRhiSupport::RhiCreateResult result = rhiSupport->createRhi(window, offscreenSurface);
780 if (!result.rhi) {
781 qWarning(msg: "QQuickRenderControl: Failed to initialize QRhi");
782 return false;
783 }
784
785 rhi = result.rhi;
786 ownRhi = result.own;
787
788 return true;
789}
790
791void QQuickRenderControlPrivate::resetRhi(const QQuickGraphicsConfiguration &config)
792{
793 if (ownRhi)
794 QSGRhiSupport::instance()->destroyRhi(rhi, config);
795
796 rhi = nullptr;
797
798 delete offscreenSurface;
799 offscreenSurface = nullptr;
800}
801
802QT_END_NAMESPACE
803
804#include "moc_qquickrendercontrol.cpp"
805

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