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