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
34namespace deqp
35{
36namespace gles3
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 << "#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
350void 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
378ConditionalCase::~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
389void 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
404class LoopCase : public ControlStatementCase
405{
406public:
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
422private:
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
431LoopCase::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
440void 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
625void 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
655LoopCase::~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
666void 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.
682class WorkloadReferenceCase : public ControlStatementCase
683{
684public:
685							WorkloadReferenceCase		(Context& context, const char* name, const char* description, bool isVertex);
686
687	void					init						(void);
688
689protected:
690	virtual void			writeWorkload				(std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const = 0;
691};
692
693WorkloadReferenceCase::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
698void 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
763class LoopWorkloadReferenceCase : public WorkloadReferenceCase
764{
765public:
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
772protected:
773	void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const;
774
775private:
776	bool m_isAttributeStable;
777};
778
779void 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
800class ConditionalWorkloadReferenceCase : public WorkloadReferenceCase
801{
802public:
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
809protected:
810	void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const;
811
812private:
813	bool m_isAttributeStable;
814};
815
816void 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.
832class EmptyWorkloadReferenceCase : public WorkloadReferenceCase
833{
834public:
835	EmptyWorkloadReferenceCase	(Context& context, const char* name, const char* description, bool isVertex)
836		: WorkloadReferenceCase (context, name, description, isVertex)
837	{
838	}
839
840protected:
841	void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const
842	{
843		dst << "\t" << resultVariableName << " = " << inputVariableName << ";\n";
844	}
845};
846
847ShaderControlStatementTests::ShaderControlStatementTests (Context& context)
848	: TestCaseGroup(context, "control_statement", "Control Statement Performance Tests")
849{
850}
851
852ShaderControlStatementTests::~ShaderControlStatementTests (void)
853{
854}
855
856void 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