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)
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		group->addChild(makeSwitchCase(group->getContext(), (string(name) + "_" + switchTypeNames[type] + "_vertex").c_str(),	desc, (SwitchType)type, true,	switchBody));
164		group->addChild(makeSwitchCase(group->getContext(), (string(name) + "_" + switchTypeNames[type] + "_fragment").c_str(),	desc, (SwitchType)type, false,	switchBody));
165	}
166}
167
168ShaderSwitchTests::ShaderSwitchTests (Context& context)
169	: TestCaseGroup(context, "switch", "Switch statement tests")
170{
171}
172
173ShaderSwitchTests::~ShaderSwitchTests (void)
174{
175}
176
177void ShaderSwitchTests::init (void)
178{
179	// Expected swizzles:
180	// 0: xyz
181	// 1: wzy
182	// 2: yzw
183	// 3: zyx
184
185	makeSwitchCases(this, "basic", "Basic switch statement usage",
186		LineStream(1)
187		<< "switch (${CONDITION})"
188		<< "{"
189		<< "	case 0:		res = coords.xyz;	break;"
190		<< "	case 1:		res = coords.wzy;	break;"
191		<< "	case 2:		res = coords.yzw;	break;"
192		<< "	case 3:		res = coords.zyx;	break;"
193		<< "}");
194
195	makeSwitchCases(this, "const_expr_in_label", "Constant expression in label",
196		LineStream(1)
197		<< "const int t = 2;"
198		<< "switch (${CONDITION})"
199		<< "{"
200		<< "	case int(0.0):	res = coords.xyz;	break;"
201		<< "	case 2-1:		res = coords.wzy;	break;"
202		<< "	case 3&(1<<1):	res = coords.yzw;	break;"
203		<< "	case t+1:		res = coords.zyx;	break;"
204		<< "}");
205
206	makeSwitchCases(this, "default_label", "Default label usage",
207		LineStream(1)
208		<< "switch (${CONDITION})"
209		<< "{"
210		<< "	case 0:		res = coords.xyz;	break;"
211		<< "	case 1:		res = coords.wzy;	break;"
212		<< "	case 3:		res = coords.zyx;	break;"
213		<< "	default:	res = coords.yzw;"
214		<< "}");
215
216	makeSwitchCases(this, "default_not_last", "Default label usage",
217		LineStream(1)
218		<< "switch (${CONDITION})"
219		<< "{"
220		<< "	case 0:		res = coords.xyz;	break;"
221		<< "	default:	res = coords.yzw;	break;"
222		<< "	case 1:		res = coords.wzy;	break;"
223		<< "	case 3:		res = coords.zyx;	break;"
224		<< "}");
225
226	makeSwitchCases(this, "no_default_label", "No match in switch without default label",
227		LineStream(1)
228		<< "res = coords.yzw;\n"
229		<< "switch (${CONDITION})"
230		<< "{"
231		<< "	case 0:		res = coords.xyz;	break;"
232		<< "	case 1:		res = coords.wzy;	break;"
233		<< "	case 3:		res = coords.zyx;	break;"
234		<< "}");
235
236	makeSwitchCases(this, "fall_through", "Fall-through",
237		LineStream(1)
238		<< "switch (${CONDITION})"
239		<< "{"
240		<< "	case 0:		res = coords.xyz;	break;"
241		<< "	case 1:		res = coords.wzy;	break;"
242		<< "	case 2:		coords = coords.yzwx;"
243		<< "	case 4:		res = vec3(coords);	break;"
244		<< "	case 3:		res = coords.zyx;	break;"
245		<< "}");
246
247	makeSwitchCases(this, "fall_through_default", "Fall-through",
248		LineStream(1)
249		<< "switch (${CONDITION})"
250		<< "{"
251		<< "	case 0:		res = coords.xyz;	break;"
252		<< "	case 1:		res = coords.wzy;	break;"
253		<< "	case 3:		res = coords.zyx;	break;"
254		<< "	case 2:		coords = coords.yzwx;"
255		<< "	default:	res = vec3(coords);"
256		<< "}");
257
258	makeSwitchCases(this, "conditional_fall_through", "Fall-through",
259		LineStream(1)
260		<< "highp vec4 tmp = coords;"
261		<< "switch (${CONDITION})"
262		<< "{"
263		<< "	case 0:		res = coords.xyz;	break;"
264		<< "	case 1:		res = coords.wzy;	break;"
265		<< "	case 2:"
266		<< "		tmp = coords.yzwx;"
267		<< "	case 3:"
268		<< "		res = vec3(tmp);"
269		<< "		if (${CONDITION} != 3)"
270		<< "			break;"
271		<< "	default:	res = tmp.zyx;		break;"
272		<< "}");
273
274	makeSwitchCases(this, "conditional_fall_through_2", "Fall-through",
275		LineStream(1)
276		<< "highp vec4 tmp = coords;"
277		<< "mediump int c = ${CONDITION};"
278		<< "switch (c)"
279		<< "{"
280		<< "	case 0:		res = coords.xyz;	break;"
281		<< "	case 1:		res = coords.wzy;	break;"
282		<< "	case 2:"
283		<< "		c += ${CONDITION};"
284		<< "		tmp = coords.yzwx;"
285		<< "	case 3:"
286		<< "		res = vec3(tmp);"
287		<< "		if (c == 4)"
288		<< "			break;"
289		<< "	default:	res = tmp.zyx;		break;"
290		<< "}");
291
292	makeSwitchCases(this, "scope", "Basic switch statement usage",
293		LineStream(1)
294		<< "switch (${CONDITION})"
295		<< "{"
296		<< "	case 0:		res = coords.xyz;	break;"
297		<< "	case 1:		res = coords.wzy;	break;"
298		<< "	case 2:"
299		<< "	{"
300		<< "		mediump vec3 t = coords.yzw;"
301		<< "		res = t;"
302		<< "		break;"
303		<< "	}"
304		<< "	case 3:		res = coords.zyx;	break;"
305		<< "}");
306
307	makeSwitchCases(this, "switch_in_if", "Switch in for loop",
308		LineStream(1)
309		<< "if (${CONDITION} >= 0)"
310		<< "{"
311		<< "	switch (${CONDITION})"
312		<< "	{"
313		<< "		case 0:		res = coords.xyz;	break;"
314		<< "		case 1:		res = coords.wzy;	break;"
315		<< "		case 2:		res = coords.yzw;	break;"
316		<< "		case 3:		res = coords.zyx;	break;"
317		<< "	}"
318		<< "}");
319
320	makeSwitchCases(this, "switch_in_for_loop", "Switch in for loop",
321		LineStream(1)
322		<< "for (int i = 0; i <= ${CONDITION}; i++)"
323		<< "{"
324		<< "	switch (i)"
325		<< "	{"
326		<< "		case 0:		res = coords.xyz;	break;"
327		<< "		case 1:		res = coords.wzy;	break;"
328		<< "		case 2:		res = coords.yzw;	break;"
329		<< "		case 3:		res = coords.zyx;	break;"
330		<< "	}"
331		<< "}");
332
333	makeSwitchCases(this, "switch_in_while_loop", "Switch in while loop",
334		LineStream(1)
335		<< "int i = 0;"
336		<< "while (i <= ${CONDITION})"
337		<< "{"
338		<< "	switch (i)"
339		<< "	{"
340		<< "		case 0:		res = coords.xyz;	break;"
341		<< "		case 1:		res = coords.wzy;	break;"
342		<< "		case 2:		res = coords.yzw;	break;"
343		<< "		case 3:		res = coords.zyx;	break;"
344		<< "	}"
345		<< "	i += 1;"
346		<< "}");
347
348	makeSwitchCases(this, "switch_in_do_while_loop", "Switch in do-while loop",
349		LineStream(1)
350		<< "int i = 0;"
351		<< "do"
352		<< "{"
353		<< "	switch (i)"
354		<< "	{"
355		<< "		case 0:		res = coords.xyz;	break;"
356		<< "		case 1:		res = coords.wzy;	break;"
357		<< "		case 2:		res = coords.yzw;	break;"
358		<< "		case 3:		res = coords.zyx;	break;"
359		<< "	}"
360		<< "	i += 1;"
361		<< "} while (i <= ${CONDITION});");
362
363	makeSwitchCases(this, "if_in_switch", "Basic switch statement usage",
364		LineStream(1)
365		<< "switch (${CONDITION})"
366		<< "{"
367		<< "	case 0:		res = coords.xyz;	break;"
368		<< "	case 1:		res = coords.wzy;	break;"
369		<< "	default:"
370		<< "		if (${CONDITION} == 2)"
371		<< "			res = coords.yzw;"
372		<< "		else"
373		<< "			res = coords.zyx;"
374		<< "		break;"
375		<< "}");
376
377	makeSwitchCases(this, "for_loop_in_switch", "Basic switch statement usage",
378		LineStream(1)
379		<< "switch (${CONDITION})"
380		<< "{"
381		<< "	case 0:		res = coords.xyz;	break;"
382		<< "	case 1:"
383		<< "	case 2:"
384		<< "	{"
385		<< "		highp vec3 t = coords.yzw;"
386		<< "		for (int i = 0; i < ${CONDITION}; i++)"
387		<< "			t = t.zyx;"
388		<< "		res = t;"
389		<< "		break;"
390		<< "	}"
391		<< "	default:	res = coords.zyx;	break;"
392		<< "}");
393
394	makeSwitchCases(this, "while_loop_in_switch", "Basic switch statement usage",
395		LineStream(1)
396		<< "switch (${CONDITION})"
397		<< "{"
398		<< "	case 0:		res = coords.xyz;	break;"
399		<< "	case 1:"
400		<< "	case 2:"
401		<< "	{"
402		<< "		highp vec3 t = coords.yzw;"
403		<< "		int i = 0;"
404		<< "		while (i < ${CONDITION})"
405		<< "		{"
406		<< "			t = t.zyx;"
407		<< "			i += 1;"
408		<< "		}"
409		<< "		res = t;"
410		<< "		break;"
411		<< "	}"
412		<< "	default:	res = coords.zyx;	break;"
413		<< "}");
414
415	makeSwitchCases(this, "do_while_loop_in_switch", "Basic switch statement usage",
416		LineStream(1)
417		<< "switch (${CONDITION})"
418		<< "{"
419		<< "	case 0:		res = coords.xyz;	break;"
420		<< "	case 1:"
421		<< "	case 2:"
422		<< "	{"
423		<< "		highp vec3 t = coords.yzw;"
424		<< "		int i = 0;"
425		<< "		do"
426		<< "		{"
427		<< "			t = t.zyx;"
428		<< "			i += 1;"
429		<< "		} while (i < ${CONDITION});"
430		<< "		res = t;"
431		<< "		break;"
432		<< "	}"
433		<< "	default:	res = coords.zyx;	break;"
434		<< "}");
435
436	makeSwitchCases(this, "switch_in_switch", "Basic switch statement usage",
437		LineStream(1)
438		<< "switch (${CONDITION})"
439		<< "{"
440		<< "	case 0:		res = coords.xyz;	break;"
441		<< "	case 1:"
442		<< "	case 2:"
443		<< "		switch (${CONDITION} - 1)"
444		<< "		{"
445		<< "			case 0:		res = coords.wzy;	break;"
446		<< "			case 1:		res = coords.yzw;	break;"
447		<< "		}"
448		<< "		break;"
449		<< "	default:	res = coords.zyx;	break;"
450		<< "}");
451
452	// Negative cases.
453	ShaderLibrary library(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo());
454	vector<tcu::TestNode*> negativeCases = library.loadShaderFile("shaders/switch.test");
455
456	for (vector<tcu::TestNode*>::iterator i = negativeCases.begin(); i != negativeCases.end(); i++)
457		addChild(*i);
458}
459
460} // Functional
461} // gles3
462} // deqp
463