GLES1: gl(Enable|Disable)ClientState

+ Introduce the GL_OES_point_size_array extension for point size array
support.

BUG=angleproject:2306

Change-Id: Ib1a60b7dcd0497eb807f0d3c80bc95b4748d9a96
Reviewed-on: https://chromium-review.googlesource.com/1014282
Commit-Queue: Lingfeng Yang <lfy@google.com>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/libANGLE/Caps.cpp b/src/libANGLE/Caps.cpp
index 1bf0b35..6c2e5bf 100644
--- a/src/libANGLE/Caps.cpp
+++ b/src/libANGLE/Caps.cpp
@@ -226,7 +226,8 @@
       robustResourceInitialization(false),
       programCacheControl(false),
       textureRectangle(false),
-      geometryShader(false)
+      geometryShader(false),
+      pointSizeArray(false)
 {
 }
 
@@ -698,6 +699,8 @@
         map["GL_ANGLE_program_cache_control"] = esOnlyExtension(&Extensions::programCacheControl);
         map["GL_ANGLE_texture_rectangle"] = enableableExtension(&Extensions::textureRectangle);
         map["GL_EXT_geometry_shader"] = enableableExtension(&Extensions::geometryShader);
+        // GLES1 extensinos
+        map["GL_OES_point_size_array"] = enableableExtension(&Extensions::pointSizeArray);
         // clang-format on
 
         return map;
diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h
index 9e34092..c2b83d8 100644
--- a/src/libANGLE/Caps.h
+++ b/src/libANGLE/Caps.h
@@ -374,6 +374,9 @@
 
     // GL_EXT_geometry_shader
     bool geometryShader;
