/*------------------------------------------------------------------------- * OpenGL Conformance Test Suite * ----------------------------- * * Copyright (c) 2017 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 glcShaderConstExprTests.cpp * \brief Declares shader constant expressions tests. */ /*-------------------------------------------------------------------*/ #include "glcShaderConstExprTests.hpp" #include "deMath.h" #include "deSharedPtr.hpp" #include "glsShaderExecUtil.hpp" #include "gluContextInfo.hpp" #include "gluShaderUtil.hpp" #include "tcuFloat.hpp" #include "tcuStringTemplate.hpp" #include "tcuTestLog.hpp" #include using namespace deqp::gls::ShaderExecUtil; namespace glcts { namespace ShaderConstExpr { struct TestParams { const char* name; const char* expression; glu::DataType inType; int minComponents; int maxComponents; glu::DataType outType; union { float outputFloat; int outputInt; }; }; struct ShaderExecutorParams { deqp::Context* context; std::string caseName; std::string source; glu::DataType outType; union { float outputFloat; int outputInt; }; }; template class ExecutorTestCase : public deqp::TestCase { public: ExecutorTestCase(deqp::Context& context, const char* name, glu::ShaderType shaderType, const ShaderSpec& shaderSpec, OutputType expectedOutput); virtual ~ExecutorTestCase(void); virtual tcu::TestNode::IterateResult iterate(void); protected: void validateOutput(de::SharedPtr executor); glu::ShaderType m_shaderType; ShaderSpec m_shaderSpec; OutputType m_expectedOutput; }; template ExecutorTestCase::ExecutorTestCase(deqp::Context& context, const char* name, glu::ShaderType shaderType, const ShaderSpec& shaderSpec, OutputType expectedOutput) : deqp::TestCase(context, name, "") , m_shaderType(shaderType) , m_shaderSpec(shaderSpec) , m_expectedOutput(expectedOutput) { } template ExecutorTestCase::~ExecutorTestCase(void) { } template <> void ExecutorTestCase::validateOutput(de::SharedPtr executor) { float result = 0.0f; void* const outputs = &result; executor->execute(1, DE_NULL, &outputs); const float epsilon = 0.01f; if (de::abs(m_expectedOutput - result) > epsilon) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Expected: " << m_expectedOutput << " (" << tcu::toHex(tcu::Float32(m_expectedOutput).bits()) << ") but constant expresion returned: " << result << " (" << tcu::toHex(tcu::Float32(result).bits()) << "), used " << epsilon << " epsilon for comparison" << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); return; } m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return; } template <> void ExecutorTestCase::validateOutput(de::SharedPtr executor) { int result = 0; void* const outputs = &result; executor->execute(1, DE_NULL, &outputs); if (result == m_expectedOutput) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return; } m_context.getTestContext().getLog() << tcu::TestLog::Message << "Expected: " << m_expectedOutput << " but constant expresion returned: " << result << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } template tcu::TestNode::IterateResult ExecutorTestCase::iterate(void) { de::SharedPtr executor(createExecutor(m_context.getRenderContext(), m_shaderType, m_shaderSpec)); DE_ASSERT(executor.get()); executor->log(m_context.getTestContext().getLog()); try { if (!executor->isOk()) TCU_FAIL("Compilation failed"); executor->useProgram(); validateOutput(executor); } catch (const tcu::NotSupportedError& e) { m_testCtx.getLog() << tcu::TestLog::Message << e.what() << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, e.getMessage()); } catch (const tcu::TestError& e) { m_testCtx.getLog() << tcu::TestLog::Message << e.what() << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, e.getMessage()); } return tcu::TestNode::STOP; } template void createTestCasesForAllShaderTypes(const ShaderExecutorParams& params, std::vector& outputTests) { DE_ASSERT(params.context); deqp::Context& context = *(params.context); glu::ContextType contextType = context.getRenderContext().getType(); ShaderSpec shaderSpec; shaderSpec.version = glu::getContextTypeGLSLVersion(contextType); shaderSpec.source = params.source; shaderSpec.outputs.push_back(Symbol("out0", glu::VarType(params.outType, glu::PRECISION_HIGHP))); // Construct list of shaders for which tests can be created std::vector shaderTypes; if (glu::contextSupports(contextType, glu::ApiType::core(4, 3))) { shaderTypes.push_back(glu::SHADERTYPE_VERTEX); shaderTypes.push_back(glu::SHADERTYPE_FRAGMENT); shaderTypes.push_back(glu::SHADERTYPE_COMPUTE); shaderTypes.push_back(glu::SHADERTYPE_GEOMETRY); shaderTypes.push_back(glu::SHADERTYPE_TESSELLATION_CONTROL); shaderTypes.push_back(glu::SHADERTYPE_TESSELLATION_EVALUATION); } else if (glu::contextSupports(contextType, glu::ApiType::es(3, 2))) { shaderSpec.version = glu::GLSL_VERSION_320_ES; shaderTypes.push_back(glu::SHADERTYPE_GEOMETRY); shaderTypes.push_back(glu::SHADERTYPE_TESSELLATION_CONTROL); shaderTypes.push_back(glu::SHADERTYPE_TESSELLATION_EVALUATION); } else if (glu::contextSupports(contextType, glu::ApiType::es(3, 1))) { shaderSpec.version = glu::GLSL_VERSION_310_ES; shaderTypes.push_back(glu::SHADERTYPE_COMPUTE); shaderTypes.push_back(glu::SHADERTYPE_GEOMETRY); shaderTypes.push_back(glu::SHADERTYPE_TESSELLATION_CONTROL); shaderTypes.push_back(glu::SHADERTYPE_TESSELLATION_EVALUATION); } else { shaderTypes.push_back(glu::SHADERTYPE_VERTEX); shaderTypes.push_back(glu::SHADERTYPE_FRAGMENT); } shaderSpec.globalDeclarations += "precision highp float;\n"; for (std::size_t typeIndex = 0; typeIndex < shaderTypes.size(); ++typeIndex) { glu::ShaderType shaderType = shaderTypes[typeIndex]; std::string caseName(params.caseName + '_' + getShaderTypeName(shaderType)); outputTests.push_back( new ExecutorTestCase(context, caseName.c_str(), shaderType, shaderSpec, static_cast(params.outputFloat))); } } void createTests(deqp::Context& context, const TestParams* cases, int numCases, const char* shaderTemplateSrc, const char* casePrefix, std::vector& outputTests) { const tcu::StringTemplate shaderTemplate(shaderTemplateSrc); const char* componentAccess[] = { "", ".y", ".z", ".w" }; ShaderExecutorParams shaderExecutorParams; shaderExecutorParams.context = &context; for (int caseIndex = 0; caseIndex < numCases; caseIndex++) { const TestParams& testCase = cases[caseIndex]; const std::string baseName = testCase.name; const int minComponents = testCase.minComponents; const int maxComponents = testCase.maxComponents; const glu::DataType inType = testCase.inType; const std::string expression = testCase.expression; // Check for presence of func(vec, scalar) style specialization, // use as gatekeeper for applying said specialization const bool alwaysScalar = expression.find("${MT}") != std::string::npos; std::map shaderTemplateParams; shaderTemplateParams["CASE_BASE_TYPE"] = glu::getDataTypeName(testCase.outType); shaderExecutorParams.outType = testCase.outType; shaderExecutorParams.outputFloat = testCase.outputFloat; for (int component = minComponents - 1; component < maxComponents; component++) { // Get type name eg. float, vec2, vec3, vec4 (same for other primitive types) glu::DataType dataType = static_cast(inType + component); std::string typeName = glu::getDataTypeName(dataType); // ${T} => final type, ${MT} => final type but with scalar version usable even when T is a vector std::map expressionTemplateParams; expressionTemplateParams["T"] = typeName; expressionTemplateParams["MT"] = typeName; const tcu::StringTemplate expressionTemplate(expression); // Add vector access to expression as needed shaderTemplateParams["CASE_EXPRESSION"] = expressionTemplate.specialize(expressionTemplateParams) + componentAccess[component]; { // Add type to case name if we are generating multiple versions shaderExecutorParams.caseName = (casePrefix + baseName); if (minComponents != maxComponents) shaderExecutorParams.caseName += ("_" + typeName); shaderExecutorParams.source = shaderTemplate.specialize(shaderTemplateParams); if (shaderExecutorParams.outType == glu::TYPE_FLOAT) createTestCasesForAllShaderTypes(shaderExecutorParams, outputTests); else createTestCasesForAllShaderTypes(shaderExecutorParams, outputTests); } // Deal with functions that allways accept one ore more scalar parameters even when others are vectors if (alwaysScalar && component > 0) { shaderExecutorParams.caseName = casePrefix + baseName + "_" + typeName + "_" + glu::getDataTypeName(inType); expressionTemplateParams["MT"] = glu::getDataTypeName(inType); shaderTemplateParams["CASE_EXPRESSION"] = expressionTemplate.specialize(expressionTemplateParams) + componentAccess[component]; shaderExecutorParams.source = shaderTemplate.specialize(shaderTemplateParams); if (shaderExecutorParams.outType == glu::TYPE_FLOAT) createTestCasesForAllShaderTypes(shaderExecutorParams, outputTests); else createTestCasesForAllShaderTypes(shaderExecutorParams, outputTests); } } // component loop } } } // namespace ShaderConstExpr ShaderConstExprTests::ShaderConstExprTests(deqp::Context& context) : deqp::TestCaseGroup(context, "constant_expressions", "Constant expressions") { } ShaderConstExprTests::~ShaderConstExprTests(void) { } void ShaderConstExprTests::init(void) { // Needed for autogenerating shader code for increased component counts DE_STATIC_ASSERT(glu::TYPE_FLOAT + 1 == glu::TYPE_FLOAT_VEC2); DE_STATIC_ASSERT(glu::TYPE_FLOAT + 2 == glu::TYPE_FLOAT_VEC3); DE_STATIC_ASSERT(glu::TYPE_FLOAT + 3 == glu::TYPE_FLOAT_VEC4); DE_STATIC_ASSERT(glu::TYPE_INT + 1 == glu::TYPE_INT_VEC2); DE_STATIC_ASSERT(glu::TYPE_INT + 2 == glu::TYPE_INT_VEC3); DE_STATIC_ASSERT(glu::TYPE_INT + 3 == glu::TYPE_INT_VEC4); DE_STATIC_ASSERT(glu::TYPE_UINT + 1 == glu::TYPE_UINT_VEC2); DE_STATIC_ASSERT(glu::TYPE_UINT + 2 == glu::TYPE_UINT_VEC3); DE_STATIC_ASSERT(glu::TYPE_UINT + 3 == glu::TYPE_UINT_VEC4); DE_STATIC_ASSERT(glu::TYPE_BOOL + 1 == glu::TYPE_BOOL_VEC2); DE_STATIC_ASSERT(glu::TYPE_BOOL + 2 == glu::TYPE_BOOL_VEC3); DE_STATIC_ASSERT(glu::TYPE_BOOL + 3 == glu::TYPE_BOOL_VEC4); // ${T} => final type, ${MT} => final type but with scalar version usable even when T is a vector const ShaderConstExpr::TestParams baseCases[] = { { "radians", "radians(${T} (90.0))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { deFloatRadians(90.0f) } }, { "degrees", "degrees(${T} (2.0))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { deFloatDegrees(2.0f) } }, { "sin", "sin(${T} (3.0))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { deFloatSin(3.0f) } }, { "cos", "cos(${T} (3.2))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { deFloatCos(3.2f) } }, { "asin", "asin(${T} (0.0))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { deFloatAsin(0.0f) } }, { "acos", "acos(${T} (1.0))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { deFloatAcos(1.0f) } }, { "pow", "pow(${T} (1.7), ${T} (3.5))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { deFloatPow(1.7f, 3.5f) } }, { "exp", "exp(${T} (4.2))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { deFloatExp(4.2f) } }, { "log", "log(${T} (42.12))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { deFloatLog(42.12f) } }, { "exp2", "exp2(${T} (6.7))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { deFloatExp2(6.7f) } }, { "log2", "log2(${T} (100.0))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { deFloatLog2(100.0f) } }, { "sqrt", "sqrt(${T} (10.0))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { deFloatSqrt(10.0f) } }, { "inversesqrt", "inversesqrt(${T} (10.0))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { deFloatRsq(10.0f) } }, { "abs", "abs(${T} (-42))", glu::TYPE_INT, 1, 4, glu::TYPE_INT, { 42 } }, { "sign", "sign(${T} (-18.0))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { -1.0f } }, { "floor", "floor(${T} (37.3))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { deFloatFloor(37.3f) } }, { "trunc", "trunc(${T} (-1.8))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { -1.0f } }, { "round", "round(${T} (42.1))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { 42.0f } }, { "ceil", "ceil(${T} (82.2))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { deFloatCeil(82.2f) } }, { "mod", "mod(${T} (87.65), ${MT} (3.7))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { deFloatMod(87.65f, 3.7f) } }, { "min", "min(${T} (12.3), ${MT} (32.1))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { 12.3f } }, { "max", "max(${T} (12.3), ${MT} (32.1))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { 32.1f } }, { "clamp", "clamp(${T} (42.1), ${MT} (10.0), ${MT} (15.0))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_FLOAT, { 15.0f } }, { "length_float", "length(1.0)", glu::TYPE_FLOAT, 1, 1, glu::TYPE_FLOAT, { 1.0f } }, { "length_vec2", "length(vec2(1.0))", glu::TYPE_FLOAT, 1, 1, glu::TYPE_FLOAT, { deFloatSqrt(2.0f) } }, { "length_vec3", "length(vec3(1.0))", glu::TYPE_FLOAT, 1, 1, glu::TYPE_FLOAT, { deFloatSqrt(3.0f) } }, { "length_vec4", "length(vec4(1.0))", glu::TYPE_FLOAT, 1, 1, glu::TYPE_FLOAT, { deFloatSqrt(4.0f) } }, { "dot_float", "dot(1.0, 1.0)", glu::TYPE_FLOAT, 1, 1, glu::TYPE_FLOAT, { 1.0f } }, { "dot_vec2", "dot(vec2(1.0), vec2(1.0))", glu::TYPE_FLOAT, 1, 1, glu::TYPE_FLOAT, { 2.0f } }, { "dot_vec3", "dot(vec3(1.0), vec3(1.0))", glu::TYPE_FLOAT, 1, 1, glu::TYPE_FLOAT, { 3.0f } }, { "dot_vec4", "dot(vec4(1.0), vec4(1.0))", glu::TYPE_FLOAT, 1, 1, glu::TYPE_FLOAT, { 4.0f } }, { "normalize_float", "normalize(1.0)", glu::TYPE_FLOAT, 1, 1, glu::TYPE_FLOAT, { 1.0f } }, { "normalize_vec2", "normalize(vec2(1.0)).x", glu::TYPE_FLOAT, 1, 1, glu::TYPE_FLOAT, { deFloatRsq(2.0f) } }, { "normalize_vec3", "normalize(vec3(1.0)).x", glu::TYPE_FLOAT, 1, 1, glu::TYPE_FLOAT, { deFloatRsq(3.0f) } }, { "normalize_vec4", "normalize(vec4(1.0)).x", glu::TYPE_FLOAT, 1, 1, glu::TYPE_FLOAT, { deFloatRsq(4.0f) } }, }; const ShaderConstExpr::TestParams arrayCases[] = { { "radians", "radians(${T} (60.0))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatRadians(60.0f) } }, { "degrees", "degrees(${T} (0.11))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatDegrees(0.11f) } }, { "sin", "${T} (1.0) + sin(${T} (0.7))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { 1.0f + deFloatSin(0.7f) } }, { "cos", "${T} (1.0) + cos(${T} (0.7))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { 1.0f + deFloatCos(0.7f) } }, { "asin", "asin(${T} (0.9))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatAsin(0.9f) } }, { "acos", "acos(${T} (-0.5))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatAcos(-0.5f) } }, { "pow", "pow(${T} (2.0), ${T} (2.0))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatPow(2.0f, 2.0f) } }, { "exp", "exp(${T} (1.2))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatExp(1.2f) } }, { "log", "log(${T} (8.0))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatLog(8.0f) } }, { "exp2", "exp2(${T} (2.1))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatExp2(2.1f) } }, { "log2", "log2(${T} (9.0))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatLog2(9.0) } }, { "sqrt", "sqrt(${T} (4.5))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatSqrt(4.5f) } }, { "inversesqrt", "inversesqrt(${T} (0.26))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatRsq(0.26f) } }, { "abs", "abs(${T} (-2))", glu::TYPE_INT, 1, 4, glu::TYPE_INT, { 2 } }, { "sign", "sign(${T} (18.0))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatSign(18.0f) } }, { "floor", "floor(${T} (3.3))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatFloor(3.3f) } }, { "trunc", "trunc(${T} (2.8))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { 2 } }, { "round", "round(${T} (2.2))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatRound(2.2f) } }, { "ceil", "ceil(${T} (2.2))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatCeil(2.2f) } }, { "mod", "mod(${T} (7.1), ${MT} (4.0))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatMod(7.1f, 4.0f) } }, { "min", "min(${T} (2.3), ${MT} (3.1))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatMin(2.3f, 3.1f) } }, { "max", "max(${T} (2.3), ${MT} (3.1))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { deFloatMax(2.3f, 3.1f) } }, { "clamp", "clamp(${T} (4.1), ${MT} (2.1), ${MT} (3.1))", glu::TYPE_FLOAT, 1, 4, glu::TYPE_INT, { 3 } }, { "length_float", "length(2.1)", glu::TYPE_FLOAT, 1, 1, glu::TYPE_INT, { 2 } }, { "length_vec2", "length(vec2(1.0))", glu::TYPE_FLOAT, 1, 1, glu::TYPE_INT, { deFloatSqrt(2.0f) } }, { "length_vec3", "length(vec3(1.0))", glu::TYPE_FLOAT, 1, 1, glu::TYPE_INT, { deFloatSqrt(3.0f) } }, { "length_vec4", "length(vec4(1.0))", glu::TYPE_FLOAT, 1, 1, glu::TYPE_INT, { deFloatSqrt(4.0f) } }, { "dot_float", "dot(1.0, 1.0)", glu::TYPE_FLOAT, 1, 1, glu::TYPE_INT, { 1 } }, { "dot_vec2", "dot(vec2(1.0), vec2(1.01))", glu::TYPE_FLOAT, 1, 1, glu::TYPE_INT, { 2 } }, { "dot_vec3", "dot(vec3(1.0), vec3(1.1))", glu::TYPE_FLOAT, 1, 1, glu::TYPE_INT, { 3 } }, { "dot_vec4", "dot(vec4(1.0), vec4(1.1))", glu::TYPE_FLOAT, 1, 1, glu::TYPE_INT, { 4 } }, { "normalize_float", "${T} (1.0) + normalize(2.0)", glu::TYPE_FLOAT, 1, 1, glu::TYPE_INT, { 2 } }, { "normalize_vec2", "${T} (1.0) + normalize(vec2(1.0)).x", glu::TYPE_FLOAT, 1, 1, glu::TYPE_INT, { 1.0f + deFloatRsq(2.0f) } }, { "normalize_vec3", "${T} (1.0) + normalize(vec3(1.0)).x", glu::TYPE_FLOAT, 1, 1, glu::TYPE_INT, { 1.0f + deFloatRsq(3.0f) } }, { "normalize_vec4", "${T} (1.0) + normalize(vec4(1.0)).x", glu::TYPE_FLOAT, 1, 1, glu::TYPE_INT, { 1.0f + deFloatRsq(4.0f) } }, }; const char* basicShaderTemplate = "const ${CASE_BASE_TYPE} cval = ${CASE_EXPRESSION};\n" "out0 = cval;\n"; std::vector children; ShaderConstExpr::createTests(m_context, baseCases, DE_LENGTH_OF_ARRAY(baseCases), basicShaderTemplate, "basic_", children); const char* arrayShaderTemplate = "float array[int(${CASE_EXPRESSION})];\n" "out0 = array.length();\n"; ShaderConstExpr::createTests(m_context, arrayCases, DE_LENGTH_OF_ARRAY(arrayCases), arrayShaderTemplate, "array_", children); for (std::size_t i = 0; i < children.size(); i++) addChild(children[i]); } } // glcts