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