+
+    // GLES1 emulation: GLES1 extensions
+    bool pointSizeArray;
 };
 
 struct ExtensionInfo
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 4b4f347..fada66e 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -2978,6 +2978,10 @@
 {
     mCaps = mImplementation->getNativeCaps();
 
+    mExtensions = mImplementation->getNativeExtensions();
+
+    mLimitations = mImplementation->getNativeLimitations();
+
     // GLES1 emulation: Initialize caps (Table 6.20 / 6.22 in the ES 1.1 spec)
     if (getClientVersion() < Version(2, 0))
     {
@@ -2987,12 +2991,11 @@
         mCaps.maxModelviewMatrixStackDepth  = Caps::GlobalMatrixStackDepth;
         mCaps.maxProjectionMatrixStackDepth = Caps::GlobalMatrixStackDepth;
         mCaps.maxTextureMatrixStackDepth    = Caps::GlobalMatrixStackDepth;
+
+        // Default extensions for GLES1
+        mExtensions.pointSizeArray = true;
     }
 
-    mExtensions = mImplementation->getNativeExtensions();
-
-    mLimitations = mImplementation->getNativeLimitations();
-
     if (getClientVersion() < Version(3, 0))
     {
         // Disable ES3+ extensions
diff --git a/src/libANGLE/Context_gles_1_0.cpp b/src/libANGLE/Context_gles_1_0.cpp
index 3f1b823..6aa7f71 100644
--- a/src/libANGLE/Context_gles_1_0.cpp
+++ b/src/libANGLE/Context_gles_1_0.cpp
@@ -95,14 +95,14 @@
     UNIMPLEMENTED();
 }
 
-void Context::disableClientState(GLenum clientState)
+void Context::disableClientState(ClientVertexArrayType clientState)
 {
-    UNIMPLEMENTED();
+    mGLState.gles1().setClientStateEnabled(clientState, false);
 }
 
-void Context::enableClientState(GLenum clientState)
+void Context::enableClientState(ClientVertexArrayType clientState)
 {
-    UNIMPLEMENTED();
+    mGLState.gles1().setClientStateEnabled(clientState, true);
 }
 
 void Context::fogf(GLenum pname, GLfloat param)
diff --git a/src/libANGLE/Context_gles_1_0_autogen.h b/src/libANGLE/Context_gles_1_0_autogen.h
index 8dd4ab4..0584898 100644
--- a/src/libANGLE/Context_gles_1_0_autogen.h
+++ b/src/libANGLE/Context_gles_1_0_autogen.h
@@ -23,8 +23,8 @@
     void color4x(GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha);                 \
     void colorPointer(GLint size, GLenum type, GLsizei stride, const void *pointer);       \
     void depthRangex(GLfixed n, GLfixed f);                                                \
-    void disableClientState(GLenum array);                                                 \
-    void enableClientState(GLenum array);                                                  \
+    void disableClientState(ClientVertexArrayType arrayPacked);                            \
+    void enableClientState(ClientVertexArrayType arrayPacked);                             \
     void fogf(GLenum pname, GLfloat param);                                                \
     void fogfv(GLenum pname, const GLfloat *params);                                       \
     void fogx(GLenum pname, GLfixed param);                                                \
diff --git a/src/libANGLE/ErrorStrings.h b/src/libANGLE/ErrorStrings.h
index 9234b80..0b1f807 100644
--- a/src/libANGLE/ErrorStrings.h
+++ b/src/libANGLE/ErrorStrings.h
@@ -64,6 +64,7 @@
 ERRMSG(InvalidBufferTypes, "Invalid buffer target enum.");
 ERRMSG(InvalidBufferUsage, "Invalid buffer usage enum.");
 ERRMSG(InvalidClearMask, "Invalid mask bits.");
+ERRMSG(InvalidClientState, "Invalid client vertex array type.");
 ERRMSG(InvalidCombinedImageUnit,
        "Specified unit must be in [GL_TEXTURE0, GL_TEXTURE0 + GL_MAX_COMBINED_IMAGE_UNITS)");
 ERRMSG(InvalidConstantColor,
@@ -172,6 +173,7 @@
 ERRMSG(
     PixelUnpackBufferBoundForTransformFeedback,
     "It is undefined behavior to use a pixel unpack buffer that is bound for transform feedback.");
+ERRMSG(PointSizeArrayExtensionNotEnabled, "GL_OES_point_size_array not enabled.");
 ERRMSG(ProgramDoesNotExist, "Program doesn't exist.");
 ERRMSG(ProgramNotBound, "A program must be bound.");
 ERRMSG(ProgramNotLinked, "Program not linked.");
diff --git a/src/libANGLE/GLES1State.cpp b/src/libANGLE/GLES1State.cpp
index de9cf67..7a3c932 100644
--- a/src/libANGLE/GLES1State.cpp
+++ b/src/libANGLE/GLES1State.cpp
@@ -264,8 +264,53 @@
 
 void GLES1State::multMatrix(const angle::Mat4 &m)
 {
-    angle::Mat4 currentMatrix             = currentMatrixStack().back();
+    angle::Mat4 currentMatrix   = currentMatrixStack().back();
     currentMatrixStack().back() = currentMatrix.product(m);
 }
 
+void GLES1State::setClientStateEnabled(ClientVertexArrayType clientState, bool enable)
+{
+    switch (clientState)
+    {
+        case ClientVertexArrayType::Vertex:
+            mVertexArrayEnabled = enable;
+            break;
+        case ClientVertexArrayType::Normal:
+            mNormalArrayEnabled = enable;
+            break;
+        case ClientVertexArrayType::Color:
+            mColorArrayEnabled = enable;
+            break;
+        case ClientVertexArrayType::PointSize:
+            mPointSizeArrayEnabled = enable;
+            break;
+        case ClientVertexArrayType::TextureCoord:
+            mTexCoordArrayEnabled[mClientActiveTexture] = enable;
+            break;
+        default:
+            UNREACHABLE();
+            break;
+    }
+}
+
+bool GLES1State::isClientStateEnabled(ClientVertexArrayType clientState) const
+{
+    switch (clientState)
+    {
+        case ClientVertexArrayType::Vertex:
+            return mVertexArrayEnabled;
+        case ClientVertexArrayType::Normal:
+            return mNormalArrayEnabled;
+        case ClientVertexArrayType::Color:
+            return mColorArrayEnabled;
+        case ClientVertexArrayType::PointSize:
+            return mPointSizeArrayEnabled;
+        case ClientVertexArrayType::TextureCoord:
+            return mTexCoordArrayEnabled[mClientActiveTexture];
+        default:
+            UNREACHABLE();
+            return false;
+    }
+}
+
 }  // namespace gl
diff --git a/src/libANGLE/GLES1State.h b/src/libANGLE/GLES1State.h
index b9582b1..c6d7a44 100644
--- a/src/libANGLE/GLES1State.h
+++ b/src/libANGLE/GLES1State.h
@@ -150,6 +150,9 @@
     void loadMatrix(const angle::Mat4 &m);
     void multMatrix(const angle::Mat4 &m);
 
+    void setClientStateEnabled(ClientVertexArrayType clientState, bool enable);
+    bool isClientStateEnabled(ClientVertexArrayType clientState) const;
+
   private:
     friend class State;
 
diff --git a/src/libANGLE/PackedGLEnums_autogen.cpp b/src/libANGLE/PackedGLEnums_autogen.cpp
index 81f2c8d..1731f6e 100644
--- a/src/libANGLE/PackedGLEnums_autogen.cpp
+++ b/src/libANGLE/PackedGLEnums_autogen.cpp
@@ -192,6 +192,46 @@
 }
 
 template <>
+ClientVertexArrayType FromGLenum<ClientVertexArrayType>(GLenum from)
+{
+    switch (from)
+    {
+        case GL_COLOR_ARRAY:
+            return ClientVertexArrayType::Color;
+        case GL_NORMAL_ARRAY:
+            return ClientVertexArrayType::Normal;
+        case GL_POINT_SIZE_ARRAY_OES:
+            return ClientVertexArrayType::PointSize;
+        case GL_TEXTURE_COORD_ARRAY:
+            return ClientVertexArrayType::TextureCoord;
+        case GL_VERTEX_ARRAY:
+            return ClientVertexArrayType::Vertex;
+        default:
+            return ClientVertexArrayType::InvalidEnum;
+    }
+}
+
+GLenum ToGLenum(ClientVertexArrayType from)
+{
+    switch (from)
+    {
+        case ClientVertexArrayType::Color:
+            return GL_COLOR_ARRAY;
+        case ClientVertexArrayType::Normal:
+            return GL_NORMAL_ARRAY;
+        case ClientVertexArrayType::PointSize:
+            return GL_POINT_SIZE_ARRAY_OES;
+        case ClientVertexArrayType::TextureCoord:
+            return GL_TEXTURE_COORD_ARRAY;
+        case ClientVertexArrayType::Vertex:
+            return GL_VERTEX_ARRAY;
+        default:
+            UNREACHABLE();
+            return GL_NONE;
+    }
+}
+
+template <>
 CullFaceMode FromGLenum<CullFaceMode>(GLenum from)
 {
     switch (from)
diff --git a/src/libANGLE/PackedGLEnums_autogen.h b/src/libANGLE/PackedGLEnums_autogen.h
index 79d263a..3f68081 100644
--- a/src/libANGLE/PackedGLEnums_autogen.h
+++ b/src/libANGLE/PackedGLEnums_autogen.h
@@ -84,6 +84,22 @@
 BufferUsage FromGLenum<BufferUsage>(GLenum from);
 GLenum ToGLenum(BufferUsage from);
 
+enum class ClientVertexArrayType : uint8_t
+{
+    Color        = 0,
+    Normal       = 1,
+    PointSize    = 2,
+    TextureCoord = 3,
+    Vertex       = 4,
+
+    InvalidEnum = 5,
+    EnumCount   = 5,
+};
+
+template <>
+ClientVertexArrayType FromGLenum<ClientVertexArrayType>(GLenum from);
+GLenum ToGLenum(ClientVertexArrayType from);
+
 enum class CullFaceMode : uint8_t
 {
     Back         = 0,
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index a81fe6f..eea13f9 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -827,6 +827,16 @@
         // GLES1 emulation
         case GL_ALPHA_TEST:
             return mGLES1State.mAlphaTestEnabled;
+        case GL_VERTEX_ARRAY:
+            return mGLES1State.mVertexArrayEnabled;
+        case GL_NORMAL_ARRAY:
+            return mGLES1State.mNormalArrayEnabled;
+        case GL_COLOR_ARRAY:
+            return mGLES1State.mColorArrayEnabled;
+        case GL_POINT_SIZE_ARRAY_OES:
+            return mGLES1State.mPointSizeArrayEnabled;
+        case GL_TEXTURE_COORD_ARRAY:
+            return mGLES1State.mTexCoordArrayEnabled[mGLES1State.mClientActiveTexture];
 
         default:
             UNREACHABLE();
diff --git a/src/libANGLE/packed_gl_enums.json b/src/libANGLE/packed_gl_enums.json
index 936e5f3..53b840e 100644
--- a/src/libANGLE/packed_gl_enums.json
+++ b/src/libANGLE/packed_gl_enums.json
@@ -37,6 +37,14 @@
         "StreamDraw": "GL_STREAM_DRAW",
         "StreamRead": "GL_STREAM_READ"
     },
+    "ClientVertexArrayType":
+    {
+        "Vertex": "GL_VERTEX_ARRAY",
+        "Normal": "GL_NORMAL_ARRAY",
+        "Color": "GL_COLOR_ARRAY",
+        "PointSize": "GL_POINT_SIZE_ARRAY_OES",
+        "TextureCoord": "GL_TEXTURE_COORD_ARRAY"
+    },
     "CullFaceMode":
     {
         "Back": "GL_BACK",
diff --git a/src/libANGLE/validationES1.cpp b/src/libANGLE/validationES1.cpp
index dfa817b..6a7730f 100644
--- a/src/libANGLE/validationES1.cpp
+++ b/src/libANGLE/validationES1.cpp
@@ -20,7 +20,7 @@
         return false;                                                 \
     }
 
-namespace
+namespace gl
 {
 
 bool ValidateAlphaFuncCommon(gl::Context *context, gl::AlphaTestFunc func)
@@ -37,12 +37,35 @@
         case gl::AlphaTestFunc::NotEqual:
             return true;
         default:
-            context->handleError(gl::InvalidEnum() << gl::kErrorEnumNotSupported);
+            ANGLE_VALIDATION_ERR(context, InvalidEnum(), EnumNotSupported);
             return false;
     }
 }
 
