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,
+                                 &param);
+        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