• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
63 using glu::ShaderProgram;
64 using glu::RenderContext;
65 using tcu::RenderTarget;
66 using tcu::TestLog;
67 using tcu::Vec2;
68 using tcu::Vec3;
69 using tcu::Vec4;
70 using de::Random;
71 using de::SharedPtr;
72 
73 using std::vector;
74 using std::string;
75 
76 using namespace glw; // For GL types.
77 
78 namespace deqp
79 {
80 
81 using gls::TextureTestUtil::RandomViewport;
82 
83 namespace gles31
84 {
85 namespace Functional
86 {
87 
88 using namespace gls::StateQueryUtil;
89 
90 enum
91 {
92 	MINIMUM_MAX_TESS_GEN_LEVEL = 64 //!< GL-defined minimum for GL_MAX_TESS_GEN_LEVEL.
93 };
94 
vec3XLessThan(const Vec3 & a,const Vec3 & b)95 static inline bool vec3XLessThan (const Vec3& a, const Vec3& b) { return a.x() < b.x(); }
96 
97 template <typename IterT>
elemsStr(const IterT & begin,const IterT & end,int wrapLengthParam=0,int numIndentationSpaces=0)98 static 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 
127 template <typename ContainerT>
containerStr(const ContainerT & c,int wrapLengthParam=0,int numIndentationSpaces=0)128 static string containerStr (const ContainerT& c, int wrapLengthParam = 0, int numIndentationSpaces = 0)
129 {
130 	return elemsStr(c.begin(), c.end(), wrapLengthParam, numIndentationSpaces);
131 }
132 
133 template <typename T, int N>
arrayStr(const T (& arr)[N],int wrapLengthParam=0,int numIndentationSpaces=0)134 static 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 
139 template <typename T, int N>
arrayMax(const T (& arr)[N])140 static T arrayMax (const T (&arr)[N])
141 {
142 	return *std::max_element(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr));
143 }
144 
145 template <typename T, typename MembT>
members(const vector<T> & objs,MembT T::* membP)146 static 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 
154 template <typename T, int N>
arrayToVector(const T (& arr)[N])155 static vector<T> arrayToVector (const T (&arr)[N])
156 {
157 	return vector<T>(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr));
158 }
159 
160 template <typename ContainerT, typename T>
contains(const ContainerT & c,const T & key)161 static inline bool contains (const ContainerT& c, const T& key)
162 {
163 	return c.find(key) != c.end();
164 }
165 
166 template <int Size>
singleTrueMask(int index)167 static 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 
intPow(int base,int exp)175 static 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 
getPixels(const glu::RenderContext & rCtx,int x,int y,int width,int height)190 tcu::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 
getPixels(const glu::RenderContext & rCtx,const RandomViewport & vp)197 tcu::Surface getPixels (const glu::RenderContext& rCtx, const RandomViewport& vp)
198 {
199 	return getPixels(rCtx, vp.x, vp.y, vp.width, vp.height);
200 }
201 
checkRenderTargetSize(const RenderTarget & renderTarget,int minSize)202 static 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 
getPNG(const tcu::Archive & archive,const string & filename)208 tcu::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 
numBasicSubobjects(const glu::VarType & type)215 static 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 
numVerticesPerPrimitive(deUint32 primitiveTypeGL)236 static 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 
setViewport(const glw::Functions & gl,const RandomViewport & vp)249 static inline void setViewport (const glw::Functions& gl, const RandomViewport& vp)
250 {
251 	gl.viewport(vp.x, vp.y, vp.width, vp.height);
252 }
253 
getQueryResult(const glw::Functions & gl,deUint32 queryObject)254 static 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 
262 template <typename T>
readDataMapped(const glw::Functions & gl,deUint32 bufferTarget,int numElems,T * dst)263 static 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 
276 template <typename T>
readDataMapped(const glw::Functions & gl,deUint32 bufferTarget,int numElems)277 static 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 
284 namespace
285 {
286 
287 template <typename ArgT, bool res>
288 struct ConstantUnaryPredicate
289 {
operator ()deqp::gles31::Functional::__anon7e98ac8e0211::ConstantUnaryPredicate290 	bool operator() (const ArgT&) const { return res; }
291 };
292 
293 //! Helper for handling simple, one-varying transform feedbacks.
294 template <typename VaryingT>
295 class TransformFeedbackHandler
296 {
297 public:
298 	struct Result
299 	{
300 		int					numPrimitives;
301 		vector<VaryingT>	varying;
302 
Resultdeqp::gles31::Functional::__anon7e98ac8e0211::TransformFeedbackHandler::Result303 		Result (void)								: numPrimitives(-1) {}
Resultdeqp::gles31::Functional::__anon7e98ac8e0211::TransformFeedbackHandler::Result304 		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 
311 private:
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 
318 template <typename AttribType>
TransformFeedbackHandler(const glu::RenderContext & renderCtx,int maxNumVertices)319 TransformFeedbackHandler<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 
333 template <typename AttribType>
renderAndGetPrimitives(deUint32 programGL,deUint32 tfPrimTypeGL,int numBindings,const glu::VertexArrayBinding * bindings,int numVertices) const334 typename 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 
359 template <typename T>
360 class SizeLessThan
361 {
362 public:
operator ()(const T & a,const T & b) const363 	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.
367 template <typename Pred, typename T, typename MembT>
368 class MemberPred
369 {
370 public:
MemberPred(MembT T::* membP)371 				MemberPred	(MembT T::* membP) : m_membP(membP), m_pred(Pred()) {}
operator ()(const T & a,const T & b) const372 	bool		operator()	(const T& a, const T& b) const { return m_pred(a.*m_membP, b.*m_membP); }
373 
374 private:
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.
380 template <template <typename> class Pred, typename T, typename MembT>
memberPred(MembT T::* membP)381 static MemberPred<Pred<MembT>, T, MembT> memberPred (MembT T::* membP) { return MemberPred<Pred<MembT>, T, MembT>(membP); }
382 
383 template <typename SeqT, int Size, typename Pred>
384 class LexCompare
385 {
386 public:
LexCompare(void)387 	LexCompare (void) : m_pred(Pred()) {}
388 
operator ()(const SeqT & a,const SeqT & b) const389 	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 
401 private:
402 	Pred m_pred;
403 };
404 
405 template <int Size>
406 class VecLexLessThan : public LexCompare<tcu::Vector<float, Size>, Size, std::less<float> >
407 {
408 };
409 
410 enum TessPrimitiveType
411 {
412 	TESSPRIMITIVETYPE_TRIANGLES = 0,
413 	TESSPRIMITIVETYPE_QUADS,
414 	TESSPRIMITIVETYPE_ISOLINES,
415 
416 	TESSPRIMITIVETYPE_LAST
417 };
418 
419 enum SpacingMode
420 {
421 	SPACINGMODE_EQUAL,
422 	SPACINGMODE_FRACTIONAL_ODD,
423 	SPACINGMODE_FRACTIONAL_EVEN,
424 
425 	SPACINGMODE_LAST
426 };
427 
428 enum Winding
429 {
430 	WINDING_CCW = 0,
431 	WINDING_CW,
432 
433 	WINDING_LAST
434 };
435 
getTessPrimitiveTypeShaderName(TessPrimitiveType type)436 static 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 
getSpacingModeShaderName(SpacingMode mode)449 static 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 
getWindingShaderName(Winding winding)462 static 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 
getTessellationEvaluationInLayoutString(TessPrimitiveType primType,SpacingMode spacing,Winding winding,bool usePointMode=false)474 static 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 
getTessellationEvaluationInLayoutString(TessPrimitiveType primType,SpacingMode spacing,bool usePointMode=false)483 static 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 
getTessellationEvaluationInLayoutString(TessPrimitiveType primType,Winding winding,bool usePointMode=false)491 static 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 
getTessellationEvaluationInLayoutString(TessPrimitiveType primType,bool usePointMode=false)499 static inline string getTessellationEvaluationInLayoutString (TessPrimitiveType primType, bool usePointMode=false)
500 {
501 	return string() + "layout (" + getTessPrimitiveTypeShaderName(primType)
502 								 + (usePointMode ? ", point_mode" : "")
503 								 + ") in;\n";
504 }
505 
outputPrimitiveTypeGL(TessPrimitiveType tessPrimType,bool usePointMode)506 static 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 
numInnerTessellationLevels(TessPrimitiveType primType)524 static 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 
numOuterTessellationLevels(TessPrimitiveType primType)535 static 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 
tessellationLevelsString(const float * inner,int numInner,const float * outer,int numOuter)546 static 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 
tessellationLevelsString(const float * inner,const float * outer,TessPrimitiveType primType)552 static string tessellationLevelsString (const float* inner, const float* outer, TessPrimitiveType primType)
553 {
554 	return tessellationLevelsString(inner, numInnerTessellationLevels(primType), outer, numOuterTessellationLevels(primType));
555 }
556 
tessellationLevelsString(const float * inner,const float * outer)557 static string tessellationLevelsString (const float* inner, const float* outer)
558 {
559 	return tessellationLevelsString(inner, 2, outer, 4);
560 }
561 
getClampedTessLevel(SpacingMode mode,float tessLevel)562 static 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 
getRoundedTessLevel(SpacingMode mode,float clampedTessLevel)575 static 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 
getClampedRoundedTessLevel(SpacingMode mode,float tessLevel)592 static 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.
600 struct OuterEdgeDescription
601 {
602 	int		constantCoordinateIndex;
603 	float	constantCoordinateValueChoices[2];
604 	int		numConstantCoordinateValueChoices;
605 
OuterEdgeDescriptiondeqp::gles31::Functional::__anon7e98ac8e0211::OuterEdgeDescription606 	OuterEdgeDescription (int i, float c0)			: constantCoordinateIndex(i), numConstantCoordinateValueChoices(1) { constantCoordinateValueChoices[0] = c0; }
OuterEdgeDescriptiondeqp::gles31::Functional::__anon7e98ac8e0211::OuterEdgeDescription607 	OuterEdgeDescription (int i, float c0, float c1)	: constantCoordinateIndex(i), numConstantCoordinateValueChoices(2) { constantCoordinateValueChoices[0] = c0; constantCoordinateValueChoices[1] = c1; }
608 
descriptiondeqp::gles31::Functional::__anon7e98ac8e0211::OuterEdgeDescription609 	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 
containsdeqp::gles31::Functional::__anon7e98ac8e0211::OuterEdgeDescription618 	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 
outerEdgeDescriptions(TessPrimitiveType primType)627 static 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).
generateReferenceTriangleTessCoords(SpacingMode spacingMode,int inner,int outer0,int outer1,int outer2)659 static 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 
referenceTriangleNonPointModePrimitiveCount(SpacingMode spacingMode,int inner,int outer0,int outer1,int outer2)713 static 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).
generateReferenceQuadTessCoords(SpacingMode spacingMode,int inner0,int inner1,int outer0,int outer1,int outer2,int outer3)743 static 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 
referenceQuadNonPointModePrimitiveCount(SpacingMode spacingMode,int inner0,int inner1,int outer0,int outer1,int outer2,int outer3)779 static 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).
generateReferenceIsolineTessCoords(int outer0,int outer1)797 static 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 
referenceIsolineNonPointModePrimitiveCount(int outer0,int outer1)810 static int referenceIsolineNonPointModePrimitiveCount (int outer0, int outer1)
811 {
812 	return outer0*outer1;
813 }
814 
getClampedRoundedTriangleTessLevels(SpacingMode spacingMode,const float * innerSrc,const float * outerSrc,int * innerDst,int * outerDst)815 static 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 
getClampedRoundedQuadTessLevels(SpacingMode spacingMode,const float * innerSrc,const float * outerSrc,int * innerDst,int * outerDst)822 static 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 
getClampedRoundedIsolineTessLevels(SpacingMode spacingMode,const float * outerSrc,int * outerDst)830 static 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 
isPatchDiscarded(TessPrimitiveType primitiveType,const float * outerLevels)836 static 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 
generateReferenceTessCoords(TessPrimitiveType primitiveType,SpacingMode spacingMode,const float * innerLevels,const float * outerLevels)845 static 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 
referencePointModePrimitiveCount(TessPrimitiveType primitiveType,SpacingMode spacingMode,const float * innerLevels,const float * outerLevels)910 static 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 
referenceNonPointModePrimitiveCount(TessPrimitiveType primitiveType,SpacingMode spacingMode,const float * innerLevels,const float * outerLevels)946 static 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 
referencePrimitiveCount(TessPrimitiveType primitiveType,SpacingMode spacingMode,bool usePointMode,const float * innerLevels,const float * outerLevels)982 static 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 
referenceVertexCount(TessPrimitiveType primitiveType,SpacingMode spacingMode,bool usePointMode,const float * innerLevels,const float * outerLevels)988 static 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.
multiplePatchReferenceVertexCount(TessPrimitiveType primitiveType,SpacingMode spacingMode,bool usePointMode,const float * levels,int numPatches)996 static 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 
generateRandomPatchTessLevels(int numPatches,int constantOuterLevelIndex,float constantOuterLevel,de::Random & rnd)1004 vector<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 
drawPoint(tcu::Surface & dst,int centerX,int centerY,const tcu::RGBA & color,int size)1022 static 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 
drawTessCoordPoint(tcu::Surface & dst,TessPrimitiveType primitiveType,const Vec3 & pt,const tcu::RGBA & color,int size)1039 static 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 
drawTessCoordVisualization(tcu::Surface & dst,TessPrimitiveType primitiveType,const vector<Vec3> & coords)1068 static 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 
binarySearchFirstVec3WithXAtLeast(const vector<Vec3> & sorted,float x)1080 static 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 
1089 template <typename T, typename P>
sorted(const vector<T> & unsorted,P pred)1090 static 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 
1097 template <typename T>
sorted(const vector<T> & unsorted)1098 static 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.
oneWayComparePointSets(TestLog & log,tcu::Surface & errorDst,TessPrimitiveType primitiveType,const vector<Vec3> & subset,const vector<Vec3> & superset,const char * subsetName,const char * supersetName,const tcu::RGBA & errorColor)1106 static 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 
compareTessCoords(TestLog & log,TessPrimitiveType primitiveType,const vector<Vec3> & refCoords,const vector<Vec3> & resCoords)1164 static 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 
1190 namespace VerifyFractionalSpacingSingleInternal
1191 {
1192 
1193 struct Segment
1194 {
1195 	int		index; //!< Index of left coordinate in sortedXCoords.
1196 	float	length;
Segmentdeqp::gles31::Functional::__anon7e98ac8e0211::VerifyFractionalSpacingSingleInternal::Segment1197 	Segment (void)						: index(-1),		length(-1.0f)	{}
Segmentdeqp::gles31::Functional::__anon7e98ac8e0211::VerifyFractionalSpacingSingleInternal::Segment1198 	Segment (int index_, float length_)	: index(index_),	length(length_)	{}
1199 
lengthsdeqp::gles31::Functional::__anon7e98ac8e0211::VerifyFractionalSpacingSingleInternal::Segment1200 	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  *//*--------------------------------------------------------------------*/
verifyFractionalSpacingSingle(TestLog & log,SpacingMode spacingMode,float tessLevel,const vector<float> & coords,float & additionalSegmentLengthDst,int & additionalSegmentLocationDst)1225 static 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 
1358 namespace VerifyFractionalSpacingMultipleInternal
1359 {
1360 
1361 struct LineData
1362 {
1363 	float	tessLevel;
1364 	float	additionalSegmentLength;
1365 	int		additionalSegmentLocation;
LineDatadeqp::gles31::Functional::__anon7e98ac8e0211::VerifyFractionalSpacingMultipleInternal::LineData1366 	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  *//*--------------------------------------------------------------------*/
verifyFractionalSpacingMultiple(TestLog & log,SpacingMode spacingMode,const vector<float> & tessLevels,const vector<float> & additionalSegmentLengths,const vector<int> & additionalSegmentLocations)1378 static 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.
1455 template <typename IsTriangleRelevantT>
compareTriangleSets(const vector<Vec3> & coordsA,const vector<Vec3> & coordsB,TestLog & log,const IsTriangleRelevantT & isTriangleRelevant,const char * ignoredTriangleDescription=DE_NULL)1456 static 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 
compareTriangleSets(const vector<Vec3> & coordsA,const vector<Vec3> & coordsB,TestLog & log)1523 static 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 
checkExtensionSupport(Context & context,const char * extName)1528 static void checkExtensionSupport (Context& context, const char* extName)
1529 {
1530 	if (!context.getContextInfo().isExtensionSupported(extName))
1531 		throw tcu::NotSupportedError(string(extName) + " not supported");
1532 }
1533 
checkTessellationSupport(Context & context)1534 static 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.
1540 class CommonEdgeCase : public TestCase
1541 {
1542 public:
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 
CommonEdgeCase(Context & context,const char * name,const char * description,TessPrimitiveType primitiveType,SpacingMode spacing,CaseType caseType)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 
1564 private:
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 
init(void)1574 void 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 
deinit(void)1693 void CommonEdgeCase::deinit (void)
1694 {
1695 	m_program.clear();
1696 }
1697 
iterate(void)1698 CommonEdgeCase::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).
1846 class TessCoordCase : public TestCase
1847 {
1848 public:
TessCoordCase(Context & context,const char * name,const char * description,TessPrimitiveType primitiveType,SpacingMode spacing)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 
1860 private:
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 
init(void)1877 void 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 
deinit(void)1943 void TessCoordCase::deinit (void)
1944 {
1945 	m_program.clear();
1946 }
1947 
genTessLevelCases(void) const1948 vector<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 
iterate(void)2005 TessCoordCase::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.
2088 class FractionalSpacingModeCase : public TestCase
2089 {
2090 public:
FractionalSpacingModeCase(Context & context,const char * name,const char * description,SpacingMode spacing)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 
2102 private:
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 
init(void)2112 void 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 
deinit(void)2167 void FractionalSpacingModeCase::deinit (void)
2168 {
2169 	m_program.clear();
2170 }
2171 
genTessLevelCases(void)2172 vector<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 
iterate(void)2193 FractionalSpacingModeCase::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.
2251 class BasicVariousTessLevelsPosAttrCase : public TestCase
2252 {
2253 public:
BasicVariousTessLevelsPosAttrCase(Context & context,const char * name,const char * description,TessPrimitiveType primitiveType,SpacingMode spacing,const char * referenceImagePathPrefix)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 
2271 protected:
2272 	virtual const glu::ProgramSources	makeSources (TessPrimitiveType, SpacingMode, const char* vtxOutPosAttrName) const = DE_NULL;
2273 
2274 private:
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 
init(void)2284 void 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 
deinit(void)2330 void BasicVariousTessLevelsPosAttrCase::deinit (void)
2331 {
2332 	m_program.clear();
2333 }
2334 
iterate(void)2335 BasicVariousTessLevelsPosAttrCase::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.
2436 class BasicTriangleFillCoverCase : public BasicVariousTessLevelsPosAttrCase
2437 {
2438 public:
BasicTriangleFillCoverCase(Context & context,const char * name,const char * description,TessPrimitiveType primitiveType,SpacingMode spacing,const char * referenceImagePathPrefix)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 
2445 protected:
init(void)2446 	void init (void)
2447 	{
2448 		checkExtensionSupport(m_context, "GL_EXT_gpu_shader5");
2449 		BasicVariousTessLevelsPosAttrCase::init();
2450 	}
2451 
makeSources(TessPrimitiveType primitiveType,SpacingMode spacing,const char * vtxOutPosAttrName) const2452 	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.
2517 class BasicTriangleFillNonOverlapCase : public BasicVariousTessLevelsPosAttrCase
2518 {
2519 public:
BasicTriangleFillNonOverlapCase(Context & context,const char * name,const char * description,TessPrimitiveType primitiveType,SpacingMode spacing,const char * referenceImagePathPrefix)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 
2526 protected:
makeSources(TessPrimitiveType primitiveType,SpacingMode spacing,const char * vtxOutPosAttrName) const2527 	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.
2601 class IsolinesRenderCase : public BasicVariousTessLevelsPosAttrCase
2602 {
2603 public:
IsolinesRenderCase(Context & context,const char * name,const char * description,SpacingMode spacing,const char * referenceImagePathPrefix)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 
2609 protected:
makeSources(TessPrimitiveType primitiveType,SpacingMode spacing,const char * vtxOutPosAttrName) const2610 	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.
2673 class WindingCase : public TestCase
2674 {
2675 public:
WindingCase(Context & context,const char * name,const char * description,TessPrimitiveType primitiveType,Winding winding)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 
2688 private:
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 
init(void)2697 void 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 
deinit(void)2749 void WindingCase::deinit (void)
2750 {
2751 	m_program.clear();
2752 }
2753 
iterate(void)2754 WindingCase::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.
2856 class PatchVertexCountCase : public TestCase
2857 {
2858 public:
PatchVertexCountCase(Context & context,const char * name,const char * description,int inputPatchSize,int outputPatchSize,const char * referenceImagePath)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 
2871 private:
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 
init(void)2882 void 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 
deinit(void)2957 void PatchVertexCountCase::deinit (void)
2958 {
2959 	m_program.clear();
2960 }
2961 
iterate(void)2962 PatchVertexCountCase::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.
3010 class PerPatchDataCase : public TestCase
3011 {
3012 public:
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 
PerPatchDataCase(Context & context,const char * name,const char * description,CaseType caseType,const char * referenceImagePath)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 
3045 private:
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 
3056 const int PerPatchDataCase::INPUT_PATCH_SIZE	= 10;
3057 const int PerPatchDataCase::OUTPUT_PATCH_SIZE	= 5;
3058 
getCaseTypeName(CaseType type)3059 const 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 
getCaseTypeDescription(CaseType type)3079 const 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 
caseTypeUsesRefImageFromFile(CaseType type)3099 bool 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 
init(void)3112 void 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 
deinit(void)3211 void PerPatchDataCase::deinit (void)
3212 {
3213 	m_program.clear();
3214 }
3215 
iterate(void)3216 PerPatchDataCase::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.
3295 class BarrierCase : public TestCase
3296 {
3297 public:
BarrierCase(Context & context,const char * name,const char * description,const char * referenceImagePath)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 
3308 private:
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 
3317 const int BarrierCase::NUM_VERTICES = 32;
3318 
init(void)3319 void 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 
deinit(void)3410 void BarrierCase::deinit (void)
3411 {
3412 	m_program.clear();
3413 }
3414 
iterate(void)3415 BarrierCase::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  *//*--------------------------------------------------------------------*/
3472 class PrimitiveSetInvarianceCase : public TestCase
3473 {
3474 public:
3475 	enum WindingUsage
3476 	{
3477 		WINDINGUSAGE_CCW = 0,
3478 		WINDINGUSAGE_CW,
3479 		WINDINGUSAGE_VARY,
3480 
3481 		WINDINGUSAGE_LAST
3482 	};
3483 
PrimitiveSetInvarianceCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing,bool usePointMode,WindingUsage windingUsage)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 
3497 protected:
3498 	struct TessLevels
3499 	{
3500 		float inner[2];
3501 		float outer[4];
descriptiondeqp::gles31::Functional::__anon7e98ac8e0211::PrimitiveSetInvarianceCase::TessLevels3502 		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().
LevelCasedeqp::gles31::Functional::__anon7e98ac8e0211::PrimitiveSetInvarianceCase::LevelCase3508 		LevelCase (const TessLevels& lev) : levels(vector<TessLevels>(1, lev)), mem(0) {}
LevelCasedeqp::gles31::Functional::__anon7e98ac8e0211::PrimitiveSetInvarianceCase::LevelCase3509 		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 
3517 private:
3518 	struct Program
3519 	{
3520 		Winding							winding;
3521 		SharedPtr<const ShaderProgram>	program;
3522 
Programdeqp::gles31::Functional::__anon7e98ac8e0211::PrimitiveSetInvarianceCase::Program3523 				Program			(Winding w, const SharedPtr<const ShaderProgram>& prog) : winding(w), program(prog) {}
3524 
descriptiondeqp::gles31::Functional::__anon7e98ac8e0211::PrimitiveSetInvarianceCase::Program3525 		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 
genTessLevelCases(void) const3537 vector<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 
init(void)3572 void 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 
deinit(void)3676 void PrimitiveSetInvarianceCase::deinit (void)
3677 {
3678 	m_programs.clear();
3679 }
3680 
iterate(void)3681 PrimitiveSetInvarianceCase::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  *//*--------------------------------------------------------------------*/
3795 class InvariantPrimitiveSetCase : public PrimitiveSetInvarianceCase
3796 {
3797 public:
InvariantPrimitiveSetCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing,Winding winding,bool usePointMode)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 
3805 protected:
compare(const vector<Vec3> & coordsA,const vector<Vec3> & coordsB,int) const3806 	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  *//*--------------------------------------------------------------------*/
3834 class InvariantOuterEdgeCase : public TestCase
3835 {
3836 public:
InvariantOuterEdgeCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing)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 
3849 private:
3850 	struct Program
3851 	{
3852 		Winding							winding;
3853 		bool							usePointMode;
3854 		SharedPtr<const ShaderProgram>	program;
3855 
Programdeqp::gles31::Functional::__anon7e98ac8e0211::InvariantOuterEdgeCase::Program3856 				Program			(Winding w, bool point, const SharedPtr<const ShaderProgram>& prog) : winding(w), usePointMode(point), program(prog) {}
3857 
descriptiondeqp::gles31::Functional::__anon7e98ac8e0211::InvariantOuterEdgeCase::Program3858 		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 
generatePatchTessLevels(int numPatches,int constantOuterLevelIndex,float constantOuterLevel)3871 vector<float> InvariantOuterEdgeCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel)
3872 {
3873 	de::Random rnd(123);
3874 	return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd);
3875 }
3876 
init(void)3877 void 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 
deinit(void)3967 void InvariantOuterEdgeCase::deinit (void)
3968 {
3969 	m_programs.clear();
3970 }
3971 
iterate(void)3972 InvariantOuterEdgeCase::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  *//*--------------------------------------------------------------------*/
4124 class SymmetricOuterEdgeCase : public TestCase
4125 {
4126 public:
SymmetricOuterEdgeCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing,Winding winding,bool usePointMode)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 
4140 private:
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 
generatePatchTessLevels(int numPatches,int constantOuterLevelIndex,float constantOuterLevel)4153 vector<float> SymmetricOuterEdgeCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel)
4154 {
4155 	de::Random rnd(123);
4156 	return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd);
4157 }
4158 
init(void)4159 void 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 
deinit(void)4250 void SymmetricOuterEdgeCase::deinit (void)
4251 {
4252 	m_program.clear();
4253 }
4254 
iterate(void)4255 SymmetricOuterEdgeCase::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  *//*--------------------------------------------------------------------*/
4407 class OuterEdgeVertexSetIndexIndependenceCase : public TestCase
4408 {
4409 public:
OuterEdgeVertexSetIndexIndependenceCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing,Winding winding,bool usePointMode)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 
4424 private:
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 
generatePatchTessLevels(int numPatches,int constantOuterLevelIndex,float constantOuterLevel)4437 vector<float> OuterEdgeVertexSetIndexIndependenceCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel)
4438 {
4439 	de::Random rnd(123);
4440 	return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd);
4441 }
4442 
init(void)4443 void 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 
deinit(void)4511 void OuterEdgeVertexSetIndexIndependenceCase::deinit (void)
4512 {
4513 	m_program.clear();
4514 }
4515 
iterate(void)4516 OuterEdgeVertexSetIndexIndependenceCase::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  *//*--------------------------------------------------------------------*/
4652 class InvariantTriangleSetCase : public PrimitiveSetInvarianceCase
4653 {
4654 public:
InvariantTriangleSetCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing)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 
4661 protected:
compare(const vector<Vec3> & coordsA,const vector<Vec3> & coordsB,int) const4662 	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  *//*--------------------------------------------------------------------*/
4674 class InvariantInnerTriangleSetCase : public PrimitiveSetInvarianceCase
4675 {
4676 public:
InvariantInnerTriangleSetCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing)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 
4683 protected:
genTessLevelCases(void) const4684 	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 	{
operator ()deqp::gles31::Functional::__anon7e98ac8e0211::InvariantInnerTriangleSetCase::IsInnerTriangleTriangle4716 		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 	{
operator ()deqp::gles31::Functional::__anon7e98ac8e0211::InvariantInnerTriangleSetCase::IsInnerQuadTriangle4728 		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 
compare(const vector<Vec3> & coordsA,const vector<Vec3> & coordsB,int) const4738 	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  *//*--------------------------------------------------------------------*/
4760 class InvariantOuterTriangleSetCase : public PrimitiveSetInvarianceCase
4761 {
4762 public:
InvariantOuterTriangleSetCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing)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 
4769 protected:
genTessLevelCases(void) const4770 	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:
IsTriangleTriangleOnOuterEdge(int edgeNdx)4817 		IsTriangleTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
operator ()(const Vec3 * vertices) const4818 		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:
IsQuadTriangleOnOuterEdge(int edgeNdx)4841 		IsQuadTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
4842 
onEdge(const Vec3 & v) const4843 		bool onEdge (const Vec3& v) const
4844 		{
4845 			return v[m_edgeNdx%2] == (m_edgeNdx <= 1 ? 0.0f : 1.0f);
4846 		}
4847 
onAnyEdge(const Vec3 & v)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 
operator ()(const Vec3 * vertices) const4853 		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 
compare(const vector<Vec3> & coordsA,const vector<Vec3> & coordsB,int outerEdgeNdx) const4873 	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  *//*--------------------------------------------------------------------*/
4901 class TessCoordComponentInvarianceCase : public TestCase
4902 {
4903 public:
TessCoordComponentInvarianceCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing,Winding winding,bool usePointMode)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 
4917 protected:
4918 	virtual string							tessEvalOutputComponentStatements	(const char* tessCoordComponentName, const char* outputComponentName) const = 0;
4919 	virtual bool							checkTessCoordComponent				(float component) const = 0;
4920 
4921 private:
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 
init(void)4934 void 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 
deinit(void)5007 void TessCoordComponentInvarianceCase::deinit (void)
5008 {
5009 	m_program.clear();
5010 }
5011 
genTessLevelCases(int numCases)5012 vector<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 
iterate(void)5024 TessCoordComponentInvarianceCase::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  *//*--------------------------------------------------------------------*/
5087 class TessCoordComponentRangeCase : public TessCoordComponentInvarianceCase
5088 {
5089 public:
TessCoordComponentRangeCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing,Winding winding,bool usePointMode)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 
5095 protected:
tessEvalOutputComponentStatements(const char * tessCoordComponentName,const char * outputComponentName) const5096 	virtual string tessEvalOutputComponentStatements (const char* tessCoordComponentName, const char* outputComponentName) const
5097 	{
5098 		return string() + "\t" + outputComponentName + " = " + tessCoordComponentName + ";\n";
5099 	}
5100 
checkTessCoordComponent(float component) const5101 	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  *//*--------------------------------------------------------------------*/
5118 class OneMinusTessCoordComponentCase : public TessCoordComponentInvarianceCase
5119 {
5120 public:
OneMinusTessCoordComponentCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing,Winding winding,bool usePointMode)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 
5126 protected:
tessEvalOutputComponentStatements(const char * tessCoordComponentName,const char * outputComponentName) const5127 	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 
checkTessCoordComponent(float component) const5135 	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  *//*--------------------------------------------------------------------*/
5156 class PrimitiveDiscardCase : public TestCase
5157 {
5158 public:
PrimitiveDiscardCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing,Winding winding,bool usePointMode)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 
5172 private:
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 
init(void)5185 void 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 
deinit(void)5258 void PrimitiveDiscardCase::deinit (void)
5259 {
5260 	m_program.clear();
5261 }
5262 
genAttributes(void)5263 vector<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 
iterate(void)5321 PrimitiveDiscardCase::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  *//*--------------------------------------------------------------------*/
5488 class UserDefinedIOCase : public TestCase
5489 {
5490 public:
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 
UserDefinedIOCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,IOType ioType,VertexIOArraySize vertexIOArraySize,TessControlOutArraySize tessControlOutArraySize,const char * referenceImagePath)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 
5534 private:
5535 	typedef string (*BasicTypeVisitFunc)(const string& name, glu::DataType type, int indentationDepth); //!< See glslTraverseBasicTypes below.
5536 
5537 	class TopLevelObject
5538 	{
5539 	public:
~TopLevelObject(void)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:
Variable(const string & name_,const glu::VarType & type,bool isArray)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 
name(void) const5565 		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;
Memberdeqp::gles31::Functional::__anon7e98ac8e0211::UserDefinedIOCase::IOBlock::Member5586 			Member (const string& n, const glu::VarType& t) : name(n), type(t) {}
5587 		};
5588 
IOBlock(const string & blockName,const string & interfaceName,const vector<Member> & members)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 
name(void) const5596 		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  *//*--------------------------------------------------------------------*/
glslTraverseBasicTypes(const string & rootName,const glu::VarType & rootType,int arrayNestingDepth,int indentationDepth,BasicTypeVisitFunc visit)5660 string 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 
declare(void) const5699 string UserDefinedIOCase::Variable::declare (void) const
5700 {
5701 	DE_ASSERT(!m_isArray);
5702 	return de::toString(glu::declare(m_type, m_name)) + ";\n";
5703 }
5704 
declareArray(const string & sizeExpr) const5705 string 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 
declare(void) const5711 string 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 
declareArray(const string & sizeExpr) const5725 string 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 
glslTraverseBasicTypeArray(int numArrayElements,int indentationDepth,BasicTypeVisitFunc visit) const5739 string 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 
glslTraverseBasicType(int indentationDepth,BasicTypeVisitFunc visit) const5750 string 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 
glslTraverseBasicTypeArray(int numArrayElements,int indentationDepth,BasicTypeVisitFunc visit) const5757 string 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 
glslTraverseBasicType(int indentationDepth,BasicTypeVisitFunc visit) const5779 string 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 
numBasicSubobjectsInElementType(void) const5787 int UserDefinedIOCase::Variable::numBasicSubobjectsInElementType (void) const
5788 {
5789 	return numBasicSubobjects(m_type);
5790 }
5791 
numBasicSubobjectsInElementType(void) const5792 int 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 
basicSubobjectAtIndex(int subobjectIndex,int arraySize) const5800 string 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 
basicSubobjectAtIndex(int subobjectIndex,int arraySize) const5817 string 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.
glslAssignBasicTypeObject(const string & name,glu::DataType type,int indentationDepth)5840 string 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.
glslCheckBasicTypeObject(const string & name,glu::DataType type,int indentationDepth)5861 string 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 
numBasicSubobjectsInElementType(const vector<SharedPtr<TopLevelObject>> & objects)5883 int 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 
basicSubobjectAtIndex(int subobjectIndex,const vector<SharedPtr<TopLevelObject>> & objects,int topLevelArraySize)5891 string 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 
init(void)5906 void 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 
deinit(void)6158 void UserDefinedIOCase::deinit (void)
6159 {
6160 	m_program.clear();
6161 }
6162 
iterate(void)6163 UserDefinedIOCase::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  *//*--------------------------------------------------------------------*/
6241 class GLPositionCase : public TestCase
6242 {
6243 public:
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 
GLPositionCase(Context & context,const char * name,const char * description,CaseType caseType,const char * referenceImagePath)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 
6266 private:
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 
getCaseTypeName(CaseType type)6275 const 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 
init(void)6287 void 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 
deinit(void)6370 void GLPositionCase::deinit (void)
6371 {
6372 	m_program.clear();
6373 }
6374 
iterate(void)6375 GLPositionCase::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 
6420 class LimitQueryCase : public TestCase
6421 {
6422 public:
6423 						LimitQueryCase	(Context& context, const char* name, const char* desc, glw::GLenum target, int minValue);
6424 private:
6425 	IterateResult		iterate			(void);
6426 
6427 	const glw::GLenum	m_target;
6428 	const int			m_minValue;
6429 };
6430 
LimitQueryCase(Context & context,const char * name,const char * desc,glw::GLenum target,int minValue)6431 LimitQueryCase::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 
iterate(void)6438 LimitQueryCase::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 
6459 class CombinedUniformLimitCase : public TestCase
6460 {
6461 public:
6462 						CombinedUniformLimitCase	(Context& context, const char* name, const char* desc, glw::GLenum combined, glw::GLenum numBlocks, glw::GLenum defaultComponents);
6463 private:
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 
CombinedUniformLimitCase(Context & context,const char * name,const char * desc,glw::GLenum combined,glw::GLenum numBlocks,glw::GLenum defaultComponents)6471 CombinedUniformLimitCase::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 
iterate(void)6479 CombinedUniformLimitCase::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 
6524 class PatchVerticesStateCase : public TestCase
6525 {
6526 public:
6527 						PatchVerticesStateCase	(Context& context, const char* name, const char* desc);
6528 private:
6529 	IterateResult		iterate					(void);
6530 };
6531 
PatchVerticesStateCase(Context & context,const char * name,const char * desc)6532 PatchVerticesStateCase::PatchVerticesStateCase (Context& context, const char* name, const char* desc)
6533 	: TestCase(context, name, desc)
6534 {
6535 }
6536 
iterate(void)6537 PatchVerticesStateCase::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 
6573 class PrimitiveRestartForPatchesSupportedCase : public TestCase
6574 {
6575 public:
6576 						PrimitiveRestartForPatchesSupportedCase	(Context& context, const char* name, const char* desc);
6577 private:
6578 	IterateResult		iterate									(void);
6579 };
6580 
PrimitiveRestartForPatchesSupportedCase(Context & context,const char * name,const char * desc)6581 PrimitiveRestartForPatchesSupportedCase::PrimitiveRestartForPatchesSupportedCase (Context& context, const char* name, const char* desc)
6582 	: TestCase(context, name, desc)
6583 {
6584 }
6585 
iterate(void)6586 PrimitiveRestartForPatchesSupportedCase::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 
6610 class TessProgramQueryCase : public TestCase
6611 {
6612 public:
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 
TessProgramQueryCase(Context & context,const char * name,const char * desc)6621 TessProgramQueryCase::TessProgramQueryCase (Context& context, const char* name, const char* desc)
6622 	: TestCase(context, name, desc)
6623 {
6624 }
6625 
getVertexSource(void) const6626 std::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 
getFragmentSource(void) const6635 std::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 
getTessCtrlSource(const char * globalLayouts) const6645 std::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 
getTessEvalSource(const char * globalLayouts) const6662 std::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 
6676 class TessControlOutputVerticesCase : public TessProgramQueryCase
6677 {
6678 public:
6679 						TessControlOutputVerticesCase	(Context& context, const char* name, const char* desc);
6680 private:
6681 	IterateResult		iterate							(void);
6682 };
6683 
TessControlOutputVerticesCase(Context & context,const char * name,const char * desc)6684 TessControlOutputVerticesCase::TessControlOutputVerticesCase (Context& context, const char* name, const char* desc)
6685 	: TessProgramQueryCase(context, name, desc)
6686 {
6687 }
6688 
iterate(void)6689 TessControlOutputVerticesCase::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 
6715 class TessGenModeQueryCase : public TessProgramQueryCase
6716 {
6717 public:
6718 						TessGenModeQueryCase	(Context& context, const char* name, const char* desc);
6719 private:
6720 	IterateResult		iterate					(void);
6721 };
6722 
TessGenModeQueryCase(Context & context,const char * name,const char * desc)6723 TessGenModeQueryCase::TessGenModeQueryCase (Context& context, const char* name, const char* desc)
6724 	: TessProgramQueryCase(context, name, desc)
6725 {
6726 }
6727 
iterate(void)6728 TessGenModeQueryCase::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 
6769 class TessGenSpacingQueryCase : public TessProgramQueryCase
6770 {
6771 public:
6772 						TessGenSpacingQueryCase	(Context& context, const char* name, const char* desc);
6773 private:
6774 	IterateResult		iterate					(void);
6775 };
6776 
TessGenSpacingQueryCase(Context & context,const char * name,const char * desc)6777 TessGenSpacingQueryCase::TessGenSpacingQueryCase (Context& context, const char* name, const char* desc)
6778 	: TessProgramQueryCase(context, name, desc)
6779 {
6780 }
6781 
iterate(void)6782 TessGenSpacingQueryCase::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 
6824 class TessGenVertexOrderQueryCase : public TessProgramQueryCase
6825 {
6826 public:
6827 						TessGenVertexOrderQueryCase	(Context& context, const char* name, const char* desc);
6828 private:
6829 	IterateResult		iterate						(void);
6830 };
6831 
TessGenVertexOrderQueryCase(Context & context,const char * name,const char * desc)6832 TessGenVertexOrderQueryCase::TessGenVertexOrderQueryCase (Context& context, const char* name, const char* desc)
6833 	: TessProgramQueryCase(context, name, desc)
6834 {
6835 }
6836 
iterate(void)6837 TessGenVertexOrderQueryCase::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 
6878 class TessGenPointModeQueryCase : public TessProgramQueryCase
6879 {
6880 public:
6881 						TessGenPointModeQueryCase	(Context& context, const char* name, const char* desc);
6882 private:
6883 	IterateResult		iterate						(void);
6884 };
6885 
TessGenPointModeQueryCase(Context & context,const char * name,const char * desc)6886 TessGenPointModeQueryCase::TessGenPointModeQueryCase (Context& context, const char* name, const char* desc)
6887 	: TessProgramQueryCase(context, name, desc)
6888 {
6889 }
6890 
iterate(void)6891 TessGenPointModeQueryCase::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 
6931 class ReferencedByTessellationQueryCase : public TestCase
6932 {
6933 public:
6934 					ReferencedByTessellationQueryCase	(Context& context, const char* name, const char* desc, bool isCtrlCase);
6935 private:
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 
ReferencedByTessellationQueryCase(Context & context,const char * name,const char * desc,bool isCtrlCase)6947 ReferencedByTessellationQueryCase::ReferencedByTessellationQueryCase (Context& context, const char* name, const char* desc, bool isCtrlCase)
6948 	: TestCase		(context, name, desc)
6949 	, m_isCtrlCase	(isCtrlCase)
6950 {
6951 }
6952 
init(void)6953 void ReferencedByTessellationQueryCase::init (void)
6954 {
6955 	checkTessellationSupport(m_context);
6956 }
6957 
iterate(void)6958 ReferencedByTessellationQueryCase::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 
getVertexSource(void) const7036 std::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 
getFragmentSource(void) const7045 std::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 
getTessCtrlSource(void) const7055 std::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 
getTessEvalSource(void) const7076 std::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 
7095 class IsPerPatchQueryCase : public TestCase
7096 {
7097 public:
7098 					IsPerPatchQueryCase		(Context& context, const char* name, const char* desc);
7099 private:
7100 	void			init					(void);
7101 	IterateResult	iterate					(void);
7102 };
7103 
IsPerPatchQueryCase(Context & context,const char * name,const char * desc)7104 IsPerPatchQueryCase::IsPerPatchQueryCase (Context& context, const char* name, const char* desc)
7105 	: TestCase(context, name, desc)
7106 {
7107 }
7108 
init(void)7109 void IsPerPatchQueryCase::init (void)
7110 {
7111 	checkTessellationSupport(m_context);
7112 }
7113 
iterate(void)7114 IsPerPatchQueryCase::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 
TessellationTests(Context & context)7209 TessellationTests::TessellationTests (Context& context)
7210 	: TestCaseGroup(context, "tessellation", "Tessellation Tests")
7211 {
7212 }
7213 
~TessellationTests(void)7214 TessellationTests::~TessellationTests (void)
7215 {
7216 }
7217 
init(void)7218 void 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