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