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
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
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(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(window);
194 cd->cleanupNodesOnShutdown();
195
196 rc->invalidate();
197
198 QQuickWindowPrivate::get(window)->animationController.reset();
199
200#if QT_CONFIG(quick_shadereffect)
201 QSGRhiShaderEffectNode::resetMaterialTypeCache(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(targetThread);
219 QQuickWindowPrivate::get(d->window)->animationController->moveToThread(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(1, 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("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(d->window);
311 wd->rhi = d->rhi;
312
313 QSGDefaultRenderContext *renderContext = qobject_cast<QSGDefaultRenderContext *>(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);
321 d->initialized = true;
322 } else {
323 qWarning("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(d->window);
341 cd->deliveryAgentPrivate()->flushFrameSynchronousEvents(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(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("QQuickRenderControl can only sync when beginFrame() has been called");
368 return false;
369 }
370 if (!d->cb) {
371 qWarning("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(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(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("QQuickRenderControl can only render when beginFrame() has been called");
437 return;
438 }
439 if (!d->cb) {
440 qWarning("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(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("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(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(quickWin)->renderControl;
559 if (rc)
560 return QQuickRenderControlPrivate::get(rc)->isRenderWindow(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(window, 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 returned command buffer reference should only be used between
617 beginFrame() and endFrame(). There are specific exceptions, for example
618 calling
619 \l{QRhiCommandBuffer::lastCompletedGpuTime()}{lastCompletedGpuTime()} on
620 the command buffer right after endFrame(), but before the next
621 beginFrame(), is valid.
622
623 \note This function is not applicable and returns null when using the
624 \c software adaptation of Qt Quick.
625
626 \since 6.6
627
628 \sa rhi(), beginFrame(), endFrame()
629 */
630QRhiCommandBuffer *QQuickRenderControl::commandBuffer() const
631{
632 Q_D(const QQuickRenderControl);
633 return d->cb;
634}
635
636/*!
637 Specifies the start of a graphics frame. Calls to sync() or render() must
638 be enclosed by calls to beginFrame() and endFrame().
639
640 Unlike the earlier OpenGL-only world of Qt 5, rendering with other graphics
641 APIs requires more well-defined points of starting and ending a frame. When
642 manually driving the rendering loop via QQuickRenderControl, it now falls
643 to the user of QQuickRenderControl to specify these points.
644
645 A typical update step, including initialization of rendering into an
646 existing texture, could look like the following. The example snippet
647 assumes Direct3D 11 but the same concepts apply other graphics APIs as
648 well.
649
650 \code
651 if (!m_quickInitialized) {
652 m_quickWindow->setGraphicsDevice(QQuickGraphicsDevice::fromDeviceAndContext(m_engine->device(), m_engine->context()));
653
654 if (!m_renderControl->initialize())
655 qWarning("Failed to initialize redirected Qt Quick rendering");
656
657 m_quickWindow->setRenderTarget(QQuickRenderTarget::fromNativeTexture({ quint64(m_res.texture), 0 },
658 QSize(QML_WIDTH, QML_HEIGHT),
659 SAMPLE_COUNT));
660
661 m_quickInitialized = true;
662 }
663
664 m_renderControl->polishItems();
665
666 m_renderControl->beginFrame();
667 m_renderControl->sync();
668 m_renderControl->render();
669 m_renderControl->endFrame(); // Qt Quick's rendering commands are submitted to the device context here
670 \endcode
671
672 \note This function does not need to be, and must not be, called when using
673 the \c software adaptation of Qt Quick.
674
675 \note Internally beginFrame() and endFrame() invoke
676 \l{QRhi::}{beginOffscreenFrame()} and \l{QRhi::}{endOffscreenFrame()},
677 respectively. This implies that there must not be a frame (neither
678 offscreen, nor swapchain-based) being recorded on the QRhi when
679 this function is called.
680
681 \since 6.0
682
683 \sa endFrame(), initialize(), sync(), render(), QQuickGraphicsDevice, QQuickRenderTarget
684 */
685void QQuickRenderControl::beginFrame()
686{
687 Q_D(QQuickRenderControl);
688 if (!d->rhi) {
689 qWarning("QQuickRenderControl: No QRhi in beginFrame()");
690 return;
691 }
692 if (d->frameStatus == QQuickRenderControlPrivate::RecordingFrame) {
693 qWarning("QQuickRenderControl: beginFrame() must be followed by a call to endFrame() before calling beginFrame() again");
694 return;
695 }
696 if (d->rhi->isRecordingFrame()) {
697 qWarning("QQuickRenderControl: Attempted to beginFrame() while the QRhi is already recording a frame");
698 return;
699 }
700
701 emit d->window->beforeFrameBegin();
702
703 QRhi::FrameOpResult result = d->rhi->beginOffscreenFrame(&d->cb);
704
705 switch (result) {
706 case QRhi::FrameOpSuccess:
707 case QRhi::FrameOpSwapChainOutOfDate:
708 d->frameStatus = QQuickRenderControlPrivate::RecordingFrame;
709 break;
710 case QRhi::FrameOpError:
711 d->frameStatus = QQuickRenderControlPrivate::ErrorInBeginFrame;
712 break;
713 case QRhi::FrameOpDeviceLost:
714 d->frameStatus = QQuickRenderControlPrivate::DeviceLostInBeginFrame;
715 break;
716 default:
717 d->frameStatus = QQuickRenderControlPrivate::NotRecordingFrame;
718 break;
719 }
720}
721
722/*!
723 Specifies the end of a graphics frame. Calls to sync() or render() must be
724 enclosed by calls to beginFrame() and endFrame().
725
726 When this function is called, any graphics commands enqueued by the
727 scenegraph are submitted to the context or command queue, whichever is
728 applicable.
729
730 \note This function does not need to be, and must not be, called when using
731 the \c software adaptation of Qt Quick.
732
733 \since 6.0
734
735 \sa beginFrame(), initialize(), sync(), render(), QQuickGraphicsDevice, QQuickRenderTarget
736 */
737void QQuickRenderControl::endFrame()
738{
739 Q_D(QQuickRenderControl);
740 if (!d->rhi) {
741 qWarning("QQuickRenderControl: No QRhi in endFrame()");
742 return;
743 }
744 if (d->frameStatus != QQuickRenderControlPrivate::RecordingFrame) {
745 qWarning("QQuickRenderControl: endFrame() must only be called after a successful beginFrame()");
746 return;
747 }
748 if (!d->rhi->isRecordingFrame()) {
749 qWarning("QQuickRenderControl: Attempted to endFrame() while the QRhi is not recording a frame");
750 return;
751 }
752
753 d->rhi->endOffscreenFrame();
754 // do not null out d->cb; this allows calling lastCompletedGpuTime() for example
755
756 d->frameStatus = QQuickRenderControlPrivate::NotRecordingFrame;
757
758 emit d->window->afterFrameEnd();
759}
760
761bool QQuickRenderControlPrivate::initRhi()
762{
763 // initialize() - invalidate() - initialize() uses the QRhi the first
764 // initialize() created, so if already exists, we are done. Does not apply
765 // when wrapping an externally created QRhi, because we may be associated
766 // with a new one now.
767 if (rhi && ownRhi)
768 return true;
769
770 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
771
772 // sanity check for Vulkan
773#if QT_CONFIG(vulkan)
774 if (rhiSupport->rhiBackend() == QRhi::Vulkan && !window->vulkanInstance()) {
775 qWarning("QQuickRenderControl: No QVulkanInstance set for QQuickWindow, cannot initialize");
776 return false;
777 }
778#endif
779
780 // for OpenGL
781 if (!offscreenSurface)
782 offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window);
783
784 QSGRhiSupport::RhiCreateResult result = rhiSupport->createRhi(window, offscreenSurface);
785 if (!result.rhi) {
786 qWarning("QQuickRenderControl: Failed to initialize QRhi");
787 return false;
788 }
789
790 rhi = result.rhi;
791 ownRhi = result.own;
792
793 return true;
794}
795
796void QQuickRenderControlPrivate::resetRhi(const QQuickGraphicsConfiguration &config)
797{
798 if (ownRhi)
799 QSGRhiSupport::instance()->destroyRhi(rhi, config);
800
801 rhi = nullptr;
802
803 delete offscreenSurface;
804 offscreenSurface = nullptr;
805}
806
807QT_END_NAMESPACE
808
809#include "moc_qquickrendercontrol.cpp"
Combined button and popup list for selecting options.