WebGL compatibility: remove UB for draw buffers in the GL backend.
WebGL adds two rules:
- Fragment outputs declared but not written to should default to black.
- FBO attachments for outputs not declared in the shader should not be
written to (it is UB in OpenGL ES).
Fix the first one by using the SH_INIT_OUTPUT_VARIABLES compiler
options, and the second one by messing with glDrawBuffers so that the
enabled draw buffers are always a subset of the ones declared by the
shader.
BUG=angleproject:2048
Change-Id: I1d851c190959c1acfc3e41d837e6990aec1d4086
Reviewed-on: https://chromium-review.googlesource.com/521682
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/renderer/gl/FramebufferGL.cpp b/src/libANGLE/renderer/gl/FramebufferGL.cpp
index d286f8d..67c025c 100644
--- a/src/libANGLE/renderer/gl/FramebufferGL.cpp
+++ b/src/libANGLE/renderer/gl/FramebufferGL.cpp
@@ -44,7 +44,8 @@
mWorkarounds(workarounds),
mBlitter(blitter),
mFramebufferID(0),
- mIsDefault(isDefault)
+ mIsDefault(isDefault),
+ mAppliedEnabledDrawBuffers(1)
{
if (!mIsDefault)
{
@@ -64,7 +65,8 @@
mWorkarounds(workarounds),
mBlitter(blitter),
mFramebufferID(id),
- mIsDefault(true)
+ mIsDefault(true),
+ mAppliedEnabledDrawBuffers(1)
{
}
@@ -438,6 +440,7 @@
const auto &drawBuffers = mState.getDrawBufferStates();
mFunctions->drawBuffers(static_cast<GLsizei>(drawBuffers.size()),
drawBuffers.data());
+ mAppliedEnabledDrawBuffers = mState.getEnabledDrawBuffers();
break;
}
case Framebuffer::DIRTY_BIT_READ_BUFFER:
@@ -485,6 +488,28 @@
return mIsDefault;
}
+void FramebufferGL::maskOutInactiveOutputDrawBuffers(DrawBufferMask maxSet)
+{
+ auto targetAppliedDrawBuffers = mState.getEnabledDrawBuffers() & maxSet;
+ if (mAppliedEnabledDrawBuffers != targetAppliedDrawBuffers)
+ {
+ mAppliedEnabledDrawBuffers = targetAppliedDrawBuffers;
+
+ const auto &stateDrawBuffers = mState.getDrawBufferStates();
+ GLsizei drawBufferCount = static_cast<GLsizei>(stateDrawBuffers.size());
+ ASSERT(drawBufferCount <= IMPLEMENTATION_MAX_DRAW_BUFFERS);
+
+ GLenum drawBuffers[IMPLEMENTATION_MAX_DRAW_BUFFERS];
+ for (GLenum i = 0; static_cast<int>(i) < drawBufferCount; ++i)
+ {
+ drawBuffers[i] = targetAppliedDrawBuffers[i] ? stateDrawBuffers[i] : GL_NONE;
+ }
+
+ mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
+ mFunctions->drawBuffers(drawBufferCount, drawBuffers);
+ }
+}
+
void FramebufferGL::syncClearState(const gl::Context *context, GLbitfield mask)
{
if (mFunctions->standard == STANDARD_GL_DESKTOP)