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