Support EXT_multisample_compatibility in the GL backend
BUG=angleproject:1377
Change-Id: Ie14aceca8e01f1cbc93fd5bd06d986336fb752b3
Reviewed-on: https://chromium-review.googlesource.com/343501
Reviewed-by: Sami Väisänen <svaisanen@nvidia.com>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Sami Väisänen <svaisanen@nvidia.com>
diff --git a/src/libANGLE/Caps.cpp b/src/libANGLE/Caps.cpp
index 56580b3..ba1f7d4 100644
--- a/src/libANGLE/Caps.cpp
+++ b/src/libANGLE/Caps.cpp
@@ -160,7 +160,8 @@
lossyETCDecode(false),
bindUniformLocation(false),
syncQuery(false),
- colorBufferFloat(false)
+ colorBufferFloat(false),
+ multisampleCompatibility(false)
{
}
@@ -234,6 +235,7 @@
InsertExtensionString("GL_ANGLE_lossy_etc_decode", lossyETCDecode, &extensionStrings);
InsertExtensionString("GL_CHROMIUM_bind_uniform_location", bindUniformLocation, &extensionStrings);
InsertExtensionString("GL_CHROMIUM_sync_query", syncQuery, &extensionStrings);
+ InsertExtensionString("GL_EXT_multisample_compatibility", multisampleCompatibility, &extensionStrings);
// clang-format on
return extensionStrings;
diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h
index 2ac3ef4..0104381 100644
--- a/src/libANGLE/Caps.h
+++ b/src/libANGLE/Caps.h
@@ -291,6 +291,10 @@
// GL_EXT_color_buffer_float
bool colorBufferFloat;
+
+ // GL_EXT_multisample_compatibility.
+ // written against ES 3.1 but can apply to earlier versions.
+ bool multisampleCompatibility;
};
struct Limitations
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index eaa4a51..4330803 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -1405,6 +1405,18 @@
}
}
+ if (mExtensions.multisampleCompatibility)
+ {
+ switch (pname)
+ {
+ case GL_MULTISAMPLE_EXT:
+ case GL_SAMPLE_ALPHA_TO_ONE_EXT:
+ *type = GL_BOOL;
+ *numParams = 1;
+ return true;
+ }
+ }
+
// Check for ES3.0+ parameter names which are also exposed as ES2 extensions
switch (pname)
{
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index 40f0656..4c923e7 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -42,7 +42,9 @@
mProgram(nullptr),
mVertexArray(nullptr),
mActiveSampler(0),
- mPrimitiveRestart(false)
+ mPrimitiveRestart(false),
+ mMultiSampling(false),
+ mSampleAlphaToOne(false)
{
}
@@ -170,6 +172,12 @@
mDebug.setOutputEnabled(debug);
mDebug.setMaxLoggedMessages(extensions.maxDebugLoggedMessages);
+
+ if (extensions.framebufferMultisample)
+ {
+ mMultiSampling = true;
+ mSampleAlphaToOne = false;
+ }
}
void State::reset()
@@ -504,6 +512,28 @@
return mSampleCoverageInvert;
}
+void State::setSampleAlphaToOne(bool enabled)
+{
+ mSampleAlphaToOne = enabled;
+ mDirtyBits.set(DIRTY_BIT_SAMPLE_ALPHA_TO_ONE);
+}
+
+bool State::isSampleAlphaToOneEnabled() const
+{
+ return mSampleAlphaToOne;
+}
+
+void State::setMultisampling(bool enabled)
+{
+ mMultiSampling = enabled;
+ mDirtyBits.set(DIRTY_BIT_MULTISAMPLING);
+}
+
+bool State::isMultisamplingEnabled() const
+{
+ return mMultiSampling;
+}
+
bool State::isScissorTestEnabled() const
{
return mScissorTest;
@@ -555,6 +585,8 @@
{
switch (feature)
{
+ case GL_MULTISAMPLE_EXT: setMultisampling(enabled); break;
+ case GL_SAMPLE_ALPHA_TO_ONE_EXT: setSampleAlphaToOne(enabled); break;
case GL_CULL_FACE: setCullFace(enabled); break;
case GL_POLYGON_OFFSET_FILL: setPolygonOffsetFill(enabled); break;
case GL_SAMPLE_ALPHA_TO_COVERAGE: setSampleAlphaToCoverage(enabled); break;
@@ -580,6 +612,8 @@
{
switch (feature)
{
+ case GL_MULTISAMPLE_EXT: return isMultisamplingEnabled();
+ case GL_SAMPLE_ALPHA_TO_ONE_EXT: return isSampleAlphaToOneEnabled();
case GL_CULL_FACE: return isCullFaceEnabled();
case GL_POLYGON_OFFSET_FILL: return isPolygonOffsetFillEnabled();
case GL_SAMPLE_ALPHA_TO_COVERAGE: return isSampleAlphaToCoverageEnabled();
@@ -1360,6 +1394,12 @@
case GL_DEBUG_OUTPUT:
*params = mDebug.isOutputEnabled() ? GL_TRUE : GL_FALSE;
break;
+ case GL_MULTISAMPLE_EXT:
+ *params = mMultiSampling;
+ break;
+ case GL_SAMPLE_ALPHA_TO_ONE_EXT:
+ *params = mSampleAlphaToOne;
+ break;
default:
UNREACHABLE();
break;
@@ -1395,6 +1435,12 @@
params[2] = mBlendColor.blue;
params[3] = mBlendColor.alpha;
break;
+ case GL_MULTISAMPLE_EXT:
+ *params = static_cast<GLfloat>(mMultiSampling);
+ break;
+ case GL_SAMPLE_ALPHA_TO_ONE_EXT:
+ *params = static_cast<GLfloat>(mSampleAlphaToOne);
+ break;
default:
UNREACHABLE();
break;
@@ -1633,6 +1679,12 @@
case GL_DEBUG_GROUP_STACK_DEPTH:
*params = static_cast<GLint>(mDebug.getGroupStackDepth());
break;
+ case GL_MULTISAMPLE_EXT:
+ *params = static_cast<GLint>(mMultiSampling);
+ break;
+ case GL_SAMPLE_ALPHA_TO_ONE_EXT:
+ *params = static_cast<GLint>(mSampleAlphaToOne);
+ break;
default:
UNREACHABLE();
break;
diff --git a/src/libANGLE/State.h b/src/libANGLE/State.h
index 727da3e..25ca0bd 100644
--- a/src/libANGLE/State.h
+++ b/src/libANGLE/State.h
@@ -118,6 +118,12 @@
GLclampf getSampleCoverageValue() const;
bool getSampleCoverageInvert() const;
+ // Multisampling/alpha to one manipulation.
+ void setSampleAlphaToOne(bool enabled);
+ bool isSampleAlphaToOneEnabled() const;
+ void setMultisampling(bool enabled);
+ bool isMultisamplingEnabled() const;
+
// Scissor test state toggle & query
bool isScissorTestEnabled() const;
void setScissorTest(bool enabled);
@@ -334,6 +340,8 @@
DIRTY_BIT_RENDERBUFFER_BINDING,
DIRTY_BIT_VERTEX_ARRAY_BINDING,
DIRTY_BIT_PROGRAM_BINDING,
+ DIRTY_BIT_MULTISAMPLING,
+ DIRTY_BIT_SAMPLE_ALPHA_TO_ONE,
DIRTY_BIT_CURRENT_VALUE_0,
DIRTY_BIT_CURRENT_VALUE_MAX = DIRTY_BIT_CURRENT_VALUE_0 + MAX_VERTEX_ATTRIBS,
DIRTY_BIT_INVALID = DIRTY_BIT_CURRENT_VALUE_MAX,
@@ -436,6 +444,9 @@
Debug mDebug;
+ bool mMultiSampling;
+ bool mSampleAlphaToOne;
+
DirtyBits mDirtyBits;
DirtyObjects mDirtyObjects;
};
diff --git a/src/libANGLE/renderer/gl/StateManagerGL.cpp b/src/libANGLE/renderer/gl/StateManagerGL.cpp
index 8d2ebf5..3ea6ae3 100644
--- a/src/libANGLE/renderer/gl/StateManagerGL.cpp
+++ b/src/libANGLE/renderer/gl/StateManagerGL.cpp
@@ -113,6 +113,8 @@
mClearStencil(0),
mFramebufferSRGBEnabled(false),
mTextureCubemapSeamlessEnabled(false),
+ mMultisamplingEnabled(true),
+ mSampleAlphaToOneEnabled(false),
mLocalDirtyBits()
{
ASSERT(mFunctions);
@@ -1513,6 +1515,12 @@
case gl::State::DIRTY_BIT_PROGRAM_BINDING:
// TODO(jmadill): implement this
break;
+ case gl::State::DIRTY_BIT_MULTISAMPLING:
+ setMultisamplingStateEnabled(state.isMultisamplingEnabled());
+ break;
+ case gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_ONE:
+ setSampleAlphaToOneStateEnabled(state.isSampleAlphaToOneEnabled());
+ break;
default:
{
ASSERT(dirtyBit >= gl::State::DIRTY_BIT_CURRENT_VALUE_0 &&
@@ -1545,6 +1553,40 @@
}
}
+void StateManagerGL::setMultisamplingStateEnabled(bool enabled)
+{
+ if (mMultisamplingEnabled != enabled)
+ {
+ mMultisamplingEnabled = enabled;
+ if (mMultisamplingEnabled)
+ {
+ mFunctions->enable(GL_MULTISAMPLE_EXT);
+ }
+ else
+ {
+ mFunctions->disable(GL_MULTISAMPLE_EXT);
+ }
+ mLocalDirtyBits.set(gl::State::DIRTY_BIT_MULTISAMPLING);
+ }
+}
+
+void StateManagerGL::setSampleAlphaToOneStateEnabled(bool enabled)
+{
+ if (mSampleAlphaToOneEnabled != enabled)
+ {
+ mSampleAlphaToOneEnabled = enabled;
+ if (mSampleAlphaToOneEnabled)
+ {
+ mFunctions->enable(GL_SAMPLE_ALPHA_TO_ONE);
+ }
+ else
+ {
+ mFunctions->disable(GL_SAMPLE_ALPHA_TO_ONE);
+ }
+ mLocalDirtyBits.set(gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_ONE);
+ }
+}
+
void StateManagerGL::setTextureCubemapSeamlessEnabled(bool enabled)
{
if (mTextureCubemapSeamlessEnabled != enabled)
diff --git a/src/libANGLE/renderer/gl/StateManagerGL.h b/src/libANGLE/renderer/gl/StateManagerGL.h
index 4978d96..4d42a94 100644
--- a/src/libANGLE/renderer/gl/StateManagerGL.h
+++ b/src/libANGLE/renderer/gl/StateManagerGL.h
@@ -124,6 +124,9 @@
void setFramebufferSRGBEnabled(bool enabled);
+ void setMultisamplingStateEnabled(bool enabled);
+ void setSampleAlphaToOneStateEnabled(bool enabled);
+
void onDeleteQueryObject(QueryGL *query);
gl::Error setDrawArraysState(const gl::ContextState &data,
@@ -254,6 +257,9 @@
bool mFramebufferSRGBEnabled;
bool mTextureCubemapSeamlessEnabled;
+ bool mMultisamplingEnabled;
+ bool mSampleAlphaToOneEnabled;
+
gl::State::DirtyBits mLocalDirtyBits;
};
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp
index e6549aa..4f0ab95 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.cpp
+++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp
@@ -657,6 +657,12 @@
QueryQueryValue(functions, GL_TIMESTAMP, GL_QUERY_COUNTER_BITS);
}
+ // the EXT_multisample_compatibility is written against ES3.1 but can apply
+ // to earlier versions so therefore we're only checking for the extension string
+ // and not the specific GLES version.
+ extensions->multisampleCompatibility = functions->isAtLeastGL(gl::Version(1, 3)) ||
+ functions->hasGLESExtension("GL_EXT_multisample_compatibility");
+
// ANGLE emulates vertex array objects in its GL layer
extensions->vertexArrayObject = true;
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index 40db7aa..76e8c64 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -102,6 +102,11 @@
{
switch (cap)
{
+ // EXT_multisample_compatibility
+ case GL_MULTISAMPLE_EXT:
+ case GL_SAMPLE_ALPHA_TO_ONE_EXT:
+ return context->getExtensions().multisampleCompatibility;
+
case GL_CULL_FACE:
case GL_POLYGON_OFFSET_FILL:
case GL_SAMPLE_ALPHA_TO_COVERAGE:
diff --git a/src/tests/angle_end2end_tests.gypi b/src/tests/angle_end2end_tests.gypi
index 37490b2..f83971d 100644
--- a/src/tests/angle_end2end_tests.gypi
+++ b/src/tests/angle_end2end_tests.gypi
@@ -15,6 +15,7 @@
{
'angle_end2end_tests_sources':
[
+ '<(angle_path)/src/tests/gl_tests/MultisampleCompatibilityTest.cpp',
'<(angle_path)/src/tests/gl_tests/BindUniformLocationTest.cpp',
'<(angle_path)/src/tests/gl_tests/BlendMinMaxTest.cpp',
'<(angle_path)/src/tests/gl_tests/BlitFramebufferANGLETest.cpp',
diff --git a/src/tests/gl_tests/MultisampleCompatibilityTest.cpp b/src/tests/gl_tests/MultisampleCompatibilityTest.cpp
new file mode 100644
index 0000000..30708ee
--- /dev/null
+++ b/src/tests/gl_tests/MultisampleCompatibilityTest.cpp
@@ -0,0 +1,300 @@
+//
+// Copyright 2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// StateChangeTest:
+// Specifically designed for an ANGLE implementation of GL, these tests validate that
+// ANGLE's dirty bits systems don't get confused by certain sequences of state changes.
+//
+
+#include "test_utils/ANGLETest.h"
+#include "shader_utils.h"
+
+using namespace angle;
+
+namespace
+{
+
+const GLint kWidth = 64;
+const GLint kHeight = 64;
+
+// test drawing with GL_MULTISAMPLE_EXT enabled/disabled.
+class EXTMultisampleCompatibilityTest : public ANGLETest
+{
+
+protected:
+ EXTMultisampleCompatibilityTest()
+ {
+ setWindowWidth(64);
+ setWindowHeight(64);
+ setConfigRedBits(8);
+ setConfigBlueBits(8);
+ setConfigAlphaBits(8);
+ }
+
+ void SetUp() override
+ {
+ ANGLETest::SetUp();
+
+ static const char* v_shader_str =
+ "attribute vec4 a_Position;\n"
+ "void main()\n"
+ "{ gl_Position = a_Position; }";
+
+ static const char* f_shader_str =
+ "precision mediump float;\n"
+ "uniform vec4 color;"
+ "void main() { gl_FragColor = color; }";
+
+ mProgram = CompileProgram(v_shader_str, f_shader_str);
+
+ GLuint position_loc = glGetAttribLocation(mProgram, "a_Position");
+ mColorLoc = glGetUniformLocation(mProgram, "color");
+
+ glGenBuffers(1, &mVBO);
+ glBindBuffer(GL_ARRAY_BUFFER, mVBO);
+ static float vertices[] = {
+ 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
+ -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
+ };
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+ glEnableVertexAttribArray(position_loc);
+ glVertexAttribPointer(position_loc, 2, GL_FLOAT, GL_FALSE, 0, 0);
+ }
+
+ void TearDown() override
+ {
+ glDeleteBuffers(1, &mVBO);
+ glDeleteProgram(mProgram);
+
+ ANGLETest::TearDown();
+ }
+
+ void prepareForDraw()
+ {
+ // Create a sample buffer.
+ GLsizei num_samples = 4, max_samples = 0;
+ glGetIntegerv(GL_MAX_SAMPLES, &max_samples);
+ num_samples = std::min(num_samples, max_samples);
+
+ glGenRenderbuffers(1, &mSampleRB);
+ glBindRenderbuffer(GL_RENDERBUFFER, mSampleRB);
+ glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER, num_samples,
+ GL_RGBA8_OES, kWidth, kHeight);
+ GLint param = 0;
+ glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES,
+ ¶m);
+ EXPECT_GE(param, num_samples);
+
+ glGenFramebuffers(1, &mSampleFBO);
+ glBindFramebuffer(GL_FRAMEBUFFER, mSampleFBO);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_RENDERBUFFER, mSampleRB);
+ EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
+ glCheckFramebufferStatus(GL_FRAMEBUFFER));
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ // Create another FBO to resolve the multisample buffer into.
+ glGenTextures(1, &mResolveTex);
+ glBindTexture(GL_TEXTURE_2D, mResolveTex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, NULL);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glGenFramebuffers(1, &mResolveFBO);
+ glBindFramebuffer(GL_FRAMEBUFFER, mResolveFBO);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ mResolveTex, 0);
+ EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
+ glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+ glUseProgram(mProgram);
+ glViewport(0, 0, kWidth, kHeight);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ glBindFramebuffer(GL_FRAMEBUFFER, mSampleFBO);
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+
+ void prepareForVerify()
+ {
+ // Resolve.
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, mSampleFBO);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mResolveFBO);
+ glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glBlitFramebufferANGLE(0, 0, kWidth, kHeight, 0, 0, kWidth, kHeight,
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, mResolveFBO);
+
+ ASSERT_GL_NO_ERROR();
+ }
+
+ void cleanup()
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glDeleteFramebuffers(1, &mResolveFBO);
+ glDeleteFramebuffers(1, &mSampleFBO);
+ glDeleteTextures(1, &mResolveTex);
+ glDeleteRenderbuffers(1, &mSampleRB);
+
+ ASSERT_GL_NO_ERROR();
+
+ }
+
+ bool isApplicable() const
+ {
+ return extensionEnabled("GL_EXT_multisample_compatibility") &&
+ extensionEnabled("GL_ANGLE_framebuffer_multisample") &&
+ extensionEnabled("GL_OES_rgb8_rgba8") &&
+ !IsAMD();
+ }
+ GLuint mSampleFBO;
+ GLuint mResolveFBO;
+ GLuint mSampleRB;
+ GLuint mResolveTex;
+
+ GLuint mColorLoc;
+ GLuint mProgram;
+ GLuint mVBO;
+};
+
+} //
+
+// Test simple state tracking
+TEST_P(EXTMultisampleCompatibilityTest, TestStateTracking)
+{
+ if (!isApplicable())
+ return;
+
+ EXPECT_TRUE(glIsEnabled(GL_MULTISAMPLE_EXT));
+ glDisable(GL_MULTISAMPLE_EXT);
+ EXPECT_FALSE(glIsEnabled(GL_MULTISAMPLE_EXT));
+ glEnable(GL_MULTISAMPLE_EXT);
+ EXPECT_TRUE(glIsEnabled(GL_MULTISAMPLE_EXT));
+
+ EXPECT_FALSE(glIsEnabled(GL_SAMPLE_ALPHA_TO_ONE_EXT));
+ glEnable(GL_SAMPLE_ALPHA_TO_ONE_EXT);
+ EXPECT_TRUE(glIsEnabled(GL_SAMPLE_ALPHA_TO_ONE_EXT));
+ glDisable(GL_SAMPLE_ALPHA_TO_ONE_EXT);
+ EXPECT_FALSE(glIsEnabled(GL_SAMPLE_ALPHA_TO_ONE_EXT));
+
+ EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
+}
+
+// Test that disabling GL_MULTISAMPLE_EXT is handled correctly.
+TEST_P(EXTMultisampleCompatibilityTest, DrawAndResolve)
+{
+ if (!isApplicable())
+ return;
+
+ static const float kBlue[] = {0.0f, 0.0f, 1.0f, 1.0f};
+ static const float kGreen[] = {0.0f, 1.0f, 0.0f, 1.0f};
+ static const float kRed[] = {1.0f, 0.0f, 0.0f, 1.0f};
+
+ // Different drivers seem to behave differently with respect to resulting
+ // values. These might be due to different MSAA sample counts causing
+ // different samples to hit. Other option is driver bugs. Just test that
+ // disabling multisample causes a difference.
+ std::unique_ptr<uint8_t[]> results[3];
+ const GLint kResultSize = kWidth * kHeight * 4;
+ for (int pass = 0; pass < 3; pass++)
+ {
+ prepareForDraw();
+ // Green: from top right to bottom left.
+ glUniform4fv(mColorLoc, 1, kGreen);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+
+ // Blue: from top left to bottom right.
+ glUniform4fv(mColorLoc, 1, kBlue);
+ glDrawArrays(GL_TRIANGLES, 3, 3);
+
+ // Red, with and without MSAA: from bottom left to top right.
+ if (pass == 1)
+ {
+ glDisable(GL_MULTISAMPLE_EXT);
+ }
+ glUniform4fv(mColorLoc, 1, kRed);
+ glDrawArrays(GL_TRIANGLES, 6, 3);
+ if (pass == 1)
+ {
+ glEnable(GL_MULTISAMPLE_EXT);
+ }
+ prepareForVerify();
+ results[pass].reset(new uint8_t[kResultSize]);
+ memset(results[pass].get(), 123u, kResultSize);
+ glReadPixels(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE,
+ results[pass].get());
+
+ cleanup();
+ }
+ EXPECT_NE(0, memcmp(results[0].get(), results[1].get(), kResultSize));
+ // Verify that rendering is deterministic, so that the pass above does not
+ // come from non-deterministic rendering.
+ EXPECT_EQ(0, memcmp(results[0].get(), results[2].get(), kResultSize));
+}
+
+// Test that enabling GL_SAMPLE_ALPHA_TO_ONE_EXT affects rendering.
+TEST_P(EXTMultisampleCompatibilityTest, DrawAlphaOneAndResolve)
+{
+ if (!isApplicable())
+ return;
+
+ // SAMPLE_ALPHA_TO_ONE is specified to transform alpha values of
+ // covered samples to 1.0. In order to detect it, we use non-1.0
+ // alpha.
+ static const float kBlue[] = {0.0f, 0.0f, 1.0f, 0.5f};
+ static const float kGreen[] = {0.0f, 1.0f, 0.0f, 0.5f};
+ static const float kRed[] = {1.0f, 0.0f, 0.0f, 0.5f};
+
+ // Different drivers seem to behave differently with respect to resulting
+ // alpha value. These might be due to different MSAA sample counts causing
+ // different samples to hit. Other option is driver bugs. Testing exact or
+ // even approximate sample values is not that easy. Thus, just test
+ // representative positions which have fractional pixels, inspecting that
+ // normal rendering is different to SAMPLE_ALPHA_TO_ONE rendering.
+ std::unique_ptr<uint8_t[]> results[3];
+ const GLint kResultSize = kWidth * kHeight * 4;
+
+ for (int pass = 0; pass < 3; ++pass)
+ {
+ prepareForDraw();
+ if (pass == 1)
+ {
+ glEnable(GL_SAMPLE_ALPHA_TO_ONE_EXT);
+ }
+ glEnable(GL_MULTISAMPLE_EXT);
+ glUniform4fv(mColorLoc, 1, kGreen);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+
+ glUniform4fv(mColorLoc, 1, kBlue);
+ glDrawArrays(GL_TRIANGLES, 3, 3);
+
+ glDisable(GL_MULTISAMPLE_EXT);
+ glUniform4fv(mColorLoc, 1, kRed);
+ glDrawArrays(GL_TRIANGLES, 6, 3);
+
+ prepareForVerify();
+ results[pass].reset(new uint8_t[kResultSize]);
+ memset(results[pass].get(), 123u, kResultSize);
+ glReadPixels(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE,
+ results[pass].get());
+ if (pass == 1)
+ {
+ glDisable(GL_SAMPLE_ALPHA_TO_ONE_EXT);
+ }
+
+ cleanup();
+ }
+ EXPECT_NE(0, memcmp(results[0].get(), results[1].get(), kResultSize));
+ // Verify that rendering is deterministic, so that the pass above does not
+ // come from non-deterministic rendering.
+ EXPECT_EQ(0, memcmp(results[0].get(), results[2].get(), kResultSize));
+}
+
+
+ANGLE_INSTANTIATE_TEST(EXTMultisampleCompatibilityTest, ES2_OPENGL(), ES2_OPENGLES(), ES3_OPENGL());
\ No newline at end of file