1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.1 Module
3 * -------------------------------------------------
5 * Copyright 2014 The Android Open Source Project
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief Tessellation Tests.
22 *//*--------------------------------------------------------------------*/
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 "tcuSurface.hpp"
41 #include "tcuTextureUtil.hpp"
42 #include "tcuVectorUtil.hpp"
43 #include "tcuImageIO.hpp"
44 #include "tcuResource.hpp"
45 #include "tcuImageCompare.hpp"
46 #include "deRandom.hpp"
47 #include "deStringUtil.hpp"
48 #include "deSharedPtr.hpp"
52 #include "glwEnums.hpp"
53 #include "glwDefs.hpp"
54 #include "glwFunctions.hpp"
63 using glu::ShaderProgram;
64 using glu::RenderContext;
65 using tcu::RenderTarget;
76 using namespace glw; // For GL types.
81 using gls::TextureTestUtil::RandomViewport;
88 using namespace gls::StateQueryUtil;
92 MINIMUM_MAX_TESS_GEN_LEVEL = 64 //!< GL-defined minimum for GL_MAX_TESS_GEN_LEVEL.
95 static inline bool vec3XLessThan (const Vec3& a, const Vec3& b) { return a.x() < b.x(); }
97 template <typename IterT>
98 static string elemsStr (const IterT& begin, const IterT& end, int wrapLengthParam = 0, int numIndentationSpaces = 0)
100 const string baseIndentation = string(numIndentationSpaces, ' ');
101 const string deepIndentation = baseIndentation + string(4, ' ');
102 const int wrapLength = wrapLengthParam > 0 ? wrapLengthParam : std::numeric_limits<int>::max();
103 const int length = (int)std::distance(begin, end);
106 if (length > wrapLength)
107 result += "(amount: " + de::toString(length) + ") ";
108 result += string() + "{" + (length > wrapLength ? "\n"+deepIndentation : " ");
112 for (IterT it = begin; it != end; ++it)
115 result += string() + ", " + (index % wrapLength == 0 ? "\n"+deepIndentation : "");
116 result += de::toString(*it);
120 result += length > wrapLength ? "\n"+baseIndentation : " ";
127 template <typename ContainerT>
128 static string containerStr (const ContainerT& c, int wrapLengthParam = 0, int numIndentationSpaces = 0)
130 return elemsStr(c.begin(), c.end(), wrapLengthParam, numIndentationSpaces);
133 template <typename T, int N>
134 static string arrayStr (const T (&arr)[N], int wrapLengthParam = 0, int numIndentationSpaces = 0)
136 return elemsStr(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr), wrapLengthParam, numIndentationSpaces);
139 template <typename T, int N>
140 static T arrayMax (const T (&arr)[N])
142 return *std::max_element(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr));
145 template <typename T, typename MembT>
146 static vector<MembT> members (const vector<T>& objs, MembT T::* membP)
148 vector<MembT> result(objs.size());
149 for (int i = 0; i < (int)objs.size(); i++)
150 result[i] = objs[i].*membP;
154 template <typename T, int N>
155 static vector<T> arrayToVector (const T (&arr)[N])
157 return vector<T>(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr));
160 template <typename ContainerT, typename T>
161 static inline bool contains (const ContainerT& c, const T& key)
163 return c.find(key) != c.end();
167 static inline tcu::Vector<bool, Size> singleTrueMask (int index)
169 DE_ASSERT(de::inBounds(index, 0, Size));
170 tcu::Vector<bool, Size> result;
171 result[index] = true;
175 static int intPow (int base, int exp)
182 const int sub = intPow(base, exp/2);
190 tcu::Surface getPixels (const glu::RenderContext& rCtx, int x, int y, int width, int height)
192 tcu::Surface result(width, height);
193 glu::readPixels(rCtx, x, y, result.getAccess());
197 tcu::Surface getPixels (const glu::RenderContext& rCtx, const RandomViewport& vp)
199 return getPixels(rCtx, vp.x, vp.y, vp.width, vp.height);
202 static inline void checkRenderTargetSize (const RenderTarget& renderTarget, int minSize)
204 if (renderTarget.getWidth() < minSize || renderTarget.getHeight() < minSize)
205 throw tcu::NotSupportedError("Render target width and height must be at least " + de::toString(minSize));
208 tcu::TextureLevel getPNG (const tcu::Archive& archive, const string& filename)
210 tcu::TextureLevel result;
211 tcu::ImageIO::loadPNG(result, archive, filename.c_str());
215 static int numBasicSubobjects (const glu::VarType& type)
217 if (type.isBasicType())
219 else if (type.isArrayType())
220 return type.getArraySize()*numBasicSubobjects(type.getElementType());
221 else if (type.isStructType())
223 const glu::StructType& structType = *type.getStructPtr();
225 for (int i = 0; i < structType.getNumMembers(); i++)
226 result += numBasicSubobjects(structType.getMember(i).getType());
236 static inline int numVerticesPerPrimitive (deUint32 primitiveTypeGL)
238 switch (primitiveTypeGL)
240 case GL_POINTS: return 1;
241 case GL_TRIANGLES: return 3;
242 case GL_LINES: return 2;
249 static inline void setViewport (const glw::Functions& gl, const RandomViewport& vp)
251 gl.viewport(vp.x, vp.y, vp.width, vp.height);
254 static inline deUint32 getQueryResult (const glw::Functions& gl, deUint32 queryObject)
256 deUint32 result = (deUint32)-1;
257 gl.getQueryObjectuiv(queryObject, GL_QUERY_RESULT, &result);
258 TCU_CHECK(result != (deUint32)-1);
262 template <typename T>
263 static void readDataMapped (const glw::Functions& gl, deUint32 bufferTarget, int numElems, T* dst)
265 const int numBytes = numElems*(int)sizeof(T);
266 const T* const mappedData = (const T*)gl.mapBufferRange(bufferTarget, 0, numBytes, GL_MAP_READ_BIT);
267 GLU_EXPECT_NO_ERROR(gl.getError(), (string() + "glMapBufferRange(" + glu::getBufferTargetName((int)bufferTarget) + ", 0, " + de::toString(numBytes) + ", GL_MAP_READ_BIT)").c_str());
268 TCU_CHECK(mappedData != DE_NULL);
270 for (int i = 0; i < numElems; i++)
271 dst[i] = mappedData[i];
273 gl.unmapBuffer(bufferTarget);
276 template <typename T>
277 static vector<T> readDataMapped (const glw::Functions& gl, deUint32 bufferTarget, int numElems)
279 vector<T> result(numElems);
280 readDataMapped(gl, bufferTarget, numElems, &result[0]);
287 template <typename ArgT, bool res>
288 struct ConstantUnaryPredicate
290 bool operator() (const ArgT&) const { return res; }
293 //! Helper for handling simple, one-varying transform feedbacks.
294 template <typename VaryingT>
295 class TransformFeedbackHandler
301 vector<VaryingT> varying;
303 Result (void) : numPrimitives(-1) {}
304 Result (int n, const vector<VaryingT>& v) : numPrimitives(n), varying(v) {}
307 TransformFeedbackHandler (const glu::RenderContext& renderCtx, int maxNumVertices);
309 Result renderAndGetPrimitives (deUint32 programGL, deUint32 tfPrimTypeGL, int numBindings, const glu::VertexArrayBinding* bindings, int numVertices) const;
312 const glu::RenderContext& m_renderCtx;
313 const glu::TransformFeedback m_tf;
314 const glu::Buffer m_tfBuffer;
315 const glu::Query m_tfPrimQuery;
318 template <typename AttribType>
319 TransformFeedbackHandler<AttribType>::TransformFeedbackHandler (const glu::RenderContext& renderCtx, int maxNumVertices)
320 : m_renderCtx (renderCtx)
322 , m_tfBuffer (renderCtx)
323 , m_tfPrimQuery (renderCtx)
325 const glw::Functions& gl = m_renderCtx.getFunctions();
326 // \note Room for 1 extra triangle, to detect if GL returns too many primitives.
327 const int bufferSize = (maxNumVertices + 3) * (int)sizeof(AttribType);
329 gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, *m_tfBuffer);
330 gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bufferSize, DE_NULL, GL_DYNAMIC_READ);
333 template <typename AttribType>
334 typename TransformFeedbackHandler<AttribType>::Result TransformFeedbackHandler<AttribType>::renderAndGetPrimitives (deUint32 programGL, deUint32 tfPrimTypeGL, int numBindings, const glu::VertexArrayBinding* bindings, int numVertices) const
336 DE_ASSERT(tfPrimTypeGL == GL_POINTS || tfPrimTypeGL == GL_LINES || tfPrimTypeGL == GL_TRIANGLES);
338 const glw::Functions& gl = m_renderCtx.getFunctions();
340 gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, *m_tf);
341 gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, *m_tfBuffer);
342 gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, *m_tfBuffer);
344 gl.beginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, *m_tfPrimQuery);
345 gl.beginTransformFeedback(tfPrimTypeGL);
347 glu::draw(m_renderCtx, programGL, numBindings, bindings, glu::pr::Patches(numVertices));
348 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
350 gl.endTransformFeedback();
351 gl.endQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
354 const int numPrimsWritten = (int)getQueryResult(gl, *m_tfPrimQuery);
355 return Result(numPrimsWritten, readDataMapped<AttribType>(gl, GL_TRANSFORM_FEEDBACK_BUFFER, numPrimsWritten * numVerticesPerPrimitive(tfPrimTypeGL)));
359 template <typename T>
363 bool operator() (const T& a, const T& b) const { return a.size() < b.size(); }
366 //! Predicate functor for comparing structs by their members.
367 template <typename Pred, typename T, typename MembT>
371 MemberPred (MembT T::* membP) : m_membP(membP), m_pred(Pred()) {}
372 bool operator() (const T& a, const T& b) const { return m_pred(a.*m_membP, b.*m_membP); }
379 //! Convenience wrapper for MemberPred, because class template arguments aren't deduced based on constructor arguments.
380 template <template <typename> class Pred, typename T, typename MembT>
381 static MemberPred<Pred<MembT>, T, MembT> memberPred (MembT T::* membP) { return MemberPred<Pred<MembT>, T, MembT>(membP); }
383 template <typename SeqT, int Size, typename Pred>
387 LexCompare (void) : m_pred(Pred()) {}
389 bool operator() (const SeqT& a, const SeqT& b) const
391 for (int i = 0; i < Size; i++)
393 if (m_pred(a[i], b[i]))
395 if (m_pred(b[i], a[i]))
406 class VecLexLessThan : public LexCompare<tcu::Vector<float, Size>, Size, std::less<float> >
410 enum TessPrimitiveType
412 TESSPRIMITIVETYPE_TRIANGLES = 0,
413 TESSPRIMITIVETYPE_QUADS,
414 TESSPRIMITIVETYPE_ISOLINES,
416 TESSPRIMITIVETYPE_LAST
422 SPACINGMODE_FRACTIONAL_ODD,
423 SPACINGMODE_FRACTIONAL_EVEN,
436 static inline const char* getTessPrimitiveTypeShaderName (TessPrimitiveType type)
440 case TESSPRIMITIVETYPE_TRIANGLES: return "triangles";
441 case TESSPRIMITIVETYPE_QUADS: return "quads";
442 case TESSPRIMITIVETYPE_ISOLINES: return "isolines";
449 static inline const char* getSpacingModeShaderName (SpacingMode mode)
453 case SPACINGMODE_EQUAL: return "equal_spacing";
454 case SPACINGMODE_FRACTIONAL_ODD: return "fractional_odd_spacing";
455 case SPACINGMODE_FRACTIONAL_EVEN: return "fractional_even_spacing";
462 static inline const char* getWindingShaderName (Winding winding)
466 case WINDING_CCW: return "ccw";
467 case WINDING_CW: return "cw";
474 static inline string getTessellationEvaluationInLayoutString (TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode=false)
476 return string() + "layout (" + getTessPrimitiveTypeShaderName(primType)
477 + ", " + getSpacingModeShaderName(spacing)
478 + ", " + getWindingShaderName(winding)
479 + (usePointMode ? ", point_mode" : "")
483 static inline string getTessellationEvaluationInLayoutString (TessPrimitiveType primType, SpacingMode spacing, bool usePointMode=false)
485 return string() + "layout (" + getTessPrimitiveTypeShaderName(primType)
486 + ", " + getSpacingModeShaderName(spacing)
487 + (usePointMode ? ", point_mode" : "")
491 static inline string getTessellationEvaluationInLayoutString (TessPrimitiveType primType, Winding winding, bool usePointMode=false)
493 return string() + "layout (" + getTessPrimitiveTypeShaderName(primType)
494 + ", " + getWindingShaderName(winding)
495 + (usePointMode ? ", point_mode" : "")
499 static inline string getTessellationEvaluationInLayoutString (TessPrimitiveType primType, bool usePointMode=false)
501 return string() + "layout (" + getTessPrimitiveTypeShaderName(primType)
502 + (usePointMode ? ", point_mode" : "")
506 static inline deUint32 outputPrimitiveTypeGL (TessPrimitiveType tessPrimType, bool usePointMode)
512 switch (tessPrimType)
514 case TESSPRIMITIVETYPE_TRIANGLES: return GL_TRIANGLES;
515 case TESSPRIMITIVETYPE_QUADS: return GL_TRIANGLES;
516 case TESSPRIMITIVETYPE_ISOLINES: return GL_LINES;
524 static inline int numInnerTessellationLevels (TessPrimitiveType primType)
528 case TESSPRIMITIVETYPE_TRIANGLES: return 1;
529 case TESSPRIMITIVETYPE_QUADS: return 2;
530 case TESSPRIMITIVETYPE_ISOLINES: return 0;
531 default: DE_ASSERT(false); return -1;
535 static inline int numOuterTessellationLevels (TessPrimitiveType primType)
539 case TESSPRIMITIVETYPE_TRIANGLES: return 3;
540 case TESSPRIMITIVETYPE_QUADS: return 4;
541 case TESSPRIMITIVETYPE_ISOLINES: return 2;
542 default: DE_ASSERT(false); return -1;
546 static string tessellationLevelsString (const float* inner, int numInner, const float* outer, int numOuter)
548 DE_ASSERT(numInner >= 0 && numOuter >= 0);
549 return "inner: " + elemsStr(inner, inner+numInner) + ", outer: " + elemsStr(outer, outer+numOuter);
552 static string tessellationLevelsString (const float* inner, const float* outer, TessPrimitiveType primType)
554 return tessellationLevelsString(inner, numInnerTessellationLevels(primType), outer, numOuterTessellationLevels(primType));
557 static string tessellationLevelsString (const float* inner, const float* outer)
559 return tessellationLevelsString(inner, 2, outer, 4);
562 static inline float getClampedTessLevel (SpacingMode mode, float tessLevel)
566 case SPACINGMODE_EQUAL: return de::max(1.0f, tessLevel);
567 case SPACINGMODE_FRACTIONAL_ODD: return de::max(1.0f, tessLevel);
568 case SPACINGMODE_FRACTIONAL_EVEN: return de::max(2.0f, tessLevel);
575 static inline int getRoundedTessLevel (SpacingMode mode, float clampedTessLevel)
577 int result = (int)deFloatCeil(clampedTessLevel);
581 case SPACINGMODE_EQUAL: break;
582 case SPACINGMODE_FRACTIONAL_ODD: result += 1 - result % 2; break;
583 case SPACINGMODE_FRACTIONAL_EVEN: result += result % 2; break;
587 DE_ASSERT(de::inRange<int>(result, 1, MINIMUM_MAX_TESS_GEN_LEVEL));
592 static int getClampedRoundedTessLevel (SpacingMode mode, float tessLevel)
594 return getRoundedTessLevel(mode, getClampedTessLevel(mode, tessLevel));
597 //! A description of an outer edge of a triangle, quad or isolines.
598 //! An outer edge can be described by the index of a u/v/w coordinate
599 //! and the coordinate's value along that edge.
600 struct OuterEdgeDescription
602 int constantCoordinateIndex;
603 float constantCoordinateValueChoices[2];
604 int numConstantCoordinateValueChoices;
606 OuterEdgeDescription (int i, float c0) : constantCoordinateIndex(i), numConstantCoordinateValueChoices(1) { constantCoordinateValueChoices[0] = c0; }
607 OuterEdgeDescription (int i, float c0, float c1) : constantCoordinateIndex(i), numConstantCoordinateValueChoices(2) { constantCoordinateValueChoices[0] = c0; constantCoordinateValueChoices[1] = c1; }
609 string description (void) const
611 static const char* const coordinateNames[] = { "u", "v", "w" };
613 for (int i = 0; i < numConstantCoordinateValueChoices; i++)
614 result += string() + (i > 0 ? " or " : "") + coordinateNames[constantCoordinateIndex] + "=" + de::toString(constantCoordinateValueChoices[i]);
618 bool contains (const Vec3& v) const
620 for (int i = 0; i < numConstantCoordinateValueChoices; i++)
621 if (v[constantCoordinateIndex] == constantCoordinateValueChoices[i])
627 static vector<OuterEdgeDescription> outerEdgeDescriptions (TessPrimitiveType primType)
629 static const OuterEdgeDescription triangleOuterEdgeDescriptions[3] =
631 OuterEdgeDescription(0, 0.0f),
632 OuterEdgeDescription(1, 0.0f),
633 OuterEdgeDescription(2, 0.0f)
636 static const OuterEdgeDescription quadOuterEdgeDescriptions[4] =
638 OuterEdgeDescription(0, 0.0f),
639 OuterEdgeDescription(1, 0.0f),
640 OuterEdgeDescription(0, 1.0f),
641 OuterEdgeDescription(1, 1.0f)
644 static const OuterEdgeDescription isolinesOuterEdgeDescriptions[1] =
646 OuterEdgeDescription(0, 0.0f, 1.0f),
651 case TESSPRIMITIVETYPE_TRIANGLES: return arrayToVector(triangleOuterEdgeDescriptions);
652 case TESSPRIMITIVETYPE_QUADS: return arrayToVector(quadOuterEdgeDescriptions);
653 case TESSPRIMITIVETYPE_ISOLINES: return arrayToVector(isolinesOuterEdgeDescriptions);
654 default: DE_ASSERT(false); return vector<OuterEdgeDescription>();
658 // \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).
659 static vector<Vec3> generateReferenceTriangleTessCoords (SpacingMode spacingMode, int inner, int outer0, int outer1, int outer2)
661 vector<Vec3> tessCoords;
665 if (outer0 == 1 && outer1 == 1 && outer2 == 1)
667 tessCoords.push_back(Vec3(1.0f, 0.0f, 0.0f));
668 tessCoords.push_back(Vec3(0.0f, 1.0f, 0.0f));
669 tessCoords.push_back(Vec3(0.0f, 0.0f, 1.0f));
673 return generateReferenceTriangleTessCoords(spacingMode, spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
674 outer0, outer1, outer2);
678 for (int i = 0; i < outer0; i++) { const float v = (float)i / (float)outer0; tessCoords.push_back(Vec3( 0.0f, v, 1.0f - v)); }
679 for (int i = 0; i < outer1; i++) { const float v = (float)i / (float)outer1; tessCoords.push_back(Vec3(1.0f - v, 0.0f, v)); }
680 for (int i = 0; i < outer2; i++) { const float v = (float)i / (float)outer2; tessCoords.push_back(Vec3( v, 1.0f - v, 0.0f)); }
682 const int numInnerTriangles = inner/2;
683 for (int innerTriangleNdx = 0; innerTriangleNdx < numInnerTriangles; innerTriangleNdx++)
685 const int curInnerTriangleLevel = inner - 2*(innerTriangleNdx+1);
687 if (curInnerTriangleLevel == 0)
688 tessCoords.push_back(Vec3(1.0f/3.0f));
691 const float minUVW = (float)(2 * (innerTriangleNdx + 1)) / (float)(3 * inner);
692 const float maxUVW = 1.0f - 2.0f*minUVW;
693 const Vec3 corners[3] =
695 Vec3(maxUVW, minUVW, minUVW),
696 Vec3(minUVW, maxUVW, minUVW),
697 Vec3(minUVW, minUVW, maxUVW)
700 for (int i = 0; i < curInnerTriangleLevel; i++)
702 const float f = (float)i / (float)curInnerTriangleLevel;
703 for (int j = 0; j < 3; j++)
704 tessCoords.push_back((1.0f - f)*corners[j] + f*corners[(j+1)%3]);
713 static int referenceTriangleNonPointModePrimitiveCount (SpacingMode spacingMode, int inner, int outer0, int outer1, int outer2)
717 if (outer0 == 1 && outer1 == 1 && outer2 == 1)
720 return referenceTriangleNonPointModePrimitiveCount(spacingMode, spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
721 outer0, outer1, outer2);
725 int result = outer0 + outer1 + outer2;
727 const int numInnerTriangles = inner/2;
728 for (int innerTriangleNdx = 0; innerTriangleNdx < numInnerTriangles; innerTriangleNdx++)
730 const int curInnerTriangleLevel = inner - 2*(innerTriangleNdx+1);
732 if (curInnerTriangleLevel == 1)
735 result += 2*3*curInnerTriangleLevel;
742 // \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).
743 static vector<Vec3> generateReferenceQuadTessCoords (SpacingMode spacingMode, int inner0, int inner1, int outer0, int outer1, int outer2, int outer3)
745 vector<Vec3> tessCoords;
747 if (inner0 == 1 || inner1 == 1)
749 if (inner0 == 1 && inner1 == 1 && outer0 == 1 && outer1 == 1 && outer2 == 1 && outer3 == 1)
751 tessCoords.push_back(Vec3(0.0f, 0.0f, 0.0f));
752 tessCoords.push_back(Vec3(1.0f, 0.0f, 0.0f));
753 tessCoords.push_back(Vec3(0.0f, 1.0f, 0.0f));
754 tessCoords.push_back(Vec3(1.0f, 1.0f, 0.0f));
758 return generateReferenceQuadTessCoords(spacingMode, inner0 > 1 ? inner0 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
759 inner1 > 1 ? inner1 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
760 outer0, outer1, outer2, outer3);
764 for (int i = 0; i < outer0; i++) { const float v = (float)i / (float)outer0; tessCoords.push_back(Vec3(0.0f, v, 0.0f)); }
765 for (int i = 0; i < outer1; i++) { const float v = (float)i / (float)outer1; tessCoords.push_back(Vec3(1.0f-v, 0.0f, 0.0f)); }
766 for (int i = 0; i < outer2; i++) { const float v = (float)i / (float)outer2; tessCoords.push_back(Vec3(1.0f, 1.0f-v, 0.0f)); }
767 for (int i = 0; i < outer3; i++) { const float v = (float)i / (float)outer3; tessCoords.push_back(Vec3(v, 1.0f, 0.0f)); }
769 for (int innerVtxY = 0; innerVtxY < inner1-1; innerVtxY++)
770 for (int innerVtxX = 0; innerVtxX < inner0-1; innerVtxX++)
771 tessCoords.push_back(Vec3((float)(innerVtxX + 1) / (float)inner0,
772 (float)(innerVtxY + 1) / (float)inner1,
779 static int referenceQuadNonPointModePrimitiveCount (SpacingMode spacingMode, int inner0, int inner1, int outer0, int outer1, int outer2, int outer3)
781 vector<Vec3> tessCoords;
783 if (inner0 == 1 || inner1 == 1)
785 if (inner0 == 1 && inner1 == 1 && outer0 == 1 && outer1 == 1 && outer2 == 1 && outer3 == 1)
788 return referenceQuadNonPointModePrimitiveCount(spacingMode, inner0 > 1 ? inner0 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
789 inner1 > 1 ? inner1 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
790 outer0, outer1, outer2, outer3);
793 return 2*(inner0-2)*(inner1-2) + 2*(inner0-2) + 2*(inner1-2) + outer0+outer1+outer2+outer3;
796 // \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).
797 static vector<Vec3> generateReferenceIsolineTessCoords (int outer0, int outer1)
799 vector<Vec3> tessCoords;
801 for (int y = 0; y < outer0; y++)
802 for (int x = 0; x < outer1+1; x++)
803 tessCoords.push_back(Vec3((float)x / (float)outer1,
804 (float)y / (float)outer0,
810 static int referenceIsolineNonPointModePrimitiveCount (int outer0, int outer1)
812 return outer0*outer1;
815 static void getClampedRoundedTriangleTessLevels (SpacingMode spacingMode, const float* innerSrc, const float* outerSrc, int* innerDst, int *outerDst)
817 innerDst[0] = getClampedRoundedTessLevel(spacingMode, innerSrc[0]);
818 for (int i = 0; i < 3; i++)
819 outerDst[i] = getClampedRoundedTessLevel(spacingMode, outerSrc[i]);
822 static void getClampedRoundedQuadTessLevels (SpacingMode spacingMode, const float* innerSrc, const float* outerSrc, int* innerDst, int *outerDst)
824 for (int i = 0; i < 2; i++)
825 innerDst[i] = getClampedRoundedTessLevel(spacingMode, innerSrc[i]);
826 for (int i = 0; i < 4; i++)
827 outerDst[i] = getClampedRoundedTessLevel(spacingMode, outerSrc[i]);
830 static void getClampedRoundedIsolineTessLevels (SpacingMode spacingMode, const float* outerSrc, int* outerDst)
832 outerDst[0] = getClampedRoundedTessLevel(SPACINGMODE_EQUAL, outerSrc[0]);
833 outerDst[1] = getClampedRoundedTessLevel(spacingMode, outerSrc[1]);
836 static inline bool isPatchDiscarded (TessPrimitiveType primitiveType, const float* outerLevels)
838 const int numOuterLevels = numOuterTessellationLevels(primitiveType);
839 for (int i = 0; i < numOuterLevels; i++)
840 if (outerLevels[i] <= 0.0f)
845 static vector<Vec3> generateReferenceTessCoords (TessPrimitiveType primitiveType, SpacingMode spacingMode, const float* innerLevels, const float* outerLevels)
847 if (isPatchDiscarded(primitiveType, outerLevels))
848 return vector<Vec3>();
850 switch (primitiveType)
852 case TESSPRIMITIVETYPE_TRIANGLES:
856 getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
858 if (spacingMode != SPACINGMODE_EQUAL)
860 // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
861 DE_ASSERT(de::abs(innerLevels[0] - (float)inner) < 0.001f);
862 for (int i = 0; i < 3; i++)
863 DE_ASSERT(de::abs(outerLevels[i] - (float)outer[i]) < 0.001f);
864 DE_ASSERT(inner > 1 || (outer[0] == 1 && outer[1] == 1 && outer[2] == 1));
867 return generateReferenceTriangleTessCoords(spacingMode, inner, outer[0], outer[1], outer[2]);
870 case TESSPRIMITIVETYPE_QUADS:
874 getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
876 if (spacingMode != SPACINGMODE_EQUAL)
878 // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
879 for (int i = 0; i < 2; i++)
880 DE_ASSERT(de::abs(innerLevels[i] - (float)inner[i]) < 0.001f);
881 for (int i = 0; i < 4; i++)
882 DE_ASSERT(de::abs(outerLevels[i] - (float)outer[i]) < 0.001f);
884 DE_ASSERT((inner[0] > 1 && inner[1] > 1) || (inner[0] == 1 && inner[1] == 1 && outer[0] == 1 && outer[1] == 1 && outer[2] == 1 && outer[3] == 1));
887 return generateReferenceQuadTessCoords(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]);
890 case TESSPRIMITIVETYPE_ISOLINES:
893 getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
895 if (spacingMode != SPACINGMODE_EQUAL)
897 // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
898 DE_ASSERT(de::abs(outerLevels[1] - (float)outer[1]) < 0.001f);
901 return generateReferenceIsolineTessCoords(outer[0], outer[1]);
906 return vector<Vec3>();
910 static int referencePointModePrimitiveCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, const float* innerLevels, const float* outerLevels)
912 if (isPatchDiscarded(primitiveType, outerLevels))
915 switch (primitiveType)
917 case TESSPRIMITIVETYPE_TRIANGLES:
921 getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
922 return (int)generateReferenceTriangleTessCoords(spacingMode, inner, outer[0], outer[1], outer[2]).size();
925 case TESSPRIMITIVETYPE_QUADS:
929 getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
930 return (int)generateReferenceQuadTessCoords(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]).size();
933 case TESSPRIMITIVETYPE_ISOLINES:
936 getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
937 return (int)generateReferenceIsolineTessCoords(outer[0], outer[1]).size();
946 static int referenceNonPointModePrimitiveCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, const float* innerLevels, const float* outerLevels)
948 if (isPatchDiscarded(primitiveType, outerLevels))
951 switch (primitiveType)
953 case TESSPRIMITIVETYPE_TRIANGLES:
957 getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
958 return referenceTriangleNonPointModePrimitiveCount(spacingMode, inner, outer[0], outer[1], outer[2]);
961 case TESSPRIMITIVETYPE_QUADS:
965 getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
966 return referenceQuadNonPointModePrimitiveCount(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]);
969 case TESSPRIMITIVETYPE_ISOLINES:
972 getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
973 return referenceIsolineNonPointModePrimitiveCount(outer[0], outer[1]);
982 static int referencePrimitiveCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, bool usePointMode, const float* innerLevels, const float* outerLevels)
984 return usePointMode ? referencePointModePrimitiveCount (primitiveType, spacingMode, innerLevels, outerLevels)
985 : referenceNonPointModePrimitiveCount (primitiveType, spacingMode, innerLevels, outerLevels);
988 static int referenceVertexCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, bool usePointMode, const float* innerLevels, const float* outerLevels)
990 return referencePrimitiveCount(primitiveType, spacingMode, usePointMode, innerLevels, outerLevels)
991 * numVerticesPerPrimitive(outputPrimitiveTypeGL(primitiveType, usePointMode));
994 //! Helper for calling referenceVertexCount multiple times with different tessellation levels.
995 //! \note Levels contains inner and outer levels, per patch, in order IIOOOO. The full 6 levels must always be present, irrespective of primitiveType.
996 static int multiplePatchReferenceVertexCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, bool usePointMode, const float* levels, int numPatches)
999 for (int patchNdx = 0; patchNdx < numPatches; patchNdx++)
1000 result += referenceVertexCount(primitiveType, spacingMode, usePointMode, &levels[6*patchNdx + 0], &levels[6*patchNdx + 2]);
1004 vector<float> generateRandomPatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel, de::Random& rnd)
1006 vector<float> tessLevels(numPatches*6);
1008 for (int patchNdx = 0; patchNdx < numPatches; patchNdx++)
1010 float* const inner = &tessLevels[patchNdx*6 + 0];
1011 float* const outer = &tessLevels[patchNdx*6 + 2];
1013 for (int j = 0; j < 2; j++)
1014 inner[j] = rnd.getFloat(1.0f, 62.0f);
1015 for (int j = 0; j < 4; j++)
1016 outer[j] = j == constantOuterLevelIndex ? constantOuterLevel : rnd.getFloat(1.0f, 62.0f);
1022 static inline void drawPoint (tcu::Surface& dst, int centerX, int centerY, const tcu::RGBA& color, int size)
1024 const int width = dst.getWidth();
1025 const int height = dst.getHeight();
1026 DE_ASSERT(de::inBounds(centerX, 0, width) && de::inBounds(centerY, 0, height));
1027 DE_ASSERT(size > 0);
1029 for (int yOff = -((size-1)/2); yOff <= size/2; yOff++)
1030 for (int xOff = -((size-1)/2); xOff <= size/2; xOff++)
1032 const int pixX = centerX + xOff;
1033 const int pixY = centerY + yOff;
1034 if (de::inBounds(pixX, 0, width) && de::inBounds(pixY, 0, height))
1035 dst.setPixel(pixX, pixY, color);
1039 static void drawTessCoordPoint (tcu::Surface& dst, TessPrimitiveType primitiveType, const Vec3& pt, const tcu::RGBA& color, int size)
1041 // \note These coordinates should match the description in the log message in TessCoordCase::iterate.
1043 static const Vec2 triangleCorners[3] =
1046 Vec2(0.5f, 0.95f - 0.9f*deFloatSqrt(3.0f/4.0f)),
1050 static const float quadIsolineLDRU[4] =
1052 0.1f, 0.9f, 0.9f, 0.1f
1055 const Vec2 dstPos = primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? pt.x()*triangleCorners[0]
1056 + pt.y()*triangleCorners[1]
1057 + pt.z()*triangleCorners[2]
1059 : primitiveType == TESSPRIMITIVETYPE_QUADS ||
1060 primitiveType == TESSPRIMITIVETYPE_ISOLINES ? Vec2((1.0f - pt.x())*quadIsolineLDRU[0] + pt.x()*quadIsolineLDRU[2],
1061 (1.0f - pt.y())*quadIsolineLDRU[1] + pt.y()*quadIsolineLDRU[3])
1065 drawPoint(dst, (int)(dstPos.x() * (float)dst.getWidth()), (int)(dstPos.y() * (float)dst.getHeight()), color, size);
1068 static void drawTessCoordVisualization (tcu::Surface& dst, TessPrimitiveType primitiveType, const vector<Vec3>& coords)
1070 const int imageWidth = 256;
1071 const int imageHeight = 256;
1072 dst.setSize(imageWidth, imageHeight);
1074 tcu::clear(dst.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
1076 for (int i = 0; i < (int)coords.size(); i++)
1077 drawTessCoordPoint(dst, primitiveType, coords[i], tcu::RGBA::white(), 2);
1080 static int binarySearchFirstVec3WithXAtLeast (const vector<Vec3>& sorted, float x)
1082 const Vec3 ref(x, 0.0f, 0.0f);
1083 const vector<Vec3>::const_iterator first = std::lower_bound(sorted.begin(), sorted.end(), ref, vec3XLessThan);
1084 if (first == sorted.end())
1086 return (int)std::distance(sorted.begin(), first);
1089 template <typename T, typename P>
1090 static vector<T> sorted (const vector<T>& unsorted, P pred)
1092 vector<T> result = unsorted;
1093 std::sort(result.begin(), result.end(), pred);
1097 template <typename T>
1098 static vector<T> sorted (const vector<T>& unsorted)
1100 vector<T> result = unsorted;
1101 std::sort(result.begin(), result.end());
1105 // Check that all points in subset are (approximately) present also in superset.
1106 static bool oneWayComparePointSets (TestLog& log,
1107 tcu::Surface& errorDst,
1108 TessPrimitiveType primitiveType,
1109 const vector<Vec3>& subset,
1110 const vector<Vec3>& superset,
1111 const char* subsetName,
1112 const char* supersetName,
1113 const tcu::RGBA& errorColor)
1115 const vector<Vec3> supersetSorted = sorted(superset, vec3XLessThan);
1116 const float epsilon = 0.01f;
1117 const int maxNumFailurePrints = 5;
1118 int numFailuresDetected = 0;
1120 for (int subNdx = 0; subNdx < (int)subset.size(); subNdx++)
1122 const Vec3& subPt = subset[subNdx];
1124 bool matchFound = false;
1127 // Binary search the index of the first point in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range.
1128 const Vec3 matchMin = subPt - epsilon;
1129 const Vec3 matchMax = subPt + epsilon;
1130 int firstCandidateNdx = binarySearchFirstVec3WithXAtLeast(supersetSorted, matchMin.x());
1132 if (firstCandidateNdx >= 0)
1134 // Compare subPt to all points in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range.
1135 for (int superNdx = firstCandidateNdx; superNdx < (int)supersetSorted.size() && supersetSorted[superNdx].x() <= matchMax.x(); superNdx++)
1137 const Vec3& superPt = supersetSorted[superNdx];
1139 if (tcu::boolAll(tcu::greaterThanEqual (superPt, matchMin)) &&
1140 tcu::boolAll(tcu::lessThanEqual (superPt, matchMax)))
1151 numFailuresDetected++;
1152 if (numFailuresDetected < maxNumFailurePrints)
1153 log << TestLog::Message << "Failure: no matching " << supersetName << " point found for " << subsetName << " point " << subPt << TestLog::EndMessage;
1154 else if (numFailuresDetected == maxNumFailurePrints)
1155 log << TestLog::Message << "Note: More errors follow" << TestLog::EndMessage;
1157 drawTessCoordPoint(errorDst, primitiveType, subPt, errorColor, 4);
1161 return numFailuresDetected == 0;
1164 static bool compareTessCoords (TestLog& log, TessPrimitiveType primitiveType, const vector<Vec3>& refCoords, const vector<Vec3>& resCoords)
1166 tcu::Surface refVisual;
1167 tcu::Surface resVisual;
1168 bool success = true;
1170 drawTessCoordVisualization(refVisual, primitiveType, refCoords);
1171 drawTessCoordVisualization(resVisual, primitiveType, resCoords);
1173 // Check that all points in reference also exist in result.
1174 success = oneWayComparePointSets(log, refVisual, primitiveType, refCoords, resCoords, "reference", "result", tcu::RGBA::blue()) && success;
1175 // Check that all points in result also exist in reference.
1176 success = oneWayComparePointSets(log, resVisual, primitiveType, resCoords, refCoords, "result", "reference", tcu::RGBA::red()) && success;
1180 log << TestLog::Message << "Note: in the following reference visualization, points that are missing in result point set are blue (if any)" << TestLog::EndMessage
1181 << TestLog::Image("RefTessCoordVisualization", "Reference tessCoord visualization", refVisual)
1182 << TestLog::Message << "Note: in the following result visualization, points that are missing in reference point set are red (if any)" << TestLog::EndMessage;
1185 log << TestLog::Image("ResTessCoordVisualization", "Result tessCoord visualization", resVisual);
1190 namespace VerifyFractionalSpacingSingleInternal
1195 int index; //!< Index of left coordinate in sortedXCoords.
1197 Segment (void) : index(-1), length(-1.0f) {}
1198 Segment (int index_, float length_) : index(index_), length(length_) {}
1200 static vector<float> lengths (const vector<Segment>& segments) { return members(segments, &Segment::length); }
1205 /*--------------------------------------------------------------------*//*!
1206 * \brief Verify fractional spacing conditions for a single line
1208 * Verify that the splitting of an edge (resulting from e.g. an isoline
1209 * with outer levels { 1.0, tessLevel }) with a given fractional spacing
1210 * mode fulfills certain conditions given in the spec.
1212 * Note that some conditions can't be checked from just one line
1213 * (specifically, that the additional segment decreases monotonically
1214 * length and the requirement that the additional segments be placed
1215 * identically for identical values of clamped level).
1217 * Therefore, the function stores some values to additionalSegmentLengthDst
1218 * and additionalSegmentLocationDst that can later be given to
1219 * verifyFractionalSpacingMultiple(). A negative value in length means that
1220 * no additional segments are present, i.e. there's just one segment.
1221 * A negative value in location means that the value wasn't determinable,
1222 * i.e. all segments had same length.
1223 * The values are not stored if false is returned.
1224 *//*--------------------------------------------------------------------*/
1225 static bool verifyFractionalSpacingSingle (TestLog& log, SpacingMode spacingMode, float tessLevel, const vector<float>& coords, float& additionalSegmentLengthDst, int& additionalSegmentLocationDst)
1227 using namespace VerifyFractionalSpacingSingleInternal;
1229 DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN);
1231 const float clampedLevel = getClampedTessLevel(spacingMode, tessLevel);
1232 const int finalLevel = getRoundedTessLevel(spacingMode, clampedLevel);
1233 const vector<float> sortedCoords = sorted(coords);
1234 string failNote = "Note: tessellation level is " + de::toString(tessLevel) + "\nNote: sorted coordinates are:\n " + containerStr(sortedCoords);
1236 if ((int)coords.size() != finalLevel + 1)
1238 log << TestLog::Message << "Failure: number of vertices is " << coords.size() << "; expected " << finalLevel + 1
1239 << " (clamped tessellation level is " << clampedLevel << ")"
1240 << "; final level (clamped level rounded up to " << (spacingMode == SPACINGMODE_FRACTIONAL_EVEN ? "even" : "odd") << ") is " << finalLevel
1241 << " and should equal the number of segments, i.e. number of vertices minus 1" << TestLog::EndMessage
1242 << TestLog::Message << failNote << TestLog::EndMessage;
1246 if (sortedCoords[0] != 0.0f || sortedCoords.back() != 1.0f)
1248 log << TestLog::Message << "Failure: smallest coordinate should be 0.0 and biggest should be 1.0" << TestLog::EndMessage
1249 << TestLog::Message << failNote << TestLog::EndMessage;
1254 vector<Segment> segments(finalLevel);
1255 for (int i = 0; i < finalLevel; i++)
1256 segments[i] = Segment(i, sortedCoords[i+1] - sortedCoords[i]);
1258 failNote += "\nNote: segment lengths are, from left to right:\n " + containerStr(Segment::lengths(segments));
1261 // Divide segments to two different groups based on length.
1263 vector<Segment> segmentsA;
1264 vector<Segment> segmentsB;
1265 segmentsA.push_back(segments[0]);
1267 for (int segNdx = 1; segNdx < (int)segments.size(); segNdx++)
1269 const float epsilon = 0.001f;
1270 const Segment& seg = segments[segNdx];
1272 if (de::abs(seg.length - segmentsA[0].length) < epsilon)
1273 segmentsA.push_back(seg);
1274 else if (segmentsB.empty() || de::abs(seg.length - segmentsB[0].length) < epsilon)
1275 segmentsB.push_back(seg);
1278 log << TestLog::Message << "Failure: couldn't divide segments to 2 groups by length; "
1279 << "e.g. segment of length " << seg.length << " isn't approximately equal to either "
1280 << segmentsA[0].length << " or " << segmentsB[0].length << TestLog::EndMessage
1281 << TestLog::Message << failNote << TestLog::EndMessage;
1286 if (clampedLevel == (float)finalLevel)
1288 // All segments should be of equal length.
1289 if (!segmentsA.empty() && !segmentsB.empty())
1291 log << TestLog::Message << "Failure: clamped and final tessellation level are equal, but not all segments are of equal length." << TestLog::EndMessage
1292 << TestLog::Message << failNote << TestLog::EndMessage;
1297 if (segmentsA.empty() || segmentsB.empty()) // All segments have same length. This is ok.
1299 additionalSegmentLengthDst = segments.size() == 1 ? -1.0f : segments[0].length;
1300 additionalSegmentLocationDst = -1;
1304 if (segmentsA.size() != 2 && segmentsB.size() != 2)
1306 log << TestLog::Message << "Failure: when dividing the segments to 2 groups by length, neither of the two groups has exactly 2 or 0 segments in it" << TestLog::EndMessage
1307 << TestLog::Message << failNote << TestLog::EndMessage;
1311 // For convenience, arrange so that the 2-segment group is segmentsB.
1312 if (segmentsB.size() != 2)
1313 std::swap(segmentsA, segmentsB);
1315 // \note For 4-segment lines both segmentsA and segmentsB have 2 segments each.
1316 // Thus, we can't be sure which ones were meant as the additional segments.
1317 // We give the benefit of the doubt by assuming that they're the shorter
1318 // ones (as they should).
1320 if (segmentsA.size() != 2)
1322 if (segmentsB[0].length > segmentsA[0].length + 0.001f)
1324 log << TestLog::Message << "Failure: the two additional segments are longer than the other segments" << TestLog::EndMessage
1325 << TestLog::Message << failNote << TestLog::EndMessage;
1331 // We have 2 segmentsA and 2 segmentsB, ensure segmentsB has the shorter lengths
1332 if (segmentsB[0].length > segmentsA[0].length)
1333 std::swap(segmentsA, segmentsB);
1336 // Check that the additional segments are placed symmetrically.
1337 if (segmentsB[0].index + segmentsB[1].index + 1 != (int)segments.size())
1339 log << TestLog::Message << "Failure: the two additional segments aren't placed symmetrically; "
1340 << "one is at index " << segmentsB[0].index << " and other is at index " << segmentsB[1].index
1341 << " (note: the two indexes should sum to " << (int)segments.size()-1 << ", i.e. numberOfSegments-1)" << TestLog::EndMessage
1342 << TestLog::Message << failNote << TestLog::EndMessage;
1346 additionalSegmentLengthDst = segmentsB[0].length;
1347 if (segmentsA.size() != 2)
1348 additionalSegmentLocationDst = de::min(segmentsB[0].index, segmentsB[1].index);
1350 additionalSegmentLocationDst = segmentsB[0].length < segmentsA[0].length - 0.001f ? de::min(segmentsB[0].index, segmentsB[1].index)
1351 : -1; // \note -1 when can't reliably decide which ones are the additional segments, a or b.
1358 namespace VerifyFractionalSpacingMultipleInternal
1364 float additionalSegmentLength;
1365 int additionalSegmentLocation;
1366 LineData (float lev, float len, int loc) : tessLevel(lev), additionalSegmentLength(len), additionalSegmentLocation(loc) {}
1371 /*--------------------------------------------------------------------*//*!
1372 * \brief Verify fractional spacing conditions between multiple lines
1374 * Verify the fractional spacing conditions that are not checked in
1375 * verifyFractionalSpacingSingle(). Uses values given by said function
1376 * as parameters, in addition to the spacing mode and tessellation level.
1377 *//*--------------------------------------------------------------------*/
1378 static bool verifyFractionalSpacingMultiple (TestLog& log, SpacingMode spacingMode, const vector<float>& tessLevels, const vector<float>& additionalSegmentLengths, const vector<int>& additionalSegmentLocations)
1380 using namespace VerifyFractionalSpacingMultipleInternal;
1382 DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN);
1383 DE_ASSERT(tessLevels.size() == additionalSegmentLengths.size() &&
1384 tessLevels.size() == additionalSegmentLocations.size());
1386 vector<LineData> lineDatas;
1388 for (int i = 0; i < (int)tessLevels.size(); i++)
1389 lineDatas.push_back(LineData(tessLevels[i], additionalSegmentLengths[i], additionalSegmentLocations[i]));
1392 const vector<LineData> lineDatasSortedByLevel = sorted(lineDatas, memberPred<std::less>(&LineData::tessLevel));
1394 // Check that lines with identical clamped tessellation levels have identical additionalSegmentLocation.
1396 for (int lineNdx = 1; lineNdx < (int)lineDatasSortedByLevel.size(); lineNdx++)
1398 const LineData& curData = lineDatasSortedByLevel[lineNdx];
1399 const LineData& prevData = lineDatasSortedByLevel[lineNdx-1];
1401 if (curData.additionalSegmentLocation < 0 || prevData.additionalSegmentLocation < 0)
1402 continue; // Unknown locations, skip.
1404 if (getClampedTessLevel(spacingMode, curData.tessLevel) == getClampedTessLevel(spacingMode, prevData.tessLevel) &&
1405 curData.additionalSegmentLocation != prevData.additionalSegmentLocation)
1407 log << TestLog::Message << "Failure: additional segments not located identically for two edges with identical clamped tessellation levels" << TestLog::EndMessage
1408 << TestLog::Message << "Note: tessellation levels are " << curData.tessLevel << " and " << prevData.tessLevel
1409 << " (clamped level " << getClampedTessLevel(spacingMode, curData.tessLevel) << ")"
1410 << "; but first additional segments located at indices "
1411 << curData.additionalSegmentLocation << " and " << prevData.additionalSegmentLocation << ", respectively" << TestLog::EndMessage;
1416 // Check that, among lines with same clamped rounded tessellation level, additionalSegmentLength is monotonically decreasing with "clampedRoundedTessLevel - clampedTessLevel" (the "fraction").
1418 for (int lineNdx = 1; lineNdx < (int)lineDatasSortedByLevel.size(); lineNdx++)
1420 const LineData& curData = lineDatasSortedByLevel[lineNdx];
1421 const LineData& prevData = lineDatasSortedByLevel[lineNdx-1];
1423 if (curData.additionalSegmentLength < 0.0f || prevData.additionalSegmentLength < 0.0f)
1424 continue; // Unknown segment lengths, skip.
1426 const float curClampedLevel = getClampedTessLevel(spacingMode, curData.tessLevel);
1427 const float prevClampedLevel = getClampedTessLevel(spacingMode, prevData.tessLevel);
1428 const int curFinalLevel = getRoundedTessLevel(spacingMode, curClampedLevel);
1429 const int prevFinalLevel = getRoundedTessLevel(spacingMode, prevClampedLevel);
1431 if (curFinalLevel != prevFinalLevel)
1434 const float curFraction = (float)curFinalLevel - curClampedLevel;
1435 const float prevFraction = (float)prevFinalLevel - prevClampedLevel;
1437 if (curData.additionalSegmentLength < prevData.additionalSegmentLength ||
1438 (curClampedLevel == prevClampedLevel && curData.additionalSegmentLength != prevData.additionalSegmentLength))
1440 log << TestLog::Message << "Failure: additional segment length isn't monotonically decreasing with the fraction <n> - <f>, among edges with same final tessellation level" << TestLog::EndMessage
1441 << TestLog::Message << "Note: <f> stands for the clamped tessellation level and <n> for the final (rounded and clamped) tessellation level" << TestLog::EndMessage
1442 << TestLog::Message << "Note: two edges have tessellation levels " << prevData.tessLevel << " and " << curData.tessLevel << " respectively"
1443 << ", clamped " << prevClampedLevel << " and " << curClampedLevel << ", final " << prevFinalLevel << " and " << curFinalLevel
1444 << "; fractions are " << prevFraction << " and " << curFraction
1445 << ", but resulted in segment lengths " << prevData.additionalSegmentLength << " and " << curData.additionalSegmentLength << TestLog::EndMessage;
1454 //! Compare triangle sets, ignoring triangle order and vertex order within triangle, and possibly exclude some triangles too.
1455 template <typename IsTriangleRelevantT>
1456 static bool compareTriangleSets (const vector<Vec3>& coordsA,
1457 const vector<Vec3>& coordsB,
1459 const IsTriangleRelevantT& isTriangleRelevant,
1460 const char* ignoredTriangleDescription = DE_NULL)
1462 typedef tcu::Vector<Vec3, 3> Triangle;
1463 typedef LexCompare<Triangle, 3, VecLexLessThan<3> > TriangleLexLessThan;
1464 typedef std::set<Triangle, TriangleLexLessThan> TriangleSet;
1466 DE_ASSERT(coordsA.size() % 3 == 0 && coordsB.size() % 3 == 0);
1468 const int numTrianglesA = (int)coordsA.size()/3;
1469 const int numTrianglesB = (int)coordsB.size()/3;
1470 TriangleSet trianglesA;
1471 TriangleSet trianglesB;
1473 for (int aOrB = 0; aOrB < 2; aOrB++)
1475 const vector<Vec3>& coords = aOrB == 0 ? coordsA : coordsB;
1476 const int numTriangles = aOrB == 0 ? numTrianglesA : numTrianglesB;
1477 TriangleSet& triangles = aOrB == 0 ? trianglesA : trianglesB;
1479 for (int triNdx = 0; triNdx < numTriangles; triNdx++)
1481 Triangle triangle(coords[3*triNdx + 0],
1482 coords[3*triNdx + 1],
1483 coords[3*triNdx + 2]);
1485 if (isTriangleRelevant(triangle.getPtr()))
1487 std::sort(triangle.getPtr(), triangle.getPtr()+3, VecLexLessThan<3>());
1488 triangles.insert(triangle);
1494 TriangleSet::const_iterator aIt = trianglesA.begin();
1495 TriangleSet::const_iterator bIt = trianglesB.begin();
1497 while (aIt != trianglesA.end() || bIt != trianglesB.end())
1499 const bool aEnd = aIt == trianglesA.end();
1500 const bool bEnd = bIt == trianglesB.end();
1502 if (aEnd || bEnd || *aIt != *bIt)
1504 log << TestLog::Message << "Failure: triangle sets in two cases are not equal (when ignoring triangle and vertex order"
1505 << (ignoredTriangleDescription == DE_NULL ? "" : string() + ", and " + ignoredTriangleDescription) << ")" << TestLog::EndMessage;
1507 if (!aEnd && (bEnd || TriangleLexLessThan()(*aIt, *bIt)))
1508 log << TestLog::Message << "Note: e.g. triangle " << *aIt << " exists for first case but not for second" << TestLog::EndMessage;
1510 log << TestLog::Message << "Note: e.g. triangle " << *bIt << " exists for second case but not for first" << TestLog::EndMessage;
1523 static bool compareTriangleSets (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, TestLog& log)
1525 return compareTriangleSets(coordsA, coordsB, log, ConstantUnaryPredicate<const Vec3*, true>());
1528 static void checkExtensionSupport (Context& context, const char* extName)
1530 if (!context.getContextInfo().isExtensionSupported(extName))
1531 throw tcu::NotSupportedError(string(extName) + " not supported");
1534 static void checkTessellationSupport (Context& context)
1536 checkExtensionSupport(context, "GL_EXT_tessellation_shader");
1539 // Draw primitives with shared edges and check that no cracks are visible at the shared edges.
1540 class CommonEdgeCase : public TestCase
1545 CASETYPE_BASIC = 0, //!< Order patch vertices such that when two patches share a vertex, it's at the same index for both.
1546 CASETYPE_PRECISE, //!< Vertex indices don't match like for CASETYPE_BASIC, but other measures are taken, using the 'precise' qualifier.
1551 CommonEdgeCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing, CaseType caseType)
1552 : TestCase (context, name, description)
1553 , m_primitiveType (primitiveType)
1554 , m_spacing (spacing)
1555 , m_caseType (caseType)
1557 DE_ASSERT(m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES || m_primitiveType == TESSPRIMITIVETYPE_QUADS);
1562 IterateResult iterate (void);
1565 static const int RENDER_SIZE = 256;
1567 const TessPrimitiveType m_primitiveType;
1568 const SpacingMode m_spacing;
1569 const CaseType m_caseType;
1571 SharedPtr<const ShaderProgram> m_program;
1574 void CommonEdgeCase::init (void)
1576 checkTessellationSupport(m_context);
1578 if (m_caseType == CASETYPE_PRECISE)
1579 checkExtensionSupport(m_context, "GL_EXT_gpu_shader5");
1581 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
1583 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
1584 << glu::VertexSource ("#version 310 es\n"
1586 "in highp vec2 in_v_position;\n"
1587 "in highp float in_v_tessParam;\n"
1589 "out highp vec2 in_tc_position;\n"
1590 "out highp float in_tc_tessParam;\n"
1592 "void main (void)\n"
1594 " in_tc_position = in_v_position;\n"
1595 " in_tc_tessParam = in_v_tessParam;\n"
1598 << glu::TessellationControlSource ("#version 310 es\n"
1599 "#extension GL_EXT_tessellation_shader : require\n"
1600 + string(m_caseType == CASETYPE_PRECISE ? "#extension GL_EXT_gpu_shader5 : require\n" : "") +
1602 "layout (vertices = " + string(m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "3" : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? "4" : DE_NULL) + ") out;\n"
1604 "in highp vec2 in_tc_position[];\n"
1605 "in highp float in_tc_tessParam[];\n"
1607 "out highp vec2 in_te_position[];\n"
1609 + (m_caseType == CASETYPE_PRECISE ? "precise gl_TessLevelOuter;\n\n" : "") +
1610 "void main (void)\n"
1612 " in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];\n"
1614 " gl_TessLevelInner[0] = 5.0;\n"
1615 " gl_TessLevelInner[1] = 5.0;\n"
1617 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
1618 " gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[2]);\n"
1619 " gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[0]);\n"
1620 " gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[1]);\n"
1621 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ?
1622 " gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[2]);\n"
1623 " gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[0]);\n"
1624 " gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[3] + in_tc_tessParam[1]);\n"
1625 " gl_TessLevelOuter[3] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[3]);\n"
1629 << glu::TessellationEvaluationSource ("#version 310 es\n"
1630 "#extension GL_EXT_tessellation_shader : require\n"
1631 + string(m_caseType == CASETYPE_PRECISE ? "#extension GL_EXT_gpu_shader5 : require\n" : "") +
1633 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing) +
1635 "in highp vec2 in_te_position[];\n"
1637 "out mediump vec4 in_f_color;\n"
1639 + (m_caseType == CASETYPE_PRECISE ? "precise gl_Position;\n\n" : "") +
1640 "void main (void)\n"
1642 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
1643 " highp vec2 pos = gl_TessCoord.x*in_te_position[0] + gl_TessCoord.y*in_te_position[1] + gl_TessCoord.z*in_te_position[2];\n"
1645 " highp float f = sqrt(3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z))) * 0.5 + 0.5;\n"
1646 " in_f_color = vec4(gl_TessCoord*f, 1.0);\n"
1647 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ?
1649 + (m_caseType == CASETYPE_BASIC ?
1650 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0]\n"
1651 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1]\n"
1652 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[2]\n"
1653 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[3];\n"
1654 : m_caseType == CASETYPE_PRECISE ?
1655 " highp vec2 a = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0];\n"
1656 " highp vec2 b = ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1];\n"
1657 " highp vec2 c = (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[2];\n"
1658 " highp vec2 d = ( gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[3];\n"
1659 " highp vec2 pos = a+b+c+d;\n"
1662 " highp float f = sqrt(1.0 - 2.0 * max(abs(gl_TessCoord.x - 0.5), abs(gl_TessCoord.y - 0.5)))*0.5 + 0.5;\n"
1663 " in_f_color = vec4(0.1, gl_TessCoord.xy*f, 1.0);\n"
1666 " // Offset the position slightly, based on the parity of the bits in the float representation.\n"
1667 " // This is done to detect possible small differences in edge vertex positions between patches.\n"
1668 " uvec2 bits = floatBitsToUint(pos);\n"
1669 " uint numBits = 0u;\n"
1670 " for (uint i = 0u; i < 32u; i++)\n"
1671 " numBits += ((bits[0] >> i) & 1u) + ((bits[1] >> i) & 1u);\n"
1672 " pos += float(numBits&1u)*0.04;\n"
1674 " gl_Position = vec4(pos, 0.0, 1.0);\n"
1677 << glu::FragmentSource ("#version 310 es\n"
1679 "layout (location = 0) out mediump vec4 o_color;\n"
1681 "in mediump vec4 in_f_color;\n"
1683 "void main (void)\n"
1685 " o_color = in_f_color;\n"
1688 m_testCtx.getLog() << *m_program;
1689 if (!m_program->isOk())
1690 TCU_FAIL("Program compilation failed");
1693 void CommonEdgeCase::deinit (void)
1698 CommonEdgeCase::IterateResult CommonEdgeCase::iterate (void)
1700 TestLog& log = m_testCtx.getLog();
1701 const RenderContext& renderCtx = m_context.getRenderContext();
1702 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
1703 const deUint32 programGL = m_program->getProgram();
1704 const glw::Functions& gl = renderCtx.getFunctions();
1706 const int gridWidth = 4;
1707 const int gridHeight = 4;
1708 const int numVertices = (gridWidth+1)*(gridHeight+1);
1709 const int numIndices = gridWidth*gridHeight * (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3*2 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4 : -1);
1710 const int numPosCompsPerVertex = 2;
1711 const int totalNumPosComps = numPosCompsPerVertex*numVertices;
1712 vector<float> gridPosComps;
1713 vector<float> gridTessParams;
1714 vector<deUint16> gridIndices;
1716 gridPosComps.reserve(totalNumPosComps);
1717 gridTessParams.reserve(numVertices);
1718 gridIndices.reserve(numIndices);
1721 for (int i = 0; i < gridHeight+1; i++)
1722 for (int j = 0; j < gridWidth+1; j++)
1724 gridPosComps.push_back(-1.0f + 2.0f * ((float)j + 0.5f) / (float)(gridWidth+1));
1725 gridPosComps.push_back(-1.0f + 2.0f * ((float)i + 0.5f) / (float)(gridHeight+1));
1726 gridTessParams.push_back((float)(i*(gridWidth+1) + j) / (float)(numVertices-1));
1730 // Generate patch vertex indices.
1731 // \note If CASETYPE_BASIC, the vertices are ordered such that when multiple
1732 // triangles/quads share a vertex, it's at the same index for everyone.
1734 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
1736 for (int i = 0; i < gridHeight; i++)
1737 for (int j = 0; j < gridWidth; j++)
1739 const deUint16 corners[4] =
1741 (deUint16)((i+0)*(gridWidth+1) + j+0),
1742 (deUint16)((i+0)*(gridWidth+1) + j+1),
1743 (deUint16)((i+1)*(gridWidth+1) + j+0),
1744 (deUint16)((i+1)*(gridWidth+1) + j+1)
1747 const int secondTriangleVertexIndexOffset = m_caseType == CASETYPE_BASIC ? 0
1748 : m_caseType == CASETYPE_PRECISE ? 1
1750 DE_ASSERT(secondTriangleVertexIndexOffset != -1);
1752 for (int k = 0; k < 3; k++)
1753 gridIndices.push_back(corners[(k+0 + i + (2-j%3)) % 3]);
1754 for (int k = 0; k < 3; k++)
1755 gridIndices.push_back(corners[(k+2 + i + (2-j%3) + secondTriangleVertexIndexOffset) % 3 + 1]);
1758 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
1760 for (int i = 0; i < gridHeight; i++)
1761 for (int j = 0; j < gridWidth; j++)
1763 // \note The vertices are ordered such that when multiple quads
1764 // share a vertices, it's at the same index for everyone.
1765 for (int m = 0; m < 2; m++)
1766 for (int n = 0; n < 2; n++)
1767 gridIndices.push_back((deUint16)((i+(i+m)%2)*(gridWidth+1) + j+(j+n)%2));
1769 if(m_caseType == CASETYPE_PRECISE && (i+j) % 2 == 0)
1770 std::reverse(gridIndices.begin() + (gridIndices.size() - 4),
1771 gridIndices.begin() + gridIndices.size());
1777 DE_ASSERT((int)gridPosComps.size() == totalNumPosComps);
1778 DE_ASSERT((int)gridTessParams.size() == numVertices);
1779 DE_ASSERT((int)gridIndices.size() == numIndices);
1781 setViewport(gl, viewport);
1782 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
1783 gl.useProgram(programGL);
1786 gl.patchParameteri(GL_PATCH_VERTICES, m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4 : -1);
1787 gl.clear(GL_COLOR_BUFFER_BIT);
1789 const glu::VertexArrayBinding attrBindings[] =
1791 glu::va::Float("in_v_position", numPosCompsPerVertex, numVertices, 0, &gridPosComps[0]),
1792 glu::va::Float("in_v_tessParam", 1, numVertices, 0, &gridTessParams[0])
1795 glu::draw(renderCtx, programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
1796 glu::pr::Patches((int)gridIndices.size(), &gridIndices[0]));
1797 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
1801 const tcu::Surface rendered = getPixels(renderCtx, viewport);
1803 log << TestLog::Image("RenderedImage", "Rendered Image", rendered)
1804 << TestLog::Message << "Note: coloring is done to clarify the positioning and orientation of the "
1805 << (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "triangles" : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? "quads" : DE_NULL)
1806 << "; the color of a vertex corresponds to the index of that vertex in the patch"
1807 << TestLog::EndMessage;
1809 if (m_caseType == CASETYPE_BASIC)
1810 log << TestLog::Message << "Note: each shared vertex has the same index among the primitives it belongs to" << TestLog::EndMessage;
1811 else if (m_caseType == CASETYPE_PRECISE)
1812 log << TestLog::Message << "Note: the 'precise' qualifier is used to avoid cracks between primitives" << TestLog::EndMessage;
1816 // Ad-hoc result verification - check that a certain rectangle in the image contains no black pixels.
1818 const int startX = (int)(0.15f * (float)rendered.getWidth());
1819 const int endX = (int)(0.85f * (float)rendered.getWidth());
1820 const int startY = (int)(0.15f * (float)rendered.getHeight());
1821 const int endY = (int)(0.85f * (float)rendered.getHeight());
1823 for (int y = startY; y < endY; y++)
1824 for (int x = startX; x < endX; x++)
1826 const tcu::RGBA pixel = rendered.getPixel(x, y);
1828 if (pixel.getRed() == 0 && pixel.getGreen() == 0 && pixel.getBlue() == 0)
1830 log << TestLog::Message << "Failure: there seem to be cracks in the rendered result" << TestLog::EndMessage
1831 << TestLog::Message << "Note: pixel with zero r, g and b channels found at " << tcu::IVec2(x, y) << TestLog::EndMessage;
1833 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
1838 log << TestLog::Message << "Success: there seem to be no cracks in the rendered result" << TestLog::EndMessage;
1841 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1845 // Check tessellation coordinates (read with transform feedback).
1846 class TessCoordCase : public TestCase
1849 TessCoordCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing)
1850 : TestCase (context, name, description)
1851 , m_primitiveType (primitiveType)
1852 , m_spacing (spacing)
1858 IterateResult iterate (void);
1867 static const int RENDER_SIZE = 16;
1869 vector<TessLevels> genTessLevelCases (void) const;
1871 const TessPrimitiveType m_primitiveType;
1872 const SpacingMode m_spacing;
1874 SharedPtr<const ShaderProgram> m_program;
1877 void TessCoordCase::init (void)
1879 checkTessellationSupport(m_context);
1880 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
1882 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
1883 << glu::VertexSource ("#version 310 es\n"
1885 "void main (void)\n"
1889 << glu::TessellationControlSource ("#version 310 es\n"
1890 "#extension GL_EXT_tessellation_shader : require\n"
1892 "layout (vertices = 1) out;\n"
1894 "uniform mediump float u_tessLevelInner0;\n"
1895 "uniform mediump float u_tessLevelInner1;\n"
1897 "uniform mediump float u_tessLevelOuter0;\n"
1898 "uniform mediump float u_tessLevelOuter1;\n"
1899 "uniform mediump float u_tessLevelOuter2;\n"
1900 "uniform mediump float u_tessLevelOuter3;\n"
1902 "void main (void)\n"
1904 " gl_TessLevelInner[0] = u_tessLevelInner0;\n"
1905 " gl_TessLevelInner[1] = u_tessLevelInner1;\n"
1907 " gl_TessLevelOuter[0] = u_tessLevelOuter0;\n"
1908 " gl_TessLevelOuter[1] = u_tessLevelOuter1;\n"
1909 " gl_TessLevelOuter[2] = u_tessLevelOuter2;\n"
1910 " gl_TessLevelOuter[3] = u_tessLevelOuter3;\n"
1913 << glu::TessellationEvaluationSource ("#version 310 es\n"
1914 "#extension GL_EXT_tessellation_shader : require\n"
1916 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, true) +
1918 "out highp vec3 out_te_tessCoord;\n"
1920 "void main (void)\n"
1922 " out_te_tessCoord = gl_TessCoord;\n"
1923 " gl_Position = vec4(gl_TessCoord.xy*1.6 - 0.8, 0.0, 1.0);\n"
1926 << glu::FragmentSource ("#version 310 es\n"
1928 "layout (location = 0) out mediump vec4 o_color;\n"
1930 "void main (void)\n"
1932 " o_color = vec4(1.0);\n"
1935 << glu::TransformFeedbackVarying ("out_te_tessCoord")
1936 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
1938 m_testCtx.getLog() << *m_program;
1939 if (!m_program->isOk())
1940 TCU_FAIL("Program compilation failed");
1943 void TessCoordCase::deinit (void)
1948 vector<TessCoordCase::TessLevels> TessCoordCase::genTessLevelCases (void) const
1950 static const TessLevels rawTessLevelCases[] =
1952 { { 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
1953 { { 63.0f, 24.0f }, { 15.0f, 42.0f, 10.0f, 12.0f } },
1954 { { 3.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } },
1955 { { 4.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
1956 { { 2.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } },
1957 { { 5.0f, 6.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
1958 { { 1.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
1959 { { 5.0f, 1.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
1960 { { 5.2f, 1.6f }, { 2.9f, 3.4f, 1.5f, 4.1f } }
1963 if (m_spacing == SPACINGMODE_EQUAL)
1964 return vector<TessLevels>(DE_ARRAY_BEGIN(rawTessLevelCases), DE_ARRAY_END(rawTessLevelCases));
1967 vector<TessLevels> result;
1968 result.reserve(DE_LENGTH_OF_ARRAY(rawTessLevelCases));
1970 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(rawTessLevelCases); tessLevelCaseNdx++)
1972 TessLevels curTessLevelCase = rawTessLevelCases[tessLevelCaseNdx];
1974 float* const inner = &curTessLevelCase.inner[0];
1975 float* const outer = &curTessLevelCase.outer[0];
1977 for (int j = 0; j < 2; j++) inner[j] = (float)getClampedRoundedTessLevel(m_spacing, inner[j]);
1978 for (int j = 0; j < 4; j++) outer[j] = (float)getClampedRoundedTessLevel(m_spacing, outer[j]);
1980 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
1982 if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f)
1984 if (inner[0] == 1.0f)
1985 inner[0] = (float)getClampedRoundedTessLevel(m_spacing, inner[0] + 0.1f);
1988 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
1990 if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f || outer[3] > 1.0f)
1992 if (inner[0] == 1.0f) inner[0] = (float)getClampedRoundedTessLevel(m_spacing, inner[0] + 0.1f);
1993 if (inner[1] == 1.0f) inner[1] = (float)getClampedRoundedTessLevel(m_spacing, inner[1] + 0.1f);
1997 result.push_back(curTessLevelCase);
2000 DE_ASSERT((int)result.size() == DE_LENGTH_OF_ARRAY(rawTessLevelCases));
2005 TessCoordCase::IterateResult TessCoordCase::iterate (void)
2007 typedef TransformFeedbackHandler<Vec3> TFHandler;
2009 TestLog& log = m_testCtx.getLog();
2010 const RenderContext& renderCtx = m_context.getRenderContext();
2011 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2012 const deUint32 programGL = m_program->getProgram();
2013 const glw::Functions& gl = renderCtx.getFunctions();
2015 const int tessLevelInner0Loc = gl.getUniformLocation(programGL, "u_tessLevelInner0");
2016 const int tessLevelInner1Loc = gl.getUniformLocation(programGL, "u_tessLevelInner1");
2017 const int tessLevelOuter0Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter0");
2018 const int tessLevelOuter1Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter1");
2019 const int tessLevelOuter2Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter2");
2020 const int tessLevelOuter3Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter3");
2022 const vector<TessLevels> tessLevelCases = genTessLevelCases();
2023 vector<vector<Vec3> > caseReferences (tessLevelCases.size());
2025 for (int i = 0; i < (int)tessLevelCases.size(); i++)
2026 caseReferences[i] = generateReferenceTessCoords(m_primitiveType, m_spacing, &tessLevelCases[i].inner[0], &tessLevelCases[i].outer[0]);
2028 const int maxNumVertices = (int)std::max_element(caseReferences.begin(), caseReferences.end(), SizeLessThan<vector<Vec3> >())->size();
2029 const TFHandler tfHandler (m_context.getRenderContext(), maxNumVertices);
2031 bool success = true;
2033 setViewport(gl, viewport);
2034 gl.useProgram(programGL);
2036 gl.patchParameteri(GL_PATCH_VERTICES, 1);
2038 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < (int)tessLevelCases.size(); tessLevelCaseNdx++)
2040 const float* const innerLevels = &tessLevelCases[tessLevelCaseNdx].inner[0];
2041 const float* const outerLevels = &tessLevelCases[tessLevelCaseNdx].outer[0];
2043 log << TestLog::Message << "Tessellation levels: " << tessellationLevelsString(innerLevels, outerLevels, m_primitiveType) << TestLog::EndMessage;
2045 gl.uniform1f(tessLevelInner0Loc, innerLevels[0]);
2046 gl.uniform1f(tessLevelInner1Loc, innerLevels[1]);
2047 gl.uniform1f(tessLevelOuter0Loc, outerLevels[0]);
2048 gl.uniform1f(tessLevelOuter1Loc, outerLevels[1]);
2049 gl.uniform1f(tessLevelOuter2Loc, outerLevels[2]);
2050 gl.uniform1f(tessLevelOuter3Loc, outerLevels[3]);
2051 GLU_EXPECT_NO_ERROR(gl.getError(), "Setup failed");
2054 const vector<Vec3>& tessCoordsRef = caseReferences[tessLevelCaseNdx];
2055 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, GL_POINTS, 0, DE_NULL, 1);
2057 if (tfResult.numPrimitives != (int)tessCoordsRef.size())
2059 log << TestLog::Message << "Failure: GL reported GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN to be "
2060 << tfResult.numPrimitives << ", reference value is " << tessCoordsRef.size()
2061 << " (logging further info anyway)" << TestLog::EndMessage;
2065 log << TestLog::Message << "Note: GL reported GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN to be " << tfResult.numPrimitives << TestLog::EndMessage;
2067 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
2068 log << TestLog::Message << "Note: in the following visualization(s), the u=1, v=1, w=1 corners are at the right, top, and left corners, respectively" << TestLog::EndMessage;
2069 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS || m_primitiveType == TESSPRIMITIVETYPE_ISOLINES)
2070 log << TestLog::Message << "Note: in the following visualization(s), u and v coordinate go left-to-right and bottom-to-top, respectively" << TestLog::EndMessage;
2074 success = compareTessCoords(log, m_primitiveType, tessCoordsRef, tfResult.varying) && success;
2080 log << TestLog::Message << "All OK" << TestLog::EndMessage;
2083 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Invalid tessellation coordinates");
2087 // Check validity of fractional spacing modes. Draws a single isoline, reads tesscoords with transform feedback.
2088 class FractionalSpacingModeCase : public TestCase
2091 FractionalSpacingModeCase (Context& context, const char* name, const char* description, SpacingMode spacing)
2092 : TestCase (context, name, description)
2093 , m_spacing (spacing)
2095 DE_ASSERT(m_spacing == SPACINGMODE_FRACTIONAL_EVEN || m_spacing == SPACINGMODE_FRACTIONAL_ODD);
2100 IterateResult iterate (void);
2103 static const int RENDER_SIZE = 16;
2105 static vector<float> genTessLevelCases (void);
2107 const SpacingMode m_spacing;
2109 SharedPtr<const ShaderProgram> m_program;
2112 void FractionalSpacingModeCase::init (void)
2114 checkTessellationSupport(m_context);
2115 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
2117 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
2118 << glu::VertexSource ("#version 310 es\n"
2120 "void main (void)\n"
2124 << glu::TessellationControlSource ("#version 310 es\n"
2125 "#extension GL_EXT_tessellation_shader : require\n"
2127 "layout (vertices = 1) out;\n"
2129 "uniform mediump float u_tessLevelOuter1;\n"
2131 "void main (void)\n"
2133 " gl_TessLevelOuter[0] = 1.0;\n"
2134 " gl_TessLevelOuter[1] = u_tessLevelOuter1;\n"
2137 << glu::TessellationEvaluationSource ("#version 310 es\n"
2138 "#extension GL_EXT_tessellation_shader : require\n"
2140 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_ISOLINES, m_spacing, true) +
2142 "out highp float out_te_tessCoord;\n"
2144 "void main (void)\n"
2146 " out_te_tessCoord = gl_TessCoord.x;\n"
2147 " gl_Position = vec4(gl_TessCoord.xy*1.6 - 0.8, 0.0, 1.0);\n"
2150 << glu::FragmentSource ("#version 310 es\n"
2152 "layout (location = 0) out mediump vec4 o_color;\n"
2154 "void main (void)\n"
2156 " o_color = vec4(1.0);\n"
2159 << glu::TransformFeedbackVarying ("out_te_tessCoord")
2160 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
2162 m_testCtx.getLog() << *m_program;
2163 if (!m_program->isOk())
2164 TCU_FAIL("Program compilation failed");
2167 void FractionalSpacingModeCase::deinit (void)
2172 vector<float> FractionalSpacingModeCase::genTessLevelCases (void)
2174 vector<float> result;
2176 // Ranges [7.0 .. 8.0), [8.0 .. 9.0) and [9.0 .. 10.0)
2178 static const float rangeStarts[] = { 7.0f, 8.0f, 9.0f };
2179 const int numSamplesPerRange = 10;
2181 for (int rangeNdx = 0; rangeNdx < DE_LENGTH_OF_ARRAY(rangeStarts); rangeNdx++)
2182 for (int i = 0; i < numSamplesPerRange; i++)
2183 result.push_back(rangeStarts[rangeNdx] + (float)i/(float)numSamplesPerRange);
2186 // 0.3, 1.3, 2.3, ... , 62.3
2187 for (int i = 0; i <= 62; i++)
2188 result.push_back((float)i + 0.3f);
2193 FractionalSpacingModeCase::IterateResult FractionalSpacingModeCase::iterate (void)
2195 typedef TransformFeedbackHandler<float> TFHandler;
2197 TestLog& log = m_testCtx.getLog();
2198 const RenderContext& renderCtx = m_context.getRenderContext();
2199 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2200 const deUint32 programGL = m_program->getProgram();
2201 const glw::Functions& gl = renderCtx.getFunctions();
2203 const int tessLevelOuter1Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter1");
2205 // Second outer tessellation levels.
2206 const vector<float> tessLevelCases = genTessLevelCases();
2207 const int maxNumVertices = 1 + getClampedRoundedTessLevel(m_spacing, *std::max_element(tessLevelCases.begin(), tessLevelCases.end()));
2208 vector<float> additionalSegmentLengths;
2209 vector<int> additionalSegmentLocations;
2211 const TFHandler tfHandler (m_context.getRenderContext(), maxNumVertices);
2213 bool success = true;
2215 setViewport(gl, viewport);
2216 gl.useProgram(programGL);
2218 gl.patchParameteri(GL_PATCH_VERTICES, 1);
2220 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < (int)tessLevelCases.size(); tessLevelCaseNdx++)
2222 const float outerLevel1 = tessLevelCases[tessLevelCaseNdx];
2224 gl.uniform1f(tessLevelOuter1Loc, outerLevel1);
2225 GLU_EXPECT_NO_ERROR(gl.getError(), "Setup failed");
2228 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, GL_POINTS, 0, DE_NULL, 1);
2229 float additionalSegmentLength;
2230 int additionalSegmentLocation;
2232 success = verifyFractionalSpacingSingle(log, m_spacing, outerLevel1, tfResult.varying,
2233 additionalSegmentLength, additionalSegmentLocation);
2238 additionalSegmentLengths.push_back(additionalSegmentLength);
2239 additionalSegmentLocations.push_back(additionalSegmentLocation);
2244 success = verifyFractionalSpacingMultiple(log, m_spacing, tessLevelCases, additionalSegmentLengths, additionalSegmentLocations);
2246 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Invalid tessellation coordinates");
2250 // 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.
2251 class BasicVariousTessLevelsPosAttrCase : public TestCase
2254 BasicVariousTessLevelsPosAttrCase (Context& context,
2256 const char* description,
2257 TessPrimitiveType primitiveType,
2258 SpacingMode spacing,
2259 const char* referenceImagePathPrefix)
2260 : TestCase (context, name, description)
2261 , m_primitiveType (primitiveType)
2262 , m_spacing (spacing)
2263 , m_referenceImagePathPrefix (referenceImagePathPrefix)
2269 IterateResult iterate (void);
2272 virtual const glu::ProgramSources makeSources (TessPrimitiveType, SpacingMode, const char* vtxOutPosAttrName) const = DE_NULL;
2275 static const int RENDER_SIZE = 256;
2277 const TessPrimitiveType m_primitiveType;
2278 const SpacingMode m_spacing;
2279 const string m_referenceImagePathPrefix;
2281 SharedPtr<const ShaderProgram> m_program;
2284 void BasicVariousTessLevelsPosAttrCase::init (void)
2286 checkTessellationSupport(m_context);
2287 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
2290 glu::ProgramSources sources = makeSources(m_primitiveType, m_spacing, "in_tc_position");
2291 DE_ASSERT(sources.sources[glu::SHADERTYPE_TESSELLATION_CONTROL].empty());
2293 sources << glu::TessellationControlSource( "#version 310 es\n"
2294 "#extension GL_EXT_tessellation_shader : require\n"
2296 "layout (vertices = " + string(m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "3" : "4") + ") out;\n"
2298 "in highp vec2 in_tc_position[];\n"
2300 "out highp vec2 in_te_position[];\n"
2302 "uniform mediump float u_tessLevelInner0;\n"
2303 "uniform mediump float u_tessLevelInner1;\n"
2304 "uniform mediump float u_tessLevelOuter0;\n"
2305 "uniform mediump float u_tessLevelOuter1;\n"
2306 "uniform mediump float u_tessLevelOuter2;\n"
2307 "uniform mediump float u_tessLevelOuter3;\n"
2309 "void main (void)\n"
2311 " in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];\n"
2313 " gl_TessLevelInner[0] = u_tessLevelInner0;\n"
2314 " gl_TessLevelInner[1] = u_tessLevelInner1;\n"
2316 " gl_TessLevelOuter[0] = u_tessLevelOuter0;\n"
2317 " gl_TessLevelOuter[1] = u_tessLevelOuter1;\n"
2318 " gl_TessLevelOuter[2] = u_tessLevelOuter2;\n"
2319 " gl_TessLevelOuter[3] = u_tessLevelOuter3;\n"
2322 m_program = SharedPtr<const ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources));
2325 m_testCtx.getLog() << *m_program;
2326 if (!m_program->isOk())
2327 TCU_FAIL("Program compilation failed");
2330 void BasicVariousTessLevelsPosAttrCase::deinit (void)
2335 BasicVariousTessLevelsPosAttrCase::IterateResult BasicVariousTessLevelsPosAttrCase::iterate (void)
2341 } tessLevelCases[] =
2343 { { 9.0f, 9.0f }, { 9.0f, 9.0f, 9.0f, 9.0f } },
2344 { { 8.0f, 11.0f }, { 13.0f, 15.0f, 18.0f, 21.0f } },
2345 { { 17.0f, 14.0f }, { 3.0f, 6.0f, 9.0f, 12.0f } }
2348 TestLog& log = m_testCtx.getLog();
2349 const RenderContext& renderCtx = m_context.getRenderContext();
2350 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2351 const deUint32 programGL = m_program->getProgram();
2352 const glw::Functions& gl = renderCtx.getFunctions();
2353 const int patchSize = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3
2354 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4
2355 : m_primitiveType == TESSPRIMITIVETYPE_ISOLINES ? 4
2358 setViewport(gl, viewport);
2359 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
2360 gl.useProgram(programGL);
2362 gl.patchParameteri(GL_PATCH_VERTICES, patchSize);
2364 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(tessLevelCases); tessLevelCaseNdx++)
2366 float innerLevels[2];
2367 float outerLevels[4];
2369 for (int i = 0; i < DE_LENGTH_OF_ARRAY(innerLevels); i++)
2370 innerLevels[i] = (float)getClampedRoundedTessLevel(m_spacing, tessLevelCases[tessLevelCaseNdx].inner[i]);
2372 for (int i = 0; i < DE_LENGTH_OF_ARRAY(outerLevels); i++)
2373 outerLevels[i] = (float)getClampedRoundedTessLevel(m_spacing, tessLevelCases[tessLevelCaseNdx].outer[i]);
2375 log << TestLog::Message << "Tessellation levels: " << tessellationLevelsString(&innerLevels[0], &outerLevels[0], m_primitiveType) << TestLog::EndMessage;
2377 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelInner0"), innerLevels[0]);
2378 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelInner1"), innerLevels[1]);
2379 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter0"), outerLevels[0]);
2380 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter1"), outerLevels[1]);
2381 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter2"), outerLevels[2]);
2382 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter3"), outerLevels[3]);
2384 gl.clear(GL_COLOR_BUFFER_BIT);
2387 vector<Vec2> positions;
2388 positions.reserve(4);
2390 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
2392 positions.push_back(Vec2( 0.8f, 0.6f));
2393 positions.push_back(Vec2( 0.0f, -0.786f));
2394 positions.push_back(Vec2(-0.8f, 0.6f));
2396 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS || m_primitiveType == TESSPRIMITIVETYPE_ISOLINES)
2398 positions.push_back(Vec2(-0.8f, -0.8f));
2399 positions.push_back(Vec2( 0.8f, -0.8f));
2400 positions.push_back(Vec2(-0.8f, 0.8f));
2401 positions.push_back(Vec2( 0.8f, 0.8f));
2406 DE_ASSERT((int)positions.size() == patchSize);
2408 const glu::VertexArrayBinding attrBindings[] =
2410 glu::va::Float("in_v_position", 2, (int)positions.size(), 0, &positions[0].x())
2413 glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
2414 glu::pr::Patches(patchSize));
2415 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
2419 const tcu::Surface rendered = getPixels(renderCtx, viewport);
2420 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePathPrefix + "_" + de::toString(tessLevelCaseNdx) + ".png");
2421 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.002f, tcu::COMPARE_LOG_RESULT);
2425 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
2431 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2435 // Test that there are no obvious gaps in the triangulation of a tessellated triangle or quad.
2436 class BasicTriangleFillCoverCase : public BasicVariousTessLevelsPosAttrCase
2439 BasicTriangleFillCoverCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing, const char* referenceImagePathPrefix)
2440 : BasicVariousTessLevelsPosAttrCase (context, name, description, primitiveType, spacing, referenceImagePathPrefix)
2442 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
2448 checkExtensionSupport(m_context, "GL_EXT_gpu_shader5");
2449 BasicVariousTessLevelsPosAttrCase::init();
2452 const glu::ProgramSources makeSources (TessPrimitiveType primitiveType, SpacingMode spacing, const char* vtxOutPosAttrName) const
2454 return glu::ProgramSources()
2455 << glu::VertexSource ("#version 310 es\n"
2457 "in highp vec2 in_v_position;\n"
2459 "out highp vec2 " + string(vtxOutPosAttrName) + ";\n"
2461 "void main (void)\n"
2463 " " + vtxOutPosAttrName + " = in_v_position;\n"
2466 << glu::TessellationEvaluationSource ("#version 310 es\n"
2467 "#extension GL_EXT_tessellation_shader : require\n"
2468 "#extension GL_EXT_gpu_shader5 : require\n"
2470 + getTessellationEvaluationInLayoutString(primitiveType, spacing) +
2472 "in highp vec2 in_te_position[];\n"
2474 "precise gl_Position;\n"
2475 "void main (void)\n"
2477 + (primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
2479 " highp float d = 3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z));\n"
2480 " highp vec2 corner0 = in_te_position[0];\n"
2481 " highp vec2 corner1 = in_te_position[1];\n"
2482 " highp vec2 corner2 = in_te_position[2];\n"
2483 " highp vec2 pos = corner0*gl_TessCoord.x + corner1*gl_TessCoord.y + corner2*gl_TessCoord.z;\n"
2484 " highp vec2 fromCenter = pos - (corner0 + corner1 + corner2) / 3.0;\n"
2485 " highp float f = (1.0 - length(fromCenter)) * (1.5 - d);\n"
2486 " pos += 0.75 * f * fromCenter / (length(fromCenter) + 0.3);\n"
2487 " gl_Position = vec4(pos, 0.0, 1.0);\n"
2488 : primitiveType == TESSPRIMITIVETYPE_QUADS ?
2489 " highp vec2 corner0 = in_te_position[0];\n"
2490 " highp vec2 corner1 = in_te_position[1];\n"
2491 " highp vec2 corner2 = in_te_position[2];\n"
2492 " highp vec2 corner3 = in_te_position[3];\n"
2493 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
2494 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
2495 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n"
2496 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n"
2497 " highp float d = 2.0 * min(abs(gl_TessCoord.x-0.5), abs(gl_TessCoord.y-0.5));\n"
2498 " highp vec2 fromCenter = pos - (corner0 + corner1 + corner2 + corner3) / 4.0;\n"
2499 " highp float f = (1.0 - length(fromCenter)) * sqrt(1.7 - d);\n"
2500 " pos += 0.75 * f * fromCenter / (length(fromCenter) + 0.3);\n"
2501 " gl_Position = vec4(pos, 0.0, 1.0);\n"
2505 << glu::FragmentSource ("#version 310 es\n"
2507 "layout (location = 0) out mediump vec4 o_color;\n"
2509 "void main (void)\n"
2511 " o_color = vec4(1.0);\n"
2516 // Check that there are no obvious overlaps in the triangulation of a tessellated triangle or quad.
2517 class BasicTriangleFillNonOverlapCase : public BasicVariousTessLevelsPosAttrCase
2520 BasicTriangleFillNonOverlapCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing, const char* referenceImagePathPrefix)
2521 : BasicVariousTessLevelsPosAttrCase (context, name, description, primitiveType, spacing, referenceImagePathPrefix)
2523 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
2527 const glu::ProgramSources makeSources (TessPrimitiveType primitiveType, SpacingMode spacing, const char* vtxOutPosAttrName) const
2529 return glu::ProgramSources()
2530 << glu::VertexSource ("#version 310 es\n"
2532 "in highp vec2 in_v_position;\n"
2534 "out highp vec2 " + string(vtxOutPosAttrName) + ";\n"
2536 "void main (void)\n"
2538 " " + vtxOutPosAttrName + " = in_v_position;\n"
2541 << glu::TessellationEvaluationSource ("#version 310 es\n"
2542 "#extension GL_EXT_tessellation_shader : require\n"
2544 + getTessellationEvaluationInLayoutString(primitiveType, spacing) +
2546 "in highp vec2 in_te_position[];\n"
2548 "out mediump vec4 in_f_color;\n"
2550 "uniform mediump float u_tessLevelInner0;\n"
2551 "uniform mediump float u_tessLevelInner1;\n"
2553 "void main (void)\n"
2555 + (primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
2557 " highp vec2 corner0 = in_te_position[0];\n"
2558 " highp vec2 corner1 = in_te_position[1];\n"
2559 " highp vec2 corner2 = in_te_position[2];\n"
2560 " highp vec2 pos = corner0*gl_TessCoord.x + corner1*gl_TessCoord.y + corner2*gl_TessCoord.z;\n"
2561 " gl_Position = vec4(pos, 0.0, 1.0);\n"
2562 " highp int numConcentricTriangles = int(round(u_tessLevelInner0)) / 2 + 1;\n"
2563 " highp float d = 3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z));\n"
2564 " highp int phase = int(d*float(numConcentricTriangles)) % 3;\n"
2565 " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
2566 " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
2567 " : vec4(0.0, 0.0, 1.0, 1.0);\n"
2568 : primitiveType == TESSPRIMITIVETYPE_QUADS ?
2569 " highp vec2 corner0 = in_te_position[0];\n"
2570 " highp vec2 corner1 = in_te_position[1];\n"
2571 " highp vec2 corner2 = in_te_position[2];\n"
2572 " highp vec2 corner3 = in_te_position[3];\n"
2573 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
2574 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
2575 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n"
2576 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n"
2577 " gl_Position = vec4(pos, 0.0, 1.0);\n"
2578 " highp int phaseX = int(round((0.5 - abs(gl_TessCoord.x-0.5)) * u_tessLevelInner0));\n"
2579 " highp int phaseY = int(round((0.5 - abs(gl_TessCoord.y-0.5)) * u_tessLevelInner1));\n"
2580 " highp int phase = min(phaseX, phaseY) % 3;\n"
2581 " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
2582 " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
2583 " : vec4(0.0, 0.0, 1.0, 1.0);\n"
2587 << glu::FragmentSource ("#version 310 es\n"
2589 "layout (location = 0) out mediump vec4 o_color;\n"
2591 "in mediump vec4 in_f_color;\n"
2593 "void main (void)\n"
2595 " o_color = in_f_color;\n"
2600 // Basic isolines rendering case.
2601 class IsolinesRenderCase : public BasicVariousTessLevelsPosAttrCase
2604 IsolinesRenderCase (Context& context, const char* name, const char* description, SpacingMode spacing, const char* referenceImagePathPrefix)
2605 : BasicVariousTessLevelsPosAttrCase (context, name, description, TESSPRIMITIVETYPE_ISOLINES, spacing, referenceImagePathPrefix)
2610 const glu::ProgramSources makeSources (TessPrimitiveType primitiveType, SpacingMode spacing, const char* vtxOutPosAttrName) const
2612 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_ISOLINES);
2613 DE_UNREF(primitiveType);
2615 return glu::ProgramSources()
2616 << glu::VertexSource ("#version 310 es\n"
2618 "in highp vec2 in_v_position;\n"
2620 "out highp vec2 " + string(vtxOutPosAttrName) + ";\n"
2622 "void main (void)\n"
2624 " " + vtxOutPosAttrName + " = in_v_position;\n"
2627 << glu::TessellationEvaluationSource ("#version 310 es\n"
2628 "#extension GL_EXT_tessellation_shader : require\n"
2630 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_ISOLINES, spacing) +
2632 "in highp vec2 in_te_position[];\n"
2634 "out mediump vec4 in_f_color;\n"
2636 "uniform mediump float u_tessLevelOuter0;\n"
2637 "uniform mediump float u_tessLevelOuter1;\n"
2639 "void main (void)\n"
2641 " highp vec2 corner0 = in_te_position[0];\n"
2642 " highp vec2 corner1 = in_te_position[1];\n"
2643 " highp vec2 corner2 = in_te_position[2];\n"
2644 " highp vec2 corner3 = in_te_position[3];\n"
2645 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
2646 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
2647 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n"
2648 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n"
2649 " pos.y += 0.15*sin(gl_TessCoord.x*10.0);\n"
2650 " gl_Position = vec4(pos, 0.0, 1.0);\n"
2651 " highp int phaseX = int(round(gl_TessCoord.x*u_tessLevelOuter1));\n"
2652 " highp int phaseY = int(round(gl_TessCoord.y*u_tessLevelOuter0));\n"
2653 " highp int phase = (phaseX + phaseY) % 3;\n"
2654 " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
2655 " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
2656 " : vec4(0.0, 0.0, 1.0, 1.0);\n"
2659 << glu::FragmentSource ("#version 310 es\n"
2661 "layout (location = 0) out mediump vec4 o_color;\n"
2663 "in mediump vec4 in_f_color;\n"
2665 "void main (void)\n"
2667 " o_color = in_f_color;\n"
2672 // Test the "cw" and "ccw" TES input layout qualifiers.
2673 class WindingCase : public TestCase
2676 WindingCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, Winding winding)
2677 : TestCase (context, name, description)
2678 , m_primitiveType (primitiveType)
2679 , m_winding (winding)
2681 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
2686 IterateResult iterate (void);
2689 static const int RENDER_SIZE = 64;
2691 const TessPrimitiveType m_primitiveType;
2692 const Winding m_winding;
2694 SharedPtr<const ShaderProgram> m_program;
2697 void WindingCase::init (void)
2699 checkTessellationSupport(m_context);
2700 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
2702 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
2703 << glu::VertexSource ("#version 310 es\n"
2705 "void main (void)\n"
2709 << glu::TessellationControlSource ("#version 310 es\n"
2710 "#extension GL_EXT_tessellation_shader : require\n"
2712 "layout (vertices = 1) out;\n"
2714 "void main (void)\n"
2716 " gl_TessLevelInner[0] = 5.0;\n"
2717 " gl_TessLevelInner[1] = 5.0;\n"
2719 " gl_TessLevelOuter[0] = 5.0;\n"
2720 " gl_TessLevelOuter[1] = 5.0;\n"
2721 " gl_TessLevelOuter[2] = 5.0;\n"
2722 " gl_TessLevelOuter[3] = 5.0;\n"
2725 << glu::TessellationEvaluationSource ("#version 310 es\n"
2726 "#extension GL_EXT_tessellation_shader : require\n"
2728 + getTessellationEvaluationInLayoutString(m_primitiveType, m_winding) +
2730 "void main (void)\n"
2732 " gl_Position = vec4(gl_TessCoord.xy*2.0 - 1.0, 0.0, 1.0);\n"
2735 << glu::FragmentSource ("#version 310 es\n"
2737 "layout (location = 0) out mediump vec4 o_color;\n"
2739 "void main (void)\n"
2741 " o_color = vec4(1.0);\n"
2744 m_testCtx.getLog() << *m_program;
2745 if (!m_program->isOk())
2746 TCU_FAIL("Program compilation failed");
2749 void WindingCase::deinit (void)
2754 WindingCase::IterateResult WindingCase::iterate (void)
2756 TestLog& log = m_testCtx.getLog();
2757 const RenderContext& renderCtx = m_context.getRenderContext();
2758 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2759 const deUint32 programGL = m_program->getProgram();
2760 const glw::Functions& gl = renderCtx.getFunctions();
2761 const glu::VertexArray vao (renderCtx);
2763 bool success = true;
2765 setViewport(gl, viewport);
2766 gl.clearColor(1.0f, 0.0f, 0.0f, 1.0f);
2767 gl.useProgram(programGL);
2769 gl.patchParameteri(GL_PATCH_VERTICES, 1);
2771 gl.enable(GL_CULL_FACE);
2773 gl.bindVertexArray(*vao);
2775 log << TestLog::Message << "Face culling enabled" << TestLog::EndMessage;
2777 for (int frontFaceWinding = 0; frontFaceWinding < WINDING_LAST; frontFaceWinding++)
2779 log << TestLog::Message << "Setting glFrontFace(" << (frontFaceWinding == WINDING_CW ? "GL_CW" : "GL_CCW") << ")" << TestLog::EndMessage;
2781 gl.frontFace(frontFaceWinding == WINDING_CW ? GL_CW : GL_CCW);
2783 gl.clear(GL_COLOR_BUFFER_BIT);
2784 gl.drawArrays(GL_PATCHES, 0, 1);
2785 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
2788 const tcu::Surface rendered = getPixels(renderCtx, viewport);
2789 log << TestLog::Image("RenderedImage", "Rendered Image", rendered);
2792 const int totalNumPixels = rendered.getWidth()*rendered.getHeight();
2793 const int badPixelTolerance = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 5*de::max(rendered.getWidth(), rendered.getHeight()) : 0;
2795 int numWhitePixels = 0;
2796 int numRedPixels = 0;
2797 for (int y = 0; y < rendered.getHeight(); y++)
2798 for (int x = 0; x < rendered.getWidth(); x++)
2800 numWhitePixels += rendered.getPixel(x, y) == tcu::RGBA::white() ? 1 : 0;
2801 numRedPixels += rendered.getPixel(x, y) == tcu::RGBA::red() ? 1 : 0;
2804 DE_ASSERT(numWhitePixels + numRedPixels <= totalNumPixels);
2806 log << TestLog::Message << "Note: got " << numWhitePixels << " white and " << numRedPixels << " red pixels" << TestLog::EndMessage;
2808 if (totalNumPixels - numWhitePixels - numRedPixels > badPixelTolerance)
2810 log << TestLog::Message << "Failure: Got " << totalNumPixels - numWhitePixels - numRedPixels << " other than white or red pixels (maximum tolerance " << badPixelTolerance << ")" << TestLog::EndMessage;
2815 if ((Winding)frontFaceWinding == m_winding)
2817 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
2819 if (de::abs(numWhitePixels - totalNumPixels/2) > badPixelTolerance)
2821 log << TestLog::Message << "Failure: wrong number of white pixels; expected approximately " << totalNumPixels/2 << TestLog::EndMessage;
2826 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
2828 if (numWhitePixels != totalNumPixels)
2830 log << TestLog::Message << "Failure: expected only white pixels (full-viewport quad)" << TestLog::EndMessage;
2840 if (numWhitePixels != 0)
2842 log << TestLog::Message << "Failure: expected only red pixels (everything culled)" << TestLog::EndMessage;
2851 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image verification failed");
2855 // Test potentially differing input and output patch sizes.
2856 class PatchVertexCountCase : public TestCase
2859 PatchVertexCountCase (Context& context, const char* name, const char* description, int inputPatchSize, int outputPatchSize, const char* referenceImagePath)
2860 : TestCase (context, name, description)
2861 , m_inputPatchSize (inputPatchSize)
2862 , m_outputPatchSize (outputPatchSize)
2863 , m_referenceImagePath (referenceImagePath)
2869 IterateResult iterate (void);
2872 static const int RENDER_SIZE = 256;
2874 const int m_inputPatchSize;
2875 const int m_outputPatchSize;
2877 const string m_referenceImagePath;
2879 SharedPtr<const ShaderProgram> m_program;
2882 void PatchVertexCountCase::init (void)
2884 checkTessellationSupport(m_context);
2885 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
2887 const string inSizeStr = de::toString(m_inputPatchSize);
2888 const string outSizeStr = de::toString(m_outputPatchSize);
2890 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
2891 << glu::VertexSource ("#version 310 es\n"
2893 "in highp float in_v_attr;\n"
2895 "out highp float in_tc_attr;\n"
2897 "void main (void)\n"
2899 " in_tc_attr = in_v_attr;\n"
2902 << glu::TessellationControlSource ("#version 310 es\n"
2903 "#extension GL_EXT_tessellation_shader : require\n"
2905 "layout (vertices = " + outSizeStr + ") out;\n"
2907 "in highp float in_tc_attr[];\n"
2909 "out highp float in_te_attr[];\n"
2911 "void main (void)\n"
2913 " in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID*" + inSizeStr + "/" + outSizeStr + "];\n"
2915 " gl_TessLevelInner[0] = 5.0;\n"
2916 " gl_TessLevelInner[1] = 5.0;\n"
2918 " gl_TessLevelOuter[0] = 5.0;\n"
2919 " gl_TessLevelOuter[1] = 5.0;\n"
2920 " gl_TessLevelOuter[2] = 5.0;\n"
2921 " gl_TessLevelOuter[3] = 5.0;\n"
2924 << glu::TessellationEvaluationSource ("#version 310 es\n"
2925 "#extension GL_EXT_tessellation_shader : require\n"
2927 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_QUADS) +
2929 "in highp float in_te_attr[];\n"
2931 "out mediump vec4 in_f_color;\n"
2933 "void main (void)\n"
2935 " highp float x = gl_TessCoord.x*2.0 - 1.0;\n"
2936 " highp float y = gl_TessCoord.y - in_te_attr[int(round(gl_TessCoord.x*float(" + outSizeStr + "-1)))];\n"
2937 " gl_Position = vec4(x, y, 0.0, 1.0);\n"
2938 " in_f_color = vec4(1.0);\n"
2941 << glu::FragmentSource ("#version 310 es\n"
2943 "layout (location = 0) out mediump vec4 o_color;\n"
2945 "in mediump vec4 in_f_color;\n"
2947 "void main (void)\n"
2949 " o_color = in_f_color;\n"
2952 m_testCtx.getLog() << *m_program;
2953 if (!m_program->isOk())
2954 TCU_FAIL("Program compilation failed");
2957 void PatchVertexCountCase::deinit (void)
2962 PatchVertexCountCase::IterateResult PatchVertexCountCase::iterate (void)
2964 TestLog& log = m_testCtx.getLog();
2965 const RenderContext& renderCtx = m_context.getRenderContext();
2966 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2967 const deUint32 programGL = m_program->getProgram();
2968 const glw::Functions& gl = renderCtx.getFunctions();
2970 setViewport(gl, viewport);
2971 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
2972 gl.useProgram(programGL);
2974 log << TestLog::Message << "Note: input patch size is " << m_inputPatchSize << ", output patch size is " << m_outputPatchSize << TestLog::EndMessage;
2977 vector<float> attributeData;
2978 attributeData.reserve(m_inputPatchSize);
2980 for (int i = 0; i < m_inputPatchSize; i++)
2982 const float f = (float)i / (float)(m_inputPatchSize-1);
2983 attributeData.push_back(f*f);
2986 gl.patchParameteri(GL_PATCH_VERTICES, m_inputPatchSize);
2987 gl.clear(GL_COLOR_BUFFER_BIT);
2989 const glu::VertexArrayBinding attrBindings[] =
2991 glu::va::Float("in_v_attr", 1, (int)attributeData.size(), 0, &attributeData[0])
2994 glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
2995 glu::pr::Patches(m_inputPatchSize));
2996 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
3000 const tcu::Surface rendered = getPixels(renderCtx, viewport);
3001 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath);
3002 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
3004 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image comparison failed");
3009 // Test per-patch inputs/outputs.
3010 class PerPatchDataCase : public TestCase
3015 CASETYPE_PRIMITIVE_ID_TCS = 0,
3016 CASETYPE_PRIMITIVE_ID_TES,
3017 CASETYPE_PATCH_VERTICES_IN_TCS,
3018 CASETYPE_PATCH_VERTICES_IN_TES,
3019 CASETYPE_TESS_LEVEL_INNER0_TES,
3020 CASETYPE_TESS_LEVEL_INNER1_TES,
3021 CASETYPE_TESS_LEVEL_OUTER0_TES,
3022 CASETYPE_TESS_LEVEL_OUTER1_TES,
3023 CASETYPE_TESS_LEVEL_OUTER2_TES,
3024 CASETYPE_TESS_LEVEL_OUTER3_TES,
3029 PerPatchDataCase (Context& context, const char* name, const char* description, CaseType caseType, const char* referenceImagePath)
3030 : TestCase (context, name, description)
3031 , m_caseType (caseType)
3032 , m_referenceImagePath (caseTypeUsesRefImageFromFile(caseType) ? referenceImagePath : "")
3034 DE_ASSERT(caseTypeUsesRefImageFromFile(caseType) == (referenceImagePath != DE_NULL));
3039 IterateResult iterate (void);
3041 static const char* getCaseTypeName (CaseType);
3042 static const char* getCaseTypeDescription (CaseType);
3043 static bool caseTypeUsesRefImageFromFile (CaseType);
3046 static const int RENDER_SIZE = 256;
3047 static const int INPUT_PATCH_SIZE;
3048 static const int OUTPUT_PATCH_SIZE;
3050 const CaseType m_caseType;
3051 const string m_referenceImagePath;
3053 SharedPtr<const ShaderProgram> m_program;
3056 const int PerPatchDataCase::INPUT_PATCH_SIZE = 10;
3057 const int PerPatchDataCase::OUTPUT_PATCH_SIZE = 5;
3059 const char* PerPatchDataCase::getCaseTypeName (CaseType type)
3063 case CASETYPE_PRIMITIVE_ID_TCS: return "primitive_id_tcs";
3064 case CASETYPE_PRIMITIVE_ID_TES: return "primitive_id_tes";
3065 case CASETYPE_PATCH_VERTICES_IN_TCS: return "patch_vertices_in_tcs";
3066 case CASETYPE_PATCH_VERTICES_IN_TES: return "patch_vertices_in_tes";
3067 case CASETYPE_TESS_LEVEL_INNER0_TES: return "tess_level_inner_0_tes";
3068 case CASETYPE_TESS_LEVEL_INNER1_TES: return "tess_level_inner_1_tes";
3069 case CASETYPE_TESS_LEVEL_OUTER0_TES: return "tess_level_outer_0_tes";
3070 case CASETYPE_TESS_LEVEL_OUTER1_TES: return "tess_level_outer_1_tes";
3071 case CASETYPE_TESS_LEVEL_OUTER2_TES: return "tess_level_outer_2_tes";
3072 case CASETYPE_TESS_LEVEL_OUTER3_TES: return "tess_level_outer_3_tes";
3079 const char* PerPatchDataCase::getCaseTypeDescription (CaseType type)
3083 case CASETYPE_PRIMITIVE_ID_TCS: return "Read gl_PrimitiveID in TCS and pass it as patch output to TES";
3084 case CASETYPE_PRIMITIVE_ID_TES: return "Read gl_PrimitiveID in TES";
3085 case CASETYPE_PATCH_VERTICES_IN_TCS: return "Read gl_PatchVerticesIn in TCS and pass it as patch output to TES";
3086 case CASETYPE_PATCH_VERTICES_IN_TES: return "Read gl_PatchVerticesIn in TES";
3087 case CASETYPE_TESS_LEVEL_INNER0_TES: return "Read gl_TessLevelInner[0] in TES";
3088 case CASETYPE_TESS_LEVEL_INNER1_TES: return "Read gl_TessLevelInner[1] in TES";
3089 case CASETYPE_TESS_LEVEL_OUTER0_TES: return "Read gl_TessLevelOuter[0] in TES";
3090 case CASETYPE_TESS_LEVEL_OUTER1_TES: return "Read gl_TessLevelOuter[1] in TES";
3091 case CASETYPE_TESS_LEVEL_OUTER2_TES: return "Read gl_TessLevelOuter[2] in TES";
3092 case CASETYPE_TESS_LEVEL_OUTER3_TES: return "Read gl_TessLevelOuter[3] in TES";
3099 bool PerPatchDataCase::caseTypeUsesRefImageFromFile (CaseType type)
3103 case CASETYPE_PRIMITIVE_ID_TCS:
3104 case CASETYPE_PRIMITIVE_ID_TES:
3112 void PerPatchDataCase::init (void)
3114 checkTessellationSupport(m_context);
3115 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
3117 DE_ASSERT(OUTPUT_PATCH_SIZE < INPUT_PATCH_SIZE);
3119 const string inSizeStr = de::toString(INPUT_PATCH_SIZE);
3120 const string outSizeStr = de::toString(OUTPUT_PATCH_SIZE);
3122 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
3123 << glu::VertexSource ("#version 310 es\n"
3125 "in highp float in_v_attr;\n"
3127 "out highp float in_tc_attr;\n"
3129 "void main (void)\n"
3131 " in_tc_attr = in_v_attr;\n"
3134 << glu::TessellationControlSource ("#version 310 es\n"
3135 "#extension GL_EXT_tessellation_shader : require\n"
3137 "layout (vertices = " + outSizeStr + ") out;\n"
3139 "in highp float in_tc_attr[];\n"
3141 "out highp float in_te_attr[];\n"
3142 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS ? "patch out mediump int in_te_primitiveIDFromTCS;\n"
3143 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "patch out mediump int in_te_patchVerticesInFromTCS;\n"
3146 "void main (void)\n"
3148 " in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID];\n"
3149 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS ? "\tin_te_primitiveIDFromTCS = gl_PrimitiveID;\n"
3150 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "\tin_te_patchVerticesInFromTCS = gl_PatchVerticesIn;\n"
3153 " gl_TessLevelInner[0] = 9.0;\n"
3154 " gl_TessLevelInner[1] = 8.0;\n"
3156 " gl_TessLevelOuter[0] = 7.0;\n"
3157 " gl_TessLevelOuter[1] = 6.0;\n"
3158 " gl_TessLevelOuter[2] = 5.0;\n"
3159 " gl_TessLevelOuter[3] = 4.0;\n"
3162 << glu::TessellationEvaluationSource ("#version 310 es\n"
3163 "#extension GL_EXT_tessellation_shader : require\n"
3165 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_QUADS) +
3167 "in highp float in_te_attr[];\n"
3168 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS ? "patch in mediump int in_te_primitiveIDFromTCS;\n"
3169 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "patch in mediump int in_te_patchVerticesInFromTCS;\n"
3172 "out mediump vec4 in_f_color;\n"
3174 "uniform highp float u_xScale;\n"
3176 "void main (void)\n"
3178 " highp float x = (gl_TessCoord.x*u_xScale + in_te_attr[0]) * 2.0 - 1.0;\n"
3179 " highp float y = gl_TessCoord.y*2.0 - 1.0;\n"
3180 " gl_Position = vec4(x, y, 0.0, 1.0);\n"
3181 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS ? "\tbool ok = in_te_primitiveIDFromTCS == 3;\n"
3182 : m_caseType == CASETYPE_PRIMITIVE_ID_TES ? "\tbool ok = gl_PrimitiveID == 3;\n"
3183 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "\tbool ok = in_te_patchVerticesInFromTCS == " + inSizeStr + ";\n"
3184 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TES ? "\tbool ok = gl_PatchVerticesIn == " + outSizeStr + ";\n"
3185 : m_caseType == CASETYPE_TESS_LEVEL_INNER0_TES ? "\tbool ok = abs(gl_TessLevelInner[0] - 9.0) < 0.1f;\n"
3186 : m_caseType == CASETYPE_TESS_LEVEL_INNER1_TES ? "\tbool ok = abs(gl_TessLevelInner[1] - 8.0) < 0.1f;\n"
3187 : m_caseType == CASETYPE_TESS_LEVEL_OUTER0_TES ? "\tbool ok = abs(gl_TessLevelOuter[0] - 7.0) < 0.1f;\n"
3188 : m_caseType == CASETYPE_TESS_LEVEL_OUTER1_TES ? "\tbool ok = abs(gl_TessLevelOuter[1] - 6.0) < 0.1f;\n"
3189 : m_caseType == CASETYPE_TESS_LEVEL_OUTER2_TES ? "\tbool ok = abs(gl_TessLevelOuter[2] - 5.0) < 0.1f;\n"
3190 : m_caseType == CASETYPE_TESS_LEVEL_OUTER3_TES ? "\tbool ok = abs(gl_TessLevelOuter[3] - 4.0) < 0.1f;\n"
3192 " in_f_color = ok ? vec4(1.0) : vec4(vec3(0.0), 1.0);\n"
3195 << glu::FragmentSource ("#version 310 es\n"
3197 "layout (location = 0) out mediump vec4 o_color;\n"
3199 "in mediump vec4 in_f_color;\n"
3201 "void main (void)\n"
3203 " o_color = in_f_color;\n"
3206 m_testCtx.getLog() << *m_program;
3207 if (!m_program->isOk())
3208 TCU_FAIL("Program compilation failed");
3211 void PerPatchDataCase::deinit (void)
3216 PerPatchDataCase::IterateResult PerPatchDataCase::iterate (void)
3218 TestLog& log = m_testCtx.getLog();
3219 const RenderContext& renderCtx = m_context.getRenderContext();
3220 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
3221 const deUint32 programGL = m_program->getProgram();
3222 const glw::Functions& gl = renderCtx.getFunctions();
3224 setViewport(gl, viewport);
3225 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3226 gl.useProgram(programGL);
3228 log << TestLog::Message << "Note: input patch size is " << INPUT_PATCH_SIZE << ", output patch size is " << OUTPUT_PATCH_SIZE << TestLog::EndMessage;
3231 const int numPrimitives = m_caseType == CASETYPE_PRIMITIVE_ID_TCS || m_caseType == CASETYPE_PRIMITIVE_ID_TES ? 8 : 1;
3233 vector<float> attributeData;
3234 attributeData.reserve(numPrimitives*INPUT_PATCH_SIZE);
3236 for (int i = 0; i < numPrimitives; i++)
3238 attributeData.push_back((float)i / (float)numPrimitives);
3239 for (int j = 0; j < INPUT_PATCH_SIZE-1; j++)
3240 attributeData.push_back(0.0f);
3243 gl.patchParameteri(GL_PATCH_VERTICES, INPUT_PATCH_SIZE);
3244 gl.clear(GL_COLOR_BUFFER_BIT);
3246 gl.uniform1f(gl.getUniformLocation(programGL, "u_xScale"), 1.0f / (float)numPrimitives);
3248 const glu::VertexArrayBinding attrBindings[] =
3250 glu::va::Float("in_v_attr", 1, (int)attributeData.size(), 0, &attributeData[0])
3253 glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
3254 glu::pr::Patches(numPrimitives*INPUT_PATCH_SIZE));
3255 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
3259 const tcu::Surface rendered = getPixels(renderCtx, viewport);
3261 if (m_caseType == CASETYPE_PRIMITIVE_ID_TCS || m_caseType == CASETYPE_PRIMITIVE_ID_TES)
3263 DE_ASSERT(caseTypeUsesRefImageFromFile(m_caseType));
3265 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath);
3266 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
3268 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image comparison failed");
3273 DE_ASSERT(!caseTypeUsesRefImageFromFile(m_caseType));
3275 log << TestLog::Image("RenderedImage", "Rendered Image", rendered);
3277 for (int y = 0; y < rendered.getHeight(); y++)
3278 for (int x = 0; x < rendered.getWidth(); x++)
3280 if (rendered.getPixel(x, y) != tcu::RGBA::white())
3282 log << TestLog::Message << "Failure: expected all white pixels" << TestLog::EndMessage;
3283 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
3288 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3294 // Basic barrier() usage in TCS.
3295 class BarrierCase : public TestCase
3298 BarrierCase (Context& context, const char* name, const char* description, const char* referenceImagePath)
3299 : TestCase (context, name, description)
3300 , m_referenceImagePath (referenceImagePath)
3306 IterateResult iterate (void);
3309 static const int RENDER_SIZE = 256;
3310 static const int NUM_VERTICES;
3312 const string m_referenceImagePath;
3314 SharedPtr<const ShaderProgram> m_program;
3317 const int BarrierCase::NUM_VERTICES = 32;
3319 void BarrierCase::init (void)
3321 checkTessellationSupport(m_context);
3322 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
3324 const string numVertsStr = de::toString(NUM_VERTICES);
3326 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
3327 << glu::VertexSource ("#version 310 es\n"
3329 "in highp float in_v_attr;\n"
3331 "out highp float in_tc_attr;\n"
3333 "void main (void)\n"
3335 " in_tc_attr = in_v_attr;\n"
3338 << glu::TessellationControlSource ("#version 310 es\n"
3339 "#extension GL_EXT_tessellation_shader : require\n"
3341 "layout (vertices = " + numVertsStr + ") out;\n"
3343 "in highp float in_tc_attr[];\n"
3345 "out highp float in_te_attr[];\n"
3346 "patch out highp float in_te_patchAttr;\n"
3348 "void main (void)\n"
3350 " in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID];\n"
3351 " in_te_patchAttr = 0.0f;\n"
3353 " if (gl_InvocationID == 5)\n"
3354 " in_te_patchAttr = float(gl_InvocationID)*0.1;\n"
3356 " highp float temp = in_te_patchAttr + in_te_attr[gl_InvocationID];\n"
3358 " if (gl_InvocationID == " + numVertsStr + "-1)\n"
3359 " in_te_patchAttr = float(gl_InvocationID);\n"
3361 " in_te_attr[gl_InvocationID] = temp;\n"
3363 " temp = temp + in_te_attr[(gl_InvocationID+1) % " + numVertsStr + "];\n"
3365 " in_te_attr[gl_InvocationID] = 0.25*temp;\n"
3367 " gl_TessLevelInner[0] = 32.0;\n"
3368 " gl_TessLevelInner[1] = 32.0;\n"
3370 " gl_TessLevelOuter[0] = 32.0;\n"
3371 " gl_TessLevelOuter[1] = 32.0;\n"
3372 " gl_TessLevelOuter[2] = 32.0;\n"
3373 " gl_TessLevelOuter[3] = 32.0;\n"
3376 << glu::TessellationEvaluationSource ("#version 310 es\n"
3377 "#extension GL_EXT_tessellation_shader : require\n"
3379 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_QUADS) +
3381 "in highp float in_te_attr[];\n"
3382 "patch in highp float in_te_patchAttr;\n"
3384 "out highp float in_f_blue;\n"
3386 "void main (void)\n"
3388 " highp float x = gl_TessCoord.x*2.0 - 1.0;\n"
3389 " highp float y = gl_TessCoord.y - in_te_attr[int(round(gl_TessCoord.x*float(" + numVertsStr + "-1)))];\n"
3390 " gl_Position = vec4(x, y, 0.0, 1.0);\n"
3391 " in_f_blue = abs(in_te_patchAttr - float(" + numVertsStr + "-1));\n"
3394 << glu::FragmentSource ("#version 310 es\n"
3396 "layout (location = 0) out mediump vec4 o_color;\n"
3398 "in highp float in_f_blue;\n"
3400 "void main (void)\n"
3402 " o_color = vec4(1.0, 0.0, in_f_blue, 1.0);\n"
3405 m_testCtx.getLog() << *m_program;
3406 if (!m_program->isOk())
3407 TCU_FAIL("Program compilation failed");
3410 void BarrierCase::deinit (void)
3415 BarrierCase::IterateResult BarrierCase::iterate (void)
3417 TestLog& log = m_testCtx.getLog();
3418 const RenderContext& renderCtx = m_context.getRenderContext();
3419 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
3420 const deUint32 programGL = m_program->getProgram();
3421 const glw::Functions& gl = renderCtx.getFunctions();
3423 setViewport(gl, viewport);
3424 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3425 gl.useProgram(programGL);
3428 vector<float> attributeData(NUM_VERTICES);
3430 for (int i = 0; i < NUM_VERTICES; i++)
3431 attributeData[i] = (float)i / (float)(NUM_VERTICES-1);
3433 gl.patchParameteri(GL_PATCH_VERTICES, NUM_VERTICES);
3434 gl.clear(GL_COLOR_BUFFER_BIT);
3436 const glu::VertexArrayBinding attrBindings[] =
3438 glu::va::Float("in_v_attr", 1, (int)attributeData.size(), 0, &attributeData[0])
3441 glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
3442 glu::pr::Patches(NUM_VERTICES));
3443 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
3447 const tcu::Surface rendered = getPixels(renderCtx, viewport);
3448 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath);
3449 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
3451 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image comparison failed");
3456 /*--------------------------------------------------------------------*//*!
3457 * \brief Base class for testing invariance of entire primitive set
3459 * Draws two patches with identical tessellation levels and compares the
3460 * results. Repeats the same with other programs that are only different
3461 * in irrelevant ways; compares the results between these two programs.
3462 * Also potentially compares to results produced by different tessellation
3463 * levels (see e.g. invariance rule #6).
3464 * Furthermore, repeats the above with multiple different tessellation
3467 * The manner of primitive set comparison is defined by subclass. E.g.
3468 * case for invariance rule #1 tests that same vertices come out, in same
3469 * order; rule #5 only requires that the same triangles are output, but
3470 * not necessarily in the same order.
3471 *//*--------------------------------------------------------------------*/
3472 class PrimitiveSetInvarianceCase : public TestCase
3477 WINDINGUSAGE_CCW = 0,
3484 PrimitiveSetInvarianceCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, bool usePointMode, WindingUsage windingUsage)
3485 : TestCase (context, name, description)
3486 , m_primitiveType (primType)
3487 , m_spacing (spacing)
3488 , m_usePointMode (usePointMode)
3489 , m_windingUsage (windingUsage)
3495 IterateResult iterate (void);
3502 string description (void) const { return tessellationLevelsString(&inner[0], &outer[0]); }
3506 vector<TessLevels> levels;
3507 int mem; //!< Subclass-defined arbitrary piece of data, for type of the levelcase, if needed. Passed to compare().
3508 LevelCase (const TessLevels& lev) : levels(vector<TessLevels>(1, lev)), mem(0) {}
3509 LevelCase (void) : mem(0) {}
3512 virtual vector<LevelCase> genTessLevelCases (void) const;
3513 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int levelCaseMem) const = 0;
3515 const TessPrimitiveType m_primitiveType;
3521 SharedPtr<const ShaderProgram> program;
3523 Program (Winding w, const SharedPtr<const ShaderProgram>& prog) : winding(w), program(prog) {}
3525 string description (void) const { return string() + "winding mode " + getWindingShaderName(winding); };
3528 static const int RENDER_SIZE = 16;
3530 const SpacingMode m_spacing;
3531 const bool m_usePointMode;
3532 const WindingUsage m_windingUsage;
3534 vector<Program> m_programs;
3537 vector<PrimitiveSetInvarianceCase::LevelCase> PrimitiveSetInvarianceCase::genTessLevelCases (void) const
3539 static const TessLevels basicTessLevelCases[] =
3541 { { 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
3542 { { 63.0f, 24.0f }, { 15.0f, 42.0f, 10.0f, 12.0f } },
3543 { { 3.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } },
3544 { { 4.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
3545 { { 2.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } },
3546 { { 5.0f, 6.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
3547 { { 1.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
3548 { { 5.0f, 1.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
3549 { { 5.2f, 1.6f }, { 2.9f, 3.4f, 1.5f, 4.1f } }
3552 vector<LevelCase> result;
3553 for (int i = 0; i < DE_LENGTH_OF_ARRAY(basicTessLevelCases); i++)
3554 result.push_back(LevelCase(basicTessLevelCases[i]));
3557 de::Random rnd(123);
3558 for (int i = 0; i < 10; i++)
3561 for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.inner); j++)
3562 levels.inner[j] = rnd.getFloat(1.0f, 16.0f);
3563 for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.outer); j++)
3564 levels.outer[j] = rnd.getFloat(1.0f, 16.0f);
3565 result.push_back(LevelCase(levels));
3572 void PrimitiveSetInvarianceCase::init (void)
3574 const int numDifferentConstantExprCases = 2;
3575 vector<Winding> windings;
3576 switch (m_windingUsage)
3578 case WINDINGUSAGE_CCW: windings.push_back(WINDING_CCW); break;
3579 case WINDINGUSAGE_CW: windings.push_back(WINDING_CW); break;
3580 case WINDINGUSAGE_VARY: windings.push_back(WINDING_CCW);
3581 windings.push_back(WINDING_CW); break;
3582 default: DE_ASSERT(false);
3585 checkTessellationSupport(m_context);
3586 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
3588 for (int constantExprCaseNdx = 0; constantExprCaseNdx < numDifferentConstantExprCases; constantExprCaseNdx++)
3590 for (int windingCaseNdx = 0; windingCaseNdx < (int)windings.size(); windingCaseNdx++)
3592 const string floatLit01 = de::floatToString(10.0f / (float)(constantExprCaseNdx + 10), 2);
3593 const int programNdx = (int)m_programs.size();
3595 m_programs.push_back(Program(windings[windingCaseNdx],
3596 SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
3597 << glu::VertexSource ("#version 310 es\n"
3599 "in highp float in_v_attr;\n"
3600 "out highp float in_tc_attr;\n"
3602 "void main (void)\n"
3604 " in_tc_attr = in_v_attr;\n"
3607 << glu::TessellationControlSource ("#version 310 es\n"
3608 "#extension GL_EXT_tessellation_shader : require\n"
3610 "layout (vertices = " + de::toString(constantExprCaseNdx+1) + ") out;\n"
3612 "in highp float in_tc_attr[];\n"
3614 "patch out highp float in_te_positionOffset;\n"
3616 "void main (void)\n"
3618 " in_te_positionOffset = in_tc_attr[6];\n"
3620 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
3621 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
3623 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
3624 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
3625 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
3626 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
3629 << glu::TessellationEvaluationSource ("#version 310 es\n"
3630 "#extension GL_EXT_tessellation_shader : require\n"
3632 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, windings[windingCaseNdx], m_usePointMode) +
3634 "patch in highp float in_te_positionOffset;\n"
3636 "out highp vec4 in_f_color;\n"
3637 "invariant out highp vec3 out_te_tessCoord;\n"
3639 "void main (void)\n"
3641 " gl_Position = vec4(gl_TessCoord.xy*" + floatLit01 + " - in_te_positionOffset + float(gl_PrimitiveID)*0.1, 0.0, 1.0);\n"
3642 " in_f_color = vec4(" + floatLit01 + ");\n"
3643 " out_te_tessCoord = gl_TessCoord;\n"
3646 << glu::FragmentSource ("#version 310 es\n"
3648 "layout (location = 0) out mediump vec4 o_color;\n"
3650 "in highp vec4 in_f_color;\n"
3652 "void main (void)\n"
3654 " o_color = in_f_color;\n"
3657 << glu::TransformFeedbackVarying ("out_te_tessCoord")
3658 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)))));
3661 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Program" + de::toString(programNdx), "Program " + de::toString(programNdx));
3663 if (programNdx == 0 || !m_programs.back().program->isOk())
3664 m_testCtx.getLog() << *m_programs.back().program;
3666 if (!m_programs.back().program->isOk())
3667 TCU_FAIL("Program compilation failed");
3670 m_testCtx.getLog() << TestLog::Message << "Note: program " << programNdx << " is similar to above, except some constants are different, and: " << m_programs.back().description() << TestLog::EndMessage;
3676 void PrimitiveSetInvarianceCase::deinit (void)
3681 PrimitiveSetInvarianceCase::IterateResult PrimitiveSetInvarianceCase::iterate (void)
3683 typedef TransformFeedbackHandler<Vec3> TFHandler;
3685 TestLog& log = m_testCtx.getLog();
3686 const RenderContext& renderCtx = m_context.getRenderContext();
3687 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
3688 const glw::Functions& gl = renderCtx.getFunctions();
3689 const vector<LevelCase> tessLevelCases = genTessLevelCases();
3690 vector<vector<int> > primitiveCounts;
3691 int maxNumPrimitives = -1;
3693 for (int caseNdx = 0; caseNdx < (int)tessLevelCases.size(); caseNdx++)
3695 primitiveCounts.push_back(vector<int>());
3696 for (int i = 0; i < (int)tessLevelCases[caseNdx].levels.size(); i++)
3698 const int primCount = referencePrimitiveCount(m_primitiveType, m_spacing, m_usePointMode,
3699 &tessLevelCases[caseNdx].levels[i].inner[0], &tessLevelCases[caseNdx].levels[i].outer[0]);
3700 primitiveCounts.back().push_back(primCount);
3701 maxNumPrimitives = de::max(maxNumPrimitives, primCount);
3705 const deUint32 primitiveTypeGL = outputPrimitiveTypeGL(m_primitiveType, m_usePointMode);
3706 const TFHandler transformFeedback (m_context.getRenderContext(), 2*maxNumPrimitives*numVerticesPerPrimitive(primitiveTypeGL));
3708 setViewport(gl, viewport);
3709 gl.patchParameteri(GL_PATCH_VERTICES, 7);
3711 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < (int)tessLevelCases.size(); tessLevelCaseNdx++)
3713 const LevelCase& levelCase = tessLevelCases[tessLevelCaseNdx];
3714 vector<Vec3> firstPrimVertices;
3717 string tessLevelsStr;
3718 for (int i = 0; i < (int)levelCase.levels.size(); i++)
3719 tessLevelsStr += (levelCase.levels.size() > 1 ? "\n" : "") + levelCase.levels[i].description();
3720 log << TestLog::Message << "Tessellation level sets: " << tessLevelsStr << TestLog::EndMessage;
3723 for (int subTessLevelCaseNdx = 0; subTessLevelCaseNdx < (int)levelCase.levels.size(); subTessLevelCaseNdx++)
3725 const TessLevels& tessLevels = levelCase.levels[subTessLevelCaseNdx];
3726 const float (&inner)[2] = tessLevels.inner;
3727 const float (&outer)[4] = tessLevels.outer;
3728 const float attribute[2*7] = { inner[0], inner[1], outer[0], outer[1], outer[2], outer[3], 0.0f,
3729 inner[0], inner[1], outer[0], outer[1], outer[2], outer[3], 0.5f };
3730 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, DE_LENGTH_OF_ARRAY(attribute), 0, &attribute[0]) };
3732 for (int programNdx = 0; programNdx < (int)m_programs.size(); programNdx++)
3734 const deUint32 programGL = m_programs[programNdx].program->getProgram();
3735 gl.useProgram(programGL);
3736 const TFHandler::Result tfResult = transformFeedback.renderAndGetPrimitives(programGL, primitiveTypeGL, DE_LENGTH_OF_ARRAY(bindings), &bindings[0], DE_LENGTH_OF_ARRAY(attribute));
3738 if (tfResult.numPrimitives != 2*primitiveCounts[tessLevelCaseNdx][subTessLevelCaseNdx])
3740 log << TestLog::Message << "Failure: GL reported GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN to be "
3741 << tfResult.numPrimitives << ", reference value is " << 2*primitiveCounts[tessLevelCaseNdx][subTessLevelCaseNdx]
3742 << TestLog::EndMessage;
3744 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of primitives");
3749 const int half = (int)tfResult.varying.size()/2;
3750 const vector<Vec3> prim0Vertices = vector<Vec3>(tfResult.varying.begin(), tfResult.varying.begin() + half);
3751 const Vec3* const prim1Vertices = &tfResult.varying[half];
3753 for (int vtxNdx = 0; vtxNdx < (int)prim0Vertices.size(); vtxNdx++)
3755 if (prim0Vertices[vtxNdx] != prim1Vertices[vtxNdx])
3757 log << TestLog::Message << "Failure: tessellation coordinate at index " << vtxNdx << " differs between two primitives drawn in one draw call" << TestLog::EndMessage
3758 << TestLog::Message << "Note: the coordinate is " << prim0Vertices[vtxNdx] << " for the first primitive and " << prim1Vertices[vtxNdx] << " for the second" << TestLog::EndMessage
3759 << TestLog::Message << "Note: tessellation levels for both primitives were: " << tessellationLevelsString(&inner[0], &outer[0]) << TestLog::EndMessage;
3760 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of primitives");
3765 if (programNdx == 0 && subTessLevelCaseNdx == 0)
3766 firstPrimVertices = prim0Vertices;
3769 const bool compareOk = compare(firstPrimVertices, prim0Vertices, levelCase.mem);
3772 log << TestLog::Message << "Note: comparison of tessellation coordinates failed; comparison was made between following cases:\n"
3773 << " - case A: program 0, tessellation levels: " << tessLevelCases[tessLevelCaseNdx].levels[0].description() << "\n"
3774 << " - case B: program " << programNdx << ", tessellation levels: " << tessLevels.description() << TestLog::EndMessage;
3775 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of primitives");
3784 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3788 /*--------------------------------------------------------------------*//*!
3789 * \brief Test invariance rule #1
3791 * Test that the sequence of primitives input to the TES only depends on
3792 * the tessellation levels, tessellation mode, spacing mode, winding, and
3794 *//*--------------------------------------------------------------------*/
3795 class InvariantPrimitiveSetCase : public PrimitiveSetInvarianceCase
3798 InvariantPrimitiveSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
3799 : PrimitiveSetInvarianceCase(context, name, description, primType, spacing, usePointMode, winding == WINDING_CCW ? WINDINGUSAGE_CCW
3800 : winding == WINDING_CW ? WINDINGUSAGE_CW
3801 : WINDINGUSAGE_LAST)
3806 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int) const
3808 for (int vtxNdx = 0; vtxNdx < (int)coordsA.size(); vtxNdx++)
3810 if (coordsA[vtxNdx] != coordsB[vtxNdx])
3812 m_testCtx.getLog() << TestLog::Message << "Failure: tessellation coordinate at index " << vtxNdx << " differs between two programs" << TestLog::EndMessage
3813 << TestLog::Message << "Note: the coordinate is " << coordsA[vtxNdx] << " for the first program and " << coordsB[vtxNdx] << " for the other" << TestLog::EndMessage;
3821 /*--------------------------------------------------------------------*//*!
3822 * \brief Test invariance rule #2
3824 * Test that the set of vertices along an outer edge of a quad or triangle
3825 * only depends on that edge's tessellation level, and spacing.
3827 * For each (outer) edge in the quad or triangle, draw multiple patches
3828 * with identical tessellation levels for that outer edge but with
3829 * different values for the other outer edges; compare, among the
3830 * primitives, the vertices generated for that outer edge. Repeat with
3831 * different programs, using different winding etc. settings. Compare
3832 * the edge's vertices between different programs.
3833 *//*--------------------------------------------------------------------*/
3834 class InvariantOuterEdgeCase : public TestCase
3837 InvariantOuterEdgeCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing)
3838 : TestCase (context, name, description)
3839 , m_primitiveType (primType)
3840 , m_spacing (spacing)
3842 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
3847 IterateResult iterate (void);
3854 SharedPtr<const ShaderProgram> program;
3856 Program (Winding w, bool point, const SharedPtr<const ShaderProgram>& prog) : winding(w), usePointMode(point), program(prog) {}
3858 string description (void) const { return string() + "winding mode " + getWindingShaderName(winding) + ", " + (usePointMode ? "" : "don't ") + "use point mode"; };
3861 static vector<float> generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel);
3863 static const int RENDER_SIZE = 16;
3865 const TessPrimitiveType m_primitiveType;
3866 const SpacingMode m_spacing;
3868 vector<Program> m_programs;
3871 vector<float> InvariantOuterEdgeCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel)
3873 de::Random rnd(123);
3874 return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd);
3877 void InvariantOuterEdgeCase::init (void)
3879 checkTessellationSupport(m_context);
3880 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
3882 for (int windingI = 0; windingI < WINDING_LAST; windingI++)
3884 const Winding winding = (Winding)windingI;
3886 for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++)
3888 const bool usePointMode = usePointModeI != 0;
3889 const int programNdx = (int)m_programs.size();
3890 const string floatLit01 = de::floatToString(10.0f / (float)(programNdx + 10), 2);
3892 m_programs.push_back(Program(winding, usePointMode,
3893 SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
3894 << glu::VertexSource ("#version 310 es\n"
3896 "in highp float in_v_attr;\n"
3897 "out highp float in_tc_attr;\n"
3899 "void main (void)\n"
3901 " in_tc_attr = in_v_attr;\n"
3904 << glu::TessellationControlSource ("#version 310 es\n"
3905 "#extension GL_EXT_tessellation_shader : require\n"
3907 "layout (vertices = " + de::toString(programNdx+1) + ") out;\n"
3909 "in highp float in_tc_attr[];\n"
3911 "void main (void)\n"
3913 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
3914 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
3916 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
3917 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
3918 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
3919 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
3922 << glu::TessellationEvaluationSource ("#version 310 es\n"
3923 "#extension GL_EXT_tessellation_shader : require\n"
3925 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, winding, usePointMode) +
3927 "out highp vec4 in_f_color;\n"
3928 "invariant out highp vec3 out_te_tessCoord;\n"
3930 "void main (void)\n"
3932 " gl_Position = vec4(gl_TessCoord.xy*" + floatLit01 + " - float(gl_PrimitiveID)*0.05, 0.0, 1.0);\n"
3933 " in_f_color = vec4(" + floatLit01 + ");\n"
3934 " out_te_tessCoord = gl_TessCoord;\n"
3937 << glu::FragmentSource ("#version 310 es\n"
3939 "layout (location = 0) out mediump vec4 o_color;\n"
3941 "in highp vec4 in_f_color;\n"
3943 "void main (void)\n"
3945 " o_color = in_f_color;\n"
3948 << glu::TransformFeedbackVarying ("out_te_tessCoord")
3949 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)))));
3952 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Program" + de::toString(programNdx), "Program " + de::toString(programNdx));
3954 if (programNdx == 0 || !m_programs.back().program->isOk())
3955 m_testCtx.getLog() << *m_programs.back().program;
3957 if (!m_programs.back().program->isOk())
3958 TCU_FAIL("Program compilation failed");
3961 m_testCtx.getLog() << TestLog::Message << "Note: program " << programNdx << " is similar to above, except some constants are different, and: " << m_programs.back().description() << TestLog::EndMessage;
3967 void InvariantOuterEdgeCase::deinit (void)
3972 InvariantOuterEdgeCase::IterateResult InvariantOuterEdgeCase::iterate (void)
3974 typedef TransformFeedbackHandler<Vec3> TFHandler;
3976 TestLog& log = m_testCtx.getLog();
3977 const RenderContext& renderCtx = m_context.getRenderContext();
3978 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
3979 const glw::Functions& gl = renderCtx.getFunctions();
3981 static const float singleOuterEdgeLevels[] = { 1.0f, 1.2f, 1.9f, 2.3f, 2.8f, 3.3f, 3.8f, 10.2f, 1.6f, 24.4f, 24.7f, 63.0f };
3982 const int numPatchesPerDrawCall = 10;
3983 const vector<OuterEdgeDescription> edgeDescriptions = outerEdgeDescriptions(m_primitiveType);
3986 // Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once.
3987 int maxNumVerticesInDrawCall = 0;
3989 const vector<float> patchTessLevels = generatePatchTessLevels(numPatchesPerDrawCall, 0 /* outer-edge index doesn't affect vertex count */, arrayMax(singleOuterEdgeLevels));
3991 for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++)
3992 maxNumVerticesInDrawCall = de::max(maxNumVerticesInDrawCall,
3993 multiplePatchReferenceVertexCount(m_primitiveType, m_spacing, usePointModeI != 0, &patchTessLevels[0], numPatchesPerDrawCall));
3997 const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall);
3999 setViewport(gl, viewport);
4000 gl.patchParameteri(GL_PATCH_VERTICES, 6);
4002 for (int outerEdgeIndex = 0; outerEdgeIndex < (int)edgeDescriptions.size(); outerEdgeIndex++)
4004 const OuterEdgeDescription& edgeDesc = edgeDescriptions[outerEdgeIndex];
4006 for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(singleOuterEdgeLevels); outerEdgeLevelCaseNdx++)
4008 typedef std::set<Vec3, VecLexLessThan<3> > Vec3Set;
4010 const vector<float> patchTessLevels = generatePatchTessLevels(numPatchesPerDrawCall, outerEdgeIndex, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
4011 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)patchTessLevels.size(), 0, &patchTessLevels[0]) };
4012 Vec3Set firstOuterEdgeVertices; // Vertices of the outer edge of the first patch of the first program's draw call; used for comparison with other patches.
4014 log << TestLog::Message << "Testing with outer tessellation level " << singleOuterEdgeLevels[outerEdgeLevelCaseNdx]
4015 << " for the " << edgeDesc.description() << " edge, and with various levels for other edges, and with all programs" << TestLog::EndMessage;
4017 for (int programNdx = 0; programNdx < (int)m_programs.size(); programNdx++)
4019 const Program& program = m_programs[programNdx];
4020 const deUint32 programGL = program.program->getProgram();
4022 gl.useProgram(programGL);
4025 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, program.usePointMode),
4026 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)patchTessLevels.size());
4027 const int refNumVertices = multiplePatchReferenceVertexCount(m_primitiveType, m_spacing, program.usePointMode, &patchTessLevels[0], numPatchesPerDrawCall);
4028 int numVerticesRead = 0;
4030 if ((int)tfResult.varying.size() != refNumVertices)
4032 log << TestLog::Message << "Failure: the number of vertices returned by transform feedback is "
4033 << tfResult.varying.size() << ", expected " << refNumVertices << TestLog::EndMessage
4034 << TestLog::Message << "Note: rendered " << numPatchesPerDrawCall
4035 << " patches in one draw call; tessellation levels for each patch are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n"
4036 << containerStr(patchTessLevels, 6) << TestLog::EndMessage;
4038 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4042 // Check the vertices of each patch.
4044 for (int patchNdx = 0; patchNdx < numPatchesPerDrawCall; patchNdx++)
4046 const float* const innerLevels = &patchTessLevels[6*patchNdx + 0];
4047 const float* const outerLevels = &patchTessLevels[6*patchNdx + 2];
4048 const int patchNumVertices = referenceVertexCount(m_primitiveType, m_spacing, program.usePointMode, innerLevels, outerLevels);
4049 Vec3Set outerEdgeVertices;
4051 // We're interested in just the vertices on the current outer edge.
4052 for(int vtxNdx = numVerticesRead; vtxNdx < numVerticesRead + patchNumVertices; vtxNdx++)
4054 const Vec3& vtx = tfResult.varying[vtxNdx];
4055 if (edgeDesc.contains(vtx))
4056 outerEdgeVertices.insert(tfResult.varying[vtxNdx]);
4059 // Check that the outer edge contains an appropriate number of vertices.
4061 const int refNumVerticesOnOuterEdge = 1 + getClampedRoundedTessLevel(m_spacing, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
4063 if ((int)outerEdgeVertices.size() != refNumVerticesOnOuterEdge)
4065 log << TestLog::Message << "Failure: the number of vertices on outer edge is " << outerEdgeVertices.size()
4066 << ", expected " << refNumVerticesOnOuterEdge << TestLog::EndMessage
4067 << TestLog::Message << "Note: vertices on the outer edge are:\n" << containerStr(outerEdgeVertices, 5, 0) << TestLog::EndMessage
4068 << TestLog::Message << "Note: the following parameters were used: " << program.description()
4069 << ", tessellation levels: " << tessellationLevelsString(innerLevels, outerLevels) << TestLog::EndMessage;
4070 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4075 // Compare the vertices to those of the first patch (unless this is the first patch).
4077 if (programNdx == 0 && patchNdx == 0)
4078 firstOuterEdgeVertices = outerEdgeVertices;
4081 if (firstOuterEdgeVertices != outerEdgeVertices)
4083 log << TestLog::Message << "Failure: vertices generated for the edge differ between the following cases:\n"
4084 << " - case A: " << m_programs[0].description() << ", tessellation levels: "
4085 << tessellationLevelsString(&patchTessLevels[0], &patchTessLevels[2]) << "\n"
4086 << " - case B: " << program.description() << ", tessellation levels: "
4087 << tessellationLevelsString(innerLevels, outerLevels) << TestLog::EndMessage;
4089 log << TestLog::Message << "Note: resulting vertices for the edge for the cases were:\n"
4090 << " - case A: " << containerStr(firstOuterEdgeVertices, 5, 14) << "\n"
4091 << " - case B: " << containerStr(outerEdgeVertices, 5, 14) << TestLog::EndMessage;
4093 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4098 numVerticesRead += patchNumVertices;
4101 DE_ASSERT(numVerticesRead == (int)tfResult.varying.size());
4109 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4113 /*--------------------------------------------------------------------*//*!
4114 * \brief Test invariance rule #3
4116 * Test that the vertices along an outer edge are placed symmetrically.
4118 * Draw multiple patches with different tessellation levels and different
4119 * point_mode, winding etc. Before outputting tesscoords with TF, mirror
4120 * the vertices in the TES such that every vertex on an outer edge -
4121 * except the possible middle vertex - should be duplicated in the output.
4122 * Check that appropriate duplicates exist.
4123 *//*--------------------------------------------------------------------*/
4124 class SymmetricOuterEdgeCase : public TestCase
4127 SymmetricOuterEdgeCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
4128 : TestCase (context, name, description)
4129 , m_primitiveType (primType)
4130 , m_spacing (spacing)
4131 , m_winding (winding)
4132 , m_usePointMode (usePointMode)
4138 IterateResult iterate (void);
4141 static vector<float> generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel);
4143 static const int RENDER_SIZE = 16;
4145 const TessPrimitiveType m_primitiveType;
4146 const SpacingMode m_spacing;
4147 const Winding m_winding;
4148 const bool m_usePointMode;
4150 SharedPtr<const glu::ShaderProgram> m_program;
4153 vector<float> SymmetricOuterEdgeCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel)
4155 de::Random rnd(123);
4156 return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd);
4159 void SymmetricOuterEdgeCase::init (void)
4161 checkTessellationSupport(m_context);
4162 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
4164 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
4165 << glu::VertexSource ("#version 310 es\n"
4167 "in highp float in_v_attr;\n"
4168 "out highp float in_tc_attr;\n"
4170 "void main (void)\n"
4172 " in_tc_attr = in_v_attr;\n"
4175 << glu::TessellationControlSource ("#version 310 es\n"
4176 "#extension GL_EXT_tessellation_shader : require\n"
4178 "layout (vertices = 1) out;\n"
4180 "in highp float in_tc_attr[];\n"
4182 "void main (void)\n"
4184 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
4185 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
4187 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
4188 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
4189 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
4190 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
4193 << glu::TessellationEvaluationSource ("#version 310 es\n"
4194 "#extension GL_EXT_tessellation_shader : require\n"
4196 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) +
4198 "out highp vec4 in_f_color;\n"
4199 "out highp vec4 out_te_tessCoord_isMirrored;\n"
4201 "void main (void)\n"
4203 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
4204 " float x = gl_TessCoord.x;\n"
4205 " float y = gl_TessCoord.y;\n"
4206 " float z = gl_TessCoord.z;\n"
4207 " // Mirror one half of each outer edge onto the other half, except the endpoints (because they belong to two edges)\n"
4208 " 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"
4209 " : y == 0.0 && z > 0.5 && z != 1.0 ? vec4(1.0-x, 0.0, 1.0-z, 1.0)\n"
4210 " : x == 0.0 && y > 0.5 && y != 1.0 ? vec4( 0.0, 1.0-y, 1.0-z, 1.0)\n"
4211 " : vec4(x, y, z, 0.0);\n"
4212 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ?
4213 " float x = gl_TessCoord.x;\n"
4214 " float y = gl_TessCoord.y;\n"
4215 " // Mirror one half of each outer edge onto the other half, except the endpoints (because they belong to two edges)\n"
4216 " out_te_tessCoord_isMirrored = (x == 0.0 || x == 1.0) && y > 0.5 && y != 1.0 ? vec4( x, 1.0-y, 0.0, 1.0)\n"
4217 " : (y == 0.0 || y == 1.0) && x > 0.5 && x != 1.0 ? vec4(1.0-x, y, 0.0, 1.0)\n"
4218 " : vec4(x, y, 0.0, 0.0);\n"
4219 : m_primitiveType == TESSPRIMITIVETYPE_ISOLINES ?
4220 " float x = gl_TessCoord.x;\n"
4221 " float y = gl_TessCoord.y;\n"
4222 " // Mirror one half of each outer edge onto the other half\n"
4223 " out_te_tessCoord_isMirrored = (x == 0.0 || x == 1.0) && y > 0.5 ? vec4(x, 1.0-y, 0.0, 1.0)\n"
4224 " : vec4(x, y, 0.0, 0.0f);\n"
4227 " gl_Position = vec4(gl_TessCoord.xy, 0.0, 1.0);\n"
4228 " in_f_color = vec4(1.0);\n"
4231 << glu::FragmentSource ("#version 310 es\n"
4233 "layout (location = 0) out mediump vec4 o_color;\n"
4235 "in highp vec4 in_f_color;\n"
4237 "void main (void)\n"
4239 " o_color = in_f_color;\n"
4242 << glu::TransformFeedbackVarying ("out_te_tessCoord_isMirrored")
4243 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
4245 m_testCtx.getLog() << *m_program;
4246 if (!m_program->isOk())
4247 TCU_FAIL("Program compilation failed");
4250 void SymmetricOuterEdgeCase::deinit (void)
4255 SymmetricOuterEdgeCase::IterateResult SymmetricOuterEdgeCase::iterate (void)
4257 typedef TransformFeedbackHandler<Vec4> TFHandler;
4259 TestLog& log = m_testCtx.getLog();
4260 const RenderContext& renderCtx = m_context.getRenderContext();
4261 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
4262 const glw::Functions& gl = renderCtx.getFunctions();
4264 static const float singleOuterEdgeLevels[] = { 1.0f, 1.2f, 1.9f, 2.3f, 2.8f, 3.3f, 3.8f, 10.2f, 1.6f, 24.4f, 24.7f, 63.0f };
4265 const vector<OuterEdgeDescription> edgeDescriptions = outerEdgeDescriptions(m_primitiveType);
4268 // Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once.
4269 int maxNumVerticesInDrawCall;
4271 const vector<float> patchTessLevels = generatePatchTessLevels(1, 0 /* outer-edge index doesn't affect vertex count */, arrayMax(singleOuterEdgeLevels));
4272 maxNumVerticesInDrawCall = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]);
4276 const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall);
4278 setViewport(gl, viewport);
4279 gl.patchParameteri(GL_PATCH_VERTICES, 6);
4281 for (int outerEdgeIndex = 0; outerEdgeIndex < (int)edgeDescriptions.size(); outerEdgeIndex++)
4283 const OuterEdgeDescription& edgeDesc = edgeDescriptions[outerEdgeIndex];
4285 for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(singleOuterEdgeLevels); outerEdgeLevelCaseNdx++)
4287 typedef std::set<Vec3, VecLexLessThan<3> > Vec3Set;
4289 const vector<float> patchTessLevels = generatePatchTessLevels(1, outerEdgeIndex, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
4290 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)patchTessLevels.size(), 0, &patchTessLevels[0]) };
4292 log << TestLog::Message << "Testing with outer tessellation level " << singleOuterEdgeLevels[outerEdgeLevelCaseNdx]
4293 << " for the " << edgeDesc.description() << " edge, and with various levels for other edges" << TestLog::EndMessage;
4296 const deUint32 programGL = m_program->getProgram();
4298 gl.useProgram(programGL);
4301 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode),
4302 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)patchTessLevels.size());
4303 const int refNumVertices = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]);
4305 if ((int)tfResult.varying.size() != refNumVertices)
4307 log << TestLog::Message << "Failure: the number of vertices returned by transform feedback is "
4308 << tfResult.varying.size() << ", expected " << refNumVertices << TestLog::EndMessage
4309 << TestLog::Message << "Note: rendered 1 patch, tessellation levels are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n"
4310 << containerStr(patchTessLevels, 6) << TestLog::EndMessage;
4312 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4316 // Check the vertices.
4319 Vec3Set nonMirroredEdgeVertices;
4320 Vec3Set mirroredEdgeVertices;
4322 // We're interested in just the vertices on the current outer edge.
4323 for(int vtxNdx = 0; vtxNdx < refNumVertices; vtxNdx++)
4325 const Vec3& vtx = tfResult.varying[vtxNdx].swizzle(0,1,2);
4326 if (edgeDesc.contains(vtx))
4328 // Ignore the middle vertex of the outer edge, as it's exactly at the mirroring point;
4329 // for isolines, also ignore (0, 0) and (1, 0) because there's no mirrored counterpart for them.
4330 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES && vtx == tcu::select(Vec3(0.0f), Vec3(0.5f), singleTrueMask<3>(edgeDesc.constantCoordinateIndex)))
4332 if (m_primitiveType == TESSPRIMITIVETYPE_QUADS && vtx.swizzle(0,1) == tcu::select(Vec2(edgeDesc.constantCoordinateValueChoices[0]),
4334 singleTrueMask<2>(edgeDesc.constantCoordinateIndex)))
4336 if (m_primitiveType == TESSPRIMITIVETYPE_ISOLINES && (vtx == Vec3(0.0f, 0.5f, 0.0f) || vtx == Vec3(1.0f, 0.5f, 0.0f) ||
4337 vtx == Vec3(0.0f, 0.0f, 0.0f) || vtx == Vec3(1.0f, 0.0f, 0.0f)))
4340 const bool isMirrored = tfResult.varying[vtxNdx].w() > 0.5f;
4342 mirroredEdgeVertices.insert(vtx);
4344 nonMirroredEdgeVertices.insert(vtx);
4348 if (m_primitiveType != TESSPRIMITIVETYPE_ISOLINES)
4350 // Check that both endpoints are present. Note that endpoints aren't mirrored by the shader, since they belong to more than one edge.
4355 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4357 endpointA = tcu::select(Vec3(1.0f), Vec3(0.0f), singleTrueMask<3>((edgeDesc.constantCoordinateIndex + 1) % 3));
4358 endpointB = tcu::select(Vec3(1.0f), Vec3(0.0f), singleTrueMask<3>((edgeDesc.constantCoordinateIndex + 2) % 3));
4360 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
4362 endpointA.xy() = tcu::select(Vec2(edgeDesc.constantCoordinateValueChoices[0]), Vec2(0.0f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex));
4363 endpointB.xy() = tcu::select(Vec2(edgeDesc.constantCoordinateValueChoices[0]), Vec2(1.0f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex));
4368 if (!contains(nonMirroredEdgeVertices, endpointA) ||
4369 !contains(nonMirroredEdgeVertices, endpointB))
4371 log << TestLog::Message << "Failure: edge doesn't contain both endpoints, " << endpointA << " and " << endpointB << TestLog::EndMessage
4372 << TestLog::Message << "Note: non-mirrored vertices:\n" << containerStr(nonMirroredEdgeVertices, 5)
4373 << "\nmirrored vertices:\n" << containerStr(mirroredEdgeVertices, 5) << TestLog::EndMessage;
4374 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4377 nonMirroredEdgeVertices.erase(endpointA);
4378 nonMirroredEdgeVertices.erase(endpointB);
4381 if (nonMirroredEdgeVertices != mirroredEdgeVertices)
4383 log << TestLog::Message << "Failure: the set of mirrored edges isn't equal to the set of non-mirrored edges (ignoring endpoints and possible middle)" << TestLog::EndMessage
4384 << TestLog::Message << "Note: non-mirrored vertices:\n" << containerStr(nonMirroredEdgeVertices, 5)
4385 << "\nmirrored vertices:\n" << containerStr(mirroredEdgeVertices, 5) << TestLog::EndMessage;
4386 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4397 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4401 /*--------------------------------------------------------------------*//*!
4402 * \brief Test invariance rule #4
4404 * Test that the vertices on an outer edge don't depend on which of the
4405 * edges it is, other than with respect to component order.
4406 *//*--------------------------------------------------------------------*/
4407 class OuterEdgeVertexSetIndexIndependenceCase : public TestCase
4410 OuterEdgeVertexSetIndexIndependenceCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
4411 : TestCase (context, name, description)
4412 , m_primitiveType (primType)
4413 , m_spacing (spacing)
4414 , m_winding (winding)
4415 , m_usePointMode (usePointMode)
4417 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
4422 IterateResult iterate (void);
4425 static vector<float> generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel);
4427 static const int RENDER_SIZE = 16;
4429 const TessPrimitiveType m_primitiveType;
4430 const SpacingMode m_spacing;
4431 const Winding m_winding;
4432 const bool m_usePointMode;
4434 SharedPtr<const glu::ShaderProgram> m_program;
4437 vector<float> OuterEdgeVertexSetIndexIndependenceCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel)
4439 de::Random rnd(123);
4440 return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd);
4443 void OuterEdgeVertexSetIndexIndependenceCase::init (void)
4445 checkTessellationSupport(m_context);
4446 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
4448 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
4449 << glu::VertexSource ("#version 310 es\n"
4451 "in highp float in_v_attr;\n"
4452 "out highp float in_tc_attr;\n"
4454 "void main (void)\n"
4456 " in_tc_attr = in_v_attr;\n"
4459 << glu::TessellationControlSource ("#version 310 es\n"
4460 "#extension GL_EXT_tessellation_shader : require\n"
4462 "layout (vertices = 1) out;\n"
4464 "in highp float in_tc_attr[];\n"
4466 "void main (void)\n"
4468 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
4469 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
4471 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
4472 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
4473 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
4474 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
4477 << glu::TessellationEvaluationSource ("#version 310 es\n"
4478 "#extension GL_EXT_tessellation_shader : require\n"
4480 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) +
4482 "out highp vec4 in_f_color;\n"
4483 "out highp vec3 out_te_tessCoord;\n"
4485 "void main (void)\n"
4487 " out_te_tessCoord = gl_TessCoord;"
4488 " gl_Position = vec4(gl_TessCoord.xy, 0.0, 1.0);\n"
4489 " in_f_color = vec4(1.0);\n"
4492 << glu::FragmentSource ("#version 310 es\n"
4494 "layout (location = 0) out mediump vec4 o_color;\n"
4496 "in highp vec4 in_f_color;\n"
4498 "void main (void)\n"
4500 " o_color = in_f_color;\n"
4503 << glu::TransformFeedbackVarying ("out_te_tessCoord")
4504 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
4506 m_testCtx.getLog() << *m_program;
4507 if (!m_program->isOk())
4508 TCU_FAIL("Program compilation failed");
4511 void OuterEdgeVertexSetIndexIndependenceCase::deinit (void)
4516 OuterEdgeVertexSetIndexIndependenceCase::IterateResult OuterEdgeVertexSetIndexIndependenceCase::iterate (void)
4518 typedef TransformFeedbackHandler<Vec3> TFHandler;
4520 TestLog& log = m_testCtx.getLog();
4521 const RenderContext& renderCtx = m_context.getRenderContext();
4522 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
4523 const glw::Functions& gl = renderCtx.getFunctions();
4524 const deUint32 programGL = m_program->getProgram();
4526 static const float singleOuterEdgeLevels[] = { 1.0f, 1.2f, 1.9f, 2.3f, 2.8f, 3.3f, 3.8f, 10.2f, 1.6f, 24.4f, 24.7f, 63.0f };
4527 const vector<OuterEdgeDescription> edgeDescriptions = outerEdgeDescriptions(m_primitiveType);
4529 gl.useProgram(programGL);
4530 setViewport(gl, viewport);
4531 gl.patchParameteri(GL_PATCH_VERTICES, 6);
4534 // Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once.
4535 int maxNumVerticesInDrawCall = 0;
4537 const vector<float> patchTessLevels = generatePatchTessLevels(1, 0 /* outer-edge index doesn't affect vertex count */, arrayMax(singleOuterEdgeLevels));
4538 maxNumVerticesInDrawCall = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]);
4542 const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall);
4544 for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(singleOuterEdgeLevels); outerEdgeLevelCaseNdx++)
4546 typedef std::set<Vec3, VecLexLessThan<3> > Vec3Set;
4548 Vec3Set firstEdgeVertices;
4550 for (int outerEdgeIndex = 0; outerEdgeIndex < (int)edgeDescriptions.size(); outerEdgeIndex++)
4552 const OuterEdgeDescription& edgeDesc = edgeDescriptions[outerEdgeIndex];
4553 const vector<float> patchTessLevels = generatePatchTessLevels(1, outerEdgeIndex, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
4554 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)patchTessLevels.size(), 0, &patchTessLevels[0]) };
4556 log << TestLog::Message << "Testing with outer tessellation level " << singleOuterEdgeLevels[outerEdgeLevelCaseNdx]
4557 << " for the " << edgeDesc.description() << " edge, and with various levels for other edges" << TestLog::EndMessage;
4560 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode),
4561 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)patchTessLevels.size());
4562 const int refNumVertices = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]);
4564 if ((int)tfResult.varying.size() != refNumVertices)
4566 log << TestLog::Message << "Failure: the number of vertices returned by transform feedback is "
4567 << tfResult.varying.size() << ", expected " << refNumVertices << TestLog::EndMessage
4568 << TestLog::Message << "Note: rendered 1 patch, tessellation levels are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n"
4569 << containerStr(patchTessLevels, 6) << TestLog::EndMessage;
4571 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4576 Vec3Set currentEdgeVertices;
4578 // Get the vertices on the current outer edge.
4579 for(int vtxNdx = 0; vtxNdx < refNumVertices; vtxNdx++)
4581 const Vec3& vtx = tfResult.varying[vtxNdx];
4582 if (edgeDesc.contains(vtx))
4584 // Swizzle components to match the order of the first edge.
4585 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4587 currentEdgeVertices.insert(outerEdgeIndex == 0 ? vtx
4588 : outerEdgeIndex == 1 ? vtx.swizzle(1, 0, 2)
4589 : outerEdgeIndex == 2 ? vtx.swizzle(2, 1, 0)
4592 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
4594 currentEdgeVertices.insert(Vec3(outerEdgeIndex == 0 ? vtx.y()
4595 : outerEdgeIndex == 1 ? vtx.x()
4596 : outerEdgeIndex == 2 ? vtx.y()
4597 : outerEdgeIndex == 3 ? vtx.x()
4606 if (outerEdgeIndex == 0)
4607 firstEdgeVertices = currentEdgeVertices;
4610 // Compare vertices of this edge to those of the first edge.
4612 if (currentEdgeVertices != firstEdgeVertices)
4614 const char* const swizzleDesc = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? (outerEdgeIndex == 1 ? "(y, x, z)"
4615 : outerEdgeIndex == 2 ? "(z, y, x)"
4617 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? (outerEdgeIndex == 1 ? "(x, 0)"
4618 : outerEdgeIndex == 2 ? "(y, 0)"
4619 : outerEdgeIndex == 3 ? "(x, 0)"
4623 log << TestLog::Message << "Failure: the set of vertices on the " << edgeDesc.description() << " edge"
4624 << " doesn't match the set of vertices on the " << edgeDescriptions[0].description() << " edge" << TestLog::EndMessage
4625 << TestLog::Message << "Note: set of vertices on " << edgeDesc.description() << " edge, components swizzled like " << swizzleDesc
4626 << " to match component order on first edge:\n" << containerStr(currentEdgeVertices, 5)
4627 << "\non " << edgeDescriptions[0].description() << " edge:\n" << containerStr(firstEdgeVertices, 5) << TestLog::EndMessage;
4628 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4639 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4643 /*--------------------------------------------------------------------*//*!
4644 * \brief Test invariance rule #5
4646 * Test that the set of triangles input to the TES only depends on the
4647 * tessellation levels, tessellation mode and spacing mode. Specifically,
4648 * winding doesn't change the set of triangles, though it can change the
4649 * order in which they are input to TES, and can (and will) change the
4650 * vertex order within a triangle.
4651 *//*--------------------------------------------------------------------*/
4652 class InvariantTriangleSetCase : public PrimitiveSetInvarianceCase
4655 InvariantTriangleSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing)
4656 : PrimitiveSetInvarianceCase(context, name, description, primType, spacing, false, WINDINGUSAGE_VARY)
4658 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
4662 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int) const
4664 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog());
4668 /*--------------------------------------------------------------------*//*!
4669 * \brief Test invariance rule #6
4671 * Test that the set of inner triangles input to the TES only depends on
4672 * the inner tessellation levels, tessellation mode and spacing mode.
4673 *//*--------------------------------------------------------------------*/
4674 class InvariantInnerTriangleSetCase : public PrimitiveSetInvarianceCase
4677 InvariantInnerTriangleSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing)
4678 : PrimitiveSetInvarianceCase(context, name, description, primType, spacing, false, WINDINGUSAGE_VARY)
4680 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
4684 virtual vector<LevelCase> genTessLevelCases (void) const
4686 const int numSubCases = 4;
4687 const vector<LevelCase> baseResults = PrimitiveSetInvarianceCase::genTessLevelCases();
4688 vector<LevelCase> result;
4689 de::Random rnd (123);
4691 // Generate variants with different values for irrelevant levels.
4692 for (int baseNdx = 0; baseNdx < (int)baseResults.size(); baseNdx++)
4694 const TessLevels& base = baseResults[baseNdx].levels[0];
4695 TessLevels levels = base;
4696 LevelCase levelCase;
4698 for (int subNdx = 0; subNdx < numSubCases; subNdx++)
4700 levelCase.levels.push_back(levels);
4702 for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); i++)
4703 levels.outer[i] = rnd.getFloat(2.0f, 16.0f);
4704 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4705 levels.inner[1] = rnd.getFloat(2.0f, 16.0f);
4708 result.push_back(levelCase);
4714 struct IsInnerTriangleTriangle
4716 bool operator() (const Vec3* vertices) const
4718 for (int v = 0; v < 3; v++)
4719 for (int c = 0; c < 3; c++)
4720 if (vertices[v][c] == 0.0f)
4726 struct IsInnerQuadTriangle
4728 bool operator() (const Vec3* vertices) const
4730 for (int v = 0; v < 3; v++)
4731 for (int c = 0; c < 2; c++)
4732 if (vertices[v][c] == 0.0f || vertices[v][c] == 1.0f)
4738 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int) const
4740 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4741 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(), IsInnerTriangleTriangle(), "outer triangles");
4742 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
4743 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(), IsInnerQuadTriangle(), "outer triangles");
4752 /*--------------------------------------------------------------------*//*!
4753 * \brief Test invariance rule #7
4755 * Test that the set of outer triangles input to the TES only depends on
4756 * tessellation mode, spacing mode and the inner and outer tessellation
4757 * levels corresponding to the inner and outer edges relevant to that
4759 *//*--------------------------------------------------------------------*/
4760 class InvariantOuterTriangleSetCase : public PrimitiveSetInvarianceCase
4763 InvariantOuterTriangleSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing)
4764 : PrimitiveSetInvarianceCase(context, name, description, primType, spacing, false, WINDINGUSAGE_VARY)
4766 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
4770 virtual vector<LevelCase> genTessLevelCases (void) const
4772 const int numSubCasesPerEdge = 4;
4773 const int numEdges = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3
4774 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4
4776 const vector<LevelCase> baseResult = PrimitiveSetInvarianceCase::genTessLevelCases();
4777 vector<LevelCase> result;
4778 de::Random rnd (123);
4780 // Generate variants with different values for irrelevant levels.
4781 for (int baseNdx = 0; baseNdx < (int)baseResult.size(); baseNdx++)
4783 const TessLevels& base = baseResult[baseNdx].levels[0];
4784 if (base.inner[0] == 1.0f || (m_primitiveType == TESSPRIMITIVETYPE_QUADS && base.inner[1] == 1.0f))
4787 for (int edgeNdx = 0; edgeNdx < numEdges; edgeNdx++)
4789 TessLevels levels = base;
4790 LevelCase levelCase;
4791 levelCase.mem = edgeNdx;
4793 for (int subCaseNdx = 0; subCaseNdx < numSubCasesPerEdge; subCaseNdx++)
4795 levelCase.levels.push_back(levels);
4797 for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); i++)
4800 levels.outer[i] = rnd.getFloat(2.0f, 16.0f);
4803 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4804 levels.inner[1] = rnd.getFloat(2.0f, 16.0f);
4807 result.push_back(levelCase);
4814 class IsTriangleTriangleOnOuterEdge
4817 IsTriangleTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
4818 bool operator() (const Vec3* vertices) const
4820 bool touchesAppropriateEdge = false;
4821 for (int v = 0; v < 3; v++)
4822 if (vertices[v][m_edgeNdx] == 0.0f)
4823 touchesAppropriateEdge = true;
4825 if (touchesAppropriateEdge)
4827 const Vec3 avg = (vertices[0] + vertices[1] + vertices[2]) / 3.0f;
4828 return avg[m_edgeNdx] < avg[(m_edgeNdx+1)%3] &&
4829 avg[m_edgeNdx] < avg[(m_edgeNdx+2)%3];
4838 class IsQuadTriangleOnOuterEdge
4841 IsQuadTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
4843 bool onEdge (const Vec3& v) const
4845 return v[m_edgeNdx%2] == (m_edgeNdx <= 1 ? 0.0f : 1.0f);
4848 static inline bool onAnyEdge (const Vec3& v)
4850 return v[0] == 0.0f || v[0] == 1.0f || v[1] == 0.0f || v[1] == 1.0f;
4853 bool operator() (const Vec3* vertices) const
4855 for (int v = 0; v < 3; v++)
4857 const Vec3& a = vertices[v];
4858 const Vec3& b = vertices[(v+1)%3];
4859 const Vec3& c = vertices[(v+2)%3];
4860 if (onEdge(a) && onEdge(b))
4862 if (onEdge(c) && !onAnyEdge(a) && !onAnyEdge(b) && a[m_edgeNdx%2] == b[m_edgeNdx%2])
4873 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int outerEdgeNdx) const
4875 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4877 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(),
4878 IsTriangleTriangleOnOuterEdge(outerEdgeNdx),
4879 ("inner triangles, and outer triangles corresponding to other edge than edge "
4880 + outerEdgeDescriptions(m_primitiveType)[outerEdgeNdx].description()).c_str());
4882 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
4884 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(),
4885 IsQuadTriangleOnOuterEdge(outerEdgeNdx),
4886 ("inner triangles, and outer triangles corresponding to other edge than edge "
4887 + outerEdgeDescriptions(m_primitiveType)[outerEdgeNdx].description()).c_str());
4896 /*--------------------------------------------------------------------*//*!
4897 * \brief Base class for testing individual components of tess coords
4899 * Useful for testing parts of invariance rule #8.
4900 *//*--------------------------------------------------------------------*/
4901 class TessCoordComponentInvarianceCase : public TestCase
4904 TessCoordComponentInvarianceCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
4905 : TestCase (context, name, description)
4906 , m_primitiveType (primType)
4907 , m_spacing (spacing)
4908 , m_winding (winding)
4909 , m_usePointMode (usePointMode)
4915 IterateResult iterate (void);
4918 virtual string tessEvalOutputComponentStatements (const char* tessCoordComponentName, const char* outputComponentName) const = 0;
4919 virtual bool checkTessCoordComponent (float component) const = 0;
4922 static vector<float> genTessLevelCases (int numCases);
4924 static const int RENDER_SIZE = 16;
4926 const TessPrimitiveType m_primitiveType;
4927 const SpacingMode m_spacing;
4928 const Winding m_winding;
4929 const bool m_usePointMode;
4931 SharedPtr<const glu::ShaderProgram> m_program;
4934 void TessCoordComponentInvarianceCase::init (void)
4936 checkTessellationSupport(m_context);
4937 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
4939 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
4940 << glu::VertexSource ("#version 310 es\n"
4942 "in highp float in_v_attr;\n"
4943 "out highp float in_tc_attr;\n"
4945 "void main (void)\n"
4947 " in_tc_attr = in_v_attr;\n"
4950 << glu::TessellationControlSource ("#version 310 es\n"
4951 "#extension GL_EXT_tessellation_shader : require\n"
4953 "layout (vertices = 1) out;\n"
4955 "in highp float in_tc_attr[];\n"
4957 "void main (void)\n"
4959 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
4960 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
4962 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
4963 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
4964 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
4965 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
4968 << glu::TessellationEvaluationSource ("#version 310 es\n"
4969 "#extension GL_EXT_tessellation_shader : require\n"
4971 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) +
4973 "out highp vec4 in_f_color;\n"
4974 "out highp vec3 out_te_output;\n"
4976 "void main (void)\n"
4978 + tessEvalOutputComponentStatements("gl_TessCoord.x", "out_te_output.x")
4979 + tessEvalOutputComponentStatements("gl_TessCoord.y", "out_te_output.y")
4981 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
4982 tessEvalOutputComponentStatements("gl_TessCoord.z", "out_te_output.z") :
4983 " out_te_output.z = 0.0f;\n") +
4984 " gl_Position = vec4(gl_TessCoord.xy, 0.0, 1.0);\n"
4985 " in_f_color = vec4(1.0);\n"
4988 << glu::FragmentSource ("#version 310 es\n"
4990 "layout (location = 0) out mediump vec4 o_color;\n"
4992 "in highp vec4 in_f_color;\n"
4994 "void main (void)\n"
4996 " o_color = in_f_color;\n"
4999 << glu::TransformFeedbackVarying ("out_te_output")
5000 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
5002 m_testCtx.getLog() << *m_program;
5003 if (!m_program->isOk())
5004 TCU_FAIL("Program compilation failed");
5007 void TessCoordComponentInvarianceCase::deinit (void)
5012 vector<float> TessCoordComponentInvarianceCase::genTessLevelCases (int numCases)
5014 de::Random rnd(123);
5015 vector<float> result;
5017 for (int i = 0; i < numCases; i++)
5018 for (int j = 0; j < 6; j++)
5019 result.push_back(rnd.getFloat(1.0f, 63.0f));
5024 TessCoordComponentInvarianceCase::IterateResult TessCoordComponentInvarianceCase::iterate (void)
5026 typedef TransformFeedbackHandler<Vec3> TFHandler;
5028 TestLog& log = m_testCtx.getLog();
5029 const RenderContext& renderCtx = m_context.getRenderContext();
5030 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
5031 const glw::Functions& gl = renderCtx.getFunctions();
5032 const int numTessLevelCases = 32;
5033 const vector<float> tessLevels = genTessLevelCases(numTessLevelCases);
5034 const deUint32 programGL = m_program->getProgram();
5036 gl.useProgram(programGL);
5037 setViewport(gl, viewport);
5038 gl.patchParameteri(GL_PATCH_VERTICES, 6);
5041 // Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once.
5042 int maxNumVerticesInDrawCall = 0;
5043 for (int i = 0; i < numTessLevelCases; i++)
5044 maxNumVerticesInDrawCall = de::max(maxNumVerticesInDrawCall, referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &tessLevels[6*i+0], &tessLevels[6*i+2]));
5047 const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall);
5049 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < numTessLevelCases; tessLevelCaseNdx++)
5051 log << TestLog::Message << "Testing with tessellation levels: " << tessellationLevelsString(&tessLevels[6*tessLevelCaseNdx+0], &tessLevels[6*tessLevelCaseNdx+2]) << TestLog::EndMessage;
5053 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)6, 0, &tessLevels[6*tessLevelCaseNdx]) };
5054 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode),
5055 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], 6);
5057 for (int vtxNdx = 0; vtxNdx < (int)tfResult.varying.size(); vtxNdx++)
5059 const Vec3& vec = tfResult.varying[vtxNdx];
5060 const int numComps = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2;
5062 for (int compNdx = 0; compNdx < numComps; compNdx++)
5064 if (!checkTessCoordComponent(vec[compNdx]))
5066 log << TestLog::Message << "Note: output value at index " << vtxNdx << " is "
5067 << (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? de::toString(vec) : de::toString(vec.swizzle(0,1)))
5068 << TestLog::EndMessage;
5069 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid tessellation coordinate component");
5078 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
5082 /*--------------------------------------------------------------------*//*!
5083 * \brief Test first part of invariance rule #8
5085 * Test that all (relevant) components of tess coord are in [0,1].
5086 *//*--------------------------------------------------------------------*/
5087 class TessCoordComponentRangeCase : public TessCoordComponentInvarianceCase
5090 TessCoordComponentRangeCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
5091 : TessCoordComponentInvarianceCase(context, name, description, primType, spacing, winding, usePointMode)
5096 virtual string tessEvalOutputComponentStatements (const char* tessCoordComponentName, const char* outputComponentName) const
5098 return string() + "\t" + outputComponentName + " = " + tessCoordComponentName + ";\n";
5101 virtual bool checkTessCoordComponent (float component) const
5103 if (!de::inRange(component, 0.0f, 1.0f))
5105 m_testCtx.getLog() << TestLog::Message << "Failure: tess coord component isn't in range [0,1]" << TestLog::EndMessage;
5112 /*--------------------------------------------------------------------*//*!
5113 * \brief Test second part of invariance rule #8
5115 * Test that all (relevant) components of tess coord are in [0,1] and
5116 * 1.0-c is exact for every such component c.
5117 *//*--------------------------------------------------------------------*/
5118 class OneMinusTessCoordComponentCase : public TessCoordComponentInvarianceCase
5121 OneMinusTessCoordComponentCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
5122 : TessCoordComponentInvarianceCase(context, name, description, primType, spacing, winding, usePointMode)
5127 virtual string tessEvalOutputComponentStatements (const char* tessCoordComponentName, const char* outputComponentName) const
5129 return string() + " {\n"
5130 " float oneMinusComp = 1.0 - " + tessCoordComponentName + ";\n"
5131 " " + outputComponentName + " = " + tessCoordComponentName + " + oneMinusComp;\n"
5135 virtual bool checkTessCoordComponent (float component) const
5137 if (component != 1.0f)
5139 m_testCtx.getLog() << TestLog::Message << "Failure: comp + (1.0-comp) doesn't equal 1.0 for some component of tessellation coordinate" << TestLog::EndMessage;
5146 /*--------------------------------------------------------------------*//*!
5147 * \brief Test that patch is discarded if relevant outer level <= 0.0
5149 * Draws patches with different combinations of tessellation levels,
5150 * varying which levels are negative. Verifies by checking that colored
5151 * pixels exist inside the area of valid primitives, and only black pixels
5152 * exist inside the area of discarded primitives. An additional sanity
5153 * test is done, checking that the number of primitives written by TF is
5155 *//*--------------------------------------------------------------------*/
5156 class PrimitiveDiscardCase : public TestCase
5159 PrimitiveDiscardCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
5160 : TestCase (context, name, description)
5161 , m_primitiveType (primType)
5162 , m_spacing (spacing)
5163 , m_winding (winding)
5164 , m_usePointMode (usePointMode)
5170 IterateResult iterate (void);
5173 static vector<float> genAttributes (void);
5175 static const int RENDER_SIZE = 256;
5177 const TessPrimitiveType m_primitiveType;
5178 const SpacingMode m_spacing;
5179 const Winding m_winding;
5180 const bool m_usePointMode;
5182 SharedPtr<const glu::ShaderProgram> m_program;
5185 void PrimitiveDiscardCase::init (void)
5187 checkTessellationSupport(m_context);
5188 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
5190 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
5191 << glu::VertexSource ("#version 310 es\n"
5193 "in highp float in_v_attr;\n"
5194 "out highp float in_tc_attr;\n"
5196 "void main (void)\n"
5198 " in_tc_attr = in_v_attr;\n"
5201 << glu::TessellationControlSource ("#version 310 es\n"
5202 "#extension GL_EXT_tessellation_shader : require\n"
5204 "layout (vertices = 1) out;\n"
5206 "in highp float in_tc_attr[];\n"
5208 "patch out highp vec2 in_te_positionScale;\n"
5209 "patch out highp vec2 in_te_positionOffset;\n"
5211 "void main (void)\n"
5213 " in_te_positionScale = vec2(in_tc_attr[6], in_tc_attr[7]);\n"
5214 " in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\n"
5216 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
5217 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
5219 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
5220 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
5221 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
5222 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
5225 << glu::TessellationEvaluationSource ("#version 310 es\n"
5226 "#extension GL_EXT_tessellation_shader : require\n"
5228 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) +
5230 "patch in highp vec2 in_te_positionScale;\n"
5231 "patch in highp vec2 in_te_positionOffset;\n"
5233 "out highp vec3 out_te_tessCoord;\n"
5235 "void main (void)\n"
5237 " out_te_tessCoord = gl_TessCoord;\n"
5238 " gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n"
5241 << glu::FragmentSource ("#version 310 es\n"
5243 "layout (location = 0) out mediump vec4 o_color;\n"
5245 "void main (void)\n"
5247 " o_color = vec4(1.0);\n"
5250 << glu::TransformFeedbackVarying ("out_te_tessCoord")
5251 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
5253 m_testCtx.getLog() << *m_program;
5254 if (!m_program->isOk())
5255 TCU_FAIL("Program compilation failed");
5258 void PrimitiveDiscardCase::deinit (void)
5263 vector<float> PrimitiveDiscardCase::genAttributes (void)
5265 // Generate input attributes (tessellation levels, and position scale and
5266 // offset) for a number of primitives. Each primitive has a different
5267 // combination of tessellatio levels; each level is either a valid
5268 // value or an "invalid" value (negative or zero, chosen from
5269 // invalidTessLevelChoices).
5271 // \note The attributes are generated in such an order that all of the
5272 // valid attribute tuples come before the first invalid one both
5273 // in the result vector, and when scanning the resulting 2d grid
5274 // of primitives is scanned in y-major order. This makes
5275 // verification somewhat simpler.
5277 static const float baseTessLevels[6] = { 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f };
5278 static const float invalidTessLevelChoices[] = { -0.42f, 0.0f };
5279 const int numChoices = 1 + DE_LENGTH_OF_ARRAY(invalidTessLevelChoices);
5280 float choices[6][numChoices];
5281 vector<float> result;
5283 for (int levelNdx = 0; levelNdx < 6; levelNdx++)
5284 for (int choiceNdx = 0; choiceNdx < numChoices; choiceNdx++)
5285 choices[levelNdx][choiceNdx] = choiceNdx == 0 ? baseTessLevels[levelNdx] : invalidTessLevelChoices[choiceNdx-1];
5288 const int numCols = intPow(numChoices, 6/2); // sqrt(numChoices**6) == sqrt(number of primitives)
5289 const int numRows = numCols;
5292 // We could do this with some generic combination-generation function, but meh, it's not that bad.
5293 for (i[2] = 0; i[2] < numChoices; i[2]++) // First outer
5294 for (i[3] = 0; i[3] < numChoices; i[3]++) // Second outer
5295 for (i[4] = 0; i[4] < numChoices; i[4]++) // Third outer
5296 for (i[5] = 0; i[5] < numChoices; i[5]++) // Fourth outer
5297 for (i[0] = 0; i[0] < numChoices; i[0]++) // First inner
5298 for (i[1] = 0; i[1] < numChoices; i[1]++) // Second inner
5300 for (int j = 0; j < 6; j++)
5301 result.push_back(choices[j][i[j]]);
5304 const int col = index % numCols;
5305 const int row = index / numCols;
5307 result.push_back((float)2.0f / (float)numCols);
5308 result.push_back((float)2.0f / (float)numRows);
5310 result.push_back((float)col / (float)numCols * 2.0f - 1.0f);
5311 result.push_back((float)row / (float)numRows * 2.0f - 1.0f);
5321 PrimitiveDiscardCase::IterateResult PrimitiveDiscardCase::iterate (void)
5323 typedef TransformFeedbackHandler<Vec3> TFHandler;
5325 TestLog& log = m_testCtx.getLog();
5326 const RenderContext& renderCtx = m_context.getRenderContext();
5327 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
5328 const glw::Functions& gl = renderCtx.getFunctions();
5329 const vector<float> attributes = genAttributes();
5330 const int numAttribsPerPrimitive = 6+2+2; // Tess levels, scale, offset.
5331 const int numPrimitives = (int)attributes.size() / numAttribsPerPrimitive;
5332 const deUint32 programGL = m_program->getProgram();
5334 gl.useProgram(programGL);
5335 setViewport(gl, viewport);
5336 gl.patchParameteri(GL_PATCH_VERTICES, numAttribsPerPrimitive);
5338 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
5339 gl.clear(GL_COLOR_BUFFER_BIT);
5341 // Check the convenience assertion that all discarded patches come after the last non-discarded patch.
5343 bool discardedPatchEncountered = false;
5344 for (int patchNdx = 0; patchNdx < numPrimitives; patchNdx++)
5346 const bool discard = isPatchDiscarded(m_primitiveType, &attributes[numAttribsPerPrimitive*patchNdx + 2]);
5347 DE_ASSERT(discard || !discardedPatchEncountered);
5348 discardedPatchEncountered = discard;
5350 DE_UNREF(discardedPatchEncountered);
5354 int numVerticesInDrawCall = 0;
5355 for (int patchNdx = 0; patchNdx < numPrimitives; patchNdx++)
5356 numVerticesInDrawCall += referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &attributes[numAttribsPerPrimitive*patchNdx+0], &attributes[numAttribsPerPrimitive*patchNdx+2]);
5358 log << TestLog::Message << "Note: rendering " << numPrimitives << " patches; first patches have valid relevant outer levels, "
5359 << "but later patches have one or more invalid (i.e. less than or equal to 0.0) relevant outer levels" << TestLog::EndMessage;
5362 const TFHandler tfHandler (m_context.getRenderContext(), numVerticesInDrawCall);
5363 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)attributes.size(), 0, &attributes[0]) };
5364 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode),
5365 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)attributes.size());
5366 const tcu::Surface pixels = getPixels(renderCtx, viewport);
5368 log << TestLog::Image("RenderedImage", "Rendered image", pixels);
5370 if ((int)tfResult.varying.size() != numVerticesInDrawCall)
5372 log << TestLog::Message << "Failure: expected " << numVerticesInDrawCall << " vertices from transform feedback, got " << tfResult.varying.size() << TestLog::EndMessage;
5373 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Wrong number of tessellation coordinates");
5377 // Check that white pixels are found around every non-discarded
5378 // patch, and that only black pixels are found after the last
5379 // non-discarded patch.
5381 int lastWhitePixelRow = 0;
5382 int secondToLastWhitePixelRow = 0;
5383 int lastWhitePixelColumnOnSecondToLastWhitePixelRow = 0;
5385 for (int patchNdx = 0; patchNdx < numPrimitives; patchNdx++)
5387 const float* const attr = &attributes[numAttribsPerPrimitive*patchNdx];
5388 const bool validLevels = !isPatchDiscarded(m_primitiveType, &attr[2]);
5392 // Not a discarded patch; check that at least one white pixel is found in its area.
5394 const float* const scale = &attr[6];
5395 const float* const offset = &attr[8];
5396 const int x0 = (int)(( offset[0] + 1.0f)*0.5f*(float)pixels.getWidth()) - 1;
5397 const int x1 = (int)((scale[0] + offset[0] + 1.0f)*0.5f*(float)pixels.getWidth()) + 1;
5398 const int y0 = (int)(( offset[1] + 1.0f)*0.5f*(float)pixels.getHeight()) - 1;
5399 const int y1 = (int)((scale[1] + offset[1] + 1.0f)*0.5f*(float)pixels.getHeight()) + 1;
5400 const bool isMSAA = renderCtx.getRenderTarget().getNumSamples() > 1;
5401 bool pixelOk = false;
5403 if (y1 > lastWhitePixelRow)
5405 secondToLastWhitePixelRow = lastWhitePixelRow;
5406 lastWhitePixelRow = y1;
5408 lastWhitePixelColumnOnSecondToLastWhitePixelRow = x1;
5410 for (int y = y0; y <= y1 && !pixelOk; y++)
5411 for (int x = x0; x <= x1 && !pixelOk; x++)
5413 if (!de::inBounds(x, 0, pixels.getWidth()) || !de::inBounds(y, 0, pixels.getHeight()))
5418 if (pixels.getPixel(x, y) != tcu::RGBA::black())
5423 if (pixels.getPixel(x, y) == tcu::RGBA::white())
5430 log << TestLog::Message << "Failure: expected at least one " << (isMSAA ? "non-black" : "white") << " pixel in the rectangle "
5431 << "[x0=" << x0 << ", y0=" << y0 << ", x1=" << x1 << ", y1=" << y1 << "]" << TestLog::EndMessage
5432 << TestLog::Message << "Note: the rectangle approximately corresponds to the patch with these tessellation levels: "
5433 << tessellationLevelsString(&attr[0], &attr[1]) << TestLog::EndMessage;
5434 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
5440 // First discarded primitive patch; the remaining are guaranteed to be discarded ones as well.
5442 for (int y = 0; y < pixels.getHeight(); y++)
5443 for (int x = 0; x < pixels.getWidth(); x++)
5445 if (y > lastWhitePixelRow || (y > secondToLastWhitePixelRow && x > lastWhitePixelColumnOnSecondToLastWhitePixelRow))
5447 if (pixels.getPixel(x, y) != tcu::RGBA::black())
5449 log << TestLog::Message << "Failure: expected all pixels to be black in the area "
5450 << (lastWhitePixelColumnOnSecondToLastWhitePixelRow < pixels.getWidth()-1
5451 ? string() + "y > " + de::toString(lastWhitePixelRow) + " || (y > " + de::toString(secondToLastWhitePixelRow)
5452 + " && x > " + de::toString(lastWhitePixelColumnOnSecondToLastWhitePixelRow) + ")"
5453 : string() + "y > " + de::toString(lastWhitePixelRow))
5454 << " (they all correspond to patches that should be discarded)" << TestLog::EndMessage
5455 << TestLog::Message << "Note: pixel " << tcu::IVec2(x, y) << " isn't black" << TestLog::EndMessage;
5456 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
5469 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
5473 /*--------------------------------------------------------------------*//*!
5474 * \brief Case testing user-defined IO between TCS and TES
5476 * TCS outputs various values to TES, including aggregates. The outputs
5477 * can be per-patch or per-vertex, and if per-vertex, they can also be in
5478 * an IO block. Per-vertex input array size can be left implicit (i.e.
5479 * inputArray[]) or explicit either by gl_MaxPatchVertices or an integer
5480 * literal whose value is queried from GL.
5482 * The values output are generated in TCS and verified in TES against
5483 * similarly generated values. In case a verification of a value fails, the
5484 * index of the invalid value is output with TF.
5485 * As a sanity check, also the rendering result is verified (against pre-
5486 * rendered reference).
5487 *//*--------------------------------------------------------------------*/
5488 class UserDefinedIOCase : public TestCase
5493 IO_TYPE_PER_PATCH = 0,
5494 IO_TYPE_PER_PATCH_ARRAY,
5495 IO_TYPE_PER_PATCH_BLOCK,
5496 IO_TYPE_PER_PATCH_BLOCK_ARRAY,
5498 IO_TYPE_PER_VERTEX_BLOCK,
5503 enum VertexIOArraySize
5505 VERTEX_IO_ARRAY_SIZE_IMPLICIT = 0,
5506 VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN, //!< Use gl_MaxPatchVertices as size for per-vertex input array.
5507 VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY, //!< Query GL_MAX_PATCH_VERTICES, and use that as size for per-vertex input array.
5509 VERTEX_IO_ARRAY_SIZE_LAST
5512 enum TessControlOutArraySize
5514 TESS_CONTROL_OUT_ARRAY_SIZE_IMPLICIT = 0,
5515 TESS_CONTROL_OUT_ARRAY_SIZE_LAYOUT,
5516 TESS_CONTROL_OUT_ARRAY_SIZE_QUERY,
5517 TESS_CONTROL_OUT_ARRAY_SIZE_SHADER_BUILTIN
5520 UserDefinedIOCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, IOType ioType, VertexIOArraySize vertexIOArraySize, TessControlOutArraySize tessControlOutArraySize, const char* referenceImagePath)
5521 : TestCase (context, name, description)
5522 , m_primitiveType (primType)
5524 , m_vertexIOArraySize (vertexIOArraySize)
5525 , m_tessControlOutArraySize (tessControlOutArraySize)
5526 , m_referenceImagePath (referenceImagePath)
5532 IterateResult iterate (void);
5535 typedef string (*BasicTypeVisitFunc)(const string& name, glu::DataType type, int indentationDepth); //!< See glslTraverseBasicTypes below.
5537 class TopLevelObject
5540 virtual ~TopLevelObject (void) {}
5542 virtual string name (void) const = 0;
5543 virtual string declare (void) const = 0;
5544 virtual string declareArray (const string& arraySizeExpr) const = 0;
5545 virtual string glslTraverseBasicTypeArray (int numArrayElements, //!< If negative, traverse just array[gl_InvocationID], not all indices.
5546 int indentationDepth,
5547 BasicTypeVisitFunc) const = 0;
5548 virtual string glslTraverseBasicType (int indentationDepth,
5549 BasicTypeVisitFunc) const = 0;
5550 virtual int numBasicSubobjectsInElementType (void) const = 0;
5551 virtual string basicSubobjectAtIndex (int index, int arraySize) const = 0;
5554 class Variable : public TopLevelObject
5557 Variable (const string& name_, const glu::VarType& type, bool isArray)
5560 , m_isArray (isArray)
5562 DE_ASSERT(!type.isArrayType());
5565 string name (void) const { return m_name; }
5566 string declare (void) const;
5567 string declareArray (const string& arraySizeExpr) const;
5568 string glslTraverseBasicTypeArray (int numArrayElements, int indentationDepth, BasicTypeVisitFunc) const;
5569 string glslTraverseBasicType (int indentationDepth, BasicTypeVisitFunc) const;
5570 int numBasicSubobjectsInElementType (void) const;
5571 string basicSubobjectAtIndex (int index, int arraySize) const;
5575 glu::VarType m_type; //!< If this Variable is an array element, m_type is the element type; otherwise just the variable type.
5576 const bool m_isArray;
5579 class IOBlock : public TopLevelObject
5586 Member (const string& n, const glu::VarType& t) : name(n), type(t) {}
5589 IOBlock (const string& blockName, const string& interfaceName, const vector<Member>& members)
5590 : m_blockName (blockName)
5591 , m_interfaceName (interfaceName)
5592 , m_members (members)
5596 string name (void) const { return m_interfaceName; }
5597 string declare (void) const;
5598 string declareArray (const string& arraySizeExpr) const;
5599 string glslTraverseBasicTypeArray (int numArrayElements, int indentationDepth, BasicTypeVisitFunc) const;
5600 string glslTraverseBasicType (int indentationDepth, BasicTypeVisitFunc) const;
5601 int numBasicSubobjectsInElementType (void) const;
5602 string basicSubobjectAtIndex (int index, int arraySize) const;
5606 string m_interfaceName;
5607 vector<Member> m_members;
5610 static string glslTraverseBasicTypes (const string& rootName,
5611 const glu::VarType& rootType,
5612 int arrayNestingDepth,
5613 int indentationDepth,
5614 BasicTypeVisitFunc visit);
5616 static string glslAssignBasicTypeObject (const string& name, glu::DataType, int indentationDepth);
5617 static string glslCheckBasicTypeObject (const string& name, glu::DataType, int indentationDepth);
5618 static int numBasicSubobjectsInElementType (const vector<SharedPtr<TopLevelObject> >&);
5619 static string basicSubobjectAtIndex (int index, const vector<SharedPtr<TopLevelObject> >&, int topLevelArraySizes);
5627 NUM_OUTPUT_VERTICES = 5
5631 NUM_PER_PATCH_ARRAY_ELEMS = 3
5635 NUM_PER_PATCH_BLOCKS = 2
5638 const TessPrimitiveType m_primitiveType;
5639 const IOType m_ioType;
5640 const VertexIOArraySize m_vertexIOArraySize;
5641 const TessControlOutArraySize m_tessControlOutArraySize;
5642 const string m_referenceImagePath;
5644 vector<glu::StructType> m_structTypes;
5645 vector<SharedPtr<TopLevelObject> > m_tcsOutputs;
5646 vector<SharedPtr<TopLevelObject> > m_tesInputs;
5648 SharedPtr<const glu::ShaderProgram> m_program;
5651 /*--------------------------------------------------------------------*//*!
5652 * \brief Generate GLSL code to traverse (possibly aggregate) object
5654 * Generates a string that represents GLSL code that traverses the
5655 * basic-type subobjects in a rootType-typed object named rootName. Arrays
5656 * are traversed with loops and struct members are each traversed
5657 * separately. The code for each basic-type subobject is generated with
5658 * the function given as the 'visit' argument.
5659 *//*--------------------------------------------------------------------*/
5660 string UserDefinedIOCase::glslTraverseBasicTypes (const string& rootName,
5661 const glu::VarType& rootType,
5662 int arrayNestingDepth,
5663 int indentationDepth,
5664 BasicTypeVisitFunc visit)
5666 if (rootType.isBasicType())
5667 return visit(rootName, rootType.getBasicType(), indentationDepth);
5668 else if (rootType.isArrayType())
5670 const string indentation = string(indentationDepth, '\t');
5671 const string loopIndexName = "i" + de::toString(arrayNestingDepth);
5672 const string arrayLength = de::toString(rootType.getArraySize());
5673 return indentation + "for (int " + loopIndexName + " = 0; " + loopIndexName + " < " + de::toString(rootType.getArraySize()) + "; " + loopIndexName + "++)\n" +
5674 indentation + "{\n" +
5675 glslTraverseBasicTypes(rootName + "[" + loopIndexName + "]", rootType.getElementType(), arrayNestingDepth+1, indentationDepth+1, visit) +
5676 indentation + "}\n";
5678 else if (rootType.isStructType())
5680 const glu::StructType& structType = *rootType.getStructPtr();
5681 const int numMembers = structType.getNumMembers();
5684 for (int membNdx = 0; membNdx < numMembers; membNdx++)
5686 const glu::StructMember& member = structType.getMember(membNdx);
5687 result += glslTraverseBasicTypes(rootName + "." + member.getName(), member.getType(), arrayNestingDepth, indentationDepth, visit);
5699 string UserDefinedIOCase::Variable::declare (void) const
5701 DE_ASSERT(!m_isArray);
5702 return de::toString(glu::declare(m_type, m_name)) + ";\n";
5705 string UserDefinedIOCase::Variable::declareArray (const string& sizeExpr) const
5707 DE_ASSERT(m_isArray);
5708 return de::toString(glu::declare(m_type, m_name)) + "[" + sizeExpr + "];\n";
5711 string UserDefinedIOCase::IOBlock::declare (void) const
5713 std::ostringstream buf;
5715 buf << m_blockName << "\n"
5718 for (int i = 0; i < (int)m_members.size(); i++)
5719 buf << "\t" << glu::declare(m_members[i].type, m_members[i].name) << ";\n";
5721 buf << "} " << m_interfaceName << ";\n";
5725 string UserDefinedIOCase::IOBlock::declareArray (const string& sizeExpr) const
5727 std::ostringstream buf;
5729 buf << m_blockName << "\n"
5732 for (int i = 0; i < (int)m_members.size(); i++)
5733 buf << "\t" << glu::declare(m_members[i].type, m_members[i].name) << ";\n";
5735 buf << "} " << m_interfaceName << "[" << sizeExpr << "];\n";
5739 string UserDefinedIOCase::Variable::glslTraverseBasicTypeArray (int numArrayElements, int indentationDepth, BasicTypeVisitFunc visit) const
5741 DE_ASSERT(m_isArray);
5743 const bool traverseAsArray = numArrayElements >= 0;
5744 const string traversedName = m_name + (!traverseAsArray ? "[gl_InvocationID]" : "");
5745 const glu::VarType type = traverseAsArray ? glu::VarType(m_type, numArrayElements) : m_type;
5747 return UserDefinedIOCase::glslTraverseBasicTypes(traversedName, type, 0, indentationDepth, visit);
5750 string UserDefinedIOCase::Variable::glslTraverseBasicType (int indentationDepth, BasicTypeVisitFunc visit) const
5752 DE_ASSERT(!m_isArray);
5754 return UserDefinedIOCase::glslTraverseBasicTypes(m_name, m_type, 0, indentationDepth, visit);
5757 string UserDefinedIOCase::IOBlock::glslTraverseBasicTypeArray (int numArrayElements, int indentationDepth, BasicTypeVisitFunc visit) const
5759 if (numArrayElements >= 0)
5761 const string indentation = string(indentationDepth, '\t');
5762 string result = indentation + "for (int i0 = 0; i0 < " + de::toString(numArrayElements) + "; i0++)\n" +
5763 indentation + "{\n";
5764 for (int i = 0; i < (int)m_members.size(); i++)
5765 result += UserDefinedIOCase::glslTraverseBasicTypes(m_interfaceName + "[i0]." + m_members[i].name, m_members[i].type, 1, indentationDepth+1, visit);
5766 result += indentation + "}\n";
5772 for (int i = 0; i < (int)m_members.size(); i++)
5773 result += UserDefinedIOCase::glslTraverseBasicTypes(m_interfaceName + "[gl_InvocationID]." + m_members[i].name, m_members[i].type, 0, indentationDepth, visit);
5779 string UserDefinedIOCase::IOBlock::glslTraverseBasicType (int indentationDepth, BasicTypeVisitFunc visit) const
5782 for (int i = 0; i < (int)m_members.size(); i++)
5783 result += UserDefinedIOCase::glslTraverseBasicTypes(m_interfaceName + "." + m_members[i].name, m_members[i].type, 0, indentationDepth, visit);
5787 int UserDefinedIOCase::Variable::numBasicSubobjectsInElementType (void) const
5789 return numBasicSubobjects(m_type);
5792 int UserDefinedIOCase::IOBlock::numBasicSubobjectsInElementType (void) const
5795 for (int i = 0; i < (int)m_members.size(); i++)
5796 result += numBasicSubobjects(m_members[i].type);
5800 string UserDefinedIOCase::Variable::basicSubobjectAtIndex (int subobjectIndex, int arraySize) const
5802 const glu::VarType type = m_isArray ? glu::VarType(m_type, arraySize) : m_type;
5803 int currentIndex = 0;
5805 for (glu::BasicTypeIterator basicIt = glu::BasicTypeIterator::begin(&type);
5806 basicIt != glu::BasicTypeIterator::end(&type);
5809 if (currentIndex == subobjectIndex)
5810 return m_name + de::toString(glu::TypeAccessFormat(type, basicIt.getPath()));
5817 string UserDefinedIOCase::IOBlock::basicSubobjectAtIndex (int subobjectIndex, int arraySize) const
5819 int currentIndex = 0;
5820 for (int arrayNdx = 0; arrayNdx < arraySize; arrayNdx++)
5822 for (int memberNdx = 0; memberNdx < (int)m_members.size(); memberNdx++)
5824 const glu::VarType& membType = m_members[memberNdx].type;
5825 for (glu::BasicTypeIterator basicIt = glu::BasicTypeIterator::begin(&membType);
5826 basicIt != glu::BasicTypeIterator::end(&membType);
5829 if (currentIndex == subobjectIndex)
5830 return m_interfaceName + "[" + de::toString(arrayNdx) + "]." + m_members[memberNdx].name + de::toString(glu::TypeAccessFormat(membType, basicIt.getPath()));
5839 // Used as the 'visit' argument for glslTraverseBasicTypes.
5840 string UserDefinedIOCase::glslAssignBasicTypeObject (const string& name, glu::DataType type, int indentationDepth)
5842 const int scalarSize = glu::getDataTypeScalarSize(type);
5843 const string indentation = string(indentationDepth, '\t');
5846 result += indentation + name + " = ";
5848 if (type != glu::TYPE_FLOAT)
5849 result += string() + glu::getDataTypeName(type) + "(";
5850 for (int i = 0; i < scalarSize; i++)
5851 result += (i > 0 ? ", v+" + de::floatToString(0.8f*(float)i, 1)
5853 if (type != glu::TYPE_FLOAT)
5856 indentation + "v += 0.4;\n";
5860 // Used as the 'visit' argument for glslTraverseBasicTypes.
5861 string UserDefinedIOCase::glslCheckBasicTypeObject (const string& name, glu::DataType type, int indentationDepth)
5863 const int scalarSize = glu::getDataTypeScalarSize(type);
5864 const string indentation = string(indentationDepth, '\t');
5867 result += indentation + "allOk = allOk && compare_" + glu::getDataTypeName(type) + "(" + name + ", ";
5869 if (type != glu::TYPE_FLOAT)
5870 result += string() + glu::getDataTypeName(type) + "(";
5871 for (int i = 0; i < scalarSize; i++)
5872 result += (i > 0 ? ", v+" + de::floatToString(0.8f*(float)i, 1)
5874 if (type != glu::TYPE_FLOAT)
5877 indentation + "v += 0.4;\n" +
5878 indentation + "if (allOk) firstFailedInputIndex++;\n";
5883 int UserDefinedIOCase::numBasicSubobjectsInElementType (const vector<SharedPtr<TopLevelObject> >& objects)
5886 for (int i = 0; i < (int)objects.size(); i++)
5887 result += objects[i]->numBasicSubobjectsInElementType();
5891 string UserDefinedIOCase::basicSubobjectAtIndex (int subobjectIndex, const vector<SharedPtr<TopLevelObject> >& objects, int topLevelArraySize)
5893 int currentIndex = 0;
5894 int objectIndex = 0;
5895 for (; currentIndex < subobjectIndex; objectIndex++)
5896 currentIndex += objects[objectIndex]->numBasicSubobjectsInElementType() * topLevelArraySize;
5897 if (currentIndex > subobjectIndex)
5900 currentIndex -= objects[objectIndex]->numBasicSubobjectsInElementType() * topLevelArraySize;
5903 return objects[objectIndex]->basicSubobjectAtIndex(subobjectIndex - currentIndex, topLevelArraySize);
5906 void UserDefinedIOCase::init (void)
5908 checkTessellationSupport(m_context);
5909 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
5911 const bool isPerPatchIO = m_ioType == IO_TYPE_PER_PATCH ||
5912 m_ioType == IO_TYPE_PER_PATCH_ARRAY ||
5913 m_ioType == IO_TYPE_PER_PATCH_BLOCK ||
5914 m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY;
5916 const bool isExplicitVertexArraySize = m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN ||
5917 m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY;
5919 const string vertexAttrArrayInputSize = m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_IMPLICIT ? ""
5920 : m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN ? "gl_MaxPatchVertices"
5921 : m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY ? de::toString(m_context.getContextInfo().getInt(GL_MAX_PATCH_VERTICES))
5924 const char* const maybePatch = isPerPatchIO ? "patch " : "";
5925 const string outMaybePatch = string() + maybePatch + "out ";
5926 const string inMaybePatch = string() + maybePatch + "in ";
5927 const bool useBlock = m_ioType == IO_TYPE_PER_VERTEX_BLOCK ||
5928 m_ioType == IO_TYPE_PER_PATCH_BLOCK ||
5929 m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY;
5931 string tcsDeclarations;
5932 string tcsStatements;
5934 string tesDeclarations;
5935 string tesStatements;
5938 m_structTypes.push_back(glu::StructType("S"));
5940 const glu::VarType highpFloat (glu::TYPE_FLOAT, glu::PRECISION_HIGHP);
5941 glu::StructType& structType = m_structTypes.back();
5942 const glu::VarType structVarType (&structType);
5943 bool usedStruct = false;
5945 structType.addMember("x", glu::VarType(glu::TYPE_INT, glu::PRECISION_HIGHP));
5946 structType.addMember("y", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP));
5950 // It is illegal to have a structure containing an array as an output variable
5951 structType.addMember("z", glu::VarType(highpFloat, 2));
5956 const bool useLightweightBlock = (m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY); // use leaner block to make sure it is not larger than allowed (per-patch storage is very limited)
5957 vector<IOBlock::Member> blockMembers;
5959 if (!useLightweightBlock)
5960 blockMembers.push_back(IOBlock::Member("blockS", structVarType));
5962 blockMembers.push_back(IOBlock::Member("blockFa", glu::VarType(highpFloat, 3)));
5963 blockMembers.push_back(IOBlock::Member("blockSa", glu::VarType(structVarType, 2)));
5964 blockMembers.push_back(IOBlock::Member("blockF", highpFloat));
5966 m_tcsOutputs.push_back (SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "tcBlock", blockMembers)));
5967 m_tesInputs.push_back (SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "teBlock", blockMembers)));
5973 const Variable var0("in_te_s", structVarType, m_ioType != IO_TYPE_PER_PATCH);
5974 const Variable var1("in_te_f", highpFloat, m_ioType != IO_TYPE_PER_PATCH);
5976 if (m_ioType != IO_TYPE_PER_PATCH_ARRAY)
5978 // Arrays of structures are disallowed, add struct cases only if not arrayed variable
5979 m_tcsOutputs.push_back (SharedPtr<TopLevelObject>(new Variable(var0)));
5980 m_tesInputs.push_back (SharedPtr<TopLevelObject>(new Variable(var0)));
5985 m_tcsOutputs.push_back (SharedPtr<TopLevelObject>(new Variable(var1)));
5986 m_tesInputs.push_back (SharedPtr<TopLevelObject>(new Variable(var1)));
5989 tcsDeclarations += "in " + Variable("in_tc_attr", highpFloat, true).declareArray(vertexAttrArrayInputSize);
5992 tcsDeclarations += de::toString(glu::declare(structType)) + ";\n";
5994 tcsStatements += "\t{\n"
5995 "\t\thighp float v = 1.3;\n";
5997 for (int tcsOutputNdx = 0; tcsOutputNdx < (int)m_tcsOutputs.size(); tcsOutputNdx++)
5999 const TopLevelObject& output = *m_tcsOutputs[tcsOutputNdx];
6000 const int numElements = !isPerPatchIO ? -1 //!< \note -1 means indexing with gl_InstanceID
6001 : m_ioType == IO_TYPE_PER_PATCH ? 1
6002 : m_ioType == IO_TYPE_PER_PATCH_ARRAY ? NUM_PER_PATCH_ARRAY_ELEMS
6003 : m_ioType == IO_TYPE_PER_PATCH_BLOCK ? 1
6004 : m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? NUM_PER_PATCH_BLOCKS
6006 const bool isArray = (numElements != 1);
6008 DE_ASSERT(numElements != -2);
6012 tcsDeclarations += outMaybePatch + output.declareArray(m_ioType == IO_TYPE_PER_PATCH_ARRAY ? de::toString(int(NUM_PER_PATCH_ARRAY_ELEMS))
6013 : m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? de::toString(int(NUM_PER_PATCH_BLOCKS))
6014 : m_tessControlOutArraySize == TESS_CONTROL_OUT_ARRAY_SIZE_LAYOUT ? de::toString(int(NUM_OUTPUT_VERTICES))
6015 : m_tessControlOutArraySize == TESS_CONTROL_OUT_ARRAY_SIZE_QUERY ? de::toString(m_context.getContextInfo().getInt(GL_MAX_PATCH_VERTICES))
6016 : m_tessControlOutArraySize == TESS_CONTROL_OUT_ARRAY_SIZE_SHADER_BUILTIN ? "gl_MaxPatchVertices"
6020 tcsDeclarations += outMaybePatch + output.declare();
6023 tcsStatements += "\t\tv += float(gl_InvocationID)*" + de::floatToString(0.4f * (float)output.numBasicSubobjectsInElementType(), 1) + ";\n";
6025 tcsStatements += "\n\t\t// Assign values to output " + output.name() + "\n";
6027 tcsStatements += output.glslTraverseBasicTypeArray(numElements, 2, glslAssignBasicTypeObject);
6029 tcsStatements += output.glslTraverseBasicType(2, glslAssignBasicTypeObject);
6032 tcsStatements += "\t\tv += float(" + de::toString(int(NUM_OUTPUT_VERTICES)) + "-gl_InvocationID-1)*" + de::floatToString(0.4f * (float)output.numBasicSubobjectsInElementType(), 1) + ";\n";
6034 tcsStatements += "\t}\n";
6037 tesDeclarations += de::toString(glu::declare(structType)) + ";\n";
6039 tesStatements += "\tbool allOk = true;\n"
6040 "\thighp uint firstFailedInputIndex = 0u;\n"
6042 "\t\thighp float v = 1.3;\n";
6043 for (int tesInputNdx = 0; tesInputNdx < (int)m_tesInputs.size(); tesInputNdx++)
6045 const TopLevelObject& input = *m_tesInputs[tesInputNdx];
6046 const int numElements = !isPerPatchIO ? (int)NUM_OUTPUT_VERTICES
6047 : m_ioType == IO_TYPE_PER_PATCH ? 1
6048 : m_ioType == IO_TYPE_PER_PATCH_BLOCK ? 1
6049 : m_ioType == IO_TYPE_PER_PATCH_ARRAY ? NUM_PER_PATCH_ARRAY_ELEMS
6050 : m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? NUM_PER_PATCH_BLOCKS
6052 const bool isArray = (numElements != 1);
6054 DE_ASSERT(numElements != -2);
6057 tesDeclarations += inMaybePatch + input.declareArray(m_ioType == IO_TYPE_PER_PATCH_ARRAY ? de::toString(int(NUM_PER_PATCH_ARRAY_ELEMS))
6058 : m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? de::toString(int(NUM_PER_PATCH_BLOCKS))
6059 : isExplicitVertexArraySize ? de::toString(vertexAttrArrayInputSize)
6062 tesDeclarations += inMaybePatch + input.declare();
6064 tesStatements += "\n\t\t// Check values in input " + input.name() + "\n";
6066 tesStatements += input.glslTraverseBasicTypeArray(numElements, 2, glslCheckBasicTypeObject);
6068 tesStatements += input.glslTraverseBasicType(2, glslCheckBasicTypeObject);
6070 tesStatements += "\t}\n";
6073 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
6074 << glu::VertexSource ("#version 310 es\n"
6076 "in highp float in_v_attr;\n"
6077 "out highp float in_tc_attr;\n"
6079 "void main (void)\n"
6081 " in_tc_attr = in_v_attr;\n"
6084 << glu::TessellationControlSource ("#version 310 es\n"
6085 "#extension GL_EXT_tessellation_shader : require\n"
6087 "layout (vertices = " + de::toString(int(NUM_OUTPUT_VERTICES)) + ") out;\n"
6091 "patch out highp vec2 in_te_positionScale;\n"
6092 "patch out highp vec2 in_te_positionOffset;\n"
6094 "void main (void)\n"
6098 " in_te_positionScale = vec2(in_tc_attr[6], in_tc_attr[7]);\n"
6099 " in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\n"
6101 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
6102 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
6104 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
6105 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
6106 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
6107 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
6110 << glu::TessellationEvaluationSource ("#version 310 es\n"
6111 "#extension GL_EXT_tessellation_shader : require\n"
6113 + getTessellationEvaluationInLayoutString(m_primitiveType) +
6117 "patch in highp vec2 in_te_positionScale;\n"
6118 "patch in highp vec2 in_te_positionOffset;\n"
6120 "out highp vec4 in_f_color;\n"
6121 "// Will contain the index of the first incorrect input,\n"
6122 "// or the number of inputs if all are correct\n"
6123 "flat out highp uint out_te_firstFailedInputIndex;\n"
6125 "bool compare_int (int a, int b) { return a == b; }\n"
6126 "bool compare_float (float a, float b) { return abs(a - b) < 0.01f; }\n"
6127 "bool compare_vec4 (vec4 a, vec4 b) { return all(lessThan(abs(a - b), vec4(0.01f))); }\n"
6129 "void main (void)\n"
6133 " gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n"
6134 " in_f_color = allOk ? vec4(0.0, 1.0, 0.0, 1.0)\n"
6135 " : vec4(1.0, 0.0, 0.0, 1.0);\n"
6136 " out_te_firstFailedInputIndex = firstFailedInputIndex;\n"
6139 << glu::FragmentSource ("#version 310 es\n"
6141 "layout (location = 0) out mediump vec4 o_color;\n"
6143 "in highp vec4 in_f_color;\n"
6145 "void main (void)\n"
6147 " o_color = in_f_color;\n"
6150 << glu::TransformFeedbackVarying ("out_te_firstFailedInputIndex")
6151 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
6153 m_testCtx.getLog() << *m_program;
6154 if (!m_program->isOk())
6155 TCU_FAIL("Program compilation failed");
6158 void UserDefinedIOCase::deinit (void)
6163 UserDefinedIOCase::IterateResult UserDefinedIOCase::iterate (void)
6165 typedef TransformFeedbackHandler<deUint32> TFHandler;
6167 TestLog& log = m_testCtx.getLog();
6168 const RenderContext& renderCtx = m_context.getRenderContext();
6169 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
6170 const glw::Functions& gl = renderCtx.getFunctions();
6171 static const float attributes[6+2+2] = { /* inner */ 3.0f, 4.0f, /* outer */ 5.0f, 6.0f, 7.0f, 8.0f, /* pos. scale */ 1.2f, 1.3f, /* pos. offset */ -0.3f, -0.4f };
6172 const deUint32 programGL = m_program->getProgram();
6173 const int numVertices = referenceVertexCount(m_primitiveType, SPACINGMODE_EQUAL, false, &attributes[0], &attributes[2]);
6174 const TFHandler tfHandler (renderCtx, numVertices);
6175 tcu::ResultCollector result;
6177 gl.useProgram(programGL);
6178 setViewport(gl, viewport);
6179 gl.patchParameteri(GL_PATCH_VERTICES, DE_LENGTH_OF_ARRAY(attributes));
6181 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
6182 gl.clear(GL_COLOR_BUFFER_BIT);
6185 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, DE_LENGTH_OF_ARRAY(attributes), 0, &attributes[0]) };
6186 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, false),
6187 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], DE_LENGTH_OF_ARRAY(attributes));
6190 const tcu::Surface pixels = getPixels(renderCtx, viewport);
6191 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath.c_str());
6192 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), pixels.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
6195 result.fail("Image comparison failed");
6198 if ((int)tfResult.varying.size() != numVertices)
6200 log << TestLog::Message << "Failure: transform feedback returned " << tfResult.varying.size() << " vertices; expected " << numVertices << TestLog::EndMessage;
6201 result.fail("Wrong number of vertices");
6205 const int topLevelArraySize = (m_ioType == IO_TYPE_PER_PATCH ? 1
6206 : m_ioType == IO_TYPE_PER_PATCH_ARRAY ? NUM_PER_PATCH_ARRAY_ELEMS
6207 : m_ioType == IO_TYPE_PER_PATCH_BLOCK ? 1
6208 : m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? NUM_PER_PATCH_BLOCKS
6209 : (int)NUM_OUTPUT_VERTICES);
6210 const int numTEInputs = numBasicSubobjectsInElementType(m_tesInputs) * topLevelArraySize;
6212 for (int vertexNdx = 0; vertexNdx < (int)numVertices; vertexNdx++)
6214 if (tfResult.varying[vertexNdx] > (deUint32)numTEInputs)
6216 log << TestLog::Message << "Failure: out_te_firstFailedInputIndex has value " << tfResult.varying[vertexNdx]
6217 << ", should be in range [0, " << numTEInputs << "]" << TestLog::EndMessage;
6218 result.fail("Invalid transform feedback output");
6220 else if (tfResult.varying[vertexNdx] != (deUint32)numTEInputs)
6222 log << TestLog::Message << "Failure: in tessellation evaluation shader, check for input "
6223 << basicSubobjectAtIndex(tfResult.varying[vertexNdx], m_tesInputs, topLevelArraySize) << " failed" << TestLog::EndMessage;
6224 result.fail("Invalid input value in tessellation evaluation shader");
6230 result.setTestContextResult(m_testCtx);
6234 /*--------------------------------------------------------------------*//*!
6235 * \brief Pass gl_Position between VS and TCS, or between TCS and TES.
6237 * In TCS gl_Position is in the gl_out[] block and in TES in the gl_in[]
6238 * block, and has no special semantics in those. Arbitrary vec4 data can
6239 * thus be passed there.
6240 *//*--------------------------------------------------------------------*/
6241 class GLPositionCase : public TestCase
6246 CASETYPE_VS_TO_TCS = 0,
6247 CASETYPE_TCS_TO_TES,
6248 CASETYPE_VS_TO_TCS_TO_TES,
6253 GLPositionCase (Context& context, const char* name, const char* description, CaseType caseType, const char* referenceImagePath)
6254 : TestCase (context, name, description)
6255 , m_caseType (caseType)
6256 , m_referenceImagePath (referenceImagePath)
6262 IterateResult iterate (void);
6264 static const char* getCaseTypeName (CaseType type);
6267 static const int RENDER_SIZE = 256;
6269 const CaseType m_caseType;
6270 const string m_referenceImagePath;
6272 SharedPtr<const glu::ShaderProgram> m_program;
6275 const char* GLPositionCase::getCaseTypeName (CaseType type)
6279 case CASETYPE_VS_TO_TCS: return "gl_position_vs_to_tcs";
6280 case CASETYPE_TCS_TO_TES: return "gl_position_tcs_to_tes";
6281 case CASETYPE_VS_TO_TCS_TO_TES: return "gl_position_vs_to_tcs_to_tes";
6283 DE_ASSERT(false); return DE_NULL;
6287 void GLPositionCase::init (void)
6289 checkTessellationSupport(m_context);
6290 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
6292 const bool vsToTCS = m_caseType == CASETYPE_VS_TO_TCS || m_caseType == CASETYPE_VS_TO_TCS_TO_TES;
6293 const bool tcsToTES = m_caseType == CASETYPE_TCS_TO_TES || m_caseType == CASETYPE_VS_TO_TCS_TO_TES;
6295 const string tesIn0 = tcsToTES ? "gl_in[0].gl_Position" : "in_te_attr[0]";
6296 const string tesIn1 = tcsToTES ? "gl_in[1].gl_Position" : "in_te_attr[1]";
6297 const string tesIn2 = tcsToTES ? "gl_in[2].gl_Position" : "in_te_attr[2]";
6299 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
6300 << glu::VertexSource ("#version 310 es\n"
6302 "in highp vec4 in_v_attr;\n"
6303 + string(!vsToTCS ? "out highp vec4 in_tc_attr;\n" : "") +
6305 "void main (void)\n"
6307 " " + (vsToTCS ? "gl_Position" : "in_tc_attr") + " = in_v_attr;\n"
6310 << glu::TessellationControlSource ("#version 310 es\n"
6311 "#extension GL_EXT_tessellation_shader : require\n"
6313 "layout (vertices = 3) out;\n"
6315 + string(!vsToTCS ? "in highp vec4 in_tc_attr[];\n" : "") +
6317 + (!tcsToTES ? "out highp vec4 in_te_attr[];\n" : "") +
6319 "void main (void)\n"
6321 " " + (tcsToTES ? "gl_out[gl_InvocationID].gl_Position" : "in_te_attr[gl_InvocationID]") + " = "
6322 + (vsToTCS ? "gl_in[gl_InvocationID].gl_Position" : "in_tc_attr[gl_InvocationID]") + ";\n"
6324 " gl_TessLevelInner[0] = 2.0;\n"
6325 " gl_TessLevelInner[1] = 3.0;\n"
6327 " gl_TessLevelOuter[0] = 4.0;\n"
6328 " gl_TessLevelOuter[1] = 5.0;\n"
6329 " gl_TessLevelOuter[2] = 6.0;\n"
6330 " gl_TessLevelOuter[3] = 7.0;\n"
6333 << glu::TessellationEvaluationSource ("#version 310 es\n"
6334 "#extension GL_EXT_tessellation_shader : require\n"
6336 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_TRIANGLES) +
6338 + (!tcsToTES ? "in highp vec4 in_te_attr[];\n" : "") +
6340 "out highp vec4 in_f_color;\n"
6342 "void main (void)\n"
6344 " highp vec2 xy = gl_TessCoord.x * " + tesIn0 + ".xy\n"
6345 " + gl_TessCoord.y * " + tesIn1 + ".xy\n"
6346 " + gl_TessCoord.z * " + tesIn2 + ".xy;\n"
6347 " gl_Position = vec4(xy, 0.0, 1.0);\n"
6348 " in_f_color = vec4(" + tesIn0 + ".z + " + tesIn1 + ".w,\n"
6349 " " + tesIn2 + ".z + " + tesIn0 + ".w,\n"
6350 " " + tesIn1 + ".z + " + tesIn2 + ".w,\n"
6354 << glu::FragmentSource ("#version 310 es\n"
6356 "layout (location = 0) out mediump vec4 o_color;\n"
6358 "in highp vec4 in_f_color;\n"
6360 "void main (void)\n"
6362 " o_color = in_f_color;\n"
6365 m_testCtx.getLog() << *m_program;
6366 if (!m_program->isOk())
6367 TCU_FAIL("Program compilation failed");
6370 void GLPositionCase::deinit (void)
6375 GLPositionCase::IterateResult GLPositionCase::iterate (void)
6377 TestLog& log = m_testCtx.getLog();
6378 const RenderContext& renderCtx = m_context.getRenderContext();
6379 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
6380 const glw::Functions& gl = renderCtx.getFunctions();
6381 const deUint32 programGL = m_program->getProgram();
6383 static const float attributes[3*4] =
6385 -0.8f, -0.7f, 0.1f, 0.7f,
6386 -0.5f, 0.4f, 0.2f, 0.5f,
6387 0.3f, 0.2f, 0.3f, 0.45f
6390 gl.useProgram(programGL);
6391 setViewport(gl, viewport);
6392 gl.patchParameteri(GL_PATCH_VERTICES, 3);
6394 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
6395 gl.clear(GL_COLOR_BUFFER_BIT);
6397 log << TestLog::Message << "Note: input data for in_v_attr:\n" << arrayStr(attributes, 4) << TestLog::EndMessage;
6400 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 4, 3, 0, &attributes[0]) };
6401 glu::draw(renderCtx, programGL, DE_LENGTH_OF_ARRAY(bindings), &bindings[0], glu::pr::Patches(3));
6404 const tcu::Surface pixels = getPixels(renderCtx, viewport);
6405 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath.c_str());
6406 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), pixels.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
6410 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
6416 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
6420 class LimitQueryCase : public TestCase
6423 LimitQueryCase (Context& context, const char* name, const char* desc, glw::GLenum target, int minValue);
6425 IterateResult iterate (void);
6427 const glw::GLenum m_target;
6428 const int m_minValue;
6431 LimitQueryCase::LimitQueryCase (Context& context, const char* name, const char* desc, glw::GLenum target, int minValue)
6432 : TestCase (context, name, desc)
6434 , m_minValue (minValue)
6438 LimitQueryCase::IterateResult LimitQueryCase::iterate (void)
6440 checkTessellationSupport(m_context);
6442 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6443 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6445 gl.enableLogging(true);
6446 verifyStateIntegerMin(result, gl, m_target, m_minValue, QUERY_INTEGER);
6449 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Types", "Alternative queries");
6450 verifyStateIntegerMin(result, gl, m_target, m_minValue, QUERY_BOOLEAN);
6451 verifyStateIntegerMin(result, gl, m_target, m_minValue, QUERY_INTEGER64);
6452 verifyStateIntegerMin(result, gl, m_target, m_minValue, QUERY_FLOAT);
6455 result.setTestContextResult(m_testCtx);
6459 class CombinedUniformLimitCase : public TestCase
6462 CombinedUniformLimitCase (Context& context, const char* name, const char* desc, glw::GLenum combined, glw::GLenum numBlocks, glw::GLenum defaultComponents);
6464 IterateResult iterate (void);
6466 const glw::GLenum m_combined;
6467 const glw::GLenum m_numBlocks;
6468 const glw::GLenum m_defaultComponents;
6471 CombinedUniformLimitCase::CombinedUniformLimitCase (Context& context, const char* name, const char* desc, glw::GLenum combined, glw::GLenum numBlocks, glw::GLenum defaultComponents)
6472 : TestCase (context, name, desc)
6473 , m_combined (combined)
6474 , m_numBlocks (numBlocks)
6475 , m_defaultComponents (defaultComponents)
6479 CombinedUniformLimitCase::IterateResult CombinedUniformLimitCase::iterate (void)
6481 checkTessellationSupport(m_context);
6483 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6484 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6486 gl.enableLogging(true);
6488 m_testCtx.getLog() << tcu::TestLog::Message
6489 << "The minimum value of " << glu::getGettableStateStr(m_combined)
6490 << " is " << glu::getGettableStateStr(m_numBlocks)
6491 << " x MAX_UNIFORM_BLOCK_SIZE / 4 + "
6492 << glu::getGettableStateStr(m_defaultComponents)
6493 << tcu::TestLog::EndMessage;
6495 StateQueryMemoryWriteGuard<glw::GLint> maxUniformBlocks;
6496 gl.glGetIntegerv(m_numBlocks, &maxUniformBlocks);
6497 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "glGetIntegerv");
6499 StateQueryMemoryWriteGuard<glw::GLint> maxUniformBlockSize;
6500 gl.glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUniformBlockSize);
6501 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "glGetIntegerv");
6503 StateQueryMemoryWriteGuard<glw::GLint> maxUniformComponents;
6504 gl.glGetIntegerv(m_defaultComponents, &maxUniformComponents);
6505 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "glGetIntegerv");
6507 if (maxUniformBlocks.verifyValidity(result) && maxUniformBlockSize.verifyValidity(result) && maxUniformComponents.verifyValidity(result))
6509 const int limit = ((int)maxUniformBlocks) * ((int)maxUniformBlockSize) / 4 + (int)maxUniformComponents;
6510 verifyStateIntegerMin(result, gl, m_combined, limit, QUERY_INTEGER);
6513 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Types", "Alternative queries");
6514 verifyStateIntegerMin(result, gl, m_combined, limit, QUERY_BOOLEAN);
6515 verifyStateIntegerMin(result, gl, m_combined, limit, QUERY_INTEGER64);
6516 verifyStateIntegerMin(result, gl, m_combined, limit, QUERY_FLOAT);
6520 result.setTestContextResult(m_testCtx);
6524 class PatchVerticesStateCase : public TestCase
6527 PatchVerticesStateCase (Context& context, const char* name, const char* desc);
6529 IterateResult iterate (void);
6532 PatchVerticesStateCase::PatchVerticesStateCase (Context& context, const char* name, const char* desc)
6533 : TestCase(context, name, desc)
6537 PatchVerticesStateCase::IterateResult PatchVerticesStateCase::iterate (void)
6539 checkTessellationSupport(m_context);
6541 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6542 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6544 gl.enableLogging(true);
6548 const tcu::ScopedLogSection section(m_testCtx.getLog(), "initial", "Initial value");
6550 verifyStateInteger(result, gl, GL_PATCH_VERTICES, 3, QUERY_INTEGER);
6555 const tcu::ScopedLogSection section(m_testCtx.getLog(), "set", "After set");
6557 gl.glPatchParameteri(GL_PATCH_VERTICES, 22);
6558 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "glPatchParameteri");
6560 verifyStateInteger(result, gl, GL_PATCH_VERTICES, 22, QUERY_INTEGER);
6562 const tcu::ScopedLogSection subsection(m_testCtx.getLog(), "Types", "Alternative queries");
6563 verifyStateIntegerMin(result, gl, GL_PATCH_VERTICES, 22, QUERY_BOOLEAN);
6564 verifyStateIntegerMin(result, gl, GL_PATCH_VERTICES, 22, QUERY_INTEGER64);
6565 verifyStateIntegerMin(result, gl, GL_PATCH_VERTICES, 22, QUERY_FLOAT);
6569 result.setTestContextResult(m_testCtx);
6573 class PrimitiveRestartForPatchesSupportedCase : public TestCase
6576 PrimitiveRestartForPatchesSupportedCase (Context& context, const char* name, const char* desc);
6578 IterateResult iterate (void);
6581 PrimitiveRestartForPatchesSupportedCase::PrimitiveRestartForPatchesSupportedCase (Context& context, const char* name, const char* desc)
6582 : TestCase(context, name, desc)
6586 PrimitiveRestartForPatchesSupportedCase::IterateResult PrimitiveRestartForPatchesSupportedCase::iterate (void)
6588 checkTessellationSupport(m_context);
6590 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6591 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6594 gl.enableLogging(true);
6596 queryState(result, gl, QUERY_BOOLEAN, GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED, state);
6598 if (!state.isUndefined())
6600 const tcu::ScopedLogSection subsection(m_testCtx.getLog(), "Types", "Alternative types");
6601 verifyStateBoolean(result, gl, GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED, state.getBoolAccess(), QUERY_INTEGER);
6602 verifyStateBoolean(result, gl, GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED, state.getBoolAccess(), QUERY_INTEGER64);
6603 verifyStateBoolean(result, gl, GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED, state.getBoolAccess(), QUERY_FLOAT);
6606 result.setTestContextResult(m_testCtx);
6610 class TessProgramQueryCase : public TestCase
6613 TessProgramQueryCase (Context& context, const char* name, const char* desc);
6615 std::string getVertexSource (void) const;
6616 std::string getFragmentSource (void) const;
6617 std::string getTessCtrlSource (const char* globalLayouts) const;
6618 std::string getTessEvalSource (const char* globalLayouts) const;
6621 TessProgramQueryCase::TessProgramQueryCase (Context& context, const char* name, const char* desc)
6622 : TestCase(context, name, desc)
6626 std::string TessProgramQueryCase::getVertexSource (void) const
6628 return "#version 310 es\n"
6629 "void main (void)\n"
6631 " gl_Position = vec4(float(gl_VertexID), float(gl_VertexID / 2), 0.0, 1.0);\n"
6635 std::string TessProgramQueryCase::getFragmentSource (void) const
6637 return "#version 310 es\n"
6638 "layout (location = 0) out mediump vec4 o_color;\n"
6639 "void main (void)\n"
6641 " o_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
6645 std::string TessProgramQueryCase::getTessCtrlSource (const char* globalLayouts) const
6647 return "#version 310 es\n"
6648 "#extension GL_EXT_tessellation_shader : require\n"
6649 + std::string(globalLayouts) + ";\n"
6650 "void main (void)\n"
6652 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
6653 " gl_TessLevelInner[0] = 2.8;\n"
6654 " gl_TessLevelInner[1] = 2.8;\n"
6655 " gl_TessLevelOuter[0] = 2.8;\n"
6656 " gl_TessLevelOuter[1] = 2.8;\n"
6657 " gl_TessLevelOuter[2] = 2.8;\n"
6658 " gl_TessLevelOuter[3] = 2.8;\n"
6662 std::string TessProgramQueryCase::getTessEvalSource (const char* globalLayouts) const
6664 return "#version 310 es\n"
6665 "#extension GL_EXT_tessellation_shader : require\n"
6666 + std::string(globalLayouts) + ";\n"
6667 "void main (void)\n"
6669 " gl_Position = gl_TessCoord.x * gl_in[0].gl_Position\n"
6670 " + gl_TessCoord.y * gl_in[1].gl_Position\n"
6671 " + gl_TessCoord.y * gl_in[2].gl_Position\n"
6672 " + gl_TessCoord.z * gl_in[3].gl_Position;\n"
6676 class TessControlOutputVerticesCase : public TessProgramQueryCase
6679 TessControlOutputVerticesCase (Context& context, const char* name, const char* desc);
6681 IterateResult iterate (void);
6684 TessControlOutputVerticesCase::TessControlOutputVerticesCase (Context& context, const char* name, const char* desc)
6685 : TessProgramQueryCase(context, name, desc)
6689 TessControlOutputVerticesCase::IterateResult TessControlOutputVerticesCase::iterate (void)
6691 checkTessellationSupport(m_context);
6693 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
6694 << glu::VertexSource(getVertexSource())
6695 << glu::FragmentSource(getFragmentSource())
6696 << glu::TessellationControlSource(getTessCtrlSource("layout(vertices=4) out"))
6697 << glu::TessellationEvaluationSource(getTessEvalSource("layout(triangles) in")));
6699 m_testCtx.getLog() << program;
6700 if (!program.isOk())
6701 throw tcu::TestError("failed to build program");
6704 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6705 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6707 gl.enableLogging(true);
6708 verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_CONTROL_OUTPUT_VERTICES, 4, QUERY_PROGRAM_INTEGER);
6710 result.setTestContextResult(m_testCtx);
6715 class TessGenModeQueryCase : public TessProgramQueryCase
6718 TessGenModeQueryCase (Context& context, const char* name, const char* desc);
6720 IterateResult iterate (void);
6723 TessGenModeQueryCase::TessGenModeQueryCase (Context& context, const char* name, const char* desc)
6724 : TessProgramQueryCase(context, name, desc)
6728 TessGenModeQueryCase::IterateResult TessGenModeQueryCase::iterate (void)
6730 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6731 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6735 const char* description;
6740 { "Triangles", "layout(triangles) in", GL_TRIANGLES },
6741 { "Isolines", "layout(isolines) in", GL_ISOLINES },
6742 { "Quads", "layout(quads) in", GL_QUADS },
6745 checkTessellationSupport(m_context);
6746 gl.enableLogging(true);
6748 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_modes); ++ndx)
6750 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Type", s_modes[ndx].description);
6752 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
6753 << glu::VertexSource(getVertexSource())
6754 << glu::FragmentSource(getFragmentSource())
6755 << glu::TessellationControlSource(getTessCtrlSource("layout(vertices=6) out"))
6756 << glu::TessellationEvaluationSource(getTessEvalSource(s_modes[ndx].layout)));
6758 m_testCtx.getLog() << program;
6759 if (!program.isOk())
6760 result.fail("failed to build program");
6762 verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_MODE, s_modes[ndx].mode, QUERY_PROGRAM_INTEGER);
6765 result.setTestContextResult(m_testCtx);
6769 class TessGenSpacingQueryCase : public TessProgramQueryCase
6772 TessGenSpacingQueryCase (Context& context, const char* name, const char* desc);
6774 IterateResult iterate (void);
6777 TessGenSpacingQueryCase::TessGenSpacingQueryCase (Context& context, const char* name, const char* desc)
6778 : TessProgramQueryCase(context, name, desc)
6782 TessGenSpacingQueryCase::IterateResult TessGenSpacingQueryCase::iterate (void)
6784 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6785 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6789 const char* description;
6791 glw::GLenum spacing;
6794 { "Default spacing", "layout(triangles) in", GL_EQUAL },
6795 { "Equal spacing", "layout(triangles, equal_spacing) in", GL_EQUAL },
6796 { "Fractional even spacing", "layout(triangles, fractional_even_spacing) in", GL_FRACTIONAL_EVEN },
6797 { "Fractional odd spacing", "layout(triangles, fractional_odd_spacing) in", GL_FRACTIONAL_ODD },
6800 checkTessellationSupport(m_context);
6801 gl.enableLogging(true);
6803 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_modes); ++ndx)
6805 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Type", s_modes[ndx].description);
6807 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
6808 << glu::VertexSource(getVertexSource())
6809 << glu::FragmentSource(getFragmentSource())
6810 << glu::TessellationControlSource(getTessCtrlSource("layout(vertices=6) out"))
6811 << glu::TessellationEvaluationSource(getTessEvalSource(s_modes[ndx].layout)));
6813 m_testCtx.getLog() << program;
6814 if (!program.isOk())
6815 result.fail("failed to build program");
6817 verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_SPACING, s_modes[ndx].spacing, QUERY_PROGRAM_INTEGER);
6820 result.setTestContextResult(m_testCtx);
6824 class TessGenVertexOrderQueryCase : public TessProgramQueryCase
6827 TessGenVertexOrderQueryCase (Context& context, const char* name, const char* desc);
6829 IterateResult iterate (void);
6832 TessGenVertexOrderQueryCase::TessGenVertexOrderQueryCase (Context& context, const char* name, const char* desc)
6833 : TessProgramQueryCase(context, name, desc)
6837 TessGenVertexOrderQueryCase::IterateResult TessGenVertexOrderQueryCase::iterate (void)
6839 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6840 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6844 const char* description;
6849 { "Default order", "layout(triangles) in", GL_CCW },
6850 { "CW order", "layout(triangles, cw) in", GL_CW },
6851 { "CCW order", "layout(triangles, ccw) in", GL_CCW },
6854 checkTessellationSupport(m_context);
6855 gl.enableLogging(true);
6857 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_modes); ++ndx)
6859 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Type", s_modes[ndx].description);
6861 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
6862 << glu::VertexSource(getVertexSource())
6863 << glu::FragmentSource(getFragmentSource())
6864 << glu::TessellationControlSource(getTessCtrlSource("layout(vertices=6) out"))
6865 << glu::TessellationEvaluationSource(getTessEvalSource(s_modes[ndx].layout)));
6867 m_testCtx.getLog() << program;
6868 if (!program.isOk())
6869 result.fail("failed to build program");
6871 verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_VERTEX_ORDER, s_modes[ndx].order, QUERY_PROGRAM_INTEGER);
6874 result.setTestContextResult(m_testCtx);
6878 class TessGenPointModeQueryCase : public TessProgramQueryCase
6881 TessGenPointModeQueryCase (Context& context, const char* name, const char* desc);
6883 IterateResult iterate (void);
6886 TessGenPointModeQueryCase::TessGenPointModeQueryCase (Context& context, const char* name, const char* desc)
6887 : TessProgramQueryCase(context, name, desc)
6891 TessGenPointModeQueryCase::IterateResult TessGenPointModeQueryCase::iterate (void)
6893 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6894 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6898 const char* description;
6903 { "Default mode", "layout(triangles) in", GL_FALSE },
6904 { "Point mode", "layout(triangles, point_mode) in", GL_TRUE },
6907 checkTessellationSupport(m_context);
6908 gl.enableLogging(true);
6910 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_modes); ++ndx)
6912 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Type", s_modes[ndx].description);
6914 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
6915 << glu::VertexSource(getVertexSource())
6916 << glu::FragmentSource(getFragmentSource())
6917 << glu::TessellationControlSource(getTessCtrlSource("layout(vertices=6) out"))
6918 << glu::TessellationEvaluationSource(getTessEvalSource(s_modes[ndx].layout)));
6920 m_testCtx.getLog() << program;
6921 if (!program.isOk())
6922 result.fail("failed to build program");
6924 verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_POINT_MODE, s_modes[ndx].mode, QUERY_PROGRAM_INTEGER);
6927 result.setTestContextResult(m_testCtx);
6931 class ReferencedByTessellationQueryCase : public TestCase
6934 ReferencedByTessellationQueryCase (Context& context, const char* name, const char* desc, bool isCtrlCase);
6937 IterateResult iterate (void);
6939 std::string getVertexSource (void) const;
6940 std::string getFragmentSource (void) const;
6941 std::string getTessCtrlSource (void) const;
6942 std::string getTessEvalSource (void) const;
6944 const bool m_isCtrlCase;
6947 ReferencedByTessellationQueryCase::ReferencedByTessellationQueryCase (Context& context, const char* name, const char* desc, bool isCtrlCase)
6948 : TestCase (context, name, desc)
6949 , m_isCtrlCase (isCtrlCase)
6953 void ReferencedByTessellationQueryCase::init (void)
6955 checkTessellationSupport(m_context);
6958 ReferencedByTessellationQueryCase::IterateResult ReferencedByTessellationQueryCase::iterate (void)
6960 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6961 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6962 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
6963 << glu::VertexSource(getVertexSource())
6964 << glu::FragmentSource(getFragmentSource())
6965 << glu::TessellationControlSource(getTessCtrlSource())
6966 << glu::TessellationEvaluationSource(getTessEvalSource()));
6968 gl.enableLogging(true);
6970 m_testCtx.getLog() << program;
6971 if (!program.isOk())
6972 result.fail("failed to build program");
6975 const deUint32 props[1] = { (deUint32)((m_isCtrlCase) ? (GL_REFERENCED_BY_TESS_CONTROL_SHADER) : (GL_REFERENCED_BY_TESS_EVALUATION_SHADER)) };
6978 const tcu::ScopedLogSection section (m_testCtx.getLog(), "UnreferencedUniform", "Unreferenced uniform u_unreferenced");
6979 deUint32 resourcePos;
6980 glw::GLsizei length = 0;
6981 glw::GLint referenced = 0;
6983 resourcePos = gl.glGetProgramResourceIndex(program.getProgram(), GL_UNIFORM, "u_unreferenced");
6984 m_testCtx.getLog() << tcu::TestLog::Message << "u_unreferenced resource index: " << resourcePos << tcu::TestLog::EndMessage;
6986 if (resourcePos == GL_INVALID_INDEX)
6987 result.fail("resourcePos was GL_INVALID_INDEX");
6990 gl.glGetProgramResourceiv(program.getProgram(), GL_UNIFORM, resourcePos, 1, props, 1, &length, &referenced);
6992 << tcu::TestLog::Message
6993 << "Query " << glu::getProgramResourcePropertyStr(props[0])
6994 << ", got " << length << " value(s), value[0] = " << glu::getBooleanStr(referenced)
6995 << tcu::TestLog::EndMessage;
6997 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "query resource");
6999 if (length == 0 || referenced != GL_FALSE)
7000 result.fail("expected GL_FALSE");
7005 const tcu::ScopedLogSection section (m_testCtx.getLog(), "ReferencedUniform", "Referenced uniform u_referenced");
7006 deUint32 resourcePos;
7007 glw::GLsizei length = 0;
7008 glw::GLint referenced = 0;
7010 resourcePos = gl.glGetProgramResourceIndex(program.getProgram(), GL_UNIFORM, "u_referenced");
7011 m_testCtx.getLog() << tcu::TestLog::Message << "u_referenced resource index: " << resourcePos << tcu::TestLog::EndMessage;
7013 if (resourcePos == GL_INVALID_INDEX)
7014 result.fail("resourcePos was GL_INVALID_INDEX");
7017 gl.glGetProgramResourceiv(program.getProgram(), GL_UNIFORM, resourcePos, 1, props, 1, &length, &referenced);
7019 << tcu::TestLog::Message
7020 << "Query " << glu::getProgramResourcePropertyStr(props[0])
7021 << ", got " << length << " value(s), value[0] = " << glu::getBooleanStr(referenced)
7022 << tcu::TestLog::EndMessage;
7024 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query resource");
7026 if (length == 0 || referenced != GL_TRUE)
7027 result.fail("expected GL_TRUE");
7032 result.setTestContextResult(m_testCtx);
7036 std::string ReferencedByTessellationQueryCase::getVertexSource (void) const
7038 return "#version 310 es\n"
7039 "void main (void)\n"
7041 " gl_Position = vec4(float(gl_VertexID), float(gl_VertexID / 2), 0.0, 1.0);\n"
7045 std::string ReferencedByTessellationQueryCase::getFragmentSource (void) const
7047 return "#version 310 es\n"
7048 "layout (location = 0) out mediump vec4 o_color;\n"
7049 "void main (void)\n"
7051 " o_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
7055 std::string ReferencedByTessellationQueryCase::getTessCtrlSource (void) const
7057 std::ostringstream buf;
7058 buf << "#version 310 es\n"
7059 "#extension GL_EXT_tessellation_shader : require\n"
7060 "layout(vertices = 3) out;\n"
7061 "uniform highp vec4 " << ((m_isCtrlCase) ? ("u_referenced") : ("u_unreferenced")) << ";\n"
7062 "void main (void)\n"
7064 " vec4 offset = " << ((m_isCtrlCase) ? ("u_referenced") : ("u_unreferenced")) << ";\n"
7065 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position + offset;\n"
7066 " gl_TessLevelInner[0] = 2.8;\n"
7067 " gl_TessLevelInner[1] = 2.8;\n"
7068 " gl_TessLevelOuter[0] = 2.8;\n"
7069 " gl_TessLevelOuter[1] = 2.8;\n"
7070 " gl_TessLevelOuter[2] = 2.8;\n"
7071 " gl_TessLevelOuter[3] = 2.8;\n"
7076 std::string ReferencedByTessellationQueryCase::getTessEvalSource (void) const
7078 std::ostringstream buf;
7079 buf << "#version 310 es\n"
7080 "#extension GL_EXT_tessellation_shader : require\n"
7081 "layout(triangles) in;\n"
7082 "uniform highp vec4 " << ((m_isCtrlCase) ? ("u_unreferenced") : ("u_referenced")) << ";\n"
7083 "void main (void)\n"
7085 " vec4 offset = " << ((m_isCtrlCase) ? ("u_unreferenced") : ("u_referenced")) << ";\n"
7086 " gl_Position = gl_TessCoord.x * gl_in[0].gl_Position\n"
7087 " + gl_TessCoord.y * gl_in[1].gl_Position\n"
7088 " + gl_TessCoord.z * gl_in[2].gl_Position\n"
7095 class IsPerPatchQueryCase : public TestCase
7098 IsPerPatchQueryCase (Context& context, const char* name, const char* desc);
7101 IterateResult iterate (void);
7104 IsPerPatchQueryCase::IsPerPatchQueryCase (Context& context, const char* name, const char* desc)
7105 : TestCase(context, name, desc)
7109 void IsPerPatchQueryCase::init (void)
7111 checkTessellationSupport(m_context);
7114 IsPerPatchQueryCase::IterateResult IsPerPatchQueryCase::iterate (void)
7116 static const char* const s_controlSource = "#version 310 es\n"
7117 "#extension GL_EXT_tessellation_shader : require\n"
7118 "layout(vertices = 3) out;\n"
7119 "patch out highp vec4 v_perPatch;\n"
7120 "out highp vec4 v_perVertex[];\n"
7121 "void main (void)\n"
7123 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
7124 " v_perPatch = gl_in[0].gl_Position;\n"
7125 " v_perVertex[gl_InvocationID] = -gl_in[gl_InvocationID].gl_Position;\n"
7126 " gl_TessLevelInner[0] = 2.8;\n"
7127 " gl_TessLevelInner[1] = 2.8;\n"
7128 " gl_TessLevelOuter[0] = 2.8;\n"
7129 " gl_TessLevelOuter[1] = 2.8;\n"
7130 " gl_TessLevelOuter[2] = 2.8;\n"
7131 " gl_TessLevelOuter[3] = 2.8;\n"
7133 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
7134 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
7135 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
7136 << glu::TessellationControlSource(s_controlSource)
7137 << glu::ProgramSeparable(true));
7139 gl.enableLogging(true);
7141 m_testCtx.getLog() << program;
7142 if (!program.isOk())
7143 result.fail("failed to build program");
7146 const deUint32 props[1] = { GL_IS_PER_PATCH };
7149 const tcu::ScopedLogSection section (m_testCtx.getLog(), "PerPatchOutput", "Per patch v_perPatch");
7150 deUint32 resourcePos;
7151 glw::GLsizei length = 0;
7152 glw::GLint referenced = 0;
7154 resourcePos = gl.glGetProgramResourceIndex(program.getProgram(), GL_PROGRAM_OUTPUT, "v_perPatch");
7155 m_testCtx.getLog() << tcu::TestLog::Message << "v_perPatch resource index: " << resourcePos << tcu::TestLog::EndMessage;
7157 if (resourcePos == GL_INVALID_INDEX)
7158 result.fail("resourcePos was GL_INVALID_INDEX");
7161 gl.glGetProgramResourceiv(program.getProgram(), GL_PROGRAM_OUTPUT, resourcePos, 1, props, 1, &length, &referenced);
7163 << tcu::TestLog::Message
7164 << "Query " << glu::getProgramResourcePropertyStr(props[0])
7165 << ", got " << length << " value(s), value[0] = " << glu::getBooleanStr(referenced)
7166 << tcu::TestLog::EndMessage;
7168 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "query resource");
7170 if (length == 0 || referenced != GL_TRUE)
7171 result.fail("expected GL_TRUE");
7176 const tcu::ScopedLogSection section (m_testCtx.getLog(), "PerVertexhOutput", "Per vertex v_perVertex");
7177 deUint32 resourcePos;
7178 glw::GLsizei length = 0;
7179 glw::GLint referenced = 0;
7181 resourcePos = gl.glGetProgramResourceIndex(program.getProgram(), GL_PROGRAM_OUTPUT, "v_perVertex");
7182 m_testCtx.getLog() << tcu::TestLog::Message << "v_perVertex resource index: " << resourcePos << tcu::TestLog::EndMessage;
7184 if (resourcePos == GL_INVALID_INDEX)
7185 result.fail("resourcePos was GL_INVALID_INDEX");
7188 gl.glGetProgramResourceiv(program.getProgram(), GL_PROGRAM_OUTPUT, resourcePos, 1, props, 1, &length, &referenced);
7190 << tcu::TestLog::Message
7191 << "Query " << glu::getProgramResourcePropertyStr(props[0])
7192 << ", got " << length << " value(s), value[0] = " << glu::getBooleanStr(referenced)
7193 << tcu::TestLog::EndMessage;
7195 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query resource");
7197 if (length == 0 || referenced != GL_FALSE)
7198 result.fail("expected GL_FALSE");
7203 result.setTestContextResult(m_testCtx);
7209 TessellationTests::TessellationTests (Context& context)
7210 : TestCaseGroup(context, "tessellation", "Tessellation Tests")
7214 TessellationTests::~TessellationTests (void)
7218 void TessellationTests::init (void)
7221 tcu::TestCaseGroup* const queryGroup = new tcu::TestCaseGroup(m_testCtx, "state_query", "Query tests");
7222 addChild(queryGroup);
7225 queryGroup->addChild(new LimitQueryCase(m_context, "max_patch_vertices", "Test MAX_PATCH_VERTICES", GL_MAX_PATCH_VERTICES, 32));
7226 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_gen_level", "Test MAX_TESS_GEN_LEVEL", GL_MAX_TESS_GEN_LEVEL, 64));
7227 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_uniform_components", "Test MAX_TESS_CONTROL_UNIFORM_COMPONENTS", GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS, 1024));
7228 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_uniform_components", "Test MAX_TESS_EVALUATION_UNIFORM_COMPONENTS", GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS, 1024));
7229 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_texture_image_units", "Test MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS", GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS, 16));
7230 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_texture_image_units", "Test MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS", GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS, 16));
7231 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_output_components", "Test MAX_TESS_CONTROL_OUTPUT_COMPONENTS", GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS, 64));
7232 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_patch_components", "Test MAX_TESS_PATCH_COMPONENTS", GL_MAX_TESS_PATCH_COMPONENTS, 120));
7233 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_total_output_components", "Test MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS", GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS, 4096));
7234 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_output_components", "Test MAX_TESS_EVALUATION_OUTPUT_COMPONENTS", GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS, 64));
7235 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_uniform_blocks", "Test MAX_TESS_CONTROL_UNIFORM_BLOCKS", GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS, 12));
7236 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_uniform_blocks", "Test MAX_TESS_EVALUATION_UNIFORM_BLOCKS", GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS, 12));
7237 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_input_components", "Test MAX_TESS_CONTROL_INPUT_COMPONENTS", GL_MAX_TESS_CONTROL_INPUT_COMPONENTS, 64));
7238 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_input_components", "Test MAX_TESS_EVALUATION_INPUT_COMPONENTS", GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS, 64));
7239 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_atomic_counter_buffers", "Test MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS", GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS, 0));
7240 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_atomic_counter_buffers", "Test MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS", GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS, 0));
7241 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_atomic_counters", "Test MAX_TESS_CONTROL_ATOMIC_COUNTERS", GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS, 0));
7242 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_atomic_counters", "Test MAX_TESS_EVALUATION_ATOMIC_COUNTERS", GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS, 0));
7243 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_image_uniforms", "Test MAX_TESS_CONTROL_IMAGE_UNIFORMS", GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, 0));
7244 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_image_uniforms", "Test MAX_TESS_EVALUATION_IMAGE_UNIFORMS", GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, 0));
7245 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_shader_storage_blocks", "Test MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS", GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS, 0));
7246 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_shader_storage_blocks", "Test MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS", GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS, 0));
7249 queryGroup->addChild(new LimitQueryCase(m_context, "max_uniform_buffer_bindings", "Test MAX_UNIFORM_BUFFER_BINDINGS", GL_MAX_UNIFORM_BUFFER_BINDINGS, 72));
7250 queryGroup->addChild(new LimitQueryCase(m_context, "max_combined_uniform_blocks", "Test MAX_COMBINED_UNIFORM_BLOCKS", GL_MAX_COMBINED_UNIFORM_BLOCKS, 60));
7251 queryGroup->addChild(new LimitQueryCase(m_context, "max_combined_texture_image_units", "Test MAX_COMBINED_TEXTURE_IMAGE_UNITS", GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, 96));
7254 queryGroup->addChild(new CombinedUniformLimitCase(m_context, "max_combined_tess_control_uniform_components", "Test MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS", GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS, GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS, GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS));
7255 queryGroup->addChild(new CombinedUniformLimitCase(m_context, "max_combined_tess_evaluation_uniform_components", "Test MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS", GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS, GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS, GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS));
7258 queryGroup->addChild(new PrimitiveRestartForPatchesSupportedCase(m_context, "primitive_restart_for_patches_supported", "Test PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED"));
7261 queryGroup->addChild(new PatchVerticesStateCase(m_context, "patch_vertices", "Test PATCH_VERTICES"));
7264 queryGroup->addChild(new TessControlOutputVerticesCase (m_context, "tess_control_output_vertices", "Test TESS_CONTROL_OUTPUT_VERTICES"));
7265 queryGroup->addChild(new TessGenModeQueryCase (m_context, "tess_gen_mode", "Test TESS_GEN_MODE"));
7266 queryGroup->addChild(new TessGenSpacingQueryCase (m_context, "tess_gen_spacing", "Test TESS_GEN_SPACING"));
7267 queryGroup->addChild(new TessGenVertexOrderQueryCase (m_context, "tess_gen_vertex_order", "Test TESS_GEN_VERTEX_ORDER"));
7268 queryGroup->addChild(new TessGenPointModeQueryCase (m_context, "tess_gen_point_mode", "Test TESS_GEN_POINT_MODE"));
7271 queryGroup->addChild(new ReferencedByTessellationQueryCase (m_context, "referenced_by_tess_control_shader", "Test REFERENCED_BY_TESS_CONTROL_SHADER", true));
7272 queryGroup->addChild(new ReferencedByTessellationQueryCase (m_context, "referenced_by_tess_evaluation_shader", "Test REFERENCED_BY_TESS_EVALUATION_SHADER", false));
7273 queryGroup->addChild(new IsPerPatchQueryCase (m_context, "is_per_patch", "Test IS_PER_PATCH"));
7277 TestCaseGroup* const tessCoordGroup = new TestCaseGroup(m_context, "tesscoord", "Get tessellation coordinates with transform feedback and validate them");
7278 addChild(tessCoordGroup);
7280 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7282 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7284 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7285 tessCoordGroup->addChild(new TessCoordCase(m_context,
7286 (string() + getTessPrimitiveTypeShaderName(primitiveType) + "_" + getSpacingModeShaderName((SpacingMode)spacingI)).c_str(), "",
7287 primitiveType, (SpacingMode)spacingI));
7292 TestCaseGroup* const windingGroup = new TestCaseGroup(m_context, "winding", "Test the cw and ccw input layout qualifiers");
7293 addChild(windingGroup);
7295 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7297 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7298 if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
7301 for (int windingI = 0; windingI < WINDING_LAST; windingI++)
7303 const Winding winding = (Winding)windingI;
7304 windingGroup->addChild(new WindingCase(m_context, (string() + getTessPrimitiveTypeShaderName(primitiveType) + "_" + getWindingShaderName(winding)).c_str(), "", primitiveType, winding));
7310 TestCaseGroup* const shaderInputOutputGroup = new TestCaseGroup(m_context, "shader_input_output", "Test tessellation control and evaluation shader inputs and outputs");
7311 addChild(shaderInputOutputGroup);
7318 } patchVertexCountCases[] =
7324 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(patchVertexCountCases); caseNdx++)
7326 const int inSize = patchVertexCountCases[caseNdx].inPatchSize;
7327 const int outSize = patchVertexCountCases[caseNdx].outPatchSize;
7329 const string caseName = "patch_vertices_" + de::toString(inSize) + "_in_" + de::toString(outSize) + "_out";
7331 shaderInputOutputGroup->addChild(new PatchVertexCountCase(m_context, caseName.c_str(), "Test input and output patch vertex counts", inSize, outSize,
7332 ("data/tessellation/" + caseName + "_ref.png").c_str()));
7336 for (int caseTypeI = 0; caseTypeI < PerPatchDataCase::CASETYPE_LAST; caseTypeI++)
7338 const PerPatchDataCase::CaseType caseType = (PerPatchDataCase::CaseType)caseTypeI;
7339 const char* const caseName = PerPatchDataCase::getCaseTypeName(caseType);
7341 shaderInputOutputGroup->addChild(new PerPatchDataCase(m_context, caseName, PerPatchDataCase::getCaseTypeDescription(caseType), caseType,
7342 PerPatchDataCase::caseTypeUsesRefImageFromFile(caseType) ? (string() + "data/tessellation/" + caseName + "_ref.png").c_str() : DE_NULL));
7345 for (int caseTypeI = 0; caseTypeI < GLPositionCase::CASETYPE_LAST; caseTypeI++)
7347 const GLPositionCase::CaseType caseType = (GLPositionCase::CaseType)caseTypeI;
7348 const char* const caseName = GLPositionCase::getCaseTypeName(caseType);
7350 shaderInputOutputGroup->addChild(new GLPositionCase(m_context, caseName, "", caseType, "data/tessellation/gl_position_ref.png"));
7353 shaderInputOutputGroup->addChild(new BarrierCase(m_context, "barrier", "Basic barrier usage", "data/tessellation/barrier_ref.png"));
7357 TestCaseGroup* const miscDrawGroup = new TestCaseGroup(m_context, "misc_draw", "Miscellaneous draw-result-verifying cases");
7358 addChild(miscDrawGroup);
7360 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7362 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7363 if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
7366 const char* const primTypeName = getTessPrimitiveTypeShaderName(primitiveType);
7368 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7370 const string caseName = string() + "fill_cover_" + primTypeName + "_" + getSpacingModeShaderName((SpacingMode)spacingI);
7372 miscDrawGroup->addChild(new BasicTriangleFillCoverCase(m_context,
7373 caseName.c_str(), "Check that there are no obvious gaps in the triangle-filled area of a tessellated shape",
7374 primitiveType, (SpacingMode)spacingI,
7375 ("data/tessellation/" + caseName + "_ref").c_str()));
7379 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7381 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7382 if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
7385 const char* const primTypeName = getTessPrimitiveTypeShaderName(primitiveType);
7387 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7389 const string caseName = string() + "fill_overlap_" + primTypeName + "_" + getSpacingModeShaderName((SpacingMode)spacingI);
7391 miscDrawGroup->addChild(new BasicTriangleFillNonOverlapCase(m_context,
7392 caseName.c_str(), "Check that there are no obvious triangle overlaps in the triangle-filled area of a tessellated shape",
7393 primitiveType, (SpacingMode)spacingI,
7394 ("data/tessellation/" + caseName + "_ref").c_str()));
7398 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7400 const string caseName = string() + "isolines_" + getSpacingModeShaderName((SpacingMode)spacingI);
7402 miscDrawGroup->addChild(new IsolinesRenderCase(m_context,
7403 caseName.c_str(), "Basic isolines render test",
7404 (SpacingMode)spacingI,
7405 ("data/tessellation/" + caseName + "_ref").c_str()));
7410 TestCaseGroup* const commonEdgeGroup = new TestCaseGroup(m_context, "common_edge", "Draw multiple adjacent shapes and check that no cracks appear between them");
7411 addChild(commonEdgeGroup);
7413 for (int caseTypeI = 0; caseTypeI < CommonEdgeCase::CASETYPE_LAST; caseTypeI++)
7415 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7417 const CommonEdgeCase::CaseType caseType = (CommonEdgeCase::CaseType)caseTypeI;
7418 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7419 if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
7422 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7424 const SpacingMode spacing = (SpacingMode)spacingI;
7425 const string caseName = (string() + getTessPrimitiveTypeShaderName(primitiveType)
7426 + "_" + getSpacingModeShaderName(spacing)
7427 + (caseType == CommonEdgeCase::CASETYPE_BASIC ? ""
7428 : caseType == CommonEdgeCase::CASETYPE_PRECISE ? "_precise"
7431 commonEdgeGroup->addChild(new CommonEdgeCase(m_context, caseName.c_str(), "", primitiveType, spacing, caseType));
7438 TestCaseGroup* const fractionalSpacingModeGroup = new TestCaseGroup(m_context, "fractional_spacing", "Test fractional spacing modes");
7439 addChild(fractionalSpacingModeGroup);
7441 fractionalSpacingModeGroup->addChild(new FractionalSpacingModeCase(m_context, "odd", "", SPACINGMODE_FRACTIONAL_ODD));
7442 fractionalSpacingModeGroup->addChild(new FractionalSpacingModeCase(m_context, "even", "", SPACINGMODE_FRACTIONAL_EVEN));
7446 TestCaseGroup* const primitiveDiscardGroup = new TestCaseGroup(m_context, "primitive_discard", "Test primitive discard with relevant outer tessellation level <= 0.0");
7447 addChild(primitiveDiscardGroup);
7449 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7451 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7453 for (int windingI = 0; windingI < WINDING_LAST; windingI++)
7455 for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++)
7457 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7458 const SpacingMode spacing = (SpacingMode)spacingI;
7459 const Winding winding = (Winding)windingI;
7460 const bool usePointMode = usePointModeI != 0;
7462 primitiveDiscardGroup->addChild(new PrimitiveDiscardCase(m_context, (string() + getTessPrimitiveTypeShaderName(primitiveType)
7463 + "_" + getSpacingModeShaderName(spacing)
7464 + "_" + getWindingShaderName(winding)
7465 + (usePointMode ? "_point_mode" : "")).c_str(), "",
7466 primitiveType, spacing, winding, usePointMode));
7474 TestCaseGroup* const invarianceGroup = new TestCaseGroup(m_context, "invariance", "Test tessellation invariance rules");
7476 TestCaseGroup* const invariantPrimitiveSetGroup = new TestCaseGroup(m_context, "primitive_set", "Test invariance rule #1");
7477 TestCaseGroup* const invariantOuterEdgeGroup = new TestCaseGroup(m_context, "outer_edge_division", "Test invariance rule #2");
7478 TestCaseGroup* const symmetricOuterEdgeGroup = new TestCaseGroup(m_context, "outer_edge_symmetry", "Test invariance rule #3");
7479 TestCaseGroup* const outerEdgeVertexSetIndexIndependenceGroup = new TestCaseGroup(m_context, "outer_edge_index_independence", "Test invariance rule #4");
7480 TestCaseGroup* const invariantTriangleSetGroup = new TestCaseGroup(m_context, "triangle_set", "Test invariance rule #5");
7481 TestCaseGroup* const invariantInnerTriangleSetGroup = new TestCaseGroup(m_context, "inner_triangle_set", "Test invariance rule #6");
7482 TestCaseGroup* const invariantOuterTriangleSetGroup = new TestCaseGroup(m_context, "outer_triangle_set", "Test invariance rule #7");
7483 TestCaseGroup* const tessCoordComponentRangeGroup = new TestCaseGroup(m_context, "tess_coord_component_range", "Test invariance rule #8, first part");
7484 TestCaseGroup* const oneMinusTessCoordComponentGroup = new TestCaseGroup(m_context, "one_minus_tess_coord_component", "Test invariance rule #8, second part");
7486 addChild(invarianceGroup);
7487 invarianceGroup->addChild(invariantPrimitiveSetGroup);
7488 invarianceGroup->addChild(invariantOuterEdgeGroup);
7489 invarianceGroup->addChild(symmetricOuterEdgeGroup);
7490 invarianceGroup->addChild(outerEdgeVertexSetIndexIndependenceGroup);
7491 invarianceGroup->addChild(invariantTriangleSetGroup);
7492 invarianceGroup->addChild(invariantInnerTriangleSetGroup);
7493 invarianceGroup->addChild(invariantOuterTriangleSetGroup);
7494 invarianceGroup->addChild(tessCoordComponentRangeGroup);
7495 invarianceGroup->addChild(oneMinusTessCoordComponentGroup);
7497 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7499 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7500 const string primName = getTessPrimitiveTypeShaderName(primitiveType);
7501 const bool triOrQuad = primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS;
7503 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7505 const SpacingMode spacing = (SpacingMode)spacingI;
7506 const string primSpacName = primName + "_" + getSpacingModeShaderName(spacing);
7510 invariantOuterEdgeGroup->addChild (new InvariantOuterEdgeCase (m_context, primSpacName.c_str(), "", primitiveType, spacing));
7511 invariantTriangleSetGroup->addChild (new InvariantTriangleSetCase (m_context, primSpacName.c_str(), "", primitiveType, spacing));
7512 invariantInnerTriangleSetGroup->addChild(new InvariantInnerTriangleSetCase (m_context, primSpacName.c_str(), "", primitiveType, spacing));
7513 invariantOuterTriangleSetGroup->addChild(new InvariantOuterTriangleSetCase (m_context, primSpacName.c_str(), "", primitiveType, spacing));
7516 for (int windingI = 0; windingI < WINDING_LAST; windingI++)
7518 const Winding winding = (Winding)windingI;
7519 const string primSpacWindName = primSpacName + "_" + getWindingShaderName(winding);
7521 for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++)
7523 const bool usePointMode = usePointModeI != 0;
7524 const string primSpacWindPointName = primSpacWindName + (usePointMode ? "_point_mode" : "");
7526 invariantPrimitiveSetGroup->addChild (new InvariantPrimitiveSetCase (m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode));
7527 symmetricOuterEdgeGroup->addChild (new SymmetricOuterEdgeCase (m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode));
7528 tessCoordComponentRangeGroup->addChild (new TessCoordComponentRangeCase (m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode));
7529 oneMinusTessCoordComponentGroup->addChild (new OneMinusTessCoordComponentCase (m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode));
7532 outerEdgeVertexSetIndexIndependenceGroup->addChild(new OuterEdgeVertexSetIndexIndependenceCase(m_context, primSpacWindPointName.c_str(), "",
7533 primitiveType, spacing, winding, usePointMode));
7544 const char* description;
7545 UserDefinedIOCase::IOType ioType;
7548 { "per_patch", "Per-patch TCS outputs", UserDefinedIOCase::IO_TYPE_PER_PATCH },
7549 { "per_patch_array", "Per-patch array TCS outputs", UserDefinedIOCase::IO_TYPE_PER_PATCH_ARRAY },
7550 { "per_patch_block", "Per-patch TCS outputs in IO block", UserDefinedIOCase::IO_TYPE_PER_PATCH_BLOCK },
7551 { "per_patch_block_array", "Per-patch TCS outputs in IO block array", UserDefinedIOCase::IO_TYPE_PER_PATCH_BLOCK_ARRAY },
7552 { "per_vertex", "Per-vertex TCS outputs", UserDefinedIOCase::IO_TYPE_PER_VERTEX },
7553 { "per_vertex_block", "Per-vertex TCS outputs in IO block", UserDefinedIOCase::IO_TYPE_PER_VERTEX_BLOCK },
7556 TestCaseGroup* const userDefinedIOGroup = new TestCaseGroup(m_context, "user_defined_io", "Test non-built-in per-patch and per-vertex inputs and outputs");
7557 addChild(userDefinedIOGroup);
7559 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(ioCases); ++ndx)
7561 TestCaseGroup* const ioTypeGroup = new TestCaseGroup(m_context, ioCases[ndx].name, ioCases[ndx].description);
7562 userDefinedIOGroup->addChild(ioTypeGroup);
7564 for (int vertexArraySizeI = 0; vertexArraySizeI < UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_LAST; vertexArraySizeI++)
7566 const UserDefinedIOCase::VertexIOArraySize vertexArraySize = (UserDefinedIOCase::VertexIOArraySize)vertexArraySizeI;
7567 TestCaseGroup* const vertexArraySizeGroup = new TestCaseGroup(m_context,
7568 vertexArraySizeI == UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_IMPLICIT
7569 ? "vertex_io_array_size_implicit"
7570 : vertexArraySizeI == UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN
7571 ? "vertex_io_array_size_shader_builtin"
7572 : vertexArraySizeI == UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY
7573 ? "vertex_io_array_size_query"
7576 ioTypeGroup->addChild(vertexArraySizeGroup);
7578 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7580 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7581 vertexArraySizeGroup->addChild(new UserDefinedIOCase(m_context, getTessPrimitiveTypeShaderName(primitiveType), "", primitiveType, ioCases[ndx].ioType, vertexArraySize, UserDefinedIOCase::TESS_CONTROL_OUT_ARRAY_SIZE_IMPLICIT,
7582 (string() + "data/tessellation/user_defined_io_" + getTessPrimitiveTypeShaderName(primitiveType) + "_ref.png").c_str()));
7585 if (ioCases[ndx].ioType == UserDefinedIOCase::IO_TYPE_PER_VERTEX
7586 || ioCases[ndx].ioType == UserDefinedIOCase::IO_TYPE_PER_VERTEX_BLOCK)
7588 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7590 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7591 vertexArraySizeGroup->addChild(new UserDefinedIOCase(m_context, (string(getTessPrimitiveTypeShaderName(primitiveType)) + "_explicit_tcs_out_size").c_str(), "", primitiveType, ioCases[ndx].ioType, vertexArraySize, UserDefinedIOCase::TESS_CONTROL_OUT_ARRAY_SIZE_LAYOUT,
7592 (string() + "data/tessellation/user_defined_io_" + getTessPrimitiveTypeShaderName(primitiveType) + "_ref.png").c_str()));
7599 TestCaseGroup* const negativeGroup = new TestCaseGroup(m_context, "negative", "Negative cases");
7600 gls::ShaderLibrary shaderLibrary (m_testCtx, m_context.getRenderContext(), m_context.getContextInfo());
7601 const std::vector<tcu::TestNode*> children = shaderLibrary.loadShaderFile("shaders/tessellation_negative_user_defined_io.test");
7603 userDefinedIOGroup->addChild(negativeGroup);
7605 for (int i = 0; i < (int)children.size(); i++)
7606 negativeGroup->addChild(children[i]);