GLES1: Texture environment API

BUG=angleproject:2306

Change-Id: Ibb168d5c9f7aa96a48c96ffbe96ecead2276975e
Reviewed-on: https://chromium-review.googlesource.com/1092101
Commit-Queue: Lingfeng Yang <lfy@google.com>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/common/PackedGLEnums_autogen.cpp b/src/common/PackedGLEnums_autogen.cpp
index b578d46..2131cd0 100644
--- a/src/common/PackedGLEnums_autogen.cpp
+++ b/src/common/PackedGLEnums_autogen.cpp
@@ -840,6 +840,30 @@
             return TextureEnvParameter::RgbScale;
         case GL_ALPHA_SCALE:
             return TextureEnvParameter::AlphaScale;
+        case GL_SRC0_RGB:
+            return TextureEnvParameter::Src0Rgb;
+        case GL_SRC1_RGB:
+            return TextureEnvParameter::Src1Rgb;
+        case GL_SRC2_RGB:
+            return TextureEnvParameter::Src2Rgb;
+        case GL_SRC0_ALPHA:
+            return TextureEnvParameter::Src0Alpha;
+        case GL_SRC1_ALPHA:
+            return TextureEnvParameter::Src1Alpha;
+        case GL_SRC2_ALPHA:
+            return TextureEnvParameter::Src2Alpha;
+        case GL_OPERAND0_RGB:
+            return TextureEnvParameter::Op0Rgb;
+        case GL_OPERAND1_RGB:
+            return TextureEnvParameter::Op1Rgb;
+        case GL_OPERAND2_RGB:
+            return TextureEnvParameter::Op2Rgb;
+        case GL_OPERAND0_ALPHA:
+            return TextureEnvParameter::Op0Alpha;
+        case GL_OPERAND1_ALPHA:
+            return TextureEnvParameter::Op1Alpha;
+        case GL_OPERAND2_ALPHA:
+            return TextureEnvParameter::Op2Alpha;
         default:
             return TextureEnvParameter::InvalidEnum;
     }
@@ -861,6 +885,30 @@
             return GL_RGB_SCALE;
         case TextureEnvParameter::AlphaScale:
             return GL_ALPHA_SCALE;
+        case TextureEnvParameter::Src0Rgb:
+            return GL_SRC0_RGB;
+        case TextureEnvParameter::Src1Rgb:
+            return GL_SRC1_RGB;
+        case TextureEnvParameter::Src2Rgb:
+            return GL_SRC2_RGB;
+        case TextureEnvParameter::Src0Alpha:
+            return GL_SRC0_ALPHA;
+        case TextureEnvParameter::Src1Alpha:
+            return GL_SRC1_ALPHA;
+        case TextureEnvParameter::Src2Alpha:
+            return GL_SRC2_ALPHA;
+        case TextureEnvParameter::Op0Rgb:
+            return GL_OPERAND0_RGB;
+        case TextureEnvParameter::Op1Rgb:
+            return GL_OPERAND1_RGB;
+        case TextureEnvParameter::Op2Rgb:
+            return GL_OPERAND2_RGB;
+        case TextureEnvParameter::Op0Alpha:
+            return GL_OPERAND0_ALPHA;
+        case TextureEnvParameter::Op1Alpha:
+            return GL_OPERAND1_ALPHA;
+        case TextureEnvParameter::Op2Alpha:
+            return GL_OPERAND2_ALPHA;
         default:
             UNREACHABLE();
             return 0;
diff --git a/src/common/PackedGLEnums_autogen.h b/src/common/PackedGLEnums_autogen.h
index 98642dd..1b57f6d 100644
--- a/src/common/PackedGLEnums_autogen.h
+++ b/src/common/PackedGLEnums_autogen.h
@@ -336,9 +336,21 @@
     CombineAlpha = 3,
     RgbScale     = 4,
     AlphaScale   = 5,
+    Src0Rgb      = 6,
+    Src1Rgb      = 7,
+    Src2Rgb      = 8,
+    Src0Alpha    = 9,
+    Src1Alpha    = 10,
+    Src2Alpha    = 11,
+    Op0Rgb       = 12,
+    Op1Rgb       = 13,
+    Op2Rgb       = 14,
+    Op0Alpha     = 15,
+    Op1Alpha     = 16,
+    Op2Alpha     = 17,
 
-    InvalidEnum = 6,
-    EnumCount   = 6,
+    InvalidEnum = 18,
+    EnumCount   = 18,
 };
 
 template <>
diff --git a/src/common/packed_gl_enums.json b/src/common/packed_gl_enums.json
index 04fe239..c76a0f6 100644
--- a/src/common/packed_gl_enums.json
+++ b/src/common/packed_gl_enums.json
@@ -141,7 +141,19 @@
         "CombineRgb": "GL_COMBINE_RGB",
         "CombineAlpha": "GL_COMBINE_ALPHA",
         "RgbScale": "GL_RGB_SCALE",
-        "AlphaScale": "GL_ALPHA_SCALE"
+        "AlphaScale": "GL_ALPHA_SCALE",
+        "Src0Rgb": "GL_SRC0_RGB",
+        "Src1Rgb": "GL_SRC1_RGB",
+        "Src2Rgb": "GL_SRC2_RGB",
+        "Src0Alpha": "GL_SRC0_ALPHA",
+        "Src1Alpha": "GL_SRC1_ALPHA",
+        "Src2Alpha": "GL_SRC2_ALPHA",
+        "Op0Rgb": "GL_OPERAND0_RGB",
+        "Op1Rgb": "GL_OPERAND1_RGB",
+        "Op2Rgb": "GL_OPERAND2_RGB",
+        "Op0Alpha": "GL_OPERAND0_ALPHA",
+        "Op1Alpha": "GL_OPERAND1_ALPHA",
+        "Op2Alpha": "GL_OPERAND2_ALPHA"
     },
     "TextureOp":
     {
diff --git a/src/libANGLE/Context_gles_1_0.cpp b/src/libANGLE/Context_gles_1_0.cpp
index 6cce051..543b068 100644
--- a/src/libANGLE/Context_gles_1_0.cpp
+++ b/src/libANGLE/Context_gles_1_0.cpp
@@ -12,6 +12,7 @@
 #include "common/utilities.h"
 
 #include "libANGLE/GLES1Renderer.h"
+#include "libANGLE/queryconversions.h"
 #include "libANGLE/queryutils.h"
 
 namespace
@@ -130,7 +131,7 @@
 {
     if (GetFogParameterCount(pname) == 1)
     {
-        GLfloat paramf = pname == GL_FOG_MODE ? FixedToEnum(param) : FixedToFloat(param);
+        GLfloat paramf = pname == GL_FOG_MODE ? ConvertToGLenum(param) : FixedToFloat(param);
         fogf(pname, paramf);
     }
     else
@@ -148,7 +149,8 @@
         GLfloat paramsf[4];
         for (int i = 0; i < paramCount; i++)
         {
-            paramsf[i] = pname == GL_FOG_MODE ? FixedToEnum(params[i]) : FixedToFloat(params[i]);
+            paramsf[i] =
+                pname == GL_FOG_MODE ? ConvertToGLenum(params[i]) : FixedToFloat(params[i]);
         }
         fogfv(pname, paramsf);
     }
@@ -224,19 +226,23 @@
     }
 }
 
