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 Transform feedback tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es3fTransformFeedbackTests.hpp"
25#include "tcuTestLog.hpp"
26#include "tcuSurface.hpp"
27#include "tcuImageCompare.hpp"
28#include "tcuVector.hpp"
29#include "tcuFormatUtil.hpp"
30#include "tcuRenderTarget.hpp"
31#include "gluShaderUtil.hpp"
32#include "gluVarType.hpp"
33#include "gluVarTypeUtil.hpp"
34#include "gluPixelTransfer.hpp"
35#include "gluRenderContext.hpp"
36#include "gluShaderProgram.hpp"
37#include "gluObjectWrapper.hpp"
38#include "glwFunctions.hpp"
39#include "glwEnums.hpp"
40#include "deRandom.hpp"
41#include "deStringUtil.hpp"
42#include "deMemory.h"
43#include "deString.h"
44
45#include <set>
46#include <map>
47#include <algorithm>
48
49using std::string;
50using std::vector;
51using std::set;
52
53using std::map;
54using std::set;
55
56using tcu::TestLog;
57
58namespace deqp
59{
60namespace gles3
61{
62namespace Functional
63{
64namespace TransformFeedback
65{
66
67enum
68{
69	VIEWPORT_WIDTH			= 128,
70	VIEWPORT_HEIGHT			= 128,
71	BUFFER_GUARD_MULTIPLIER = 2		//!< stride*BUFFER_GUARD_MULTIPLIER bytes are added to the end of tf buffer and used to check for overruns.
72};
73
74enum Interpolation
75{
76	INTERPOLATION_SMOOTH = 0,
77	INTERPOLATION_FLAT,
78	INTERPOLATION_CENTROID,
79
80	INTERPOLATION_LAST
81};
82
83static const char* getInterpolationName (Interpolation interp)
84{
85	switch (interp)
86	{
87		case INTERPOLATION_SMOOTH:		return "smooth";
88		case INTERPOLATION_FLAT:		return "flat";
89		case INTERPOLATION_CENTROID:	return "centroid";
90		default:
91			DE_ASSERT(false);
92			return DE_NULL;
93	}
94}
95
96struct Varying
97{
98						Varying				(const char* name_, const glu::VarType& type_, Interpolation interp_)
99							: name			(name_)
100							, type			(type_)
101							, interpolation	(interp_)
102						{
103						}
104
105	std::string			name;				//!< Variable name.
106	glu::VarType		type;				//!< Variable type.
107	Interpolation		interpolation;		//!< Interpolation mode (smooth, flat, centroid).
108};
109
110struct VaryingNameEquals
111{
112					VaryingNameEquals	(const std::string& name_) : name(name_) {}
113	bool			operator()			(const Varying& var) const { return var.name == name; }
114
115	std::string		name;
116};
117
118struct Attribute
119{
120	Attribute (const std::string& name_, const glu::VarType& type_, int offset_)
121		: name		(name_)
122		, type		(type_)
123		, offset	(offset_)
124	{
125	}
126
127	std::string			name;
128	glu::VarType		type;
129	int					offset;
130};
131
132struct AttributeNameEquals
133{
134					AttributeNameEquals	(const std::string& name_) : name(name_) {}
135	bool			operator()			(const Attribute& attr) const { return attr.name == name; }
136
137	std::string		name;
138};
139
140struct Output
141{
142	Output (void)
143		: bufferNdx	(0)
144		, offset	(0)
145	{
146	}
147
148	std::string					name;
149	glu::VarType				type;
150	int							bufferNdx;
151	int							offset;
152	vector<const Attribute*>	inputs;
153};
154
155struct DrawCall
156{
157				DrawCall (int numElements_, bool tfEnabled_)
158					: numElements				(numElements_)
159					, transformFeedbackEnabled	(tfEnabled_)
160				{
161				}
162
163				DrawCall (void)
164					: numElements				(0)
165					, transformFeedbackEnabled	(false)
166				{
167				}
168
169	int			numElements;
170	bool		transformFeedbackEnabled;
171};
172
173std::ostream& operator<< (std::ostream& str, const DrawCall& call)
174{
175	return str << "(" << call.numElements << ", " << (call.transformFeedbackEnabled ? "resumed" : "paused") << ")";
176}
177
178class ProgramSpec
179{
180public:
181									ProgramSpec						(void);
182									~ProgramSpec					(void);
183
184	glu::StructType*				createStruct					(const char* name);
185	void							addVarying						(const char* name, const glu::VarType& type, Interpolation interp);
186	void							addTransformFeedbackVarying		(const char* name);
187
188	const vector<glu::StructType*>&	getStructs						(void) const { return m_structs;	}
189	const vector<Varying>&			getVaryings						(void) const { return m_varyings;	}
190	const vector<string>&			getTransformFeedbackVaryings	(void) const { return m_transformFeedbackVaryings; }
191	bool							isPointSizeUsed					(void) const;
192
193private:
194									ProgramSpec						(const ProgramSpec& other);
195	ProgramSpec&					operator=						(const ProgramSpec& other);
196
197	vector<glu::StructType*>		m_structs;
198	vector<Varying>					m_varyings;
199	vector<string>					m_transformFeedbackVaryings;
200};
201
202// ProgramSpec
203
204ProgramSpec::ProgramSpec (void)
205{
206}
207
208ProgramSpec::~ProgramSpec (void)
209{
210	for (vector<glu::StructType*>::iterator i = m_structs.begin(); i != m_structs.end(); i++)
211		delete *i;
212}
213
214glu::StructType* ProgramSpec::createStruct (const char* name)
215{
216	m_structs.reserve(m_structs.size()+1);
217	m_structs.push_back(new glu::StructType(name));
218	return m_structs.back();
219}
220
221void ProgramSpec::addVarying (const char* name, const glu::VarType& type, Interpolation interp)
222{
223	m_varyings.push_back(Varying(name, type, interp));
224}
225
226void ProgramSpec::addTransformFeedbackVarying (const char* name)
227{
228	m_transformFeedbackVaryings.push_back(name);
229}
230
231bool ProgramSpec::isPointSizeUsed (void) const
232{
233	return std::find(m_transformFeedbackVaryings.begin(), m_transformFeedbackVaryings.end(), "gl_PointSize") != m_transformFeedbackVaryings.end();
234}
235
236static bool isProgramSupported (const glw::Functions& gl, const ProgramSpec& spec, deUint32 tfMode)
237{
238	int		maxVertexAttribs			= 0;
239	int		maxTfInterleavedComponents	= 0;
240	int		maxTfSeparateAttribs		= 0;
241	int		maxTfSeparateComponents		= 0;
242
243	gl.getIntegerv(GL_MAX_VERTEX_ATTRIBS,								&maxVertexAttribs);
244	gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS,	&maxTfInterleavedComponents);
245	gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,			&maxTfSeparateAttribs);
246	gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS,		&maxTfSeparateComponents);
247
248	// Check vertex attribs.
249	int totalVertexAttribs	= 1 /* a_position */ + (spec.isPointSizeUsed() ? 1 : 0);
250	for (vector<Varying>::const_iterator var = spec.getVaryings().begin(); var != spec.getVaryings().end(); var++)
251	{
252		for (glu::VectorTypeIterator vecIter = glu::VectorTypeIterator::begin(&var->type); vecIter != glu::VectorTypeIterator::end(&var->type); vecIter++)
253			totalVertexAttribs += 1;
254	}
255
256	if (totalVertexAttribs > maxVertexAttribs)
257		return false; // Vertex attribute count exceeded.
258
259	// Check varyings.
260	int totalTfComponents	= 0;
261	int totalTfAttribs		= 0;
262	for (vector<string>::const_iterator iter = spec.getTransformFeedbackVaryings().begin(); iter != spec.getTransformFeedbackVaryings().end(); iter++)
263	{
264		const string&	name			= *iter;
265		int				numComponents	= 0;
266
267		if (name == "gl_Position")
268			numComponents = 4;
269		else if (name == "gl_PointSize")
270			numComponents = 1;
271		else
272		{
273			string						varName		= glu::parseVariableName(name.c_str());
274			const Varying&				varying		= *std::find_if(spec.getVaryings().begin(), spec.getVaryings().end(), VaryingNameEquals(varName));
275			glu::TypeComponentVector	varPath;
276
277			glu::parseTypePath(name.c_str(), varying.type, varPath);
278			numComponents = glu::getVarType(varying.type, varPath).getScalarSize();
279		}
280
281		if (tfMode == GL_SEPARATE_ATTRIBS && numComponents > maxTfSeparateComponents)
282			return false; // Per-attribute component count exceeded.
283
284		totalTfComponents	+= numComponents;
285		totalTfAttribs		+= 1;
286	}
287
288	if (tfMode == GL_SEPARATE_ATTRIBS && totalTfAttribs > maxTfSeparateAttribs)
289		return false;
290
291	if (tfMode == GL_INTERLEAVED_ATTRIBS && totalTfComponents > maxTfInterleavedComponents)
292		return false;
293
294	return true;
295}
296
297// Program
298
299static std::string getAttributeName (const char* varyingName, const glu::TypeComponentVector& path)
300{
301	std::ostringstream str;
302
303	str << "a_" << (deStringBeginsWith(varyingName, "v_") ? varyingName+2 : varyingName);
304
305	for (glu::TypeComponentVector::const_iterator iter = path.begin(); iter != path.end(); iter++)
306	{
307		const char* prefix = DE_NULL;
308
309		switch (iter->type)
310		{
311			case glu::VarTypeComponent::STRUCT_MEMBER:		prefix = "_m";	break;
312			case glu::VarTypeComponent::ARRAY_ELEMENT:		prefix = "_e";	break;
313			case glu::VarTypeComponent::MATRIX_COLUMN:		prefix = "_c";	break;
314			case glu::VarTypeComponent::VECTOR_COMPONENT:	prefix = "_s";	break;
315			default:
316				DE_ASSERT(false);
317		}
318
319		str << prefix << iter->index;
320	}
321
322	return str.str();
323}
324
325static void genShaderSources (const ProgramSpec& spec, std::string& vertSource, std::string& fragSource, bool pointSizeRequired)
326{
327	std::ostringstream	vtx;
328	std::ostringstream	frag;
329	bool				addPointSize	= spec.isPointSizeUsed();
330
331	vtx << "#version 300 es\n"
332		<< "in highp vec4 a_position;\n";
333	frag << "#version 300 es\n"
334		 << "layout(location = 0) out mediump vec4 o_color;\n"
335		 << "uniform highp vec4 u_scale;\n"
336		 << "uniform highp vec4 u_bias;\n";
337
338	if (addPointSize)
339		vtx << "in highp float a_pointSize;\n";
340
341	// Declare attributes.
342	for (vector<Varying>::const_iterator var = spec.getVaryings().begin(); var != spec.getVaryings().end(); var++)
343	{
344		const char*			name	= var->name.c_str();
345		const glu::VarType&	type	= var->type;
346
347		for (glu::VectorTypeIterator vecIter = glu::VectorTypeIterator::begin(&type); vecIter != glu::VectorTypeIterator::end(&type); vecIter++)
348		{
349			glu::VarType	attribType	= glu::getVarType(type, vecIter.getPath());
350			string			attribName	= getAttributeName(name, vecIter.getPath());
351
352			vtx << "in " << glu::declare(attribType, attribName.c_str()) << ";\n";
353		}
354	}
355
356	// Declare vayrings.
357	for (int ndx = 0; ndx < 2; ndx++)
358	{
359		const char*			inout	= ndx ? "in" : "out";
360		std::ostringstream&	str		= ndx ? frag : vtx;
361
362		// Declare structs that have type name.
363		for (vector<glu::StructType*>::const_iterator structIter = spec.getStructs().begin(); structIter != spec.getStructs().end(); structIter++)
364		{
365			const glu::StructType* structPtr = *structIter;
366			if (structPtr->hasTypeName())
367				str << glu::declare(structPtr) << ";\n";
368		}
369
370		for (vector<Varying>::const_iterator var = spec.getVaryings().begin(); var != spec.getVaryings().end(); var++)
371			str << getInterpolationName(var->interpolation) << " " << inout << " " << glu::declare(var->type, var->name.c_str()) << ";\n";
372	}
373
374	vtx << "\nvoid main (void)\n{\n"
375		<< "\tgl_Position = a_position;\n";
376	frag << "\nvoid main (void)\n{\n"
377		 << "\thighp vec4 res = vec4(0.0);\n";
378
379	if (addPointSize)
380		vtx << "\tgl_PointSize = a_pointSize;\n";
381	else if (pointSizeRequired)
382		vtx << "\tgl_PointSize = 1.0;\n";
383
384	// Generate assignments / usage.
385	for (vector<Varying>::const_iterator var = spec.getVaryings().begin(); var != spec.getVaryings().end(); var++)
386	{
387		const char*			name	= var->name.c_str();
388		const glu::VarType&	type	= var->type;
389
390		for (glu::VectorTypeIterator vecIter = glu::VectorTypeIterator::begin(&type); vecIter != glu::VectorTypeIterator::end(&type); vecIter++)
391		{
392			glu::VarType	subType		= glu::getVarType(type, vecIter.getPath());
393			string			attribName	= getAttributeName(name, vecIter.getPath());
394
395			DE_ASSERT(subType.isBasicType() && glu::isDataTypeScalarOrVector(subType.getBasicType()));
396
397			// Vertex: assign from attribute.
398			vtx << "\t" << name << vecIter << " = " << attribName << ";\n";
399
400			// Fragment: add to res variable.
401			int scalarSize = glu::getDataTypeScalarSize(subType.getBasicType());
402
403			frag << "\tres += ";
404			if (scalarSize == 1)		frag << "vec4(" << name << vecIter << ")";
405			else if (scalarSize == 2)	frag << "vec2(" << name << vecIter << ").xxyy";
406			else if (scalarSize == 3)	frag << "vec3(" << name << vecIter << ").xyzx";
407			else if (scalarSize == 4)	frag << "vec4(" << name << vecIter << ")";
408
409			frag << ";\n";
410		}
411	}
412
413	frag << "\to_color = res * u_scale + u_bias;\n";
414
415	vtx << "}\n";
416	frag << "}\n";
417
418	vertSource = vtx.str();
419	fragSource = frag.str();
420}
421
422static glu::ShaderProgram* createVertexCaptureProgram (const glu::RenderContext& context, const ProgramSpec& spec, deUint32 bufferMode, deUint32 primitiveType)
423{
424	std::string vertSource, fragSource;
425
426	genShaderSources(spec, vertSource, fragSource, primitiveType == GL_POINTS /* Is point size required? */);
427
428	return new glu::ShaderProgram(context, glu::ProgramSources()
429										   << glu::VertexSource(vertSource)
430										   << glu::FragmentSource(fragSource)
431										   << glu::TransformFeedbackVaryings<vector<string>::const_iterator>(spec.getTransformFeedbackVaryings().begin(), spec.getTransformFeedbackVaryings().end())
432										   << glu::TransformFeedbackMode(bufferMode));
433}
434
435// Helpers.
436
437static void computeInputLayout (vector<Attribute>& attributes, int& inputStride, const vector<Varying>& varyings, bool usePointSize)
438{
439	inputStride = 0;
440
441	// Add position.
442	attributes.push_back(Attribute("a_position", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP), inputStride));
443	inputStride += 4*(int)sizeof(deUint32);
444
445	if (usePointSize)
446	{
447		attributes.push_back(Attribute("a_pointSize", glu::VarType(glu::TYPE_FLOAT, glu::PRECISION_HIGHP), inputStride));
448		inputStride += 1*(int)sizeof(deUint32);
449	}
450
451	// Compute attribute vector.
452	for (vector<Varying>::const_iterator var = varyings.begin(); var != varyings.end(); var++)
453	{
454		for (glu::VectorTypeIterator vecIter = glu::VectorTypeIterator::begin(&var->type); vecIter != glu::VectorTypeIterator::end(&var->type); vecIter++)
455		{
456			glu::VarType	type	= vecIter.getType();
457			string			name	= getAttributeName(var->name.c_str(), vecIter.getPath());
458
459			attributes.push_back(Attribute(name, type, inputStride));
460			inputStride += glu::getDataTypeScalarSize(type.getBasicType())*(int)sizeof(deUint32);
461		}
462	}
463}
464
465static void computeTransformFeedbackOutputs (vector<Output>& transformFeedbackOutputs, const vector<Attribute>& attributes, const vector<Varying>& varyings, const vector<string>& transformFeedbackVaryings, deUint32 bufferMode)
466{
467	int accumulatedSize = 0;
468
469	transformFeedbackOutputs.resize(transformFeedbackVaryings.size());
470	for (int varNdx = 0; varNdx < (int)transformFeedbackVaryings.size(); varNdx++)
471	{
472		const string&	name		= transformFeedbackVaryings[varNdx];
473		int				bufNdx		= (bufferMode == GL_SEPARATE_ATTRIBS ? varNdx : 0);
474		int				offset		= (bufferMode == GL_SEPARATE_ATTRIBS ? 0 : accumulatedSize);
475		Output&			output		= transformFeedbackOutputs[varNdx];
476
477		output.name			= name;
478		output.bufferNdx	= bufNdx;
479		output.offset		= offset;
480
481		if (name == "gl_Position")
482		{
483			const Attribute* posIn = &(*std::find_if(attributes.begin(), attributes.end(), AttributeNameEquals("a_position")));
484			output.type = posIn->type;
485			output.inputs.push_back(posIn);
486		}
487		else if (name == "gl_PointSize")
488		{
489			const Attribute* sizeIn = &(*std::find_if(attributes.begin(), attributes.end(), AttributeNameEquals("a_pointSize")));
490			output.type = sizeIn->type;
491			output.inputs.push_back(sizeIn);
492		}
493		else
494		{
495			string						varName		= glu::parseVariableName(name.c_str());
496			const Varying&				varying		= *std::find_if(varyings.begin(), varyings.end(), VaryingNameEquals(varName));
497			glu::TypeComponentVector	varPath;
498
499			glu::parseTypePath(name.c_str(), varying.type, varPath);
500
501			output.type = glu::getVarType(varying.type, varPath);
502
503			// Add all vectorized attributes as inputs.
504			for (glu::VectorTypeIterator iter = glu::VectorTypeIterator::begin(&output.type); iter != glu::VectorTypeIterator::end(&output.type); iter++)
505			{
506				// Full path.
507				glu::TypeComponentVector fullPath(varPath.size() + iter.getPath().size());
508
509				std::copy(varPath.begin(), varPath.end(), fullPath.begin());
510				std::copy(iter.getPath().begin(), iter.getPath().end(), fullPath.begin()+varPath.size());
511
512				string				attribName	= getAttributeName(varName.c_str(), fullPath);
513				const Attribute*	attrib		= &(*std::find_if(attributes.begin(), attributes.end(), AttributeNameEquals(attribName)));
514
515				output.inputs.push_back(attrib);
516			}
517		}
518
519		accumulatedSize += output.type.getScalarSize()*(int)sizeof(deUint32);
520	}
521}
522
523static deUint32 signExtend (deUint32 value, deUint32 numBits)
524{
525	DE_ASSERT(numBits >= 1u && numBits <= 32u);
526	if (numBits == 32u)
527		return value;
528	else if ((value & (1u << (numBits-1u))) == 0u)
529		return value;
530	else
531		return value | ~((1u << numBits) - 1u);
532}
533
534static void genAttributeData (const Attribute& attrib, deUint8* basePtr, int stride, int numElements, de::Random& rnd)
535{
536	const int				elementSize	= (int)sizeof(deUint32);
537	const bool				isFloat		= glu::isDataTypeFloatOrVec(attrib.type.getBasicType());
538	const bool				isInt		= glu::isDataTypeIntOrIVec(attrib.type.getBasicType());
539	const bool				isUint		= glu::isDataTypeUintOrUVec(attrib.type.getBasicType());
540	const glu::Precision	precision	= attrib.type.getPrecision();
541	const int				numComps	= glu::getDataTypeScalarSize(attrib.type.getBasicType());
542
543	for (int elemNdx = 0; elemNdx < numElements; elemNdx++)
544	{
545		for (int compNdx = 0; compNdx < numComps; compNdx++)
546		{
547			int offset = attrib.offset+elemNdx*stride+compNdx*elementSize;
548			if (isFloat)
549			{
550				float* comp = (float*)(basePtr+offset);
551				switch (precision)
552				{
553					case glu::PRECISION_LOWP:		*comp = 0.0f + 0.25f*(float)rnd.getInt(0, 4);	break;
554					case glu::PRECISION_MEDIUMP:	*comp = rnd.getFloat(-1e3f, 1e3f);				break;
555					case glu::PRECISION_HIGHP:		*comp = rnd.getFloat(-1e5f, 1e5f);				break;
556					default:
557						DE_ASSERT(false);
558				}
559			}
560			else if (isInt)
561			{
562				int* comp = (int*)(basePtr+offset);
563				switch (precision)
564				{
565					case glu::PRECISION_LOWP:		*comp = (int)signExtend(rnd.getUint32()&0xff, 8);		break;
566					case glu::PRECISION_MEDIUMP:	*comp = (int)signExtend(rnd.getUint32()&0xffff, 16);	break;
567					case glu::PRECISION_HIGHP:		*comp = (int)rnd.getUint32();							break;
568					default:
569						DE_ASSERT(false);
570				}
571			}
572			else if (isUint)
573			{
574				deUint32* comp = (deUint32*)(basePtr+offset);
575				switch (precision)
576				{
577					case glu::PRECISION_LOWP:		*comp = rnd.getUint32()&0xff;	break;
578					case glu::PRECISION_MEDIUMP:	*comp = rnd.getUint32()&0xffff;	break;
579					case glu::PRECISION_HIGHP:		*comp = rnd.getUint32();		break;
580					default:
581						DE_ASSERT(false);
582				}
583			}
584			else
585				DE_ASSERT(false);
586		}
587	}
588}
589
590static void genInputData (const vector<Attribute>& attributes, int numInputs, int inputStride, deUint8* inputBasePtr, de::Random& rnd)
591{
592	// Random positions.
593	const Attribute& position = *std::find_if(attributes.begin(), attributes.end(), AttributeNameEquals("a_position"));
594
595	for (int ndx = 0; ndx < numInputs; ndx++)
596	{
597		deUint8* ptr = inputBasePtr + position.offset + inputStride*ndx;
598		*((float*)(ptr+ 0)) = rnd.getFloat(-1.2f, 1.2f);
599		*((float*)(ptr+ 4)) = rnd.getFloat(-1.2f, 1.2f);
600		*((float*)(ptr+ 8)) = rnd.getFloat(-1.2f, 1.2f);
601		*((float*)(ptr+12)) = rnd.getFloat(0.1f, 2.0f);
602	}
603
604	// Point size.
605	vector<Attribute>::const_iterator pointSizePos = std::find_if(attributes.begin(), attributes.end(), AttributeNameEquals("a_pointSize"));
606	if (pointSizePos != attributes.end())
607	{
608		for (int ndx = 0; ndx < numInputs; ndx++)
609		{
610			deUint8* ptr = inputBasePtr + pointSizePos->offset + inputStride*ndx;
611			*((float*)ptr) = rnd.getFloat(1.0f, 8.0f);
612		}
613	}
614
615	// Random data for rest of components.
616	for (vector<Attribute>::const_iterator attrib = attributes.begin(); attrib != attributes.end(); attrib++)
617	{
618		if (attrib->name == "a_position" || attrib->name == "a_pointSize")
619			continue;
620
621		genAttributeData(*attrib, inputBasePtr, inputStride, numInputs, rnd);
622	}
623}
624
625static deUint32 getTransformFeedbackOutputCount (deUint32 primitiveType, int numElements)
626{
627	switch (primitiveType)
628	{
629		case GL_TRIANGLES:			return numElements - numElements%3;
630		case GL_TRIANGLE_STRIP:		return de::max(0, numElements-2)*3;
631		case GL_TRIANGLE_FAN:		return de::max(0, numElements-2)*3;
632		case GL_LINES:				return numElements - numElements%2;
633		case GL_LINE_STRIP:			return de::max(0, numElements-1)*2;
634		case GL_LINE_LOOP:			return numElements > 1 ? numElements*2 : 0;
635		case GL_POINTS:				return numElements;
636
637		default:
638			DE_ASSERT(false);
639			return 0;
640	}
641}
642
643static deUint32 getTransformFeedbackPrimitiveCount (deUint32 primitiveType, int numElements)
644{
645	switch (primitiveType)
646	{
647		case GL_TRIANGLES:			return numElements/3;
648		case GL_TRIANGLE_STRIP:		return de::max(0, numElements-2);
649		case GL_TRIANGLE_FAN:		return de::max(0, numElements-2);
650		case GL_LINES:				return numElements/2;
651		case GL_LINE_STRIP:			return de::max(0, numElements-1);
652		case GL_LINE_LOOP:			return numElements > 1 ? numElements : 0;
653		case GL_POINTS:				return numElements;
654
655		default:
656			DE_ASSERT(false);
657			return 0;
658	}
659}
660
661static deUint32 getTransformFeedbackPrimitiveMode (deUint32 primitiveType)
662{
663	switch (primitiveType)
664	{
665		case GL_TRIANGLES:
666		case GL_TRIANGLE_STRIP:
667		case GL_TRIANGLE_FAN:
668			return GL_TRIANGLES;
669
670		case GL_LINES:
671		case GL_LINE_LOOP:
672		case GL_LINE_STRIP:
673			return GL_LINES;
674
675		case GL_POINTS:
676			return GL_POINTS;
677
678		default:
679			DE_ASSERT(false);
680			return 0;
681	}
682}
683
684static int getAttributeIndex (deUint32 primitiveType, int numInputs, int outNdx)
685{
686	switch (primitiveType)
687	{
688		case GL_TRIANGLES:			return outNdx;
689		case GL_LINES:				return outNdx;
690		case GL_POINTS:				return outNdx;
691
692		case GL_TRIANGLE_STRIP:
693		{
694			int triNdx = outNdx/3;
695			int vtxNdx = outNdx%3;
696			return (triNdx%2 != 0 && vtxNdx < 2) ? (triNdx+1-vtxNdx) : (triNdx+vtxNdx);
697		}
698
699		case GL_TRIANGLE_FAN:
700			return (outNdx%3 != 0) ? (outNdx/3 + outNdx%3) : 0;
701
702		case GL_LINE_STRIP:
703			return outNdx/2 + outNdx%2;
704
705		case GL_LINE_LOOP:
706		{
707			int inNdx = outNdx/2 + outNdx%2;
708			return inNdx < numInputs ? inNdx : 0;
709		}
710
711		default:
712			DE_ASSERT(false);
713			return 0;
714	}
715}
716
717static bool compareTransformFeedbackOutput (tcu::TestLog& log, deUint32 primitiveType, const Output& output, int numInputs, const deUint8* inBasePtr, int inStride, const deUint8* outBasePtr, int outStride)
718{
719	bool		isOk		= true;
720	int			outOffset	= output.offset;
721
722	for (int attrNdx = 0; attrNdx < (int)output.inputs.size(); attrNdx++)
723	{
724		const Attribute&	attribute		= *output.inputs[attrNdx];
725		glu::DataType		type			= attribute.type.getBasicType();
726		int					numComponents	= glu::getDataTypeScalarSize(type);
727		glu::Precision		precision		= attribute.type.getPrecision();
728		glu::DataType		scalarType		= glu::getDataTypeScalarType(type);
729		int					numOutputs		= getTransformFeedbackOutputCount(primitiveType, numInputs);
730
731		for (int outNdx = 0; outNdx < numOutputs; outNdx++)
732		{
733			int inNdx = getAttributeIndex(primitiveType, numInputs, outNdx);
734
735			for (int compNdx = 0; compNdx < numComponents; compNdx++)
736			{
737				const deUint8*	inPtr	= inBasePtr + inStride*inNdx + attribute.offset + compNdx*sizeof(deUint32);
738				const deUint8*	outPtr	= outBasePtr + outStride*outNdx + outOffset + compNdx*sizeof(deUint32);
739				deUint32		inVal	= *(const deUint32*)inPtr;
740				deUint32		outVal	= *(const deUint32*)outPtr;
741				bool			isEqual	= false;
742
743				if (scalarType == glu::TYPE_FLOAT)
744				{
745					// ULP comparison is used for highp and mediump. Lowp uses threshold-comparison.
746					switch (precision)
747					{
748						case glu::PRECISION_HIGHP:		isEqual = de::abs((int)inVal - (int)outVal) < 2;				break;
749						case glu::PRECISION_MEDIUMP:	isEqual = de::abs((int)inVal - (int)outVal) < 2+(1<<13);		break;
750						case glu::PRECISION_LOWP:
751						{
752							float inF	= *(const float*)inPtr;
753							float outF	= *(const float*)outPtr;
754							isEqual = de::abs(inF - outF) < 0.1f;
755							break;
756						}
757						default:
758							DE_ASSERT(false);
759					}
760				}
761				else
762					isEqual = (inVal == outVal); // Bit-exact match required for integer types.
763
764				if (!isEqual)
765				{
766					log << TestLog::Message << "Mismatch in " << output.name << " (" << attribute.name << "), output = " << outNdx << ", input = " << inNdx << ", component = " << compNdx << TestLog::EndMessage;
767					isOk = false;
768					break;
769				}
770			}
771
772			if (!isOk)
773				break;
774		}
775
776		if (!isOk)
777			break;
778
779		outOffset += numComponents*(int)sizeof(deUint32);
780	}
781
782	return isOk;
783}
784
785static int computeTransformFeedbackPrimitiveCount (deUint32 primitiveType, const DrawCall* first, const DrawCall* end)
786{
787	int primCount = 0;
788
789	for (const DrawCall* call = first; call != end; ++call)
790	{
791		if (call->transformFeedbackEnabled)
792			primCount += getTransformFeedbackPrimitiveCount(primitiveType, call->numElements);
793	}
794
795	return primCount;
796}
797
798static void writeBufferGuard (const glw::Functions& gl, deUint32 target, int bufferSize, int guardSize)
799{
800	deUint8* ptr = (deUint8*)gl.mapBufferRange(target, bufferSize, guardSize, GL_MAP_WRITE_BIT);
801	if (ptr)
802		deMemset(ptr, 0xcd, guardSize);
803	gl.unmapBuffer(target);
804	GLU_EXPECT_NO_ERROR(gl.getError(), "guardband write");
805}
806
807static bool verifyGuard (const deUint8* ptr, int guardSize)
808{
809	for (int ndx = 0; ndx < guardSize; ndx++)
810	{
811		if (ptr[ndx] != 0xcd)
812			return false;
813	}
814	return true;
815}
816
817static void logTransformFeedbackVaryings (TestLog& log, const glw::Functions& gl, deUint32 program)
818{
819	int numTfVaryings	= 0;
820	int	maxNameLen		= 0;
821
822	gl.getProgramiv(program, GL_TRANSFORM_FEEDBACK_VARYINGS, &numTfVaryings);
823	gl.getProgramiv(program, GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, &maxNameLen);
824	GLU_EXPECT_NO_ERROR(gl.getError(), "Query TF varyings");
825
826	log << TestLog::Message << "GL_TRANSFORM_FEEDBACK_VARYINGS = " << numTfVaryings << TestLog::EndMessage;
827
828	vector<char> nameBuf(maxNameLen+1);
829
830	for (int ndx = 0; ndx < numTfVaryings; ndx++)
831	{
832		glw::GLsizei	size	= 0;
833		glw::GLenum		type	= 0;
834
835		gl.getTransformFeedbackVarying(program, ndx, (glw::GLsizei)nameBuf.size(), DE_NULL, &size, &type, &nameBuf[0]);
836		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetTransformFeedbackVarying()");
837
838		const glu::DataType	dataType	= glu::getDataTypeFromGLType(type);
839		const std::string	typeName	= dataType != glu::TYPE_LAST ? std::string(glu::getDataTypeName(dataType))
840																	 : (std::string("unknown(") + tcu::toHex(type).toString() + ")");
841
842		log << TestLog::Message << (const char*)&nameBuf[0] << ": " << typeName << "[" << size << "]" << TestLog::EndMessage;
843	}
844}
845
846class TransformFeedbackCase : public TestCase
847{
848public:
849								TransformFeedbackCase		(Context& context, const char* name, const char* desc, deUint32 bufferMode, deUint32 primitiveType);
850								~TransformFeedbackCase		(void);
851
852	void						init						(void);
853	void						deinit						(void);
854	IterateResult				iterate						(void);
855
856protected:
857	ProgramSpec					m_progSpec;
858	deUint32					m_bufferMode;
859	deUint32					m_primitiveType;
860
861private:
862								TransformFeedbackCase		(const TransformFeedbackCase& other);
863	TransformFeedbackCase&		operator=					(const TransformFeedbackCase& other);
864
865	bool						runTest						(const DrawCall* first, const DrawCall* end, deUint32 seed);
866
867	// Derived from ProgramSpec in init()
868	int							m_inputStride;
869	vector<Attribute>			m_attributes;
870	vector<Output>				m_transformFeedbackOutputs;
871	vector<int>					m_bufferStrides;
872
873	// GL state.
874	glu::ShaderProgram*			m_program;
875	glu::TransformFeedback*		m_transformFeedback;
876	vector<deUint32>			m_outputBuffers;
877
878	int							m_iterNdx;
879};
880
881TransformFeedbackCase::TransformFeedbackCase (Context& context, const char* name, const char* desc, deUint32 bufferMode, deUint32 primitiveType)
882	: TestCase				(context, name, desc)
883	, m_bufferMode			(bufferMode)
884	, m_primitiveType		(primitiveType)
885	, m_inputStride			(0)
886	, m_program				(DE_NULL)
887	, m_transformFeedback	(DE_NULL)
888	, m_iterNdx				(0)
889{
890}
891
892TransformFeedbackCase::~TransformFeedbackCase (void)
893{
894	TransformFeedbackCase::deinit();
895}
896
897static bool hasArraysInTFVaryings (const ProgramSpec& spec)
898{
899	for (vector<string>::const_iterator tfVar = spec.getTransformFeedbackVaryings().begin(); tfVar != spec.getTransformFeedbackVaryings().end(); ++tfVar)
900	{
901		string							varName	= glu::parseVariableName(tfVar->c_str());
902		vector<Varying>::const_iterator	varIter	= std::find_if(spec.getVaryings().begin(), spec.getVaryings().end(), VaryingNameEquals(varName));
903
904		if (varName == "gl_Position" || varName == "gl_PointSize")
905			continue;
906
907		DE_ASSERT(varIter != spec.getVaryings().end());
908
909		if (varIter->type.isArrayType())
910			return true;
911	}
912
913	return false;
914}
915
916void TransformFeedbackCase::init (void)
917{
918	TestLog&				log	= m_testCtx.getLog();
919	const glw::Functions&	gl	= m_context.getRenderContext().getFunctions();
920
921	DE_ASSERT(!m_program);
922	m_program = createVertexCaptureProgram(m_context.getRenderContext(), m_progSpec, m_bufferMode, m_primitiveType);
923
924	log << *m_program;
925	if (!m_program->isOk())
926	{
927		const bool linkFail = m_program->getShaderInfo(glu::SHADERTYPE_VERTEX).compileOk &&
928							  m_program->getShaderInfo(glu::SHADERTYPE_FRAGMENT).compileOk &&
929							  !m_program->getProgramInfo().linkOk;
930
931		if (linkFail)
932		{
933			if (!isProgramSupported(gl, m_progSpec, m_bufferMode))
934				throw tcu::NotSupportedError("Implementation limits execeeded", "", __FILE__, __LINE__);
935			else if (hasArraysInTFVaryings(m_progSpec))
936				throw tcu::NotSupportedError("Capturing arrays is not supported (undefined in specification)", "", __FILE__, __LINE__);
937			else
938				throw tcu::TestError("Link failed", "", __FILE__, __LINE__);
939		}
940		else
941			throw tcu::TestError("Compile failed", "", __FILE__, __LINE__);
942	}
943
944	log << TestLog::Message << "Transform feedback varyings: " << tcu::formatArray(m_progSpec.getTransformFeedbackVaryings().begin(), m_progSpec.getTransformFeedbackVaryings().end()) << TestLog::EndMessage;
945
946	// Print out transform feedback points reported by GL.
947	log << TestLog::Message << "Transform feedback varyings reported by compiler:" << TestLog::EndMessage;
948	logTransformFeedbackVaryings(log, gl, m_program->getProgram());
949
950	// Compute input specification.
951	computeInputLayout(m_attributes, m_inputStride, m_progSpec.getVaryings(), m_progSpec.isPointSizeUsed());
952
953	// Build list of varyings used in transform feedback.
954	computeTransformFeedbackOutputs(m_transformFeedbackOutputs, m_attributes, m_progSpec.getVaryings(), m_progSpec.getTransformFeedbackVaryings(), m_bufferMode);
955	DE_ASSERT(!m_transformFeedbackOutputs.empty());
956
957	// Buffer strides.
958	DE_ASSERT(m_bufferStrides.empty());
959	if (m_bufferMode == GL_SEPARATE_ATTRIBS)
960	{
961		for (vector<Output>::const_iterator outIter = m_transformFeedbackOutputs.begin(); outIter != m_transformFeedbackOutputs.end(); outIter++)
962			m_bufferStrides.push_back(outIter->type.getScalarSize()*(int)sizeof(deUint32));
963	}
964	else
965	{
966		int totalSize = 0;
967		for (vector<Output>::const_iterator outIter = m_transformFeedbackOutputs.begin(); outIter != m_transformFeedbackOutputs.end(); outIter++)
968			totalSize += outIter->type.getScalarSize()*(int)sizeof(deUint32);
969
970		m_bufferStrides.push_back(totalSize);
971	}
972
973	// \note Actual storage is allocated in iterate().
974	m_outputBuffers.resize(m_bufferStrides.size());
975	gl.genBuffers((glw::GLsizei)m_outputBuffers.size(), &m_outputBuffers[0]);
976
977	DE_ASSERT(!m_transformFeedback);
978	m_transformFeedback = new glu::TransformFeedback(m_context.getRenderContext());
979
980	GLU_EXPECT_NO_ERROR(gl.getError(), "init");
981
982	m_iterNdx = 0;
983	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
984}
985
986void TransformFeedbackCase::deinit (void)
987{
988	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
989
990	if (!m_outputBuffers.empty())
991	{
992		gl.deleteBuffers((glw::GLsizei)m_outputBuffers.size(), &m_outputBuffers[0]);
993		m_outputBuffers.clear();
994	}
995
996	delete m_transformFeedback;
997	m_transformFeedback = DE_NULL;
998
999	delete m_program;
1000	m_program = DE_NULL;
1001
1002	// Clean up state.
1003	m_attributes.clear();
1004	m_transformFeedbackOutputs.clear();
1005	m_bufferStrides.clear();
1006	m_inputStride = 0;
1007}
1008
1009TransformFeedbackCase::IterateResult TransformFeedbackCase::iterate (void)
1010{
1011	// Test cases.
1012	static const DrawCall s_elemCount1[]	= { DrawCall(1, true) };
1013	static const DrawCall s_elemCount2[]	= { DrawCall(2, true) };
1014	static const DrawCall s_elemCount3[]	= { DrawCall(3, true) };
1015	static const DrawCall s_elemCount4[]	= { DrawCall(4, true) };
1016	static const DrawCall s_elemCount123[]	= { DrawCall(123, true) };
1017	static const DrawCall s_basicPause1[]	= { DrawCall(64, true), DrawCall(64, false), DrawCall(64, true) };
1018	static const DrawCall s_basicPause2[]	= { DrawCall(13, true), DrawCall(5, true), DrawCall(17, false), DrawCall(3, true), DrawCall(7, false) };
1019	static const DrawCall s_startPaused[]	= { DrawCall(123, false), DrawCall(123, true) };
1020	static const DrawCall s_random1[]		= { DrawCall(65, true), DrawCall(135, false), DrawCall(74, true), DrawCall(16, false), DrawCall(226, false), DrawCall(9, true), DrawCall(174, false) };
1021	static const DrawCall s_random2[]		= { DrawCall(217, true), DrawCall(171, true), DrawCall(147, true), DrawCall(152, false), DrawCall(55, true) };
1022
1023	static const struct
1024	{
1025		const DrawCall*		calls;
1026		int					numCalls;
1027	} s_iterations[] =
1028	{
1029#define ITER(ARR) { ARR, DE_LENGTH_OF_ARRAY(ARR) }
1030		ITER(s_elemCount1),
1031		ITER(s_elemCount2),
1032		ITER(s_elemCount3),
1033		ITER(s_elemCount4),
1034		ITER(s_elemCount123),
1035		ITER(s_basicPause1),
1036		ITER(s_basicPause2),
1037		ITER(s_startPaused),
1038		ITER(s_random1),
1039		ITER(s_random2)
1040#undef ITER
1041	};
1042
1043	TestLog&				log				= m_testCtx.getLog();
1044	bool					isOk			= true;
1045	deUint32				seed			= deStringHash(getName()) ^ deInt32Hash(m_iterNdx);
1046	int						numIterations	= DE_LENGTH_OF_ARRAY(s_iterations);
1047	const DrawCall*			first			= s_iterations[m_iterNdx].calls;
1048	const DrawCall*			end				= s_iterations[m_iterNdx].calls + s_iterations[m_iterNdx].numCalls;
1049
1050	std::string				sectionName		= std::string("Iteration") + de::toString(m_iterNdx+1);
1051	std::string				sectionDesc		= std::string("Iteration ") + de::toString(m_iterNdx+1) + " / " + de::toString(numIterations);
1052	tcu::ScopedLogSection	section			(log, sectionName, sectionDesc);
1053
1054	log << TestLog::Message << "Testing " << s_iterations[m_iterNdx].numCalls << " draw calls, (element count, TF state): " << tcu::formatArray(first, end) << TestLog::EndMessage;
1055
1056	isOk = runTest(first, end, seed);
1057
1058	if (!isOk)
1059		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result comparison failed");
1060
1061	m_iterNdx += 1;
1062	return (isOk && m_iterNdx < numIterations) ? CONTINUE : STOP;
1063}
1064
1065bool TransformFeedbackCase::runTest (const DrawCall* first, const DrawCall* end, deUint32 seed)
1066{
1067	TestLog&				log				= m_testCtx.getLog();
1068	const glw::Functions&	gl				= m_context.getRenderContext().getFunctions();
1069	de::Random				rnd				(seed);
1070	int						numInputs		= 0;		//!< Sum of element counts in calls.
1071	int						numOutputs		= 0;		//!< Sum of output counts for calls that have transform feedback enabled.
1072	int						width			= m_context.getRenderContext().getRenderTarget().getWidth();
1073	int						height			= m_context.getRenderContext().getRenderTarget().getHeight();
1074	int						viewportW		= de::min((int)VIEWPORT_WIDTH, width);
1075	int						viewportH		= de::min((int)VIEWPORT_HEIGHT, height);
1076	int						viewportX		= rnd.getInt(0, width-viewportW);
1077	int						viewportY		= rnd.getInt(0, height-viewportH);
1078	tcu::Surface			frameWithTf		(viewportW, viewportH);
1079	tcu::Surface			frameWithoutTf	(viewportW, viewportH);
1080	glu::Query				primitiveQuery	(m_context.getRenderContext());
1081	bool					outputsOk		= true;
1082	bool					imagesOk		= true;
1083	bool					queryOk			= true;
1084
1085	// Compute totals.
1086	for (const DrawCall* call = first; call != end; call++)
1087	{
1088		numInputs	+= call->numElements;
1089		numOutputs	+= call->transformFeedbackEnabled ? getTransformFeedbackOutputCount(m_primitiveType, call->numElements) : 0;
1090	}
1091
1092	// Input data.
1093	vector<deUint8> inputData(m_inputStride*numInputs);
1094	genInputData(m_attributes, numInputs, m_inputStride, &inputData[0], rnd);
1095
1096	gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_transformFeedback->get());
1097	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTransformFeedback()");
1098
1099	// Allocate storage for transform feedback output buffers and bind to targets.
1100	for (int bufNdx = 0; bufNdx < (int)m_outputBuffers.size(); bufNdx++)
1101	{
1102		deUint32		buffer		= m_outputBuffers[bufNdx];
1103		int				stride		= m_bufferStrides[bufNdx];
1104		int				target		= bufNdx;
1105		int				size		= stride*numOutputs;
1106		int				guardSize	= stride*BUFFER_GUARD_MULTIPLIER;
1107		const deUint32	usage		= GL_DYNAMIC_READ;
1108
1109		gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buffer);
1110		gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, size+guardSize, DE_NULL, usage);
1111		writeBufferGuard(gl, GL_TRANSFORM_FEEDBACK_BUFFER, size, guardSize);
1112
1113		// \todo [2012-07-30 pyry] glBindBufferRange()?
1114		gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, target, buffer);
1115
1116		GLU_EXPECT_NO_ERROR(gl.getError(), "transform feedback buffer setup");
1117	}
1118
1119	// Setup attributes.
1120	for (vector<Attribute>::const_iterator attrib = m_attributes.begin(); attrib != m_attributes.end(); attrib++)
1121	{
1122		int				loc				= gl.getAttribLocation(m_program->getProgram(), attrib->name.c_str());
1123		glu::DataType	scalarType		= glu::getDataTypeScalarType(attrib->type.getBasicType());
1124		int				numComponents	= glu::getDataTypeScalarSize(attrib->type.getBasicType());
1125		const void*		ptr				= &inputData[0] + attrib->offset;
1126
1127		if (loc >= 0)
1128		{
1129			gl.enableVertexAttribArray(loc);
1130
1131			if (scalarType == glu::TYPE_FLOAT)		gl.vertexAttribPointer	(loc, numComponents, GL_FLOAT, GL_FALSE, m_inputStride, ptr);
1132			else if (scalarType == glu::TYPE_INT)	gl.vertexAttribIPointer	(loc, numComponents, GL_INT, m_inputStride, ptr);
1133			else if (scalarType == glu::TYPE_UINT)	gl.vertexAttribIPointer	(loc, numComponents, GL_UNSIGNED_INT, m_inputStride, ptr);
1134		}
1135	}
1136
1137	// Setup viewport.
1138	gl.viewport(viewportX, viewportY, viewportW, viewportH);
1139
1140	// Setup program.
1141	gl.useProgram(m_program->getProgram());
1142
1143	gl.uniform4fv(gl.getUniformLocation(m_program->getProgram(), "u_scale"),	1, tcu::Vec4(0.01f).getPtr());
1144	gl.uniform4fv(gl.getUniformLocation(m_program->getProgram(), "u_bias"),		1, tcu::Vec4(0.5f).getPtr());
1145
1146	// Enable query.
1147	gl.beginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, *primitiveQuery);
1148	GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)");
1149
1150	// Draw.
1151	{
1152		int		offset		= 0;
1153		bool	tfEnabled	= true;
1154
1155		gl.clear(GL_COLOR_BUFFER_BIT);
1156
1157		gl.beginTransformFeedback(getTransformFeedbackPrimitiveMode(m_primitiveType));
1158
1159		for (const DrawCall* call = first; call != end; call++)
1160		{
1161			// Pause or resume transform feedback if necessary.
1162			if (call->transformFeedbackEnabled != tfEnabled)
1163			{
1164				if (call->transformFeedbackEnabled)
1165					gl.resumeTransformFeedback();
1166				else
1167					gl.pauseTransformFeedback();
1168				tfEnabled = call->transformFeedbackEnabled;
1169			}
1170
1171			gl.drawArrays(m_primitiveType, offset, call->numElements);
1172			offset += call->numElements;
1173		}
1174
1175		// Resume feedback before finishing it.
1176		if (!tfEnabled)
1177			gl.resumeTransformFeedback();
1178
1179		gl.endTransformFeedback();
1180		GLU_EXPECT_NO_ERROR(gl.getError(), "render");
1181	}
1182
1183	gl.endQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
1184	GLU_EXPECT_NO_ERROR(gl.getError(), "glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)");
1185
1186	// Check and log query status right after submit
1187	{
1188		deUint32 available = GL_FALSE;
1189		gl.getQueryObjectuiv(*primitiveQuery, GL_QUERY_RESULT_AVAILABLE, &available);
1190		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetQueryObjectuiv()");
1191
1192		log << TestLog::Message << "GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN status after submit: " << (available != GL_FALSE ? "GL_TRUE" : "GL_FALSE") << TestLog::EndMessage;
1193	}
1194
1195	// Compare result buffers.
1196	for (int bufferNdx = 0; bufferNdx < (int)m_outputBuffers.size(); bufferNdx++)
1197	{
1198		deUint32		buffer		= m_outputBuffers[bufferNdx];
1199		int				stride		= m_bufferStrides[bufferNdx];
1200		int				size		= stride*numOutputs;
1201		int				guardSize	= stride*BUFFER_GUARD_MULTIPLIER;
1202		const void*		bufPtr		= DE_NULL;
1203
1204		// Bind buffer for reading.
1205		gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buffer);
1206		bufPtr = gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, size+guardSize, GL_MAP_READ_BIT);
1207		GLU_EXPECT_NO_ERROR(gl.getError(), "mapping buffer");
1208
1209		// Verify all output variables that are written to this buffer.
1210		for (vector<Output>::const_iterator out = m_transformFeedbackOutputs.begin(); out != m_transformFeedbackOutputs.end(); out++)
1211		{
1212			if (out->bufferNdx != bufferNdx)
1213				continue;
1214
1215			int inputOffset		= 0;
1216			int	outputOffset	= 0;
1217
1218			// Process all draw calls and check ones with transform feedback enabled.
1219			for (const DrawCall* call = first; call != end; call++)
1220			{
1221				if (call->transformFeedbackEnabled)
1222				{
1223					const deUint8*	inputPtr	= &inputData[0] + inputOffset*m_inputStride;
1224					const deUint8*	outputPtr	= (const deUint8*)bufPtr + outputOffset*stride;
1225
1226					if (!compareTransformFeedbackOutput(log, m_primitiveType, *out, call->numElements, inputPtr, m_inputStride, outputPtr, stride))
1227					{
1228						outputsOk = false;
1229						break;
1230					}
1231				}
1232
1233				inputOffset		+= call->numElements;
1234				outputOffset	+= call->transformFeedbackEnabled ? getTransformFeedbackOutputCount(m_primitiveType, call->numElements) : 0;
1235			}
1236		}
1237
1238		// Verify guardband.
1239		if (!verifyGuard((const deUint8*)bufPtr + size, guardSize))
1240		{
1241			log << TestLog::Message << "Error: Transform feedback buffer overrun detected" << TestLog::EndMessage;
1242			outputsOk = false;
1243		}
1244
1245		gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
1246	}
1247
1248	// Check status after mapping buffers.
1249	{
1250		const bool	mustBeReady		= !m_outputBuffers.empty(); // Mapping buffer forces synchronization.
1251		const int	expectedCount	= computeTransformFeedbackPrimitiveCount(m_primitiveType, first, end);
1252		deUint32	available		= GL_FALSE;
1253		deUint32	numPrimitives	= 0;
1254
1255		gl.getQueryObjectuiv(*primitiveQuery, GL_QUERY_RESULT_AVAILABLE, &available);
1256		gl.getQueryObjectuiv(*primitiveQuery, GL_QUERY_RESULT, &numPrimitives);
1257		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetQueryObjectuiv()");
1258
1259		if (!mustBeReady && available == GL_FALSE)
1260		{
1261			log << TestLog::Message << "ERROR: GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN result not available after mapping buffers!" << TestLog::EndMessage;
1262			queryOk = false;
1263		}
1264
1265		log << TestLog::Message << "GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN = " << numPrimitives << TestLog::EndMessage;
1266
1267		if ((int)numPrimitives != expectedCount)
1268		{
1269			log << TestLog::Message << "ERROR: Expected " << expectedCount << " primitives!" << TestLog::EndMessage;
1270			queryOk = false;
1271		}
1272	}
1273
1274	// Clear transform feedback state.
1275	gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
1276	for (int bufNdx = 0; bufNdx < (int)m_outputBuffers.size(); bufNdx++)
1277	{
1278		gl.bindBuffer		(GL_TRANSFORM_FEEDBACK_BUFFER, 0);
1279		gl.bindBufferBase	(GL_TRANSFORM_FEEDBACK_BUFFER, bufNdx, 0);
1280	}
1281
1282	// Read back rendered image.
1283	glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, frameWithTf.getAccess());
1284
1285	// Render without transform feedback.
1286	{
1287		int offset = 0;
1288
1289		gl.clear(GL_COLOR_BUFFER_BIT);
1290
1291		for (const DrawCall* call = first; call != end; call++)
1292		{
1293			gl.drawArrays(m_primitiveType, offset, call->numElements);
1294			offset += call->numElements;
1295		}
1296
1297		GLU_EXPECT_NO_ERROR(gl.getError(), "render");
1298		glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, frameWithoutTf.getAccess());
1299	}
1300
1301	// Compare images with and without transform feedback.
1302	imagesOk = tcu::pixelThresholdCompare(log, "Result", "Image comparison result", frameWithoutTf, frameWithTf, tcu::RGBA(1, 1, 1, 1), tcu::COMPARE_LOG_ON_ERROR);
1303
1304	if (imagesOk)
1305		m_testCtx.getLog() << TestLog::Message << "Rendering result comparison between TF enabled and TF disabled passed." << TestLog::EndMessage;
1306	else
1307		m_testCtx.getLog() << TestLog::Message << "ERROR: Rendering result comparison between TF enabled and TF disabled failed!" << TestLog::EndMessage;
1308
1309	return outputsOk && imagesOk && queryOk;
1310}
1311
1312// Test cases.
1313
1314class PositionCase : public TransformFeedbackCase
1315{
1316public:
1317	PositionCase (Context& context, const char* name, const char* desc, deUint32 bufferType, deUint32 primitiveType)
1318		: TransformFeedbackCase(context, name, desc, bufferType, primitiveType)
1319	{
1320		m_progSpec.addTransformFeedbackVarying("gl_Position");
1321	}
1322};
1323
1324class PointSizeCase : public TransformFeedbackCase
1325{
1326public:
1327	PointSizeCase (Context& context, const char* name, const char* desc, deUint32 bufferType, deUint32 primitiveType)
1328		: TransformFeedbackCase(context, name, desc, bufferType, primitiveType)
1329	{
1330		m_progSpec.addTransformFeedbackVarying("gl_PointSize");
1331	}
1332};
1333
1334class BasicTypeCase : public TransformFeedbackCase
1335{
1336public:
1337	BasicTypeCase (Context& context, const char* name, const char* desc, deUint32 bufferType, deUint32 primitiveType, glu::DataType type, glu::Precision precision, Interpolation interpolation)
1338		: TransformFeedbackCase(context, name, desc, bufferType, primitiveType)
1339	{
1340		m_progSpec.addVarying("v_varA", glu::VarType(type, precision), interpolation);
1341		m_progSpec.addVarying("v_varB", glu::VarType(type, precision), interpolation);
1342
1343		m_progSpec.addTransformFeedbackVarying("v_varA");
1344		m_progSpec.addTransformFeedbackVarying("v_varB");
1345	}
1346};
1347
1348class BasicArrayCase : public TransformFeedbackCase
1349{
1350public:
1351	BasicArrayCase (Context& context, const char* name, const char* desc, deUint32 bufferType, deUint32 primitiveType, glu::DataType type, glu::Precision precision, Interpolation interpolation)
1352		: TransformFeedbackCase(context, name, desc, bufferType, primitiveType)
1353	{
1354		if (glu::isDataTypeMatrix(type) || m_bufferMode == GL_SEPARATE_ATTRIBS)
1355		{
1356			// \note For matrix types we need to use reduced array sizes or otherwise we will exceed maximum attribute (16)
1357			//		 or transform feedback component count (64).
1358			//		 On separate attribs mode maximum component count per varying is 4.
1359			m_progSpec.addVarying("v_varA", glu::VarType(glu::VarType(type, precision), 1), interpolation);
1360			m_progSpec.addVarying("v_varB", glu::VarType(glu::VarType(type, precision), 2), interpolation);
1361		}
1362		else
1363		{
1364			m_progSpec.addVarying("v_varA", glu::VarType(glu::VarType(type, precision), 3), interpolation);
1365			m_progSpec.addVarying("v_varB", glu::VarType(glu::VarType(type, precision), 4), interpolation);
1366		}
1367
1368		m_progSpec.addTransformFeedbackVarying("v_varA");
1369		m_progSpec.addTransformFeedbackVarying("v_varB");
1370	}
1371};
1372
1373class ArrayElementCase : public TransformFeedbackCase
1374{
1375public:
1376	ArrayElementCase (Context& context, const char* name, const char* desc, deUint32 bufferType, deUint32 primitiveType, glu::DataType type, glu::Precision precision, Interpolation interpolation)
1377		: TransformFeedbackCase(context, name, desc, bufferType, primitiveType)
1378	{
1379		m_progSpec.addVarying("v_varA", glu::VarType(glu::VarType(type, precision), 3), interpolation);
1380		m_progSpec.addVarying("v_varB", glu::VarType(glu::VarType(type, precision), 4), interpolation);
1381
1382		m_progSpec.addTransformFeedbackVarying("v_varA[1]");
1383		m_progSpec.addTransformFeedbackVarying("v_varB[0]");
1384		m_progSpec.addTransformFeedbackVarying("v_varB[3]");
1385	}
1386};
1387
1388class RandomCase : public TransformFeedbackCase
1389{
1390public:
1391	RandomCase (Context& context, const char* name, const char* desc, deUint32 bufferType, deUint32 primitiveType, deUint32 seed)
1392		: TransformFeedbackCase	(context, name, desc, bufferType, primitiveType)
1393		, m_seed				(seed)
1394	{
1395	}
1396
1397	void init (void)
1398	{
1399		// \note Hard-coded indices and hackery are used when indexing this, beware.
1400		static const glu::DataType typeCandidates[] =
1401		{
1402			glu::TYPE_FLOAT,
1403			glu::TYPE_FLOAT_VEC2,
1404			glu::TYPE_FLOAT_VEC3,
1405			glu::TYPE_FLOAT_VEC4,
1406			glu::TYPE_INT,
1407			glu::TYPE_INT_VEC2,
1408			glu::TYPE_INT_VEC3,
1409			glu::TYPE_INT_VEC4,
1410			glu::TYPE_UINT,
1411			glu::TYPE_UINT_VEC2,
1412			glu::TYPE_UINT_VEC3,
1413			glu::TYPE_UINT_VEC4,
1414
1415			glu::TYPE_FLOAT_MAT2,
1416			glu::TYPE_FLOAT_MAT2X3,
1417			glu::TYPE_FLOAT_MAT2X4,
1418
1419			glu::TYPE_FLOAT_MAT3X2,
1420			glu::TYPE_FLOAT_MAT3,
1421			glu::TYPE_FLOAT_MAT3X4,
1422
1423			glu::TYPE_FLOAT_MAT4X2,
1424			glu::TYPE_FLOAT_MAT4X3,
1425			glu::TYPE_FLOAT_MAT4
1426		};
1427
1428		static const glu::Precision precisions[] =
1429		{
1430			glu::PRECISION_LOWP,
1431			glu::PRECISION_MEDIUMP,
1432			glu::PRECISION_HIGHP
1433		};
1434
1435		static const Interpolation interpModes[] =
1436		{
1437			INTERPOLATION_FLAT,
1438			INTERPOLATION_SMOOTH,
1439			INTERPOLATION_CENTROID
1440		};
1441
1442		const int	maxAttributeVectors					= 16;
1443//		const int	maxTransformFeedbackComponents		= 64; // \note It is enough to limit attribute set size.
1444		bool		isSeparateMode						= m_bufferMode == GL_SEPARATE_ATTRIBS;
1445		int			maxTransformFeedbackVars			= isSeparateMode ? 4 : maxAttributeVectors;
1446		const float	arrayWeight							= 0.3f;
1447		const float	positionWeight						= 0.7f;
1448		const float	pointSizeWeight						= 0.1f;
1449		const float	captureFullArrayWeight				= 0.5f;
1450
1451		de::Random	rnd									(m_seed);
1452		bool		usePosition							= rnd.getFloat() < positionWeight;
1453		bool		usePointSize						= rnd.getFloat() < pointSizeWeight;
1454		int			numAttribVectorsToUse				= rnd.getInt(1, maxAttributeVectors - 1/*position*/ - (usePointSize ? 1 : 0));
1455
1456		int			numAttributeVectors					= 0;
1457		int			varNdx								= 0;
1458
1459		// Generate varyings.
1460		while (numAttributeVectors < numAttribVectorsToUse)
1461		{
1462			int						maxVecs		= isSeparateMode ? de::min(2 /*at most 2*mat2*/, numAttribVectorsToUse-numAttributeVectors) : numAttribVectorsToUse-numAttributeVectors;
1463			const glu::DataType*	begin		= &typeCandidates[0];
1464			const glu::DataType*	end			= begin + (maxVecs >= 4 ? 21 :
1465														   maxVecs >= 3 ? 18 :
1466														   maxVecs >= 2 ? (isSeparateMode ? 13 : 15) : 12);
1467
1468			glu::DataType			type		= rnd.choose<glu::DataType>(begin, end);
1469			glu::Precision			precision	= rnd.choose<glu::Precision>(&precisions[0], &precisions[0]+DE_LENGTH_OF_ARRAY(precisions));
1470			Interpolation			interp		= glu::getDataTypeScalarType(type) == glu::TYPE_FLOAT
1471												? rnd.choose<Interpolation>(&interpModes[0], &interpModes[0]+DE_LENGTH_OF_ARRAY(interpModes))
1472												: INTERPOLATION_FLAT;
1473			int						numVecs		= glu::isDataTypeMatrix(type) ? glu::getDataTypeMatrixNumColumns(type) : 1;
1474			int						numComps	= glu::getDataTypeScalarSize(type);
1475			int						maxArrayLen	= de::max(1, isSeparateMode ? 4/numComps : maxVecs/numVecs);
1476			bool					useArray	= rnd.getFloat() < arrayWeight;
1477			int						arrayLen	= useArray ? rnd.getInt(1, maxArrayLen) : 1;
1478			std::string				name		= "v_var" + de::toString(varNdx);
1479
1480			if (useArray)
1481				m_progSpec.addVarying(name.c_str(), glu::VarType(glu::VarType(type, precision), arrayLen), interp);
1482			else
1483				m_progSpec.addVarying(name.c_str(), glu::VarType(type, precision), interp);
1484
1485			numAttributeVectors	+= arrayLen*numVecs;
1486			varNdx				+= 1;
1487		}
1488
1489		// Generate transform feedback candidate set.
1490		vector<string> tfCandidates;
1491
1492		if (usePosition)	tfCandidates.push_back("gl_Position");
1493		if (usePointSize)	tfCandidates.push_back("gl_PointSize");
1494
1495		for (int ndx = 0; ndx < varNdx /* num varyings */; ndx++)
1496		{
1497			const Varying& var = m_progSpec.getVaryings()[ndx];
1498
1499			if (var.type.isArrayType())
1500			{
1501				const bool captureFull = rnd.getFloat() < captureFullArrayWeight;
1502
1503				if (captureFull)
1504					tfCandidates.push_back(var.name);
1505				else
1506				{
1507					const int numElem = var.type.getArraySize();
1508					for (int elemNdx = 0; elemNdx < numElem; elemNdx++)
1509						tfCandidates.push_back(var.name + "[" + de::toString(elemNdx) + "]");
1510				}
1511			}
1512			else
1513				tfCandidates.push_back(var.name);
1514		}
1515
1516		// Pick random selection.
1517		vector<string> tfVaryings(de::min((int)tfCandidates.size(), maxTransformFeedbackVars));
1518		rnd.choose(tfCandidates.begin(), tfCandidates.end(), tfVaryings.begin(), (int)tfVaryings.size());
1519		rnd.shuffle(tfVaryings.begin(), tfVaryings.end());
1520
1521		for (vector<string>::const_iterator var = tfVaryings.begin(); var != tfVaryings.end(); var++)
1522			m_progSpec.addTransformFeedbackVarying(var->c_str());
1523
1524		TransformFeedbackCase::init();
1525	}
1526
1527private:
1528	deUint32 m_seed;
1529};
1530
1531} // TransformFeedback
1532
1533using namespace TransformFeedback;
1534
1535TransformFeedbackTests::TransformFeedbackTests (Context& context)
1536	: TestCaseGroup(context, "transform_feedback", "Transform feedback tests")
1537{
1538}
1539
1540TransformFeedbackTests::~TransformFeedbackTests (void)
1541{
1542}
1543
1544void TransformFeedbackTests::init (void)
1545{
1546	static const struct
1547	{
1548		const char*		name;
1549		deUint32		mode;
1550	} bufferModes[] =
1551	{
1552		{ "separate",		GL_SEPARATE_ATTRIBS		},
1553		{ "interleaved",	GL_INTERLEAVED_ATTRIBS	}
1554	};
1555
1556	static const struct
1557	{
1558		const char*		name;
1559		deUint32		type;
1560	} primitiveTypes[] =
1561	{
1562		{ "points",			GL_POINTS			},
1563		{ "lines",			GL_LINES			},
1564		{ "triangles",		GL_TRIANGLES		}
1565
1566		// Not supported by GLES3.
1567//		{ "line_strip",		GL_LINE_STRIP		},
1568//		{ "line_loop",		GL_LINE_LOOP		},
1569//		{ "triangle_fan",	GL_TRIANGLE_FAN		},
1570//		{ "triangle_strip",	GL_TRIANGLE_STRIP	}
1571	};
1572
1573	static const glu::DataType basicTypes[] =
1574	{
1575		glu::TYPE_FLOAT,
1576		glu::TYPE_FLOAT_VEC2,
1577		glu::TYPE_FLOAT_VEC3,
1578		glu::TYPE_FLOAT_VEC4,
1579		glu::TYPE_FLOAT_MAT2,
1580		glu::TYPE_FLOAT_MAT2X3,
1581		glu::TYPE_FLOAT_MAT2X4,
1582		glu::TYPE_FLOAT_MAT3X2,
1583		glu::TYPE_FLOAT_MAT3,
1584		glu::TYPE_FLOAT_MAT3X4,
1585		glu::TYPE_FLOAT_MAT4X2,
1586		glu::TYPE_FLOAT_MAT4X3,
1587		glu::TYPE_FLOAT_MAT4,
1588		glu::TYPE_INT,
1589		glu::TYPE_INT_VEC2,
1590		glu::TYPE_INT_VEC3,
1591		glu::TYPE_INT_VEC4,
1592		glu::TYPE_UINT,
1593		glu::TYPE_UINT_VEC2,
1594		glu::TYPE_UINT_VEC3,
1595		glu::TYPE_UINT_VEC4
1596	};
1597
1598	static const glu::Precision precisions[] =
1599	{
1600		glu::PRECISION_LOWP,
1601		glu::PRECISION_MEDIUMP,
1602		glu::PRECISION_HIGHP
1603	};
1604
1605	static const struct
1606	{
1607		const char*		name;
1608		Interpolation	interp;
1609	} interpModes[] =
1610	{
1611		{ "smooth",		INTERPOLATION_SMOOTH	},
1612		{ "flat",		INTERPOLATION_FLAT		},
1613		{ "centroid",	INTERPOLATION_CENTROID	}
1614	};
1615
1616	// .position
1617	{
1618		tcu::TestCaseGroup* positionGroup = new tcu::TestCaseGroup(m_testCtx, "position", "gl_Position capture using transform feedback");
1619		addChild(positionGroup);
1620
1621		for (int primitiveType = 0; primitiveType < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveType++)
1622		{
1623			for (int bufferMode = 0; bufferMode < DE_LENGTH_OF_ARRAY(bufferModes); bufferMode++)
1624			{
1625				string name = string(primitiveTypes[primitiveType].name) + "_" + bufferModes[bufferMode].name;
1626				positionGroup->addChild(new PositionCase(m_context, name.c_str(), "", bufferModes[bufferMode].mode, primitiveTypes[primitiveType].type));
1627			}
1628		}
1629	}
1630
1631	// .point_size
1632	{
1633		tcu::TestCaseGroup* pointSizeGroup = new tcu::TestCaseGroup(m_testCtx, "point_size", "gl_PointSize capture using transform feedback");
1634		addChild(pointSizeGroup);
1635
1636		for (int primitiveType = 0; primitiveType < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveType++)
1637		{
1638			for (int bufferMode = 0; bufferMode < DE_LENGTH_OF_ARRAY(bufferModes); bufferMode++)
1639			{
1640				string name = string(primitiveTypes[primitiveType].name) + "_" + bufferModes[bufferMode].name;
1641				pointSizeGroup->addChild(new PointSizeCase(m_context, name.c_str(), "", bufferModes[bufferMode].mode, primitiveTypes[primitiveType].type));
1642			}
1643		}
1644	}
1645
1646	// .basic_type
1647	{
1648		tcu::TestCaseGroup* basicTypeGroup = new tcu::TestCaseGroup(m_testCtx, "basic_types", "Basic types in transform feedback");
1649		addChild(basicTypeGroup);
1650
1651		for (int bufferModeNdx = 0; bufferModeNdx < DE_LENGTH_OF_ARRAY(bufferModes); bufferModeNdx++)
1652		{
1653			tcu::TestCaseGroup* modeGroup	= new tcu::TestCaseGroup(m_testCtx, bufferModes[bufferModeNdx].name, "");
1654			deUint32			bufferMode	= bufferModes[bufferModeNdx].mode;
1655			basicTypeGroup->addChild(modeGroup);
1656
1657			for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveTypeNdx++)
1658			{
1659				tcu::TestCaseGroup* primitiveGroup	= new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, "");
1660				deUint32			primitiveType	= primitiveTypes[primitiveTypeNdx].type;
1661				modeGroup->addChild(primitiveGroup);
1662
1663				for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(basicTypes); typeNdx++)
1664				{
1665					glu::DataType		type		= basicTypes[typeNdx];
1666					bool				isFloat		= glu::getDataTypeScalarType(type) == glu::TYPE_FLOAT;
1667
1668					for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
1669					{
1670						glu::Precision precision = precisions[precNdx];
1671
1672						string name = string(glu::getPrecisionName(precision)) + "_" + glu::getDataTypeName(type);
1673						primitiveGroup->addChild(new BasicTypeCase(m_context, name.c_str(), "", bufferMode, primitiveType, type, precision, isFloat ? INTERPOLATION_SMOOTH : INTERPOLATION_FLAT));
1674					}
1675				}
1676			}
1677		}
1678	}
1679
1680	// .array
1681	{
1682		tcu::TestCaseGroup* arrayGroup = new tcu::TestCaseGroup(m_testCtx, "array", "Capturing whole array in TF");
1683		addChild(arrayGroup);
1684
1685		for (int bufferModeNdx = 0; bufferModeNdx < DE_LENGTH_OF_ARRAY(bufferModes); bufferModeNdx++)
1686		{
1687			tcu::TestCaseGroup* modeGroup	= new tcu::TestCaseGroup(m_testCtx, bufferModes[bufferModeNdx].name, "");
1688			deUint32			bufferMode	= bufferModes[bufferModeNdx].mode;
1689			arrayGroup->addChild(modeGroup);
1690
1691			for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveTypeNdx++)
1692			{
1693				tcu::TestCaseGroup* primitiveGroup	= new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, "");
1694				deUint32			primitiveType	= primitiveTypes[primitiveTypeNdx].type;
1695				modeGroup->addChild(primitiveGroup);
1696
1697				for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(basicTypes); typeNdx++)
1698				{
1699					glu::DataType		type		= basicTypes[typeNdx];
1700					bool				isFloat		= glu::getDataTypeScalarType(type) == glu::TYPE_FLOAT;
1701
1702					for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
1703					{
1704						glu::Precision precision = precisions[precNdx];
1705
1706						string name = string(glu::getPrecisionName(precision)) + "_" + glu::getDataTypeName(type);
1707						primitiveGroup->addChild(new BasicArrayCase(m_context, name.c_str(), "", bufferMode, primitiveType, type, precision, isFloat ? INTERPOLATION_SMOOTH : INTERPOLATION_FLAT));
1708					}
1709				}
1710			}
1711		}
1712	}
1713
1714	// .array_element
1715	{
1716		tcu::TestCaseGroup* arrayElemGroup = new tcu::TestCaseGroup(m_testCtx, "array_element", "Capturing single array element in TF");
1717		addChild(arrayElemGroup);
1718
1719		for (int bufferModeNdx = 0; bufferModeNdx < DE_LENGTH_OF_ARRAY(bufferModes); bufferModeNdx++)
1720		{
1721			tcu::TestCaseGroup* modeGroup	= new tcu::TestCaseGroup(m_testCtx, bufferModes[bufferModeNdx].name, "");
1722			deUint32			bufferMode	= bufferModes[bufferModeNdx].mode;
1723			arrayElemGroup->addChild(modeGroup);
1724
1725			for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveTypeNdx++)
1726			{
1727				tcu::TestCaseGroup* primitiveGroup	= new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, "");
1728				deUint32			primitiveType	= primitiveTypes[primitiveTypeNdx].type;
1729				modeGroup->addChild(primitiveGroup);
1730
1731				for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(basicTypes); typeNdx++)
1732				{
1733					glu::DataType		type		= basicTypes[typeNdx];
1734					bool				isFloat		= glu::getDataTypeScalarType(type) == glu::TYPE_FLOAT;
1735
1736					for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
1737					{
1738						glu::Precision precision = precisions[precNdx];
1739
1740						string name = string(glu::getPrecisionName(precision)) + "_" + glu::getDataTypeName(type);
1741						primitiveGroup->addChild(new ArrayElementCase(m_context, name.c_str(), "", bufferMode, primitiveType, type, precision, isFloat ? INTERPOLATION_SMOOTH : INTERPOLATION_FLAT));
1742					}
1743				}
1744			}
1745		}
1746	}
1747
1748	// .interpolation
1749	{
1750		tcu::TestCaseGroup* interpolationGroup = new tcu::TestCaseGroup(m_testCtx, "interpolation", "Different interpolation modes in transform feedback varyings");
1751		addChild(interpolationGroup);
1752
1753		for (int modeNdx = 0; modeNdx < DE_LENGTH_OF_ARRAY(interpModes); modeNdx++)
1754		{
1755			Interpolation		interp		= interpModes[modeNdx].interp;
1756			tcu::TestCaseGroup*	modeGroup	= new tcu::TestCaseGroup(m_testCtx, interpModes[modeNdx].name, "");
1757
1758			interpolationGroup->addChild(modeGroup);
1759
1760			for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
1761			{
1762				glu::Precision precision = precisions[precNdx];
1763
1764				for (int primitiveType = 0; primitiveType < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveType++)
1765				{
1766					for (int bufferMode = 0; bufferMode < DE_LENGTH_OF_ARRAY(bufferModes); bufferMode++)
1767					{
1768						string name = string(glu::getPrecisionName(precision)) + "_vec4_" + primitiveTypes[primitiveType].name + "_" + bufferModes[bufferMode].name;
1769						modeGroup->addChild(new BasicTypeCase(m_context, name.c_str(), "", bufferModes[bufferMode].mode, primitiveTypes[primitiveType].type, glu::TYPE_FLOAT_VEC4, precision, interp));
1770					}
1771				}
1772			}
1773		}
1774	}
1775
1776	// .random
1777	{
1778		tcu::TestCaseGroup* randomGroup = new tcu::TestCaseGroup(m_testCtx, "random", "Randomized transform feedback cases");
1779		addChild(randomGroup);
1780
1781		for (int bufferModeNdx = 0; bufferModeNdx < DE_LENGTH_OF_ARRAY(bufferModes); bufferModeNdx++)
1782		{
1783			tcu::TestCaseGroup* modeGroup	= new tcu::TestCaseGroup(m_testCtx, bufferModes[bufferModeNdx].name, "");
1784			deUint32			bufferMode	= bufferModes[bufferModeNdx].mode;
1785			randomGroup->addChild(modeGroup);
1786
1787			for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveTypeNdx++)
1788			{
1789				tcu::TestCaseGroup* primitiveGroup	= new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, "");
1790				deUint32			primitiveType	= primitiveTypes[primitiveTypeNdx].type;
1791				modeGroup->addChild(primitiveGroup);
1792
1793				for (int ndx = 0; ndx < 10; ndx++)
1794				{
1795					deUint32 seed = deInt32Hash(bufferMode) ^ deInt32Hash(primitiveType) ^ deInt32Hash(ndx);
1796					primitiveGroup->addChild(new RandomCase(m_context, de::toString(ndx+1).c_str(), "", bufferMode, primitiveType, seed));
1797				}
1798			}
1799		}
1800	}
1801}
1802
1803} // Functional
1804} // gles3
1805} // deqp
1806