Merge "Fix error double accounting in fuzzyCompare()" am: 0cf17c4bf8
[platform/upstream/VK-GL-CTS.git] / external / vulkancts / modules / vulkan / tessellation / vktTessellationUserDefinedIO.cpp
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