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