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