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