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