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