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 User Defined IO Tests
     23  *//*--------------------------------------------------------------------*/
     24 
     25 #include "vktTessellationUserDefinedIO.hpp"
     26 #include "vktTestCaseUtil.hpp"
     27 #include "vktTessellationUtil.hpp"
     28 
     29 #include "tcuTestLog.hpp"
     30 #include "tcuImageCompare.hpp"
     31 #include "tcuImageIO.hpp"
     32 
     33 #include "gluVarType.hpp"
     34 #include "gluVarTypeUtil.hpp"
     35 
     36 #include "vkDefs.hpp"
     37 #include "vkQueryUtil.hpp"
     38 #include "vkImageUtil.hpp"
     39 #include "vkBuilderUtil.hpp"
     40 #include "vkTypeUtil.hpp"
     41 
     42 #include "deUniquePtr.hpp"
     43 #include "deSharedPtr.hpp"
     44 
     45 namespace vkt
     46 {
     47 namespace tessellation
     48 {
     49 
     50 using namespace vk;
     51 
     52 namespace
     53 {
     54 
     55 enum Constants
     56 {
     57 	NUM_PER_PATCH_BLOCKS		= 2,
     58 	NUM_PER_PATCH_ARRAY_ELEMS	= 3,
     59 	NUM_OUTPUT_VERTICES			= 5,
     60 	NUM_TESS_LEVELS				= 6,
     61 	MAX_TESSELLATION_PATCH_SIZE = 32,
     62 	RENDER_SIZE					= 256,
     63 };
     64 
     65 enum IOType
     66 {
     67 	IO_TYPE_PER_PATCH = 0,
     68 	IO_TYPE_PER_PATCH_ARRAY,
     69 	IO_TYPE_PER_PATCH_BLOCK,
     70 	IO_TYPE_PER_PATCH_BLOCK_ARRAY,
     71 	IO_TYPE_PER_VERTEX,
     72 	IO_TYPE_PER_VERTEX_BLOCK,
     73 
     74 	IO_TYPE_LAST
     75 };
     76 
     77 enum VertexIOArraySize
     78 {
     79 	VERTEX_IO_ARRAY_SIZE_IMPLICIT = 0,
     80 	VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN,		//!< Use gl_MaxPatchVertices as size for per-vertex input array.
     81 	VERTEX_IO_ARRAY_SIZE_EXPLICIT_SPEC_MIN,				//!< Minimum maxTessellationPatchSize required by the spec.
     82 
     83 	VERTEX_IO_ARRAY_SIZE_LAST
     84 };
     85 
     86 struct CaseDefinition
     87 {
     88 	TessPrimitiveType	primitiveType;
     89 	IOType				ioType;
     90 	VertexIOArraySize	vertexIOArraySize;
     91 	std::string			referenceImagePath;
     92 };
     93 
     94 typedef std::string (*BasicTypeVisitFunc)(const std::string& name, glu::DataType type, int indentationDepth); //!< See glslTraverseBasicTypes below.
     95 
     96 class TopLevelObject
     97 {
     98 public:
     99 	virtual					~TopLevelObject					(void) {}
    100 
    101 	virtual std::string		name							(void) const = 0;
    102 	virtual std::string		declare							(void) const = 0;
    103 	virtual std::string		declareArray					(const std::string& arraySizeExpr) const = 0;
    104 	virtual std::string		glslTraverseBasicTypeArray		(const int numArrayElements, //!< If negative, traverse just array[gl_InvocationID], not all indices.
    105 															 const int indentationDepth,
    106 															 BasicTypeVisitFunc) const = 0;
    107 	virtual std::string		glslTraverseBasicType			(const int indentationDepth,
    108 															 BasicTypeVisitFunc) const = 0;
    109 	virtual int				numBasicSubobjectsInElementType	(void) const = 0;
    110 	virtual std::string		basicSubobjectAtIndex			(const int index, const int arraySize) const = 0;
    111 };
    112 
    113 std::string glslTraverseBasicTypes (const std::string&			rootName,
    114 									const glu::VarType&			rootType,
    115 									const int					arrayNestingDepth,
    116 									const int					indentationDepth,
    117 									const BasicTypeVisitFunc	visit)
    118 {
    119 	if (rootType.isBasicType())
    120 		return visit(rootName, rootType.getBasicType(), indentationDepth);
    121 	else if (rootType.isArrayType())
    122 	{
    123 		const std::string indentation	= std::string(indentationDepth, '\t');
    124 		const std::string loopIndexName	= "i" + de::toString(arrayNestingDepth);
    125 		const std::string arrayLength	= de::toString(rootType.getArraySize());
    126 		return indentation + "for (int " + loopIndexName + " = 0; " + loopIndexName + " < " + de::toString(rootType.getArraySize()) + "; ++" + loopIndexName + ")\n" +
    127 			   indentation + "{\n" +
    128 			   glslTraverseBasicTypes(rootName + "[" + loopIndexName + "]", rootType.getElementType(), arrayNestingDepth+1, indentationDepth+1, visit) +
    129 			   indentation + "}\n";
    130 	}
    131 	else if (rootType.isStructType())
    132 	{
    133 		const glu::StructType&	structType = *rootType.getStructPtr();
    134 		const int				numMembers = structType.getNumMembers();
    135 		std::string				result;
    136 
    137 		for (int membNdx = 0; membNdx < numMembers; ++membNdx)
    138 		{
    139 			const glu::StructMember& member = structType.getMember(membNdx);
    140 			result += glslTraverseBasicTypes(rootName + "." + member.getName(), member.getType(), arrayNestingDepth, indentationDepth, visit);
    141 		}
    142 
    143 		return result;
    144 	}
    145 	else
    146 	{
    147 		DE_ASSERT(false);
    148 		return DE_NULL;
    149 	}
    150 }
    151 
    152 //! Used as the 'visit' argument for glslTraverseBasicTypes.
    153 std::string glslAssignBasicTypeObject (const std::string& name, const glu::DataType type, const int indentationDepth)
    154 {
    155 	const int			scalarSize	= glu::getDataTypeScalarSize(type);
    156 	const std::string	indentation	= std::string(indentationDepth, '\t');
    157 	std::ostringstream	result;
    158 
    159 	result << indentation << name << " = ";
    160 
    161 	if (type != glu::TYPE_FLOAT)
    162 		result << std::string() << glu::getDataTypeName(type) << "(";
    163 	for (int i = 0; i < scalarSize; ++i)
    164 		result << (i > 0 ? ", v+" + de::floatToString(0.8f*(float)i, 1) : "v");
    165 	if (type != glu::TYPE_FLOAT)
    166 		result << ")";
    167 	result << ";\n"
    168 		   << indentation << "v += 0.4;\n";
    169 	return result.str();
    170 }
    171 
    172 //! Used as the 'visit' argument for glslTraverseBasicTypes.
    173 std::string glslCheckBasicTypeObject (const std::string& name, const glu::DataType type, const int indentationDepth)
    174 {
    175 	const int			scalarSize	= glu::getDataTypeScalarSize(type);
    176 	const std::string	indentation	= std::string(indentationDepth, '\t');
    177 	std::ostringstream	result;
    178 
    179 	result << indentation << "allOk = allOk && compare_" << glu::getDataTypeName(type) << "(" << name << ", ";
    180 
    181 	if (type != glu::TYPE_FLOAT)
    182 		result << std::string() << glu::getDataTypeName(type) << "(";
    183 	for (int i = 0; i < scalarSize; ++i)
    184 		result << (i > 0 ? ", v+" + de::floatToString(0.8f*(float)i, 1) : "v");
    185 	if (type != glu::TYPE_FLOAT)
    186 		result << ")";
    187 	result << ");\n"
    188 		   << indentation << "v += 0.4;\n"
    189 		   << indentation << "if (allOk) ++firstFailedInputIndex;\n";
    190 
    191 	return result.str();
    192 }
    193 
    194 int numBasicSubobjectsInElementType (const std::vector<de::SharedPtr<TopLevelObject> >& objects)
    195 {
    196 	int result = 0;
    197 	for (int i = 0; i < static_cast<int>(objects.size()); ++i)
    198 		result += objects[i]->numBasicSubobjectsInElementType();
    199 	return result;
    200 }
    201 
    202 std::string basicSubobjectAtIndex (const int subobjectIndex, const std::vector<de::SharedPtr<TopLevelObject> >& objects, const int topLevelArraySize)
    203 {
    204 	int currentIndex = 0;
    205 	int objectIndex  = 0;
    206 
    207 	for (; currentIndex < subobjectIndex; ++objectIndex)
    208 		currentIndex += objects[objectIndex]->numBasicSubobjectsInElementType() * topLevelArraySize;
    209 
    210 	if (currentIndex > subobjectIndex)
    211 	{
    212 		--objectIndex;
    213 		currentIndex -= objects[objectIndex]->numBasicSubobjectsInElementType() * topLevelArraySize;
    214 	}
    215 
    216 	return objects[objectIndex]->basicSubobjectAtIndex(subobjectIndex - currentIndex, topLevelArraySize);
    217 }
    218 
    219 int numBasicSubobjects (const glu::VarType& type)
    220 {
    221 	if (type.isBasicType())
    222 		return 1;
    223 	else if (type.isArrayType())
    224 		return type.getArraySize()*numBasicSubobjects(type.getElementType());
    225 	else if (type.isStructType())
    226 	{
    227 		const glu::StructType&	structType	= *type.getStructPtr();
    228 		int						result		= 0;
    229 		for (int i = 0; i < structType.getNumMembers(); ++i)
    230 			result += numBasicSubobjects(structType.getMember(i).getType());
    231 		return result;
    232 	}
    233 	else
    234 	{
    235 		DE_ASSERT(false);
    236 		return -1;
    237 	}
    238 }
    239 
    240 class Variable : public TopLevelObject
    241 {
    242 public:
    243 	Variable (const std::string& name_, const glu::VarType& type, const bool isArray)
    244 		: m_name		(name_)
    245 		, m_type		(type)
    246 		, m_isArray		(isArray)
    247 	{
    248 		DE_ASSERT(!type.isArrayType());
    249 	}
    250 
    251 	std::string		name								(void) const { return m_name; }
    252 	std::string		declare								(void) const;
    253 	std::string		declareArray						(const std::string& arraySizeExpr) const;
    254 	std::string		glslTraverseBasicTypeArray			(const int numArrayElements, const int indentationDepth, BasicTypeVisitFunc) const;
    255 	std::string		glslTraverseBasicType				(const int indentationDepth, BasicTypeVisitFunc) const;
    256 	int				numBasicSubobjectsInElementType		(void) const;
    257 	std::string		basicSubobjectAtIndex				(const int index, const int arraySize) const;
    258 
    259 private:
    260 	std::string		m_name;
    261 	glu::VarType	m_type; //!< If this Variable is an array element, m_type is the element type; otherwise just the variable type.
    262 	const bool		m_isArray;
    263 };
    264 
    265 std::string Variable::declare (void) const
    266 {
    267 	DE_ASSERT(!m_isArray);
    268 	return de::toString(glu::declare(m_type, m_name)) + ";\n";
    269 }
    270 
    271 std::string Variable::declareArray (const std::string& sizeExpr) const
    272 {
    273 	DE_ASSERT(m_isArray);
    274 	return de::toString(glu::declare(m_type, m_name)) + "[" + sizeExpr + "];\n";
    275 }
    276 
    277 std::string Variable::glslTraverseBasicTypeArray (const int numArrayElements, const int indentationDepth, BasicTypeVisitFunc visit) const
    278 {
    279 	DE_ASSERT(m_isArray);
    280 
    281 	const bool			traverseAsArray	= numArrayElements >= 0;
    282 	const std::string	traversedName	= m_name + (!traverseAsArray ? "[gl_InvocationID]" : "");
    283 	const glu::VarType	type			= traverseAsArray ? glu::VarType(m_type, numArrayElements) : m_type;
    284 
    285 	return glslTraverseBasicTypes(traversedName, type, 0, indentationDepth, visit);
    286 }
    287 
    288 std::string Variable::glslTraverseBasicType (const int indentationDepth, BasicTypeVisitFunc visit) const
    289 {
    290 	DE_ASSERT(!m_isArray);
    291 	return glslTraverseBasicTypes(m_name, m_type, 0, indentationDepth, visit);
    292 }
    293 
    294 int Variable::numBasicSubobjectsInElementType (void) const
    295 {
    296 	return numBasicSubobjects(m_type);
    297 }
    298 
    299 std::string Variable::basicSubobjectAtIndex (const int subobjectIndex, const int arraySize) const
    300 {
    301 	const glu::VarType	type		 = m_isArray ? glu::VarType(m_type, arraySize) : m_type;
    302 	int					currentIndex = 0;
    303 
    304 	for (glu::BasicTypeIterator basicIt = glu::BasicTypeIterator::begin(&type); basicIt != glu::BasicTypeIterator::end(&type); ++basicIt)
    305 	{
    306 		if (currentIndex == subobjectIndex)
    307 			return m_name + de::toString(glu::TypeAccessFormat(type, basicIt.getPath()));
    308 		++currentIndex;
    309 	}
    310 	DE_ASSERT(false);
    311 	return DE_NULL;
    312 }
    313 
    314 class IOBlock : public TopLevelObject
    315 {
    316 public:
    317 	struct Member
    318 	{
    319 		std::string		name;
    320 		glu::VarType	type;
    321 
    322 		Member (const std::string& n, const glu::VarType& t) : name(n), type(t) {}
    323 	};
    324 
    325 	IOBlock (const std::string& blockName, const std::string& interfaceName, const std::vector<Member>& members)
    326 		: m_blockName		(blockName)
    327 		, m_interfaceName	(interfaceName)
    328 		, m_members			(members)
    329 	{
    330 	}
    331 
    332 	std::string			name								(void) const { return m_interfaceName; }
    333 	std::string			declare								(void) const;
    334 	std::string			declareArray						(const std::string& arraySizeExpr) const;
    335 	std::string			glslTraverseBasicTypeArray			(const int numArrayElements, const int indentationDepth, BasicTypeVisitFunc) const;
    336 	std::string			glslTraverseBasicType				(const int indentationDepth, BasicTypeVisitFunc) const;
    337 	int					numBasicSubobjectsInElementType		(void) const;
    338 	std::string			basicSubobjectAtIndex				(const int index, const int arraySize) const;
    339 
    340 private:
    341 	std::string			m_blockName;
    342 	std::string			m_interfaceName;
    343 	std::vector<Member>	m_members;
    344 };
    345 
    346 std::string IOBlock::declare (void) const
    347 {
    348 	std::ostringstream buf;
    349 
    350 	buf << m_blockName << "\n"
    351 		<< "{\n";
    352 
    353 	for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
    354 		buf << "\t" << glu::declare(m_members[i].type, m_members[i].name) << ";\n";
    355 
    356 	buf << "} " << m_interfaceName << ";\n";
    357 	return buf.str();
    358 }
    359 
    360 std::string IOBlock::declareArray (const std::string& sizeExpr) const
    361 {
    362 	std::ostringstream buf;
    363 
    364 	buf << m_blockName << "\n"
    365 		<< "{\n";
    366 
    367 	for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
    368 		buf << "\t" << glu::declare(m_members[i].type, m_members[i].name) << ";\n";
    369 
    370 	buf << "} " << m_interfaceName << "[" << sizeExpr << "];\n";
    371 	return buf.str();
    372 }
    373 
    374 std::string IOBlock::glslTraverseBasicTypeArray (const int numArrayElements, const int indentationDepth, BasicTypeVisitFunc visit) const
    375 {
    376 	if (numArrayElements >= 0)
    377 	{
    378 		const std::string	indentation = std::string(indentationDepth, '\t');
    379 		std::ostringstream	result;
    380 
    381 		result << indentation << "for (int i0 = 0; i0 < " << numArrayElements << "; ++i0)\n"
    382 			   << indentation << "{\n";
    383 		for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
    384 			result << glslTraverseBasicTypes(m_interfaceName + "[i0]." + m_members[i].name, m_members[i].type, 1, indentationDepth + 1, visit);
    385 		result << indentation + "}\n";
    386 		return result.str();
    387 	}
    388 	else
    389 	{
    390 		std::ostringstream result;
    391 		for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
    392 			result << glslTraverseBasicTypes(m_interfaceName + "[gl_InvocationID]." + m_members[i].name, m_members[i].type, 0, indentationDepth, visit);
    393 		return result.str();
    394 	}
    395 }
    396 
    397 std::string IOBlock::glslTraverseBasicType (const int indentationDepth, BasicTypeVisitFunc visit) const
    398 {
    399 	std::ostringstream result;
    400 	for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
    401 		result << glslTraverseBasicTypes(m_interfaceName + "." + m_members[i].name, m_members[i].type, 0, indentationDepth, visit);
    402 	return result.str();
    403 }
    404 
    405 int IOBlock::numBasicSubobjectsInElementType (void) const
    406 {
    407 	int result = 0;
    408 	for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
    409 		result += numBasicSubobjects(m_members[i].type);
    410 	return result;
    411 }
    412 
    413 std::string IOBlock::basicSubobjectAtIndex (const int subobjectIndex, const int arraySize) const
    414 {
    415 	int currentIndex = 0;
    416 	for (int arrayNdx = 0; arrayNdx < arraySize; ++arrayNdx)
    417 	for (int memberNdx = 0; memberNdx < static_cast<int>(m_members.size()); ++memberNdx)
    418 	{
    419 		const glu::VarType& membType = m_members[memberNdx].type;
    420 		for (glu::BasicTypeIterator basicIt = glu::BasicTypeIterator::begin(&membType); basicIt != glu::BasicTypeIterator::end(&membType); ++basicIt)
    421 		{
    422 			if (currentIndex == subobjectIndex)
    423 				return m_interfaceName + "[" + de::toString(arrayNdx) + "]." + m_members[memberNdx].name + de::toString(glu::TypeAccessFormat(membType, basicIt.getPath()));
    424 			currentIndex++;
    425 		}
    426 	}
    427 	DE_ASSERT(false);
    428 	return DE_NULL;
    429 }
    430 
    431 class UserDefinedIOTest : public TestCase
    432 {
    433 public:
    434 							UserDefinedIOTest	(tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef);
    435 	void					initPrograms		(vk::SourceCollections& programCollection) const;
    436 	TestInstance*			createInstance		(Context& context) const;
    437 
    438 private:
    439 	const CaseDefinition						m_caseDef;
    440 	std::vector<glu::StructType>				m_structTypes;
    441 	std::vector<de::SharedPtr<TopLevelObject> >	m_tcsOutputs;
    442 	std::vector<de::SharedPtr<TopLevelObject> >	m_tesInputs;
    443 	std::string									m_tcsDeclarations;
    444 	std::string									m_tcsStatements;
    445 	std::string									m_tesDeclarations;
    446 	std::string									m_tesStatements;
    447 };
    448 
    449 UserDefinedIOTest::UserDefinedIOTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef)
    450 	: TestCase	(testCtx, name, description)
    451 	, m_caseDef	(caseDef)
    452 {
    453 	const bool			isPerPatchIO				= m_caseDef.ioType == IO_TYPE_PER_PATCH				||
    454 													  m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY		||
    455 													  m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK		||
    456 													  m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY;
    457 
    458 	const bool			isExplicitVertexArraySize	= m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN ||
    459 													  m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SPEC_MIN;
    460 
    461 	const std::string	vertexAttrArrayInputSize	= m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_IMPLICIT					? ""
    462 													: m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN	? "gl_MaxPatchVertices"
    463 													: m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SPEC_MIN			? de::toString(MAX_TESSELLATION_PATCH_SIZE)
    464 													: DE_NULL;
    465 
    466 	const char* const	maybePatch					= isPerPatchIO ? "patch " : "";
    467 	const std::string	outMaybePatch				= std::string() + maybePatch + "out ";
    468 	const std::string	inMaybePatch				= std::string() + maybePatch + "in ";
    469 	const bool			useBlock					= m_caseDef.ioType == IO_TYPE_PER_VERTEX_BLOCK		||
    470 													  m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK		||
    471 													  m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY;
    472 	const int			wrongNumElements			= -2;
    473 
    474 	std::ostringstream tcsDeclarations;
    475 	std::ostringstream tcsStatements;
    476 	std::ostringstream tesDeclarations;
    477 	std::ostringstream tesStatements;
    478 
    479 	// Indices 0 and 1 are taken, see initPrograms()
    480 	int tcsNextOutputLocation = 2;
    481 	int tesNextInputLocation  = 2;
    482 
    483 	m_structTypes.push_back(glu::StructType("S"));
    484 
    485 	const glu::VarType	highpFloat		(glu::TYPE_FLOAT, glu::PRECISION_HIGHP);
    486 	glu::StructType&	structType		= m_structTypes.back();
    487 	const glu::VarType	structVarType	(&structType);
    488 	bool				usedStruct		= false;
    489 
    490 	structType.addMember("x", glu::VarType(glu::TYPE_INT,		 glu::PRECISION_HIGHP));
    491 	structType.addMember("y", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP));
    492 
    493 	// It is illegal to have a structure containing an array as an output variable
    494 	if (useBlock)
    495 		structType.addMember("z", glu::VarType(highpFloat, 2));
    496 
    497 	if (useBlock)
    498 	{
    499 		std::vector<IOBlock::Member> blockMembers;
    500 
    501 		// use leaner block to make sure it is not larger than allowed (per-patch storage is very limited)
    502 		const bool useLightweightBlock = (m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY);
    503 
    504 		if (!useLightweightBlock)
    505 			blockMembers.push_back(IOBlock::Member("blockS",	structVarType));
    506 
    507 		blockMembers.push_back(IOBlock::Member("blockFa",	glu::VarType(highpFloat, 3)));
    508 		blockMembers.push_back(IOBlock::Member("blockSa",	glu::VarType(structVarType, 2)));
    509 		blockMembers.push_back(IOBlock::Member("blockF",	highpFloat));
    510 
    511 		m_tcsOutputs.push_back	(de::SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "tcBlock", blockMembers)));
    512 		m_tesInputs.push_back	(de::SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "teBlock", blockMembers)));
    513 
    514 		usedStruct = true;
    515 	}
    516 	else
    517 	{
    518 		const Variable var0("in_te_s", structVarType,	m_caseDef.ioType != IO_TYPE_PER_PATCH);
    519 		const Variable var1("in_te_f", highpFloat,		m_caseDef.ioType != IO_TYPE_PER_PATCH);
    520 
    521 		if (m_caseDef.ioType != IO_TYPE_PER_PATCH_ARRAY)
    522 		{
    523 			// Arrays of structures are disallowed, add struct cases only if not arrayed variable
    524 			m_tcsOutputs.push_back	(de::SharedPtr<TopLevelObject>(new Variable(var0)));
    525 			m_tesInputs.push_back	(de::SharedPtr<TopLevelObject>(new Variable(var0)));
    526 
    527 			usedStruct = true;
    528 		}
    529 
    530 		m_tcsOutputs.push_back	(de::SharedPtr<TopLevelObject>(new Variable(var1)));
    531 		m_tesInputs.push_back	(de::SharedPtr<TopLevelObject>(new Variable(var1)));
    532 	}
    533 
    534 	if (usedStruct)
    535 		tcsDeclarations << de::toString(glu::declare(structType)) + ";\n";
    536 
    537 	tcsStatements << "\t{\n"
    538 				  << "\t\thighp float v = 1.3;\n";
    539 
    540 	for (int tcsOutputNdx = 0; tcsOutputNdx < static_cast<int>(m_tcsOutputs.size()); ++tcsOutputNdx)
    541 	{
    542 		const TopLevelObject&	output		= *m_tcsOutputs[tcsOutputNdx];
    543 		const int				numElements	= !isPerPatchIO										? -1	//!< \note -1 means indexing with gl_InstanceID
    544 											: m_caseDef.ioType == IO_TYPE_PER_PATCH				? 1
    545 											: m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY		? NUM_PER_PATCH_ARRAY_ELEMS
    546 											: m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK		? 1
    547 											: m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY	? NUM_PER_PATCH_BLOCKS
    548 											: wrongNumElements;
    549 		const bool				isArray		= (numElements != 1);
    550 
    551 		DE_ASSERT(numElements != wrongNumElements);
    552 
    553 		// \note: TCS output arrays are always implicitly-sized
    554 		tcsDeclarations << "layout(location = " << tcsNextOutputLocation << ") ";
    555 		if (isArray)
    556 			tcsDeclarations << outMaybePatch << output.declareArray(m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY			? de::toString(NUM_PER_PATCH_ARRAY_ELEMS)
    557 																  : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY	? de::toString(NUM_PER_PATCH_BLOCKS)
    558 																  : "");
    559 		else
    560 			tcsDeclarations << outMaybePatch << output.declare();
    561 
    562 		tcsNextOutputLocation += output.numBasicSubobjectsInElementType();
    563 
    564 		if (!isPerPatchIO)
    565 			tcsStatements << "\t\tv += float(gl_InvocationID)*" << de::floatToString(0.4f * (float)output.numBasicSubobjectsInElementType(), 1) << ";\n";
    566 
    567 		tcsStatements << "\n\t\t// Assign values to output " << output.name() << "\n";
    568 		if (isArray)
    569 			tcsStatements << output.glslTraverseBasicTypeArray(numElements, 2, glslAssignBasicTypeObject);
    570 		else
    571 			tcsStatements << output.glslTraverseBasicType(2, glslAssignBasicTypeObject);
    572 
    573 		if (!isPerPatchIO)
    574 			tcsStatements << "\t\tv += float(" << de::toString(NUM_OUTPUT_VERTICES) << "-gl_InvocationID-1)*" << de::floatToString(0.4f * (float)output.numBasicSubobjectsInElementType(), 1) << ";\n";
    575 	}
    576 	tcsStatements << "\t}\n";
    577 
    578 	tcsDeclarations << "\n"
    579 					<< "layout(location = 0) in " + Variable("in_tc_attr", highpFloat, true).declareArray(vertexAttrArrayInputSize);
    580 
    581 	if (usedStruct)
    582 		tesDeclarations << de::toString(glu::declare(structType)) << ";\n";
    583 
    584 	tesStatements << "\tbool allOk = true;\n"
    585 				  << "\thighp uint firstFailedInputIndex = 0u;\n"
    586 				  << "\t{\n"
    587 				  << "\t\thighp float v = 1.3;\n";
    588 
    589 	for (int tesInputNdx = 0; tesInputNdx < static_cast<int>(m_tesInputs.size()); ++tesInputNdx)
    590 	{
    591 		const TopLevelObject&	input		= *m_tesInputs[tesInputNdx];
    592 		const int				numElements	= !isPerPatchIO										? NUM_OUTPUT_VERTICES
    593 											: m_caseDef.ioType == IO_TYPE_PER_PATCH				? 1
    594 											: m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK		? 1
    595 											: m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY		? NUM_PER_PATCH_ARRAY_ELEMS
    596 											: m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY	? NUM_PER_PATCH_BLOCKS
    597 											: wrongNumElements;
    598 		const bool				isArray		= (numElements != 1);
    599 
    600 		DE_ASSERT(numElements != wrongNumElements);
    601 
    602 		tesDeclarations << "layout(location = " << tesNextInputLocation << ") ";
    603 		if (isArray)
    604 			tesDeclarations << inMaybePatch << input.declareArray(m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY		? de::toString(NUM_PER_PATCH_ARRAY_ELEMS)
    605 																: m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY	? de::toString(NUM_PER_PATCH_BLOCKS)
    606 																: isExplicitVertexArraySize							? de::toString(vertexAttrArrayInputSize)
    607 																: "");
    608 		else
    609 			tesDeclarations << inMaybePatch + input.declare();
    610 
    611 		tesNextInputLocation += input.numBasicSubobjectsInElementType();
    612 
    613 		tesStatements << "\n\t\t// Check values in input " << input.name() << "\n";
    614 		if (isArray)
    615 			tesStatements << input.glslTraverseBasicTypeArray(numElements, 2, glslCheckBasicTypeObject);
    616 		else
    617 			tesStatements << input.glslTraverseBasicType(2, glslCheckBasicTypeObject);
    618 	}
    619 	tesStatements << "\t}\n";
    620 
    621 	m_tcsDeclarations = tcsDeclarations.str();
    622 	m_tcsStatements   = tcsStatements.str();
    623 	m_tesDeclarations = tesDeclarations.str();
    624 	m_tesStatements   = tesStatements.str();
    625 }
    626 
    627 void UserDefinedIOTest::initPrograms (vk::SourceCollections& programCollection) const
    628 {
    629 	// Vertex shader
    630 	{
    631 		std::ostringstream src;
    632 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    633 			<< "\n"
    634 			<< "layout(location = 0) in  highp float in_v_attr;\n"
    635 			<< "layout(location = 0) out highp float in_tc_attr;\n"
    636 			<< "\n"
    637 			<< "void main (void)\n"
    638 			<< "{\n"
    639 			<< "	in_tc_attr = in_v_attr;\n"
    640 			<< "}\n";
    641 
    642 		programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
    643 	}
    644 
    645 	// Tessellation control shader
    646 	{
    647 		std::ostringstream src;
    648 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    649 			<< "#extension GL_EXT_tessellation_shader : require\n"
    650 			<< "\n"
    651 			<< "layout(vertices = " << NUM_OUTPUT_VERTICES << ") out;\n"
    652 			<< "\n"
    653 			<< "layout(location = 0) patch out highp vec2 in_te_positionScale;\n"
    654 			<< "layout(location = 1) patch out highp vec2 in_te_positionOffset;\n"
    655 			<< "\n"
    656 			<< m_tcsDeclarations
    657 			<< "\n"
    658 			<< "void main (void)\n"
    659 			<< "{\n"
    660 			<< m_tcsStatements
    661 			<< "\n"
    662 			<< "	gl_TessLevelInner[0] = in_tc_attr[0];\n"
    663 			<< "	gl_TessLevelInner[1] = in_tc_attr[1];\n"
    664 			<< "\n"
    665 			<< "	gl_TessLevelOuter[0] = in_tc_attr[2];\n"
    666 			<< "	gl_TessLevelOuter[1] = in_tc_attr[3];\n"
    667 			<< "	gl_TessLevelOuter[2] = in_tc_attr[4];\n"
    668 			<< "	gl_TessLevelOuter[3] = in_tc_attr[5];\n"
    669 			<< "\n"
    670 			<< "	in_te_positionScale  = vec2(in_tc_attr[6], in_tc_attr[7]);\n"
    671 			<< "	in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\n"
    672 			<< "}\n";
    673 
    674 		programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
    675 	}
    676 
    677 	// Tessellation evaluation shader
    678 	{
    679 		std::ostringstream src;
    680 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    681 			<< "#extension GL_EXT_tessellation_shader : require\n"
    682 			<< "\n"
    683 			<< "layout(" << getTessPrimitiveTypeShaderName(m_caseDef.primitiveType) << ") in;\n"
    684 			<< "\n"
    685 			<< "layout(location = 0) patch in highp vec2 in_te_positionScale;\n"
    686 			<< "layout(location = 1) patch in highp vec2 in_te_positionOffset;\n"
    687 			<< "\n"
    688 			<< m_tesDeclarations
    689 			<< "\n"
    690 			<< "layout(location = 0) out highp vec4 in_f_color;\n"
    691 			<< "\n"
    692 			<< "// Will contain the index of the first incorrect input,\n"
    693 			<< "// or the number of inputs if all are correct\n"
    694 			<< "layout (set = 0, binding = 0, std430) coherent restrict buffer Output {\n"
    695 			<< "    int  numInvocations;\n"
    696 			<< "    uint firstFailedInputIndex[];\n"
    697 			<< "} sb_out;\n"
    698 			<< "\n"
    699 			<< "bool compare_int   (int   a, int   b) { return a == b; }\n"
    700 			<< "bool compare_float (float a, float b) { return abs(a - b) < 0.01f; }\n"
    701 			<< "bool compare_vec4  (vec4  a, vec4  b) { return all(lessThan(abs(a - b), vec4(0.01f))); }\n"
    702 			<< "\n"
    703 			<< "void main (void)\n"
    704 			<< "{\n"
    705 			<< m_tesStatements
    706 			<< "\n"
    707 			<< "	gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n"
    708 			<< "	in_f_color  = allOk ? vec4(0.0, 1.0, 0.0, 1.0)\n"
    709 			<< "	                    : vec4(1.0, 0.0, 0.0, 1.0);\n"
    710 			<< "\n"
    711 			<< "	int index = atomicAdd(sb_out.numInvocations, 1);\n"
    712 			<< "	sb_out.firstFailedInputIndex[index] = firstFailedInputIndex;\n"
    713 			<< "}\n";
    714 
    715 		programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
    716 	}
    717 
    718 	// Fragment shader
    719 	{
    720 		std::ostringstream src;
    721 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    722 			<< "\n"
    723 			<< "layout(location = 0) in  highp   vec4 in_f_color;\n"
    724 			<< "layout(location = 0) out mediump vec4 o_color;\n"
    725 			<< "\n"
    726 			<< "void main (void)\n"
    727 			<< "{\n"
    728 			<< "    o_color = in_f_color;\n"
    729 			<< "}\n";
    730 
    731 		programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
    732 	}
    733 }
    734 
    735 class UserDefinedIOTestInstance : public TestInstance
    736 {
    737 public:
    738 							UserDefinedIOTestInstance	(Context&											context,
    739 														 const CaseDefinition								caseDef,
    740 														 const std::vector<de::SharedPtr<TopLevelObject> >&	tesInputs);
    741 	tcu::TestStatus			iterate						(void);
    742 
    743 private:
    744 	const CaseDefinition								m_caseDef;
    745 	const std::vector<de::SharedPtr<TopLevelObject> >	m_tesInputs;
    746 };
    747 
    748 UserDefinedIOTestInstance::UserDefinedIOTestInstance (Context& context, const CaseDefinition caseDef, const std::vector<de::SharedPtr<TopLevelObject> >& tesInputs)
    749 	: TestInstance		(context)
    750 	, m_caseDef			(caseDef)
    751 	, m_tesInputs		(tesInputs)
    752 {
    753 }
    754 
    755 tcu::TestStatus UserDefinedIOTestInstance::iterate (void)
    756 {
    757 	requireFeatures(m_context.getInstanceInterface(), m_context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
    758 
    759 	const DeviceInterface&	vk					= m_context.getDeviceInterface();
    760 	const VkDevice			device				= m_context.getDevice();
    761 	const VkQueue			queue				= m_context.getUniversalQueue();
    762 	const deUint32			queueFamilyIndex	= m_context.getUniversalQueueFamilyIndex();
    763 	Allocator&				allocator			= m_context.getDefaultAllocator();
    764 
    765 	const int				numAttributes				= NUM_TESS_LEVELS + 2 + 2;
    766 	static const float		attributes[numAttributes]	= { /* inner */ 3.0f, 4.0f, /* outer */ 5.0f, 6.0f, 7.0f, 8.0f, /* pos. scale */ 1.2f, 1.3f, /* pos. offset */ -0.3f, -0.4f };
    767 	const int				refNumVertices				= referenceVertexCount(m_caseDef.primitiveType, SPACINGMODE_EQUAL, false, &attributes[0], &attributes[2]);
    768 	const int				refNumUniqueVertices		= referenceVertexCount(m_caseDef.primitiveType, SPACINGMODE_EQUAL, true, &attributes[0], &attributes[2]);
    769 
    770 	// Vertex input attributes buffer: to pass tessellation levels
    771 
    772 	const VkFormat     vertexFormat				= VK_FORMAT_R32_SFLOAT;
    773 	const deUint32     vertexStride				= tcu::getPixelSize(mapVkFormat(vertexFormat));
    774 	const VkDeviceSize vertexDataSizeBytes		= numAttributes * vertexStride;
    775 	const Buffer       vertexBuffer				(vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
    776 
    777 	{
    778 		const Allocation& alloc = vertexBuffer.getAllocation();
    779 		deMemcpy(alloc.getHostPtr(), &attributes[0], static_cast<std::size_t>(vertexDataSizeBytes));
    780 		flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexDataSizeBytes);
    781 	}
    782 
    783 	// Output buffer: number of invocations and verification indices
    784 
    785 	const int		   resultBufferMaxVertices	= refNumVertices;
    786 	const VkDeviceSize resultBufferSizeBytes    = sizeof(deInt32) + resultBufferMaxVertices * sizeof(deUint32);
    787 	const Buffer       resultBuffer             (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
    788 
    789 	{
    790 		const Allocation& alloc = resultBuffer.getAllocation();
    791 		deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
    792 		flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
    793 	}
    794 
    795 	// Color attachment
    796 
    797 	const tcu::IVec2			  renderSize				 = tcu::IVec2(RENDER_SIZE, RENDER_SIZE);
    798 	const VkFormat				  colorFormat				 = VK_FORMAT_R8G8B8A8_UNORM;
    799 	const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
    800 	const Image					  colorAttachmentImage		 (vk, device, allocator,
    801 															 makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
    802 															 MemoryRequirement::Any);
    803 
    804 	// Color output buffer: image will be copied here for verification
    805 
    806 	const VkDeviceSize	colorBufferSizeBytes	= renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
    807 	const Buffer		colorBuffer				(vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
    808 
    809 	// Descriptors
    810 
    811 	const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
    812 		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
    813 		.build(vk, device));
    814 
    815 	const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
    816 		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
    817 		.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
    818 
    819 	const Unique<VkDescriptorSet> descriptorSet    (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
    820 	const VkDescriptorBufferInfo  resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
    821 
    822 	DescriptorSetUpdateBuilder()
    823 		.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
    824 		.update(vk, device);
    825 
    826 	// Pipeline
    827 
    828 	const Unique<VkImageView>      colorAttachmentView(makeImageView(vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
    829 	const Unique<VkRenderPass>     renderPass         (makeRenderPass(vk, device, colorFormat));
    830 	const Unique<VkFramebuffer>    framebuffer        (makeFramebuffer(vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
    831 	const Unique<VkPipelineLayout> pipelineLayout     (makePipelineLayout(vk, device, *descriptorSetLayout));
    832 	const Unique<VkCommandPool>    cmdPool            (makeCommandPool(vk, device, queueFamilyIndex));
    833 	const Unique<VkCommandBuffer>  cmdBuffer          (allocateCommandBuffer (vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
    834 
    835 	const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
    836 		.setRenderSize                (renderSize)
    837 		.setPatchControlPoints        (numAttributes)
    838 		.setVertexInputSingleAttribute(vertexFormat, vertexStride)
    839 		.setShader                    (vk, device, VK_SHADER_STAGE_VERTEX_BIT,					m_context.getBinaryCollection().get("vert"), DE_NULL)
    840 		.setShader                    (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,	m_context.getBinaryCollection().get("tesc"), DE_NULL)
    841 		.setShader                    (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
    842 		.setShader                    (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,				m_context.getBinaryCollection().get("frag"), DE_NULL)
    843 		.build                        (vk, device, *pipelineLayout, *renderPass));
    844 
    845 	// Begin draw
    846 
    847 	beginCommandBuffer(vk, *cmdBuffer);
    848 
    849 	// Change color attachment image layout
    850 	{
    851 		const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
    852 			(VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
    853 			VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
    854 			*colorAttachmentImage, colorImageSubresourceRange);
    855 
    856 		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
    857 			0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
    858 	}
    859 
    860 	{
    861 		const VkRect2D renderArea = {
    862 			makeOffset2D(0, 0),
    863 			makeExtent2D(renderSize.x(), renderSize.y()),
    864 		};
    865 		const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
    866 
    867 		beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
    868 	}
    869 
    870 	vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
    871 	vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
    872 	{
    873 		const VkDeviceSize vertexBufferOffset = 0ull;
    874 		vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
    875 	}
    876 
    877 	vk.cmdDraw(*cmdBuffer, numAttributes, 1u, 0u, 0u);
    878 	endRenderPass(vk, *cmdBuffer);
    879 
    880 	// Copy render result to a host-visible buffer
    881 	{
    882 		const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
    883 			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
    884 			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
    885 			*colorAttachmentImage, colorImageSubresourceRange);
    886 
    887 		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
    888 			0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
    889 	}
    890 	{
    891 		const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 1), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
    892 		vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, &copyRegion);
    893 	}
    894 	{
    895 		const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
    896 			VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
    897 
    898 		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
    899 			0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
    900 	}
    901 
    902 	endCommandBuffer(vk, *cmdBuffer);
    903 	submitCommandsAndWait(vk, device, queue, *cmdBuffer);
    904 
    905 	// Verification
    906 
    907 	bool isImageCompareOK = false;
    908 	{
    909 		const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
    910 		invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
    911 
    912 		// Load reference image
    913 		tcu::TextureLevel referenceImage;
    914 		tcu::ImageIO::loadPNG(referenceImage, m_context.getTestContext().getArchive(), m_caseDef.referenceImagePath.c_str());
    915 
    916 		// Verify case result
    917 		const tcu::ConstPixelBufferAccess resultImageAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
    918 		isImageCompareOK = tcu::fuzzyCompare(m_context.getTestContext().getLog(), "ImageComparison", "Image Comparison",
    919 											 referenceImage.getAccess(), resultImageAccess, 0.02f, tcu::COMPARE_LOG_RESULT);
    920 	}
    921 	{
    922 		const Allocation& resultAlloc = resultBuffer.getAllocation();
    923 		invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
    924 
    925 		const deInt32			numVertices = *static_cast<deInt32*>(resultAlloc.getHostPtr());
    926 		const deUint32* const	vertices    = reinterpret_cast<deUint32*>(static_cast<deUint8*>(resultAlloc.getHostPtr()) + sizeof(deInt32));
    927 
    928 		// If this fails then we didn't read all vertices from shader and test must be changed to allow more.
    929 		DE_ASSERT(numVertices <= refNumVertices);
    930 
    931 		if (numVertices < refNumUniqueVertices)
    932 		{
    933 			m_context.getTestContext().getLog()
    934 				<< tcu::TestLog::Message << "Failure: got " << numVertices << " vertices, but expected at least " << refNumUniqueVertices << tcu::TestLog::EndMessage;
    935 
    936 			return tcu::TestStatus::fail("Wrong number of vertices");
    937 		}
    938 		else
    939 		{
    940 			tcu::TestLog&	log					= m_context.getTestContext().getLog();
    941 			const int		topLevelArraySize	= (m_caseDef.ioType == IO_TYPE_PER_PATCH			? 1
    942 												: m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY		? NUM_PER_PATCH_ARRAY_ELEMS
    943 												: m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK		? 1
    944 												: m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY	? NUM_PER_PATCH_BLOCKS
    945 												: NUM_OUTPUT_VERTICES);
    946 			const deUint32	numTEInputs			= numBasicSubobjectsInElementType(m_tesInputs) * topLevelArraySize;
    947 
    948 			for (int vertexNdx = 0; vertexNdx < numVertices; ++vertexNdx)
    949 				if (vertices[vertexNdx] > numTEInputs)
    950 				{
    951 					log << tcu::TestLog::Message
    952 						<< "Failure: out_te_firstFailedInputIndex has value " << vertices[vertexNdx]
    953 						<< ", but should be in range [0, " << numTEInputs << "]" << tcu::TestLog::EndMessage;
    954 
    955 					return tcu::TestStatus::fail("Invalid values returned from shader");
    956 				}
    957 				else if (vertices[vertexNdx] != numTEInputs)
    958 				{
    959 					log << tcu::TestLog::Message << "Failure: in tessellation evaluation shader, check for input "
    960 						<< basicSubobjectAtIndex(vertices[vertexNdx], m_tesInputs, topLevelArraySize) << " failed" << tcu::TestLog::EndMessage;
    961 
    962 					return tcu::TestStatus::fail("Invalid input value in tessellation evaluation shader");
    963 				}
    964 		}
    965 	}
    966 	return (isImageCompareOK ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Image comparison failed"));
    967 }
    968 
    969 TestInstance* UserDefinedIOTest::createInstance (Context& context) const
    970 {
    971 	return new UserDefinedIOTestInstance(context, m_caseDef, m_tesInputs);
    972 }
    973 
    974 } // anonymous
    975 
    976 //! These tests correspond roughly to dEQP-GLES31.functional.tessellation.user_defined_io.*
    977 //! Original GLES test queried maxTessellationPatchSize, but this can't be done at the stage the shader source is prepared.
    978 //! Instead, we use minimum supported value.
    979 //! Negative tests weren't ported because vktShaderLibrary doesn't support tests that are expected to fail shader compilation.
    980 tcu::TestCaseGroup* createUserDefinedIOTests (tcu::TestContext& testCtx)
    981 {
    982 	de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "user_defined_io", "Test non-built-in per-patch and per-vertex inputs and outputs"));
    983 
    984 	static const struct
    985 	{
    986 		const char*	name;
    987 		const char*	description;
    988 		IOType		ioType;
    989 	} ioCases[] =
    990 	{
    991 		{ "per_patch",					"Per-patch TCS outputs",					IO_TYPE_PER_PATCH				},
    992 		{ "per_patch_array",			"Per-patch array TCS outputs",				IO_TYPE_PER_PATCH_ARRAY			},
    993 		{ "per_patch_block",			"Per-patch TCS outputs in IO block",		IO_TYPE_PER_PATCH_BLOCK			},
    994 		{ "per_patch_block_array",		"Per-patch TCS outputs in IO block array",	IO_TYPE_PER_PATCH_BLOCK_ARRAY	},
    995 		{ "per_vertex",					"Per-vertex TCS outputs",					IO_TYPE_PER_VERTEX				},
    996 		{ "per_vertex_block",			"Per-vertex TCS outputs in IO block",		IO_TYPE_PER_VERTEX_BLOCK		},
    997 	};
    998 
    999 	static const struct
   1000 	{
   1001 		const char*			name;
   1002 		VertexIOArraySize	vertexIOArraySize;
   1003 	} vertexArraySizeCases[] =
   1004 	{
   1005 		{ "vertex_io_array_size_implicit",			VERTEX_IO_ARRAY_SIZE_IMPLICIT					},
   1006 		{ "vertex_io_array_size_shader_builtin",	VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN	},
   1007 		{ "vertex_io_array_size_spec_min",			VERTEX_IO_ARRAY_SIZE_EXPLICIT_SPEC_MIN			},
   1008 	};
   1009 
   1010 	for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(ioCases); ++caseNdx)
   1011 	{
   1012 		de::MovePtr<tcu::TestCaseGroup> ioTypeGroup (new tcu::TestCaseGroup(testCtx, ioCases[caseNdx].name, ioCases[caseNdx].description));
   1013 		for (int arrayCaseNdx = 0; arrayCaseNdx < DE_LENGTH_OF_ARRAY(vertexArraySizeCases); ++arrayCaseNdx)
   1014 		{
   1015 			de::MovePtr<tcu::TestCaseGroup> vertexArraySizeGroup (new tcu::TestCaseGroup(testCtx, vertexArraySizeCases[arrayCaseNdx].name, ""));
   1016 			for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; ++primitiveTypeNdx)
   1017 			{
   1018 				const TessPrimitiveType primitiveType = static_cast<TessPrimitiveType>(primitiveTypeNdx);
   1019 				const std::string		primitiveName = getTessPrimitiveTypeShaderName(primitiveType);
   1020 				const CaseDefinition	caseDef		  = { primitiveType, ioCases[caseNdx].ioType, vertexArraySizeCases[arrayCaseNdx].vertexIOArraySize,
   1021 														  std::string() + "vulkan/data/tessellation/user_defined_io_" + primitiveName + "_ref.png" };
   1022 
   1023 				vertexArraySizeGroup->addChild(new UserDefinedIOTest(testCtx, primitiveName, "", caseDef));
   1024 			}
   1025 			ioTypeGroup->addChild(vertexArraySizeGroup.release());
   1026 		}
   1027 		group->addChild(ioTypeGroup.release());
   1028 	}
   1029 
   1030 	return group.release();
   1031 }
   1032 
   1033 } // tessellation
   1034 } // vkt
   1035