diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index 25df9c60031..a39709c7263 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -1022,6 +1022,20 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general") \l{https://microsoft.github.io/DirectX-Specs/d3d/RelaxedCasting.html} (and note that QRhi always uses fully typed formats for textures.) This enum value has been introduced in Qt 6.8. + + \value ResolveDepthStencil Indicates that resolving a multisample depth or + depth-stencil texture is supported. Otherwise, + \l{QRhiTextureRenderTargetDescription::setDepthResolveTexture()}{setting a + depth resolve texture} is not functional and must be avoided. Direct 3D 11 + and 12 have no support for resolving depth/depth-stencil formats, and + therefore this feature will never be supported with those. Vulkan 1.0 has no + API to request resolving a depth-stencil attachment. Therefore, with Vulkan + this feature will only be supported with Vulkan 1.2 and up, and on 1.1 + implementations with the appropriate extensions present. This feature is + provided for the rare case when resolving into a non-multisample depth + texture becomes necessary, for example when rendering into an + OpenXR-provided depth texture (XR_KHR_composition_layer_depth). This enum + value has been introduced in Qt 6.8. */ /*! @@ -2692,6 +2706,39 @@ QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription(const QRh \sa setDepthStencilBuffer() */ +/*! + \fn QRhiTexture *QRhiTextureRenderTargetDescription::depthResolveTexture() const + + \return the texture to which a multisample depth (or depth-stencil) texture + (or texture array) is resolved to. \nullptr if there is none, which is the + most common case. + + \since 6.8 + \sa QRhiColorAttachment::resolveTexture(), depthTexture() + */ + +/*! + \fn void QRhiTextureRenderTargetDescription::setDepthResolveTexture(QRhiTexture *tex) + + Sets the depth (or depth-stencil) resolve texture \a tex. + + \a tex is expected to be a 2D texture or a 2D texture array with a format + matching the texture set via setDepthTexture(). + + \note Resolving depth (or depth-stencil) data is only functional when the + \l ResolveDepthStencil feature is reported as supported at run time. Support + for depth-stencil resolve is not universally available among the graphics + APIs. Designs assuming unconditional availability of depth-stencil resolve + are therefore non-portable, and should be avoided. + + \note As an additional limitation for OpenGL ES in particular, setting a + depth resolve texture may only be functional in combination with + setDepthTexture(), not with setDepthStencilBuffer(). + + \since 6.8 + \sa QRhiColorAttachment::setResolveTexture(), setDepthTexture() + */ + /*! \class QRhiTextureSubresourceUploadDescription \inmodule QtGui @@ -5088,8 +5135,9 @@ QRhiResource::Type QRhiSwapChainRenderTarget::resourceType() const \value DoNotStoreDepthStencilContents Indicates that the contents of the depth texture does not need to be written out. Relevant only when a QRhiTexture, not QRhiRenderBuffer, is used as the depth-stencil buffer, - because for QRhiRenderBuffer this is implicit. This enum value is introduced - in Qt 6.8. + because for QRhiRenderBuffer this is implicit. When a depthResolveTexture is + set, the flag is not relevant, because the behavior is then as if the flag + was set. This enum value is introduced in Qt 6.8. */ /*! diff --git a/src/gui/rhi/qrhi.h b/src/gui/rhi/qrhi.h index a9836bf8925..d20b7e00d1c 100644 --- a/src/gui/rhi/qrhi.h +++ b/src/gui/rhi/qrhi.h @@ -642,10 +642,14 @@ public: QRhiTexture *depthTexture() const { return m_depthTexture; } void setDepthTexture(QRhiTexture *texture) { m_depthTexture = texture; } + QRhiTexture *depthResolveTexture() const { return m_depthResolveTexture; } + void setDepthResolveTexture(QRhiTexture *tex) { m_depthResolveTexture = tex; } + private: QVarLengthArray m_colorAttachments; QRhiRenderBuffer *m_depthStencilBuffer = nullptr; QRhiTexture *m_depthTexture = nullptr; + QRhiTexture *m_depthResolveTexture = nullptr; }; class Q_GUI_EXPORT QRhiTextureSubresourceUploadDescription @@ -1866,7 +1870,8 @@ public: RenderToOneDimensionalTexture, ThreeDimensionalTextureMipmaps, MultiView, - TextureViewFormat + TextureViewFormat, + ResolveDepthStencil }; enum BeginFrameFlag { diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 26e1f45dc75..ba2b783b0e3 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -600,6 +600,8 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const return false; case QRhi::TextureViewFormat: return false; // because we use fully typed formats for textures and relaxed casting is a D3D12 thing + case QRhi::ResolveDepthStencil: + return false; default: Q_UNREACHABLE(); return false; @@ -2119,6 +2121,8 @@ void QRhiD3D11::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource cmd.args.resolveSubRes.srcSubRes = D3D11CalcSubresource(0, UINT(colorAtt.layer()), 1); cmd.args.resolveSubRes.format = dstTexD->dxgiFormat; } + if (rtTex->m_desc.depthResolveTexture()) + qWarning("Resolving multisample depth-stencil buffers is not supported with D3D"); } cbD->recordingPass = QD3D11CommandBuffer::NoPass; diff --git a/src/gui/rhi/qrhid3d12.cpp b/src/gui/rhi/qrhid3d12.cpp index 08224984d2f..d77e2db091b 100644 --- a/src/gui/rhi/qrhid3d12.cpp +++ b/src/gui/rhi/qrhid3d12.cpp @@ -712,6 +712,10 @@ bool QRhiD3D12::isFeatureSupported(QRhi::Feature feature) const return caps.multiView; case QRhi::TextureViewFormat: return caps.textureViewFormat; + case QRhi::ResolveDepthStencil: + // there is no Multisample Resolve support for depth/stencil formats + // https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/hardware-support-for-direct3d-12-1-formats + return false; } return false; } @@ -1960,7 +1964,8 @@ void QRhiD3D12::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource dstTexD->dxgiFormat); } } - + if (rtTex->m_desc.depthResolveTexture()) + qWarning("Resolving multisample depth-stencil buffers is not supported with D3D"); } cbD->recordingPass = QD3D12CommandBuffer::NoPass; diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index d86b5368c45..62830c291d6 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -1457,6 +1457,8 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const return caps.multiView && caps.maxTextureArraySize > 0; case QRhi::TextureViewFormat: return false; + case QRhi::ResolveDepthStencil: + return true; default: Q_UNREACHABLE_RETURN(false); } @@ -3580,21 +3582,47 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) GLuint fbo[2]; f->glGenFramebuffers(2, fbo); f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]); - f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer); + const bool ds = cmd.args.blitFromRenderbuffer.isDepthStencil; + if (ds) { + f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer); + f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer); + } else { + f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer); + } f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]); if (cmd.args.blitFromRenderbuffer.target == GL_TEXTURE_3D || cmd.args.blitFromRenderbuffer.target == GL_TEXTURE_2D_ARRAY) { - f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - cmd.args.blitFromRenderbuffer.dstTexture, - cmd.args.blitFromRenderbuffer.dstLevel, - cmd.args.blitFromRenderbuffer.dstLayer); + if (ds) { + f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + cmd.args.blitFromRenderbuffer.dstTexture, + cmd.args.blitFromRenderbuffer.dstLevel, + cmd.args.blitFromRenderbuffer.dstLayer); + f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + cmd.args.blitFromRenderbuffer.dstTexture, + cmd.args.blitFromRenderbuffer.dstLevel, + cmd.args.blitFromRenderbuffer.dstLayer); + } else { + f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + cmd.args.blitFromRenderbuffer.dstTexture, + cmd.args.blitFromRenderbuffer.dstLevel, + cmd.args.blitFromRenderbuffer.dstLayer); + } } else { - f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRenderbuffer.target, - cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel); + if (ds) { + f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromRenderbuffer.target, + cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel); + f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromRenderbuffer.target, + cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel); + } else { + f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRenderbuffer.target, + cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel); + } } f->glBlitFramebuffer(0, 0, cmd.args.blitFromRenderbuffer.w, cmd.args.blitFromRenderbuffer.h, 0, 0, cmd.args.blitFromRenderbuffer.w, cmd.args.blitFromRenderbuffer.h, - GL_COLOR_BUFFER_BIT, + ds ? GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT : GL_COLOR_BUFFER_BIT, GL_NEAREST); // Qt 5 used Nearest when resolving samples, stick to that f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); f->glDeleteFramebuffers(2, fbo); @@ -3609,28 +3637,65 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb) GLuint fbo[2]; f->glGenFramebuffers(2, fbo); f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]); + const bool ds = cmd.args.blitFromTexture.isDepthStencil; if (cmd.args.blitFromTexture.srcTarget == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) { - f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - cmd.args.blitFromTexture.srcTexture, - cmd.args.blitFromTexture.srcLevel, - cmd.args.blitFromTexture.srcLayer); + if (ds) { + f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + cmd.args.blitFromTexture.srcTexture, + cmd.args.blitFromTexture.srcLevel, + cmd.args.blitFromTexture.srcLayer); + f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + cmd.args.blitFromTexture.srcTexture, + cmd.args.blitFromTexture.srcLevel, + cmd.args.blitFromTexture.srcLayer); + } else { + f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + cmd.args.blitFromTexture.srcTexture, + cmd.args.blitFromTexture.srcLevel, + cmd.args.blitFromTexture.srcLayer); + } } else { - f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.srcTarget, - cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel); + if (ds) { + f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromTexture.srcTarget, + cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel); + f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromTexture.srcTarget, + cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel); + } else { + f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.srcTarget, + cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel); + } } f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]); if (cmd.args.blitFromTexture.dstTarget == GL_TEXTURE_3D || cmd.args.blitFromTexture.dstTarget == GL_TEXTURE_2D_ARRAY) { - f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - cmd.args.blitFromTexture.dstTexture, - cmd.args.blitFromTexture.dstLevel, - cmd.args.blitFromTexture.dstLayer); + if (ds) { + f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, + cmd.args.blitFromTexture.dstTexture, + cmd.args.blitFromTexture.dstLevel, + cmd.args.blitFromTexture.dstLayer); + f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + cmd.args.blitFromTexture.dstTexture, + cmd.args.blitFromTexture.dstLevel, + cmd.args.blitFromTexture.dstLayer); + } else { + f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + cmd.args.blitFromTexture.dstTexture, + cmd.args.blitFromTexture.dstLevel, + cmd.args.blitFromTexture.dstLayer); + } } else { - f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.dstTarget, - cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel); + if (ds) { + f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromTexture.dstTarget, + cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel); + f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromTexture.dstTarget, + cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel); + } else { + f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.dstTarget, + cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel); + } } f->glBlitFramebuffer(0, 0, cmd.args.blitFromTexture.w, cmd.args.blitFromTexture.h, 0, 0, cmd.args.blitFromTexture.w, cmd.args.blitFromTexture.h, - GL_COLOR_BUFFER_BIT, + ds ? GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT : GL_COLOR_BUFFER_BIT, GL_NEAREST); // Qt 5 used Nearest when resolving samples, stick to that f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); f->glDeleteFramebuffers(2, fbo); @@ -4514,6 +4579,7 @@ void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource const bool hasZ = resolveTexD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || resolveTexD->m_flags.testFlag(QRhiTexture::TextureArray); cmd.args.blitFromRenderbuffer.dstLayer = hasZ ? colorAtt.resolveLayer() : 0; + cmd.args.blitFromRenderbuffer.isDepthStencil = false; } else if (caps.glesMultisampleRenderToTexture) { // Nothing to do, resolving into colorAtt.resolveTexture() is automatic, // colorAtt.texture() is in fact not used for anything. @@ -4550,12 +4616,53 @@ void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource cmd.args.blitFromTexture.dstLayer = 0; if (resolveTexD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || resolveTexD->m_flags.testFlag(QRhiTexture::TextureArray)) cmd.args.blitFromTexture.dstLayer = dstLayer; + cmd.args.blitFromTexture.isDepthStencil = false; } } } - const bool mayDiscardDepthStencil = rtTex->m_desc.depthStencilBuffer() - || (rtTex->m_desc.depthTexture() && rtTex->m_flags.testFlag(QRhiTextureRenderTarget::DoNotStoreDepthStencilContents)); + if (rtTex->m_desc.depthResolveTexture()) { + QGles2Texture *depthResolveTexD = QRHI_RES(QGles2Texture, rtTex->m_desc.depthResolveTexture()); + const QSize size = depthResolveTexD->pixelSize(); + if (rtTex->m_desc.depthStencilBuffer()) { + QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, rtTex->m_desc.depthStencilBuffer()); + QGles2CommandBuffer::Command &cmd(cbD->commands.get()); + cmd.cmd = QGles2CommandBuffer::Command::BlitFromRenderbuffer; + cmd.args.blitFromRenderbuffer.renderbuffer = rbD->renderbuffer; + cmd.args.blitFromRenderbuffer.w = size.width(); + cmd.args.blitFromRenderbuffer.h = size.height(); + cmd.args.blitFromRenderbuffer.target = depthResolveTexD->target; + cmd.args.blitFromRenderbuffer.dstTexture = depthResolveTexD->texture; + cmd.args.blitFromRenderbuffer.dstLevel = 0; + cmd.args.blitFromRenderbuffer.dstLayer = 0; + cmd.args.blitFromRenderbuffer.isDepthStencil = true; + } else if (caps.glesMultisampleRenderToTexture) { + // Nothing to do, resolving into depthResolveTexture() is automatic. + } else { + QGles2Texture *depthTexD = QRHI_RES(QGles2Texture, rtTex->m_desc.depthTexture()); + const int resolveCount = depthTexD->arraySize() >= 2 ? depthTexD->arraySize() : 1; + for (int resolveIdx = 0; resolveIdx < resolveCount; ++resolveIdx) { + QGles2CommandBuffer::Command &cmd(cbD->commands.get()); + cmd.cmd = QGles2CommandBuffer::Command::BlitFromTexture; + cmd.args.blitFromTexture.srcTarget = depthTexD->target; + cmd.args.blitFromTexture.srcTexture = depthTexD->texture; + cmd.args.blitFromTexture.srcLevel = 0; + cmd.args.blitFromTexture.srcLayer = resolveIdx; + cmd.args.blitFromTexture.w = size.width(); + cmd.args.blitFromTexture.h = size.height(); + cmd.args.blitFromTexture.dstTarget = depthResolveTexD->target; + cmd.args.blitFromTexture.dstTexture = depthResolveTexD->texture; + cmd.args.blitFromTexture.dstLevel = 0; + cmd.args.blitFromTexture.dstLayer = resolveIdx; + cmd.args.blitFromTexture.isDepthStencil = true; + } + } + } + + const bool mayDiscardDepthStencil = + (rtTex->m_desc.depthStencilBuffer() + || (rtTex->m_desc.depthTexture() && rtTex->m_flags.testFlag(QRhiTextureRenderTarget::DoNotStoreDepthStencilContents))) + && !rtTex->m_desc.depthResolveTexture(); if (mayDiscardDepthStencil) { QGles2CommandBuffer::Command &cmd(cbD->commands.get()); cmd.cmd = QGles2CommandBuffer::Command::InvalidateFramebuffer; @@ -5923,11 +6030,24 @@ bool QGles2TextureRenderTarget::create() } else { QGles2Texture *depthTexD = QRHI_RES(QGles2Texture, m_desc.depthTexture()); if (multiViewCount < 2) { - rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexD->target, - depthTexD->texture, 0); - if (rhiD->isStencilSupportingFormat(depthTexD->format())) { - rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexD->target, + if (depthTexD->sampleCount() > 1 && rhiD->caps.glesMultisampleRenderToTexture && m_desc.depthResolveTexture()) { + // Special path for GLES and + // GL_EXT_multisampled_render_to_texture, for depth-stencil. + // Relevant only when depthResolveTexture is set. + QGles2Texture *depthResolveTexD = QRHI_RES(QGles2Texture, m_desc.depthResolveTexture()); + rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthResolveTexD->target, + depthResolveTexD->texture, 0, depthTexD->sampleCount()); + if (rhiD->isStencilSupportingFormat(depthResolveTexD->format())) { + rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthResolveTexD->target, + depthResolveTexD->texture, 0, depthTexD->sampleCount()); + } + } else { + rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexD->target, depthTexD->texture, 0); + if (rhiD->isStencilSupportingFormat(depthTexD->format())) { + rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexD->target, + depthTexD->texture, 0); + } } } else { if (depthTexD->sampleCount() > 1 && rhiD->caps.glesMultiviewMultisampleRenderToTexture) { @@ -5944,37 +6064,55 @@ bool QGles2TextureRenderTarget::create() // multisample-multiview-auto-resolving version (which in // turn is not supported on desktop GL e.g. by NVIDIA), too // bad we have a multisample depth texture array here as - // every other API out there requires that. So create a - // temporary one ignoring what the user has already created. + // every other API out there requires that. So, in absence + // of a depthResolveTexture, create a temporary one ignoring + // what the user has already created. // - // (also, why on Earth would we want to waste time on - // resolving the throwaway depth-stencil buffer? Hopefully - // the invalidation at the end of the pass avoids that...) - if (!m_flags.testFlag(DoNotStoreDepthStencilContents)) { + if (!m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture()) { qWarning("Attempted to create a multiview+multisample QRhiTextureRenderTarget, but DoNotStoreDepthStencilContents was not set." " This path has no choice but to behave as if DoNotStoreDepthStencilContents was set, because QRhi is forced to create" - " a throwaway non-multisample depth texture here. Set the flag to silence this warning."); + " a throwaway non-multisample depth texture here. Set the flag to silence this warning, or set a depthResolveTexture."); } - if (!nonMsaaThrowawayDepthTexture) { - rhiD->f->glGenTextures(1, &nonMsaaThrowawayDepthTexture); - rhiD->f->glBindTexture(GL_TEXTURE_2D_ARRAY, nonMsaaThrowawayDepthTexture); - rhiD->f->glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH24_STENCIL8, - depthTexD->pixelSize().width(), depthTexD->pixelSize().height(), multiViewCount); + if (m_desc.depthResolveTexture()) { + QGles2Texture *depthResolveTexD = QRHI_RES(QGles2Texture, m_desc.depthResolveTexture()); + rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + depthResolveTexD->texture, + 0, + depthTexD->sampleCount(), + 0, + multiViewCount); + if (rhiD->isStencilSupportingFormat(depthResolveTexD->format())) { + rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, + depthResolveTexD->texture, + 0, + depthTexD->sampleCount(), + 0, + multiViewCount); + } + } else { + if (!nonMsaaThrowawayDepthTexture) { + rhiD->f->glGenTextures(1, &nonMsaaThrowawayDepthTexture); + rhiD->f->glBindTexture(GL_TEXTURE_2D_ARRAY, nonMsaaThrowawayDepthTexture); + rhiD->f->glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH24_STENCIL8, + depthTexD->pixelSize().width(), depthTexD->pixelSize().height(), multiViewCount); + } + rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + nonMsaaThrowawayDepthTexture, + 0, + depthTexD->sampleCount(), + 0, + multiViewCount); + rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, + nonMsaaThrowawayDepthTexture, + 0, + depthTexD->sampleCount(), + 0, + multiViewCount); } - rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, - GL_DEPTH_ATTACHMENT, - nonMsaaThrowawayDepthTexture, - 0, - depthTexD->sampleCount(), - 0, - multiViewCount); - rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, - GL_STENCIL_ATTACHMENT, - nonMsaaThrowawayDepthTexture, - 0, - depthTexD->sampleCount(), - 0, - multiViewCount); } else { // The depth texture here must be an array with at least // multiViewCount elements, and the format should be D24 or D32F diff --git a/src/gui/rhi/qrhigles2_p.h b/src/gui/rhi/qrhigles2_p.h index 1365d439c22..4305186c029 100644 --- a/src/gui/rhi/qrhigles2_p.h +++ b/src/gui/rhi/qrhigles2_p.h @@ -507,6 +507,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer GLuint dstTexture; int dstLevel; int dstLayer; + bool isDepthStencil; } blitFromRenderbuffer; struct { GLenum srcTarget; @@ -519,6 +520,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer GLuint dstTexture; int dstLevel; int dstLayer; + bool isDepthStencil; } blitFromTexture; struct { GLenum target; diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 2cdac0531af..b69bc17f51c 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -363,6 +363,7 @@ struct QMetalRenderTargetData struct { ColorAtt colorAtt[QMetalRenderPassDescriptor::MAX_COLOR_ATTACHMENTS]; id dsTex = nil; + id dsResolveTex = nil; bool hasStencil = false; bool depthNeedsStore = false; bool preserveColor = false; @@ -854,6 +855,8 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const return caps.multiView; case QRhi::TextureViewFormat: return false; + case QRhi::ResolveDepthStencil: + return true; default: Q_UNREACHABLE(); return false; @@ -2368,6 +2371,7 @@ QRhi::FrameOpResult QRhiMetal::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF swapChainD->rtWrapper.d->fb.colorAtt[0] = colorAtt; swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil; + swapChainD->rtWrapper.d->fb.dsResolveTex = nil; swapChainD->rtWrapper.d->fb.hasStencil = swapChainD->ds ? true : false; swapChainD->rtWrapper.d->fb.depthNeedsStore = false; @@ -2979,6 +2983,8 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb, QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot; if (rtTex->m_desc.depthTexture()) QRHI_RES(QMetalTexture, rtTex->m_desc.depthTexture())->lastActiveFrameSlot = currentFrameSlot; + if (rtTex->m_desc.depthResolveTexture()) + QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot; } break; default: @@ -3006,6 +3012,15 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb, cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil; if (rtD->fb.depthNeedsStore) // Depth/Stencil is set to DontCare by default, override if needed cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore; + if (rtD->fb.dsResolveTex) { + cbD->d->currentPassRpDesc.depthAttachment.storeAction = rtD->fb.depthNeedsStore ? MTLStoreActionStoreAndMultisampleResolve + : MTLStoreActionMultisampleResolve; + cbD->d->currentPassRpDesc.depthAttachment.resolveTexture = rtD->fb.dsResolveTex; + if (rtD->fb.hasStencil) { + cbD->d->currentPassRpDesc.stencilAttachment.resolveTexture = rtD->fb.dsResolveTex; + cbD->d->currentPassRpDesc.stencilAttachment.storeAction = cbD->d->currentPassRpDesc.depthAttachment.storeAction; + } + } } cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc]; @@ -4229,7 +4244,7 @@ bool QMetalTextureRenderTarget::create() QMetalTexture *depthTexD = QRHI_RES(QMetalTexture, m_desc.depthTexture()); d->fb.dsTex = depthTexD->d->tex; d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format()); - d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents); + d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture(); d->fb.preserveDs = m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents); if (d->colorAttCount == 0) { d->pixelSize = depthTexD->pixelSize(); @@ -4246,6 +4261,10 @@ bool QMetalTextureRenderTarget::create() d->sampleCount = depthRbD->samples; } } + if (m_desc.depthResolveTexture()) { + QMetalTexture *depthResolveTexD = QRHI_RES(QMetalTexture, m_desc.depthResolveTexture()); + d->fb.dsResolveTex = depthResolveTexD->d->tex; + } d->dsAttCount = 1; } else { d->dsAttCount = 0; diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index da8c8fc52a8..f0b51146ccc 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -334,7 +334,9 @@ QByteArrayList QRhiVulkanInitParams::preferredExtensionsForImportedDevice() { return { QByteArrayLiteral("VK_KHR_swapchain"), - QByteArrayLiteral("VK_EXT_vertex_attribute_divisor") + QByteArrayLiteral("VK_EXT_vertex_attribute_divisor"), + QByteArrayLiteral("VK_KHR_create_renderpass2"), + QByteArrayLiteral("VK_KHR_depth_stencil_resolve") }; } @@ -428,6 +430,8 @@ bool QRhiVulkan::create(QRhi::Flags flags) for (const char *ext : inst->extensions()) qCDebug(QRHI_LOG_INFO, " %s", ext); } + + caps = {}; caps.debugUtils = inst->extensions().contains(QByteArrayLiteral("VK_EXT_debug_utils")); QList queueFamilyProps; @@ -659,7 +663,6 @@ bool QRhiVulkan::create(QRhi::Flags flags) } } - caps.vertexAttribDivisor = false; #ifdef VK_EXT_vertex_attribute_divisor if (devExts.contains(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) { if (hasPhysDevProp2) { @@ -669,6 +672,20 @@ bool QRhiVulkan::create(QRhi::Flags flags) } #endif +#ifdef VK_KHR_create_renderpass2 + if (devExts.contains(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME)) { + requestedDevExts.append(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME); + caps.renderPass2KHR = true; + } +#endif + +#ifdef VK_KHR_depth_stencil_resolve + if (devExts.contains(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME)) { + requestedDevExts.append(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME); + caps.depthStencilResolveKHR = true; + } +#endif + for (const QByteArray &ext : requestedDeviceExtensions) { if (!ext.isEmpty() && !requestedDevExts.contains(ext)) { if (devExts.contains(ext)) { @@ -750,6 +767,13 @@ bool QRhiVulkan::create(QRhi::Flags flags) } } else { qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev); + + // Here we have no way to tell if the extensions got enabled or not. + // Pretend it's all there and supported. If getProcAddress fails, we'll + // handle that gracefully. + caps.vertexAttribDivisor = true; + caps.renderPass2KHR = true; + caps.depthStencilResolveKHR = true; } vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast( @@ -807,6 +831,18 @@ bool QRhiVulkan::create(QRhi::Flags flags) caps.multiView = multiviewFeaturesIfApi11.multiview; #endif + // With Vulkan 1.2 renderpass2 and depth_stencil_resolve are core, but we + // have to support the case of 1.1 + extensions, in particular for the Quest + // 3 (Android, Vulkan 1.1 at the time of writing). Therefore, always rely on + // the KHR extension for now. +#ifdef VK_KHR_create_renderpass2 + if (caps.renderPass2KHR) { + vkCreateRenderPass2KHR = reinterpret_cast(f->vkGetDeviceProcAddr(dev, "vkCreateRenderPass2KHR")); + if (!vkCreateRenderPass2KHR) // handle it gracefully, the caps flag may be incorrect when using an imported VkDevice + caps.renderPass2KHR = false; + } +#endif + if (!importedAllocator) { VmaVulkanFunctions funcs = {}; funcs.vkGetInstanceProcAddr = wrap_vkGetInstanceProcAddr; @@ -1331,6 +1367,7 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD rpD->colorRefs.append({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }); rpD->hasDepthStencil = hasDepthStencil; + rpD->hasDepthStencilResolve = false; rpD->multiViewCount = 0; if (hasDepthStencil) { @@ -1434,6 +1471,132 @@ struct MultiViewRenderPassSetupHelper #endif }; +#ifdef VK_KHR_create_renderpass2 +// Effectively converts a VkRenderPassCreateInfo into a VkRenderPassCreateInfo2, +// adding depth-stencil resolve support. Assumes a single subpass and no subpass +// dependencies. +struct RenderPass2SetupHelper +{ + bool prepare(VkRenderPassCreateInfo2 *rpInfo2, const VkRenderPassCreateInfo *rpInfo, const QVkRenderPassDescriptor *rpD, int multiViewCount) { + *rpInfo2 = {}; + + viewMask = 0; + if (multiViewCount >= 2) { + for (uint32_t i = 0; i < uint32_t(multiViewCount); ++i) + viewMask |= (1 << i); + } + + attDescs2.resize(rpInfo->attachmentCount); + for (qsizetype i = 0; i < attDescs2.count(); ++i) { + VkAttachmentDescription2KHR &att2(attDescs2[i]); + const VkAttachmentDescription &att(rpInfo->pAttachments[i]); + att2 = {}; + att2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2; + att2.flags = att.flags; + att2.format = att.format; + att2.samples = att.samples; + att2.loadOp = att.loadOp; + att2.storeOp = att.storeOp; + att2.stencilLoadOp = att.stencilLoadOp; + att2.stencilStoreOp = att.stencilStoreOp; + att2.initialLayout = att.initialLayout; + att2.finalLayout = att.finalLayout; + } + + attRefs2.clear(); + subpass2 = {}; + subpass2.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR; + const VkSubpassDescription &subpassDesc(rpInfo->pSubpasses[0]); + subpass2.flags = subpassDesc.flags; + subpass2.pipelineBindPoint = subpassDesc.pipelineBindPoint; + if (multiViewCount >= 2) + subpass2.viewMask = viewMask; + + // color attachment refs + qsizetype startIndex = attRefs2.count(); + for (uint32_t j = 0; j < subpassDesc.colorAttachmentCount; ++j) { + attRefs2.append({}); + VkAttachmentReference2KHR &attref2(attRefs2.last()); + const VkAttachmentReference &attref(subpassDesc.pColorAttachments[j]); + attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + attref2.attachment = attref.attachment; + attref2.layout = attref.layout; + attref2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + subpass2.colorAttachmentCount = subpassDesc.colorAttachmentCount; + subpass2.pColorAttachments = attRefs2.constData() + startIndex; + + // color resolve refs + if (subpassDesc.pResolveAttachments) { + startIndex = attRefs2.count(); + for (uint32_t j = 0; j < subpassDesc.colorAttachmentCount; ++j) { + attRefs2.append({}); + VkAttachmentReference2KHR &attref2(attRefs2.last()); + const VkAttachmentReference &attref(subpassDesc.pResolveAttachments[j]); + attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + attref2.attachment = attref.attachment; + attref2.layout = attref.layout; + attref2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + subpass2.pResolveAttachments = attRefs2.constData() + startIndex; + } + + // depth-stencil ref + if (subpassDesc.pDepthStencilAttachment) { + startIndex = attRefs2.count(); + attRefs2.append({}); + VkAttachmentReference2KHR &attref2(attRefs2.last()); + const VkAttachmentReference &attref(*subpassDesc.pDepthStencilAttachment); + attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + attref2.attachment = attref.attachment; + attref2.layout = attref.layout; + attref2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + subpass2.pDepthStencilAttachment = attRefs2.constData() + startIndex; + } + + // depth-stencil resolve ref +#ifdef VK_KHR_depth_stencil_resolve + dsResolveDesc = {}; + if (rpD->hasDepthStencilResolve) { + startIndex = attRefs2.count(); + attRefs2.append({}); + VkAttachmentReference2KHR &attref2(attRefs2.last()); + attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR; + attref2.attachment = rpD->dsResolveRef.attachment; + attref2.layout = rpD->dsResolveRef.layout; + attref2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + dsResolveDesc.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE_KHR; + dsResolveDesc.depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; + dsResolveDesc.stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; + dsResolveDesc.pDepthStencilResolveAttachment = attRefs2.constData() + startIndex; + subpass2.pNext = &dsResolveDesc; + } +#endif + + rpInfo2->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR; + rpInfo2->pNext = nullptr; // the 1.1 VkRenderPassMultiviewCreateInfo is part of the '2' structs + rpInfo2->flags = rpInfo->flags; + rpInfo2->attachmentCount = rpInfo->attachmentCount; + rpInfo2->pAttachments = attDescs2.constData(); + rpInfo2->subpassCount = 1; + rpInfo2->pSubpasses = &subpass2; + if (multiViewCount >= 2) { + rpInfo2->correlatedViewMaskCount = 1; + rpInfo2->pCorrelatedViewMasks = &viewMask; + } + return true; + } + + QVarLengthArray attDescs2; + QVarLengthArray attRefs2; + VkSubpassDescription2KHR subpass2; +#ifdef VK_KHR_depth_stencil_resolve + VkSubpassDescriptionDepthStencilResolveKHR dsResolveDesc; +#endif + uint32_t viewMask; +}; +#endif // VK_KHR_create_renderpass2 + bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD, const QRhiColorAttachment *firstColorAttachment, const QRhiColorAttachment *lastColorAttachment, @@ -1441,9 +1604,10 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD, bool preserveDs, bool storeDs, QRhiRenderBuffer *depthStencilBuffer, - QRhiTexture *depthTexture) + QRhiTexture *depthTexture, + QRhiTexture *depthResolveTexture) { - // attachment list layout is color (0-8), ds (0-1), resolve (0-8) + // attachment list layout is color (0-8), ds (0-1), resolve (0-8), ds resolve (0-1) int multiViewCount = 0; for (auto it = firstColorAttachment; it != lastColorAttachment; ++it) { @@ -1539,6 +1703,31 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD, } Q_ASSERT(rpD->colorRefs.size() == rpD->resolveRefs.size()); + rpD->hasDepthStencilResolve = rpD->hasDepthStencil && depthResolveTexture; + if (rpD->hasDepthStencilResolve) { + QVkTexture *rtexD = QRHI_RES(QVkTexture, depthResolveTexture); + if (rtexD->samples > VK_SAMPLE_COUNT_1_BIT) + qWarning("Resolving into a multisample depth texture is not supported"); + + QVkTexture *texD = QRHI_RES(QVkTexture, depthResolveTexture); + if (texD->vkformat != rtexD->vkformat) { + qWarning("Multisample resolve between different depth-stencil formats (%d and %d) is not supported.", + int(texD->vkformat), int(rtexD->vkformat)); + } + + VkAttachmentDescription attDesc = {}; + attDesc.format = rtexD->viewFormat; + attDesc.samples = VK_SAMPLE_COUNT_1_BIT; + attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // ignored + attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attDesc.stencilLoadOp = attDesc.loadOp; + attDesc.stencilStoreOp = attDesc.storeOp; + attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + rpD->attDescs.append(attDesc); + } + rpD->dsResolveRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; + // rpD->subpassDeps stays empty: don't yet know the correct initial/final // access and stage stuff for the implicit deps at this point, so leave it // to the resource tracking and activateTextureRenderTarget() to generate @@ -1552,10 +1741,31 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD, if (!multiViewHelper.prepare(&rpInfo, multiViewCount, caps.multiView)) return false; - VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp); - if (err != VK_SUCCESS) { - qWarning("Failed to create renderpass: %d", err); - return false; +#ifdef VK_KHR_create_renderpass2 + if (rpD->hasDepthStencilResolve && caps.renderPass2KHR) { + // Use the KHR extension, not the 1.2 core API, in order to support Vulkan 1.1. + VkRenderPassCreateInfo2KHR rpInfo2; + RenderPass2SetupHelper rp2Helper; + if (!rp2Helper.prepare(&rpInfo2, &rpInfo, rpD, multiViewCount)) + return false; + + VkResult err = vkCreateRenderPass2KHR(dev, &rpInfo2, nullptr, &rpD->rp); + if (err != VK_SUCCESS) { + qWarning("Failed to create renderpass (using VkRenderPassCreateInfo2KHR): %d", err); + return false; + } + } else +#endif + { + if (rpD->hasDepthStencilResolve) { + qWarning("Resolving multisample depth-stencil buffers is not supported without " + "VK_KHR_depth_stencil_resolve and VK_KHR_create_renderpass2"); + } + VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp); + if (err != VK_SUCCESS) { + qWarning("Failed to create renderpass: %d", err); + return false; + } } return true; @@ -2487,6 +2697,13 @@ void QRhiVulkan::activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRe QRhiPassResourceTracker::TexDepthOutputStage); depthTexD->lastActiveFrameSlot = currentFrameSlot; } + if (rtD->m_desc.depthResolveTexture()) { + QVkTexture *depthResolveTexD = QRHI_RES(QVkTexture, rtD->m_desc.depthResolveTexture()); + trackedRegisterTexture(&passResTracker, depthResolveTexD, + QRhiPassResourceTracker::TexDepthOutput, + QRhiPassResourceTracker::TexDepthOutputStage); + depthResolveTexD->lastActiveFrameSlot = currentFrameSlot; + } } void QRhiVulkan::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) @@ -2628,6 +2845,11 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb, float(colorClearValue.alphaF()) } }; cvs.append(cv); } + for (int i = 0; i < rtD->dsResolveAttCount; ++i) { + VkClearValue cv; + cv.depthStencil = { depthStencilClearValue.depthClearValue(), depthStencilClearValue.stencilClearValue() }; + cvs.append(cv); + } rpBeginInfo.clearValueCount = uint32_t(cvs.size()); QVkCommandBuffer::Command &cmd(cbD->commands.get()); @@ -3931,6 +4153,7 @@ void QRhiVulkan::executeDeferredReleases(bool forced) df->vkDestroyImageView(dev, e.textureRenderTarget.resrtv[att], nullptr); } df->vkDestroyImageView(dev, e.textureRenderTarget.dsv, nullptr); + df->vkDestroyImageView(dev, e.textureRenderTarget.resdsv, nullptr); break; case QRhiVulkan::DeferredReleaseEntry::RenderPass: df->vkDestroyRenderPass(dev, e.renderPass.rp, nullptr); @@ -4592,6 +4815,8 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const return caps.multiView; case QRhi::TextureViewFormat: return true; + case QRhi::ResolveDepthStencil: + return caps.renderPass2KHR && caps.depthStencilResolveKHR; default: Q_UNREACHABLE_RETURN(false); } @@ -6590,7 +6815,7 @@ bool QVkSampler::create() QVkRenderPassDescriptor::QVkRenderPassDescriptor(QRhiImplementation *rhi) : QRhiRenderPassDescriptor(rhi) { - serializedFormatData.reserve(32); + serializedFormatData.reserve(64); } QVkRenderPassDescriptor::~QVkRenderPassDescriptor() @@ -6653,6 +6878,8 @@ bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other return false; if (hasDepthStencil != o->hasDepthStencil) return false; + if (hasDepthStencilResolve != o->hasDepthStencilResolve) + return false; if (multiViewCount != o->multiViewCount) return false; @@ -6680,6 +6907,14 @@ bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other return false; } + if (hasDepthStencilResolve) { + const uint32_t attIdx = dsResolveRef.attachment; + if (attIdx != o->dsResolveRef.attachment) + return false; + if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx])) + return false; + } + // subpassDeps is not included return true; @@ -6694,6 +6929,7 @@ void QVkRenderPassDescriptor::updateSerializedFormat() *p++ = colorRefs.size(); *p++ = resolveRefs.size(); *p++ = hasDepthStencil; + *p++ = hasDepthStencilResolve; *p++ = multiViewCount; auto serializeAttachmentData = [this, &p](uint32_t attIdx) { @@ -6726,6 +6962,12 @@ void QVkRenderPassDescriptor::updateSerializedFormat() *p++ = attIdx; serializeAttachmentData(attIdx); } + + if (hasDepthStencilResolve) { + const uint32_t attIdx = dsResolveRef.attachment; + *p++ = attIdx; + serializeAttachmentData(attIdx); + } } QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescriptor() const @@ -6738,8 +6980,10 @@ QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescri rpD->resolveRefs = resolveRefs; rpD->subpassDeps = subpassDeps; rpD->hasDepthStencil = hasDepthStencil; + rpD->hasDepthStencilResolve = hasDepthStencilResolve; rpD->multiViewCount = multiViewCount; rpD->dsRef = dsRef; + rpD->dsResolveRef = dsResolveRef; VkRenderPassCreateInfo rpInfo; VkSubpassDescription subpassDesc; @@ -6842,6 +7086,8 @@ void QVkTextureRenderTarget::destroy() e.textureRenderTarget.dsv = dsv; dsv = VK_NULL_HANDLE; + e.textureRenderTarget.resdsv = resdsv; + resdsv = VK_NULL_HANDLE; QRHI_RES_RHI(QRhiVulkan); if (rhiD) { @@ -6861,9 +7107,10 @@ QRhiRenderPassDescriptor *QVkTextureRenderTarget::newCompatibleRenderPassDescrip m_desc.cendColorAttachments(), m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents), m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents), - m_desc.depthTexture() && !m_flags.testFlag(DoNotStoreDepthStencilContents), + m_desc.depthTexture() && !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture(), m_desc.depthStencilBuffer(), - m_desc.depthTexture())) + m_desc.depthTexture(), + m_desc.depthResolveTexture())) { delete rp; return nullptr; @@ -7010,6 +7257,36 @@ bool QVkTextureRenderTarget::create() } } + if (m_desc.depthResolveTexture()) { + QVkTexture *resTexD = QRHI_RES(QVkTexture, m_desc.depthResolveTexture()); + Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget)); + + VkImageViewCreateInfo viewInfo = {}; + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = resTexD->image; + viewInfo.viewType = d.multiViewCount ? VK_IMAGE_VIEW_TYPE_2D_ARRAY + : VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = resTexD->viewFormat; + viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; + viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; + viewInfo.components.b = VK_COMPONENT_SWIZZLE_B; + viewInfo.components.a = VK_COMPONENT_SWIZZLE_A; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = qMax(1, d.multiViewCount); + VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resdsv); + if (err != VK_SUCCESS) { + qWarning("Failed to create render target depth resolve image view: %d", err); + return false; + } + views.append(resdsv); + d.dsResolveAttCount = 1; + } else { + d.dsResolveAttCount = 0; + } + if (!m_renderPassDesc) qWarning("QVkTextureRenderTarget: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor()."); @@ -7019,7 +7296,7 @@ bool QVkTextureRenderTarget::create() VkFramebufferCreateInfo fbInfo = {}; fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; fbInfo.renderPass = d.rp->rp; - fbInfo.attachmentCount = uint32_t(d.colorAttCount + d.dsAttCount + d.resolveAttCount); + fbInfo.attachmentCount = uint32_t(d.colorAttCount + d.dsAttCount + d.resolveAttCount + d.dsResolveAttCount); fbInfo.pAttachments = views.constData(); fbInfo.width = uint32_t(d.pixelSize.width()); fbInfo.height = uint32_t(d.pixelSize.height()); @@ -7870,6 +8147,7 @@ bool QVkSwapChain::createOrResize() rtWrapper.d.dsAttCount = 0; ds = nullptr; } + rtWrapper.d.dsResolveAttCount = 0; if (samples > VK_SAMPLE_COUNT_1_BIT) rtWrapper.d.resolveAttCount = 1; else @@ -7886,7 +8164,7 @@ bool QVkSwapChain::createOrResize() VkFramebufferCreateInfo fbInfo = {}; fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; fbInfo.renderPass = rtWrapper.d.rp->rp; - fbInfo.attachmentCount = uint32_t(rtWrapper.d.colorAttCount + rtWrapper.d.dsAttCount + rtWrapper.d.resolveAttCount); + fbInfo.attachmentCount = uint32_t(rtWrapper.d.colorAttCount + rtWrapper.d.dsAttCount + rtWrapper.d.resolveAttCount + rtWrapper.d.dsResolveAttCount); fbInfo.pAttachments = views; fbInfo.width = uint32_t(pixelSize.width()); fbInfo.height = uint32_t(pixelSize.height()); @@ -7916,6 +8194,7 @@ bool QVkSwapChain::createOrResize() rtWrapperRight.d.dsAttCount = 0; ds = nullptr; } + rtWrapperRight.d.dsResolveAttCount = 0; if (samples > VK_SAMPLE_COUNT_1_BIT) rtWrapperRight.d.resolveAttCount = 1; else @@ -7934,7 +8213,7 @@ bool QVkSwapChain::createOrResize() fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; fbInfo.renderPass = rtWrapperRight.d.rp->rp; fbInfo.attachmentCount = uint32_t(rtWrapperRight.d.colorAttCount + rtWrapperRight.d.dsAttCount - + rtWrapperRight.d.resolveAttCount); + + rtWrapperRight.d.resolveAttCount + rtWrapperRight.d.dsResolveAttCount); fbInfo.pAttachments = views; fbInfo.width = uint32_t(pixelSize.width()); fbInfo.height = uint32_t(pixelSize.height()); diff --git a/src/gui/rhi/qrhivulkan_p.h b/src/gui/rhi/qrhivulkan_p.h index bdd65e177b8..ad8687de5dd 100644 --- a/src/gui/rhi/qrhivulkan_p.h +++ b/src/gui/rhi/qrhivulkan_p.h @@ -164,8 +164,10 @@ struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor QVarLengthArray resolveRefs; QVarLengthArray subpassDeps; bool hasDepthStencil = false; + bool hasDepthStencilResolve = false; uint32_t multiViewCount = 0; VkAttachmentReference dsRef; + VkAttachmentReference dsResolveRef; QVector serializedFormatData; QRhiVulkanRenderPassNativeHandles nativeHandlesStruct; int lastActiveFrameSlot = -1; @@ -181,6 +183,7 @@ struct QVkRenderTargetData int colorAttCount = 0; int dsAttCount = 0; int resolveAttCount = 0; + int dsResolveAttCount = 0; int multiViewCount = 0; QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList; static const int MAX_COLOR_ATTACHMENTS = 8; @@ -216,6 +219,7 @@ struct QVkTextureRenderTarget : public QRhiTextureRenderTarget VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS]; VkImageView dsv = VK_NULL_HANDLE; VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS]; + VkImageView resdsv = VK_NULL_HANDLE; int lastActiveFrameSlot = -1; friend class QRhiVulkan; }; @@ -775,7 +779,8 @@ public: bool preserveDs, bool storeDs, QRhiRenderBuffer *depthStencilBuffer, - QRhiTexture *depthTexture); + QRhiTexture *depthTexture, + QRhiTexture *depthResolveTexture); bool ensurePipelineCache(const void *initialData = nullptr, size_t initialDataSize = 0); VkShaderModule createShader(const QByteArray &spirv); @@ -876,6 +881,10 @@ public: PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR; PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR; +#ifdef VK_KHR_create_renderpass2 + PFN_vkCreateRenderPass2KHR vkCreateRenderPass2KHR = nullptr; +#endif + struct { bool compute = false; bool wideLines = false; @@ -886,6 +895,8 @@ public: bool geometryShader = false; bool nonFillPolygonMode = false; bool multiView = false; + bool renderPass2KHR = false; + bool depthStencilResolveKHR = false; QVersionNumber apiVersion; } caps; @@ -1001,6 +1012,7 @@ public: VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS]; VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS]; VkImageView dsv; + VkImageView resdsv; } textureRenderTarget; struct { VkRenderPass rp; diff --git a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp index cdbf247fceb..7187b6858f4 100644 --- a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp +++ b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp @@ -425,7 +425,8 @@ void tst_QRhi::create() QRhi::RenderToOneDimensionalTexture, QRhi::ThreeDimensionalTextureMipmaps, QRhi::MultiView, - QRhi::TextureViewFormat + QRhi::TextureViewFormat, + QRhi::ResolveDepthStencil }; for (size_t i = 0; i isFeatureSupported(features[i]); diff --git a/tests/manual/rhi/CMakeLists.txt b/tests/manual/rhi/CMakeLists.txt index b0637b208cb..8f48bf219dc 100644 --- a/tests/manual/rhi/CMakeLists.txt +++ b/tests/manual/rhi/CMakeLists.txt @@ -33,6 +33,7 @@ add_subdirectory(tex1d) add_subdirectory(displacement) add_subdirectory(imguirenderer) add_subdirectory(multiview) +add_subdirectory(msaatextureresolve) if(QT_FEATURE_widgets) add_subdirectory(rhiwidgetproto) endif() diff --git a/tests/manual/rhi/msaatextureresolve/CMakeLists.txt b/tests/manual/rhi/msaatextureresolve/CMakeLists.txt new file mode 100644 index 00000000000..fb14b119de0 --- /dev/null +++ b/tests/manual/rhi/msaatextureresolve/CMakeLists.txt @@ -0,0 +1,41 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(msaatextureresolve LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_manual_test(msaatextureresolve + GUI + SOURCES + msaatextureresolve.cpp + LIBRARIES + Qt::Gui + Qt::GuiPrivate +) + +# Resources: +set_source_files_properties("../shared/color.frag.qsb" + PROPERTIES QT_RESOURCE_ALIAS "color.frag.qsb" +) +set_source_files_properties("../shared/color.vert.qsb" + PROPERTIES QT_RESOURCE_ALIAS "color.vert.qsb" +) +set_source_files_properties("../shared/texture.frag.qsb" + PROPERTIES QT_RESOURCE_ALIAS "texture.frag.qsb" +) +set_source_files_properties("../shared/texture.vert.qsb" + PROPERTIES QT_RESOURCE_ALIAS "texture.vert.qsb" +) + +qt_internal_add_resource(msaatextureresolve "msaatextureresolve" + PREFIX + "/" + FILES + "../shared/color.frag.qsb" + "../shared/color.vert.qsb" + "../shared/texture.frag.qsb" + "../shared/texture.vert.qsb" +) diff --git a/tests/manual/rhi/msaatextureresolve/msaatextureresolve.cpp b/tests/manual/rhi/msaatextureresolve/msaatextureresolve.cpp new file mode 100644 index 00000000000..128cecf7071 --- /dev/null +++ b/tests/manual/rhi/msaatextureresolve/msaatextureresolve.cpp @@ -0,0 +1,235 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "../shared/examplefw.h" + +// Uses a multisample texture both for color and depth-stencil, renders into +// those, and then resolves into non-multisample textures. Also the +// depth-stencil, in order to exercise that rarely used path. If that is +// functional, will not be visible on-screen. Frame captures with tools such as +// RenderDoc can be used to verify that there is indeed a non-multisample depth +// texture generated. + +static float vertexData[] = +{ // Y up, CCW + -0.5f, 0.5f, 0.0f, 0.0f, + -0.5f, -0.5f, 0.0f, 1.0f, + 0.5f, -0.5f, 1.0f, 1.0f, + 0.5f, 0.5f, 1.0f, 0.0f +}; + +static quint16 indexData[] = +{ + 0, 1, 2, 0, 2, 3 +}; + +static float triangleData[] = +{ // Y up, CCW + 0.0f, 0.5f, 1.0f, 0.0f, 0.0f, + -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, +}; + +struct { + QList releasePool; + QRhiBuffer *vbuf = nullptr; + QRhiBuffer *ibuf = nullptr; + QRhiBuffer *ubuf = nullptr; + QRhiTexture *msaaColorTexture = nullptr; + QRhiTexture *msaaDepthTexture = nullptr; + QRhiTextureRenderTarget *rt = nullptr; + QRhiRenderPassDescriptor *rtRp = nullptr; + QRhiTexture *tex = nullptr; + QRhiTexture *depthTex = nullptr; + QRhiSampler *sampler = nullptr; + QRhiBuffer *triUbuf = nullptr; + QRhiShaderResourceBindings *triSrb = nullptr; + QRhiGraphicsPipeline *triPs = nullptr; + QRhiShaderResourceBindings *srb = nullptr; + QRhiGraphicsPipeline *ps = nullptr; + QRhiResourceUpdateBatch *initialUpdates = nullptr; + QMatrix4x4 triBaseMvp; + float triRot = 0; + QMatrix4x4 winProj; +} d; + +void Window::customInit() +{ + if (!m_r->isFeatureSupported(QRhi::MultisampleTexture)) + qFatal("Multisample textures not supported by this backend"); + + // Skip the check for ResolveDepthStencil, and let it run regardless. When + // not supported, it won't be functional, but should be handled somewhat + // gracefully. + + d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData) + sizeof(triangleData)); + d.vbuf->create(); + d.releasePool << d.vbuf; + + d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(indexData)); + d.ibuf->create(); + d.releasePool << d.ibuf; + + d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68); + d.ubuf->create(); + d.releasePool << d.ubuf; + + d.msaaColorTexture = m_r->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 4, QRhiTexture::RenderTarget); // 4x MSAA + d.msaaColorTexture->create(); + d.releasePool << d.msaaColorTexture; + + d.msaaDepthTexture = m_r->newTexture(QRhiTexture::D24S8, QSize(512, 512), 4, QRhiTexture::RenderTarget); // 4x MSAA + d.msaaDepthTexture->create(); + d.releasePool << d.msaaDepthTexture; + + // the non-msaa texture that will be the destination in the resolve + d.tex = m_r->newTexture(QRhiTexture::RGBA8, d.msaaColorTexture->pixelSize(), 1, QRhiTexture::RenderTarget); + d.releasePool << d.tex; + d.tex->create(); + + d.depthTex = m_r->newTexture(QRhiTexture::D24S8, d.msaaDepthTexture->pixelSize(), 1, QRhiTexture::RenderTarget); + d.releasePool << d.depthTex; + d.depthTex->create(); + + QRhiTextureRenderTargetDescription rtDesc; + QRhiColorAttachment rtAtt(d.msaaColorTexture); + rtAtt.setResolveTexture(d.tex); + rtDesc.setColorAttachments({ rtAtt }); + rtDesc.setDepthTexture(d.msaaDepthTexture); + rtDesc.setDepthResolveTexture(d.depthTex); + + d.rt = m_r->newTextureRenderTarget(rtDesc); + d.releasePool << d.rt; + d.rtRp = d.rt->newCompatibleRenderPassDescriptor(); + d.releasePool << d.rtRp; + d.rt->setRenderPassDescriptor(d.rtRp); + d.rt->create(); + + d.triUbuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68); + d.releasePool << d.triUbuf; + d.triUbuf->create(); + + d.triSrb = m_r->newShaderResourceBindings(); + d.releasePool << d.triSrb; + d.triSrb->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.triUbuf) + }); + d.triSrb->create(); + + d.triPs = m_r->newGraphicsPipeline(); + d.releasePool << d.triPs; + d.triPs->setDepthTest(true); + d.triPs->setDepthWrite(true); + d.triPs->setSampleCount(4); // must match the render target + d.triPs->setShaderStages({ + { QRhiShaderStage::Vertex, getShader(QLatin1String(":/color.vert.qsb")) }, + { QRhiShaderStage::Fragment, getShader(QLatin1String(":/color.frag.qsb")) } + }); + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ + { 5 * sizeof(float) } + }); + inputLayout.setAttributes({ + { 0, 0, QRhiVertexInputAttribute::Float2, 0 }, + { 0, 1, QRhiVertexInputAttribute::Float3, quint32(2 * sizeof(float)) } + }); + d.triPs->setVertexInputLayout(inputLayout); + d.triPs->setShaderResourceBindings(d.triSrb); + d.triPs->setRenderPassDescriptor(d.rtRp); + d.triPs->create(); + + d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None, + QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge); + d.releasePool << d.sampler; + d.sampler->create(); + + d.srb = m_r->newShaderResourceBindings(); + d.releasePool << d.srb; + d.srb->setBindings({ + QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf), + QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler) + }); + d.srb->create(); + + d.ps = m_r->newGraphicsPipeline(); + d.releasePool << d.ps; + d.ps->setShaderStages({ + { QRhiShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) }, + { QRhiShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) } + }); + inputLayout.setBindings({ + { 4 * sizeof(float) } + }); + inputLayout.setAttributes({ + { 0, 0, QRhiVertexInputAttribute::Float2, 0 }, + { 0, 1, QRhiVertexInputAttribute::Float2, quint32(2 * sizeof(float)) } + }); + d.ps->setVertexInputLayout(inputLayout); + d.ps->setShaderResourceBindings(d.srb); + d.ps->setRenderPassDescriptor(m_rp); + d.ps->create(); + + d.initialUpdates = m_r->nextResourceUpdateBatch(); + d.initialUpdates->uploadStaticBuffer(d.vbuf, 0, sizeof(vertexData), vertexData); + d.initialUpdates->uploadStaticBuffer(d.vbuf, sizeof(vertexData), sizeof(triangleData), triangleData); + d.initialUpdates->uploadStaticBuffer(d.ibuf, indexData); + + d.triBaseMvp = m_r->clipSpaceCorrMatrix(); + d.triBaseMvp.perspective(45.0f, d.msaaColorTexture->pixelSize().width() / float(d.msaaColorTexture->pixelSize().height()), 0.01f, 1000.0f); + d.triBaseMvp.translate(0, 0, -2); + float opacity = 1.0f; + d.initialUpdates->updateDynamicBuffer(d.triUbuf, 64, 4, &opacity); + + qint32 flip = m_r->isYUpInFramebuffer() ? 1 : 0; + d.initialUpdates->updateDynamicBuffer(d.ubuf, 64, 4, &flip); +} + +void Window::customRelease() +{ + qDeleteAll(d.releasePool); + d.releasePool.clear(); +} + +void Window::customRender() +{ + QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer(); + QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch(); + if (d.initialUpdates) { + u->merge(d.initialUpdates); + d.initialUpdates->release(); + d.initialUpdates = nullptr; + } + + QMatrix4x4 triMvp = d.triBaseMvp; + triMvp.rotate(d.triRot, 0, 1, 0); + d.triRot += 1; + u->updateDynamicBuffer(d.triUbuf, 0, 64, triMvp.constData()); + + if (d.winProj != m_proj) { + d.winProj = m_proj; + QMatrix4x4 mvp = m_proj; + mvp.scale(2.5f); + u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData()); + } + + // offscreen (triangle, msaa) + cb->beginPass(d.rt, QColor::fromRgbF(0.5f, 0.2f, 0.0f, 1.0f), { 1.0f, 0 }, u); + cb->setGraphicsPipeline(d.triPs); + cb->setViewport({ 0, 0, float(d.msaaColorTexture->pixelSize().width()), float(d.msaaColorTexture->pixelSize().height()) }); + cb->setShaderResources(); + QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, quint32(sizeof(vertexData))); + cb->setVertexInput(0, 1, &vbufBinding); + cb->draw(3); + cb->endPass(); + + // onscreen (quad) + const QSize outputSizeInPixels = m_sc->currentPixelSize(); + cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }); + cb->setGraphicsPipeline(d.ps); + cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) }); + cb->setShaderResources(); + vbufBinding.second = 0; + cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16); + cb->drawIndexed(6); + cb->endPass(); +}