-void Context::getTexEnvfv(TextureEnvTarget env, TextureEnvParameter pname, GLfloat *params)
+void Context::getTexEnvfv(TextureEnvTarget target, TextureEnvParameter pname, GLfloat *params)
 {
-    UNIMPLEMENTED();
+    GetTextureEnv(mGLState.getActiveSampler(), &mGLState.gles1(), target, pname, params);
 }
 
-void Context::getTexEnviv(TextureEnvTarget env, TextureEnvParameter pname, GLint *params)
+void Context::getTexEnviv(TextureEnvTarget target, TextureEnvParameter pname, GLint *params)
 {
-    UNIMPLEMENTED();
+    GLfloat paramsf[4];
+    GetTextureEnv(mGLState.getActiveSampler(), &mGLState.gles1(), target, pname, paramsf);
+    ConvertTextureEnvToInt(pname, paramsf, params);
 }
 
 void Context::getTexEnvxv(TextureEnvTarget target, TextureEnvParameter pname, GLfixed *params)
 {
-    UNIMPLEMENTED();
+    GLfloat paramsf[4];
+    GetTextureEnv(mGLState.getActiveSampler(), &mGLState.gles1(), target, pname, paramsf);
+    ConvertTextureEnvToFixed(pname, paramsf, params);
 }
 
 void Context::getTexParameterxv(TextureType target, GLenum pname, GLfixed *params)
@@ -498,32 +504,40 @@
 
 void Context::texEnvf(TextureEnvTarget target, TextureEnvParameter pname, GLfloat param)
 {
-    UNIMPLEMENTED();
+    SetTextureEnv(mGLState.getActiveSampler(), &mGLState.gles1(), target, pname, &param);
 }
 
 void Context::texEnvfv(TextureEnvTarget target, TextureEnvParameter pname, const GLfloat *params)
 {
-    UNIMPLEMENTED();
+    SetTextureEnv(mGLState.getActiveSampler(), &mGLState.gles1(), target, pname, params);
 }
 
 void Context::texEnvi(TextureEnvTarget target, TextureEnvParameter pname, GLint param)
 {
-    UNIMPLEMENTED();
+    GLfloat paramsf[4] = {};
+    ConvertTextureEnvFromInt(pname, &param, paramsf);
+    SetTextureEnv(mGLState.getActiveSampler(), &mGLState.gles1(), target, pname, paramsf);
 }
 
 void Context::texEnviv(TextureEnvTarget target, TextureEnvParameter pname, const GLint *params)
 {
-    UNIMPLEMENTED();
+    GLfloat paramsf[4] = {};
+    ConvertTextureEnvFromInt(pname, params, paramsf);
+    SetTextureEnv(mGLState.getActiveSampler(), &mGLState.gles1(), target, pname, paramsf);
 }
 
 void Context::texEnvx(TextureEnvTarget target, TextureEnvParameter pname, GLfixed param)
 {
-    UNIMPLEMENTED();
+    GLfloat paramsf[4] = {};
+    ConvertTextureEnvFromFixed(pname, &param, paramsf);
+    SetTextureEnv(mGLState.getActiveSampler(), &mGLState.gles1(), target, pname, paramsf);
 }
 
 void Context::texEnvxv(TextureEnvTarget target, TextureEnvParameter pname, const GLfixed *params)
 {
-    UNIMPLEMENTED();
+    GLfloat paramsf[4] = {};
+    ConvertTextureEnvFromFixed(pname, params, paramsf);
+    SetTextureEnv(mGLState.getActiveSampler(), &mGLState.gles1(), target, pname, paramsf);
 }
 
 void Context::texParameterx(TextureType target, GLenum pname, GLfixed param)
diff --git a/src/libANGLE/ErrorStrings.h b/src/libANGLE/ErrorStrings.h
index 0114d74..ce305cf 100644
--- a/src/libANGLE/ErrorStrings.h
+++ b/src/libANGLE/ErrorStrings.h
@@ -135,6 +135,13 @@
 ERRMSG(InvalidStencil, "Invalid stencil.");
 ERRMSG(InvalidStencilBitMask, "Invalid stencil bit mask.");
 ERRMSG(InvalidTarget, "Invalid target.");
+ERRMSG(InvalidTextureCombine, "Invalid texture combine mode.");
+ERRMSG(InvalidTextureCombineSrc, "Invalid texture combine source.");
+ERRMSG(InvalidTextureCombineOp, "Invalid texture combine operand.");
+ERRMSG(InvalidTextureEnvMode, "Invalid texture environment mode.");
+ERRMSG(InvalidTextureEnvParameter, "Invalid texture environment parameter.");
+ERRMSG(InvalidTextureEnvScale, "Invalid texture environment scale.");
+ERRMSG(InvalidTextureEnvTarget, "Invalid texture environment target.");
 ERRMSG(InvalidTextureFilterParam, "Texture filter not recognized.");
 ERRMSG(InvalidTextureName, "Not a valid texture object name.");
 ERRMSG(InvalidTextureRange, "Cannot be less than 0 or greater than maximum number of textures.");
diff --git a/src/libANGLE/GLES1State.cpp b/src/libANGLE/GLES1State.cpp
index 74ed1bc..181bd93 100644
--- a/src/libANGLE/GLES1State.cpp
+++ b/src/libANGLE/GLES1State.cpp
@@ -394,4 +394,16 @@
     return mFog;
 }
 
+TextureEnvironmentParameters &GLES1State::textureEnvironment(unsigned int unit)
+{
+    assert(unit < mTextureEnvironments.size());
+    return mTextureEnvironments[unit];
+}
+
+const TextureEnvironmentParameters &GLES1State::textureEnvironment(unsigned int unit) const
+{
+    assert(unit < mTextureEnvironments.size());
+    return mTextureEnvironments[unit];
+}
+
 }  // namespace gl
