• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::__anon28fabe0d0111::Segment88 			Segment (void)						: index(-1),		length(-1.0f)	{}
Segmentvkt::tessellation::__anon28fabe0d0111::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::__anon28fabe0d0111::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::__anon28fabe0d0111::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)373 std::vector<float> readFloatArray(const int count, const void* memory)
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));
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 		for (deUint32 i = 0; i < 2; ++i)
425 		{
426 			bool pointSize = i == 1;
427 			std::ostringstream src;
428 			src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
429 				<< "#extension GL_EXT_tessellation_shader : require\n";
430 			if (pointSize)
431 				src << "#extension GL_EXT_tessellation_point_size : require\n";
432 			src << "\n"
433 				<< "layout(" << getTessPrimitiveTypeShaderName(TESSPRIMITIVETYPE_ISOLINES) << ", "
434 							 << getSpacingModeShaderName(testParams.spacingMode) << ", point_mode) in;\n"
435 				<< "\n"
436 				<< "layout(set = 0, binding = 1, std430) restrict buffer Results {\n"
437 				<< "    float data[];\n"
438 				<< "} sb_out_tessCoord;\n"
439 				<< "layout(set = 0, binding = 2, std430) coherent restrict buffer Counter {\n"
440 				<< "    int   data;\n"
441 				<< "} sb_out_numInvocations;\n"
442 				<< "\n"
443 				<< "void main (void)\n"
444 				<< "{\n"
445 				<< "    int index = atomicAdd(sb_out_numInvocations.data, 1);\n"
446 				<< "    sb_out_tessCoord.data[index] = gl_TessCoord.x;\n";
447 			if (pointSize)
448 				src << "    gl_PointSize = 1.0f;\n";
449 			src << "}\n";
450 
451 			std::string tese = pointSize ? "tese_point_size" : "tese";
452 			programCollection.glslSources.add(tese.c_str()) << glu::TessellationEvaluationSource(src.str());
453 		}
454 	}
455 	else
456 	{
457 		// Vertex shader - no inputs
458 		{
459 			std::ostringstream src;
460 			src << "void main (void)\n"
461 				<< "{\n"
462 				<< "}\n";
463 
464 			programCollection.hlslSources.add("vert") << glu::VertexSource(src.str());
465 		}
466 
467 		// Tessellation control shader
468 		{
469 			std::ostringstream src;
470 			src << "struct HS_CONSTANT_OUT\n"
471 				<< "{\n"
472 				<< "    float tessLevelsOuter[2] : SV_TessFactor;\n"
473 				<< "};\n"
474 				<< "\n"
475 				<< "tbuffer TessLevels : register(b0)\n"
476 				<< "{\n"
477 				<< "    float outer1;\n"
478 				<< "}\n"
479 				<< "\n"
480 				<< "[domain(\"isoline\")]\n"
481 				<< "[partitioning(\"" << getPartitioningShaderName(testParams.spacingMode) << "\")]\n"
482 				<< "[outputtopology(\"point\")]\n"
483 				<< "[outputcontrolpoints(1)]\n"
484 				<< "[patchconstantfunc(\"PCF\")]\n"
485 				<< "void main()\n"
486 				<< "{\n"
487 				<< "}\n"
488 				<< "\n"
489 				<< "HS_CONSTANT_OUT PCF()\n"
490 				<< "{\n"
491 				<< "    HS_CONSTANT_OUT output;\n"
492 				<< "    output.tessLevelsOuter[0] = 1.0;\n"
493 				<< "    output.tessLevelsOuter[1] = outer1;\n"
494 				<< "    return output;\n"
495 				<< "}\n";
496 
497 			programCollection.hlslSources.add("tesc") << glu::TessellationControlSource(src.str());
498 		}
499 
500 		// Tessellation evaluation shader
501 		{
502 			std::ostringstream src;
503 
504 			src	<< "RWStructuredBuffer <float> tessCoord : register(b1);\n"
505 				<< "globallycoherent RWStructuredBuffer <int> numInvocations : register(b2);\n"
506 				<< "\n"
507 				<< "void main(float2 tessCoords : SV_DOMAINLOCATION)\n"
508 				<< "{\n"
509 				<< "    int index;\n"
510 				<< "    InterlockedAdd(numInvocations[0], 1, index);\n"
511 				<< "    tessCoord[index] = tessCoords.x;\n"
512 				<< "}\n";
513 
514 			programCollection.hlslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
515 			programCollection.hlslSources.add("tese_point_size") << glu::TessellationEvaluationSource(src.str());
516 		}
517 	}
518 }
519 
test(Context & context,TestParams testParams)520 tcu::TestStatus test (Context& context, TestParams testParams)
521 {
522 	DE_ASSERT(testParams.spacingMode == SPACINGMODE_FRACTIONAL_ODD || testParams.spacingMode == SPACINGMODE_FRACTIONAL_EVEN);
523 	DE_ASSERT(testParams.shaderLanguage == SHADER_LANGUAGE_GLSL || testParams.shaderLanguage == SHADER_LANGUAGE_HLSL);
524 
525 	requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
526 
527 	const DeviceInterface&	vk					= context.getDeviceInterface();
528 	const VkDevice			device				= context.getDevice();
529 	const VkQueue			queue				= context.getUniversalQueue();
530 	const deUint32			queueFamilyIndex	= context.getUniversalQueueFamilyIndex();
531 	Allocator&				allocator			= context.getDefaultAllocator();
532 	const bool				tessGeomPointSize	= context.getDeviceFeatures().shaderTessellationAndGeometryPointSize;
533 
534 	const std::vector<float>	tessLevelCases = genTessLevelCases();
535 	const int					maxNumVertices = 1 + getClampedRoundedTessLevel(testParams.spacingMode, *std::max_element(tessLevelCases.begin(), tessLevelCases.end()));
536 
537 	// Counter buffer: used to calculate offset into result buffer.
538 
539 	const VkDeviceSize		counterBufferSizeBytes = sizeof(int);
540 	const BufferWithMemory	counterBuffer(vk, device, allocator, makeBufferCreateInfo(counterBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
541 
542 	// Result buffer: generated tess coords go here.
543 
544 	const VkDeviceSize		resultBufferSizeBytes = sizeof(float) * maxNumVertices;
545 	const BufferWithMemory	resultBuffer			 (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
546 
547 	// Outer1 tessellation level constant buffer.
548 
549 	const VkDeviceSize		tessLevelsBufferSizeBytes = sizeof(float);  // we pass only outer1
550 	const BufferWithMemory	tessLevelsBuffer			 (vk, device, allocator, makeBufferCreateInfo(tessLevelsBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
551 
552 	// Descriptors
553 
554 	const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
555 		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)
556 		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
557 		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
558 		.build(vk, device));
559 
560 	const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
561 		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
562 		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
563 		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
564 		.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
565 
566 	const Unique<VkDescriptorSet> descriptorSet			(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
567 	const VkDescriptorBufferInfo  tessLevelsBufferInfo	= makeDescriptorBufferInfo(tessLevelsBuffer.get(), 0ull, tessLevelsBufferSizeBytes);
568 	const VkDescriptorBufferInfo  resultBufferInfo		= makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
569 	const VkDescriptorBufferInfo  counterBufferInfo		= makeDescriptorBufferInfo(counterBuffer.get(), 0ull, counterBufferSizeBytes);
570 
571 	DescriptorSetUpdateBuilder()
572 		.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo)
573 		.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
574 		.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(2u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &counterBufferInfo)
575 		.update(vk, device);
576 
577 	// Pipeline
578 
579 	const Unique<VkRenderPass>		renderPass		(makeRenderPassWithoutAttachments	(vk, device));
580 	const Unique<VkFramebuffer>		framebuffer		(makeFramebuffer					(vk, device, *renderPass, 0u, DE_NULL, 1u, 1u));
581 	const Unique<VkPipelineLayout>	pipelineLayout	(makePipelineLayout					(vk, device, *descriptorSetLayout));
582 	const Unique<VkCommandPool>		cmdPool			(makeCommandPool					(vk, device, queueFamilyIndex));
583 	const Unique<VkCommandBuffer>	cmdBuffer		(allocateCommandBuffer				(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
584 
585 	const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
586 		.setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT,					context.getBinaryCollection().get("vert"), DE_NULL)
587 		.setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,	context.getBinaryCollection().get("tesc"), DE_NULL)
588 		.setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get(tessGeomPointSize ? "tese_point_size" : "tese"), DE_NULL)
589 		.build(vk, device, *pipelineLayout, *renderPass));
590 
591 	// Data that will be verified across all cases
592 	std::vector<float> additionalSegmentLengths;
593 	std::vector<int>   additionalSegmentLocations;
594 
595 	bool success = false;
596 
597 	// Repeat the test for all tessellation coords cases
598 	for (deUint32 tessLevelCaseNdx = 0; tessLevelCaseNdx < tessLevelCases.size(); ++tessLevelCaseNdx)
599 	{
600 		// Upload tessellation levels data to the input buffer
601 		{
602 			const Allocation&	alloc			= tessLevelsBuffer.getAllocation();
603 			float* const		tessLevelOuter1	= static_cast<float*>(alloc.getHostPtr());
604 
605 			*tessLevelOuter1 = tessLevelCases[tessLevelCaseNdx];
606 			flushAlloc(vk, device, alloc);
607 		}
608 
609 		// Clear the results buffer
610 		{
611 			const Allocation& alloc = resultBuffer.getAllocation();
612 
613 			deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
614 			flushAlloc(vk, device, alloc);
615 		}
616 
617 		// Clear the counter buffer
618 		{
619 			const Allocation& alloc = counterBuffer.getAllocation();
620 
621 			deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(counterBufferSizeBytes));
622 			flushAlloc(vk, device, alloc);
623 		}
624 
625 		beginCommandBuffer(vk, *cmdBuffer);
626 
627 		// Begin render pass
628 		beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, makeRect2D(1, 1));
629 
630 		vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
631 		vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
632 
633 		vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
634 		endRenderPass(vk, *cmdBuffer);
635 
636 		{
637 			const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
638 				VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes);
639 
640 			vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
641 				0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
642 		}
643 
644 		endCommandBuffer(vk, *cmdBuffer);
645 		submitCommandsAndWait(vk, device, queue, *cmdBuffer);
646 
647 		// Verify the result.
648 		{
649 			tcu::TestLog&				log					= context.getTestContext().getLog();
650 			const Allocation&			resultAlloc			= resultBuffer.getAllocation();
651 			const Allocation&			counterAlloc		= counterBuffer.getAllocation();
652 
653 			invalidateAlloc(vk, device, resultAlloc);
654 			invalidateAlloc(vk, device, counterAlloc);
655 
656 			const deInt32				numResults			= *static_cast<deInt32*>(counterAlloc.getHostPtr());
657 			const std::vector<float>	resultTessCoords	= readFloatArray(numResults, resultAlloc.getHostPtr());
658 
659 			// Outputs
660 			float						additionalSegmentLength;
661 			int							additionalSegmentLocation;
662 
663 			success = verifyFractionalSpacingSingle(log, testParams.spacingMode, tessLevelCases[tessLevelCaseNdx], resultTessCoords,
664 													&additionalSegmentLength, &additionalSegmentLocation);
665 
666 			if (!success)
667 				break;
668 
669 			additionalSegmentLengths.push_back(additionalSegmentLength);
670 			additionalSegmentLocations.push_back(additionalSegmentLocation);
671 		}
672 	} // for tessLevelCaseNdx
673 
674 	if (success)
675 		success = verifyFractionalSpacingMultiple(context.getTestContext().getLog(), testParams.spacingMode, tessLevelCases, additionalSegmentLengths, additionalSegmentLocations);
676 
677 	return (success ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
678 }
679 
checkSupportTess(Context & context,const TestParams)680 void checkSupportTess(Context& context, const TestParams)
681 {
682 #ifndef CTS_USES_VULKANSC
683 	if (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR* const features = getPortability(context))
684 	{
685 		checkPointMode(*features);
686 		checkIsolines(*features);
687 	}
688 #else
689 	DE_UNREF(context);
690 #endif // CTS_USES_VULKANSC
691 }
692 
693 } // anonymous
694 
695 //! These tests correspond to dEQP-GLES31.functional.tessellation.fractional_spacing.*
696 //! Check validity of fractional spacing modes. Draws a single isoline, reads tess coords with SSBO.
createFractionalSpacingTests(tcu::TestContext & testCtx)697 tcu::TestCaseGroup* createFractionalSpacingTests (tcu::TestContext& testCtx)
698 {
699 	de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "fractional_spacing"));
700 
701 	addFunctionCaseWithPrograms(group.get(), "glsl_odd", checkSupportTess, initPrograms, test, TestParams(SHADER_LANGUAGE_GLSL, SPACINGMODE_FRACTIONAL_ODD));
702 	addFunctionCaseWithPrograms(group.get(), "glsl_even", checkSupportTess, initPrograms, test, TestParams(SHADER_LANGUAGE_GLSL, SPACINGMODE_FRACTIONAL_EVEN));
703 	addFunctionCaseWithPrograms(group.get(), "hlsl_odd", checkSupportTess, initPrograms, test, TestParams(SHADER_LANGUAGE_HLSL, SPACINGMODE_FRACTIONAL_ODD));
704 	addFunctionCaseWithPrograms(group.get(), "hlsl_even", checkSupportTess, initPrograms, test, TestParams(SHADER_LANGUAGE_HLSL, SPACINGMODE_FRACTIONAL_EVEN));
705 
706 	return group.release();
707 }
708 
709 } // tessellation
710 } // vkt
711