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