diff --git a/src/libANGLE/GLES1State.h b/src/libANGLE/GLES1State.h
index a5b1f10..1b15d8b 100644
--- a/src/libANGLE/GLES1State.h
+++ b/src/libANGLE/GLES1State.h
@@ -76,29 +76,29 @@
 
 struct TextureEnvironmentParameters
 {
-    TextureEnvMode envMode      = TextureEnvMode::Modulate;
+    TextureEnvMode mode         = TextureEnvMode::Modulate;
     TextureCombine combineRgb   = TextureCombine::Modulate;
     TextureCombine combineAlpha = TextureCombine::Modulate;
 
-    TextureSrc src0rgb   = TextureSrc::Texture;
-    TextureSrc src0alpha = TextureSrc::Texture;
+    TextureSrc src0Rgb   = TextureSrc::Texture;
+    TextureSrc src0Alpha = TextureSrc::Texture;
 
-    TextureSrc src1rgb   = TextureSrc::Previous;
-    TextureSrc src1alpha = TextureSrc::Previous;
+    TextureSrc src1Rgb   = TextureSrc::Previous;
+    TextureSrc src1Alpha = TextureSrc::Previous;
 
-    TextureSrc src2rgb   = TextureSrc::Constant;
-    TextureSrc src2alpha = TextureSrc::Constant;
+    TextureSrc src2Rgb   = TextureSrc::Constant;
+    TextureSrc src2Alpha = TextureSrc::Constant;
 
-    TextureOp op0rgb   = TextureOp::SrcColor;
-    TextureOp op0alpha = TextureOp::SrcAlpha;
+    TextureOp op0Rgb   = TextureOp::SrcColor;
+    TextureOp op0Alpha = TextureOp::SrcAlpha;
 
-    TextureOp op1rgb   = TextureOp::SrcColor;
-    TextureOp op1alpha = TextureOp::SrcAlpha;
+    TextureOp op1Rgb   = TextureOp::SrcColor;
+    TextureOp op1Alpha = TextureOp::SrcAlpha;
 
-    TextureOp op2rgb   = TextureOp::SrcAlpha;
-    TextureOp op2alpha = TextureOp::SrcAlpha;
+    TextureOp op2Rgb   = TextureOp::SrcAlpha;
+    TextureOp op2Alpha = TextureOp::SrcAlpha;
 
-    ColorF envColor    = {0.0f, 0.0f, 0.0f, 0.0f};
+    ColorF color       = {0.0f, 0.0f, 0.0f, 0.0f};
     GLfloat rgbScale   = 1.0f;
     GLfloat alphaScale = 1.0f;
 
@@ -183,6 +183,9 @@
     FogParameters &fogParameters();
     const FogParameters &fogParameters() const;
 
+    TextureEnvironmentParameters &textureEnvironment(unsigned int unit);
+    const TextureEnvironmentParameters &textureEnvironment(unsigned int unit) const;
+
   private:
     friend class State;
     friend class GLES1Renderer;
diff --git a/src/libANGLE/queryconversions.h b/src/libANGLE/queryconversions.h
index d295091..267e0e7 100644
--- a/src/libANGLE/queryconversions.h
+++ b/src/libANGLE/queryconversions.h
@@ -81,6 +81,24 @@
     return ConvertToGLenum(GL_NONE, param);
 }
 
+template <typename OutType>
+OutType ConvertGLenum(GLenum param)
+{
+    return static_cast<OutType>(param);
+}
+
+template <typename InType, typename OutType>
+void ConvertGLenumValue(InType param, OutType *out)
+{
+    *out = ConvertGLenum<OutType>(static_cast<GLenum>(param));
+}
+
+template <typename PackedEnumType, typename OutType>
+void ConvertPackedEnum(PackedEnumType param, OutType *out)
+{
+    *out = static_cast<OutType>(ToGLenum(param));
+}
+
 template <typename ParamType>
 GLint ConvertToGLint(ParamType param)
 {
@@ -101,8 +119,11 @@
 
 // The GL state query API types are: bool, int, uint, float, int64, uint64
 template <typename QueryT>
-void CastStateValues(Context *context, GLenum nativeType, GLenum pname,
-                     unsigned int numParams, QueryT *outParams);
+void CastStateValues(Context *context,
+                     GLenum nativeType,
+                     GLenum pname,
+                     unsigned int numParams,
+                     QueryT *outParams);
 
 // The GL state query API types are: bool, int, uint, float, int64, uint64
 template <typename QueryT>
diff --git a/src/libANGLE/queryutils.cpp b/src/libANGLE/queryutils.cpp
index f8c59fd..b489bb3 100644
--- a/src/libANGLE/queryutils.cpp
+++ b/src/libANGLE/queryutils.cpp
@@ -798,6 +798,33 @@
     GetShaderVariableBufferResourceProperty(buffer, pname, params, bufSize, outputPosition);
 }
 
+bool IsTextureEnvEnumParameter(TextureEnvParameter pname)
+{
+    switch (pname)
+    {
+        case TextureEnvParameter::Mode:
+        case TextureEnvParameter::CombineRgb:
+        case TextureEnvParameter::CombineAlpha:
+        case TextureEnvParameter::Src0Rgb:
+        case TextureEnvParameter::Src1Rgb:
+        case TextureEnvParameter::Src2Rgb:
+        case TextureEnvParameter::Src0Alpha:
+        case TextureEnvParameter::Src1Alpha:
+        case TextureEnvParameter::Src2Alpha:
+        case TextureEnvParameter::Op0Rgb:
+        case TextureEnvParameter::Op1Rgb:
+        case TextureEnvParameter::Op2Rgb:
+        case TextureEnvParameter::Op0Alpha:
+        case TextureEnvParameter::Op1Alpha:
+        case TextureEnvParameter::Op2Alpha:
+        case TextureEnvParameter::RgbScale:
+        case TextureEnvParameter::AlphaScale:
+            return true;
+        default:
+            return false;
+    }
+}
+
 }  // anonymous namespace
 
 void QueryFramebufferAttachmentParameteriv(const Context *context,
@@ -2086,9 +2113,293 @@
     }
 }
 
