Commit 9229b805 by Mohan Maiya Committed by Commit Bot

Vulkan: Add the tests for EXT_clip_cull_distance

Before supporting EXT_clip_cull_distance extension, some tests are added to validate the implementation of EXT_clip_cull_distance extension. These tests are implemented based on the tests for APPLE_clip_distance Bug: angleproject:5458 Tests: angle_end2end_tests --gtest_filter=Clip*DistanceTest* angle_unittests --gtest_filter=*Clip*Distance* Change-Id: I018c72ae8f0aff616c9d2600e63246e9609cf3de Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2585986 Commit-Queue: Mohan Maiya <m.maiya@samsung.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org>
parent e8c5525c
//
// 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.
//
// EXT_clip_cull_distance_test.cpp:
// Test for EXT_clip_cull_distance
//
#include "tests/test_utils/ShaderExtensionTest.h"
namespace
{
const char EXTPragma[] = "#extension GL_EXT_clip_cull_distance : require\n";
// Shader using gl_ClipDistance and gl_CullDistance
const char VertexShaderCompileSucceeds1[] =
R"(
uniform vec4 uPlane;
in vec4 aPosition;
void main()
{
gl_Position = aPosition;
gl_ClipDistance[1] = dot(aPosition, uPlane);
gl_CullDistance[1] = dot(aPosition, uPlane);
})";
// Shader redeclares gl_ClipDistance and gl_CullDistance
const char VertexShaderCompileSucceeds2[] =
R"(
uniform vec4 uPlane;
in vec4 aPosition;
out highp float gl_ClipDistance[4];
out highp float gl_CullDistance[4];
void main()
{
gl_Position = aPosition;
gl_ClipDistance[gl_MaxClipDistances - 6 + 1] = dot(aPosition, uPlane);
gl_ClipDistance[gl_MaxClipDistances - int(aPosition.x)] = dot(aPosition, uPlane);
gl_CullDistance[gl_MaxCullDistances - 6 + 1] = dot(aPosition, uPlane);
gl_CullDistance[gl_MaxCullDistances - int(aPosition.x)] = dot(aPosition, uPlane);
})";
// Shader using gl_ClipDistance and gl_CullDistance
// But, the sum of the sizes is greater than gl_MaxCombinedClipAndCullDistances
const char VertexShaderCompileFails1[] =
R"(
uniform vec4 uPlane;
in vec4 aPosition;
void main()
{
gl_Position = aPosition;
gl_ClipDistance[5] = dot(aPosition, uPlane);
gl_CullDistance[4] = dot(aPosition, uPlane);
})";
// Shader redeclares gl_ClipDistance and gl_CullDistance
// But, the sum of the sizes is greater than gl_MaxCombinedClipAndCullDistances
const char VertexShaderCompileFails2[] =
R"(
uniform vec4 uPlane;
in vec4 aPosition;
out highp float gl_ClipDistance[5];
out highp float gl_CullDistance[4];
void main()
{
gl_Position = aPosition;
gl_ClipDistance[gl_MaxClipDistances - 6 + 1] = dot(aPosition, uPlane);
gl_ClipDistance[gl_MaxClipDistances - int(aPosition.x)] = dot(aPosition, uPlane);
gl_CullDistance[gl_MaxCullDistances - 6 + 1] = dot(aPosition, uPlane);
gl_CullDistance[gl_MaxCullDistances - int(aPosition.x)] = dot(aPosition, uPlane);
})";
// Shader redeclares gl_ClipDistance
// But, the array size is greater than gl_MaxClipDistances
const char VertexShaderCompileFails3[] =
R"(
uniform vec4 uPlane;
in vec4 aPosition;
out highp float gl_ClipDistance[gl_MaxClipDistances + 1];
void main()
{
gl_Position = aPosition;
gl_ClipDistance[gl_MaxClipDistances - 6 + 1] = dot(aPosition, uPlane);
gl_ClipDistance[gl_MaxClipDistances - int(aPosition.x)] = dot(aPosition, uPlane);
})";
// Access gl_CullDistance with integral constant index
// But, the index is greater than gl_MaxCullDistances
const char VertexShaderCompileFails4[] =
R"(
uniform vec4 uPlane;
in vec4 aPosition;
void main()
{
gl_Position = aPosition;
gl_CullDistance[gl_MaxCullDistances] = dot(aPosition, uPlane);
})";
// Shader using gl_ClipDistance and gl_CullDistance
const char FragmentShaderCompileSucceeds1[] =
R"(
out highp vec4 fragColor;
void main()
{
fragColor = vec4(gl_ClipDistance[0], gl_CullDistance[0], 0, 1);
})";
// Shader redeclares gl_ClipDistance and gl_CullDistance
const char FragmentShaderCompileSucceeds2[] =
R"(
in highp float gl_ClipDistance[4];
in highp float gl_CullDistance[4];
in highp vec4 aPosition;
out highp vec4 fragColor;
void main()
{
fragColor.x = gl_ClipDistance[gl_MaxClipDistances - 6 + 1];
fragColor.y = gl_ClipDistance[gl_MaxClipDistances - int(aPosition.x)];
fragColor.z = gl_CullDistance[gl_MaxCullDistances - 6 + 1];
fragColor.w = gl_CullDistance[gl_MaxCullDistances - int(aPosition.x)];
})";
// Shader using gl_ClipDistance and gl_CullDistance
// But, the sum of the sizes is greater than gl_MaxCombinedClipAndCullDistances
const char FragmentShaderCompileFails1[] =
R"(
out highp vec4 fragColor;
void main()
{
fragColor = vec4(gl_ClipDistance[4], gl_CullDistance[5], 0, 1);
})";
// Shader redeclares gl_ClipDistance and gl_CullDistance
// But, the sum of the sizes is greater than gl_MaxCombinedClipAndCullDistances
const char FragmentShaderCompileFails2[] =
R"(
in highp float gl_ClipDistance[5];
in highp float gl_CullDistance[4];
in highp vec4 aPosition;
out highp vec4 fragColor;
void main()
{
fragColor.x = gl_ClipDistance[gl_MaxClipDistances - 6 + 1];
fragColor.y = gl_ClipDistance[gl_MaxClipDistances - int(aPosition.x)];
fragColor.z = gl_CullDistance[gl_MaxCullDistances - 6 + 1];
fragColor.w = gl_CullDistance[gl_MaxCullDistances - int(aPosition.x)];
})";
// In fragment shader, writing to gl_ClipDistance should be denied.
const char FragmentShaderCompileFails3[] =
R"(
out highp vec4 fragColor;
void main()
{
gl_ClipDistance[0] = 0.0f;
fragColor = vec4(1, gl_ClipDistance[0], 0, 1);
})";
// In fragment shader, writing to gl_CullDistance should be denied even if redeclaring it with the
// array size
const char FragmentShaderCompileFails4[] =
R"(
out highp vec4 fragColor;
in highp float gl_CullDistance[1];
void main()
{
gl_CullDistance[0] = 0.0f;
fragColor = vec4(1, gl_CullDistance[0], 0, 1);
})";
// Accessing to gl_Clip/CullDistance with non-const index should be denied if the size of
// gl_Clip/CullDistance is not decided.
const char FragmentShaderCompileFails5[] =
R"(
out highp vec4 fragColor;
void main()
{
medium float color[3];
for(int i = 0 ; i < 3 ; i++)
{
color[i] = gl_CullDistance[i];
}
fragColor = vec4(color[0], color[1], color[2], 1.0f);
})";
class EXTClipCullDistanceTest : public sh::ShaderExtensionTest
{
public:
void InitializeCompiler(ShShaderOutput shaderOutputType, GLenum shaderType)
{
DestroyCompiler();
mCompiler = sh::ConstructCompiler(shaderType, testing::get<0>(GetParam()), shaderOutputType,
&mResources);
ASSERT_TRUE(mCompiler != nullptr) << "Compiler could not be constructed.";
}
testing::AssertionResult TestShaderCompile(const char *pragma)
{
const char *shaderStrings[] = {testing::get<1>(GetParam()), pragma,
testing::get<2>(GetParam())};
bool success = sh::Compile(mCompiler, shaderStrings, 3, SH_VARIABLES | SH_OBJECT_CODE);
if (success)
{
return ::testing::AssertionSuccess() << "Compilation success";
}
return ::testing::AssertionFailure() << sh::GetInfoLog(mCompiler);
}
void SetExtensionEnable(bool enable)
{
// GL_APPLE_clip_distance is implicitly enabled when GL_EXT_clip_cull_distance is enabled
#if defined(ANGLE_ENABLE_VULKAN)
mResources.APPLE_clip_distance = enable;
#endif
mResources.EXT_clip_cull_distance = enable;
}
};
class EXTClipCullDistanceForVertexShaderTest : public EXTClipCullDistanceTest
{
public:
void InitializeCompiler() { InitializeCompiler(SH_GLSL_450_CORE_OUTPUT); }
void InitializeCompiler(ShShaderOutput shaderOutputType)
{
EXTClipCullDistanceTest::InitializeCompiler(shaderOutputType, GL_VERTEX_SHADER);
}
};
class EXTClipCullDistanceForFragmentShaderTest : public EXTClipCullDistanceTest
{
public:
void InitializeCompiler() { InitializeCompiler(SH_GLSL_450_CORE_OUTPUT); }
void InitializeCompiler(ShShaderOutput shaderOutputType)
{
EXTClipCullDistanceTest::InitializeCompiler(shaderOutputType, GL_FRAGMENT_SHADER);
}
};
// Extension flag is required to compile properly. Expect failure when it is
// not present.
TEST_P(EXTClipCullDistanceForVertexShaderTest, CompileFailsWithoutExtension)
{
SetExtensionEnable(false);
InitializeCompiler();
EXPECT_FALSE(TestShaderCompile(EXTPragma));
}
// Extension directive is required to compile properly. Expect failure when
// it is not present.
TEST_P(EXTClipCullDistanceForVertexShaderTest, CompileFailsWithExtensionWithoutPragma)
{
SetExtensionEnable(true);
InitializeCompiler();
EXPECT_FALSE(TestShaderCompile(""));
}
#if defined(ANGLE_ENABLE_VULKAN)
// With extension flag and extension directive, compiling using TranslatorVulkan succeeds.
TEST_P(EXTClipCullDistanceForVertexShaderTest, CompileSucceedsVulkan)
{
SetExtensionEnable(true);
mResources.MaxClipDistances = 8;
mResources.MaxCullDistances = 8;
mResources.MaxCombinedClipAndCullDistances = 8;
InitializeCompiler(SH_GLSL_VULKAN_OUTPUT);
EXPECT_TRUE(TestShaderCompile(EXTPragma));
EXPECT_FALSE(TestShaderCompile(""));
EXPECT_TRUE(TestShaderCompile(EXTPragma));
}
#endif
// Extension flag is required to compile properly. Expect failure when it is
// not present.
TEST_P(EXTClipCullDistanceForFragmentShaderTest, CompileFailsWithoutExtension)
{
SetExtensionEnable(false);
InitializeCompiler();
EXPECT_FALSE(TestShaderCompile(EXTPragma));
}
// Extension directive is required to compile properly. Expect failure when
// it is not present.
TEST_P(EXTClipCullDistanceForFragmentShaderTest, CompileFailsWithExtensionWithoutPragma)
{
SetExtensionEnable(true);
InitializeCompiler();
EXPECT_FALSE(TestShaderCompile(""));
}
#if defined(ANGLE_ENABLE_VULKAN)
// With extension flag and extension directive, compiling using TranslatorVulkan succeeds.
TEST_P(EXTClipCullDistanceForFragmentShaderTest, CompileSucceedsVulkan)
{
SetExtensionEnable(true);
mResources.MaxClipDistances = 8;
mResources.MaxCullDistances = 8;
mResources.MaxCombinedClipAndCullDistances = 8;
InitializeCompiler(SH_GLSL_VULKAN_OUTPUT);
EXPECT_TRUE(TestShaderCompile(EXTPragma));
EXPECT_FALSE(TestShaderCompile(""));
EXPECT_TRUE(TestShaderCompile(EXTPragma));
}
#endif
class EXTClipCullDistanceForVertexShaderCompileFailureTest
: public EXTClipCullDistanceForVertexShaderTest
{};
class EXTClipCullDistanceForFragmentShaderCompileFailureTest
: public EXTClipCullDistanceForFragmentShaderTest
{};
#if defined(ANGLE_ENABLE_VULKAN)
TEST_P(EXTClipCullDistanceForVertexShaderCompileFailureTest, CompileFails)
{
SetExtensionEnable(true);
mResources.MaxClipDistances = 8;
mResources.MaxCullDistances = 8;
mResources.MaxCombinedClipAndCullDistances = 8;
InitializeCompiler(SH_GLSL_VULKAN_OUTPUT);
EXPECT_FALSE(TestShaderCompile(EXTPragma));
}
TEST_P(EXTClipCullDistanceForFragmentShaderCompileFailureTest, CompileFails)
{
SetExtensionEnable(true);
mResources.MaxClipDistances = 8;
mResources.MaxCullDistances = 8;
mResources.MaxCombinedClipAndCullDistances = 8;
InitializeCompiler(SH_GLSL_VULKAN_OUTPUT);
EXPECT_FALSE(TestShaderCompile(EXTPragma));
}
#endif
INSTANTIATE_TEST_SUITE_P(CorrectESSL300Shaders,
EXTClipCullDistanceForVertexShaderTest,
Combine(Values(SH_GLES3_SPEC),
Values(sh::ESSLVersion300),
Values(VertexShaderCompileSucceeds1,
VertexShaderCompileSucceeds2)));
INSTANTIATE_TEST_SUITE_P(CorrectESSL300Shaders,
EXTClipCullDistanceForVertexShaderCompileFailureTest,
Combine(Values(SH_GLES3_SPEC),
Values(sh::ESSLVersion300),
Values(VertexShaderCompileFails1,
VertexShaderCompileFails2,
VertexShaderCompileFails3,
VertexShaderCompileFails4)));
INSTANTIATE_TEST_SUITE_P(IncorrectESSL300Shaders,
EXTClipCullDistanceForFragmentShaderTest,
Combine(Values(SH_GLES3_SPEC),
Values(sh::ESSLVersion300),
Values(FragmentShaderCompileSucceeds1,
FragmentShaderCompileSucceeds2)));
INSTANTIATE_TEST_SUITE_P(IncorrectESSL300Shaders,
EXTClipCullDistanceForFragmentShaderCompileFailureTest,
Combine(Values(SH_GLES3_SPEC),
Values(sh::ESSLVersion300),
Values(FragmentShaderCompileFails1,
FragmentShaderCompileFails2,
FragmentShaderCompileFails3,
FragmentShaderCompileFails4,
FragmentShaderCompileFails5)));
} // anonymous namespace
...@@ -286,7 +286,7 @@ void main() ...@@ -286,7 +286,7 @@ void main()
} }
} }
// Redeclare gl_ClipDistance in shader with explitcit size, also use it in a global function // Redeclare gl_ClipDistance in shader with explicit size, also use it in a global function
// outside main() // outside main()
TEST_P(ClipDistanceTest, ThreeClipDistancesRedeclared) TEST_P(ClipDistanceTest, ThreeClipDistancesRedeclared)
{ {
...@@ -378,6 +378,1000 @@ void main() ...@@ -378,6 +378,1000 @@ void main()
} }
} }
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 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against. // tests should be run against.
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(ClipDistanceTest); ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(ClipDistanceTest);
ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(ClipCullDistanceTest);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment