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