1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.1 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Tessellation Tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es31fTessellationTests.hpp"
25#include "glsTextureTestUtil.hpp"
26#include "gluShaderProgram.hpp"
27#include "gluRenderContext.hpp"
28#include "gluPixelTransfer.hpp"
29#include "gluDrawUtil.hpp"
30#include "gluObjectWrapper.hpp"
31#include "gluStrUtil.hpp"
32#include "gluContextInfo.hpp"
33#include "gluVarType.hpp"
34#include "gluVarTypeUtil.hpp"
35#include "tcuTestLog.hpp"
36#include "tcuRenderTarget.hpp"
37#include "tcuSurface.hpp"
38#include "tcuTextureUtil.hpp"
39#include "tcuVectorUtil.hpp"
40#include "tcuImageIO.hpp"
41#include "tcuResource.hpp"
42#include "tcuImageCompare.hpp"
43#include "deRandom.hpp"
44#include "deStringUtil.hpp"
45#include "deSharedPtr.hpp"
46#include "deString.h"
47#include "deMath.h"
48
49#include "glwEnums.hpp"
50#include "glwDefs.hpp"
51#include "glwFunctions.hpp"
52
53#include <vector>
54#include <string>
55#include <algorithm>
56#include <functional>
57#include <set>
58#include <limits>
59
60using glu::ShaderProgram;
61using glu::RenderContext;
62using tcu::RenderTarget;
63using tcu::TestLog;
64using tcu::Vec2;
65using tcu::Vec3;
66using tcu::Vec4;
67using de::Random;
68using de::SharedPtr;
69
70using std::vector;
71using std::string;
72
73using namespace glw; // For GL types.
74
75namespace deqp
76{
77
78using gls::TextureTestUtil::RandomViewport;
79
80namespace gles31
81{
82namespace Functional
83{
84
85enum
86{
87	MINIMUM_MAX_TESS_GEN_LEVEL = 64 //!< GL-defined minimum for GL_MAX_TESS_GEN_LEVEL.
88};
89
90static inline bool vec3XLessThan (const Vec3& a, const Vec3& b) { return a.x() < b.x(); }
91
92template <typename IterT>
93static string elemsStr (const IterT& begin, const IterT& end, int wrapLengthParam = 0, int numIndentationSpaces = 0)
94{
95	const string	baseIndentation	= string(numIndentationSpaces, ' ');
96	const string	deepIndentation	= baseIndentation + string(4, ' ');
97	const int		wrapLength		= wrapLengthParam > 0 ? wrapLengthParam : std::numeric_limits<int>::max();
98	const int		length			= (int)std::distance(begin, end);
99	string			result;
100
101	if (length > wrapLength)
102		result += "(amount: " + de::toString(length) + ") ";
103	result += string() + "{" + (length > wrapLength ? "\n"+deepIndentation : " ");
104
105	{
106		int index = 0;
107		for (IterT it = begin; it != end; ++it)
108		{
109			if (it != begin)
110				result += string() + ", " + (index % wrapLength == 0 ? "\n"+deepIndentation : "");
111			result += de::toString(*it);
112			index++;
113		}
114
115		result += length > wrapLength ? "\n"+baseIndentation : " ";
116	}
117
118	result += "}";
119	return result;
120}
121
122template <typename ContainerT>
123static string containerStr (const ContainerT& c, int wrapLengthParam = 0, int numIndentationSpaces = 0)
124{
125	return elemsStr(c.begin(), c.end(), wrapLengthParam, numIndentationSpaces);
126}
127
128template <typename T, int N>
129static string arrayStr (const T (&arr)[N], int wrapLengthParam = 0, int numIndentationSpaces = 0)
130{
131	return elemsStr(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr), wrapLengthParam, numIndentationSpaces);
132}
133
134template <typename T, int N>
135static T arrayMax (const T (&arr)[N])
136{
137	return *std::max_element(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr));
138}
139
140template <typename T, typename MembT>
141static vector<MembT> members (const vector<T>& objs, MembT T::* membP)
142{
143	vector<MembT> result(objs.size());
144	for (int i = 0; i < (int)objs.size(); i++)
145		result[i] = objs[i].*membP;
146	return result;
147}
148
149template <typename T, int N>
150static vector<T> arrayToVector (const T (&arr)[N])
151{
152	return vector<T>(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr));
153}
154
155template <typename ContainerT, typename T>
156static inline bool contains (const ContainerT& c, const T& key)
157{
158	return c.find(key) != c.end();
159}
160
161template <int Size>
162static inline tcu::Vector<bool, Size> singleTrueMask (int index)
163{
164	DE_ASSERT(de::inBounds(index, 0, Size));
165	tcu::Vector<bool, Size> result;
166	result[index] = true;
167	return result;
168}
169
170static int intPow (int base, int exp)
171{
172	DE_ASSERT(exp >= 0);
173	if (exp == 0)
174		return 1;
175	else
176	{
177		const int sub = intPow(base, exp/2);
178		if (exp % 2 == 0)
179			return sub*sub;
180		else
181			return sub*sub*base;
182	}
183}
184
185tcu::Surface getPixels (const glu::RenderContext& rCtx, int x, int y, int width, int height)
186{
187	tcu::Surface result(width, height);
188	glu::readPixels(rCtx, x, y, result.getAccess());
189	return result;
190}
191
192tcu::Surface getPixels (const glu::RenderContext& rCtx, const RandomViewport& vp)
193{
194	return getPixels(rCtx, vp.x, vp.y, vp.width, vp.height);
195}
196
197static inline void checkRenderTargetSize (const RenderTarget& renderTarget, int minSize)
198{
199	if (renderTarget.getWidth() < minSize || renderTarget.getHeight() < minSize)
200		throw tcu::NotSupportedError("Render target width and height must be at least " + de::toString(minSize));
201}
202
203tcu::TextureLevel getPNG (const tcu::Archive& archive, const string& filename)
204{
205	tcu::TextureLevel result;
206	tcu::ImageIO::loadPNG(result, archive, filename.c_str());
207	return result;
208}
209
210static int numBasicSubobjects (const glu::VarType& type)
211{
212	if (type.isBasicType())
213		return 1;
214	else if (type.isArrayType())
215		return type.getArraySize()*numBasicSubobjects(type.getElementType());
216	else if (type.isStructType())
217	{
218		const glu::StructType&	structType	= *type.getStructPtr();
219		int						result		= 0;
220		for (int i = 0; i < structType.getNumMembers(); i++)
221			result += numBasicSubobjects(structType.getMember(i).getType());
222		return result;
223	}
224	else
225	{
226		DE_ASSERT(false);
227		return -1;
228	}
229}
230
231static inline int numVerticesPerPrimitive (deUint32 primitiveTypeGL)
232{
233	switch (primitiveTypeGL)
234	{
235		case GL_POINTS:		return 1;
236		case GL_TRIANGLES:	return 3;
237		case GL_LINES:		return 2;
238		default:
239			DE_ASSERT(false);
240			return -1;
241	}
242}
243
244static inline void setViewport (const glw::Functions& gl, const RandomViewport& vp)
245{
246	gl.viewport(vp.x, vp.y, vp.width, vp.height);
247}
248
249static inline deUint32 getQueryResult (const glw::Functions& gl, deUint32 queryObject)
250{
251	deUint32 result = (deUint32)-1;
252	gl.getQueryObjectuiv(queryObject, GL_QUERY_RESULT, &result);
253	TCU_CHECK(result != (deUint32)-1);
254	return result;
255}
256
257template <typename T>
258static void readDataMapped (const glw::Functions& gl, deUint32 bufferTarget, int numElems, T* dst)
259{
260	const int							numBytes	= numElems*(int)sizeof(T);
261	const T* const						mappedData	= (const T*)gl.mapBufferRange(bufferTarget, 0, numBytes, GL_MAP_READ_BIT);
262	GLU_EXPECT_NO_ERROR(gl.getError(), (string() + "glMapBufferRange(" + glu::getBufferTargetName((int)bufferTarget) + ", 0, " + de::toString(numBytes) + ", GL_MAP_READ_BIT)").c_str());
263	TCU_CHECK(mappedData != DE_NULL);
264
265	for (int i = 0; i < numElems; i++)
266		dst[i] = mappedData[i];
267
268	gl.unmapBuffer(bufferTarget);
269}
270
271template <typename T>
272static vector<T> readDataMapped (const glw::Functions& gl, deUint32 bufferTarget, int numElems)
273{
274	vector<T> result(numElems);
275	readDataMapped(gl, bufferTarget, numElems, &result[0]);
276	return result;
277}
278
279namespace
280{
281
282template <typename ArgT, bool res>
283struct ConstantUnaryPredicate
284{
285	bool operator() (const ArgT&) const { return res; }
286};
287
288//! Helper for handling simple, one-varying transform feedbacks.
289template <typename VaryingT>
290class TransformFeedbackHandler
291{
292public:
293	struct Result
294	{
295		int					numPrimitives;
296		vector<VaryingT>	varying;
297
298		Result (void)								: numPrimitives(-1) {}
299		Result (int n, const vector<VaryingT>& v)	: numPrimitives(n), varying(v) {}
300	};
301
302									TransformFeedbackHandler	(const glu::RenderContext& renderCtx, int maxNumVertices);
303
304	Result							renderAndGetPrimitives		(deUint32 programGL, deUint32 tfPrimTypeGL, int numBindings, const glu::VertexArrayBinding* bindings, int numVertices) const;
305
306private:
307	const glu::RenderContext&		m_renderCtx;
308	const glu::TransformFeedback	m_tf;
309	const glu::Buffer				m_tfBuffer;
310	const glu::Query				m_tfPrimQuery;
311};
312
313template <typename AttribType>
314TransformFeedbackHandler<AttribType>::TransformFeedbackHandler (const glu::RenderContext& renderCtx, int maxNumVertices)
315	: m_renderCtx		(renderCtx)
316	, m_tf				(renderCtx)
317	, m_tfBuffer		(renderCtx)
318	, m_tfPrimQuery		(renderCtx)
319{
320	const glw::Functions&	gl			= m_renderCtx.getFunctions();
321	// \note Room for 1 extra triangle, to detect if GL returns too many primitives.
322	const int				bufferSize	= (maxNumVertices + 3) * (int)sizeof(AttribType);
323
324	gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, *m_tfBuffer);
325	gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bufferSize, DE_NULL, GL_DYNAMIC_READ);
326}
327
328template <typename AttribType>
329typename TransformFeedbackHandler<AttribType>::Result TransformFeedbackHandler<AttribType>::renderAndGetPrimitives (deUint32 programGL, deUint32 tfPrimTypeGL, int numBindings, const glu::VertexArrayBinding* bindings, int numVertices) const
330{
331	DE_ASSERT(tfPrimTypeGL == GL_POINTS || tfPrimTypeGL == GL_LINES || tfPrimTypeGL == GL_TRIANGLES);
332
333	const glw::Functions& gl = m_renderCtx.getFunctions();
334
335	gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, *m_tf);
336	gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, *m_tfBuffer);
337	gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, *m_tfBuffer);
338
339	gl.beginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, *m_tfPrimQuery);
340	gl.beginTransformFeedback(tfPrimTypeGL);
341
342	glu::draw(m_renderCtx, programGL, numBindings, bindings, glu::pr::Patches(numVertices));
343	GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
344
345	gl.endTransformFeedback();
346	gl.endQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
347
348	{
349		const int numPrimsWritten = (int)getQueryResult(gl, *m_tfPrimQuery);
350		return Result(numPrimsWritten, readDataMapped<AttribType>(gl, GL_TRANSFORM_FEEDBACK_BUFFER, numPrimsWritten * numVerticesPerPrimitive(tfPrimTypeGL)));
351	}
352}
353
354template <typename T>
355class SizeLessThan
356{
357public:
358	bool operator() (const T& a, const T& b) const { return a.size() < b.size(); }
359};
360
361//! Predicate functor for comparing structs by their members.
362template <typename Pred, typename T, typename MembT>
363class MemberPred
364{
365public:
366				MemberPred	(MembT T::* membP) : m_membP(membP), m_pred(Pred()) {}
367	bool		operator()	(const T& a, const T& b) const { return m_pred(a.*m_membP, b.*m_membP); }
368
369private:
370	MembT T::*	m_membP;
371	Pred		m_pred;
372};
373
374//! Convenience wrapper for MemberPred, because class template arguments aren't deduced based on constructor arguments.
375template <template <typename> class Pred, typename T, typename MembT>
376static MemberPred<Pred<MembT>, T, MembT> memberPred (MembT T::* membP) { return MemberPred<Pred<MembT>, T, MembT>(membP); }
377
378template <typename SeqT, int Size, typename Pred>
379class LexCompare
380{
381public:
382	LexCompare (void) : m_pred(Pred()) {}
383
384	bool operator() (const SeqT& a, const SeqT& b) const
385	{
386		for (int i = 0; i < Size; i++)
387		{
388			if (m_pred(a[i], b[i]))
389				return true;
390			if (m_pred(b[i], a[i]))
391				return false;
392		}
393		return false;
394	}
395
396private:
397	Pred m_pred;
398};
399
400template <int Size>
401class VecLexLessThan : public LexCompare<tcu::Vector<float, Size>, Size, std::less<float> >
402{
403};
404
405enum TessPrimitiveType
406{
407	TESSPRIMITIVETYPE_TRIANGLES = 0,
408	TESSPRIMITIVETYPE_QUADS,
409	TESSPRIMITIVETYPE_ISOLINES,
410
411	TESSPRIMITIVETYPE_LAST
412};
413
414enum SpacingMode
415{
416	SPACINGMODE_EQUAL,
417	SPACINGMODE_FRACTIONAL_ODD,
418	SPACINGMODE_FRACTIONAL_EVEN,
419
420	SPACINGMODE_LAST
421};
422
423enum Winding
424{
425	WINDING_CCW = 0,
426	WINDING_CW,
427
428	WINDING_LAST
429};
430
431static inline const char* getTessPrimitiveTypeShaderName (TessPrimitiveType type)
432{
433	switch (type)
434	{
435		case TESSPRIMITIVETYPE_TRIANGLES:	return "triangles";
436		case TESSPRIMITIVETYPE_QUADS:		return "quads";
437		case TESSPRIMITIVETYPE_ISOLINES:	return "isolines";
438		default:
439			DE_ASSERT(false);
440			return DE_NULL;
441	}
442}
443
444static inline const char* getSpacingModeShaderName (SpacingMode mode)
445{
446	switch (mode)
447	{
448		case SPACINGMODE_EQUAL:				return "equal_spacing";
449		case SPACINGMODE_FRACTIONAL_ODD:	return "fractional_odd_spacing";
450		case SPACINGMODE_FRACTIONAL_EVEN:	return "fractional_even_spacing";
451		default:
452			DE_ASSERT(false);
453			return DE_NULL;
454	}
455}
456
457static inline const char* getWindingShaderName (Winding winding)
458{
459	switch (winding)
460	{
461		case WINDING_CCW:	return "ccw";
462		case WINDING_CW:	return "cw";
463		default:
464			DE_ASSERT(false);
465			return DE_NULL;
466	}
467}
468
469static inline string getTessellationEvaluationInLayoutString (TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode=false)
470{
471	return string() + "layout (" + getTessPrimitiveTypeShaderName(primType)
472								 + ", " + getSpacingModeShaderName(spacing)
473								 + ", " + getWindingShaderName(winding)
474								 + (usePointMode ? ", point_mode" : "")
475								 + ") in;\n";
476}
477
478static inline string getTessellationEvaluationInLayoutString (TessPrimitiveType primType, SpacingMode spacing, bool usePointMode=false)
479{
480	return string() + "layout (" + getTessPrimitiveTypeShaderName(primType)
481								 + ", " + getSpacingModeShaderName(spacing)
482								 + (usePointMode ? ", point_mode" : "")
483								 + ") in;\n";
484}
485
486static inline string getTessellationEvaluationInLayoutString (TessPrimitiveType primType, Winding winding, bool usePointMode=false)
487{
488	return string() + "layout (" + getTessPrimitiveTypeShaderName(primType)
489								 + ", " + getWindingShaderName(winding)
490								 + (usePointMode ? ", point_mode" : "")
491								 + ") in;\n";
492}
493
494static inline string getTessellationEvaluationInLayoutString (TessPrimitiveType primType, bool usePointMode=false)
495{
496	return string() + "layout (" + getTessPrimitiveTypeShaderName(primType)
497								 + (usePointMode ? ", point_mode" : "")
498								 + ") in;\n";
499}
500
501static inline deUint32 outputPrimitiveTypeGL (TessPrimitiveType tessPrimType, bool usePointMode)
502{
503	if (usePointMode)
504		return GL_POINTS;
505	else
506	{
507		switch (tessPrimType)
508		{
509			case TESSPRIMITIVETYPE_TRIANGLES:	return GL_TRIANGLES;
510			case TESSPRIMITIVETYPE_QUADS:		return GL_TRIANGLES;
511			case TESSPRIMITIVETYPE_ISOLINES:	return GL_LINES;
512			default:
513				DE_ASSERT(false);
514				return (deUint32)-1;
515		}
516	}
517}
518
519static inline int numInnerTessellationLevels (TessPrimitiveType primType)
520{
521	switch (primType)
522	{
523		case TESSPRIMITIVETYPE_TRIANGLES:	return 1;
524		case TESSPRIMITIVETYPE_QUADS:		return 2;
525		case TESSPRIMITIVETYPE_ISOLINES:	return 0;
526		default: DE_ASSERT(false); return -1;
527	}
528}
529
530static inline int numOuterTessellationLevels (TessPrimitiveType primType)
531{
532	switch (primType)
533	{
534		case TESSPRIMITIVETYPE_TRIANGLES:	return 3;
535		case TESSPRIMITIVETYPE_QUADS:		return 4;
536		case TESSPRIMITIVETYPE_ISOLINES:	return 2;
537		default: DE_ASSERT(false); return -1;
538	}
539}
540
541static string tessellationLevelsString (const float* inner, int numInner, const float* outer, int numOuter)
542{
543	DE_ASSERT(numInner >= 0 && numOuter >= 0);
544	return "inner: " + elemsStr(inner, inner+numInner) + ", outer: " + elemsStr(outer, outer+numOuter);
545}
546
547static string tessellationLevelsString (const float* inner, const float* outer, TessPrimitiveType primType)
548{
549	return tessellationLevelsString(inner, numInnerTessellationLevels(primType), outer, numOuterTessellationLevels(primType));
550}
551
552static string tessellationLevelsString (const float* inner, const float* outer)
553{
554	return tessellationLevelsString(inner, 2, outer, 4);
555}
556
557static inline float getClampedTessLevel (SpacingMode mode, float tessLevel)
558{
559	switch (mode)
560	{
561		case SPACINGMODE_EQUAL:				return de::max(1.0f, tessLevel);
562		case SPACINGMODE_FRACTIONAL_ODD:	return de::max(1.0f, tessLevel);
563		case SPACINGMODE_FRACTIONAL_EVEN:	return de::max(2.0f, tessLevel);
564		default:
565			DE_ASSERT(false);
566			return -1.0f;
567	}
568}
569
570static inline int getRoundedTessLevel (SpacingMode mode, float clampedTessLevel)
571{
572	int result = (int)deFloatCeil(clampedTessLevel);
573
574	switch (mode)
575	{
576		case SPACINGMODE_EQUAL:											break;
577		case SPACINGMODE_FRACTIONAL_ODD:	result += 1 - result % 2;	break;
578		case SPACINGMODE_FRACTIONAL_EVEN:	result += result % 2;		break;
579		default:
580			DE_ASSERT(false);
581	}
582	DE_ASSERT(de::inRange<int>(result, 1, MINIMUM_MAX_TESS_GEN_LEVEL));
583
584	return result;
585}
586
587static int getClampedRoundedTessLevel (SpacingMode mode, float tessLevel)
588{
589	return getRoundedTessLevel(mode, getClampedTessLevel(mode, tessLevel));
590}
591
592//! A description of an outer edge of a triangle, quad or isolines.
593//! An outer edge can be described by the index of a u/v/w coordinate
594//! and the coordinate's value along that edge.
595struct OuterEdgeDescription
596{
597	int		constantCoordinateIndex;
598	float	constantCoordinateValueChoices[2];
599	int		numConstantCoordinateValueChoices;
600
601	OuterEdgeDescription (int i, float c0)			: constantCoordinateIndex(i), numConstantCoordinateValueChoices(1) { constantCoordinateValueChoices[0] = c0; }
602	OuterEdgeDescription (int i, float c0, float c1)	: constantCoordinateIndex(i), numConstantCoordinateValueChoices(2) { constantCoordinateValueChoices[0] = c0; constantCoordinateValueChoices[1] = c1; }
603
604	string description (void) const
605	{
606		static const char* const	coordinateNames[] = { "u", "v", "w" };
607		string						result;
608		for (int i = 0; i < numConstantCoordinateValueChoices; i++)
609			result += string() + (i > 0 ? " or " : "") + coordinateNames[constantCoordinateIndex] + "=" + de::toString(constantCoordinateValueChoices[i]);
610		return result;
611	}
612
613	bool contains (const Vec3& v) const
614	{
615		for (int i = 0; i < numConstantCoordinateValueChoices; i++)
616			if (v[constantCoordinateIndex] == constantCoordinateValueChoices[i])
617				return true;
618		return false;
619	}
620};
621
622static vector<OuterEdgeDescription> outerEdgeDescriptions (TessPrimitiveType primType)
623{
624	static const OuterEdgeDescription triangleOuterEdgeDescriptions[3] =
625	{
626		OuterEdgeDescription(0, 0.0f),
627		OuterEdgeDescription(1, 0.0f),
628		OuterEdgeDescription(2, 0.0f)
629	};
630
631	static const OuterEdgeDescription quadOuterEdgeDescriptions[4] =
632	{
633		OuterEdgeDescription(0, 0.0f),
634		OuterEdgeDescription(1, 0.0f),
635		OuterEdgeDescription(0, 1.0f),
636		OuterEdgeDescription(1, 1.0f)
637	};
638
639	static const OuterEdgeDescription isolinesOuterEdgeDescriptions[1] =
640	{
641		OuterEdgeDescription(0, 0.0f, 1.0f),
642	};
643
644	switch (primType)
645	{
646		case TESSPRIMITIVETYPE_TRIANGLES:	return arrayToVector(triangleOuterEdgeDescriptions);
647		case TESSPRIMITIVETYPE_QUADS:		return arrayToVector(quadOuterEdgeDescriptions);
648		case TESSPRIMITIVETYPE_ISOLINES:	return arrayToVector(isolinesOuterEdgeDescriptions);
649		default: DE_ASSERT(false); return vector<OuterEdgeDescription>();
650	}
651}
652
653// \note The tessellation coordinates generated by this function could break some of the rules given in the spec (e.g. it may not exactly hold that u+v+w == 1.0f, or [uvw] + (1.0f-[uvw]) == 1.0f).
654static vector<Vec3> generateReferenceTriangleTessCoords (SpacingMode spacingMode, int inner, int outer0, int outer1, int outer2)
655{
656	vector<Vec3> tessCoords;
657
658	if (inner == 1)
659	{
660		if (outer0 == 1 && outer1 == 1 && outer2 == 1)
661		{
662			tessCoords.push_back(Vec3(1.0f, 0.0f, 0.0f));
663			tessCoords.push_back(Vec3(0.0f, 1.0f, 0.0f));
664			tessCoords.push_back(Vec3(0.0f, 0.0f, 1.0f));
665			return tessCoords;
666		}
667		else
668			return generateReferenceTriangleTessCoords(spacingMode, spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
669																	outer0, outer1, outer2);
670	}
671	else
672	{
673		for (int i = 0; i < outer0; i++) { const float v = (float)i / (float)outer0; tessCoords.push_back(Vec3(	   0.0f,		   v,	1.0f - v)); }
674		for (int i = 0; i < outer1; i++) { const float v = (float)i / (float)outer1; tessCoords.push_back(Vec3(1.0f - v,		0.0f,		   v)); }
675		for (int i = 0; i < outer2; i++) { const float v = (float)i / (float)outer2; tessCoords.push_back(Vec3(		  v,	1.0f - v,		0.0f)); }
676
677		const int numInnerTriangles = inner/2;
678		for (int innerTriangleNdx = 0; innerTriangleNdx < numInnerTriangles; innerTriangleNdx++)
679		{
680			const int curInnerTriangleLevel = inner - 2*(innerTriangleNdx+1);
681
682			if (curInnerTriangleLevel == 0)
683				tessCoords.push_back(Vec3(1.0f/3.0f));
684			else
685			{
686				const float		minUVW		= (float)(2 * (innerTriangleNdx + 1)) / (float)(3 * inner);
687				const float		maxUVW		= 1.0f - 2.0f*minUVW;
688				const Vec3		corners[3]	=
689				{
690					Vec3(maxUVW, minUVW, minUVW),
691					Vec3(minUVW, maxUVW, minUVW),
692					Vec3(minUVW, minUVW, maxUVW)
693				};
694
695				for (int i = 0; i < curInnerTriangleLevel; i++)
696				{
697					const float f = (float)i / (float)curInnerTriangleLevel;
698					for (int j = 0; j < 3; j++)
699						tessCoords.push_back((1.0f - f)*corners[j] + f*corners[(j+1)%3]);
700				}
701			}
702		}
703
704		return tessCoords;
705	}
706}
707
708static int referenceTriangleNonPointModePrimitiveCount (SpacingMode spacingMode, int inner, int outer0, int outer1, int outer2)
709{
710	if (inner == 1)
711	{
712		if (outer0 == 1 && outer1 == 1 && outer2 == 1)
713			return 1;
714		else
715			return referenceTriangleNonPointModePrimitiveCount(spacingMode, spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
716																			outer0, outer1, outer2);
717	}
718	else
719	{
720		int result = outer0 + outer1 + outer2;
721
722		const int numInnerTriangles = inner/2;
723		for (int innerTriangleNdx = 0; innerTriangleNdx < numInnerTriangles; innerTriangleNdx++)
724		{
725			const int curInnerTriangleLevel = inner - 2*(innerTriangleNdx+1);
726
727			if (curInnerTriangleLevel == 1)
728				result += 4;
729			else
730				result += 2*3*curInnerTriangleLevel;
731		}
732
733		return result;
734	}
735}
736
737// \note The tessellation coordinates generated by this function could break some of the rules given in the spec (e.g. it may not exactly hold that [uv] + (1.0f-[uv]) == 1.0f).
738static vector<Vec3> generateReferenceQuadTessCoords (SpacingMode spacingMode, int inner0, int inner1, int outer0, int outer1, int outer2, int outer3)
739{
740	vector<Vec3> tessCoords;
741
742	if (inner0 == 1 || inner1 == 1)
743	{
744		if (inner0 == 1 && inner1 == 1 && outer0 == 1 && outer1 == 1 && outer2 == 1 && outer3 == 1)
745		{
746			tessCoords.push_back(Vec3(0.0f, 0.0f, 0.0f));
747			tessCoords.push_back(Vec3(1.0f, 0.0f, 0.0f));
748			tessCoords.push_back(Vec3(0.0f, 1.0f, 0.0f));
749			tessCoords.push_back(Vec3(1.0f, 1.0f, 0.0f));
750			return tessCoords;
751		}
752		else
753			return generateReferenceQuadTessCoords(spacingMode, inner0 > 1 ? inner0 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
754																inner1 > 1 ? inner1 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
755																outer0, outer1, outer2, outer3);
756	}
757	else
758	{
759		for (int i = 0; i < outer0; i++) { const float v = (float)i / (float)outer0; tessCoords.push_back(Vec3(0.0f,	v,			0.0f)); }
760		for (int i = 0; i < outer1; i++) { const float v = (float)i / (float)outer1; tessCoords.push_back(Vec3(1.0f-v,	0.0f,		0.0f)); }
761		for (int i = 0; i < outer2; i++) { const float v = (float)i / (float)outer2; tessCoords.push_back(Vec3(1.0f,	1.0f-v,		0.0f)); }
762		for (int i = 0; i < outer3; i++) { const float v = (float)i / (float)outer3; tessCoords.push_back(Vec3(v,		1.0f,		0.0f)); }
763
764		for (int innerVtxY = 0; innerVtxY < inner1-1; innerVtxY++)
765		for (int innerVtxX = 0; innerVtxX < inner0-1; innerVtxX++)
766			tessCoords.push_back(Vec3((float)(innerVtxX + 1) / (float)inner0,
767									  (float)(innerVtxY + 1) / (float)inner1,
768									  0.0f));
769
770		return tessCoords;
771	}
772}
773
774static int referenceQuadNonPointModePrimitiveCount (SpacingMode spacingMode, int inner0, int inner1, int outer0, int outer1, int outer2, int outer3)
775{
776	vector<Vec3> tessCoords;
777
778	if (inner0 == 1 || inner1 == 1)
779	{
780		if (inner0 == 1 && inner1 == 1 && outer0 == 1 && outer1 == 1 && outer2 == 1 && outer3 == 1)
781			return 2;
782		else
783			return referenceQuadNonPointModePrimitiveCount(spacingMode, inner0 > 1 ? inner0 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
784																		inner1 > 1 ? inner1 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
785																		outer0, outer1, outer2, outer3);
786	}
787	else
788		return 2*(inner0-2)*(inner1-2) + 2*(inner0-2) + 2*(inner1-2) + outer0+outer1+outer2+outer3;
789}
790
791// \note The tessellation coordinates generated by this function could break some of the rules given in the spec (e.g. it may not exactly hold that [uv] + (1.0f-[uv]) == 1.0f).
792static vector<Vec3> generateReferenceIsolineTessCoords (int outer0, int outer1)
793{
794	vector<Vec3> tessCoords;
795
796	for (int y = 0; y < outer0;		y++)
797	for (int x = 0; x < outer1+1;	x++)
798		tessCoords.push_back(Vec3((float)x / (float)outer1,
799												  (float)y / (float)outer0,
800												  0.0f));
801
802	return tessCoords;
803}
804
805static int referenceIsolineNonPointModePrimitiveCount (int outer0, int outer1)
806{
807	return outer0*outer1;
808}
809
810static void getClampedRoundedTriangleTessLevels (SpacingMode spacingMode, const float* innerSrc, const float* outerSrc, int* innerDst, int *outerDst)
811{
812	innerDst[0] = getClampedRoundedTessLevel(spacingMode, innerSrc[0]);
813	for (int i = 0; i < 3; i++)
814		outerDst[i] = getClampedRoundedTessLevel(spacingMode, outerSrc[i]);
815}
816
817static void getClampedRoundedQuadTessLevels (SpacingMode spacingMode, const float* innerSrc, const float* outerSrc, int* innerDst, int *outerDst)
818{
819	for (int i = 0; i < 2; i++)
820		innerDst[i] = getClampedRoundedTessLevel(spacingMode, innerSrc[i]);
821	for (int i = 0; i < 4; i++)
822		outerDst[i] = getClampedRoundedTessLevel(spacingMode, outerSrc[i]);
823}
824
825static void getClampedRoundedIsolineTessLevels (SpacingMode spacingMode, const float* outerSrc, int* outerDst)
826{
827	outerDst[0] = getClampedRoundedTessLevel(SPACINGMODE_EQUAL,	outerSrc[0]);
828	outerDst[1] = getClampedRoundedTessLevel(spacingMode,		outerSrc[1]);
829}
830
831static inline bool isPatchDiscarded (TessPrimitiveType primitiveType, const float* outerLevels)
832{
833	const int numOuterLevels = numOuterTessellationLevels(primitiveType);
834	for (int i = 0; i < numOuterLevels; i++)
835		if (outerLevels[i] <= 0.0f)
836			return true;
837	return false;
838}
839
840static vector<Vec3> generateReferenceTessCoords (TessPrimitiveType primitiveType, SpacingMode spacingMode, const float* innerLevels, const float* outerLevels)
841{
842	if (isPatchDiscarded(primitiveType, outerLevels))
843		return vector<Vec3>();
844
845	switch (primitiveType)
846	{
847		case TESSPRIMITIVETYPE_TRIANGLES:
848		{
849			int inner;
850			int outer[3];
851			getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
852
853			if (spacingMode != SPACINGMODE_EQUAL)
854			{
855				// \note For fractional spacing modes, exact results are implementation-defined except in special cases.
856				DE_ASSERT(de::abs(innerLevels[0] - (float)inner) < 0.001f);
857				for (int i = 0; i < 3; i++)
858					DE_ASSERT(de::abs(outerLevels[i] - (float)outer[i]) < 0.001f);
859				DE_ASSERT(inner > 1 || (outer[0] == 1 && outer[1] == 1 && outer[2] == 1));
860			}
861
862			return generateReferenceTriangleTessCoords(spacingMode, inner, outer[0], outer[1], outer[2]);
863		}
864
865		case TESSPRIMITIVETYPE_QUADS:
866		{
867			int inner[2];
868			int outer[4];
869			getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
870
871			if (spacingMode != SPACINGMODE_EQUAL)
872			{
873				// \note For fractional spacing modes, exact results are implementation-defined except in special cases.
874				for (int i = 0; i < 2; i++)
875					DE_ASSERT(de::abs(innerLevels[i] - (float)inner[i]) < 0.001f);
876				for (int i = 0; i < 4; i++)
877					DE_ASSERT(de::abs(outerLevels[i] - (float)outer[i]) < 0.001f);
878
879				DE_ASSERT((inner[0] > 1 && inner[1] > 1) || (inner[0] == 1 && inner[1] == 1 && outer[0] == 1 && outer[1] == 1 && outer[2] == 1 && outer[3] == 1));
880			}
881
882			return generateReferenceQuadTessCoords(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]);
883		}
884
885		case TESSPRIMITIVETYPE_ISOLINES:
886		{
887			int outer[2];
888			getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
889
890			if (spacingMode != SPACINGMODE_EQUAL)
891			{
892				// \note For fractional spacing modes, exact results are implementation-defined except in special cases.
893				DE_ASSERT(de::abs(outerLevels[1] - (float)outer[1]) < 0.001f);
894			}
895
896			return generateReferenceIsolineTessCoords(outer[0], outer[1]);
897		}
898
899		default:
900			DE_ASSERT(false);
901			return vector<Vec3>();
902	}
903}
904
905static int referencePointModePrimitiveCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, const float* innerLevels, const float* outerLevels)
906{
907	if (isPatchDiscarded(primitiveType, outerLevels))
908		return 0;
909
910	switch (primitiveType)
911	{
912		case TESSPRIMITIVETYPE_TRIANGLES:
913		{
914			int inner;
915			int outer[3];
916			getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
917			return (int)generateReferenceTriangleTessCoords(spacingMode, inner, outer[0], outer[1], outer[2]).size();
918		}
919
920		case TESSPRIMITIVETYPE_QUADS:
921		{
922			int inner[2];
923			int outer[4];
924			getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
925			return (int)generateReferenceQuadTessCoords(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]).size();
926		}
927
928		case TESSPRIMITIVETYPE_ISOLINES:
929		{
930			int outer[2];
931			getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
932			return (int)generateReferenceIsolineTessCoords(outer[0], outer[1]).size();
933		}
934
935		default:
936			DE_ASSERT(false);
937			return -1;
938	}
939}
940
941static int referenceNonPointModePrimitiveCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, const float* innerLevels, const float* outerLevels)
942{
943	if (isPatchDiscarded(primitiveType, outerLevels))
944		return 0;
945
946	switch (primitiveType)
947	{
948		case TESSPRIMITIVETYPE_TRIANGLES:
949		{
950			int inner;
951			int outer[3];
952			getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
953			return referenceTriangleNonPointModePrimitiveCount(spacingMode, inner, outer[0], outer[1], outer[2]);
954		}
955
956		case TESSPRIMITIVETYPE_QUADS:
957		{
958			int inner[2];
959			int outer[4];
960			getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
961			return referenceQuadNonPointModePrimitiveCount(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]);
962		}
963
964		case TESSPRIMITIVETYPE_ISOLINES:
965		{
966			int outer[2];
967			getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
968			return referenceIsolineNonPointModePrimitiveCount(outer[0], outer[1]);
969		}
970
971		default:
972			DE_ASSERT(false);
973			return -1;
974	}
975}
976
977static int referencePrimitiveCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, bool usePointMode, const float* innerLevels, const float* outerLevels)
978{
979	return usePointMode ? referencePointModePrimitiveCount		(primitiveType, spacingMode, innerLevels, outerLevels)
980						: referenceNonPointModePrimitiveCount	(primitiveType, spacingMode, innerLevels, outerLevels);
981}
982
983static int referenceVertexCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, bool usePointMode, const float* innerLevels, const float* outerLevels)
984{
985	return referencePrimitiveCount(primitiveType, spacingMode, usePointMode, innerLevels, outerLevels)
986		   * numVerticesPerPrimitive(outputPrimitiveTypeGL(primitiveType, usePointMode));
987}
988
989//! Helper for calling referenceVertexCount multiple times with different tessellation levels.
990//! \note Levels contains inner and outer levels, per patch, in order IIOOOO. The full 6 levels must always be present, irrespective of primitiveType.
991static int multiplePatchReferenceVertexCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, bool usePointMode, const float* levels, int numPatches)
992{
993	int result = 0;
994	for (int patchNdx = 0; patchNdx < numPatches; patchNdx++)
995		result += referenceVertexCount(primitiveType, spacingMode, usePointMode, &levels[6*patchNdx + 0], &levels[6*patchNdx + 2]);
996	return result;
997}
998
999vector<float> generateRandomPatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel, de::Random& rnd)
1000{
1001	vector<float> tessLevels(numPatches*6);
1002
1003	for (int patchNdx = 0; patchNdx < numPatches; patchNdx++)
1004	{
1005		float* const inner = &tessLevels[patchNdx*6 + 0];
1006		float* const outer = &tessLevels[patchNdx*6 + 2];
1007
1008		for (int j = 0; j < 2; j++)
1009			inner[j] = rnd.getFloat(1.0f, 62.0f);
1010		for (int j = 0; j < 4; j++)
1011			outer[j] = j == constantOuterLevelIndex ? constantOuterLevel : rnd.getFloat(1.0f, 62.0f);
1012	}
1013
1014	return tessLevels;
1015}
1016
1017static inline void drawPoint (tcu::Surface& dst, int centerX, int centerY, const tcu::RGBA& color, int size)
1018{
1019	const int width		= dst.getWidth();
1020	const int height	= dst.getHeight();
1021	DE_ASSERT(de::inBounds(centerX, 0, width) && de::inBounds(centerY, 0, height));
1022	DE_ASSERT(size > 0);
1023
1024	for (int yOff = -((size-1)/2); yOff <= size/2; yOff++)
1025	for (int xOff = -((size-1)/2); xOff <= size/2; xOff++)
1026	{
1027		const int pixX = centerX + xOff;
1028		const int pixY = centerY + yOff;
1029		if (de::inBounds(pixX, 0, width) && de::inBounds(pixY, 0, height))
1030			dst.setPixel(pixX, pixY, color);
1031	}
1032}
1033
1034static void drawTessCoordPoint (tcu::Surface& dst, TessPrimitiveType primitiveType, const Vec3& pt, const tcu::RGBA& color, int size)
1035{
1036	// \note These coordinates should match the description in the log message in TessCoordCase::iterate.
1037
1038	static const Vec2 triangleCorners[3] =
1039	{
1040		Vec2(0.95f, 0.95f),
1041		Vec2(0.5f,  0.95f - 0.9f*deFloatSqrt(3.0f/4.0f)),
1042		Vec2(0.05f, 0.95f)
1043	};
1044
1045	static const float quadIsolineLDRU[4] =
1046	{
1047		0.1f, 0.9f, 0.9f, 0.1f
1048	};
1049
1050	const Vec2 dstPos = primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? pt.x()*triangleCorners[0]
1051																	 + pt.y()*triangleCorners[1]
1052																	 + pt.z()*triangleCorners[2]
1053
1054					  : primitiveType == TESSPRIMITIVETYPE_QUADS ||
1055						primitiveType == TESSPRIMITIVETYPE_ISOLINES ? Vec2((1.0f - pt.x())*quadIsolineLDRU[0] + pt.x()*quadIsolineLDRU[2],
1056																		   (1.0f - pt.y())*quadIsolineLDRU[1] + pt.y()*quadIsolineLDRU[3])
1057
1058					  : Vec2(-1.0f);
1059
1060	drawPoint(dst, (int)(dstPos.x()*dst.getWidth()), (int)(dstPos.y()*dst.getHeight()), color, size);
1061}
1062
1063static void drawTessCoordVisualization (tcu::Surface& dst, TessPrimitiveType primitiveType, const vector<Vec3>& coords)
1064{
1065	const int		imageWidth		= 256;
1066	const int		imageHeight		= 256;
1067	const Vec2		imageSizeFloat	((float)imageWidth, (float)imageHeight);
1068	dst.setSize(imageWidth, imageHeight);
1069
1070	tcu::clear(dst.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
1071
1072	for (int i = 0; i < (int)coords.size(); i++)
1073		drawTessCoordPoint(dst, primitiveType, coords[i], tcu::RGBA::white, 2);
1074}
1075
1076static int binarySearchFirstVec3WithXAtLeast (const vector<Vec3>& sorted, float x)
1077{
1078	const Vec3 ref(x, 0.0f, 0.0f);
1079	const vector<Vec3>::const_iterator first = std::lower_bound(sorted.begin(), sorted.end(), ref, vec3XLessThan);
1080	if (first == sorted.end())
1081		return -1;
1082	return (int)std::distance(sorted.begin(), first);
1083}
1084
1085template <typename T, typename P>
1086static vector<T> sorted (const vector<T>& unsorted, P pred)
1087{
1088	vector<T> result = unsorted;
1089	std::sort(result.begin(), result.end(), pred);
1090	return result;
1091}
1092
1093template <typename T>
1094static vector<T> sorted (const vector<T>& unsorted)
1095{
1096	vector<T> result = unsorted;
1097	std::sort(result.begin(), result.end());
1098	return result;
1099}
1100
1101// Check that all points in subset are (approximately) present also in superset.
1102static bool oneWayComparePointSets (TestLog&				log,
1103									tcu::Surface&			errorDst,
1104									TessPrimitiveType		primitiveType,
1105									const vector<Vec3>&		subset,
1106									const vector<Vec3>&		superset,
1107									const char*				subsetName,
1108									const char*				supersetName,
1109									const tcu::RGBA&		errorColor)
1110{
1111	const vector<Vec3>	supersetSorted			= sorted(superset, vec3XLessThan);
1112	const float			epsilon					= 0.01f;
1113	const int			maxNumFailurePrints		= 5;
1114	int					numFailuresDetected		= 0;
1115
1116	for (int subNdx = 0; subNdx < (int)subset.size(); subNdx++)
1117	{
1118		const Vec3& subPt = subset[subNdx];
1119
1120		bool matchFound = false;
1121
1122		{
1123			// Binary search the index of the first point in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range.
1124			const Vec3	matchMin			= subPt - epsilon;
1125			const Vec3	matchMax			= subPt + epsilon;
1126			int			firstCandidateNdx	= binarySearchFirstVec3WithXAtLeast(supersetSorted, matchMin.x());
1127
1128			if (firstCandidateNdx >= 0)
1129			{
1130				// Compare subPt to all points in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range.
1131				for (int superNdx = firstCandidateNdx; superNdx < (int)supersetSorted.size() && supersetSorted[superNdx].x() <= matchMax.x(); superNdx++)
1132				{
1133					const Vec3& superPt = supersetSorted[superNdx];
1134
1135					if (tcu::boolAll(tcu::greaterThanEqual	(superPt, matchMin)) &&
1136						tcu::boolAll(tcu::lessThanEqual		(superPt, matchMax)))
1137					{
1138						matchFound = true;
1139						break;
1140					}
1141				}
1142			}
1143		}
1144
1145		if (!matchFound)
1146		{
1147			numFailuresDetected++;
1148			if (numFailuresDetected < maxNumFailurePrints)
1149				log << TestLog::Message << "Failure: no matching " << supersetName << " point found for " << subsetName << " point " << subPt << TestLog::EndMessage;
1150			else if (numFailuresDetected == maxNumFailurePrints)
1151				log << TestLog::Message << "Note: More errors follow" << TestLog::EndMessage;
1152
1153			drawTessCoordPoint(errorDst, primitiveType, subPt, errorColor, 4);
1154		}
1155	}
1156
1157	return numFailuresDetected == 0;
1158}
1159
1160static bool compareTessCoords (TestLog& log, TessPrimitiveType primitiveType, const vector<Vec3>& refCoords, const vector<Vec3>& resCoords)
1161{
1162	tcu::Surface	refVisual;
1163	tcu::Surface	resVisual;
1164	bool			success = true;
1165
1166	drawTessCoordVisualization(refVisual, primitiveType, refCoords);
1167	drawTessCoordVisualization(resVisual, primitiveType, resCoords);
1168
1169	// Check that all points in reference also exist in result.
1170	success = oneWayComparePointSets(log, refVisual, primitiveType, refCoords, resCoords, "reference", "result", tcu::RGBA::blue) && success;
1171	// Check that all points in result also exist in reference.
1172	success = oneWayComparePointSets(log, resVisual, primitiveType, resCoords, refCoords, "result", "reference", tcu::RGBA::red) && success;
1173
1174	if (!success)
1175	{
1176		log << TestLog::Message << "Note: in the following reference visualization, points that are missing in result point set are blue (if any)" << TestLog::EndMessage
1177			<< TestLog::Image("RefTessCoordVisualization", "Reference tessCoord visualization", refVisual)
1178			<< TestLog::Message << "Note: in the following result visualization, points that are missing in reference point set are red (if any)" << TestLog::EndMessage;
1179	}
1180
1181	log << TestLog::Image("ResTessCoordVisualization", "Result tessCoord visualization", resVisual);
1182
1183	return success;
1184}
1185
1186namespace VerifyFractionalSpacingSingleInternal
1187{
1188
1189struct Segment
1190{
1191	int		index; //!< Index of left coordinate in sortedXCoords.
1192	float	length;
1193	Segment (void)						: index(-1),		length(-1.0f)	{}
1194	Segment (int index_, float length_)	: index(index_),	length(length_)	{}
1195
1196	static vector<float> lengths (const vector<Segment>& segments) { return members(segments, &Segment::length); }
1197};
1198
1199}
1200
1201/*--------------------------------------------------------------------*//*!
1202 * \brief Verify fractional spacing conditions for a single line
1203 *
1204 * Verify that the splitting of an edge (resulting from e.g. an isoline
1205 * with outer levels { 1.0, tessLevel }) with a given fractional spacing
1206 * mode fulfills certain conditions given in the spec.
1207 *
1208 * Note that some conditions can't be checked from just one line
1209 * (specifically, that the additional segment decreases monotonically
1210 * length and the requirement that the additional segments be placed
1211 * identically for identical values of clamped level).
1212 *
1213 * Therefore, the function stores some values to additionalSegmentLengthDst
1214 * and additionalSegmentLocationDst that can later be given to
1215 * verifyFractionalSpacingMultiple(). A negative value in length means that
1216 * no additional segments are present, i.e. there's just one segment.
1217 * A negative value in location means that the value wasn't determinable,
1218 * i.e. all segments had same length.
1219 * The values are not stored if false is returned.
1220 *//*--------------------------------------------------------------------*/
1221static bool verifyFractionalSpacingSingle (TestLog& log, SpacingMode spacingMode, float tessLevel, const vector<float>& coords, float& additionalSegmentLengthDst, int& additionalSegmentLocationDst)
1222{
1223	using namespace VerifyFractionalSpacingSingleInternal;
1224
1225	DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN);
1226
1227	const float				clampedLevel	= getClampedTessLevel(spacingMode, tessLevel);
1228	const int				finalLevel		= getRoundedTessLevel(spacingMode, clampedLevel);
1229	const vector<float>		sortedCoords	= sorted(coords);
1230	string					failNote		= "Note: tessellation level is " + de::toString(tessLevel) + "\nNote: sorted coordinates are:\n    " + containerStr(sortedCoords);
1231
1232	if ((int)coords.size() != finalLevel + 1)
1233	{
1234		log << TestLog::Message << "Failure: number of vertices is " << coords.size() << "; expected " << finalLevel + 1
1235			<< " (clamped tessellation level is " << clampedLevel << ")"
1236			<< "; final level (clamped level rounded up to " << (spacingMode == SPACINGMODE_FRACTIONAL_EVEN ? "even" : "odd") << ") is " << finalLevel
1237			<< " and should equal the number of segments, i.e. number of vertices minus 1" << TestLog::EndMessage
1238			<< TestLog::Message << failNote << TestLog::EndMessage;
1239		return false;
1240	}
1241
1242	if (sortedCoords[0] != 0.0f || sortedCoords.back() != 1.0f)
1243	{
1244		log << TestLog::Message << "Failure: smallest coordinate should be 0.0 and biggest should be 1.0" << TestLog::EndMessage
1245			<< TestLog::Message << failNote << TestLog::EndMessage;
1246		return false;
1247	}
1248
1249	{
1250		vector<Segment> segments(finalLevel);
1251		for (int i = 0; i < finalLevel; i++)
1252			segments[i] = Segment(i, sortedCoords[i+1] - sortedCoords[i]);
1253
1254		failNote += "\nNote: segment lengths are, from left to right:\n    " + containerStr(Segment::lengths(segments));
1255
1256		{
1257			// Divide segments to two different groups based on length.
1258
1259			vector<Segment> segmentsA;
1260			vector<Segment> segmentsB;
1261			segmentsA.push_back(segments[0]);
1262
1263			for (int segNdx = 1; segNdx < (int)segments.size(); segNdx++)
1264			{
1265				const float		epsilon		= 0.001f;
1266				const Segment&	seg			= segments[segNdx];
1267
1268				if (de::abs(seg.length - segmentsA[0].length) < epsilon)
1269					segmentsA.push_back(seg);
1270				else if (segmentsB.empty() || de::abs(seg.length - segmentsB[0].length) < epsilon)
1271					segmentsB.push_back(seg);
1272				else
1273				{
1274					log << TestLog::Message << "Failure: couldn't divide segments to 2 groups by length; "
1275											<< "e.g. segment of length " << seg.length << " isn't approximately equal to either "
1276											<< segmentsA[0].length << " or " << segmentsB[0].length << TestLog::EndMessage
1277											<< TestLog::Message << failNote << TestLog::EndMessage;
1278					return false;
1279				}
1280			}
1281
1282			if (clampedLevel == (float)finalLevel)
1283			{
1284				// All segments should be of equal length.
1285				if (!segmentsA.empty() && !segmentsB.empty())
1286				{
1287					log << TestLog::Message << "Failure: clamped and final tessellation level are equal, but not all segments are of equal length." << TestLog::EndMessage
1288						<< TestLog::Message << failNote << TestLog::EndMessage;
1289					return false;
1290				}
1291			}
1292
1293			if (segmentsA.empty() || segmentsB.empty()) // All segments have same length. This is ok.
1294			{
1295				additionalSegmentLengthDst		= segments.size() == 1 ? -1.0f : segments[0].length;
1296				additionalSegmentLocationDst	= -1;
1297				return true;
1298			}
1299
1300			if (segmentsA.size() != 2 && segmentsB.size() != 2)
1301			{
1302				log << 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" << TestLog::EndMessage
1303					<< TestLog::Message << failNote << TestLog::EndMessage;
1304				return false;
1305			}
1306
1307			// For convenience, arrange so that the 2-segment group is segmentsB.
1308			if (segmentsB.size() != 2)
1309				std::swap(segmentsA, segmentsB);
1310
1311			// \note For 4-segment lines both segmentsA and segmentsB have 2 segments each.
1312			//		 Thus, we can't be sure which ones were meant as the additional segments.
1313			//		 We give the benefit of the doubt by assuming that they're the shorter
1314			//		 ones (as they should).
1315
1316			if (segmentsA.size() != 2)
1317			{
1318				if (segmentsB[0].length > segmentsA[0].length + 0.001f)
1319				{
1320					log << TestLog::Message << "Failure: the two additional segments are longer than the other segments" << TestLog::EndMessage
1321						<< TestLog::Message << failNote << TestLog::EndMessage;
1322					return false;
1323				}
1324			}
1325
1326			// Check that the additional segments are placed symmetrically.
1327			if (segmentsB[0].index + segmentsB[1].index + 1 != (int)segments.size())
1328			{
1329				log << TestLog::Message << "Failure: the two additional segments aren't placed symmetrically; "
1330										<< "one is at index " << segmentsB[0].index << " and other is at index " << segmentsB[1].index
1331										<< " (note: the two indexes should sum to " << (int)segments.size()-1 << ", i.e. numberOfSegments-1)" << TestLog::EndMessage
1332					<< TestLog::Message << failNote << TestLog::EndMessage;
1333				return false;
1334			}
1335
1336			additionalSegmentLengthDst = segmentsB[0].length;
1337			if (segmentsA.size() != 2)
1338				additionalSegmentLocationDst = de::min(segmentsB[0].index, segmentsB[1].index);
1339			else
1340				additionalSegmentLocationDst = segmentsB[0].length < segmentsA[0].length - 0.001f ? de::min(segmentsB[0].index, segmentsB[1].index)
1341											 : segmentsA[0].length < segmentsB[0].length - 0.001f ? de::min(segmentsA[0].index, segmentsA[1].index)
1342											 : -1; // \note -1 when can't reliably decide which ones are the additional segments, a or b.
1343			return true;
1344		}
1345	}
1346}
1347
1348namespace VerifyFractionalSpacingMultipleInternal
1349{
1350
1351struct LineData
1352{
1353	float	tessLevel;
1354	float	additionalSegmentLength;
1355	int		additionalSegmentLocation;
1356	LineData (float lev, float len, int loc) : tessLevel(lev), additionalSegmentLength(len), additionalSegmentLocation(loc) {}
1357};
1358
1359}
1360
1361/*--------------------------------------------------------------------*//*!
1362 * \brief Verify fractional spacing conditions between multiple lines
1363 *
1364 * Verify the fractional spacing conditions that are not checked in
1365 * verifyFractionalSpacingSingle(). Uses values given by said function
1366 * as parameters, in addition to the spacing mode and tessellation level.
1367 *//*--------------------------------------------------------------------*/
1368static bool verifyFractionalSpacingMultiple (TestLog& log, SpacingMode spacingMode, const vector<float>& tessLevels, const vector<float>& additionalSegmentLengths, const vector<int>& additionalSegmentLocations)
1369{
1370	using namespace VerifyFractionalSpacingMultipleInternal;
1371
1372	DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN);
1373	DE_ASSERT(tessLevels.size() == additionalSegmentLengths.size() &&
1374			  tessLevels.size() == additionalSegmentLocations.size());
1375
1376	vector<LineData> lineDatas;
1377
1378	for (int i = 0; i < (int)tessLevels.size(); i++)
1379		lineDatas.push_back(LineData(tessLevels[i], additionalSegmentLengths[i], additionalSegmentLocations[i]));
1380
1381	{
1382		const vector<LineData> lineDatasSortedByLevel = sorted(lineDatas, memberPred<std::less>(&LineData::tessLevel));
1383
1384		// Check that lines with identical clamped tessellation levels have identical additionalSegmentLocation.
1385
1386		for (int lineNdx = 1; lineNdx < (int)lineDatasSortedByLevel.size(); lineNdx++)
1387		{
1388			const LineData& curData		= lineDatasSortedByLevel[lineNdx];
1389			const LineData& prevData	= lineDatasSortedByLevel[lineNdx-1];
1390
1391			if (curData.additionalSegmentLocation < 0 || prevData.additionalSegmentLocation < 0)
1392				continue; // Unknown locations, skip.
1393
1394			if (getClampedTessLevel(spacingMode, curData.tessLevel) == getClampedTessLevel(spacingMode, prevData.tessLevel) &&
1395				curData.additionalSegmentLocation != prevData.additionalSegmentLocation)
1396			{
1397				log << TestLog::Message << "Failure: additional segments not located identically for two edges with identical clamped tessellation levels" << TestLog::EndMessage
1398					<< TestLog::Message << "Note: tessellation levels are " << curData.tessLevel << " and " << prevData.tessLevel
1399										<< " (clamped level " << getClampedTessLevel(spacingMode, curData.tessLevel) << ")"
1400										<< "; but first additional segments located at indices "
1401										<< curData.additionalSegmentLocation << " and " << prevData.additionalSegmentLocation << ", respectively" << TestLog::EndMessage;
1402				return false;
1403			}
1404		}
1405
1406		// Check that, among lines with same clamped rounded tessellation level, additionalSegmentLength is monotonically decreasing with "clampedRoundedTessLevel - clampedTessLevel" (the "fraction").
1407
1408		for (int lineNdx = 1; lineNdx < (int)lineDatasSortedByLevel.size(); lineNdx++)
1409		{
1410			const LineData&		curData				= lineDatasSortedByLevel[lineNdx];
1411			const LineData&		prevData			= lineDatasSortedByLevel[lineNdx-1];
1412
1413			if (curData.additionalSegmentLength < 0.0f || prevData.additionalSegmentLength < 0.0f)
1414				continue; // Unknown segment lengths, skip.
1415
1416			const float			curClampedLevel		= getClampedTessLevel(spacingMode, curData.tessLevel);
1417			const float			prevClampedLevel	= getClampedTessLevel(spacingMode, prevData.tessLevel);
1418			const int			curFinalLevel		= getRoundedTessLevel(spacingMode, curClampedLevel);
1419			const int			prevFinalLevel		= getRoundedTessLevel(spacingMode, prevClampedLevel);
1420
1421			if (curFinalLevel != prevFinalLevel)
1422				continue;
1423
1424			const float			curFraction		= curFinalLevel - curClampedLevel;
1425			const float			prevFraction	= prevFinalLevel - prevClampedLevel;
1426
1427			if (curData.additionalSegmentLength < prevData.additionalSegmentLength ||
1428				(curClampedLevel == prevClampedLevel && curData.additionalSegmentLength != prevData.additionalSegmentLength))
1429			{
1430				log << TestLog::Message << "Failure: additional segment length isn't monotonically decreasing with the fraction <n> - <f>, among edges with same final tessellation level" << TestLog::EndMessage
1431					<< TestLog::Message << "Note: <f> stands for the clamped tessellation level and <n> for the final (rounded and clamped) tessellation level" << TestLog::EndMessage
1432					<< TestLog::Message << "Note: two edges have tessellation levels " << prevData.tessLevel << " and " << curData.tessLevel << " respectively"
1433										<< ", clamped " << prevClampedLevel << " and " << curClampedLevel << ", final " << prevFinalLevel << " and " << curFinalLevel
1434										<< "; fractions are " << prevFraction << " and " << curFraction
1435										<< ", but resulted in segment lengths " << prevData.additionalSegmentLength << " and " << curData.additionalSegmentLength << TestLog::EndMessage;
1436				return false;
1437			}
1438		}
1439	}
1440
1441	return true;
1442}
1443
1444//! Compare triangle sets, ignoring triangle order and vertex order within triangle, and possibly exclude some triangles too.
1445template <typename IsTriangleRelevantT>
1446static bool compareTriangleSets (const vector<Vec3>&			coordsA,
1447								 const vector<Vec3>&			coordsB,
1448								 TestLog&						log,
1449								 const IsTriangleRelevantT&		isTriangleRelevant,
1450								 const char*					ignoredTriangleDescription = DE_NULL)
1451{
1452	typedef tcu::Vector<Vec3, 3>							Triangle;
1453	typedef LexCompare<Triangle, 3, VecLexLessThan<3> >		TriangleLexLessThan;
1454	typedef std::set<Triangle, TriangleLexLessThan>			TriangleSet;
1455
1456	DE_ASSERT(coordsA.size() % 3 == 0 && coordsB.size() % 3 == 0);
1457
1458	const int		numTrianglesA = (int)coordsA.size()/3;
1459	const int		numTrianglesB = (int)coordsB.size()/3;
1460	TriangleSet		trianglesA;
1461	TriangleSet		trianglesB;
1462
1463	for (int aOrB = 0; aOrB < 2; aOrB++)
1464	{
1465		const vector<Vec3>&		coords			= aOrB == 0 ? coordsA			: coordsB;
1466		const int				numTriangles	= aOrB == 0 ? numTrianglesA		: numTrianglesB;
1467		TriangleSet&			triangles		= aOrB == 0 ? trianglesA		: trianglesB;
1468
1469		for (int triNdx = 0; triNdx < numTriangles; triNdx++)
1470		{
1471			Triangle triangle(coords[3*triNdx + 0],
1472							  coords[3*triNdx + 1],
1473							  coords[3*triNdx + 2]);
1474
1475			if (isTriangleRelevant(triangle.getPtr()))
1476			{
1477				std::sort(triangle.getPtr(), triangle.getPtr()+3, VecLexLessThan<3>());
1478				triangles.insert(triangle);
1479			}
1480		}
1481	}
1482
1483	{
1484		TriangleSet::const_iterator aIt = trianglesA.begin();
1485		TriangleSet::const_iterator bIt = trianglesB.begin();
1486
1487		while (aIt != trianglesA.end() || bIt != trianglesB.end())
1488		{
1489			const bool aEnd = aIt == trianglesA.end();
1490			const bool bEnd = bIt == trianglesB.end();
1491
1492			if (aEnd || bEnd || *aIt != *bIt)
1493			{
1494				log << TestLog::Message << "Failure: triangle sets in two cases are not equal (when ignoring triangle and vertex order"
1495					<< (ignoredTriangleDescription == DE_NULL ? "" : string() + ", and " + ignoredTriangleDescription) << ")" << TestLog::EndMessage;
1496
1497				if (!aEnd && (bEnd || TriangleLexLessThan()(*aIt, *bIt)))
1498					log << TestLog::Message << "Note: e.g. triangle " << *aIt << " exists for first case but not for second" << TestLog::EndMessage;
1499				else
1500					log << TestLog::Message << "Note: e.g. triangle " << *bIt << " exists for second case but not for first" << TestLog::EndMessage;
1501
1502				return false;
1503			}
1504
1505			++aIt;
1506			++bIt;
1507		}
1508
1509		return true;
1510	}
1511}
1512
1513static bool compareTriangleSets (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, TestLog& log)
1514{
1515	return compareTriangleSets(coordsA, coordsB, log, ConstantUnaryPredicate<const Vec3*, true>());
1516}
1517
1518static void checkExtensionSupport (Context& context, const char* extName)
1519{
1520	if (!context.getContextInfo().isExtensionSupported(extName))
1521		throw tcu::NotSupportedError(string(extName) + " not supported");
1522}
1523
1524static void checkTessellationSupport (Context& context)
1525{
1526	checkExtensionSupport(context, "GL_EXT_tessellation_shader");
1527}
1528
1529// Draw primitives with shared edges and check that no cracks are visible at the shared edges.
1530class CommonEdgeCase : public TestCase
1531{
1532public:
1533	enum CaseType
1534	{
1535		CASETYPE_BASIC = 0,		//!< Order patch vertices such that when two patches share a vertex, it's at the same index for both.
1536		CASETYPE_PRECISE,		//!< Vertex indices don't match like for CASETYPE_BASIC, but other measures are taken, using the 'precise' qualifier.
1537
1538		CASETYPE_LAST
1539	};
1540
1541	CommonEdgeCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing, CaseType caseType)
1542		: TestCase			(context, name, description)
1543		, m_primitiveType	(primitiveType)
1544		, m_spacing			(spacing)
1545		, m_caseType		(caseType)
1546	{
1547		DE_ASSERT(m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES || m_primitiveType == TESSPRIMITIVETYPE_QUADS);
1548	}
1549
1550	void							init		(void);
1551	void							deinit		(void);
1552	IterateResult					iterate		(void);
1553
1554private:
1555	static const int				RENDER_SIZE = 256;
1556
1557	const TessPrimitiveType			m_primitiveType;
1558	const SpacingMode				m_spacing;
1559	const CaseType					m_caseType;
1560
1561	SharedPtr<const ShaderProgram>	m_program;
1562};
1563
1564void CommonEdgeCase::init (void)
1565{
1566	checkTessellationSupport(m_context);
1567
1568	if (m_caseType == CASETYPE_PRECISE)
1569		checkExtensionSupport(m_context, "GL_EXT_gpu_shader5");
1570
1571	checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
1572
1573	m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
1574			<< glu::VertexSource					("#version 310 es\n"
1575													 "\n"
1576													 "in highp vec2 in_v_position;\n"
1577													 "in highp float in_v_tessParam;\n"
1578													 "\n"
1579													 "out highp vec2 in_tc_position;\n"
1580													 "out highp float in_tc_tessParam;\n"
1581													 "\n"
1582													 "void main (void)\n"
1583													 "{\n"
1584													 "	in_tc_position = in_v_position;\n"
1585													 "	in_tc_tessParam = in_v_tessParam;\n"
1586													 "}\n")
1587
1588			<< glu::TessellationControlSource		("#version 310 es\n"
1589													 "#extension GL_EXT_tessellation_shader : require\n"
1590													 + string(m_caseType == CASETYPE_PRECISE ? "#extension GL_EXT_gpu_shader5 : require\n" : "") +
1591													 "\n"
1592													 "layout (vertices = " + string(m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "3" : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? "4" : DE_NULL) + ") out;\n"
1593													 "\n"
1594													 "in highp vec2 in_tc_position[];\n"
1595													 "in highp float in_tc_tessParam[];\n"
1596													 "\n"
1597													 "out highp vec2 in_te_position[];\n"
1598													 "\n"
1599													 + (m_caseType == CASETYPE_PRECISE ? "precise gl_TessLevelOuter;\n\n" : "") +
1600													 "void main (void)\n"
1601													 "{\n"
1602													 "	in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];\n"
1603													 "\n"
1604													 "	gl_TessLevelInner[0] = 5.0;\n"
1605													 "	gl_TessLevelInner[1] = 5.0;\n"
1606													 "\n"
1607													 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
1608														"	gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[2]);\n"
1609														"	gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[0]);\n"
1610														"	gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[1]);\n"
1611													  : m_primitiveType == TESSPRIMITIVETYPE_QUADS ?
1612														"	gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[2]);\n"
1613														"	gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[0]);\n"
1614														"	gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[3] + in_tc_tessParam[1]);\n"
1615														"	gl_TessLevelOuter[3] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[3]);\n"
1616													  : DE_NULL) +
1617													 "}\n")
1618
1619			<< glu::TessellationEvaluationSource	("#version 310 es\n"
1620													 "#extension GL_EXT_tessellation_shader : require\n"
1621													 + string(m_caseType == CASETYPE_PRECISE ? "#extension GL_EXT_gpu_shader5 : require\n" : "") +
1622													 "\n"
1623													 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing) +
1624													 "\n"
1625													 "in highp vec2 in_te_position[];\n"
1626													 "\n"
1627													 "out mediump vec4 in_f_color;\n"
1628													 "\n"
1629													 + (m_caseType == CASETYPE_PRECISE ? "precise gl_Position;\n\n" : "") +
1630													 "void main (void)\n"
1631													 "{\n"
1632													 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
1633														string(m_caseType == CASETYPE_PRECISE
1634															? "\t// Note: when this is an edge vertex, at most two of the following terms are non-zero (so order doesn't matter)\n"
1635															: "") +
1636														"	highp vec2 pos = gl_TessCoord.x*in_te_position[0] + gl_TessCoord.y*in_te_position[1] + gl_TessCoord.z*in_te_position[2];\n"
1637														"\n"
1638														"	highp float f = sqrt(3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z))) * 0.5 + 0.5;\n"
1639														"	in_f_color = vec4(gl_TessCoord*f, 1.0);\n"
1640													  : m_primitiveType == TESSPRIMITIVETYPE_QUADS ?
1641													    string()
1642														+ (m_caseType == CASETYPE_BASIC ?
1643															"	highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0]\n"
1644															"	               + (    gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1]\n"
1645															"	               + (1.0-gl_TessCoord.x)*(    gl_TessCoord.y)*in_te_position[2]\n"
1646															"	               + (    gl_TessCoord.x)*(    gl_TessCoord.y)*in_te_position[3];\n"
1647														 : m_caseType == CASETYPE_PRECISE ?
1648															"	highp vec2 a = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0];\n"
1649															"	highp vec2 b = (    gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1];\n"
1650															"	highp vec2 c = (1.0-gl_TessCoord.x)*(    gl_TessCoord.y)*in_te_position[2];\n"
1651															"	highp vec2 d = (    gl_TessCoord.x)*(    gl_TessCoord.y)*in_te_position[3];\n"
1652															"	// Note: when this is an edge vertex, at most two of the following terms are non-zero (so order doesn't matter)\n"
1653															"	highp vec2 pos = a+b+c+d;\n"
1654														 : DE_NULL) +
1655														"\n"
1656														"	highp float f = sqrt(1.0 - 2.0 * max(abs(gl_TessCoord.x - 0.5), abs(gl_TessCoord.y - 0.5)))*0.5 + 0.5;\n"
1657														"	in_f_color = vec4(0.1, gl_TessCoord.xy*f, 1.0);\n"
1658													  : DE_NULL) +
1659													 "\n"
1660													 "	// Offset the position slightly, based on the parity of the bits in the float representation.\n"
1661													 "	// This is done to detect possible small differences in edge vertex positions between patches.\n"
1662													 "	uvec2 bits = floatBitsToUint(pos);\n"
1663													 "	uint numBits = 0u;\n"
1664													 "	for (uint i = 0u; i < 32u; i++)\n"
1665													 "		numBits += ((bits[0] >> i) & 1u) + ((bits[1] >> i) & 1u);\n"
1666													 "	pos += float(numBits&1u)*0.04;\n"
1667													 "\n"
1668													 "	gl_Position = vec4(pos, 0.0, 1.0);\n"
1669													 "}\n")
1670
1671			<< glu::FragmentSource					("#version 310 es\n"
1672													 "\n"
1673													 "layout (location = 0) out mediump vec4 o_color;\n"
1674													 "\n"
1675													 "in mediump vec4 in_f_color;\n"
1676													 "\n"
1677													 "void main (void)\n"
1678													 "{\n"
1679													 "	o_color = in_f_color;\n"
1680													 "}\n")));
1681
1682	m_testCtx.getLog() << *m_program;
1683	if (!m_program->isOk())
1684		TCU_FAIL("Program compilation failed");
1685}
1686
1687void CommonEdgeCase::deinit (void)
1688{
1689	m_program.clear();
1690}
1691
1692CommonEdgeCase::IterateResult CommonEdgeCase::iterate (void)
1693{
1694	TestLog&					log						= m_testCtx.getLog();
1695	const RenderContext&		renderCtx				= m_context.getRenderContext();
1696	const RandomViewport		viewport				(renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
1697	const deUint32				programGL				= m_program->getProgram();
1698	const glw::Functions&		gl						= renderCtx.getFunctions();
1699
1700	const int					gridWidth				= 4;
1701	const int					gridHeight				= 4;
1702	const int					numVertices				= (gridWidth+1)*(gridHeight+1);
1703	const int					numIndices				= gridWidth*gridHeight * (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3*2 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4 : -1);
1704	const int					numPosCompsPerVertex	= 2;
1705	const int					totalNumPosComps		= numPosCompsPerVertex*numVertices;
1706	vector<float>				gridPosComps;
1707	vector<float>				gridTessParams;
1708	vector<deUint16>			gridIndices;
1709
1710	gridPosComps.reserve(totalNumPosComps);
1711	gridTessParams.reserve(numVertices);
1712	gridIndices.reserve(numIndices);
1713
1714	{
1715		Random constantSeedRnd(42);
1716
1717		for (int i = 0; i < gridHeight+1; i++)
1718		for (int j = 0; j < gridWidth+1; j++)
1719		{
1720			gridPosComps.push_back(-1.0f + 2.0f * ((float)j + 0.5f) / (float)(gridWidth+1));
1721			gridPosComps.push_back(-1.0f + 2.0f * ((float)i + 0.5f) / (float)(gridHeight+1));
1722			gridTessParams.push_back((float)(i*(gridWidth+1) + j) / (float)(numVertices-1));
1723		}
1724	}
1725
1726	// Generate patch vertex indices.
1727	// \note If CASETYPE_BASIC, the vertices are ordered such that when multiple
1728	//		 triangles/quads share a vertex, it's at the same index for everyone.
1729
1730	if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
1731	{
1732		for (int i = 0; i < gridHeight; i++)
1733		for (int j = 0; j < gridWidth; j++)
1734		{
1735			const int corners[4] =
1736			{
1737				(i+0)*(gridWidth+1) + j+0,
1738				(i+0)*(gridWidth+1) + j+1,
1739				(i+1)*(gridWidth+1) + j+0,
1740				(i+1)*(gridWidth+1) + j+1
1741			};
1742
1743			const int secondTriangleVertexIndexOffset = m_caseType == CASETYPE_BASIC	? 0
1744													  : m_caseType == CASETYPE_PRECISE	? 1
1745													  : -1;
1746			DE_ASSERT(secondTriangleVertexIndexOffset != -1);
1747
1748			for (int k = 0; k < 3; k++)
1749				gridIndices.push_back(corners[(k+0 + i + (2-j%3)) % 3]);
1750			for (int k = 0; k < 3; k++)
1751				gridIndices.push_back(corners[(k+2 + i + (2-j%3) + secondTriangleVertexIndexOffset) % 3 + 1]);
1752		}
1753	}
1754	else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
1755	{
1756		for (int i = 0; i < gridHeight; i++)
1757		for (int j = 0; j < gridWidth; j++)
1758		{
1759			// \note The vertices are ordered such that when multiple quads
1760			//		 share a vertices, it's at the same index for everyone.
1761			for (int m = 0; m < 2; m++)
1762			for (int n = 0; n < 2; n++)
1763				gridIndices.push_back((i+(i+m)%2)*(gridWidth+1) + j+(j+n)%2);
1764
1765			if(m_caseType == CASETYPE_PRECISE && (i+j) % 2 == 0)
1766				std::reverse(gridIndices.begin() + (gridIndices.size() - 4),
1767							 gridIndices.begin() + gridIndices.size());
1768		}
1769	}
1770	else
1771		DE_ASSERT(false);
1772
1773	DE_ASSERT((int)gridPosComps.size() == totalNumPosComps);
1774	DE_ASSERT((int)gridTessParams.size() == numVertices);
1775	DE_ASSERT((int)gridIndices.size() == numIndices);
1776
1777	setViewport(gl, viewport);
1778	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
1779	gl.useProgram(programGL);
1780
1781	{
1782		gl.patchParameteri(GL_PATCH_VERTICES, m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4 : -1);
1783		gl.clear(GL_COLOR_BUFFER_BIT);
1784
1785		const glu::VertexArrayBinding attrBindings[] =
1786		{
1787			glu::va::Float("in_v_position", numPosCompsPerVertex, numVertices, 0, &gridPosComps[0]),
1788			glu::va::Float("in_v_tessParam", 1, numVertices, 0, &gridTessParams[0])
1789		};
1790
1791		glu::draw(renderCtx, programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
1792			glu::pr::Patches((int)gridIndices.size(), &gridIndices[0]));
1793		GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
1794	}
1795
1796	{
1797		const tcu::Surface rendered = getPixels(renderCtx, viewport);
1798
1799		log << TestLog::Image("RenderedImage", "Rendered Image", rendered)
1800			<< TestLog::Message << "Note: coloring is done to clarify the positioning and orientation of the "
1801								<< (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "triangles" : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? "quads" : DE_NULL)
1802								<< "; the color of a vertex corresponds to the index of that vertex in the patch"
1803								<< TestLog::EndMessage;
1804
1805		if (m_caseType == CASETYPE_BASIC)
1806			log << TestLog::Message << "Note: each shared vertex has the same index among the primitives it belongs to" << TestLog::EndMessage;
1807		else if (m_caseType == CASETYPE_PRECISE)
1808			log << TestLog::Message << "Note: the 'precise' qualifier is used to avoid cracks between primitives" << TestLog::EndMessage;
1809		else
1810			DE_ASSERT(false);
1811
1812		// Ad-hoc result verification - check that a certain rectangle in the image contains no black pixels.
1813
1814		const int startX	= (int)(0.15f * (float)rendered.getWidth());
1815		const int endX		= (int)(0.85f * (float)rendered.getWidth());
1816		const int startY	= (int)(0.15f * (float)rendered.getHeight());
1817		const int endY		= (int)(0.85f * (float)rendered.getHeight());
1818
1819		for (int y = startY; y < endY; y++)
1820		for (int x = startX; x < endX; x++)
1821		{
1822			const tcu::RGBA pixel = rendered.getPixel(x, y);
1823
1824			if (pixel.getRed() == 0 && pixel.getGreen() == 0 && pixel.getBlue() == 0)
1825			{
1826				log << TestLog::Message << "Failure: there seem to be cracks in the rendered result" << TestLog::EndMessage
1827					<< TestLog::Message << "Note: pixel with zero r, g and b channels found at " << tcu::IVec2(x, y) << TestLog::EndMessage;
1828
1829				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
1830				return STOP;
1831			}
1832		}
1833
1834		log << TestLog::Message << "Success: there seem to be no cracks in the rendered result" << TestLog::EndMessage;
1835	}
1836
1837	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1838	return STOP;
1839}
1840
1841// Check tessellation coordinates (read with transform feedback).
1842class TessCoordCase : public TestCase
1843{
1844public:
1845					TessCoordCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing)
1846						: TestCase			(context, name, description)
1847						, m_primitiveType	(primitiveType)
1848						, m_spacing			(spacing)
1849					{
1850					}
1851
1852	void			init		(void);
1853	void			deinit		(void);
1854	IterateResult	iterate		(void);
1855
1856private:
1857	struct TessLevels
1858	{
1859		float inner[2];
1860		float outer[4];
1861	};
1862
1863	static const int				RENDER_SIZE = 16;
1864
1865	vector<TessLevels>				genTessLevelCases (void) const;
1866
1867	const TessPrimitiveType			m_primitiveType;
1868	const SpacingMode				m_spacing;
1869
1870	SharedPtr<const ShaderProgram>	m_program;
1871};
1872
1873void TessCoordCase::init (void)
1874{
1875	checkTessellationSupport(m_context);
1876	checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
1877
1878	m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
1879			<< glu::VertexSource					("#version 310 es\n"
1880													 "\n"
1881													 "void main (void)\n"
1882													 "{\n"
1883													 "}\n")
1884
1885			<< glu::TessellationControlSource		("#version 310 es\n"
1886													 "#extension GL_EXT_tessellation_shader : require\n"
1887													 "\n"
1888													 "layout (vertices = 1) out;\n"
1889													 "\n"
1890													 "uniform mediump float u_tessLevelInner0;\n"
1891													 "uniform mediump float u_tessLevelInner1;\n"
1892													 "\n"
1893													 "uniform mediump float u_tessLevelOuter0;\n"
1894													 "uniform mediump float u_tessLevelOuter1;\n"
1895													 "uniform mediump float u_tessLevelOuter2;\n"
1896													 "uniform mediump float u_tessLevelOuter3;\n"
1897													 "\n"
1898													 "void main (void)\n"
1899													 "{\n"
1900													 "	gl_TessLevelInner[0] = u_tessLevelInner0;\n"
1901													 "	gl_TessLevelInner[1] = u_tessLevelInner1;\n"
1902													 "\n"
1903													 "	gl_TessLevelOuter[0] = u_tessLevelOuter0;\n"
1904													 "	gl_TessLevelOuter[1] = u_tessLevelOuter1;\n"
1905													 "	gl_TessLevelOuter[2] = u_tessLevelOuter2;\n"
1906													 "	gl_TessLevelOuter[3] = u_tessLevelOuter3;\n"
1907													 "}\n")
1908
1909			<< glu::TessellationEvaluationSource	("#version 310 es\n"
1910													 "#extension GL_EXT_tessellation_shader : require\n"
1911													 "\n"
1912													 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, true) +
1913													 "\n"
1914													 "out highp vec3 out_te_tessCoord;\n"
1915													 "\n"
1916													 "void main (void)\n"
1917													 "{\n"
1918													 "	out_te_tessCoord = gl_TessCoord;\n"
1919													 "	gl_Position = vec4(gl_TessCoord.xy*1.6 - 0.8, 0.0, 1.0);\n"
1920													 "}\n")
1921
1922			<< glu::FragmentSource					("#version 310 es\n"
1923													 "\n"
1924													 "layout (location = 0) out mediump vec4 o_color;\n"
1925													 "\n"
1926													 "void main (void)\n"
1927													 "{\n"
1928													 "	o_color = vec4(1.0);\n"
1929													 "}\n")
1930
1931			<< glu::TransformFeedbackVarying		("out_te_tessCoord")
1932			<< glu::TransformFeedbackMode			(GL_INTERLEAVED_ATTRIBS)));
1933
1934	m_testCtx.getLog() << *m_program;
1935	if (!m_program->isOk())
1936		TCU_FAIL("Program compilation failed");
1937}
1938
1939void TessCoordCase::deinit (void)
1940{
1941	m_program.clear();
1942}
1943
1944vector<TessCoordCase::TessLevels> TessCoordCase::genTessLevelCases (void) const
1945{
1946	static const TessLevels rawTessLevelCases[] =
1947	{
1948		{ { 1.0f,	1.0f	},	{ 1.0f,		1.0f,	1.0f,	1.0f	} },
1949		{ { 63.0f,	24.0f	},	{ 15.0f,	42.0f,	10.0f,	12.0f	} },
1950		{ { 3.0f,	2.0f	},	{ 6.0f,		8.0f,	7.0f,	9.0f	} },
1951		{ { 4.0f,	6.0f	},	{ 2.0f,		3.0f,	1.0f,	4.0f	} },
1952		{ { 2.0f,	2.0f	},	{ 6.0f,		8.0f,	7.0f,	9.0f	} },
1953		{ { 5.0f,	6.0f	},	{ 1.0f,		1.0f,	1.0f,	1.0f	} },
1954		{ { 1.0f,	6.0f	},	{ 2.0f,		3.0f,	1.0f,	4.0f	} },
1955		{ { 5.0f,	1.0f	},	{ 2.0f,		3.0f,	1.0f,	4.0f	} },
1956		{ { 5.2f,	1.6f	},	{ 2.9f,		3.4f,	1.5f,	4.1f	} }
1957	};
1958
1959	if (m_spacing == SPACINGMODE_EQUAL)
1960		return vector<TessLevels>(DE_ARRAY_BEGIN(rawTessLevelCases), DE_ARRAY_END(rawTessLevelCases));
1961	else
1962	{
1963		vector<TessLevels> result;
1964		result.reserve(DE_LENGTH_OF_ARRAY(rawTessLevelCases));
1965
1966		for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(rawTessLevelCases); tessLevelCaseNdx++)
1967		{
1968			TessLevels curTessLevelCase = rawTessLevelCases[tessLevelCaseNdx];
1969
1970			float* const inner = &curTessLevelCase.inner[0];
1971			float* const outer = &curTessLevelCase.outer[0];
1972
1973			for (int j = 0; j < 2; j++) inner[j] = (float)getClampedRoundedTessLevel(m_spacing, inner[j]);
1974			for (int j = 0; j < 4; j++) outer[j] = (float)getClampedRoundedTessLevel(m_spacing, outer[j]);
1975
1976			if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
1977			{
1978				if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f)
1979				{
1980					if (inner[0] == 1.0f)
1981						inner[0] = (float)getClampedRoundedTessLevel(m_spacing, inner[0] + 0.1f);
1982				}
1983			}
1984			else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
1985			{
1986				if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f || outer[3] > 1.0f)
1987				{
1988					if (inner[0] == 1.0f) inner[0] = (float)getClampedRoundedTessLevel(m_spacing, inner[0] + 0.1f);
1989					if (inner[1] == 1.0f) inner[1] = (float)getClampedRoundedTessLevel(m_spacing, inner[1] + 0.1f);
1990				}
1991			}
1992
1993			result.push_back(curTessLevelCase);
1994		}
1995
1996		DE_ASSERT((int)result.size() == DE_LENGTH_OF_ARRAY(rawTessLevelCases));
1997		return result;
1998	}
1999}
2000
2001TessCoordCase::IterateResult TessCoordCase::iterate (void)
2002{
2003	typedef TransformFeedbackHandler<Vec3> TFHandler;
2004
2005	TestLog&						log							= m_testCtx.getLog();
2006	const RenderContext&			renderCtx					= m_context.getRenderContext();
2007	const RandomViewport			viewport					(renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2008	const deUint32					programGL					= m_program->getProgram();
2009	const glw::Functions&			gl							= renderCtx.getFunctions();
2010
2011	const int						tessLevelInner0Loc			= gl.getUniformLocation(programGL, "u_tessLevelInner0");
2012	const int						tessLevelInner1Loc			= gl.getUniformLocation(programGL, "u_tessLevelInner1");
2013	const int						tessLevelOuter0Loc			= gl.getUniformLocation(programGL, "u_tessLevelOuter0");
2014	const int						tessLevelOuter1Loc			= gl.getUniformLocation(programGL, "u_tessLevelOuter1");
2015	const int						tessLevelOuter2Loc			= gl.getUniformLocation(programGL, "u_tessLevelOuter2");
2016	const int						tessLevelOuter3Loc			= gl.getUniformLocation(programGL, "u_tessLevelOuter3");
2017
2018	const vector<TessLevels>		tessLevelCases				= genTessLevelCases();
2019	vector<vector<Vec3> >			caseReferences				(tessLevelCases.size());
2020
2021	for (int i = 0; i < (int)tessLevelCases.size(); i++)
2022		caseReferences[i] = generateReferenceTessCoords(m_primitiveType, m_spacing, &tessLevelCases[i].inner[0], &tessLevelCases[i].outer[0]);
2023
2024	const int						maxNumVertices				= (int)std::max_element(caseReferences.begin(), caseReferences.end(), SizeLessThan<vector<Vec3> >())->size();
2025	const TFHandler					tfHandler					(m_context.getRenderContext(), maxNumVertices);
2026
2027	bool							success						= true;
2028
2029	setViewport(gl, viewport);
2030	gl.useProgram(programGL);
2031
2032	gl.patchParameteri(GL_PATCH_VERTICES, 1);
2033
2034	for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < (int)tessLevelCases.size(); tessLevelCaseNdx++)
2035	{
2036		const float* const innerLevels = &tessLevelCases[tessLevelCaseNdx].inner[0];
2037		const float* const outerLevels = &tessLevelCases[tessLevelCaseNdx].outer[0];
2038
2039		log << TestLog::Message << "Tessellation levels: " << tessellationLevelsString(innerLevels, outerLevels, m_primitiveType) << TestLog::EndMessage;
2040
2041		gl.uniform1f(tessLevelInner0Loc, innerLevels[0]);
2042		gl.uniform1f(tessLevelInner1Loc, innerLevels[1]);
2043		gl.uniform1f(tessLevelOuter0Loc, outerLevels[0]);
2044		gl.uniform1f(tessLevelOuter1Loc, outerLevels[1]);
2045		gl.uniform1f(tessLevelOuter2Loc, outerLevels[2]);
2046		gl.uniform1f(tessLevelOuter3Loc, outerLevels[3]);
2047		GLU_EXPECT_NO_ERROR(gl.getError(), "Setup failed");
2048
2049		{
2050			const vector<Vec3>&			tessCoordsRef	= caseReferences[tessLevelCaseNdx];
2051			const TFHandler::Result		tfResult		= tfHandler.renderAndGetPrimitives(programGL, GL_POINTS, 0, DE_NULL, 1);
2052
2053			if (tfResult.numPrimitives != (int)tessCoordsRef.size())
2054			{
2055				log << TestLog::Message << "Failure: GL reported GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN to be "
2056										<< tfResult.numPrimitives << ", reference value is " << tessCoordsRef.size()
2057										<< " (logging further info anyway)" << TestLog::EndMessage;
2058				success = false;
2059			}
2060			else
2061				log << TestLog::Message << "Note: GL reported GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN to be " << tfResult.numPrimitives << TestLog::EndMessage;
2062
2063			if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
2064				log << TestLog::Message << "Note: in the following visualization(s), the u=1, v=1, w=1 corners are at the right, top, and left corners, respectively" << TestLog::EndMessage;
2065			else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS || m_primitiveType == TESSPRIMITIVETYPE_ISOLINES)
2066				log << TestLog::Message << "Note: in the following visualization(s), u and v coordinate go left-to-right and bottom-to-top, respectively" << TestLog::EndMessage;
2067			else
2068				DE_ASSERT(false);
2069
2070			success = compareTessCoords(log, m_primitiveType, tessCoordsRef, tfResult.varying) && success;
2071		}
2072
2073		if (!success)
2074			break;
2075		else
2076			log << TestLog::Message << "All OK" << TestLog::EndMessage;
2077	}
2078
2079	m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Invalid tessellation coordinates");
2080	return STOP;
2081}
2082
2083// Check validity of fractional spacing modes. Draws a single isoline, reads tesscoords with transform feedback.
2084class FractionalSpacingModeCase : public TestCase
2085{
2086public:
2087					FractionalSpacingModeCase (Context& context, const char* name, const char* description, SpacingMode spacing)
2088						: TestCase	(context, name, description)
2089						, m_spacing	(spacing)
2090					{
2091						DE_ASSERT(m_spacing == SPACINGMODE_FRACTIONAL_EVEN || m_spacing == SPACINGMODE_FRACTIONAL_ODD);
2092					}
2093
2094	void			init		(void);
2095	void			deinit		(void);
2096	IterateResult	iterate		(void);
2097
2098private:
2099	static const int				RENDER_SIZE = 16;
2100
2101	static vector<float>			genTessLevelCases (void);
2102
2103	const SpacingMode				m_spacing;
2104
2105	SharedPtr<const ShaderProgram>	m_program;
2106};
2107
2108void FractionalSpacingModeCase::init (void)
2109{
2110	checkTessellationSupport(m_context);
2111	checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
2112
2113	m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
2114			<< glu::VertexSource					("#version 310 es\n"
2115													 "\n"
2116													 "void main (void)\n"
2117													 "{\n"
2118													 "}\n")
2119
2120			<< glu::TessellationControlSource		("#version 310 es\n"
2121													 "#extension GL_EXT_tessellation_shader : require\n"
2122													 "\n"
2123													 "layout (vertices = 1) out;\n"
2124													 "\n"
2125													 "uniform mediump float u_tessLevelOuter1;\n"
2126													 "\n"
2127													 "void main (void)\n"
2128													 "{\n"
2129													 "	gl_TessLevelOuter[0] = 1.0;\n"
2130													 "	gl_TessLevelOuter[1] = u_tessLevelOuter1;\n"
2131													 "}\n")
2132
2133			<< glu::TessellationEvaluationSource	("#version 310 es\n"
2134													 "#extension GL_EXT_tessellation_shader : require\n"
2135													 "\n"
2136													 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_ISOLINES, m_spacing, true) +
2137													 "\n"
2138													 "out highp float out_te_tessCoord;\n"
2139													 "\n"
2140													 "void main (void)\n"
2141													 "{\n"
2142													 "	out_te_tessCoord = gl_TessCoord.x;\n"
2143													 "	gl_Position = vec4(gl_TessCoord.xy*1.6 - 0.8, 0.0, 1.0);\n"
2144													 "}\n")
2145
2146			<< glu::FragmentSource					("#version 310 es\n"
2147													 "\n"
2148													 "layout (location = 0) out mediump vec4 o_color;\n"
2149													 "\n"
2150													 "void main (void)\n"
2151													 "{\n"
2152													 "	o_color = vec4(1.0);\n"
2153													 "}\n")
2154
2155			<< glu::TransformFeedbackVarying		("out_te_tessCoord")
2156			<< glu::TransformFeedbackMode			(GL_INTERLEAVED_ATTRIBS)));
2157
2158	m_testCtx.getLog() << *m_program;
2159	if (!m_program->isOk())
2160		TCU_FAIL("Program compilation failed");
2161}
2162
2163void FractionalSpacingModeCase::deinit (void)
2164{
2165	m_program.clear();
2166}
2167
2168vector<float> FractionalSpacingModeCase::genTessLevelCases (void)
2169{
2170	vector<float> result;
2171
2172	// Ranges [7.0 .. 8.0), [8.0 .. 9.0) and [9.0 .. 10.0)
2173	{
2174		static const float	rangeStarts[]		= { 7.0f, 8.0f, 9.0f };
2175		const int			numSamplesPerRange	= 10;
2176
2177		for (int rangeNdx = 0; rangeNdx < DE_LENGTH_OF_ARRAY(rangeStarts); rangeNdx++)
2178			for (int i = 0; i < numSamplesPerRange; i++)
2179				result.push_back(rangeStarts[rangeNdx] + (float)i/(float)numSamplesPerRange);
2180	}
2181
2182	// 0.3, 1.3, 2.3,  ... , 62.3
2183	for (int i = 0; i <= 62; i++)
2184		result.push_back((float)i + 0.3f);
2185
2186	return result;
2187}
2188
2189FractionalSpacingModeCase::IterateResult FractionalSpacingModeCase::iterate (void)
2190{
2191	typedef TransformFeedbackHandler<float> TFHandler;
2192
2193	TestLog&						log							= m_testCtx.getLog();
2194	const RenderContext&			renderCtx					= m_context.getRenderContext();
2195	const RandomViewport			viewport					(renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2196	const deUint32					programGL					= m_program->getProgram();
2197	const glw::Functions&			gl							= renderCtx.getFunctions();
2198
2199	const int						tessLevelOuter1Loc			= gl.getUniformLocation(programGL, "u_tessLevelOuter1");
2200
2201	// Second outer tessellation levels.
2202	const vector<float>				tessLevelCases				= genTessLevelCases();
2203	const int						maxNumVertices				= 1 + getClampedRoundedTessLevel(m_spacing, *std::max_element(tessLevelCases.begin(), tessLevelCases.end()));
2204	vector<float>					additionalSegmentLengths;
2205	vector<int>						additionalSegmentLocations;
2206
2207	const TFHandler					tfHandler					(m_context.getRenderContext(), maxNumVertices);
2208
2209	bool							success						= true;
2210
2211	setViewport(gl, viewport);
2212	gl.useProgram(programGL);
2213
2214	gl.patchParameteri(GL_PATCH_VERTICES, 1);
2215
2216	for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < (int)tessLevelCases.size(); tessLevelCaseNdx++)
2217	{
2218		const float outerLevel1 = tessLevelCases[tessLevelCaseNdx];
2219
2220		gl.uniform1f(tessLevelOuter1Loc, outerLevel1);
2221		GLU_EXPECT_NO_ERROR(gl.getError(), "Setup failed");
2222
2223		{
2224			const TFHandler::Result		tfResult = tfHandler.renderAndGetPrimitives(programGL, GL_POINTS, 0, DE_NULL, 1);
2225			float						additionalSegmentLength;
2226			int							additionalSegmentLocation;
2227
2228			success = verifyFractionalSpacingSingle(log, m_spacing, outerLevel1, tfResult.varying,
2229													additionalSegmentLength, additionalSegmentLocation);
2230
2231			if (!success)
2232				break;
2233
2234			additionalSegmentLengths.push_back(additionalSegmentLength);
2235			additionalSegmentLocations.push_back(additionalSegmentLocation);
2236		}
2237	}
2238
2239	if (success)
2240		success = verifyFractionalSpacingMultiple(log, m_spacing, tessLevelCases, additionalSegmentLengths, additionalSegmentLocations);
2241
2242	m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Invalid tessellation coordinates");
2243	return STOP;
2244}
2245
2246// Base class for a case with one input attribute (in_v_position) and optionally a TCS; tests with a couple of different sets of tessellation levels.
2247class BasicVariousTessLevelsPosAttrCase : public TestCase
2248{
2249public:
2250					BasicVariousTessLevelsPosAttrCase (Context&				context,
2251													   const char*			name,
2252													   const char*			description,
2253													   TessPrimitiveType	primitiveType,
2254													   SpacingMode			spacing,
2255													   const char*			referenceImagePathPrefix)
2256						: TestCase						(context, name, description)
2257						, m_primitiveType				(primitiveType)
2258						, m_spacing						(spacing)
2259						, m_referenceImagePathPrefix	(referenceImagePathPrefix)
2260					{
2261					}
2262
2263	void			init			(void);
2264	void			deinit			(void);
2265	IterateResult	iterate			(void);
2266
2267protected:
2268	virtual const glu::ProgramSources	makeSources (TessPrimitiveType, SpacingMode, const char* vtxOutPosAttrName) const = DE_NULL;
2269
2270private:
2271	static const int					RENDER_SIZE = 256;
2272
2273	const TessPrimitiveType				m_primitiveType;
2274	const SpacingMode					m_spacing;
2275	const string						m_referenceImagePathPrefix;
2276
2277	SharedPtr<const ShaderProgram>		m_program;
2278};
2279
2280void BasicVariousTessLevelsPosAttrCase::init (void)
2281{
2282	checkTessellationSupport(m_context);
2283	checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
2284
2285	{
2286		glu::ProgramSources sources = makeSources(m_primitiveType, m_spacing, "in_tc_position");
2287		DE_ASSERT(sources.sources[glu::SHADERTYPE_TESSELLATION_CONTROL].empty());
2288
2289		sources << glu::TessellationControlSource(	"#version 310 es\n"
2290													 "#extension GL_EXT_tessellation_shader : require\n"
2291													"\n"
2292													"layout (vertices = " + string(m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "3" : "4") + ") out;\n"
2293													"\n"
2294													"in highp vec2 in_tc_position[];\n"
2295													"\n"
2296													"out highp vec2 in_te_position[];\n"
2297													"\n"
2298													"uniform mediump float u_tessLevelInner0;\n"
2299													"uniform mediump float u_tessLevelInner1;\n"
2300													"uniform mediump float u_tessLevelOuter0;\n"
2301													"uniform mediump float u_tessLevelOuter1;\n"
2302													"uniform mediump float u_tessLevelOuter2;\n"
2303													"uniform mediump float u_tessLevelOuter3;\n"
2304													"\n"
2305													"void main (void)\n"
2306													"{\n"
2307													"	in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];\n"
2308													"\n"
2309													"	gl_TessLevelInner[0] = u_tessLevelInner0;\n"
2310													"	gl_TessLevelInner[1] = u_tessLevelInner1;\n"
2311													"\n"
2312													"	gl_TessLevelOuter[0] = u_tessLevelOuter0;\n"
2313													"	gl_TessLevelOuter[1] = u_tessLevelOuter1;\n"
2314													"	gl_TessLevelOuter[2] = u_tessLevelOuter2;\n"
2315													"	gl_TessLevelOuter[3] = u_tessLevelOuter3;\n"
2316													"}\n");
2317
2318		m_program = SharedPtr<const ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources));
2319	}
2320
2321	m_testCtx.getLog() << *m_program;
2322	if (!m_program->isOk())
2323		TCU_FAIL("Program compilation failed");
2324}
2325
2326void BasicVariousTessLevelsPosAttrCase::deinit (void)
2327{
2328	m_program.clear();
2329}
2330
2331BasicVariousTessLevelsPosAttrCase::IterateResult BasicVariousTessLevelsPosAttrCase::iterate (void)
2332{
2333	static const struct
2334	{
2335		float inner[2];
2336		float outer[4];
2337	} tessLevelCases[] =
2338	{
2339		{ { 9.0f,	9.0f	},	{ 9.0f,		9.0f,	9.0f,	9.0f	} },
2340		{ { 8.0f,	11.0f	},	{ 13.0f,	15.0f,	18.0f,	21.0f	} },
2341		{ { 17.0f,	14.0f	},	{ 3.0f,		6.0f,	9.0f,	12.0f	} }
2342	};
2343
2344	TestLog&				log					= m_testCtx.getLog();
2345	const RenderContext&	renderCtx			= m_context.getRenderContext();
2346	const RandomViewport	viewport			(renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2347	const deUint32			programGL			= m_program->getProgram();
2348	const glw::Functions&	gl					= renderCtx.getFunctions();
2349	const int				patchSize			= m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES	? 3
2350												: m_primitiveType == TESSPRIMITIVETYPE_QUADS		? 4
2351												: m_primitiveType == TESSPRIMITIVETYPE_ISOLINES		? 4
2352												: -1;
2353
2354	setViewport(gl, viewport);
2355	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
2356	gl.useProgram(programGL);
2357
2358	gl.patchParameteri(GL_PATCH_VERTICES, patchSize);
2359
2360	for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(tessLevelCases); tessLevelCaseNdx++)
2361	{
2362		float innerLevels[2];
2363		float outerLevels[4];
2364
2365		for (int i = 0; i < DE_LENGTH_OF_ARRAY(innerLevels); i++)
2366			innerLevels[i] = (float)getClampedRoundedTessLevel(m_spacing, tessLevelCases[tessLevelCaseNdx].inner[i]);
2367
2368		for (int i = 0; i < DE_LENGTH_OF_ARRAY(outerLevels); i++)
2369			outerLevels[i] = (float)getClampedRoundedTessLevel(m_spacing, tessLevelCases[tessLevelCaseNdx].outer[i]);
2370
2371		log << TestLog::Message << "Tessellation levels: " << tessellationLevelsString(&innerLevels[0], &outerLevels[0], m_primitiveType) << TestLog::EndMessage;
2372
2373		gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelInner0"), innerLevels[0]);
2374		gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelInner1"), innerLevels[1]);
2375		gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter0"), outerLevels[0]);
2376		gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter1"), outerLevels[1]);
2377		gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter2"), outerLevels[2]);
2378		gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter3"), outerLevels[3]);
2379
2380		gl.clear(GL_COLOR_BUFFER_BIT);
2381
2382		{
2383			vector<Vec2> positions;
2384			positions.reserve(4);
2385
2386			if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
2387			{
2388				positions.push_back(Vec2( 0.8f,    0.6f));
2389				positions.push_back(Vec2( 0.0f, -0.786f));
2390				positions.push_back(Vec2(-0.8f,    0.6f));
2391			}
2392			else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS || m_primitiveType == TESSPRIMITIVETYPE_ISOLINES)
2393			{
2394				positions.push_back(Vec2(-0.8f, -0.8f));
2395				positions.push_back(Vec2( 0.8f, -0.8f));
2396				positions.push_back(Vec2(-0.8f,  0.8f));
2397				positions.push_back(Vec2( 0.8f,  0.8f));
2398			}
2399			else
2400				DE_ASSERT(false);
2401
2402			DE_ASSERT((int)positions.size() == patchSize);
2403
2404			const glu::VertexArrayBinding attrBindings[] =
2405			{
2406				glu::va::Float("in_v_position", 2, (int)positions.size(), 0, &positions[0].x())
2407			};
2408
2409			glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
2410				glu::pr::Patches(patchSize));
2411			GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
2412		}
2413
2414		{
2415			const tcu::Surface			rendered	= getPixels(renderCtx, viewport);
2416			const tcu::TextureLevel		reference	= getPNG(m_testCtx.getArchive(), m_referenceImagePathPrefix + "_" + de::toString(tessLevelCaseNdx) + ".png");
2417			const bool					success		= tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.002f, tcu::COMPARE_LOG_RESULT);
2418
2419			if (!success)
2420			{
2421				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
2422				return STOP;
2423			}
2424		}
2425	}
2426
2427	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2428	return STOP;
2429}
2430
2431// Test that there are no obvious gaps in the triangulation of a tessellated triangle or quad.
2432class BasicTriangleFillCoverCase : public BasicVariousTessLevelsPosAttrCase
2433{
2434public:
2435	BasicTriangleFillCoverCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing, const char* referenceImagePathPrefix)
2436		: BasicVariousTessLevelsPosAttrCase (context, name, description, primitiveType, spacing, referenceImagePathPrefix)
2437	{
2438		DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
2439	}
2440
2441protected:
2442	const glu::ProgramSources makeSources (TessPrimitiveType primitiveType, SpacingMode spacing, const char* vtxOutPosAttrName) const
2443	{
2444		return glu::ProgramSources()
2445			<< glu::VertexSource					("#version 310 es\n"
2446													 "\n"
2447													 "in highp vec2 in_v_position;\n"
2448													 "\n"
2449													 "out highp vec2 " + string(vtxOutPosAttrName) + ";\n"
2450													 "\n"
2451													 "void main (void)\n"
2452													 "{\n"
2453													 "	" + vtxOutPosAttrName + " = in_v_position;\n"
2454													 "}\n")
2455
2456			<< glu::TessellationEvaluationSource	("#version 310 es\n"
2457													 "#extension GL_EXT_tessellation_shader : require\n"
2458													 "\n"
2459													 + getTessellationEvaluationInLayoutString(primitiveType, spacing) +
2460													 "\n"
2461													 "in highp vec2 in_te_position[];\n"
2462													 "\n"
2463													 "void main (void)\n"
2464													 "{\n"
2465													 + (primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
2466														"\n"
2467														"	highp float d = 3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z));\n"
2468														"	highp vec2 corner0 = in_te_position[0];\n"
2469														"	highp vec2 corner1 = in_te_position[1];\n"
2470														"	highp vec2 corner2 = in_te_position[2];\n"
2471														"	highp vec2 pos =  corner0*gl_TessCoord.x + corner1*gl_TessCoord.y + corner2*gl_TessCoord.z;\n"
2472														"	highp vec2 fromCenter = pos - (corner0 + corner1 + corner2) / 3.0;\n"
2473														"	highp float f = (1.0 - length(fromCenter)) * (1.5 - d);\n"
2474														"	pos += 0.75 * f * fromCenter / (length(fromCenter) + 0.3);\n"
2475														"	gl_Position = vec4(pos, 0.0, 1.0);\n"
2476													  : primitiveType == TESSPRIMITIVETYPE_QUADS ?
2477														"	highp vec2 corner0 = in_te_position[0];\n"
2478														"	highp vec2 corner1 = in_te_position[1];\n"
2479														"	highp vec2 corner2 = in_te_position[2];\n"
2480														"	highp vec2 corner3 = in_te_position[3];\n"
2481														"	highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
2482														"	               + (    gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
2483														"	               + (1.0-gl_TessCoord.x)*(    gl_TessCoord.y)*corner2\n"
2484														"	               + (    gl_TessCoord.x)*(    gl_TessCoord.y)*corner3;\n"
2485														"	highp float d = 2.0 * min(abs(gl_TessCoord.x-0.5), abs(gl_TessCoord.y-0.5));\n"
2486														"	highp vec2 fromCenter = pos - (corner0 + corner1 + corner2 + corner3) / 4.0;\n"
2487														"	highp float f = (1.0 - length(fromCenter)) * sqrt(1.7 - d);\n"
2488														"	pos += 0.75 * f * fromCenter / (length(fromCenter) + 0.3);\n"
2489														"	gl_Position = vec4(pos, 0.0, 1.0);\n"
2490													  : DE_NULL) +
2491													 "}\n")
2492
2493			<< glu::FragmentSource					("#version 310 es\n"
2494													 "\n"
2495													 "layout (location = 0) out mediump vec4 o_color;\n"
2496													 "\n"
2497													 "void main (void)\n"
2498													 "{\n"
2499													 "	o_color = vec4(1.0);\n"
2500													 "}\n");
2501	}
2502};
2503
2504// Check that there are no obvious overlaps in the triangulation of a tessellated triangle or quad.
2505class BasicTriangleFillNonOverlapCase : public BasicVariousTessLevelsPosAttrCase
2506{
2507public:
2508	BasicTriangleFillNonOverlapCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing, const char* referenceImagePathPrefix)
2509		: BasicVariousTessLevelsPosAttrCase (context, name, description, primitiveType, spacing, referenceImagePathPrefix)
2510	{
2511		DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
2512	}
2513
2514protected:
2515	const glu::ProgramSources makeSources (TessPrimitiveType primitiveType, SpacingMode spacing, const char* vtxOutPosAttrName) const
2516	{
2517		return glu::ProgramSources()
2518			<< glu::VertexSource					("#version 310 es\n"
2519													 "\n"
2520													 "in highp vec2 in_v_position;\n"
2521													 "\n"
2522													 "out highp vec2 " + string(vtxOutPosAttrName) + ";\n"
2523													 "\n"
2524													 "void main (void)\n"
2525													 "{\n"
2526													 "	" + vtxOutPosAttrName + " = in_v_position;\n"
2527													 "}\n")
2528
2529			<< glu::TessellationEvaluationSource	("#version 310 es\n"
2530													 "#extension GL_EXT_tessellation_shader : require\n"
2531													 "\n"
2532													 + getTessellationEvaluationInLayoutString(primitiveType, spacing) +
2533													 "\n"
2534													 "in highp vec2 in_te_position[];\n"
2535													 "\n"
2536													 "out mediump vec4 in_f_color;\n"
2537													 "\n"
2538													 "uniform mediump float u_tessLevelInner0;\n"
2539													 "uniform mediump float u_tessLevelInner1;\n"
2540													 "\n"
2541													 "void main (void)\n"
2542													 "{\n"
2543													 + (primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
2544														"\n"
2545														"	highp vec2 corner0 = in_te_position[0];\n"
2546														"	highp vec2 corner1 = in_te_position[1];\n"
2547														"	highp vec2 corner2 = in_te_position[2];\n"
2548														"	highp vec2 pos =  corner0*gl_TessCoord.x + corner1*gl_TessCoord.y + corner2*gl_TessCoord.z;\n"
2549														"	gl_Position = vec4(pos, 0.0, 1.0);\n"
2550														"	highp int numConcentricTriangles = int(round(u_tessLevelInner0)) / 2 + 1;\n"
2551														"	highp float d = 3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z));\n"
2552														"	highp int phase = int(d*float(numConcentricTriangles)) % 3;\n"
2553														"	in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
2554														"	           : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
2555														"	           :              vec4(0.0, 0.0, 1.0, 1.0);\n"
2556													  : primitiveType == TESSPRIMITIVETYPE_QUADS ?
2557														"	highp vec2 corner0 = in_te_position[0];\n"
2558														"	highp vec2 corner1 = in_te_position[1];\n"
2559														"	highp vec2 corner2 = in_te_position[2];\n"
2560														"	highp vec2 corner3 = in_te_position[3];\n"
2561														"	highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
2562														"	               + (    gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
2563														"	               + (1.0-gl_TessCoord.x)*(    gl_TessCoord.y)*corner2\n"
2564														"	               + (    gl_TessCoord.x)*(    gl_TessCoord.y)*corner3;\n"
2565														"	gl_Position = vec4(pos, 0.0, 1.0);\n"
2566														"	highp int phaseX = int(round((0.5 - abs(gl_TessCoord.x-0.5)) * u_tessLevelInner0));\n"
2567														"	highp int phaseY = int(round((0.5 - abs(gl_TessCoord.y-0.5)) * u_tessLevelInner1));\n"
2568														"	highp int phase = min(phaseX, phaseY) % 3;\n"
2569														"	in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
2570														"	           : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
2571														"	           :              vec4(0.0, 0.0, 1.0, 1.0);\n"
2572													  : DE_NULL) +
2573													 "}\n")
2574
2575			<< glu::FragmentSource					("#version 310 es\n"
2576													 "\n"
2577													 "layout (location = 0) out mediump vec4 o_color;\n"
2578													 "\n"
2579													 "in mediump vec4 in_f_color;\n"
2580													 "\n"
2581													 "void main (void)\n"
2582													 "{\n"
2583													 "	o_color = in_f_color;\n"
2584													 "}\n");
2585	}
2586};
2587
2588// Basic isolines rendering case.
2589class IsolinesRenderCase : public BasicVariousTessLevelsPosAttrCase
2590{
2591public:
2592	IsolinesRenderCase (Context& context, const char* name, const char* description, SpacingMode spacing, const char* referenceImagePathPrefix)
2593		: BasicVariousTessLevelsPosAttrCase (context, name, description, TESSPRIMITIVETYPE_ISOLINES, spacing, referenceImagePathPrefix)
2594	{
2595	}
2596
2597protected:
2598	const glu::ProgramSources makeSources (TessPrimitiveType primitiveType, SpacingMode spacing, const char* vtxOutPosAttrName) const
2599	{
2600		DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_ISOLINES);
2601		DE_UNREF(primitiveType);
2602
2603		return glu::ProgramSources()
2604			<< glu::VertexSource					("#version 310 es\n"
2605													 "\n"
2606													 "in highp vec2 in_v_position;\n"
2607													 "\n"
2608													 "out highp vec2 " + string(vtxOutPosAttrName) + ";\n"
2609													 "\n"
2610													 "void main (void)\n"
2611													 "{\n"
2612													 "	" + vtxOutPosAttrName + " = in_v_position;\n"
2613													 "}\n")
2614
2615			<< glu::TessellationEvaluationSource	("#version 310 es\n"
2616													 "#extension GL_EXT_tessellation_shader : require\n"
2617													 "\n"
2618													 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_ISOLINES, spacing) +
2619													 "\n"
2620													 "in highp vec2 in_te_position[];\n"
2621													 "\n"
2622													 "out mediump vec4 in_f_color;\n"
2623													 "\n"
2624													 "uniform mediump float u_tessLevelOuter0;\n"
2625													 "uniform mediump float u_tessLevelOuter1;\n"
2626													 "\n"
2627													 "void main (void)\n"
2628													 "{\n"
2629													 "	highp vec2 corner0 = in_te_position[0];\n"
2630													 "	highp vec2 corner1 = in_te_position[1];\n"
2631													 "	highp vec2 corner2 = in_te_position[2];\n"
2632													 "	highp vec2 corner3 = in_te_position[3];\n"
2633													 "	highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
2634													 "	               + (    gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
2635													 "	               + (1.0-gl_TessCoord.x)*(    gl_TessCoord.y)*corner2\n"
2636													 "	               + (    gl_TessCoord.x)*(    gl_TessCoord.y)*corner3;\n"
2637													 "	pos.y += 0.15*sin(gl_TessCoord.x*10.0);\n"
2638													 "	gl_Position = vec4(pos, 0.0, 1.0);\n"
2639													 "	highp int phaseX = int(round(gl_TessCoord.x*u_tessLevelOuter1));\n"
2640													 "	highp int phaseY = int(round(gl_TessCoord.y*u_tessLevelOuter0));\n"
2641													 "	highp int phase = (phaseX + phaseY) % 3;\n"
2642													 "	in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
2643													 "	           : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
2644													 "	           :              vec4(0.0, 0.0, 1.0, 1.0);\n"
2645													 "}\n")
2646
2647			<< glu::FragmentSource					("#version 310 es\n"
2648													 "\n"
2649													 "layout (location = 0) out mediump vec4 o_color;\n"
2650													 "\n"
2651													 "in mediump vec4 in_f_color;\n"
2652													 "\n"
2653													 "void main (void)\n"
2654													 "{\n"
2655													 "	o_color = in_f_color;\n"
2656													 "}\n");
2657	}
2658};
2659
2660// Test the "cw" and "ccw" TES input layout qualifiers.
2661class WindingCase : public TestCase
2662{
2663public:
2664					WindingCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, Winding winding)
2665						: TestCase			(context, name, description)
2666						, m_primitiveType	(primitiveType)
2667						, m_winding			(winding)
2668					{
2669						DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
2670					}
2671
2672	void			init		(void);
2673	void			deinit		(void);
2674	IterateResult	iterate		(void);
2675
2676private:
2677	static const int				RENDER_SIZE = 64;
2678
2679	const TessPrimitiveType			m_primitiveType;
2680	const Winding					m_winding;
2681
2682	SharedPtr<const ShaderProgram>	m_program;
2683};
2684
2685void WindingCase::init (void)
2686{
2687	checkTessellationSupport(m_context);
2688	checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
2689
2690	m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
2691			<< glu::VertexSource					("#version 310 es\n"
2692													 "\n"
2693													 "void main (void)\n"
2694													 "{\n"
2695													 "}\n")
2696
2697			<< glu::TessellationControlSource		("#version 310 es\n"
2698													 "#extension GL_EXT_tessellation_shader : require\n"
2699													 "\n"
2700													 "layout (vertices = 1) out;\n"
2701													 "\n"
2702													 "void main (void)\n"
2703													 "{\n"
2704													 "	gl_TessLevelInner[0] = 5.0;\n"
2705													 "	gl_TessLevelInner[1] = 5.0;\n"
2706													 "\n"
2707													 "	gl_TessLevelOuter[0] = 5.0;\n"
2708													 "	gl_TessLevelOuter[1] = 5.0;\n"
2709													 "	gl_TessLevelOuter[2] = 5.0;\n"
2710													 "	gl_TessLevelOuter[3] = 5.0;\n"
2711													 "}\n")
2712
2713			<< glu::TessellationEvaluationSource	("#version 310 es\n"
2714													 "#extension GL_EXT_tessellation_shader : require\n"
2715													 "\n"
2716													 + getTessellationEvaluationInLayoutString(m_primitiveType, m_winding) +
2717													 "\n"
2718													 "void main (void)\n"
2719													 "{\n"
2720													 "	gl_Position = vec4(gl_TessCoord.xy*2.0 - 1.0, 0.0, 1.0);\n"
2721													 "}\n")
2722
2723			<< glu::FragmentSource					("#version 310 es\n"
2724													 "\n"
2725													 "layout (location = 0) out mediump vec4 o_color;\n"
2726													 "\n"
2727													 "void main (void)\n"
2728													 "{\n"
2729													 "	o_color = vec4(1.0);\n"
2730													 "}\n")));
2731
2732	m_testCtx.getLog() << *m_program;
2733	if (!m_program->isOk())
2734		TCU_FAIL("Program compilation failed");
2735}
2736
2737void WindingCase::deinit (void)
2738{
2739	m_program.clear();
2740}
2741
2742WindingCase::IterateResult WindingCase::iterate (void)
2743{
2744	TestLog&						log							= m_testCtx.getLog();
2745	const RenderContext&			renderCtx					= m_context.getRenderContext();
2746	const RandomViewport			viewport					(renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2747	const deUint32					programGL					= m_program->getProgram();
2748	const glw::Functions&			gl							= renderCtx.getFunctions();
2749	const glu::VertexArray			vao							(renderCtx);
2750
2751	bool							success						= true;
2752
2753	setViewport(gl, viewport);
2754	gl.clearColor(1.0f, 0.0f, 0.0f, 1.0f);
2755	gl.useProgram(programGL);
2756
2757	gl.patchParameteri(GL_PATCH_VERTICES, 1);
2758
2759	gl.enable(GL_CULL_FACE);
2760
2761	gl.bindVertexArray(*vao);
2762
2763	log << TestLog::Message << "Face culling enabled" << TestLog::EndMessage;
2764
2765	for (int frontFaceWinding = 0; frontFaceWinding < WINDING_LAST; frontFaceWinding++)
2766	{
2767		log << TestLog::Message << "Setting glFrontFace(" << (frontFaceWinding == WINDING_CW ? "GL_CW" : "GL_CCW") << ")" << TestLog::EndMessage;
2768
2769		gl.frontFace(frontFaceWinding == WINDING_CW ? GL_CW : GL_CCW);
2770
2771		gl.clear(GL_COLOR_BUFFER_BIT);
2772		gl.drawArrays(GL_PATCHES, 0, 1);
2773		GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
2774
2775		{
2776			const tcu::Surface rendered = getPixels(renderCtx, viewport);
2777			log << TestLog::Image("RenderedImage", "Rendered Image", rendered);
2778
2779			{
2780				const int totalNumPixels		= rendered.getWidth()*rendered.getHeight();
2781				const int badPixelTolerance		= m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 5*de::max(rendered.getWidth(), rendered.getHeight()) : 0;
2782
2783				int numWhitePixels	= 0;
2784				int numRedPixels	= 0;
2785				for (int y = 0; y < rendered.getHeight();	y++)
2786				for (int x = 0; x < rendered.getWidth();	x++)
2787				{
2788					numWhitePixels	+= rendered.getPixel(x, y) == tcu::RGBA::white	? 1 : 0;
2789					numRedPixels	+= rendered.getPixel(x, y) == tcu::RGBA::red	? 1 : 0;
2790				}
2791
2792				DE_ASSERT(numWhitePixels + numRedPixels <= totalNumPixels);
2793
2794				log << TestLog::Message << "Note: got " << numWhitePixels << " white and " << numRedPixels << " red pixels" << TestLog::EndMessage;
2795
2796				if (totalNumPixels - numWhitePixels - numRedPixels > badPixelTolerance)
2797				{
2798					log << TestLog::Message << "Failure: Got " << totalNumPixels - numWhitePixels - numRedPixels << " other than white or red pixels (maximum tolerance " << badPixelTolerance << ")" << TestLog::EndMessage;
2799					success = false;
2800					break;
2801				}
2802
2803				if ((Winding)frontFaceWinding == m_winding)
2804				{
2805					if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
2806					{
2807						if (de::abs(numWhitePixels - totalNumPixels/2) > badPixelTolerance)
2808						{
2809							log << TestLog::Message << "Failure: wrong number of white pixels; expected approximately " << totalNumPixels/2 << TestLog::EndMessage;
2810							success = false;
2811							break;
2812						}
2813					}
2814					else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
2815					{
2816						if (numWhitePixels != totalNumPixels)
2817						{
2818							log << TestLog::Message << "Failure: expected only white pixels (full-viewport quad)" << TestLog::EndMessage;
2819							success = false;
2820							break;
2821						}
2822					}
2823					else
2824						DE_ASSERT(false);
2825				}
2826				else
2827				{
2828					if (numWhitePixels != 0)
2829					{
2830						log << TestLog::Message << "Failure: expected only red pixels (everything culled)" << TestLog::EndMessage;
2831						success = false;
2832						break;
2833					}
2834				}
2835			}
2836		}
2837	}
2838
2839	m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image verification failed");
2840	return STOP;
2841}
2842
2843// Test potentially differing input and output patch sizes.
2844class PatchVertexCountCase : public TestCase
2845{
2846public:
2847	PatchVertexCountCase (Context& context, const char* name, const char* description, int inputPatchSize, int outputPatchSize, const char* referenceImagePath)
2848		: TestCase				(context, name, description)
2849		, m_inputPatchSize		(inputPatchSize)
2850		, m_outputPatchSize		(outputPatchSize)
2851		, m_referenceImagePath	(referenceImagePath)
2852	{
2853	}
2854
2855	void							init				(void);
2856	void							deinit				(void);
2857	IterateResult					iterate				(void);
2858
2859private:
2860	static const int				RENDER_SIZE = 256;
2861
2862	const int						m_inputPatchSize;
2863	const int						m_outputPatchSize;
2864
2865	const string					m_referenceImagePath;
2866
2867	SharedPtr<const ShaderProgram>	m_program;
2868};
2869
2870void PatchVertexCountCase::init (void)
2871{
2872	checkTessellationSupport(m_context);
2873	checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
2874
2875	const string inSizeStr		= de::toString(m_inputPatchSize);
2876	const string outSizeStr		= de::toString(m_outputPatchSize);
2877
2878	m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
2879			<< glu::VertexSource					("#version 310 es\n"
2880													 "\n"
2881													 "in highp float in_v_attr;\n"
2882													 "\n"
2883													 "out highp float in_tc_attr;\n"
2884													 "\n"
2885													 "void main (void)\n"
2886													 "{\n"
2887													 "	in_tc_attr = in_v_attr;\n"
2888													 "}\n")
2889
2890			<< glu::TessellationControlSource		("#version 310 es\n"
2891													 "#extension GL_EXT_tessellation_shader : require\n"
2892													 "\n"
2893													 "layout (vertices = " + outSizeStr + ") out;\n"
2894													 "\n"
2895													 "in highp float in_tc_attr[];\n"
2896													 "\n"
2897													 "out highp float in_te_attr[];\n"
2898													 "\n"
2899													 "void main (void)\n"
2900													 "{\n"
2901													 "	in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID*" + inSizeStr + "/" + outSizeStr + "];\n"
2902													 "\n"
2903													 "	gl_TessLevelInner[0] = 5.0;\n"
2904													 "	gl_TessLevelInner[1] = 5.0;\n"
2905													 "\n"
2906													"	gl_TessLevelOuter[0] = 5.0;\n"
2907													"	gl_TessLevelOuter[1] = 5.0;\n"
2908													"	gl_TessLevelOuter[2] = 5.0;\n"
2909													"	gl_TessLevelOuter[3] = 5.0;\n"
2910													 "}\n")
2911
2912			<< glu::TessellationEvaluationSource	("#version 310 es\n"
2913													 "#extension GL_EXT_tessellation_shader : require\n"
2914													 "\n"
2915													 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_QUADS) +
2916													 "\n"
2917													 "in highp float in_te_attr[];\n"
2918													 "\n"
2919													 "out mediump vec4 in_f_color;\n"
2920													 "\n"
2921													 "void main (void)\n"
2922													 "{\n"
2923													 "	highp float x = gl_TessCoord.x*2.0 - 1.0;\n"
2924													 "	highp float y = gl_TessCoord.y - in_te_attr[int(round(gl_TessCoord.x*float(" + outSizeStr + "-1)))];\n"
2925													 "	gl_Position = vec4(x, y, 0.0, 1.0);\n"
2926													 "	in_f_color = vec4(1.0);\n"
2927													 "}\n")
2928
2929			<< glu::FragmentSource					("#version 310 es\n"
2930													 "\n"
2931													 "layout (location = 0) out mediump vec4 o_color;\n"
2932													 "\n"
2933													 "in mediump vec4 in_f_color;\n"
2934													 "\n"
2935													 "void main (void)\n"
2936													 "{\n"
2937													 "	o_color = in_f_color;\n"
2938													 "}\n")));
2939
2940	m_testCtx.getLog() << *m_program;
2941	if (!m_program->isOk())
2942		TCU_FAIL("Program compilation failed");
2943}
2944
2945void PatchVertexCountCase::deinit (void)
2946{
2947	m_program.clear();
2948}
2949
2950PatchVertexCountCase::IterateResult PatchVertexCountCase::iterate (void)
2951{
2952	TestLog&					log						= m_testCtx.getLog();
2953	const RenderContext&		renderCtx				= m_context.getRenderContext();
2954	const RandomViewport		viewport				(renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2955	const deUint32				programGL				= m_program->getProgram();
2956	const glw::Functions&		gl						= renderCtx.getFunctions();
2957
2958	setViewport(gl, viewport);
2959	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
2960	gl.useProgram(programGL);
2961
2962	log << TestLog::Message << "Note: input patch size is " << m_inputPatchSize << ", output patch size is " << m_outputPatchSize << TestLog::EndMessage;
2963
2964	{
2965		vector<float> attributeData;
2966		attributeData.reserve(m_inputPatchSize);
2967
2968		for (int i = 0; i < m_inputPatchSize; i++)
2969		{
2970			const float f = (float)i / (float)(m_inputPatchSize-1);
2971			attributeData.push_back(f*f);
2972		}
2973
2974		gl.patchParameteri(GL_PATCH_VERTICES, m_inputPatchSize);
2975		gl.clear(GL_COLOR_BUFFER_BIT);
2976
2977		const glu::VertexArrayBinding attrBindings[] =
2978		{
2979			glu::va::Float("in_v_attr", 1, (int)attributeData.size(), 0, &attributeData[0])
2980		};
2981
2982		glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
2983			glu::pr::Patches(m_inputPatchSize));
2984		GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
2985	}
2986
2987	{
2988		const tcu::Surface			rendered	= getPixels(renderCtx, viewport);
2989		const tcu::TextureLevel		reference	= getPNG(m_testCtx.getArchive(), m_referenceImagePath);
2990		const bool					success		= tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
2991
2992		m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image comparison failed");
2993		return STOP;
2994	}
2995}
2996
2997// Test per-patch inputs/outputs.
2998class PerPatchDataCase : public TestCase
2999{
3000public:
3001	enum CaseType
3002	{
3003		CASETYPE_PRIMITIVE_ID_TCS = 0,
3004		CASETYPE_PRIMITIVE_ID_TES,
3005		CASETYPE_PATCH_VERTICES_IN_TCS,
3006		CASETYPE_PATCH_VERTICES_IN_TES,
3007		CASETYPE_TESS_LEVEL_INNER0_TES,
3008		CASETYPE_TESS_LEVEL_INNER1_TES,
3009		CASETYPE_TESS_LEVEL_OUTER0_TES,
3010		CASETYPE_TESS_LEVEL_OUTER1_TES,
3011		CASETYPE_TESS_LEVEL_OUTER2_TES,
3012		CASETYPE_TESS_LEVEL_OUTER3_TES,
3013
3014		CASETYPE_LAST
3015	};
3016
3017	PerPatchDataCase (Context& context, const char* name, const char* description, CaseType caseType, const char* referenceImagePath)
3018		: TestCase				(context, name, description)
3019		, m_caseType			(caseType)
3020		, m_referenceImagePath	(caseTypeUsesRefImageFromFile(caseType) ? referenceImagePath : "")
3021	{
3022		DE_ASSERT(caseTypeUsesRefImageFromFile(caseType) == (referenceImagePath != DE_NULL));
3023	}
3024
3025	void							init							(void);
3026	void							deinit							(void);
3027	IterateResult					iterate							(void);
3028
3029	static const char*				getCaseTypeName					(CaseType);
3030	static const char*				getCaseTypeDescription			(CaseType);
3031	static bool						caseTypeUsesRefImageFromFile	(CaseType);
3032
3033private:
3034	static const int				RENDER_SIZE = 256;
3035	static const int				INPUT_PATCH_SIZE;
3036	static const int				OUTPUT_PATCH_SIZE;
3037
3038	const CaseType					m_caseType;
3039	const string					m_referenceImagePath;
3040
3041	SharedPtr<const ShaderProgram>	m_program;
3042};
3043
3044const int PerPatchDataCase::INPUT_PATCH_SIZE	= 10;
3045const int PerPatchDataCase::OUTPUT_PATCH_SIZE	= 5;
3046
3047const char* PerPatchDataCase::getCaseTypeName (CaseType type)
3048{
3049	switch (type)
3050	{
3051		case CASETYPE_PRIMITIVE_ID_TCS:			return "primitive_id_tcs";
3052		case CASETYPE_PRIMITIVE_ID_TES:			return "primitive_id_tes";
3053		case CASETYPE_PATCH_VERTICES_IN_TCS:	return "patch_vertices_in_tcs";
3054		case CASETYPE_PATCH_VERTICES_IN_TES:	return "patch_vertices_in_tes";
3055		case CASETYPE_TESS_LEVEL_INNER0_TES:	return "tess_level_inner_0_tes";
3056		case CASETYPE_TESS_LEVEL_INNER1_TES:	return "tess_level_inner_1_tes";
3057		case CASETYPE_TESS_LEVEL_OUTER0_TES:	return "tess_level_outer_0_tes";
3058		case CASETYPE_TESS_LEVEL_OUTER1_TES:	return "tess_level_outer_1_tes";
3059		case CASETYPE_TESS_LEVEL_OUTER2_TES:	return "tess_level_outer_2_tes";
3060		case CASETYPE_TESS_LEVEL_OUTER3_TES:	return "tess_level_outer_3_tes";
3061		default:
3062			DE_ASSERT(false);
3063			return DE_NULL;
3064	}
3065}
3066
3067const char* PerPatchDataCase::getCaseTypeDescription (CaseType type)
3068{
3069	switch (type)
3070	{
3071		case CASETYPE_PRIMITIVE_ID_TCS:			return "Read gl_PrimitiveID in TCS and pass it as patch output to TES";
3072		case CASETYPE_PRIMITIVE_ID_TES:			return "Read gl_PrimitiveID in TES";
3073		case CASETYPE_PATCH_VERTICES_IN_TCS:	return "Read gl_PatchVerticesIn in TCS and pass it as patch output to TES";
3074		case CASETYPE_PATCH_VERTICES_IN_TES:	return "Read gl_PatchVerticesIn in TES";
3075		case CASETYPE_TESS_LEVEL_INNER0_TES:	return "Read gl_TessLevelInner[0] in TES";
3076		case CASETYPE_TESS_LEVEL_INNER1_TES:	return "Read gl_TessLevelInner[1] in TES";
3077		case CASETYPE_TESS_LEVEL_OUTER0_TES:	return "Read gl_TessLevelOuter[0] in TES";
3078		case CASETYPE_TESS_LEVEL_OUTER1_TES:	return "Read gl_TessLevelOuter[1] in TES";
3079		case CASETYPE_TESS_LEVEL_OUTER2_TES:	return "Read gl_TessLevelOuter[2] in TES";
3080		case CASETYPE_TESS_LEVEL_OUTER3_TES:	return "Read gl_TessLevelOuter[3] in TES";
3081		default:
3082			DE_ASSERT(false);
3083			return DE_NULL;
3084	}
3085}
3086
3087bool PerPatchDataCase::caseTypeUsesRefImageFromFile (CaseType type)
3088{
3089	switch (type)
3090	{
3091		case CASETYPE_PRIMITIVE_ID_TCS:
3092		case CASETYPE_PRIMITIVE_ID_TES:
3093			return true;
3094
3095		default:
3096			return false;
3097	}
3098}
3099
3100void PerPatchDataCase::init (void)
3101{
3102	checkTessellationSupport(m_context);
3103	checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
3104
3105	DE_ASSERT(OUTPUT_PATCH_SIZE < INPUT_PATCH_SIZE);
3106
3107	const string inSizeStr		= de::toString(INPUT_PATCH_SIZE);
3108	const string outSizeStr		= de::toString(OUTPUT_PATCH_SIZE);
3109
3110	m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
3111			<< glu::VertexSource					("#version 310 es\n"
3112													 "\n"
3113													 "in highp float in_v_attr;\n"
3114													 "\n"
3115													 "out highp float in_tc_attr;\n"
3116													 "\n"
3117													 "void main (void)\n"
3118													 "{\n"
3119													 "	in_tc_attr = in_v_attr;\n"
3120													 "}\n")
3121
3122			<< glu::TessellationControlSource		("#version 310 es\n"
3123													 "#extension GL_EXT_tessellation_shader : require\n"
3124													 "\n"
3125													 "layout (vertices = " + outSizeStr + ") out;\n"
3126													 "\n"
3127													 "in highp float in_tc_attr[];\n"
3128													 "\n"
3129													 "out highp float in_te_attr[];\n"
3130													 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS			? "patch out mediump int in_te_primitiveIDFromTCS;\n"
3131													  : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS	? "patch out mediump int in_te_patchVerticesInFromTCS;\n"
3132													  : "") +
3133													 "\n"
3134													 "void main (void)\n"
3135													 "{\n"
3136													 "	in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID];\n"
3137													 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS			? "\tin_te_primitiveIDFromTCS = gl_PrimitiveID;\n"
3138													  : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS	? "\tin_te_patchVerticesInFromTCS = gl_PatchVerticesIn;\n"
3139													  : "") +
3140													 "\n"
3141													 "	gl_TessLevelInner[0] = 9.0;\n"
3142													 "	gl_TessLevelInner[1] = 8.0;\n"
3143													 "\n"
3144													"	gl_TessLevelOuter[0] = 7.0;\n"
3145													"	gl_TessLevelOuter[1] = 6.0;\n"
3146													"	gl_TessLevelOuter[2] = 5.0;\n"
3147													"	gl_TessLevelOuter[3] = 4.0;\n"
3148													 "}\n")
3149
3150			<< glu::TessellationEvaluationSource	("#version 310 es\n"
3151													 "#extension GL_EXT_tessellation_shader : require\n"
3152													 "\n"
3153													 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_QUADS) +
3154													 "\n"
3155													 "in highp float in_te_attr[];\n"
3156													 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS			? "patch in mediump int in_te_primitiveIDFromTCS;\n"
3157													  : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS	? "patch in mediump int in_te_patchVerticesInFromTCS;\n"
3158													  : string()) +
3159													 "\n"
3160													 "out mediump vec4 in_f_color;\n"
3161													 "\n"
3162													 "uniform highp float u_xScale;\n"
3163													 "\n"
3164													 "void main (void)\n"
3165													 "{\n"
3166													 "	highp float x = (gl_TessCoord.x*u_xScale + in_te_attr[0]) * 2.0 - 1.0;\n"
3167													 "	highp float y = gl_TessCoord.y*2.0 - 1.0;\n"
3168													 "	gl_Position = vec4(x, y, 0.0, 1.0);\n"
3169													 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS			? "\tbool ok = in_te_primitiveIDFromTCS == 3;\n"
3170													  : m_caseType == CASETYPE_PRIMITIVE_ID_TES			? "\tbool ok = gl_PrimitiveID == 3;\n"
3171													  : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS	? "\tbool ok = in_te_patchVerticesInFromTCS == " + inSizeStr + ";\n"
3172													  : m_caseType == CASETYPE_PATCH_VERTICES_IN_TES	? "\tbool ok = gl_PatchVerticesIn == " + outSizeStr + ";\n"
3173													  : m_caseType == CASETYPE_TESS_LEVEL_INNER0_TES	? "\tbool ok = abs(gl_TessLevelInner[0] - 9.0) < 0.1f;\n"
3174													  : m_caseType == CASETYPE_TESS_LEVEL_INNER1_TES	? "\tbool ok = abs(gl_TessLevelInner[1] - 8.0) < 0.1f;\n"
3175													  : m_caseType == CASETYPE_TESS_LEVEL_OUTER0_TES	? "\tbool ok = abs(gl_TessLevelOuter[0] - 7.0) < 0.1f;\n"
3176													  : m_caseType == CASETYPE_TESS_LEVEL_OUTER1_TES	? "\tbool ok = abs(gl_TessLevelOuter[1] - 6.0) < 0.1f;\n"
3177													  : m_caseType == CASETYPE_TESS_LEVEL_OUTER2_TES	? "\tbool ok = abs(gl_TessLevelOuter[2] - 5.0) < 0.1f;\n"
3178													  : m_caseType == CASETYPE_TESS_LEVEL_OUTER3_TES	? "\tbool ok = abs(gl_TessLevelOuter[3] - 4.0) < 0.1f;\n"
3179													  : DE_NULL) +
3180													  "	in_f_color = ok ? vec4(1.0) : vec4(vec3(0.0), 1.0);\n"
3181													 "}\n")
3182
3183			<< glu::FragmentSource					("#version 310 es\n"
3184													 "\n"
3185													 "layout (location = 0) out mediump vec4 o_color;\n"
3186													 "\n"
3187													 "in mediump vec4 in_f_color;\n"
3188													 "\n"
3189													 "void main (void)\n"
3190													 "{\n"
3191													 "	o_color = in_f_color;\n"
3192													 "}\n")));
3193
3194	m_testCtx.getLog() << *m_program;
3195	if (!m_program->isOk())
3196		TCU_FAIL("Program compilation failed");
3197}
3198
3199void PerPatchDataCase::deinit (void)
3200{
3201	m_program.clear();
3202}
3203
3204PerPatchDataCase::IterateResult PerPatchDataCase::iterate (void)
3205{
3206	TestLog&					log						= m_testCtx.getLog();
3207	const RenderContext&		renderCtx				= m_context.getRenderContext();
3208	const RandomViewport		viewport				(renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
3209	const deUint32				programGL				= m_program->getProgram();
3210	const glw::Functions&		gl						= renderCtx.getFunctions();
3211
3212	setViewport(gl, viewport);
3213	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3214	gl.useProgram(programGL);
3215
3216	log << TestLog::Message << "Note: input patch size is " << INPUT_PATCH_SIZE << ", output patch size is " << OUTPUT_PATCH_SIZE << TestLog::EndMessage;
3217
3218	{
3219		const int numPrimitives = m_caseType == CASETYPE_PRIMITIVE_ID_TCS || m_caseType == CASETYPE_PRIMITIVE_ID_TES ? 8 : 1;
3220
3221		vector<float> attributeData;
3222		attributeData.reserve(numPrimitives*INPUT_PATCH_SIZE);
3223
3224		for (int i = 0; i < numPrimitives; i++)
3225		{
3226			attributeData.push_back((float)i / (float)numPrimitives);
3227			for (int j = 0; j < INPUT_PATCH_SIZE-1; j++)
3228				attributeData.push_back(0.0f);
3229		}
3230
3231		gl.patchParameteri(GL_PATCH_VERTICES, INPUT_PATCH_SIZE);
3232		gl.clear(GL_COLOR_BUFFER_BIT);
3233
3234		gl.uniform1f(gl.getUniformLocation(programGL, "u_xScale"), 1.0f / (float)numPrimitives);
3235
3236		const glu::VertexArrayBinding attrBindings[] =
3237		{
3238			glu::va::Float("in_v_attr", 1, (int)attributeData.size(), 0, &attributeData[0])
3239		};
3240
3241		glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
3242			glu::pr::Patches(numPrimitives*INPUT_PATCH_SIZE));
3243		GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
3244	}
3245
3246	{
3247		const tcu::Surface rendered = getPixels(renderCtx, viewport);
3248
3249		if (m_caseType == CASETYPE_PRIMITIVE_ID_TCS || m_caseType == CASETYPE_PRIMITIVE_ID_TES)
3250		{
3251			DE_ASSERT(caseTypeUsesRefImageFromFile(m_caseType));
3252
3253			const tcu::TextureLevel		reference	= getPNG(m_testCtx.getArchive(), m_referenceImagePath);
3254			const bool					success		= tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
3255
3256			m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image comparison failed");
3257			return STOP;
3258		}
3259		else
3260		{
3261			DE_ASSERT(!caseTypeUsesRefImageFromFile(m_caseType));
3262
3263			log << TestLog::Image("RenderedImage", "Rendered Image", rendered);
3264
3265			for (int y = 0; y < rendered.getHeight();	y++)
3266			for (int x = 0; x < rendered.getWidth();	x++)
3267			{
3268				if (rendered.getPixel(x, y) != tcu::RGBA::white)
3269				{
3270					log << TestLog::Message << "Failure: expected all white pixels" << TestLog::EndMessage;
3271					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
3272					return STOP;
3273				}
3274			}
3275
3276			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3277			return STOP;
3278		}
3279	}
3280}
3281
3282// Basic barrier() usage in TCS.
3283class BarrierCase : public TestCase
3284{
3285public:
3286	BarrierCase (Context& context, const char* name, const char* description, const char* referenceImagePath)
3287		: TestCase				(context, name, description)
3288		, m_referenceImagePath	(referenceImagePath)
3289	{
3290	}
3291
3292	void							init		(void);
3293	void							deinit		(void);
3294	IterateResult					iterate		(void);
3295
3296private:
3297	static const int				RENDER_SIZE = 256;
3298	static const int				NUM_VERTICES;
3299
3300	const string					m_referenceImagePath;
3301
3302	SharedPtr<const ShaderProgram>	m_program;
3303};
3304
3305const int BarrierCase::NUM_VERTICES = 32;
3306
3307void BarrierCase::init (void)
3308{
3309	checkTessellationSupport(m_context);
3310	checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
3311
3312	const string numVertsStr = de::toString(NUM_VERTICES);
3313
3314	m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
3315			<< glu::VertexSource					("#version 310 es\n"
3316													 "\n"
3317													 "in highp float in_v_attr;\n"
3318													 "\n"
3319													 "out highp float in_tc_attr;\n"
3320													 "\n"
3321													 "void main (void)\n"
3322													 "{\n"
3323													 "	in_tc_attr = in_v_attr;\n"
3324													 "}\n")
3325
3326			<< glu::TessellationControlSource		("#version 310 es\n"
3327													 "#extension GL_EXT_tessellation_shader : require\n"
3328													 "\n"
3329													 "layout (vertices = " + numVertsStr + ") out;\n"
3330													 "\n"
3331													 "in highp float in_tc_attr[];\n"
3332													 "\n"
3333													 "out highp float in_te_attr[];\n"
3334													 "patch out highp float in_te_patchAttr;\n"
3335													 "\n"
3336													 "void main (void)\n"
3337													 "{\n"
3338													 "	in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID];\n"
3339													 "	in_te_patchAttr = 0.0f;\n"
3340													 "	barrier();\n"
3341													 "	if (gl_InvocationID == 5)\n"
3342													 "		in_te_patchAttr = float(gl_InvocationID)*0.1;\n"
3343													 "	barrier();\n"
3344													 "	highp float temp = in_te_patchAttr + in_te_attr[gl_InvocationID];\n"
3345													 "	barrier();\n"
3346													 "	if (gl_InvocationID == " + numVertsStr + "-1)\n"
3347													 "		in_te_patchAttr = float(gl_InvocationID);\n"
3348													 "	barrier();\n"
3349													 "	in_te_attr[gl_InvocationID] = temp;\n"
3350													 "	barrier();\n"
3351													 "	temp = temp + in_te_attr[(gl_InvocationID+1) % " + numVertsStr + "];\n"
3352													 "	barrier();\n"
3353													 "	in_te_attr[gl_InvocationID] = 0.25*temp;\n"
3354													 "\n"
3355													 "	gl_TessLevelInner[0] = 32.0;\n"
3356													 "	gl_TessLevelInner[1] = 32.0;\n"
3357													 "\n"
3358													 "	gl_TessLevelOuter[0] = 32.0;\n"
3359													 "	gl_TessLevelOuter[1] = 32.0;\n"
3360													 "	gl_TessLevelOuter[2] = 32.0;\n"
3361													 "	gl_TessLevelOuter[3] = 32.0;\n"
3362													 "}\n")
3363
3364			<< glu::TessellationEvaluationSource	("#version 310 es\n"
3365													 "#extension GL_EXT_tessellation_shader : require\n"
3366													 "\n"
3367													 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_QUADS) +
3368													 "\n"
3369													 "in highp float in_te_attr[];\n"
3370													 "patch in highp float in_te_patchAttr;\n"
3371													 "\n"
3372													 "out highp float in_f_blue;\n"
3373													 "\n"
3374													 "void main (void)\n"
3375													 "{\n"
3376													 "	highp float x = gl_TessCoord.x*2.0 - 1.0;\n"
3377													 "	highp float y = gl_TessCoord.y - in_te_attr[int(round(gl_TessCoord.x*float(" + numVertsStr + "-1)))];\n"
3378													 "	gl_Position = vec4(x, y, 0.0, 1.0);\n"
3379													 "	in_f_blue = abs(in_te_patchAttr - float(" + numVertsStr + "-1));\n"
3380													 "}\n")
3381
3382			<< glu::FragmentSource					("#version 310 es\n"
3383													 "\n"
3384													 "layout (location = 0) out mediump vec4 o_color;\n"
3385													 "\n"
3386													 "in highp float in_f_blue;\n"
3387													 "\n"
3388													 "void main (void)\n"
3389													 "{\n"
3390													 "	o_color = vec4(1.0, 0.0, in_f_blue, 1.0);\n"
3391													 "}\n")));
3392
3393	m_testCtx.getLog() << *m_program;
3394	if (!m_program->isOk())
3395		TCU_FAIL("Program compilation failed");
3396}
3397
3398void BarrierCase::deinit (void)
3399{
3400	m_program.clear();
3401}
3402
3403BarrierCase::IterateResult BarrierCase::iterate (void)
3404{
3405	TestLog&					log						= m_testCtx.getLog();
3406	const RenderContext&		renderCtx				= m_context.getRenderContext();
3407	const RandomViewport		viewport				(renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
3408	const deUint32				programGL				= m_program->getProgram();
3409	const glw::Functions&		gl						= renderCtx.getFunctions();
3410
3411	setViewport(gl, viewport);
3412	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3413	gl.useProgram(programGL);
3414
3415	{
3416		vector<float> attributeData(NUM_VERTICES);
3417
3418		for (int i = 0; i < NUM_VERTICES; i++)
3419			attributeData[i] = (float)i / (float)(NUM_VERTICES-1);
3420
3421		gl.patchParameteri(GL_PATCH_VERTICES, NUM_VERTICES);
3422		gl.clear(GL_COLOR_BUFFER_BIT);
3423
3424		const glu::VertexArrayBinding attrBindings[] =
3425		{
3426			glu::va::Float("in_v_attr", 1, (int)attributeData.size(), 0, &attributeData[0])
3427		};
3428
3429		glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
3430			glu::pr::Patches(NUM_VERTICES));
3431		GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
3432	}
3433
3434	{
3435		const tcu::Surface			rendered	= getPixels(renderCtx, viewport);
3436		const tcu::TextureLevel		reference	= getPNG(m_testCtx.getArchive(), m_referenceImagePath);
3437		const bool					success		= tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
3438
3439		m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image comparison failed");
3440		return STOP;
3441	}
3442}
3443
3444/*--------------------------------------------------------------------*//*!
3445 * \brief Base class for testing invariance of entire primitive set
3446 *
3447 * Draws two patches with identical tessellation levels and compares the
3448 * results. Repeats the same with other programs that are only different
3449 * in irrelevant ways; compares the results between these two programs.
3450 * Also potentially compares to results produced by different tessellation
3451 * levels (see e.g. invariance rule #6).
3452 * Furthermore, repeats the above with multiple different tessellation
3453 * value sets.
3454 *
3455 * The manner of primitive set comparison is defined by subclass. E.g.
3456 * case for invariance rule #1 tests that same vertices come out, in same
3457 * order; rule #5 only requires that the same triangles are output, but
3458 * not necessarily in the same order.
3459 *//*--------------------------------------------------------------------*/
3460class PrimitiveSetInvarianceCase : public TestCase
3461{
3462public:
3463	enum WindingUsage
3464	{
3465		WINDINGUSAGE_CCW = 0,
3466		WINDINGUSAGE_CW,
3467		WINDINGUSAGE_VARY,
3468
3469		WINDINGUSAGE_LAST
3470	};
3471
3472	PrimitiveSetInvarianceCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, bool usePointMode, WindingUsage windingUsage)
3473		: TestCase			(context, name, description)
3474		, m_primitiveType	(primType)
3475		, m_spacing			(spacing)
3476		, m_usePointMode	(usePointMode)
3477		, m_windingUsage	(windingUsage)
3478	{
3479	}
3480
3481	void									init				(void);
3482	void									deinit				(void);
3483	IterateResult							iterate				(void);
3484
3485protected:
3486	struct TessLevels
3487	{
3488		float inner[2];
3489		float outer[4];
3490		string description (void) const { return tessellationLevelsString(&inner[0], &outer[0]); }
3491	};
3492	struct LevelCase
3493	{
3494		vector<TessLevels>	levels;
3495		int					mem; //!< Subclass-defined arbitrary piece of data, for type of the levelcase, if needed. Passed to compare().
3496		LevelCase (const TessLevels& lev) : levels(vector<TessLevels>(1, lev)), mem(0) {}
3497		LevelCase (void) : mem(0) {}
3498	};
3499
3500	virtual vector<LevelCase>	genTessLevelCases	(void) const;
3501	virtual bool				compare				(const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int levelCaseMem) const = 0;
3502
3503	const TessPrimitiveType		m_primitiveType;
3504
3505private:
3506	struct Program
3507	{
3508		Winding							winding;
3509		SharedPtr<const ShaderProgram>	program;
3510
3511				Program			(Winding w, const SharedPtr<const ShaderProgram>& prog) : winding(w), program(prog) {}
3512
3513		string	description		(void) const { return string() + "winding mode " + getWindingShaderName(winding); };
3514	};
3515
3516	static const int			RENDER_SIZE = 16;
3517
3518	const SpacingMode			m_spacing;
3519	const bool					m_usePointMode;
3520	const WindingUsage			m_windingUsage;
3521
3522	vector<Program>				m_programs;
3523};
3524
3525vector<PrimitiveSetInvarianceCase::LevelCase> PrimitiveSetInvarianceCase::genTessLevelCases (void) const
3526{
3527	static const TessLevels basicTessLevelCases[] =
3528	{
3529		{ { 1.0f,	1.0f	},	{ 1.0f,		1.0f,	1.0f,	1.0f	} },
3530		{ { 63.0f,	24.0f	},	{ 15.0f,	42.0f,	10.0f,	12.0f	} },
3531		{ { 3.0f,	2.0f	},	{ 6.0f,		8.0f,	7.0f,	9.0f	} },
3532		{ { 4.0f,	6.0f	},	{ 2.0f,		3.0f,	1.0f,	4.0f	} },
3533		{ { 2.0f,	2.0f	},	{ 6.0f,		8.0f,	7.0f,	9.0f	} },
3534		{ { 5.0f,	6.0f	},	{ 1.0f,		1.0f,	1.0f,	1.0f	} },
3535		{ { 1.0f,	6.0f	},	{ 2.0f,		3.0f,	1.0f,	4.0f	} },
3536		{ { 5.0f,	1.0f	},	{ 2.0f,		3.0f,	1.0f,	4.0f	} },
3537		{ { 5.2f,	1.6f	},	{ 2.9f,		3.4f,	1.5f,	4.1f	} }
3538	};
3539
3540	vector<LevelCase> result;
3541	for (int i = 0; i < DE_LENGTH_OF_ARRAY(basicTessLevelCases); i++)
3542		result.push_back(LevelCase(basicTessLevelCases[i]));
3543
3544	{
3545		de::Random rnd(123);
3546		for (int i = 0; i < 10; i++)
3547		{
3548			TessLevels levels;
3549			for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.inner); j++)
3550				levels.inner[j] = rnd.getFloat(1.0f, 16.0f);
3551			for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.outer); j++)
3552				levels.outer[j] = rnd.getFloat(1.0f, 16.0f);
3553			result.push_back(LevelCase(levels));
3554		}
3555	}
3556
3557	return result;
3558}
3559
3560void PrimitiveSetInvarianceCase::init (void)
3561{
3562	const int			numDifferentConstantExprCases = 2;
3563	vector<Winding>		windings;
3564	switch (m_windingUsage)
3565	{
3566		case WINDINGUSAGE_CCW:		windings.push_back(WINDING_CCW); break;
3567		case WINDINGUSAGE_CW:		windings.push_back(WINDING_CW); break;
3568		case WINDINGUSAGE_VARY:		windings.push_back(WINDING_CCW);
3569									windings.push_back(WINDING_CW); break;
3570		default: DE_ASSERT(false);
3571	}
3572
3573	checkTessellationSupport(m_context);
3574	checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
3575
3576	for (int constantExprCaseNdx = 0; constantExprCaseNdx < numDifferentConstantExprCases; constantExprCaseNdx++)
3577	{
3578		for (int windingCaseNdx = 0; windingCaseNdx < (int)windings.size(); windingCaseNdx++)
3579		{
3580			const string	floatLit01 = de::floatToString(10.0f / (float)(constantExprCaseNdx + 10), 2);
3581			const int		programNdx = (int)m_programs.size();
3582
3583			m_programs.push_back(Program(windings[windingCaseNdx],
3584										 SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
3585					<< glu::VertexSource					("#version 310 es\n"
3586															 "\n"
3587															 "in highp float in_v_attr;\n"
3588															 "out highp float in_tc_attr;\n"
3589															 "\n"
3590															 "void main (void)\n"
3591															 "{\n"
3592															 "	in_tc_attr = in_v_attr;\n"
3593															 "}\n")
3594
3595					<< glu::TessellationControlSource		("#version 310 es\n"
3596															 "#extension GL_EXT_tessellation_shader : require\n"
3597															 "\n"
3598															 "layout (vertices = " + de::toString(constantExprCaseNdx+1) + ") out;\n"
3599															 "\n"
3600															 "in highp float in_tc_attr[];\n"
3601															 "\n"
3602															 "patch out highp float in_te_positionOffset;\n"
3603															 "\n"
3604															 "void main (void)\n"
3605															 "{\n"
3606															 "	in_te_positionOffset = in_tc_attr[6];\n"
3607															 "\n"
3608															 "	gl_TessLevelInner[0] = in_tc_attr[0];\n"
3609															 "	gl_TessLevelInner[1] = in_tc_attr[1];\n"
3610															 "\n"
3611															 "	gl_TessLevelOuter[0] = in_tc_attr[2];\n"
3612															 "	gl_TessLevelOuter[1] = in_tc_attr[3];\n"
3613															 "	gl_TessLevelOuter[2] = in_tc_attr[4];\n"
3614															 "	gl_TessLevelOuter[3] = in_tc_attr[5];\n"
3615															 "}\n")
3616
3617					<< glu::TessellationEvaluationSource	("#version 310 es\n"
3618															 "#extension GL_EXT_tessellation_shader : require\n"
3619															 "\n"
3620															 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, windings[windingCaseNdx], m_usePointMode) +
3621															 "\n"
3622															 "patch in highp float in_te_positionOffset;\n"
3623															 "\n"
3624															 "out highp vec4 in_f_color;\n"
3625															 "invariant out highp vec3 out_te_tessCoord;\n"
3626															 "\n"
3627															 "void main (void)\n"
3628															 "{\n"
3629															 "	gl_Position = vec4(gl_TessCoord.xy*" + floatLit01 + " - in_te_positionOffset + float(gl_PrimitiveID)*0.1, 0.0, 1.0);\n"
3630															 "	in_f_color = vec4(" + floatLit01 + ");\n"
3631															 "	out_te_tessCoord = gl_TessCoord;\n"
3632															 "}\n")
3633
3634					<< glu::FragmentSource					("#version 310 es\n"
3635															 "\n"
3636															 "layout (location = 0) out mediump vec4 o_color;\n"
3637															 "\n"
3638															 "in highp vec4 in_f_color;\n"
3639															 "\n"
3640															 "void main (void)\n"
3641															 "{\n"
3642															 "	o_color = in_f_color;\n"
3643															 "}\n")
3644
3645					<< glu::TransformFeedbackVarying		("out_te_tessCoord")
3646					<< glu::TransformFeedbackMode			(GL_INTERLEAVED_ATTRIBS)))));
3647
3648			{
3649				const tcu::ScopedLogSection section(m_testCtx.getLog(), "Program" + de::toString(programNdx), "Program " + de::toString(programNdx));
3650
3651				if (programNdx == 0 || !m_programs.back().program->isOk())
3652					m_testCtx.getLog() << *m_programs.back().program;
3653
3654				if (!m_programs.back().program->isOk())
3655					TCU_FAIL("Program compilation failed");
3656
3657				if (programNdx > 0)
3658					m_testCtx.getLog() << TestLog::Message << "Note: program " << programNdx << " is similar to above, except some constants are different, and: " << m_programs.back().description() << TestLog::EndMessage;
3659			}
3660		}
3661	}
3662}
3663
3664void PrimitiveSetInvarianceCase::deinit (void)
3665{
3666	m_programs.clear();
3667}
3668
3669PrimitiveSetInvarianceCase::IterateResult PrimitiveSetInvarianceCase::iterate (void)
3670{
3671	typedef TransformFeedbackHandler<Vec3> TFHandler;
3672
3673	TestLog&					log					= m_testCtx.getLog();
3674	const RenderContext&		renderCtx			= m_context.getRenderContext();
3675	const RandomViewport		viewport			(renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
3676	const glw::Functions&		gl					= renderCtx.getFunctions();
3677	const vector<LevelCase>		tessLevelCases		= genTessLevelCases();
3678	vector<vector<int> >		primitiveCounts;
3679	int							maxNumPrimitives	= -1;
3680
3681	for (int caseNdx = 0; caseNdx < (int)tessLevelCases.size(); caseNdx++)
3682	{
3683		primitiveCounts.push_back(vector<int>());
3684		for (int i = 0; i < (int)tessLevelCases[caseNdx].levels.size(); i++)
3685		{
3686			const int primCount = referencePrimitiveCount(m_primitiveType, m_spacing, m_usePointMode,
3687														  &tessLevelCases[caseNdx].levels[i].inner[0], &tessLevelCases[caseNdx].levels[i].outer[0]);
3688			primitiveCounts.back().push_back(primCount);
3689			maxNumPrimitives = de::max(maxNumPrimitives, primCount);
3690		}
3691	}
3692
3693	const deUint32				primitiveTypeGL		= outputPrimitiveTypeGL(m_primitiveType, m_usePointMode);
3694	const TFHandler				transformFeedback	(m_context.getRenderContext(), 2*maxNumPrimitives*numVerticesPerPrimitive(primitiveTypeGL));
3695
3696	setViewport(gl, viewport);
3697	gl.patchParameteri(GL_PATCH_VERTICES, 7);
3698
3699	for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < (int)tessLevelCases.size(); tessLevelCaseNdx++)
3700	{
3701		const LevelCase&	levelCase = tessLevelCases[tessLevelCaseNdx];
3702		vector<Vec3>		firstPrimVertices;
3703
3704		{
3705			string tessLevelsStr;
3706			for (int i = 0; i < (int)levelCase.levels.size(); i++)
3707				tessLevelsStr += (levelCase.levels.size() > 1 ? "\n" : "") + levelCase.levels[i].description();
3708			log << TestLog::Message << "Tessellation level sets: " << tessLevelsStr << TestLog::EndMessage;
3709		}
3710
3711		for (int subTessLevelCaseNdx = 0; subTessLevelCaseNdx < (int)levelCase.levels.size(); subTessLevelCaseNdx++)
3712		{
3713			const TessLevels&				tessLevels		= levelCase.levels[subTessLevelCaseNdx];
3714			const float						(&inner)[2]		= tessLevels.inner;
3715			const float						(&outer)[4]		= tessLevels.outer;
3716			const float						attribute[2*7]	= { inner[0], inner[1], outer[0], outer[1], outer[2], outer[3], 0.0f,
3717																inner[0], inner[1], outer[0], outer[1], outer[2], outer[3], 0.5f };
3718			const glu::VertexArrayBinding	bindings[]		= { glu::va::Float("in_v_attr", 1, DE_LENGTH_OF_ARRAY(attribute), 0, &attribute[0]) };
3719
3720			for (int programNdx = 0; programNdx < (int)m_programs.size(); programNdx++)
3721			{
3722				const deUint32				programGL	= m_programs[programNdx].program->getProgram();
3723				gl.useProgram(programGL);
3724				const TFHandler::Result		tfResult	= transformFeedback.renderAndGetPrimitives(programGL, primitiveTypeGL, DE_LENGTH_OF_ARRAY(bindings), &bindings[0], DE_LENGTH_OF_ARRAY(attribute));
3725
3726				if (tfResult.numPrimitives != 2*primitiveCounts[tessLevelCaseNdx][subTessLevelCaseNdx])
3727				{
3728					log << TestLog::Message << "Failure: GL reported GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN to be "
3729											<< tfResult.numPrimitives << ", reference value is " << 2*primitiveCounts[tessLevelCaseNdx][subTessLevelCaseNdx]
3730											<< TestLog::EndMessage;
3731
3732					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of primitives");
3733					return STOP;
3734				}
3735
3736				{
3737					const int			half			= (int)tfResult.varying.size()/2;
3738					const vector<Vec3>	prim0Vertices	= vector<Vec3>(tfResult.varying.begin(), tfResult.varying.begin() + half);
3739					const Vec3* const	prim1Vertices	= &tfResult.varying[half];
3740
3741					for (int vtxNdx = 0; vtxNdx < (int)prim0Vertices.size(); vtxNdx++)
3742					{
3743						if (prim0Vertices[vtxNdx] != prim1Vertices[vtxNdx])
3744						{
3745							log << TestLog::Message << "Failure: tessellation coordinate at index " << vtxNdx << " differs between two primitives drawn in one draw call" << TestLog::EndMessage
3746								<< TestLog::Message << "Note: the coordinate is " << prim0Vertices[vtxNdx] << " for the first primitive and " << prim1Vertices[vtxNdx] << " for the second" << TestLog::EndMessage
3747								<< TestLog::Message << "Note: tessellation levels for both primitives were: " << tessellationLevelsString(&inner[0], &outer[0]) << TestLog::EndMessage;
3748							m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of primitives");
3749							return STOP;
3750						}
3751					}
3752
3753					if (programNdx == 0 && subTessLevelCaseNdx == 0)
3754						firstPrimVertices = prim0Vertices;
3755					else
3756					{
3757						const bool compareOk = compare(firstPrimVertices, prim0Vertices, levelCase.mem);
3758						if (!compareOk)
3759						{
3760							log << TestLog::Message << "Note: comparison of tessellation coordinates failed; comparison was made between following cases:\n"
3761													<< "  - case A: program 0, tessellation levels: " << tessLevelCases[tessLevelCaseNdx].levels[0].description() << "\n"
3762													<< "  - case B: program " << programNdx << ", tessellation levels: " << tessLevels.description() << TestLog::EndMessage;
3763							m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of primitives");
3764							return STOP;
3765						}
3766					}
3767				}
3768			}
3769		}
3770	}
3771
3772	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3773	return STOP;
3774}
3775
3776/*--------------------------------------------------------------------*//*!
3777 * \brief Test invariance rule #1
3778 *
3779 * Test that the sequence of primitives input to the TES only depends on
3780 * the tessellation levels, tessellation mode, spacing mode, winding, and
3781 * point mode.
3782 *//*--------------------------------------------------------------------*/
3783class InvariantPrimitiveSetCase : public PrimitiveSetInvarianceCase
3784{
3785public:
3786	InvariantPrimitiveSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
3787		: PrimitiveSetInvarianceCase(context, name, description, primType, spacing, usePointMode, winding == WINDING_CCW	? WINDINGUSAGE_CCW
3788																								: winding == WINDING_CW		? WINDINGUSAGE_CW
3789																								: WINDINGUSAGE_LAST)
3790	{
3791	}
3792
3793protected:
3794	virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int) const
3795	{
3796		for (int vtxNdx = 0; vtxNdx < (int)coordsA.size(); vtxNdx++)
3797		{
3798			if (coordsA[vtxNdx] != coordsB[vtxNdx])
3799			{
3800				m_testCtx.getLog() << TestLog::Message << "Failure: tessellation coordinate at index " << vtxNdx << " differs between two programs" << TestLog::EndMessage
3801								   << TestLog::Message << "Note: the coordinate is " << coordsA[vtxNdx] << " for the first program and " << coordsB[vtxNdx] << " for the other" << TestLog::EndMessage;
3802				return false;
3803			}
3804		}
3805		return true;
3806	}
3807};
3808
3809/*--------------------------------------------------------------------*//*!
3810 * \brief Test invariance rule #2
3811 *
3812 * Test that the set of vertices along an outer edge of a quad or triangle
3813 * only depends on that edge's tessellation level, and spacing.
3814 *
3815 * For each (outer) edge in the quad or triangle, draw multiple patches
3816 * with identical tessellation levels for that outer edge but with
3817 * different values for the other outer edges; compare, among the
3818 * primitives, the vertices generated for that outer edge. Repeat with
3819 * different programs, using different winding etc. settings. Compare
3820 * the edge's vertices between different programs.
3821 *//*--------------------------------------------------------------------*/
3822class InvariantOuterEdgeCase : public TestCase
3823{
3824public:
3825	InvariantOuterEdgeCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing)
3826		: TestCase			(context, name, description)
3827		, m_primitiveType	(primType)
3828		, m_spacing			(spacing)
3829	{
3830		DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
3831	}
3832
3833	void						init		(void);
3834	void						deinit		(void);
3835	IterateResult				iterate		(void);
3836
3837private:
3838	struct Program
3839	{
3840		Winding							winding;
3841		bool							usePointMode;
3842		SharedPtr<const ShaderProgram>	program;
3843
3844				Program			(Winding w, bool point, const SharedPtr<const ShaderProgram>& prog) : winding(w), usePointMode(point), program(prog) {}
3845
3846		string	description		(void) const { return string() + "winding mode " + getWindingShaderName(winding) + ", " + (usePointMode ? "" : "don't ") + "use point mode"; };
3847	};
3848
3849	static vector<float>		generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel);
3850
3851	static const int			RENDER_SIZE = 16;
3852
3853	const TessPrimitiveType		m_primitiveType;
3854	const SpacingMode			m_spacing;
3855
3856	vector<Program>				m_programs;
3857};
3858
3859vector<float> InvariantOuterEdgeCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel)
3860{
3861	de::Random rnd(123);
3862	return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd);
3863}
3864
3865void InvariantOuterEdgeCase::init (void)
3866{
3867	checkTessellationSupport(m_context);
3868	checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
3869
3870	for (int windingI = 0; windingI < WINDING_LAST; windingI++)
3871	{
3872		const Winding winding = (Winding)windingI;
3873
3874		for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++)
3875		{
3876			const bool		usePointMode	= usePointModeI != 0;
3877			const int		programNdx		= (int)m_programs.size();
3878			const string	floatLit01		= de::floatToString(10.0f / (float)(programNdx + 10), 2);
3879
3880			m_programs.push_back(Program(winding, usePointMode,
3881										 SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
3882				<< glu::VertexSource					("#version 310 es\n"
3883														 "\n"
3884														 "in highp float in_v_attr;\n"
3885														 "out highp float in_tc_attr;\n"
3886														 "\n"
3887														 "void main (void)\n"
3888														 "{\n"
3889														 "	in_tc_attr = in_v_attr;\n"
3890														 "}\n")
3891
3892				<< glu::TessellationControlSource		("#version 310 es\n"
3893														 "#extension GL_EXT_tessellation_shader : require\n"
3894														 "\n"
3895														 "layout (vertices = " + de::toString(programNdx+1) + ") out;\n"
3896														 "\n"
3897														 "in highp float in_tc_attr[];\n"
3898														 "\n"
3899														 "void main (void)\n"
3900														 "{\n"
3901														 "	gl_TessLevelInner[0] = in_tc_attr[0];\n"
3902														 "	gl_TessLevelInner[1] = in_tc_attr[1];\n"
3903														 "\n"
3904														 "	gl_TessLevelOuter[0] = in_tc_attr[2];\n"
3905														 "	gl_TessLevelOuter[1] = in_tc_attr[3];\n"
3906														 "	gl_TessLevelOuter[2] = in_tc_attr[4];\n"
3907														 "	gl_TessLevelOuter[3] = in_tc_attr[5];\n"
3908														 "}\n")
3909
3910				<< glu::TessellationEvaluationSource	("#version 310 es\n"
3911														 "#extension GL_EXT_tessellation_shader : require\n"
3912														 "\n"
3913														 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, winding, usePointMode) +
3914														 "\n"
3915														 "out highp vec4 in_f_color;\n"
3916														 "invariant out highp vec3 out_te_tessCoord;\n"
3917														 "\n"
3918														 "void main (void)\n"
3919														 "{\n"
3920														 "	gl_Position = vec4(gl_TessCoord.xy*" + floatLit01 + " - float(gl_PrimitiveID)*0.05, 0.0, 1.0);\n"
3921														 "	in_f_color = vec4(" + floatLit01 + ");\n"
3922														 "	out_te_tessCoord = gl_TessCoord;\n"
3923														 "}\n")
3924
3925				<< glu::FragmentSource					("#version 310 es\n"
3926														 "\n"
3927														 "layout (location = 0) out mediump vec4 o_color;\n"
3928														 "\n"
3929														 "in highp vec4 in_f_color;\n"
3930														 "\n"
3931														 "void main (void)\n"
3932														 "{\n"
3933														 "	o_color = in_f_color;\n"
3934														 "}\n")
3935
3936				<< glu::TransformFeedbackVarying		("out_te_tessCoord")
3937				<< glu::TransformFeedbackMode			(GL_INTERLEAVED_ATTRIBS)))));
3938
3939			{
3940				const tcu::ScopedLogSection section(m_testCtx.getLog(), "Program" + de::toString(programNdx), "Program " + de::toString(programNdx));
3941
3942				if (programNdx == 0 || !m_programs.back().program->isOk())
3943					m_testCtx.getLog() << *m_programs.back().program;
3944
3945				if (!m_programs.back().program->isOk())
3946					TCU_FAIL("Program compilation failed");
3947
3948				if (programNdx > 0)
3949					m_testCtx.getLog() << TestLog::Message << "Note: program " << programNdx << " is similar to above, except some constants are different, and: " << m_programs.back().description() << TestLog::EndMessage;
3950			}
3951		}
3952	}
3953}
3954
3955void InvariantOuterEdgeCase::deinit (void)
3956{
3957	m_programs.clear();
3958}
3959
3960InvariantOuterEdgeCase::IterateResult InvariantOuterEdgeCase::iterate (void)
3961{
3962	typedef TransformFeedbackHandler<Vec3> TFHandler;
3963
3964	TestLog&							log							= m_testCtx.getLog();
3965	const RenderContext&				renderCtx					= m_context.getRenderContext();
3966	const RandomViewport				viewport					(renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
3967	const glw::Functions&				gl							= renderCtx.getFunctions();
3968
3969	static const float					singleOuterEdgeLevels[]		= { 1.0f, 1.2f, 1.9f, 2.3f, 2.8f, 3.3f, 3.8f, 10.2f, 1.6f, 24.4f, 24.7f, 63.0f };
3970	const int							numPatchesPerDrawCall		= 10;
3971	const vector<OuterEdgeDescription>	edgeDescriptions			= outerEdgeDescriptions(m_primitiveType);
3972
3973	{
3974		// Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once.
3975		int maxNumVerticesInDrawCall = 0;
3976		{
3977			const vector<float> patchTessLevels = generatePatchTessLevels(numPatchesPerDrawCall, 0 /* outer-edge index doesn't affect vertex count */, arrayMax(singleOuterEdgeLevels));
3978
3979			for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++)
3980				maxNumVerticesInDrawCall = de::max(maxNumVerticesInDrawCall,
3981												   multiplePatchReferenceVertexCount(m_primitiveType, m_spacing, usePointModeI != 0, &patchTessLevels[0], numPatchesPerDrawCall));
3982		}
3983
3984		{
3985			const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall);
3986
3987			setViewport(gl, viewport);
3988			gl.patchParameteri(GL_PATCH_VERTICES, 6);
3989
3990			for (int outerEdgeIndex = 0; outerEdgeIndex < (int)edgeDescriptions.size(); outerEdgeIndex++)
3991			{
3992				const OuterEdgeDescription& edgeDesc = edgeDescriptions[outerEdgeIndex];
3993
3994				for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(singleOuterEdgeLevels); outerEdgeLevelCaseNdx++)
3995				{
3996					typedef std::set<Vec3, VecLexLessThan<3> > Vec3Set;
3997
3998					const vector<float>				patchTessLevels		= generatePatchTessLevels(numPatchesPerDrawCall, outerEdgeIndex, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
3999					const glu::VertexArrayBinding	bindings[]			= { glu::va::Float("in_v_attr", 1, (int)patchTessLevels.size(), 0, &patchTessLevels[0]) };
4000					Vec3Set							firstOuterEdgeVertices; // Vertices of the outer edge of the first patch of the first program's draw call; used for comparison with other patches.
4001
4002					log << TestLog::Message << "Testing with outer tessellation level " << singleOuterEdgeLevels[outerEdgeLevelCaseNdx]
4003											<< " for the " << edgeDesc.description() << " edge, and with various levels for other edges, and with all programs" << TestLog::EndMessage;
4004
4005					for (int programNdx = 0; programNdx < (int)m_programs.size(); programNdx++)
4006					{
4007						const Program& program		= m_programs[programNdx];
4008						const deUint32 programGL	= program.program->getProgram();
4009
4010						gl.useProgram(programGL);
4011
4012						{
4013							const TFHandler::Result		tfResult			= tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, program.usePointMode),
4014																											   DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)patchTessLevels.size());
4015							const int					refNumVertices		= multiplePatchReferenceVertexCount(m_primitiveType, m_spacing, program.usePointMode, &patchTessLevels[0], numPatchesPerDrawCall);
4016							int							numVerticesRead		= 0;
4017
4018							if ((int)tfResult.varying.size() != refNumVertices)
4019							{
4020								log << TestLog::Message << "Failure: the number of vertices returned by transform feedback is "
4021														<< tfResult.varying.size() << ", expected " << refNumVertices << TestLog::EndMessage
4022									<< TestLog::Message << "Note: rendered " << numPatchesPerDrawCall
4023														<< " patches in one draw call; tessellation levels for each patch are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n"
4024														<< containerStr(patchTessLevels, 6) << TestLog::EndMessage;
4025
4026								m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4027								return STOP;
4028							}
4029
4030							// Check the vertices of each patch.
4031
4032							for (int patchNdx = 0; patchNdx < numPatchesPerDrawCall; patchNdx++)
4033							{
4034								const float* const	innerLevels			= &patchTessLevels[6*patchNdx + 0];
4035								const float* const	outerLevels			= &patchTessLevels[6*patchNdx + 2];
4036								const int			patchNumVertices	= referenceVertexCount(m_primitiveType, m_spacing, program.usePointMode, innerLevels, outerLevels);
4037								Vec3Set				outerEdgeVertices;
4038
4039								// We're interested in just the vertices on the current outer edge.
4040								for(int vtxNdx = numVerticesRead; vtxNdx < numVerticesRead + patchNumVertices; vtxNdx++)
4041								{
4042									const Vec3& vtx = tfResult.varying[vtxNdx];
4043									if (edgeDesc.contains(vtx))
4044										outerEdgeVertices.insert(tfResult.varying[vtxNdx]);
4045								}
4046
4047								// Check that the outer edge contains an appropriate number of vertices.
4048								{
4049									const int refNumVerticesOnOuterEdge = 1 + getClampedRoundedTessLevel(m_spacing, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
4050
4051									if ((int)outerEdgeVertices.size() != refNumVerticesOnOuterEdge)
4052									{
4053										log << TestLog::Message << "Failure: the number of vertices on outer edge is " << outerEdgeVertices.size()
4054																<< ", expected " << refNumVerticesOnOuterEdge << TestLog::EndMessage
4055											<< TestLog::Message << "Note: vertices on the outer edge are:\n" << containerStr(outerEdgeVertices, 5, 0) << TestLog::EndMessage
4056											<< TestLog::Message << "Note: the following parameters were used: " << program.description()
4057																<< ", tessellation levels: " << tessellationLevelsString(innerLevels, outerLevels) << TestLog::EndMessage;
4058										m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4059										return STOP;
4060									}
4061								}
4062
4063								// Compare the vertices to those of the first patch (unless this is the first patch).
4064
4065								if (programNdx == 0 && patchNdx == 0)
4066									firstOuterEdgeVertices = outerEdgeVertices;
4067								else
4068								{
4069									if (firstOuterEdgeVertices != outerEdgeVertices)
4070									{
4071										log << TestLog::Message << "Failure: vertices generated for the edge differ between the following cases:\n"
4072																<< "  - case A: " << m_programs[0].description() << ", tessellation levels: "
4073																				  << tessellationLevelsString(&patchTessLevels[0], &patchTessLevels[2]) << "\n"
4074																<< "  - case B: " << program.description() << ", tessellation levels: "
4075																				  << tessellationLevelsString(innerLevels, outerLevels) << TestLog::EndMessage;
4076
4077										log << TestLog::Message << "Note: resulting vertices for the edge for the cases were:\n"
4078																<< "  - case A: " << containerStr(firstOuterEdgeVertices, 5, 14) << "\n"
4079																<< "  - case B: " << containerStr(outerEdgeVertices, 5, 14) << TestLog::EndMessage;
4080
4081										m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4082										return STOP;
4083									}
4084								}
4085
4086								numVerticesRead += patchNumVertices;
4087							}
4088
4089							DE_ASSERT(numVerticesRead == (int)tfResult.varying.size());
4090						}
4091					}
4092				}
4093			}
4094		}
4095	}
4096
4097	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4098	return STOP;
4099}
4100
4101/*--------------------------------------------------------------------*//*!
4102 * \brief Test invariance rule #3
4103 *
4104 * Test that the vertices along an outer edge are placed symmetrically.
4105 *
4106 * Draw multiple patches with different tessellation levels and different
4107 * point_mode, winding etc. Before outputting tesscoords with TF, mirror
4108 * the vertices in the TES such that every vertex on an outer edge -
4109 * except the possible middle vertex - should be duplicated in the output.
4110 * Check that appropriate duplicates exist.
4111 *//*--------------------------------------------------------------------*/
4112class SymmetricOuterEdgeCase : public TestCase
4113{
4114public:
4115	SymmetricOuterEdgeCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
4116		: TestCase			(context, name, description)
4117		, m_primitiveType	(primType)
4118		, m_spacing			(spacing)
4119		, m_winding			(winding)
4120		, m_usePointMode	(usePointMode)
4121	{
4122	}
4123
4124	void									init		(void);
4125	void									deinit		(void);
4126	IterateResult							iterate		(void);
4127
4128private:
4129	static vector<float>					generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel);
4130
4131	static const int						RENDER_SIZE = 16;
4132
4133	const TessPrimitiveType					m_primitiveType;
4134	const SpacingMode						m_spacing;
4135	const Winding							m_winding;
4136	const bool								m_usePointMode;
4137
4138	SharedPtr<const glu::ShaderProgram>		m_program;
4139};
4140
4141vector<float> SymmetricOuterEdgeCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel)
4142{
4143	de::Random rnd(123);
4144	return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd);
4145}
4146
4147void SymmetricOuterEdgeCase::init (void)
4148{
4149	checkTessellationSupport(m_context);
4150	checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
4151
4152	m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
4153		<< glu::VertexSource					("#version 310 es\n"
4154												 "\n"
4155												 "in highp float in_v_attr;\n"
4156												 "out highp float in_tc_attr;\n"
4157												 "\n"
4158												 "void main (void)\n"
4159												 "{\n"
4160												 "	in_tc_attr = in_v_attr;\n"
4161												 "}\n")
4162
4163		<< glu::TessellationControlSource		("#version 310 es\n"
4164												 "#extension GL_EXT_tessellation_shader : require\n"
4165												 "\n"
4166												 "layout (vertices = 1) out;\n"
4167												 "\n"
4168												 "in highp float in_tc_attr[];\n"
4169												 "\n"
4170												 "void main (void)\n"
4171												 "{\n"
4172												 "	gl_TessLevelInner[0] = in_tc_attr[0];\n"
4173												 "	gl_TessLevelInner[1] = in_tc_attr[1];\n"
4174												 "\n"
4175												 "	gl_TessLevelOuter[0] = in_tc_attr[2];\n"
4176												 "	gl_TessLevelOuter[1] = in_tc_attr[3];\n"
4177												 "	gl_TessLevelOuter[2] = in_tc_attr[4];\n"
4178												 "	gl_TessLevelOuter[3] = in_tc_attr[5];\n"
4179												 "}\n")
4180
4181		<< glu::TessellationEvaluationSource	("#version 310 es\n"
4182												 "#extension GL_EXT_tessellation_shader : require\n"
4183												 "\n"
4184												 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) +
4185												 "\n"
4186												 "out highp vec4 in_f_color;\n"
4187												 "out highp vec4 out_te_tessCoord_isMirrored;\n"
4188												 "\n"
4189												 "void main (void)\n"
4190												 "{\n"
4191												 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
4192													"	float x = gl_TessCoord.x;\n"
4193													"	float y = gl_TessCoord.y;\n"
4194													"	float z = gl_TessCoord.z;\n"
4195													"	// Mirror one half of each outer edge onto the other half, except the endpoints (because they belong to two edges)\n"
4196													"	out_te_tessCoord_isMirrored = z == 0.0 && x > 0.5 && x != 1.0 ? vec4(1.0-x,  1.0-y,    0.0, 1.0)\n"
4197													"	                            : y == 0.0 && z > 0.5 && z != 1.0 ? vec4(1.0-x,    0.0,  1.0-z, 1.0)\n"
4198													"	                            : x == 0.0 && y > 0.5 && y != 1.0 ? vec4(  0.0,  1.0-y,  1.0-z, 1.0)\n"
4199													"	                            : vec4(x, y, z, 0.0);\n"
4200												  : m_primitiveType == TESSPRIMITIVETYPE_QUADS ?
4201													"	float x = gl_TessCoord.x;\n"
4202													"	float y = gl_TessCoord.y;\n"
4203													"	// Mirror one half of each outer edge onto the other half, except the endpoints (because they belong to two edges)\n"
4204													"	out_te_tessCoord_isMirrored = (x == 0.0 || x == 1.0) && y > 0.5 && y != 1.0 ? vec4(    x, 1.0-y, 0.0, 1.0)\n"
4205													"	                            : (y == 0.0 || y == 1.0) && x > 0.5 && x != 1.0 ? vec4(1.0-x,     y, 0.0, 1.0)\n"
4206													"	                            : vec4(x, y, 0.0, 0.0);\n"
4207												  : m_primitiveType == TESSPRIMITIVETYPE_ISOLINES ?
4208													"	float x = gl_TessCoord.x;\n"
4209													"	float y = gl_TessCoord.y;\n"
4210													"	// Mirror one half of each outer edge onto the other half\n"
4211													"	out_te_tessCoord_isMirrored = (x == 0.0 || x == 1.0) && y > 0.5 ? vec4(x, 1.0-y, 0.0, 1.0)\n"
4212													"	                            : vec4(x, y, 0.0, 0.0f);\n"
4213												  : DE_NULL) +
4214												 "\n"
4215												 "	gl_Position = vec4(gl_TessCoord.xy, 0.0, 1.0);\n"
4216												 "	in_f_color = vec4(1.0);\n"
4217												 "}\n")
4218
4219		<< glu::FragmentSource					("#version 310 es\n"
4220												 "\n"
4221												 "layout (location = 0) out mediump vec4 o_color;\n"
4222												 "\n"
4223												 "in highp vec4 in_f_color;\n"
4224												 "\n"
4225												 "void main (void)\n"
4226												 "{\n"
4227												 "	o_color = in_f_color;\n"
4228												 "}\n")
4229
4230		<< glu::TransformFeedbackVarying		("out_te_tessCoord_isMirrored")
4231		<< glu::TransformFeedbackMode			(GL_INTERLEAVED_ATTRIBS)));
4232
4233	m_testCtx.getLog() << *m_program;
4234	if (!m_program->isOk())
4235		TCU_FAIL("Program compilation failed");
4236}
4237
4238void SymmetricOuterEdgeCase::deinit (void)
4239{
4240	m_program.clear();
4241}
4242
4243SymmetricOuterEdgeCase::IterateResult SymmetricOuterEdgeCase::iterate (void)
4244{
4245	typedef TransformFeedbackHandler<Vec4> TFHandler;
4246
4247	TestLog&							log							= m_testCtx.getLog();
4248	const RenderContext&				renderCtx					= m_context.getRenderContext();
4249	const RandomViewport				viewport					(renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
4250	const glw::Functions&				gl							= renderCtx.getFunctions();
4251
4252	static const float					singleOuterEdgeLevels[]		= { 1.0f, 1.2f, 1.9f, 2.3f, 2.8f, 3.3f, 3.8f, 10.2f, 1.6f, 24.4f, 24.7f, 63.0f };
4253	const vector<OuterEdgeDescription>	edgeDescriptions			= outerEdgeDescriptions(m_primitiveType);
4254
4255	{
4256		// Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once.
4257		int maxNumVerticesInDrawCall;
4258		{
4259			const vector<float> patchTessLevels = generatePatchTessLevels(1, 0 /* outer-edge index doesn't affect vertex count */, arrayMax(singleOuterEdgeLevels));
4260			maxNumVerticesInDrawCall = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]);
4261		}
4262
4263		{
4264			const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall);
4265
4266			setViewport(gl, viewport);
4267			gl.patchParameteri(GL_PATCH_VERTICES, 6);
4268
4269			for (int outerEdgeIndex = 0; outerEdgeIndex < (int)edgeDescriptions.size(); outerEdgeIndex++)
4270			{
4271				const OuterEdgeDescription& edgeDesc = edgeDescriptions[outerEdgeIndex];
4272
4273				for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(singleOuterEdgeLevels); outerEdgeLevelCaseNdx++)
4274				{
4275					typedef std::set<Vec3, VecLexLessThan<3> > Vec3Set;
4276
4277					const vector<float>				patchTessLevels		= generatePatchTessLevels(1, outerEdgeIndex, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
4278					const glu::VertexArrayBinding	bindings[]			= { glu::va::Float("in_v_attr", 1, (int)patchTessLevels.size(), 0, &patchTessLevels[0]) };
4279
4280					log << TestLog::Message << "Testing with outer tessellation level " << singleOuterEdgeLevels[outerEdgeLevelCaseNdx]
4281											<< " for the " << edgeDesc.description() << " edge, and with various levels for other edges" << TestLog::EndMessage;
4282
4283					{
4284						const deUint32 programGL = m_program->getProgram();
4285
4286						gl.useProgram(programGL);
4287
4288						{
4289							const TFHandler::Result		tfResult		= tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode),
4290																										   DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)patchTessLevels.size());
4291							const int					refNumVertices	= referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]);
4292
4293							if ((int)tfResult.varying.size() != refNumVertices)
4294							{
4295								log << TestLog::Message << "Failure: the number of vertices returned by transform feedback is "
4296														<< tfResult.varying.size() << ", expected " << refNumVertices << TestLog::EndMessage
4297									<< TestLog::Message << "Note: rendered 1 patch, tessellation levels are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n"
4298														<< containerStr(patchTessLevels, 6) << TestLog::EndMessage;
4299
4300								m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4301								return STOP;
4302							}
4303
4304							// Check the vertices.
4305
4306							{
4307								Vec3Set nonMirroredEdgeVertices;
4308								Vec3Set mirroredEdgeVertices;
4309
4310								// We're interested in just the vertices on the current outer edge.
4311								for(int vtxNdx = 0; vtxNdx < refNumVertices; vtxNdx++)
4312								{
4313									const Vec3& vtx = tfResult.varying[vtxNdx].swizzle(0,1,2);
4314									if (edgeDesc.contains(vtx))
4315									{
4316										// Ignore the middle vertex of the outer edge, as it's exactly at the mirroring point;
4317										// for isolines, also ignore (0, 0) and (1, 0) because there's no mirrored counterpart for them.
4318										if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES && vtx == tcu::select(Vec3(0.0f), Vec3(0.5f), singleTrueMask<3>(edgeDesc.constantCoordinateIndex)))
4319											continue;
4320										if (m_primitiveType == TESSPRIMITIVETYPE_QUADS && vtx.swizzle(0,1) == tcu::select(Vec2(edgeDesc.constantCoordinateValueChoices[0]),
4321																															   Vec2(0.5f),
4322																															   singleTrueMask<2>(edgeDesc.constantCoordinateIndex)))
4323											continue;
4324										if (m_primitiveType == TESSPRIMITIVETYPE_ISOLINES && (vtx == Vec3(0.0f, 0.5f, 0.0f) || vtx == Vec3(1.0f, 0.5f, 0.0f) ||
4325																							  vtx == Vec3(0.0f, 0.0f, 0.0f) || vtx == Vec3(1.0f, 0.0f, 0.0f)))
4326											continue;
4327
4328										const bool isMirrored = tfResult.varying[vtxNdx].w() > 0.5f;
4329										if (isMirrored)
4330											mirroredEdgeVertices.insert(vtx);
4331										else
4332											nonMirroredEdgeVertices.insert(vtx);
4333									}
4334								}
4335
4336								if (m_primitiveType != TESSPRIMITIVETYPE_ISOLINES)
4337								{
4338									// Check that both endpoints are present. Note that endpoints aren't mirrored by the shader, since they belong to more than one edge.
4339
4340									Vec3 endpointA;
4341									Vec3 endpointB;
4342
4343									if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4344									{
4345										endpointA = tcu::select(Vec3(1.0f), Vec3(0.0f), singleTrueMask<3>((edgeDesc.constantCoordinateIndex + 1) % 3));
4346										endpointB = tcu::select(Vec3(1.0f), Vec3(0.0f), singleTrueMask<3>((edgeDesc.constantCoordinateIndex + 2) % 3));
4347									}
4348									else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
4349									{
4350										endpointA.xy() = tcu::select(Vec2(edgeDesc.constantCoordinateValueChoices[0]), Vec2(0.0f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex));
4351										endpointB.xy() = tcu::select(Vec2(edgeDesc.constantCoordinateValueChoices[0]), Vec2(1.0f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex));
4352									}
4353									else
4354										DE_ASSERT(false);
4355
4356									if (!contains(nonMirroredEdgeVertices, endpointA) ||
4357										!contains(nonMirroredEdgeVertices, endpointB))
4358									{
4359										log << TestLog::Message << "Failure: edge doesn't contain both endpoints, " << endpointA << " and " << endpointB << TestLog::EndMessage
4360											<< TestLog::Message << "Note: non-mirrored vertices:\n" << containerStr(nonMirroredEdgeVertices, 5)
4361																<< "\nmirrored vertices:\n" << containerStr(mirroredEdgeVertices, 5) << TestLog::EndMessage;
4362										m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4363										return STOP;
4364									}
4365									nonMirroredEdgeVertices.erase(endpointA);
4366									nonMirroredEdgeVertices.erase(endpointB);
4367								}
4368
4369								if (nonMirroredEdgeVertices != mirroredEdgeVertices)
4370								{
4371									log << TestLog::Message << "Failure: the set of mirrored edges isn't equal to the set of non-mirrored edges (ignoring endpoints and possible middle)" << TestLog::EndMessage
4372										<< TestLog::Message << "Note: non-mirrored vertices:\n" << containerStr(nonMirroredEdgeVertices, 5)
4373																<< "\nmirrored vertices:\n" << containerStr(mirroredEdgeVertices, 5) << TestLog::EndMessage;
4374									m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4375									return STOP;
4376								}
4377							}
4378						}
4379					}
4380				}
4381			}
4382		}
4383	}
4384
4385	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4386	return STOP;
4387}
4388
4389/*--------------------------------------------------------------------*//*!
4390 * \brief Test invariance rule #4
4391 *
4392 * Test that the vertices on an outer edge don't depend on which of the
4393 * edges it is, other than with respect to component order.
4394 *//*--------------------------------------------------------------------*/
4395class OuterEdgeVertexSetIndexIndependenceCase : public TestCase
4396{
4397public:
4398	OuterEdgeVertexSetIndexIndependenceCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
4399		: TestCase			(context, name, description)
4400		, m_primitiveType	(primType)
4401		, m_spacing			(spacing)
4402		, m_winding			(winding)
4403		, m_usePointMode	(usePointMode)
4404	{
4405		DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
4406	}
4407
4408	void									init		(void);
4409	void									deinit		(void);
4410	IterateResult							iterate		(void);
4411
4412private:
4413	static vector<float>					generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel);
4414
4415	static const int						RENDER_SIZE = 16;
4416
4417	const TessPrimitiveType					m_primitiveType;
4418	const SpacingMode						m_spacing;
4419	const Winding							m_winding;
4420	const bool								m_usePointMode;
4421
4422	SharedPtr<const glu::ShaderProgram>		m_program;
4423};
4424
4425vector<float> OuterEdgeVertexSetIndexIndependenceCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel)
4426{
4427	de::Random rnd(123);
4428	return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd);
4429}
4430
4431void OuterEdgeVertexSetIndexIndependenceCase::init (void)
4432{
4433	checkTessellationSupport(m_context);
4434	checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
4435
4436	m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
4437		<< glu::VertexSource					("#version 310 es\n"
4438												 "\n"
4439												 "in highp float in_v_attr;\n"
4440												 "out highp float in_tc_attr;\n"
4441												 "\n"
4442												 "void main (void)\n"
4443												 "{\n"
4444												 "	in_tc_attr = in_v_attr;\n"
4445												 "}\n")
4446
4447		<< glu::TessellationControlSource		("#version 310 es\n"
4448												 "#extension GL_EXT_tessellation_shader : require\n"
4449												 "\n"
4450												 "layout (vertices = 1) out;\n"
4451												 "\n"
4452												 "in highp float in_tc_attr[];\n"
4453												 "\n"
4454												 "void main (void)\n"
4455												 "{\n"
4456												 "	gl_TessLevelInner[0] = in_tc_attr[0];\n"
4457												 "	gl_TessLevelInner[1] = in_tc_attr[1];\n"
4458												 "\n"
4459												 "	gl_TessLevelOuter[0] = in_tc_attr[2];\n"
4460												 "	gl_TessLevelOuter[1] = in_tc_attr[3];\n"
4461												 "	gl_TessLevelOuter[2] = in_tc_attr[4];\n"
4462												 "	gl_TessLevelOuter[3] = in_tc_attr[5];\n"
4463												 "}\n")
4464
4465		<< glu::TessellationEvaluationSource	("#version 310 es\n"
4466												 "#extension GL_EXT_tessellation_shader : require\n"
4467												 "\n"
4468												 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) +
4469												 "\n"
4470												 "out highp vec4 in_f_color;\n"
4471												 "out highp vec3 out_te_tessCoord;\n"
4472												 "\n"
4473												 "void main (void)\n"
4474												 "{\n"
4475												 "	out_te_tessCoord = gl_TessCoord;"
4476												 "	gl_Position = vec4(gl_TessCoord.xy, 0.0, 1.0);\n"
4477												 "	in_f_color = vec4(1.0);\n"
4478												 "}\n")
4479
4480		<< glu::FragmentSource					("#version 310 es\n"
4481												 "\n"
4482												 "layout (location = 0) out mediump vec4 o_color;\n"
4483												 "\n"
4484												 "in highp vec4 in_f_color;\n"
4485												 "\n"
4486												 "void main (void)\n"
4487												 "{\n"
4488												 "	o_color = in_f_color;\n"
4489												 "}\n")
4490
4491		<< glu::TransformFeedbackVarying		("out_te_tessCoord")
4492		<< glu::TransformFeedbackMode			(GL_INTERLEAVED_ATTRIBS)));
4493
4494	m_testCtx.getLog() << *m_program;
4495	if (!m_program->isOk())
4496		TCU_FAIL("Program compilation failed");
4497}
4498
4499void OuterEdgeVertexSetIndexIndependenceCase::deinit (void)
4500{
4501	m_program.clear();
4502}
4503
4504OuterEdgeVertexSetIndexIndependenceCase::IterateResult OuterEdgeVertexSetIndexIndependenceCase::iterate (void)
4505{
4506	typedef TransformFeedbackHandler<Vec3> TFHandler;
4507
4508	TestLog&							log							= m_testCtx.getLog();
4509	const RenderContext&				renderCtx					= m_context.getRenderContext();
4510	const RandomViewport				viewport					(renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
4511	const glw::Functions&				gl							= renderCtx.getFunctions();
4512	const deUint32						programGL					= m_program->getProgram();
4513
4514	static const float					singleOuterEdgeLevels[]		= { 1.0f, 1.2f, 1.9f, 2.3f, 2.8f, 3.3f, 3.8f, 10.2f, 1.6f, 24.4f, 24.7f, 63.0f };
4515	const vector<OuterEdgeDescription>	edgeDescriptions			= outerEdgeDescriptions(m_primitiveType);
4516
4517	gl.useProgram(programGL);
4518	setViewport(gl, viewport);
4519	gl.patchParameteri(GL_PATCH_VERTICES, 6);
4520
4521	{
4522		// Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once.
4523		int maxNumVerticesInDrawCall = 0;
4524		{
4525			const vector<float> patchTessLevels = generatePatchTessLevels(1, 0 /* outer-edge index doesn't affect vertex count */, arrayMax(singleOuterEdgeLevels));
4526			maxNumVerticesInDrawCall = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]);
4527		}
4528
4529		{
4530			const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall);
4531
4532			for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(singleOuterEdgeLevels); outerEdgeLevelCaseNdx++)
4533			{
4534				typedef std::set<Vec3, VecLexLessThan<3> > Vec3Set;
4535
4536				Vec3Set firstEdgeVertices;
4537
4538				for (int outerEdgeIndex = 0; outerEdgeIndex < (int)edgeDescriptions.size(); outerEdgeIndex++)
4539				{
4540					const OuterEdgeDescription&		edgeDesc			= edgeDescriptions[outerEdgeIndex];
4541					const vector<float>				patchTessLevels		= generatePatchTessLevels(1, outerEdgeIndex, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
4542					const glu::VertexArrayBinding	bindings[]			= { glu::va::Float("in_v_attr", 1, (int)patchTessLevels.size(), 0, &patchTessLevels[0]) };
4543
4544					log << TestLog::Message << "Testing with outer tessellation level " << singleOuterEdgeLevels[outerEdgeLevelCaseNdx]
4545											<< " for the " << edgeDesc.description() << " edge, and with various levels for other edges" << TestLog::EndMessage;
4546
4547					{
4548						const TFHandler::Result		tfResult		= tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode),
4549																										DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)patchTessLevels.size());
4550						const int					refNumVertices	= referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]);
4551
4552						if ((int)tfResult.varying.size() != refNumVertices)
4553						{
4554							log << TestLog::Message << "Failure: the number of vertices returned by transform feedback is "
4555													<< tfResult.varying.size() << ", expected " << refNumVertices << TestLog::EndMessage
4556								<< TestLog::Message << "Note: rendered 1 patch, tessellation levels are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n"
4557													<< containerStr(patchTessLevels, 6) << TestLog::EndMessage;
4558
4559							m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4560							return STOP;
4561						}
4562
4563						{
4564							Vec3Set currentEdgeVertices;
4565
4566							// Get the vertices on the current outer edge.
4567							for(int vtxNdx = 0; vtxNdx < refNumVertices; vtxNdx++)
4568							{
4569								const Vec3& vtx = tfResult.varying[vtxNdx];
4570								if (edgeDesc.contains(vtx))
4571								{
4572									// Swizzle components to match the order of the first edge.
4573									if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4574									{
4575										currentEdgeVertices.insert(outerEdgeIndex == 0 ? vtx
4576																	: outerEdgeIndex == 1 ? vtx.swizzle(1, 0, 2)
4577																	: outerEdgeIndex == 2 ? vtx.swizzle(2, 1, 0)
4578																	: Vec3(-1.0f));
4579									}
4580									else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
4581									{
4582										currentEdgeVertices.insert(Vec3(outerEdgeIndex == 0 ? vtx.y()
4583																		: outerEdgeIndex == 1 ? vtx.x()
4584																		: outerEdgeIndex == 2 ? vtx.y()
4585																		: outerEdgeIndex == 3 ? vtx.x()
4586																		: -1.0f,
4587																		0.0f, 0.0f));
4588									}
4589									else
4590										DE_ASSERT(false);
4591								}
4592							}
4593
4594							if (outerEdgeIndex == 0)
4595								firstEdgeVertices = currentEdgeVertices;
4596							else
4597							{
4598								// Compare vertices of this edge to those of the first edge.
4599
4600								if (currentEdgeVertices != firstEdgeVertices)
4601								{
4602									const char* const swizzleDesc = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? (outerEdgeIndex == 1 ? "(y, x, z)"
4603																													: outerEdgeIndex == 2 ? "(z, y, x)"
4604																													: DE_NULL)
4605																	: m_primitiveType == TESSPRIMITIVETYPE_QUADS ? (outerEdgeIndex == 1 ? "(x, 0)"
4606																												: outerEdgeIndex == 2 ? "(y, 0)"
4607																												: outerEdgeIndex == 3 ? "(x, 0)"
4608																												: DE_NULL)
4609																	: DE_NULL;
4610
4611									log << TestLog::Message << "Failure: the set of vertices on the " << edgeDesc.description() << " edge"
4612															<< " doesn't match the set of vertices on the " << edgeDescriptions[0].description() << " edge" << TestLog::EndMessage
4613										<< TestLog::Message << "Note: set of vertices on " << edgeDesc.description() << " edge, components swizzled like " << swizzleDesc
4614															<< " to match component order on first edge:\n" << containerStr(currentEdgeVertices, 5)
4615															<< "\non " << edgeDescriptions[0].description() << " edge:\n" << containerStr(firstEdgeVertices, 5) << TestLog::EndMessage;
4616									m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4617									return STOP;
4618								}
4619							}
4620						}
4621					}
4622				}
4623			}
4624		}
4625	}
4626
4627	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4628	return STOP;
4629}
4630
4631/*--------------------------------------------------------------------*//*!
4632 * \brief Test invariance rule #5
4633 *
4634 * Test that the set of triangles input to the TES only depends on the
4635 * tessellation levels, tessellation mode and spacing mode. Specifically,
4636 * winding doesn't change the set of triangles, though it can change the
4637 * order in which they are input to TES, and can (and will) change the
4638 * vertex order within a triangle.
4639 *//*--------------------------------------------------------------------*/
4640class InvariantTriangleSetCase : public PrimitiveSetInvarianceCase
4641{
4642public:
4643	InvariantTriangleSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing)
4644		: PrimitiveSetInvarianceCase(context, name, description, primType, spacing, false, WINDINGUSAGE_VARY)
4645	{
4646		DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
4647	}
4648
4649protected:
4650	virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int) const
4651	{
4652		return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog());
4653	}
4654};
4655
4656/*--------------------------------------------------------------------*//*!
4657 * \brief Test invariance rule #6
4658 *
4659 * Test that the set of inner triangles input to the TES only depends on
4660 * the inner tessellation levels, tessellation mode and spacing mode.
4661 *//*--------------------------------------------------------------------*/
4662class InvariantInnerTriangleSetCase : public PrimitiveSetInvarianceCase
4663{
4664public:
4665	InvariantInnerTriangleSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing)
4666		: PrimitiveSetInvarianceCase(context, name, description, primType, spacing, false, WINDINGUSAGE_VARY)
4667	{
4668		DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
4669	}
4670
4671protected:
4672	virtual vector<LevelCase> genTessLevelCases (void) const
4673	{
4674		const int					numSubCases		= 4;
4675		const vector<LevelCase>		baseResults		= PrimitiveSetInvarianceCase::genTessLevelCases();
4676		vector<LevelCase>			result;
4677		de::Random					rnd				(123);
4678
4679		// Generate variants with different values for irrelevant levels.
4680		for (int baseNdx = 0; baseNdx < (int)baseResults.size(); baseNdx++)
4681		{
4682			const TessLevels&	base	= baseResults[baseNdx].levels[0];
4683			TessLevels			levels	= base;
4684			LevelCase			levelCase;
4685
4686			for (int subNdx = 0; subNdx < numSubCases; subNdx++)
4687			{
4688				levelCase.levels.push_back(levels);
4689
4690				for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); i++)
4691					levels.outer[i] = rnd.getFloat(2.0f, 16.0f);
4692				if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4693					levels.inner[1] = rnd.getFloat(2.0f, 16.0f);
4694			}
4695
4696			result.push_back(levelCase);
4697		}
4698
4699		return result;
4700	}
4701
4702	struct IsInnerTriangleTriangle
4703	{
4704		bool operator() (const Vec3* vertices) const
4705		{
4706			for (int v = 0; v < 3; v++)
4707				for (int c = 0; c < 3; c++)
4708					if (vertices[v][c] == 0.0f)
4709						return false;
4710			return true;
4711		}
4712	};
4713
4714	struct IsInnerQuadTriangle
4715	{
4716		bool operator() (const Vec3* vertices) const
4717		{
4718			for (int v = 0; v < 3; v++)
4719				for (int c = 0; c < 2; c++)
4720					if (vertices[v][c] == 0.0f || vertices[v][c] == 1.0f)
4721						return false;
4722			return true;
4723		}
4724	};
4725
4726	virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int) const
4727	{
4728		if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4729			return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(), IsInnerTriangleTriangle(), "outer triangles");
4730		else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
4731			return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(), IsInnerQuadTriangle(), "outer triangles");
4732		else
4733		{
4734			DE_ASSERT(false);
4735			return false;
4736		}
4737	}
4738};
4739
4740/*--------------------------------------------------------------------*//*!
4741 * \brief Test invariance rule #7
4742 *
4743 * Test that the set of outer triangles input to the TES only depends on
4744 * tessellation mode, spacing mode and the inner and outer tessellation
4745 * levels corresponding to the inner and outer edges relevant to that
4746 * triangle.
4747 *//*--------------------------------------------------------------------*/
4748class InvariantOuterTriangleSetCase : public PrimitiveSetInvarianceCase
4749{
4750public:
4751	InvariantOuterTriangleSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing)
4752		: PrimitiveSetInvarianceCase(context, name, description, primType, spacing, false, WINDINGUSAGE_VARY)
4753	{
4754		DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
4755	}
4756
4757protected:
4758	virtual vector<LevelCase> genTessLevelCases (void) const
4759	{
4760		const int					numSubCasesPerEdge	= 4;
4761		const int					numEdges			= m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES	? 3
4762														: m_primitiveType == TESSPRIMITIVETYPE_QUADS		? 4
4763														: -1;
4764		const vector<LevelCase>		baseResult			= PrimitiveSetInvarianceCase::genTessLevelCases();
4765		vector<LevelCase>			result;
4766		de::Random					rnd					(123);
4767
4768		// Generate variants with different values for irrelevant levels.
4769		for (int baseNdx = 0; baseNdx < (int)baseResult.size(); baseNdx++)
4770		{
4771			const TessLevels& base = baseResult[baseNdx].levels[0];
4772			if (base.inner[0] == 1.0f || (m_primitiveType == TESSPRIMITIVETYPE_QUADS && base.inner[1] == 1.0f))
4773				continue;
4774
4775			for (int edgeNdx = 0; edgeNdx < numEdges; edgeNdx++)
4776			{
4777				TessLevels	levels = base;
4778				LevelCase	levelCase;
4779				levelCase.mem = edgeNdx;
4780
4781				for (int subCaseNdx = 0; subCaseNdx < numSubCasesPerEdge; subCaseNdx++)
4782				{
4783					levelCase.levels.push_back(levels);
4784
4785					for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); i++)
4786					{
4787						if (i != edgeNdx)
4788							levels.outer[i] = rnd.getFloat(2.0f, 16.0f);
4789					}
4790
4791					if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4792						levels.inner[1] = rnd.getFloat(2.0f, 16.0f);
4793				}
4794
4795				result.push_back(levelCase);
4796			}
4797		}
4798
4799		return result;
4800	}
4801
4802	class IsTriangleTriangleOnOuterEdge
4803	{
4804	public:
4805		IsTriangleTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
4806		bool operator() (const Vec3* vertices) const
4807		{
4808			bool touchesAppropriateEdge = false;
4809			for (int v = 0; v < 3; v++)
4810				if (vertices[v][m_edgeNdx] == 0.0f)
4811					touchesAppropriateEdge = true;
4812
4813			if (touchesAppropriateEdge)
4814			{
4815				const Vec3 avg = (vertices[0] + vertices[1] + vertices[2]) / 3.0f;
4816				return avg[m_edgeNdx] < avg[(m_edgeNdx+1)%3] &&
4817					   avg[m_edgeNdx] < avg[(m_edgeNdx+2)%3];
4818			}
4819			return false;
4820		}
4821
4822	private:
4823		int m_edgeNdx;
4824	};
4825
4826	class IsQuadTriangleOnOuterEdge
4827	{
4828	public:
4829		IsQuadTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
4830
4831		bool onEdge (const Vec3& v) const
4832		{
4833			return v[m_edgeNdx%2] == (m_edgeNdx <= 1 ? 0.0f : 1.0f);
4834		}
4835
4836		static inline bool onAnyEdge (const Vec3& v)
4837		{
4838			return v[0] == 0.0f || v[0] == 1.0f || v[1] == 0.0f || v[1] == 1.0f;
4839		}
4840
4841		bool operator() (const Vec3* vertices) const
4842		{
4843			for (int v = 0; v < 3; v++)
4844			{
4845				const Vec3& a = vertices[v];
4846				const Vec3& b = vertices[(v+1)%3];
4847				const Vec3& c = vertices[(v+2)%3];
4848				if (onEdge(a) && onEdge(b))
4849					return true;
4850				if (onEdge(c) && !onAnyEdge(a) && !onAnyEdge(b) && a[m_edgeNdx%2] == b[m_edgeNdx%2])
4851					return true;
4852			}
4853
4854			return false;
4855		}
4856
4857	private:
4858		int m_edgeNdx;
4859	};
4860
4861	virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int outerEdgeNdx) const
4862	{
4863		if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4864		{
4865			return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(),
4866									   IsTriangleTriangleOnOuterEdge(outerEdgeNdx),
4867									   ("inner triangles, and outer triangles corresponding to other edge than edge "
4868										+ outerEdgeDescriptions(m_primitiveType)[outerEdgeNdx].description()).c_str());
4869		}
4870		else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
4871		{
4872			return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(),
4873									   IsQuadTriangleOnOuterEdge(outerEdgeNdx),
4874									   ("inner triangles, and outer triangles corresponding to other edge than edge "
4875										+ outerEdgeDescriptions(m_primitiveType)[outerEdgeNdx].description()).c_str());
4876		}
4877		else
4878			DE_ASSERT(false);
4879
4880		return true;
4881	}
4882};
4883
4884/*--------------------------------------------------------------------*//*!
4885 * \brief Base class for testing individual components of tess coords
4886 *
4887 * Useful for testing parts of invariance rule #8.
4888 *//*--------------------------------------------------------------------*/
4889class TessCoordComponentInvarianceCase : public TestCase
4890{
4891public:
4892	TessCoordComponentInvarianceCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
4893		: TestCase			(context, name, description)
4894		, m_primitiveType	(primType)
4895		, m_spacing			(spacing)
4896		, m_winding			(winding)
4897		, m_usePointMode	(usePointMode)
4898	{
4899	}
4900
4901	void									init		(void);
4902	void									deinit		(void);
4903	IterateResult							iterate		(void);
4904
4905protected:
4906	virtual string							tessEvalOutputComponentStatements	(const char* tessCoordComponentName, const char* outputComponentName) const = 0;
4907	virtual bool							checkTessCoordComponent				(float component) const = 0;
4908
4909private:
4910	static vector<float>					genTessLevelCases (int numCases);
4911
4912	static const int						RENDER_SIZE = 16;
4913
4914	const TessPrimitiveType					m_primitiveType;
4915	const SpacingMode						m_spacing;
4916	const Winding							m_winding;
4917	const bool								m_usePointMode;
4918
4919	SharedPtr<const glu::ShaderProgram>		m_program;
4920};
4921
4922void TessCoordComponentInvarianceCase::init (void)
4923{
4924	checkTessellationSupport(m_context);
4925	checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
4926
4927	m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
4928		<< glu::VertexSource					("#version 310 es\n"
4929												 "\n"
4930												 "in highp float in_v_attr;\n"
4931												 "out highp float in_tc_attr;\n"
4932												 "\n"
4933												 "void main (void)\n"
4934												 "{\n"
4935												 "	in_tc_attr = in_v_attr;\n"
4936												 "}\n")
4937
4938		<< glu::TessellationControlSource		("#version 310 es\n"
4939												 "#extension GL_EXT_tessellation_shader : require\n"
4940												 "\n"
4941												 "layout (vertices = 1) out;\n"
4942												 "\n"
4943												 "in highp float in_tc_attr[];\n"
4944												 "\n"
4945												 "void main (void)\n"
4946												 "{\n"
4947												 "	gl_TessLevelInner[0] = in_tc_attr[0];\n"
4948												 "	gl_TessLevelInner[1] = in_tc_attr[1];\n"
4949												 "\n"
4950												 "	gl_TessLevelOuter[0] = in_tc_attr[2];\n"
4951												 "	gl_TessLevelOuter[1] = in_tc_attr[3];\n"
4952												 "	gl_TessLevelOuter[2] = in_tc_attr[4];\n"
4953												 "	gl_TessLevelOuter[3] = in_tc_attr[5];\n"
4954												 "}\n")
4955
4956		<< glu::TessellationEvaluationSource	("#version 310 es\n"
4957												 "#extension GL_EXT_tessellation_shader : require\n"
4958												 "\n"
4959												 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) +
4960												 "\n"
4961												 "out highp vec4 in_f_color;\n"
4962												 "out highp vec3 out_te_output;\n"
4963												 "\n"
4964												 "void main (void)\n"
4965												 "{\n"
4966												 + tessEvalOutputComponentStatements("gl_TessCoord.x", "out_te_output.x")
4967												 + tessEvalOutputComponentStatements("gl_TessCoord.y", "out_te_output.y")
4968
4969												 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
4970													tessEvalOutputComponentStatements("gl_TessCoord.z", "out_te_output.z") :
4971													"	out_te_output.z = 0.0f;\n") +
4972												 "	gl_Position = vec4(gl_TessCoord.xy, 0.0, 1.0);\n"
4973												 "	in_f_color = vec4(1.0);\n"
4974												 "}\n")
4975
4976		<< glu::FragmentSource					("#version 310 es\n"
4977												 "\n"
4978												 "layout (location = 0) out mediump vec4 o_color;\n"
4979												 "\n"
4980												 "in highp vec4 in_f_color;\n"
4981												 "\n"
4982												 "void main (void)\n"
4983												 "{\n"
4984												 "	o_color = in_f_color;\n"
4985												 "}\n")
4986
4987		<< glu::TransformFeedbackVarying		("out_te_output")
4988		<< glu::TransformFeedbackMode			(GL_INTERLEAVED_ATTRIBS)));
4989
4990	m_testCtx.getLog() << *m_program;
4991	if (!m_program->isOk())
4992		TCU_FAIL("Program compilation failed");
4993}
4994
4995void TessCoordComponentInvarianceCase::deinit (void)
4996{
4997	m_program.clear();
4998}
4999
5000vector<float> TessCoordComponentInvarianceCase::genTessLevelCases (int numCases)
5001{
5002	de::Random		rnd(123);
5003	vector<float>	result;
5004
5005	for (int i = 0; i < numCases; i++)
5006		for (int j = 0; j < 6; j++)
5007			result.push_back(rnd.getFloat(1.0f, 63.0f));
5008
5009	return result;
5010}
5011
5012TessCoordComponentInvarianceCase::IterateResult TessCoordComponentInvarianceCase::iterate (void)
5013{
5014	typedef TransformFeedbackHandler<Vec3> TFHandler;
5015
5016	TestLog&				log					= m_testCtx.getLog();
5017	const RenderContext&	renderCtx			= m_context.getRenderContext();
5018	const RandomViewport	viewport			(renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
5019	const glw::Functions&	gl					= renderCtx.getFunctions();
5020	const int				numTessLevelCases	= 32;
5021	const vector<float>		tessLevels			= genTessLevelCases(numTessLevelCases);
5022	const deUint32			programGL			= m_program->getProgram();
5023
5024	gl.useProgram(programGL);
5025	setViewport(gl, viewport);
5026	gl.patchParameteri(GL_PATCH_VERTICES, 6);
5027
5028	{
5029		// Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once.
5030		int maxNumVerticesInDrawCall = 0;
5031		for (int i = 0; i < numTessLevelCases; i++)
5032			maxNumVerticesInDrawCall = de::max(maxNumVerticesInDrawCall, referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &tessLevels[6*i+0], &tessLevels[6*i+2]));
5033
5034		{
5035			const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall);
5036
5037			for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < numTessLevelCases; tessLevelCaseNdx++)
5038			{
5039				log << TestLog::Message << "Testing with tessellation levels: " << tessellationLevelsString(&tessLevels[6*tessLevelCaseNdx+0], &tessLevels[6*tessLevelCaseNdx+2]) << TestLog::EndMessage;
5040
5041				const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)6, 0, &tessLevels[6*tessLevelCaseNdx]) };
5042				const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode),
5043																					DE_LENGTH_OF_ARRAY(bindings), &bindings[0], 6);
5044
5045				for (int vtxNdx = 0; vtxNdx < (int)tfResult.varying.size(); vtxNdx++)
5046				{
5047					const Vec3&		vec			= tfResult.varying[vtxNdx];
5048					const int		numComps	= m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2;
5049
5050					for (int compNdx = 0; compNdx < numComps; compNdx++)
5051					{
5052						if (!checkTessCoordComponent(vec[compNdx]))
5053						{
5054							log << TestLog::Message << "Note: output value at index " << vtxNdx << " is "
5055													<< (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? de::toString(vec) : de::toString(vec.swizzle(0,1)))
5056													<< TestLog::EndMessage;
5057							m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid tessellation coordinate component");
5058							return STOP;
5059						}
5060					}
5061				}
5062			}
5063		}
5064	}
5065
5066	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
5067	return STOP;
5068}
5069
5070/*--------------------------------------------------------------------*//*!
5071 * \brief Test first part of invariance rule #8
5072 *
5073 * Test that all (relevant) components of tess coord are in [0,1].
5074 *//*--------------------------------------------------------------------*/
5075class TessCoordComponentRangeCase : public TessCoordComponentInvarianceCase
5076{
5077public:
5078	TessCoordComponentRangeCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
5079		: TessCoordComponentInvarianceCase(context, name, description, primType, spacing, winding, usePointMode)
5080	{
5081	}
5082
5083protected:
5084	virtual string tessEvalOutputComponentStatements (const char* tessCoordComponentName, const char* outputComponentName) const
5085	{
5086		return string() + "\t" + outputComponentName + " = " + tessCoordComponentName + ";\n";
5087	}
5088
5089	virtual bool checkTessCoordComponent (float component) const
5090	{
5091		if (!de::inRange(component, 0.0f, 1.0f))
5092		{
5093			m_testCtx.getLog() << TestLog::Message << "Failure: tess coord component isn't in range [0,1]" << TestLog::EndMessage;
5094			return false;
5095		}
5096		return true;
5097	}
5098};
5099
5100/*--------------------------------------------------------------------*//*!
5101 * \brief Test second part of invariance rule #8
5102 *
5103 * Test that all (relevant) components of tess coord are in [0,1] and
5104 * 1.0-c is exact for every such component c.
5105 *//*--------------------------------------------------------------------*/
5106class OneMinusTessCoordComponentCase : public TessCoordComponentInvarianceCase
5107{
5108public:
5109	OneMinusTessCoordComponentCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
5110		: TessCoordComponentInvarianceCase(context, name, description, primType, spacing, winding, usePointMode)
5111	{
5112	}
5113
5114protected:
5115	virtual string tessEvalOutputComponentStatements (const char* tessCoordComponentName, const char* outputComponentName) const
5116	{
5117		return string() + "	{\n"
5118						  "		float oneMinusComp = 1.0 - " + tessCoordComponentName + ";\n"
5119						  "		" + outputComponentName + " = " + tessCoordComponentName + " + oneMinusComp;\n"
5120						  "	}\n";
5121	}
5122
5123	virtual bool checkTessCoordComponent (float component) const
5124	{
5125		if (component != 1.0f)
5126		{
5127			m_testCtx.getLog() << TestLog::Message << "Failure: comp + (1.0-comp) doesn't equal 1.0 for some component of tessellation coordinate" << TestLog::EndMessage;
5128			return false;
5129		}
5130		return true;
5131	}
5132};
5133
5134/*--------------------------------------------------------------------*//*!
5135 * \brief Test that patch is discarded if relevant outer level <= 0.0
5136 *
5137 * Draws patches with different combinations of tessellation levels,
5138 * varying which levels are negative. Verifies by checking that colored
5139 * pixels exist inside the area of valid primitives, and only black pixels
5140 * exist inside the area of discarded primitives. An additional sanity
5141 * test is done, checking that the number of primitives written by TF is
5142 * correct.
5143 *//*--------------------------------------------------------------------*/
5144class PrimitiveDiscardCase : public TestCase
5145{
5146public:
5147	PrimitiveDiscardCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
5148		: TestCase			(context, name, description)
5149		, m_primitiveType	(primType)
5150		, m_spacing			(spacing)
5151		, m_winding			(winding)
5152		, m_usePointMode	(usePointMode)
5153	{
5154	}
5155
5156	void									init		(void);
5157	void									deinit		(void);
5158	IterateResult							iterate		(void);
5159
5160private:
5161	static vector<float>					genAttributes (void);
5162
5163	static const int						RENDER_SIZE = 256;
5164
5165	const TessPrimitiveType					m_primitiveType;
5166	const SpacingMode						m_spacing;
5167	const Winding							m_winding;
5168	const bool								m_usePointMode;
5169
5170	SharedPtr<const glu::ShaderProgram>		m_program;
5171};
5172
5173void PrimitiveDiscardCase::init (void)
5174{
5175	checkTessellationSupport(m_context);
5176	checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
5177
5178	m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
5179		<< glu::VertexSource					("#version 310 es\n"
5180												 "\n"
5181												 "in highp float in_v_attr;\n"
5182												 "out highp float in_tc_attr;\n"
5183												 "\n"
5184												 "void main (void)\n"
5185												 "{\n"
5186												 "	in_tc_attr = in_v_attr;\n"
5187												 "}\n")
5188
5189		<< glu::TessellationControlSource		("#version 310 es\n"
5190												 "#extension GL_EXT_tessellation_shader : require\n"
5191												 "\n"
5192												 "layout (vertices = 1) out;\n"
5193												 "\n"
5194												 "in highp float in_tc_attr[];\n"
5195												 "\n"
5196												 "patch out highp vec2 in_te_positionScale;\n"
5197												 "patch out highp vec2 in_te_positionOffset;\n"
5198												 "\n"
5199												 "void main (void)\n"
5200												 "{\n"
5201												 "	in_te_positionScale  = vec2(in_tc_attr[6], in_tc_attr[7]);\n"
5202												 "	in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\n"
5203												 "\n"
5204												 "	gl_TessLevelInner[0] = in_tc_attr[0];\n"
5205												 "	gl_TessLevelInner[1] = in_tc_attr[1];\n"
5206												 "\n"
5207												 "	gl_TessLevelOuter[0] = in_tc_attr[2];\n"
5208												 "	gl_TessLevelOuter[1] = in_tc_attr[3];\n"
5209												 "	gl_TessLevelOuter[2] = in_tc_attr[4];\n"
5210												 "	gl_TessLevelOuter[3] = in_tc_attr[5];\n"
5211												 "}\n")
5212
5213		<< glu::TessellationEvaluationSource	("#version 310 es\n"
5214												 "#extension GL_EXT_tessellation_shader : require\n"
5215												 "\n"
5216												 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) +
5217												 "\n"
5218												 "patch in highp vec2 in_te_positionScale;\n"
5219												 "patch in highp vec2 in_te_positionOffset;\n"
5220												 "\n"
5221												 "out highp vec3 out_te_tessCoord;\n"
5222												 "\n"
5223												 "void main (void)\n"
5224												 "{\n"
5225												 "	out_te_tessCoord = gl_TessCoord;\n"
5226												 "	gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n"
5227												 "}\n")
5228
5229		<< glu::FragmentSource					("#version 310 es\n"
5230												 "\n"
5231												 "layout (location = 0) out mediump vec4 o_color;\n"
5232												 "\n"
5233												 "void main (void)\n"
5234												 "{\n"
5235												 "	o_color = vec4(1.0);\n"
5236												 "}\n")
5237
5238		<< glu::TransformFeedbackVarying		("out_te_tessCoord")
5239		<< glu::TransformFeedbackMode			(GL_INTERLEAVED_ATTRIBS)));
5240
5241	m_testCtx.getLog() << *m_program;
5242	if (!m_program->isOk())
5243		TCU_FAIL("Program compilation failed");
5244}
5245
5246void PrimitiveDiscardCase::deinit (void)
5247{
5248	m_program.clear();
5249}
5250
5251vector<float> PrimitiveDiscardCase::genAttributes (void)
5252{
5253	// Generate input attributes (tessellation levels, and position scale and
5254	// offset) for a number of primitives. Each primitive has a different
5255	// combination of tessellatio levels; each level is either a valid
5256	// value or an "invalid" value (negative or zero, chosen from
5257	// invalidTessLevelChoices).
5258
5259	// \note The attributes are generated in such an order that all of the
5260	//		 valid attribute tuples come before the first invalid one both
5261	//		 in the result vector, and when scanning the resulting 2d grid
5262	//		 of primitives is scanned in y-major order. This makes
5263	//		 verification somewhat simpler.
5264
5265	static const float	baseTessLevels[6]			= { 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f };
5266	static const float	invalidTessLevelChoices[]	= { -0.42f, 0.0f };
5267	const int			numChoices					= 1 + DE_LENGTH_OF_ARRAY(invalidTessLevelChoices);
5268	float				choices[6][numChoices];
5269	vector<float>		result;
5270
5271	for (int levelNdx = 0; levelNdx < 6; levelNdx++)
5272		for (int choiceNdx = 0; choiceNdx < numChoices; choiceNdx++)
5273			choices[levelNdx][choiceNdx] = choiceNdx == 0 ? baseTessLevels[levelNdx] : invalidTessLevelChoices[choiceNdx-1];
5274
5275	{
5276		const int	numCols			= intPow(numChoices, 6/2); // sqrt(numChoices**6) == sqrt(number of primitives)
5277		const int	numRows			= numCols;
5278		int			index			= 0;
5279		int			i[6];
5280		// We could do this with some generic combination-generation function, but meh, it's not that bad.
5281		for (i[2] = 0; i[2] < numChoices; i[2]++) // First  outer
5282		for (i[3] = 0; i[3] < numChoices; i[3]++) // Second outer
5283		for (i[4] = 0; i[4] < numChoices; i[4]++) // Third  outer
5284		for (i[5] = 0; i[5] < numChoices; i[5]++) // Fourth outer
5285		for (i[0] = 0; i[0] < numChoices; i[0]++) // First  inner
5286		for (i[1] = 0; i[1] < numChoices; i[1]++) // Second inner
5287		{
5288			for (int j = 0; j < 6; j++)
5289				result.push_back(choices[j][i[j]]);
5290
5291			{
5292				const int col = index % numCols;
5293				const int row = index / numCols;
5294				// Position scale.
5295				result.push_back((float)2.0f / (float)numCols);
5296				result.push_back((float)2.0f / (float)numRows);
5297				// Position offset.
5298				result.push_back((float)col / (float)numCols * 2.0f - 1.0f);
5299				result.push_back((float)row / (float)numRows * 2.0f - 1.0f);
5300			}
5301
5302			index++;
5303		}
5304	}
5305
5306	return result;
5307}
5308
5309PrimitiveDiscardCase::IterateResult PrimitiveDiscardCase::iterate (void)
5310{
5311	typedef TransformFeedbackHandler<Vec3> TFHandler;
5312
5313	TestLog&				log						= m_testCtx.getLog();
5314	const RenderContext&	renderCtx				= m_context.getRenderContext();
5315	const RandomViewport	viewport				(renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
5316	const glw::Functions&	gl						= renderCtx.getFunctions();
5317	const vector<float>		attributes				= genAttributes();
5318	const int				numAttribsPerPrimitive	= 6+2+2; // Tess levels, scale, offset.
5319	const int				numPrimitives			= (int)attributes.size() / numAttribsPerPrimitive;
5320	const deUint32			programGL				= m_program->getProgram();
5321
5322	gl.useProgram(programGL);
5323	setViewport(gl, viewport);
5324	gl.patchParameteri(GL_PATCH_VERTICES, numAttribsPerPrimitive);
5325
5326	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
5327	gl.clear(GL_COLOR_BUFFER_BIT);
5328
5329	// Check the convenience assertion that all discarded patches come after the last non-discarded patch.
5330	{
5331		bool discardedPatchEncountered = false;
5332		for (int patchNdx = 0; patchNdx < numPrimitives; patchNdx++)
5333		{
5334			const bool discard = isPatchDiscarded(m_primitiveType, &attributes[numAttribsPerPrimitive*patchNdx + 2]);
5335			DE_ASSERT(discard || !discardedPatchEncountered);
5336			discardedPatchEncountered = discard;
5337		}
5338		DE_UNREF(discardedPatchEncountered);
5339	}
5340
5341	{
5342		int numVerticesInDrawCall = 0;
5343		for (int patchNdx = 0; patchNdx < numPrimitives; patchNdx++)
5344			numVerticesInDrawCall += referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &attributes[numAttribsPerPrimitive*patchNdx+0], &attributes[numAttribsPerPrimitive*patchNdx+2]);
5345
5346		log << TestLog::Message << "Note: rendering " << numPrimitives << " patches; first patches have valid relevant outer levels, "
5347								<< "but later patches have one or more invalid (i.e. less than or equal to 0.0) relevant outer levels" << TestLog::EndMessage;
5348
5349		{
5350			const TFHandler					tfHandler	(m_context.getRenderContext(), numVerticesInDrawCall);
5351			const glu::VertexArrayBinding	bindings[]	= { glu::va::Float("in_v_attr", 1, (int)attributes.size(), 0, &attributes[0]) };
5352			const TFHandler::Result			tfResult	= tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode),
5353																						   DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)attributes.size());
5354			const tcu::Surface				pixels		= getPixels(renderCtx, viewport);
5355
5356			log << TestLog::Image("RenderedImage", "Rendered image", pixels);
5357
5358			if ((int)tfResult.varying.size() != numVerticesInDrawCall)
5359			{
5360				log << TestLog::Message << "Failure: expected " << numVerticesInDrawCall << " vertices from transform feedback, got " << tfResult.varying.size() << TestLog::EndMessage;
5361				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Wrong number of tessellation coordinates");
5362				return STOP;
5363			}
5364
5365			// Check that white pixels are found around every non-discarded
5366			// patch, and that only black pixels are found after the last
5367			// non-discarded patch.
5368			{
5369				int lastWhitePixelRow									= 0;
5370				int secondToLastWhitePixelRow							= 0;
5371				int	lastWhitePixelColumnOnSecondToLastWhitePixelRow		= 0;
5372
5373				for (int patchNdx = 0; patchNdx < numPrimitives; patchNdx++)
5374				{
5375					const float* const	attr			= &attributes[numAttribsPerPrimitive*patchNdx];
5376					const bool			validLevels		= !isPatchDiscarded(m_primitiveType, &attr[2]);
5377
5378					if (validLevels)
5379					{
5380						// Not a discarded patch; check that at least one white pixel is found in its area.
5381
5382						const float* const	scale		= &attr[6];
5383						const float* const	offset		= &attr[8];
5384						const int			x0			= (int)((			offset[0] + 1.0f)*0.5f*(float)pixels.getWidth()) - 1;
5385						const int			x1			= (int)((scale[0] + offset[0] + 1.0f)*0.5f*(float)pixels.getWidth()) + 1;
5386						const int			y0			= (int)((			offset[1] + 1.0f)*0.5f*(float)pixels.getHeight()) - 1;
5387						const int			y1			= (int)((scale[1] + offset[1] + 1.0f)*0.5f*(float)pixels.getHeight()) + 1;
5388						const bool			isMSAA		= renderCtx.getRenderTarget().getNumSamples() > 1;
5389						bool				pixelOk		= false;
5390
5391						if (y1 > lastWhitePixelRow)
5392						{
5393							secondToLastWhitePixelRow	= lastWhitePixelRow;
5394							lastWhitePixelRow			= y1;
5395						}
5396						lastWhitePixelColumnOnSecondToLastWhitePixelRow = x1;
5397
5398						for (int y = y0; y <= y1 && !pixelOk; y++)
5399						for (int x = x0; x <= x1 && !pixelOk; x++)
5400						{
5401							if (!de::inBounds(x, 0, pixels.getWidth()) || !de::inBounds(y, 0, pixels.getHeight()))
5402								continue;
5403
5404							if (isMSAA)
5405							{
5406								if (pixels.getPixel(x, y) != tcu::RGBA::black)
5407									pixelOk = true;
5408							}
5409							else
5410							{
5411								if (pixels.getPixel(x, y) == tcu::RGBA::white)
5412									pixelOk = true;
5413							}
5414						}
5415
5416						if (!pixelOk)
5417						{
5418							log << TestLog::Message << "Failure: expected at least one " << (isMSAA ? "non-black" : "white") << " pixel in the rectangle "
5419													<< "[x0=" << x0 << ", y0=" << y0 << ", x1=" << x1 << ", y1=" << y1 << "]" << TestLog::EndMessage
5420								<< TestLog::Message << "Note: the rectangle approximately corresponds to the patch with these tessellation levels: "
5421													<< tessellationLevelsString(&attr[0], &attr[1]) << TestLog::EndMessage;
5422							m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
5423							return STOP;
5424						}
5425					}
5426					else
5427					{
5428						// First discarded primitive patch; the remaining are guaranteed to be discarded ones as well.
5429
5430						for (int y = 0; y < pixels.getHeight(); y++)
5431						for (int x = 0; x < pixels.getWidth(); x++)
5432						{
5433							if (y > lastWhitePixelRow || (y > secondToLastWhitePixelRow && x > lastWhitePixelColumnOnSecondToLastWhitePixelRow))
5434							{
5435								if (pixels.getPixel(x, y) != tcu::RGBA::black)
5436								{
5437									log << TestLog::Message << "Failure: expected all pixels to be black in the area "
5438															<< (lastWhitePixelColumnOnSecondToLastWhitePixelRow < pixels.getWidth()-1
5439																	? string() + "y > " + de::toString(lastWhitePixelRow) + " || (y > " + de::toString(secondToLastWhitePixelRow)
5440																			   + " && x > " + de::toString(lastWhitePixelColumnOnSecondToLastWhitePixelRow) + ")"
5441																	: string() + "y > " + de::toString(lastWhitePixelRow))
5442															<< " (they all correspond to patches that should be discarded)" << TestLog::EndMessage
5443										<< TestLog::Message << "Note: pixel " << tcu::IVec2(x, y) << " isn't black" << TestLog::EndMessage;
5444									m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
5445									return STOP;
5446								}
5447							}
5448						}
5449
5450						break;
5451					}
5452				}
5453			}
5454		}
5455	}
5456
5457	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
5458	return STOP;
5459}
5460
5461/*--------------------------------------------------------------------*//*!
5462 * \brief Case testing user-defined IO between TCS and TES
5463 *
5464 * TCS outputs various values to TES, including aggregates. The outputs
5465 * can be per-patch or per-vertex, and if per-vertex, they can also be in
5466 * an IO block. Per-vertex input array size can be left implicit (i.e.
5467 * inputArray[]) or explicit either by gl_MaxPatchVertices or an integer
5468 * literal whose value is queried from GL.
5469 *
5470 * The values output are generated in TCS and verified in TES against
5471 * similarly generated values. In case a verification of a value fails, the
5472 * index of the invalid value is output with TF.
5473 * As a sanity check, also the rendering result is verified (against pre-
5474 * rendered reference).
5475 *//*--------------------------------------------------------------------*/
5476class UserDefinedIOCase : public TestCase
5477{
5478public:
5479	enum IOType
5480	{
5481		IO_TYPE_PER_PATCH = 0,
5482		IO_TYPE_PER_PATCH_ARRAY,
5483		IO_TYPE_PER_VERTEX,
5484		IO_TYPE_PER_VERTEX_BLOCK,
5485
5486		IO_TYPE_LAST
5487	};
5488
5489	enum VertexIOArraySize
5490	{
5491		VERTEX_IO_ARRAY_SIZE_IMPLICIT = 0,
5492		VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN,		//!< Use gl_MaxPatchVertices as size for per-vertex input array.
5493		VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY,				//!< Query GL_MAX_PATCH_VERTICES, and use that as size for per-vertex input array.
5494
5495		VERTEX_IO_ARRAY_SIZE_LAST
5496	};
5497
5498	UserDefinedIOCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, IOType ioType, VertexIOArraySize vertexIOArraySize, const char* referenceImagePath)
5499		: TestCase				(context, name, description)
5500		, m_primitiveType		(primType)
5501		, m_ioType				(ioType)
5502		, m_vertexIOArraySize	(vertexIOArraySize)
5503		, m_referenceImagePath	(referenceImagePath)
5504	{
5505	}
5506
5507	void									init		(void);
5508	void									deinit		(void);
5509	IterateResult							iterate		(void);
5510
5511private:
5512	typedef string (*BasicTypeVisitFunc)(const string& name, glu::DataType type, int indentationDepth); //!< See glslTraverseBasicTypes below.
5513
5514	class TopLevelObject
5515	{
5516	public:
5517		virtual			~TopLevelObject					(void) {}
5518
5519		virtual string	name							(void) const = 0;
5520		virtual string	declare							(const string& arraySizeExpr) const = 0;
5521		virtual string	glslTraverseBasicTypes			(int numArrayElements, //!< If negative, traverse just array[gl_InvocationID], not all indices.
5522														 int indentationDepth,
5523														 BasicTypeVisitFunc) const = 0;
5524		virtual int		numBasicSubobjectsInElementType	(void) const = 0;
5525		virtual string	basicSubobjectAtIndex			(int index, int arraySize) const = 0;
5526	};
5527
5528	class Variable : public TopLevelObject
5529	{
5530	public:
5531		Variable (const string& name_, const glu::VarType& type, bool isArray)
5532			: m_name		(name_)
5533			, m_type		(type)
5534			, m_isArray		(isArray)
5535		{
5536			DE_ASSERT(!type.isArrayType());
5537		}
5538
5539		string	name								(void) const { return m_name; }
5540		string	declare								(const string& arraySizeExpr) const;
5541		string	glslTraverseBasicTypes				(int numArrayElements, int indentationDepth, BasicTypeVisitFunc) const;
5542		int		numBasicSubobjectsInElementType		(void) const;
5543		string	basicSubobjectAtIndex				(int index, int arraySize) const;
5544
5545	private:
5546		string			m_name;
5547		glu::VarType	m_type; //!< If this Variable is an array element, m_type is the element type; otherwise just the variable type.
5548		const bool		m_isArray;
5549	};
5550
5551	class IOBlock : public TopLevelObject
5552	{
5553	public:
5554		struct Member
5555		{
5556			string			name;
5557			glu::VarType	type;
5558			Member (const string& n, const glu::VarType& t) : name(n), type(t) {}
5559		};
5560
5561		IOBlock (const string& blockName, const string& interfaceName, const vector<Member>& members)
5562			: m_blockName		(blockName)
5563			, m_interfaceName	(interfaceName)
5564			, m_members			(members)
5565		{
5566		}
5567
5568		string	name								(void) const { return m_interfaceName; }
5569		string	declare								(const string& arraySizeExpr) const;
5570		string	glslTraverseBasicTypes				(int numArrayElements, int indentationDepth, BasicTypeVisitFunc) const;
5571		int		numBasicSubobjectsInElementType		(void) const;
5572		string	basicSubobjectAtIndex				(int index, int arraySize) const;
5573
5574	private:
5575		string			m_blockName;
5576		string			m_interfaceName;
5577		vector<Member>	m_members;
5578	};
5579
5580	static string							glslTraverseBasicTypes				(const string&			rootName,
5581																				 const glu::VarType&	rootType,
5582																				 int					arrayNestingDepth,
5583																				 int					indentationDepth,
5584																				 BasicTypeVisitFunc		visit);
5585
5586	static string							glslAssignBasicTypeObject			(const string& name, glu::DataType, int indentationDepth);
5587	static string							glslCheckBasicTypeObject			(const string& name, glu::DataType, int indentationDepth);
5588	static int								numBasicSubobjectsInElementType		(const vector<SharedPtr<TopLevelObject> >&);
5589	static string							basicSubobjectAtIndex				(int index, const vector<SharedPtr<TopLevelObject> >&, int topLevelArraySizes);
5590
5591	static const int						RENDER_SIZE = 256;
5592	static const int						NUM_OUTPUT_VERTICES;
5593	static const int						NUM_PER_PATCH_ARRAY_ELEMS;
5594
5595	const TessPrimitiveType					m_primitiveType;
5596	const IOType							m_ioType;
5597	const VertexIOArraySize					m_vertexIOArraySize;
5598	const string							m_referenceImagePath;
5599
5600	vector<glu::StructType>					m_structTypes;
5601	vector<SharedPtr<TopLevelObject> >		m_tcsOutputs;
5602	vector<SharedPtr<TopLevelObject> >		m_tesInputs;
5603
5604	SharedPtr<const glu::ShaderProgram>		m_program;
5605};
5606
5607const int UserDefinedIOCase::NUM_OUTPUT_VERTICES			= 5;
5608const int UserDefinedIOCase::NUM_PER_PATCH_ARRAY_ELEMS		= 3;
5609
5610/*--------------------------------------------------------------------*//*!
5611 * \brief Generate GLSL code to traverse (possibly aggregate) object
5612 *
5613 * Generates a string that represents GLSL code that traverses the
5614 * basic-type subobjects in a rootType-typed object named rootName. Arrays
5615 * are traversed with loops and struct members are each traversed
5616 * separately. The code for each basic-type subobject is generated with
5617 * the function given as the 'visit' argument.
5618 *//*--------------------------------------------------------------------*/
5619string UserDefinedIOCase::glslTraverseBasicTypes (const string&			rootName,
5620												  const glu::VarType&	rootType,
5621												  int					arrayNestingDepth,
5622												  int					indentationDepth,
5623												  BasicTypeVisitFunc	visit)
5624{
5625	if (rootType.isBasicType())
5626		return visit(rootName, rootType.getBasicType(), indentationDepth);
5627	else if (rootType.isArrayType())
5628	{
5629		const string indentation	= string(indentationDepth, '\t');
5630		const string loopIndexName	= "i" + de::toString(arrayNestingDepth);
5631		const string arrayLength	= de::toString(rootType.getArraySize());
5632		return indentation + "for (int " + loopIndexName + " = 0; " + loopIndexName + " < " + de::toString(rootType.getArraySize()) + "; " + loopIndexName + "++)\n" +
5633			   indentation + "{\n" +
5634			   glslTraverseBasicTypes(rootName + "[" + loopIndexName + "]", rootType.getElementType(), arrayNestingDepth+1, indentationDepth+1, visit) +
5635			   indentation + "}\n";
5636	}
5637	else if (rootType.isStructType())
5638	{
5639		const glu::StructType&	structType = *rootType.getStructPtr();
5640		const int				numMembers = structType.getNumMembers();
5641		string					result;
5642
5643		for (int membNdx = 0; membNdx < numMembers; membNdx++)
5644		{
5645			const glu::StructMember& member = structType.getMember(membNdx);
5646			result += glslTraverseBasicTypes(rootName + "." + member.getName(), member.getType(), arrayNestingDepth, indentationDepth, visit);
5647		}
5648
5649		return result;
5650	}
5651	else
5652	{
5653		DE_ASSERT(false);
5654		return DE_NULL;
5655	}
5656}
5657
5658string UserDefinedIOCase::Variable::declare (const string& sizeExpr) const
5659{
5660	return de::toString(glu::declare(m_type, m_name)) + (m_isArray ? "[" + sizeExpr + "]" : "") + ";\n";
5661}
5662
5663string UserDefinedIOCase::IOBlock::declare (const string& sizeExpr) const
5664{
5665	string result = m_blockName + "\n" +
5666					"{\n";
5667	for (int i = 0; i < (int)m_members.size(); i++)
5668		result += "\t" + de::toString(glu::declare(m_members[i].type, m_members[i].name)) + ";\n";
5669	result += "} " + m_interfaceName + "[" + sizeExpr + "]" + ";\n";
5670	return result;
5671}
5672
5673string UserDefinedIOCase::Variable::glslTraverseBasicTypes (int numArrayElements, int indentationDepth, BasicTypeVisitFunc visit) const
5674{
5675	const bool				traverseAsArray		= m_isArray && numArrayElements >= 0;
5676	const string			traversedName		= m_name + (m_isArray && !traverseAsArray ? "[gl_InvocationID]" : "");
5677	const glu::VarType		type				= traverseAsArray ? glu::VarType(m_type, numArrayElements) : m_type;
5678
5679	return UserDefinedIOCase::glslTraverseBasicTypes(traversedName, type, 0, indentationDepth, visit);
5680}
5681
5682string UserDefinedIOCase::IOBlock::glslTraverseBasicTypes (int numArrayElements, int indentationDepth, BasicTypeVisitFunc visit) const
5683{
5684	if (numArrayElements >= 0)
5685	{
5686		const string	indentation			= string(indentationDepth, '\t');
5687		string			result				= indentation + "for (int i0 = 0; i0 < " + de::toString(numArrayElements) + "; i0++)\n" +
5688											  indentation + "{\n";
5689		for (int i = 0; i < (int)m_members.size(); i++)
5690			result += UserDefinedIOCase::glslTraverseBasicTypes(m_interfaceName + "[i0]." + m_members[i].name, m_members[i].type, 1, indentationDepth+1, visit);
5691		result += indentation + "}\n";
5692		return result;
5693	}
5694	else
5695	{
5696		string result;
5697		for (int i = 0; i < (int)m_members.size(); i++)
5698			result += UserDefinedIOCase::glslTraverseBasicTypes(m_interfaceName + "[gl_InvocationID]." + m_members[i].name, m_members[i].type, 0, indentationDepth, visit);
5699		return result;
5700	}
5701}
5702
5703int UserDefinedIOCase::Variable::numBasicSubobjectsInElementType (void) const
5704{
5705	return numBasicSubobjects(m_type);
5706}
5707
5708int UserDefinedIOCase::IOBlock::numBasicSubobjectsInElementType (void) const
5709{
5710	int result = 0;
5711	for (int i = 0; i < (int)m_members.size(); i++)
5712		result += numBasicSubobjects(m_members[i].type);
5713	return result;
5714}
5715
5716string UserDefinedIOCase::Variable::basicSubobjectAtIndex (int subobjectIndex, int arraySize) const
5717{
5718	const glu::VarType	type			= m_isArray ? glu::VarType(m_type, arraySize) : m_type;
5719	int					currentIndex	= 0;
5720
5721	for (glu::BasicTypeIterator basicIt = glu::BasicTypeIterator::begin(&type);
5722		 basicIt != glu::BasicTypeIterator::end(&type);
5723		 ++basicIt)
5724	{
5725		if (currentIndex == subobjectIndex)
5726			return m_name + de::toString(glu::TypeAccessFormat(type, basicIt.getPath()));
5727		currentIndex++;
5728	}
5729	DE_ASSERT(false);
5730	return DE_NULL;
5731}
5732
5733string UserDefinedIOCase::IOBlock::basicSubobjectAtIndex (int subobjectIndex, int arraySize) const
5734{
5735	int currentIndex = 0;
5736	for (int arrayNdx = 0; arrayNdx < arraySize; arrayNdx++)
5737	{
5738		for (int memberNdx = 0; memberNdx < (int)m_members.size(); memberNdx++)
5739		{
5740			const glu::VarType& membType = m_members[memberNdx].type;
5741			for (glu::BasicTypeIterator basicIt = glu::BasicTypeIterator::begin(&membType);
5742				 basicIt != glu::BasicTypeIterator::end(&membType);
5743				 ++basicIt)
5744			{
5745				if (currentIndex == subobjectIndex)
5746					return m_interfaceName + "[" + de::toString(arrayNdx) + "]." + m_members[memberNdx].name + de::toString(glu::TypeAccessFormat(membType, basicIt.getPath()));
5747				currentIndex++;
5748			}
5749		}
5750	}
5751	DE_ASSERT(false);
5752	return DE_NULL;
5753}
5754
5755// Used as the 'visit' argument for glslTraverseBasicTypes.
5756string UserDefinedIOCase::glslAssignBasicTypeObject (const string& name, glu::DataType type, int indentationDepth)
5757{
5758	const int		scalarSize		= glu::getDataTypeScalarSize(type);
5759	const string	indentation		= string(indentationDepth, '\t');
5760	string			result;
5761
5762	result += indentation + name + " = ";
5763
5764	if (type != glu::TYPE_FLOAT)
5765		result += string() + glu::getDataTypeName(type) + "(";
5766	for (int i = 0; i < scalarSize; i++)
5767		result += (i > 0 ? ", v+" + de::floatToString(0.8f*(float)i, 1)
5768						 : "v");
5769	if (type != glu::TYPE_FLOAT)
5770		result += ")";
5771	result += ";\n" +
5772			  indentation + "v += 0.4;\n";
5773	return result;
5774}
5775
5776// Used as the 'visit' argument for glslTraverseBasicTypes.
5777string UserDefinedIOCase::glslCheckBasicTypeObject (const string& name, glu::DataType type, int indentationDepth)
5778{
5779	const int		scalarSize		= glu::getDataTypeScalarSize(type);
5780	const string	indentation		= string(indentationDepth, '\t');
5781	string			result;
5782
5783	result += indentation + "allOk = allOk && compare_" + glu::getDataTypeName(type) + "(" + name + ", ";
5784
5785	if (type != glu::TYPE_FLOAT)
5786		result += string() + glu::getDataTypeName(type) + "(";
5787	for (int i = 0; i < scalarSize; i++)
5788		result += (i > 0 ? ", v+" + de::floatToString(0.8f*(float)i, 1)
5789						 : "v");
5790	if (type != glu::TYPE_FLOAT)
5791		result += ")";
5792	result += ");\n" +
5793			  indentation + "v += 0.4;\n" +
5794			  indentation + "if (allOk) firstFailedInputIndex++;\n";
5795
5796	return result;
5797}
5798
5799int UserDefinedIOCase::numBasicSubobjectsInElementType (const vector<SharedPtr<TopLevelObject> >& objects)
5800{
5801	int result = 0;
5802	for (int i = 0; i < (int)objects.size(); i++)
5803		result += objects[i]->numBasicSubobjectsInElementType();
5804	return result;
5805}
5806
5807string UserDefinedIOCase::basicSubobjectAtIndex (int subobjectIndex, const vector<SharedPtr<TopLevelObject> >& objects, int topLevelArraySize)
5808{
5809	int currentIndex	= 0;
5810	int objectIndex		= 0;
5811	for (; currentIndex < subobjectIndex; objectIndex++)
5812		currentIndex += objects[objectIndex]->numBasicSubobjectsInElementType() * topLevelArraySize;
5813	if (currentIndex > subobjectIndex)
5814	{
5815		objectIndex--;
5816		currentIndex -= objects[objectIndex]->numBasicSubobjectsInElementType() * topLevelArraySize;
5817	}
5818
5819	return objects[objectIndex]->basicSubobjectAtIndex(subobjectIndex - currentIndex, topLevelArraySize);
5820}
5821
5822void UserDefinedIOCase::init (void)
5823{
5824	checkTessellationSupport(m_context);
5825	checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
5826
5827	const bool			isPerPatchIO				= m_ioType == IO_TYPE_PER_PATCH || m_ioType == IO_TYPE_PER_PATCH_ARRAY;
5828	const bool			isExplicitVertexArraySize	= m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN ||
5829													  m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY;
5830
5831	const string		vertexAttrArrayInputSize	= m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_IMPLICIT					? ""
5832													: m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN	? "gl_MaxPatchVertices"
5833													: m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY			? de::toString(m_context.getContextInfo().getInt(GL_MAX_PATCH_VERTICES))
5834													: DE_NULL;
5835
5836	const char* const	maybePatch					= isPerPatchIO ? "patch " : "";
5837	const string		outMaybePatch				= string() + maybePatch + "out ";
5838	const string		inMaybePatch				= string() + maybePatch + "in ";
5839	const bool			useBlock					= m_ioType == IO_TYPE_PER_VERTEX_BLOCK;
5840
5841	string				tcsDeclarations;
5842	string				tcsStatements;
5843
5844	string				tesDeclarations;
5845	string				tesStatements;
5846
5847	{
5848		m_structTypes.push_back(glu::StructType("S"));
5849
5850		const glu::VarType	highpFloat		(glu::TYPE_FLOAT, glu::PRECISION_HIGHP);
5851		glu::StructType&	structType		= m_structTypes.back();
5852		const glu::VarType	structVarType	(&structType);
5853
5854		structType.addMember("x", glu::VarType(glu::TYPE_INT, glu::PRECISION_HIGHP));
5855		structType.addMember("y", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP));
5856		structType.addMember("z", glu::VarType(highpFloat, 2));
5857
5858		if (useBlock)
5859		{
5860			vector<IOBlock::Member> blockMembers;
5861			blockMembers.push_back(IOBlock::Member("blockS",	structVarType));
5862			blockMembers.push_back(IOBlock::Member("blockFa",	glu::VarType(highpFloat, 3)));
5863			blockMembers.push_back(IOBlock::Member("blockSa",	glu::VarType(structVarType, 2)));
5864			blockMembers.push_back(IOBlock::Member("blockF",	highpFloat));
5865
5866			m_tcsOutputs.push_back	(SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "tcBlock", blockMembers)));
5867			m_tesInputs.push_back	(SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "teBlock", blockMembers)));
5868		}
5869		else
5870		{
5871			const Variable var0("in_te_s", structVarType,	m_ioType != IO_TYPE_PER_PATCH);
5872			const Variable var1("in_te_f", highpFloat,		m_ioType != IO_TYPE_PER_PATCH);
5873
5874			m_tcsOutputs.push_back	(SharedPtr<TopLevelObject>(new Variable(var0)));
5875			m_tesInputs.push_back	(SharedPtr<TopLevelObject>(new Variable(var0)));
5876			m_tcsOutputs.push_back	(SharedPtr<TopLevelObject>(new Variable(var1)));
5877			m_tesInputs.push_back	(SharedPtr<TopLevelObject>(new Variable(var1)));
5878		}
5879
5880		tcsDeclarations += "in " + Variable("in_tc_attr", highpFloat, true).declare(vertexAttrArrayInputSize);
5881		tcsDeclarations += de::toString(glu::declare(structType)) + ";\n";
5882		tcsStatements += "\t{\n"
5883						 "\t\thighp float v = 1.3;\n";
5884
5885		for (int tcsOutputNdx = 0; tcsOutputNdx < (int)m_tcsOutputs.size(); tcsOutputNdx++)
5886		{
5887			const TopLevelObject& output = *m_tcsOutputs[tcsOutputNdx];
5888
5889			tcsDeclarations += outMaybePatch + output.declare(m_ioType == IO_TYPE_PER_PATCH_ARRAY	? de::toString(NUM_PER_PATCH_ARRAY_ELEMS)
5890															  : isExplicitVertexArraySize			? de::toString(NUM_OUTPUT_VERTICES)
5891															  : "");
5892			if (!isPerPatchIO)
5893				tcsStatements += "\t\tv += float(gl_InvocationID)*" + de::floatToString(0.4f*output.numBasicSubobjectsInElementType(), 1) + ";\n";
5894
5895			tcsStatements += "\n\t\t// Assign values to output " + output.name() + "\n" +
5896							 output.glslTraverseBasicTypes(isPerPatchIO ? NUM_PER_PATCH_ARRAY_ELEMS : -1, 2, glslAssignBasicTypeObject);
5897
5898			if (!isPerPatchIO)
5899				tcsStatements += "\t\tv += float(" + de::toString(NUM_OUTPUT_VERTICES) + "-gl_InvocationID-1)*" + de::floatToString(0.4f*output.numBasicSubobjectsInElementType(), 1) + ";\n";
5900		}
5901		tcsStatements += "\t}\n";
5902
5903		tesDeclarations += de::toString(glu::declare(structType)) + ";\n";
5904		tesStatements += "\tbool allOk = true;\n"
5905						 "\thighp uint firstFailedInputIndex = 0u;\n"
5906						 "\t{\n"
5907						 "\t\thighp float v = 1.3;\n";
5908		for (int tesInputNdx = 0; tesInputNdx < (int)m_tesInputs.size(); tesInputNdx++)
5909		{
5910			const TopLevelObject& input = *m_tesInputs[tesInputNdx];
5911			tesDeclarations += inMaybePatch + input.declare(m_ioType == IO_TYPE_PER_PATCH_ARRAY	? de::toString(NUM_PER_PATCH_ARRAY_ELEMS)
5912														  : isExplicitVertexArraySize			? de::toString(vertexAttrArrayInputSize)
5913														  : "");
5914			tesStatements += "\n\t\t// Check values in input " + input.name() + "\n" +
5915							 input.glslTraverseBasicTypes(isPerPatchIO ? NUM_PER_PATCH_ARRAY_ELEMS : NUM_OUTPUT_VERTICES, 2, glslCheckBasicTypeObject);
5916		}
5917		tesStatements += "\t}\n";
5918	}
5919
5920	m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
5921		<< glu::VertexSource					("#version 310 es\n"
5922												 "\n"
5923												 "in highp float in_v_attr;\n"
5924												 "out highp float in_tc_attr;\n"
5925												 "\n"
5926												 "void main (void)\n"
5927												 "{\n"
5928												 "	in_tc_attr = in_v_attr;\n"
5929												 "}\n")
5930
5931		<< glu::TessellationControlSource		("#version 310 es\n"
5932												 "#extension GL_EXT_tessellation_shader : require\n"
5933												 "\n"
5934												 "layout (vertices = " + de::toString(NUM_OUTPUT_VERTICES) + ") out;\n"
5935												 "\n"
5936												 + tcsDeclarations +
5937												 "\n"
5938												 "patch out highp vec2 in_te_positionScale;\n"
5939												 "patch out highp vec2 in_te_positionOffset;\n"
5940												 "\n"
5941												 "void main (void)\n"
5942												 "{\n"
5943												 + tcsStatements +
5944												 "\n"
5945												 "	in_te_positionScale  = vec2(in_tc_attr[6], in_tc_attr[7]);\n"
5946												 "	in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\n"
5947												 "\n"
5948												 "	gl_TessLevelInner[0] = in_tc_attr[0];\n"
5949												 "	gl_TessLevelInner[1] = in_tc_attr[1];\n"
5950												 "\n"
5951												 "	gl_TessLevelOuter[0] = in_tc_attr[2];\n"
5952												 "	gl_TessLevelOuter[1] = in_tc_attr[3];\n"
5953												 "	gl_TessLevelOuter[2] = in_tc_attr[4];\n"
5954												 "	gl_TessLevelOuter[3] = in_tc_attr[5];\n"
5955												 "}\n")
5956
5957		<< glu::TessellationEvaluationSource	("#version 310 es\n"
5958												 "#extension GL_EXT_tessellation_shader : require\n"
5959												 "\n"
5960												 + getTessellationEvaluationInLayoutString(m_primitiveType) +
5961												 "\n"
5962												 + tesDeclarations +
5963												 "\n"
5964												 "patch in highp vec2 in_te_positionScale;\n"
5965												 "patch in highp vec2 in_te_positionOffset;\n"
5966												 "\n"
5967												 "out highp vec4 in_f_color;\n"
5968												 "// Will contain the index of the first incorrect input,\n"
5969												 "// or the number of inputs if all are correct\n"
5970												 "flat out highp uint out_te_firstFailedInputIndex;\n"
5971												 "\n"
5972												 "bool compare_int   (int   a, int   b) { return a == b; }\n"
5973												 "bool compare_float (float a, float b) { return abs(a - b) < 0.01f; }\n"
5974												 "bool compare_vec4  (vec4  a, vec4  b) { return all(lessThan(abs(a - b), vec4(0.01f))); }\n"
5975												 "\n"
5976												 "void main (void)\n"
5977												 "{\n"
5978												 + tesStatements +
5979												 "\n"
5980												 "	gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n"
5981												 "	in_f_color = allOk ? vec4(0.0, 1.0, 0.0, 1.0)\n"
5982												 "	                   : vec4(1.0, 0.0, 0.0, 1.0);\n"
5983												 "	out_te_firstFailedInputIndex = firstFailedInputIndex;\n"
5984												 "}\n")
5985
5986		<< glu::FragmentSource					("#version 310 es\n"
5987												 "\n"
5988												 "layout (location = 0) out mediump vec4 o_color;\n"
5989												 "\n"
5990												 "in highp vec4 in_f_color;\n"
5991												 "\n"
5992												 "void main (void)\n"
5993												 "{\n"
5994												 "	o_color = in_f_color;\n"
5995												 "}\n")
5996
5997		<< glu::TransformFeedbackVarying		("out_te_firstFailedInputIndex")
5998		<< glu::TransformFeedbackMode			(GL_INTERLEAVED_ATTRIBS)));
5999
6000	m_testCtx.getLog() << *m_program;
6001	if (!m_program->isOk())
6002		TCU_FAIL("Program compilation failed");
6003}
6004
6005void UserDefinedIOCase::deinit (void)
6006{
6007	m_program.clear();
6008}
6009
6010UserDefinedIOCase::IterateResult UserDefinedIOCase::iterate (void)
6011{
6012	typedef TransformFeedbackHandler<deUint32> TFHandler;
6013
6014	TestLog&				log						= m_testCtx.getLog();
6015	const RenderContext&	renderCtx				= m_context.getRenderContext();
6016	const RandomViewport	viewport				(renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
6017	const glw::Functions&	gl						= renderCtx.getFunctions();
6018	static const float		attributes[6+2+2]		= { /* inner */ 3.0f, 4.0f, /* outer */ 5.0f, 6.0f, 7.0f, 8.0f, /* pos. scale */ 1.2f, 1.3f, /* pos. offset */ -0.3f, -0.4f };
6019	const deUint32			programGL				= m_program->getProgram();
6020	const int				numVertices				= referenceVertexCount(m_primitiveType, SPACINGMODE_EQUAL, false, &attributes[0], &attributes[2]);
6021	const TFHandler			tfHandler				(renderCtx, numVertices);
6022
6023	gl.useProgram(programGL);
6024	setViewport(gl, viewport);
6025	gl.patchParameteri(GL_PATCH_VERTICES, DE_LENGTH_OF_ARRAY(attributes));
6026
6027	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
6028	gl.clear(GL_COLOR_BUFFER_BIT);
6029
6030	{
6031		const glu::VertexArrayBinding	bindings[]	= { glu::va::Float("in_v_attr", 1, DE_LENGTH_OF_ARRAY(attributes), 0, &attributes[0]) };
6032		const TFHandler::Result			tfResult	= tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, false),
6033																					   DE_LENGTH_OF_ARRAY(bindings), &bindings[0], DE_LENGTH_OF_ARRAY(attributes));
6034
6035		{
6036			const tcu::Surface			pixels		= getPixels(renderCtx, viewport);
6037			const tcu::TextureLevel		reference	= getPNG(m_testCtx.getArchive(), m_referenceImagePath.c_str());
6038			const bool					success		= tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), pixels.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
6039
6040			if (!success)
6041			{
6042				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
6043				return STOP;
6044			}
6045		}
6046
6047		if ((int)tfResult.varying.size() != numVertices)
6048		{
6049			log << TestLog::Message << "Failure: transform feedback returned " << tfResult.varying.size() << " vertices; expected " << numVertices << TestLog::EndMessage;
6050			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Wrong number of vertices");
6051			return STOP;
6052		}
6053
6054		{
6055			const int topLevelArraySize		= (m_ioType == IO_TYPE_PER_PATCH		? 1
6056											 : m_ioType == IO_TYPE_PER_PATCH_ARRAY	? NUM_PER_PATCH_ARRAY_ELEMS
6057											 : NUM_OUTPUT_VERTICES);
6058			const int numTEInputs			= numBasicSubobjectsInElementType(m_tesInputs) * topLevelArraySize;
6059
6060			for (int vertexNdx = 0; vertexNdx < (int)numVertices; vertexNdx++)
6061			{
6062				if (tfResult.varying[vertexNdx] > (deUint32)numTEInputs)
6063				{
6064					log << TestLog::Message << "Failure: out_te_firstFailedInputIndex has value " << tfResult.varying[vertexNdx]
6065											<< ", should be in range [0, " << numTEInputs << "]" << TestLog::EndMessage;
6066					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid transform feedback output");
6067					return STOP;
6068				}
6069				else if (tfResult.varying[vertexNdx] != (deUint32)numTEInputs)
6070				{
6071					log << TestLog::Message << "Failure: in tessellation evaluation shader, check for input "
6072											<< basicSubobjectAtIndex(tfResult.varying[vertexNdx], m_tesInputs, topLevelArraySize) << " failed" << TestLog::EndMessage;
6073					m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid input value in tessellation evaluation shader");
6074					return STOP;
6075				}
6076			}
6077		}
6078	}
6079
6080	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
6081	return STOP;
6082}
6083
6084/*--------------------------------------------------------------------*//*!
6085 * \brief Pass gl_Position between VS and TCS, or between TCS and TES.
6086 *
6087 * In TCS gl_Position is in the gl_out[] block and in TES in the gl_in[]
6088 * block, and has no special semantics in those. Arbitrary vec4 data can
6089 * thus be passed there.
6090 *//*--------------------------------------------------------------------*/
6091class GLPositionCase : public TestCase
6092{
6093public:
6094	enum CaseType
6095	{
6096		CASETYPE_VS_TO_TCS = 0,
6097		CASETYPE_TCS_TO_TES,
6098		CASETYPE_VS_TO_TCS_TO_TES,
6099
6100		CASETYPE_LAST
6101	};
6102
6103	GLPositionCase (Context& context, const char* name, const char* description, CaseType caseType, const char* referenceImagePath)
6104		: TestCase				(context, name, description)
6105		, m_caseType			(caseType)
6106		, m_referenceImagePath	(referenceImagePath)
6107	{
6108	}
6109
6110	void									init				(void);
6111	void									deinit				(void);
6112	IterateResult							iterate				(void);
6113
6114	static const char*						getCaseTypeName		(CaseType type);
6115
6116private:
6117	static const int						RENDER_SIZE = 256;
6118
6119	const CaseType							m_caseType;
6120	const string							m_referenceImagePath;
6121
6122	SharedPtr<const glu::ShaderProgram>		m_program;
6123};
6124
6125const char* GLPositionCase::getCaseTypeName (CaseType type)
6126{
6127	switch (type)
6128	{
6129		case CASETYPE_VS_TO_TCS:			return "gl_position_vs_to_tcs";
6130		case CASETYPE_TCS_TO_TES:			return "gl_position_tcs_to_tes";
6131		case CASETYPE_VS_TO_TCS_TO_TES:		return "gl_position_vs_to_tcs_to_tes";
6132		default:
6133			DE_ASSERT(false); return DE_NULL;
6134	}
6135}
6136
6137void GLPositionCase::init (void)
6138{
6139	checkTessellationSupport(m_context);
6140	checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
6141
6142	const bool		vsToTCS		= m_caseType == CASETYPE_VS_TO_TCS		|| m_caseType == CASETYPE_VS_TO_TCS_TO_TES;
6143	const bool		tcsToTES	= m_caseType == CASETYPE_TCS_TO_TES		|| m_caseType == CASETYPE_VS_TO_TCS_TO_TES;
6144
6145	const string	tesIn0		= tcsToTES ? "gl_in[0].gl_Position" : "in_te_attr[0]";
6146	const string	tesIn1		= tcsToTES ? "gl_in[1].gl_Position" : "in_te_attr[1]";
6147	const string	tesIn2		= tcsToTES ? "gl_in[2].gl_Position" : "in_te_attr[2]";
6148
6149	m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
6150		<< glu::VertexSource					("#version 310 es\n"
6151												 "\n"
6152												 "in highp vec4 in_v_attr;\n"
6153												 + string(!vsToTCS ? "out highp vec4 in_tc_attr;\n" : "") +
6154												 "\n"
6155												 "void main (void)\n"
6156												 "{\n"
6157												 "	" + (vsToTCS ? "gl_Position" : "in_tc_attr") + " = in_v_attr;\n"
6158												 "}\n")
6159
6160		<< glu::TessellationControlSource		("#version 310 es\n"
6161												 "#extension GL_EXT_tessellation_shader : require\n"
6162												 "\n"
6163												 "layout (vertices = 3) out;\n"
6164												 "\n"
6165												 + string(!vsToTCS ? "in highp vec4 in_tc_attr[];\n" : "") +
6166												 "\n"
6167												 + (!tcsToTES ? "out highp vec4 in_te_attr[];\n" : "") +
6168												 "\n"
6169												 "void main (void)\n"
6170												 "{\n"
6171												 "	" + (tcsToTES ? "gl_out[gl_InvocationID].gl_Position" : "in_te_attr[gl_InvocationID]") + " = "
6172													  + (vsToTCS ? "gl_in[gl_InvocationID].gl_Position" : "in_tc_attr[gl_InvocationID]") + ";\n"
6173												 "\n"
6174												 "	gl_TessLevelInner[0] = 2.0;\n"
6175												 "	gl_TessLevelInner[1] = 3.0;\n"
6176												 "\n"
6177												 "	gl_TessLevelOuter[0] = 4.0;\n"
6178												 "	gl_TessLevelOuter[1] = 5.0;\n"
6179												 "	gl_TessLevelOuter[2] = 6.0;\n"
6180												 "	gl_TessLevelOuter[3] = 7.0;\n"
6181												 "}\n")
6182
6183		<< glu::TessellationEvaluationSource	("#version 310 es\n"
6184												 "#extension GL_EXT_tessellation_shader : require\n"
6185												 "\n"
6186												 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_TRIANGLES) +
6187												 "\n"
6188												 + (!tcsToTES ? "in highp vec4 in_te_attr[];\n" : "") +
6189												 "\n"
6190												 "out highp vec4 in_f_color;\n"
6191												 "\n"
6192												 "void main (void)\n"
6193												 "{\n"
6194												 "	highp vec2 xy = gl_TessCoord.x * " + tesIn0 + ".xy\n"
6195												 "	              + gl_TessCoord.y * " + tesIn1 + ".xy\n"
6196												 "	              + gl_TessCoord.z * " + tesIn2 + ".xy;\n"
6197												 "	gl_Position = vec4(xy, 0.0, 1.0);\n"
6198												 "	in_f_color = vec4(" + tesIn0 + ".z + " + tesIn1 + ".w,\n"
6199												 "	                  " + tesIn2 + ".z + " + tesIn0 + ".w,\n"
6200												 "	                  " + tesIn1 + ".z + " + tesIn2 + ".w,\n"
6201												 "	                  1.0);\n"
6202												 "}\n")
6203
6204		<< glu::FragmentSource					("#version 310 es\n"
6205												 "\n"
6206												 "layout (location = 0) out mediump vec4 o_color;\n"
6207												 "\n"
6208												 "in highp vec4 in_f_color;\n"
6209												 "\n"
6210												 "void main (void)\n"
6211												 "{\n"
6212												 "	o_color = in_f_color;\n"
6213												 "}\n")));
6214
6215	m_testCtx.getLog() << *m_program;
6216	if (!m_program->isOk())
6217		TCU_FAIL("Program compilation failed");
6218}
6219
6220void GLPositionCase::deinit (void)
6221{
6222	m_program.clear();
6223}
6224
6225GLPositionCase::IterateResult GLPositionCase::iterate (void)
6226{
6227	TestLog&				log						= m_testCtx.getLog();
6228	const RenderContext&	renderCtx				= m_context.getRenderContext();
6229	const RandomViewport	viewport				(renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
6230	const glw::Functions&	gl						= renderCtx.getFunctions();
6231	const deUint32			programGL				= m_program->getProgram();
6232
6233	static const float attributes[3*4] =
6234	{
6235		-0.8f, -0.7f, 0.1f, 0.7f,
6236		-0.5f,  0.4f, 0.2f, 0.5f,
6237		 0.3f,  0.2f, 0.3f, 0.45f
6238	};
6239
6240	gl.useProgram(programGL);
6241	setViewport(gl, viewport);
6242	gl.patchParameteri(GL_PATCH_VERTICES, 3);
6243
6244	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
6245	gl.clear(GL_COLOR_BUFFER_BIT);
6246
6247	log << TestLog::Message << "Note: input data for in_v_attr:\n" << arrayStr(attributes, 4) << TestLog::EndMessage;
6248
6249	{
6250		const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 4, 3, 0, &attributes[0]) };
6251		glu::draw(renderCtx, programGL, DE_LENGTH_OF_ARRAY(bindings), &bindings[0], glu::pr::Patches(3));
6252
6253		{
6254			const tcu::Surface			pixels		= getPixels(renderCtx, viewport);
6255			const tcu::TextureLevel		reference	= getPNG(m_testCtx.getArchive(), m_referenceImagePath.c_str());
6256			const bool					success		= tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), pixels.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
6257
6258			if (!success)
6259			{
6260				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
6261				return STOP;
6262			}
6263		}
6264	}
6265
6266	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
6267	return STOP;
6268}
6269
6270} // anonymous
6271
6272TessellationTests::TessellationTests (Context& context)
6273	: TestCaseGroup(context, "tessellation", "Tessellation Tests")
6274{
6275}
6276
6277TessellationTests::~TessellationTests (void)
6278{
6279}
6280
6281void TessellationTests::init (void)
6282{
6283	{
6284		TestCaseGroup* const tessCoordGroup = new TestCaseGroup(m_context, "tesscoord", "Get tessellation coordinates with transform feedback and validate them");
6285		addChild(tessCoordGroup);
6286
6287		for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
6288		{
6289			const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
6290
6291			for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
6292				tessCoordGroup->addChild(new TessCoordCase(m_context,
6293														   (string() + getTessPrimitiveTypeShaderName(primitiveType) + "_" + getSpacingModeShaderName((SpacingMode)spacingI)).c_str(), "",
6294														   primitiveType, (SpacingMode)spacingI));
6295		}
6296	}
6297
6298	{
6299		TestCaseGroup* const windingGroup = new TestCaseGroup(m_context, "winding", "Test the cw and ccw input layout qualifiers");
6300		addChild(windingGroup);
6301
6302		for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
6303		{
6304			const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
6305			if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
6306				continue;
6307
6308			for (int windingI = 0; windingI < WINDING_LAST; windingI++)
6309			{
6310				const Winding winding = (Winding)windingI;
6311				windingGroup->addChild(new WindingCase(m_context, (string() + getTessPrimitiveTypeShaderName(primitiveType) + "_" + getWindingShaderName(winding)).c_str(), "", primitiveType, winding));
6312			}
6313		}
6314	}
6315
6316	{
6317		TestCaseGroup* const shaderInputOutputGroup = new TestCaseGroup(m_context, "shader_input_output", "Test tessellation control and evaluation shader inputs and outputs");
6318		addChild(shaderInputOutputGroup);
6319
6320		{
6321			static const struct
6322			{
6323				int inPatchSize;
6324				int outPatchSize;
6325			} patchVertexCountCases[] =
6326			{
6327				{  5, 10 },
6328				{ 10,  5 }
6329			};
6330
6331			for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(patchVertexCountCases); caseNdx++)
6332			{
6333				const int inSize	= patchVertexCountCases[caseNdx].inPatchSize;
6334				const int outSize	= patchVertexCountCases[caseNdx].outPatchSize;
6335
6336				const string caseName = "patch_vertices_" + de::toString(inSize) + "_in_" + de::toString(outSize) + "_out";
6337
6338				shaderInputOutputGroup->addChild(new PatchVertexCountCase(m_context, caseName.c_str(), "Test input and output patch vertex counts", inSize, outSize,
6339																		  ("data/tessellation/" + caseName + "_ref.png").c_str()));
6340			}
6341		}
6342
6343		for (int caseTypeI = 0; caseTypeI < PerPatchDataCase::CASETYPE_LAST; caseTypeI++)
6344		{
6345			const PerPatchDataCase::CaseType	caseType	= (PerPatchDataCase::CaseType)caseTypeI;
6346			const char* const					caseName	= PerPatchDataCase::getCaseTypeName(caseType);
6347
6348			shaderInputOutputGroup->addChild(new PerPatchDataCase(m_context, caseName, PerPatchDataCase::getCaseTypeDescription(caseType), caseType,
6349																  PerPatchDataCase::caseTypeUsesRefImageFromFile(caseType) ? (string() + "data/tessellation/" + caseName + "_ref.png").c_str() : DE_NULL));
6350		}
6351
6352		for (int caseTypeI = 0; caseTypeI < GLPositionCase::CASETYPE_LAST; caseTypeI++)
6353		{
6354			const GLPositionCase::CaseType	caseType	= (GLPositionCase::CaseType)caseTypeI;
6355			const char* const				caseName	= GLPositionCase::getCaseTypeName(caseType);
6356
6357			shaderInputOutputGroup->addChild(new GLPositionCase(m_context, caseName, "", caseType, "data/tessellation/gl_position_ref.png"));
6358		}
6359
6360		shaderInputOutputGroup->addChild(new BarrierCase(m_context, "barrier", "Basic barrier usage", "data/tessellation/barrier_ref.png"));
6361	}
6362
6363	{
6364		TestCaseGroup* const miscDrawGroup = new TestCaseGroup(m_context, "misc_draw", "Miscellaneous draw-result-verifying cases");
6365		addChild(miscDrawGroup);
6366
6367		for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
6368		{
6369			const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
6370			if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
6371				continue;
6372
6373			const char* const primTypeName = getTessPrimitiveTypeShaderName(primitiveType);
6374
6375			for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
6376			{
6377				const string caseName = string() + "fill_cover_" + primTypeName + "_" + getSpacingModeShaderName((SpacingMode)spacingI);
6378
6379				miscDrawGroup->addChild(new BasicTriangleFillCoverCase(m_context,
6380																	   caseName.c_str(), "Check that there are no obvious gaps in the triangle-filled area of a tessellated shape",
6381																	   primitiveType, (SpacingMode)spacingI,
6382																	   ("data/tessellation/" + caseName + "_ref").c_str()));
6383			}
6384		}
6385
6386		for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
6387		{
6388			const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
6389			if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
6390				continue;
6391
6392			const char* const primTypeName = getTessPrimitiveTypeShaderName(primitiveType);
6393
6394			for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
6395			{
6396				const string caseName = string() + "fill_overlap_" + primTypeName + "_" + getSpacingModeShaderName((SpacingMode)spacingI);
6397
6398				miscDrawGroup->addChild(new BasicTriangleFillNonOverlapCase(m_context,
6399																			caseName.c_str(), "Check that there are no obvious triangle overlaps in the triangle-filled area of a tessellated shape",
6400																			primitiveType, (SpacingMode)spacingI,
6401																			("data/tessellation/" + caseName + "_ref").c_str()));
6402			}
6403		}
6404
6405		for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
6406		{
6407			const string caseName = string() + "isolines_" + getSpacingModeShaderName((SpacingMode)spacingI);
6408
6409			miscDrawGroup->addChild(new IsolinesRenderCase(m_context,
6410														   caseName.c_str(), "Basic isolines render test",
6411														   (SpacingMode)spacingI,
6412														   ("data/tessellation/" + caseName + "_ref").c_str()));
6413		}
6414	}
6415
6416	{
6417		TestCaseGroup* const commonEdgeGroup = new TestCaseGroup(m_context, "common_edge", "Draw multiple adjacent shapes and check that no cracks appear between them");
6418		addChild(commonEdgeGroup);
6419
6420		for (int caseTypeI = 0; caseTypeI < CommonEdgeCase::CASETYPE_LAST; caseTypeI++)
6421		{
6422			for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
6423			{
6424				const CommonEdgeCase::CaseType	caseType		= (CommonEdgeCase::CaseType)caseTypeI;
6425				const TessPrimitiveType			primitiveType	= (TessPrimitiveType)primitiveTypeI;
6426				if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
6427						continue;
6428
6429				for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
6430				{
6431					const SpacingMode	spacing		= (SpacingMode)spacingI;
6432					const string		caseName	= (string() + getTessPrimitiveTypeShaderName(primitiveType)
6433																+ "_" + getSpacingModeShaderName(spacing)
6434																+ (caseType == CommonEdgeCase::CASETYPE_BASIC		? ""
6435																 : caseType == CommonEdgeCase::CASETYPE_PRECISE		? "_precise"
6436																 : DE_NULL));
6437
6438					commonEdgeGroup->addChild(new CommonEdgeCase(m_context, caseName.c_str(), "", primitiveType, spacing, caseType));
6439				}
6440			}
6441		}
6442	}
6443
6444	{
6445		TestCaseGroup* const fractionalSpacingModeGroup = new TestCaseGroup(m_context, "fractional_spacing", "Test fractional spacing modes");
6446		addChild(fractionalSpacingModeGroup);
6447
6448		fractionalSpacingModeGroup->addChild(new FractionalSpacingModeCase(m_context, "odd",	"", SPACINGMODE_FRACTIONAL_ODD));
6449		fractionalSpacingModeGroup->addChild(new FractionalSpacingModeCase(m_context, "even",	"", SPACINGMODE_FRACTIONAL_EVEN));
6450	}
6451
6452	{
6453		TestCaseGroup* const primitiveDiscardGroup = new TestCaseGroup(m_context, "primitive_discard", "Test primitive discard with relevant outer tessellation level <= 0.0");
6454		addChild(primitiveDiscardGroup);
6455
6456		for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
6457		{
6458			for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
6459			{
6460				for (int windingI = 0; windingI < WINDING_LAST; windingI++)
6461				{
6462					for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++)
6463					{
6464						const TessPrimitiveType		primitiveType	= (TessPrimitiveType)primitiveTypeI;
6465						const SpacingMode			spacing			= (SpacingMode)spacingI;
6466						const Winding				winding			= (Winding)windingI;
6467						const bool					usePointMode	= usePointModeI != 0;
6468
6469						primitiveDiscardGroup->addChild(new PrimitiveDiscardCase(m_context, (string() + getTessPrimitiveTypeShaderName(primitiveType)
6470																									  + "_" + getSpacingModeShaderName(spacing)
6471																									  + "_" + getWindingShaderName(winding)
6472																									  + (usePointMode ? "_point_mode" : "")).c_str(), "",
6473																				 primitiveType, spacing, winding, usePointMode));
6474					}
6475				}
6476			}
6477		}
6478	}
6479
6480	{
6481		TestCaseGroup* const invarianceGroup							= new TestCaseGroup(m_context, "invariance",						"Test tessellation invariance rules");
6482
6483		TestCaseGroup* const invariantPrimitiveSetGroup					= new TestCaseGroup(m_context, "primitive_set",						"Test invariance rule #1");
6484		TestCaseGroup* const invariantOuterEdgeGroup					= new TestCaseGroup(m_context, "outer_edge_division",				"Test invariance rule #2");
6485		TestCaseGroup* const symmetricOuterEdgeGroup					= new TestCaseGroup(m_context, "outer_edge_symmetry",				"Test invariance rule #3");
6486		TestCaseGroup* const outerEdgeVertexSetIndexIndependenceGroup	= new TestCaseGroup(m_context, "outer_edge_index_independence",		"Test invariance rule #4");
6487		TestCaseGroup* const invariantTriangleSetGroup					= new TestCaseGroup(m_context, "triangle_set",						"Test invariance rule #5");
6488		TestCaseGroup* const invariantInnerTriangleSetGroup				= new TestCaseGroup(m_context, "inner_triangle_set",				"Test invariance rule #6");
6489		TestCaseGroup* const invariantOuterTriangleSetGroup				= new TestCaseGroup(m_context, "outer_triangle_set",				"Test invariance rule #7");
6490		TestCaseGroup* const tessCoordComponentRangeGroup				= new TestCaseGroup(m_context, "tess_coord_component_range",		"Test invariance rule #8, first part");
6491		TestCaseGroup* const oneMinusTessCoordComponentGroup			= new TestCaseGroup(m_context, "one_minus_tess_coord_component",	"Test invariance rule #8, second part");
6492
6493		addChild(invarianceGroup);
6494		invarianceGroup->addChild(invariantPrimitiveSetGroup);
6495		invarianceGroup->addChild(invariantOuterEdgeGroup);
6496		invarianceGroup->addChild(symmetricOuterEdgeGroup);
6497		invarianceGroup->addChild(outerEdgeVertexSetIndexIndependenceGroup);
6498		invarianceGroup->addChild(invariantTriangleSetGroup);
6499		invarianceGroup->addChild(invariantInnerTriangleSetGroup);
6500		invarianceGroup->addChild(invariantOuterTriangleSetGroup);
6501		invarianceGroup->addChild(tessCoordComponentRangeGroup);
6502		invarianceGroup->addChild(oneMinusTessCoordComponentGroup);
6503
6504		for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
6505		{
6506			const TessPrimitiveType		primitiveType	= (TessPrimitiveType)primitiveTypeI;
6507			const string				primName		= getTessPrimitiveTypeShaderName(primitiveType);
6508			const bool					triOrQuad		= primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS;
6509
6510			for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
6511			{
6512				const SpacingMode	spacing			= (SpacingMode)spacingI;
6513				const string		primSpacName	= primName + "_" + getSpacingModeShaderName(spacing);
6514
6515				if (triOrQuad)
6516				{
6517					invariantOuterEdgeGroup->addChild		(new InvariantOuterEdgeCase			(m_context, primSpacName.c_str(), "", primitiveType, spacing));
6518					invariantTriangleSetGroup->addChild		(new InvariantTriangleSetCase		(m_context, primSpacName.c_str(), "", primitiveType, spacing));
6519					invariantInnerTriangleSetGroup->addChild(new InvariantInnerTriangleSetCase	(m_context, primSpacName.c_str(), "", primitiveType, spacing));
6520					invariantOuterTriangleSetGroup->addChild(new InvariantOuterTriangleSetCase	(m_context, primSpacName.c_str(), "", primitiveType, spacing));
6521				}
6522
6523				for (int windingI = 0; windingI < WINDING_LAST; windingI++)
6524				{
6525					const Winding	winding				= (Winding)windingI;
6526					const string	primSpacWindName	= primSpacName + "_" + getWindingShaderName(winding);
6527
6528					for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++)
6529					{
6530						const bool		usePointMode			= usePointModeI != 0;
6531						const string	primSpacWindPointName	= primSpacWindName + (usePointMode ? "_point_mode" : "");
6532
6533						invariantPrimitiveSetGroup->addChild		(new InvariantPrimitiveSetCase			(m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode));
6534						symmetricOuterEdgeGroup->addChild			(new SymmetricOuterEdgeCase				(m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode));
6535						tessCoordComponentRangeGroup->addChild		(new TessCoordComponentRangeCase		(m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode));
6536						oneMinusTessCoordComponentGroup->addChild	(new OneMinusTessCoordComponentCase		(m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode));
6537
6538						if (triOrQuad)
6539							outerEdgeVertexSetIndexIndependenceGroup->addChild(new OuterEdgeVertexSetIndexIndependenceCase(m_context, primSpacWindPointName.c_str(), "",
6540																														   primitiveType, spacing, winding, usePointMode));
6541					}
6542				}
6543			}
6544		}
6545	}
6546
6547	{
6548		TestCaseGroup* const userDefinedIOGroup = new TestCaseGroup(m_context, "user_defined_io", "Test non-built-in per-patch and per-vertex inputs and outputs");
6549		addChild(userDefinedIOGroup);
6550
6551		for (int ioTypeI = 0; ioTypeI < UserDefinedIOCase::IO_TYPE_LAST; ioTypeI++)
6552		{
6553			const UserDefinedIOCase::IOType		ioType			= (UserDefinedIOCase::IOType)ioTypeI;
6554			TestCaseGroup* const				ioTypeGroup		= new TestCaseGroup(m_context,
6555																					ioType == UserDefinedIOCase::IO_TYPE_PER_PATCH			? "per_patch"
6556																				  : ioType == UserDefinedIOCase::IO_TYPE_PER_PATCH_ARRAY	? "per_patch_array"
6557																				  : ioType == UserDefinedIOCase::IO_TYPE_PER_VERTEX			? "per_vertex"
6558																				  : ioType == UserDefinedIOCase::IO_TYPE_PER_VERTEX_BLOCK	? "per_vertex_block"
6559																				  : DE_NULL,
6560																					ioType == UserDefinedIOCase::IO_TYPE_PER_PATCH			? "Per-patch TCS outputs"
6561																				  : ioType == UserDefinedIOCase::IO_TYPE_PER_PATCH_ARRAY	? "Per-patch array TCS outputs"
6562																				  : ioType == UserDefinedIOCase::IO_TYPE_PER_VERTEX			? "Per-vertex TCS outputs"
6563																				  : ioType == UserDefinedIOCase::IO_TYPE_PER_VERTEX_BLOCK	? "Per-vertex TCS outputs in IO block"
6564																				  : DE_NULL);
6565			userDefinedIOGroup->addChild(ioTypeGroup);
6566
6567			for (int vertexArraySizeI = 0; vertexArraySizeI < UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_LAST; vertexArraySizeI++)
6568			{
6569				const UserDefinedIOCase::VertexIOArraySize	vertexArraySize			= (UserDefinedIOCase::VertexIOArraySize)vertexArraySizeI;
6570				TestCaseGroup* const						vertexArraySizeGroup	= new TestCaseGroup(m_context,
6571																										vertexArraySizeI == UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_IMPLICIT
6572																											? "vertex_io_array_size_implicit"
6573																									  : vertexArraySizeI == UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN
6574																											? "vertex_io_array_size_shader_builtin"
6575																									  : vertexArraySizeI == UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY
6576																											? "vertex_io_array_size_query"
6577																									  : DE_NULL,
6578																									    "");
6579				ioTypeGroup->addChild(vertexArraySizeGroup);
6580
6581				for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
6582				{
6583					const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
6584					vertexArraySizeGroup->addChild(new UserDefinedIOCase(m_context, getTessPrimitiveTypeShaderName(primitiveType), "", primitiveType, ioType, vertexArraySize,
6585																		 (string() + "data/tessellation/user_defined_io_" + getTessPrimitiveTypeShaderName(primitiveType) + "_ref.png").c_str()));
6586				}
6587			}
6588		}
6589	}
6590}
6591
6592} // Functional
6593} // gles31
6594} // deqp
6595