/*------------------------------------------------------------------------- * 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 glcAggressiveShaderOptimizationsTests.cpp * \brief Conformance tests that checks if shader optimizations are not * overly aggressive. This is done by compering result of complex * trigonometric functions aproximation to shader buil */ /*-------------------------------------------------------------------*/ #include "glcAggressiveShaderOptimizationsTests.hpp" #include "deSharedPtr.hpp" #include "glsShaderExecUtil.hpp" #include "gluContextInfo.hpp" #include "gluDrawUtil.hpp" #include "gluPixelTransfer.hpp" #include "gluShaderProgram.hpp" #include "glwFunctions.hpp" #include "tcuImageCompare.hpp" #include "tcuRenderTarget.hpp" #include "tcuStringTemplate.hpp" #include "tcuSurface.hpp" #include "tcuTestLog.hpp" using namespace glw; namespace glcts { enum ShaderType { TEST_VERTEX_SHADER, TEST_FRAGMENT_SHADER }; struct TrigonometryCaseData { const char* testedFunction; const char* testedType; const char* colorComponents; ShaderType shaderType; }; class TrigonometryTestCase : public deqp::TestCase { public: TrigonometryTestCase(deqp::Context& context, const std::string& name, const TrigonometryCaseData& data); virtual ~TrigonometryTestCase(); IterateResult iterate(void); protected: glu::ProgramSources prepareSineSources(bool useBuiltin); glu::ProgramSources prepareCosineSources(bool useBuiltin); void renderAndGrabSurface(glu::ProgramSources sources, tcu::Surface& result) const; private: ShaderType m_shaderType; const char* m_testedFunction; std::map m_specializationMap; }; TrigonometryTestCase::TrigonometryTestCase(deqp::Context& context, const std::string& name, const TrigonometryCaseData& data) : deqp::TestCase(context, name.c_str(), ""), m_shaderType(data.shaderType), m_testedFunction(data.testedFunction) { glu::ContextType contextType = m_context.getRenderContext().getType(); glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(contextType); m_specializationMap["VERSION"] = glu::getGLSLVersionDeclaration(glslVersion); m_specializationMap["TYPE"] = data.testedType; m_specializationMap["COLOR_COMPONENTS"] = data.colorComponents; if (glu::contextSupports(contextType, glu::ApiType::es(3, 0)) || glu::isContextTypeGLCore(contextType)) { m_specializationMap["IN"] = "in"; m_specializationMap["OUT"] = "out"; m_specializationMap["ATTRIBUTE"] = "in"; m_specializationMap["FS_OUT_COLOR_NAME"] = "fragColor"; m_specializationMap["FS_OUT_COLOR_DECLARATION"] = "out vec4 fragColor;"; } else { m_specializationMap["IN"] = "varying"; m_specializationMap["OUT"] = "varying"; m_specializationMap["ATTRIBUTE"] = "attribute"; m_specializationMap["FS_OUT_COLOR_NAME"] = "gl_FragColor"; m_specializationMap["FS_OUT_COLOR_DECLARATION"] = ""; } } TrigonometryTestCase::~TrigonometryTestCase() { } glu::ProgramSources TrigonometryTestCase::prepareSineSources(bool useBuiltinSin) { const char* vsDefault = "${VERSION}\n" "${ATTRIBUTE} highp vec2 position;\n" "${ATTRIBUTE} highp vec3 baseColor;\n" "${OUT} vec4 color;\n" "void main (void) {\n" " color = vec4(baseColor, 1.0);\n" " gl_Position = vec4(position, 0.0, 1.0);\n" "}\n"; const char* vsCalculateSin = "${VERSION}\n" "${ATTRIBUTE} highp vec2 position;\n" "${ATTRIBUTE} highp vec3 baseColor;\n" "${OUT} vec4 color;\n" "${SIN_FUNCTION_DEFINITION_VS}\n" "void main (void) {\n" " const float M_2PI = 2.0 * 3.14159265358979323846;\n" " ${TYPE} c = baseColor.${COLOR_COMPONENTS} * M_2PI;\n" " ${TYPE} sin_c = ${SIN_FUNCTION_NAME}(c);\n" " \n" " color = vec4(0.0, 0.0, 0.0, 1.0);\n" " color.${COLOR_COMPONENTS} = sin_c * 0.5 + 0.5;\n" " gl_Position = vec4(position, 0.0, 1.0);\n" "}\n"; const char* fsDefault = "${VERSION}\n" "precision mediump float;\n" "${IN} vec4 color;\n" "${FS_OUT_COLOR_DECLARATION}\n" "void main (void) {\n" " ${FS_OUT_COLOR_NAME} = color;\n" "}\n"; const char* fsCalculateSin = "${VERSION}\n" "precision mediump float;\n" "${IN} vec4 color;\n" "${FS_OUT_COLOR_DECLARATION}\n\n" "${SIN_FUNCTION_DEFINITION_FS}\n" "void main (void) {\n" " const float M_2PI = 2.0 * 3.14159265358979323846;\n" " ${TYPE} c = color.${COLOR_COMPONENTS};\n" " ${TYPE} sin_c = ${SIN_FUNCTION_NAME}(c * M_2PI);\n" " \n" " ${FS_OUT_COLOR_NAME} =vec4(0.0, 0.0, 0.0, 1.0);\n" " ${FS_OUT_COLOR_NAME}.${COLOR_COMPONENTS} = sin_c * 0.5 + 0.5;\n" "}\n"; std::string vsTemplate; std::string fsTemplate; if (m_shaderType == TEST_VERTEX_SHADER) { vsTemplate = vsCalculateSin; fsTemplate = fsDefault; } else { vsTemplate = vsDefault; fsTemplate = fsCalculateSin; } if (useBuiltinSin) { m_specializationMap["SIN_FUNCTION_NAME"] = "sin"; m_specializationMap["SIN_FUNCTION_DEFINITION_VS"] = ""; m_specializationMap["SIN_FUNCTION_DEFINITION_FS"] = ""; } else { std::string sinFunctionDefinitionVS = "${TYPE} ${SIN_FUNCTION_NAME}(${TYPE} c) {\n" " ${TYPE} sin_c = ${TYPE}(0.0);\n" " float sign = 1.0;\n" " float fact;\n" " float fact_of;\n" " \n" " // Taylors series expansion for sin \n" " for(int i = 0; i < 12; i++) {\n" " fact = 1.0;\n" " for(int j = 2; j <= 23; j++)\n" " if (j <= 2 * i + 1)\n" " fact *= float(j);\n" " \n" " sin_c += sign * pow(c, ${TYPE}(2.0 * float(i) + 1.0)) / fact;\n" " sign *= -1.0;\n" " }\n" " return sin_c;\n" "}"; std::string sinFunctionDefinitionFS = "float lerpHelper(float a, float b, float weight) {\n" " return a + (b - a) * weight;\n" "}\n" "float sinLerpHelper(int index, float weight) {\n" " float sArray[17];\n" " sArray[0] = 0.0;\n" " sArray[1] = 0.382683;\n" " sArray[2] = 0.707107;\n" " sArray[3] = 0.92388;\n" " sArray[4] = 1.0;\n" " sArray[5] = 0.92388;\n" " sArray[6] = 0.707107;\n" " sArray[7] = 0.382683;\n" " sArray[8] = 0.0;\n" " sArray[9] = -0.382683;\n" " sArray[10] = -0.707107;\n" " sArray[11] = -0.92388;\n" " sArray[12] = -1.0;\n" " sArray[13] = -0.923879;\n" " sArray[14] = -0.707107;\n" " sArray[15] = -0.382683;\n" " sArray[16] = 0.0;\n" " \n" " if (index == 0)\n" " return lerpHelper(sArray[0], sArray[1], weight);\n" " if (index == 1)\n" " return lerpHelper(sArray[1], sArray[2], weight);\n" " if (index == 2)\n" " return lerpHelper(sArray[2], sArray[3], weight);\n" " if (index == 3)\n" " return lerpHelper(sArray[3], sArray[4], weight);\n" " if (index == 4)\n" " return lerpHelper(sArray[4], sArray[5], weight);\n" " if (index == 5)\n" " return lerpHelper(sArray[5], sArray[6], weight);\n" " if (index == 6)\n" " return lerpHelper(sArray[6], sArray[7], weight);\n" " if (index == 7)\n" " return lerpHelper(sArray[7], sArray[8], weight);\n" " if (index == 8)\n" " return lerpHelper(sArray[8], sArray[9], weight);\n" " if (index == 9)\n" " return lerpHelper(sArray[9], sArray[10], weight);\n" " if (index == 10)\n" " return lerpHelper(sArray[10], sArray[11], weight);\n" " if (index == 11)\n" " return lerpHelper(sArray[11], sArray[12], weight);\n" " if (index == 12)\n" " return lerpHelper(sArray[12], sArray[13], weight);\n" " if (index == 13)\n" " return lerpHelper(sArray[13], sArray[14], weight);\n" " if (index == 14)\n" " return lerpHelper(sArray[14], sArray[15], weight);\n" " if (index == 15)\n" " return lerpHelper(sArray[15], sArray[16], weight);\n" " return sArray[16];\n" "}\n" "${TYPE} ${SIN_FUNCTION_NAME}(${TYPE} c) {\n" " ${TYPE} arrVal = c * 2.546478971;\n" " ${TYPE} weight = arrVal - floor(arrVal);\n" " ${TYPE} sin_c = ${TYPE}(0.0);\n" " ${INTERPOLATE_SIN}" " return sin_c;\n" "}"; if (m_specializationMap["TYPE"] == "float") { m_specializationMap["INTERPOLATE_SIN"] = "\n" " int index = int(floor(arrVal));\n" " sin_c = sinLerpHelper(index, weight);\n"; } else if (m_specializationMap["TYPE"] == "vec2") { m_specializationMap["INTERPOLATE_SIN"] = "\n" " int indexX = int(floor(arrVal.x));\n" " sin_c.x = sinLerpHelper(indexX, weight.x);\n" " int indexY = int(floor(arrVal.y));\n" " sin_c.y = sinLerpHelper(indexY, weight.y);\n"; } else if (m_specializationMap["TYPE"] == "vec3") { m_specializationMap["INTERPOLATE_SIN"] = "\n" " int indexX = int(floor(arrVal.x));\n" " sin_c.x = sinLerpHelper(indexX, weight.x);\n" " int indexY = int(floor(arrVal.y));\n" " sin_c.y = sinLerpHelper(indexY, weight.y);\n" " int indexZ = int(floor(arrVal.z));\n" " sin_c.z = sinLerpHelper(indexZ, weight.z);\n"; } m_specializationMap["SIN_FUNCTION_NAME"] = "calculateSin"; m_specializationMap["SIN_FUNCTION_DEFINITION_VS"] = tcu::StringTemplate(sinFunctionDefinitionVS).specialize(m_specializationMap); m_specializationMap["SIN_FUNCTION_DEFINITION_FS"] = tcu::StringTemplate(sinFunctionDefinitionFS).specialize(m_specializationMap); } // Specialize shader templates vsTemplate = tcu::StringTemplate(vsTemplate).specialize(m_specializationMap); fsTemplate = tcu::StringTemplate(fsTemplate).specialize(m_specializationMap); return glu::makeVtxFragSources(vsTemplate.c_str(), fsTemplate.c_str()); } glu::ProgramSources TrigonometryTestCase::prepareCosineSources(bool useBuiltinCos) { const char* vsDefault = "${VERSION}\n" "${ATTRIBUTE} highp vec2 position;\n" "${ATTRIBUTE} highp vec3 baseColor;\n" "${OUT} vec4 color;\n" "void main (void) {\n" " color = vec4(baseColor, 1.0);\n" " gl_Position = vec4(position, 0.0, 1.0);\n" "}\n"; const char* vsCalculateCos = "${VERSION}\n" "${ATTRIBUTE} highp vec2 position;\n" "${ATTRIBUTE} highp vec3 baseColor;\n" "${OUT} vec4 color;\n" "${COS_FUNCTION_DEFINITION_VS}\n" "void main (void) {\n" " const float M_2PI = 2.0 * 3.14159265358979323846;\n" " ${TYPE} c = baseColor.${COLOR_COMPONENTS};\n" " ${TYPE} cos_c = ${COS_FUNCTION_NAME}(c * M_2PI);\n" " \n" " color = vec4(0.0, 0.0, 0.0, 1.0);\n" " color.${COLOR_COMPONENTS} = cos_c * 0.5 + 0.5;\n" " gl_Position = vec4(position, 0.0, 1.0);\n" "}\n"; const char* fsDefault = "${VERSION}\n" "precision mediump float;\n" "${IN} vec4 color;\n" "${FS_OUT_COLOR_DECLARATION}\n" "void main (void) {\n" " ${FS_OUT_COLOR_NAME} = color;\n" "}\n"; const char* fsCalculateCos = "${VERSION}\n" "precision mediump float;\n" "${IN} vec4 color;\n" "${FS_OUT_COLOR_DECLARATION}\n\n" "// function definitions \n" "${COS_FUNCTION_DEFINITION_FS}\n" "${TYPE} preprocessColor(${TYPE} c) {\n" " ${PREPROCESS_COLOR};\n" " return c;\n" "}\n\n" "void main (void) {\n" " const float M_2PI = 2.0 * 3.14159265358979323846;\n" " ${TYPE} c = preprocessColor(color.${COLOR_COMPONENTS});\n" " ${TYPE} cos_c = ${COS_FUNCTION_NAME}(c * M_2PI);\n" " \n" " ${FS_OUT_COLOR_NAME} = vec4(0.0, 0.0, 0.0, 1.0);\n" " ${FS_OUT_COLOR_NAME}.${COLOR_COMPONENTS} = cos_c * 0.5 + 0.5;\n" "}\n"; std::string vsTemplate; std::string fsTemplate; if (m_shaderType == TEST_VERTEX_SHADER) { vsTemplate = vsCalculateCos; fsTemplate = fsDefault; } else { vsTemplate = vsDefault; fsTemplate = fsCalculateCos; } if (useBuiltinCos) { m_specializationMap["PREPROCESS_COLOR"] = ""; m_specializationMap["COS_FUNCTION_NAME"] = "cos"; m_specializationMap["COS_FUNCTION_DEFINITION_VS"] = ""; m_specializationMap["COS_FUNCTION_DEFINITION_FS"] = ""; } else { std::string cosFunctionDefinitionVS = "${TYPE} ${COS_FUNCTION_NAME}(${TYPE} c) {\n" " ${TYPE} cos_c = ${TYPE}(1.0);\n" " float sign = -1.0;\n" " float fact = 1.0;\n" " \n" " for(int i = 2; i <= 20; i += 2) {\n" " fact *= float(i)*float(i-1);\n" " cos_c += sign*pow(c, ${TYPE}(float(i)))/fact;\n" " sign = -sign;\n" " }\n" " return cos_c;\n" "}"; std::string cosFunctionDefinitionFS = "${TYPE} ${COS_FUNCTION_NAME}(${TYPE} c) {\n" " ${TYPE} cos_c = ${TYPE}(-1.0);\n" " float sign = 1.0;\n" " float fact_even = 1.0;\n" " float fact_odd = 1.0;\n" " ${TYPE} sum;\n" " ${TYPE} exp;\n" " \n" " for(int i = 2; i <= 10; i += 2) {\n" " fact_even *= float(i);\n" " fact_odd *= float(i-1);\n" " exp = ${TYPE}(float(i/2));\n" " sum = sign * pow(abs(c), exp)/fact_even;\n" " cos_c += pow(abs(c), exp)*(sum/fact_odd);\n" " sign = -sign;\n" " }\n" " return cos_c;\n" "}"; m_specializationMap["PREPROCESS_COLOR"] = "c = (fract(abs(c)) - 0.5)"; m_specializationMap["COS_FUNCTION_NAME"] = "calculateCos"; m_specializationMap["COS_FUNCTION_DEFINITION_VS"] = tcu::StringTemplate(cosFunctionDefinitionVS).specialize(m_specializationMap); m_specializationMap["COS_FUNCTION_DEFINITION_FS"] = tcu::StringTemplate(cosFunctionDefinitionFS).specialize(m_specializationMap); } // Specialize shader templates vsTemplate = tcu::StringTemplate(vsTemplate).specialize(m_specializationMap); fsTemplate = tcu::StringTemplate(fsTemplate).specialize(m_specializationMap); return glu::makeVtxFragSources(vsTemplate.c_str(), fsTemplate.c_str()); } void TrigonometryTestCase::renderAndGrabSurface(glu::ProgramSources sources, tcu::Surface& result) const { static const deUint16 quadIndices[] = { 0, 1, 2, 2, 1, 3 }; static const float positions[] = { -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0 }; static const float baseColors[] = { 1.0, 0.0, 0.25, 0.75, 0.25, 1.0, 0.0, 1.0, 0.75, 0.25, 0.5, 0.0 }; glu::RenderContext& renderContext = m_context.getRenderContext(); const glw::Functions& gl = renderContext.getFunctions(); glu::ShaderProgram testProgram(renderContext, sources); if (!testProgram.isOk()) { m_testCtx.getLog() << testProgram; TCU_FAIL("Test program compilation failed"); } // Render gl.useProgram(testProgram.getProgram()); const glu::VertexArrayBinding vertexArrays[] = { glu::va::Float("position", 2, 4, 0, positions), glu::va::Float("baseColor", 3, 4, 0, baseColors) }; glu::draw(renderContext, testProgram.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), vertexArrays, glu::pr::TriangleStrip(DE_LENGTH_OF_ARRAY(quadIndices), quadIndices)); // Grab surface glu::readPixels(renderContext, 0, 0, result.getAccess()); } tcu::TestNode::IterateResult TrigonometryTestCase::iterate(void) { glu::RenderContext& renderContext = m_context.getRenderContext(); const glw::Functions& gl = renderContext.getFunctions(); int renderWidth = 64; int renderHeight = 64; if (renderWidth > m_context.getRenderTarget().getWidth()) renderWidth = m_context.getRenderTarget().getWidth(); if (renderHeight > m_context.getRenderTarget().getHeight()) renderHeight = m_context.getRenderTarget().getHeight(); bool isSin = std::string(m_testedFunction) == "sin"; gl.viewport(0, 0, renderWidth, renderHeight); // Use program that will call trigonometric function aproximation tcu::Surface testSurface(renderWidth, renderHeight); if (isSin) renderAndGrabSurface(prepareSineSources(false), testSurface); else renderAndGrabSurface(prepareCosineSources(false), testSurface); // Use reference program that will call builtin function tcu::Surface referenceSurface(renderWidth, renderHeight); if (isSin) renderAndGrabSurface(prepareSineSources(true), referenceSurface); else renderAndGrabSurface(prepareCosineSources(true), referenceSurface); // Compare surfaces qpTestResult testResult = QP_TEST_RESULT_FAIL; if (tcu::fuzzyCompare(m_testCtx.getLog(), "Result", "Image comparison result", referenceSurface, testSurface, 0.05f, tcu::COMPARE_LOG_RESULT)) testResult = QP_TEST_RESULT_PASS; m_testCtx.setTestResult(testResult, qpGetTestResultName(testResult)); return STOP; } AggressiveShaderOptimizationsTests::AggressiveShaderOptimizationsTests(deqp::Context& context) : TestCaseGroup(context, "aggressive_optimizations", "checks if shader optimizations are not overly aggressive") { } AggressiveShaderOptimizationsTests::~AggressiveShaderOptimizationsTests() { } void AggressiveShaderOptimizationsTests::init(void) { TrigonometryCaseData trigonometryCases[] = { { "sin", "float", "r", TEST_VERTEX_SHADER }, { "sin", "float", "r", TEST_FRAGMENT_SHADER }, { "sin", "vec2", "rg", TEST_VERTEX_SHADER }, { "sin", "vec2", "rg", TEST_FRAGMENT_SHADER }, { "sin", "vec3", "rgb", TEST_VERTEX_SHADER }, { "sin", "vec3", "rgb", TEST_FRAGMENT_SHADER }, { "cos", "float", "r", TEST_VERTEX_SHADER }, { "cos", "float", "r", TEST_FRAGMENT_SHADER }, { "cos", "vec2", "rg", TEST_VERTEX_SHADER }, { "cos", "vec2", "rg", TEST_FRAGMENT_SHADER }, { "cos", "vec3", "rgb", TEST_VERTEX_SHADER }, { "cos", "vec3", "rgb", TEST_FRAGMENT_SHADER }, }; for (int i = 0; i < DE_LENGTH_OF_ARRAY(trigonometryCases); ++i) { const TrigonometryCaseData& tcd = trigonometryCases[i]; std::string shaderType = (tcd.shaderType == TEST_VERTEX_SHADER) ? "_vert" : "_frag"; std::string name = std::string(tcd.testedFunction) + "_" + tcd.testedType + shaderType; addChild(new TrigonometryTestCase(m_context, name, tcd)); } } } // glcts namespace