Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
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
5
#
include
"qquickrendercontrol.h"
6
#
include
"qquickrendercontrol_p.h"
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
32
QT_BEGIN_NAMESPACE
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
111
QSGContext *QQuickRenderControlPrivate::sg =
nullptr
;
112
113
QQuickRenderControlPrivate::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
131
void
QQuickRenderControlPrivate::cleanup()
132
{
133
delete
sg;
134
sg =
nullptr
;
135
}
136
137
/*!
138
Constructs a QQuickRenderControl object, with parent
139
object \a parent.
140
*/
141
QQuickRenderControl::QQuickRenderControl(QObject *parent)
142
: QObject(*(
new
QQuickRenderControlPrivate(
this
)), parent)
143
{
144
}
145
146
/*!
147
\internal
148
*/
149
QQuickRenderControl::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
*/
159
QQuickRenderControl::~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
187
void
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
*/
212
void
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
*/
232
void
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
*/
243
int
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
*/
296
bool
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(¶ms);
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
*/
331
void
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
*/
354
bool
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
*/
398
void
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
*/
423
void
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
468
QImage 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
509
void
QQuickRenderControlPrivate::update()
510
{
511
Q_Q(QQuickRenderControl);
512
emit q->renderRequested();
513
}
514
515
void
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
*/
543
QWindow *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
553
bool
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
*/
566
bool
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
585
bool
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
*/
604
QQuickWindow *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
*/
623
QRhi *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
*/
651
QRhiCommandBuffer *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
*/
706
void
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
*/
758
void
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
782
bool
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
826
void
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
837
QT_END_NAMESPACE
838
839
#
include
"moc_qquickrendercontrol.cpp"
QT_BEGIN_NAMESPACE
Combined button and popup list for selecting options.
Definition
qsequentialanimationgroup.cpp:47
qtdeclarative
src
quick
items
qquickrendercontrol.cpp
Generated on
for Qt by
1.18.0