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 \enum QQuickRhiItem::TextureFormat
603 Specifies the format of the backing texture to which the QQuickRhiItem renders.
604
605 \value RGBA8 See QRhiTexture::RGBA8. This is the default.
606 \value RGBA16F See QRhiTexture::RGBA16F.
607 \value RGBA32F See QRhiTexture::RGBA32F.
608 \value RGB10A2 See QRhiTexture::RGB10A2.
609
610 \sa QRhiTexture
611 */
612
613/*!
614 \property QQuickRhiItem::colorBufferFormat
615
616 This property controls the texture format for the texture used as the color
617 buffer. The default value is TextureFormat::RGBA8. QQuickRhiItem supports
618 rendering to a subset of the formats supported by \l QRhiTexture. Only
619 formats that are reported as supported from
620 \l QRhi::isTextureFormatSupported() should be specified, rendering will not be
621 functional otherwise.
622
623 \note Setting a new format when the item and its renderer are already
624 initialized and have rendered implies that all QRhiGraphicsPipeline objects
625 created by the renderer may become unusable, if the associated
626 QRhiRenderPassDescriptor is now incompatible due to the different texture
627 format. Similarly to changing
628 \l sampleCount dynamically, this means that initialize() or render()
629 implementations must then take care of releasing the existing pipelines and
630 creating new ones.
631 */
632
633QQuickRhiItem::TextureFormat QQuickRhiItem::colorBufferFormat() const
634{
635 Q_D(const QQuickRhiItem);
636 return d->itemTextureFormat;
637}
638
639void QQuickRhiItem::setColorBufferFormat(TextureFormat format)
640{
641 Q_D(QQuickRhiItem);
642 if (d->itemTextureFormat == format)
643 return;
644
645 d->itemTextureFormat = format;
646 switch (format) {
647 case TextureFormat::RGBA8:
648 d->rhiTextureFormat = QRhiTexture::RGBA8;
649 break;
650 case TextureFormat::RGBA16F:
651 d->rhiTextureFormat = QRhiTexture::RGBA16F;
652 break;
653 case TextureFormat::RGBA32F:
654 d->rhiTextureFormat = QRhiTexture::RGBA32F;
655 break;
656 case TextureFormat::RGB10A2:
657 d->rhiTextureFormat = QRhiTexture::RGB10A2;
658 break;
659 }
660 emit colorBufferFormatChanged();
661 update();
662}
663
664/*!
665 \return the current automatic depth-stencil buffer and render target management setting.
666
667 By default this value is \c true.
668
669 \sa setAutoRenderTarget()
670 */
671bool QQuickRhiItem::isAutoRenderTargetEnabled() const
672{
673 Q_D(const QQuickRhiItem);
674 return d->autoRenderTarget;
675}
676
677/*!
678 Controls if a depth-stencil QRhiRenderBuffer and a QRhiTextureRenderTarget
679 is created and maintained automatically by the item. The default value is
680 \c true. Call this function early on, for example from the derived class'
681 constructor, with \a enabled set to \c false to disable this.
682
683 In automatic mode, the size and sample count of the depth-stencil buffer
684 follows the color buffer texture's settings. In non-automatic mode,
685 renderTarget() and depthStencilBuffer() always return \nullptr and it is
686 then up to the application's implementation of initialize() to take care of
687 setting up and managing these objects.
688 */
689void QQuickRhiItem::setAutoRenderTarget(bool enabled)
690{
691 Q_D(QQuickRhiItem);
692 if (d->autoRenderTarget == enabled)
693 return;
694
695 d->autoRenderTarget = enabled;
696 emit autoRenderTargetChanged();
697 update();
698}
699
700/*!
701 \property QQuickRhiItem::mirrorVertically
702
703 This property controls if texture UVs are flipped when drawing the textured
704 quad. It has no effect on the contents of the offscreen color buffer and
705 the rendering implemented by the QQuickRhiItemRenderer.
706
707 The default value is \c false.
708 */
709
710bool QQuickRhiItem::isMirrorVerticallyEnabled() const
711{
712 Q_D(const QQuickRhiItem);
713 return d->mirrorVertically;
714}
715
716void QQuickRhiItem::setMirrorVertically(bool enable)
717{
718 Q_D(QQuickRhiItem);
719 if (d->mirrorVertically == enable)
720 return;
721
722 d->mirrorVertically = enable;
723 emit mirrorVerticallyChanged();
724 update();
725}
726
727/*!
728 \property QQuickRhiItem::fixedColorBufferWidth
729
730 The fixed width, in pixels, of the item's associated texture or
731 renderbuffer. Relevant when a fixed color buffer size is desired that does
732 not depend on the item's size. This size has no effect on the geometry of
733 the item (its size and placement within the scene), which means the
734 texture's content will appear stretched (scaled up) or scaled down onto the
735 item's area.
736
737 For example, setting a size that is exactly twice the item's (pixel) size
738 effectively performs 2x supersampling (rendering at twice the resolution and
739 then implicitly scaling down when texturing the quad corresponding to the
740 item in the scene). On the other hand, setting a size that is half of the
741 item's pixel size effectively achieves rendering at half resolution and then
742 upscaling the results.
743
744 By default the value is \c 0. A value of 0 means that texture's size
745 follows the item's size. (\c{texture size} = \c{item size} * \c{device
746 pixel ratio}).
747
748 \note The device pixel ratio (the system compositor's scale factor) can have
749 a big impact on performance, since a scale factor of 2 (200%) means
750 rendering at twice the resolution, so twice of what the developer and UI
751 designer perceives as the item's size, and then effectively downscaling the
752 content, similarly to what happens when setting this property to twice the
753 item's pixel size on a system where the device pixel ratio is 1. Therefore,
754 this property is expected to be rarely used with sizes bigger than the
755 item's pixel size, since many modern desktop systems have neither the need
756 nor have the performance budget for it, when a larger than 1 device pixel
757 ratio is used anyway by the system. Instead, the main use case for this
758 property is to set a smaller size, in order to render at a reasonable
759 smaller resolution instead of blindly following the item (and perhaps
760 window) geometry, however big that may be.
761 */
762int QQuickRhiItem::fixedColorBufferWidth() const
763{
764 Q_D(const QQuickRhiItem);
765 return d->fixedTextureWidth;
766}
767
768void QQuickRhiItem::setFixedColorBufferWidth(int width)
769{
770 Q_D(QQuickRhiItem);
771 if (d->fixedTextureWidth == width)
772 return;
773
774 d->fixedTextureWidth = width;
775 emit fixedColorBufferWidthChanged();
776 update();
777}
778
779/*!
780 \property QQuickRhiItem::fixedColorBufferHeight
781
782 The fixed height, in pixels, of the item's associated texture. Relevant when
783 a fixed texture size is desired that does not depend on the item's size.
784 This size has no effect on the geometry of the item (its size and placement
785 within the scene), which means the texture's content will appear stretched
786 (scaled up) or scaled down onto the item's area.
787
788 By default the value is \c 0. A value of 0 means that texture's size
789 follows the item's size. (\c{texture size} = \c{item size} * \c{device
790 pixel ratio}).
791
792 See \l fixedColorBufferWidth for more information on the use cases for
793 setting a fixed width and height.
794 */
795
796int QQuickRhiItem::fixedColorBufferHeight() const
797{
798 Q_D(const QQuickRhiItem);
799 return d->fixedTextureHeight;
800}
801
802void QQuickRhiItem::setFixedColorBufferHeight(int height)
803{
804 Q_D(QQuickRhiItem);
805 if (d->fixedTextureHeight == height)
806 return;
807
808 d->fixedTextureHeight = height;
809 emit fixedColorBufferHeightChanged();
810 update();
811}
812
813/*!
814 \property QQuickRhiItem::effectiveColorBufferSize
815
816 This property exposes the size, in pixels, of the underlying color buffer
817 (the QRhiTexture or QRhiRenderBuffer). It is provided for use on the GUI
818 (main) thread, in QML bindings or JavaScript.
819
820 \note QQuickRhiItemRenderer implementations, operating on the scene graph
821 render thread, should not use this property. Those should rather query the
822 size from the
823 \l{QQuickRhiItemRenderer::renderTarget()}{render target}.
824
825 \note The value becomes available asynchronously from the main thread's
826 perspective in the sense that the value changes when rendering happens on
827 the render thread. This means that this property is useful mainly in QML
828 bindings. Application code must not assume that the value is up to date
829 already when the QQuickRhiItem object is constructed.
830
831 This is a read-only property.
832 */
833
834QSize QQuickRhiItem::effectiveColorBufferSize() const
835{
836 Q_D(const QQuickRhiItem);
837 return d->effectiveTextureSize;
838}
839
840/*!
841 \property QQuickRhiItem::alphaBlending
842
843 Controls if blending is always enabled when drawing the quad textured with
844 the content generated by the QQuickRhiItem and its renderer.
845
846 The default value is \c false. This is for performance reasons: if
847 semi-transparency is not involved, because the QQuickRhiItemRenderer clears
848 to an opaque color and never renders fragments with alpha smaller than 1,
849 then there is no point in enabling blending.
850
851 If the QQuickRhiItemRenderer subclass renders with semi-transparency involved,
852 set this property to true.
853
854 \note Under certain conditions blending is still going to happen regardless
855 of the value of this property. For example, if the item's
856 \l{QQuickItem::opacity}{opacity} (more precisely, the combined opacity
857 inherited from the parent chain) is smaller than 1, blending will be
858 automatically enabled even when this property is set to false.
859
860 \note The Qt Quick scene graph relies on and expect pre-multiplied alpha.
861 For example, if the intention is to clear the background in the renderer to
862 an alpha value of 0.5, then make sure to multiply the red, green, and blue
863 clear color values with 0.5 as well. Otherwise the blending results will be
864 incorrect.
865 */
866
867bool QQuickRhiItem::alphaBlending() const
868{
869 Q_D(const QQuickRhiItem);
870 return d->blend;
871}
872
873void QQuickRhiItem::setAlphaBlending(bool enable)
874{
875 Q_D(QQuickRhiItem);
876 if (d->blend == enable)
877 return;
878
879 d->blend = enable;
880 emit alphaBlendingChanged();
881 update();
882}
883
884/*!
885 Constructs a new renderer.
886
887 This function is called on the rendering thread during the scene graph sync
888 phase when the GUI thread is blocked.
889
890 \sa QQuickRhiItem::createRenderer()
891 */
892QQuickRhiItemRenderer::QQuickRhiItemRenderer()
893{
894}
895
896/*!
897 The Renderer is automatically deleted when the scene graph resources for
898 the QQuickRhiItem item are cleaned up.
899
900 This function is called on the rendering thread.
901
902 Under certain conditions it is normal and expected that the renderer object
903 is destroyed and then recreated. This is because the renderer's lifetime
904 effectively follows the underlying scene graph node. For example, when
905 changing the parent of a QQuickRhiItem object so that it then belongs to a
906 different \l QQuickWindow, the scene graph nodes are all dropped and
907 recreated due to the window change. This will also involve dropping and
908 creating a new QQuickRhiItemRenderer.
909
910 Unlike \l QRhiWidget, QQuickRhiItemRenderer has no need to implement
911 additional code paths for releasing (or early-relasing) graphics resources
912 created via QRhi. It is sufficient to release everything in the destructor,
913 or rely on smart pointers.
914 */
915QQuickRhiItemRenderer::~QQuickRhiItemRenderer()
916{
917}
918
919/*!
920 Call this function when the content of the offscreen color buffer should be
921 updated. (i.e. to request that render() is called again; the call will
922 happen at a later point, and note that updates are typically throttled to
923 the presentation rate)
924
925 This function can be called from render() to schedule an update.
926
927 \note This function should be used from inside the renderer. To update
928 the item on the GUI thread, use QQuickRhiItem::update().
929 */
930void QQuickRhiItemRenderer::update()
931{
932 if (node)
933 node->scheduleUpdate();
934}
935
936/*!
937 \return the current QRhi object.
938
939 Must only be called from initialize() and render().
940 */
941QRhi *QQuickRhiItemRenderer::rhi() const
942{
943 return node ? node->m_rhi : nullptr;
944}
945
946/*!
947 \return the texture serving as the color buffer for the item.
948
949 Must only be called from initialize() and render().
950
951 Unlike the depth-stencil buffer and the QRhiRenderTarget, this texture is
952 always available and is managed by the QQuickRhiItem, independent of the
953 value of \l {QQuickRhiItem::}{isAutoRenderTargetEnabled}.
954
955 \note When \l {QQuickRhiItem::}{sampleCount} is larger than 1, and so
956 multisample antialiasing is enabled, the return value is \nullptr. Instead,
957 query the \l QRhiRenderBuffer by calling msaaColorBuffer().
958
959 \note The backing texture size and sample count can also be queried via the
960 QRhiRenderTarget returned from renderTarget(). This can be more convenient
961 and compact than querying from the QRhiTexture or QRhiRenderBuffer, because
962 it works regardless of multisampling is in use or not.
963
964 \sa msaaColorBuffer(), depthStencilBuffer(), renderTarget(), resolveTexture()
965 */
966QRhiTexture *QQuickRhiItemRenderer::colorTexture() const
967{
968 return node ? node->m_colorTexture : nullptr;
969}
970
971/*!
972 \return the renderbuffer serving as the multisample color buffer for the item.
973
974 Must only be called from initialize() and render().
975
976 When \l {QQuickRhiItem::}{sampleCount} is larger than 1, and so multisample
977 antialising is enabled, the returned QRhiRenderBuffer has a matching sample
978 count and serves as the color buffer. Graphics pipelines used to render
979 into this buffer must be created with the same sample count, and the
980 depth-stencil buffer's sample count must match as well. The multisample
981 content is expected to be resolved into the texture returned from
982 resolveTexture(). When \l {QQuickRhiItem::}{isAutoRenderTargetEnabled} is
983 \c true, renderTarget() is set up automatically to do this, by setting up
984 msaaColorBuffer() as the
985 \l{QRhiColorAttachment::renderBuffer()}{renderbuffer} of color attachment 0
986 and resolveTexture() as its
987 \l{QRhiColorAttachment::resolveTexture()}{resolveTexture}.
988
989 When MSAA is not in use, the return value is \nullptr. Use colorTexture()
990 instead then.
991
992 Depending on the underlying 3D graphics API, there may be no practical
993 difference between multisample textures and color renderbuffers with a
994 sample count larger than 1 (QRhi may just map both to the same native
995 resource type). Some older APIs however may differentiate between textures
996 and renderbuffers. In order to support OpenGL ES 3.0, where multisample
997 renderbuffers are available, but multisample textures are not, QQuickRhiItem
998 always performs MSAA by using a multisample QRhiRenderBuffer as the color
999 attachment (and never a multisample QRhiTexture).
1000
1001 \note The backing texture size and sample count can also be queried via the
1002 QRhiRenderTarget returned from renderTarget(). This can be more convenient
1003 and compact than querying from the QRhiTexture or QRhiRenderBuffer, because
1004 it works regardless of multisampling is in use or not.
1005
1006 \sa colorTexture(), depthStencilBuffer(), renderTarget(), resolveTexture()
1007 */
1008QRhiRenderBuffer *QQuickRhiItemRenderer::msaaColorBuffer() const
1009{
1010 return node ? node->m_msaaColorBuffer.get() : nullptr;
1011}
1012
1013/*!
1014 \return the non-multisample texture to which the multisample content is resolved.
1015
1016 The result is \nullptr when multisample antialiasing is not enabled.
1017
1018 Must only be called from initialize() and render().
1019
1020 With MSAA enabled, this is the texture that gets used by the item's
1021 underlying scene graph node when texturing a quad in the main render pass
1022 of Qt Quick. However, the QQuickRhiItemRenderer's rendering must target the
1023 (multisample) QRhiRenderBuffer returned from msaaColorBuffer(). When \l
1024 {QQuickRhiItem::}{isAutoRenderTargetEnabled} is \c true, this is taken care
1025 of by the QRhiRenderTarget returned from renderTarget(). Otherwise, it is
1026 up to the subclass code to correctly configure a render target object with
1027 both the color buffer and resolve textures.
1028
1029 \sa colorTexture()
1030 */
1031QRhiTexture *QQuickRhiItemRenderer::resolveTexture() const
1032{
1033 return node ? node->m_resolveTexture : nullptr;
1034}
1035
1036/*!
1037 \return the depth-stencil buffer used by the item's rendering.
1038
1039 Must only be called from initialize() and render().
1040
1041 Available only when \l {QQuickRhiItem::}{isAutoRenderTargetEnabled} is \c
1042 true. Otherwise the returned value is \nullptr and it is up the
1043 reimplementation of initialize() to create and manage a depth-stencil
1044 buffer and a QRhiTextureRenderTarget.
1045
1046 \sa colorTexture(), renderTarget()
1047 */
1048QRhiRenderBuffer *QQuickRhiItemRenderer::depthStencilBuffer() const
1049{
1050 return node ? node->m_depthStencilBuffer.get() : nullptr;
1051}
1052
1053/*!
1054 \return the render target object that must be used with
1055 \l QRhiCommandBuffer::beginPass() in reimplementations of render().
1056
1057 Must only be called from initialize() and render().
1058
1059 Available only when \l {QQuickRhiItem::}{isAutoRenderTargetEnabled} is \c
1060 true. Otherwise the returned value is \nullptr and it is up the
1061 reimplementation of initialize() to create and manage a depth-stencil
1062 buffer and a QRhiTextureRenderTarget.
1063
1064 When creating \l{QRhiGraphicsPipeline}{graphics pipelines}, a
1065 QRhiRenderPassDescriptor is needed. This can be queried from the returned
1066 QRhiTextureRenderTarget by calling
1067 \l{QRhiTextureRenderTarget::renderPassDescriptor()}{renderPassDescriptor()}.
1068
1069 \note The returned QRhiTextureRenderTarget always reports a
1070 \l{QRhiTextureRenderTarget::}{devicePixelRatio()} of \c 1.
1071 This is because only swapchains and the associated window have a concept of
1072 device pixel ratio, not textures, and the render target here always refers
1073 to a texture. If the on-screen scale factor is relevant for rendering,
1074 query and store it via the item's
1075 \c{window()->effectiveDevicePixelRatio()} in \l synchronize().
1076 When doing so, always prefer using \l{QQuickWindow::}{effectiveDevicePixelRatio()}
1077 over the base class' \l{QWindow::}{devicePixelRatio()}.
1078
1079 \sa colorTexture(), depthStencilBuffer(), QQuickWindow::effectiveDevicePixelRatio()
1080 */
1081QRhiRenderTarget *QQuickRhiItemRenderer::renderTarget() const
1082{
1083 return node ? node->m_renderTarget.get() : nullptr;
1084}
1085
1086/*!
1087 \fn QQuickRhiItemRenderer *QQuickRhiItem::createRenderer()
1088
1089 Reimplement this function to create and return a new instance of a
1090 QQuickRhiItemRenderer subclass.
1091
1092 This function will be called on the rendering thread while the GUI thread
1093 is blocked.
1094 */
1095
1096/*!
1097 \fn void QQuickRhiItemRenderer::initialize(QRhiCommandBuffer *cb)
1098
1099 Called when the item is initialized for the first time, when the
1100 associated texture's size, format, or sample count changes, or when the
1101 QRhi or texture change for any reason. The function is expected to
1102 maintain (create if not yet created, adjust and rebuild if the size has
1103 changed) the graphics resources used by the rendering code in render().
1104
1105 To query the QRhi, QRhiTexture, and other related objects, call rhi(),
1106 colorTexture(), depthStencilBuffer(), and renderTarget().
1107
1108 When the item size changes, the QRhi object, the color buffer texture,
1109 and the depth stencil buffer objects are all the same instances (so the
1110 getters return the same pointers) as before, but the color and
1111 depth/stencil buffers will likely have been rebuilt, meaning the
1112 \l{QRhiTexture::pixelSize()}{size} and the underlying native texture
1113 resource may be different than in the last invocation.
1114
1115 Reimplementations should also be prepared that the QRhi object and the
1116 color buffer texture may change between invocations of this function. For
1117 example, when the item is reparented so that it belongs to a new
1118 QQuickWindow, the the QRhi and all related resources managed by the
1119 QQuickRhiItem will be different instances than before in the subsequent
1120 call to this function. Is is then important that all existing QRhi
1121 resources previously created by the subclass are destroyed because they
1122 belong to the previous QRhi that should not be used anymore.
1123
1124 When \l {QQuickRhiItem::}{isAutoRenderTargetEnabled} is \c true, which is
1125 the default, a depth-stencil QRhiRenderBuffer and a QRhiTextureRenderTarget
1126 associated with the colorTexture() (or msaaColorBuffer()) and the
1127 depth-stencil buffer are created and managed automatically.
1128 Reimplementations of initialize() and render() can query those objects via
1129 depthStencilBuffer() and renderTarget(). When \l
1130 {QQuickRhiItem::}{isAutoRenderTargetEnabled} is set to \c false, these
1131 objects are no longer created and managed automatically. Rather, it will be
1132 up the the initialize() implementation to create buffers and set up the
1133 render target as it sees fit. When manually managing additional color or
1134 depth-stencil attachments for the render target, their size and sample
1135 count must always follow the size and sample count of colorTexture() (or
1136 msaaColorBuffer()), otherwise rendering or 3D API validation errors may
1137 occur.
1138
1139 The subclass-created graphics resources are expected to be released in the
1140 destructor implementation of the subclass.
1141
1142 \a cb is the QRhiCommandBuffer for the current frame. The function is
1143 called with a frame being recorded, but without an active render pass. The
1144 command buffer is provided primarily to allow enqueuing
1145 \l{QRhiCommandBuffer::resourceUpdate()}{resource updates} without deferring
1146 to render().
1147
1148 This function is called on the render thread, if there is one.
1149
1150 \sa render()
1151 */
1152
1153/*!
1154 \fn void QQuickRhiItemRenderer::synchronize(QQuickRhiItem *item)
1155
1156 This function is called on the render thread, if there is one, while the
1157 main/GUI thread is blocked. It is called from
1158 \l{QQuickItem::updatePaintNode()}{the \a {item}'s synchronize step},
1159 and allows reading and writing data belonging to the main and render
1160 threads. Typically property values stored in the QQuickRhiItem are copied
1161 into the QQuickRhiItemRenderer, so that they can be safely read afterwards
1162 in render() when the render and main threads continue to work in parallel.
1163
1164 \sa initialize(), render()
1165 */
1166
1167/*!
1168 \fn void QQuickRhiItemRenderer::render(QRhiCommandBuffer *cb)
1169
1170 Called when the backing color buffer's contents needs updating.
1171
1172 There is always at least one call to initialize() before this function is
1173 called.
1174
1175 To request updates, call \l QQuickItem::update() when calling from QML or
1176 from C++ code on the main/GUI thread (e.g. when in a property setter), or
1177 \l update() when calling from within a QQuickRhiItemRenderer callback.
1178 Calling QQuickRhiItemRenderer's update() from within
1179 render() will lead to triggering updates continuously.
1180
1181 \a cb is the QRhiCommandBuffer for the current frame. The function is
1182 called with a frame being recorded, but without an active render pass.
1183
1184 This function is called on the render thread, if there is one.
1185
1186 \sa initialize(), synchronize()
1187 */
1188
1189QT_END_NAMESPACE
1190
1191#include "moc_qquickrhiitem.cpp"
1192#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:578
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:622
Combined button and popup list for selecting options.