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