// // 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: 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(); } }; 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 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 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(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(ArraySize(kProps)); std::array 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 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 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 . 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 is not the name of a texture object, or if // is not a supported texture level for . 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(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 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); } ANGLE_INSTANTIATE_TEST_ES3(GeometryShaderTestES3); ANGLE_INSTANTIATE_TEST_ES31(GeometryShaderTest); } // namespace