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 \inmodule QtQuick
108*/
109
110QSGContext *QQuickRenderControlPrivate::sg = nullptr;
111
112QQuickRenderControlPrivate::QQuickRenderControlPrivate(QQuickRenderControl *renderControl)
113 : q(renderControl),
114 initialized(false),
115 window(nullptr),
116 rhi(nullptr),
117 ownRhi(true),
118 cb(nullptr),
119 offscreenSurface(nullptr),
120 sampleCount(1),
121 frameStatus(NotRecordingFrame)
122{
123 if (!sg) {
124 qAddPostRoutine(cleanup);
125 sg = QSGContext::createDefaultContext();
126 }
127 rc = sg->createRenderContext();
128}
129
130void QQuickRenderControlPrivate::cleanup()
131{
132 delete sg;
133 sg = nullptr;
134}
135
136/*!
137 Constructs a QQuickRenderControl object, with parent
138 object \a parent.
139*/
140QQuickRenderControl::QQuickRenderControl(QObject *parent)
141 : QObject(*(new QQuickRenderControlPrivate(this)), parent)
142{
143}
144
145/*!
146 \internal
147*/
148QQuickRenderControl::QQuickRenderControl(QQuickRenderControlPrivate &dd, QObject * parent)
149 : QObject(dd, parent)
150{
151}
152
153/*!
154 Destroys the instance. Releases all scenegraph resources.
155
156 \sa invalidate()
157 */
158QQuickRenderControl::~QQuickRenderControl()
159{
160 Q_D(QQuickRenderControl);
161
162 invalidate();
163
164 QQuickGraphicsConfiguration config;
165 if (d->window) {
166 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(c: d->window);
167 wd->renderControl = nullptr;
168 config = wd->graphicsConfig;
169 }
170
171 // It is likely that the cleanup in windowDestroyed() is not called since
172 // the standard pattern is to destroy the rendercontrol before the QQuickWindow.
173 // Do it here.
174 d->windowDestroyed();
175
176 delete d->rc;
177
178 // Only call rhi related cleanup when we actually got to initialize() and
179 // managed to get a QRhi. The software backend for instance would mean
180 // using the rendercontrol without ever calling initialize() - it is then
181 // important to completely skip calling any QSGRhiSupport functions.
182 if (d->rhi)
183 d->resetRhi(config);
184}
185
186void QQuickRenderControlPrivate::windowDestroyed()
187{
188 if (window) {
189 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: window);
190 cd->cleanupNodesOnShutdown();
191
192 rc->invalidate();
193
194 QQuickWindowPrivate::get(c: window)->animationController.reset();
195
196#if QT_CONFIG(quick_shadereffect)
197 QSGRhiShaderEffectNode::resetMaterialTypeCache(materialTypeCacheKey: window);
198#endif
199
200 window = nullptr;
201 }
202}
203
204/*!
205 Prepares rendering the Qt Quick scene outside the GUI thread.
206
207 \a targetThread specifies the thread on which synchronization and
208 rendering will happen. There is no need to call this function in a
209 single threaded scenario.
210 */
211void QQuickRenderControl::prepareThread(QThread *targetThread)
212{
213 Q_D(QQuickRenderControl);
214 d->rc->moveToThread(thread: targetThread);
215 QQuickWindowPrivate::get(c: d->window)->animationController->moveToThread(thread: targetThread);
216}
217
218/*!
219 Sets the number of samples to use for multisampling. When \a sampleCount is
220 0 or 1, multisampling is disabled.
221
222 \note This function is always used in combination with a multisample render
223 target, which means \a sampleCount must match the sample count passed to
224 QQuickRenderTarget::fromNativeTexture(), which in turn must match the
225 sample count of the native texture.
226
227 \since 6.0
228
229 \sa initialize(), QQuickRenderTarget
230 */
231void QQuickRenderControl::setSamples(int sampleCount)
232{
233 Q_D(QQuickRenderControl);
234 d->sampleCount = qMax(a: 1, b: sampleCount);
235}
236
237/*!
238 \return the current sample count. 1 or 0 means no multisampling.
239
240 \since 6.0
241 */
242int QQuickRenderControl::samples() const
243{
244 Q_D(const QQuickRenderControl);
245 return d->sampleCount;
246}
247
248/*!
249 Initializes the scene graph resources. When using a graphics API, such as
250 Vulkan, Metal, OpenGL, or Direct3D, for Qt Quick rendering,
251 QQuickRenderControl will set up an appropriate rendering engine when this
252 function is called. This rendering infrastructure exists as long as the
253 QQuickRenderControl exists.
254
255 To control what graphics API Qt Quick uses, call
256 QQuickWindow::setGraphicsApi() with one of the
257 QSGRendererInterface:GraphicsApi constants. That must be done before
258 calling this function.
259
260 To prevent the scenegraph from creating its own device and context objects,
261 specify an appropriate QQuickGraphicsDevice, wrapping existing graphics
262 objects, by calling QQuickWindow::setGraphicsDevice().
263
264 To configure which device extensions to enable (for example, for Vulkan),
265 call QQuickWindow::setGraphicsConfiguration() before this function.
266
267 \note When using Vulkan, QQuickRenderControl does not create a QVulkanInstance
268 automatically. Rather, it is the application's responsibility to create a
269 suitable QVulkanInstance and \l{QWindow::setVulkanInstance()}{associate it} with
270 the QQuickWindow. Before initializing the QVulkanInstance, it is strongly
271 encouraged to query the list of Qt Quick's desired instance extensions by calling
272 the static function QQuickGraphicsConfiguration::preferredInstanceExtensions()
273 and to pass the returned list to QVulkanInstance::setExtensions().
274
275 Returns \c true on success, \c false otherwise.
276
277 \note This function does not need to be, and must not be, called when using
278 the \c software adaptation of Qt Quick.
279
280 With the default Qt Quick adaptation this function creates a new \l QRhi
281 object, similarly to what would happen with an on-screen QQuickWindow when
282 QQuickRenderControl was not used. To make this new QRhi object adopt some
283 existing device or context resource (e.g. use an existing QOpenGLContext
284 instead of creating a new one), use QQuickWindow::setGraphicsDevice() as
285 mentioned above. When the application wants to make the Qt Quick rendering
286 use an already existing \l QRhi object, that is possible as well via
287 \l QQuickGraphicsDevice::fromRhi(). When such a QQuickGraphicsDevice,
288 referencing an already existing QRhi, is set, there will be no new,
289 dedicated \l QRhi object created in initialize().
290
291 \since 6.0
292
293 \sa QQuickRenderTarget, QQuickGraphicsDevice, QQuickGraphicsConfiguration::preferredInstanceExtensions()
294 */
295bool QQuickRenderControl::initialize()
296{
297 Q_D(QQuickRenderControl);
298 if (!d->window) {
299 qWarning(msg: "QQuickRenderControl::initialize called with no associated window");
300 return false;
301 }
302
303 if (!d->initRhi())
304 return false;
305
306 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(c: d->window);
307 wd->rhi = d->rhi;
308
309 QSGDefaultRenderContext *renderContext = qobject_cast<QSGDefaultRenderContext *>(object: d->rc);
310 if (renderContext) {
311 QSGDefaultRenderContext::InitParams params;
312 params.rhi = d->rhi;
313 params.sampleCount = d->sampleCount;
314 params.initialSurfacePixelSize = d->window->size() * d->window->effectiveDevicePixelRatio();
315 params.maybeSurface = d->window;
316 renderContext->initialize(params: &params);
317 d->initialized = true;
318 } else {
319 qWarning(msg: "QRhi is only compatible with default adaptation");
320 return false;
321 }
322 return true;
323}
324
325/*!
326 This function should be called as late as possible before
327 sync(). In a threaded scenario, rendering can happen in parallel
328 with this function.
329 */
330void QQuickRenderControl::polishItems()
331{
332 Q_D(QQuickRenderControl);
333 if (!d->window)
334 return;
335
336 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window);
337 cd->deliveryAgentPrivate()->flushFrameSynchronousEvents(win: d->window);
338 if (!d->window)
339 return;
340 cd->polishItems();
341 emit d->window->afterAnimating();
342}
343
344/*!
345 This function is used to synchronize the QML scene with the rendering scene
346 graph.
347
348 If a dedicated render thread is used, the GUI thread should be blocked for the
349 duration of this call.
350
351 \return \e true if the synchronization changed the scene graph.
352 */
353bool QQuickRenderControl::sync()
354{
355 Q_D(QQuickRenderControl);
356 if (!d->window)
357 return false;
358
359 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window);
360 // we may not have a d->rhi (software backend) hence the check is important
361 if (d->rhi) {
362 if (!d->rhi->isRecordingFrame()) {
363 qWarning(msg: "QQuickRenderControl can only sync when beginFrame() has been called");
364 return false;
365 }
366 if (!d->cb) {
367 qWarning(msg: "QQuickRenderControl cannot be used with QRhi when no QRhiCommandBuffer is provided "
368 "(perhaps beginFrame() was not called or it was unsuccessful?)");
369 return false;
370 }
371 cd->setCustomCommandBuffer(d->cb);
372 }
373
374 cd->syncSceneGraph();
375 d->rc->endSync();
376
377 return true;
378}
379
380/*!
381 Stop rendering and release resources.
382
383 This is the equivalent of the cleanup operations that happen with a
384 real QQuickWindow when the window becomes hidden.
385
386 This function is called from the destructor. Therefore there will
387 typically be no need to call it directly.
388
389 Once invalidate() has been called, it is possible to reuse the
390 QQuickRenderControl instance by calling initialize() again.
391
392 \note This function does not take
393 QQuickWindow::persistentSceneGraph() or
394 QQuickWindow::persistentGraphics() into account. This means
395 that context-specific resources are always released.
396 */
397void QQuickRenderControl::invalidate()
398{
399 Q_D(QQuickRenderControl);
400 if (!d->window)
401 return;
402
403 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window);
404 cd->fireAboutToStop();
405 cd->cleanupNodesOnShutdown();
406
407 if (!d->initialized)
408 return;
409
410 // We must invalidate since the context can potentially be destroyed by the
411 // application right after returning from this function. Invalidating is
412 // also essential to allow a subsequent initialize() to succeed.
413 d->rc->invalidate();
414
415 d->frameStatus = QQuickRenderControlPrivate::NotRecordingFrame;
416 d->initialized = false;
417}
418
419/*!
420 Renders the scenegraph using the current context.
421 */
422void QQuickRenderControl::render()
423{
424 Q_D(QQuickRenderControl);
425 if (!d->window)
426 return;
427
428 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: d->window);
429 // we may not have a d->rhi (software backend) hence the check is important
430 if (d->rhi) {
431 if (!d->rhi->isRecordingFrame()) {
432 qWarning(msg: "QQuickRenderControl can only render when beginFrame() has been called");
433 return;
434 }
435 if (!d->cb) {
436 qWarning(msg: "QQuickRenderControl cannot be used with QRhi when no QRhiCommandBuffer is provided");
437 return;
438 }
439 cd->setCustomCommandBuffer(d->cb);
440 }
441
442 cd->renderSceneGraph();
443}
444
445/*!
446 \fn void QQuickRenderControl::renderRequested()
447
448 This signal is emitted when the scene graph needs to be rendered. It is not necessary to call sync().
449
450 \note Avoid triggering rendering directly when this signal is
451 emitted. Instead, prefer deferring it by using a timer for example. This
452 will lead to better performance.
453*/
454
455/*!
456 \fn void QQuickRenderControl::sceneChanged()
457
458 This signal is emitted when the scene graph is updated, meaning that
459 polishItems() and sync() needs to be called. If sync() returns
460 true, then render() needs to be called.
461
462 \note Avoid triggering polishing, synchronization and rendering directly
463 when this signal is emitted. Instead, prefer deferring it by using a timer
464 for example. This will lead to better performance.
465*/
466
467QImage QQuickRenderControlPrivate::grab()
468{
469 if (!window)
470 return QImage();
471
472 QImage grabContent;
473
474 if (rhi) {
475
476 // As documented by QQuickWindow::grabWindow(): Nothing to do here, we
477 // do not support "grabbing" with an application-provided render target
478 // in Qt 6. (with the exception of the software backend because that
479 // does not support custom render targets, so the grab implementation
480 // here is still valuable)
481
482#if QT_CONFIG(thread)
483 } else if (window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) {
484 QQuickWindowPrivate *cd = QQuickWindowPrivate::get(c: window);
485 cd->polishItems();
486 cd->syncSceneGraph();
487 QSGSoftwareRenderer *softwareRenderer = static_cast<QSGSoftwareRenderer *>(cd->renderer);
488 if (softwareRenderer) {
489 const qreal dpr = window->effectiveDevicePixelRatio();
490 const QSize imageSize = window->size() * dpr;
491 grabContent = QImage(imageSize, QImage::Format_ARGB32_Premultiplied);
492 grabContent.setDevicePixelRatio(dpr);
493 QPaintDevice *prevDev = softwareRenderer->currentPaintDevice();
494 softwareRenderer->setCurrentPaintDevice(&grabContent);
495 softwareRenderer->markDirty();
496 rc->endSync();
497 q->render();
498 softwareRenderer->setCurrentPaintDevice(prevDev);
499 }
500#endif
501 } else {
502 qWarning(msg: "QQuickRenderControl: grabs are not supported with the current Qt Quick backend");
503 }
504
505 return grabContent;
506}
507
508void QQuickRenderControlPrivate::update()
509{
510 Q_Q(QQuickRenderControl);
511 emit q->renderRequested();
512}
513
514void QQuickRenderControlPrivate::maybeUpdate()
515{
516 Q_Q(QQuickRenderControl);
517 emit q->sceneChanged();
518}
519
520/*!
521 \fn QWindow *QQuickRenderControl::renderWindow(QPoint *offset)
522
523 Reimplemented in subclasses to return the real window this render control
524 is rendering into.
525
526 If \a offset is non-null, it is set to the offset of the control
527 inside the window.
528
529 \note While not mandatory, reimplementing this function becomes essential for
530 supporting multiple screens with different device pixel ratios and properly positioning
531 popup windows opened from QML. Therefore providing it in subclasses is highly
532 recommended.
533*/
534
535/*!
536 Returns the real window that \a win is being rendered to, if any.
537
538 If \a offset is non-null, it is set to the offset of the rendering
539 inside its window.
540
541 */
542QWindow *QQuickRenderControl::renderWindowFor(QQuickWindow *win, QPoint *offset)
543{
544 if (!win)
545 return nullptr;
546 QQuickRenderControl *rc = QQuickWindowPrivate::get(c: win)->renderControl;
547 if (rc)
548 return rc->renderWindow(offset);
549 return nullptr;
550}
551
552bool QQuickRenderControlPrivate::isRenderWindowFor(QQuickWindow *quickWin, const QWindow *renderWin)
553{
554 QQuickRenderControl *rc = QQuickWindowPrivate::get(c: quickWin)->renderControl;
555 if (rc)
556 return QQuickRenderControlPrivate::get(renderControl: rc)->isRenderWindow(w: renderWin);
557 return false;
558}
559
560bool QQuickRenderControlPrivate::isRenderWindow(const QWindow *w)
561{
562 Q_Q(QQuickRenderControl);
563
564 if (window && w)
565 return q->renderWindowFor(win: window, offset: nullptr) == w;
566
567 return false;
568}
569
570/*!
571 \return the QQuickWindow this QQuickRenderControl is associated with.
572
573 \note A QQuickRenderControl gets associated with a QQuickWindow when
574 constructing the QQuickWindow. The return value from this function is null
575 before that point.
576
577 \since 6.0
578 */
579QQuickWindow *QQuickRenderControl::window() const
580{
581 Q_D(const QQuickRenderControl);
582 return d->window;
583}
584
585/*!
586 \return the QRhi this QQuickRenderControl is associated with.
587
588 \note The QRhi exists only when initialize() has successfully completed.
589 Before that the return value is null.
590
591 \note This function is not applicable and returns null when using the
592 \c software adaptation of Qt Quick.
593
594 \since 6.6
595
596 \sa commandBuffer(), beginFrame(), endFrame()
597 */
598QRhi *QQuickRenderControl::rhi() const
599{
600 Q_D(const QQuickRenderControl);
601 return d->rhi;
602}
603
604/*!
605 \return the current command buffer.
606
607 Once beginFrame() is called, a QRhiCommandBuffer is set up automatically.
608 That is the command buffer Qt Quick scenegraph uses, but in some cases
609 applications may also want to query it, for example to issue resource
610 updates (for example, a texture readback).
611
612 The returned command buffer reference should only be used between
613 beginFrame() and endFrame(). There are specific exceptions, for example
614 calling
615 \l{QRhiCommandBuffer::lastCompletedGpuTime()}{lastCompletedGpuTime()} on
616 the command buffer right after endFrame(), but before the next
617 beginFrame(), is valid.
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 \code
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 // do not null out d->cb; this allows calling lastCompletedGpuTime() for example
751
752 d->frameStatus = QQuickRenderControlPrivate::NotRecordingFrame;
753
754 emit d->window->afterFrameEnd();
755}
756
757bool QQuickRenderControlPrivate::initRhi()
758{
759 // initialize() - invalidate() - initialize() uses the QRhi the first
760 // initialize() created, so if already exists, we are done. Does not apply
761 // when wrapping an externally created QRhi, because we may be associated
762 // with a new one now.
763 if (rhi && ownRhi)
764 return true;
765
766 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
767
768 // sanity check for Vulkan
769#if QT_CONFIG(vulkan)
770 if (rhiSupport->rhiBackend() == QRhi::Vulkan && !window->vulkanInstance()) {
771 qWarning(msg: "QQuickRenderControl: No QVulkanInstance set for QQuickWindow, cannot initialize");
772 return false;
773 }
774#endif
775
776 // for OpenGL
777 if (!offscreenSurface)
778 offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window);
779
780 QSGRhiSupport::RhiCreateResult result = rhiSupport->createRhi(window, offscreenSurface);
781 if (!result.rhi) {
782 qWarning(msg: "QQuickRenderControl: Failed to initialize QRhi");
783 return false;
784 }
785
786 rhi = result.rhi;
787 ownRhi = result.own;
788
789 return true;
790}
791
792void QQuickRenderControlPrivate::resetRhi(const QQuickGraphicsConfiguration &config)
793{
794 if (ownRhi)
795 QSGRhiSupport::instance()->destroyRhi(rhi, config);
796
797 rhi = nullptr;
798
799 delete offscreenSurface;
800 offscreenSurface = nullptr;
801}
802
803QT_END_NAMESPACE
804
805#include "moc_qquickrendercontrol.cpp"
806

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