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