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