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