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
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 gles3
44{
45namespace Functional
46{
47
48enum ReturnMode
49{
50	RETURNMODE_ALWAYS = 0,
51	RETURNMODE_NEVER,
52	RETURNMODE_DYNAMIC,
53
54	RETURNMODE_LAST
55};
56
57// Evaluation functions
58inline void evalReturnAlways	(ShaderEvalContext& c) { c.color.xyz() = c.coords.swizzle(0,1,2); }
59inline void evalReturnNever		(ShaderEvalContext& c) { c.color.xyz() = c.coords.swizzle(3,2,1); }
60inline 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
62static 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
75class ShaderReturnCase : public ShaderRenderCase
76{
77public:
78						ShaderReturnCase			(Context& context, const char* name, const char* description, bool isVertexCase, const char* shaderSource, ShaderEvalFunc evalFunc);
79	virtual				~ShaderReturnCase			(void);
80};
81
82ShaderReturnCase::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
113ShaderReturnCase::~ShaderReturnCase (void)
114{
115}
116
117ShaderReturnTests::ShaderReturnTests (Context& context)
118	: TestCaseGroup(context, "return", "Return Statement Tests")
119{
120}
121
122ShaderReturnTests::~ShaderReturnTests (void)
123{
124}
125
126ShaderReturnCase* 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
166ShaderReturnCase* 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
222ShaderReturnCase* 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
269static 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
282static 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
295void 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