1/*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2015 The Khronos Group Inc.
6 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
7 * Copyright (c) 2016 The Android Open Source Project
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 *
21 *//*!
22 * \file
23 * \brief Shader switch statement tests.
24 *
25 * Variables:
26 *  + Selection expression type: static, uniform, dynamic
27 *  + Switch layout - fall-through or use of default label
28 *  + Switch nested in loop/conditional statement
29 *  + Loop/conditional statement nested in switch
30 *//*--------------------------------------------------------------------*/
31
32#include "vktShaderRenderSwitchTests.hpp"
33#include "vktShaderRender.hpp"
34#include "tcuStringTemplate.hpp"
35#include "deMath.h"
36
37namespace vkt
38{
39namespace sr
40{
41namespace
42{
43
44static void setUniforms(ShaderRenderCaseInstance& instance, const tcu::Vec4&)
45{
46	instance.useUniform(0u, UI_TWO);
47}
48
49using std::string;
50
51class ShaderSwitchCase : public ShaderRenderCase
52{
53public:
54						ShaderSwitchCase			(tcu::TestContext&	testCtx,
55													 const string&		name,
56													 bool				isVertexCase,
57													 const string&		vtxSource,
58													 const string&		fragSource,
59													 ShaderEvalFunc		evalFunc,
60													 UniformSetupFunc	setupUniformsFunc);
61	virtual				~ShaderSwitchCase			(void);
62};
63
64ShaderSwitchCase::ShaderSwitchCase (tcu::TestContext&	testCtx,
65									const string&		name,
66									bool				isVertexCase,
67									const string&		vtxSource,
68									const string&		fragSource,
69									ShaderEvalFunc		evalFunc,
70									UniformSetupFunc	setupUniformsFunc)
71	: ShaderRenderCase (testCtx, name, isVertexCase, evalFunc, new UniformSetup(setupUniformsFunc), DE_NULL)
72{
73	m_vertShaderSource	= vtxSource;
74	m_fragShaderSource	= fragSource;
75}
76
77ShaderSwitchCase::~ShaderSwitchCase (void)
78{
79}
80
81enum SwitchType
82{
83	SWITCHTYPE_STATIC = 0,
84	SWITCHTYPE_UNIFORM,
85	SWITCHTYPE_DYNAMIC,
86
87	SWITCHTYPE_LAST
88};
89
90static void evalSwitchStatic	(ShaderEvalContext& evalCtx)	{ evalCtx.color.xyz() = evalCtx.coords.swizzle(1,2,3); }
91static void evalSwitchUniform	(ShaderEvalContext& evalCtx)	{ evalCtx.color.xyz() = evalCtx.coords.swizzle(1,2,3); }
92static void evalSwitchDynamic	(ShaderEvalContext& evalCtx)
93{
94	switch (int(deFloatFloor(evalCtx.coords.z()*1.5f + 2.0f)))
95	{
96		case 0:		evalCtx.color.xyz() = evalCtx.coords.swizzle(0,1,2);	break;
97		case 1:		evalCtx.color.xyz() = evalCtx.coords.swizzle(3,2,1);	break;
98		case 2:		evalCtx.color.xyz() = evalCtx.coords.swizzle(1,2,3);	break;
99		case 3:		evalCtx.color.xyz() = evalCtx.coords.swizzle(2,1,0);	break;
100		default:	evalCtx.color.xyz() = evalCtx.coords.swizzle(0,0,0);	break;
101	}
102}
103
104static de::MovePtr<ShaderSwitchCase> makeSwitchCase (tcu::TestContext& testCtx, const string& name, SwitchType type, bool isVertex, const LineStream& switchBody)
105{
106	std::ostringstream	vtx;
107	std::ostringstream	frag;
108	std::ostringstream&	op		= isVertex ? vtx : frag;
109
110	vtx << "#version 310 es\n"
111		<< "layout(location = 0) in highp vec4 a_position;\n"
112		<< "layout(location = 1) in highp vec4 a_coords;\n\n";
113	frag	<< "#version 310 es\n"
114			<< "layout(location = 0) out mediump vec4 o_color;\n";
115
116	if (isVertex)
117	{
118		vtx << "layout(location = 0) out mediump vec4 v_color;\n";
119		frag << "layout(location = 0) in mediump vec4 v_color;\n";
120	}
121	else
122	{
123		vtx << "layout(location = 0) out highp vec4 v_coords;\n";
124		frag << "layout(location = 0) in highp vec4 v_coords;\n";
125	}
126
127	if (type == SWITCHTYPE_UNIFORM)
128		op << "layout (std140, set=0, binding=0) uniform buffer0 { highp int ui_two; };\n";
129
130	vtx << "\n"
131		<< "void main (void)\n"
132		<< "{\n"
133		<< "	gl_Position = a_position;\n";
134	frag << "\n"
135		 << "void main (void)\n"
136		 << "{\n";
137
138	// Setup.
139	op << "	highp vec4 coords = " << (isVertex ? "a_coords" : "v_coords") << ";\n";
140	op << "	mediump vec3 res = vec3(0.0);\n\n";
141
142	// Switch body.
143	std::map<string, string> params;
144	params["CONDITION"] = type == SWITCHTYPE_STATIC		? "2"								:
145						  type == SWITCHTYPE_UNIFORM	? "ui_two"							:
146						  type == SWITCHTYPE_DYNAMIC	? "int(floor(coords.z*1.5 + 2.0))"	: "???";
147
148	op << tcu::StringTemplate(switchBody.str()).specialize(params);
149	op << "\n";
150
151	if (isVertex)
152	{
153		vtx << "	v_color = vec4(res, 1.0);\n";
154		frag << "	o_color = v_color;\n";
155	}
156	else
157	{
158		vtx << "	v_coords = a_coords;\n";
159		frag << "	o_color = vec4(res, 1.0);\n";
160	}
161
162	vtx << "}\n";
163	frag << "}\n";
164
165	return de::MovePtr<ShaderSwitchCase>(new ShaderSwitchCase(testCtx, name, isVertex, vtx.str(), frag.str(),
166															type == SWITCHTYPE_STATIC	? evalSwitchStatic	:
167															type == SWITCHTYPE_UNIFORM	? evalSwitchUniform	:
168															type == SWITCHTYPE_DYNAMIC	? evalSwitchDynamic	: (ShaderEvalFunc)DE_NULL,
169															type == SWITCHTYPE_UNIFORM	? setUniforms : DE_NULL));
170}
171
172class ShaderSwitchTests : public tcu::TestCaseGroup
173{
174public:
175							ShaderSwitchTests		(tcu::TestContext& context);
176	virtual					~ShaderSwitchTests		(void);
177
178	virtual void			init					(void);
179
180private:
181							ShaderSwitchTests		(const ShaderSwitchTests&);		// not allowed!
182	ShaderSwitchTests&		operator=				(const ShaderSwitchTests&);		// not allowed!
183
184	void					makeSwitchCases			(const string& name, const LineStream& switchBody, const bool skipDynamicType = false);
185};
186
187ShaderSwitchTests::ShaderSwitchTests (tcu::TestContext& testCtx)
188	: tcu::TestCaseGroup (testCtx, "switch")
189{
190}
191
192ShaderSwitchTests::~ShaderSwitchTests (void)
193{
194}
195
196void ShaderSwitchTests::makeSwitchCases (const string& name, const LineStream& switchBody, const bool skipDynamicType)
197{
198	static const char* switchTypeNames[] = { "static", "uniform", "dynamic" };
199	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(switchTypeNames) == SWITCHTYPE_LAST);
200
201	for (int type = 0; type < SWITCHTYPE_LAST; type++)
202	{
203		if (skipDynamicType && (type == SWITCHTYPE_DYNAMIC))
204			continue;
205
206		addChild(makeSwitchCase(m_testCtx, (name + "_" + switchTypeNames[type] + "_vertex"), (SwitchType)type, true,	switchBody).release());
207		addChild(makeSwitchCase(m_testCtx, (name + "_" + switchTypeNames[type] + "_fragment"), (SwitchType)type, false,	switchBody).release());
208	}
209}
210
211void ShaderSwitchTests::init (void)
212{
213	// Expected swizzles:
214	// 0: xyz
215	// 1: wzy
216	// 2: yzw
217	// 3: zyx
218
219	// Basic switch statement usage
220	makeSwitchCases("basic",
221		LineStream(1)
222		<< "switch (${CONDITION})"
223		<< "{"
224		<< "	case 0:		res = coords.xyz;	break;"
225		<< "	case 1:		res = coords.wzy;	break;"
226		<< "	case 2:		res = coords.yzw;	break;"
227		<< "	case 3:		res = coords.zyx;	break;"
228		<< "}");
229
230	// Constant expression in label
231	makeSwitchCases("const_expr_in_label",
232		LineStream(1)
233		<< "const int t = 2;"
234		<< "switch (${CONDITION})"
235		<< "{"
236		<< "	case int(0.0):	res = coords.xyz;	break;"
237		<< "	case 2-1:		res = coords.wzy;	break;"
238		<< "	case 3&(1<<1):	res = coords.yzw;	break;"
239		<< "	case t+1:		res = coords.zyx;	break;"
240		<< "}");
241
242	// Default label usage
243	makeSwitchCases("default_label",
244		LineStream(1)
245		<< "switch (${CONDITION})"
246		<< "{"
247		<< "	case 0:		res = coords.xyz;	break;"
248		<< "	case 1:		res = coords.wzy;	break;"
249		<< "	case 3:		res = coords.zyx;	break;"
250		<< "	default:	res = coords.yzw;"
251		<< "}");
252
253	// Default label usage
254	makeSwitchCases("default_not_last",
255		LineStream(1)
256		<< "switch (${CONDITION})"
257		<< "{"
258		<< "	case 0:		res = coords.xyz;	break;"
259		<< "	default:	res = coords.yzw;	break;"
260		<< "	case 1:		res = coords.wzy;	break;"
261		<< "	case 3:		res = coords.zyx;	break;"
262		<< "}");
263
264	// No match in switch without default label
265	makeSwitchCases("no_default_label",
266		LineStream(1)
267		<< "res = coords.yzw;\n"
268		<< "switch (${CONDITION})"
269		<< "{"
270		<< "	case 0:		res = coords.xyz;	break;"
271		<< "	case 1:		res = coords.wzy;	break;"
272		<< "	case 3:		res = coords.zyx;	break;"
273		<< "}");
274
275	// Default case only
276	makeSwitchCases("default_only",
277		LineStream(1)
278		<< "switch (${CONDITION})"
279		<< "{"
280		<< "	default:"
281		<< "		res = coords.yzw;"
282		<< "}", true);
283
284	// Empty case and default
285	makeSwitchCases("empty_case_default",
286		LineStream(1)
287		<< "switch (${CONDITION})"
288		<< "{"
289		<< "	case 2:"
290		<< "	default:"
291		<< "		res = coords.yzw;"
292		<< "}", true);
293
294	// Fall-through
295	makeSwitchCases("fall_through",
296		LineStream(1)
297		<< "switch (${CONDITION})"
298		<< "{"
299		<< "	case 0:		res = coords.xyz;	break;"
300		<< "	case 1:		res = coords.wzy;	break;"
301		<< "	case 2:		coords = coords.yzwx;"
302		<< "	case 4:		res = vec3(coords);	break;"
303		<< "	case 3:		res = coords.zyx;	break;"
304		<< "}");
305
306	// Fall-through
307	makeSwitchCases("fall_through_default",
308		LineStream(1)
309		<< "switch (${CONDITION})"
310		<< "{"
311		<< "	case 0:		res = coords.xyz;	break;"
312		<< "	case 1:		res = coords.wzy;	break;"
313		<< "	case 3:		res = coords.zyx;	break;"
314		<< "	case 2:		coords = coords.yzwx;"
315		<< "	default:	res = vec3(coords);"
316		<< "}");
317
318	// Fall-through
319	makeSwitchCases("conditional_fall_through",
320		LineStream(1)
321		<< "highp vec4 tmp = coords;"
322		<< "switch (${CONDITION})"
323		<< "{"
324		<< "	case 0:		res = coords.xyz;	break;"
325		<< "	case 1:		res = coords.wzy;	break;"
326		<< "	case 2:"
327		<< "		tmp = coords.yzwx;"
328		<< "	case 3:"
329		<< "		res = vec3(tmp);"
330		<< "		if (${CONDITION} != 3)"
331		<< "			break;"
332		<< "	default:	res = tmp.zyx;		break;"
333		<< "}");
334
335	// Fall-through
336	makeSwitchCases("conditional_fall_through_2",
337		LineStream(1)
338		<< "highp vec4 tmp = coords;"
339		<< "mediump int c = ${CONDITION};"
340		<< "switch (c)"
341		<< "{"
342		<< "	case 0:		res = coords.xyz;	break;"
343		<< "	case 1:		res = coords.wzy;	break;"
344		<< "	case 2:"
345		<< "		c += ${CONDITION};"
346		<< "		tmp = coords.yzwx;"
347		<< "	case 3:"
348		<< "		res = vec3(tmp);"
349		<< "		if (c == 4)"
350		<< "			break;"
351		<< "	default:	res = tmp.zyx;		break;"
352		<< "}");
353
354	// Basic switch statement usage
355	makeSwitchCases("scope",
356		LineStream(1)
357		<< "switch (${CONDITION})"
358		<< "{"
359		<< "	case 0:		res = coords.xyz;	break;"
360		<< "	case 1:		res = coords.wzy;	break;"
361		<< "	case 2:"
362		<< "	{"
363		<< "		mediump vec3 t = coords.yzw;"
364		<< "		res = t;"
365		<< "		break;"
366		<< "	}"
367		<< "	case 3:		res = coords.zyx;	break;"
368		<< "}");
369
370	// Switch in for loop
371	makeSwitchCases("switch_in_if",
372		LineStream(1)
373		<< "if (${CONDITION} >= 0)"
374		<< "{"
375		<< "	switch (${CONDITION})"
376		<< "	{"
377		<< "		case 0:		res = coords.xyz;	break;"
378		<< "		case 1:		res = coords.wzy;	break;"
379		<< "		case 2:		res = coords.yzw;	break;"
380		<< "		case 3:		res = coords.zyx;	break;"
381		<< "	}"
382		<< "}");
383
384	// Switch in for loop
385	makeSwitchCases("switch_in_for_loop",
386		LineStream(1)
387		<< "for (int i = 0; i <= ${CONDITION}; i++)"
388		<< "{"
389		<< "	switch (i)"
390		<< "	{"
391		<< "		case 0:		res = coords.xyz;	break;"
392		<< "		case 1:		res = coords.wzy;	break;"
393		<< "		case 2:		res = coords.yzw;	break;"
394		<< "		case 3:		res = coords.zyx;	break;"
395		<< "	}"
396		<< "}");
397
398
399	// Switch in while loop
400	makeSwitchCases("switch_in_while_loop",
401		LineStream(1)
402		<< "int i = 0;"
403		<< "while (i <= ${CONDITION})"
404		<< "{"
405		<< "	switch (i)"
406		<< "	{"
407		<< "		case 0:		res = coords.xyz;	break;"
408		<< "		case 1:		res = coords.wzy;	break;"
409		<< "		case 2:		res = coords.yzw;	break;"
410		<< "		case 3:		res = coords.zyx;	break;"
411		<< "	}"
412		<< "	i += 1;"
413		<< "}");
414
415	// Switch in do-while loop
416	makeSwitchCases("switch_in_do_while_loop",
417		LineStream(1)
418		<< "int i = 0;"
419		<< "do"
420		<< "{"
421		<< "	switch (i)"
422		<< "	{"
423		<< "		case 0:		res = coords.xyz;	break;"
424		<< "		case 1:		res = coords.wzy;	break;"
425		<< "		case 2:		res = coords.yzw;	break;"
426		<< "		case 3:		res = coords.zyx;	break;"
427		<< "	}"
428		<< "	i += 1;"
429		<< "} while (i <= ${CONDITION});");
430
431	// Basic switch statement usage
432	makeSwitchCases("if_in_switch",
433		LineStream(1)
434		<< "switch (${CONDITION})"
435		<< "{"
436		<< "	case 0:		res = coords.xyz;	break;"
437		<< "	case 1:		res = coords.wzy;	break;"
438		<< "	default:"
439		<< "		if (${CONDITION} == 2)"
440		<< "			res = coords.yzw;"
441		<< "		else"
442		<< "			res = coords.zyx;"
443		<< "		break;"
444		<< "}");
445
446	// Basic switch statement usage
447	makeSwitchCases("for_loop_in_switch",
448		LineStream(1)
449		<< "switch (${CONDITION})"
450		<< "{"
451		<< "	case 0:		res = coords.xyz;	break;"
452		<< "	case 1:"
453		<< "	case 2:"
454		<< "	{"
455		<< "		highp vec3 t = coords.yzw;"
456		<< "		for (int i = 0; i < ${CONDITION}; i++)"
457		<< "			t = t.zyx;"
458		<< "		res = t;"
459		<< "		break;"
460		<< "	}"
461		<< "	default:	res = coords.zyx;	break;"
462		<< "}");
463
464	// Basic switch statement usage
465	makeSwitchCases("while_loop_in_switch",
466		LineStream(1)
467		<< "switch (${CONDITION})"
468		<< "{"
469		<< "	case 0:		res = coords.xyz;	break;"
470		<< "	case 1:"
471		<< "	case 2:"
472		<< "	{"
473		<< "		highp vec3 t = coords.yzw;"
474		<< "		int i = 0;"
475		<< "		while (i < ${CONDITION})"
476		<< "		{"
477		<< "			t = t.zyx;"
478		<< "			i += 1;"
479		<< "		}"
480		<< "		res = t;"
481		<< "		break;"
482		<< "	}"
483		<< "	default:	res = coords.zyx;	break;"
484		<< "}");
485
486	// Basic switch statement usage
487	makeSwitchCases("do_while_loop_in_switch",
488		LineStream(1)
489		<< "switch (${CONDITION})"
490		<< "{"
491		<< "	case 0:		res = coords.xyz;	break;"
492		<< "	case 1:"
493		<< "	case 2:"
494		<< "	{"
495		<< "		highp vec3 t = coords.yzw;"
496		<< "		int i = 0;"
497		<< "		do"
498		<< "		{"
499		<< "			t = t.zyx;"
500		<< "			i += 1;"
501		<< "		} while (i < ${CONDITION});"
502		<< "		res = t;"
503		<< "		break;"
504		<< "	}"
505		<< "	default:	res = coords.zyx;	break;"
506		<< "}");
507
508	// Basic switch statement usage
509	makeSwitchCases("switch_in_switch",
510		LineStream(1)
511		<< "switch (${CONDITION})"
512		<< "{"
513		<< "	case 0:		res = coords.xyz;	break;"
514		<< "	case 1:"
515		<< "	case 2:"
516		<< "		switch (${CONDITION} - 1)"
517		<< "		{"
518		<< "			case 0:		res = coords.wzy;	break;"
519		<< "			case 1:		res = coords.yzw;	break;"
520		<< "		}"
521		<< "		break;"
522		<< "	default:	res = coords.zyx;	break;"
523		<< "}");
524}
525
526} // anonymous
527
528tcu::TestCaseGroup* createSwitchTests (tcu::TestContext& testCtx)
529{
530	return new ShaderSwitchTests(testCtx);
531}
532
533} // sr
534} // vkt
535