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
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 gles2
44{
45namespace Functional
46{
47
48enum ReturnMode
49{
50	RETURNMODE_ALWAYS = 0,
51	RETURNMODE_NEVER,
52	RETURNMODE_DYNAMIC,
53
54	RETURNMODE_LAST
55};
56
57enum RequireFlags
58{
59	REQUIRE_DYNAMIC_LOOPS = (1<<0),
60};
61
62// Evaluation functions
63inline void evalReturnAlways	(ShaderEvalContext& c) { c.color.xyz() = c.coords.swizzle(0,1,2); }
64inline void evalReturnNever		(ShaderEvalContext& c) { c.color.xyz() = c.coords.swizzle(3,2,1); }
65inline 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
67static 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
80class ShaderReturnCase : public ShaderRenderCase
81{
82public:
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
88private:
89	const deUint32		m_requirements;
90};
91
92ShaderReturnCase::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
121ShaderReturnCase::~ShaderReturnCase (void)
122{
123}
124
125void 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
144ShaderReturnTests::ShaderReturnTests (Context& context)
145	: TestCaseGroup(context, "return", "Return Statement Tests")
146{
147}
148
149ShaderReturnTests::~ShaderReturnTests (void)
150{
151}
152
153ShaderReturnCase* 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
193ShaderReturnCase* 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
247ShaderReturnCase* 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
294static 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
307static 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
320void 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