Add tessellation shader tests (ported from ES 3.1)
[platform/upstream/VK-GL-CTS.git] / external / vulkancts / modules / vulkan / tessellation / vktTessellationInvarianceTests.cpp
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
46 namespace vkt
47 {
48 namespace tessellation
49 {
50
51 using namespace vk;
52
53 namespace
54 {
55
56 enum Constants
57 {
58         NUM_TESS_LEVELS = 6,  // two inner and four outer levels
59 };
60
61 enum WindingUsage
62 {
63         WINDING_USAGE_CCW = 0,
64         WINDING_USAGE_CW,
65         WINDING_USAGE_VARY,
66
67         WINDING_USAGE_LAST,
68 };
69
70 inline 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
78 std::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
100 enum 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
109 inline PointModeUsage getPointModeUsage (const bool usePointMode)
110 {
111         return usePointMode ? POINT_MODE_USAGE_USE : POINT_MODE_USAGE_DONT_USE;
112 }
113
114 std::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).
137 struct 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
147 typedef std::vector<PerPrimitive> PerPrimitiveVec;
148
149 inline bool byPatchPrimitiveID (const PerPrimitive& a, const PerPrimitive& b)
150 {
151         return a.patchPrimitiveID < b.patchPrimitiveID;
152 }
153
154 inline 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
161 inline 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
168 inline 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
175 template <typename T, int N>
176 std::vector<T> arrayToVector (const T (&arr)[N])
177 {
178         return std::vector<T>(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr));
179 }
180
181 template <typename T, int N>
182 T arrayMax (const T (&arr)[N])
183 {
184         return *std::max_element(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr));
185 }
186
187 template <int Size>
188 inline 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
196 template <typename ContainerT, typename T>
197 inline bool contains (const ContainerT& c, const T& key)
198 {
199         return c.find(key) != c.end();
200 }
201
202 template <typename SeqT, int Size, typename Pred>
203 class LexCompare
204 {
205 public:
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
220 private:
221         Pred m_pred;
222 };
223
224 template <int Size>
225 class 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.
233 void 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.
402 struct 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
429 std::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
463 namespace InvariantOuterEdge
464 {
465
466 struct CaseDefinition
467 {
468         TessPrimitiveType       primitiveType;
469         SpacingMode                     spacingMode;
470         Winding                         winding;
471         bool                            usePointMode;
472 };
473
474 typedef std::set<tcu::Vec3, VecLexLessThan<3> > Vec3Set;
475
476 std::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
494 std::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
500 int 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
508 template<std::size_t N>
509 int 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
516 void 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
523 void 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
542 class BaseTestInstance : public TestInstance
543 {
544 public:
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
558 protected:
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
582 const float BaseTestInstance::m_singleOuterEdgeLevels[] = { 1.0f, 1.2f, 1.9f, 2.3f, 2.8f, 3.3f, 3.8f, 10.2f, 1.6f, 24.4f, 24.7f, 63.0f };
583
584 BaseTestInstance::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             (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.
623 BaseTestInstance::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
693 void 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  *//*--------------------------------------------------------------------*/
716 class OuterEdgeDivisionTestInstance : public BaseTestInstance
717 {
718 public:
719                                                 OuterEdgeDivisionTestInstance   (Context& context, const CaseDefinition caseDef) : BaseTestInstance (context, caseDef, 10) {}
720         tcu::TestStatus         iterate                                                 (void);
721 };
722
723 tcu::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  *//*--------------------------------------------------------------------*/
807 class OuterEdgeIndexIndependenceTestInstance : public BaseTestInstance
808 {
809 public:
810                                                 OuterEdgeIndexIndependenceTestInstance  (Context& context, const CaseDefinition caseDef) : BaseTestInstance (context, caseDef, 1) {}
811         tcu::TestStatus         iterate                                                                 (void);
812 };
813
814 tcu::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  *//*--------------------------------------------------------------------*/
905 class SymmetricOuterEdgeTestInstance : public BaseTestInstance
906 {
907 public:
908                                                 SymmetricOuterEdgeTestInstance  (Context& context, const CaseDefinition caseDef) : BaseTestInstance (context, caseDef, 1) {}
909         tcu::TestStatus         iterate                                                 (void);
910 };
911
912 tcu::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
1006 class OuterEdgeDivisionTest : public TestCase
1007 {
1008 public:
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
1025 private:
1026         const CaseDefinition m_caseDef;
1027 };
1028
1029 class OuterEdgeIndexIndependenceTest : public TestCase
1030 {
1031 public:
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
1049 private:
1050         const CaseDefinition m_caseDef;
1051 };
1052
1053 class SymmetricOuterEdgeTest : public TestCase
1054 {
1055 public:
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
1073 private:
1074         const CaseDefinition m_caseDef;
1075 };
1076
1077 tcu::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
1083 tcu::TestCase* makeOuterEdgeIndexIndependenceTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode)
1084 {
1085         const CaseDefinition caseDef = { primitiveType, spacingMode, winding, usePointMode };
1086         return new OuterEdgeIndexIndependenceTest(testCtx, name, description, caseDef);
1087 }
1088
1089 tcu::TestCase* makeSymmetricOuterEdgeTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode)
1090 {
1091         const CaseDefinition caseDef = { primitiveType, spacingMode, winding, usePointMode };
1092         return new SymmetricOuterEdgeTest(testCtx, name, description, caseDef);
1093 }
1094
1095 } // InvariantOuterEdge ns
1096
1097 namespace PrimitiveSetInvariance
1098 {
1099
1100 enum 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
1108 struct CaseDefinition
1109 {
1110         CaseType                                caseType;
1111         TessPrimitiveType               primitiveType;
1112         SpacingMode                             spacingMode;
1113         WindingUsage                    windingUsage;
1114         bool                                    usePointMode;
1115 };
1116
1117 struct 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
1126 typedef tcu::Vector<tcu::Vec3, 3> Triangle;
1127
1128 inline 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.
1136 template <typename IsTriangleRelevantT>
1137 bool 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
1198 template <typename ArgT, bool res>
1199 struct ConstantUnaryPredicate
1200 {
1201         bool operator() (const ArgT&) const { return res; }
1202 };
1203
1204 bool 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.
1211 bool 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  *//*--------------------------------------------------------------------*/
1254 class InvarianceTestCase : public TestCase
1255 {
1256 public:
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
1266 private:
1267         const CaseDefinition                    m_caseDef;
1268 };
1269
1270 void InvarianceTestCase::initPrograms (SourceCollections& programCollection) const
1271 {
1272         addDefaultPrograms(programCollection, m_caseDef.primitiveType, m_caseDef.spacingMode, m_caseDef.windingUsage, getPointModeUsage(m_caseDef.usePointMode));
1273 }
1274
1275 class InvarianceTestInstance : public TestInstance
1276 {
1277 public:
1278                                                                         InvarianceTestInstance          (Context& context, const CaseDefinition& caseDef) : TestInstance(context), m_caseDef(caseDef) {}
1279         virtual                                                 ~InvarianceTestInstance         (void) {}
1280
1281         tcu::TestStatus                                 iterate                                         (void);
1282
1283 protected:
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
1290 std::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
1325 tcu::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 = 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
1475                                         tcu::TestLog& log = m_context.getTestContext().getLog();
1476
1477                                         if (numPrimitives != refNumPrimitives)
1478                                         {
1479                                                 log << tcu::TestLog::Message << "Failure: got " << numPrimitives << " primitives, but expected " << refNumPrimitives << tcu::TestLog::EndMessage;
1480
1481                                                 return tcu::TestStatus::fail("Invalid set of primitives");
1482                                         }
1483
1484                                         const int                                       half  = static_cast<int>(primitives.size() / 2);
1485                                         const PerPrimitiveVec           prim0 = PerPrimitiveVec(primitives.begin(), primitives.begin() + half);
1486                                         const PerPrimitive* const       prim1 = &primitives[half];
1487
1488                                         if (!comparePrimitivesExact(&prim0[0], prim1, half))
1489                                         {
1490                                                         log << tcu::TestLog::Message << "Failure: tessellation coordinates differ between two primitives drawn in one draw call" << tcu::TestLog::EndMessage
1491                                                                 << tcu::TestLog::Message << "Note: tessellation levels for both primitives were: " << getTessellationLevelsString(tessLevels, m_caseDef.primitiveType) << tcu::TestLog::EndMessage;
1492
1493                                                         return tcu::TestStatus::fail("Invalid set of primitives");
1494                                         }
1495
1496                                         if (programNdx == 0 && subTessLevelCaseNdx == 0)
1497                                                 firstPrim = prim0;
1498                                         else
1499                                         {
1500                                                 const bool compareOk = compare(firstPrim, prim0, levelCase.mem);
1501                                                 if (!compareOk)
1502                                                 {
1503                                                         log << tcu::TestLog::Message
1504                                                                 << "Note: comparison of tessellation coordinates failed; comparison was made between following cases:\n"
1505                                                                 << "  - case A: program 0, tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx].levels[0], m_caseDef.primitiveType) << "\n"
1506                                                                 << "  - case B: program " << programNdx << ", tessellation levels: " << getTessellationLevelsString(tessLevels, m_caseDef.primitiveType)
1507                                                                 << tcu::TestLog::EndMessage;
1508
1509                                                         return tcu::TestStatus::fail("Invalid set of primitives");
1510                                                 }
1511                                         }
1512                                 }
1513                                 ++programNdx;
1514                         }
1515                 }
1516         }
1517         return tcu::TestStatus::pass("OK");
1518 }
1519
1520 /*--------------------------------------------------------------------*//*!
1521  * \brief Test invariance rule #1
1522  *
1523  * Test that the sequence of primitives input to the TES only depends on
1524  * the tessellation levels, tessellation mode, spacing mode, winding, and
1525  * point mode.
1526  *//*--------------------------------------------------------------------*/
1527 class InvariantPrimitiveSetTestInstance : public InvarianceTestInstance
1528 {
1529 public:
1530         InvariantPrimitiveSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {}
1531
1532 protected:
1533         bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int) const
1534         {
1535                 if (!comparePrimitivesExact(&primitivesA[0], &primitivesB[0], static_cast<int>(primitivesA.size())))
1536                 {
1537                         m_context.getTestContext().getLog()
1538                                 << tcu::TestLog::Message << "Failure: tessellation coordinates differ between two programs" << tcu::TestLog::EndMessage;
1539
1540                         return false;
1541                 }
1542                 return true;
1543         }
1544 };
1545
1546 /*--------------------------------------------------------------------*//*!
1547  * \brief Test invariance rule #5
1548  *
1549  * Test that the set of triangles input to the TES only depends on the
1550  * tessellation levels, tessellation mode and spacing mode. Specifically,
1551  * winding doesn't change the set of triangles, though it can change the
1552  * order in which they are input to TES, and can (and will) change the
1553  * vertex order within a triangle.
1554  *//*--------------------------------------------------------------------*/
1555 class InvariantTriangleSetTestInstance : public InvarianceTestInstance
1556 {
1557 public:
1558         InvariantTriangleSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {}
1559
1560 protected:
1561         bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int) const
1562         {
1563                 return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog());
1564         }
1565 };
1566
1567 /*--------------------------------------------------------------------*//*!
1568  * \brief Test invariance rule #6
1569  *
1570  * Test that the set of inner triangles input to the TES only depends on
1571  * the inner tessellation levels, tessellation mode and spacing mode.
1572  *//*--------------------------------------------------------------------*/
1573 class InvariantInnerTriangleSetTestInstance : public InvarianceTestInstance
1574 {
1575 public:
1576         InvariantInnerTriangleSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {}
1577
1578 protected:
1579         std::vector<LevelCase> genTessLevelCases (void) const
1580         {
1581                 const int                                               numSubCases             = 4;
1582                 const std::vector<LevelCase>    baseResults             = InvarianceTestInstance::genTessLevelCases();
1583                 std::vector<LevelCase>                  result;
1584                 de::Random                                              rnd                             (123);
1585
1586                 // Generate variants with different values for irrelevant levels.
1587                 for (int baseNdx = 0; baseNdx < static_cast<int>(baseResults.size()); ++baseNdx)
1588                 {
1589                         const TessLevels&       base    = baseResults[baseNdx].levels[0];
1590                         TessLevels                      levels  = base;
1591                         LevelCase                       levelCase;
1592
1593                         for (int subNdx = 0; subNdx < numSubCases; ++subNdx)
1594                         {
1595                                 levelCase.levels.push_back(levels);
1596
1597                                 for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); ++i)
1598                                         levels.outer[i] = rnd.getFloat(2.0f, 16.0f);
1599                                 if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
1600                                         levels.inner[1] = rnd.getFloat(2.0f, 16.0f);
1601                         }
1602
1603                         result.push_back(levelCase);
1604                 }
1605
1606                 return result;
1607         }
1608
1609         struct IsInnerTriangleTriangle
1610         {
1611                 bool operator() (const tcu::Vec3* vertices) const
1612                 {
1613                         for (int v = 0; v < 3; ++v)
1614                                 for (int c = 0; c < 3; ++c)
1615                                         if (vertices[v][c] == 0.0f)
1616                                                 return false;
1617                         return true;
1618                 }
1619         };
1620
1621         struct IsInnerQuadTriangle
1622         {
1623                 bool operator() (const tcu::Vec3* vertices) const
1624                 {
1625                         for (int v = 0; v < 3; ++v)
1626                                 for (int c = 0; c < 2; ++c)
1627                                         if (vertices[v][c] == 0.0f || vertices[v][c] == 1.0f)
1628                                                 return false;
1629                         return true;
1630                 }
1631         };
1632
1633         bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int) const
1634         {
1635                 if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
1636                         return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(), IsInnerTriangleTriangle(), "outer triangles");
1637                 else if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
1638                         return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(), IsInnerQuadTriangle(), "outer triangles");
1639                 else
1640                 {
1641                         DE_ASSERT(false);
1642                         return false;
1643                 }
1644         }
1645 };
1646
1647 /*--------------------------------------------------------------------*//*!
1648  * \brief Test invariance rule #7
1649  *
1650  * Test that the set of outer triangles input to the TES only depends on
1651  * tessellation mode, spacing mode and the inner and outer tessellation
1652  * levels corresponding to the inner and outer edges relevant to that
1653  * triangle.
1654  *//*--------------------------------------------------------------------*/
1655 class InvariantOuterTriangleSetTestInstance : public InvarianceTestInstance
1656 {
1657 public:
1658         InvariantOuterTriangleSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {}
1659
1660 protected:
1661         std::vector<LevelCase> genTessLevelCases (void) const
1662         {
1663                 const int                                               numSubCasesPerEdge      = 4;
1664                 const int                                               numEdges                        = m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES        ? 3
1665                                                                                                                         : m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS            ? 4 : 0;
1666                 const std::vector<LevelCase>    baseResult                      = InvarianceTestInstance::genTessLevelCases();
1667                 std::vector<LevelCase>                  result;
1668                 de::Random                                              rnd                                     (123);
1669
1670                 // Generate variants with different values for irrelevant levels.
1671                 for (int baseNdx = 0; baseNdx < static_cast<int>(baseResult.size()); ++baseNdx)
1672                 {
1673                         const TessLevels& base = baseResult[baseNdx].levels[0];
1674                         if (base.inner[0] == 1.0f || (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS && base.inner[1] == 1.0f))
1675                                 continue;
1676
1677                         for (int edgeNdx = 0; edgeNdx < numEdges; ++edgeNdx)
1678                         {
1679                                 TessLevels      levels = base;
1680                                 LevelCase       levelCase;
1681                                 levelCase.mem = edgeNdx;
1682
1683                                 for (int subCaseNdx = 0; subCaseNdx < numSubCasesPerEdge; ++subCaseNdx)
1684                                 {
1685                                         levelCase.levels.push_back(levels);
1686
1687                                         for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); ++i)
1688                                         {
1689                                                 if (i != edgeNdx)
1690                                                         levels.outer[i] = rnd.getFloat(2.0f, 16.0f);
1691                                         }
1692
1693                                         if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
1694                                                 levels.inner[1] = rnd.getFloat(2.0f, 16.0f);
1695                                 }
1696
1697                                 result.push_back(levelCase);
1698                         }
1699                 }
1700
1701                 return result;
1702         }
1703
1704         class IsTriangleTriangleOnOuterEdge
1705         {
1706         public:
1707                 IsTriangleTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
1708                 bool operator() (const tcu::Vec3* vertices) const
1709                 {
1710                         bool touchesAppropriateEdge = false;
1711                         for (int v = 0; v < 3; ++v)
1712                                 if (vertices[v][m_edgeNdx] == 0.0f)
1713                                         touchesAppropriateEdge = true;
1714
1715                         if (touchesAppropriateEdge)
1716                         {
1717                                 const tcu::Vec3 avg = (vertices[0] + vertices[1] + vertices[2]) / 3.0f;
1718                                 return avg[m_edgeNdx] < avg[(m_edgeNdx+1)%3] &&
1719                                            avg[m_edgeNdx] < avg[(m_edgeNdx+2)%3];
1720                         }
1721                         return false;
1722                 }
1723
1724         private:
1725                 const int m_edgeNdx;
1726         };
1727
1728         class IsQuadTriangleOnOuterEdge
1729         {
1730         public:
1731                 IsQuadTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
1732
1733                 bool onEdge (const tcu::Vec3& v) const
1734                 {
1735                         return v[m_edgeNdx%2] == (m_edgeNdx <= 1 ? 0.0f : 1.0f);
1736                 }
1737
1738                 static inline bool onAnyEdge (const tcu::Vec3& v)
1739                 {
1740                         return v[0] == 0.0f || v[0] == 1.0f || v[1] == 0.0f || v[1] == 1.0f;
1741                 }
1742
1743                 bool operator() (const tcu::Vec3* vertices) const
1744                 {
1745                         for (int v = 0; v < 3; ++v)
1746                         {
1747                                 const tcu::Vec3& a = vertices[v];
1748                                 const tcu::Vec3& b = vertices[(v+1)%3];
1749                                 const tcu::Vec3& c = vertices[(v+2)%3];
1750                                 if (onEdge(a) && onEdge(b))
1751                                         return true;
1752                                 if (onEdge(c) && !onAnyEdge(a) && !onAnyEdge(b) && a[m_edgeNdx%2] == b[m_edgeNdx%2])
1753                                         return true;
1754                         }
1755
1756                         return false;
1757                 }
1758
1759         private:
1760                 const int m_edgeNdx;
1761         };
1762
1763         bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int outerEdgeNdx) const
1764         {
1765                 if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
1766                 {
1767                         return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(),
1768                                                                            IsTriangleTriangleOnOuterEdge(outerEdgeNdx),
1769                                                                            ("inner triangles, and outer triangles corresponding to other edge than edge "
1770                                                                                 + outerEdgeDescriptions(m_caseDef.primitiveType)[outerEdgeNdx].description()).c_str());
1771                 }
1772                 else if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
1773                 {
1774                         return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(),
1775                                                                            IsQuadTriangleOnOuterEdge(outerEdgeNdx),
1776                                                                            ("inner triangles, and outer triangles corresponding to other edge than edge "
1777                                                                                 + outerEdgeDescriptions(m_caseDef.primitiveType)[outerEdgeNdx].description()).c_str());
1778                 }
1779                 else
1780                         DE_ASSERT(false);
1781
1782                 return true;
1783         }
1784 };
1785
1786 TestInstance* InvarianceTestCase::createInstance (Context& context) const
1787 {
1788         switch (m_caseDef.caseType)
1789         {
1790                 case CASETYPE_INVARIANT_PRIMITIVE_SET:                  return new InvariantPrimitiveSetTestInstance    (context, m_caseDef);
1791                 case CASETYPE_INVARIANT_TRIANGLE_SET:                   return new InvariantTriangleSetTestInstance     (context, m_caseDef);
1792                 case CASETYPE_INVARIANT_OUTER_TRIANGLE_SET:             return new InvariantOuterTriangleSetTestInstance(context, m_caseDef);
1793                 case CASETYPE_INVARIANT_INNER_TRIANGLE_SET:             return new InvariantInnerTriangleSetTestInstance(context, m_caseDef);
1794                 default:
1795                         DE_ASSERT(false);
1796                         return DE_NULL;
1797         }
1798 }
1799
1800 TestCase* makeInvariantPrimitiveSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode)
1801 {
1802         const CaseDefinition caseDef = { CASETYPE_INVARIANT_PRIMITIVE_SET, primitiveType, spacingMode, getWindingUsage(winding), usePointMode };
1803         return new InvarianceTestCase(testCtx, name, description, caseDef);
1804 }
1805
1806 TestCase* makeInvariantTriangleSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
1807 {
1808         DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
1809         const CaseDefinition caseDef = { CASETYPE_INVARIANT_TRIANGLE_SET, primitiveType, spacingMode, WINDING_USAGE_VARY, false };
1810         return new InvarianceTestCase(testCtx, name, description, caseDef);
1811 }
1812
1813 TestCase* makeInvariantInnerTriangleSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
1814 {
1815         DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
1816         const CaseDefinition caseDef = { CASETYPE_INVARIANT_INNER_TRIANGLE_SET, primitiveType, spacingMode, WINDING_USAGE_VARY, false };
1817         return new InvarianceTestCase(testCtx, name, description, caseDef);
1818 }
1819
1820 TestCase* makeInvariantOuterTriangleSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
1821 {
1822         DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
1823         const CaseDefinition caseDef = { CASETYPE_INVARIANT_OUTER_TRIANGLE_SET, primitiveType, spacingMode, WINDING_USAGE_VARY, false };
1824         return new InvarianceTestCase(testCtx, name, description, caseDef);
1825 }
1826
1827 } // PrimitiveSetInvariance ns
1828
1829 namespace TessCoordComponent
1830 {
1831
1832 enum CaseType
1833 {
1834         CASETYPE_TESS_COORD_RANGE = 0,          //!< Test that all (relevant) components of tess coord are in [0,1].
1835         CASETYPE_ONE_MINUS_TESS_COORD,          //!< Test that for every (relevant) component c of a tess coord, 1.0-c is exact.
1836
1837         CASETYPE_LAST
1838 };
1839
1840 struct CaseDefinition
1841 {
1842         CaseType                        caseType;
1843         TessPrimitiveType       primitiveType;
1844         SpacingMode                     spacingMode;
1845         Winding                         winding;
1846         bool                            usePointMode;
1847 };
1848
1849 std::vector<TessLevels> genTessLevelCases (const int numCases)
1850 {
1851         de::Random                              rnd(123);
1852         std::vector<TessLevels> result;
1853
1854         for (int i = 0; i < numCases; ++i)
1855         {
1856                 TessLevels levels;
1857                 levels.inner[0] = rnd.getFloat(1.0f, 63.0f);
1858                 levels.inner[1] = rnd.getFloat(1.0f, 63.0f);
1859                 levels.outer[0] = rnd.getFloat(1.0f, 63.0f);
1860                 levels.outer[1] = rnd.getFloat(1.0f, 63.0f);
1861                 levels.outer[2] = rnd.getFloat(1.0f, 63.0f);
1862                 levels.outer[3] = rnd.getFloat(1.0f, 63.0f);
1863                 result.push_back(levels);
1864         }
1865
1866         return result;
1867 }
1868
1869 typedef bool (*CompareFunc)(tcu::TestLog& log, const float value);
1870
1871 bool compareTessCoordRange (tcu::TestLog& log, const float value)
1872 {
1873         if (!de::inRange(value, 0.0f, 1.0f))
1874         {
1875                 log << tcu::TestLog::Message << "Failure: tess coord component isn't in range [0,1]" << tcu::TestLog::EndMessage;
1876                 return false;
1877         }
1878         return true;
1879 }
1880
1881 bool compareOneMinusTessCoord (tcu::TestLog& log, const float value)
1882 {
1883         if (value != 1.0f)
1884         {
1885                 log << tcu::TestLog::Message << "Failure: comp + (1.0-comp) doesn't equal 1.0 for some component of tessellation coordinate" << tcu::TestLog::EndMessage;
1886                 return false;
1887         }
1888         return true;
1889 }
1890
1891 void initPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
1892 {
1893         // Vertex shader
1894         {
1895                 std::ostringstream src;
1896                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
1897                         << "\n"
1898                         << "layout(location = 0) in  highp float in_v_attr;\n"
1899                         << "layout(location = 0) out highp float in_tc_attr;\n"
1900                         << "\n"
1901                         << "void main (void)\n"
1902                         << "{\n"
1903                         << "    in_tc_attr = in_v_attr;\n"
1904                         << "}\n";
1905
1906                 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
1907         }
1908
1909         // Tessellation control shader
1910         {
1911                 std::ostringstream src;
1912                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
1913                         << "#extension GL_EXT_tessellation_shader : require\n"
1914                         << "\n"
1915                         << "layout(vertices = 1) out;\n"
1916                         << "\n"
1917                         << "layout(location = 0) in highp float in_tc_attr[];\n"
1918                         << "\n"
1919                         << "void main (void)\n"
1920                         << "{\n"
1921                         << "    gl_TessLevelInner[0] = in_tc_attr[0];\n"
1922                         << "    gl_TessLevelInner[1] = in_tc_attr[1];\n"
1923                         << "\n"
1924                         << "    gl_TessLevelOuter[0] = in_tc_attr[2];\n"
1925                         << "    gl_TessLevelOuter[1] = in_tc_attr[3];\n"
1926                         << "    gl_TessLevelOuter[2] = in_tc_attr[4];\n"
1927                         << "    gl_TessLevelOuter[3] = in_tc_attr[5];\n"
1928                         << "}\n";
1929
1930                 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
1931         }
1932
1933         // Tessellation evaluation shader
1934         {
1935                 std::ostringstream tessCoordSrc;
1936
1937                 if (caseDef.caseType == CASETYPE_TESS_COORD_RANGE)
1938                         tessCoordSrc << "    sb_out.tessCoord[index] = gl_TessCoord;\n";
1939                 else if (caseDef.caseType == CASETYPE_ONE_MINUS_TESS_COORD)
1940                 {
1941                         const char* components[]  = { "x" , "y", "z" };
1942                         const int   numComponents = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2);
1943
1944                         for (int i = 0; i < numComponents; ++i)
1945                                 tessCoordSrc << "    {\n"
1946                                                          << "        float oneMinusComp        = 1.0 - gl_TessCoord." << components[i] << ";\n"
1947                                                          << "        sb_out.tessCoord[index]." << components[i] << " = gl_TessCoord." << components[i] << " + oneMinusComp;\n"
1948                                                          << "    }\n";
1949                 }
1950                 else
1951                 {
1952                         DE_ASSERT(false);
1953                         return;
1954                 }
1955
1956                 std::ostringstream src;
1957                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
1958                         << "#extension GL_EXT_tessellation_shader : require\n"
1959                         << "\n"
1960                         << "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", "
1961                                                  << getSpacingModeShaderName(caseDef.spacingMode) << ", "
1962                                                  << getWindingShaderName(caseDef.winding)
1963                                                  << (caseDef.usePointMode ? ", point_mode" : "") << ") in;\n"
1964                         << "\n"
1965                         << "layout(set = 0, binding = 0, std430) coherent restrict buffer Output {\n"
1966                         << "    int  numInvocations;\n"
1967                         << "    vec3 tessCoord[];\n"
1968                         << "} sb_out;\n"
1969                         << "\n"
1970                         << "void main (void)\n"
1971                         << "{\n"
1972                         << "    int index = atomicAdd(sb_out.numInvocations, 1);\n"
1973                         << tessCoordSrc.str()
1974                         << "}\n";
1975
1976                 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
1977         }
1978 }
1979
1980 tcu::TestStatus test (Context& context, const CaseDefinition caseDef)
1981 {
1982         requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
1983
1984         const DeviceInterface&  vk                                      = context.getDeviceInterface();
1985         const VkDevice                  device                          = context.getDevice();
1986         const VkQueue                   queue                           = context.getUniversalQueue();
1987         const deUint32                  queueFamilyIndex        = context.getUniversalQueueFamilyIndex();
1988         Allocator&                              allocator                       = context.getDefaultAllocator();
1989
1990         const int                                               numTessLevelCases       = 32;
1991         const std::vector<TessLevels>   tessLevelCases          = genTessLevelCases(numTessLevelCases);
1992
1993         int maxNumVerticesInDrawCall = 0;
1994         for (int i = 0; i < numTessLevelCases; ++i)
1995                 maxNumVerticesInDrawCall = de::max(maxNumVerticesInDrawCall, referenceVertexCount(caseDef.primitiveType, caseDef.spacingMode, caseDef.usePointMode,
1996                                                                                    &tessLevelCases[i].inner[0], &tessLevelCases[i].outer[0]));
1997
1998         // We may get more invocations than expected, so add some more space (arbitrary number).
1999         maxNumVerticesInDrawCall += 4;
2000
2001         // Vertex input attributes buffer: to pass tessellation levels
2002
2003         const VkFormat          vertexFormat        = VK_FORMAT_R32_SFLOAT;
2004         const deUint32          vertexStride        = tcu::getPixelSize(mapVkFormat(vertexFormat));
2005         const VkDeviceSize      vertexDataSizeBytes = NUM_TESS_LEVELS * vertexStride;
2006         const Buffer            vertexBuffer        (vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
2007
2008         DE_ASSERT(vertexDataSizeBytes == sizeof(TessLevels));
2009
2010         // Output buffer: number of invocations and array of tess coords
2011
2012         const int                  resultBufferTessCoordsOffset = sizeof(deInt32) * 4;
2013         const VkDeviceSize resultBufferSizeBytes        = resultBufferTessCoordsOffset + maxNumVerticesInDrawCall * sizeof(tcu::Vec4);
2014         const Buffer       resultBuffer                 (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
2015
2016         // Descriptors
2017
2018         const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
2019                 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
2020                 .build(vk, device));
2021
2022         const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
2023                 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
2024                 .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
2025
2026         const Unique<VkDescriptorSet> descriptorSet    (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
2027         const VkDescriptorBufferInfo  resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
2028
2029         DescriptorSetUpdateBuilder()
2030                 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
2031                 .update(vk, device);
2032
2033         const Unique<VkRenderPass>     renderPass    (makeRenderPassWithoutAttachments (vk, device));
2034         const Unique<VkFramebuffer>    framebuffer   (makeFramebufferWithoutAttachments(vk, device, *renderPass));
2035         const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout               (vk, device, *descriptorSetLayout));
2036         const Unique<VkCommandPool>    cmdPool       (makeCommandPool                  (vk, device, queueFamilyIndex));
2037         const Unique<VkCommandBuffer>  cmdBuffer     (makeCommandBuffer                (vk, device, *cmdPool));
2038
2039         const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
2040                 .setPatchControlPoints        (NUM_TESS_LEVELS)
2041                 .setVertexInputSingleAttribute(vertexFormat, vertexStride)
2042                 .setShader                    (vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                  context.getBinaryCollection().get("vert"), DE_NULL)
2043                 .setShader                    (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,    context.getBinaryCollection().get("tesc"), DE_NULL)
2044                 .setShader                    (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"), DE_NULL)
2045                 .build                        (vk, device, *pipelineLayout, *renderPass));
2046
2047         for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < numTessLevelCases; ++tessLevelCaseNdx)
2048         {
2049                 context.getTestContext().getLog()
2050                         << tcu::TestLog::Message
2051                         << "Testing with tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx], caseDef.primitiveType)
2052                         << tcu::TestLog::EndMessage;
2053
2054                 {
2055                         const Allocation& alloc = vertexBuffer.getAllocation();
2056                         deMemcpy(alloc.getHostPtr(), &tessLevelCases[tessLevelCaseNdx], sizeof(TessLevels));
2057                         flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(TessLevels));
2058                 }
2059                 {
2060                         const Allocation& alloc = resultBuffer.getAllocation();
2061                         deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
2062                         flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
2063                 }
2064
2065                 beginCommandBuffer(vk, *cmdBuffer);
2066                 beginRenderPassWithRasterizationDisabled(vk, *cmdBuffer, *renderPass, *framebuffer);
2067
2068                 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
2069                 vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
2070                 {
2071                         const VkDeviceSize vertexBufferOffset = 0ull;
2072                         vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
2073                 }
2074
2075                 vk.cmdDraw(*cmdBuffer, NUM_TESS_LEVELS, 1u, 0u, 0u);
2076                 endRenderPass(vk, *cmdBuffer);
2077
2078                 {
2079                         const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
2080                                 VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes);
2081
2082                         vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
2083                                 0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
2084                 }
2085
2086                 endCommandBuffer(vk, *cmdBuffer);
2087                 submitCommandsAndWait(vk, device, queue, *cmdBuffer);
2088
2089                 // Verify case result
2090                 {
2091                         const Allocation& resultAlloc = resultBuffer.getAllocation();
2092                         invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
2093
2094                         const deInt32                            numVertices = *static_cast<deInt32*>(resultAlloc.getHostPtr());
2095                         const std::vector<tcu::Vec3> vertices    = readInterleavedData<tcu::Vec3>(numVertices, resultAlloc.getHostPtr(), resultBufferTessCoordsOffset, sizeof(tcu::Vec4));
2096
2097                         // If this fails then we didn't read all vertices from shader and test must be changed to allow more.
2098                         DE_ASSERT(numVertices <= maxNumVerticesInDrawCall);
2099
2100                         tcu::TestLog& log           = context.getTestContext().getLog();
2101                         const int     numComponents = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2);
2102
2103                         CompareFunc compare = (caseDef.caseType == CASETYPE_TESS_COORD_RANGE     ? compareTessCoordRange :
2104                                                                    caseDef.caseType == CASETYPE_ONE_MINUS_TESS_COORD ? compareOneMinusTessCoord : DE_NULL);
2105
2106                         DE_ASSERT(compare != DE_NULL);
2107
2108                         for (std::vector<tcu::Vec3>::const_iterator vertexIter = vertices.begin(); vertexIter != vertices.end(); ++vertexIter)
2109                         for (int i = 0; i < numComponents; ++i)
2110                                 if (!compare(log, (*vertexIter)[i]))
2111                                 {
2112                                                 log << tcu::TestLog::Message << "Note: got a wrong tessellation coordinate "
2113                                                         << (numComponents == 3 ? de::toString(*vertexIter) : de::toString(vertexIter->swizzle(0,1))) << tcu::TestLog::EndMessage;
2114
2115                                                 tcu::TestStatus::fail("Invalid tessellation coordinate component");
2116                                 }
2117                 }
2118         }
2119         return tcu::TestStatus::pass("OK");
2120 }
2121
2122 tcu::TestCase* makeTessCoordRangeTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode)
2123 {
2124         const CaseDefinition caseDef = { CASETYPE_TESS_COORD_RANGE, primitiveType, spacingMode, winding, usePointMode };
2125         return createFunctionCaseWithPrograms(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, description, initPrograms, test, caseDef);
2126 }
2127
2128 tcu::TestCase* makeOneMinusTessCoordTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode)
2129 {
2130         const CaseDefinition caseDef = { CASETYPE_ONE_MINUS_TESS_COORD, primitiveType, spacingMode, winding, usePointMode };
2131         return createFunctionCaseWithPrograms(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, description, initPrograms, test, caseDef);
2132 }
2133
2134 } // TessCoordComponent ns
2135
2136 } // anonymous
2137
2138 //! These tests correspond to dEQP-GLES31.functional.tessellation.invariance.*
2139 //! Original OpenGL ES tests used transform feedback to get vertices in primitive order. To emulate this behavior we have to use geometry shader,
2140 //! which allows us to intercept verticess of final output primitives. This can't be done with tessellation shaders alone as number and order of
2141 //! invocation is undefined.
2142 tcu::TestCaseGroup* createInvarianceTests (tcu::TestContext& testCtx)
2143 {
2144         de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "invariance", "Test tessellation invariance rules"));
2145
2146         de::MovePtr<tcu::TestCaseGroup> invariantPrimitiveSetGroup              (new tcu::TestCaseGroup(testCtx, "primitive_set",                                       "Test invariance rule #1"));
2147         de::MovePtr<tcu::TestCaseGroup> invariantOuterEdgeGroup                                 (new tcu::TestCaseGroup(testCtx, "outer_edge_division",                         "Test invariance rule #2"));
2148         de::MovePtr<tcu::TestCaseGroup> symmetricOuterEdgeGroup                                 (new tcu::TestCaseGroup(testCtx, "outer_edge_symmetry",                         "Test invariance rule #3"));
2149         de::MovePtr<tcu::TestCaseGroup> outerEdgeVertexSetIndexIndependenceGroup(new tcu::TestCaseGroup(testCtx, "outer_edge_index_independence",       "Test invariance rule #4"));
2150         de::MovePtr<tcu::TestCaseGroup> invariantTriangleSetGroup                               (new tcu::TestCaseGroup(testCtx, "triangle_set",                                        "Test invariance rule #5"));
2151         de::MovePtr<tcu::TestCaseGroup> invariantInnerTriangleSetGroup                  (new tcu::TestCaseGroup(testCtx, "inner_triangle_set",                          "Test invariance rule #6"));
2152         de::MovePtr<tcu::TestCaseGroup> invariantOuterTriangleSetGroup                  (new tcu::TestCaseGroup(testCtx, "outer_triangle_set",                          "Test invariance rule #7"));
2153         de::MovePtr<tcu::TestCaseGroup> tessCoordComponentRangeGroup                    (new tcu::TestCaseGroup(testCtx, "tess_coord_component_range",          "Test invariance rule #8, first part"));
2154         de::MovePtr<tcu::TestCaseGroup> oneMinusTessCoordComponentGroup                 (new tcu::TestCaseGroup(testCtx, "one_minus_tess_coord_component",      "Test invariance rule #8, second part"));
2155
2156         for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; ++primitiveTypeNdx)
2157         for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
2158         {
2159                 const TessPrimitiveType primitiveType = static_cast<TessPrimitiveType>(primitiveTypeNdx);
2160                 const SpacingMode       spacingMode   = static_cast<SpacingMode>(spacingModeNdx);
2161                 const bool              triOrQuad     = primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS;
2162                 const std::string       primName      = getTessPrimitiveTypeShaderName(primitiveType);
2163                 const std::string       primSpacName  = primName + "_" + getSpacingModeShaderName(spacingMode);
2164
2165                 if (triOrQuad)
2166                 {
2167                         invariantOuterEdgeGroup->addChild       (    InvariantOuterEdge::makeOuterEdgeDivisionTest        (testCtx, primSpacName, "", primitiveType, spacingMode));
2168                         invariantTriangleSetGroup->addChild     (PrimitiveSetInvariance::makeInvariantTriangleSetTest     (testCtx, primSpacName, "", primitiveType, spacingMode));
2169                         invariantInnerTriangleSetGroup->addChild(PrimitiveSetInvariance::makeInvariantInnerTriangleSetTest(testCtx, primSpacName, "", primitiveType, spacingMode));
2170                         invariantOuterTriangleSetGroup->addChild(PrimitiveSetInvariance::makeInvariantOuterTriangleSetTest(testCtx, primSpacName, "", primitiveType, spacingMode));
2171                 }
2172
2173                 for (int windingNdx = 0; windingNdx < WINDING_LAST; ++windingNdx)
2174                 for (int usePointModeNdx = 0; usePointModeNdx <= 1; ++usePointModeNdx)
2175                 {
2176                         const Winding     winding               = static_cast<Winding>(windingNdx);
2177                         const bool        usePointMode          = (usePointModeNdx != 0);
2178                         const std::string primSpacWindPointName = primSpacName + "_" + getWindingShaderName(winding) + (usePointMode ? "_point_mode" : "");
2179
2180                         invariantPrimitiveSetGroup->addChild     (PrimitiveSetInvariance::makeInvariantPrimitiveSetTest(testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
2181                         tessCoordComponentRangeGroup->addChild   (    TessCoordComponent::makeTessCoordRangeTest       (testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
2182                         oneMinusTessCoordComponentGroup->addChild(    TessCoordComponent::makeOneMinusTessCoordTest    (testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
2183                         symmetricOuterEdgeGroup->addChild        (    InvariantOuterEdge::makeSymmetricOuterEdgeTest   (testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
2184
2185                         if (triOrQuad)
2186                                 outerEdgeVertexSetIndexIndependenceGroup->addChild(InvariantOuterEdge::makeOuterEdgeIndexIndependenceTest(testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
2187                 }
2188         }
2189
2190         group->addChild(invariantPrimitiveSetGroup.release());
2191         group->addChild(invariantOuterEdgeGroup.release());
2192         group->addChild(symmetricOuterEdgeGroup.release());
2193         group->addChild(outerEdgeVertexSetIndexIndependenceGroup.release());
2194         group->addChild(invariantTriangleSetGroup.release());
2195         group->addChild(invariantInnerTriangleSetGroup.release());
2196         group->addChild(invariantOuterTriangleSetGroup.release());
2197         group->addChild(tessCoordComponentRangeGroup.release());
2198         group->addChild(oneMinusTessCoordComponentGroup.release());
2199
2200         return group.release();
2201 }
2202
2203 } // tessellation
2204 } // vkt