-GLenum FixedToEnum(GLfixed val)
+unsigned int GetTextureEnvParameterCount(TextureEnvParameter pname)
 {
-    return static_cast<GLenum>(val);
+    switch (pname)
+    {
+        case TextureEnvParameter::Mode:
+        case TextureEnvParameter::CombineRgb:
+        case TextureEnvParameter::CombineAlpha:
+        case TextureEnvParameter::Src0Rgb:
+        case TextureEnvParameter::Src1Rgb:
+        case TextureEnvParameter::Src2Rgb:
+        case TextureEnvParameter::Src0Alpha:
+        case TextureEnvParameter::Src1Alpha:
+        case TextureEnvParameter::Src2Alpha:
+        case TextureEnvParameter::Op0Rgb:
+        case TextureEnvParameter::Op1Rgb:
+        case TextureEnvParameter::Op2Rgb:
+        case TextureEnvParameter::Op0Alpha:
+        case TextureEnvParameter::Op1Alpha:
+        case TextureEnvParameter::Op2Alpha:
+        case TextureEnvParameter::RgbScale:
+        case TextureEnvParameter::AlphaScale:
+            return 1;
+        case TextureEnvParameter::Color:
+            return 4;
+        default:
+            return 0;
+    }
+}
+
+void ConvertTextureEnvFromInt(TextureEnvParameter pname, const GLint *input, GLfloat *output)
+{
+    if (IsTextureEnvEnumParameter(pname))
+    {
+        ConvertGLenumValue(input[0], output);
+        return;
+    }
+
+    switch (pname)
+    {
+        case TextureEnvParameter::RgbScale:
+        case TextureEnvParameter::AlphaScale:
+            output[0] = static_cast<GLfloat>(input[0]);
+            break;
+        case TextureEnvParameter::Color:
+            for (int i = 0; i < 4; i++)
+            {
+                output[i] = input[i] / 255.0f;
+            }
+            break;
+        default:
+            UNREACHABLE();
+            break;
+    }
+}
+
+void ConvertTextureEnvFromFixed(TextureEnvParameter pname, const GLfixed *input, GLfloat *output)
+{
+    if (IsTextureEnvEnumParameter(pname))
+    {
+        ConvertGLenumValue(input[0], output);
+        return;
+    }
+
+    switch (pname)
+    {
+        case TextureEnvParameter::RgbScale:
+        case TextureEnvParameter::AlphaScale:
+            output[0] = FixedToFloat(input[0]);
+            break;
+        case TextureEnvParameter::Color:
+            for (int i = 0; i < 4; i++)
+            {
+                output[i] = FixedToFloat(input[i]);
+            }
+            break;
+            break;
+        default:
+            UNREACHABLE();
+            break;
+    }
+}
+
+void ConvertTextureEnvToInt(TextureEnvParameter pname, const GLfloat *input, GLint *output)
+{
+    if (IsTextureEnvEnumParameter(pname))
+    {
+        ConvertGLenumValue(input[0], output);
+        return;
+    }
+
+    switch (pname)
+    {
+        case TextureEnvParameter::RgbScale:
+        case TextureEnvParameter::AlphaScale:
+            output[0] = static_cast<GLint>(input[0]);
+            break;
+        case TextureEnvParameter::Color:
+            for (int i = 0; i < 4; i++)
+            {
+                output[i] = static_cast<GLint>(input[i] * 255.0f);
+            }
+            break;
+        default:
+            UNREACHABLE();
+            break;
+    }
+}
+
+void ConvertTextureEnvToFixed(TextureEnvParameter pname, const GLfloat *input, GLfixed *output)
+{
+    if (IsTextureEnvEnumParameter(pname))
+    {
+        ConvertGLenumValue(input[0], output);
+        return;
+    }
+
+    switch (pname)
+    {
+        case TextureEnvParameter::RgbScale:
+        case TextureEnvParameter::AlphaScale:
+            output[0] = FloatToFixed(input[0]);
+            break;
+        case TextureEnvParameter::Color:
+            for (int i = 0; i < 4; i++)
+            {
+                output[i] = FloatToFixed(input[i]);
+            }
+            break;
+        default:
+            UNREACHABLE();
+            break;
+    }
+}
+
+void SetTextureEnv(unsigned int unit,
+                   GLES1State *state,
+                   TextureEnvTarget target,
+                   TextureEnvParameter pname,
+                   const GLfloat *params)
+{
+    if (target == TextureEnvTarget::Env)
+    {
+        TextureEnvironmentParameters &env = state->textureEnvironment(unit);
+        GLenum asEnum                     = ConvertToGLenum(params[0]);
+        switch (pname)
+        {
+            case TextureEnvParameter::Mode:
+                env.mode = FromGLenum<TextureEnvMode>(asEnum);
+                break;
+            case TextureEnvParameter::CombineRgb:
+                env.combineRgb = FromGLenum<TextureCombine>(asEnum);
+                break;
+            case TextureEnvParameter::CombineAlpha:
+                env.combineAlpha = FromGLenum<TextureCombine>(asEnum);
+                break;
+            case TextureEnvParameter::Src0Rgb:
+                env.src0Rgb = FromGLenum<TextureSrc>(asEnum);
+                break;
+            case TextureEnvParameter::Src1Rgb:
+                env.src1Rgb = FromGLenum<TextureSrc>(asEnum);
+                break;
+            case TextureEnvParameter::Src2Rgb:
+                env.src2Rgb = FromGLenum<TextureSrc>(asEnum);
+                break;
+            case TextureEnvParameter::Src0Alpha:
+                env.src0Alpha = FromGLenum<TextureSrc>(asEnum);
+                break;
+            case TextureEnvParameter::Src1Alpha:
+                env.src1Alpha = FromGLenum<TextureSrc>(asEnum);
+                break;
+            case TextureEnvParameter::Src2Alpha:
+                env.src2Alpha = FromGLenum<TextureSrc>(asEnum);
+                break;
+            case TextureEnvParameter::Op0Rgb:
+                env.op0Rgb = FromGLenum<TextureOp>(asEnum);
+                break;
+            case TextureEnvParameter::Op1Rgb:
+                env.op1Rgb = FromGLenum<TextureOp>(asEnum);
+                break;
+            case TextureEnvParameter::Op2Rgb:
+                env.op2Rgb = FromGLenum<TextureOp>(asEnum);
+                break;
+            case TextureEnvParameter::Op0Alpha:
+                env.op0Alpha = FromGLenum<TextureOp>(asEnum);
+                break;
+            case TextureEnvParameter::Op1Alpha:
+                env.op1Alpha = FromGLenum<TextureOp>(asEnum);
+                break;
+            case TextureEnvParameter::Op2Alpha:
+                env.op2Alpha = FromGLenum<TextureOp>(asEnum);
+                break;
+            case TextureEnvParameter::Color:
+                env.color = ColorF::fromData(params);
+                break;
+            case TextureEnvParameter::RgbScale:
+                env.rgbScale = params[0];
+                break;
+            case TextureEnvParameter::AlphaScale:
+                env.alphaScale = params[0];
+                break;
+            default:
+                UNREACHABLE();
+                break;
+        }
+    }
+    else
+    {
+        // TODO(lfy@google.com): point sprite target
+        UNREACHABLE();
+    }
+}
+
+void GetTextureEnv(unsigned int unit,
+                   const GLES1State *state,
+                   TextureEnvTarget target,
+                   TextureEnvParameter pname,
+                   GLfloat *params)
+{
+    if (target == TextureEnvTarget::Env)
+    {
+        const TextureEnvironmentParameters &env = state->textureEnvironment(unit);
+        switch (pname)
+        {
+            case TextureEnvParameter::Mode:
+                ConvertPackedEnum(env.mode, params);
+                break;
+            case TextureEnvParameter::CombineRgb:
+                ConvertPackedEnum(env.combineRgb, params);
+                break;
+            case TextureEnvParameter::CombineAlpha:
+                ConvertPackedEnum(env.combineAlpha, params);
+                break;
+            case TextureEnvParameter::Src0Rgb:
+                ConvertPackedEnum(env.src0Rgb, params);
+                break;
+            case TextureEnvParameter::Src1Rgb:
+                ConvertPackedEnum(env.src1Rgb, params);
+                break;
+            case TextureEnvParameter::Src2Rgb:
+                ConvertPackedEnum(env.src2Rgb, params);
+                break;
+            case TextureEnvParameter::Src0Alpha:
+                ConvertPackedEnum(env.src0Alpha, params);
+                break;
+            case TextureEnvParameter::Src1Alpha:
+                ConvertPackedEnum(env.src1Alpha, params);
+                break;
+            case TextureEnvParameter::Src2Alpha:
+                ConvertPackedEnum(env.src2Alpha, params);
+                break;
+            case TextureEnvParameter::Op0Rgb:
+                ConvertPackedEnum(env.op0Rgb, params);
+                break;
+            case TextureEnvParameter::Op1Rgb:
+                ConvertPackedEnum(env.op1Rgb, params);
+                break;
+            case TextureEnvParameter::Op2Rgb:
+                ConvertPackedEnum(env.op2Rgb, params);
+                break;
+            case TextureEnvParameter::Op0Alpha:
+                ConvertPackedEnum(env.op0Alpha, params);
+                break;
+            case TextureEnvParameter::Op1Alpha:
+                ConvertPackedEnum(env.op1Alpha, params);
+                break;
+            case TextureEnvParameter::Op2Alpha:
+                ConvertPackedEnum(env.op2Alpha, params);
+                break;
+            case TextureEnvParameter::Color:
+                env.color.writeData(params);
+                break;
+            case TextureEnvParameter::RgbScale:
+                *params = env.rgbScale;
+                break;
+            case TextureEnvParameter::AlphaScale:
+                *params = env.alphaScale;
+                break;
+            default:
+                UNREACHABLE();
+                break;
+        }
+    }
+    else
+    {
+        // TODO(lfy@google.com): point sprite target
+        UNREACHABLE();
+    }
 }
 
 }  // namespace gl
