Support GL_OES_texture_border_clamp

Added support for GL_TEXTURE_BORDER_COLOR and GL_CLAMP_TO_BORDER in
OpenGL/OpenGLES, Direct3D9 and Direct3D11 backends.
For integer textures in OpenGLES3 contexts these additional entry points
are available now:

void glTexParameterIivOES(enum target, enum pname, const int *params);
void glTexParameterIuivOES(enum target, enum pname, const uint *params);

void glGetTexParameterIivOES(enum target, enum pname, int *params);
void glGetTexParameterIuivOES(enum target, enum pname, uint *params);

void glSamplerParameterIivOES(uint sampler, enum pname, const int *params);
void glSamplerParameterIuivOES(uint sampler, enum pname, const uint *params);

void glGetSamplerParameterIivOES(uint sampler, enum pname, int *params);
void glGetSamplerParameterIuivOES(uint sampler, enum pname, uint *params);

BUG=angleproject:2890
TEST=angle_end2end_tests.TextureBorderClamp*

Change-Id: Iee3eeb399d8d7851b3b30694ad8f21a2111f5828
Reviewed-on: https://chromium-review.googlesource.com/c/1257824
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/tests/gl_tests/TextureTest.cpp b/src/tests/gl_tests/TextureTest.cpp
index 4ee8151..e6c94b5 100644
--- a/src/tests/gl_tests/TextureTest.cpp
+++ b/src/tests/gl_tests/TextureTest.cpp
@@ -2751,6 +2751,439 @@
     runSamplerInStructTest();
 }
 
