/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 2.0 Module * ------------------------------------------------- * * Copyright 2014 The Android Open Source Project * * 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 Shader return statement tests. *//*--------------------------------------------------------------------*/ #include "es2fShaderReturnTests.hpp" #include "glsShaderRenderCase.hpp" #include "tcuStringTemplate.hpp" #include #include #include using tcu::StringTemplate; using std::map; using std::string; using std::ostringstream; using namespace glu; using namespace deqp::gls; namespace deqp { namespace gles2 { namespace Functional { enum ReturnMode { RETURNMODE_ALWAYS = 0, RETURNMODE_NEVER, RETURNMODE_DYNAMIC, RETURNMODE_LAST }; enum RequireFlags { REQUIRE_DYNAMIC_LOOPS = (1<<0), }; // Evaluation functions inline void evalReturnAlways (ShaderEvalContext& c) { c.color.xyz() = c.coords.swizzle(0,1,2); } inline void evalReturnNever (ShaderEvalContext& c) { c.color.xyz() = c.coords.swizzle(3,2,1); } inline void evalReturnDynamic (ShaderEvalContext& c) { c.color.xyz() = (c.coords.x()+c.coords.y() >= 0.0f) ? c.coords.swizzle(0,1,2) : c.coords.swizzle(3,2,1); } static ShaderEvalFunc getEvalFunc (ReturnMode mode) { switch (mode) { case RETURNMODE_ALWAYS: return evalReturnAlways; case RETURNMODE_NEVER: return evalReturnNever; case RETURNMODE_DYNAMIC: return evalReturnDynamic; default: DE_ASSERT(DE_FALSE); return (ShaderEvalFunc)DE_NULL; } } class ShaderReturnCase : public ShaderRenderCase { public: ShaderReturnCase (Context& context, const char* name, const char* description, bool isVertexCase, const char* shaderSource, ShaderEvalFunc evalFunc, deUint32 requirements = 0); virtual ~ShaderReturnCase (void); void init (void); private: const deUint32 m_requirements; }; ShaderReturnCase::ShaderReturnCase (Context& context, const char* name, const char* description, bool isVertexCase, const char* shaderSource, ShaderEvalFunc evalFunc, deUint32 requirements) : ShaderRenderCase (context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name, description, isVertexCase, evalFunc) , m_requirements (requirements) { if (isVertexCase) { m_vertShaderSource = shaderSource; m_fragShaderSource = "varying mediump vec4 v_color;\n\n" "void main (void)\n" "{\n" " gl_FragColor = v_color;\n" "}\n"; } else { m_fragShaderSource = shaderSource; m_vertShaderSource = "attribute highp vec4 a_position;\n" "attribute highp vec4 a_coords;\n" "varying mediump vec4 v_coords;\n\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" " v_coords = a_coords;\n" "}\n"; } } ShaderReturnCase::~ShaderReturnCase (void) { } void ShaderReturnCase::init (void) { try { ShaderRenderCase::init(); } catch (const CompileFailed&) { if (m_requirements & REQUIRE_DYNAMIC_LOOPS) { const bool isSupported = m_isVertexCase ? m_ctxInfo.isVertexDynamicLoopSupported() : m_ctxInfo.isFragmentDynamicLoopSupported(); if (!isSupported) throw tcu::NotSupportedError("Dynamic loops not supported"); } throw; } } ShaderReturnTests::ShaderReturnTests (Context& context) : TestCaseGroup(context, "return", "Return Statement Tests") { } ShaderReturnTests::~ShaderReturnTests (void) { } ShaderReturnCase* makeConditionalReturnInFuncCase (Context& context, const char* name, const char* description, ReturnMode returnMode, bool isVertex) { // Template StringTemplate tmpl( "${COORDSTORAGE} ${COORDPREC} vec4 ${COORDS};\n" "${EXTRADECL}\n" "${COORDPREC} vec4 getColor (void)\n" "{\n" " if (${RETURNCOND})\n" " return vec4(${COORDS}.xyz, 1.0);\n" " return vec4(${COORDS}.wzy, 1.0);\n" "}\n\n" "void main (void)\n" "{\n" "${POSITIONWRITE}" " ${OUTPUT} = getColor();\n" "}\n"); const char* coords = isVertex ? "a_coords" : "v_coords"; map params; params["COORDSTORAGE"] = isVertex ? "attribute" : "varying"; params["COORDPREC"] = isVertex ? "highp" : "mediump"; params["OUTPUT"] = isVertex ? "v_color" : "gl_FragColor"; params["COORDS"] = coords; params["EXTRADECL"] = isVertex ? "attribute highp vec4 a_position;\nvarying mediump vec4 v_color;\n" : ""; params["POSITIONWRITE"] = isVertex ? " gl_Position = a_position;\n" : ""; switch (returnMode) { case RETURNMODE_ALWAYS: params["RETURNCOND"] = "true"; break; case RETURNMODE_NEVER: params["RETURNCOND"] = "false"; break; case RETURNMODE_DYNAMIC: params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0"; break; default: DE_ASSERT(DE_FALSE); } return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(), getEvalFunc(returnMode)); } ShaderReturnCase* makeOutputWriteReturnCase (Context& context, const char* name, const char* description, bool inFunction, ReturnMode returnMode, bool isVertex) { // Template StringTemplate tmpl( inFunction ? "${COORDATTRS} vec4 ${COORDS};\n" "${EXTRADECL}\n" "void myfunc (void)\n" "{\n" " ${OUTPUT} = vec4(${COORDS}.xyz, 1.0);\n" " if (${RETURNCOND})\n" " return;\n" " ${OUTPUT} = vec4(${COORDS}.wzy, 1.0);\n" "}\n\n" "void main (void)\n" "{\n" "${POSITIONWRITE}" " myfunc();\n" "}\n" : "${COORDATTRS} vec4 ${COORDS};\n" "uniform mediump int ui_one;\n" "${EXTRADECL}\n" "void main ()\n" "{\n" "${POSITIONWRITE}" " ${OUTPUT} = vec4(${COORDS}.xyz, 1.0);\n" " if (${RETURNCOND})\n" " return;\n" " ${OUTPUT} = vec4(${COORDS}.wzy, 1.0);\n" "}\n"); const char* coords = isVertex ? "a_coords" : "v_coords"; map params; params["COORDATTRS"] = isVertex ? "attribute highp" : "varying mediump"; params["COORDS"] = coords; params["OUTPUT"] = isVertex ? "v_color" : "gl_FragColor"; params["EXTRADECL"] = isVertex ? "attribute highp vec4 a_position;\nvarying mediump vec4 v_color;\n" : ""; params["POSITIONWRITE"] = isVertex ? " gl_Position = a_position;\n" : ""; switch (returnMode) { case RETURNMODE_ALWAYS: params["RETURNCOND"] = "true"; break; case RETURNMODE_NEVER: params["RETURNCOND"] = "false"; break; case RETURNMODE_DYNAMIC: params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0"; break; default: DE_ASSERT(DE_FALSE); } return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(), getEvalFunc(returnMode)); } ShaderReturnCase* makeReturnInLoopCase (Context& context, const char* name, const char* description, bool isDynamicLoop, ReturnMode returnMode, bool isVertex) { // Template StringTemplate tmpl( "${COORDSTORAGE} ${COORDPREC} vec4 ${COORDS};\n" "uniform mediump int ui_one;\n" "${EXTRADECL}\n" "${COORDPREC} vec4 getCoords (void)\n" "{\n" " ${COORDPREC} vec4 coords = ${COORDS};\n" " for (int i = 0; i < ${ITERLIMIT}; i++)\n" " {\n" " if (${RETURNCOND})\n" " return coords;\n" " coords = coords.wzyx;\n" " }\n" " return coords;\n" "}\n\n" "void main (void)\n" "{\n" "${POSITIONWRITE}" " ${OUTPUT} = vec4(getCoords().xyz, 1.0);\n" "}\n"); const char* coords = isVertex ? "a_coords" : "v_coords"; map params; params["COORDSTORAGE"] = isVertex ? "attribute" : "varying"; params["COORDPREC"] = isVertex ? "highp" : "mediump"; params["OUTPUT"] = isVertex ? "v_color" : "gl_FragColor"; params["COORDS"] = coords; params["EXTRADECL"] = isVertex ? "attribute highp vec4 a_position;\nvarying mediump vec4 v_color;\n" : ""; params["POSITIONWRITE"] = isVertex ? " gl_Position = a_position;\n" : ""; params["ITERLIMIT"] = isDynamicLoop ? "ui_one" : "1"; switch (returnMode) { case RETURNMODE_ALWAYS: params["RETURNCOND"] = "true"; break; case RETURNMODE_NEVER: params["RETURNCOND"] = "false"; break; case RETURNMODE_DYNAMIC: params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0"; break; default: DE_ASSERT(DE_FALSE); } return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(), getEvalFunc(returnMode), isDynamicLoop ? REQUIRE_DYNAMIC_LOOPS : 0); } static const char* getReturnModeName (ReturnMode mode) { switch (mode) { case RETURNMODE_ALWAYS: return "always"; case RETURNMODE_NEVER: return "never"; case RETURNMODE_DYNAMIC: return "dynamic"; default: DE_ASSERT(DE_FALSE); return DE_NULL; } } static const char* getReturnModeDesc (ReturnMode mode) { switch (mode) { case RETURNMODE_ALWAYS: return "Always return"; case RETURNMODE_NEVER: return "Never return"; case RETURNMODE_DYNAMIC: return "Return based on coords"; default: DE_ASSERT(DE_FALSE); return DE_NULL; } } void ShaderReturnTests::init (void) { // Single return statement in function. addChild(new ShaderReturnCase(m_context, "single_return_vertex", "Single return statement in function", true, "attribute highp vec4 a_position;\n" "attribute highp vec4 a_coords;\n" "varying highp vec4 v_color;\n\n" "vec4 getColor (void)\n" "{\n" " return vec4(a_coords.xyz, 1.0);\n" "}\n\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" " v_color = getColor();\n" "}\n", evalReturnAlways)); addChild(new ShaderReturnCase(m_context, "single_return_fragment", "Single return statement in function", false, "varying mediump vec4 v_coords;\n" "mediump vec4 getColor (void)\n" "{\n" " return vec4(v_coords.xyz, 1.0);\n" "}\n\n" "void main (void)\n" "{\n" " gl_FragColor = getColor();\n" "}\n", evalReturnAlways)); // Conditional return statement in function. for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++) { for (int isFragment = 0; isFragment < 2; isFragment++) { string name = string("conditional_return_") + getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex"); string description = string(getReturnModeDesc((ReturnMode)returnMode)) + " in function"; addChild(makeConditionalReturnInFuncCase(m_context, name.c_str(), description.c_str(), (ReturnMode)returnMode, isFragment == 0)); } } // Unconditional double return in function. addChild(new ShaderReturnCase(m_context, "double_return_vertex", "Unconditional double return in function", true, "attribute highp vec4 a_position;\n" "attribute highp vec4 a_coords;\n" "varying highp vec4 v_color;\n\n" "vec4 getColor (void)\n" "{\n" " return vec4(a_coords.xyz, 1.0);\n" " return vec4(a_coords.wzy, 1.0);\n" "}\n\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" " v_color = getColor();\n" "}\n", evalReturnAlways)); addChild(new ShaderReturnCase(m_context, "double_return_fragment", "Unconditional double return in function", false, "varying mediump vec4 v_coords;\n" "mediump vec4 getColor (void)\n" "{\n" " return vec4(v_coords.xyz, 1.0);\n" " return vec4(v_coords.wzy, 1.0);\n" "}\n\n" "void main (void)\n" "{\n" " gl_FragColor = getColor();\n" "}\n", evalReturnAlways)); // Last statement in main. addChild(new ShaderReturnCase(m_context, "last_statement_in_main_vertex", "Return as a final statement in main()", true, "attribute highp vec4 a_position;\n" "attribute highp vec4 a_coords;\n" "varying highp vec4 v_color;\n\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" " v_color = vec4(a_coords.xyz, 1.0);\n" " return;\n" "}\n", evalReturnAlways)); addChild(new ShaderReturnCase(m_context, "last_statement_in_main_fragment", "Return as a final statement in main()", false, "varying mediump vec4 v_coords;\n\n" "void main (void)\n" "{\n" " gl_FragColor = vec4(v_coords.xyz, 1.0);\n" " return;\n" "}\n", evalReturnAlways)); // Return between output variable writes. for (int inFunc = 0; inFunc < 2; inFunc++) { for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++) { for (int isFragment = 0; isFragment < 2; isFragment++) { string name = string("output_write_") + (inFunc ? "in_func_" : "") + getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex"); string desc = string(getReturnModeDesc((ReturnMode)returnMode)) + (inFunc ? " in user-defined function" : " in main()") + " between output writes"; addChild(makeOutputWriteReturnCase(m_context, name.c_str(), desc.c_str(), inFunc != 0, (ReturnMode)returnMode, isFragment == 0)); } } } // Conditional return statement in loop. for (int isDynamicLoop = 0; isDynamicLoop < 2; isDynamicLoop++) { for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++) { for (int isFragment = 0; isFragment < 2; isFragment++) { string name = string("return_in_") + (isDynamicLoop ? "dynamic" : "static") + "_loop_" + getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex"); string description = string(getReturnModeDesc((ReturnMode)returnMode)) + " in loop"; addChild(makeReturnInLoopCase(m_context, name.c_str(), description.c_str(), isDynamicLoop != 0, (ReturnMode)returnMode, isFragment == 0)); } } } // Unconditional return in infinite loop. addChild(new ShaderReturnCase(m_context, "return_in_infinite_loop_vertex", "Return in infinite loop", true, "attribute highp vec4 a_position;\n" "attribute highp vec4 a_coords;\n" "varying highp vec4 v_color;\n" "uniform int ui_zero;\n\n" "highp vec4 getCoords (void)\n" "{\n" " for (int i = 1; i < 10; i += ui_zero)\n" " return a_coords;\n" " return a_coords.wzyx;\n" "}\n\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" " v_color = vec4(getCoords().xyz, 1.0);\n" " return;\n" "}\n", evalReturnAlways, REQUIRE_DYNAMIC_LOOPS)); addChild(new ShaderReturnCase(m_context, "return_in_infinite_loop_fragment", "Return in infinite loop", false, "varying mediump vec4 v_coords;\n" "uniform int ui_zero;\n\n" "mediump vec4 getCoords (void)\n" "{\n" " for (int i = 1; i < 10; i += ui_zero)\n" " return v_coords;\n" " return v_coords.wzyx;\n" "}\n\n" "void main (void)\n" "{\n" " gl_FragColor = vec4(getCoords().xyz, 1.0);\n" " return;\n" "}\n", evalReturnAlways, REQUIRE_DYNAMIC_LOOPS)); } } // Functional } // gles2 } // deqp