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