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