add ext_float_blend feature and test
Bug: chromium:930993
Change-Id: I8edbd01c5c9f1ed63243cc4a42f6de44c92db8bd
Reviewed-on: https://chromium-review.googlesource.com/c/1481242
Commit-Queue: Shrek Shao <shrekshao@google.com>
Reviewed-by: Jamie Madill <jmadill@google.com>
diff --git a/src/libANGLE/Caps.cpp b/src/libANGLE/Caps.cpp
index f7793d8..a49a93a 100644
--- a/src/libANGLE/Caps.cpp
+++ b/src/libANGLE/Caps.cpp
@@ -255,6 +255,7 @@
multiviewMultisample(false),
blendFuncExtended(false),
maxDualSourceDrawBuffers(0),
+ floatBlend(false),
memorySize(false),
textureMultisample(false),
multiDraw(false)
@@ -909,6 +910,7 @@
map["GL_OES_texture_storage_multisample_2d_array"] = enableableExtension(&Extensions::textureStorageMultisample2DArray);
map["GL_ANGLE_multiview_multisample"] = enableableExtension(&Extensions::multiviewMultisample);
map["GL_EXT_blend_func_extended"] = enableableExtension(&Extensions::blendFuncExtended);
+ map["GL_EXT_float_blend"] = enableableExtension(&Extensions::floatBlend);
map["GL_ANGLE_texture_multisample"] = enableableExtension(&Extensions::textureMultisample);
map["GL_ANGLE_multi_draw"] = enableableExtension(&Extensions::multiDraw);
map["GL_ANGLE_provoking_vertex"] = enableableExtension(&Extensions::provokingVertex);
diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h
index ff835a0..bbd17dd 100644
--- a/src/libANGLE/Caps.h
+++ b/src/libANGLE/Caps.h
@@ -466,6 +466,9 @@
bool blendFuncExtended;
GLuint maxDualSourceDrawBuffers;
+ // GL_EXT_float_blend
+ bool floatBlend;
+
// GL_ANGLE_memory_size
bool memorySize;
diff --git a/src/libANGLE/ErrorStrings.h b/src/libANGLE/ErrorStrings.h
index df89e94..473fe01 100644
--- a/src/libANGLE/ErrorStrings.h
+++ b/src/libANGLE/ErrorStrings.h
@@ -474,6 +474,7 @@
MSG kUnknownParameter = "Unknown parameter value.";
MSG kUnsizedInternalFormatUnsupported = "Internalformat is one of the unsupported unsized base internalformats.";
MSG kUnsupportedDrawModeForTransformFeedback = "The draw command is unsupported when transform feedback is active and not paused.";
+MSG kUnsupportedFloatBlending = "GL_BLEND with floating-point color attachments requires the EXT_float_blend extension.";
MSG kVertexArrayNoBuffer = "An enabled vertex array has no buffer.";
MSG kVertexArrayNoBufferPointer = "An enabled vertex array has no buffer and no pointer.";
MSG kVertexBufferBoundForTransformFeedback = "It is undefined behavior to use a vertex buffer that is bound for transform feedback.";
diff --git a/src/libANGLE/Framebuffer.cpp b/src/libANGLE/Framebuffer.cpp
index ec99a04..11e1d90 100644
--- a/src/libANGLE/Framebuffer.cpp
+++ b/src/libANGLE/Framebuffer.cpp
@@ -1758,6 +1758,16 @@
textureIndex, resource, numViews, baseViewIndex, multiviewLayout,
viewportOffsets);
+ if (!resource)
+ {
+ mFloat32ColorAttachmentBits.reset(colorIndex);
+ }
+ else
+ {
+ updateFloat32ColorAttachmentBits(
+ colorIndex, resource->getAttachmentFormat(binding, textureIndex).info);
+ }
+
// TODO(jmadill): ASSERT instead of checking the attachment exists in
// formsRenderingFeedbackLoopWith
bool enabled = (type != GL_NONE && getDrawBufferState(colorIndex) != GL_NONE);
@@ -1829,6 +1839,15 @@
// Mark the appropriate init flag.
mState.mResourceNeedsInit.set(index, attachment->initState() == InitState::MayNeedInit);
+
+ // Update mFloat32ColorAttachmentBits Cache
+ if (index < DIRTY_BIT_COLOR_ATTACHMENT_MAX)
+ {
+ ASSERT(index != DIRTY_BIT_DEPTH_ATTACHMENT);
+ ASSERT(index != DIRTY_BIT_STENCIL_ATTACHMENT);
+ updateFloat32ColorAttachmentBits(index - DIRTY_BIT_COLOR_ATTACHMENT_0,
+ attachment->getFormat().info);
+ }
}
FramebufferAttachment *Framebuffer::getAttachmentFromSubjectIndex(angle::SubjectIndex index)
diff --git a/src/libANGLE/Framebuffer.h b/src/libANGLE/Framebuffer.h
index 8295410..219384b 100644
--- a/src/libANGLE/Framebuffer.h
+++ b/src/libANGLE/Framebuffer.h
@@ -337,6 +337,11 @@
using DirtyBits = angle::BitSet<DIRTY_BIT_MAX>;
bool hasAnyDirtyBit() const { return mDirtyBits.any(); }
+ bool hasActiveFloat32ColorAttachment() const
+ {
+ return (mFloat32ColorAttachmentBits & getDrawBufferMask()).any();
+ }
+
bool hasResourceThatNeedsInit() const { return mState.mResourceNeedsInit.any(); }
angle::Result syncState(const Context *context);
@@ -418,6 +423,12 @@
FramebufferAttachment *getAttachmentFromSubjectIndex(angle::SubjectIndex index);
+ ANGLE_INLINE void updateFloat32ColorAttachmentBits(size_t index,
+ const gl::InternalFormat *format)
+ {
+ mFloat32ColorAttachmentBits.set(index, format->type == GL_FLOAT);
+ }
+
FramebufferState mState;
rx::FramebufferImpl *mImpl;
@@ -427,6 +438,7 @@
angle::ObserverBinding mDirtyStencilAttachmentBinding;
DirtyBits mDirtyBits;
+ DrawBufferMask mFloat32ColorAttachmentBits;
// The dirty bits guard is checked when we get a dependent state change message. We verify that
// we don't set a dirty bit that isn't already set, when inside the dirty bits syncState.
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp
index d0356dc..c0d3bfa 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.cpp
+++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp
@@ -1345,6 +1345,11 @@
extensions->maxDualSourceDrawBuffers = 1;
}
+ // EXT_float_blend
+ // Assume all desktop driver supports this by default.
+ extensions->floatBlend = functions->standard == STANDARD_GL_DESKTOP ||
+ functions->hasGLESExtension("GL_EXT_float_blend");
+
// GL_CHROMIUM_compressed_texture_etc
// Expose this extension only when we support the formats or we're running on top of a native
// ES driver.
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index 0361a42..80e743a 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -2623,6 +2623,12 @@
}
}
+ if (!extensions.floatBlend && context->getState().isBlendEnabled() &&
+ framebuffer->hasActiveFloat32ColorAttachment())
+ {
+ return kUnsupportedFloatBlending;
+ }
+
if (!framebuffer->isComplete(context))
{
// Note: this error should be generated as INVALID_FRAMEBUFFER_OPERATION.
diff --git a/src/tests/gl_tests/BlendMinMaxTest.cpp b/src/tests/gl_tests/BlendMinMaxTest.cpp
index 97ab18e..5eeb42d 100644
--- a/src/tests/gl_tests/BlendMinMaxTest.cpp
+++ b/src/tests/gl_tests/BlendMinMaxTest.cpp
@@ -171,7 +171,7 @@
TEST_P(BlendMinMaxTest, RGBA32F)
{
- ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 ||
+ ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 || !extensionEnabled("GL_EXT_float_blend") ||
!extensionEnabled("GL_EXT_color_buffer_float"));
// Ignore SDK layers messages on D3D11 FL 9.3 (http://anglebug.com/1284)
diff --git a/src/tests/gl_tests/WebGLCompatibilityTest.cpp b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
index e0869e7..5ce992f 100644
--- a/src/tests/gl_tests/WebGLCompatibilityTest.cpp
+++ b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
@@ -43,6 +43,7 @@
"GL_OES_texture_float",
"GL_OES_texture_float_linear",
"GL_EXT_color_buffer_float",
+ "GL_EXT_float_blend",
"GL_CHROMIUM_color_buffer_float_rgba",
"GL_CHROMIUM_color_buffer_float_rgb",
};
@@ -213,6 +214,49 @@
0, 0, GLColor32F(floatData[0], floatData[1], floatData[2], floatData[3]), 1.0f);
}
+ void TestExtFloatBlend(bool shouldBlend)
+ {
+ constexpr char kVS[] =
+ "void main()\n"
+ "{\n"
+ " gl_Position = vec4(0, 0, 0, 1);\n"
+ "}\n";
+
+ constexpr char kFS[] =
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = vec4(0, 1, 0, 1);\n"
+ "}\n";
+
+ ANGLE_GL_PROGRAM(program, kVS, kFS);
+ glUseProgram(program);
+
+ GLTexture texture;
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 1, 1, 0, GL_RGBA, GL_FLOAT, nullptr);
+ EXPECT_GL_NO_ERROR();
+
+ GLFramebuffer fbo;
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
+ ASSERT_EGLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+ glDisable(GL_BLEND);
+ glDrawArrays(GL_POINTS, 0, 1);
+ EXPECT_GL_NO_ERROR();
+
+ glEnable(GL_BLEND);
+ glDrawArrays(GL_POINTS, 0, 1);
+ if (shouldBlend)
+ {
+ EXPECT_GL_NO_ERROR();
+ }
+ else
+ {
+ EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+ }
+ }
+
void TestDifferentStencilMaskAndRef(GLenum errIfMismatch);
// Called from RenderingFeedbackLoopWithDrawBuffersEXT.
@@ -2861,6 +2905,234 @@
}
}
+// Test that has float color attachment caching works when color attachments change, by calling draw
+// command when blending is enabled
+TEST_P(WebGLCompatibilityTest, FramebufferFloatColorAttachment)
+{
+ if (getClientMajorVersion() >= 3)
+ {
+ ANGLE_SKIP_TEST_IF(!ensureExtensionEnabled("GL_EXT_color_buffer_float"));
+ }
+ else
+ {
+ ANGLE_SKIP_TEST_IF(!ensureExtensionEnabled("GL_OES_texture_float"));
+ ANGLE_SKIP_TEST_IF(!ensureExtensionEnabled("GL_CHROMIUM_color_buffer_float_rgba"));
+ }
+
+ constexpr char kVS[] =
+ "void main()\n"
+ "{\n"
+ " gl_Position = vec4(0, 0, 0, 1);\n"
+ "}\n";
+
+ constexpr char kFS[] =
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = vec4(0, 1, 0, 1);\n"
+ "}\n";
+
+ ANGLE_GL_PROGRAM(program, kVS, kFS);
+ glUseProgram(program);
+
+ glEnable(GL_BLEND);
+
+ GLTexture texture1;
+ glBindTexture(GL_TEXTURE_2D, texture1);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ EXPECT_GL_NO_ERROR();
+
+ GLTexture texture2;
+ glBindTexture(GL_TEXTURE_2D, texture2);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 1, 1, 0, GL_RGBA, GL_FLOAT, nullptr);
+ EXPECT_GL_NO_ERROR();
+
+ GLFramebuffer fbo1;
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo1);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture1, 0);
+ ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+ GLFramebuffer fbo2;
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo2);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture2, 0);
+ ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+ glDrawArrays(GL_POINTS, 0, 1);
+ EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+ glDisable(GL_BLEND);
+ glDrawArrays(GL_POINTS, 0, 1);
+ EXPECT_GL_NO_ERROR();
+ glEnable(GL_BLEND);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo1);
+ glDrawArrays(GL_POINTS, 0, 1);
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0,
+ 0); // test unbind
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture2, 0);
+ ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+ glDrawArrays(GL_POINTS, 0, 1);
+ EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+ glDisable(GL_BLEND);
+ glDrawArrays(GL_POINTS, 0, 1);
+ EXPECT_GL_NO_ERROR();
+ glEnable(GL_BLEND);
+
+ glBindTexture(GL_TEXTURE_2D, texture2);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+
+ glDrawArrays(GL_POINTS, 0, 1);
+ EXPECT_GL_NO_ERROR();
+}
+
+// Test that has float color attachment caching works with multiple color attachments bound to a
+// Framebuffer
+TEST_P(WebGLCompatibilityTest, FramebufferFloatColorAttachmentMRT)
+{
+ bool isWebGL2 = getClientMajorVersion() >= 3;
+ if (isWebGL2)
+ {
+ ANGLE_SKIP_TEST_IF(!ensureExtensionEnabled("GL_EXT_color_buffer_float"));
+
+ constexpr char kVS[] =
+ "#version 300 es\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = vec4(0, 0, 0, 1);\n"
+ "}\n";
+
+ constexpr char kFS[] =
+ "#version 300 es\n"
+ "precision lowp float;\n"
+ "layout(location = 0) out vec4 o_color0;\n"
+ "layout(location = 1) out vec4 o_color1;\n"
+ "void main()\n"
+ "{\n"
+ " o_color0 = vec4(1, 0, 0, 1);\n"
+ " o_color1 = vec4(0, 1, 0, 1);\n"
+ "}\n";
+
+ ANGLE_GL_PROGRAM(program, kVS, kFS);
+ glUseProgram(program);
+ }
+ else
+ {
+ ANGLE_SKIP_TEST_IF(!ensureExtensionEnabled("GL_OES_texture_float"));
+ ANGLE_SKIP_TEST_IF(!ensureExtensionEnabled("GL_CHROMIUM_color_buffer_float_rgba"));
+ ANGLE_SKIP_TEST_IF(!ensureExtensionEnabled("GL_EXT_draw_buffers"));
+
+ constexpr char kVS[] =
+ "void main()\n"
+ "{\n"
+ " gl_Position = vec4(0, 0, 0, 1);\n"
+ "}\n";
+
+ constexpr char kFS[] =
+ "#extension GL_EXT_draw_buffers : require\n"
+ "precision lowp float;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragData[0] = vec4(1, 0, 0, 1);\n"
+ " gl_FragData[1] = vec4(0, 1, 0, 1);\n"
+ "}\n";
+
+ ANGLE_GL_PROGRAM(program, kVS, kFS);
+ glUseProgram(program);
+ }
+
+ glEnable(GL_BLEND);
+
+ GLTexture texture1;
+ glBindTexture(GL_TEXTURE_2D, texture1);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ EXPECT_GL_NO_ERROR();
+
+ GLTexture texture2;
+ glBindTexture(GL_TEXTURE_2D, texture2);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ EXPECT_GL_NO_ERROR();
+
+ GLTexture textureF1;
+ glBindTexture(GL_TEXTURE_2D, textureF1);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 1, 1, 0, GL_RGBA, GL_FLOAT, nullptr);
+ EXPECT_GL_NO_ERROR();
+
+ GLTexture textureF2;
+ glBindTexture(GL_TEXTURE_2D, textureF2);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, 1, 1, 0, GL_RGBA, GL_FLOAT, nullptr);
+ EXPECT_GL_NO_ERROR();
+
+ GLFramebuffer fbo;
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture1, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, texture2, 0);
+ ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+ GLenum drawbuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
+ if (isWebGL2)
+ {
+ glDrawBuffers(2, drawbuffers);
+ }
+ else
+ {
+ glDrawBuffersEXT(2, drawbuffers);
+ }
+
+ glDrawArrays(GL_POINTS, 0, 1);
+ EXPECT_GL_NO_ERROR();
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureF1, 0);
+ glDrawArrays(GL_POINTS, 0, 1);
+ EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, textureF2, 0);
+ glDrawArrays(GL_POINTS, 0, 1);
+ EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture1, 0);
+ glDrawArrays(GL_POINTS, 0, 1);
+ EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+ if (isWebGL2)
+ {
+ // WebGL 1 will report a FRAMEBUFFER_UNSUPPORTED for one unsigned_byte and one float
+ // attachment bound to one FBO at the same time
+ glDrawBuffers(1, drawbuffers);
+ ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+ glDrawArrays(GL_POINTS, 0, 1);
+ EXPECT_GL_NO_ERROR();
+ glDrawBuffers(2, drawbuffers);
+ }
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, texture2, 0);
+ glDrawArrays(GL_POINTS, 0, 1);
+ EXPECT_GL_NO_ERROR();
+}
+
+// Test if blending of float32 color attachment generates GL_INVALID_OPERATION when
+// GL_EXT_float_blend is not enabled
+TEST_P(WebGLCompatibilityTest, FloatBlend)
+{
+ ANGLE_SKIP_TEST_IF(!extensionRequestable("GL_EXT_float_blend"));
+ if (getClientMajorVersion() >= 3)
+ {
+ ANGLE_SKIP_TEST_IF(!ensureExtensionEnabled("GL_EXT_color_buffer_float"));
+ }
+ else
+ {
+ ANGLE_SKIP_TEST_IF(!ensureExtensionEnabled("GL_OES_texture_float"));
+ ANGLE_SKIP_TEST_IF(!ensureExtensionEnabled("GL_CHROMIUM_color_buffer_float_rgba"));
+ }
+
+ TestExtFloatBlend(false);
+
+ glRequestExtensionANGLE("GL_EXT_float_blend");
+ ASSERT_GL_NO_ERROR();
+
+ TestExtFloatBlend(true);
+}
+
TEST_P(WebGLCompatibilityTest, R16FTextures)
{
constexpr float readPixelsData[] = {-5000.0f, 0.0f, 0.0f, 1.0f};