WebGL compatibility: add stencil mask and ref restriction

BUG=angleproject:1523
BUG=chromium:668223

Change-Id: I0726769c938fdfd50af0fad1cef1746d4af2a589
Reviewed-on: https://chromium-review.googlesource.com/422084
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index cd248fb..3bb107a 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -531,8 +531,7 @@
 
 GLuint Context::createShader(GLenum type)
 {
-    return mResourceManager->createShader(mImplementation.get(),
-                                          mImplementation->getNativeLimitations(), type);
+    return mResourceManager->createShader(mImplementation.get(), mLimitations, type);
 }
 
 GLuint Context::createTexture()
diff --git a/src/libANGLE/Framebuffer.cpp b/src/libANGLE/Framebuffer.cpp
index ee72b36..72886d6 100644
--- a/src/libANGLE/Framebuffer.cpp
+++ b/src/libANGLE/Framebuffer.cpp
@@ -132,6 +132,15 @@
     return nullptr;
 }
 
+const FramebufferAttachment *FramebufferState::getStencilOrDepthStencilAttachment() const
+{
+    if (mStencilAttachment.isAttached())
+    {
+        return &mStencilAttachment;
+    }
+    return getDepthStencilAttachment();
+}
+
 const FramebufferAttachment *FramebufferState::getColorAttachment(size_t colorAttachment) const
 {
     ASSERT(colorAttachment < mColorAttachments.size());
@@ -360,6 +369,11 @@
     return mState.getDepthOrStencilAttachment();
 }
 
+const FramebufferAttachment *Framebuffer::getStencilOrDepthStencilAttachment() const
+{
+    return mState.getStencilOrDepthStencilAttachment();
+}
+
 const FramebufferAttachment *Framebuffer::getReadColorbuffer() const
 {
     return mState.getReadAttachment();
diff --git a/src/libANGLE/Framebuffer.h b/src/libANGLE/Framebuffer.h
index db0d621..bdc7a30 100644
--- a/src/libANGLE/Framebuffer.h
+++ b/src/libANGLE/Framebuffer.h
@@ -62,6 +62,7 @@
     const FramebufferAttachment *getReadAttachment() const;
     const FramebufferAttachment *getFirstColorAttachment() const;
     const FramebufferAttachment *getDepthOrStencilAttachment() const;
+    const FramebufferAttachment *getStencilOrDepthStencilAttachment() const;
     const FramebufferAttachment *getColorAttachment(size_t colorAttachment) const;
     const FramebufferAttachment *getDepthAttachment() const;
     const FramebufferAttachment *getStencilAttachment() const;
@@ -121,6 +122,7 @@
     const FramebufferAttachment *getStencilbuffer() const;
     const FramebufferAttachment *getDepthStencilBuffer() const;
     const FramebufferAttachment *getDepthOrStencilbuffer() const;
+    const FramebufferAttachment *getStencilOrDepthStencilAttachment() const;
     const FramebufferAttachment *getReadColorbuffer() const;
     GLenum getReadColorbufferType() const;
     const FramebufferAttachment *getFirstColorbuffer() const;
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index 9483931..afd7a5a 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -3114,17 +3114,23 @@
     }
 
     Framebuffer *framebuffer = state.getDrawFramebuffer();
