non-sampleable textures didn't support "magic resolve"

There was an inconsistency in the gles backend where non-sampleable,
non-MS textures failed (assert) if they were used with a render target 
with MS (i.e. "magic resolve" case).

We fix this by taking the same code path for textures and renderbuffers,
the only difference being the calls to glFrameBufferRenderbuffer
instead of glFramebufferTexture2D.
This commit is contained in:
Pixelflinger
2020-09-16 11:03:47 -07:00
committed by Mathias Agopian
parent 86125ca72a
commit 090c02b2cf

View File

@@ -789,8 +789,8 @@ void OpenGLDriver::framebufferTexture(backend::TargetBufferInfo const& binfo,
auto& gl = mContext;
GLenum target = GL_TEXTURE_2D;
if (any(t->usage & TextureUsage::SAMPLEABLE)) {
GLenum target = GL_TEXTURE_2D;
switch (t->target) {
case SamplerType::SAMPLER_2D:
case SamplerType::SAMPLER_2D_ARRAY:
@@ -805,128 +805,131 @@ void OpenGLDriver::framebufferTexture(backend::TargetBufferInfo const& binfo,
default:
break;
}
}
if (rt->gl.samples <= 1 ||
(rt->gl.samples > 1 && t->samples > 1 && gl.features.multisample_texture)) {
// on GL3.2 / GLES3.1 and above multisample is handled when creating the texture.
// If multisampled textures are not supported and we end-up here, things should
// still work, albeit without MSAA.
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
switch (target) {
case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
case GL_TEXTURE_2D:
case GL_TEXTURE_2D_MULTISAMPLE:
if (rt->gl.samples <= 1 ||
(rt->gl.samples > 1 && t->samples > 1 && gl.features.multisample_texture)) {
// on GL3.2 / GLES3.1 and above multisample is handled when creating the texture.
// If multisampled textures are not supported and we end-up here, things should
// still work, albeit without MSAA.
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
switch (target) {
case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
case GL_TEXTURE_2D:
case GL_TEXTURE_2D_MULTISAMPLE:
if (any(t->usage & TextureUsage::SAMPLEABLE)) {
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment,
target, t->gl.id, binfo.level);
break;
case GL_TEXTURE_2D_ARRAY:
// GL_TEXTURE_2D_MULTISAMPLE_ARRAY is not supported in GLES
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment,
t->gl.id, binfo.level, binfo.layer);
break;
default:
// we shouldn't be here
break;
}
CHECK_GL_ERROR(utils::slog.e)
} else
} else {
assert(target == GL_TEXTURE_2D);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment,
GL_RENDERBUFFER, t->gl.id);
}
break;
case GL_TEXTURE_2D_ARRAY:
// GL_TEXTURE_2D_MULTISAMPLE_ARRAY is not supported in GLES
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment,
t->gl.id, binfo.level, binfo.layer);
break;
default:
// we shouldn't be here
break;
}
CHECK_GL_ERROR(utils::slog.e)
} else
#ifdef GL_EXT_multisampled_render_to_texture
// EXT_multisampled_render_to_texture only support GL_COLOR_ATTACHMENT0
if (!attachmentTypeNotSupportedByMSRTT && (t->depth <= 1)
&& ((gl.ext.EXT_multisampled_render_to_texture && attachment == GL_COLOR_ATTACHMENT0)
|| gl.ext.EXT_multisampled_render_to_texture2)) {
assert(rt->gl.samples > 1);
// We have a multi-sample rendertarget and we have EXT_multisampled_render_to_texture,
// so, we can directly use a 1-sample texture as attachment, multi-sample resolve,
// will happen automagically and efficiently in the driver.
// This extension only exists on OpenGL ES.
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
// EXT_multisampled_render_to_texture only support GL_COLOR_ATTACHMENT0
if (!attachmentTypeNotSupportedByMSRTT && (t->depth <= 1)
&& ((gl.ext.EXT_multisampled_render_to_texture && attachment == GL_COLOR_ATTACHMENT0)
|| gl.ext.EXT_multisampled_render_to_texture2)) {
assert(rt->gl.samples > 1);
// We have a multi-sample rendertarget and we have EXT_multisampled_render_to_texture,
// so, we can directly use a 1-sample texture as attachment, multi-sample resolve,
// will happen automagically and efficiently in the driver.
// This extension only exists on OpenGL ES.
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
if (any(t->usage & TextureUsage::SAMPLEABLE)) {
glext::glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER,
attachment, target, t->gl.id, binfo.level, rt->gl.samples);
} else
} else {
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment,
GL_RENDERBUFFER, t->gl.id);
}
} else
#endif
{ // here we emulate ext.EXT_multisampled_render_to_texture
assert(rt->gl.samples > 1);
{ // here we emulate ext.EXT_multisampled_render_to_texture
assert(rt->gl.samples > 1);
// If the texture doesn't already have one, create a sidecar multi-sampled renderbuffer,
// which is where drawing will actually take place, make that our attachment.
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
if (t->gl.rb == 0) {
glGenRenderbuffers(1, &t->gl.rb);
renderBufferStorage(t->gl.rb,
t->gl.internalFormat, rt->width, rt->height, rt->gl.samples);
}
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, t->gl.rb);
// If the texture doesn't already have one, create a sidecar multi-sampled renderbuffer,
// which is where drawing will actually take place, make that our attachment.
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
if (t->gl.rb == 0) {
glGenRenderbuffers(1, &t->gl.rb);
renderBufferStorage(t->gl.rb,
t->gl.internalFormat, rt->width, rt->height, rt->gl.samples);
}
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, t->gl.rb);
// We also need a "read" sidecar fbo, used later for the resolve, which takes place in
// endRenderPass().
if (!rt->gl.fbo_read) {
glGenFramebuffers(1, &rt->gl.fbo_read);
}
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo_read);
switch (target) {
case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
case GL_TEXTURE_2D:
// We also need a "read" sidecar fbo, used later for the resolve, which takes place in
// endRenderPass().
if (!rt->gl.fbo_read) {
glGenFramebuffers(1, &rt->gl.fbo_read);
}
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo_read);
switch (target) {
case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
case GL_TEXTURE_2D:
if (any(t->usage & TextureUsage::SAMPLEABLE)) {
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment,
target, t->gl.id, binfo.level);
break;
case GL_TEXTURE_2D_ARRAY:
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment,
t->gl.id, binfo.level, binfo.layer);
break;
default:
// we shouldn't be here
break;
}
CHECK_GL_ERROR(utils::slog.e)
switch (attachment) {
case GL_COLOR_ATTACHMENT0:
rt->gl.resolve |= TargetBufferFlags::COLOR0;
break;
case GL_COLOR_ATTACHMENT1:
rt->gl.resolve |= TargetBufferFlags::COLOR1;
break;
case GL_COLOR_ATTACHMENT2:
rt->gl.resolve |= TargetBufferFlags::COLOR2;
break;
case GL_COLOR_ATTACHMENT3:
rt->gl.resolve |= TargetBufferFlags::COLOR3;
break;
case GL_DEPTH_ATTACHMENT:
rt->gl.resolve |= TargetBufferFlags::DEPTH;
break;
case GL_STENCIL_ATTACHMENT:
rt->gl.resolve |= TargetBufferFlags::STENCIL;
break;
case GL_DEPTH_STENCIL_ATTACHMENT:
rt->gl.resolve |= TargetBufferFlags::DEPTH;
rt->gl.resolve |= TargetBufferFlags::STENCIL;
break;
default:
break;
}
} else {
assert(target == GL_TEXTURE_2D);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment,
GL_RENDERBUFFER, t->gl.id);
}
break;
case GL_TEXTURE_2D_ARRAY:
glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment,
t->gl.id, binfo.level, binfo.layer);
break;
default:
// we shouldn't be here
break;
}
CHECK_GL_ERROR(utils::slog.e)
switch (attachment) {
case GL_COLOR_ATTACHMENT0: rt->gl.resolve |= TargetBufferFlags::COLOR0; break;
case GL_COLOR_ATTACHMENT1: rt->gl.resolve |= TargetBufferFlags::COLOR1; break;
case GL_COLOR_ATTACHMENT2: rt->gl.resolve |= TargetBufferFlags::COLOR2; break;
case GL_COLOR_ATTACHMENT3: rt->gl.resolve |= TargetBufferFlags::COLOR3; break;
case GL_DEPTH_ATTACHMENT: rt->gl.resolve |= TargetBufferFlags::DEPTH; break;
case GL_STENCIL_ATTACHMENT: rt->gl.resolve |= TargetBufferFlags::STENCIL; break;
case GL_DEPTH_STENCIL_ATTACHMENT:
rt->gl.resolve |= TargetBufferFlags::DEPTH;
rt->gl.resolve |= TargetBufferFlags::STENCIL;
break;
default:
break;
}
}
if (any(t->usage & TextureUsage::SAMPLEABLE)) {
// In a sense, drawing to a texture level is similar to calling setTextureData on it; in
// both cases, we update the base/max LOD to give shaders access to levels as they become
// available.
updateTextureLodRange(t, binfo.level);
} else {
assert(std::max(uint8_t(1), t->samples) == std::max(uint8_t(1), rt->gl.samples));
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, t->gl.id);
}
CHECK_GL_ERROR(utils::slog.e)
@@ -1143,7 +1146,7 @@ void OpenGLDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
auto& gl = mContext;
GLVertexBuffer const* eb = handle_cast<const GLVertexBuffer*>(vbh);
GLsizei n = GLsizei(eb->bufferCount);
auto& buffers = eb->gl.buffers;
const auto& buffers = eb->gl.buffers;
gl.deleteBuffers(n, buffers.data(), GL_ARRAY_BUFFER);
destruct(vbh, eb);
}
@@ -1219,7 +1222,9 @@ void OpenGLDriver::destroyTexture(Handle<HwTexture> th) {
}
} else {
assert(t->gl.target == GL_RENDERBUFFER);
assert(t->gl.rb == 0);
if (t->gl.rb) {
glDeleteRenderbuffers(1, &t->gl.rb);
}
glDeleteRenderbuffers(1, &t->gl.id);
}
if (t->gl.fence) {