/*------------------------------------------------------------------------- * 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 "esextcTestCaseBase.hpp" #include "gluContextInfo.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "tcuStringTemplate.hpp" #include "tcuTestLog.hpp" #include #include #include namespace glcts { /* Predefined shader source code */ const char* TestCaseBase::m_boilerplate_vs_code = "${VERSION}\n" "\n" "precision highp float;\n" "\n" "void main()\n" "{\n" " gl_Position = vec4(gl_VertexID, 0, 0, 1);\n" "}\n"; const float TestCaseBase::m_epsilon_float = 0.0001f; /** Constructor * * @param context Test context * @param name Test case's name * @param description Test case's description **/ TestCaseBase::TestCaseBase(Context& context, const ExtParameters& extParam, const char* name, const char* description) : tcu::TestCase(context.getTestContext(), name, description) , m_context(context) , m_glslVersion(extParam.glslVersion) , m_extType(extParam.extType) , m_is_framebuffer_no_attachments_supported(false) , m_is_geometry_shader_extension_supported(false) , m_is_geometry_shader_point_size_supported(false) , m_is_gpu_shader5_supported(false) , m_is_program_interface_query_supported(false) , m_is_shader_image_load_store_supported(false) , m_is_shader_image_atomic_supported(false) , m_is_texture_storage_multisample_supported(false) , m_is_texture_storage_multisample_2d_array_supported(false) , m_is_tessellation_shader_supported(false) , m_is_tessellation_shader_point_size_supported(false) , m_is_texture_cube_map_array_supported(false) , m_is_texture_border_clamp_supported(false) , m_is_texture_buffer_supported(false) , m_is_viewport_array_supported(false) , seed_value(1) { m_glExtTokens.init(context.getRenderContext().getType()); } /** Initializes base class that all geometry shader test implementations derive from. * **/ void TestCaseBase::init(void) { initExtensions(); initGLSLSpecializationMap(); } /** Initializes function pointers for ES3.1 extensions, as well as determines * availability of these extensions. **/ void TestCaseBase::initExtensions() { const glu::ContextType& context_type = m_context.getRenderContext().getType(); /* OpenGL 4.0 or higher is minimum expectation for any of these tests */ if (glu::contextSupports(context_type, glu::ApiType::core(4, 0))) { m_is_geometry_shader_extension_supported = true; m_is_geometry_shader_point_size_supported = true; m_is_gpu_shader5_supported = true; m_is_tessellation_shader_supported = true; m_is_tessellation_shader_point_size_supported = true; m_is_texture_cube_map_array_supported = true; m_is_texture_border_clamp_supported = true; m_is_texture_buffer_supported = true; m_is_shader_image_atomic_supported = glu::contextSupports(context_type, glu::ApiType::core(4, 2)); m_is_texture_storage_multisample_2d_array_supported = glu::contextSupports(context_type, glu::ApiType::core(4, 3)); m_is_framebuffer_no_attachments_supported = glu::contextSupports(context_type, glu::ApiType::core(4, 3)); m_is_program_interface_query_supported = glu::contextSupports(context_type, glu::ApiType::core(4, 3)); m_is_texture_storage_multisample_supported = glu::contextSupports(context_type, glu::ApiType::core(4, 3)); m_is_shader_image_load_store_supported = glu::contextSupports(context_type, glu::ApiType::core(4, 2)); m_is_viewport_array_supported = glu::contextSupports(context_type, glu::ApiType::core(4, 1)); } else if (glu::contextSupports(context_type, glu::ApiType::es(3, 2))) { m_is_geometry_shader_extension_supported = true; m_is_gpu_shader5_supported = true; m_is_tessellation_shader_supported = true; m_is_texture_cube_map_array_supported = true; m_is_texture_border_clamp_supported = true; m_is_texture_buffer_supported = true; m_is_shader_image_atomic_supported = true; m_is_texture_storage_multisample_2d_array_supported = true; m_is_framebuffer_no_attachments_supported = true; m_is_program_interface_query_supported = true; m_is_texture_storage_multisample_supported = true; m_is_shader_image_load_store_supported = true; m_is_geometry_shader_point_size_supported = isExtensionSupported("GL_OES_geometry_point_size") || isExtensionSupported("GL_EXT_geometry_point_size"); m_is_tessellation_shader_point_size_supported = isExtensionSupported("GL_OES_tessellation_point_size") || isExtensionSupported("GL_EXT_tessellation_point_size"); m_is_viewport_array_supported = isExtensionSupported("GL_OES_viewport_array"); } else { /* ES3.1 core functionality is assumed*/ DE_ASSERT(isContextTypeES(context_type)); DE_ASSERT(glu::contextSupports(context_type, glu::ApiType::es(3, 1))); /* these are part of ES 3.1 */ m_is_framebuffer_no_attachments_supported = true; m_is_program_interface_query_supported = true; m_is_texture_storage_multisample_supported = true; m_is_shader_image_load_store_supported = true; /* AEP extensions - either test OES variants or EXT variants */ if (m_extType == EXTENSIONTYPE_OES) { /* These are all ES 3.1 extensions */ m_is_geometry_shader_extension_supported = isExtensionSupported("GL_OES_geometry_shader"); m_is_geometry_shader_point_size_supported = isExtensionSupported("GL_OES_geometry_point_size"); m_is_gpu_shader5_supported = isExtensionSupported("GL_OES_gpu_shader5"); m_is_tessellation_shader_supported = isExtensionSupported("GL_OES_tessellation_shader"); m_is_tessellation_shader_point_size_supported = isExtensionSupported("GL_OES_tessellation_point_size"); m_is_texture_cube_map_array_supported = isExtensionSupported("GL_OES_texture_cube_map_array"); m_is_texture_border_clamp_supported = isExtensionSupported("GL_OES_texture_border_clamp"); m_is_texture_buffer_supported = isExtensionSupported("GL_OES_texture_buffer"); } else { DE_ASSERT(m_extType == EXTENSIONTYPE_EXT); /* These are all ES 3.1 extensions */ m_is_geometry_shader_extension_supported = isExtensionSupported("GL_EXT_geometry_shader"); m_is_geometry_shader_point_size_supported = isExtensionSupported("GL_EXT_geometry_point_size"); m_is_gpu_shader5_supported = isExtensionSupported("GL_EXT_gpu_shader5"); m_is_tessellation_shader_supported = isExtensionSupported("GL_EXT_tessellation_shader"); m_is_tessellation_shader_point_size_supported = isExtensionSupported("GL_EXT_tessellation_point_size"); m_is_texture_cube_map_array_supported = isExtensionSupported("GL_EXT_texture_cube_map_array"); m_is_texture_border_clamp_supported = isExtensionSupported("GL_EXT_texture_border_clamp"); m_is_texture_buffer_supported = isExtensionSupported("GL_EXT_texture_buffer"); m_is_fragment_shading_rate_supported = isExtensionSupported("GL_EXT_fragment_shading_rate"); m_is_fragment_shading_rate_primitive_supported = isExtensionSupported("GL_EXT_fragment_shading_rate_primitive"); m_is_fragment_shading_rate_attachment_supported = isExtensionSupported("GL_EXT_fragment_shading_rate_attachment"); } /* other ES 3.1 extensions */ m_is_shader_image_atomic_supported = isExtensionSupported("GL_OES_shader_image_atomic"); m_is_texture_storage_multisample_2d_array_supported = isExtensionSupported("GL_OES_texture_storage_multisample_2d_array"); m_is_viewport_array_supported = isExtensionSupported("GL_OES_viewport_array"); m_is_multiview_ovr_supported = isExtensionSupported("GL_OVR_multiview"); } } /** Initializes function pointers for ES3.1 extensions, as well as determines * availability of these extensions. **/ void TestCaseBase::initGLSLSpecializationMap() { m_specializationMap["VERSION"] = glu::getGLSLVersionDeclaration(m_glslVersion); m_specializationMap["SHADER_IO_BLOCKS_ENABLE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_SHADER_IO_BLOCKS, EXTENSIONBEHAVIOR_ENABLE); m_specializationMap["SHADER_IO_BLOCKS_REQUIRE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_SHADER_IO_BLOCKS, EXTENSIONBEHAVIOR_REQUIRE); m_specializationMap["GEOMETRY_SHADER_ENABLE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_GEOMETRY_SHADER, EXTENSIONBEHAVIOR_ENABLE); m_specializationMap["GEOMETRY_SHADER_REQUIRE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_GEOMETRY_SHADER, EXTENSIONBEHAVIOR_REQUIRE); m_specializationMap["GEOMETRY_POINT_SIZE_ENABLE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_GEOMETRY_POINT_SIZE, EXTENSIONBEHAVIOR_ENABLE); m_specializationMap["GEOMETRY_POINT_SIZE_REQUIRE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_GEOMETRY_POINT_SIZE, EXTENSIONBEHAVIOR_REQUIRE); m_specializationMap["TESSELLATION_SHADER_ENABLE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_TESSELLATION_SHADER, EXTENSIONBEHAVIOR_ENABLE); m_specializationMap["TESSELLATION_SHADER_REQUIRE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_TESSELLATION_SHADER, EXTENSIONBEHAVIOR_REQUIRE); m_specializationMap["TESSELLATION_POINT_SIZE_ENABLE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_TESSELLATION_POINT_SIZE, EXTENSIONBEHAVIOR_ENABLE); m_specializationMap["TESSELLATION_POINT_SIZE_REQUIRE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_TESSELLATION_POINT_SIZE, EXTENSIONBEHAVIOR_REQUIRE); m_specializationMap["GPU_SHADER5_ENABLE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_GPU_SHADER5, EXTENSIONBEHAVIOR_ENABLE); m_specializationMap["GPU_SHADER5_REQUIRE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_GPU_SHADER5, EXTENSIONBEHAVIOR_REQUIRE); m_specializationMap["TEXTURE_BUFFER_ENABLE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_TEXTURE_BUFFER, EXTENSIONBEHAVIOR_ENABLE); m_specializationMap["TEXTURE_BUFFER_REQUIRE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_TEXTURE_BUFFER, EXTENSIONBEHAVIOR_REQUIRE); m_specializationMap["TEXTURE_CUBE_MAP_ARRAY_ENABLE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_TEXTURE_CUBE_MAP_ARRAY, EXTENSIONBEHAVIOR_ENABLE); m_specializationMap["TEXTURE_CUBE_MAP_ARRAY_REQUIRE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_TEXTURE_CUBE_MAP_ARRAY, EXTENSIONBEHAVIOR_REQUIRE); m_specializationMap["SHADER_IMAGE_ATOMIC_ENABLE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_SHADER_IMAGE_ATOMIC, EXTENSIONBEHAVIOR_ENABLE); m_specializationMap["SHADER_IMAGE_ATOMIC_REQUIRE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_SHADER_IMAGE_ATOMIC, EXTENSIONBEHAVIOR_REQUIRE); m_specializationMap["VIEWPORT_ARRAY_ENABLE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_VIEWPORT_ARRAY, EXTENSIONBEHAVIOR_ENABLE); m_specializationMap["VIEWPORT_ARRAY_REQUIRE"] = getGLSLExtDirective(m_extType, EXTENSIONNAME_VIEWPORT_ARRAY, EXTENSIONBEHAVIOR_REQUIRE); if (glu::isContextTypeES(m_context.getRenderContext().getType())) { m_specializationMap["IN_PER_VERTEX_DECL_ARRAY"] = "\n"; m_specializationMap["IN_PER_VERTEX_DECL_ARRAY_POINT_SIZE"] = "\n"; m_specializationMap["OUT_PER_VERTEX_DECL"] = "\n"; m_specializationMap["OUT_PER_VERTEX_DECL_POINT_SIZE"] = "\n"; m_specializationMap["OUT_PER_VERTEX_DECL_ARRAY"] = "\n"; m_specializationMap["OUT_PER_VERTEX_DECL_ARRAY_POINT_SIZE"] = "\n"; m_specializationMap["IN_DATA_DECL"] = "\n"; m_specializationMap["POSITION_WITH_IN_DATA"] = "gl_Position = gl_in[0].gl_Position;\n"; } else { m_specializationMap["IN_PER_VERTEX_DECL_ARRAY"] = "in gl_PerVertex {\n" " vec4 gl_Position;\n" "} gl_in[];\n"; m_specializationMap["IN_PER_VERTEX_DECL_ARRAY_POINT_SIZE"] = "in gl_PerVertex {\n" " vec4 gl_Position;\n" " float gl_PointSize;\n" "} gl_in[];\n"; m_specializationMap["OUT_PER_VERTEX_DECL"] = "out gl_PerVertex {\n" " vec4 gl_Position;\n" "};\n"; m_specializationMap["OUT_PER_VERTEX_DECL_POINT_SIZE"] = "out gl_PerVertex {\n" " vec4 gl_Position;\n" " float gl_PointSize;\n" "};\n"; m_specializationMap["OUT_PER_VERTEX_DECL_ARRAY"] = "out gl_PerVertex {\n" " vec4 gl_Position;\n" "} gl_out[];\n"; m_specializationMap["OUT_PER_VERTEX_DECL_ARRAY_POINT_SIZE"] = "out gl_PerVertex {\n" " vec4 gl_Position;\n" " float gl_PointSize;\n" "} gl_out[];\n"; m_specializationMap["IN_DATA_DECL"] = "in Data {\n" " vec4 pos;\n" "} input_data[1];\n"; m_specializationMap["POSITION_WITH_IN_DATA"] = "gl_Position = input_data[0].pos;\n"; } } /** Sets the seed for the random generator * @param seed - seed for the random generator */ void TestCaseBase::randomSeed(const glw::GLuint seed) { seed_value = seed; } /** Returns random unsigned integer from the range [0,max) * @param max - the value that is the upper boundary for the returned random numbers * @return random unsigned integer from the range [0,max) */ glw::GLuint TestCaseBase::randomFormula(const glw::GLuint max) { static const glw::GLuint a = 11; static const glw::GLuint b = 17; seed_value = (a * seed_value + b) % max; return seed_value; } /** Executes the test. * Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise. * * @return STOP if the test has finished, CONTINUE to indicate iterate should be called once again. * * Note the function throws exception should an error occur! **/ tcu::TestNode::IterateResult TestCaseBase::iterate(void) { qpTestResult result = QP_TEST_RESULT_FAIL; m_testCtx.setTestResult(result, "This location should never be called."); return STOP; } /** Deinitializes base class that all test implementations inherit from. * **/ void TestCaseBase::deinit(void) { /* Left empty on purpose */ } /** Tells whether particular extension is supported. * * @param extName: The name of the extension * * @return true if given extension name is reported as supported, false otherwise. **/ bool TestCaseBase::isExtensionSupported(const std::string& extName) const { const std::vector& extensions = m_context.getContextInfo().getExtensions(); if (std::find(extensions.begin(), extensions.end(), extName) != extensions.end()) { return true; } return false; } /** Helper method for specializing a shader */ std::string TestCaseBase::specializeShader(const unsigned int parts, const char* const* code) const { std::stringstream code_merged; for (unsigned int i = 0; i < parts; i++) { code_merged << code[i]; } return tcu::StringTemplate(code_merged.str().c_str()).specialize(m_specializationMap); } void TestCaseBase::shaderSourceSpecialized(glw::GLuint shader_id, glw::GLsizei shader_count, const glw::GLchar* const* shader_string) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); std::string specialized = specializeShader(shader_count, shader_string); const char* specialized_cstr = specialized.c_str(); gl.shaderSource(shader_id, 1, &specialized_cstr, NULL); } std::string getShaderTypeName(glw::GLenum shader_type) { switch (shader_type) { case GL_VERTEX_SHADER: return "Vertex shader"; case GL_TESS_CONTROL_SHADER: return "Tessellation control shader"; case GL_TESS_EVALUATION_SHADER: return "Tessellation evaluation shader"; case GL_GEOMETRY_SHADER: return "Geometry shader"; case GL_FRAGMENT_SHADER: return "Fragment shader"; case GL_COMPUTE_SHADER: return "Compute shader"; default: DE_ASSERT(0); return "??? shader"; } } /** Compiles and links program with variable amount of shaders * * @param po_id Program handle * @param out_has_compilation_failed Deref will be set to true, if shader compilation * failed for any of the submitted shaders. * Will be set to false otherwise. Can be NULL. * @param sh_stages Shader stages * @for all shader stages * { * @param sh_id Shader handle. 0 means "skip" * @param sh_parts Number of shader source code parts. * 0 means that it's already compiled. * @param sh_code Shader source code. * } **/ bool TestCaseBase::buildProgramVA(glw::GLuint po_id, bool* out_has_compilation_failed, unsigned int sh_stages, ...) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); std::vector vec_sh_id; va_list values; va_start(values, sh_stages); /* Shaders compilation */ glw::GLint compilation_status = GL_FALSE; for (unsigned int stage = 0; stage < sh_stages; ++stage) { glw::GLuint sh_id = va_arg(values, glw::GLuint); unsigned int sh_parts = va_arg(values, unsigned int); const char* const* sh_code = va_arg(values, const char* const*); if (sh_id == 0) { continue; } if (sh_parts != 0) { std::string sh_merged_string = specializeShader(sh_parts, sh_code); const char* sh_merged_ptr = sh_merged_string.c_str(); gl.shaderSource(sh_id, 1, &sh_merged_ptr, NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed!"); gl.compileShader(sh_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() failed!"); gl.getShaderiv(sh_id, GL_COMPILE_STATUS, &compilation_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() failed!"); if (compilation_status != GL_TRUE) { glw::GLint shader_type = 0; std::string info_log = getCompilationInfoLog(sh_id); gl.getShaderiv(sh_id, GL_SHADER_TYPE, &shader_type); std::string shader_type_str = getShaderTypeName(shader_type); m_testCtx.getLog() << tcu::TestLog::Message << shader_type_str << " compilation failure:\n\n" << info_log << "\n\n" << shader_type_str << " source:\n\n" << sh_merged_string << "\n\n" << tcu::TestLog::EndMessage; break; } } gl.attachShader(po_id, sh_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader(VERTEX_SHADER) call failed"); vec_sh_id.push_back(sh_id); } va_end(values); if (out_has_compilation_failed != NULL) { *out_has_compilation_failed = (compilation_status == GL_FALSE); } if (compilation_status != GL_TRUE) { return false; } /* Linking the program */ glw::GLint link_status = GL_FALSE; gl.linkProgram(po_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() failed!"); gl.getProgramiv(po_id, GL_LINK_STATUS, &link_status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() failed!"); if (link_status != GL_TRUE) { /* Dump link log */ std::string link_log = getLinkingInfoLog(po_id); m_testCtx.getLog() << tcu::TestLog::Message << "Link failure:\n\n" << link_log << "\n\n" << tcu::TestLog::EndMessage; /* Dump shader source */ for (std::vector::iterator it = vec_sh_id.begin(); it != vec_sh_id.end(); ++it) { glw::GLint shader_type = 0; gl.getShaderiv(*it, GL_SHADER_TYPE, &shader_type); std::string shader_type_str = getShaderTypeName(shader_type); std::string shader_source = getShaderSource(*it); m_testCtx.getLog() << tcu::TestLog::Message << shader_type_str << " source:\n\n" << shader_source << "\n\n" << tcu::TestLog::EndMessage; } return false; } return true; } /** Builds an OpenGL ES program by configuring contents of 1 shader object, * compiling it, attaching to specified program object, and finally * by linking the program object. * * Implementation assumes all aforementioned objects have already been * generated. * * @param po_id ID of program object * @param sh1_shader_id ID of first shader to configure. * @param n_sh1_body_parts Number of elements of @param sh1_body_parts array. * @param sh1_body_parts Pointer to array of strings to make up first shader's body. * Can be NULL. * * @return GTFtrue if successful, false otherwise. */ bool TestCaseBase::buildProgram(glw::GLuint po_id, glw::GLuint sh1_shader_id, unsigned int n_sh1_body_parts, const char* const* sh1_body_parts, bool* out_has_compilation_failed) { return buildProgramVA(po_id, out_has_compilation_failed, 1, sh1_shader_id, n_sh1_body_parts, sh1_body_parts); } /** Builds an OpenGL ES program by configuring contents of 2 shader objects, * compiling them, attaching to specified program object, and finally * by linking the program object. * * Implementation assumes all aforementioned objects have already been * generated. * * @param po_id ID of program object * @param sh1_shader_id ID of first shader to configure. * @param n_sh1_body_parts Number of elements of @param sh1_body_parts array. * @param sh1_body_parts Pointer to array of strings to make up first shader's body. * Can be NULL. * @param sh2_shader_id ID of second shader to configure. * @param n_sh2_body_parts Number of elements of @param sh2_body_parts array. * @param sh2_body_parts Pointer to array of strings to make up second shader's body. * Can be NULL. * * @return GTFtrue if successful, false otherwise. */ bool TestCaseBase::buildProgram(glw::GLuint po_id, glw::GLuint sh1_shader_id, unsigned int n_sh1_body_parts, const char* const* sh1_body_parts, glw::GLuint sh2_shader_id, unsigned int n_sh2_body_parts, const char* const* sh2_body_parts, bool* out_has_compilation_failed) { return buildProgramVA(po_id, out_has_compilation_failed, 2, sh1_shader_id, n_sh1_body_parts, sh1_body_parts, sh2_shader_id, n_sh2_body_parts, sh2_body_parts); } /** Builds an OpenGL ES program by configuring contents of 3 shader objects, * compiling them, attaching to specified program object, and finally * by linking the program object. * * Implementation assumes all aforementioned objects have already been * generated. * * @param po_id ID of program object * @param sh1_shader_id ID of first shader to configure. * @param n_sh1_body_parts Number of elements of @param sh1_body_parts array. * @param sh1_body_parts Pointer to array of strings to make up first shader's body. * Can be NULL. * @param sh2_shader_id ID of second shader to configure. * @param n_sh2_body_parts Number of elements of @param sh2_body_parts array. * @param sh2_body_parts Pointer to array of strings to make up second shader's body. * Can be NULL. * @param sh3_shader_id ID of third shader to configure. * @param n_sh3_body_parts Number of elements of @param sh3_body_parts array. * @param sh3_body_parts Pointer to array of strings to make up third shader's body. * Can be NULL. * @param has_compilation_failed Deref will be set to true if shader compilation failed, * false if shader compilation was successful. Can be NULL. * * @return GTFtrue if successful, false otherwise. */ bool TestCaseBase::buildProgram(glw::GLuint po_id, glw::GLuint sh1_shader_id, unsigned int n_sh1_body_parts, const char* const* sh1_body_parts, glw::GLuint sh2_shader_id, unsigned int n_sh2_body_parts, const char* const* sh2_body_parts, glw::GLuint sh3_shader_id, unsigned int n_sh3_body_parts, const char* const* sh3_body_parts, bool* out_has_compilation_failed) { return buildProgramVA(po_id, out_has_compilation_failed, 3, sh1_shader_id, n_sh1_body_parts, sh1_body_parts, sh2_shader_id, n_sh2_body_parts, sh2_body_parts, sh3_shader_id, n_sh3_body_parts, sh3_body_parts); } /** Builds an OpenGL ES program by configuring contents of 4 shader objects, * compiling them, attaching to specified program object, and finally * by linking the program object. * * Implementation assumes all aforementioned objects have already been * generated. * * @param po_id ID of program object * @param sh1_shader_id ID of first shader to configure. * @param n_sh1_body_parts Number of elements of @param sh1_body_parts array. * @param sh1_body_parts Pointer to array of strings to make up first shader's body. * Can be NULL. * @param sh2_shader_id ID of second shader to configure. * @param n_sh2_body_parts Number of elements of @param sh2_body_parts array. * @param sh2_body_parts Pointer to array of strings to make up second shader's body. * Can be NULL. * @param sh3_shader_id ID of third shader to configure. * @param n_sh3_body_parts Number of elements of @param sh3_body_parts array. * @param sh3_body_parts Pointer to array of strings to make up third shader's body. * Can be NULL. * @param sh4_shader_id ID of fourth shader to configure. * @param n_sh4_body_parts Number of elements of @param sh4_body_parts array. * @param sh4_body_parts Pointer to array of strings to make up fourth shader's body. * Can be NULL. * * @return GTFtrue if successful, false otherwise. */ bool TestCaseBase::buildProgram(glw::GLuint po_id, glw::GLuint sh1_shader_id, unsigned int n_sh1_body_parts, const char* const* sh1_body_parts, glw::GLuint sh2_shader_id, unsigned int n_sh2_body_parts, const char* const* sh2_body_parts, glw::GLuint sh3_shader_id, unsigned int n_sh3_body_parts, const char* const* sh3_body_parts, glw::GLuint sh4_shader_id, unsigned int n_sh4_body_parts, const char* const* sh4_body_parts, bool* out_has_compilation_failed) { return buildProgramVA(po_id, out_has_compilation_failed, 4, sh1_shader_id, n_sh1_body_parts, sh1_body_parts, sh2_shader_id, n_sh2_body_parts, sh2_body_parts, sh3_shader_id, n_sh3_body_parts, sh3_body_parts, sh4_shader_id, n_sh4_body_parts, sh4_body_parts); } /** Builds an OpenGL ES program by configuring contents of 5 shader objects, * compiling them, attaching to specified program object, and finally * by linking the program object. * * Implementation assumes all aforementioned objects have already been * generated. * * @param po_id ID of program object * @param sh1_shader_id ID of first shader to configure. * @param n_sh1_body_parts Number of elements of @param sh1_body_parts array. * @param sh1_body_parts Pointer to array of strings to make up first shader's body. * Can be NULL. * @param sh2_shader_id ID of second shader to configure. * @param n_sh2_body_parts Number of elements of @param sh2_body_parts array. * @param sh2_body_parts Pointer to array of strings to make up second shader's body. * Can be NULL. * @param sh3_shader_id ID of third shader to configure. * @param n_sh3_body_parts Number of elements of @param sh3_body_parts array. * @param sh3_body_parts Pointer to array of strings to make up third shader's body. * Can be NULL. * @param sh4_shader_id ID of fourth shader to configure. * @param n_sh4_body_parts Number of elements of @param sh4_body_parts array. * @param sh4_body_parts Pointer to array of strings to make up fourth shader's body. * Can be NULL. * @param sh5_shader_id ID of fifth shader to configure. * @param n_sh5_body_parts Number of elements of @param sh5_body_parts array. * @param sh5_body_parts Pointer to array of strings to make up fifth shader's body. * Can be NULL. * * @return GTFtrue if successful, false otherwise. */ bool TestCaseBase::buildProgram(glw::GLuint po_id, glw::GLuint sh1_shader_id, unsigned int n_sh1_body_parts, const char* const* sh1_body_parts, glw::GLuint sh2_shader_id, unsigned int n_sh2_body_parts, const char* const* sh2_body_parts, glw::GLuint sh3_shader_id, unsigned int n_sh3_body_parts, const char* const* sh3_body_parts, glw::GLuint sh4_shader_id, unsigned int n_sh4_body_parts, const char* const* sh4_body_parts, glw::GLuint sh5_shader_id, unsigned int n_sh5_body_parts, const char* const* sh5_body_parts, bool* out_has_compilation_failed) { return buildProgramVA(po_id, out_has_compilation_failed, 5, sh1_shader_id, n_sh1_body_parts, sh1_body_parts, sh2_shader_id, n_sh2_body_parts, sh2_body_parts, sh3_shader_id, n_sh3_body_parts, sh3_body_parts, sh4_shader_id, n_sh4_body_parts, sh4_body_parts, sh5_shader_id, n_sh5_body_parts, sh5_body_parts); } /** Compare pixel's color with specified value. * Assumptions: * - size of each channel is 1 byte * - channel order is R G B A * - lines are stored one after another, without any additional data * * @param buffer Image data * @param x X coordinate of pixel * @param y Y coordinate of pixel * @param width Image width * @param height Image height * @param pixel_size Size of single pixel in bytes, eg. for RGBA8 it should be set to 4 * @param expected_red Expected value of red channel, default is 0 * @param expected_green Expected value of green channel, default is 0 * @param expected_blue Expected value of blue channel, default is 0 * @param expected_alpha Expected value of alpha channel, default is 0 * * @retrun true When pixel color matches expected values * false When: * - buffer is null_ptr * - offset of pixel exceeds size of image * - pixel_size is not in range <1 ; 4> * - pixel color does not match expected values **/ bool TestCaseBase::comparePixel(const unsigned char* buffer, unsigned int x, unsigned int y, unsigned int width, unsigned int height, unsigned int pixel_size, unsigned char expected_red, unsigned char expected_green, unsigned char expected_blue, unsigned char expected_alpha) const { const unsigned int line_size = width * pixel_size; const unsigned int image_size = height * line_size; const unsigned int texel_offset = y * line_size + x * pixel_size; bool result = true; /* Sanity checks */ if (0 == buffer) { return false; } if (image_size < texel_offset) { return false; } switch (pixel_size) { /* Fall through by design */ case 4: { result &= (expected_alpha == buffer[texel_offset + 3]); } /* Fallthrough */ case 3: { result &= (expected_blue == buffer[texel_offset + 2]); } /* Fallthrough */ case 2: { result &= (expected_green == buffer[texel_offset + 1]); } /* Fallthrough */ case 1: { result &= (expected_red == buffer[texel_offset + 0]); break; } default: { return false; } } /* switch (pixel_size) */ return result; } /** Checks whether a combination of fragment/geometry/vertex shader objects compiles and links into a program * * @param n_fs_body_parts Number of elements of @param fs_body_parts array. * @param fs_body_parts Pointer to array of strings to make up fragment shader's body. * Must not be NULL. * * @param n_gs_body_parts Number of elements of @param gs_body_parts array. * @param gs_body_parts Pointer to array of strings to make up geometry shader's body. * Can be NULL. * * @param n_vs_body_parts Number of elements of @param vs_body_parts array. * @param vs_body_parts Pointer to array of strings to make up vertex shader's body. * Must not be NULL. * * @return true if program creation was successful, false otherwise. **/ bool TestCaseBase::doesProgramBuild(unsigned int n_fs_body_parts, const char* const* fs_body_parts, unsigned int n_gs_body_parts, const char* const* gs_body_parts, unsigned int n_vs_body_parts, const char* const* vs_body_parts) { /* General variables */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); bool result = false; /* Shaders */ glw::GLuint vertex_shader_id = 0; glw::GLuint geometry_shader_id = 0; glw::GLuint fragment_shader_id = 0; /* Program */ glw::GLuint program_object_id = 0; /* Create shaders */ vertex_shader_id = gl.createShader(GL_VERTEX_SHADER); geometry_shader_id = gl.createShader(m_glExtTokens.GEOMETRY_SHADER); fragment_shader_id = gl.createShader(GL_FRAGMENT_SHADER); /* Create program */ program_object_id = gl.createProgram(); /* Check createProgram call for errors */ GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed."); /* Compile and link the program */ result = buildProgram(program_object_id, fragment_shader_id, n_fs_body_parts, fs_body_parts, geometry_shader_id, n_gs_body_parts, gs_body_parts, vertex_shader_id, n_vs_body_parts, vs_body_parts); if (program_object_id != 0) gl.deleteProgram(program_object_id); if (fragment_shader_id != 0) gl.deleteShader(fragment_shader_id); if (geometry_shader_id != 0) gl.deleteShader(geometry_shader_id); if (vertex_shader_id != 0) gl.deleteShader(vertex_shader_id); return result; } /** Retrieves source for a shader object with GLES id @param shader_id. * * @param shader_id GLES id of a shader object to retrieve source for. * * @return String instance containing the shader source. **/ std::string TestCaseBase::getShaderSource(glw::GLuint shader_id) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLint length = 0; gl.getShaderiv(shader_id, GL_SHADER_SOURCE_LENGTH, &length); std::vector result_vec(length + 1); gl.getShaderSource(shader_id, length + 1, NULL, &result_vec[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not retrieve shader source!"); return std::string(&result_vec[0]); } /** Retrieves compilation info log for a shader object with GLES id * @param shader_id. * * @param shader_id GLES id of a shader object to retrieve compilation * info log for. * * @return String instance containing the log. **/ std::string TestCaseBase::getCompilationInfoLog(glw::GLuint shader_id) { return getInfoLog(LT_SHADER_OBJECT, shader_id); } /** Retrieves linking info log for a program object with GLES id * @param po_id. * * @param po_id GLES id of a program object to retrieve linking * info log for. * * @return String instance containing the log. **/ std::string TestCaseBase::getLinkingInfoLog(glw::GLuint po_id) { return getInfoLog(LT_PROGRAM_OBJECT, po_id); } /** Retrieves linking info log for a pipeline object with GLES id * @param ppo_id. * * @param ppo_id GLES id of a pipeline object to retrieve validation * info log for. * * @return String instance containing the log. **/ std::string TestCaseBase::getPipelineInfoLog(glw::GLuint ppo_id) { return getInfoLog(LT_PIPELINE_OBJECT, ppo_id); } /** Retrieves compilation OR linking info log for a shader/program object with GLES id * @param id. * * @param is_compilation_info_log true if @param id is a GLES id of a shader object; * false if it represents a program object. * @param id GLES id of a shader OR a program object to * retrieve info log for. * * @return String instance containing the log.. **/ std::string TestCaseBase::getInfoLog(LOG_TYPE log_type, glw::GLuint id) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLint n_characters = 0; /* Retrieve amount of characters needed to store the info log (terminator-inclusive) */ switch (log_type) { case LT_SHADER_OBJECT: gl.getShaderiv(id, GL_INFO_LOG_LENGTH, &n_characters); break; case LT_PROGRAM_OBJECT: gl.getProgramiv(id, GL_INFO_LOG_LENGTH, &n_characters); break; case LT_PIPELINE_OBJECT: gl.getProgramPipelineiv(id, GL_INFO_LOG_LENGTH, &n_characters); break; default: TCU_FAIL("Invalid parameter"); } /* Check if everything is fine so far */ GLU_EXPECT_NO_ERROR(gl.getError(), "Could not query info log length!"); /* Allocate buffer */ std::vector result_vec(n_characters + 1); /* Retrieve the info log */ switch (log_type) { case LT_SHADER_OBJECT: gl.getShaderInfoLog(id, n_characters + 1, 0, &result_vec[0]); break; case LT_PROGRAM_OBJECT: gl.getProgramInfoLog(id, n_characters + 1, 0, &result_vec[0]); break; case LT_PIPELINE_OBJECT: gl.getProgramPipelineInfoLog(id, n_characters + 1, 0, &result_vec[0]); break; } GLU_EXPECT_NO_ERROR(gl.getError(), "Could not retrieve info log!"); return std::string(&result_vec[0]); } /** Setup frame buffer: * 1 allocate texture storage for specified format and dimensions, * 2 bind framebuffer and attach texture to GL_COLOR_ATTACHMENT0 * 3 setup viewport to specified dimensions * * @param framebuffer_object_id FBO handle * @param color_texture_id Texture handle * @param texture_format Requested texture format, eg. GL_RGBA8 * @param texture_width Requested texture width * @param texture_height Requested texture height * * @return true All operations succeded * false In case of any error **/ bool TestCaseBase::setupFramebufferWithTextureAsAttachment(glw::GLuint framebuffer_object_id, glw::GLuint color_texture_id, glw::GLenum texture_format, glw::GLuint texture_width, glw::GLuint texture_height) const { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Allocate texture storage */ gl.bindTexture(GL_TEXTURE_2D, color_texture_id); gl.texStorage2D(GL_TEXTURE_2D, 1 /* levels */, texture_format, texture_width, texture_height); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not allocate texture storage!"); /* Setup framebuffer */ gl.bindFramebuffer(GL_FRAMEBUFFER, framebuffer_object_id); gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_texture_id, 0 /* level */); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not setup framebuffer!"); /* Setup viewport */ gl.viewport(0, 0, texture_width, texture_height); GLU_EXPECT_NO_ERROR(gl.getError(), "Could not setup viewport!"); /* Success */ return true; } /** Check Framebuffer Status. * Throws a TestError exception, should the framebuffer be found incomplete. * * @param framebuffer - GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER or GL_FRAMEBUFFER * */ void TestCaseBase::checkFramebufferStatus(glw::GLenum framebuffer) const { /* Get GL entry points */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLenum framebuffer_status = gl.checkFramebufferStatus(framebuffer); GLU_EXPECT_NO_ERROR(gl.getError(), "Error getting framebuffer status!"); if (GL_FRAMEBUFFER_COMPLETE != framebuffer_status) { switch (framebuffer_status) { case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: { TCU_FAIL("Framebuffer incomplete, status: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); } case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: { TCU_FAIL("Framebuffer incomplete, status: GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS"); } case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: { TCU_FAIL("Framebuffer incomplete, status: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"); } case GL_FRAMEBUFFER_UNSUPPORTED: { TCU_FAIL("Framebuffer incomplete, status: Error: GL_FRAMEBUFFER_UNSUPPORTED"); } default: { TCU_FAIL("Framebuffer incomplete, status not recognized"); } } /* switch (framebuffer_status) */ } /* if (GL_FRAMEBUFFER_COMPLETE != framebuffer_status) */ } std::string TestCaseBase::getGLSLExtDirective(ExtensionType type, ExtensionName name, ExtensionBehavior behavior) { if (type == EXTENSIONTYPE_NONE && name != EXTENSIONNAME_GEOMETRY_POINT_SIZE && name != EXTENSIONNAME_TESSELLATION_POINT_SIZE) { return ""; } const char* type_str = NULL; const char* name_str = NULL; const char* behavior_str = NULL; if (name == EXTENSIONNAME_SHADER_IMAGE_ATOMIC) { // There is no EXT version of shader_image_atomic; use OES type = EXTENSIONTYPE_OES; } if (name == EXTENSIONNAME_TESSELLATION_POINT_SIZE) { // there is no core version of tessellation_point_size, use OES or EXT if (isExtensionSupported("GL_OES_tessellation_point_size")) { type = EXTENSIONTYPE_OES; } else if (isExtensionSupported("GL_EXT_tessellation_point_size")) { type = EXTENSIONTYPE_EXT; } else { return ""; } } if (name == EXTENSIONNAME_GEOMETRY_POINT_SIZE) { // there is no core version of geometry_point_size, use OES or EXT if (isExtensionSupported("GL_OES_geometry_point_size")) { type = EXTENSIONTYPE_OES; } else if (isExtensionSupported("GL_EXT_geometry_point_size")) { type = EXTENSIONTYPE_EXT; } else { return ""; } } switch (type) { case EXTENSIONTYPE_EXT: type_str = "EXT_"; break; case EXTENSIONTYPE_OES: type_str = "OES_"; break; default: DE_ASSERT(0); return "#error unknown extension type\n"; } switch (name) { case EXTENSIONNAME_SHADER_IMAGE_ATOMIC: name_str = "shader_image_atomic"; break; case EXTENSIONNAME_SHADER_IO_BLOCKS: name_str = "shader_io_blocks"; break; case EXTENSIONNAME_GEOMETRY_SHADER: name_str = "geometry_shader"; break; case EXTENSIONNAME_GEOMETRY_POINT_SIZE: name_str = "geometry_point_size"; break; case EXTENSIONNAME_TESSELLATION_SHADER: name_str = "tessellation_shader"; break; case EXTENSIONNAME_TESSELLATION_POINT_SIZE: name_str = "tessellation_point_size"; break; case EXTENSIONNAME_TEXTURE_BUFFER: name_str = "texture_buffer"; break; case EXTENSIONNAME_TEXTURE_CUBE_MAP_ARRAY: name_str = "texture_cube_map_array"; break; case EXTENSIONNAME_GPU_SHADER5: name_str = "gpu_shader5"; break; case EXTENSIONNAME_VIEWPORT_ARRAY: name_str = "viewport_array"; break; default: DE_ASSERT(0); return "#error unknown extension name\n"; } switch (behavior) { case EXTENSIONBEHAVIOR_DISABLE: behavior_str = "disable"; break; case EXTENSIONBEHAVIOR_WARN: behavior_str = "warn"; break; case EXTENSIONBEHAVIOR_ENABLE: behavior_str = "enable"; break; case EXTENSIONBEHAVIOR_REQUIRE: behavior_str = "require"; break; default: DE_ASSERT(0); return "#error unknown extension behavior"; } std::stringstream str; str << "#extension GL_" << type_str << name_str << " : " << behavior_str; return str.str(); } } // namespace glcts