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
36 namespace deqp
37 {
38 namespace gles3
39 {
40 namespace Functional
41 {
42
43 using namespace deqp::gls;
44 using std::string;
45 using std::map;
46 using std::vector;
47
48 class ShaderSwitchCase : public ShaderRenderCase
49 {
50 public:
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
ShaderSwitchCase(Context & context,const char * name,const char * description,bool isVertexCase,const char * vtxSource,const char * fragSource,ShaderEvalFunc evalFunc)55 ShaderSwitchCase::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
~ShaderSwitchCase(void)62 ShaderSwitchCase::~ShaderSwitchCase (void)
63 {
64 }
65
66 enum SwitchType
67 {
68 SWITCHTYPE_STATIC = 0,
69 SWITCHTYPE_UNIFORM,
70 SWITCHTYPE_DYNAMIC,
71
72 SWITCHTYPE_LAST
73 };
74
evalSwitchStatic(ShaderEvalContext & evalCtx)75 static void evalSwitchStatic (ShaderEvalContext& evalCtx) { evalCtx.color.xyz() = evalCtx.coords.swizzle(1,2,3); }
evalSwitchUniform(ShaderEvalContext & evalCtx)76 static void evalSwitchUniform (ShaderEvalContext& evalCtx) { evalCtx.color.xyz() = evalCtx.coords.swizzle(1,2,3); }
evalSwitchDynamic(ShaderEvalContext & evalCtx)77 static 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
makeSwitchCase(Context & context,const char * name,const char * desc,SwitchType type,bool isVertex,const LineStream & switchBody)89 static 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
makeSwitchCases(TestCaseGroup * group,const char * name,const char * desc,const LineStream & switchBody,const bool skipDynamicType=false)156 static 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
ShaderSwitchTests(Context & context)171 ShaderSwitchTests::ShaderSwitchTests (Context& context)
172 : TestCaseGroup(context, "switch", "Switch statement tests")
173 {
174 }
175
~ShaderSwitchTests(void)176 ShaderSwitchTests::~ShaderSwitchTests (void)
177 {
178 }
179
init(void)180 void 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