1/*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES 2.0 Module 3 * ------------------------------------------------- 4 * 5 * Copyright 2014 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 *//*! 20 * \file 21 * \brief Shader return statement tests. 22 *//*--------------------------------------------------------------------*/ 23 24#include "es2fShaderReturnTests.hpp" 25#include "glsShaderRenderCase.hpp" 26#include "tcuStringTemplate.hpp" 27 28#include <map> 29#include <sstream> 30#include <string> 31 32using tcu::StringTemplate; 33 34using std::map; 35using std::string; 36using std::ostringstream; 37 38using namespace glu; 39using namespace deqp::gls; 40 41namespace deqp 42{ 43namespace gles2 44{ 45namespace Functional 46{ 47 48enum ReturnMode 49{ 50 RETURNMODE_ALWAYS = 0, 51 RETURNMODE_NEVER, 52 RETURNMODE_DYNAMIC, 53 54 RETURNMODE_LAST 55}; 56 57enum RequireFlags 58{ 59 REQUIRE_DYNAMIC_LOOPS = (1<<0), 60}; 61 62// Evaluation functions 63inline void evalReturnAlways (ShaderEvalContext& c) { c.color.xyz() = c.coords.swizzle(0,1,2); } 64inline void evalReturnNever (ShaderEvalContext& c) { c.color.xyz() = c.coords.swizzle(3,2,1); } 65inline 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); } 66 67static ShaderEvalFunc getEvalFunc (ReturnMode mode) 68{ 69 switch (mode) 70 { 71 case RETURNMODE_ALWAYS: return evalReturnAlways; 72 case RETURNMODE_NEVER: return evalReturnNever; 73 case RETURNMODE_DYNAMIC: return evalReturnDynamic; 74 default: 75 DE_ASSERT(DE_FALSE); 76 return (ShaderEvalFunc)DE_NULL; 77 } 78} 79 80class ShaderReturnCase : public ShaderRenderCase 81{ 82public: 83 ShaderReturnCase (Context& context, const char* name, const char* description, bool isVertexCase, const char* shaderSource, ShaderEvalFunc evalFunc, deUint32 requirements = 0); 84 virtual ~ShaderReturnCase (void); 85 86 void init (void); 87 88private: 89 const deUint32 m_requirements; 90}; 91 92ShaderReturnCase::ShaderReturnCase (Context& context, const char* name, const char* description, bool isVertexCase, const char* shaderSource, ShaderEvalFunc evalFunc, deUint32 requirements) 93 : ShaderRenderCase (context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name, description, isVertexCase, evalFunc) 94 , m_requirements (requirements) 95{ 96 if (isVertexCase) 97 { 98 m_vertShaderSource = shaderSource; 99 m_fragShaderSource = 100 "varying mediump vec4 v_color;\n\n" 101 "void main (void)\n" 102 "{\n" 103 " gl_FragColor = v_color;\n" 104 "}\n"; 105 } 106 else 107 { 108 m_fragShaderSource = shaderSource; 109 m_vertShaderSource = 110 "attribute highp vec4 a_position;\n" 111 "attribute highp vec4 a_coords;\n" 112 "varying mediump vec4 v_coords;\n\n" 113 "void main (void)\n" 114 "{\n" 115 " gl_Position = a_position;\n" 116 " v_coords = a_coords;\n" 117 "}\n"; 118 } 119} 120 121ShaderReturnCase::~ShaderReturnCase (void) 122{ 123} 124 125void ShaderReturnCase::init (void) 126{ 127 try 128 { 129 ShaderRenderCase::init(); 130 } 131 catch (const CompileFailed&) 132 { 133 if (m_requirements & REQUIRE_DYNAMIC_LOOPS) 134 { 135 const bool isSupported = m_isVertexCase ? m_ctxInfo.isVertexDynamicLoopSupported() : m_ctxInfo.isFragmentDynamicLoopSupported(); 136 if (!isSupported) 137 throw tcu::NotSupportedError("Dynamic loops not supported"); 138 } 139 140 throw; 141 } 142} 143 144ShaderReturnTests::ShaderReturnTests (Context& context) 145 : TestCaseGroup(context, "return", "Return Statement Tests") 146{ 147} 148 149ShaderReturnTests::~ShaderReturnTests (void) 150{ 151} 152 153ShaderReturnCase* makeConditionalReturnInFuncCase (Context& context, const char* name, const char* description, ReturnMode returnMode, bool isVertex) 154{ 155 // Template 156 StringTemplate tmpl( 157 "${COORDSTORAGE} ${COORDPREC} vec4 ${COORDS};\n" 158 "${EXTRADECL}\n" 159 "${COORDPREC} vec4 getColor (void)\n" 160 "{\n" 161 " if (${RETURNCOND})\n" 162 " return vec4(${COORDS}.xyz, 1.0);\n" 163 " return vec4(${COORDS}.wzy, 1.0);\n" 164 "}\n\n" 165 "void main (void)\n" 166 "{\n" 167 "${POSITIONWRITE}" 168 " ${OUTPUT} = getColor();\n" 169 "}\n"); 170 171 const char* coords = isVertex ? "a_coords" : "v_coords"; 172 173 map<string, string> params; 174 175 params["COORDSTORAGE"] = isVertex ? "attribute" : "varying"; 176 params["COORDPREC"] = isVertex ? "highp" : "mediump"; 177 params["OUTPUT"] = isVertex ? "v_color" : "gl_FragColor"; 178 params["COORDS"] = coords; 179 params["EXTRADECL"] = isVertex ? "attribute highp vec4 a_position;\nvarying mediump vec4 v_color;\n" : ""; 180 params["POSITIONWRITE"] = isVertex ? " gl_Position = a_position;\n" : ""; 181 182 switch (returnMode) 183 { 184 case RETURNMODE_ALWAYS: params["RETURNCOND"] = "true"; break; 185 case RETURNMODE_NEVER: params["RETURNCOND"] = "false"; break; 186 case RETURNMODE_DYNAMIC: params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0"; break; 187 default: DE_ASSERT(DE_FALSE); 188 } 189 190 return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(), getEvalFunc(returnMode)); 191} 192 193ShaderReturnCase* makeOutputWriteReturnCase (Context& context, const char* name, const char* description, bool inFunction, ReturnMode returnMode, bool isVertex) 194{ 195 // Template 196 StringTemplate tmpl( 197 inFunction 198 ? 199 "${COORDATTRS} vec4 ${COORDS};\n" 200 "${EXTRADECL}\n" 201 "void myfunc (void)\n" 202 "{\n" 203 " ${OUTPUT} = vec4(${COORDS}.xyz, 1.0);\n" 204 " if (${RETURNCOND})\n" 205 " return;\n" 206 " ${OUTPUT} = vec4(${COORDS}.wzy, 1.0);\n" 207 "}\n\n" 208 "void main (void)\n" 209 "{\n" 210 "${POSITIONWRITE}" 211 " myfunc();\n" 212 "}\n" 213 : 214 "${COORDATTRS} vec4 ${COORDS};\n" 215 "uniform mediump int ui_one;\n" 216 "${EXTRADECL}\n" 217 "void main ()\n" 218 "{\n" 219 "${POSITIONWRITE}" 220 " ${OUTPUT} = vec4(${COORDS}.xyz, 1.0);\n" 221 " if (${RETURNCOND})\n" 222 " return;\n" 223 " ${OUTPUT} = vec4(${COORDS}.wzy, 1.0);\n" 224 "}\n"); 225 226 const char* coords = isVertex ? "a_coords" : "v_coords"; 227 228 map<string, string> params; 229 230 params["COORDATTRS"] = isVertex ? "attribute highp" : "varying mediump"; 231 params["COORDS"] = coords; 232 params["OUTPUT"] = isVertex ? "v_color" : "gl_FragColor"; 233 params["EXTRADECL"] = isVertex ? "attribute highp vec4 a_position;\nvarying mediump vec4 v_color;\n" : ""; 234 params["POSITIONWRITE"] = isVertex ? " gl_Position = a_position;\n" : ""; 235 236 switch (returnMode) 237 { 238 case RETURNMODE_ALWAYS: params["RETURNCOND"] = "true"; break; 239 case RETURNMODE_NEVER: params["RETURNCOND"] = "false"; break; 240 case RETURNMODE_DYNAMIC: params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0"; break; 241 default: DE_ASSERT(DE_FALSE); 242 } 243 244 return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(), getEvalFunc(returnMode)); 245} 246 247ShaderReturnCase* makeReturnInLoopCase (Context& context, const char* name, const char* description, bool isDynamicLoop, ReturnMode returnMode, bool isVertex) 248{ 249 // Template 250 StringTemplate tmpl( 251 "${COORDSTORAGE} ${COORDPREC} vec4 ${COORDS};\n" 252 "uniform mediump int ui_one;\n" 253 "${EXTRADECL}\n" 254 "${COORDPREC} vec4 getCoords (void)\n" 255 "{\n" 256 " ${COORDPREC} vec4 coords = ${COORDS};\n" 257 " for (int i = 0; i < ${ITERLIMIT}; i++)\n" 258 " {\n" 259 " if (${RETURNCOND})\n" 260 " return coords;\n" 261 " coords = coords.wzyx;\n" 262 " }\n" 263 " return coords;\n" 264 "}\n\n" 265 "void main (void)\n" 266 "{\n" 267 "${POSITIONWRITE}" 268 " ${OUTPUT} = vec4(getCoords().xyz, 1.0);\n" 269 "}\n"); 270 271 const char* coords = isVertex ? "a_coords" : "v_coords"; 272 273 map<string, string> params; 274 275 params["COORDSTORAGE"] = isVertex ? "attribute" : "varying"; 276 params["COORDPREC"] = isVertex ? "highp" : "mediump"; 277 params["OUTPUT"] = isVertex ? "v_color" : "gl_FragColor"; 278 params["COORDS"] = coords; 279 params["EXTRADECL"] = isVertex ? "attribute highp vec4 a_position;\nvarying mediump vec4 v_color;\n" : ""; 280 params["POSITIONWRITE"] = isVertex ? " gl_Position = a_position;\n" : ""; 281 params["ITERLIMIT"] = isDynamicLoop ? "ui_one" : "1"; 282 283 switch (returnMode) 284 { 285 case RETURNMODE_ALWAYS: params["RETURNCOND"] = "true"; break; 286 case RETURNMODE_NEVER: params["RETURNCOND"] = "false"; break; 287 case RETURNMODE_DYNAMIC: params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0"; break; 288 default: DE_ASSERT(DE_FALSE); 289 } 290 291 return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(), getEvalFunc(returnMode), isDynamicLoop ? REQUIRE_DYNAMIC_LOOPS : 0); 292} 293 294static const char* getReturnModeName (ReturnMode mode) 295{ 296 switch (mode) 297 { 298 case RETURNMODE_ALWAYS: return "always"; 299 case RETURNMODE_NEVER: return "never"; 300 case RETURNMODE_DYNAMIC: return "dynamic"; 301 default: 302 DE_ASSERT(DE_FALSE); 303 return DE_NULL; 304 } 305} 306 307static const char* getReturnModeDesc (ReturnMode mode) 308{ 309 switch (mode) 310 { 311 case RETURNMODE_ALWAYS: return "Always return"; 312 case RETURNMODE_NEVER: return "Never return"; 313 case RETURNMODE_DYNAMIC: return "Return based on coords"; 314 default: 315 DE_ASSERT(DE_FALSE); 316 return DE_NULL; 317 } 318} 319 320void ShaderReturnTests::init (void) 321{ 322 // Single return statement in function. 323 addChild(new ShaderReturnCase(m_context, "single_return_vertex", "Single return statement in function", true, 324 "attribute highp vec4 a_position;\n" 325 "attribute highp vec4 a_coords;\n" 326 "varying highp vec4 v_color;\n\n" 327 "vec4 getColor (void)\n" 328 "{\n" 329 " return vec4(a_coords.xyz, 1.0);\n" 330 "}\n\n" 331 "void main (void)\n" 332 "{\n" 333 " gl_Position = a_position;\n" 334 " v_color = getColor();\n" 335 "}\n", evalReturnAlways)); 336 addChild(new ShaderReturnCase(m_context, "single_return_fragment", "Single return statement in function", false, 337 "varying mediump vec4 v_coords;\n" 338 "mediump vec4 getColor (void)\n" 339 "{\n" 340 " return vec4(v_coords.xyz, 1.0);\n" 341 "}\n\n" 342 "void main (void)\n" 343 "{\n" 344 " gl_FragColor = getColor();\n" 345 "}\n", evalReturnAlways)); 346 347 // Conditional return statement in function. 348 for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++) 349 { 350 for (int isFragment = 0; isFragment < 2; isFragment++) 351 { 352 string name = string("conditional_return_") + getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex"); 353 string description = string(getReturnModeDesc((ReturnMode)returnMode)) + " in function"; 354 addChild(makeConditionalReturnInFuncCase(m_context, name.c_str(), description.c_str(), (ReturnMode)returnMode, isFragment == 0)); 355 } 356 } 357 358 // Unconditional double return in function. 359 addChild(new ShaderReturnCase(m_context, "double_return_vertex", "Unconditional double return in function", true, 360 "attribute highp vec4 a_position;\n" 361 "attribute highp vec4 a_coords;\n" 362 "varying highp vec4 v_color;\n\n" 363 "vec4 getColor (void)\n" 364 "{\n" 365 " return vec4(a_coords.xyz, 1.0);\n" 366 " return vec4(a_coords.wzy, 1.0);\n" 367 "}\n\n" 368 "void main (void)\n" 369 "{\n" 370 " gl_Position = a_position;\n" 371 " v_color = getColor();\n" 372 "}\n", evalReturnAlways)); 373 addChild(new ShaderReturnCase(m_context, "double_return_fragment", "Unconditional double return in function", false, 374 "varying mediump vec4 v_coords;\n" 375 "mediump vec4 getColor (void)\n" 376 "{\n" 377 " return vec4(v_coords.xyz, 1.0);\n" 378 " return vec4(v_coords.wzy, 1.0);\n" 379 "}\n\n" 380 "void main (void)\n" 381 "{\n" 382 " gl_FragColor = getColor();\n" 383 "}\n", evalReturnAlways)); 384 385 // Last statement in main. 386 addChild(new ShaderReturnCase(m_context, "last_statement_in_main_vertex", "Return as a final statement in main()", true, 387 "attribute highp vec4 a_position;\n" 388 "attribute highp vec4 a_coords;\n" 389 "varying highp vec4 v_color;\n\n" 390 "void main (void)\n" 391 "{\n" 392 " gl_Position = a_position;\n" 393 " v_color = vec4(a_coords.xyz, 1.0);\n" 394 " return;\n" 395 "}\n", evalReturnAlways)); 396 addChild(new ShaderReturnCase(m_context, "last_statement_in_main_fragment", "Return as a final statement in main()", false, 397 "varying mediump vec4 v_coords;\n\n" 398 "void main (void)\n" 399 "{\n" 400 " gl_FragColor = vec4(v_coords.xyz, 1.0);\n" 401 " return;\n" 402 "}\n", evalReturnAlways)); 403 404 // Return between output variable writes. 405 for (int inFunc = 0; inFunc < 2; inFunc++) 406 { 407 for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++) 408 { 409 for (int isFragment = 0; isFragment < 2; isFragment++) 410 { 411 string name = string("output_write_") + (inFunc ? "in_func_" : "") + getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex"); 412 string desc = string(getReturnModeDesc((ReturnMode)returnMode)) + (inFunc ? " in user-defined function" : " in main()") + " between output writes"; 413 414 addChild(makeOutputWriteReturnCase(m_context, name.c_str(), desc.c_str(), inFunc != 0, (ReturnMode)returnMode, isFragment == 0)); 415 } 416 } 417 } 418 419 // Conditional return statement in loop. 420 for (int isDynamicLoop = 0; isDynamicLoop < 2; isDynamicLoop++) 421 { 422 for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++) 423 { 424 for (int isFragment = 0; isFragment < 2; isFragment++) 425 { 426 string name = string("return_in_") + (isDynamicLoop ? "dynamic" : "static") + "_loop_" + getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex"); 427 string description = string(getReturnModeDesc((ReturnMode)returnMode)) + " in loop"; 428 addChild(makeReturnInLoopCase(m_context, name.c_str(), description.c_str(), isDynamicLoop != 0, (ReturnMode)returnMode, isFragment == 0)); 429 } 430 } 431 } 432 433 // Unconditional return in infinite loop. 434 addChild(new ShaderReturnCase(m_context, "return_in_infinite_loop_vertex", "Return in infinite loop", true, 435 "attribute highp vec4 a_position;\n" 436 "attribute highp vec4 a_coords;\n" 437 "varying highp vec4 v_color;\n" 438 "uniform int ui_zero;\n\n" 439 "highp vec4 getCoords (void)\n" 440 "{\n" 441 " for (int i = 1; i < 10; i += ui_zero)\n" 442 " return a_coords;\n" 443 " return a_coords.wzyx;\n" 444 "}\n\n" 445 "void main (void)\n" 446 "{\n" 447 " gl_Position = a_position;\n" 448 " v_color = vec4(getCoords().xyz, 1.0);\n" 449 " return;\n" 450 "}\n", evalReturnAlways, REQUIRE_DYNAMIC_LOOPS)); 451 addChild(new ShaderReturnCase(m_context, "return_in_infinite_loop_fragment", "Return in infinite loop", false, 452 "varying mediump vec4 v_coords;\n" 453 "uniform int ui_zero;\n\n" 454 "mediump vec4 getCoords (void)\n" 455 "{\n" 456 " for (int i = 1; i < 10; i += ui_zero)\n" 457 " return v_coords;\n" 458 " return v_coords.wzyx;\n" 459 "}\n\n" 460 "void main (void)\n" 461 "{\n" 462 " gl_FragColor = vec4(getCoords().xyz, 1.0);\n" 463 " return;\n" 464 "}\n", evalReturnAlways, REQUIRE_DYNAMIC_LOOPS)); 465} 466 467} // Functional 468} // gles2 469} // deqp 470