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