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
561/*
562 Returns \c true if \a focusWindow is the window that holds focus on behalf of
563 \a quickWindow, which is the case when it's the window \a quickWindow renders
564 into, or an ancestor of it.
565*/
566bool QQuickRenderControlPrivate::isFocusWindowFor(QQuickWindow *quickWindow, const QWindow *focusWindow)
567{
568 if (!quickWindow || !focusWindow)
569 return false;
570
571 if (isRenderWindowFor(quickWindow, focusWindow))
572 return true;
573
574 // Qt Widgets manages focus via the top level QWindow, so the check
575 // above is not sufficient when the render window is a child QWindow.
576 // To match Qt Widgets expectations, we also check if the render window
577 // is a non-transient descendant of the focus window. Note: We still
578 // need the isRenderWindowFor check above, to handle the case of graphics
579 // view proxy widgets, where a single QQuickWidget may be proxied into
580 // multiple graphics views in different windows.
581 QWindow *renderWindow = QQuickRenderControl::renderWindowFor(quickWindow);
582 return renderWindow && focusWindow->isAncestorOf(renderWindow, QWindow::ExcludeTransients);
583}
584
585bool QQuickRenderControlPrivate::isRenderWindow(const QWindow *w)
586{
587 Q_Q(QQuickRenderControl);
588
589 if (window && w)
590 return q->renderWindowFor(window, nullptr) == w;
591
592 return false;
593}
594
595/*!
596 \return the QQuickWindow this QQuickRenderControl is associated with.
597
598 \note A QQuickRenderControl gets associated with a QQuickWindow when
599 constructing the QQuickWindow. The return value from this function is null
600 before that point.
601
602 \since 6.0
603 */
604QQuickWindow *QQuickRenderControl::window() const
605{
606 Q_D(const QQuickRenderControl);
607 return d->window;
608}
609
610/*!
611 \return the QRhi this QQuickRenderControl is associated with.
612
613 \note The QRhi exists only when initialize() has successfully completed.
614 Before that the return value is null.
615
616 \note This function is not applicable and returns null when using the
617 \c software adaptation of Qt Quick.
618
619 \since 6.6
620
621 \sa commandBuffer(), beginFrame(), endFrame()
622 */
623QRhi *QQuickRenderControl::rhi() const
624{
625 Q_D(const QQuickRenderControl);
626 return d->rhi;
627}
628
629/*!
630 \return the current command buffer.
631
632 Once beginFrame() is called, a QRhiCommandBuffer is set up automatically.
633 That is the command buffer Qt Quick scenegraph uses, but in some cases
634 applications may also want to query it, for example to issue resource
635 updates (for example, a texture readback).
636
637 The returned command buffer reference should only be used between
638 beginFrame() and endFrame(). There are specific exceptions, for example
639 calling
640 \l{QRhiCommandBuffer::lastCompletedGpuTime()}{lastCompletedGpuTime()} on
641 the command buffer right after endFrame(), but before the next
642 beginFrame(), is valid.
643
644 \note This function is not applicable and returns null when using the
645 \c software adaptation of Qt Quick.
646
647 \since 6.6
648
649 \sa rhi(), beginFrame(), endFrame()
650 */
651QRhiCommandBuffer *QQuickRenderControl::commandBuffer() const
652{
653 Q_D(const QQuickRenderControl);
654 return d->cb;
655}
656
657/*!
658 Specifies the start of a graphics frame. Calls to sync() or render() must
659 be enclosed by calls to beginFrame() and endFrame().
660
661 Unlike the earlier OpenGL-only world of Qt 5, rendering with other graphics
662 APIs requires more well-defined points of starting and ending a frame. When
663 manually driving the rendering loop via QQuickRenderControl, it now falls
664 to the user of QQuickRenderControl to specify these points.
665
666 A typical update step, including initialization of rendering into an
667 existing texture, could look like the following. The example snippet
668 assumes Direct3D 11 but the same concepts apply other graphics APIs as
669 well.
670
671 \code
672 if (!m_quickInitialized) {
673 m_quickWindow->setGraphicsDevice(QQuickGraphicsDevice::fromDeviceAndContext(m_engine->device(), m_engine->context()));
674
675 if (!m_renderControl->initialize())
676 qWarning("Failed to initialize redirected Qt Quick rendering");
677
678 m_quickWindow->setRenderTarget(QQuickRenderTarget::fromNativeTexture({ quint64(m_res.texture), 0 },
679 QSize(QML_WIDTH, QML_HEIGHT),
680 SAMPLE_COUNT));
681
682 m_quickInitialized = true;
683 }
684
685 m_renderControl->polishItems();
686
687 m_renderControl->beginFrame();
688 m_renderControl->sync();
689 m_renderControl->render();
690 m_renderControl->endFrame(); // Qt Quick's rendering commands are submitted to the device context here
691 \endcode
692
693 \note This function does not need to be, and must not be, called when using
694 the \c software adaptation of Qt Quick.
695
696 \note Internally beginFrame() and endFrame() invoke
697 \l{QRhi::}{beginOffscreenFrame()} and \l{QRhi::}{endOffscreenFrame()},
698 respectively. This implies that there must not be a frame (neither
699 offscreen, nor swapchain-based) being recorded on the QRhi when
700 this function is called.
701
702 \since 6.0
703
704 \sa endFrame(), initialize(), sync(), render(), QQuickGraphicsDevice, QQuickRenderTarget
705 */
706void QQuickRenderControl::beginFrame()
707{
708 Q_D(QQuickRenderControl);
709 if (!d->rhi) {
710 qWarning("QQuickRenderControl: No QRhi in beginFrame()");
711 return;
712 }
713 if (d->frameStatus == QQuickRenderControlPrivate::RecordingFrame) {
714 qWarning("QQuickRenderControl: beginFrame() must be followed by a call to endFrame() before calling beginFrame() again");
715 return;
716 }
717 if (d->rhi->isRecordingFrame()) {
718 qWarning("QQuickRenderControl: Attempted to beginFrame() while the QRhi is already recording a frame");
719 return;
720 }
721
722 emit d->window->beforeFrameBegin();
723
724 QRhi::FrameOpResult result = d->rhi->beginOffscreenFrame(&d->cb);
725
726 switch (result) {
727 case QRhi::FrameOpSuccess:
728 case QRhi::FrameOpSwapChainOutOfDate:
729 d->frameStatus = QQuickRenderControlPrivate::RecordingFrame;
730 break;
731 case QRhi::FrameOpError:
732 d->frameStatus = QQuickRenderControlPrivate::ErrorInBeginFrame;
733 break;
734 case QRhi::FrameOpDeviceLost:
735 d->frameStatus = QQuickRenderControlPrivate::DeviceLostInBeginFrame;
736 break;
737 default:
738 d->frameStatus = QQuickRenderControlPrivate::NotRecordingFrame;
739 break;
740 }
741}
742
743/*!
744 Specifies the end of a graphics frame. Calls to sync() or render() must be
745 enclosed by calls to beginFrame() and endFrame().
746
747 When this function is called, any graphics commands enqueued by the
748 scenegraph are submitted to the context or command queue, whichever is
749 applicable.
750
751 \note This function does not need to be, and must not be, called when using
752 the \c software adaptation of Qt Quick.
753
754 \since 6.0
755
756 \sa beginFrame(), initialize(), sync(), render(), QQuickGraphicsDevice, QQuickRenderTarget
757 */
758void QQuickRenderControl::endFrame()
759{
760 Q_D(QQuickRenderControl);
761 if (!d->rhi) {
762 qWarning("QQuickRenderControl: No QRhi in endFrame()");
763 return;
764 }
765 if (d->frameStatus != QQuickRenderControlPrivate::RecordingFrame) {
766 qWarning("QQuickRenderControl: endFrame() must only be called after a successful beginFrame()");
767 return;
768 }
769 if (!d->rhi->isRecordingFrame()) {
770 qWarning("QQuickRenderControl: Attempted to endFrame() while the QRhi is not recording a frame");
771 return;
772 }
773
774 d->rhi->endOffscreenFrame();
775 // do not null out d->cb; this allows calling lastCompletedGpuTime() for example
776
777 d->frameStatus = QQuickRenderControlPrivate::NotRecordingFrame;
778
779 emit d->window->afterFrameEnd();
780}
781
782bool QQuickRenderControlPrivate::initRhi()
783{
784 // initialize() - invalidate() - initialize() uses the QRhi the first
785 // initialize() created, so if already exists, we are done. Does not apply
786 // when wrapping an externally created QRhi, because we may be associated
787 // with a new one now.
788 if (rhi && ownRhi)
789 return true;
790
791 QSGRhiSupport *rhiSupport = QSGRhiSupport::instance();
792
793 // sanity check for Vulkan
794#if QT_CONFIG(vulkan)
795 if (rhiSupport->rhiBackend() == QRhi::Vulkan && !window->vulkanInstance()) {
796 qWarning("QQuickRenderControl: No QVulkanInstance set for QQuickWindow, cannot initialize");
797 return false;
798 }
799#endif
800
801 // for OpenGL
802 bool wantOffscreenSurface = true;
803
804 QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window);
805 const QQuickGraphicsDevicePrivate *customDevD = QQuickGraphicsDevicePrivate::get(&wd->customDeviceObjects);
806 if (customDevD->type == QQuickGraphicsDevicePrivate::Type::Rhi) {
807 if (customDevD->u.rhi)
808 wantOffscreenSurface = false;
809 }
810
811 if (wantOffscreenSurface && !offscreenSurface)
812 offscreenSurface = rhiSupport->maybeCreateOffscreenSurface(window);
813
814 QSGRhiSupport::RhiCreateResult result = rhiSupport->createRhi(window, offscreenSurface);
815 if (!result.rhi) {
816 qWarning("QQuickRenderControl: Failed to initialize QRhi");
817 return false;
818 }
819
820 rhi = result.rhi;
821 ownRhi = result.own;
822
823 return true;
824}
825
826void QQuickRenderControlPrivate::resetRhi(const QQuickGraphicsConfiguration &config)
827{
828 if (ownRhi)
829 QSGRhiSupport::instance()->destroyRhi(rhi, config);
830
831 rhi = nullptr;
832
833 delete offscreenSurface;
834 offscreenSurface = nullptr;
835}
836
837QT_END_NAMESPACE
838
839#include "moc_qquickrendercontrol.cpp"
Combined button and popup list for selecting options.