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
32 using tcu::StringTemplate;
33
34 using std::map;
35 using std::ostringstream;
36 using std::string;
37
38 using namespace glu;
39 using namespace deqp::gls;
40
41 namespace deqp
42 {
43 namespace gles2
44 {
45 namespace Functional
46 {
47
48 enum ReturnMode
49 {
50 RETURNMODE_ALWAYS = 0,
51 RETURNMODE_NEVER,
52 RETURNMODE_DYNAMIC,
53
54 RETURNMODE_LAST
55 };
56
57 enum RequireFlags
58 {
59 REQUIRE_DYNAMIC_LOOPS = (1 << 0),
60 };
61
62 // Evaluation functions
evalReturnAlways(ShaderEvalContext & c)63 inline void evalReturnAlways(ShaderEvalContext &c)
64 {
65 c.color.xyz() = c.coords.swizzle(0, 1, 2);
66 }
evalReturnNever(ShaderEvalContext & c)67 inline void evalReturnNever(ShaderEvalContext &c)
68 {
69 c.color.xyz() = c.coords.swizzle(3, 2, 1);
70 }
evalReturnDynamic(ShaderEvalContext & c)71 inline void evalReturnDynamic(ShaderEvalContext &c)
72 {
73 c.color.xyz() = (c.coords.x() + c.coords.y() >= 0.0f) ? c.coords.swizzle(0, 1, 2) : c.coords.swizzle(3, 2, 1);
74 }
75
getEvalFunc(ReturnMode mode)76 static ShaderEvalFunc getEvalFunc(ReturnMode mode)
77 {
78 switch (mode)
79 {
80 case RETURNMODE_ALWAYS:
81 return evalReturnAlways;
82 case RETURNMODE_NEVER:
83 return evalReturnNever;
84 case RETURNMODE_DYNAMIC:
85 return evalReturnDynamic;
86 default:
87 DE_ASSERT(false);
88 return (ShaderEvalFunc)DE_NULL;
89 }
90 }
91
92 class ShaderReturnCase : public ShaderRenderCase
93 {
94 public:
95 ShaderReturnCase(Context &context, const char *name, const char *description, bool isVertexCase,
96 const char *shaderSource, ShaderEvalFunc evalFunc, uint32_t requirements = 0);
97 virtual ~ShaderReturnCase(void);
98
99 void init(void);
100
101 private:
102 const uint32_t m_requirements;
103 };
104
ShaderReturnCase(Context & context,const char * name,const char * description,bool isVertexCase,const char * shaderSource,ShaderEvalFunc evalFunc,uint32_t requirements)105 ShaderReturnCase::ShaderReturnCase(Context &context, const char *name, const char *description, bool isVertexCase,
106 const char *shaderSource, ShaderEvalFunc evalFunc, uint32_t requirements)
107 : ShaderRenderCase(context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name,
108 description, isVertexCase, evalFunc)
109 , m_requirements(requirements)
110 {
111 if (isVertexCase)
112 {
113 m_vertShaderSource = shaderSource;
114 m_fragShaderSource = "varying mediump vec4 v_color;\n\n"
115 "void main (void)\n"
116 "{\n"
117 " gl_FragColor = v_color;\n"
118 "}\n";
119 }
120 else
121 {
122 m_fragShaderSource = shaderSource;
123 m_vertShaderSource = "attribute highp vec4 a_position;\n"
124 "attribute highp vec4 a_coords;\n"
125 "varying mediump vec4 v_coords;\n\n"
126 "void main (void)\n"
127 "{\n"
128 " gl_Position = a_position;\n"
129 " v_coords = a_coords;\n"
130 "}\n";
131 }
132 }
133
~ShaderReturnCase(void)134 ShaderReturnCase::~ShaderReturnCase(void)
135 {
136 }
137
init(void)138 void ShaderReturnCase::init(void)
139 {
140 try
141 {
142 ShaderRenderCase::init();
143 }
144 catch (const CompileFailed &)
145 {
146 if (m_requirements & REQUIRE_DYNAMIC_LOOPS)
147 {
148 const bool isSupported =
149 m_isVertexCase ? m_ctxInfo.isVertexDynamicLoopSupported() : m_ctxInfo.isFragmentDynamicLoopSupported();
150 if (!isSupported)
151 throw tcu::NotSupportedError("Dynamic loops not supported");
152 }
153
154 throw;
155 }
156 }
157
ShaderReturnTests(Context & context)158 ShaderReturnTests::ShaderReturnTests(Context &context) : TestCaseGroup(context, "return", "Return Statement Tests")
159 {
160 }
161
~ShaderReturnTests(void)162 ShaderReturnTests::~ShaderReturnTests(void)
163 {
164 }
165
makeConditionalReturnInFuncCase(Context & context,const char * name,const char * description,ReturnMode returnMode,bool isVertex)166 ShaderReturnCase *makeConditionalReturnInFuncCase(Context &context, const char *name, const char *description,
167 ReturnMode returnMode, bool isVertex)
168 {
169 // Template
170 StringTemplate tmpl("${COORDSTORAGE} ${COORDPREC} vec4 ${COORDS};\n"
171 "${EXTRADECL}\n"
172 "${COORDPREC} vec4 getColor (void)\n"
173 "{\n"
174 " if (${RETURNCOND})\n"
175 " return vec4(${COORDS}.xyz, 1.0);\n"
176 " return vec4(${COORDS}.wzy, 1.0);\n"
177 "}\n\n"
178 "void main (void)\n"
179 "{\n"
180 "${POSITIONWRITE}"
181 " ${OUTPUT} = getColor();\n"
182 "}\n");
183
184 const char *coords = isVertex ? "a_coords" : "v_coords";
185
186 map<string, string> params;
187
188 params["COORDSTORAGE"] = isVertex ? "attribute" : "varying";
189 params["COORDPREC"] = isVertex ? "highp" : "mediump";
190 params["OUTPUT"] = isVertex ? "v_color" : "gl_FragColor";
191 params["COORDS"] = coords;
192 params["EXTRADECL"] = isVertex ? "attribute highp vec4 a_position;\nvarying mediump vec4 v_color;\n" : "";
193 params["POSITIONWRITE"] = isVertex ? " gl_Position = a_position;\n" : "";
194
195 switch (returnMode)
196 {
197 case RETURNMODE_ALWAYS:
198 params["RETURNCOND"] = "true";
199 break;
200 case RETURNMODE_NEVER:
201 params["RETURNCOND"] = "false";
202 break;
203 case RETURNMODE_DYNAMIC:
204 params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0";
205 break;
206 default:
207 DE_ASSERT(false);
208 }
209
210 return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(),
211 getEvalFunc(returnMode));
212 }
213
makeOutputWriteReturnCase(Context & context,const char * name,const char * description,bool inFunction,ReturnMode returnMode,bool isVertex)214 ShaderReturnCase *makeOutputWriteReturnCase(Context &context, const char *name, const char *description,
215 bool inFunction, ReturnMode returnMode, bool isVertex)
216 {
217 // Template
218 StringTemplate tmpl(inFunction ? "${COORDATTRS} vec4 ${COORDS};\n"
219 "${EXTRADECL}\n"
220 "void myfunc (void)\n"
221 "{\n"
222 " ${OUTPUT} = vec4(${COORDS}.xyz, 1.0);\n"
223 " if (${RETURNCOND})\n"
224 " return;\n"
225 " ${OUTPUT} = vec4(${COORDS}.wzy, 1.0);\n"
226 "}\n\n"
227 "void main (void)\n"
228 "{\n"
229 "${POSITIONWRITE}"
230 " myfunc();\n"
231 "}\n" :
232 "${COORDATTRS} vec4 ${COORDS};\n"
233 "uniform mediump int ui_one;\n"
234 "${EXTRADECL}\n"
235 "void main ()\n"
236 "{\n"
237 "${POSITIONWRITE}"
238 " ${OUTPUT} = vec4(${COORDS}.xyz, 1.0);\n"
239 " if (${RETURNCOND})\n"
240 " return;\n"
241 " ${OUTPUT} = vec4(${COORDS}.wzy, 1.0);\n"
242 "}\n");
243
244 const char *coords = isVertex ? "a_coords" : "v_coords";
245
246 map<string, string> params;
247
248 params["COORDATTRS"] = isVertex ? "attribute highp" : "varying mediump";
249 params["COORDS"] = coords;
250 params["OUTPUT"] = isVertex ? "v_color" : "gl_FragColor";
251 params["EXTRADECL"] = isVertex ? "attribute highp vec4 a_position;\nvarying mediump vec4 v_color;\n" : "";
252 params["POSITIONWRITE"] = isVertex ? " gl_Position = a_position;\n" : "";
253
254 switch (returnMode)
255 {
256 case RETURNMODE_ALWAYS:
257 params["RETURNCOND"] = "true";
258 break;
259 case RETURNMODE_NEVER:
260 params["RETURNCOND"] = "false";
261 break;
262 case RETURNMODE_DYNAMIC:
263 params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0";
264 break;
265 default:
266 DE_ASSERT(false);
267 }
268
269 return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(),
270 getEvalFunc(returnMode));
271 }
272
makeReturnInLoopCase(Context & context,const char * name,const char * description,bool isDynamicLoop,ReturnMode returnMode,bool isVertex)273 ShaderReturnCase *makeReturnInLoopCase(Context &context, const char *name, const char *description, bool isDynamicLoop,
274 ReturnMode returnMode, bool isVertex)
275 {
276 // Template
277 StringTemplate tmpl("${COORDSTORAGE} ${COORDPREC} vec4 ${COORDS};\n"
278 "uniform mediump int ui_one;\n"
279 "${EXTRADECL}\n"
280 "${COORDPREC} vec4 getCoords (void)\n"
281 "{\n"
282 " ${COORDPREC} vec4 coords = ${COORDS};\n"
283 " for (int i = 0; i < ${ITERLIMIT}; i++)\n"
284 " {\n"
285 " if (${RETURNCOND})\n"
286 " return coords;\n"
287 " coords = coords.wzyx;\n"
288 " }\n"
289 " return coords;\n"
290 "}\n\n"
291 "void main (void)\n"
292 "{\n"
293 "${POSITIONWRITE}"
294 " ${OUTPUT} = vec4(getCoords().xyz, 1.0);\n"
295 "}\n");
296
297 const char *coords = isVertex ? "a_coords" : "v_coords";
298
299 map<string, string> params;
300
301 params["COORDSTORAGE"] = isVertex ? "attribute" : "varying";
302 params["COORDPREC"] = isVertex ? "highp" : "mediump";
303 params["OUTPUT"] = isVertex ? "v_color" : "gl_FragColor";
304 params["COORDS"] = coords;
305 params["EXTRADECL"] = isVertex ? "attribute highp vec4 a_position;\nvarying mediump vec4 v_color;\n" : "";
306 params["POSITIONWRITE"] = isVertex ? " gl_Position = a_position;\n" : "";
307 params["ITERLIMIT"] = isDynamicLoop ? "ui_one" : "1";
308
309 switch (returnMode)
310 {
311 case RETURNMODE_ALWAYS:
312 params["RETURNCOND"] = "true";
313 break;
314 case RETURNMODE_NEVER:
315 params["RETURNCOND"] = "false";
316 break;
317 case RETURNMODE_DYNAMIC:
318 params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0";
319 break;
320 default:
321 DE_ASSERT(false);
322 }
323
324 return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(),
325 getEvalFunc(returnMode), isDynamicLoop ? REQUIRE_DYNAMIC_LOOPS : 0);
326 }
327
getReturnModeName(ReturnMode mode)328 static const char *getReturnModeName(ReturnMode mode)
329 {
330 switch (mode)
331 {
332 case RETURNMODE_ALWAYS:
333 return "always";
334 case RETURNMODE_NEVER:
335 return "never";
336 case RETURNMODE_DYNAMIC:
337 return "dynamic";
338 default:
339 DE_ASSERT(false);
340 return DE_NULL;
341 }
342 }
343
getReturnModeDesc(ReturnMode mode)344 static const char *getReturnModeDesc(ReturnMode mode)
345 {
346 switch (mode)
347 {
348 case RETURNMODE_ALWAYS:
349 return "Always return";
350 case RETURNMODE_NEVER:
351 return "Never return";
352 case RETURNMODE_DYNAMIC:
353 return "Return based on coords";
354 default:
355 DE_ASSERT(false);
356 return DE_NULL;
357 }
358 }
359
init(void)360 void ShaderReturnTests::init(void)
361 {
362 // Single return statement in function.
363 addChild(new ShaderReturnCase(m_context, "single_return_vertex", "Single return statement in function", true,
364 "attribute highp vec4 a_position;\n"
365 "attribute highp vec4 a_coords;\n"
366 "varying highp vec4 v_color;\n\n"
367 "vec4 getColor (void)\n"
368 "{\n"
369 " return vec4(a_coords.xyz, 1.0);\n"
370 "}\n\n"
371 "void main (void)\n"
372 "{\n"
373 " gl_Position = a_position;\n"
374 " v_color = getColor();\n"
375 "}\n",
376 evalReturnAlways));
377 addChild(new ShaderReturnCase(m_context, "single_return_fragment", "Single return statement in function", false,
378 "varying mediump vec4 v_coords;\n"
379 "mediump vec4 getColor (void)\n"
380 "{\n"
381 " return vec4(v_coords.xyz, 1.0);\n"
382 "}\n\n"
383 "void main (void)\n"
384 "{\n"
385 " gl_FragColor = getColor();\n"
386 "}\n",
387 evalReturnAlways));
388
389 // Conditional return statement in function.
390 for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
391 {
392 for (int isFragment = 0; isFragment < 2; isFragment++)
393 {
394 string name = string("conditional_return_") + getReturnModeName((ReturnMode)returnMode) +
395 (isFragment ? "_fragment" : "_vertex");
396 string description = string(getReturnModeDesc((ReturnMode)returnMode)) + " in function";
397 addChild(makeConditionalReturnInFuncCase(m_context, name.c_str(), description.c_str(),
398 (ReturnMode)returnMode, isFragment == 0));
399 }
400 }
401
402 // Unconditional double return in function.
403 addChild(new ShaderReturnCase(m_context, "double_return_vertex", "Unconditional double return in function", true,
404 "attribute highp vec4 a_position;\n"
405 "attribute highp vec4 a_coords;\n"
406 "varying highp vec4 v_color;\n\n"
407 "vec4 getColor (void)\n"
408 "{\n"
409 " return vec4(a_coords.xyz, 1.0);\n"
410 " return vec4(a_coords.wzy, 1.0);\n"
411 "}\n\n"
412 "void main (void)\n"
413 "{\n"
414 " gl_Position = a_position;\n"
415 " v_color = getColor();\n"
416 "}\n",
417 evalReturnAlways));
418 addChild(new ShaderReturnCase(m_context, "double_return_fragment", "Unconditional double return in function", false,
419 "varying mediump vec4 v_coords;\n"
420 "mediump vec4 getColor (void)\n"
421 "{\n"
422 " return vec4(v_coords.xyz, 1.0);\n"
423 " return vec4(v_coords.wzy, 1.0);\n"
424 "}\n\n"
425 "void main (void)\n"
426 "{\n"
427 " gl_FragColor = getColor();\n"
428 "}\n",
429 evalReturnAlways));
430
431 // Last statement in main.
432 addChild(new ShaderReturnCase(m_context, "last_statement_in_main_vertex", "Return as a final statement in main()",
433 true,
434 "attribute highp vec4 a_position;\n"
435 "attribute highp vec4 a_coords;\n"
436 "varying highp vec4 v_color;\n\n"
437 "void main (void)\n"
438 "{\n"
439 " gl_Position = a_position;\n"
440 " v_color = vec4(a_coords.xyz, 1.0);\n"
441 " return;\n"
442 "}\n",
443 evalReturnAlways));
444 addChild(new ShaderReturnCase(m_context, "last_statement_in_main_fragment", "Return as a final statement in main()",
445 false,
446 "varying mediump vec4 v_coords;\n\n"
447 "void main (void)\n"
448 "{\n"
449 " gl_FragColor = vec4(v_coords.xyz, 1.0);\n"
450 " return;\n"
451 "}\n",
452 evalReturnAlways));
453
454 // Return between output variable writes.
455 for (int inFunc = 0; inFunc < 2; inFunc++)
456 {
457 for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
458 {
459 for (int isFragment = 0; isFragment < 2; isFragment++)
460 {
461 string name = string("output_write_") + (inFunc ? "in_func_" : "") +
462 getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex");
463 string desc = string(getReturnModeDesc((ReturnMode)returnMode)) +
464 (inFunc ? " in user-defined function" : " in main()") + " between output writes";
465
466 addChild(makeOutputWriteReturnCase(m_context, name.c_str(), desc.c_str(), inFunc != 0,
467 (ReturnMode)returnMode, isFragment == 0));
468 }
469 }
470 }
471
472 // Conditional return statement in loop.
473 for (int isDynamicLoop = 0; isDynamicLoop < 2; isDynamicLoop++)
474 {
475 for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
476 {
477 for (int isFragment = 0; isFragment < 2; isFragment++)
478 {
479 string name = string("return_in_") + (isDynamicLoop ? "dynamic" : "static") + "_loop_" +
480 getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex");
481 string description = string(getReturnModeDesc((ReturnMode)returnMode)) + " in loop";
482 addChild(makeReturnInLoopCase(m_context, name.c_str(), description.c_str(), isDynamicLoop != 0,
483 (ReturnMode)returnMode, isFragment == 0));
484 }
485 }
486 }
487
488 // Unconditional return in infinite loop.
489 addChild(new ShaderReturnCase(m_context, "return_in_infinite_loop_vertex", "Return in infinite loop", true,
490 "attribute highp vec4 a_position;\n"
491 "attribute highp vec4 a_coords;\n"
492 "varying highp vec4 v_color;\n"
493 "uniform int ui_zero;\n\n"
494 "highp vec4 getCoords (void)\n"
495 "{\n"
496 " for (int i = 1; i < 10; i += ui_zero)\n"
497 " return a_coords;\n"
498 " return a_coords.wzyx;\n"
499 "}\n\n"
500 "void main (void)\n"
501 "{\n"
502 " gl_Position = a_position;\n"
503 " v_color = vec4(getCoords().xyz, 1.0);\n"
504 " return;\n"
505 "}\n",
506 evalReturnAlways, REQUIRE_DYNAMIC_LOOPS));
507 addChild(new ShaderReturnCase(m_context, "return_in_infinite_loop_fragment", "Return in infinite loop", false,
508 "varying mediump vec4 v_coords;\n"
509 "uniform int ui_zero;\n\n"
510 "mediump vec4 getCoords (void)\n"
511 "{\n"
512 " for (int i = 1; i < 10; i += ui_zero)\n"
513 " return v_coords;\n"
514 " return v_coords.wzyx;\n"
515 "}\n\n"
516 "void main (void)\n"
517 "{\n"
518 " gl_FragColor = vec4(getCoords().xyz, 1.0);\n"
519 " return;\n"
520 "}\n",
521 evalReturnAlways, REQUIRE_DYNAMIC_LOOPS));
522 }
523
524 } // namespace Functional
525 } // namespace gles2
526 } // namespace deqp
527