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 Fractional Spacing Tests
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktTessellationFractionalSpacingTests.hpp"
26 #include "vktTestCaseUtil.hpp"
27 #include "vktTessellationUtil.hpp"
28 
29 #include "tcuTestLog.hpp"
30 
31 #include "vkDefs.hpp"
32 #include "vkBarrierUtil.hpp"
33 #include "vkQueryUtil.hpp"
34 #include "vkBuilderUtil.hpp"
35 #include "vkTypeUtil.hpp"
36 #include "vkCmdUtil.hpp"
37 #include "vkObjUtil.hpp"
38 #include "vkBufferWithMemory.hpp"
39 #include "vkImageWithMemory.hpp"
40 
41 #include "deUniquePtr.hpp"
42 #include "deStringUtil.hpp"
43 
44 #include <string>
45 #include <vector>
46 
47 namespace vkt
48 {
49 namespace tessellation
50 {
51 
52 using namespace vk;
53 
54 namespace
55 {
56 
57 template <typename T, typename MembT>
members(const std::vector<T> & objs,MembT T::* membP)58 std::vector<MembT> members(const std::vector<T> &objs, MembT T::*membP)
59 {
60     std::vector<MembT> result(objs.size());
61     for (int i = 0; i < static_cast<int>(objs.size()); ++i)
62         result[i] = objs[i].*membP;
63     return result;
64 }
65 
66 //! Predicate functor for comparing structs by their members.
67 template <typename Pred, typename T, typename MembT>
68 class MemberPred
69 {
70 public:
MemberPred(MembT T::* membP)71     MemberPred(MembT T::*membP) : m_membP(membP), m_pred(Pred())
72     {
73     }
operator ()(const T & a,const T & b) const74     bool operator()(const T &a, const T &b) const
75     {
76         return m_pred(a.*m_membP, b.*m_membP);
77     }
78 
79 private:
80     MembT T::*m_membP;
81     Pred m_pred;
82 };
83 
84 //! Convenience wrapper for MemberPred, because class template arguments aren't deduced based on constructor arguments.
85 template <template <typename> class Pred, typename T, typename MembT>
memberPred(MembT T::* membP)86 inline MemberPred<Pred<MembT>, T, MembT> memberPred(MembT T::*membP)
87 {
88     return MemberPred<Pred<MembT>, T, MembT>(membP);
89 }
90 
91 struct Segment
92 {
93     int index; //!< Index of left coordinate in sortedXCoords.
94     float length;
95 
Segmentvkt::tessellation::__anon9b0c31420111::Segment96     Segment(void) : index(-1), length(-1.0f)
97     {
98     }
Segmentvkt::tessellation::__anon9b0c31420111::Segment99     Segment(int index_, float length_) : index(index_), length(length_)
100     {
101     }
102 };
103 
lengths(const std::vector<Segment> & segments)104 inline std::vector<float> lengths(const std::vector<Segment> &segments)
105 {
106     return members(segments, &Segment::length);
107 }
108 
109 struct LineData
110 {
111     float tessLevel;
112     float additionalSegmentLength;
113     int additionalSegmentLocation;
114 
LineDatavkt::tessellation::__anon9b0c31420111::LineData115     LineData(float lev, float len, int loc)
116         : tessLevel(lev)
117         , additionalSegmentLength(len)
118         , additionalSegmentLocation(loc)
119     {
120     }
121 };
122 
123 struct TestParams
124 {
125     ShaderLanguage shaderLanguage;
126     SpacingMode spacingMode;
127 
TestParamsvkt::tessellation::__anon9b0c31420111::TestParams128     TestParams(ShaderLanguage sl, SpacingMode sm) : shaderLanguage(sl), spacingMode(sm)
129     {
130     }
131 };
132 
133 /*--------------------------------------------------------------------*//*!
134  * \brief Verify fractional spacing conditions for a single line
135  *
136  * Verify that the splitting of an edge (resulting from e.g. an isoline
137  * with outer levels { 1.0, tessLevel }) with a given fractional spacing
138  * mode fulfills certain conditions given in the spec.
139  *
140  * Note that some conditions can't be checked from just one line
141  * (specifically, that the additional segment decreases monotonically
142  * length and the requirement that the additional segments be placed
143  * identically for identical values of clamped level).
144  *
145  * Therefore, the function stores some values to additionalSegmentLengthDst
146  * and additionalSegmentLocationDst that can later be given to
147  * verifyFractionalSpacingMultiple(). A negative value in length means that
148  * no additional segments are present, i.e. there's just one segment.
149  * A negative value in location means that the value wasn't determinable,
150  * i.e. all segments had same length.
151  * The values are not stored if false is returned.
152  *//*--------------------------------------------------------------------*/
verifyFractionalSpacingSingle(tcu::TestLog & log,const SpacingMode spacingMode,const float tessLevel,const std::vector<float> & coords,float * const pOutAdditionalSegmentLength,int * const pOutAdditionalSegmentLocation)153 bool verifyFractionalSpacingSingle(tcu::TestLog &log, const SpacingMode spacingMode, const float tessLevel,
154                                    const std::vector<float> &coords, float *const pOutAdditionalSegmentLength,
155                                    int *const pOutAdditionalSegmentLocation)
156 {
157     DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN);
158 
159     const float clampedLevel              = getClampedTessLevel(spacingMode, tessLevel);
160     const int finalLevel                  = getRoundedTessLevel(spacingMode, clampedLevel);
161     const std::vector<float> sortedCoords = sorted(coords);
162     std::string failNote                  = "Note: tessellation level is " + de::toString(tessLevel) +
163                            "\nNote: sorted coordinates are:\n    " + containerStr(sortedCoords);
164 
165     if (static_cast<int>(coords.size()) != finalLevel + 1)
166     {
167         log << tcu::TestLog::Message << "Failure: number of vertices is " << coords.size() << "; expected "
168             << finalLevel + 1 << " (clamped tessellation level is " << clampedLevel << ")"
169             << "; final level (clamped level rounded up to "
170             << (spacingMode == SPACINGMODE_FRACTIONAL_EVEN ? "even" : "odd") << ") is " << finalLevel
171             << " and should equal the number of segments, i.e. number of vertices minus 1" << tcu::TestLog::EndMessage
172             << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage;
173         return false;
174     }
175 
176     if (sortedCoords[0] != 0.0f || sortedCoords.back() != 1.0f)
177     {
178         log << tcu::TestLog::Message << "Failure: smallest coordinate should be 0.0 and biggest should be 1.0"
179             << tcu::TestLog::EndMessage << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage;
180         return false;
181     }
182 
183     {
184         std::vector<Segment> segments(finalLevel);
185         for (int i = 0; i < finalLevel; ++i)
186             segments[i] = Segment(i, sortedCoords[i + 1] - sortedCoords[i]);
187 
188         failNote += "\nNote: segment lengths are, from left to right:\n    " + containerStr(lengths(segments));
189 
190         {
191             // Divide segments to two different groups based on length.
192 
193             std::vector<Segment> segmentsA;
194             std::vector<Segment> segmentsB;
195             segmentsA.push_back(segments[0]);
196 
197             for (int segNdx = 1; segNdx < static_cast<int>(segments.size()); ++segNdx)
198             {
199                 const float epsilon = 0.001f;
200                 const Segment &seg  = segments[segNdx];
201 
202                 if (de::abs(seg.length - segmentsA[0].length) < epsilon)
203                     segmentsA.push_back(seg);
204                 else if (segmentsB.empty() || de::abs(seg.length - segmentsB[0].length) < epsilon)
205                     segmentsB.push_back(seg);
206                 else
207                 {
208                     log << tcu::TestLog::Message << "Failure: couldn't divide segments to 2 groups by length; "
209                         << "e.g. segment of length " << seg.length << " isn't approximately equal to either "
210                         << segmentsA[0].length << " or " << segmentsB[0].length << tcu::TestLog::EndMessage
211                         << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage;
212                     return false;
213                 }
214             }
215 
216             if (clampedLevel == static_cast<float>(finalLevel))
217             {
218                 // All segments should be of equal length.
219                 if (!segmentsA.empty() && !segmentsB.empty())
220                 {
221                     log << tcu::TestLog::Message
222                         << "Failure: clamped and final tessellation level are equal, but not all segments are of equal "
223                            "length."
224                         << tcu::TestLog::EndMessage << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage;
225                     return false;
226                 }
227             }
228 
229             if (segmentsA.empty() || segmentsB.empty()) // All segments have same length. This is ok.
230             {
231                 *pOutAdditionalSegmentLength   = (segments.size() == 1 ? -1.0f : segments[0].length);
232                 *pOutAdditionalSegmentLocation = -1;
233                 return true;
234             }
235 
236             if (segmentsA.size() != 2 && segmentsB.size() != 2)
237             {
238                 log << tcu::TestLog::Message
239                     << "Failure: when dividing the segments to 2 groups by length, neither of the two groups has "
240                        "exactly 2 or 0 segments in it"
241                     << tcu::TestLog::EndMessage << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage;
242                 return false;
243             }
244 
245             // For convenience, arrange so that the 2-segment group is segmentsB.
246             if (segmentsB.size() != 2)
247                 std::swap(segmentsA, segmentsB);
248 
249             // \note For 4-segment lines both segmentsA and segmentsB have 2 segments each.
250             //         Thus, we can't be sure which ones were meant as the additional segments.
251             //         We give the benefit of the doubt by assuming that they're the shorter
252             //         ones (as they should).
253 
254             if (segmentsA.size() != 2)
255             {
256                 if (segmentsB[0].length > segmentsA[0].length + 0.001f)
257                 {
258                     log << tcu::TestLog::Message
259                         << "Failure: the two additional segments are longer than the other segments"
260                         << tcu::TestLog::EndMessage << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage;
261                     return false;
262                 }
263             }
264             else
265             {
266                 // We have 2 segmentsA and 2 segmentsB, ensure segmentsB has the shorter lengths
267                 if (segmentsB[0].length > segmentsA[0].length)
268                     std::swap(segmentsA, segmentsB);
269             }
270 
271             // Check that the additional segments are placed symmetrically.
272             if (segmentsB[0].index + segmentsB[1].index + 1 != static_cast<int>(segments.size()))
273             {
274                 log << tcu::TestLog::Message << "Failure: the two additional segments aren't placed symmetrically; "
275                     << "one is at index " << segmentsB[0].index << " and other is at index " << segmentsB[1].index
276                     << " (note: the two indexes should sum to " << static_cast<int>(segments.size()) - 1
277                     << ", i.e. numberOfSegments-1)" << tcu::TestLog::EndMessage << tcu::TestLog::Message << failNote
278                     << tcu::TestLog::EndMessage;
279                 return false;
280             }
281 
282             *pOutAdditionalSegmentLength = segmentsB[0].length;
283             if (segmentsA.size() != 2)
284                 *pOutAdditionalSegmentLocation = de::min(segmentsB[0].index, segmentsB[1].index);
285             else
286                 *pOutAdditionalSegmentLocation =
287                     segmentsB[0].length < segmentsA[0].length - 0.001f ?
288                         de::min(segmentsB[0].index, segmentsB[1].index) :
289                         -1; // \note -1 when can't reliably decide which ones are the additional segments, a or b.
290 
291             return true;
292         }
293     }
294 }
295 
296 /*--------------------------------------------------------------------*//*!
297  * \brief Verify fractional spacing conditions between multiple lines
298  *
299  * Verify the fractional spacing conditions that are not checked in
300  * verifyFractionalSpacingSingle(). Uses values given by said function
301  * as parameters, in addition to the spacing mode and tessellation level.
302  *//*--------------------------------------------------------------------*/
verifyFractionalSpacingMultiple(tcu::TestLog & log,const SpacingMode spacingMode,const std::vector<float> & tessLevels,const std::vector<float> & additionalSegmentLengths,const std::vector<int> & additionalSegmentLocations)303 static bool verifyFractionalSpacingMultiple(tcu::TestLog &log, const SpacingMode spacingMode,
304                                             const std::vector<float> &tessLevels,
305                                             const std::vector<float> &additionalSegmentLengths,
306                                             const std::vector<int> &additionalSegmentLocations)
307 {
308     DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN);
309     DE_ASSERT(tessLevels.size() == additionalSegmentLengths.size() &&
310               tessLevels.size() == additionalSegmentLocations.size());
311 
312     std::vector<LineData> lineDatas;
313 
314     for (int i = 0; i < static_cast<int>(tessLevels.size()); ++i)
315         lineDatas.push_back(LineData(tessLevels[i], additionalSegmentLengths[i], additionalSegmentLocations[i]));
316 
317     {
318         const std::vector<LineData> lineDatasSortedByLevel =
319             sorted(lineDatas, memberPred<std::less>(&LineData::tessLevel));
320 
321         // Check that lines with identical clamped tessellation levels have identical additionalSegmentLocation.
322 
323         for (int lineNdx = 1; lineNdx < static_cast<int>(lineDatasSortedByLevel.size()); ++lineNdx)
324         {
325             const LineData &curData  = lineDatasSortedByLevel[lineNdx];
326             const LineData &prevData = lineDatasSortedByLevel[lineNdx - 1];
327 
328             if (curData.additionalSegmentLocation < 0 || prevData.additionalSegmentLocation < 0)
329                 continue; // Unknown locations, skip.
330 
331             if (getClampedTessLevel(spacingMode, curData.tessLevel) ==
332                     getClampedTessLevel(spacingMode, prevData.tessLevel) &&
333                 curData.additionalSegmentLocation != prevData.additionalSegmentLocation)
334             {
335                 log << tcu::TestLog::Message
336                     << "Failure: additional segments not located identically for two edges with identical clamped "
337                        "tessellation levels"
338                     << tcu::TestLog::EndMessage << tcu::TestLog::Message << "Note: tessellation levels are "
339                     << curData.tessLevel << " and " << prevData.tessLevel << " (clamped level "
340                     << getClampedTessLevel(spacingMode, curData.tessLevel) << ")"
341                     << "; but first additional segments located at indices " << curData.additionalSegmentLocation
342                     << " and " << prevData.additionalSegmentLocation << ", respectively" << tcu::TestLog::EndMessage;
343                 return false;
344             }
345         }
346 
347         // Check that, among lines with same clamped rounded tessellation level, additionalSegmentLength is monotonically decreasing with "clampedRoundedTessLevel - clampedTessLevel" (the "fraction").
348 
349         for (int lineNdx = 1; lineNdx < static_cast<int>(lineDatasSortedByLevel.size()); ++lineNdx)
350         {
351             const LineData &curData  = lineDatasSortedByLevel[lineNdx];
352             const LineData &prevData = lineDatasSortedByLevel[lineNdx - 1];
353 
354             if (curData.additionalSegmentLength < 0.0f || prevData.additionalSegmentLength < 0.0f)
355                 continue; // Unknown segment lengths, skip.
356 
357             const float curClampedLevel  = getClampedTessLevel(spacingMode, curData.tessLevel);
358             const float prevClampedLevel = getClampedTessLevel(spacingMode, prevData.tessLevel);
359             const int curFinalLevel      = getRoundedTessLevel(spacingMode, curClampedLevel);
360             const int prevFinalLevel     = getRoundedTessLevel(spacingMode, prevClampedLevel);
361 
362             if (curFinalLevel != prevFinalLevel)
363                 continue;
364 
365             const float curFraction  = static_cast<float>(curFinalLevel) - curClampedLevel;
366             const float prevFraction = static_cast<float>(prevFinalLevel) - prevClampedLevel;
367 
368             if (curData.additionalSegmentLength < prevData.additionalSegmentLength ||
369                 (curClampedLevel == prevClampedLevel &&
370                  curData.additionalSegmentLength != prevData.additionalSegmentLength))
371             {
372                 log << tcu::TestLog::Message
373                     << "Failure: additional segment length isn't monotonically decreasing with the fraction <n> - <f>, "
374                        "among edges with same final tessellation level"
375                     << tcu::TestLog::EndMessage << tcu::TestLog::Message
376                     << "Note: <f> stands for the clamped tessellation level and <n> for the final (rounded and "
377                        "clamped) tessellation level"
378                     << tcu::TestLog::EndMessage << tcu::TestLog::Message << "Note: two edges have tessellation levels "
379                     << prevData.tessLevel << " and " << curData.tessLevel << " respectively"
380                     << ", clamped " << prevClampedLevel << " and " << curClampedLevel << ", final " << prevFinalLevel
381                     << " and " << curFinalLevel << "; fractions are " << prevFraction << " and " << curFraction
382                     << ", but resulted in segment lengths " << prevData.additionalSegmentLength << " and "
383                     << curData.additionalSegmentLength << tcu::TestLog::EndMessage;
384                 return false;
385             }
386         }
387     }
388 
389     return true;
390 }
391 
genTessLevelCases(void)392 std::vector<float> genTessLevelCases(void)
393 {
394     std::vector<float> result;
395 
396     // Ranges [7.0 .. 8.0), [8.0 .. 9.0) and [9.0 .. 10.0)
397     {
398         static const float rangeStarts[] = {7.0f, 8.0f, 9.0f};
399         const int numSamplesPerRange     = 10;
400 
401         for (int rangeNdx = 0; rangeNdx < DE_LENGTH_OF_ARRAY(rangeStarts); ++rangeNdx)
402             for (int i = 0; i < numSamplesPerRange; ++i)
403                 result.push_back(rangeStarts[rangeNdx] + static_cast<float>(i) / numSamplesPerRange);
404     }
405 
406     // 0.3, 1.3, 2.3,  ... , 62.3
407     for (int i = 0; i <= 62; ++i)
408         result.push_back(static_cast<float>(i) + 0.3f);
409 
410     return result;
411 }
412 
413 //! Create a vector of floats from an array of floats. Offset is in bytes.
readFloatArray(const int count,const void * memory)414 std::vector<float> readFloatArray(const int count, const void *memory)
415 {
416     std::vector<float> results(count);
417 
418     if (count != 0)
419     {
420         const float *pFloatData = reinterpret_cast<const float *>(static_cast<const uint8_t *>(memory));
421         deMemcpy(&results[0], pFloatData, sizeof(float) * count);
422     }
423 
424     return results;
425 }
426 
initPrograms(vk::SourceCollections & programCollection,TestParams testParams)427 void initPrograms(vk::SourceCollections &programCollection, TestParams testParams)
428 {
429     if (testParams.shaderLanguage == SHADER_LANGUAGE_GLSL)
430     {
431         // Vertex shader: no inputs
432         {
433             std::ostringstream src;
434             src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
435                 << "\n"
436                 << "void main (void)\n"
437                 << "{\n"
438                 << "}\n";
439 
440             programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
441         }
442 
443         // Tessellation control shader
444         {
445             std::ostringstream src;
446             src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
447                 << "#extension GL_EXT_tessellation_shader : require\n"
448                 << "\n"
449                 << "layout(vertices = 1) out;\n"
450                 << "\n"
451                 << "layout(set = 0, binding = 0, std430) readonly restrict buffer TessLevels {\n"
452                 << "    float outer1;\n"
453                 << "} sb_levels;\n"
454                 << "\n"
455                 << "void main (void)\n"
456                 << "{\n"
457                 << "    gl_TessLevelOuter[0] = 1.0;\n"
458                 << "    gl_TessLevelOuter[1] = sb_levels.outer1;\n"
459                 << "}\n";
460 
461             programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
462         }
463 
464         // Tessellation evaluation shader
465         for (uint32_t i = 0; i < 2; ++i)
466         {
467             bool pointSize = i == 1;
468             std::ostringstream src;
469             src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
470                 << "#extension GL_EXT_tessellation_shader : require\n";
471             if (pointSize)
472                 src << "#extension GL_EXT_tessellation_point_size : require\n";
473             src << "\n"
474                 << "layout(" << getTessPrimitiveTypeShaderName(TESSPRIMITIVETYPE_ISOLINES) << ", "
475                 << getSpacingModeShaderName(testParams.spacingMode) << ", point_mode) in;\n"
476                 << "\n"
477                 << "layout(set = 0, binding = 1, std430) restrict buffer Results {\n"
478                 << "    float data[];\n"
479                 << "} sb_out_tessCoord;\n"
480                 << "layout(set = 0, binding = 2, std430) coherent restrict buffer Counter {\n"
481                 << "    int   data;\n"
482                 << "} sb_out_numInvocations;\n"
483                 << "\n"
484                 << "void main (void)\n"
485                 << "{\n"
486                 << "    int index = atomicAdd(sb_out_numInvocations.data, 1);\n"
487                 << "    sb_out_tessCoord.data[index] = gl_TessCoord.x;\n";
488             if (pointSize)
489                 src << "    gl_PointSize = 1.0f;\n";
490             src << "}\n";
491 
492             std::string tese = pointSize ? "tese_point_size" : "tese";
493             programCollection.glslSources.add(tese.c_str()) << glu::TessellationEvaluationSource(src.str());
494         }
495     }
496     else
497     {
498         // Vertex shader - no inputs
499         {
500             std::ostringstream src;
501             src << "void main (void)\n"
502                 << "{\n"
503                 << "}\n";
504 
505             programCollection.hlslSources.add("vert") << glu::VertexSource(src.str());
506         }
507 
508         // Tessellation control shader
509         {
510             std::ostringstream src;
511             src << "struct HS_CONSTANT_OUT\n"
512                 << "{\n"
513                 << "    float tessLevelsOuter[2] : SV_TessFactor;\n"
514                 << "};\n"
515                 << "\n"
516                 << "tbuffer TessLevels : register(b0)\n"
517                 << "{\n"
518                 << "    float outer1;\n"
519                 << "}\n"
520                 << "\n"
521                 << "[domain(\"isoline\")]\n"
522                 << "[partitioning(\"" << getPartitioningShaderName(testParams.spacingMode) << "\")]\n"
523                 << "[outputtopology(\"point\")]\n"
524                 << "[outputcontrolpoints(1)]\n"
525                 << "[patchconstantfunc(\"PCF\")]\n"
526                 << "void main()\n"
527                 << "{\n"
528                 << "}\n"
529                 << "\n"
530                 << "HS_CONSTANT_OUT PCF()\n"
531                 << "{\n"
532                 << "    HS_CONSTANT_OUT output;\n"
533                 << "    output.tessLevelsOuter[0] = 1.0;\n"
534                 << "    output.tessLevelsOuter[1] = outer1;\n"
535                 << "    return output;\n"
536                 << "}\n";
537 
538             programCollection.hlslSources.add("tesc") << glu::TessellationControlSource(src.str());
539         }
540 
541         // Tessellation evaluation shader
542         {
543             std::ostringstream src;
544 
545             src << "RWStructuredBuffer <float> tessCoord : register(b1);\n"
546                 << "globallycoherent RWStructuredBuffer <int> numInvocations : register(b2);\n"
547                 << "\n"
548                 << "void main(float2 tessCoords : SV_DOMAINLOCATION)\n"
549                 << "{\n"
550                 << "    int index;\n"
551                 << "    InterlockedAdd(numInvocations[0], 1, index);\n"
552                 << "    tessCoord[index] = tessCoords.x;\n"
553                 << "}\n";
554 
555             programCollection.hlslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
556             programCollection.hlslSources.add("tese_point_size") << glu::TessellationEvaluationSource(src.str());
557         }
558     }
559 }
560 
test(Context & context,TestParams testParams)561 tcu::TestStatus test(Context &context, TestParams testParams)
562 {
563     DE_ASSERT(testParams.spacingMode == SPACINGMODE_FRACTIONAL_ODD ||
564               testParams.spacingMode == SPACINGMODE_FRACTIONAL_EVEN);
565     DE_ASSERT(testParams.shaderLanguage == SHADER_LANGUAGE_GLSL || testParams.shaderLanguage == SHADER_LANGUAGE_HLSL);
566 
567     requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(),
568                     FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
569 
570     const DeviceInterface &vk       = context.getDeviceInterface();
571     const VkDevice device           = context.getDevice();
572     const VkQueue queue             = context.getUniversalQueue();
573     const uint32_t queueFamilyIndex = context.getUniversalQueueFamilyIndex();
574     Allocator &allocator            = context.getDefaultAllocator();
575     const bool tessGeomPointSize    = context.getDeviceFeatures().shaderTessellationAndGeometryPointSize;
576 
577     const std::vector<float> tessLevelCases = genTessLevelCases();
578     const int maxNumVertices =
579         1 + getClampedRoundedTessLevel(testParams.spacingMode,
580                                        *std::max_element(tessLevelCases.begin(), tessLevelCases.end()));
581 
582     // Counter buffer: used to calculate offset into result buffer.
583 
584     const VkDeviceSize counterBufferSizeBytes = sizeof(int);
585     const BufferWithMemory counterBuffer(
586         vk, device, allocator, makeBufferCreateInfo(counterBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT),
587         MemoryRequirement::HostVisible);
588 
589     // Result buffer: generated tess coords go here.
590 
591     const VkDeviceSize resultBufferSizeBytes = sizeof(float) * maxNumVertices;
592     const BufferWithMemory resultBuffer(vk, device, allocator,
593                                         makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT),
594                                         MemoryRequirement::HostVisible);
595 
596     // Outer1 tessellation level constant buffer.
597 
598     const VkDeviceSize tessLevelsBufferSizeBytes = sizeof(float); // we pass only outer1
599     const BufferWithMemory tessLevelsBuffer(
600         vk, device, allocator, makeBufferCreateInfo(tessLevelsBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT),
601         MemoryRequirement::HostVisible);
602 
603     // Descriptors
604 
605     const Unique<VkDescriptorSetLayout> descriptorSetLayout(
606         DescriptorSetLayoutBuilder()
607             .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)
608             .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
609             .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
610             .build(vk, device));
611 
612     const Unique<VkDescriptorPool> descriptorPool(
613         DescriptorPoolBuilder()
614             .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
615             .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
616             .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
617             .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
618 
619     const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
620     const VkDescriptorBufferInfo tessLevelsBufferInfo =
621         makeDescriptorBufferInfo(tessLevelsBuffer.get(), 0ull, tessLevelsBufferSizeBytes);
622     const VkDescriptorBufferInfo resultBufferInfo =
623         makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
624     const VkDescriptorBufferInfo counterBufferInfo =
625         makeDescriptorBufferInfo(counterBuffer.get(), 0ull, counterBufferSizeBytes);
626 
627     DescriptorSetUpdateBuilder()
628         .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u),
629                      VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo)
630         .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u),
631                      VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
632         .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(2u),
633                      VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &counterBufferInfo)
634         .update(vk, device);
635 
636     // Pipeline
637 
638     const Unique<VkRenderPass> renderPass(makeRenderPassWithoutAttachments(vk, device));
639     const Unique<VkFramebuffer> framebuffer(makeFramebuffer(vk, device, *renderPass, 0u, DE_NULL, 1u, 1u));
640     const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device, *descriptorSetLayout));
641     const Unique<VkCommandPool> cmdPool(makeCommandPool(vk, device, queueFamilyIndex));
642     const Unique<VkCommandBuffer> cmdBuffer(
643         allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
644 
645     const Unique<VkPipeline> pipeline(
646         GraphicsPipelineBuilder()
647             .setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert"), DE_NULL)
648             .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, context.getBinaryCollection().get("tesc"),
649                        DE_NULL)
650             .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
651                        context.getBinaryCollection().get(tessGeomPointSize ? "tese_point_size" : "tese"), DE_NULL)
652             .build(vk, device, *pipelineLayout, *renderPass));
653 
654     // Data that will be verified across all cases
655     std::vector<float> additionalSegmentLengths;
656     std::vector<int> additionalSegmentLocations;
657 
658     bool success = false;
659 
660     // Repeat the test for all tessellation coords cases
661     for (uint32_t tessLevelCaseNdx = 0; tessLevelCaseNdx < tessLevelCases.size(); ++tessLevelCaseNdx)
662     {
663         // Upload tessellation levels data to the input buffer
664         {
665             const Allocation &alloc      = tessLevelsBuffer.getAllocation();
666             float *const tessLevelOuter1 = static_cast<float *>(alloc.getHostPtr());
667 
668             *tessLevelOuter1 = tessLevelCases[tessLevelCaseNdx];
669             flushAlloc(vk, device, alloc);
670         }
671 
672         // Clear the results buffer
673         {
674             const Allocation &alloc = resultBuffer.getAllocation();
675 
676             deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
677             flushAlloc(vk, device, alloc);
678         }
679 
680         // Clear the counter buffer
681         {
682             const Allocation &alloc = counterBuffer.getAllocation();
683 
684             deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(counterBufferSizeBytes));
685             flushAlloc(vk, device, alloc);
686         }
687 
688         beginCommandBuffer(vk, *cmdBuffer);
689 
690         // Begin render pass
691         beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, makeRect2D(1, 1));
692 
693         vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
694         vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u,
695                                  &descriptorSet.get(), 0u, DE_NULL);
696 
697         vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
698         endRenderPass(vk, *cmdBuffer);
699 
700         {
701             const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
702                 VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes);
703 
704             vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u,
705                                   DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
706         }
707 
708         endCommandBuffer(vk, *cmdBuffer);
709         submitCommandsAndWait(vk, device, queue, *cmdBuffer);
710 
711         // Verify the result.
712         {
713             tcu::TestLog &log              = context.getTestContext().getLog();
714             const Allocation &resultAlloc  = resultBuffer.getAllocation();
715             const Allocation &counterAlloc = counterBuffer.getAllocation();
716 
717             invalidateAlloc(vk, device, resultAlloc);
718             invalidateAlloc(vk, device, counterAlloc);
719 
720             const int32_t numResults                  = *static_cast<int32_t *>(counterAlloc.getHostPtr());
721             const std::vector<float> resultTessCoords = readFloatArray(numResults, resultAlloc.getHostPtr());
722 
723             // Outputs
724             float additionalSegmentLength;
725             int additionalSegmentLocation;
726 
727             success =
728                 verifyFractionalSpacingSingle(log, testParams.spacingMode, tessLevelCases[tessLevelCaseNdx],
729                                               resultTessCoords, &additionalSegmentLength, &additionalSegmentLocation);
730 
731             if (!success)
732                 break;
733 
734             additionalSegmentLengths.push_back(additionalSegmentLength);
735             additionalSegmentLocations.push_back(additionalSegmentLocation);
736         }
737     } // for tessLevelCaseNdx
738 
739     if (success)
740         success = verifyFractionalSpacingMultiple(context.getTestContext().getLog(), testParams.spacingMode,
741                                                   tessLevelCases, additionalSegmentLengths, additionalSegmentLocations);
742 
743     return (success ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
744 }
745 
checkSupportTess(Context & context,const TestParams)746 void checkSupportTess(Context &context, const TestParams)
747 {
748 #ifndef CTS_USES_VULKANSC
749     if (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR *const features = getPortability(context))
750     {
751         checkPointMode(*features);
752         checkIsolines(*features);
753     }
754 #else
755     DE_UNREF(context);
756 #endif // CTS_USES_VULKANSC
757 }
758 
759 } // namespace
760 
761 //! These tests correspond to dEQP-GLES31.functional.tessellation.fractional_spacing.*
762 //! Check validity of fractional spacing modes. Draws a single isoline, reads tess coords with SSBO.
createFractionalSpacingTests(tcu::TestContext & testCtx)763 tcu::TestCaseGroup *createFractionalSpacingTests(tcu::TestContext &testCtx)
764 {
765     de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "fractional_spacing"));
766 
767     addFunctionCaseWithPrograms(group.get(), "glsl_odd", checkSupportTess, initPrograms, test,
768                                 TestParams(SHADER_LANGUAGE_GLSL, SPACINGMODE_FRACTIONAL_ODD));
769     addFunctionCaseWithPrograms(group.get(), "glsl_even", checkSupportTess, initPrograms, test,
770                                 TestParams(SHADER_LANGUAGE_GLSL, SPACINGMODE_FRACTIONAL_EVEN));
771     addFunctionCaseWithPrograms(group.get(), "hlsl_odd", checkSupportTess, initPrograms, test,
772                                 TestParams(SHADER_LANGUAGE_HLSL, SPACINGMODE_FRACTIONAL_ODD));
773     addFunctionCaseWithPrograms(group.get(), "hlsl_even", checkSupportTess, initPrograms, test,
774                                 TestParams(SHADER_LANGUAGE_HLSL, SPACINGMODE_FRACTIONAL_EVEN));
775 
776     return group.release();
777 }
778 
779 } // namespace tessellation
780 } // namespace vkt
781