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