| // |
| // Copyright 2017 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. |
| // |
| |
| // GeometryShaderTest.cpp : Tests of the implementation of geometry shader |
| |
| #include "test_utils/ANGLETest.h" |
| #include "test_utils/gl_raii.h" |
| |
| using namespace angle; |
| |
| namespace |
| { |
| |
| class GeometryShaderTest : public ANGLETest |
| { |
| protected: |
| GeometryShaderTest() |
| { |
| setWindowWidth(64); |
| setWindowHeight(32); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| setConfigDepthBits(24); |
| setConfigStencilBits(8); |
| } |
| |
| static std::string CreateEmptyGeometryShader(const std::string &inputPrimitive, |
| const std::string &outputPrimitive, |
| int invocations, |
| int maxVertices) |
| { |
| std::ostringstream ostream; |
| ostream << "#version 310 es\n" |
| "#extension GL_EXT_geometry_shader : require\n"; |
| if (!inputPrimitive.empty()) |
| { |
| ostream << "layout (" << inputPrimitive << ") in;\n"; |
| } |
| if (!outputPrimitive.empty()) |
| { |
| ostream << "layout (" << outputPrimitive << ") out;\n"; |
| } |
| if (invocations > 0) |
| { |
| ostream << "layout (invocations = " << invocations << ") in;\n"; |
| } |
| if (maxVertices >= 0) |
| { |
| ostream << "layout (max_vertices = " << maxVertices << ") out;\n"; |
| } |
| ostream << "void main()\n" |
| "{\n" |
| "}"; |
| return ostream.str(); |
| } |
| |
| void setupLayeredFramebuffer(GLuint framebuffer, |
| GLuint color0, |
| GLuint color1, |
| GLuint depthStencil, |
| GLenum colorTarget, |
| const GLColor &color0InitialColor, |
| const GLColor &color1InitialColor, |
| float depthInitialValue, |
| GLint stencilInitialValue); |
| void setupLayeredFramebufferProgram(GLProgram *program); |
| void verifyLayeredFramebufferColor(GLuint colorTexture, |
| GLenum colorTarget, |
| const GLColor expected[], |
| GLsizei layerCount); |
| void verifyLayeredFramebufferDepthStencil(GLuint depthStencilTexture, |
| const float expectedDepth[], |
| const GLint expectedStencil[], |
| GLsizei layerCount); |
| |
| void layeredFramebufferClearTest(GLenum colorTarget); |
| void layeredFramebufferPreRenderClearTest(GLenum colorTarget); |
| void layeredFramebufferMidRenderClearTest(GLenum colorTarget); |
| |
| static constexpr GLsizei kWidth = 16; |
| static constexpr GLsizei kHeight = 16; |
| static constexpr GLsizei kColor0Layers = 4; |
| static constexpr GLsizei kColor1Layers = 3; |
| static constexpr GLsizei kDepthStencilLayers = 5; |
| static constexpr GLsizei kFramebufferLayers = |
| std::min({kColor0Layers, kColor1Layers, kDepthStencilLayers}); |
| }; |
| |
| class GeometryShaderTestES3 : public ANGLETest |
| {}; |
| |
| // Verify that Geometry Shader cannot be created in an OpenGL ES 3.0 context. |
| TEST_P(GeometryShaderTestES3, CreateGeometryShaderInES3) |
| { |
| EXPECT_TRUE(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| GLuint geometryShader = glCreateShader(GL_GEOMETRY_SHADER_EXT); |
| EXPECT_EQ(0u, geometryShader); |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); |
| } |
| |
| // Verify that Geometry Shader can be created and attached to a program. |
| TEST_P(GeometryShaderTest, CreateAndAttachGeometryShader) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| constexpr char kGS[] = R"(#version 310 es |
| #extension GL_EXT_geometry_shader : require |
| layout (invocations = 3, triangles) in; |
| layout (triangle_strip, max_vertices = 3) out; |
| in vec4 texcoord[]; |
| out vec4 o_texcoord; |
| void main() |
| { |
| int n; |
| for (n = 0; n < gl_in.length(); n++) |
| { |
| gl_Position = gl_in[n].gl_Position; |
| gl_Layer = gl_InvocationID; |
| o_texcoord = texcoord[n]; |
| EmitVertex(); |
| } |
| EndPrimitive(); |
| })"; |
| |
| GLuint geometryShader = CompileShader(GL_GEOMETRY_SHADER_EXT, kGS); |
| |
| EXPECT_NE(0u, geometryShader); |
| |
| GLuint programID = glCreateProgram(); |
| glAttachShader(programID, geometryShader); |
| |
| glDetachShader(programID, geometryShader); |
| glDeleteShader(geometryShader); |
| glDeleteProgram(programID); |
| |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Verify that all the implementation dependent geometry shader related resource limits meet the |
| // requirement of GL_EXT_geometry_shader SPEC. |
| TEST_P(GeometryShaderTest, GeometryShaderImplementationDependentLimits) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| const std::map<GLenum, int> limits = {{GL_MAX_FRAMEBUFFER_LAYERS_EXT, 256}, |
| {GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT, 1024}, |
| {GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT, 12}, |
| {GL_MAX_GEOMETRY_INPUT_COMPONENTS_EXT, 64}, |
| {GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_EXT, 64}, |
| {GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT, 256}, |
| {GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT, 1024}, |
| {GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT, 16}, |
| {GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT, 0}, |
| {GL_MAX_GEOMETRY_ATOMIC_COUNTERS_EXT, 0}, |
| {GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT, 0}, |
| {GL_MAX_GEOMETRY_IMAGE_UNIFORMS_EXT, 0}, |
| {GL_MAX_GEOMETRY_SHADER_INVOCATIONS_EXT, 32}}; |
| |
| GLint value; |
| for (const auto &limit : limits) |
| { |
| value = 0; |
| glGetIntegerv(limit.first, &value); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_GE(value, limit.second); |
| } |
| |
| value = 0; |
| glGetIntegerv(GL_LAYER_PROVOKING_VERTEX_EXT, &value); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_TRUE(value == GL_FIRST_VERTEX_CONVENTION_EXT || value == GL_LAST_VERTEX_CONVENTION_EXT || |
| value == GL_UNDEFINED_VERTEX_EXT); |
| } |
| |
| // Verify that all the combined resource limits meet the requirement of GL_EXT_geometry_shader SPEC. |
| TEST_P(GeometryShaderTest, CombinedResourceLimits) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| // See http://anglebug.com/2261. |
| ANGLE_SKIP_TEST_IF(IsAndroid()); |
| |
| const std::map<GLenum, int> limits = {{GL_MAX_UNIFORM_BUFFER_BINDINGS, 48}, |
| {GL_MAX_COMBINED_UNIFORM_BLOCKS, 36}, |
| {GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, 64}}; |
| |
| GLint value; |
| for (const auto &limit : limits) |
| { |
| value = 0; |
| glGetIntegerv(limit.first, &value); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_GE(value, limit.second); |
| } |
| } |
| |
| // Verify that linking a program with an uncompiled geometry shader causes a link failure. |
| TEST_P(GeometryShaderTest, LinkWithUncompiledGeometryShader) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| GLuint vertexShader = CompileShader(GL_VERTEX_SHADER, essl31_shaders::vs::Simple()); |
| GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, essl31_shaders::fs::Red()); |
| ASSERT_NE(0u, vertexShader); |
| ASSERT_NE(0u, fragmentShader); |
| |
| GLuint geometryShader = glCreateShader(GL_GEOMETRY_SHADER_EXT); |
| |
| GLuint program = glCreateProgram(); |
| glAttachShader(program, vertexShader); |
| glAttachShader(program, fragmentShader); |
| glAttachShader(program, geometryShader); |
| glDeleteShader(vertexShader); |
| glDeleteShader(fragmentShader); |
| glDeleteShader(geometryShader); |
| |
| glLinkProgram(program); |
| |
| GLint linkStatus; |
| glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); |
| EXPECT_EQ(0, linkStatus); |
| |
| glDeleteProgram(program); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Verify that linking a program with geometry shader whose version is different from other shaders |
| // in this program causes a link error. |
| TEST_P(GeometryShaderTest, LinkWhenShaderVersionMismatch) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| const std::string &emptyGeometryShader = CreateEmptyGeometryShader("points", "points", 2, 1); |
| |
| GLuint program = CompileProgramWithGS(essl3_shaders::vs::Simple(), emptyGeometryShader.c_str(), |
| essl3_shaders::fs::Red()); |
| EXPECT_EQ(0u, program); |
| } |
| |
| // Verify that linking a program with geometry shader that lacks input primitive, |
| // output primitive, or declaration on 'max_vertices' causes a link failure. |
| TEST_P(GeometryShaderTest, LinkValidationOnGeometryShaderLayouts) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| const std::string gsWithoutInputPrimitive = CreateEmptyGeometryShader("", "points", 2, 1); |
| const std::string gsWithoutOutputPrimitive = CreateEmptyGeometryShader("points", "", 2, 1); |
| const std::string gsWithoutInvocations = CreateEmptyGeometryShader("points", "points", -1, 1); |
| const std::string gsWithoutMaxVertices = CreateEmptyGeometryShader("points", "points", 2, -1); |
| |
| // Linking a program with a geometry shader that only lacks 'invocations' should not cause a |
| // link failure. |
| GLuint program = CompileProgramWithGS(essl31_shaders::vs::Simple(), |
| gsWithoutInvocations.c_str(), essl31_shaders::fs::Red()); |
| EXPECT_NE(0u, program); |
| |
| glDeleteProgram(program); |
| |
| // Linking a program with a geometry shader that lacks input primitive, output primitive or |
| // 'max_vertices' causes a link failure. |
| program = CompileProgramWithGS(essl31_shaders::vs::Simple(), gsWithoutInputPrimitive.c_str(), |
| essl31_shaders::fs::Red()); |
| EXPECT_EQ(0u, program); |
| |
| program = CompileProgramWithGS(essl31_shaders::vs::Simple(), gsWithoutOutputPrimitive.c_str(), |
| essl31_shaders::fs::Red()); |
| EXPECT_EQ(0u, program); |
| |
| program = CompileProgramWithGS(essl31_shaders::vs::Simple(), gsWithoutMaxVertices.c_str(), |
| essl31_shaders::fs::Red()); |
| EXPECT_EQ(0u, program); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| // Verify that an link error occurs when the vertex shader has an array output and there is a |
| // geometry shader in the program. |
| TEST_P(GeometryShaderTest, VertexShaderArrayOutput) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| in vec4 vertex_in; |
| out vec4 vertex_out[3]; |
| void main() |
| { |
| gl_Position = vertex_in; |
| vertex_out[0] = vec4(1.0, 0.0, 0.0, 1.0); |
| vertex_out[1] = vec4(0.0, 1.0, 0.0, 1.0); |
| vertex_out[2] = vec4(0.0, 0.0, 1.0, 1.0); |
| })"; |
| |
| constexpr char kGS[] = R"(#version 310 es |
| #extension GL_EXT_geometry_shader : require |
| layout (invocations = 3, triangles) in; |
| layout (points, max_vertices = 3) out; |
| in vec4 vertex_out[]; |
| out vec4 geometry_color; |
| void main() |
| { |
| gl_Position = gl_in[0].gl_Position; |
| geometry_color = vertex_out[0]; |
| EmitVertex(); |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| precision mediump float; |
| in vec4 geometry_color; |
| layout (location = 0) out vec4 output_color; |
| void main() |
| { |
| output_color = geometry_color; |
| })"; |
| |
| GLuint program = CompileProgramWithGS(kVS, kGS, kFS); |
| EXPECT_EQ(0u, program); |
| |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Verify that an link error occurs when the definition of a unform in fragment shader is different |
| // from those in a geometry shader. |
| TEST_P(GeometryShaderTest, UniformMismatchBetweenGeometryAndFragmentShader) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| uniform highp vec4 uniform_value_vert; |
| in vec4 vertex_in; |
| out vec4 vertex_out; |
| void main() |
| { |
| gl_Position = vertex_in; |
| vertex_out = uniform_value_vert; |
| })"; |
| |
| constexpr char kGS[] = R"(#version 310 es |
| #extension GL_EXT_geometry_shader : require |
| uniform vec4 uniform_value; |
| layout (invocations = 3, triangles) in; |
| layout (points, max_vertices = 3) out; |
| in vec4 vertex_out[]; |
| out vec4 geometry_color; |
| void main() |
| { |
| gl_Position = gl_in[0].gl_Position; |
| geometry_color = vertex_out[0] + uniform_value; |
| EmitVertex(); |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| precision highp float; |
| uniform float uniform_value; |
| in vec4 geometry_color; |
| layout (location = 0) out vec4 output_color; |
| void main() |
| { |
| output_color = vec4(geometry_color.rgb, uniform_value); |
| })"; |
| |
| GLuint program = CompileProgramWithGS(kVS, kGS, kFS); |
| EXPECT_EQ(0u, program); |
| |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Verify that an link error occurs when the number of uniform blocks in a geometry shader exceeds |
| // MAX_GEOMETRY_UNIFORM_BLOCKS_EXT. |
| TEST_P(GeometryShaderTest, TooManyUniformBlocks) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| GLint maxGeometryUniformBlocks = 0; |
| glGetIntegerv(GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT, &maxGeometryUniformBlocks); |
| |
| GLint numUniformBlocks = maxGeometryUniformBlocks + 1; |
| std::ostringstream stream; |
| stream << "#version 310 es\n" |
| "#extension GL_EXT_geometry_shader : require\n" |
| "uniform ubo\n" |
| "{\n" |
| " vec4 value1;\n" |
| "} block0[" |
| << numUniformBlocks |
| << "];\n" |
| "layout (triangles) in;\n" |
| "layout (points, max_vertices = 1) out;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = gl_in[0].gl_Position;\n"; |
| |
| for (GLint i = 0; i < numUniformBlocks; ++i) |
| { |
| stream << " gl_Position += block0[" << i << "].value1;\n"; |
| } |
| stream << " EmitVertex();\n" |
| "}\n"; |
| |
| GLuint program = CompileProgramWithGS(essl31_shaders::vs::Simple(), stream.str().c_str(), |
| essl31_shaders::fs::Red()); |
| EXPECT_EQ(0u, program); |
| |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Verify that an link error occurs when the number of shader storage blocks in a geometry shader |
| // exceeds MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT. |
| TEST_P(GeometryShaderTest, TooManyShaderStorageBlocks) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| GLint maxGeometryShaderStorageBlocks = 0; |
| glGetIntegerv(GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT, &maxGeometryShaderStorageBlocks); |
| |
| GLint numSSBOs = maxGeometryShaderStorageBlocks + 1; |
| std::ostringstream stream; |
| stream << "#version 310 es\n" |
| "#extension GL_EXT_geometry_shader : require\n" |
| "buffer ssbo\n" |
| "{\n" |
| " vec4 value1;\n" |
| "} block0[" |
| << numSSBOs |
| << "];\n" |
| "layout (triangles) in;\n" |
| "layout (points, max_vertices = 1) out;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = gl_in[0].gl_Position;\n"; |
| |
| for (GLint i = 0; i < numSSBOs; ++i) |
| { |
| stream << " gl_Position += block0[" << i << "].value1;\n"; |
| } |
| stream << " EmitVertex();\n" |
| "}\n"; |
| |
| GLuint program = CompileProgramWithGS(essl31_shaders::vs::Simple(), stream.str().c_str(), |
| essl31_shaders::fs::Red()); |
| EXPECT_EQ(0u, program); |
| |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Verify that an link error occurs when the definition of a unform block in the vertex shader is |
| // different from that in a geometry shader. |
| TEST_P(GeometryShaderTest, UniformBlockMismatchBetweenVertexAndGeometryShader) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| uniform ubo |
| { |
| vec4 uniform_value_vert; |
| } block0; |
| in vec4 vertex_in; |
| out vec4 vertex_out; |
| void main() |
| { |
| gl_Position = vertex_in; |
| vertex_out = block0.uniform_value_vert; |
| })"; |
| |
| constexpr char kGS[] = R"(#version 310 es |
| #extension GL_EXT_geometry_shader : require |
| uniform ubo |
| { |
| vec4 uniform_value_geom; |
| } block0; |
| layout (triangles) in; |
| layout (points, max_vertices = 1) out; |
| in vec4 vertex_out[]; |
| void main() |
| { |
| gl_Position = gl_in[0].gl_Position + vertex_out[0]; |
| gl_Position += block0.uniform_value_geom; |
| EmitVertex(); |
| })"; |
| |
| GLuint program = CompileProgramWithGS(kVS, kGS, essl31_shaders::fs::Red()); |
| EXPECT_EQ(0u, program); |
| |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Verify that an link error occurs when the definition of a shader storage block in the geometry |
| // shader is different from that in a fragment shader. |
| TEST_P(GeometryShaderTest, ShaderStorageBlockMismatchBetweenGeometryAndFragmentShader) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| GLint maxGeometryShaderStorageBlocks = 0; |
| glGetIntegerv(GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT, &maxGeometryShaderStorageBlocks); |
| |
| // The minimun value of MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT can be 0. |
| // [EXT_geometry_shader] Table 20.43gs |
| ANGLE_SKIP_TEST_IF(maxGeometryShaderStorageBlocks == 0); |
| |
| GLint maxFragmentShaderStorageBlocks = 0; |
| glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &maxFragmentShaderStorageBlocks); |
| |
| // The minimun value of MAX_FRAGMENT_SHADER_STORAGE_BLOCKS can be 0. |
| // [OpenGL ES 3.1] Table 20.44 |
| ANGLE_SKIP_TEST_IF(maxFragmentShaderStorageBlocks == 0); |
| |
| constexpr char kGS[] = R"(#version 310 es |
| #extension GL_EXT_geometry_shader : require |
| buffer ssbo |
| { |
| vec4 ssbo_value; |
| } block0; |
| layout (triangles) in; |
| layout (points, max_vertices = 1) out; |
| void main() |
| { |
| gl_Position = gl_in[0].gl_Position + block0.ssbo_value; |
| EmitVertex(); |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| precision highp float; |
| buffer ssbo |
| { |
| vec3 ssbo_value; |
| } block0; |
| layout (location = 0) out vec4 output_color; |
| void main() |
| { |
| output_color = vec4(block0.ssbo_value, 1); |
| })"; |
| |
| GLuint program = CompileProgramWithGS(essl31_shaders::vs::Simple(), kGS, kFS); |
| EXPECT_EQ(0u, program); |
| |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Verify GL_REFERENCED_BY_GEOMETRY_SHADER_EXT cannot be used on platforms that don't support |
| // EXT_geometry_shader, or we will get an INVALID_ENUM error. |
| TEST_P(GeometryShaderTest, ReferencedByGeometryShaderWithoutExtensionEnabled) |
| { |
| ANGLE_SKIP_TEST_IF(IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| constexpr char kFS[] = R"(#version 310 es |
| precision highp float; |
| uniform vec4 color; |
| layout(location = 0) out vec4 oColor; |
| void main() |
| { |
| oColor = color; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS); |
| |
| const GLuint index = glGetProgramResourceIndex(program, GL_UNIFORM, "color"); |
| ASSERT_GL_NO_ERROR(); |
| ASSERT_NE(GL_INVALID_INDEX, index); |
| |
| constexpr GLenum kProps[] = {GL_REFERENCED_BY_GEOMETRY_SHADER_EXT}; |
| constexpr GLsizei kPropCount = static_cast<GLsizei>(ArraySize(kProps)); |
| GLint params[ArraySize(kProps)]; |
| GLsizei length; |
| |
| glGetProgramResourceiv(program, GL_UNIFORM, index, kPropCount, kProps, kPropCount, &length, |
| params); |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); |
| } |
| |
| // Verify GL_REFERENCED_BY_GEOMETRY_SHADER_EXT can work correctly on platforms that support |
| // EXT_geometry_shader. |
| TEST_P(GeometryShaderTest, ReferencedByGeometryShader) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| precision highp float; |
| layout(location = 0) in highp vec4 position; |
| void main() |
| { |
| gl_Position = position; |
| })"; |
| |
| constexpr char kGS[] = R"(#version 310 es |
| #extension GL_EXT_geometry_shader : require |
| layout (binding = 3) uniform ubo0 |
| { |
| vec4 ubo0_location; |
| } block0; |
| layout (binding = 4) uniform ubo1 |
| { |
| vec4 ubo1_location; |
| } block1; |
| uniform vec4 u_color; |
| layout (triangles) in; |
| layout (points, max_vertices = 1) out; |
| out vec4 gs_out; |
| void main() |
| { |
| gl_Position = gl_in[0].gl_Position; |
| gl_Position += block0.ubo0_location + block1.ubo1_location; |
| gs_out = u_color; |
| EmitVertex(); |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| precision highp float; |
| in vec4 gs_out; |
| layout(location = 0) out vec4 oColor; |
| void main() |
| { |
| oColor = gs_out; |
| })"; |
| |
| ANGLE_GL_PROGRAM_WITH_GS(program, kVS, kGS, kFS); |
| |
| constexpr GLenum kProps[] = {GL_REFERENCED_BY_GEOMETRY_SHADER_EXT}; |
| constexpr GLsizei kPropCount = static_cast<GLsizei>(ArraySize(kProps)); |
| std::array<GLint, ArraySize(kProps)> params; |
| GLsizei length; |
| |
| params.fill(1); |
| GLuint index = glGetProgramResourceIndex(program, GL_PROGRAM_INPUT, "position"); |
| glGetProgramResourceiv(program, GL_PROGRAM_INPUT, index, kPropCount, kProps, kPropCount, |
| &length, params.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(0, params[0]); |
| |
| params.fill(1); |
| index = glGetProgramResourceIndex(program, GL_PROGRAM_OUTPUT, "oColor"); |
| glGetProgramResourceiv(program, GL_PROGRAM_OUTPUT, index, kPropCount, kProps, kPropCount, |
| &length, params.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(0, params[0]); |
| |
| index = glGetProgramResourceIndex(program, GL_UNIFORM, "u_color"); |
| glGetProgramResourceiv(program, GL_UNIFORM, index, kPropCount, kProps, kPropCount, &length, |
| params.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(1, params[0]); |
| |
| params.fill(0); |
| index = glGetProgramResourceIndex(program, GL_UNIFORM_BLOCK, "ubo1"); |
| glGetProgramResourceiv(program, GL_UNIFORM_BLOCK, index, kPropCount, kProps, kPropCount, |
| &length, params.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(1, params[0]); |
| |
| GLint maxGeometryShaderStorageBlocks = 0; |
| glGetIntegerv(GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT, &maxGeometryShaderStorageBlocks); |
| // The maximum number of shader storage blocks in a geometry shader can be 0. |
| // [EXT_geometry_shader] Table 20.43gs |
| if (maxGeometryShaderStorageBlocks > 0) |
| { |
| constexpr char kGSWithSSBO[] = R"(#version 310 es |
| #extension GL_EXT_geometry_shader : require |
| layout (binding = 2) buffer ssbo |
| { |
| vec4 ssbo_value; |
| } block0; |
| layout (triangles) in; |
| layout (points, max_vertices = 1) out; |
| out vec4 gs_out; |
| void main() |
| { |
| gl_Position = gl_in[0].gl_Position + block0.ssbo_value; |
| gs_out = block0.ssbo_value; |
| EmitVertex(); |
| })"; |
| |
| ANGLE_GL_PROGRAM_WITH_GS(programWithSSBO, kVS, kGSWithSSBO, kFS); |
| |
| params.fill(0); |
| index = glGetProgramResourceIndex(programWithSSBO, GL_SHADER_STORAGE_BLOCK, "ssbo"); |
| glGetProgramResourceiv(programWithSSBO, GL_SHADER_STORAGE_BLOCK, index, kPropCount, kProps, |
| kPropCount, &length, params.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(1, params[0]); |
| |
| params.fill(0); |
| index = glGetProgramResourceIndex(programWithSSBO, GL_BUFFER_VARIABLE, "ssbo.ssbo_value"); |
| glGetProgramResourceiv(programWithSSBO, GL_BUFFER_VARIABLE, index, kPropCount, kProps, |
| kPropCount, &length, params.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(1, params[0]); |
| } |
| |
| GLint maxGeometryAtomicCounterBuffers = 0; |
| glGetIntegerv(GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT, &maxGeometryAtomicCounterBuffers); |
| // The maximum number of atomic counter buffers in a geometry shader can be 0. |
| // [EXT_geometry_shader] Table 20.43gs |
| if (maxGeometryAtomicCounterBuffers > 0) |
| { |
| constexpr char kGSWithAtomicCounters[] = R"(#version 310 es |
| #extension GL_EXT_geometry_shader : require |
| layout(binding = 1, offset = 0) uniform atomic_uint gs_counter; |
| layout (triangles) in; |
| layout (points, max_vertices = 1) out; |
| out vec4 gs_out; |
| void main() |
| { |
| atomicCounterIncrement(gs_counter); |
| gl_Position = gl_in[0].gl_Position; |
| gs_out = vec4(1.0, 0.0, 0.0, 1.0); |
| EmitVertex(); |
| })"; |
| |
| ANGLE_GL_PROGRAM_WITH_GS(programWithAtomicCounter, kVS, kGSWithAtomicCounters, kFS); |
| |
| params.fill(0); |
| index = glGetProgramResourceIndex(programWithAtomicCounter, GL_UNIFORM, "gs_counter"); |
| EXPECT_GL_NO_ERROR(); |
| glGetProgramResourceiv(programWithAtomicCounter, GL_ATOMIC_COUNTER_BUFFER, index, |
| kPropCount, kProps, kPropCount, &length, params.data()); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_EQ(1, params[0]); |
| } |
| } |
| |
| // Verify correct errors can be reported when we use illegal parameters on FramebufferTextureEXT. |
| TEST_P(GeometryShaderTest, NegativeFramebufferTextureEXT) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_3D, tex); |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 32, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| |
| // [EXT_geometry_shader] Section 9.2.8, "Attaching Texture Images to a Framebuffer" |
| // An INVALID_ENUM error is generated if <target> is not DRAW_FRAMEBUFFER, READ_FRAMEBUFFER, or |
| // FRAMEBUFFER. |
| glFramebufferTextureEXT(GL_TEXTURE_2D, GL_COLOR_ATTACHMENT0, tex, 0); |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); |
| |
| // An INVALID_ENUM error is generated if <attachment> is not one of the attachments in Table |
| // 9.1. |
| glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_TEXTURE_2D, tex, 0); |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); |
| |
| // An INVALID_OPERATION error is generated if zero is bound to <target>. |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| |
| // An INVALID_VALUE error is generated if <texture> is not the name of a texture object, or if |
| // <level> is not a supported texture level for <texture>. |
| GLuint tex2; |
| glGenTextures(1, &tex2); |
| glDeleteTextures(1, &tex2); |
| ASSERT_FALSE(glIsTexture(tex2)); |
| glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex2, 0); |
| EXPECT_GL_ERROR(GL_INVALID_VALUE); |
| |
| GLint max3DSize; |
| glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &max3DSize); |
| GLint max3DLevel = static_cast<GLint>(std::log2(max3DSize)); |
| glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, max3DLevel + 1); |
| EXPECT_GL_ERROR(GL_INVALID_VALUE); |
| } |
| |
| // Verify CheckFramebufferStatus can work correctly on layered depth and stencil attachments. |
| TEST_P(GeometryShaderTest, LayeredFramebufferCompletenessWithDepthAttachment) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| GLint maxFramebufferLayers; |
| glGetIntegerv(GL_MAX_FRAMEBUFFER_LAYERS_EXT, &maxFramebufferLayers); |
| |
| constexpr GLint kTexLayers = 2; |
| ASSERT_LT(kTexLayers, maxFramebufferLayers); |
| |
| GLTexture layeredColorTex; |
| glBindTexture(GL_TEXTURE_3D, layeredColorTex); |
| glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 32, 32, kTexLayers, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| nullptr); |
| |
| // [EXT_geometry_shader] section 9.4.1, "Framebuffer Completeness" |
| // If any framebuffer attachment is layered, all populated attachments must be layered. |
| // {FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT } |
| GLTexture layeredDepthStencilTex; |
| glBindTexture(GL_TEXTURE_2D_ARRAY, layeredDepthStencilTex); |
| glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH24_STENCIL8, 32, 32, kTexLayers, 0, |
| GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); |
| |
| // 1. Color attachment is layered, while depth attachment is not layered. |
| GLFramebuffer fbo1; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo1); |
| glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, layeredColorTex, 0); |
| glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, layeredDepthStencilTex, 0, 0); |
| GLenum status1 = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT, status1); |
| |
| // 2. Color attachment is not layered, while depth attachment is layered. |
| GLFramebuffer fbo2; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo2); |
| glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, layeredColorTex, 0, 0); |
| glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, layeredDepthStencilTex, 0); |
| GLenum status2 = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT, status2); |
| |
| // 3. Color attachment is not layered, while stencil attachment is layered. |
| GLFramebuffer fbo3; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo3); |
| glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, layeredColorTex, 0, 0); |
| glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, layeredDepthStencilTex, 0); |
| GLenum status3 = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT, status3); |
| |
| // [EXT_geometry_shader] section 9.4.1, "Framebuffer Completeness" |
| // If <image> is a three-dimensional texture or a two-dimensional array texture and the |
| // attachment is layered, the depth or layer count, respectively, of the texture is less than or |
| // equal to the value of MAX_FRAMEBUFFER_LAYERS_EXT. |
| GLint maxArrayTextureLayers; |
| glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxArrayTextureLayers); |
| GLint depthTexLayer4 = maxFramebufferLayers + 1; |
| ANGLE_SKIP_TEST_IF(maxArrayTextureLayers < depthTexLayer4); |
| |
| // Use a depth attachment whose layer count exceeds MAX_FRAMEBUFFER_LAYERS |
| GLTexture depthTex4; |
| glBindTexture(GL_TEXTURE_2D_ARRAY, depthTex4); |
| glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH24_STENCIL8, 32, 32, depthTexLayer4, 0, |
| GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr); |
| GLFramebuffer fbo4; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo4); |
| glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTex4, 0); |
| GLenum status4 = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, status4); |
| } |
| |
| // Verify correct errors can be reported when we use layered cube map attachments on a framebuffer. |
| TEST_P(GeometryShaderTest, NegativeLayeredFramebufferCompletenessWithCubeMapTextures) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_CUBE_MAP, tex); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0); |
| GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, status); |
| |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA8, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| nullptr); |
| glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, status); |
| } |
| |
| // Verify that we can use default interpolation in the GS. |
| TEST_P(GeometryShaderTest, FlatQualifierNotRequired) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| constexpr char kGS[] = R"(#version 310 es |
| #extension GL_EXT_geometry_shader : require |
| layout(points) in; |
| layout(points, max_vertices=1) out; |
| |
| in highp int target[]; |
| highp uniform vec4 dummyZero; // Default value is vec4(0.0). |
| |
| void main() |
| { |
| highp vec4 retValue = dummyZero; |
| retValue += vec4(float(target[0])); |
| retValue += gl_in[0].gl_Position; |
| gl_Position = retValue; |
| EmitVertex(); |
| })"; |
| |
| GLuint geometryShader = CompileShader(GL_GEOMETRY_SHADER_EXT, kGS); |
| |
| EXPECT_NE(0u, geometryShader); |
| |
| GLuint programID = glCreateProgram(); |
| glAttachShader(programID, geometryShader); |
| |
| glDetachShader(programID, geometryShader); |
| glDeleteShader(geometryShader); |
| glDeleteProgram(programID); |
| |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| void GeometryShaderTest::setupLayeredFramebuffer(GLuint framebuffer, |
| GLuint color0, |
| GLuint color1, |
| GLuint depthStencil, |
| GLenum colorTarget, |
| const GLColor &color0InitialColor, |
| const GLColor &color1InitialColor, |
| float depthInitialValue, |
| GLint stencilInitialValue) |
| { |
| const uint32_t depthInitialValueUnorm = static_cast<uint32_t>(depthInitialValue * 0xFFFFFF); |
| const uint32_t depthStencilInitialValue = depthInitialValueUnorm << 8 | stencilInitialValue; |
| |
| const std::vector<GLColor> kColor0InitData(kWidth * kHeight * kColor0Layers, |
| color0InitialColor); |
| const std::vector<GLColor> kColor1InitData(kWidth * kHeight * kColor1Layers, |
| color1InitialColor); |
| const std::vector<uint32_t> kDepthStencilInitData(kWidth * kHeight * kDepthStencilLayers, |
| depthStencilInitialValue); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| ASSERT_GL_NO_ERROR(); |
| |
| glBindTexture(colorTarget, color0); |
| glTexStorage3D(colorTarget, 1, GL_RGBA8, kWidth, kHeight, kColor0Layers); |
| glTexSubImage3D(colorTarget, 0, 0, 0, 0, kWidth, kHeight, kColor0Layers, GL_RGBA, |
| GL_UNSIGNED_BYTE, kColor0InitData.data()); |
| ASSERT_GL_NO_ERROR(); |
| |
| glBindTexture(colorTarget, color1); |
| glTexStorage3D(colorTarget, 1, GL_RGBA8, kWidth, kHeight, kColor1Layers); |
| glTexSubImage3D(colorTarget, 0, 0, 0, 0, kWidth, kHeight, kColor1Layers, GL_RGBA, |
| GL_UNSIGNED_BYTE, kColor1InitData.data()); |
| ASSERT_GL_NO_ERROR(); |
| |
| glBindTexture(GL_TEXTURE_2D_ARRAY, depthStencil); |
| glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH24_STENCIL8, kWidth, kHeight, |
| kDepthStencilLayers); |
| glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, kWidth, kHeight, kDepthStencilLayers, |
| GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, kDepthStencilInitData.data()); |
| ASSERT_GL_NO_ERROR(); |
| |
| glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, color0, 0); |
| glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, color1, 0); |
| glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, depthStencil, 0); |
| |
| constexpr GLenum kDrawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}; |
| glDrawBuffers(2, kDrawBuffers); |
| |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| } |
| |
| void GeometryShaderTest::setupLayeredFramebufferProgram(GLProgram *program) |
| { |
| constexpr char kVS[] = R"(#version 310 es |
| |
| in highp vec4 position; |
| |
| void main() |
| { |
| gl_Position = position; |
| })"; |
| |
| static_assert(kFramebufferLayers == 3, |
| "Adjust the invocations parameter in the geometry shader, and color arrays in " |
| "fragment shader"); |
| |
| constexpr char kGS[] = R"(#version 310 es |
| #extension GL_EXT_geometry_shader : require |
| layout (invocations = 3, triangles) in; |
| layout (triangle_strip, max_vertices = 3) out; |
| |
| void main() |
| { |
| for (int n = 0; n < gl_in.length(); n++) |
| { |
| gl_Position = gl_in[n].gl_Position; |
| gl_Layer = gl_InvocationID; |
| EmitVertex(); |
| } |
| EndPrimitive(); |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_geometry_shader : require |
| precision mediump float; |
| |
| layout(location = 0) out mediump vec4 color0; |
| layout(location = 1) out mediump vec4 color1; |
| |
| const vec4 color0Layers[3] = vec4[]( |
| vec4(1, 0, 0, 1), |
| vec4(0, 1, 0, 1), |
| vec4(0, 0, 1, 1) |
| ); |
| |
| const vec4 color1Layers[3] = vec4[]( |
| vec4(1, 1, 0, 1), |
| vec4(0, 1, 1, 1), |
| vec4(1, 0, 1, 1) |
| ); |
| |
| void main() |
| { |
| color0 = color0Layers[gl_Layer]; |
| color1 = color1Layers[gl_Layer]; |
| })"; |
| |
| program->makeRaster(kVS, kGS, kFS); |
| ASSERT_TRUE(program->valid()); |
| } |
| |
| void GeometryShaderTest::verifyLayeredFramebufferColor(GLuint colorTexture, |
| GLenum colorTarget, |
| const GLColor expected[], |
| GLsizei layerCount) |
| { |
| // Note: the OpenGL and Vulkan specs are unclear regarding whether clear should affect all |
| // layers of the framebuffer or the attachment. The GL spec says: |
| // |
| // > When the Clear or ClearBuffer* commands described in section 15.2.3 are |
| // > used to clear a layered framebuffer attachment, all layers of the attachment are |
| // > cleared. |
| // |
| // Which implies that all layers are cleared. However, it's common among vendors to consider |
| // only the attachments accessible to a draw call to be affected by clear (otherwise |
| // clear-through-draw cannot be done). |
| // |
| // There is inconsistency between implementations in both the OpenGL and Vulkan implementations |
| // in this regard. In OpenGL, Qualcomm and Intel drivers clear all layers while Nvidia drivers |
| // clear only the framebuffer layers. In Vulkan, Intel and AMD windows drivers clear all layers |
| // with loadOp=CLEAR, while the other implementations (including Intel mesa) only clear the |
| // framebuffer layers. |
| // |
| // Due to this inconsistency, only the framebuffer layers are verified. The other layers, if |
| // the texture has them will either contain the initial or the cleared color, but is not |
| // verified by these tests. |
| layerCount = kFramebufferLayers; |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| |
| glBindTexture(colorTarget, colorTexture); |
| |
| for (GLsizei layer = 0; layer < layerCount; ++layer) |
| { |
| glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture, 0, layer); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, expected[layer], 1); |
| EXPECT_PIXEL_COLOR_NEAR(kWidth - 1, 0, expected[layer], 1); |
| EXPECT_PIXEL_COLOR_NEAR(0, kHeight - 1, expected[layer], 1); |
| EXPECT_PIXEL_COLOR_NEAR(kWidth - 1, kHeight - 1, expected[layer], 1); |
| } |
| } |
| |
| void GeometryShaderTest::verifyLayeredFramebufferDepthStencil(GLuint depthStencilTexture, |
| const float expectedDepth[], |
| const GLint expectedStencil[], |
| GLsizei layerCount) |
| { |
| // See comment in verifyLayeredFramebufferColor |
| layerCount = kFramebufferLayers; |
| |
| // Setup program |
| ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor()); |
| glUseProgram(drawColor); |
| GLint colorUniformLocation = |
| glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform()); |
| ASSERT_NE(colorUniformLocation, -1); |
| |
| // Set up state to validate depth and stencil |
| glEnable(GL_DEPTH_TEST); |
| glEnable(GL_STENCIL_TEST); |
| glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); |
| glStencilMask(0xFF); |
| glClearColor(0, 0, 0, 0); |
| |
| // Set up framebuffer |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| |
| GLTexture color; |
| glBindTexture(GL_TEXTURE_2D, color); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kWidth, kHeight); |
| |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0); |
| |
| glBindTexture(GL_TEXTURE_2D_ARRAY, depthStencilTexture); |
| |
| for (GLsizei layer = 0; layer < layerCount; ++layer) |
| { |
| glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, depthStencilTexture, |
| 0, layer); |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| glStencilFunc(GL_EQUAL, expectedStencil[layer], 0xFF); |
| |
| // Pass depth slightly less than expected |
| glDepthFunc(GL_LESS); |
| glUniform4f(colorUniformLocation, 0.1f, 0.2f, 0.3f, 0.4f); |
| drawQuad(drawColor, essl1_shaders::PositionAttrib(), expectedDepth[layer] * 2 - 1 - 0.01f); |
| |
| // Fail depth slightly greater than expected |
| glUniform4f(colorUniformLocation, 0.5f, 0.6f, 0.7f, 0.8f); |
| drawQuad(drawColor, essl1_shaders::PositionAttrib(), expectedDepth[layer] * 2 - 1 + 0.01f); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| // Verify results |
| const GLColor kExpected(25, 51, 76, 102); |
| |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, kExpected, 1); |
| EXPECT_PIXEL_COLOR_NEAR(kWidth - 1, 0, kExpected, 1); |
| EXPECT_PIXEL_COLOR_NEAR(0, kHeight - 1, kExpected, 1); |
| EXPECT_PIXEL_COLOR_NEAR(kWidth - 1, kHeight - 1, kExpected, 1); |
| } |
| } |
| |
| void GeometryShaderTest::layeredFramebufferClearTest(GLenum colorTarget) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| const GLColor kColor0InitColor(10, 20, 30, 40); |
| const GLColor kColor1InitColor(200, 210, 220, 230); |
| constexpr float kDepthInitValue = 0.35f; |
| constexpr GLint kStencilInitValue = 0x33; |
| |
| GLFramebuffer framebuffer; |
| GLTexture color0; |
| GLTexture color1; |
| GLTexture depthStencil; |
| GLProgram program; |
| |
| setupLayeredFramebuffer(framebuffer, color0, color1, depthStencil, colorTarget, |
| kColor0InitColor, kColor1InitColor, kDepthInitValue, kStencilInitValue); |
| setupLayeredFramebufferProgram(&program); |
| |
| glClearColor(0.5, 0.5, 0.5, 0.5); |
| glClearDepthf(1.0f); |
| glClearStencil(0x55); |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| |
| const GLColor kClearColor(127, 127, 127, 127); |
| const GLColor kExpectedColor0[kColor0Layers] = { |
| kClearColor, |
| kClearColor, |
| kClearColor, |
| kColor0InitColor, |
| }; |
| const GLColor kExpectedColor1[kColor1Layers] = { |
| kClearColor, |
| kClearColor, |
| kClearColor, |
| }; |
| const float kExpectedDepth[kDepthStencilLayers] = { |
| 1.0f, 1.0f, 1.0f, kDepthInitValue, kDepthInitValue, |
| }; |
| const GLint kExpectedStencil[kDepthStencilLayers] = { |
| 0x55, 0x55, 0x55, kStencilInitValue, kStencilInitValue, |
| }; |
| |
| verifyLayeredFramebufferColor(color0, colorTarget, kExpectedColor0, kColor0Layers); |
| verifyLayeredFramebufferColor(color1, colorTarget, kExpectedColor1, kColor1Layers); |
| verifyLayeredFramebufferDepthStencil(depthStencil, kExpectedDepth, kExpectedStencil, |
| kDepthStencilLayers); |
| } |
| |
| // Verify clear of layered attachments. Uses 3D color textures. |
| TEST_P(GeometryShaderTest, LayeredFramebufferClear3DColor) |
| { |
| // Mesa considers the framebuffer with mixed 3D and 2D array attachments to be incomplete. |
| // http://anglebug.com/5463 |
| ANGLE_SKIP_TEST_IF((IsAMD() || IsIntel()) && IsOpenGL() && IsLinux()); |
| |
| layeredFramebufferClearTest(GL_TEXTURE_3D); |
| } |
| |
| // Verify clear of layered attachments. Uses 2D array color textures. |
| TEST_P(GeometryShaderTest, LayeredFramebufferClear2DArrayColor) |
| { |
| layeredFramebufferClearTest(GL_TEXTURE_2D_ARRAY); |
| } |
| |
| void GeometryShaderTest::layeredFramebufferPreRenderClearTest(GLenum colorTarget) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| const GLColor kColor0InitColor(10, 20, 30, 40); |
| const GLColor kColor1InitColor(200, 210, 220, 230); |
| constexpr float kDepthInitValue = 0.35f; |
| constexpr GLint kStencilInitValue = 0x33; |
| |
| GLFramebuffer framebuffer; |
| GLTexture color0; |
| GLTexture color1; |
| GLTexture depthStencil; |
| GLProgram program; |
| |
| setupLayeredFramebuffer(framebuffer, color0, color1, depthStencil, colorTarget, |
| kColor0InitColor, kColor1InitColor, kDepthInitValue, kStencilInitValue); |
| setupLayeredFramebufferProgram(&program); |
| |
| glClearColor(0.5, 0.5, 0.5, 0.5); |
| glClearDepthf(1.0f); |
| glClearStencil(0x55); |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| |
| glEnable(GL_DEPTH_TEST); |
| glDepthFunc(GL_LESS); |
| |
| glEnable(GL_STENCIL_TEST); |
| glStencilFunc(GL_EQUAL, 0x5A, 0xF0); |
| glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); |
| glStencilMask(0xFF); |
| |
| drawQuad(program, "position", 0.9f); |
| |
| const GLColor kExpectedColor0[kColor0Layers] = { |
| GLColor::red, |
| GLColor::green, |
| GLColor::blue, |
| kColor0InitColor, |
| }; |
| const GLColor kExpectedColor1[kColor1Layers] = { |
| GLColor::yellow, |
| GLColor::cyan, |
| GLColor::magenta, |
| }; |
| const float kExpectedDepth[kDepthStencilLayers] = { |
| 0.95f, 0.95f, 0.95f, kDepthInitValue, kDepthInitValue, |
| }; |
| const GLint kExpectedStencil[kDepthStencilLayers] = { |
| 0x5A, 0x5A, 0x5A, kStencilInitValue, kStencilInitValue, |
| }; |
| |
| verifyLayeredFramebufferColor(color0, colorTarget, kExpectedColor0, kColor0Layers); |
| verifyLayeredFramebufferColor(color1, colorTarget, kExpectedColor1, kColor1Layers); |
| verifyLayeredFramebufferDepthStencil(depthStencil, kExpectedDepth, kExpectedStencil, |
| kDepthStencilLayers); |
| } |
| |
| // Verify pre-render clear of layered attachments. Uses 3D color textures. |
| TEST_P(GeometryShaderTest, LayeredFramebufferPreRenderClear3DColor) |
| { |
| // Mesa considers the framebuffer with mixed 3D and 2D array attachments to be incomplete. |
| // http://anglebug.com/5463 |
| ANGLE_SKIP_TEST_IF((IsAMD() || IsIntel()) && IsOpenGL() && IsLinux()); |
| |
| layeredFramebufferPreRenderClearTest(GL_TEXTURE_3D); |
| } |
| |
| // Verify pre-render clear of layered attachments. Uses 2D array color textures. |
| TEST_P(GeometryShaderTest, LayeredFramebufferPreRenderClear2DArrayColor) |
| { |
| layeredFramebufferPreRenderClearTest(GL_TEXTURE_2D_ARRAY); |
| } |
| |
| void GeometryShaderTest::layeredFramebufferMidRenderClearTest(GLenum colorTarget) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| // Vulkan's draw path for clear doesn't support layered framebuffers. http://anglebug.com/5453 |
| ANGLE_SKIP_TEST_IF(IsVulkan()); |
| |
| const GLColor kColor0InitColor(10, 20, 30, 40); |
| const GLColor kColor1InitColor(200, 210, 220, 230); |
| constexpr float kDepthInitValue = 0.35f; |
| constexpr GLint kStencilInitValue = 0x33; |
| |
| GLFramebuffer framebuffer; |
| GLTexture color0; |
| GLTexture color1; |
| GLTexture depthStencil; |
| GLProgram program; |
| |
| setupLayeredFramebuffer(framebuffer, color0, color1, depthStencil, colorTarget, |
| kColor0InitColor, kColor1InitColor, kDepthInitValue, kStencilInitValue); |
| setupLayeredFramebufferProgram(&program); |
| |
| glEnable(GL_DEPTH_TEST); |
| glDepthFunc(GL_ALWAYS); |
| |
| glEnable(GL_STENCIL_TEST); |
| glStencilFunc(GL_ALWAYS, 0x55, 0xF0); |
| glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); |
| glStencilMask(0xFF); |
| |
| drawQuad(program, "position", 0.3f); |
| |
| glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE); |
| glClearColor(0.5, 0.5, 0.5, 0.5); |
| glClearDepthf(0.8f); |
| glStencilMask(0xF0); |
| glClearStencil(0xAA); |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| |
| glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| glStencilMask(0xFF); |
| |
| const GLColor kExpectedColor0[kColor0Layers] = { |
| GLColor(255, 0, 127, 127), |
| GLColor(0, 255, 127, 127), |
| GLColor(0, 0, 127, 127), |
| kColor0InitColor, |
| }; |
| const GLColor kExpectedColor1[kColor1Layers] = { |
| GLColor(255, 255, 127, 127), |
| GLColor(0, 255, 127, 127), |
| GLColor(255, 0, 127, 127), |
| }; |
| const float kExpectedDepth[kDepthStencilLayers] = { |
| 0.6f, 0.6f, 0.6f, kDepthInitValue, kDepthInitValue, |
| }; |
| const GLint kExpectedStencil[kDepthStencilLayers] = { |
| 0xA5, 0xA5, 0xA5, kStencilInitValue, kStencilInitValue, |
| }; |
| |
| verifyLayeredFramebufferColor(color0, colorTarget, kExpectedColor0, kColor0Layers); |
| verifyLayeredFramebufferColor(color1, colorTarget, kExpectedColor1, kColor1Layers); |
| verifyLayeredFramebufferDepthStencil(depthStencil, kExpectedDepth, kExpectedStencil, |
| kDepthStencilLayers); |
| } |
| |
| // Verify mid-render clear of layered attachments. Uses 3D color textures. |
| TEST_P(GeometryShaderTest, LayeredFramebufferMidRenderClear3DColor) |
| { |
| // Mesa considers the framebuffer with mixed 3D and 2D array attachments to be incomplete. |
| // http://anglebug.com/5463 |
| ANGLE_SKIP_TEST_IF((IsAMD() || IsIntel()) && IsOpenGL() && IsLinux()); |
| |
| layeredFramebufferMidRenderClearTest(GL_TEXTURE_3D); |
| } |
| |
| // Verify mid-render clear of layered attachments. Uses 2D array color textures. |
| TEST_P(GeometryShaderTest, LayeredFramebufferMidRenderClear2DArrayColor) |
| { |
| layeredFramebufferMidRenderClearTest(GL_TEXTURE_2D_ARRAY); |
| } |
| |
| // Verify that prerotation applies to the geometry shader stage if present. |
| TEST_P(GeometryShaderTest, Prerotation) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| // http://anglebug.com/5478 |
| ANGLE_SKIP_TEST_IF(IsVulkan()); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| void main() |
| { |
| // Geometry shader will output fixed positions, so this is ignored. |
| gl_Position = vec4(0); |
| })"; |
| |
| constexpr char kGS[] = R"(#version 310 es |
| #extension GL_EXT_geometry_shader : require |
| layout (triangles) in; |
| layout (triangle_strip, max_vertices = 4) out; |
| |
| void main() |
| { |
| // Generate two triangles to cover the lower-left quarter of the screen. |
| gl_Position = vec4(0, -1, 0, 1); |
| EmitVertex(); |
| |
| gl_Position = vec4(0, 0, 0, 1); |
| EmitVertex(); |
| |
| gl_Position = vec4(-1, -1, 0, 1); |
| EmitVertex(); |
| |
| gl_Position = vec4(-1, 0, 0, 1); |
| EmitVertex(); |
| |
| EndPrimitive(); |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| precision mediump float; |
| |
| layout(location = 0) out mediump vec4 color; |
| |
| void main() |
| { |
| // Output solid green |
| color = vec4(0, 1.0, 0, 1.0); |
| })"; |
| |
| ANGLE_GL_PROGRAM_WITH_GS(program, kVS, kGS, kFS); |
| EXPECT_GL_NO_ERROR(); |
| |
| glClearColor(1.0, 0, 0, 1.0); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| glUseProgram(program); |
| glDrawArrays(GL_TRIANGLES, 0, 3); |
| |
| const int w = getWindowWidth(); |
| const int h = getWindowHeight(); |
| |
| EXPECT_PIXEL_RECT_EQ(0, 0, w / 2, h / 2, GLColor::green); |
| EXPECT_PIXEL_RECT_EQ(0, h / 2, w, h / 2, GLColor::red); |
| EXPECT_PIXEL_RECT_EQ(w / 2, 0, w / 2, h / 2, GLColor::red); |
| } |
| |
| // Verify that depth viewport transform applies to the geometry shader stage if present. |
| TEST_P(GeometryShaderTest, DepthViewportTransform) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| |
| // http://anglebug.com/5479 |
| ANGLE_SKIP_TEST_IF(IsVulkan()); |
| |
| constexpr char kVS[] = R"(#version 310 es |
| void main() |
| { |
| // Geometry shader will output fixed positions, so this is ignored. |
| gl_Position = vec4(0); |
| })"; |
| |
| constexpr char kGS[] = R"(#version 310 es |
| #extension GL_EXT_geometry_shader : require |
| layout (triangles) in; |
| layout (triangle_strip, max_vertices = 4) out; |
| |
| void main() |
| { |
| // Generate two triangles to cover the whole screen, with depth at -0.5. After viewport |
| // transformation, the depth buffer should contain 0.25. |
| gl_Position = vec4(1, -1, -0.5, 1); |
| EmitVertex(); |
| |
| gl_Position = vec4(1, 1, -0.5, 1); |
| EmitVertex(); |
| |
| gl_Position = vec4(-1, -1, -0.5, 1); |
| EmitVertex(); |
| |
| gl_Position = vec4(-1, 1, -0.5, 1); |
| EmitVertex(); |
| |
| EndPrimitive(); |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| #extension GL_EXT_shader_io_blocks : require |
| precision mediump float; |
| |
| layout(location = 0) out mediump vec4 color; |
| |
| void main() |
| { |
| // Output solid green |
| color = vec4(0, 1.0, 0, 1.0); |
| })"; |
| |
| ANGLE_GL_PROGRAM_WITH_GS(program, kVS, kGS, kFS); |
| EXPECT_GL_NO_ERROR(); |
| |
| glClearColor(1.0, 0, 0, 1.0); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| glEnable(GL_DEPTH_TEST); |
| glDepthFunc(GL_ALWAYS); |
| |
| glUseProgram(program); |
| glDrawArrays(GL_TRIANGLES, 0, 3); |
| |
| const int w = getWindowWidth(); |
| const int h = getWindowHeight(); |
| |
| EXPECT_PIXEL_RECT_EQ(0, 0, w, h, GLColor::green); |
| |
| // Verify depth |
| glDepthFunc(GL_LESS); |
| |
| // An epsilon below the depth value should pass the depth test |
| ANGLE_GL_PROGRAM(drawBlue, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| drawQuad(drawBlue, essl1_shaders::PositionAttrib(), -0.5f - 0.01f); |
| |
| // An epsilon above the depth value should fail the depth test |
| ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| drawQuad(drawRed, essl1_shaders::PositionAttrib(), -0.5f + 0.01f); |
| |
| EXPECT_PIXEL_RECT_EQ(0, 0, w, h, GLColor::blue); |
| } |
| |
| ANGLE_INSTANTIATE_TEST_ES3(GeometryShaderTestES3); |
| ANGLE_INSTANTIATE_TEST_ES31_AND(GeometryShaderTest, |
| WithEmulatedPrerotation(ES31_VULKAN(), 90), |
| WithEmulatedPrerotation(ES31_VULKAN(), 180), |
| WithEmulatedPrerotation(ES31_VULKAN(), 270)); |
| } // namespace |