-}  // anonymous namespace
+bool ValidateClientStateCommon(gl::Context *context, gl::ClientVertexArrayType arrayType)
+{
+    ANGLE_VALIDATE_IS_GLES1(context);
+    switch (arrayType)
+    {
+        case gl::ClientVertexArrayType::Vertex:
+        case gl::ClientVertexArrayType::Normal:
+        case gl::ClientVertexArrayType::Color:
+        case gl::ClientVertexArrayType::TextureCoord:
+            return true;
+        case gl::ClientVertexArrayType::PointSize:
+            if (!context->getExtensions().pointSizeArray)
+            {
+                ANGLE_VALIDATION_ERR(context, InvalidEnum(), PointSizeArrayExtensionNotEnabled);
+                return false;
+            }
+            return true;
+        default:
+            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidClientState);
+            return false;
+    }
+}
+
+}  // namespace gl
 
 namespace gl
 {
@@ -129,16 +152,14 @@
     return true;
 }
 
-bool ValidateDisableClientState(Context *context, GLenum array)
+bool ValidateDisableClientState(Context *context, ClientVertexArrayType arrayType)
 {
-    UNIMPLEMENTED();
-    return true;
+    return ValidateClientStateCommon(context, arrayType);
 }
 
-bool ValidateEnableClientState(Context *context, GLenum array)
+bool ValidateEnableClientState(Context *context, ClientVertexArrayType arrayType)
 {
-    UNIMPLEMENTED();
-    return true;
+    return ValidateClientStateCommon(context, arrayType);
 }
 
 bool ValidateFogf(Context *context, GLenum pname, GLfloat param)
