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