diff --git a/src/libANGLE/queryutils.h b/src/libANGLE/queryutils.h
index b7f01c9..148f007 100644
--- a/src/libANGLE/queryutils.h
+++ b/src/libANGLE/queryutils.h
@@ -179,7 +179,23 @@
 void GetFogParameters(const GLES1State *state, GLenum pname, GLfloat *params);
 unsigned int GetFogParameterCount(GLenum pname);
 
-GLenum FixedToEnum(GLfixed val);
+unsigned int GetTextureEnvParameterCount(TextureEnvParameter pname);
+
+void ConvertTextureEnvFromInt(TextureEnvParameter pname, const GLint *input, GLfloat *output);
+void ConvertTextureEnvFromFixed(TextureEnvParameter pname, const GLfixed *input, GLfloat *output);
+void ConvertTextureEnvToInt(TextureEnvParameter pname, const GLfloat *input, GLint *output);
+void ConvertTextureEnvToFixed(TextureEnvParameter pname, const GLfloat *input, GLfixed *output);
+
+void SetTextureEnv(unsigned int unit,
+                   GLES1State *state,
+                   TextureEnvTarget target,
+                   TextureEnvParameter pname,
+                   const GLfloat *params);
+void GetTextureEnv(unsigned int unit,
+                   const GLES1State *state,
+                   TextureEnvTarget target,
+                   TextureEnvParameter pname,
+                   GLfloat *params);
 
 }  // namespace gl
 
diff --git a/src/libANGLE/validationES1.cpp b/src/libANGLE/validationES1.cpp
index 64638eb..4ec2c53 100644
--- a/src/libANGLE/validationES1.cpp
+++ b/src/libANGLE/validationES1.cpp
@@ -12,6 +12,7 @@
 #include "libANGLE/Context.h"
 #include "libANGLE/ErrorStrings.h"
 #include "libANGLE/GLES1State.h"
+#include "libANGLE/queryconversions.h"
 #include "libANGLE/queryutils.h"
 #include "libANGLE/validationES.h"
 
@@ -368,6 +369,172 @@
     return true;
 }
 
