Home | History | Annotate | Download | only in tessellation
      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 
     42 namespace vkt
     43 {
     44 namespace tessellation
     45 {
     46 
     47 using namespace vk;
     48 
     49 namespace
     50 {
     51 
     52 template <typename T, typename MembT>
     53 std::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.
     62 template <typename Pred, typename T, typename MembT>
     63 class MemberPred
     64 {
     65 public:
     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 
     69 private:
     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.
     75 template <template <typename> class Pred, typename T, typename MembT>
     76 inline MemberPred<Pred<MembT>, T, MembT> memberPred (MembT T::* membP) { return MemberPred<Pred<MembT>, T, MembT>(membP); }
     77 
     78 struct 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 
     87 inline std::vector<float> lengths (const std::vector<Segment>& segments) { return members(segments, &Segment::length); }
     88 
     89 struct 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 
     98 struct 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  *//*--------------------------------------------------------------------*/
    126 bool 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  *//*--------------------------------------------------------------------*/
    269 static 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 
    346 std::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.
    368 std::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 
    381 void 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 
    509 tcu::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.
    652 tcu::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