-    if (context->getLimitations().noSeparateStencilRefsAndMasks)
+    if (context->getLimitations().noSeparateStencilRefsAndMasks ||
+        context->getExtensions().webglCompatibility)
     {
-        const FramebufferAttachment *stencilBuffer = framebuffer->getStencilbuffer();
-        GLuint stencilBits                = stencilBuffer ? stencilBuffer->getStencilSize() : 0;
+        const FramebufferAttachment *dsAttachment =
+            framebuffer->getStencilOrDepthStencilAttachment();
+        GLuint stencilBits                = dsAttachment ? dsAttachment->getStencilSize() : 0;
         GLuint minimumRequiredStencilMask = (1 << stencilBits) - 1;
         const DepthStencilState &depthStencilState = state.getDepthStencilState();
-        if ((depthStencilState.stencilWritemask & minimumRequiredStencilMask) !=
-                (depthStencilState.stencilBackWritemask & minimumRequiredStencilMask) ||
-            state.getStencilRef() != state.getStencilBackRef() ||
-            (depthStencilState.stencilMask & minimumRequiredStencilMask) !=
-                (depthStencilState.stencilBackMask & minimumRequiredStencilMask))
+
+        bool differentRefs = state.getStencilRef() != state.getStencilBackRef();
+        bool differentWritemasks =
+            (depthStencilState.stencilWritemask & minimumRequiredStencilMask) !=
+            (depthStencilState.stencilBackWritemask & minimumRequiredStencilMask);
+        bool differentMasks = (depthStencilState.stencilMask & minimumRequiredStencilMask) !=
+                              (depthStencilState.stencilBackMask & minimumRequiredStencilMask);
+
+        if (differentRefs || differentWritemasks || differentMasks)
         {
             // Note: these separate values are not supported in WebGL, due to D3D's limitations. See
             // Section 6.10 of the WebGL 1.0 spec
diff --git a/src/tests/gl_tests/WebGLCompatibilityTest.cpp b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
index 8464111..86f4954 100644
--- a/src/tests/gl_tests/WebGLCompatibilityTest.cpp
+++ b/src/tests/gl_tests/WebGLCompatibilityTest.cpp
@@ -212,6 +212,66 @@
     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
 }
 
+// Tests the WebGL requirement of having the same stencil mask, writemask and ref for fron and back
+TEST_P(WebGLCompatibilityTest, RequiresSameStencilMaskAndRef)
+{
+    // Run the test in an FBO to make sure we have some stencil bits.
+    GLRenderbuffer renderbuffer;
+    glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer.get());
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 32, 32);
+
+    GLFramebuffer framebuffer;
+    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
+                              renderbuffer.get());
+
+    ANGLE_GL_PROGRAM(program, "void main() { gl_Position = vec4(0, 0, 0, 1); }",
+                     "void main() { gl_FragColor = vec4(0, 1, 0, 1); }")
+    glUseProgram(program.get());
+    ASSERT_GL_NO_ERROR();
+
+    // Having ref and mask the same for front and back is valid.
+    glStencilMask(255);
+    glStencilFunc(GL_ALWAYS, 0, 255);
+    glDrawArrays(GL_TRIANGLES, 0, 6);
+    ASSERT_GL_NO_ERROR();
+
+    // Having a different front - back write mask generates an error.
+    glStencilMaskSeparate(GL_FRONT, 1);
+    glDrawArrays(GL_TRIANGLES, 0, 6);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    // Setting both write masks separately to the same value is valid.
+    glStencilMaskSeparate(GL_BACK, 1);
+    glDrawArrays(GL_TRIANGLES, 0, 6);
+    ASSERT_GL_NO_ERROR();
+
+    // Having a different stencil front - back mask generates an error
+    glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 0, 1);
+    glDrawArrays(GL_TRIANGLES, 0, 6);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    // Setting both masks separately to the same value is valid.
+    glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 1);
+    glDrawArrays(GL_TRIANGLES, 0, 6);
+    ASSERT_GL_NO_ERROR();
+
+    // Having a different stencil front - back reference generates an error
+    glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, 255, 1);
+    glDrawArrays(GL_TRIANGLES, 0, 6);
+    EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+    // Setting both references separately to the same value is valid.
+    glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 255, 1);
+    glDrawArrays(GL_TRIANGLES, 0, 6);
+    ASSERT_GL_NO_ERROR();
+
+    // Using different stencil funcs, everything being equal is valid.
+    glStencilFuncSeparate(GL_BACK, GL_NEVER, 255, 1);
+    glDrawArrays(GL_TRIANGLES, 0, 6);
+    ASSERT_GL_NO_ERROR();
+}
+
 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
 // tests should be run against.
 ANGLE_INSTANTIATE_TEST(WebGLCompatibilityTest,