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 | |
4 | #include "qquickrhiitem_p.h" |
5 | |
6 | QT_BEGIN_NAMESPACE |
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 | |
167 | QQuickRhiItemNode::QQuickRhiItemNode(QQuickRhiItem *item) |
168 | : m_item(item) |
169 | { |
170 | m_window = m_item->window(); |
171 | connect(sender: m_window, signal: &QQuickWindow::beforeRendering, context: this, slot: &QQuickRhiItemNode::render, |
172 | type: Qt::DirectConnection); |
173 | connect(sender: m_window, signal: &QQuickWindow::screenChanged, context: this, slot: [this]() { |
174 | if (m_window->effectiveDevicePixelRatio() != m_dpr) |
175 | m_item->update(); |
176 | }, type: Qt::DirectConnection); |
177 | } |
178 | |
179 | QSGTexture *QQuickRhiItemNode::texture() const |
180 | { |
181 | return m_sgTexture.get(); |
182 | } |
183 | |
184 | void QQuickRhiItemNode::resetColorBufferObjects() |
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 | |
195 | void QQuickRhiItemNode::resetRenderTargetObjects() |
196 | { |
197 | m_renderTarget.reset(); |
198 | m_renderPassDescriptor.reset(); |
199 | m_depthStencilBuffer.reset(); |
200 | } |
201 | |
202 | void QQuickRhiItemNode::sync() |
203 | { |
204 | if (!m_rhi) { |
205 | m_rhi = m_window->rhi(); |
206 | if (!m_rhi) { |
207 | qWarning(msg: "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(limit: QRhi::TextureSizeMin); |
214 | const int maxTexSize = m_rhi->resourceLimit(limit: 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(a: maxTexSize, b: qMax(a: minTexSize, b: newSize.width()))); |
222 | newSize.setHeight(qMin(a: maxTexSize, b: qMax(a: minTexSize, b: newSize.height()))); |
223 | |
224 | if (m_colorTexture) { |
225 | if (m_colorTexture->format() != itemD->rhiTextureFormat |
226 | || m_colorTexture->sampleCount() != itemD->samples) |
227 | { |
228 | resetColorBufferObjects(); |
229 | resetRenderTargetObjects(); |
230 | } |
231 | } |
232 | |
233 | if (m_msaaColorBuffer) { |
234 | if (m_msaaColorBuffer->backingFormat() != itemD->rhiTextureFormat |
235 | || m_msaaColorBuffer->sampleCount() != itemD->samples) |
236 | { |
237 | resetColorBufferObjects(); |
238 | resetRenderTargetObjects(); |
239 | } |
240 | } |
241 | |
242 | if (m_sgTexture && m_sgTexture->hasAlphaChannel() != itemD->blend) { |
243 | resetColorBufferObjects(); |
244 | resetRenderTargetObjects(); |
245 | } |
246 | |
247 | if (!m_colorTexture && itemD->samples <= 1) { |
248 | if (!m_rhi->isTextureFormatSupported(format: itemD->rhiTextureFormat)) { |
249 | qWarning(msg: "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(format: itemD->rhiTextureFormat, pixelSize: newSize, sampleCount: itemD->samples, |
253 | flags: QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource); |
254 | if (!m_colorTexture->create()) { |
255 | qWarning(msg: "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(feature: QRhi::MultisampleRenderBuffer)) { |
265 | qWarning(msg: "QQuickRhiItem: Multisample renderbuffers are reported as unsupported; " |
266 | "sample count %d will not work as expected", itemD->samples); |
267 | } |
268 | if (!m_rhi->isTextureFormatSupported(format: itemD->rhiTextureFormat)) { |
269 | qWarning(msg: "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(p: m_rhi->newRenderBuffer(type: QRhiRenderBuffer::Color, pixelSize: newSize, sampleCount: itemD->samples, |
273 | flags: {}, backingFormatHint: itemD->rhiTextureFormat)); |
274 | if (!m_msaaColorBuffer->create()) { |
275 | qWarning(msg: "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(format: itemD->rhiTextureFormat, pixelSize: newSize, sampleCount: 1, |
282 | flags: QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource); |
283 | if (!m_resolveTexture->create()) { |
284 | qWarning(msg: "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(msg: "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(msg: "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(msg: "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(p: m_window->createTextureFromRhiTexture(texture: 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(p: m_rhi->newRenderBuffer(type: QRhiRenderBuffer::DepthStencil, pixelSize, sampleCount: itemD->samples)); |
328 | if (!m_depthStencilBuffer->create()) { |
329 | qWarning(msg: "Failed to create depth-stencil buffer for QQuickRhiItem"); |
330 | resetRenderTargetObjects(); |
331 | return; |
332 | } |
333 | } else if (m_depthStencilBuffer->pixelSize() != pixelSize) { |
334 | m_depthStencilBuffer->setPixelSize(pixelSize); |
335 | if (!m_depthStencilBuffer->create()) { |
336 | qWarning(msg: "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(p: m_rhi->newTextureRenderTarget(desc: rtDesc)); |
350 | m_renderPassDescriptor.reset(p: m_renderTarget->newCompatibleRenderPassDescriptor()); |
351 | m_renderTarget->setRenderPassDescriptor(m_renderPassDescriptor.get()); |
352 | if (!m_renderTarget->create()) { |
353 | qWarning(msg: "Failed to create render target for QQuickRhiitem"); |
354 | resetRenderTargetObjects(); |
355 | return; |
356 | } |
357 | } |
358 | } else { |
359 | resetRenderTargetObjects(); |
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(item: m_item); |
372 | } |
373 | |
374 | QRhiCommandBuffer *QQuickRhiItemNode::queryCommandBuffer() |
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(window: m_window, resource: QSGRendererInterface::RhiRedirectCommandBuffer)); |
384 | |
385 | if (!cb) { |
386 | qWarning(msg: "QQuickRhiItem: Neither swapchain nor redirected command buffer are available."); |
387 | return nullptr; |
388 | } |
389 | |
390 | return cb; |
391 | } |
392 | |
393 | void 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(bits: QSGNode::DirtyMaterial); |
408 | emit textureChanged(); |
409 | } |
410 | |
411 | /*! |
412 | Constructs a new QQuickRhiItem with the given \a parent. |
413 | */ |
414 | QQuickRhiItem::QQuickRhiItem(QQuickItem *parent) |
415 | : QQuickItem(*new QQuickRhiItemPrivate, parent) |
416 | { |
417 | setFlag(flag: ItemHasContents); |
418 | } |
419 | |
420 | /*! |
421 | Destructor. |
422 | */ |
423 | QQuickRhiItem::~QQuickRhiItem() |
424 | { |
425 | } |
426 | |
427 | /*! |
428 | \internal |
429 | */ |
430 | QSGNode *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(msg: "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(x: 0, y: 0, w: qMax<int>(a: 0, b: width()), h: qMax<int>(a: 0, b: height())); |
478 | |
479 | n->scheduleUpdate(); |
480 | |
481 | return n; |
482 | } |
483 | |
484 | /*! |
485 | \reimp |
486 | */ |
487 | bool QQuickRhiItem::event(QEvent *e) |
488 | { |
489 | return QQuickItem::event(e); |
490 | } |
491 | |
492 | /*! |
493 | \reimp |
494 | */ |
495 | void 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 | */ |
505 | void 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 | |
513 | void 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 | */ |
524 | bool QQuickRhiItem::isTextureProvider() const |
525 | { |
526 | return true; |
527 | } |
528 | |
529 | /*! |
530 | \reimp |
531 | */ |
532 | QSGTextureProvider *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 | |
579 | int QQuickRhiItem::sampleCount() const |
580 | { |
581 | Q_D(const QQuickRhiItem); |
582 | return d->samples; |
583 | } |
584 | |
585 | void 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 | |
616 | QQuickRhiItem::TextureFormat QQuickRhiItem::colorBufferFormat() const |
617 | { |
618 | Q_D(const QQuickRhiItem); |
619 | return d->itemTextureFormat; |
620 | } |
621 | |
622 | void 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 | */ |
654 | bool 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 | */ |
672 | void 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 | |
693 | bool QQuickRhiItem::isMirrorVerticallyEnabled() const |
694 | { |
695 | Q_D(const QQuickRhiItem); |
696 | return d->mirrorVertically; |
697 | } |
698 | |
699 | void 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 | */ |
729 | int QQuickRhiItem::fixedColorBufferWidth() const |
730 | { |
731 | Q_D(const QQuickRhiItem); |
732 | return d->fixedTextureWidth; |
733 | } |
734 | |
735 | void 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 | |
765 | int QQuickRhiItem::fixedColorBufferHeight() const |
766 | { |
767 | Q_D(const QQuickRhiItem); |
768 | return d->fixedTextureHeight; |
769 | } |
770 | |
771 | void 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 | |
803 | QSize 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 | |
836 | bool QQuickRhiItem::alphaBlending() const |
837 | { |
838 | Q_D(const QQuickRhiItem); |
839 | return d->blend; |
840 | } |
841 | |
842 | void 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 | */ |
861 | QQuickRhiItemRenderer::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 | */ |
884 | QQuickRhiItemRenderer::~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 | */ |
899 | void 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 | */ |
910 | QRhi *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 | */ |
935 | QRhiTexture *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 | */ |
977 | QRhiRenderBuffer *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 | */ |
1000 | QRhiTexture *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 | */ |
1017 | QRhiRenderBuffer *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 | */ |
1050 | QRhiRenderTarget *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 | |
1158 | QT_END_NAMESPACE |
1159 |
Definitions
- QQuickRhiItemNode
- texture
- resetColorBufferObjects
- resetRenderTargetObjects
- sync
- queryCommandBuffer
- render
- QQuickRhiItem
- ~QQuickRhiItem
- updatePaintNode
- event
- geometryChange
- releaseResources
- invalidateSceneGraph
- isTextureProvider
- textureProvider
- sampleCount
- setSampleCount
- colorBufferFormat
- setColorBufferFormat
- isAutoRenderTargetEnabled
- setAutoRenderTarget
- isMirrorVerticallyEnabled
- setMirrorVertically
- fixedColorBufferWidth
- setFixedColorBufferWidth
- fixedColorBufferHeight
- setFixedColorBufferHeight
- effectiveColorBufferSize
- alphaBlending
- setAlphaBlending
- QQuickRhiItemRenderer
- ~QQuickRhiItemRenderer
- update
- rhi
- colorTexture
- msaaColorBuffer
- resolveTexture
- depthStencilBuffer
Learn to use CMake with our Intro Training
Find out more