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,bool elementCapture)1391 RandomCase (Context& context, const char* name, const char* desc, deUint32 bufferType, deUint32 primitiveType, deUint32 seed, bool elementCapture)
1392 : TransformFeedbackCase (context, name, desc, bufferType, primitiveType)
1393 , m_seed (seed)
1394 , m_elementCapture (elementCapture)
1395 {
1396 }
1397
init(void)1398 void init (void)
1399 {
1400 // \note Hard-coded indices and hackery are used when indexing this, beware.
1401 static const glu::DataType typeCandidates[] =
1402 {
1403 glu::TYPE_FLOAT,
1404 glu::TYPE_FLOAT_VEC2,
1405 glu::TYPE_FLOAT_VEC3,
1406 glu::TYPE_FLOAT_VEC4,
1407 glu::TYPE_INT,
1408 glu::TYPE_INT_VEC2,
1409 glu::TYPE_INT_VEC3,
1410 glu::TYPE_INT_VEC4,
1411 glu::TYPE_UINT,
1412 glu::TYPE_UINT_VEC2,
1413 glu::TYPE_UINT_VEC3,
1414 glu::TYPE_UINT_VEC4,
1415
1416 glu::TYPE_FLOAT_MAT2,
1417 glu::TYPE_FLOAT_MAT2X3,
1418 glu::TYPE_FLOAT_MAT2X4,
1419
1420 glu::TYPE_FLOAT_MAT3X2,
1421 glu::TYPE_FLOAT_MAT3,
1422 glu::TYPE_FLOAT_MAT3X4,
1423
1424 glu::TYPE_FLOAT_MAT4X2,
1425 glu::TYPE_FLOAT_MAT4X3,
1426 glu::TYPE_FLOAT_MAT4
1427 };
1428
1429 static const glu::Precision precisions[] =
1430 {
1431 glu::PRECISION_LOWP,
1432 glu::PRECISION_MEDIUMP,
1433 glu::PRECISION_HIGHP
1434 };
1435
1436 static const Interpolation interpModes[] =
1437 {
1438 INTERPOLATION_FLAT,
1439 INTERPOLATION_SMOOTH,
1440 INTERPOLATION_CENTROID
1441 };
1442
1443 const int maxAttributeVectors = 16;
1444 // const int maxTransformFeedbackComponents = 64; // \note It is enough to limit attribute set size.
1445 bool isSeparateMode = m_bufferMode == GL_SEPARATE_ATTRIBS;
1446 int maxTransformFeedbackVars = isSeparateMode ? 4 : maxAttributeVectors;
1447 const float arrayWeight = 0.3f;
1448 const float positionWeight = 0.7f;
1449 const float pointSizeWeight = 0.1f;
1450 const float captureFullArrayWeight = 0.5f;
1451
1452 de::Random rnd (m_seed);
1453 bool usePosition = rnd.getFloat() < positionWeight;
1454 bool usePointSize = rnd.getFloat() < pointSizeWeight;
1455 int numAttribVectorsToUse = rnd.getInt(1, maxAttributeVectors - 1/*position*/ - (usePointSize ? 1 : 0));
1456
1457 int numAttributeVectors = 0;
1458 int varNdx = 0;
1459
1460 // Generate varyings.
1461 while (numAttributeVectors < numAttribVectorsToUse)
1462 {
1463 int maxVecs = isSeparateMode ? de::min(2 /*at most 2*mat2*/, numAttribVectorsToUse-numAttributeVectors) : numAttribVectorsToUse-numAttributeVectors;
1464 const glu::DataType* begin = &typeCandidates[0];
1465 const glu::DataType* end = begin + (maxVecs >= 4 ? 21 :
1466 maxVecs >= 3 ? 18 :
1467 maxVecs >= 2 ? (isSeparateMode ? 13 : 15) : 12);
1468
1469 glu::DataType type = rnd.choose<glu::DataType>(begin, end);
1470 glu::Precision precision = rnd.choose<glu::Precision>(&precisions[0], &precisions[0]+DE_LENGTH_OF_ARRAY(precisions));
1471 Interpolation interp = glu::getDataTypeScalarType(type) == glu::TYPE_FLOAT
1472 ? rnd.choose<Interpolation>(&interpModes[0], &interpModes[0]+DE_LENGTH_OF_ARRAY(interpModes))
1473 : INTERPOLATION_FLAT;
1474 int numVecs = glu::isDataTypeMatrix(type) ? glu::getDataTypeMatrixNumColumns(type) : 1;
1475 int numComps = glu::getDataTypeScalarSize(type);
1476 int maxArrayLen = de::max(1, isSeparateMode ? 4/numComps : maxVecs/numVecs);
1477 bool useArray = rnd.getFloat() < arrayWeight;
1478 int arrayLen = useArray ? rnd.getInt(1, maxArrayLen) : 1;
1479 std::string name = "v_var" + de::toString(varNdx);
1480
1481 if (useArray)
1482 m_progSpec.addVarying(name.c_str(), glu::VarType(glu::VarType(type, precision), arrayLen), interp);
1483 else
1484 m_progSpec.addVarying(name.c_str(), glu::VarType(type, precision), interp);
1485
1486 numAttributeVectors += arrayLen*numVecs;
1487 varNdx += 1;
1488 }
1489
1490 // Generate transform feedback candidate set.
1491 vector<string> tfCandidates;
1492
1493 if (usePosition) tfCandidates.push_back("gl_Position");
1494 if (usePointSize) tfCandidates.push_back("gl_PointSize");
1495
1496 for (int ndx = 0; ndx < varNdx /* num varyings */; ndx++)
1497 {
1498 const Varying& var = m_progSpec.getVaryings()[ndx];
1499
1500 if (var.type.isArrayType())
1501 {
1502 const bool captureFull = m_elementCapture ? (rnd.getFloat() < captureFullArrayWeight) : true;
1503
1504 if (captureFull)
1505 tfCandidates.push_back(var.name);
1506 else
1507 {
1508 const int numElem = var.type.getArraySize();
1509 for (int elemNdx = 0; elemNdx < numElem; elemNdx++)
1510 tfCandidates.push_back(var.name + "[" + de::toString(elemNdx) + "]");
1511 }
1512 }
1513 else
1514 tfCandidates.push_back(var.name);
1515 }
1516
1517 // Pick random selection.
1518 vector<string> tfVaryings(de::min((int)tfCandidates.size(), maxTransformFeedbackVars));
1519 rnd.choose(tfCandidates.begin(), tfCandidates.end(), tfVaryings.begin(), (int)tfVaryings.size());
1520 rnd.shuffle(tfVaryings.begin(), tfVaryings.end());
1521
1522 for (vector<string>::const_iterator var = tfVaryings.begin(); var != tfVaryings.end(); var++)
1523 m_progSpec.addTransformFeedbackVarying(var->c_str());
1524
1525 TransformFeedbackCase::init();
1526 }
1527
1528 private:
1529 deUint32 m_seed;
1530 bool m_elementCapture;
1531 };
1532
1533 } // TransformFeedback
1534
1535 using namespace TransformFeedback;
1536
TransformFeedbackTests(Context & context)1537 TransformFeedbackTests::TransformFeedbackTests (Context& context)
1538 : TestCaseGroup(context, "transform_feedback", "Transform feedback tests")
1539 {
1540 }
1541
~TransformFeedbackTests(void)1542 TransformFeedbackTests::~TransformFeedbackTests (void)
1543 {
1544 }
1545
init(void)1546 void TransformFeedbackTests::init (void)
1547 {
1548 static const struct
1549 {
1550 const char* name;
1551 deUint32 mode;
1552 } bufferModes[] =
1553 {
1554 { "separate", GL_SEPARATE_ATTRIBS },
1555 { "interleaved", GL_INTERLEAVED_ATTRIBS }
1556 };
1557
1558 static const struct
1559 {
1560 const char* name;
1561 deUint32 type;
1562 } primitiveTypes[] =
1563 {
1564 { "points", GL_POINTS },
1565 { "lines", GL_LINES },
1566 { "triangles", GL_TRIANGLES }
1567
1568 // Not supported by GLES3.
1569 // { "line_strip", GL_LINE_STRIP },
1570 // { "line_loop", GL_LINE_LOOP },
1571 // { "triangle_fan", GL_TRIANGLE_FAN },
1572 // { "triangle_strip", GL_TRIANGLE_STRIP }
1573 };
1574
1575 static const glu::DataType basicTypes[] =
1576 {
1577 glu::TYPE_FLOAT,
1578 glu::TYPE_FLOAT_VEC2,
1579 glu::TYPE_FLOAT_VEC3,
1580 glu::TYPE_FLOAT_VEC4,
1581 glu::TYPE_FLOAT_MAT2,
1582 glu::TYPE_FLOAT_MAT2X3,
1583 glu::TYPE_FLOAT_MAT2X4,
1584 glu::TYPE_FLOAT_MAT3X2,
1585 glu::TYPE_FLOAT_MAT3,
1586 glu::TYPE_FLOAT_MAT3X4,
1587 glu::TYPE_FLOAT_MAT4X2,
1588 glu::TYPE_FLOAT_MAT4X3,
1589 glu::TYPE_FLOAT_MAT4,
1590 glu::TYPE_INT,
1591 glu::TYPE_INT_VEC2,
1592 glu::TYPE_INT_VEC3,
1593 glu::TYPE_INT_VEC4,
1594 glu::TYPE_UINT,
1595 glu::TYPE_UINT_VEC2,
1596 glu::TYPE_UINT_VEC3,
1597 glu::TYPE_UINT_VEC4
1598 };
1599
1600 static const glu::Precision precisions[] =
1601 {
1602 glu::PRECISION_LOWP,
1603 glu::PRECISION_MEDIUMP,
1604 glu::PRECISION_HIGHP
1605 };
1606
1607 static const struct
1608 {
1609 const char* name;
1610 Interpolation interp;
1611 } interpModes[] =
1612 {
1613 { "smooth", INTERPOLATION_SMOOTH },
1614 { "flat", INTERPOLATION_FLAT },
1615 { "centroid", INTERPOLATION_CENTROID }
1616 };
1617
1618 // .position
1619 {
1620 tcu::TestCaseGroup* positionGroup = new tcu::TestCaseGroup(m_testCtx, "position", "gl_Position capture using transform feedback");
1621 addChild(positionGroup);
1622
1623 for (int primitiveType = 0; primitiveType < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveType++)
1624 {
1625 for (int bufferMode = 0; bufferMode < DE_LENGTH_OF_ARRAY(bufferModes); bufferMode++)
1626 {
1627 string name = string(primitiveTypes[primitiveType].name) + "_" + bufferModes[bufferMode].name;
1628 positionGroup->addChild(new PositionCase(m_context, name.c_str(), "", bufferModes[bufferMode].mode, primitiveTypes[primitiveType].type));
1629 }
1630 }
1631 }
1632
1633 // .point_size
1634 {
1635 tcu::TestCaseGroup* pointSizeGroup = new tcu::TestCaseGroup(m_testCtx, "point_size", "gl_PointSize capture using transform feedback");
1636 addChild(pointSizeGroup);
1637
1638 for (int primitiveType = 0; primitiveType < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveType++)
1639 {
1640 for (int bufferMode = 0; bufferMode < DE_LENGTH_OF_ARRAY(bufferModes); bufferMode++)
1641 {
1642 string name = string(primitiveTypes[primitiveType].name) + "_" + bufferModes[bufferMode].name;
1643 pointSizeGroup->addChild(new PointSizeCase(m_context, name.c_str(), "", bufferModes[bufferMode].mode, primitiveTypes[primitiveType].type));
1644 }
1645 }
1646 }
1647
1648 // .basic_type
1649 {
1650 tcu::TestCaseGroup* basicTypeGroup = new tcu::TestCaseGroup(m_testCtx, "basic_types", "Basic types in transform feedback");
1651 addChild(basicTypeGroup);
1652
1653 for (int bufferModeNdx = 0; bufferModeNdx < DE_LENGTH_OF_ARRAY(bufferModes); bufferModeNdx++)
1654 {
1655 tcu::TestCaseGroup* modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[bufferModeNdx].name, "");
1656 deUint32 bufferMode = bufferModes[bufferModeNdx].mode;
1657 basicTypeGroup->addChild(modeGroup);
1658
1659 for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveTypeNdx++)
1660 {
1661 tcu::TestCaseGroup* primitiveGroup = new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, "");
1662 deUint32 primitiveType = primitiveTypes[primitiveTypeNdx].type;
1663 modeGroup->addChild(primitiveGroup);
1664
1665 for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(basicTypes); typeNdx++)
1666 {
1667 glu::DataType type = basicTypes[typeNdx];
1668 bool isFloat = glu::getDataTypeScalarType(type) == glu::TYPE_FLOAT;
1669
1670 for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
1671 {
1672 glu::Precision precision = precisions[precNdx];
1673
1674 string name = string(glu::getPrecisionName(precision)) + "_" + glu::getDataTypeName(type);
1675 primitiveGroup->addChild(new BasicTypeCase(m_context, name.c_str(), "", bufferMode, primitiveType, type, precision, isFloat ? INTERPOLATION_SMOOTH : INTERPOLATION_FLAT));
1676 }
1677 }
1678 }
1679 }
1680 }
1681
1682 // .array
1683 {
1684 tcu::TestCaseGroup* arrayGroup = new tcu::TestCaseGroup(m_testCtx, "array", "Capturing whole array in TF");
1685 addChild(arrayGroup);
1686
1687 for (int bufferModeNdx = 0; bufferModeNdx < DE_LENGTH_OF_ARRAY(bufferModes); bufferModeNdx++)
1688 {
1689 tcu::TestCaseGroup* modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[bufferModeNdx].name, "");
1690 deUint32 bufferMode = bufferModes[bufferModeNdx].mode;
1691 arrayGroup->addChild(modeGroup);
1692
1693 for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveTypeNdx++)
1694 {
1695 tcu::TestCaseGroup* primitiveGroup = new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, "");
1696 deUint32 primitiveType = primitiveTypes[primitiveTypeNdx].type;
1697 modeGroup->addChild(primitiveGroup);
1698
1699 for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(basicTypes); typeNdx++)
1700 {
1701 glu::DataType type = basicTypes[typeNdx];
1702 bool isFloat = glu::getDataTypeScalarType(type) == glu::TYPE_FLOAT;
1703
1704 for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
1705 {
1706 glu::Precision precision = precisions[precNdx];
1707
1708 string name = string(glu::getPrecisionName(precision)) + "_" + glu::getDataTypeName(type);
1709 primitiveGroup->addChild(new BasicArrayCase(m_context, name.c_str(), "", bufferMode, primitiveType, type, precision, isFloat ? INTERPOLATION_SMOOTH : INTERPOLATION_FLAT));
1710 }
1711 }
1712 }
1713 }
1714 }
1715
1716 // .array_element
1717 {
1718 tcu::TestCaseGroup* arrayElemGroup = new tcu::TestCaseGroup(m_testCtx, "array_element", "Capturing single array element in TF");
1719 addChild(arrayElemGroup);
1720
1721 for (int bufferModeNdx = 0; bufferModeNdx < DE_LENGTH_OF_ARRAY(bufferModes); bufferModeNdx++)
1722 {
1723 tcu::TestCaseGroup* modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[bufferModeNdx].name, "");
1724 deUint32 bufferMode = bufferModes[bufferModeNdx].mode;
1725 arrayElemGroup->addChild(modeGroup);
1726
1727 for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveTypeNdx++)
1728 {
1729 tcu::TestCaseGroup* primitiveGroup = new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, "");
1730 deUint32 primitiveType = primitiveTypes[primitiveTypeNdx].type;
1731 modeGroup->addChild(primitiveGroup);
1732
1733 for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(basicTypes); typeNdx++)
1734 {
1735 glu::DataType type = basicTypes[typeNdx];
1736 bool isFloat = glu::getDataTypeScalarType(type) == glu::TYPE_FLOAT;
1737
1738 for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
1739 {
1740 glu::Precision precision = precisions[precNdx];
1741
1742 string name = string(glu::getPrecisionName(precision)) + "_" + glu::getDataTypeName(type);
1743 primitiveGroup->addChild(new ArrayElementCase(m_context, name.c_str(), "", bufferMode, primitiveType, type, precision, isFloat ? INTERPOLATION_SMOOTH : INTERPOLATION_FLAT));
1744 }
1745 }
1746 }
1747 }
1748 }
1749
1750 // .interpolation
1751 {
1752 tcu::TestCaseGroup* interpolationGroup = new tcu::TestCaseGroup(m_testCtx, "interpolation", "Different interpolation modes in transform feedback varyings");
1753 addChild(interpolationGroup);
1754
1755 for (int modeNdx = 0; modeNdx < DE_LENGTH_OF_ARRAY(interpModes); modeNdx++)
1756 {
1757 Interpolation interp = interpModes[modeNdx].interp;
1758 tcu::TestCaseGroup* modeGroup = new tcu::TestCaseGroup(m_testCtx, interpModes[modeNdx].name, "");
1759
1760 interpolationGroup->addChild(modeGroup);
1761
1762 for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
1763 {
1764 glu::Precision precision = precisions[precNdx];
1765
1766 for (int primitiveType = 0; primitiveType < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveType++)
1767 {
1768 for (int bufferMode = 0; bufferMode < DE_LENGTH_OF_ARRAY(bufferModes); bufferMode++)
1769 {
1770 string name = string(glu::getPrecisionName(precision)) + "_vec4_" + primitiveTypes[primitiveType].name + "_" + bufferModes[bufferMode].name;
1771 modeGroup->addChild(new BasicTypeCase(m_context, name.c_str(), "", bufferModes[bufferMode].mode, primitiveTypes[primitiveType].type, glu::TYPE_FLOAT_VEC4, precision, interp));
1772 }
1773 }
1774 }
1775 }
1776 }
1777
1778 // .random
1779 {
1780 tcu::TestCaseGroup* randomGroup = new tcu::TestCaseGroup(m_testCtx, "random", "Randomized transform feedback cases");
1781 addChild(randomGroup);
1782
1783 for (int bufferModeNdx = 0; bufferModeNdx < DE_LENGTH_OF_ARRAY(bufferModes); bufferModeNdx++)
1784 {
1785 tcu::TestCaseGroup* modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[bufferModeNdx].name, "");
1786 deUint32 bufferMode = bufferModes[bufferModeNdx].mode;
1787 randomGroup->addChild(modeGroup);
1788
1789 for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveTypeNdx++)
1790 {
1791 tcu::TestCaseGroup* primitiveGroup = new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, "");
1792 deUint32 primitiveType = primitiveTypes[primitiveTypeNdx].type;
1793 modeGroup->addChild(primitiveGroup);
1794
1795 for (int ndx = 0; ndx < 10; ndx++)
1796 {
1797 deUint32 seed = deInt32Hash(bufferMode) ^ deInt32Hash(primitiveType) ^ deInt32Hash(ndx);
1798 primitiveGroup->addChild(new RandomCase(m_context, de::toString(ndx+1).c_str(), "", bufferMode, primitiveType, seed, true));
1799 }
1800 }
1801 }
1802 }
1803
1804 // .random_full_array_capture
1805 {
1806 tcu::TestCaseGroup* randomNecGroup = new tcu::TestCaseGroup(m_testCtx, "random_full_array_capture", "Randomized transform feedback cases without array element capture");
1807 addChild(randomNecGroup);
1808
1809 for (int bufferModeNdx = 0; bufferModeNdx < DE_LENGTH_OF_ARRAY(bufferModes); bufferModeNdx++)
1810 {
1811 tcu::TestCaseGroup* modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[bufferModeNdx].name, "");
1812 deUint32 bufferMode = bufferModes[bufferModeNdx].mode;
1813 randomNecGroup->addChild(modeGroup);
1814
1815 for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveTypeNdx++)
1816 {
1817 tcu::TestCaseGroup* primitiveGroup = new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, "");
1818 deUint32 primitiveType = primitiveTypes[primitiveTypeNdx].type;
1819 modeGroup->addChild(primitiveGroup);
1820
1821 for (int ndx = 0; ndx < 10; ndx++)
1822 {
1823 deUint32 seed = deInt32Hash(bufferMode) ^ deInt32Hash(primitiveType) ^ deInt32Hash(ndx);
1824 primitiveGroup->addChild(new RandomCase(m_context, de::toString(ndx+1).c_str(), "", bufferMode, primitiveType, seed, false));
1825 }
1826 }
1827 }
1828 }
1829 }
1830
1831 } // Functional
1832 } // gles3
1833 } // deqp
1834