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