From 590c85c80b5e84f20282256f64662da8a25d51a9 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 5 Aug 2024 14:15:29 +0200 Subject: [PATCH] rhi: gl: Use GL_EXT_multisampled_render_to_texture, if present, always Meaning on the common MSAA path that involves passing in a multisample renderbuffer and a non-multisample resolve texture, i.e. the traditional GLES 3.0 compatible approach, that is used throughout Qt in Qt Quick and elsewhere. When EXT_multisampled_render_to_texture (not to be confused with non-GLES extensions or similarly named non-GLES, core functions), is present, which is relatively rare in practice (probably offered on Mali and Qualcomm perhaps), we could ignore the multisample renderbuffer object completely, and get automatic resolving into the (non-multisample) texture. This is already done on the true texture-based path, mainly because using the sibling extension for multiview is mandatory when dealing with multiview (texture arrays) and MSAA on e.g. the Quest 3. However, the texture-based code path is not generally utilized in the rest of Qt when doing MSAA since renderbuffer+explicit resolve into a texture is GLES 3.0 compatible, whereas multisample textures (and then resolve into a texture) needs GLES 3.1. So 1. use glFramebufferTexture2DMultisampleEXT instead of glFramebufferRenderbuffer when the conditions are met (multisample renderbuffer, there is a resolve texture specified, and the extension is supported), and 2. skip the explicit resolve (glBlitFramebuffer) when the conditions for 1) were met. Do this only for color attachments. As the docs for setDepthResolveTexture() state, we do not actually support depth(stencil) resolving with OpenGL ES in combination with QRhiRenderBuffer, so do not have to worry about that here. Change-Id: I0bf88089c3b1b15a8ccc5652e1f918a2dad215e6 Reviewed-by: Andy Nichols --- src/gui/rhi/qrhigles2.cpp | 48 ++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index b17cebcaf74..87a5e886a9e 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -4616,21 +4616,26 @@ void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource qWarning("Resolve source (%dx%d) and target (%dx%d) size does not match", rbD->pixelSize().width(), rbD->pixelSize().height(), size.width(), size.height()); } - 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(); - if (resolveTexD->m_flags.testFlag(QRhiTexture::CubeMap)) - cmd.args.blitFromRenderbuffer.target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(colorAtt.resolveLayer()); - else - cmd.args.blitFromRenderbuffer.target = resolveTexD->target; - cmd.args.blitFromRenderbuffer.dstTexture = resolveTexD->texture; - cmd.args.blitFromRenderbuffer.dstLevel = colorAtt.resolveLevel(); - 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; + if (caps.glesMultisampleRenderToTexture) { + // colorAtt.renderBuffer() is not actually used for anything if OpenGL ES' + // auto-resolving GL_EXT_multisampled_render_to_texture is used. + } else { + 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(); + if (resolveTexD->m_flags.testFlag(QRhiTexture::CubeMap)) + cmd.args.blitFromRenderbuffer.target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + uint(colorAtt.resolveLayer()); + else + cmd.args.blitFromRenderbuffer.target = resolveTexD->target; + cmd.args.blitFromRenderbuffer.dstTexture = resolveTexD->texture; + cmd.args.blitFromRenderbuffer.dstLevel = colorAtt.resolveLevel(); + 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. @@ -6060,7 +6065,18 @@ bool QGles2TextureRenderTarget::create() } } else if (renderBuffer) { QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, renderBuffer); - rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), GL_RENDERBUFFER, rbD->renderbuffer); + if (rbD->sampleCount() > 1 && rhiD->caps.glesMultisampleRenderToTexture && colorAtt.resolveTexture()) { + // Special path for GLES and GL_EXT_multisampled_render_to_texture: ignore + // the (multisample) renderbuffer and give the resolve texture to GL. (so + // no explicit resolve; depending on GL implementation internals, this may + // play nicer with tiled architectures) + QGles2Texture *resolveTexD = QRHI_RES(QGles2Texture, colorAtt.resolveTexture()); + const GLenum faceTargetBase = resolveTexD->flags().testFlag(QRhiTexture::CubeMap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : resolveTexD->target; + rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), faceTargetBase + uint(colorAtt.resolveLayer()), + resolveTexD->texture, colorAtt.level(), rbD->sampleCount()); + } else { + rhiD->f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + uint(attIndex), GL_RENDERBUFFER, rbD->renderbuffer); + } if (attIndex == 0) { d.pixelSize = rbD->pixelSize(); d.sampleCount = rbD->samples;