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
qquickrhiitem.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 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
6#include <QtQuick/private/qsgrhisupport_p.h>
7
9
10/*!
11 \class QQuickRhiItem
12 \inmodule QtQuick
13 \since 6.7
14
15 \brief The QQuickRhiItem class is a portable alternative to
16 QQuickFramebufferObject that is not tied to OpenGL, but rather allows
17 integrating rendering with the QRhi APIs with Qt Quick.
18
19 QQuickRhiItem is effectively the counterpart of \l QRhiWidget in the world of
20 Qt Quick. Both of these are meant to be subclassed, and they both enable
21 recording QRhi-based rendering that targets an offscreen color buffer. The
22 resulting 2D image is then composited with the rest of the Qt Quick scene.
23
24 \note While QQuickRhiItem is a public Qt API, the QRhi family of classes in
25 the Qt Gui module, including QShader and QShaderDescription, offer limited
26 compatibility guarantees. There are no source or binary compatibility
27 guarantees for these classes, meaning the API is only guaranteed to work
28 with the Qt version the application was developed against. Source
29 incompatible changes are however aimed to be kept at a minimum and will
30 only be made in minor releases (6.7, 6.8, and so on). \c{qquickrhiitem.h}
31 does not directly include any QRhi-related headers. To use those classes
32 when implementing a QQuickRhiItem subclass, link to
33 \c{Qt::GuiPrivate} (if using CMake), and include the appropriate headers
34 with the \c rhi prefix, for example \c{#include <rhi/qrhi.h>}.
35
36 QQuickRhiItem is a replacement for the legacy \l QQuickFramebufferObject
37 class. The latter is inherently tied to OpenGL / OpenGL ES, whereas
38 QQuickRhiItem works with the QRhi classes, allowing to run the same
39 rendering code with Vulkan, Metal, Direct 3D 11/12, and OpenGL / OpenGL ES.
40 Conceptually and functionally they are very close, and migrating from
41 QQuickFramebufferObject to QQuickRhiItem is straightforward.
42 QQuickFramebufferObject continues to be available to ensure compatibility
43 for existing application code that works directly with the OpenGL API.
44
45 \note QQuickRhiItem will not be functional when using the \c software
46 adaptation of the Qt Quick scene graph.
47
48 On most platforms, the scene graph rendering, and thus the rendering
49 performed by the QQuickRhiItem will occur on a \l {Scene Graph and
50 Rendering}{dedicated thread}. For this reason, the QQuickRhiItem class
51 enforces a strict separation between the item implementation (the
52 QQuickItem subclass) and the actual rendering logic. All item logic, such
53 as properties and UI-related helper functions exposed to QML must be
54 located in the QQuickRhiItem subclass. Everything that relates to rendering
55 must be located in the QQuickRhiItemRenderer class. To avoid race
56 conditions and read/write issues from two threads it is important that the
57 renderer and the item never read or write shared variables. Communication
58 between the item and the renderer should primarily happen via the
59 QQuickRhiItem::synchronize() function. This function will be called on the
60 render thread while the GUI thread is blocked. Using queued connections or
61 events for communication between item and renderer is also possible.
62
63 Applications must subclass both QQuickRhiItem and QQuickRhiItemRenderer.
64 The pure virtual createRenderer() function must be reimplemented to return
65 a new instance of the QQuickRhiItemRenderer subclass.
66
67 As with QRhiWidget, QQuickRhiItem automatically managed the color buffer,
68 which is a 2D texture (QRhiTexture) normally, or a QRhiRenderBuffer when
69 multisampling is in use. (some 3D APIs differentiate between textures and
70 renderbuffers, while with some others the underlying native resource is the
71 same; renderbuffers are used mainly to allow multisampling with OpenGL ES
72 3.0)
73
74 The size of the texture will by default adapt to the size of the item (with
75 the \l{QQuickWindow::effectiveDevicePixelRatio()}{device pixel ratio} taken
76 into account). If the item size changes, the texture is recreated with the
77 correct size. If a fixed size is preferred, set \l fixedColorBufferWidth and
78 \l fixedColorBufferHeight to non-zero values.
79
80 QQuickRhiItem is a \l{QSGTextureProvider}{texture provider} and can be used
81 directly in \l {ShaderEffect}{ShaderEffects} and other classes that consume
82 texture providers.
83
84 While not a primary use case, QQuickRhiItem also allows incorporating
85 rendering code that directly uses a 3D graphics API such as Vulkan, Metal,
86 Direct 3D, or OpenGL. See \l QRhiCommandBuffer::beginExternal() for details
87 on recording native commands within a QRhi render pass, as well as
88 \l QRhiTexture::createFrom() for a way to wrap an existing native texture and
89 then use it with QRhi in a subsequent render pass. See also
90 \l QQuickGraphicsConfiguration regarding configuring the native 3D API
91 environment (e.g. device extensions) and note that the \l QQuickWindow can be
92 associated with a custom \l QVulkanInstance by calling
93 \l QWindow::setVulkanInstance() early enough.
94
95 \note QQuickRhiItem always uses the same QRhi instance the QQuickWindow
96 uses (and by extension, the same OpenGL context, Vulkan device, etc.). To
97 choose which underlying 3D graphics API is used, call
98 \l{QQuickWindow::setGraphicsApi()}{setGraphicsApi()} on the QQuickWindow
99 early enough. Changing it is not possible once the scene graph has
100 initialized, and all QQuickRhiItem instances in the scene will render using
101 the same 3D API.
102
103 \section1 A simple example
104
105 Take the following subclass of QQuickRhiItem. It is shown here in complete
106 form. It renders a single triangle with a perspective projection, where the
107 triangle is rotated based on the \c angle property of the custom item.
108 (meaning it can be driven for example with animations such as
109 \l NumberAnimation from QML)
110
111 \snippet qquickrhiitem/qquickrhiitem_intro.cpp 0
112
113 It is notable that this simple class is almost exactly the same as the code
114 shown in the \l QRhiWidget introduction. The vertex and fragment shaders are
115 the same as well. These are provided as Vulkan-style GLSL source code and
116 must be processed first by the Qt shader infrastructure first. This is
117 achieved either by running the \c qsb command-line tool manually, or by
118 using the \l qt_add_shaders() function in CMake.
119 The QQuickRhiItem loads these pre-processed \c{.qsb}
120 files that are shipped with the application. See \l{Qt Shader Tools} for
121 more information about Qt's shader translation infrastructure.
122
123 \c{color.vert}
124
125 \snippet qquickrhiitem/qquickrhiitem_intro.vert 0
126
127 \c{color.frag}
128
129 \snippet qquickrhiitem/qquickrhiitem_intro.frag 0
130
131 Once exposed to QML (note the \c QML_NAMED_ELEMENT), our custom item can be
132 instantiated in any scene. (after importing the appropriate \c URI specified
133 for \l qt_add_qml_module() in the CMake project)
134
135 \code
136 ExampleRhiItem {
137 anchors.fill: parent
138 anchors.margins: 10
139 NumberAnimation on angle { from: 0; to: 360; duration: 5000; loops: Animation.Infinite }
140 }
141 \endcode
142
143 See \l{Scene Graph - RHI Texture Item} for a more complex example.
144
145 \sa QQuickRhiItemRenderer, {Scene Graph - RHI Texture Item}, QRhi, {Scene Graph and Rendering}
146 */
147
148/*!
149 \class QQuickRhiItemRenderer
150 \inmodule QtQuick
151 \since 6.7
152
153 \brief A QQuickRhiItemRenderer implements the rendering logic of a
154 QQuickRhiItem.
155
156 \preliminary
157
158 \sa QQuickRhiItem, QRhi
159 */
160
162 : m_item(item)
163{
164 m_window = m_item->window();
165 connect(m_window, &QQuickWindow::beforeRendering, this, &QQuickRhiItemNode::render,
166 Qt::DirectConnection);
167 connect(m_window, &QQuickWindow::screenChanged, this, [this]() {
168 if (m_window->effectiveDevicePixelRatio() != m_dpr)
169 m_item->update();
170 }, Qt::DirectConnection);
171}
172
173QSGTexture *QQuickRhiItemNode::texture() const
174{
175 return m_sgTexture.get();
176}
177
179{
180 // owns either m_colorTexture or m_resolveTexture
181 m_sgTexture.reset();
182
183 m_colorTexture = nullptr;
184 m_resolveTexture = nullptr;
185
186 m_msaaColorBuffer.reset();
187}
188
190{
191 m_renderTarget.reset();
192 m_renderPassDescriptor.reset();
193 m_depthStencilBuffer.reset();
194}
195
197{
198 if (!m_rhi) {
199 m_rhi = m_window->rhi();
200 if (!m_rhi) {
201 qWarning("No QRhi found for window %p, QQuickRhiItem will not be functional", m_window);
202 return;
203 }
204 }
205
206 m_dpr = m_window->effectiveDevicePixelRatio();
207 const int minTexSize = m_rhi->resourceLimit(QRhi::TextureSizeMin);
208 const int maxTexSize = m_rhi->resourceLimit(QRhi::TextureSizeMax);
209
210 QQuickRhiItemPrivate *itemD = m_item->d_func();
211 QSize newSize = QSize(itemD->fixedTextureWidth, itemD->fixedTextureHeight);
212 if (newSize.isEmpty())
213 newSize = (QSizeF(m_item->width(), m_item->height()) * m_dpr).toSize();
214
215 newSize.setWidth(qMin(maxTexSize, qMax(minTexSize, newSize.width())));
216 newSize.setHeight(qMin(maxTexSize, qMax(minTexSize, newSize.height())));
217
218 if (m_colorTexture) {
219 if (m_colorTexture->format() != itemD->rhiTextureFormat
220 || m_colorTexture->sampleCount() != itemD->samples)
221 {
224 }
225 }
226
227 if (m_msaaColorBuffer) {
228 if (m_msaaColorBuffer->backingFormat() != itemD->rhiTextureFormat
229 || m_msaaColorBuffer->sampleCount() != itemD->samples)
230 {
233 }
234 }
235
236 if (m_sgTexture && m_sgTexture->hasAlphaChannel() != itemD->blend) {
239 }
240
241 if (!m_colorTexture && itemD->samples <= 1) {
242 if (!m_rhi->isTextureFormatSupported(itemD->rhiTextureFormat)) {
243 qWarning("QQuickRhiItem: The requested texture format (%d) is not supported by the "
244 "underlying 3D graphics API implementation", int(itemD->rhiTextureFormat));
245 }
246 m_colorTexture = m_rhi->newTexture(itemD->rhiTextureFormat, newSize, itemD->samples,
247 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
248 if (!m_colorTexture->create()) {
249 qWarning("Failed to create backing texture for QQuickRhiItem");
250 delete m_colorTexture;
251 m_colorTexture = nullptr;
252 return;
253 }
254 }
255
256 if (itemD->samples > 1) {
257 if (!m_msaaColorBuffer) {
258 if (!m_rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer)) {
259 qWarning("QQuickRhiItem: Multisample renderbuffers are reported as unsupported; "
260 "sample count %d will not work as expected", itemD->samples);
261 }
262 if (!m_rhi->isTextureFormatSupported(itemD->rhiTextureFormat)) {
263 qWarning("QQuickRhiItem: The requested texture format (%d) is not supported by the "
264 "underlying 3D graphics API implementation", int(itemD->rhiTextureFormat));
265 }
266 m_msaaColorBuffer.reset(m_rhi->newRenderBuffer(QRhiRenderBuffer::Color, newSize, itemD->samples,
267 {}, itemD->rhiTextureFormat));
268 if (!m_msaaColorBuffer->create()) {
269 qWarning("Failed to create multisample color buffer for QQuickRhiItem");
270 m_msaaColorBuffer.reset();
271 return;
272 }
273 }
274 if (!m_resolveTexture) {
275 m_resolveTexture = m_rhi->newTexture(itemD->rhiTextureFormat, newSize, 1,
276 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource);
277 if (!m_resolveTexture->create()) {
278 qWarning("Failed to create resolve texture for QQuickRhiItem");
279 delete m_resolveTexture;
280 m_resolveTexture = nullptr;
281 return;
282 }
283 }
284 } else if (m_resolveTexture) {
285 m_resolveTexture->deleteLater();
286 m_resolveTexture = nullptr;
287 }
288
289 if (m_colorTexture && m_colorTexture->pixelSize() != newSize) {
290 m_colorTexture->setPixelSize(newSize);
291 if (!m_colorTexture->create())
292 qWarning("Failed to rebuild texture for QQuickRhiItem after resizing");
293 }
294
295 if (m_msaaColorBuffer && m_msaaColorBuffer->pixelSize() != newSize) {
296 m_msaaColorBuffer->setPixelSize(newSize);
297 if (!m_msaaColorBuffer->create())
298 qWarning("Failed to rebuild multisample color buffer for QQuickRhiitem after resizing");
299 }
300
301 if (m_resolveTexture && m_resolveTexture->pixelSize() != newSize) {
302 m_resolveTexture->setPixelSize(newSize);
303 if (!m_resolveTexture->create())
304 qWarning("Failed to rebuild resolve texture for QQuickRhiItem after resizing");
305 }
306
307 if (!m_sgTexture) {
308 QQuickWindow::CreateTextureOptions options;
309 if (itemD->blend)
310 options |= QQuickWindow::TextureHasAlphaChannel;
311 // the QSGTexture takes ownership of the QRhiTexture
312 m_sgTexture.reset(m_window->createTextureFromRhiTexture(m_colorTexture ? m_colorTexture : m_resolveTexture,
313 options));
314 setTexture(m_sgTexture.get());
315 }
316
317 if (itemD->autoRenderTarget) {
318 const QSize pixelSize = m_colorTexture ? m_colorTexture->pixelSize()
319 : m_msaaColorBuffer->pixelSize();
320 if (!m_depthStencilBuffer) {
321 m_depthStencilBuffer.reset(m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, itemD->samples,
322 QSGRhiSupport::depthStencilBufferFlags()));
323 if (!m_depthStencilBuffer->create()) {
324 qWarning("Failed to create depth-stencil buffer for QQuickRhiItem");
326 return;
327 }
328 } else if (m_depthStencilBuffer->pixelSize() != pixelSize) {
329 m_depthStencilBuffer->setPixelSize(pixelSize);
330 if (!m_depthStencilBuffer->create()) {
331 qWarning("Failed to rebuild depth-stencil buffer for QQuickRhiItem with new size");
332 return;
333 }
334 }
335 if (!m_renderTarget) {
336 QRhiColorAttachment color0;
337 if (m_colorTexture)
338 color0.setTexture(m_colorTexture);
339 else
340 color0.setRenderBuffer(m_msaaColorBuffer.get());
341 if (itemD->samples > 1)
342 color0.setResolveTexture(m_resolveTexture);
343 QRhiTextureRenderTargetDescription rtDesc(color0, m_depthStencilBuffer.get());
344 m_renderTarget.reset(m_rhi->newTextureRenderTarget(rtDesc));
345 m_renderPassDescriptor.reset(m_renderTarget->newCompatibleRenderPassDescriptor());
346 m_renderTarget->setRenderPassDescriptor(m_renderPassDescriptor.get());
347 if (!m_renderTarget->create()) {
348 qWarning("Failed to create render target for QQuickRhiitem");
350 return;
351 }
352 }
353 } else {
355 }
356
357 if (newSize != itemD->effectiveTextureSize) {
358 itemD->effectiveTextureSize = newSize;
359 emit m_item->effectiveColorBufferSizeChanged();
360 }
361
362 QRhiCommandBuffer *cb = queryCommandBuffer();
363 if (cb)
364 m_renderer->initialize(cb);
365
366 m_renderer->synchronize(m_item);
367}
368
370{
371 QRhiSwapChain *swapchain = m_window->swapChain();
372 QSGRendererInterface *rif = m_window->rendererInterface();
373
374 // Handle both cases: on-screen QQuickWindow vs. off-screen QQuickWindow
375 // e.g. by using QQuickRenderControl to redirect into a texture.
376 QRhiCommandBuffer *cb = swapchain ? swapchain->currentFrameCommandBuffer()
377 : static_cast<QRhiCommandBuffer *>(
378 rif->getResource(m_window, QSGRendererInterface::RhiRedirectCommandBuffer));
379
380 if (!cb) {
381 qWarning("QQuickRhiItem: Neither swapchain nor redirected command buffer are available.");
382 return nullptr;
383 }
384
385 return cb;
386}
387
388void QQuickRhiItemNode::render()
389{
390 // called before Qt Quick starts recording its main render pass
391
392 if (!isValid() || !m_renderPending)
393 return;
394
395 QRhiCommandBuffer *cb = queryCommandBuffer();
396 if (!cb)
397 return;
398
399 m_renderPending = false;
400 m_renderer->render(cb);
401
402 markDirty(QSGNode::DirtyMaterial);
403 emit textureChanged();
404}
405
406/*!
407 Constructs a new QQuickRhiItem with the given \a parent.
408 */
409QQuickRhiItem::QQuickRhiItem(QQuickItem *parent)
410 : QQuickItem(*new QQuickRhiItemPrivate, parent)
411{
412 setFlag(ItemHasContents);
413}
414
415/*!
416 * \internal
417 */
418QQuickRhiItem::QQuickRhiItem(QQuickRhiItemPrivate &dd, QQuickItem *parent)
419 : QQuickItem(dd, parent)
420{
421 setFlag(ItemHasContents);
422}
423
424/*!
425 Destructor.
426*/
427QQuickRhiItem::~QQuickRhiItem()
428{
429}
430
431/*!
432 \internal
433 */
434QSGNode *QQuickRhiItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
435{
436 // Changing to an empty size should not involve destroying and then later
437 // recreating the node, because we do not know how expensive the user's
438 // renderer setup is. Rather, keep the node if it already exist, and clamp
439 // all accesses to width and height. Hence the unusual !oldNode condition here.
440 if (!oldNode && (width() <= 0 || height() <= 0))
441 return nullptr;
442
443 Q_D(QQuickRhiItem);
444 QQuickRhiItemNode *n = static_cast<QQuickRhiItemNode *>(oldNode);
445 if (!n) {
446 if (!d->node)
447 d->node = new QQuickRhiItemNode(this);
448 if (!d->node->hasRenderer()) {
449 QQuickRhiItemRenderer *r = createRenderer();
450 if (r) {
451 r->node = d->node;
452 d->node->setRenderer(r);
453 } else {
454 qWarning("No QQuickRhiItemRenderer was created; the item will not render");
455 delete d->node;
456 d->node = nullptr;
457 return nullptr;
458 }
459 }
460 n = d->node;
461 }
462
463 n->sync();
464
465 if (!n->isValid()) {
466 delete n;
467 d->node = nullptr;
468 return nullptr;
469 }
470
471 if (window()->rhi()->isYUpInFramebuffer()) {
472 n->setTextureCoordinatesTransform(d->mirrorVertically
473 ? QSGSimpleTextureNode::NoTransform
474 : QSGSimpleTextureNode::MirrorVertically);
475 } else {
476 n->setTextureCoordinatesTransform(d->mirrorVertically
477 ? QSGSimpleTextureNode::MirrorVertically
478 : QSGSimpleTextureNode::NoTransform);
479 }
480 n->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
481 n->setRect(0, 0, qMax(qreal(0), width()), qMax(qreal(0), height()));
482
483 n->scheduleUpdate();
484
485 return n;
486}
487
488/*!
489 \reimp
490 */
491bool QQuickRhiItem::event(QEvent *e)
492{
493 return QQuickItem::event(e);
494}
495
496/*!
497 \reimp
498 */
499void QQuickRhiItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
500{
501 QQuickItem::geometryChange(newGeometry, oldGeometry);
502 if (newGeometry.size() != oldGeometry.size())
503 update();
504}
505
506/*!
507 \reimp
508 */
509void QQuickRhiItem::releaseResources()
510{
511 // called on the gui thread if the item is removed from scene
512
513 Q_D(QQuickRhiItem);
514 d->node = nullptr;
515}
516
517void QQuickRhiItem::invalidateSceneGraph()
518{
519 // called on the render thread when the scenegraph is invalidated
520
521 Q_D(QQuickRhiItem);
522 d->node = nullptr;
523}
524
525/*!
526 \reimp
527 */
528bool QQuickRhiItem::isTextureProvider() const
529{
530 return true;
531}
532
533/*!
534 \reimp
535 */
536QSGTextureProvider *QQuickRhiItem::textureProvider() const
537{
538 if (QQuickItem::isTextureProvider()) // e.g. if Item::layer::enabled == true
539 return QQuickItem::textureProvider();
540
541 Q_D(const QQuickRhiItem);
542 if (!d->node) // create a node to have a provider, the texture will be null but that's ok
543 d->node = new QQuickRhiItemNode(const_cast<QQuickRhiItem *>(this));
544
545 return d->node;
546}
547
548/*!
549 \property QQuickRhiItem::sampleCount
550
551 This property controls for sample count for multisample antialiasing.
552 By default the value is \c 1 which means MSAA is disabled.
553
554 Valid values are 1, 4, 8, and sometimes 16 and 32.
555 \l QRhi::supportedSampleCounts() can be used to query the supported sample
556 counts at run time, but typically applications should request 1 (no MSAA),
557 4x (normal MSAA) or 8x (high MSAA).
558
559 \note Setting a new value implies that all QRhiGraphicsPipeline objects
560 created by the renderer must use the same sample count from then on.
561 Existing QRhiGraphicsPipeline objects created with a different sample count
562 must not be used anymore. When the value changes, all color and
563 depth-stencil buffers are destroyed and recreated automatically, and
564 \l {QQuickRhiItemRenderer::}{initialize()} is invoked again. However, when
565 isAutoRenderTargetEnabled() is \c false, it will be up to the application to
566 manage this with regards to the depth-stencil buffer or additional color
567 buffers.
568
569 Changing the sample count from the default 1 to a higher value implies that
570 \l {QQuickRhiItemRenderer::}{colorTexture()} becomes \nullptr and
571 \l {QQuickRhiItemRenderer::}{msaaColorBuffer()} starts returning a
572 valid object. Switching back to 1 (or 0), implies the opposite: in the next
573 call to initialize() msaaColorBuffer() is going to return \nullptr, whereas
574 colorTexture() becomes once again valid. In addition,
575 \l {QQuickRhiItemRenderer::}{resolveTexture()}
576 returns a valid (non-multisample) QRhiTexture whenever the sample count is
577 greater than 1 (i.e., MSAA is in use).
578
579 \sa QQuickRhiItemRenderer::msaaColorBuffer(),
580 QQuickRhiItemRenderer::resolveTexture()
581 */
582
583int QQuickRhiItem::sampleCount() const
584{
585 Q_D(const QQuickRhiItem);
586 return d->samples;
587}
588
589void QQuickRhiItem::setSampleCount(int samples)
590{
591 Q_D(QQuickRhiItem);
592 if (d->samples == samples)
593 return;
594
595 d->samples = samples;
596 emit sampleCountChanged();
597 update();
598}
599
600/*!
601 \enum QQuickRhiItem::TextureFormat
602 Specifies the format of the backing texture to which the QQuickRhiItem renders.
603
604 \value RGBA8 See QRhiTexture::RGBA8. This is the default.
605 \value RGBA16F See QRhiTexture::RGBA16F.
606 \value RGBA32F See QRhiTexture::RGBA32F.
607 \value RGB10A2 See QRhiTexture::RGB10A2.
608
609 \sa QRhiTexture
610 */
611
612/*!
613 \property QQuickRhiItem::colorBufferFormat
614
615 This property controls the texture format for the texture used as the color
616 buffer. The default value is TextureFormat::RGBA8. QQuickRhiItem supports
617 rendering to a subset of the formats supported by \l QRhiTexture. Only
618 formats that are reported as supported from
619 \l QRhi::isTextureFormatSupported() should be specified, rendering will not be
620 functional otherwise.
621
622 \note Setting a new format when the item and its renderer are already
623 initialized and have rendered implies that all QRhiGraphicsPipeline objects
624 created by the renderer may become unusable, if the associated
625 QRhiRenderPassDescriptor is now incompatible due to the different texture
626 format. Similarly to changing
627 \l sampleCount dynamically, this means that initialize() or render()
628 implementations must then take care of releasing the existing pipelines and
629 creating new ones.
630 */
631
632QQuickRhiItem::TextureFormat QQuickRhiItem::colorBufferFormat() const
633{
634 Q_D(const QQuickRhiItem);
635 return d->itemTextureFormat;
636}
637
638void QQuickRhiItem::setColorBufferFormat(TextureFormat format)
639{
640 Q_D(QQuickRhiItem);
641 if (d->itemTextureFormat == format)
642 return;
643
644 d->itemTextureFormat = format;
645 switch (format) {
646 case TextureFormat::RGBA8:
647 d->rhiTextureFormat = QRhiTexture::RGBA8;
648 break;
649 case TextureFormat::RGBA16F:
650 d->rhiTextureFormat = QRhiTexture::RGBA16F;
651 break;
652 case TextureFormat::RGBA32F:
653 d->rhiTextureFormat = QRhiTexture::RGBA32F;
654 break;
655 case TextureFormat::RGB10A2:
656 d->rhiTextureFormat = QRhiTexture::RGB10A2;
657 break;
658 }
659 emit colorBufferFormatChanged();
660 update();
661}
662
663/*!
664 \return the current automatic depth-stencil buffer and render target management setting.
665
666 By default this value is \c true.
667
668 \sa setAutoRenderTarget()
669 */
670bool QQuickRhiItem::isAutoRenderTargetEnabled() const
671{
672 Q_D(const QQuickRhiItem);
673 return d->autoRenderTarget;
674}
675
676/*!
677 Controls if a depth-stencil QRhiRenderBuffer and a QRhiTextureRenderTarget
678 is created and maintained automatically by the item. The default value is
679 \c true. Call this function early on, for example from the derived class'
680 constructor, with \a enabled set to \c false to disable this.
681
682 In automatic mode, the size and sample count of the depth-stencil buffer
683 follows the color buffer texture's settings. In non-automatic mode,
684 renderTarget() and depthStencilBuffer() always return \nullptr and it is
685 then up to the application's implementation of initialize() to take care of
686 setting up and managing these objects.
687 */
688void QQuickRhiItem::setAutoRenderTarget(bool enabled)
689{
690 Q_D(QQuickRhiItem);
691 if (d->autoRenderTarget == enabled)
692 return;
693
694 d->autoRenderTarget = enabled;
695 emit autoRenderTargetChanged();
696 update();
697}
698
699/*!
700 \property QQuickRhiItem::mirrorVertically
701
702 This property controls if texture UVs are flipped when drawing the textured
703 quad. It has no effect on the contents of the offscreen color buffer and
704 the rendering implemented by the QQuickRhiItemRenderer.
705
706 The default value is \c false.
707 */
708
709bool QQuickRhiItem::isMirrorVerticallyEnabled() const
710{
711 Q_D(const QQuickRhiItem);
712 return d->mirrorVertically;
713}
714
715void QQuickRhiItem::setMirrorVertically(bool enable)
716{
717 Q_D(QQuickRhiItem);
718 if (d->mirrorVertically == enable)
719 return;
720
721 d->mirrorVertically = enable;
722 emit mirrorVerticallyChanged();
723 update();
724}
725
726/*!
727 \property QQuickRhiItem::fixedColorBufferWidth
728
729 The fixed width, in pixels, of the item's associated texture or
730 renderbuffer. Relevant when a fixed color buffer size is desired that does
731 not depend on the item's size. This size has no effect on the geometry of
732 the item (its size and placement within the scene), which means the
733 texture's content will appear stretched (scaled up) or scaled down onto the
734 item's area.
735
736 For example, setting a size that is exactly twice the item's (pixel) size
737 effectively performs 2x supersampling (rendering at twice the resolution and
738 then implicitly scaling down when texturing the quad corresponding to the
739 item in the scene). On the other hand, setting a size that is half of the
740 item's pixel size effectively achieves rendering at half resolution and then
741 upscaling the results.
742
743 By default the value is \c 0. A value of 0 means that texture's size
744 follows the item's size. (\c{texture size} = \c{item size} * \c{device
745 pixel ratio}).
746
747 \note The device pixel ratio (the system compositor's scale factor) can have
748 a big impact on performance, since a scale factor of 2 (200%) means
749 rendering at twice the resolution, so twice of what the developer and UI
750 designer perceives as the item's size, and then effectively downscaling the
751 content, similarly to what happens when setting this property to twice the
752 item's pixel size on a system where the device pixel ratio is 1. Therefore,
753 this property is expected to be rarely used with sizes bigger than the
754 item's pixel size, since many modern desktop systems have neither the need
755 nor have the performance budget for it, when a larger than 1 device pixel
756 ratio is used anyway by the system. Instead, the main use case for this
757 property is to set a smaller size, in order to render at a reasonable
758 smaller resolution instead of blindly following the item (and perhaps
759 window) geometry, however big that may be.
760 */
761int QQuickRhiItem::fixedColorBufferWidth() const
762{
763 Q_D(const QQuickRhiItem);
764 return d->fixedTextureWidth;
765}
766
767void QQuickRhiItem::setFixedColorBufferWidth(int width)
768{
769 Q_D(QQuickRhiItem);
770 if (d->fixedTextureWidth == width)
771 return;
772
773 d->fixedTextureWidth = width;
774 emit fixedColorBufferWidthChanged();
775 update();
776}
777
778/*!
779 \property QQuickRhiItem::fixedColorBufferHeight
780
781 The fixed height, in pixels, of the item's associated texture. Relevant when
782 a fixed texture size is desired that does not depend on the item's size.
783 This size has no effect on the geometry of the item (its size and placement
784 within the scene), which means the texture's content will appear stretched
785 (scaled up) or scaled down onto the item's area.
786
787 By default the value is \c 0. A value of 0 means that texture's size
788 follows the item's size. (\c{texture size} = \c{item size} * \c{device
789 pixel ratio}).
790
791 See \l fixedColorBufferWidth for more information on the use cases for
792 setting a fixed width and height.
793 */
794
795int QQuickRhiItem::fixedColorBufferHeight() const
796{
797 Q_D(const QQuickRhiItem);
798 return d->fixedTextureHeight;
799}
800
801void QQuickRhiItem::setFixedColorBufferHeight(int height)
802{
803 Q_D(QQuickRhiItem);
804 if (d->fixedTextureHeight == height)
805 return;
806
807 d->fixedTextureHeight = height;
808 emit fixedColorBufferHeightChanged();
809 update();
810}
811
812/*!
813 \property QQuickRhiItem::effectiveColorBufferSize
814
815 This property exposes the size, in pixels, of the underlying color buffer
816 (the QRhiTexture or QRhiRenderBuffer). It is provided for use on the GUI
817 (main) thread, in QML bindings or JavaScript.
818
819 \note QQuickRhiItemRenderer implementations, operating on the scene graph
820 render thread, should not use this property. Those should rather query the
821 size from the
822 \l{QQuickRhiItemRenderer::renderTarget()}{render target}.
823
824 \note The value becomes available asynchronously from the main thread's
825 perspective in the sense that the value changes when rendering happens on
826 the render thread. This means that this property is useful mainly in QML
827 bindings. Application code must not assume that the value is up to date
828 already when the QQuickRhiItem object is constructed.
829
830 This is a read-only property.
831 */
832
833QSize QQuickRhiItem::effectiveColorBufferSize() const
834{
835 Q_D(const QQuickRhiItem);
836 return d->effectiveTextureSize;
837}
838
839/*!
840 \property QQuickRhiItem::alphaBlending
841
842 Controls if blending is always enabled when drawing the quad textured with
843 the content generated by the QQuickRhiItem and its renderer.
844
845 The default value is \c false. This is for performance reasons: if
846 semi-transparency is not involved, because the QQuickRhiItemRenderer clears
847 to an opaque color and never renders fragments with alpha smaller than 1,
848 then there is no point in enabling blending.
849
850 If the QQuickRhiItemRenderer subclass renders with semi-transparency involved,
851 set this property to true.
852
853 \note Under certain conditions blending is still going to happen regardless
854 of the value of this property. For example, if the item's
855 \l{QQuickItem::opacity}{opacity} (more precisely, the combined opacity
856 inherited from the parent chain) is smaller than 1, blending will be
857 automatically enabled even when this property is set to false.
858
859 \note The Qt Quick scene graph relies on and expect pre-multiplied alpha.
860 For example, if the intention is to clear the background in the renderer to
861 an alpha value of 0.5, then make sure to multiply the red, green, and blue
862 clear color values with 0.5 as well. Otherwise the blending results will be
863 incorrect.
864 */
865
866bool QQuickRhiItem::alphaBlending() const
867{
868 Q_D(const QQuickRhiItem);
869 return d->blend;
870}
871
872void QQuickRhiItem::setAlphaBlending(bool enable)
873{
874 Q_D(QQuickRhiItem);
875 if (d->blend == enable)
876 return;
877
878 d->blend = enable;
879 emit alphaBlendingChanged();
880 update();
881}
882
883/*!
884 Constructs a new renderer.
885
886 This function is called on the rendering thread during the scene graph sync
887 phase when the GUI thread is blocked.
888
889 \sa QQuickRhiItem::createRenderer()
890 */
891QQuickRhiItemRenderer::QQuickRhiItemRenderer()
892{
893}
894
895/*!
896 The Renderer is automatically deleted when the scene graph resources for
897 the QQuickRhiItem item are cleaned up.
898
899 This function is called on the rendering thread.
900
901 Under certain conditions it is normal and expected that the renderer object
902 is destroyed and then recreated. This is because the renderer's lifetime
903 effectively follows the underlying scene graph node. For example, when
904 changing the parent of a QQuickRhiItem object so that it then belongs to a
905 different \l QQuickWindow, the scene graph nodes are all dropped and
906 recreated due to the window change. This will also involve dropping and
907 creating a new QQuickRhiItemRenderer.
908
909 Unlike \l QRhiWidget, QQuickRhiItemRenderer has no need to implement
910 additional code paths for releasing (or early-relasing) graphics resources
911 created via QRhi. It is sufficient to release everything in the destructor,
912 or rely on smart pointers.
913 */
914QQuickRhiItemRenderer::~QQuickRhiItemRenderer()
915{
916}
917
918/*!
919 Call this function when the content of the offscreen color buffer should be
920 updated. (i.e. to request that render() is called again; the call will
921 happen at a later point, and note that updates are typically throttled to
922 the presentation rate)
923
924 This function can be called from render() to schedule an update.
925
926 \note This function should be used from inside the renderer. To update
927 the item on the GUI thread, use QQuickRhiItem::update().
928 */
929void QQuickRhiItemRenderer::update()
930{
931 if (node)
932 node->scheduleUpdate();
933}
934
935/*!
936 \return the current QRhi object.
937
938 Must only be called from initialize() and render().
939 */
940QRhi *QQuickRhiItemRenderer::rhi() const
941{
942 return node ? node->m_rhi : nullptr;
943}
944
945/*!
946 \return the texture serving as the color buffer for the item.
947
948 Must only be called from initialize() and render().
949
950 Unlike the depth-stencil buffer and the QRhiRenderTarget, this texture is
951 always available and is managed by the QQuickRhiItem, independent of the
952 value of \l {QQuickRhiItem::}{isAutoRenderTargetEnabled}.
953
954 \note When \l {QQuickRhiItem::}{sampleCount} is larger than 1, and so
955 multisample antialiasing is enabled, the return value is \nullptr. Instead,
956 query the \l QRhiRenderBuffer by calling msaaColorBuffer().
957
958 \note The backing texture size and sample count can also be queried via the
959 QRhiRenderTarget returned from renderTarget(). This can be more convenient
960 and compact than querying from the QRhiTexture or QRhiRenderBuffer, because
961 it works regardless of multisampling is in use or not.
962
963 \sa msaaColorBuffer(), depthStencilBuffer(), renderTarget(), resolveTexture()
964 */
965QRhiTexture *QQuickRhiItemRenderer::colorTexture() const
966{
967 return node ? node->m_colorTexture : nullptr;
968}
969
970/*!
971 \return the renderbuffer serving as the multisample color buffer for the item.
972
973 Must only be called from initialize() and render().
974
975 When \l {QQuickRhiItem::}{sampleCount} is larger than 1, and so multisample
976 antialising is enabled, the returned QRhiRenderBuffer has a matching sample
977 count and serves as the color buffer. Graphics pipelines used to render
978 into this buffer must be created with the same sample count, and the
979 depth-stencil buffer's sample count must match as well. The multisample
980 content is expected to be resolved into the texture returned from
981 resolveTexture(). When \l {QQuickRhiItem::}{isAutoRenderTargetEnabled} is
982 \c true, renderTarget() is set up automatically to do this, by setting up
983 msaaColorBuffer() as the
984 \l{QRhiColorAttachment::renderBuffer()}{renderbuffer} of color attachment 0
985 and resolveTexture() as its
986 \l{QRhiColorAttachment::resolveTexture()}{resolveTexture}.
987
988 When MSAA is not in use, the return value is \nullptr. Use colorTexture()
989 instead then.
990
991 Depending on the underlying 3D graphics API, there may be no practical
992 difference between multisample textures and color renderbuffers with a
993 sample count larger than 1 (QRhi may just map both to the same native
994 resource type). Some older APIs however may differentiate between textures
995 and renderbuffers. In order to support OpenGL ES 3.0, where multisample
996 renderbuffers are available, but multisample textures are not, QQuickRhiItem
997 always performs MSAA by using a multisample QRhiRenderBuffer as the color
998 attachment (and never a multisample QRhiTexture).
999
1000 \note The backing texture size and sample count can also be queried via the
1001 QRhiRenderTarget returned from renderTarget(). This can be more convenient
1002 and compact than querying from the QRhiTexture or QRhiRenderBuffer, because
1003 it works regardless of multisampling is in use or not.
1004
1005 \sa colorTexture(), depthStencilBuffer(), renderTarget(), resolveTexture()
1006 */
1007QRhiRenderBuffer *QQuickRhiItemRenderer::msaaColorBuffer() const
1008{
1009 return node ? node->m_msaaColorBuffer.get() : nullptr;
1010}
1011
1012/*!
1013 \return the non-multisample texture to which the multisample content is resolved.
1014
1015 The result is \nullptr when multisample antialiasing is not enabled.
1016
1017 Must only be called from initialize() and render().
1018
1019 With MSAA enabled, this is the texture that gets used by the item's
1020 underlying scene graph node when texturing a quad in the main render pass
1021 of Qt Quick. However, the QQuickRhiItemRenderer's rendering must target the
1022 (multisample) QRhiRenderBuffer returned from msaaColorBuffer(). When \l
1023 {QQuickRhiItem::}{isAutoRenderTargetEnabled} is \c true, this is taken care
1024 of by the QRhiRenderTarget returned from renderTarget(). Otherwise, it is
1025 up to the subclass code to correctly configure a render target object with
1026 both the color buffer and resolve textures.
1027
1028 \sa colorTexture()
1029 */
1030QRhiTexture *QQuickRhiItemRenderer::resolveTexture() const
1031{
1032 return node ? node->m_resolveTexture : nullptr;
1033}
1034
1035/*!
1036 \return the depth-stencil buffer used by the item's rendering.
1037
1038 Must only be called from initialize() and render().
1039
1040 Available only when \l {QQuickRhiItem::}{isAutoRenderTargetEnabled} is \c
1041 true. Otherwise the returned value is \nullptr and it is up the
1042 reimplementation of initialize() to create and manage a depth-stencil
1043 buffer and a QRhiTextureRenderTarget.
1044
1045 \sa colorTexture(), renderTarget()
1046 */
1047QRhiRenderBuffer *QQuickRhiItemRenderer::depthStencilBuffer() const
1048{
1049 return node ? node->m_depthStencilBuffer.get() : nullptr;
1050}
1051
1052/*!
1053 \return the render target object that must be used with
1054 \l QRhiCommandBuffer::beginPass() in reimplementations of render().
1055
1056 Must only be called from initialize() and render().
1057
1058 Available only when \l {QQuickRhiItem::}{isAutoRenderTargetEnabled} is \c
1059 true. Otherwise the returned value is \nullptr and it is up the
1060 reimplementation of initialize() to create and manage a depth-stencil
1061 buffer and a QRhiTextureRenderTarget.
1062
1063 When creating \l{QRhiGraphicsPipeline}{graphics pipelines}, a
1064 QRhiRenderPassDescriptor is needed. This can be queried from the returned
1065 QRhiTextureRenderTarget by calling
1066 \l{QRhiTextureRenderTarget::renderPassDescriptor()}{renderPassDescriptor()}.
1067
1068 \note The returned QRhiTextureRenderTarget always reports a
1069 \l{QRhiTextureRenderTarget::}{devicePixelRatio()} of \c 1.
1070 This is because only swapchains and the associated window have a concept of
1071 device pixel ratio, not textures, and the render target here always refers
1072 to a texture. If the on-screen scale factor is relevant for rendering,
1073 query and store it via the item's
1074 \c{window()->effectiveDevicePixelRatio()} in \l synchronize().
1075 When doing so, always prefer using \l{QQuickWindow::}{effectiveDevicePixelRatio()}
1076 over the base class' \l{QWindow::}{devicePixelRatio()}.
1077
1078 \sa colorTexture(), depthStencilBuffer(), QQuickWindow::effectiveDevicePixelRatio()
1079 */
1080QRhiRenderTarget *QQuickRhiItemRenderer::renderTarget() const
1081{
1082 return node ? node->m_renderTarget.get() : nullptr;
1083}
1084
1085/*!
1086 \fn QQuickRhiItemRenderer *QQuickRhiItem::createRenderer()
1087
1088 Reimplement this function to create and return a new instance of a
1089 QQuickRhiItemRenderer subclass.
1090
1091 This function will be called on the rendering thread while the GUI thread
1092 is blocked.
1093 */
1094
1095/*!
1096 \fn void QQuickRhiItemRenderer::initialize(QRhiCommandBuffer *cb)
1097
1098 Called when the item is initialized for the first time, when the
1099 associated texture's size, format, or sample count changes, or when the
1100 QRhi or texture change for any reason. The function is expected to
1101 maintain (create if not yet created, adjust and rebuild if the size has
1102 changed) the graphics resources used by the rendering code in render().
1103
1104 To query the QRhi, QRhiTexture, and other related objects, call rhi(),
1105 colorTexture(), depthStencilBuffer(), and renderTarget().
1106
1107 When the item size changes, the QRhi object, the color buffer texture,
1108 and the depth stencil buffer objects are all the same instances (so the
1109 getters return the same pointers) as before, but the color and
1110 depth/stencil buffers will likely have been rebuilt, meaning the
1111 \l{QRhiTexture::pixelSize()}{size} and the underlying native texture
1112 resource may be different than in the last invocation.
1113
1114 Reimplementations should also be prepared that the QRhi object and the
1115 color buffer texture may change between invocations of this function. For
1116 example, when the item is reparented so that it belongs to a new
1117 QQuickWindow, the the QRhi and all related resources managed by the
1118 QQuickRhiItem will be different instances than before in the subsequent
1119 call to this function. Is is then important that all existing QRhi
1120 resources previously created by the subclass are destroyed because they
1121 belong to the previous QRhi that should not be used anymore.
1122
1123 When \l {QQuickRhiItem::}{isAutoRenderTargetEnabled} is \c true, which is
1124 the default, a depth-stencil QRhiRenderBuffer and a QRhiTextureRenderTarget
1125 associated with the colorTexture() (or msaaColorBuffer()) and the
1126 depth-stencil buffer are created and managed automatically.
1127 Reimplementations of initialize() and render() can query those objects via
1128 depthStencilBuffer() and renderTarget(). When \l
1129 {QQuickRhiItem::}{isAutoRenderTargetEnabled} is set to \c false, these
1130 objects are no longer created and managed automatically. Rather, it will be
1131 up the the initialize() implementation to create buffers and set up the
1132 render target as it sees fit. When manually managing additional color or
1133 depth-stencil attachments for the render target, their size and sample
1134 count must always follow the size and sample count of colorTexture() (or
1135 msaaColorBuffer()), otherwise rendering or 3D API validation errors may
1136 occur.
1137
1138 The subclass-created graphics resources are expected to be released in the
1139 destructor implementation of the subclass.
1140
1141 \a cb is the QRhiCommandBuffer for the current frame. The function is
1142 called with a frame being recorded, but without an active render pass. The
1143 command buffer is provided primarily to allow enqueuing
1144 \l{QRhiCommandBuffer::resourceUpdate()}{resource updates} without deferring
1145 to render().
1146
1147 This function is called on the render thread, if there is one.
1148
1149 \sa render()
1150 */
1151
1152/*!
1153 \fn void QQuickRhiItemRenderer::synchronize(QQuickRhiItem *item)
1154
1155 This function is called on the render thread, if there is one, while the
1156 main/GUI thread is blocked. It is called from
1157 \l{QQuickItem::updatePaintNode()}{the \a {item}'s synchronize step},
1158 and allows reading and writing data belonging to the main and render
1159 threads. Typically property values stored in the QQuickRhiItem are copied
1160 into the QQuickRhiItemRenderer, so that they can be safely read afterwards
1161 in render() when the render and main threads continue to work in parallel.
1162
1163 \sa initialize(), render()
1164 */
1165
1166/*!
1167 \fn void QQuickRhiItemRenderer::render(QRhiCommandBuffer *cb)
1168
1169 Called when the backing color buffer's contents needs updating.
1170
1171 There is always at least one call to initialize() before this function is
1172 called.
1173
1174 To request updates, call \l QQuickItem::update() when calling from QML or
1175 from C++ code on the main/GUI thread (e.g. when in a property setter), or
1176 \l update() when calling from within a QQuickRhiItemRenderer callback.
1177 Calling QQuickRhiItemRenderer's update() from within
1178 render() will lead to triggering updates continuously.
1179
1180 \a cb is the QRhiCommandBuffer for the current frame. The function is
1181 called with a frame being recorded, but without an active render pass.
1182
1183 This function is called on the render thread, if there is one.
1184
1185 \sa initialize(), synchronize()
1186 */
1187
1188QT_END_NAMESPACE
1189
1190#include "moc_qquickrhiitem.cpp"
1191#include "moc_qquickrhiitem_p.cpp"
QRhiTexture * m_colorTexture
QQuickRhiItem * m_item
QRhiCommandBuffer * queryCommandBuffer()
bool isValid() const
QRhiTexture * m_resolveTexture
\inmodule QtQuick
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:606
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:650
Combined button and popup list for selecting options.