@@ -933,4 +954,5 @@
     UNIMPLEMENTED();
     return true;
 }
-}
+
+}  // namespace gl
diff --git a/src/libANGLE/validationES1.h b/src/libANGLE/validationES1.h
index be871e7..a549d8b 100644
--- a/src/libANGLE/validationES1.h
+++ b/src/libANGLE/validationES1.h
@@ -35,8 +35,8 @@
                           const void *pointer);
 bool ValidateCullFace(Context *context, GLenum mode);
 bool ValidateDepthRangex(Context *context, GLfixed n, GLfixed f);
-bool ValidateDisableClientState(Context *context, GLenum array);
-bool ValidateEnableClientState(Context *context, GLenum array);
+bool ValidateDisableClientState(Context *context, ClientVertexArrayType array);
+bool ValidateEnableClientState(Context *context, ClientVertexArrayType array);
 bool ValidateFogf(Context *context, GLenum pname, GLfloat param);
 bool ValidateFogfv(Context *context, GLenum pname, const GLfloat *params);
 bool ValidateFogx(Context *context, GLenum pname, GLfixed param);
diff --git a/src/libANGLE/validationES2.cpp b/src/libANGLE/validationES2.cpp
index 29f11f5..27d0d3b 100644
--- a/src/libANGLE/validationES2.cpp
+++ b/src/libANGLE/validationES2.cpp
@@ -794,7 +794,14 @@
 
         // GLES1 emulation: GLES1-specific caps
         case GL_ALPHA_TEST:
+        case GL_VERTEX_ARRAY:
+        case GL_NORMAL_ARRAY:
+        case GL_COLOR_ARRAY:
+        case GL_TEXTURE_COORD_ARRAY:
             return context->getClientVersion() < Version(2, 0);
+        case GL_POINT_SIZE_ARRAY_OES:
+            return context->getClientVersion() < Version(2, 0) &&
+                   context->getExtensions().pointSizeArray;
 
         default:
             return false;
diff --git a/src/libGLESv2/entry_points_gles_1_0_autogen.cpp b/src/libGLESv2/entry_points_gles_1_0_autogen.cpp
index beada99..e606904 100644
--- a/src/libGLESv2/entry_points_gles_1_0_autogen.cpp
+++ b/src/libGLESv2/entry_points_gles_1_0_autogen.cpp
@@ -223,11 +223,12 @@
     Context *context = GetValidGlobalContext();
     if (context)
     {
-        context->gatherParams<EntryPoint::DisableClientState>(array);
+        ClientVertexArrayType arrayPacked = FromGLenum<ClientVertexArrayType>(array);
+        context->gatherParams<EntryPoint::DisableClientState>(arrayPacked);
 
-        if (context->skipValidation() || ValidateDisableClientState(context, array))
+        if (context->skipValidation() || ValidateDisableClientState(context, arrayPacked))
         {
-            context->disableClientState(array);
+            context->disableClientState(arrayPacked);
         }
     }
 }
@@ -239,11 +240,12 @@
     Context *context = GetValidGlobalContext();
     if (context)
     {
-        context->gatherParams<EntryPoint::EnableClientState>(array);
+        ClientVertexArrayType arrayPacked = FromGLenum<ClientVertexArrayType>(array);
+        context->gatherParams<EntryPoint::EnableClientState>(arrayPacked);
 
-        if (context->skipValidation() || ValidateEnableClientState(context, array))
+        if (context->skipValidation() || ValidateEnableClientState(context, arrayPacked))
         {
-            context->enableClientState(array);
+            context->enableClientState(arrayPacked);
         }
     }
 }
diff --git a/src/tests/angle_end2end_tests.gypi b/src/tests/angle_end2end_tests.gypi
index a02248e..4e63d56 100644
--- a/src/tests/angle_end2end_tests.gypi
+++ b/src/tests/angle_end2end_tests.gypi
@@ -49,6 +49,7 @@
             '<(angle_path)/src/tests/gl_tests/GeometryShaderTest.cpp',
             '<(angle_path)/src/tests/gl_tests/gles1/AlphaFuncTest.cpp',
             '<(angle_path)/src/tests/gl_tests/gles1/ClientActiveTextureTest.cpp',
