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>
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::__anon64b6dc450111::OuterEdgeDescription412 	OuterEdgeDescription (const int i, const float c0)					: constantCoordinateIndex(i), numConstantCoordinateValueChoices(1) { constantCoordinateValueChoices[0] = c0; }
OuterEdgeDescriptionvkt::tessellation::__anon64b6dc450111::OuterEdgeDescription413 	OuterEdgeDescription (const int i, const float c0, const float c1)	: constantCoordinateIndex(i), numConstantCoordinateValueChoices(2) { constantCoordinateValueChoices[0] = c0; constantCoordinateValueChoices[1] = c1; }
414 
descriptionvkt::tessellation::__anon64b6dc450111::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::__anon64b6dc450111::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 
1030 private:
1031 	const CaseDefinition m_caseDef;
1032 };
1033 
1034 class OuterEdgeIndexIndependenceTest : public TestCase
1035 {
1036 public:
OuterEdgeIndexIndependenceTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const CaseDefinition caseDef)1037 	OuterEdgeIndexIndependenceTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef)
1038 		: TestCase	(testCtx, name, description)
1039 		, m_caseDef	(caseDef)
1040 	{
1041 		DE_ASSERT(m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS);
1042 	}
1043 
initPrograms(vk::SourceCollections & programCollection) const1044 	void initPrograms (vk::SourceCollections& programCollection) const
1045 	{
1046 		addDefaultPrograms(programCollection, m_caseDef.primitiveType, m_caseDef.spacingMode, getWindingUsage(m_caseDef.winding), getPointModeUsage(m_caseDef.usePointMode));
1047 	}
1048 
createInstance(Context & context) const1049 	TestInstance* createInstance (Context& context) const
1050 	{
1051 		return new OuterEdgeIndexIndependenceTestInstance(context, m_caseDef);
1052 	};
1053 
1054 private:
1055 	const CaseDefinition m_caseDef;
1056 };
1057 
1058 class SymmetricOuterEdgeTest : public TestCase
1059 {
1060 public:
SymmetricOuterEdgeTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const CaseDefinition caseDef)1061 	SymmetricOuterEdgeTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef)
1062 		: TestCase	(testCtx, name, description)
1063 		, m_caseDef	(caseDef)
1064 	{
1065 	}
1066 
initPrograms(vk::SourceCollections & programCollection) const1067 	void initPrograms (vk::SourceCollections& programCollection) const
1068 	{
1069 		const bool mirrorCoords = true;
1070 		addDefaultPrograms(programCollection, m_caseDef.primitiveType, m_caseDef.spacingMode, getWindingUsage(m_caseDef.winding), getPointModeUsage(m_caseDef.usePointMode), mirrorCoords);
1071 	}
1072 
createInstance(Context & context) const1073 	TestInstance* createInstance (Context& context) const
1074 	{
1075 		return new SymmetricOuterEdgeTestInstance(context, m_caseDef);
1076 	};
1077 
1078 private:
1079 	const CaseDefinition m_caseDef;
1080 };
1081 
makeOuterEdgeDivisionTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TessPrimitiveType primitiveType,const SpacingMode spacingMode)1082 tcu::TestCase* makeOuterEdgeDivisionTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
1083 {
1084 	const CaseDefinition caseDef = { primitiveType, spacingMode, WINDING_LAST, false };  // winding is ignored by this test
1085 	return new OuterEdgeDivisionTest(testCtx, name, description, caseDef);
1086 }
1087 
makeOuterEdgeIndexIndependenceTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TessPrimitiveType primitiveType,const SpacingMode spacingMode,const Winding winding,const bool usePointMode)1088 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)
1089 {
1090 	const CaseDefinition caseDef = { primitiveType, spacingMode, winding, usePointMode };
1091 	return new OuterEdgeIndexIndependenceTest(testCtx, name, description, caseDef);
1092 }
1093 
makeSymmetricOuterEdgeTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TessPrimitiveType primitiveType,const SpacingMode spacingMode,const Winding winding,const bool usePointMode)1094 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)
1095 {
1096 	const CaseDefinition caseDef = { primitiveType, spacingMode, winding, usePointMode };
1097 	return new SymmetricOuterEdgeTest(testCtx, name, description, caseDef);
1098 }
1099 
1100 } // InvariantOuterEdge ns
1101 
1102 namespace PrimitiveSetInvariance
1103 {
1104 
1105 enum CaseType
1106 {
1107 	CASETYPE_INVARIANT_PRIMITIVE_SET,
1108 	CASETYPE_INVARIANT_TRIANGLE_SET,
1109 	CASETYPE_INVARIANT_OUTER_TRIANGLE_SET,
1110 	CASETYPE_INVARIANT_INNER_TRIANGLE_SET,
1111 };
1112 
1113 struct CaseDefinition
1114 {
1115 	CaseType				caseType;
1116 	TessPrimitiveType		primitiveType;
1117 	SpacingMode				spacingMode;
1118 	WindingUsage			windingUsage;
1119 	bool					usePointMode;
1120 };
1121 
1122 struct LevelCase
1123 {
1124 	std::vector<TessLevels>	levels;
1125 	int						mem; //!< Subclass-defined arbitrary piece of data, for type of the levelcase, if needed.
1126 
LevelCasevkt::tessellation::__anon64b6dc450111::PrimitiveSetInvariance::LevelCase1127 	LevelCase (const TessLevels& lev) : levels(std::vector<TessLevels>(1, lev)), mem(0) {}
LevelCasevkt::tessellation::__anon64b6dc450111::PrimitiveSetInvariance::LevelCase1128 	LevelCase (void) : mem(0) {}
1129 };
1130 
1131 typedef tcu::Vector<tcu::Vec3, 3> Triangle;
1132 
makeTriangle(const PerPrimitive & primitive)1133 inline Triangle makeTriangle (const PerPrimitive& primitive)
1134 {
1135 	return Triangle(primitive.tessCoord[0].swizzle(0, 1, 2),
1136 					primitive.tessCoord[1].swizzle(0, 1, 2),
1137 					primitive.tessCoord[2].swizzle(0, 1, 2));
1138 }
1139 
1140 //! Compare triangle sets, ignoring triangle order and vertex order within triangle, and possibly exclude some triangles too.
1141 template <typename IsTriangleRelevantT>
compareTriangleSets(const PerPrimitiveVec & primitivesA,const PerPrimitiveVec & primitivesB,tcu::TestLog & log,const IsTriangleRelevantT & isTriangleRelevant,const char * ignoredTriangleDescription=DE_NULL)1142 bool compareTriangleSets (const PerPrimitiveVec&		primitivesA,
1143 						  const PerPrimitiveVec&		primitivesB,
1144 						  tcu::TestLog&					log,
1145 						  const IsTriangleRelevantT&	isTriangleRelevant,
1146 						  const char*					ignoredTriangleDescription = DE_NULL)
1147 {
1148 	typedef LexCompare<Triangle, 3, VecLexLessThan<3> >		TriangleLexLessThan;
1149 	typedef std::set<Triangle, TriangleLexLessThan>			TriangleSet;
1150 
1151 	const int		numTrianglesA = static_cast<int>(primitivesA.size());
1152 	const int		numTrianglesB = static_cast<int>(primitivesB.size());
1153 	TriangleSet		trianglesA;
1154 	TriangleSet		trianglesB;
1155 
1156 	for (int aOrB = 0; aOrB < 2; ++aOrB)
1157 	{
1158 		const PerPrimitiveVec& primitives	= aOrB == 0 ? primitivesA	: primitivesB;
1159 		const int			   numTriangles	= aOrB == 0 ? numTrianglesA	: numTrianglesB;
1160 		TriangleSet&		   triangles	= aOrB == 0 ? trianglesA	: trianglesB;
1161 
1162 		for (int triNdx = 0; triNdx < numTriangles; ++triNdx)
1163 		{
1164 			Triangle triangle = makeTriangle(primitives[triNdx]);
1165 
1166 			if (isTriangleRelevant(triangle.getPtr()))
1167 			{
1168 				std::sort(triangle.getPtr(), triangle.getPtr()+3, VecLexLessThan<3>());
1169 				triangles.insert(triangle);
1170 			}
1171 		}
1172 	}
1173 	{
1174 		TriangleSet::const_iterator aIt = trianglesA.begin();
1175 		TriangleSet::const_iterator bIt = trianglesB.begin();
1176 
1177 		while (aIt != trianglesA.end() || bIt != trianglesB.end())
1178 		{
1179 			const bool aEnd = aIt == trianglesA.end();
1180 			const bool bEnd = bIt == trianglesB.end();
1181 
1182 			if (aEnd || bEnd || *aIt != *bIt)
1183 			{
1184 				log << tcu::TestLog::Message << "Failure: triangle sets in two cases are not equal (when ignoring triangle and vertex order"
1185 					<< (ignoredTriangleDescription == DE_NULL ? "" : std::string() + ", and " + ignoredTriangleDescription) << ")" << tcu::TestLog::EndMessage;
1186 
1187 				if (!aEnd && (bEnd || TriangleLexLessThan()(*aIt, *bIt)))
1188 					log << tcu::TestLog::Message << "Note: e.g. triangle " << *aIt << " exists for first case but not for second" << tcu::TestLog::EndMessage;
1189 				else
1190 					log << tcu::TestLog::Message << "Note: e.g. triangle " << *bIt << " exists for second case but not for first" << tcu::TestLog::EndMessage;
1191 
1192 				return false;
1193 			}
1194 
1195 			++aIt;
1196 			++bIt;
1197 		}
1198 
1199 		return true;
1200 	}
1201 }
1202 
1203 template <typename ArgT, bool res>
1204 struct ConstantUnaryPredicate
1205 {
operator ()vkt::tessellation::__anon64b6dc450111::PrimitiveSetInvariance::ConstantUnaryPredicate1206 	bool operator() (const ArgT&) const { return res; }
1207 };
1208 
compareTriangleSets(const PerPrimitiveVec & primitivesA,const PerPrimitiveVec & primitivesB,tcu::TestLog & log)1209 bool compareTriangleSets (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, tcu::TestLog& log)
1210 {
1211 	return compareTriangleSets(primitivesA, primitivesB, log, ConstantUnaryPredicate<const tcu::Vec3*, true>());
1212 }
1213 
1214 //! Compare two sets of primitives. Order of primitives in each set is undefined, but within each primitive
1215 //! vertex order and coordinates are expected to match exactly.
comparePrimitivesExact(const PerPrimitive * const primitivesA,const PerPrimitive * const primitivesB,const int numPrimitivesPerPatch)1216 bool comparePrimitivesExact (const PerPrimitive* const primitivesA, const PerPrimitive* const primitivesB, const int numPrimitivesPerPatch)
1217 {
1218 	int ndxB = 0;
1219 	for (int ndxA = 0; ndxA < numPrimitivesPerPatch; ++ndxA)
1220 	{
1221 		const tcu::Vec4 (&coordsA)[3] = primitivesA[ndxA].tessCoord;
1222 		bool match = false;
1223 
1224 		// Actually both sets are usually somewhat sorted, so don't reset ndxB after each match. Instead, continue from the next index.
1225 		for (int i = 0; i < numPrimitivesPerPatch; ++i)
1226 		{
1227 			const tcu::Vec4 (&coordsB)[3] = primitivesB[ndxB].tessCoord;
1228 			ndxB = (ndxB + 1) % numPrimitivesPerPatch;
1229 
1230 			if (coordsA[0] == coordsB[0] && coordsA[1] == coordsB[1] && coordsA[2] == coordsB[2])
1231 			{
1232 				match = true;
1233 				break;
1234 			}
1235 		}
1236 
1237 		if (!match)
1238 			return false;
1239 	}
1240 	return true;
1241 }
1242 
1243 /*--------------------------------------------------------------------*//*!
1244  * \brief Base class for testing invariance of entire primitive set
1245  *
1246  * Draws two patches with identical tessellation levels and compares the
1247  * results. Repeats the same with other programs that are only different
1248  * in irrelevant ways; compares the results between these two programs.
1249  * Also potentially compares to results produced by different tessellation
1250  * levels (see e.g. invariance rule #6).
1251  * Furthermore, repeats the above with multiple different tessellation
1252  * value sets.
1253  *
1254  * The manner of primitive set comparison is defined by subclass. E.g.
1255  * case for invariance rule #1 tests that same vertices come out, in same
1256  * order; rule #5 only requires that the same triangles are output, but
1257  * not necessarily in the same order.
1258  *//*--------------------------------------------------------------------*/
1259 class InvarianceTestCase : public TestCase
1260 {
1261 public:
InvarianceTestCase(tcu::TestContext & context,const std::string & name,const std::string & description,const CaseDefinition & caseDef)1262 									InvarianceTestCase			(tcu::TestContext& context, const std::string& name, const std::string& description, const CaseDefinition& caseDef)
1263 										: TestCase	(context, name, description)
1264 										, m_caseDef	(caseDef) {}
1265 
~InvarianceTestCase(void)1266 	virtual							~InvarianceTestCase			(void) {}
1267 
1268 	void							initPrograms				(SourceCollections& programCollection) const;
1269 	TestInstance*					createInstance				(Context& context) const;
1270 
1271 private:
1272 	const CaseDefinition			m_caseDef;
1273 };
1274 
initPrograms(SourceCollections & programCollection) const1275 void InvarianceTestCase::initPrograms (SourceCollections& programCollection) const
1276 {
1277 	addDefaultPrograms(programCollection, m_caseDef.primitiveType, m_caseDef.spacingMode, m_caseDef.windingUsage, getPointModeUsage(m_caseDef.usePointMode));
1278 }
1279 
1280 class InvarianceTestInstance : public TestInstance
1281 {
1282 public:
InvarianceTestInstance(Context & context,const CaseDefinition & caseDef)1283 									InvarianceTestInstance		(Context& context, const CaseDefinition& caseDef) : TestInstance(context), m_caseDef(caseDef) {}
~InvarianceTestInstance(void)1284 	virtual							~InvarianceTestInstance		(void) {}
1285 
1286 	tcu::TestStatus					iterate						(void);
1287 
1288 protected:
1289 	virtual std::vector<LevelCase>	genTessLevelCases			(void) const;
1290 	virtual bool					compare						(const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int levelCaseMem) const = 0;
1291 
1292 	const CaseDefinition			m_caseDef;
1293 };
1294 
genTessLevelCases(void) const1295 std::vector<LevelCase> InvarianceTestInstance::genTessLevelCases (void) const
1296 {
1297 	static const TessLevels basicTessLevelCases[] =
1298 	{
1299 		{ { 1.0f,	1.0f	},	{ 1.0f,		1.0f,	1.0f,	1.0f	} },
1300 		{ { 63.0f,	24.0f	},	{ 15.0f,	42.0f,	10.0f,	12.0f	} },
1301 		{ { 3.0f,	2.0f	},	{ 6.0f,		8.0f,	7.0f,	9.0f	} },
1302 		{ { 4.0f,	6.0f	},	{ 2.0f,		3.0f,	1.0f,	4.0f	} },
1303 		{ { 2.0f,	2.0f	},	{ 6.0f,		8.0f,	7.0f,	9.0f	} },
1304 		{ { 5.0f,	6.0f	},	{ 1.0f,		1.0f,	1.0f,	1.0f	} },
1305 		{ { 1.0f,	6.0f	},	{ 2.0f,		3.0f,	1.0f,	4.0f	} },
1306 		{ { 5.0f,	1.0f	},	{ 2.0f,		3.0f,	1.0f,	4.0f	} },
1307 		{ { 5.2f,	1.6f	},	{ 2.9f,		3.4f,	1.5f,	4.1f	} }
1308 	};
1309 
1310 	std::vector<LevelCase> result;
1311 	for (int i = 0; i < DE_LENGTH_OF_ARRAY(basicTessLevelCases); ++i)
1312 		result.push_back(LevelCase(basicTessLevelCases[i]));
1313 
1314 	{
1315 		de::Random rnd(123);
1316 		for (int i = 0; i < 10; ++i)
1317 		{
1318 			TessLevels levels;
1319 			for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.inner); ++j)
1320 				levels.inner[j] = rnd.getFloat(1.0f, 16.0f);
1321 			for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.outer); ++j)
1322 				levels.outer[j] = rnd.getFloat(1.0f, 16.0f);
1323 			result.push_back(LevelCase(levels));
1324 		}
1325 	}
1326 
1327 	return result;
1328 }
1329 
iterate(void)1330 tcu::TestStatus InvarianceTestInstance::iterate (void)
1331 {
1332 	requireFeatures(m_context.getInstanceInterface(), m_context.getPhysicalDevice(),
1333 					FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
1334 
1335 	const DeviceInterface&	vk					= m_context.getDeviceInterface();
1336 	const VkDevice			device				= m_context.getDevice();
1337 	const VkQueue			queue				= m_context.getUniversalQueue();
1338 	const deUint32			queueFamilyIndex	= m_context.getUniversalQueueFamilyIndex();
1339 	Allocator&				allocator			= m_context.getDefaultAllocator();
1340 
1341 	const std::vector<LevelCase>	tessLevelCases				= genTessLevelCases();
1342 	const int						numPatchesPerDrawCall		= 2;
1343 	int								maxNumPrimitivesPerPatch	= 0;  // computed below
1344 	std::vector<std::vector<int> >	primitiveCounts;
1345 
1346 	for (int caseNdx = 0; caseNdx < static_cast<int>(tessLevelCases.size()); ++caseNdx)
1347 	{
1348 		primitiveCounts.push_back(std::vector<int>());
1349 		for (int levelNdx = 0; levelNdx < static_cast<int>(tessLevelCases[caseNdx].levels.size()); ++levelNdx)
1350 		{
1351 			const int primitiveCount = referencePrimitiveCount(m_caseDef.primitiveType, m_caseDef.spacingMode, m_caseDef.usePointMode,
1352 															   &tessLevelCases[caseNdx].levels[levelNdx].inner[0], &tessLevelCases[caseNdx].levels[levelNdx].outer[0]);
1353 			primitiveCounts.back().push_back(primitiveCount);
1354 			maxNumPrimitivesPerPatch = de::max(maxNumPrimitivesPerPatch, primitiveCount);
1355 		}
1356 	}
1357 
1358 	// Allow for more primitievs in case tessellation/geometry has extra invocations
1359 	maxNumPrimitivesPerPatch *= NUM_EXTRA_TESS_GEOM_INVOCATIONS;
1360 
1361 	// Vertex input attributes buffer: to pass tessellation levels
1362 
1363 	const VkFormat		vertexFormat		= VK_FORMAT_R32_SFLOAT;
1364 	const deUint32		vertexStride		= tcu::getPixelSize(mapVkFormat(vertexFormat));
1365 	const VkDeviceSize	vertexDataSizeBytes	= NUM_TESS_LEVELS * numPatchesPerDrawCall * vertexStride;
1366 	const Buffer		vertexBuffer		(vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
1367 
1368 	// Output buffer: number of primitives and an array of PerPrimitive structures
1369 
1370 	const int			resultBufferMaxVertices			= numPatchesPerDrawCall * maxNumPrimitivesPerPatch * numVerticesPerPrimitive(m_caseDef.primitiveType, m_caseDef.usePointMode);
1371 	const int			resultBufferTessCoordsOffset	= (int)sizeof(deInt32) * 4;
1372 	const VkDeviceSize	resultBufferSizeBytes			= resultBufferTessCoordsOffset + resultBufferMaxVertices * sizeof(PerPrimitive);
1373 	const Buffer		resultBuffer					(vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
1374 
1375 	// Descriptors
1376 
1377 	const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
1378 		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_GEOMETRY_BIT)
1379 		.build(vk, device));
1380 
1381 	const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
1382 		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
1383 		.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
1384 
1385 	const Unique<VkDescriptorSet> descriptorSet    (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
1386 	const VkDescriptorBufferInfo  resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
1387 
1388 	DescriptorSetUpdateBuilder()
1389 		.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
1390 		.update(vk, device);
1391 
1392 	const Unique<VkRenderPass>		renderPass		(makeRenderPassWithoutAttachments	(vk, device));
1393 	const Unique<VkFramebuffer>		framebuffer		(makeFramebuffer					(vk, device, *renderPass, 0u, DE_NULL, 1u, 1u));
1394 	const Unique<VkPipelineLayout>	pipelineLayout	(makePipelineLayout					(vk, device, *descriptorSetLayout));
1395 	const Unique<VkCommandPool>		cmdPool			(makeCommandPool					(vk, device, queueFamilyIndex));
1396 	const Unique<VkCommandBuffer>	cmdBuffer		(allocateCommandBuffer				(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
1397 
1398 	for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < static_cast<int>(tessLevelCases.size()); ++tessLevelCaseNdx)
1399 	{
1400 		const LevelCase& levelCase = tessLevelCases[tessLevelCaseNdx];
1401 		PerPrimitiveVec  firstPrim;
1402 
1403 		{
1404 			tcu::TestLog& log = m_context.getTestContext().getLog();
1405 			std::ostringstream tessLevelsStr;
1406 
1407 			for (int i = 0; i < static_cast<int>(levelCase.levels.size()); ++i)
1408 				tessLevelsStr << (levelCase.levels.size() > 1u ? "\n" : "") << getTessellationLevelsString(levelCase.levels[i], m_caseDef.primitiveType);
1409 
1410 			log << tcu::TestLog::Message << "Tessellation level sets: " << tessLevelsStr.str() << tcu::TestLog::EndMessage;
1411 		}
1412 
1413 		for (int subTessLevelCaseNdx = 0; subTessLevelCaseNdx < static_cast<int>(levelCase.levels.size()); ++subTessLevelCaseNdx)
1414 		{
1415 			const TessLevels& tessLevels = levelCase.levels[subTessLevelCaseNdx];
1416 			{
1417 				TessLevels data[2];
1418 				data[0] = tessLevels;
1419 				data[1] = tessLevels;
1420 
1421 				const Allocation& alloc = vertexBuffer.getAllocation();
1422 
1423 				deMemcpy(alloc.getHostPtr(), data, sizeof(data));
1424 				flushAlloc(vk, device, alloc);
1425 			}
1426 
1427 			int programNdx = 0;
1428 			const std::vector<Winding> windingCases = getWindingCases(m_caseDef.windingUsage);
1429 			for (std::vector<Winding>::const_iterator windingIter = windingCases.begin(); windingIter != windingCases.end(); ++windingIter)
1430 			{
1431 				const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
1432 					.setPatchControlPoints			(NUM_TESS_LEVELS)
1433 					.setVertexInputSingleAttribute	(vertexFormat, vertexStride)
1434 					.setShader						(vk, device, VK_SHADER_STAGE_VERTEX_BIT,					m_context.getBinaryCollection().get("vert"), DE_NULL)
1435 					.setShader						(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,		m_context.getBinaryCollection().get("tesc"), DE_NULL)
1436 					.setShader						(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,	m_context.getBinaryCollection().get(getProgramName("tese", *windingIter, m_caseDef.usePointMode)), DE_NULL)
1437 					.setShader						(vk, device, VK_SHADER_STAGE_GEOMETRY_BIT,					m_context.getBinaryCollection().get(getProgramName("geom", m_caseDef.usePointMode)), DE_NULL)
1438 					.build							(vk, device, *pipelineLayout, *renderPass));
1439 
1440 				{
1441 					const Allocation& alloc = resultBuffer.getAllocation();
1442 
1443 					deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
1444 					flushAlloc(vk, device, alloc);
1445 				}
1446 
1447 				beginCommandBuffer(vk, *cmdBuffer);
1448 				beginRenderPassWithRasterizationDisabled(vk, *cmdBuffer, *renderPass, *framebuffer);
1449 
1450 				vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
1451 				vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
1452 				{
1453 					const VkDeviceSize vertexBufferOffset = 0ull;
1454 					vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
1455 				}
1456 
1457 				vk.cmdDraw(*cmdBuffer, numPatchesPerDrawCall * NUM_TESS_LEVELS, 1u, 0u, 0u);
1458 				endRenderPass(vk, *cmdBuffer);
1459 
1460 				{
1461 					const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
1462 						VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes);
1463 
1464 					vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
1465 						0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
1466 				}
1467 
1468 				endCommandBuffer(vk, *cmdBuffer);
1469 				submitCommandsAndWait(vk, device, queue, *cmdBuffer);
1470 
1471 				// Verify case result
1472 				{
1473 					const Allocation&		resultAlloc				= resultBuffer.getAllocation();
1474 
1475 					invalidateAlloc(vk, device, resultAlloc);
1476 
1477 					const int				refNumPrimitives		= numPatchesPerDrawCall * primitiveCounts[tessLevelCaseNdx][subTessLevelCaseNdx];
1478 					const int				numPrimitiveVertices	= numVerticesPerPrimitive(m_caseDef.primitiveType, m_caseDef.usePointMode);
1479 					const deInt32			numPrimitives			= *static_cast<deInt32*>(resultAlloc.getHostPtr());
1480 					const PerPrimitiveVec	primitives				= sorted(readInterleavedData<PerPrimitive>(numPrimitives, resultAlloc.getHostPtr(),
1481 																			 resultBufferTessCoordsOffset, sizeof(PerPrimitive)), byPatchPrimitiveID);
1482 
1483 					// If this fails then we didn't read all vertices from shader and test must be changed to allow more.
1484 					DE_ASSERT(numPrimitiveVertices * numPrimitives <= resultBufferMaxVertices);
1485 					DE_UNREF(numPrimitiveVertices);
1486 
1487 					tcu::TestLog& log = m_context.getTestContext().getLog();
1488 
1489 					if (numPrimitives < refNumPrimitives)
1490 					{
1491 						log << tcu::TestLog::Message << "Failure: got " << numPrimitives << " primitives, but expected at least" << refNumPrimitives << tcu::TestLog::EndMessage;
1492 
1493 						return tcu::TestStatus::fail("Invalid set of primitives");
1494 					}
1495 
1496 					const int					half  = static_cast<int>(primitives.size() / 2);
1497 					const PerPrimitiveVec		prim0 = PerPrimitiveVec(primitives.begin(), primitives.begin() + half);
1498 					const PerPrimitive* const	prim1 = &primitives[half];
1499 
1500 					if (!comparePrimitivesExact(&prim0[0], prim1, half))
1501 					{
1502 							log << tcu::TestLog::Message << "Failure: tessellation coordinates differ between two primitives drawn in one draw call" << tcu::TestLog::EndMessage
1503 								<< tcu::TestLog::Message << "Note: tessellation levels for both primitives were: " << getTessellationLevelsString(tessLevels, m_caseDef.primitiveType) << tcu::TestLog::EndMessage;
1504 
1505 							return tcu::TestStatus::fail("Invalid set of primitives");
1506 					}
1507 
1508 					if (programNdx == 0 && subTessLevelCaseNdx == 0)
1509 						firstPrim = prim0;
1510 					else
1511 					{
1512 						const bool compareOk = compare(firstPrim, prim0, levelCase.mem);
1513 						if (!compareOk)
1514 						{
1515 							log << tcu::TestLog::Message
1516 								<< "Note: comparison of tessellation coordinates failed; comparison was made between following cases:\n"
1517 								<< "  - case A: program 0, tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx].levels[0], m_caseDef.primitiveType) << "\n"
1518 								<< "  - case B: program " << programNdx << ", tessellation levels: " << getTessellationLevelsString(tessLevels, m_caseDef.primitiveType)
1519 								<< tcu::TestLog::EndMessage;
1520 
1521 							return tcu::TestStatus::fail("Invalid set of primitives");
1522 						}
1523 					}
1524 				}
1525 				++programNdx;
1526 			}
1527 		}
1528 	}
1529 	return tcu::TestStatus::pass("OK");
1530 }
1531 
1532 /*--------------------------------------------------------------------*//*!
1533  * \brief Test invariance rule #1
1534  *
1535  * Test that the sequence of primitives input to the TES only depends on
1536  * the tessellation levels, tessellation mode, spacing mode, winding, and
1537  * point mode.
1538  *//*--------------------------------------------------------------------*/
1539 class InvariantPrimitiveSetTestInstance : public InvarianceTestInstance
1540 {
1541 public:
InvariantPrimitiveSetTestInstance(Context & context,const CaseDefinition & caseDef)1542 	InvariantPrimitiveSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {}
1543 
1544 protected:
compare(const PerPrimitiveVec & primitivesA,const PerPrimitiveVec & primitivesB,const int) const1545 	bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int) const
1546 	{
1547 		if (!comparePrimitivesExact(&primitivesA[0], &primitivesB[0], static_cast<int>(primitivesA.size())))
1548 		{
1549 			m_context.getTestContext().getLog()
1550 				<< tcu::TestLog::Message << "Failure: tessellation coordinates differ between two programs" << tcu::TestLog::EndMessage;
1551 
1552 			return false;
1553 		}
1554 		return true;
1555 	}
1556 };
1557 
1558 /*--------------------------------------------------------------------*//*!
1559  * \brief Test invariance rule #5
1560  *
1561  * Test that the set of triangles input to the TES only depends on the
1562  * tessellation levels, tessellation mode and spacing mode. Specifically,
1563  * winding doesn't change the set of triangles, though it can change the
1564  * order in which they are input to TES, and can (and will) change the
1565  * vertex order within a triangle.
1566  *//*--------------------------------------------------------------------*/
1567 class InvariantTriangleSetTestInstance : public InvarianceTestInstance
1568 {
1569 public:
InvariantTriangleSetTestInstance(Context & context,const CaseDefinition & caseDef)1570 	InvariantTriangleSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {}
1571 
1572 protected:
compare(const PerPrimitiveVec & primitivesA,const PerPrimitiveVec & primitivesB,const int) const1573 	bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int) const
1574 	{
1575 		return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog());
1576 	}
1577 };
1578 
1579 /*--------------------------------------------------------------------*//*!
1580  * \brief Test invariance rule #6
1581  *
1582  * Test that the set of inner triangles input to the TES only depends on
1583  * the inner tessellation levels, tessellation mode and spacing mode.
1584  *//*--------------------------------------------------------------------*/
1585 class InvariantInnerTriangleSetTestInstance : public InvarianceTestInstance
1586 {
1587 public:
InvariantInnerTriangleSetTestInstance(Context & context,const CaseDefinition & caseDef)1588 	InvariantInnerTriangleSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {}
1589 
1590 protected:
genTessLevelCases(void) const1591 	std::vector<LevelCase> genTessLevelCases (void) const
1592 	{
1593 		const int						numSubCases		= 4;
1594 		const std::vector<LevelCase>	baseResults		= InvarianceTestInstance::genTessLevelCases();
1595 		std::vector<LevelCase>			result;
1596 		de::Random						rnd				(123);
1597 
1598 		// Generate variants with different values for irrelevant levels.
1599 		for (int baseNdx = 0; baseNdx < static_cast<int>(baseResults.size()); ++baseNdx)
1600 		{
1601 			const TessLevels&	base	= baseResults[baseNdx].levels[0];
1602 			TessLevels			levels	= base;
1603 			LevelCase			levelCase;
1604 
1605 			for (int subNdx = 0; subNdx < numSubCases; ++subNdx)
1606 			{
1607 				levelCase.levels.push_back(levels);
1608 
1609 				for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); ++i)
1610 					levels.outer[i] = rnd.getFloat(2.0f, 16.0f);
1611 				if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
1612 					levels.inner[1] = rnd.getFloat(2.0f, 16.0f);
1613 			}
1614 
1615 			result.push_back(levelCase);
1616 		}
1617 
1618 		return result;
1619 	}
1620 
1621 	struct IsInnerTriangleTriangle
1622 	{
operator ()vkt::tessellation::__anon64b6dc450111::PrimitiveSetInvariance::InvariantInnerTriangleSetTestInstance::IsInnerTriangleTriangle1623 		bool operator() (const tcu::Vec3* vertices) const
1624 		{
1625 			for (int v = 0; v < 3; ++v)
1626 				for (int c = 0; c < 3; ++c)
1627 					if (vertices[v][c] == 0.0f)
1628 						return false;
1629 			return true;
1630 		}
1631 	};
1632 
1633 	struct IsInnerQuadTriangle
1634 	{
operator ()vkt::tessellation::__anon64b6dc450111::PrimitiveSetInvariance::InvariantInnerTriangleSetTestInstance::IsInnerQuadTriangle1635 		bool operator() (const tcu::Vec3* vertices) const
1636 		{
1637 			for (int v = 0; v < 3; ++v)
1638 				for (int c = 0; c < 2; ++c)
1639 					if (vertices[v][c] == 0.0f || vertices[v][c] == 1.0f)
1640 						return false;
1641 			return true;
1642 		}
1643 	};
1644 
compare(const PerPrimitiveVec & primitivesA,const PerPrimitiveVec & primitivesB,const int) const1645 	bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int) const
1646 	{
1647 		if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
1648 			return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(), IsInnerTriangleTriangle(), "outer triangles");
1649 		else if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
1650 			return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(), IsInnerQuadTriangle(), "outer triangles");
1651 		else
1652 		{
1653 			DE_ASSERT(false);
1654 			return false;
1655 		}
1656 	}
1657 };
1658 
1659 /*--------------------------------------------------------------------*//*!
1660  * \brief Test invariance rule #7
1661  *
1662  * Test that the set of outer triangles input to the TES only depends on
1663  * tessellation mode, spacing mode and the inner and outer tessellation
1664  * levels corresponding to the inner and outer edges relevant to that
1665  * triangle.
1666  *//*--------------------------------------------------------------------*/
1667 class InvariantOuterTriangleSetTestInstance : public InvarianceTestInstance
1668 {
1669 public:
InvariantOuterTriangleSetTestInstance(Context & context,const CaseDefinition & caseDef)1670 	InvariantOuterTriangleSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {}
1671 
1672 protected:
genTessLevelCases(void) const1673 	std::vector<LevelCase> genTessLevelCases (void) const
1674 	{
1675 		const int						numSubCasesPerEdge	= 4;
1676 		const int						numEdges			= m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES	? 3
1677 															: m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS		? 4 : 0;
1678 		const std::vector<LevelCase>	baseResult			= InvarianceTestInstance::genTessLevelCases();
1679 		std::vector<LevelCase>			result;
1680 		de::Random						rnd					(123);
1681 
1682 		// Generate variants with different values for irrelevant levels.
1683 		for (int baseNdx = 0; baseNdx < static_cast<int>(baseResult.size()); ++baseNdx)
1684 		{
1685 			const TessLevels& base = baseResult[baseNdx].levels[0];
1686 			if (base.inner[0] == 1.0f || (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS && base.inner[1] == 1.0f))
1687 				continue;
1688 
1689 			for (int edgeNdx = 0; edgeNdx < numEdges; ++edgeNdx)
1690 			{
1691 				TessLevels	levels = base;
1692 				LevelCase	levelCase;
1693 				levelCase.mem = edgeNdx;
1694 
1695 				for (int subCaseNdx = 0; subCaseNdx < numSubCasesPerEdge; ++subCaseNdx)
1696 				{
1697 					levelCase.levels.push_back(levels);
1698 
1699 					for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); ++i)
1700 					{
1701 						if (i != edgeNdx)
1702 							levels.outer[i] = rnd.getFloat(2.0f, 16.0f);
1703 					}
1704 
1705 					if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
1706 						levels.inner[1] = rnd.getFloat(2.0f, 16.0f);
1707 				}
1708 
1709 				result.push_back(levelCase);
1710 			}
1711 		}
1712 
1713 		return result;
1714 	}
1715 
1716 	class IsTriangleTriangleOnOuterEdge
1717 	{
1718 	public:
IsTriangleTriangleOnOuterEdge(int edgeNdx)1719 		IsTriangleTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
operator ()(const tcu::Vec3 * vertices) const1720 		bool operator() (const tcu::Vec3* vertices) const
1721 		{
1722 			bool touchesAppropriateEdge = false;
1723 			for (int v = 0; v < 3; ++v)
1724 				if (vertices[v][m_edgeNdx] == 0.0f)
1725 					touchesAppropriateEdge = true;
1726 
1727 			if (touchesAppropriateEdge)
1728 			{
1729 				const tcu::Vec3 avg = (vertices[0] + vertices[1] + vertices[2]) / 3.0f;
1730 				return avg[m_edgeNdx] < avg[(m_edgeNdx+1)%3] &&
1731 					   avg[m_edgeNdx] < avg[(m_edgeNdx+2)%3];
1732 			}
1733 			return false;
1734 		}
1735 
1736 	private:
1737 		const int m_edgeNdx;
1738 	};
1739 
1740 	class IsQuadTriangleOnOuterEdge
1741 	{
1742 	public:
IsQuadTriangleOnOuterEdge(int edgeNdx)1743 		IsQuadTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
1744 
onEdge(const tcu::Vec3 & v) const1745 		bool onEdge (const tcu::Vec3& v) const
1746 		{
1747 			return v[m_edgeNdx%2] == (m_edgeNdx <= 1 ? 0.0f : 1.0f);
1748 		}
1749 
onAnyEdge(const tcu::Vec3 & v)1750 		static inline bool onAnyEdge (const tcu::Vec3& v)
1751 		{
1752 			return v[0] == 0.0f || v[0] == 1.0f || v[1] == 0.0f || v[1] == 1.0f;
1753 		}
1754 
operator ()(const tcu::Vec3 * vertices) const1755 		bool operator() (const tcu::Vec3* vertices) const
1756 		{
1757 			for (int v = 0; v < 3; ++v)
1758 			{
1759 				const tcu::Vec3& a = vertices[v];
1760 				const tcu::Vec3& b = vertices[(v+1)%3];
1761 				const tcu::Vec3& c = vertices[(v+2)%3];
1762 				if (onEdge(a) && onEdge(b))
1763 					return true;
1764 				if (onEdge(c) && !onAnyEdge(a) && !onAnyEdge(b) && a[m_edgeNdx%2] == b[m_edgeNdx%2])
1765 					return true;
1766 			}
1767 
1768 			return false;
1769 		}
1770 
1771 	private:
1772 		const int m_edgeNdx;
1773 	};
1774 
compare(const PerPrimitiveVec & primitivesA,const PerPrimitiveVec & primitivesB,const int outerEdgeNdx) const1775 	bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int outerEdgeNdx) const
1776 	{
1777 		if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
1778 		{
1779 			return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(),
1780 									   IsTriangleTriangleOnOuterEdge(outerEdgeNdx),
1781 									   ("inner triangles, and outer triangles corresponding to other edge than edge "
1782 										+ outerEdgeDescriptions(m_caseDef.primitiveType)[outerEdgeNdx].description()).c_str());
1783 		}
1784 		else if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
1785 		{
1786 			return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(),
1787 									   IsQuadTriangleOnOuterEdge(outerEdgeNdx),
1788 									   ("inner triangles, and outer triangles corresponding to other edge than edge "
1789 										+ outerEdgeDescriptions(m_caseDef.primitiveType)[outerEdgeNdx].description()).c_str());
1790 		}
1791 		else
1792 			DE_ASSERT(false);
1793 
1794 		return true;
1795 	}
1796 };
1797 
createInstance(Context & context) const1798 TestInstance* InvarianceTestCase::createInstance (Context& context) const
1799 {
1800 	switch (m_caseDef.caseType)
1801 	{
1802 		case CASETYPE_INVARIANT_PRIMITIVE_SET:			return new InvariantPrimitiveSetTestInstance	(context, m_caseDef);
1803 		case CASETYPE_INVARIANT_TRIANGLE_SET:			return new InvariantTriangleSetTestInstance		(context, m_caseDef);
1804 		case CASETYPE_INVARIANT_OUTER_TRIANGLE_SET:		return new InvariantOuterTriangleSetTestInstance(context, m_caseDef);
1805 		case CASETYPE_INVARIANT_INNER_TRIANGLE_SET:		return new InvariantInnerTriangleSetTestInstance(context, m_caseDef);
1806 		default:
1807 			DE_ASSERT(false);
1808 			return DE_NULL;
1809 	}
1810 }
1811 
makeInvariantPrimitiveSetTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TessPrimitiveType primitiveType,const SpacingMode spacingMode,const Winding winding,const bool usePointMode)1812 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)
1813 {
1814 	const CaseDefinition caseDef = { CASETYPE_INVARIANT_PRIMITIVE_SET, primitiveType, spacingMode, getWindingUsage(winding), usePointMode };
1815 	return new InvarianceTestCase(testCtx, name, description, caseDef);
1816 }
1817 
makeInvariantTriangleSetTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TessPrimitiveType primitiveType,const SpacingMode spacingMode)1818 TestCase* makeInvariantTriangleSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
1819 {
1820 	DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
1821 	const CaseDefinition caseDef = { CASETYPE_INVARIANT_TRIANGLE_SET, primitiveType, spacingMode, WINDING_USAGE_VARY, false };
1822 	return new InvarianceTestCase(testCtx, name, description, caseDef);
1823 }
1824 
makeInvariantInnerTriangleSetTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TessPrimitiveType primitiveType,const SpacingMode spacingMode)1825 TestCase* makeInvariantInnerTriangleSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
1826 {
1827 	DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
1828 	const CaseDefinition caseDef = { CASETYPE_INVARIANT_INNER_TRIANGLE_SET, primitiveType, spacingMode, WINDING_USAGE_VARY, false };
1829 	return new InvarianceTestCase(testCtx, name, description, caseDef);
1830 }
1831 
makeInvariantOuterTriangleSetTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TessPrimitiveType primitiveType,const SpacingMode spacingMode)1832 TestCase* makeInvariantOuterTriangleSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
1833 {
1834 	DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
1835 	const CaseDefinition caseDef = { CASETYPE_INVARIANT_OUTER_TRIANGLE_SET, primitiveType, spacingMode, WINDING_USAGE_VARY, false };
1836 	return new InvarianceTestCase(testCtx, name, description, caseDef);
1837 }
1838 
1839 } // PrimitiveSetInvariance ns
1840 
1841 namespace TessCoordComponent
1842 {
1843 
1844 enum CaseType
1845 {
1846 	CASETYPE_TESS_COORD_RANGE = 0,		//!< Test that all (relevant) components of tess coord are in [0,1].
1847 	CASETYPE_ONE_MINUS_TESS_COORD,		//!< Test that for every (relevant) component c of a tess coord, 1.0-c is exact.
1848 
1849 	CASETYPE_LAST
1850 };
1851 
1852 struct CaseDefinition
1853 {
1854 	CaseType			caseType;
1855 	TessPrimitiveType	primitiveType;
1856 	SpacingMode			spacingMode;
1857 	Winding				winding;
1858 	bool				usePointMode;
1859 };
1860 
genTessLevelCases(const int numCases)1861 std::vector<TessLevels> genTessLevelCases (const int numCases)
1862 {
1863 	de::Random				rnd(123);
1864 	std::vector<TessLevels>	result;
1865 
1866 	for (int i = 0; i < numCases; ++i)
1867 	{
1868 		TessLevels levels;
1869 		levels.inner[0] = rnd.getFloat(1.0f, 63.0f);
1870 		levels.inner[1] = rnd.getFloat(1.0f, 63.0f);
1871 		levels.outer[0] = rnd.getFloat(1.0f, 63.0f);
1872 		levels.outer[1] = rnd.getFloat(1.0f, 63.0f);
1873 		levels.outer[2] = rnd.getFloat(1.0f, 63.0f);
1874 		levels.outer[3] = rnd.getFloat(1.0f, 63.0f);
1875 		result.push_back(levels);
1876 	}
1877 
1878 	return result;
1879 }
1880 
1881 typedef bool (*CompareFunc)(tcu::TestLog& log, const float value);
1882 
compareTessCoordRange(tcu::TestLog & log,const float value)1883 bool compareTessCoordRange (tcu::TestLog& log, const float value)
1884 {
1885 	if (!de::inRange(value, 0.0f, 1.0f))
1886 	{
1887 		log << tcu::TestLog::Message << "Failure: tess coord component isn't in range [0,1]" << tcu::TestLog::EndMessage;
1888 		return false;
1889 	}
1890 	return true;
1891 }
1892 
compareOneMinusTessCoord(tcu::TestLog & log,const float value)1893 bool compareOneMinusTessCoord (tcu::TestLog& log, const float value)
1894 {
1895 	if (value != 1.0f)
1896 	{
1897 		log << tcu::TestLog::Message << "Failure: comp + (1.0-comp) doesn't equal 1.0 for some component of tessellation coordinate" << tcu::TestLog::EndMessage;
1898 		return false;
1899 	}
1900 	return true;
1901 }
1902 
initPrograms(vk::SourceCollections & programCollection,const CaseDefinition caseDef)1903 void initPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
1904 {
1905 	// Vertex shader
1906 	{
1907 		std::ostringstream src;
1908 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
1909 			<< "\n"
1910 			<< "layout(location = 0) in  highp float in_v_attr;\n"
1911 			<< "layout(location = 0) out highp float in_tc_attr;\n"
1912 			<< "\n"
1913 			<< "void main (void)\n"
1914 			<< "{\n"
1915 			<< "    in_tc_attr = in_v_attr;\n"
1916 			<< "}\n";
1917 
1918 		programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
1919 	}
1920 
1921 	// Tessellation control shader
1922 	{
1923 		std::ostringstream src;
1924 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
1925 			<< "#extension GL_EXT_tessellation_shader : require\n"
1926 			<< "\n"
1927 			<< "layout(vertices = 1) out;\n"
1928 			<< "\n"
1929 			<< "layout(location = 0) in highp float in_tc_attr[];\n"
1930 			<< "\n"
1931 			<< "void main (void)\n"
1932 			<< "{\n"
1933 			<< "    gl_TessLevelInner[0] = in_tc_attr[0];\n"
1934 			<< "    gl_TessLevelInner[1] = in_tc_attr[1];\n"
1935 			<< "\n"
1936 			<< "    gl_TessLevelOuter[0] = in_tc_attr[2];\n"
1937 			<< "    gl_TessLevelOuter[1] = in_tc_attr[3];\n"
1938 			<< "    gl_TessLevelOuter[2] = in_tc_attr[4];\n"
1939 			<< "    gl_TessLevelOuter[3] = in_tc_attr[5];\n"
1940 			<< "}\n";
1941 
1942 		programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
1943 	}
1944 
1945 	// Tessellation evaluation shader
1946 	{
1947 		std::ostringstream tessCoordSrc;
1948 
1949 		if (caseDef.caseType == CASETYPE_TESS_COORD_RANGE)
1950 			tessCoordSrc << "    sb_out.tessCoord[index] = gl_TessCoord;\n";
1951 		else if (caseDef.caseType == CASETYPE_ONE_MINUS_TESS_COORD)
1952 		{
1953 			const char* components[]  = { "x" , "y", "z" };
1954 			const int   numComponents = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2);
1955 
1956 			for (int i = 0; i < numComponents; ++i)
1957 				tessCoordSrc << "    {\n"
1958 							 << "        float oneMinusComp        = 1.0 - gl_TessCoord." << components[i] << ";\n"
1959 							 << "        sb_out.tessCoord[index]." << components[i] << " = gl_TessCoord." << components[i] << " + oneMinusComp;\n"
1960 							 << "    }\n";
1961 		}
1962 		else
1963 		{
1964 			DE_ASSERT(false);
1965 			return;
1966 		}
1967 
1968 		std::ostringstream src;
1969 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
1970 			<< "#extension GL_EXT_tessellation_shader : require\n"
1971 			<< "\n"
1972 			<< "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", "
1973 						 << getSpacingModeShaderName(caseDef.spacingMode) << ", "
1974 						 << getWindingShaderName(caseDef.winding)
1975 						 << (caseDef.usePointMode ? ", point_mode" : "") << ") in;\n"
1976 			<< "\n"
1977 			<< "layout(set = 0, binding = 0, std430) coherent restrict buffer Output {\n"
1978 			<< "    int  numInvocations;\n"
1979 			<< "    vec3 tessCoord[];\n"
1980 			<< "} sb_out;\n"
1981 			<< "\n"
1982 			<< "void main (void)\n"
1983 			<< "{\n"
1984 			<< "    int index = atomicAdd(sb_out.numInvocations, 1);\n"
1985 			<< tessCoordSrc.str()
1986 			<< "}\n";
1987 
1988 		programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
1989 	}
1990 }
1991 
test(Context & context,const CaseDefinition caseDef)1992 tcu::TestStatus test (Context& context, const CaseDefinition caseDef)
1993 {
1994 	requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
1995 
1996 	const DeviceInterface&	vk					= context.getDeviceInterface();
1997 	const VkDevice			device				= context.getDevice();
1998 	const VkQueue			queue				= context.getUniversalQueue();
1999 	const deUint32			queueFamilyIndex	= context.getUniversalQueueFamilyIndex();
2000 	Allocator&				allocator			= context.getDefaultAllocator();
2001 
2002 	const int						numTessLevelCases	= 32;
2003 	const std::vector<TessLevels>	tessLevelCases		= genTessLevelCases(numTessLevelCases);
2004 
2005 	int maxNumVerticesInDrawCall = 0;
2006 	for (int i = 0; i < numTessLevelCases; ++i)
2007 		maxNumVerticesInDrawCall = de::max(maxNumVerticesInDrawCall, referenceVertexCount(caseDef.primitiveType, caseDef.spacingMode, caseDef.usePointMode,
2008 										   &tessLevelCases[i].inner[0], &tessLevelCases[i].outer[0]));
2009 
2010 	// We may get more invocations than expected, so add some more space (arbitrary number).
2011 	maxNumVerticesInDrawCall += 4;
2012 
2013 	// Vertex input attributes buffer: to pass tessellation levels
2014 
2015 	const VkFormat		vertexFormat		= VK_FORMAT_R32_SFLOAT;
2016 	const deUint32		vertexStride		= tcu::getPixelSize(mapVkFormat(vertexFormat));
2017 	const VkDeviceSize	vertexDataSizeBytes	= NUM_TESS_LEVELS * vertexStride;
2018 	const Buffer		vertexBuffer		(vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
2019 
2020 	DE_ASSERT(vertexDataSizeBytes == sizeof(TessLevels));
2021 
2022 	// Output buffer: number of invocations and array of tess coords
2023 
2024 	const int			resultBufferTessCoordsOffset	= (int)sizeof(deInt32) * 4;
2025 	const VkDeviceSize	resultBufferSizeBytes			= resultBufferTessCoordsOffset + maxNumVerticesInDrawCall * sizeof(tcu::Vec4);
2026 	const Buffer		resultBuffer					(vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
2027 
2028 	// Descriptors
2029 
2030 	const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
2031 		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
2032 		.build(vk, device));
2033 
2034 	const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
2035 		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
2036 		.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
2037 
2038 	const Unique<VkDescriptorSet> descriptorSet    (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
2039 	const VkDescriptorBufferInfo  resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
2040 
2041 	DescriptorSetUpdateBuilder()
2042 		.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
2043 		.update(vk, device);
2044 
2045 	const Unique<VkRenderPass>		renderPass		(makeRenderPassWithoutAttachments	(vk, device));
2046 	const Unique<VkFramebuffer>		framebuffer		(makeFramebuffer					(vk, device, *renderPass, 0u, DE_NULL, 1u, 1u));
2047 	const Unique<VkPipelineLayout>	pipelineLayout	(makePipelineLayout					(vk, device, *descriptorSetLayout));
2048 	const Unique<VkCommandPool>		cmdPool			(makeCommandPool					(vk, device, queueFamilyIndex));
2049 	const Unique<VkCommandBuffer>	cmdBuffer		(allocateCommandBuffer				(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
2050 
2051 	const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
2052 		.setPatchControlPoints        (NUM_TESS_LEVELS)
2053 		.setVertexInputSingleAttribute(vertexFormat, vertexStride)
2054 		.setShader                    (vk, device, VK_SHADER_STAGE_VERTEX_BIT,					context.getBinaryCollection().get("vert"), DE_NULL)
2055 		.setShader                    (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,	context.getBinaryCollection().get("tesc"), DE_NULL)
2056 		.setShader                    (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"), DE_NULL)
2057 		.build                        (vk, device, *pipelineLayout, *renderPass));
2058 
2059 	for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < numTessLevelCases; ++tessLevelCaseNdx)
2060 	{
2061 		context.getTestContext().getLog()
2062 			<< tcu::TestLog::Message
2063 			<< "Testing with tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx], caseDef.primitiveType)
2064 			<< tcu::TestLog::EndMessage;
2065 
2066 		{
2067 			const Allocation& alloc = vertexBuffer.getAllocation();
2068 
2069 			deMemcpy(alloc.getHostPtr(), &tessLevelCases[tessLevelCaseNdx], sizeof(TessLevels));
2070 			flushAlloc(vk, device, alloc);
2071 		}
2072 		{
2073 			const Allocation& alloc = resultBuffer.getAllocation();
2074 
2075 			deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
2076 			flushAlloc(vk, device, alloc);
2077 		}
2078 
2079 		beginCommandBuffer(vk, *cmdBuffer);
2080 		beginRenderPassWithRasterizationDisabled(vk, *cmdBuffer, *renderPass, *framebuffer);
2081 
2082 		vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
2083 		vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
2084 		{
2085 			const VkDeviceSize vertexBufferOffset = 0ull;
2086 			vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
2087 		}
2088 
2089 		vk.cmdDraw(*cmdBuffer, NUM_TESS_LEVELS, 1u, 0u, 0u);
2090 		endRenderPass(vk, *cmdBuffer);
2091 
2092 		{
2093 			const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
2094 				VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes);
2095 
2096 			vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
2097 				0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
2098 		}
2099 
2100 		endCommandBuffer(vk, *cmdBuffer);
2101 		submitCommandsAndWait(vk, device, queue, *cmdBuffer);
2102 
2103 		// Verify case result
2104 		{
2105 			const Allocation&				resultAlloc		= resultBuffer.getAllocation();
2106 
2107 			invalidateAlloc(vk, device, resultAlloc);
2108 
2109 			const deInt32					numVertices		= *static_cast<deInt32*>(resultAlloc.getHostPtr());
2110 			const std::vector<tcu::Vec3>	vertices		= readInterleavedData<tcu::Vec3>(numVertices, resultAlloc.getHostPtr(), resultBufferTessCoordsOffset, sizeof(tcu::Vec4));
2111 
2112 			// If this fails then we didn't read all vertices from shader and test must be changed to allow more.
2113 			DE_ASSERT(numVertices <= maxNumVerticesInDrawCall);
2114 
2115 			tcu::TestLog&					log				= context.getTestContext().getLog();
2116 			const int						numComponents	= (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2);
2117 
2118 			CompareFunc						compare			= (caseDef.caseType == CASETYPE_TESS_COORD_RANGE     ? compareTessCoordRange :
2119 															   caseDef.caseType == CASETYPE_ONE_MINUS_TESS_COORD ? compareOneMinusTessCoord : DE_NULL);
2120 
2121 			DE_ASSERT(compare != DE_NULL);
2122 
2123 			for (std::vector<tcu::Vec3>::const_iterator vertexIter = vertices.begin(); vertexIter != vertices.end(); ++vertexIter)
2124 			for (int i = 0; i < numComponents; ++i)
2125 				if (!compare(log, (*vertexIter)[i]))
2126 				{
2127 					log << tcu::TestLog::Message << "Note: got a wrong tessellation coordinate "
2128 						<< (numComponents == 3 ? de::toString(*vertexIter) : de::toString(vertexIter->swizzle(0,1))) << tcu::TestLog::EndMessage;
2129 
2130 					tcu::TestStatus::fail("Invalid tessellation coordinate component");
2131 				}
2132 		}
2133 	}
2134 	return tcu::TestStatus::pass("OK");
2135 }
2136 
makeTessCoordRangeTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TessPrimitiveType primitiveType,const SpacingMode spacingMode,const Winding winding,const bool usePointMode)2137 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)
2138 {
2139 	const CaseDefinition caseDef = { CASETYPE_TESS_COORD_RANGE, primitiveType, spacingMode, winding, usePointMode };
2140 	return createFunctionCaseWithPrograms(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, description, initPrograms, test, caseDef);
2141 }
2142 
makeOneMinusTessCoordTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TessPrimitiveType primitiveType,const SpacingMode spacingMode,const Winding winding,const bool usePointMode)2143 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)
2144 {
2145 	const CaseDefinition caseDef = { CASETYPE_ONE_MINUS_TESS_COORD, primitiveType, spacingMode, winding, usePointMode };
2146 	return createFunctionCaseWithPrograms(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, description, initPrograms, test, caseDef);
2147 }
2148 
2149 } // TessCoordComponent ns
2150 
2151 } // anonymous
2152 
2153 //! These tests correspond to dEQP-GLES31.functional.tessellation.invariance.*
2154 //! Original OpenGL ES tests used transform feedback to get vertices in primitive order. To emulate this behavior we have to use geometry shader,
2155 //! which allows us to intercept verticess of final output primitives. This can't be done with tessellation shaders alone as number and order of
2156 //! invocation is undefined.
createInvarianceTests(tcu::TestContext & testCtx)2157 tcu::TestCaseGroup* createInvarianceTests (tcu::TestContext& testCtx)
2158 {
2159 	de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "invariance", "Test tessellation invariance rules"));
2160 
2161 	de::MovePtr<tcu::TestCaseGroup> invariantPrimitiveSetGroup				(new tcu::TestCaseGroup(testCtx, "primitive_set",					"Test invariance rule #1"));
2162 	de::MovePtr<tcu::TestCaseGroup> invariantOuterEdgeGroup					(new tcu::TestCaseGroup(testCtx, "outer_edge_division",				"Test invariance rule #2"));
2163 	de::MovePtr<tcu::TestCaseGroup> symmetricOuterEdgeGroup					(new tcu::TestCaseGroup(testCtx, "outer_edge_symmetry",				"Test invariance rule #3"));
2164 	de::MovePtr<tcu::TestCaseGroup> outerEdgeVertexSetIndexIndependenceGroup(new tcu::TestCaseGroup(testCtx, "outer_edge_index_independence",	"Test invariance rule #4"));
2165 	de::MovePtr<tcu::TestCaseGroup> invariantTriangleSetGroup				(new tcu::TestCaseGroup(testCtx, "triangle_set",					"Test invariance rule #5"));
2166 	de::MovePtr<tcu::TestCaseGroup> invariantInnerTriangleSetGroup			(new tcu::TestCaseGroup(testCtx, "inner_triangle_set",				"Test invariance rule #6"));
2167 	de::MovePtr<tcu::TestCaseGroup> invariantOuterTriangleSetGroup			(new tcu::TestCaseGroup(testCtx, "outer_triangle_set",				"Test invariance rule #7"));
2168 	de::MovePtr<tcu::TestCaseGroup> tessCoordComponentRangeGroup			(new tcu::TestCaseGroup(testCtx, "tess_coord_component_range",		"Test invariance rule #8, first part"));
2169 	de::MovePtr<tcu::TestCaseGroup> oneMinusTessCoordComponentGroup			(new tcu::TestCaseGroup(testCtx, "one_minus_tess_coord_component",	"Test invariance rule #8, second part"));
2170 
2171 	for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; ++primitiveTypeNdx)
2172 	for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
2173 	{
2174 		const TessPrimitiveType primitiveType = static_cast<TessPrimitiveType>(primitiveTypeNdx);
2175 		const SpacingMode       spacingMode   = static_cast<SpacingMode>(spacingModeNdx);
2176 		const bool              triOrQuad     = primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS;
2177 		const std::string       primName      = getTessPrimitiveTypeShaderName(primitiveType);
2178 		const std::string       primSpacName  = primName + "_" + getSpacingModeShaderName(spacingMode);
2179 
2180 		if (triOrQuad)
2181 		{
2182 			invariantOuterEdgeGroup->addChild		(    InvariantOuterEdge::makeOuterEdgeDivisionTest			(testCtx, primSpacName, "", primitiveType, spacingMode));
2183 			invariantTriangleSetGroup->addChild		(PrimitiveSetInvariance::makeInvariantTriangleSetTest		(testCtx, primSpacName, "", primitiveType, spacingMode));
2184 			invariantInnerTriangleSetGroup->addChild(PrimitiveSetInvariance::makeInvariantInnerTriangleSetTest	(testCtx, primSpacName, "", primitiveType, spacingMode));
2185 			invariantOuterTriangleSetGroup->addChild(PrimitiveSetInvariance::makeInvariantOuterTriangleSetTest	(testCtx, primSpacName, "", primitiveType, spacingMode));
2186 		}
2187 
2188 		for (int windingNdx = 0; windingNdx < WINDING_LAST; ++windingNdx)
2189 		for (int usePointModeNdx = 0; usePointModeNdx <= 1; ++usePointModeNdx)
2190 		{
2191 			const Winding		winding					= static_cast<Winding>(windingNdx);
2192 			const bool			usePointMode			= (usePointModeNdx != 0);
2193 			const std::string	primSpacWindPointName	= primSpacName + "_" + getWindingShaderName(winding) + (usePointMode ? "_point_mode" : "");
2194 
2195 			invariantPrimitiveSetGroup->addChild		(PrimitiveSetInvariance::makeInvariantPrimitiveSetTest	(testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
2196 			tessCoordComponentRangeGroup->addChild		(    TessCoordComponent::makeTessCoordRangeTest			(testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
2197 			oneMinusTessCoordComponentGroup->addChild	(    TessCoordComponent::makeOneMinusTessCoordTest		(testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
2198 			symmetricOuterEdgeGroup->addChild			(    InvariantOuterEdge::makeSymmetricOuterEdgeTest		(testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
2199 
2200 			if (triOrQuad)
2201 				outerEdgeVertexSetIndexIndependenceGroup->addChild(InvariantOuterEdge::makeOuterEdgeIndexIndependenceTest(testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
2202 		}
2203 	}
2204 
2205 	group->addChild(invariantPrimitiveSetGroup.release());
2206 	group->addChild(invariantOuterEdgeGroup.release());
2207 	group->addChild(symmetricOuterEdgeGroup.release());
2208 	group->addChild(outerEdgeVertexSetIndexIndependenceGroup.release());
2209 	group->addChild(invariantTriangleSetGroup.release());
2210 	group->addChild(invariantInnerTriangleSetGroup.release());
2211 	group->addChild(invariantOuterTriangleSetGroup.release());
2212 	group->addChild(tessCoordComponentRangeGroup.release());
2213 	group->addChild(oneMinusTessCoordComponentGroup.release());
2214 
2215 	return group.release();
2216 }
2217 
2218 } // tessellation
2219 } // vkt
2220