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