Implement GL_RASTERIZER_DISCARD.

BUG=angle:498

Change-Id: Ib60c39e206003ae67c93769e35f7f9ef790ce9f4
Reviewed-on: https://chromium-review.googlesource.com/184396
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Tested-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libGLESv2/Context.cpp b/src/libGLESv2/Context.cpp
index 379dc82..92227b5 100644
--- a/src/libGLESv2/Context.cpp
+++ b/src/libGLESv2/Context.cpp
@@ -59,6 +59,7 @@
     mState.depthClearValue = 1.0f;
     mState.stencilClearValue = 0;
 
+    mState.rasterizer.rasterizerDiscard = false;
     mState.rasterizer.cullFace = false;
     mState.rasterizer.cullMode = GL_BACK;
     mState.rasterizer.frontFace = GL_CCW;
@@ -405,6 +406,44 @@
     return mContextLost;
 }
 
+void Context::setCap(GLenum cap, bool enabled)
+{
+    switch (cap)
+    {
+      case GL_CULL_FACE:                     setCullFace(enabled);              break;
+      case GL_POLYGON_OFFSET_FILL:           setPolygonOffsetFill(enabled);     break;
+      case GL_SAMPLE_ALPHA_TO_COVERAGE:      setSampleAlphaToCoverage(enabled); break;
+      case GL_SAMPLE_COVERAGE:               setSampleCoverage(enabled);        break;
+      case GL_SCISSOR_TEST:                  setScissorTest(enabled);           break;
+      case GL_STENCIL_TEST:                  setStencilTest(enabled);           break;
+      case GL_DEPTH_TEST:                    setDepthTest(enabled);             break;
+      case GL_BLEND:                         setBlend(enabled);                 break;
+      case GL_DITHER:                        setDither(enabled);                break;
+      case GL_PRIMITIVE_RESTART_FIXED_INDEX: UNIMPLEMENTED();                   break;
+      case GL_RASTERIZER_DISCARD:            setRasterizerDiscard(enabled);     break;
+      default:                               UNREACHABLE();
+    }
+}
+
+bool Context::getCap(GLenum cap)
+{
+    switch (cap)
+    {
+      case GL_CULL_FACE:                     return isCullFaceEnabled();
+      case GL_POLYGON_OFFSET_FILL:           return isPolygonOffsetFillEnabled();
+      case GL_SAMPLE_ALPHA_TO_COVERAGE:      return isSampleAlphaToCoverageEnabled();
+      case GL_SAMPLE_COVERAGE:               return isSampleCoverageEnabled();
+      case GL_SCISSOR_TEST:                  return isScissorTestEnabled();
+      case GL_STENCIL_TEST:                  return isStencilTestEnabled();
+      case GL_DEPTH_TEST:                    return isDepthTestEnabled();
+      case GL_BLEND:                         return isBlendEnabled();
+      case GL_DITHER:                        return isDitherEnabled();
+      case GL_PRIMITIVE_RESTART_FIXED_INDEX: UNIMPLEMENTED(); return false;
+      case GL_RASTERIZER_DISCARD:            return isRasterizerDiscardEnabled();
+      default:                               UNREACHABLE(); return false;
+    }
+}
+
 void Context::setClearColor(float red, float green, float blue, float alpha)
 {
     mState.colorClearValue.red = red;
@@ -423,6 +462,16 @@
     mState.stencilClearValue = stencil;
 }
 
+void Context::setRasterizerDiscard(bool enabled)
+{
+    mState.rasterizer.rasterizerDiscard = enabled;
+}
+
+bool Context::isRasterizerDiscardEnabled() const
+{
+    return mState.rasterizer.rasterizerDiscard;
+}
+
 void Context::setCullFace(bool enabled)
 {
     mState.rasterizer.cullFace = enabled;
@@ -2400,10 +2449,10 @@
 }
 
 // Applies the shaders and shader constants to the Direct3D 9 device
-void Context::applyShaders(ProgramBinary *programBinary)
+void Context::applyShaders(ProgramBinary *programBinary, bool rasterizerDiscard)
 {
-    mRenderer->applyShaders(programBinary);
-    
+    mRenderer->applyShaders(programBinary, rasterizerDiscard);
+
     programBinary->applyUniforms();
 }
 
