/*------------------------------------------------------------------------- * OpenGL Conformance Test Suite * ----------------------------- * * Copyright (c) 2015-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 */ /*-------------------------------------------------------------------*/ /*! * \file glcShaderNegativeTests.cpp * \brief Negative tests for shaders and interface matching. */ /*-------------------------------------------------------------------*/ #include "glcShaderNegativeTests.hpp" #include "deString.h" #include "deStringUtil.hpp" #include "gluContextInfo.hpp" #include "gluShaderProgram.hpp" #include "glw.h" #include "tcuStringTemplate.hpp" #include "tcuTestLog.hpp" namespace deqp { using tcu::TestLog; using namespace glu; struct ShaderVariants { GLSLVersion minimum_supported_version; const char* vertex_precision; const char* vertex_body; const char* frag_precision; const char* frag_body; bool should_link; }; class ShaderUniformInitializeGlobalCase : public TestCase { public: ShaderUniformInitializeGlobalCase(Context& context, const char* name, const char* description, GLSLVersion glslVersion) : TestCase(context, name, description), m_glslVersion(glslVersion) { } ~ShaderUniformInitializeGlobalCase() { // empty } IterateResult iterate() { qpTestResult result = QP_TEST_RESULT_PASS; static const char vertex_source_template[] = "${VERSION_DECL}\n" "precision mediump float;\n" "uniform vec4 nonconstantexpression;\n" "vec4 globalconstant0 = vec4(1.0, 1.0, 1.0, 1.0);\n" "vec4 globalconstant1 = nonconstantexpression;\n" "\n" "void main(void) { gl_Position = globalconstant0+globalconstant1; }\n"; static const char fragment_source_template[] = "${VERSION_DECL}\n" "precision mediump float;\n" "uniform vec4 nonconstantexpression;\n" "vec4 globalconstant0 = vec4(1.0, 1.0, 1.0, 1.0);\n" "vec4 globalconstant1 = nonconstantexpression;\n" "\n" "void main(void) { }\n"; std::map args; args["VERSION_DECL"] = getGLSLVersionDeclaration(m_glslVersion); std::string vertex_code = tcu::StringTemplate(vertex_source_template).specialize(args); std::string fragment_code = tcu::StringTemplate(fragment_source_template).specialize(args); // Setup program. ShaderProgram program(m_context.getRenderContext(), makeVtxFragSources(vertex_code.c_str(), fragment_code.c_str())); // GLSL ES does not allow initialization of global variables with non-constant // expressions, but GLSL does. // Check that either compilation or linking fails for ES, and that everything // succeeds for GL. bool vertexOk = program.getShaderInfo(SHADERTYPE_VERTEX).compileOk; bool fragmentOk = program.getShaderInfo(SHADERTYPE_FRAGMENT).compileOk; bool linkOk = program.getProgramInfo().linkOk; if (glslVersionIsES(m_glslVersion)) { if (vertexOk && fragmentOk && linkOk) result = QP_TEST_RESULT_FAIL; } else { if (!vertexOk && !fragmentOk && !linkOk) result = QP_TEST_RESULT_FAIL; } m_testCtx.setTestResult(result, qpGetTestResultName(result)); return STOP; } protected: GLSLVersion m_glslVersion; }; class ShaderUniformPrecisionLinkCase : public TestCase { public: ShaderUniformPrecisionLinkCase(Context& context, const char* name, const char* description, const ShaderVariants* shaderVariants, unsigned int shaderVariantsCount, GLSLVersion glslVersion) : TestCase(context, name, description) , m_glslVersion(glslVersion) , m_shaderVariants(shaderVariants) , m_shaderVariantsCount(shaderVariantsCount) { } ~ShaderUniformPrecisionLinkCase() { // empty } IterateResult iterate() { TestLog& log = m_testCtx.getLog(); qpTestResult result = QP_TEST_RESULT_PASS; static const char vertex_source_template[] = "${VERSION_DECL}\n" "uniform ${PREC_QUALIFIER} vec4 value;\n" "\n" "void main(void) { ${BODY} }\n"; static const char fragment_source_template[] = "${VERSION_DECL}\n" "out highp vec4 result;\n" "uniform ${PREC_QUALIFIER} vec4 value;\n" "\n" "void main(void) { ${BODY} }\n"; for (unsigned int i = 0; i < m_shaderVariantsCount; i++) { std::map args; if (m_glslVersion <= m_shaderVariants[i].minimum_supported_version) { continue; } args["VERSION_DECL"] = getGLSLVersionDeclaration(m_glslVersion); args["PREC_QUALIFIER"] = m_shaderVariants[i].vertex_precision; args["BODY"] = m_shaderVariants[i].vertex_body; std::string vcode = tcu::StringTemplate(vertex_source_template).specialize(args); args["PREC_QUALIFIER"] = m_shaderVariants[i].frag_precision; args["BODY"] = m_shaderVariants[i].frag_body; std::string fcode = tcu::StringTemplate(fragment_source_template).specialize(args); // Setup program. ShaderProgram program(m_context.getRenderContext(), makeVtxFragSources(vcode.c_str(), fcode.c_str())); // Check that compile/link results are what we expect. bool vertexOk = program.getShaderInfo(SHADERTYPE_VERTEX).compileOk; bool fragmentOk = program.getShaderInfo(SHADERTYPE_FRAGMENT).compileOk; bool linkOk = program.getProgramInfo().linkOk; const char* failReason = DE_NULL; if (!vertexOk || !fragmentOk) { failReason = "expected shaders to compile, but failed."; } else if (m_shaderVariants[i].should_link && !linkOk) { failReason = "expected shaders to link, but failed."; } else if (!m_shaderVariants[i].should_link && linkOk) { failReason = "expected shaders to fail linking, but succeeded."; } if (failReason != DE_NULL) { log << TestLog::Message << "ERROR: " << failReason << TestLog::EndMessage; result = QP_TEST_RESULT_FAIL; } } m_testCtx.setTestResult(result, qpGetTestResultName(result)); return STOP; } protected: GLSLVersion m_glslVersion; const ShaderVariants* m_shaderVariants; unsigned int m_shaderVariantsCount; }; class ShaderConstantSequenceExpressionCase : public TestCase { public: ShaderConstantSequenceExpressionCase(Context& context, const char* name, const char* description, GLSLVersion glslVersion) : TestCase(context, name, description), m_glslVersion(glslVersion) { } ~ShaderConstantSequenceExpressionCase() { // empty } IterateResult iterate() { qpTestResult result = QP_TEST_RESULT_PASS; static const char vertex_source_template[] = "${VERSION_DECL}\n" "precision mediump float;\n" "const int test = (1, 2);\n" "\n" "void main(void) { gl_Position = vec4(test); }\n"; static const char fragment_source_template[] = "${VERSION_DECL}\n" "precision mediump float;\n" "\n" "void main(void) { }\n"; std::map args; args["VERSION_DECL"] = getGLSLVersionDeclaration(m_glslVersion); std::string vertex_code = tcu::StringTemplate(vertex_source_template).specialize(args); std::string fragment_code = tcu::StringTemplate(fragment_source_template).specialize(args); // Setup program. ShaderProgram program(m_context.getRenderContext(), makeVtxFragSources(vertex_code.c_str(), fragment_code.c_str())); // GLSL does not allow the sequence operator in a constant expression // Check that either compilation or linking fails bool vertexOk = program.getShaderInfo(SHADERTYPE_VERTEX).compileOk; bool fragmentOk = program.getShaderInfo(SHADERTYPE_FRAGMENT).compileOk; bool linkOk = program.getProgramInfo().linkOk; bool run_test_es = (glslVersionIsES(m_glslVersion) && m_glslVersion > GLSL_VERSION_100_ES); bool run_test_desktop = (m_glslVersion > GLSL_VERSION_420); if (run_test_es || run_test_desktop) { if (vertexOk && fragmentOk && linkOk) result = QP_TEST_RESULT_FAIL; } m_testCtx.setTestResult(result, qpGetTestResultName(result)); return STOP; } protected: GLSLVersion m_glslVersion; }; ShaderNegativeTests::ShaderNegativeTests(Context& context, GLSLVersion glslVersion) : TestCaseGroup(context, "negative", "Shader Negative tests"), m_glslVersion(glslVersion) { // empty } ShaderNegativeTests::~ShaderNegativeTests() { // empty } void ShaderNegativeTests::init(void) { addChild(new ShaderUniformInitializeGlobalCase( m_context, "initialize", "Verify initialization of globals with non-constant expressions fails on ES.", m_glslVersion)); addChild(new ShaderConstantSequenceExpressionCase( m_context, "constant_sequence", "Verify that the sequence operator cannot be used as a constant expression.", m_glslVersion)); if (isGLSLVersionSupported(m_context.getRenderContext().getType(), GLSL_VERSION_320_ES)) { static const ShaderVariants used_variables_variants[] = { /* These variants should pass since the precision qualifiers match. * These variants require highp to be supported, so will not be run for GLSL_VERSION_100_ES. */ { GLSL_VERSION_300_ES, "", "gl_Position = vec4(1.0) + value;", "highp", "result = value;", true }, { GLSL_VERSION_300_ES, "highp", "gl_Position = vec4(1.0) + value;", "highp", "result = value;", true }, /* Use highp in vertex shaders, mediump in fragment shaders. Check variations as above. * These variants should fail since the precision qualifiers do not match, and matching is done * based on declaration - independent of static use. */ { GLSL_VERSION_100_ES, "", "gl_Position = vec4(1.0) + value;", "mediump", "result = value;", false }, { GLSL_VERSION_100_ES, "highp", "gl_Position = vec4(1.0) + value;", "mediump", "result = value;", false }, }; unsigned int used_variables_variants_count = sizeof(used_variables_variants) / sizeof(ShaderVariants); addChild(new ShaderUniformPrecisionLinkCase( m_context, "used_uniform_precision_matching", "Verify that linking fails if precision qualifiers on default uniform do not match", used_variables_variants, used_variables_variants_count, m_glslVersion)); } } } // deqp