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