7#include <private/qqmlglobal_p.h>
8#include <private/qsgrenderer_p.h>
10QSGRhiLayer::QSGRhiLayer(QSGRenderContext *context)
11 : QSGLayer(*(
new QSGTexturePrivate(
this)))
15 , m_dirtyTexture(
true)
16 , m_multisampling(
false)
18 , m_mirrorHorizontal(
false)
19 , m_mirrorVertical(
true)
21 m_context =
static_cast<QSGDefaultRenderContext *>(context);
22 m_rhi = m_context->rhi();
26QSGRhiLayer::~QSGRhiLayer()
31void QSGRhiLayer::invalidated()
36 m_prevTexture =
nullptr;
42qint64 QSGRhiLayer::comparisonKey()
const
44 return qint64(m_texture);
47bool QSGRhiLayer::hasAlphaChannel()
const
52bool QSGRhiLayer::hasMipmaps()
const
57QRhiTexture *QSGRhiLayer::rhiTexture()
const
59 return m_prevTexture ? m_prevTexture : m_texture;
62void QSGRhiLayer::commitTextureOperations(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
65 Q_UNUSED(resourceUpdates);
68bool QSGRhiLayer::updateTexture()
72 bool doGrab = (m_live || m_grab) && m_dirtyTexture;
77 emit scheduledUpdateCompleted();
83void QSGRhiLayer::setHasMipmaps(
bool mipmap)
85 if (mipmap == m_mipmap)
89 if (m_mipmap && m_texture)
94void QSGRhiLayer::setItem(QSGNode *item)
101 if (m_live && !m_item)
107void QSGRhiLayer::setRect(
const QRectF &logicalRect)
109 if (logicalRect == m_logicalRect)
112 m_logicalRect = logicalRect;
116void QSGRhiLayer::setSize(
const QSize &pixelSize)
118 if (pixelSize == m_pixelSize)
121 const int textureSizeMax = m_rhi->resourceLimit(QRhi::TextureSizeMax);
122 m_pixelSize = pixelSize.boundedTo(QSize(textureSizeMax, textureSizeMax));
124 if (Q_UNLIKELY(m_pixelSize != pixelSize)) {
125 qWarning(
"QSGRhiLayer: Unsupported size requested: [%d, %d]. "
126 "Maximum texture size: %d",
132 if (m_live && m_pixelSize.isNull())
138void QSGRhiLayer::setFormat(Format format)
140 QRhiTexture::Format rhiFormat = QRhiTexture::RGBA8;
143 rhiFormat = QRhiTexture::RGBA16F;
146 rhiFormat = QRhiTexture::RGBA32F;
152 if (rhiFormat == m_format)
155 if (m_rhi->isTextureFormatSupported(rhiFormat)) {
156 m_format = rhiFormat;
159 qWarning(
"QSGRhiLayer: Attempted to set unsupported texture format %d",
int(rhiFormat));
163void QSGRhiLayer::setLive(
bool live)
170 if (m_live && (!m_item || m_pixelSize.isNull()))
176void QSGRhiLayer::scheduleUpdate()
183 emit updateRequested();
186void QSGRhiLayer::setRecursive(
bool recursive)
188 m_recursive = recursive;
191void QSGRhiLayer::setMirrorHorizontal(
bool mirror)
193 m_mirrorHorizontal = mirror;
196void QSGRhiLayer::setMirrorVertical(
bool mirror)
198 m_mirrorVertical = mirror;
201void QSGRhiLayer::markDirtyTexture()
203 m_dirtyTexture =
true;
204 if (m_live || m_grab)
205 emit updateRequested();
208void QSGRhiLayer::releaseResources()
218 delete m_msaaColorBuffer;
219 m_msaaColorBuffer =
nullptr;
221 if (m_prevTexture != m_texture)
226 delete m_secondaryTexture;
227 m_secondaryTexture =
nullptr;
230void QSGRhiLayer::clearMainTexture()
232 std::unique_ptr<QRhiTextureRenderTarget> tempRt(m_rhi->newTextureRenderTarget({ m_texture }));
233 std::unique_ptr<QRhiRenderPassDescriptor> tempRp(tempRt->newCompatibleRenderPassDescriptor());
234 tempRt->setRenderPassDescriptor(tempRp.get());
235 if (tempRt->create()) {
236 m_context->currentFrameCommandBuffer()->beginPass(tempRt.get(), Qt::transparent, { 1.0f, 0 });
237 m_context->currentFrameCommandBuffer()->endPass();
241 tempRt.release()->deleteLater();
242 tempRp.release()->deleteLater();
244 qWarning(
"Failed to clear layer main texture in recursive mode");
248void QSGRhiLayer::grab()
250 if (!m_item || m_pixelSize.isEmpty()) {
252 m_dirtyTexture =
false;
256 int effectiveSamples = m_samples;
258 if (effectiveSamples <= 1)
259 effectiveSamples = m_context->msaaSampleCount();
261 const bool needsNewRt = !m_rt || m_rt->pixelSize() != m_pixelSize || (m_recursive && !m_secondaryTexture) || (m_texture && m_texture->format() != m_format);
262 const bool mipmapSettingChanged = m_texture && m_texture->flags().testFlag(QRhiTexture::MipMapped) != m_mipmap;
263 const bool msaaSettingChanged = (effectiveSamples > 1 && !m_msaaColorBuffer) || (effectiveSamples <= 1 && m_msaaColorBuffer);
265 if (needsNewRt ||mipmapSettingChanged || msaaSettingChanged) {
266 if (effectiveSamples <= 1) {
267 m_multisampling =
false;
269 m_multisampling = m_rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer);
270 if (!m_multisampling)
271 qWarning(
"Layer requested %d samples but multisample renderbuffers are not supported", effectiveSamples);
274 QRhiTexture::Flags textureFlags = QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource;
276 textureFlags |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
285 static bool depthBufferEnabled = qEnvironmentVariableIsEmpty(
"QSG_NO_DEPTH_BUFFER");
287 if (m_recursive && m_texture) {
288 if (m_prevTexture != m_texture)
289 delete m_prevTexture;
290 m_prevTexture = m_texture;
295 if (m_multisampling) {
296 m_msaaColorBuffer = m_rhi->newRenderBuffer(QRhiRenderBuffer::Color, m_pixelSize, effectiveSamples);
297 if (!m_msaaColorBuffer->create()) {
298 qWarning(
"Failed to build multisample color buffer for layer of size %dx%d, sample count %d",
299 m_pixelSize.width(), m_pixelSize.height(), effectiveSamples);
303 m_texture = m_rhi->newTexture(m_format, m_pixelSize, 1, textureFlags);
304 if (!m_texture->create()) {
305 qWarning(
"Failed to build texture for layer of size %dx%d", m_pixelSize.width(), m_pixelSize.height());
309 if (depthBufferEnabled) {
310 m_ds = m_context->getDepthStencilBuffer(m_pixelSize, effectiveSamples);
316 QRhiTextureRenderTargetDescription desc;
317 QRhiColorAttachment color0(m_msaaColorBuffer);
319 m_secondaryTexture = m_rhi->newTexture(m_format, m_pixelSize, 1, textureFlags);
320 if (!m_secondaryTexture->create()) {
321 qWarning(
"Failed to build secondary texture for layer of size %dx%d", m_pixelSize.width(), m_pixelSize.height());
325 color0.setResolveTexture(m_secondaryTexture);
329 color0.setResolveTexture(m_texture);
331 desc.setColorAttachments({ color0 });
332 if (depthBufferEnabled)
333 desc.setDepthStencilBuffer(m_ds->ds);
334 m_rt = m_rhi->newTextureRenderTarget(desc);
335 m_rtRp = m_rt->newCompatibleRenderPassDescriptor();
337 qWarning(
"Failed to build render pass descriptor for layer");
341 m_rt->setRenderPassDescriptor(m_rtRp);
342 if (!m_rt->create()) {
343 qWarning(
"Failed to build texture render target for layer");
348 m_texture = m_rhi->newTexture(m_format, m_pixelSize, 1, textureFlags);
349 if (!m_texture->create()) {
350 qWarning(
"Failed to build texture for layer of size %dx%d", m_pixelSize.width(), m_pixelSize.height());
354 if (depthBufferEnabled) {
355 m_ds = m_context->getDepthStencilBuffer(m_pixelSize, 1);
361 QRhiColorAttachment color0(m_texture);
365 m_secondaryTexture = m_rhi->newTexture(m_format, m_pixelSize, 1, textureFlags);
366 if (!m_secondaryTexture->create()) {
367 qWarning(
"Failed to build texture for layer of size %dx%d", m_pixelSize.width(), m_pixelSize.height());
371 color0.setTexture(m_secondaryTexture);
375 QRhiTextureRenderTargetDescription desc({ color0 });
376 if (depthBufferEnabled)
377 desc.setDepthStencilBuffer(m_ds->ds);
378 m_rt = m_rhi->newTextureRenderTarget(desc);
379 m_rtRp = m_rt->newCompatibleRenderPassDescriptor();
381 qWarning(
"Failed to build render pass descriptor for layer");
385 m_rt->setRenderPassDescriptor(m_rtRp);
386 if (!m_rt->create()) {
387 qWarning(
"Failed to build texture render target for layer");
394 QSGNode *root = m_item;
395 while (root->firstChild() && root->type() != QSGNode::RootNodeType)
396 root = root->firstChild();
397 if (root->type() != QSGNode::RootNodeType)
401 const bool useDepth = m_context->useDepthBufferFor2D();
402 const QSGRendererInterface::RenderMode renderMode = useDepth ? QSGRendererInterface::RenderMode2D
403 : QSGRendererInterface::RenderMode2DNoDepthBuffer;
404 m_renderer = m_context->createRenderer(renderMode);
405 connect(m_renderer, SIGNAL(sceneGraphChanged()),
this, SLOT(markDirtyTexture()));
407 m_renderer->setRootNode(
static_cast<QSGRootNode *>(root));
408 root->markDirty(QSGNode::DirtyForceUpdate);
409 m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate);
414 m_dirtyTexture =
false;
421 if (m_logicalRect.isValid() && !m_pixelSize.isNull()) {
422 qreal actualDpr = std::max(m_pixelSize.width() / m_logicalRect.width(),
423 m_pixelSize.height() / m_logicalRect.height());
424 m_renderer->setDevicePixelRatio(actualDpr);
426 m_renderer->setDevicePixelRatio(m_dpr);
428 m_renderer->setDeviceRect(m_pixelSize);
429 m_renderer->setViewportRect(m_pixelSize);
437 bool frontFaceSwap =
false;
439 if (m_rhi->isYUpInFramebuffer()) {
440 mirrored = QRectF(m_mirrorHorizontal ? m_logicalRect.right() : m_logicalRect.left(),
441 m_mirrorVertical ? m_logicalRect.bottom() : m_logicalRect.top(),
442 m_mirrorHorizontal ? -m_logicalRect.width() : m_logicalRect.width(),
443 m_mirrorVertical ? -m_logicalRect.height() : m_logicalRect.height());
444 if (m_mirrorHorizontal)
445 frontFaceSwap = !frontFaceSwap;
446 if (m_mirrorVertical)
447 frontFaceSwap = !frontFaceSwap;
449 mirrored = QRectF(m_mirrorHorizontal ? m_logicalRect.right() : m_logicalRect.left(),
450 m_mirrorVertical ? m_logicalRect.top() : m_logicalRect.bottom(),
451 m_mirrorHorizontal ? -m_logicalRect.width() : m_logicalRect.width(),
452 m_mirrorVertical ? m_logicalRect.height() : -m_logicalRect.height());
453 if (m_mirrorHorizontal)
454 frontFaceSwap = !frontFaceSwap;
455 if (!m_mirrorVertical)
456 frontFaceSwap = !frontFaceSwap;
459 QSGAbstractRenderer::MatrixTransformFlags matrixFlags;
460 if (!m_rhi->isYUpInNDC())
461 matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
463 m_renderer->setProjectionMatrixToRect(mirrored, matrixFlags);
464 m_renderer->setInvertFrontFace(frontFaceSwap);
465 m_renderer->setClearColor(Qt::transparent);
466 m_renderer->setRenderTarget({ m_rt, m_rtRp, m_context->currentFrameCommandBuffer() });
468 QRhiResourceUpdateBatch *resourceUpdates =
nullptr;
472 m_context->renderNextFrame(m_renderer);
473 if (!resourceUpdates)
474 resourceUpdates = m_rhi->nextResourceUpdateBatch();
475 resourceUpdates->copyTexture(m_texture, m_secondaryTexture);
477 delete m_prevTexture;
478 m_prevTexture =
nullptr;
481 m_context->renderNextFrame(m_renderer);
485 if (!resourceUpdates)
486 resourceUpdates = m_rhi->nextResourceUpdateBatch();
488 resourceUpdates->generateMips(m_texture);
494 m_context->currentFrameCommandBuffer()->resourceUpdate(resourceUpdates);
496 root->markDirty(QSGNode::DirtyForceUpdate);
502QImage QSGRhiLayer::toImage()
const
507 QRhiCommandBuffer *cb = m_context->currentFrameCommandBuffer();
508 QRhiResourceUpdateBatch *resourceUpdates = m_rhi->nextResourceUpdateBatch();
509 QRhiReadbackResult result;
510 QRhiReadbackDescription readbackDesc(m_texture);
511 resourceUpdates->readBackTexture(readbackDesc, &result);
513 cb->resourceUpdate(resourceUpdates);
520 if (result.data.isEmpty()) {
521 qWarning(
"Layer grab failed");
527 QImage::Format imageFormat = QImage::Format_RGBA8888_Premultiplied;
528 if (m_format == QRhiTexture::RGBA16F)
529 imageFormat = QImage::Format_RGBA16FPx4_Premultiplied;
530 else if (m_format == QRhiTexture::RGBA32F)
531 imageFormat = QImage::Format_RGBA32FPx4_Premultiplied;
533 const uchar *p =
reinterpret_cast<
const uchar *>(result.data.constData());
534 return QImage(p, result.pixelSize.width(), result.pixelSize.height(), imageFormat).flipped();
537QRectF QSGRhiLayer::normalizedTextureSubRect()
const
539 return QRectF(m_mirrorHorizontal ? 1 : 0,
540 m_mirrorVertical ? 0 : 1,
541 m_mirrorHorizontal ? -1 : 1,
542 m_mirrorVertical ? 1 : -1);
545#include "moc_qsgrhilayer_p.cpp"