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