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