vktTessellationUserDefinedIO.cpp revision abbf1c4efd3388dcfe59d125e5f33140c444a53a
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
45namespace vkt
46{
47namespace tessellation
48{
49
50using namespace vk;
51
52namespace
53{
54
55enum 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
65enum 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
77enum 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
86struct CaseDefinition
87{
88	TessPrimitiveType	primitiveType;
89	IOType				ioType;
90	VertexIOArraySize	vertexIOArraySize;
91	std::string			referenceImagePath;
92};
93
94typedef std::string (*BasicTypeVisitFunc)(const std::string& name, glu::DataType type, int indentationDepth); //!< See glslTraverseBasicTypes below.
95
96class TopLevelObject
97{
98public:
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
113std::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.
153std::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*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.
173std::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*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
194int 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
202std::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
219int 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
240class Variable : public TopLevelObject
241{
242public:
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
259private:
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
265std::string Variable::declare (void) const
266{
267	DE_ASSERT(!m_isArray);
268	return de::toString(glu::declare(m_type, m_name)) + ";\n";
269}
270
271std::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
277std::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
288std::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
294int Variable::numBasicSubobjectsInElementType (void) const
295{
296	return numBasicSubobjects(m_type);
297}
298
299std::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
314class IOBlock : public TopLevelObject
315{
316public:
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
340private:
341	std::string			m_blockName;
342	std::string			m_interfaceName;
343	std::vector<Member>	m_members;
344};
345
346std::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
360std::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
374std::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
397std::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
405int 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
413std::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
431class UserDefinedIOTest : public TestCase
432{
433public:
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
438private:
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
449UserDefinedIOTest::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 * 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 * 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
627void 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
735class UserDefinedIOTestInstance : public TestInstance
736{
737public:
738							UserDefinedIOTestInstance	(Context&											context,
739														 const CaseDefinition								caseDef,
740														 const std::vector<de::SharedPtr<TopLevelObject> >&	tesInputs);
741	tcu::TestStatus			iterate						(void);
742
743private:
744	const CaseDefinition								m_caseDef;
745	const std::vector<de::SharedPtr<TopLevelObject> >	m_tesInputs;
746};
747
748UserDefinedIOTestInstance::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
755tcu::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          (makeCommandBuffer (vk, device, *cmdPool));
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(), 0), 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
969TestInstance* 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.
980tcu::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