@@ -2550,6 +2599,11 @@
 
 void Context::clear(GLbitfield mask)
 {
+    if (isRasterizerDiscardEnabled())
+    {
+        return;
+    }
+
     ClearParameters clearParams = { 0 };
     for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++)
     {
@@ -2618,8 +2672,12 @@
 
 void Context::clearBufferfv(GLenum buffer, int drawbuffer, const float *values)
 {
-    // glClearBufferfv can be called to clear the color buffer or depth buffer
+    if (isRasterizerDiscardEnabled())
+    {
+        return;
+    }
 
+    // glClearBufferfv can be called to clear the color buffer or depth buffer
     ClearParameters clearParams = { 0 };
 
     if (buffer == GL_COLOR)
@@ -2673,8 +2731,12 @@
 
 void Context::clearBufferuiv(GLenum buffer, int drawbuffer, const unsigned int *values)
 {
-    // glClearBufferuv can only be called to clear a color buffer
+    if (isRasterizerDiscardEnabled())
+    {
+        return;
+    }
 
+    // glClearBufferuv can only be called to clear a color buffer
     ClearParameters clearParams = { 0 };
     for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++)
     {
@@ -2704,8 +2766,12 @@
 
 void Context::clearBufferiv(GLenum buffer, int drawbuffer, const int *values)
 {
-    // glClearBufferfv can be called to clear the color buffer or stencil buffer
+    if (isRasterizerDiscardEnabled())
+    {
+        return;
+    }
 
+    // glClearBufferfv can be called to clear the color buffer or stencil buffer
     ClearParameters clearParams = { 0 };
 
     if (buffer == GL_COLOR)
@@ -2760,8 +2826,12 @@
 
 void Context::clearBufferfi(GLenum buffer, int drawbuffer, float depth, int stencil)
 {
-    // glClearBufferfi can only be called to clear a depth stencil buffer
+    if (isRasterizerDiscardEnabled())
+    {
+        return;
+    }
 
+    // glClearBufferfi can only be called to clear a depth stencil buffer
     ClearParameters clearParams = { 0 };
     for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++)
     {
@@ -2851,7 +2921,7 @@
         return gl::error(err);
     }
 
-    applyShaders(programBinary);
+    applyShaders(programBinary, mState.rasterizer.rasterizerDiscard);
     applyTextures(programBinary);
 
     if (!applyUniformBuffers())
@@ -2914,7 +2984,7 @@
         return gl::error(err);
     }
 
-    applyShaders(programBinary);
+    applyShaders(programBinary, mState.rasterizer.rasterizerDiscard);
     applyTextures(programBinary);
 
     if (!applyUniformBuffers())
diff --git a/src/libGLESv2/Context.h b/src/libGLESv2/Context.h
index f5c4fc4..4ba106d 100644
--- a/src/libGLESv2/Context.h
+++ b/src/libGLESv2/Context.h
@@ -146,12 +146,18 @@
     bool isContextLost();
 
     // State manipulation
+    void setCap(GLenum cap, bool enabled);
+    bool getCap(GLenum cap);
+
     void setClearColor(float red, float green, float blue, float alpha);
 
     void setClearDepth(float depth);
 
     void setClearStencil(int stencil);
 
+    void setRasterizerDiscard(bool enabled);
+    bool isRasterizerDiscardEnabled() const;
+
     void setCullFace(bool enabled);
     bool isCullFaceEnabled() const;
 
@@ -453,7 +459,7 @@
 
     bool applyRenderTarget(GLenum drawMode, bool ignoreViewport);
     void applyState(GLenum drawMode);
-    void applyShaders(ProgramBinary *programBinary);
+    void applyShaders(ProgramBinary *programBinary, bool rasterizerDiscard);
     void applyTextures(ProgramBinary *programBinary);
     void applyTextures(ProgramBinary *programBinary, SamplerType type);
     bool applyUniformBuffers();
diff --git a/src/libGLESv2/angletypes.h b/src/libGLESv2/angletypes.h
index 4f6df67..6ab68b8 100644
--- a/src/libGLESv2/angletypes.h
+++ b/src/libGLESv2/angletypes.h
@@ -100,6 +100,8 @@
 
     bool pointDrawMode;
     bool multiSample;
+
+    bool rasterizerDiscard;
 };
 
 struct BlendState
diff --git a/src/libGLESv2/libGLESv2.cpp b/src/libGLESv2/libGLESv2.cpp
index 15ffe45..0d19eb3 100644
--- a/src/libGLESv2/libGLESv2.cpp
+++ b/src/libGLESv2/libGLESv2.cpp
@@ -1632,30 +1632,12 @@
 
         if (context)
         {
-            switch (cap)
+            if (!ValidCap(context, cap))
             {
-              case GL_CULL_FACE:                context->setCullFace(false);              break;
-              case GL_POLYGON_OFFSET_FILL:      context->setPolygonOffsetFill(false);     break;
-              case GL_SAMPLE_ALPHA_TO_COVERAGE: context->setSampleAlphaToCoverage(false); break;
-              case GL_SAMPLE_COVERAGE:          context->setSampleCoverage(false);        break;
-              case GL_SCISSOR_TEST:             context->setScissorTest(false);           break;
-              case GL_STENCIL_TEST:             context->setStencilTest(false);           break;
-              case GL_DEPTH_TEST:               context->setDepthTest(false);             break;
-              case GL_BLEND:                    context->setBlend(false);                 break;
-              case GL_DITHER:                   context->setDither(false);                break;
-
-              case GL_PRIMITIVE_RESTART_FIXED_INDEX:
-              case GL_RASTERIZER_DISCARD:
-                if (context->getClientVersion() < 3)
-                {
-                    return gl::error(GL_INVALID_ENUM);
-                }
-                UNIMPLEMENTED();
-                break;
-
-              default:
                 return gl::error(GL_INVALID_ENUM);
             }
+
+            context->setCap(cap, false);
         }
     }
     catch(std::bad_alloc&)
@@ -1832,20 +1814,12 @@
 
         if (context)
         {
-            switch (cap)
+            if (!ValidCap(context, cap))
             {
-              case GL_CULL_FACE:                context->setCullFace(true);              break;
-              case GL_POLYGON_OFFSET_FILL:      context->setPolygonOffsetFill(true);     break;
-              case GL_SAMPLE_ALPHA_TO_COVERAGE: context->setSampleAlphaToCoverage(true); break;
-              case GL_SAMPLE_COVERAGE:          context->setSampleCoverage(true);        break;
-              case GL_SCISSOR_TEST:             context->setScissorTest(true);           break;
-              case GL_STENCIL_TEST:             context->setStencilTest(true);           break;
-              case GL_DEPTH_TEST:               context->setDepthTest(true);             break;
-              case GL_BLEND:                    context->setBlend(true);                 break;
-              case GL_DITHER:                   context->setDither(true);                break;
-              default:
                 return gl::error(GL_INVALID_ENUM);
             }
+
+            context->setCap(cap, true);
         }
     }
     catch(std::bad_alloc&)
