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::__anon755cd4500211::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::__anon755cd4500211::TransformFeedbackHandler::Result305 Result (void) : numPrimitives(-1) {}
Resultdeqp::gles31::Functional::__anon755cd4500211::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::__anon755cd4500211::OuterEdgeDescription608 OuterEdgeDescription (int i, float c0) : constantCoordinateIndex(i), numConstantCoordinateValueChoices(1) { constantCoordinateValueChoices[0] = c0; }
OuterEdgeDescriptiondeqp::gles31::Functional::__anon755cd4500211::OuterEdgeDescription609 OuterEdgeDescription (int i, float c0, float c1) : constantCoordinateIndex(i), numConstantCoordinateValueChoices(2) { constantCoordinateValueChoices[0] = c0; constantCoordinateValueChoices[1] = c1; }
610
descriptiondeqp::gles31::Functional::__anon755cd4500211::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::__anon755cd4500211::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::__anon755cd4500211::VerifyFractionalSpacingSingleInternal::Segment1199 Segment (void) : index(-1), length(-1.0f) {}
Segmentdeqp::gles31::Functional::__anon755cd4500211::VerifyFractionalSpacingSingleInternal::Segment1200 Segment (int index_, float length_) : index(index_), length(length_) {}
1201
lengthsdeqp::gles31::Functional::__anon755cd4500211::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::__anon755cd4500211::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
supportsES32orGL45(Context & context)1530 static bool supportsES32orGL45(Context& context)
1531 {
1532 glu::ContextType contextType = context.getRenderContext().getType();
1533 return glu::contextSupports(contextType, glu::ApiType::es(3, 2)) ||
1534 glu::contextSupports(contextType, glu::ApiType::core(4, 5));
1535 }
1536
checkGPUShader5Support(Context & context)1537 static void checkGPUShader5Support (Context& context)
1538 {
1539 TCU_CHECK_AND_THROW(NotSupportedError, supportsES32orGL45(context) || context.getContextInfo().isExtensionSupported("GL_EXT_gpu_shader5"), "GL_EXT_gpu_shader5 is not supported");
1540 }
1541
checkTessellationSupport(Context & context)1542 static void checkTessellationSupport (Context& context)
1543 {
1544 TCU_CHECK_AND_THROW(NotSupportedError, supportsES32orGL45(context) || context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"), "GL_EXT_tessellation_shader is not supported");
1545 }
1546
specializeShader(Context & context,const char * code)1547 static std::string specializeShader(Context& context, const char* code)
1548 {
1549 const glu::ContextType contextType = context.getRenderContext().getType();
1550 const glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(contextType);
1551 const bool cSupportsES32orGL45 = supportsES32orGL45(context);
1552
1553 std::map<std::string, std::string> specializationMap =
1554 {
1555 { "GLSL_VERSION_DECL", glu::getGLSLVersionDeclaration(glslVersion) },
1556 { "GPU_SHADER5_REQUIRE", cSupportsES32orGL45 ? "" : "#extension GL_EXT_gpu_shader5 : require" },
1557 { "TESSELLATION_SHADER_REQUIRE", cSupportsES32orGL45 ? "" : "#extension GL_EXT_tessellation_shader : require" },
1558 { "GLSL_PER_VERTEX_OUT", "" }, // needed for GL4.5
1559 { "GLSL_PER_VERTEX_IN_ARR", "" },
1560 { "GLSL_PER_VERTEX_OUT_ARR", "" },
1561 { "GLSL_PRECISE_PER_VERTEX_OUT", "" },
1562 { "GLSL_PRECISE_PER_VERTEX_IN_ARR", "" },
1563 { "GLSL_PRECISE_PER_VERTEX_OUT_ARR", "" }
1564 };
1565
1566 // for gl4.5 we need to add per vertex sections
1567 if (!glu::isContextTypeES(context.getRenderContext().getType()))
1568 {
1569 specializationMap["GLSL_PER_VERTEX_OUT"] = "out gl_PerVertex { vec4 gl_Position; };\n";
1570 specializationMap["GLSL_PER_VERTEX_IN_ARR"] = "in gl_PerVertex { vec4 gl_Position; } gl_in[];\n";
1571 specializationMap["GLSL_PER_VERTEX_OUT_ARR"] = "out gl_PerVertex { vec4 gl_Position; } gl_out[];\n";
1572 specializationMap["GLSL_PRECISE_PER_VERTEX_OUT"] = "out gl_PerVertex { vec4 gl_Position; };\nprecise gl_Position;\n";
1573 specializationMap["GLSL_PRECISE_PER_VERTEX_IN_ARR"] = "in gl_PerVertex { vec4 gl_Position; } gl_in[];\nprecise gl_in;\n";
1574 specializationMap["GLSL_PRECISE_PER_VERTEX_OUT_ARR"] = "out gl_PerVertex { vec4 gl_Position; } gl_out[];\nprecise gl_out;\n";
1575 }
1576
1577 return tcu::StringTemplate(code).specialize(specializationMap);
1578 }
1579
1580 // Draw primitives with shared edges and check that no cracks are visible at the shared edges.
1581 class CommonEdgeCase : public TestCase
1582 {
1583 public:
1584 enum CaseType
1585 {
1586 CASETYPE_BASIC = 0, //!< Order patch vertices such that when two patches share a vertex, it's at the same index for both.
1587 CASETYPE_PRECISE, //!< Vertex indices don't match like for CASETYPE_BASIC, but other measures are taken, using the 'precise' qualifier.
1588
1589 CASETYPE_LAST
1590 };
1591
CommonEdgeCase(Context & context,const char * name,const char * description,TessPrimitiveType primitiveType,SpacingMode spacing,CaseType caseType)1592 CommonEdgeCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing, CaseType caseType)
1593 : TestCase (context, name, description)
1594 , m_primitiveType (primitiveType)
1595 , m_spacing (spacing)
1596 , m_caseType (caseType)
1597 {
1598 DE_ASSERT(m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES || m_primitiveType == TESSPRIMITIVETYPE_QUADS);
1599 }
1600
1601 void init (void);
1602 void deinit (void);
1603 IterateResult iterate (void);
1604
1605 private:
1606 static const int RENDER_SIZE = 256;
1607
1608 const TessPrimitiveType m_primitiveType;
1609 const SpacingMode m_spacing;
1610 const CaseType m_caseType;
1611
1612 SharedPtr<const ShaderProgram> m_program;
1613 };
1614
init(void)1615 void CommonEdgeCase::init (void)
1616 {
1617 bool isGL45 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5));
1618 if (!isGL45)
1619 {
1620 checkTessellationSupport(m_context);
1621 if (m_caseType == CASETYPE_PRECISE)
1622 checkGPUShader5Support(m_context);
1623 }
1624
1625 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
1626
1627 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
1628 "${GLSL_PER_VERTEX_OUT}\n"
1629 "\n"
1630 "in highp vec2 in_v_position;\n"
1631 "in highp float in_v_tessParam;\n"
1632 "\n"
1633 "out highp vec2 in_tc_position;\n"
1634 "out highp float in_tc_tessParam;\n"
1635 "\n"
1636 "void main (void)\n"
1637 "{\n"
1638 " in_tc_position = in_v_position;\n"
1639 " in_tc_tessParam = in_v_tessParam;\n"
1640 "}\n");
1641
1642 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
1643 "${TESSELLATION_SHADER_REQUIRE}\n"
1644 + string(m_caseType == CASETYPE_PRECISE ? "${GPU_SHADER5_REQUIRE}\n" : "") +
1645 "${GLSL_PER_VERTEX_IN_ARR}\n"
1646 "${GLSL_PER_VERTEX_OUT_ARR}\n"
1647 "\n"
1648 "layout (vertices = " + string(m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "3" : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? "4" : DE_NULL) + ") out;\n"
1649 "\n"
1650 "in highp vec2 in_tc_position[];\n"
1651 "in highp float in_tc_tessParam[];\n"
1652 "\n"
1653 "out highp vec2 in_te_position[];\n"
1654 "\n"
1655 + (m_caseType == CASETYPE_PRECISE ? "precise gl_TessLevelOuter;\n\n" : "") +
1656 "void main (void)\n"
1657 "{\n"
1658 " in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];\n"
1659 "\n"
1660 " gl_TessLevelInner[0] = 5.0;\n"
1661 " gl_TessLevelInner[1] = 5.0;\n"
1662 "\n"
1663 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
1664 " gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[2]);\n"
1665 " gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[0]);\n"
1666 " gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[1]);\n"
1667 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ?
1668 " gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[2]);\n"
1669 " gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[0]);\n"
1670 " gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[3] + in_tc_tessParam[1]);\n"
1671 " gl_TessLevelOuter[3] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[3]);\n"
1672 : DE_NULL) +
1673 "}\n");
1674
1675 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
1676 "${TESSELLATION_SHADER_REQUIRE}\n"
1677 + string(m_caseType == CASETYPE_PRECISE ? "${GPU_SHADER5_REQUIRE}\n" : "") +
1678 "${GLSL_PRECISE_PER_VERTEX_IN_ARR}\n"
1679 "${GLSL_PRECISE_PER_VERTEX_OUT}\n"
1680 "\n"
1681 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing) +
1682 "\n"
1683 "in highp vec2 in_te_position[];\n"
1684 "\n"
1685 "out mediump vec4 in_f_color;\n"
1686 "\n"
1687 + ((m_caseType == CASETYPE_PRECISE && !isGL45) ? "precise gl_Position;\n\n" : "") +
1688 "void main (void)\n"
1689 "{\n"
1690 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
1691 " 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"
1692 "\n"
1693 " highp float f = sqrt(3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z))) * 0.5 + 0.5;\n"
1694 " in_f_color = vec4(gl_TessCoord*f, 1.0);\n"
1695 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ?
1696 string()
1697 + (m_caseType == CASETYPE_BASIC ?
1698 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0]\n"
1699 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1]\n"
1700 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[2]\n"
1701 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[3];\n"
1702 : m_caseType == CASETYPE_PRECISE ?
1703 " highp vec2 a = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0];\n"
1704 " highp vec2 b = ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1];\n"
1705 " highp vec2 c = (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[2];\n"
1706 " highp vec2 d = ( gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[3];\n"
1707 " highp vec2 pos = a+b+c+d;\n"
1708 : DE_NULL) +
1709 "\n"
1710 " 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"
1711 " in_f_color = vec4(0.1, gl_TessCoord.xy*f, 1.0);\n"
1712 : DE_NULL) +
1713 "\n"
1714 " // Offset the position slightly, based on the parity of the bits in the float representation.\n"
1715 " // This is done to detect possible small differences in edge vertex positions between patches.\n"
1716 " uvec2 bits = floatBitsToUint(pos);\n"
1717 " uint numBits = 0u;\n"
1718 " for (uint i = 0u; i < 32u; i++)\n"
1719 " numBits += ((bits[0] >> i) & 1u) + ((bits[1] >> i) & 1u);\n"
1720 " pos += float(numBits&1u)*0.04;\n"
1721 "\n"
1722 " gl_Position = vec4(pos, 0.0, 1.0);\n"
1723 "}\n");
1724
1725 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
1726 "\n"
1727 "layout (location = 0) out mediump vec4 o_color;\n"
1728 "\n"
1729 "in mediump vec4 in_f_color;\n"
1730 "\n"
1731 "void main (void)\n"
1732 "{\n"
1733 " o_color = in_f_color;\n"
1734 "}\n");
1735
1736 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
1737 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
1738 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
1739 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
1740 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))));
1741
1742 m_testCtx.getLog() << *m_program;
1743 if (!m_program->isOk())
1744 TCU_FAIL("Program compilation failed");
1745 }
1746
deinit(void)1747 void CommonEdgeCase::deinit (void)
1748 {
1749 m_program.clear();
1750 }
1751
iterate(void)1752 CommonEdgeCase::IterateResult CommonEdgeCase::iterate (void)
1753 {
1754 TestLog& log = m_testCtx.getLog();
1755 const RenderContext& renderCtx = m_context.getRenderContext();
1756 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
1757 const deUint32 programGL = m_program->getProgram();
1758 const glw::Functions& gl = renderCtx.getFunctions();
1759
1760 const int gridWidth = 4;
1761 const int gridHeight = 4;
1762 const int numVertices = (gridWidth+1)*(gridHeight+1);
1763 const int numIndices = gridWidth*gridHeight * (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3*2 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4 : -1);
1764 const int numPosCompsPerVertex = 2;
1765 const int totalNumPosComps = numPosCompsPerVertex*numVertices;
1766 vector<float> gridPosComps;
1767 vector<float> gridTessParams;
1768 vector<deUint16> gridIndices;
1769
1770 gridPosComps.reserve(totalNumPosComps);
1771 gridTessParams.reserve(numVertices);
1772 gridIndices.reserve(numIndices);
1773
1774 {
1775 for (int i = 0; i < gridHeight+1; i++)
1776 for (int j = 0; j < gridWidth+1; j++)
1777 {
1778 gridPosComps.push_back(-1.0f + 2.0f * ((float)j + 0.5f) / (float)(gridWidth+1));
1779 gridPosComps.push_back(-1.0f + 2.0f * ((float)i + 0.5f) / (float)(gridHeight+1));
1780 gridTessParams.push_back((float)(i*(gridWidth+1) + j) / (float)(numVertices-1));
1781 }
1782 }
1783
1784 // Generate patch vertex indices.
1785 // \note If CASETYPE_BASIC, the vertices are ordered such that when multiple
1786 // triangles/quads share a vertex, it's at the same index for everyone.
1787
1788 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
1789 {
1790 for (int i = 0; i < gridHeight; i++)
1791 for (int j = 0; j < gridWidth; j++)
1792 {
1793 const deUint16 corners[4] =
1794 {
1795 (deUint16)((i+0)*(gridWidth+1) + j+0),
1796 (deUint16)((i+0)*(gridWidth+1) + j+1),
1797 (deUint16)((i+1)*(gridWidth+1) + j+0),
1798 (deUint16)((i+1)*(gridWidth+1) + j+1)
1799 };
1800
1801 const int secondTriangleVertexIndexOffset = m_caseType == CASETYPE_BASIC ? 0
1802 : m_caseType == CASETYPE_PRECISE ? 1
1803 : -1;
1804 DE_ASSERT(secondTriangleVertexIndexOffset != -1);
1805
1806 for (int k = 0; k < 3; k++)
1807 gridIndices.push_back(corners[(k+0 + i + (2-j%3)) % 3]);
1808 for (int k = 0; k < 3; k++)
1809 gridIndices.push_back(corners[(k+2 + i + (2-j%3) + secondTriangleVertexIndexOffset) % 3 + 1]);
1810 }
1811 }
1812 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
1813 {
1814 for (int i = 0; i < gridHeight; i++)
1815 for (int j = 0; j < gridWidth; j++)
1816 {
1817 // \note The vertices are ordered such that when multiple quads
1818 // share a vertices, it's at the same index for everyone.
1819 for (int m = 0; m < 2; m++)
1820 for (int n = 0; n < 2; n++)
1821 gridIndices.push_back((deUint16)((i+(i+m)%2)*(gridWidth+1) + j+(j+n)%2));
1822
1823 if(m_caseType == CASETYPE_PRECISE && (i+j) % 2 == 0)
1824 std::reverse(gridIndices.begin() + (gridIndices.size() - 4),
1825 gridIndices.begin() + gridIndices.size());
1826 }
1827 }
1828 else
1829 DE_ASSERT(false);
1830
1831 DE_ASSERT((int)gridPosComps.size() == totalNumPosComps);
1832 DE_ASSERT((int)gridTessParams.size() == numVertices);
1833 DE_ASSERT((int)gridIndices.size() == numIndices);
1834
1835 setViewport(gl, viewport);
1836 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
1837 gl.useProgram(programGL);
1838
1839 {
1840 gl.patchParameteri(GL_PATCH_VERTICES, m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4 : -1);
1841 gl.clear(GL_COLOR_BUFFER_BIT);
1842
1843 const glu::VertexArrayBinding attrBindings[] =
1844 {
1845 glu::va::Float("in_v_position", numPosCompsPerVertex, numVertices, 0, &gridPosComps[0]),
1846 glu::va::Float("in_v_tessParam", 1, numVertices, 0, &gridTessParams[0])
1847 };
1848
1849 glu::draw(renderCtx, programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
1850 glu::pr::Patches((int)gridIndices.size(), &gridIndices[0]));
1851 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
1852 }
1853
1854 {
1855 const tcu::Surface rendered = getPixels(renderCtx, viewport);
1856
1857 log << TestLog::Image("RenderedImage", "Rendered Image", rendered)
1858 << TestLog::Message << "Note: coloring is done to clarify the positioning and orientation of the "
1859 << (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "triangles" : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? "quads" : DE_NULL)
1860 << "; the color of a vertex corresponds to the index of that vertex in the patch"
1861 << TestLog::EndMessage;
1862
1863 if (m_caseType == CASETYPE_BASIC)
1864 log << TestLog::Message << "Note: each shared vertex has the same index among the primitives it belongs to" << TestLog::EndMessage;
1865 else if (m_caseType == CASETYPE_PRECISE)
1866 log << TestLog::Message << "Note: the 'precise' qualifier is used to avoid cracks between primitives" << TestLog::EndMessage;
1867 else
1868 DE_ASSERT(false);
1869
1870 // Ad-hoc result verification - check that a certain rectangle in the image contains no black pixels.
1871
1872 const int startX = (int)(0.15f * (float)rendered.getWidth());
1873 const int endX = (int)(0.85f * (float)rendered.getWidth());
1874 const int startY = (int)(0.15f * (float)rendered.getHeight());
1875 const int endY = (int)(0.85f * (float)rendered.getHeight());
1876
1877 for (int y = startY; y < endY; y++)
1878 for (int x = startX; x < endX; x++)
1879 {
1880 const tcu::RGBA pixel = rendered.getPixel(x, y);
1881
1882 if (pixel.getRed() == 0 && pixel.getGreen() == 0 && pixel.getBlue() == 0)
1883 {
1884 log << TestLog::Message << "Failure: there seem to be cracks in the rendered result" << TestLog::EndMessage
1885 << TestLog::Message << "Note: pixel with zero r, g and b channels found at " << tcu::IVec2(x, y) << TestLog::EndMessage;
1886
1887 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
1888 return STOP;
1889 }
1890 }
1891
1892 log << TestLog::Message << "Success: there seem to be no cracks in the rendered result" << TestLog::EndMessage;
1893 }
1894
1895 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1896 return STOP;
1897 }
1898
1899 // Check tessellation coordinates (read with transform feedback).
1900 class TessCoordCase : public TestCase
1901 {
1902 public:
TessCoordCase(Context & context,const char * name,const char * description,TessPrimitiveType primitiveType,SpacingMode spacing)1903 TessCoordCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing)
1904 : TestCase (context, name, description)
1905 , m_primitiveType (primitiveType)
1906 , m_spacing (spacing)
1907 {
1908 }
1909
1910 void init (void);
1911 void deinit (void);
1912 IterateResult iterate (void);
1913
1914 private:
1915 struct TessLevels
1916 {
1917 float inner[2];
1918 float outer[4];
1919 };
1920
1921 static const int RENDER_SIZE = 16;
1922
1923 vector<TessLevels> genTessLevelCases (void) const;
1924
1925 const TessPrimitiveType m_primitiveType;
1926 const SpacingMode m_spacing;
1927
1928 SharedPtr<const ShaderProgram> m_program;
1929 };
1930
init(void)1931 void TessCoordCase::init (void)
1932 {
1933 checkTessellationSupport(m_context);
1934 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
1935
1936 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
1937 "${GLSL_PER_VERTEX_OUT}\n"
1938 "\n"
1939 "void main (void)\n"
1940 "{\n"
1941 "}\n");
1942
1943 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
1944 "${TESSELLATION_SHADER_REQUIRE}\n"
1945 "${GLSL_PER_VERTEX_IN_ARR}\n"
1946 "${GLSL_PER_VERTEX_OUT_ARR}\n"
1947 "\n"
1948 "layout (vertices = 1) out;\n"
1949 "\n"
1950 "uniform mediump float u_tessLevelInner0;\n"
1951 "uniform mediump float u_tessLevelInner1;\n"
1952 "\n"
1953 "uniform mediump float u_tessLevelOuter0;\n"
1954 "uniform mediump float u_tessLevelOuter1;\n"
1955 "uniform mediump float u_tessLevelOuter2;\n"
1956 "uniform mediump float u_tessLevelOuter3;\n"
1957 "\n"
1958 "void main (void)\n"
1959 "{\n"
1960 " gl_TessLevelInner[0] = u_tessLevelInner0;\n"
1961 " gl_TessLevelInner[1] = u_tessLevelInner1;\n"
1962 "\n"
1963 " gl_TessLevelOuter[0] = u_tessLevelOuter0;\n"
1964 " gl_TessLevelOuter[1] = u_tessLevelOuter1;\n"
1965 " gl_TessLevelOuter[2] = u_tessLevelOuter2;\n"
1966 " gl_TessLevelOuter[3] = u_tessLevelOuter3;\n"
1967 "}\n");
1968
1969 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
1970 "${TESSELLATION_SHADER_REQUIRE}\n"
1971 "${GLSL_PER_VERTEX_IN_ARR}\n"
1972 "${GLSL_PER_VERTEX_OUT}\n"
1973 "\n"
1974 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, true) +
1975 "\n"
1976 "out highp vec3 out_te_tessCoord;\n"
1977 "\n"
1978 "void main (void)\n"
1979 "{\n"
1980 " out_te_tessCoord = gl_TessCoord;\n"
1981 " gl_Position = vec4(gl_TessCoord.xy*1.6 - 0.8, 0.0, 1.0);\n"
1982 "}\n");
1983
1984 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
1985 "\n"
1986 "layout (location = 0) out mediump vec4 o_color;\n"
1987 "\n"
1988 "void main (void)\n"
1989 "{\n"
1990 " o_color = vec4(1.0);\n"
1991 "}\n");
1992
1993 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
1994 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
1995 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
1996 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
1997 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
1998 << glu::TransformFeedbackVarying ("out_te_tessCoord")
1999 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
2000
2001 m_testCtx.getLog() << *m_program;
2002 if (!m_program->isOk())
2003 TCU_FAIL("Program compilation failed");
2004 }
2005
deinit(void)2006 void TessCoordCase::deinit (void)
2007 {
2008 m_program.clear();
2009 }
2010
genTessLevelCases(void) const2011 vector<TessCoordCase::TessLevels> TessCoordCase::genTessLevelCases (void) const
2012 {
2013 static const TessLevels rawTessLevelCases[] =
2014 {
2015 { { 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
2016 { { 63.0f, 24.0f }, { 15.0f, 42.0f, 10.0f, 12.0f } },
2017 { { 3.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } },
2018 { { 4.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
2019 { { 2.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } },
2020 { { 5.0f, 6.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
2021 { { 1.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
2022 { { 5.0f, 1.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
2023 { { 5.2f, 1.6f }, { 2.9f, 3.4f, 1.5f, 4.1f } }
2024 };
2025
2026 if (m_spacing == SPACINGMODE_EQUAL)
2027 return vector<TessLevels>(DE_ARRAY_BEGIN(rawTessLevelCases), DE_ARRAY_END(rawTessLevelCases));
2028 else
2029 {
2030 vector<TessLevels> result;
2031 result.reserve(DE_LENGTH_OF_ARRAY(rawTessLevelCases));
2032
2033 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(rawTessLevelCases); tessLevelCaseNdx++)
2034 {
2035 TessLevels curTessLevelCase = rawTessLevelCases[tessLevelCaseNdx];
2036
2037 float* const inner = &curTessLevelCase.inner[0];
2038 float* const outer = &curTessLevelCase.outer[0];
2039
2040 for (int j = 0; j < 2; j++) inner[j] = (float)getClampedRoundedTessLevel(m_spacing, inner[j]);
2041 for (int j = 0; j < 4; j++) outer[j] = (float)getClampedRoundedTessLevel(m_spacing, outer[j]);
2042
2043 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
2044 {
2045 if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f)
2046 {
2047 if (inner[0] == 1.0f)
2048 inner[0] = (float)getClampedRoundedTessLevel(m_spacing, inner[0] + 0.1f);
2049 }
2050 }
2051 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
2052 {
2053 if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f || outer[3] > 1.0f)
2054 {
2055 if (inner[0] == 1.0f) inner[0] = (float)getClampedRoundedTessLevel(m_spacing, inner[0] + 0.1f);
2056 if (inner[1] == 1.0f) inner[1] = (float)getClampedRoundedTessLevel(m_spacing, inner[1] + 0.1f);
2057 }
2058 }
2059
2060 result.push_back(curTessLevelCase);
2061 }
2062
2063 DE_ASSERT((int)result.size() == DE_LENGTH_OF_ARRAY(rawTessLevelCases));
2064 return result;
2065 }
2066 }
2067
iterate(void)2068 TessCoordCase::IterateResult TessCoordCase::iterate (void)
2069 {
2070 typedef TransformFeedbackHandler<Vec3> TFHandler;
2071
2072 TestLog& log = m_testCtx.getLog();
2073 const RenderContext& renderCtx = m_context.getRenderContext();
2074 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2075 const deUint32 programGL = m_program->getProgram();
2076 const glw::Functions& gl = renderCtx.getFunctions();
2077
2078 const int tessLevelInner0Loc = gl.getUniformLocation(programGL, "u_tessLevelInner0");
2079 const int tessLevelInner1Loc = gl.getUniformLocation(programGL, "u_tessLevelInner1");
2080 const int tessLevelOuter0Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter0");
2081 const int tessLevelOuter1Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter1");
2082 const int tessLevelOuter2Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter2");
2083 const int tessLevelOuter3Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter3");
2084
2085 const vector<TessLevels> tessLevelCases = genTessLevelCases();
2086 vector<vector<Vec3> > caseReferences (tessLevelCases.size());
2087
2088 for (int i = 0; i < (int)tessLevelCases.size(); i++)
2089 caseReferences[i] = generateReferenceTessCoords(m_primitiveType, m_spacing, &tessLevelCases[i].inner[0], &tessLevelCases[i].outer[0]);
2090
2091 const int maxNumVertices = (int)std::max_element(caseReferences.begin(), caseReferences.end(), SizeLessThan<vector<Vec3> >())->size();
2092 const TFHandler tfHandler (m_context.getRenderContext(), maxNumVertices);
2093
2094 bool success = true;
2095
2096 setViewport(gl, viewport);
2097 gl.useProgram(programGL);
2098
2099 gl.patchParameteri(GL_PATCH_VERTICES, 1);
2100
2101 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < (int)tessLevelCases.size(); tessLevelCaseNdx++)
2102 {
2103 const float* const innerLevels = &tessLevelCases[tessLevelCaseNdx].inner[0];
2104 const float* const outerLevels = &tessLevelCases[tessLevelCaseNdx].outer[0];
2105
2106 log << TestLog::Message << "Tessellation levels: " << tessellationLevelsString(innerLevels, outerLevels, m_primitiveType) << TestLog::EndMessage;
2107
2108 gl.uniform1f(tessLevelInner0Loc, innerLevels[0]);
2109 gl.uniform1f(tessLevelInner1Loc, innerLevels[1]);
2110 gl.uniform1f(tessLevelOuter0Loc, outerLevels[0]);
2111 gl.uniform1f(tessLevelOuter1Loc, outerLevels[1]);
2112 gl.uniform1f(tessLevelOuter2Loc, outerLevels[2]);
2113 gl.uniform1f(tessLevelOuter3Loc, outerLevels[3]);
2114 GLU_EXPECT_NO_ERROR(gl.getError(), "Setup failed");
2115
2116 {
2117 const vector<Vec3>& tessCoordsRef = caseReferences[tessLevelCaseNdx];
2118 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, GL_POINTS, 0, DE_NULL, 1);
2119
2120 if (tfResult.numPrimitives != (int)tessCoordsRef.size())
2121 {
2122 log << TestLog::Message << "Failure: GL reported GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN to be "
2123 << tfResult.numPrimitives << ", reference value is " << tessCoordsRef.size()
2124 << " (logging further info anyway)" << TestLog::EndMessage;
2125 success = false;
2126 }
2127 else
2128 log << TestLog::Message << "Note: GL reported GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN to be " << tfResult.numPrimitives << TestLog::EndMessage;
2129
2130 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
2131 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;
2132 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS || m_primitiveType == TESSPRIMITIVETYPE_ISOLINES)
2133 log << TestLog::Message << "Note: in the following visualization(s), u and v coordinate go left-to-right and bottom-to-top, respectively" << TestLog::EndMessage;
2134 else
2135 DE_ASSERT(false);
2136
2137 success = compareTessCoords(log, m_primitiveType, tessCoordsRef, tfResult.varying) && success;
2138 }
2139
2140 if (!success)
2141 break;
2142 else
2143 log << TestLog::Message << "All OK" << TestLog::EndMessage;
2144 }
2145
2146 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Invalid tessellation coordinates");
2147 return STOP;
2148 }
2149
2150 // Check validity of fractional spacing modes. Draws a single isoline, reads tesscoords with transform feedback.
2151 class FractionalSpacingModeCase : public TestCase
2152 {
2153 public:
FractionalSpacingModeCase(Context & context,const char * name,const char * description,SpacingMode spacing)2154 FractionalSpacingModeCase (Context& context, const char* name, const char* description, SpacingMode spacing)
2155 : TestCase (context, name, description)
2156 , m_spacing (spacing)
2157 {
2158 DE_ASSERT(m_spacing == SPACINGMODE_FRACTIONAL_EVEN || m_spacing == SPACINGMODE_FRACTIONAL_ODD);
2159 }
2160
2161 void init (void);
2162 void deinit (void);
2163 IterateResult iterate (void);
2164
2165 private:
2166 static const int RENDER_SIZE = 16;
2167
2168 static vector<float> genTessLevelCases (void);
2169
2170 const SpacingMode m_spacing;
2171
2172 SharedPtr<const ShaderProgram> m_program;
2173 };
2174
init(void)2175 void FractionalSpacingModeCase::init (void)
2176 {
2177 checkTessellationSupport(m_context);
2178 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
2179
2180 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
2181 "${GLSL_PER_VERTEX_OUT}\n"
2182 "\n"
2183 "void main (void)\n"
2184 "{\n"
2185 "}\n");
2186 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
2187 "${TESSELLATION_SHADER_REQUIRE}\n"
2188 "${GLSL_PER_VERTEX_IN_ARR}\n"
2189 "${GLSL_PER_VERTEX_OUT_ARR}\n"
2190 "\n"
2191 "layout (vertices = 1) out;\n"
2192 "\n"
2193 "uniform mediump float u_tessLevelOuter1;\n"
2194 "\n"
2195 "void main (void)\n"
2196 "{\n"
2197 " gl_TessLevelOuter[0] = 1.0;\n"
2198 " gl_TessLevelOuter[1] = u_tessLevelOuter1;\n"
2199 "}\n");
2200 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
2201 "${TESSELLATION_SHADER_REQUIRE}\n"
2202 "${GLSL_PER_VERTEX_IN_ARR}\n"
2203 "${GLSL_PER_VERTEX_OUT}\n"
2204 "\n"
2205 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_ISOLINES, m_spacing, true) +
2206 "\n"
2207 "out highp float out_te_tessCoord;\n"
2208 "\n"
2209 "void main (void)\n"
2210 "{\n"
2211 " out_te_tessCoord = gl_TessCoord.x;\n"
2212 " gl_Position = vec4(gl_TessCoord.xy*1.6 - 0.8, 0.0, 1.0);\n"
2213 "}\n");
2214 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
2215 "\n"
2216 "layout (location = 0) out mediump vec4 o_color;\n"
2217 "\n"
2218 "void main (void)\n"
2219 "{\n"
2220 " o_color = vec4(1.0);\n"
2221 "}\n");
2222
2223 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
2224 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
2225 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
2226 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
2227 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
2228 << glu::TransformFeedbackVarying ("out_te_tessCoord")
2229 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
2230
2231 m_testCtx.getLog() << *m_program;
2232 if (!m_program->isOk())
2233 TCU_FAIL("Program compilation failed");
2234 }
2235
deinit(void)2236 void FractionalSpacingModeCase::deinit (void)
2237 {
2238 m_program.clear();
2239 }
2240
genTessLevelCases(void)2241 vector<float> FractionalSpacingModeCase::genTessLevelCases (void)
2242 {
2243 vector<float> result;
2244
2245 // Ranges [7.0 .. 8.0), [8.0 .. 9.0) and [9.0 .. 10.0)
2246 {
2247 static const float rangeStarts[] = { 7.0f, 8.0f, 9.0f };
2248 const int numSamplesPerRange = 10;
2249
2250 for (int rangeNdx = 0; rangeNdx < DE_LENGTH_OF_ARRAY(rangeStarts); rangeNdx++)
2251 for (int i = 0; i < numSamplesPerRange; i++)
2252 result.push_back(rangeStarts[rangeNdx] + (float)i/(float)numSamplesPerRange);
2253 }
2254
2255 // 0.3, 1.3, 2.3, ... , 62.3
2256 for (int i = 0; i <= 62; i++)
2257 result.push_back((float)i + 0.3f);
2258
2259 return result;
2260 }
2261
iterate(void)2262 FractionalSpacingModeCase::IterateResult FractionalSpacingModeCase::iterate (void)
2263 {
2264 typedef TransformFeedbackHandler<float> TFHandler;
2265
2266 TestLog& log = m_testCtx.getLog();
2267 const RenderContext& renderCtx = m_context.getRenderContext();
2268 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2269 const deUint32 programGL = m_program->getProgram();
2270 const glw::Functions& gl = renderCtx.getFunctions();
2271
2272 const int tessLevelOuter1Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter1");
2273
2274 // Second outer tessellation levels.
2275 const vector<float> tessLevelCases = genTessLevelCases();
2276 const int maxNumVertices = 1 + getClampedRoundedTessLevel(m_spacing, *std::max_element(tessLevelCases.begin(), tessLevelCases.end()));
2277 vector<float> additionalSegmentLengths;
2278 vector<int> additionalSegmentLocations;
2279
2280 const TFHandler tfHandler (m_context.getRenderContext(), maxNumVertices);
2281
2282 bool success = true;
2283
2284 setViewport(gl, viewport);
2285 gl.useProgram(programGL);
2286
2287 gl.patchParameteri(GL_PATCH_VERTICES, 1);
2288
2289 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < (int)tessLevelCases.size(); tessLevelCaseNdx++)
2290 {
2291 const float outerLevel1 = tessLevelCases[tessLevelCaseNdx];
2292
2293 gl.uniform1f(tessLevelOuter1Loc, outerLevel1);
2294 GLU_EXPECT_NO_ERROR(gl.getError(), "Setup failed");
2295
2296 {
2297 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, GL_POINTS, 0, DE_NULL, 1);
2298 float additionalSegmentLength;
2299 int additionalSegmentLocation;
2300
2301 success = verifyFractionalSpacingSingle(log, m_spacing, outerLevel1, tfResult.varying,
2302 additionalSegmentLength, additionalSegmentLocation);
2303
2304 if (!success)
2305 break;
2306
2307 additionalSegmentLengths.push_back(additionalSegmentLength);
2308 additionalSegmentLocations.push_back(additionalSegmentLocation);
2309 }
2310 }
2311
2312 if (success)
2313 success = verifyFractionalSpacingMultiple(log, m_spacing, tessLevelCases, additionalSegmentLengths, additionalSegmentLocations);
2314
2315 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Invalid tessellation coordinates");
2316 return STOP;
2317 }
2318
2319 // 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.
2320 class BasicVariousTessLevelsPosAttrCase : public TestCase
2321 {
2322 public:
BasicVariousTessLevelsPosAttrCase(Context & context,const char * name,const char * description,TessPrimitiveType primitiveType,SpacingMode spacing,const char * referenceImagePathPrefix)2323 BasicVariousTessLevelsPosAttrCase (Context& context,
2324 const char* name,
2325 const char* description,
2326 TessPrimitiveType primitiveType,
2327 SpacingMode spacing,
2328 const char* referenceImagePathPrefix)
2329 : TestCase (context, name, description)
2330 , m_primitiveType (primitiveType)
2331 , m_spacing (spacing)
2332 , m_referenceImagePathPrefix (referenceImagePathPrefix)
2333 {
2334 }
2335
2336 void init (void);
2337 void deinit (void);
2338 IterateResult iterate (void);
2339
2340 protected:
2341 virtual const glu::ProgramSources makeSources (TessPrimitiveType, SpacingMode, const char* vtxOutPosAttrName) const = DE_NULL;
2342
2343 private:
2344 static const int RENDER_SIZE = 256;
2345
2346 const TessPrimitiveType m_primitiveType;
2347 const SpacingMode m_spacing;
2348 const string m_referenceImagePathPrefix;
2349
2350 SharedPtr<const ShaderProgram> m_program;
2351 };
2352
init(void)2353 void BasicVariousTessLevelsPosAttrCase::init (void)
2354 {
2355 checkTessellationSupport(m_context);
2356 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
2357
2358 {
2359 glu::ProgramSources sources = makeSources(m_primitiveType, m_spacing, "in_tc_position");
2360 DE_ASSERT(sources.sources[glu::SHADERTYPE_TESSELLATION_CONTROL].empty());
2361
2362 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
2363 "${TESSELLATION_SHADER_REQUIRE}\n"
2364 "${GLSL_PER_VERTEX_IN_ARR}\n"
2365 "${GLSL_PER_VERTEX_OUT_ARR}\n"
2366 "\n"
2367 "layout (vertices = " + string(m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "3" : "4") + ") out;\n"
2368 "\n"
2369 "in highp vec2 in_tc_position[];\n"
2370 "\n"
2371 "out highp vec2 in_te_position[];\n"
2372 "\n"
2373 "uniform mediump float u_tessLevelInner0;\n"
2374 "uniform mediump float u_tessLevelInner1;\n"
2375 "uniform mediump float u_tessLevelOuter0;\n"
2376 "uniform mediump float u_tessLevelOuter1;\n"
2377 "uniform mediump float u_tessLevelOuter2;\n"
2378 "uniform mediump float u_tessLevelOuter3;\n"
2379 "\n"
2380 "void main (void)\n"
2381 "{\n"
2382 " in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];\n"
2383 "\n"
2384 " gl_TessLevelInner[0] = u_tessLevelInner0;\n"
2385 " gl_TessLevelInner[1] = u_tessLevelInner1;\n"
2386 "\n"
2387 " gl_TessLevelOuter[0] = u_tessLevelOuter0;\n"
2388 " gl_TessLevelOuter[1] = u_tessLevelOuter1;\n"
2389 " gl_TessLevelOuter[2] = u_tessLevelOuter2;\n"
2390 " gl_TessLevelOuter[3] = u_tessLevelOuter3;\n"
2391 "}\n");
2392
2393 sources << glu::TessellationControlSource(specializeShader(m_context, tessellationControlTemplate.c_str()));
2394
2395 m_program = SharedPtr<const ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources));
2396 }
2397
2398 m_testCtx.getLog() << *m_program;
2399 if (!m_program->isOk())
2400 TCU_FAIL("Program compilation failed");
2401 }
2402
deinit(void)2403 void BasicVariousTessLevelsPosAttrCase::deinit (void)
2404 {
2405 m_program.clear();
2406 }
2407
iterate(void)2408 BasicVariousTessLevelsPosAttrCase::IterateResult BasicVariousTessLevelsPosAttrCase::iterate (void)
2409 {
2410 static const struct
2411 {
2412 float inner[2];
2413 float outer[4];
2414 } tessLevelCases[] =
2415 {
2416 { { 9.0f, 9.0f }, { 9.0f, 9.0f, 9.0f, 9.0f } },
2417 { { 8.0f, 11.0f }, { 13.0f, 15.0f, 18.0f, 21.0f } },
2418 { { 17.0f, 14.0f }, { 3.0f, 6.0f, 9.0f, 12.0f } }
2419 };
2420
2421 TestLog& log = m_testCtx.getLog();
2422 const RenderContext& renderCtx = m_context.getRenderContext();
2423 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2424 const deUint32 programGL = m_program->getProgram();
2425 const glw::Functions& gl = renderCtx.getFunctions();
2426 const int patchSize = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3
2427 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4
2428 : m_primitiveType == TESSPRIMITIVETYPE_ISOLINES ? 4
2429 : -1;
2430
2431 setViewport(gl, viewport);
2432 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
2433 gl.useProgram(programGL);
2434
2435 gl.patchParameteri(GL_PATCH_VERTICES, patchSize);
2436
2437 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(tessLevelCases); tessLevelCaseNdx++)
2438 {
2439 float innerLevels[2];
2440 float outerLevels[4];
2441
2442 for (int i = 0; i < DE_LENGTH_OF_ARRAY(innerLevels); i++)
2443 innerLevels[i] = (float)getClampedRoundedTessLevel(m_spacing, tessLevelCases[tessLevelCaseNdx].inner[i]);
2444
2445 for (int i = 0; i < DE_LENGTH_OF_ARRAY(outerLevels); i++)
2446 outerLevels[i] = (float)getClampedRoundedTessLevel(m_spacing, tessLevelCases[tessLevelCaseNdx].outer[i]);
2447
2448 log << TestLog::Message << "Tessellation levels: " << tessellationLevelsString(&innerLevels[0], &outerLevels[0], m_primitiveType) << TestLog::EndMessage;
2449
2450 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelInner0"), innerLevels[0]);
2451 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelInner1"), innerLevels[1]);
2452 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter0"), outerLevels[0]);
2453 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter1"), outerLevels[1]);
2454 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter2"), outerLevels[2]);
2455 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter3"), outerLevels[3]);
2456
2457 gl.clear(GL_COLOR_BUFFER_BIT);
2458
2459 {
2460 vector<Vec2> positions;
2461 positions.reserve(4);
2462
2463 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
2464 {
2465 positions.push_back(Vec2( 0.8f, 0.6f));
2466 positions.push_back(Vec2( 0.0f, -0.786f));
2467 positions.push_back(Vec2(-0.8f, 0.6f));
2468 }
2469 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS || m_primitiveType == TESSPRIMITIVETYPE_ISOLINES)
2470 {
2471 positions.push_back(Vec2(-0.8f, -0.8f));
2472 positions.push_back(Vec2( 0.8f, -0.8f));
2473 positions.push_back(Vec2(-0.8f, 0.8f));
2474 positions.push_back(Vec2( 0.8f, 0.8f));
2475 }
2476 else
2477 DE_ASSERT(false);
2478
2479 DE_ASSERT((int)positions.size() == patchSize);
2480
2481 const glu::VertexArrayBinding attrBindings[] =
2482 {
2483 glu::va::Float("in_v_position", 2, (int)positions.size(), 0, &positions[0].x())
2484 };
2485
2486 glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
2487 glu::pr::Patches(patchSize));
2488 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
2489 }
2490
2491 {
2492 const tcu::Surface rendered = getPixels(renderCtx, viewport);
2493 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePathPrefix + "_" + de::toString(tessLevelCaseNdx) + ".png");
2494 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.002f, tcu::COMPARE_LOG_RESULT);
2495
2496 if (!success)
2497 {
2498 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
2499 return STOP;
2500 }
2501 }
2502 }
2503
2504 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2505 return STOP;
2506 }
2507
2508 // Test that there are no obvious gaps in the triangulation of a tessellated triangle or quad.
2509 class BasicTriangleFillCoverCase : public BasicVariousTessLevelsPosAttrCase
2510 {
2511 public:
BasicTriangleFillCoverCase(Context & context,const char * name,const char * description,TessPrimitiveType primitiveType,SpacingMode spacing,const char * referenceImagePathPrefix)2512 BasicTriangleFillCoverCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing, const char* referenceImagePathPrefix)
2513 : BasicVariousTessLevelsPosAttrCase (context, name, description, primitiveType, spacing, referenceImagePathPrefix)
2514 {
2515 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
2516 }
2517
2518 protected:
init(void)2519 void init (void)
2520 {
2521 checkGPUShader5Support(m_context);
2522 BasicVariousTessLevelsPosAttrCase::init();
2523 }
2524
makeSources(TessPrimitiveType primitiveType,SpacingMode spacing,const char * vtxOutPosAttrName) const2525 const glu::ProgramSources makeSources (TessPrimitiveType primitiveType, SpacingMode spacing, const char* vtxOutPosAttrName) const
2526 {
2527 bool isGL45 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5));
2528 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
2529 "${GLSL_PER_VERTEX_OUT}\n"
2530 "\n"
2531 "in highp vec2 in_v_position;\n"
2532 "\n"
2533 "out highp vec2 " + string(vtxOutPosAttrName) + ";\n"
2534 "\n"
2535 "void main (void)\n"
2536 "{\n"
2537 " " + vtxOutPosAttrName + " = in_v_position;\n"
2538 "}\n");
2539 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
2540 "${TESSELLATION_SHADER_REQUIRE}\n"
2541 "${GPU_SHADER5_REQUIRE}\n"
2542 "${GLSL_PER_VERTEX_IN_ARR}\n"
2543 "${GLSL_PRECISE_PER_VERTEX_OUT}\n"
2544 "\n"
2545 + getTessellationEvaluationInLayoutString(primitiveType, spacing) +
2546 "\n"
2547 "in highp vec2 in_te_position[];\n"
2548 "\n"
2549 + (isGL45 ? "" : "precise gl_Position;\n") +
2550 "void main (void)\n"
2551 "{\n"
2552 + (primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
2553 "\n"
2554 " highp float d = 3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z));\n"
2555 " highp vec2 corner0 = in_te_position[0];\n"
2556 " highp vec2 corner1 = in_te_position[1];\n"
2557 " highp vec2 corner2 = in_te_position[2];\n"
2558 " highp vec2 pos = corner0*gl_TessCoord.x + corner1*gl_TessCoord.y + corner2*gl_TessCoord.z;\n"
2559 " highp vec2 fromCenter = pos - (corner0 + corner1 + corner2) / 3.0;\n"
2560 " highp float f = (1.0 - length(fromCenter)) * (1.5 - d);\n"
2561 " pos += 0.75 * f * fromCenter / (length(fromCenter) + 0.3);\n"
2562 " gl_Position = vec4(pos, 0.0, 1.0);\n"
2563 : primitiveType == TESSPRIMITIVETYPE_QUADS ?
2564 " highp vec2 corner0 = in_te_position[0];\n"
2565 " highp vec2 corner1 = in_te_position[1];\n"
2566 " highp vec2 corner2 = in_te_position[2];\n"
2567 " highp vec2 corner3 = in_te_position[3];\n"
2568 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
2569 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
2570 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n"
2571 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n"
2572 " highp float d = 2.0 * min(abs(gl_TessCoord.x-0.5), abs(gl_TessCoord.y-0.5));\n"
2573 " highp vec2 fromCenter = pos - (corner0 + corner1 + corner2 + corner3) / 4.0;\n"
2574 " highp float f = (1.0 - length(fromCenter)) * sqrt(1.7 - d);\n"
2575 " pos += 0.75 * f * fromCenter / (length(fromCenter) + 0.3);\n"
2576 " gl_Position = vec4(pos, 0.0, 1.0);\n"
2577 : DE_NULL) +
2578 "}\n");
2579 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
2580 "\n"
2581 "layout (location = 0) out mediump vec4 o_color;\n"
2582 "\n"
2583 "void main (void)\n"
2584 "{\n"
2585 " o_color = vec4(1.0);\n"
2586 "}\n");
2587
2588 return glu::ProgramSources()
2589 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
2590 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
2591 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()));
2592 }
2593 };
2594
2595 // Check that there are no obvious overlaps in the triangulation of a tessellated triangle or quad.
2596 class BasicTriangleFillNonOverlapCase : public BasicVariousTessLevelsPosAttrCase
2597 {
2598 public:
BasicTriangleFillNonOverlapCase(Context & context,const char * name,const char * description,TessPrimitiveType primitiveType,SpacingMode spacing,const char * referenceImagePathPrefix)2599 BasicTriangleFillNonOverlapCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing, const char* referenceImagePathPrefix)
2600 : BasicVariousTessLevelsPosAttrCase (context, name, description, primitiveType, spacing, referenceImagePathPrefix)
2601 {
2602 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
2603 }
2604
2605 protected:
makeSources(TessPrimitiveType primitiveType,SpacingMode spacing,const char * vtxOutPosAttrName) const2606 const glu::ProgramSources makeSources (TessPrimitiveType primitiveType, SpacingMode spacing, const char* vtxOutPosAttrName) const
2607 {
2608 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
2609 "${GLSL_PER_VERTEX_OUT}\n"
2610 "\n"
2611 "in highp vec2 in_v_position;\n"
2612 "\n"
2613 "out highp vec2 " + string(vtxOutPosAttrName) + ";\n"
2614 "\n"
2615 "void main (void)\n"
2616 "{\n"
2617 " " + vtxOutPosAttrName + " = in_v_position;\n"
2618 "}\n");
2619 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
2620 "${TESSELLATION_SHADER_REQUIRE}\n"
2621 "${GLSL_PER_VERTEX_IN_ARR}\n"
2622 "${GLSL_PER_VERTEX_OUT}\n"
2623 "\n"
2624 + getTessellationEvaluationInLayoutString(primitiveType, spacing) +
2625 "\n"
2626 "in highp vec2 in_te_position[];\n"
2627 "\n"
2628 "out mediump vec4 in_f_color;\n"
2629 "\n"
2630 "uniform mediump float u_tessLevelInner0;\n"
2631 "uniform mediump float u_tessLevelInner1;\n"
2632 "\n"
2633 "void main (void)\n"
2634 "{\n"
2635 + (primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
2636 "\n"
2637 " highp vec2 corner0 = in_te_position[0];\n"
2638 " highp vec2 corner1 = in_te_position[1];\n"
2639 " highp vec2 corner2 = in_te_position[2];\n"
2640 " highp vec2 pos = corner0*gl_TessCoord.x + corner1*gl_TessCoord.y + corner2*gl_TessCoord.z;\n"
2641 " gl_Position = vec4(pos, 0.0, 1.0);\n"
2642 " highp int numConcentricTriangles = int(round(u_tessLevelInner0)) / 2 + 1;\n"
2643 " highp float d = 3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z));\n"
2644 " highp int phase = int(d*float(numConcentricTriangles)) % 3;\n"
2645 " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
2646 " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
2647 " : vec4(0.0, 0.0, 1.0, 1.0);\n"
2648 : primitiveType == TESSPRIMITIVETYPE_QUADS ?
2649 " highp vec2 corner0 = in_te_position[0];\n"
2650 " highp vec2 corner1 = in_te_position[1];\n"
2651 " highp vec2 corner2 = in_te_position[2];\n"
2652 " highp vec2 corner3 = in_te_position[3];\n"
2653 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
2654 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
2655 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n"
2656 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n"
2657 " gl_Position = vec4(pos, 0.0, 1.0);\n"
2658 " highp int phaseX = int(round((0.5 - abs(gl_TessCoord.x-0.5)) * u_tessLevelInner0));\n"
2659 " highp int phaseY = int(round((0.5 - abs(gl_TessCoord.y-0.5)) * u_tessLevelInner1));\n"
2660 " highp int phase = min(phaseX, phaseY) % 3;\n"
2661 " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
2662 " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
2663 " : vec4(0.0, 0.0, 1.0, 1.0);\n"
2664 : DE_NULL) +
2665 "}\n");
2666 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
2667 "\n"
2668 "layout (location = 0) out mediump vec4 o_color;\n"
2669 "\n"
2670 "in mediump vec4 in_f_color;\n"
2671 "\n"
2672 "void main (void)\n"
2673 "{\n"
2674 " o_color = in_f_color;\n"
2675 "}\n");
2676
2677 return glu::ProgramSources()
2678 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
2679 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
2680 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()));
2681 }
2682 };
2683
2684 // Basic isolines rendering case.
2685 class IsolinesRenderCase : public BasicVariousTessLevelsPosAttrCase
2686 {
2687 public:
IsolinesRenderCase(Context & context,const char * name,const char * description,SpacingMode spacing,const char * referenceImagePathPrefix)2688 IsolinesRenderCase (Context& context, const char* name, const char* description, SpacingMode spacing, const char* referenceImagePathPrefix)
2689 : BasicVariousTessLevelsPosAttrCase (context, name, description, TESSPRIMITIVETYPE_ISOLINES, spacing, referenceImagePathPrefix)
2690 {
2691 }
2692
2693 protected:
makeSources(TessPrimitiveType primitiveType,SpacingMode spacing,const char * vtxOutPosAttrName) const2694 const glu::ProgramSources makeSources (TessPrimitiveType primitiveType, SpacingMode spacing, const char* vtxOutPosAttrName) const
2695 {
2696 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_ISOLINES);
2697 DE_UNREF(primitiveType);
2698
2699 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
2700 "${GLSL_PER_VERTEX_OUT}\n"
2701 "\n"
2702 "in highp vec2 in_v_position;\n"
2703 "\n"
2704 "out highp vec2 " + string(vtxOutPosAttrName) + ";\n"
2705 "\n"
2706 "void main (void)\n"
2707 "{\n"
2708 " " + vtxOutPosAttrName + " = in_v_position;\n"
2709 "}\n");
2710 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
2711 "${TESSELLATION_SHADER_REQUIRE}\n"
2712 "${GLSL_PER_VERTEX_IN_ARR}\n"
2713 "${GLSL_PER_VERTEX_OUT}\n"
2714 "\n"
2715 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_ISOLINES, spacing) +
2716 "\n"
2717 "in highp vec2 in_te_position[];\n"
2718 "\n"
2719 "out mediump vec4 in_f_color;\n"
2720 "\n"
2721 "uniform mediump float u_tessLevelOuter0;\n"
2722 "uniform mediump float u_tessLevelOuter1;\n"
2723 "\n"
2724 "void main (void)\n"
2725 "{\n"
2726 " highp vec2 corner0 = in_te_position[0];\n"
2727 " highp vec2 corner1 = in_te_position[1];\n"
2728 " highp vec2 corner2 = in_te_position[2];\n"
2729 " highp vec2 corner3 = in_te_position[3];\n"
2730 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
2731 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
2732 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n"
2733 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n"
2734 " pos.y += 0.15*sin(gl_TessCoord.x*10.0);\n"
2735 " gl_Position = vec4(pos, 0.0, 1.0);\n"
2736 " highp int phaseX = int(round(gl_TessCoord.x*u_tessLevelOuter1));\n"
2737 " highp int phaseY = int(round(gl_TessCoord.y*u_tessLevelOuter0));\n"
2738 " highp int phase = (phaseX + phaseY) % 3;\n"
2739 " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
2740 " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
2741 " : vec4(0.0, 0.0, 1.0, 1.0);\n"
2742 "}\n");
2743 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
2744 "\n"
2745 "layout (location = 0) out mediump vec4 o_color;\n"
2746 "\n"
2747 "in mediump vec4 in_f_color;\n"
2748 "\n"
2749 "void main (void)\n"
2750 "{\n"
2751 " o_color = in_f_color;\n"
2752 "}\n");
2753
2754 return glu::ProgramSources()
2755 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
2756 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
2757 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()));
2758 }
2759 };
2760
2761 // Test the "cw" and "ccw" TES input layout qualifiers.
2762 class WindingCase : public TestCase
2763 {
2764 public:
WindingCase(Context & context,const char * name,const char * description,TessPrimitiveType primitiveType,Winding winding)2765 WindingCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, Winding winding)
2766 : TestCase (context, name, description)
2767 , m_primitiveType (primitiveType)
2768 , m_winding (winding)
2769 {
2770 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
2771 }
2772
2773 void init (void);
2774 void deinit (void);
2775 IterateResult iterate (void);
2776
2777 private:
2778 static const int RENDER_SIZE = 64;
2779
2780 const TessPrimitiveType m_primitiveType;
2781 const Winding m_winding;
2782
2783 SharedPtr<const ShaderProgram> m_program;
2784 };
2785
init(void)2786 void WindingCase::init (void)
2787 {
2788 checkTessellationSupport(m_context);
2789 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
2790
2791 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
2792 "${GLSL_PER_VERTEX_OUT}\n"
2793 "\n"
2794 "void main (void)\n"
2795 "{\n"
2796 "}\n");
2797 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
2798 "${TESSELLATION_SHADER_REQUIRE}\n"
2799 "${GLSL_PER_VERTEX_IN_ARR}\n"
2800 "${GLSL_PER_VERTEX_OUT_ARR}\n"
2801 "\n"
2802 "layout (vertices = 1) out;\n"
2803 "\n"
2804 "void main (void)\n"
2805 "{\n"
2806 " gl_TessLevelInner[0] = 5.0;\n"
2807 " gl_TessLevelInner[1] = 5.0;\n"
2808 "\n"
2809 " gl_TessLevelOuter[0] = 5.0;\n"
2810 " gl_TessLevelOuter[1] = 5.0;\n"
2811 " gl_TessLevelOuter[2] = 5.0;\n"
2812 " gl_TessLevelOuter[3] = 5.0;\n"
2813 "}\n");
2814 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
2815 "${TESSELLATION_SHADER_REQUIRE}\n"
2816 "${GLSL_PER_VERTEX_IN_ARR}\n"
2817 "${GLSL_PER_VERTEX_OUT}\n"
2818 "\n"
2819 + getTessellationEvaluationInLayoutString(m_primitiveType, m_winding) +
2820 "\n"
2821 "void main (void)\n"
2822 "{\n"
2823 " gl_Position = vec4(gl_TessCoord.xy*2.0 - 1.0, 0.0, 1.0);\n"
2824 "}\n");
2825 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
2826 "\n"
2827 "layout (location = 0) out mediump vec4 o_color;\n"
2828 "\n"
2829 "void main (void)\n"
2830 "{\n"
2831 " o_color = vec4(1.0);\n"
2832 "}\n");
2833
2834 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
2835 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
2836 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
2837 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
2838 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))));
2839
2840 m_testCtx.getLog() << *m_program;
2841 if (!m_program->isOk())
2842 TCU_FAIL("Program compilation failed");
2843 }
2844
deinit(void)2845 void WindingCase::deinit (void)
2846 {
2847 m_program.clear();
2848 }
2849
iterate(void)2850 WindingCase::IterateResult WindingCase::iterate (void)
2851 {
2852 TestLog& log = m_testCtx.getLog();
2853 const RenderContext& renderCtx = m_context.getRenderContext();
2854 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2855 const deUint32 programGL = m_program->getProgram();
2856 const glw::Functions& gl = renderCtx.getFunctions();
2857 const glu::VertexArray vao (renderCtx);
2858
2859 bool success = true;
2860
2861 setViewport(gl, viewport);
2862 gl.clearColor(1.0f, 0.0f, 0.0f, 1.0f);
2863 gl.useProgram(programGL);
2864
2865 gl.patchParameteri(GL_PATCH_VERTICES, 1);
2866
2867 gl.enable(GL_CULL_FACE);
2868
2869 gl.bindVertexArray(*vao);
2870
2871 log << TestLog::Message << "Face culling enabled" << TestLog::EndMessage;
2872
2873 for (int frontFaceWinding = 0; frontFaceWinding < WINDING_LAST; frontFaceWinding++)
2874 {
2875 log << TestLog::Message << "Setting glFrontFace(" << (frontFaceWinding == WINDING_CW ? "GL_CW" : "GL_CCW") << ")" << TestLog::EndMessage;
2876
2877 gl.frontFace(frontFaceWinding == WINDING_CW ? GL_CW : GL_CCW);
2878
2879 gl.clear(GL_COLOR_BUFFER_BIT);
2880 gl.drawArrays(GL_PATCHES, 0, 1);
2881 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
2882
2883 {
2884 const tcu::Surface rendered = getPixels(renderCtx, viewport);
2885 log << TestLog::Image("RenderedImage", "Rendered Image", rendered);
2886
2887 {
2888 const int totalNumPixels = rendered.getWidth()*rendered.getHeight();
2889 const int badPixelTolerance = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 5*de::max(rendered.getWidth(), rendered.getHeight()) : 0;
2890
2891 int numWhitePixels = 0;
2892 int numRedPixels = 0;
2893 for (int y = 0; y < rendered.getHeight(); y++)
2894 for (int x = 0; x < rendered.getWidth(); x++)
2895 {
2896 numWhitePixels += rendered.getPixel(x, y) == tcu::RGBA::white() ? 1 : 0;
2897 numRedPixels += rendered.getPixel(x, y) == tcu::RGBA::red() ? 1 : 0;
2898 }
2899
2900 DE_ASSERT(numWhitePixels + numRedPixels <= totalNumPixels);
2901
2902 log << TestLog::Message << "Note: got " << numWhitePixels << " white and " << numRedPixels << " red pixels" << TestLog::EndMessage;
2903
2904 if (totalNumPixels - numWhitePixels - numRedPixels > badPixelTolerance)
2905 {
2906 log << TestLog::Message << "Failure: Got " << totalNumPixels - numWhitePixels - numRedPixels << " other than white or red pixels (maximum tolerance " << badPixelTolerance << ")" << TestLog::EndMessage;
2907 success = false;
2908 break;
2909 }
2910
2911 if ((Winding)frontFaceWinding == m_winding)
2912 {
2913 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
2914 {
2915 if (de::abs(numWhitePixels - totalNumPixels/2) > badPixelTolerance)
2916 {
2917 log << TestLog::Message << "Failure: wrong number of white pixels; expected approximately " << totalNumPixels/2 << TestLog::EndMessage;
2918 success = false;
2919 break;
2920 }
2921 }
2922 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
2923 {
2924 if (numWhitePixels != totalNumPixels)
2925 {
2926 log << TestLog::Message << "Failure: expected only white pixels (full-viewport quad)" << TestLog::EndMessage;
2927 success = false;
2928 break;
2929 }
2930 }
2931 else
2932 DE_ASSERT(false);
2933 }
2934 else
2935 {
2936 if (numWhitePixels != 0)
2937 {
2938 log << TestLog::Message << "Failure: expected only red pixels (everything culled)" << TestLog::EndMessage;
2939 success = false;
2940 break;
2941 }
2942 }
2943 }
2944 }
2945 }
2946
2947 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image verification failed");
2948 return STOP;
2949 }
2950
2951 // Test potentially differing input and output patch sizes.
2952 class PatchVertexCountCase : public TestCase
2953 {
2954 public:
PatchVertexCountCase(Context & context,const char * name,const char * description,int inputPatchSize,int outputPatchSize,const char * referenceImagePath)2955 PatchVertexCountCase (Context& context, const char* name, const char* description, int inputPatchSize, int outputPatchSize, const char* referenceImagePath)
2956 : TestCase (context, name, description)
2957 , m_inputPatchSize (inputPatchSize)
2958 , m_outputPatchSize (outputPatchSize)
2959 , m_referenceImagePath (referenceImagePath)
2960 {
2961 }
2962
2963 void init (void);
2964 void deinit (void);
2965 IterateResult iterate (void);
2966
2967 private:
2968 static const int RENDER_SIZE = 256;
2969
2970 const int m_inputPatchSize;
2971 const int m_outputPatchSize;
2972
2973 const string m_referenceImagePath;
2974
2975 SharedPtr<const ShaderProgram> m_program;
2976 };
2977
init(void)2978 void PatchVertexCountCase::init (void)
2979 {
2980 checkTessellationSupport(m_context);
2981 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
2982
2983 const string inSizeStr = de::toString(m_inputPatchSize);
2984 const string outSizeStr = de::toString(m_outputPatchSize);
2985
2986 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
2987 "${GLSL_PER_VERTEX_OUT}\n"
2988 "\n"
2989 "in highp float in_v_attr;\n"
2990 "\n"
2991 "out highp float in_tc_attr;\n"
2992 "\n"
2993 "void main (void)\n"
2994 "{\n"
2995 " in_tc_attr = in_v_attr;\n"
2996 "}\n");
2997 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
2998 "${TESSELLATION_SHADER_REQUIRE}\n"
2999 "${GLSL_PER_VERTEX_IN_ARR}\n"
3000 "${GLSL_PER_VERTEX_OUT_ARR}\n"
3001 "\n"
3002 "layout (vertices = " + outSizeStr + ") out;\n"
3003 "\n"
3004 "in highp float in_tc_attr[];\n"
3005 "\n"
3006 "out highp float in_te_attr[];\n"
3007 "\n"
3008 "void main (void)\n"
3009 "{\n"
3010 " in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID*" + inSizeStr + "/" + outSizeStr + "];\n"
3011 "\n"
3012 " gl_TessLevelInner[0] = 5.0;\n"
3013 " gl_TessLevelInner[1] = 5.0;\n"
3014 "\n"
3015 " gl_TessLevelOuter[0] = 5.0;\n"
3016 " gl_TessLevelOuter[1] = 5.0;\n"
3017 " gl_TessLevelOuter[2] = 5.0;\n"
3018 " gl_TessLevelOuter[3] = 5.0;\n"
3019 "}\n");
3020 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
3021 "${TESSELLATION_SHADER_REQUIRE}\n"
3022 "${GLSL_PER_VERTEX_IN_ARR}\n"
3023 "${GLSL_PER_VERTEX_OUT}\n"
3024 "\n"
3025 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_QUADS) +
3026 "\n"
3027 "in highp float in_te_attr[];\n"
3028 "\n"
3029 "out mediump vec4 in_f_color;\n"
3030 "\n"
3031 "void main (void)\n"
3032 "{\n"
3033 " highp float x = gl_TessCoord.x*2.0 - 1.0;\n"
3034 " highp float y = gl_TessCoord.y - in_te_attr[int(round(gl_TessCoord.x*float(" + outSizeStr + "-1)))];\n"
3035 " gl_Position = vec4(x, y, 0.0, 1.0);\n"
3036 " in_f_color = vec4(1.0);\n"
3037 "}\n");
3038 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
3039 "\n"
3040 "layout (location = 0) out mediump vec4 o_color;\n"
3041 "\n"
3042 "in mediump vec4 in_f_color;\n"
3043 "\n"
3044 "void main (void)\n"
3045 "{\n"
3046 " o_color = in_f_color;\n"
3047 "}\n");
3048
3049 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
3050 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
3051 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
3052 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
3053 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))));
3054
3055 m_testCtx.getLog() << *m_program;
3056 if (!m_program->isOk())
3057 TCU_FAIL("Program compilation failed");
3058 }
3059
deinit(void)3060 void PatchVertexCountCase::deinit (void)
3061 {
3062 m_program.clear();
3063 }
3064
iterate(void)3065 PatchVertexCountCase::IterateResult PatchVertexCountCase::iterate (void)
3066 {
3067 TestLog& log = m_testCtx.getLog();
3068 const RenderContext& renderCtx = m_context.getRenderContext();
3069 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
3070 const deUint32 programGL = m_program->getProgram();
3071 const glw::Functions& gl = renderCtx.getFunctions();
3072
3073 setViewport(gl, viewport);
3074 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3075 gl.useProgram(programGL);
3076
3077 log << TestLog::Message << "Note: input patch size is " << m_inputPatchSize << ", output patch size is " << m_outputPatchSize << TestLog::EndMessage;
3078
3079 {
3080 vector<float> attributeData;
3081 attributeData.reserve(m_inputPatchSize);
3082
3083 for (int i = 0; i < m_inputPatchSize; i++)
3084 {
3085 const float f = (float)i / (float)(m_inputPatchSize-1);
3086 attributeData.push_back(f*f);
3087 }
3088
3089 gl.patchParameteri(GL_PATCH_VERTICES, m_inputPatchSize);
3090 gl.clear(GL_COLOR_BUFFER_BIT);
3091
3092 const glu::VertexArrayBinding attrBindings[] =
3093 {
3094 glu::va::Float("in_v_attr", 1, (int)attributeData.size(), 0, &attributeData[0])
3095 };
3096
3097 glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
3098 glu::pr::Patches(m_inputPatchSize));
3099 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
3100 }
3101
3102 {
3103 const tcu::Surface rendered = getPixels(renderCtx, viewport);
3104 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath);
3105 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
3106
3107 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image comparison failed");
3108 return STOP;
3109 }
3110 }
3111
3112 // Test per-patch inputs/outputs.
3113 class PerPatchDataCase : public TestCase
3114 {
3115 public:
3116 enum CaseType
3117 {
3118 CASETYPE_PRIMITIVE_ID_TCS = 0,
3119 CASETYPE_PRIMITIVE_ID_TES,
3120 CASETYPE_PATCH_VERTICES_IN_TCS,
3121 CASETYPE_PATCH_VERTICES_IN_TES,
3122 CASETYPE_TESS_LEVEL_INNER0_TES,
3123 CASETYPE_TESS_LEVEL_INNER1_TES,
3124 CASETYPE_TESS_LEVEL_OUTER0_TES,
3125 CASETYPE_TESS_LEVEL_OUTER1_TES,
3126 CASETYPE_TESS_LEVEL_OUTER2_TES,
3127 CASETYPE_TESS_LEVEL_OUTER3_TES,
3128
3129 CASETYPE_LAST
3130 };
3131
PerPatchDataCase(Context & context,const char * name,const char * description,CaseType caseType,const char * referenceImagePath)3132 PerPatchDataCase (Context& context, const char* name, const char* description, CaseType caseType, const char* referenceImagePath)
3133 : TestCase (context, name, description)
3134 , m_caseType (caseType)
3135 , m_referenceImagePath (caseTypeUsesRefImageFromFile(caseType) ? referenceImagePath : "")
3136 {
3137 DE_ASSERT(caseTypeUsesRefImageFromFile(caseType) == (referenceImagePath != DE_NULL));
3138 }
3139
3140 void init (void);
3141 void deinit (void);
3142 IterateResult iterate (void);
3143
3144 static const char* getCaseTypeName (CaseType);
3145 static const char* getCaseTypeDescription (CaseType);
3146 static bool caseTypeUsesRefImageFromFile (CaseType);
3147
3148 private:
3149 static const int RENDER_SIZE = 256;
3150 static const int INPUT_PATCH_SIZE;
3151 static const int OUTPUT_PATCH_SIZE;
3152
3153 const CaseType m_caseType;
3154 const string m_referenceImagePath;
3155
3156 SharedPtr<const ShaderProgram> m_program;
3157 };
3158
3159 const int PerPatchDataCase::INPUT_PATCH_SIZE = 10;
3160 const int PerPatchDataCase::OUTPUT_PATCH_SIZE = 5;
3161
getCaseTypeName(CaseType type)3162 const char* PerPatchDataCase::getCaseTypeName (CaseType type)
3163 {
3164 switch (type)
3165 {
3166 case CASETYPE_PRIMITIVE_ID_TCS: return "primitive_id_tcs";
3167 case CASETYPE_PRIMITIVE_ID_TES: return "primitive_id_tes";
3168 case CASETYPE_PATCH_VERTICES_IN_TCS: return "patch_vertices_in_tcs";
3169 case CASETYPE_PATCH_VERTICES_IN_TES: return "patch_vertices_in_tes";
3170 case CASETYPE_TESS_LEVEL_INNER0_TES: return "tess_level_inner_0_tes";
3171 case CASETYPE_TESS_LEVEL_INNER1_TES: return "tess_level_inner_1_tes";
3172 case CASETYPE_TESS_LEVEL_OUTER0_TES: return "tess_level_outer_0_tes";
3173 case CASETYPE_TESS_LEVEL_OUTER1_TES: return "tess_level_outer_1_tes";
3174 case CASETYPE_TESS_LEVEL_OUTER2_TES: return "tess_level_outer_2_tes";
3175 case CASETYPE_TESS_LEVEL_OUTER3_TES: return "tess_level_outer_3_tes";
3176 default:
3177 DE_ASSERT(false);
3178 return DE_NULL;
3179 }
3180 }
3181
getCaseTypeDescription(CaseType type)3182 const char* PerPatchDataCase::getCaseTypeDescription (CaseType type)
3183 {
3184 switch (type)
3185 {
3186 case CASETYPE_PRIMITIVE_ID_TCS: return "Read gl_PrimitiveID in TCS and pass it as patch output to TES";
3187 case CASETYPE_PRIMITIVE_ID_TES: return "Read gl_PrimitiveID in TES";
3188 case CASETYPE_PATCH_VERTICES_IN_TCS: return "Read gl_PatchVerticesIn in TCS and pass it as patch output to TES";
3189 case CASETYPE_PATCH_VERTICES_IN_TES: return "Read gl_PatchVerticesIn in TES";
3190 case CASETYPE_TESS_LEVEL_INNER0_TES: return "Read gl_TessLevelInner[0] in TES";
3191 case CASETYPE_TESS_LEVEL_INNER1_TES: return "Read gl_TessLevelInner[1] in TES";
3192 case CASETYPE_TESS_LEVEL_OUTER0_TES: return "Read gl_TessLevelOuter[0] in TES";
3193 case CASETYPE_TESS_LEVEL_OUTER1_TES: return "Read gl_TessLevelOuter[1] in TES";
3194 case CASETYPE_TESS_LEVEL_OUTER2_TES: return "Read gl_TessLevelOuter[2] in TES";
3195 case CASETYPE_TESS_LEVEL_OUTER3_TES: return "Read gl_TessLevelOuter[3] in TES";
3196 default:
3197 DE_ASSERT(false);
3198 return DE_NULL;
3199 }
3200 }
3201
caseTypeUsesRefImageFromFile(CaseType type)3202 bool PerPatchDataCase::caseTypeUsesRefImageFromFile (CaseType type)
3203 {
3204 switch (type)
3205 {
3206 case CASETYPE_PRIMITIVE_ID_TCS:
3207 case CASETYPE_PRIMITIVE_ID_TES:
3208 return true;
3209
3210 default:
3211 return false;
3212 }
3213 }
3214
init(void)3215 void PerPatchDataCase::init (void)
3216 {
3217 checkTessellationSupport(m_context);
3218 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
3219
3220 DE_ASSERT(OUTPUT_PATCH_SIZE < INPUT_PATCH_SIZE);
3221
3222 const string inSizeStr = de::toString(INPUT_PATCH_SIZE);
3223 const string outSizeStr = de::toString(OUTPUT_PATCH_SIZE);
3224
3225 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
3226 "${GLSL_PER_VERTEX_OUT}\n"
3227 "\n"
3228 "in highp float in_v_attr;\n"
3229 "\n"
3230 "out highp float in_tc_attr;\n"
3231 "\n"
3232 "void main (void)\n"
3233 "{\n"
3234 " in_tc_attr = in_v_attr;\n"
3235 "}\n");
3236 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
3237 "${TESSELLATION_SHADER_REQUIRE}\n"
3238 "${GLSL_PER_VERTEX_IN_ARR}\n"
3239 "${GLSL_PER_VERTEX_OUT_ARR}\n"
3240 "\n"
3241 "layout (vertices = " + outSizeStr + ") out;\n"
3242 "\n"
3243 "in highp float in_tc_attr[];\n"
3244 "\n"
3245 "out highp float in_te_attr[];\n"
3246 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS ? "patch out mediump int in_te_primitiveIDFromTCS;\n"
3247 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "patch out mediump int in_te_patchVerticesInFromTCS;\n"
3248 : "") +
3249 "\n"
3250 "void main (void)\n"
3251 "{\n"
3252 " in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID];\n"
3253 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS ? "\tin_te_primitiveIDFromTCS = gl_PrimitiveID;\n"
3254 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "\tin_te_patchVerticesInFromTCS = gl_PatchVerticesIn;\n"
3255 : "") +
3256 "\n"
3257 " gl_TessLevelInner[0] = 9.0;\n"
3258 " gl_TessLevelInner[1] = 8.0;\n"
3259 "\n"
3260 " gl_TessLevelOuter[0] = 7.0;\n"
3261 " gl_TessLevelOuter[1] = 6.0;\n"
3262 " gl_TessLevelOuter[2] = 5.0;\n"
3263 " gl_TessLevelOuter[3] = 4.0;\n"
3264 "}\n");
3265 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
3266 "${TESSELLATION_SHADER_REQUIRE}\n"
3267 "${GLSL_PER_VERTEX_IN_ARR}\n"
3268 "${GLSL_PER_VERTEX_OUT}\n"
3269 "\n"
3270 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_QUADS) +
3271 "\n"
3272 "in highp float in_te_attr[];\n"
3273 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS ? "patch in mediump int in_te_primitiveIDFromTCS;\n"
3274 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "patch in mediump int in_te_patchVerticesInFromTCS;\n"
3275 : string()) +
3276 "\n"
3277 "out mediump vec4 in_f_color;\n"
3278 "\n"
3279 "uniform highp float u_xScale;\n"
3280 "\n"
3281 "void main (void)\n"
3282 "{\n"
3283 " highp float x = (gl_TessCoord.x*u_xScale + in_te_attr[0]) * 2.0 - 1.0;\n"
3284 " highp float y = gl_TessCoord.y*2.0 - 1.0;\n"
3285 " gl_Position = vec4(x, y, 0.0, 1.0);\n"
3286 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS ? "\tbool ok = in_te_primitiveIDFromTCS == 3;\n"
3287 : m_caseType == CASETYPE_PRIMITIVE_ID_TES ? "\tbool ok = gl_PrimitiveID == 3;\n"
3288 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "\tbool ok = in_te_patchVerticesInFromTCS == " + inSizeStr + ";\n"
3289 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TES ? "\tbool ok = gl_PatchVerticesIn == " + outSizeStr + ";\n"
3290 : m_caseType == CASETYPE_TESS_LEVEL_INNER0_TES ? "\tbool ok = abs(gl_TessLevelInner[0] - 9.0) < 0.1f;\n"
3291 : m_caseType == CASETYPE_TESS_LEVEL_INNER1_TES ? "\tbool ok = abs(gl_TessLevelInner[1] - 8.0) < 0.1f;\n"
3292 : m_caseType == CASETYPE_TESS_LEVEL_OUTER0_TES ? "\tbool ok = abs(gl_TessLevelOuter[0] - 7.0) < 0.1f;\n"
3293 : m_caseType == CASETYPE_TESS_LEVEL_OUTER1_TES ? "\tbool ok = abs(gl_TessLevelOuter[1] - 6.0) < 0.1f;\n"
3294 : m_caseType == CASETYPE_TESS_LEVEL_OUTER2_TES ? "\tbool ok = abs(gl_TessLevelOuter[2] - 5.0) < 0.1f;\n"
3295 : m_caseType == CASETYPE_TESS_LEVEL_OUTER3_TES ? "\tbool ok = abs(gl_TessLevelOuter[3] - 4.0) < 0.1f;\n"
3296 : DE_NULL) +
3297 " in_f_color = ok ? vec4(1.0) : vec4(vec3(0.0), 1.0);\n"
3298 "}\n");
3299 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
3300 "\n"
3301 "layout (location = 0) out mediump vec4 o_color;\n"
3302 "\n"
3303 "in mediump vec4 in_f_color;\n"
3304 "\n"
3305 "void main (void)\n"
3306 "{\n"
3307 " o_color = in_f_color;\n"
3308 "}\n");
3309
3310 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
3311 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
3312 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
3313 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
3314 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))));
3315
3316 m_testCtx.getLog() << *m_program;
3317 if (!m_program->isOk())
3318 TCU_FAIL("Program compilation failed");
3319 }
3320
deinit(void)3321 void PerPatchDataCase::deinit (void)
3322 {
3323 m_program.clear();
3324 }
3325
iterate(void)3326 PerPatchDataCase::IterateResult PerPatchDataCase::iterate (void)
3327 {
3328 TestLog& log = m_testCtx.getLog();
3329 const RenderContext& renderCtx = m_context.getRenderContext();
3330 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
3331 const deUint32 programGL = m_program->getProgram();
3332 const glw::Functions& gl = renderCtx.getFunctions();
3333
3334 setViewport(gl, viewport);
3335 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3336 gl.useProgram(programGL);
3337
3338 log << TestLog::Message << "Note: input patch size is " << INPUT_PATCH_SIZE << ", output patch size is " << OUTPUT_PATCH_SIZE << TestLog::EndMessage;
3339
3340 {
3341 const int numPrimitives = m_caseType == CASETYPE_PRIMITIVE_ID_TCS || m_caseType == CASETYPE_PRIMITIVE_ID_TES ? 8 : 1;
3342
3343 vector<float> attributeData;
3344 attributeData.reserve(numPrimitives*INPUT_PATCH_SIZE);
3345
3346 for (int i = 0; i < numPrimitives; i++)
3347 {
3348 attributeData.push_back((float)i / (float)numPrimitives);
3349 for (int j = 0; j < INPUT_PATCH_SIZE-1; j++)
3350 attributeData.push_back(0.0f);
3351 }
3352
3353 gl.patchParameteri(GL_PATCH_VERTICES, INPUT_PATCH_SIZE);
3354 gl.clear(GL_COLOR_BUFFER_BIT);
3355
3356 gl.uniform1f(gl.getUniformLocation(programGL, "u_xScale"), 1.0f / (float)numPrimitives);
3357
3358 const glu::VertexArrayBinding attrBindings[] =
3359 {
3360 glu::va::Float("in_v_attr", 1, (int)attributeData.size(), 0, &attributeData[0])
3361 };
3362
3363 glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
3364 glu::pr::Patches(numPrimitives*INPUT_PATCH_SIZE));
3365 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
3366 }
3367
3368 {
3369 const tcu::Surface rendered = getPixels(renderCtx, viewport);
3370
3371 if (m_caseType == CASETYPE_PRIMITIVE_ID_TCS || m_caseType == CASETYPE_PRIMITIVE_ID_TES)
3372 {
3373 DE_ASSERT(caseTypeUsesRefImageFromFile(m_caseType));
3374
3375 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath);
3376 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
3377
3378 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image comparison failed");
3379 return STOP;
3380 }
3381 else
3382 {
3383 DE_ASSERT(!caseTypeUsesRefImageFromFile(m_caseType));
3384
3385 log << TestLog::Image("RenderedImage", "Rendered Image", rendered);
3386
3387 for (int y = 0; y < rendered.getHeight(); y++)
3388 for (int x = 0; x < rendered.getWidth(); x++)
3389 {
3390 if (rendered.getPixel(x, y) != tcu::RGBA::white())
3391 {
3392 log << TestLog::Message << "Failure: expected all white pixels" << TestLog::EndMessage;
3393 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
3394 return STOP;
3395 }
3396 }
3397
3398 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3399 return STOP;
3400 }
3401 }
3402 }
3403
3404 // Basic barrier() usage in TCS.
3405 class BarrierCase : public TestCase
3406 {
3407 public:
BarrierCase(Context & context,const char * name,const char * description,const char * referenceImagePath)3408 BarrierCase (Context& context, const char* name, const char* description, const char* referenceImagePath)
3409 : TestCase (context, name, description)
3410 , m_referenceImagePath (referenceImagePath)
3411 {
3412 }
3413
3414 void init (void);
3415 void deinit (void);
3416 IterateResult iterate (void);
3417
3418 private:
3419 static const int RENDER_SIZE = 256;
3420 static const int NUM_VERTICES;
3421
3422 const string m_referenceImagePath;
3423
3424 SharedPtr<const ShaderProgram> m_program;
3425 };
3426
3427 const int BarrierCase::NUM_VERTICES = 32;
3428
init(void)3429 void BarrierCase::init (void)
3430 {
3431 checkTessellationSupport(m_context);
3432 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
3433
3434 const string numVertsStr = de::toString(NUM_VERTICES);
3435
3436 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
3437 "${GLSL_PER_VERTEX_OUT}\n"
3438 "\n"
3439 "in highp float in_v_attr;\n"
3440 "\n"
3441 "out highp float in_tc_attr;\n"
3442 "\n"
3443 "void main (void)\n"
3444 "{\n"
3445 " in_tc_attr = in_v_attr;\n"
3446 "}\n");
3447 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
3448 "${TESSELLATION_SHADER_REQUIRE}\n"
3449 "${GLSL_PER_VERTEX_IN_ARR}\n"
3450 "${GLSL_PER_VERTEX_OUT_ARR}\n"
3451 "\n"
3452 "layout (vertices = " + numVertsStr + ") out;\n"
3453 "\n"
3454 "in highp float in_tc_attr[];\n"
3455 "\n"
3456 "out highp float in_te_attr[];\n"
3457 "patch out highp float in_te_patchAttr;\n"
3458 "\n"
3459 "void main (void)\n"
3460 "{\n"
3461 " in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID];\n"
3462 " in_te_patchAttr = 0.0f;\n"
3463 " barrier();\n"
3464 " if (gl_InvocationID == 5)\n"
3465 " in_te_patchAttr = float(gl_InvocationID)*0.1;\n"
3466 " barrier();\n"
3467 " highp float temp = in_te_patchAttr + in_te_attr[gl_InvocationID];\n"
3468 " barrier();\n"
3469 " if (gl_InvocationID == " + numVertsStr + "-1)\n"
3470 " in_te_patchAttr = float(gl_InvocationID);\n"
3471 " barrier();\n"
3472 " in_te_attr[gl_InvocationID] = temp;\n"
3473 " barrier();\n"
3474 " temp = temp + in_te_attr[(gl_InvocationID+1) % " + numVertsStr + "];\n"
3475 " barrier();\n"
3476 " in_te_attr[gl_InvocationID] = 0.25*temp;\n"
3477 "\n"
3478 " gl_TessLevelInner[0] = 32.0;\n"
3479 " gl_TessLevelInner[1] = 32.0;\n"
3480 "\n"
3481 " gl_TessLevelOuter[0] = 32.0;\n"
3482 " gl_TessLevelOuter[1] = 32.0;\n"
3483 " gl_TessLevelOuter[2] = 32.0;\n"
3484 " gl_TessLevelOuter[3] = 32.0;\n"
3485 "}\n");
3486 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
3487 "${TESSELLATION_SHADER_REQUIRE}\n"
3488 "${GLSL_PER_VERTEX_IN_ARR}\n"
3489 "${GLSL_PER_VERTEX_OUT}\n"
3490 "\n"
3491 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_QUADS) +
3492 "\n"
3493 "in highp float in_te_attr[];\n"
3494 "patch in highp float in_te_patchAttr;\n"
3495 "\n"
3496 "out highp float in_f_blue;\n"
3497 "\n"
3498 "void main (void)\n"
3499 "{\n"
3500 " highp float x = gl_TessCoord.x*2.0 - 1.0;\n"
3501 " highp float y = gl_TessCoord.y - in_te_attr[int(round(gl_TessCoord.x*float(" + numVertsStr + "-1)))];\n"
3502 " gl_Position = vec4(x, y, 0.0, 1.0);\n"
3503 " in_f_blue = abs(in_te_patchAttr - float(" + numVertsStr + "-1));\n"
3504 "}\n");
3505 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
3506 "\n"
3507 "layout (location = 0) out mediump vec4 o_color;\n"
3508 "\n"
3509 "in highp float in_f_blue;\n"
3510 "\n"
3511 "void main (void)\n"
3512 "{\n"
3513 " o_color = vec4(1.0, 0.0, in_f_blue, 1.0);\n"
3514 "}\n");
3515
3516 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
3517 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
3518 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
3519 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
3520 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))));
3521
3522 m_testCtx.getLog() << *m_program;
3523 if (!m_program->isOk())
3524 TCU_FAIL("Program compilation failed");
3525 }
3526
deinit(void)3527 void BarrierCase::deinit (void)
3528 {
3529 m_program.clear();
3530 }
3531
iterate(void)3532 BarrierCase::IterateResult BarrierCase::iterate (void)
3533 {
3534 TestLog& log = m_testCtx.getLog();
3535 const RenderContext& renderCtx = m_context.getRenderContext();
3536 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
3537 const deUint32 programGL = m_program->getProgram();
3538 const glw::Functions& gl = renderCtx.getFunctions();
3539
3540 setViewport(gl, viewport);
3541 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3542 gl.useProgram(programGL);
3543
3544 {
3545 vector<float> attributeData(NUM_VERTICES);
3546
3547 for (int i = 0; i < NUM_VERTICES; i++)
3548 attributeData[i] = (float)i / (float)(NUM_VERTICES-1);
3549
3550 gl.patchParameteri(GL_PATCH_VERTICES, NUM_VERTICES);
3551 gl.clear(GL_COLOR_BUFFER_BIT);
3552
3553 const glu::VertexArrayBinding attrBindings[] =
3554 {
3555 glu::va::Float("in_v_attr", 1, (int)attributeData.size(), 0, &attributeData[0])
3556 };
3557
3558 glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
3559 glu::pr::Patches(NUM_VERTICES));
3560 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
3561 }
3562
3563 {
3564 const tcu::Surface rendered = getPixels(renderCtx, viewport);
3565 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath);
3566 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
3567
3568 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image comparison failed");
3569 return STOP;
3570 }
3571 }
3572
3573 /*--------------------------------------------------------------------*//*!
3574 * \brief Base class for testing invariance of entire primitive set
3575 *
3576 * Draws two patches with identical tessellation levels and compares the
3577 * results. Repeats the same with other programs that are only different
3578 * in irrelevant ways; compares the results between these two programs.
3579 * Also potentially compares to results produced by different tessellation
3580 * levels (see e.g. invariance rule #6).
3581 * Furthermore, repeats the above with multiple different tessellation
3582 * value sets.
3583 *
3584 * The manner of primitive set comparison is defined by subclass. E.g.
3585 * case for invariance rule #1 tests that same vertices come out, in same
3586 * order; rule #5 only requires that the same triangles are output, but
3587 * not necessarily in the same order.
3588 *//*--------------------------------------------------------------------*/
3589 class PrimitiveSetInvarianceCase : public TestCase
3590 {
3591 public:
3592 enum WindingUsage
3593 {
3594 WINDINGUSAGE_CCW = 0,
3595 WINDINGUSAGE_CW,
3596 WINDINGUSAGE_VARY,
3597
3598 WINDINGUSAGE_LAST
3599 };
3600
PrimitiveSetInvarianceCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing,bool usePointMode,WindingUsage windingUsage)3601 PrimitiveSetInvarianceCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, bool usePointMode, WindingUsage windingUsage)
3602 : TestCase (context, name, description)
3603 , m_primitiveType (primType)
3604 , m_spacing (spacing)
3605 , m_usePointMode (usePointMode)
3606 , m_windingUsage (windingUsage)
3607 {
3608 }
3609
3610 void init (void);
3611 void deinit (void);
3612 IterateResult iterate (void);
3613
3614 protected:
3615 struct TessLevels
3616 {
3617 float inner[2];
3618 float outer[4];
descriptiondeqp::gles31::Functional::__anon755cd4500211::PrimitiveSetInvarianceCase::TessLevels3619 string description (void) const { return tessellationLevelsString(&inner[0], &outer[0]); }
3620 };
3621 struct LevelCase
3622 {
3623 vector<TessLevels> levels;
3624 int mem; //!< Subclass-defined arbitrary piece of data, for type of the levelcase, if needed. Passed to compare().
LevelCasedeqp::gles31::Functional::__anon755cd4500211::PrimitiveSetInvarianceCase::LevelCase3625 LevelCase (const TessLevels& lev) : levels(vector<TessLevels>(1, lev)), mem(0) {}
LevelCasedeqp::gles31::Functional::__anon755cd4500211::PrimitiveSetInvarianceCase::LevelCase3626 LevelCase (void) : mem(0) {}
3627 };
3628
3629 virtual vector<LevelCase> genTessLevelCases (void) const;
3630 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int levelCaseMem) const = 0;
3631
3632 const TessPrimitiveType m_primitiveType;
3633
3634 private:
3635 struct Program
3636 {
3637 Winding winding;
3638 SharedPtr<const ShaderProgram> program;
3639
Programdeqp::gles31::Functional::__anon755cd4500211::PrimitiveSetInvarianceCase::Program3640 Program (Winding w, const SharedPtr<const ShaderProgram>& prog) : winding(w), program(prog) {}
3641
descriptiondeqp::gles31::Functional::__anon755cd4500211::PrimitiveSetInvarianceCase::Program3642 string description (void) const { return string() + "winding mode " + getWindingShaderName(winding); }
3643 };
3644
3645 static const int RENDER_SIZE = 16;
3646
3647 const SpacingMode m_spacing;
3648 const bool m_usePointMode;
3649 const WindingUsage m_windingUsage;
3650
3651 vector<Program> m_programs;
3652 };
3653
genTessLevelCases(void) const3654 vector<PrimitiveSetInvarianceCase::LevelCase> PrimitiveSetInvarianceCase::genTessLevelCases (void) const
3655 {
3656 static const TessLevels basicTessLevelCases[] =
3657 {
3658 { { 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
3659 { { 63.0f, 24.0f }, { 15.0f, 42.0f, 10.0f, 12.0f } },
3660 { { 3.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } },
3661 { { 4.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
3662 { { 2.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } },
3663 { { 5.0f, 6.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
3664 { { 1.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
3665 { { 5.0f, 1.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
3666 { { 5.2f, 1.6f }, { 2.9f, 3.4f, 1.5f, 4.1f } }
3667 };
3668
3669 vector<LevelCase> result;
3670 for (int i = 0; i < DE_LENGTH_OF_ARRAY(basicTessLevelCases); i++)
3671 result.push_back(LevelCase(basicTessLevelCases[i]));
3672
3673 {
3674 de::Random rnd(123);
3675 for (int i = 0; i < 10; i++)
3676 {
3677 TessLevels levels;
3678 for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.inner); j++)
3679 levels.inner[j] = rnd.getFloat(1.0f, 16.0f);
3680 for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.outer); j++)
3681 levels.outer[j] = rnd.getFloat(1.0f, 16.0f);
3682 result.push_back(LevelCase(levels));
3683 }
3684 }
3685
3686 return result;
3687 }
3688
init(void)3689 void PrimitiveSetInvarianceCase::init (void)
3690 {
3691 const int numDifferentConstantExprCases = 2;
3692 vector<Winding> windings;
3693 switch (m_windingUsage)
3694 {
3695 case WINDINGUSAGE_CCW: windings.push_back(WINDING_CCW); break;
3696 case WINDINGUSAGE_CW: windings.push_back(WINDING_CW); break;
3697 case WINDINGUSAGE_VARY: windings.push_back(WINDING_CCW);
3698 windings.push_back(WINDING_CW); break;
3699 default: DE_ASSERT(false);
3700 }
3701
3702 checkTessellationSupport(m_context);
3703 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
3704
3705 for (int constantExprCaseNdx = 0; constantExprCaseNdx < numDifferentConstantExprCases; constantExprCaseNdx++)
3706 {
3707 for (int windingCaseNdx = 0; windingCaseNdx < (int)windings.size(); windingCaseNdx++)
3708 {
3709 const string floatLit01 = de::floatToString(10.0f / (float)(constantExprCaseNdx + 10), 2);
3710 const int programNdx = (int)m_programs.size();
3711
3712 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
3713 "${GLSL_PER_VERTEX_OUT}\n"
3714 "\n"
3715 "in highp float in_v_attr;\n"
3716 "out highp float in_tc_attr;\n"
3717 "\n"
3718 "void main (void)\n"
3719 "{\n"
3720 " in_tc_attr = in_v_attr;\n"
3721 "}\n");
3722 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
3723 "${TESSELLATION_SHADER_REQUIRE}\n"
3724 "${GLSL_PER_VERTEX_IN_ARR}\n"
3725 "${GLSL_PER_VERTEX_OUT_ARR}\n"
3726 "\n"
3727 "layout (vertices = " + de::toString(constantExprCaseNdx+1) + ") out;\n"
3728 "\n"
3729 "in highp float in_tc_attr[];\n"
3730 "\n"
3731 "patch out highp float in_te_positionOffset;\n"
3732 "\n"
3733 "void main (void)\n"
3734 "{\n"
3735 " in_te_positionOffset = in_tc_attr[6];\n"
3736 "\n"
3737 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
3738 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
3739 "\n"
3740 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
3741 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
3742 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
3743 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
3744 "}\n");
3745 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
3746 "${TESSELLATION_SHADER_REQUIRE}\n"
3747 "${GLSL_PER_VERTEX_IN_ARR}\n"
3748 "${GLSL_PER_VERTEX_OUT}\n"
3749 "\n"
3750 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, windings[windingCaseNdx], m_usePointMode) +
3751 "\n"
3752 "patch in highp float in_te_positionOffset;\n"
3753 "\n"
3754 "out highp vec4 in_f_color;\n"
3755 "invariant out highp vec3 out_te_tessCoord;\n"
3756 "\n"
3757 "void main (void)\n"
3758 "{\n"
3759 " gl_Position = vec4(gl_TessCoord.xy*" + floatLit01 + " - in_te_positionOffset + float(gl_PrimitiveID)*0.1, 0.0, 1.0);\n"
3760 " in_f_color = vec4(" + floatLit01 + ");\n"
3761 " out_te_tessCoord = gl_TessCoord;\n"
3762 "}\n");
3763 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
3764 "\n"
3765 "layout (location = 0) out mediump vec4 o_color;\n"
3766 "\n"
3767 "in highp vec4 in_f_color;\n"
3768 "\n"
3769 "void main (void)\n"
3770 "{\n"
3771 " o_color = in_f_color;\n"
3772 "}\n");
3773
3774 m_programs.push_back(Program(windings[windingCaseNdx],
3775 SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
3776 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
3777 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
3778 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
3779 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
3780 << glu::TransformFeedbackVarying ("out_te_tessCoord")
3781 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)))));
3782
3783 {
3784 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Program" + de::toString(programNdx), "Program " + de::toString(programNdx));
3785
3786 if (programNdx == 0 || !m_programs.back().program->isOk())
3787 m_testCtx.getLog() << *m_programs.back().program;
3788
3789 if (!m_programs.back().program->isOk())
3790 TCU_FAIL("Program compilation failed");
3791
3792 if (programNdx > 0)
3793 m_testCtx.getLog() << TestLog::Message << "Note: program " << programNdx << " is similar to above, except some constants are different, and: " << m_programs.back().description() << TestLog::EndMessage;
3794 }
3795 }
3796 }
3797 }
3798
deinit(void)3799 void PrimitiveSetInvarianceCase::deinit (void)
3800 {
3801 m_programs.clear();
3802 }
3803
iterate(void)3804 PrimitiveSetInvarianceCase::IterateResult PrimitiveSetInvarianceCase::iterate (void)
3805 {
3806 typedef TransformFeedbackHandler<Vec3> TFHandler;
3807
3808 TestLog& log = m_testCtx.getLog();
3809 const RenderContext& renderCtx = m_context.getRenderContext();
3810 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
3811 const glw::Functions& gl = renderCtx.getFunctions();
3812 const vector<LevelCase> tessLevelCases = genTessLevelCases();
3813 vector<vector<int> > primitiveCounts;
3814 int maxNumPrimitives = -1;
3815
3816 for (int caseNdx = 0; caseNdx < (int)tessLevelCases.size(); caseNdx++)
3817 {
3818 primitiveCounts.push_back(vector<int>());
3819 for (int i = 0; i < (int)tessLevelCases[caseNdx].levels.size(); i++)
3820 {
3821 const int primCount = referencePrimitiveCount(m_primitiveType, m_spacing, m_usePointMode,
3822 &tessLevelCases[caseNdx].levels[i].inner[0], &tessLevelCases[caseNdx].levels[i].outer[0]);
3823 primitiveCounts.back().push_back(primCount);
3824 maxNumPrimitives = de::max(maxNumPrimitives, primCount);
3825 }
3826 }
3827
3828 const deUint32 primitiveTypeGL = outputPrimitiveTypeGL(m_primitiveType, m_usePointMode);
3829 const TFHandler transformFeedback (m_context.getRenderContext(), 2*maxNumPrimitives*numVerticesPerPrimitive(primitiveTypeGL));
3830
3831 setViewport(gl, viewport);
3832 gl.patchParameteri(GL_PATCH_VERTICES, 7);
3833
3834 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < (int)tessLevelCases.size(); tessLevelCaseNdx++)
3835 {
3836 const LevelCase& levelCase = tessLevelCases[tessLevelCaseNdx];
3837 vector<Vec3> firstPrimVertices;
3838
3839 {
3840 string tessLevelsStr;
3841 for (int i = 0; i < (int)levelCase.levels.size(); i++)
3842 tessLevelsStr += (levelCase.levels.size() > 1 ? "\n" : "") + levelCase.levels[i].description();
3843 log << TestLog::Message << "Tessellation level sets: " << tessLevelsStr << TestLog::EndMessage;
3844 }
3845
3846 for (int subTessLevelCaseNdx = 0; subTessLevelCaseNdx < (int)levelCase.levels.size(); subTessLevelCaseNdx++)
3847 {
3848 const TessLevels& tessLevels = levelCase.levels[subTessLevelCaseNdx];
3849 const float (&inner)[2] = tessLevels.inner;
3850 const float (&outer)[4] = tessLevels.outer;
3851 const float attribute[2*7] = { inner[0], inner[1], outer[0], outer[1], outer[2], outer[3], 0.0f,
3852 inner[0], inner[1], outer[0], outer[1], outer[2], outer[3], 0.5f };
3853 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, DE_LENGTH_OF_ARRAY(attribute), 0, &attribute[0]) };
3854
3855 for (int programNdx = 0; programNdx < (int)m_programs.size(); programNdx++)
3856 {
3857 const deUint32 programGL = m_programs[programNdx].program->getProgram();
3858 gl.useProgram(programGL);
3859 const TFHandler::Result tfResult = transformFeedback.renderAndGetPrimitives(programGL, primitiveTypeGL, DE_LENGTH_OF_ARRAY(bindings), &bindings[0], DE_LENGTH_OF_ARRAY(attribute));
3860
3861 if (tfResult.numPrimitives != 2*primitiveCounts[tessLevelCaseNdx][subTessLevelCaseNdx])
3862 {
3863 log << TestLog::Message << "Failure: GL reported GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN to be "
3864 << tfResult.numPrimitives << ", reference value is " << 2*primitiveCounts[tessLevelCaseNdx][subTessLevelCaseNdx]
3865 << TestLog::EndMessage;
3866
3867 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of primitives");
3868 return STOP;
3869 }
3870
3871 {
3872 const int half = (int)tfResult.varying.size()/2;
3873 const vector<Vec3> prim0Vertices = vector<Vec3>(tfResult.varying.begin(), tfResult.varying.begin() + half);
3874 const Vec3* const prim1Vertices = &tfResult.varying[half];
3875
3876 for (int vtxNdx = 0; vtxNdx < (int)prim0Vertices.size(); vtxNdx++)
3877 {
3878 if (prim0Vertices[vtxNdx] != prim1Vertices[vtxNdx])
3879 {
3880 log << TestLog::Message << "Failure: tessellation coordinate at index " << vtxNdx << " differs between two primitives drawn in one draw call" << TestLog::EndMessage
3881 << TestLog::Message << "Note: the coordinate is " << prim0Vertices[vtxNdx] << " for the first primitive and " << prim1Vertices[vtxNdx] << " for the second" << TestLog::EndMessage
3882 << TestLog::Message << "Note: tessellation levels for both primitives were: " << tessellationLevelsString(&inner[0], &outer[0]) << TestLog::EndMessage;
3883 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of primitives");
3884 return STOP;
3885 }
3886 }
3887
3888 if (programNdx == 0 && subTessLevelCaseNdx == 0)
3889 firstPrimVertices = prim0Vertices;
3890 else
3891 {
3892 const bool compareOk = compare(firstPrimVertices, prim0Vertices, levelCase.mem);
3893 if (!compareOk)
3894 {
3895 log << TestLog::Message << "Note: comparison of tessellation coordinates failed; comparison was made between following cases:\n"
3896 << " - case A: program 0, tessellation levels: " << tessLevelCases[tessLevelCaseNdx].levels[0].description() << "\n"
3897 << " - case B: program " << programNdx << ", tessellation levels: " << tessLevels.description() << TestLog::EndMessage;
3898 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of primitives");
3899 return STOP;
3900 }
3901 }
3902 }
3903 }
3904 }
3905 }
3906
3907 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3908 return STOP;
3909 }
3910
3911 /*--------------------------------------------------------------------*//*!
3912 * \brief Test invariance rule #1
3913 *
3914 * Test that the sequence of primitives input to the TES only depends on
3915 * the tessellation levels, tessellation mode, spacing mode, winding, and
3916 * point mode.
3917 *//*--------------------------------------------------------------------*/
3918 class InvariantPrimitiveSetCase : public PrimitiveSetInvarianceCase
3919 {
3920 public:
InvariantPrimitiveSetCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing,Winding winding,bool usePointMode)3921 InvariantPrimitiveSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
3922 : PrimitiveSetInvarianceCase(context, name, description, primType, spacing, usePointMode, winding == WINDING_CCW ? WINDINGUSAGE_CCW
3923 : winding == WINDING_CW ? WINDINGUSAGE_CW
3924 : WINDINGUSAGE_LAST)
3925 {
3926 }
3927
3928 protected:
compare(const vector<Vec3> & coordsA,const vector<Vec3> & coordsB,int) const3929 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int) const
3930 {
3931 for (int vtxNdx = 0; vtxNdx < (int)coordsA.size(); vtxNdx++)
3932 {
3933 if (coordsA[vtxNdx] != coordsB[vtxNdx])
3934 {
3935 m_testCtx.getLog() << TestLog::Message << "Failure: tessellation coordinate at index " << vtxNdx << " differs between two programs" << TestLog::EndMessage
3936 << TestLog::Message << "Note: the coordinate is " << coordsA[vtxNdx] << " for the first program and " << coordsB[vtxNdx] << " for the other" << TestLog::EndMessage;
3937 return false;
3938 }
3939 }
3940 return true;
3941 }
3942 };
3943
3944 /*--------------------------------------------------------------------*//*!
3945 * \brief Test invariance rule #2
3946 *
3947 * Test that the set of vertices along an outer edge of a quad or triangle
3948 * only depends on that edge's tessellation level, and spacing.
3949 *
3950 * For each (outer) edge in the quad or triangle, draw multiple patches
3951 * with identical tessellation levels for that outer edge but with
3952 * different values for the other outer edges; compare, among the
3953 * primitives, the vertices generated for that outer edge. Repeat with
3954 * different programs, using different winding etc. settings. Compare
3955 * the edge's vertices between different programs.
3956 *//*--------------------------------------------------------------------*/
3957 class InvariantOuterEdgeCase : public TestCase
3958 {
3959 public:
InvariantOuterEdgeCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing)3960 InvariantOuterEdgeCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing)
3961 : TestCase (context, name, description)
3962 , m_primitiveType (primType)
3963 , m_spacing (spacing)
3964 {
3965 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
3966 }
3967
3968 void init (void);
3969 void deinit (void);
3970 IterateResult iterate (void);
3971
3972 private:
3973 struct Program
3974 {
3975 Winding winding;
3976 bool usePointMode;
3977 SharedPtr<const ShaderProgram> program;
3978
Programdeqp::gles31::Functional::__anon755cd4500211::InvariantOuterEdgeCase::Program3979 Program (Winding w, bool point, const SharedPtr<const ShaderProgram>& prog) : winding(w), usePointMode(point), program(prog) {}
3980
descriptiondeqp::gles31::Functional::__anon755cd4500211::InvariantOuterEdgeCase::Program3981 string description (void) const { return string() + "winding mode " + getWindingShaderName(winding) + ", " + (usePointMode ? "" : "don't ") + "use point mode"; }
3982 };
3983
3984 static vector<float> generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel);
3985
3986 static const int RENDER_SIZE = 16;
3987
3988 const TessPrimitiveType m_primitiveType;
3989 const SpacingMode m_spacing;
3990
3991 vector<Program> m_programs;
3992 };
3993
generatePatchTessLevels(int numPatches,int constantOuterLevelIndex,float constantOuterLevel)3994 vector<float> InvariantOuterEdgeCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel)
3995 {
3996 de::Random rnd(123);
3997 return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd);
3998 }
3999
init(void)4000 void InvariantOuterEdgeCase::init (void)
4001 {
4002 checkTessellationSupport(m_context);
4003 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
4004
4005 for (int windingI = 0; windingI < WINDING_LAST; windingI++)
4006 {
4007 const Winding winding = (Winding)windingI;
4008
4009 for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++)
4010 {
4011 const bool usePointMode = usePointModeI != 0;
4012 const int programNdx = (int)m_programs.size();
4013 const string floatLit01 = de::floatToString(10.0f / (float)(programNdx + 10), 2);
4014
4015 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
4016 "${GLSL_PER_VERTEX_OUT}\n"
4017 "\n"
4018 "in highp float in_v_attr;\n"
4019 "out highp float in_tc_attr;\n"
4020 "\n"
4021 "void main (void)\n"
4022 "{\n"
4023 " in_tc_attr = in_v_attr;\n"
4024 "}\n");
4025 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
4026 "${TESSELLATION_SHADER_REQUIRE}\n"
4027 "${GLSL_PER_VERTEX_IN_ARR}\n"
4028 "${GLSL_PER_VERTEX_OUT_ARR}\n"
4029 "\n"
4030 "layout (vertices = " + de::toString(programNdx+1) + ") out;\n"
4031 "\n"
4032 "in highp float in_tc_attr[];\n"
4033 "\n"
4034 "void main (void)\n"
4035 "{\n"
4036 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
4037 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
4038 "\n"
4039 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
4040 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
4041 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
4042 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
4043 "}\n");
4044 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
4045 "${TESSELLATION_SHADER_REQUIRE}\n"
4046 "${GLSL_PER_VERTEX_IN_ARR}\n"
4047 "${GLSL_PER_VERTEX_OUT}\n"
4048 "\n"
4049 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, winding, usePointMode) +
4050 "\n"
4051 "out highp vec4 in_f_color;\n"
4052 "invariant out highp vec3 out_te_tessCoord;\n"
4053 "\n"
4054 "void main (void)\n"
4055 "{\n"
4056 " gl_Position = vec4(gl_TessCoord.xy*" + floatLit01 + " - float(gl_PrimitiveID)*0.05, 0.0, 1.0);\n"
4057 " in_f_color = vec4(" + floatLit01 + ");\n"
4058 " out_te_tessCoord = gl_TessCoord;\n"
4059 "}\n");
4060 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
4061 "\n"
4062 "layout (location = 0) out mediump vec4 o_color;\n"
4063 "\n"
4064 "in highp vec4 in_f_color;\n"
4065 "\n"
4066 "void main (void)\n"
4067 "{\n"
4068 " o_color = in_f_color;\n"
4069 "}\n");
4070
4071 m_programs.push_back(Program(winding, usePointMode,
4072 SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
4073 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
4074 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
4075 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
4076 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
4077 << glu::TransformFeedbackVarying ("out_te_tessCoord")
4078 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)))));
4079
4080 {
4081 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Program" + de::toString(programNdx), "Program " + de::toString(programNdx));
4082
4083 if (programNdx == 0 || !m_programs.back().program->isOk())
4084 m_testCtx.getLog() << *m_programs.back().program;
4085
4086 if (!m_programs.back().program->isOk())
4087 TCU_FAIL("Program compilation failed");
4088
4089 if (programNdx > 0)
4090 m_testCtx.getLog() << TestLog::Message << "Note: program " << programNdx << " is similar to above, except some constants are different, and: " << m_programs.back().description() << TestLog::EndMessage;
4091 }
4092 }
4093 }
4094 }
4095
deinit(void)4096 void InvariantOuterEdgeCase::deinit (void)
4097 {
4098 m_programs.clear();
4099 }
4100
iterate(void)4101 InvariantOuterEdgeCase::IterateResult InvariantOuterEdgeCase::iterate (void)
4102 {
4103 typedef TransformFeedbackHandler<Vec3> TFHandler;
4104
4105 TestLog& log = m_testCtx.getLog();
4106 const RenderContext& renderCtx = m_context.getRenderContext();
4107 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
4108 const glw::Functions& gl = renderCtx.getFunctions();
4109
4110 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 };
4111 const int numPatchesPerDrawCall = 10;
4112 const vector<OuterEdgeDescription> edgeDescriptions = outerEdgeDescriptions(m_primitiveType);
4113
4114 {
4115 // Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once.
4116 int maxNumVerticesInDrawCall = 0;
4117 {
4118 const vector<float> patchTessLevels = generatePatchTessLevels(numPatchesPerDrawCall, 0 /* outer-edge index doesn't affect vertex count */, arrayMax(singleOuterEdgeLevels));
4119
4120 for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++)
4121 maxNumVerticesInDrawCall = de::max(maxNumVerticesInDrawCall,
4122 multiplePatchReferenceVertexCount(m_primitiveType, m_spacing, usePointModeI != 0, &patchTessLevels[0], numPatchesPerDrawCall));
4123 }
4124
4125 {
4126 const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall);
4127
4128 setViewport(gl, viewport);
4129 gl.patchParameteri(GL_PATCH_VERTICES, 6);
4130
4131 for (int outerEdgeIndex = 0; outerEdgeIndex < (int)edgeDescriptions.size(); outerEdgeIndex++)
4132 {
4133 const OuterEdgeDescription& edgeDesc = edgeDescriptions[outerEdgeIndex];
4134
4135 for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(singleOuterEdgeLevels); outerEdgeLevelCaseNdx++)
4136 {
4137 typedef std::set<Vec3, VecLexLessThan<3> > Vec3Set;
4138
4139 const vector<float> patchTessLevels = generatePatchTessLevels(numPatchesPerDrawCall, outerEdgeIndex, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
4140 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)patchTessLevels.size(), 0, &patchTessLevels[0]) };
4141 Vec3Set firstOuterEdgeVertices; // Vertices of the outer edge of the first patch of the first program's draw call; used for comparison with other patches.
4142
4143 log << TestLog::Message << "Testing with outer tessellation level " << singleOuterEdgeLevels[outerEdgeLevelCaseNdx]
4144 << " for the " << edgeDesc.description() << " edge, and with various levels for other edges, and with all programs" << TestLog::EndMessage;
4145
4146 for (int programNdx = 0; programNdx < (int)m_programs.size(); programNdx++)
4147 {
4148 const Program& program = m_programs[programNdx];
4149 const deUint32 programGL = program.program->getProgram();
4150
4151 gl.useProgram(programGL);
4152
4153 {
4154 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, program.usePointMode),
4155 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)patchTessLevels.size());
4156 const int refNumVertices = multiplePatchReferenceVertexCount(m_primitiveType, m_spacing, program.usePointMode, &patchTessLevels[0], numPatchesPerDrawCall);
4157 int numVerticesRead = 0;
4158
4159 if ((int)tfResult.varying.size() != refNumVertices)
4160 {
4161 log << TestLog::Message << "Failure: the number of vertices returned by transform feedback is "
4162 << tfResult.varying.size() << ", expected " << refNumVertices << TestLog::EndMessage
4163 << TestLog::Message << "Note: rendered " << numPatchesPerDrawCall
4164 << " patches in one draw call; tessellation levels for each patch are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n"
4165 << containerStr(patchTessLevels, 6) << TestLog::EndMessage;
4166
4167 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4168 return STOP;
4169 }
4170
4171 // Check the vertices of each patch.
4172
4173 for (int patchNdx = 0; patchNdx < numPatchesPerDrawCall; patchNdx++)
4174 {
4175 const float* const innerLevels = &patchTessLevels[6*patchNdx + 0];
4176 const float* const outerLevels = &patchTessLevels[6*patchNdx + 2];
4177 const int patchNumVertices = referenceVertexCount(m_primitiveType, m_spacing, program.usePointMode, innerLevels, outerLevels);
4178 Vec3Set outerEdgeVertices;
4179
4180 // We're interested in just the vertices on the current outer edge.
4181 for(int vtxNdx = numVerticesRead; vtxNdx < numVerticesRead + patchNumVertices; vtxNdx++)
4182 {
4183 const Vec3& vtx = tfResult.varying[vtxNdx];
4184 if (edgeDesc.contains(vtx))
4185 outerEdgeVertices.insert(tfResult.varying[vtxNdx]);
4186 }
4187
4188 // Check that the outer edge contains an appropriate number of vertices.
4189 {
4190 const int refNumVerticesOnOuterEdge = 1 + getClampedRoundedTessLevel(m_spacing, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
4191
4192 if ((int)outerEdgeVertices.size() != refNumVerticesOnOuterEdge)
4193 {
4194 log << TestLog::Message << "Failure: the number of vertices on outer edge is " << outerEdgeVertices.size()
4195 << ", expected " << refNumVerticesOnOuterEdge << TestLog::EndMessage
4196 << TestLog::Message << "Note: vertices on the outer edge are:\n" << containerStr(outerEdgeVertices, 5, 0) << TestLog::EndMessage
4197 << TestLog::Message << "Note: the following parameters were used: " << program.description()
4198 << ", tessellation levels: " << tessellationLevelsString(innerLevels, outerLevels) << TestLog::EndMessage;
4199 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4200 return STOP;
4201 }
4202 }
4203
4204 // Compare the vertices to those of the first patch (unless this is the first patch).
4205
4206 if (programNdx == 0 && patchNdx == 0)
4207 firstOuterEdgeVertices = outerEdgeVertices;
4208 else
4209 {
4210 if (firstOuterEdgeVertices != outerEdgeVertices)
4211 {
4212 log << TestLog::Message << "Failure: vertices generated for the edge differ between the following cases:\n"
4213 << " - case A: " << m_programs[0].description() << ", tessellation levels: "
4214 << tessellationLevelsString(&patchTessLevels[0], &patchTessLevels[2]) << "\n"
4215 << " - case B: " << program.description() << ", tessellation levels: "
4216 << tessellationLevelsString(innerLevels, outerLevels) << TestLog::EndMessage;
4217
4218 log << TestLog::Message << "Note: resulting vertices for the edge for the cases were:\n"
4219 << " - case A: " << containerStr(firstOuterEdgeVertices, 5, 14) << "\n"
4220 << " - case B: " << containerStr(outerEdgeVertices, 5, 14) << TestLog::EndMessage;
4221
4222 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4223 return STOP;
4224 }
4225 }
4226
4227 numVerticesRead += patchNumVertices;
4228 }
4229
4230 DE_ASSERT(numVerticesRead == (int)tfResult.varying.size());
4231 }
4232 }
4233 }
4234 }
4235 }
4236 }
4237
4238 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4239 return STOP;
4240 }
4241
4242 /*--------------------------------------------------------------------*//*!
4243 * \brief Test invariance rule #3
4244 *
4245 * Test that the vertices along an outer edge are placed symmetrically.
4246 *
4247 * Draw multiple patches with different tessellation levels and different
4248 * point_mode, winding etc. Before outputting tesscoords with TF, mirror
4249 * the vertices in the TES such that every vertex on an outer edge -
4250 * except the possible middle vertex - should be duplicated in the output.
4251 * Check that appropriate duplicates exist.
4252 *//*--------------------------------------------------------------------*/
4253 class SymmetricOuterEdgeCase : public TestCase
4254 {
4255 public:
SymmetricOuterEdgeCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing,Winding winding,bool usePointMode)4256 SymmetricOuterEdgeCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
4257 : TestCase (context, name, description)
4258 , m_primitiveType (primType)
4259 , m_spacing (spacing)
4260 , m_winding (winding)
4261 , m_usePointMode (usePointMode)
4262 {
4263 }
4264
4265 void init (void);
4266 void deinit (void);
4267 IterateResult iterate (void);
4268
4269 private:
4270 static vector<float> generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel);
4271
4272 static const int RENDER_SIZE = 16;
4273
4274 const TessPrimitiveType m_primitiveType;
4275 const SpacingMode m_spacing;
4276 const Winding m_winding;
4277 const bool m_usePointMode;
4278
4279 SharedPtr<const glu::ShaderProgram> m_program;
4280 };
4281
generatePatchTessLevels(int numPatches,int constantOuterLevelIndex,float constantOuterLevel)4282 vector<float> SymmetricOuterEdgeCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel)
4283 {
4284 de::Random rnd(123);
4285 return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd);
4286 }
4287
init(void)4288 void SymmetricOuterEdgeCase::init (void)
4289 {
4290 checkTessellationSupport(m_context);
4291 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
4292
4293 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
4294 "\n"
4295 "${GLSL_PER_VERTEX_OUT}\n"
4296 "in highp float in_v_attr;\n"
4297 "out highp float in_tc_attr;\n"
4298 "\n"
4299 "void main (void)\n"
4300 "{\n"
4301 " in_tc_attr = in_v_attr;\n"
4302 "}\n");
4303 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
4304 "${TESSELLATION_SHADER_REQUIRE}\n"
4305 "${GLSL_PER_VERTEX_IN_ARR}\n"
4306 "${GLSL_PER_VERTEX_OUT_ARR}\n"
4307 "\n"
4308 "layout (vertices = 1) out;\n"
4309 "\n"
4310 "in highp float in_tc_attr[];\n"
4311 "\n"
4312 "void main (void)\n"
4313 "{\n"
4314 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
4315 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
4316 "\n"
4317 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
4318 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
4319 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
4320 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
4321 "}\n");
4322 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
4323 "${TESSELLATION_SHADER_REQUIRE}\n"
4324 "${GLSL_PER_VERTEX_IN_ARR}\n"
4325 "${GLSL_PER_VERTEX_OUT}\n"
4326 "\n"
4327 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) +
4328 "\n"
4329 "out highp vec4 in_f_color;\n"
4330 "out highp vec4 out_te_tessCoord_isMirrored;\n"
4331 "\n"
4332 "void main (void)\n"
4333 "{\n"
4334 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
4335 " float x = gl_TessCoord.x;\n"
4336 " float y = gl_TessCoord.y;\n"
4337 " float z = gl_TessCoord.z;\n"
4338 " // Mirror one half of each outer edge onto the other half, except the endpoints (because they belong to two edges)\n"
4339 " 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"
4340 " : y == 0.0 && z > 0.5 && z != 1.0 ? vec4(1.0-x, 0.0, 1.0-z, 1.0)\n"
4341 " : x == 0.0 && y > 0.5 && y != 1.0 ? vec4( 0.0, 1.0-y, 1.0-z, 1.0)\n"
4342 " : vec4(x, y, z, 0.0);\n"
4343 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ?
4344 " float x = gl_TessCoord.x;\n"
4345 " float y = gl_TessCoord.y;\n"
4346 " // Mirror one half of each outer edge onto the other half, except the endpoints (because they belong to two edges)\n"
4347 " 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"
4348 " : (y == 0.0 || y == 1.0) && x > 0.5 && x != 1.0 ? vec4(1.0-x, y, 0.0, 1.0)\n"
4349 " : vec4(x, y, 0.0, 0.0);\n"
4350 : m_primitiveType == TESSPRIMITIVETYPE_ISOLINES ?
4351 " float x = gl_TessCoord.x;\n"
4352 " float y = gl_TessCoord.y;\n"
4353 " // Mirror one half of each outer edge onto the other half\n"
4354 " out_te_tessCoord_isMirrored = (x == 0.0 || x == 1.0) && y > 0.5 ? vec4(x, 1.0-y, 0.0, 1.0)\n"
4355 " : vec4(x, y, 0.0, 0.0f);\n"
4356 : DE_NULL) +
4357 "\n"
4358 " gl_Position = vec4(gl_TessCoord.xy, 0.0, 1.0);\n"
4359 " in_f_color = vec4(1.0);\n"
4360 "}\n");
4361 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
4362 "\n"
4363 "layout (location = 0) out mediump vec4 o_color;\n"
4364 "\n"
4365 "in highp vec4 in_f_color;\n"
4366 "\n"
4367 "void main (void)\n"
4368 "{\n"
4369 " o_color = in_f_color;\n"
4370 "}\n");
4371
4372 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
4373 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
4374 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
4375 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
4376 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
4377 << glu::TransformFeedbackVarying ("out_te_tessCoord_isMirrored")
4378 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
4379
4380 m_testCtx.getLog() << *m_program;
4381 if (!m_program->isOk())
4382 TCU_FAIL("Program compilation failed");
4383 }
4384
deinit(void)4385 void SymmetricOuterEdgeCase::deinit (void)
4386 {
4387 m_program.clear();
4388 }
4389
iterate(void)4390 SymmetricOuterEdgeCase::IterateResult SymmetricOuterEdgeCase::iterate (void)
4391 {
4392 typedef TransformFeedbackHandler<Vec4> TFHandler;
4393
4394 TestLog& log = m_testCtx.getLog();
4395 const RenderContext& renderCtx = m_context.getRenderContext();
4396 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
4397 const glw::Functions& gl = renderCtx.getFunctions();
4398
4399 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 };
4400 const vector<OuterEdgeDescription> edgeDescriptions = outerEdgeDescriptions(m_primitiveType);
4401
4402 {
4403 // Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once.
4404 int maxNumVerticesInDrawCall;
4405 {
4406 const vector<float> patchTessLevels = generatePatchTessLevels(1, 0 /* outer-edge index doesn't affect vertex count */, arrayMax(singleOuterEdgeLevels));
4407 maxNumVerticesInDrawCall = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]);
4408 }
4409
4410 {
4411 const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall);
4412
4413 setViewport(gl, viewport);
4414 gl.patchParameteri(GL_PATCH_VERTICES, 6);
4415
4416 for (int outerEdgeIndex = 0; outerEdgeIndex < (int)edgeDescriptions.size(); outerEdgeIndex++)
4417 {
4418 const OuterEdgeDescription& edgeDesc = edgeDescriptions[outerEdgeIndex];
4419
4420 for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(singleOuterEdgeLevels); outerEdgeLevelCaseNdx++)
4421 {
4422 typedef std::set<Vec3, VecLexLessThan<3> > Vec3Set;
4423
4424 const vector<float> patchTessLevels = generatePatchTessLevels(1, outerEdgeIndex, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
4425 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)patchTessLevels.size(), 0, &patchTessLevels[0]) };
4426
4427 log << TestLog::Message << "Testing with outer tessellation level " << singleOuterEdgeLevels[outerEdgeLevelCaseNdx]
4428 << " for the " << edgeDesc.description() << " edge, and with various levels for other edges" << TestLog::EndMessage;
4429
4430 {
4431 const deUint32 programGL = m_program->getProgram();
4432
4433 gl.useProgram(programGL);
4434
4435 {
4436 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode),
4437 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)patchTessLevels.size());
4438 const int refNumVertices = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]);
4439
4440 if ((int)tfResult.varying.size() != refNumVertices)
4441 {
4442 log << TestLog::Message << "Failure: the number of vertices returned by transform feedback is "
4443 << tfResult.varying.size() << ", expected " << refNumVertices << TestLog::EndMessage
4444 << TestLog::Message << "Note: rendered 1 patch, tessellation levels are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n"
4445 << containerStr(patchTessLevels, 6) << TestLog::EndMessage;
4446
4447 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4448 return STOP;
4449 }
4450
4451 // Check the vertices.
4452
4453 {
4454 Vec3Set nonMirroredEdgeVertices;
4455 Vec3Set mirroredEdgeVertices;
4456
4457 // We're interested in just the vertices on the current outer edge.
4458 for(int vtxNdx = 0; vtxNdx < refNumVertices; vtxNdx++)
4459 {
4460 const Vec3& vtx = tfResult.varying[vtxNdx].swizzle(0,1,2);
4461 if (edgeDesc.contains(vtx))
4462 {
4463 // Ignore the middle vertex of the outer edge, as it's exactly at the mirroring point;
4464 // for isolines, also ignore (0, 0) and (1, 0) because there's no mirrored counterpart for them.
4465 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES && vtx == tcu::select(Vec3(0.0f), Vec3(0.5f), singleTrueMask<3>(edgeDesc.constantCoordinateIndex)))
4466 continue;
4467 if (m_primitiveType == TESSPRIMITIVETYPE_QUADS && vtx.swizzle(0,1) == tcu::select(Vec2(edgeDesc.constantCoordinateValueChoices[0]),
4468 Vec2(0.5f),
4469 singleTrueMask<2>(edgeDesc.constantCoordinateIndex)))
4470 continue;
4471 if (m_primitiveType == TESSPRIMITIVETYPE_ISOLINES && (vtx == Vec3(0.0f, 0.5f, 0.0f) || vtx == Vec3(1.0f, 0.5f, 0.0f) ||
4472 vtx == Vec3(0.0f, 0.0f, 0.0f) || vtx == Vec3(1.0f, 0.0f, 0.0f)))
4473 continue;
4474
4475 const bool isMirrored = tfResult.varying[vtxNdx].w() > 0.5f;
4476 if (isMirrored)
4477 mirroredEdgeVertices.insert(vtx);
4478 else
4479 nonMirroredEdgeVertices.insert(vtx);
4480 }
4481 }
4482
4483 if (m_primitiveType != TESSPRIMITIVETYPE_ISOLINES)
4484 {
4485 // Check that both endpoints are present. Note that endpoints aren't mirrored by the shader, since they belong to more than one edge.
4486
4487 Vec3 endpointA;
4488 Vec3 endpointB;
4489
4490 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4491 {
4492 endpointA = tcu::select(Vec3(1.0f), Vec3(0.0f), singleTrueMask<3>((edgeDesc.constantCoordinateIndex + 1) % 3));
4493 endpointB = tcu::select(Vec3(1.0f), Vec3(0.0f), singleTrueMask<3>((edgeDesc.constantCoordinateIndex + 2) % 3));
4494 }
4495 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
4496 {
4497 endpointA.xy() = tcu::select(Vec2(edgeDesc.constantCoordinateValueChoices[0]), Vec2(0.0f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex));
4498 endpointB.xy() = tcu::select(Vec2(edgeDesc.constantCoordinateValueChoices[0]), Vec2(1.0f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex));
4499 }
4500 else
4501 DE_ASSERT(false);
4502
4503 if (!contains(nonMirroredEdgeVertices, endpointA) ||
4504 !contains(nonMirroredEdgeVertices, endpointB))
4505 {
4506 log << TestLog::Message << "Failure: edge doesn't contain both endpoints, " << endpointA << " and " << endpointB << TestLog::EndMessage
4507 << TestLog::Message << "Note: non-mirrored vertices:\n" << containerStr(nonMirroredEdgeVertices, 5)
4508 << "\nmirrored vertices:\n" << containerStr(mirroredEdgeVertices, 5) << TestLog::EndMessage;
4509 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4510 return STOP;
4511 }
4512 nonMirroredEdgeVertices.erase(endpointA);
4513 nonMirroredEdgeVertices.erase(endpointB);
4514 }
4515
4516 if (nonMirroredEdgeVertices != mirroredEdgeVertices)
4517 {
4518 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
4519 << TestLog::Message << "Note: non-mirrored vertices:\n" << containerStr(nonMirroredEdgeVertices, 5)
4520 << "\nmirrored vertices:\n" << containerStr(mirroredEdgeVertices, 5) << TestLog::EndMessage;
4521 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4522 return STOP;
4523 }
4524 }
4525 }
4526 }
4527 }
4528 }
4529 }
4530 }
4531
4532 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4533 return STOP;
4534 }
4535
4536 /*--------------------------------------------------------------------*//*!
4537 * \brief Test invariance rule #4
4538 *
4539 * Test that the vertices on an outer edge don't depend on which of the
4540 * edges it is, other than with respect to component order.
4541 *//*--------------------------------------------------------------------*/
4542 class OuterEdgeVertexSetIndexIndependenceCase : public TestCase
4543 {
4544 public:
OuterEdgeVertexSetIndexIndependenceCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing,Winding winding,bool usePointMode)4545 OuterEdgeVertexSetIndexIndependenceCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
4546 : TestCase (context, name, description)
4547 , m_primitiveType (primType)
4548 , m_spacing (spacing)
4549 , m_winding (winding)
4550 , m_usePointMode (usePointMode)
4551 {
4552 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
4553 }
4554
4555 void init (void);
4556 void deinit (void);
4557 IterateResult iterate (void);
4558
4559 private:
4560 static vector<float> generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel);
4561
4562 static const int RENDER_SIZE = 16;
4563
4564 const TessPrimitiveType m_primitiveType;
4565 const SpacingMode m_spacing;
4566 const Winding m_winding;
4567 const bool m_usePointMode;
4568
4569 SharedPtr<const glu::ShaderProgram> m_program;
4570 };
4571
generatePatchTessLevels(int numPatches,int constantOuterLevelIndex,float constantOuterLevel)4572 vector<float> OuterEdgeVertexSetIndexIndependenceCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel)
4573 {
4574 de::Random rnd(123);
4575 return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd);
4576 }
4577
init(void)4578 void OuterEdgeVertexSetIndexIndependenceCase::init (void)
4579 {
4580 checkTessellationSupport(m_context);
4581 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
4582
4583 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
4584 "${GLSL_PER_VERTEX_OUT}\n"
4585 "\n"
4586 "in highp float in_v_attr;\n"
4587 "out highp float in_tc_attr;\n"
4588 "\n"
4589 "void main (void)\n"
4590 "{\n"
4591 " in_tc_attr = in_v_attr;\n"
4592 "}\n");
4593 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
4594 "${TESSELLATION_SHADER_REQUIRE}\n"
4595 "${GLSL_PER_VERTEX_IN_ARR}\n"
4596 "${GLSL_PER_VERTEX_OUT_ARR}\n"
4597 "\n"
4598 "layout (vertices = 1) out;\n"
4599 "\n"
4600 "in highp float in_tc_attr[];\n"
4601 "\n"
4602 "void main (void)\n"
4603 "{\n"
4604 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
4605 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
4606 "\n"
4607 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
4608 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
4609 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
4610 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
4611 "}\n");
4612 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
4613 "${TESSELLATION_SHADER_REQUIRE}\n"
4614 "${GLSL_PER_VERTEX_IN_ARR}\n"
4615 "${GLSL_PER_VERTEX_OUT}\n"
4616 "\n"
4617 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) +
4618 "\n"
4619 "out highp vec4 in_f_color;\n"
4620 "out highp vec3 out_te_tessCoord;\n"
4621 "\n"
4622 "void main (void)\n"
4623 "{\n"
4624 " out_te_tessCoord = gl_TessCoord;"
4625 " gl_Position = vec4(gl_TessCoord.xy, 0.0, 1.0);\n"
4626 " in_f_color = vec4(1.0);\n"
4627 "}\n");
4628 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
4629 "\n"
4630 "layout (location = 0) out mediump vec4 o_color;\n"
4631 "\n"
4632 "in highp vec4 in_f_color;\n"
4633 "\n"
4634 "void main (void)\n"
4635 "{\n"
4636 " o_color = in_f_color;\n"
4637 "}\n");
4638
4639 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
4640 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
4641 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
4642 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
4643 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
4644 << glu::TransformFeedbackVarying ("out_te_tessCoord")
4645 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
4646
4647 m_testCtx.getLog() << *m_program;
4648 if (!m_program->isOk())
4649 TCU_FAIL("Program compilation failed");
4650 }
4651
deinit(void)4652 void OuterEdgeVertexSetIndexIndependenceCase::deinit (void)
4653 {
4654 m_program.clear();
4655 }
4656
iterate(void)4657 OuterEdgeVertexSetIndexIndependenceCase::IterateResult OuterEdgeVertexSetIndexIndependenceCase::iterate (void)
4658 {
4659 typedef TransformFeedbackHandler<Vec3> TFHandler;
4660
4661 TestLog& log = m_testCtx.getLog();
4662 const RenderContext& renderCtx = m_context.getRenderContext();
4663 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
4664 const glw::Functions& gl = renderCtx.getFunctions();
4665 const deUint32 programGL = m_program->getProgram();
4666
4667 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 };
4668 const vector<OuterEdgeDescription> edgeDescriptions = outerEdgeDescriptions(m_primitiveType);
4669
4670 gl.useProgram(programGL);
4671 setViewport(gl, viewport);
4672 gl.patchParameteri(GL_PATCH_VERTICES, 6);
4673
4674 {
4675 // Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once.
4676 int maxNumVerticesInDrawCall = 0;
4677 {
4678 const vector<float> patchTessLevels = generatePatchTessLevels(1, 0 /* outer-edge index doesn't affect vertex count */, arrayMax(singleOuterEdgeLevels));
4679 maxNumVerticesInDrawCall = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]);
4680 }
4681
4682 {
4683 const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall);
4684
4685 for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(singleOuterEdgeLevels); outerEdgeLevelCaseNdx++)
4686 {
4687 typedef std::set<Vec3, VecLexLessThan<3> > Vec3Set;
4688
4689 Vec3Set firstEdgeVertices;
4690
4691 for (int outerEdgeIndex = 0; outerEdgeIndex < (int)edgeDescriptions.size(); outerEdgeIndex++)
4692 {
4693 const OuterEdgeDescription& edgeDesc = edgeDescriptions[outerEdgeIndex];
4694 const vector<float> patchTessLevels = generatePatchTessLevels(1, outerEdgeIndex, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
4695 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)patchTessLevels.size(), 0, &patchTessLevels[0]) };
4696
4697 log << TestLog::Message << "Testing with outer tessellation level " << singleOuterEdgeLevels[outerEdgeLevelCaseNdx]
4698 << " for the " << edgeDesc.description() << " edge, and with various levels for other edges" << TestLog::EndMessage;
4699
4700 {
4701 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode),
4702 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)patchTessLevels.size());
4703 const int refNumVertices = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]);
4704
4705 if ((int)tfResult.varying.size() != refNumVertices)
4706 {
4707 log << TestLog::Message << "Failure: the number of vertices returned by transform feedback is "
4708 << tfResult.varying.size() << ", expected " << refNumVertices << TestLog::EndMessage
4709 << TestLog::Message << "Note: rendered 1 patch, tessellation levels are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n"
4710 << containerStr(patchTessLevels, 6) << TestLog::EndMessage;
4711
4712 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4713 return STOP;
4714 }
4715
4716 {
4717 Vec3Set currentEdgeVertices;
4718
4719 // Get the vertices on the current outer edge.
4720 for(int vtxNdx = 0; vtxNdx < refNumVertices; vtxNdx++)
4721 {
4722 const Vec3& vtx = tfResult.varying[vtxNdx];
4723 if (edgeDesc.contains(vtx))
4724 {
4725 // Swizzle components to match the order of the first edge.
4726 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4727 {
4728 currentEdgeVertices.insert(outerEdgeIndex == 0 ? vtx
4729 : outerEdgeIndex == 1 ? vtx.swizzle(1, 0, 2)
4730 : outerEdgeIndex == 2 ? vtx.swizzle(2, 1, 0)
4731 : Vec3(-1.0f));
4732 }
4733 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
4734 {
4735 currentEdgeVertices.insert(Vec3(outerEdgeIndex == 0 ? vtx.y()
4736 : outerEdgeIndex == 1 ? vtx.x()
4737 : outerEdgeIndex == 2 ? vtx.y()
4738 : outerEdgeIndex == 3 ? vtx.x()
4739 : -1.0f,
4740 0.0f, 0.0f));
4741 }
4742 else
4743 DE_ASSERT(false);
4744 }
4745 }
4746
4747 if (outerEdgeIndex == 0)
4748 firstEdgeVertices = currentEdgeVertices;
4749 else
4750 {
4751 // Compare vertices of this edge to those of the first edge.
4752
4753 if (currentEdgeVertices != firstEdgeVertices)
4754 {
4755 const char* const swizzleDesc = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? (outerEdgeIndex == 1 ? "(y, x, z)"
4756 : outerEdgeIndex == 2 ? "(z, y, x)"
4757 : DE_NULL)
4758 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? (outerEdgeIndex == 1 ? "(x, 0)"
4759 : outerEdgeIndex == 2 ? "(y, 0)"
4760 : outerEdgeIndex == 3 ? "(x, 0)"
4761 : DE_NULL)
4762 : DE_NULL;
4763
4764 log << TestLog::Message << "Failure: the set of vertices on the " << edgeDesc.description() << " edge"
4765 << " doesn't match the set of vertices on the " << edgeDescriptions[0].description() << " edge" << TestLog::EndMessage
4766 << TestLog::Message << "Note: set of vertices on " << edgeDesc.description() << " edge, components swizzled like " << swizzleDesc
4767 << " to match component order on first edge:\n" << containerStr(currentEdgeVertices, 5)
4768 << "\non " << edgeDescriptions[0].description() << " edge:\n" << containerStr(firstEdgeVertices, 5) << TestLog::EndMessage;
4769 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4770 return STOP;
4771 }
4772 }
4773 }
4774 }
4775 }
4776 }
4777 }
4778 }
4779
4780 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4781 return STOP;
4782 }
4783
4784 /*--------------------------------------------------------------------*//*!
4785 * \brief Test invariance rule #5
4786 *
4787 * Test that the set of triangles input to the TES only depends on the
4788 * tessellation levels, tessellation mode and spacing mode. Specifically,
4789 * winding doesn't change the set of triangles, though it can change the
4790 * order in which they are input to TES, and can (and will) change the
4791 * vertex order within a triangle.
4792 *//*--------------------------------------------------------------------*/
4793 class InvariantTriangleSetCase : public PrimitiveSetInvarianceCase
4794 {
4795 public:
InvariantTriangleSetCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing)4796 InvariantTriangleSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing)
4797 : PrimitiveSetInvarianceCase(context, name, description, primType, spacing, false, WINDINGUSAGE_VARY)
4798 {
4799 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
4800 }
4801
4802 protected:
compare(const vector<Vec3> & coordsA,const vector<Vec3> & coordsB,int) const4803 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int) const
4804 {
4805 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog());
4806 }
4807 };
4808
4809 /*--------------------------------------------------------------------*//*!
4810 * \brief Test invariance rule #6
4811 *
4812 * Test that the set of inner triangles input to the TES only depends on
4813 * the inner tessellation levels, tessellation mode and spacing mode.
4814 *//*--------------------------------------------------------------------*/
4815 class InvariantInnerTriangleSetCase : public PrimitiveSetInvarianceCase
4816 {
4817 public:
InvariantInnerTriangleSetCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing)4818 InvariantInnerTriangleSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing)
4819 : PrimitiveSetInvarianceCase(context, name, description, primType, spacing, false, WINDINGUSAGE_VARY)
4820 {
4821 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
4822 }
4823
4824 protected:
genTessLevelCases(void) const4825 virtual vector<LevelCase> genTessLevelCases (void) const
4826 {
4827 const int numSubCases = 4;
4828 const vector<LevelCase> baseResults = PrimitiveSetInvarianceCase::genTessLevelCases();
4829 vector<LevelCase> result;
4830 de::Random rnd (123);
4831
4832 // Generate variants with different values for irrelevant levels.
4833 for (int baseNdx = 0; baseNdx < (int)baseResults.size(); baseNdx++)
4834 {
4835 const TessLevels& base = baseResults[baseNdx].levels[0];
4836 TessLevels levels = base;
4837 LevelCase levelCase;
4838
4839 for (int subNdx = 0; subNdx < numSubCases; subNdx++)
4840 {
4841 levelCase.levels.push_back(levels);
4842
4843 for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); i++)
4844 levels.outer[i] = rnd.getFloat(2.0f, 16.0f);
4845 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4846 levels.inner[1] = rnd.getFloat(2.0f, 16.0f);
4847 }
4848
4849 result.push_back(levelCase);
4850 }
4851
4852 return result;
4853 }
4854
4855 struct IsInnerTriangleTriangle
4856 {
operator ()deqp::gles31::Functional::__anon755cd4500211::InvariantInnerTriangleSetCase::IsInnerTriangleTriangle4857 bool operator() (const Vec3* vertices) const
4858 {
4859 for (int v = 0; v < 3; v++)
4860 for (int c = 0; c < 3; c++)
4861 if (vertices[v][c] == 0.0f)
4862 return false;
4863 return true;
4864 }
4865 };
4866
4867 struct IsInnerQuadTriangle
4868 {
operator ()deqp::gles31::Functional::__anon755cd4500211::InvariantInnerTriangleSetCase::IsInnerQuadTriangle4869 bool operator() (const Vec3* vertices) const
4870 {
4871 for (int v = 0; v < 3; v++)
4872 for (int c = 0; c < 2; c++)
4873 if (vertices[v][c] == 0.0f || vertices[v][c] == 1.0f)
4874 return false;
4875 return true;
4876 }
4877 };
4878
compare(const vector<Vec3> & coordsA,const vector<Vec3> & coordsB,int) const4879 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int) const
4880 {
4881 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4882 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(), IsInnerTriangleTriangle(), "outer triangles");
4883 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
4884 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(), IsInnerQuadTriangle(), "outer triangles");
4885 else
4886 {
4887 DE_ASSERT(false);
4888 return false;
4889 }
4890 }
4891 };
4892
4893 /*--------------------------------------------------------------------*//*!
4894 * \brief Test invariance rule #7
4895 *
4896 * Test that the set of outer triangles input to the TES only depends on
4897 * tessellation mode, spacing mode and the inner and outer tessellation
4898 * levels corresponding to the inner and outer edges relevant to that
4899 * triangle.
4900 *//*--------------------------------------------------------------------*/
4901 class InvariantOuterTriangleSetCase : public PrimitiveSetInvarianceCase
4902 {
4903 public:
InvariantOuterTriangleSetCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing)4904 InvariantOuterTriangleSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing)
4905 : PrimitiveSetInvarianceCase(context, name, description, primType, spacing, false, WINDINGUSAGE_VARY)
4906 {
4907 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
4908 }
4909
4910 protected:
genTessLevelCases(void) const4911 virtual vector<LevelCase> genTessLevelCases (void) const
4912 {
4913 const int numSubCasesPerEdge = 4;
4914 const int numEdges = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3
4915 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4
4916 : -1;
4917 const vector<LevelCase> baseResult = PrimitiveSetInvarianceCase::genTessLevelCases();
4918 vector<LevelCase> result;
4919 de::Random rnd (123);
4920
4921 // Generate variants with different values for irrelevant levels.
4922 for (int baseNdx = 0; baseNdx < (int)baseResult.size(); baseNdx++)
4923 {
4924 const TessLevels& base = baseResult[baseNdx].levels[0];
4925 if (base.inner[0] == 1.0f || (m_primitiveType == TESSPRIMITIVETYPE_QUADS && base.inner[1] == 1.0f))
4926 continue;
4927
4928 for (int edgeNdx = 0; edgeNdx < numEdges; edgeNdx++)
4929 {
4930 TessLevels levels = base;
4931 LevelCase levelCase;
4932 levelCase.mem = edgeNdx;
4933
4934 for (int subCaseNdx = 0; subCaseNdx < numSubCasesPerEdge; subCaseNdx++)
4935 {
4936 levelCase.levels.push_back(levels);
4937
4938 for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); i++)
4939 {
4940 if (i != edgeNdx)
4941 levels.outer[i] = rnd.getFloat(2.0f, 16.0f);
4942 }
4943
4944 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4945 levels.inner[1] = rnd.getFloat(2.0f, 16.0f);
4946 }
4947
4948 result.push_back(levelCase);
4949 }
4950 }
4951
4952 return result;
4953 }
4954
4955 class IsTriangleTriangleOnOuterEdge
4956 {
4957 public:
IsTriangleTriangleOnOuterEdge(int edgeNdx)4958 IsTriangleTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
operator ()(const Vec3 * vertices) const4959 bool operator() (const Vec3* vertices) const
4960 {
4961 bool touchesAppropriateEdge = false;
4962 for (int v = 0; v < 3; v++)
4963 if (vertices[v][m_edgeNdx] == 0.0f)
4964 touchesAppropriateEdge = true;
4965
4966 if (touchesAppropriateEdge)
4967 {
4968 const Vec3 avg = (vertices[0] + vertices[1] + vertices[2]) / 3.0f;
4969 return avg[m_edgeNdx] < avg[(m_edgeNdx+1)%3] &&
4970 avg[m_edgeNdx] < avg[(m_edgeNdx+2)%3];
4971 }
4972 return false;
4973 }
4974
4975 private:
4976 int m_edgeNdx;
4977 };
4978
4979 class IsQuadTriangleOnOuterEdge
4980 {
4981 public:
IsQuadTriangleOnOuterEdge(int edgeNdx)4982 IsQuadTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
4983
onEdge(const Vec3 & v) const4984 bool onEdge (const Vec3& v) const
4985 {
4986 return v[m_edgeNdx%2] == (m_edgeNdx <= 1 ? 0.0f : 1.0f);
4987 }
4988
onAnyEdge(const Vec3 & v)4989 static inline bool onAnyEdge (const Vec3& v)
4990 {
4991 return v[0] == 0.0f || v[0] == 1.0f || v[1] == 0.0f || v[1] == 1.0f;
4992 }
4993
operator ()(const Vec3 * vertices) const4994 bool operator() (const Vec3* vertices) const
4995 {
4996 for (int v = 0; v < 3; v++)
4997 {
4998 const Vec3& a = vertices[v];
4999 const Vec3& b = vertices[(v+1)%3];
5000 const Vec3& c = vertices[(v+2)%3];
5001 if (onEdge(a) && onEdge(b))
5002 return true;
5003 if (onEdge(c) && !onAnyEdge(a) && !onAnyEdge(b) && a[m_edgeNdx%2] == b[m_edgeNdx%2])
5004 return true;
5005 }
5006
5007 return false;
5008 }
5009
5010 private:
5011 int m_edgeNdx;
5012 };
5013
compare(const vector<Vec3> & coordsA,const vector<Vec3> & coordsB,int outerEdgeNdx) const5014 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int outerEdgeNdx) const
5015 {
5016 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
5017 {
5018 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(),
5019 IsTriangleTriangleOnOuterEdge(outerEdgeNdx),
5020 ("inner triangles, and outer triangles corresponding to other edge than edge "
5021 + outerEdgeDescriptions(m_primitiveType)[outerEdgeNdx].description()).c_str());
5022 }
5023 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
5024 {
5025 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(),
5026 IsQuadTriangleOnOuterEdge(outerEdgeNdx),
5027 ("inner triangles, and outer triangles corresponding to other edge than edge "
5028 + outerEdgeDescriptions(m_primitiveType)[outerEdgeNdx].description()).c_str());
5029 }
5030 else
5031 DE_ASSERT(false);
5032
5033 return true;
5034 }
5035 };
5036
5037 /*--------------------------------------------------------------------*//*!
5038 * \brief Base class for testing individual components of tess coords
5039 *
5040 * Useful for testing parts of invariance rule #8.
5041 *//*--------------------------------------------------------------------*/
5042 class TessCoordComponentInvarianceCase : public TestCase
5043 {
5044 public:
TessCoordComponentInvarianceCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing,Winding winding,bool usePointMode)5045 TessCoordComponentInvarianceCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
5046 : TestCase (context, name, description)
5047 , m_primitiveType (primType)
5048 , m_spacing (spacing)
5049 , m_winding (winding)
5050 , m_usePointMode (usePointMode)
5051 {
5052 }
5053
5054 void init (void);
5055 void deinit (void);
5056 IterateResult iterate (void);
5057
5058 protected:
5059 virtual string tessEvalOutputComponentStatements (const char* tessCoordComponentName, const char* outputComponentName) const = 0;
5060 virtual bool checkTessCoordComponent (float component) const = 0;
5061
5062 private:
5063 static vector<float> genTessLevelCases (int numCases);
5064
5065 static const int RENDER_SIZE = 16;
5066
5067 const TessPrimitiveType m_primitiveType;
5068 const SpacingMode m_spacing;
5069 const Winding m_winding;
5070 const bool m_usePointMode;
5071
5072 SharedPtr<const glu::ShaderProgram> m_program;
5073 };
5074
init(void)5075 void TessCoordComponentInvarianceCase::init (void)
5076 {
5077 checkTessellationSupport(m_context);
5078 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
5079
5080 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
5081 "${GLSL_PER_VERTEX_OUT}\n"
5082 "\n"
5083 "in highp float in_v_attr;\n"
5084 "out highp float in_tc_attr;\n"
5085 "\n"
5086 "void main (void)\n"
5087 "{\n"
5088 " in_tc_attr = in_v_attr;\n"
5089 "}\n");
5090 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
5091 "${TESSELLATION_SHADER_REQUIRE}\n"
5092 "${GLSL_PER_VERTEX_IN_ARR}\n"
5093 "${GLSL_PER_VERTEX_OUT_ARR}\n"
5094 "\n"
5095 "layout (vertices = 1) out;\n"
5096 "\n"
5097 "in highp float in_tc_attr[];\n"
5098 "\n"
5099 "void main (void)\n"
5100 "{\n"
5101 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
5102 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
5103 "\n"
5104 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
5105 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
5106 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
5107 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
5108 "}\n");
5109 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
5110 "${TESSELLATION_SHADER_REQUIRE}\n"
5111 "${GLSL_PER_VERTEX_IN_ARR}\n"
5112 "${GLSL_PER_VERTEX_OUT}\n"
5113 "\n"
5114 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) +
5115 "\n"
5116 "out highp vec4 in_f_color;\n"
5117 "out highp vec3 out_te_output;\n"
5118 "\n"
5119 "void main (void)\n"
5120 "{\n"
5121 + tessEvalOutputComponentStatements("gl_TessCoord.x", "out_te_output.x")
5122 + tessEvalOutputComponentStatements("gl_TessCoord.y", "out_te_output.y")
5123
5124 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
5125 tessEvalOutputComponentStatements("gl_TessCoord.z", "out_te_output.z") :
5126 " out_te_output.z = 0.0f;\n") +
5127 " gl_Position = vec4(gl_TessCoord.xy, 0.0, 1.0);\n"
5128 " in_f_color = vec4(1.0);\n"
5129 "}\n");
5130 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
5131 "\n"
5132 "layout (location = 0) out mediump vec4 o_color;\n"
5133 "\n"
5134 "in highp vec4 in_f_color;\n"
5135 "\n"
5136 "void main (void)\n"
5137 "{\n"
5138 " o_color = in_f_color;\n"
5139 "}\n");
5140
5141 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
5142 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
5143 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
5144 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
5145 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
5146 << glu::TransformFeedbackVarying ("out_te_output")
5147 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
5148
5149 m_testCtx.getLog() << *m_program;
5150 if (!m_program->isOk())
5151 TCU_FAIL("Program compilation failed");
5152 }
5153
deinit(void)5154 void TessCoordComponentInvarianceCase::deinit (void)
5155 {
5156 m_program.clear();
5157 }
5158
genTessLevelCases(int numCases)5159 vector<float> TessCoordComponentInvarianceCase::genTessLevelCases (int numCases)
5160 {
5161 de::Random rnd(123);
5162 vector<float> result;
5163
5164 for (int i = 0; i < numCases; i++)
5165 for (int j = 0; j < 6; j++)
5166 result.push_back(rnd.getFloat(1.0f, 63.0f));
5167
5168 return result;
5169 }
5170
iterate(void)5171 TessCoordComponentInvarianceCase::IterateResult TessCoordComponentInvarianceCase::iterate (void)
5172 {
5173 typedef TransformFeedbackHandler<Vec3> TFHandler;
5174
5175 TestLog& log = m_testCtx.getLog();
5176 const RenderContext& renderCtx = m_context.getRenderContext();
5177 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
5178 const glw::Functions& gl = renderCtx.getFunctions();
5179 const int numTessLevelCases = 32;
5180 const vector<float> tessLevels = genTessLevelCases(numTessLevelCases);
5181 const deUint32 programGL = m_program->getProgram();
5182
5183 gl.useProgram(programGL);
5184 setViewport(gl, viewport);
5185 gl.patchParameteri(GL_PATCH_VERTICES, 6);
5186
5187 {
5188 // Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once.
5189 int maxNumVerticesInDrawCall = 0;
5190 for (int i = 0; i < numTessLevelCases; i++)
5191 maxNumVerticesInDrawCall = de::max(maxNumVerticesInDrawCall, referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &tessLevels[6*i+0], &tessLevels[6*i+2]));
5192
5193 {
5194 const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall);
5195
5196 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < numTessLevelCases; tessLevelCaseNdx++)
5197 {
5198 log << TestLog::Message << "Testing with tessellation levels: " << tessellationLevelsString(&tessLevels[6*tessLevelCaseNdx+0], &tessLevels[6*tessLevelCaseNdx+2]) << TestLog::EndMessage;
5199
5200 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)6, 0, &tessLevels[6*tessLevelCaseNdx]) };
5201 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode),
5202 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], 6);
5203
5204 for (int vtxNdx = 0; vtxNdx < (int)tfResult.varying.size(); vtxNdx++)
5205 {
5206 const Vec3& vec = tfResult.varying[vtxNdx];
5207 const int numComps = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2;
5208
5209 for (int compNdx = 0; compNdx < numComps; compNdx++)
5210 {
5211 if (!checkTessCoordComponent(vec[compNdx]))
5212 {
5213 log << TestLog::Message << "Note: output value at index " << vtxNdx << " is "
5214 << (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? de::toString(vec) : de::toString(vec.swizzle(0,1)))
5215 << TestLog::EndMessage;
5216 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid tessellation coordinate component");
5217 return STOP;
5218 }
5219 }
5220 }
5221 }
5222 }
5223 }
5224
5225 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
5226 return STOP;
5227 }
5228
5229 /*--------------------------------------------------------------------*//*!
5230 * \brief Test first part of invariance rule #8
5231 *
5232 * Test that all (relevant) components of tess coord are in [0,1].
5233 *//*--------------------------------------------------------------------*/
5234 class TessCoordComponentRangeCase : public TessCoordComponentInvarianceCase
5235 {
5236 public:
TessCoordComponentRangeCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing,Winding winding,bool usePointMode)5237 TessCoordComponentRangeCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
5238 : TessCoordComponentInvarianceCase(context, name, description, primType, spacing, winding, usePointMode)
5239 {
5240 }
5241
5242 protected:
tessEvalOutputComponentStatements(const char * tessCoordComponentName,const char * outputComponentName) const5243 virtual string tessEvalOutputComponentStatements (const char* tessCoordComponentName, const char* outputComponentName) const
5244 {
5245 return string() + "\t" + outputComponentName + " = " + tessCoordComponentName + ";\n";
5246 }
5247
checkTessCoordComponent(float component) const5248 virtual bool checkTessCoordComponent (float component) const
5249 {
5250 if (!de::inRange(component, 0.0f, 1.0f))
5251 {
5252 m_testCtx.getLog() << TestLog::Message << "Failure: tess coord component isn't in range [0,1]" << TestLog::EndMessage;
5253 return false;
5254 }
5255 return true;
5256 }
5257 };
5258
5259 /*--------------------------------------------------------------------*//*!
5260 * \brief Test second part of invariance rule #8
5261 *
5262 * Test that all (relevant) components of tess coord are in [0,1] and
5263 * 1.0-c is exact for every such component c.
5264 *//*--------------------------------------------------------------------*/
5265 class OneMinusTessCoordComponentCase : public TessCoordComponentInvarianceCase
5266 {
5267 public:
OneMinusTessCoordComponentCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing,Winding winding,bool usePointMode)5268 OneMinusTessCoordComponentCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
5269 : TessCoordComponentInvarianceCase(context, name, description, primType, spacing, winding, usePointMode)
5270 {
5271 }
5272
5273 protected:
tessEvalOutputComponentStatements(const char * tessCoordComponentName,const char * outputComponentName) const5274 virtual string tessEvalOutputComponentStatements (const char* tessCoordComponentName, const char* outputComponentName) const
5275 {
5276 return string() + " {\n"
5277 " float oneMinusComp = 1.0 - " + tessCoordComponentName + ";\n"
5278 " " + outputComponentName + " = " + tessCoordComponentName + " + oneMinusComp;\n"
5279 " }\n";
5280 }
5281
checkTessCoordComponent(float component) const5282 virtual bool checkTessCoordComponent (float component) const
5283 {
5284 if (component != 1.0f)
5285 {
5286 m_testCtx.getLog() << TestLog::Message << "Failure: comp + (1.0-comp) doesn't equal 1.0 for some component of tessellation coordinate" << TestLog::EndMessage;
5287 return false;
5288 }
5289 return true;
5290 }
5291 };
5292
5293 /*--------------------------------------------------------------------*//*!
5294 * \brief Test that patch is discarded if relevant outer level <= 0.0
5295 *
5296 * Draws patches with different combinations of tessellation levels,
5297 * varying which levels are negative. Verifies by checking that colored
5298 * pixels exist inside the area of valid primitives, and only black pixels
5299 * exist inside the area of discarded primitives. An additional sanity
5300 * test is done, checking that the number of primitives written by TF is
5301 * correct.
5302 *//*--------------------------------------------------------------------*/
5303 class PrimitiveDiscardCase : public TestCase
5304 {
5305 public:
PrimitiveDiscardCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,SpacingMode spacing,Winding winding,bool usePointMode)5306 PrimitiveDiscardCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
5307 : TestCase (context, name, description)
5308 , m_primitiveType (primType)
5309 , m_spacing (spacing)
5310 , m_winding (winding)
5311 , m_usePointMode (usePointMode)
5312 {
5313 }
5314
5315 void init (void);
5316 void deinit (void);
5317 IterateResult iterate (void);
5318
5319 private:
5320 static vector<float> genAttributes (void);
5321
5322 static const int RENDER_SIZE = 256;
5323
5324 const TessPrimitiveType m_primitiveType;
5325 const SpacingMode m_spacing;
5326 const Winding m_winding;
5327 const bool m_usePointMode;
5328
5329 SharedPtr<const glu::ShaderProgram> m_program;
5330 };
5331
init(void)5332 void PrimitiveDiscardCase::init (void)
5333 {
5334 checkTessellationSupport(m_context);
5335 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
5336
5337 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
5338 "${GLSL_PER_VERTEX_OUT}\n"
5339 "\n"
5340 "in highp float in_v_attr;\n"
5341 "out highp float in_tc_attr;\n"
5342 "\n"
5343 "void main (void)\n"
5344 "{\n"
5345 " in_tc_attr = in_v_attr;\n"
5346 "}\n");
5347 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
5348 "${TESSELLATION_SHADER_REQUIRE}\n"
5349 "${GLSL_PER_VERTEX_IN_ARR}\n"
5350 "${GLSL_PER_VERTEX_OUT_ARR}\n"
5351 "\n"
5352 "layout (vertices = 1) out;\n"
5353 "\n"
5354 "in highp float in_tc_attr[];\n"
5355 "\n"
5356 "patch out highp vec2 in_te_positionScale;\n"
5357 "patch out highp vec2 in_te_positionOffset;\n"
5358 "\n"
5359 "void main (void)\n"
5360 "{\n"
5361 " in_te_positionScale = vec2(in_tc_attr[6], in_tc_attr[7]);\n"
5362 " in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\n"
5363 "\n"
5364 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
5365 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
5366 "\n"
5367 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
5368 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
5369 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
5370 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
5371 "}\n");
5372 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
5373 "${TESSELLATION_SHADER_REQUIRE}\n"
5374 "${GLSL_PER_VERTEX_IN_ARR}\n"
5375 "${GLSL_PER_VERTEX_OUT}\n"
5376 "\n"
5377 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) +
5378 "\n"
5379 "patch in highp vec2 in_te_positionScale;\n"
5380 "patch in highp vec2 in_te_positionOffset;\n"
5381 "\n"
5382 "out highp vec3 out_te_tessCoord;\n"
5383 "\n"
5384 "void main (void)\n"
5385 "{\n"
5386 " out_te_tessCoord = gl_TessCoord;\n"
5387 " gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n"
5388 "}\n");
5389 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
5390 "\n"
5391 "layout (location = 0) out mediump vec4 o_color;\n"
5392 "\n"
5393 "void main (void)\n"
5394 "{\n"
5395 " o_color = vec4(1.0);\n"
5396 "}\n");
5397
5398 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
5399 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
5400 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
5401 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
5402 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
5403 << glu::TransformFeedbackVarying ("out_te_tessCoord")
5404 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
5405
5406 m_testCtx.getLog() << *m_program;
5407 if (!m_program->isOk())
5408 TCU_FAIL("Program compilation failed");
5409 }
5410
deinit(void)5411 void PrimitiveDiscardCase::deinit (void)
5412 {
5413 m_program.clear();
5414 }
5415
genAttributes(void)5416 vector<float> PrimitiveDiscardCase::genAttributes (void)
5417 {
5418 // Generate input attributes (tessellation levels, and position scale and
5419 // offset) for a number of primitives. Each primitive has a different
5420 // combination of tessellatio levels; each level is either a valid
5421 // value or an "invalid" value (negative or zero, chosen from
5422 // invalidTessLevelChoices).
5423
5424 // \note The attributes are generated in such an order that all of the
5425 // valid attribute tuples come before the first invalid one both
5426 // in the result vector, and when scanning the resulting 2d grid
5427 // of primitives is scanned in y-major order. This makes
5428 // verification somewhat simpler.
5429
5430 static const float baseTessLevels[6] = { 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f };
5431 static const float invalidTessLevelChoices[] = { -0.42f, 0.0f };
5432 const int numChoices = 1 + DE_LENGTH_OF_ARRAY(invalidTessLevelChoices);
5433 float choices[6][numChoices];
5434 vector<float> result;
5435
5436 for (int levelNdx = 0; levelNdx < 6; levelNdx++)
5437 for (int choiceNdx = 0; choiceNdx < numChoices; choiceNdx++)
5438 choices[levelNdx][choiceNdx] = choiceNdx == 0 ? baseTessLevels[levelNdx] : invalidTessLevelChoices[choiceNdx-1];
5439
5440 {
5441 const int numCols = intPow(numChoices, 6/2); // sqrt(numChoices**6) == sqrt(number of primitives)
5442 const int numRows = numCols;
5443 int index = 0;
5444 int i[6];
5445 // We could do this with some generic combination-generation function, but meh, it's not that bad.
5446 for (i[2] = 0; i[2] < numChoices; i[2]++) // First outer
5447 for (i[3] = 0; i[3] < numChoices; i[3]++) // Second outer
5448 for (i[4] = 0; i[4] < numChoices; i[4]++) // Third outer
5449 for (i[5] = 0; i[5] < numChoices; i[5]++) // Fourth outer
5450 for (i[0] = 0; i[0] < numChoices; i[0]++) // First inner
5451 for (i[1] = 0; i[1] < numChoices; i[1]++) // Second inner
5452 {
5453 for (int j = 0; j < 6; j++)
5454 result.push_back(choices[j][i[j]]);
5455
5456 {
5457 const int col = index % numCols;
5458 const int row = index / numCols;
5459 // Position scale.
5460 result.push_back((float)2.0f / (float)numCols);
5461 result.push_back((float)2.0f / (float)numRows);
5462 // Position offset.
5463 result.push_back((float)col / (float)numCols * 2.0f - 1.0f);
5464 result.push_back((float)row / (float)numRows * 2.0f - 1.0f);
5465 }
5466
5467 index++;
5468 }
5469 }
5470
5471 return result;
5472 }
5473
iterate(void)5474 PrimitiveDiscardCase::IterateResult PrimitiveDiscardCase::iterate (void)
5475 {
5476 typedef TransformFeedbackHandler<Vec3> TFHandler;
5477
5478 TestLog& log = m_testCtx.getLog();
5479 const RenderContext& renderCtx = m_context.getRenderContext();
5480 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
5481 const glw::Functions& gl = renderCtx.getFunctions();
5482 const vector<float> attributes = genAttributes();
5483 const int numAttribsPerPrimitive = 6+2+2; // Tess levels, scale, offset.
5484 const int numPrimitives = (int)attributes.size() / numAttribsPerPrimitive;
5485 const deUint32 programGL = m_program->getProgram();
5486
5487 gl.useProgram(programGL);
5488 setViewport(gl, viewport);
5489 gl.patchParameteri(GL_PATCH_VERTICES, numAttribsPerPrimitive);
5490
5491 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
5492 gl.clear(GL_COLOR_BUFFER_BIT);
5493
5494 // Check the convenience assertion that all discarded patches come after the last non-discarded patch.
5495 {
5496 bool discardedPatchEncountered = false;
5497 for (int patchNdx = 0; patchNdx < numPrimitives; patchNdx++)
5498 {
5499 const bool discard = isPatchDiscarded(m_primitiveType, &attributes[numAttribsPerPrimitive*patchNdx + 2]);
5500 DE_ASSERT(discard || !discardedPatchEncountered);
5501 discardedPatchEncountered = discard;
5502 }
5503 DE_UNREF(discardedPatchEncountered);
5504 }
5505
5506 {
5507 int numVerticesInDrawCall = 0;
5508 for (int patchNdx = 0; patchNdx < numPrimitives; patchNdx++)
5509 numVerticesInDrawCall += referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &attributes[numAttribsPerPrimitive*patchNdx+0], &attributes[numAttribsPerPrimitive*patchNdx+2]);
5510
5511 log << TestLog::Message << "Note: rendering " << numPrimitives << " patches; first patches have valid relevant outer levels, "
5512 << "but later patches have one or more invalid (i.e. less than or equal to 0.0) relevant outer levels" << TestLog::EndMessage;
5513
5514 {
5515 const TFHandler tfHandler (m_context.getRenderContext(), numVerticesInDrawCall);
5516 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)attributes.size(), 0, &attributes[0]) };
5517 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode),
5518 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)attributes.size());
5519 const tcu::Surface pixels = getPixels(renderCtx, viewport);
5520
5521 log << TestLog::Image("RenderedImage", "Rendered image", pixels);
5522
5523 if ((int)tfResult.varying.size() != numVerticesInDrawCall)
5524 {
5525 log << TestLog::Message << "Failure: expected " << numVerticesInDrawCall << " vertices from transform feedback, got " << tfResult.varying.size() << TestLog::EndMessage;
5526 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Wrong number of tessellation coordinates");
5527 return STOP;
5528 }
5529
5530 // Check that white pixels are found around every non-discarded
5531 // patch, and that only black pixels are found after the last
5532 // non-discarded patch.
5533 {
5534 int lastWhitePixelRow = 0;
5535 int secondToLastWhitePixelRow = 0;
5536 int lastWhitePixelColumnOnSecondToLastWhitePixelRow = 0;
5537
5538 for (int patchNdx = 0; patchNdx < numPrimitives; patchNdx++)
5539 {
5540 const float* const attr = &attributes[numAttribsPerPrimitive*patchNdx];
5541 const bool validLevels = !isPatchDiscarded(m_primitiveType, &attr[2]);
5542
5543 if (validLevels)
5544 {
5545 // Not a discarded patch; check that at least one white pixel is found in its area.
5546
5547 const float* const scale = &attr[6];
5548 const float* const offset = &attr[8];
5549 const int x0 = (int)(( offset[0] + 1.0f)*0.5f*(float)pixels.getWidth()) - 1;
5550 const int x1 = (int)((scale[0] + offset[0] + 1.0f)*0.5f*(float)pixels.getWidth()) + 1;
5551 const int y0 = (int)(( offset[1] + 1.0f)*0.5f*(float)pixels.getHeight()) - 1;
5552 const int y1 = (int)((scale[1] + offset[1] + 1.0f)*0.5f*(float)pixels.getHeight()) + 1;
5553 const bool isMSAA = renderCtx.getRenderTarget().getNumSamples() > 1;
5554 bool pixelOk = false;
5555
5556 if (y1 > lastWhitePixelRow)
5557 {
5558 secondToLastWhitePixelRow = lastWhitePixelRow;
5559 lastWhitePixelRow = y1;
5560 }
5561 lastWhitePixelColumnOnSecondToLastWhitePixelRow = x1;
5562
5563 for (int y = y0; y <= y1 && !pixelOk; y++)
5564 for (int x = x0; x <= x1 && !pixelOk; x++)
5565 {
5566 if (!de::inBounds(x, 0, pixels.getWidth()) || !de::inBounds(y, 0, pixels.getHeight()))
5567 continue;
5568
5569 if (isMSAA)
5570 {
5571 if (pixels.getPixel(x, y) != tcu::RGBA::black())
5572 pixelOk = true;
5573 }
5574 else
5575 {
5576 if (pixels.getPixel(x, y) == tcu::RGBA::white())
5577 pixelOk = true;
5578 }
5579 }
5580
5581 if (!pixelOk)
5582 {
5583 log << TestLog::Message << "Failure: expected at least one " << (isMSAA ? "non-black" : "white") << " pixel in the rectangle "
5584 << "[x0=" << x0 << ", y0=" << y0 << ", x1=" << x1 << ", y1=" << y1 << "]" << TestLog::EndMessage
5585 << TestLog::Message << "Note: the rectangle approximately corresponds to the patch with these tessellation levels: "
5586 << tessellationLevelsString(&attr[0], &attr[1]) << TestLog::EndMessage;
5587 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
5588 return STOP;
5589 }
5590 }
5591 else
5592 {
5593 // First discarded primitive patch; the remaining are guaranteed to be discarded ones as well.
5594
5595 for (int y = 0; y < pixels.getHeight(); y++)
5596 for (int x = 0; x < pixels.getWidth(); x++)
5597 {
5598 if (y > lastWhitePixelRow || (y > secondToLastWhitePixelRow && x > lastWhitePixelColumnOnSecondToLastWhitePixelRow))
5599 {
5600 if (pixels.getPixel(x, y) != tcu::RGBA::black())
5601 {
5602 log << TestLog::Message << "Failure: expected all pixels to be black in the area "
5603 << (lastWhitePixelColumnOnSecondToLastWhitePixelRow < pixels.getWidth()-1
5604 ? string() + "y > " + de::toString(lastWhitePixelRow) + " || (y > " + de::toString(secondToLastWhitePixelRow)
5605 + " && x > " + de::toString(lastWhitePixelColumnOnSecondToLastWhitePixelRow) + ")"
5606 : string() + "y > " + de::toString(lastWhitePixelRow))
5607 << " (they all correspond to patches that should be discarded)" << TestLog::EndMessage
5608 << TestLog::Message << "Note: pixel " << tcu::IVec2(x, y) << " isn't black" << TestLog::EndMessage;
5609 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
5610 return STOP;
5611 }
5612 }
5613 }
5614
5615 break;
5616 }
5617 }
5618 }
5619 }
5620 }
5621
5622 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
5623 return STOP;
5624 }
5625
5626 /*--------------------------------------------------------------------*//*!
5627 * \brief Case testing user-defined IO between TCS and TES
5628 *
5629 * TCS outputs various values to TES, including aggregates. The outputs
5630 * can be per-patch or per-vertex, and if per-vertex, they can also be in
5631 * an IO block. Per-vertex input array size can be left implicit (i.e.
5632 * inputArray[]) or explicit either by gl_MaxPatchVertices or an integer
5633 * literal whose value is queried from GL.
5634 *
5635 * The values output are generated in TCS and verified in TES against
5636 * similarly generated values. In case a verification of a value fails, the
5637 * index of the invalid value is output with TF.
5638 * As a sanity check, also the rendering result is verified (against pre-
5639 * rendered reference).
5640 *//*--------------------------------------------------------------------*/
5641 class UserDefinedIOCase : public TestCase
5642 {
5643 public:
5644 enum IOType
5645 {
5646 IO_TYPE_PER_PATCH = 0,
5647 IO_TYPE_PER_PATCH_ARRAY,
5648 IO_TYPE_PER_PATCH_BLOCK,
5649 IO_TYPE_PER_PATCH_BLOCK_ARRAY,
5650 IO_TYPE_PER_VERTEX,
5651 IO_TYPE_PER_VERTEX_BLOCK,
5652
5653 IO_TYPE_LAST
5654 };
5655
5656 enum VertexIOArraySize
5657 {
5658 VERTEX_IO_ARRAY_SIZE_IMPLICIT = 0,
5659 VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN, //!< Use gl_MaxPatchVertices as size for per-vertex input array.
5660 VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY, //!< Query GL_MAX_PATCH_VERTICES, and use that as size for per-vertex input array.
5661
5662 VERTEX_IO_ARRAY_SIZE_LAST
5663 };
5664
5665 enum TessControlOutArraySize
5666 {
5667 TESS_CONTROL_OUT_ARRAY_SIZE_IMPLICIT = 0,
5668 TESS_CONTROL_OUT_ARRAY_SIZE_LAYOUT,
5669 TESS_CONTROL_OUT_ARRAY_SIZE_QUERY,
5670 TESS_CONTROL_OUT_ARRAY_SIZE_SHADER_BUILTIN
5671 };
5672
UserDefinedIOCase(Context & context,const char * name,const char * description,TessPrimitiveType primType,IOType ioType,VertexIOArraySize vertexIOArraySize,TessControlOutArraySize tessControlOutArraySize,const char * referenceImagePath)5673 UserDefinedIOCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, IOType ioType, VertexIOArraySize vertexIOArraySize, TessControlOutArraySize tessControlOutArraySize, const char* referenceImagePath)
5674 : TestCase (context, name, description)
5675 , m_primitiveType (primType)
5676 , m_ioType (ioType)
5677 , m_vertexIOArraySize (vertexIOArraySize)
5678 , m_tessControlOutArraySize (tessControlOutArraySize)
5679 , m_referenceImagePath (referenceImagePath)
5680 {
5681 }
5682
5683 void init (void);
5684 void deinit (void);
5685 IterateResult iterate (void);
5686
5687 private:
5688 typedef string (*BasicTypeVisitFunc)(const string& name, glu::DataType type, int indentationDepth); //!< See glslTraverseBasicTypes below.
5689
5690 class TopLevelObject
5691 {
5692 public:
~TopLevelObject(void)5693 virtual ~TopLevelObject (void) {}
5694
5695 virtual string name (void) const = 0;
5696 virtual string declare (void) const = 0;
5697 virtual string declareArray (const string& arraySizeExpr) const = 0;
5698 virtual string glslTraverseBasicTypeArray (int numArrayElements, //!< If negative, traverse just array[gl_InvocationID], not all indices.
5699 int indentationDepth,
5700 BasicTypeVisitFunc) const = 0;
5701 virtual string glslTraverseBasicType (int indentationDepth,
5702 BasicTypeVisitFunc) const = 0;
5703 virtual int numBasicSubobjectsInElementType (void) const = 0;
5704 virtual string basicSubobjectAtIndex (int index, int arraySize) const = 0;
5705 };
5706
5707 class Variable : public TopLevelObject
5708 {
5709 public:
Variable(const string & name_,const glu::VarType & type,bool isArray)5710 Variable (const string& name_, const glu::VarType& type, bool isArray)
5711 : m_name (name_)
5712 , m_type (type)
5713 , m_isArray (isArray)
5714 {
5715 DE_ASSERT(!type.isArrayType());
5716 }
5717
name(void) const5718 string name (void) const { return m_name; }
5719 string declare (void) const;
5720 string declareArray (const string& arraySizeExpr) const;
5721 string glslTraverseBasicTypeArray (int numArrayElements, int indentationDepth, BasicTypeVisitFunc) const;
5722 string glslTraverseBasicType (int indentationDepth, BasicTypeVisitFunc) const;
5723 int numBasicSubobjectsInElementType (void) const;
5724 string basicSubobjectAtIndex (int index, int arraySize) const;
5725
5726 private:
5727 string m_name;
5728 glu::VarType m_type; //!< If this Variable is an array element, m_type is the element type; otherwise just the variable type.
5729 const bool m_isArray;
5730 };
5731
5732 class IOBlock : public TopLevelObject
5733 {
5734 public:
5735 struct Member
5736 {
5737 string name;
5738 glu::VarType type;
Memberdeqp::gles31::Functional::__anon755cd4500211::UserDefinedIOCase::IOBlock::Member5739 Member (const string& n, const glu::VarType& t) : name(n), type(t) {}
5740 };
5741
IOBlock(const string & blockName,const string & interfaceName,const vector<Member> & members)5742 IOBlock (const string& blockName, const string& interfaceName, const vector<Member>& members)
5743 : m_blockName (blockName)
5744 , m_interfaceName (interfaceName)
5745 , m_members (members)
5746 {
5747 }
5748
name(void) const5749 string name (void) const { return m_interfaceName; }
5750 string declare (void) const;
5751 string declareArray (const string& arraySizeExpr) const;
5752 string glslTraverseBasicTypeArray (int numArrayElements, int indentationDepth, BasicTypeVisitFunc) const;
5753 string glslTraverseBasicType (int indentationDepth, BasicTypeVisitFunc) const;
5754 int numBasicSubobjectsInElementType (void) const;
5755 string basicSubobjectAtIndex (int index, int arraySize) const;
5756
5757 private:
5758 string m_blockName;
5759 string m_interfaceName;
5760 vector<Member> m_members;
5761 };
5762
5763 static string glslTraverseBasicTypes (const string& rootName,
5764 const glu::VarType& rootType,
5765 int arrayNestingDepth,
5766 int indentationDepth,
5767 BasicTypeVisitFunc visit);
5768
5769 static string glslAssignBasicTypeObject (const string& name, glu::DataType, int indentationDepth);
5770 static string glslCheckBasicTypeObject (const string& name, glu::DataType, int indentationDepth);
5771 static int numBasicSubobjectsInElementType (const vector<SharedPtr<TopLevelObject> >&);
5772 static string basicSubobjectAtIndex (int index, const vector<SharedPtr<TopLevelObject> >&, int topLevelArraySizes);
5773
5774 enum
5775 {
5776 RENDER_SIZE = 256
5777 };
5778 enum
5779 {
5780 NUM_OUTPUT_VERTICES = 5
5781 };
5782 enum
5783 {
5784 NUM_PER_PATCH_ARRAY_ELEMS = 3
5785 };
5786 enum
5787 {
5788 NUM_PER_PATCH_BLOCKS = 2
5789 };
5790
5791 const TessPrimitiveType m_primitiveType;
5792 const IOType m_ioType;
5793 const VertexIOArraySize m_vertexIOArraySize;
5794 const TessControlOutArraySize m_tessControlOutArraySize;
5795 const string m_referenceImagePath;
5796
5797 vector<glu::StructType> m_structTypes;
5798 vector<SharedPtr<TopLevelObject> > m_tcsOutputs;
5799 vector<SharedPtr<TopLevelObject> > m_tesInputs;
5800
5801 SharedPtr<const glu::ShaderProgram> m_program;
5802 };
5803
5804 /*--------------------------------------------------------------------*//*!
5805 * \brief Generate GLSL code to traverse (possibly aggregate) object
5806 *
5807 * Generates a string that represents GLSL code that traverses the
5808 * basic-type subobjects in a rootType-typed object named rootName. Arrays
5809 * are traversed with loops and struct members are each traversed
5810 * separately. The code for each basic-type subobject is generated with
5811 * the function given as the 'visit' argument.
5812 *//*--------------------------------------------------------------------*/
glslTraverseBasicTypes(const string & rootName,const glu::VarType & rootType,int arrayNestingDepth,int indentationDepth,BasicTypeVisitFunc visit)5813 string UserDefinedIOCase::glslTraverseBasicTypes (const string& rootName,
5814 const glu::VarType& rootType,
5815 int arrayNestingDepth,
5816 int indentationDepth,
5817 BasicTypeVisitFunc visit)
5818 {
5819 if (rootType.isBasicType())
5820 return visit(rootName, rootType.getBasicType(), indentationDepth);
5821 else if (rootType.isArrayType())
5822 {
5823 const string indentation = string(indentationDepth, '\t');
5824 const string loopIndexName = "i" + de::toString(arrayNestingDepth);
5825 const string arrayLength = de::toString(rootType.getArraySize());
5826 return indentation + "for (int " + loopIndexName + " = 0; " + loopIndexName + " < " + de::toString(rootType.getArraySize()) + "; " + loopIndexName + "++)\n" +
5827 indentation + "{\n" +
5828 glslTraverseBasicTypes(rootName + "[" + loopIndexName + "]", rootType.getElementType(), arrayNestingDepth+1, indentationDepth+1, visit) +
5829 indentation + "}\n";
5830 }
5831 else if (rootType.isStructType())
5832 {
5833 const glu::StructType& structType = *rootType.getStructPtr();
5834 const int numMembers = structType.getNumMembers();
5835 string result;
5836
5837 for (int membNdx = 0; membNdx < numMembers; membNdx++)
5838 {
5839 const glu::StructMember& member = structType.getMember(membNdx);
5840 result += glslTraverseBasicTypes(rootName + "." + member.getName(), member.getType(), arrayNestingDepth, indentationDepth, visit);
5841 }
5842
5843 return result;
5844 }
5845 else
5846 {
5847 DE_ASSERT(false);
5848 return DE_NULL;
5849 }
5850 }
5851
declare(void) const5852 string UserDefinedIOCase::Variable::declare (void) const
5853 {
5854 DE_ASSERT(!m_isArray);
5855 return de::toString(glu::declare(m_type, m_name)) + ";\n";
5856 }
5857
declareArray(const string & sizeExpr) const5858 string UserDefinedIOCase::Variable::declareArray (const string& sizeExpr) const
5859 {
5860 DE_ASSERT(m_isArray);
5861 return de::toString(glu::declare(m_type, m_name)) + "[" + sizeExpr + "];\n";
5862 }
5863
declare(void) const5864 string UserDefinedIOCase::IOBlock::declare (void) const
5865 {
5866 std::ostringstream buf;
5867
5868 buf << m_blockName << "\n"
5869 << "{\n";
5870
5871 for (int i = 0; i < (int)m_members.size(); i++)
5872 buf << "\t" << glu::declare(m_members[i].type, m_members[i].name) << ";\n";
5873
5874 buf << "} " << m_interfaceName << ";\n";
5875 return buf.str();
5876 }
5877
declareArray(const string & sizeExpr) const5878 string UserDefinedIOCase::IOBlock::declareArray (const string& sizeExpr) const
5879 {
5880 std::ostringstream buf;
5881
5882 buf << m_blockName << "\n"
5883 << "{\n";
5884
5885 for (int i = 0; i < (int)m_members.size(); i++)
5886 buf << "\t" << glu::declare(m_members[i].type, m_members[i].name) << ";\n";
5887
5888 buf << "} " << m_interfaceName << "[" << sizeExpr << "];\n";
5889 return buf.str();
5890 }
5891
glslTraverseBasicTypeArray(int numArrayElements,int indentationDepth,BasicTypeVisitFunc visit) const5892 string UserDefinedIOCase::Variable::glslTraverseBasicTypeArray (int numArrayElements, int indentationDepth, BasicTypeVisitFunc visit) const
5893 {
5894 DE_ASSERT(m_isArray);
5895
5896 const bool traverseAsArray = numArrayElements >= 0;
5897 const string traversedName = m_name + (!traverseAsArray ? "[gl_InvocationID]" : "");
5898 const glu::VarType type = traverseAsArray ? glu::VarType(m_type, numArrayElements) : m_type;
5899
5900 return UserDefinedIOCase::glslTraverseBasicTypes(traversedName, type, 0, indentationDepth, visit);
5901 }
5902
glslTraverseBasicType(int indentationDepth,BasicTypeVisitFunc visit) const5903 string UserDefinedIOCase::Variable::glslTraverseBasicType (int indentationDepth, BasicTypeVisitFunc visit) const
5904 {
5905 DE_ASSERT(!m_isArray);
5906
5907 return UserDefinedIOCase::glslTraverseBasicTypes(m_name, m_type, 0, indentationDepth, visit);
5908 }
5909
glslTraverseBasicTypeArray(int numArrayElements,int indentationDepth,BasicTypeVisitFunc visit) const5910 string UserDefinedIOCase::IOBlock::glslTraverseBasicTypeArray (int numArrayElements, int indentationDepth, BasicTypeVisitFunc visit) const
5911 {
5912 if (numArrayElements >= 0)
5913 {
5914 const string indentation = string(indentationDepth, '\t');
5915 string result = indentation + "for (int i0 = 0; i0 < " + de::toString(numArrayElements) + "; i0++)\n" +
5916 indentation + "{\n";
5917 for (int i = 0; i < (int)m_members.size(); i++)
5918 result += UserDefinedIOCase::glslTraverseBasicTypes(m_interfaceName + "[i0]." + m_members[i].name, m_members[i].type, 1, indentationDepth+1, visit);
5919 result += indentation + "}\n";
5920 return result;
5921 }
5922 else
5923 {
5924 string result;
5925 for (int i = 0; i < (int)m_members.size(); i++)
5926 result += UserDefinedIOCase::glslTraverseBasicTypes(m_interfaceName + "[gl_InvocationID]." + m_members[i].name, m_members[i].type, 0, indentationDepth, visit);
5927 return result;
5928 }
5929 }
5930
5931
glslTraverseBasicType(int indentationDepth,BasicTypeVisitFunc visit) const5932 string UserDefinedIOCase::IOBlock::glslTraverseBasicType (int indentationDepth, BasicTypeVisitFunc visit) const
5933 {
5934 string result;
5935 for (int i = 0; i < (int)m_members.size(); i++)
5936 result += UserDefinedIOCase::glslTraverseBasicTypes(m_interfaceName + "." + m_members[i].name, m_members[i].type, 0, indentationDepth, visit);
5937 return result;
5938 }
5939
numBasicSubobjectsInElementType(void) const5940 int UserDefinedIOCase::Variable::numBasicSubobjectsInElementType (void) const
5941 {
5942 return numBasicSubobjects(m_type);
5943 }
5944
numBasicSubobjectsInElementType(void) const5945 int UserDefinedIOCase::IOBlock::numBasicSubobjectsInElementType (void) const
5946 {
5947 int result = 0;
5948 for (int i = 0; i < (int)m_members.size(); i++)
5949 result += numBasicSubobjects(m_members[i].type);
5950 return result;
5951 }
5952
basicSubobjectAtIndex(int subobjectIndex,int arraySize) const5953 string UserDefinedIOCase::Variable::basicSubobjectAtIndex (int subobjectIndex, int arraySize) const
5954 {
5955 const glu::VarType type = m_isArray ? glu::VarType(m_type, arraySize) : m_type;
5956 int currentIndex = 0;
5957
5958 for (glu::BasicTypeIterator basicIt = glu::BasicTypeIterator::begin(&type);
5959 basicIt != glu::BasicTypeIterator::end(&type);
5960 ++basicIt)
5961 {
5962 if (currentIndex == subobjectIndex)
5963 return m_name + de::toString(glu::TypeAccessFormat(type, basicIt.getPath()));
5964 currentIndex++;
5965 }
5966 DE_ASSERT(false);
5967 return DE_NULL;
5968 }
5969
basicSubobjectAtIndex(int subobjectIndex,int arraySize) const5970 string UserDefinedIOCase::IOBlock::basicSubobjectAtIndex (int subobjectIndex, int arraySize) const
5971 {
5972 int currentIndex = 0;
5973 for (int arrayNdx = 0; arrayNdx < arraySize; arrayNdx++)
5974 {
5975 for (int memberNdx = 0; memberNdx < (int)m_members.size(); memberNdx++)
5976 {
5977 const glu::VarType& membType = m_members[memberNdx].type;
5978 for (glu::BasicTypeIterator basicIt = glu::BasicTypeIterator::begin(&membType);
5979 basicIt != glu::BasicTypeIterator::end(&membType);
5980 ++basicIt)
5981 {
5982 if (currentIndex == subobjectIndex)
5983 return m_interfaceName + "[" + de::toString(arrayNdx) + "]." + m_members[memberNdx].name + de::toString(glu::TypeAccessFormat(membType, basicIt.getPath()));
5984 currentIndex++;
5985 }
5986 }
5987 }
5988 DE_ASSERT(false);
5989 return DE_NULL;
5990 }
5991
5992 // Used as the 'visit' argument for glslTraverseBasicTypes.
glslAssignBasicTypeObject(const string & name,glu::DataType type,int indentationDepth)5993 string UserDefinedIOCase::glslAssignBasicTypeObject (const string& name, glu::DataType type, int indentationDepth)
5994 {
5995 const int scalarSize = glu::getDataTypeScalarSize(type);
5996 const string indentation = string(indentationDepth, '\t');
5997 string result;
5998
5999 result += indentation + name + " = ";
6000
6001 if (type != glu::TYPE_FLOAT)
6002 result += string() + glu::getDataTypeName(type) + "(";
6003 for (int i = 0; i < scalarSize; i++)
6004 result += (i > 0 ? ", v+" + de::floatToString(0.8f*(float)i, 1)
6005 : "v");
6006 if (type != glu::TYPE_FLOAT)
6007 result += ")";
6008 result += ";\n" +
6009 indentation + "v += 0.4;\n";
6010 return result;
6011 }
6012
6013 // Used as the 'visit' argument for glslTraverseBasicTypes.
glslCheckBasicTypeObject(const string & name,glu::DataType type,int indentationDepth)6014 string UserDefinedIOCase::glslCheckBasicTypeObject (const string& name, glu::DataType type, int indentationDepth)
6015 {
6016 const int scalarSize = glu::getDataTypeScalarSize(type);
6017 const string indentation = string(indentationDepth, '\t');
6018 string result;
6019
6020 result += indentation + "allOk = allOk && compare_" + glu::getDataTypeName(type) + "(" + name + ", ";
6021
6022 if (type != glu::TYPE_FLOAT)
6023 result += string() + glu::getDataTypeName(type) + "(";
6024 for (int i = 0; i < scalarSize; i++)
6025 result += (i > 0 ? ", v+" + de::floatToString(0.8f*(float)i, 1)
6026 : "v");
6027 if (type != glu::TYPE_FLOAT)
6028 result += ")";
6029 result += ");\n" +
6030 indentation + "v += 0.4;\n" +
6031 indentation + "if (allOk) firstFailedInputIndex++;\n";
6032
6033 return result;
6034 }
6035
numBasicSubobjectsInElementType(const vector<SharedPtr<TopLevelObject>> & objects)6036 int UserDefinedIOCase::numBasicSubobjectsInElementType (const vector<SharedPtr<TopLevelObject> >& objects)
6037 {
6038 int result = 0;
6039 for (int i = 0; i < (int)objects.size(); i++)
6040 result += objects[i]->numBasicSubobjectsInElementType();
6041 return result;
6042 }
6043
basicSubobjectAtIndex(int subobjectIndex,const vector<SharedPtr<TopLevelObject>> & objects,int topLevelArraySize)6044 string UserDefinedIOCase::basicSubobjectAtIndex (int subobjectIndex, const vector<SharedPtr<TopLevelObject> >& objects, int topLevelArraySize)
6045 {
6046 int currentIndex = 0;
6047 int objectIndex = 0;
6048 for (; currentIndex < subobjectIndex; objectIndex++)
6049 currentIndex += objects[objectIndex]->numBasicSubobjectsInElementType() * topLevelArraySize;
6050 if (currentIndex > subobjectIndex)
6051 {
6052 objectIndex--;
6053 currentIndex -= objects[objectIndex]->numBasicSubobjectsInElementType() * topLevelArraySize;
6054 }
6055
6056 return objects[objectIndex]->basicSubobjectAtIndex(subobjectIndex - currentIndex, topLevelArraySize);
6057 }
6058
init(void)6059 void UserDefinedIOCase::init (void)
6060 {
6061 checkTessellationSupport(m_context);
6062 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
6063
6064 const bool isPerPatchIO = m_ioType == IO_TYPE_PER_PATCH ||
6065 m_ioType == IO_TYPE_PER_PATCH_ARRAY ||
6066 m_ioType == IO_TYPE_PER_PATCH_BLOCK ||
6067 m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY;
6068
6069 const bool isExplicitVertexArraySize = m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN ||
6070 m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY;
6071
6072 const string vertexAttrArrayInputSize = m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_IMPLICIT ? ""
6073 : m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN ? "gl_MaxPatchVertices"
6074 : m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY ? de::toString(m_context.getContextInfo().getInt(GL_MAX_PATCH_VERTICES))
6075 : DE_NULL;
6076
6077 const char* const maybePatch = isPerPatchIO ? "patch " : "";
6078 const string outMaybePatch = string() + maybePatch + "out ";
6079 const string inMaybePatch = string() + maybePatch + "in ";
6080 const bool useBlock = m_ioType == IO_TYPE_PER_VERTEX_BLOCK ||
6081 m_ioType == IO_TYPE_PER_PATCH_BLOCK ||
6082 m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY;
6083
6084 string tcsDeclarations;
6085 string tcsStatements;
6086
6087 string tesDeclarations;
6088 string tesStatements;
6089
6090 {
6091 m_structTypes.push_back(glu::StructType("S"));
6092
6093 const glu::VarType highpFloat (glu::TYPE_FLOAT, glu::PRECISION_HIGHP);
6094 glu::StructType& structType = m_structTypes.back();
6095 const glu::VarType structVarType (&structType);
6096 bool usedStruct = false;
6097
6098 structType.addMember("x", glu::VarType(glu::TYPE_INT, glu::PRECISION_HIGHP));
6099 structType.addMember("y", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP));
6100
6101 if (useBlock)
6102 {
6103 // It is illegal to have a structure containing an array as an output variable
6104 structType.addMember("z", glu::VarType(highpFloat, 2));
6105 }
6106
6107 if (useBlock)
6108 {
6109 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)
6110 vector<IOBlock::Member> blockMembers;
6111
6112 if (!useLightweightBlock)
6113 blockMembers.push_back(IOBlock::Member("blockS", structVarType));
6114
6115 blockMembers.push_back(IOBlock::Member("blockFa", glu::VarType(highpFloat, 3)));
6116 blockMembers.push_back(IOBlock::Member("blockSa", glu::VarType(structVarType, 2)));
6117 blockMembers.push_back(IOBlock::Member("blockF", highpFloat));
6118
6119 m_tcsOutputs.push_back (SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "tcBlock", blockMembers)));
6120 m_tesInputs.push_back (SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "teBlock", blockMembers)));
6121
6122 usedStruct = true;
6123 }
6124 else
6125 {
6126 const Variable var0("in_te_s", structVarType, m_ioType != IO_TYPE_PER_PATCH);
6127 const Variable var1("in_te_f", highpFloat, m_ioType != IO_TYPE_PER_PATCH);
6128
6129 if (m_ioType != IO_TYPE_PER_PATCH_ARRAY)
6130 {
6131 // Arrays of structures are disallowed, add struct cases only if not arrayed variable
6132 m_tcsOutputs.push_back (SharedPtr<TopLevelObject>(new Variable(var0)));
6133 m_tesInputs.push_back (SharedPtr<TopLevelObject>(new Variable(var0)));
6134
6135 usedStruct = true;
6136 }
6137
6138 m_tcsOutputs.push_back (SharedPtr<TopLevelObject>(new Variable(var1)));
6139 m_tesInputs.push_back (SharedPtr<TopLevelObject>(new Variable(var1)));
6140 }
6141
6142 tcsDeclarations += "in " + Variable("in_tc_attr", highpFloat, true).declareArray(vertexAttrArrayInputSize);
6143
6144 if (usedStruct)
6145 tcsDeclarations += de::toString(glu::declare(structType)) + ";\n";
6146
6147 tcsStatements += "\t{\n"
6148 "\t\thighp float v = 1.3;\n";
6149
6150 for (int tcsOutputNdx = 0; tcsOutputNdx < (int)m_tcsOutputs.size(); tcsOutputNdx++)
6151 {
6152 const TopLevelObject& output = *m_tcsOutputs[tcsOutputNdx];
6153 const int numElements = !isPerPatchIO ? -1 //!< \note -1 means indexing with gl_InstanceID
6154 : m_ioType == IO_TYPE_PER_PATCH ? 1
6155 : m_ioType == IO_TYPE_PER_PATCH_ARRAY ? NUM_PER_PATCH_ARRAY_ELEMS
6156 : m_ioType == IO_TYPE_PER_PATCH_BLOCK ? 1
6157 : m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? NUM_PER_PATCH_BLOCKS
6158 : -2;
6159 const bool isArray = (numElements != 1);
6160
6161 DE_ASSERT(numElements != -2);
6162
6163 if (isArray)
6164 {
6165 tcsDeclarations += outMaybePatch + output.declareArray(m_ioType == IO_TYPE_PER_PATCH_ARRAY ? de::toString(int(NUM_PER_PATCH_ARRAY_ELEMS))
6166 : m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? de::toString(int(NUM_PER_PATCH_BLOCKS))
6167 : m_tessControlOutArraySize == TESS_CONTROL_OUT_ARRAY_SIZE_LAYOUT ? de::toString(int(NUM_OUTPUT_VERTICES))
6168 : m_tessControlOutArraySize == TESS_CONTROL_OUT_ARRAY_SIZE_QUERY ? de::toString(m_context.getContextInfo().getInt(GL_MAX_PATCH_VERTICES))
6169 : m_tessControlOutArraySize == TESS_CONTROL_OUT_ARRAY_SIZE_SHADER_BUILTIN ? "gl_MaxPatchVertices"
6170 : "");
6171 }
6172 else
6173 tcsDeclarations += outMaybePatch + output.declare();
6174
6175 if (!isPerPatchIO)
6176 tcsStatements += "\t\tv += float(gl_InvocationID)*" + de::floatToString(0.4f * (float)output.numBasicSubobjectsInElementType(), 1) + ";\n";
6177
6178 tcsStatements += "\n\t\t// Assign values to output " + output.name() + "\n";
6179 if (isArray)
6180 tcsStatements += output.glslTraverseBasicTypeArray(numElements, 2, glslAssignBasicTypeObject);
6181 else
6182 tcsStatements += output.glslTraverseBasicType(2, glslAssignBasicTypeObject);
6183
6184 if (!isPerPatchIO)
6185 tcsStatements += "\t\tv += float(" + de::toString(int(NUM_OUTPUT_VERTICES)) + "-gl_InvocationID-1)*" + de::floatToString(0.4f * (float)output.numBasicSubobjectsInElementType(), 1) + ";\n";
6186 }
6187 tcsStatements += "\t}\n";
6188
6189 if (usedStruct)
6190 tesDeclarations += de::toString(glu::declare(structType)) + ";\n";
6191
6192 tesStatements += "\tbool allOk = true;\n"
6193 "\thighp uint firstFailedInputIndex = 0u;\n"
6194 "\t{\n"
6195 "\t\thighp float v = 1.3;\n";
6196 for (int tesInputNdx = 0; tesInputNdx < (int)m_tesInputs.size(); tesInputNdx++)
6197 {
6198 const TopLevelObject& input = *m_tesInputs[tesInputNdx];
6199 const int numElements = !isPerPatchIO ? (int)NUM_OUTPUT_VERTICES
6200 : m_ioType == IO_TYPE_PER_PATCH ? 1
6201 : m_ioType == IO_TYPE_PER_PATCH_BLOCK ? 1
6202 : m_ioType == IO_TYPE_PER_PATCH_ARRAY ? NUM_PER_PATCH_ARRAY_ELEMS
6203 : m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? NUM_PER_PATCH_BLOCKS
6204 : -2;
6205 const bool isArray = (numElements != 1);
6206
6207 DE_ASSERT(numElements != -2);
6208
6209 if (isArray)
6210 tesDeclarations += inMaybePatch + input.declareArray(m_ioType == IO_TYPE_PER_PATCH_ARRAY ? de::toString(int(NUM_PER_PATCH_ARRAY_ELEMS))
6211 : m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? de::toString(int(NUM_PER_PATCH_BLOCKS))
6212 : isExplicitVertexArraySize ? de::toString(vertexAttrArrayInputSize)
6213 : "");
6214 else
6215 tesDeclarations += inMaybePatch + input.declare();
6216
6217 tesStatements += "\n\t\t// Check values in input " + input.name() + "\n";
6218 if (isArray)
6219 tesStatements += input.glslTraverseBasicTypeArray(numElements, 2, glslCheckBasicTypeObject);
6220 else
6221 tesStatements += input.glslTraverseBasicType(2, glslCheckBasicTypeObject);
6222 }
6223 tesStatements += "\t}\n";
6224 }
6225
6226 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
6227 "${GLSL_PER_VERTEX_OUT}\n"
6228 "\n"
6229 "in highp float in_v_attr;\n"
6230 "out highp float in_tc_attr;\n"
6231 "\n"
6232 "void main (void)\n"
6233 "{\n"
6234 " in_tc_attr = in_v_attr;\n"
6235 "}\n");
6236 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
6237 "${TESSELLATION_SHADER_REQUIRE}\n"
6238 "${GLSL_PER_VERTEX_IN_ARR}\n"
6239 "${GLSL_PER_VERTEX_OUT_ARR}\n"
6240 "\n"
6241 "layout (vertices = " + de::toString(int(NUM_OUTPUT_VERTICES)) + ") out;\n"
6242 "\n"
6243 + tcsDeclarations +
6244 "\n"
6245 "patch out highp vec2 in_te_positionScale;\n"
6246 "patch out highp vec2 in_te_positionOffset;\n"
6247 "\n"
6248 "void main (void)\n"
6249 "{\n"
6250 + tcsStatements +
6251 "\n"
6252 " in_te_positionScale = vec2(in_tc_attr[6], in_tc_attr[7]);\n"
6253 " in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\n"
6254 "\n"
6255 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
6256 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
6257 "\n"
6258 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
6259 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
6260 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
6261 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
6262 "}\n");
6263 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
6264 "${TESSELLATION_SHADER_REQUIRE}\n"
6265 "${GLSL_PER_VERTEX_IN_ARR}\n"
6266 "${GLSL_PER_VERTEX_OUT}\n"
6267 "\n"
6268 + getTessellationEvaluationInLayoutString(m_primitiveType) +
6269 "\n"
6270 + tesDeclarations +
6271 "\n"
6272 "patch in highp vec2 in_te_positionScale;\n"
6273 "patch in highp vec2 in_te_positionOffset;\n"
6274 "\n"
6275 "out highp vec4 in_f_color;\n"
6276 "// Will contain the index of the first incorrect input,\n"
6277 "// or the number of inputs if all are correct\n"
6278 "flat out highp uint out_te_firstFailedInputIndex;\n"
6279 "\n"
6280 "bool compare_int (int a, int b) { return a == b; }\n"
6281 "bool compare_float (float a, float b) { return abs(a - b) < 0.01f; }\n"
6282 "bool compare_vec4 (vec4 a, vec4 b) { return all(lessThan(abs(a - b), vec4(0.01f))); }\n"
6283 "\n"
6284 "void main (void)\n"
6285 "{\n"
6286 + tesStatements +
6287 "\n"
6288 " gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n"
6289 " in_f_color = allOk ? vec4(0.0, 1.0, 0.0, 1.0)\n"
6290 " : vec4(1.0, 0.0, 0.0, 1.0);\n"
6291 " out_te_firstFailedInputIndex = firstFailedInputIndex;\n"
6292 "}\n");
6293 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
6294 "\n"
6295 "layout (location = 0) out mediump vec4 o_color;\n"
6296 "\n"
6297 "in highp vec4 in_f_color;\n"
6298 "\n"
6299 "void main (void)\n"
6300 "{\n"
6301 " o_color = in_f_color;\n"
6302 "}\n");
6303
6304 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
6305 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
6306 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
6307 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
6308 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
6309 << glu::TransformFeedbackVarying ("out_te_firstFailedInputIndex")
6310 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
6311
6312 m_testCtx.getLog() << *m_program;
6313 if (!m_program->isOk())
6314 TCU_FAIL("Program compilation failed");
6315 }
6316
deinit(void)6317 void UserDefinedIOCase::deinit (void)
6318 {
6319 m_program.clear();
6320 }
6321
iterate(void)6322 UserDefinedIOCase::IterateResult UserDefinedIOCase::iterate (void)
6323 {
6324 typedef TransformFeedbackHandler<deUint32> TFHandler;
6325
6326 TestLog& log = m_testCtx.getLog();
6327 const RenderContext& renderCtx = m_context.getRenderContext();
6328 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
6329 const glw::Functions& gl = renderCtx.getFunctions();
6330 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 };
6331 const deUint32 programGL = m_program->getProgram();
6332 const int numVertices = referenceVertexCount(m_primitiveType, SPACINGMODE_EQUAL, false, &attributes[0], &attributes[2]);
6333 const TFHandler tfHandler (renderCtx, numVertices);
6334 tcu::ResultCollector result;
6335
6336 gl.useProgram(programGL);
6337 setViewport(gl, viewport);
6338 gl.patchParameteri(GL_PATCH_VERTICES, DE_LENGTH_OF_ARRAY(attributes));
6339
6340 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
6341 gl.clear(GL_COLOR_BUFFER_BIT);
6342
6343 {
6344 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, DE_LENGTH_OF_ARRAY(attributes), 0, &attributes[0]) };
6345 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, false),
6346 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], DE_LENGTH_OF_ARRAY(attributes));
6347
6348 {
6349 const tcu::Surface pixels = getPixels(renderCtx, viewport);
6350 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath.c_str());
6351 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), pixels.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
6352
6353 if (!success)
6354 result.fail("Image comparison failed");
6355 }
6356
6357 if ((int)tfResult.varying.size() != numVertices)
6358 {
6359 log << TestLog::Message << "Failure: transform feedback returned " << tfResult.varying.size() << " vertices; expected " << numVertices << TestLog::EndMessage;
6360 result.fail("Wrong number of vertices");
6361 }
6362 else
6363 {
6364 const int topLevelArraySize = (m_ioType == IO_TYPE_PER_PATCH ? 1
6365 : m_ioType == IO_TYPE_PER_PATCH_ARRAY ? NUM_PER_PATCH_ARRAY_ELEMS
6366 : m_ioType == IO_TYPE_PER_PATCH_BLOCK ? 1
6367 : m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? NUM_PER_PATCH_BLOCKS
6368 : (int)NUM_OUTPUT_VERTICES);
6369 const int numTEInputs = numBasicSubobjectsInElementType(m_tesInputs) * topLevelArraySize;
6370
6371 for (int vertexNdx = 0; vertexNdx < (int)numVertices; vertexNdx++)
6372 {
6373 if (tfResult.varying[vertexNdx] > (deUint32)numTEInputs)
6374 {
6375 log << TestLog::Message << "Failure: out_te_firstFailedInputIndex has value " << tfResult.varying[vertexNdx]
6376 << ", should be in range [0, " << numTEInputs << "]" << TestLog::EndMessage;
6377 result.fail("Invalid transform feedback output");
6378 }
6379 else if (tfResult.varying[vertexNdx] != (deUint32)numTEInputs)
6380 {
6381 log << TestLog::Message << "Failure: in tessellation evaluation shader, check for input "
6382 << basicSubobjectAtIndex(tfResult.varying[vertexNdx], m_tesInputs, topLevelArraySize) << " failed" << TestLog::EndMessage;
6383 result.fail("Invalid input value in tessellation evaluation shader");
6384 }
6385 }
6386 }
6387 }
6388
6389 result.setTestContextResult(m_testCtx);
6390 return STOP;
6391 }
6392
6393 /*--------------------------------------------------------------------*//*!
6394 * \brief Pass gl_Position between VS and TCS, or between TCS and TES.
6395 *
6396 * In TCS gl_Position is in the gl_out[] block and in TES in the gl_in[]
6397 * block, and has no special semantics in those. Arbitrary vec4 data can
6398 * thus be passed there.
6399 *//*--------------------------------------------------------------------*/
6400 class GLPositionCase : public TestCase
6401 {
6402 public:
6403 enum CaseType
6404 {
6405 CASETYPE_VS_TO_TCS = 0,
6406 CASETYPE_TCS_TO_TES,
6407 CASETYPE_VS_TO_TCS_TO_TES,
6408
6409 CASETYPE_LAST
6410 };
6411
GLPositionCase(Context & context,const char * name,const char * description,CaseType caseType,const char * referenceImagePath)6412 GLPositionCase (Context& context, const char* name, const char* description, CaseType caseType, const char* referenceImagePath)
6413 : TestCase (context, name, description)
6414 , m_caseType (caseType)
6415 , m_referenceImagePath (referenceImagePath)
6416 {
6417 }
6418
6419 void init (void);
6420 void deinit (void);
6421 IterateResult iterate (void);
6422
6423 static const char* getCaseTypeName (CaseType type);
6424
6425 private:
6426 static const int RENDER_SIZE = 256;
6427
6428 const CaseType m_caseType;
6429 const string m_referenceImagePath;
6430
6431 SharedPtr<const glu::ShaderProgram> m_program;
6432 };
6433
getCaseTypeName(CaseType type)6434 const char* GLPositionCase::getCaseTypeName (CaseType type)
6435 {
6436 switch (type)
6437 {
6438 case CASETYPE_VS_TO_TCS: return "gl_position_vs_to_tcs";
6439 case CASETYPE_TCS_TO_TES: return "gl_position_tcs_to_tes";
6440 case CASETYPE_VS_TO_TCS_TO_TES: return "gl_position_vs_to_tcs_to_tes";
6441 default:
6442 DE_ASSERT(false); return DE_NULL;
6443 }
6444 }
6445
init(void)6446 void GLPositionCase::init (void)
6447 {
6448 checkTessellationSupport(m_context);
6449 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
6450
6451 const bool vsToTCS = m_caseType == CASETYPE_VS_TO_TCS || m_caseType == CASETYPE_VS_TO_TCS_TO_TES;
6452 const bool tcsToTES = m_caseType == CASETYPE_TCS_TO_TES || m_caseType == CASETYPE_VS_TO_TCS_TO_TES;
6453
6454 const string tesIn0 = tcsToTES ? "gl_in[0].gl_Position" : "in_te_attr[0]";
6455 const string tesIn1 = tcsToTES ? "gl_in[1].gl_Position" : "in_te_attr[1]";
6456 const string tesIn2 = tcsToTES ? "gl_in[2].gl_Position" : "in_te_attr[2]";
6457
6458 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
6459 "${GLSL_PER_VERTEX_OUT}\n"
6460 "\n"
6461 "in highp vec4 in_v_attr;\n"
6462 + string(!vsToTCS ? "out highp vec4 in_tc_attr;\n" : "") +
6463 "\n"
6464 "void main (void)\n"
6465 "{\n"
6466 " " + (vsToTCS ? "gl_Position" : "in_tc_attr") + " = in_v_attr;\n"
6467 "}\n");
6468 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
6469 "${TESSELLATION_SHADER_REQUIRE}\n"
6470 "${GLSL_PER_VERTEX_IN_ARR}\n"
6471 "${GLSL_PER_VERTEX_OUT_ARR}\n"
6472 "\n"
6473 "layout (vertices = 3) out;\n"
6474 "\n"
6475 + string(!vsToTCS ? "in highp vec4 in_tc_attr[];\n" : "") +
6476 "\n"
6477 + (!tcsToTES ? "out highp vec4 in_te_attr[];\n" : "") +
6478 "\n"
6479 "void main (void)\n"
6480 "{\n"
6481 " " + (tcsToTES ? "gl_out[gl_InvocationID].gl_Position" : "in_te_attr[gl_InvocationID]") + " = "
6482 + (vsToTCS ? "gl_in[gl_InvocationID].gl_Position" : "in_tc_attr[gl_InvocationID]") + ";\n"
6483 "\n"
6484 " gl_TessLevelInner[0] = 2.0;\n"
6485 " gl_TessLevelInner[1] = 3.0;\n"
6486 "\n"
6487 " gl_TessLevelOuter[0] = 4.0;\n"
6488 " gl_TessLevelOuter[1] = 5.0;\n"
6489 " gl_TessLevelOuter[2] = 6.0;\n"
6490 " gl_TessLevelOuter[3] = 7.0;\n"
6491 "}\n");
6492 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
6493 "${TESSELLATION_SHADER_REQUIRE}\n"
6494 "${GLSL_PER_VERTEX_IN_ARR}\n"
6495 "${GLSL_PER_VERTEX_OUT}\n"
6496 "\n"
6497 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_TRIANGLES) +
6498 "\n"
6499 + (!tcsToTES ? "in highp vec4 in_te_attr[];\n" : "") +
6500 "\n"
6501 "out highp vec4 in_f_color;\n"
6502 "\n"
6503 "void main (void)\n"
6504 "{\n"
6505 " highp vec2 xy = gl_TessCoord.x * " + tesIn0 + ".xy\n"
6506 " + gl_TessCoord.y * " + tesIn1 + ".xy\n"
6507 " + gl_TessCoord.z * " + tesIn2 + ".xy;\n"
6508 " gl_Position = vec4(xy, 0.0, 1.0);\n"
6509 " in_f_color = vec4(" + tesIn0 + ".z + " + tesIn1 + ".w,\n"
6510 " " + tesIn2 + ".z + " + tesIn0 + ".w,\n"
6511 " " + tesIn1 + ".z + " + tesIn2 + ".w,\n"
6512 " 1.0);\n"
6513 "}\n");
6514 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
6515 "\n"
6516 "layout (location = 0) out mediump vec4 o_color;\n"
6517 "\n"
6518 "in highp vec4 in_f_color;\n"
6519 "\n"
6520 "void main (void)\n"
6521 "{\n"
6522 " o_color = in_f_color;\n"
6523 "}\n");
6524
6525 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
6526 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
6527 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
6528 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
6529 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))));
6530
6531 m_testCtx.getLog() << *m_program;
6532 if (!m_program->isOk())
6533 TCU_FAIL("Program compilation failed");
6534 }
6535
deinit(void)6536 void GLPositionCase::deinit (void)
6537 {
6538 m_program.clear();
6539 }
6540
iterate(void)6541 GLPositionCase::IterateResult GLPositionCase::iterate (void)
6542 {
6543 TestLog& log = m_testCtx.getLog();
6544 const RenderContext& renderCtx = m_context.getRenderContext();
6545 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
6546 const glw::Functions& gl = renderCtx.getFunctions();
6547 const deUint32 programGL = m_program->getProgram();
6548
6549 static const float attributes[3*4] =
6550 {
6551 -0.8f, -0.7f, 0.1f, 0.7f,
6552 -0.5f, 0.4f, 0.2f, 0.5f,
6553 0.3f, 0.2f, 0.3f, 0.45f
6554 };
6555
6556 gl.useProgram(programGL);
6557 setViewport(gl, viewport);
6558 gl.patchParameteri(GL_PATCH_VERTICES, 3);
6559
6560 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
6561 gl.clear(GL_COLOR_BUFFER_BIT);
6562
6563 log << TestLog::Message << "Note: input data for in_v_attr:\n" << arrayStr(attributes, 4) << TestLog::EndMessage;
6564
6565 {
6566 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 4, 3, 0, &attributes[0]) };
6567 glu::draw(renderCtx, programGL, DE_LENGTH_OF_ARRAY(bindings), &bindings[0], glu::pr::Patches(3));
6568
6569 {
6570 const tcu::Surface pixels = getPixels(renderCtx, viewport);
6571 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath.c_str());
6572 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), pixels.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
6573
6574 if (!success)
6575 {
6576 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
6577 return STOP;
6578 }
6579 }
6580 }
6581
6582 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
6583 return STOP;
6584 }
6585
6586 class LimitQueryCase : public TestCase
6587 {
6588 public:
6589 LimitQueryCase (Context& context, const char* name, const char* desc, glw::GLenum target, int minValue);
6590 private:
6591 IterateResult iterate (void);
6592
6593 const glw::GLenum m_target;
6594 const int m_minValue;
6595 };
6596
LimitQueryCase(Context & context,const char * name,const char * desc,glw::GLenum target,int minValue)6597 LimitQueryCase::LimitQueryCase (Context& context, const char* name, const char* desc, glw::GLenum target, int minValue)
6598 : TestCase (context, name, desc)
6599 , m_target (target)
6600 , m_minValue (minValue)
6601 {
6602 }
6603
iterate(void)6604 LimitQueryCase::IterateResult LimitQueryCase::iterate (void)
6605 {
6606 checkTessellationSupport(m_context);
6607
6608 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6609 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6610
6611 gl.enableLogging(true);
6612 verifyStateIntegerMin(result, gl, m_target, m_minValue, QUERY_INTEGER);
6613
6614 {
6615 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Types", "Alternative queries");
6616 verifyStateIntegerMin(result, gl, m_target, m_minValue, QUERY_BOOLEAN);
6617 verifyStateIntegerMin(result, gl, m_target, m_minValue, QUERY_INTEGER64);
6618 verifyStateIntegerMin(result, gl, m_target, m_minValue, QUERY_FLOAT);
6619 }
6620
6621 result.setTestContextResult(m_testCtx);
6622 return STOP;
6623 }
6624
6625 class CombinedUniformLimitCase : public TestCase
6626 {
6627 public:
6628 CombinedUniformLimitCase (Context& context, const char* name, const char* desc, glw::GLenum combined, glw::GLenum numBlocks, glw::GLenum defaultComponents);
6629 private:
6630 IterateResult iterate (void);
6631
6632 const glw::GLenum m_combined;
6633 const glw::GLenum m_numBlocks;
6634 const glw::GLenum m_defaultComponents;
6635 };
6636
CombinedUniformLimitCase(Context & context,const char * name,const char * desc,glw::GLenum combined,glw::GLenum numBlocks,glw::GLenum defaultComponents)6637 CombinedUniformLimitCase::CombinedUniformLimitCase (Context& context, const char* name, const char* desc, glw::GLenum combined, glw::GLenum numBlocks, glw::GLenum defaultComponents)
6638 : TestCase (context, name, desc)
6639 , m_combined (combined)
6640 , m_numBlocks (numBlocks)
6641 , m_defaultComponents (defaultComponents)
6642 {
6643 }
6644
iterate(void)6645 CombinedUniformLimitCase::IterateResult CombinedUniformLimitCase::iterate (void)
6646 {
6647 checkTessellationSupport(m_context);
6648
6649 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6650 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6651
6652 gl.enableLogging(true);
6653
6654 m_testCtx.getLog() << tcu::TestLog::Message
6655 << "The minimum value of " << glu::getGettableStateStr(m_combined)
6656 << " is " << glu::getGettableStateStr(m_numBlocks)
6657 << " x MAX_UNIFORM_BLOCK_SIZE / 4 + "
6658 << glu::getGettableStateStr(m_defaultComponents)
6659 << tcu::TestLog::EndMessage;
6660
6661 StateQueryMemoryWriteGuard<glw::GLint> maxUniformBlocks;
6662 gl.glGetIntegerv(m_numBlocks, &maxUniformBlocks);
6663 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "glGetIntegerv");
6664
6665 StateQueryMemoryWriteGuard<glw::GLint> maxUniformBlockSize;
6666 gl.glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUniformBlockSize);
6667 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "glGetIntegerv");
6668
6669 StateQueryMemoryWriteGuard<glw::GLint> maxUniformComponents;
6670 gl.glGetIntegerv(m_defaultComponents, &maxUniformComponents);
6671 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "glGetIntegerv");
6672
6673 if (maxUniformBlocks.verifyValidity(result) && maxUniformBlockSize.verifyValidity(result) && maxUniformComponents.verifyValidity(result))
6674 {
6675 const int limit = ((int)maxUniformBlocks) * ((int)maxUniformBlockSize) / 4 + (int)maxUniformComponents;
6676 verifyStateIntegerMin(result, gl, m_combined, limit, QUERY_INTEGER);
6677
6678 {
6679 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Types", "Alternative queries");
6680 verifyStateIntegerMin(result, gl, m_combined, limit, QUERY_BOOLEAN);
6681 verifyStateIntegerMin(result, gl, m_combined, limit, QUERY_INTEGER64);
6682 verifyStateIntegerMin(result, gl, m_combined, limit, QUERY_FLOAT);
6683 }
6684 }
6685
6686 result.setTestContextResult(m_testCtx);
6687 return STOP;
6688 }
6689
6690 class PatchVerticesStateCase : public TestCase
6691 {
6692 public:
6693 PatchVerticesStateCase (Context& context, const char* name, const char* desc);
6694 private:
6695 IterateResult iterate (void);
6696 };
6697
PatchVerticesStateCase(Context & context,const char * name,const char * desc)6698 PatchVerticesStateCase::PatchVerticesStateCase (Context& context, const char* name, const char* desc)
6699 : TestCase(context, name, desc)
6700 {
6701 }
6702
iterate(void)6703 PatchVerticesStateCase::IterateResult PatchVerticesStateCase::iterate (void)
6704 {
6705 checkTessellationSupport(m_context);
6706
6707 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6708 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6709
6710 gl.enableLogging(true);
6711
6712 // initial
6713 {
6714 const tcu::ScopedLogSection section(m_testCtx.getLog(), "initial", "Initial value");
6715
6716 verifyStateInteger(result, gl, GL_PATCH_VERTICES, 3, QUERY_INTEGER);
6717 }
6718
6719 // bind
6720 {
6721 const tcu::ScopedLogSection section(m_testCtx.getLog(), "set", "After set");
6722
6723 gl.glPatchParameteri(GL_PATCH_VERTICES, 22);
6724 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "glPatchParameteri");
6725
6726 verifyStateInteger(result, gl, GL_PATCH_VERTICES, 22, QUERY_INTEGER);
6727 {
6728 const tcu::ScopedLogSection subsection(m_testCtx.getLog(), "Types", "Alternative queries");
6729 verifyStateIntegerMin(result, gl, GL_PATCH_VERTICES, 22, QUERY_BOOLEAN);
6730 verifyStateIntegerMin(result, gl, GL_PATCH_VERTICES, 22, QUERY_INTEGER64);
6731 verifyStateIntegerMin(result, gl, GL_PATCH_VERTICES, 22, QUERY_FLOAT);
6732 }
6733 }
6734
6735 result.setTestContextResult(m_testCtx);
6736 return STOP;
6737 }
6738
6739 class PrimitiveRestartForPatchesSupportedCase : public TestCase
6740 {
6741 public:
6742 PrimitiveRestartForPatchesSupportedCase (Context& context, const char* name, const char* desc);
6743 private:
6744 IterateResult iterate (void);
6745 };
6746
PrimitiveRestartForPatchesSupportedCase(Context & context,const char * name,const char * desc)6747 PrimitiveRestartForPatchesSupportedCase::PrimitiveRestartForPatchesSupportedCase (Context& context, const char* name, const char* desc)
6748 : TestCase(context, name, desc)
6749 {
6750 }
6751
iterate(void)6752 PrimitiveRestartForPatchesSupportedCase::IterateResult PrimitiveRestartForPatchesSupportedCase::iterate (void)
6753 {
6754 checkTessellationSupport(m_context);
6755
6756 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6757 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6758 QueriedState state;
6759
6760 gl.enableLogging(true);
6761
6762 queryState(result, gl, QUERY_BOOLEAN, GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED, state);
6763
6764 if (!state.isUndefined())
6765 {
6766 const tcu::ScopedLogSection subsection(m_testCtx.getLog(), "Types", "Alternative types");
6767 verifyStateBoolean(result, gl, GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED, state.getBoolAccess(), QUERY_INTEGER);
6768 verifyStateBoolean(result, gl, GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED, state.getBoolAccess(), QUERY_INTEGER64);
6769 verifyStateBoolean(result, gl, GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED, state.getBoolAccess(), QUERY_FLOAT);
6770 }
6771
6772 result.setTestContextResult(m_testCtx);
6773 return STOP;
6774 }
6775
6776 class TessProgramQueryCase : public TestCase
6777 {
6778 public:
6779 TessProgramQueryCase (Context& context, const char* name, const char* desc);
6780
6781 std::string getVertexSource (void) const;
6782 std::string getFragmentSource (void) const;
6783 std::string getTessCtrlSource (const char* globalLayouts) const;
6784 std::string getTessEvalSource (const char* globalLayouts) const;
6785 };
6786
TessProgramQueryCase(Context & context,const char * name,const char * desc)6787 TessProgramQueryCase::TessProgramQueryCase (Context& context, const char* name, const char* desc)
6788 : TestCase(context, name, desc)
6789 {
6790 }
6791
getVertexSource(void) const6792 std::string TessProgramQueryCase::getVertexSource (void) const
6793 {
6794 return "${GLSL_VERSION_DECL}\n"
6795 "${GLSL_PER_VERTEX_OUT}\n"
6796 "void main (void)\n"
6797 "{\n"
6798 " gl_Position = vec4(float(gl_VertexID), float(gl_VertexID / 2), 0.0, 1.0);\n"
6799 "}\n";
6800 }
6801
getFragmentSource(void) const6802 std::string TessProgramQueryCase::getFragmentSource (void) const
6803 {
6804 return "${GLSL_VERSION_DECL}\n"
6805 "layout (location = 0) out mediump vec4 o_color;\n"
6806 "void main (void)\n"
6807 "{\n"
6808 " o_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
6809 "}\n";
6810 }
6811
getTessCtrlSource(const char * globalLayouts) const6812 std::string TessProgramQueryCase::getTessCtrlSource (const char* globalLayouts) const
6813 {
6814 return "${GLSL_VERSION_DECL}\n"
6815 "${TESSELLATION_SHADER_REQUIRE}\n"
6816 "${GLSL_PER_VERTEX_IN_ARR}\n"
6817 "${GLSL_PER_VERTEX_OUT_ARR}\n"
6818 + std::string(globalLayouts) + ";\n"
6819 "void main (void)\n"
6820 "{\n"
6821 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
6822 " gl_TessLevelInner[0] = 2.8;\n"
6823 " gl_TessLevelInner[1] = 2.8;\n"
6824 " gl_TessLevelOuter[0] = 2.8;\n"
6825 " gl_TessLevelOuter[1] = 2.8;\n"
6826 " gl_TessLevelOuter[2] = 2.8;\n"
6827 " gl_TessLevelOuter[3] = 2.8;\n"
6828 "}\n";
6829 }
6830
getTessEvalSource(const char * globalLayouts) const6831 std::string TessProgramQueryCase::getTessEvalSource (const char* globalLayouts) const
6832 {
6833 return "${GLSL_VERSION_DECL}\n"
6834 "${TESSELLATION_SHADER_REQUIRE}\n"
6835 "${GLSL_PER_VERTEX_IN_ARR}\n"
6836 "${GLSL_PER_VERTEX_OUT}\n"
6837 + std::string(globalLayouts) + ";\n"
6838 "void main (void)\n"
6839 "{\n"
6840 " gl_Position = gl_TessCoord.x * gl_in[0].gl_Position\n"
6841 " + gl_TessCoord.y * gl_in[1].gl_Position\n"
6842 " + gl_TessCoord.y * gl_in[2].gl_Position\n"
6843 " + gl_TessCoord.z * gl_in[3].gl_Position;\n"
6844 "}\n";
6845 }
6846
6847 class TessControlOutputVerticesCase : public TessProgramQueryCase
6848 {
6849 public:
6850 TessControlOutputVerticesCase (Context& context, const char* name, const char* desc);
6851 private:
6852 IterateResult iterate (void);
6853 };
6854
TessControlOutputVerticesCase(Context & context,const char * name,const char * desc)6855 TessControlOutputVerticesCase::TessControlOutputVerticesCase (Context& context, const char* name, const char* desc)
6856 : TessProgramQueryCase(context, name, desc)
6857 {
6858 }
6859
iterate(void)6860 TessControlOutputVerticesCase::IterateResult TessControlOutputVerticesCase::iterate (void)
6861 {
6862 checkTessellationSupport(m_context);
6863
6864 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
6865 << glu::VertexSource(specializeShader(m_context, getVertexSource().c_str()))
6866 << glu::FragmentSource(specializeShader(m_context, getFragmentSource().c_str()))
6867 << glu::TessellationControlSource(specializeShader(m_context, getTessCtrlSource("layout(vertices=4) out").c_str()))
6868 << glu::TessellationEvaluationSource(specializeShader(m_context, getTessEvalSource("layout(triangles) in").c_str())));
6869
6870 m_testCtx.getLog() << program;
6871 if (!program.isOk())
6872 throw tcu::TestError("failed to build program");
6873
6874 {
6875 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6876 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6877
6878 gl.enableLogging(true);
6879 verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_CONTROL_OUTPUT_VERTICES, 4, QUERY_PROGRAM_INTEGER);
6880
6881 result.setTestContextResult(m_testCtx);
6882 }
6883 return STOP;
6884 }
6885
6886 class TessGenModeQueryCase : public TessProgramQueryCase
6887 {
6888 public:
6889 TessGenModeQueryCase (Context& context, const char* name, const char* desc);
6890 private:
6891 IterateResult iterate (void);
6892 };
6893
TessGenModeQueryCase(Context & context,const char * name,const char * desc)6894 TessGenModeQueryCase::TessGenModeQueryCase (Context& context, const char* name, const char* desc)
6895 : TessProgramQueryCase(context, name, desc)
6896 {
6897 }
6898
iterate(void)6899 TessGenModeQueryCase::IterateResult TessGenModeQueryCase::iterate (void)
6900 {
6901 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6902 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6903
6904 static const struct
6905 {
6906 const char* description;
6907 const char* layout;
6908 glw::GLenum mode;
6909 } s_modes[] =
6910 {
6911 { "Triangles", "layout(triangles) in", GL_TRIANGLES },
6912 { "Isolines", "layout(isolines) in", GL_ISOLINES },
6913 { "Quads", "layout(quads) in", GL_QUADS },
6914 };
6915
6916 checkTessellationSupport(m_context);
6917 gl.enableLogging(true);
6918
6919 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_modes); ++ndx)
6920 {
6921 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Type", s_modes[ndx].description);
6922
6923 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
6924 << glu::VertexSource(specializeShader(m_context, getVertexSource().c_str()))
6925 << glu::FragmentSource(specializeShader(m_context, getFragmentSource().c_str()))
6926 << glu::TessellationControlSource(specializeShader(m_context, getTessCtrlSource("layout(vertices=6) out").c_str()))
6927 << glu::TessellationEvaluationSource(specializeShader(m_context, getTessEvalSource(s_modes[ndx].layout).c_str())));
6928
6929 m_testCtx.getLog() << program;
6930 if (!program.isOk())
6931 result.fail("failed to build program");
6932 else
6933 verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_MODE, s_modes[ndx].mode, QUERY_PROGRAM_INTEGER);
6934 }
6935
6936 result.setTestContextResult(m_testCtx);
6937 return STOP;
6938 }
6939
6940 class TessGenSpacingQueryCase : public TessProgramQueryCase
6941 {
6942 public:
6943 TessGenSpacingQueryCase (Context& context, const char* name, const char* desc);
6944 private:
6945 IterateResult iterate (void);
6946 };
6947
TessGenSpacingQueryCase(Context & context,const char * name,const char * desc)6948 TessGenSpacingQueryCase::TessGenSpacingQueryCase (Context& context, const char* name, const char* desc)
6949 : TessProgramQueryCase(context, name, desc)
6950 {
6951 }
6952
iterate(void)6953 TessGenSpacingQueryCase::IterateResult TessGenSpacingQueryCase::iterate (void)
6954 {
6955 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6956 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6957
6958 static const struct
6959 {
6960 const char* description;
6961 const char* layout;
6962 glw::GLenum spacing;
6963 } s_modes[] =
6964 {
6965 { "Default spacing", "layout(triangles) in", GL_EQUAL },
6966 { "Equal spacing", "layout(triangles, equal_spacing) in", GL_EQUAL },
6967 { "Fractional even spacing", "layout(triangles, fractional_even_spacing) in", GL_FRACTIONAL_EVEN },
6968 { "Fractional odd spacing", "layout(triangles, fractional_odd_spacing) in", GL_FRACTIONAL_ODD },
6969 };
6970
6971 checkTessellationSupport(m_context);
6972 gl.enableLogging(true);
6973
6974 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_modes); ++ndx)
6975 {
6976 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Type", s_modes[ndx].description);
6977
6978 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
6979 << glu::VertexSource(specializeShader(m_context, getVertexSource().c_str()))
6980 << glu::FragmentSource(specializeShader(m_context, getFragmentSource().c_str()))
6981 << glu::TessellationControlSource(specializeShader(m_context, getTessCtrlSource("layout(vertices=6) out").c_str()))
6982 << glu::TessellationEvaluationSource(specializeShader(m_context, getTessEvalSource(s_modes[ndx].layout).c_str())));
6983
6984 m_testCtx.getLog() << program;
6985 if (!program.isOk())
6986 result.fail("failed to build program");
6987 else
6988 verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_SPACING, s_modes[ndx].spacing, QUERY_PROGRAM_INTEGER);
6989 }
6990
6991 result.setTestContextResult(m_testCtx);
6992 return STOP;
6993 }
6994
6995 class TessGenVertexOrderQueryCase : public TessProgramQueryCase
6996 {
6997 public:
6998 TessGenVertexOrderQueryCase (Context& context, const char* name, const char* desc);
6999 private:
7000 IterateResult iterate (void);
7001 };
7002
TessGenVertexOrderQueryCase(Context & context,const char * name,const char * desc)7003 TessGenVertexOrderQueryCase::TessGenVertexOrderQueryCase (Context& context, const char* name, const char* desc)
7004 : TessProgramQueryCase(context, name, desc)
7005 {
7006 }
7007
iterate(void)7008 TessGenVertexOrderQueryCase::IterateResult TessGenVertexOrderQueryCase::iterate (void)
7009 {
7010 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
7011 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
7012
7013 static const struct
7014 {
7015 const char* description;
7016 const char* layout;
7017 glw::GLenum order;
7018 } s_modes[] =
7019 {
7020 { "Default order", "layout(triangles) in", GL_CCW },
7021 { "CW order", "layout(triangles, cw) in", GL_CW },
7022 { "CCW order", "layout(triangles, ccw) in", GL_CCW },
7023 };
7024
7025 checkTessellationSupport(m_context);
7026 gl.enableLogging(true);
7027
7028 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_modes); ++ndx)
7029 {
7030 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Type", s_modes[ndx].description);
7031
7032 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
7033 << glu::VertexSource(specializeShader(m_context, getVertexSource().c_str()))
7034 << glu::FragmentSource(specializeShader(m_context, getFragmentSource().c_str()))
7035 << glu::TessellationControlSource(specializeShader(m_context, getTessCtrlSource("layout(vertices=6) out").c_str()))
7036 << glu::TessellationEvaluationSource(specializeShader(m_context, getTessEvalSource(s_modes[ndx].layout).c_str())));
7037
7038 m_testCtx.getLog() << program;
7039 if (!program.isOk())
7040 result.fail("failed to build program");
7041 else
7042 verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_VERTEX_ORDER, s_modes[ndx].order, QUERY_PROGRAM_INTEGER);
7043 }
7044
7045 result.setTestContextResult(m_testCtx);
7046 return STOP;
7047 }
7048
7049 class TessGenPointModeQueryCase : public TessProgramQueryCase
7050 {
7051 public:
7052 TessGenPointModeQueryCase (Context& context, const char* name, const char* desc);
7053 private:
7054 IterateResult iterate (void);
7055 };
7056
TessGenPointModeQueryCase(Context & context,const char * name,const char * desc)7057 TessGenPointModeQueryCase::TessGenPointModeQueryCase (Context& context, const char* name, const char* desc)
7058 : TessProgramQueryCase(context, name, desc)
7059 {
7060 }
7061
iterate(void)7062 TessGenPointModeQueryCase::IterateResult TessGenPointModeQueryCase::iterate (void)
7063 {
7064 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
7065 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
7066
7067 static const struct
7068 {
7069 const char* description;
7070 const char* layout;
7071 glw::GLenum mode;
7072 } s_modes[] =
7073 {
7074 { "Default mode", "layout(triangles) in", GL_FALSE },
7075 { "Point mode", "layout(triangles, point_mode) in", GL_TRUE },
7076 };
7077
7078 checkTessellationSupport(m_context);
7079 gl.enableLogging(true);
7080
7081 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_modes); ++ndx)
7082 {
7083 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Type", s_modes[ndx].description);
7084
7085 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
7086 << glu::VertexSource(specializeShader(m_context, getVertexSource().c_str()))
7087 << glu::FragmentSource(specializeShader(m_context, getFragmentSource().c_str()))
7088 << glu::TessellationControlSource(specializeShader(m_context, getTessCtrlSource("layout(vertices=6) out").c_str()))
7089 << glu::TessellationEvaluationSource(specializeShader(m_context, getTessEvalSource(s_modes[ndx].layout).c_str())));
7090
7091 m_testCtx.getLog() << program;
7092 if (!program.isOk())
7093 result.fail("failed to build program");
7094 else
7095 verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_POINT_MODE, s_modes[ndx].mode, QUERY_PROGRAM_INTEGER);
7096 }
7097
7098 result.setTestContextResult(m_testCtx);
7099 return STOP;
7100 }
7101
7102 class ReferencedByTessellationQueryCase : public TestCase
7103 {
7104 public:
7105 ReferencedByTessellationQueryCase (Context& context, const char* name, const char* desc, bool isCtrlCase);
7106 private:
7107 void init (void);
7108 IterateResult iterate (void);
7109
7110 std::string getVertexSource (void) const;
7111 std::string getFragmentSource (void) const;
7112 std::string getTessCtrlSource (void) const;
7113 std::string getTessEvalSource (void) const;
7114
7115 const bool m_isCtrlCase;
7116 };
7117
ReferencedByTessellationQueryCase(Context & context,const char * name,const char * desc,bool isCtrlCase)7118 ReferencedByTessellationQueryCase::ReferencedByTessellationQueryCase (Context& context, const char* name, const char* desc, bool isCtrlCase)
7119 : TestCase (context, name, desc)
7120 , m_isCtrlCase (isCtrlCase)
7121 {
7122 }
7123
init(void)7124 void ReferencedByTessellationQueryCase::init (void)
7125 {
7126 checkTessellationSupport(m_context);
7127 }
7128
iterate(void)7129 ReferencedByTessellationQueryCase::IterateResult ReferencedByTessellationQueryCase::iterate (void)
7130 {
7131 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
7132 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
7133 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
7134 << glu::VertexSource(specializeShader(m_context, getVertexSource().c_str()))
7135 << glu::FragmentSource(specializeShader(m_context, getFragmentSource().c_str()))
7136 << glu::TessellationControlSource(specializeShader(m_context, getTessCtrlSource().c_str()))
7137 << glu::TessellationEvaluationSource(specializeShader(m_context, getTessEvalSource().c_str())));
7138
7139 gl.enableLogging(true);
7140
7141 m_testCtx.getLog() << program;
7142 if (!program.isOk())
7143 result.fail("failed to build program");
7144 else
7145 {
7146 const deUint32 props[1] = { (deUint32)((m_isCtrlCase) ? (GL_REFERENCED_BY_TESS_CONTROL_SHADER) : (GL_REFERENCED_BY_TESS_EVALUATION_SHADER)) };
7147
7148 {
7149 const tcu::ScopedLogSection section (m_testCtx.getLog(), "UnreferencedUniform", "Unreferenced uniform u_unreferenced");
7150 deUint32 resourcePos;
7151 glw::GLsizei length = 0;
7152 glw::GLint referenced = 0;
7153
7154 resourcePos = gl.glGetProgramResourceIndex(program.getProgram(), GL_UNIFORM, "u_unreferenced");
7155 m_testCtx.getLog() << tcu::TestLog::Message << "u_unreferenced resource index: " << resourcePos << tcu::TestLog::EndMessage;
7156
7157 if (resourcePos == GL_INVALID_INDEX)
7158 result.fail("resourcePos was GL_INVALID_INDEX");
7159 else
7160 {
7161 gl.glGetProgramResourceiv(program.getProgram(), GL_UNIFORM, resourcePos, 1, props, 1, &length, &referenced);
7162 m_testCtx.getLog()
7163 << tcu::TestLog::Message
7164 << "Query " << glu::getProgramResourcePropertyStr(props[0])
7165 << ", got " << length << " value(s), value[0] = " << glu::getBooleanStr(referenced)
7166 << tcu::TestLog::EndMessage;
7167
7168 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "query resource");
7169
7170 if (length == 0 || referenced != GL_FALSE)
7171 result.fail("expected GL_FALSE");
7172 }
7173 }
7174
7175 {
7176 const tcu::ScopedLogSection section (m_testCtx.getLog(), "ReferencedUniform", "Referenced uniform u_referenced");
7177 deUint32 resourcePos;
7178 glw::GLsizei length = 0;
7179 glw::GLint referenced = 0;
7180
7181 resourcePos = gl.glGetProgramResourceIndex(program.getProgram(), GL_UNIFORM, "u_referenced");
7182 m_testCtx.getLog() << tcu::TestLog::Message << "u_referenced resource index: " << resourcePos << tcu::TestLog::EndMessage;
7183
7184 if (resourcePos == GL_INVALID_INDEX)
7185 result.fail("resourcePos was GL_INVALID_INDEX");
7186 else
7187 {
7188 gl.glGetProgramResourceiv(program.getProgram(), GL_UNIFORM, resourcePos, 1, props, 1, &length, &referenced);
7189 m_testCtx.getLog()
7190 << tcu::TestLog::Message
7191 << "Query " << glu::getProgramResourcePropertyStr(props[0])
7192 << ", got " << length << " value(s), value[0] = " << glu::getBooleanStr(referenced)
7193 << tcu::TestLog::EndMessage;
7194
7195 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query resource");
7196
7197 if (length == 0 || referenced != GL_TRUE)
7198 result.fail("expected GL_TRUE");
7199 }
7200 }
7201 }
7202
7203 result.setTestContextResult(m_testCtx);
7204 return STOP;
7205 }
7206
getVertexSource(void) const7207 std::string ReferencedByTessellationQueryCase::getVertexSource (void) const
7208 {
7209 return "${GLSL_VERSION_DECL}\n"
7210 "${GLSL_PER_VERTEX_OUT}\n"
7211 "void main (void)\n"
7212 "{\n"
7213 " gl_Position = vec4(float(gl_VertexID), float(gl_VertexID / 2), 0.0, 1.0);\n"
7214 "}\n";
7215 }
7216
getFragmentSource(void) const7217 std::string ReferencedByTessellationQueryCase::getFragmentSource (void) const
7218 {
7219 return "${GLSL_VERSION_DECL}\n"
7220 "layout (location = 0) out mediump vec4 o_color;\n"
7221 "void main (void)\n"
7222 "{\n"
7223 " o_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
7224 "}\n";
7225 }
7226
getTessCtrlSource(void) const7227 std::string ReferencedByTessellationQueryCase::getTessCtrlSource (void) const
7228 {
7229 std::ostringstream buf;
7230 buf << "${GLSL_VERSION_DECL}\n"
7231 "${TESSELLATION_SHADER_REQUIRE}\n"
7232 "${GLSL_PER_VERTEX_IN_ARR}\n"
7233 "${GLSL_PER_VERTEX_OUT_ARR}\n"
7234 "layout(vertices = 3) out;\n"
7235 "uniform highp vec4 " << ((m_isCtrlCase) ? ("u_referenced") : ("u_unreferenced")) << ";\n"
7236 "void main (void)\n"
7237 "{\n"
7238 " vec4 offset = " << ((m_isCtrlCase) ? ("u_referenced") : ("u_unreferenced")) << ";\n"
7239 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position + offset;\n"
7240 " gl_TessLevelInner[0] = 2.8;\n"
7241 " gl_TessLevelInner[1] = 2.8;\n"
7242 " gl_TessLevelOuter[0] = 2.8;\n"
7243 " gl_TessLevelOuter[1] = 2.8;\n"
7244 " gl_TessLevelOuter[2] = 2.8;\n"
7245 " gl_TessLevelOuter[3] = 2.8;\n"
7246 "}\n";
7247 return buf.str();
7248 }
7249
getTessEvalSource(void) const7250 std::string ReferencedByTessellationQueryCase::getTessEvalSource (void) const
7251 {
7252 std::ostringstream buf;
7253 buf << "${GLSL_VERSION_DECL}\n"
7254 "${TESSELLATION_SHADER_REQUIRE}\n"
7255 "${GLSL_PER_VERTEX_IN_ARR}\n"
7256 "${GLSL_PER_VERTEX_OUT}\n"
7257 "layout(triangles) in;\n"
7258 "uniform highp vec4 " << ((m_isCtrlCase) ? ("u_unreferenced") : ("u_referenced")) << ";\n"
7259 "void main (void)\n"
7260 "{\n"
7261 " vec4 offset = " << ((m_isCtrlCase) ? ("u_unreferenced") : ("u_referenced")) << ";\n"
7262 " gl_Position = gl_TessCoord.x * gl_in[0].gl_Position\n"
7263 " + gl_TessCoord.y * gl_in[1].gl_Position\n"
7264 " + gl_TessCoord.z * gl_in[2].gl_Position\n"
7265 " + offset;\n"
7266 "}\n";
7267
7268 return buf.str();
7269 }
7270
7271 class IsPerPatchQueryCase : public TestCase
7272 {
7273 public:
7274 IsPerPatchQueryCase (Context& context, const char* name, const char* desc);
7275 private:
7276 void init (void);
7277 IterateResult iterate (void);
7278 };
7279
IsPerPatchQueryCase(Context & context,const char * name,const char * desc)7280 IsPerPatchQueryCase::IsPerPatchQueryCase (Context& context, const char* name, const char* desc)
7281 : TestCase(context, name, desc)
7282 {
7283 }
7284
init(void)7285 void IsPerPatchQueryCase::init (void)
7286 {
7287 checkTessellationSupport(m_context);
7288 }
7289
iterate(void)7290 IsPerPatchQueryCase::IterateResult IsPerPatchQueryCase::iterate (void)
7291 {
7292 static const char* const s_controlSource = "${GLSL_VERSION_DECL}\n"
7293 "${TESSELLATION_SHADER_REQUIRE}\n"
7294 "${GLSL_PER_VERTEX_IN_ARR}\n"
7295 "${GLSL_PER_VERTEX_OUT_ARR}\n"
7296 "layout(vertices = 3) out;\n"
7297 "patch out highp vec4 v_perPatch;\n"
7298 "out highp vec4 v_perVertex[];\n"
7299 "void main (void)\n"
7300 "{\n"
7301 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
7302 " v_perPatch = gl_in[0].gl_Position;\n"
7303 " v_perVertex[gl_InvocationID] = -gl_in[gl_InvocationID].gl_Position;\n"
7304 " gl_TessLevelInner[0] = 2.8;\n"
7305 " gl_TessLevelInner[1] = 2.8;\n"
7306 " gl_TessLevelOuter[0] = 2.8;\n"
7307 " gl_TessLevelOuter[1] = 2.8;\n"
7308 " gl_TessLevelOuter[2] = 2.8;\n"
7309 " gl_TessLevelOuter[3] = 2.8;\n"
7310 "}\n";
7311 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
7312 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
7313 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
7314 << glu::TessellationControlSource(specializeShader(m_context, s_controlSource))
7315 << glu::ProgramSeparable(true));
7316
7317 gl.enableLogging(true);
7318
7319 m_testCtx.getLog() << program;
7320 if (!program.isOk())
7321 result.fail("failed to build program");
7322 else
7323 {
7324 const deUint32 props[1] = { GL_IS_PER_PATCH };
7325
7326 {
7327 const tcu::ScopedLogSection section (m_testCtx.getLog(), "PerPatchOutput", "Per patch v_perPatch");
7328 deUint32 resourcePos;
7329 glw::GLsizei length = 0;
7330 glw::GLint referenced = 0;
7331
7332 resourcePos = gl.glGetProgramResourceIndex(program.getProgram(), GL_PROGRAM_OUTPUT, "v_perPatch");
7333 m_testCtx.getLog() << tcu::TestLog::Message << "v_perPatch resource index: " << resourcePos << tcu::TestLog::EndMessage;
7334
7335 if (resourcePos == GL_INVALID_INDEX)
7336 result.fail("resourcePos was GL_INVALID_INDEX");
7337 else
7338 {
7339 gl.glGetProgramResourceiv(program.getProgram(), GL_PROGRAM_OUTPUT, resourcePos, 1, props, 1, &length, &referenced);
7340 m_testCtx.getLog()
7341 << tcu::TestLog::Message
7342 << "Query " << glu::getProgramResourcePropertyStr(props[0])
7343 << ", got " << length << " value(s), value[0] = " << glu::getBooleanStr(referenced)
7344 << tcu::TestLog::EndMessage;
7345
7346 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "query resource");
7347
7348 if (length == 0 || referenced != GL_TRUE)
7349 result.fail("expected GL_TRUE");
7350 }
7351 }
7352
7353 {
7354 const tcu::ScopedLogSection section (m_testCtx.getLog(), "PerVertexhOutput", "Per vertex v_perVertex");
7355 deUint32 resourcePos;
7356 glw::GLsizei length = 0;
7357 glw::GLint referenced = 0;
7358
7359 resourcePos = gl.glGetProgramResourceIndex(program.getProgram(), GL_PROGRAM_OUTPUT, "v_perVertex");
7360 m_testCtx.getLog() << tcu::TestLog::Message << "v_perVertex resource index: " << resourcePos << tcu::TestLog::EndMessage;
7361
7362 if (resourcePos == GL_INVALID_INDEX)
7363 result.fail("resourcePos was GL_INVALID_INDEX");
7364 else
7365 {
7366 gl.glGetProgramResourceiv(program.getProgram(), GL_PROGRAM_OUTPUT, resourcePos, 1, props, 1, &length, &referenced);
7367 m_testCtx.getLog()
7368 << tcu::TestLog::Message
7369 << "Query " << glu::getProgramResourcePropertyStr(props[0])
7370 << ", got " << length << " value(s), value[0] = " << glu::getBooleanStr(referenced)
7371 << tcu::TestLog::EndMessage;
7372
7373 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query resource");
7374
7375 if (length == 0 || referenced != GL_FALSE)
7376 result.fail("expected GL_FALSE");
7377 }
7378 }
7379 }
7380
7381 result.setTestContextResult(m_testCtx);
7382 return STOP;
7383 }
7384
7385 } // anonymous
7386
TessellationTests(Context & context,bool isGL45)7387 TessellationTests::TessellationTests (Context& context, bool isGL45)
7388 : TestCaseGroup(context, "tessellation", "Tessellation Tests")
7389 , m_isGL45(isGL45)
7390 {
7391 }
7392
~TessellationTests(void)7393 TessellationTests::~TessellationTests (void)
7394 {
7395 }
7396
init(void)7397 void TessellationTests::init (void)
7398 {
7399 {
7400 tcu::TestCaseGroup* const queryGroup = new tcu::TestCaseGroup(m_testCtx, "state_query", "Query tests");
7401 addChild(queryGroup);
7402
7403 // new limits
7404 queryGroup->addChild(new LimitQueryCase(m_context, "max_patch_vertices", "Test MAX_PATCH_VERTICES", GL_MAX_PATCH_VERTICES, 32));
7405 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_gen_level", "Test MAX_TESS_GEN_LEVEL", GL_MAX_TESS_GEN_LEVEL, 64));
7406 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_uniform_components", "Test MAX_TESS_CONTROL_UNIFORM_COMPONENTS", GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS, 1024));
7407 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_uniform_components", "Test MAX_TESS_EVALUATION_UNIFORM_COMPONENTS", GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS, 1024));
7408 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));
7409 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));
7410 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_output_components", "Test MAX_TESS_CONTROL_OUTPUT_COMPONENTS", GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS, 64));
7411 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_patch_components", "Test MAX_TESS_PATCH_COMPONENTS", GL_MAX_TESS_PATCH_COMPONENTS, 120));
7412 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));
7413 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_output_components", "Test MAX_TESS_EVALUATION_OUTPUT_COMPONENTS", GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS, 64));
7414 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_uniform_blocks", "Test MAX_TESS_CONTROL_UNIFORM_BLOCKS", GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS, 12));
7415 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_uniform_blocks", "Test MAX_TESS_EVALUATION_UNIFORM_BLOCKS", GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS, 12));
7416 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_input_components", "Test MAX_TESS_CONTROL_INPUT_COMPONENTS", GL_MAX_TESS_CONTROL_INPUT_COMPONENTS, 64));
7417 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_input_components", "Test MAX_TESS_EVALUATION_INPUT_COMPONENTS", GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS, 64));
7418 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));
7419 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));
7420 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_atomic_counters", "Test MAX_TESS_CONTROL_ATOMIC_COUNTERS", GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS, 0));
7421 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_atomic_counters", "Test MAX_TESS_EVALUATION_ATOMIC_COUNTERS", GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS, 0));
7422 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_image_uniforms", "Test MAX_TESS_CONTROL_IMAGE_UNIFORMS", GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, 0));
7423 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_image_uniforms", "Test MAX_TESS_EVALUATION_IMAGE_UNIFORMS", GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, 0));
7424 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));
7425 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));
7426
7427 // modified limits
7428 queryGroup->addChild(new LimitQueryCase(m_context, "max_uniform_buffer_bindings", "Test MAX_UNIFORM_BUFFER_BINDINGS", GL_MAX_UNIFORM_BUFFER_BINDINGS, 72));
7429 queryGroup->addChild(new LimitQueryCase(m_context, "max_combined_uniform_blocks", "Test MAX_COMBINED_UNIFORM_BLOCKS", GL_MAX_COMBINED_UNIFORM_BLOCKS, 60));
7430 queryGroup->addChild(new LimitQueryCase(m_context, "max_combined_texture_image_units", "Test MAX_COMBINED_TEXTURE_IMAGE_UNITS", GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, 96));
7431
7432 // combined limits
7433 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));
7434 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));
7435
7436 // features
7437 queryGroup->addChild(new PrimitiveRestartForPatchesSupportedCase(m_context, "primitive_restart_for_patches_supported", "Test PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED"));
7438
7439 // states
7440 queryGroup->addChild(new PatchVerticesStateCase(m_context, "patch_vertices", "Test PATCH_VERTICES"));
7441
7442 // program states
7443 queryGroup->addChild(new TessControlOutputVerticesCase (m_context, "tess_control_output_vertices", "Test TESS_CONTROL_OUTPUT_VERTICES"));
7444 queryGroup->addChild(new TessGenModeQueryCase (m_context, "tess_gen_mode", "Test TESS_GEN_MODE"));
7445 queryGroup->addChild(new TessGenSpacingQueryCase (m_context, "tess_gen_spacing", "Test TESS_GEN_SPACING"));
7446 queryGroup->addChild(new TessGenVertexOrderQueryCase (m_context, "tess_gen_vertex_order", "Test TESS_GEN_VERTEX_ORDER"));
7447 queryGroup->addChild(new TessGenPointModeQueryCase (m_context, "tess_gen_point_mode", "Test TESS_GEN_POINT_MODE"));
7448
7449 // resource queries
7450 queryGroup->addChild(new ReferencedByTessellationQueryCase (m_context, "referenced_by_tess_control_shader", "Test REFERENCED_BY_TESS_CONTROL_SHADER", true));
7451 queryGroup->addChild(new ReferencedByTessellationQueryCase (m_context, "referenced_by_tess_evaluation_shader", "Test REFERENCED_BY_TESS_EVALUATION_SHADER", false));
7452 queryGroup->addChild(new IsPerPatchQueryCase (m_context, "is_per_patch", "Test IS_PER_PATCH"));
7453 }
7454
7455 {
7456 TestCaseGroup* const tessCoordGroup = new TestCaseGroup(m_context, "tesscoord", "Get tessellation coordinates with transform feedback and validate them");
7457 addChild(tessCoordGroup);
7458
7459 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7460 {
7461 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7462
7463 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7464 tessCoordGroup->addChild(new TessCoordCase(m_context,
7465 (string() + getTessPrimitiveTypeShaderName(primitiveType) + "_" + getSpacingModeShaderName((SpacingMode)spacingI)).c_str(), "",
7466 primitiveType, (SpacingMode)spacingI));
7467 }
7468 }
7469
7470 {
7471 TestCaseGroup* const windingGroup = new TestCaseGroup(m_context, "winding", "Test the cw and ccw input layout qualifiers");
7472 addChild(windingGroup);
7473
7474 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7475 {
7476 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7477 if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
7478 continue;
7479
7480 for (int windingI = 0; windingI < WINDING_LAST; windingI++)
7481 {
7482 const Winding winding = (Winding)windingI;
7483 windingGroup->addChild(new WindingCase(m_context, (string() + getTessPrimitiveTypeShaderName(primitiveType) + "_" + getWindingShaderName(winding)).c_str(), "", primitiveType, winding));
7484 }
7485 }
7486 }
7487
7488 {
7489 TestCaseGroup* const shaderInputOutputGroup = new TestCaseGroup(m_context, "shader_input_output", "Test tessellation control and evaluation shader inputs and outputs");
7490 addChild(shaderInputOutputGroup);
7491
7492 {
7493 static const struct
7494 {
7495 int inPatchSize;
7496 int outPatchSize;
7497 } patchVertexCountCases[] =
7498 {
7499 { 5, 10 },
7500 { 10, 5 }
7501 };
7502
7503 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(patchVertexCountCases); caseNdx++)
7504 {
7505 const int inSize = patchVertexCountCases[caseNdx].inPatchSize;
7506 const int outSize = patchVertexCountCases[caseNdx].outPatchSize;
7507
7508 const string caseName = "patch_vertices_" + de::toString(inSize) + "_in_" + de::toString(outSize) + "_out";
7509
7510 shaderInputOutputGroup->addChild(new PatchVertexCountCase(m_context, caseName.c_str(), "Test input and output patch vertex counts", inSize, outSize,
7511 ("data/tessellation/" + caseName + "_ref.png").c_str()));
7512 }
7513 }
7514
7515 for (int caseTypeI = 0; caseTypeI < PerPatchDataCase::CASETYPE_LAST; caseTypeI++)
7516 {
7517 const PerPatchDataCase::CaseType caseType = (PerPatchDataCase::CaseType)caseTypeI;
7518 const char* const caseName = PerPatchDataCase::getCaseTypeName(caseType);
7519
7520 shaderInputOutputGroup->addChild(new PerPatchDataCase(m_context, caseName, PerPatchDataCase::getCaseTypeDescription(caseType), caseType,
7521 PerPatchDataCase::caseTypeUsesRefImageFromFile(caseType) ? (string() + "data/tessellation/" + caseName + "_ref.png").c_str() : DE_NULL));
7522 }
7523
7524 for (int caseTypeI = 0; caseTypeI < GLPositionCase::CASETYPE_LAST; caseTypeI++)
7525 {
7526 const GLPositionCase::CaseType caseType = (GLPositionCase::CaseType)caseTypeI;
7527 const char* const caseName = GLPositionCase::getCaseTypeName(caseType);
7528
7529 shaderInputOutputGroup->addChild(new GLPositionCase(m_context, caseName, "", caseType, "data/tessellation/gl_position_ref.png"));
7530 }
7531
7532 shaderInputOutputGroup->addChild(new BarrierCase(m_context, "barrier", "Basic barrier usage", "data/tessellation/barrier_ref.png"));
7533 }
7534
7535 {
7536 TestCaseGroup* const miscDrawGroup = new TestCaseGroup(m_context, "misc_draw", "Miscellaneous draw-result-verifying cases");
7537 addChild(miscDrawGroup);
7538
7539 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7540 {
7541 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7542 if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
7543 continue;
7544
7545 const char* const primTypeName = getTessPrimitiveTypeShaderName(primitiveType);
7546
7547 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7548 {
7549 const string caseName = string() + "fill_cover_" + primTypeName + "_" + getSpacingModeShaderName((SpacingMode)spacingI);
7550
7551 miscDrawGroup->addChild(new BasicTriangleFillCoverCase(m_context,
7552 caseName.c_str(), "Check that there are no obvious gaps in the triangle-filled area of a tessellated shape",
7553 primitiveType, (SpacingMode)spacingI,
7554 ("data/tessellation/" + caseName + "_ref").c_str()));
7555 }
7556 }
7557
7558 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7559 {
7560 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7561 if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
7562 continue;
7563
7564 const char* const primTypeName = getTessPrimitiveTypeShaderName(primitiveType);
7565
7566 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7567 {
7568 const string caseName = string() + "fill_overlap_" + primTypeName + "_" + getSpacingModeShaderName((SpacingMode)spacingI);
7569
7570 miscDrawGroup->addChild(new BasicTriangleFillNonOverlapCase(m_context,
7571 caseName.c_str(), "Check that there are no obvious triangle overlaps in the triangle-filled area of a tessellated shape",
7572 primitiveType, (SpacingMode)spacingI,
7573 ("data/tessellation/" + caseName + "_ref").c_str()));
7574 }
7575 }
7576
7577 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7578 {
7579 const string caseName = string() + "isolines_" + getSpacingModeShaderName((SpacingMode)spacingI);
7580
7581 miscDrawGroup->addChild(new IsolinesRenderCase(m_context,
7582 caseName.c_str(), "Basic isolines render test",
7583 (SpacingMode)spacingI,
7584 ("data/tessellation/" + caseName + "_ref").c_str()));
7585 }
7586 }
7587
7588 {
7589 TestCaseGroup* const commonEdgeGroup = new TestCaseGroup(m_context, "common_edge", "Draw multiple adjacent shapes and check that no cracks appear between them");
7590 addChild(commonEdgeGroup);
7591
7592 for (int caseTypeI = 0; caseTypeI < CommonEdgeCase::CASETYPE_LAST; caseTypeI++)
7593 {
7594 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7595 {
7596 const CommonEdgeCase::CaseType caseType = (CommonEdgeCase::CaseType)caseTypeI;
7597 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7598 if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
7599 continue;
7600
7601 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7602 {
7603 const SpacingMode spacing = (SpacingMode)spacingI;
7604 const string caseName = (string() + getTessPrimitiveTypeShaderName(primitiveType)
7605 + "_" + getSpacingModeShaderName(spacing)
7606 + (caseType == CommonEdgeCase::CASETYPE_BASIC ? ""
7607 : caseType == CommonEdgeCase::CASETYPE_PRECISE ? "_precise"
7608 : DE_NULL));
7609
7610 commonEdgeGroup->addChild(new CommonEdgeCase(m_context, caseName.c_str(), "", primitiveType, spacing, caseType));
7611 }
7612 }
7613 }
7614 }
7615
7616 {
7617 TestCaseGroup* const fractionalSpacingModeGroup = new TestCaseGroup(m_context, "fractional_spacing", "Test fractional spacing modes");
7618 addChild(fractionalSpacingModeGroup);
7619
7620 fractionalSpacingModeGroup->addChild(new FractionalSpacingModeCase(m_context, "odd", "", SPACINGMODE_FRACTIONAL_ODD));
7621 fractionalSpacingModeGroup->addChild(new FractionalSpacingModeCase(m_context, "even", "", SPACINGMODE_FRACTIONAL_EVEN));
7622 }
7623
7624 {
7625 TestCaseGroup* const primitiveDiscardGroup = new TestCaseGroup(m_context, "primitive_discard", "Test primitive discard with relevant outer tessellation level <= 0.0");
7626 addChild(primitiveDiscardGroup);
7627
7628 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7629 {
7630 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7631 {
7632 for (int windingI = 0; windingI < WINDING_LAST; windingI++)
7633 {
7634 for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++)
7635 {
7636 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7637 const SpacingMode spacing = (SpacingMode)spacingI;
7638 const Winding winding = (Winding)windingI;
7639 const bool usePointMode = usePointModeI != 0;
7640
7641 primitiveDiscardGroup->addChild(new PrimitiveDiscardCase(m_context, (string() + getTessPrimitiveTypeShaderName(primitiveType)
7642 + "_" + getSpacingModeShaderName(spacing)
7643 + "_" + getWindingShaderName(winding)
7644 + (usePointMode ? "_point_mode" : "")).c_str(), "",
7645 primitiveType, spacing, winding, usePointMode));
7646 }
7647 }
7648 }
7649 }
7650 }
7651
7652 {
7653 TestCaseGroup* const invarianceGroup = new TestCaseGroup(m_context, "invariance", "Test tessellation invariance rules");
7654
7655 TestCaseGroup* const invariantPrimitiveSetGroup = new TestCaseGroup(m_context, "primitive_set", "Test invariance rule #1");
7656 TestCaseGroup* const invariantOuterEdgeGroup = new TestCaseGroup(m_context, "outer_edge_division", "Test invariance rule #2");
7657 TestCaseGroup* const symmetricOuterEdgeGroup = new TestCaseGroup(m_context, "outer_edge_symmetry", "Test invariance rule #3");
7658 TestCaseGroup* const outerEdgeVertexSetIndexIndependenceGroup = new TestCaseGroup(m_context, "outer_edge_index_independence", "Test invariance rule #4");
7659 TestCaseGroup* const invariantTriangleSetGroup = new TestCaseGroup(m_context, "triangle_set", "Test invariance rule #5");
7660 TestCaseGroup* const invariantInnerTriangleSetGroup = new TestCaseGroup(m_context, "inner_triangle_set", "Test invariance rule #6");
7661 TestCaseGroup* const invariantOuterTriangleSetGroup = new TestCaseGroup(m_context, "outer_triangle_set", "Test invariance rule #7");
7662 TestCaseGroup* const tessCoordComponentRangeGroup = new TestCaseGroup(m_context, "tess_coord_component_range", "Test invariance rule #8, first part");
7663 TestCaseGroup* const oneMinusTessCoordComponentGroup = new TestCaseGroup(m_context, "one_minus_tess_coord_component", "Test invariance rule #8, second part");
7664
7665 addChild(invarianceGroup);
7666 invarianceGroup->addChild(invariantPrimitiveSetGroup);
7667 invarianceGroup->addChild(invariantOuterEdgeGroup);
7668 invarianceGroup->addChild(symmetricOuterEdgeGroup);
7669 invarianceGroup->addChild(outerEdgeVertexSetIndexIndependenceGroup);
7670 invarianceGroup->addChild(invariantTriangleSetGroup);
7671 invarianceGroup->addChild(invariantInnerTriangleSetGroup);
7672 invarianceGroup->addChild(invariantOuterTriangleSetGroup);
7673 invarianceGroup->addChild(tessCoordComponentRangeGroup);
7674 invarianceGroup->addChild(oneMinusTessCoordComponentGroup);
7675
7676 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7677 {
7678 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7679 const string primName = getTessPrimitiveTypeShaderName(primitiveType);
7680 const bool triOrQuad = primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS;
7681
7682 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7683 {
7684 const SpacingMode spacing = (SpacingMode)spacingI;
7685 const string primSpacName = primName + "_" + getSpacingModeShaderName(spacing);
7686
7687 if (triOrQuad)
7688 {
7689 invariantOuterEdgeGroup->addChild (new InvariantOuterEdgeCase (m_context, primSpacName.c_str(), "", primitiveType, spacing));
7690 invariantTriangleSetGroup->addChild (new InvariantTriangleSetCase (m_context, primSpacName.c_str(), "", primitiveType, spacing));
7691 invariantInnerTriangleSetGroup->addChild(new InvariantInnerTriangleSetCase (m_context, primSpacName.c_str(), "", primitiveType, spacing));
7692 invariantOuterTriangleSetGroup->addChild(new InvariantOuterTriangleSetCase (m_context, primSpacName.c_str(), "", primitiveType, spacing));
7693 }
7694
7695 for (int windingI = 0; windingI < WINDING_LAST; windingI++)
7696 {
7697 const Winding winding = (Winding)windingI;
7698 const string primSpacWindName = primSpacName + "_" + getWindingShaderName(winding);
7699
7700 for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++)
7701 {
7702 const bool usePointMode = usePointModeI != 0;
7703 const string primSpacWindPointName = primSpacWindName + (usePointMode ? "_point_mode" : "");
7704
7705 invariantPrimitiveSetGroup->addChild (new InvariantPrimitiveSetCase (m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode));
7706 symmetricOuterEdgeGroup->addChild (new SymmetricOuterEdgeCase (m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode));
7707 tessCoordComponentRangeGroup->addChild (new TessCoordComponentRangeCase (m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode));
7708 oneMinusTessCoordComponentGroup->addChild (new OneMinusTessCoordComponentCase (m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode));
7709
7710 if (triOrQuad)
7711 outerEdgeVertexSetIndexIndependenceGroup->addChild(new OuterEdgeVertexSetIndexIndependenceCase(m_context, primSpacWindPointName.c_str(), "",
7712 primitiveType, spacing, winding, usePointMode));
7713 }
7714 }
7715 }
7716 }
7717 }
7718
7719 {
7720 static const struct
7721 {
7722 const char* name;
7723 const char* description;
7724 UserDefinedIOCase::IOType ioType;
7725 } ioCases[] =
7726 {
7727 { "per_patch", "Per-patch TCS outputs", UserDefinedIOCase::IO_TYPE_PER_PATCH },
7728 { "per_patch_array", "Per-patch array TCS outputs", UserDefinedIOCase::IO_TYPE_PER_PATCH_ARRAY },
7729 { "per_patch_block", "Per-patch TCS outputs in IO block", UserDefinedIOCase::IO_TYPE_PER_PATCH_BLOCK },
7730 { "per_patch_block_array", "Per-patch TCS outputs in IO block array", UserDefinedIOCase::IO_TYPE_PER_PATCH_BLOCK_ARRAY },
7731 { "per_vertex", "Per-vertex TCS outputs", UserDefinedIOCase::IO_TYPE_PER_VERTEX },
7732 { "per_vertex_block", "Per-vertex TCS outputs in IO block", UserDefinedIOCase::IO_TYPE_PER_VERTEX_BLOCK },
7733 };
7734
7735 TestCaseGroup* const userDefinedIOGroup = new TestCaseGroup(m_context, "user_defined_io", "Test non-built-in per-patch and per-vertex inputs and outputs");
7736 addChild(userDefinedIOGroup);
7737
7738 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(ioCases); ++ndx)
7739 {
7740 TestCaseGroup* const ioTypeGroup = new TestCaseGroup(m_context, ioCases[ndx].name, ioCases[ndx].description);
7741 userDefinedIOGroup->addChild(ioTypeGroup);
7742
7743 for (int vertexArraySizeI = 0; vertexArraySizeI < UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_LAST; vertexArraySizeI++)
7744 {
7745 const UserDefinedIOCase::VertexIOArraySize vertexArraySize = (UserDefinedIOCase::VertexIOArraySize)vertexArraySizeI;
7746 TestCaseGroup* const vertexArraySizeGroup = new TestCaseGroup(m_context,
7747 vertexArraySizeI == UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_IMPLICIT
7748 ? "vertex_io_array_size_implicit"
7749 : vertexArraySizeI == UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN
7750 ? "vertex_io_array_size_shader_builtin"
7751 : vertexArraySizeI == UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY
7752 ? "vertex_io_array_size_query"
7753 : DE_NULL,
7754 "");
7755 ioTypeGroup->addChild(vertexArraySizeGroup);
7756
7757 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7758 {
7759 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7760 vertexArraySizeGroup->addChild(new UserDefinedIOCase(m_context, getTessPrimitiveTypeShaderName(primitiveType), "", primitiveType, ioCases[ndx].ioType, vertexArraySize, UserDefinedIOCase::TESS_CONTROL_OUT_ARRAY_SIZE_IMPLICIT,
7761 (string() + "data/tessellation/user_defined_io_" + getTessPrimitiveTypeShaderName(primitiveType) + "_ref.png").c_str()));
7762 }
7763
7764 if (ioCases[ndx].ioType == UserDefinedIOCase::IO_TYPE_PER_VERTEX
7765 || ioCases[ndx].ioType == UserDefinedIOCase::IO_TYPE_PER_VERTEX_BLOCK)
7766 {
7767 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7768 {
7769 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7770 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,
7771 (string() + "data/tessellation/user_defined_io_" + getTessPrimitiveTypeShaderName(primitiveType) + "_ref.png").c_str()));
7772 }
7773 }
7774 }
7775 }
7776
7777 // ES only
7778 if (!m_isGL45)
7779 {
7780 de::MovePtr<TestCaseGroup> negativeGroup (new TestCaseGroup(m_context, "negative", "Negative cases"));
7781
7782 {
7783 de::MovePtr<TestCaseGroup> es31Group (new TestCaseGroup(m_context, "es31", "GLSL ES 3.1 Negative cases"));
7784 gls::ShaderLibrary shaderLibrary (m_testCtx, m_context.getRenderContext(), m_context.getContextInfo());
7785 const std::vector<tcu::TestNode*> children = shaderLibrary.loadShaderFile("shaders/es31/tessellation_negative_user_defined_io.test");
7786
7787 for (int i = 0; i < (int)children.size(); i++)
7788 es31Group->addChild(children[i]);
7789
7790 negativeGroup->addChild(es31Group.release());
7791 }
7792
7793 {
7794 de::MovePtr<TestCaseGroup> es32Group (new TestCaseGroup(m_context, "es32", "GLSL ES 3.2 Negative cases"));
7795 gls::ShaderLibrary shaderLibrary (m_testCtx, m_context.getRenderContext(), m_context.getContextInfo());
7796 const std::vector<tcu::TestNode*> children = shaderLibrary.loadShaderFile("shaders/es32/tessellation_negative_user_defined_io.test");
7797
7798 for (int i = 0; i < (int)children.size(); i++)
7799 es32Group->addChild(children[i]);
7800
7801 negativeGroup->addChild(es32Group.release());
7802 }
7803
7804 userDefinedIOGroup->addChild(negativeGroup.release());
7805 }
7806 }
7807 }
7808
7809 } // Functional
7810 } // gles31
7811 } // deqp
7812