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::__anonfd61fdf70111::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 TestInstance* createInstance (Context& context) const;
440
441 private:
442 const CaseDefinition m_caseDef;
443 std::vector<glu::StructType> m_structTypes;
444 std::vector<de::SharedPtr<TopLevelObject> > m_tcsOutputs;
445 std::vector<de::SharedPtr<TopLevelObject> > m_tesInputs;
446 std::string m_tcsDeclarations;
447 std::string m_tcsStatements;
448 std::string m_tesDeclarations;
449 std::string m_tesStatements;
450 };
451
UserDefinedIOTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const CaseDefinition caseDef)452 UserDefinedIOTest::UserDefinedIOTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef)
453 : TestCase (testCtx, name, description)
454 , m_caseDef (caseDef)
455 {
456 const bool isPerPatchIO = m_caseDef.ioType == IO_TYPE_PER_PATCH ||
457 m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY ||
458 m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK ||
459 m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY;
460
461 const bool isExplicitVertexArraySize = m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN ||
462 m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SPEC_MIN;
463
464 const std::string vertexAttrArrayInputSize = m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_IMPLICIT ? ""
465 : m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN ? "gl_MaxPatchVertices"
466 : m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SPEC_MIN ? de::toString(MAX_TESSELLATION_PATCH_SIZE)
467 : DE_NULL;
468
469 const char* const maybePatch = isPerPatchIO ? "patch " : "";
470 const std::string outMaybePatch = std::string() + maybePatch + "out ";
471 const std::string inMaybePatch = std::string() + maybePatch + "in ";
472 const bool useBlock = m_caseDef.ioType == IO_TYPE_PER_VERTEX_BLOCK ||
473 m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK ||
474 m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY;
475 const int wrongNumElements = -2;
476
477 std::ostringstream tcsDeclarations;
478 std::ostringstream tcsStatements;
479 std::ostringstream tesDeclarations;
480 std::ostringstream tesStatements;
481
482 // Indices 0 and 1 are taken, see initPrograms()
483 int tcsNextOutputLocation = 2;
484 int tesNextInputLocation = 2;
485
486 m_structTypes.push_back(glu::StructType("S"));
487
488 const glu::VarType highpFloat (glu::TYPE_FLOAT, glu::PRECISION_HIGHP);
489 glu::StructType& structType = m_structTypes.back();
490 const glu::VarType structVarType (&structType);
491 bool usedStruct = false;
492
493 structType.addMember("x", glu::VarType(glu::TYPE_INT, glu::PRECISION_HIGHP));
494 structType.addMember("y", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP));
495
496 // It is illegal to have a structure containing an array as an output variable
497 if (useBlock)
498 structType.addMember("z", glu::VarType(highpFloat, 2));
499
500 if (useBlock)
501 {
502 std::vector<IOBlock::Member> blockMembers;
503
504 // use leaner block to make sure it is not larger than allowed (per-patch storage is very limited)
505 const bool useLightweightBlock = (m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY);
506
507 if (!useLightweightBlock)
508 blockMembers.push_back(IOBlock::Member("blockS", structVarType));
509
510 blockMembers.push_back(IOBlock::Member("blockFa", glu::VarType(highpFloat, 3)));
511 blockMembers.push_back(IOBlock::Member("blockSa", glu::VarType(structVarType, 2)));
512 blockMembers.push_back(IOBlock::Member("blockF", highpFloat));
513
514 m_tcsOutputs.push_back (de::SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "tcBlock", blockMembers)));
515 m_tesInputs.push_back (de::SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "teBlock", blockMembers)));
516
517 usedStruct = true;
518 }
519 else
520 {
521 const Variable var0("in_te_s", structVarType, m_caseDef.ioType != IO_TYPE_PER_PATCH);
522 const Variable var1("in_te_f", highpFloat, m_caseDef.ioType != IO_TYPE_PER_PATCH);
523
524 if (m_caseDef.ioType != IO_TYPE_PER_PATCH_ARRAY)
525 {
526 // Arrays of structures are disallowed, add struct cases only if not arrayed variable
527 m_tcsOutputs.push_back (de::SharedPtr<TopLevelObject>(new Variable(var0)));
528 m_tesInputs.push_back (de::SharedPtr<TopLevelObject>(new Variable(var0)));
529
530 usedStruct = true;
531 }
532
533 m_tcsOutputs.push_back (de::SharedPtr<TopLevelObject>(new Variable(var1)));
534 m_tesInputs.push_back (de::SharedPtr<TopLevelObject>(new Variable(var1)));
535 }
536
537 if (usedStruct)
538 tcsDeclarations << de::toString(glu::declare(structType)) + ";\n";
539
540 tcsStatements << "\t{\n"
541 << "\t\thighp float v = 1.3;\n";
542
543 for (int tcsOutputNdx = 0; tcsOutputNdx < static_cast<int>(m_tcsOutputs.size()); ++tcsOutputNdx)
544 {
545 const TopLevelObject& output = *m_tcsOutputs[tcsOutputNdx];
546 const int numElements = !isPerPatchIO ? -1 //!< \note -1 means indexing with gl_InstanceID
547 : m_caseDef.ioType == IO_TYPE_PER_PATCH ? 1
548 : m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY ? NUM_PER_PATCH_ARRAY_ELEMS
549 : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK ? 1
550 : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? NUM_PER_PATCH_BLOCKS
551 : wrongNumElements;
552 const bool isArray = (numElements != 1);
553
554 DE_ASSERT(numElements != wrongNumElements);
555
556 // \note: TCS output arrays are always implicitly-sized
557 tcsDeclarations << "layout(location = " << tcsNextOutputLocation << ") ";
558 if (isArray)
559 tcsDeclarations << outMaybePatch << output.declareArray(m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY ? de::toString(NUM_PER_PATCH_ARRAY_ELEMS)
560 : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? de::toString(NUM_PER_PATCH_BLOCKS)
561 : "");
562 else
563 tcsDeclarations << outMaybePatch << output.declare();
564
565 tcsNextOutputLocation += output.numBasicSubobjectsInElementType();
566
567 if (!isPerPatchIO)
568 tcsStatements << "\t\tv += float(gl_InvocationID)*" << de::floatToString(0.4f * (float)output.numBasicSubobjectsInElementType(), 1) << ";\n";
569
570 tcsStatements << "\n\t\t// Assign values to output " << output.name() << "\n";
571 if (isArray)
572 tcsStatements << output.glslTraverseBasicTypeArray(numElements, 2, glslAssignBasicTypeObject);
573 else
574 tcsStatements << output.glslTraverseBasicType(2, glslAssignBasicTypeObject);
575
576 if (!isPerPatchIO)
577 tcsStatements << "\t\tv += float(" << de::toString(NUM_OUTPUT_VERTICES) << "-gl_InvocationID-1)*" << de::floatToString(0.4f * (float)output.numBasicSubobjectsInElementType(), 1) << ";\n";
578 }
579 tcsStatements << "\t}\n";
580
581 tcsDeclarations << "\n"
582 << "layout(location = 0) in " + Variable("in_tc_attr", highpFloat, true).declareArray(vertexAttrArrayInputSize);
583
584 if (usedStruct)
585 tesDeclarations << de::toString(glu::declare(structType)) << ";\n";
586
587 tesStatements << "\tbool allOk = true;\n"
588 << "\thighp uint firstFailedInputIndex = 0u;\n"
589 << "\t{\n"
590 << "\t\thighp float v = 1.3;\n";
591
592 for (int tesInputNdx = 0; tesInputNdx < static_cast<int>(m_tesInputs.size()); ++tesInputNdx)
593 {
594 const TopLevelObject& input = *m_tesInputs[tesInputNdx];
595 const int numElements = !isPerPatchIO ? NUM_OUTPUT_VERTICES
596 : m_caseDef.ioType == IO_TYPE_PER_PATCH ? 1
597 : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK ? 1
598 : m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY ? NUM_PER_PATCH_ARRAY_ELEMS
599 : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? NUM_PER_PATCH_BLOCKS
600 : wrongNumElements;
601 const bool isArray = (numElements != 1);
602
603 DE_ASSERT(numElements != wrongNumElements);
604
605 tesDeclarations << "layout(location = " << tesNextInputLocation << ") ";
606 if (isArray)
607 tesDeclarations << inMaybePatch << input.declareArray(m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY ? de::toString(NUM_PER_PATCH_ARRAY_ELEMS)
608 : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? de::toString(NUM_PER_PATCH_BLOCKS)
609 : isExplicitVertexArraySize ? de::toString(vertexAttrArrayInputSize)
610 : "");
611 else
612 tesDeclarations << inMaybePatch + input.declare();
613
614 tesNextInputLocation += input.numBasicSubobjectsInElementType();
615
616 tesStatements << "\n\t\t// Check values in input " << input.name() << "\n";
617 if (isArray)
618 tesStatements << input.glslTraverseBasicTypeArray(numElements, 2, glslCheckBasicTypeObject);
619 else
620 tesStatements << input.glslTraverseBasicType(2, glslCheckBasicTypeObject);
621 }
622 tesStatements << "\t}\n";
623
624 m_tcsDeclarations = tcsDeclarations.str();
625 m_tcsStatements = tcsStatements.str();
626 m_tesDeclarations = tesDeclarations.str();
627 m_tesStatements = tesStatements.str();
628 }
629
initPrograms(vk::SourceCollections & programCollection) const630 void UserDefinedIOTest::initPrograms (vk::SourceCollections& programCollection) const
631 {
632 // Vertex shader
633 {
634 std::ostringstream src;
635 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
636 << "\n"
637 << "layout(location = 0) in highp float in_v_attr;\n"
638 << "layout(location = 0) out highp float in_tc_attr;\n"
639 << "\n"
640 << "void main (void)\n"
641 << "{\n"
642 << " in_tc_attr = in_v_attr;\n"
643 << "}\n";
644
645 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
646 }
647
648 // Tessellation control shader
649 {
650 std::ostringstream src;
651 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
652 << "#extension GL_EXT_tessellation_shader : require\n"
653 << "\n"
654 << "layout(vertices = " << NUM_OUTPUT_VERTICES << ") out;\n"
655 << "\n"
656 << "layout(location = 0) patch out highp vec2 in_te_positionScale;\n"
657 << "layout(location = 1) patch out highp vec2 in_te_positionOffset;\n"
658 << "\n"
659 << m_tcsDeclarations
660 << "\n"
661 << "void main (void)\n"
662 << "{\n"
663 << m_tcsStatements
664 << "\n"
665 << " gl_TessLevelInner[0] = in_tc_attr[0];\n"
666 << " gl_TessLevelInner[1] = in_tc_attr[1];\n"
667 << "\n"
668 << " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
669 << " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
670 << " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
671 << " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
672 << "\n"
673 << " in_te_positionScale = vec2(in_tc_attr[6], in_tc_attr[7]);\n"
674 << " in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\n"
675 << "}\n";
676
677 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
678 }
679
680 // Tessellation evaluation shader
681 {
682 std::ostringstream src;
683 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
684 << "#extension GL_EXT_tessellation_shader : require\n"
685 << "\n"
686 << "layout(" << getTessPrimitiveTypeShaderName(m_caseDef.primitiveType) << ") in;\n"
687 << "\n"
688 << "layout(location = 0) patch in highp vec2 in_te_positionScale;\n"
689 << "layout(location = 1) patch in highp vec2 in_te_positionOffset;\n"
690 << "\n"
691 << m_tesDeclarations
692 << "\n"
693 << "layout(location = 0) out highp vec4 in_f_color;\n"
694 << "\n"
695 << "// Will contain the index of the first incorrect input,\n"
696 << "// or the number of inputs if all are correct\n"
697 << "layout (set = 0, binding = 0, std430) coherent restrict buffer Output {\n"
698 << " int numInvocations;\n"
699 << " uint firstFailedInputIndex[];\n"
700 << "} sb_out;\n"
701 << "\n"
702 << "bool compare_int (int a, int b) { return a == b; }\n"
703 << "bool compare_float (float a, float b) { return abs(a - b) < 0.01f; }\n"
704 << "bool compare_vec4 (vec4 a, vec4 b) { return all(lessThan(abs(a - b), vec4(0.01f))); }\n"
705 << "\n"
706 << "void main (void)\n"
707 << "{\n"
708 << m_tesStatements
709 << "\n"
710 << " gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n"
711 << " in_f_color = allOk ? vec4(0.0, 1.0, 0.0, 1.0)\n"
712 << " : vec4(1.0, 0.0, 0.0, 1.0);\n"
713 << "\n"
714 << " int index = atomicAdd(sb_out.numInvocations, 1);\n"
715 << " sb_out.firstFailedInputIndex[index] = firstFailedInputIndex;\n"
716 << "}\n";
717
718 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
719 }
720
721 // Fragment shader
722 {
723 std::ostringstream src;
724 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
725 << "\n"
726 << "layout(location = 0) in highp vec4 in_f_color;\n"
727 << "layout(location = 0) out mediump vec4 o_color;\n"
728 << "\n"
729 << "void main (void)\n"
730 << "{\n"
731 << " o_color = in_f_color;\n"
732 << "}\n";
733
734 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
735 }
736 }
737
738 class UserDefinedIOTestInstance : public TestInstance
739 {
740 public:
741 UserDefinedIOTestInstance (Context& context,
742 const CaseDefinition caseDef,
743 const std::vector<de::SharedPtr<TopLevelObject> >& tesInputs);
744 tcu::TestStatus iterate (void);
745
746 private:
747 const CaseDefinition m_caseDef;
748 const std::vector<de::SharedPtr<TopLevelObject> > m_tesInputs;
749 };
750
UserDefinedIOTestInstance(Context & context,const CaseDefinition caseDef,const std::vector<de::SharedPtr<TopLevelObject>> & tesInputs)751 UserDefinedIOTestInstance::UserDefinedIOTestInstance (Context& context, const CaseDefinition caseDef, const std::vector<de::SharedPtr<TopLevelObject> >& tesInputs)
752 : TestInstance (context)
753 , m_caseDef (caseDef)
754 , m_tesInputs (tesInputs)
755 {
756 }
757
iterate(void)758 tcu::TestStatus UserDefinedIOTestInstance::iterate (void)
759 {
760 requireFeatures(m_context.getInstanceInterface(), m_context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
761
762 const DeviceInterface& vk = m_context.getDeviceInterface();
763 const VkDevice device = m_context.getDevice();
764 const VkQueue queue = m_context.getUniversalQueue();
765 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
766 Allocator& allocator = m_context.getDefaultAllocator();
767
768 const int numAttributes = NUM_TESS_LEVELS + 2 + 2;
769 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 };
770 const int refNumVertices = referenceVertexCount(m_caseDef.primitiveType, SPACINGMODE_EQUAL, false, &attributes[0], &attributes[2]);
771 const int refNumUniqueVertices = referenceVertexCount(m_caseDef.primitiveType, SPACINGMODE_EQUAL, true, &attributes[0], &attributes[2]);
772
773 // Vertex input attributes buffer: to pass tessellation levels
774
775 const VkFormat vertexFormat = VK_FORMAT_R32_SFLOAT;
776 const deUint32 vertexStride = tcu::getPixelSize(mapVkFormat(vertexFormat));
777 const VkDeviceSize vertexDataSizeBytes = numAttributes * vertexStride;
778 const Buffer vertexBuffer (vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
779
780 {
781 const Allocation& alloc = vertexBuffer.getAllocation();
782 deMemcpy(alloc.getHostPtr(), &attributes[0], static_cast<std::size_t>(vertexDataSizeBytes));
783 flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexDataSizeBytes);
784 }
785
786 // Output buffer: number of invocations and verification indices
787
788 const int resultBufferMaxVertices = refNumVertices;
789 const VkDeviceSize resultBufferSizeBytes = sizeof(deInt32) + resultBufferMaxVertices * sizeof(deUint32);
790 const Buffer resultBuffer (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
791
792 {
793 const Allocation& alloc = resultBuffer.getAllocation();
794 deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
795 flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
796 }
797
798 // Color attachment
799
800 const tcu::IVec2 renderSize = tcu::IVec2(RENDER_SIZE, RENDER_SIZE);
801 const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
802 const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
803 const Image colorAttachmentImage (vk, device, allocator,
804 makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
805 MemoryRequirement::Any);
806
807 // Color output buffer: image will be copied here for verification
808
809 const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
810 const Buffer colorBuffer (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
811
812 // Descriptors
813
814 const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
815 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
816 .build(vk, device));
817
818 const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
819 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
820 .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
821
822 const Unique<VkDescriptorSet> descriptorSet (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
823 const VkDescriptorBufferInfo resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
824
825 DescriptorSetUpdateBuilder()
826 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
827 .update(vk, device);
828
829 // Pipeline
830
831 const Unique<VkImageView> colorAttachmentView(makeImageView(vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
832 const Unique<VkRenderPass> renderPass (makeRenderPass(vk, device, colorFormat));
833 const Unique<VkFramebuffer> framebuffer (makeFramebuffer(vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
834 const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device, *descriptorSetLayout));
835 const Unique<VkCommandPool> cmdPool (makeCommandPool(vk, device, queueFamilyIndex));
836 const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer (vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
837
838 const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
839 .setRenderSize (renderSize)
840 .setPatchControlPoints (numAttributes)
841 .setVertexInputSingleAttribute(vertexFormat, vertexStride)
842 .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL)
843 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL)
844 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
845 .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("frag"), DE_NULL)
846 .build (vk, device, *pipelineLayout, *renderPass));
847
848 // Begin draw
849
850 beginCommandBuffer(vk, *cmdBuffer);
851
852 // Change color attachment image layout
853 {
854 const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
855 (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
856 VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
857 *colorAttachmentImage, colorImageSubresourceRange);
858
859 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0u,
860 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
861 }
862
863 {
864 const VkRect2D renderArea = makeRect2D(renderSize);
865 const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
866
867 beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
868 }
869
870 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
871 vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
872 {
873 const VkDeviceSize vertexBufferOffset = 0ull;
874 vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
875 }
876
877 vk.cmdDraw(*cmdBuffer, numAttributes, 1u, 0u, 0u);
878 endRenderPass(vk, *cmdBuffer);
879
880 // Copy render result to a host-visible buffer
881 copyImageToBuffer(vk, *cmdBuffer, *colorAttachmentImage, *colorBuffer, renderSize);
882
883 endCommandBuffer(vk, *cmdBuffer);
884 submitCommandsAndWait(vk, device, queue, *cmdBuffer);
885
886 // Verification
887
888 bool isImageCompareOK = false;
889 {
890 const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
891 invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
892
893 // Load reference image
894 tcu::TextureLevel referenceImage;
895 tcu::ImageIO::loadPNG(referenceImage, m_context.getTestContext().getArchive(), m_caseDef.referenceImagePath.c_str());
896
897 // Verify case result
898 const tcu::ConstPixelBufferAccess resultImageAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
899 isImageCompareOK = tcu::fuzzyCompare(m_context.getTestContext().getLog(), "ImageComparison", "Image Comparison",
900 referenceImage.getAccess(), resultImageAccess, 0.02f, tcu::COMPARE_LOG_RESULT);
901 }
902 {
903 const Allocation& resultAlloc = resultBuffer.getAllocation();
904 invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
905
906 const deInt32 numVertices = *static_cast<deInt32*>(resultAlloc.getHostPtr());
907 const deUint32* const vertices = reinterpret_cast<deUint32*>(static_cast<deUint8*>(resultAlloc.getHostPtr()) + sizeof(deInt32));
908
909 // If this fails then we didn't read all vertices from shader and test must be changed to allow more.
910 DE_ASSERT(numVertices <= refNumVertices);
911
912 if (numVertices < refNumUniqueVertices)
913 {
914 m_context.getTestContext().getLog()
915 << tcu::TestLog::Message << "Failure: got " << numVertices << " vertices, but expected at least " << refNumUniqueVertices << tcu::TestLog::EndMessage;
916
917 return tcu::TestStatus::fail("Wrong number of vertices");
918 }
919 else
920 {
921 tcu::TestLog& log = m_context.getTestContext().getLog();
922 const int topLevelArraySize = (m_caseDef.ioType == IO_TYPE_PER_PATCH ? 1
923 : m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY ? NUM_PER_PATCH_ARRAY_ELEMS
924 : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK ? 1
925 : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? NUM_PER_PATCH_BLOCKS
926 : NUM_OUTPUT_VERTICES);
927 const deUint32 numTEInputs = numBasicSubobjectsInElementType(m_tesInputs) * topLevelArraySize;
928
929 for (int vertexNdx = 0; vertexNdx < numVertices; ++vertexNdx)
930 if (vertices[vertexNdx] > numTEInputs)
931 {
932 log << tcu::TestLog::Message
933 << "Failure: out_te_firstFailedInputIndex has value " << vertices[vertexNdx]
934 << ", but should be in range [0, " << numTEInputs << "]" << tcu::TestLog::EndMessage;
935
936 return tcu::TestStatus::fail("Invalid values returned from shader");
937 }
938 else if (vertices[vertexNdx] != numTEInputs)
939 {
940 log << tcu::TestLog::Message << "Failure: in tessellation evaluation shader, check for input "
941 << basicSubobjectAtIndex(vertices[vertexNdx], m_tesInputs, topLevelArraySize) << " failed" << tcu::TestLog::EndMessage;
942
943 return tcu::TestStatus::fail("Invalid input value in tessellation evaluation shader");
944 }
945 }
946 }
947 return (isImageCompareOK ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Image comparison failed"));
948 }
949
createInstance(Context & context) const950 TestInstance* UserDefinedIOTest::createInstance (Context& context) const
951 {
952 return new UserDefinedIOTestInstance(context, m_caseDef, m_tesInputs);
953 }
954
955 } // anonymous
956
957 //! These tests correspond roughly to dEQP-GLES31.functional.tessellation.user_defined_io.*
958 //! Original GLES test queried maxTessellationPatchSize, but this can't be done at the stage the shader source is prepared.
959 //! Instead, we use minimum supported value.
960 //! Negative tests weren't ported because vktShaderLibrary doesn't support tests that are expected to fail shader compilation.
createUserDefinedIOTests(tcu::TestContext & testCtx)961 tcu::TestCaseGroup* createUserDefinedIOTests (tcu::TestContext& testCtx)
962 {
963 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "user_defined_io", "Test non-built-in per-patch and per-vertex inputs and outputs"));
964
965 static const struct
966 {
967 const char* name;
968 const char* description;
969 IOType ioType;
970 } ioCases[] =
971 {
972 { "per_patch", "Per-patch TCS outputs", IO_TYPE_PER_PATCH },
973 { "per_patch_array", "Per-patch array TCS outputs", IO_TYPE_PER_PATCH_ARRAY },
974 { "per_patch_block", "Per-patch TCS outputs in IO block", IO_TYPE_PER_PATCH_BLOCK },
975 { "per_patch_block_array", "Per-patch TCS outputs in IO block array", IO_TYPE_PER_PATCH_BLOCK_ARRAY },
976 { "per_vertex", "Per-vertex TCS outputs", IO_TYPE_PER_VERTEX },
977 { "per_vertex_block", "Per-vertex TCS outputs in IO block", IO_TYPE_PER_VERTEX_BLOCK },
978 };
979
980 static const struct
981 {
982 const char* name;
983 VertexIOArraySize vertexIOArraySize;
984 } vertexArraySizeCases[] =
985 {
986 { "vertex_io_array_size_implicit", VERTEX_IO_ARRAY_SIZE_IMPLICIT },
987 { "vertex_io_array_size_shader_builtin", VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN },
988 { "vertex_io_array_size_spec_min", VERTEX_IO_ARRAY_SIZE_EXPLICIT_SPEC_MIN },
989 };
990
991 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(ioCases); ++caseNdx)
992 {
993 de::MovePtr<tcu::TestCaseGroup> ioTypeGroup (new tcu::TestCaseGroup(testCtx, ioCases[caseNdx].name, ioCases[caseNdx].description));
994 for (int arrayCaseNdx = 0; arrayCaseNdx < DE_LENGTH_OF_ARRAY(vertexArraySizeCases); ++arrayCaseNdx)
995 {
996 de::MovePtr<tcu::TestCaseGroup> vertexArraySizeGroup (new tcu::TestCaseGroup(testCtx, vertexArraySizeCases[arrayCaseNdx].name, ""));
997 for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; ++primitiveTypeNdx)
998 {
999 const TessPrimitiveType primitiveType = static_cast<TessPrimitiveType>(primitiveTypeNdx);
1000 const std::string primitiveName = getTessPrimitiveTypeShaderName(primitiveType);
1001 const CaseDefinition caseDef = { primitiveType, ioCases[caseNdx].ioType, vertexArraySizeCases[arrayCaseNdx].vertexIOArraySize,
1002 std::string() + "vulkan/data/tessellation/user_defined_io_" + primitiveName + "_ref.png" };
1003
1004 vertexArraySizeGroup->addChild(new UserDefinedIOTest(testCtx, primitiveName, "", caseDef));
1005 }
1006 ioTypeGroup->addChild(vertexArraySizeGroup.release());
1007 }
1008 group->addChild(ioTypeGroup.release());
1009 }
1010
1011 return group.release();
1012 }
1013
1014 } // tessellation
1015 } // vkt
1016