@@ -4105,20 +4079,12 @@
 
         if (context)
         {
-            switch (cap)
+            if (!ValidCap(context, cap))
             {
-              case GL_CULL_FACE:                return context->isCullFaceEnabled();
-              case GL_POLYGON_OFFSET_FILL:      return context->isPolygonOffsetFillEnabled();
-              case GL_SAMPLE_ALPHA_TO_COVERAGE: return context->isSampleAlphaToCoverageEnabled();
-              case GL_SAMPLE_COVERAGE:          return context->isSampleCoverageEnabled();
-              case GL_SCISSOR_TEST:             return context->isScissorTestEnabled();
-              case GL_STENCIL_TEST:             return context->isStencilTestEnabled();
-              case GL_DEPTH_TEST:               return context->isDepthTestEnabled();
-              case GL_BLEND:                    return context->isBlendEnabled();
-              case GL_DITHER:                   return context->isDitherEnabled();
-              default:
                 return gl::error(GL_INVALID_ENUM, false);
             }
+
+            return context->getCap(cap);
         }
     }
     catch(std::bad_alloc&)
diff --git a/src/libGLESv2/renderer/Renderer.h b/src/libGLESv2/renderer/Renderer.h
index fca70e1..8f1ab44 100644
--- a/src/libGLESv2/renderer/Renderer.h
+++ b/src/libGLESv2/renderer/Renderer.h
@@ -136,7 +136,7 @@
                              bool ignoreViewport) = 0;
 
     virtual bool applyRenderTarget(gl::Framebuffer *frameBuffer) = 0;
-    virtual void applyShaders(gl::ProgramBinary *programBinary) = 0;
+    virtual void applyShaders(gl::ProgramBinary *programBinary, bool rasterizerDiscard) = 0;
     virtual void applyUniforms(const gl::ProgramBinary &programBinary) = 0;
     virtual bool applyPrimitiveType(GLenum primitiveType, GLsizei elementCount) = 0;
     virtual GLenum applyVertexBuffer(gl::ProgramBinary *programBinary, const gl::VertexAttribute vertexAttributes[], gl::VertexAttribCurrentValueData currentValues[],
diff --git a/src/libGLESv2/renderer/d3d11/Renderer11.cpp b/src/libGLESv2/renderer/d3d11/Renderer11.cpp
index ecc1887..0c80c03 100644
--- a/src/libGLESv2/renderer/d3d11/Renderer11.cpp
+++ b/src/libGLESv2/renderer/d3d11/Renderer11.cpp
@@ -1403,7 +1403,7 @@
     }
 }
 
-void Renderer11::applyShaders(gl::ProgramBinary *programBinary)
+void Renderer11::applyShaders(gl::ProgramBinary *programBinary, bool rasterizerDiscard)
 {
     ShaderExecutable *vertexExe = programBinary->getVertexExecutable();
     ShaderExecutable *pixelExe = programBinary->getPixelExecutable();
@@ -1419,6 +1419,12 @@
         geometryShader = NULL;
     }
 
