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