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