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 return statement tests.
24  *//*--------------------------------------------------------------------*/
25 
26 #include "vktShaderRenderReturnTests.hpp"
27 #include "vktShaderRender.hpp"
28 #include "tcuStringTemplate.hpp"
29 
30 #include <map>
31 #include <string>
32 
33 namespace vkt
34 {
35 namespace sr
36 {
37 namespace
38 {
39 
40 enum ReturnMode
41 {
42     RETURNMODE_ALWAYS = 0,
43     RETURNMODE_NEVER,
44     RETURNMODE_DYNAMIC,
45 
46     RETURNMODE_LAST
47 };
48 
49 // Evaluation functions
evalReturnAlways(ShaderEvalContext & c)50 inline void evalReturnAlways(ShaderEvalContext &c)
51 {
52     c.color.xyz() = c.coords.swizzle(0, 1, 2);
53 }
evalReturnNever(ShaderEvalContext & c)54 inline void evalReturnNever(ShaderEvalContext &c)
55 {
56     c.color.xyz() = c.coords.swizzle(3, 2, 1);
57 }
evalReturnDynamic(ShaderEvalContext & c)58 inline void evalReturnDynamic(ShaderEvalContext &c)
59 {
60     c.color.xyz() = (c.coords.x() + c.coords.y() >= 0.0f) ? c.coords.swizzle(0, 1, 2) : c.coords.swizzle(3, 2, 1);
61 }
62 
getEvalFunc(ReturnMode mode)63 static ShaderEvalFunc getEvalFunc(ReturnMode mode)
64 {
65     switch (mode)
66     {
67     case RETURNMODE_ALWAYS:
68         return evalReturnAlways;
69     case RETURNMODE_NEVER:
70         return evalReturnNever;
71     case RETURNMODE_DYNAMIC:
72         return evalReturnDynamic;
73     default:
74         DE_ASSERT(false);
75         return (ShaderEvalFunc)DE_NULL;
76     }
77 }
78 
79 class ShaderReturnCase : public ShaderRenderCase
80 {
81 public:
82     ShaderReturnCase(tcu::TestContext &testCtx, const std::string &name, bool isVertexCase,
83                      const std::string &shaderSource, const ShaderEvalFunc evalFunc, const UniformSetup *uniformFunc);
84     virtual ~ShaderReturnCase(void);
85 };
86 
ShaderReturnCase(tcu::TestContext & testCtx,const std::string & name,bool isVertexCase,const std::string & shaderSource,const ShaderEvalFunc evalFunc,const UniformSetup * uniformFunc)87 ShaderReturnCase::ShaderReturnCase(tcu::TestContext &testCtx, const std::string &name, bool isVertexCase,
88                                    const std::string &shaderSource, const ShaderEvalFunc evalFunc,
89                                    const UniformSetup *uniformFunc)
90     : ShaderRenderCase(testCtx, name, isVertexCase, evalFunc, uniformFunc, DE_NULL)
91 {
92     if (isVertexCase)
93     {
94         m_vertShaderSource = shaderSource;
95         m_fragShaderSource = "#version 310 es\n"
96                              "layout(location = 0) in mediump vec4 v_color;\n"
97                              "layout(location = 0) out mediump vec4 o_color;\n\n"
98                              "void main (void)\n"
99                              "{\n"
100                              "    o_color = v_color;\n"
101                              "}\n";
102     }
103     else
104     {
105         m_fragShaderSource = shaderSource;
106         m_vertShaderSource = "#version 310 es\n"
107                              "layout(location = 0) in  highp   vec4 a_position;\n"
108                              "layout(location = 1) in  highp   vec4 a_coords;\n"
109                              "layout(location = 0) out mediump vec4 v_coords;\n\n"
110                              "void main (void)\n"
111                              "{\n"
112                              "    gl_Position = a_position;\n"
113                              "    v_coords = a_coords;\n"
114                              "}\n";
115     }
116 }
117 
~ShaderReturnCase(void)118 ShaderReturnCase::~ShaderReturnCase(void)
119 {
120 }
121 
122 class ReturnTestUniformSetup : public UniformSetup
123 {
124 public:
ReturnTestUniformSetup(const BaseUniformType uniformType)125     ReturnTestUniformSetup(const BaseUniformType uniformType) : m_uniformType(uniformType)
126     {
127     }
setup(ShaderRenderCaseInstance & instance,const tcu::Vec4 &) const128     virtual void setup(ShaderRenderCaseInstance &instance, const tcu::Vec4 &) const
129     {
130         instance.useUniform(0u, m_uniformType);
131     }
132 
133 private:
134     const BaseUniformType m_uniformType;
135 };
136 
137 // Test case builders.
138 
makeConditionalReturnInFuncCase(tcu::TestContext & context,const std::string & name,ReturnMode returnMode,bool isVertex)139 de::MovePtr<ShaderReturnCase> makeConditionalReturnInFuncCase(tcu::TestContext &context, const std::string &name,
140                                                               ReturnMode returnMode, bool isVertex)
141 {
142     tcu::StringTemplate tmpl("#version 310 es\n"
143                              "layout(location = ${COORDLOC}) in ${COORDPREC} vec4 ${COORDS};\n"
144                              "${EXTRADECL}\n"
145                              "${COORDPREC} vec4 getColor (void)\n"
146                              "{\n"
147                              "    if (${RETURNCOND})\n"
148                              "        return vec4(${COORDS}.xyz, 1.0);\n"
149                              "    return vec4(${COORDS}.wzy, 1.0);\n"
150                              "}\n\n"
151                              "void main (void)\n"
152                              "{\n"
153                              "${POSITIONWRITE}"
154                              "    ${OUTPUT} = getColor();\n"
155                              "}\n");
156 
157     const char *coords = isVertex ? "a_coords" : "v_coords";
158 
159     std::map<std::string, std::string> params;
160 
161     params["COORDLOC"]  = isVertex ? "1" : "0";
162     params["COORDPREC"] = isVertex ? "highp" : "mediump";
163     params["OUTPUT"]    = isVertex ? "v_color" : "o_color";
164     params["COORDS"]    = coords;
165     params["EXTRADECL"] =
166         isVertex ? "layout(location = 0) in highp vec4 a_position;\nlayout(location = 0) out mediump vec4 v_color;\n" :
167                    "layout(location = 0) out mediump vec4 o_color;\n";
168     params["POSITIONWRITE"] = isVertex ? "    gl_Position = a_position;\n" : "";
169 
170     switch (returnMode)
171     {
172     case RETURNMODE_ALWAYS:
173         params["RETURNCOND"] = "true";
174         break;
175     case RETURNMODE_NEVER:
176         params["RETURNCOND"] = "false";
177         break;
178     case RETURNMODE_DYNAMIC:
179         params["RETURNCOND"] = std::string(coords) + ".x+" + coords + ".y >= 0.0";
180         break;
181     default:
182         DE_ASSERT(false);
183     }
184 
185     return de::MovePtr<ShaderReturnCase>(
186         new ShaderReturnCase(context, name, isVertex, tmpl.specialize(params), getEvalFunc(returnMode), DE_NULL));
187 }
188 
makeOutputWriteReturnCase(tcu::TestContext & context,const std::string & name,bool inFunction,ReturnMode returnMode,bool isVertex)189 de::MovePtr<ShaderReturnCase> makeOutputWriteReturnCase(tcu::TestContext &context, const std::string &name,
190                                                         bool inFunction, ReturnMode returnMode, bool isVertex)
191 {
192     tcu::StringTemplate tmpl(inFunction ? "#version 310 es\n"
193                                           "layout(location = ${COORDLOC}) in ${COORDPREC} vec4 ${COORDS};\n"
194                                           "${EXTRADECL}\n"
195                                           "void myfunc (void)\n"
196                                           "{\n"
197                                           "    ${OUTPUT} = vec4(${COORDS}.xyz, 1.0);\n"
198                                           "    if (${RETURNCOND})\n"
199                                           "        return;\n"
200                                           "    ${OUTPUT} = vec4(${COORDS}.wzy, 1.0);\n"
201                                           "}\n\n"
202                                           "void main (void)\n"
203                                           "{\n"
204                                           "${POSITIONWRITE}"
205                                           "    myfunc();\n"
206                                           "}\n" :
207                                           "#version 310 es\n"
208                                           "layout(location = ${COORDLOC}) in ${COORDPREC} vec4 ${COORDS};\n"
209                                           "${EXTRADECL}\n"
210                                           "void main ()\n"
211                                           "{\n"
212                                           "${POSITIONWRITE}"
213                                           "    ${OUTPUT} = vec4(${COORDS}.xyz, 1.0);\n"
214                                           "    if (${RETURNCOND})\n"
215                                           "        return;\n"
216                                           "    ${OUTPUT} = vec4(${COORDS}.wzy, 1.0);\n"
217                                           "}\n");
218 
219     const char *coords = isVertex ? "a_coords" : "v_coords";
220 
221     std::map<std::string, std::string> params;
222 
223     params["COORDLOC"]  = isVertex ? "1" : "0";
224     params["COORDPREC"] = isVertex ? "highp" : "mediump";
225     params["COORDS"]    = coords;
226     params["OUTPUT"]    = isVertex ? "v_color" : "o_color";
227     params["EXTRADECL"] =
228         isVertex ? "layout(location = 0) in highp vec4 a_position;\nlayout(location = 0) out mediump vec4 v_color;\n" :
229                    "layout(location = 0) out mediump vec4 o_color;\n";
230     params["POSITIONWRITE"] = isVertex ? "    gl_Position = a_position;\n" : "";
231 
232     switch (returnMode)
233     {
234     case RETURNMODE_ALWAYS:
235         params["RETURNCOND"] = "true";
236         break;
237     case RETURNMODE_NEVER:
238         params["RETURNCOND"] = "false";
239         break;
240     case RETURNMODE_DYNAMIC:
241         params["RETURNCOND"] = std::string(coords) + ".x+" + coords + ".y >= 0.0";
242         break;
243     default:
244         DE_ASSERT(false);
245     }
246 
247     return de::MovePtr<ShaderReturnCase>(
248         new ShaderReturnCase(context, name, isVertex, tmpl.specialize(params), getEvalFunc(returnMode), DE_NULL));
249 }
250 
makeReturnInLoopCase(tcu::TestContext & context,const std::string & name,bool isDynamicLoop,ReturnMode returnMode,bool isVertex)251 de::MovePtr<ShaderReturnCase> makeReturnInLoopCase(tcu::TestContext &context, const std::string &name,
252                                                    bool isDynamicLoop, ReturnMode returnMode, bool isVertex)
253 {
254     tcu::StringTemplate tmpl("#version 310 es\n"
255                              "layout(location = ${COORDLOC}) in ${COORDPREC} vec4 ${COORDS};\n"
256                              "layout(binding = 0, std140) uniform something { mediump int ui_one; };\n"
257                              "${EXTRADECL}\n"
258                              "${COORDPREC} vec4 getCoords (void)\n"
259                              "{\n"
260                              "    ${COORDPREC} vec4 coords = ${COORDS};\n"
261                              "    for (int i = 0; i < ${ITERLIMIT}; i++)\n"
262                              "    {\n"
263                              "        if (${RETURNCOND})\n"
264                              "            return coords;\n"
265                              "        coords = coords.wzyx;\n"
266                              "    }\n"
267                              "    return coords;\n"
268                              "}\n\n"
269                              "void main (void)\n"
270                              "{\n"
271                              "${POSITIONWRITE}"
272                              "    ${OUTPUT} = vec4(getCoords().xyz, 1.0);\n"
273                              "}\n");
274 
275     const char *coords = isVertex ? "a_coords" : "v_coords";
276 
277     std::map<std::string, std::string> params;
278 
279     params["COORDLOC"]  = isVertex ? "1" : "0";
280     params["COORDPREC"] = isVertex ? "highp" : "mediump";
281     params["OUTPUT"]    = isVertex ? "v_color" : "o_color";
282     params["COORDS"]    = coords;
283     params["EXTRADECL"] =
284         isVertex ? "layout(location = 0) in highp vec4 a_position;\nlayout(location = 0) out mediump vec4 v_color;\n" :
285                    "layout(location = 0) out mediump vec4 o_color;\n";
286     params["POSITIONWRITE"] = isVertex ? "    gl_Position = a_position;\n" : "";
287     params["ITERLIMIT"]     = isDynamicLoop ? "ui_one" : "1";
288 
289     switch (returnMode)
290     {
291     case RETURNMODE_ALWAYS:
292         params["RETURNCOND"] = "true";
293         break;
294     case RETURNMODE_NEVER:
295         params["RETURNCOND"] = "false";
296         break;
297     case RETURNMODE_DYNAMIC:
298         params["RETURNCOND"] = std::string(coords) + ".x+" + coords + ".y >= 0.0";
299         break;
300     default:
301         DE_ASSERT(false);
302     }
303 
304     return de::MovePtr<ShaderReturnCase>(new ShaderReturnCase(
305         context, name, isVertex, tmpl.specialize(params), getEvalFunc(returnMode), new ReturnTestUniformSetup(UI_ONE)));
306 }
307 
getReturnModeName(ReturnMode mode)308 static const char *getReturnModeName(ReturnMode mode)
309 {
310     switch (mode)
311     {
312     case RETURNMODE_ALWAYS:
313         return "always";
314     case RETURNMODE_NEVER:
315         return "never";
316     case RETURNMODE_DYNAMIC:
317         return "dynamic";
318     default:
319         DE_ASSERT(false);
320         return DE_NULL;
321     }
322 }
323 
324 class ShaderReturnTests : public tcu::TestCaseGroup
325 {
326 public:
327     ShaderReturnTests(tcu::TestContext &context);
328     virtual ~ShaderReturnTests(void);
329     virtual void init(void);
330 
331 private:
332     ShaderReturnTests(const ShaderReturnTests &);            // not allowed!
333     ShaderReturnTests &operator=(const ShaderReturnTests &); // not allowed!
334 };
335 
ShaderReturnTests(tcu::TestContext & context)336 ShaderReturnTests::ShaderReturnTests(tcu::TestContext &context) : TestCaseGroup(context, "return")
337 {
338 }
339 
~ShaderReturnTests(void)340 ShaderReturnTests::~ShaderReturnTests(void)
341 {
342 }
343 
init(void)344 void ShaderReturnTests::init(void)
345 {
346     // Single return statement in function
347     addChild(new ShaderReturnCase(m_testCtx, "single_return_vertex", true,
348                                   "#version 310 es\n"
349                                   "layout(location = 0) in highp vec4 a_position;\n"
350                                   "layout(location = 1) in highp vec4 a_coords;\n"
351                                   "layout(location = 0) out mediump vec4 v_color;\n\n"
352                                   "vec4 getColor (void)\n"
353                                   "{\n"
354                                   "    return vec4(a_coords.xyz, 1.0);\n"
355                                   "}\n\n"
356                                   "void main (void)\n"
357                                   "{\n"
358                                   "    gl_Position = a_position;\n"
359                                   "    v_color = getColor();\n"
360                                   "}\n",
361                                   evalReturnAlways, DE_NULL));
362     // Single return statement in function
363     addChild(new ShaderReturnCase(m_testCtx, "single_return_fragment", false,
364                                   "#version 310 es\n"
365                                   "layout(location = 0) in mediump vec4 v_coords;\n"
366                                   "layout(location = 0) out mediump vec4 o_color;\n"
367                                   "mediump vec4 getColor (void)\n"
368                                   "{\n"
369                                   "    return vec4(v_coords.xyz, 1.0);\n"
370                                   "}\n\n"
371                                   "void main (void)\n"
372                                   "{\n"
373                                   "    o_color = getColor();\n"
374                                   "}\n",
375                                   evalReturnAlways, DE_NULL));
376 
377     // Conditional return statement in function.
378     for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
379     {
380         for (int isFragment = 0; isFragment < 2; isFragment++)
381         {
382             std::string name = std::string("conditional_return_") + getReturnModeName((ReturnMode)returnMode) +
383                                (isFragment ? "_fragment" : "_vertex");
384             de::MovePtr<ShaderReturnCase> testCase(
385                 makeConditionalReturnInFuncCase(m_testCtx, name, (ReturnMode)returnMode, isFragment == 0));
386             addChild(testCase.release());
387         }
388     }
389 
390     // Unconditional double return in function.
391     addChild(new ShaderReturnCase(m_testCtx, "double_return_vertex", true,
392                                   "#version 310 es\n"
393                                   "layout(location = 0) in highp vec4 a_position;\n"
394                                   "layout(location = 1) in highp vec4 a_coords;\n"
395                                   "layout(location = 0) out mediump vec4 v_color;\n\n"
396                                   "vec4 getColor (void)\n"
397                                   "{\n"
398                                   "    return vec4(a_coords.xyz, 1.0);\n"
399                                   "    return vec4(a_coords.wzy, 1.0);\n"
400                                   "}\n\n"
401                                   "void main (void)\n"
402                                   "{\n"
403                                   "    gl_Position = a_position;\n"
404                                   "    v_color = getColor();\n"
405                                   "}\n",
406                                   evalReturnAlways, DE_NULL));
407     // Unconditional double return in function
408     addChild(new ShaderReturnCase(m_testCtx, "double_return_fragment", false,
409                                   "#version 310 es\n"
410                                   "layout(location = 0) in mediump vec4 v_coords;\n"
411                                   "layout(location = 0) out mediump vec4 o_color;\n\n"
412                                   "mediump vec4 getColor (void)\n"
413                                   "{\n"
414                                   "    return vec4(v_coords.xyz, 1.0);\n"
415                                   "    return vec4(v_coords.wzy, 1.0);\n"
416                                   "}\n\n"
417                                   "void main (void)\n"
418                                   "{\n"
419                                   "    o_color = getColor();\n"
420                                   "}\n",
421                                   evalReturnAlways, DE_NULL));
422 
423     // Last statement in main.
424     // Return as a final statement in main()
425     addChild(new ShaderReturnCase(m_testCtx, "last_statement_in_main_vertex", true,
426                                   "#version 310 es\n"
427                                   "layout(location = 0) in highp vec4 a_position;\n"
428                                   "layout(location = 1) in highp vec4 a_coords;\n"
429                                   "layout(location = 0) out mediump vec4 v_color;\n\n"
430                                   "void main (void)\n"
431                                   "{\n"
432                                   "    gl_Position = a_position;\n"
433                                   "    v_color = vec4(a_coords.xyz, 1.0);\n"
434                                   "    return;\n"
435                                   "}\n",
436                                   evalReturnAlways, DE_NULL));
437     // Return as a final statement in main()
438     addChild(new ShaderReturnCase(m_testCtx, "last_statement_in_main_fragment", false,
439                                   "#version 310 es\n"
440                                   "layout(location = 0) in mediump vec4 v_coords;\n"
441                                   "layout(location = 0) out mediump vec4 o_color;\n\n"
442                                   "void main (void)\n"
443                                   "{\n"
444                                   "    o_color = vec4(v_coords.xyz, 1.0);\n"
445                                   "    return;\n"
446                                   "}\n",
447                                   evalReturnAlways, DE_NULL));
448 
449     // Return between output variable writes.
450     for (int inFunc = 0; inFunc < 2; inFunc++)
451     {
452         for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
453         {
454             for (int isFragment = 0; isFragment < 2; isFragment++)
455             {
456                 std::string name = std::string("output_write_") + (inFunc ? "in_func_" : "") +
457                                    getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex");
458                 de::MovePtr<ShaderReturnCase> testCase =
459                     (makeOutputWriteReturnCase(m_testCtx, name, inFunc != 0, (ReturnMode)returnMode, isFragment == 0));
460                 addChild(testCase.release());
461             }
462         }
463     }
464 
465     // Conditional return statement in loop.
466     for (int isDynamicLoop = 0; isDynamicLoop < 2; isDynamicLoop++)
467     {
468         for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
469         {
470             for (int isFragment = 0; isFragment < 2; isFragment++)
471             {
472                 std::string name = std::string("return_in_") + (isDynamicLoop ? "dynamic" : "static") + "_loop_" +
473                                    getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex");
474                 de::MovePtr<ShaderReturnCase> testCase(
475                     makeReturnInLoopCase(m_testCtx, name, isDynamicLoop != 0, (ReturnMode)returnMode, isFragment == 0));
476                 addChild(testCase.release());
477             }
478         }
479     }
480 
481     // Unconditional return in infinite loop.
482     addChild(new ShaderReturnCase(m_testCtx, "return_in_infinite_loop_vertex", true,
483                                   "#version 310 es\n"
484                                   "layout(location = 0) in highp vec4 a_position;\n"
485                                   "layout(location = 1) in highp vec4 a_coords;\n"
486                                   "layout(location = 0) out mediump vec4 v_color;\n"
487                                   "layout(binding = 0, std140) uniform something { int ui_zero; };\n"
488                                   "highp vec4 getCoords (void)\n"
489                                   "{\n"
490                                   "    for (int i = 1; i < 10; i += ui_zero)\n"
491                                   "        return a_coords;\n"
492                                   "    return a_coords.wzyx;\n"
493                                   "}\n\n"
494                                   "void main (void)\n"
495                                   "{\n"
496                                   "    gl_Position = a_position;\n"
497                                   "    v_color = vec4(getCoords().xyz, 1.0);\n"
498                                   "    return;\n"
499                                   "}\n",
500                                   evalReturnAlways, new ReturnTestUniformSetup(UI_ZERO)));
501     // Return in infinite loop
502     addChild(new ShaderReturnCase(m_testCtx, "return_in_infinite_loop_fragment", false,
503                                   "#version 310 es\n"
504                                   "layout(location = 0) in mediump vec4 v_coords;\n"
505                                   "layout(location = 0) out mediump vec4 o_color;\n"
506                                   "layout(binding = 0, std140) uniform something { int ui_zero; };\n\n"
507                                   "mediump vec4 getCoords (void)\n"
508                                   "{\n"
509                                   "    for (int i = 1; i < 10; i += ui_zero)\n"
510                                   "        return v_coords;\n"
511                                   "    return v_coords.wzyx;\n"
512                                   "}\n\n"
513                                   "void main (void)\n"
514                                   "{\n"
515                                   "    o_color = vec4(getCoords().xyz, 1.0);\n"
516                                   "    return;\n"
517                                   "}\n",
518                                   evalReturnAlways, new ReturnTestUniformSetup(UI_ZERO)));
519 }
520 
521 } // namespace
522 
createReturnTests(tcu::TestContext & testCtx)523 tcu::TestCaseGroup *createReturnTests(tcu::TestContext &testCtx)
524 {
525     return new ShaderReturnTests(testCtx);
526 }
527 
528 } // namespace sr
529 } // namespace vkt
530