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