Add tessellation shader tests (ported from ES 3.1)
[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, static_cast<int>(dstPos.x() * dst.getWidth()), static_cast<int>(dstPos.y() * dst.getHeight()), color, size);
239 }
240
241 void drawTessCoordVisualization (tcu::Surface& dst, const TessPrimitiveType primitiveType, const std::vector<tcu::Vec3>& coords)
242 {
243         const int imageWidth  = 256;
244         const int imageHeight = 256;
245         dst.setSize(imageWidth, imageHeight);
246
247         tcu::clear(dst.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
248
249         for (int i = 0; i < static_cast<int>(coords.size()); ++i)
250                 drawTessCoordPoint(dst, primitiveType, coords[i], tcu::RGBA::white(), 2);
251 }
252
253 inline bool vec3XLessThan (const tcu::Vec3& a, const tcu::Vec3& b)
254 {
255         return a.x() < b.x();
256 }
257
258 int binarySearchFirstVec3WithXAtLeast (const std::vector<tcu::Vec3>& sorted, float x)
259 {
260         const tcu::Vec3 ref(x, 0.0f, 0.0f);
261         const std::vector<tcu::Vec3>::const_iterator first = std::lower_bound(sorted.begin(), sorted.end(), ref, vec3XLessThan);
262         if (first == sorted.end())
263                 return -1;
264         return static_cast<int>(std::distance(sorted.begin(), first));
265 }
266
267 // Check that all points in subset are (approximately) present also in superset.
268 bool oneWayComparePointSets (tcu::TestLog&                                      log,
269                                                          tcu::Surface&                                  errorDst,
270                                                          const TessPrimitiveType                primitiveType,
271                                                          const std::vector<tcu::Vec3>&  subset,
272                                                          const std::vector<tcu::Vec3>&  superset,
273                                                          const char*                                    subsetName,
274                                                          const char*                                    supersetName,
275                                                          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; superNdx < static_cast<int>(supersetSorted.size()) && supersetSorted[superNdx].x() <= matchMax.x(); ++superNdx)
298                                 {
299                                         const tcu::Vec3& superPt = supersetSorted[superNdx];
300
301                                         if (tcu::boolAll(tcu::greaterThanEqual  (superPt, matchMin)) &&
302                                                 tcu::boolAll(tcu::lessThanEqual         (superPt, matchMax)))
303                                         {
304                                                 matchFound = true;
305                                                 break;
306                                         }
307                                 }
308                         }
309                 }
310
311                 if (!matchFound)
312                 {
313                         ++numFailuresDetected;
314                         if (numFailuresDetected < maxNumFailurePrints)
315                                 log << tcu::TestLog::Message << "Failure: no matching " << supersetName << " point found for " << subsetName << " point " << subPt << tcu::TestLog::EndMessage;
316                         else if (numFailuresDetected == maxNumFailurePrints)
317                                 log << tcu::TestLog::Message << "Note: More errors follow" << tcu::TestLog::EndMessage;
318
319                         drawTessCoordPoint(errorDst, primitiveType, subPt, errorColor, 4);
320                 }
321         }
322
323         return numFailuresDetected == 0;
324 }
325
326 //! Returns true on matching coordinate sets.
327 bool compareTessCoords (tcu::TestLog&                                   log,
328                                                 TessPrimitiveType                               primitiveType,
329                                                 const std::vector<tcu::Vec3>&   refCoords,
330                                                 const std::vector<tcu::Vec3>&   resCoords)
331 {
332         tcu::Surface    refVisual;
333         tcu::Surface    resVisual;
334         bool                    success = true;
335
336         drawTessCoordVisualization(refVisual, primitiveType, refCoords);
337         drawTessCoordVisualization(resVisual, primitiveType, resCoords);
338
339         // Check that all points in reference also exist in result.
340         success = oneWayComparePointSets(log, refVisual, primitiveType, refCoords, resCoords, "reference", "result", tcu::RGBA::blue()) && success;
341         // Check that all points in result also exist in reference.
342         success = oneWayComparePointSets(log, resVisual, primitiveType, resCoords, refCoords, "result", "reference", tcu::RGBA::red()) && success;
343
344         if (!success)
345         {
346                 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
347                         << tcu::TestLog::Image("RefTessCoordVisualization", "Reference tessCoord visualization", refVisual)
348                         << tcu::TestLog::Message << "Note: in the following result visualization, points that are missing in reference point set are red (if any)" << tcu::TestLog::EndMessage;
349         }
350
351         log << tcu::TestLog::Image("ResTessCoordVisualization", "Result tessCoord visualization", resVisual);
352
353         return success;
354 }
355
356 class TessCoordTest : public TestCase
357 {
358 public:
359                                                                 TessCoordTest   (tcu::TestContext&                      testCtx,
360                                                                                                  const TessPrimitiveType        primitiveType,
361                                                                                                  const SpacingMode                      spacingMode);
362
363         void                                            initPrograms    (SourceCollections&                     programCollection) const;
364         TestInstance*                           createInstance  (Context&                                       context) const;
365
366 private:
367         const TessPrimitiveType         m_primitiveType;
368         const SpacingMode                       m_spacingMode;
369 };
370
371 TessCoordTest::TessCoordTest (tcu::TestContext&                 testCtx,
372                                                           const TessPrimitiveType       primitiveType,
373                                                           const SpacingMode                     spacingMode)
374         : TestCase                      (testCtx, getCaseName(primitiveType, spacingMode), "")
375         , m_primitiveType       (primitiveType)
376         , m_spacingMode         (spacingMode)
377 {
378 }
379
380 void TessCoordTest::initPrograms (SourceCollections& programCollection) const
381 {
382         // Vertex shader - no inputs
383         {
384                 std::ostringstream src;
385                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
386                         << "\n"
387                         << "void main (void)\n"
388                         << "{\n"
389                         << "}\n";
390
391                 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
392         }
393
394         // Tessellation control shader
395         {
396                 std::ostringstream src;
397                 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
398                         << "#extension GL_EXT_tessellation_shader : require\n"
399                         << "\n"
400                         << "layout(vertices = 1) out;\n"
401                         << "\n"
402                         << "layout(set = 0, binding = 0, std430) readonly restrict buffer TessLevels {\n"
403                         << "    float inner0;\n"
404                         << "    float inner1;\n"
405                         << "    float outer0;\n"
406                         << "    float outer1;\n"
407                         << "    float outer2;\n"
408                         << "    float outer3;\n"
409                         << "} sb_levels;\n"
410                         << "\n"
411                         << "void main (void)\n"
412                         << "{\n"
413                         << "    gl_TessLevelInner[0] = sb_levels.inner0;\n"
414                         << "    gl_TessLevelInner[1] = sb_levels.inner1;\n"
415                         << "\n"
416                         << "    gl_TessLevelOuter[0] = sb_levels.outer0;\n"
417                         << "    gl_TessLevelOuter[1] = sb_levels.outer1;\n"
418                         << "    gl_TessLevelOuter[2] = sb_levels.outer2;\n"
419                         << "    gl_TessLevelOuter[3] = sb_levels.outer3;\n"
420                         << "}\n";
421
422                 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
423         }
424
425         // Tessellation evaluation 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(" << getTessPrimitiveTypeShaderName(m_primitiveType) << ", "
432                                                  << getSpacingModeShaderName(m_spacingMode) << ", point_mode) in;\n"
433                         << "\n"
434                         << "layout(set = 0, binding = 1, std430) coherent restrict buffer Output {\n"
435                         << "    int  numInvocations;\n"
436                         << "    vec3 tessCoord[];\n"            // alignment is 16 bytes, same as vec4
437                         << "} sb_out;\n"
438                         << "\n"
439                         << "void main (void)\n"
440                         << "{\n"
441                         << "    int index = atomicAdd(sb_out.numInvocations, 1);\n"
442                         << "    sb_out.tessCoord[index] = gl_TessCoord;\n"
443                         << "}\n";
444
445                 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
446         }
447 }
448
449 class TessCoordTestInstance : public TestInstance
450 {
451 public:
452                                                                 TessCoordTestInstance (Context&                                 context,
453                                                                                                            const TessPrimitiveType      primitiveType,
454                                                                                                            const SpacingMode            spacingMode);
455
456         tcu::TestStatus                         iterate                           (void);
457
458 private:
459         const TessPrimitiveType         m_primitiveType;
460         const SpacingMode                       m_spacingMode;
461 };
462
463 TessCoordTestInstance::TessCoordTestInstance (Context&                                  context,
464                                                                                           const TessPrimitiveType       primitiveType,
465                                                                                           const SpacingMode                     spacingMode)
466         : TestInstance          (context)
467         , m_primitiveType       (primitiveType)
468         , m_spacingMode         (spacingMode)
469 {
470 }
471
472 tcu::TestStatus TessCoordTestInstance::iterate (void)
473 {
474         const DeviceInterface&  vk                                      = m_context.getDeviceInterface();
475         const VkDevice                  device                          = m_context.getDevice();
476         const VkQueue                   queue                           = m_context.getUniversalQueue();
477         const deUint32                  queueFamilyIndex        = m_context.getUniversalQueueFamilyIndex();
478         Allocator&                              allocator                       = m_context.getDefaultAllocator();
479
480         // Test data
481
482         const std::vector<TessLevels>            tessLevelCases                 = genTessLevelCases(m_primitiveType, m_spacingMode);
483         std::vector<std::vector<tcu::Vec3> > allReferenceTessCoords     (tessLevelCases.size());
484
485         for (deUint32 i = 0; i < tessLevelCases.size(); ++i)
486                 allReferenceTessCoords[i] = generateReferenceTessCoords(m_primitiveType, m_spacingMode, &tessLevelCases[i].inner[0], &tessLevelCases[i].outer[0]);
487
488         const int maxNumVertices = static_cast<int>(std::max_element(allReferenceTessCoords.begin(), allReferenceTessCoords.end(), SizeLessThan<std::vector<tcu::Vec3> >())->size());
489
490         // Input buffer: tessellation levels. Data is filled in later.
491
492         const Buffer tessLevelsBuffer(vk, device, allocator,
493                 makeBufferCreateInfo(sizeof(TessLevels), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
494
495         // Output buffer: number of invocations + padding + tessellation coordinates. Initialized later.
496
497         const int          resultBufferTessCoordsOffset  = 4 * sizeof(deInt32);
498         const int          extraneousVertices                    = 16;  // allow some room for extraneous vertices from duplicate shader invocations (number is arbitrary)
499         const VkDeviceSize resultBufferSizeBytes                 = resultBufferTessCoordsOffset + (maxNumVertices + extraneousVertices)*sizeof(tcu::Vec4);
500         const Buffer       resultBuffer                                  (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
501
502         // Descriptors
503
504         const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
505                 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)
506                 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
507                 .build(vk, device));
508
509         const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
510                 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
511                 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
512                 .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
513
514         const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
515
516         const VkDescriptorBufferInfo tessLevelsBufferInfo = makeDescriptorBufferInfo(tessLevelsBuffer.get(), 0ull, sizeof(TessLevels));
517         const VkDescriptorBufferInfo resultBufferInfo     = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
518
519         DescriptorSetUpdateBuilder()
520                 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo)
521                 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
522                 .update(vk, device);
523
524         // Pipeline: set up vertex processing without rasterization
525
526         const Unique<VkRenderPass>              renderPass    (makeRenderPassWithoutAttachments (vk, device));
527         const Unique<VkFramebuffer>             framebuffer   (makeFramebufferWithoutAttachments(vk, device, *renderPass));
528         const Unique<VkPipelineLayout>  pipelineLayout(makePipelineLayout               (vk, device, *descriptorSetLayout));
529         const Unique<VkCommandPool>             cmdPool       (makeCommandPool                  (vk, device, queueFamilyIndex));
530         const Unique<VkCommandBuffer>   cmdBuffer     (makeCommandBuffer                (vk, device, *cmdPool));
531
532         const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
533                 .setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                      m_context.getBinaryCollection().get("vert"), DE_NULL)
534                 .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,        m_context.getBinaryCollection().get("tesc"), DE_NULL)
535                 .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
536                 .build    (vk, device, *pipelineLayout, *renderPass));
537
538         deUint32 numPassedCases = 0;
539
540         // Repeat the test for all tessellation coords cases
541         for (deUint32 tessLevelCaseNdx = 0; tessLevelCaseNdx < tessLevelCases.size(); ++tessLevelCaseNdx)
542         {
543                 m_context.getTestContext().getLog()
544                         << tcu::TestLog::Message
545                         << "Tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx], m_primitiveType)
546                         << tcu::TestLog::EndMessage;
547
548                 // Upload tessellation levels data to the input buffer
549                 {
550                         const Allocation& alloc = tessLevelsBuffer.getAllocation();
551                         TessLevels* const bufferTessLevels = static_cast<TessLevels*>(alloc.getHostPtr());
552                         *bufferTessLevels = tessLevelCases[tessLevelCaseNdx];
553                         flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(TessLevels));
554                 }
555
556                 // Clear the results buffer
557                 {
558                         const Allocation& alloc = resultBuffer.getAllocation();
559                         deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
560                         flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
561                 }
562
563                 // Reset the command buffer and begin recording.
564                 beginCommandBuffer(vk, *cmdBuffer);
565                 beginRenderPassWithRasterizationDisabled(vk, *cmdBuffer, *renderPass, *framebuffer);
566
567                 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
568                 vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
569
570                 // Process a single abstract vertex.
571                 vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
572                 endRenderPass(vk, *cmdBuffer);
573
574                 {
575                         const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
576                                 VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes);
577
578                         vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
579                                 0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
580                 }
581
582                 endCommandBuffer(vk, *cmdBuffer);
583                 submitCommandsAndWait(vk, device, queue, *cmdBuffer);
584
585                 // Verify results
586                 {
587                         const Allocation& resultAlloc = resultBuffer.getAllocation();
588                         invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
589
590                         const deInt32                                   numResults                      = *static_cast<deInt32*>(resultAlloc.getHostPtr());
591                         const std::vector<tcu::Vec3>    resultTessCoords    = readInterleavedData<tcu::Vec3>(numResults, resultAlloc.getHostPtr(), resultBufferTessCoordsOffset, sizeof(tcu::Vec4));
592                         const std::vector<tcu::Vec3>&   referenceTessCoords = allReferenceTessCoords[tessLevelCaseNdx];
593                         const int                                               numExpectedResults  = static_cast<int>(referenceTessCoords.size());
594                         tcu::TestLog&                                   log                                     = m_context.getTestContext().getLog();
595
596                         if (numResults < numExpectedResults)
597                         {
598                                 log << tcu::TestLog::Message
599                                         << "Failure: generated " << numResults << " coordinates, but the expected reference value is " << numExpectedResults
600                                         << tcu::TestLog::EndMessage;
601                         }
602                         else if (numResults == numExpectedResults)
603                                 log << tcu::TestLog::Message << "Note: generated " << numResults << " tessellation coordinates" << tcu::TestLog::EndMessage;
604                         else
605                         {
606                                 log << tcu::TestLog::Message
607                                         << "Note: generated " << numResults << " coordinates (out of which " << numExpectedResults << " must be unique)"
608                                         << tcu::TestLog::EndMessage;
609                         }
610
611                         if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
612                                 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;
613                         else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS || m_primitiveType == TESSPRIMITIVETYPE_ISOLINES)
614                                 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;
615                         else
616                                 DE_ASSERT(false);
617
618                         if (compareTessCoords(log, m_primitiveType, referenceTessCoords, resultTessCoords) && (numResults >= numExpectedResults))
619                                 ++numPassedCases;
620                 }
621         }  // for tessLevelCaseNdx
622
623         return (numPassedCases == tessLevelCases.size() ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Some cases have failed"));
624 }
625
626 TestInstance* TessCoordTest::createInstance (Context& context) const
627 {
628         requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
629
630         return new TessCoordTestInstance(context, m_primitiveType, m_spacingMode);
631 }
632
633 } // anonymous
634
635 //! Based on dEQP-GLES31.functional.tessellation.tesscoord.*
636 //! \note Transform feedback is replaced with SSBO. Because of that, this version allows duplicate coordinates from shader invocations.
637 //! The test still fails if not enough coordinates are generated, or if coordinates don't match the reference data.
638 tcu::TestCaseGroup* createCoordinatesTests (tcu::TestContext& testCtx)
639 {
640         de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "tesscoord", "Tessellation coordinates tests"));
641
642         for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; ++primitiveTypeNdx)
643         for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
644                 group->addChild(new TessCoordTest(testCtx, (TessPrimitiveType)primitiveTypeNdx, (SpacingMode)spacingModeNdx));
645
646         return group.release();
647 }
648
649 } // tessellation
650 } // vkt