1 /*------------------------------------------------------------------------ 2 * Vulkan Conformance Tests 3 * ------------------------ 4 * 5 * Copyright (c) 2014 The Android Open Source Project 6 * Copyright (c) 2016 The Khronos Group Inc. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * 20 *//*! 21 * \file 22 * \brief Tessellation Invariance Tests 23 *//*--------------------------------------------------------------------*/ 24 25 #include "vktTessellationInvarianceTests.hpp" 26 #include "vktTestCaseUtil.hpp" 27 #include "vktTessellationUtil.hpp" 28 29 #include "tcuTestLog.hpp" 30 #include "tcuVectorUtil.hpp" 31 32 #include "vkDefs.hpp" 33 #include "vkBarrierUtil.hpp" 34 #include "vkQueryUtil.hpp" 35 #include "vkBuilderUtil.hpp" 36 #include "vkImageUtil.hpp" 37 #include "vkTypeUtil.hpp" 38 #include "vkCmdUtil.hpp" 39 #include "vkObjUtil.hpp" 40 41 #include "deUniquePtr.hpp" 42 #include "deStringUtil.hpp" 43 #include "deRandom.hpp" 44 45 #include <string> 46 #include <vector> 47 #include <set> 48 49 namespace vkt 50 { 51 namespace tessellation 52 { 53 54 using namespace vk; 55 56 namespace 57 { 58 59 enum Constants 60 { 61 NUM_EXTRA_TESS_GEOM_INVOCATIONS = 4, // Need to set this value properly to allocate enough memory to store vertices data 62 NUM_TESS_LEVELS = 6, // two inner and four outer levels 63 }; 64 65 enum WindingUsage 66 { 67 WINDING_USAGE_CCW = 0, 68 WINDING_USAGE_CW, 69 WINDING_USAGE_VARY, 70 71 WINDING_USAGE_LAST, 72 }; 73 74 inline WindingUsage getWindingUsage (const Winding winding) 75 { 76 const WindingUsage usage = winding == WINDING_CCW ? WINDING_USAGE_CCW : 77 winding == WINDING_CW ? WINDING_USAGE_CW : WINDING_USAGE_LAST; 78 DE_ASSERT(usage != WINDING_USAGE_LAST); 79 return usage; 80 } 81 82 std::vector<Winding> getWindingCases (const WindingUsage windingUsage) 83 { 84 std::vector<Winding> cases; 85 switch (windingUsage) 86 { 87 case WINDING_USAGE_CCW: 88 cases.push_back(WINDING_CCW); 89 break; 90 case WINDING_USAGE_CW: 91 cases.push_back(WINDING_CW); 92 break; 93 case WINDING_USAGE_VARY: 94 cases.push_back(WINDING_CCW); 95 cases.push_back(WINDING_CW); 96 break; 97 default: 98 DE_ASSERT(false); 99 break; 100 } 101 return cases; 102 } 103 104 enum PointModeUsage 105 { 106 POINT_MODE_USAGE_DONT_USE = 0, 107 POINT_MODE_USAGE_USE, 108 POINT_MODE_USAGE_VARY, 109 110 POINT_MODE_USAGE_LAST, 111 }; 112 113 inline PointModeUsage getPointModeUsage (const bool usePointMode) 114 { 115 return usePointMode ? POINT_MODE_USAGE_USE : POINT_MODE_USAGE_DONT_USE; 116 } 117 118 std::vector<bool> getUsePointModeCases (const PointModeUsage pointModeUsage) 119 { 120 std::vector<bool> cases; 121 switch (pointModeUsage) 122 { 123 case POINT_MODE_USAGE_DONT_USE: 124 cases.push_back(false); 125 break; 126 case POINT_MODE_USAGE_USE: 127 cases.push_back(true); 128 break; 129 case POINT_MODE_USAGE_VARY: 130 cases.push_back(false); 131 cases.push_back(true); 132 break; 133 default: 134 DE_ASSERT(false); 135 break; 136 } 137 return cases; 138 } 139 140 //! Data captured in the shader per output primitive (in geometry stage). 141 struct PerPrimitive 142 { 143 deInt32 patchPrimitiveID; //!< gl_PrimitiveID in tessellation evaluation shader 144 deInt32 primitiveID; //!< ID of an output primitive in geometry shader (user-defined) 145 146 deInt32 unused_padding[2]; 147 148 tcu::Vec4 tessCoord[3]; //!< 3 coords for triangles/quads, 2 for isolines, 1 for point mode. Vec4 due to alignment. 149 }; 150 151 typedef std::vector<PerPrimitive> PerPrimitiveVec; 152 153 inline bool byPatchPrimitiveID (const PerPrimitive& a, const PerPrimitive& b) 154 { 155 return a.patchPrimitiveID < b.patchPrimitiveID; 156 } 157 158 inline std::string getProgramName (const std::string& baseName, const Winding winding, const bool usePointMode) 159 { 160 std::ostringstream str; 161 str << baseName << "_" << getWindingShaderName(winding) << (usePointMode ? "_point_mode" : ""); 162 return str.str(); 163 } 164 165 inline std::string getProgramName (const std::string& baseName, const bool usePointMode) 166 { 167 std::ostringstream str; 168 str << baseName << (usePointMode ? "_point_mode" : ""); 169 return str.str(); 170 } 171 172 inline std::string getProgramDescription (const Winding winding, const bool usePointMode) 173 { 174 std::ostringstream str; 175 str << "winding mode " << getWindingShaderName(winding) << ", " << (usePointMode ? "" : "don't ") << "use point mode"; 176 return str.str(); 177 }; 178 179 template <typename T, int N> 180 std::vector<T> arrayToVector (const T (&arr)[N]) 181 { 182 return std::vector<T>(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr)); 183 } 184 185 template <typename T, int N> 186 T arrayMax (const T (&arr)[N]) 187 { 188 return *std::max_element(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr)); 189 } 190 191 template <int Size> 192 inline tcu::Vector<bool, Size> singleTrueMask (int index) 193 { 194 DE_ASSERT(de::inBounds(index, 0, Size)); 195 tcu::Vector<bool, Size> result; 196 result[index] = true; 197 return result; 198 } 199 200 template <typename ContainerT, typename T> 201 inline bool contains (const ContainerT& c, const T& key) 202 { 203 return c.find(key) != c.end(); 204 } 205 206 template <typename SeqT, int Size, typename Pred> 207 class LexCompare 208 { 209 public: 210 LexCompare (void) : m_pred(Pred()) {} 211 212 bool operator() (const SeqT& a, const SeqT& b) const 213 { 214 for (int i = 0; i < Size; ++i) 215 { 216 if (m_pred(a[i], b[i])) 217 return true; 218 if (m_pred(b[i], a[i])) 219 return false; 220 } 221 return false; 222 } 223 224 private: 225 Pred m_pred; 226 }; 227 228 template <int Size> 229 class VecLexLessThan : public LexCompare<tcu::Vector<float, Size>, Size, std::less<float> > 230 { 231 }; 232 233 //! Add default programs for invariance tests. 234 //! Creates multiple shader programs for combinations of winding and point mode. 235 //! mirrorCoords - special mode where some tessellation coordinates are mirrored in tessellation evaluation shader. 236 //! This is used by symmetric outer edge test. 237 void addDefaultPrograms (vk::SourceCollections& programCollection, 238 const TessPrimitiveType primitiveType, 239 const SpacingMode spacingMode, 240 const WindingUsage windingUsage, 241 const PointModeUsage pointModeUsage, 242 const bool mirrorCoords = false) 243 { 244 // Vertex shader 245 { 246 std::ostringstream src; 247 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 248 << "\n" 249 << "layout(location = 0) in highp float in_v_attr;\n" 250 << "layout(location = 0) out highp float in_tc_attr;\n" 251 << "\n" 252 << "void main (void)\n" 253 << "{\n" 254 << " in_tc_attr = in_v_attr;\n" 255 << "}\n"; 256 257 programCollection.glslSources.add("vert") << glu::VertexSource(src.str()); 258 } 259 260 // Tessellation control shader 261 { 262 std::ostringstream src; 263 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 264 << "#extension GL_EXT_tessellation_shader : require\n" 265 << "\n" 266 << "layout(vertices = 1) out;\n" 267 << "\n" 268 << "layout(location = 0) in highp float in_tc_attr[];\n" 269 << "\n" 270 << "void main (void)\n" 271 << "{\n" 272 << " gl_TessLevelInner[0] = in_tc_attr[0];\n" 273 << " gl_TessLevelInner[1] = in_tc_attr[1];\n" 274 << "\n" 275 << " gl_TessLevelOuter[0] = in_tc_attr[2];\n" 276 << " gl_TessLevelOuter[1] = in_tc_attr[3];\n" 277 << " gl_TessLevelOuter[2] = in_tc_attr[4];\n" 278 << " gl_TessLevelOuter[3] = in_tc_attr[5];\n" 279 << "}\n"; 280 281 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str()); 282 } 283 284 const std::string perVertexInterfaceBlock = \ 285 "VertexData {\n" // no in/out qualifier 286 " vec4 in_gs_tessCoord;\n" // w component is used by mirroring test 287 " int in_gs_primitiveID;\n" 288 "}"; // no newline nor semicolon 289 290 // Alternative tess coordinates handling code 291 std::ostringstream tessEvalCoordSrc; 292 if (mirrorCoords) 293 switch (primitiveType) 294 { 295 case TESSPRIMITIVETYPE_TRIANGLES: 296 tessEvalCoordSrc << " float x = gl_TessCoord.x;\n" 297 << " float y = gl_TessCoord.y;\n" 298 << " float z = gl_TessCoord.z;\n" 299 << "\n" 300 << " // Mirror one half of each outer edge onto the other half, except the endpoints (because they belong to two edges)\n" 301 << " ib_out.in_gs_tessCoord = z == 0.0 && x > 0.5 && x != 1.0 ? vec4(1.0-x, 1.0-y, 0.0, 1.0)\n" 302 << " : y == 0.0 && z > 0.5 && z != 1.0 ? vec4(1.0-x, 0.0, 1.0-z, 1.0)\n" 303 << " : x == 0.0 && y > 0.5 && y != 1.0 ? vec4( 0.0, 1.0-y, 1.0-z, 1.0)\n" 304 << " : vec4(x, y, z, 0.0);\n"; 305 break; 306 case TESSPRIMITIVETYPE_QUADS: 307 tessEvalCoordSrc << " float x = gl_TessCoord.x;\n" 308 << " float y = gl_TessCoord.y;\n" 309 << "\n" 310 << " // Mirror one half of each outer edge onto the other half, except the endpoints (because they belong to two edges)\n" 311 << " ib_out.in_gs_tessCoord = (x == 0.0 || x == 1.0) && y > 0.5 && y != 1.0 ? vec4( x, 1.0-y, 0.0, 1.0)\n" 312 << " : (y == 0.0 || y == 1.0) && x > 0.5 && x != 1.0 ? vec4(1.0-x, y, 0.0, 1.0)\n" 313 << " : vec4(x, y, 0.0, 0.0);\n"; 314 break; 315 case TESSPRIMITIVETYPE_ISOLINES: 316 tessEvalCoordSrc << " float x = gl_TessCoord.x;\n" 317 << " float y = gl_TessCoord.y;\n" 318 << "\n" 319 << " // Mirror one half of each outer edge onto the other half\n" 320 << " ib_out.in_gs_tessCoord = (x == 0.0 || x == 1.0) && y > 0.5 ? vec4(x, 1.0-y, 0.0, 1.0)\n" 321 << " : vec4(x, y, 0.0, 0.0);\n"; 322 break; 323 default: 324 DE_ASSERT(false); 325 return; 326 } 327 else 328 tessEvalCoordSrc << " ib_out.in_gs_tessCoord = vec4(gl_TessCoord, 0.0);\n"; 329 330 const std::vector<Winding> windingCases = getWindingCases(windingUsage); 331 const std::vector<bool> usePointModeCases = getUsePointModeCases(pointModeUsage); 332 333 for (std::vector<Winding>::const_iterator windingIter = windingCases.begin(); windingIter != windingCases.end(); ++windingIter) 334 for (std::vector<bool>::const_iterator usePointModeIter = usePointModeCases.begin(); usePointModeIter != usePointModeCases.end(); ++usePointModeIter) 335 { 336 // Tessellation evaluation shader 337 { 338 std::ostringstream src; 339 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 340 << "#extension GL_EXT_tessellation_shader : require\n" 341 << "\n" 342 << "layout(" << getTessPrimitiveTypeShaderName(primitiveType) << ", " 343 << getSpacingModeShaderName(spacingMode) << ", " 344 << getWindingShaderName(*windingIter) 345 << (*usePointModeIter ? ", point_mode" : "") << ") in;\n" 346 << "\n" 347 << "layout(location = 0) out " << perVertexInterfaceBlock << " ib_out;\n" 348 << "\n" 349 << "void main (void)\n" 350 << "{\n" 351 << tessEvalCoordSrc.str() 352 << " ib_out.in_gs_primitiveID = gl_PrimitiveID;\n" 353 << "}\n"; 354 355 programCollection.glslSources.add(getProgramName("tese", *windingIter, *usePointModeIter)) << glu::TessellationEvaluationSource(src.str()); 356 } 357 } // for windingNdx, usePointModeNdx 358 359 // Geometry shader: data is captured here. 360 { 361 for (std::vector<bool>::const_iterator usePointModeIter = usePointModeCases.begin(); usePointModeIter != usePointModeCases.end(); ++usePointModeIter) 362 { 363 const int numVertices = numVerticesPerPrimitive(primitiveType, *usePointModeIter); // Primitives that the tessellated patch comprises of. 364 365 std::ostringstream src; 366 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 367 << "#extension GL_EXT_geometry_shader : require\n" 368 << "\n" 369 << "layout(" << getGeometryShaderInputPrimitiveTypeShaderName(primitiveType, *usePointModeIter) << ") in;\n" 370 << "layout(" << getGeometryShaderOutputPrimitiveTypeShaderName(primitiveType, *usePointModeIter) << ", max_vertices = " << numVertices << ") out;\n" 371 << "\n" 372 << "layout(location = 0) in " << perVertexInterfaceBlock << " ib_in[];\n" 373 << "\n" 374 << "struct PerPrimitive {\n" 375 << " int patchPrimitiveID;\n" 376 << " int primitiveID;\n" 377 << " vec4 tessCoord[3];\n" 378 << "};\n" 379 << "\n" 380 << "layout(set = 0, binding = 0, std430) coherent restrict buffer Output {\n" 381 << " int numPrimitives;\n" 382 << " PerPrimitive primitive[];\n" 383 << "} sb_out;\n" 384 << "\n" 385 << "void main (void)\n" 386 << "{\n" 387 << " int index = atomicAdd(sb_out.numPrimitives, 1);\n" 388 << " sb_out.primitive[index].patchPrimitiveID = ib_in[0].in_gs_primitiveID;\n" 389 << " sb_out.primitive[index].primitiveID = index;\n"; 390 for (int i = 0; i < numVertices; ++i) 391 src << " sb_out.primitive[index].tessCoord[" << i << "] = ib_in[" << i << "].in_gs_tessCoord;\n"; 392 for (int i = 0; i < numVertices; ++i) 393 src << "\n" 394 << " gl_Position = vec4(0.0);\n" 395 << " EmitVertex();\n"; 396 src << "}\n"; 397 398 programCollection.glslSources.add(getProgramName("geom", *usePointModeIter)) << glu::GeometrySource(src.str()); 399 } 400 } 401 } 402 403 //! A description of an outer edge of a triangle, quad or isolines. 404 //! An outer edge can be described by the index of a u/v/w coordinate 405 //! and the coordinate's value along that edge. 406 struct OuterEdgeDescription 407 { 408 int constantCoordinateIndex; 409 float constantCoordinateValueChoices[2]; 410 int numConstantCoordinateValueChoices; 411 412 OuterEdgeDescription (const int i, const float c0) : constantCoordinateIndex(i), numConstantCoordinateValueChoices(1) { constantCoordinateValueChoices[0] = c0; } 413 OuterEdgeDescription (const int i, const float c0, const float c1) : constantCoordinateIndex(i), numConstantCoordinateValueChoices(2) { constantCoordinateValueChoices[0] = c0; constantCoordinateValueChoices[1] = c1; } 414 415 std::string description (void) const 416 { 417 static const char* const coordinateNames[] = { "u", "v", "w" }; 418 std::string result; 419 for (int i = 0; i < numConstantCoordinateValueChoices; ++i) 420 result += std::string() + (i > 0 ? " or " : "") + coordinateNames[constantCoordinateIndex] + "=" + de::toString(constantCoordinateValueChoices[i]); 421 return result; 422 } 423 424 bool contains (const tcu::Vec3& v) const 425 { 426 for (int i = 0; i < numConstantCoordinateValueChoices; ++i) 427 if (v[constantCoordinateIndex] == constantCoordinateValueChoices[i]) 428 return true; 429 return false; 430 } 431 }; 432 433 std::vector<OuterEdgeDescription> outerEdgeDescriptions (const TessPrimitiveType primType) 434 { 435 static const OuterEdgeDescription triangleOuterEdgeDescriptions[3] = 436 { 437 OuterEdgeDescription(0, 0.0f), 438 OuterEdgeDescription(1, 0.0f), 439 OuterEdgeDescription(2, 0.0f) 440 }; 441 442 static const OuterEdgeDescription quadOuterEdgeDescriptions[4] = 443 { 444 OuterEdgeDescription(0, 0.0f), 445 OuterEdgeDescription(1, 0.0f), 446 OuterEdgeDescription(0, 1.0f), 447 OuterEdgeDescription(1, 1.0f) 448 }; 449 450 static const OuterEdgeDescription isolinesOuterEdgeDescriptions[1] = 451 { 452 OuterEdgeDescription(0, 0.0f, 1.0f), 453 }; 454 455 switch (primType) 456 { 457 case TESSPRIMITIVETYPE_TRIANGLES: return arrayToVector(triangleOuterEdgeDescriptions); 458 case TESSPRIMITIVETYPE_QUADS: return arrayToVector(quadOuterEdgeDescriptions); 459 case TESSPRIMITIVETYPE_ISOLINES: return arrayToVector(isolinesOuterEdgeDescriptions); 460 461 default: 462 DE_ASSERT(false); 463 return std::vector<OuterEdgeDescription>(); 464 } 465 } 466 467 namespace InvariantOuterEdge 468 { 469 470 struct CaseDefinition 471 { 472 TessPrimitiveType primitiveType; 473 SpacingMode spacingMode; 474 Winding winding; 475 bool usePointMode; 476 }; 477 478 typedef std::set<tcu::Vec3, VecLexLessThan<3> > Vec3Set; 479 480 std::vector<float> generateRandomPatchTessLevels (const int numPatches, const int constantOuterLevelIndex, const float constantOuterLevel, de::Random& rnd) 481 { 482 std::vector<float> tessLevels(numPatches*NUM_TESS_LEVELS); 483 484 for (int patchNdx = 0; patchNdx < numPatches; ++patchNdx) 485 { 486 float* const inner = &tessLevels[patchNdx*NUM_TESS_LEVELS + 0]; 487 float* const outer = &tessLevels[patchNdx*NUM_TESS_LEVELS + 2]; 488 489 for (int j = 0; j < 2; ++j) 490 inner[j] = rnd.getFloat(1.0f, 62.0f); 491 for (int j = 0; j < 4; ++j) 492 outer[j] = j == constantOuterLevelIndex ? constantOuterLevel : rnd.getFloat(1.0f, 62.0f); 493 } 494 495 return tessLevels; 496 } 497 498 std::vector<float> generatePatchTessLevels (const int numPatches, const int constantOuterLevelIndex, const float constantOuterLevel) 499 { 500 de::Random rnd(123); 501 return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd); 502 } 503 504 int multiplePatchReferencePrimitiveCount (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float* levels, int numPatches) 505 { 506 int result = 0; 507 for (int patchNdx = 0; patchNdx < numPatches; ++patchNdx) 508 result += referencePrimitiveCount(primitiveType, spacingMode, usePointMode, &levels[NUM_TESS_LEVELS*patchNdx + 0], &levels[NUM_TESS_LEVELS*patchNdx + 2]); 509 return result; 510 } 511 512 template<std::size_t N> 513 int computeMaxPrimitiveCount (const int numPatchesToDraw, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float (&singleOuterEdgeLevels)[N]) 514 { 515 const int outerEdgeIndex = 0; // outer-edge index doesn't affect vertex count 516 const std::vector<float> patchTessLevels = generatePatchTessLevels(numPatchesToDraw, outerEdgeIndex, arrayMax(singleOuterEdgeLevels)); 517 return multiplePatchReferencePrimitiveCount(primitiveType, spacingMode, usePointMode, &patchTessLevels[0], numPatchesToDraw); 518 } 519 520 void logOuterTessellationLevel (tcu::TestLog& log, const float tessLevel, const OuterEdgeDescription& edgeDesc) 521 { 522 log << tcu::TestLog::Message 523 << "Testing with outer tessellation level " << tessLevel << " for the " << edgeDesc.description() << " edge, and with various levels for other edges, and with all programs" 524 << tcu::TestLog::EndMessage; 525 } 526 527 void logPrimitiveCountError (tcu::TestLog& log, const int numPatchesToDraw, int numPrimitives, const int refNumPrimitives, const std::vector<float>& patchTessLevels) 528 { 529 log << tcu::TestLog::Message 530 << "Failure: the number of generated primitives is " << numPrimitives << ", expected at least " << refNumPrimitives 531 << tcu::TestLog::EndMessage; 532 533 if (numPatchesToDraw == 1) 534 log << tcu::TestLog::Message 535 << "Note: rendered one patch; tessellation levels are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n" 536 << containerStr(patchTessLevels, NUM_TESS_LEVELS) 537 << tcu::TestLog::EndMessage; 538 else 539 log << tcu::TestLog::Message 540 << "Note: rendered " << numPatchesToDraw << " patches in one draw call; " 541 << "tessellation levels for each patch are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n" 542 << containerStr(patchTessLevels, NUM_TESS_LEVELS) 543 << tcu::TestLog::EndMessage; 544 } 545 546 class BaseTestInstance : public TestInstance 547 { 548 public: 549 struct DrawResult 550 { 551 bool success; 552 int refNumPrimitives; 553 int numPrimitiveVertices; 554 deInt32 numPrimitives; 555 PerPrimitiveVec primitives; 556 }; 557 558 BaseTestInstance (Context& context, const CaseDefinition caseDef, const int numPatchesToDraw); 559 DrawResult draw (const deUint32 vertexCount, const std::vector<float>& patchTessLevels, const Winding winding, const bool usePointMode); 560 void uploadVertexAttributes (const std::vector<float>& vertexData); 561 562 protected: 563 static const float m_singleOuterEdgeLevels[]; 564 565 const CaseDefinition m_caseDef; 566 const int m_numPatchesToDraw; 567 const VkFormat m_vertexFormat; 568 const deUint32 m_vertexStride; 569 const std::vector<OuterEdgeDescription> m_edgeDescriptions; 570 const int m_maxNumPrimitivesInDrawCall; 571 const VkDeviceSize m_vertexDataSizeBytes; 572 const Buffer m_vertexBuffer; 573 const int m_resultBufferPrimitiveDataOffset; 574 const VkDeviceSize m_resultBufferSizeBytes; 575 const Buffer m_resultBuffer; 576 Unique<VkDescriptorSetLayout> m_descriptorSetLayout; 577 Unique<VkDescriptorPool> m_descriptorPool; 578 Unique<VkDescriptorSet> m_descriptorSet; 579 Unique<VkRenderPass> m_renderPass; 580 Unique<VkFramebuffer> m_framebuffer; 581 Unique<VkPipelineLayout> m_pipelineLayout; 582 Unique<VkCommandPool> m_cmdPool; 583 Unique<VkCommandBuffer> m_cmdBuffer; 584 }; 585 586 const float BaseTestInstance::m_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 }; 587 588 BaseTestInstance::BaseTestInstance (Context& context, const CaseDefinition caseDef, const int numPatchesToDraw) 589 : TestInstance (context) 590 , m_caseDef (caseDef) 591 , m_numPatchesToDraw (numPatchesToDraw) 592 , m_vertexFormat (VK_FORMAT_R32_SFLOAT) 593 , m_vertexStride (tcu::getPixelSize(mapVkFormat(m_vertexFormat))) 594 , m_edgeDescriptions (outerEdgeDescriptions(m_caseDef.primitiveType)) 595 , m_maxNumPrimitivesInDrawCall (NUM_EXTRA_TESS_GEOM_INVOCATIONS * computeMaxPrimitiveCount(m_numPatchesToDraw, caseDef.primitiveType, caseDef.spacingMode, caseDef.usePointMode, m_singleOuterEdgeLevels)) 596 , m_vertexDataSizeBytes (NUM_TESS_LEVELS * m_numPatchesToDraw * m_vertexStride) 597 , m_vertexBuffer (m_context.getDeviceInterface(), m_context.getDevice(), m_context.getDefaultAllocator(), 598 makeBufferCreateInfo(m_vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible) 599 , m_resultBufferPrimitiveDataOffset ((int)sizeof(deInt32) * 4) 600 , m_resultBufferSizeBytes (m_resultBufferPrimitiveDataOffset + m_maxNumPrimitivesInDrawCall * sizeof(PerPrimitive)) 601 , m_resultBuffer (m_context.getDeviceInterface(), m_context.getDevice(), m_context.getDefaultAllocator(), 602 makeBufferCreateInfo(m_resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible) 603 , m_descriptorSetLayout (DescriptorSetLayoutBuilder() 604 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_GEOMETRY_BIT) 605 .build(m_context.getDeviceInterface(), m_context.getDevice())) 606 , m_descriptorPool (DescriptorPoolBuilder() 607 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) 608 .build(m_context.getDeviceInterface(), m_context.getDevice(), VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u)) 609 , m_descriptorSet (makeDescriptorSet(m_context.getDeviceInterface(), m_context.getDevice(), *m_descriptorPool, *m_descriptorSetLayout)) 610 , m_renderPass (makeRenderPassWithoutAttachments (m_context.getDeviceInterface(), m_context.getDevice())) 611 , m_framebuffer (makeFramebufferWithoutAttachments(m_context.getDeviceInterface(), m_context.getDevice(), *m_renderPass)) 612 , m_pipelineLayout (makePipelineLayout (m_context.getDeviceInterface(), m_context.getDevice(), *m_descriptorSetLayout)) 613 , m_cmdPool (makeCommandPool (m_context.getDeviceInterface(), m_context.getDevice(), m_context.getUniversalQueueFamilyIndex())) 614 , m_cmdBuffer (allocateCommandBuffer (m_context.getDeviceInterface(), m_context.getDevice(), *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY)) 615 { 616 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), 617 FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS); 618 619 const VkDescriptorBufferInfo resultBufferInfo = makeDescriptorBufferInfo(m_resultBuffer.get(), 0ull, m_resultBufferSizeBytes); 620 621 DescriptorSetUpdateBuilder() 622 .writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo) 623 .update(m_context.getDeviceInterface(), m_context.getDevice()); 624 } 625 626 //! patchTessLevels are tessellation levels for all drawn patches. 627 BaseTestInstance::DrawResult BaseTestInstance::draw (const deUint32 vertexCount, const std::vector<float>& patchTessLevels, const Winding winding, const bool usePointMode) 628 { 629 const DeviceInterface& vk = m_context.getDeviceInterface(); 630 const VkDevice device = m_context.getDevice(); 631 const VkQueue queue = m_context.getUniversalQueue(); 632 633 const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder() 634 .setPatchControlPoints (NUM_TESS_LEVELS) 635 .setVertexInputSingleAttribute(m_vertexFormat, m_vertexStride) 636 .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL) 637 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL) 638 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get(getProgramName("tese", winding, usePointMode)), DE_NULL) 639 .setShader (vk, device, VK_SHADER_STAGE_GEOMETRY_BIT, m_context.getBinaryCollection().get(getProgramName("geom", usePointMode)), DE_NULL) 640 .build (vk, device, *m_pipelineLayout, *m_renderPass)); 641 642 { 643 const Allocation& alloc = m_resultBuffer.getAllocation(); 644 deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(m_resultBufferSizeBytes)); 645 flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), m_resultBufferSizeBytes); 646 } 647 648 beginCommandBuffer(vk, *m_cmdBuffer); 649 beginRenderPassWithRasterizationDisabled(vk, *m_cmdBuffer, *m_renderPass, *m_framebuffer); 650 651 vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); 652 vk.cmdBindDescriptorSets(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelineLayout, 0u, 1u, &m_descriptorSet.get(), 0u, DE_NULL); 653 { 654 const VkDeviceSize vertexBufferOffset = 0ull; 655 vk.cmdBindVertexBuffers(*m_cmdBuffer, 0u, 1u, &m_vertexBuffer.get(), &vertexBufferOffset); 656 } 657 658 vk.cmdDraw(*m_cmdBuffer, vertexCount, 1u, 0u, 0u); 659 endRenderPass(vk, *m_cmdBuffer); 660 661 { 662 const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier( 663 VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *m_resultBuffer, 0ull, m_resultBufferSizeBytes); 664 665 vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 666 0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL); 667 } 668 669 endCommandBuffer(vk, *m_cmdBuffer); 670 submitCommandsAndWait(vk, device, queue, *m_cmdBuffer); 671 672 // Read back and check results 673 674 const Allocation& resultAlloc = m_resultBuffer.getAllocation(); 675 invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), m_resultBufferSizeBytes); 676 677 DrawResult result; 678 result.success = true; 679 result.refNumPrimitives = multiplePatchReferencePrimitiveCount(m_caseDef.primitiveType, m_caseDef.spacingMode, usePointMode, &patchTessLevels[0], m_numPatchesToDraw); 680 result.numPrimitiveVertices = numVerticesPerPrimitive(m_caseDef.primitiveType, usePointMode); 681 result.numPrimitives = *static_cast<deInt32*>(resultAlloc.getHostPtr()); 682 result.primitives = sorted(readInterleavedData<PerPrimitive>(result.numPrimitives, resultAlloc.getHostPtr(), m_resultBufferPrimitiveDataOffset, sizeof(PerPrimitive)), 683 byPatchPrimitiveID); 684 685 // If this fails then we didn't read all vertices from shader and test must be changed to allow more. 686 DE_ASSERT(result.numPrimitives <= m_maxNumPrimitivesInDrawCall); 687 688 tcu::TestLog& log = m_context.getTestContext().getLog(); 689 if (result.numPrimitives < result.refNumPrimitives) 690 { 691 logPrimitiveCountError(log, m_numPatchesToDraw, result.numPrimitives, result.refNumPrimitives, patchTessLevels); 692 result.success = false; 693 } 694 return result; 695 } 696 697 void BaseTestInstance::uploadVertexAttributes (const std::vector<float>& vertexData) 698 { 699 const DeviceInterface& vk = m_context.getDeviceInterface(); 700 const VkDevice device = m_context.getDevice(); 701 702 const Allocation& alloc = m_vertexBuffer.getAllocation(); 703 deMemcpy(alloc.getHostPtr(), &vertexData[0], sizeInBytes(vertexData)); 704 flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeInBytes(vertexData)); 705 } 706 707 /*--------------------------------------------------------------------*//*! 708 * \brief Test invariance rule #2 709 * 710 * Test that the set of vertices along an outer edge of a quad or triangle 711 * only depends on that edge's tessellation level, and spacing. 712 * 713 * For each (outer) edge in the quad or triangle, draw multiple patches 714 * with identical tessellation levels for that outer edge but with 715 * different values for the other outer edges; compare, among the 716 * primitives, the vertices generated for that outer edge. Repeat with 717 * different programs, using different winding etc. settings. Compare 718 * the edge's vertices between different programs. 719 *//*--------------------------------------------------------------------*/ 720 class OuterEdgeDivisionTestInstance : public BaseTestInstance 721 { 722 public: 723 OuterEdgeDivisionTestInstance (Context& context, const CaseDefinition caseDef) : BaseTestInstance (context, caseDef, 10) {} 724 tcu::TestStatus iterate (void); 725 }; 726 727 tcu::TestStatus OuterEdgeDivisionTestInstance::iterate (void) 728 { 729 for (int outerEdgeIndex = 0; outerEdgeIndex < static_cast<int>(m_edgeDescriptions.size()); ++outerEdgeIndex) 730 for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(m_singleOuterEdgeLevels); ++outerEdgeLevelCaseNdx) 731 { 732 const OuterEdgeDescription& edgeDesc = m_edgeDescriptions[outerEdgeIndex]; 733 const std::vector<float> patchTessLevels = generatePatchTessLevels(m_numPatchesToDraw, outerEdgeIndex, m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx]); 734 735 Vec3Set firstOuterEdgeVertices; // Vertices of the outer edge of the first patch of the first program's draw call; used for comparison with other patches. 736 737 uploadVertexAttributes(patchTessLevels); 738 logOuterTessellationLevel(m_context.getTestContext().getLog(), m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx], edgeDesc); 739 740 for (int windingNdx = 0; windingNdx < WINDING_LAST; ++windingNdx) 741 for (int usePointModeNdx = 0; usePointModeNdx <= 1; ++usePointModeNdx) 742 { 743 const Winding winding = static_cast<Winding>(windingNdx); 744 const bool usePointMode = (usePointModeNdx != 0); 745 const bool isFirstProgram = (windingNdx == 0 && usePointModeNdx == 0); 746 747 const DrawResult result = draw(static_cast<deUint32>(patchTessLevels.size()), patchTessLevels, winding, usePointMode); 748 749 if (!result.success) 750 return tcu::TestStatus::fail("Invalid set of vertices"); 751 752 // Check the vertices of each patch. 753 754 int primitiveNdx = 0; 755 for (int patchNdx = 0; patchNdx < m_numPatchesToDraw; ++patchNdx) 756 { 757 const float* const innerLevels = &patchTessLevels[NUM_TESS_LEVELS*patchNdx + 0]; 758 const float* const outerLevels = &patchTessLevels[NUM_TESS_LEVELS*patchNdx + 2]; 759 760 Vec3Set outerEdgeVertices; 761 762 // We're interested in just the vertices on the current outer edge. 763 for (; primitiveNdx < result.numPrimitives && result.primitives[primitiveNdx].patchPrimitiveID == patchNdx; ++primitiveNdx) 764 for (int i = 0; i < result.numPrimitiveVertices; ++i) 765 { 766 const tcu::Vec3& coord = result.primitives[primitiveNdx].tessCoord[i].swizzle(0, 1, 2); 767 if (edgeDesc.contains(coord)) 768 outerEdgeVertices.insert(coord); 769 } 770 771 // Compare the vertices to those of the first patch (unless this is the first patch). 772 773 if (isFirstProgram && patchNdx == 0) 774 firstOuterEdgeVertices = outerEdgeVertices; 775 else if (firstOuterEdgeVertices != outerEdgeVertices) 776 { 777 tcu::TestLog& log = m_context.getTestContext().getLog(); 778 779 log << tcu::TestLog::Message 780 << "Failure: vertices generated for the edge differ between the following cases:\n" 781 << " - case A: " << getProgramDescription((Winding)0, (bool)0) << ", tessellation levels: " 782 << getTessellationLevelsString(&patchTessLevels[0], &patchTessLevels[2]) << "\n" 783 << " - case B: " << getProgramDescription(winding, usePointMode) << ", tessellation levels: " 784 << getTessellationLevelsString(innerLevels, outerLevels) 785 << tcu::TestLog::EndMessage; 786 787 log << tcu::TestLog::Message 788 << "Note: resulting vertices for the edge for the cases were:\n" 789 << " - case A: " << containerStr(firstOuterEdgeVertices, 5, 14) << "\n" 790 << " - case B: " << containerStr(outerEdgeVertices, 5, 14) 791 << tcu::TestLog::EndMessage; 792 793 return tcu::TestStatus::fail("Invalid set of vertices"); 794 } 795 } 796 DE_ASSERT(primitiveNdx == result.numPrimitives); 797 } // for windingNdx, usePointModeNdx 798 } // for outerEdgeIndex, outerEdgeLevelCaseNdx 799 800 return tcu::TestStatus::pass("OK"); 801 } 802 803 /*--------------------------------------------------------------------*//*! 804 * \brief Test invariance rule #4 805 * 806 * Test that the vertices on an outer edge don't depend on which of the 807 * edges it is, other than with respect to component order. 808 *//*--------------------------------------------------------------------*/ 809 class OuterEdgeIndexIndependenceTestInstance : public BaseTestInstance 810 { 811 public: 812 OuterEdgeIndexIndependenceTestInstance (Context& context, const CaseDefinition caseDef) : BaseTestInstance (context, caseDef, 1) {} 813 tcu::TestStatus iterate (void); 814 }; 815 816 tcu::TestStatus OuterEdgeIndexIndependenceTestInstance::iterate (void) 817 { 818 for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(m_singleOuterEdgeLevels); ++outerEdgeLevelCaseNdx) 819 { 820 Vec3Set firstEdgeVertices; 821 822 for (int outerEdgeIndex = 0; outerEdgeIndex < static_cast<int>(m_edgeDescriptions.size()); ++outerEdgeIndex) 823 { 824 const OuterEdgeDescription& edgeDesc = m_edgeDescriptions[outerEdgeIndex]; 825 const std::vector<float> patchTessLevels = generatePatchTessLevels(m_numPatchesToDraw, outerEdgeIndex, m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx]); 826 827 uploadVertexAttributes(patchTessLevels); 828 logOuterTessellationLevel(m_context.getTestContext().getLog(), m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx], edgeDesc); 829 const DrawResult result = draw(static_cast<deUint32>(patchTessLevels.size()), patchTessLevels, m_caseDef.winding, m_caseDef.usePointMode); 830 831 // Verify case result 832 833 if (!result.success) 834 return tcu::TestStatus::fail("Invalid set of vertices"); 835 836 Vec3Set currentEdgeVertices; 837 838 // Get the vertices on the current outer edge. 839 for (int primitiveNdx = 0; primitiveNdx < result.numPrimitives; ++primitiveNdx) 840 for (int i = 0; i < result.numPrimitiveVertices; ++i) 841 { 842 const tcu::Vec3& coord = result.primitives[primitiveNdx].tessCoord[i].swizzle(0, 1, 2); 843 if (edgeDesc.contains(coord)) 844 { 845 // Swizzle components to match the order of the first edge. 846 if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 847 currentEdgeVertices.insert(outerEdgeIndex == 0 ? coord : 848 outerEdgeIndex == 1 ? coord.swizzle(1, 0, 2) : 849 outerEdgeIndex == 2 ? coord.swizzle(2, 1, 0) : tcu::Vec3(-1.0f)); 850 else if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS) 851 currentEdgeVertices.insert(tcu::Vec3(outerEdgeIndex == 0 ? coord.y() : 852 outerEdgeIndex == 1 ? coord.x() : 853 outerEdgeIndex == 2 ? coord.y() : 854 outerEdgeIndex == 3 ? coord.x() : -1.0f, 855 0.0f, 0.0f)); 856 else 857 DE_ASSERT(false); 858 } 859 } 860 861 if (outerEdgeIndex == 0) 862 firstEdgeVertices = currentEdgeVertices; 863 else 864 { 865 // Compare vertices of this edge to those of the first edge. 866 if (currentEdgeVertices != firstEdgeVertices) 867 { 868 const char* const swizzleDesc = 869 m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? (outerEdgeIndex == 1 ? "(y, x, z)" : 870 outerEdgeIndex == 2 ? "(z, y, x)" : DE_NULL) : 871 m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS ? (outerEdgeIndex == 1 ? "(x, 0)" : 872 outerEdgeIndex == 2 ? "(y, 0)" : 873 outerEdgeIndex == 3 ? "(x, 0)" : DE_NULL) 874 : DE_NULL; 875 876 tcu::TestLog& log = m_context.getTestContext().getLog(); 877 log << tcu::TestLog::Message 878 << "Failure: the set of vertices on the " << edgeDesc.description() << " edge" 879 << " doesn't match the set of vertices on the " << m_edgeDescriptions[0].description() << " edge" 880 << tcu::TestLog::EndMessage; 881 882 log << tcu::TestLog::Message 883 << "Note: set of vertices on " << edgeDesc.description() << " edge, components swizzled like " << swizzleDesc 884 << " to match component order on first edge:\n" << containerStr(currentEdgeVertices, 5) 885 << "\non " << m_edgeDescriptions[0].description() << " edge:\n" << containerStr(firstEdgeVertices, 5) 886 << tcu::TestLog::EndMessage; 887 888 return tcu::TestStatus::fail("Invalid set of vertices"); 889 } 890 } 891 } 892 } 893 return tcu::TestStatus::pass("OK"); 894 } 895 896 /*--------------------------------------------------------------------*//*! 897 * \brief Test invariance rule #3 898 * 899 * Test that the vertices along an outer edge are placed symmetrically. 900 * 901 * Draw multiple patches with different tessellation levels and different 902 * point_mode, winding etc. Before outputting tesscoords from shader, mirror 903 * the vertices in the TES such that every vertex on an outer edge - 904 * except the possible middle vertex - should be duplicated in the output. 905 * Check that appropriate duplicates exist. 906 *//*--------------------------------------------------------------------*/ 907 class SymmetricOuterEdgeTestInstance : public BaseTestInstance 908 { 909 public: 910 SymmetricOuterEdgeTestInstance (Context& context, const CaseDefinition caseDef) : BaseTestInstance (context, caseDef, 1) {} 911 tcu::TestStatus iterate (void); 912 }; 913 914 tcu::TestStatus SymmetricOuterEdgeTestInstance::iterate (void) 915 { 916 for (int outerEdgeIndex = 0; outerEdgeIndex < static_cast<int>(m_edgeDescriptions.size()); ++outerEdgeIndex) 917 for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(m_singleOuterEdgeLevels); ++outerEdgeLevelCaseNdx) 918 { 919 const OuterEdgeDescription& edgeDesc = m_edgeDescriptions[outerEdgeIndex]; 920 const std::vector<float> patchTessLevels = generatePatchTessLevels(m_numPatchesToDraw, outerEdgeIndex, m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx]); 921 922 uploadVertexAttributes(patchTessLevels); 923 logOuterTessellationLevel(m_context.getTestContext().getLog(), m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx], edgeDesc); 924 const DrawResult result = draw(static_cast<deUint32>(patchTessLevels.size()), patchTessLevels, m_caseDef.winding, m_caseDef.usePointMode); 925 926 // Verify case result 927 928 if (!result.success) 929 return tcu::TestStatus::fail("Invalid set of vertices"); 930 931 Vec3Set nonMirroredEdgeVertices; 932 Vec3Set mirroredEdgeVertices; 933 934 // Get the vertices on the current outer edge. 935 for (int primitiveNdx = 0; primitiveNdx < result.numPrimitives; ++primitiveNdx) 936 for (int i = 0; i < result.numPrimitiveVertices; ++i) 937 { 938 const tcu::Vec3& coord = result.primitives[primitiveNdx].tessCoord[i].swizzle(0, 1, 2); 939 if (edgeDesc.contains(coord)) 940 { 941 // Ignore the middle vertex of the outer edge, as it's exactly at the mirroring point; 942 // for isolines, also ignore (0, 0) and (1, 0) because there's no mirrored counterpart for them. 943 if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES && 944 coord == tcu::select(tcu::Vec3(0.0f), tcu::Vec3(0.5f), singleTrueMask<3>(edgeDesc.constantCoordinateIndex))) 945 continue; 946 if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS && 947 coord.swizzle(0,1) == tcu::select(tcu::Vec2(edgeDesc.constantCoordinateValueChoices[0]), tcu::Vec2(0.5f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex))) 948 continue; 949 if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_ISOLINES && 950 (coord == tcu::Vec3(0.0f, 0.5f, 0.0f) || coord == tcu::Vec3(1.0f, 0.5f, 0.0f) || coord == tcu::Vec3(0.0f, 0.0f, 0.0f) || coord == tcu::Vec3(1.0f, 0.0f, 0.0f))) 951 continue; 952 953 const bool isMirrored = result.primitives[primitiveNdx].tessCoord[i].w() > 0.5f; 954 if (isMirrored) 955 mirroredEdgeVertices.insert(coord); 956 else 957 nonMirroredEdgeVertices.insert(coord); 958 } 959 } 960 961 if (m_caseDef.primitiveType != TESSPRIMITIVETYPE_ISOLINES) 962 { 963 // Check that both endpoints are present. Note that endpoints aren't mirrored by the shader, since they belong to more than one edge. 964 965 tcu::Vec3 endpointA; 966 tcu::Vec3 endpointB; 967 968 if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 969 { 970 endpointA = tcu::select(tcu::Vec3(1.0f), tcu::Vec3(0.0f), singleTrueMask<3>((edgeDesc.constantCoordinateIndex + 1) % 3)); 971 endpointB = tcu::select(tcu::Vec3(1.0f), tcu::Vec3(0.0f), singleTrueMask<3>((edgeDesc.constantCoordinateIndex + 2) % 3)); 972 } 973 else if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS) 974 { 975 endpointA.xy() = tcu::select(tcu::Vec2(edgeDesc.constantCoordinateValueChoices[0]), tcu::Vec2(0.0f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex)); 976 endpointB.xy() = tcu::select(tcu::Vec2(edgeDesc.constantCoordinateValueChoices[0]), tcu::Vec2(1.0f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex)); 977 } 978 else 979 DE_ASSERT(false); 980 981 if (!contains(nonMirroredEdgeVertices, endpointA) || 982 !contains(nonMirroredEdgeVertices, endpointB)) 983 { 984 m_context.getTestContext().getLog() 985 << tcu::TestLog::Message << "Failure: edge doesn't contain both endpoints, " << endpointA << " and " << endpointB << tcu::TestLog::EndMessage 986 << tcu::TestLog::Message << "Note: non-mirrored vertices:\n" << containerStr(nonMirroredEdgeVertices, 5) 987 << "\nmirrored vertices:\n" << containerStr(mirroredEdgeVertices, 5) << tcu::TestLog::EndMessage; 988 989 return tcu::TestStatus::fail("Invalid set of vertices"); 990 } 991 nonMirroredEdgeVertices.erase(endpointA); 992 nonMirroredEdgeVertices.erase(endpointB); 993 } 994 995 if (nonMirroredEdgeVertices != mirroredEdgeVertices) 996 { 997 m_context.getTestContext().getLog() 998 << tcu::TestLog::Message << "Failure: the set of mirrored edges isn't equal to the set of non-mirrored edges (ignoring endpoints and possible middle)" << tcu::TestLog::EndMessage 999 << tcu::TestLog::Message << "Note: non-mirrored vertices:\n" << containerStr(nonMirroredEdgeVertices, 5) 1000 << "\nmirrored vertices:\n" << containerStr(mirroredEdgeVertices, 5) << tcu::TestLog::EndMessage; 1001 1002 return tcu::TestStatus::fail("Invalid set of vertices"); 1003 } 1004 } 1005 return tcu::TestStatus::pass("OK"); 1006 } 1007 1008 class OuterEdgeDivisionTest : public TestCase 1009 { 1010 public: 1011 OuterEdgeDivisionTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef) 1012 : TestCase (testCtx, name, description) 1013 , m_caseDef (caseDef) 1014 { 1015 } 1016 1017 void initPrograms (vk::SourceCollections& programCollection) const 1018 { 1019 addDefaultPrograms(programCollection, m_caseDef.primitiveType, m_caseDef.spacingMode, WINDING_USAGE_VARY, POINT_MODE_USAGE_VARY); 1020 } 1021 1022 TestInstance* createInstance (Context& context) const 1023 { 1024 return new OuterEdgeDivisionTestInstance(context, m_caseDef); 1025 }; 1026 1027 private: 1028 const CaseDefinition m_caseDef; 1029 }; 1030 1031 class OuterEdgeIndexIndependenceTest : public TestCase 1032 { 1033 public: 1034 OuterEdgeIndexIndependenceTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef) 1035 : TestCase (testCtx, name, description) 1036 , m_caseDef (caseDef) 1037 { 1038 DE_ASSERT(m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS); 1039 } 1040 1041 void initPrograms (vk::SourceCollections& programCollection) const 1042 { 1043 addDefaultPrograms(programCollection, m_caseDef.primitiveType, m_caseDef.spacingMode, getWindingUsage(m_caseDef.winding), getPointModeUsage(m_caseDef.usePointMode)); 1044 } 1045 1046 TestInstance* createInstance (Context& context) const 1047 { 1048 return new OuterEdgeIndexIndependenceTestInstance(context, m_caseDef); 1049 }; 1050 1051 private: 1052 const CaseDefinition m_caseDef; 1053 }; 1054 1055 class SymmetricOuterEdgeTest : public TestCase 1056 { 1057 public: 1058 SymmetricOuterEdgeTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef) 1059 : TestCase (testCtx, name, description) 1060 , m_caseDef (caseDef) 1061 { 1062 } 1063 1064 void initPrograms (vk::SourceCollections& programCollection) const 1065 { 1066 const bool mirrorCoords = true; 1067 addDefaultPrograms(programCollection, m_caseDef.primitiveType, m_caseDef.spacingMode, getWindingUsage(m_caseDef.winding), getPointModeUsage(m_caseDef.usePointMode), mirrorCoords); 1068 } 1069 1070 TestInstance* createInstance (Context& context) const 1071 { 1072 return new SymmetricOuterEdgeTestInstance(context, m_caseDef); 1073 }; 1074 1075 private: 1076 const CaseDefinition m_caseDef; 1077 }; 1078 1079 tcu::TestCase* makeOuterEdgeDivisionTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode) 1080 { 1081 const CaseDefinition caseDef = { primitiveType, spacingMode, WINDING_LAST, false }; // winding is ignored by this test 1082 return new OuterEdgeDivisionTest(testCtx, name, description, caseDef); 1083 } 1084 1085 tcu::TestCase* makeOuterEdgeIndexIndependenceTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode) 1086 { 1087 const CaseDefinition caseDef = { primitiveType, spacingMode, winding, usePointMode }; 1088 return new OuterEdgeIndexIndependenceTest(testCtx, name, description, caseDef); 1089 } 1090 1091 tcu::TestCase* makeSymmetricOuterEdgeTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode) 1092 { 1093 const CaseDefinition caseDef = { primitiveType, spacingMode, winding, usePointMode }; 1094 return new SymmetricOuterEdgeTest(testCtx, name, description, caseDef); 1095 } 1096 1097 } // InvariantOuterEdge ns 1098 1099 namespace PrimitiveSetInvariance 1100 { 1101 1102 enum CaseType 1103 { 1104 CASETYPE_INVARIANT_PRIMITIVE_SET, 1105 CASETYPE_INVARIANT_TRIANGLE_SET, 1106 CASETYPE_INVARIANT_OUTER_TRIANGLE_SET, 1107 CASETYPE_INVARIANT_INNER_TRIANGLE_SET, 1108 }; 1109 1110 struct CaseDefinition 1111 { 1112 CaseType caseType; 1113 TessPrimitiveType primitiveType; 1114 SpacingMode spacingMode; 1115 WindingUsage windingUsage; 1116 bool usePointMode; 1117 }; 1118 1119 struct LevelCase 1120 { 1121 std::vector<TessLevels> levels; 1122 int mem; //!< Subclass-defined arbitrary piece of data, for type of the levelcase, if needed. 1123 1124 LevelCase (const TessLevels& lev) : levels(std::vector<TessLevels>(1, lev)), mem(0) {} 1125 LevelCase (void) : mem(0) {} 1126 }; 1127 1128 typedef tcu::Vector<tcu::Vec3, 3> Triangle; 1129 1130 inline Triangle makeTriangle (const PerPrimitive& primitive) 1131 { 1132 return Triangle(primitive.tessCoord[0].swizzle(0, 1, 2), 1133 primitive.tessCoord[1].swizzle(0, 1, 2), 1134 primitive.tessCoord[2].swizzle(0, 1, 2)); 1135 } 1136 1137 //! Compare triangle sets, ignoring triangle order and vertex order within triangle, and possibly exclude some triangles too. 1138 template <typename IsTriangleRelevantT> 1139 bool compareTriangleSets (const PerPrimitiveVec& primitivesA, 1140 const PerPrimitiveVec& primitivesB, 1141 tcu::TestLog& log, 1142 const IsTriangleRelevantT& isTriangleRelevant, 1143 const char* ignoredTriangleDescription = DE_NULL) 1144 { 1145 typedef LexCompare<Triangle, 3, VecLexLessThan<3> > TriangleLexLessThan; 1146 typedef std::set<Triangle, TriangleLexLessThan> TriangleSet; 1147 1148 const int numTrianglesA = static_cast<int>(primitivesA.size()); 1149 const int numTrianglesB = static_cast<int>(primitivesB.size()); 1150 TriangleSet trianglesA; 1151 TriangleSet trianglesB; 1152 1153 for (int aOrB = 0; aOrB < 2; ++aOrB) 1154 { 1155 const PerPrimitiveVec& primitives = aOrB == 0 ? primitivesA : primitivesB; 1156 const int numTriangles = aOrB == 0 ? numTrianglesA : numTrianglesB; 1157 TriangleSet& triangles = aOrB == 0 ? trianglesA : trianglesB; 1158 1159 for (int triNdx = 0; triNdx < numTriangles; ++triNdx) 1160 { 1161 Triangle triangle = makeTriangle(primitives[triNdx]); 1162 1163 if (isTriangleRelevant(triangle.getPtr())) 1164 { 1165 std::sort(triangle.getPtr(), triangle.getPtr()+3, VecLexLessThan<3>()); 1166 triangles.insert(triangle); 1167 } 1168 } 1169 } 1170 { 1171 TriangleSet::const_iterator aIt = trianglesA.begin(); 1172 TriangleSet::const_iterator bIt = trianglesB.begin(); 1173 1174 while (aIt != trianglesA.end() || bIt != trianglesB.end()) 1175 { 1176 const bool aEnd = aIt == trianglesA.end(); 1177 const bool bEnd = bIt == trianglesB.end(); 1178 1179 if (aEnd || bEnd || *aIt != *bIt) 1180 { 1181 log << tcu::TestLog::Message << "Failure: triangle sets in two cases are not equal (when ignoring triangle and vertex order" 1182 << (ignoredTriangleDescription == DE_NULL ? "" : std::string() + ", and " + ignoredTriangleDescription) << ")" << tcu::TestLog::EndMessage; 1183 1184 if (!aEnd && (bEnd || TriangleLexLessThan()(*aIt, *bIt))) 1185 log << tcu::TestLog::Message << "Note: e.g. triangle " << *aIt << " exists for first case but not for second" << tcu::TestLog::EndMessage; 1186 else 1187 log << tcu::TestLog::Message << "Note: e.g. triangle " << *bIt << " exists for second case but not for first" << tcu::TestLog::EndMessage; 1188 1189 return false; 1190 } 1191 1192 ++aIt; 1193 ++bIt; 1194 } 1195 1196 return true; 1197 } 1198 } 1199 1200 template <typename ArgT, bool res> 1201 struct ConstantUnaryPredicate 1202 { 1203 bool operator() (const ArgT&) const { return res; } 1204 }; 1205 1206 bool compareTriangleSets (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, tcu::TestLog& log) 1207 { 1208 return compareTriangleSets(primitivesA, primitivesB, log, ConstantUnaryPredicate<const tcu::Vec3*, true>()); 1209 } 1210 1211 //! Compare two sets of primitives. Order of primitives in each set is undefined, but within each primitive 1212 //! vertex order and coordinates are expected to match exactly. 1213 bool comparePrimitivesExact (const PerPrimitive* const primitivesA, const PerPrimitive* const primitivesB, const int numPrimitivesPerPatch) 1214 { 1215 int ndxB = 0; 1216 for (int ndxA = 0; ndxA < numPrimitivesPerPatch; ++ndxA) 1217 { 1218 const tcu::Vec4 (&coordsA)[3] = primitivesA[ndxA].tessCoord; 1219 bool match = false; 1220 1221 // Actually both sets are usually somewhat sorted, so don't reset ndxB after each match. Instead, continue from the next index. 1222 for (int i = 0; i < numPrimitivesPerPatch; ++i) 1223 { 1224 const tcu::Vec4 (&coordsB)[3] = primitivesB[ndxB].tessCoord; 1225 ndxB = (ndxB + 1) % numPrimitivesPerPatch; 1226 1227 if (coordsA[0] == coordsB[0] && coordsA[1] == coordsB[1] && coordsA[2] == coordsB[2]) 1228 { 1229 match = true; 1230 break; 1231 } 1232 } 1233 1234 if (!match) 1235 return false; 1236 } 1237 return true; 1238 } 1239 1240 /*--------------------------------------------------------------------*//*! 1241 * \brief Base class for testing invariance of entire primitive set 1242 * 1243 * Draws two patches with identical tessellation levels and compares the 1244 * results. Repeats the same with other programs that are only different 1245 * in irrelevant ways; compares the results between these two programs. 1246 * Also potentially compares to results produced by different tessellation 1247 * levels (see e.g. invariance rule #6). 1248 * Furthermore, repeats the above with multiple different tessellation 1249 * value sets. 1250 * 1251 * The manner of primitive set comparison is defined by subclass. E.g. 1252 * case for invariance rule #1 tests that same vertices come out, in same 1253 * order; rule #5 only requires that the same triangles are output, but 1254 * not necessarily in the same order. 1255 *//*--------------------------------------------------------------------*/ 1256 class InvarianceTestCase : public TestCase 1257 { 1258 public: 1259 InvarianceTestCase (tcu::TestContext& context, const std::string& name, const std::string& description, const CaseDefinition& caseDef) 1260 : TestCase (context, name, description) 1261 , m_caseDef (caseDef) {} 1262 1263 virtual ~InvarianceTestCase (void) {} 1264 1265 void initPrograms (SourceCollections& programCollection) const; 1266 TestInstance* createInstance (Context& context) const; 1267 1268 private: 1269 const CaseDefinition m_caseDef; 1270 }; 1271 1272 void InvarianceTestCase::initPrograms (SourceCollections& programCollection) const 1273 { 1274 addDefaultPrograms(programCollection, m_caseDef.primitiveType, m_caseDef.spacingMode, m_caseDef.windingUsage, getPointModeUsage(m_caseDef.usePointMode)); 1275 } 1276 1277 class InvarianceTestInstance : public TestInstance 1278 { 1279 public: 1280 InvarianceTestInstance (Context& context, const CaseDefinition& caseDef) : TestInstance(context), m_caseDef(caseDef) {} 1281 virtual ~InvarianceTestInstance (void) {} 1282 1283 tcu::TestStatus iterate (void); 1284 1285 protected: 1286 virtual std::vector<LevelCase> genTessLevelCases (void) const; 1287 virtual bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int levelCaseMem) const = 0; 1288 1289 const CaseDefinition m_caseDef; 1290 }; 1291 1292 std::vector<LevelCase> InvarianceTestInstance::genTessLevelCases (void) const 1293 { 1294 static const TessLevels basicTessLevelCases[] = 1295 { 1296 { { 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, 1297 { { 63.0f, 24.0f }, { 15.0f, 42.0f, 10.0f, 12.0f } }, 1298 { { 3.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } }, 1299 { { 4.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } }, 1300 { { 2.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } }, 1301 { { 5.0f, 6.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, 1302 { { 1.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } }, 1303 { { 5.0f, 1.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } }, 1304 { { 5.2f, 1.6f }, { 2.9f, 3.4f, 1.5f, 4.1f } } 1305 }; 1306 1307 std::vector<LevelCase> result; 1308 for (int i = 0; i < DE_LENGTH_OF_ARRAY(basicTessLevelCases); ++i) 1309 result.push_back(LevelCase(basicTessLevelCases[i])); 1310 1311 { 1312 de::Random rnd(123); 1313 for (int i = 0; i < 10; ++i) 1314 { 1315 TessLevels levels; 1316 for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.inner); ++j) 1317 levels.inner[j] = rnd.getFloat(1.0f, 16.0f); 1318 for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.outer); ++j) 1319 levels.outer[j] = rnd.getFloat(1.0f, 16.0f); 1320 result.push_back(LevelCase(levels)); 1321 } 1322 } 1323 1324 return result; 1325 } 1326 1327 tcu::TestStatus InvarianceTestInstance::iterate (void) 1328 { 1329 requireFeatures(m_context.getInstanceInterface(), m_context.getPhysicalDevice(), 1330 FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS); 1331 1332 const DeviceInterface& vk = m_context.getDeviceInterface(); 1333 const VkDevice device = m_context.getDevice(); 1334 const VkQueue queue = m_context.getUniversalQueue(); 1335 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); 1336 Allocator& allocator = m_context.getDefaultAllocator(); 1337 1338 const std::vector<LevelCase> tessLevelCases = genTessLevelCases(); 1339 const int numPatchesPerDrawCall = 2; 1340 int maxNumPrimitivesPerPatch = 0; // computed below 1341 std::vector<std::vector<int> > primitiveCounts; 1342 1343 for (int caseNdx = 0; caseNdx < static_cast<int>(tessLevelCases.size()); ++caseNdx) 1344 { 1345 primitiveCounts.push_back(std::vector<int>()); 1346 for (int levelNdx = 0; levelNdx < static_cast<int>(tessLevelCases[caseNdx].levels.size()); ++levelNdx) 1347 { 1348 const int primitiveCount = referencePrimitiveCount(m_caseDef.primitiveType, m_caseDef.spacingMode, m_caseDef.usePointMode, 1349 &tessLevelCases[caseNdx].levels[levelNdx].inner[0], &tessLevelCases[caseNdx].levels[levelNdx].outer[0]); 1350 primitiveCounts.back().push_back(primitiveCount); 1351 maxNumPrimitivesPerPatch = de::max(maxNumPrimitivesPerPatch, primitiveCount); 1352 } 1353 } 1354 1355 // Allow for more primitievs in case tessellation/geometry has extra invocations 1356 maxNumPrimitivesPerPatch *= NUM_EXTRA_TESS_GEOM_INVOCATIONS; 1357 1358 // Vertex input attributes buffer: to pass tessellation levels 1359 1360 const VkFormat vertexFormat = VK_FORMAT_R32_SFLOAT; 1361 const deUint32 vertexStride = tcu::getPixelSize(mapVkFormat(vertexFormat)); 1362 const VkDeviceSize vertexDataSizeBytes = NUM_TESS_LEVELS * numPatchesPerDrawCall * vertexStride; 1363 const Buffer vertexBuffer (vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible); 1364 1365 // Output buffer: number of primitives and an array of PerPrimitive structures 1366 1367 const int resultBufferMaxVertices = numPatchesPerDrawCall * maxNumPrimitivesPerPatch * numVerticesPerPrimitive(m_caseDef.primitiveType, m_caseDef.usePointMode); 1368 const int resultBufferTessCoordsOffset = (int)sizeof(deInt32) * 4; 1369 const VkDeviceSize resultBufferSizeBytes = resultBufferTessCoordsOffset + resultBufferMaxVertices * sizeof(PerPrimitive); 1370 const Buffer resultBuffer (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible); 1371 1372 // Descriptors 1373 1374 const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder() 1375 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_GEOMETRY_BIT) 1376 .build(vk, device)); 1377 1378 const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder() 1379 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) 1380 .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u)); 1381 1382 const Unique<VkDescriptorSet> descriptorSet (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout)); 1383 const VkDescriptorBufferInfo resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes); 1384 1385 DescriptorSetUpdateBuilder() 1386 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo) 1387 .update(vk, device); 1388 1389 const Unique<VkRenderPass> renderPass (makeRenderPassWithoutAttachments (vk, device)); 1390 const Unique<VkFramebuffer> framebuffer (makeFramebufferWithoutAttachments(vk, device, *renderPass)); 1391 const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout (vk, device, *descriptorSetLayout)); 1392 const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex)); 1393 const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer (vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY)); 1394 1395 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < static_cast<int>(tessLevelCases.size()); ++tessLevelCaseNdx) 1396 { 1397 const LevelCase& levelCase = tessLevelCases[tessLevelCaseNdx]; 1398 PerPrimitiveVec firstPrim; 1399 1400 { 1401 tcu::TestLog& log = m_context.getTestContext().getLog(); 1402 std::ostringstream tessLevelsStr; 1403 1404 for (int i = 0; i < static_cast<int>(levelCase.levels.size()); ++i) 1405 tessLevelsStr << (levelCase.levels.size() > 1u ? "\n" : "") << getTessellationLevelsString(levelCase.levels[i], m_caseDef.primitiveType); 1406 1407 log << tcu::TestLog::Message << "Tessellation level sets: " << tessLevelsStr.str() << tcu::TestLog::EndMessage; 1408 } 1409 1410 for (int subTessLevelCaseNdx = 0; subTessLevelCaseNdx < static_cast<int>(levelCase.levels.size()); ++subTessLevelCaseNdx) 1411 { 1412 const TessLevels& tessLevels = levelCase.levels[subTessLevelCaseNdx]; 1413 { 1414 TessLevels data[2]; 1415 data[0] = tessLevels; 1416 data[1] = tessLevels; 1417 1418 const Allocation& alloc = vertexBuffer.getAllocation(); 1419 deMemcpy(alloc.getHostPtr(), data, sizeof(data)); 1420 flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(data)); 1421 } 1422 1423 int programNdx = 0; 1424 const std::vector<Winding> windingCases = getWindingCases(m_caseDef.windingUsage); 1425 for (std::vector<Winding>::const_iterator windingIter = windingCases.begin(); windingIter != windingCases.end(); ++windingIter) 1426 { 1427 const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder() 1428 .setPatchControlPoints (NUM_TESS_LEVELS) 1429 .setVertexInputSingleAttribute(vertexFormat, vertexStride) 1430 .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL) 1431 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL) 1432 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get(getProgramName("tese", *windingIter, m_caseDef.usePointMode)), DE_NULL) 1433 .setShader (vk, device, VK_SHADER_STAGE_GEOMETRY_BIT, m_context.getBinaryCollection().get(getProgramName("geom", m_caseDef.usePointMode)), DE_NULL) 1434 .build (vk, device, *pipelineLayout, *renderPass)); 1435 1436 { 1437 const Allocation& alloc = resultBuffer.getAllocation(); 1438 deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes)); 1439 flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes); 1440 } 1441 1442 beginCommandBuffer(vk, *cmdBuffer); 1443 beginRenderPassWithRasterizationDisabled(vk, *cmdBuffer, *renderPass, *framebuffer); 1444 1445 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); 1446 vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL); 1447 { 1448 const VkDeviceSize vertexBufferOffset = 0ull; 1449 vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset); 1450 } 1451 1452 vk.cmdDraw(*cmdBuffer, numPatchesPerDrawCall * NUM_TESS_LEVELS, 1u, 0u, 0u); 1453 endRenderPass(vk, *cmdBuffer); 1454 1455 { 1456 const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier( 1457 VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes); 1458 1459 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1460 0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL); 1461 } 1462 1463 endCommandBuffer(vk, *cmdBuffer); 1464 submitCommandsAndWait(vk, device, queue, *cmdBuffer); 1465 1466 // Verify case result 1467 { 1468 const Allocation& resultAlloc = resultBuffer.getAllocation(); 1469 invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes); 1470 1471 const int refNumPrimitives = numPatchesPerDrawCall * primitiveCounts[tessLevelCaseNdx][subTessLevelCaseNdx]; 1472 const int numPrimitiveVertices = numVerticesPerPrimitive(m_caseDef.primitiveType, m_caseDef.usePointMode); 1473 const deInt32 numPrimitives = *static_cast<deInt32*>(resultAlloc.getHostPtr()); 1474 const PerPrimitiveVec primitives = sorted(readInterleavedData<PerPrimitive>(numPrimitives, resultAlloc.getHostPtr(), resultBufferTessCoordsOffset, sizeof(PerPrimitive)), 1475 byPatchPrimitiveID); 1476 1477 // If this fails then we didn't read all vertices from shader and test must be changed to allow more. 1478 DE_ASSERT(numPrimitiveVertices * numPrimitives <= resultBufferMaxVertices); 1479 DE_UNREF(numPrimitiveVertices); 1480 1481 tcu::TestLog& log = m_context.getTestContext().getLog(); 1482 1483 if (numPrimitives < refNumPrimitives) 1484 { 1485 log << tcu::TestLog::Message << "Failure: got " << numPrimitives << " primitives, but expected at least" << refNumPrimitives << tcu::TestLog::EndMessage; 1486 1487 return tcu::TestStatus::fail("Invalid set of primitives"); 1488 } 1489 1490 const int half = static_cast<int>(primitives.size() / 2); 1491 const PerPrimitiveVec prim0 = PerPrimitiveVec(primitives.begin(), primitives.begin() + half); 1492 const PerPrimitive* const prim1 = &primitives[half]; 1493 1494 if (!comparePrimitivesExact(&prim0[0], prim1, half)) 1495 { 1496 log << tcu::TestLog::Message << "Failure: tessellation coordinates differ between two primitives drawn in one draw call" << tcu::TestLog::EndMessage 1497 << tcu::TestLog::Message << "Note: tessellation levels for both primitives were: " << getTessellationLevelsString(tessLevels, m_caseDef.primitiveType) << tcu::TestLog::EndMessage; 1498 1499 return tcu::TestStatus::fail("Invalid set of primitives"); 1500 } 1501 1502 if (programNdx == 0 && subTessLevelCaseNdx == 0) 1503 firstPrim = prim0; 1504 else 1505 { 1506 const bool compareOk = compare(firstPrim, prim0, levelCase.mem); 1507 if (!compareOk) 1508 { 1509 log << tcu::TestLog::Message 1510 << "Note: comparison of tessellation coordinates failed; comparison was made between following cases:\n" 1511 << " - case A: program 0, tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx].levels[0], m_caseDef.primitiveType) << "\n" 1512 << " - case B: program " << programNdx << ", tessellation levels: " << getTessellationLevelsString(tessLevels, m_caseDef.primitiveType) 1513 << tcu::TestLog::EndMessage; 1514 1515 return tcu::TestStatus::fail("Invalid set of primitives"); 1516 } 1517 } 1518 } 1519 ++programNdx; 1520 } 1521 } 1522 } 1523 return tcu::TestStatus::pass("OK"); 1524 } 1525 1526 /*--------------------------------------------------------------------*//*! 1527 * \brief Test invariance rule #1 1528 * 1529 * Test that the sequence of primitives input to the TES only depends on 1530 * the tessellation levels, tessellation mode, spacing mode, winding, and 1531 * point mode. 1532 *//*--------------------------------------------------------------------*/ 1533 class InvariantPrimitiveSetTestInstance : public InvarianceTestInstance 1534 { 1535 public: 1536 InvariantPrimitiveSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {} 1537 1538 protected: 1539 bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int) const 1540 { 1541 if (!comparePrimitivesExact(&primitivesA[0], &primitivesB[0], static_cast<int>(primitivesA.size()))) 1542 { 1543 m_context.getTestContext().getLog() 1544 << tcu::TestLog::Message << "Failure: tessellation coordinates differ between two programs" << tcu::TestLog::EndMessage; 1545 1546 return false; 1547 } 1548 return true; 1549 } 1550 }; 1551 1552 /*--------------------------------------------------------------------*//*! 1553 * \brief Test invariance rule #5 1554 * 1555 * Test that the set of triangles input to the TES only depends on the 1556 * tessellation levels, tessellation mode and spacing mode. Specifically, 1557 * winding doesn't change the set of triangles, though it can change the 1558 * order in which they are input to TES, and can (and will) change the 1559 * vertex order within a triangle. 1560 *//*--------------------------------------------------------------------*/ 1561 class InvariantTriangleSetTestInstance : public InvarianceTestInstance 1562 { 1563 public: 1564 InvariantTriangleSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {} 1565 1566 protected: 1567 bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int) const 1568 { 1569 return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog()); 1570 } 1571 }; 1572 1573 /*--------------------------------------------------------------------*//*! 1574 * \brief Test invariance rule #6 1575 * 1576 * Test that the set of inner triangles input to the TES only depends on 1577 * the inner tessellation levels, tessellation mode and spacing mode. 1578 *//*--------------------------------------------------------------------*/ 1579 class InvariantInnerTriangleSetTestInstance : public InvarianceTestInstance 1580 { 1581 public: 1582 InvariantInnerTriangleSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {} 1583 1584 protected: 1585 std::vector<LevelCase> genTessLevelCases (void) const 1586 { 1587 const int numSubCases = 4; 1588 const std::vector<LevelCase> baseResults = InvarianceTestInstance::genTessLevelCases(); 1589 std::vector<LevelCase> result; 1590 de::Random rnd (123); 1591 1592 // Generate variants with different values for irrelevant levels. 1593 for (int baseNdx = 0; baseNdx < static_cast<int>(baseResults.size()); ++baseNdx) 1594 { 1595 const TessLevels& base = baseResults[baseNdx].levels[0]; 1596 TessLevels levels = base; 1597 LevelCase levelCase; 1598 1599 for (int subNdx = 0; subNdx < numSubCases; ++subNdx) 1600 { 1601 levelCase.levels.push_back(levels); 1602 1603 for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); ++i) 1604 levels.outer[i] = rnd.getFloat(2.0f, 16.0f); 1605 if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 1606 levels.inner[1] = rnd.getFloat(2.0f, 16.0f); 1607 } 1608 1609 result.push_back(levelCase); 1610 } 1611 1612 return result; 1613 } 1614 1615 struct IsInnerTriangleTriangle 1616 { 1617 bool operator() (const tcu::Vec3* vertices) const 1618 { 1619 for (int v = 0; v < 3; ++v) 1620 for (int c = 0; c < 3; ++c) 1621 if (vertices[v][c] == 0.0f) 1622 return false; 1623 return true; 1624 } 1625 }; 1626 1627 struct IsInnerQuadTriangle 1628 { 1629 bool operator() (const tcu::Vec3* vertices) const 1630 { 1631 for (int v = 0; v < 3; ++v) 1632 for (int c = 0; c < 2; ++c) 1633 if (vertices[v][c] == 0.0f || vertices[v][c] == 1.0f) 1634 return false; 1635 return true; 1636 } 1637 }; 1638 1639 bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int) const 1640 { 1641 if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 1642 return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(), IsInnerTriangleTriangle(), "outer triangles"); 1643 else if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS) 1644 return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(), IsInnerQuadTriangle(), "outer triangles"); 1645 else 1646 { 1647 DE_ASSERT(false); 1648 return false; 1649 } 1650 } 1651 }; 1652 1653 /*--------------------------------------------------------------------*//*! 1654 * \brief Test invariance rule #7 1655 * 1656 * Test that the set of outer triangles input to the TES only depends on 1657 * tessellation mode, spacing mode and the inner and outer tessellation 1658 * levels corresponding to the inner and outer edges relevant to that 1659 * triangle. 1660 *//*--------------------------------------------------------------------*/ 1661 class InvariantOuterTriangleSetTestInstance : public InvarianceTestInstance 1662 { 1663 public: 1664 InvariantOuterTriangleSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {} 1665 1666 protected: 1667 std::vector<LevelCase> genTessLevelCases (void) const 1668 { 1669 const int numSubCasesPerEdge = 4; 1670 const int numEdges = m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 1671 : m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS ? 4 : 0; 1672 const std::vector<LevelCase> baseResult = InvarianceTestInstance::genTessLevelCases(); 1673 std::vector<LevelCase> result; 1674 de::Random rnd (123); 1675 1676 // Generate variants with different values for irrelevant levels. 1677 for (int baseNdx = 0; baseNdx < static_cast<int>(baseResult.size()); ++baseNdx) 1678 { 1679 const TessLevels& base = baseResult[baseNdx].levels[0]; 1680 if (base.inner[0] == 1.0f || (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS && base.inner[1] == 1.0f)) 1681 continue; 1682 1683 for (int edgeNdx = 0; edgeNdx < numEdges; ++edgeNdx) 1684 { 1685 TessLevels levels = base; 1686 LevelCase levelCase; 1687 levelCase.mem = edgeNdx; 1688 1689 for (int subCaseNdx = 0; subCaseNdx < numSubCasesPerEdge; ++subCaseNdx) 1690 { 1691 levelCase.levels.push_back(levels); 1692 1693 for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); ++i) 1694 { 1695 if (i != edgeNdx) 1696 levels.outer[i] = rnd.getFloat(2.0f, 16.0f); 1697 } 1698 1699 if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 1700 levels.inner[1] = rnd.getFloat(2.0f, 16.0f); 1701 } 1702 1703 result.push_back(levelCase); 1704 } 1705 } 1706 1707 return result; 1708 } 1709 1710 class IsTriangleTriangleOnOuterEdge 1711 { 1712 public: 1713 IsTriangleTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {} 1714 bool operator() (const tcu::Vec3* vertices) const 1715 { 1716 bool touchesAppropriateEdge = false; 1717 for (int v = 0; v < 3; ++v) 1718 if (vertices[v][m_edgeNdx] == 0.0f) 1719 touchesAppropriateEdge = true; 1720 1721 if (touchesAppropriateEdge) 1722 { 1723 const tcu::Vec3 avg = (vertices[0] + vertices[1] + vertices[2]) / 3.0f; 1724 return avg[m_edgeNdx] < avg[(m_edgeNdx+1)%3] && 1725 avg[m_edgeNdx] < avg[(m_edgeNdx+2)%3]; 1726 } 1727 return false; 1728 } 1729 1730 private: 1731 const int m_edgeNdx; 1732 }; 1733 1734 class IsQuadTriangleOnOuterEdge 1735 { 1736 public: 1737 IsQuadTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {} 1738 1739 bool onEdge (const tcu::Vec3& v) const 1740 { 1741 return v[m_edgeNdx%2] == (m_edgeNdx <= 1 ? 0.0f : 1.0f); 1742 } 1743 1744 static inline bool onAnyEdge (const tcu::Vec3& v) 1745 { 1746 return v[0] == 0.0f || v[0] == 1.0f || v[1] == 0.0f || v[1] == 1.0f; 1747 } 1748 1749 bool operator() (const tcu::Vec3* vertices) const 1750 { 1751 for (int v = 0; v < 3; ++v) 1752 { 1753 const tcu::Vec3& a = vertices[v]; 1754 const tcu::Vec3& b = vertices[(v+1)%3]; 1755 const tcu::Vec3& c = vertices[(v+2)%3]; 1756 if (onEdge(a) && onEdge(b)) 1757 return true; 1758 if (onEdge(c) && !onAnyEdge(a) && !onAnyEdge(b) && a[m_edgeNdx%2] == b[m_edgeNdx%2]) 1759 return true; 1760 } 1761 1762 return false; 1763 } 1764 1765 private: 1766 const int m_edgeNdx; 1767 }; 1768 1769 bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int outerEdgeNdx) const 1770 { 1771 if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 1772 { 1773 return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(), 1774 IsTriangleTriangleOnOuterEdge(outerEdgeNdx), 1775 ("inner triangles, and outer triangles corresponding to other edge than edge " 1776 + outerEdgeDescriptions(m_caseDef.primitiveType)[outerEdgeNdx].description()).c_str()); 1777 } 1778 else if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS) 1779 { 1780 return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(), 1781 IsQuadTriangleOnOuterEdge(outerEdgeNdx), 1782 ("inner triangles, and outer triangles corresponding to other edge than edge " 1783 + outerEdgeDescriptions(m_caseDef.primitiveType)[outerEdgeNdx].description()).c_str()); 1784 } 1785 else 1786 DE_ASSERT(false); 1787 1788 return true; 1789 } 1790 }; 1791 1792 TestInstance* InvarianceTestCase::createInstance (Context& context) const 1793 { 1794 switch (m_caseDef.caseType) 1795 { 1796 case CASETYPE_INVARIANT_PRIMITIVE_SET: return new InvariantPrimitiveSetTestInstance (context, m_caseDef); 1797 case CASETYPE_INVARIANT_TRIANGLE_SET: return new InvariantTriangleSetTestInstance (context, m_caseDef); 1798 case CASETYPE_INVARIANT_OUTER_TRIANGLE_SET: return new InvariantOuterTriangleSetTestInstance(context, m_caseDef); 1799 case CASETYPE_INVARIANT_INNER_TRIANGLE_SET: return new InvariantInnerTriangleSetTestInstance(context, m_caseDef); 1800 default: 1801 DE_ASSERT(false); 1802 return DE_NULL; 1803 } 1804 } 1805 1806 TestCase* makeInvariantPrimitiveSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode) 1807 { 1808 const CaseDefinition caseDef = { CASETYPE_INVARIANT_PRIMITIVE_SET, primitiveType, spacingMode, getWindingUsage(winding), usePointMode }; 1809 return new InvarianceTestCase(testCtx, name, description, caseDef); 1810 } 1811 1812 TestCase* makeInvariantTriangleSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode) 1813 { 1814 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS); 1815 const CaseDefinition caseDef = { CASETYPE_INVARIANT_TRIANGLE_SET, primitiveType, spacingMode, WINDING_USAGE_VARY, false }; 1816 return new InvarianceTestCase(testCtx, name, description, caseDef); 1817 } 1818 1819 TestCase* makeInvariantInnerTriangleSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode) 1820 { 1821 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS); 1822 const CaseDefinition caseDef = { CASETYPE_INVARIANT_INNER_TRIANGLE_SET, primitiveType, spacingMode, WINDING_USAGE_VARY, false }; 1823 return new InvarianceTestCase(testCtx, name, description, caseDef); 1824 } 1825 1826 TestCase* makeInvariantOuterTriangleSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode) 1827 { 1828 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS); 1829 const CaseDefinition caseDef = { CASETYPE_INVARIANT_OUTER_TRIANGLE_SET, primitiveType, spacingMode, WINDING_USAGE_VARY, false }; 1830 return new InvarianceTestCase(testCtx, name, description, caseDef); 1831 } 1832 1833 } // PrimitiveSetInvariance ns 1834 1835 namespace TessCoordComponent 1836 { 1837 1838 enum CaseType 1839 { 1840 CASETYPE_TESS_COORD_RANGE = 0, //!< Test that all (relevant) components of tess coord are in [0,1]. 1841 CASETYPE_ONE_MINUS_TESS_COORD, //!< Test that for every (relevant) component c of a tess coord, 1.0-c is exact. 1842 1843 CASETYPE_LAST 1844 }; 1845 1846 struct CaseDefinition 1847 { 1848 CaseType caseType; 1849 TessPrimitiveType primitiveType; 1850 SpacingMode spacingMode; 1851 Winding winding; 1852 bool usePointMode; 1853 }; 1854 1855 std::vector<TessLevels> genTessLevelCases (const int numCases) 1856 { 1857 de::Random rnd(123); 1858 std::vector<TessLevels> result; 1859 1860 for (int i = 0; i < numCases; ++i) 1861 { 1862 TessLevels levels; 1863 levels.inner[0] = rnd.getFloat(1.0f, 63.0f); 1864 levels.inner[1] = rnd.getFloat(1.0f, 63.0f); 1865 levels.outer[0] = rnd.getFloat(1.0f, 63.0f); 1866 levels.outer[1] = rnd.getFloat(1.0f, 63.0f); 1867 levels.outer[2] = rnd.getFloat(1.0f, 63.0f); 1868 levels.outer[3] = rnd.getFloat(1.0f, 63.0f); 1869 result.push_back(levels); 1870 } 1871 1872 return result; 1873 } 1874 1875 typedef bool (*CompareFunc)(tcu::TestLog& log, const float value); 1876 1877 bool compareTessCoordRange (tcu::TestLog& log, const float value) 1878 { 1879 if (!de::inRange(value, 0.0f, 1.0f)) 1880 { 1881 log << tcu::TestLog::Message << "Failure: tess coord component isn't in range [0,1]" << tcu::TestLog::EndMessage; 1882 return false; 1883 } 1884 return true; 1885 } 1886 1887 bool compareOneMinusTessCoord (tcu::TestLog& log, const float value) 1888 { 1889 if (value != 1.0f) 1890 { 1891 log << tcu::TestLog::Message << "Failure: comp + (1.0-comp) doesn't equal 1.0 for some component of tessellation coordinate" << tcu::TestLog::EndMessage; 1892 return false; 1893 } 1894 return true; 1895 } 1896 1897 void initPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef) 1898 { 1899 // Vertex shader 1900 { 1901 std::ostringstream src; 1902 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 1903 << "\n" 1904 << "layout(location = 0) in highp float in_v_attr;\n" 1905 << "layout(location = 0) out highp float in_tc_attr;\n" 1906 << "\n" 1907 << "void main (void)\n" 1908 << "{\n" 1909 << " in_tc_attr = in_v_attr;\n" 1910 << "}\n"; 1911 1912 programCollection.glslSources.add("vert") << glu::VertexSource(src.str()); 1913 } 1914 1915 // Tessellation control shader 1916 { 1917 std::ostringstream src; 1918 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 1919 << "#extension GL_EXT_tessellation_shader : require\n" 1920 << "\n" 1921 << "layout(vertices = 1) out;\n" 1922 << "\n" 1923 << "layout(location = 0) in highp float in_tc_attr[];\n" 1924 << "\n" 1925 << "void main (void)\n" 1926 << "{\n" 1927 << " gl_TessLevelInner[0] = in_tc_attr[0];\n" 1928 << " gl_TessLevelInner[1] = in_tc_attr[1];\n" 1929 << "\n" 1930 << " gl_TessLevelOuter[0] = in_tc_attr[2];\n" 1931 << " gl_TessLevelOuter[1] = in_tc_attr[3];\n" 1932 << " gl_TessLevelOuter[2] = in_tc_attr[4];\n" 1933 << " gl_TessLevelOuter[3] = in_tc_attr[5];\n" 1934 << "}\n"; 1935 1936 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str()); 1937 } 1938 1939 // Tessellation evaluation shader 1940 { 1941 std::ostringstream tessCoordSrc; 1942 1943 if (caseDef.caseType == CASETYPE_TESS_COORD_RANGE) 1944 tessCoordSrc << " sb_out.tessCoord[index] = gl_TessCoord;\n"; 1945 else if (caseDef.caseType == CASETYPE_ONE_MINUS_TESS_COORD) 1946 { 1947 const char* components[] = { "x" , "y", "z" }; 1948 const int numComponents = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2); 1949 1950 for (int i = 0; i < numComponents; ++i) 1951 tessCoordSrc << " {\n" 1952 << " float oneMinusComp = 1.0 - gl_TessCoord." << components[i] << ";\n" 1953 << " sb_out.tessCoord[index]." << components[i] << " = gl_TessCoord." << components[i] << " + oneMinusComp;\n" 1954 << " }\n"; 1955 } 1956 else 1957 { 1958 DE_ASSERT(false); 1959 return; 1960 } 1961 1962 std::ostringstream src; 1963 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 1964 << "#extension GL_EXT_tessellation_shader : require\n" 1965 << "\n" 1966 << "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", " 1967 << getSpacingModeShaderName(caseDef.spacingMode) << ", " 1968 << getWindingShaderName(caseDef.winding) 1969 << (caseDef.usePointMode ? ", point_mode" : "") << ") in;\n" 1970 << "\n" 1971 << "layout(set = 0, binding = 0, std430) coherent restrict buffer Output {\n" 1972 << " int numInvocations;\n" 1973 << " vec3 tessCoord[];\n" 1974 << "} sb_out;\n" 1975 << "\n" 1976 << "void main (void)\n" 1977 << "{\n" 1978 << " int index = atomicAdd(sb_out.numInvocations, 1);\n" 1979 << tessCoordSrc.str() 1980 << "}\n"; 1981 1982 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str()); 1983 } 1984 } 1985 1986 tcu::TestStatus test (Context& context, const CaseDefinition caseDef) 1987 { 1988 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS); 1989 1990 const DeviceInterface& vk = context.getDeviceInterface(); 1991 const VkDevice device = context.getDevice(); 1992 const VkQueue queue = context.getUniversalQueue(); 1993 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex(); 1994 Allocator& allocator = context.getDefaultAllocator(); 1995 1996 const int numTessLevelCases = 32; 1997 const std::vector<TessLevels> tessLevelCases = genTessLevelCases(numTessLevelCases); 1998 1999 int maxNumVerticesInDrawCall = 0; 2000 for (int i = 0; i < numTessLevelCases; ++i) 2001 maxNumVerticesInDrawCall = de::max(maxNumVerticesInDrawCall, referenceVertexCount(caseDef.primitiveType, caseDef.spacingMode, caseDef.usePointMode, 2002 &tessLevelCases[i].inner[0], &tessLevelCases[i].outer[0])); 2003 2004 // We may get more invocations than expected, so add some more space (arbitrary number). 2005 maxNumVerticesInDrawCall += 4; 2006 2007 // Vertex input attributes buffer: to pass tessellation levels 2008 2009 const VkFormat vertexFormat = VK_FORMAT_R32_SFLOAT; 2010 const deUint32 vertexStride = tcu::getPixelSize(mapVkFormat(vertexFormat)); 2011 const VkDeviceSize vertexDataSizeBytes = NUM_TESS_LEVELS * vertexStride; 2012 const Buffer vertexBuffer (vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible); 2013 2014 DE_ASSERT(vertexDataSizeBytes == sizeof(TessLevels)); 2015 2016 // Output buffer: number of invocations and array of tess coords 2017 2018 const int resultBufferTessCoordsOffset = (int)sizeof(deInt32) * 4; 2019 const VkDeviceSize resultBufferSizeBytes = resultBufferTessCoordsOffset + maxNumVerticesInDrawCall * sizeof(tcu::Vec4); 2020 const Buffer resultBuffer (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible); 2021 2022 // Descriptors 2023 2024 const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder() 2025 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) 2026 .build(vk, device)); 2027 2028 const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder() 2029 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) 2030 .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u)); 2031 2032 const Unique<VkDescriptorSet> descriptorSet (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout)); 2033 const VkDescriptorBufferInfo resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes); 2034 2035 DescriptorSetUpdateBuilder() 2036 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo) 2037 .update(vk, device); 2038 2039 const Unique<VkRenderPass> renderPass (makeRenderPassWithoutAttachments (vk, device)); 2040 const Unique<VkFramebuffer> framebuffer (makeFramebufferWithoutAttachments(vk, device, *renderPass)); 2041 const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout (vk, device, *descriptorSetLayout)); 2042 const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex)); 2043 const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer (vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY)); 2044 2045 const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder() 2046 .setPatchControlPoints (NUM_TESS_LEVELS) 2047 .setVertexInputSingleAttribute(vertexFormat, vertexStride) 2048 .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert"), DE_NULL) 2049 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, context.getBinaryCollection().get("tesc"), DE_NULL) 2050 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"), DE_NULL) 2051 .build (vk, device, *pipelineLayout, *renderPass)); 2052 2053 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < numTessLevelCases; ++tessLevelCaseNdx) 2054 { 2055 context.getTestContext().getLog() 2056 << tcu::TestLog::Message 2057 << "Testing with tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx], caseDef.primitiveType) 2058 << tcu::TestLog::EndMessage; 2059 2060 { 2061 const Allocation& alloc = vertexBuffer.getAllocation(); 2062 deMemcpy(alloc.getHostPtr(), &tessLevelCases[tessLevelCaseNdx], sizeof(TessLevels)); 2063 flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(TessLevels)); 2064 } 2065 { 2066 const Allocation& alloc = resultBuffer.getAllocation(); 2067 deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes)); 2068 flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes); 2069 } 2070 2071 beginCommandBuffer(vk, *cmdBuffer); 2072 beginRenderPassWithRasterizationDisabled(vk, *cmdBuffer, *renderPass, *framebuffer); 2073 2074 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); 2075 vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL); 2076 { 2077 const VkDeviceSize vertexBufferOffset = 0ull; 2078 vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset); 2079 } 2080 2081 vk.cmdDraw(*cmdBuffer, NUM_TESS_LEVELS, 1u, 0u, 0u); 2082 endRenderPass(vk, *cmdBuffer); 2083 2084 { 2085 const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier( 2086 VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes); 2087 2088 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 2089 0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL); 2090 } 2091 2092 endCommandBuffer(vk, *cmdBuffer); 2093 submitCommandsAndWait(vk, device, queue, *cmdBuffer); 2094 2095 // Verify case result 2096 { 2097 const Allocation& resultAlloc = resultBuffer.getAllocation(); 2098 invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes); 2099 2100 const deInt32 numVertices = *static_cast<deInt32*>(resultAlloc.getHostPtr()); 2101 const std::vector<tcu::Vec3> vertices = readInterleavedData<tcu::Vec3>(numVertices, resultAlloc.getHostPtr(), resultBufferTessCoordsOffset, sizeof(tcu::Vec4)); 2102 2103 // If this fails then we didn't read all vertices from shader and test must be changed to allow more. 2104 DE_ASSERT(numVertices <= maxNumVerticesInDrawCall); 2105 2106 tcu::TestLog& log = context.getTestContext().getLog(); 2107 const int numComponents = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2); 2108 2109 CompareFunc compare = (caseDef.caseType == CASETYPE_TESS_COORD_RANGE ? compareTessCoordRange : 2110 caseDef.caseType == CASETYPE_ONE_MINUS_TESS_COORD ? compareOneMinusTessCoord : DE_NULL); 2111 2112 DE_ASSERT(compare != DE_NULL); 2113 2114 for (std::vector<tcu::Vec3>::const_iterator vertexIter = vertices.begin(); vertexIter != vertices.end(); ++vertexIter) 2115 for (int i = 0; i < numComponents; ++i) 2116 if (!compare(log, (*vertexIter)[i])) 2117 { 2118 log << tcu::TestLog::Message << "Note: got a wrong tessellation coordinate " 2119 << (numComponents == 3 ? de::toString(*vertexIter) : de::toString(vertexIter->swizzle(0,1))) << tcu::TestLog::EndMessage; 2120 2121 tcu::TestStatus::fail("Invalid tessellation coordinate component"); 2122 } 2123 } 2124 } 2125 return tcu::TestStatus::pass("OK"); 2126 } 2127 2128 tcu::TestCase* makeTessCoordRangeTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode) 2129 { 2130 const CaseDefinition caseDef = { CASETYPE_TESS_COORD_RANGE, primitiveType, spacingMode, winding, usePointMode }; 2131 return createFunctionCaseWithPrograms(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, description, initPrograms, test, caseDef); 2132 } 2133 2134 tcu::TestCase* makeOneMinusTessCoordTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode) 2135 { 2136 const CaseDefinition caseDef = { CASETYPE_ONE_MINUS_TESS_COORD, primitiveType, spacingMode, winding, usePointMode }; 2137 return createFunctionCaseWithPrograms(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, description, initPrograms, test, caseDef); 2138 } 2139 2140 } // TessCoordComponent ns 2141 2142 } // anonymous 2143 2144 //! These tests correspond to dEQP-GLES31.functional.tessellation.invariance.* 2145 //! Original OpenGL ES tests used transform feedback to get vertices in primitive order. To emulate this behavior we have to use geometry shader, 2146 //! which allows us to intercept verticess of final output primitives. This can't be done with tessellation shaders alone as number and order of 2147 //! invocation is undefined. 2148 tcu::TestCaseGroup* createInvarianceTests (tcu::TestContext& testCtx) 2149 { 2150 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "invariance", "Test tessellation invariance rules")); 2151 2152 de::MovePtr<tcu::TestCaseGroup> invariantPrimitiveSetGroup (new tcu::TestCaseGroup(testCtx, "primitive_set", "Test invariance rule #1")); 2153 de::MovePtr<tcu::TestCaseGroup> invariantOuterEdgeGroup (new tcu::TestCaseGroup(testCtx, "outer_edge_division", "Test invariance rule #2")); 2154 de::MovePtr<tcu::TestCaseGroup> symmetricOuterEdgeGroup (new tcu::TestCaseGroup(testCtx, "outer_edge_symmetry", "Test invariance rule #3")); 2155 de::MovePtr<tcu::TestCaseGroup> outerEdgeVertexSetIndexIndependenceGroup(new tcu::TestCaseGroup(testCtx, "outer_edge_index_independence", "Test invariance rule #4")); 2156 de::MovePtr<tcu::TestCaseGroup> invariantTriangleSetGroup (new tcu::TestCaseGroup(testCtx, "triangle_set", "Test invariance rule #5")); 2157 de::MovePtr<tcu::TestCaseGroup> invariantInnerTriangleSetGroup (new tcu::TestCaseGroup(testCtx, "inner_triangle_set", "Test invariance rule #6")); 2158 de::MovePtr<tcu::TestCaseGroup> invariantOuterTriangleSetGroup (new tcu::TestCaseGroup(testCtx, "outer_triangle_set", "Test invariance rule #7")); 2159 de::MovePtr<tcu::TestCaseGroup> tessCoordComponentRangeGroup (new tcu::TestCaseGroup(testCtx, "tess_coord_component_range", "Test invariance rule #8, first part")); 2160 de::MovePtr<tcu::TestCaseGroup> oneMinusTessCoordComponentGroup (new tcu::TestCaseGroup(testCtx, "one_minus_tess_coord_component", "Test invariance rule #8, second part")); 2161 2162 for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; ++primitiveTypeNdx) 2163 for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx) 2164 { 2165 const TessPrimitiveType primitiveType = static_cast<TessPrimitiveType>(primitiveTypeNdx); 2166 const SpacingMode spacingMode = static_cast<SpacingMode>(spacingModeNdx); 2167 const bool triOrQuad = primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS; 2168 const std::string primName = getTessPrimitiveTypeShaderName(primitiveType); 2169 const std::string primSpacName = primName + "_" + getSpacingModeShaderName(spacingMode); 2170 2171 if (triOrQuad) 2172 { 2173 invariantOuterEdgeGroup->addChild ( InvariantOuterEdge::makeOuterEdgeDivisionTest (testCtx, primSpacName, "", primitiveType, spacingMode)); 2174 invariantTriangleSetGroup->addChild (PrimitiveSetInvariance::makeInvariantTriangleSetTest (testCtx, primSpacName, "", primitiveType, spacingMode)); 2175 invariantInnerTriangleSetGroup->addChild(PrimitiveSetInvariance::makeInvariantInnerTriangleSetTest(testCtx, primSpacName, "", primitiveType, spacingMode)); 2176 invariantOuterTriangleSetGroup->addChild(PrimitiveSetInvariance::makeInvariantOuterTriangleSetTest(testCtx, primSpacName, "", primitiveType, spacingMode)); 2177 } 2178 2179 for (int windingNdx = 0; windingNdx < WINDING_LAST; ++windingNdx) 2180 for (int usePointModeNdx = 0; usePointModeNdx <= 1; ++usePointModeNdx) 2181 { 2182 const Winding winding = static_cast<Winding>(windingNdx); 2183 const bool usePointMode = (usePointModeNdx != 0); 2184 const std::string primSpacWindPointName = primSpacName + "_" + getWindingShaderName(winding) + (usePointMode ? "_point_mode" : ""); 2185 2186 invariantPrimitiveSetGroup->addChild (PrimitiveSetInvariance::makeInvariantPrimitiveSetTest(testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode)); 2187 tessCoordComponentRangeGroup->addChild ( TessCoordComponent::makeTessCoordRangeTest (testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode)); 2188 oneMinusTessCoordComponentGroup->addChild( TessCoordComponent::makeOneMinusTessCoordTest (testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode)); 2189 symmetricOuterEdgeGroup->addChild ( InvariantOuterEdge::makeSymmetricOuterEdgeTest (testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode)); 2190 2191 if (triOrQuad) 2192 outerEdgeVertexSetIndexIndependenceGroup->addChild(InvariantOuterEdge::makeOuterEdgeIndexIndependenceTest(testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode)); 2193 } 2194 } 2195 2196 group->addChild(invariantPrimitiveSetGroup.release()); 2197 group->addChild(invariantOuterEdgeGroup.release()); 2198 group->addChild(symmetricOuterEdgeGroup.release()); 2199 group->addChild(outerEdgeVertexSetIndexIndependenceGroup.release()); 2200 group->addChild(invariantTriangleSetGroup.release()); 2201 group->addChild(invariantInnerTriangleSetGroup.release()); 2202 group->addChild(invariantOuterTriangleSetGroup.release()); 2203 group->addChild(tessCoordComponentRangeGroup.release()); 2204 group->addChild(oneMinusTessCoordComponentGroup.release()); 2205 2206 return group.release(); 2207 } 2208 2209 } // tessellation 2210 } // vkt 2211