6#include <private/qqmlglobal_p.h>
7#include <private/qsgrenderer_p.h>
9QSGRhiLayer::QSGRhiLayer(QSGRenderContext *context)
10 : QSGLayer(*(
new QSGTexturePrivate(
this)))
14 , m_dirtyTexture(
true)
15 , m_multisampling(
false)
17 , m_mirrorHorizontal(
false)
18 , m_mirrorVertical(
true)
20 m_context =
static_cast<QSGDefaultRenderContext *>(context);
21 m_rhi = m_context->rhi();
25QSGRhiLayer::~QSGRhiLayer()
30void QSGRhiLayer::invalidated()
35 m_prevTexture =
nullptr;
41qint64 QSGRhiLayer::comparisonKey()
const
43 return qint64(m_texture);
46bool QSGRhiLayer::hasAlphaChannel()
const
51bool QSGRhiLayer::hasMipmaps()
const
56QRhiTexture *QSGRhiLayer::rhiTexture()
const
58 return m_prevTexture ? m_prevTexture : m_texture;
61void QSGRhiLayer::commitTextureOperations(QRhi *rhi, QRhiResourceUpdateBatch *resourceUpdates)
64 Q_UNUSED(resourceUpdates);
67bool QSGRhiLayer::updateTexture()
71 bool doGrab = (m_live || m_grab) && m_dirtyTexture;
76 emit scheduledUpdateCompleted();
82void QSGRhiLayer::setHasMipmaps(
bool mipmap)
84 if (mipmap == m_mipmap)
88 if (m_mipmap && m_texture)
93void QSGRhiLayer::setItem(QSGNode *item)
100 if (m_live && !m_item)
106void QSGRhiLayer::setRect(
const QRectF &logicalRect)
108 if (logicalRect == m_logicalRect)
111 m_logicalRect = logicalRect;
115void QSGRhiLayer::setSize(
const QSize &pixelSize)
117 if (pixelSize == m_pixelSize)
120 const int textureSizeMax = m_rhi->resourceLimit(QRhi::TextureSizeMax);
121 m_pixelSize = pixelSize.boundedTo(QSize(textureSizeMax, textureSizeMax));
123 if (Q_UNLIKELY(m_pixelSize != pixelSize)) {
124 qWarning(
"QSGRhiLayer: Unsupported size requested: [%d, %d]. "
125 "Maximum texture size: %d",
131 if (m_live && m_pixelSize.isNull())
137void QSGRhiLayer::setFormat(Format format)
139 QRhiTexture::Format rhiFormat = QRhiTexture::RGBA8;
142 rhiFormat = QRhiTexture::RGBA16F;
145 rhiFormat = QRhiTexture::RGBA32F;
151 if (rhiFormat == m_format)
154 if (m_rhi->isTextureFormatSupported(rhiFormat)) {
155 m_format = rhiFormat;
158 qWarning(
"QSGRhiLayer: Attempted to set unsupported texture format %d",
int(rhiFormat));
162void QSGRhiLayer::setLive(
bool live)
169 if (m_live && (!m_item || m_pixelSize.isNull()))
175void QSGRhiLayer::scheduleUpdate()
182 emit updateRequested();
185void QSGRhiLayer::setRecursive(
bool recursive)
187 m_recursive = recursive;
190void QSGRhiLayer::setMirrorHorizontal(
bool mirror)
192 m_mirrorHorizontal = mirror;
195void QSGRhiLayer::setMirrorVertical(
bool mirror)
197 m_mirrorVertical = mirror;
200void QSGRhiLayer::markDirtyTexture()
202 m_dirtyTexture =
true;
203 if (m_live || m_grab)
204 emit updateRequested();
207void QSGRhiLayer::releaseResources()
217 delete m_msaaColorBuffer;
218 m_msaaColorBuffer =
nullptr;
220 if (m_prevTexture != m_texture)
225 delete m_secondaryTexture;
226 m_secondaryTexture =
nullptr;
229void QSGRhiLayer::clearMainTexture()
231 std::unique_ptr<QRhiTextureRenderTarget> tempRt(m_rhi->newTextureRenderTarget({ m_texture }));
232 std::unique_ptr<QRhiRenderPassDescriptor> tempRp(tempRt->newCompatibleRenderPassDescriptor());
233 tempRt->setRenderPassDescriptor(tempRp.get());
234 if (tempRt->create()) {
235 m_context->currentFrameCommandBuffer()->beginPass(tempRt.get(), Qt::transparent, { 1.0f, 0 });
236 m_context->currentFrameCommandBuffer()->endPass();
238 qWarning(
"Failed to clear layer main texture in recursive mode");
242void QSGRhiLayer::grab()
244 if (!m_item || m_pixelSize.isEmpty()) {
246 m_dirtyTexture =
false;
250 int effectiveSamples = m_samples;
252 if (effectiveSamples <= 1)
253 effectiveSamples = m_context->msaaSampleCount();
255 const bool needsNewRt = !m_rt || m_rt->pixelSize() != m_pixelSize || (m_recursive && !m_secondaryTexture) || (m_texture && m_texture->format() != m_format);
256 const bool mipmapSettingChanged = m_texture && m_texture->flags().testFlag(QRhiTexture::MipMapped) != m_mipmap;
257 const bool msaaSettingChanged = (effectiveSamples > 1 && !m_msaaColorBuffer) || (effectiveSamples <= 1 && m_msaaColorBuffer);
259 if (needsNewRt ||mipmapSettingChanged || msaaSettingChanged) {
260 if (effectiveSamples <= 1) {
261 m_multisampling =
false;
263 m_multisampling = m_rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer);
264 if (!m_multisampling)
265 qWarning(
"Layer requested %d samples but multisample renderbuffers are not supported", effectiveSamples);
268 QRhiTexture::Flags textureFlags = QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource;
270 textureFlags |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
279 static bool depthBufferEnabled = qEnvironmentVariableIsEmpty(
"QSG_NO_DEPTH_BUFFER");
281 if (m_recursive && m_texture) {
282 if (m_prevTexture != m_texture)
283 delete m_prevTexture;
284 m_prevTexture = m_texture;
289 if (m_multisampling) {
290 m_msaaColorBuffer = m_rhi->newRenderBuffer(QRhiRenderBuffer::Color, m_pixelSize, effectiveSamples);
291 if (!m_msaaColorBuffer->create()) {
292 qWarning(
"Failed to build multisample color buffer for layer of size %dx%d, sample count %d",
293 m_pixelSize.width(), m_pixelSize.height(), effectiveSamples);
297 m_texture = m_rhi->newTexture(m_format, m_pixelSize, 1, textureFlags);
298 if (!m_texture->create()) {
299 qWarning(
"Failed to build texture for layer of size %dx%d", m_pixelSize.width(), m_pixelSize.height());
303 if (depthBufferEnabled) {
304 m_ds = m_context->getDepthStencilBuffer(m_pixelSize, effectiveSamples);
310 QRhiTextureRenderTargetDescription desc;
311 QRhiColorAttachment color0(m_msaaColorBuffer);
313 m_secondaryTexture = m_rhi->newTexture(m_format, m_pixelSize, 1, textureFlags);
314 if (!m_secondaryTexture->create()) {
315 qWarning(
"Failed to build secondary texture for layer of size %dx%d", m_pixelSize.width(), m_pixelSize.height());
319 color0.setResolveTexture(m_secondaryTexture);
323 color0.setResolveTexture(m_texture);
325 desc.setColorAttachments({ color0 });
326 if (depthBufferEnabled)
327 desc.setDepthStencilBuffer(m_ds->ds);
328 m_rt = m_rhi->newTextureRenderTarget(desc);
329 m_rtRp = m_rt->newCompatibleRenderPassDescriptor();
331 qWarning(
"Failed to build render pass descriptor for layer");
335 m_rt->setRenderPassDescriptor(m_rtRp);
336 if (!m_rt->create()) {
337 qWarning(
"Failed to build texture render target for layer");
342 m_texture = m_rhi->newTexture(m_format, m_pixelSize, 1, textureFlags);
343 if (!m_texture->create()) {
344 qWarning(
"Failed to build texture for layer of size %dx%d", m_pixelSize.width(), m_pixelSize.height());
348 if (depthBufferEnabled) {
349 m_ds = m_context->getDepthStencilBuffer(m_pixelSize, 1);
355 QRhiColorAttachment color0(m_texture);
359 m_secondaryTexture = m_rhi->newTexture(m_format, m_pixelSize, 1, textureFlags);
360 if (!m_secondaryTexture->create()) {
361 qWarning(
"Failed to build texture for layer of size %dx%d", m_pixelSize.width(), m_pixelSize.height());
365 color0.setTexture(m_secondaryTexture);
369 QRhiTextureRenderTargetDescription desc({ color0 });
370 if (depthBufferEnabled)
371 desc.setDepthStencilBuffer(m_ds->ds);
372 m_rt = m_rhi->newTextureRenderTarget(desc);
373 m_rtRp = m_rt->newCompatibleRenderPassDescriptor();
375 qWarning(
"Failed to build render pass descriptor for layer");
379 m_rt->setRenderPassDescriptor(m_rtRp);
380 if (!m_rt->create()) {
381 qWarning(
"Failed to build texture render target for layer");
388 QSGNode *root = m_item;
389 while (root->firstChild() && root->type() != QSGNode::RootNodeType)
390 root = root->firstChild();
391 if (root->type() != QSGNode::RootNodeType)
395 const bool useDepth = m_context->useDepthBufferFor2D();
396 const QSGRendererInterface::RenderMode renderMode = useDepth ? QSGRendererInterface::RenderMode2D
397 : QSGRendererInterface::RenderMode2DNoDepthBuffer;
398 m_renderer = m_context->createRenderer(renderMode);
399 connect(m_renderer, SIGNAL(sceneGraphChanged()),
this, SLOT(markDirtyTexture()));
401 m_renderer->setRootNode(
static_cast<QSGRootNode *>(root));
402 root->markDirty(QSGNode::DirtyForceUpdate);
403 m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate);
408 m_dirtyTexture =
false;
415 if (m_logicalRect.isValid() && !m_pixelSize.isNull()) {
416 qreal actualDpr = std::max(m_pixelSize.width() / m_logicalRect.width(),
417 m_pixelSize.height() / m_logicalRect.height());
418 m_renderer->setDevicePixelRatio(actualDpr);
420 m_renderer->setDevicePixelRatio(m_dpr);
422 m_renderer->setDeviceRect(m_pixelSize);
423 m_renderer->setViewportRect(m_pixelSize);
431 bool frontFaceSwap =
false;
433 if (m_rhi->isYUpInFramebuffer()) {
434 mirrored = QRectF(m_mirrorHorizontal ? m_logicalRect.right() : m_logicalRect.left(),
435 m_mirrorVertical ? m_logicalRect.bottom() : m_logicalRect.top(),
436 m_mirrorHorizontal ? -m_logicalRect.width() : m_logicalRect.width(),
437 m_mirrorVertical ? -m_logicalRect.height() : m_logicalRect.height());
438 if (m_mirrorHorizontal)
439 frontFaceSwap = !frontFaceSwap;
440 if (m_mirrorVertical)
441 frontFaceSwap = !frontFaceSwap;
443 mirrored = QRectF(m_mirrorHorizontal ? m_logicalRect.right() : m_logicalRect.left(),
444 m_mirrorVertical ? m_logicalRect.top() : m_logicalRect.bottom(),
445 m_mirrorHorizontal ? -m_logicalRect.width() : m_logicalRect.width(),
446 m_mirrorVertical ? m_logicalRect.height() : -m_logicalRect.height());
447 if (m_mirrorHorizontal)
448 frontFaceSwap = !frontFaceSwap;
449 if (!m_mirrorVertical)
450 frontFaceSwap = !frontFaceSwap;
453 QSGAbstractRenderer::MatrixTransformFlags matrixFlags;
454 if (!m_rhi->isYUpInNDC())
455 matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
457 m_renderer->setProjectionMatrixToRect(mirrored, matrixFlags);
458 m_renderer->setInvertFrontFace(frontFaceSwap);
459 m_renderer->setClearColor(Qt::transparent);
460 m_renderer->setRenderTarget({ m_rt, m_rtRp, m_context->currentFrameCommandBuffer() });
462 QRhiResourceUpdateBatch *resourceUpdates =
nullptr;
466 m_context->renderNextFrame(m_renderer);
467 if (!resourceUpdates)
468 resourceUpdates = m_rhi->nextResourceUpdateBatch();
469 resourceUpdates->copyTexture(m_texture, m_secondaryTexture);
471 delete m_prevTexture;
472 m_prevTexture =
nullptr;
475 m_context->renderNextFrame(m_renderer);
479 if (!resourceUpdates)
480 resourceUpdates = m_rhi->nextResourceUpdateBatch();
482 resourceUpdates->generateMips(m_texture);
488 m_context->currentFrameCommandBuffer()->resourceUpdate(resourceUpdates);
490 root->markDirty(QSGNode::DirtyForceUpdate);
496QImage QSGRhiLayer::toImage()
const
501 QRhiCommandBuffer *cb = m_context->currentFrameCommandBuffer();
502 QRhiResourceUpdateBatch *resourceUpdates = m_rhi->nextResourceUpdateBatch();
503 QRhiReadbackResult result;
504 QRhiReadbackDescription readbackDesc(m_texture);
505 resourceUpdates->readBackTexture(readbackDesc, &result);
507 cb->resourceUpdate(resourceUpdates);
514 if (result.data.isEmpty()) {
515 qWarning(
"Layer grab failed");
521 QImage::Format imageFormat = QImage::Format_RGBA8888_Premultiplied;
522 if (m_format == QRhiTexture::RGBA16F)
523 imageFormat = QImage::Format_RGBA16FPx4_Premultiplied;
524 else if (m_format == QRhiTexture::RGBA32F)
525 imageFormat = QImage::Format_RGBA32FPx4_Premultiplied;
527 const uchar *p =
reinterpret_cast<
const uchar *>(result.data.constData());
528 return QImage(p, result.pixelSize.width(), result.pixelSize.height(), imageFormat).flipped();
531QRectF QSGRhiLayer::normalizedTextureSubRect()
const
533 return QRectF(m_mirrorHorizontal ? 1 : 0,
534 m_mirrorVertical ? 0 : 1,
535 m_mirrorHorizontal ? -1 : 1,
536 m_mirrorVertical ? 1 : -1);
539#include "moc_qsgrhilayer_p.cpp"