+            '<(angle_path)/src/tests/gl_tests/gles1/ClientStateEnable.cpp',
             '<(angle_path)/src/tests/gl_tests/gles1/CurrentColorTest.cpp',
             '<(angle_path)/src/tests/gl_tests/gles1/CurrentNormalTest.cpp',
             '<(angle_path)/src/tests/gl_tests/gles1/CurrentTextureCoordsTest.cpp',
diff --git a/src/tests/gl_tests/gles1/ClientStateEnable.cpp b/src/tests/gl_tests/gles1/ClientStateEnable.cpp
new file mode 100644
index 0000000..6ef1203
--- /dev/null
+++ b/src/tests/gl_tests/gles1/ClientStateEnable.cpp
@@ -0,0 +1,92 @@
+//
+// Copyright 2018 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.
+//
+
+// ClientStateEnable.cpp: Tests basic usage of gl(Enable|Disable)ClientState.
+
+#include "test_utils/ANGLETest.h"
+#include "test_utils/gl_raii.h"
+
+#include <vector>
+
+using namespace angle;
+
+class ClientStateEnable : public ANGLETest
+{
+  protected:
+    ClientStateEnable()
+    {
+        setWindowWidth(32);
+        setWindowHeight(32);
+        setConfigRedBits(8);
+        setConfigGreenBits(8);
+        setConfigBlueBits(8);
+        setConfigAlphaBits(8);
+        setConfigDepthBits(24);
+    }
+
+    std::vector<GLenum> mClientStates = {
+        GL_VERTEX_ARRAY,         GL_NORMAL_ARRAY,        GL_COLOR_ARRAY,
+        GL_POINT_SIZE_ARRAY_OES, GL_TEXTURE_COORD_ARRAY,
+    };
+};
+
+// Checks that all client vertex array states are disabled to start with.
+TEST_P(ClientStateEnable, InitialState)
+{
+    for (auto clientState : mClientStates)
+    {
+        EXPECT_GL_FALSE(glIsEnabled(clientState));
+        EXPECT_GL_NO_ERROR();
+    }
+}
+
+// Checks that glEnableClientState sets the state to be enabled,
+// and glDisableClientState sets the state to be disabled.
+TEST_P(ClientStateEnable, EnableState)
+{
+    for (auto clientState : mClientStates)
+    {
+        EXPECT_GL_FALSE(glIsEnabled(clientState));
+        glEnableClientState(clientState);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GL_TRUE(glIsEnabled(clientState));
+        glDisableClientState(clientState);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GL_FALSE(glIsEnabled(clientState));
+    }
+}
+
+// Negative test: Checks that invalid enums for client state generate the proper GL error.
+TEST_P(ClientStateEnable, Negative)
+{
+    glEnableClientState(0);
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+}
+
+// Checks that enable/disable states are different if we are in different client texture unit
+// states.
+TEST_P(ClientStateEnable, TextureUnit)
+{
+    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+    // Spec minimum lets us assume 2 multitexturing units.
+    glClientActiveTexture(GL_TEXTURE1);
+    EXPECT_GL_FALSE(glIsEnabled(GL_TEXTURE_COORD_ARRAY));
+
+    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+    EXPECT_GL_TRUE(glIsEnabled(GL_TEXTURE_COORD_ARRAY));
+
+    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+    EXPECT_GL_FALSE(glIsEnabled(GL_TEXTURE_COORD_ARRAY));
+
+    glClientActiveTexture(GL_TEXTURE0);
+    EXPECT_GL_TRUE(glIsEnabled(GL_TEXTURE_COORD_ARRAY));
+
+    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+    EXPECT_GL_FALSE(glIsEnabled(GL_TEXTURE_COORD_ARRAY));
+}
+
+ANGLE_INSTANTIATE_TEST(ClientStateEnable, ES1_D3D11(), ES1_OPENGL(), ES1_OPENGLES());