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