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 Coordinates Tests
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktTessellationCoordinatesTests.hpp"
26 #include "vktTestCaseUtil.hpp"
27 #include "vktTessellationUtil.hpp"
28 
29 #include "tcuTestLog.hpp"
30 #include "tcuRGBA.hpp"
31 #include "tcuSurface.hpp"
32 #include "tcuTextureUtil.hpp"
33 #include "tcuVectorUtil.hpp"
34 
35 #include "vkDefs.hpp"
36 #include "vkBarrierUtil.hpp"
37 #include "vkQueryUtil.hpp"
38 #include "vkBuilderUtil.hpp"
39 #include "vkTypeUtil.hpp"
40 #include "vkCmdUtil.hpp"
41 #include "vkObjUtil.hpp"
42 
43 #include "deUniquePtr.hpp"
44 
45 #include <string>
46 #include <vector>
47 
48 namespace vkt
49 {
50 namespace tessellation
51 {
52 
53 using namespace vk;
54 
55 namespace
56 {
57 
58 template <typename T>
59 class SizeLessThan
60 {
61 public:
operator ()(const T & a,const T & b) const62 	bool operator() (const T& a, const T& b) const { return a.size() < b.size(); }
63 };
64 
getCaseName(const TessPrimitiveType primitiveType,const SpacingMode spacingMode)65 std::string getCaseName (const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
66 {
67 	std::ostringstream str;
68 	str << getTessPrimitiveTypeShaderName(primitiveType) << "_" << getSpacingModeShaderName(spacingMode);
69 	return str.str();
70 }
71 
genTessLevelCases(const TessPrimitiveType primitiveType,const SpacingMode spacingMode)72 std::vector<TessLevels> genTessLevelCases (const TessPrimitiveType	primitiveType,
73 										   const SpacingMode		spacingMode)
74 {
75 	static const TessLevels rawTessLevelCases[] =
76 	{
77 		{ { 1.0f,	1.0f	},	{ 1.0f,		1.0f,	1.0f,	1.0f	} },
78 		{ { 63.0f,	24.0f	},	{ 15.0f,	42.0f,	10.0f,	12.0f	} },
79 		{ { 3.0f,	2.0f	},	{ 6.0f,		8.0f,	7.0f,	9.0f	} },
80 		{ { 4.0f,	6.0f	},	{ 2.0f,		3.0f,	1.0f,	4.0f	} },
81 		{ { 2.0f,	2.0f	},	{ 6.0f,		8.0f,	7.0f,	9.0f	} },
82 		{ { 5.0f,	6.0f	},	{ 1.0f,		1.0f,	1.0f,	1.0f	} },
83 		{ { 1.0f,	6.0f	},	{ 2.0f,		3.0f,	1.0f,	4.0f	} },
84 		{ { 5.0f,	1.0f	},	{ 2.0f,		3.0f,	1.0f,	4.0f	} },
85 		{ { 5.2f,	1.6f	},	{ 2.9f,		3.4f,	1.5f,	4.1f	} }
86 	};
87 
88 	if (spacingMode == SPACINGMODE_EQUAL)
89 		return std::vector<TessLevels>(DE_ARRAY_BEGIN(rawTessLevelCases), DE_ARRAY_END(rawTessLevelCases));
90 	else
91 	{
92 		std::vector<TessLevels> result;
93 		result.reserve(DE_LENGTH_OF_ARRAY(rawTessLevelCases));
94 
95 		for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(rawTessLevelCases); ++tessLevelCaseNdx)
96 		{
97 			TessLevels curTessLevelCase = rawTessLevelCases[tessLevelCaseNdx];
98 
99 			float* const inner = &curTessLevelCase.inner[0];
100 			float* const outer = &curTessLevelCase.outer[0];
101 
102 			for (int j = 0; j < 2; ++j) inner[j] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[j]));
103 			for (int j = 0; j < 4; ++j) outer[j] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, outer[j]));
104 
105 			if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
106 			{
107 				if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f)
108 				{
109 					if (inner[0] == 1.0f)
110 						inner[0] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[0] + 0.1f));
111 				}
112 			}
113 			else if (primitiveType == TESSPRIMITIVETYPE_QUADS)
114 			{
115 				if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f || outer[3] > 1.0f)
116 				{
117 					if (inner[0] == 1.0f) inner[0] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[0] + 0.1f));
118 					if (inner[1] == 1.0f) inner[1] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[1] + 0.1f));
119 				}
120 			}
121 
122 			result.push_back(curTessLevelCase);
123 		}
124 
125 		DE_ASSERT(static_cast<int>(result.size()) == DE_LENGTH_OF_ARRAY(rawTessLevelCases));
126 		return result;
127 	}
128 }
129 
generateReferenceTessCoords(const TessPrimitiveType primitiveType,const SpacingMode spacingMode,const float * innerLevels,const float * outerLevels)130 std::vector<tcu::Vec3> generateReferenceTessCoords (const TessPrimitiveType	primitiveType,
131 													const SpacingMode		spacingMode,
132 													const float*			innerLevels,
133 													const float*			outerLevels)
134 {
135 	if (isPatchDiscarded(primitiveType, outerLevels))
136 		return std::vector<tcu::Vec3>();
137 
138 	switch (primitiveType)
139 	{
140 		case TESSPRIMITIVETYPE_TRIANGLES:
141 		{
142 			int inner;
143 			int outer[3];
144 			getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
145 
146 			if (spacingMode != SPACINGMODE_EQUAL)
147 			{
148 				// \note For fractional spacing modes, exact results are implementation-defined except in special cases.
149 				DE_ASSERT(de::abs(innerLevels[0] - static_cast<float>(inner)) < 0.001f);
150 				for (int i = 0; i < 3; ++i)
151 					DE_ASSERT(de::abs(outerLevels[i] - static_cast<float>(outer[i])) < 0.001f);
152 				DE_ASSERT(inner > 1 || (outer[0] == 1 && outer[1] == 1 && outer[2] == 1));
153 			}
154 
155 			return generateReferenceTriangleTessCoords(spacingMode, inner, outer[0], outer[1], outer[2]);
156 		}
157 
158 		case TESSPRIMITIVETYPE_QUADS:
159 		{
160 			int inner[2];
161 			int outer[4];
162 			getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
163 
164 			if (spacingMode != SPACINGMODE_EQUAL)
165 			{
166 				// \note For fractional spacing modes, exact results are implementation-defined except in special cases.
167 				for (int i = 0; i < 2; ++i)
168 					DE_ASSERT(de::abs(innerLevels[i] - static_cast<float>(inner[i])) < 0.001f);
169 				for (int i = 0; i < 4; ++i)
170 					DE_ASSERT(de::abs(outerLevels[i] - static_cast<float>(outer[i])) < 0.001f);
171 
172 				DE_ASSERT((inner[0] > 1 && inner[1] > 1) || (inner[0] == 1 && inner[1] == 1 && outer[0] == 1 && outer[1] == 1 && outer[2] == 1 && outer[3] == 1));
173 			}
174 
175 			return generateReferenceQuadTessCoords(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]);
176 		}
177 
178 		case TESSPRIMITIVETYPE_ISOLINES:
179 		{
180 			int outer[2];
181 			getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
182 
183 			if (spacingMode != SPACINGMODE_EQUAL)
184 			{
185 				// \note For fractional spacing modes, exact results are implementation-defined except in special cases.
186 				DE_ASSERT(de::abs(outerLevels[1] - static_cast<float>(outer[1])) < 0.001f);
187 			}
188 
189 			return generateReferenceIsolineTessCoords(outer[0], outer[1]);
190 		}
191 
192 		default:
193 			DE_ASSERT(false);
194 			return std::vector<tcu::Vec3>();
195 	}
196 }
197 
drawPoint(tcu::Surface & dst,const int centerX,const int centerY,const tcu::RGBA & color,const int size)198 void drawPoint (tcu::Surface& dst, const int centerX, const int centerY, const tcu::RGBA& color, const int size)
199 {
200 	const int width		= dst.getWidth();
201 	const int height	= dst.getHeight();
202 	DE_ASSERT(de::inBounds(centerX, 0, width) && de::inBounds(centerY, 0, height));
203 	DE_ASSERT(size > 0);
204 
205 	for (int yOff = -((size-1)/2); yOff <= size/2; ++yOff)
206 	for (int xOff = -((size-1)/2); xOff <= size/2; ++xOff)
207 	{
208 		const int pixX = centerX + xOff;
209 		const int pixY = centerY + yOff;
210 		if (de::inBounds(pixX, 0, width) && de::inBounds(pixY, 0, height))
211 			dst.setPixel(pixX, pixY, color);
212 	}
213 }
214 
drawTessCoordPoint(tcu::Surface & dst,const TessPrimitiveType primitiveType,const tcu::Vec3 & pt,const tcu::RGBA & color,const int size)215 void drawTessCoordPoint (tcu::Surface& dst, const TessPrimitiveType primitiveType, const tcu::Vec3& pt, const tcu::RGBA& color, const int size)
216 {
217 	// \note These coordinates should match the description in the log message in TessCoordTestInstance::iterate.
218 
219 	static const tcu::Vec2 triangleCorners[3] =
220 	{
221 		tcu::Vec2(0.95f, 0.95f),
222 		tcu::Vec2(0.5f,  0.95f - 0.9f*deFloatSqrt(3.0f/4.0f)),
223 		tcu::Vec2(0.05f, 0.95f)
224 	};
225 
226 	static const float quadIsolineLDRU[4] =
227 	{
228 		0.1f, 0.9f, 0.9f, 0.1f
229 	};
230 
231 	const tcu::Vec2 dstPos = primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? pt.x()*triangleCorners[0]
232 																		  + pt.y()*triangleCorners[1]
233 																		  + pt.z()*triangleCorners[2]
234 
235 					  : primitiveType == TESSPRIMITIVETYPE_QUADS ||
236 						primitiveType == TESSPRIMITIVETYPE_ISOLINES ? tcu::Vec2((1.0f - pt.x())*quadIsolineLDRU[0] + pt.x()*quadIsolineLDRU[2],
237 																			    (1.0f - pt.y())*quadIsolineLDRU[1] + pt.y()*quadIsolineLDRU[3])
238 
239 					  : tcu::Vec2(-1.0f);
240 
241 	drawPoint(dst,
242 			  static_cast<int>(dstPos.x() * (float)dst.getWidth()),
243 			  static_cast<int>(dstPos.y() * (float)dst.getHeight()),
244 			  color,
245 			  size);
246 }
247 
drawTessCoordVisualization(tcu::Surface & dst,const TessPrimitiveType primitiveType,const std::vector<tcu::Vec3> & coords)248 void drawTessCoordVisualization (tcu::Surface& dst, const TessPrimitiveType primitiveType, const std::vector<tcu::Vec3>& coords)
249 {
250 	const int imageWidth  = 256;
251 	const int imageHeight = 256;
252 	dst.setSize(imageWidth, imageHeight);
253 
254 	tcu::clear(dst.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
255 
256 	for (int i = 0; i < static_cast<int>(coords.size()); ++i)
257 		drawTessCoordPoint(dst, primitiveType, coords[i], tcu::RGBA::white(), 2);
258 }
259 
vec3XLessThan(const tcu::Vec3 & a,const tcu::Vec3 & b)260 inline bool vec3XLessThan (const tcu::Vec3& a, const tcu::Vec3& b)
261 {
262 	return a.x() < b.x();
263 }
264 
binarySearchFirstVec3WithXAtLeast(const std::vector<tcu::Vec3> & sorted,float x)265 int binarySearchFirstVec3WithXAtLeast (const std::vector<tcu::Vec3>& sorted, float x)
266 {
267 	const tcu::Vec3 ref(x, 0.0f, 0.0f);
268 	const std::vector<tcu::Vec3>::const_iterator first = std::lower_bound(sorted.begin(), sorted.end(), ref, vec3XLessThan);
269 	if (first == sorted.end())
270 		return -1;
271 	return static_cast<int>(std::distance(sorted.begin(), first));
272 }
273 
274 // Check that all points in subset are (approximately) present also in superset.
oneWayComparePointSets(tcu::TestLog & log,tcu::Surface & errorDst,const TessPrimitiveType primitiveType,const std::vector<tcu::Vec3> & subset,const std::vector<tcu::Vec3> & superset,const char * subsetName,const char * supersetName,const tcu::RGBA & errorColor)275 bool oneWayComparePointSets (tcu::TestLog&					log,
276 							 tcu::Surface&					errorDst,
277 							 const TessPrimitiveType		primitiveType,
278 							 const std::vector<tcu::Vec3>&	subset,
279 							 const std::vector<tcu::Vec3>&	superset,
280 							 const char*					subsetName,
281 							 const char*					supersetName,
282 							 const tcu::RGBA&				errorColor)
283 {
284 	const std::vector<tcu::Vec3> supersetSorted		 = sorted(superset, vec3XLessThan);
285 	const float					 epsilon			 = 0.01f;
286 	const int					 maxNumFailurePrints = 5;
287 	int							 numFailuresDetected = 0;
288 
289 	for (int subNdx = 0; subNdx < static_cast<int>(subset.size()); ++subNdx)
290 	{
291 		const tcu::Vec3& subPt = subset[subNdx];
292 
293 		bool matchFound = false;
294 
295 		{
296 			// Binary search the index of the first point in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range.
297 			const tcu::Vec3	matchMin			= subPt - epsilon;
298 			const tcu::Vec3	matchMax			= subPt + epsilon;
299 			const int		firstCandidateNdx	= binarySearchFirstVec3WithXAtLeast(supersetSorted, matchMin.x());
300 
301 			if (firstCandidateNdx >= 0)
302 			{
303 				// Compare subPt to all points in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range.
304 				for (int superNdx = firstCandidateNdx; superNdx < static_cast<int>(supersetSorted.size()) && supersetSorted[superNdx].x() <= matchMax.x(); ++superNdx)
305 				{
306 					const tcu::Vec3& superPt = supersetSorted[superNdx];
307 
308 					if (tcu::boolAll(tcu::greaterThanEqual	(superPt, matchMin)) &&
309 						tcu::boolAll(tcu::lessThanEqual		(superPt, matchMax)))
310 					{
311 						matchFound = true;
312 						break;
313 					}
314 				}
315 			}
316 		}
317 
318 		if (!matchFound)
319 		{
320 			++numFailuresDetected;
321 			if (numFailuresDetected < maxNumFailurePrints)
322 				log << tcu::TestLog::Message << "Failure: no matching " << supersetName << " point found for " << subsetName << " point " << subPt << tcu::TestLog::EndMessage;
323 			else if (numFailuresDetected == maxNumFailurePrints)
324 				log << tcu::TestLog::Message << "Note: More errors follow" << tcu::TestLog::EndMessage;
325 
326 			drawTessCoordPoint(errorDst, primitiveType, subPt, errorColor, 4);
327 		}
328 	}
329 
330 	return numFailuresDetected == 0;
331 }
332 
333 //! Returns true on matching coordinate sets.
compareTessCoords(tcu::TestLog & log,TessPrimitiveType primitiveType,const std::vector<tcu::Vec3> & refCoords,const std::vector<tcu::Vec3> & resCoords)334 bool compareTessCoords (tcu::TestLog&					log,
335 						TessPrimitiveType				primitiveType,
336 						const std::vector<tcu::Vec3>&	refCoords,
337 						const std::vector<tcu::Vec3>&	resCoords)
338 {
339 	tcu::Surface	refVisual;
340 	tcu::Surface	resVisual;
341 	bool			success = true;
342 
343 	drawTessCoordVisualization(refVisual, primitiveType, refCoords);
344 	drawTessCoordVisualization(resVisual, primitiveType, resCoords);
345 
346 	// Check that all points in reference also exist in result.
347 	success = oneWayComparePointSets(log, refVisual, primitiveType, refCoords, resCoords, "reference", "result", tcu::RGBA::blue()) && success;
348 	// Check that all points in result also exist in reference.
349 	success = oneWayComparePointSets(log, resVisual, primitiveType, resCoords, refCoords, "result", "reference", tcu::RGBA::red()) && success;
350 
351 	if (!success)
352 	{
353 		log << tcu::TestLog::Message << "Note: in the following reference visualization, points that are missing in result point set are blue (if any)" << tcu::TestLog::EndMessage
354 			<< tcu::TestLog::Image("RefTessCoordVisualization", "Reference tessCoord visualization", refVisual)
355 			<< tcu::TestLog::Message << "Note: in the following result visualization, points that are missing in reference point set are red (if any)" << tcu::TestLog::EndMessage;
356 	}
357 
358 	log << tcu::TestLog::Image("ResTessCoordVisualization", "Result tessCoord visualization", resVisual);
359 
360 	return success;
361 }
362 
363 class TessCoordTest : public TestCase
364 {
365 public:
366 								TessCoordTest	(tcu::TestContext&			testCtx,
367 												 const TessPrimitiveType	primitiveType,
368 												 const SpacingMode			spacingMode);
369 
370 	void						initPrograms	(SourceCollections&			programCollection) const;
371 	TestInstance*				createInstance	(Context&					context) const;
372 
373 private:
374 	const TessPrimitiveType		m_primitiveType;
375 	const SpacingMode			m_spacingMode;
376 };
377 
TessCoordTest(tcu::TestContext & testCtx,const TessPrimitiveType primitiveType,const SpacingMode spacingMode)378 TessCoordTest::TessCoordTest (tcu::TestContext&			testCtx,
379 							  const TessPrimitiveType	primitiveType,
380 							  const SpacingMode			spacingMode)
381 	: TestCase			(testCtx, getCaseName(primitiveType, spacingMode), "")
382 	, m_primitiveType	(primitiveType)
383 	, m_spacingMode		(spacingMode)
384 {
385 }
386 
initPrograms(SourceCollections & programCollection) const387 void TessCoordTest::initPrograms (SourceCollections& programCollection) const
388 {
389 	// Vertex shader - no inputs
390 	{
391 		std::ostringstream src;
392 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
393 			<< "\n"
394 			<< "void main (void)\n"
395 			<< "{\n"
396 			<< "}\n";
397 
398 		programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
399 	}
400 
401 	// Tessellation control shader
402 	{
403 		std::ostringstream src;
404 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
405 			<< "#extension GL_EXT_tessellation_shader : require\n"
406 			<< "\n"
407 			<< "layout(vertices = 1) out;\n"
408 			<< "\n"
409 			<< "layout(set = 0, binding = 0, std430) readonly restrict buffer TessLevels {\n"
410 			<< "    float inner0;\n"
411 			<< "    float inner1;\n"
412 			<< "    float outer0;\n"
413 			<< "    float outer1;\n"
414 			<< "    float outer2;\n"
415 			<< "    float outer3;\n"
416 			<< "} sb_levels;\n"
417 			<< "\n"
418 			<< "void main (void)\n"
419 			<< "{\n"
420 			<< "    gl_TessLevelInner[0] = sb_levels.inner0;\n"
421 			<< "    gl_TessLevelInner[1] = sb_levels.inner1;\n"
422 			<< "\n"
423 			<< "    gl_TessLevelOuter[0] = sb_levels.outer0;\n"
424 			<< "    gl_TessLevelOuter[1] = sb_levels.outer1;\n"
425 			<< "    gl_TessLevelOuter[2] = sb_levels.outer2;\n"
426 			<< "    gl_TessLevelOuter[3] = sb_levels.outer3;\n"
427 			<< "}\n";
428 
429 		programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
430 	}
431 
432 	// Tessellation evaluation shader
433 	{
434 		std::ostringstream src;
435 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
436 			<< "#extension GL_EXT_tessellation_shader : require\n"
437 			<< "\n"
438 			<< "layout(" << getTessPrimitiveTypeShaderName(m_primitiveType) << ", "
439 						 << getSpacingModeShaderName(m_spacingMode) << ", point_mode) in;\n"
440 			<< "\n"
441 			<< "layout(set = 0, binding = 1, std430) coherent restrict buffer Output {\n"
442 			<< "    int  numInvocations;\n"
443 			<< "    vec3 tessCoord[];\n"		// alignment is 16 bytes, same as vec4
444 			<< "} sb_out;\n"
445 			<< "\n"
446 			<< "void main (void)\n"
447 			<< "{\n"
448 			<< "    int index = atomicAdd(sb_out.numInvocations, 1);\n"
449 			<< "    sb_out.tessCoord[index] = gl_TessCoord;\n"
450 			<< "}\n";
451 
452 		programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
453 	}
454 }
455 
456 class TessCoordTestInstance : public TestInstance
457 {
458 public:
459 								TessCoordTestInstance (Context&					context,
460 													   const TessPrimitiveType	primitiveType,
461 													   const SpacingMode		spacingMode);
462 
463 	tcu::TestStatus				iterate				  (void);
464 
465 private:
466 	const TessPrimitiveType		m_primitiveType;
467 	const SpacingMode			m_spacingMode;
468 };
469 
TessCoordTestInstance(Context & context,const TessPrimitiveType primitiveType,const SpacingMode spacingMode)470 TessCoordTestInstance::TessCoordTestInstance (Context&					context,
471 											  const TessPrimitiveType	primitiveType,
472 											  const SpacingMode			spacingMode)
473 	: TestInstance		(context)
474 	, m_primitiveType	(primitiveType)
475 	, m_spacingMode		(spacingMode)
476 {
477 }
478 
iterate(void)479 tcu::TestStatus TessCoordTestInstance::iterate (void)
480 {
481 	const DeviceInterface&	vk					= m_context.getDeviceInterface();
482 	const VkDevice			device				= m_context.getDevice();
483 	const VkQueue			queue				= m_context.getUniversalQueue();
484 	const deUint32			queueFamilyIndex	= m_context.getUniversalQueueFamilyIndex();
485 	Allocator&				allocator			= m_context.getDefaultAllocator();
486 
487 	// Test data
488 
489 	const std::vector<TessLevels>		 tessLevelCases			= genTessLevelCases(m_primitiveType, m_spacingMode);
490 	std::vector<std::vector<tcu::Vec3> > allReferenceTessCoords	(tessLevelCases.size());
491 
492 	for (deUint32 i = 0; i < tessLevelCases.size(); ++i)
493 		allReferenceTessCoords[i] = generateReferenceTessCoords(m_primitiveType, m_spacingMode, &tessLevelCases[i].inner[0], &tessLevelCases[i].outer[0]);
494 
495 	const size_t maxNumVertices = static_cast<int>(std::max_element(allReferenceTessCoords.begin(), allReferenceTessCoords.end(), SizeLessThan<std::vector<tcu::Vec3> >())->size());
496 
497 	// Input buffer: tessellation levels. Data is filled in later.
498 
499 	const Buffer tessLevelsBuffer(vk, device, allocator,
500 		makeBufferCreateInfo(sizeof(TessLevels), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
501 
502 	// Output buffer: number of invocations + padding + tessellation coordinates. Initialized later.
503 
504 	const int          resultBufferTessCoordsOffset	 = 4 * (int)sizeof(deInt32);
505 	const int          extraneousVertices			 = 16;	// allow some room for extraneous vertices from duplicate shader invocations (number is arbitrary)
506 	const VkDeviceSize resultBufferSizeBytes		 = resultBufferTessCoordsOffset + (maxNumVertices + extraneousVertices)*sizeof(tcu::Vec4);
507 	const Buffer       resultBuffer					 (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
508 
509 	// Descriptors
510 
511 	const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
512 		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)
513 		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
514 		.build(vk, device));
515 
516 	const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
517 		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
518 		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
519 		.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
520 
521 	const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
522 
523 	const VkDescriptorBufferInfo tessLevelsBufferInfo = makeDescriptorBufferInfo(tessLevelsBuffer.get(), 0ull, sizeof(TessLevels));
524 	const VkDescriptorBufferInfo resultBufferInfo     = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
525 
526 	DescriptorSetUpdateBuilder()
527 		.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo)
528 		.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
529 		.update(vk, device);
530 
531 	// Pipeline: set up vertex processing without rasterization
532 
533 	const Unique<VkRenderPass>		renderPass		(makeRenderPassWithoutAttachments (vk, device));
534 	const Unique<VkFramebuffer>		framebuffer		(makeFramebufferWithoutAttachments(vk, device, *renderPass));
535 	const Unique<VkPipelineLayout>	pipelineLayout	(makePipelineLayout(vk, device, *descriptorSetLayout));
536 	const Unique<VkCommandPool>		cmdPool			(makeCommandPool(vk, device, queueFamilyIndex));
537 	const Unique<VkCommandBuffer>	cmdBuffer		(allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
538 
539 	const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
540 		.setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT,					m_context.getBinaryCollection().get("vert"), DE_NULL)
541 		.setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,	m_context.getBinaryCollection().get("tesc"), DE_NULL)
542 		.setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
543 		.build    (vk, device, *pipelineLayout, *renderPass));
544 
545 	deUint32 numPassedCases = 0;
546 
547 	// Repeat the test for all tessellation coords cases
548 	for (deUint32 tessLevelCaseNdx = 0; tessLevelCaseNdx < tessLevelCases.size(); ++tessLevelCaseNdx)
549 	{
550 		m_context.getTestContext().getLog()
551 			<< tcu::TestLog::Message
552 			<< "Tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx], m_primitiveType)
553 			<< tcu::TestLog::EndMessage;
554 
555 		// Upload tessellation levels data to the input buffer
556 		{
557 			const Allocation& alloc = tessLevelsBuffer.getAllocation();
558 			TessLevels* const bufferTessLevels = static_cast<TessLevels*>(alloc.getHostPtr());
559 			*bufferTessLevels = tessLevelCases[tessLevelCaseNdx];
560 			flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(TessLevels));
561 		}
562 
563 		// Clear the results buffer
564 		{
565 			const Allocation& alloc = resultBuffer.getAllocation();
566 			deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
567 			flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
568 		}
569 
570 		// Reset the command buffer and begin recording.
571 		beginCommandBuffer(vk, *cmdBuffer);
572 		beginRenderPassWithRasterizationDisabled(vk, *cmdBuffer, *renderPass, *framebuffer);
573 
574 		vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
575 		vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
576 
577 		// Process a single abstract vertex.
578 		vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
579 		endRenderPass(vk, *cmdBuffer);
580 
581 		{
582 			const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
583 				VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes);
584 
585 			vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
586 				0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
587 		}
588 
589 		endCommandBuffer(vk, *cmdBuffer);
590 		submitCommandsAndWait(vk, device, queue, *cmdBuffer);
591 
592 		// Verify results
593 		{
594 			const Allocation& resultAlloc = resultBuffer.getAllocation();
595 			invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
596 
597 			const deInt32					numResults			= *static_cast<deInt32*>(resultAlloc.getHostPtr());
598 			const std::vector<tcu::Vec3>	resultTessCoords    = readInterleavedData<tcu::Vec3>(numResults, resultAlloc.getHostPtr(), resultBufferTessCoordsOffset, sizeof(tcu::Vec4));
599 			const std::vector<tcu::Vec3>&	referenceTessCoords = allReferenceTessCoords[tessLevelCaseNdx];
600 			const int						numExpectedResults  = static_cast<int>(referenceTessCoords.size());
601 			tcu::TestLog&					log					= m_context.getTestContext().getLog();
602 
603 			if (numResults < numExpectedResults)
604 			{
605 				log << tcu::TestLog::Message
606 					<< "Failure: generated " << numResults << " coordinates, but the expected reference value is " << numExpectedResults
607 					<< tcu::TestLog::EndMessage;
608 			}
609 			else if (numResults == numExpectedResults)
610 				log << tcu::TestLog::Message << "Note: generated " << numResults << " tessellation coordinates" << tcu::TestLog::EndMessage;
611 			else
612 			{
613 				log << tcu::TestLog::Message
614 					<< "Note: generated " << numResults << " coordinates (out of which " << numExpectedResults << " must be unique)"
615 					<< tcu::TestLog::EndMessage;
616 			}
617 
618 			if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
619 				log << tcu::TestLog::Message << "Note: in the following visualization(s), the u=1, v=1, w=1 corners are at the right, top, and left corners, respectively" << tcu::TestLog::EndMessage;
620 			else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS || m_primitiveType == TESSPRIMITIVETYPE_ISOLINES)
621 				log << tcu::TestLog::Message << "Note: in the following visualization(s), u and v coordinate go left-to-right and bottom-to-top, respectively" << tcu::TestLog::EndMessage;
622 			else
623 				DE_ASSERT(false);
624 
625 			if (compareTessCoords(log, m_primitiveType, referenceTessCoords, resultTessCoords) && (numResults >= numExpectedResults))
626 				++numPassedCases;
627 		}
628 	}  // for tessLevelCaseNdx
629 
630 	return (numPassedCases == tessLevelCases.size() ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Some cases have failed"));
631 }
632 
createInstance(Context & context) const633 TestInstance* TessCoordTest::createInstance (Context& context) const
634 {
635 	requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
636 
637 	return new TessCoordTestInstance(context, m_primitiveType, m_spacingMode);
638 }
639 
640 } // anonymous
641 
642 //! Based on dEQP-GLES31.functional.tessellation.tesscoord.*
643 //! \note Transform feedback is replaced with SSBO. Because of that, this version allows duplicate coordinates from shader invocations.
644 //! The test still fails if not enough coordinates are generated, or if coordinates don't match the reference data.
createCoordinatesTests(tcu::TestContext & testCtx)645 tcu::TestCaseGroup* createCoordinatesTests (tcu::TestContext& testCtx)
646 {
647 	de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "tesscoord", "Tessellation coordinates tests"));
648 
649 	for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; ++primitiveTypeNdx)
650 	for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
651 		group->addChild(new TessCoordTest(testCtx, (TessPrimitiveType)primitiveTypeNdx, (SpacingMode)spacingModeNdx));
652 
653 	return group.release();
654 }
655 
656 } // tessellation
657 } // vkt
658