Merge "Fix error double accounting in fuzzyCompare()" am: 0cf17c4bf8
[platform/upstream/VK-GL-CTS.git] / external / vulkancts / modules / vulkan / tessellation / vktTessellationCoordinatesTests.cpp
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 "vkQueryUtil.hpp"
37 #include "vkBuilderUtil.hpp"
38 #include "vkTypeUtil.hpp"
39
40 #include "deUniquePtr.hpp"
41
42 #include <string>
43 #include <vector>
44
45 namespace vkt
46 {
47 namespace tessellation
48 {
49
50 using namespace vk;
51
52 namespace
53 {
54
55 template <typename T>
56 class SizeLessThan
57 {
58 public:
59         bool operator() (const T& a, const T& b) const { return a.size() < b.size(); }
60 };
61
62 std::string getCaseName (const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
63 {
64         std::ostringstream str;
65         str << getTessPrimitiveTypeShaderName(primitiveType) << "_" << getSpacingModeShaderName(spacingMode);
66         return str.str();
67 }
68
69 std::vector<TessLevels> genTessLevelCases (const TessPrimitiveType      primitiveType,
70                                                                                    const SpacingMode            spacingMode)
71 {
72         static const TessLevels rawTessLevelCases[] =
73         {
74                 { { 1.0f,       1.0f    },      { 1.0f,         1.0f,   1.0f,   1.0f    } },
75                 { { 63.0f,      24.0f   },      { 15.0f,        42.0f,  10.0f,  12.0f   } },
76                 { { 3.0f,       2.0f    },      { 6.0f,         8.0f,   7.0f,   9.0f    } },
77                 { { 4.0f,       6.0f    },      { 2.0f,         3.0f,   1.0f,   4.0f    } },
78                 { { 2.0f,       2.0f    },      { 6.0f,         8.0f,   7.0f,   9.0f    } },
79                 { { 5.0f,       6.0f    },      { 1.0f,         1.0f,   1.0f,   1.0f    } },
80                 { { 1.0f,       6.0f    },      { 2.0f,         3.0f,   1.0f,   4.0f    } },
81                 { { 5.0f,       1.0f    },      { 2.0f,         3.0f,   1.0f,   4.0f    } },
82                 { { 5.2f,       1.6f    },      { 2.9f,         3.4f,   1.5f,   4.1f    } }
83         };
84
85         if (spacingMode == SPACINGMODE_EQUAL)
86                 return std::vector<TessLevels>(DE_ARRAY_BEGIN(rawTessLevelCases), DE_ARRAY_END(rawTessLevelCases));
87         else
88         {
89                 std::vector<TessLevels> result;
90                 result.reserve(DE_LENGTH_OF_ARRAY(rawTessLevelCases));
91
92                 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(rawTessLevelCases); ++tessLevelCaseNdx)
93                 {
94                         TessLevels curTessLevelCase = rawTessLevelCases[tessLevelCaseNdx];
95
96                         float* const inner = &curTessLevelCase.inner[0];
97                         float* const outer = &curTessLevelCase.outer[0];
98
99                         for (int j = 0; j < 2; ++j) inner[j] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[j]));
100                         for (int j = 0; j < 4; ++j) outer[j] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, outer[j]));
101
102                         if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
103                         {
104                                 if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f)
105                                 {
106                                         if (inner[0] == 1.0f)
107                                                 inner[0] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[0] + 0.1f));
108                                 }
109                         }
110                         else if (primitiveType == TESSPRIMITIVETYPE_QUADS)
111                         {
112                                 if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f || outer[3] > 1.0f)
113                                 {
114                                         if (inner[0] == 1.0f) inner[0] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[0] + 0.1f));
115                                         if (inner[1] == 1.0f) inner[1] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[1] + 0.1f));
116                                 }
117                         }
118
119                         result.push_back(curTessLevelCase);
120                 }
121
122                 DE_ASSERT(static_cast<int>(result.size()) == DE_LENGTH_OF_ARRAY(rawTessLevelCases));
123                 return result;
124         }
125 }
126
127 std::vector<tcu::Vec3> generateReferenceTessCoords (const TessPrimitiveType     primitiveType,
128                                                                                                         const SpacingMode               spacingMode,
129                                                                                                         const float*                    innerLevels,
130                                                                                                         const float*                    outerLevels)
131 {
132         if (isPatchDiscarded(primitiveType, outerLevels))
133                 return std::vector<tcu::Vec3>();
134
135         switch (primitiveType)
136         {
137                 case TESSPRIMITIVETYPE_TRIANGLES:
138                 {
139                         int inner;
140                         int outer[3];
141                         getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
142
143                         if (spacingMode != SPACINGMODE_EQUAL)
144                         {
145                                 // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
146                                 DE_ASSERT(de::abs(innerLevels[0] - static_cast<float>(inner)) < 0.001f);
147                                 for (int i = 0; i < 3; ++i)
148                                         DE_ASSERT(de::abs(outerLevels[i] - static_cast<float>(outer[i])) < 0.001f);
149                                 DE_ASSERT(inner > 1 || (outer[0] == 1 && outer[1] == 1 && outer[2] == 1));
150                         }
151
152                         return generateReferenceTriangleTessCoords(spacingMode, inner, outer[0], outer[1], outer[2]);
153                 }
154
155                 case TESSPRIMITIVETYPE_QUADS:
156                 {
157                         int inner[2];
158                         int outer[4];
159                         getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
160
161                         if (spacingMode != SPACINGMODE_EQUAL)
162                         {
163                                 // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
164                                 for (int i = 0; i < 2; ++i)
165                                         DE_ASSERT(de::abs(innerLevels[i] - static_cast<float>(inner[i])) < 0.001f);
166                                 for (int i = 0; i < 4; ++i)
167                                         DE_ASSERT(de::abs(outerLevels[i] - static_cast<float>(outer[i])) < 0.001f);
168
169                                 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));
170                         }
171
172                         return generateReferenceQuadTessCoords(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]);
173                 }
174
175                 case TESSPRIMITIVETYPE_ISOLINES:
176                 {
177                         int outer[2];
178                         getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
179
180                         if (spacingMode != SPACINGMODE_EQUAL)
181                         {
182                                 // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
183                                 DE_ASSERT(de::abs(outerLevels[1] - static_cast<float>(outer[1])) < 0.001f);
184                         }
185
186                         return generateReferenceIsolineTessCoords(outer[0], outer[1]);
187                 }
188
189                 default:
190                         DE_ASSERT(false);
191                         return std::vector<tcu::Vec3>();
192         }
193 }
194
195 void drawPoint (tcu::Surface& dst, const int centerX, const int centerY, const tcu::RGBA& color, const int size)
196 {
197         const int width         = dst.getWidth();
198         const int height        = dst.getHeight();
199         DE_ASSERT(de::inBounds(centerX, 0, width) && de::inBounds(centerY, 0, height));
200         DE_ASSERT(size > 0);
201
202         for (int yOff = -((size-1)/2); yOff <= size/2; ++yOff)
203         for (int xOff = -((size-1)/2); xOff <= size/2; ++xOff)
204         {
205                 const int pixX = centerX + xOff;
206                 const int pixY = centerY + yOff;
207                 if (de::inBounds(pixX, 0, width) && de::inBounds(pixY, 0, height))
208                         dst.setPixel(pixX, pixY, color);
209         }
210 }
211
212 void drawTessCoordPoint (tcu::Surface& dst, const TessPrimitiveType primitiveType, const tcu::Vec3& pt, const tcu::RGBA& color, const int size)
213 {
214         // \note These coordinates should match the description in the log message in TessCoordTestInstance::iterate.
215
216         static const tcu::Vec2 triangleCorners[3] =
217         {
218                 tcu::Vec2(0.95f, 0.95f),
219                 tcu::Vec2(0.5f,  0.95f - 0.9f*deFloatSqrt(3.0f/4.0f)),
220                 tcu::Vec2(0.05f, 0.95f)
221         };
222
223         static const float quadIsolineLDRU[4] =
224         {
225                 0.1f, 0.9f, 0.9f, 0.1f
226         };
227
228         const tcu::Vec2 dstPos = primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? pt.x()*triangleCorners[0]
229                                                                                                                                                   + pt.y()*triangleCorners[1]
230                                                                                                                                                   + pt.z()*triangleCorners[2]
231
232                                           : primitiveType == TESSPRIMITIVETYPE_QUADS ||
233                                                 primitiveType == TESSPRIMITIVETYPE_ISOLINES ? tcu::Vec2((1.0f - pt.x())*quadIsolineLDRU[0] + pt.x()*quadIsolineLDRU[2],
234                                                                                                                                                             (1.0f - pt.y())*quadIsolineLDRU[1] + pt.y()*quadIsolineLDRU[3])
235
236                                           : tcu::Vec2(-1.0f);
237
238         drawPoint(dst,
239                           static_cast<int>(dstPos.x() * (float)dst.getWidth()),
240                           static_cast<int>(dstPos.y() * (float)dst.getHeight()),
241                           color,
242                           size);
243 }
244
245 void drawTessCoordVisualization (tcu::Surface& dst, const TessPrimitiveType primitiveType, 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
257 inline bool vec3XLessThan (const tcu::Vec3& a, const tcu::Vec3& b)
258 {
259         return a.x() < b.x();
260 }
261
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 = std::lower_bound(sorted.begin(), sorted.end(), ref, vec3XLessThan);
266         if (first == sorted.end())
267                 return -1;
268         return static_cast<int>(std::distance(sorted.begin(), first));
269 }
270
271 // Check that all points in subset are (approximately) present also in superset.
272 bool oneWayComparePointSets (tcu::TestLog&                                      log,
273                                                          tcu::Surface&                                  errorDst,
274                                                          const TessPrimitiveType                primitiveType,
275                                                          const std::vector<tcu::Vec3>&  subset,
276                                                          const std::vector<tcu::Vec3>&  superset,
277                                                          const char*                                    subsetName,
278                                                          const char*                                    supersetName,
279                                                          const tcu::RGBA&                               errorColor)
280 {
281         const std::vector<tcu::Vec3> supersetSorted              = sorted(superset, vec3XLessThan);
282         const float                                      epsilon                         = 0.01f;
283         const int                                        maxNumFailurePrints = 5;
284         int                                                      numFailuresDetected = 0;
285
286         for (int subNdx = 0; subNdx < static_cast<int>(subset.size()); ++subNdx)
287         {
288                 const tcu::Vec3& subPt = subset[subNdx];
289
290                 bool matchFound = false;
291
292                 {
293                         // Binary search the index of the first point in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range.
294                         const tcu::Vec3 matchMin                        = subPt - epsilon;
295                         const tcu::Vec3 matchMax                        = subPt + epsilon;
296                         const int               firstCandidateNdx       = binarySearchFirstVec3WithXAtLeast(supersetSorted, matchMin.x());
297
298                         if (firstCandidateNdx >= 0)
299                         {
300                                 // Compare subPt to all points in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range.
301                                 for (int superNdx = firstCandidateNdx; superNdx < static_cast<int>(supersetSorted.size()) && supersetSorted[superNdx].x() <= matchMax.x(); ++superNdx)
302                                 {
303                                         const tcu::Vec3& superPt = supersetSorted[superNdx];
304
305                                         if (tcu::boolAll(tcu::greaterThanEqual  (superPt, matchMin)) &&
306                                                 tcu::boolAll(tcu::lessThanEqual         (superPt, matchMax)))
307                                         {
308                                                 matchFound = true;
309                                                 break;
310                                         }
311                                 }
312                         }
313                 }
314
315                 if (!matchFound)
316                 {
317                         ++numFailuresDetected;
318                         if (numFailuresDetected < maxNumFailurePrints)
319                                 log << tcu::TestLog::Message << "Failure: no matching " << supersetName << " point found for " << subsetName << " point " << subPt << tcu::TestLog::EndMessage;
320                         else if (numFailuresDetected == maxNumFailurePrints)
321                                 log << tcu::TestLog::Message << "Note: More errors follow" << tcu::TestLog::EndMessage;
322
323                         drawTessCoordPoint(errorDst, primitiveType, subPt, errorColor, 4);
324                 }
325         }
326
327         return numFailuresDetected == 0;
328 }
329
330 //! Returns true on matching coordinate sets.
331 bool compareTessCoords (tcu::TestLog&                                   log,
332                                                 TessPrimitiveType                               primitiveType,
333                                                 const std::vector<tcu::Vec3>&   refCoords,
334                                                 const std::vector<tcu::Vec3>&   resCoords)
335 {
336         tcu::Surface    refVisual;
337         tcu::Surface    resVisual;
338         bool                    success = true;
339
340         drawTessCoordVisualization(refVisual, primitiveType, refCoords);
341         drawTessCoordVisualization(resVisual, primitiveType, resCoords);
342
343         // Check that all points in reference also exist in result.
344         success = oneWayComparePointSets(log, refVisual, primitiveType, refCoords, resCoords, "reference", "result", tcu::RGBA::blue()) && success;
345         // Check that all points in result also exist in reference.
346         success = oneWayComparePointSets(log, resVisual, primitiveType, resCoords, refCoords, "result", "reference", tcu::RGBA::red()) && success;
347
348         if (!success)
349         {
350                 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
351                         << tcu::TestLog::Image("RefTessCoordVisualization", "Reference tessCoord visualization", refVisual)
352                         << tcu::TestLog::Message << "Note: in the following result visualization, points that are missing in reference point set are red (if any)" << tcu::TestLog::EndMessage;
353         }
354
355         log << tcu::TestLog::Image("ResTessCoordVisualization", "Result tessCoord visualization", resVisual);
356
357         return success;
358 }
359
360 class TessCoordTest : public TestCase
361 {
362 public:
363                                                                 TessCoordTest   (tcu::TestContext&                      testCtx,
364                                                                                                  const TessPrimitiveType        primitiveType,
365                                                                                                  const SpacingMode                      spacingMode);
366
367         void                                            initPrograms    (SourceCollections&                     programCollection) const;
368         TestInstance*                           createInstance  (Context&                                       context) const;
369
370 private:
371         const TessPrimitiveType         m_primitiveType;
372         const SpacingMode                       m_spacingMode;
373 };
374
375 TessCoordTest::TessCoordTest (tcu::TestContext&                 testCtx,
376                                                           const TessPrimitiveType       primitiveType,
377                                                           const SpacingMode                     spacingMode)
378         : TestCase                      (testCtx, getCaseName(primitiveType, spacingMode), "")
379         , m_primitiveType       (primitiveType)
380         , m_spacingMode         (spacingMode)
381 {
382 }
383
384 void TessCoordTest::initPrograms (SourceCollections& programCollection) const
385 {
386         // Vertex shader - no inputs
387         {
388                 std::ostringstream src;
389                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
390                         << "\n"
391                         << "void main (void)\n"
392                         << "{\n"
393                         << "}\n";
394
395                 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
396         }
397
398         // Tessellation control shader
399         {
400                 std::ostringstream src;
401                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
402                         << "#extension GL_EXT_tessellation_shader : require\n"
403                         << "\n"
404                         << "layout(vertices = 1) out;\n"
405                         << "\n"
406                         << "layout(set = 0, binding = 0, std430) readonly restrict buffer TessLevels {\n"
407                         << "    float inner0;\n"
408                         << "    float inner1;\n"
409                         << "    float outer0;\n"
410                         << "    float outer1;\n"
411                         << "    float outer2;\n"
412                         << "    float outer3;\n"
413                         << "} sb_levels;\n"
414                         << "\n"
415                         << "void main (void)\n"
416                         << "{\n"
417                         << "    gl_TessLevelInner[0] = sb_levels.inner0;\n"
418                         << "    gl_TessLevelInner[1] = sb_levels.inner1;\n"
419                         << "\n"
420                         << "    gl_TessLevelOuter[0] = sb_levels.outer0;\n"
421                         << "    gl_TessLevelOuter[1] = sb_levels.outer1;\n"
422                         << "    gl_TessLevelOuter[2] = sb_levels.outer2;\n"
423                         << "    gl_TessLevelOuter[3] = sb_levels.outer3;\n"
424                         << "}\n";
425
426                 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
427         }
428
429         // Tessellation evaluation shader
430         {
431                 std::ostringstream src;
432                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
433                         << "#extension GL_EXT_tessellation_shader : require\n"
434                         << "\n"
435                         << "layout(" << getTessPrimitiveTypeShaderName(m_primitiveType) << ", "
436                                                  << getSpacingModeShaderName(m_spacingMode) << ", point_mode) in;\n"
437                         << "\n"
438                         << "layout(set = 0, binding = 1, std430) coherent restrict buffer Output {\n"
439                         << "    int  numInvocations;\n"
440                         << "    vec3 tessCoord[];\n"            // alignment is 16 bytes, same as vec4
441                         << "} sb_out;\n"
442                         << "\n"
443                         << "void main (void)\n"
444                         << "{\n"
445                         << "    int index = atomicAdd(sb_out.numInvocations, 1);\n"
446                         << "    sb_out.tessCoord[index] = gl_TessCoord;\n"
447                         << "}\n";
448
449                 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
450         }
451 }
452
453 class TessCoordTestInstance : public TestInstance
454 {
455 public:
456                                                                 TessCoordTestInstance (Context&                                 context,
457                                                                                                            const TessPrimitiveType      primitiveType,
458                                                                                                            const SpacingMode            spacingMode);
459
460         tcu::TestStatus                         iterate                           (void);
461
462 private:
463         const TessPrimitiveType         m_primitiveType;
464         const SpacingMode                       m_spacingMode;
465 };
466
467 TessCoordTestInstance::TessCoordTestInstance (Context&                                  context,
468                                                                                           const TessPrimitiveType       primitiveType,
469                                                                                           const SpacingMode                     spacingMode)
470         : TestInstance          (context)
471         , m_primitiveType       (primitiveType)
472         , m_spacingMode         (spacingMode)
473 {
474 }
475
476 tcu::TestStatus TessCoordTestInstance::iterate (void)
477 {
478         const DeviceInterface&  vk                                      = m_context.getDeviceInterface();
479         const VkDevice                  device                          = m_context.getDevice();
480         const VkQueue                   queue                           = m_context.getUniversalQueue();
481         const deUint32                  queueFamilyIndex        = m_context.getUniversalQueueFamilyIndex();
482         Allocator&                              allocator                       = m_context.getDefaultAllocator();
483
484         // Test data
485
486         const std::vector<TessLevels>            tessLevelCases                 = genTessLevelCases(m_primitiveType, m_spacingMode);
487         std::vector<std::vector<tcu::Vec3> > allReferenceTessCoords     (tessLevelCases.size());
488
489         for (deUint32 i = 0; i < tessLevelCases.size(); ++i)
490                 allReferenceTessCoords[i] = generateReferenceTessCoords(m_primitiveType, m_spacingMode, &tessLevelCases[i].inner[0], &tessLevelCases[i].outer[0]);
491
492         const size_t maxNumVertices = static_cast<int>(std::max_element(allReferenceTessCoords.begin(), allReferenceTessCoords.end(), SizeLessThan<std::vector<tcu::Vec3> >())->size());
493
494         // Input buffer: tessellation levels. Data is filled in later.
495
496         const Buffer tessLevelsBuffer(vk, device, allocator,
497                 makeBufferCreateInfo(sizeof(TessLevels), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
498
499         // Output buffer: number of invocations + padding + tessellation coordinates. Initialized later.
500
501         const int          resultBufferTessCoordsOffset  = 4 * (int)sizeof(deInt32);
502         const int          extraneousVertices                    = 16;  // allow some room for extraneous vertices from duplicate shader invocations (number is arbitrary)
503         const VkDeviceSize resultBufferSizeBytes                 = resultBufferTessCoordsOffset + (maxNumVertices + extraneousVertices)*sizeof(tcu::Vec4);
504         const Buffer       resultBuffer                                  (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
505
506         // Descriptors
507
508         const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
509                 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)
510                 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
511                 .build(vk, device));
512
513         const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
514                 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
515                 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
516                 .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
517
518         const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
519
520         const VkDescriptorBufferInfo tessLevelsBufferInfo = makeDescriptorBufferInfo(tessLevelsBuffer.get(), 0ull, sizeof(TessLevels));
521         const VkDescriptorBufferInfo resultBufferInfo     = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
522
523         DescriptorSetUpdateBuilder()
524                 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo)
525                 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
526                 .update(vk, device);
527
528         // Pipeline: set up vertex processing without rasterization
529
530         const Unique<VkRenderPass>              renderPass              (makeRenderPassWithoutAttachments (vk, device));
531         const Unique<VkFramebuffer>             framebuffer             (makeFramebufferWithoutAttachments(vk, device, *renderPass));
532         const Unique<VkPipelineLayout>  pipelineLayout  (makePipelineLayout(vk, device, *descriptorSetLayout));
533         const Unique<VkCommandPool>             cmdPool                 (makeCommandPool(vk, device, queueFamilyIndex));
534         const Unique<VkCommandBuffer>   cmdBuffer               (allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
535
536         const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
537                 .setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                      m_context.getBinaryCollection().get("vert"), DE_NULL)
538                 .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,        m_context.getBinaryCollection().get("tesc"), DE_NULL)
539                 .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
540                 .build    (vk, device, *pipelineLayout, *renderPass));
541
542         deUint32 numPassedCases = 0;
543
544         // Repeat the test for all tessellation coords cases
545         for (deUint32 tessLevelCaseNdx = 0; tessLevelCaseNdx < tessLevelCases.size(); ++tessLevelCaseNdx)
546         {
547                 m_context.getTestContext().getLog()
548                         << tcu::TestLog::Message
549                         << "Tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx], m_primitiveType)
550                         << tcu::TestLog::EndMessage;
551
552                 // Upload tessellation levels data to the input buffer
553                 {
554                         const Allocation& alloc = tessLevelsBuffer.getAllocation();
555                         TessLevels* const bufferTessLevels = static_cast<TessLevels*>(alloc.getHostPtr());
556                         *bufferTessLevels = tessLevelCases[tessLevelCaseNdx];
557                         flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(TessLevels));
558                 }
559
560                 // Clear the results buffer
561                 {
562                         const Allocation& alloc = resultBuffer.getAllocation();
563                         deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
564                         flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
565                 }
566
567                 // Reset the command buffer and begin recording.
568                 beginCommandBuffer(vk, *cmdBuffer);
569                 beginRenderPassWithRasterizationDisabled(vk, *cmdBuffer, *renderPass, *framebuffer);
570
571                 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
572                 vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
573
574                 // Process a single abstract vertex.
575                 vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
576                 endRenderPass(vk, *cmdBuffer);
577
578                 {
579                         const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
580                                 VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes);
581
582                         vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
583                                 0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
584                 }
585
586                 endCommandBuffer(vk, *cmdBuffer);
587                 submitCommandsAndWait(vk, device, queue, *cmdBuffer);
588
589                 // Verify results
590                 {
591                         const Allocation& resultAlloc = resultBuffer.getAllocation();
592                         invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
593
594                         const deInt32                                   numResults                      = *static_cast<deInt32*>(resultAlloc.getHostPtr());
595                         const std::vector<tcu::Vec3>    resultTessCoords    = readInterleavedData<tcu::Vec3>(numResults, resultAlloc.getHostPtr(), resultBufferTessCoordsOffset, sizeof(tcu::Vec4));
596                         const std::vector<tcu::Vec3>&   referenceTessCoords = allReferenceTessCoords[tessLevelCaseNdx];
597                         const int                                               numExpectedResults  = static_cast<int>(referenceTessCoords.size());
598                         tcu::TestLog&                                   log                                     = m_context.getTestContext().getLog();
599
600                         if (numResults < numExpectedResults)
601                         {
602                                 log << tcu::TestLog::Message
603                                         << "Failure: generated " << numResults << " coordinates, but the expected reference value is " << numExpectedResults
604                                         << tcu::TestLog::EndMessage;
605                         }
606                         else if (numResults == numExpectedResults)
607                                 log << tcu::TestLog::Message << "Note: generated " << numResults << " tessellation coordinates" << tcu::TestLog::EndMessage;
608                         else
609                         {
610                                 log << tcu::TestLog::Message
611                                         << "Note: generated " << numResults << " coordinates (out of which " << numExpectedResults << " must be unique)"
612                                         << tcu::TestLog::EndMessage;
613                         }
614
615                         if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
616                                 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;
617                         else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS || m_primitiveType == TESSPRIMITIVETYPE_ISOLINES)
618                                 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;
619                         else
620                                 DE_ASSERT(false);
621
622                         if (compareTessCoords(log, m_primitiveType, referenceTessCoords, resultTessCoords) && (numResults >= numExpectedResults))
623                                 ++numPassedCases;
624                 }
625         }  // for tessLevelCaseNdx
626
627         return (numPassedCases == tessLevelCases.size() ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Some cases have failed"));
628 }
629
630 TestInstance* TessCoordTest::createInstance (Context& context) const
631 {
632         requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
633
634         return new TessCoordTestInstance(context, m_primitiveType, m_spacingMode);
635 }
636
637 } // anonymous
638
639 //! Based on dEQP-GLES31.functional.tessellation.tesscoord.*
640 //! \note Transform feedback is replaced with SSBO. Because of that, this version allows duplicate coordinates from shader invocations.
641 //! The test still fails if not enough coordinates are generated, or if coordinates don't match the reference data.
642 tcu::TestCaseGroup* createCoordinatesTests (tcu::TestContext& testCtx)
643 {
644         de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "tesscoord", "Tessellation coordinates tests"));
645
646         for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; ++primitiveTypeNdx)
647         for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
648                 group->addChild(new TessCoordTest(testCtx, (TessPrimitiveType)primitiveTypeNdx, (SpacingMode)spacingModeNdx));
649
650         return group.release();
651 }
652
653 } // tessellation
654 } // vkt