• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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