Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qsgrhilayer.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 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 "qsgrhilayer_p.h"
5
6#include <private/qqmlglobal_p.h>
7#include <private/qsgrenderer_p.h>
8#include <private/qsgdefaultrendercontext_p.h>
9
12 , m_mipmap(false)
13 , m_live(true)
14 , m_recursive(false)
15 , m_dirtyTexture(true)
16 , m_multisampling(false)
17 , m_grab(false)
18 , m_mirrorHorizontal(false)
19 , m_mirrorVertical(true)
20{
21 m_context = static_cast<QSGDefaultRenderContext *>(context);
22 m_rhi = m_context->rhi();
23 Q_ASSERT(m_rhi);
24}
25
30
32{
33 releaseResources();
34
35 delete m_renderer;
36 m_renderer = nullptr;
37}
38
40{
41 return qint64(m_texture);
42}
43
45{
46 return true;
47}
48
50{
51 return m_mipmap;
52}
53
55{
56 return m_texture;
57}
58
60{
61 Q_UNUSED(rhi);
62 Q_UNUSED(resourceUpdates);
63}
64
66{
67 // called during the preprocess phase, outside of frame rendering -> good
68
69 bool doGrab = (m_live || m_grab) && m_dirtyTexture;
70 if (doGrab)
71 grab();
72
73 if (m_grab)
75
76 m_grab = false;
77 return doGrab;
78}
79
81{
82 if (mipmap == m_mipmap)
83 return;
84
85 m_mipmap = mipmap;
86 if (m_mipmap && m_texture)
88}
89
90
92{
93 if (item == m_item)
94 return;
95
96 m_item = item;
97
98 if (m_live && !m_item)
99 releaseResources();
100
102}
103
104void QSGRhiLayer::setRect(const QRectF &logicalRect)
105{
106 if (logicalRect == m_logicalRect)
107 return;
108
109 m_logicalRect = logicalRect;
111}
112
113void QSGRhiLayer::setSize(const QSize &pixelSize)
114{
115 if (pixelSize == m_pixelSize)
116 return;
117
118 m_pixelSize = pixelSize;
119
120 if (m_live && m_pixelSize.isNull())
121 releaseResources();
122
124}
125
127{
129 switch (format) {
130 case RGBA16F:
131 rhiFormat = QRhiTexture::RGBA16F;
132 break;
133 case RGBA32F:
134 rhiFormat = QRhiTexture::RGBA32F;
135 break;
136 default:
137 break;
138 }
139
140 if (rhiFormat == m_format)
141 return;
142
143 if (m_rhi->isTextureFormatSupported(rhiFormat)) {
144 m_format = rhiFormat;
146 } else {
147 qWarning("QSGRhiLayer: Attempted to set unsupported texture format %d", int(rhiFormat));
148 }
149}
150
151void QSGRhiLayer::setLive(bool live)
152{
153 if (live == m_live)
154 return;
155
156 m_live = live;
157
158 if (m_live && (!m_item || m_pixelSize.isNull()))
159 releaseResources();
160
162}
163
165{
166 if (m_grab)
167 return;
168
169 m_grab = true;
170 if (m_dirtyTexture)
172}
173
174void QSGRhiLayer::setRecursive(bool recursive)
175{
176 m_recursive = recursive;
177}
178
180{
181 m_mirrorHorizontal = mirror;
182}
183
185{
186 m_mirrorVertical = mirror;
187}
188
190{
191 m_dirtyTexture = true;
192 if (m_live || m_grab)
194}
195
196void QSGRhiLayer::releaseResources()
197{
198 delete m_rt;
199 m_rt = nullptr;
200
201 delete m_rtRp;
202 m_rtRp = nullptr;
203
204 delete m_ds;
205 m_ds = nullptr;
206
207 delete m_msaaColorBuffer;
208 m_msaaColorBuffer = nullptr;
209
210 delete m_texture;
211 m_texture = nullptr;
212
213 delete m_secondaryTexture;
214 m_secondaryTexture = nullptr;
215}
216
217void QSGRhiLayer::grab()
218{
219 if (!m_item || m_pixelSize.isEmpty()) {
220 releaseResources();
221 m_dirtyTexture = false;
222 return;
223 }
224
225 int effectiveSamples = m_samples;
226 // if no layer.samples was provided use the window's msaa setting
227 if (effectiveSamples <= 1)
228 effectiveSamples = m_context->msaaSampleCount();
229
230 const bool needsNewRt = !m_rt || m_rt->pixelSize() != m_pixelSize || (m_recursive && !m_secondaryTexture) || (m_texture && m_texture->format() != m_format);
231 const bool mipmapSettingChanged = m_texture && m_texture->flags().testFlag(QRhiTexture::MipMapped) != m_mipmap;
232 const bool msaaSettingChanged = (effectiveSamples > 1 && !m_msaaColorBuffer) || (effectiveSamples <= 1 && m_msaaColorBuffer);
233
234 if (needsNewRt ||mipmapSettingChanged || msaaSettingChanged) {
235 if (effectiveSamples <= 1) {
236 m_multisampling = false;
237 } else {
238 m_multisampling = m_rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer);
239 if (!m_multisampling)
240 qWarning("Layer requested %d samples but multisample renderbuffers are not supported", effectiveSamples);
241 }
242
243 QRhiTexture::Flags textureFlags = QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource;
244 if (m_mipmap)
246
247 // Not the same as m_context->useDepthBufferFor2D(), only the env.var
248 // is to be checked here. Consider a layer with a non-offscreen View3D
249 // in it. That still needs a depth buffer, even when the 2D content
250 // renders without relying on it (i.e. RenderMode2DNoDepthBuffer does
251 // not imply not having a depth/stencil attachment for the render
252 // target! The env.var serves as a hard switch, on the other hand, and
253 // that will likely break 3D for instance but that's fine)
254 static bool depthBufferEnabled = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
255
256 if (m_multisampling) {
257 releaseResources();
258 m_msaaColorBuffer = m_rhi->newRenderBuffer(QRhiRenderBuffer::Color, m_pixelSize, effectiveSamples);
259 if (!m_msaaColorBuffer->create()) {
260 qWarning("Failed to build multisample color buffer for layer of size %dx%d, sample count %d",
261 m_pixelSize.width(), m_pixelSize.height(), effectiveSamples);
262 releaseResources();
263 return;
264 }
265 m_texture = m_rhi->newTexture(m_format, m_pixelSize, 1, textureFlags);
266 if (!m_texture->create()) {
267 qWarning("Failed to build texture for layer of size %dx%d", m_pixelSize.width(), m_pixelSize.height());
268 releaseResources();
269 return;
270 }
271 if (depthBufferEnabled) {
272 m_ds = m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, m_pixelSize, effectiveSamples);
273 if (!m_ds->create()) {
274 qWarning("Failed to build depth-stencil buffer for layer");
275 releaseResources();
276 return;
277 }
278 }
280 QRhiColorAttachment color0(m_msaaColorBuffer);
281 color0.setResolveTexture(m_texture);
282 desc.setColorAttachments({ color0 });
283 if (depthBufferEnabled)
284 desc.setDepthStencilBuffer(m_ds);
285 m_rt = m_rhi->newTextureRenderTarget(desc);
286 m_rtRp = m_rt->newCompatibleRenderPassDescriptor();
287 if (!m_rtRp) {
288 qWarning("Failed to build render pass descriptor for layer");
289 releaseResources();
290 return;
291 }
292 m_rt->setRenderPassDescriptor(m_rtRp);
293 if (!m_rt->create()) {
294 qWarning("Failed to build texture render target for layer");
295 releaseResources();
296 return;
297 }
298 } else {
299 releaseResources();
300 m_texture = m_rhi->newTexture(m_format, m_pixelSize, 1, textureFlags);
301 if (!m_texture->create()) {
302 qWarning("Failed to build texture for layer of size %dx%d", m_pixelSize.width(), m_pixelSize.height());
303 releaseResources();
304 return;
305 }
306 if (depthBufferEnabled) {
307 m_ds = m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, m_pixelSize);
308 if (!m_ds->create()) {
309 qWarning("Failed to build depth-stencil buffer for layer");
310 releaseResources();
311 return;
312 }
313 }
314 QRhiColorAttachment color0(m_texture);
315 if (m_recursive) {
316 // Here rt is associated with m_secondaryTexture instead of m_texture.
317 // We will issue a copy to m_texture afterwards.
318 m_secondaryTexture = m_rhi->newTexture(m_format, m_pixelSize, 1, textureFlags);
319 if (!m_secondaryTexture->create()) {
320 qWarning("Failed to build texture for layer of size %dx%d", m_pixelSize.width(), m_pixelSize.height());
321 releaseResources();
322 return;
323 }
324 color0.setTexture(m_secondaryTexture);
325 }
326 if (depthBufferEnabled)
327 m_rt = m_rhi->newTextureRenderTarget({ color0, m_ds });
328 else
329 m_rt = m_rhi->newTextureRenderTarget({ color0 });
330 m_rtRp = m_rt->newCompatibleRenderPassDescriptor();
331 if (!m_rtRp) {
332 qWarning("Failed to build render pass descriptor for layer");
333 releaseResources();
334 return;
335 }
336 m_rt->setRenderPassDescriptor(m_rtRp);
337 if (!m_rt->create()) {
338 qWarning("Failed to build texture render target for layer");
339 releaseResources();
340 return;
341 }
342 }
343 }
344
345 QSGNode *root = m_item;
346 while (root->firstChild() && root->type() != QSGNode::RootNodeType)
347 root = root->firstChild();
348 if (root->type() != QSGNode::RootNodeType)
349 return;
350
351 if (!m_renderer) {
352 const bool useDepth = m_context->useDepthBufferFor2D();
355 m_renderer = m_context->createRenderer(renderMode);
356 connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture()));
357 }
358 m_renderer->setRootNode(static_cast<QSGRootNode *>(root));
359 root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip and opacity update.
360 m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate); // Force render list update.
361
362 // This must not be moved. The flag must be reset only after the
363 // nodeChanged otherwise we end up with constantly updating even when the
364 // layer contents do not change.
365 m_dirtyTexture = false;
366
367 m_renderer->setDevicePixelRatio(m_dpr);
368 m_renderer->setDeviceRect(m_pixelSize);
369 m_renderer->setViewportRect(m_pixelSize);
370 QRectF mirrored; // in logical coordinates (no dpr) since this gets passed to setProjectionMatrixToRect()
371 if (m_rhi->isYUpInFramebuffer()) {
372 mirrored = QRectF(m_mirrorHorizontal ? m_logicalRect.right() : m_logicalRect.left(),
373 m_mirrorVertical ? m_logicalRect.bottom() : m_logicalRect.top(),
374 m_mirrorHorizontal ? -m_logicalRect.width() : m_logicalRect.width(),
375 m_mirrorVertical ? -m_logicalRect.height() : m_logicalRect.height());
376 } else {
377 mirrored = QRectF(m_mirrorHorizontal ? m_logicalRect.right() : m_logicalRect.left(),
378 m_mirrorVertical ? m_logicalRect.top() : m_logicalRect.bottom(),
379 m_mirrorHorizontal ? -m_logicalRect.width() : m_logicalRect.width(),
380 m_mirrorVertical ? m_logicalRect.height() : -m_logicalRect.height());
381 }
382 QSGAbstractRenderer::MatrixTransformFlags matrixFlags;
383 if (!m_rhi->isYUpInNDC())
385 m_renderer->setProjectionMatrixToRect(mirrored, matrixFlags);
386 m_renderer->setClearColor(Qt::transparent);
387 m_renderer->setRenderTarget({ m_rt, m_rtRp, m_context->currentFrameCommandBuffer() });
388
389 QRhiResourceUpdateBatch *resourceUpdates = nullptr;
390
391 // render with our own "sub-renderer" (this will just add a render pass to the command buffer)
392 if (m_multisampling) {
393 m_context->renderNextFrame(m_renderer);
394 } else {
395 if (m_recursive) {
396 m_context->renderNextFrame(m_renderer);
397 if (!resourceUpdates)
398 resourceUpdates = m_rhi->nextResourceUpdateBatch();
399 resourceUpdates->copyTexture(m_texture, m_secondaryTexture);
400 } else {
401 m_context->renderNextFrame(m_renderer);
402 }
403 }
404
405 if (m_mipmap) {
406 if (!resourceUpdates)
407 resourceUpdates = m_rhi->nextResourceUpdateBatch();
408 // going to be expensive - if done every frame - but the user asked for it...
409 resourceUpdates->generateMips(m_texture);
410 }
411
412 // Do not defer committing the resource updates to the main pass - with
413 // multiple layers there can be dependencies, so the texture should be
414 // usable once we return.
415 m_context->currentFrameCommandBuffer()->resourceUpdate(resourceUpdates);
416
417 root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip, opacity and render list update.
418
419 if (m_recursive)
420 markDirtyTexture(); // Continuously update if 'live' and 'recursive'.
421}
422
424{
425 if (!m_texture)
426 return QImage();
427
429 QRhiResourceUpdateBatch *resourceUpdates = m_rhi->nextResourceUpdateBatch();
431 QRhiReadbackDescription readbackDesc(m_texture);
432 resourceUpdates->readBackTexture(readbackDesc, &result);
433
434 cb->resourceUpdate(resourceUpdates);
435
436 // Inefficient but what can you do. We need the results right away. This is
437 // not something that occurs in a normal rendering process anyway. (used by
438 // QQuickItem's grabToImage).
439 m_rhi->finish();
440
441 if (result.data.isEmpty()) {
442 qWarning("Layer grab failed");
443 return QImage();
444 }
445
446 // There is little room for negotiation here, the texture is one of the formats from setFormat.
447 // Also, Quick is always premultiplied alpha.
449 if (m_format == QRhiTexture::RGBA16F)
451 else if (m_format == QRhiTexture::RGBA32F)
453
454 const uchar *p = reinterpret_cast<const uchar *>(result.data.constData());
455 return QImage(p, result.pixelSize.width(), result.pixelSize.height(), imageFormat).mirrored();
456}
457
459{
460 return QRectF(m_mirrorHorizontal ? 1 : 0,
461 m_mirrorVertical ? 0 : 1,
462 m_mirrorHorizontal ? -1 : 1,
463 m_mirrorVertical ? 1 : -1);
464}
465
466#include "moc_qsgrhilayer_p.cpp"
\inmodule QtGui
Definition qimage.h:37
Format
The following image formats are available in Qt.
Definition qimage.h:41
@ Format_RGBA32FPx4_Premultiplied
Definition qimage.h:77
@ Format_RGBA8888_Premultiplied
Definition qimage.h:60
@ Format_RGBA16FPx4_Premultiplied
Definition qimage.h:74
QImage mirrored(bool horizontally=false, bool vertically=true) const &
Definition qimage.h:219
\inmodule QtCore\reentrant
Definition qrect.h:484
constexpr qreal right() const noexcept
Returns the x-coordinate of the rectangle's right edge.
Definition qrect.h:499
\inmodule QtGui
Definition qrhi.h:576
\inmodule QtGui
Definition qrhi.h:1651
void resourceUpdate(QRhiResourceUpdateBatch *resourceUpdates)
Sometimes committing resource updates is necessary or just more convenient without starting a render ...
Definition qrhi.cpp:9384
\inmodule QtGui
Definition qrhi.h:777
virtual bool create()=0
Creates the corresponding native graphics resources.
void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc)
Sets the QRhiRenderPassDescriptor desc for use with this render target.
Definition qrhi.h:1165
virtual QSize pixelSize() const =0
\inmodule QtGui
Definition qrhi.h:1731
void generateMips(QRhiTexture *tex)
Enqueues a mipmap generation operation for the specified texture tex.
Definition qrhi.cpp:9210
void copyTexture(QRhiTexture *dst, QRhiTexture *src, const QRhiTextureCopyDescription &desc=QRhiTextureCopyDescription())
Enqueues a texture-to-texture copy operation from src into dst as described by desc.
Definition qrhi.cpp:9116
virtual QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor()=0
virtual bool create()=0
Creates the corresponding native graphics resources.
\inmodule QtGui
Definition qrhi.h:895
Format format() const
Definition qrhi.h:972
@ UsedAsTransferSource
Definition qrhi.h:902
@ UsedWithGenerateMips
Definition qrhi.h:903
@ MipMapped
Definition qrhi.h:900
@ RenderTarget
Definition qrhi.h:898
virtual bool create()=0
Creates the corresponding native graphics resources.
Format
Specifies the texture format.
Definition qrhi.h:914
@ RGBA32F
Definition qrhi.h:926
@ RGBA16F
Definition qrhi.h:925
Flags flags() const
Definition qrhi.h:992
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1804
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags={}) const
Definition qrhi.cpp:10102
bool isYUpInFramebuffer() const
Definition qrhi.cpp:10030
bool isYUpInNDC() const
Definition qrhi.cpp:10044
bool isFeatureSupported(QRhi::Feature feature) const
Definition qrhi.cpp:10110
QRhiRenderBuffer * newRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount=1, QRhiRenderBuffer::Flags flags={}, QRhiTexture::Format backingFormatHint=QRhiTexture::UnknownFormat)
Definition qrhi.cpp:10535
QRhi::FrameOpResult finish()
Waits for any work on the graphics queue (where applicable) to complete, then executes all deferred o...
Definition qrhi.cpp:10936
QRhiTextureRenderTarget * newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, QRhiTextureRenderTarget::Flags flags={})
Definition qrhi.cpp:10682
QRhiTexture * newTexture(QRhiTexture::Format format, const QSize &pixelSize, int sampleCount=1, QRhiTexture::Flags flags={})
Definition qrhi.cpp:10562
@ MultisampleRenderBuffer
Definition qrhi.h:1833
QRhiResourceUpdateBatch * nextResourceUpdateBatch()
Definition qrhi.cpp:9252
void setViewportRect(const QRect &rect)
Sets rect as the geometry of the viewport to render on the surface.
void setProjectionMatrixToRect(const QRectF &rect)
Convenience method that calls setProjectionMatrix() with an orthographic matrix generated from rect.
void setDeviceRect(const QRect &rect)
Sets rect as the geometry of the surface being rendered to.
void setClearColor(const QColor &color)
Sets the color to clear the framebuffer.
void setRootNode(QSGRootNode *node)
Sets the node as the root of the QSGNode scene that you want to render.
QSGRenderer * createRenderer(QSGRendererInterface::RenderMode renderMode=QSGRendererInterface::RenderMode2D) override
void renderNextFrame(QSGRenderer *renderer) override
QRhiCommandBuffer * currentFrameCommandBuffer() const
void updateRequested()
void scheduledUpdateCompleted()
\group qtquick-scenegraph-nodes \title Qt Quick Scene Graph Node classes
Definition qsgnode.h:37
@ DirtyForceUpdate
Definition qsgnode.h:78
@ RootNodeType
Definition qsgnode.h:45
QSGNode * firstChild() const
Returns the first child of this node.
Definition qsgnode.h:105
void markDirty(DirtyState bits)
Notifies all connected renderers that the node has dirty bits.
Definition qsgnode.cpp:624
NodeType type() const
Returns the type of this node.
Definition qsgnode.h:110
RenderMode
\value RenderMode2D Normal 2D rendering \value RenderMode2DNoDepthBuffer Normal 2D rendering with dep...
void setRenderTarget(const QSGRenderTarget &rt)
void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override
Updates internal data structures and emits the sceneGraphChanged() signal.
void setDevicePixelRatio(qreal ratio)
void setRect(const QRectF &logicalRect) override
bool hasMipmaps() const override
Returns true if the texture data contains mipmap levels.
bool hasAlphaChannel() const override
Returns true if the texture data contains an alpha channel.
void setLive(bool live) override
void setItem(QSGNode *item) override
void setFormat(Format format) override
void commitTextureOperations(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates) override
Call this function to enqueue image upload operations to resourceUpdates, in case there are any pendi...
QImage toImage() const override
qint64 comparisonKey() const override
Returns a key suitable for comparing textures.
void setMirrorVertical(bool mirror) override
void setMirrorHorizontal(bool mirror) override
void scheduleUpdate() override
void setRecursive(bool recursive) override
void invalidated() override
void markDirtyTexture() override
QSGRhiLayer(QSGRenderContext *context)
bool updateTexture() override
Call this function to explicitly update the dynamic texture.
QRectF normalizedTextureSubRect() const override
Returns the rectangle inside textureSize() that this texture represents in normalized coordinates.
void setSize(const QSize &pixelSize) override
QRhiTexture * rhiTexture() const override
void setHasMipmaps(bool mipmap) override
The QSGRootNode is the toplevel root of any scene graph.
Definition qsgnode.h:259
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
constexpr bool isNull() const noexcept
Returns true if both the width and height is 0; otherwise returns false.
Definition qsize.h:121
constexpr bool isEmpty() const noexcept
Returns true if either of the width and height is less than or equal to 0; otherwise returns false.
Definition qsize.h:124
#define this
Definition dialogs.cpp:9
@ transparent
Definition qnamespace.h:47
@ color0
Definition qnamespace.h:28
static void * context
#define qWarning
Definition qlogging.h:166
#define SLOT(a)
Definition qobjectdefs.h:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLint GLsizei GLsizei height
GLdouble GLdouble GLdouble GLdouble top
GLint GLsizei width
GLint left
GLint GLint bottom
GLint GLsizei GLsizei GLenum format
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int(* cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
Q_CORE_EXPORT bool qEnvironmentVariableIsEmpty(const char *varName) noexcept
#define emit
#define Q_UNUSED(x)
unsigned char uchar
Definition qtypes.h:32
long long qint64
Definition qtypes.h:60
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
QGraphicsItem * item
\inmodule QtGui
Definition qrhi.h:1723