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