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