+    // Skip pixel shader if we're doing rasterizer discard.
+    if (rasterizerDiscard)
+    {
+        pixelShader = NULL;
+    }
+
     bool dirtyUniforms = false;
 
     if (vertexShader != mAppliedVertexShader)
diff --git a/src/libGLESv2/renderer/d3d11/Renderer11.h b/src/libGLESv2/renderer/d3d11/Renderer11.h
index 4f626a7..69e04ab 100644
--- a/src/libGLESv2/renderer/d3d11/Renderer11.h
+++ b/src/libGLESv2/renderer/d3d11/Renderer11.h
@@ -75,7 +75,7 @@
 
     virtual bool applyPrimitiveType(GLenum mode, GLsizei count);
     virtual bool applyRenderTarget(gl::Framebuffer *frameBuffer);
-    virtual void applyShaders(gl::ProgramBinary *programBinary);
+    virtual void applyShaders(gl::ProgramBinary *programBinary, bool rasterizerDiscard);
     virtual void applyUniforms(const gl::ProgramBinary &programBinary);
     virtual GLenum applyVertexBuffer(gl::ProgramBinary *programBinary, const gl::VertexAttribute vertexAttributes[], gl::VertexAttribCurrentValueData currentValues[],
                                      GLint first, GLsizei count, GLsizei instances);
diff --git a/src/libGLESv2/renderer/d3d9/Renderer9.cpp b/src/libGLESv2/renderer/d3d9/Renderer9.cpp
index ffb537b..eef5a12 100644
--- a/src/libGLESv2/renderer/d3d9/Renderer9.cpp
+++ b/src/libGLESv2/renderer/d3d9/Renderer9.cpp
@@ -1722,8 +1722,10 @@
     }
 }
 
-void Renderer9::applyShaders(gl::ProgramBinary *programBinary)
+void Renderer9::applyShaders(gl::ProgramBinary *programBinary, bool rasterizerDiscard)
 {
+    ASSERT(!rasterizerDiscard);
+
     ShaderExecutable *vertexExe = programBinary->getVertexExecutable();
     ShaderExecutable *pixelExe = programBinary->getPixelExecutable();
 
diff --git a/src/libGLESv2/renderer/d3d9/Renderer9.h b/src/libGLESv2/renderer/d3d9/Renderer9.h
index baa69c2..e26928a 100644
--- a/src/libGLESv2/renderer/d3d9/Renderer9.h
+++ b/src/libGLESv2/renderer/d3d9/Renderer9.h
@@ -86,7 +86,7 @@
                              bool ignoreViewport);
 
     virtual bool applyRenderTarget(gl::Framebuffer *frameBuffer);
-    virtual void applyShaders(gl::ProgramBinary *programBinary);
+    virtual void applyShaders(gl::ProgramBinary *programBinary, bool rasterizerDiscard);
     virtual void applyUniforms(const gl::ProgramBinary &programBinary);
     virtual bool applyPrimitiveType(GLenum primitiveType, GLsizei elementCount);
     virtual GLenum applyVertexBuffer(gl::ProgramBinary *programBinary, const gl::VertexAttribute vertexAttributes[], gl::VertexAttribCurrentValueData currentValues[],
diff --git a/src/libGLESv2/validationES.cpp b/src/libGLESv2/validationES.cpp
index ef759a5..3bc63f6 100644
--- a/src/libGLESv2/validationES.cpp
+++ b/src/libGLESv2/validationES.cpp
@@ -21,6 +21,28 @@
 namespace gl
 {
 
+bool ValidCap(const Context *context, GLenum cap)
+{
+    switch (cap)
+    {
+      case GL_CULL_FACE:
+      case GL_POLYGON_OFFSET_FILL:
+      case GL_SAMPLE_ALPHA_TO_COVERAGE:
+      case GL_SAMPLE_COVERAGE:
+      case GL_SCISSOR_TEST:
+      case GL_STENCIL_TEST:
+      case GL_DEPTH_TEST:
+      case GL_BLEND:
+      case GL_DITHER:
+        return true;
+      case GL_PRIMITIVE_RESTART_FIXED_INDEX:
+      case GL_RASTERIZER_DISCARD:
+        return (context->getClientVersion() >= 3);
+      default:
+        return false;
+    }
+}
+
 bool ValidTextureTarget(const Context *context, GLenum target)
 {
     switch (target)
diff --git a/src/libGLESv2/validationES.h b/src/libGLESv2/validationES.h
index 2afd2dc..a22028f 100644
--- a/src/libGLESv2/validationES.h
+++ b/src/libGLESv2/validationES.h
@@ -14,6 +14,7 @@
 
 class Context;
 
+bool ValidCap(const Context *context, GLenum cap);
 bool ValidTextureTarget(const Context *context, GLenum target);
 bool ValidFramebufferTarget(GLenum target);
 bool ValidMipLevel(const Context *context, GLenum target, GLint level);