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