/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.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 switch statement tests. * * Variables: * + Selection expression type: static, uniform, dynamic * + Switch layout - fall-through or use of default label * + Switch nested in loop/conditional statement * + Loop/conditional statement nested in switch *//*--------------------------------------------------------------------*/ #include "es3fShaderSwitchTests.hpp" #include "glsShaderRenderCase.hpp" #include "glsShaderLibrary.hpp" #include "tcuStringTemplate.hpp" #include "deMath.h" namespace deqp { namespace gles3 { namespace Functional { using namespace deqp::gls; using std::string; using std::map; using std::vector; class ShaderSwitchCase : public ShaderRenderCase { public: ShaderSwitchCase (Context& context, const char* name, const char* description, bool isVertexCase, const char* vtxSource, const char* fragSource, ShaderEvalFunc evalFunc); virtual ~ShaderSwitchCase (void); }; ShaderSwitchCase::ShaderSwitchCase (Context& context, const char* name, const char* description, bool isVertexCase, const char* vtxSource, const char* fragSource, ShaderEvalFunc evalFunc) : ShaderRenderCase(context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name, description, isVertexCase, evalFunc) { m_vertShaderSource = vtxSource; m_fragShaderSource = fragSource; } ShaderSwitchCase::~ShaderSwitchCase (void) { } enum SwitchType { SWITCHTYPE_STATIC = 0, SWITCHTYPE_UNIFORM, SWITCHTYPE_DYNAMIC, SWITCHTYPE_LAST }; static void evalSwitchStatic (ShaderEvalContext& evalCtx) { evalCtx.color.xyz() = evalCtx.coords.swizzle(1,2,3); } static void evalSwitchUniform (ShaderEvalContext& evalCtx) { evalCtx.color.xyz() = evalCtx.coords.swizzle(1,2,3); } static void evalSwitchDynamic (ShaderEvalContext& evalCtx) { switch (int(deFloatFloor(evalCtx.coords.z()*1.5f + 2.0f))) { case 0: evalCtx.color.xyz() = evalCtx.coords.swizzle(0,1,2); break; case 1: evalCtx.color.xyz() = evalCtx.coords.swizzle(3,2,1); break; case 2: evalCtx.color.xyz() = evalCtx.coords.swizzle(1,2,3); break; case 3: evalCtx.color.xyz() = evalCtx.coords.swizzle(2,1,0); break; default: evalCtx.color.xyz() = evalCtx.coords.swizzle(0,0,0); break; } } static tcu::TestCase* makeSwitchCase (Context& context, const char* name, const char* desc, SwitchType type, bool isVertex, const LineStream& switchBody) { std::ostringstream vtx; std::ostringstream frag; std::ostringstream& op = isVertex ? vtx : frag; vtx << "#version 300 es\n" << "in highp vec4 a_position;\n" << "in highp vec4 a_coords;\n"; frag << "#version 300 es\n" << "layout(location = 0) out mediump vec4 o_color;\n"; if (isVertex) { vtx << "out mediump vec4 v_color;\n"; frag << "in mediump vec4 v_color;\n"; } else { vtx << "out highp vec4 v_coords;\n"; frag << "in highp vec4 v_coords;\n"; } if (type == SWITCHTYPE_UNIFORM) op << "uniform highp int ui_two;\n"; vtx << "\n" << "void main (void)\n" << "{\n" << " gl_Position = a_position;\n"; frag << "\n" << "void main (void)\n" << "{\n"; // Setup. op << " highp vec4 coords = " << (isVertex ? "a_coords" : "v_coords") << ";\n"; op << " mediump vec3 res = vec3(0.0);\n\n"; // Switch body. map<string, string> params; params["CONDITION"] = type == SWITCHTYPE_STATIC ? "2" : type == SWITCHTYPE_UNIFORM ? "ui_two" : type == SWITCHTYPE_DYNAMIC ? "int(floor(coords.z*1.5 + 2.0))" : "???"; op << tcu::StringTemplate(switchBody.str()).specialize(params).c_str(); op << "\n"; if (isVertex) { vtx << " v_color = vec4(res, 1.0);\n"; frag << " o_color = v_color;\n"; } else { vtx << " v_coords = a_coords;\n"; frag << " o_color = vec4(res, 1.0);\n"; } vtx << "}\n"; frag << "}\n"; return new ShaderSwitchCase(context, name, desc, isVertex, vtx.str().c_str(), frag.str().c_str(), type == SWITCHTYPE_STATIC ? evalSwitchStatic : type == SWITCHTYPE_UNIFORM ? evalSwitchUniform : type == SWITCHTYPE_DYNAMIC ? evalSwitchDynamic : (ShaderEvalFunc)DE_NULL); } static void makeSwitchCases (TestCaseGroup* group, const char* name, const char* desc, const LineStream& switchBody) { static const char* switchTypeNames[] = { "static", "uniform", "dynamic" }; DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(switchTypeNames) == SWITCHTYPE_LAST); for (int type = 0; type < SWITCHTYPE_LAST; type++) { group->addChild(makeSwitchCase(group->getContext(), (string(name) + "_" + switchTypeNames[type] + "_vertex").c_str(), desc, (SwitchType)type, true, switchBody)); group->addChild(makeSwitchCase(group->getContext(), (string(name) + "_" + switchTypeNames[type] + "_fragment").c_str(), desc, (SwitchType)type, false, switchBody)); } } ShaderSwitchTests::ShaderSwitchTests (Context& context) : TestCaseGroup(context, "switch", "Switch statement tests") { } ShaderSwitchTests::~ShaderSwitchTests (void) { } void ShaderSwitchTests::init (void) { // Expected swizzles: // 0: xyz // 1: wzy // 2: yzw // 3: zyx makeSwitchCases(this, "basic", "Basic switch statement usage", LineStream(1) << "switch (${CONDITION})" << "{" << " case 0: res = coords.xyz; break;" << " case 1: res = coords.wzy; break;" << " case 2: res = coords.yzw; break;" << " case 3: res = coords.zyx; break;" << "}"); makeSwitchCases(this, "const_expr_in_label", "Constant expression in label", LineStream(1) << "const int t = 2;" << "switch (${CONDITION})" << "{" << " case int(0.0): res = coords.xyz; break;" << " case 2-1: res = coords.wzy; break;" << " case 3&(1<<1): res = coords.yzw; break;" << " case t+1: res = coords.zyx; break;" << "}"); makeSwitchCases(this, "default_label", "Default label usage", LineStream(1) << "switch (${CONDITION})" << "{" << " case 0: res = coords.xyz; break;" << " case 1: res = coords.wzy; break;" << " case 3: res = coords.zyx; break;" << " default: res = coords.yzw;" << "}"); makeSwitchCases(this, "default_not_last", "Default label usage", LineStream(1) << "switch (${CONDITION})" << "{" << " case 0: res = coords.xyz; break;" << " default: res = coords.yzw; break;" << " case 1: res = coords.wzy; break;" << " case 3: res = coords.zyx; break;" << "}"); makeSwitchCases(this, "no_default_label", "No match in switch without default label", LineStream(1) << "res = coords.yzw;\n" << "switch (${CONDITION})" << "{" << " case 0: res = coords.xyz; break;" << " case 1: res = coords.wzy; break;" << " case 3: res = coords.zyx; break;" << "}"); makeSwitchCases(this, "fall_through", "Fall-through", LineStream(1) << "switch (${CONDITION})" << "{" << " case 0: res = coords.xyz; break;" << " case 1: res = coords.wzy; break;" << " case 2: coords = coords.yzwx;" << " case 4: res = vec3(coords); break;" << " case 3: res = coords.zyx; break;" << "}"); makeSwitchCases(this, "fall_through_default", "Fall-through", LineStream(1) << "switch (${CONDITION})" << "{" << " case 0: res = coords.xyz; break;" << " case 1: res = coords.wzy; break;" << " case 3: res = coords.zyx; break;" << " case 2: coords = coords.yzwx;" << " default: res = vec3(coords);" << "}"); makeSwitchCases(this, "conditional_fall_through", "Fall-through", LineStream(1) << "highp vec4 tmp = coords;" << "switch (${CONDITION})" << "{" << " case 0: res = coords.xyz; break;" << " case 1: res = coords.wzy; break;" << " case 2:" << " tmp = coords.yzwx;" << " case 3:" << " res = vec3(tmp);" << " if (${CONDITION} != 3)" << " break;" << " default: res = tmp.zyx; break;" << "}"); makeSwitchCases(this, "conditional_fall_through_2", "Fall-through", LineStream(1) << "highp vec4 tmp = coords;" << "mediump int c = ${CONDITION};" << "switch (c)" << "{" << " case 0: res = coords.xyz; break;" << " case 1: res = coords.wzy; break;" << " case 2:" << " c += ${CONDITION};" << " tmp = coords.yzwx;" << " case 3:" << " res = vec3(tmp);" << " if (c == 4)" << " break;" << " default: res = tmp.zyx; break;" << "}"); makeSwitchCases(this, "scope", "Basic switch statement usage", LineStream(1) << "switch (${CONDITION})" << "{" << " case 0: res = coords.xyz; break;" << " case 1: res = coords.wzy; break;" << " case 2:" << " {" << " mediump vec3 t = coords.yzw;" << " res = t;" << " break;" << " }" << " case 3: res = coords.zyx; break;" << "}"); makeSwitchCases(this, "switch_in_if", "Switch in for loop", LineStream(1) << "if (${CONDITION} >= 0)" << "{" << " switch (${CONDITION})" << " {" << " case 0: res = coords.xyz; break;" << " case 1: res = coords.wzy; break;" << " case 2: res = coords.yzw; break;" << " case 3: res = coords.zyx; break;" << " }" << "}"); makeSwitchCases(this, "switch_in_for_loop", "Switch in for loop", LineStream(1) << "for (int i = 0; i <= ${CONDITION}; i++)" << "{" << " switch (i)" << " {" << " case 0: res = coords.xyz; break;" << " case 1: res = coords.wzy; break;" << " case 2: res = coords.yzw; break;" << " case 3: res = coords.zyx; break;" << " }" << "}"); makeSwitchCases(this, "switch_in_while_loop", "Switch in while loop", LineStream(1) << "int i = 0;" << "while (i <= ${CONDITION})" << "{" << " switch (i)" << " {" << " case 0: res = coords.xyz; break;" << " case 1: res = coords.wzy; break;" << " case 2: res = coords.yzw; break;" << " case 3: res = coords.zyx; break;" << " }" << " i += 1;" << "}"); makeSwitchCases(this, "switch_in_do_while_loop", "Switch in do-while loop", LineStream(1) << "int i = 0;" << "do" << "{" << " switch (i)" << " {" << " case 0: res = coords.xyz; break;" << " case 1: res = coords.wzy; break;" << " case 2: res = coords.yzw; break;" << " case 3: res = coords.zyx; break;" << " }" << " i += 1;" << "} while (i <= ${CONDITION});"); makeSwitchCases(this, "if_in_switch", "Basic switch statement usage", LineStream(1) << "switch (${CONDITION})" << "{" << " case 0: res = coords.xyz; break;" << " case 1: res = coords.wzy; break;" << " default:" << " if (${CONDITION} == 2)" << " res = coords.yzw;" << " else" << " res = coords.zyx;" << " break;" << "}"); makeSwitchCases(this, "for_loop_in_switch", "Basic switch statement usage", LineStream(1) << "switch (${CONDITION})" << "{" << " case 0: res = coords.xyz; break;" << " case 1:" << " case 2:" << " {" << " highp vec3 t = coords.yzw;" << " for (int i = 0; i < ${CONDITION}; i++)" << " t = t.zyx;" << " res = t;" << " break;" << " }" << " default: res = coords.zyx; break;" << "}"); makeSwitchCases(this, "while_loop_in_switch", "Basic switch statement usage", LineStream(1) << "switch (${CONDITION})" << "{" << " case 0: res = coords.xyz; break;" << " case 1:" << " case 2:" << " {" << " highp vec3 t = coords.yzw;" << " int i = 0;" << " while (i < ${CONDITION})" << " {" << " t = t.zyx;" << " i += 1;" << " }" << " res = t;" << " break;" << " }" << " default: res = coords.zyx; break;" << "}"); makeSwitchCases(this, "do_while_loop_in_switch", "Basic switch statement usage", LineStream(1) << "switch (${CONDITION})" << "{" << " case 0: res = coords.xyz; break;" << " case 1:" << " case 2:" << " {" << " highp vec3 t = coords.yzw;" << " int i = 0;" << " do" << " {" << " t = t.zyx;" << " i += 1;" << " } while (i < ${CONDITION});" << " res = t;" << " break;" << " }" << " default: res = coords.zyx; break;" << "}"); makeSwitchCases(this, "switch_in_switch", "Basic switch statement usage", LineStream(1) << "switch (${CONDITION})" << "{" << " case 0: res = coords.xyz; break;" << " case 1:" << " case 2:" << " switch (${CONDITION} - 1)" << " {" << " case 0: res = coords.wzy; break;" << " case 1: res = coords.yzw; break;" << " }" << " break;" << " default: res = coords.zyx; break;" << "}"); // Negative cases. ShaderLibrary library(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo()); vector<tcu::TestNode*> negativeCases = library.loadShaderFile("shaders/switch.test"); for (vector<tcu::TestNode*>::iterator i = negativeCases.begin(); i != negativeCases.end(); i++) addChild(*i); } } // Functional } // gles3 } // deqp