+bool ValidateTexEnvCommon(Context *context,
+                          TextureEnvTarget target,
+                          TextureEnvParameter pname,
+                          const GLfloat *params)
+{
+    ANGLE_VALIDATE_IS_GLES1(context);
+
+    if (target != TextureEnvTarget::Env)
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidTextureEnvTarget);
+        return false;
+    }
+
+    switch (pname)
+    {
+        case TextureEnvParameter::Mode:
+        {
+            TextureEnvMode mode = FromGLenum<TextureEnvMode>(ConvertToGLenum(params[0]));
+            switch (mode)
+            {
+                case TextureEnvMode::Add:
+                case TextureEnvMode::Blend:
+                case TextureEnvMode::Combine:
+                case TextureEnvMode::Decal:
+                case TextureEnvMode::Modulate:
+                case TextureEnvMode::Replace:
+                    break;
+                default:
+                    ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidTextureEnvMode);
+                    return false;
+            }
+            break;
+        }
+        case TextureEnvParameter::CombineRgb:
+        case TextureEnvParameter::CombineAlpha:
+        {
+            TextureCombine combine = FromGLenum<TextureCombine>(ConvertToGLenum(params[0]));
+            switch (combine)
+            {
+                case TextureCombine::Add:
+                case TextureCombine::AddSigned:
+                case TextureCombine::Interpolate:
+                case TextureCombine::Modulate:
+                case TextureCombine::Replace:
+                case TextureCombine::Subtract:
+                    break;
+                case TextureCombine::Dot3Rgb:
+                case TextureCombine::Dot3Rgba:
+                    if (pname == TextureEnvParameter::CombineAlpha)
+                    {
+                        ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidTextureCombine);
+                        return false;
+                    }
+                    break;
+                default:
+                    ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidTextureCombine);
+                    return false;
+            }
+            break;
+        }
+        case TextureEnvParameter::Src0Rgb:
+        case TextureEnvParameter::Src1Rgb:
+        case TextureEnvParameter::Src2Rgb:
+        case TextureEnvParameter::Src0Alpha:
+        case TextureEnvParameter::Src1Alpha:
+        case TextureEnvParameter::Src2Alpha:
+        {
+            TextureSrc combine = FromGLenum<TextureSrc>(ConvertToGLenum(params[0]));
+            switch (combine)
+            {
+                case TextureSrc::Constant:
+                case TextureSrc::Previous:
+                case TextureSrc::PrimaryColor:
+                case TextureSrc::Texture:
+                    break;
+                default:
+                    ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidTextureCombineSrc);
+                    return false;
+            }
+            break;
+        }
+        case TextureEnvParameter::Op0Rgb:
+        case TextureEnvParameter::Op1Rgb:
+        case TextureEnvParameter::Op2Rgb:
+        case TextureEnvParameter::Op0Alpha:
+        case TextureEnvParameter::Op1Alpha:
+        case TextureEnvParameter::Op2Alpha:
+        {
+            TextureOp operand = FromGLenum<TextureOp>(ConvertToGLenum(params[0]));
+            switch (operand)
+            {
+                case TextureOp::SrcAlpha:
+                case TextureOp::OneMinusSrcAlpha:
+                    break;
+                case TextureOp::SrcColor:
+                case TextureOp::OneMinusSrcColor:
+                    if (pname == TextureEnvParameter::Op0Alpha ||
+                        pname == TextureEnvParameter::Op1Alpha ||
+                        pname == TextureEnvParameter::Op2Alpha)
+                    {
+                        ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidTextureCombine);
+                        return false;
+                    }
+                    break;
+                default:
+                    ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidTextureCombineOp);
+                    return false;
+            }
+            break;
+        }
+        case TextureEnvParameter::RgbScale:
+        case TextureEnvParameter::AlphaScale:
+            if (params[0] != 1.0f && params[0] != 2.0f && params[0] != 4.0f)
+            {
+                ANGLE_VALIDATION_ERR(context, InvalidValue(), InvalidTextureEnvScale);
+                return false;
+            }
+            break;
+        case TextureEnvParameter::Color:
+            break;
+        default:
+            ANGLE_VALIDATION_ERR(context, InvalidEnum(), InvalidTextureEnvParameter);
+            return false;
+    }
+    return true;
+}
+
+bool ValidateGetTexEnvCommon(Context *context, TextureEnvTarget target, TextureEnvParameter pname)
+{
+    GLfloat dummy[4] = {};
+    switch (pname)
+    {
+        case TextureEnvParameter::Mode:
+            ConvertPackedEnum(TextureEnvMode::Add, dummy);
+            break;
+        case TextureEnvParameter::CombineRgb:
+        case TextureEnvParameter::CombineAlpha:
+            ConvertPackedEnum(TextureCombine::Add, dummy);
+            break;
+        case TextureEnvParameter::Src0Rgb:
+        case TextureEnvParameter::Src1Rgb:
+        case TextureEnvParameter::Src2Rgb:
+        case TextureEnvParameter::Src0Alpha:
+        case TextureEnvParameter::Src1Alpha:
+        case TextureEnvParameter::Src2Alpha:
+            ConvertPackedEnum(TextureSrc::Constant, dummy);
+            break;
+        case TextureEnvParameter::Op0Rgb:
+        case TextureEnvParameter::Op1Rgb:
+        case TextureEnvParameter::Op2Rgb:
+        case TextureEnvParameter::Op0Alpha:
+        case TextureEnvParameter::Op1Alpha:
+        case TextureEnvParameter::Op2Alpha:
+            ConvertPackedEnum(TextureOp::SrcAlpha, dummy);
+            break;
+        case TextureEnvParameter::RgbScale:
+        case TextureEnvParameter::AlphaScale:
+            dummy[0] = 1.0f;
+            break;
+        default:
+            break;
+    }
+
+    return ValidateTexEnvCommon(context, target, pname, dummy);
+}
+
 }  // namespace gl
 
 namespace gl
@@ -592,8 +759,7 @@
                          TextureEnvParameter pname,
                          GLfloat *params)
 {
-    UNIMPLEMENTED();
-    return true;
+    return ValidateGetTexEnvCommon(context, target, pname);
 }
 
 bool ValidateGetTexEnviv(Context *context,
@@ -601,8 +767,7 @@
                          TextureEnvParameter pname,
                          GLint *params)
 {
-    UNIMPLEMENTED();
-    return true;
+    return ValidateGetTexEnvCommon(context, target, pname);
 }
 
 bool ValidateGetTexEnvxv(Context *context,
@@ -610,8 +775,7 @@
                          TextureEnvParameter pname,
                          GLfixed *params)
 {
-    UNIMPLEMENTED();
-    return true;
+    return ValidateGetTexEnvCommon(context, target, pname);
 }
 
 bool ValidateGetTexParameterxv(Context *context, TextureType target, GLenum pname, GLfixed *params)
@@ -953,8 +1117,7 @@
                      TextureEnvParameter pname,
                      GLfloat param)
 {
-    UNIMPLEMENTED();
-    return true;
+    return ValidateTexEnvCommon(context, target, pname, &param);
 }
 
 bool ValidateTexEnvfv(Context *context,
@@ -962,8 +1125,7 @@
                       TextureEnvParameter pname,
                       const GLfloat *params)
 {
-    UNIMPLEMENTED();
-    return true;
+    return ValidateTexEnvCommon(context, target, pname, params);
 }
 
 bool ValidateTexEnvi(Context *context,
@@ -971,8 +1133,8 @@
                      TextureEnvParameter pname,
                      GLint param)
 {
-    UNIMPLEMENTED();
-    return true;
+    GLfloat paramf = static_cast<GLfloat>(param);
+    return ValidateTexEnvCommon(context, target, pname, &paramf);
 }
 
 bool ValidateTexEnviv(Context *context,
@@ -980,8 +1142,12 @@
                       TextureEnvParameter pname,
                       const GLint *params)
 {
-    UNIMPLEMENTED();
-    return true;
+    GLfloat paramsf[4];
+    for (unsigned int i = 0; i < GetTextureEnvParameterCount(pname); i++)
+    {
+        paramsf[i] = static_cast<GLfloat>(params[i]);
+    }
+    return ValidateTexEnvCommon(context, target, pname, paramsf);
 }
 
 bool ValidateTexEnvx(Context *context,
@@ -989,8 +1155,8 @@
                      TextureEnvParameter pname,
                      GLfixed param)
 {
-    UNIMPLEMENTED();
-    return true;
+    GLfloat paramf = static_cast<GLfloat>(param);
+    return ValidateTexEnvCommon(context, target, pname, &paramf);
 }
 
 bool ValidateTexEnvxv(Context *context,
@@ -998,8 +1164,12 @@
                       TextureEnvParameter pname,
                       const GLfixed *params)
 {
-    UNIMPLEMENTED();
-    return true;
+    GLfloat paramsf[4];
+    for (unsigned int i = 0; i < GetTextureEnvParameterCount(pname); i++)
+    {
+        paramsf[i] = static_cast<GLfloat>(params[i]);
+    }
+    return ValidateTexEnvCommon(context, target, pname, paramsf);
 }
 
 bool ValidateTexParameterx(Context *context, TextureType target, GLenum pname, GLfixed param)