+// GL_OES_texture_border_clamp
+class TextureBorderClampTest : public Texture2DTest
+{
+  protected:
+    TextureBorderClampTest() : Texture2DTest() {}
+
+    std::string getVertexShaderSource() override
+    {
+        return
+            R"(precision highp float;
+            attribute vec4 position;
+            varying vec2 texcoord;
+
+            void main()
+            {
+                gl_Position = vec4(position.xy, 0.0, 1.0);
+                // texcoords in [-0.5, 1.5]
+                texcoord = (position.xy) + 0.5;
+            })";
+    }
+
+    void uploadTexture()
+    {
+        glActiveTexture(GL_TEXTURE0);
+        glBindTexture(GL_TEXTURE_2D, mTexture2D);
+        std::vector<GLColor> texDataRed(1, GLColor::red);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+                     texDataRed.data());
+        EXPECT_GL_NO_ERROR();
+    }
+};
+
+// Test if the color set as GL_TEXTURE_BORDER_COLOR is used when sampling outside of the texture in
+// GL_CLAMP_TO_BORDER wrap mode (set with glTexParameter).
+TEST_P(TextureBorderClampTest, TextureBorderClampFunctional)
+{
+    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
+
+    setUpProgram();
+
+    uploadTexture();
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, &kFloatGreen.R);
+    EXPECT_GL_NO_ERROR();
+
+    drawQuad(mProgram, "position", 0.5f);
+
+    EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+    EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::green);
+}
+
+// Test reading back GL_TEXTURE_BORDER_COLOR by glGetTexParameter.
+TEST_P(TextureBorderClampTest, TextureBorderClampFunctional2)
+{
+    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, mTexture2D);
+
+    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, &kFloatGreen.R);
+
+    GLint colorFixedPoint[4] = {0};
+    glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorFixedPoint);
+    constexpr GLint colorGreenFixedPoint[4] = {0, std::numeric_limits<GLint>::max(), 0,
+                                               std::numeric_limits<GLint>::max()};
+    EXPECT_EQ(colorFixedPoint[0], colorGreenFixedPoint[0]);
+    EXPECT_EQ(colorFixedPoint[1], colorGreenFixedPoint[1]);
+    EXPECT_EQ(colorFixedPoint[2], colorGreenFixedPoint[2]);
+    EXPECT_EQ(colorFixedPoint[3], colorGreenFixedPoint[3]);
+
+    constexpr GLint colorBlueFixedPoint[4] = {0, 0, std::numeric_limits<GLint>::max(),
+                                              std::numeric_limits<GLint>::max()};
+    glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorBlueFixedPoint);
+
+    GLfloat color[4] = {0.0f};
+    glGetTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color);
+    EXPECT_EQ(color[0], kFloatBlue.R);
+    EXPECT_EQ(color[1], kFloatBlue.G);
+    EXPECT_EQ(color[2], kFloatBlue.B);
+    EXPECT_EQ(color[3], kFloatBlue.A);
+}
+
+// Test GL_TEXTURE_BORDER_COLOR parameter validation at glTexParameter.
+TEST_P(TextureBorderClampTest, TextureBorderClampValidation)
+{
+    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
+
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, mTexture2D);
+
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, 1.0f);
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, std::numeric_limits<GLint>::max());
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+
+    glTexParameterfv(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_BORDER_COLOR, &kFloatGreen.R);
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+
+    GLint colorInt[4] = {0};
+    glTexParameteriv(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, GL_TEXTURE_BORDER_COLOR, colorInt);
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+
+    if (getClientMajorVersion() < 3)
+    {
+        glTexParameterIivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorInt);
+        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+        glGetTexParameterIivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorInt);
+        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+        GLuint colorUInt[4] = {0};
+        glTexParameterIuivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorUInt);
+        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+        glGetTexParameterIuivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorUInt);
+        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+        GLSampler sampler;
+        glSamplerParameterIivOES(sampler, GL_TEXTURE_BORDER_COLOR, colorInt);
+        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+        glGetSamplerParameterIivOES(sampler, GL_TEXTURE_BORDER_COLOR, colorInt);
+        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+        glSamplerParameterIuivOES(sampler, GL_TEXTURE_BORDER_COLOR, colorUInt);
+        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+        glGetSamplerParameterIuivOES(sampler, GL_TEXTURE_BORDER_COLOR, colorUInt);
+        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+    }
+}
+
+class TextureBorderClampTestES3 : public TextureBorderClampTest
+{
+  protected:
+    TextureBorderClampTestES3() : TextureBorderClampTest() {}
+};
+
+// Test if the color set as GL_TEXTURE_BORDER_COLOR is used when sampling outside of the texture in
+// GL_CLAMP_TO_BORDER wrap mode (set with glSamplerParameter).
+TEST_P(TextureBorderClampTestES3, TextureBorderClampES3Functional)
+{
+    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
+
+    setUpProgram();
+
+    uploadTexture();
+
+    GLSampler sampler;
+    glBindSampler(0, sampler);
+    glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+    glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+    glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glSamplerParameterfv(sampler, GL_TEXTURE_BORDER_COLOR, &kFloatGreen.R);
+    EXPECT_GL_NO_ERROR();
+
+    drawQuad(mProgram, "position", 0.5f);
+
+    EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+    EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::green);
+}
+
+// Test reading back GL_TEXTURE_BORDER_COLOR by glGetSamplerParameter.
+TEST_P(TextureBorderClampTestES3, TextureBorderClampES3Functional2)
+{
+    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
+
+    glActiveTexture(GL_TEXTURE0);
+
+    GLSampler sampler;
+    glBindSampler(0, sampler);
+
+    glSamplerParameterfv(sampler, GL_TEXTURE_BORDER_COLOR, &kFloatGreen.R);
+
+    GLint colorFixedPoint[4] = {0};
+    glGetSamplerParameteriv(sampler, GL_TEXTURE_BORDER_COLOR, colorFixedPoint);
+    constexpr GLint colorGreenFixedPoint[4] = {0, std::numeric_limits<GLint>::max(), 0,
+                                               std::numeric_limits<GLint>::max()};
+    EXPECT_EQ(colorFixedPoint[0], colorGreenFixedPoint[0]);
+    EXPECT_EQ(colorFixedPoint[1], colorGreenFixedPoint[1]);
+    EXPECT_EQ(colorFixedPoint[2], colorGreenFixedPoint[2]);
+    EXPECT_EQ(colorFixedPoint[3], colorGreenFixedPoint[3]);
+
+    constexpr GLint colorBlueFixedPoint[4] = {0, 0, std::numeric_limits<GLint>::max(),
+                                              std::numeric_limits<GLint>::max()};
+    glSamplerParameteriv(sampler, GL_TEXTURE_BORDER_COLOR, colorBlueFixedPoint);
+
+    GLfloat color[4] = {0.0f};
+    glGetSamplerParameterfv(sampler, GL_TEXTURE_BORDER_COLOR, color);
+    EXPECT_EQ(color[0], kFloatBlue.R);
+    EXPECT_EQ(color[1], kFloatBlue.G);
+    EXPECT_EQ(color[2], kFloatBlue.B);
+    EXPECT_EQ(color[3], kFloatBlue.A);
+
+    constexpr GLint colorSomewhatRedInt[4] = {500000, 0, 0, std::numeric_limits<GLint>::max()};
+    glSamplerParameterIivOES(sampler, GL_TEXTURE_BORDER_COLOR, colorSomewhatRedInt);
+    GLint colorInt[4] = {0};
+    glGetSamplerParameterIivOES(sampler, GL_TEXTURE_BORDER_COLOR, colorInt);
+    EXPECT_EQ(colorInt[0], colorSomewhatRedInt[0]);
+    EXPECT_EQ(colorInt[1], colorSomewhatRedInt[1]);
+    EXPECT_EQ(colorInt[2], colorSomewhatRedInt[2]);
+    EXPECT_EQ(colorInt[3], colorSomewhatRedInt[3]);
+
+    constexpr GLuint colorSomewhatRedUInt[4] = {500000, 0, 0, std::numeric_limits<GLuint>::max()};
+    glSamplerParameterIuivOES(sampler, GL_TEXTURE_BORDER_COLOR, colorSomewhatRedUInt);
+    GLuint colorUInt[4] = {0};
+    glGetSamplerParameterIuivOES(sampler, GL_TEXTURE_BORDER_COLOR, colorUInt);
+    EXPECT_EQ(colorUInt[0], colorSomewhatRedUInt[0]);
+    EXPECT_EQ(colorUInt[1], colorSomewhatRedUInt[1]);
+    EXPECT_EQ(colorUInt[2], colorSomewhatRedUInt[2]);
+    EXPECT_EQ(colorUInt[3], colorSomewhatRedUInt[3]);
+
+    glBindTexture(GL_TEXTURE_2D, mTexture2D);
+
+    constexpr GLint colorSomewhatGreenInt[4] = {0, 500000, 0, std::numeric_limits<GLint>::max()};
+    glTexParameterIivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorSomewhatGreenInt);
+    glGetTexParameterIivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorInt);
+    EXPECT_EQ(colorInt[0], colorSomewhatGreenInt[0]);
+    EXPECT_EQ(colorInt[1], colorSomewhatGreenInt[1]);
+    EXPECT_EQ(colorInt[2], colorSomewhatGreenInt[2]);
+    EXPECT_EQ(colorInt[3], colorSomewhatGreenInt[3]);
+
+    constexpr GLuint colorSomewhatGreenUInt[4] = {0, 500000, 0, std::numeric_limits<GLuint>::max()};
+    glTexParameterIuivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorSomewhatGreenUInt);
+    glGetTexParameterIuivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, colorUInt);
+    EXPECT_EQ(colorUInt[0], colorSomewhatGreenUInt[0]);
+    EXPECT_EQ(colorUInt[1], colorSomewhatGreenUInt[1]);
+    EXPECT_EQ(colorUInt[2], colorSomewhatGreenUInt[2]);
+    EXPECT_EQ(colorUInt[3], colorSomewhatGreenUInt[3]);
+}
+
+// Test GL_TEXTURE_BORDER_COLOR parameter validation at glSamplerParameter.
+TEST_P(TextureBorderClampTestES3, TextureBorderClampES3Validation)
+{
+    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
+
+    glActiveTexture(GL_TEXTURE0);
+
+    GLSampler sampler;
+    glBindSampler(0, sampler);
+
+    glSamplerParameterf(sampler, GL_TEXTURE_BORDER_COLOR, 1.0f);
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+
+    glSamplerParameteri(sampler, GL_TEXTURE_BORDER_COLOR, std::numeric_limits<GLint>::max());
+    EXPECT_GL_ERROR(GL_INVALID_ENUM);
+}
+
+class TextureBorderClampIntegerTestES3 : public Texture2DTest
+{
+  protected:
+    TextureBorderClampIntegerTestES3() : Texture2DTest(), isUnsignedIntTest(false) {}
+
+    std::string getVertexShaderSource() override
+    {
+        return
+            R"(#version 300 es
+            out vec2 texcoord;
+            in vec4 position;
+
+            void main()
+            {
+                gl_Position = vec4(position.xy, 0.0, 1.0);
+                // texcoords in [-0.5, 1.5]
+                texcoord = (position.xy) + 0.5;
+            })";
+    }
+
+    std::string getFragmentShaderSource()
+    {
+        // clang-format off
+        return std::string(
+               "#version 300 es\n"
+               "precision highp float;\n"
+               "uniform highp ") + (isUnsignedIntTest ? "usampler2D" : "isampler2D") + " tex;\n"
+               "in vec2 texcoord;\n"
+               "out vec4 fragColor;\n"
+
+               "void main()\n"
+               "{\n"
+               "vec4 red   = vec4(1.0, 0.0, 0.0, 1.0);\n"
+               "vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
+               "fragColor = (texture(tex, texcoord).r == " + (isUnsignedIntTest ? "150u" : "-50") + ")"
+               "            ? green : red;\n"
+               "}\n";
+        // clang-format on
+    }
+
+    void uploadTexture()
+    {
+        glActiveTexture(GL_TEXTURE0);
+        glBindTexture(GL_TEXTURE_2D, mTexture2D);
+        if (isUnsignedIntTest)
+        {
+            std::vector<GLubyte> texData(4, 100);
+            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, 1, 1, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE,
+                         texData.data());
+        }
+        else
+        {
+            std::vector<GLbyte> texData(4, 100);
+            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8I, 1, 1, 0, GL_RGBA_INTEGER, GL_BYTE,
+                         texData.data());
+        }
+        EXPECT_GL_NO_ERROR();
+    }
+
+    bool isUnsignedIntTest;
+};
+
+// Test if the integer values set as GL_TEXTURE_BORDER_COLOR is used when sampling outside of the
+// integer texture in GL_CLAMP_TO_BORDER wrap mode (set with glTexParameterIivOES).
+TEST_P(TextureBorderClampIntegerTestES3, TextureBorderClampInteger)
+{
+    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
+
+    setUpProgram();
+
+    uploadTexture();
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+    constexpr GLint borderColor[4] = {-50, -50, -50, -50};
+    glTexParameterIivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
+
+    EXPECT_GL_NO_ERROR();
+
+    drawQuad(mProgram, "position", 0.5f);
+
+    EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+    EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::green);
+}
+
+// Test if the integer values set as GL_TEXTURE_BORDER_COLOR is used when sampling outside of the
+// integer texture in GL_CLAMP_TO_BORDER wrap mode (set with glTexParameterIivOES).
+TEST_P(TextureBorderClampIntegerTestES3, TextureBorderClampInteger2)
+{
+    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
+
+    setUpProgram();
+
+    uploadTexture();
+
+    GLSampler sampler;
+    glBindSampler(0, sampler);
+    glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+    glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+    glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+    constexpr GLint borderColor[4] = {-50, -50, -50, -50};
+    glSamplerParameterIivOES(sampler, GL_TEXTURE_BORDER_COLOR, borderColor);
+
+    EXPECT_GL_NO_ERROR();
+
+    drawQuad(mProgram, "position", 0.5f);
+
+    EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+    EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::green);
+}
+
+// Test if the unsigned integer values set as GL_TEXTURE_BORDER_COLOR is used when sampling outside
+// of the unsigned integer texture in GL_CLAMP_TO_BORDER wrap mode (set with glTexParameterIuivOES).
+TEST_P(TextureBorderClampIntegerTestES3, TextureBorderClampIntegerUnsigned)
+{
+    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
+
+    isUnsignedIntTest = true;
+
+    setUpProgram();
+
+    uploadTexture();
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+    constexpr GLuint borderColor[4] = {150, 150, 150, 150};
+    glTexParameterIuivOES(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
+
+    EXPECT_GL_NO_ERROR();
+
+    drawQuad(mProgram, "position", 0.5f);
+
+    EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+    EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::green);
+}
+
+// Test if the unsigned integer values set as GL_TEXTURE_BORDER_COLOR is used when sampling outside
+// of the unsigned integer texture in GL_CLAMP_TO_BORDER wrap mode (set with
+// glSamplerParameterIuivOES).
+TEST_P(TextureBorderClampIntegerTestES3, TextureBorderClampIntegerUnsigned2)
+{
+    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_OES_texture_border_clamp"));
+
+    isUnsignedIntTest = true;
+
+    setUpProgram();
+
+    uploadTexture();
+
+    GLSampler sampler;
+    glBindSampler(0, sampler);
+    glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+    glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+    glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+    constexpr GLuint borderColor[4] = {150, 150, 150, 150};
+    glSamplerParameterIuivOES(sampler, GL_TEXTURE_BORDER_COLOR, borderColor);
+
+    EXPECT_GL_NO_ERROR();
+
+    drawQuad(mProgram, "position", 0.5f);
+
+    EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+    EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::green);
+}
+
+// ~GL_OES_texture_border_clamp
+
 class TextureLimitsTest : public ANGLETest
 {
   protected:
@@ -3932,6 +4365,13 @@
                        ES2_D3D9(),
                        ES2_OPENGL(),
                        ES2_OPENGLES());
+ANGLE_INSTANTIATE_TEST(TextureBorderClampTest,
+                       ES2_D3D11(),
+                       ES2_D3D9(),
+                       ES2_OPENGL(),
+                       ES2_OPENGLES());
+ANGLE_INSTANTIATE_TEST(TextureBorderClampTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
+ANGLE_INSTANTIATE_TEST(TextureBorderClampIntegerTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
 ANGLE_INSTANTIATE_TEST(TextureLimitsTest, ES2_D3D11(), ES2_OPENGL(), ES2_OPENGLES(), ES2_VULKAN());
 ANGLE_INSTANTIATE_TEST(Texture2DNorm16TestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
 ANGLE_INSTANTIATE_TEST(TextureCubeTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());