1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES 3.1 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 Tessellation Tests. 22 *//*--------------------------------------------------------------------*/ 23 24 #include "es31fTessellationTests.hpp" 25 #include "glsTextureTestUtil.hpp" 26 #include "gluShaderProgram.hpp" 27 #include "gluRenderContext.hpp" 28 #include "gluPixelTransfer.hpp" 29 #include "gluDrawUtil.hpp" 30 #include "gluObjectWrapper.hpp" 31 #include "gluStrUtil.hpp" 32 #include "gluContextInfo.hpp" 33 #include "gluVarType.hpp" 34 #include "gluVarTypeUtil.hpp" 35 #include "tcuTestLog.hpp" 36 #include "tcuRenderTarget.hpp" 37 #include "tcuSurface.hpp" 38 #include "tcuTextureUtil.hpp" 39 #include "tcuVectorUtil.hpp" 40 #include "tcuImageIO.hpp" 41 #include "tcuResource.hpp" 42 #include "tcuImageCompare.hpp" 43 #include "deRandom.hpp" 44 #include "deStringUtil.hpp" 45 #include "deSharedPtr.hpp" 46 #include "deString.h" 47 #include "deMath.h" 48 49 #include "glwEnums.hpp" 50 #include "glwDefs.hpp" 51 #include "glwFunctions.hpp" 52 53 #include <vector> 54 #include <string> 55 #include <algorithm> 56 #include <functional> 57 #include <set> 58 #include <limits> 59 60 using glu::ShaderProgram; 61 using glu::RenderContext; 62 using tcu::RenderTarget; 63 using tcu::TestLog; 64 using tcu::Vec2; 65 using tcu::Vec3; 66 using tcu::Vec4; 67 using de::Random; 68 using de::SharedPtr; 69 70 using std::vector; 71 using std::string; 72 73 using namespace glw; // For GL types. 74 75 namespace deqp 76 { 77 78 using gls::TextureTestUtil::RandomViewport; 79 80 namespace gles31 81 { 82 namespace Functional 83 { 84 85 enum 86 { 87 MINIMUM_MAX_TESS_GEN_LEVEL = 64 //!< GL-defined minimum for GL_MAX_TESS_GEN_LEVEL. 88 }; 89 90 static inline bool vec3XLessThan (const Vec3& a, const Vec3& b) { return a.x() < b.x(); } 91 92 template <typename IterT> 93 static string elemsStr (const IterT& begin, const IterT& end, int wrapLengthParam = 0, int numIndentationSpaces = 0) 94 { 95 const string baseIndentation = string(numIndentationSpaces, ' '); 96 const string deepIndentation = baseIndentation + string(4, ' '); 97 const int wrapLength = wrapLengthParam > 0 ? wrapLengthParam : std::numeric_limits<int>::max(); 98 const int length = (int)std::distance(begin, end); 99 string result; 100 101 if (length > wrapLength) 102 result += "(amount: " + de::toString(length) + ") "; 103 result += string() + "{" + (length > wrapLength ? "\n"+deepIndentation : " "); 104 105 { 106 int index = 0; 107 for (IterT it = begin; it != end; ++it) 108 { 109 if (it != begin) 110 result += string() + ", " + (index % wrapLength == 0 ? "\n"+deepIndentation : ""); 111 result += de::toString(*it); 112 index++; 113 } 114 115 result += length > wrapLength ? "\n"+baseIndentation : " "; 116 } 117 118 result += "}"; 119 return result; 120 } 121 122 template <typename ContainerT> 123 static string containerStr (const ContainerT& c, int wrapLengthParam = 0, int numIndentationSpaces = 0) 124 { 125 return elemsStr(c.begin(), c.end(), wrapLengthParam, numIndentationSpaces); 126 } 127 128 template <typename T, int N> 129 static string arrayStr (const T (&arr)[N], int wrapLengthParam = 0, int numIndentationSpaces = 0) 130 { 131 return elemsStr(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr), wrapLengthParam, numIndentationSpaces); 132 } 133 134 template <typename T, int N> 135 static T arrayMax (const T (&arr)[N]) 136 { 137 return *std::max_element(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr)); 138 } 139 140 template <typename T, typename MembT> 141 static vector<MembT> members (const vector<T>& objs, MembT T::* membP) 142 { 143 vector<MembT> result(objs.size()); 144 for (int i = 0; i < (int)objs.size(); i++) 145 result[i] = objs[i].*membP; 146 return result; 147 } 148 149 template <typename T, int N> 150 static vector<T> arrayToVector (const T (&arr)[N]) 151 { 152 return vector<T>(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr)); 153 } 154 155 template <typename ContainerT, typename T> 156 static inline bool contains (const ContainerT& c, const T& key) 157 { 158 return c.find(key) != c.end(); 159 } 160 161 template <int Size> 162 static inline tcu::Vector<bool, Size> singleTrueMask (int index) 163 { 164 DE_ASSERT(de::inBounds(index, 0, Size)); 165 tcu::Vector<bool, Size> result; 166 result[index] = true; 167 return result; 168 } 169 170 static int intPow (int base, int exp) 171 { 172 DE_ASSERT(exp >= 0); 173 if (exp == 0) 174 return 1; 175 else 176 { 177 const int sub = intPow(base, exp/2); 178 if (exp % 2 == 0) 179 return sub*sub; 180 else 181 return sub*sub*base; 182 } 183 } 184 185 tcu::Surface getPixels (const glu::RenderContext& rCtx, int x, int y, int width, int height) 186 { 187 tcu::Surface result(width, height); 188 glu::readPixels(rCtx, x, y, result.getAccess()); 189 return result; 190 } 191 192 tcu::Surface getPixels (const glu::RenderContext& rCtx, const RandomViewport& vp) 193 { 194 return getPixels(rCtx, vp.x, vp.y, vp.width, vp.height); 195 } 196 197 static inline void checkRenderTargetSize (const RenderTarget& renderTarget, int minSize) 198 { 199 if (renderTarget.getWidth() < minSize || renderTarget.getHeight() < minSize) 200 throw tcu::NotSupportedError("Render target width and height must be at least " + de::toString(minSize)); 201 } 202 203 tcu::TextureLevel getPNG (const tcu::Archive& archive, const string& filename) 204 { 205 tcu::TextureLevel result; 206 tcu::ImageIO::loadPNG(result, archive, filename.c_str()); 207 return result; 208 } 209 210 static int numBasicSubobjects (const glu::VarType& type) 211 { 212 if (type.isBasicType()) 213 return 1; 214 else if (type.isArrayType()) 215 return type.getArraySize()*numBasicSubobjects(type.getElementType()); 216 else if (type.isStructType()) 217 { 218 const glu::StructType& structType = *type.getStructPtr(); 219 int result = 0; 220 for (int i = 0; i < structType.getNumMembers(); i++) 221 result += numBasicSubobjects(structType.getMember(i).getType()); 222 return result; 223 } 224 else 225 { 226 DE_ASSERT(false); 227 return -1; 228 } 229 } 230 231 static inline int numVerticesPerPrimitive (deUint32 primitiveTypeGL) 232 { 233 switch (primitiveTypeGL) 234 { 235 case GL_POINTS: return 1; 236 case GL_TRIANGLES: return 3; 237 case GL_LINES: return 2; 238 default: 239 DE_ASSERT(false); 240 return -1; 241 } 242 } 243 244 static inline void setViewport (const glw::Functions& gl, const RandomViewport& vp) 245 { 246 gl.viewport(vp.x, vp.y, vp.width, vp.height); 247 } 248 249 static inline deUint32 getQueryResult (const glw::Functions& gl, deUint32 queryObject) 250 { 251 deUint32 result = (deUint32)-1; 252 gl.getQueryObjectuiv(queryObject, GL_QUERY_RESULT, &result); 253 TCU_CHECK(result != (deUint32)-1); 254 return result; 255 } 256 257 template <typename T> 258 static void readDataMapped (const glw::Functions& gl, deUint32 bufferTarget, int numElems, T* dst) 259 { 260 const int numBytes = numElems*(int)sizeof(T); 261 const T* const mappedData = (const T*)gl.mapBufferRange(bufferTarget, 0, numBytes, GL_MAP_READ_BIT); 262 GLU_EXPECT_NO_ERROR(gl.getError(), (string() + "glMapBufferRange(" + glu::getBufferTargetName((int)bufferTarget) + ", 0, " + de::toString(numBytes) + ", GL_MAP_READ_BIT)").c_str()); 263 TCU_CHECK(mappedData != DE_NULL); 264 265 for (int i = 0; i < numElems; i++) 266 dst[i] = mappedData[i]; 267 268 gl.unmapBuffer(bufferTarget); 269 } 270 271 template <typename T> 272 static vector<T> readDataMapped (const glw::Functions& gl, deUint32 bufferTarget, int numElems) 273 { 274 vector<T> result(numElems); 275 readDataMapped(gl, bufferTarget, numElems, &result[0]); 276 return result; 277 } 278 279 namespace 280 { 281 282 template <typename ArgT, bool res> 283 struct ConstantUnaryPredicate 284 { 285 bool operator() (const ArgT&) const { return res; } 286 }; 287 288 //! Helper for handling simple, one-varying transform feedbacks. 289 template <typename VaryingT> 290 class TransformFeedbackHandler 291 { 292 public: 293 struct Result 294 { 295 int numPrimitives; 296 vector<VaryingT> varying; 297 298 Result (void) : numPrimitives(-1) {} 299 Result (int n, const vector<VaryingT>& v) : numPrimitives(n), varying(v) {} 300 }; 301 302 TransformFeedbackHandler (const glu::RenderContext& renderCtx, int maxNumVertices); 303 304 Result renderAndGetPrimitives (deUint32 programGL, deUint32 tfPrimTypeGL, int numBindings, const glu::VertexArrayBinding* bindings, int numVertices) const; 305 306 private: 307 const glu::RenderContext& m_renderCtx; 308 const glu::TransformFeedback m_tf; 309 const glu::Buffer m_tfBuffer; 310 const glu::Query m_tfPrimQuery; 311 }; 312 313 template <typename AttribType> 314 TransformFeedbackHandler<AttribType>::TransformFeedbackHandler (const glu::RenderContext& renderCtx, int maxNumVertices) 315 : m_renderCtx (renderCtx) 316 , m_tf (renderCtx) 317 , m_tfBuffer (renderCtx) 318 , m_tfPrimQuery (renderCtx) 319 { 320 const glw::Functions& gl = m_renderCtx.getFunctions(); 321 // \note Room for 1 extra triangle, to detect if GL returns too many primitives. 322 const int bufferSize = (maxNumVertices + 3) * (int)sizeof(AttribType); 323 324 gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, *m_tfBuffer); 325 gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bufferSize, DE_NULL, GL_DYNAMIC_READ); 326 } 327 328 template <typename AttribType> 329 typename TransformFeedbackHandler<AttribType>::Result TransformFeedbackHandler<AttribType>::renderAndGetPrimitives (deUint32 programGL, deUint32 tfPrimTypeGL, int numBindings, const glu::VertexArrayBinding* bindings, int numVertices) const 330 { 331 DE_ASSERT(tfPrimTypeGL == GL_POINTS || tfPrimTypeGL == GL_LINES || tfPrimTypeGL == GL_TRIANGLES); 332 333 const glw::Functions& gl = m_renderCtx.getFunctions(); 334 335 gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, *m_tf); 336 gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, *m_tfBuffer); 337 gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, *m_tfBuffer); 338 339 gl.beginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, *m_tfPrimQuery); 340 gl.beginTransformFeedback(tfPrimTypeGL); 341 342 glu::draw(m_renderCtx, programGL, numBindings, bindings, glu::pr::Patches(numVertices)); 343 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed"); 344 345 gl.endTransformFeedback(); 346 gl.endQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); 347 348 { 349 const int numPrimsWritten = (int)getQueryResult(gl, *m_tfPrimQuery); 350 return Result(numPrimsWritten, readDataMapped<AttribType>(gl, GL_TRANSFORM_FEEDBACK_BUFFER, numPrimsWritten * numVerticesPerPrimitive(tfPrimTypeGL))); 351 } 352 } 353 354 template <typename T> 355 class SizeLessThan 356 { 357 public: 358 bool operator() (const T& a, const T& b) const { return a.size() < b.size(); } 359 }; 360 361 //! Predicate functor for comparing structs by their members. 362 template <typename Pred, typename T, typename MembT> 363 class MemberPred 364 { 365 public: 366 MemberPred (MembT T::* membP) : m_membP(membP), m_pred(Pred()) {} 367 bool operator() (const T& a, const T& b) const { return m_pred(a.*m_membP, b.*m_membP); } 368 369 private: 370 MembT T::* m_membP; 371 Pred m_pred; 372 }; 373 374 //! Convenience wrapper for MemberPred, because class template arguments aren't deduced based on constructor arguments. 375 template <template <typename> class Pred, typename T, typename MembT> 376 static MemberPred<Pred<MembT>, T, MembT> memberPred (MembT T::* membP) { return MemberPred<Pred<MembT>, T, MembT>(membP); } 377 378 template <typename SeqT, int Size, typename Pred> 379 class LexCompare 380 { 381 public: 382 LexCompare (void) : m_pred(Pred()) {} 383 384 bool operator() (const SeqT& a, const SeqT& b) const 385 { 386 for (int i = 0; i < Size; i++) 387 { 388 if (m_pred(a[i], b[i])) 389 return true; 390 if (m_pred(b[i], a[i])) 391 return false; 392 } 393 return false; 394 } 395 396 private: 397 Pred m_pred; 398 }; 399 400 template <int Size> 401 class VecLexLessThan : public LexCompare<tcu::Vector<float, Size>, Size, std::less<float> > 402 { 403 }; 404 405 enum TessPrimitiveType 406 { 407 TESSPRIMITIVETYPE_TRIANGLES = 0, 408 TESSPRIMITIVETYPE_QUADS, 409 TESSPRIMITIVETYPE_ISOLINES, 410 411 TESSPRIMITIVETYPE_LAST 412 }; 413 414 enum SpacingMode 415 { 416 SPACINGMODE_EQUAL, 417 SPACINGMODE_FRACTIONAL_ODD, 418 SPACINGMODE_FRACTIONAL_EVEN, 419 420 SPACINGMODE_LAST 421 }; 422 423 enum Winding 424 { 425 WINDING_CCW = 0, 426 WINDING_CW, 427 428 WINDING_LAST 429 }; 430 431 static inline const char* getTessPrimitiveTypeShaderName (TessPrimitiveType type) 432 { 433 switch (type) 434 { 435 case TESSPRIMITIVETYPE_TRIANGLES: return "triangles"; 436 case TESSPRIMITIVETYPE_QUADS: return "quads"; 437 case TESSPRIMITIVETYPE_ISOLINES: return "isolines"; 438 default: 439 DE_ASSERT(false); 440 return DE_NULL; 441 } 442 } 443 444 static inline const char* getSpacingModeShaderName (SpacingMode mode) 445 { 446 switch (mode) 447 { 448 case SPACINGMODE_EQUAL: return "equal_spacing"; 449 case SPACINGMODE_FRACTIONAL_ODD: return "fractional_odd_spacing"; 450 case SPACINGMODE_FRACTIONAL_EVEN: return "fractional_even_spacing"; 451 default: 452 DE_ASSERT(false); 453 return DE_NULL; 454 } 455 } 456 457 static inline const char* getWindingShaderName (Winding winding) 458 { 459 switch (winding) 460 { 461 case WINDING_CCW: return "ccw"; 462 case WINDING_CW: return "cw"; 463 default: 464 DE_ASSERT(false); 465 return DE_NULL; 466 } 467 } 468 469 static inline string getTessellationEvaluationInLayoutString (TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode=false) 470 { 471 return string() + "layout (" + getTessPrimitiveTypeShaderName(primType) 472 + ", " + getSpacingModeShaderName(spacing) 473 + ", " + getWindingShaderName(winding) 474 + (usePointMode ? ", point_mode" : "") 475 + ") in;\n"; 476 } 477 478 static inline string getTessellationEvaluationInLayoutString (TessPrimitiveType primType, SpacingMode spacing, bool usePointMode=false) 479 { 480 return string() + "layout (" + getTessPrimitiveTypeShaderName(primType) 481 + ", " + getSpacingModeShaderName(spacing) 482 + (usePointMode ? ", point_mode" : "") 483 + ") in;\n"; 484 } 485 486 static inline string getTessellationEvaluationInLayoutString (TessPrimitiveType primType, Winding winding, bool usePointMode=false) 487 { 488 return string() + "layout (" + getTessPrimitiveTypeShaderName(primType) 489 + ", " + getWindingShaderName(winding) 490 + (usePointMode ? ", point_mode" : "") 491 + ") in;\n"; 492 } 493 494 static inline string getTessellationEvaluationInLayoutString (TessPrimitiveType primType, bool usePointMode=false) 495 { 496 return string() + "layout (" + getTessPrimitiveTypeShaderName(primType) 497 + (usePointMode ? ", point_mode" : "") 498 + ") in;\n"; 499 } 500 501 static inline deUint32 outputPrimitiveTypeGL (TessPrimitiveType tessPrimType, bool usePointMode) 502 { 503 if (usePointMode) 504 return GL_POINTS; 505 else 506 { 507 switch (tessPrimType) 508 { 509 case TESSPRIMITIVETYPE_TRIANGLES: return GL_TRIANGLES; 510 case TESSPRIMITIVETYPE_QUADS: return GL_TRIANGLES; 511 case TESSPRIMITIVETYPE_ISOLINES: return GL_LINES; 512 default: 513 DE_ASSERT(false); 514 return (deUint32)-1; 515 } 516 } 517 } 518 519 static inline int numInnerTessellationLevels (TessPrimitiveType primType) 520 { 521 switch (primType) 522 { 523 case TESSPRIMITIVETYPE_TRIANGLES: return 1; 524 case TESSPRIMITIVETYPE_QUADS: return 2; 525 case TESSPRIMITIVETYPE_ISOLINES: return 0; 526 default: DE_ASSERT(false); return -1; 527 } 528 } 529 530 static inline int numOuterTessellationLevels (TessPrimitiveType primType) 531 { 532 switch (primType) 533 { 534 case TESSPRIMITIVETYPE_TRIANGLES: return 3; 535 case TESSPRIMITIVETYPE_QUADS: return 4; 536 case TESSPRIMITIVETYPE_ISOLINES: return 2; 537 default: DE_ASSERT(false); return -1; 538 } 539 } 540 541 static string tessellationLevelsString (const float* inner, int numInner, const float* outer, int numOuter) 542 { 543 DE_ASSERT(numInner >= 0 && numOuter >= 0); 544 return "inner: " + elemsStr(inner, inner+numInner) + ", outer: " + elemsStr(outer, outer+numOuter); 545 } 546 547 static string tessellationLevelsString (const float* inner, const float* outer, TessPrimitiveType primType) 548 { 549 return tessellationLevelsString(inner, numInnerTessellationLevels(primType), outer, numOuterTessellationLevels(primType)); 550 } 551 552 static string tessellationLevelsString (const float* inner, const float* outer) 553 { 554 return tessellationLevelsString(inner, 2, outer, 4); 555 } 556 557 static inline float getClampedTessLevel (SpacingMode mode, float tessLevel) 558 { 559 switch (mode) 560 { 561 case SPACINGMODE_EQUAL: return de::max(1.0f, tessLevel); 562 case SPACINGMODE_FRACTIONAL_ODD: return de::max(1.0f, tessLevel); 563 case SPACINGMODE_FRACTIONAL_EVEN: return de::max(2.0f, tessLevel); 564 default: 565 DE_ASSERT(false); 566 return -1.0f; 567 } 568 } 569 570 static inline int getRoundedTessLevel (SpacingMode mode, float clampedTessLevel) 571 { 572 int result = (int)deFloatCeil(clampedTessLevel); 573 574 switch (mode) 575 { 576 case SPACINGMODE_EQUAL: break; 577 case SPACINGMODE_FRACTIONAL_ODD: result += 1 - result % 2; break; 578 case SPACINGMODE_FRACTIONAL_EVEN: result += result % 2; break; 579 default: 580 DE_ASSERT(false); 581 } 582 DE_ASSERT(de::inRange<int>(result, 1, MINIMUM_MAX_TESS_GEN_LEVEL)); 583 584 return result; 585 } 586 587 static int getClampedRoundedTessLevel (SpacingMode mode, float tessLevel) 588 { 589 return getRoundedTessLevel(mode, getClampedTessLevel(mode, tessLevel)); 590 } 591 592 //! A description of an outer edge of a triangle, quad or isolines. 593 //! An outer edge can be described by the index of a u/v/w coordinate 594 //! and the coordinate's value along that edge. 595 struct OuterEdgeDescription 596 { 597 int constantCoordinateIndex; 598 float constantCoordinateValueChoices[2]; 599 int numConstantCoordinateValueChoices; 600 601 OuterEdgeDescription (int i, float c0) : constantCoordinateIndex(i), numConstantCoordinateValueChoices(1) { constantCoordinateValueChoices[0] = c0; } 602 OuterEdgeDescription (int i, float c0, float c1) : constantCoordinateIndex(i), numConstantCoordinateValueChoices(2) { constantCoordinateValueChoices[0] = c0; constantCoordinateValueChoices[1] = c1; } 603 604 string description (void) const 605 { 606 static const char* const coordinateNames[] = { "u", "v", "w" }; 607 string result; 608 for (int i = 0; i < numConstantCoordinateValueChoices; i++) 609 result += string() + (i > 0 ? " or " : "") + coordinateNames[constantCoordinateIndex] + "=" + de::toString(constantCoordinateValueChoices[i]); 610 return result; 611 } 612 613 bool contains (const Vec3& v) const 614 { 615 for (int i = 0; i < numConstantCoordinateValueChoices; i++) 616 if (v[constantCoordinateIndex] == constantCoordinateValueChoices[i]) 617 return true; 618 return false; 619 } 620 }; 621 622 static vector<OuterEdgeDescription> outerEdgeDescriptions (TessPrimitiveType primType) 623 { 624 static const OuterEdgeDescription triangleOuterEdgeDescriptions[3] = 625 { 626 OuterEdgeDescription(0, 0.0f), 627 OuterEdgeDescription(1, 0.0f), 628 OuterEdgeDescription(2, 0.0f) 629 }; 630 631 static const OuterEdgeDescription quadOuterEdgeDescriptions[4] = 632 { 633 OuterEdgeDescription(0, 0.0f), 634 OuterEdgeDescription(1, 0.0f), 635 OuterEdgeDescription(0, 1.0f), 636 OuterEdgeDescription(1, 1.0f) 637 }; 638 639 static const OuterEdgeDescription isolinesOuterEdgeDescriptions[1] = 640 { 641 OuterEdgeDescription(0, 0.0f, 1.0f), 642 }; 643 644 switch (primType) 645 { 646 case TESSPRIMITIVETYPE_TRIANGLES: return arrayToVector(triangleOuterEdgeDescriptions); 647 case TESSPRIMITIVETYPE_QUADS: return arrayToVector(quadOuterEdgeDescriptions); 648 case TESSPRIMITIVETYPE_ISOLINES: return arrayToVector(isolinesOuterEdgeDescriptions); 649 default: DE_ASSERT(false); return vector<OuterEdgeDescription>(); 650 } 651 } 652 653 // \note The tessellation coordinates generated by this function could break some of the rules given in the spec (e.g. it may not exactly hold that u+v+w == 1.0f, or [uvw] + (1.0f-[uvw]) == 1.0f). 654 static vector<Vec3> generateReferenceTriangleTessCoords (SpacingMode spacingMode, int inner, int outer0, int outer1, int outer2) 655 { 656 vector<Vec3> tessCoords; 657 658 if (inner == 1) 659 { 660 if (outer0 == 1 && outer1 == 1 && outer2 == 1) 661 { 662 tessCoords.push_back(Vec3(1.0f, 0.0f, 0.0f)); 663 tessCoords.push_back(Vec3(0.0f, 1.0f, 0.0f)); 664 tessCoords.push_back(Vec3(0.0f, 0.0f, 1.0f)); 665 return tessCoords; 666 } 667 else 668 return generateReferenceTriangleTessCoords(spacingMode, spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2, 669 outer0, outer1, outer2); 670 } 671 else 672 { 673 for (int i = 0; i < outer0; i++) { const float v = (float)i / (float)outer0; tessCoords.push_back(Vec3( 0.0f, v, 1.0f - v)); } 674 for (int i = 0; i < outer1; i++) { const float v = (float)i / (float)outer1; tessCoords.push_back(Vec3(1.0f - v, 0.0f, v)); } 675 for (int i = 0; i < outer2; i++) { const float v = (float)i / (float)outer2; tessCoords.push_back(Vec3( v, 1.0f - v, 0.0f)); } 676 677 const int numInnerTriangles = inner/2; 678 for (int innerTriangleNdx = 0; innerTriangleNdx < numInnerTriangles; innerTriangleNdx++) 679 { 680 const int curInnerTriangleLevel = inner - 2*(innerTriangleNdx+1); 681 682 if (curInnerTriangleLevel == 0) 683 tessCoords.push_back(Vec3(1.0f/3.0f)); 684 else 685 { 686 const float minUVW = (float)(2 * (innerTriangleNdx + 1)) / (float)(3 * inner); 687 const float maxUVW = 1.0f - 2.0f*minUVW; 688 const Vec3 corners[3] = 689 { 690 Vec3(maxUVW, minUVW, minUVW), 691 Vec3(minUVW, maxUVW, minUVW), 692 Vec3(minUVW, minUVW, maxUVW) 693 }; 694 695 for (int i = 0; i < curInnerTriangleLevel; i++) 696 { 697 const float f = (float)i / (float)curInnerTriangleLevel; 698 for (int j = 0; j < 3; j++) 699 tessCoords.push_back((1.0f - f)*corners[j] + f*corners[(j+1)%3]); 700 } 701 } 702 } 703 704 return tessCoords; 705 } 706 } 707 708 static int referenceTriangleNonPointModePrimitiveCount (SpacingMode spacingMode, int inner, int outer0, int outer1, int outer2) 709 { 710 if (inner == 1) 711 { 712 if (outer0 == 1 && outer1 == 1 && outer2 == 1) 713 return 1; 714 else 715 return referenceTriangleNonPointModePrimitiveCount(spacingMode, spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2, 716 outer0, outer1, outer2); 717 } 718 else 719 { 720 int result = outer0 + outer1 + outer2; 721 722 const int numInnerTriangles = inner/2; 723 for (int innerTriangleNdx = 0; innerTriangleNdx < numInnerTriangles; innerTriangleNdx++) 724 { 725 const int curInnerTriangleLevel = inner - 2*(innerTriangleNdx+1); 726 727 if (curInnerTriangleLevel == 1) 728 result += 4; 729 else 730 result += 2*3*curInnerTriangleLevel; 731 } 732 733 return result; 734 } 735 } 736 737 // \note The tessellation coordinates generated by this function could break some of the rules given in the spec (e.g. it may not exactly hold that [uv] + (1.0f-[uv]) == 1.0f). 738 static vector<Vec3> generateReferenceQuadTessCoords (SpacingMode spacingMode, int inner0, int inner1, int outer0, int outer1, int outer2, int outer3) 739 { 740 vector<Vec3> tessCoords; 741 742 if (inner0 == 1 || inner1 == 1) 743 { 744 if (inner0 == 1 && inner1 == 1 && outer0 == 1 && outer1 == 1 && outer2 == 1 && outer3 == 1) 745 { 746 tessCoords.push_back(Vec3(0.0f, 0.0f, 0.0f)); 747 tessCoords.push_back(Vec3(1.0f, 0.0f, 0.0f)); 748 tessCoords.push_back(Vec3(0.0f, 1.0f, 0.0f)); 749 tessCoords.push_back(Vec3(1.0f, 1.0f, 0.0f)); 750 return tessCoords; 751 } 752 else 753 return generateReferenceQuadTessCoords(spacingMode, inner0 > 1 ? inner0 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2, 754 inner1 > 1 ? inner1 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2, 755 outer0, outer1, outer2, outer3); 756 } 757 else 758 { 759 for (int i = 0; i < outer0; i++) { const float v = (float)i / (float)outer0; tessCoords.push_back(Vec3(0.0f, v, 0.0f)); } 760 for (int i = 0; i < outer1; i++) { const float v = (float)i / (float)outer1; tessCoords.push_back(Vec3(1.0f-v, 0.0f, 0.0f)); } 761 for (int i = 0; i < outer2; i++) { const float v = (float)i / (float)outer2; tessCoords.push_back(Vec3(1.0f, 1.0f-v, 0.0f)); } 762 for (int i = 0; i < outer3; i++) { const float v = (float)i / (float)outer3; tessCoords.push_back(Vec3(v, 1.0f, 0.0f)); } 763 764 for (int innerVtxY = 0; innerVtxY < inner1-1; innerVtxY++) 765 for (int innerVtxX = 0; innerVtxX < inner0-1; innerVtxX++) 766 tessCoords.push_back(Vec3((float)(innerVtxX + 1) / (float)inner0, 767 (float)(innerVtxY + 1) / (float)inner1, 768 0.0f)); 769 770 return tessCoords; 771 } 772 } 773 774 static int referenceQuadNonPointModePrimitiveCount (SpacingMode spacingMode, int inner0, int inner1, int outer0, int outer1, int outer2, int outer3) 775 { 776 vector<Vec3> tessCoords; 777 778 if (inner0 == 1 || inner1 == 1) 779 { 780 if (inner0 == 1 && inner1 == 1 && outer0 == 1 && outer1 == 1 && outer2 == 1 && outer3 == 1) 781 return 2; 782 else 783 return referenceQuadNonPointModePrimitiveCount(spacingMode, inner0 > 1 ? inner0 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2, 784 inner1 > 1 ? inner1 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2, 785 outer0, outer1, outer2, outer3); 786 } 787 else 788 return 2*(inner0-2)*(inner1-2) + 2*(inner0-2) + 2*(inner1-2) + outer0+outer1+outer2+outer3; 789 } 790 791 // \note The tessellation coordinates generated by this function could break some of the rules given in the spec (e.g. it may not exactly hold that [uv] + (1.0f-[uv]) == 1.0f). 792 static vector<Vec3> generateReferenceIsolineTessCoords (int outer0, int outer1) 793 { 794 vector<Vec3> tessCoords; 795 796 for (int y = 0; y < outer0; y++) 797 for (int x = 0; x < outer1+1; x++) 798 tessCoords.push_back(Vec3((float)x / (float)outer1, 799 (float)y / (float)outer0, 800 0.0f)); 801 802 return tessCoords; 803 } 804 805 static int referenceIsolineNonPointModePrimitiveCount (int outer0, int outer1) 806 { 807 return outer0*outer1; 808 } 809 810 static void getClampedRoundedTriangleTessLevels (SpacingMode spacingMode, const float* innerSrc, const float* outerSrc, int* innerDst, int *outerDst) 811 { 812 innerDst[0] = getClampedRoundedTessLevel(spacingMode, innerSrc[0]); 813 for (int i = 0; i < 3; i++) 814 outerDst[i] = getClampedRoundedTessLevel(spacingMode, outerSrc[i]); 815 } 816 817 static void getClampedRoundedQuadTessLevels (SpacingMode spacingMode, const float* innerSrc, const float* outerSrc, int* innerDst, int *outerDst) 818 { 819 for (int i = 0; i < 2; i++) 820 innerDst[i] = getClampedRoundedTessLevel(spacingMode, innerSrc[i]); 821 for (int i = 0; i < 4; i++) 822 outerDst[i] = getClampedRoundedTessLevel(spacingMode, outerSrc[i]); 823 } 824 825 static void getClampedRoundedIsolineTessLevels (SpacingMode spacingMode, const float* outerSrc, int* outerDst) 826 { 827 outerDst[0] = getClampedRoundedTessLevel(SPACINGMODE_EQUAL, outerSrc[0]); 828 outerDst[1] = getClampedRoundedTessLevel(spacingMode, outerSrc[1]); 829 } 830 831 static inline bool isPatchDiscarded (TessPrimitiveType primitiveType, const float* outerLevels) 832 { 833 const int numOuterLevels = numOuterTessellationLevels(primitiveType); 834 for (int i = 0; i < numOuterLevels; i++) 835 if (outerLevels[i] <= 0.0f) 836 return true; 837 return false; 838 } 839 840 static vector<Vec3> generateReferenceTessCoords (TessPrimitiveType primitiveType, SpacingMode spacingMode, const float* innerLevels, const float* outerLevels) 841 { 842 if (isPatchDiscarded(primitiveType, outerLevels)) 843 return vector<Vec3>(); 844 845 switch (primitiveType) 846 { 847 case TESSPRIMITIVETYPE_TRIANGLES: 848 { 849 int inner; 850 int outer[3]; 851 getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]); 852 853 if (spacingMode != SPACINGMODE_EQUAL) 854 { 855 // \note For fractional spacing modes, exact results are implementation-defined except in special cases. 856 DE_ASSERT(de::abs(innerLevels[0] - (float)inner) < 0.001f); 857 for (int i = 0; i < 3; i++) 858 DE_ASSERT(de::abs(outerLevels[i] - (float)outer[i]) < 0.001f); 859 DE_ASSERT(inner > 1 || (outer[0] == 1 && outer[1] == 1 && outer[2] == 1)); 860 } 861 862 return generateReferenceTriangleTessCoords(spacingMode, inner, outer[0], outer[1], outer[2]); 863 } 864 865 case TESSPRIMITIVETYPE_QUADS: 866 { 867 int inner[2]; 868 int outer[4]; 869 getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]); 870 871 if (spacingMode != SPACINGMODE_EQUAL) 872 { 873 // \note For fractional spacing modes, exact results are implementation-defined except in special cases. 874 for (int i = 0; i < 2; i++) 875 DE_ASSERT(de::abs(innerLevels[i] - (float)inner[i]) < 0.001f); 876 for (int i = 0; i < 4; i++) 877 DE_ASSERT(de::abs(outerLevels[i] - (float)outer[i]) < 0.001f); 878 879 DE_ASSERT((inner[0] > 1 && inner[1] > 1) || (inner[0] == 1 && inner[1] == 1 && outer[0] == 1 && outer[1] == 1 && outer[2] == 1 && outer[3] == 1)); 880 } 881 882 return generateReferenceQuadTessCoords(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]); 883 } 884 885 case TESSPRIMITIVETYPE_ISOLINES: 886 { 887 int outer[2]; 888 getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]); 889 890 if (spacingMode != SPACINGMODE_EQUAL) 891 { 892 // \note For fractional spacing modes, exact results are implementation-defined except in special cases. 893 DE_ASSERT(de::abs(outerLevels[1] - (float)outer[1]) < 0.001f); 894 } 895 896 return generateReferenceIsolineTessCoords(outer[0], outer[1]); 897 } 898 899 default: 900 DE_ASSERT(false); 901 return vector<Vec3>(); 902 } 903 } 904 905 static int referencePointModePrimitiveCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, const float* innerLevels, const float* outerLevels) 906 { 907 if (isPatchDiscarded(primitiveType, outerLevels)) 908 return 0; 909 910 switch (primitiveType) 911 { 912 case TESSPRIMITIVETYPE_TRIANGLES: 913 { 914 int inner; 915 int outer[3]; 916 getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]); 917 return (int)generateReferenceTriangleTessCoords(spacingMode, inner, outer[0], outer[1], outer[2]).size(); 918 } 919 920 case TESSPRIMITIVETYPE_QUADS: 921 { 922 int inner[2]; 923 int outer[4]; 924 getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]); 925 return (int)generateReferenceQuadTessCoords(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]).size(); 926 } 927 928 case TESSPRIMITIVETYPE_ISOLINES: 929 { 930 int outer[2]; 931 getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]); 932 return (int)generateReferenceIsolineTessCoords(outer[0], outer[1]).size(); 933 } 934 935 default: 936 DE_ASSERT(false); 937 return -1; 938 } 939 } 940 941 static int referenceNonPointModePrimitiveCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, const float* innerLevels, const float* outerLevels) 942 { 943 if (isPatchDiscarded(primitiveType, outerLevels)) 944 return 0; 945 946 switch (primitiveType) 947 { 948 case TESSPRIMITIVETYPE_TRIANGLES: 949 { 950 int inner; 951 int outer[3]; 952 getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]); 953 return referenceTriangleNonPointModePrimitiveCount(spacingMode, inner, outer[0], outer[1], outer[2]); 954 } 955 956 case TESSPRIMITIVETYPE_QUADS: 957 { 958 int inner[2]; 959 int outer[4]; 960 getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]); 961 return referenceQuadNonPointModePrimitiveCount(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]); 962 } 963 964 case TESSPRIMITIVETYPE_ISOLINES: 965 { 966 int outer[2]; 967 getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]); 968 return referenceIsolineNonPointModePrimitiveCount(outer[0], outer[1]); 969 } 970 971 default: 972 DE_ASSERT(false); 973 return -1; 974 } 975 } 976 977 static int referencePrimitiveCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, bool usePointMode, const float* innerLevels, const float* outerLevels) 978 { 979 return usePointMode ? referencePointModePrimitiveCount (primitiveType, spacingMode, innerLevels, outerLevels) 980 : referenceNonPointModePrimitiveCount (primitiveType, spacingMode, innerLevels, outerLevels); 981 } 982 983 static int referenceVertexCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, bool usePointMode, const float* innerLevels, const float* outerLevels) 984 { 985 return referencePrimitiveCount(primitiveType, spacingMode, usePointMode, innerLevels, outerLevels) 986 * numVerticesPerPrimitive(outputPrimitiveTypeGL(primitiveType, usePointMode)); 987 } 988 989 //! Helper for calling referenceVertexCount multiple times with different tessellation levels. 990 //! \note Levels contains inner and outer levels, per patch, in order IIOOOO. The full 6 levels must always be present, irrespective of primitiveType. 991 static int multiplePatchReferenceVertexCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, bool usePointMode, const float* levels, int numPatches) 992 { 993 int result = 0; 994 for (int patchNdx = 0; patchNdx < numPatches; patchNdx++) 995 result += referenceVertexCount(primitiveType, spacingMode, usePointMode, &levels[6*patchNdx + 0], &levels[6*patchNdx + 2]); 996 return result; 997 } 998 999 vector<float> generateRandomPatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel, de::Random& rnd) 1000 { 1001 vector<float> tessLevels(numPatches*6); 1002 1003 for (int patchNdx = 0; patchNdx < numPatches; patchNdx++) 1004 { 1005 float* const inner = &tessLevels[patchNdx*6 + 0]; 1006 float* const outer = &tessLevels[patchNdx*6 + 2]; 1007 1008 for (int j = 0; j < 2; j++) 1009 inner[j] = rnd.getFloat(1.0f, 62.0f); 1010 for (int j = 0; j < 4; j++) 1011 outer[j] = j == constantOuterLevelIndex ? constantOuterLevel : rnd.getFloat(1.0f, 62.0f); 1012 } 1013 1014 return tessLevels; 1015 } 1016 1017 static inline void drawPoint (tcu::Surface& dst, int centerX, int centerY, const tcu::RGBA& color, int size) 1018 { 1019 const int width = dst.getWidth(); 1020 const int height = dst.getHeight(); 1021 DE_ASSERT(de::inBounds(centerX, 0, width) && de::inBounds(centerY, 0, height)); 1022 DE_ASSERT(size > 0); 1023 1024 for (int yOff = -((size-1)/2); yOff <= size/2; yOff++) 1025 for (int xOff = -((size-1)/2); xOff <= size/2; xOff++) 1026 { 1027 const int pixX = centerX + xOff; 1028 const int pixY = centerY + yOff; 1029 if (de::inBounds(pixX, 0, width) && de::inBounds(pixY, 0, height)) 1030 dst.setPixel(pixX, pixY, color); 1031 } 1032 } 1033 1034 static void drawTessCoordPoint (tcu::Surface& dst, TessPrimitiveType primitiveType, const Vec3& pt, const tcu::RGBA& color, int size) 1035 { 1036 // \note These coordinates should match the description in the log message in TessCoordCase::iterate. 1037 1038 static const Vec2 triangleCorners[3] = 1039 { 1040 Vec2(0.95f, 0.95f), 1041 Vec2(0.5f, 0.95f - 0.9f*deFloatSqrt(3.0f/4.0f)), 1042 Vec2(0.05f, 0.95f) 1043 }; 1044 1045 static const float quadIsolineLDRU[4] = 1046 { 1047 0.1f, 0.9f, 0.9f, 0.1f 1048 }; 1049 1050 const Vec2 dstPos = primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? pt.x()*triangleCorners[0] 1051 + pt.y()*triangleCorners[1] 1052 + pt.z()*triangleCorners[2] 1053 1054 : primitiveType == TESSPRIMITIVETYPE_QUADS || 1055 primitiveType == TESSPRIMITIVETYPE_ISOLINES ? Vec2((1.0f - pt.x())*quadIsolineLDRU[0] + pt.x()*quadIsolineLDRU[2], 1056 (1.0f - pt.y())*quadIsolineLDRU[1] + pt.y()*quadIsolineLDRU[3]) 1057 1058 : Vec2(-1.0f); 1059 1060 drawPoint(dst, (int)(dstPos.x()*dst.getWidth()), (int)(dstPos.y()*dst.getHeight()), color, size); 1061 } 1062 1063 static void drawTessCoordVisualization (tcu::Surface& dst, TessPrimitiveType primitiveType, const vector<Vec3>& coords) 1064 { 1065 const int imageWidth = 256; 1066 const int imageHeight = 256; 1067 const Vec2 imageSizeFloat ((float)imageWidth, (float)imageHeight); 1068 dst.setSize(imageWidth, imageHeight); 1069 1070 tcu::clear(dst.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f)); 1071 1072 for (int i = 0; i < (int)coords.size(); i++) 1073 drawTessCoordPoint(dst, primitiveType, coords[i], tcu::RGBA::white, 2); 1074 } 1075 1076 static int binarySearchFirstVec3WithXAtLeast (const vector<Vec3>& sorted, float x) 1077 { 1078 const Vec3 ref(x, 0.0f, 0.0f); 1079 const vector<Vec3>::const_iterator first = std::lower_bound(sorted.begin(), sorted.end(), ref, vec3XLessThan); 1080 if (first == sorted.end()) 1081 return -1; 1082 return (int)std::distance(sorted.begin(), first); 1083 } 1084 1085 template <typename T, typename P> 1086 static vector<T> sorted (const vector<T>& unsorted, P pred) 1087 { 1088 vector<T> result = unsorted; 1089 std::sort(result.begin(), result.end(), pred); 1090 return result; 1091 } 1092 1093 template <typename T> 1094 static vector<T> sorted (const vector<T>& unsorted) 1095 { 1096 vector<T> result = unsorted; 1097 std::sort(result.begin(), result.end()); 1098 return result; 1099 } 1100 1101 // Check that all points in subset are (approximately) present also in superset. 1102 static bool oneWayComparePointSets (TestLog& log, 1103 tcu::Surface& errorDst, 1104 TessPrimitiveType primitiveType, 1105 const vector<Vec3>& subset, 1106 const vector<Vec3>& superset, 1107 const char* subsetName, 1108 const char* supersetName, 1109 const tcu::RGBA& errorColor) 1110 { 1111 const vector<Vec3> supersetSorted = sorted(superset, vec3XLessThan); 1112 const float epsilon = 0.01f; 1113 const int maxNumFailurePrints = 5; 1114 int numFailuresDetected = 0; 1115 1116 for (int subNdx = 0; subNdx < (int)subset.size(); subNdx++) 1117 { 1118 const Vec3& subPt = subset[subNdx]; 1119 1120 bool matchFound = false; 1121 1122 { 1123 // Binary search the index of the first point in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range. 1124 const Vec3 matchMin = subPt - epsilon; 1125 const Vec3 matchMax = subPt + epsilon; 1126 int firstCandidateNdx = binarySearchFirstVec3WithXAtLeast(supersetSorted, matchMin.x()); 1127 1128 if (firstCandidateNdx >= 0) 1129 { 1130 // Compare subPt to all points in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range. 1131 for (int superNdx = firstCandidateNdx; superNdx < (int)supersetSorted.size() && supersetSorted[superNdx].x() <= matchMax.x(); superNdx++) 1132 { 1133 const Vec3& superPt = supersetSorted[superNdx]; 1134 1135 if (tcu::boolAll(tcu::greaterThanEqual (superPt, matchMin)) && 1136 tcu::boolAll(tcu::lessThanEqual (superPt, matchMax))) 1137 { 1138 matchFound = true; 1139 break; 1140 } 1141 } 1142 } 1143 } 1144 1145 if (!matchFound) 1146 { 1147 numFailuresDetected++; 1148 if (numFailuresDetected < maxNumFailurePrints) 1149 log << TestLog::Message << "Failure: no matching " << supersetName << " point found for " << subsetName << " point " << subPt << TestLog::EndMessage; 1150 else if (numFailuresDetected == maxNumFailurePrints) 1151 log << TestLog::Message << "Note: More errors follow" << TestLog::EndMessage; 1152 1153 drawTessCoordPoint(errorDst, primitiveType, subPt, errorColor, 4); 1154 } 1155 } 1156 1157 return numFailuresDetected == 0; 1158 } 1159 1160 static bool compareTessCoords (TestLog& log, TessPrimitiveType primitiveType, const vector<Vec3>& refCoords, const vector<Vec3>& resCoords) 1161 { 1162 tcu::Surface refVisual; 1163 tcu::Surface resVisual; 1164 bool success = true; 1165 1166 drawTessCoordVisualization(refVisual, primitiveType, refCoords); 1167 drawTessCoordVisualization(resVisual, primitiveType, resCoords); 1168 1169 // Check that all points in reference also exist in result. 1170 success = oneWayComparePointSets(log, refVisual, primitiveType, refCoords, resCoords, "reference", "result", tcu::RGBA::blue) && success; 1171 // Check that all points in result also exist in reference. 1172 success = oneWayComparePointSets(log, resVisual, primitiveType, resCoords, refCoords, "result", "reference", tcu::RGBA::red) && success; 1173 1174 if (!success) 1175 { 1176 log << TestLog::Message << "Note: in the following reference visualization, points that are missing in result point set are blue (if any)" << TestLog::EndMessage 1177 << TestLog::Image("RefTessCoordVisualization", "Reference tessCoord visualization", refVisual) 1178 << TestLog::Message << "Note: in the following result visualization, points that are missing in reference point set are red (if any)" << TestLog::EndMessage; 1179 } 1180 1181 log << TestLog::Image("ResTessCoordVisualization", "Result tessCoord visualization", resVisual); 1182 1183 return success; 1184 } 1185 1186 namespace VerifyFractionalSpacingSingleInternal 1187 { 1188 1189 struct Segment 1190 { 1191 int index; //!< Index of left coordinate in sortedXCoords. 1192 float length; 1193 Segment (void) : index(-1), length(-1.0f) {} 1194 Segment (int index_, float length_) : index(index_), length(length_) {} 1195 1196 static vector<float> lengths (const vector<Segment>& segments) { return members(segments, &Segment::length); } 1197 }; 1198 1199 } 1200 1201 /*--------------------------------------------------------------------*//*! 1202 * \brief Verify fractional spacing conditions for a single line 1203 * 1204 * Verify that the splitting of an edge (resulting from e.g. an isoline 1205 * with outer levels { 1.0, tessLevel }) with a given fractional spacing 1206 * mode fulfills certain conditions given in the spec. 1207 * 1208 * Note that some conditions can't be checked from just one line 1209 * (specifically, that the additional segment decreases monotonically 1210 * length and the requirement that the additional segments be placed 1211 * identically for identical values of clamped level). 1212 * 1213 * Therefore, the function stores some values to additionalSegmentLengthDst 1214 * and additionalSegmentLocationDst that can later be given to 1215 * verifyFractionalSpacingMultiple(). A negative value in length means that 1216 * no additional segments are present, i.e. there's just one segment. 1217 * A negative value in location means that the value wasn't determinable, 1218 * i.e. all segments had same length. 1219 * The values are not stored if false is returned. 1220 *//*--------------------------------------------------------------------*/ 1221 static bool verifyFractionalSpacingSingle (TestLog& log, SpacingMode spacingMode, float tessLevel, const vector<float>& coords, float& additionalSegmentLengthDst, int& additionalSegmentLocationDst) 1222 { 1223 using namespace VerifyFractionalSpacingSingleInternal; 1224 1225 DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN); 1226 1227 const float clampedLevel = getClampedTessLevel(spacingMode, tessLevel); 1228 const int finalLevel = getRoundedTessLevel(spacingMode, clampedLevel); 1229 const vector<float> sortedCoords = sorted(coords); 1230 string failNote = "Note: tessellation level is " + de::toString(tessLevel) + "\nNote: sorted coordinates are:\n " + containerStr(sortedCoords); 1231 1232 if ((int)coords.size() != finalLevel + 1) 1233 { 1234 log << TestLog::Message << "Failure: number of vertices is " << coords.size() << "; expected " << finalLevel + 1 1235 << " (clamped tessellation level is " << clampedLevel << ")" 1236 << "; final level (clamped level rounded up to " << (spacingMode == SPACINGMODE_FRACTIONAL_EVEN ? "even" : "odd") << ") is " << finalLevel 1237 << " and should equal the number of segments, i.e. number of vertices minus 1" << TestLog::EndMessage 1238 << TestLog::Message << failNote << TestLog::EndMessage; 1239 return false; 1240 } 1241 1242 if (sortedCoords[0] != 0.0f || sortedCoords.back() != 1.0f) 1243 { 1244 log << TestLog::Message << "Failure: smallest coordinate should be 0.0 and biggest should be 1.0" << TestLog::EndMessage 1245 << TestLog::Message << failNote << TestLog::EndMessage; 1246 return false; 1247 } 1248 1249 { 1250 vector<Segment> segments(finalLevel); 1251 for (int i = 0; i < finalLevel; i++) 1252 segments[i] = Segment(i, sortedCoords[i+1] - sortedCoords[i]); 1253 1254 failNote += "\nNote: segment lengths are, from left to right:\n " + containerStr(Segment::lengths(segments)); 1255 1256 { 1257 // Divide segments to two different groups based on length. 1258 1259 vector<Segment> segmentsA; 1260 vector<Segment> segmentsB; 1261 segmentsA.push_back(segments[0]); 1262 1263 for (int segNdx = 1; segNdx < (int)segments.size(); segNdx++) 1264 { 1265 const float epsilon = 0.001f; 1266 const Segment& seg = segments[segNdx]; 1267 1268 if (de::abs(seg.length - segmentsA[0].length) < epsilon) 1269 segmentsA.push_back(seg); 1270 else if (segmentsB.empty() || de::abs(seg.length - segmentsB[0].length) < epsilon) 1271 segmentsB.push_back(seg); 1272 else 1273 { 1274 log << TestLog::Message << "Failure: couldn't divide segments to 2 groups by length; " 1275 << "e.g. segment of length " << seg.length << " isn't approximately equal to either " 1276 << segmentsA[0].length << " or " << segmentsB[0].length << TestLog::EndMessage 1277 << TestLog::Message << failNote << TestLog::EndMessage; 1278 return false; 1279 } 1280 } 1281 1282 if (clampedLevel == (float)finalLevel) 1283 { 1284 // All segments should be of equal length. 1285 if (!segmentsA.empty() && !segmentsB.empty()) 1286 { 1287 log << TestLog::Message << "Failure: clamped and final tessellation level are equal, but not all segments are of equal length." << TestLog::EndMessage 1288 << TestLog::Message << failNote << TestLog::EndMessage; 1289 return false; 1290 } 1291 } 1292 1293 if (segmentsA.empty() || segmentsB.empty()) // All segments have same length. This is ok. 1294 { 1295 additionalSegmentLengthDst = segments.size() == 1 ? -1.0f : segments[0].length; 1296 additionalSegmentLocationDst = -1; 1297 return true; 1298 } 1299 1300 if (segmentsA.size() != 2 && segmentsB.size() != 2) 1301 { 1302 log << TestLog::Message << "Failure: when dividing the segments to 2 groups by length, neither of the two groups has exactly 2 or 0 segments in it" << TestLog::EndMessage 1303 << TestLog::Message << failNote << TestLog::EndMessage; 1304 return false; 1305 } 1306 1307 // For convenience, arrange so that the 2-segment group is segmentsB. 1308 if (segmentsB.size() != 2) 1309 std::swap(segmentsA, segmentsB); 1310 1311 // \note For 4-segment lines both segmentsA and segmentsB have 2 segments each. 1312 // Thus, we can't be sure which ones were meant as the additional segments. 1313 // We give the benefit of the doubt by assuming that they're the shorter 1314 // ones (as they should). 1315 1316 if (segmentsA.size() != 2) 1317 { 1318 if (segmentsB[0].length > segmentsA[0].length + 0.001f) 1319 { 1320 log << TestLog::Message << "Failure: the two additional segments are longer than the other segments" << TestLog::EndMessage 1321 << TestLog::Message << failNote << TestLog::EndMessage; 1322 return false; 1323 } 1324 } 1325 1326 // Check that the additional segments are placed symmetrically. 1327 if (segmentsB[0].index + segmentsB[1].index + 1 != (int)segments.size()) 1328 { 1329 log << TestLog::Message << "Failure: the two additional segments aren't placed symmetrically; " 1330 << "one is at index " << segmentsB[0].index << " and other is at index " << segmentsB[1].index 1331 << " (note: the two indexes should sum to " << (int)segments.size()-1 << ", i.e. numberOfSegments-1)" << TestLog::EndMessage 1332 << TestLog::Message << failNote << TestLog::EndMessage; 1333 return false; 1334 } 1335 1336 additionalSegmentLengthDst = segmentsB[0].length; 1337 if (segmentsA.size() != 2) 1338 additionalSegmentLocationDst = de::min(segmentsB[0].index, segmentsB[1].index); 1339 else 1340 additionalSegmentLocationDst = segmentsB[0].length < segmentsA[0].length - 0.001f ? de::min(segmentsB[0].index, segmentsB[1].index) 1341 : segmentsA[0].length < segmentsB[0].length - 0.001f ? de::min(segmentsA[0].index, segmentsA[1].index) 1342 : -1; // \note -1 when can't reliably decide which ones are the additional segments, a or b. 1343 return true; 1344 } 1345 } 1346 } 1347 1348 namespace VerifyFractionalSpacingMultipleInternal 1349 { 1350 1351 struct LineData 1352 { 1353 float tessLevel; 1354 float additionalSegmentLength; 1355 int additionalSegmentLocation; 1356 LineData (float lev, float len, int loc) : tessLevel(lev), additionalSegmentLength(len), additionalSegmentLocation(loc) {} 1357 }; 1358 1359 } 1360 1361 /*--------------------------------------------------------------------*//*! 1362 * \brief Verify fractional spacing conditions between multiple lines 1363 * 1364 * Verify the fractional spacing conditions that are not checked in 1365 * verifyFractionalSpacingSingle(). Uses values given by said function 1366 * as parameters, in addition to the spacing mode and tessellation level. 1367 *//*--------------------------------------------------------------------*/ 1368 static bool verifyFractionalSpacingMultiple (TestLog& log, SpacingMode spacingMode, const vector<float>& tessLevels, const vector<float>& additionalSegmentLengths, const vector<int>& additionalSegmentLocations) 1369 { 1370 using namespace VerifyFractionalSpacingMultipleInternal; 1371 1372 DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN); 1373 DE_ASSERT(tessLevels.size() == additionalSegmentLengths.size() && 1374 tessLevels.size() == additionalSegmentLocations.size()); 1375 1376 vector<LineData> lineDatas; 1377 1378 for (int i = 0; i < (int)tessLevels.size(); i++) 1379 lineDatas.push_back(LineData(tessLevels[i], additionalSegmentLengths[i], additionalSegmentLocations[i])); 1380 1381 { 1382 const vector<LineData> lineDatasSortedByLevel = sorted(lineDatas, memberPred<std::less>(&LineData::tessLevel)); 1383 1384 // Check that lines with identical clamped tessellation levels have identical additionalSegmentLocation. 1385 1386 for (int lineNdx = 1; lineNdx < (int)lineDatasSortedByLevel.size(); lineNdx++) 1387 { 1388 const LineData& curData = lineDatasSortedByLevel[lineNdx]; 1389 const LineData& prevData = lineDatasSortedByLevel[lineNdx-1]; 1390 1391 if (curData.additionalSegmentLocation < 0 || prevData.additionalSegmentLocation < 0) 1392 continue; // Unknown locations, skip. 1393 1394 if (getClampedTessLevel(spacingMode, curData.tessLevel) == getClampedTessLevel(spacingMode, prevData.tessLevel) && 1395 curData.additionalSegmentLocation != prevData.additionalSegmentLocation) 1396 { 1397 log << TestLog::Message << "Failure: additional segments not located identically for two edges with identical clamped tessellation levels" << TestLog::EndMessage 1398 << TestLog::Message << "Note: tessellation levels are " << curData.tessLevel << " and " << prevData.tessLevel 1399 << " (clamped level " << getClampedTessLevel(spacingMode, curData.tessLevel) << ")" 1400 << "; but first additional segments located at indices " 1401 << curData.additionalSegmentLocation << " and " << prevData.additionalSegmentLocation << ", respectively" << TestLog::EndMessage; 1402 return false; 1403 } 1404 } 1405 1406 // Check that, among lines with same clamped rounded tessellation level, additionalSegmentLength is monotonically decreasing with "clampedRoundedTessLevel - clampedTessLevel" (the "fraction"). 1407 1408 for (int lineNdx = 1; lineNdx < (int)lineDatasSortedByLevel.size(); lineNdx++) 1409 { 1410 const LineData& curData = lineDatasSortedByLevel[lineNdx]; 1411 const LineData& prevData = lineDatasSortedByLevel[lineNdx-1]; 1412 1413 if (curData.additionalSegmentLength < 0.0f || prevData.additionalSegmentLength < 0.0f) 1414 continue; // Unknown segment lengths, skip. 1415 1416 const float curClampedLevel = getClampedTessLevel(spacingMode, curData.tessLevel); 1417 const float prevClampedLevel = getClampedTessLevel(spacingMode, prevData.tessLevel); 1418 const int curFinalLevel = getRoundedTessLevel(spacingMode, curClampedLevel); 1419 const int prevFinalLevel = getRoundedTessLevel(spacingMode, prevClampedLevel); 1420 1421 if (curFinalLevel != prevFinalLevel) 1422 continue; 1423 1424 const float curFraction = curFinalLevel - curClampedLevel; 1425 const float prevFraction = prevFinalLevel - prevClampedLevel; 1426 1427 if (curData.additionalSegmentLength < prevData.additionalSegmentLength || 1428 (curClampedLevel == prevClampedLevel && curData.additionalSegmentLength != prevData.additionalSegmentLength)) 1429 { 1430 log << TestLog::Message << "Failure: additional segment length isn't monotonically decreasing with the fraction <n> - <f>, among edges with same final tessellation level" << TestLog::EndMessage 1431 << TestLog::Message << "Note: <f> stands for the clamped tessellation level and <n> for the final (rounded and clamped) tessellation level" << TestLog::EndMessage 1432 << TestLog::Message << "Note: two edges have tessellation levels " << prevData.tessLevel << " and " << curData.tessLevel << " respectively" 1433 << ", clamped " << prevClampedLevel << " and " << curClampedLevel << ", final " << prevFinalLevel << " and " << curFinalLevel 1434 << "; fractions are " << prevFraction << " and " << curFraction 1435 << ", but resulted in segment lengths " << prevData.additionalSegmentLength << " and " << curData.additionalSegmentLength << TestLog::EndMessage; 1436 return false; 1437 } 1438 } 1439 } 1440 1441 return true; 1442 } 1443 1444 //! Compare triangle sets, ignoring triangle order and vertex order within triangle, and possibly exclude some triangles too. 1445 template <typename IsTriangleRelevantT> 1446 static bool compareTriangleSets (const vector<Vec3>& coordsA, 1447 const vector<Vec3>& coordsB, 1448 TestLog& log, 1449 const IsTriangleRelevantT& isTriangleRelevant, 1450 const char* ignoredTriangleDescription = DE_NULL) 1451 { 1452 typedef tcu::Vector<Vec3, 3> Triangle; 1453 typedef LexCompare<Triangle, 3, VecLexLessThan<3> > TriangleLexLessThan; 1454 typedef std::set<Triangle, TriangleLexLessThan> TriangleSet; 1455 1456 DE_ASSERT(coordsA.size() % 3 == 0 && coordsB.size() % 3 == 0); 1457 1458 const int numTrianglesA = (int)coordsA.size()/3; 1459 const int numTrianglesB = (int)coordsB.size()/3; 1460 TriangleSet trianglesA; 1461 TriangleSet trianglesB; 1462 1463 for (int aOrB = 0; aOrB < 2; aOrB++) 1464 { 1465 const vector<Vec3>& coords = aOrB == 0 ? coordsA : coordsB; 1466 const int numTriangles = aOrB == 0 ? numTrianglesA : numTrianglesB; 1467 TriangleSet& triangles = aOrB == 0 ? trianglesA : trianglesB; 1468 1469 for (int triNdx = 0; triNdx < numTriangles; triNdx++) 1470 { 1471 Triangle triangle(coords[3*triNdx + 0], 1472 coords[3*triNdx + 1], 1473 coords[3*triNdx + 2]); 1474 1475 if (isTriangleRelevant(triangle.getPtr())) 1476 { 1477 std::sort(triangle.getPtr(), triangle.getPtr()+3, VecLexLessThan<3>()); 1478 triangles.insert(triangle); 1479 } 1480 } 1481 } 1482 1483 { 1484 TriangleSet::const_iterator aIt = trianglesA.begin(); 1485 TriangleSet::const_iterator bIt = trianglesB.begin(); 1486 1487 while (aIt != trianglesA.end() || bIt != trianglesB.end()) 1488 { 1489 const bool aEnd = aIt == trianglesA.end(); 1490 const bool bEnd = bIt == trianglesB.end(); 1491 1492 if (aEnd || bEnd || *aIt != *bIt) 1493 { 1494 log << TestLog::Message << "Failure: triangle sets in two cases are not equal (when ignoring triangle and vertex order" 1495 << (ignoredTriangleDescription == DE_NULL ? "" : string() + ", and " + ignoredTriangleDescription) << ")" << TestLog::EndMessage; 1496 1497 if (!aEnd && (bEnd || TriangleLexLessThan()(*aIt, *bIt))) 1498 log << TestLog::Message << "Note: e.g. triangle " << *aIt << " exists for first case but not for second" << TestLog::EndMessage; 1499 else 1500 log << TestLog::Message << "Note: e.g. triangle " << *bIt << " exists for second case but not for first" << TestLog::EndMessage; 1501 1502 return false; 1503 } 1504 1505 ++aIt; 1506 ++bIt; 1507 } 1508 1509 return true; 1510 } 1511 } 1512 1513 static bool compareTriangleSets (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, TestLog& log) 1514 { 1515 return compareTriangleSets(coordsA, coordsB, log, ConstantUnaryPredicate<const Vec3*, true>()); 1516 } 1517 1518 static void checkExtensionSupport (Context& context, const char* extName) 1519 { 1520 if (!context.getContextInfo().isExtensionSupported(extName)) 1521 throw tcu::NotSupportedError(string(extName) + " not supported"); 1522 } 1523 1524 static void checkTessellationSupport (Context& context) 1525 { 1526 checkExtensionSupport(context, "GL_EXT_tessellation_shader"); 1527 } 1528 1529 // Draw primitives with shared edges and check that no cracks are visible at the shared edges. 1530 class CommonEdgeCase : public TestCase 1531 { 1532 public: 1533 enum CaseType 1534 { 1535 CASETYPE_BASIC = 0, //!< Order patch vertices such that when two patches share a vertex, it's at the same index for both. 1536 CASETYPE_PRECISE, //!< Vertex indices don't match like for CASETYPE_BASIC, but other measures are taken, using the 'precise' qualifier. 1537 1538 CASETYPE_LAST 1539 }; 1540 1541 CommonEdgeCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing, CaseType caseType) 1542 : TestCase (context, name, description) 1543 , m_primitiveType (primitiveType) 1544 , m_spacing (spacing) 1545 , m_caseType (caseType) 1546 { 1547 DE_ASSERT(m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES || m_primitiveType == TESSPRIMITIVETYPE_QUADS); 1548 } 1549 1550 void init (void); 1551 void deinit (void); 1552 IterateResult iterate (void); 1553 1554 private: 1555 static const int RENDER_SIZE = 256; 1556 1557 const TessPrimitiveType m_primitiveType; 1558 const SpacingMode m_spacing; 1559 const CaseType m_caseType; 1560 1561 SharedPtr<const ShaderProgram> m_program; 1562 }; 1563 1564 void CommonEdgeCase::init (void) 1565 { 1566 checkTessellationSupport(m_context); 1567 1568 if (m_caseType == CASETYPE_PRECISE) 1569 checkExtensionSupport(m_context, "GL_EXT_gpu_shader5"); 1570 1571 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE); 1572 1573 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() 1574 << glu::VertexSource ("#version 310 es\n" 1575 "\n" 1576 "in highp vec2 in_v_position;\n" 1577 "in highp float in_v_tessParam;\n" 1578 "\n" 1579 "out highp vec2 in_tc_position;\n" 1580 "out highp float in_tc_tessParam;\n" 1581 "\n" 1582 "void main (void)\n" 1583 "{\n" 1584 " in_tc_position = in_v_position;\n" 1585 " in_tc_tessParam = in_v_tessParam;\n" 1586 "}\n") 1587 1588 << glu::TessellationControlSource ("#version 310 es\n" 1589 "#extension GL_EXT_tessellation_shader : require\n" 1590 + string(m_caseType == CASETYPE_PRECISE ? "#extension GL_EXT_gpu_shader5 : require\n" : "") + 1591 "\n" 1592 "layout (vertices = " + string(m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "3" : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? "4" : DE_NULL) + ") out;\n" 1593 "\n" 1594 "in highp vec2 in_tc_position[];\n" 1595 "in highp float in_tc_tessParam[];\n" 1596 "\n" 1597 "out highp vec2 in_te_position[];\n" 1598 "\n" 1599 + (m_caseType == CASETYPE_PRECISE ? "precise gl_TessLevelOuter;\n\n" : "") + 1600 "void main (void)\n" 1601 "{\n" 1602 " in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];\n" 1603 "\n" 1604 " gl_TessLevelInner[0] = 5.0;\n" 1605 " gl_TessLevelInner[1] = 5.0;\n" 1606 "\n" 1607 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 1608 " gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[2]);\n" 1609 " gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[0]);\n" 1610 " gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[1]);\n" 1611 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 1612 " gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[2]);\n" 1613 " gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[0]);\n" 1614 " gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[3] + in_tc_tessParam[1]);\n" 1615 " gl_TessLevelOuter[3] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[3]);\n" 1616 : DE_NULL) + 1617 "}\n") 1618 1619 << glu::TessellationEvaluationSource ("#version 310 es\n" 1620 "#extension GL_EXT_tessellation_shader : require\n" 1621 + string(m_caseType == CASETYPE_PRECISE ? "#extension GL_EXT_gpu_shader5 : require\n" : "") + 1622 "\n" 1623 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing) + 1624 "\n" 1625 "in highp vec2 in_te_position[];\n" 1626 "\n" 1627 "out mediump vec4 in_f_color;\n" 1628 "\n" 1629 + (m_caseType == CASETYPE_PRECISE ? "precise gl_Position;\n\n" : "") + 1630 "void main (void)\n" 1631 "{\n" 1632 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 1633 string(m_caseType == CASETYPE_PRECISE 1634 ? "\t// Note: when this is an edge vertex, at most two of the following terms are non-zero (so order doesn't matter)\n" 1635 : "") + 1636 " highp vec2 pos = gl_TessCoord.x*in_te_position[0] + gl_TessCoord.y*in_te_position[1] + gl_TessCoord.z*in_te_position[2];\n" 1637 "\n" 1638 " highp float f = sqrt(3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z))) * 0.5 + 0.5;\n" 1639 " in_f_color = vec4(gl_TessCoord*f, 1.0);\n" 1640 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 1641 string() 1642 + (m_caseType == CASETYPE_BASIC ? 1643 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0]\n" 1644 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1]\n" 1645 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[2]\n" 1646 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[3];\n" 1647 : m_caseType == CASETYPE_PRECISE ? 1648 " highp vec2 a = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0];\n" 1649 " highp vec2 b = ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1];\n" 1650 " highp vec2 c = (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[2];\n" 1651 " highp vec2 d = ( gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[3];\n" 1652 " // Note: when this is an edge vertex, at most two of the following terms are non-zero (so order doesn't matter)\n" 1653 " highp vec2 pos = a+b+c+d;\n" 1654 : DE_NULL) + 1655 "\n" 1656 " highp float f = sqrt(1.0 - 2.0 * max(abs(gl_TessCoord.x - 0.5), abs(gl_TessCoord.y - 0.5)))*0.5 + 0.5;\n" 1657 " in_f_color = vec4(0.1, gl_TessCoord.xy*f, 1.0);\n" 1658 : DE_NULL) + 1659 "\n" 1660 " // Offset the position slightly, based on the parity of the bits in the float representation.\n" 1661 " // This is done to detect possible small differences in edge vertex positions between patches.\n" 1662 " uvec2 bits = floatBitsToUint(pos);\n" 1663 " uint numBits = 0u;\n" 1664 " for (uint i = 0u; i < 32u; i++)\n" 1665 " numBits += ((bits[0] >> i) & 1u) + ((bits[1] >> i) & 1u);\n" 1666 " pos += float(numBits&1u)*0.04;\n" 1667 "\n" 1668 " gl_Position = vec4(pos, 0.0, 1.0);\n" 1669 "}\n") 1670 1671 << glu::FragmentSource ("#version 310 es\n" 1672 "\n" 1673 "layout (location = 0) out mediump vec4 o_color;\n" 1674 "\n" 1675 "in mediump vec4 in_f_color;\n" 1676 "\n" 1677 "void main (void)\n" 1678 "{\n" 1679 " o_color = in_f_color;\n" 1680 "}\n"))); 1681 1682 m_testCtx.getLog() << *m_program; 1683 if (!m_program->isOk()) 1684 TCU_FAIL("Program compilation failed"); 1685 } 1686 1687 void CommonEdgeCase::deinit (void) 1688 { 1689 m_program.clear(); 1690 } 1691 1692 CommonEdgeCase::IterateResult CommonEdgeCase::iterate (void) 1693 { 1694 TestLog& log = m_testCtx.getLog(); 1695 const RenderContext& renderCtx = m_context.getRenderContext(); 1696 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName())); 1697 const deUint32 programGL = m_program->getProgram(); 1698 const glw::Functions& gl = renderCtx.getFunctions(); 1699 1700 const int gridWidth = 4; 1701 const int gridHeight = 4; 1702 const int numVertices = (gridWidth+1)*(gridHeight+1); 1703 const int numIndices = gridWidth*gridHeight * (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3*2 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4 : -1); 1704 const int numPosCompsPerVertex = 2; 1705 const int totalNumPosComps = numPosCompsPerVertex*numVertices; 1706 vector<float> gridPosComps; 1707 vector<float> gridTessParams; 1708 vector<deUint16> gridIndices; 1709 1710 gridPosComps.reserve(totalNumPosComps); 1711 gridTessParams.reserve(numVertices); 1712 gridIndices.reserve(numIndices); 1713 1714 { 1715 Random constantSeedRnd(42); 1716 1717 for (int i = 0; i < gridHeight+1; i++) 1718 for (int j = 0; j < gridWidth+1; j++) 1719 { 1720 gridPosComps.push_back(-1.0f + 2.0f * ((float)j + 0.5f) / (float)(gridWidth+1)); 1721 gridPosComps.push_back(-1.0f + 2.0f * ((float)i + 0.5f) / (float)(gridHeight+1)); 1722 gridTessParams.push_back((float)(i*(gridWidth+1) + j) / (float)(numVertices-1)); 1723 } 1724 } 1725 1726 // Generate patch vertex indices. 1727 // \note If CASETYPE_BASIC, the vertices are ordered such that when multiple 1728 // triangles/quads share a vertex, it's at the same index for everyone. 1729 1730 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 1731 { 1732 for (int i = 0; i < gridHeight; i++) 1733 for (int j = 0; j < gridWidth; j++) 1734 { 1735 const int corners[4] = 1736 { 1737 (i+0)*(gridWidth+1) + j+0, 1738 (i+0)*(gridWidth+1) + j+1, 1739 (i+1)*(gridWidth+1) + j+0, 1740 (i+1)*(gridWidth+1) + j+1 1741 }; 1742 1743 const int secondTriangleVertexIndexOffset = m_caseType == CASETYPE_BASIC ? 0 1744 : m_caseType == CASETYPE_PRECISE ? 1 1745 : -1; 1746 DE_ASSERT(secondTriangleVertexIndexOffset != -1); 1747 1748 for (int k = 0; k < 3; k++) 1749 gridIndices.push_back(corners[(k+0 + i + (2-j%3)) % 3]); 1750 for (int k = 0; k < 3; k++) 1751 gridIndices.push_back(corners[(k+2 + i + (2-j%3) + secondTriangleVertexIndexOffset) % 3 + 1]); 1752 } 1753 } 1754 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS) 1755 { 1756 for (int i = 0; i < gridHeight; i++) 1757 for (int j = 0; j < gridWidth; j++) 1758 { 1759 // \note The vertices are ordered such that when multiple quads 1760 // share a vertices, it's at the same index for everyone. 1761 for (int m = 0; m < 2; m++) 1762 for (int n = 0; n < 2; n++) 1763 gridIndices.push_back((i+(i+m)%2)*(gridWidth+1) + j+(j+n)%2); 1764 1765 if(m_caseType == CASETYPE_PRECISE && (i+j) % 2 == 0) 1766 std::reverse(gridIndices.begin() + (gridIndices.size() - 4), 1767 gridIndices.begin() + gridIndices.size()); 1768 } 1769 } 1770 else 1771 DE_ASSERT(false); 1772 1773 DE_ASSERT((int)gridPosComps.size() == totalNumPosComps); 1774 DE_ASSERT((int)gridTessParams.size() == numVertices); 1775 DE_ASSERT((int)gridIndices.size() == numIndices); 1776 1777 setViewport(gl, viewport); 1778 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 1779 gl.useProgram(programGL); 1780 1781 { 1782 gl.patchParameteri(GL_PATCH_VERTICES, m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4 : -1); 1783 gl.clear(GL_COLOR_BUFFER_BIT); 1784 1785 const glu::VertexArrayBinding attrBindings[] = 1786 { 1787 glu::va::Float("in_v_position", numPosCompsPerVertex, numVertices, 0, &gridPosComps[0]), 1788 glu::va::Float("in_v_tessParam", 1, numVertices, 0, &gridTessParams[0]) 1789 }; 1790 1791 glu::draw(renderCtx, programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0], 1792 glu::pr::Patches((int)gridIndices.size(), &gridIndices[0])); 1793 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed"); 1794 } 1795 1796 { 1797 const tcu::Surface rendered = getPixels(renderCtx, viewport); 1798 1799 log << TestLog::Image("RenderedImage", "Rendered Image", rendered) 1800 << TestLog::Message << "Note: coloring is done to clarify the positioning and orientation of the " 1801 << (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "triangles" : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? "quads" : DE_NULL) 1802 << "; the color of a vertex corresponds to the index of that vertex in the patch" 1803 << TestLog::EndMessage; 1804 1805 if (m_caseType == CASETYPE_BASIC) 1806 log << TestLog::Message << "Note: each shared vertex has the same index among the primitives it belongs to" << TestLog::EndMessage; 1807 else if (m_caseType == CASETYPE_PRECISE) 1808 log << TestLog::Message << "Note: the 'precise' qualifier is used to avoid cracks between primitives" << TestLog::EndMessage; 1809 else 1810 DE_ASSERT(false); 1811 1812 // Ad-hoc result verification - check that a certain rectangle in the image contains no black pixels. 1813 1814 const int startX = (int)(0.15f * (float)rendered.getWidth()); 1815 const int endX = (int)(0.85f * (float)rendered.getWidth()); 1816 const int startY = (int)(0.15f * (float)rendered.getHeight()); 1817 const int endY = (int)(0.85f * (float)rendered.getHeight()); 1818 1819 for (int y = startY; y < endY; y++) 1820 for (int x = startX; x < endX; x++) 1821 { 1822 const tcu::RGBA pixel = rendered.getPixel(x, y); 1823 1824 if (pixel.getRed() == 0 && pixel.getGreen() == 0 && pixel.getBlue() == 0) 1825 { 1826 log << TestLog::Message << "Failure: there seem to be cracks in the rendered result" << TestLog::EndMessage 1827 << TestLog::Message << "Note: pixel with zero r, g and b channels found at " << tcu::IVec2(x, y) << TestLog::EndMessage; 1828 1829 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); 1830 return STOP; 1831 } 1832 } 1833 1834 log << TestLog::Message << "Success: there seem to be no cracks in the rendered result" << TestLog::EndMessage; 1835 } 1836 1837 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 1838 return STOP; 1839 } 1840 1841 // Check tessellation coordinates (read with transform feedback). 1842 class TessCoordCase : public TestCase 1843 { 1844 public: 1845 TessCoordCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing) 1846 : TestCase (context, name, description) 1847 , m_primitiveType (primitiveType) 1848 , m_spacing (spacing) 1849 { 1850 } 1851 1852 void init (void); 1853 void deinit (void); 1854 IterateResult iterate (void); 1855 1856 private: 1857 struct TessLevels 1858 { 1859 float inner[2]; 1860 float outer[4]; 1861 }; 1862 1863 static const int RENDER_SIZE = 16; 1864 1865 vector<TessLevels> genTessLevelCases (void) const; 1866 1867 const TessPrimitiveType m_primitiveType; 1868 const SpacingMode m_spacing; 1869 1870 SharedPtr<const ShaderProgram> m_program; 1871 }; 1872 1873 void TessCoordCase::init (void) 1874 { 1875 checkTessellationSupport(m_context); 1876 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE); 1877 1878 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() 1879 << glu::VertexSource ("#version 310 es\n" 1880 "\n" 1881 "void main (void)\n" 1882 "{\n" 1883 "}\n") 1884 1885 << glu::TessellationControlSource ("#version 310 es\n" 1886 "#extension GL_EXT_tessellation_shader : require\n" 1887 "\n" 1888 "layout (vertices = 1) out;\n" 1889 "\n" 1890 "uniform mediump float u_tessLevelInner0;\n" 1891 "uniform mediump float u_tessLevelInner1;\n" 1892 "\n" 1893 "uniform mediump float u_tessLevelOuter0;\n" 1894 "uniform mediump float u_tessLevelOuter1;\n" 1895 "uniform mediump float u_tessLevelOuter2;\n" 1896 "uniform mediump float u_tessLevelOuter3;\n" 1897 "\n" 1898 "void main (void)\n" 1899 "{\n" 1900 " gl_TessLevelInner[0] = u_tessLevelInner0;\n" 1901 " gl_TessLevelInner[1] = u_tessLevelInner1;\n" 1902 "\n" 1903 " gl_TessLevelOuter[0] = u_tessLevelOuter0;\n" 1904 " gl_TessLevelOuter[1] = u_tessLevelOuter1;\n" 1905 " gl_TessLevelOuter[2] = u_tessLevelOuter2;\n" 1906 " gl_TessLevelOuter[3] = u_tessLevelOuter3;\n" 1907 "}\n") 1908 1909 << glu::TessellationEvaluationSource ("#version 310 es\n" 1910 "#extension GL_EXT_tessellation_shader : require\n" 1911 "\n" 1912 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, true) + 1913 "\n" 1914 "out highp vec3 out_te_tessCoord;\n" 1915 "\n" 1916 "void main (void)\n" 1917 "{\n" 1918 " out_te_tessCoord = gl_TessCoord;\n" 1919 " gl_Position = vec4(gl_TessCoord.xy*1.6 - 0.8, 0.0, 1.0);\n" 1920 "}\n") 1921 1922 << glu::FragmentSource ("#version 310 es\n" 1923 "\n" 1924 "layout (location = 0) out mediump vec4 o_color;\n" 1925 "\n" 1926 "void main (void)\n" 1927 "{\n" 1928 " o_color = vec4(1.0);\n" 1929 "}\n") 1930 1931 << glu::TransformFeedbackVarying ("out_te_tessCoord") 1932 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS))); 1933 1934 m_testCtx.getLog() << *m_program; 1935 if (!m_program->isOk()) 1936 TCU_FAIL("Program compilation failed"); 1937 } 1938 1939 void TessCoordCase::deinit (void) 1940 { 1941 m_program.clear(); 1942 } 1943 1944 vector<TessCoordCase::TessLevels> TessCoordCase::genTessLevelCases (void) const 1945 { 1946 static const TessLevels rawTessLevelCases[] = 1947 { 1948 { { 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, 1949 { { 63.0f, 24.0f }, { 15.0f, 42.0f, 10.0f, 12.0f } }, 1950 { { 3.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } }, 1951 { { 4.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } }, 1952 { { 2.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } }, 1953 { { 5.0f, 6.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, 1954 { { 1.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } }, 1955 { { 5.0f, 1.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } }, 1956 { { 5.2f, 1.6f }, { 2.9f, 3.4f, 1.5f, 4.1f } } 1957 }; 1958 1959 if (m_spacing == SPACINGMODE_EQUAL) 1960 return vector<TessLevels>(DE_ARRAY_BEGIN(rawTessLevelCases), DE_ARRAY_END(rawTessLevelCases)); 1961 else 1962 { 1963 vector<TessLevels> result; 1964 result.reserve(DE_LENGTH_OF_ARRAY(rawTessLevelCases)); 1965 1966 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(rawTessLevelCases); tessLevelCaseNdx++) 1967 { 1968 TessLevels curTessLevelCase = rawTessLevelCases[tessLevelCaseNdx]; 1969 1970 float* const inner = &curTessLevelCase.inner[0]; 1971 float* const outer = &curTessLevelCase.outer[0]; 1972 1973 for (int j = 0; j < 2; j++) inner[j] = (float)getClampedRoundedTessLevel(m_spacing, inner[j]); 1974 for (int j = 0; j < 4; j++) outer[j] = (float)getClampedRoundedTessLevel(m_spacing, outer[j]); 1975 1976 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 1977 { 1978 if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f) 1979 { 1980 if (inner[0] == 1.0f) 1981 inner[0] = (float)getClampedRoundedTessLevel(m_spacing, inner[0] + 0.1f); 1982 } 1983 } 1984 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS) 1985 { 1986 if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f || outer[3] > 1.0f) 1987 { 1988 if (inner[0] == 1.0f) inner[0] = (float)getClampedRoundedTessLevel(m_spacing, inner[0] + 0.1f); 1989 if (inner[1] == 1.0f) inner[1] = (float)getClampedRoundedTessLevel(m_spacing, inner[1] + 0.1f); 1990 } 1991 } 1992 1993 result.push_back(curTessLevelCase); 1994 } 1995 1996 DE_ASSERT((int)result.size() == DE_LENGTH_OF_ARRAY(rawTessLevelCases)); 1997 return result; 1998 } 1999 } 2000 2001 TessCoordCase::IterateResult TessCoordCase::iterate (void) 2002 { 2003 typedef TransformFeedbackHandler<Vec3> TFHandler; 2004 2005 TestLog& log = m_testCtx.getLog(); 2006 const RenderContext& renderCtx = m_context.getRenderContext(); 2007 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName())); 2008 const deUint32 programGL = m_program->getProgram(); 2009 const glw::Functions& gl = renderCtx.getFunctions(); 2010 2011 const int tessLevelInner0Loc = gl.getUniformLocation(programGL, "u_tessLevelInner0"); 2012 const int tessLevelInner1Loc = gl.getUniformLocation(programGL, "u_tessLevelInner1"); 2013 const int tessLevelOuter0Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter0"); 2014 const int tessLevelOuter1Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter1"); 2015 const int tessLevelOuter2Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter2"); 2016 const int tessLevelOuter3Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter3"); 2017 2018 const vector<TessLevels> tessLevelCases = genTessLevelCases(); 2019 vector<vector<Vec3> > caseReferences (tessLevelCases.size()); 2020 2021 for (int i = 0; i < (int)tessLevelCases.size(); i++) 2022 caseReferences[i] = generateReferenceTessCoords(m_primitiveType, m_spacing, &tessLevelCases[i].inner[0], &tessLevelCases[i].outer[0]); 2023 2024 const int maxNumVertices = (int)std::max_element(caseReferences.begin(), caseReferences.end(), SizeLessThan<vector<Vec3> >())->size(); 2025 const TFHandler tfHandler (m_context.getRenderContext(), maxNumVertices); 2026 2027 bool success = true; 2028 2029 setViewport(gl, viewport); 2030 gl.useProgram(programGL); 2031 2032 gl.patchParameteri(GL_PATCH_VERTICES, 1); 2033 2034 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < (int)tessLevelCases.size(); tessLevelCaseNdx++) 2035 { 2036 const float* const innerLevels = &tessLevelCases[tessLevelCaseNdx].inner[0]; 2037 const float* const outerLevels = &tessLevelCases[tessLevelCaseNdx].outer[0]; 2038 2039 log << TestLog::Message << "Tessellation levels: " << tessellationLevelsString(innerLevels, outerLevels, m_primitiveType) << TestLog::EndMessage; 2040 2041 gl.uniform1f(tessLevelInner0Loc, innerLevels[0]); 2042 gl.uniform1f(tessLevelInner1Loc, innerLevels[1]); 2043 gl.uniform1f(tessLevelOuter0Loc, outerLevels[0]); 2044 gl.uniform1f(tessLevelOuter1Loc, outerLevels[1]); 2045 gl.uniform1f(tessLevelOuter2Loc, outerLevels[2]); 2046 gl.uniform1f(tessLevelOuter3Loc, outerLevels[3]); 2047 GLU_EXPECT_NO_ERROR(gl.getError(), "Setup failed"); 2048 2049 { 2050 const vector<Vec3>& tessCoordsRef = caseReferences[tessLevelCaseNdx]; 2051 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, GL_POINTS, 0, DE_NULL, 1); 2052 2053 if (tfResult.numPrimitives != (int)tessCoordsRef.size()) 2054 { 2055 log << TestLog::Message << "Failure: GL reported GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN to be " 2056 << tfResult.numPrimitives << ", reference value is " << tessCoordsRef.size() 2057 << " (logging further info anyway)" << TestLog::EndMessage; 2058 success = false; 2059 } 2060 else 2061 log << TestLog::Message << "Note: GL reported GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN to be " << tfResult.numPrimitives << TestLog::EndMessage; 2062 2063 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 2064 log << TestLog::Message << "Note: in the following visualization(s), the u=1, v=1, w=1 corners are at the right, top, and left corners, respectively" << TestLog::EndMessage; 2065 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS || m_primitiveType == TESSPRIMITIVETYPE_ISOLINES) 2066 log << TestLog::Message << "Note: in the following visualization(s), u and v coordinate go left-to-right and bottom-to-top, respectively" << TestLog::EndMessage; 2067 else 2068 DE_ASSERT(false); 2069 2070 success = compareTessCoords(log, m_primitiveType, tessCoordsRef, tfResult.varying) && success; 2071 } 2072 2073 if (!success) 2074 break; 2075 else 2076 log << TestLog::Message << "All OK" << TestLog::EndMessage; 2077 } 2078 2079 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Invalid tessellation coordinates"); 2080 return STOP; 2081 } 2082 2083 // Check validity of fractional spacing modes. Draws a single isoline, reads tesscoords with transform feedback. 2084 class FractionalSpacingModeCase : public TestCase 2085 { 2086 public: 2087 FractionalSpacingModeCase (Context& context, const char* name, const char* description, SpacingMode spacing) 2088 : TestCase (context, name, description) 2089 , m_spacing (spacing) 2090 { 2091 DE_ASSERT(m_spacing == SPACINGMODE_FRACTIONAL_EVEN || m_spacing == SPACINGMODE_FRACTIONAL_ODD); 2092 } 2093 2094 void init (void); 2095 void deinit (void); 2096 IterateResult iterate (void); 2097 2098 private: 2099 static const int RENDER_SIZE = 16; 2100 2101 static vector<float> genTessLevelCases (void); 2102 2103 const SpacingMode m_spacing; 2104 2105 SharedPtr<const ShaderProgram> m_program; 2106 }; 2107 2108 void FractionalSpacingModeCase::init (void) 2109 { 2110 checkTessellationSupport(m_context); 2111 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE); 2112 2113 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() 2114 << glu::VertexSource ("#version 310 es\n" 2115 "\n" 2116 "void main (void)\n" 2117 "{\n" 2118 "}\n") 2119 2120 << glu::TessellationControlSource ("#version 310 es\n" 2121 "#extension GL_EXT_tessellation_shader : require\n" 2122 "\n" 2123 "layout (vertices = 1) out;\n" 2124 "\n" 2125 "uniform mediump float u_tessLevelOuter1;\n" 2126 "\n" 2127 "void main (void)\n" 2128 "{\n" 2129 " gl_TessLevelOuter[0] = 1.0;\n" 2130 " gl_TessLevelOuter[1] = u_tessLevelOuter1;\n" 2131 "}\n") 2132 2133 << glu::TessellationEvaluationSource ("#version 310 es\n" 2134 "#extension GL_EXT_tessellation_shader : require\n" 2135 "\n" 2136 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_ISOLINES, m_spacing, true) + 2137 "\n" 2138 "out highp float out_te_tessCoord;\n" 2139 "\n" 2140 "void main (void)\n" 2141 "{\n" 2142 " out_te_tessCoord = gl_TessCoord.x;\n" 2143 " gl_Position = vec4(gl_TessCoord.xy*1.6 - 0.8, 0.0, 1.0);\n" 2144 "}\n") 2145 2146 << glu::FragmentSource ("#version 310 es\n" 2147 "\n" 2148 "layout (location = 0) out mediump vec4 o_color;\n" 2149 "\n" 2150 "void main (void)\n" 2151 "{\n" 2152 " o_color = vec4(1.0);\n" 2153 "}\n") 2154 2155 << glu::TransformFeedbackVarying ("out_te_tessCoord") 2156 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS))); 2157 2158 m_testCtx.getLog() << *m_program; 2159 if (!m_program->isOk()) 2160 TCU_FAIL("Program compilation failed"); 2161 } 2162 2163 void FractionalSpacingModeCase::deinit (void) 2164 { 2165 m_program.clear(); 2166 } 2167 2168 vector<float> FractionalSpacingModeCase::genTessLevelCases (void) 2169 { 2170 vector<float> result; 2171 2172 // Ranges [7.0 .. 8.0), [8.0 .. 9.0) and [9.0 .. 10.0) 2173 { 2174 static const float rangeStarts[] = { 7.0f, 8.0f, 9.0f }; 2175 const int numSamplesPerRange = 10; 2176 2177 for (int rangeNdx = 0; rangeNdx < DE_LENGTH_OF_ARRAY(rangeStarts); rangeNdx++) 2178 for (int i = 0; i < numSamplesPerRange; i++) 2179 result.push_back(rangeStarts[rangeNdx] + (float)i/(float)numSamplesPerRange); 2180 } 2181 2182 // 0.3, 1.3, 2.3, ... , 62.3 2183 for (int i = 0; i <= 62; i++) 2184 result.push_back((float)i + 0.3f); 2185 2186 return result; 2187 } 2188 2189 FractionalSpacingModeCase::IterateResult FractionalSpacingModeCase::iterate (void) 2190 { 2191 typedef TransformFeedbackHandler<float> TFHandler; 2192 2193 TestLog& log = m_testCtx.getLog(); 2194 const RenderContext& renderCtx = m_context.getRenderContext(); 2195 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName())); 2196 const deUint32 programGL = m_program->getProgram(); 2197 const glw::Functions& gl = renderCtx.getFunctions(); 2198 2199 const int tessLevelOuter1Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter1"); 2200 2201 // Second outer tessellation levels. 2202 const vector<float> tessLevelCases = genTessLevelCases(); 2203 const int maxNumVertices = 1 + getClampedRoundedTessLevel(m_spacing, *std::max_element(tessLevelCases.begin(), tessLevelCases.end())); 2204 vector<float> additionalSegmentLengths; 2205 vector<int> additionalSegmentLocations; 2206 2207 const TFHandler tfHandler (m_context.getRenderContext(), maxNumVertices); 2208 2209 bool success = true; 2210 2211 setViewport(gl, viewport); 2212 gl.useProgram(programGL); 2213 2214 gl.patchParameteri(GL_PATCH_VERTICES, 1); 2215 2216 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < (int)tessLevelCases.size(); tessLevelCaseNdx++) 2217 { 2218 const float outerLevel1 = tessLevelCases[tessLevelCaseNdx]; 2219 2220 gl.uniform1f(tessLevelOuter1Loc, outerLevel1); 2221 GLU_EXPECT_NO_ERROR(gl.getError(), "Setup failed"); 2222 2223 { 2224 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, GL_POINTS, 0, DE_NULL, 1); 2225 float additionalSegmentLength; 2226 int additionalSegmentLocation; 2227 2228 success = verifyFractionalSpacingSingle(log, m_spacing, outerLevel1, tfResult.varying, 2229 additionalSegmentLength, additionalSegmentLocation); 2230 2231 if (!success) 2232 break; 2233 2234 additionalSegmentLengths.push_back(additionalSegmentLength); 2235 additionalSegmentLocations.push_back(additionalSegmentLocation); 2236 } 2237 } 2238 2239 if (success) 2240 success = verifyFractionalSpacingMultiple(log, m_spacing, tessLevelCases, additionalSegmentLengths, additionalSegmentLocations); 2241 2242 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Invalid tessellation coordinates"); 2243 return STOP; 2244 } 2245 2246 // Base class for a case with one input attribute (in_v_position) and optionally a TCS; tests with a couple of different sets of tessellation levels. 2247 class BasicVariousTessLevelsPosAttrCase : public TestCase 2248 { 2249 public: 2250 BasicVariousTessLevelsPosAttrCase (Context& context, 2251 const char* name, 2252 const char* description, 2253 TessPrimitiveType primitiveType, 2254 SpacingMode spacing, 2255 const char* referenceImagePathPrefix) 2256 : TestCase (context, name, description) 2257 , m_primitiveType (primitiveType) 2258 , m_spacing (spacing) 2259 , m_referenceImagePathPrefix (referenceImagePathPrefix) 2260 { 2261 } 2262 2263 void init (void); 2264 void deinit (void); 2265 IterateResult iterate (void); 2266 2267 protected: 2268 virtual const glu::ProgramSources makeSources (TessPrimitiveType, SpacingMode, const char* vtxOutPosAttrName) const = DE_NULL; 2269 2270 private: 2271 static const int RENDER_SIZE = 256; 2272 2273 const TessPrimitiveType m_primitiveType; 2274 const SpacingMode m_spacing; 2275 const string m_referenceImagePathPrefix; 2276 2277 SharedPtr<const ShaderProgram> m_program; 2278 }; 2279 2280 void BasicVariousTessLevelsPosAttrCase::init (void) 2281 { 2282 checkTessellationSupport(m_context); 2283 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE); 2284 2285 { 2286 glu::ProgramSources sources = makeSources(m_primitiveType, m_spacing, "in_tc_position"); 2287 DE_ASSERT(sources.sources[glu::SHADERTYPE_TESSELLATION_CONTROL].empty()); 2288 2289 sources << glu::TessellationControlSource( "#version 310 es\n" 2290 "#extension GL_EXT_tessellation_shader : require\n" 2291 "\n" 2292 "layout (vertices = " + string(m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "3" : "4") + ") out;\n" 2293 "\n" 2294 "in highp vec2 in_tc_position[];\n" 2295 "\n" 2296 "out highp vec2 in_te_position[];\n" 2297 "\n" 2298 "uniform mediump float u_tessLevelInner0;\n" 2299 "uniform mediump float u_tessLevelInner1;\n" 2300 "uniform mediump float u_tessLevelOuter0;\n" 2301 "uniform mediump float u_tessLevelOuter1;\n" 2302 "uniform mediump float u_tessLevelOuter2;\n" 2303 "uniform mediump float u_tessLevelOuter3;\n" 2304 "\n" 2305 "void main (void)\n" 2306 "{\n" 2307 " in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];\n" 2308 "\n" 2309 " gl_TessLevelInner[0] = u_tessLevelInner0;\n" 2310 " gl_TessLevelInner[1] = u_tessLevelInner1;\n" 2311 "\n" 2312 " gl_TessLevelOuter[0] = u_tessLevelOuter0;\n" 2313 " gl_TessLevelOuter[1] = u_tessLevelOuter1;\n" 2314 " gl_TessLevelOuter[2] = u_tessLevelOuter2;\n" 2315 " gl_TessLevelOuter[3] = u_tessLevelOuter3;\n" 2316 "}\n"); 2317 2318 m_program = SharedPtr<const ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources)); 2319 } 2320 2321 m_testCtx.getLog() << *m_program; 2322 if (!m_program->isOk()) 2323 TCU_FAIL("Program compilation failed"); 2324 } 2325 2326 void BasicVariousTessLevelsPosAttrCase::deinit (void) 2327 { 2328 m_program.clear(); 2329 } 2330 2331 BasicVariousTessLevelsPosAttrCase::IterateResult BasicVariousTessLevelsPosAttrCase::iterate (void) 2332 { 2333 static const struct 2334 { 2335 float inner[2]; 2336 float outer[4]; 2337 } tessLevelCases[] = 2338 { 2339 { { 9.0f, 9.0f }, { 9.0f, 9.0f, 9.0f, 9.0f } }, 2340 { { 8.0f, 11.0f }, { 13.0f, 15.0f, 18.0f, 21.0f } }, 2341 { { 17.0f, 14.0f }, { 3.0f, 6.0f, 9.0f, 12.0f } } 2342 }; 2343 2344 TestLog& log = m_testCtx.getLog(); 2345 const RenderContext& renderCtx = m_context.getRenderContext(); 2346 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName())); 2347 const deUint32 programGL = m_program->getProgram(); 2348 const glw::Functions& gl = renderCtx.getFunctions(); 2349 const int patchSize = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 2350 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4 2351 : m_primitiveType == TESSPRIMITIVETYPE_ISOLINES ? 4 2352 : -1; 2353 2354 setViewport(gl, viewport); 2355 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 2356 gl.useProgram(programGL); 2357 2358 gl.patchParameteri(GL_PATCH_VERTICES, patchSize); 2359 2360 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(tessLevelCases); tessLevelCaseNdx++) 2361 { 2362 float innerLevels[2]; 2363 float outerLevels[4]; 2364 2365 for (int i = 0; i < DE_LENGTH_OF_ARRAY(innerLevels); i++) 2366 innerLevels[i] = (float)getClampedRoundedTessLevel(m_spacing, tessLevelCases[tessLevelCaseNdx].inner[i]); 2367 2368 for (int i = 0; i < DE_LENGTH_OF_ARRAY(outerLevels); i++) 2369 outerLevels[i] = (float)getClampedRoundedTessLevel(m_spacing, tessLevelCases[tessLevelCaseNdx].outer[i]); 2370 2371 log << TestLog::Message << "Tessellation levels: " << tessellationLevelsString(&innerLevels[0], &outerLevels[0], m_primitiveType) << TestLog::EndMessage; 2372 2373 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelInner0"), innerLevels[0]); 2374 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelInner1"), innerLevels[1]); 2375 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter0"), outerLevels[0]); 2376 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter1"), outerLevels[1]); 2377 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter2"), outerLevels[2]); 2378 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter3"), outerLevels[3]); 2379 2380 gl.clear(GL_COLOR_BUFFER_BIT); 2381 2382 { 2383 vector<Vec2> positions; 2384 positions.reserve(4); 2385 2386 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 2387 { 2388 positions.push_back(Vec2( 0.8f, 0.6f)); 2389 positions.push_back(Vec2( 0.0f, -0.786f)); 2390 positions.push_back(Vec2(-0.8f, 0.6f)); 2391 } 2392 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS || m_primitiveType == TESSPRIMITIVETYPE_ISOLINES) 2393 { 2394 positions.push_back(Vec2(-0.8f, -0.8f)); 2395 positions.push_back(Vec2( 0.8f, -0.8f)); 2396 positions.push_back(Vec2(-0.8f, 0.8f)); 2397 positions.push_back(Vec2( 0.8f, 0.8f)); 2398 } 2399 else 2400 DE_ASSERT(false); 2401 2402 DE_ASSERT((int)positions.size() == patchSize); 2403 2404 const glu::VertexArrayBinding attrBindings[] = 2405 { 2406 glu::va::Float("in_v_position", 2, (int)positions.size(), 0, &positions[0].x()) 2407 }; 2408 2409 glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0], 2410 glu::pr::Patches(patchSize)); 2411 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed"); 2412 } 2413 2414 { 2415 const tcu::Surface rendered = getPixels(renderCtx, viewport); 2416 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePathPrefix + "_" + de::toString(tessLevelCaseNdx) + ".png"); 2417 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.002f, tcu::COMPARE_LOG_RESULT); 2418 2419 if (!success) 2420 { 2421 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); 2422 return STOP; 2423 } 2424 } 2425 } 2426 2427 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 2428 return STOP; 2429 } 2430 2431 // Test that there are no obvious gaps in the triangulation of a tessellated triangle or quad. 2432 class BasicTriangleFillCoverCase : public BasicVariousTessLevelsPosAttrCase 2433 { 2434 public: 2435 BasicTriangleFillCoverCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing, const char* referenceImagePathPrefix) 2436 : BasicVariousTessLevelsPosAttrCase (context, name, description, primitiveType, spacing, referenceImagePathPrefix) 2437 { 2438 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS); 2439 } 2440 2441 protected: 2442 const glu::ProgramSources makeSources (TessPrimitiveType primitiveType, SpacingMode spacing, const char* vtxOutPosAttrName) const 2443 { 2444 return glu::ProgramSources() 2445 << glu::VertexSource ("#version 310 es\n" 2446 "\n" 2447 "in highp vec2 in_v_position;\n" 2448 "\n" 2449 "out highp vec2 " + string(vtxOutPosAttrName) + ";\n" 2450 "\n" 2451 "void main (void)\n" 2452 "{\n" 2453 " " + vtxOutPosAttrName + " = in_v_position;\n" 2454 "}\n") 2455 2456 << glu::TessellationEvaluationSource ("#version 310 es\n" 2457 "#extension GL_EXT_tessellation_shader : require\n" 2458 "\n" 2459 + getTessellationEvaluationInLayoutString(primitiveType, spacing) + 2460 "\n" 2461 "in highp vec2 in_te_position[];\n" 2462 "\n" 2463 "void main (void)\n" 2464 "{\n" 2465 + (primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 2466 "\n" 2467 " highp float d = 3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z));\n" 2468 " highp vec2 corner0 = in_te_position[0];\n" 2469 " highp vec2 corner1 = in_te_position[1];\n" 2470 " highp vec2 corner2 = in_te_position[2];\n" 2471 " highp vec2 pos = corner0*gl_TessCoord.x + corner1*gl_TessCoord.y + corner2*gl_TessCoord.z;\n" 2472 " highp vec2 fromCenter = pos - (corner0 + corner1 + corner2) / 3.0;\n" 2473 " highp float f = (1.0 - length(fromCenter)) * (1.5 - d);\n" 2474 " pos += 0.75 * f * fromCenter / (length(fromCenter) + 0.3);\n" 2475 " gl_Position = vec4(pos, 0.0, 1.0);\n" 2476 : primitiveType == TESSPRIMITIVETYPE_QUADS ? 2477 " highp vec2 corner0 = in_te_position[0];\n" 2478 " highp vec2 corner1 = in_te_position[1];\n" 2479 " highp vec2 corner2 = in_te_position[2];\n" 2480 " highp vec2 corner3 = in_te_position[3];\n" 2481 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n" 2482 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n" 2483 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n" 2484 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n" 2485 " highp float d = 2.0 * min(abs(gl_TessCoord.x-0.5), abs(gl_TessCoord.y-0.5));\n" 2486 " highp vec2 fromCenter = pos - (corner0 + corner1 + corner2 + corner3) / 4.0;\n" 2487 " highp float f = (1.0 - length(fromCenter)) * sqrt(1.7 - d);\n" 2488 " pos += 0.75 * f * fromCenter / (length(fromCenter) + 0.3);\n" 2489 " gl_Position = vec4(pos, 0.0, 1.0);\n" 2490 : DE_NULL) + 2491 "}\n") 2492 2493 << glu::FragmentSource ("#version 310 es\n" 2494 "\n" 2495 "layout (location = 0) out mediump vec4 o_color;\n" 2496 "\n" 2497 "void main (void)\n" 2498 "{\n" 2499 " o_color = vec4(1.0);\n" 2500 "}\n"); 2501 } 2502 }; 2503 2504 // Check that there are no obvious overlaps in the triangulation of a tessellated triangle or quad. 2505 class BasicTriangleFillNonOverlapCase : public BasicVariousTessLevelsPosAttrCase 2506 { 2507 public: 2508 BasicTriangleFillNonOverlapCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing, const char* referenceImagePathPrefix) 2509 : BasicVariousTessLevelsPosAttrCase (context, name, description, primitiveType, spacing, referenceImagePathPrefix) 2510 { 2511 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS); 2512 } 2513 2514 protected: 2515 const glu::ProgramSources makeSources (TessPrimitiveType primitiveType, SpacingMode spacing, const char* vtxOutPosAttrName) const 2516 { 2517 return glu::ProgramSources() 2518 << glu::VertexSource ("#version 310 es\n" 2519 "\n" 2520 "in highp vec2 in_v_position;\n" 2521 "\n" 2522 "out highp vec2 " + string(vtxOutPosAttrName) + ";\n" 2523 "\n" 2524 "void main (void)\n" 2525 "{\n" 2526 " " + vtxOutPosAttrName + " = in_v_position;\n" 2527 "}\n") 2528 2529 << glu::TessellationEvaluationSource ("#version 310 es\n" 2530 "#extension GL_EXT_tessellation_shader : require\n" 2531 "\n" 2532 + getTessellationEvaluationInLayoutString(primitiveType, spacing) + 2533 "\n" 2534 "in highp vec2 in_te_position[];\n" 2535 "\n" 2536 "out mediump vec4 in_f_color;\n" 2537 "\n" 2538 "uniform mediump float u_tessLevelInner0;\n" 2539 "uniform mediump float u_tessLevelInner1;\n" 2540 "\n" 2541 "void main (void)\n" 2542 "{\n" 2543 + (primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 2544 "\n" 2545 " highp vec2 corner0 = in_te_position[0];\n" 2546 " highp vec2 corner1 = in_te_position[1];\n" 2547 " highp vec2 corner2 = in_te_position[2];\n" 2548 " highp vec2 pos = corner0*gl_TessCoord.x + corner1*gl_TessCoord.y + corner2*gl_TessCoord.z;\n" 2549 " gl_Position = vec4(pos, 0.0, 1.0);\n" 2550 " highp int numConcentricTriangles = int(round(u_tessLevelInner0)) / 2 + 1;\n" 2551 " highp float d = 3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z));\n" 2552 " highp int phase = int(d*float(numConcentricTriangles)) % 3;\n" 2553 " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n" 2554 " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n" 2555 " : vec4(0.0, 0.0, 1.0, 1.0);\n" 2556 : primitiveType == TESSPRIMITIVETYPE_QUADS ? 2557 " highp vec2 corner0 = in_te_position[0];\n" 2558 " highp vec2 corner1 = in_te_position[1];\n" 2559 " highp vec2 corner2 = in_te_position[2];\n" 2560 " highp vec2 corner3 = in_te_position[3];\n" 2561 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n" 2562 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n" 2563 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n" 2564 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n" 2565 " gl_Position = vec4(pos, 0.0, 1.0);\n" 2566 " highp int phaseX = int(round((0.5 - abs(gl_TessCoord.x-0.5)) * u_tessLevelInner0));\n" 2567 " highp int phaseY = int(round((0.5 - abs(gl_TessCoord.y-0.5)) * u_tessLevelInner1));\n" 2568 " highp int phase = min(phaseX, phaseY) % 3;\n" 2569 " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n" 2570 " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n" 2571 " : vec4(0.0, 0.0, 1.0, 1.0);\n" 2572 : DE_NULL) + 2573 "}\n") 2574 2575 << glu::FragmentSource ("#version 310 es\n" 2576 "\n" 2577 "layout (location = 0) out mediump vec4 o_color;\n" 2578 "\n" 2579 "in mediump vec4 in_f_color;\n" 2580 "\n" 2581 "void main (void)\n" 2582 "{\n" 2583 " o_color = in_f_color;\n" 2584 "}\n"); 2585 } 2586 }; 2587 2588 // Basic isolines rendering case. 2589 class IsolinesRenderCase : public BasicVariousTessLevelsPosAttrCase 2590 { 2591 public: 2592 IsolinesRenderCase (Context& context, const char* name, const char* description, SpacingMode spacing, const char* referenceImagePathPrefix) 2593 : BasicVariousTessLevelsPosAttrCase (context, name, description, TESSPRIMITIVETYPE_ISOLINES, spacing, referenceImagePathPrefix) 2594 { 2595 } 2596 2597 protected: 2598 const glu::ProgramSources makeSources (TessPrimitiveType primitiveType, SpacingMode spacing, const char* vtxOutPosAttrName) const 2599 { 2600 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_ISOLINES); 2601 DE_UNREF(primitiveType); 2602 2603 return glu::ProgramSources() 2604 << glu::VertexSource ("#version 310 es\n" 2605 "\n" 2606 "in highp vec2 in_v_position;\n" 2607 "\n" 2608 "out highp vec2 " + string(vtxOutPosAttrName) + ";\n" 2609 "\n" 2610 "void main (void)\n" 2611 "{\n" 2612 " " + vtxOutPosAttrName + " = in_v_position;\n" 2613 "}\n") 2614 2615 << glu::TessellationEvaluationSource ("#version 310 es\n" 2616 "#extension GL_EXT_tessellation_shader : require\n" 2617 "\n" 2618 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_ISOLINES, spacing) + 2619 "\n" 2620 "in highp vec2 in_te_position[];\n" 2621 "\n" 2622 "out mediump vec4 in_f_color;\n" 2623 "\n" 2624 "uniform mediump float u_tessLevelOuter0;\n" 2625 "uniform mediump float u_tessLevelOuter1;\n" 2626 "\n" 2627 "void main (void)\n" 2628 "{\n" 2629 " highp vec2 corner0 = in_te_position[0];\n" 2630 " highp vec2 corner1 = in_te_position[1];\n" 2631 " highp vec2 corner2 = in_te_position[2];\n" 2632 " highp vec2 corner3 = in_te_position[3];\n" 2633 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n" 2634 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n" 2635 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n" 2636 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n" 2637 " pos.y += 0.15*sin(gl_TessCoord.x*10.0);\n" 2638 " gl_Position = vec4(pos, 0.0, 1.0);\n" 2639 " highp int phaseX = int(round(gl_TessCoord.x*u_tessLevelOuter1));\n" 2640 " highp int phaseY = int(round(gl_TessCoord.y*u_tessLevelOuter0));\n" 2641 " highp int phase = (phaseX + phaseY) % 3;\n" 2642 " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n" 2643 " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n" 2644 " : vec4(0.0, 0.0, 1.0, 1.0);\n" 2645 "}\n") 2646 2647 << glu::FragmentSource ("#version 310 es\n" 2648 "\n" 2649 "layout (location = 0) out mediump vec4 o_color;\n" 2650 "\n" 2651 "in mediump vec4 in_f_color;\n" 2652 "\n" 2653 "void main (void)\n" 2654 "{\n" 2655 " o_color = in_f_color;\n" 2656 "}\n"); 2657 } 2658 }; 2659 2660 // Test the "cw" and "ccw" TES input layout qualifiers. 2661 class WindingCase : public TestCase 2662 { 2663 public: 2664 WindingCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, Winding winding) 2665 : TestCase (context, name, description) 2666 , m_primitiveType (primitiveType) 2667 , m_winding (winding) 2668 { 2669 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS); 2670 } 2671 2672 void init (void); 2673 void deinit (void); 2674 IterateResult iterate (void); 2675 2676 private: 2677 static const int RENDER_SIZE = 64; 2678 2679 const TessPrimitiveType m_primitiveType; 2680 const Winding m_winding; 2681 2682 SharedPtr<const ShaderProgram> m_program; 2683 }; 2684 2685 void WindingCase::init (void) 2686 { 2687 checkTessellationSupport(m_context); 2688 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE); 2689 2690 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() 2691 << glu::VertexSource ("#version 310 es\n" 2692 "\n" 2693 "void main (void)\n" 2694 "{\n" 2695 "}\n") 2696 2697 << glu::TessellationControlSource ("#version 310 es\n" 2698 "#extension GL_EXT_tessellation_shader : require\n" 2699 "\n" 2700 "layout (vertices = 1) out;\n" 2701 "\n" 2702 "void main (void)\n" 2703 "{\n" 2704 " gl_TessLevelInner[0] = 5.0;\n" 2705 " gl_TessLevelInner[1] = 5.0;\n" 2706 "\n" 2707 " gl_TessLevelOuter[0] = 5.0;\n" 2708 " gl_TessLevelOuter[1] = 5.0;\n" 2709 " gl_TessLevelOuter[2] = 5.0;\n" 2710 " gl_TessLevelOuter[3] = 5.0;\n" 2711 "}\n") 2712 2713 << glu::TessellationEvaluationSource ("#version 310 es\n" 2714 "#extension GL_EXT_tessellation_shader : require\n" 2715 "\n" 2716 + getTessellationEvaluationInLayoutString(m_primitiveType, m_winding) + 2717 "\n" 2718 "void main (void)\n" 2719 "{\n" 2720 " gl_Position = vec4(gl_TessCoord.xy*2.0 - 1.0, 0.0, 1.0);\n" 2721 "}\n") 2722 2723 << glu::FragmentSource ("#version 310 es\n" 2724 "\n" 2725 "layout (location = 0) out mediump vec4 o_color;\n" 2726 "\n" 2727 "void main (void)\n" 2728 "{\n" 2729 " o_color = vec4(1.0);\n" 2730 "}\n"))); 2731 2732 m_testCtx.getLog() << *m_program; 2733 if (!m_program->isOk()) 2734 TCU_FAIL("Program compilation failed"); 2735 } 2736 2737 void WindingCase::deinit (void) 2738 { 2739 m_program.clear(); 2740 } 2741 2742 WindingCase::IterateResult WindingCase::iterate (void) 2743 { 2744 TestLog& log = m_testCtx.getLog(); 2745 const RenderContext& renderCtx = m_context.getRenderContext(); 2746 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName())); 2747 const deUint32 programGL = m_program->getProgram(); 2748 const glw::Functions& gl = renderCtx.getFunctions(); 2749 const glu::VertexArray vao (renderCtx); 2750 2751 bool success = true; 2752 2753 setViewport(gl, viewport); 2754 gl.clearColor(1.0f, 0.0f, 0.0f, 1.0f); 2755 gl.useProgram(programGL); 2756 2757 gl.patchParameteri(GL_PATCH_VERTICES, 1); 2758 2759 gl.enable(GL_CULL_FACE); 2760 2761 gl.bindVertexArray(*vao); 2762 2763 log << TestLog::Message << "Face culling enabled" << TestLog::EndMessage; 2764 2765 for (int frontFaceWinding = 0; frontFaceWinding < WINDING_LAST; frontFaceWinding++) 2766 { 2767 log << TestLog::Message << "Setting glFrontFace(" << (frontFaceWinding == WINDING_CW ? "GL_CW" : "GL_CCW") << ")" << TestLog::EndMessage; 2768 2769 gl.frontFace(frontFaceWinding == WINDING_CW ? GL_CW : GL_CCW); 2770 2771 gl.clear(GL_COLOR_BUFFER_BIT); 2772 gl.drawArrays(GL_PATCHES, 0, 1); 2773 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed"); 2774 2775 { 2776 const tcu::Surface rendered = getPixels(renderCtx, viewport); 2777 log << TestLog::Image("RenderedImage", "Rendered Image", rendered); 2778 2779 { 2780 const int totalNumPixels = rendered.getWidth()*rendered.getHeight(); 2781 const int badPixelTolerance = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 5*de::max(rendered.getWidth(), rendered.getHeight()) : 0; 2782 2783 int numWhitePixels = 0; 2784 int numRedPixels = 0; 2785 for (int y = 0; y < rendered.getHeight(); y++) 2786 for (int x = 0; x < rendered.getWidth(); x++) 2787 { 2788 numWhitePixels += rendered.getPixel(x, y) == tcu::RGBA::white ? 1 : 0; 2789 numRedPixels += rendered.getPixel(x, y) == tcu::RGBA::red ? 1 : 0; 2790 } 2791 2792 DE_ASSERT(numWhitePixels + numRedPixels <= totalNumPixels); 2793 2794 log << TestLog::Message << "Note: got " << numWhitePixels << " white and " << numRedPixels << " red pixels" << TestLog::EndMessage; 2795 2796 if (totalNumPixels - numWhitePixels - numRedPixels > badPixelTolerance) 2797 { 2798 log << TestLog::Message << "Failure: Got " << totalNumPixels - numWhitePixels - numRedPixels << " other than white or red pixels (maximum tolerance " << badPixelTolerance << ")" << TestLog::EndMessage; 2799 success = false; 2800 break; 2801 } 2802 2803 if ((Winding)frontFaceWinding == m_winding) 2804 { 2805 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 2806 { 2807 if (de::abs(numWhitePixels - totalNumPixels/2) > badPixelTolerance) 2808 { 2809 log << TestLog::Message << "Failure: wrong number of white pixels; expected approximately " << totalNumPixels/2 << TestLog::EndMessage; 2810 success = false; 2811 break; 2812 } 2813 } 2814 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS) 2815 { 2816 if (numWhitePixels != totalNumPixels) 2817 { 2818 log << TestLog::Message << "Failure: expected only white pixels (full-viewport quad)" << TestLog::EndMessage; 2819 success = false; 2820 break; 2821 } 2822 } 2823 else 2824 DE_ASSERT(false); 2825 } 2826 else 2827 { 2828 if (numWhitePixels != 0) 2829 { 2830 log << TestLog::Message << "Failure: expected only red pixels (everything culled)" << TestLog::EndMessage; 2831 success = false; 2832 break; 2833 } 2834 } 2835 } 2836 } 2837 } 2838 2839 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image verification failed"); 2840 return STOP; 2841 } 2842 2843 // Test potentially differing input and output patch sizes. 2844 class PatchVertexCountCase : public TestCase 2845 { 2846 public: 2847 PatchVertexCountCase (Context& context, const char* name, const char* description, int inputPatchSize, int outputPatchSize, const char* referenceImagePath) 2848 : TestCase (context, name, description) 2849 , m_inputPatchSize (inputPatchSize) 2850 , m_outputPatchSize (outputPatchSize) 2851 , m_referenceImagePath (referenceImagePath) 2852 { 2853 } 2854 2855 void init (void); 2856 void deinit (void); 2857 IterateResult iterate (void); 2858 2859 private: 2860 static const int RENDER_SIZE = 256; 2861 2862 const int m_inputPatchSize; 2863 const int m_outputPatchSize; 2864 2865 const string m_referenceImagePath; 2866 2867 SharedPtr<const ShaderProgram> m_program; 2868 }; 2869 2870 void PatchVertexCountCase::init (void) 2871 { 2872 checkTessellationSupport(m_context); 2873 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE); 2874 2875 const string inSizeStr = de::toString(m_inputPatchSize); 2876 const string outSizeStr = de::toString(m_outputPatchSize); 2877 2878 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() 2879 << glu::VertexSource ("#version 310 es\n" 2880 "\n" 2881 "in highp float in_v_attr;\n" 2882 "\n" 2883 "out highp float in_tc_attr;\n" 2884 "\n" 2885 "void main (void)\n" 2886 "{\n" 2887 " in_tc_attr = in_v_attr;\n" 2888 "}\n") 2889 2890 << glu::TessellationControlSource ("#version 310 es\n" 2891 "#extension GL_EXT_tessellation_shader : require\n" 2892 "\n" 2893 "layout (vertices = " + outSizeStr + ") out;\n" 2894 "\n" 2895 "in highp float in_tc_attr[];\n" 2896 "\n" 2897 "out highp float in_te_attr[];\n" 2898 "\n" 2899 "void main (void)\n" 2900 "{\n" 2901 " in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID*" + inSizeStr + "/" + outSizeStr + "];\n" 2902 "\n" 2903 " gl_TessLevelInner[0] = 5.0;\n" 2904 " gl_TessLevelInner[1] = 5.0;\n" 2905 "\n" 2906 " gl_TessLevelOuter[0] = 5.0;\n" 2907 " gl_TessLevelOuter[1] = 5.0;\n" 2908 " gl_TessLevelOuter[2] = 5.0;\n" 2909 " gl_TessLevelOuter[3] = 5.0;\n" 2910 "}\n") 2911 2912 << glu::TessellationEvaluationSource ("#version 310 es\n" 2913 "#extension GL_EXT_tessellation_shader : require\n" 2914 "\n" 2915 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_QUADS) + 2916 "\n" 2917 "in highp float in_te_attr[];\n" 2918 "\n" 2919 "out mediump vec4 in_f_color;\n" 2920 "\n" 2921 "void main (void)\n" 2922 "{\n" 2923 " highp float x = gl_TessCoord.x*2.0 - 1.0;\n" 2924 " highp float y = gl_TessCoord.y - in_te_attr[int(round(gl_TessCoord.x*float(" + outSizeStr + "-1)))];\n" 2925 " gl_Position = vec4(x, y, 0.0, 1.0);\n" 2926 " in_f_color = vec4(1.0);\n" 2927 "}\n") 2928 2929 << glu::FragmentSource ("#version 310 es\n" 2930 "\n" 2931 "layout (location = 0) out mediump vec4 o_color;\n" 2932 "\n" 2933 "in mediump vec4 in_f_color;\n" 2934 "\n" 2935 "void main (void)\n" 2936 "{\n" 2937 " o_color = in_f_color;\n" 2938 "}\n"))); 2939 2940 m_testCtx.getLog() << *m_program; 2941 if (!m_program->isOk()) 2942 TCU_FAIL("Program compilation failed"); 2943 } 2944 2945 void PatchVertexCountCase::deinit (void) 2946 { 2947 m_program.clear(); 2948 } 2949 2950 PatchVertexCountCase::IterateResult PatchVertexCountCase::iterate (void) 2951 { 2952 TestLog& log = m_testCtx.getLog(); 2953 const RenderContext& renderCtx = m_context.getRenderContext(); 2954 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName())); 2955 const deUint32 programGL = m_program->getProgram(); 2956 const glw::Functions& gl = renderCtx.getFunctions(); 2957 2958 setViewport(gl, viewport); 2959 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 2960 gl.useProgram(programGL); 2961 2962 log << TestLog::Message << "Note: input patch size is " << m_inputPatchSize << ", output patch size is " << m_outputPatchSize << TestLog::EndMessage; 2963 2964 { 2965 vector<float> attributeData; 2966 attributeData.reserve(m_inputPatchSize); 2967 2968 for (int i = 0; i < m_inputPatchSize; i++) 2969 { 2970 const float f = (float)i / (float)(m_inputPatchSize-1); 2971 attributeData.push_back(f*f); 2972 } 2973 2974 gl.patchParameteri(GL_PATCH_VERTICES, m_inputPatchSize); 2975 gl.clear(GL_COLOR_BUFFER_BIT); 2976 2977 const glu::VertexArrayBinding attrBindings[] = 2978 { 2979 glu::va::Float("in_v_attr", 1, (int)attributeData.size(), 0, &attributeData[0]) 2980 }; 2981 2982 glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0], 2983 glu::pr::Patches(m_inputPatchSize)); 2984 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed"); 2985 } 2986 2987 { 2988 const tcu::Surface rendered = getPixels(renderCtx, viewport); 2989 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath); 2990 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT); 2991 2992 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image comparison failed"); 2993 return STOP; 2994 } 2995 } 2996 2997 // Test per-patch inputs/outputs. 2998 class PerPatchDataCase : public TestCase 2999 { 3000 public: 3001 enum CaseType 3002 { 3003 CASETYPE_PRIMITIVE_ID_TCS = 0, 3004 CASETYPE_PRIMITIVE_ID_TES, 3005 CASETYPE_PATCH_VERTICES_IN_TCS, 3006 CASETYPE_PATCH_VERTICES_IN_TES, 3007 CASETYPE_TESS_LEVEL_INNER0_TES, 3008 CASETYPE_TESS_LEVEL_INNER1_TES, 3009 CASETYPE_TESS_LEVEL_OUTER0_TES, 3010 CASETYPE_TESS_LEVEL_OUTER1_TES, 3011 CASETYPE_TESS_LEVEL_OUTER2_TES, 3012 CASETYPE_TESS_LEVEL_OUTER3_TES, 3013 3014 CASETYPE_LAST 3015 }; 3016 3017 PerPatchDataCase (Context& context, const char* name, const char* description, CaseType caseType, const char* referenceImagePath) 3018 : TestCase (context, name, description) 3019 , m_caseType (caseType) 3020 , m_referenceImagePath (caseTypeUsesRefImageFromFile(caseType) ? referenceImagePath : "") 3021 { 3022 DE_ASSERT(caseTypeUsesRefImageFromFile(caseType) == (referenceImagePath != DE_NULL)); 3023 } 3024 3025 void init (void); 3026 void deinit (void); 3027 IterateResult iterate (void); 3028 3029 static const char* getCaseTypeName (CaseType); 3030 static const char* getCaseTypeDescription (CaseType); 3031 static bool caseTypeUsesRefImageFromFile (CaseType); 3032 3033 private: 3034 static const int RENDER_SIZE = 256; 3035 static const int INPUT_PATCH_SIZE; 3036 static const int OUTPUT_PATCH_SIZE; 3037 3038 const CaseType m_caseType; 3039 const string m_referenceImagePath; 3040 3041 SharedPtr<const ShaderProgram> m_program; 3042 }; 3043 3044 const int PerPatchDataCase::INPUT_PATCH_SIZE = 10; 3045 const int PerPatchDataCase::OUTPUT_PATCH_SIZE = 5; 3046 3047 const char* PerPatchDataCase::getCaseTypeName (CaseType type) 3048 { 3049 switch (type) 3050 { 3051 case CASETYPE_PRIMITIVE_ID_TCS: return "primitive_id_tcs"; 3052 case CASETYPE_PRIMITIVE_ID_TES: return "primitive_id_tes"; 3053 case CASETYPE_PATCH_VERTICES_IN_TCS: return "patch_vertices_in_tcs"; 3054 case CASETYPE_PATCH_VERTICES_IN_TES: return "patch_vertices_in_tes"; 3055 case CASETYPE_TESS_LEVEL_INNER0_TES: return "tess_level_inner_0_tes"; 3056 case CASETYPE_TESS_LEVEL_INNER1_TES: return "tess_level_inner_1_tes"; 3057 case CASETYPE_TESS_LEVEL_OUTER0_TES: return "tess_level_outer_0_tes"; 3058 case CASETYPE_TESS_LEVEL_OUTER1_TES: return "tess_level_outer_1_tes"; 3059 case CASETYPE_TESS_LEVEL_OUTER2_TES: return "tess_level_outer_2_tes"; 3060 case CASETYPE_TESS_LEVEL_OUTER3_TES: return "tess_level_outer_3_tes"; 3061 default: 3062 DE_ASSERT(false); 3063 return DE_NULL; 3064 } 3065 } 3066 3067 const char* PerPatchDataCase::getCaseTypeDescription (CaseType type) 3068 { 3069 switch (type) 3070 { 3071 case CASETYPE_PRIMITIVE_ID_TCS: return "Read gl_PrimitiveID in TCS and pass it as patch output to TES"; 3072 case CASETYPE_PRIMITIVE_ID_TES: return "Read gl_PrimitiveID in TES"; 3073 case CASETYPE_PATCH_VERTICES_IN_TCS: return "Read gl_PatchVerticesIn in TCS and pass it as patch output to TES"; 3074 case CASETYPE_PATCH_VERTICES_IN_TES: return "Read gl_PatchVerticesIn in TES"; 3075 case CASETYPE_TESS_LEVEL_INNER0_TES: return "Read gl_TessLevelInner[0] in TES"; 3076 case CASETYPE_TESS_LEVEL_INNER1_TES: return "Read gl_TessLevelInner[1] in TES"; 3077 case CASETYPE_TESS_LEVEL_OUTER0_TES: return "Read gl_TessLevelOuter[0] in TES"; 3078 case CASETYPE_TESS_LEVEL_OUTER1_TES: return "Read gl_TessLevelOuter[1] in TES"; 3079 case CASETYPE_TESS_LEVEL_OUTER2_TES: return "Read gl_TessLevelOuter[2] in TES"; 3080 case CASETYPE_TESS_LEVEL_OUTER3_TES: return "Read gl_TessLevelOuter[3] in TES"; 3081 default: 3082 DE_ASSERT(false); 3083 return DE_NULL; 3084 } 3085 } 3086 3087 bool PerPatchDataCase::caseTypeUsesRefImageFromFile (CaseType type) 3088 { 3089 switch (type) 3090 { 3091 case CASETYPE_PRIMITIVE_ID_TCS: 3092 case CASETYPE_PRIMITIVE_ID_TES: 3093 return true; 3094 3095 default: 3096 return false; 3097 } 3098 } 3099 3100 void PerPatchDataCase::init (void) 3101 { 3102 checkTessellationSupport(m_context); 3103 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE); 3104 3105 DE_ASSERT(OUTPUT_PATCH_SIZE < INPUT_PATCH_SIZE); 3106 3107 const string inSizeStr = de::toString(INPUT_PATCH_SIZE); 3108 const string outSizeStr = de::toString(OUTPUT_PATCH_SIZE); 3109 3110 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() 3111 << glu::VertexSource ("#version 310 es\n" 3112 "\n" 3113 "in highp float in_v_attr;\n" 3114 "\n" 3115 "out highp float in_tc_attr;\n" 3116 "\n" 3117 "void main (void)\n" 3118 "{\n" 3119 " in_tc_attr = in_v_attr;\n" 3120 "}\n") 3121 3122 << glu::TessellationControlSource ("#version 310 es\n" 3123 "#extension GL_EXT_tessellation_shader : require\n" 3124 "\n" 3125 "layout (vertices = " + outSizeStr + ") out;\n" 3126 "\n" 3127 "in highp float in_tc_attr[];\n" 3128 "\n" 3129 "out highp float in_te_attr[];\n" 3130 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS ? "patch out mediump int in_te_primitiveIDFromTCS;\n" 3131 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "patch out mediump int in_te_patchVerticesInFromTCS;\n" 3132 : "") + 3133 "\n" 3134 "void main (void)\n" 3135 "{\n" 3136 " in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID];\n" 3137 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS ? "\tin_te_primitiveIDFromTCS = gl_PrimitiveID;\n" 3138 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "\tin_te_patchVerticesInFromTCS = gl_PatchVerticesIn;\n" 3139 : "") + 3140 "\n" 3141 " gl_TessLevelInner[0] = 9.0;\n" 3142 " gl_TessLevelInner[1] = 8.0;\n" 3143 "\n" 3144 " gl_TessLevelOuter[0] = 7.0;\n" 3145 " gl_TessLevelOuter[1] = 6.0;\n" 3146 " gl_TessLevelOuter[2] = 5.0;\n" 3147 " gl_TessLevelOuter[3] = 4.0;\n" 3148 "}\n") 3149 3150 << glu::TessellationEvaluationSource ("#version 310 es\n" 3151 "#extension GL_EXT_tessellation_shader : require\n" 3152 "\n" 3153 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_QUADS) + 3154 "\n" 3155 "in highp float in_te_attr[];\n" 3156 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS ? "patch in mediump int in_te_primitiveIDFromTCS;\n" 3157 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "patch in mediump int in_te_patchVerticesInFromTCS;\n" 3158 : string()) + 3159 "\n" 3160 "out mediump vec4 in_f_color;\n" 3161 "\n" 3162 "uniform highp float u_xScale;\n" 3163 "\n" 3164 "void main (void)\n" 3165 "{\n" 3166 " highp float x = (gl_TessCoord.x*u_xScale + in_te_attr[0]) * 2.0 - 1.0;\n" 3167 " highp float y = gl_TessCoord.y*2.0 - 1.0;\n" 3168 " gl_Position = vec4(x, y, 0.0, 1.0);\n" 3169 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS ? "\tbool ok = in_te_primitiveIDFromTCS == 3;\n" 3170 : m_caseType == CASETYPE_PRIMITIVE_ID_TES ? "\tbool ok = gl_PrimitiveID == 3;\n" 3171 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "\tbool ok = in_te_patchVerticesInFromTCS == " + inSizeStr + ";\n" 3172 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TES ? "\tbool ok = gl_PatchVerticesIn == " + outSizeStr + ";\n" 3173 : m_caseType == CASETYPE_TESS_LEVEL_INNER0_TES ? "\tbool ok = abs(gl_TessLevelInner[0] - 9.0) < 0.1f;\n" 3174 : m_caseType == CASETYPE_TESS_LEVEL_INNER1_TES ? "\tbool ok = abs(gl_TessLevelInner[1] - 8.0) < 0.1f;\n" 3175 : m_caseType == CASETYPE_TESS_LEVEL_OUTER0_TES ? "\tbool ok = abs(gl_TessLevelOuter[0] - 7.0) < 0.1f;\n" 3176 : m_caseType == CASETYPE_TESS_LEVEL_OUTER1_TES ? "\tbool ok = abs(gl_TessLevelOuter[1] - 6.0) < 0.1f;\n" 3177 : m_caseType == CASETYPE_TESS_LEVEL_OUTER2_TES ? "\tbool ok = abs(gl_TessLevelOuter[2] - 5.0) < 0.1f;\n" 3178 : m_caseType == CASETYPE_TESS_LEVEL_OUTER3_TES ? "\tbool ok = abs(gl_TessLevelOuter[3] - 4.0) < 0.1f;\n" 3179 : DE_NULL) + 3180 " in_f_color = ok ? vec4(1.0) : vec4(vec3(0.0), 1.0);\n" 3181 "}\n") 3182 3183 << glu::FragmentSource ("#version 310 es\n" 3184 "\n" 3185 "layout (location = 0) out mediump vec4 o_color;\n" 3186 "\n" 3187 "in mediump vec4 in_f_color;\n" 3188 "\n" 3189 "void main (void)\n" 3190 "{\n" 3191 " o_color = in_f_color;\n" 3192 "}\n"))); 3193 3194 m_testCtx.getLog() << *m_program; 3195 if (!m_program->isOk()) 3196 TCU_FAIL("Program compilation failed"); 3197 } 3198 3199 void PerPatchDataCase::deinit (void) 3200 { 3201 m_program.clear(); 3202 } 3203 3204 PerPatchDataCase::IterateResult PerPatchDataCase::iterate (void) 3205 { 3206 TestLog& log = m_testCtx.getLog(); 3207 const RenderContext& renderCtx = m_context.getRenderContext(); 3208 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName())); 3209 const deUint32 programGL = m_program->getProgram(); 3210 const glw::Functions& gl = renderCtx.getFunctions(); 3211 3212 setViewport(gl, viewport); 3213 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 3214 gl.useProgram(programGL); 3215 3216 log << TestLog::Message << "Note: input patch size is " << INPUT_PATCH_SIZE << ", output patch size is " << OUTPUT_PATCH_SIZE << TestLog::EndMessage; 3217 3218 { 3219 const int numPrimitives = m_caseType == CASETYPE_PRIMITIVE_ID_TCS || m_caseType == CASETYPE_PRIMITIVE_ID_TES ? 8 : 1; 3220 3221 vector<float> attributeData; 3222 attributeData.reserve(numPrimitives*INPUT_PATCH_SIZE); 3223 3224 for (int i = 0; i < numPrimitives; i++) 3225 { 3226 attributeData.push_back((float)i / (float)numPrimitives); 3227 for (int j = 0; j < INPUT_PATCH_SIZE-1; j++) 3228 attributeData.push_back(0.0f); 3229 } 3230 3231 gl.patchParameteri(GL_PATCH_VERTICES, INPUT_PATCH_SIZE); 3232 gl.clear(GL_COLOR_BUFFER_BIT); 3233 3234 gl.uniform1f(gl.getUniformLocation(programGL, "u_xScale"), 1.0f / (float)numPrimitives); 3235 3236 const glu::VertexArrayBinding attrBindings[] = 3237 { 3238 glu::va::Float("in_v_attr", 1, (int)attributeData.size(), 0, &attributeData[0]) 3239 }; 3240 3241 glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0], 3242 glu::pr::Patches(numPrimitives*INPUT_PATCH_SIZE)); 3243 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed"); 3244 } 3245 3246 { 3247 const tcu::Surface rendered = getPixels(renderCtx, viewport); 3248 3249 if (m_caseType == CASETYPE_PRIMITIVE_ID_TCS || m_caseType == CASETYPE_PRIMITIVE_ID_TES) 3250 { 3251 DE_ASSERT(caseTypeUsesRefImageFromFile(m_caseType)); 3252 3253 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath); 3254 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT); 3255 3256 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image comparison failed"); 3257 return STOP; 3258 } 3259 else 3260 { 3261 DE_ASSERT(!caseTypeUsesRefImageFromFile(m_caseType)); 3262 3263 log << TestLog::Image("RenderedImage", "Rendered Image", rendered); 3264 3265 for (int y = 0; y < rendered.getHeight(); y++) 3266 for (int x = 0; x < rendered.getWidth(); x++) 3267 { 3268 if (rendered.getPixel(x, y) != tcu::RGBA::white) 3269 { 3270 log << TestLog::Message << "Failure: expected all white pixels" << TestLog::EndMessage; 3271 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); 3272 return STOP; 3273 } 3274 } 3275 3276 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 3277 return STOP; 3278 } 3279 } 3280 } 3281 3282 // Basic barrier() usage in TCS. 3283 class BarrierCase : public TestCase 3284 { 3285 public: 3286 BarrierCase (Context& context, const char* name, const char* description, const char* referenceImagePath) 3287 : TestCase (context, name, description) 3288 , m_referenceImagePath (referenceImagePath) 3289 { 3290 } 3291 3292 void init (void); 3293 void deinit (void); 3294 IterateResult iterate (void); 3295 3296 private: 3297 static const int RENDER_SIZE = 256; 3298 static const int NUM_VERTICES; 3299 3300 const string m_referenceImagePath; 3301 3302 SharedPtr<const ShaderProgram> m_program; 3303 }; 3304 3305 const int BarrierCase::NUM_VERTICES = 32; 3306 3307 void BarrierCase::init (void) 3308 { 3309 checkTessellationSupport(m_context); 3310 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE); 3311 3312 const string numVertsStr = de::toString(NUM_VERTICES); 3313 3314 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() 3315 << glu::VertexSource ("#version 310 es\n" 3316 "\n" 3317 "in highp float in_v_attr;\n" 3318 "\n" 3319 "out highp float in_tc_attr;\n" 3320 "\n" 3321 "void main (void)\n" 3322 "{\n" 3323 " in_tc_attr = in_v_attr;\n" 3324 "}\n") 3325 3326 << glu::TessellationControlSource ("#version 310 es\n" 3327 "#extension GL_EXT_tessellation_shader : require\n" 3328 "\n" 3329 "layout (vertices = " + numVertsStr + ") out;\n" 3330 "\n" 3331 "in highp float in_tc_attr[];\n" 3332 "\n" 3333 "out highp float in_te_attr[];\n" 3334 "patch out highp float in_te_patchAttr;\n" 3335 "\n" 3336 "void main (void)\n" 3337 "{\n" 3338 " in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID];\n" 3339 " in_te_patchAttr = 0.0f;\n" 3340 " barrier();\n" 3341 " if (gl_InvocationID == 5)\n" 3342 " in_te_patchAttr = float(gl_InvocationID)*0.1;\n" 3343 " barrier();\n" 3344 " highp float temp = in_te_patchAttr + in_te_attr[gl_InvocationID];\n" 3345 " barrier();\n" 3346 " if (gl_InvocationID == " + numVertsStr + "-1)\n" 3347 " in_te_patchAttr = float(gl_InvocationID);\n" 3348 " barrier();\n" 3349 " in_te_attr[gl_InvocationID] = temp;\n" 3350 " barrier();\n" 3351 " temp = temp + in_te_attr[(gl_InvocationID+1) % " + numVertsStr + "];\n" 3352 " barrier();\n" 3353 " in_te_attr[gl_InvocationID] = 0.25*temp;\n" 3354 "\n" 3355 " gl_TessLevelInner[0] = 32.0;\n" 3356 " gl_TessLevelInner[1] = 32.0;\n" 3357 "\n" 3358 " gl_TessLevelOuter[0] = 32.0;\n" 3359 " gl_TessLevelOuter[1] = 32.0;\n" 3360 " gl_TessLevelOuter[2] = 32.0;\n" 3361 " gl_TessLevelOuter[3] = 32.0;\n" 3362 "}\n") 3363 3364 << glu::TessellationEvaluationSource ("#version 310 es\n" 3365 "#extension GL_EXT_tessellation_shader : require\n" 3366 "\n" 3367 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_QUADS) + 3368 "\n" 3369 "in highp float in_te_attr[];\n" 3370 "patch in highp float in_te_patchAttr;\n" 3371 "\n" 3372 "out highp float in_f_blue;\n" 3373 "\n" 3374 "void main (void)\n" 3375 "{\n" 3376 " highp float x = gl_TessCoord.x*2.0 - 1.0;\n" 3377 " highp float y = gl_TessCoord.y - in_te_attr[int(round(gl_TessCoord.x*float(" + numVertsStr + "-1)))];\n" 3378 " gl_Position = vec4(x, y, 0.0, 1.0);\n" 3379 " in_f_blue = abs(in_te_patchAttr - float(" + numVertsStr + "-1));\n" 3380 "}\n") 3381 3382 << glu::FragmentSource ("#version 310 es\n" 3383 "\n" 3384 "layout (location = 0) out mediump vec4 o_color;\n" 3385 "\n" 3386 "in highp float in_f_blue;\n" 3387 "\n" 3388 "void main (void)\n" 3389 "{\n" 3390 " o_color = vec4(1.0, 0.0, in_f_blue, 1.0);\n" 3391 "}\n"))); 3392 3393 m_testCtx.getLog() << *m_program; 3394 if (!m_program->isOk()) 3395 TCU_FAIL("Program compilation failed"); 3396 } 3397 3398 void BarrierCase::deinit (void) 3399 { 3400 m_program.clear(); 3401 } 3402 3403 BarrierCase::IterateResult BarrierCase::iterate (void) 3404 { 3405 TestLog& log = m_testCtx.getLog(); 3406 const RenderContext& renderCtx = m_context.getRenderContext(); 3407 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName())); 3408 const deUint32 programGL = m_program->getProgram(); 3409 const glw::Functions& gl = renderCtx.getFunctions(); 3410 3411 setViewport(gl, viewport); 3412 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 3413 gl.useProgram(programGL); 3414 3415 { 3416 vector<float> attributeData(NUM_VERTICES); 3417 3418 for (int i = 0; i < NUM_VERTICES; i++) 3419 attributeData[i] = (float)i / (float)(NUM_VERTICES-1); 3420 3421 gl.patchParameteri(GL_PATCH_VERTICES, NUM_VERTICES); 3422 gl.clear(GL_COLOR_BUFFER_BIT); 3423 3424 const glu::VertexArrayBinding attrBindings[] = 3425 { 3426 glu::va::Float("in_v_attr", 1, (int)attributeData.size(), 0, &attributeData[0]) 3427 }; 3428 3429 glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0], 3430 glu::pr::Patches(NUM_VERTICES)); 3431 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed"); 3432 } 3433 3434 { 3435 const tcu::Surface rendered = getPixels(renderCtx, viewport); 3436 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath); 3437 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT); 3438 3439 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image comparison failed"); 3440 return STOP; 3441 } 3442 } 3443 3444 /*--------------------------------------------------------------------*//*! 3445 * \brief Base class for testing invariance of entire primitive set 3446 * 3447 * Draws two patches with identical tessellation levels and compares the 3448 * results. Repeats the same with other programs that are only different 3449 * in irrelevant ways; compares the results between these two programs. 3450 * Also potentially compares to results produced by different tessellation 3451 * levels (see e.g. invariance rule #6). 3452 * Furthermore, repeats the above with multiple different tessellation 3453 * value sets. 3454 * 3455 * The manner of primitive set comparison is defined by subclass. E.g. 3456 * case for invariance rule #1 tests that same vertices come out, in same 3457 * order; rule #5 only requires that the same triangles are output, but 3458 * not necessarily in the same order. 3459 *//*--------------------------------------------------------------------*/ 3460 class PrimitiveSetInvarianceCase : public TestCase 3461 { 3462 public: 3463 enum WindingUsage 3464 { 3465 WINDINGUSAGE_CCW = 0, 3466 WINDINGUSAGE_CW, 3467 WINDINGUSAGE_VARY, 3468 3469 WINDINGUSAGE_LAST 3470 }; 3471 3472 PrimitiveSetInvarianceCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, bool usePointMode, WindingUsage windingUsage) 3473 : TestCase (context, name, description) 3474 , m_primitiveType (primType) 3475 , m_spacing (spacing) 3476 , m_usePointMode (usePointMode) 3477 , m_windingUsage (windingUsage) 3478 { 3479 } 3480 3481 void init (void); 3482 void deinit (void); 3483 IterateResult iterate (void); 3484 3485 protected: 3486 struct TessLevels 3487 { 3488 float inner[2]; 3489 float outer[4]; 3490 string description (void) const { return tessellationLevelsString(&inner[0], &outer[0]); } 3491 }; 3492 struct LevelCase 3493 { 3494 vector<TessLevels> levels; 3495 int mem; //!< Subclass-defined arbitrary piece of data, for type of the levelcase, if needed. Passed to compare(). 3496 LevelCase (const TessLevels& lev) : levels(vector<TessLevels>(1, lev)), mem(0) {} 3497 LevelCase (void) : mem(0) {} 3498 }; 3499 3500 virtual vector<LevelCase> genTessLevelCases (void) const; 3501 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int levelCaseMem) const = 0; 3502 3503 const TessPrimitiveType m_primitiveType; 3504 3505 private: 3506 struct Program 3507 { 3508 Winding winding; 3509 SharedPtr<const ShaderProgram> program; 3510 3511 Program (Winding w, const SharedPtr<const ShaderProgram>& prog) : winding(w), program(prog) {} 3512 3513 string description (void) const { return string() + "winding mode " + getWindingShaderName(winding); }; 3514 }; 3515 3516 static const int RENDER_SIZE = 16; 3517 3518 const SpacingMode m_spacing; 3519 const bool m_usePointMode; 3520 const WindingUsage m_windingUsage; 3521 3522 vector<Program> m_programs; 3523 }; 3524 3525 vector<PrimitiveSetInvarianceCase::LevelCase> PrimitiveSetInvarianceCase::genTessLevelCases (void) const 3526 { 3527 static const TessLevels basicTessLevelCases[] = 3528 { 3529 { { 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, 3530 { { 63.0f, 24.0f }, { 15.0f, 42.0f, 10.0f, 12.0f } }, 3531 { { 3.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } }, 3532 { { 4.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } }, 3533 { { 2.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } }, 3534 { { 5.0f, 6.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, 3535 { { 1.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } }, 3536 { { 5.0f, 1.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } }, 3537 { { 5.2f, 1.6f }, { 2.9f, 3.4f, 1.5f, 4.1f } } 3538 }; 3539 3540 vector<LevelCase> result; 3541 for (int i = 0; i < DE_LENGTH_OF_ARRAY(basicTessLevelCases); i++) 3542 result.push_back(LevelCase(basicTessLevelCases[i])); 3543 3544 { 3545 de::Random rnd(123); 3546 for (int i = 0; i < 10; i++) 3547 { 3548 TessLevels levels; 3549 for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.inner); j++) 3550 levels.inner[j] = rnd.getFloat(1.0f, 16.0f); 3551 for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.outer); j++) 3552 levels.outer[j] = rnd.getFloat(1.0f, 16.0f); 3553 result.push_back(LevelCase(levels)); 3554 } 3555 } 3556 3557 return result; 3558 } 3559 3560 void PrimitiveSetInvarianceCase::init (void) 3561 { 3562 const int numDifferentConstantExprCases = 2; 3563 vector<Winding> windings; 3564 switch (m_windingUsage) 3565 { 3566 case WINDINGUSAGE_CCW: windings.push_back(WINDING_CCW); break; 3567 case WINDINGUSAGE_CW: windings.push_back(WINDING_CW); break; 3568 case WINDINGUSAGE_VARY: windings.push_back(WINDING_CCW); 3569 windings.push_back(WINDING_CW); break; 3570 default: DE_ASSERT(false); 3571 } 3572 3573 checkTessellationSupport(m_context); 3574 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE); 3575 3576 for (int constantExprCaseNdx = 0; constantExprCaseNdx < numDifferentConstantExprCases; constantExprCaseNdx++) 3577 { 3578 for (int windingCaseNdx = 0; windingCaseNdx < (int)windings.size(); windingCaseNdx++) 3579 { 3580 const string floatLit01 = de::floatToString(10.0f / (float)(constantExprCaseNdx + 10), 2); 3581 const int programNdx = (int)m_programs.size(); 3582 3583 m_programs.push_back(Program(windings[windingCaseNdx], 3584 SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() 3585 << glu::VertexSource ("#version 310 es\n" 3586 "\n" 3587 "in highp float in_v_attr;\n" 3588 "out highp float in_tc_attr;\n" 3589 "\n" 3590 "void main (void)\n" 3591 "{\n" 3592 " in_tc_attr = in_v_attr;\n" 3593 "}\n") 3594 3595 << glu::TessellationControlSource ("#version 310 es\n" 3596 "#extension GL_EXT_tessellation_shader : require\n" 3597 "\n" 3598 "layout (vertices = " + de::toString(constantExprCaseNdx+1) + ") out;\n" 3599 "\n" 3600 "in highp float in_tc_attr[];\n" 3601 "\n" 3602 "patch out highp float in_te_positionOffset;\n" 3603 "\n" 3604 "void main (void)\n" 3605 "{\n" 3606 " in_te_positionOffset = in_tc_attr[6];\n" 3607 "\n" 3608 " gl_TessLevelInner[0] = in_tc_attr[0];\n" 3609 " gl_TessLevelInner[1] = in_tc_attr[1];\n" 3610 "\n" 3611 " gl_TessLevelOuter[0] = in_tc_attr[2];\n" 3612 " gl_TessLevelOuter[1] = in_tc_attr[3];\n" 3613 " gl_TessLevelOuter[2] = in_tc_attr[4];\n" 3614 " gl_TessLevelOuter[3] = in_tc_attr[5];\n" 3615 "}\n") 3616 3617 << glu::TessellationEvaluationSource ("#version 310 es\n" 3618 "#extension GL_EXT_tessellation_shader : require\n" 3619 "\n" 3620 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, windings[windingCaseNdx], m_usePointMode) + 3621 "\n" 3622 "patch in highp float in_te_positionOffset;\n" 3623 "\n" 3624 "out highp vec4 in_f_color;\n" 3625 "invariant out highp vec3 out_te_tessCoord;\n" 3626 "\n" 3627 "void main (void)\n" 3628 "{\n" 3629 " gl_Position = vec4(gl_TessCoord.xy*" + floatLit01 + " - in_te_positionOffset + float(gl_PrimitiveID)*0.1, 0.0, 1.0);\n" 3630 " in_f_color = vec4(" + floatLit01 + ");\n" 3631 " out_te_tessCoord = gl_TessCoord;\n" 3632 "}\n") 3633 3634 << glu::FragmentSource ("#version 310 es\n" 3635 "\n" 3636 "layout (location = 0) out mediump vec4 o_color;\n" 3637 "\n" 3638 "in highp vec4 in_f_color;\n" 3639 "\n" 3640 "void main (void)\n" 3641 "{\n" 3642 " o_color = in_f_color;\n" 3643 "}\n") 3644 3645 << glu::TransformFeedbackVarying ("out_te_tessCoord") 3646 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS))))); 3647 3648 { 3649 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Program" + de::toString(programNdx), "Program " + de::toString(programNdx)); 3650 3651 if (programNdx == 0 || !m_programs.back().program->isOk()) 3652 m_testCtx.getLog() << *m_programs.back().program; 3653 3654 if (!m_programs.back().program->isOk()) 3655 TCU_FAIL("Program compilation failed"); 3656 3657 if (programNdx > 0) 3658 m_testCtx.getLog() << TestLog::Message << "Note: program " << programNdx << " is similar to above, except some constants are different, and: " << m_programs.back().description() << TestLog::EndMessage; 3659 } 3660 } 3661 } 3662 } 3663 3664 void PrimitiveSetInvarianceCase::deinit (void) 3665 { 3666 m_programs.clear(); 3667 } 3668 3669 PrimitiveSetInvarianceCase::IterateResult PrimitiveSetInvarianceCase::iterate (void) 3670 { 3671 typedef TransformFeedbackHandler<Vec3> TFHandler; 3672 3673 TestLog& log = m_testCtx.getLog(); 3674 const RenderContext& renderCtx = m_context.getRenderContext(); 3675 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName())); 3676 const glw::Functions& gl = renderCtx.getFunctions(); 3677 const vector<LevelCase> tessLevelCases = genTessLevelCases(); 3678 vector<vector<int> > primitiveCounts; 3679 int maxNumPrimitives = -1; 3680 3681 for (int caseNdx = 0; caseNdx < (int)tessLevelCases.size(); caseNdx++) 3682 { 3683 primitiveCounts.push_back(vector<int>()); 3684 for (int i = 0; i < (int)tessLevelCases[caseNdx].levels.size(); i++) 3685 { 3686 const int primCount = referencePrimitiveCount(m_primitiveType, m_spacing, m_usePointMode, 3687 &tessLevelCases[caseNdx].levels[i].inner[0], &tessLevelCases[caseNdx].levels[i].outer[0]); 3688 primitiveCounts.back().push_back(primCount); 3689 maxNumPrimitives = de::max(maxNumPrimitives, primCount); 3690 } 3691 } 3692 3693 const deUint32 primitiveTypeGL = outputPrimitiveTypeGL(m_primitiveType, m_usePointMode); 3694 const TFHandler transformFeedback (m_context.getRenderContext(), 2*maxNumPrimitives*numVerticesPerPrimitive(primitiveTypeGL)); 3695 3696 setViewport(gl, viewport); 3697 gl.patchParameteri(GL_PATCH_VERTICES, 7); 3698 3699 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < (int)tessLevelCases.size(); tessLevelCaseNdx++) 3700 { 3701 const LevelCase& levelCase = tessLevelCases[tessLevelCaseNdx]; 3702 vector<Vec3> firstPrimVertices; 3703 3704 { 3705 string tessLevelsStr; 3706 for (int i = 0; i < (int)levelCase.levels.size(); i++) 3707 tessLevelsStr += (levelCase.levels.size() > 1 ? "\n" : "") + levelCase.levels[i].description(); 3708 log << TestLog::Message << "Tessellation level sets: " << tessLevelsStr << TestLog::EndMessage; 3709 } 3710 3711 for (int subTessLevelCaseNdx = 0; subTessLevelCaseNdx < (int)levelCase.levels.size(); subTessLevelCaseNdx++) 3712 { 3713 const TessLevels& tessLevels = levelCase.levels[subTessLevelCaseNdx]; 3714 const float (&inner)[2] = tessLevels.inner; 3715 const float (&outer)[4] = tessLevels.outer; 3716 const float attribute[2*7] = { inner[0], inner[1], outer[0], outer[1], outer[2], outer[3], 0.0f, 3717 inner[0], inner[1], outer[0], outer[1], outer[2], outer[3], 0.5f }; 3718 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, DE_LENGTH_OF_ARRAY(attribute), 0, &attribute[0]) }; 3719 3720 for (int programNdx = 0; programNdx < (int)m_programs.size(); programNdx++) 3721 { 3722 const deUint32 programGL = m_programs[programNdx].program->getProgram(); 3723 gl.useProgram(programGL); 3724 const TFHandler::Result tfResult = transformFeedback.renderAndGetPrimitives(programGL, primitiveTypeGL, DE_LENGTH_OF_ARRAY(bindings), &bindings[0], DE_LENGTH_OF_ARRAY(attribute)); 3725 3726 if (tfResult.numPrimitives != 2*primitiveCounts[tessLevelCaseNdx][subTessLevelCaseNdx]) 3727 { 3728 log << TestLog::Message << "Failure: GL reported GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN to be " 3729 << tfResult.numPrimitives << ", reference value is " << 2*primitiveCounts[tessLevelCaseNdx][subTessLevelCaseNdx] 3730 << TestLog::EndMessage; 3731 3732 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of primitives"); 3733 return STOP; 3734 } 3735 3736 { 3737 const int half = (int)tfResult.varying.size()/2; 3738 const vector<Vec3> prim0Vertices = vector<Vec3>(tfResult.varying.begin(), tfResult.varying.begin() + half); 3739 const Vec3* const prim1Vertices = &tfResult.varying[half]; 3740 3741 for (int vtxNdx = 0; vtxNdx < (int)prim0Vertices.size(); vtxNdx++) 3742 { 3743 if (prim0Vertices[vtxNdx] != prim1Vertices[vtxNdx]) 3744 { 3745 log << TestLog::Message << "Failure: tessellation coordinate at index " << vtxNdx << " differs between two primitives drawn in one draw call" << TestLog::EndMessage 3746 << TestLog::Message << "Note: the coordinate is " << prim0Vertices[vtxNdx] << " for the first primitive and " << prim1Vertices[vtxNdx] << " for the second" << TestLog::EndMessage 3747 << TestLog::Message << "Note: tessellation levels for both primitives were: " << tessellationLevelsString(&inner[0], &outer[0]) << TestLog::EndMessage; 3748 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of primitives"); 3749 return STOP; 3750 } 3751 } 3752 3753 if (programNdx == 0 && subTessLevelCaseNdx == 0) 3754 firstPrimVertices = prim0Vertices; 3755 else 3756 { 3757 const bool compareOk = compare(firstPrimVertices, prim0Vertices, levelCase.mem); 3758 if (!compareOk) 3759 { 3760 log << TestLog::Message << "Note: comparison of tessellation coordinates failed; comparison was made between following cases:\n" 3761 << " - case A: program 0, tessellation levels: " << tessLevelCases[tessLevelCaseNdx].levels[0].description() << "\n" 3762 << " - case B: program " << programNdx << ", tessellation levels: " << tessLevels.description() << TestLog::EndMessage; 3763 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of primitives"); 3764 return STOP; 3765 } 3766 } 3767 } 3768 } 3769 } 3770 } 3771 3772 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 3773 return STOP; 3774 } 3775 3776 /*--------------------------------------------------------------------*//*! 3777 * \brief Test invariance rule #1 3778 * 3779 * Test that the sequence of primitives input to the TES only depends on 3780 * the tessellation levels, tessellation mode, spacing mode, winding, and 3781 * point mode. 3782 *//*--------------------------------------------------------------------*/ 3783 class InvariantPrimitiveSetCase : public PrimitiveSetInvarianceCase 3784 { 3785 public: 3786 InvariantPrimitiveSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode) 3787 : PrimitiveSetInvarianceCase(context, name, description, primType, spacing, usePointMode, winding == WINDING_CCW ? WINDINGUSAGE_CCW 3788 : winding == WINDING_CW ? WINDINGUSAGE_CW 3789 : WINDINGUSAGE_LAST) 3790 { 3791 } 3792 3793 protected: 3794 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int) const 3795 { 3796 for (int vtxNdx = 0; vtxNdx < (int)coordsA.size(); vtxNdx++) 3797 { 3798 if (coordsA[vtxNdx] != coordsB[vtxNdx]) 3799 { 3800 m_testCtx.getLog() << TestLog::Message << "Failure: tessellation coordinate at index " << vtxNdx << " differs between two programs" << TestLog::EndMessage 3801 << TestLog::Message << "Note: the coordinate is " << coordsA[vtxNdx] << " for the first program and " << coordsB[vtxNdx] << " for the other" << TestLog::EndMessage; 3802 return false; 3803 } 3804 } 3805 return true; 3806 } 3807 }; 3808 3809 /*--------------------------------------------------------------------*//*! 3810 * \brief Test invariance rule #2 3811 * 3812 * Test that the set of vertices along an outer edge of a quad or triangle 3813 * only depends on that edge's tessellation level, and spacing. 3814 * 3815 * For each (outer) edge in the quad or triangle, draw multiple patches 3816 * with identical tessellation levels for that outer edge but with 3817 * different values for the other outer edges; compare, among the 3818 * primitives, the vertices generated for that outer edge. Repeat with 3819 * different programs, using different winding etc. settings. Compare 3820 * the edge's vertices between different programs. 3821 *//*--------------------------------------------------------------------*/ 3822 class InvariantOuterEdgeCase : public TestCase 3823 { 3824 public: 3825 InvariantOuterEdgeCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing) 3826 : TestCase (context, name, description) 3827 , m_primitiveType (primType) 3828 , m_spacing (spacing) 3829 { 3830 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS); 3831 } 3832 3833 void init (void); 3834 void deinit (void); 3835 IterateResult iterate (void); 3836 3837 private: 3838 struct Program 3839 { 3840 Winding winding; 3841 bool usePointMode; 3842 SharedPtr<const ShaderProgram> program; 3843 3844 Program (Winding w, bool point, const SharedPtr<const ShaderProgram>& prog) : winding(w), usePointMode(point), program(prog) {} 3845 3846 string description (void) const { return string() + "winding mode " + getWindingShaderName(winding) + ", " + (usePointMode ? "" : "don't ") + "use point mode"; }; 3847 }; 3848 3849 static vector<float> generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel); 3850 3851 static const int RENDER_SIZE = 16; 3852 3853 const TessPrimitiveType m_primitiveType; 3854 const SpacingMode m_spacing; 3855 3856 vector<Program> m_programs; 3857 }; 3858 3859 vector<float> InvariantOuterEdgeCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel) 3860 { 3861 de::Random rnd(123); 3862 return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd); 3863 } 3864 3865 void InvariantOuterEdgeCase::init (void) 3866 { 3867 checkTessellationSupport(m_context); 3868 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE); 3869 3870 for (int windingI = 0; windingI < WINDING_LAST; windingI++) 3871 { 3872 const Winding winding = (Winding)windingI; 3873 3874 for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++) 3875 { 3876 const bool usePointMode = usePointModeI != 0; 3877 const int programNdx = (int)m_programs.size(); 3878 const string floatLit01 = de::floatToString(10.0f / (float)(programNdx + 10), 2); 3879 3880 m_programs.push_back(Program(winding, usePointMode, 3881 SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() 3882 << glu::VertexSource ("#version 310 es\n" 3883 "\n" 3884 "in highp float in_v_attr;\n" 3885 "out highp float in_tc_attr;\n" 3886 "\n" 3887 "void main (void)\n" 3888 "{\n" 3889 " in_tc_attr = in_v_attr;\n" 3890 "}\n") 3891 3892 << glu::TessellationControlSource ("#version 310 es\n" 3893 "#extension GL_EXT_tessellation_shader : require\n" 3894 "\n" 3895 "layout (vertices = " + de::toString(programNdx+1) + ") out;\n" 3896 "\n" 3897 "in highp float in_tc_attr[];\n" 3898 "\n" 3899 "void main (void)\n" 3900 "{\n" 3901 " gl_TessLevelInner[0] = in_tc_attr[0];\n" 3902 " gl_TessLevelInner[1] = in_tc_attr[1];\n" 3903 "\n" 3904 " gl_TessLevelOuter[0] = in_tc_attr[2];\n" 3905 " gl_TessLevelOuter[1] = in_tc_attr[3];\n" 3906 " gl_TessLevelOuter[2] = in_tc_attr[4];\n" 3907 " gl_TessLevelOuter[3] = in_tc_attr[5];\n" 3908 "}\n") 3909 3910 << glu::TessellationEvaluationSource ("#version 310 es\n" 3911 "#extension GL_EXT_tessellation_shader : require\n" 3912 "\n" 3913 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, winding, usePointMode) + 3914 "\n" 3915 "out highp vec4 in_f_color;\n" 3916 "invariant out highp vec3 out_te_tessCoord;\n" 3917 "\n" 3918 "void main (void)\n" 3919 "{\n" 3920 " gl_Position = vec4(gl_TessCoord.xy*" + floatLit01 + " - float(gl_PrimitiveID)*0.05, 0.0, 1.0);\n" 3921 " in_f_color = vec4(" + floatLit01 + ");\n" 3922 " out_te_tessCoord = gl_TessCoord;\n" 3923 "}\n") 3924 3925 << glu::FragmentSource ("#version 310 es\n" 3926 "\n" 3927 "layout (location = 0) out mediump vec4 o_color;\n" 3928 "\n" 3929 "in highp vec4 in_f_color;\n" 3930 "\n" 3931 "void main (void)\n" 3932 "{\n" 3933 " o_color = in_f_color;\n" 3934 "}\n") 3935 3936 << glu::TransformFeedbackVarying ("out_te_tessCoord") 3937 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS))))); 3938 3939 { 3940 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Program" + de::toString(programNdx), "Program " + de::toString(programNdx)); 3941 3942 if (programNdx == 0 || !m_programs.back().program->isOk()) 3943 m_testCtx.getLog() << *m_programs.back().program; 3944 3945 if (!m_programs.back().program->isOk()) 3946 TCU_FAIL("Program compilation failed"); 3947 3948 if (programNdx > 0) 3949 m_testCtx.getLog() << TestLog::Message << "Note: program " << programNdx << " is similar to above, except some constants are different, and: " << m_programs.back().description() << TestLog::EndMessage; 3950 } 3951 } 3952 } 3953 } 3954 3955 void InvariantOuterEdgeCase::deinit (void) 3956 { 3957 m_programs.clear(); 3958 } 3959 3960 InvariantOuterEdgeCase::IterateResult InvariantOuterEdgeCase::iterate (void) 3961 { 3962 typedef TransformFeedbackHandler<Vec3> TFHandler; 3963 3964 TestLog& log = m_testCtx.getLog(); 3965 const RenderContext& renderCtx = m_context.getRenderContext(); 3966 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName())); 3967 const glw::Functions& gl = renderCtx.getFunctions(); 3968 3969 static const float singleOuterEdgeLevels[] = { 1.0f, 1.2f, 1.9f, 2.3f, 2.8f, 3.3f, 3.8f, 10.2f, 1.6f, 24.4f, 24.7f, 63.0f }; 3970 const int numPatchesPerDrawCall = 10; 3971 const vector<OuterEdgeDescription> edgeDescriptions = outerEdgeDescriptions(m_primitiveType); 3972 3973 { 3974 // Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once. 3975 int maxNumVerticesInDrawCall = 0; 3976 { 3977 const vector<float> patchTessLevels = generatePatchTessLevels(numPatchesPerDrawCall, 0 /* outer-edge index doesn't affect vertex count */, arrayMax(singleOuterEdgeLevels)); 3978 3979 for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++) 3980 maxNumVerticesInDrawCall = de::max(maxNumVerticesInDrawCall, 3981 multiplePatchReferenceVertexCount(m_primitiveType, m_spacing, usePointModeI != 0, &patchTessLevels[0], numPatchesPerDrawCall)); 3982 } 3983 3984 { 3985 const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall); 3986 3987 setViewport(gl, viewport); 3988 gl.patchParameteri(GL_PATCH_VERTICES, 6); 3989 3990 for (int outerEdgeIndex = 0; outerEdgeIndex < (int)edgeDescriptions.size(); outerEdgeIndex++) 3991 { 3992 const OuterEdgeDescription& edgeDesc = edgeDescriptions[outerEdgeIndex]; 3993 3994 for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(singleOuterEdgeLevels); outerEdgeLevelCaseNdx++) 3995 { 3996 typedef std::set<Vec3, VecLexLessThan<3> > Vec3Set; 3997 3998 const vector<float> patchTessLevels = generatePatchTessLevels(numPatchesPerDrawCall, outerEdgeIndex, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]); 3999 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)patchTessLevels.size(), 0, &patchTessLevels[0]) }; 4000 Vec3Set firstOuterEdgeVertices; // Vertices of the outer edge of the first patch of the first program's draw call; used for comparison with other patches. 4001 4002 log << TestLog::Message << "Testing with outer tessellation level " << singleOuterEdgeLevels[outerEdgeLevelCaseNdx] 4003 << " for the " << edgeDesc.description() << " edge, and with various levels for other edges, and with all programs" << TestLog::EndMessage; 4004 4005 for (int programNdx = 0; programNdx < (int)m_programs.size(); programNdx++) 4006 { 4007 const Program& program = m_programs[programNdx]; 4008 const deUint32 programGL = program.program->getProgram(); 4009 4010 gl.useProgram(programGL); 4011 4012 { 4013 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, program.usePointMode), 4014 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)patchTessLevels.size()); 4015 const int refNumVertices = multiplePatchReferenceVertexCount(m_primitiveType, m_spacing, program.usePointMode, &patchTessLevels[0], numPatchesPerDrawCall); 4016 int numVerticesRead = 0; 4017 4018 if ((int)tfResult.varying.size() != refNumVertices) 4019 { 4020 log << TestLog::Message << "Failure: the number of vertices returned by transform feedback is " 4021 << tfResult.varying.size() << ", expected " << refNumVertices << TestLog::EndMessage 4022 << TestLog::Message << "Note: rendered " << numPatchesPerDrawCall 4023 << " patches in one draw call; tessellation levels for each patch are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n" 4024 << containerStr(patchTessLevels, 6) << TestLog::EndMessage; 4025 4026 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices"); 4027 return STOP; 4028 } 4029 4030 // Check the vertices of each patch. 4031 4032 for (int patchNdx = 0; patchNdx < numPatchesPerDrawCall; patchNdx++) 4033 { 4034 const float* const innerLevels = &patchTessLevels[6*patchNdx + 0]; 4035 const float* const outerLevels = &patchTessLevels[6*patchNdx + 2]; 4036 const int patchNumVertices = referenceVertexCount(m_primitiveType, m_spacing, program.usePointMode, innerLevels, outerLevels); 4037 Vec3Set outerEdgeVertices; 4038 4039 // We're interested in just the vertices on the current outer edge. 4040 for(int vtxNdx = numVerticesRead; vtxNdx < numVerticesRead + patchNumVertices; vtxNdx++) 4041 { 4042 const Vec3& vtx = tfResult.varying[vtxNdx]; 4043 if (edgeDesc.contains(vtx)) 4044 outerEdgeVertices.insert(tfResult.varying[vtxNdx]); 4045 } 4046 4047 // Check that the outer edge contains an appropriate number of vertices. 4048 { 4049 const int refNumVerticesOnOuterEdge = 1 + getClampedRoundedTessLevel(m_spacing, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]); 4050 4051 if ((int)outerEdgeVertices.size() != refNumVerticesOnOuterEdge) 4052 { 4053 log << TestLog::Message << "Failure: the number of vertices on outer edge is " << outerEdgeVertices.size() 4054 << ", expected " << refNumVerticesOnOuterEdge << TestLog::EndMessage 4055 << TestLog::Message << "Note: vertices on the outer edge are:\n" << containerStr(outerEdgeVertices, 5, 0) << TestLog::EndMessage 4056 << TestLog::Message << "Note: the following parameters were used: " << program.description() 4057 << ", tessellation levels: " << tessellationLevelsString(innerLevels, outerLevels) << TestLog::EndMessage; 4058 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices"); 4059 return STOP; 4060 } 4061 } 4062 4063 // Compare the vertices to those of the first patch (unless this is the first patch). 4064 4065 if (programNdx == 0 && patchNdx == 0) 4066 firstOuterEdgeVertices = outerEdgeVertices; 4067 else 4068 { 4069 if (firstOuterEdgeVertices != outerEdgeVertices) 4070 { 4071 log << TestLog::Message << "Failure: vertices generated for the edge differ between the following cases:\n" 4072 << " - case A: " << m_programs[0].description() << ", tessellation levels: " 4073 << tessellationLevelsString(&patchTessLevels[0], &patchTessLevels[2]) << "\n" 4074 << " - case B: " << program.description() << ", tessellation levels: " 4075 << tessellationLevelsString(innerLevels, outerLevels) << TestLog::EndMessage; 4076 4077 log << TestLog::Message << "Note: resulting vertices for the edge for the cases were:\n" 4078 << " - case A: " << containerStr(firstOuterEdgeVertices, 5, 14) << "\n" 4079 << " - case B: " << containerStr(outerEdgeVertices, 5, 14) << TestLog::EndMessage; 4080 4081 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices"); 4082 return STOP; 4083 } 4084 } 4085 4086 numVerticesRead += patchNumVertices; 4087 } 4088 4089 DE_ASSERT(numVerticesRead == (int)tfResult.varying.size()); 4090 } 4091 } 4092 } 4093 } 4094 } 4095 } 4096 4097 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 4098 return STOP; 4099 } 4100 4101 /*--------------------------------------------------------------------*//*! 4102 * \brief Test invariance rule #3 4103 * 4104 * Test that the vertices along an outer edge are placed symmetrically. 4105 * 4106 * Draw multiple patches with different tessellation levels and different 4107 * point_mode, winding etc. Before outputting tesscoords with TF, mirror 4108 * the vertices in the TES such that every vertex on an outer edge - 4109 * except the possible middle vertex - should be duplicated in the output. 4110 * Check that appropriate duplicates exist. 4111 *//*--------------------------------------------------------------------*/ 4112 class SymmetricOuterEdgeCase : public TestCase 4113 { 4114 public: 4115 SymmetricOuterEdgeCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode) 4116 : TestCase (context, name, description) 4117 , m_primitiveType (primType) 4118 , m_spacing (spacing) 4119 , m_winding (winding) 4120 , m_usePointMode (usePointMode) 4121 { 4122 } 4123 4124 void init (void); 4125 void deinit (void); 4126 IterateResult iterate (void); 4127 4128 private: 4129 static vector<float> generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel); 4130 4131 static const int RENDER_SIZE = 16; 4132 4133 const TessPrimitiveType m_primitiveType; 4134 const SpacingMode m_spacing; 4135 const Winding m_winding; 4136 const bool m_usePointMode; 4137 4138 SharedPtr<const glu::ShaderProgram> m_program; 4139 }; 4140 4141 vector<float> SymmetricOuterEdgeCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel) 4142 { 4143 de::Random rnd(123); 4144 return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd); 4145 } 4146 4147 void SymmetricOuterEdgeCase::init (void) 4148 { 4149 checkTessellationSupport(m_context); 4150 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE); 4151 4152 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() 4153 << glu::VertexSource ("#version 310 es\n" 4154 "\n" 4155 "in highp float in_v_attr;\n" 4156 "out highp float in_tc_attr;\n" 4157 "\n" 4158 "void main (void)\n" 4159 "{\n" 4160 " in_tc_attr = in_v_attr;\n" 4161 "}\n") 4162 4163 << glu::TessellationControlSource ("#version 310 es\n" 4164 "#extension GL_EXT_tessellation_shader : require\n" 4165 "\n" 4166 "layout (vertices = 1) out;\n" 4167 "\n" 4168 "in highp float in_tc_attr[];\n" 4169 "\n" 4170 "void main (void)\n" 4171 "{\n" 4172 " gl_TessLevelInner[0] = in_tc_attr[0];\n" 4173 " gl_TessLevelInner[1] = in_tc_attr[1];\n" 4174 "\n" 4175 " gl_TessLevelOuter[0] = in_tc_attr[2];\n" 4176 " gl_TessLevelOuter[1] = in_tc_attr[3];\n" 4177 " gl_TessLevelOuter[2] = in_tc_attr[4];\n" 4178 " gl_TessLevelOuter[3] = in_tc_attr[5];\n" 4179 "}\n") 4180 4181 << glu::TessellationEvaluationSource ("#version 310 es\n" 4182 "#extension GL_EXT_tessellation_shader : require\n" 4183 "\n" 4184 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) + 4185 "\n" 4186 "out highp vec4 in_f_color;\n" 4187 "out highp vec4 out_te_tessCoord_isMirrored;\n" 4188 "\n" 4189 "void main (void)\n" 4190 "{\n" 4191 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 4192 " float x = gl_TessCoord.x;\n" 4193 " float y = gl_TessCoord.y;\n" 4194 " float z = gl_TessCoord.z;\n" 4195 " // Mirror one half of each outer edge onto the other half, except the endpoints (because they belong to two edges)\n" 4196 " out_te_tessCoord_isMirrored = z == 0.0 && x > 0.5 && x != 1.0 ? vec4(1.0-x, 1.0-y, 0.0, 1.0)\n" 4197 " : y == 0.0 && z > 0.5 && z != 1.0 ? vec4(1.0-x, 0.0, 1.0-z, 1.0)\n" 4198 " : x == 0.0 && y > 0.5 && y != 1.0 ? vec4( 0.0, 1.0-y, 1.0-z, 1.0)\n" 4199 " : vec4(x, y, z, 0.0);\n" 4200 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4201 " float x = gl_TessCoord.x;\n" 4202 " float y = gl_TessCoord.y;\n" 4203 " // Mirror one half of each outer edge onto the other half, except the endpoints (because they belong to two edges)\n" 4204 " out_te_tessCoord_isMirrored = (x == 0.0 || x == 1.0) && y > 0.5 && y != 1.0 ? vec4( x, 1.0-y, 0.0, 1.0)\n" 4205 " : (y == 0.0 || y == 1.0) && x > 0.5 && x != 1.0 ? vec4(1.0-x, y, 0.0, 1.0)\n" 4206 " : vec4(x, y, 0.0, 0.0);\n" 4207 : m_primitiveType == TESSPRIMITIVETYPE_ISOLINES ? 4208 " float x = gl_TessCoord.x;\n" 4209 " float y = gl_TessCoord.y;\n" 4210 " // Mirror one half of each outer edge onto the other half\n" 4211 " out_te_tessCoord_isMirrored = (x == 0.0 || x == 1.0) && y > 0.5 ? vec4(x, 1.0-y, 0.0, 1.0)\n" 4212 " : vec4(x, y, 0.0, 0.0f);\n" 4213 : DE_NULL) + 4214 "\n" 4215 " gl_Position = vec4(gl_TessCoord.xy, 0.0, 1.0);\n" 4216 " in_f_color = vec4(1.0);\n" 4217 "}\n") 4218 4219 << glu::FragmentSource ("#version 310 es\n" 4220 "\n" 4221 "layout (location = 0) out mediump vec4 o_color;\n" 4222 "\n" 4223 "in highp vec4 in_f_color;\n" 4224 "\n" 4225 "void main (void)\n" 4226 "{\n" 4227 " o_color = in_f_color;\n" 4228 "}\n") 4229 4230 << glu::TransformFeedbackVarying ("out_te_tessCoord_isMirrored") 4231 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS))); 4232 4233 m_testCtx.getLog() << *m_program; 4234 if (!m_program->isOk()) 4235 TCU_FAIL("Program compilation failed"); 4236 } 4237 4238 void SymmetricOuterEdgeCase::deinit (void) 4239 { 4240 m_program.clear(); 4241 } 4242 4243 SymmetricOuterEdgeCase::IterateResult SymmetricOuterEdgeCase::iterate (void) 4244 { 4245 typedef TransformFeedbackHandler<Vec4> TFHandler; 4246 4247 TestLog& log = m_testCtx.getLog(); 4248 const RenderContext& renderCtx = m_context.getRenderContext(); 4249 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName())); 4250 const glw::Functions& gl = renderCtx.getFunctions(); 4251 4252 static const float singleOuterEdgeLevels[] = { 1.0f, 1.2f, 1.9f, 2.3f, 2.8f, 3.3f, 3.8f, 10.2f, 1.6f, 24.4f, 24.7f, 63.0f }; 4253 const vector<OuterEdgeDescription> edgeDescriptions = outerEdgeDescriptions(m_primitiveType); 4254 4255 { 4256 // Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once. 4257 int maxNumVerticesInDrawCall; 4258 { 4259 const vector<float> patchTessLevels = generatePatchTessLevels(1, 0 /* outer-edge index doesn't affect vertex count */, arrayMax(singleOuterEdgeLevels)); 4260 maxNumVerticesInDrawCall = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]); 4261 } 4262 4263 { 4264 const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall); 4265 4266 setViewport(gl, viewport); 4267 gl.patchParameteri(GL_PATCH_VERTICES, 6); 4268 4269 for (int outerEdgeIndex = 0; outerEdgeIndex < (int)edgeDescriptions.size(); outerEdgeIndex++) 4270 { 4271 const OuterEdgeDescription& edgeDesc = edgeDescriptions[outerEdgeIndex]; 4272 4273 for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(singleOuterEdgeLevels); outerEdgeLevelCaseNdx++) 4274 { 4275 typedef std::set<Vec3, VecLexLessThan<3> > Vec3Set; 4276 4277 const vector<float> patchTessLevels = generatePatchTessLevels(1, outerEdgeIndex, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]); 4278 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)patchTessLevels.size(), 0, &patchTessLevels[0]) }; 4279 4280 log << TestLog::Message << "Testing with outer tessellation level " << singleOuterEdgeLevels[outerEdgeLevelCaseNdx] 4281 << " for the " << edgeDesc.description() << " edge, and with various levels for other edges" << TestLog::EndMessage; 4282 4283 { 4284 const deUint32 programGL = m_program->getProgram(); 4285 4286 gl.useProgram(programGL); 4287 4288 { 4289 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode), 4290 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)patchTessLevels.size()); 4291 const int refNumVertices = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]); 4292 4293 if ((int)tfResult.varying.size() != refNumVertices) 4294 { 4295 log << TestLog::Message << "Failure: the number of vertices returned by transform feedback is " 4296 << tfResult.varying.size() << ", expected " << refNumVertices << TestLog::EndMessage 4297 << TestLog::Message << "Note: rendered 1 patch, tessellation levels are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n" 4298 << containerStr(patchTessLevels, 6) << TestLog::EndMessage; 4299 4300 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices"); 4301 return STOP; 4302 } 4303 4304 // Check the vertices. 4305 4306 { 4307 Vec3Set nonMirroredEdgeVertices; 4308 Vec3Set mirroredEdgeVertices; 4309 4310 // We're interested in just the vertices on the current outer edge. 4311 for(int vtxNdx = 0; vtxNdx < refNumVertices; vtxNdx++) 4312 { 4313 const Vec3& vtx = tfResult.varying[vtxNdx].swizzle(0,1,2); 4314 if (edgeDesc.contains(vtx)) 4315 { 4316 // Ignore the middle vertex of the outer edge, as it's exactly at the mirroring point; 4317 // for isolines, also ignore (0, 0) and (1, 0) because there's no mirrored counterpart for them. 4318 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES && vtx == tcu::select(Vec3(0.0f), Vec3(0.5f), singleTrueMask<3>(edgeDesc.constantCoordinateIndex))) 4319 continue; 4320 if (m_primitiveType == TESSPRIMITIVETYPE_QUADS && vtx.swizzle(0,1) == tcu::select(Vec2(edgeDesc.constantCoordinateValueChoices[0]), 4321 Vec2(0.5f), 4322 singleTrueMask<2>(edgeDesc.constantCoordinateIndex))) 4323 continue; 4324 if (m_primitiveType == TESSPRIMITIVETYPE_ISOLINES && (vtx == Vec3(0.0f, 0.5f, 0.0f) || vtx == Vec3(1.0f, 0.5f, 0.0f) || 4325 vtx == Vec3(0.0f, 0.0f, 0.0f) || vtx == Vec3(1.0f, 0.0f, 0.0f))) 4326 continue; 4327 4328 const bool isMirrored = tfResult.varying[vtxNdx].w() > 0.5f; 4329 if (isMirrored) 4330 mirroredEdgeVertices.insert(vtx); 4331 else 4332 nonMirroredEdgeVertices.insert(vtx); 4333 } 4334 } 4335 4336 if (m_primitiveType != TESSPRIMITIVETYPE_ISOLINES) 4337 { 4338 // Check that both endpoints are present. Note that endpoints aren't mirrored by the shader, since they belong to more than one edge. 4339 4340 Vec3 endpointA; 4341 Vec3 endpointB; 4342 4343 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 4344 { 4345 endpointA = tcu::select(Vec3(1.0f), Vec3(0.0f), singleTrueMask<3>((edgeDesc.constantCoordinateIndex + 1) % 3)); 4346 endpointB = tcu::select(Vec3(1.0f), Vec3(0.0f), singleTrueMask<3>((edgeDesc.constantCoordinateIndex + 2) % 3)); 4347 } 4348 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS) 4349 { 4350 endpointA.xy() = tcu::select(Vec2(edgeDesc.constantCoordinateValueChoices[0]), Vec2(0.0f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex)); 4351 endpointB.xy() = tcu::select(Vec2(edgeDesc.constantCoordinateValueChoices[0]), Vec2(1.0f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex)); 4352 } 4353 else 4354 DE_ASSERT(false); 4355 4356 if (!contains(nonMirroredEdgeVertices, endpointA) || 4357 !contains(nonMirroredEdgeVertices, endpointB)) 4358 { 4359 log << TestLog::Message << "Failure: edge doesn't contain both endpoints, " << endpointA << " and " << endpointB << TestLog::EndMessage 4360 << TestLog::Message << "Note: non-mirrored vertices:\n" << containerStr(nonMirroredEdgeVertices, 5) 4361 << "\nmirrored vertices:\n" << containerStr(mirroredEdgeVertices, 5) << TestLog::EndMessage; 4362 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices"); 4363 return STOP; 4364 } 4365 nonMirroredEdgeVertices.erase(endpointA); 4366 nonMirroredEdgeVertices.erase(endpointB); 4367 } 4368 4369 if (nonMirroredEdgeVertices != mirroredEdgeVertices) 4370 { 4371 log << TestLog::Message << "Failure: the set of mirrored edges isn't equal to the set of non-mirrored edges (ignoring endpoints and possible middle)" << TestLog::EndMessage 4372 << TestLog::Message << "Note: non-mirrored vertices:\n" << containerStr(nonMirroredEdgeVertices, 5) 4373 << "\nmirrored vertices:\n" << containerStr(mirroredEdgeVertices, 5) << TestLog::EndMessage; 4374 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices"); 4375 return STOP; 4376 } 4377 } 4378 } 4379 } 4380 } 4381 } 4382 } 4383 } 4384 4385 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 4386 return STOP; 4387 } 4388 4389 /*--------------------------------------------------------------------*//*! 4390 * \brief Test invariance rule #4 4391 * 4392 * Test that the vertices on an outer edge don't depend on which of the 4393 * edges it is, other than with respect to component order. 4394 *//*--------------------------------------------------------------------*/ 4395 class OuterEdgeVertexSetIndexIndependenceCase : public TestCase 4396 { 4397 public: 4398 OuterEdgeVertexSetIndexIndependenceCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode) 4399 : TestCase (context, name, description) 4400 , m_primitiveType (primType) 4401 , m_spacing (spacing) 4402 , m_winding (winding) 4403 , m_usePointMode (usePointMode) 4404 { 4405 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS); 4406 } 4407 4408 void init (void); 4409 void deinit (void); 4410 IterateResult iterate (void); 4411 4412 private: 4413 static vector<float> generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel); 4414 4415 static const int RENDER_SIZE = 16; 4416 4417 const TessPrimitiveType m_primitiveType; 4418 const SpacingMode m_spacing; 4419 const Winding m_winding; 4420 const bool m_usePointMode; 4421 4422 SharedPtr<const glu::ShaderProgram> m_program; 4423 }; 4424 4425 vector<float> OuterEdgeVertexSetIndexIndependenceCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel) 4426 { 4427 de::Random rnd(123); 4428 return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd); 4429 } 4430 4431 void OuterEdgeVertexSetIndexIndependenceCase::init (void) 4432 { 4433 checkTessellationSupport(m_context); 4434 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE); 4435 4436 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() 4437 << glu::VertexSource ("#version 310 es\n" 4438 "\n" 4439 "in highp float in_v_attr;\n" 4440 "out highp float in_tc_attr;\n" 4441 "\n" 4442 "void main (void)\n" 4443 "{\n" 4444 " in_tc_attr = in_v_attr;\n" 4445 "}\n") 4446 4447 << glu::TessellationControlSource ("#version 310 es\n" 4448 "#extension GL_EXT_tessellation_shader : require\n" 4449 "\n" 4450 "layout (vertices = 1) out;\n" 4451 "\n" 4452 "in highp float in_tc_attr[];\n" 4453 "\n" 4454 "void main (void)\n" 4455 "{\n" 4456 " gl_TessLevelInner[0] = in_tc_attr[0];\n" 4457 " gl_TessLevelInner[1] = in_tc_attr[1];\n" 4458 "\n" 4459 " gl_TessLevelOuter[0] = in_tc_attr[2];\n" 4460 " gl_TessLevelOuter[1] = in_tc_attr[3];\n" 4461 " gl_TessLevelOuter[2] = in_tc_attr[4];\n" 4462 " gl_TessLevelOuter[3] = in_tc_attr[5];\n" 4463 "}\n") 4464 4465 << glu::TessellationEvaluationSource ("#version 310 es\n" 4466 "#extension GL_EXT_tessellation_shader : require\n" 4467 "\n" 4468 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) + 4469 "\n" 4470 "out highp vec4 in_f_color;\n" 4471 "out highp vec3 out_te_tessCoord;\n" 4472 "\n" 4473 "void main (void)\n" 4474 "{\n" 4475 " out_te_tessCoord = gl_TessCoord;" 4476 " gl_Position = vec4(gl_TessCoord.xy, 0.0, 1.0);\n" 4477 " in_f_color = vec4(1.0);\n" 4478 "}\n") 4479 4480 << glu::FragmentSource ("#version 310 es\n" 4481 "\n" 4482 "layout (location = 0) out mediump vec4 o_color;\n" 4483 "\n" 4484 "in highp vec4 in_f_color;\n" 4485 "\n" 4486 "void main (void)\n" 4487 "{\n" 4488 " o_color = in_f_color;\n" 4489 "}\n") 4490 4491 << glu::TransformFeedbackVarying ("out_te_tessCoord") 4492 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS))); 4493 4494 m_testCtx.getLog() << *m_program; 4495 if (!m_program->isOk()) 4496 TCU_FAIL("Program compilation failed"); 4497 } 4498 4499 void OuterEdgeVertexSetIndexIndependenceCase::deinit (void) 4500 { 4501 m_program.clear(); 4502 } 4503 4504 OuterEdgeVertexSetIndexIndependenceCase::IterateResult OuterEdgeVertexSetIndexIndependenceCase::iterate (void) 4505 { 4506 typedef TransformFeedbackHandler<Vec3> TFHandler; 4507 4508 TestLog& log = m_testCtx.getLog(); 4509 const RenderContext& renderCtx = m_context.getRenderContext(); 4510 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName())); 4511 const glw::Functions& gl = renderCtx.getFunctions(); 4512 const deUint32 programGL = m_program->getProgram(); 4513 4514 static const float singleOuterEdgeLevels[] = { 1.0f, 1.2f, 1.9f, 2.3f, 2.8f, 3.3f, 3.8f, 10.2f, 1.6f, 24.4f, 24.7f, 63.0f }; 4515 const vector<OuterEdgeDescription> edgeDescriptions = outerEdgeDescriptions(m_primitiveType); 4516 4517 gl.useProgram(programGL); 4518 setViewport(gl, viewport); 4519 gl.patchParameteri(GL_PATCH_VERTICES, 6); 4520 4521 { 4522 // Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once. 4523 int maxNumVerticesInDrawCall = 0; 4524 { 4525 const vector<float> patchTessLevels = generatePatchTessLevels(1, 0 /* outer-edge index doesn't affect vertex count */, arrayMax(singleOuterEdgeLevels)); 4526 maxNumVerticesInDrawCall = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]); 4527 } 4528 4529 { 4530 const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall); 4531 4532 for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(singleOuterEdgeLevels); outerEdgeLevelCaseNdx++) 4533 { 4534 typedef std::set<Vec3, VecLexLessThan<3> > Vec3Set; 4535 4536 Vec3Set firstEdgeVertices; 4537 4538 for (int outerEdgeIndex = 0; outerEdgeIndex < (int)edgeDescriptions.size(); outerEdgeIndex++) 4539 { 4540 const OuterEdgeDescription& edgeDesc = edgeDescriptions[outerEdgeIndex]; 4541 const vector<float> patchTessLevels = generatePatchTessLevels(1, outerEdgeIndex, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]); 4542 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)patchTessLevels.size(), 0, &patchTessLevels[0]) }; 4543 4544 log << TestLog::Message << "Testing with outer tessellation level " << singleOuterEdgeLevels[outerEdgeLevelCaseNdx] 4545 << " for the " << edgeDesc.description() << " edge, and with various levels for other edges" << TestLog::EndMessage; 4546 4547 { 4548 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode), 4549 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)patchTessLevels.size()); 4550 const int refNumVertices = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]); 4551 4552 if ((int)tfResult.varying.size() != refNumVertices) 4553 { 4554 log << TestLog::Message << "Failure: the number of vertices returned by transform feedback is " 4555 << tfResult.varying.size() << ", expected " << refNumVertices << TestLog::EndMessage 4556 << TestLog::Message << "Note: rendered 1 patch, tessellation levels are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n" 4557 << containerStr(patchTessLevels, 6) << TestLog::EndMessage; 4558 4559 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices"); 4560 return STOP; 4561 } 4562 4563 { 4564 Vec3Set currentEdgeVertices; 4565 4566 // Get the vertices on the current outer edge. 4567 for(int vtxNdx = 0; vtxNdx < refNumVertices; vtxNdx++) 4568 { 4569 const Vec3& vtx = tfResult.varying[vtxNdx]; 4570 if (edgeDesc.contains(vtx)) 4571 { 4572 // Swizzle components to match the order of the first edge. 4573 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 4574 { 4575 currentEdgeVertices.insert(outerEdgeIndex == 0 ? vtx 4576 : outerEdgeIndex == 1 ? vtx.swizzle(1, 0, 2) 4577 : outerEdgeIndex == 2 ? vtx.swizzle(2, 1, 0) 4578 : Vec3(-1.0f)); 4579 } 4580 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS) 4581 { 4582 currentEdgeVertices.insert(Vec3(outerEdgeIndex == 0 ? vtx.y() 4583 : outerEdgeIndex == 1 ? vtx.x() 4584 : outerEdgeIndex == 2 ? vtx.y() 4585 : outerEdgeIndex == 3 ? vtx.x() 4586 : -1.0f, 4587 0.0f, 0.0f)); 4588 } 4589 else 4590 DE_ASSERT(false); 4591 } 4592 } 4593 4594 if (outerEdgeIndex == 0) 4595 firstEdgeVertices = currentEdgeVertices; 4596 else 4597 { 4598 // Compare vertices of this edge to those of the first edge. 4599 4600 if (currentEdgeVertices != firstEdgeVertices) 4601 { 4602 const char* const swizzleDesc = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? (outerEdgeIndex == 1 ? "(y, x, z)" 4603 : outerEdgeIndex == 2 ? "(z, y, x)" 4604 : DE_NULL) 4605 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? (outerEdgeIndex == 1 ? "(x, 0)" 4606 : outerEdgeIndex == 2 ? "(y, 0)" 4607 : outerEdgeIndex == 3 ? "(x, 0)" 4608 : DE_NULL) 4609 : DE_NULL; 4610 4611 log << TestLog::Message << "Failure: the set of vertices on the " << edgeDesc.description() << " edge" 4612 << " doesn't match the set of vertices on the " << edgeDescriptions[0].description() << " edge" << TestLog::EndMessage 4613 << TestLog::Message << "Note: set of vertices on " << edgeDesc.description() << " edge, components swizzled like " << swizzleDesc 4614 << " to match component order on first edge:\n" << containerStr(currentEdgeVertices, 5) 4615 << "\non " << edgeDescriptions[0].description() << " edge:\n" << containerStr(firstEdgeVertices, 5) << TestLog::EndMessage; 4616 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices"); 4617 return STOP; 4618 } 4619 } 4620 } 4621 } 4622 } 4623 } 4624 } 4625 } 4626 4627 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 4628 return STOP; 4629 } 4630 4631 /*--------------------------------------------------------------------*//*! 4632 * \brief Test invariance rule #5 4633 * 4634 * Test that the set of triangles input to the TES only depends on the 4635 * tessellation levels, tessellation mode and spacing mode. Specifically, 4636 * winding doesn't change the set of triangles, though it can change the 4637 * order in which they are input to TES, and can (and will) change the 4638 * vertex order within a triangle. 4639 *//*--------------------------------------------------------------------*/ 4640 class InvariantTriangleSetCase : public PrimitiveSetInvarianceCase 4641 { 4642 public: 4643 InvariantTriangleSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing) 4644 : PrimitiveSetInvarianceCase(context, name, description, primType, spacing, false, WINDINGUSAGE_VARY) 4645 { 4646 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS); 4647 } 4648 4649 protected: 4650 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int) const 4651 { 4652 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog()); 4653 } 4654 }; 4655 4656 /*--------------------------------------------------------------------*//*! 4657 * \brief Test invariance rule #6 4658 * 4659 * Test that the set of inner triangles input to the TES only depends on 4660 * the inner tessellation levels, tessellation mode and spacing mode. 4661 *//*--------------------------------------------------------------------*/ 4662 class InvariantInnerTriangleSetCase : public PrimitiveSetInvarianceCase 4663 { 4664 public: 4665 InvariantInnerTriangleSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing) 4666 : PrimitiveSetInvarianceCase(context, name, description, primType, spacing, false, WINDINGUSAGE_VARY) 4667 { 4668 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS); 4669 } 4670 4671 protected: 4672 virtual vector<LevelCase> genTessLevelCases (void) const 4673 { 4674 const int numSubCases = 4; 4675 const vector<LevelCase> baseResults = PrimitiveSetInvarianceCase::genTessLevelCases(); 4676 vector<LevelCase> result; 4677 de::Random rnd (123); 4678 4679 // Generate variants with different values for irrelevant levels. 4680 for (int baseNdx = 0; baseNdx < (int)baseResults.size(); baseNdx++) 4681 { 4682 const TessLevels& base = baseResults[baseNdx].levels[0]; 4683 TessLevels levels = base; 4684 LevelCase levelCase; 4685 4686 for (int subNdx = 0; subNdx < numSubCases; subNdx++) 4687 { 4688 levelCase.levels.push_back(levels); 4689 4690 for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); i++) 4691 levels.outer[i] = rnd.getFloat(2.0f, 16.0f); 4692 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 4693 levels.inner[1] = rnd.getFloat(2.0f, 16.0f); 4694 } 4695 4696 result.push_back(levelCase); 4697 } 4698 4699 return result; 4700 } 4701 4702 struct IsInnerTriangleTriangle 4703 { 4704 bool operator() (const Vec3* vertices) const 4705 { 4706 for (int v = 0; v < 3; v++) 4707 for (int c = 0; c < 3; c++) 4708 if (vertices[v][c] == 0.0f) 4709 return false; 4710 return true; 4711 } 4712 }; 4713 4714 struct IsInnerQuadTriangle 4715 { 4716 bool operator() (const Vec3* vertices) const 4717 { 4718 for (int v = 0; v < 3; v++) 4719 for (int c = 0; c < 2; c++) 4720 if (vertices[v][c] == 0.0f || vertices[v][c] == 1.0f) 4721 return false; 4722 return true; 4723 } 4724 }; 4725 4726 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int) const 4727 { 4728 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 4729 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(), IsInnerTriangleTriangle(), "outer triangles"); 4730 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS) 4731 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(), IsInnerQuadTriangle(), "outer triangles"); 4732 else 4733 { 4734 DE_ASSERT(false); 4735 return false; 4736 } 4737 } 4738 }; 4739 4740 /*--------------------------------------------------------------------*//*! 4741 * \brief Test invariance rule #7 4742 * 4743 * Test that the set of outer triangles input to the TES only depends on 4744 * tessellation mode, spacing mode and the inner and outer tessellation 4745 * levels corresponding to the inner and outer edges relevant to that 4746 * triangle. 4747 *//*--------------------------------------------------------------------*/ 4748 class InvariantOuterTriangleSetCase : public PrimitiveSetInvarianceCase 4749 { 4750 public: 4751 InvariantOuterTriangleSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing) 4752 : PrimitiveSetInvarianceCase(context, name, description, primType, spacing, false, WINDINGUSAGE_VARY) 4753 { 4754 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS); 4755 } 4756 4757 protected: 4758 virtual vector<LevelCase> genTessLevelCases (void) const 4759 { 4760 const int numSubCasesPerEdge = 4; 4761 const int numEdges = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 4762 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4 4763 : -1; 4764 const vector<LevelCase> baseResult = PrimitiveSetInvarianceCase::genTessLevelCases(); 4765 vector<LevelCase> result; 4766 de::Random rnd (123); 4767 4768 // Generate variants with different values for irrelevant levels. 4769 for (int baseNdx = 0; baseNdx < (int)baseResult.size(); baseNdx++) 4770 { 4771 const TessLevels& base = baseResult[baseNdx].levels[0]; 4772 if (base.inner[0] == 1.0f || (m_primitiveType == TESSPRIMITIVETYPE_QUADS && base.inner[1] == 1.0f)) 4773 continue; 4774 4775 for (int edgeNdx = 0; edgeNdx < numEdges; edgeNdx++) 4776 { 4777 TessLevels levels = base; 4778 LevelCase levelCase; 4779 levelCase.mem = edgeNdx; 4780 4781 for (int subCaseNdx = 0; subCaseNdx < numSubCasesPerEdge; subCaseNdx++) 4782 { 4783 levelCase.levels.push_back(levels); 4784 4785 for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); i++) 4786 { 4787 if (i != edgeNdx) 4788 levels.outer[i] = rnd.getFloat(2.0f, 16.0f); 4789 } 4790 4791 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 4792 levels.inner[1] = rnd.getFloat(2.0f, 16.0f); 4793 } 4794 4795 result.push_back(levelCase); 4796 } 4797 } 4798 4799 return result; 4800 } 4801 4802 class IsTriangleTriangleOnOuterEdge 4803 { 4804 public: 4805 IsTriangleTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {} 4806 bool operator() (const Vec3* vertices) const 4807 { 4808 bool touchesAppropriateEdge = false; 4809 for (int v = 0; v < 3; v++) 4810 if (vertices[v][m_edgeNdx] == 0.0f) 4811 touchesAppropriateEdge = true; 4812 4813 if (touchesAppropriateEdge) 4814 { 4815 const Vec3 avg = (vertices[0] + vertices[1] + vertices[2]) / 3.0f; 4816 return avg[m_edgeNdx] < avg[(m_edgeNdx+1)%3] && 4817 avg[m_edgeNdx] < avg[(m_edgeNdx+2)%3]; 4818 } 4819 return false; 4820 } 4821 4822 private: 4823 int m_edgeNdx; 4824 }; 4825 4826 class IsQuadTriangleOnOuterEdge 4827 { 4828 public: 4829 IsQuadTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {} 4830 4831 bool onEdge (const Vec3& v) const 4832 { 4833 return v[m_edgeNdx%2] == (m_edgeNdx <= 1 ? 0.0f : 1.0f); 4834 } 4835 4836 static inline bool onAnyEdge (const Vec3& v) 4837 { 4838 return v[0] == 0.0f || v[0] == 1.0f || v[1] == 0.0f || v[1] == 1.0f; 4839 } 4840 4841 bool operator() (const Vec3* vertices) const 4842 { 4843 for (int v = 0; v < 3; v++) 4844 { 4845 const Vec3& a = vertices[v]; 4846 const Vec3& b = vertices[(v+1)%3]; 4847 const Vec3& c = vertices[(v+2)%3]; 4848 if (onEdge(a) && onEdge(b)) 4849 return true; 4850 if (onEdge(c) && !onAnyEdge(a) && !onAnyEdge(b) && a[m_edgeNdx%2] == b[m_edgeNdx%2]) 4851 return true; 4852 } 4853 4854 return false; 4855 } 4856 4857 private: 4858 int m_edgeNdx; 4859 }; 4860 4861 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int outerEdgeNdx) const 4862 { 4863 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 4864 { 4865 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(), 4866 IsTriangleTriangleOnOuterEdge(outerEdgeNdx), 4867 ("inner triangles, and outer triangles corresponding to other edge than edge " 4868 + outerEdgeDescriptions(m_primitiveType)[outerEdgeNdx].description()).c_str()); 4869 } 4870 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS) 4871 { 4872 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(), 4873 IsQuadTriangleOnOuterEdge(outerEdgeNdx), 4874 ("inner triangles, and outer triangles corresponding to other edge than edge " 4875 + outerEdgeDescriptions(m_primitiveType)[outerEdgeNdx].description()).c_str()); 4876 } 4877 else 4878 DE_ASSERT(false); 4879 4880 return true; 4881 } 4882 }; 4883 4884 /*--------------------------------------------------------------------*//*! 4885 * \brief Base class for testing individual components of tess coords 4886 * 4887 * Useful for testing parts of invariance rule #8. 4888 *//*--------------------------------------------------------------------*/ 4889 class TessCoordComponentInvarianceCase : public TestCase 4890 { 4891 public: 4892 TessCoordComponentInvarianceCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode) 4893 : TestCase (context, name, description) 4894 , m_primitiveType (primType) 4895 , m_spacing (spacing) 4896 , m_winding (winding) 4897 , m_usePointMode (usePointMode) 4898 { 4899 } 4900 4901 void init (void); 4902 void deinit (void); 4903 IterateResult iterate (void); 4904 4905 protected: 4906 virtual string tessEvalOutputComponentStatements (const char* tessCoordComponentName, const char* outputComponentName) const = 0; 4907 virtual bool checkTessCoordComponent (float component) const = 0; 4908 4909 private: 4910 static vector<float> genTessLevelCases (int numCases); 4911 4912 static const int RENDER_SIZE = 16; 4913 4914 const TessPrimitiveType m_primitiveType; 4915 const SpacingMode m_spacing; 4916 const Winding m_winding; 4917 const bool m_usePointMode; 4918 4919 SharedPtr<const glu::ShaderProgram> m_program; 4920 }; 4921 4922 void TessCoordComponentInvarianceCase::init (void) 4923 { 4924 checkTessellationSupport(m_context); 4925 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE); 4926 4927 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() 4928 << glu::VertexSource ("#version 310 es\n" 4929 "\n" 4930 "in highp float in_v_attr;\n" 4931 "out highp float in_tc_attr;\n" 4932 "\n" 4933 "void main (void)\n" 4934 "{\n" 4935 " in_tc_attr = in_v_attr;\n" 4936 "}\n") 4937 4938 << glu::TessellationControlSource ("#version 310 es\n" 4939 "#extension GL_EXT_tessellation_shader : require\n" 4940 "\n" 4941 "layout (vertices = 1) out;\n" 4942 "\n" 4943 "in highp float in_tc_attr[];\n" 4944 "\n" 4945 "void main (void)\n" 4946 "{\n" 4947 " gl_TessLevelInner[0] = in_tc_attr[0];\n" 4948 " gl_TessLevelInner[1] = in_tc_attr[1];\n" 4949 "\n" 4950 " gl_TessLevelOuter[0] = in_tc_attr[2];\n" 4951 " gl_TessLevelOuter[1] = in_tc_attr[3];\n" 4952 " gl_TessLevelOuter[2] = in_tc_attr[4];\n" 4953 " gl_TessLevelOuter[3] = in_tc_attr[5];\n" 4954 "}\n") 4955 4956 << glu::TessellationEvaluationSource ("#version 310 es\n" 4957 "#extension GL_EXT_tessellation_shader : require\n" 4958 "\n" 4959 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) + 4960 "\n" 4961 "out highp vec4 in_f_color;\n" 4962 "out highp vec3 out_te_output;\n" 4963 "\n" 4964 "void main (void)\n" 4965 "{\n" 4966 + tessEvalOutputComponentStatements("gl_TessCoord.x", "out_te_output.x") 4967 + tessEvalOutputComponentStatements("gl_TessCoord.y", "out_te_output.y") 4968 4969 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 4970 tessEvalOutputComponentStatements("gl_TessCoord.z", "out_te_output.z") : 4971 " out_te_output.z = 0.0f;\n") + 4972 " gl_Position = vec4(gl_TessCoord.xy, 0.0, 1.0);\n" 4973 " in_f_color = vec4(1.0);\n" 4974 "}\n") 4975 4976 << glu::FragmentSource ("#version 310 es\n" 4977 "\n" 4978 "layout (location = 0) out mediump vec4 o_color;\n" 4979 "\n" 4980 "in highp vec4 in_f_color;\n" 4981 "\n" 4982 "void main (void)\n" 4983 "{\n" 4984 " o_color = in_f_color;\n" 4985 "}\n") 4986 4987 << glu::TransformFeedbackVarying ("out_te_output") 4988 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS))); 4989 4990 m_testCtx.getLog() << *m_program; 4991 if (!m_program->isOk()) 4992 TCU_FAIL("Program compilation failed"); 4993 } 4994 4995 void TessCoordComponentInvarianceCase::deinit (void) 4996 { 4997 m_program.clear(); 4998 } 4999 5000 vector<float> TessCoordComponentInvarianceCase::genTessLevelCases (int numCases) 5001 { 5002 de::Random rnd(123); 5003 vector<float> result; 5004 5005 for (int i = 0; i < numCases; i++) 5006 for (int j = 0; j < 6; j++) 5007 result.push_back(rnd.getFloat(1.0f, 63.0f)); 5008 5009 return result; 5010 } 5011 5012 TessCoordComponentInvarianceCase::IterateResult TessCoordComponentInvarianceCase::iterate (void) 5013 { 5014 typedef TransformFeedbackHandler<Vec3> TFHandler; 5015 5016 TestLog& log = m_testCtx.getLog(); 5017 const RenderContext& renderCtx = m_context.getRenderContext(); 5018 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName())); 5019 const glw::Functions& gl = renderCtx.getFunctions(); 5020 const int numTessLevelCases = 32; 5021 const vector<float> tessLevels = genTessLevelCases(numTessLevelCases); 5022 const deUint32 programGL = m_program->getProgram(); 5023 5024 gl.useProgram(programGL); 5025 setViewport(gl, viewport); 5026 gl.patchParameteri(GL_PATCH_VERTICES, 6); 5027 5028 { 5029 // Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once. 5030 int maxNumVerticesInDrawCall = 0; 5031 for (int i = 0; i < numTessLevelCases; i++) 5032 maxNumVerticesInDrawCall = de::max(maxNumVerticesInDrawCall, referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &tessLevels[6*i+0], &tessLevels[6*i+2])); 5033 5034 { 5035 const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall); 5036 5037 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < numTessLevelCases; tessLevelCaseNdx++) 5038 { 5039 log << TestLog::Message << "Testing with tessellation levels: " << tessellationLevelsString(&tessLevels[6*tessLevelCaseNdx+0], &tessLevels[6*tessLevelCaseNdx+2]) << TestLog::EndMessage; 5040 5041 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)6, 0, &tessLevels[6*tessLevelCaseNdx]) }; 5042 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode), 5043 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], 6); 5044 5045 for (int vtxNdx = 0; vtxNdx < (int)tfResult.varying.size(); vtxNdx++) 5046 { 5047 const Vec3& vec = tfResult.varying[vtxNdx]; 5048 const int numComps = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2; 5049 5050 for (int compNdx = 0; compNdx < numComps; compNdx++) 5051 { 5052 if (!checkTessCoordComponent(vec[compNdx])) 5053 { 5054 log << TestLog::Message << "Note: output value at index " << vtxNdx << " is " 5055 << (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? de::toString(vec) : de::toString(vec.swizzle(0,1))) 5056 << TestLog::EndMessage; 5057 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid tessellation coordinate component"); 5058 return STOP; 5059 } 5060 } 5061 } 5062 } 5063 } 5064 } 5065 5066 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 5067 return STOP; 5068 } 5069 5070 /*--------------------------------------------------------------------*//*! 5071 * \brief Test first part of invariance rule #8 5072 * 5073 * Test that all (relevant) components of tess coord are in [0,1]. 5074 *//*--------------------------------------------------------------------*/ 5075 class TessCoordComponentRangeCase : public TessCoordComponentInvarianceCase 5076 { 5077 public: 5078 TessCoordComponentRangeCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode) 5079 : TessCoordComponentInvarianceCase(context, name, description, primType, spacing, winding, usePointMode) 5080 { 5081 } 5082 5083 protected: 5084 virtual string tessEvalOutputComponentStatements (const char* tessCoordComponentName, const char* outputComponentName) const 5085 { 5086 return string() + "\t" + outputComponentName + " = " + tessCoordComponentName + ";\n"; 5087 } 5088 5089 virtual bool checkTessCoordComponent (float component) const 5090 { 5091 if (!de::inRange(component, 0.0f, 1.0f)) 5092 { 5093 m_testCtx.getLog() << TestLog::Message << "Failure: tess coord component isn't in range [0,1]" << TestLog::EndMessage; 5094 return false; 5095 } 5096 return true; 5097 } 5098 }; 5099 5100 /*--------------------------------------------------------------------*//*! 5101 * \brief Test second part of invariance rule #8 5102 * 5103 * Test that all (relevant) components of tess coord are in [0,1] and 5104 * 1.0-c is exact for every such component c. 5105 *//*--------------------------------------------------------------------*/ 5106 class OneMinusTessCoordComponentCase : public TessCoordComponentInvarianceCase 5107 { 5108 public: 5109 OneMinusTessCoordComponentCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode) 5110 : TessCoordComponentInvarianceCase(context, name, description, primType, spacing, winding, usePointMode) 5111 { 5112 } 5113 5114 protected: 5115 virtual string tessEvalOutputComponentStatements (const char* tessCoordComponentName, const char* outputComponentName) const 5116 { 5117 return string() + " {\n" 5118 " float oneMinusComp = 1.0 - " + tessCoordComponentName + ";\n" 5119 " " + outputComponentName + " = " + tessCoordComponentName + " + oneMinusComp;\n" 5120 " }\n"; 5121 } 5122 5123 virtual bool checkTessCoordComponent (float component) const 5124 { 5125 if (component != 1.0f) 5126 { 5127 m_testCtx.getLog() << TestLog::Message << "Failure: comp + (1.0-comp) doesn't equal 1.0 for some component of tessellation coordinate" << TestLog::EndMessage; 5128 return false; 5129 } 5130 return true; 5131 } 5132 }; 5133 5134 /*--------------------------------------------------------------------*//*! 5135 * \brief Test that patch is discarded if relevant outer level <= 0.0 5136 * 5137 * Draws patches with different combinations of tessellation levels, 5138 * varying which levels are negative. Verifies by checking that colored 5139 * pixels exist inside the area of valid primitives, and only black pixels 5140 * exist inside the area of discarded primitives. An additional sanity 5141 * test is done, checking that the number of primitives written by TF is 5142 * correct. 5143 *//*--------------------------------------------------------------------*/ 5144 class PrimitiveDiscardCase : public TestCase 5145 { 5146 public: 5147 PrimitiveDiscardCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode) 5148 : TestCase (context, name, description) 5149 , m_primitiveType (primType) 5150 , m_spacing (spacing) 5151 , m_winding (winding) 5152 , m_usePointMode (usePointMode) 5153 { 5154 } 5155 5156 void init (void); 5157 void deinit (void); 5158 IterateResult iterate (void); 5159 5160 private: 5161 static vector<float> genAttributes (void); 5162 5163 static const int RENDER_SIZE = 256; 5164 5165 const TessPrimitiveType m_primitiveType; 5166 const SpacingMode m_spacing; 5167 const Winding m_winding; 5168 const bool m_usePointMode; 5169 5170 SharedPtr<const glu::ShaderProgram> m_program; 5171 }; 5172 5173 void PrimitiveDiscardCase::init (void) 5174 { 5175 checkTessellationSupport(m_context); 5176 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE); 5177 5178 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() 5179 << glu::VertexSource ("#version 310 es\n" 5180 "\n" 5181 "in highp float in_v_attr;\n" 5182 "out highp float in_tc_attr;\n" 5183 "\n" 5184 "void main (void)\n" 5185 "{\n" 5186 " in_tc_attr = in_v_attr;\n" 5187 "}\n") 5188 5189 << glu::TessellationControlSource ("#version 310 es\n" 5190 "#extension GL_EXT_tessellation_shader : require\n" 5191 "\n" 5192 "layout (vertices = 1) out;\n" 5193 "\n" 5194 "in highp float in_tc_attr[];\n" 5195 "\n" 5196 "patch out highp vec2 in_te_positionScale;\n" 5197 "patch out highp vec2 in_te_positionOffset;\n" 5198 "\n" 5199 "void main (void)\n" 5200 "{\n" 5201 " in_te_positionScale = vec2(in_tc_attr[6], in_tc_attr[7]);\n" 5202 " in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\n" 5203 "\n" 5204 " gl_TessLevelInner[0] = in_tc_attr[0];\n" 5205 " gl_TessLevelInner[1] = in_tc_attr[1];\n" 5206 "\n" 5207 " gl_TessLevelOuter[0] = in_tc_attr[2];\n" 5208 " gl_TessLevelOuter[1] = in_tc_attr[3];\n" 5209 " gl_TessLevelOuter[2] = in_tc_attr[4];\n" 5210 " gl_TessLevelOuter[3] = in_tc_attr[5];\n" 5211 "}\n") 5212 5213 << glu::TessellationEvaluationSource ("#version 310 es\n" 5214 "#extension GL_EXT_tessellation_shader : require\n" 5215 "\n" 5216 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) + 5217 "\n" 5218 "patch in highp vec2 in_te_positionScale;\n" 5219 "patch in highp vec2 in_te_positionOffset;\n" 5220 "\n" 5221 "out highp vec3 out_te_tessCoord;\n" 5222 "\n" 5223 "void main (void)\n" 5224 "{\n" 5225 " out_te_tessCoord = gl_TessCoord;\n" 5226 " gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n" 5227 "}\n") 5228 5229 << glu::FragmentSource ("#version 310 es\n" 5230 "\n" 5231 "layout (location = 0) out mediump vec4 o_color;\n" 5232 "\n" 5233 "void main (void)\n" 5234 "{\n" 5235 " o_color = vec4(1.0);\n" 5236 "}\n") 5237 5238 << glu::TransformFeedbackVarying ("out_te_tessCoord") 5239 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS))); 5240 5241 m_testCtx.getLog() << *m_program; 5242 if (!m_program->isOk()) 5243 TCU_FAIL("Program compilation failed"); 5244 } 5245 5246 void PrimitiveDiscardCase::deinit (void) 5247 { 5248 m_program.clear(); 5249 } 5250 5251 vector<float> PrimitiveDiscardCase::genAttributes (void) 5252 { 5253 // Generate input attributes (tessellation levels, and position scale and 5254 // offset) for a number of primitives. Each primitive has a different 5255 // combination of tessellatio levels; each level is either a valid 5256 // value or an "invalid" value (negative or zero, chosen from 5257 // invalidTessLevelChoices). 5258 5259 // \note The attributes are generated in such an order that all of the 5260 // valid attribute tuples come before the first invalid one both 5261 // in the result vector, and when scanning the resulting 2d grid 5262 // of primitives is scanned in y-major order. This makes 5263 // verification somewhat simpler. 5264 5265 static const float baseTessLevels[6] = { 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f }; 5266 static const float invalidTessLevelChoices[] = { -0.42f, 0.0f }; 5267 const int numChoices = 1 + DE_LENGTH_OF_ARRAY(invalidTessLevelChoices); 5268 float choices[6][numChoices]; 5269 vector<float> result; 5270 5271 for (int levelNdx = 0; levelNdx < 6; levelNdx++) 5272 for (int choiceNdx = 0; choiceNdx < numChoices; choiceNdx++) 5273 choices[levelNdx][choiceNdx] = choiceNdx == 0 ? baseTessLevels[levelNdx] : invalidTessLevelChoices[choiceNdx-1]; 5274 5275 { 5276 const int numCols = intPow(numChoices, 6/2); // sqrt(numChoices**6) == sqrt(number of primitives) 5277 const int numRows = numCols; 5278 int index = 0; 5279 int i[6]; 5280 // We could do this with some generic combination-generation function, but meh, it's not that bad. 5281 for (i[2] = 0; i[2] < numChoices; i[2]++) // First outer 5282 for (i[3] = 0; i[3] < numChoices; i[3]++) // Second outer 5283 for (i[4] = 0; i[4] < numChoices; i[4]++) // Third outer 5284 for (i[5] = 0; i[5] < numChoices; i[5]++) // Fourth outer 5285 for (i[0] = 0; i[0] < numChoices; i[0]++) // First inner 5286 for (i[1] = 0; i[1] < numChoices; i[1]++) // Second inner 5287 { 5288 for (int j = 0; j < 6; j++) 5289 result.push_back(choices[j][i[j]]); 5290 5291 { 5292 const int col = index % numCols; 5293 const int row = index / numCols; 5294 // Position scale. 5295 result.push_back((float)2.0f / (float)numCols); 5296 result.push_back((float)2.0f / (float)numRows); 5297 // Position offset. 5298 result.push_back((float)col / (float)numCols * 2.0f - 1.0f); 5299 result.push_back((float)row / (float)numRows * 2.0f - 1.0f); 5300 } 5301 5302 index++; 5303 } 5304 } 5305 5306 return result; 5307 } 5308 5309 PrimitiveDiscardCase::IterateResult PrimitiveDiscardCase::iterate (void) 5310 { 5311 typedef TransformFeedbackHandler<Vec3> TFHandler; 5312 5313 TestLog& log = m_testCtx.getLog(); 5314 const RenderContext& renderCtx = m_context.getRenderContext(); 5315 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName())); 5316 const glw::Functions& gl = renderCtx.getFunctions(); 5317 const vector<float> attributes = genAttributes(); 5318 const int numAttribsPerPrimitive = 6+2+2; // Tess levels, scale, offset. 5319 const int numPrimitives = (int)attributes.size() / numAttribsPerPrimitive; 5320 const deUint32 programGL = m_program->getProgram(); 5321 5322 gl.useProgram(programGL); 5323 setViewport(gl, viewport); 5324 gl.patchParameteri(GL_PATCH_VERTICES, numAttribsPerPrimitive); 5325 5326 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 5327 gl.clear(GL_COLOR_BUFFER_BIT); 5328 5329 // Check the convenience assertion that all discarded patches come after the last non-discarded patch. 5330 { 5331 bool discardedPatchEncountered = false; 5332 for (int patchNdx = 0; patchNdx < numPrimitives; patchNdx++) 5333 { 5334 const bool discard = isPatchDiscarded(m_primitiveType, &attributes[numAttribsPerPrimitive*patchNdx + 2]); 5335 DE_ASSERT(discard || !discardedPatchEncountered); 5336 discardedPatchEncountered = discard; 5337 } 5338 DE_UNREF(discardedPatchEncountered); 5339 } 5340 5341 { 5342 int numVerticesInDrawCall = 0; 5343 for (int patchNdx = 0; patchNdx < numPrimitives; patchNdx++) 5344 numVerticesInDrawCall += referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &attributes[numAttribsPerPrimitive*patchNdx+0], &attributes[numAttribsPerPrimitive*patchNdx+2]); 5345 5346 log << TestLog::Message << "Note: rendering " << numPrimitives << " patches; first patches have valid relevant outer levels, " 5347 << "but later patches have one or more invalid (i.e. less than or equal to 0.0) relevant outer levels" << TestLog::EndMessage; 5348 5349 { 5350 const TFHandler tfHandler (m_context.getRenderContext(), numVerticesInDrawCall); 5351 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)attributes.size(), 0, &attributes[0]) }; 5352 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode), 5353 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)attributes.size()); 5354 const tcu::Surface pixels = getPixels(renderCtx, viewport); 5355 5356 log << TestLog::Image("RenderedImage", "Rendered image", pixels); 5357 5358 if ((int)tfResult.varying.size() != numVerticesInDrawCall) 5359 { 5360 log << TestLog::Message << "Failure: expected " << numVerticesInDrawCall << " vertices from transform feedback, got " << tfResult.varying.size() << TestLog::EndMessage; 5361 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Wrong number of tessellation coordinates"); 5362 return STOP; 5363 } 5364 5365 // Check that white pixels are found around every non-discarded 5366 // patch, and that only black pixels are found after the last 5367 // non-discarded patch. 5368 { 5369 int lastWhitePixelRow = 0; 5370 int secondToLastWhitePixelRow = 0; 5371 int lastWhitePixelColumnOnSecondToLastWhitePixelRow = 0; 5372 5373 for (int patchNdx = 0; patchNdx < numPrimitives; patchNdx++) 5374 { 5375 const float* const attr = &attributes[numAttribsPerPrimitive*patchNdx]; 5376 const bool validLevels = !isPatchDiscarded(m_primitiveType, &attr[2]); 5377 5378 if (validLevels) 5379 { 5380 // Not a discarded patch; check that at least one white pixel is found in its area. 5381 5382 const float* const scale = &attr[6]; 5383 const float* const offset = &attr[8]; 5384 const int x0 = (int)(( offset[0] + 1.0f)*0.5f*(float)pixels.getWidth()) - 1; 5385 const int x1 = (int)((scale[0] + offset[0] + 1.0f)*0.5f*(float)pixels.getWidth()) + 1; 5386 const int y0 = (int)(( offset[1] + 1.0f)*0.5f*(float)pixels.getHeight()) - 1; 5387 const int y1 = (int)((scale[1] + offset[1] + 1.0f)*0.5f*(float)pixels.getHeight()) + 1; 5388 const bool isMSAA = renderCtx.getRenderTarget().getNumSamples() > 1; 5389 bool pixelOk = false; 5390 5391 if (y1 > lastWhitePixelRow) 5392 { 5393 secondToLastWhitePixelRow = lastWhitePixelRow; 5394 lastWhitePixelRow = y1; 5395 } 5396 lastWhitePixelColumnOnSecondToLastWhitePixelRow = x1; 5397 5398 for (int y = y0; y <= y1 && !pixelOk; y++) 5399 for (int x = x0; x <= x1 && !pixelOk; x++) 5400 { 5401 if (!de::inBounds(x, 0, pixels.getWidth()) || !de::inBounds(y, 0, pixels.getHeight())) 5402 continue; 5403 5404 if (isMSAA) 5405 { 5406 if (pixels.getPixel(x, y) != tcu::RGBA::black) 5407 pixelOk = true; 5408 } 5409 else 5410 { 5411 if (pixels.getPixel(x, y) == tcu::RGBA::white) 5412 pixelOk = true; 5413 } 5414 } 5415 5416 if (!pixelOk) 5417 { 5418 log << TestLog::Message << "Failure: expected at least one " << (isMSAA ? "non-black" : "white") << " pixel in the rectangle " 5419 << "[x0=" << x0 << ", y0=" << y0 << ", x1=" << x1 << ", y1=" << y1 << "]" << TestLog::EndMessage 5420 << TestLog::Message << "Note: the rectangle approximately corresponds to the patch with these tessellation levels: " 5421 << tessellationLevelsString(&attr[0], &attr[1]) << TestLog::EndMessage; 5422 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); 5423 return STOP; 5424 } 5425 } 5426 else 5427 { 5428 // First discarded primitive patch; the remaining are guaranteed to be discarded ones as well. 5429 5430 for (int y = 0; y < pixels.getHeight(); y++) 5431 for (int x = 0; x < pixels.getWidth(); x++) 5432 { 5433 if (y > lastWhitePixelRow || (y > secondToLastWhitePixelRow && x > lastWhitePixelColumnOnSecondToLastWhitePixelRow)) 5434 { 5435 if (pixels.getPixel(x, y) != tcu::RGBA::black) 5436 { 5437 log << TestLog::Message << "Failure: expected all pixels to be black in the area " 5438 << (lastWhitePixelColumnOnSecondToLastWhitePixelRow < pixels.getWidth()-1 5439 ? string() + "y > " + de::toString(lastWhitePixelRow) + " || (y > " + de::toString(secondToLastWhitePixelRow) 5440 + " && x > " + de::toString(lastWhitePixelColumnOnSecondToLastWhitePixelRow) + ")" 5441 : string() + "y > " + de::toString(lastWhitePixelRow)) 5442 << " (they all correspond to patches that should be discarded)" << TestLog::EndMessage 5443 << TestLog::Message << "Note: pixel " << tcu::IVec2(x, y) << " isn't black" << TestLog::EndMessage; 5444 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); 5445 return STOP; 5446 } 5447 } 5448 } 5449 5450 break; 5451 } 5452 } 5453 } 5454 } 5455 } 5456 5457 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 5458 return STOP; 5459 } 5460 5461 /*--------------------------------------------------------------------*//*! 5462 * \brief Case testing user-defined IO between TCS and TES 5463 * 5464 * TCS outputs various values to TES, including aggregates. The outputs 5465 * can be per-patch or per-vertex, and if per-vertex, they can also be in 5466 * an IO block. Per-vertex input array size can be left implicit (i.e. 5467 * inputArray[]) or explicit either by gl_MaxPatchVertices or an integer 5468 * literal whose value is queried from GL. 5469 * 5470 * The values output are generated in TCS and verified in TES against 5471 * similarly generated values. In case a verification of a value fails, the 5472 * index of the invalid value is output with TF. 5473 * As a sanity check, also the rendering result is verified (against pre- 5474 * rendered reference). 5475 *//*--------------------------------------------------------------------*/ 5476 class UserDefinedIOCase : public TestCase 5477 { 5478 public: 5479 enum IOType 5480 { 5481 IO_TYPE_PER_PATCH = 0, 5482 IO_TYPE_PER_PATCH_ARRAY, 5483 IO_TYPE_PER_VERTEX, 5484 IO_TYPE_PER_VERTEX_BLOCK, 5485 5486 IO_TYPE_LAST 5487 }; 5488 5489 enum VertexIOArraySize 5490 { 5491 VERTEX_IO_ARRAY_SIZE_IMPLICIT = 0, 5492 VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN, //!< Use gl_MaxPatchVertices as size for per-vertex input array. 5493 VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY, //!< Query GL_MAX_PATCH_VERTICES, and use that as size for per-vertex input array. 5494 5495 VERTEX_IO_ARRAY_SIZE_LAST 5496 }; 5497 5498 UserDefinedIOCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, IOType ioType, VertexIOArraySize vertexIOArraySize, const char* referenceImagePath) 5499 : TestCase (context, name, description) 5500 , m_primitiveType (primType) 5501 , m_ioType (ioType) 5502 , m_vertexIOArraySize (vertexIOArraySize) 5503 , m_referenceImagePath (referenceImagePath) 5504 { 5505 } 5506 5507 void init (void); 5508 void deinit (void); 5509 IterateResult iterate (void); 5510 5511 private: 5512 typedef string (*BasicTypeVisitFunc)(const string& name, glu::DataType type, int indentationDepth); //!< See glslTraverseBasicTypes below. 5513 5514 class TopLevelObject 5515 { 5516 public: 5517 virtual ~TopLevelObject (void) {} 5518 5519 virtual string name (void) const = 0; 5520 virtual string declare (const string& arraySizeExpr) const = 0; 5521 virtual string glslTraverseBasicTypes (int numArrayElements, //!< If negative, traverse just array[gl_InvocationID], not all indices. 5522 int indentationDepth, 5523 BasicTypeVisitFunc) const = 0; 5524 virtual int numBasicSubobjectsInElementType (void) const = 0; 5525 virtual string basicSubobjectAtIndex (int index, int arraySize) const = 0; 5526 }; 5527 5528 class Variable : public TopLevelObject 5529 { 5530 public: 5531 Variable (const string& name_, const glu::VarType& type, bool isArray) 5532 : m_name (name_) 5533 , m_type (type) 5534 , m_isArray (isArray) 5535 { 5536 DE_ASSERT(!type.isArrayType()); 5537 } 5538 5539 string name (void) const { return m_name; } 5540 string declare (const string& arraySizeExpr) const; 5541 string glslTraverseBasicTypes (int numArrayElements, int indentationDepth, BasicTypeVisitFunc) const; 5542 int numBasicSubobjectsInElementType (void) const; 5543 string basicSubobjectAtIndex (int index, int arraySize) const; 5544 5545 private: 5546 string m_name; 5547 glu::VarType m_type; //!< If this Variable is an array element, m_type is the element type; otherwise just the variable type. 5548 const bool m_isArray; 5549 }; 5550 5551 class IOBlock : public TopLevelObject 5552 { 5553 public: 5554 struct Member 5555 { 5556 string name; 5557 glu::VarType type; 5558 Member (const string& n, const glu::VarType& t) : name(n), type(t) {} 5559 }; 5560 5561 IOBlock (const string& blockName, const string& interfaceName, const vector<Member>& members) 5562 : m_blockName (blockName) 5563 , m_interfaceName (interfaceName) 5564 , m_members (members) 5565 { 5566 } 5567 5568 string name (void) const { return m_interfaceName; } 5569 string declare (const string& arraySizeExpr) const; 5570 string glslTraverseBasicTypes (int numArrayElements, int indentationDepth, BasicTypeVisitFunc) const; 5571 int numBasicSubobjectsInElementType (void) const; 5572 string basicSubobjectAtIndex (int index, int arraySize) const; 5573 5574 private: 5575 string m_blockName; 5576 string m_interfaceName; 5577 vector<Member> m_members; 5578 }; 5579 5580 static string glslTraverseBasicTypes (const string& rootName, 5581 const glu::VarType& rootType, 5582 int arrayNestingDepth, 5583 int indentationDepth, 5584 BasicTypeVisitFunc visit); 5585 5586 static string glslAssignBasicTypeObject (const string& name, glu::DataType, int indentationDepth); 5587 static string glslCheckBasicTypeObject (const string& name, glu::DataType, int indentationDepth); 5588 static int numBasicSubobjectsInElementType (const vector<SharedPtr<TopLevelObject> >&); 5589 static string basicSubobjectAtIndex (int index, const vector<SharedPtr<TopLevelObject> >&, int topLevelArraySizes); 5590 5591 static const int RENDER_SIZE = 256; 5592 static const int NUM_OUTPUT_VERTICES; 5593 static const int NUM_PER_PATCH_ARRAY_ELEMS; 5594 5595 const TessPrimitiveType m_primitiveType; 5596 const IOType m_ioType; 5597 const VertexIOArraySize m_vertexIOArraySize; 5598 const string m_referenceImagePath; 5599 5600 vector<glu::StructType> m_structTypes; 5601 vector<SharedPtr<TopLevelObject> > m_tcsOutputs; 5602 vector<SharedPtr<TopLevelObject> > m_tesInputs; 5603 5604 SharedPtr<const glu::ShaderProgram> m_program; 5605 }; 5606 5607 const int UserDefinedIOCase::NUM_OUTPUT_VERTICES = 5; 5608 const int UserDefinedIOCase::NUM_PER_PATCH_ARRAY_ELEMS = 3; 5609 5610 /*--------------------------------------------------------------------*//*! 5611 * \brief Generate GLSL code to traverse (possibly aggregate) object 5612 * 5613 * Generates a string that represents GLSL code that traverses the 5614 * basic-type subobjects in a rootType-typed object named rootName. Arrays 5615 * are traversed with loops and struct members are each traversed 5616 * separately. The code for each basic-type subobject is generated with 5617 * the function given as the 'visit' argument. 5618 *//*--------------------------------------------------------------------*/ 5619 string UserDefinedIOCase::glslTraverseBasicTypes (const string& rootName, 5620 const glu::VarType& rootType, 5621 int arrayNestingDepth, 5622 int indentationDepth, 5623 BasicTypeVisitFunc visit) 5624 { 5625 if (rootType.isBasicType()) 5626 return visit(rootName, rootType.getBasicType(), indentationDepth); 5627 else if (rootType.isArrayType()) 5628 { 5629 const string indentation = string(indentationDepth, '\t'); 5630 const string loopIndexName = "i" + de::toString(arrayNestingDepth); 5631 const string arrayLength = de::toString(rootType.getArraySize()); 5632 return indentation + "for (int " + loopIndexName + " = 0; " + loopIndexName + " < " + de::toString(rootType.getArraySize()) + "; " + loopIndexName + "++)\n" + 5633 indentation + "{\n" + 5634 glslTraverseBasicTypes(rootName + "[" + loopIndexName + "]", rootType.getElementType(), arrayNestingDepth+1, indentationDepth+1, visit) + 5635 indentation + "}\n"; 5636 } 5637 else if (rootType.isStructType()) 5638 { 5639 const glu::StructType& structType = *rootType.getStructPtr(); 5640 const int numMembers = structType.getNumMembers(); 5641 string result; 5642 5643 for (int membNdx = 0; membNdx < numMembers; membNdx++) 5644 { 5645 const glu::StructMember& member = structType.getMember(membNdx); 5646 result += glslTraverseBasicTypes(rootName + "." + member.getName(), member.getType(), arrayNestingDepth, indentationDepth, visit); 5647 } 5648 5649 return result; 5650 } 5651 else 5652 { 5653 DE_ASSERT(false); 5654 return DE_NULL; 5655 } 5656 } 5657 5658 string UserDefinedIOCase::Variable::declare (const string& sizeExpr) const 5659 { 5660 return de::toString(glu::declare(m_type, m_name)) + (m_isArray ? "[" + sizeExpr + "]" : "") + ";\n"; 5661 } 5662 5663 string UserDefinedIOCase::IOBlock::declare (const string& sizeExpr) const 5664 { 5665 string result = m_blockName + "\n" + 5666 "{\n"; 5667 for (int i = 0; i < (int)m_members.size(); i++) 5668 result += "\t" + de::toString(glu::declare(m_members[i].type, m_members[i].name)) + ";\n"; 5669 result += "} " + m_interfaceName + "[" + sizeExpr + "]" + ";\n"; 5670 return result; 5671 } 5672 5673 string UserDefinedIOCase::Variable::glslTraverseBasicTypes (int numArrayElements, int indentationDepth, BasicTypeVisitFunc visit) const 5674 { 5675 const bool traverseAsArray = m_isArray && numArrayElements >= 0; 5676 const string traversedName = m_name + (m_isArray && !traverseAsArray ? "[gl_InvocationID]" : ""); 5677 const glu::VarType type = traverseAsArray ? glu::VarType(m_type, numArrayElements) : m_type; 5678 5679 return UserDefinedIOCase::glslTraverseBasicTypes(traversedName, type, 0, indentationDepth, visit); 5680 } 5681 5682 string UserDefinedIOCase::IOBlock::glslTraverseBasicTypes (int numArrayElements, int indentationDepth, BasicTypeVisitFunc visit) const 5683 { 5684 if (numArrayElements >= 0) 5685 { 5686 const string indentation = string(indentationDepth, '\t'); 5687 string result = indentation + "for (int i0 = 0; i0 < " + de::toString(numArrayElements) + "; i0++)\n" + 5688 indentation + "{\n"; 5689 for (int i = 0; i < (int)m_members.size(); i++) 5690 result += UserDefinedIOCase::glslTraverseBasicTypes(m_interfaceName + "[i0]." + m_members[i].name, m_members[i].type, 1, indentationDepth+1, visit); 5691 result += indentation + "}\n"; 5692 return result; 5693 } 5694 else 5695 { 5696 string result; 5697 for (int i = 0; i < (int)m_members.size(); i++) 5698 result += UserDefinedIOCase::glslTraverseBasicTypes(m_interfaceName + "[gl_InvocationID]." + m_members[i].name, m_members[i].type, 0, indentationDepth, visit); 5699 return result; 5700 } 5701 } 5702 5703 int UserDefinedIOCase::Variable::numBasicSubobjectsInElementType (void) const 5704 { 5705 return numBasicSubobjects(m_type); 5706 } 5707 5708 int UserDefinedIOCase::IOBlock::numBasicSubobjectsInElementType (void) const 5709 { 5710 int result = 0; 5711 for (int i = 0; i < (int)m_members.size(); i++) 5712 result += numBasicSubobjects(m_members[i].type); 5713 return result; 5714 } 5715 5716 string UserDefinedIOCase::Variable::basicSubobjectAtIndex (int subobjectIndex, int arraySize) const 5717 { 5718 const glu::VarType type = m_isArray ? glu::VarType(m_type, arraySize) : m_type; 5719 int currentIndex = 0; 5720 5721 for (glu::BasicTypeIterator basicIt = glu::BasicTypeIterator::begin(&type); 5722 basicIt != glu::BasicTypeIterator::end(&type); 5723 ++basicIt) 5724 { 5725 if (currentIndex == subobjectIndex) 5726 return m_name + de::toString(glu::TypeAccessFormat(type, basicIt.getPath())); 5727 currentIndex++; 5728 } 5729 DE_ASSERT(false); 5730 return DE_NULL; 5731 } 5732 5733 string UserDefinedIOCase::IOBlock::basicSubobjectAtIndex (int subobjectIndex, int arraySize) const 5734 { 5735 int currentIndex = 0; 5736 for (int arrayNdx = 0; arrayNdx < arraySize; arrayNdx++) 5737 { 5738 for (int memberNdx = 0; memberNdx < (int)m_members.size(); memberNdx++) 5739 { 5740 const glu::VarType& membType = m_members[memberNdx].type; 5741 for (glu::BasicTypeIterator basicIt = glu::BasicTypeIterator::begin(&membType); 5742 basicIt != glu::BasicTypeIterator::end(&membType); 5743 ++basicIt) 5744 { 5745 if (currentIndex == subobjectIndex) 5746 return m_interfaceName + "[" + de::toString(arrayNdx) + "]." + m_members[memberNdx].name + de::toString(glu::TypeAccessFormat(membType, basicIt.getPath())); 5747 currentIndex++; 5748 } 5749 } 5750 } 5751 DE_ASSERT(false); 5752 return DE_NULL; 5753 } 5754 5755 // Used as the 'visit' argument for glslTraverseBasicTypes. 5756 string UserDefinedIOCase::glslAssignBasicTypeObject (const string& name, glu::DataType type, int indentationDepth) 5757 { 5758 const int scalarSize = glu::getDataTypeScalarSize(type); 5759 const string indentation = string(indentationDepth, '\t'); 5760 string result; 5761 5762 result += indentation + name + " = "; 5763 5764 if (type != glu::TYPE_FLOAT) 5765 result += string() + glu::getDataTypeName(type) + "("; 5766 for (int i = 0; i < scalarSize; i++) 5767 result += (i > 0 ? ", v+" + de::floatToString(0.8f*(float)i, 1) 5768 : "v"); 5769 if (type != glu::TYPE_FLOAT) 5770 result += ")"; 5771 result += ";\n" + 5772 indentation + "v += 0.4;\n"; 5773 return result; 5774 } 5775 5776 // Used as the 'visit' argument for glslTraverseBasicTypes. 5777 string UserDefinedIOCase::glslCheckBasicTypeObject (const string& name, glu::DataType type, int indentationDepth) 5778 { 5779 const int scalarSize = glu::getDataTypeScalarSize(type); 5780 const string indentation = string(indentationDepth, '\t'); 5781 string result; 5782 5783 result += indentation + "allOk = allOk && compare_" + glu::getDataTypeName(type) + "(" + name + ", "; 5784 5785 if (type != glu::TYPE_FLOAT) 5786 result += string() + glu::getDataTypeName(type) + "("; 5787 for (int i = 0; i < scalarSize; i++) 5788 result += (i > 0 ? ", v+" + de::floatToString(0.8f*(float)i, 1) 5789 : "v"); 5790 if (type != glu::TYPE_FLOAT) 5791 result += ")"; 5792 result += ");\n" + 5793 indentation + "v += 0.4;\n" + 5794 indentation + "if (allOk) firstFailedInputIndex++;\n"; 5795 5796 return result; 5797 } 5798 5799 int UserDefinedIOCase::numBasicSubobjectsInElementType (const vector<SharedPtr<TopLevelObject> >& objects) 5800 { 5801 int result = 0; 5802 for (int i = 0; i < (int)objects.size(); i++) 5803 result += objects[i]->numBasicSubobjectsInElementType(); 5804 return result; 5805 } 5806 5807 string UserDefinedIOCase::basicSubobjectAtIndex (int subobjectIndex, const vector<SharedPtr<TopLevelObject> >& objects, int topLevelArraySize) 5808 { 5809 int currentIndex = 0; 5810 int objectIndex = 0; 5811 for (; currentIndex < subobjectIndex; objectIndex++) 5812 currentIndex += objects[objectIndex]->numBasicSubobjectsInElementType() * topLevelArraySize; 5813 if (currentIndex > subobjectIndex) 5814 { 5815 objectIndex--; 5816 currentIndex -= objects[objectIndex]->numBasicSubobjectsInElementType() * topLevelArraySize; 5817 } 5818 5819 return objects[objectIndex]->basicSubobjectAtIndex(subobjectIndex - currentIndex, topLevelArraySize); 5820 } 5821 5822 void UserDefinedIOCase::init (void) 5823 { 5824 checkTessellationSupport(m_context); 5825 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE); 5826 5827 const bool isPerPatchIO = m_ioType == IO_TYPE_PER_PATCH || m_ioType == IO_TYPE_PER_PATCH_ARRAY; 5828 const bool isExplicitVertexArraySize = m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN || 5829 m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY; 5830 5831 const string vertexAttrArrayInputSize = m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_IMPLICIT ? "" 5832 : m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN ? "gl_MaxPatchVertices" 5833 : m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY ? de::toString(m_context.getContextInfo().getInt(GL_MAX_PATCH_VERTICES)) 5834 : DE_NULL; 5835 5836 const char* const maybePatch = isPerPatchIO ? "patch " : ""; 5837 const string outMaybePatch = string() + maybePatch + "out "; 5838 const string inMaybePatch = string() + maybePatch + "in "; 5839 const bool useBlock = m_ioType == IO_TYPE_PER_VERTEX_BLOCK; 5840 5841 string tcsDeclarations; 5842 string tcsStatements; 5843 5844 string tesDeclarations; 5845 string tesStatements; 5846 5847 { 5848 m_structTypes.push_back(glu::StructType("S")); 5849 5850 const glu::VarType highpFloat (glu::TYPE_FLOAT, glu::PRECISION_HIGHP); 5851 glu::StructType& structType = m_structTypes.back(); 5852 const glu::VarType structVarType (&structType); 5853 5854 structType.addMember("x", glu::VarType(glu::TYPE_INT, glu::PRECISION_HIGHP)); 5855 structType.addMember("y", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP)); 5856 structType.addMember("z", glu::VarType(highpFloat, 2)); 5857 5858 if (useBlock) 5859 { 5860 vector<IOBlock::Member> blockMembers; 5861 blockMembers.push_back(IOBlock::Member("blockS", structVarType)); 5862 blockMembers.push_back(IOBlock::Member("blockFa", glu::VarType(highpFloat, 3))); 5863 blockMembers.push_back(IOBlock::Member("blockSa", glu::VarType(structVarType, 2))); 5864 blockMembers.push_back(IOBlock::Member("blockF", highpFloat)); 5865 5866 m_tcsOutputs.push_back (SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "tcBlock", blockMembers))); 5867 m_tesInputs.push_back (SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "teBlock", blockMembers))); 5868 } 5869 else 5870 { 5871 const Variable var0("in_te_s", structVarType, m_ioType != IO_TYPE_PER_PATCH); 5872 const Variable var1("in_te_f", highpFloat, m_ioType != IO_TYPE_PER_PATCH); 5873 5874 m_tcsOutputs.push_back (SharedPtr<TopLevelObject>(new Variable(var0))); 5875 m_tesInputs.push_back (SharedPtr<TopLevelObject>(new Variable(var0))); 5876 m_tcsOutputs.push_back (SharedPtr<TopLevelObject>(new Variable(var1))); 5877 m_tesInputs.push_back (SharedPtr<TopLevelObject>(new Variable(var1))); 5878 } 5879 5880 tcsDeclarations += "in " + Variable("in_tc_attr", highpFloat, true).declare(vertexAttrArrayInputSize); 5881 tcsDeclarations += de::toString(glu::declare(structType)) + ";\n"; 5882 tcsStatements += "\t{\n" 5883 "\t\thighp float v = 1.3;\n"; 5884 5885 for (int tcsOutputNdx = 0; tcsOutputNdx < (int)m_tcsOutputs.size(); tcsOutputNdx++) 5886 { 5887 const TopLevelObject& output = *m_tcsOutputs[tcsOutputNdx]; 5888 5889 tcsDeclarations += outMaybePatch + output.declare(m_ioType == IO_TYPE_PER_PATCH_ARRAY ? de::toString(NUM_PER_PATCH_ARRAY_ELEMS) 5890 : isExplicitVertexArraySize ? de::toString(NUM_OUTPUT_VERTICES) 5891 : ""); 5892 if (!isPerPatchIO) 5893 tcsStatements += "\t\tv += float(gl_InvocationID)*" + de::floatToString(0.4f*output.numBasicSubobjectsInElementType(), 1) + ";\n"; 5894 5895 tcsStatements += "\n\t\t// Assign values to output " + output.name() + "\n" + 5896 output.glslTraverseBasicTypes(isPerPatchIO ? NUM_PER_PATCH_ARRAY_ELEMS : -1, 2, glslAssignBasicTypeObject); 5897 5898 if (!isPerPatchIO) 5899 tcsStatements += "\t\tv += float(" + de::toString(NUM_OUTPUT_VERTICES) + "-gl_InvocationID-1)*" + de::floatToString(0.4f*output.numBasicSubobjectsInElementType(), 1) + ";\n"; 5900 } 5901 tcsStatements += "\t}\n"; 5902 5903 tesDeclarations += de::toString(glu::declare(structType)) + ";\n"; 5904 tesStatements += "\tbool allOk = true;\n" 5905 "\thighp uint firstFailedInputIndex = 0u;\n" 5906 "\t{\n" 5907 "\t\thighp float v = 1.3;\n"; 5908 for (int tesInputNdx = 0; tesInputNdx < (int)m_tesInputs.size(); tesInputNdx++) 5909 { 5910 const TopLevelObject& input = *m_tesInputs[tesInputNdx]; 5911 tesDeclarations += inMaybePatch + input.declare(m_ioType == IO_TYPE_PER_PATCH_ARRAY ? de::toString(NUM_PER_PATCH_ARRAY_ELEMS) 5912 : isExplicitVertexArraySize ? de::toString(vertexAttrArrayInputSize) 5913 : ""); 5914 tesStatements += "\n\t\t// Check values in input " + input.name() + "\n" + 5915 input.glslTraverseBasicTypes(isPerPatchIO ? NUM_PER_PATCH_ARRAY_ELEMS : NUM_OUTPUT_VERTICES, 2, glslCheckBasicTypeObject); 5916 } 5917 tesStatements += "\t}\n"; 5918 } 5919 5920 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() 5921 << glu::VertexSource ("#version 310 es\n" 5922 "\n" 5923 "in highp float in_v_attr;\n" 5924 "out highp float in_tc_attr;\n" 5925 "\n" 5926 "void main (void)\n" 5927 "{\n" 5928 " in_tc_attr = in_v_attr;\n" 5929 "}\n") 5930 5931 << glu::TessellationControlSource ("#version 310 es\n" 5932 "#extension GL_EXT_tessellation_shader : require\n" 5933 "\n" 5934 "layout (vertices = " + de::toString(NUM_OUTPUT_VERTICES) + ") out;\n" 5935 "\n" 5936 + tcsDeclarations + 5937 "\n" 5938 "patch out highp vec2 in_te_positionScale;\n" 5939 "patch out highp vec2 in_te_positionOffset;\n" 5940 "\n" 5941 "void main (void)\n" 5942 "{\n" 5943 + tcsStatements + 5944 "\n" 5945 " in_te_positionScale = vec2(in_tc_attr[6], in_tc_attr[7]);\n" 5946 " in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\n" 5947 "\n" 5948 " gl_TessLevelInner[0] = in_tc_attr[0];\n" 5949 " gl_TessLevelInner[1] = in_tc_attr[1];\n" 5950 "\n" 5951 " gl_TessLevelOuter[0] = in_tc_attr[2];\n" 5952 " gl_TessLevelOuter[1] = in_tc_attr[3];\n" 5953 " gl_TessLevelOuter[2] = in_tc_attr[4];\n" 5954 " gl_TessLevelOuter[3] = in_tc_attr[5];\n" 5955 "}\n") 5956 5957 << glu::TessellationEvaluationSource ("#version 310 es\n" 5958 "#extension GL_EXT_tessellation_shader : require\n" 5959 "\n" 5960 + getTessellationEvaluationInLayoutString(m_primitiveType) + 5961 "\n" 5962 + tesDeclarations + 5963 "\n" 5964 "patch in highp vec2 in_te_positionScale;\n" 5965 "patch in highp vec2 in_te_positionOffset;\n" 5966 "\n" 5967 "out highp vec4 in_f_color;\n" 5968 "// Will contain the index of the first incorrect input,\n" 5969 "// or the number of inputs if all are correct\n" 5970 "flat out highp uint out_te_firstFailedInputIndex;\n" 5971 "\n" 5972 "bool compare_int (int a, int b) { return a == b; }\n" 5973 "bool compare_float (float a, float b) { return abs(a - b) < 0.01f; }\n" 5974 "bool compare_vec4 (vec4 a, vec4 b) { return all(lessThan(abs(a - b), vec4(0.01f))); }\n" 5975 "\n" 5976 "void main (void)\n" 5977 "{\n" 5978 + tesStatements + 5979 "\n" 5980 " gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n" 5981 " in_f_color = allOk ? vec4(0.0, 1.0, 0.0, 1.0)\n" 5982 " : vec4(1.0, 0.0, 0.0, 1.0);\n" 5983 " out_te_firstFailedInputIndex = firstFailedInputIndex;\n" 5984 "}\n") 5985 5986 << glu::FragmentSource ("#version 310 es\n" 5987 "\n" 5988 "layout (location = 0) out mediump vec4 o_color;\n" 5989 "\n" 5990 "in highp vec4 in_f_color;\n" 5991 "\n" 5992 "void main (void)\n" 5993 "{\n" 5994 " o_color = in_f_color;\n" 5995 "}\n") 5996 5997 << glu::TransformFeedbackVarying ("out_te_firstFailedInputIndex") 5998 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS))); 5999 6000 m_testCtx.getLog() << *m_program; 6001 if (!m_program->isOk()) 6002 TCU_FAIL("Program compilation failed"); 6003 } 6004 6005 void UserDefinedIOCase::deinit (void) 6006 { 6007 m_program.clear(); 6008 } 6009 6010 UserDefinedIOCase::IterateResult UserDefinedIOCase::iterate (void) 6011 { 6012 typedef TransformFeedbackHandler<deUint32> TFHandler; 6013 6014 TestLog& log = m_testCtx.getLog(); 6015 const RenderContext& renderCtx = m_context.getRenderContext(); 6016 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName())); 6017 const glw::Functions& gl = renderCtx.getFunctions(); 6018 static const float attributes[6+2+2] = { /* inner */ 3.0f, 4.0f, /* outer */ 5.0f, 6.0f, 7.0f, 8.0f, /* pos. scale */ 1.2f, 1.3f, /* pos. offset */ -0.3f, -0.4f }; 6019 const deUint32 programGL = m_program->getProgram(); 6020 const int numVertices = referenceVertexCount(m_primitiveType, SPACINGMODE_EQUAL, false, &attributes[0], &attributes[2]); 6021 const TFHandler tfHandler (renderCtx, numVertices); 6022 6023 gl.useProgram(programGL); 6024 setViewport(gl, viewport); 6025 gl.patchParameteri(GL_PATCH_VERTICES, DE_LENGTH_OF_ARRAY(attributes)); 6026 6027 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 6028 gl.clear(GL_COLOR_BUFFER_BIT); 6029 6030 { 6031 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, DE_LENGTH_OF_ARRAY(attributes), 0, &attributes[0]) }; 6032 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, false), 6033 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], DE_LENGTH_OF_ARRAY(attributes)); 6034 6035 { 6036 const tcu::Surface pixels = getPixels(renderCtx, viewport); 6037 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath.c_str()); 6038 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), pixels.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT); 6039 6040 if (!success) 6041 { 6042 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); 6043 return STOP; 6044 } 6045 } 6046 6047 if ((int)tfResult.varying.size() != numVertices) 6048 { 6049 log << TestLog::Message << "Failure: transform feedback returned " << tfResult.varying.size() << " vertices; expected " << numVertices << TestLog::EndMessage; 6050 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Wrong number of vertices"); 6051 return STOP; 6052 } 6053 6054 { 6055 const int topLevelArraySize = (m_ioType == IO_TYPE_PER_PATCH ? 1 6056 : m_ioType == IO_TYPE_PER_PATCH_ARRAY ? NUM_PER_PATCH_ARRAY_ELEMS 6057 : NUM_OUTPUT_VERTICES); 6058 const int numTEInputs = numBasicSubobjectsInElementType(m_tesInputs) * topLevelArraySize; 6059 6060 for (int vertexNdx = 0; vertexNdx < (int)numVertices; vertexNdx++) 6061 { 6062 if (tfResult.varying[vertexNdx] > (deUint32)numTEInputs) 6063 { 6064 log << TestLog::Message << "Failure: out_te_firstFailedInputIndex has value " << tfResult.varying[vertexNdx] 6065 << ", should be in range [0, " << numTEInputs << "]" << TestLog::EndMessage; 6066 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid transform feedback output"); 6067 return STOP; 6068 } 6069 else if (tfResult.varying[vertexNdx] != (deUint32)numTEInputs) 6070 { 6071 log << TestLog::Message << "Failure: in tessellation evaluation shader, check for input " 6072 << basicSubobjectAtIndex(tfResult.varying[vertexNdx], m_tesInputs, topLevelArraySize) << " failed" << TestLog::EndMessage; 6073 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid input value in tessellation evaluation shader"); 6074 return STOP; 6075 } 6076 } 6077 } 6078 } 6079 6080 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 6081 return STOP; 6082 } 6083 6084 /*--------------------------------------------------------------------*//*! 6085 * \brief Pass gl_Position between VS and TCS, or between TCS and TES. 6086 * 6087 * In TCS gl_Position is in the gl_out[] block and in TES in the gl_in[] 6088 * block, and has no special semantics in those. Arbitrary vec4 data can 6089 * thus be passed there. 6090 *//*--------------------------------------------------------------------*/ 6091 class GLPositionCase : public TestCase 6092 { 6093 public: 6094 enum CaseType 6095 { 6096 CASETYPE_VS_TO_TCS = 0, 6097 CASETYPE_TCS_TO_TES, 6098 CASETYPE_VS_TO_TCS_TO_TES, 6099 6100 CASETYPE_LAST 6101 }; 6102 6103 GLPositionCase (Context& context, const char* name, const char* description, CaseType caseType, const char* referenceImagePath) 6104 : TestCase (context, name, description) 6105 , m_caseType (caseType) 6106 , m_referenceImagePath (referenceImagePath) 6107 { 6108 } 6109 6110 void init (void); 6111 void deinit (void); 6112 IterateResult iterate (void); 6113 6114 static const char* getCaseTypeName (CaseType type); 6115 6116 private: 6117 static const int RENDER_SIZE = 256; 6118 6119 const CaseType m_caseType; 6120 const string m_referenceImagePath; 6121 6122 SharedPtr<const glu::ShaderProgram> m_program; 6123 }; 6124 6125 const char* GLPositionCase::getCaseTypeName (CaseType type) 6126 { 6127 switch (type) 6128 { 6129 case CASETYPE_VS_TO_TCS: return "gl_position_vs_to_tcs"; 6130 case CASETYPE_TCS_TO_TES: return "gl_position_tcs_to_tes"; 6131 case CASETYPE_VS_TO_TCS_TO_TES: return "gl_position_vs_to_tcs_to_tes"; 6132 default: 6133 DE_ASSERT(false); return DE_NULL; 6134 } 6135 } 6136 6137 void GLPositionCase::init (void) 6138 { 6139 checkTessellationSupport(m_context); 6140 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE); 6141 6142 const bool vsToTCS = m_caseType == CASETYPE_VS_TO_TCS || m_caseType == CASETYPE_VS_TO_TCS_TO_TES; 6143 const bool tcsToTES = m_caseType == CASETYPE_TCS_TO_TES || m_caseType == CASETYPE_VS_TO_TCS_TO_TES; 6144 6145 const string tesIn0 = tcsToTES ? "gl_in[0].gl_Position" : "in_te_attr[0]"; 6146 const string tesIn1 = tcsToTES ? "gl_in[1].gl_Position" : "in_te_attr[1]"; 6147 const string tesIn2 = tcsToTES ? "gl_in[2].gl_Position" : "in_te_attr[2]"; 6148 6149 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() 6150 << glu::VertexSource ("#version 310 es\n" 6151 "\n" 6152 "in highp vec4 in_v_attr;\n" 6153 + string(!vsToTCS ? "out highp vec4 in_tc_attr;\n" : "") + 6154 "\n" 6155 "void main (void)\n" 6156 "{\n" 6157 " " + (vsToTCS ? "gl_Position" : "in_tc_attr") + " = in_v_attr;\n" 6158 "}\n") 6159 6160 << glu::TessellationControlSource ("#version 310 es\n" 6161 "#extension GL_EXT_tessellation_shader : require\n" 6162 "\n" 6163 "layout (vertices = 3) out;\n" 6164 "\n" 6165 + string(!vsToTCS ? "in highp vec4 in_tc_attr[];\n" : "") + 6166 "\n" 6167 + (!tcsToTES ? "out highp vec4 in_te_attr[];\n" : "") + 6168 "\n" 6169 "void main (void)\n" 6170 "{\n" 6171 " " + (tcsToTES ? "gl_out[gl_InvocationID].gl_Position" : "in_te_attr[gl_InvocationID]") + " = " 6172 + (vsToTCS ? "gl_in[gl_InvocationID].gl_Position" : "in_tc_attr[gl_InvocationID]") + ";\n" 6173 "\n" 6174 " gl_TessLevelInner[0] = 2.0;\n" 6175 " gl_TessLevelInner[1] = 3.0;\n" 6176 "\n" 6177 " gl_TessLevelOuter[0] = 4.0;\n" 6178 " gl_TessLevelOuter[1] = 5.0;\n" 6179 " gl_TessLevelOuter[2] = 6.0;\n" 6180 " gl_TessLevelOuter[3] = 7.0;\n" 6181 "}\n") 6182 6183 << glu::TessellationEvaluationSource ("#version 310 es\n" 6184 "#extension GL_EXT_tessellation_shader : require\n" 6185 "\n" 6186 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_TRIANGLES) + 6187 "\n" 6188 + (!tcsToTES ? "in highp vec4 in_te_attr[];\n" : "") + 6189 "\n" 6190 "out highp vec4 in_f_color;\n" 6191 "\n" 6192 "void main (void)\n" 6193 "{\n" 6194 " highp vec2 xy = gl_TessCoord.x * " + tesIn0 + ".xy\n" 6195 " + gl_TessCoord.y * " + tesIn1 + ".xy\n" 6196 " + gl_TessCoord.z * " + tesIn2 + ".xy;\n" 6197 " gl_Position = vec4(xy, 0.0, 1.0);\n" 6198 " in_f_color = vec4(" + tesIn0 + ".z + " + tesIn1 + ".w,\n" 6199 " " + tesIn2 + ".z + " + tesIn0 + ".w,\n" 6200 " " + tesIn1 + ".z + " + tesIn2 + ".w,\n" 6201 " 1.0);\n" 6202 "}\n") 6203 6204 << glu::FragmentSource ("#version 310 es\n" 6205 "\n" 6206 "layout (location = 0) out mediump vec4 o_color;\n" 6207 "\n" 6208 "in highp vec4 in_f_color;\n" 6209 "\n" 6210 "void main (void)\n" 6211 "{\n" 6212 " o_color = in_f_color;\n" 6213 "}\n"))); 6214 6215 m_testCtx.getLog() << *m_program; 6216 if (!m_program->isOk()) 6217 TCU_FAIL("Program compilation failed"); 6218 } 6219 6220 void GLPositionCase::deinit (void) 6221 { 6222 m_program.clear(); 6223 } 6224 6225 GLPositionCase::IterateResult GLPositionCase::iterate (void) 6226 { 6227 TestLog& log = m_testCtx.getLog(); 6228 const RenderContext& renderCtx = m_context.getRenderContext(); 6229 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName())); 6230 const glw::Functions& gl = renderCtx.getFunctions(); 6231 const deUint32 programGL = m_program->getProgram(); 6232 6233 static const float attributes[3*4] = 6234 { 6235 -0.8f, -0.7f, 0.1f, 0.7f, 6236 -0.5f, 0.4f, 0.2f, 0.5f, 6237 0.3f, 0.2f, 0.3f, 0.45f 6238 }; 6239 6240 gl.useProgram(programGL); 6241 setViewport(gl, viewport); 6242 gl.patchParameteri(GL_PATCH_VERTICES, 3); 6243 6244 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 6245 gl.clear(GL_COLOR_BUFFER_BIT); 6246 6247 log << TestLog::Message << "Note: input data for in_v_attr:\n" << arrayStr(attributes, 4) << TestLog::EndMessage; 6248 6249 { 6250 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 4, 3, 0, &attributes[0]) }; 6251 glu::draw(renderCtx, programGL, DE_LENGTH_OF_ARRAY(bindings), &bindings[0], glu::pr::Patches(3)); 6252 6253 { 6254 const tcu::Surface pixels = getPixels(renderCtx, viewport); 6255 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath.c_str()); 6256 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), pixels.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT); 6257 6258 if (!success) 6259 { 6260 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); 6261 return STOP; 6262 } 6263 } 6264 } 6265 6266 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 6267 return STOP; 6268 } 6269 6270 } // anonymous 6271 6272 TessellationTests::TessellationTests (Context& context) 6273 : TestCaseGroup(context, "tessellation", "Tessellation Tests") 6274 { 6275 } 6276 6277 TessellationTests::~TessellationTests (void) 6278 { 6279 } 6280 6281 void TessellationTests::init (void) 6282 { 6283 { 6284 TestCaseGroup* const tessCoordGroup = new TestCaseGroup(m_context, "tesscoord", "Get tessellation coordinates with transform feedback and validate them"); 6285 addChild(tessCoordGroup); 6286 6287 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++) 6288 { 6289 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI; 6290 6291 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++) 6292 tessCoordGroup->addChild(new TessCoordCase(m_context, 6293 (string() + getTessPrimitiveTypeShaderName(primitiveType) + "_" + getSpacingModeShaderName((SpacingMode)spacingI)).c_str(), "", 6294 primitiveType, (SpacingMode)spacingI)); 6295 } 6296 } 6297 6298 { 6299 TestCaseGroup* const windingGroup = new TestCaseGroup(m_context, "winding", "Test the cw and ccw input layout qualifiers"); 6300 addChild(windingGroup); 6301 6302 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++) 6303 { 6304 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI; 6305 if (primitiveType == TESSPRIMITIVETYPE_ISOLINES) 6306 continue; 6307 6308 for (int windingI = 0; windingI < WINDING_LAST; windingI++) 6309 { 6310 const Winding winding = (Winding)windingI; 6311 windingGroup->addChild(new WindingCase(m_context, (string() + getTessPrimitiveTypeShaderName(primitiveType) + "_" + getWindingShaderName(winding)).c_str(), "", primitiveType, winding)); 6312 } 6313 } 6314 } 6315 6316 { 6317 TestCaseGroup* const shaderInputOutputGroup = new TestCaseGroup(m_context, "shader_input_output", "Test tessellation control and evaluation shader inputs and outputs"); 6318 addChild(shaderInputOutputGroup); 6319 6320 { 6321 static const struct 6322 { 6323 int inPatchSize; 6324 int outPatchSize; 6325 } patchVertexCountCases[] = 6326 { 6327 { 5, 10 }, 6328 { 10, 5 } 6329 }; 6330 6331 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(patchVertexCountCases); caseNdx++) 6332 { 6333 const int inSize = patchVertexCountCases[caseNdx].inPatchSize; 6334 const int outSize = patchVertexCountCases[caseNdx].outPatchSize; 6335 6336 const string caseName = "patch_vertices_" + de::toString(inSize) + "_in_" + de::toString(outSize) + "_out"; 6337 6338 shaderInputOutputGroup->addChild(new PatchVertexCountCase(m_context, caseName.c_str(), "Test input and output patch vertex counts", inSize, outSize, 6339 ("data/tessellation/" + caseName + "_ref.png").c_str())); 6340 } 6341 } 6342 6343 for (int caseTypeI = 0; caseTypeI < PerPatchDataCase::CASETYPE_LAST; caseTypeI++) 6344 { 6345 const PerPatchDataCase::CaseType caseType = (PerPatchDataCase::CaseType)caseTypeI; 6346 const char* const caseName = PerPatchDataCase::getCaseTypeName(caseType); 6347 6348 shaderInputOutputGroup->addChild(new PerPatchDataCase(m_context, caseName, PerPatchDataCase::getCaseTypeDescription(caseType), caseType, 6349 PerPatchDataCase::caseTypeUsesRefImageFromFile(caseType) ? (string() + "data/tessellation/" + caseName + "_ref.png").c_str() : DE_NULL)); 6350 } 6351 6352 for (int caseTypeI = 0; caseTypeI < GLPositionCase::CASETYPE_LAST; caseTypeI++) 6353 { 6354 const GLPositionCase::CaseType caseType = (GLPositionCase::CaseType)caseTypeI; 6355 const char* const caseName = GLPositionCase::getCaseTypeName(caseType); 6356 6357 shaderInputOutputGroup->addChild(new GLPositionCase(m_context, caseName, "", caseType, "data/tessellation/gl_position_ref.png")); 6358 } 6359 6360 shaderInputOutputGroup->addChild(new BarrierCase(m_context, "barrier", "Basic barrier usage", "data/tessellation/barrier_ref.png")); 6361 } 6362 6363 { 6364 TestCaseGroup* const miscDrawGroup = new TestCaseGroup(m_context, "misc_draw", "Miscellaneous draw-result-verifying cases"); 6365 addChild(miscDrawGroup); 6366 6367 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++) 6368 { 6369 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI; 6370 if (primitiveType == TESSPRIMITIVETYPE_ISOLINES) 6371 continue; 6372 6373 const char* const primTypeName = getTessPrimitiveTypeShaderName(primitiveType); 6374 6375 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++) 6376 { 6377 const string caseName = string() + "fill_cover_" + primTypeName + "_" + getSpacingModeShaderName((SpacingMode)spacingI); 6378 6379 miscDrawGroup->addChild(new BasicTriangleFillCoverCase(m_context, 6380 caseName.c_str(), "Check that there are no obvious gaps in the triangle-filled area of a tessellated shape", 6381 primitiveType, (SpacingMode)spacingI, 6382 ("data/tessellation/" + caseName + "_ref").c_str())); 6383 } 6384 } 6385 6386 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++) 6387 { 6388 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI; 6389 if (primitiveType == TESSPRIMITIVETYPE_ISOLINES) 6390 continue; 6391 6392 const char* const primTypeName = getTessPrimitiveTypeShaderName(primitiveType); 6393 6394 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++) 6395 { 6396 const string caseName = string() + "fill_overlap_" + primTypeName + "_" + getSpacingModeShaderName((SpacingMode)spacingI); 6397 6398 miscDrawGroup->addChild(new BasicTriangleFillNonOverlapCase(m_context, 6399 caseName.c_str(), "Check that there are no obvious triangle overlaps in the triangle-filled area of a tessellated shape", 6400 primitiveType, (SpacingMode)spacingI, 6401 ("data/tessellation/" + caseName + "_ref").c_str())); 6402 } 6403 } 6404 6405 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++) 6406 { 6407 const string caseName = string() + "isolines_" + getSpacingModeShaderName((SpacingMode)spacingI); 6408 6409 miscDrawGroup->addChild(new IsolinesRenderCase(m_context, 6410 caseName.c_str(), "Basic isolines render test", 6411 (SpacingMode)spacingI, 6412 ("data/tessellation/" + caseName + "_ref").c_str())); 6413 } 6414 } 6415 6416 { 6417 TestCaseGroup* const commonEdgeGroup = new TestCaseGroup(m_context, "common_edge", "Draw multiple adjacent shapes and check that no cracks appear between them"); 6418 addChild(commonEdgeGroup); 6419 6420 for (int caseTypeI = 0; caseTypeI < CommonEdgeCase::CASETYPE_LAST; caseTypeI++) 6421 { 6422 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++) 6423 { 6424 const CommonEdgeCase::CaseType caseType = (CommonEdgeCase::CaseType)caseTypeI; 6425 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI; 6426 if (primitiveType == TESSPRIMITIVETYPE_ISOLINES) 6427 continue; 6428 6429 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++) 6430 { 6431 const SpacingMode spacing = (SpacingMode)spacingI; 6432 const string caseName = (string() + getTessPrimitiveTypeShaderName(primitiveType) 6433 + "_" + getSpacingModeShaderName(spacing) 6434 + (caseType == CommonEdgeCase::CASETYPE_BASIC ? "" 6435 : caseType == CommonEdgeCase::CASETYPE_PRECISE ? "_precise" 6436 : DE_NULL)); 6437 6438 commonEdgeGroup->addChild(new CommonEdgeCase(m_context, caseName.c_str(), "", primitiveType, spacing, caseType)); 6439 } 6440 } 6441 } 6442 } 6443 6444 { 6445 TestCaseGroup* const fractionalSpacingModeGroup = new TestCaseGroup(m_context, "fractional_spacing", "Test fractional spacing modes"); 6446 addChild(fractionalSpacingModeGroup); 6447 6448 fractionalSpacingModeGroup->addChild(new FractionalSpacingModeCase(m_context, "odd", "", SPACINGMODE_FRACTIONAL_ODD)); 6449 fractionalSpacingModeGroup->addChild(new FractionalSpacingModeCase(m_context, "even", "", SPACINGMODE_FRACTIONAL_EVEN)); 6450 } 6451 6452 { 6453 TestCaseGroup* const primitiveDiscardGroup = new TestCaseGroup(m_context, "primitive_discard", "Test primitive discard with relevant outer tessellation level <= 0.0"); 6454 addChild(primitiveDiscardGroup); 6455 6456 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++) 6457 { 6458 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++) 6459 { 6460 for (int windingI = 0; windingI < WINDING_LAST; windingI++) 6461 { 6462 for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++) 6463 { 6464 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI; 6465 const SpacingMode spacing = (SpacingMode)spacingI; 6466 const Winding winding = (Winding)windingI; 6467 const bool usePointMode = usePointModeI != 0; 6468 6469 primitiveDiscardGroup->addChild(new PrimitiveDiscardCase(m_context, (string() + getTessPrimitiveTypeShaderName(primitiveType) 6470 + "_" + getSpacingModeShaderName(spacing) 6471 + "_" + getWindingShaderName(winding) 6472 + (usePointMode ? "_point_mode" : "")).c_str(), "", 6473 primitiveType, spacing, winding, usePointMode)); 6474 } 6475 } 6476 } 6477 } 6478 } 6479 6480 { 6481 TestCaseGroup* const invarianceGroup = new TestCaseGroup(m_context, "invariance", "Test tessellation invariance rules"); 6482 6483 TestCaseGroup* const invariantPrimitiveSetGroup = new TestCaseGroup(m_context, "primitive_set", "Test invariance rule #1"); 6484 TestCaseGroup* const invariantOuterEdgeGroup = new TestCaseGroup(m_context, "outer_edge_division", "Test invariance rule #2"); 6485 TestCaseGroup* const symmetricOuterEdgeGroup = new TestCaseGroup(m_context, "outer_edge_symmetry", "Test invariance rule #3"); 6486 TestCaseGroup* const outerEdgeVertexSetIndexIndependenceGroup = new TestCaseGroup(m_context, "outer_edge_index_independence", "Test invariance rule #4"); 6487 TestCaseGroup* const invariantTriangleSetGroup = new TestCaseGroup(m_context, "triangle_set", "Test invariance rule #5"); 6488 TestCaseGroup* const invariantInnerTriangleSetGroup = new TestCaseGroup(m_context, "inner_triangle_set", "Test invariance rule #6"); 6489 TestCaseGroup* const invariantOuterTriangleSetGroup = new TestCaseGroup(m_context, "outer_triangle_set", "Test invariance rule #7"); 6490 TestCaseGroup* const tessCoordComponentRangeGroup = new TestCaseGroup(m_context, "tess_coord_component_range", "Test invariance rule #8, first part"); 6491 TestCaseGroup* const oneMinusTessCoordComponentGroup = new TestCaseGroup(m_context, "one_minus_tess_coord_component", "Test invariance rule #8, second part"); 6492 6493 addChild(invarianceGroup); 6494 invarianceGroup->addChild(invariantPrimitiveSetGroup); 6495 invarianceGroup->addChild(invariantOuterEdgeGroup); 6496 invarianceGroup->addChild(symmetricOuterEdgeGroup); 6497 invarianceGroup->addChild(outerEdgeVertexSetIndexIndependenceGroup); 6498 invarianceGroup->addChild(invariantTriangleSetGroup); 6499 invarianceGroup->addChild(invariantInnerTriangleSetGroup); 6500 invarianceGroup->addChild(invariantOuterTriangleSetGroup); 6501 invarianceGroup->addChild(tessCoordComponentRangeGroup); 6502 invarianceGroup->addChild(oneMinusTessCoordComponentGroup); 6503 6504 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++) 6505 { 6506 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI; 6507 const string primName = getTessPrimitiveTypeShaderName(primitiveType); 6508 const bool triOrQuad = primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS; 6509 6510 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++) 6511 { 6512 const SpacingMode spacing = (SpacingMode)spacingI; 6513 const string primSpacName = primName + "_" + getSpacingModeShaderName(spacing); 6514 6515 if (triOrQuad) 6516 { 6517 invariantOuterEdgeGroup->addChild (new InvariantOuterEdgeCase (m_context, primSpacName.c_str(), "", primitiveType, spacing)); 6518 invariantTriangleSetGroup->addChild (new InvariantTriangleSetCase (m_context, primSpacName.c_str(), "", primitiveType, spacing)); 6519 invariantInnerTriangleSetGroup->addChild(new InvariantInnerTriangleSetCase (m_context, primSpacName.c_str(), "", primitiveType, spacing)); 6520 invariantOuterTriangleSetGroup->addChild(new InvariantOuterTriangleSetCase (m_context, primSpacName.c_str(), "", primitiveType, spacing)); 6521 } 6522 6523 for (int windingI = 0; windingI < WINDING_LAST; windingI++) 6524 { 6525 const Winding winding = (Winding)windingI; 6526 const string primSpacWindName = primSpacName + "_" + getWindingShaderName(winding); 6527 6528 for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++) 6529 { 6530 const bool usePointMode = usePointModeI != 0; 6531 const string primSpacWindPointName = primSpacWindName + (usePointMode ? "_point_mode" : ""); 6532 6533 invariantPrimitiveSetGroup->addChild (new InvariantPrimitiveSetCase (m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode)); 6534 symmetricOuterEdgeGroup->addChild (new SymmetricOuterEdgeCase (m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode)); 6535 tessCoordComponentRangeGroup->addChild (new TessCoordComponentRangeCase (m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode)); 6536 oneMinusTessCoordComponentGroup->addChild (new OneMinusTessCoordComponentCase (m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode)); 6537 6538 if (triOrQuad) 6539 outerEdgeVertexSetIndexIndependenceGroup->addChild(new OuterEdgeVertexSetIndexIndependenceCase(m_context, primSpacWindPointName.c_str(), "", 6540 primitiveType, spacing, winding, usePointMode)); 6541 } 6542 } 6543 } 6544 } 6545 } 6546 6547 { 6548 TestCaseGroup* const userDefinedIOGroup = new TestCaseGroup(m_context, "user_defined_io", "Test non-built-in per-patch and per-vertex inputs and outputs"); 6549 addChild(userDefinedIOGroup); 6550 6551 for (int ioTypeI = 0; ioTypeI < UserDefinedIOCase::IO_TYPE_LAST; ioTypeI++) 6552 { 6553 const UserDefinedIOCase::IOType ioType = (UserDefinedIOCase::IOType)ioTypeI; 6554 TestCaseGroup* const ioTypeGroup = new TestCaseGroup(m_context, 6555 ioType == UserDefinedIOCase::IO_TYPE_PER_PATCH ? "per_patch" 6556 : ioType == UserDefinedIOCase::IO_TYPE_PER_PATCH_ARRAY ? "per_patch_array" 6557 : ioType == UserDefinedIOCase::IO_TYPE_PER_VERTEX ? "per_vertex" 6558 : ioType == UserDefinedIOCase::IO_TYPE_PER_VERTEX_BLOCK ? "per_vertex_block" 6559 : DE_NULL, 6560 ioType == UserDefinedIOCase::IO_TYPE_PER_PATCH ? "Per-patch TCS outputs" 6561 : ioType == UserDefinedIOCase::IO_TYPE_PER_PATCH_ARRAY ? "Per-patch array TCS outputs" 6562 : ioType == UserDefinedIOCase::IO_TYPE_PER_VERTEX ? "Per-vertex TCS outputs" 6563 : ioType == UserDefinedIOCase::IO_TYPE_PER_VERTEX_BLOCK ? "Per-vertex TCS outputs in IO block" 6564 : DE_NULL); 6565 userDefinedIOGroup->addChild(ioTypeGroup); 6566 6567 for (int vertexArraySizeI = 0; vertexArraySizeI < UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_LAST; vertexArraySizeI++) 6568 { 6569 const UserDefinedIOCase::VertexIOArraySize vertexArraySize = (UserDefinedIOCase::VertexIOArraySize)vertexArraySizeI; 6570 TestCaseGroup* const vertexArraySizeGroup = new TestCaseGroup(m_context, 6571 vertexArraySizeI == UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_IMPLICIT 6572 ? "vertex_io_array_size_implicit" 6573 : vertexArraySizeI == UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN 6574 ? "vertex_io_array_size_shader_builtin" 6575 : vertexArraySizeI == UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY 6576 ? "vertex_io_array_size_query" 6577 : DE_NULL, 6578 ""); 6579 ioTypeGroup->addChild(vertexArraySizeGroup); 6580 6581 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++) 6582 { 6583 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI; 6584 vertexArraySizeGroup->addChild(new UserDefinedIOCase(m_context, getTessPrimitiveTypeShaderName(primitiveType), "", primitiveType, ioType, vertexArraySize, 6585 (string() + "data/tessellation/user_defined_io_" + getTessPrimitiveTypeShaderName(primitiveType) + "_ref.png").c_str())); 6586 } 6587 } 6588 } 6589 } 6590 } 6591 6592 } // Functional 6593 } // gles31 6594 } // deqp 6595