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