Home | History | Annotate | Download | only in performance
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program OpenGL ES 3.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 control statement performance tests.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "es3pShaderControlStatementTests.hpp"
     25 #include "glsShaderPerformanceCase.hpp"
     26 #include "tcuTestLog.hpp"
     27 
     28 #include "glwEnums.hpp"
     29 #include "glwFunctions.hpp"
     30 
     31 #include <string>
     32 #include <vector>
     33 
     34 namespace deqp
     35 {
     36 namespace gles3
     37 {
     38 namespace Performance
     39 {
     40 
     41 using namespace gls;
     42 using namespace glw; // GL types
     43 using tcu::Vec4;
     44 using tcu::TestLog;
     45 using std::string;
     46 using std::vector;
     47 
     48 // Writes the workload expression used in conditional tests.
     49 static void writeConditionalWorkload (std::ostringstream& stream, const char* resultName, const char* operandName)
     50 {
     51 	const int numMultiplications = 64;
     52 
     53 	stream << resultName << " = ";
     54 
     55 	for (int i = 0; i < numMultiplications; i++)
     56 	{
     57 		if (i > 0)
     58 			stream << "*";
     59 
     60 		stream << operandName;
     61 	}
     62 
     63 	stream << ";";
     64 }
     65 
     66 // Writes the workload expression used in loop tests (one iteration).
     67 static void writeLoopWorkload (std::ostringstream& stream, const char* resultName, const char* operandName)
     68 {
     69 	const int numMultiplications = 8;
     70 
     71 	stream << resultName << " = ";
     72 
     73 	for (int i = 0; i < numMultiplications; i++)
     74 	{
     75 		if (i > 0)
     76 			stream << " * ";
     77 
     78 		stream << "(" << resultName << " + " << operandName << ")";
     79 	}
     80 
     81 	stream << ";";
     82 }
     83 
     84 // The type of decision to be made in a conditional expression.
     85 // \note In fragment cases with DECISION_ATTRIBUTE, the value in the expression will actually be a varying.
     86 enum DecisionType
     87 {
     88 	DECISION_STATIC = 0,
     89 	DECISION_UNIFORM,
     90 	DECISION_ATTRIBUTE,
     91 
     92 	DECISION_LAST
     93 };
     94 
     95 class ControlStatementCase :  public ShaderPerformanceCase
     96 {
     97 public:
     98 	ControlStatementCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, gls::PerfCaseType caseType)
     99 		: ShaderPerformanceCase(testCtx, renderCtx, name, description, caseType)
    100 	{
    101 	}
    102 
    103 	void init (void)
    104 	{
    105 		m_testCtx.getLog() << TestLog::Message << "Using additive blending." << TestLog::EndMessage;
    106 		ShaderPerformanceCase::init();
    107 	}
    108 
    109 	void setupRenderState (void)
    110 	{
    111 		const glw::Functions& gl = m_renderCtx.getFunctions();
    112 
    113 		gl.enable(GL_BLEND);
    114 		gl.blendEquation(GL_FUNC_ADD);
    115 		gl.blendFunc(GL_ONE, GL_ONE);
    116 	}
    117 };
    118 
    119 class ConditionalCase : public ControlStatementCase
    120 {
    121 public:
    122 	enum BranchResult
    123 	{
    124 		BRANCH_TRUE = 0,
    125 		BRANCH_FALSE,
    126 		BRANCH_MIXED,
    127 
    128 		BRANCH_LAST
    129 	};
    130 
    131 	enum WorkloadDivision
    132 	{
    133 		WORKLOAD_DIVISION_EVEN = 0,		//! Both true and false branches contain same amount of computation.
    134 		WORKLOAD_DIVISION_TRUE_HEAVY,	//! True branch contains more computation.
    135 		WORKLOAD_DIVISION_FALSE_HEAVY,	//! False branch contains more computation.
    136 
    137 		WORKLOAD_DIVISION_LAST
    138 	};
    139 
    140 						ConditionalCase		(Context& context, const char* name, const char* description, DecisionType decisionType, BranchResult branchType, WorkloadDivision workloadDivision, bool isVertex);
    141 						~ConditionalCase	(void);
    142 
    143 	void				init				(void);
    144 	void				deinit				(void);
    145 	void				setupProgram		(deUint32 program);
    146 
    147 private:
    148 	DecisionType		m_decisionType;
    149 	BranchResult		m_branchType;
    150 	WorkloadDivision	m_workloadDivision;
    151 
    152 	vector<float>		m_comparisonValueArray; // Will contain per-vertex comparison values if using mixed branch type in vertex case.
    153 	deUint32			m_arrayBuffer;
    154 };
    155 
    156 ConditionalCase::ConditionalCase (Context& context, const char* name, const char* description, DecisionType decisionType, BranchResult branchType, WorkloadDivision workloadDivision, bool isVertex)
    157 	: ControlStatementCase			(context.getTestContext(), context.getRenderContext(), name, description, isVertex ? CASETYPE_VERTEX : CASETYPE_FRAGMENT)
    158 	, m_decisionType				(decisionType)
    159 	, m_branchType					(branchType)
    160 	, m_workloadDivision			(workloadDivision)
    161 	, m_arrayBuffer					(0)
    162 {
    163 }
    164 
    165 void ConditionalCase::init (void)
    166 {
    167 	bool			isVertexCase		= m_caseType == CASETYPE_VERTEX;
    168 
    169 	bool			isStaticCase		= m_decisionType == DECISION_STATIC;
    170 	bool			isUniformCase		= m_decisionType == DECISION_UNIFORM;
    171 	bool			isAttributeCase		= m_decisionType == DECISION_ATTRIBUTE;
    172 
    173 	DE_ASSERT(isStaticCase || isUniformCase || isAttributeCase);
    174 
    175 	bool			isConditionTrue		= m_branchType == BRANCH_TRUE;
    176 	bool			isConditionFalse	= m_branchType == BRANCH_FALSE;
    177 	bool			isConditionMixed	= m_branchType == BRANCH_MIXED;
    178 
    179 	DE_ASSERT(isConditionTrue || isConditionFalse || isConditionMixed);
    180 	DE_UNREF(isConditionFalse);
    181 
    182 	DE_ASSERT(isAttributeCase || !isConditionMixed); // The branch taken can vary between executions only if using attribute input.
    183 
    184 	const char*		staticCompareValueStr	= isConditionTrue	? "1.0" : "-1.0";
    185 	const char*		compareValueStr			= isStaticCase		? staticCompareValueStr :
    186 											  isUniformCase		? "u_compareValue" :
    187 											  isVertexCase		? "a_compareValue" :
    188 																  "v_compareValue";
    189 
    190 	std::ostringstream	vtx;
    191 	std::ostringstream	frag;
    192 	std::ostringstream&	op		= isVertexCase ? vtx : frag;
    193 
    194 	vtx << "#version 300 es\n";
    195 	vtx << "in highp vec4 a_position;\n";	// Position attribute.
    196 	vtx << "in mediump vec4 a_value0;\n";	// Input for workload calculations of "true" branch.
    197 	vtx << "in mediump vec4 a_value1;\n";	// Input for workload calculations of "false" branch.
    198 
    199 	frag << "#version 300 es\n";
    200 	frag << "layout(location = 0) out mediump vec4 o_color;\n";
    201 
    202 	// Value to be used in the conditional expression.
    203 	if (isAttributeCase)
    204 		vtx << "in mediump float a_compareValue;\n";
    205 	else if (isUniformCase)
    206 		op << "uniform mediump float u_compareValue;\n";
    207 
    208 	// Varyings.
    209 	if (isVertexCase)
    210 	{
    211 		vtx << "out mediump vec4 v_color;\n";
    212 		frag << "in mediump vec4 v_color;\n";
    213 	}
    214 	else
    215 	{
    216 		vtx << "out mediump vec4 v_value0;\n";
    217 		vtx << "out mediump vec4 v_value1;\n";
    218 		frag << "in mediump vec4 v_value0;\n";
    219 		frag << "in mediump vec4 v_value1;\n";
    220 
    221 		if (isAttributeCase)
    222 		{
    223 			vtx << "out mediump float v_compareValue;\n";
    224 			frag << "in mediump float v_compareValue;\n";
    225 		}
    226 	}
    227 
    228 	vtx << "\n";
    229 	vtx << "void main()\n";
    230 	vtx << "{\n";
    231 	vtx << "	gl_Position = a_position;\n";
    232 
    233 	frag << "\n";
    234 	frag << "void main()\n";
    235 	frag << "{\n";
    236 
    237 	op << "	mediump vec4 res;\n";
    238 
    239 	string condition;
    240 
    241 	if (isConditionMixed && !isVertexCase)
    242 		condition = string("") + "fract(" + compareValueStr + ") < 0.5"; // Comparison result varies with high frequency.
    243 	else
    244 		condition = string("") + compareValueStr + " > 0.0";
    245 
    246 	op << "	if (" << condition << ")\n";
    247 	op << "	{\n";
    248 
    249 	op << "\t\t";
    250 	if (m_workloadDivision == WORKLOAD_DIVISION_EVEN || m_workloadDivision == WORKLOAD_DIVISION_TRUE_HEAVY)
    251 		writeConditionalWorkload(op, "res", isVertexCase ? "a_value0" : "v_value0"); // Workload calculation for the "true" branch.
    252 	else
    253 		op << "res = " << (isVertexCase ? "a_value0" : "v_value0") << ";";
    254 	op << "\n";
    255 
    256 	op << "	}\n";
    257 	op << "	else\n";
    258 	op << "	{\n";
    259 
    260 	op << "\t\t";
    261 	if (m_workloadDivision == WORKLOAD_DIVISION_EVEN || m_workloadDivision == WORKLOAD_DIVISION_FALSE_HEAVY)
    262 		writeConditionalWorkload(op, "res", isVertexCase ? "a_value1" : "v_value1"); // Workload calculations for the "false" branch.
    263 	else
    264 		op << "res = " << (isVertexCase ? "a_value1" : "v_value1") << ";";
    265 	op << "\n";
    266 
    267 	op << "	}\n";
    268 
    269 	if (isVertexCase)
    270 	{
    271 		// Put result to color variable.
    272 		vtx << "	v_color = res;\n";
    273 		frag << "	o_color = v_color;\n";
    274 	}
    275 	else
    276 	{
    277 		// Transfer inputs to fragment shader through varyings.
    278 		if (isAttributeCase)
    279 			vtx << "	v_compareValue = a_compareValue;\n";
    280 		vtx << "	v_value0 = a_value0;\n";
    281 		vtx << "	v_value1 = a_value1;\n";
    282 
    283 		frag << "	o_color = res;\n"; // Put result to color variable.
    284 	}
    285 
    286 	vtx << "}\n";
    287 	frag << "}\n";
    288 
    289 	m_vertShaderSource = vtx.str();
    290 	m_fragShaderSource = frag.str();
    291 
    292 	if (isAttributeCase)
    293 	{
    294 		if (!isConditionMixed)
    295 		{
    296 			// Every execution takes the same branch.
    297 
    298 			float value = isConditionTrue ? +1.0f : -1.0f;
    299 			m_attributes.push_back(AttribSpec("a_compareValue",	Vec4(value, 0.0f, 0.0f, 0.0f),
    300 																Vec4(value, 0.0f, 0.0f, 0.0f),
    301 																Vec4(value, 0.0f, 0.0f, 0.0f),
    302 																Vec4(value, 0.0f, 0.0f, 0.0f)));
    303 		}
    304 		else if (isVertexCase)
    305 		{
    306 			// Vertex case, not every execution takes the same branch.
    307 
    308 			const int	numComponents	= 4;
    309 			int			numVertices		= (getGridWidth() + 1) * (getGridHeight() + 1);
    310 
    311 			// setupProgram() will later bind this array as an attribute.
    312 			m_comparisonValueArray.resize(numVertices * numComponents);
    313 
    314 			// Make every second vertex take the true branch, and every second the false branch.
    315 			for (int i = 0; i < (int)m_comparisonValueArray.size(); i++)
    316 			{
    317 				if (i % numComponents == 0)
    318 					m_comparisonValueArray[i] = (i / numComponents) % 2 == 0 ? +1.0f : -1.0f;
    319 				else
    320 					m_comparisonValueArray[i] = 0.0f;
    321 			}
    322 		}
    323 		else // isConditionMixed && !isVertexCase
    324 		{
    325 			// Fragment case, not every execution takes the same branch.
    326 			// \note fract(a_compareValue) < 0.5 will be true for every second column of fragments.
    327 
    328 			float minValue = 0.0f;
    329 			float maxValue = (float)getViewportWidth()*0.5f;
    330 			m_attributes.push_back(AttribSpec("a_compareValue",	Vec4(minValue, 0.0f, 0.0f, 0.0f),
    331 																Vec4(maxValue, 0.0f, 0.0f, 0.0f),
    332 																Vec4(minValue, 0.0f, 0.0f, 0.0f),
    333 																Vec4(maxValue, 0.0f, 0.0f, 0.0f)));
    334 		}
    335 	}
    336 
    337 	m_attributes.push_back(AttribSpec("a_value0",	Vec4(0.0f, 0.1f, 0.2f, 0.3f),
    338 													Vec4(0.4f, 0.5f, 0.6f, 0.7f),
    339 													Vec4(0.8f, 0.9f, 1.0f, 1.1f),
    340 													Vec4(1.2f, 1.3f, 1.4f, 1.5f)));
    341 
    342 	m_attributes.push_back(AttribSpec("a_value1",	Vec4(0.0f, 0.1f, 0.2f, 0.3f),
    343 													Vec4(0.4f, 0.5f, 0.6f, 0.7f),
    344 													Vec4(0.8f, 0.9f, 1.0f, 1.1f),
    345 													Vec4(1.2f, 1.3f, 1.4f, 1.5f)));
    346 
    347 	ControlStatementCase::init();
    348 }
    349 
    350 void ConditionalCase::setupProgram (deUint32 program)
    351 {
    352 	const glw::Functions& gl = m_renderCtx.getFunctions();
    353 
    354 	if (m_decisionType == DECISION_UNIFORM)
    355 	{
    356 		int location = gl.getUniformLocation(program, "u_compareValue");
    357 		gl.uniform1f(location, m_branchType == BRANCH_TRUE ? +1.0f : -1.0f);
    358 	}
    359 	else if (m_decisionType == DECISION_ATTRIBUTE && m_branchType == BRANCH_MIXED && m_caseType == CASETYPE_VERTEX)
    360 	{
    361 		// Setup per-vertex comparison values calculated in init().
    362 
    363 		const int	numComponents			= 4;
    364 		int			compareAttribLocation	= gl.getAttribLocation(program, "a_compareValue");
    365 
    366 		DE_ASSERT((int)m_comparisonValueArray.size() == numComponents * (getGridWidth() + 1) * (getGridHeight() + 1));
    367 
    368 		gl.genBuffers(1, &m_arrayBuffer);
    369 		gl.bindBuffer(GL_ARRAY_BUFFER, m_arrayBuffer);
    370 		gl.bufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(m_comparisonValueArray.size()*sizeof(float)), &m_comparisonValueArray[0], GL_STATIC_DRAW);
    371 		gl.enableVertexAttribArray(compareAttribLocation);
    372 		gl.vertexAttribPointer(compareAttribLocation, (GLint)numComponents, GL_FLOAT, GL_FALSE, 0, DE_NULL);
    373 	}
    374 
    375 	GLU_EXPECT_NO_ERROR(gl.getError(), "Setup program state");
    376 }
    377 
    378 ConditionalCase::~ConditionalCase (void)
    379 {
    380 	const glw::Functions& gl = m_renderCtx.getFunctions();
    381 
    382 	if (m_arrayBuffer != 0)
    383 	{
    384 		gl.deleteBuffers(1, &m_arrayBuffer);
    385 		m_arrayBuffer = 0;
    386 	}
    387 }
    388 
    389 void ConditionalCase::deinit (void)
    390 {
    391 	const glw::Functions& gl = m_renderCtx.getFunctions();
    392 
    393 	m_comparisonValueArray.clear();
    394 
    395 	if (m_arrayBuffer != 0)
    396 	{
    397 		gl.deleteBuffers(1, &m_arrayBuffer);
    398 		m_arrayBuffer = 0;
    399 	}
    400 
    401 	ShaderPerformanceCase::deinit();
    402 }
    403 
    404 class LoopCase : public ControlStatementCase
    405 {
    406 public:
    407 	enum LoopType
    408 	{
    409 		LOOP_FOR = 0,
    410 		LOOP_WHILE,
    411 		LOOP_DO_WHILE,
    412 
    413 		LOOP_LAST
    414 	};
    415 					LoopCase			(Context& context, const char* name, const char* description, LoopType type, DecisionType decisionType, bool isLoopBoundStable, bool isVertex);
    416 					~LoopCase			(void);
    417 
    418 	void			init				(void);
    419 	void			deinit				(void);
    420 	void			setupProgram		(deUint32 program);
    421 
    422 private:
    423 	DecisionType	m_decisionType;
    424 	LoopType		m_type;
    425 
    426 	bool			m_isLoopBoundStable;	// Whether loop bound is same in all executions.
    427 	vector<float>	m_boundArray;			// Will contain per-vertex loop bounds if using non-stable attribute in vertex case.
    428 	deUint32		m_arrayBuffer;
    429 };
    430 
    431 LoopCase::LoopCase (Context& context, const char* name, const char* description, LoopType type, DecisionType decisionType, bool isLoopBoundStable, bool isVertex)
    432 	: ControlStatementCase	(context.getTestContext(), context.getRenderContext(), name, description, isVertex ? CASETYPE_VERTEX : CASETYPE_FRAGMENT)
    433 	, m_decisionType		(decisionType)
    434 	, m_type				(type)
    435 	, m_isLoopBoundStable	(isLoopBoundStable)
    436 	, m_arrayBuffer			(0)
    437 {
    438 }
    439 
    440 void LoopCase::init (void)
    441 {
    442 	bool				isVertexCase	= m_caseType == CASETYPE_VERTEX;
    443 
    444 	bool				isStaticCase	= m_decisionType == DECISION_STATIC;
    445 	bool				isUniformCase	= m_decisionType == DECISION_UNIFORM;
    446 	bool				isAttributeCase	= m_decisionType == DECISION_ATTRIBUTE;
    447 
    448 	DE_ASSERT(isStaticCase || isUniformCase || isAttributeCase);
    449 
    450 	DE_ASSERT(m_type == LOOP_FOR		||
    451 			  m_type == LOOP_WHILE		||
    452 			  m_type == LOOP_DO_WHILE);
    453 
    454 	DE_ASSERT(isAttributeCase || m_isLoopBoundStable); // The loop bound count can vary between executions only if using attribute input.
    455 
    456 	// \note The fractional part is .5 (instead of .0) so that these can be safely used as loop bounds.
    457 	const float			loopBound				= 10.5f;
    458 	const float			unstableBoundLow		= 5.5f;
    459 	const float			unstableBoundHigh		= 15.5f;
    460 	static const char*	loopBoundStr			= "10.5";
    461 	static const char*	unstableBoundLowStr		= "5.5";
    462 	static const char*	unstableBoundHighStr	= "15.5";
    463 
    464 	const char*			boundValueStr		= isStaticCase			? loopBoundStr :
    465 											  isUniformCase			? "u_bound" :
    466 											  isVertexCase			? "a_bound" :
    467 											  m_isLoopBoundStable	? "v_bound" :
    468 																	  "loopBound";
    469 
    470 	std::ostringstream	vtx;
    471 	std::ostringstream	frag;
    472 	std::ostringstream&	op		= isVertexCase ? vtx : frag;
    473 
    474 	vtx << "#version 300 es\n";
    475 	vtx << "in highp vec4 a_position;\n";	// Position attribute.
    476 	vtx << "in mediump vec4 a_value;\n";	// Input for workload calculations.
    477 
    478 	frag << "#version 300 es\n";
    479 	frag << "layout(location = 0) out mediump vec4 o_color;\n";
    480 
    481 	// Value to be used as the loop iteration count.
    482 	if (isAttributeCase)
    483 		vtx << "in mediump float a_bound;\n";
    484 	else if (isUniformCase)
    485 		op << "uniform mediump float u_bound;\n";
    486 
    487 	// Varyings.
    488 	if (isVertexCase)
    489 	{
    490 		vtx << "out mediump vec4 v_color;\n";
    491 		frag << "in mediump vec4 v_color;\n";
    492 	}
    493 	else
    494 	{
    495 		vtx << "out mediump vec4 v_value;\n";
    496 		frag << "in mediump vec4 v_value;\n";
    497 
    498 		if (isAttributeCase)
    499 		{
    500 			vtx << "out mediump float v_bound;\n";
    501 			frag << "in mediump float v_bound;\n";
    502 		}
    503 	}
    504 
    505 	vtx << "\n";
    506 	vtx << "void main()\n";
    507 	vtx << "{\n";
    508 	vtx << "	gl_Position = a_position;\n";
    509 
    510 	frag << "\n";
    511 	frag << "void main()\n";
    512 	frag << "{\n";
    513 
    514 	op << "	mediump vec4 res = vec4(0.0);\n";
    515 
    516 	if (!m_isLoopBoundStable && !isVertexCase)
    517 	{
    518 		// Choose the actual loop bound based on v_bound.
    519 		// \note Loop bound will vary with high frequency between fragment columns, given appropriate range for v_bound.
    520 		op << "	mediump float loopBound = fract(v_bound) < 0.5 ? " << unstableBoundLowStr << " : " << unstableBoundHighStr << ";\n";
    521 	}
    522 
    523 	// Start a for, while or do-while loop.
    524 	if (m_type == LOOP_FOR)
    525 		op << "	for (mediump float i = 0.0; i < " << boundValueStr << "; i++)\n";
    526 	else
    527 	{
    528 		op << "	mediump float i = 0.0;\n";
    529 		if (m_type == LOOP_WHILE)
    530 			op << "	while (i < " << boundValueStr << ")\n";
    531 		else // LOOP_DO_WHILE
    532 			op << "	do\n";
    533 	}
    534 	op << "	{\n";
    535 
    536 	// Workload calculations inside the loop.
    537 	op << "\t\t";
    538 	writeLoopWorkload(op, "res", isVertexCase ? "a_value" : "v_value");
    539 	op << "\n";
    540 
    541 	// Only "for" has counter increment in the loop head.
    542 	if (m_type != LOOP_FOR)
    543 		op << "		i++;\n";
    544 
    545 	// End the loop.
    546 	if (m_type == LOOP_DO_WHILE)
    547 		op << "	} while (i < " << boundValueStr << ");\n";
    548 	else
    549 		op << "	}\n";
    550 
    551 	if (isVertexCase)
    552 	{
    553 		// Put result to color variable.
    554 		vtx << "	v_color = res;\n";
    555 		frag << "	o_color = v_color;\n";
    556 	}
    557 	else
    558 	{
    559 		// Transfer inputs to fragment shader through varyings.
    560 		if (isAttributeCase)
    561 			vtx << "	v_bound = a_bound;\n";
    562 		vtx << "	v_value = a_value;\n";
    563 
    564 		frag << "	o_color = res;\n"; // Put result to color variable.
    565 	}
    566 
    567 	vtx << "}\n";
    568 	frag << "}\n";
    569 
    570 	m_vertShaderSource = vtx.str();
    571 	m_fragShaderSource = frag.str();
    572 
    573 	if (isAttributeCase)
    574 	{
    575 		if (m_isLoopBoundStable)
    576 		{
    577 			// Every execution has same number of iterations.
    578 
    579 			m_attributes.push_back(AttribSpec("a_bound",	Vec4(loopBound, 0.0f, 0.0f, 0.0f),
    580 															Vec4(loopBound, 0.0f, 0.0f, 0.0f),
    581 															Vec4(loopBound, 0.0f, 0.0f, 0.0f),
    582 															Vec4(loopBound, 0.0f, 0.0f, 0.0f)));
    583 		}
    584 		else if (isVertexCase)
    585 		{
    586 			// Vertex case, with non-constant number of iterations.
    587 
    588 			const int	numComponents	= 4;
    589 			int			numVertices		= (getGridWidth() + 1) * (getGridHeight() + 1);
    590 
    591 			// setupProgram() will later bind this array as an attribute.
    592 			m_boundArray.resize(numVertices * numComponents);
    593 
    594 			// Vary between low and high loop bounds; they should average to loopBound however.
    595 			for (int i = 0; i < (int)m_boundArray.size(); i++)
    596 			{
    597 				if (i % numComponents == 0)
    598 					m_boundArray[i] = (i / numComponents) % 2 == 0 ? unstableBoundLow : unstableBoundHigh;
    599 				else
    600 					m_boundArray[i] = 0.0f;
    601 			}
    602 		}
    603 		else // !m_isLoopBoundStable && !isVertexCase
    604 		{
    605 			// Fragment case, with non-constant number of iterations.
    606 			// \note fract(a_bound) < 0.5 will be true for every second fragment.
    607 
    608 			float minValue = 0.0f;
    609 			float maxValue = (float)getViewportWidth()*0.5f;
    610 			m_attributes.push_back(AttribSpec("a_bound",	Vec4(minValue, 0.0f, 0.0f, 0.0f),
    611 															Vec4(maxValue, 0.0f, 0.0f, 0.0f),
    612 															Vec4(minValue, 0.0f, 0.0f, 0.0f),
    613 															Vec4(maxValue, 0.0f, 0.0f, 0.0f)));
    614 		}
    615 	}
    616 
    617 	m_attributes.push_back(AttribSpec("a_value",	Vec4(0.0f, 0.1f, 0.2f, 0.3f),
    618 													Vec4(0.4f, 0.5f, 0.6f, 0.7f),
    619 													Vec4(0.8f, 0.9f, 1.0f, 1.1f),
    620 													Vec4(1.2f, 1.3f, 1.4f, 1.5f)));
    621 
    622 	ControlStatementCase::init();
    623 }
    624 
    625 void LoopCase::setupProgram (deUint32 program)
    626 {
    627 	const glw::Functions& gl = m_renderCtx.getFunctions();
    628 
    629 	if (m_decisionType == DECISION_UNIFORM)
    630 	{
    631 		const float loopBound = 10.5f;
    632 
    633 		int location = gl.getUniformLocation(program, "u_bound");
    634 		gl.uniform1f(location, loopBound);
    635 	}
    636 	else if (m_decisionType == DECISION_ATTRIBUTE && !m_isLoopBoundStable && m_caseType == CASETYPE_VERTEX)
    637 	{
    638 		// Setup per-vertex loop bounds calculated in init().
    639 
    640 		const int	numComponents		= 4;
    641 		int			boundAttribLocation	= gl.getAttribLocation(program, "a_bound");
    642 
    643 		DE_ASSERT((int)m_boundArray.size() == numComponents * (getGridWidth() + 1) * (getGridHeight() + 1));
    644 
    645 		gl.genBuffers(1, &m_arrayBuffer);
    646 		gl.bindBuffer(GL_ARRAY_BUFFER, m_arrayBuffer);
    647 		gl.bufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(m_boundArray.size()*sizeof(float)), &m_boundArray[0], GL_STATIC_DRAW);
    648 		gl.enableVertexAttribArray(boundAttribLocation);
    649 		gl.vertexAttribPointer(boundAttribLocation, (GLint)numComponents, GL_FLOAT, GL_FALSE, 0, DE_NULL);
    650 	}
    651 
    652 	GLU_EXPECT_NO_ERROR(gl.getError(), "Setup program state");
    653 }
    654 
    655 LoopCase::~LoopCase (void)
    656 {
    657 	const glw::Functions& gl = m_renderCtx.getFunctions();
    658 
    659 	if (m_arrayBuffer)
    660 	{
    661 		gl.deleteBuffers(1, &m_arrayBuffer);
    662 		m_arrayBuffer = 0;
    663 	}
    664 }
    665 
    666 void LoopCase::deinit (void)
    667 {
    668 	const glw::Functions& gl = m_renderCtx.getFunctions();
    669 
    670 	m_boundArray.clear();
    671 
    672 	if (m_arrayBuffer)
    673 	{
    674 		gl.deleteBuffers(1, &m_arrayBuffer);
    675 		m_arrayBuffer = 0;
    676 	}
    677 
    678 	ShaderPerformanceCase::deinit();
    679 }
    680 
    681 // A reference case, same calculations as the actual tests but without control statements.
    682 class WorkloadReferenceCase : public ControlStatementCase
    683 {
    684 public:
    685 							WorkloadReferenceCase		(Context& context, const char* name, const char* description, bool isVertex);
    686 
    687 	void					init						(void);
    688 
    689 protected:
    690 	virtual void			writeWorkload				(std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const = 0;
    691 };
    692 
    693 WorkloadReferenceCase::WorkloadReferenceCase (Context& context, const char* name, const char* description, bool isVertex)
    694 	: ControlStatementCase(context.getTestContext(), context.getRenderContext(), name, description, isVertex ? CASETYPE_VERTEX : CASETYPE_FRAGMENT)
    695 {
    696 }
    697 
    698 void WorkloadReferenceCase::init (void)
    699 {
    700 	bool isVertexCase = m_caseType == CASETYPE_VERTEX;
    701 
    702 	std::ostringstream	vtx;
    703 	std::ostringstream	frag;
    704 	std::ostringstream&	op			= isVertexCase ? vtx : frag;
    705 
    706 	vtx << "#version 300 es\n";
    707 	vtx << "in highp vec4 a_position;\n";	// Position attribute.
    708 	vtx << "in mediump vec4 a_value;\n";	// Value for workload calculations.
    709 
    710 	frag << "#version 300 es\n";
    711 	frag << "layout(location = 0) out mediump vec4 o_color;\n";
    712 
    713 	// Varyings.
    714 	if (isVertexCase)
    715 	{
    716 		vtx << "out mediump vec4 v_color;\n";
    717 		frag << "in mediump vec4 v_color;\n";
    718 	}
    719 	else
    720 	{
    721 		vtx << "out mediump vec4 v_value;\n";
    722 		frag << "in mediump vec4 v_value;\n";
    723 	}
    724 
    725 	vtx << "\n";
    726 	vtx << "void main()\n";
    727 	vtx << "{\n";
    728 	vtx << "	gl_Position = a_position;\n";
    729 
    730 	frag << "\n";
    731 	frag << "void main()\n";
    732 	frag << "{\n";
    733 
    734 	op << "\tmediump vec4 res;\n";
    735 	writeWorkload(op, "res", isVertexCase ? "a_value" : "v_value");
    736 
    737 	if (isVertexCase)
    738 	{
    739 		// Put result to color variable.
    740 		vtx << "	v_color = res;\n";
    741 		frag << "	o_color = v_color;\n";
    742 	}
    743 	else
    744 	{
    745 		vtx << "	v_value = a_value;\n";	// Transfer input to fragment shader through varying.
    746 		frag << "	o_color = res;\n";	// Put result to color variable.
    747 	}
    748 
    749 	vtx << "}\n";
    750 	frag << "}\n";
    751 
    752 	m_vertShaderSource = vtx.str();
    753 	m_fragShaderSource = frag.str();
    754 
    755 	m_attributes.push_back(AttribSpec("a_value",	Vec4(0.0f, 0.1f, 0.2f, 0.3f),
    756 													Vec4(0.4f, 0.5f, 0.6f, 0.7f),
    757 													Vec4(0.8f, 0.9f, 1.0f, 1.1f),
    758 													Vec4(1.2f, 1.3f, 1.4f, 1.5f)));
    759 
    760 	ControlStatementCase::init();
    761 }
    762 
    763 class LoopWorkloadReferenceCase : public WorkloadReferenceCase
    764 {
    765 public:
    766 	LoopWorkloadReferenceCase (Context& context, const char* name, const char* description, bool isAttributeStable, bool isVertex)
    767 		: WorkloadReferenceCase		(context, name, description, isVertex)
    768 		, m_isAttributeStable		(isAttributeStable)
    769 	{
    770 	}
    771 
    772 protected:
    773 	void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const;
    774 
    775 private:
    776 	bool m_isAttributeStable;
    777 };
    778 
    779 void LoopWorkloadReferenceCase::writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const
    780 {
    781 	const int	loopIterations	= 11;
    782 	bool		isVertexCase	= m_caseType == CASETYPE_VERTEX;
    783 
    784 	dst << "\t" << resultVariableName << " = vec4(0.0);\n";
    785 
    786 	for (int i = 0; i < loopIterations; i++)
    787 	{
    788 		dst << "\t";
    789 		writeLoopWorkload(dst, resultVariableName, inputVariableName);
    790 		dst << "\n";
    791 	}
    792 
    793 	if (!isVertexCase && !m_isAttributeStable)
    794 	{
    795 		// Corresponds to the fract() done in a real test's fragment case with non-stable attribute.
    796 		dst << "	res.x = fract(res.x);\n";
    797 	}
    798 }
    799 
    800 class ConditionalWorkloadReferenceCase : public WorkloadReferenceCase
    801 {
    802 public:
    803 	ConditionalWorkloadReferenceCase (Context& context, const char* name, const char* description, bool isAttributeStable, bool isVertex)
    804 		: WorkloadReferenceCase		(context, name, description, isVertex)
    805 		, m_isAttributeStable		(isAttributeStable)
    806 	{
    807 	}
    808 
    809 protected:
    810 	void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const;
    811 
    812 private:
    813 	bool m_isAttributeStable;
    814 };
    815 
    816 void ConditionalWorkloadReferenceCase::writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const
    817 {
    818 	bool isVertexCase = m_caseType == CASETYPE_VERTEX;
    819 
    820 	dst << "\t";
    821 	writeConditionalWorkload(dst, resultVariableName, inputVariableName);
    822 	dst << "\n";
    823 
    824 	if (!isVertexCase && !m_isAttributeStable)
    825 	{
    826 		// Corresponds to the fract() done in a real test's fragment case with non-stable attribute.
    827 		dst << "	res.x = fract(res.x);\n";
    828 	}
    829 }
    830 
    831 // A workload reference case for e.g. a conditional case with a branch with no computation.
    832 class EmptyWorkloadReferenceCase : public WorkloadReferenceCase
    833 {
    834 public:
    835 	EmptyWorkloadReferenceCase	(Context& context, const char* name, const char* description, bool isVertex)
    836 		: WorkloadReferenceCase (context, name, description, isVertex)
    837 	{
    838 	}
    839 
    840 protected:
    841 	void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const
    842 	{
    843 		dst << "\t" << resultVariableName << " = " << inputVariableName << ";\n";
    844 	}
    845 };
    846 
    847 ShaderControlStatementTests::ShaderControlStatementTests (Context& context)
    848 	: TestCaseGroup(context, "control_statement", "Control Statement Performance Tests")
    849 {
    850 }
    851 
    852 ShaderControlStatementTests::~ShaderControlStatementTests (void)
    853 {
    854 }
    855 
    856 void ShaderControlStatementTests::init (void)
    857 {
    858 	// Conditional cases (if-else).
    859 
    860 	tcu::TestCaseGroup* ifElseGroup = new tcu::TestCaseGroup(m_testCtx, "if_else", "if-else Conditional Performance Tests");
    861 	addChild(ifElseGroup);
    862 
    863 	for (int isFrag = 0; isFrag <= 1; isFrag++)
    864 	{
    865 		bool isVertex = isFrag == 0;
    866 		ShaderPerformanceCaseGroup* vertexOrFragmentGroup = new ShaderPerformanceCaseGroup(m_testCtx, isVertex ? "vertex" : "fragment", "");
    867 		ifElseGroup->addChild(vertexOrFragmentGroup);
    868 
    869 		DE_STATIC_ASSERT(DECISION_STATIC == 0);
    870 		for (int decisionType = (int)DECISION_STATIC; decisionType < (int)DECISION_LAST; decisionType++)
    871 		{
    872 			const char* decisionName = decisionType == (int)DECISION_STATIC		? "static" :
    873 										decisionType == (int)DECISION_UNIFORM	? "uniform" :
    874 										decisionType == (int)DECISION_ATTRIBUTE	? (isVertex ? "attribute" : "varying") :
    875 																					DE_NULL;
    876 			DE_ASSERT(decisionName != DE_NULL);
    877 
    878 			for (int workloadDivision = 0; workloadDivision < ConditionalCase::WORKLOAD_DIVISION_LAST; workloadDivision++)
    879 			{
    880 				const char* workloadDivisionSuffix = workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_EVEN			? "" :
    881 													 workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_TRUE_HEAVY		? "_with_heavier_true" :
    882 													 workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_FALSE_HEAVY	? "_with_heavier_false" :
    883 																																  DE_NULL;
    884 				DE_ASSERT(workloadDivisionSuffix != DE_NULL);
    885 
    886 				DE_STATIC_ASSERT(ConditionalCase::BRANCH_TRUE == 0);
    887 				for (int branchResult = (int)ConditionalCase::BRANCH_TRUE; branchResult < (int)ConditionalCase::BRANCH_LAST; branchResult++)
    888 				{
    889 					if (decisionType != (int)DECISION_ATTRIBUTE && branchResult == (int)ConditionalCase::BRANCH_MIXED)
    890 						continue;
    891 
    892 					const char* branchResultName = branchResult == (int)ConditionalCase::BRANCH_TRUE	? "true" :
    893 												   branchResult == (int)ConditionalCase::BRANCH_FALSE	? "false" :
    894 												   branchResult == (int)ConditionalCase::BRANCH_MIXED	? "mixed" :
    895 																										  DE_NULL;
    896 					DE_ASSERT(branchResultName != DE_NULL);
    897 
    898 					string caseName = string("") + decisionName + "_" + branchResultName + workloadDivisionSuffix;
    899 
    900 					vertexOrFragmentGroup->addChild(new ConditionalCase(m_context, caseName.c_str(), "",
    901 																		(DecisionType)decisionType, (ConditionalCase::BranchResult)branchResult,
    902 																		(ConditionalCase::WorkloadDivision)workloadDivision, isVertex));
    903 				}
    904 			}
    905 		}
    906 
    907 		if (isVertex)
    908 			vertexOrFragmentGroup->addChild(new ConditionalWorkloadReferenceCase(m_context, "reference", "", true, isVertex));
    909 		else
    910 		{
    911 			// Only fragment case with BRANCH_MIXED has an additional fract() call.
    912 			vertexOrFragmentGroup->addChild(new ConditionalWorkloadReferenceCase(m_context, "reference_unmixed", "", true, isVertex));
    913 			vertexOrFragmentGroup->addChild(new ConditionalWorkloadReferenceCase(m_context, "reference_mixed", "", false, isVertex));
    914 		}
    915 
    916 		vertexOrFragmentGroup->addChild(new EmptyWorkloadReferenceCase(m_context, "reference_empty", "", isVertex));
    917 	}
    918 
    919 	// Loop cases.
    920 
    921 	static const struct
    922 	{
    923 		LoopCase::LoopType	type;
    924 		const char*			name;
    925 		const char*			description;
    926 	} loopGroups[] =
    927 	{
    928 		{LoopCase::LOOP_FOR,		"for",		"for Loop Performance Tests"},
    929 		{LoopCase::LOOP_WHILE,		"while",	"while Loop Performance Tests"},
    930 		{LoopCase::LOOP_DO_WHILE,	"do_while",	"do-while Loop Performance Tests"}
    931 	};
    932 
    933 	for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(loopGroups); groupNdx++)
    934 	{
    935 		tcu::TestCaseGroup* currentLoopGroup = new tcu::TestCaseGroup(m_testCtx, loopGroups[groupNdx].name, loopGroups[groupNdx].description);
    936 		addChild(currentLoopGroup);
    937 
    938 		for (int isFrag = 0; isFrag <= 1; isFrag++)
    939 		{
    940 			bool isVertex = isFrag == 0;
    941 			ShaderPerformanceCaseGroup* vertexOrFragmentGroup = new ShaderPerformanceCaseGroup(m_testCtx, isVertex ? "vertex" : "fragment", "");
    942 			currentLoopGroup->addChild(vertexOrFragmentGroup);
    943 
    944 			DE_STATIC_ASSERT(DECISION_STATIC == 0);
    945 			for (int decisionType = (int)DECISION_STATIC; decisionType < (int)DECISION_LAST; decisionType++)
    946 			{
    947 				const char* decisionName = decisionType == (int)DECISION_STATIC		? "static" :
    948 										   decisionType == (int)DECISION_UNIFORM	? "uniform" :
    949 										   decisionType == (int)DECISION_ATTRIBUTE	? (isVertex ? "attribute" : "varying") :
    950 																					  DE_NULL;
    951 				DE_ASSERT(decisionName != DE_NULL);
    952 
    953 				if (decisionType == (int)DECISION_ATTRIBUTE)
    954 				{
    955 					vertexOrFragmentGroup->addChild(new LoopCase(m_context, (string(decisionName) + "_stable").c_str(), "", loopGroups[groupNdx].type, (DecisionType)decisionType, true, isVertex));
    956 					vertexOrFragmentGroup->addChild(new LoopCase(m_context, (string(decisionName) + "_unstable").c_str(), "", loopGroups[groupNdx].type, (DecisionType)decisionType, false, isVertex));
    957 				}
    958 				else
    959 					vertexOrFragmentGroup->addChild(new LoopCase(m_context, decisionName, "", loopGroups[groupNdx].type, (DecisionType)decisionType, true, isVertex));
    960 
    961 			}
    962 
    963 			if (isVertex)
    964 				vertexOrFragmentGroup->addChild(new LoopWorkloadReferenceCase(m_context, "reference", "", true, isVertex));
    965 			else
    966 			{
    967 				// Only fragment case with unstable attribute has an additional fract() call.
    968 				vertexOrFragmentGroup->addChild(new LoopWorkloadReferenceCase(m_context, "reference_stable", "", true, isVertex));
    969 				vertexOrFragmentGroup->addChild(new LoopWorkloadReferenceCase(m_context, "reference_unstable", "", false, isVertex));
    970 			}
    971 		}
    972 	}
    973 }
    974 
    975 } // Performance
    976 } // gles3
    977 } // deqp
    978