Edit

kc3-lang/angle/src/tests/gl_tests/ClipDistanceTest.cpp

Branch :

  • Show log

    Commit

  • Author : Yuly Novikov
    Date : 2021-03-02 19:04:57
    Hash : a6b16d29
    Message : Suppress UNINSTANTIATED_PARAMETERIZED_TEST failures on Ozone We only support ES2 on Ozone, so tests that depend on ES3 or ES31 support are not instantiated there. Bug: chromium:1183147 Change-Id: Id58bcd9b44a5b9a70b5ae8115e27c44f5dc81226 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2726550 Reviewed-by: Jonah Ryan-Davis <jonahr@google.com> Commit-Queue: Yuly Novikov <ynovikov@chromium.org>

  • src/tests/gl_tests/ClipDistanceTest.cpp
  • //
    // Copyright 2020 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.
    //
    // ClipDistanceTest.cpp: Test cases for GL_APPLE_clip_distance/GL_EXT_clip_cull_distance extension.
    //
    
    #include "test_utils/ANGLETest.h"
    #include "test_utils/gl_raii.h"
    #include "util/EGLWindow.h"
    #include "util/test_utils.h"
    
    using namespace angle;
    
    class ClipDistanceTest : public ANGLETest
    {
      protected:
        ClipDistanceTest()
        {
            setWindowWidth(16);
            setWindowHeight(16);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
            setConfigDepthBits(24);
        }
    };
    
    // Query max clip distances and enable, disable states of clip distances
    TEST_P(ClipDistanceTest, StateQuery)
    {
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_APPLE_clip_distance"));
    
        GLint maxClipDistances = 0;
        glGetIntegerv(GL_MAX_CLIP_DISTANCES_APPLE, &maxClipDistances);
    
        EXPECT_GL_NO_ERROR();
        EXPECT_GE(maxClipDistances, 8);
    
        GLboolean enabled = glIsEnabled(GL_CLIP_DISTANCE1_APPLE);
        EXPECT_GL_NO_ERROR();
        EXPECT_EQ(enabled, GL_FALSE);
    
        glEnable(GL_CLIP_DISTANCE1_APPLE);
        EXPECT_GL_NO_ERROR();
        glEnable(GL_CLIP_DISTANCE7_APPLE);
        EXPECT_GL_NO_ERROR();
        enabled = glIsEnabled(GL_CLIP_DISTANCE1_APPLE);
        EXPECT_EQ(enabled, GL_TRUE);
    
        glDisable(GL_CLIP_DISTANCE1_APPLE);
        EXPECT_GL_NO_ERROR();
        enabled = glIsEnabled(GL_CLIP_DISTANCE1_APPLE);
        EXPECT_EQ(enabled, GL_FALSE);
    
        EXPECT_EQ(glIsEnabled(GL_CLIP_DISTANCE7_APPLE), GL_TRUE);
    }
    
    // Write to one gl_ClipDistance element
    TEST_P(ClipDistanceTest, OneClipDistance)
    {
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_APPLE_clip_distance"));
    
        constexpr char kVS[] = R"(
    #extension GL_APPLE_clip_distance : require
    
    uniform vec4 u_plane;
    
    attribute vec2 a_position;
    
    void main()
    {
        gl_Position = vec4(a_position, 0.0, 1.0);
    
        gl_ClipDistance[0] = dot(gl_Position, u_plane);
    })";
    
        ANGLE_GL_PROGRAM(programRed, kVS, essl1_shaders::fs::Red());
        glLinkProgram(programRed);
        glUseProgram(programRed);
        ASSERT_GL_NO_ERROR();
    
        glEnable(GL_CLIP_DISTANCE0_APPLE);
    
        // Clear to blue
        glClearColor(0, 0, 1, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Draw full screen quad with color red
        glUniform4f(glGetUniformLocation(programRed, "u_plane"), 1, 0, 0, 0.5);
        EXPECT_GL_NO_ERROR();
        drawQuad(programRed, "a_position", 0);
        EXPECT_GL_NO_ERROR();
    
        // All pixels on the left of the plane x = -0.5 must be blue
        for (int x = 0; x < getWindowWidth() / 4 - 1; ++x)
        {
            for (int y = 0; y < getWindowHeight(); ++y)
            {
                EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::blue);
            }
        }
    
        // All pixels on the right of the plane x = -0.5 must be red
        for (int x = getWindowWidth() / 4 + 2; x < getWindowWidth(); ++x)
        {
            for (int y = 0; y < getWindowHeight(); ++y)
            {
                EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::red);
            }
        }
    
        // Clear to green
        glClearColor(0, 1, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Draw full screen quad with color red
        glUniform4f(glGetUniformLocation(programRed, "u_plane"), -1, 0, 0, -0.5);
        EXPECT_GL_NO_ERROR();
        drawQuad(programRed, "a_position", 0);
        EXPECT_GL_NO_ERROR();
    
        // All pixels on the left of the plane x = -0.5 must be red
        for (int x = 0; x < getWindowWidth() / 4 - 1; ++x)
        {
            for (int y = 0; y < getWindowHeight(); ++y)
            {
                EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::red);
            }
        }
    
        // All pixels on the right of the plane x = -0.5 must be green
        for (int x = getWindowWidth() / 4 + 2; x < getWindowWidth(); ++x)
        {
            for (int y = 0; y < getWindowHeight(); ++y)
            {
                EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::green);
            }
        }
    
        // Disable GL_CLIP_DISTANCE
        glDisable(GL_CLIP_DISTANCE0_APPLE);
        drawQuad(programRed, "a_position", 0);
    
        // All pixels must be red
        for (int x = 0; x < getWindowWidth(); ++x)
        {
            for (int y = 0; y < getWindowHeight(); ++y)
            {
                EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::red);
            }
        }
    }
    
    // Write to 3 clip distances
    TEST_P(ClipDistanceTest, ThreeClipDistances)
    {
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_APPLE_clip_distance"));
    
        constexpr char kVS[] = R"(
    #extension GL_APPLE_clip_distance : require
    
    uniform vec4 u_plane[3];
    
    attribute vec2 a_position;
    
    void main()
    {
        gl_Position = vec4(a_position, 0.0, 1.0);
    
        gl_ClipDistance[0] = dot(gl_Position, u_plane[0]);
        gl_ClipDistance[3] = dot(gl_Position, u_plane[1]);
        gl_ClipDistance[7] = dot(gl_Position, u_plane[2]);
    })";
    
        ANGLE_GL_PROGRAM(programRed, kVS, essl1_shaders::fs::Red());
        glLinkProgram(programRed);
        glUseProgram(programRed);
        ASSERT_GL_NO_ERROR();
    
        // Enable 3 clip distances
        glEnable(GL_CLIP_DISTANCE0_APPLE);
        glEnable(GL_CLIP_DISTANCE3_APPLE);
        glEnable(GL_CLIP_DISTANCE7_APPLE);
        ASSERT_GL_NO_ERROR();
    
        // Clear to blue
        glClearColor(0, 0, 1, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Draw full screen quad with color red
        // x = -0.5
        glUniform4f(glGetUniformLocation(programRed, "u_plane[0]"), 1, 0, 0, 0.5);
        // x = 0.5
        glUniform4f(glGetUniformLocation(programRed, "u_plane[1]"), -1, 0, 0, 0.5);
        // x + y = 1
        glUniform4f(glGetUniformLocation(programRed, "u_plane[2]"), -1, -1, 0, 1);
        EXPECT_GL_NO_ERROR();
        drawQuad(programRed, "a_position", 0);
        EXPECT_GL_NO_ERROR();
    
        // All pixels on the left of the plane x = -0.5 must be blue
        for (int x = 0; x < getWindowWidth() / 4 - 1; ++x)
        {
            for (int y = 0; y < getWindowHeight(); ++y)
            {
                EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::blue);
            }
        }
    
        // All pixels on the right of the plane x = -0.5 must be red, except those in the upper right
        // triangle
        for (int x = getWindowWidth() / 4 + 2; x < getWindowWidth() / 2; ++x)
        {
            for (int y = 0; y < getWindowHeight(); ++y)
            {
                EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::red);
            }
        }
    
        for (int y = 0; y < getWindowHeight(); ++y)
        {
            for (int x = getWindowWidth() / 2; x < getWindowWidth(); ++x)
            {
                if (x < getWindowWidth() * 3 / 2 - y - 1 && x < getWindowWidth() * 3 / 4 - 1)
                {
                    // bottom left triangle clipped by x=0.5 plane
                    EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::red);
                }
                else if (x > getWindowWidth() * 3 / 2 - y + 1 || x > getWindowWidth() * 3 / 4 + 1)
                {
                    // upper right triangle plus right of x=0.5 plane
                    EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::blue);
                }
            }
        }
    
        // Clear to green
        glClearColor(0, 1, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Disable gl_ClipDistance[3]
        glDisable(GL_CLIP_DISTANCE3_APPLE);
    
        // Draw full screen quad with color red
        EXPECT_GL_NO_ERROR();
        drawQuad(programRed, "a_position", 0);
        EXPECT_GL_NO_ERROR();
    
        // All pixels on the left of the plane x = -0.5 must be green
        for (int x = 0; x < getWindowWidth() / 4 - 1; ++x)
        {
            for (int y = 0; y < getWindowHeight(); ++y)
            {
                EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::green);
            }
        }
    
        // All pixels on the right of the plane x = -0.5 must be red, except those in the upper right
        // triangle
        for (int x = getWindowWidth() / 4 + 2; x < getWindowWidth() / 2; ++x)
        {
            for (int y = 0; y < getWindowHeight(); ++y)
            {
                EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::red);
            }
        }
    
        for (int y = 0; y < getWindowHeight(); ++y)
        {
            for (int x = getWindowWidth() / 2; x < getWindowWidth(); ++x)
            {
                if (x < getWindowWidth() * 3 / 2 - y - 1)
                {
                    // bottom left triangle
                    EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::red);
                }
                else if (x > getWindowWidth() * 3 / 2 - y + 1)
                {
                    // upper right triangle
                    EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::green);
                }
            }
        }
    }
    
    // Redeclare gl_ClipDistance in shader with explicit size, also use it in a global function
    // outside main()
    TEST_P(ClipDistanceTest, ThreeClipDistancesRedeclared)
    {
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_APPLE_clip_distance"));
    
        constexpr char kVS[] = R"(
    #extension GL_APPLE_clip_distance : require
    
    varying highp float gl_ClipDistance[3];
    
    void computeClipDistances(in vec4 position, in vec4 plane[3])
    {
        gl_ClipDistance[0] = dot(position, plane[0]);
        gl_ClipDistance[1] = dot(position, plane[1]);
        gl_ClipDistance[2] = dot(position, plane[2]);
    }
    
    uniform vec4 u_plane[3];
    
    attribute vec2 a_position;
    
    void main()
    {
        gl_Position = vec4(a_position, 0.0, 1.0);
    
        computeClipDistances(gl_Position, u_plane);
    })";
    
        ANGLE_GL_PROGRAM(programRed, kVS, essl1_shaders::fs::Red());
        glLinkProgram(programRed);
        glUseProgram(programRed);
        ASSERT_GL_NO_ERROR();
    
        // Enable 3 clip distances
        glEnable(GL_CLIP_DISTANCE0_APPLE);
        glEnable(GL_CLIP_DISTANCE1_APPLE);
        glEnable(GL_CLIP_DISTANCE2_APPLE);
        ASSERT_GL_NO_ERROR();
    
        // Clear to blue
        glClearColor(0, 0, 1, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Draw full screen quad with color red
        // x = -0.5
        glUniform4f(glGetUniformLocation(programRed, "u_plane[0]"), 1, 0, 0, 0.5);
        // x = 0.5
        glUniform4f(glGetUniformLocation(programRed, "u_plane[1]"), -1, 0, 0, 0.5);
        // x + y = 1
        glUniform4f(glGetUniformLocation(programRed, "u_plane[2]"), -1, -1, 0, 1);
        EXPECT_GL_NO_ERROR();
        drawQuad(programRed, "a_position", 0);
        EXPECT_GL_NO_ERROR();
    
        // All pixels on the left of the plane x = -0.5 must be blue
        for (int x = 0; x < getWindowWidth() / 4 - 1; ++x)
        {
            for (int y = 0; y < getWindowHeight(); ++y)
            {
                EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::blue);
            }
        }
    
        // All pixels on the right of the plane x = -0.5 must be red, except those in the upper right
        // triangle
        for (int x = getWindowWidth() / 4 + 2; x < getWindowWidth() / 2; ++x)
        {
            for (int y = 0; y < getWindowHeight(); ++y)
            {
                EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::red);
            }
        }
    
        for (int y = 0; y < getWindowHeight(); ++y)
        {
            for (int x = getWindowWidth() / 2; x < getWindowWidth(); ++x)
            {
                if (x < getWindowWidth() * 3 / 2 - y - 1 && x < getWindowWidth() * 3 / 4 - 1)
                {
                    // bottom left triangle clipped by x=0.5 plane
                    EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::red);
                }
                else if (x > getWindowWidth() * 3 / 2 - y + 1 || x > getWindowWidth() * 3 / 4 + 1)
                {
                    // upper right triangle plus right of x=0.5 plane
                    EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::blue);
                }
            }
        }
    }
    
    class ClipCullDistanceTest : public ClipDistanceTest
    {};
    
    // Query max clip distances and enable, disable states of clip distances
    TEST_P(ClipCullDistanceTest, StateQuery)
    {
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_clip_cull_distance"));
    
        GLint maxClipDistances = 0;
        glGetIntegerv(GL_MAX_CLIP_DISTANCES_EXT, &maxClipDistances);
    
        EXPECT_GL_NO_ERROR();
        EXPECT_GE(maxClipDistances, 8);
    
        GLint maxCullDistances = 0;
        glGetIntegerv(GL_MAX_CULL_DISTANCES_EXT, &maxCullDistances);
    
        EXPECT_GL_NO_ERROR();
        EXPECT_GE(maxCullDistances, 8);
    
        GLint maxCombinedClipAndCullDistances = 0;
        glGetIntegerv(GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES_EXT, &maxCombinedClipAndCullDistances);
    
        EXPECT_GL_NO_ERROR();
        EXPECT_GE(maxCombinedClipAndCullDistances, 8);
    
        GLboolean enabled = glIsEnabled(GL_CLIP_DISTANCE1_EXT);
        EXPECT_GL_NO_ERROR();
        EXPECT_EQ(enabled, GL_FALSE);
    
        glEnable(GL_CLIP_DISTANCE1_EXT);
        EXPECT_GL_NO_ERROR();
        glEnable(GL_CLIP_DISTANCE7_EXT);
        EXPECT_GL_NO_ERROR();
        enabled = glIsEnabled(GL_CLIP_DISTANCE1_EXT);
        EXPECT_EQ(enabled, GL_TRUE);
    
        glDisable(GL_CLIP_DISTANCE1_EXT);
        EXPECT_GL_NO_ERROR();
        enabled = glIsEnabled(GL_CLIP_DISTANCE1_EXT);
        EXPECT_EQ(enabled, GL_FALSE);
    
        EXPECT_EQ(glIsEnabled(GL_CLIP_DISTANCE7_EXT), GL_TRUE);
    }
    
    // Check that the validation for EXT_clip_cull_distance extension is correct
    // If gl_ClipDistance or gl_CullDistance is redeclared in some shader stages, the array size of the
    // redeclared variables should match
    TEST_P(ClipCullDistanceTest, SizeCheck)
    {
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_clip_cull_distance"));
    
        // Validate array size match of redeclared ClipDistance built-in
        constexpr char kVSErrorClipDistance[] = R"(#version 300 es
    #extension GL_EXT_clip_cull_distance : require
    
    out highp float gl_ClipDistance[1];
    
    uniform vec4 u_plane;
    
    in vec2 a_position;
    
    void main()
    {
        gl_Position = vec4(a_position, 0.0, 1.0);
    
        gl_ClipDistance[0] = dot(gl_Position, u_plane);
    
        gl_CullDistance[0] = dot(gl_Position, u_plane);
        gl_CullDistance[1] = dot(gl_Position, u_plane);
    })";
    
        constexpr char kFSErrorClipDistance[] = R"(#version 300 es
    #extension GL_EXT_clip_cull_distance : require
    in highp float gl_ClipDistance[2];
    precision highp float;
    out vec4 my_FragColor;
    void main()
    {
        my_FragColor = vec4(gl_ClipDistance[0], gl_ClipDistance[1], gl_CullDistance[0], 1.0f);
    })";
    
        GLProgram programClipDistance;
        programClipDistance.makeRaster(kVSErrorClipDistance, kFSErrorClipDistance);
        EXPECT_GL_FALSE(programClipDistance.valid());
    
        // Validate array size match of redeclared CullDistance built-in
        constexpr char kVSErrorCullDistance[] = R"(#version 300 es
    #extension GL_EXT_clip_cull_distance : require
    
    out highp float gl_CullDistance[1];
    
    uniform vec4 u_plane;
    
    in vec2 a_position;
    
    void main()
    {
        gl_Position = vec4(a_position, 0.0, 1.0);
    
        gl_CullDistance[0] = dot(gl_Position, u_plane);
    
        gl_ClipDistance[0] = dot(gl_Position, u_plane);
        gl_ClipDistance[1] = dot(gl_Position, u_plane);
    })";
    
        constexpr char kFSErrorCullDistance[] = R"(#version 300 es
    #extension GL_EXT_clip_cull_distance : require
    in highp float gl_CullDistance[2];
    precision highp float;
    out vec4 my_FragColor;
    void main()
    {
        my_FragColor = vec4(gl_CullDistance[0], gl_CullDistance[1], gl_ClipDistance[0], 1.0f);
    })";
    
        GLProgram programCullDistance;
        programCullDistance.makeRaster(kVSErrorCullDistance, kFSErrorCullDistance);
        EXPECT_GL_FALSE(programCullDistance.valid());
    }
    
    // Write to one gl_ClipDistance element
    TEST_P(ClipCullDistanceTest, OneClipDistance)
    {
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_clip_cull_distance"));
    
        constexpr char kVS[] = R"(#version 300 es
    #extension GL_EXT_clip_cull_distance : require
    
    uniform vec4 u_plane;
    
    in vec2 a_position;
    
    void main()
    {
        gl_Position = vec4(a_position, 0.0, 1.0);
    
        gl_ClipDistance[0] = dot(gl_Position, u_plane);
    })";
    
        ANGLE_GL_PROGRAM(programRed, kVS, essl3_shaders::fs::Red());
        glLinkProgram(programRed);
        glUseProgram(programRed);
        ASSERT_GL_NO_ERROR();
    
        glEnable(GL_CLIP_DISTANCE0_EXT);
    
        // Clear to blue
        glClearColor(0, 0, 1, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Draw full screen quad with color red
        glUniform4f(glGetUniformLocation(programRed, "u_plane"), 1, 0, 0, 0.5);
        EXPECT_GL_NO_ERROR();
        drawQuad(programRed, "a_position", 0);
        EXPECT_GL_NO_ERROR();
    
        // All pixels on the left of the plane x = -0.5 must be blue
        GLuint x      = 0;
        GLuint y      = 0;
        GLuint width  = getWindowWidth() / 4 - 1;
        GLuint height = getWindowHeight();
        EXPECT_PIXEL_RECT_EQ(x, y, width, height, GLColor::blue);
    
        // All pixels on the right of the plane x = -0.5 must be red
        x      = getWindowWidth() / 4 + 2;
        y      = 0;
        width  = getWindowWidth() - x;
        height = getWindowHeight();
        EXPECT_PIXEL_RECT_EQ(x, y, width, height, GLColor::red);
    
        // Clear to green
        glClearColor(0, 1, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Draw full screen quad with color red
        glUniform4f(glGetUniformLocation(programRed, "u_plane"), -1, 0, 0, -0.5);
        EXPECT_GL_NO_ERROR();
        drawQuad(programRed, "a_position", 0);
        EXPECT_GL_NO_ERROR();
    
        // All pixels on the left of the plane x = -0.5 must be red
        x      = 0;
        y      = 0;
        width  = getWindowWidth() / 4 - 1;
        height = getWindowHeight();
        EXPECT_PIXEL_RECT_EQ(x, y, width, height, GLColor::red);
    
        // All pixels on the right of the plane x = -0.5 must be green
        x      = getWindowWidth() / 4 + 2;
        y      = 0;
        width  = getWindowWidth() - x;
        height = getWindowHeight();
        EXPECT_PIXEL_RECT_EQ(x, y, width, height, GLColor::green);
    
        // Disable GL_CLIP_DISTANCE
        glDisable(GL_CLIP_DISTANCE0_EXT);
        drawQuad(programRed, "a_position", 0);
    
        // All pixels must be red
        x      = 0;
        y      = 0;
        width  = getWindowWidth();
        height = getWindowHeight();
        EXPECT_PIXEL_RECT_EQ(x, y, width, height, GLColor::red);
    }
    
    // Write to 3 clip distances
    TEST_P(ClipCullDistanceTest, ThreeClipDistances)
    {
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_clip_cull_distance"));
    
        constexpr char kVS[] = R"(#version 300 es
    #extension GL_EXT_clip_cull_distance : require
    
    uniform vec4 u_plane[3];
    
    in vec2 a_position;
    
    void main()
    {
        gl_Position = vec4(a_position, 0.0, 1.0);
    
        gl_ClipDistance[0] = dot(gl_Position, u_plane[0]);
        gl_ClipDistance[3] = dot(gl_Position, u_plane[1]);
        gl_ClipDistance[7] = dot(gl_Position, u_plane[2]);
    })";
    
        ANGLE_GL_PROGRAM(programRed, kVS, essl3_shaders::fs::Red());
        glLinkProgram(programRed);
        glUseProgram(programRed);
        ASSERT_GL_NO_ERROR();
    
        // Enable 3 clip distances
        glEnable(GL_CLIP_DISTANCE0_EXT);
        glEnable(GL_CLIP_DISTANCE3_EXT);
        glEnable(GL_CLIP_DISTANCE7_EXT);
        ASSERT_GL_NO_ERROR();
    
        // Clear to blue
        glClearColor(0, 0, 1, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Draw full screen quad with color red
        // x = -0.5
        glUniform4f(glGetUniformLocation(programRed, "u_plane[0]"), 1, 0, 0, 0.5);
        // x = 0.5
        glUniform4f(glGetUniformLocation(programRed, "u_plane[1]"), -1, 0, 0, 0.5);
        // x + y = 1
        glUniform4f(glGetUniformLocation(programRed, "u_plane[2]"), -1, -1, 0, 1);
        EXPECT_GL_NO_ERROR();
        drawQuad(programRed, "a_position", 0);
        EXPECT_GL_NO_ERROR();
    
        // All pixels on the left of the plane x = -0.5 must be blue
        GLuint x      = 0;
        GLuint y      = 0;
        GLuint width  = getWindowWidth() / 4 - 1;
        GLuint height = getWindowHeight();
        EXPECT_PIXEL_RECT_EQ(x, y, width, height, GLColor::blue);
    
        // All pixels on the right of the plane x = -0.5 must be red, except those in the upper right
        // triangle
        x      = getWindowWidth() / 4 + 2;
        y      = 0;
        width  = getWindowWidth() / 2 - x;
        height = getWindowHeight();
        EXPECT_PIXEL_RECT_EQ(x, y, width, height, GLColor::red);
    
        std::vector<GLColor> actualColors(getWindowWidth() * getWindowHeight());
        glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
                     actualColors.data());
        for (int y = 0; y < getWindowHeight(); ++y)
        {
            for (int x = getWindowWidth() / 2; x < getWindowWidth(); ++x)
            {
                const int currentPosition = y * getWindowHeight() + x;
    
                if (x < getWindowWidth() * 3 / 2 - y - 1 && x < getWindowWidth() * 3 / 4 - 1)
                {
                    // bottom left triangle clipped by x=0.5 plane
                    EXPECT_EQ(GLColor::red, actualColors[currentPosition]);
                }
                else if (x > getWindowWidth() * 3 / 2 - y + 1 || x > getWindowWidth() * 3 / 4 + 1)
                {
                    // upper right triangle plus right of x=0.5 plane
                    EXPECT_EQ(GLColor::blue, actualColors[currentPosition]);
                }
            }
        }
    
        // Clear to green
        glClearColor(0, 1, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Disable gl_ClipDistance[3]
        glDisable(GL_CLIP_DISTANCE3_EXT);
    
        // Draw full screen quad with color red
        EXPECT_GL_NO_ERROR();
        drawQuad(programRed, "a_position", 0);
        EXPECT_GL_NO_ERROR();
    
        // All pixels on the left of the plane x = -0.5 must be green
        for (int x = 0; x < getWindowWidth() / 4 - 1; ++x)
        {
            for (int y = 0; y < getWindowHeight(); ++y)
            {
                EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::green);
            }
        }
    
        // All pixels on the right of the plane x = -0.5 must be red, except those in the upper right
        // triangle
        x      = getWindowWidth() / 4 + 2;
        y      = 0;
        width  = getWindowWidth() / 2 - x;
        height = getWindowHeight();
        EXPECT_PIXEL_RECT_EQ(x, y, width, height, GLColor::red);
    
        glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
                     actualColors.data());
        for (int y = 0; y < getWindowHeight(); ++y)
        {
            for (int x = getWindowWidth() / 2; x < getWindowWidth(); ++x)
            {
                const int currentPosition = y * getWindowHeight() + x;
    
                if (x < getWindowWidth() * 3 / 2 - y - 1)
                {
                    // bottom left triangle
                    EXPECT_EQ(GLColor::red, actualColors[currentPosition]);
                }
                else if (x > getWindowWidth() * 3 / 2 - y + 1)
                {
                    // upper right triangle
                    EXPECT_EQ(GLColor::green, actualColors[currentPosition]);
                }
            }
        }
    }
    
    // Redeclare gl_ClipDistance in shader with explicit size, also use it in a global function
    // outside main()
    TEST_P(ClipCullDistanceTest, ThreeClipDistancesRedeclared)
    {
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_clip_cull_distance"));
    
        constexpr char kVS[] = R"(#version 300 es
    #extension GL_EXT_clip_cull_distance : require
    
    out highp float gl_ClipDistance[3];
    
    void computeClipDistances(in vec4 position, in vec4 plane[3])
    {
        gl_ClipDistance[0] = dot(position, plane[0]);
        gl_ClipDistance[1] = dot(position, plane[1]);
        gl_ClipDistance[2] = dot(position, plane[2]);
    }
    
    uniform vec4 u_plane[3];
    
    in vec2 a_position;
    
    void main()
    {
        gl_Position = vec4(a_position, 0.0, 1.0);
    
        computeClipDistances(gl_Position, u_plane);
    })";
    
        ANGLE_GL_PROGRAM(programRed, kVS, essl3_shaders::fs::Red());
        glLinkProgram(programRed);
        glUseProgram(programRed);
        ASSERT_GL_NO_ERROR();
    
        // Enable 3 clip distances
        glEnable(GL_CLIP_DISTANCE0_EXT);
        glEnable(GL_CLIP_DISTANCE1_EXT);
        glEnable(GL_CLIP_DISTANCE2_EXT);
        ASSERT_GL_NO_ERROR();
    
        // Clear to blue
        glClearColor(0, 0, 1, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Draw full screen quad with color red
        // x = -0.5
        glUniform4f(glGetUniformLocation(programRed, "u_plane[0]"), 1, 0, 0, 0.5);
        // x = 0.5
        glUniform4f(glGetUniformLocation(programRed, "u_plane[1]"), -1, 0, 0, 0.5);
        // x + y = 1
        glUniform4f(glGetUniformLocation(programRed, "u_plane[2]"), -1, -1, 0, 1);
        EXPECT_GL_NO_ERROR();
        drawQuad(programRed, "a_position", 0);
        EXPECT_GL_NO_ERROR();
    
        // All pixels on the left of the plane x = -0.5 must be blue
        GLuint x      = 0;
        GLuint y      = 0;
        GLuint width  = getWindowWidth() / 4 - 1;
        GLuint height = getWindowHeight();
        EXPECT_PIXEL_RECT_EQ(x, y, width, height, GLColor::blue);
    
        // All pixels on the right of the plane x = -0.5 must be red, except those in the upper right
        // triangle
        x      = getWindowWidth() / 4 + 2;
        y      = 0;
        width  = getWindowWidth() / 2 - x;
        height = getWindowHeight();
        EXPECT_PIXEL_RECT_EQ(x, y, width, height, GLColor::red);
    
        std::vector<GLColor> actualColors(getWindowWidth() * getWindowHeight());
        glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
                     actualColors.data());
        for (int y = 0; y < getWindowHeight(); ++y)
        {
            for (int x = getWindowWidth() / 2; x < getWindowWidth(); ++x)
            {
                const int currentPosition = y * getWindowHeight() + x;
    
                if (x < getWindowWidth() * 3 / 2 - y - 1 && x < getWindowWidth() * 3 / 4 - 1)
                {
                    // bottom left triangle clipped by x=0.5 plane
                    EXPECT_EQ(GLColor::red, actualColors[currentPosition]);
                }
                else if (x > getWindowWidth() * 3 / 2 - y + 1 || x > getWindowWidth() * 3 / 4 + 1)
                {
                    // upper right triangle plus right of x=0.5 plane
                    EXPECT_EQ(GLColor::blue, actualColors[currentPosition]);
                }
            }
        }
    }
    
    // Write to one gl_CullDistance element
    TEST_P(ClipCullDistanceTest, OneCullDistance)
    {
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_clip_cull_distance"));
    
        constexpr char kVS[] = R"(#version 300 es
    #extension GL_EXT_clip_cull_distance : require
    
    uniform vec4 u_plane;
    
    in vec2 a_position;
    
    void main()
    {
        gl_Position = vec4(a_position, 0.0, 1.0);
    
        gl_CullDistance[0] = dot(gl_Position, u_plane);
    })";
    
        ANGLE_GL_PROGRAM(programRed, kVS, essl3_shaders::fs::Red());
        glLinkProgram(programRed);
        glUseProgram(programRed);
        ASSERT_GL_NO_ERROR();
    
        // Clear to blue
        glClearColor(0, 0, 1, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Draw full screen quad with color red
        glUniform4f(glGetUniformLocation(programRed, "u_plane"), 1, 0, 0, 0.5);
        EXPECT_GL_NO_ERROR();
        drawQuad(programRed, "a_position", 0);
        EXPECT_GL_NO_ERROR();
    
        // All pixels must be red
        GLuint x      = 0;
        GLuint y      = 0;
        GLuint width  = getWindowWidth();
        GLuint height = getWindowHeight();
        EXPECT_PIXEL_RECT_EQ(x, y, width, height, GLColor::red);
    
        // Clear to green
        glClearColor(0, 1, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // Draw full screen quad with color red
        glUniform4f(glGetUniformLocation(programRed, "u_plane"), 1, 1, 0, 0);
        EXPECT_GL_NO_ERROR();
        drawQuad(programRed, "a_position", 0);
        EXPECT_GL_NO_ERROR();
    
        // All pixels on the plane y >= -x must be red
        std::vector<GLColor> actualColors(getWindowWidth() * getWindowHeight());
        glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
                     actualColors.data());
        for (int x = 0; x < getWindowWidth(); ++x)
        {
            for (int y = 0; y < getWindowHeight(); ++y)
            {
                const int currentPosition = y * getWindowHeight() + x;
    
                if ((x + y) >= 0)
                {
                    EXPECT_EQ(GLColor::red, actualColors[currentPosition]);
                }
                else
                {
                    EXPECT_EQ(GLColor::green, actualColors[currentPosition]);
                }
            }
        }
    }
    
    // Write to 4 clip distances
    TEST_P(ClipCullDistanceTest, FourClipDistances)
    {
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_clip_cull_distance"));
    
        constexpr char kVS[] = R"(#version 300 es
    #extension GL_EXT_clip_cull_distance : require
    
    in vec2 a_position;
    uniform vec4 u_plane[4];
    
    void main()
    {
        gl_Position = vec4(a_position, 0.0, 1.0);
    
        gl_ClipDistance[0] = dot(gl_Position, u_plane[0]);
        gl_ClipDistance[1] = dot(gl_Position, u_plane[1]);
        gl_ClipDistance[2] = dot(gl_Position, u_plane[2]);
        gl_ClipDistance[3] = dot(gl_Position, u_plane[3]);
    })";
    
        ANGLE_GL_PROGRAM(programRed, kVS, essl3_shaders::fs::Red());
        glUseProgram(programRed);
        ASSERT_GL_NO_ERROR();
    
        // Enable 3 clip distances
        glEnable(GL_CLIP_DISTANCE0_EXT);
        glEnable(GL_CLIP_DISTANCE2_EXT);
        glEnable(GL_CLIP_DISTANCE3_EXT);
        ASSERT_GL_NO_ERROR();
    
        // Disable 1 clip distances
        glDisable(GL_CLIP_DISTANCE1_EXT);
        ASSERT_GL_NO_ERROR();
    
        // Clear to blue
        glClearColor(0, 0, 1, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        constexpr unsigned int kNumVertices                  = 12;
        const std::array<Vector3, kNumVertices> quadVertices = {
            {Vector3(-1.0f, 1.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(1.0f, 1.0f, 0.0f),
             Vector3(1.0f, 1.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(1.0f, -1.0f, 0.0f),
             Vector3(1.0f, -1.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(-1.0f, -1.0f, 0.0f),
             Vector3(-1.0f, -1.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(-1.0f, 1.0f, 0.0f)}};
    
        GLBuffer vertexBuffer;
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * kNumVertices, quadVertices.data(),
                     GL_STATIC_DRAW);
        ASSERT_GL_NO_ERROR();
    
        GLint positionLocation = glGetAttribLocation(programRed, "a_position");
        glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
        ASSERT_GL_NO_ERROR();
    
        glEnableVertexAttribArray(positionLocation);
        ASSERT_GL_NO_ERROR();
    
        // Draw full screen quad and small size triangle with color red
        // y <= 1.0f
        glUniform4f(glGetUniformLocation(programRed, "u_plane[0]"), 0, -1, 0, 1);
        // y >= 0.5f
        glUniform4f(glGetUniformLocation(programRed, "u_plane[1]"), 0, 1, 0, -0.5);
        // y >= 3x-0.5f
        glUniform4f(glGetUniformLocation(programRed, "u_plane[2]"), -3, 1, 0, 0.5);
        // y >= -3x-0.5f
        glUniform4f(glGetUniformLocation(programRed, "u_plane[3]"), 3, 1, 0, 0.5);
        EXPECT_GL_NO_ERROR();
    
        glDrawArrays(GL_TRIANGLES, 0, kNumVertices);
        EXPECT_GL_NO_ERROR();
    
        const int windowWidth   = getWindowWidth();
        const int windowHeight  = getWindowHeight();
        auto checkLeftPlaneFunc = [windowWidth, windowHeight](int x, int y) -> float {
            return (3 * (x - (windowWidth / 2 - 1)) - (windowHeight / 4 + 1) + y);
        };
        auto checkRightPlaneFunc = [windowWidth, windowHeight](int x, int y) -> float {
            return (-3 * (x - (windowWidth / 2)) - (windowHeight / 4 + 1) + y);
        };
    
        // Only pixels in the triangle must be red
        std::vector<GLColor> actualColors(getWindowWidth() * getWindowHeight());
        glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
                     actualColors.data());
        for (int x = 0; x < getWindowWidth(); ++x)
        {
            for (int y = 0; y < getWindowHeight(); ++y)
            {
                // The drawing method of Swiftshader and Native graphic card is different. So the
                // compare function doesn't check the value on the line.
                const int currentPosition = y * getWindowHeight() + x;
    
                if (checkLeftPlaneFunc(x, y) > 0 && checkRightPlaneFunc(x, y) > 0)
                {
                    EXPECT_EQ(GLColor::red, actualColors[currentPosition]);
                }
                else if (checkLeftPlaneFunc(x, y) < 0 || checkRightPlaneFunc(x, y) < 0)
                {
                    EXPECT_EQ(GLColor::blue, actualColors[currentPosition]);
                }
            }
        }
    }
    
    // Write to 4 cull distances
    TEST_P(ClipCullDistanceTest, FourCullDistances)
    {
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_clip_cull_distance"));
    
        // SwiftShader bug: http://anglebug.com/5451
        ANGLE_SKIP_TEST_IF(isSwiftshader());
    
        constexpr char kVS[] = R"(#version 300 es
    #extension GL_EXT_clip_cull_distance : require
    
    uniform vec4 u_plane[4];
    
    in vec2 a_position;
    
    void main()
    {
        gl_Position = vec4(a_position, 0.0, 1.0);
    
        gl_CullDistance[0] = dot(gl_Position, u_plane[0]);
        gl_CullDistance[1] = dot(gl_Position, u_plane[1]);
        gl_CullDistance[2] = dot(gl_Position, u_plane[2]);
        gl_CullDistance[3] = dot(gl_Position, u_plane[3]);
    })";
    
        ANGLE_GL_PROGRAM(programRed, kVS, essl3_shaders::fs::Red());
        glLinkProgram(programRed);
        glUseProgram(programRed);
        ASSERT_GL_NO_ERROR();
    
        // Clear to blue
        glClearColor(0, 0, 1, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        constexpr unsigned int kNumVertices                  = 12;
        const std::array<Vector3, kNumVertices> quadVertices = {
            {Vector3(-1.0f, 1.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(1.0f, 1.0f, 0.0f),
             Vector3(1.0f, 1.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(1.0f, -1.0f, 0.0f),
             Vector3(1.0f, -1.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(-1.0f, -1.0f, 0.0f),
             Vector3(-1.0f, -1.0f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(-1.0f, 1.0f, 0.0f)}};
    
        GLBuffer vertexBuffer;
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * kNumVertices, quadVertices.data(),
                     GL_STATIC_DRAW);
        ASSERT_GL_NO_ERROR();
    
        GLint positionLocation = glGetAttribLocation(programRed, "a_position");
        glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
        ASSERT_GL_NO_ERROR();
    
        glEnableVertexAttribArray(positionLocation);
        ASSERT_GL_NO_ERROR();
    
        // Draw full screen quad and small size triangle with color red
        // y <= 1.0f
        glUniform4f(glGetUniformLocation(programRed, "u_plane[0]"), 0, -1, 0, 1);
        // y >= 0.5f
        glUniform4f(glGetUniformLocation(programRed, "u_plane[1]"), 0, 1, 0, -0.5);
        // y >= 3x-0.5f
        glUniform4f(glGetUniformLocation(programRed, "u_plane[2]"), -3, 1, 0, 0.5);
        // y >= -3x-0.5f
        glUniform4f(glGetUniformLocation(programRed, "u_plane[3]"), 3, 1, 0, 0.5);
        EXPECT_GL_NO_ERROR();
    
        glDrawArrays(GL_TRIANGLES, 0, kNumVertices);
        EXPECT_GL_NO_ERROR();
    
        // Only pixels in the triangle must be red
        std::vector<GLColor> actualColors(getWindowWidth() * getWindowHeight());
        glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
                     actualColors.data());
        for (int x = 0; x < getWindowWidth(); ++x)
        {
            for (int y = 0; y < getWindowHeight(); ++y)
            {
                const int currentPosition = y * getWindowHeight() + x;
    
                if (y > x || y >= -x + getWindowHeight() - 1)
                {
                    EXPECT_EQ(GLColor::red, actualColors[currentPosition]);
                }
                else
                {
                    EXPECT_EQ(GLColor::blue, actualColors[currentPosition]);
                }
            }
        }
    }
    
    // Verify that EXT_clip_cull_distance works with EXT_geometry_shader
    TEST_P(ClipCullDistanceTest, ClipDistanceInteractWithGeometryShader)
    {
        // TODO: http://anglebug.com/5466
        // After implementing EXT_geometry_shader, EXT_clip_cull_distance should be additionally
        // implemented to support the geometry shader. And then, this skip can be removed.
        ANGLE_SKIP_TEST_IF(IsVulkan());
    
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_clip_cull_distance") ||
                           !IsGLExtensionEnabled("GL_EXT_geometry_shader"));
    
        constexpr char kVS[] = R"(#version 310 es
    #extension GL_EXT_clip_cull_distance : require
    #extension GL_EXT_geometry_shader : require
    
    in vec2 a_position;
    uniform vec4 u_plane[4];
    
    void main()
    {
        gl_Position = vec4(a_position, 0.0, 1.0);
    
        gl_ClipDistance[0] = dot(gl_Position, u_plane[0]);
        gl_ClipDistance[1] = dot(gl_Position, u_plane[1]);
        gl_ClipDistance[2] = dot(gl_Position, u_plane[2]);
        gl_ClipDistance[3] = dot(gl_Position, u_plane[3]);
    })";
    
        constexpr char kGS[] = R"(#version 310 es
    #extension GL_EXT_clip_cull_distance : require
    #extension GL_EXT_geometry_shader : require
    
    layout (triangles) in;
    layout (triangle_strip, max_vertices = 3) out;
    
    in gl_PerVertex {
        highp vec4 gl_Position;
        highp float gl_ClipDistance[];
    } gl_in[];
    
    out gl_PerVertex {
        highp vec4 gl_Position;
        highp float gl_ClipDistance[];
    };
    
    uniform vec4 u_plane[4];
    
    void GetNewPosition(int i)
    {
        gl_Position = 2.0f * gl_in[i].gl_Position;
    
        for (int index = 0 ; index < 4 ; index++)
        {
            if (gl_in[i].gl_ClipDistance[index] < 0.0f)
            {
                gl_ClipDistance[index] = dot(gl_Position, u_plane[index]);
            }
        }
        EmitVertex();
    }
    
    void main()
    {
        for (int i = 0 ; i < 3 ; i++)
        {
            GetNewPosition(i);
        }
        EndPrimitive();
    })";
    
        ANGLE_GL_PROGRAM_WITH_GS(programRed, kVS, kGS, essl31_shaders::fs::Red());
        glUseProgram(programRed);
        ASSERT_GL_NO_ERROR();
    
        // Enable 3 clip distances
        glEnable(GL_CLIP_DISTANCE0_EXT);
        glEnable(GL_CLIP_DISTANCE2_EXT);
        glEnable(GL_CLIP_DISTANCE3_EXT);
        ASSERT_GL_NO_ERROR();
    
        // Disable 1 clip distances
        glDisable(GL_CLIP_DISTANCE1_EXT);
        ASSERT_GL_NO_ERROR();
    
        // Clear to blue
        glClearColor(0, 0, 1, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        constexpr unsigned int kNumVertices                  = 12;
        const std::array<Vector3, kNumVertices> quadVertices = {
            {Vector3(-0.5f, 0.5f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(0.5f, 0.5f, 0.0f),
             Vector3(0.5f, 0.5f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(0.5f, -0.5f, 0.0f),
             Vector3(0.5f, -0.5f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(-0.5f, -0.5f, 0.0f),
             Vector3(-0.5f, -0.5f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(-0.5f, 0.5f, 0.0f)}};
    
        GLBuffer vertexBuffer;
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * kNumVertices, quadVertices.data(),
                     GL_STATIC_DRAW);
        ASSERT_GL_NO_ERROR();
    
        GLint positionLocation = glGetAttribLocation(programRed, "a_position");
        glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
        ASSERT_GL_NO_ERROR();
    
        glEnableVertexAttribArray(positionLocation);
        ASSERT_GL_NO_ERROR();
    
        // Draw full screen quad and small size triangle with color red
        // y <= 1.0f
        glUniform4f(glGetUniformLocation(programRed, "u_plane[0]"), 0, -1, 0, 1);
        // y >= 0.5f
        glUniform4f(glGetUniformLocation(programRed, "u_plane[1]"), 0, 1, 0, -0.5);
        // y >= 3x-0.5f
        glUniform4f(glGetUniformLocation(programRed, "u_plane[2]"), -3, 1, 0, 0.5);
        // y >= -3x-0.5f
        glUniform4f(glGetUniformLocation(programRed, "u_plane[3]"), 3, 1, 0, 0.5);
        EXPECT_GL_NO_ERROR();
    
        glDrawArrays(GL_TRIANGLES, 0, kNumVertices);
        EXPECT_GL_NO_ERROR();
    
        const int windowWidth   = getWindowWidth();
        const int windowHeight  = getWindowHeight();
        auto checkLeftPlaneFunc = [windowWidth, windowHeight](int x, int y) -> float {
            return (3 * (x - (windowWidth / 2 - 1)) - (windowHeight / 4 + 1) + y);
        };
        auto checkRightPlaneFunc = [windowWidth, windowHeight](int x, int y) -> float {
            return (-3 * (x - (windowWidth / 2)) - (windowHeight / 4 + 1) + y);
        };
    
        // Only pixels in the triangle must be red
        std::vector<GLColor> actualColors(getWindowWidth() * getWindowHeight());
        glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
                     actualColors.data());
        for (int x = 0; x < getWindowWidth(); ++x)
        {
            for (int y = 0; y < getWindowHeight(); ++y)
            {
                // The drawing method of Swiftshader and Native graphic card is different. So the
                // compare function doesn't check the value on the line.
                const int currentPosition = y * getWindowHeight() + x;
    
                if (checkLeftPlaneFunc(x, y) > 0 && checkRightPlaneFunc(x, y) > 0)
                {
                    EXPECT_EQ(GLColor::red, actualColors[currentPosition]);
                }
                else if (checkLeftPlaneFunc(x, y) < 0 || checkRightPlaneFunc(x, y) < 0)
                {
                    EXPECT_EQ(GLColor::blue, actualColors[currentPosition]);
                }
            }
        }
    }
    
    // Verify that EXT_clip_cull_distance works with EXT_geometry_shader
    TEST_P(ClipCullDistanceTest, CullDistanceInteractWithGeometryShader)
    {
        // TODO: http://anglebug.com/5466
        // After implementing EXT_geometry_shader, EXT_clip_cull_distance should be additionally
        // implemented to support the geometry shader. And then, this skip can be removed.
        ANGLE_SKIP_TEST_IF(IsVulkan());
    
        ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_clip_cull_distance") ||
                           !IsGLExtensionEnabled("GL_EXT_geometry_shader"));
    
        constexpr char kVS[] = R"(#version 310 es
    #extension GL_EXT_clip_cull_distance : require
    #extension GL_EXT_geometry_shader : require
    
    in vec2 a_position;
    uniform vec4 u_plane[4];
    
    void main()
    {
        gl_Position = vec4(a_position, 0.0, 1.0);
    
        gl_CullDistance[0] = dot(gl_Position, u_plane[0]);
        gl_CullDistance[1] = dot(gl_Position, u_plane[1]);
        gl_CullDistance[2] = dot(gl_Position, u_plane[2]);
        gl_CullDistance[3] = dot(gl_Position, u_plane[3]);
    })";
    
        constexpr char kGS[] = R"(#version 310 es
    #extension GL_EXT_clip_cull_distance : require
    #extension GL_EXT_geometry_shader : require
    
    layout (triangles) in;
    layout (triangle_strip, max_vertices = 3) out;
    
    in gl_PerVertex {
        highp vec4 gl_Position;
        highp float gl_CullDistance[];
    } gl_in[];
    
    out gl_PerVertex {
        highp vec4 gl_Position;
        highp float gl_CullDistance[];
    };
    
    uniform vec4 u_plane[4];
    
    void GetNewPosition(int i)
    {
        gl_Position = 2.0f * gl_in[i].gl_Position;
    
        for (int index = 0 ; index < 4 ; index++)
        {
            if (gl_in[i].gl_CullDistance[index] < 0.0f)
            {
                gl_CullDistance[index] = dot(gl_Position, u_plane[index]);
            }
        }
        EmitVertex();
    }
    
    void main()
    {
        for (int i = 0 ; i < 3 ; i++)
        {
            GetNewPosition(i);
        }
        EndPrimitive();
    })";
    
        ANGLE_GL_PROGRAM_WITH_GS(programRed, kVS, kGS, essl31_shaders::fs::Red());
        glUseProgram(programRed);
        ASSERT_GL_NO_ERROR();
    
        // Clear to blue
        glClearColor(0, 0, 1, 1);
        glClear(GL_COLOR_BUFFER_BIT);
    
        constexpr unsigned int kNumVertices                  = 12;
        const std::array<Vector3, kNumVertices> quadVertices = {
            {Vector3(-0.5f, 0.5f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(0.5f, 0.5f, 0.0f),
             Vector3(0.5f, 0.5f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(0.5f, -0.5f, 0.0f),
             Vector3(0.5f, -0.5f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(-0.5f, -0.5f, 0.0f),
             Vector3(-0.5f, -0.5f, 0.0f), Vector3(0.0f, 0.0f, 0.0f), Vector3(-0.5f, 0.5f, 0.0f)}};
    
        GLBuffer vertexBuffer;
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * kNumVertices, quadVertices.data(),
                     GL_STATIC_DRAW);
        ASSERT_GL_NO_ERROR();
    
        GLint positionLocation = glGetAttribLocation(programRed, "a_position");
        glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
        ASSERT_GL_NO_ERROR();
    
        glEnableVertexAttribArray(positionLocation);
        ASSERT_GL_NO_ERROR();
    
        // Draw full screen quad and small size triangle with color red
        // y <= 1.0f
        glUniform4f(glGetUniformLocation(programRed, "u_plane[0]"), 0, -1, 0, 1);
        // y >= 0.5f
        glUniform4f(glGetUniformLocation(programRed, "u_plane[1]"), 0, 1, 0, -0.5);
        // y >= 3x-0.5f
        glUniform4f(glGetUniformLocation(programRed, "u_plane[2]"), -3, 1, 0, 0.5);
        // y >= -3x-0.5f
        glUniform4f(glGetUniformLocation(programRed, "u_plane[3]"), 3, 1, 0, 0.5);
        EXPECT_GL_NO_ERROR();
    
        glDrawArrays(GL_TRIANGLES, 0, kNumVertices);
        EXPECT_GL_NO_ERROR();
    
        // Only pixels in the triangle must be red
        std::vector<GLColor> actualColors(getWindowWidth() * getWindowHeight());
        glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
                     actualColors.data());
        for (int x = 0; x < getWindowWidth(); ++x)
        {
            for (int y = 0; y < getWindowHeight(); ++y)
            {
                const int currentPosition = y * getWindowHeight() + x;
    
                if (y > x || y >= -x + getWindowHeight() - 1)
                {
                    EXPECT_EQ(GLColor::red, actualColors[currentPosition]);
                }
                else
                {
                    EXPECT_EQ(GLColor::blue, actualColors[currentPosition]);
                }
            }
        }
    }
    
    // Use this to select which configurations (e.g. which renderer, which GLES major version) these
    // tests should be run against.
    ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(ClipDistanceTest);
    
    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ClipCullDistanceTest);
    ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(ClipCullDistanceTest);