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