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
34namespace deqp
35{
36namespace gles2
37{
38namespace Performance
39{
40
41using namespace gls;
42using namespace glw; // GL types
43using tcu::Vec4;
44using tcu::TestLog;
45using std::string;
46using std::vector;
47
48// Writes the workload expression used in conditional tests.
49static 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).
67static 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.
86enum DecisionType
87{
88	DECISION_STATIC = 0,
89	DECISION_UNIFORM,
90	DECISION_ATTRIBUTE,
91
92	DECISION_LAST
93};
94
95class ControlStatementCase :  public ShaderPerformanceCase
96{
97public:
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
119class ConditionalCase : public ControlStatementCase
120{
121public:
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
147private:
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
156ConditionalCase::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
165void 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
346void 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
374ConditionalCase::~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
385void 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
400class LoopCase : public ControlStatementCase
401{
402public:
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
418private:
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
427LoopCase::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
436void 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
617void 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
647LoopCase::~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
658void 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.
674class WorkloadReferenceCase : public ControlStatementCase
675{
676public:
677							WorkloadReferenceCase		(Context& context, const char* name, const char* description, bool isVertex);
678
679	void					init						(void);
680
681protected:
682	virtual void			writeWorkload				(std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const = 0;
683};
684
685WorkloadReferenceCase::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
690void 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
751class LoopWorkloadReferenceCase : public WorkloadReferenceCase
752{
753public:
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
760protected:
761	void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const;
762
763private:
764	bool m_isAttributeStable;
765};
766
767void 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
788class ConditionalWorkloadReferenceCase : public WorkloadReferenceCase
789{
790public:
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
797protected:
798	void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const;
799
800private:
801	bool m_isAttributeStable;
802};
803
804void 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.
820class EmptyWorkloadReferenceCase : public WorkloadReferenceCase
821{
822public:
823	EmptyWorkloadReferenceCase	(Context& context, const char* name, const char* description, bool isVertex)
824		: WorkloadReferenceCase (context, name, description, isVertex)
825	{
826	}
827
828protected:
829	void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const
830	{
831		dst << "\t" << resultVariableName << " = " << inputVariableName << ";\n";
832	}
833};
834
835ShaderControlStatementTests::ShaderControlStatementTests (Context& context)
836	: TestCaseGroup(context, "control_statement", "Control Statement Performance Tests")
837{
838}
839
840ShaderControlStatementTests::~ShaderControlStatementTests (void)
841{
842}
843
844void 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