xref: /aosp_15_r20/external/deqp/modules/gles3/functional/es3fTransformFeedbackTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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