1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.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 "es3fShaderReturnTests.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 gles3
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 // Evaluation functions
evalReturnAlways(ShaderEvalContext & c)58 inline void evalReturnAlways (ShaderEvalContext& c) { c.color.xyz() = c.coords.swizzle(0,1,2); }
evalReturnNever(ShaderEvalContext & c)59 inline void evalReturnNever (ShaderEvalContext& c) { c.color.xyz() = c.coords.swizzle(3,2,1); }
evalReturnDynamic(ShaderEvalContext & c)60 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); }
61
getEvalFunc(ReturnMode mode)62 static ShaderEvalFunc getEvalFunc (ReturnMode mode)
63 {
64 switch (mode)
65 {
66 case RETURNMODE_ALWAYS: return evalReturnAlways;
67 case RETURNMODE_NEVER: return evalReturnNever;
68 case RETURNMODE_DYNAMIC: return evalReturnDynamic;
69 default:
70 DE_ASSERT(DE_FALSE);
71 return (ShaderEvalFunc)DE_NULL;
72 }
73 }
74
75 class ShaderReturnCase : public ShaderRenderCase
76 {
77 public:
78 ShaderReturnCase (Context& context, const char* name, const char* description, bool isVertexCase, const char* shaderSource, ShaderEvalFunc evalFunc);
79 virtual ~ShaderReturnCase (void);
80 };
81
ShaderReturnCase(Context & context,const char * name,const char * description,bool isVertexCase,const char * shaderSource,ShaderEvalFunc evalFunc)82 ShaderReturnCase::ShaderReturnCase (Context& context, const char* name, const char* description, bool isVertexCase, const char* shaderSource, ShaderEvalFunc evalFunc)
83 : ShaderRenderCase(context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name, description, isVertexCase, evalFunc)
84 {
85 if (isVertexCase)
86 {
87 m_vertShaderSource = shaderSource;
88 m_fragShaderSource =
89 "#version 300 es\n"
90 "in mediump vec4 v_color;\n"
91 "layout(location = 0) out mediump vec4 o_color;\n\n"
92 "void main (void)\n"
93 "{\n"
94 " o_color = v_color;\n"
95 "}\n";
96 }
97 else
98 {
99 m_fragShaderSource = shaderSource;
100 m_vertShaderSource =
101 "#version 300 es\n"
102 "in highp vec4 a_position;\n"
103 "in highp vec4 a_coords;\n"
104 "out mediump vec4 v_coords;\n\n"
105 "void main (void)\n"
106 "{\n"
107 " gl_Position = a_position;\n"
108 " v_coords = a_coords;\n"
109 "}\n";
110 }
111 }
112
~ShaderReturnCase(void)113 ShaderReturnCase::~ShaderReturnCase (void)
114 {
115 }
116
ShaderReturnTests(Context & context)117 ShaderReturnTests::ShaderReturnTests (Context& context)
118 : TestCaseGroup(context, "return", "Return Statement Tests")
119 {
120 }
121
~ShaderReturnTests(void)122 ShaderReturnTests::~ShaderReturnTests (void)
123 {
124 }
125
makeConditionalReturnInFuncCase(Context & context,const char * name,const char * description,ReturnMode returnMode,bool isVertex)126 ShaderReturnCase* makeConditionalReturnInFuncCase (Context& context, const char* name, const char* description, ReturnMode returnMode, bool isVertex)
127 {
128 // Template
129 StringTemplate tmpl(
130 "#version 300 es\n"
131 "in ${COORDPREC} vec4 ${COORDS};\n"
132 "${EXTRADECL}\n"
133 "${COORDPREC} vec4 getColor (void)\n"
134 "{\n"
135 " if (${RETURNCOND})\n"
136 " return vec4(${COORDS}.xyz, 1.0);\n"
137 " return vec4(${COORDS}.wzy, 1.0);\n"
138 "}\n\n"
139 "void main (void)\n"
140 "{\n"
141 "${POSITIONWRITE}"
142 " ${OUTPUT} = getColor();\n"
143 "}\n");
144
145 const char* coords = isVertex ? "a_coords" : "v_coords";
146
147 map<string, string> params;
148
149 params["COORDPREC"] = isVertex ? "highp" : "mediump";
150 params["OUTPUT"] = isVertex ? "v_color" : "o_color";
151 params["COORDS"] = coords;
152 params["EXTRADECL"] = isVertex ? "in highp vec4 a_position;\nout mediump vec4 v_color;\n" : "layout(location = 0) out mediump vec4 o_color;\n";
153 params["POSITIONWRITE"] = isVertex ? " gl_Position = a_position;\n" : "";
154
155 switch (returnMode)
156 {
157 case RETURNMODE_ALWAYS: params["RETURNCOND"] = "true"; break;
158 case RETURNMODE_NEVER: params["RETURNCOND"] = "false"; break;
159 case RETURNMODE_DYNAMIC: params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0"; break;
160 default: DE_ASSERT(DE_FALSE);
161 }
162
163 return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(), getEvalFunc(returnMode));
164 }
165
makeOutputWriteReturnCase(Context & context,const char * name,const char * description,bool inFunction,ReturnMode returnMode,bool isVertex)166 ShaderReturnCase* makeOutputWriteReturnCase (Context& context, const char* name, const char* description, bool inFunction, ReturnMode returnMode, bool isVertex)
167 {
168 // Template
169 StringTemplate tmpl(
170 inFunction
171 ?
172 "#version 300 es\n"
173 "in ${COORDPREC} vec4 ${COORDS};\n"
174 "${EXTRADECL}\n"
175 "void myfunc (void)\n"
176 "{\n"
177 " ${OUTPUT} = vec4(${COORDS}.xyz, 1.0);\n"
178 " if (${RETURNCOND})\n"
179 " return;\n"
180 " ${OUTPUT} = vec4(${COORDS}.wzy, 1.0);\n"
181 "}\n\n"
182 "void main (void)\n"
183 "{\n"
184 "${POSITIONWRITE}"
185 " myfunc();\n"
186 "}\n"
187 :
188 "#version 300 es\n"
189 "in ${COORDPREC} vec4 ${COORDS};\n"
190 "uniform mediump int ui_one;\n"
191 "${EXTRADECL}\n"
192 "void main ()\n"
193 "{\n"
194 "${POSITIONWRITE}"
195 " ${OUTPUT} = vec4(${COORDS}.xyz, 1.0);\n"
196 " if (${RETURNCOND})\n"
197 " return;\n"
198 " ${OUTPUT} = vec4(${COORDS}.wzy, 1.0);\n"
199 "}\n");
200
201 const char* coords = isVertex ? "a_coords" : "v_coords";
202
203 map<string, string> params;
204
205 params["COORDPREC"] = isVertex ? "highp" : "mediump";
206 params["COORDS"] = coords;
207 params["OUTPUT"] = isVertex ? "v_color" : "o_color";
208 params["EXTRADECL"] = isVertex ? "in highp vec4 a_position;\nout mediump vec4 v_color;\n" : "layout(location = 0) out mediump vec4 o_color;\n";
209 params["POSITIONWRITE"] = isVertex ? " gl_Position = a_position;\n" : "";
210
211 switch (returnMode)
212 {
213 case RETURNMODE_ALWAYS: params["RETURNCOND"] = "true"; break;
214 case RETURNMODE_NEVER: params["RETURNCOND"] = "false"; break;
215 case RETURNMODE_DYNAMIC: params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0"; break;
216 default: DE_ASSERT(DE_FALSE);
217 }
218
219 return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(), getEvalFunc(returnMode));
220 }
221
makeReturnInLoopCase(Context & context,const char * name,const char * description,bool isDynamicLoop,ReturnMode returnMode,bool isVertex)222 ShaderReturnCase* makeReturnInLoopCase (Context& context, const char* name, const char* description, bool isDynamicLoop, ReturnMode returnMode, bool isVertex)
223 {
224 // Template
225 StringTemplate tmpl(
226 "#version 300 es\n"
227 "in ${COORDPREC} vec4 ${COORDS};\n"
228 "uniform mediump int ui_one;\n"
229 "${EXTRADECL}\n"
230 "${COORDPREC} vec4 getCoords (void)\n"
231 "{\n"
232 " ${COORDPREC} vec4 coords = ${COORDS};\n"
233 " for (int i = 0; i < ${ITERLIMIT}; i++)\n"
234 " {\n"
235 " if (${RETURNCOND})\n"
236 " return coords;\n"
237 " coords = coords.wzyx;\n"
238 " }\n"
239 " return coords;\n"
240 "}\n\n"
241 "void main (void)\n"
242 "{\n"
243 "${POSITIONWRITE}"
244 " ${OUTPUT} = vec4(getCoords().xyz, 1.0);\n"
245 "}\n");
246
247 const char* coords = isVertex ? "a_coords" : "v_coords";
248
249 map<string, string> params;
250
251 params["COORDPREC"] = isVertex ? "highp" : "mediump";
252 params["OUTPUT"] = isVertex ? "v_color" : "o_color";
253 params["COORDS"] = coords;
254 params["EXTRADECL"] = isVertex ? "in highp vec4 a_position;\nout mediump vec4 v_color;\n" : "layout(location = 0) out mediump vec4 o_color;\n";
255 params["POSITIONWRITE"] = isVertex ? " gl_Position = a_position;\n" : "";
256 params["ITERLIMIT"] = isDynamicLoop ? "ui_one" : "1";
257
258 switch (returnMode)
259 {
260 case RETURNMODE_ALWAYS: params["RETURNCOND"] = "true"; break;
261 case RETURNMODE_NEVER: params["RETURNCOND"] = "false"; break;
262 case RETURNMODE_DYNAMIC: params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0"; break;
263 default: DE_ASSERT(DE_FALSE);
264 }
265
266 return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(), getEvalFunc(returnMode));
267 }
268
getReturnModeName(ReturnMode mode)269 static const char* getReturnModeName (ReturnMode mode)
270 {
271 switch (mode)
272 {
273 case RETURNMODE_ALWAYS: return "always";
274 case RETURNMODE_NEVER: return "never";
275 case RETURNMODE_DYNAMIC: return "dynamic";
276 default:
277 DE_ASSERT(DE_FALSE);
278 return DE_NULL;
279 }
280 }
281
getReturnModeDesc(ReturnMode mode)282 static const char* getReturnModeDesc (ReturnMode mode)
283 {
284 switch (mode)
285 {
286 case RETURNMODE_ALWAYS: return "Always return";
287 case RETURNMODE_NEVER: return "Never return";
288 case RETURNMODE_DYNAMIC: return "Return based on coords";
289 default:
290 DE_ASSERT(DE_FALSE);
291 return DE_NULL;
292 }
293 }
294
init(void)295 void ShaderReturnTests::init (void)
296 {
297 // Single return statement in function.
298 addChild(new ShaderReturnCase(m_context, "single_return_vertex", "Single return statement in function", true,
299 "#version 300 es\n"
300 "in highp vec4 a_position;\n"
301 "in highp vec4 a_coords;\n"
302 "out highp vec4 v_color;\n\n"
303 "vec4 getColor (void)\n"
304 "{\n"
305 " return vec4(a_coords.xyz, 1.0);\n"
306 "}\n\n"
307 "void main (void)\n"
308 "{\n"
309 " gl_Position = a_position;\n"
310 " v_color = getColor();\n"
311 "}\n", evalReturnAlways));
312 addChild(new ShaderReturnCase(m_context, "single_return_fragment", "Single return statement in function", false,
313 "#version 300 es\n"
314 "in mediump vec4 v_coords;\n"
315 "layout(location = 0) out mediump vec4 o_color;\n"
316 "mediump vec4 getColor (void)\n"
317 "{\n"
318 " return vec4(v_coords.xyz, 1.0);\n"
319 "}\n\n"
320 "void main (void)\n"
321 "{\n"
322 " o_color = getColor();\n"
323 "}\n", evalReturnAlways));
324
325 // Conditional return statement in function.
326 for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
327 {
328 for (int isFragment = 0; isFragment < 2; isFragment++)
329 {
330 string name = string("conditional_return_") + getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex");
331 string description = string(getReturnModeDesc((ReturnMode)returnMode)) + " in function";
332 addChild(makeConditionalReturnInFuncCase(m_context, name.c_str(), description.c_str(), (ReturnMode)returnMode, isFragment == 0));
333 }
334 }
335
336 // Unconditional double return in function.
337 addChild(new ShaderReturnCase(m_context, "double_return_vertex", "Unconditional double return in function", true,
338 "#version 300 es\n"
339 "in highp vec4 a_position;\n"
340 "in highp vec4 a_coords;\n"
341 "out highp vec4 v_color;\n\n"
342 "vec4 getColor (void)\n"
343 "{\n"
344 " return vec4(a_coords.xyz, 1.0);\n"
345 " return vec4(a_coords.wzy, 1.0);\n"
346 "}\n\n"
347 "void main (void)\n"
348 "{\n"
349 " gl_Position = a_position;\n"
350 " v_color = getColor();\n"
351 "}\n", evalReturnAlways));
352 addChild(new ShaderReturnCase(m_context, "double_return_fragment", "Unconditional double return in function", false,
353 "#version 300 es\n"
354 "in mediump vec4 v_coords;\n"
355 "layout(location = 0) out mediump vec4 o_color;\n\n"
356 "mediump vec4 getColor (void)\n"
357 "{\n"
358 " return vec4(v_coords.xyz, 1.0);\n"
359 " return vec4(v_coords.wzy, 1.0);\n"
360 "}\n\n"
361 "void main (void)\n"
362 "{\n"
363 " o_color = getColor();\n"
364 "}\n", evalReturnAlways));
365
366 // Last statement in main.
367 addChild(new ShaderReturnCase(m_context, "last_statement_in_main_vertex", "Return as a final statement in main()", true,
368 "#version 300 es\n"
369 "in highp vec4 a_position;\n"
370 "in highp vec4 a_coords;\n"
371 "out highp vec4 v_color;\n\n"
372 "void main (void)\n"
373 "{\n"
374 " gl_Position = a_position;\n"
375 " v_color = vec4(a_coords.xyz, 1.0);\n"
376 " return;\n"
377 "}\n", evalReturnAlways));
378 addChild(new ShaderReturnCase(m_context, "last_statement_in_main_fragment", "Return as a final statement in main()", false,
379 "#version 300 es\n"
380 "in mediump vec4 v_coords;\n"
381 "layout(location = 0) out mediump vec4 o_color;\n\n"
382 "void main (void)\n"
383 "{\n"
384 " o_color = vec4(v_coords.xyz, 1.0);\n"
385 " return;\n"
386 "}\n", evalReturnAlways));
387
388 // Return between output variable writes.
389 for (int inFunc = 0; inFunc < 2; inFunc++)
390 {
391 for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
392 {
393 for (int isFragment = 0; isFragment < 2; isFragment++)
394 {
395 string name = string("output_write_") + (inFunc ? "in_func_" : "") + getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex");
396 string desc = string(getReturnModeDesc((ReturnMode)returnMode)) + (inFunc ? " in user-defined function" : " in main()") + " between output writes";
397
398 addChild(makeOutputWriteReturnCase(m_context, name.c_str(), desc.c_str(), inFunc != 0, (ReturnMode)returnMode, isFragment == 0));
399 }
400 }
401 }
402
403 // Conditional return statement in loop.
404 for (int isDynamicLoop = 0; isDynamicLoop < 2; isDynamicLoop++)
405 {
406 for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
407 {
408 for (int isFragment = 0; isFragment < 2; isFragment++)
409 {
410 string name = string("return_in_") + (isDynamicLoop ? "dynamic" : "static") + "_loop_" + getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex");
411 string description = string(getReturnModeDesc((ReturnMode)returnMode)) + " in loop";
412 addChild(makeReturnInLoopCase(m_context, name.c_str(), description.c_str(), isDynamicLoop != 0, (ReturnMode)returnMode, isFragment == 0));
413 }
414 }
415 }
416
417 // Unconditional return in infinite loop.
418 addChild(new ShaderReturnCase(m_context, "return_in_infinite_loop_vertex", "Return in infinite loop", true,
419 "#version 300 es\n"
420 "in highp vec4 a_position;\n"
421 "in highp vec4 a_coords;\n"
422 "out highp vec4 v_color;\n"
423 "uniform int ui_zero;\n\n"
424 "highp vec4 getCoords (void)\n"
425 "{\n"
426 " for (int i = 1; i < 10; i += ui_zero)\n"
427 " return a_coords;\n"
428 " return a_coords.wzyx;\n"
429 "}\n\n"
430 "void main (void)\n"
431 "{\n"
432 " gl_Position = a_position;\n"
433 " v_color = vec4(getCoords().xyz, 1.0);\n"
434 " return;\n"
435 "}\n", evalReturnAlways));
436 addChild(new ShaderReturnCase(m_context, "return_in_infinite_loop_fragment", "Return in infinite loop", false,
437 "#version 300 es\n"
438 "in mediump vec4 v_coords;\n"
439 "layout(location = 0) out mediump vec4 o_color;\n"
440 "uniform int ui_zero;\n\n"
441 "mediump vec4 getCoords (void)\n"
442 "{\n"
443 " for (int i = 1; i < 10; i += ui_zero)\n"
444 " return v_coords;\n"
445 " return v_coords.wzyx;\n"
446 "}\n\n"
447 "void main (void)\n"
448 "{\n"
449 " o_color = vec4(getCoords().xyz, 1.0);\n"
450 " return;\n"
451 "}\n", evalReturnAlways));
452 }
453
454 } // Functional
455 } // gles3
456 } // deqp
457