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