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.
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 << "#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 
setupProgram(deUint32 program)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 
~ConditionalCase(void)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 
deinit(void)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 
LoopCase(Context & context,const char * name,const char * description,LoopType type,DecisionType decisionType,bool isLoopBoundStable,bool isVertex)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 
init(void)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 
setupProgram(deUint32 program)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 
~LoopCase(void)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 
deinit(void)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 
WorkloadReferenceCase(Context & context,const char * name,const char * description,bool isVertex)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 
init(void)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:
LoopWorkloadReferenceCase(Context & context,const char * name,const char * description,bool isAttributeStable,bool isVertex)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 
writeWorkload(std::ostringstream & dst,const char * resultVariableName,const char * inputVariableName) const779 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:
ConditionalWorkloadReferenceCase(Context & context,const char * name,const char * description,bool isAttributeStable,bool isVertex)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 
writeWorkload(std::ostringstream & dst,const char * resultVariableName,const char * inputVariableName) const816 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:
EmptyWorkloadReferenceCase(Context & context,const char * name,const char * description,bool isVertex)835 	EmptyWorkloadReferenceCase	(Context& context, const char* name, const char* description, bool isVertex)
836 		: WorkloadReferenceCase (context, name, description, isVertex)
837 	{
838 	}
839 
840 protected:
writeWorkload(std::ostringstream & dst,const char * resultVariableName,const char * inputVariableName) const841 	void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const
842 	{
843 		dst << "\t" << resultVariableName << " = " << inputVariableName << ";\n";
844 	}
845 };
846 
ShaderControlStatementTests(Context & context)847 ShaderControlStatementTests::ShaderControlStatementTests (Context& context)
848 	: TestCaseGroup(context, "control_statement", "Control Statement Performance Tests")
849 {
850 }
851 
~ShaderControlStatementTests(void)852 ShaderControlStatementTests::~ShaderControlStatementTests (void)
853 {
854 }
855 
init(void)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