// // 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 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 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 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 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 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 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 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 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 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 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 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);