diff --git a/src/tests/angle_end2end_tests.gypi b/src/tests/angle_end2end_tests.gypi
index a53cfd2..0265b05 100644
--- a/src/tests/angle_end2end_tests.gypi
+++ b/src/tests/angle_end2end_tests.gypi
@@ -65,6 +65,7 @@
             '<(angle_path)/src/tests/gl_tests/gles1/MatrixStackTest.cpp',
             '<(angle_path)/src/tests/gl_tests/gles1/LightsTest.cpp',
             '<(angle_path)/src/tests/gl_tests/gles1/ShadeModelTest.cpp',
+            '<(angle_path)/src/tests/gl_tests/gles1/TextureEnvTest.cpp',
             '<(angle_path)/src/tests/gl_tests/gles1/TextureTargetEnableTest.cpp',
             '<(angle_path)/src/tests/gl_tests/gles1/VertexPointerTest.cpp',
             '<(angle_path)/src/tests/gl_tests/GLSLTest.cpp',
diff --git a/src/tests/gl_tests/gles1/TextureEnvTest.cpp b/src/tests/gl_tests/gles1/TextureEnvTest.cpp
new file mode 100644
index 0000000..da01824
--- /dev/null
+++ b/src/tests/gl_tests/gles1/TextureEnvTest.cpp
@@ -0,0 +1,366 @@
+//
+// 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.
+//
+
+// TextureEnvTest.cpp: Tests basic usage of texture environments.
+
+#include "test_utils/ANGLETest.h"
+#include "test_utils/gl_raii.h"
+
+#include "random_utils.h"
+
+#include <stdint.h>
+
+using namespace angle;
+
+class TextureEnvTest : public ANGLETest
+{
+  protected:
+    TextureEnvTest()
+    {
+        setWindowWidth(32);
+        setWindowHeight(32);
+        setConfigRedBits(8);
+        setConfigGreenBits(8);
+        setConfigBlueBits(8);
+        setConfigAlphaBits(8);
+        setConfigDepthBits(24);
+    }
+
+    void verifyEnvironment(GLenum mode,
+                           GLenum combineRgb,
+                           GLenum combineAlpha,
+                           GLenum src0Rgb,
+                           GLenum src0Alpha,
+                           GLenum src1Rgb,
+                           GLenum src1Alpha,
+                           GLenum src2Rgb,
+                           GLenum src2Alpha,
+                           GLenum op0Rgb,
+                           GLenum op0Alpha,
+                           GLenum op1Rgb,
+                           GLenum op1Alpha,
+                           GLenum op2Rgb,
+                           GLenum op2Alpha,
+                           const GLColor32F &envColor,
+                           GLfloat rgbScale,
+                           GLfloat alphaScale)
+    {
+
+        GLfloat actualParams[4] = {};
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GLENUM_EQ(mode, actualParams[0]);
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_COMBINE_RGB, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GLENUM_EQ(combineRgb, actualParams[0]);
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GLENUM_EQ(combineAlpha, actualParams[0]);
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_SRC0_RGB, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GLENUM_EQ(src0Rgb, actualParams[0]);
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_SRC0_ALPHA, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GLENUM_EQ(src0Alpha, actualParams[0]);
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_SRC1_RGB, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GLENUM_EQ(src1Rgb, actualParams[0]);
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_SRC1_ALPHA, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GLENUM_EQ(src1Alpha, actualParams[0]);
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_SRC2_RGB, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GLENUM_EQ(src2Rgb, actualParams[0]);
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_SRC2_ALPHA, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GLENUM_EQ(src2Alpha, actualParams[0]);
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_OPERAND0_RGB, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GLENUM_EQ(op0Rgb, actualParams[0]);
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GLENUM_EQ(op0Alpha, actualParams[0]);
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_OPERAND1_RGB, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GLENUM_EQ(op1Rgb, actualParams[0]);
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GLENUM_EQ(op1Alpha, actualParams[0]);
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_OPERAND2_RGB, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GLENUM_EQ(op2Rgb, actualParams[0]);
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_OPERAND2_ALPHA, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GLENUM_EQ(op2Alpha, actualParams[0]);
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_EQ(envColor.R, actualParams[0]);
+        EXPECT_EQ(envColor.G, actualParams[1]);
+        EXPECT_EQ(envColor.B, actualParams[2]);
+        EXPECT_EQ(envColor.A, actualParams[3]);
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_RGB_SCALE, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_EQ(rgbScale, actualParams[0]);
+
+        glGetTexEnvfv(GL_TEXTURE_ENV, GL_ALPHA_SCALE, actualParams);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_EQ(alphaScale, actualParams[0]);
+    }
+};
+
+// Initial state check.
+TEST_P(TextureEnvTest, InitialState)
+{
+    GLint numUnits;
+    glGetIntegerv(GL_MAX_TEXTURE_UNITS, &numUnits);
+    EXPECT_GL_NO_ERROR();
+
+    for (int i = 0; i < numUnits; i++)
+    {
+        glActiveTexture(GL_TEXTURE0 + i);
+        EXPECT_GL_NO_ERROR();
+
+        verifyEnvironment(GL_MODULATE,                         // envMode
+                          GL_MODULATE,                         // combineRgb
+                          GL_MODULATE,                         // combineAlpha
+                          GL_TEXTURE,                          // src0Rgb
+                          GL_TEXTURE,                          // src0Alpha
+                          GL_PREVIOUS,                         // src1Rgb
+                          GL_PREVIOUS,                         // src1Alpha
+                          GL_CONSTANT,                         // src2Rgb
+                          GL_CONSTANT,                         // src2Alpha
+                          GL_SRC_COLOR,                        // op0Rgb
+                          GL_SRC_ALPHA,                        // op0Alpha
+                          GL_SRC_COLOR,                        // op1Rgb
+                          GL_SRC_ALPHA,                        // op1Alpha
+                          GL_SRC_ALPHA,                        // op2Rgb
+                          GL_SRC_ALPHA,                        // op2Alpha
+                          GLColor32F(0.0f, 0.0f, 0.0f, 0.0f),  // envColor
+                          1.0f,                                // rgbScale
+                          1.0f                                 // alphaScale
+                          );
+    }
+}
+
+// Negative test for parameter names.
+TEST_P(TextureEnvTest, NegativeParameter)
+{
+    glTexEnvfv(0, GL_ALPHA_SCALE, nullptr);
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+    glTexEnvfv(GL_ALPHA_SCALE, GL_ALPHA_SCALE, nullptr);
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+    glTexEnvfv(GL_TEXTURE_ENV, 0, nullptr);
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+}
+
+// Negative test for parameter values.
+TEST_P(TextureEnvTest, NegativeValues)
+{
+    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, 0.0f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, 0.0f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, 0.0f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    glTexEnvf(GL_TEXTURE_ENV, GL_SRC0_RGB, 0.0f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_SRC1_RGB, 0.0f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_SRC2_RGB, 0.0f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_SRC0_ALPHA, 0.0f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_SRC1_ALPHA, 0.0f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_SRC2_ALPHA, 0.0f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, 0.0f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB, 0.0f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND2_RGB, 0.0f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, 0.0f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, 0.0f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND2_ALPHA, 0.0f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, (GLfloat)GL_DOT3_RGB);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, (GLfloat)GL_DOT3_RGBA);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, (GLfloat)GL_SRC_COLOR);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, (GLfloat)GL_ONE_MINUS_SRC_COLOR);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, (GLfloat)GL_SRC_COLOR);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, (GLfloat)GL_ONE_MINUS_SRC_COLOR);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND2_ALPHA, (GLfloat)GL_SRC_COLOR);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND2_ALPHA, (GLfloat)GL_ONE_MINUS_SRC_COLOR);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+
+    glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, 0.5f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, 3.0f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_ALPHA_SCALE, 0.5f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+    glTexEnvf(GL_TEXTURE_ENV, GL_ALPHA_SCALE, 3.0f);
+    EXPECT_GL_ERROR(GL_INVALID_VALUE);
+}
+
+// Checks that texture environment state can be set.
+TEST_P(TextureEnvTest, Set)
+{
+    const int kTrials = 1000;
+
+    angle::RNG rng(0);
+
+    GLint numUnits;
+    glGetIntegerv(GL_MAX_TEXTURE_UNITS, &numUnits);
+    EXPECT_GL_NO_ERROR();
+
+    std::vector<GLenum> validUnits(numUnits);
+    for (int i = 0; i < numUnits; i++)
+    {
+        validUnits[i] = GL_TEXTURE0 + i;
+    }
+
+    std::vector<GLenum> validEnvModes = {
+        GL_ADD, GL_BLEND, GL_COMBINE, GL_DECAL, GL_MODULATE, GL_REPLACE,
+    };
+
+    std::vector<GLenum> validCombineRgbs = {
+        GL_MODULATE, GL_REPLACE,     GL_ADD,      GL_ADD_SIGNED,
+        GL_SUBTRACT, GL_INTERPOLATE, GL_DOT3_RGB, GL_DOT3_RGBA,
+    };
+
+    std::vector<GLenum> validCombineAlphas = {
+        GL_MODULATE, GL_REPLACE, GL_ADD, GL_ADD_SIGNED, GL_SUBTRACT, GL_INTERPOLATE,
+    };
+
+    std::vector<GLenum> validSrcs = {
+        GL_CONSTANT, GL_PREVIOUS, GL_PRIMARY_COLOR, GL_TEXTURE,
+    };
+
+    std::vector<GLenum> validOpRgbs = {
+        GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
+    };
+
+    std::vector<GLenum> validOpAlphas = {
+        GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
+    };
+
+    std::vector<GLfloat> validScales = {
+        1.0f, 2.0f, 4.0f,
+    };
+
+    for (int i = 0; i < kTrials; i++)
+    {
+        GLenum textureUnit  = rng.randomSelect(validUnits);
+        GLenum mode         = rng.randomSelect(validEnvModes);
+        GLenum combineRgb   = rng.randomSelect(validCombineRgbs);
+        GLenum combineAlpha = rng.randomSelect(validCombineAlphas);
+
+        GLenum src0Rgb   = rng.randomSelect(validSrcs);
+        GLenum src0Alpha = rng.randomSelect(validSrcs);
+        GLenum src1Rgb   = rng.randomSelect(validSrcs);
+        GLenum src1Alpha = rng.randomSelect(validSrcs);
+        GLenum src2Rgb   = rng.randomSelect(validSrcs);
+        GLenum src2Alpha = rng.randomSelect(validSrcs);
+
+        GLenum op0Rgb   = rng.randomSelect(validOpRgbs);
+        GLenum op0Alpha = rng.randomSelect(validOpAlphas);
+        GLenum op1Rgb   = rng.randomSelect(validOpRgbs);
+        GLenum op1Alpha = rng.randomSelect(validOpAlphas);
+        GLenum op2Rgb   = rng.randomSelect(validOpRgbs);
+        GLenum op2Alpha = rng.randomSelect(validOpAlphas);
+
+        GLColor32F envColor(rng.randomFloatBetween(0.0f, 1.0f), rng.randomFloatBetween(0.0f, 1.0f),
+                            rng.randomFloatBetween(0.0f, 1.0f), rng.randomFloatBetween(0.0f, 1.0f));
+
+        GLfloat rgbScale   = rng.randomSelect(validScales);
+        GLfloat alphaScale = rng.randomSelect(validScales);
+
+        glActiveTexture(textureUnit);
+        EXPECT_GL_NO_ERROR();
+
+        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode);
+        EXPECT_GL_NO_ERROR();
+        glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, combineRgb);
+        EXPECT_GL_NO_ERROR();
+        glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, combineAlpha);
+        EXPECT_GL_NO_ERROR();
+
+        glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, src0Rgb);
+        EXPECT_GL_NO_ERROR();
+        glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, src0Alpha);
+        EXPECT_GL_NO_ERROR();
+        glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, src1Rgb);
+        EXPECT_GL_NO_ERROR();
+        glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, src1Alpha);
+        EXPECT_GL_NO_ERROR();
+        glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, src2Rgb);
+        EXPECT_GL_NO_ERROR();
+        glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_ALPHA, src2Alpha);
+        EXPECT_GL_NO_ERROR();
+
+        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, op0Rgb);
+        EXPECT_GL_NO_ERROR();
+        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, op0Alpha);
+        EXPECT_GL_NO_ERROR();
+        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, op1Rgb);
+        EXPECT_GL_NO_ERROR();
+        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, op1Alpha);
+        EXPECT_GL_NO_ERROR();
+        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, op2Rgb);
+        EXPECT_GL_NO_ERROR();
+        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_ALPHA, op2Alpha);
+        EXPECT_GL_NO_ERROR();
+
+        glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, &envColor.R);
+        EXPECT_GL_NO_ERROR();
+
+        glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, rgbScale);
+        EXPECT_GL_NO_ERROR();
+
+        glTexEnvf(GL_TEXTURE_ENV, GL_ALPHA_SCALE, alphaScale);
+        EXPECT_GL_NO_ERROR();
+
+        verifyEnvironment(mode, combineRgb, combineAlpha, src0Rgb, src0Alpha, src1Rgb, src1Alpha,
+                          src2Rgb, src2Alpha, op0Rgb, op0Alpha, op1Rgb, op1Alpha, op2Rgb, op2Alpha,
+                          envColor, rgbScale, alphaScale);
+    }
+}
+
+ANGLE_INSTANTIATE_TEST(TextureEnvTest, ES1_D3D11(), ES1_OPENGL(), ES1_OPENGLES());