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 Coordinates Tests
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktTessellationCoordinatesTests.hpp"
26 #include "vktTestCaseUtil.hpp"
27 #include "vktTessellationUtil.hpp"
28
29 #include "tcuTestLog.hpp"
30 #include "tcuRGBA.hpp"
31 #include "tcuSurface.hpp"
32 #include "tcuTextureUtil.hpp"
33 #include "tcuVectorUtil.hpp"
34
35 #include "vkDefs.hpp"
36 #include "vkBarrierUtil.hpp"
37 #include "vkQueryUtil.hpp"
38 #include "vkBuilderUtil.hpp"
39 #include "vkTypeUtil.hpp"
40 #include "vkCmdUtil.hpp"
41 #include "vkObjUtil.hpp"
42 #include "vkBufferWithMemory.hpp"
43 #include "vkImageWithMemory.hpp"
44
45 #include "deUniquePtr.hpp"
46
47 #include <string>
48 #include <vector>
49
50 namespace vkt
51 {
52 namespace tessellation
53 {
54
55 using namespace vk;
56
57 namespace
58 {
59
60 template <typename T>
61 class SizeLessThan
62 {
63 public:
operator ()(const T & a,const T & b) const64 bool operator()(const T &a, const T &b) const
65 {
66 return a.size() < b.size();
67 }
68 };
69
getCaseName(const TessPrimitiveType primitiveType,const SpacingMode spacingMode,bool executionModeInEvaluationShader)70 std::string getCaseName(const TessPrimitiveType primitiveType, const SpacingMode spacingMode,
71 bool executionModeInEvaluationShader)
72 {
73 std::ostringstream str;
74 str << getTessPrimitiveTypeShaderName(primitiveType) << "_" << getSpacingModeShaderName(spacingMode);
75 if (!executionModeInEvaluationShader)
76 str << "_execution_mode_in_tesc";
77 return str.str();
78 }
79
genTessLevelCases(const TessPrimitiveType primitiveType,const SpacingMode spacingMode)80 std::vector<TessLevels> genTessLevelCases(const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
81 {
82 static const TessLevels rawTessLevelCases[] = {
83 {{1.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, {{63.0f, 24.0f}, {15.0f, 42.0f, 10.0f, 12.0f}},
84 {{3.0f, 2.0f}, {6.0f, 8.0f, 7.0f, 9.0f}}, {{4.0f, 6.0f}, {2.0f, 3.0f, 1.0f, 4.0f}},
85 {{2.0f, 2.0f}, {6.0f, 8.0f, 7.0f, 9.0f}}, {{5.0f, 6.0f}, {1.0f, 1.0f, 1.0f, 1.0f}},
86 {{1.0f, 6.0f}, {2.0f, 3.0f, 1.0f, 4.0f}}, {{5.0f, 1.0f}, {2.0f, 3.0f, 1.0f, 4.0f}},
87 {{5.2f, 1.6f}, {2.9f, 3.4f, 1.5f, 4.1f}}};
88
89 if (spacingMode == SPACINGMODE_EQUAL)
90 return std::vector<TessLevels>(DE_ARRAY_BEGIN(rawTessLevelCases), DE_ARRAY_END(rawTessLevelCases));
91 else
92 {
93 std::vector<TessLevels> result;
94 result.reserve(DE_LENGTH_OF_ARRAY(rawTessLevelCases));
95
96 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(rawTessLevelCases); ++tessLevelCaseNdx)
97 {
98 TessLevels curTessLevelCase = rawTessLevelCases[tessLevelCaseNdx];
99
100 float *const inner = &curTessLevelCase.inner[0];
101 float *const outer = &curTessLevelCase.outer[0];
102
103 for (int j = 0; j < 2; ++j)
104 inner[j] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[j]));
105 for (int j = 0; j < 4; ++j)
106 outer[j] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, outer[j]));
107
108 if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
109 {
110 if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f)
111 {
112 if (inner[0] == 1.0f)
113 inner[0] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[0] + 0.1f));
114 }
115 }
116 else if (primitiveType == TESSPRIMITIVETYPE_QUADS)
117 {
118 if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f || outer[3] > 1.0f)
119 {
120 if (inner[0] == 1.0f)
121 inner[0] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[0] + 0.1f));
122 if (inner[1] == 1.0f)
123 inner[1] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[1] + 0.1f));
124 }
125 }
126
127 result.push_back(curTessLevelCase);
128 }
129
130 DE_ASSERT(static_cast<int>(result.size()) == DE_LENGTH_OF_ARRAY(rawTessLevelCases));
131 return result;
132 }
133 }
134
generateReferenceTessCoords(const TessPrimitiveType primitiveType,const SpacingMode spacingMode,const float * innerLevels,const float * outerLevels)135 std::vector<tcu::Vec3> generateReferenceTessCoords(const TessPrimitiveType primitiveType, const SpacingMode spacingMode,
136 const float *innerLevels, const float *outerLevels)
137 {
138 if (isPatchDiscarded(primitiveType, outerLevels))
139 return std::vector<tcu::Vec3>();
140
141 switch (primitiveType)
142 {
143 case TESSPRIMITIVETYPE_TRIANGLES:
144 {
145 int inner;
146 int outer[3];
147 getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
148
149 if (spacingMode != SPACINGMODE_EQUAL)
150 {
151 // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
152 DE_ASSERT(de::abs(innerLevels[0] - static_cast<float>(inner)) < 0.001f);
153 for (int i = 0; i < 3; ++i)
154 DE_ASSERT(de::abs(outerLevels[i] - static_cast<float>(outer[i])) < 0.001f);
155 DE_ASSERT(inner > 1 || (outer[0] == 1 && outer[1] == 1 && outer[2] == 1));
156 }
157
158 return generateReferenceTriangleTessCoords(spacingMode, inner, outer[0], outer[1], outer[2]);
159 }
160
161 case TESSPRIMITIVETYPE_QUADS:
162 {
163 int inner[2];
164 int outer[4];
165 getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
166
167 if (spacingMode != SPACINGMODE_EQUAL)
168 {
169 // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
170 for (int i = 0; i < 2; ++i)
171 DE_ASSERT(de::abs(innerLevels[i] - static_cast<float>(inner[i])) < 0.001f);
172 for (int i = 0; i < 4; ++i)
173 DE_ASSERT(de::abs(outerLevels[i] - static_cast<float>(outer[i])) < 0.001f);
174
175 DE_ASSERT((inner[0] > 1 && inner[1] > 1) || (inner[0] == 1 && inner[1] == 1 && outer[0] == 1 &&
176 outer[1] == 1 && outer[2] == 1 && outer[3] == 1));
177 }
178
179 return generateReferenceQuadTessCoords(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]);
180 }
181
182 case TESSPRIMITIVETYPE_ISOLINES:
183 {
184 int outer[2];
185 getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
186
187 if (spacingMode != SPACINGMODE_EQUAL)
188 {
189 // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
190 DE_ASSERT(de::abs(outerLevels[1] - static_cast<float>(outer[1])) < 0.001f);
191 }
192
193 return generateReferenceIsolineTessCoords(outer[0], outer[1]);
194 }
195
196 default:
197 DE_ASSERT(false);
198 return std::vector<tcu::Vec3>();
199 }
200 }
201
drawPoint(tcu::Surface & dst,const int centerX,const int centerY,const tcu::RGBA & color,const int size)202 void drawPoint(tcu::Surface &dst, const int centerX, const int centerY, const tcu::RGBA &color, const int size)
203 {
204 const int width = dst.getWidth();
205 const int height = dst.getHeight();
206 DE_ASSERT(de::inBounds(centerX, 0, width) && de::inBounds(centerY, 0, height));
207 DE_ASSERT(size > 0);
208
209 for (int yOff = -((size - 1) / 2); yOff <= size / 2; ++yOff)
210 for (int xOff = -((size - 1) / 2); xOff <= size / 2; ++xOff)
211 {
212 const int pixX = centerX + xOff;
213 const int pixY = centerY + yOff;
214 if (de::inBounds(pixX, 0, width) && de::inBounds(pixY, 0, height))
215 dst.setPixel(pixX, pixY, color);
216 }
217 }
218
drawTessCoordPoint(tcu::Surface & dst,const TessPrimitiveType primitiveType,const tcu::Vec3 & pt,const tcu::RGBA & color,const int size)219 void drawTessCoordPoint(tcu::Surface &dst, const TessPrimitiveType primitiveType, const tcu::Vec3 &pt,
220 const tcu::RGBA &color, const int size)
221 {
222 // \note These coordinates should match the description in the log message in TessCoordTestInstance::iterate.
223
224 static const tcu::Vec2 triangleCorners[3] = {
225 tcu::Vec2(0.95f, 0.95f), tcu::Vec2(0.5f, 0.95f - 0.9f * deFloatSqrt(3.0f / 4.0f)), tcu::Vec2(0.05f, 0.95f)};
226
227 static const float quadIsolineLDRU[4] = {0.1f, 0.9f, 0.9f, 0.1f};
228
229 const tcu::Vec2 dstPos = primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
230 pt.x() * triangleCorners[0] + pt.y() * triangleCorners[1] + pt.z() * triangleCorners[2]
231
232 :
233 primitiveType == TESSPRIMITIVETYPE_QUADS || primitiveType == TESSPRIMITIVETYPE_ISOLINES ?
234 tcu::Vec2((1.0f - pt.x()) * quadIsolineLDRU[0] + pt.x() * quadIsolineLDRU[2],
235 (1.0f - pt.y()) * quadIsolineLDRU[1] + pt.y() * quadIsolineLDRU[3])
236
237 :
238 tcu::Vec2(-1.0f);
239
240 drawPoint(dst, static_cast<int>(dstPos.x() * (float)dst.getWidth()),
241 static_cast<int>(dstPos.y() * (float)dst.getHeight()), color, size);
242 }
243
drawTessCoordVisualization(tcu::Surface & dst,const TessPrimitiveType primitiveType,const std::vector<tcu::Vec3> & coords)244 void drawTessCoordVisualization(tcu::Surface &dst, const TessPrimitiveType primitiveType,
245 const std::vector<tcu::Vec3> &coords)
246 {
247 const int imageWidth = 256;
248 const int imageHeight = 256;
249 dst.setSize(imageWidth, imageHeight);
250
251 tcu::clear(dst.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
252
253 for (int i = 0; i < static_cast<int>(coords.size()); ++i)
254 drawTessCoordPoint(dst, primitiveType, coords[i], tcu::RGBA::white(), 2);
255 }
256
vec3XLessThan(const tcu::Vec3 & a,const tcu::Vec3 & b)257 inline bool vec3XLessThan(const tcu::Vec3 &a, const tcu::Vec3 &b)
258 {
259 return a.x() < b.x();
260 }
261
binarySearchFirstVec3WithXAtLeast(const std::vector<tcu::Vec3> & sorted,float x)262 int binarySearchFirstVec3WithXAtLeast(const std::vector<tcu::Vec3> &sorted, float x)
263 {
264 const tcu::Vec3 ref(x, 0.0f, 0.0f);
265 const std::vector<tcu::Vec3>::const_iterator first =
266 std::lower_bound(sorted.begin(), sorted.end(), ref, vec3XLessThan);
267 if (first == sorted.end())
268 return -1;
269 return static_cast<int>(std::distance(sorted.begin(), first));
270 }
271
272 // Check that all points in subset are (approximately) present also in superset.
oneWayComparePointSets(tcu::TestLog & log,tcu::Surface & errorDst,const TessPrimitiveType primitiveType,const std::vector<tcu::Vec3> & subset,const std::vector<tcu::Vec3> & superset,const char * subsetName,const char * supersetName,const tcu::RGBA & errorColor)273 bool oneWayComparePointSets(tcu::TestLog &log, tcu::Surface &errorDst, const TessPrimitiveType primitiveType,
274 const std::vector<tcu::Vec3> &subset, const std::vector<tcu::Vec3> &superset,
275 const char *subsetName, const char *supersetName, const tcu::RGBA &errorColor)
276 {
277 const std::vector<tcu::Vec3> supersetSorted = sorted(superset, vec3XLessThan);
278 const float epsilon = 0.01f;
279 const int maxNumFailurePrints = 5;
280 int numFailuresDetected = 0;
281
282 for (int subNdx = 0; subNdx < static_cast<int>(subset.size()); ++subNdx)
283 {
284 const tcu::Vec3 &subPt = subset[subNdx];
285
286 bool matchFound = false;
287
288 {
289 // Binary search the index of the first point in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range.
290 const tcu::Vec3 matchMin = subPt - epsilon;
291 const tcu::Vec3 matchMax = subPt + epsilon;
292 const int firstCandidateNdx = binarySearchFirstVec3WithXAtLeast(supersetSorted, matchMin.x());
293
294 if (firstCandidateNdx >= 0)
295 {
296 // Compare subPt to all points in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range.
297 for (int superNdx = firstCandidateNdx;
298 superNdx < static_cast<int>(supersetSorted.size()) && supersetSorted[superNdx].x() <= matchMax.x();
299 ++superNdx)
300 {
301 const tcu::Vec3 &superPt = supersetSorted[superNdx];
302
303 if (tcu::boolAll(tcu::greaterThanEqual(superPt, matchMin)) &&
304 tcu::boolAll(tcu::lessThanEqual(superPt, matchMax)))
305 {
306 matchFound = true;
307 break;
308 }
309 }
310 }
311 }
312
313 if (!matchFound)
314 {
315 ++numFailuresDetected;
316 if (numFailuresDetected < maxNumFailurePrints)
317 log << tcu::TestLog::Message << "Failure: no matching " << supersetName << " point found for "
318 << subsetName << " point " << subPt << tcu::TestLog::EndMessage;
319 else if (numFailuresDetected == maxNumFailurePrints)
320 log << tcu::TestLog::Message << "Note: More errors follow" << tcu::TestLog::EndMessage;
321
322 drawTessCoordPoint(errorDst, primitiveType, subPt, errorColor, 4);
323 }
324 }
325
326 return numFailuresDetected == 0;
327 }
328
329 //! Returns true on matching coordinate sets.
compareTessCoords(tcu::TestLog & log,TessPrimitiveType primitiveType,const std::vector<tcu::Vec3> & refCoords,const std::vector<tcu::Vec3> & resCoords)330 bool compareTessCoords(tcu::TestLog &log, TessPrimitiveType primitiveType, const std::vector<tcu::Vec3> &refCoords,
331 const std::vector<tcu::Vec3> &resCoords)
332 {
333 tcu::Surface refVisual;
334 tcu::Surface resVisual;
335 bool success = true;
336
337 drawTessCoordVisualization(refVisual, primitiveType, refCoords);
338 drawTessCoordVisualization(resVisual, primitiveType, resCoords);
339
340 // Check that all points in reference also exist in result.
341 success = oneWayComparePointSets(log, refVisual, primitiveType, refCoords, resCoords, "reference", "result",
342 tcu::RGBA::blue()) &&
343 success;
344 // Check that all points in result also exist in reference.
345 success = oneWayComparePointSets(log, resVisual, primitiveType, resCoords, refCoords, "result", "reference",
346 tcu::RGBA::red()) &&
347 success;
348
349 if (!success)
350 {
351 log << tcu::TestLog::Message
352 << "Note: in the following reference visualization, points that are missing in result point set are blue "
353 "(if any)"
354 << tcu::TestLog::EndMessage
355 << tcu::TestLog::Image("RefTessCoordVisualization", "Reference tessCoord visualization", refVisual)
356 << tcu::TestLog::Message
357 << "Note: in the following result visualization, points that are missing in reference point set are red "
358 "(if any)"
359 << tcu::TestLog::EndMessage;
360 }
361
362 log << tcu::TestLog::Image("ResTessCoordVisualization", "Result tessCoord visualization", resVisual);
363
364 return success;
365 }
366
getTeseName(const bool writePointSize)367 inline std::string getTeseName(const bool writePointSize)
368 {
369 std::ostringstream str;
370 str << "tese" << (writePointSize ? "_point_size" : "");
371 return str.str();
372 }
373
374 class TessCoordTest : public TestCase
375 {
376 public:
377 TessCoordTest(tcu::TestContext &testCtx, const TessPrimitiveType primitiveType, const SpacingMode spacingMode,
378 const bool executionModeInEvaluationShader = true);
379
380 void initPrograms(SourceCollections &programCollection) const;
381 void checkSupport(Context &context) const;
382 TestInstance *createInstance(Context &context) const;
383
384 private:
385 const TessPrimitiveType m_primitiveType;
386 const SpacingMode m_spacingMode;
387 const bool m_executionModeInEvaluationShader;
388 };
389
TessCoordTest(tcu::TestContext & testCtx,const TessPrimitiveType primitiveType,const SpacingMode spacingMode,const bool executionModeInEvaluationShader)390 TessCoordTest::TessCoordTest(tcu::TestContext &testCtx, const TessPrimitiveType primitiveType,
391 const SpacingMode spacingMode, const bool executionModeInEvaluationShader)
392 : TestCase(testCtx, getCaseName(primitiveType, spacingMode, executionModeInEvaluationShader))
393 , m_primitiveType(primitiveType)
394 , m_spacingMode(spacingMode)
395 , m_executionModeInEvaluationShader(executionModeInEvaluationShader)
396 {
397 }
398
checkSupport(Context & context) const399 void TessCoordTest::checkSupport(Context &context) const
400 {
401 #ifndef CTS_USES_VULKANSC
402 if (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR *const features = getPortability(context))
403 {
404 checkPointMode(*features);
405 checkPrimitive(*features, m_primitiveType);
406 }
407 #else
408 DE_UNREF(context);
409 #endif // CTS_USES_VULKANSC
410 }
411
initPrograms(SourceCollections & programCollection) const412 void TessCoordTest::initPrograms(SourceCollections &programCollection) const
413 {
414 if (m_executionModeInEvaluationShader)
415 {
416 // Vertex shader - no inputs
417 {
418 std::ostringstream src;
419 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
420 << "\n"
421 << "void main (void)\n"
422 << "{\n"
423 << "}\n";
424
425 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
426 }
427
428 // Tessellation control shader
429 {
430 std::ostringstream src;
431 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
432 << "#extension GL_EXT_tessellation_shader : require\n"
433 << "\n"
434 << "layout(vertices = 1) out;\n"
435 << "\n"
436 << "layout(set = 0, binding = 0, std430) readonly restrict buffer TessLevels {\n"
437 << " float inner0;\n"
438 << " float inner1;\n"
439 << " float outer0;\n"
440 << " float outer1;\n"
441 << " float outer2;\n"
442 << " float outer3;\n"
443 << "} sb_levels;\n"
444 << "\n"
445 << "void main (void)\n"
446 << "{\n"
447 << " gl_TessLevelInner[0] = sb_levels.inner0;\n"
448 << " gl_TessLevelInner[1] = sb_levels.inner1;\n"
449 << "\n"
450 << " gl_TessLevelOuter[0] = sb_levels.outer0;\n"
451 << " gl_TessLevelOuter[1] = sb_levels.outer1;\n"
452 << " gl_TessLevelOuter[2] = sb_levels.outer2;\n"
453 << " gl_TessLevelOuter[3] = sb_levels.outer3;\n"
454 << "}\n";
455
456 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
457 }
458
459 // Tessellation evaluation shader
460 for (uint32_t i = 0; i < 2; ++i)
461 {
462 const bool writePointSize = i == 1;
463
464 std::ostringstream src;
465 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
466 << "#extension GL_EXT_tessellation_shader : require\n";
467 if (writePointSize)
468 src << "#extension GL_EXT_tessellation_point_size : require\n";
469 src << "\n"
470 << "layout(" << getTessPrimitiveTypeShaderName(m_primitiveType) << ", "
471 << getSpacingModeShaderName(m_spacingMode) << ", point_mode) in;\n"
472 << "\n"
473 << "layout(set = 0, binding = 1, std430) coherent restrict buffer Output {\n"
474 << " int numInvocations;\n"
475 << " vec3 tessCoord[];\n" // alignment is 16 bytes, same as vec4
476 << "} sb_out;\n"
477 << "\n"
478 << "void main (void)\n"
479 << "{\n"
480 << " int index = atomicAdd(sb_out.numInvocations, 1);\n"
481 << " sb_out.tessCoord[index] = gl_TessCoord;\n";
482 if (writePointSize)
483 src << " gl_PointSize = 1.0f;\n";
484 src << "}\n";
485
486 programCollection.glslSources.add(getTeseName(writePointSize))
487 << glu::TessellationEvaluationSource(src.str());
488 }
489 }
490 else
491 {
492 // note: spirv code for all stages coresponds to glsl version above
493
494 programCollection.spirvAsmSources.add("vert") << "OpCapability Shader\n"
495 "%glsl_ext_inst = OpExtInstImport \"GLSL.std.450\"\n"
496 "OpMemoryModel Logical GLSL450\n"
497 "OpEntryPoint Vertex %main_fun \"main\"\n"
498 "%type_void = OpTypeVoid\n"
499 "%type_void_f = OpTypeFunction %type_void\n"
500 "%main_fun = OpFunction %type_void None %type_void_f\n"
501 "%main_label = OpLabel\n"
502 "OpReturn\n"
503 "OpFunctionEnd\n";
504
505 // glsl requires primitive_mode, vertex_spacing, ordering and point_mode layout qualifiers to be defined in
506 // tessellation evaluation shader while spirv allows corresponding execution modes to be defined in TES and/or
507 // TCS; here we test using execution modes only in TCS as TES is tested with glsl version of tests
508
509 const std::string executionMode = std::string("OpExecutionMode %main_fun ") +
510 getTessPrimitiveTypeShaderName(m_primitiveType, true) +
511 "\n"
512 "OpExecutionMode %main_fun " +
513 getSpacingModeShaderName(m_spacingMode, true) + "\n" +
514 "OpExecutionMode %main_fun PointMode\n"
515 "OpExecutionMode %main_fun VertexOrderCcw\n";
516
517 std::string tescSrc =
518 "OpCapability Tessellation\n"
519 "%glsl_ext_inst = OpExtInstImport \"GLSL.std.450\"\n"
520 "OpMemoryModel Logical GLSL450\n"
521 "OpEntryPoint TessellationControl %main_fun \"main\" %var_tess_level_inner %var_tess_level_outer\n"
522 "OpExecutionMode %main_fun OutputVertices 1\n";
523 tescSrc +=
524 executionMode +
525 "OpDecorate %var_tess_level_inner Patch\n"
526 "OpDecorate %var_tess_level_inner BuiltIn TessLevelInner\n"
527 "OpMemberDecorate %type_struct_sb_levels 0 NonWritable\n"
528 "OpMemberDecorate %type_struct_sb_levels 0 Offset 0\n"
529 "OpMemberDecorate %type_struct_sb_levels 1 NonWritable\n"
530 "OpMemberDecorate %type_struct_sb_levels 1 Offset 4\n"
531 "OpMemberDecorate %type_struct_sb_levels 2 NonWritable\n"
532 "OpMemberDecorate %type_struct_sb_levels 2 Offset 8\n"
533 "OpMemberDecorate %type_struct_sb_levels 3 NonWritable\n"
534 "OpMemberDecorate %type_struct_sb_levels 3 Offset 12\n"
535 "OpMemberDecorate %type_struct_sb_levels 4 NonWritable\n"
536 "OpMemberDecorate %type_struct_sb_levels 4 Offset 16\n"
537 "OpMemberDecorate %type_struct_sb_levels 5 NonWritable\n"
538 "OpMemberDecorate %type_struct_sb_levels 5 Offset 20\n"
539 "OpDecorate %type_struct_sb_levels BufferBlock\n"
540 "OpDecorate %var_struct_sb_levels DescriptorSet 0\n"
541 "OpDecorate %var_struct_sb_levels Binding 0\n"
542 "OpDecorate %var_struct_sb_levels Restrict\n"
543 "OpDecorate %var_tess_level_outer Patch\n"
544 "OpDecorate %var_tess_level_outer BuiltIn TessLevelOuter\n"
545 "%type_void = OpTypeVoid\n"
546 "%type_void_f = OpTypeFunction %type_void\n"
547 "%type_f32 = OpTypeFloat 32\n"
548 "%type_u32 = OpTypeInt 32 0\n"
549 "%c_u32_2 = OpConstant %type_u32 2\n"
550 "%type_arr_f32_2 = OpTypeArray %type_f32 %c_u32_2\n"
551 "%type_arr_f32_2_ptr = OpTypePointer Output %type_arr_f32_2\n"
552 "%type_i32 = OpTypeInt 32 1\n"
553 "%type_struct_sb_levels = OpTypeStruct %type_f32 %type_f32 %type_f32 %type_f32 %type_f32 %type_f32\n"
554 "%type_struct_sb_levels_ptr = OpTypePointer Uniform %type_struct_sb_levels\n"
555 "%var_struct_sb_levels = OpVariable %type_struct_sb_levels_ptr Uniform\n"
556 "%type_uni_f32_ptr = OpTypePointer Uniform %type_f32\n"
557 "%type_out_f32_ptr = OpTypePointer Output %type_f32\n"
558 "%c_i32_0 = OpConstant %type_i32 0\n"
559 "%c_i32_1 = OpConstant %type_i32 1\n"
560 "%c_u32_4 = OpConstant %type_u32 4\n"
561 "%c_i32_2 = OpConstant %type_i32 2\n"
562 "%c_i32_3 = OpConstant %type_i32 3\n"
563 "%c_i32_4 = OpConstant %type_i32 4\n"
564 "%c_i32_5 = OpConstant %type_i32 5\n"
565 "%type_arr_f32_4 = OpTypeArray %type_f32 %c_u32_4\n"
566 "%type_arr_f32_4_ptr = OpTypePointer Output %type_arr_f32_4\n"
567 "%var_tess_level_inner = OpVariable %type_arr_f32_2_ptr Output\n"
568 "%var_tess_level_outer = OpVariable %type_arr_f32_4_ptr Output\n"
569 "%main_fun = OpFunction %type_void None %type_void_f\n"
570 "%main_label = OpLabel\n"
571 "%tess_inner_0_ptr = OpAccessChain %type_uni_f32_ptr %var_struct_sb_levels %c_i32_0\n"
572 "%tess_inner_0 = OpLoad %type_f32 %tess_inner_0_ptr\n"
573 "%gl_tess_inner_0 = OpAccessChain %type_out_f32_ptr %var_tess_level_inner %c_i32_0\n"
574 " OpStore %gl_tess_inner_0 %tess_inner_0\n"
575 "%tess_inner_1_ptr = OpAccessChain %type_uni_f32_ptr %var_struct_sb_levels %c_i32_1\n"
576 "%tess_inner_1 = OpLoad %type_f32 %tess_inner_1_ptr\n"
577 "%gl_tess_inner_1 = OpAccessChain %type_out_f32_ptr %var_tess_level_inner %c_i32_1\n"
578 " OpStore %gl_tess_inner_1 %tess_inner_1\n"
579 "%tess_outer_0_ptr = OpAccessChain %type_uni_f32_ptr %var_struct_sb_levels %c_i32_2\n"
580 "%tess_outer_0 = OpLoad %type_f32 %tess_outer_0_ptr\n"
581 "%gl_tess_outer_0 = OpAccessChain %type_out_f32_ptr %var_tess_level_outer %c_i32_0\n"
582 " OpStore %gl_tess_outer_0 %tess_outer_0\n"
583 "%tess_outer_1_ptr = OpAccessChain %type_uni_f32_ptr %var_struct_sb_levels %c_i32_3\n"
584 "%tess_outer_1 = OpLoad %type_f32 %tess_outer_1_ptr\n"
585 "%gl_tess_outer_1 = OpAccessChain %type_out_f32_ptr %var_tess_level_outer %c_i32_1\n"
586 " OpStore %gl_tess_outer_1 %tess_outer_1\n"
587 "%tess_outer_2_ptr = OpAccessChain %type_uni_f32_ptr %var_struct_sb_levels %c_i32_4\n"
588 "%tess_outer_2 = OpLoad %type_f32 %tess_outer_2_ptr\n"
589 "%gl_tess_outer_2 = OpAccessChain %type_out_f32_ptr %var_tess_level_outer %c_i32_2\n"
590 " OpStore %gl_tess_outer_2 %tess_outer_2\n"
591 "%tess_outer_3_ptr = OpAccessChain %type_uni_f32_ptr %var_struct_sb_levels %c_i32_5\n"
592 "%tess_outer_3 = OpLoad %type_f32 %tess_outer_3_ptr\n"
593 "%gl_tess_outer_3 = OpAccessChain %type_out_f32_ptr %var_tess_level_outer %c_i32_3\n"
594 " OpStore %gl_tess_outer_3 %tess_outer_3\n"
595 "OpReturn\n"
596 "OpFunctionEnd\n";
597 programCollection.spirvAsmSources.add("tesc") << tescSrc;
598
599 std::string teseSrc =
600 "OpCapability Tessellation\n"
601 "%glsl_ext_inst = OpExtInstImport \"GLSL.std.450\"\n"
602 "OpMemoryModel Logical GLSL450\n"
603 "OpEntryPoint TessellationEvaluation %main_fun \"main\" %var_gl_tess_coord\n"
604 "OpDecorate %type_run_arr_v3_f32 ArrayStride 16\n"
605 "OpMemberDecorate %type_struct 0 Coherent\n"
606 "OpMemberDecorate %type_struct 0 Offset 0\n"
607 "OpMemberDecorate %type_struct 1 Coherent\n"
608 "OpMemberDecorate %type_struct 1 Offset 16\n"
609 "OpDecorate %type_struct BufferBlock\n"
610 "OpDecorate %var_struct_ptr DescriptorSet 0\n"
611 "OpDecorate %var_struct_ptr Restrict\n"
612 "OpDecorate %var_struct_ptr Binding 1\n"
613 "OpDecorate %var_gl_tess_coord BuiltIn TessCoord\n"
614 "%type_void = OpTypeVoid\n"
615 "%type_void_f = OpTypeFunction %type_void\n"
616 "%type_i32 = OpTypeInt 32 1\n"
617 "%type_u32 = OpTypeInt 32 0\n"
618 "%type_i32_fp = OpTypePointer Function %type_i32\n"
619 "%type_f32 = OpTypeFloat 32\n"
620 "%type_v3_f32 = OpTypeVector %type_f32 3\n"
621 "%type_run_arr_v3_f32 = OpTypeRuntimeArray %type_v3_f32\n"
622 "%type_struct = OpTypeStruct %type_i32 %type_run_arr_v3_f32\n"
623 "%type_uni_struct_ptr = OpTypePointer Uniform %type_struct\n"
624 "%type_uni_i32_ptr = OpTypePointer Uniform %type_i32\n"
625 "%type_uni_v3_f32_ptr = OpTypePointer Uniform %type_v3_f32\n"
626 "%type_in_v3_f32_ptr = OpTypePointer Input %type_v3_f32\n"
627 "%c_i32_0 = OpConstant %type_i32 0\n"
628 "%c_i32_1 = OpConstant %type_i32 1\n"
629 "%c_u32_0 = OpConstant %type_u32 1\n"
630 "%c_u32_1 = OpConstant %type_u32 0\n"
631 "%var_struct_ptr = OpVariable %type_uni_struct_ptr Uniform\n"
632 "%var_gl_tess_coord = OpVariable %type_in_v3_f32_ptr Input\n"
633 "%main_fun = OpFunction %type_void None %type_void_f\n"
634 "%main_label = OpLabel\n"
635 "%var_i32_ptr = OpVariable %type_i32_fp Function\n"
636 "%num_invocations = OpAccessChain %type_uni_i32_ptr %var_struct_ptr %c_i32_0\n"
637 "%index_0 = OpAtomicIAdd %type_i32 %num_invocations %c_u32_0 %c_u32_1 %c_i32_1\n"
638 " OpStore %var_i32_ptr %index_0\n"
639 "%index_1 = OpLoad %type_i32 %var_i32_ptr\n"
640 "%gl_tess_coord = OpLoad %type_v3_f32 %var_gl_tess_coord\n"
641 "%out_tess_coord = OpAccessChain %type_uni_v3_f32_ptr %var_struct_ptr %c_i32_1 %index_1\n"
642 " OpStore %out_tess_coord %gl_tess_coord\n"
643 "OpReturn\n"
644 "OpFunctionEnd\n";
645 programCollection.spirvAsmSources.add(getTeseName(false)) << teseSrc;
646 programCollection.spirvAsmSources.add(getTeseName(true)) << teseSrc;
647 }
648 }
649
650 class TessCoordTestInstance : public TestInstance
651 {
652 public:
653 TessCoordTestInstance(Context &context, const TessPrimitiveType primitiveType, const SpacingMode spacingMode);
654
655 tcu::TestStatus iterate(void);
656
657 private:
658 const TessPrimitiveType m_primitiveType;
659 const SpacingMode m_spacingMode;
660 };
661
TessCoordTestInstance(Context & context,const TessPrimitiveType primitiveType,const SpacingMode spacingMode)662 TessCoordTestInstance::TessCoordTestInstance(Context &context, const TessPrimitiveType primitiveType,
663 const SpacingMode spacingMode)
664 : TestInstance(context)
665 , m_primitiveType(primitiveType)
666 , m_spacingMode(spacingMode)
667 {
668 }
669
iterate(void)670 tcu::TestStatus TessCoordTestInstance::iterate(void)
671 {
672 const DeviceInterface &vk = m_context.getDeviceInterface();
673 const VkDevice device = m_context.getDevice();
674 const VkQueue queue = m_context.getUniversalQueue();
675 const uint32_t queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
676 Allocator &allocator = m_context.getDefaultAllocator();
677
678 // Test data
679
680 const std::vector<TessLevels> tessLevelCases = genTessLevelCases(m_primitiveType, m_spacingMode);
681 std::vector<std::vector<tcu::Vec3>> allReferenceTessCoords(tessLevelCases.size());
682
683 for (uint32_t i = 0; i < tessLevelCases.size(); ++i)
684 allReferenceTessCoords[i] = generateReferenceTessCoords(
685 m_primitiveType, m_spacingMode, &tessLevelCases[i].inner[0], &tessLevelCases[i].outer[0]);
686
687 const size_t maxNumVertices =
688 static_cast<int>(std::max_element(allReferenceTessCoords.begin(), allReferenceTessCoords.end(),
689 SizeLessThan<std::vector<tcu::Vec3>>())
690 ->size());
691
692 // Input buffer: tessellation levels. Data is filled in later.
693
694 const BufferWithMemory tessLevelsBuffer(
695 vk, device, allocator, makeBufferCreateInfo(sizeof(TessLevels), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT),
696 MemoryRequirement::HostVisible);
697
698 // Output buffer: number of invocations + padding + tessellation coordinates. Initialized later.
699
700 const int resultBufferTessCoordsOffset = 4 * (int)sizeof(int32_t);
701 const int extraneousVertices =
702 16; // allow some room for extraneous vertices from duplicate shader invocations (number is arbitrary)
703 const VkDeviceSize resultBufferSizeBytes =
704 resultBufferTessCoordsOffset + (maxNumVertices + extraneousVertices) * sizeof(tcu::Vec4);
705 const BufferWithMemory resultBuffer(vk, device, allocator,
706 makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT),
707 MemoryRequirement::HostVisible);
708
709 // Descriptors
710
711 const Unique<VkDescriptorSetLayout> descriptorSetLayout(
712 DescriptorSetLayoutBuilder()
713 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)
714 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
715 .build(vk, device));
716
717 const Unique<VkDescriptorPool> descriptorPool(
718 DescriptorPoolBuilder()
719 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
720 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
721 .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
722
723 const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
724
725 const VkDescriptorBufferInfo tessLevelsBufferInfo =
726 makeDescriptorBufferInfo(tessLevelsBuffer.get(), 0ull, sizeof(TessLevels));
727 const VkDescriptorBufferInfo resultBufferInfo =
728 makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
729
730 DescriptorSetUpdateBuilder()
731 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u),
732 VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo)
733 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u),
734 VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
735 .update(vk, device);
736
737 // Pipeline: set up vertex processing without rasterization
738
739 const Unique<VkRenderPass> renderPass(makeRenderPassWithoutAttachments(vk, device));
740 const Unique<VkFramebuffer> framebuffer(makeFramebuffer(vk, device, *renderPass, 0u, DE_NULL, 1u, 1u));
741 const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device, *descriptorSetLayout));
742 const Unique<VkCommandPool> cmdPool(makeCommandPool(vk, device, queueFamilyIndex));
743 const Unique<VkCommandBuffer> cmdBuffer(
744 allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
745
746 const Unique<VkPipeline> pipeline(
747 GraphicsPipelineBuilder()
748 .setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL)
749 .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
750 m_context.getBinaryCollection().get("tesc"), DE_NULL)
751 .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
752 m_context.getBinaryCollection().get(
753 getTeseName(m_context.getDeviceFeatures().shaderTessellationAndGeometryPointSize)),
754 DE_NULL)
755 .build(vk, device, *pipelineLayout, *renderPass));
756
757 uint32_t numPassedCases = 0;
758
759 // Repeat the test for all tessellation coords cases
760 for (uint32_t tessLevelCaseNdx = 0; tessLevelCaseNdx < tessLevelCases.size(); ++tessLevelCaseNdx)
761 {
762 m_context.getTestContext().getLog()
763 << tcu::TestLog::Message
764 << "Tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx], m_primitiveType)
765 << tcu::TestLog::EndMessage;
766
767 // Upload tessellation levels data to the input buffer
768 {
769 const Allocation &alloc = tessLevelsBuffer.getAllocation();
770 TessLevels *const bufferTessLevels = static_cast<TessLevels *>(alloc.getHostPtr());
771
772 *bufferTessLevels = tessLevelCases[tessLevelCaseNdx];
773 flushAlloc(vk, device, alloc);
774 }
775
776 // Clear the results buffer
777 {
778 const Allocation &alloc = resultBuffer.getAllocation();
779
780 deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
781 flushAlloc(vk, device, alloc);
782 }
783
784 // Reset the command buffer and begin recording.
785 beginCommandBuffer(vk, *cmdBuffer);
786 beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, makeRect2D(1, 1));
787
788 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
789 vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u,
790 &descriptorSet.get(), 0u, DE_NULL);
791
792 // Process a single abstract vertex.
793 vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
794 endRenderPass(vk, *cmdBuffer);
795
796 {
797 const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
798 VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes);
799
800 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u,
801 DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
802 }
803
804 endCommandBuffer(vk, *cmdBuffer);
805 submitCommandsAndWait(vk, device, queue, *cmdBuffer);
806
807 // Verify results
808 {
809 const Allocation &resultAlloc = resultBuffer.getAllocation();
810
811 invalidateAlloc(vk, device, resultAlloc);
812
813 const int32_t numResults = *static_cast<int32_t *>(resultAlloc.getHostPtr());
814 const std::vector<tcu::Vec3> resultTessCoords = readInterleavedData<tcu::Vec3>(
815 numResults, resultAlloc.getHostPtr(), resultBufferTessCoordsOffset, sizeof(tcu::Vec4));
816 const std::vector<tcu::Vec3> &referenceTessCoords = allReferenceTessCoords[tessLevelCaseNdx];
817 const int numExpectedResults = static_cast<int>(referenceTessCoords.size());
818 tcu::TestLog &log = m_context.getTestContext().getLog();
819
820 if (numResults < numExpectedResults)
821 {
822 log << tcu::TestLog::Message << "Failure: generated " << numResults
823 << " coordinates, but the expected reference value is " << numExpectedResults
824 << tcu::TestLog::EndMessage;
825 }
826 else if (numResults == numExpectedResults)
827 log << tcu::TestLog::Message << "Note: generated " << numResults << " tessellation coordinates"
828 << tcu::TestLog::EndMessage;
829 else
830 {
831 log << tcu::TestLog::Message << "Note: generated " << numResults << " coordinates (out of which "
832 << numExpectedResults << " must be unique)" << tcu::TestLog::EndMessage;
833 }
834
835 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
836 log << tcu::TestLog::Message
837 << "Note: in the following visualization(s), the u=1, v=1, w=1 corners are at the right, top, and "
838 "left corners, respectively"
839 << tcu::TestLog::EndMessage;
840 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS || m_primitiveType == TESSPRIMITIVETYPE_ISOLINES)
841 log << tcu::TestLog::Message
842 << "Note: in the following visualization(s), u and v coordinate go left-to-right and "
843 "bottom-to-top, respectively"
844 << tcu::TestLog::EndMessage;
845 else
846 DE_ASSERT(false);
847
848 if (compareTessCoords(log, m_primitiveType, referenceTessCoords, resultTessCoords) &&
849 (numResults >= numExpectedResults))
850 ++numPassedCases;
851 }
852 } // for tessLevelCaseNdx
853
854 return (numPassedCases == tessLevelCases.size() ? tcu::TestStatus::pass("OK") :
855 tcu::TestStatus::fail("Some cases have failed"));
856 }
857
createInstance(Context & context) const858 TestInstance *TessCoordTest::createInstance(Context &context) const
859 {
860 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(),
861 FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
862
863 return new TessCoordTestInstance(context, m_primitiveType, m_spacingMode);
864 }
865
866 } // namespace
867
868 //! Based on dEQP-GLES31.functional.tessellation.tesscoord.*
869 //! \note Transform feedback is replaced with SSBO. Because of that, this version allows duplicate coordinates from shader invocations.
870 //! The test still fails if not enough coordinates are generated, or if coordinates don't match the reference data.
createCoordinatesTests(tcu::TestContext & testCtx)871 tcu::TestCaseGroup *createCoordinatesTests(tcu::TestContext &testCtx)
872 {
873 de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "tesscoord"));
874
875 for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; ++primitiveTypeNdx)
876 for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
877 {
878 group->addChild(
879 new TessCoordTest(testCtx, (TessPrimitiveType)primitiveTypeNdx, (SpacingMode)spacingModeNdx));
880
881 // test if TessCoord builtin has correct value in Evaluation shader when execution mode is set only in Control shader
882 group->addChild(
883 new TessCoordTest(testCtx, (TessPrimitiveType)primitiveTypeNdx, (SpacingMode)spacingModeNdx, false));
884 }
885
886 return group.release();
887 }
888
889 } // namespace tessellation
890 } // namespace vkt
891