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