Home | History | Annotate | Download | only in functional
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program OpenGL ES 2.0 Module
      3  * -------------------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Shader return statement tests.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "es2fShaderReturnTests.hpp"
     25 #include "glsShaderRenderCase.hpp"
     26 #include "tcuStringTemplate.hpp"
     27 
     28 #include <map>
     29 #include <sstream>
     30 #include <string>
     31 
     32 using tcu::StringTemplate;
     33 
     34 using std::map;
     35 using std::string;
     36 using std::ostringstream;
     37 
     38 using namespace glu;
     39 using namespace deqp::gls;
     40 
     41 namespace deqp
     42 {
     43 namespace gles2
     44 {
     45 namespace Functional
     46 {
     47 
     48 enum ReturnMode
     49 {
     50 	RETURNMODE_ALWAYS = 0,
     51 	RETURNMODE_NEVER,
     52 	RETURNMODE_DYNAMIC,
     53 
     54 	RETURNMODE_LAST
     55 };
     56 
     57 enum RequireFlags
     58 {
     59 	REQUIRE_DYNAMIC_LOOPS = (1<<0),
     60 };
     61 
     62 // Evaluation functions
     63 inline void evalReturnAlways	(ShaderEvalContext& c) { c.color.xyz() = c.coords.swizzle(0,1,2); }
     64 inline void evalReturnNever		(ShaderEvalContext& c) { c.color.xyz() = c.coords.swizzle(3,2,1); }
     65 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); }
     66 
     67 static ShaderEvalFunc getEvalFunc (ReturnMode mode)
     68 {
     69 	switch (mode)
     70 	{
     71 		case RETURNMODE_ALWAYS:		return evalReturnAlways;
     72 		case RETURNMODE_NEVER:		return evalReturnNever;
     73 		case RETURNMODE_DYNAMIC:	return evalReturnDynamic;
     74 		default:
     75 			DE_ASSERT(DE_FALSE);
     76 			return (ShaderEvalFunc)DE_NULL;
     77 	}
     78 }
     79 
     80 class ShaderReturnCase : public ShaderRenderCase
     81 {
     82 public:
     83 						ShaderReturnCase			(Context& context, const char* name, const char* description, bool isVertexCase, const char* shaderSource, ShaderEvalFunc evalFunc, deUint32 requirements = 0);
     84 	virtual				~ShaderReturnCase			(void);
     85 
     86 	void				init						(void);
     87 
     88 private:
     89 	const deUint32		m_requirements;
     90 };
     91 
     92 ShaderReturnCase::ShaderReturnCase (Context& context, const char* name, const char* description, bool isVertexCase, const char* shaderSource, ShaderEvalFunc evalFunc, deUint32 requirements)
     93 	: ShaderRenderCase	(context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name, description, isVertexCase, evalFunc)
     94 	, m_requirements	(requirements)
     95 {
     96 	if (isVertexCase)
     97 	{
     98 		m_vertShaderSource = shaderSource;
     99 		m_fragShaderSource =
    100 			"varying mediump vec4 v_color;\n\n"
    101 			"void main (void)\n"
    102 			"{\n"
    103 			"    gl_FragColor = v_color;\n"
    104 			"}\n";
    105 	}
    106 	else
    107 	{
    108 		m_fragShaderSource = shaderSource;
    109 		m_vertShaderSource =
    110 			"attribute highp   vec4 a_position;\n"
    111 			"attribute highp   vec4 a_coords;\n"
    112 			"varying   mediump vec4 v_coords;\n\n"
    113 			"void main (void)\n"
    114 			"{\n"
    115 			"    gl_Position = a_position;\n"
    116 			"    v_coords = a_coords;\n"
    117 			"}\n";
    118 	}
    119 }
    120 
    121 ShaderReturnCase::~ShaderReturnCase (void)
    122 {
    123 }
    124 
    125 void ShaderReturnCase::init (void)
    126 {
    127 	try
    128 	{
    129 		ShaderRenderCase::init();
    130 	}
    131 	catch (const CompileFailed&)
    132 	{
    133 		if (m_requirements & REQUIRE_DYNAMIC_LOOPS)
    134 		{
    135 			const bool isSupported = m_isVertexCase ? m_ctxInfo.isVertexDynamicLoopSupported() : m_ctxInfo.isFragmentDynamicLoopSupported();
    136 			if (!isSupported)
    137 				throw tcu::NotSupportedError("Dynamic loops not supported");
    138 		}
    139 
    140 		throw;
    141 	}
    142 }
    143 
    144 ShaderReturnTests::ShaderReturnTests (Context& context)
    145 	: TestCaseGroup(context, "return", "Return Statement Tests")
    146 {
    147 }
    148 
    149 ShaderReturnTests::~ShaderReturnTests (void)
    150 {
    151 }
    152 
    153 ShaderReturnCase* makeConditionalReturnInFuncCase (Context& context, const char* name, const char* description, ReturnMode returnMode, bool isVertex)
    154 {
    155 	// Template
    156 	StringTemplate tmpl(
    157 		"${COORDSTORAGE} ${COORDPREC} vec4 ${COORDS};\n"
    158 		"${EXTRADECL}\n"
    159 		"${COORDPREC} vec4 getColor (void)\n"
    160 		"{\n"
    161 		"    if (${RETURNCOND})\n"
    162 		"        return vec4(${COORDS}.xyz, 1.0);\n"
    163 		"    return vec4(${COORDS}.wzy, 1.0);\n"
    164 		"}\n\n"
    165 		"void main (void)\n"
    166 		"{\n"
    167 		"${POSITIONWRITE}"
    168 		"    ${OUTPUT} = getColor();\n"
    169 		"}\n");
    170 
    171 	const char* coords = isVertex ? "a_coords" : "v_coords";
    172 
    173 	map<string, string> params;
    174 
    175 	params["COORDSTORAGE"]	= isVertex ? "attribute"	: "varying";
    176 	params["COORDPREC"]		= isVertex ? "highp"		: "mediump";
    177 	params["OUTPUT"]		= isVertex ? "v_color"		: "gl_FragColor";
    178 	params["COORDS"]		= coords;
    179 	params["EXTRADECL"]		= isVertex ? "attribute highp vec4 a_position;\nvarying mediump vec4 v_color;\n" : "";
    180 	params["POSITIONWRITE"]	= isVertex ? "    gl_Position = a_position;\n" : "";
    181 
    182 	switch (returnMode)
    183 	{
    184 		case RETURNMODE_ALWAYS:		params["RETURNCOND"] = "true";											break;
    185 		case RETURNMODE_NEVER:		params["RETURNCOND"] = "false";											break;
    186 		case RETURNMODE_DYNAMIC:	params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0";	break;
    187 		default:					DE_ASSERT(DE_FALSE);
    188 	}
    189 
    190 	return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(), getEvalFunc(returnMode));
    191 }
    192 
    193 ShaderReturnCase* makeOutputWriteReturnCase (Context& context, const char* name, const char* description, bool inFunction, ReturnMode returnMode, bool isVertex)
    194 {
    195 	// Template
    196 	StringTemplate tmpl(
    197 		inFunction
    198 		?
    199 			"${COORDATTRS} vec4 ${COORDS};\n"
    200 			"${EXTRADECL}\n"
    201 			"void myfunc (void)\n"
    202 			"{\n"
    203 			"    ${OUTPUT} = vec4(${COORDS}.xyz, 1.0);\n"
    204 			"    if (${RETURNCOND})\n"
    205 			"        return;\n"
    206 			"    ${OUTPUT} = vec4(${COORDS}.wzy, 1.0);\n"
    207 			"}\n\n"
    208 			"void main (void)\n"
    209 			"{\n"
    210 			"${POSITIONWRITE}"
    211 			"    myfunc();\n"
    212 			"}\n"
    213 		:
    214 			"${COORDATTRS} vec4 ${COORDS};\n"
    215 			"uniform mediump int ui_one;\n"
    216 			"${EXTRADECL}\n"
    217 			"void main ()\n"
    218 			"{\n"
    219 			"${POSITIONWRITE}"
    220 			"    ${OUTPUT} = vec4(${COORDS}.xyz, 1.0);\n"
    221 			"    if (${RETURNCOND})\n"
    222 			"        return;\n"
    223 			"    ${OUTPUT} = vec4(${COORDS}.wzy, 1.0);\n"
    224 			"}\n");
    225 
    226 	const char* coords = isVertex ? "a_coords" : "v_coords";
    227 
    228 	map<string, string> params;
    229 
    230 	params["COORDATTRS"]	= isVertex ? "attribute highp"	: "varying mediump";
    231 	params["COORDS"]		= coords;
    232 	params["OUTPUT"]		= isVertex ? "v_color"			: "gl_FragColor";
    233 	params["EXTRADECL"]		= isVertex ? "attribute highp vec4 a_position;\nvarying mediump vec4 v_color;\n" : "";
    234 	params["POSITIONWRITE"]	= isVertex ? "    gl_Position = a_position;\n" : "";
    235 
    236 	switch (returnMode)
    237 	{
    238 		case RETURNMODE_ALWAYS:		params["RETURNCOND"] = "true";											break;
    239 		case RETURNMODE_NEVER:		params["RETURNCOND"] = "false";											break;
    240 		case RETURNMODE_DYNAMIC:	params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0";	break;
    241 		default:					DE_ASSERT(DE_FALSE);
    242 	}
    243 
    244 	return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(), getEvalFunc(returnMode));
    245 }
    246 
    247 ShaderReturnCase* makeReturnInLoopCase (Context& context, const char* name, const char* description, bool isDynamicLoop, ReturnMode returnMode, bool isVertex)
    248 {
    249 	// Template
    250 	StringTemplate tmpl(
    251 		"${COORDSTORAGE} ${COORDPREC} vec4 ${COORDS};\n"
    252 		"uniform mediump int ui_one;\n"
    253 		"${EXTRADECL}\n"
    254 		"${COORDPREC} vec4 getCoords (void)\n"
    255 		"{\n"
    256 		"    ${COORDPREC} vec4 coords = ${COORDS};\n"
    257 		"    for (int i = 0; i < ${ITERLIMIT}; i++)\n"
    258 		"    {\n"
    259 		"        if (${RETURNCOND})\n"
    260 		"            return coords;\n"
    261 		"        coords = coords.wzyx;\n"
    262 		"    }\n"
    263 		"    return coords;\n"
    264 		"}\n\n"
    265 		"void main (void)\n"
    266 		"{\n"
    267 		"${POSITIONWRITE}"
    268 		"    ${OUTPUT} = vec4(getCoords().xyz, 1.0);\n"
    269 		"}\n");
    270 
    271 	const char* coords = isVertex ? "a_coords" : "v_coords";
    272 
    273 	map<string, string> params;
    274 
    275 	params["COORDSTORAGE"]	= isVertex ? "attribute"	: "varying";
    276 	params["COORDPREC"]		= isVertex ? "highp"		: "mediump";
    277 	params["OUTPUT"]		= isVertex ? "v_color"		: "gl_FragColor";
    278 	params["COORDS"]		= coords;
    279 	params["EXTRADECL"]		= isVertex ? "attribute highp vec4 a_position;\nvarying mediump vec4 v_color;\n" : "";
    280 	params["POSITIONWRITE"]	= isVertex ? "    gl_Position = a_position;\n" : "";
    281 	params["ITERLIMIT"]		= isDynamicLoop ? "ui_one" : "1";
    282 
    283 	switch (returnMode)
    284 	{
    285 		case RETURNMODE_ALWAYS:		params["RETURNCOND"] = "true";											break;
    286 		case RETURNMODE_NEVER:		params["RETURNCOND"] = "false";											break;
    287 		case RETURNMODE_DYNAMIC:	params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0";	break;
    288 		default:					DE_ASSERT(DE_FALSE);
    289 	}
    290 
    291 	return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(), getEvalFunc(returnMode), isDynamicLoop ? REQUIRE_DYNAMIC_LOOPS : 0);
    292 }
    293 
    294 static const char* getReturnModeName (ReturnMode mode)
    295 {
    296 	switch (mode)
    297 	{
    298 		case RETURNMODE_ALWAYS:		return "always";
    299 		case RETURNMODE_NEVER:		return "never";
    300 		case RETURNMODE_DYNAMIC:	return "dynamic";
    301 		default:
    302 			DE_ASSERT(DE_FALSE);
    303 			return DE_NULL;
    304 	}
    305 }
    306 
    307 static const char* getReturnModeDesc (ReturnMode mode)
    308 {
    309 	switch (mode)
    310 	{
    311 		case RETURNMODE_ALWAYS:		return "Always return";
    312 		case RETURNMODE_NEVER:		return "Never return";
    313 		case RETURNMODE_DYNAMIC:	return "Return based on coords";
    314 		default:
    315 			DE_ASSERT(DE_FALSE);
    316 			return DE_NULL;
    317 	}
    318 }
    319 
    320 void ShaderReturnTests::init (void)
    321 {
    322 	// Single return statement in function.
    323 	addChild(new ShaderReturnCase(m_context, "single_return_vertex", "Single return statement in function", true,
    324 		"attribute highp vec4 a_position;\n"
    325 		"attribute highp vec4 a_coords;\n"
    326 		"varying highp vec4 v_color;\n\n"
    327 		"vec4 getColor (void)\n"
    328 		"{\n"
    329 		"    return vec4(a_coords.xyz, 1.0);\n"
    330 		"}\n\n"
    331 		"void main (void)\n"
    332 		"{\n"
    333 		"    gl_Position = a_position;\n"
    334 		"    v_color = getColor();\n"
    335 		"}\n", evalReturnAlways));
    336 	addChild(new ShaderReturnCase(m_context, "single_return_fragment", "Single return statement in function", false,
    337 		"varying mediump vec4 v_coords;\n"
    338 		"mediump vec4 getColor (void)\n"
    339 		"{\n"
    340 		"    return vec4(v_coords.xyz, 1.0);\n"
    341 		"}\n\n"
    342 		"void main (void)\n"
    343 		"{\n"
    344 		"    gl_FragColor = getColor();\n"
    345 		"}\n", evalReturnAlways));
    346 
    347 	// Conditional return statement in function.
    348 	for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
    349 	{
    350 		for (int isFragment = 0; isFragment < 2; isFragment++)
    351 		{
    352 			string name			= string("conditional_return_") + getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex");
    353 			string description	= string(getReturnModeDesc((ReturnMode)returnMode)) + " in function";
    354 			addChild(makeConditionalReturnInFuncCase(m_context, name.c_str(), description.c_str(), (ReturnMode)returnMode, isFragment == 0));
    355 		}
    356 	}
    357 
    358 	// Unconditional double return in function.
    359 	addChild(new ShaderReturnCase(m_context, "double_return_vertex", "Unconditional double return in function", true,
    360 		"attribute highp vec4 a_position;\n"
    361 		"attribute highp vec4 a_coords;\n"
    362 		"varying highp vec4 v_color;\n\n"
    363 		"vec4 getColor (void)\n"
    364 		"{\n"
    365 		"    return vec4(a_coords.xyz, 1.0);\n"
    366 		"    return vec4(a_coords.wzy, 1.0);\n"
    367 		"}\n\n"
    368 		"void main (void)\n"
    369 		"{\n"
    370 		"    gl_Position = a_position;\n"
    371 		"    v_color = getColor();\n"
    372 		"}\n", evalReturnAlways));
    373 	addChild(new ShaderReturnCase(m_context, "double_return_fragment", "Unconditional double return in function", false,
    374 		"varying mediump vec4 v_coords;\n"
    375 		"mediump vec4 getColor (void)\n"
    376 		"{\n"
    377 		"    return vec4(v_coords.xyz, 1.0);\n"
    378 		"    return vec4(v_coords.wzy, 1.0);\n"
    379 		"}\n\n"
    380 		"void main (void)\n"
    381 		"{\n"
    382 		"    gl_FragColor = getColor();\n"
    383 		"}\n", evalReturnAlways));
    384 
    385 	// Last statement in main.
    386 	addChild(new ShaderReturnCase(m_context, "last_statement_in_main_vertex", "Return as a final statement in main()", true,
    387 		"attribute highp vec4 a_position;\n"
    388 		"attribute highp vec4 a_coords;\n"
    389 		"varying highp vec4 v_color;\n\n"
    390 		"void main (void)\n"
    391 		"{\n"
    392 		"    gl_Position = a_position;\n"
    393 		"    v_color = vec4(a_coords.xyz, 1.0);\n"
    394 		"    return;\n"
    395 		"}\n", evalReturnAlways));
    396 	addChild(new ShaderReturnCase(m_context, "last_statement_in_main_fragment", "Return as a final statement in main()", false,
    397 		"varying mediump vec4 v_coords;\n\n"
    398 		"void main (void)\n"
    399 		"{\n"
    400 		"    gl_FragColor = vec4(v_coords.xyz, 1.0);\n"
    401 		"    return;\n"
    402 		"}\n", evalReturnAlways));
    403 
    404 	// Return between output variable writes.
    405 	for (int inFunc = 0; inFunc < 2; inFunc++)
    406 	{
    407 		for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
    408 		{
    409 			for (int isFragment = 0; isFragment < 2; isFragment++)
    410 			{
    411 				string name = string("output_write_") + (inFunc ? "in_func_" : "") + getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex");
    412 				string desc = string(getReturnModeDesc((ReturnMode)returnMode)) + (inFunc ? " in user-defined function" : " in main()") + " between output writes";
    413 
    414 				addChild(makeOutputWriteReturnCase(m_context, name.c_str(), desc.c_str(), inFunc != 0, (ReturnMode)returnMode, isFragment == 0));
    415 			}
    416 		}
    417 	}
    418 
    419 	// Conditional return statement in loop.
    420 	for (int isDynamicLoop = 0; isDynamicLoop < 2; isDynamicLoop++)
    421 	{
    422 		for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
    423 		{
    424 			for (int isFragment = 0; isFragment < 2; isFragment++)
    425 			{
    426 				string name			= string("return_in_") + (isDynamicLoop ? "dynamic" : "static") + "_loop_" + getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex");
    427 				string description	= string(getReturnModeDesc((ReturnMode)returnMode)) + " in loop";
    428 				addChild(makeReturnInLoopCase(m_context, name.c_str(), description.c_str(), isDynamicLoop != 0, (ReturnMode)returnMode, isFragment == 0));
    429 			}
    430 		}
    431 	}
    432 
    433 	// Unconditional return in infinite loop.
    434 	addChild(new ShaderReturnCase(m_context, "return_in_infinite_loop_vertex", "Return in infinite loop", true,
    435 		"attribute highp vec4 a_position;\n"
    436 		"attribute highp vec4 a_coords;\n"
    437 		"varying highp vec4 v_color;\n"
    438 		"uniform int ui_zero;\n\n"
    439 		"highp vec4 getCoords (void)\n"
    440 		"{\n"
    441 		"	for (int i = 1; i < 10; i += ui_zero)\n"
    442 		"		return a_coords;\n"
    443 		"	return a_coords.wzyx;\n"
    444 		"}\n\n"
    445 		"void main (void)\n"
    446 		"{\n"
    447 		"    gl_Position = a_position;\n"
    448 		"    v_color = vec4(getCoords().xyz, 1.0);\n"
    449 		"    return;\n"
    450 		"}\n", evalReturnAlways, REQUIRE_DYNAMIC_LOOPS));
    451 	addChild(new ShaderReturnCase(m_context, "return_in_infinite_loop_fragment", "Return in infinite loop", false,
    452 		"varying mediump vec4 v_coords;\n"
    453 		"uniform int ui_zero;\n\n"
    454 		"mediump vec4 getCoords (void)\n"
    455 		"{\n"
    456 		"	for (int i = 1; i < 10; i += ui_zero)\n"
    457 		"		return v_coords;\n"
    458 		"	return v_coords.wzyx;\n"
    459 		"}\n\n"
    460 		"void main (void)\n"
    461 		"{\n"
    462 		"    gl_FragColor = vec4(getCoords().xyz, 1.0);\n"
    463 		"    return;\n"
    464 		"}\n", evalReturnAlways, REQUIRE_DYNAMIC_LOOPS));
    465 }
    466 
    467 } // Functional
    468 } // gles2
    469 } // deqp
    470