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