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 
49 using std::string;
50 using std::vector;
51 using std::set;
52 
53 using std::map;
54 using std::set;
55 
56 using tcu::TestLog;
57 
58 namespace deqp
59 {
60 namespace gles3
61 {
62 namespace Functional
63 {
64 namespace TransformFeedback
65 {
66 
67 enum
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 
74 enum Interpolation
75 {
76 	INTERPOLATION_SMOOTH = 0,
77 	INTERPOLATION_FLAT,
78 	INTERPOLATION_CENTROID,
79 
80 	INTERPOLATION_LAST
81 };
82 
getInterpolationName(Interpolation interp)83 static 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 
96 struct Varying
97 {
Varyingdeqp::gles3::Functional::TransformFeedback::Varying98 						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 
110 struct VaryingNameEquals
111 {
VaryingNameEqualsdeqp::gles3::Functional::TransformFeedback::VaryingNameEquals112 					VaryingNameEquals	(const std::string& name_) : name(name_) {}
operator ()deqp::gles3::Functional::TransformFeedback::VaryingNameEquals113 	bool			operator()			(const Varying& var) const { return var.name == name; }
114 
115 	std::string		name;
116 };
117 
118 struct Attribute
119 {
Attributedeqp::gles3::Functional::TransformFeedback::Attribute120 	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 
132 struct AttributeNameEquals
133 {
AttributeNameEqualsdeqp::gles3::Functional::TransformFeedback::AttributeNameEquals134 					AttributeNameEquals	(const std::string& name_) : name(name_) {}
operator ()deqp::gles3::Functional::TransformFeedback::AttributeNameEquals135 	bool			operator()			(const Attribute& attr) const { return attr.name == name; }
136 
137 	std::string		name;
138 };
139 
140 struct Output
141 {
Outputdeqp::gles3::Functional::TransformFeedback::Output142 	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 
155 struct DrawCall
156 {
DrawCalldeqp::gles3::Functional::TransformFeedback::DrawCall157 				DrawCall (int numElements_, bool tfEnabled_)
158 					: numElements				(numElements_)
159 					, transformFeedbackEnabled	(tfEnabled_)
160 				{
161 				}
162 
DrawCalldeqp::gles3::Functional::TransformFeedback::DrawCall163 				DrawCall (void)
164 					: numElements				(0)
165 					, transformFeedbackEnabled	(false)
166 				{
167 				}
168 
169 	int			numElements;
170 	bool		transformFeedbackEnabled;
171 };
172 
operator <<(std::ostream & str,const DrawCall & call)173 std::ostream& operator<< (std::ostream& str, const DrawCall& call)
174 {
175 	return str << "(" << call.numElements << ", " << (call.transformFeedbackEnabled ? "resumed" : "paused") << ")";
176 }
177 
178 class ProgramSpec
179 {
180 public:
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 
getStructs(void) const188 	const vector<glu::StructType*>&	getStructs						(void) const { return m_structs;	}
getVaryings(void) const189 	const vector<Varying>&			getVaryings						(void) const { return m_varyings;	}
getTransformFeedbackVaryings(void) const190 	const vector<string>&			getTransformFeedbackVaryings	(void) const { return m_transformFeedbackVaryings; }
191 	bool							isPointSizeUsed					(void) const;
192 
193 private:
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 
ProgramSpec(void)204 ProgramSpec::ProgramSpec (void)
205 {
206 }
207 
~ProgramSpec(void)208 ProgramSpec::~ProgramSpec (void)
209 {
210 	for (vector<glu::StructType*>::iterator i = m_structs.begin(); i != m_structs.end(); i++)
211 		delete *i;
212 }
213 
createStruct(const char * name)214 glu::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 
addVarying(const char * name,const glu::VarType & type,Interpolation interp)221 void ProgramSpec::addVarying (const char* name, const glu::VarType& type, Interpolation interp)
222 {
223 	m_varyings.push_back(Varying(name, type, interp));
224 }
225 
addTransformFeedbackVarying(const char * name)226 void ProgramSpec::addTransformFeedbackVarying (const char* name)
227 {
228 	m_transformFeedbackVaryings.push_back(name);
229 }
230 
isPointSizeUsed(void) const231 bool ProgramSpec::isPointSizeUsed (void) const
232 {
233 	return std::find(m_transformFeedbackVaryings.begin(), m_transformFeedbackVaryings.end(), "gl_PointSize") != m_transformFeedbackVaryings.end();
234 }
235 
isProgramSupported(const glw::Functions & gl,const ProgramSpec & spec,deUint32 tfMode)236 static 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 
getAttributeName(const char * varyingName,const glu::TypeComponentVector & path)299 static 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 
genShaderSources(const ProgramSpec & spec,std::string & vertSource,std::string & fragSource,bool pointSizeRequired)325 static 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 
createVertexCaptureProgram(const glu::RenderContext & context,const ProgramSpec & spec,deUint32 bufferMode,deUint32 primitiveType)422 static 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 
computeInputLayout(vector<Attribute> & attributes,int & inputStride,const vector<Varying> & varyings,bool usePointSize)437 static 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 
computeTransformFeedbackOutputs(vector<Output> & transformFeedbackOutputs,const vector<Attribute> & attributes,const vector<Varying> & varyings,const vector<string> & transformFeedbackVaryings,deUint32 bufferMode)465 static 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 
signExtend(deUint32 value,deUint32 numBits)523 static 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 
genAttributeData(const Attribute & attrib,deUint8 * basePtr,int stride,int numElements,de::Random & rnd)534 static 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 
genInputData(const vector<Attribute> & attributes,int numInputs,int inputStride,deUint8 * inputBasePtr,de::Random & rnd)590 static 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 
getTransformFeedbackOutputCount(deUint32 primitiveType,int numElements)625 static 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 
getTransformFeedbackPrimitiveCount(deUint32 primitiveType,int numElements)643 static 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 
getTransformFeedbackPrimitiveMode(deUint32 primitiveType)661 static 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 
getAttributeIndex(deUint32 primitiveType,int numInputs,int outNdx)684 static 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 
compareTransformFeedbackOutput(tcu::TestLog & log,deUint32 primitiveType,const Output & output,int numInputs,const deUint8 * inBasePtr,int inStride,const deUint8 * outBasePtr,int outStride)717 static 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 
computeTransformFeedbackPrimitiveCount(deUint32 primitiveType,const DrawCall * first,const DrawCall * end)785 static 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 
writeBufferGuard(const glw::Functions & gl,deUint32 target,int bufferSize,int guardSize)798 static 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 
verifyGuard(const deUint8 * ptr,int guardSize)807 static 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 
logTransformFeedbackVaryings(TestLog & log,const glw::Functions & gl,deUint32 program)817 static 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 
846 class TransformFeedbackCase : public TestCase
847 {
848 public:
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 
856 protected:
857 	ProgramSpec					m_progSpec;
858 	deUint32					m_bufferMode;
859 	deUint32					m_primitiveType;
860 
861 private:
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 
TransformFeedbackCase(Context & context,const char * name,const char * desc,deUint32 bufferMode,deUint32 primitiveType)881 TransformFeedbackCase::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 
~TransformFeedbackCase(void)892 TransformFeedbackCase::~TransformFeedbackCase (void)
893 {
894 	TransformFeedbackCase::deinit();
895 }
896 
hasArraysInTFVaryings(const ProgramSpec & spec)897 static 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 
init(void)916 void 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 
deinit(void)986 void 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 
iterate(void)1009 TransformFeedbackCase::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 
runTest(const DrawCall * first,const DrawCall * end,deUint32 seed)1065 bool 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 
1314 class PositionCase : public TransformFeedbackCase
1315 {
1316 public:
PositionCase(Context & context,const char * name,const char * desc,deUint32 bufferType,deUint32 primitiveType)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 
1324 class PointSizeCase : public TransformFeedbackCase
1325 {
1326 public:
PointSizeCase(Context & context,const char * name,const char * desc,deUint32 bufferType,deUint32 primitiveType)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 
1334 class BasicTypeCase : public TransformFeedbackCase
1335 {
1336 public:
BasicTypeCase(Context & context,const char * name,const char * desc,deUint32 bufferType,deUint32 primitiveType,glu::DataType type,glu::Precision precision,Interpolation interpolation)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 
1348 class BasicArrayCase : public TransformFeedbackCase
1349 {
1350 public:
BasicArrayCase(Context & context,const char * name,const char * desc,deUint32 bufferType,deUint32 primitiveType,glu::DataType type,glu::Precision precision,Interpolation interpolation)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 
1373 class ArrayElementCase : public TransformFeedbackCase
1374 {
1375 public:
ArrayElementCase(Context & context,const char * name,const char * desc,deUint32 bufferType,deUint32 primitiveType,glu::DataType type,glu::Precision precision,Interpolation interpolation)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 
1388 class RandomCase : public TransformFeedbackCase
1389 {
1390 public:
RandomCase(Context & context,const char * name,const char * desc,deUint32 bufferType,deUint32 primitiveType,deUint32 seed)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 
init(void)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 
1527 private:
1528 	deUint32 m_seed;
1529 };
1530 
1531 } // TransformFeedback
1532 
1533 using namespace TransformFeedback;
1534 
TransformFeedbackTests(Context & context)1535 TransformFeedbackTests::TransformFeedbackTests (Context& context)
1536 	: TestCaseGroup(context, "transform_feedback", "Transform feedback tests")
1537 {
1538 }
1539 
~TransformFeedbackTests(void)1540 TransformFeedbackTests::~TransformFeedbackTests (void)
1541 {
1542 }
1543 
init(void)1544 void 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