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