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::string;
36 using std::ostringstream;
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) { c.color.xyz() = c.coords.swizzle(0,1,2); }
evalReturnNever(ShaderEvalContext & c)64 inline void evalReturnNever (ShaderEvalContext& c) { c.color.xyz() = c.coords.swizzle(3,2,1); }
evalReturnDynamic(ShaderEvalContext & c)65 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); }
66
getEvalFunc(ReturnMode mode)67 static 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
80 class ShaderReturnCase : public ShaderRenderCase
81 {
82 public:
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
88 private:
89 const deUint32 m_requirements;
90 };
91
ShaderReturnCase(Context & context,const char * name,const char * description,bool isVertexCase,const char * shaderSource,ShaderEvalFunc evalFunc,deUint32 requirements)92 ShaderReturnCase::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
~ShaderReturnCase(void)121 ShaderReturnCase::~ShaderReturnCase (void)
122 {
123 }
124
init(void)125 void 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
ShaderReturnTests(Context & context)144 ShaderReturnTests::ShaderReturnTests (Context& context)
145 : TestCaseGroup(context, "return", "Return Statement Tests")
146 {
147 }
148
~ShaderReturnTests(void)149 ShaderReturnTests::~ShaderReturnTests (void)
150 {
151 }
152
makeConditionalReturnInFuncCase(Context & context,const char * name,const char * description,ReturnMode returnMode,bool isVertex)153 ShaderReturnCase* 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
makeOutputWriteReturnCase(Context & context,const char * name,const char * description,bool inFunction,ReturnMode returnMode,bool isVertex)193 ShaderReturnCase* 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
makeReturnInLoopCase(Context & context,const char * name,const char * description,bool isDynamicLoop,ReturnMode returnMode,bool isVertex)247 ShaderReturnCase* 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
getReturnModeName(ReturnMode mode)294 static 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
getReturnModeDesc(ReturnMode mode)307 static 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
init(void)320 void 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