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 <andy.nichols@qt.io>
This commit is contained in:
Laszlo Agocs 2024-08-05 14:15:29 +02:00
parent 34f127834c
commit 590c85c80b

View File

@ -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;