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