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 switch statement tests.
22 *
23 * Variables:
24 *  + Selection expression type: static, uniform, dynamic
25 *  + Switch layout - fall-through or use of default label
26 *  + Switch nested in loop/conditional statement
27 *  + Loop/conditional statement nested in switch
28 *//*--------------------------------------------------------------------*/
29
30#include "es3fShaderSwitchTests.hpp"
31#include "glsShaderRenderCase.hpp"
32#include "glsShaderLibrary.hpp"
33#include "tcuStringTemplate.hpp"
34#include "deMath.h"
35
36namespace deqp
37{
38namespace gles3
39{
40namespace Functional
41{
42
43using namespace deqp::gls;
44using std::string;
45using std::map;
46using std::vector;
47
48class ShaderSwitchCase : public ShaderRenderCase
49{
50public:
51						ShaderSwitchCase			(Context& context, const char* name, const char* description, bool isVertexCase, const char* vtxSource, const char* fragSource, ShaderEvalFunc evalFunc);
52	virtual				~ShaderSwitchCase			(void);
53};
54
55ShaderSwitchCase::ShaderSwitchCase (Context& context, const char* name, const char* description, bool isVertexCase, const char* vtxSource, const char* fragSource, ShaderEvalFunc evalFunc)
56	: ShaderRenderCase(context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name, description, isVertexCase, evalFunc)
57{
58	m_vertShaderSource	= vtxSource;
59	m_fragShaderSource	= fragSource;
60}
61
62ShaderSwitchCase::~ShaderSwitchCase (void)
63{
64}
65
66enum SwitchType
67{
68	SWITCHTYPE_STATIC = 0,
69	SWITCHTYPE_UNIFORM,
70	SWITCHTYPE_DYNAMIC,
71
72	SWITCHTYPE_LAST
73};
74
75static void evalSwitchStatic	(ShaderEvalContext& evalCtx)	{ evalCtx.color.xyz() = evalCtx.coords.swizzle(1,2,3);	}
76static void evalSwitchUniform	(ShaderEvalContext& evalCtx)	{ evalCtx.color.xyz() = evalCtx.coords.swizzle(1,2,3);	}
77static void evalSwitchDynamic	(ShaderEvalContext& evalCtx)
78{
79	switch (int(deFloatFloor(evalCtx.coords.z()*1.5f + 2.0f)))
80	{
81		case 0:		evalCtx.color.xyz() = evalCtx.coords.swizzle(0,1,2);	break;
82		case 1:		evalCtx.color.xyz() = evalCtx.coords.swizzle(3,2,1);	break;
83		case 2:		evalCtx.color.xyz() = evalCtx.coords.swizzle(1,2,3);	break;
84		case 3:		evalCtx.color.xyz() = evalCtx.coords.swizzle(2,1,0);	break;
85		default:	evalCtx.color.xyz() = evalCtx.coords.swizzle(0,0,0);	break;
86	}
87}
88
89static tcu::TestCase* makeSwitchCase (Context& context, const char* name, const char* desc, SwitchType type, bool isVertex, const LineStream& switchBody)
90{
91	std::ostringstream	vtx;
92	std::ostringstream	frag;
93	std::ostringstream&	op		= isVertex ? vtx : frag;
94
95	vtx << "#version 300 es\n"
96		<< "in highp vec4 a_position;\n"
97		<< "in highp vec4 a_coords;\n";
98	frag << "#version 300 es\n"
99		 << "layout(location = 0) out mediump vec4 o_color;\n";
100
101	if (isVertex)
102	{
103		vtx << "out mediump vec4 v_color;\n";
104		frag << "in mediump vec4 v_color;\n";
105	}
106	else
107	{
108		vtx << "out highp vec4 v_coords;\n";
109		frag << "in highp vec4 v_coords;\n";
110	}
111
112	if (type == SWITCHTYPE_UNIFORM)
113		op << "uniform highp int ui_two;\n";
114
115	vtx << "\n"
116		<< "void main (void)\n"
117		<< "{\n"
118		<< "	gl_Position = a_position;\n";
119	frag << "\n"
120		 << "void main (void)\n"
121		 << "{\n";
122
123	// Setup.
124	op << "	highp vec4 coords = " << (isVertex ? "a_coords" : "v_coords") << ";\n";
125	op << "	mediump vec3 res = vec3(0.0);\n\n";
126
127	// Switch body.
128	map<string, string> params;
129	params["CONDITION"] = type == SWITCHTYPE_STATIC		? "2"								:
130						  type == SWITCHTYPE_UNIFORM	? "ui_two"							:
131						  type == SWITCHTYPE_DYNAMIC	? "int(floor(coords.z*1.5 + 2.0))"	: "???";
132
133	op << tcu::StringTemplate(switchBody.str()).specialize(params).c_str();
134	op << "\n";
135
136	if (isVertex)
137	{
138		vtx << "	v_color = vec4(res, 1.0);\n";
139		frag << "	o_color = v_color;\n";
140	}
141	else
142	{
143		vtx << "	v_coords = a_coords;\n";
144		frag << "	o_color = vec4(res, 1.0);\n";
145	}
146
147	vtx << "}\n";
148	frag << "}\n";
149
150	return new ShaderSwitchCase(context, name, desc, isVertex, vtx.str().c_str(), frag.str().c_str(),
151								type == SWITCHTYPE_STATIC	? evalSwitchStatic	:
152								type == SWITCHTYPE_UNIFORM	? evalSwitchUniform	:
153								type == SWITCHTYPE_DYNAMIC	? evalSwitchDynamic	: (ShaderEvalFunc)DE_NULL);
154}
155
156static void makeSwitchCases (TestCaseGroup* group, const char* name, const char* desc, const LineStream& switchBody, const bool skipDynamicType = false)
157{
158	static const char* switchTypeNames[] = { "static", "uniform", "dynamic" };
159	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(switchTypeNames) == SWITCHTYPE_LAST);
160
161	for (int type = 0; type < SWITCHTYPE_LAST; type++)
162	{
163		if (skipDynamicType && (type == SWITCHTYPE_DYNAMIC))
164			continue;
165
166		group->addChild(makeSwitchCase(group->getContext(), (string(name) + "_" + switchTypeNames[type] + "_vertex").c_str(),	desc, (SwitchType)type, true,	switchBody));
167		group->addChild(makeSwitchCase(group->getContext(), (string(name) + "_" + switchTypeNames[type] + "_fragment").c_str(),	desc, (SwitchType)type, false,	switchBody));
168	}
169}
170
171ShaderSwitchTests::ShaderSwitchTests (Context& context)
172	: TestCaseGroup(context, "switch", "Switch statement tests")
173{
174}
175
176ShaderSwitchTests::~ShaderSwitchTests (void)
177{
178}
179
180void ShaderSwitchTests::init (void)
181{
182	// Expected swizzles:
183	// 0: xyz
184	// 1: wzy
185	// 2: yzw
186	// 3: zyx
187
188	makeSwitchCases(this, "basic", "Basic switch statement usage",
189		LineStream(1)
190		<< "switch (${CONDITION})"
191		<< "{"
192		<< "	case 0:		res = coords.xyz;	break;"
193		<< "	case 1:		res = coords.wzy;	break;"
194		<< "	case 2:		res = coords.yzw;	break;"
195		<< "	case 3:		res = coords.zyx;	break;"
196		<< "}");
197
198	makeSwitchCases(this, "const_expr_in_label", "Constant expression in label",
199		LineStream(1)
200		<< "const int t = 2;"
201		<< "switch (${CONDITION})"
202		<< "{"
203		<< "	case int(0.0):	res = coords.xyz;	break;"
204		<< "	case 2-1:		res = coords.wzy;	break;"
205		<< "	case 3&(1<<1):	res = coords.yzw;	break;"
206		<< "	case t+1:		res = coords.zyx;	break;"
207		<< "}");
208
209	makeSwitchCases(this, "default_label", "Default label usage",
210		LineStream(1)
211		<< "switch (${CONDITION})"
212		<< "{"
213		<< "	case 0:		res = coords.xyz;	break;"
214		<< "	case 1:		res = coords.wzy;	break;"
215		<< "	case 3:		res = coords.zyx;	break;"
216		<< "	default:	res = coords.yzw;"
217		<< "}");
218
219	makeSwitchCases(this, "default_not_last", "Default label usage",
220		LineStream(1)
221		<< "switch (${CONDITION})"
222		<< "{"
223		<< "	case 0:		res = coords.xyz;	break;"
224		<< "	default:	res = coords.yzw;	break;"
225		<< "	case 1:		res = coords.wzy;	break;"
226		<< "	case 3:		res = coords.zyx;	break;"
227		<< "}");
228
229	makeSwitchCases(this, "no_default_label", "No match in switch without default label",
230		LineStream(1)
231		<< "res = coords.yzw;\n"
232		<< "switch (${CONDITION})"
233		<< "{"
234		<< "	case 0:		res = coords.xyz;	break;"
235		<< "	case 1:		res = coords.wzy;	break;"
236		<< "	case 3:		res = coords.zyx;	break;"
237		<< "}");
238
239	makeSwitchCases(this, "default_only", "Default case only",
240		LineStream(1)
241		<< "switch (${CONDITION})"
242		<< "{"
243		<< "	default:"
244		<< "		res = coords.yzw;"
245		<< "}", true);
246
247	makeSwitchCases(this, "empty_case_default", "Empty case and default",
248		LineStream(1)
249		<< "switch (${CONDITION})"
250		<< "{"
251		<< "	case 2:"
252		<< "	default:"
253		<< "		res = coords.yzw;"
254		<< "}", true);
255
256	makeSwitchCases(this, "fall_through", "Fall-through",
257		LineStream(1)
258		<< "switch (${CONDITION})"
259		<< "{"
260		<< "	case 0:		res = coords.xyz;	break;"
261		<< "	case 1:		res = coords.wzy;	break;"
262		<< "	case 2:		coords = coords.yzwx;"
263		<< "	case 4:		res = vec3(coords);	break;"
264		<< "	case 3:		res = coords.zyx;	break;"
265		<< "}");
266
267	makeSwitchCases(this, "fall_through_default", "Fall-through",
268		LineStream(1)
269		<< "switch (${CONDITION})"
270		<< "{"
271		<< "	case 0:		res = coords.xyz;	break;"
272		<< "	case 1:		res = coords.wzy;	break;"
273		<< "	case 3:		res = coords.zyx;	break;"
274		<< "	case 2:		coords = coords.yzwx;"
275		<< "	default:	res = vec3(coords);"
276		<< "}");
277
278	makeSwitchCases(this, "conditional_fall_through", "Fall-through",
279		LineStream(1)
280		<< "highp vec4 tmp = coords;"
281		<< "switch (${CONDITION})"
282		<< "{"
283		<< "	case 0:		res = coords.xyz;	break;"
284		<< "	case 1:		res = coords.wzy;	break;"
285		<< "	case 2:"
286		<< "		tmp = coords.yzwx;"
287		<< "	case 3:"
288		<< "		res = vec3(tmp);"
289		<< "		if (${CONDITION} != 3)"
290		<< "			break;"
291		<< "	default:	res = tmp.zyx;		break;"
292		<< "}");
293
294	makeSwitchCases(this, "conditional_fall_through_2", "Fall-through",
295		LineStream(1)
296		<< "highp vec4 tmp = coords;"
297		<< "mediump int c = ${CONDITION};"
298		<< "switch (c)"
299		<< "{"
300		<< "	case 0:		res = coords.xyz;	break;"
301		<< "	case 1:		res = coords.wzy;	break;"
302		<< "	case 2:"
303		<< "		c += ${CONDITION};"
304		<< "		tmp = coords.yzwx;"
305		<< "	case 3:"
306		<< "		res = vec3(tmp);"
307		<< "		if (c == 4)"
308		<< "			break;"
309		<< "	default:	res = tmp.zyx;		break;"
310		<< "}");
311
312	makeSwitchCases(this, "scope", "Basic switch statement usage",
313		LineStream(1)
314		<< "switch (${CONDITION})"
315		<< "{"
316		<< "	case 0:		res = coords.xyz;	break;"
317		<< "	case 1:		res = coords.wzy;	break;"
318		<< "	case 2:"
319		<< "	{"
320		<< "		mediump vec3 t = coords.yzw;"
321		<< "		res = t;"
322		<< "		break;"
323		<< "	}"
324		<< "	case 3:		res = coords.zyx;	break;"
325		<< "}");
326
327	makeSwitchCases(this, "switch_in_if", "Switch in for loop",
328		LineStream(1)
329		<< "if (${CONDITION} >= 0)"
330		<< "{"
331		<< "	switch (${CONDITION})"
332		<< "	{"
333		<< "		case 0:		res = coords.xyz;	break;"
334		<< "		case 1:		res = coords.wzy;	break;"
335		<< "		case 2:		res = coords.yzw;	break;"
336		<< "		case 3:		res = coords.zyx;	break;"
337		<< "	}"
338		<< "}");
339
340	makeSwitchCases(this, "switch_in_for_loop", "Switch in for loop",
341		LineStream(1)
342		<< "for (int i = 0; i <= ${CONDITION}; i++)"
343		<< "{"
344		<< "	switch (i)"
345		<< "	{"
346		<< "		case 0:		res = coords.xyz;	break;"
347		<< "		case 1:		res = coords.wzy;	break;"
348		<< "		case 2:		res = coords.yzw;	break;"
349		<< "		case 3:		res = coords.zyx;	break;"
350		<< "	}"
351		<< "}");
352
353	makeSwitchCases(this, "switch_in_while_loop", "Switch in while loop",
354		LineStream(1)
355		<< "int i = 0;"
356		<< "while (i <= ${CONDITION})"
357		<< "{"
358		<< "	switch (i)"
359		<< "	{"
360		<< "		case 0:		res = coords.xyz;	break;"
361		<< "		case 1:		res = coords.wzy;	break;"
362		<< "		case 2:		res = coords.yzw;	break;"
363		<< "		case 3:		res = coords.zyx;	break;"
364		<< "	}"
365		<< "	i += 1;"
366		<< "}");
367
368	makeSwitchCases(this, "switch_in_do_while_loop", "Switch in do-while loop",
369		LineStream(1)
370		<< "int i = 0;"
371		<< "do"
372		<< "{"
373		<< "	switch (i)"
374		<< "	{"
375		<< "		case 0:		res = coords.xyz;	break;"
376		<< "		case 1:		res = coords.wzy;	break;"
377		<< "		case 2:		res = coords.yzw;	break;"
378		<< "		case 3:		res = coords.zyx;	break;"
379		<< "	}"
380		<< "	i += 1;"
381		<< "} while (i <= ${CONDITION});");
382
383	makeSwitchCases(this, "if_in_switch", "Basic switch statement usage",
384		LineStream(1)
385		<< "switch (${CONDITION})"
386		<< "{"
387		<< "	case 0:		res = coords.xyz;	break;"
388		<< "	case 1:		res = coords.wzy;	break;"
389		<< "	default:"
390		<< "		if (${CONDITION} == 2)"
391		<< "			res = coords.yzw;"
392		<< "		else"
393		<< "			res = coords.zyx;"
394		<< "		break;"
395		<< "}");
396
397	makeSwitchCases(this, "for_loop_in_switch", "Basic switch statement usage",
398		LineStream(1)
399		<< "switch (${CONDITION})"
400		<< "{"
401		<< "	case 0:		res = coords.xyz;	break;"
402		<< "	case 1:"
403		<< "	case 2:"
404		<< "	{"
405		<< "		highp vec3 t = coords.yzw;"
406		<< "		for (int i = 0; i < ${CONDITION}; i++)"
407		<< "			t = t.zyx;"
408		<< "		res = t;"
409		<< "		break;"
410		<< "	}"
411		<< "	default:	res = coords.zyx;	break;"
412		<< "}");
413
414	makeSwitchCases(this, "while_loop_in_switch", "Basic switch statement usage",
415		LineStream(1)
416		<< "switch (${CONDITION})"
417		<< "{"
418		<< "	case 0:		res = coords.xyz;	break;"
419		<< "	case 1:"
420		<< "	case 2:"
421		<< "	{"
422		<< "		highp vec3 t = coords.yzw;"
423		<< "		int i = 0;"
424		<< "		while (i < ${CONDITION})"
425		<< "		{"
426		<< "			t = t.zyx;"
427		<< "			i += 1;"
428		<< "		}"
429		<< "		res = t;"
430		<< "		break;"
431		<< "	}"
432		<< "	default:	res = coords.zyx;	break;"
433		<< "}");
434
435	makeSwitchCases(this, "do_while_loop_in_switch", "Basic switch statement usage",
436		LineStream(1)
437		<< "switch (${CONDITION})"
438		<< "{"
439		<< "	case 0:		res = coords.xyz;	break;"
440		<< "	case 1:"
441		<< "	case 2:"
442		<< "	{"
443		<< "		highp vec3 t = coords.yzw;"
444		<< "		int i = 0;"
445		<< "		do"
446		<< "		{"
447		<< "			t = t.zyx;"
448		<< "			i += 1;"
449		<< "		} while (i < ${CONDITION});"
450		<< "		res = t;"
451		<< "		break;"
452		<< "	}"
453		<< "	default:	res = coords.zyx;	break;"
454		<< "}");
455
456	makeSwitchCases(this, "switch_in_switch", "Basic switch statement usage",
457		LineStream(1)
458		<< "switch (${CONDITION})"
459		<< "{"
460		<< "	case 0:		res = coords.xyz;	break;"
461		<< "	case 1:"
462		<< "	case 2:"
463		<< "		switch (${CONDITION} - 1)"
464		<< "		{"
465		<< "			case 0:		res = coords.wzy;	break;"
466		<< "			case 1:		res = coords.yzw;	break;"
467		<< "		}"
468		<< "		break;"
469		<< "	default:	res = coords.zyx;	break;"
470		<< "}");
471
472	// Negative cases.
473	ShaderLibrary library(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo());
474	vector<tcu::TestNode*> negativeCases = library.loadShaderFile("shaders/switch.test");
475
476	for (vector<tcu::TestNode*>::iterator i = negativeCases.begin(); i != negativeCases.end(); i++)
477		addChild(*i);
478}
479
480} // Functional
481} // gles3
482} // deqp
483