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::__anon4a9088cf0211::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::__anon4a9088cf0211::TransformFeedbackHandler::Result303 Result (void) : numPrimitives(-1) {}
Resultdeqp::gles31::Functional::__anon4a9088cf0211::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::__anon4a9088cf0211::OuterEdgeDescription606 OuterEdgeDescription (int i, float c0) : constantCoordinateIndex(i), numConstantCoordinateValueChoices(1) { constantCoordinateValueChoices[0] = c0; }
OuterEdgeDescriptiondeqp::gles31::Functional::__anon4a9088cf0211::OuterEdgeDescription607 OuterEdgeDescription (int i, float c0, float c1) : constantCoordinateIndex(i), numConstantCoordinateValueChoices(2) { constantCoordinateValueChoices[0] = c0; constantCoordinateValueChoices[1] = c1; }
608
descriptiondeqp::gles31::Functional::__anon4a9088cf0211::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::__anon4a9088cf0211::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::__anon4a9088cf0211::VerifyFractionalSpacingSingleInternal::Segment1197 Segment (void) : index(-1), length(-1.0f) {}
Segmentdeqp::gles31::Functional::__anon4a9088cf0211::VerifyFractionalSpacingSingleInternal::Segment1198 Segment (int index_, float length_) : index(index_), length(length_) {}
1199
lengthsdeqp::gles31::Functional::__anon4a9088cf0211::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::__anon4a9088cf0211::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::__anon4a9088cf0211::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::__anon4a9088cf0211::PrimitiveSetInvarianceCase::LevelCase3508 LevelCase (const TessLevels& lev) : levels(vector<TessLevels>(1, lev)), mem(0) {}
LevelCasedeqp::gles31::Functional::__anon4a9088cf0211::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::__anon4a9088cf0211::PrimitiveSetInvarianceCase::Program3523 Program (Winding w, const SharedPtr<const ShaderProgram>& prog) : winding(w), program(prog) {}
3524
descriptiondeqp::gles31::Functional::__anon4a9088cf0211::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::__anon4a9088cf0211::InvariantOuterEdgeCase::Program3856 Program (Winding w, bool point, const SharedPtr<const ShaderProgram>& prog) : winding(w), usePointMode(point), program(prog) {}
3857
descriptiondeqp::gles31::Functional::__anon4a9088cf0211::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::__anon4a9088cf0211::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::__anon4a9088cf0211::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::__anon4a9088cf0211::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, 4096));
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