| /*------------------------------------------------------------------------- |
| * OpenGL Conformance Test Suite |
| * ----------------------------- |
| * |
| * Copyright (c) 2014-2016 The Khronos Group Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ /*! |
| * \file |
| * \brief |
| */ /*-------------------------------------------------------------------*/ |
| |
| #include "esextcTessellationShaderProgramInterfaces.hpp" |
| #include "gluContextInfo.hpp" |
| #include "gluDefs.hpp" |
| #include "glwEnums.hpp" |
| #include "glwFunctions.hpp" |
| #include "tcuTestLog.hpp" |
| |
| namespace glcts |
| { |
| /** Constructor |
| * |
| * @param context Test context |
| * @param name Test case's name |
| * @param description Test case's desricption |
| **/ |
| TessellationShaderProgramInterfaces::TessellationShaderProgramInterfaces(Context& context, |
| const ExtParameters& extParams) |
| : TestCaseBase(context, extParams, "ext_program_interface_query_dependency", |
| "Verifies EXT_program_interface_query works correctly for tessellation" |
| " control and tessellation evaluation shaders") |
| , m_fs_shader_id(0) |
| , m_po_id(0) |
| , m_tc_shader_id(0) |
| , m_te_shader_id(0) |
| , m_vs_shader_id(0) |
| , m_is_atomic_counters_supported(false) |
| , m_is_shader_storage_blocks_supported(false) |
| { |
| /* Left blank on purpose */ |
| } |
| |
| /** Deinitializes all ES objects created for the test. */ |
| void TessellationShaderProgramInterfaces::deinit() |
| { |
| /** Call base class' deinit() function */ |
| TestCaseBase::deinit(); |
| |
| /* Release all objects we might've created */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| if (m_fs_shader_id != 0) |
| { |
| gl.deleteShader(m_fs_shader_id); |
| |
| m_fs_shader_id = 0; |
| } |
| |
| if (m_po_id != 0) |
| { |
| gl.deleteProgram(m_po_id); |
| |
| m_po_id = 0; |
| } |
| |
| if (m_tc_shader_id != 0) |
| { |
| gl.deleteShader(m_tc_shader_id); |
| |
| m_tc_shader_id = 0; |
| } |
| |
| if (m_te_shader_id != 0) |
| { |
| gl.deleteShader(m_te_shader_id); |
| |
| m_te_shader_id = 0; |
| } |
| |
| if (m_vs_shader_id != 0) |
| { |
| gl.deleteShader(m_vs_shader_id); |
| |
| m_vs_shader_id = 0; |
| } |
| } |
| |
| /** Initializes all ES objects that will be used for the test. */ |
| void TessellationShaderProgramInterfaces::initTest() |
| { |
| /* The test requires EXT_tessellation_shader and EXT_program_interfaces_query extensions */ |
| if (!m_is_tessellation_shader_supported || !m_is_program_interface_query_supported) |
| { |
| return; |
| } |
| |
| /* Generate a program object we will later configure */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| m_po_id = gl.createProgram(); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed"); |
| |
| /* Generate shader objects the test will use */ |
| m_fs_shader_id = gl.createShader(GL_FRAGMENT_SHADER); |
| m_tc_shader_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER); |
| m_te_shader_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER); |
| m_vs_shader_id = gl.createShader(GL_VERTEX_SHADER); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() failed"); |
| |
| glw::GLint gl_max_tess_control_shader_storage_blocks; |
| glw::GLint gl_max_tess_control_atomic_counter_buffers; |
| glw::GLint gl_max_tess_control_atomic_counters; |
| glw::GLint gl_max_tess_evaluation_shader_storage_blocks; |
| glw::GLint gl_max_tess_evaluation_atomic_counter_buffers; |
| glw::GLint gl_max_tess_evaluation_atomic_counters; |
| |
| gl.getIntegerv(m_glExtTokens.MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS, &gl_max_tess_control_atomic_counter_buffers); |
| GLU_EXPECT_NO_ERROR(gl.getError(), |
| "glGetIntegerv() failed for GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS_EXT pname"); |
| gl.getIntegerv(m_glExtTokens.MAX_TESS_CONTROL_ATOMIC_COUNTERS, &gl_max_tess_control_atomic_counters); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS_EXT pname"); |
| gl.getIntegerv(m_glExtTokens.MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS, &gl_max_tess_control_shader_storage_blocks); |
| GLU_EXPECT_NO_ERROR(gl.getError(), |
| "glGetIntegerv() failed for GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS_EXT pname"); |
| gl.getIntegerv(m_glExtTokens.MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS, |
| &gl_max_tess_evaluation_atomic_counter_buffers); |
| GLU_EXPECT_NO_ERROR(gl.getError(), |
| "glGetIntegerv() failed for GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS_EXT pname"); |
| gl.getIntegerv(m_glExtTokens.MAX_TESS_EVALUATION_ATOMIC_COUNTERS, &gl_max_tess_evaluation_atomic_counters); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS_EXT pname"); |
| gl.getIntegerv(m_glExtTokens.MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS, |
| &gl_max_tess_evaluation_shader_storage_blocks); |
| GLU_EXPECT_NO_ERROR(gl.getError(), |
| "glGetIntegerv() failed for GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS_EXT pname"); |
| |
| m_is_atomic_counters_supported = |
| (gl_max_tess_control_atomic_counter_buffers > 1) && (gl_max_tess_evaluation_atomic_counter_buffers > 1) && |
| (gl_max_tess_control_atomic_counters > 0) && (gl_max_tess_evaluation_atomic_counters > 0); |
| |
| m_is_shader_storage_blocks_supported = |
| (gl_max_tess_control_shader_storage_blocks > 0) && (gl_max_tess_evaluation_shader_storage_blocks > 0); |
| |
| const char* atomic_counters_header = NULL; |
| if (m_is_atomic_counters_supported) |
| { |
| atomic_counters_header = "#define USE_ATOMIC_COUNTERS 1\n"; |
| } |
| else |
| { |
| atomic_counters_header = "\n"; |
| } |
| |
| const char* shader_storage_blocks_header = NULL; |
| if (m_is_shader_storage_blocks_supported) |
| { |
| shader_storage_blocks_header = "#define USE_SHADER_STORAGE_BLOCKS\n"; |
| } |
| else |
| { |
| shader_storage_blocks_header = "\n"; |
| } |
| |
| /* Build shader program */ |
| const char* fs_body = "${VERSION}\n" |
| "\n" |
| "precision highp float;\n" |
| "\n" |
| "out vec4 test_output;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " test_output = vec4(1, 0, 0, 0);\n" |
| "}\n"; |
| |
| const char* tc_body = "\n" |
| "layout (vertices = 1) out;\n" |
| "\n" |
| /* Uniforms */ |
| "uniform vec2 tc_uniform1;\n" |
| "uniform mat4 tc_uniform2;\n" |
| "\n" |
| /* Uniform blocks */ |
| "uniform tc_uniform_block1\n" |
| "{\n" |
| " float tc_uniform_block1_1;\n" |
| "};\n" |
| /* Atomic counter buffers */ |
| "#ifdef USE_ATOMIC_COUNTERS\n" |
| "layout(binding = 1, offset = 0) uniform atomic_uint tc_atomic_counter1;\n" |
| "#endif\n" |
| /* Shader storage blocks & buffer variables */ |
| "#ifdef USE_SHADER_STORAGE_BLOCKS\n" |
| "layout(std140, binding = 0) buffer tc_shader_storage_block1\n" |
| "{\n" |
| " vec4 tc_shader_storage_buffer_object_1[];\n" |
| "};\n" |
| "#endif\n" |
| /* Body */ |
| "void main()\n" |
| "{\n" |
| " int test = 1;\n" |
| "\n" |
| /* Uniforms */ |
| " if (tc_uniform1.x == 0.0) test = 2;\n" |
| " if (tc_uniform2[0].y == 1.0) test = 3;\n" |
| /* Uniform blocks */ |
| " if (tc_uniform_block1_1 == 3.0) test = 4;\n" |
| /* Atomic counter buffers */ |
| "#ifdef USE_ATOMIC_COUNTERS\n" |
| " if (atomicCounter(tc_atomic_counter1) == 1u) test = 5;\n" |
| "#endif\n" |
| /* Shader storage blocks & buffer variables */ |
| "#ifdef USE_SHADER_STORAGE_BLOCKS\n" |
| " if (tc_shader_storage_buffer_object_1[0].x == 0.0) test = 6;\n" |
| "#endif\n" |
| "\n" |
| " gl_out [gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" |
| " gl_TessLevelOuter[0] = 2.0 * float(test);\n" |
| " gl_TessLevelOuter[1] = 3.0;\n" |
| "}\n"; |
| |
| const char* tc_code[] = { "${VERSION}\n", |
| /* Required EXT_tessellation_shader functionality */ |
| "${TESSELLATION_SHADER_REQUIRE}\n", atomic_counters_header, shader_storage_blocks_header, |
| tc_body }; |
| |
| const char* te_body = "\n" |
| "layout (isolines, ccw, equal_spacing, point_mode) in;\n" |
| "\n" |
| /* Uniforms */ |
| "uniform vec2 te_uniform1;\n" |
| "uniform mat4 te_uniform2;\n" |
| "\n" |
| /* Uniform blocks */ |
| "uniform te_uniform_block1\n" |
| "{\n" |
| " float te_uniform_block1_1;\n" |
| "};\n" |
| /* Atomic counter buffers */ |
| "#ifdef USE_ATOMIC_COUNTERS\n" |
| "layout(binding = 2, offset = 0) uniform atomic_uint te_atomic_counter1;\n" |
| "#endif\n" |
| /* Shader storage blocks & buffer variables */ |
| "#ifdef USE_SHADER_STORAGE_BLOCKS\n" |
| "layout(std140, binding = 0) buffer te_shader_storage_block1\n" |
| "{\n" |
| " vec4 te_shader_storage_buffer_object_1[];\n" |
| "};\n" |
| "#endif\n" |
| "void main()\n" |
| "{\n" |
| " int test = 1;\n" |
| "\n" |
| /* Uniforms */ |
| " if (te_uniform1.x == 0.0) test = 2;\n" |
| " if (te_uniform2[0].y == 1.0) test = 3;\n" |
| /* Uniform blocks */ |
| " if (te_uniform_block1_1 == 3.0) test = 4;\n" |
| /* Atomic counter buffers */ |
| "#ifdef USE_ATOMIC_COUNTERS\n" |
| " if (atomicCounter(te_atomic_counter1) == 1u) test = 5;\n" |
| "#endif\n" |
| /* Shader storage blocks & buffer variables */ |
| "#ifdef USE_SHADER_STORAGE_BLOCKS\n" |
| " if (te_shader_storage_buffer_object_1[0].x == 0.0) test = 6;\n" |
| "#endif\n" |
| "\n" |
| " gl_Position = gl_in[0].gl_Position * float(test);\n" |
| "}\n"; |
| |
| const char* te_code[] = { "${VERSION}\n", |
| /* Required EXT_tessellation_shader functionality */ |
| "${TESSELLATION_SHADER_REQUIRE}\n", atomic_counters_header, shader_storage_blocks_header, |
| te_body }; |
| |
| const char* vs_body = "${VERSION}\n" |
| "\n" |
| "in vec4 test_input;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(gl_VertexID, test_input.y, 0, 1);\n" |
| "}\n"; |
| |
| bool link_success = buildProgram(m_po_id, m_fs_shader_id, 1, &fs_body, m_tc_shader_id, 5, tc_code, m_te_shader_id, |
| 5, te_code, m_vs_shader_id, 1, &vs_body); |
| |
| if (!link_success) |
| { |
| TCU_FAIL("Program compilation and linking failed"); |
| } |
| |
| /* We're good to execute the test! */ |
| } |
| |
| /** Executes the test. |
| * |
| * Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise. |
| * |
| * Note the function throws exception should an error occur! |
| * |
| * @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again. |
| **/ |
| tcu::TestNode::IterateResult TessellationShaderProgramInterfaces::iterate(void) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Do not execute if required extensions are not supported. */ |
| if (!m_is_tessellation_shader_supported) |
| { |
| throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); |
| } |
| |
| /* Initialize ES objects needed to run the test */ |
| initTest(); |
| |
| /* Iterate through all interfaces */ |
| const glw::GLenum interfaces[] = { |
| GL_UNIFORM, GL_UNIFORM_BLOCK, GL_ATOMIC_COUNTER_BUFFER, GL_SHADER_STORAGE_BLOCK, |
| GL_BUFFER_VARIABLE, GL_PROGRAM_INPUT, GL_PROGRAM_OUTPUT |
| }; |
| const unsigned int n_interfaces = sizeof(interfaces) / sizeof(interfaces[0]); |
| |
| for (unsigned int n_interface = 0; n_interface < n_interfaces; ++n_interface) |
| { |
| glw::GLenum interface = interfaces[n_interface]; |
| |
| if ((interface == GL_SHADER_STORAGE_BLOCK || interface == GL_BUFFER_VARIABLE) && |
| !m_is_shader_storage_blocks_supported) |
| { |
| continue; |
| } |
| |
| if (interface == GL_ATOMIC_COUNTER_BUFFER && !m_is_atomic_counters_supported) |
| { |
| continue; |
| } |
| |
| /* For each interface, we want to check whether a specific resource |
| * is recognized by the implementation. If the name is unknown, |
| * the test should fail; if it's recognized, we should verify it's referenced |
| * by both TC and TE shaders |
| */ |
| const char* tc_resource_name = DE_NULL; |
| const char* te_resource_name = DE_NULL; |
| |
| switch (interface) |
| { |
| case GL_UNIFORM: |
| { |
| tc_resource_name = "tc_uniform1"; |
| te_resource_name = "te_uniform1"; |
| |
| break; |
| } |
| |
| case GL_UNIFORM_BLOCK: |
| { |
| tc_resource_name = "tc_uniform_block1"; |
| te_resource_name = "te_uniform_block1"; |
| |
| break; |
| } |
| |
| case GL_ATOMIC_COUNTER_BUFFER: |
| { |
| /* Atomic counter buffers are tested in a separate codepath. */ |
| break; |
| } |
| |
| case GL_SHADER_STORAGE_BLOCK: |
| { |
| tc_resource_name = "tc_shader_storage_block1"; |
| te_resource_name = "te_shader_storage_block1"; |
| |
| break; |
| } |
| |
| case GL_BUFFER_VARIABLE: |
| { |
| tc_resource_name = "tc_shader_storage_buffer_object_1"; |
| te_resource_name = "te_shader_storage_buffer_object_1"; |
| |
| break; |
| } |
| |
| case GL_PROGRAM_INPUT: |
| { |
| tc_resource_name = DE_NULL; |
| te_resource_name = DE_NULL; |
| |
| break; |
| } |
| |
| case GL_PROGRAM_OUTPUT: |
| { |
| tc_resource_name = DE_NULL; |
| te_resource_name = DE_NULL; |
| |
| break; |
| } |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized interface type"); |
| } |
| } /* switch (interface) */ |
| |
| /* Run in two iterations - first for TC, then for TE */ |
| for (int n_iteration = 0; n_iteration < 2; ++n_iteration) |
| { |
| glw::GLenum property = (n_iteration == 0) ? m_glExtTokens.REFERENCED_BY_TESS_CONTROL_SHADER : |
| m_glExtTokens.REFERENCED_BY_TESS_EVALUATION_SHADER; |
| |
| if (interface == GL_ATOMIC_COUNTER_BUFFER) |
| { |
| /* We only need a single iteration run for this interface */ |
| if (n_iteration == 1) |
| { |
| continue; |
| } |
| |
| /* Atomic counter buffers are not assigned names, hence they need to be |
| * tested slightly differently. |
| * |
| * Exactly two atomic counter buffers should be defined. Make sure that's the case. |
| */ |
| glw::GLint n_active_resources = 0; |
| |
| gl.getProgramInterfaceiv(m_po_id, interface, GL_ACTIVE_RESOURCES, &n_active_resources); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInterfaceiv() failed."); |
| |
| if (n_active_resources != 2) |
| { |
| TCU_FAIL("Invalid amount of atomic counter buffer binding points reported"); |
| } |
| |
| /* Check both resources and make sure they report separate atomic counters */ |
| bool was_tc_atomic_counter_buffer_reported = false; |
| bool was_te_atomic_counter_buffer_reported = false; |
| |
| for (int n_resource = 0; n_resource < n_active_resources; ++n_resource) |
| { |
| const glw::GLenum tc_property = m_glExtTokens.REFERENCED_BY_TESS_CONTROL_SHADER; |
| glw::GLint tc_property_value = 0; |
| const glw::GLenum te_property = m_glExtTokens.REFERENCED_BY_TESS_EVALUATION_SHADER; |
| glw::GLint te_property_value = 0; |
| |
| gl.getProgramResourceiv(m_po_id, interface, n_resource, 1, /* propCount */ |
| &tc_property, 1, /* bufSize */ |
| NULL, /* length */ |
| &tc_property_value); |
| GLU_EXPECT_NO_ERROR( |
| gl.getError(), |
| "glGetProgramResourceiv() failed for GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT property"); |
| |
| gl.getProgramResourceiv(m_po_id, interface, n_resource, 1, /* propCount */ |
| &te_property, 1, /* bufSize */ |
| NULL, /* length */ |
| &te_property_value); |
| GLU_EXPECT_NO_ERROR( |
| gl.getError(), |
| "glGetProgramResourceiv() failed for GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT property"); |
| |
| if (tc_property_value == GL_TRUE) |
| { |
| if (was_tc_atomic_counter_buffer_reported) |
| { |
| TCU_FAIL("Tessellation control-specific atomic counter buffer is reported twice"); |
| } |
| |
| was_tc_atomic_counter_buffer_reported = true; |
| } |
| |
| if (te_property_value == GL_TRUE) |
| { |
| if (was_te_atomic_counter_buffer_reported) |
| { |
| TCU_FAIL("Tessellation evaluation-specific atomic counter buffer is reported twice"); |
| } |
| |
| was_te_atomic_counter_buffer_reported = true; |
| } |
| } /* for (all active resources) */ |
| |
| if (!was_tc_atomic_counter_buffer_reported || !was_te_atomic_counter_buffer_reported) |
| { |
| TCU_FAIL("Either tessellation control or tessellation evaluation atomic counter buffer was not " |
| "reported"); |
| } |
| } |
| else |
| { |
| /* Retrieve resource index first, as long as the name is not NULL. |
| * If it's NULL, the property's value is assumed to be GL_FALSE for |
| * all reported active resources. |
| **/ |
| const char* resource_name = (n_iteration == 0) ? tc_resource_name : te_resource_name; |
| |
| if (resource_name == DE_NULL) |
| { |
| /* Make sure the property has GL_FALSE value for any resources |
| * reported for this interface. */ |
| glw::GLint n_active_resources = 0; |
| |
| gl.getProgramInterfaceiv(m_po_id, interface, GL_ACTIVE_RESOURCES, &n_active_resources); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInterfaceiv() failed."); |
| |
| for (glw::GLint n_resource = 0; n_resource < n_active_resources; ++n_resource) |
| { |
| verifyPropertyValue(interface, property, n_resource, GL_FALSE); |
| } /* for (all resource indices) */ |
| } |
| else |
| { |
| glw::GLuint resource_index = gl.getProgramResourceIndex(m_po_id, interface, resource_name); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceIndex() failed"); |
| |
| if (resource_index == GL_INVALID_INDEX) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Resource [" << resource_name |
| << "] was not recognized." << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Resource not recognized."); |
| } |
| |
| /* Now that we know the index, we can check the GL_REFERENCED_BY_... |
| * property value */ |
| verifyPropertyValue(interface, property, resource_index, GL_TRUE); |
| } |
| } /* (interface != GL_ATOMIC_COUNTER_BUFFER) */ |
| } /* for (both iterations) */ |
| } /* for (all interfaces) */ |
| |
| /* All done */ |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| return STOP; |
| } |
| |
| /** Checks if a property value reported for user-specified program object interface |
| * at given index is as expected. |
| * |
| * NOTE: This function throws TestError exception if retrieved value does not |
| * match @param expected_value. |
| * |
| * @param interface Program object interface to use for the query; |
| * @param property Interface property to check; |
| * @param index Property index to use for the test; |
| * @param expected_value Value that is expected to be reported by ES implementation. |
| **/ |
| void TessellationShaderProgramInterfaces::verifyPropertyValue(glw::GLenum interface, glw::GLenum property, |
| glw::GLuint index, glw::GLint expected_value) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| glw::GLint property_value = 0; |
| |
| gl.getProgramResourceiv(m_po_id, interface, index, 1, /* propCount */ |
| &property, 1, /* bufSize */ |
| NULL, /* length */ |
| &property_value); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceiv() failed"); |
| |
| if (property_value != expected_value) |
| { |
| TCU_FAIL("Invalid GL_REFERENCED_BY_... property value reported"); |
| } |
| } |
| |
| } /* namespace glcts */ |