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