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