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 "tcuStringTemplate.hpp"
41 #include "tcuSurface.hpp"
42 #include "tcuTextureUtil.hpp"
43 #include "tcuVectorUtil.hpp"
44 #include "tcuImageIO.hpp"
45 #include "tcuResource.hpp"
46 #include "tcuImageCompare.hpp"
47 #include "deRandom.hpp"
48 #include "deStringUtil.hpp"
49 #include "deSharedPtr.hpp"
50 #include "deUniquePtr.hpp"
54 #include "glwEnums.hpp"
55 #include "glwDefs.hpp"
56 #include "glwFunctions.hpp"
65 using glu::ShaderProgram;
66 using glu::RenderContext;
67 using tcu::RenderTarget;
78 using namespace glw; // For GL types.
83 using gls::TextureTestUtil::RandomViewport;
90 using namespace gls::StateQueryUtil;
94 MINIMUM_MAX_TESS_GEN_LEVEL = 64 //!< GL-defined minimum for GL_MAX_TESS_GEN_LEVEL.
97 static inline bool vec3XLessThan (const Vec3& a, const Vec3& b) { return a.x() < b.x(); }
99 template <typename IterT>
100 static string elemsStr (const IterT& begin, const IterT& end, int wrapLengthParam = 0, int numIndentationSpaces = 0)
102 const string baseIndentation = string(numIndentationSpaces, ' ');
103 const string deepIndentation = baseIndentation + string(4, ' ');
104 const int wrapLength = wrapLengthParam > 0 ? wrapLengthParam : std::numeric_limits<int>::max();
105 const int length = (int)std::distance(begin, end);
108 if (length > wrapLength)
109 result += "(amount: " + de::toString(length) + ") ";
110 result += string() + "{" + (length > wrapLength ? "\n"+deepIndentation : " ");
114 for (IterT it = begin; it != end; ++it)
117 result += string() + ", " + (index % wrapLength == 0 ? "\n"+deepIndentation : "");
118 result += de::toString(*it);
122 result += length > wrapLength ? "\n"+baseIndentation : " ";
129 template <typename ContainerT>
130 static string containerStr (const ContainerT& c, int wrapLengthParam = 0, int numIndentationSpaces = 0)
132 return elemsStr(c.begin(), c.end(), wrapLengthParam, numIndentationSpaces);
135 template <typename T, int N>
136 static string arrayStr (const T (&arr)[N], int wrapLengthParam = 0, int numIndentationSpaces = 0)
138 return elemsStr(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr), wrapLengthParam, numIndentationSpaces);
141 template <typename T, int N>
142 static T arrayMax (const T (&arr)[N])
144 return *std::max_element(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr));
147 template <typename T, typename MembT>
148 static vector<MembT> members (const vector<T>& objs, MembT T::* membP)
150 vector<MembT> result(objs.size());
151 for (int i = 0; i < (int)objs.size(); i++)
152 result[i] = objs[i].*membP;
156 template <typename T, int N>
157 static vector<T> arrayToVector (const T (&arr)[N])
159 return vector<T>(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr));
162 template <typename ContainerT, typename T>
163 static inline bool contains (const ContainerT& c, const T& key)
165 return c.find(key) != c.end();
169 static inline tcu::Vector<bool, Size> singleTrueMask (int index)
171 DE_ASSERT(de::inBounds(index, 0, Size));
172 tcu::Vector<bool, Size> result;
173 result[index] = true;
177 static int intPow (int base, int exp)
184 const int sub = intPow(base, exp/2);
192 tcu::Surface getPixels (const glu::RenderContext& rCtx, int x, int y, int width, int height)
194 tcu::Surface result(width, height);
195 glu::readPixels(rCtx, x, y, result.getAccess());
199 tcu::Surface getPixels (const glu::RenderContext& rCtx, const RandomViewport& vp)
201 return getPixels(rCtx, vp.x, vp.y, vp.width, vp.height);
204 static inline void checkRenderTargetSize (const RenderTarget& renderTarget, int minSize)
206 if (renderTarget.getWidth() < minSize || renderTarget.getHeight() < minSize)
207 throw tcu::NotSupportedError("Render target width and height must be at least " + de::toString(minSize));
210 tcu::TextureLevel getPNG (const tcu::Archive& archive, const string& filename)
212 tcu::TextureLevel result;
213 tcu::ImageIO::loadPNG(result, archive, filename.c_str());
217 static int numBasicSubobjects (const glu::VarType& type)
219 if (type.isBasicType())
221 else if (type.isArrayType())
222 return type.getArraySize()*numBasicSubobjects(type.getElementType());
223 else if (type.isStructType())
225 const glu::StructType& structType = *type.getStructPtr();
227 for (int i = 0; i < structType.getNumMembers(); i++)
228 result += numBasicSubobjects(structType.getMember(i).getType());
238 static inline int numVerticesPerPrimitive (deUint32 primitiveTypeGL)
240 switch (primitiveTypeGL)
242 case GL_POINTS: return 1;
243 case GL_TRIANGLES: return 3;
244 case GL_LINES: return 2;
251 static inline void setViewport (const glw::Functions& gl, const RandomViewport& vp)
253 gl.viewport(vp.x, vp.y, vp.width, vp.height);
256 static inline deUint32 getQueryResult (const glw::Functions& gl, deUint32 queryObject)
258 deUint32 result = (deUint32)-1;
259 gl.getQueryObjectuiv(queryObject, GL_QUERY_RESULT, &result);
260 TCU_CHECK(result != (deUint32)-1);
264 template <typename T>
265 static void readDataMapped (const glw::Functions& gl, deUint32 bufferTarget, int numElems, T* dst)
267 const int numBytes = numElems*(int)sizeof(T);
268 const T* const mappedData = (const T*)gl.mapBufferRange(bufferTarget, 0, numBytes, GL_MAP_READ_BIT);
269 GLU_EXPECT_NO_ERROR(gl.getError(), (string() + "glMapBufferRange(" + glu::getBufferTargetName((int)bufferTarget) + ", 0, " + de::toString(numBytes) + ", GL_MAP_READ_BIT)").c_str());
270 TCU_CHECK(mappedData != DE_NULL);
272 for (int i = 0; i < numElems; i++)
273 dst[i] = mappedData[i];
275 gl.unmapBuffer(bufferTarget);
278 template <typename T>
279 static vector<T> readDataMapped (const glw::Functions& gl, deUint32 bufferTarget, int numElems)
281 vector<T> result(numElems);
282 readDataMapped(gl, bufferTarget, numElems, &result[0]);
289 template <typename ArgT, bool res>
290 struct ConstantUnaryPredicate
292 bool operator() (const ArgT&) const { return res; }
295 //! Helper for handling simple, one-varying transform feedbacks.
296 template <typename VaryingT>
297 class TransformFeedbackHandler
303 vector<VaryingT> varying;
305 Result (void) : numPrimitives(-1) {}
306 Result (int n, const vector<VaryingT>& v) : numPrimitives(n), varying(v) {}
309 TransformFeedbackHandler (const glu::RenderContext& renderCtx, int maxNumVertices);
311 Result renderAndGetPrimitives (deUint32 programGL, deUint32 tfPrimTypeGL, int numBindings, const glu::VertexArrayBinding* bindings, int numVertices) const;
314 const glu::RenderContext& m_renderCtx;
315 const glu::TransformFeedback m_tf;
316 const glu::Buffer m_tfBuffer;
317 const glu::Query m_tfPrimQuery;
320 template <typename AttribType>
321 TransformFeedbackHandler<AttribType>::TransformFeedbackHandler (const glu::RenderContext& renderCtx, int maxNumVertices)
322 : m_renderCtx (renderCtx)
324 , m_tfBuffer (renderCtx)
325 , m_tfPrimQuery (renderCtx)
327 const glw::Functions& gl = m_renderCtx.getFunctions();
328 // \note Room for 1 extra triangle, to detect if GL returns too many primitives.
329 const int bufferSize = (maxNumVertices + 3) * (int)sizeof(AttribType);
331 gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, *m_tfBuffer);
332 gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bufferSize, DE_NULL, GL_DYNAMIC_READ);
335 template <typename AttribType>
336 typename TransformFeedbackHandler<AttribType>::Result TransformFeedbackHandler<AttribType>::renderAndGetPrimitives (deUint32 programGL, deUint32 tfPrimTypeGL, int numBindings, const glu::VertexArrayBinding* bindings, int numVertices) const
338 DE_ASSERT(tfPrimTypeGL == GL_POINTS || tfPrimTypeGL == GL_LINES || tfPrimTypeGL == GL_TRIANGLES);
340 const glw::Functions& gl = m_renderCtx.getFunctions();
342 gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, *m_tf);
343 gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, *m_tfBuffer);
344 gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, *m_tfBuffer);
346 gl.beginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, *m_tfPrimQuery);
347 gl.beginTransformFeedback(tfPrimTypeGL);
349 glu::draw(m_renderCtx, programGL, numBindings, bindings, glu::pr::Patches(numVertices));
350 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
352 gl.endTransformFeedback();
353 gl.endQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
356 const int numPrimsWritten = (int)getQueryResult(gl, *m_tfPrimQuery);
357 return Result(numPrimsWritten, readDataMapped<AttribType>(gl, GL_TRANSFORM_FEEDBACK_BUFFER, numPrimsWritten * numVerticesPerPrimitive(tfPrimTypeGL)));
361 template <typename T>
365 bool operator() (const T& a, const T& b) const { return a.size() < b.size(); }
368 //! Predicate functor for comparing structs by their members.
369 template <typename Pred, typename T, typename MembT>
373 MemberPred (MembT T::* membP) : m_membP(membP), m_pred(Pred()) {}
374 bool operator() (const T& a, const T& b) const { return m_pred(a.*m_membP, b.*m_membP); }
381 //! Convenience wrapper for MemberPred, because class template arguments aren't deduced based on constructor arguments.
382 template <template <typename> class Pred, typename T, typename MembT>
383 static MemberPred<Pred<MembT>, T, MembT> memberPred (MembT T::* membP) { return MemberPred<Pred<MembT>, T, MembT>(membP); }
385 template <typename SeqT, int Size, typename Pred>
389 LexCompare (void) : m_pred(Pred()) {}
391 bool operator() (const SeqT& a, const SeqT& b) const
393 for (int i = 0; i < Size; i++)
395 if (m_pred(a[i], b[i]))
397 if (m_pred(b[i], a[i]))
408 class VecLexLessThan : public LexCompare<tcu::Vector<float, Size>, Size, std::less<float> >
412 enum TessPrimitiveType
414 TESSPRIMITIVETYPE_TRIANGLES = 0,
415 TESSPRIMITIVETYPE_QUADS,
416 TESSPRIMITIVETYPE_ISOLINES,
418 TESSPRIMITIVETYPE_LAST
424 SPACINGMODE_FRACTIONAL_ODD,
425 SPACINGMODE_FRACTIONAL_EVEN,
438 static inline const char* getTessPrimitiveTypeShaderName (TessPrimitiveType type)
442 case TESSPRIMITIVETYPE_TRIANGLES: return "triangles";
443 case TESSPRIMITIVETYPE_QUADS: return "quads";
444 case TESSPRIMITIVETYPE_ISOLINES: return "isolines";
451 static inline const char* getSpacingModeShaderName (SpacingMode mode)
455 case SPACINGMODE_EQUAL: return "equal_spacing";
456 case SPACINGMODE_FRACTIONAL_ODD: return "fractional_odd_spacing";
457 case SPACINGMODE_FRACTIONAL_EVEN: return "fractional_even_spacing";
464 static inline const char* getWindingShaderName (Winding winding)
468 case WINDING_CCW: return "ccw";
469 case WINDING_CW: return "cw";
476 static inline string getTessellationEvaluationInLayoutString (TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode=false)
478 return string() + "layout (" + getTessPrimitiveTypeShaderName(primType)
479 + ", " + getSpacingModeShaderName(spacing)
480 + ", " + getWindingShaderName(winding)
481 + (usePointMode ? ", point_mode" : "")
485 static inline string getTessellationEvaluationInLayoutString (TessPrimitiveType primType, SpacingMode spacing, bool usePointMode=false)
487 return string() + "layout (" + getTessPrimitiveTypeShaderName(primType)
488 + ", " + getSpacingModeShaderName(spacing)
489 + (usePointMode ? ", point_mode" : "")
493 static inline string getTessellationEvaluationInLayoutString (TessPrimitiveType primType, Winding winding, bool usePointMode=false)
495 return string() + "layout (" + getTessPrimitiveTypeShaderName(primType)
496 + ", " + getWindingShaderName(winding)
497 + (usePointMode ? ", point_mode" : "")
501 static inline string getTessellationEvaluationInLayoutString (TessPrimitiveType primType, bool usePointMode=false)
503 return string() + "layout (" + getTessPrimitiveTypeShaderName(primType)
504 + (usePointMode ? ", point_mode" : "")
508 static inline deUint32 outputPrimitiveTypeGL (TessPrimitiveType tessPrimType, bool usePointMode)
514 switch (tessPrimType)
516 case TESSPRIMITIVETYPE_TRIANGLES: return GL_TRIANGLES;
517 case TESSPRIMITIVETYPE_QUADS: return GL_TRIANGLES;
518 case TESSPRIMITIVETYPE_ISOLINES: return GL_LINES;
526 static inline int numInnerTessellationLevels (TessPrimitiveType primType)
530 case TESSPRIMITIVETYPE_TRIANGLES: return 1;
531 case TESSPRIMITIVETYPE_QUADS: return 2;
532 case TESSPRIMITIVETYPE_ISOLINES: return 0;
533 default: DE_ASSERT(false); return -1;
537 static inline int numOuterTessellationLevels (TessPrimitiveType primType)
541 case TESSPRIMITIVETYPE_TRIANGLES: return 3;
542 case TESSPRIMITIVETYPE_QUADS: return 4;
543 case TESSPRIMITIVETYPE_ISOLINES: return 2;
544 default: DE_ASSERT(false); return -1;
548 static string tessellationLevelsString (const float* inner, int numInner, const float* outer, int numOuter)
550 DE_ASSERT(numInner >= 0 && numOuter >= 0);
551 return "inner: " + elemsStr(inner, inner+numInner) + ", outer: " + elemsStr(outer, outer+numOuter);
554 static string tessellationLevelsString (const float* inner, const float* outer, TessPrimitiveType primType)
556 return tessellationLevelsString(inner, numInnerTessellationLevels(primType), outer, numOuterTessellationLevels(primType));
559 static string tessellationLevelsString (const float* inner, const float* outer)
561 return tessellationLevelsString(inner, 2, outer, 4);
564 static inline float getClampedTessLevel (SpacingMode mode, float tessLevel)
568 case SPACINGMODE_EQUAL: return de::max(1.0f, tessLevel);
569 case SPACINGMODE_FRACTIONAL_ODD: return de::max(1.0f, tessLevel);
570 case SPACINGMODE_FRACTIONAL_EVEN: return de::max(2.0f, tessLevel);
577 static inline int getRoundedTessLevel (SpacingMode mode, float clampedTessLevel)
579 int result = (int)deFloatCeil(clampedTessLevel);
583 case SPACINGMODE_EQUAL: break;
584 case SPACINGMODE_FRACTIONAL_ODD: result += 1 - result % 2; break;
585 case SPACINGMODE_FRACTIONAL_EVEN: result += result % 2; break;
589 DE_ASSERT(de::inRange<int>(result, 1, MINIMUM_MAX_TESS_GEN_LEVEL));
594 static int getClampedRoundedTessLevel (SpacingMode mode, float tessLevel)
596 return getRoundedTessLevel(mode, getClampedTessLevel(mode, tessLevel));
599 //! A description of an outer edge of a triangle, quad or isolines.
600 //! An outer edge can be described by the index of a u/v/w coordinate
601 //! and the coordinate's value along that edge.
602 struct OuterEdgeDescription
604 int constantCoordinateIndex;
605 float constantCoordinateValueChoices[2];
606 int numConstantCoordinateValueChoices;
608 OuterEdgeDescription (int i, float c0) : constantCoordinateIndex(i), numConstantCoordinateValueChoices(1) { constantCoordinateValueChoices[0] = c0; }
609 OuterEdgeDescription (int i, float c0, float c1) : constantCoordinateIndex(i), numConstantCoordinateValueChoices(2) { constantCoordinateValueChoices[0] = c0; constantCoordinateValueChoices[1] = c1; }
611 string description (void) const
613 static const char* const coordinateNames[] = { "u", "v", "w" };
615 for (int i = 0; i < numConstantCoordinateValueChoices; i++)
616 result += string() + (i > 0 ? " or " : "") + coordinateNames[constantCoordinateIndex] + "=" + de::toString(constantCoordinateValueChoices[i]);
620 bool contains (const Vec3& v) const
622 for (int i = 0; i < numConstantCoordinateValueChoices; i++)
623 if (v[constantCoordinateIndex] == constantCoordinateValueChoices[i])
629 static vector<OuterEdgeDescription> outerEdgeDescriptions (TessPrimitiveType primType)
631 static const OuterEdgeDescription triangleOuterEdgeDescriptions[3] =
633 OuterEdgeDescription(0, 0.0f),
634 OuterEdgeDescription(1, 0.0f),
635 OuterEdgeDescription(2, 0.0f)
638 static const OuterEdgeDescription quadOuterEdgeDescriptions[4] =
640 OuterEdgeDescription(0, 0.0f),
641 OuterEdgeDescription(1, 0.0f),
642 OuterEdgeDescription(0, 1.0f),
643 OuterEdgeDescription(1, 1.0f)
646 static const OuterEdgeDescription isolinesOuterEdgeDescriptions[1] =
648 OuterEdgeDescription(0, 0.0f, 1.0f),
653 case TESSPRIMITIVETYPE_TRIANGLES: return arrayToVector(triangleOuterEdgeDescriptions);
654 case TESSPRIMITIVETYPE_QUADS: return arrayToVector(quadOuterEdgeDescriptions);
655 case TESSPRIMITIVETYPE_ISOLINES: return arrayToVector(isolinesOuterEdgeDescriptions);
656 default: DE_ASSERT(false); return vector<OuterEdgeDescription>();
660 // \note The tessellation coordinates generated by this function could break some of the rules given in the spec (e.g. it may not exactly hold that u+v+w == 1.0f, or [uvw] + (1.0f-[uvw]) == 1.0f).
661 static vector<Vec3> generateReferenceTriangleTessCoords (SpacingMode spacingMode, int inner, int outer0, int outer1, int outer2)
663 vector<Vec3> tessCoords;
667 if (outer0 == 1 && outer1 == 1 && outer2 == 1)
669 tessCoords.push_back(Vec3(1.0f, 0.0f, 0.0f));
670 tessCoords.push_back(Vec3(0.0f, 1.0f, 0.0f));
671 tessCoords.push_back(Vec3(0.0f, 0.0f, 1.0f));
675 return generateReferenceTriangleTessCoords(spacingMode, spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
676 outer0, outer1, outer2);
680 for (int i = 0; i < outer0; i++) { const float v = (float)i / (float)outer0; tessCoords.push_back(Vec3( 0.0f, v, 1.0f - v)); }
681 for (int i = 0; i < outer1; i++) { const float v = (float)i / (float)outer1; tessCoords.push_back(Vec3(1.0f - v, 0.0f, v)); }
682 for (int i = 0; i < outer2; i++) { const float v = (float)i / (float)outer2; tessCoords.push_back(Vec3( v, 1.0f - v, 0.0f)); }
684 const int numInnerTriangles = inner/2;
685 for (int innerTriangleNdx = 0; innerTriangleNdx < numInnerTriangles; innerTriangleNdx++)
687 const int curInnerTriangleLevel = inner - 2*(innerTriangleNdx+1);
689 if (curInnerTriangleLevel == 0)
690 tessCoords.push_back(Vec3(1.0f/3.0f));
693 const float minUVW = (float)(2 * (innerTriangleNdx + 1)) / (float)(3 * inner);
694 const float maxUVW = 1.0f - 2.0f*minUVW;
695 const Vec3 corners[3] =
697 Vec3(maxUVW, minUVW, minUVW),
698 Vec3(minUVW, maxUVW, minUVW),
699 Vec3(minUVW, minUVW, maxUVW)
702 for (int i = 0; i < curInnerTriangleLevel; i++)
704 const float f = (float)i / (float)curInnerTriangleLevel;
705 for (int j = 0; j < 3; j++)
706 tessCoords.push_back((1.0f - f)*corners[j] + f*corners[(j+1)%3]);
715 static int referenceTriangleNonPointModePrimitiveCount (SpacingMode spacingMode, int inner, int outer0, int outer1, int outer2)
719 if (outer0 == 1 && outer1 == 1 && outer2 == 1)
722 return referenceTriangleNonPointModePrimitiveCount(spacingMode, spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
723 outer0, outer1, outer2);
727 int result = outer0 + outer1 + outer2;
729 const int numInnerTriangles = inner/2;
730 for (int innerTriangleNdx = 0; innerTriangleNdx < numInnerTriangles; innerTriangleNdx++)
732 const int curInnerTriangleLevel = inner - 2*(innerTriangleNdx+1);
734 if (curInnerTriangleLevel == 1)
737 result += 2*3*curInnerTriangleLevel;
744 // \note The tessellation coordinates generated by this function could break some of the rules given in the spec (e.g. it may not exactly hold that [uv] + (1.0f-[uv]) == 1.0f).
745 static vector<Vec3> generateReferenceQuadTessCoords (SpacingMode spacingMode, int inner0, int inner1, int outer0, int outer1, int outer2, int outer3)
747 vector<Vec3> tessCoords;
749 if (inner0 == 1 || inner1 == 1)
751 if (inner0 == 1 && inner1 == 1 && outer0 == 1 && outer1 == 1 && outer2 == 1 && outer3 == 1)
753 tessCoords.push_back(Vec3(0.0f, 0.0f, 0.0f));
754 tessCoords.push_back(Vec3(1.0f, 0.0f, 0.0f));
755 tessCoords.push_back(Vec3(0.0f, 1.0f, 0.0f));
756 tessCoords.push_back(Vec3(1.0f, 1.0f, 0.0f));
760 return generateReferenceQuadTessCoords(spacingMode, inner0 > 1 ? inner0 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
761 inner1 > 1 ? inner1 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
762 outer0, outer1, outer2, outer3);
766 for (int i = 0; i < outer0; i++) { const float v = (float)i / (float)outer0; tessCoords.push_back(Vec3(0.0f, v, 0.0f)); }
767 for (int i = 0; i < outer1; i++) { const float v = (float)i / (float)outer1; tessCoords.push_back(Vec3(1.0f-v, 0.0f, 0.0f)); }
768 for (int i = 0; i < outer2; i++) { const float v = (float)i / (float)outer2; tessCoords.push_back(Vec3(1.0f, 1.0f-v, 0.0f)); }
769 for (int i = 0; i < outer3; i++) { const float v = (float)i / (float)outer3; tessCoords.push_back(Vec3(v, 1.0f, 0.0f)); }
771 for (int innerVtxY = 0; innerVtxY < inner1-1; innerVtxY++)
772 for (int innerVtxX = 0; innerVtxX < inner0-1; innerVtxX++)
773 tessCoords.push_back(Vec3((float)(innerVtxX + 1) / (float)inner0,
774 (float)(innerVtxY + 1) / (float)inner1,
781 static int referenceQuadNonPointModePrimitiveCount (SpacingMode spacingMode, int inner0, int inner1, int outer0, int outer1, int outer2, int outer3)
783 vector<Vec3> tessCoords;
785 if (inner0 == 1 || inner1 == 1)
787 if (inner0 == 1 && inner1 == 1 && outer0 == 1 && outer1 == 1 && outer2 == 1 && outer3 == 1)
790 return referenceQuadNonPointModePrimitiveCount(spacingMode, inner0 > 1 ? inner0 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
791 inner1 > 1 ? inner1 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
792 outer0, outer1, outer2, outer3);
795 return 2*(inner0-2)*(inner1-2) + 2*(inner0-2) + 2*(inner1-2) + outer0+outer1+outer2+outer3;
798 // \note The tessellation coordinates generated by this function could break some of the rules given in the spec (e.g. it may not exactly hold that [uv] + (1.0f-[uv]) == 1.0f).
799 static vector<Vec3> generateReferenceIsolineTessCoords (int outer0, int outer1)
801 vector<Vec3> tessCoords;
803 for (int y = 0; y < outer0; y++)
804 for (int x = 0; x < outer1+1; x++)
805 tessCoords.push_back(Vec3((float)x / (float)outer1,
806 (float)y / (float)outer0,
812 static int referenceIsolineNonPointModePrimitiveCount (int outer0, int outer1)
814 return outer0*outer1;
817 static void getClampedRoundedTriangleTessLevels (SpacingMode spacingMode, const float* innerSrc, const float* outerSrc, int* innerDst, int *outerDst)
819 innerDst[0] = getClampedRoundedTessLevel(spacingMode, innerSrc[0]);
820 for (int i = 0; i < 3; i++)
821 outerDst[i] = getClampedRoundedTessLevel(spacingMode, outerSrc[i]);
824 static void getClampedRoundedQuadTessLevels (SpacingMode spacingMode, const float* innerSrc, const float* outerSrc, int* innerDst, int *outerDst)
826 for (int i = 0; i < 2; i++)
827 innerDst[i] = getClampedRoundedTessLevel(spacingMode, innerSrc[i]);
828 for (int i = 0; i < 4; i++)
829 outerDst[i] = getClampedRoundedTessLevel(spacingMode, outerSrc[i]);
832 static void getClampedRoundedIsolineTessLevels (SpacingMode spacingMode, const float* outerSrc, int* outerDst)
834 outerDst[0] = getClampedRoundedTessLevel(SPACINGMODE_EQUAL, outerSrc[0]);
835 outerDst[1] = getClampedRoundedTessLevel(spacingMode, outerSrc[1]);
838 static inline bool isPatchDiscarded (TessPrimitiveType primitiveType, const float* outerLevels)
840 const int numOuterLevels = numOuterTessellationLevels(primitiveType);
841 for (int i = 0; i < numOuterLevels; i++)
842 if (outerLevels[i] <= 0.0f)
847 static vector<Vec3> generateReferenceTessCoords (TessPrimitiveType primitiveType, SpacingMode spacingMode, const float* innerLevels, const float* outerLevels)
849 if (isPatchDiscarded(primitiveType, outerLevels))
850 return vector<Vec3>();
852 switch (primitiveType)
854 case TESSPRIMITIVETYPE_TRIANGLES:
858 getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
860 if (spacingMode != SPACINGMODE_EQUAL)
862 // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
863 DE_ASSERT(de::abs(innerLevels[0] - (float)inner) < 0.001f);
864 for (int i = 0; i < 3; i++)
865 DE_ASSERT(de::abs(outerLevels[i] - (float)outer[i]) < 0.001f);
866 DE_ASSERT(inner > 1 || (outer[0] == 1 && outer[1] == 1 && outer[2] == 1));
869 return generateReferenceTriangleTessCoords(spacingMode, inner, outer[0], outer[1], outer[2]);
872 case TESSPRIMITIVETYPE_QUADS:
876 getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
878 if (spacingMode != SPACINGMODE_EQUAL)
880 // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
881 for (int i = 0; i < 2; i++)
882 DE_ASSERT(de::abs(innerLevels[i] - (float)inner[i]) < 0.001f);
883 for (int i = 0; i < 4; i++)
884 DE_ASSERT(de::abs(outerLevels[i] - (float)outer[i]) < 0.001f);
886 DE_ASSERT((inner[0] > 1 && inner[1] > 1) || (inner[0] == 1 && inner[1] == 1 && outer[0] == 1 && outer[1] == 1 && outer[2] == 1 && outer[3] == 1));
889 return generateReferenceQuadTessCoords(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]);
892 case TESSPRIMITIVETYPE_ISOLINES:
895 getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
897 if (spacingMode != SPACINGMODE_EQUAL)
899 // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
900 DE_ASSERT(de::abs(outerLevels[1] - (float)outer[1]) < 0.001f);
903 return generateReferenceIsolineTessCoords(outer[0], outer[1]);
908 return vector<Vec3>();
912 static int referencePointModePrimitiveCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, const float* innerLevels, const float* outerLevels)
914 if (isPatchDiscarded(primitiveType, outerLevels))
917 switch (primitiveType)
919 case TESSPRIMITIVETYPE_TRIANGLES:
923 getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
924 return (int)generateReferenceTriangleTessCoords(spacingMode, inner, outer[0], outer[1], outer[2]).size();
927 case TESSPRIMITIVETYPE_QUADS:
931 getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
932 return (int)generateReferenceQuadTessCoords(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]).size();
935 case TESSPRIMITIVETYPE_ISOLINES:
938 getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
939 return (int)generateReferenceIsolineTessCoords(outer[0], outer[1]).size();
948 static int referenceNonPointModePrimitiveCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, const float* innerLevels, const float* outerLevels)
950 if (isPatchDiscarded(primitiveType, outerLevels))
953 switch (primitiveType)
955 case TESSPRIMITIVETYPE_TRIANGLES:
959 getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
960 return referenceTriangleNonPointModePrimitiveCount(spacingMode, inner, outer[0], outer[1], outer[2]);
963 case TESSPRIMITIVETYPE_QUADS:
967 getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
968 return referenceQuadNonPointModePrimitiveCount(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]);
971 case TESSPRIMITIVETYPE_ISOLINES:
974 getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
975 return referenceIsolineNonPointModePrimitiveCount(outer[0], outer[1]);
984 static int referencePrimitiveCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, bool usePointMode, const float* innerLevels, const float* outerLevels)
986 return usePointMode ? referencePointModePrimitiveCount (primitiveType, spacingMode, innerLevels, outerLevels)
987 : referenceNonPointModePrimitiveCount (primitiveType, spacingMode, innerLevels, outerLevels);
990 static int referenceVertexCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, bool usePointMode, const float* innerLevels, const float* outerLevels)
992 return referencePrimitiveCount(primitiveType, spacingMode, usePointMode, innerLevels, outerLevels)
993 * numVerticesPerPrimitive(outputPrimitiveTypeGL(primitiveType, usePointMode));
996 //! Helper for calling referenceVertexCount multiple times with different tessellation levels.
997 //! \note Levels contains inner and outer levels, per patch, in order IIOOOO. The full 6 levels must always be present, irrespective of primitiveType.
998 static int multiplePatchReferenceVertexCount (TessPrimitiveType primitiveType, SpacingMode spacingMode, bool usePointMode, const float* levels, int numPatches)
1001 for (int patchNdx = 0; patchNdx < numPatches; patchNdx++)
1002 result += referenceVertexCount(primitiveType, spacingMode, usePointMode, &levels[6*patchNdx + 0], &levels[6*patchNdx + 2]);
1006 vector<float> generateRandomPatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel, de::Random& rnd)
1008 vector<float> tessLevels(numPatches*6);
1010 for (int patchNdx = 0; patchNdx < numPatches; patchNdx++)
1012 float* const inner = &tessLevels[patchNdx*6 + 0];
1013 float* const outer = &tessLevels[patchNdx*6 + 2];
1015 for (int j = 0; j < 2; j++)
1016 inner[j] = rnd.getFloat(1.0f, 62.0f);
1017 for (int j = 0; j < 4; j++)
1018 outer[j] = j == constantOuterLevelIndex ? constantOuterLevel : rnd.getFloat(1.0f, 62.0f);
1024 static inline void drawPoint (tcu::Surface& dst, int centerX, int centerY, const tcu::RGBA& color, int size)
1026 const int width = dst.getWidth();
1027 const int height = dst.getHeight();
1028 DE_ASSERT(de::inBounds(centerX, 0, width) && de::inBounds(centerY, 0, height));
1029 DE_ASSERT(size > 0);
1031 for (int yOff = -((size-1)/2); yOff <= size/2; yOff++)
1032 for (int xOff = -((size-1)/2); xOff <= size/2; xOff++)
1034 const int pixX = centerX + xOff;
1035 const int pixY = centerY + yOff;
1036 if (de::inBounds(pixX, 0, width) && de::inBounds(pixY, 0, height))
1037 dst.setPixel(pixX, pixY, color);
1041 static void drawTessCoordPoint (tcu::Surface& dst, TessPrimitiveType primitiveType, const Vec3& pt, const tcu::RGBA& color, int size)
1043 // \note These coordinates should match the description in the log message in TessCoordCase::iterate.
1045 static const Vec2 triangleCorners[3] =
1048 Vec2(0.5f, 0.95f - 0.9f*deFloatSqrt(3.0f/4.0f)),
1052 static const float quadIsolineLDRU[4] =
1054 0.1f, 0.9f, 0.9f, 0.1f
1057 const Vec2 dstPos = primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? pt.x()*triangleCorners[0]
1058 + pt.y()*triangleCorners[1]
1059 + pt.z()*triangleCorners[2]
1061 : primitiveType == TESSPRIMITIVETYPE_QUADS ||
1062 primitiveType == TESSPRIMITIVETYPE_ISOLINES ? Vec2((1.0f - pt.x())*quadIsolineLDRU[0] + pt.x()*quadIsolineLDRU[2],
1063 (1.0f - pt.y())*quadIsolineLDRU[1] + pt.y()*quadIsolineLDRU[3])
1067 drawPoint(dst, (int)(dstPos.x() * (float)dst.getWidth()), (int)(dstPos.y() * (float)dst.getHeight()), color, size);
1070 static void drawTessCoordVisualization (tcu::Surface& dst, TessPrimitiveType primitiveType, const vector<Vec3>& coords)
1072 const int imageWidth = 256;
1073 const int imageHeight = 256;
1074 dst.setSize(imageWidth, imageHeight);
1076 tcu::clear(dst.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
1078 for (int i = 0; i < (int)coords.size(); i++)
1079 drawTessCoordPoint(dst, primitiveType, coords[i], tcu::RGBA::white(), 2);
1082 static int binarySearchFirstVec3WithXAtLeast (const vector<Vec3>& sorted, float x)
1084 const Vec3 ref(x, 0.0f, 0.0f);
1085 const vector<Vec3>::const_iterator first = std::lower_bound(sorted.begin(), sorted.end(), ref, vec3XLessThan);
1086 if (first == sorted.end())
1088 return (int)std::distance(sorted.begin(), first);
1091 template <typename T, typename P>
1092 static vector<T> sorted (const vector<T>& unsorted, P pred)
1094 vector<T> result = unsorted;
1095 std::sort(result.begin(), result.end(), pred);
1099 template <typename T>
1100 static vector<T> sorted (const vector<T>& unsorted)
1102 vector<T> result = unsorted;
1103 std::sort(result.begin(), result.end());
1107 // Check that all points in subset are (approximately) present also in superset.
1108 static bool oneWayComparePointSets (TestLog& log,
1109 tcu::Surface& errorDst,
1110 TessPrimitiveType primitiveType,
1111 const vector<Vec3>& subset,
1112 const vector<Vec3>& superset,
1113 const char* subsetName,
1114 const char* supersetName,
1115 const tcu::RGBA& errorColor)
1117 const vector<Vec3> supersetSorted = sorted(superset, vec3XLessThan);
1118 const float epsilon = 0.01f;
1119 const int maxNumFailurePrints = 5;
1120 int numFailuresDetected = 0;
1122 for (int subNdx = 0; subNdx < (int)subset.size(); subNdx++)
1124 const Vec3& subPt = subset[subNdx];
1126 bool matchFound = false;
1129 // Binary search the index of the first point in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range.
1130 const Vec3 matchMin = subPt - epsilon;
1131 const Vec3 matchMax = subPt + epsilon;
1132 int firstCandidateNdx = binarySearchFirstVec3WithXAtLeast(supersetSorted, matchMin.x());
1134 if (firstCandidateNdx >= 0)
1136 // Compare subPt to all points in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range.
1137 for (int superNdx = firstCandidateNdx; superNdx < (int)supersetSorted.size() && supersetSorted[superNdx].x() <= matchMax.x(); superNdx++)
1139 const Vec3& superPt = supersetSorted[superNdx];
1141 if (tcu::boolAll(tcu::greaterThanEqual (superPt, matchMin)) &&
1142 tcu::boolAll(tcu::lessThanEqual (superPt, matchMax)))
1153 numFailuresDetected++;
1154 if (numFailuresDetected < maxNumFailurePrints)
1155 log << TestLog::Message << "Failure: no matching " << supersetName << " point found for " << subsetName << " point " << subPt << TestLog::EndMessage;
1156 else if (numFailuresDetected == maxNumFailurePrints)
1157 log << TestLog::Message << "Note: More errors follow" << TestLog::EndMessage;
1159 drawTessCoordPoint(errorDst, primitiveType, subPt, errorColor, 4);
1163 return numFailuresDetected == 0;
1166 static bool compareTessCoords (TestLog& log, TessPrimitiveType primitiveType, const vector<Vec3>& refCoords, const vector<Vec3>& resCoords)
1168 tcu::Surface refVisual;
1169 tcu::Surface resVisual;
1170 bool success = true;
1172 drawTessCoordVisualization(refVisual, primitiveType, refCoords);
1173 drawTessCoordVisualization(resVisual, primitiveType, resCoords);
1175 // Check that all points in reference also exist in result.
1176 success = oneWayComparePointSets(log, refVisual, primitiveType, refCoords, resCoords, "reference", "result", tcu::RGBA::blue()) && success;
1177 // Check that all points in result also exist in reference.
1178 success = oneWayComparePointSets(log, resVisual, primitiveType, resCoords, refCoords, "result", "reference", tcu::RGBA::red()) && success;
1182 log << TestLog::Message << "Note: in the following reference visualization, points that are missing in result point set are blue (if any)" << TestLog::EndMessage
1183 << TestLog::Image("RefTessCoordVisualization", "Reference tessCoord visualization", refVisual)
1184 << TestLog::Message << "Note: in the following result visualization, points that are missing in reference point set are red (if any)" << TestLog::EndMessage;
1187 log << TestLog::Image("ResTessCoordVisualization", "Result tessCoord visualization", resVisual);
1192 namespace VerifyFractionalSpacingSingleInternal
1197 int index; //!< Index of left coordinate in sortedXCoords.
1199 Segment (void) : index(-1), length(-1.0f) {}
1200 Segment (int index_, float length_) : index(index_), length(length_) {}
1202 static vector<float> lengths (const vector<Segment>& segments) { return members(segments, &Segment::length); }
1207 /*--------------------------------------------------------------------*//*!
1208 * \brief Verify fractional spacing conditions for a single line
1210 * Verify that the splitting of an edge (resulting from e.g. an isoline
1211 * with outer levels { 1.0, tessLevel }) with a given fractional spacing
1212 * mode fulfills certain conditions given in the spec.
1214 * Note that some conditions can't be checked from just one line
1215 * (specifically, that the additional segment decreases monotonically
1216 * length and the requirement that the additional segments be placed
1217 * identically for identical values of clamped level).
1219 * Therefore, the function stores some values to additionalSegmentLengthDst
1220 * and additionalSegmentLocationDst that can later be given to
1221 * verifyFractionalSpacingMultiple(). A negative value in length means that
1222 * no additional segments are present, i.e. there's just one segment.
1223 * A negative value in location means that the value wasn't determinable,
1224 * i.e. all segments had same length.
1225 * The values are not stored if false is returned.
1226 *//*--------------------------------------------------------------------*/
1227 static bool verifyFractionalSpacingSingle (TestLog& log, SpacingMode spacingMode, float tessLevel, const vector<float>& coords, float& additionalSegmentLengthDst, int& additionalSegmentLocationDst)
1229 using namespace VerifyFractionalSpacingSingleInternal;
1231 DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN);
1233 const float clampedLevel = getClampedTessLevel(spacingMode, tessLevel);
1234 const int finalLevel = getRoundedTessLevel(spacingMode, clampedLevel);
1235 const vector<float> sortedCoords = sorted(coords);
1236 string failNote = "Note: tessellation level is " + de::toString(tessLevel) + "\nNote: sorted coordinates are:\n " + containerStr(sortedCoords);
1238 if ((int)coords.size() != finalLevel + 1)
1240 log << TestLog::Message << "Failure: number of vertices is " << coords.size() << "; expected " << finalLevel + 1
1241 << " (clamped tessellation level is " << clampedLevel << ")"
1242 << "; final level (clamped level rounded up to " << (spacingMode == SPACINGMODE_FRACTIONAL_EVEN ? "even" : "odd") << ") is " << finalLevel
1243 << " and should equal the number of segments, i.e. number of vertices minus 1" << TestLog::EndMessage
1244 << TestLog::Message << failNote << TestLog::EndMessage;
1248 if (sortedCoords[0] != 0.0f || sortedCoords.back() != 1.0f)
1250 log << TestLog::Message << "Failure: smallest coordinate should be 0.0 and biggest should be 1.0" << TestLog::EndMessage
1251 << TestLog::Message << failNote << TestLog::EndMessage;
1256 vector<Segment> segments(finalLevel);
1257 for (int i = 0; i < finalLevel; i++)
1258 segments[i] = Segment(i, sortedCoords[i+1] - sortedCoords[i]);
1260 failNote += "\nNote: segment lengths are, from left to right:\n " + containerStr(Segment::lengths(segments));
1263 // Divide segments to two different groups based on length.
1265 vector<Segment> segmentsA;
1266 vector<Segment> segmentsB;
1267 segmentsA.push_back(segments[0]);
1269 for (int segNdx = 1; segNdx < (int)segments.size(); segNdx++)
1271 const float epsilon = 0.001f;
1272 const Segment& seg = segments[segNdx];
1274 if (de::abs(seg.length - segmentsA[0].length) < epsilon)
1275 segmentsA.push_back(seg);
1276 else if (segmentsB.empty() || de::abs(seg.length - segmentsB[0].length) < epsilon)
1277 segmentsB.push_back(seg);
1280 log << TestLog::Message << "Failure: couldn't divide segments to 2 groups by length; "
1281 << "e.g. segment of length " << seg.length << " isn't approximately equal to either "
1282 << segmentsA[0].length << " or " << segmentsB[0].length << TestLog::EndMessage
1283 << TestLog::Message << failNote << TestLog::EndMessage;
1288 if (clampedLevel == (float)finalLevel)
1290 // All segments should be of equal length.
1291 if (!segmentsA.empty() && !segmentsB.empty())
1293 log << TestLog::Message << "Failure: clamped and final tessellation level are equal, but not all segments are of equal length." << TestLog::EndMessage
1294 << TestLog::Message << failNote << TestLog::EndMessage;
1299 if (segmentsA.empty() || segmentsB.empty()) // All segments have same length. This is ok.
1301 additionalSegmentLengthDst = segments.size() == 1 ? -1.0f : segments[0].length;
1302 additionalSegmentLocationDst = -1;
1306 if (segmentsA.size() != 2 && segmentsB.size() != 2)
1308 log << TestLog::Message << "Failure: when dividing the segments to 2 groups by length, neither of the two groups has exactly 2 or 0 segments in it" << TestLog::EndMessage
1309 << TestLog::Message << failNote << TestLog::EndMessage;
1313 // For convenience, arrange so that the 2-segment group is segmentsB.
1314 if (segmentsB.size() != 2)
1315 std::swap(segmentsA, segmentsB);
1317 // \note For 4-segment lines both segmentsA and segmentsB have 2 segments each.
1318 // Thus, we can't be sure which ones were meant as the additional segments.
1319 // We give the benefit of the doubt by assuming that they're the shorter
1320 // ones (as they should).
1322 if (segmentsA.size() != 2)
1324 if (segmentsB[0].length > segmentsA[0].length + 0.001f)
1326 log << TestLog::Message << "Failure: the two additional segments are longer than the other segments" << TestLog::EndMessage
1327 << TestLog::Message << failNote << TestLog::EndMessage;
1333 // We have 2 segmentsA and 2 segmentsB, ensure segmentsB has the shorter lengths
1334 if (segmentsB[0].length > segmentsA[0].length)
1335 std::swap(segmentsA, segmentsB);
1338 // Check that the additional segments are placed symmetrically.
1339 if (segmentsB[0].index + segmentsB[1].index + 1 != (int)segments.size())
1341 log << TestLog::Message << "Failure: the two additional segments aren't placed symmetrically; "
1342 << "one is at index " << segmentsB[0].index << " and other is at index " << segmentsB[1].index
1343 << " (note: the two indexes should sum to " << (int)segments.size()-1 << ", i.e. numberOfSegments-1)" << TestLog::EndMessage
1344 << TestLog::Message << failNote << TestLog::EndMessage;
1348 additionalSegmentLengthDst = segmentsB[0].length;
1349 if (segmentsA.size() != 2)
1350 additionalSegmentLocationDst = de::min(segmentsB[0].index, segmentsB[1].index);
1352 additionalSegmentLocationDst = segmentsB[0].length < segmentsA[0].length - 0.001f ? de::min(segmentsB[0].index, segmentsB[1].index)
1353 : -1; // \note -1 when can't reliably decide which ones are the additional segments, a or b.
1360 namespace VerifyFractionalSpacingMultipleInternal
1366 float additionalSegmentLength;
1367 int additionalSegmentLocation;
1368 LineData (float lev, float len, int loc) : tessLevel(lev), additionalSegmentLength(len), additionalSegmentLocation(loc) {}
1373 /*--------------------------------------------------------------------*//*!
1374 * \brief Verify fractional spacing conditions between multiple lines
1376 * Verify the fractional spacing conditions that are not checked in
1377 * verifyFractionalSpacingSingle(). Uses values given by said function
1378 * as parameters, in addition to the spacing mode and tessellation level.
1379 *//*--------------------------------------------------------------------*/
1380 static bool verifyFractionalSpacingMultiple (TestLog& log, SpacingMode spacingMode, const vector<float>& tessLevels, const vector<float>& additionalSegmentLengths, const vector<int>& additionalSegmentLocations)
1382 using namespace VerifyFractionalSpacingMultipleInternal;
1384 DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN);
1385 DE_ASSERT(tessLevels.size() == additionalSegmentLengths.size() &&
1386 tessLevels.size() == additionalSegmentLocations.size());
1388 vector<LineData> lineDatas;
1390 for (int i = 0; i < (int)tessLevels.size(); i++)
1391 lineDatas.push_back(LineData(tessLevels[i], additionalSegmentLengths[i], additionalSegmentLocations[i]));
1394 const vector<LineData> lineDatasSortedByLevel = sorted(lineDatas, memberPred<std::less>(&LineData::tessLevel));
1396 // Check that lines with identical clamped tessellation levels have identical additionalSegmentLocation.
1398 for (int lineNdx = 1; lineNdx < (int)lineDatasSortedByLevel.size(); lineNdx++)
1400 const LineData& curData = lineDatasSortedByLevel[lineNdx];
1401 const LineData& prevData = lineDatasSortedByLevel[lineNdx-1];
1403 if (curData.additionalSegmentLocation < 0 || prevData.additionalSegmentLocation < 0)
1404 continue; // Unknown locations, skip.
1406 if (getClampedTessLevel(spacingMode, curData.tessLevel) == getClampedTessLevel(spacingMode, prevData.tessLevel) &&
1407 curData.additionalSegmentLocation != prevData.additionalSegmentLocation)
1409 log << TestLog::Message << "Failure: additional segments not located identically for two edges with identical clamped tessellation levels" << TestLog::EndMessage
1410 << TestLog::Message << "Note: tessellation levels are " << curData.tessLevel << " and " << prevData.tessLevel
1411 << " (clamped level " << getClampedTessLevel(spacingMode, curData.tessLevel) << ")"
1412 << "; but first additional segments located at indices "
1413 << curData.additionalSegmentLocation << " and " << prevData.additionalSegmentLocation << ", respectively" << TestLog::EndMessage;
1418 // Check that, among lines with same clamped rounded tessellation level, additionalSegmentLength is monotonically decreasing with "clampedRoundedTessLevel - clampedTessLevel" (the "fraction").
1420 for (int lineNdx = 1; lineNdx < (int)lineDatasSortedByLevel.size(); lineNdx++)
1422 const LineData& curData = lineDatasSortedByLevel[lineNdx];
1423 const LineData& prevData = lineDatasSortedByLevel[lineNdx-1];
1425 if (curData.additionalSegmentLength < 0.0f || prevData.additionalSegmentLength < 0.0f)
1426 continue; // Unknown segment lengths, skip.
1428 const float curClampedLevel = getClampedTessLevel(spacingMode, curData.tessLevel);
1429 const float prevClampedLevel = getClampedTessLevel(spacingMode, prevData.tessLevel);
1430 const int curFinalLevel = getRoundedTessLevel(spacingMode, curClampedLevel);
1431 const int prevFinalLevel = getRoundedTessLevel(spacingMode, prevClampedLevel);
1433 if (curFinalLevel != prevFinalLevel)
1436 const float curFraction = (float)curFinalLevel - curClampedLevel;
1437 const float prevFraction = (float)prevFinalLevel - prevClampedLevel;
1439 if (curData.additionalSegmentLength < prevData.additionalSegmentLength ||
1440 (curClampedLevel == prevClampedLevel && curData.additionalSegmentLength != prevData.additionalSegmentLength))
1442 log << TestLog::Message << "Failure: additional segment length isn't monotonically decreasing with the fraction <n> - <f>, among edges with same final tessellation level" << TestLog::EndMessage
1443 << TestLog::Message << "Note: <f> stands for the clamped tessellation level and <n> for the final (rounded and clamped) tessellation level" << TestLog::EndMessage
1444 << TestLog::Message << "Note: two edges have tessellation levels " << prevData.tessLevel << " and " << curData.tessLevel << " respectively"
1445 << ", clamped " << prevClampedLevel << " and " << curClampedLevel << ", final " << prevFinalLevel << " and " << curFinalLevel
1446 << "; fractions are " << prevFraction << " and " << curFraction
1447 << ", but resulted in segment lengths " << prevData.additionalSegmentLength << " and " << curData.additionalSegmentLength << TestLog::EndMessage;
1456 //! Compare triangle sets, ignoring triangle order and vertex order within triangle, and possibly exclude some triangles too.
1457 template <typename IsTriangleRelevantT>
1458 static bool compareTriangleSets (const vector<Vec3>& coordsA,
1459 const vector<Vec3>& coordsB,
1461 const IsTriangleRelevantT& isTriangleRelevant,
1462 const char* ignoredTriangleDescription = DE_NULL)
1464 typedef tcu::Vector<Vec3, 3> Triangle;
1465 typedef LexCompare<Triangle, 3, VecLexLessThan<3> > TriangleLexLessThan;
1466 typedef std::set<Triangle, TriangleLexLessThan> TriangleSet;
1468 DE_ASSERT(coordsA.size() % 3 == 0 && coordsB.size() % 3 == 0);
1470 const int numTrianglesA = (int)coordsA.size()/3;
1471 const int numTrianglesB = (int)coordsB.size()/3;
1472 TriangleSet trianglesA;
1473 TriangleSet trianglesB;
1475 for (int aOrB = 0; aOrB < 2; aOrB++)
1477 const vector<Vec3>& coords = aOrB == 0 ? coordsA : coordsB;
1478 const int numTriangles = aOrB == 0 ? numTrianglesA : numTrianglesB;
1479 TriangleSet& triangles = aOrB == 0 ? trianglesA : trianglesB;
1481 for (int triNdx = 0; triNdx < numTriangles; triNdx++)
1483 Triangle triangle(coords[3*triNdx + 0],
1484 coords[3*triNdx + 1],
1485 coords[3*triNdx + 2]);
1487 if (isTriangleRelevant(triangle.getPtr()))
1489 std::sort(triangle.getPtr(), triangle.getPtr()+3, VecLexLessThan<3>());
1490 triangles.insert(triangle);
1496 TriangleSet::const_iterator aIt = trianglesA.begin();
1497 TriangleSet::const_iterator bIt = trianglesB.begin();
1499 while (aIt != trianglesA.end() || bIt != trianglesB.end())
1501 const bool aEnd = aIt == trianglesA.end();
1502 const bool bEnd = bIt == trianglesB.end();
1504 if (aEnd || bEnd || *aIt != *bIt)
1506 log << TestLog::Message << "Failure: triangle sets in two cases are not equal (when ignoring triangle and vertex order"
1507 << (ignoredTriangleDescription == DE_NULL ? "" : string() + ", and " + ignoredTriangleDescription) << ")" << TestLog::EndMessage;
1509 if (!aEnd && (bEnd || TriangleLexLessThan()(*aIt, *bIt)))
1510 log << TestLog::Message << "Note: e.g. triangle " << *aIt << " exists for first case but not for second" << TestLog::EndMessage;
1512 log << TestLog::Message << "Note: e.g. triangle " << *bIt << " exists for second case but not for first" << TestLog::EndMessage;
1525 static bool compareTriangleSets (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, TestLog& log)
1527 return compareTriangleSets(coordsA, coordsB, log, ConstantUnaryPredicate<const Vec3*, true>());
1530 static void checkGPUShader5Support (Context& context)
1532 const bool supportsES32 = glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::es(3, 2));
1533 TCU_CHECK_AND_THROW(NotSupportedError, supportsES32 || context.getContextInfo().isExtensionSupported("GL_EXT_gpu_shader5"), "GL_EXT_gpu_shader5 is not supported");
1536 static void checkTessellationSupport (Context& context)
1538 const bool supportsES32 = glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::es(3, 2));
1539 TCU_CHECK_AND_THROW(NotSupportedError, supportsES32 || context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"), "GL_EXT_tessellation_shader is not supported");
1542 static std::string specializeShader(Context& context, const char* code)
1544 const glu::ContextType contextType = context.getRenderContext().getType();
1545 const glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(contextType);
1546 bool supportsES32 = glu::contextSupports(contextType, glu::ApiType::es(3, 2));
1548 std::map<std::string, std::string> specializationMap;
1550 specializationMap["GLSL_VERSION_DECL"] = glu::getGLSLVersionDeclaration(glslVersion);
1551 specializationMap["GPU_SHADER5_REQUIRE"] = supportsES32 ? "" : "#extension GL_EXT_gpu_shader5 : require";
1552 specializationMap["TESSELLATION_SHADER_REQUIRE"] = supportsES32 ? "" : "#extension GL_EXT_tessellation_shader : require";
1554 return tcu::StringTemplate(code).specialize(specializationMap);
1557 // Draw primitives with shared edges and check that no cracks are visible at the shared edges.
1558 class CommonEdgeCase : public TestCase
1563 CASETYPE_BASIC = 0, //!< Order patch vertices such that when two patches share a vertex, it's at the same index for both.
1564 CASETYPE_PRECISE, //!< Vertex indices don't match like for CASETYPE_BASIC, but other measures are taken, using the 'precise' qualifier.
1569 CommonEdgeCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing, CaseType caseType)
1570 : TestCase (context, name, description)
1571 , m_primitiveType (primitiveType)
1572 , m_spacing (spacing)
1573 , m_caseType (caseType)
1575 DE_ASSERT(m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES || m_primitiveType == TESSPRIMITIVETYPE_QUADS);
1580 IterateResult iterate (void);
1583 static const int RENDER_SIZE = 256;
1585 const TessPrimitiveType m_primitiveType;
1586 const SpacingMode m_spacing;
1587 const CaseType m_caseType;
1589 SharedPtr<const ShaderProgram> m_program;
1592 void CommonEdgeCase::init (void)
1594 checkTessellationSupport(m_context);
1596 if (m_caseType == CASETYPE_PRECISE)
1597 checkGPUShader5Support(m_context);
1599 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
1601 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
1603 "in highp vec2 in_v_position;\n"
1604 "in highp float in_v_tessParam;\n"
1606 "out highp vec2 in_tc_position;\n"
1607 "out highp float in_tc_tessParam;\n"
1609 "void main (void)\n"
1611 " in_tc_position = in_v_position;\n"
1612 " in_tc_tessParam = in_v_tessParam;\n"
1615 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
1616 "${TESSELLATION_SHADER_REQUIRE}\n"
1617 + string(m_caseType == CASETYPE_PRECISE ? "${GPU_SHADER5_REQUIRE}\n" : "") +
1619 "layout (vertices = " + string(m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "3" : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? "4" : DE_NULL) + ") out;\n"
1621 "in highp vec2 in_tc_position[];\n"
1622 "in highp float in_tc_tessParam[];\n"
1624 "out highp vec2 in_te_position[];\n"
1626 + (m_caseType == CASETYPE_PRECISE ? "precise gl_TessLevelOuter;\n\n" : "") +
1627 "void main (void)\n"
1629 " in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];\n"
1631 " gl_TessLevelInner[0] = 5.0;\n"
1632 " gl_TessLevelInner[1] = 5.0;\n"
1634 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
1635 " gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[2]);\n"
1636 " gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[0]);\n"
1637 " gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[1]);\n"
1638 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ?
1639 " gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[2]);\n"
1640 " gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[0]);\n"
1641 " gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[3] + in_tc_tessParam[1]);\n"
1642 " gl_TessLevelOuter[3] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[3]);\n"
1646 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
1647 "${TESSELLATION_SHADER_REQUIRE}\n"
1648 + string(m_caseType == CASETYPE_PRECISE ? "${GPU_SHADER5_REQUIRE}\n" : "") +
1650 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing) +
1652 "in highp vec2 in_te_position[];\n"
1654 "out mediump vec4 in_f_color;\n"
1656 + (m_caseType == CASETYPE_PRECISE ? "precise gl_Position;\n\n" : "") +
1657 "void main (void)\n"
1659 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
1660 " highp vec2 pos = gl_TessCoord.x*in_te_position[0] + gl_TessCoord.y*in_te_position[1] + gl_TessCoord.z*in_te_position[2];\n"
1662 " highp float f = sqrt(3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z))) * 0.5 + 0.5;\n"
1663 " in_f_color = vec4(gl_TessCoord*f, 1.0);\n"
1664 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ?
1666 + (m_caseType == CASETYPE_BASIC ?
1667 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0]\n"
1668 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1]\n"
1669 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[2]\n"
1670 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[3];\n"
1671 : m_caseType == CASETYPE_PRECISE ?
1672 " highp vec2 a = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0];\n"
1673 " highp vec2 b = ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1];\n"
1674 " highp vec2 c = (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[2];\n"
1675 " highp vec2 d = ( gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[3];\n"
1676 " highp vec2 pos = a+b+c+d;\n"
1679 " highp float f = sqrt(1.0 - 2.0 * max(abs(gl_TessCoord.x - 0.5), abs(gl_TessCoord.y - 0.5)))*0.5 + 0.5;\n"
1680 " in_f_color = vec4(0.1, gl_TessCoord.xy*f, 1.0);\n"
1683 " // Offset the position slightly, based on the parity of the bits in the float representation.\n"
1684 " // This is done to detect possible small differences in edge vertex positions between patches.\n"
1685 " uvec2 bits = floatBitsToUint(pos);\n"
1686 " uint numBits = 0u;\n"
1687 " for (uint i = 0u; i < 32u; i++)\n"
1688 " numBits += ((bits[0] >> i) & 1u) + ((bits[1] >> i) & 1u);\n"
1689 " pos += float(numBits&1u)*0.04;\n"
1691 " gl_Position = vec4(pos, 0.0, 1.0);\n"
1694 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
1696 "layout (location = 0) out mediump vec4 o_color;\n"
1698 "in mediump vec4 in_f_color;\n"
1700 "void main (void)\n"
1702 " o_color = in_f_color;\n"
1705 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
1706 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
1707 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
1708 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
1709 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))));
1711 m_testCtx.getLog() << *m_program;
1712 if (!m_program->isOk())
1713 TCU_FAIL("Program compilation failed");
1716 void CommonEdgeCase::deinit (void)
1721 CommonEdgeCase::IterateResult CommonEdgeCase::iterate (void)
1723 TestLog& log = m_testCtx.getLog();
1724 const RenderContext& renderCtx = m_context.getRenderContext();
1725 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
1726 const deUint32 programGL = m_program->getProgram();
1727 const glw::Functions& gl = renderCtx.getFunctions();
1729 const int gridWidth = 4;
1730 const int gridHeight = 4;
1731 const int numVertices = (gridWidth+1)*(gridHeight+1);
1732 const int numIndices = gridWidth*gridHeight * (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3*2 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4 : -1);
1733 const int numPosCompsPerVertex = 2;
1734 const int totalNumPosComps = numPosCompsPerVertex*numVertices;
1735 vector<float> gridPosComps;
1736 vector<float> gridTessParams;
1737 vector<deUint16> gridIndices;
1739 gridPosComps.reserve(totalNumPosComps);
1740 gridTessParams.reserve(numVertices);
1741 gridIndices.reserve(numIndices);
1744 for (int i = 0; i < gridHeight+1; i++)
1745 for (int j = 0; j < gridWidth+1; j++)
1747 gridPosComps.push_back(-1.0f + 2.0f * ((float)j + 0.5f) / (float)(gridWidth+1));
1748 gridPosComps.push_back(-1.0f + 2.0f * ((float)i + 0.5f) / (float)(gridHeight+1));
1749 gridTessParams.push_back((float)(i*(gridWidth+1) + j) / (float)(numVertices-1));
1753 // Generate patch vertex indices.
1754 // \note If CASETYPE_BASIC, the vertices are ordered such that when multiple
1755 // triangles/quads share a vertex, it's at the same index for everyone.
1757 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
1759 for (int i = 0; i < gridHeight; i++)
1760 for (int j = 0; j < gridWidth; j++)
1762 const deUint16 corners[4] =
1764 (deUint16)((i+0)*(gridWidth+1) + j+0),
1765 (deUint16)((i+0)*(gridWidth+1) + j+1),
1766 (deUint16)((i+1)*(gridWidth+1) + j+0),
1767 (deUint16)((i+1)*(gridWidth+1) + j+1)
1770 const int secondTriangleVertexIndexOffset = m_caseType == CASETYPE_BASIC ? 0
1771 : m_caseType == CASETYPE_PRECISE ? 1
1773 DE_ASSERT(secondTriangleVertexIndexOffset != -1);
1775 for (int k = 0; k < 3; k++)
1776 gridIndices.push_back(corners[(k+0 + i + (2-j%3)) % 3]);
1777 for (int k = 0; k < 3; k++)
1778 gridIndices.push_back(corners[(k+2 + i + (2-j%3) + secondTriangleVertexIndexOffset) % 3 + 1]);
1781 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
1783 for (int i = 0; i < gridHeight; i++)
1784 for (int j = 0; j < gridWidth; j++)
1786 // \note The vertices are ordered such that when multiple quads
1787 // share a vertices, it's at the same index for everyone.
1788 for (int m = 0; m < 2; m++)
1789 for (int n = 0; n < 2; n++)
1790 gridIndices.push_back((deUint16)((i+(i+m)%2)*(gridWidth+1) + j+(j+n)%2));
1792 if(m_caseType == CASETYPE_PRECISE && (i+j) % 2 == 0)
1793 std::reverse(gridIndices.begin() + (gridIndices.size() - 4),
1794 gridIndices.begin() + gridIndices.size());
1800 DE_ASSERT((int)gridPosComps.size() == totalNumPosComps);
1801 DE_ASSERT((int)gridTessParams.size() == numVertices);
1802 DE_ASSERT((int)gridIndices.size() == numIndices);
1804 setViewport(gl, viewport);
1805 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
1806 gl.useProgram(programGL);
1809 gl.patchParameteri(GL_PATCH_VERTICES, m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4 : -1);
1810 gl.clear(GL_COLOR_BUFFER_BIT);
1812 const glu::VertexArrayBinding attrBindings[] =
1814 glu::va::Float("in_v_position", numPosCompsPerVertex, numVertices, 0, &gridPosComps[0]),
1815 glu::va::Float("in_v_tessParam", 1, numVertices, 0, &gridTessParams[0])
1818 glu::draw(renderCtx, programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
1819 glu::pr::Patches((int)gridIndices.size(), &gridIndices[0]));
1820 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
1824 const tcu::Surface rendered = getPixels(renderCtx, viewport);
1826 log << TestLog::Image("RenderedImage", "Rendered Image", rendered)
1827 << TestLog::Message << "Note: coloring is done to clarify the positioning and orientation of the "
1828 << (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "triangles" : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? "quads" : DE_NULL)
1829 << "; the color of a vertex corresponds to the index of that vertex in the patch"
1830 << TestLog::EndMessage;
1832 if (m_caseType == CASETYPE_BASIC)
1833 log << TestLog::Message << "Note: each shared vertex has the same index among the primitives it belongs to" << TestLog::EndMessage;
1834 else if (m_caseType == CASETYPE_PRECISE)
1835 log << TestLog::Message << "Note: the 'precise' qualifier is used to avoid cracks between primitives" << TestLog::EndMessage;
1839 // Ad-hoc result verification - check that a certain rectangle in the image contains no black pixels.
1841 const int startX = (int)(0.15f * (float)rendered.getWidth());
1842 const int endX = (int)(0.85f * (float)rendered.getWidth());
1843 const int startY = (int)(0.15f * (float)rendered.getHeight());
1844 const int endY = (int)(0.85f * (float)rendered.getHeight());
1846 for (int y = startY; y < endY; y++)
1847 for (int x = startX; x < endX; x++)
1849 const tcu::RGBA pixel = rendered.getPixel(x, y);
1851 if (pixel.getRed() == 0 && pixel.getGreen() == 0 && pixel.getBlue() == 0)
1853 log << TestLog::Message << "Failure: there seem to be cracks in the rendered result" << TestLog::EndMessage
1854 << TestLog::Message << "Note: pixel with zero r, g and b channels found at " << tcu::IVec2(x, y) << TestLog::EndMessage;
1856 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
1861 log << TestLog::Message << "Success: there seem to be no cracks in the rendered result" << TestLog::EndMessage;
1864 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1868 // Check tessellation coordinates (read with transform feedback).
1869 class TessCoordCase : public TestCase
1872 TessCoordCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing)
1873 : TestCase (context, name, description)
1874 , m_primitiveType (primitiveType)
1875 , m_spacing (spacing)
1881 IterateResult iterate (void);
1890 static const int RENDER_SIZE = 16;
1892 vector<TessLevels> genTessLevelCases (void) const;
1894 const TessPrimitiveType m_primitiveType;
1895 const SpacingMode m_spacing;
1897 SharedPtr<const ShaderProgram> m_program;
1900 void TessCoordCase::init (void)
1902 checkTessellationSupport(m_context);
1903 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
1905 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
1907 "void main (void)\n"
1911 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
1912 "${TESSELLATION_SHADER_REQUIRE}\n"
1914 "layout (vertices = 1) out;\n"
1916 "uniform mediump float u_tessLevelInner0;\n"
1917 "uniform mediump float u_tessLevelInner1;\n"
1919 "uniform mediump float u_tessLevelOuter0;\n"
1920 "uniform mediump float u_tessLevelOuter1;\n"
1921 "uniform mediump float u_tessLevelOuter2;\n"
1922 "uniform mediump float u_tessLevelOuter3;\n"
1924 "void main (void)\n"
1926 " gl_TessLevelInner[0] = u_tessLevelInner0;\n"
1927 " gl_TessLevelInner[1] = u_tessLevelInner1;\n"
1929 " gl_TessLevelOuter[0] = u_tessLevelOuter0;\n"
1930 " gl_TessLevelOuter[1] = u_tessLevelOuter1;\n"
1931 " gl_TessLevelOuter[2] = u_tessLevelOuter2;\n"
1932 " gl_TessLevelOuter[3] = u_tessLevelOuter3;\n"
1935 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
1936 "${TESSELLATION_SHADER_REQUIRE}\n"
1938 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, true) +
1940 "out highp vec3 out_te_tessCoord;\n"
1942 "void main (void)\n"
1944 " out_te_tessCoord = gl_TessCoord;\n"
1945 " gl_Position = vec4(gl_TessCoord.xy*1.6 - 0.8, 0.0, 1.0);\n"
1948 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
1950 "layout (location = 0) out mediump vec4 o_color;\n"
1952 "void main (void)\n"
1954 " o_color = vec4(1.0);\n"
1957 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
1958 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
1959 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
1960 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
1961 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
1962 << glu::TransformFeedbackVarying ("out_te_tessCoord")
1963 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
1965 m_testCtx.getLog() << *m_program;
1966 if (!m_program->isOk())
1967 TCU_FAIL("Program compilation failed");
1970 void TessCoordCase::deinit (void)
1975 vector<TessCoordCase::TessLevels> TessCoordCase::genTessLevelCases (void) const
1977 static const TessLevels rawTessLevelCases[] =
1979 { { 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
1980 { { 63.0f, 24.0f }, { 15.0f, 42.0f, 10.0f, 12.0f } },
1981 { { 3.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } },
1982 { { 4.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
1983 { { 2.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } },
1984 { { 5.0f, 6.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
1985 { { 1.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
1986 { { 5.0f, 1.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
1987 { { 5.2f, 1.6f }, { 2.9f, 3.4f, 1.5f, 4.1f } }
1990 if (m_spacing == SPACINGMODE_EQUAL)
1991 return vector<TessLevels>(DE_ARRAY_BEGIN(rawTessLevelCases), DE_ARRAY_END(rawTessLevelCases));
1994 vector<TessLevels> result;
1995 result.reserve(DE_LENGTH_OF_ARRAY(rawTessLevelCases));
1997 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(rawTessLevelCases); tessLevelCaseNdx++)
1999 TessLevels curTessLevelCase = rawTessLevelCases[tessLevelCaseNdx];
2001 float* const inner = &curTessLevelCase.inner[0];
2002 float* const outer = &curTessLevelCase.outer[0];
2004 for (int j = 0; j < 2; j++) inner[j] = (float)getClampedRoundedTessLevel(m_spacing, inner[j]);
2005 for (int j = 0; j < 4; j++) outer[j] = (float)getClampedRoundedTessLevel(m_spacing, outer[j]);
2007 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
2009 if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f)
2011 if (inner[0] == 1.0f)
2012 inner[0] = (float)getClampedRoundedTessLevel(m_spacing, inner[0] + 0.1f);
2015 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
2017 if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f || outer[3] > 1.0f)
2019 if (inner[0] == 1.0f) inner[0] = (float)getClampedRoundedTessLevel(m_spacing, inner[0] + 0.1f);
2020 if (inner[1] == 1.0f) inner[1] = (float)getClampedRoundedTessLevel(m_spacing, inner[1] + 0.1f);
2024 result.push_back(curTessLevelCase);
2027 DE_ASSERT((int)result.size() == DE_LENGTH_OF_ARRAY(rawTessLevelCases));
2032 TessCoordCase::IterateResult TessCoordCase::iterate (void)
2034 typedef TransformFeedbackHandler<Vec3> TFHandler;
2036 TestLog& log = m_testCtx.getLog();
2037 const RenderContext& renderCtx = m_context.getRenderContext();
2038 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2039 const deUint32 programGL = m_program->getProgram();
2040 const glw::Functions& gl = renderCtx.getFunctions();
2042 const int tessLevelInner0Loc = gl.getUniformLocation(programGL, "u_tessLevelInner0");
2043 const int tessLevelInner1Loc = gl.getUniformLocation(programGL, "u_tessLevelInner1");
2044 const int tessLevelOuter0Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter0");
2045 const int tessLevelOuter1Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter1");
2046 const int tessLevelOuter2Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter2");
2047 const int tessLevelOuter3Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter3");
2049 const vector<TessLevels> tessLevelCases = genTessLevelCases();
2050 vector<vector<Vec3> > caseReferences (tessLevelCases.size());
2052 for (int i = 0; i < (int)tessLevelCases.size(); i++)
2053 caseReferences[i] = generateReferenceTessCoords(m_primitiveType, m_spacing, &tessLevelCases[i].inner[0], &tessLevelCases[i].outer[0]);
2055 const int maxNumVertices = (int)std::max_element(caseReferences.begin(), caseReferences.end(), SizeLessThan<vector<Vec3> >())->size();
2056 const TFHandler tfHandler (m_context.getRenderContext(), maxNumVertices);
2058 bool success = true;
2060 setViewport(gl, viewport);
2061 gl.useProgram(programGL);
2063 gl.patchParameteri(GL_PATCH_VERTICES, 1);
2065 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < (int)tessLevelCases.size(); tessLevelCaseNdx++)
2067 const float* const innerLevels = &tessLevelCases[tessLevelCaseNdx].inner[0];
2068 const float* const outerLevels = &tessLevelCases[tessLevelCaseNdx].outer[0];
2070 log << TestLog::Message << "Tessellation levels: " << tessellationLevelsString(innerLevels, outerLevels, m_primitiveType) << TestLog::EndMessage;
2072 gl.uniform1f(tessLevelInner0Loc, innerLevels[0]);
2073 gl.uniform1f(tessLevelInner1Loc, innerLevels[1]);
2074 gl.uniform1f(tessLevelOuter0Loc, outerLevels[0]);
2075 gl.uniform1f(tessLevelOuter1Loc, outerLevels[1]);
2076 gl.uniform1f(tessLevelOuter2Loc, outerLevels[2]);
2077 gl.uniform1f(tessLevelOuter3Loc, outerLevels[3]);
2078 GLU_EXPECT_NO_ERROR(gl.getError(), "Setup failed");
2081 const vector<Vec3>& tessCoordsRef = caseReferences[tessLevelCaseNdx];
2082 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, GL_POINTS, 0, DE_NULL, 1);
2084 if (tfResult.numPrimitives != (int)tessCoordsRef.size())
2086 log << TestLog::Message << "Failure: GL reported GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN to be "
2087 << tfResult.numPrimitives << ", reference value is " << tessCoordsRef.size()
2088 << " (logging further info anyway)" << TestLog::EndMessage;
2092 log << TestLog::Message << "Note: GL reported GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN to be " << tfResult.numPrimitives << TestLog::EndMessage;
2094 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
2095 log << TestLog::Message << "Note: in the following visualization(s), the u=1, v=1, w=1 corners are at the right, top, and left corners, respectively" << TestLog::EndMessage;
2096 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS || m_primitiveType == TESSPRIMITIVETYPE_ISOLINES)
2097 log << TestLog::Message << "Note: in the following visualization(s), u and v coordinate go left-to-right and bottom-to-top, respectively" << TestLog::EndMessage;
2101 success = compareTessCoords(log, m_primitiveType, tessCoordsRef, tfResult.varying) && success;
2107 log << TestLog::Message << "All OK" << TestLog::EndMessage;
2110 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Invalid tessellation coordinates");
2114 // Check validity of fractional spacing modes. Draws a single isoline, reads tesscoords with transform feedback.
2115 class FractionalSpacingModeCase : public TestCase
2118 FractionalSpacingModeCase (Context& context, const char* name, const char* description, SpacingMode spacing)
2119 : TestCase (context, name, description)
2120 , m_spacing (spacing)
2122 DE_ASSERT(m_spacing == SPACINGMODE_FRACTIONAL_EVEN || m_spacing == SPACINGMODE_FRACTIONAL_ODD);
2127 IterateResult iterate (void);
2130 static const int RENDER_SIZE = 16;
2132 static vector<float> genTessLevelCases (void);
2134 const SpacingMode m_spacing;
2136 SharedPtr<const ShaderProgram> m_program;
2139 void FractionalSpacingModeCase::init (void)
2141 checkTessellationSupport(m_context);
2142 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
2144 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
2146 "void main (void)\n"
2149 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
2150 "${TESSELLATION_SHADER_REQUIRE}\n"
2152 "layout (vertices = 1) out;\n"
2154 "uniform mediump float u_tessLevelOuter1;\n"
2156 "void main (void)\n"
2158 " gl_TessLevelOuter[0] = 1.0;\n"
2159 " gl_TessLevelOuter[1] = u_tessLevelOuter1;\n"
2161 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
2162 "${TESSELLATION_SHADER_REQUIRE}\n"
2164 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_ISOLINES, m_spacing, true) +
2166 "out highp float out_te_tessCoord;\n"
2168 "void main (void)\n"
2170 " out_te_tessCoord = gl_TessCoord.x;\n"
2171 " gl_Position = vec4(gl_TessCoord.xy*1.6 - 0.8, 0.0, 1.0);\n"
2173 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
2175 "layout (location = 0) out mediump vec4 o_color;\n"
2177 "void main (void)\n"
2179 " o_color = vec4(1.0);\n"
2182 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
2183 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
2184 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
2185 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
2186 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
2187 << glu::TransformFeedbackVarying ("out_te_tessCoord")
2188 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
2190 m_testCtx.getLog() << *m_program;
2191 if (!m_program->isOk())
2192 TCU_FAIL("Program compilation failed");
2195 void FractionalSpacingModeCase::deinit (void)
2200 vector<float> FractionalSpacingModeCase::genTessLevelCases (void)
2202 vector<float> result;
2204 // Ranges [7.0 .. 8.0), [8.0 .. 9.0) and [9.0 .. 10.0)
2206 static const float rangeStarts[] = { 7.0f, 8.0f, 9.0f };
2207 const int numSamplesPerRange = 10;
2209 for (int rangeNdx = 0; rangeNdx < DE_LENGTH_OF_ARRAY(rangeStarts); rangeNdx++)
2210 for (int i = 0; i < numSamplesPerRange; i++)
2211 result.push_back(rangeStarts[rangeNdx] + (float)i/(float)numSamplesPerRange);
2214 // 0.3, 1.3, 2.3, ... , 62.3
2215 for (int i = 0; i <= 62; i++)
2216 result.push_back((float)i + 0.3f);
2221 FractionalSpacingModeCase::IterateResult FractionalSpacingModeCase::iterate (void)
2223 typedef TransformFeedbackHandler<float> TFHandler;
2225 TestLog& log = m_testCtx.getLog();
2226 const RenderContext& renderCtx = m_context.getRenderContext();
2227 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2228 const deUint32 programGL = m_program->getProgram();
2229 const glw::Functions& gl = renderCtx.getFunctions();
2231 const int tessLevelOuter1Loc = gl.getUniformLocation(programGL, "u_tessLevelOuter1");
2233 // Second outer tessellation levels.
2234 const vector<float> tessLevelCases = genTessLevelCases();
2235 const int maxNumVertices = 1 + getClampedRoundedTessLevel(m_spacing, *std::max_element(tessLevelCases.begin(), tessLevelCases.end()));
2236 vector<float> additionalSegmentLengths;
2237 vector<int> additionalSegmentLocations;
2239 const TFHandler tfHandler (m_context.getRenderContext(), maxNumVertices);
2241 bool success = true;
2243 setViewport(gl, viewport);
2244 gl.useProgram(programGL);
2246 gl.patchParameteri(GL_PATCH_VERTICES, 1);
2248 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < (int)tessLevelCases.size(); tessLevelCaseNdx++)
2250 const float outerLevel1 = tessLevelCases[tessLevelCaseNdx];
2252 gl.uniform1f(tessLevelOuter1Loc, outerLevel1);
2253 GLU_EXPECT_NO_ERROR(gl.getError(), "Setup failed");
2256 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, GL_POINTS, 0, DE_NULL, 1);
2257 float additionalSegmentLength;
2258 int additionalSegmentLocation;
2260 success = verifyFractionalSpacingSingle(log, m_spacing, outerLevel1, tfResult.varying,
2261 additionalSegmentLength, additionalSegmentLocation);
2266 additionalSegmentLengths.push_back(additionalSegmentLength);
2267 additionalSegmentLocations.push_back(additionalSegmentLocation);
2272 success = verifyFractionalSpacingMultiple(log, m_spacing, tessLevelCases, additionalSegmentLengths, additionalSegmentLocations);
2274 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Invalid tessellation coordinates");
2278 // Base class for a case with one input attribute (in_v_position) and optionally a TCS; tests with a couple of different sets of tessellation levels.
2279 class BasicVariousTessLevelsPosAttrCase : public TestCase
2282 BasicVariousTessLevelsPosAttrCase (Context& context,
2284 const char* description,
2285 TessPrimitiveType primitiveType,
2286 SpacingMode spacing,
2287 const char* referenceImagePathPrefix)
2288 : TestCase (context, name, description)
2289 , m_primitiveType (primitiveType)
2290 , m_spacing (spacing)
2291 , m_referenceImagePathPrefix (referenceImagePathPrefix)
2297 IterateResult iterate (void);
2300 virtual const glu::ProgramSources makeSources (TessPrimitiveType, SpacingMode, const char* vtxOutPosAttrName) const = DE_NULL;
2303 static const int RENDER_SIZE = 256;
2305 const TessPrimitiveType m_primitiveType;
2306 const SpacingMode m_spacing;
2307 const string m_referenceImagePathPrefix;
2309 SharedPtr<const ShaderProgram> m_program;
2312 void BasicVariousTessLevelsPosAttrCase::init (void)
2314 checkTessellationSupport(m_context);
2315 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
2318 glu::ProgramSources sources = makeSources(m_primitiveType, m_spacing, "in_tc_position");
2319 DE_ASSERT(sources.sources[glu::SHADERTYPE_TESSELLATION_CONTROL].empty());
2321 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
2322 "${TESSELLATION_SHADER_REQUIRE}\n"
2324 "layout (vertices = " + string(m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "3" : "4") + ") out;\n"
2326 "in highp vec2 in_tc_position[];\n"
2328 "out highp vec2 in_te_position[];\n"
2330 "uniform mediump float u_tessLevelInner0;\n"
2331 "uniform mediump float u_tessLevelInner1;\n"
2332 "uniform mediump float u_tessLevelOuter0;\n"
2333 "uniform mediump float u_tessLevelOuter1;\n"
2334 "uniform mediump float u_tessLevelOuter2;\n"
2335 "uniform mediump float u_tessLevelOuter3;\n"
2337 "void main (void)\n"
2339 " in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];\n"
2341 " gl_TessLevelInner[0] = u_tessLevelInner0;\n"
2342 " gl_TessLevelInner[1] = u_tessLevelInner1;\n"
2344 " gl_TessLevelOuter[0] = u_tessLevelOuter0;\n"
2345 " gl_TessLevelOuter[1] = u_tessLevelOuter1;\n"
2346 " gl_TessLevelOuter[2] = u_tessLevelOuter2;\n"
2347 " gl_TessLevelOuter[3] = u_tessLevelOuter3;\n"
2350 sources << glu::TessellationControlSource(specializeShader(m_context, tessellationControlTemplate.c_str()));
2352 m_program = SharedPtr<const ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources));
2355 m_testCtx.getLog() << *m_program;
2356 if (!m_program->isOk())
2357 TCU_FAIL("Program compilation failed");
2360 void BasicVariousTessLevelsPosAttrCase::deinit (void)
2365 BasicVariousTessLevelsPosAttrCase::IterateResult BasicVariousTessLevelsPosAttrCase::iterate (void)
2371 } tessLevelCases[] =
2373 { { 9.0f, 9.0f }, { 9.0f, 9.0f, 9.0f, 9.0f } },
2374 { { 8.0f, 11.0f }, { 13.0f, 15.0f, 18.0f, 21.0f } },
2375 { { 17.0f, 14.0f }, { 3.0f, 6.0f, 9.0f, 12.0f } }
2378 TestLog& log = m_testCtx.getLog();
2379 const RenderContext& renderCtx = m_context.getRenderContext();
2380 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2381 const deUint32 programGL = m_program->getProgram();
2382 const glw::Functions& gl = renderCtx.getFunctions();
2383 const int patchSize = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3
2384 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4
2385 : m_primitiveType == TESSPRIMITIVETYPE_ISOLINES ? 4
2388 setViewport(gl, viewport);
2389 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
2390 gl.useProgram(programGL);
2392 gl.patchParameteri(GL_PATCH_VERTICES, patchSize);
2394 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(tessLevelCases); tessLevelCaseNdx++)
2396 float innerLevels[2];
2397 float outerLevels[4];
2399 for (int i = 0; i < DE_LENGTH_OF_ARRAY(innerLevels); i++)
2400 innerLevels[i] = (float)getClampedRoundedTessLevel(m_spacing, tessLevelCases[tessLevelCaseNdx].inner[i]);
2402 for (int i = 0; i < DE_LENGTH_OF_ARRAY(outerLevels); i++)
2403 outerLevels[i] = (float)getClampedRoundedTessLevel(m_spacing, tessLevelCases[tessLevelCaseNdx].outer[i]);
2405 log << TestLog::Message << "Tessellation levels: " << tessellationLevelsString(&innerLevels[0], &outerLevels[0], m_primitiveType) << TestLog::EndMessage;
2407 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelInner0"), innerLevels[0]);
2408 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelInner1"), innerLevels[1]);
2409 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter0"), outerLevels[0]);
2410 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter1"), outerLevels[1]);
2411 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter2"), outerLevels[2]);
2412 gl.uniform1f(gl.getUniformLocation(programGL, "u_tessLevelOuter3"), outerLevels[3]);
2414 gl.clear(GL_COLOR_BUFFER_BIT);
2417 vector<Vec2> positions;
2418 positions.reserve(4);
2420 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
2422 positions.push_back(Vec2( 0.8f, 0.6f));
2423 positions.push_back(Vec2( 0.0f, -0.786f));
2424 positions.push_back(Vec2(-0.8f, 0.6f));
2426 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS || m_primitiveType == TESSPRIMITIVETYPE_ISOLINES)
2428 positions.push_back(Vec2(-0.8f, -0.8f));
2429 positions.push_back(Vec2( 0.8f, -0.8f));
2430 positions.push_back(Vec2(-0.8f, 0.8f));
2431 positions.push_back(Vec2( 0.8f, 0.8f));
2436 DE_ASSERT((int)positions.size() == patchSize);
2438 const glu::VertexArrayBinding attrBindings[] =
2440 glu::va::Float("in_v_position", 2, (int)positions.size(), 0, &positions[0].x())
2443 glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
2444 glu::pr::Patches(patchSize));
2445 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
2449 const tcu::Surface rendered = getPixels(renderCtx, viewport);
2450 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePathPrefix + "_" + de::toString(tessLevelCaseNdx) + ".png");
2451 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.002f, tcu::COMPARE_LOG_RESULT);
2455 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
2461 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2465 // Test that there are no obvious gaps in the triangulation of a tessellated triangle or quad.
2466 class BasicTriangleFillCoverCase : public BasicVariousTessLevelsPosAttrCase
2469 BasicTriangleFillCoverCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing, const char* referenceImagePathPrefix)
2470 : BasicVariousTessLevelsPosAttrCase (context, name, description, primitiveType, spacing, referenceImagePathPrefix)
2472 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
2478 checkGPUShader5Support(m_context);
2479 BasicVariousTessLevelsPosAttrCase::init();
2482 const glu::ProgramSources makeSources (TessPrimitiveType primitiveType, SpacingMode spacing, const char* vtxOutPosAttrName) const
2484 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
2486 "in highp vec2 in_v_position;\n"
2488 "out highp vec2 " + string(vtxOutPosAttrName) + ";\n"
2490 "void main (void)\n"
2492 " " + vtxOutPosAttrName + " = in_v_position;\n"
2494 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
2495 "${TESSELLATION_SHADER_REQUIRE}\n"
2496 "${GPU_SHADER5_REQUIRE}\n"
2498 + getTessellationEvaluationInLayoutString(primitiveType, spacing) +
2500 "in highp vec2 in_te_position[];\n"
2502 "precise gl_Position;\n"
2503 "void main (void)\n"
2505 + (primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
2507 " highp float d = 3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z));\n"
2508 " highp vec2 corner0 = in_te_position[0];\n"
2509 " highp vec2 corner1 = in_te_position[1];\n"
2510 " highp vec2 corner2 = in_te_position[2];\n"
2511 " highp vec2 pos = corner0*gl_TessCoord.x + corner1*gl_TessCoord.y + corner2*gl_TessCoord.z;\n"
2512 " highp vec2 fromCenter = pos - (corner0 + corner1 + corner2) / 3.0;\n"
2513 " highp float f = (1.0 - length(fromCenter)) * (1.5 - d);\n"
2514 " pos += 0.75 * f * fromCenter / (length(fromCenter) + 0.3);\n"
2515 " gl_Position = vec4(pos, 0.0, 1.0);\n"
2516 : primitiveType == TESSPRIMITIVETYPE_QUADS ?
2517 " highp vec2 corner0 = in_te_position[0];\n"
2518 " highp vec2 corner1 = in_te_position[1];\n"
2519 " highp vec2 corner2 = in_te_position[2];\n"
2520 " highp vec2 corner3 = in_te_position[3];\n"
2521 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
2522 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
2523 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n"
2524 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n"
2525 " highp float d = 2.0 * min(abs(gl_TessCoord.x-0.5), abs(gl_TessCoord.y-0.5));\n"
2526 " highp vec2 fromCenter = pos - (corner0 + corner1 + corner2 + corner3) / 4.0;\n"
2527 " highp float f = (1.0 - length(fromCenter)) * sqrt(1.7 - d);\n"
2528 " pos += 0.75 * f * fromCenter / (length(fromCenter) + 0.3);\n"
2529 " gl_Position = vec4(pos, 0.0, 1.0);\n"
2532 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
2534 "layout (location = 0) out mediump vec4 o_color;\n"
2536 "void main (void)\n"
2538 " o_color = vec4(1.0);\n"
2541 return glu::ProgramSources()
2542 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
2543 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
2544 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()));
2548 // Check that there are no obvious overlaps in the triangulation of a tessellated triangle or quad.
2549 class BasicTriangleFillNonOverlapCase : public BasicVariousTessLevelsPosAttrCase
2552 BasicTriangleFillNonOverlapCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, SpacingMode spacing, const char* referenceImagePathPrefix)
2553 : BasicVariousTessLevelsPosAttrCase (context, name, description, primitiveType, spacing, referenceImagePathPrefix)
2555 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
2559 const glu::ProgramSources makeSources (TessPrimitiveType primitiveType, SpacingMode spacing, const char* vtxOutPosAttrName) const
2561 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
2563 "in highp vec2 in_v_position;\n"
2565 "out highp vec2 " + string(vtxOutPosAttrName) + ";\n"
2567 "void main (void)\n"
2569 " " + vtxOutPosAttrName + " = in_v_position;\n"
2571 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
2572 "${TESSELLATION_SHADER_REQUIRE}\n"
2574 + getTessellationEvaluationInLayoutString(primitiveType, spacing) +
2576 "in highp vec2 in_te_position[];\n"
2578 "out mediump vec4 in_f_color;\n"
2580 "uniform mediump float u_tessLevelInner0;\n"
2581 "uniform mediump float u_tessLevelInner1;\n"
2583 "void main (void)\n"
2585 + (primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
2587 " highp vec2 corner0 = in_te_position[0];\n"
2588 " highp vec2 corner1 = in_te_position[1];\n"
2589 " highp vec2 corner2 = in_te_position[2];\n"
2590 " highp vec2 pos = corner0*gl_TessCoord.x + corner1*gl_TessCoord.y + corner2*gl_TessCoord.z;\n"
2591 " gl_Position = vec4(pos, 0.0, 1.0);\n"
2592 " highp int numConcentricTriangles = int(round(u_tessLevelInner0)) / 2 + 1;\n"
2593 " highp float d = 3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z));\n"
2594 " highp int phase = int(d*float(numConcentricTriangles)) % 3;\n"
2595 " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
2596 " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
2597 " : vec4(0.0, 0.0, 1.0, 1.0);\n"
2598 : primitiveType == TESSPRIMITIVETYPE_QUADS ?
2599 " highp vec2 corner0 = in_te_position[0];\n"
2600 " highp vec2 corner1 = in_te_position[1];\n"
2601 " highp vec2 corner2 = in_te_position[2];\n"
2602 " highp vec2 corner3 = in_te_position[3];\n"
2603 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
2604 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
2605 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n"
2606 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n"
2607 " gl_Position = vec4(pos, 0.0, 1.0);\n"
2608 " highp int phaseX = int(round((0.5 - abs(gl_TessCoord.x-0.5)) * u_tessLevelInner0));\n"
2609 " highp int phaseY = int(round((0.5 - abs(gl_TessCoord.y-0.5)) * u_tessLevelInner1));\n"
2610 " highp int phase = min(phaseX, phaseY) % 3;\n"
2611 " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
2612 " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
2613 " : vec4(0.0, 0.0, 1.0, 1.0);\n"
2616 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
2618 "layout (location = 0) out mediump vec4 o_color;\n"
2620 "in mediump vec4 in_f_color;\n"
2622 "void main (void)\n"
2624 " o_color = in_f_color;\n"
2627 return glu::ProgramSources()
2628 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
2629 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
2630 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()));
2634 // Basic isolines rendering case.
2635 class IsolinesRenderCase : public BasicVariousTessLevelsPosAttrCase
2638 IsolinesRenderCase (Context& context, const char* name, const char* description, SpacingMode spacing, const char* referenceImagePathPrefix)
2639 : BasicVariousTessLevelsPosAttrCase (context, name, description, TESSPRIMITIVETYPE_ISOLINES, spacing, referenceImagePathPrefix)
2644 const glu::ProgramSources makeSources (TessPrimitiveType primitiveType, SpacingMode spacing, const char* vtxOutPosAttrName) const
2646 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_ISOLINES);
2647 DE_UNREF(primitiveType);
2649 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
2651 "in highp vec2 in_v_position;\n"
2653 "out highp vec2 " + string(vtxOutPosAttrName) + ";\n"
2655 "void main (void)\n"
2657 " " + vtxOutPosAttrName + " = in_v_position;\n"
2659 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
2660 "${TESSELLATION_SHADER_REQUIRE}\n"
2662 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_ISOLINES, spacing) +
2664 "in highp vec2 in_te_position[];\n"
2666 "out mediump vec4 in_f_color;\n"
2668 "uniform mediump float u_tessLevelOuter0;\n"
2669 "uniform mediump float u_tessLevelOuter1;\n"
2671 "void main (void)\n"
2673 " highp vec2 corner0 = in_te_position[0];\n"
2674 " highp vec2 corner1 = in_te_position[1];\n"
2675 " highp vec2 corner2 = in_te_position[2];\n"
2676 " highp vec2 corner3 = in_te_position[3];\n"
2677 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
2678 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
2679 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n"
2680 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n"
2681 " pos.y += 0.15*sin(gl_TessCoord.x*10.0);\n"
2682 " gl_Position = vec4(pos, 0.0, 1.0);\n"
2683 " highp int phaseX = int(round(gl_TessCoord.x*u_tessLevelOuter1));\n"
2684 " highp int phaseY = int(round(gl_TessCoord.y*u_tessLevelOuter0));\n"
2685 " highp int phase = (phaseX + phaseY) % 3;\n"
2686 " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
2687 " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
2688 " : vec4(0.0, 0.0, 1.0, 1.0);\n"
2690 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
2692 "layout (location = 0) out mediump vec4 o_color;\n"
2694 "in mediump vec4 in_f_color;\n"
2696 "void main (void)\n"
2698 " o_color = in_f_color;\n"
2701 return glu::ProgramSources()
2702 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
2703 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
2704 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()));
2708 // Test the "cw" and "ccw" TES input layout qualifiers.
2709 class WindingCase : public TestCase
2712 WindingCase (Context& context, const char* name, const char* description, TessPrimitiveType primitiveType, Winding winding)
2713 : TestCase (context, name, description)
2714 , m_primitiveType (primitiveType)
2715 , m_winding (winding)
2717 DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
2722 IterateResult iterate (void);
2725 static const int RENDER_SIZE = 64;
2727 const TessPrimitiveType m_primitiveType;
2728 const Winding m_winding;
2730 SharedPtr<const ShaderProgram> m_program;
2733 void WindingCase::init (void)
2735 checkTessellationSupport(m_context);
2736 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
2738 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
2740 "void main (void)\n"
2743 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
2744 "${TESSELLATION_SHADER_REQUIRE}\n"
2746 "layout (vertices = 1) out;\n"
2748 "void main (void)\n"
2750 " gl_TessLevelInner[0] = 5.0;\n"
2751 " gl_TessLevelInner[1] = 5.0;\n"
2753 " gl_TessLevelOuter[0] = 5.0;\n"
2754 " gl_TessLevelOuter[1] = 5.0;\n"
2755 " gl_TessLevelOuter[2] = 5.0;\n"
2756 " gl_TessLevelOuter[3] = 5.0;\n"
2758 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
2759 "${TESSELLATION_SHADER_REQUIRE}\n"
2761 + getTessellationEvaluationInLayoutString(m_primitiveType, m_winding) +
2763 "void main (void)\n"
2765 " gl_Position = vec4(gl_TessCoord.xy*2.0 - 1.0, 0.0, 1.0);\n"
2767 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
2769 "layout (location = 0) out mediump vec4 o_color;\n"
2771 "void main (void)\n"
2773 " o_color = vec4(1.0);\n"
2776 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
2777 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
2778 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
2779 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
2780 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))));
2782 m_testCtx.getLog() << *m_program;
2783 if (!m_program->isOk())
2784 TCU_FAIL("Program compilation failed");
2787 void WindingCase::deinit (void)
2792 WindingCase::IterateResult WindingCase::iterate (void)
2794 TestLog& log = m_testCtx.getLog();
2795 const RenderContext& renderCtx = m_context.getRenderContext();
2796 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
2797 const deUint32 programGL = m_program->getProgram();
2798 const glw::Functions& gl = renderCtx.getFunctions();
2799 const glu::VertexArray vao (renderCtx);
2801 bool success = true;
2803 setViewport(gl, viewport);
2804 gl.clearColor(1.0f, 0.0f, 0.0f, 1.0f);
2805 gl.useProgram(programGL);
2807 gl.patchParameteri(GL_PATCH_VERTICES, 1);
2809 gl.enable(GL_CULL_FACE);
2811 gl.bindVertexArray(*vao);
2813 log << TestLog::Message << "Face culling enabled" << TestLog::EndMessage;
2815 for (int frontFaceWinding = 0; frontFaceWinding < WINDING_LAST; frontFaceWinding++)
2817 log << TestLog::Message << "Setting glFrontFace(" << (frontFaceWinding == WINDING_CW ? "GL_CW" : "GL_CCW") << ")" << TestLog::EndMessage;
2819 gl.frontFace(frontFaceWinding == WINDING_CW ? GL_CW : GL_CCW);
2821 gl.clear(GL_COLOR_BUFFER_BIT);
2822 gl.drawArrays(GL_PATCHES, 0, 1);
2823 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
2826 const tcu::Surface rendered = getPixels(renderCtx, viewport);
2827 log << TestLog::Image("RenderedImage", "Rendered Image", rendered);
2830 const int totalNumPixels = rendered.getWidth()*rendered.getHeight();
2831 const int badPixelTolerance = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 5*de::max(rendered.getWidth(), rendered.getHeight()) : 0;
2833 int numWhitePixels = 0;
2834 int numRedPixels = 0;
2835 for (int y = 0; y < rendered.getHeight(); y++)
2836 for (int x = 0; x < rendered.getWidth(); x++)
2838 numWhitePixels += rendered.getPixel(x, y) == tcu::RGBA::white() ? 1 : 0;
2839 numRedPixels += rendered.getPixel(x, y) == tcu::RGBA::red() ? 1 : 0;
2842 DE_ASSERT(numWhitePixels + numRedPixels <= totalNumPixels);
2844 log << TestLog::Message << "Note: got " << numWhitePixels << " white and " << numRedPixels << " red pixels" << TestLog::EndMessage;
2846 if (totalNumPixels - numWhitePixels - numRedPixels > badPixelTolerance)
2848 log << TestLog::Message << "Failure: Got " << totalNumPixels - numWhitePixels - numRedPixels << " other than white or red pixels (maximum tolerance " << badPixelTolerance << ")" << TestLog::EndMessage;
2853 if ((Winding)frontFaceWinding == m_winding)
2855 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
2857 if (de::abs(numWhitePixels - totalNumPixels/2) > badPixelTolerance)
2859 log << TestLog::Message << "Failure: wrong number of white pixels; expected approximately " << totalNumPixels/2 << TestLog::EndMessage;
2864 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
2866 if (numWhitePixels != totalNumPixels)
2868 log << TestLog::Message << "Failure: expected only white pixels (full-viewport quad)" << TestLog::EndMessage;
2878 if (numWhitePixels != 0)
2880 log << TestLog::Message << "Failure: expected only red pixels (everything culled)" << TestLog::EndMessage;
2889 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image verification failed");
2893 // Test potentially differing input and output patch sizes.
2894 class PatchVertexCountCase : public TestCase
2897 PatchVertexCountCase (Context& context, const char* name, const char* description, int inputPatchSize, int outputPatchSize, const char* referenceImagePath)
2898 : TestCase (context, name, description)
2899 , m_inputPatchSize (inputPatchSize)
2900 , m_outputPatchSize (outputPatchSize)
2901 , m_referenceImagePath (referenceImagePath)
2907 IterateResult iterate (void);
2910 static const int RENDER_SIZE = 256;
2912 const int m_inputPatchSize;
2913 const int m_outputPatchSize;
2915 const string m_referenceImagePath;
2917 SharedPtr<const ShaderProgram> m_program;
2920 void PatchVertexCountCase::init (void)
2922 checkTessellationSupport(m_context);
2923 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
2925 const string inSizeStr = de::toString(m_inputPatchSize);
2926 const string outSizeStr = de::toString(m_outputPatchSize);
2928 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
2930 "in highp float in_v_attr;\n"
2932 "out highp float in_tc_attr;\n"
2934 "void main (void)\n"
2936 " in_tc_attr = in_v_attr;\n"
2938 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
2939 "${TESSELLATION_SHADER_REQUIRE}\n"
2941 "layout (vertices = " + outSizeStr + ") out;\n"
2943 "in highp float in_tc_attr[];\n"
2945 "out highp float in_te_attr[];\n"
2947 "void main (void)\n"
2949 " in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID*" + inSizeStr + "/" + outSizeStr + "];\n"
2951 " gl_TessLevelInner[0] = 5.0;\n"
2952 " gl_TessLevelInner[1] = 5.0;\n"
2954 " gl_TessLevelOuter[0] = 5.0;\n"
2955 " gl_TessLevelOuter[1] = 5.0;\n"
2956 " gl_TessLevelOuter[2] = 5.0;\n"
2957 " gl_TessLevelOuter[3] = 5.0;\n"
2959 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
2960 "${TESSELLATION_SHADER_REQUIRE}\n"
2962 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_QUADS) +
2964 "in highp float in_te_attr[];\n"
2966 "out mediump vec4 in_f_color;\n"
2968 "void main (void)\n"
2970 " highp float x = gl_TessCoord.x*2.0 - 1.0;\n"
2971 " highp float y = gl_TessCoord.y - in_te_attr[int(round(gl_TessCoord.x*float(" + outSizeStr + "-1)))];\n"
2972 " gl_Position = vec4(x, y, 0.0, 1.0);\n"
2973 " in_f_color = vec4(1.0);\n"
2975 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
2977 "layout (location = 0) out mediump vec4 o_color;\n"
2979 "in mediump vec4 in_f_color;\n"
2981 "void main (void)\n"
2983 " o_color = in_f_color;\n"
2986 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
2987 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
2988 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
2989 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
2990 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))));
2992 m_testCtx.getLog() << *m_program;
2993 if (!m_program->isOk())
2994 TCU_FAIL("Program compilation failed");
2997 void PatchVertexCountCase::deinit (void)
3002 PatchVertexCountCase::IterateResult PatchVertexCountCase::iterate (void)
3004 TestLog& log = m_testCtx.getLog();
3005 const RenderContext& renderCtx = m_context.getRenderContext();
3006 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
3007 const deUint32 programGL = m_program->getProgram();
3008 const glw::Functions& gl = renderCtx.getFunctions();
3010 setViewport(gl, viewport);
3011 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3012 gl.useProgram(programGL);
3014 log << TestLog::Message << "Note: input patch size is " << m_inputPatchSize << ", output patch size is " << m_outputPatchSize << TestLog::EndMessage;
3017 vector<float> attributeData;
3018 attributeData.reserve(m_inputPatchSize);
3020 for (int i = 0; i < m_inputPatchSize; i++)
3022 const float f = (float)i / (float)(m_inputPatchSize-1);
3023 attributeData.push_back(f*f);
3026 gl.patchParameteri(GL_PATCH_VERTICES, m_inputPatchSize);
3027 gl.clear(GL_COLOR_BUFFER_BIT);
3029 const glu::VertexArrayBinding attrBindings[] =
3031 glu::va::Float("in_v_attr", 1, (int)attributeData.size(), 0, &attributeData[0])
3034 glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
3035 glu::pr::Patches(m_inputPatchSize));
3036 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
3040 const tcu::Surface rendered = getPixels(renderCtx, viewport);
3041 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath);
3042 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
3044 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image comparison failed");
3049 // Test per-patch inputs/outputs.
3050 class PerPatchDataCase : public TestCase
3055 CASETYPE_PRIMITIVE_ID_TCS = 0,
3056 CASETYPE_PRIMITIVE_ID_TES,
3057 CASETYPE_PATCH_VERTICES_IN_TCS,
3058 CASETYPE_PATCH_VERTICES_IN_TES,
3059 CASETYPE_TESS_LEVEL_INNER0_TES,
3060 CASETYPE_TESS_LEVEL_INNER1_TES,
3061 CASETYPE_TESS_LEVEL_OUTER0_TES,
3062 CASETYPE_TESS_LEVEL_OUTER1_TES,
3063 CASETYPE_TESS_LEVEL_OUTER2_TES,
3064 CASETYPE_TESS_LEVEL_OUTER3_TES,
3069 PerPatchDataCase (Context& context, const char* name, const char* description, CaseType caseType, const char* referenceImagePath)
3070 : TestCase (context, name, description)
3071 , m_caseType (caseType)
3072 , m_referenceImagePath (caseTypeUsesRefImageFromFile(caseType) ? referenceImagePath : "")
3074 DE_ASSERT(caseTypeUsesRefImageFromFile(caseType) == (referenceImagePath != DE_NULL));
3079 IterateResult iterate (void);
3081 static const char* getCaseTypeName (CaseType);
3082 static const char* getCaseTypeDescription (CaseType);
3083 static bool caseTypeUsesRefImageFromFile (CaseType);
3086 static const int RENDER_SIZE = 256;
3087 static const int INPUT_PATCH_SIZE;
3088 static const int OUTPUT_PATCH_SIZE;
3090 const CaseType m_caseType;
3091 const string m_referenceImagePath;
3093 SharedPtr<const ShaderProgram> m_program;
3096 const int PerPatchDataCase::INPUT_PATCH_SIZE = 10;
3097 const int PerPatchDataCase::OUTPUT_PATCH_SIZE = 5;
3099 const char* PerPatchDataCase::getCaseTypeName (CaseType type)
3103 case CASETYPE_PRIMITIVE_ID_TCS: return "primitive_id_tcs";
3104 case CASETYPE_PRIMITIVE_ID_TES: return "primitive_id_tes";
3105 case CASETYPE_PATCH_VERTICES_IN_TCS: return "patch_vertices_in_tcs";
3106 case CASETYPE_PATCH_VERTICES_IN_TES: return "patch_vertices_in_tes";
3107 case CASETYPE_TESS_LEVEL_INNER0_TES: return "tess_level_inner_0_tes";
3108 case CASETYPE_TESS_LEVEL_INNER1_TES: return "tess_level_inner_1_tes";
3109 case CASETYPE_TESS_LEVEL_OUTER0_TES: return "tess_level_outer_0_tes";
3110 case CASETYPE_TESS_LEVEL_OUTER1_TES: return "tess_level_outer_1_tes";
3111 case CASETYPE_TESS_LEVEL_OUTER2_TES: return "tess_level_outer_2_tes";
3112 case CASETYPE_TESS_LEVEL_OUTER3_TES: return "tess_level_outer_3_tes";
3119 const char* PerPatchDataCase::getCaseTypeDescription (CaseType type)
3123 case CASETYPE_PRIMITIVE_ID_TCS: return "Read gl_PrimitiveID in TCS and pass it as patch output to TES";
3124 case CASETYPE_PRIMITIVE_ID_TES: return "Read gl_PrimitiveID in TES";
3125 case CASETYPE_PATCH_VERTICES_IN_TCS: return "Read gl_PatchVerticesIn in TCS and pass it as patch output to TES";
3126 case CASETYPE_PATCH_VERTICES_IN_TES: return "Read gl_PatchVerticesIn in TES";
3127 case CASETYPE_TESS_LEVEL_INNER0_TES: return "Read gl_TessLevelInner[0] in TES";
3128 case CASETYPE_TESS_LEVEL_INNER1_TES: return "Read gl_TessLevelInner[1] in TES";
3129 case CASETYPE_TESS_LEVEL_OUTER0_TES: return "Read gl_TessLevelOuter[0] in TES";
3130 case CASETYPE_TESS_LEVEL_OUTER1_TES: return "Read gl_TessLevelOuter[1] in TES";
3131 case CASETYPE_TESS_LEVEL_OUTER2_TES: return "Read gl_TessLevelOuter[2] in TES";
3132 case CASETYPE_TESS_LEVEL_OUTER3_TES: return "Read gl_TessLevelOuter[3] in TES";
3139 bool PerPatchDataCase::caseTypeUsesRefImageFromFile (CaseType type)
3143 case CASETYPE_PRIMITIVE_ID_TCS:
3144 case CASETYPE_PRIMITIVE_ID_TES:
3152 void PerPatchDataCase::init (void)
3154 checkTessellationSupport(m_context);
3155 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
3157 DE_ASSERT(OUTPUT_PATCH_SIZE < INPUT_PATCH_SIZE);
3159 const string inSizeStr = de::toString(INPUT_PATCH_SIZE);
3160 const string outSizeStr = de::toString(OUTPUT_PATCH_SIZE);
3162 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
3164 "in highp float in_v_attr;\n"
3166 "out highp float in_tc_attr;\n"
3168 "void main (void)\n"
3170 " in_tc_attr = in_v_attr;\n"
3172 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
3173 "${TESSELLATION_SHADER_REQUIRE}\n"
3175 "layout (vertices = " + outSizeStr + ") out;\n"
3177 "in highp float in_tc_attr[];\n"
3179 "out highp float in_te_attr[];\n"
3180 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS ? "patch out mediump int in_te_primitiveIDFromTCS;\n"
3181 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "patch out mediump int in_te_patchVerticesInFromTCS;\n"
3184 "void main (void)\n"
3186 " in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID];\n"
3187 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS ? "\tin_te_primitiveIDFromTCS = gl_PrimitiveID;\n"
3188 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "\tin_te_patchVerticesInFromTCS = gl_PatchVerticesIn;\n"
3191 " gl_TessLevelInner[0] = 9.0;\n"
3192 " gl_TessLevelInner[1] = 8.0;\n"
3194 " gl_TessLevelOuter[0] = 7.0;\n"
3195 " gl_TessLevelOuter[1] = 6.0;\n"
3196 " gl_TessLevelOuter[2] = 5.0;\n"
3197 " gl_TessLevelOuter[3] = 4.0;\n"
3199 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
3200 "${TESSELLATION_SHADER_REQUIRE}\n"
3202 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_QUADS) +
3204 "in highp float in_te_attr[];\n"
3205 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS ? "patch in mediump int in_te_primitiveIDFromTCS;\n"
3206 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "patch in mediump int in_te_patchVerticesInFromTCS;\n"
3209 "out mediump vec4 in_f_color;\n"
3211 "uniform highp float u_xScale;\n"
3213 "void main (void)\n"
3215 " highp float x = (gl_TessCoord.x*u_xScale + in_te_attr[0]) * 2.0 - 1.0;\n"
3216 " highp float y = gl_TessCoord.y*2.0 - 1.0;\n"
3217 " gl_Position = vec4(x, y, 0.0, 1.0);\n"
3218 + (m_caseType == CASETYPE_PRIMITIVE_ID_TCS ? "\tbool ok = in_te_primitiveIDFromTCS == 3;\n"
3219 : m_caseType == CASETYPE_PRIMITIVE_ID_TES ? "\tbool ok = gl_PrimitiveID == 3;\n"
3220 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "\tbool ok = in_te_patchVerticesInFromTCS == " + inSizeStr + ";\n"
3221 : m_caseType == CASETYPE_PATCH_VERTICES_IN_TES ? "\tbool ok = gl_PatchVerticesIn == " + outSizeStr + ";\n"
3222 : m_caseType == CASETYPE_TESS_LEVEL_INNER0_TES ? "\tbool ok = abs(gl_TessLevelInner[0] - 9.0) < 0.1f;\n"
3223 : m_caseType == CASETYPE_TESS_LEVEL_INNER1_TES ? "\tbool ok = abs(gl_TessLevelInner[1] - 8.0) < 0.1f;\n"
3224 : m_caseType == CASETYPE_TESS_LEVEL_OUTER0_TES ? "\tbool ok = abs(gl_TessLevelOuter[0] - 7.0) < 0.1f;\n"
3225 : m_caseType == CASETYPE_TESS_LEVEL_OUTER1_TES ? "\tbool ok = abs(gl_TessLevelOuter[1] - 6.0) < 0.1f;\n"
3226 : m_caseType == CASETYPE_TESS_LEVEL_OUTER2_TES ? "\tbool ok = abs(gl_TessLevelOuter[2] - 5.0) < 0.1f;\n"
3227 : m_caseType == CASETYPE_TESS_LEVEL_OUTER3_TES ? "\tbool ok = abs(gl_TessLevelOuter[3] - 4.0) < 0.1f;\n"
3229 " in_f_color = ok ? vec4(1.0) : vec4(vec3(0.0), 1.0);\n"
3231 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
3233 "layout (location = 0) out mediump vec4 o_color;\n"
3235 "in mediump vec4 in_f_color;\n"
3237 "void main (void)\n"
3239 " o_color = in_f_color;\n"
3242 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
3243 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
3244 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
3245 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
3246 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))));
3248 m_testCtx.getLog() << *m_program;
3249 if (!m_program->isOk())
3250 TCU_FAIL("Program compilation failed");
3253 void PerPatchDataCase::deinit (void)
3258 PerPatchDataCase::IterateResult PerPatchDataCase::iterate (void)
3260 TestLog& log = m_testCtx.getLog();
3261 const RenderContext& renderCtx = m_context.getRenderContext();
3262 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
3263 const deUint32 programGL = m_program->getProgram();
3264 const glw::Functions& gl = renderCtx.getFunctions();
3266 setViewport(gl, viewport);
3267 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3268 gl.useProgram(programGL);
3270 log << TestLog::Message << "Note: input patch size is " << INPUT_PATCH_SIZE << ", output patch size is " << OUTPUT_PATCH_SIZE << TestLog::EndMessage;
3273 const int numPrimitives = m_caseType == CASETYPE_PRIMITIVE_ID_TCS || m_caseType == CASETYPE_PRIMITIVE_ID_TES ? 8 : 1;
3275 vector<float> attributeData;
3276 attributeData.reserve(numPrimitives*INPUT_PATCH_SIZE);
3278 for (int i = 0; i < numPrimitives; i++)
3280 attributeData.push_back((float)i / (float)numPrimitives);
3281 for (int j = 0; j < INPUT_PATCH_SIZE-1; j++)
3282 attributeData.push_back(0.0f);
3285 gl.patchParameteri(GL_PATCH_VERTICES, INPUT_PATCH_SIZE);
3286 gl.clear(GL_COLOR_BUFFER_BIT);
3288 gl.uniform1f(gl.getUniformLocation(programGL, "u_xScale"), 1.0f / (float)numPrimitives);
3290 const glu::VertexArrayBinding attrBindings[] =
3292 glu::va::Float("in_v_attr", 1, (int)attributeData.size(), 0, &attributeData[0])
3295 glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
3296 glu::pr::Patches(numPrimitives*INPUT_PATCH_SIZE));
3297 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
3301 const tcu::Surface rendered = getPixels(renderCtx, viewport);
3303 if (m_caseType == CASETYPE_PRIMITIVE_ID_TCS || m_caseType == CASETYPE_PRIMITIVE_ID_TES)
3305 DE_ASSERT(caseTypeUsesRefImageFromFile(m_caseType));
3307 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath);
3308 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
3310 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image comparison failed");
3315 DE_ASSERT(!caseTypeUsesRefImageFromFile(m_caseType));
3317 log << TestLog::Image("RenderedImage", "Rendered Image", rendered);
3319 for (int y = 0; y < rendered.getHeight(); y++)
3320 for (int x = 0; x < rendered.getWidth(); x++)
3322 if (rendered.getPixel(x, y) != tcu::RGBA::white())
3324 log << TestLog::Message << "Failure: expected all white pixels" << TestLog::EndMessage;
3325 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
3330 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3336 // Basic barrier() usage in TCS.
3337 class BarrierCase : public TestCase
3340 BarrierCase (Context& context, const char* name, const char* description, const char* referenceImagePath)
3341 : TestCase (context, name, description)
3342 , m_referenceImagePath (referenceImagePath)
3348 IterateResult iterate (void);
3351 static const int RENDER_SIZE = 256;
3352 static const int NUM_VERTICES;
3354 const string m_referenceImagePath;
3356 SharedPtr<const ShaderProgram> m_program;
3359 const int BarrierCase::NUM_VERTICES = 32;
3361 void BarrierCase::init (void)
3363 checkTessellationSupport(m_context);
3364 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
3366 const string numVertsStr = de::toString(NUM_VERTICES);
3368 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
3370 "in highp float in_v_attr;\n"
3372 "out highp float in_tc_attr;\n"
3374 "void main (void)\n"
3376 " in_tc_attr = in_v_attr;\n"
3378 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
3379 "${TESSELLATION_SHADER_REQUIRE}\n"
3381 "layout (vertices = " + numVertsStr + ") out;\n"
3383 "in highp float in_tc_attr[];\n"
3385 "out highp float in_te_attr[];\n"
3386 "patch out highp float in_te_patchAttr;\n"
3388 "void main (void)\n"
3390 " in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID];\n"
3391 " in_te_patchAttr = 0.0f;\n"
3393 " if (gl_InvocationID == 5)\n"
3394 " in_te_patchAttr = float(gl_InvocationID)*0.1;\n"
3396 " highp float temp = in_te_patchAttr + in_te_attr[gl_InvocationID];\n"
3398 " if (gl_InvocationID == " + numVertsStr + "-1)\n"
3399 " in_te_patchAttr = float(gl_InvocationID);\n"
3401 " in_te_attr[gl_InvocationID] = temp;\n"
3403 " temp = temp + in_te_attr[(gl_InvocationID+1) % " + numVertsStr + "];\n"
3405 " in_te_attr[gl_InvocationID] = 0.25*temp;\n"
3407 " gl_TessLevelInner[0] = 32.0;\n"
3408 " gl_TessLevelInner[1] = 32.0;\n"
3410 " gl_TessLevelOuter[0] = 32.0;\n"
3411 " gl_TessLevelOuter[1] = 32.0;\n"
3412 " gl_TessLevelOuter[2] = 32.0;\n"
3413 " gl_TessLevelOuter[3] = 32.0;\n"
3415 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
3416 "${TESSELLATION_SHADER_REQUIRE}\n"
3418 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_QUADS) +
3420 "in highp float in_te_attr[];\n"
3421 "patch in highp float in_te_patchAttr;\n"
3423 "out highp float in_f_blue;\n"
3425 "void main (void)\n"
3427 " highp float x = gl_TessCoord.x*2.0 - 1.0;\n"
3428 " highp float y = gl_TessCoord.y - in_te_attr[int(round(gl_TessCoord.x*float(" + numVertsStr + "-1)))];\n"
3429 " gl_Position = vec4(x, y, 0.0, 1.0);\n"
3430 " in_f_blue = abs(in_te_patchAttr - float(" + numVertsStr + "-1));\n"
3432 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
3434 "layout (location = 0) out mediump vec4 o_color;\n"
3436 "in highp float in_f_blue;\n"
3438 "void main (void)\n"
3440 " o_color = vec4(1.0, 0.0, in_f_blue, 1.0);\n"
3443 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
3444 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
3445 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
3446 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
3447 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))));
3449 m_testCtx.getLog() << *m_program;
3450 if (!m_program->isOk())
3451 TCU_FAIL("Program compilation failed");
3454 void BarrierCase::deinit (void)
3459 BarrierCase::IterateResult BarrierCase::iterate (void)
3461 TestLog& log = m_testCtx.getLog();
3462 const RenderContext& renderCtx = m_context.getRenderContext();
3463 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
3464 const deUint32 programGL = m_program->getProgram();
3465 const glw::Functions& gl = renderCtx.getFunctions();
3467 setViewport(gl, viewport);
3468 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3469 gl.useProgram(programGL);
3472 vector<float> attributeData(NUM_VERTICES);
3474 for (int i = 0; i < NUM_VERTICES; i++)
3475 attributeData[i] = (float)i / (float)(NUM_VERTICES-1);
3477 gl.patchParameteri(GL_PATCH_VERTICES, NUM_VERTICES);
3478 gl.clear(GL_COLOR_BUFFER_BIT);
3480 const glu::VertexArrayBinding attrBindings[] =
3482 glu::va::Float("in_v_attr", 1, (int)attributeData.size(), 0, &attributeData[0])
3485 glu::draw(m_context.getRenderContext(), programGL, DE_LENGTH_OF_ARRAY(attrBindings), &attrBindings[0],
3486 glu::pr::Patches(NUM_VERTICES));
3487 GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
3491 const tcu::Surface rendered = getPixels(renderCtx, viewport);
3492 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath);
3493 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), rendered.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
3495 m_testCtx.setTestResult(success ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, success ? "Pass" : "Image comparison failed");
3500 /*--------------------------------------------------------------------*//*!
3501 * \brief Base class for testing invariance of entire primitive set
3503 * Draws two patches with identical tessellation levels and compares the
3504 * results. Repeats the same with other programs that are only different
3505 * in irrelevant ways; compares the results between these two programs.
3506 * Also potentially compares to results produced by different tessellation
3507 * levels (see e.g. invariance rule #6).
3508 * Furthermore, repeats the above with multiple different tessellation
3511 * The manner of primitive set comparison is defined by subclass. E.g.
3512 * case for invariance rule #1 tests that same vertices come out, in same
3513 * order; rule #5 only requires that the same triangles are output, but
3514 * not necessarily in the same order.
3515 *//*--------------------------------------------------------------------*/
3516 class PrimitiveSetInvarianceCase : public TestCase
3521 WINDINGUSAGE_CCW = 0,
3528 PrimitiveSetInvarianceCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, bool usePointMode, WindingUsage windingUsage)
3529 : TestCase (context, name, description)
3530 , m_primitiveType (primType)
3531 , m_spacing (spacing)
3532 , m_usePointMode (usePointMode)
3533 , m_windingUsage (windingUsage)
3539 IterateResult iterate (void);
3546 string description (void) const { return tessellationLevelsString(&inner[0], &outer[0]); }
3550 vector<TessLevels> levels;
3551 int mem; //!< Subclass-defined arbitrary piece of data, for type of the levelcase, if needed. Passed to compare().
3552 LevelCase (const TessLevels& lev) : levels(vector<TessLevels>(1, lev)), mem(0) {}
3553 LevelCase (void) : mem(0) {}
3556 virtual vector<LevelCase> genTessLevelCases (void) const;
3557 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int levelCaseMem) const = 0;
3559 const TessPrimitiveType m_primitiveType;
3565 SharedPtr<const ShaderProgram> program;
3567 Program (Winding w, const SharedPtr<const ShaderProgram>& prog) : winding(w), program(prog) {}
3569 string description (void) const { return string() + "winding mode " + getWindingShaderName(winding); };
3572 static const int RENDER_SIZE = 16;
3574 const SpacingMode m_spacing;
3575 const bool m_usePointMode;
3576 const WindingUsage m_windingUsage;
3578 vector<Program> m_programs;
3581 vector<PrimitiveSetInvarianceCase::LevelCase> PrimitiveSetInvarianceCase::genTessLevelCases (void) const
3583 static const TessLevels basicTessLevelCases[] =
3585 { { 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
3586 { { 63.0f, 24.0f }, { 15.0f, 42.0f, 10.0f, 12.0f } },
3587 { { 3.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } },
3588 { { 4.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
3589 { { 2.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } },
3590 { { 5.0f, 6.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
3591 { { 1.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
3592 { { 5.0f, 1.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
3593 { { 5.2f, 1.6f }, { 2.9f, 3.4f, 1.5f, 4.1f } }
3596 vector<LevelCase> result;
3597 for (int i = 0; i < DE_LENGTH_OF_ARRAY(basicTessLevelCases); i++)
3598 result.push_back(LevelCase(basicTessLevelCases[i]));
3601 de::Random rnd(123);
3602 for (int i = 0; i < 10; i++)
3605 for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.inner); j++)
3606 levels.inner[j] = rnd.getFloat(1.0f, 16.0f);
3607 for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.outer); j++)
3608 levels.outer[j] = rnd.getFloat(1.0f, 16.0f);
3609 result.push_back(LevelCase(levels));
3616 void PrimitiveSetInvarianceCase::init (void)
3618 const int numDifferentConstantExprCases = 2;
3619 vector<Winding> windings;
3620 switch (m_windingUsage)
3622 case WINDINGUSAGE_CCW: windings.push_back(WINDING_CCW); break;
3623 case WINDINGUSAGE_CW: windings.push_back(WINDING_CW); break;
3624 case WINDINGUSAGE_VARY: windings.push_back(WINDING_CCW);
3625 windings.push_back(WINDING_CW); break;
3626 default: DE_ASSERT(false);
3629 checkTessellationSupport(m_context);
3630 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
3632 for (int constantExprCaseNdx = 0; constantExprCaseNdx < numDifferentConstantExprCases; constantExprCaseNdx++)
3634 for (int windingCaseNdx = 0; windingCaseNdx < (int)windings.size(); windingCaseNdx++)
3636 const string floatLit01 = de::floatToString(10.0f / (float)(constantExprCaseNdx + 10), 2);
3637 const int programNdx = (int)m_programs.size();
3639 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
3641 "in highp float in_v_attr;\n"
3642 "out highp float in_tc_attr;\n"
3644 "void main (void)\n"
3646 " in_tc_attr = in_v_attr;\n"
3648 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
3649 "${TESSELLATION_SHADER_REQUIRE}\n"
3651 "layout (vertices = " + de::toString(constantExprCaseNdx+1) + ") out;\n"
3653 "in highp float in_tc_attr[];\n"
3655 "patch out highp float in_te_positionOffset;\n"
3657 "void main (void)\n"
3659 " in_te_positionOffset = in_tc_attr[6];\n"
3661 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
3662 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
3664 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
3665 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
3666 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
3667 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
3669 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
3670 "${TESSELLATION_SHADER_REQUIRE}\n"
3672 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, windings[windingCaseNdx], m_usePointMode) +
3674 "patch in highp float in_te_positionOffset;\n"
3676 "out highp vec4 in_f_color;\n"
3677 "invariant out highp vec3 out_te_tessCoord;\n"
3679 "void main (void)\n"
3681 " gl_Position = vec4(gl_TessCoord.xy*" + floatLit01 + " - in_te_positionOffset + float(gl_PrimitiveID)*0.1, 0.0, 1.0);\n"
3682 " in_f_color = vec4(" + floatLit01 + ");\n"
3683 " out_te_tessCoord = gl_TessCoord;\n"
3685 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
3687 "layout (location = 0) out mediump vec4 o_color;\n"
3689 "in highp vec4 in_f_color;\n"
3691 "void main (void)\n"
3693 " o_color = in_f_color;\n"
3696 m_programs.push_back(Program(windings[windingCaseNdx],
3697 SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
3698 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
3699 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
3700 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
3701 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
3702 << glu::TransformFeedbackVarying ("out_te_tessCoord")
3703 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)))));
3706 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Program" + de::toString(programNdx), "Program " + de::toString(programNdx));
3708 if (programNdx == 0 || !m_programs.back().program->isOk())
3709 m_testCtx.getLog() << *m_programs.back().program;
3711 if (!m_programs.back().program->isOk())
3712 TCU_FAIL("Program compilation failed");
3715 m_testCtx.getLog() << TestLog::Message << "Note: program " << programNdx << " is similar to above, except some constants are different, and: " << m_programs.back().description() << TestLog::EndMessage;
3721 void PrimitiveSetInvarianceCase::deinit (void)
3726 PrimitiveSetInvarianceCase::IterateResult PrimitiveSetInvarianceCase::iterate (void)
3728 typedef TransformFeedbackHandler<Vec3> TFHandler;
3730 TestLog& log = m_testCtx.getLog();
3731 const RenderContext& renderCtx = m_context.getRenderContext();
3732 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
3733 const glw::Functions& gl = renderCtx.getFunctions();
3734 const vector<LevelCase> tessLevelCases = genTessLevelCases();
3735 vector<vector<int> > primitiveCounts;
3736 int maxNumPrimitives = -1;
3738 for (int caseNdx = 0; caseNdx < (int)tessLevelCases.size(); caseNdx++)
3740 primitiveCounts.push_back(vector<int>());
3741 for (int i = 0; i < (int)tessLevelCases[caseNdx].levels.size(); i++)
3743 const int primCount = referencePrimitiveCount(m_primitiveType, m_spacing, m_usePointMode,
3744 &tessLevelCases[caseNdx].levels[i].inner[0], &tessLevelCases[caseNdx].levels[i].outer[0]);
3745 primitiveCounts.back().push_back(primCount);
3746 maxNumPrimitives = de::max(maxNumPrimitives, primCount);
3750 const deUint32 primitiveTypeGL = outputPrimitiveTypeGL(m_primitiveType, m_usePointMode);
3751 const TFHandler transformFeedback (m_context.getRenderContext(), 2*maxNumPrimitives*numVerticesPerPrimitive(primitiveTypeGL));
3753 setViewport(gl, viewport);
3754 gl.patchParameteri(GL_PATCH_VERTICES, 7);
3756 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < (int)tessLevelCases.size(); tessLevelCaseNdx++)
3758 const LevelCase& levelCase = tessLevelCases[tessLevelCaseNdx];
3759 vector<Vec3> firstPrimVertices;
3762 string tessLevelsStr;
3763 for (int i = 0; i < (int)levelCase.levels.size(); i++)
3764 tessLevelsStr += (levelCase.levels.size() > 1 ? "\n" : "") + levelCase.levels[i].description();
3765 log << TestLog::Message << "Tessellation level sets: " << tessLevelsStr << TestLog::EndMessage;
3768 for (int subTessLevelCaseNdx = 0; subTessLevelCaseNdx < (int)levelCase.levels.size(); subTessLevelCaseNdx++)
3770 const TessLevels& tessLevels = levelCase.levels[subTessLevelCaseNdx];
3771 const float (&inner)[2] = tessLevels.inner;
3772 const float (&outer)[4] = tessLevels.outer;
3773 const float attribute[2*7] = { inner[0], inner[1], outer[0], outer[1], outer[2], outer[3], 0.0f,
3774 inner[0], inner[1], outer[0], outer[1], outer[2], outer[3], 0.5f };
3775 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, DE_LENGTH_OF_ARRAY(attribute), 0, &attribute[0]) };
3777 for (int programNdx = 0; programNdx < (int)m_programs.size(); programNdx++)
3779 const deUint32 programGL = m_programs[programNdx].program->getProgram();
3780 gl.useProgram(programGL);
3781 const TFHandler::Result tfResult = transformFeedback.renderAndGetPrimitives(programGL, primitiveTypeGL, DE_LENGTH_OF_ARRAY(bindings), &bindings[0], DE_LENGTH_OF_ARRAY(attribute));
3783 if (tfResult.numPrimitives != 2*primitiveCounts[tessLevelCaseNdx][subTessLevelCaseNdx])
3785 log << TestLog::Message << "Failure: GL reported GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN to be "
3786 << tfResult.numPrimitives << ", reference value is " << 2*primitiveCounts[tessLevelCaseNdx][subTessLevelCaseNdx]
3787 << TestLog::EndMessage;
3789 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of primitives");
3794 const int half = (int)tfResult.varying.size()/2;
3795 const vector<Vec3> prim0Vertices = vector<Vec3>(tfResult.varying.begin(), tfResult.varying.begin() + half);
3796 const Vec3* const prim1Vertices = &tfResult.varying[half];
3798 for (int vtxNdx = 0; vtxNdx < (int)prim0Vertices.size(); vtxNdx++)
3800 if (prim0Vertices[vtxNdx] != prim1Vertices[vtxNdx])
3802 log << TestLog::Message << "Failure: tessellation coordinate at index " << vtxNdx << " differs between two primitives drawn in one draw call" << TestLog::EndMessage
3803 << TestLog::Message << "Note: the coordinate is " << prim0Vertices[vtxNdx] << " for the first primitive and " << prim1Vertices[vtxNdx] << " for the second" << TestLog::EndMessage
3804 << TestLog::Message << "Note: tessellation levels for both primitives were: " << tessellationLevelsString(&inner[0], &outer[0]) << TestLog::EndMessage;
3805 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of primitives");
3810 if (programNdx == 0 && subTessLevelCaseNdx == 0)
3811 firstPrimVertices = prim0Vertices;
3814 const bool compareOk = compare(firstPrimVertices, prim0Vertices, levelCase.mem);
3817 log << TestLog::Message << "Note: comparison of tessellation coordinates failed; comparison was made between following cases:\n"
3818 << " - case A: program 0, tessellation levels: " << tessLevelCases[tessLevelCaseNdx].levels[0].description() << "\n"
3819 << " - case B: program " << programNdx << ", tessellation levels: " << tessLevels.description() << TestLog::EndMessage;
3820 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of primitives");
3829 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3833 /*--------------------------------------------------------------------*//*!
3834 * \brief Test invariance rule #1
3836 * Test that the sequence of primitives input to the TES only depends on
3837 * the tessellation levels, tessellation mode, spacing mode, winding, and
3839 *//*--------------------------------------------------------------------*/
3840 class InvariantPrimitiveSetCase : public PrimitiveSetInvarianceCase
3843 InvariantPrimitiveSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
3844 : PrimitiveSetInvarianceCase(context, name, description, primType, spacing, usePointMode, winding == WINDING_CCW ? WINDINGUSAGE_CCW
3845 : winding == WINDING_CW ? WINDINGUSAGE_CW
3846 : WINDINGUSAGE_LAST)
3851 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int) const
3853 for (int vtxNdx = 0; vtxNdx < (int)coordsA.size(); vtxNdx++)
3855 if (coordsA[vtxNdx] != coordsB[vtxNdx])
3857 m_testCtx.getLog() << TestLog::Message << "Failure: tessellation coordinate at index " << vtxNdx << " differs between two programs" << TestLog::EndMessage
3858 << TestLog::Message << "Note: the coordinate is " << coordsA[vtxNdx] << " for the first program and " << coordsB[vtxNdx] << " for the other" << TestLog::EndMessage;
3866 /*--------------------------------------------------------------------*//*!
3867 * \brief Test invariance rule #2
3869 * Test that the set of vertices along an outer edge of a quad or triangle
3870 * only depends on that edge's tessellation level, and spacing.
3872 * For each (outer) edge in the quad or triangle, draw multiple patches
3873 * with identical tessellation levels for that outer edge but with
3874 * different values for the other outer edges; compare, among the
3875 * primitives, the vertices generated for that outer edge. Repeat with
3876 * different programs, using different winding etc. settings. Compare
3877 * the edge's vertices between different programs.
3878 *//*--------------------------------------------------------------------*/
3879 class InvariantOuterEdgeCase : public TestCase
3882 InvariantOuterEdgeCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing)
3883 : TestCase (context, name, description)
3884 , m_primitiveType (primType)
3885 , m_spacing (spacing)
3887 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
3892 IterateResult iterate (void);
3899 SharedPtr<const ShaderProgram> program;
3901 Program (Winding w, bool point, const SharedPtr<const ShaderProgram>& prog) : winding(w), usePointMode(point), program(prog) {}
3903 string description (void) const { return string() + "winding mode " + getWindingShaderName(winding) + ", " + (usePointMode ? "" : "don't ") + "use point mode"; };
3906 static vector<float> generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel);
3908 static const int RENDER_SIZE = 16;
3910 const TessPrimitiveType m_primitiveType;
3911 const SpacingMode m_spacing;
3913 vector<Program> m_programs;
3916 vector<float> InvariantOuterEdgeCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel)
3918 de::Random rnd(123);
3919 return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd);
3922 void InvariantOuterEdgeCase::init (void)
3924 checkTessellationSupport(m_context);
3925 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
3927 for (int windingI = 0; windingI < WINDING_LAST; windingI++)
3929 const Winding winding = (Winding)windingI;
3931 for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++)
3933 const bool usePointMode = usePointModeI != 0;
3934 const int programNdx = (int)m_programs.size();
3935 const string floatLit01 = de::floatToString(10.0f / (float)(programNdx + 10), 2);
3937 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
3939 "in highp float in_v_attr;\n"
3940 "out highp float in_tc_attr;\n"
3942 "void main (void)\n"
3944 " in_tc_attr = in_v_attr;\n"
3946 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
3947 "${TESSELLATION_SHADER_REQUIRE}\n"
3949 "layout (vertices = " + de::toString(programNdx+1) + ") out;\n"
3951 "in highp float in_tc_attr[];\n"
3953 "void main (void)\n"
3955 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
3956 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
3958 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
3959 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
3960 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
3961 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
3963 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
3964 "${TESSELLATION_SHADER_REQUIRE}\n"
3966 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, winding, usePointMode) +
3968 "out highp vec4 in_f_color;\n"
3969 "invariant out highp vec3 out_te_tessCoord;\n"
3971 "void main (void)\n"
3973 " gl_Position = vec4(gl_TessCoord.xy*" + floatLit01 + " - float(gl_PrimitiveID)*0.05, 0.0, 1.0);\n"
3974 " in_f_color = vec4(" + floatLit01 + ");\n"
3975 " out_te_tessCoord = gl_TessCoord;\n"
3977 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
3979 "layout (location = 0) out mediump vec4 o_color;\n"
3981 "in highp vec4 in_f_color;\n"
3983 "void main (void)\n"
3985 " o_color = in_f_color;\n"
3988 m_programs.push_back(Program(winding, usePointMode,
3989 SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
3990 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
3991 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
3992 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
3993 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
3994 << glu::TransformFeedbackVarying ("out_te_tessCoord")
3995 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)))));
3998 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Program" + de::toString(programNdx), "Program " + de::toString(programNdx));
4000 if (programNdx == 0 || !m_programs.back().program->isOk())
4001 m_testCtx.getLog() << *m_programs.back().program;
4003 if (!m_programs.back().program->isOk())
4004 TCU_FAIL("Program compilation failed");
4007 m_testCtx.getLog() << TestLog::Message << "Note: program " << programNdx << " is similar to above, except some constants are different, and: " << m_programs.back().description() << TestLog::EndMessage;
4013 void InvariantOuterEdgeCase::deinit (void)
4018 InvariantOuterEdgeCase::IterateResult InvariantOuterEdgeCase::iterate (void)
4020 typedef TransformFeedbackHandler<Vec3> TFHandler;
4022 TestLog& log = m_testCtx.getLog();
4023 const RenderContext& renderCtx = m_context.getRenderContext();
4024 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
4025 const glw::Functions& gl = renderCtx.getFunctions();
4027 static const float singleOuterEdgeLevels[] = { 1.0f, 1.2f, 1.9f, 2.3f, 2.8f, 3.3f, 3.8f, 10.2f, 1.6f, 24.4f, 24.7f, 63.0f };
4028 const int numPatchesPerDrawCall = 10;
4029 const vector<OuterEdgeDescription> edgeDescriptions = outerEdgeDescriptions(m_primitiveType);
4032 // Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once.
4033 int maxNumVerticesInDrawCall = 0;
4035 const vector<float> patchTessLevels = generatePatchTessLevels(numPatchesPerDrawCall, 0 /* outer-edge index doesn't affect vertex count */, arrayMax(singleOuterEdgeLevels));
4037 for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++)
4038 maxNumVerticesInDrawCall = de::max(maxNumVerticesInDrawCall,
4039 multiplePatchReferenceVertexCount(m_primitiveType, m_spacing, usePointModeI != 0, &patchTessLevels[0], numPatchesPerDrawCall));
4043 const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall);
4045 setViewport(gl, viewport);
4046 gl.patchParameteri(GL_PATCH_VERTICES, 6);
4048 for (int outerEdgeIndex = 0; outerEdgeIndex < (int)edgeDescriptions.size(); outerEdgeIndex++)
4050 const OuterEdgeDescription& edgeDesc = edgeDescriptions[outerEdgeIndex];
4052 for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(singleOuterEdgeLevels); outerEdgeLevelCaseNdx++)
4054 typedef std::set<Vec3, VecLexLessThan<3> > Vec3Set;
4056 const vector<float> patchTessLevels = generatePatchTessLevels(numPatchesPerDrawCall, outerEdgeIndex, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
4057 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)patchTessLevels.size(), 0, &patchTessLevels[0]) };
4058 Vec3Set firstOuterEdgeVertices; // Vertices of the outer edge of the first patch of the first program's draw call; used for comparison with other patches.
4060 log << TestLog::Message << "Testing with outer tessellation level " << singleOuterEdgeLevels[outerEdgeLevelCaseNdx]
4061 << " for the " << edgeDesc.description() << " edge, and with various levels for other edges, and with all programs" << TestLog::EndMessage;
4063 for (int programNdx = 0; programNdx < (int)m_programs.size(); programNdx++)
4065 const Program& program = m_programs[programNdx];
4066 const deUint32 programGL = program.program->getProgram();
4068 gl.useProgram(programGL);
4071 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, program.usePointMode),
4072 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)patchTessLevels.size());
4073 const int refNumVertices = multiplePatchReferenceVertexCount(m_primitiveType, m_spacing, program.usePointMode, &patchTessLevels[0], numPatchesPerDrawCall);
4074 int numVerticesRead = 0;
4076 if ((int)tfResult.varying.size() != refNumVertices)
4078 log << TestLog::Message << "Failure: the number of vertices returned by transform feedback is "
4079 << tfResult.varying.size() << ", expected " << refNumVertices << TestLog::EndMessage
4080 << TestLog::Message << "Note: rendered " << numPatchesPerDrawCall
4081 << " patches in one draw call; tessellation levels for each patch are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n"
4082 << containerStr(patchTessLevels, 6) << TestLog::EndMessage;
4084 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4088 // Check the vertices of each patch.
4090 for (int patchNdx = 0; patchNdx < numPatchesPerDrawCall; patchNdx++)
4092 const float* const innerLevels = &patchTessLevels[6*patchNdx + 0];
4093 const float* const outerLevels = &patchTessLevels[6*patchNdx + 2];
4094 const int patchNumVertices = referenceVertexCount(m_primitiveType, m_spacing, program.usePointMode, innerLevels, outerLevels);
4095 Vec3Set outerEdgeVertices;
4097 // We're interested in just the vertices on the current outer edge.
4098 for(int vtxNdx = numVerticesRead; vtxNdx < numVerticesRead + patchNumVertices; vtxNdx++)
4100 const Vec3& vtx = tfResult.varying[vtxNdx];
4101 if (edgeDesc.contains(vtx))
4102 outerEdgeVertices.insert(tfResult.varying[vtxNdx]);
4105 // Check that the outer edge contains an appropriate number of vertices.
4107 const int refNumVerticesOnOuterEdge = 1 + getClampedRoundedTessLevel(m_spacing, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
4109 if ((int)outerEdgeVertices.size() != refNumVerticesOnOuterEdge)
4111 log << TestLog::Message << "Failure: the number of vertices on outer edge is " << outerEdgeVertices.size()
4112 << ", expected " << refNumVerticesOnOuterEdge << TestLog::EndMessage
4113 << TestLog::Message << "Note: vertices on the outer edge are:\n" << containerStr(outerEdgeVertices, 5, 0) << TestLog::EndMessage
4114 << TestLog::Message << "Note: the following parameters were used: " << program.description()
4115 << ", tessellation levels: " << tessellationLevelsString(innerLevels, outerLevels) << TestLog::EndMessage;
4116 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4121 // Compare the vertices to those of the first patch (unless this is the first patch).
4123 if (programNdx == 0 && patchNdx == 0)
4124 firstOuterEdgeVertices = outerEdgeVertices;
4127 if (firstOuterEdgeVertices != outerEdgeVertices)
4129 log << TestLog::Message << "Failure: vertices generated for the edge differ between the following cases:\n"
4130 << " - case A: " << m_programs[0].description() << ", tessellation levels: "
4131 << tessellationLevelsString(&patchTessLevels[0], &patchTessLevels[2]) << "\n"
4132 << " - case B: " << program.description() << ", tessellation levels: "
4133 << tessellationLevelsString(innerLevels, outerLevels) << TestLog::EndMessage;
4135 log << TestLog::Message << "Note: resulting vertices for the edge for the cases were:\n"
4136 << " - case A: " << containerStr(firstOuterEdgeVertices, 5, 14) << "\n"
4137 << " - case B: " << containerStr(outerEdgeVertices, 5, 14) << TestLog::EndMessage;
4139 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4144 numVerticesRead += patchNumVertices;
4147 DE_ASSERT(numVerticesRead == (int)tfResult.varying.size());
4155 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4159 /*--------------------------------------------------------------------*//*!
4160 * \brief Test invariance rule #3
4162 * Test that the vertices along an outer edge are placed symmetrically.
4164 * Draw multiple patches with different tessellation levels and different
4165 * point_mode, winding etc. Before outputting tesscoords with TF, mirror
4166 * the vertices in the TES such that every vertex on an outer edge -
4167 * except the possible middle vertex - should be duplicated in the output.
4168 * Check that appropriate duplicates exist.
4169 *//*--------------------------------------------------------------------*/
4170 class SymmetricOuterEdgeCase : public TestCase
4173 SymmetricOuterEdgeCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
4174 : TestCase (context, name, description)
4175 , m_primitiveType (primType)
4176 , m_spacing (spacing)
4177 , m_winding (winding)
4178 , m_usePointMode (usePointMode)
4184 IterateResult iterate (void);
4187 static vector<float> generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel);
4189 static const int RENDER_SIZE = 16;
4191 const TessPrimitiveType m_primitiveType;
4192 const SpacingMode m_spacing;
4193 const Winding m_winding;
4194 const bool m_usePointMode;
4196 SharedPtr<const glu::ShaderProgram> m_program;
4199 vector<float> SymmetricOuterEdgeCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel)
4201 de::Random rnd(123);
4202 return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd);
4205 void SymmetricOuterEdgeCase::init (void)
4207 checkTessellationSupport(m_context);
4208 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
4210 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
4212 "in highp float in_v_attr;\n"
4213 "out highp float in_tc_attr;\n"
4215 "void main (void)\n"
4217 " in_tc_attr = in_v_attr;\n"
4219 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
4220 "${TESSELLATION_SHADER_REQUIRE}\n"
4222 "layout (vertices = 1) out;\n"
4224 "in highp float in_tc_attr[];\n"
4226 "void main (void)\n"
4228 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
4229 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
4231 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
4232 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
4233 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
4234 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
4236 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
4237 "${TESSELLATION_SHADER_REQUIRE}\n"
4239 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) +
4241 "out highp vec4 in_f_color;\n"
4242 "out highp vec4 out_te_tessCoord_isMirrored;\n"
4244 "void main (void)\n"
4246 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
4247 " float x = gl_TessCoord.x;\n"
4248 " float y = gl_TessCoord.y;\n"
4249 " float z = gl_TessCoord.z;\n"
4250 " // Mirror one half of each outer edge onto the other half, except the endpoints (because they belong to two edges)\n"
4251 " out_te_tessCoord_isMirrored = z == 0.0 && x > 0.5 && x != 1.0 ? vec4(1.0-x, 1.0-y, 0.0, 1.0)\n"
4252 " : y == 0.0 && z > 0.5 && z != 1.0 ? vec4(1.0-x, 0.0, 1.0-z, 1.0)\n"
4253 " : x == 0.0 && y > 0.5 && y != 1.0 ? vec4( 0.0, 1.0-y, 1.0-z, 1.0)\n"
4254 " : vec4(x, y, z, 0.0);\n"
4255 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ?
4256 " float x = gl_TessCoord.x;\n"
4257 " float y = gl_TessCoord.y;\n"
4258 " // Mirror one half of each outer edge onto the other half, except the endpoints (because they belong to two edges)\n"
4259 " out_te_tessCoord_isMirrored = (x == 0.0 || x == 1.0) && y > 0.5 && y != 1.0 ? vec4( x, 1.0-y, 0.0, 1.0)\n"
4260 " : (y == 0.0 || y == 1.0) && x > 0.5 && x != 1.0 ? vec4(1.0-x, y, 0.0, 1.0)\n"
4261 " : vec4(x, y, 0.0, 0.0);\n"
4262 : m_primitiveType == TESSPRIMITIVETYPE_ISOLINES ?
4263 " float x = gl_TessCoord.x;\n"
4264 " float y = gl_TessCoord.y;\n"
4265 " // Mirror one half of each outer edge onto the other half\n"
4266 " out_te_tessCoord_isMirrored = (x == 0.0 || x == 1.0) && y > 0.5 ? vec4(x, 1.0-y, 0.0, 1.0)\n"
4267 " : vec4(x, y, 0.0, 0.0f);\n"
4270 " gl_Position = vec4(gl_TessCoord.xy, 0.0, 1.0);\n"
4271 " in_f_color = vec4(1.0);\n"
4273 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
4275 "layout (location = 0) out mediump vec4 o_color;\n"
4277 "in highp vec4 in_f_color;\n"
4279 "void main (void)\n"
4281 " o_color = in_f_color;\n"
4284 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
4285 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
4286 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
4287 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
4288 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
4289 << glu::TransformFeedbackVarying ("out_te_tessCoord_isMirrored")
4290 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
4292 m_testCtx.getLog() << *m_program;
4293 if (!m_program->isOk())
4294 TCU_FAIL("Program compilation failed");
4297 void SymmetricOuterEdgeCase::deinit (void)
4302 SymmetricOuterEdgeCase::IterateResult SymmetricOuterEdgeCase::iterate (void)
4304 typedef TransformFeedbackHandler<Vec4> TFHandler;
4306 TestLog& log = m_testCtx.getLog();
4307 const RenderContext& renderCtx = m_context.getRenderContext();
4308 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
4309 const glw::Functions& gl = renderCtx.getFunctions();
4311 static const float singleOuterEdgeLevels[] = { 1.0f, 1.2f, 1.9f, 2.3f, 2.8f, 3.3f, 3.8f, 10.2f, 1.6f, 24.4f, 24.7f, 63.0f };
4312 const vector<OuterEdgeDescription> edgeDescriptions = outerEdgeDescriptions(m_primitiveType);
4315 // Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once.
4316 int maxNumVerticesInDrawCall;
4318 const vector<float> patchTessLevels = generatePatchTessLevels(1, 0 /* outer-edge index doesn't affect vertex count */, arrayMax(singleOuterEdgeLevels));
4319 maxNumVerticesInDrawCall = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]);
4323 const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall);
4325 setViewport(gl, viewport);
4326 gl.patchParameteri(GL_PATCH_VERTICES, 6);
4328 for (int outerEdgeIndex = 0; outerEdgeIndex < (int)edgeDescriptions.size(); outerEdgeIndex++)
4330 const OuterEdgeDescription& edgeDesc = edgeDescriptions[outerEdgeIndex];
4332 for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(singleOuterEdgeLevels); outerEdgeLevelCaseNdx++)
4334 typedef std::set<Vec3, VecLexLessThan<3> > Vec3Set;
4336 const vector<float> patchTessLevels = generatePatchTessLevels(1, outerEdgeIndex, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
4337 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)patchTessLevels.size(), 0, &patchTessLevels[0]) };
4339 log << TestLog::Message << "Testing with outer tessellation level " << singleOuterEdgeLevels[outerEdgeLevelCaseNdx]
4340 << " for the " << edgeDesc.description() << " edge, and with various levels for other edges" << TestLog::EndMessage;
4343 const deUint32 programGL = m_program->getProgram();
4345 gl.useProgram(programGL);
4348 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode),
4349 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)patchTessLevels.size());
4350 const int refNumVertices = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]);
4352 if ((int)tfResult.varying.size() != refNumVertices)
4354 log << TestLog::Message << "Failure: the number of vertices returned by transform feedback is "
4355 << tfResult.varying.size() << ", expected " << refNumVertices << TestLog::EndMessage
4356 << TestLog::Message << "Note: rendered 1 patch, tessellation levels are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n"
4357 << containerStr(patchTessLevels, 6) << TestLog::EndMessage;
4359 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4363 // Check the vertices.
4366 Vec3Set nonMirroredEdgeVertices;
4367 Vec3Set mirroredEdgeVertices;
4369 // We're interested in just the vertices on the current outer edge.
4370 for(int vtxNdx = 0; vtxNdx < refNumVertices; vtxNdx++)
4372 const Vec3& vtx = tfResult.varying[vtxNdx].swizzle(0,1,2);
4373 if (edgeDesc.contains(vtx))
4375 // Ignore the middle vertex of the outer edge, as it's exactly at the mirroring point;
4376 // for isolines, also ignore (0, 0) and (1, 0) because there's no mirrored counterpart for them.
4377 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES && vtx == tcu::select(Vec3(0.0f), Vec3(0.5f), singleTrueMask<3>(edgeDesc.constantCoordinateIndex)))
4379 if (m_primitiveType == TESSPRIMITIVETYPE_QUADS && vtx.swizzle(0,1) == tcu::select(Vec2(edgeDesc.constantCoordinateValueChoices[0]),
4381 singleTrueMask<2>(edgeDesc.constantCoordinateIndex)))
4383 if (m_primitiveType == TESSPRIMITIVETYPE_ISOLINES && (vtx == Vec3(0.0f, 0.5f, 0.0f) || vtx == Vec3(1.0f, 0.5f, 0.0f) ||
4384 vtx == Vec3(0.0f, 0.0f, 0.0f) || vtx == Vec3(1.0f, 0.0f, 0.0f)))
4387 const bool isMirrored = tfResult.varying[vtxNdx].w() > 0.5f;
4389 mirroredEdgeVertices.insert(vtx);
4391 nonMirroredEdgeVertices.insert(vtx);
4395 if (m_primitiveType != TESSPRIMITIVETYPE_ISOLINES)
4397 // Check that both endpoints are present. Note that endpoints aren't mirrored by the shader, since they belong to more than one edge.
4402 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4404 endpointA = tcu::select(Vec3(1.0f), Vec3(0.0f), singleTrueMask<3>((edgeDesc.constantCoordinateIndex + 1) % 3));
4405 endpointB = tcu::select(Vec3(1.0f), Vec3(0.0f), singleTrueMask<3>((edgeDesc.constantCoordinateIndex + 2) % 3));
4407 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
4409 endpointA.xy() = tcu::select(Vec2(edgeDesc.constantCoordinateValueChoices[0]), Vec2(0.0f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex));
4410 endpointB.xy() = tcu::select(Vec2(edgeDesc.constantCoordinateValueChoices[0]), Vec2(1.0f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex));
4415 if (!contains(nonMirroredEdgeVertices, endpointA) ||
4416 !contains(nonMirroredEdgeVertices, endpointB))
4418 log << TestLog::Message << "Failure: edge doesn't contain both endpoints, " << endpointA << " and " << endpointB << TestLog::EndMessage
4419 << TestLog::Message << "Note: non-mirrored vertices:\n" << containerStr(nonMirroredEdgeVertices, 5)
4420 << "\nmirrored vertices:\n" << containerStr(mirroredEdgeVertices, 5) << TestLog::EndMessage;
4421 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4424 nonMirroredEdgeVertices.erase(endpointA);
4425 nonMirroredEdgeVertices.erase(endpointB);
4428 if (nonMirroredEdgeVertices != mirroredEdgeVertices)
4430 log << TestLog::Message << "Failure: the set of mirrored edges isn't equal to the set of non-mirrored edges (ignoring endpoints and possible middle)" << TestLog::EndMessage
4431 << TestLog::Message << "Note: non-mirrored vertices:\n" << containerStr(nonMirroredEdgeVertices, 5)
4432 << "\nmirrored vertices:\n" << containerStr(mirroredEdgeVertices, 5) << TestLog::EndMessage;
4433 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4444 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4448 /*--------------------------------------------------------------------*//*!
4449 * \brief Test invariance rule #4
4451 * Test that the vertices on an outer edge don't depend on which of the
4452 * edges it is, other than with respect to component order.
4453 *//*--------------------------------------------------------------------*/
4454 class OuterEdgeVertexSetIndexIndependenceCase : public TestCase
4457 OuterEdgeVertexSetIndexIndependenceCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
4458 : TestCase (context, name, description)
4459 , m_primitiveType (primType)
4460 , m_spacing (spacing)
4461 , m_winding (winding)
4462 , m_usePointMode (usePointMode)
4464 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
4469 IterateResult iterate (void);
4472 static vector<float> generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel);
4474 static const int RENDER_SIZE = 16;
4476 const TessPrimitiveType m_primitiveType;
4477 const SpacingMode m_spacing;
4478 const Winding m_winding;
4479 const bool m_usePointMode;
4481 SharedPtr<const glu::ShaderProgram> m_program;
4484 vector<float> OuterEdgeVertexSetIndexIndependenceCase::generatePatchTessLevels (int numPatches, int constantOuterLevelIndex, float constantOuterLevel)
4486 de::Random rnd(123);
4487 return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd);
4490 void OuterEdgeVertexSetIndexIndependenceCase::init (void)
4492 checkTessellationSupport(m_context);
4493 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
4495 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
4497 "in highp float in_v_attr;\n"
4498 "out highp float in_tc_attr;\n"
4500 "void main (void)\n"
4502 " in_tc_attr = in_v_attr;\n"
4504 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
4505 "${TESSELLATION_SHADER_REQUIRE}\n"
4507 "layout (vertices = 1) out;\n"
4509 "in highp float in_tc_attr[];\n"
4511 "void main (void)\n"
4513 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
4514 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
4516 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
4517 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
4518 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
4519 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
4521 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
4522 "${TESSELLATION_SHADER_REQUIRE}\n"
4524 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) +
4526 "out highp vec4 in_f_color;\n"
4527 "out highp vec3 out_te_tessCoord;\n"
4529 "void main (void)\n"
4531 " out_te_tessCoord = gl_TessCoord;"
4532 " gl_Position = vec4(gl_TessCoord.xy, 0.0, 1.0);\n"
4533 " in_f_color = vec4(1.0);\n"
4535 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
4537 "layout (location = 0) out mediump vec4 o_color;\n"
4539 "in highp vec4 in_f_color;\n"
4541 "void main (void)\n"
4543 " o_color = in_f_color;\n"
4546 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
4547 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
4548 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
4549 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
4550 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
4551 << glu::TransformFeedbackVarying ("out_te_tessCoord")
4552 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
4554 m_testCtx.getLog() << *m_program;
4555 if (!m_program->isOk())
4556 TCU_FAIL("Program compilation failed");
4559 void OuterEdgeVertexSetIndexIndependenceCase::deinit (void)
4564 OuterEdgeVertexSetIndexIndependenceCase::IterateResult OuterEdgeVertexSetIndexIndependenceCase::iterate (void)
4566 typedef TransformFeedbackHandler<Vec3> TFHandler;
4568 TestLog& log = m_testCtx.getLog();
4569 const RenderContext& renderCtx = m_context.getRenderContext();
4570 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
4571 const glw::Functions& gl = renderCtx.getFunctions();
4572 const deUint32 programGL = m_program->getProgram();
4574 static const float singleOuterEdgeLevels[] = { 1.0f, 1.2f, 1.9f, 2.3f, 2.8f, 3.3f, 3.8f, 10.2f, 1.6f, 24.4f, 24.7f, 63.0f };
4575 const vector<OuterEdgeDescription> edgeDescriptions = outerEdgeDescriptions(m_primitiveType);
4577 gl.useProgram(programGL);
4578 setViewport(gl, viewport);
4579 gl.patchParameteri(GL_PATCH_VERTICES, 6);
4582 // Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once.
4583 int maxNumVerticesInDrawCall = 0;
4585 const vector<float> patchTessLevels = generatePatchTessLevels(1, 0 /* outer-edge index doesn't affect vertex count */, arrayMax(singleOuterEdgeLevels));
4586 maxNumVerticesInDrawCall = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]);
4590 const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall);
4592 for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(singleOuterEdgeLevels); outerEdgeLevelCaseNdx++)
4594 typedef std::set<Vec3, VecLexLessThan<3> > Vec3Set;
4596 Vec3Set firstEdgeVertices;
4598 for (int outerEdgeIndex = 0; outerEdgeIndex < (int)edgeDescriptions.size(); outerEdgeIndex++)
4600 const OuterEdgeDescription& edgeDesc = edgeDescriptions[outerEdgeIndex];
4601 const vector<float> patchTessLevels = generatePatchTessLevels(1, outerEdgeIndex, singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
4602 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)patchTessLevels.size(), 0, &patchTessLevels[0]) };
4604 log << TestLog::Message << "Testing with outer tessellation level " << singleOuterEdgeLevels[outerEdgeLevelCaseNdx]
4605 << " for the " << edgeDesc.description() << " edge, and with various levels for other edges" << TestLog::EndMessage;
4608 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode),
4609 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)patchTessLevels.size());
4610 const int refNumVertices = referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &patchTessLevels[0], &patchTessLevels[2]);
4612 if ((int)tfResult.varying.size() != refNumVertices)
4614 log << TestLog::Message << "Failure: the number of vertices returned by transform feedback is "
4615 << tfResult.varying.size() << ", expected " << refNumVertices << TestLog::EndMessage
4616 << TestLog::Message << "Note: rendered 1 patch, tessellation levels are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n"
4617 << containerStr(patchTessLevels, 6) << TestLog::EndMessage;
4619 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4624 Vec3Set currentEdgeVertices;
4626 // Get the vertices on the current outer edge.
4627 for(int vtxNdx = 0; vtxNdx < refNumVertices; vtxNdx++)
4629 const Vec3& vtx = tfResult.varying[vtxNdx];
4630 if (edgeDesc.contains(vtx))
4632 // Swizzle components to match the order of the first edge.
4633 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4635 currentEdgeVertices.insert(outerEdgeIndex == 0 ? vtx
4636 : outerEdgeIndex == 1 ? vtx.swizzle(1, 0, 2)
4637 : outerEdgeIndex == 2 ? vtx.swizzle(2, 1, 0)
4640 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
4642 currentEdgeVertices.insert(Vec3(outerEdgeIndex == 0 ? vtx.y()
4643 : outerEdgeIndex == 1 ? vtx.x()
4644 : outerEdgeIndex == 2 ? vtx.y()
4645 : outerEdgeIndex == 3 ? vtx.x()
4654 if (outerEdgeIndex == 0)
4655 firstEdgeVertices = currentEdgeVertices;
4658 // Compare vertices of this edge to those of the first edge.
4660 if (currentEdgeVertices != firstEdgeVertices)
4662 const char* const swizzleDesc = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? (outerEdgeIndex == 1 ? "(y, x, z)"
4663 : outerEdgeIndex == 2 ? "(z, y, x)"
4665 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? (outerEdgeIndex == 1 ? "(x, 0)"
4666 : outerEdgeIndex == 2 ? "(y, 0)"
4667 : outerEdgeIndex == 3 ? "(x, 0)"
4671 log << TestLog::Message << "Failure: the set of vertices on the " << edgeDesc.description() << " edge"
4672 << " doesn't match the set of vertices on the " << edgeDescriptions[0].description() << " edge" << TestLog::EndMessage
4673 << TestLog::Message << "Note: set of vertices on " << edgeDesc.description() << " edge, components swizzled like " << swizzleDesc
4674 << " to match component order on first edge:\n" << containerStr(currentEdgeVertices, 5)
4675 << "\non " << edgeDescriptions[0].description() << " edge:\n" << containerStr(firstEdgeVertices, 5) << TestLog::EndMessage;
4676 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid set of vertices");
4687 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
4691 /*--------------------------------------------------------------------*//*!
4692 * \brief Test invariance rule #5
4694 * Test that the set of triangles input to the TES only depends on the
4695 * tessellation levels, tessellation mode and spacing mode. Specifically,
4696 * winding doesn't change the set of triangles, though it can change the
4697 * order in which they are input to TES, and can (and will) change the
4698 * vertex order within a triangle.
4699 *//*--------------------------------------------------------------------*/
4700 class InvariantTriangleSetCase : public PrimitiveSetInvarianceCase
4703 InvariantTriangleSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing)
4704 : PrimitiveSetInvarianceCase(context, name, description, primType, spacing, false, WINDINGUSAGE_VARY)
4706 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
4710 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int) const
4712 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog());
4716 /*--------------------------------------------------------------------*//*!
4717 * \brief Test invariance rule #6
4719 * Test that the set of inner triangles input to the TES only depends on
4720 * the inner tessellation levels, tessellation mode and spacing mode.
4721 *//*--------------------------------------------------------------------*/
4722 class InvariantInnerTriangleSetCase : public PrimitiveSetInvarianceCase
4725 InvariantInnerTriangleSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing)
4726 : PrimitiveSetInvarianceCase(context, name, description, primType, spacing, false, WINDINGUSAGE_VARY)
4728 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
4732 virtual vector<LevelCase> genTessLevelCases (void) const
4734 const int numSubCases = 4;
4735 const vector<LevelCase> baseResults = PrimitiveSetInvarianceCase::genTessLevelCases();
4736 vector<LevelCase> result;
4737 de::Random rnd (123);
4739 // Generate variants with different values for irrelevant levels.
4740 for (int baseNdx = 0; baseNdx < (int)baseResults.size(); baseNdx++)
4742 const TessLevels& base = baseResults[baseNdx].levels[0];
4743 TessLevels levels = base;
4744 LevelCase levelCase;
4746 for (int subNdx = 0; subNdx < numSubCases; subNdx++)
4748 levelCase.levels.push_back(levels);
4750 for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); i++)
4751 levels.outer[i] = rnd.getFloat(2.0f, 16.0f);
4752 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4753 levels.inner[1] = rnd.getFloat(2.0f, 16.0f);
4756 result.push_back(levelCase);
4762 struct IsInnerTriangleTriangle
4764 bool operator() (const Vec3* vertices) const
4766 for (int v = 0; v < 3; v++)
4767 for (int c = 0; c < 3; c++)
4768 if (vertices[v][c] == 0.0f)
4774 struct IsInnerQuadTriangle
4776 bool operator() (const Vec3* vertices) const
4778 for (int v = 0; v < 3; v++)
4779 for (int c = 0; c < 2; c++)
4780 if (vertices[v][c] == 0.0f || vertices[v][c] == 1.0f)
4786 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int) const
4788 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4789 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(), IsInnerTriangleTriangle(), "outer triangles");
4790 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
4791 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(), IsInnerQuadTriangle(), "outer triangles");
4800 /*--------------------------------------------------------------------*//*!
4801 * \brief Test invariance rule #7
4803 * Test that the set of outer triangles input to the TES only depends on
4804 * tessellation mode, spacing mode and the inner and outer tessellation
4805 * levels corresponding to the inner and outer edges relevant to that
4807 *//*--------------------------------------------------------------------*/
4808 class InvariantOuterTriangleSetCase : public PrimitiveSetInvarianceCase
4811 InvariantOuterTriangleSetCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing)
4812 : PrimitiveSetInvarianceCase(context, name, description, primType, spacing, false, WINDINGUSAGE_VARY)
4814 DE_ASSERT(primType == TESSPRIMITIVETYPE_TRIANGLES || primType == TESSPRIMITIVETYPE_QUADS);
4818 virtual vector<LevelCase> genTessLevelCases (void) const
4820 const int numSubCasesPerEdge = 4;
4821 const int numEdges = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3
4822 : m_primitiveType == TESSPRIMITIVETYPE_QUADS ? 4
4824 const vector<LevelCase> baseResult = PrimitiveSetInvarianceCase::genTessLevelCases();
4825 vector<LevelCase> result;
4826 de::Random rnd (123);
4828 // Generate variants with different values for irrelevant levels.
4829 for (int baseNdx = 0; baseNdx < (int)baseResult.size(); baseNdx++)
4831 const TessLevels& base = baseResult[baseNdx].levels[0];
4832 if (base.inner[0] == 1.0f || (m_primitiveType == TESSPRIMITIVETYPE_QUADS && base.inner[1] == 1.0f))
4835 for (int edgeNdx = 0; edgeNdx < numEdges; edgeNdx++)
4837 TessLevels levels = base;
4838 LevelCase levelCase;
4839 levelCase.mem = edgeNdx;
4841 for (int subCaseNdx = 0; subCaseNdx < numSubCasesPerEdge; subCaseNdx++)
4843 levelCase.levels.push_back(levels);
4845 for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); i++)
4848 levels.outer[i] = rnd.getFloat(2.0f, 16.0f);
4851 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4852 levels.inner[1] = rnd.getFloat(2.0f, 16.0f);
4855 result.push_back(levelCase);
4862 class IsTriangleTriangleOnOuterEdge
4865 IsTriangleTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
4866 bool operator() (const Vec3* vertices) const
4868 bool touchesAppropriateEdge = false;
4869 for (int v = 0; v < 3; v++)
4870 if (vertices[v][m_edgeNdx] == 0.0f)
4871 touchesAppropriateEdge = true;
4873 if (touchesAppropriateEdge)
4875 const Vec3 avg = (vertices[0] + vertices[1] + vertices[2]) / 3.0f;
4876 return avg[m_edgeNdx] < avg[(m_edgeNdx+1)%3] &&
4877 avg[m_edgeNdx] < avg[(m_edgeNdx+2)%3];
4886 class IsQuadTriangleOnOuterEdge
4889 IsQuadTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
4891 bool onEdge (const Vec3& v) const
4893 return v[m_edgeNdx%2] == (m_edgeNdx <= 1 ? 0.0f : 1.0f);
4896 static inline bool onAnyEdge (const Vec3& v)
4898 return v[0] == 0.0f || v[0] == 1.0f || v[1] == 0.0f || v[1] == 1.0f;
4901 bool operator() (const Vec3* vertices) const
4903 for (int v = 0; v < 3; v++)
4905 const Vec3& a = vertices[v];
4906 const Vec3& b = vertices[(v+1)%3];
4907 const Vec3& c = vertices[(v+2)%3];
4908 if (onEdge(a) && onEdge(b))
4910 if (onEdge(c) && !onAnyEdge(a) && !onAnyEdge(b) && a[m_edgeNdx%2] == b[m_edgeNdx%2])
4921 virtual bool compare (const vector<Vec3>& coordsA, const vector<Vec3>& coordsB, int outerEdgeNdx) const
4923 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
4925 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(),
4926 IsTriangleTriangleOnOuterEdge(outerEdgeNdx),
4927 ("inner triangles, and outer triangles corresponding to other edge than edge "
4928 + outerEdgeDescriptions(m_primitiveType)[outerEdgeNdx].description()).c_str());
4930 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS)
4932 return compareTriangleSets(coordsA, coordsB, m_testCtx.getLog(),
4933 IsQuadTriangleOnOuterEdge(outerEdgeNdx),
4934 ("inner triangles, and outer triangles corresponding to other edge than edge "
4935 + outerEdgeDescriptions(m_primitiveType)[outerEdgeNdx].description()).c_str());
4944 /*--------------------------------------------------------------------*//*!
4945 * \brief Base class for testing individual components of tess coords
4947 * Useful for testing parts of invariance rule #8.
4948 *//*--------------------------------------------------------------------*/
4949 class TessCoordComponentInvarianceCase : public TestCase
4952 TessCoordComponentInvarianceCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
4953 : TestCase (context, name, description)
4954 , m_primitiveType (primType)
4955 , m_spacing (spacing)
4956 , m_winding (winding)
4957 , m_usePointMode (usePointMode)
4963 IterateResult iterate (void);
4966 virtual string tessEvalOutputComponentStatements (const char* tessCoordComponentName, const char* outputComponentName) const = 0;
4967 virtual bool checkTessCoordComponent (float component) const = 0;
4970 static vector<float> genTessLevelCases (int numCases);
4972 static const int RENDER_SIZE = 16;
4974 const TessPrimitiveType m_primitiveType;
4975 const SpacingMode m_spacing;
4976 const Winding m_winding;
4977 const bool m_usePointMode;
4979 SharedPtr<const glu::ShaderProgram> m_program;
4982 void TessCoordComponentInvarianceCase::init (void)
4984 checkTessellationSupport(m_context);
4985 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
4987 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
4989 "in highp float in_v_attr;\n"
4990 "out highp float in_tc_attr;\n"
4992 "void main (void)\n"
4994 " in_tc_attr = in_v_attr;\n"
4996 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
4997 "${TESSELLATION_SHADER_REQUIRE}\n"
4999 "layout (vertices = 1) out;\n"
5001 "in highp float in_tc_attr[];\n"
5003 "void main (void)\n"
5005 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
5006 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
5008 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
5009 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
5010 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
5011 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
5013 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
5014 "${TESSELLATION_SHADER_REQUIRE}\n"
5016 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) +
5018 "out highp vec4 in_f_color;\n"
5019 "out highp vec3 out_te_output;\n"
5021 "void main (void)\n"
5023 + tessEvalOutputComponentStatements("gl_TessCoord.x", "out_te_output.x")
5024 + tessEvalOutputComponentStatements("gl_TessCoord.y", "out_te_output.y")
5026 + (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
5027 tessEvalOutputComponentStatements("gl_TessCoord.z", "out_te_output.z") :
5028 " out_te_output.z = 0.0f;\n") +
5029 " gl_Position = vec4(gl_TessCoord.xy, 0.0, 1.0);\n"
5030 " in_f_color = vec4(1.0);\n"
5032 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
5034 "layout (location = 0) out mediump vec4 o_color;\n"
5036 "in highp vec4 in_f_color;\n"
5038 "void main (void)\n"
5040 " o_color = in_f_color;\n"
5043 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
5044 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
5045 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
5046 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
5047 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
5048 << glu::TransformFeedbackVarying ("out_te_output")
5049 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
5051 m_testCtx.getLog() << *m_program;
5052 if (!m_program->isOk())
5053 TCU_FAIL("Program compilation failed");
5056 void TessCoordComponentInvarianceCase::deinit (void)
5061 vector<float> TessCoordComponentInvarianceCase::genTessLevelCases (int numCases)
5063 de::Random rnd(123);
5064 vector<float> result;
5066 for (int i = 0; i < numCases; i++)
5067 for (int j = 0; j < 6; j++)
5068 result.push_back(rnd.getFloat(1.0f, 63.0f));
5073 TessCoordComponentInvarianceCase::IterateResult TessCoordComponentInvarianceCase::iterate (void)
5075 typedef TransformFeedbackHandler<Vec3> TFHandler;
5077 TestLog& log = m_testCtx.getLog();
5078 const RenderContext& renderCtx = m_context.getRenderContext();
5079 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
5080 const glw::Functions& gl = renderCtx.getFunctions();
5081 const int numTessLevelCases = 32;
5082 const vector<float> tessLevels = genTessLevelCases(numTessLevelCases);
5083 const deUint32 programGL = m_program->getProgram();
5085 gl.useProgram(programGL);
5086 setViewport(gl, viewport);
5087 gl.patchParameteri(GL_PATCH_VERTICES, 6);
5090 // Compute the number vertices in the largest draw call, so we can allocate the TF buffer just once.
5091 int maxNumVerticesInDrawCall = 0;
5092 for (int i = 0; i < numTessLevelCases; i++)
5093 maxNumVerticesInDrawCall = de::max(maxNumVerticesInDrawCall, referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &tessLevels[6*i+0], &tessLevels[6*i+2]));
5096 const TFHandler tfHandler(m_context.getRenderContext(), maxNumVerticesInDrawCall);
5098 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < numTessLevelCases; tessLevelCaseNdx++)
5100 log << TestLog::Message << "Testing with tessellation levels: " << tessellationLevelsString(&tessLevels[6*tessLevelCaseNdx+0], &tessLevels[6*tessLevelCaseNdx+2]) << TestLog::EndMessage;
5102 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)6, 0, &tessLevels[6*tessLevelCaseNdx]) };
5103 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode),
5104 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], 6);
5106 for (int vtxNdx = 0; vtxNdx < (int)tfResult.varying.size(); vtxNdx++)
5108 const Vec3& vec = tfResult.varying[vtxNdx];
5109 const int numComps = m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2;
5111 for (int compNdx = 0; compNdx < numComps; compNdx++)
5113 if (!checkTessCoordComponent(vec[compNdx]))
5115 log << TestLog::Message << "Note: output value at index " << vtxNdx << " is "
5116 << (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? de::toString(vec) : de::toString(vec.swizzle(0,1)))
5117 << TestLog::EndMessage;
5118 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid tessellation coordinate component");
5127 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
5131 /*--------------------------------------------------------------------*//*!
5132 * \brief Test first part of invariance rule #8
5134 * Test that all (relevant) components of tess coord are in [0,1].
5135 *//*--------------------------------------------------------------------*/
5136 class TessCoordComponentRangeCase : public TessCoordComponentInvarianceCase
5139 TessCoordComponentRangeCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
5140 : TessCoordComponentInvarianceCase(context, name, description, primType, spacing, winding, usePointMode)
5145 virtual string tessEvalOutputComponentStatements (const char* tessCoordComponentName, const char* outputComponentName) const
5147 return string() + "\t" + outputComponentName + " = " + tessCoordComponentName + ";\n";
5150 virtual bool checkTessCoordComponent (float component) const
5152 if (!de::inRange(component, 0.0f, 1.0f))
5154 m_testCtx.getLog() << TestLog::Message << "Failure: tess coord component isn't in range [0,1]" << TestLog::EndMessage;
5161 /*--------------------------------------------------------------------*//*!
5162 * \brief Test second part of invariance rule #8
5164 * Test that all (relevant) components of tess coord are in [0,1] and
5165 * 1.0-c is exact for every such component c.
5166 *//*--------------------------------------------------------------------*/
5167 class OneMinusTessCoordComponentCase : public TessCoordComponentInvarianceCase
5170 OneMinusTessCoordComponentCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
5171 : TessCoordComponentInvarianceCase(context, name, description, primType, spacing, winding, usePointMode)
5176 virtual string tessEvalOutputComponentStatements (const char* tessCoordComponentName, const char* outputComponentName) const
5178 return string() + " {\n"
5179 " float oneMinusComp = 1.0 - " + tessCoordComponentName + ";\n"
5180 " " + outputComponentName + " = " + tessCoordComponentName + " + oneMinusComp;\n"
5184 virtual bool checkTessCoordComponent (float component) const
5186 if (component != 1.0f)
5188 m_testCtx.getLog() << TestLog::Message << "Failure: comp + (1.0-comp) doesn't equal 1.0 for some component of tessellation coordinate" << TestLog::EndMessage;
5195 /*--------------------------------------------------------------------*//*!
5196 * \brief Test that patch is discarded if relevant outer level <= 0.0
5198 * Draws patches with different combinations of tessellation levels,
5199 * varying which levels are negative. Verifies by checking that colored
5200 * pixels exist inside the area of valid primitives, and only black pixels
5201 * exist inside the area of discarded primitives. An additional sanity
5202 * test is done, checking that the number of primitives written by TF is
5204 *//*--------------------------------------------------------------------*/
5205 class PrimitiveDiscardCase : public TestCase
5208 PrimitiveDiscardCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, SpacingMode spacing, Winding winding, bool usePointMode)
5209 : TestCase (context, name, description)
5210 , m_primitiveType (primType)
5211 , m_spacing (spacing)
5212 , m_winding (winding)
5213 , m_usePointMode (usePointMode)
5219 IterateResult iterate (void);
5222 static vector<float> genAttributes (void);
5224 static const int RENDER_SIZE = 256;
5226 const TessPrimitiveType m_primitiveType;
5227 const SpacingMode m_spacing;
5228 const Winding m_winding;
5229 const bool m_usePointMode;
5231 SharedPtr<const glu::ShaderProgram> m_program;
5234 void PrimitiveDiscardCase::init (void)
5236 checkTessellationSupport(m_context);
5237 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
5239 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
5241 "in highp float in_v_attr;\n"
5242 "out highp float in_tc_attr;\n"
5244 "void main (void)\n"
5246 " in_tc_attr = in_v_attr;\n"
5248 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
5249 "${TESSELLATION_SHADER_REQUIRE}\n"
5251 "layout (vertices = 1) out;\n"
5253 "in highp float in_tc_attr[];\n"
5255 "patch out highp vec2 in_te_positionScale;\n"
5256 "patch out highp vec2 in_te_positionOffset;\n"
5258 "void main (void)\n"
5260 " in_te_positionScale = vec2(in_tc_attr[6], in_tc_attr[7]);\n"
5261 " in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\n"
5263 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
5264 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
5266 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
5267 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
5268 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
5269 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
5271 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
5272 "${TESSELLATION_SHADER_REQUIRE}\n"
5274 + getTessellationEvaluationInLayoutString(m_primitiveType, m_spacing, m_winding, m_usePointMode) +
5276 "patch in highp vec2 in_te_positionScale;\n"
5277 "patch in highp vec2 in_te_positionOffset;\n"
5279 "out highp vec3 out_te_tessCoord;\n"
5281 "void main (void)\n"
5283 " out_te_tessCoord = gl_TessCoord;\n"
5284 " gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n"
5286 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
5288 "layout (location = 0) out mediump vec4 o_color;\n"
5290 "void main (void)\n"
5292 " o_color = vec4(1.0);\n"
5295 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
5296 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
5297 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
5298 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
5299 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
5300 << glu::TransformFeedbackVarying ("out_te_tessCoord")
5301 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
5303 m_testCtx.getLog() << *m_program;
5304 if (!m_program->isOk())
5305 TCU_FAIL("Program compilation failed");
5308 void PrimitiveDiscardCase::deinit (void)
5313 vector<float> PrimitiveDiscardCase::genAttributes (void)
5315 // Generate input attributes (tessellation levels, and position scale and
5316 // offset) for a number of primitives. Each primitive has a different
5317 // combination of tessellatio levels; each level is either a valid
5318 // value or an "invalid" value (negative or zero, chosen from
5319 // invalidTessLevelChoices).
5321 // \note The attributes are generated in such an order that all of the
5322 // valid attribute tuples come before the first invalid one both
5323 // in the result vector, and when scanning the resulting 2d grid
5324 // of primitives is scanned in y-major order. This makes
5325 // verification somewhat simpler.
5327 static const float baseTessLevels[6] = { 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f };
5328 static const float invalidTessLevelChoices[] = { -0.42f, 0.0f };
5329 const int numChoices = 1 + DE_LENGTH_OF_ARRAY(invalidTessLevelChoices);
5330 float choices[6][numChoices];
5331 vector<float> result;
5333 for (int levelNdx = 0; levelNdx < 6; levelNdx++)
5334 for (int choiceNdx = 0; choiceNdx < numChoices; choiceNdx++)
5335 choices[levelNdx][choiceNdx] = choiceNdx == 0 ? baseTessLevels[levelNdx] : invalidTessLevelChoices[choiceNdx-1];
5338 const int numCols = intPow(numChoices, 6/2); // sqrt(numChoices**6) == sqrt(number of primitives)
5339 const int numRows = numCols;
5342 // We could do this with some generic combination-generation function, but meh, it's not that bad.
5343 for (i[2] = 0; i[2] < numChoices; i[2]++) // First outer
5344 for (i[3] = 0; i[3] < numChoices; i[3]++) // Second outer
5345 for (i[4] = 0; i[4] < numChoices; i[4]++) // Third outer
5346 for (i[5] = 0; i[5] < numChoices; i[5]++) // Fourth outer
5347 for (i[0] = 0; i[0] < numChoices; i[0]++) // First inner
5348 for (i[1] = 0; i[1] < numChoices; i[1]++) // Second inner
5350 for (int j = 0; j < 6; j++)
5351 result.push_back(choices[j][i[j]]);
5354 const int col = index % numCols;
5355 const int row = index / numCols;
5357 result.push_back((float)2.0f / (float)numCols);
5358 result.push_back((float)2.0f / (float)numRows);
5360 result.push_back((float)col / (float)numCols * 2.0f - 1.0f);
5361 result.push_back((float)row / (float)numRows * 2.0f - 1.0f);
5371 PrimitiveDiscardCase::IterateResult PrimitiveDiscardCase::iterate (void)
5373 typedef TransformFeedbackHandler<Vec3> TFHandler;
5375 TestLog& log = m_testCtx.getLog();
5376 const RenderContext& renderCtx = m_context.getRenderContext();
5377 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
5378 const glw::Functions& gl = renderCtx.getFunctions();
5379 const vector<float> attributes = genAttributes();
5380 const int numAttribsPerPrimitive = 6+2+2; // Tess levels, scale, offset.
5381 const int numPrimitives = (int)attributes.size() / numAttribsPerPrimitive;
5382 const deUint32 programGL = m_program->getProgram();
5384 gl.useProgram(programGL);
5385 setViewport(gl, viewport);
5386 gl.patchParameteri(GL_PATCH_VERTICES, numAttribsPerPrimitive);
5388 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
5389 gl.clear(GL_COLOR_BUFFER_BIT);
5391 // Check the convenience assertion that all discarded patches come after the last non-discarded patch.
5393 bool discardedPatchEncountered = false;
5394 for (int patchNdx = 0; patchNdx < numPrimitives; patchNdx++)
5396 const bool discard = isPatchDiscarded(m_primitiveType, &attributes[numAttribsPerPrimitive*patchNdx + 2]);
5397 DE_ASSERT(discard || !discardedPatchEncountered);
5398 discardedPatchEncountered = discard;
5400 DE_UNREF(discardedPatchEncountered);
5404 int numVerticesInDrawCall = 0;
5405 for (int patchNdx = 0; patchNdx < numPrimitives; patchNdx++)
5406 numVerticesInDrawCall += referenceVertexCount(m_primitiveType, m_spacing, m_usePointMode, &attributes[numAttribsPerPrimitive*patchNdx+0], &attributes[numAttribsPerPrimitive*patchNdx+2]);
5408 log << TestLog::Message << "Note: rendering " << numPrimitives << " patches; first patches have valid relevant outer levels, "
5409 << "but later patches have one or more invalid (i.e. less than or equal to 0.0) relevant outer levels" << TestLog::EndMessage;
5412 const TFHandler tfHandler (m_context.getRenderContext(), numVerticesInDrawCall);
5413 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, (int)attributes.size(), 0, &attributes[0]) };
5414 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, m_usePointMode),
5415 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], (int)attributes.size());
5416 const tcu::Surface pixels = getPixels(renderCtx, viewport);
5418 log << TestLog::Image("RenderedImage", "Rendered image", pixels);
5420 if ((int)tfResult.varying.size() != numVerticesInDrawCall)
5422 log << TestLog::Message << "Failure: expected " << numVerticesInDrawCall << " vertices from transform feedback, got " << tfResult.varying.size() << TestLog::EndMessage;
5423 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Wrong number of tessellation coordinates");
5427 // Check that white pixels are found around every non-discarded
5428 // patch, and that only black pixels are found after the last
5429 // non-discarded patch.
5431 int lastWhitePixelRow = 0;
5432 int secondToLastWhitePixelRow = 0;
5433 int lastWhitePixelColumnOnSecondToLastWhitePixelRow = 0;
5435 for (int patchNdx = 0; patchNdx < numPrimitives; patchNdx++)
5437 const float* const attr = &attributes[numAttribsPerPrimitive*patchNdx];
5438 const bool validLevels = !isPatchDiscarded(m_primitiveType, &attr[2]);
5442 // Not a discarded patch; check that at least one white pixel is found in its area.
5444 const float* const scale = &attr[6];
5445 const float* const offset = &attr[8];
5446 const int x0 = (int)(( offset[0] + 1.0f)*0.5f*(float)pixels.getWidth()) - 1;
5447 const int x1 = (int)((scale[0] + offset[0] + 1.0f)*0.5f*(float)pixels.getWidth()) + 1;
5448 const int y0 = (int)(( offset[1] + 1.0f)*0.5f*(float)pixels.getHeight()) - 1;
5449 const int y1 = (int)((scale[1] + offset[1] + 1.0f)*0.5f*(float)pixels.getHeight()) + 1;
5450 const bool isMSAA = renderCtx.getRenderTarget().getNumSamples() > 1;
5451 bool pixelOk = false;
5453 if (y1 > lastWhitePixelRow)
5455 secondToLastWhitePixelRow = lastWhitePixelRow;
5456 lastWhitePixelRow = y1;
5458 lastWhitePixelColumnOnSecondToLastWhitePixelRow = x1;
5460 for (int y = y0; y <= y1 && !pixelOk; y++)
5461 for (int x = x0; x <= x1 && !pixelOk; x++)
5463 if (!de::inBounds(x, 0, pixels.getWidth()) || !de::inBounds(y, 0, pixels.getHeight()))
5468 if (pixels.getPixel(x, y) != tcu::RGBA::black())
5473 if (pixels.getPixel(x, y) == tcu::RGBA::white())
5480 log << TestLog::Message << "Failure: expected at least one " << (isMSAA ? "non-black" : "white") << " pixel in the rectangle "
5481 << "[x0=" << x0 << ", y0=" << y0 << ", x1=" << x1 << ", y1=" << y1 << "]" << TestLog::EndMessage
5482 << TestLog::Message << "Note: the rectangle approximately corresponds to the patch with these tessellation levels: "
5483 << tessellationLevelsString(&attr[0], &attr[1]) << TestLog::EndMessage;
5484 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
5490 // First discarded primitive patch; the remaining are guaranteed to be discarded ones as well.
5492 for (int y = 0; y < pixels.getHeight(); y++)
5493 for (int x = 0; x < pixels.getWidth(); x++)
5495 if (y > lastWhitePixelRow || (y > secondToLastWhitePixelRow && x > lastWhitePixelColumnOnSecondToLastWhitePixelRow))
5497 if (pixels.getPixel(x, y) != tcu::RGBA::black())
5499 log << TestLog::Message << "Failure: expected all pixels to be black in the area "
5500 << (lastWhitePixelColumnOnSecondToLastWhitePixelRow < pixels.getWidth()-1
5501 ? string() + "y > " + de::toString(lastWhitePixelRow) + " || (y > " + de::toString(secondToLastWhitePixelRow)
5502 + " && x > " + de::toString(lastWhitePixelColumnOnSecondToLastWhitePixelRow) + ")"
5503 : string() + "y > " + de::toString(lastWhitePixelRow))
5504 << " (they all correspond to patches that should be discarded)" << TestLog::EndMessage
5505 << TestLog::Message << "Note: pixel " << tcu::IVec2(x, y) << " isn't black" << TestLog::EndMessage;
5506 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
5519 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
5523 /*--------------------------------------------------------------------*//*!
5524 * \brief Case testing user-defined IO between TCS and TES
5526 * TCS outputs various values to TES, including aggregates. The outputs
5527 * can be per-patch or per-vertex, and if per-vertex, they can also be in
5528 * an IO block. Per-vertex input array size can be left implicit (i.e.
5529 * inputArray[]) or explicit either by gl_MaxPatchVertices or an integer
5530 * literal whose value is queried from GL.
5532 * The values output are generated in TCS and verified in TES against
5533 * similarly generated values. In case a verification of a value fails, the
5534 * index of the invalid value is output with TF.
5535 * As a sanity check, also the rendering result is verified (against pre-
5536 * rendered reference).
5537 *//*--------------------------------------------------------------------*/
5538 class UserDefinedIOCase : public TestCase
5543 IO_TYPE_PER_PATCH = 0,
5544 IO_TYPE_PER_PATCH_ARRAY,
5545 IO_TYPE_PER_PATCH_BLOCK,
5546 IO_TYPE_PER_PATCH_BLOCK_ARRAY,
5548 IO_TYPE_PER_VERTEX_BLOCK,
5553 enum VertexIOArraySize
5555 VERTEX_IO_ARRAY_SIZE_IMPLICIT = 0,
5556 VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN, //!< Use gl_MaxPatchVertices as size for per-vertex input array.
5557 VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY, //!< Query GL_MAX_PATCH_VERTICES, and use that as size for per-vertex input array.
5559 VERTEX_IO_ARRAY_SIZE_LAST
5562 enum TessControlOutArraySize
5564 TESS_CONTROL_OUT_ARRAY_SIZE_IMPLICIT = 0,
5565 TESS_CONTROL_OUT_ARRAY_SIZE_LAYOUT,
5566 TESS_CONTROL_OUT_ARRAY_SIZE_QUERY,
5567 TESS_CONTROL_OUT_ARRAY_SIZE_SHADER_BUILTIN
5570 UserDefinedIOCase (Context& context, const char* name, const char* description, TessPrimitiveType primType, IOType ioType, VertexIOArraySize vertexIOArraySize, TessControlOutArraySize tessControlOutArraySize, const char* referenceImagePath)
5571 : TestCase (context, name, description)
5572 , m_primitiveType (primType)
5574 , m_vertexIOArraySize (vertexIOArraySize)
5575 , m_tessControlOutArraySize (tessControlOutArraySize)
5576 , m_referenceImagePath (referenceImagePath)
5582 IterateResult iterate (void);
5585 typedef string (*BasicTypeVisitFunc)(const string& name, glu::DataType type, int indentationDepth); //!< See glslTraverseBasicTypes below.
5587 class TopLevelObject
5590 virtual ~TopLevelObject (void) {}
5592 virtual string name (void) const = 0;
5593 virtual string declare (void) const = 0;
5594 virtual string declareArray (const string& arraySizeExpr) const = 0;
5595 virtual string glslTraverseBasicTypeArray (int numArrayElements, //!< If negative, traverse just array[gl_InvocationID], not all indices.
5596 int indentationDepth,
5597 BasicTypeVisitFunc) const = 0;
5598 virtual string glslTraverseBasicType (int indentationDepth,
5599 BasicTypeVisitFunc) const = 0;
5600 virtual int numBasicSubobjectsInElementType (void) const = 0;
5601 virtual string basicSubobjectAtIndex (int index, int arraySize) const = 0;
5604 class Variable : public TopLevelObject
5607 Variable (const string& name_, const glu::VarType& type, bool isArray)
5610 , m_isArray (isArray)
5612 DE_ASSERT(!type.isArrayType());
5615 string name (void) const { return m_name; }
5616 string declare (void) const;
5617 string declareArray (const string& arraySizeExpr) const;
5618 string glslTraverseBasicTypeArray (int numArrayElements, int indentationDepth, BasicTypeVisitFunc) const;
5619 string glslTraverseBasicType (int indentationDepth, BasicTypeVisitFunc) const;
5620 int numBasicSubobjectsInElementType (void) const;
5621 string basicSubobjectAtIndex (int index, int arraySize) const;
5625 glu::VarType m_type; //!< If this Variable is an array element, m_type is the element type; otherwise just the variable type.
5626 const bool m_isArray;
5629 class IOBlock : public TopLevelObject
5636 Member (const string& n, const glu::VarType& t) : name(n), type(t) {}
5639 IOBlock (const string& blockName, const string& interfaceName, const vector<Member>& members)
5640 : m_blockName (blockName)
5641 , m_interfaceName (interfaceName)
5642 , m_members (members)
5646 string name (void) const { return m_interfaceName; }
5647 string declare (void) const;
5648 string declareArray (const string& arraySizeExpr) const;
5649 string glslTraverseBasicTypeArray (int numArrayElements, int indentationDepth, BasicTypeVisitFunc) const;
5650 string glslTraverseBasicType (int indentationDepth, BasicTypeVisitFunc) const;
5651 int numBasicSubobjectsInElementType (void) const;
5652 string basicSubobjectAtIndex (int index, int arraySize) const;
5656 string m_interfaceName;
5657 vector<Member> m_members;
5660 static string glslTraverseBasicTypes (const string& rootName,
5661 const glu::VarType& rootType,
5662 int arrayNestingDepth,
5663 int indentationDepth,
5664 BasicTypeVisitFunc visit);
5666 static string glslAssignBasicTypeObject (const string& name, glu::DataType, int indentationDepth);
5667 static string glslCheckBasicTypeObject (const string& name, glu::DataType, int indentationDepth);
5668 static int numBasicSubobjectsInElementType (const vector<SharedPtr<TopLevelObject> >&);
5669 static string basicSubobjectAtIndex (int index, const vector<SharedPtr<TopLevelObject> >&, int topLevelArraySizes);
5677 NUM_OUTPUT_VERTICES = 5
5681 NUM_PER_PATCH_ARRAY_ELEMS = 3
5685 NUM_PER_PATCH_BLOCKS = 2
5688 const TessPrimitiveType m_primitiveType;
5689 const IOType m_ioType;
5690 const VertexIOArraySize m_vertexIOArraySize;
5691 const TessControlOutArraySize m_tessControlOutArraySize;
5692 const string m_referenceImagePath;
5694 vector<glu::StructType> m_structTypes;
5695 vector<SharedPtr<TopLevelObject> > m_tcsOutputs;
5696 vector<SharedPtr<TopLevelObject> > m_tesInputs;
5698 SharedPtr<const glu::ShaderProgram> m_program;
5701 /*--------------------------------------------------------------------*//*!
5702 * \brief Generate GLSL code to traverse (possibly aggregate) object
5704 * Generates a string that represents GLSL code that traverses the
5705 * basic-type subobjects in a rootType-typed object named rootName. Arrays
5706 * are traversed with loops and struct members are each traversed
5707 * separately. The code for each basic-type subobject is generated with
5708 * the function given as the 'visit' argument.
5709 *//*--------------------------------------------------------------------*/
5710 string UserDefinedIOCase::glslTraverseBasicTypes (const string& rootName,
5711 const glu::VarType& rootType,
5712 int arrayNestingDepth,
5713 int indentationDepth,
5714 BasicTypeVisitFunc visit)
5716 if (rootType.isBasicType())
5717 return visit(rootName, rootType.getBasicType(), indentationDepth);
5718 else if (rootType.isArrayType())
5720 const string indentation = string(indentationDepth, '\t');
5721 const string loopIndexName = "i" + de::toString(arrayNestingDepth);
5722 const string arrayLength = de::toString(rootType.getArraySize());
5723 return indentation + "for (int " + loopIndexName + " = 0; " + loopIndexName + " < " + de::toString(rootType.getArraySize()) + "; " + loopIndexName + "++)\n" +
5724 indentation + "{\n" +
5725 glslTraverseBasicTypes(rootName + "[" + loopIndexName + "]", rootType.getElementType(), arrayNestingDepth+1, indentationDepth+1, visit) +
5726 indentation + "}\n";
5728 else if (rootType.isStructType())
5730 const glu::StructType& structType = *rootType.getStructPtr();
5731 const int numMembers = structType.getNumMembers();
5734 for (int membNdx = 0; membNdx < numMembers; membNdx++)
5736 const glu::StructMember& member = structType.getMember(membNdx);
5737 result += glslTraverseBasicTypes(rootName + "." + member.getName(), member.getType(), arrayNestingDepth, indentationDepth, visit);
5749 string UserDefinedIOCase::Variable::declare (void) const
5751 DE_ASSERT(!m_isArray);
5752 return de::toString(glu::declare(m_type, m_name)) + ";\n";
5755 string UserDefinedIOCase::Variable::declareArray (const string& sizeExpr) const
5757 DE_ASSERT(m_isArray);
5758 return de::toString(glu::declare(m_type, m_name)) + "[" + sizeExpr + "];\n";
5761 string UserDefinedIOCase::IOBlock::declare (void) const
5763 std::ostringstream buf;
5765 buf << m_blockName << "\n"
5768 for (int i = 0; i < (int)m_members.size(); i++)
5769 buf << "\t" << glu::declare(m_members[i].type, m_members[i].name) << ";\n";
5771 buf << "} " << m_interfaceName << ";\n";
5775 string UserDefinedIOCase::IOBlock::declareArray (const string& sizeExpr) const
5777 std::ostringstream buf;
5779 buf << m_blockName << "\n"
5782 for (int i = 0; i < (int)m_members.size(); i++)
5783 buf << "\t" << glu::declare(m_members[i].type, m_members[i].name) << ";\n";
5785 buf << "} " << m_interfaceName << "[" << sizeExpr << "];\n";
5789 string UserDefinedIOCase::Variable::glslTraverseBasicTypeArray (int numArrayElements, int indentationDepth, BasicTypeVisitFunc visit) const
5791 DE_ASSERT(m_isArray);
5793 const bool traverseAsArray = numArrayElements >= 0;
5794 const string traversedName = m_name + (!traverseAsArray ? "[gl_InvocationID]" : "");
5795 const glu::VarType type = traverseAsArray ? glu::VarType(m_type, numArrayElements) : m_type;
5797 return UserDefinedIOCase::glslTraverseBasicTypes(traversedName, type, 0, indentationDepth, visit);
5800 string UserDefinedIOCase::Variable::glslTraverseBasicType (int indentationDepth, BasicTypeVisitFunc visit) const
5802 DE_ASSERT(!m_isArray);
5804 return UserDefinedIOCase::glslTraverseBasicTypes(m_name, m_type, 0, indentationDepth, visit);
5807 string UserDefinedIOCase::IOBlock::glslTraverseBasicTypeArray (int numArrayElements, int indentationDepth, BasicTypeVisitFunc visit) const
5809 if (numArrayElements >= 0)
5811 const string indentation = string(indentationDepth, '\t');
5812 string result = indentation + "for (int i0 = 0; i0 < " + de::toString(numArrayElements) + "; i0++)\n" +
5813 indentation + "{\n";
5814 for (int i = 0; i < (int)m_members.size(); i++)
5815 result += UserDefinedIOCase::glslTraverseBasicTypes(m_interfaceName + "[i0]." + m_members[i].name, m_members[i].type, 1, indentationDepth+1, visit);
5816 result += indentation + "}\n";
5822 for (int i = 0; i < (int)m_members.size(); i++)
5823 result += UserDefinedIOCase::glslTraverseBasicTypes(m_interfaceName + "[gl_InvocationID]." + m_members[i].name, m_members[i].type, 0, indentationDepth, visit);
5829 string UserDefinedIOCase::IOBlock::glslTraverseBasicType (int indentationDepth, BasicTypeVisitFunc visit) const
5832 for (int i = 0; i < (int)m_members.size(); i++)
5833 result += UserDefinedIOCase::glslTraverseBasicTypes(m_interfaceName + "." + m_members[i].name, m_members[i].type, 0, indentationDepth, visit);
5837 int UserDefinedIOCase::Variable::numBasicSubobjectsInElementType (void) const
5839 return numBasicSubobjects(m_type);
5842 int UserDefinedIOCase::IOBlock::numBasicSubobjectsInElementType (void) const
5845 for (int i = 0; i < (int)m_members.size(); i++)
5846 result += numBasicSubobjects(m_members[i].type);
5850 string UserDefinedIOCase::Variable::basicSubobjectAtIndex (int subobjectIndex, int arraySize) const
5852 const glu::VarType type = m_isArray ? glu::VarType(m_type, arraySize) : m_type;
5853 int currentIndex = 0;
5855 for (glu::BasicTypeIterator basicIt = glu::BasicTypeIterator::begin(&type);
5856 basicIt != glu::BasicTypeIterator::end(&type);
5859 if (currentIndex == subobjectIndex)
5860 return m_name + de::toString(glu::TypeAccessFormat(type, basicIt.getPath()));
5867 string UserDefinedIOCase::IOBlock::basicSubobjectAtIndex (int subobjectIndex, int arraySize) const
5869 int currentIndex = 0;
5870 for (int arrayNdx = 0; arrayNdx < arraySize; arrayNdx++)
5872 for (int memberNdx = 0; memberNdx < (int)m_members.size(); memberNdx++)
5874 const glu::VarType& membType = m_members[memberNdx].type;
5875 for (glu::BasicTypeIterator basicIt = glu::BasicTypeIterator::begin(&membType);
5876 basicIt != glu::BasicTypeIterator::end(&membType);
5879 if (currentIndex == subobjectIndex)
5880 return m_interfaceName + "[" + de::toString(arrayNdx) + "]." + m_members[memberNdx].name + de::toString(glu::TypeAccessFormat(membType, basicIt.getPath()));
5889 // Used as the 'visit' argument for glslTraverseBasicTypes.
5890 string UserDefinedIOCase::glslAssignBasicTypeObject (const string& name, glu::DataType type, int indentationDepth)
5892 const int scalarSize = glu::getDataTypeScalarSize(type);
5893 const string indentation = string(indentationDepth, '\t');
5896 result += indentation + name + " = ";
5898 if (type != glu::TYPE_FLOAT)
5899 result += string() + glu::getDataTypeName(type) + "(";
5900 for (int i = 0; i < scalarSize; i++)
5901 result += (i > 0 ? ", v+" + de::floatToString(0.8f*(float)i, 1)
5903 if (type != glu::TYPE_FLOAT)
5906 indentation + "v += 0.4;\n";
5910 // Used as the 'visit' argument for glslTraverseBasicTypes.
5911 string UserDefinedIOCase::glslCheckBasicTypeObject (const string& name, glu::DataType type, int indentationDepth)
5913 const int scalarSize = glu::getDataTypeScalarSize(type);
5914 const string indentation = string(indentationDepth, '\t');
5917 result += indentation + "allOk = allOk && compare_" + glu::getDataTypeName(type) + "(" + name + ", ";
5919 if (type != glu::TYPE_FLOAT)
5920 result += string() + glu::getDataTypeName(type) + "(";
5921 for (int i = 0; i < scalarSize; i++)
5922 result += (i > 0 ? ", v+" + de::floatToString(0.8f*(float)i, 1)
5924 if (type != glu::TYPE_FLOAT)
5927 indentation + "v += 0.4;\n" +
5928 indentation + "if (allOk) firstFailedInputIndex++;\n";
5933 int UserDefinedIOCase::numBasicSubobjectsInElementType (const vector<SharedPtr<TopLevelObject> >& objects)
5936 for (int i = 0; i < (int)objects.size(); i++)
5937 result += objects[i]->numBasicSubobjectsInElementType();
5941 string UserDefinedIOCase::basicSubobjectAtIndex (int subobjectIndex, const vector<SharedPtr<TopLevelObject> >& objects, int topLevelArraySize)
5943 int currentIndex = 0;
5944 int objectIndex = 0;
5945 for (; currentIndex < subobjectIndex; objectIndex++)
5946 currentIndex += objects[objectIndex]->numBasicSubobjectsInElementType() * topLevelArraySize;
5947 if (currentIndex > subobjectIndex)
5950 currentIndex -= objects[objectIndex]->numBasicSubobjectsInElementType() * topLevelArraySize;
5953 return objects[objectIndex]->basicSubobjectAtIndex(subobjectIndex - currentIndex, topLevelArraySize);
5956 void UserDefinedIOCase::init (void)
5958 checkTessellationSupport(m_context);
5959 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
5961 const bool isPerPatchIO = m_ioType == IO_TYPE_PER_PATCH ||
5962 m_ioType == IO_TYPE_PER_PATCH_ARRAY ||
5963 m_ioType == IO_TYPE_PER_PATCH_BLOCK ||
5964 m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY;
5966 const bool isExplicitVertexArraySize = m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN ||
5967 m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY;
5969 const string vertexAttrArrayInputSize = m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_IMPLICIT ? ""
5970 : m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN ? "gl_MaxPatchVertices"
5971 : m_vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY ? de::toString(m_context.getContextInfo().getInt(GL_MAX_PATCH_VERTICES))
5974 const char* const maybePatch = isPerPatchIO ? "patch " : "";
5975 const string outMaybePatch = string() + maybePatch + "out ";
5976 const string inMaybePatch = string() + maybePatch + "in ";
5977 const bool useBlock = m_ioType == IO_TYPE_PER_VERTEX_BLOCK ||
5978 m_ioType == IO_TYPE_PER_PATCH_BLOCK ||
5979 m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY;
5981 string tcsDeclarations;
5982 string tcsStatements;
5984 string tesDeclarations;
5985 string tesStatements;
5988 m_structTypes.push_back(glu::StructType("S"));
5990 const glu::VarType highpFloat (glu::TYPE_FLOAT, glu::PRECISION_HIGHP);
5991 glu::StructType& structType = m_structTypes.back();
5992 const glu::VarType structVarType (&structType);
5993 bool usedStruct = false;
5995 structType.addMember("x", glu::VarType(glu::TYPE_INT, glu::PRECISION_HIGHP));
5996 structType.addMember("y", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP));
6000 // It is illegal to have a structure containing an array as an output variable
6001 structType.addMember("z", glu::VarType(highpFloat, 2));
6006 const bool useLightweightBlock = (m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY); // use leaner block to make sure it is not larger than allowed (per-patch storage is very limited)
6007 vector<IOBlock::Member> blockMembers;
6009 if (!useLightweightBlock)
6010 blockMembers.push_back(IOBlock::Member("blockS", structVarType));
6012 blockMembers.push_back(IOBlock::Member("blockFa", glu::VarType(highpFloat, 3)));
6013 blockMembers.push_back(IOBlock::Member("blockSa", glu::VarType(structVarType, 2)));
6014 blockMembers.push_back(IOBlock::Member("blockF", highpFloat));
6016 m_tcsOutputs.push_back (SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "tcBlock", blockMembers)));
6017 m_tesInputs.push_back (SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "teBlock", blockMembers)));
6023 const Variable var0("in_te_s", structVarType, m_ioType != IO_TYPE_PER_PATCH);
6024 const Variable var1("in_te_f", highpFloat, m_ioType != IO_TYPE_PER_PATCH);
6026 if (m_ioType != IO_TYPE_PER_PATCH_ARRAY)
6028 // Arrays of structures are disallowed, add struct cases only if not arrayed variable
6029 m_tcsOutputs.push_back (SharedPtr<TopLevelObject>(new Variable(var0)));
6030 m_tesInputs.push_back (SharedPtr<TopLevelObject>(new Variable(var0)));
6035 m_tcsOutputs.push_back (SharedPtr<TopLevelObject>(new Variable(var1)));
6036 m_tesInputs.push_back (SharedPtr<TopLevelObject>(new Variable(var1)));
6039 tcsDeclarations += "in " + Variable("in_tc_attr", highpFloat, true).declareArray(vertexAttrArrayInputSize);
6042 tcsDeclarations += de::toString(glu::declare(structType)) + ";\n";
6044 tcsStatements += "\t{\n"
6045 "\t\thighp float v = 1.3;\n";
6047 for (int tcsOutputNdx = 0; tcsOutputNdx < (int)m_tcsOutputs.size(); tcsOutputNdx++)
6049 const TopLevelObject& output = *m_tcsOutputs[tcsOutputNdx];
6050 const int numElements = !isPerPatchIO ? -1 //!< \note -1 means indexing with gl_InstanceID
6051 : m_ioType == IO_TYPE_PER_PATCH ? 1
6052 : m_ioType == IO_TYPE_PER_PATCH_ARRAY ? NUM_PER_PATCH_ARRAY_ELEMS
6053 : m_ioType == IO_TYPE_PER_PATCH_BLOCK ? 1
6054 : m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? NUM_PER_PATCH_BLOCKS
6056 const bool isArray = (numElements != 1);
6058 DE_ASSERT(numElements != -2);
6062 tcsDeclarations += outMaybePatch + output.declareArray(m_ioType == IO_TYPE_PER_PATCH_ARRAY ? de::toString(int(NUM_PER_PATCH_ARRAY_ELEMS))
6063 : m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? de::toString(int(NUM_PER_PATCH_BLOCKS))
6064 : m_tessControlOutArraySize == TESS_CONTROL_OUT_ARRAY_SIZE_LAYOUT ? de::toString(int(NUM_OUTPUT_VERTICES))
6065 : m_tessControlOutArraySize == TESS_CONTROL_OUT_ARRAY_SIZE_QUERY ? de::toString(m_context.getContextInfo().getInt(GL_MAX_PATCH_VERTICES))
6066 : m_tessControlOutArraySize == TESS_CONTROL_OUT_ARRAY_SIZE_SHADER_BUILTIN ? "gl_MaxPatchVertices"
6070 tcsDeclarations += outMaybePatch + output.declare();
6073 tcsStatements += "\t\tv += float(gl_InvocationID)*" + de::floatToString(0.4f * (float)output.numBasicSubobjectsInElementType(), 1) + ";\n";
6075 tcsStatements += "\n\t\t// Assign values to output " + output.name() + "\n";
6077 tcsStatements += output.glslTraverseBasicTypeArray(numElements, 2, glslAssignBasicTypeObject);
6079 tcsStatements += output.glslTraverseBasicType(2, glslAssignBasicTypeObject);
6082 tcsStatements += "\t\tv += float(" + de::toString(int(NUM_OUTPUT_VERTICES)) + "-gl_InvocationID-1)*" + de::floatToString(0.4f * (float)output.numBasicSubobjectsInElementType(), 1) + ";\n";
6084 tcsStatements += "\t}\n";
6087 tesDeclarations += de::toString(glu::declare(structType)) + ";\n";
6089 tesStatements += "\tbool allOk = true;\n"
6090 "\thighp uint firstFailedInputIndex = 0u;\n"
6092 "\t\thighp float v = 1.3;\n";
6093 for (int tesInputNdx = 0; tesInputNdx < (int)m_tesInputs.size(); tesInputNdx++)
6095 const TopLevelObject& input = *m_tesInputs[tesInputNdx];
6096 const int numElements = !isPerPatchIO ? (int)NUM_OUTPUT_VERTICES
6097 : m_ioType == IO_TYPE_PER_PATCH ? 1
6098 : m_ioType == IO_TYPE_PER_PATCH_BLOCK ? 1
6099 : m_ioType == IO_TYPE_PER_PATCH_ARRAY ? NUM_PER_PATCH_ARRAY_ELEMS
6100 : m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? NUM_PER_PATCH_BLOCKS
6102 const bool isArray = (numElements != 1);
6104 DE_ASSERT(numElements != -2);
6107 tesDeclarations += inMaybePatch + input.declareArray(m_ioType == IO_TYPE_PER_PATCH_ARRAY ? de::toString(int(NUM_PER_PATCH_ARRAY_ELEMS))
6108 : m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? de::toString(int(NUM_PER_PATCH_BLOCKS))
6109 : isExplicitVertexArraySize ? de::toString(vertexAttrArrayInputSize)
6112 tesDeclarations += inMaybePatch + input.declare();
6114 tesStatements += "\n\t\t// Check values in input " + input.name() + "\n";
6116 tesStatements += input.glslTraverseBasicTypeArray(numElements, 2, glslCheckBasicTypeObject);
6118 tesStatements += input.glslTraverseBasicType(2, glslCheckBasicTypeObject);
6120 tesStatements += "\t}\n";
6123 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
6125 "in highp float in_v_attr;\n"
6126 "out highp float in_tc_attr;\n"
6128 "void main (void)\n"
6130 " in_tc_attr = in_v_attr;\n"
6132 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
6133 "${TESSELLATION_SHADER_REQUIRE}\n"
6135 "layout (vertices = " + de::toString(int(NUM_OUTPUT_VERTICES)) + ") out;\n"
6139 "patch out highp vec2 in_te_positionScale;\n"
6140 "patch out highp vec2 in_te_positionOffset;\n"
6142 "void main (void)\n"
6146 " in_te_positionScale = vec2(in_tc_attr[6], in_tc_attr[7]);\n"
6147 " in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\n"
6149 " gl_TessLevelInner[0] = in_tc_attr[0];\n"
6150 " gl_TessLevelInner[1] = in_tc_attr[1];\n"
6152 " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
6153 " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
6154 " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
6155 " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
6157 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
6158 "${TESSELLATION_SHADER_REQUIRE}\n"
6160 + getTessellationEvaluationInLayoutString(m_primitiveType) +
6164 "patch in highp vec2 in_te_positionScale;\n"
6165 "patch in highp vec2 in_te_positionOffset;\n"
6167 "out highp vec4 in_f_color;\n"
6168 "// Will contain the index of the first incorrect input,\n"
6169 "// or the number of inputs if all are correct\n"
6170 "flat out highp uint out_te_firstFailedInputIndex;\n"
6172 "bool compare_int (int a, int b) { return a == b; }\n"
6173 "bool compare_float (float a, float b) { return abs(a - b) < 0.01f; }\n"
6174 "bool compare_vec4 (vec4 a, vec4 b) { return all(lessThan(abs(a - b), vec4(0.01f))); }\n"
6176 "void main (void)\n"
6180 " gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n"
6181 " in_f_color = allOk ? vec4(0.0, 1.0, 0.0, 1.0)\n"
6182 " : vec4(1.0, 0.0, 0.0, 1.0);\n"
6183 " out_te_firstFailedInputIndex = firstFailedInputIndex;\n"
6185 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
6187 "layout (location = 0) out mediump vec4 o_color;\n"
6189 "in highp vec4 in_f_color;\n"
6191 "void main (void)\n"
6193 " o_color = in_f_color;\n"
6196 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
6197 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
6198 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
6199 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
6200 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))
6201 << glu::TransformFeedbackVarying ("out_te_firstFailedInputIndex")
6202 << glu::TransformFeedbackMode (GL_INTERLEAVED_ATTRIBS)));
6204 m_testCtx.getLog() << *m_program;
6205 if (!m_program->isOk())
6206 TCU_FAIL("Program compilation failed");
6209 void UserDefinedIOCase::deinit (void)
6214 UserDefinedIOCase::IterateResult UserDefinedIOCase::iterate (void)
6216 typedef TransformFeedbackHandler<deUint32> TFHandler;
6218 TestLog& log = m_testCtx.getLog();
6219 const RenderContext& renderCtx = m_context.getRenderContext();
6220 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
6221 const glw::Functions& gl = renderCtx.getFunctions();
6222 static const float attributes[6+2+2] = { /* inner */ 3.0f, 4.0f, /* outer */ 5.0f, 6.0f, 7.0f, 8.0f, /* pos. scale */ 1.2f, 1.3f, /* pos. offset */ -0.3f, -0.4f };
6223 const deUint32 programGL = m_program->getProgram();
6224 const int numVertices = referenceVertexCount(m_primitiveType, SPACINGMODE_EQUAL, false, &attributes[0], &attributes[2]);
6225 const TFHandler tfHandler (renderCtx, numVertices);
6226 tcu::ResultCollector result;
6228 gl.useProgram(programGL);
6229 setViewport(gl, viewport);
6230 gl.patchParameteri(GL_PATCH_VERTICES, DE_LENGTH_OF_ARRAY(attributes));
6232 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
6233 gl.clear(GL_COLOR_BUFFER_BIT);
6236 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 1, DE_LENGTH_OF_ARRAY(attributes), 0, &attributes[0]) };
6237 const TFHandler::Result tfResult = tfHandler.renderAndGetPrimitives(programGL, outputPrimitiveTypeGL(m_primitiveType, false),
6238 DE_LENGTH_OF_ARRAY(bindings), &bindings[0], DE_LENGTH_OF_ARRAY(attributes));
6241 const tcu::Surface pixels = getPixels(renderCtx, viewport);
6242 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath.c_str());
6243 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), pixels.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
6246 result.fail("Image comparison failed");
6249 if ((int)tfResult.varying.size() != numVertices)
6251 log << TestLog::Message << "Failure: transform feedback returned " << tfResult.varying.size() << " vertices; expected " << numVertices << TestLog::EndMessage;
6252 result.fail("Wrong number of vertices");
6256 const int topLevelArraySize = (m_ioType == IO_TYPE_PER_PATCH ? 1
6257 : m_ioType == IO_TYPE_PER_PATCH_ARRAY ? NUM_PER_PATCH_ARRAY_ELEMS
6258 : m_ioType == IO_TYPE_PER_PATCH_BLOCK ? 1
6259 : m_ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? NUM_PER_PATCH_BLOCKS
6260 : (int)NUM_OUTPUT_VERTICES);
6261 const int numTEInputs = numBasicSubobjectsInElementType(m_tesInputs) * topLevelArraySize;
6263 for (int vertexNdx = 0; vertexNdx < (int)numVertices; vertexNdx++)
6265 if (tfResult.varying[vertexNdx] > (deUint32)numTEInputs)
6267 log << TestLog::Message << "Failure: out_te_firstFailedInputIndex has value " << tfResult.varying[vertexNdx]
6268 << ", should be in range [0, " << numTEInputs << "]" << TestLog::EndMessage;
6269 result.fail("Invalid transform feedback output");
6271 else if (tfResult.varying[vertexNdx] != (deUint32)numTEInputs)
6273 log << TestLog::Message << "Failure: in tessellation evaluation shader, check for input "
6274 << basicSubobjectAtIndex(tfResult.varying[vertexNdx], m_tesInputs, topLevelArraySize) << " failed" << TestLog::EndMessage;
6275 result.fail("Invalid input value in tessellation evaluation shader");
6281 result.setTestContextResult(m_testCtx);
6285 /*--------------------------------------------------------------------*//*!
6286 * \brief Pass gl_Position between VS and TCS, or between TCS and TES.
6288 * In TCS gl_Position is in the gl_out[] block and in TES in the gl_in[]
6289 * block, and has no special semantics in those. Arbitrary vec4 data can
6290 * thus be passed there.
6291 *//*--------------------------------------------------------------------*/
6292 class GLPositionCase : public TestCase
6297 CASETYPE_VS_TO_TCS = 0,
6298 CASETYPE_TCS_TO_TES,
6299 CASETYPE_VS_TO_TCS_TO_TES,
6304 GLPositionCase (Context& context, const char* name, const char* description, CaseType caseType, const char* referenceImagePath)
6305 : TestCase (context, name, description)
6306 , m_caseType (caseType)
6307 , m_referenceImagePath (referenceImagePath)
6313 IterateResult iterate (void);
6315 static const char* getCaseTypeName (CaseType type);
6318 static const int RENDER_SIZE = 256;
6320 const CaseType m_caseType;
6321 const string m_referenceImagePath;
6323 SharedPtr<const glu::ShaderProgram> m_program;
6326 const char* GLPositionCase::getCaseTypeName (CaseType type)
6330 case CASETYPE_VS_TO_TCS: return "gl_position_vs_to_tcs";
6331 case CASETYPE_TCS_TO_TES: return "gl_position_tcs_to_tes";
6332 case CASETYPE_VS_TO_TCS_TO_TES: return "gl_position_vs_to_tcs_to_tes";
6334 DE_ASSERT(false); return DE_NULL;
6338 void GLPositionCase::init (void)
6340 checkTessellationSupport(m_context);
6341 checkRenderTargetSize(m_context.getRenderTarget(), RENDER_SIZE);
6343 const bool vsToTCS = m_caseType == CASETYPE_VS_TO_TCS || m_caseType == CASETYPE_VS_TO_TCS_TO_TES;
6344 const bool tcsToTES = m_caseType == CASETYPE_TCS_TO_TES || m_caseType == CASETYPE_VS_TO_TCS_TO_TES;
6346 const string tesIn0 = tcsToTES ? "gl_in[0].gl_Position" : "in_te_attr[0]";
6347 const string tesIn1 = tcsToTES ? "gl_in[1].gl_Position" : "in_te_attr[1]";
6348 const string tesIn2 = tcsToTES ? "gl_in[2].gl_Position" : "in_te_attr[2]";
6350 std::string vertexShaderTemplate ("${GLSL_VERSION_DECL}\n"
6352 "in highp vec4 in_v_attr;\n"
6353 + string(!vsToTCS ? "out highp vec4 in_tc_attr;\n" : "") +
6355 "void main (void)\n"
6357 " " + (vsToTCS ? "gl_Position" : "in_tc_attr") + " = in_v_attr;\n"
6359 std::string tessellationControlTemplate ("${GLSL_VERSION_DECL}\n"
6360 "${TESSELLATION_SHADER_REQUIRE}\n"
6362 "layout (vertices = 3) out;\n"
6364 + string(!vsToTCS ? "in highp vec4 in_tc_attr[];\n" : "") +
6366 + (!tcsToTES ? "out highp vec4 in_te_attr[];\n" : "") +
6368 "void main (void)\n"
6370 " " + (tcsToTES ? "gl_out[gl_InvocationID].gl_Position" : "in_te_attr[gl_InvocationID]") + " = "
6371 + (vsToTCS ? "gl_in[gl_InvocationID].gl_Position" : "in_tc_attr[gl_InvocationID]") + ";\n"
6373 " gl_TessLevelInner[0] = 2.0;\n"
6374 " gl_TessLevelInner[1] = 3.0;\n"
6376 " gl_TessLevelOuter[0] = 4.0;\n"
6377 " gl_TessLevelOuter[1] = 5.0;\n"
6378 " gl_TessLevelOuter[2] = 6.0;\n"
6379 " gl_TessLevelOuter[3] = 7.0;\n"
6381 std::string tessellationEvaluationTemplate ("${GLSL_VERSION_DECL}\n"
6382 "${TESSELLATION_SHADER_REQUIRE}\n"
6384 + getTessellationEvaluationInLayoutString(TESSPRIMITIVETYPE_TRIANGLES) +
6386 + (!tcsToTES ? "in highp vec4 in_te_attr[];\n" : "") +
6388 "out highp vec4 in_f_color;\n"
6390 "void main (void)\n"
6392 " highp vec2 xy = gl_TessCoord.x * " + tesIn0 + ".xy\n"
6393 " + gl_TessCoord.y * " + tesIn1 + ".xy\n"
6394 " + gl_TessCoord.z * " + tesIn2 + ".xy;\n"
6395 " gl_Position = vec4(xy, 0.0, 1.0);\n"
6396 " in_f_color = vec4(" + tesIn0 + ".z + " + tesIn1 + ".w,\n"
6397 " " + tesIn2 + ".z + " + tesIn0 + ".w,\n"
6398 " " + tesIn1 + ".z + " + tesIn2 + ".w,\n"
6401 std::string fragmentShaderTemplate ("${GLSL_VERSION_DECL}\n"
6403 "layout (location = 0) out mediump vec4 o_color;\n"
6405 "in highp vec4 in_f_color;\n"
6407 "void main (void)\n"
6409 " o_color = in_f_color;\n"
6412 m_program = SharedPtr<const ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
6413 << glu::VertexSource (specializeShader(m_context, vertexShaderTemplate.c_str()))
6414 << glu::TessellationControlSource (specializeShader(m_context, tessellationControlTemplate.c_str()))
6415 << glu::TessellationEvaluationSource (specializeShader(m_context, tessellationEvaluationTemplate.c_str()))
6416 << glu::FragmentSource (specializeShader(m_context, fragmentShaderTemplate.c_str()))));
6418 m_testCtx.getLog() << *m_program;
6419 if (!m_program->isOk())
6420 TCU_FAIL("Program compilation failed");
6423 void GLPositionCase::deinit (void)
6428 GLPositionCase::IterateResult GLPositionCase::iterate (void)
6430 TestLog& log = m_testCtx.getLog();
6431 const RenderContext& renderCtx = m_context.getRenderContext();
6432 const RandomViewport viewport (renderCtx.getRenderTarget(), RENDER_SIZE, RENDER_SIZE, deStringHash(getName()));
6433 const glw::Functions& gl = renderCtx.getFunctions();
6434 const deUint32 programGL = m_program->getProgram();
6436 static const float attributes[3*4] =
6438 -0.8f, -0.7f, 0.1f, 0.7f,
6439 -0.5f, 0.4f, 0.2f, 0.5f,
6440 0.3f, 0.2f, 0.3f, 0.45f
6443 gl.useProgram(programGL);
6444 setViewport(gl, viewport);
6445 gl.patchParameteri(GL_PATCH_VERTICES, 3);
6447 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
6448 gl.clear(GL_COLOR_BUFFER_BIT);
6450 log << TestLog::Message << "Note: input data for in_v_attr:\n" << arrayStr(attributes, 4) << TestLog::EndMessage;
6453 const glu::VertexArrayBinding bindings[] = { glu::va::Float("in_v_attr", 4, 3, 0, &attributes[0]) };
6454 glu::draw(renderCtx, programGL, DE_LENGTH_OF_ARRAY(bindings), &bindings[0], glu::pr::Patches(3));
6457 const tcu::Surface pixels = getPixels(renderCtx, viewport);
6458 const tcu::TextureLevel reference = getPNG(m_testCtx.getArchive(), m_referenceImagePath.c_str());
6459 const bool success = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", reference.getAccess(), pixels.getAccess(), 0.02f, tcu::COMPARE_LOG_RESULT);
6463 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
6469 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
6473 class LimitQueryCase : public TestCase
6476 LimitQueryCase (Context& context, const char* name, const char* desc, glw::GLenum target, int minValue);
6478 IterateResult iterate (void);
6480 const glw::GLenum m_target;
6481 const int m_minValue;
6484 LimitQueryCase::LimitQueryCase (Context& context, const char* name, const char* desc, glw::GLenum target, int minValue)
6485 : TestCase (context, name, desc)
6487 , m_minValue (minValue)
6491 LimitQueryCase::IterateResult LimitQueryCase::iterate (void)
6493 checkTessellationSupport(m_context);
6495 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6496 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6498 gl.enableLogging(true);
6499 verifyStateIntegerMin(result, gl, m_target, m_minValue, QUERY_INTEGER);
6502 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Types", "Alternative queries");
6503 verifyStateIntegerMin(result, gl, m_target, m_minValue, QUERY_BOOLEAN);
6504 verifyStateIntegerMin(result, gl, m_target, m_minValue, QUERY_INTEGER64);
6505 verifyStateIntegerMin(result, gl, m_target, m_minValue, QUERY_FLOAT);
6508 result.setTestContextResult(m_testCtx);
6512 class CombinedUniformLimitCase : public TestCase
6515 CombinedUniformLimitCase (Context& context, const char* name, const char* desc, glw::GLenum combined, glw::GLenum numBlocks, glw::GLenum defaultComponents);
6517 IterateResult iterate (void);
6519 const glw::GLenum m_combined;
6520 const glw::GLenum m_numBlocks;
6521 const glw::GLenum m_defaultComponents;
6524 CombinedUniformLimitCase::CombinedUniformLimitCase (Context& context, const char* name, const char* desc, glw::GLenum combined, glw::GLenum numBlocks, glw::GLenum defaultComponents)
6525 : TestCase (context, name, desc)
6526 , m_combined (combined)
6527 , m_numBlocks (numBlocks)
6528 , m_defaultComponents (defaultComponents)
6532 CombinedUniformLimitCase::IterateResult CombinedUniformLimitCase::iterate (void)
6534 checkTessellationSupport(m_context);
6536 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6537 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6539 gl.enableLogging(true);
6541 m_testCtx.getLog() << tcu::TestLog::Message
6542 << "The minimum value of " << glu::getGettableStateStr(m_combined)
6543 << " is " << glu::getGettableStateStr(m_numBlocks)
6544 << " x MAX_UNIFORM_BLOCK_SIZE / 4 + "
6545 << glu::getGettableStateStr(m_defaultComponents)
6546 << tcu::TestLog::EndMessage;
6548 StateQueryMemoryWriteGuard<glw::GLint> maxUniformBlocks;
6549 gl.glGetIntegerv(m_numBlocks, &maxUniformBlocks);
6550 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "glGetIntegerv");
6552 StateQueryMemoryWriteGuard<glw::GLint> maxUniformBlockSize;
6553 gl.glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUniformBlockSize);
6554 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "glGetIntegerv");
6556 StateQueryMemoryWriteGuard<glw::GLint> maxUniformComponents;
6557 gl.glGetIntegerv(m_defaultComponents, &maxUniformComponents);
6558 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "glGetIntegerv");
6560 if (maxUniformBlocks.verifyValidity(result) && maxUniformBlockSize.verifyValidity(result) && maxUniformComponents.verifyValidity(result))
6562 const int limit = ((int)maxUniformBlocks) * ((int)maxUniformBlockSize) / 4 + (int)maxUniformComponents;
6563 verifyStateIntegerMin(result, gl, m_combined, limit, QUERY_INTEGER);
6566 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Types", "Alternative queries");
6567 verifyStateIntegerMin(result, gl, m_combined, limit, QUERY_BOOLEAN);
6568 verifyStateIntegerMin(result, gl, m_combined, limit, QUERY_INTEGER64);
6569 verifyStateIntegerMin(result, gl, m_combined, limit, QUERY_FLOAT);
6573 result.setTestContextResult(m_testCtx);
6577 class PatchVerticesStateCase : public TestCase
6580 PatchVerticesStateCase (Context& context, const char* name, const char* desc);
6582 IterateResult iterate (void);
6585 PatchVerticesStateCase::PatchVerticesStateCase (Context& context, const char* name, const char* desc)
6586 : TestCase(context, name, desc)
6590 PatchVerticesStateCase::IterateResult PatchVerticesStateCase::iterate (void)
6592 checkTessellationSupport(m_context);
6594 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6595 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6597 gl.enableLogging(true);
6601 const tcu::ScopedLogSection section(m_testCtx.getLog(), "initial", "Initial value");
6603 verifyStateInteger(result, gl, GL_PATCH_VERTICES, 3, QUERY_INTEGER);
6608 const tcu::ScopedLogSection section(m_testCtx.getLog(), "set", "After set");
6610 gl.glPatchParameteri(GL_PATCH_VERTICES, 22);
6611 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "glPatchParameteri");
6613 verifyStateInteger(result, gl, GL_PATCH_VERTICES, 22, QUERY_INTEGER);
6615 const tcu::ScopedLogSection subsection(m_testCtx.getLog(), "Types", "Alternative queries");
6616 verifyStateIntegerMin(result, gl, GL_PATCH_VERTICES, 22, QUERY_BOOLEAN);
6617 verifyStateIntegerMin(result, gl, GL_PATCH_VERTICES, 22, QUERY_INTEGER64);
6618 verifyStateIntegerMin(result, gl, GL_PATCH_VERTICES, 22, QUERY_FLOAT);
6622 result.setTestContextResult(m_testCtx);
6626 class PrimitiveRestartForPatchesSupportedCase : public TestCase
6629 PrimitiveRestartForPatchesSupportedCase (Context& context, const char* name, const char* desc);
6631 IterateResult iterate (void);
6634 PrimitiveRestartForPatchesSupportedCase::PrimitiveRestartForPatchesSupportedCase (Context& context, const char* name, const char* desc)
6635 : TestCase(context, name, desc)
6639 PrimitiveRestartForPatchesSupportedCase::IterateResult PrimitiveRestartForPatchesSupportedCase::iterate (void)
6641 checkTessellationSupport(m_context);
6643 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6644 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6647 gl.enableLogging(true);
6649 queryState(result, gl, QUERY_BOOLEAN, GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED, state);
6651 if (!state.isUndefined())
6653 const tcu::ScopedLogSection subsection(m_testCtx.getLog(), "Types", "Alternative types");
6654 verifyStateBoolean(result, gl, GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED, state.getBoolAccess(), QUERY_INTEGER);
6655 verifyStateBoolean(result, gl, GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED, state.getBoolAccess(), QUERY_INTEGER64);
6656 verifyStateBoolean(result, gl, GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED, state.getBoolAccess(), QUERY_FLOAT);
6659 result.setTestContextResult(m_testCtx);
6663 class TessProgramQueryCase : public TestCase
6666 TessProgramQueryCase (Context& context, const char* name, const char* desc);
6668 std::string getVertexSource (void) const;
6669 std::string getFragmentSource (void) const;
6670 std::string getTessCtrlSource (const char* globalLayouts) const;
6671 std::string getTessEvalSource (const char* globalLayouts) const;
6674 TessProgramQueryCase::TessProgramQueryCase (Context& context, const char* name, const char* desc)
6675 : TestCase(context, name, desc)
6679 std::string TessProgramQueryCase::getVertexSource (void) const
6681 return "${GLSL_VERSION_DECL}\n"
6682 "void main (void)\n"
6684 " gl_Position = vec4(float(gl_VertexID), float(gl_VertexID / 2), 0.0, 1.0);\n"
6688 std::string TessProgramQueryCase::getFragmentSource (void) const
6690 return "${GLSL_VERSION_DECL}\n"
6691 "layout (location = 0) out mediump vec4 o_color;\n"
6692 "void main (void)\n"
6694 " o_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
6698 std::string TessProgramQueryCase::getTessCtrlSource (const char* globalLayouts) const
6700 return "${GLSL_VERSION_DECL}\n"
6701 "${TESSELLATION_SHADER_REQUIRE}\n"
6702 + std::string(globalLayouts) + ";\n"
6703 "void main (void)\n"
6705 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
6706 " gl_TessLevelInner[0] = 2.8;\n"
6707 " gl_TessLevelInner[1] = 2.8;\n"
6708 " gl_TessLevelOuter[0] = 2.8;\n"
6709 " gl_TessLevelOuter[1] = 2.8;\n"
6710 " gl_TessLevelOuter[2] = 2.8;\n"
6711 " gl_TessLevelOuter[3] = 2.8;\n"
6715 std::string TessProgramQueryCase::getTessEvalSource (const char* globalLayouts) const
6717 return "${GLSL_VERSION_DECL}\n"
6718 "${TESSELLATION_SHADER_REQUIRE}\n"
6719 + std::string(globalLayouts) + ";\n"
6720 "void main (void)\n"
6722 " gl_Position = gl_TessCoord.x * gl_in[0].gl_Position\n"
6723 " + gl_TessCoord.y * gl_in[1].gl_Position\n"
6724 " + gl_TessCoord.y * gl_in[2].gl_Position\n"
6725 " + gl_TessCoord.z * gl_in[3].gl_Position;\n"
6729 class TessControlOutputVerticesCase : public TessProgramQueryCase
6732 TessControlOutputVerticesCase (Context& context, const char* name, const char* desc);
6734 IterateResult iterate (void);
6737 TessControlOutputVerticesCase::TessControlOutputVerticesCase (Context& context, const char* name, const char* desc)
6738 : TessProgramQueryCase(context, name, desc)
6742 TessControlOutputVerticesCase::IterateResult TessControlOutputVerticesCase::iterate (void)
6744 checkTessellationSupport(m_context);
6746 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
6747 << glu::VertexSource(specializeShader(m_context, getVertexSource().c_str()))
6748 << glu::FragmentSource(specializeShader(m_context, getFragmentSource().c_str()))
6749 << glu::TessellationControlSource(specializeShader(m_context, getTessCtrlSource("layout(vertices=4) out").c_str()))
6750 << glu::TessellationEvaluationSource(specializeShader(m_context, getTessEvalSource("layout(triangles) in").c_str())));
6752 m_testCtx.getLog() << program;
6753 if (!program.isOk())
6754 throw tcu::TestError("failed to build program");
6757 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6758 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6760 gl.enableLogging(true);
6761 verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_CONTROL_OUTPUT_VERTICES, 4, QUERY_PROGRAM_INTEGER);
6763 result.setTestContextResult(m_testCtx);
6768 class TessGenModeQueryCase : public TessProgramQueryCase
6771 TessGenModeQueryCase (Context& context, const char* name, const char* desc);
6773 IterateResult iterate (void);
6776 TessGenModeQueryCase::TessGenModeQueryCase (Context& context, const char* name, const char* desc)
6777 : TessProgramQueryCase(context, name, desc)
6781 TessGenModeQueryCase::IterateResult TessGenModeQueryCase::iterate (void)
6783 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6784 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6788 const char* description;
6793 { "Triangles", "layout(triangles) in", GL_TRIANGLES },
6794 { "Isolines", "layout(isolines) in", GL_ISOLINES },
6795 { "Quads", "layout(quads) in", GL_QUADS },
6798 checkTessellationSupport(m_context);
6799 gl.enableLogging(true);
6801 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_modes); ++ndx)
6803 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Type", s_modes[ndx].description);
6805 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
6806 << glu::VertexSource(specializeShader(m_context, getVertexSource().c_str()))
6807 << glu::FragmentSource(specializeShader(m_context, getFragmentSource().c_str()))
6808 << glu::TessellationControlSource(specializeShader(m_context, getTessCtrlSource("layout(vertices=6) out").c_str()))
6809 << glu::TessellationEvaluationSource(specializeShader(m_context, getTessEvalSource(s_modes[ndx].layout).c_str())));
6811 m_testCtx.getLog() << program;
6812 if (!program.isOk())
6813 result.fail("failed to build program");
6815 verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_MODE, s_modes[ndx].mode, QUERY_PROGRAM_INTEGER);
6818 result.setTestContextResult(m_testCtx);
6822 class TessGenSpacingQueryCase : public TessProgramQueryCase
6825 TessGenSpacingQueryCase (Context& context, const char* name, const char* desc);
6827 IterateResult iterate (void);
6830 TessGenSpacingQueryCase::TessGenSpacingQueryCase (Context& context, const char* name, const char* desc)
6831 : TessProgramQueryCase(context, name, desc)
6835 TessGenSpacingQueryCase::IterateResult TessGenSpacingQueryCase::iterate (void)
6837 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6838 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6842 const char* description;
6844 glw::GLenum spacing;
6847 { "Default spacing", "layout(triangles) in", GL_EQUAL },
6848 { "Equal spacing", "layout(triangles, equal_spacing) in", GL_EQUAL },
6849 { "Fractional even spacing", "layout(triangles, fractional_even_spacing) in", GL_FRACTIONAL_EVEN },
6850 { "Fractional odd spacing", "layout(triangles, fractional_odd_spacing) in", GL_FRACTIONAL_ODD },
6853 checkTessellationSupport(m_context);
6854 gl.enableLogging(true);
6856 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_modes); ++ndx)
6858 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Type", s_modes[ndx].description);
6860 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
6861 << glu::VertexSource(specializeShader(m_context, getVertexSource().c_str()))
6862 << glu::FragmentSource(specializeShader(m_context, getFragmentSource().c_str()))
6863 << glu::TessellationControlSource(specializeShader(m_context, getTessCtrlSource("layout(vertices=6) out").c_str()))
6864 << glu::TessellationEvaluationSource(specializeShader(m_context, getTessEvalSource(s_modes[ndx].layout).c_str())));
6866 m_testCtx.getLog() << program;
6867 if (!program.isOk())
6868 result.fail("failed to build program");
6870 verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_SPACING, s_modes[ndx].spacing, QUERY_PROGRAM_INTEGER);
6873 result.setTestContextResult(m_testCtx);
6877 class TessGenVertexOrderQueryCase : public TessProgramQueryCase
6880 TessGenVertexOrderQueryCase (Context& context, const char* name, const char* desc);
6882 IterateResult iterate (void);
6885 TessGenVertexOrderQueryCase::TessGenVertexOrderQueryCase (Context& context, const char* name, const char* desc)
6886 : TessProgramQueryCase(context, name, desc)
6890 TessGenVertexOrderQueryCase::IterateResult TessGenVertexOrderQueryCase::iterate (void)
6892 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6893 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6897 const char* description;
6902 { "Default order", "layout(triangles) in", GL_CCW },
6903 { "CW order", "layout(triangles, cw) in", GL_CW },
6904 { "CCW order", "layout(triangles, ccw) in", GL_CCW },
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(specializeShader(m_context, getVertexSource().c_str()))
6916 << glu::FragmentSource(specializeShader(m_context, getFragmentSource().c_str()))
6917 << glu::TessellationControlSource(specializeShader(m_context, getTessCtrlSource("layout(vertices=6) out").c_str()))
6918 << glu::TessellationEvaluationSource(specializeShader(m_context, getTessEvalSource(s_modes[ndx].layout).c_str())));
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_VERTEX_ORDER, s_modes[ndx].order, QUERY_PROGRAM_INTEGER);
6927 result.setTestContextResult(m_testCtx);
6931 class TessGenPointModeQueryCase : public TessProgramQueryCase
6934 TessGenPointModeQueryCase (Context& context, const char* name, const char* desc);
6936 IterateResult iterate (void);
6939 TessGenPointModeQueryCase::TessGenPointModeQueryCase (Context& context, const char* name, const char* desc)
6940 : TessProgramQueryCase(context, name, desc)
6944 TessGenPointModeQueryCase::IterateResult TessGenPointModeQueryCase::iterate (void)
6946 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
6947 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
6951 const char* description;
6956 { "Default mode", "layout(triangles) in", GL_FALSE },
6957 { "Point mode", "layout(triangles, point_mode) in", GL_TRUE },
6960 checkTessellationSupport(m_context);
6961 gl.enableLogging(true);
6963 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_modes); ++ndx)
6965 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Type", s_modes[ndx].description);
6967 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
6968 << glu::VertexSource(specializeShader(m_context, getVertexSource().c_str()))
6969 << glu::FragmentSource(specializeShader(m_context, getFragmentSource().c_str()))
6970 << glu::TessellationControlSource(specializeShader(m_context, getTessCtrlSource("layout(vertices=6) out").c_str()))
6971 << glu::TessellationEvaluationSource(specializeShader(m_context, getTessEvalSource(s_modes[ndx].layout).c_str())));
6973 m_testCtx.getLog() << program;
6974 if (!program.isOk())
6975 result.fail("failed to build program");
6977 verifyStateProgramInteger(result, gl, program.getProgram(), GL_TESS_GEN_POINT_MODE, s_modes[ndx].mode, QUERY_PROGRAM_INTEGER);
6980 result.setTestContextResult(m_testCtx);
6984 class ReferencedByTessellationQueryCase : public TestCase
6987 ReferencedByTessellationQueryCase (Context& context, const char* name, const char* desc, bool isCtrlCase);
6990 IterateResult iterate (void);
6992 std::string getVertexSource (void) const;
6993 std::string getFragmentSource (void) const;
6994 std::string getTessCtrlSource (void) const;
6995 std::string getTessEvalSource (void) const;
6997 const bool m_isCtrlCase;
7000 ReferencedByTessellationQueryCase::ReferencedByTessellationQueryCase (Context& context, const char* name, const char* desc, bool isCtrlCase)
7001 : TestCase (context, name, desc)
7002 , m_isCtrlCase (isCtrlCase)
7006 void ReferencedByTessellationQueryCase::init (void)
7008 checkTessellationSupport(m_context);
7011 ReferencedByTessellationQueryCase::IterateResult ReferencedByTessellationQueryCase::iterate (void)
7013 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
7014 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
7015 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
7016 << glu::VertexSource(specializeShader(m_context, getVertexSource().c_str()))
7017 << glu::FragmentSource(specializeShader(m_context, getFragmentSource().c_str()))
7018 << glu::TessellationControlSource(specializeShader(m_context, getTessCtrlSource().c_str()))
7019 << glu::TessellationEvaluationSource(specializeShader(m_context, getTessEvalSource().c_str())));
7021 gl.enableLogging(true);
7023 m_testCtx.getLog() << program;
7024 if (!program.isOk())
7025 result.fail("failed to build program");
7028 const deUint32 props[1] = { (deUint32)((m_isCtrlCase) ? (GL_REFERENCED_BY_TESS_CONTROL_SHADER) : (GL_REFERENCED_BY_TESS_EVALUATION_SHADER)) };
7031 const tcu::ScopedLogSection section (m_testCtx.getLog(), "UnreferencedUniform", "Unreferenced uniform u_unreferenced");
7032 deUint32 resourcePos;
7033 glw::GLsizei length = 0;
7034 glw::GLint referenced = 0;
7036 resourcePos = gl.glGetProgramResourceIndex(program.getProgram(), GL_UNIFORM, "u_unreferenced");
7037 m_testCtx.getLog() << tcu::TestLog::Message << "u_unreferenced resource index: " << resourcePos << tcu::TestLog::EndMessage;
7039 if (resourcePos == GL_INVALID_INDEX)
7040 result.fail("resourcePos was GL_INVALID_INDEX");
7043 gl.glGetProgramResourceiv(program.getProgram(), GL_UNIFORM, resourcePos, 1, props, 1, &length, &referenced);
7045 << tcu::TestLog::Message
7046 << "Query " << glu::getProgramResourcePropertyStr(props[0])
7047 << ", got " << length << " value(s), value[0] = " << glu::getBooleanStr(referenced)
7048 << tcu::TestLog::EndMessage;
7050 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "query resource");
7052 if (length == 0 || referenced != GL_FALSE)
7053 result.fail("expected GL_FALSE");
7058 const tcu::ScopedLogSection section (m_testCtx.getLog(), "ReferencedUniform", "Referenced uniform u_referenced");
7059 deUint32 resourcePos;
7060 glw::GLsizei length = 0;
7061 glw::GLint referenced = 0;
7063 resourcePos = gl.glGetProgramResourceIndex(program.getProgram(), GL_UNIFORM, "u_referenced");
7064 m_testCtx.getLog() << tcu::TestLog::Message << "u_referenced resource index: " << resourcePos << tcu::TestLog::EndMessage;
7066 if (resourcePos == GL_INVALID_INDEX)
7067 result.fail("resourcePos was GL_INVALID_INDEX");
7070 gl.glGetProgramResourceiv(program.getProgram(), GL_UNIFORM, resourcePos, 1, props, 1, &length, &referenced);
7072 << tcu::TestLog::Message
7073 << "Query " << glu::getProgramResourcePropertyStr(props[0])
7074 << ", got " << length << " value(s), value[0] = " << glu::getBooleanStr(referenced)
7075 << tcu::TestLog::EndMessage;
7077 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query resource");
7079 if (length == 0 || referenced != GL_TRUE)
7080 result.fail("expected GL_TRUE");
7085 result.setTestContextResult(m_testCtx);
7089 std::string ReferencedByTessellationQueryCase::getVertexSource (void) const
7091 return "${GLSL_VERSION_DECL}\n"
7092 "void main (void)\n"
7094 " gl_Position = vec4(float(gl_VertexID), float(gl_VertexID / 2), 0.0, 1.0);\n"
7098 std::string ReferencedByTessellationQueryCase::getFragmentSource (void) const
7100 return "${GLSL_VERSION_DECL}\n"
7101 "layout (location = 0) out mediump vec4 o_color;\n"
7102 "void main (void)\n"
7104 " o_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
7108 std::string ReferencedByTessellationQueryCase::getTessCtrlSource (void) const
7110 std::ostringstream buf;
7111 buf << "${GLSL_VERSION_DECL}\n"
7112 "${TESSELLATION_SHADER_REQUIRE}\n"
7113 "layout(vertices = 3) out;\n"
7114 "uniform highp vec4 " << ((m_isCtrlCase) ? ("u_referenced") : ("u_unreferenced")) << ";\n"
7115 "void main (void)\n"
7117 " vec4 offset = " << ((m_isCtrlCase) ? ("u_referenced") : ("u_unreferenced")) << ";\n"
7118 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position + offset;\n"
7119 " gl_TessLevelInner[0] = 2.8;\n"
7120 " gl_TessLevelInner[1] = 2.8;\n"
7121 " gl_TessLevelOuter[0] = 2.8;\n"
7122 " gl_TessLevelOuter[1] = 2.8;\n"
7123 " gl_TessLevelOuter[2] = 2.8;\n"
7124 " gl_TessLevelOuter[3] = 2.8;\n"
7129 std::string ReferencedByTessellationQueryCase::getTessEvalSource (void) const
7131 std::ostringstream buf;
7132 buf << "${GLSL_VERSION_DECL}\n"
7133 "${TESSELLATION_SHADER_REQUIRE}\n"
7134 "layout(triangles) in;\n"
7135 "uniform highp vec4 " << ((m_isCtrlCase) ? ("u_unreferenced") : ("u_referenced")) << ";\n"
7136 "void main (void)\n"
7138 " vec4 offset = " << ((m_isCtrlCase) ? ("u_unreferenced") : ("u_referenced")) << ";\n"
7139 " gl_Position = gl_TessCoord.x * gl_in[0].gl_Position\n"
7140 " + gl_TessCoord.y * gl_in[1].gl_Position\n"
7141 " + gl_TessCoord.z * gl_in[2].gl_Position\n"
7148 class IsPerPatchQueryCase : public TestCase
7151 IsPerPatchQueryCase (Context& context, const char* name, const char* desc);
7154 IterateResult iterate (void);
7157 IsPerPatchQueryCase::IsPerPatchQueryCase (Context& context, const char* name, const char* desc)
7158 : TestCase(context, name, desc)
7162 void IsPerPatchQueryCase::init (void)
7164 checkTessellationSupport(m_context);
7167 IsPerPatchQueryCase::IterateResult IsPerPatchQueryCase::iterate (void)
7169 static const char* const s_controlSource = "${GLSL_VERSION_DECL}\n"
7170 "${TESSELLATION_SHADER_REQUIRE}\n"
7171 "layout(vertices = 3) out;\n"
7172 "patch out highp vec4 v_perPatch;\n"
7173 "out highp vec4 v_perVertex[];\n"
7174 "void main (void)\n"
7176 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
7177 " v_perPatch = gl_in[0].gl_Position;\n"
7178 " v_perVertex[gl_InvocationID] = -gl_in[gl_InvocationID].gl_Position;\n"
7179 " gl_TessLevelInner[0] = 2.8;\n"
7180 " gl_TessLevelInner[1] = 2.8;\n"
7181 " gl_TessLevelOuter[0] = 2.8;\n"
7182 " gl_TessLevelOuter[1] = 2.8;\n"
7183 " gl_TessLevelOuter[2] = 2.8;\n"
7184 " gl_TessLevelOuter[3] = 2.8;\n"
7186 tcu::ResultCollector result (m_testCtx.getLog(), " // ERROR: ");
7187 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
7188 glu::ShaderProgram program (m_context.getRenderContext(), glu::ProgramSources()
7189 << glu::TessellationControlSource(specializeShader(m_context, s_controlSource))
7190 << glu::ProgramSeparable(true));
7192 gl.enableLogging(true);
7194 m_testCtx.getLog() << program;
7195 if (!program.isOk())
7196 result.fail("failed to build program");
7199 const deUint32 props[1] = { GL_IS_PER_PATCH };
7202 const tcu::ScopedLogSection section (m_testCtx.getLog(), "PerPatchOutput", "Per patch v_perPatch");
7203 deUint32 resourcePos;
7204 glw::GLsizei length = 0;
7205 glw::GLint referenced = 0;
7207 resourcePos = gl.glGetProgramResourceIndex(program.getProgram(), GL_PROGRAM_OUTPUT, "v_perPatch");
7208 m_testCtx.getLog() << tcu::TestLog::Message << "v_perPatch resource index: " << resourcePos << tcu::TestLog::EndMessage;
7210 if (resourcePos == GL_INVALID_INDEX)
7211 result.fail("resourcePos was GL_INVALID_INDEX");
7214 gl.glGetProgramResourceiv(program.getProgram(), GL_PROGRAM_OUTPUT, resourcePos, 1, props, 1, &length, &referenced);
7216 << tcu::TestLog::Message
7217 << "Query " << glu::getProgramResourcePropertyStr(props[0])
7218 << ", got " << length << " value(s), value[0] = " << glu::getBooleanStr(referenced)
7219 << tcu::TestLog::EndMessage;
7221 GLS_COLLECT_GL_ERROR(result, gl.glGetError(), "query resource");
7223 if (length == 0 || referenced != GL_TRUE)
7224 result.fail("expected GL_TRUE");
7229 const tcu::ScopedLogSection section (m_testCtx.getLog(), "PerVertexhOutput", "Per vertex v_perVertex");
7230 deUint32 resourcePos;
7231 glw::GLsizei length = 0;
7232 glw::GLint referenced = 0;
7234 resourcePos = gl.glGetProgramResourceIndex(program.getProgram(), GL_PROGRAM_OUTPUT, "v_perVertex");
7235 m_testCtx.getLog() << tcu::TestLog::Message << "v_perVertex resource index: " << resourcePos << tcu::TestLog::EndMessage;
7237 if (resourcePos == GL_INVALID_INDEX)
7238 result.fail("resourcePos was GL_INVALID_INDEX");
7241 gl.glGetProgramResourceiv(program.getProgram(), GL_PROGRAM_OUTPUT, resourcePos, 1, props, 1, &length, &referenced);
7243 << tcu::TestLog::Message
7244 << "Query " << glu::getProgramResourcePropertyStr(props[0])
7245 << ", got " << length << " value(s), value[0] = " << glu::getBooleanStr(referenced)
7246 << tcu::TestLog::EndMessage;
7248 GLU_EXPECT_NO_ERROR(gl.glGetError(), "query resource");
7250 if (length == 0 || referenced != GL_FALSE)
7251 result.fail("expected GL_FALSE");
7256 result.setTestContextResult(m_testCtx);
7262 TessellationTests::TessellationTests (Context& context)
7263 : TestCaseGroup(context, "tessellation", "Tessellation Tests")
7267 TessellationTests::~TessellationTests (void)
7271 void TessellationTests::init (void)
7274 tcu::TestCaseGroup* const queryGroup = new tcu::TestCaseGroup(m_testCtx, "state_query", "Query tests");
7275 addChild(queryGroup);
7278 queryGroup->addChild(new LimitQueryCase(m_context, "max_patch_vertices", "Test MAX_PATCH_VERTICES", GL_MAX_PATCH_VERTICES, 32));
7279 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_gen_level", "Test MAX_TESS_GEN_LEVEL", GL_MAX_TESS_GEN_LEVEL, 64));
7280 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_uniform_components", "Test MAX_TESS_CONTROL_UNIFORM_COMPONENTS", GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS, 1024));
7281 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_uniform_components", "Test MAX_TESS_EVALUATION_UNIFORM_COMPONENTS", GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS, 1024));
7282 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_texture_image_units", "Test MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS", GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS, 16));
7283 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_texture_image_units", "Test MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS", GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS, 16));
7284 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_output_components", "Test MAX_TESS_CONTROL_OUTPUT_COMPONENTS", GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS, 64));
7285 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_patch_components", "Test MAX_TESS_PATCH_COMPONENTS", GL_MAX_TESS_PATCH_COMPONENTS, 120));
7286 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_total_output_components", "Test MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS", GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS, 2048));
7287 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_output_components", "Test MAX_TESS_EVALUATION_OUTPUT_COMPONENTS", GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS, 64));
7288 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_uniform_blocks", "Test MAX_TESS_CONTROL_UNIFORM_BLOCKS", GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS, 12));
7289 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_uniform_blocks", "Test MAX_TESS_EVALUATION_UNIFORM_BLOCKS", GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS, 12));
7290 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_input_components", "Test MAX_TESS_CONTROL_INPUT_COMPONENTS", GL_MAX_TESS_CONTROL_INPUT_COMPONENTS, 64));
7291 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_input_components", "Test MAX_TESS_EVALUATION_INPUT_COMPONENTS", GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS, 64));
7292 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_atomic_counter_buffers", "Test MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS", GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS, 0));
7293 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_atomic_counter_buffers", "Test MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS", GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS, 0));
7294 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_atomic_counters", "Test MAX_TESS_CONTROL_ATOMIC_COUNTERS", GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS, 0));
7295 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_atomic_counters", "Test MAX_TESS_EVALUATION_ATOMIC_COUNTERS", GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS, 0));
7296 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_image_uniforms", "Test MAX_TESS_CONTROL_IMAGE_UNIFORMS", GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, 0));
7297 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_image_uniforms", "Test MAX_TESS_EVALUATION_IMAGE_UNIFORMS", GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS, 0));
7298 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_control_shader_storage_blocks", "Test MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS", GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS, 0));
7299 queryGroup->addChild(new LimitQueryCase(m_context, "max_tess_evaluation_shader_storage_blocks", "Test MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS", GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS, 0));
7302 queryGroup->addChild(new LimitQueryCase(m_context, "max_uniform_buffer_bindings", "Test MAX_UNIFORM_BUFFER_BINDINGS", GL_MAX_UNIFORM_BUFFER_BINDINGS, 72));
7303 queryGroup->addChild(new LimitQueryCase(m_context, "max_combined_uniform_blocks", "Test MAX_COMBINED_UNIFORM_BLOCKS", GL_MAX_COMBINED_UNIFORM_BLOCKS, 60));
7304 queryGroup->addChild(new LimitQueryCase(m_context, "max_combined_texture_image_units", "Test MAX_COMBINED_TEXTURE_IMAGE_UNITS", GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, 96));
7307 queryGroup->addChild(new CombinedUniformLimitCase(m_context, "max_combined_tess_control_uniform_components", "Test MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS", GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS, GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS, GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS));
7308 queryGroup->addChild(new CombinedUniformLimitCase(m_context, "max_combined_tess_evaluation_uniform_components", "Test MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS", GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS, GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS, GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS));
7311 queryGroup->addChild(new PrimitiveRestartForPatchesSupportedCase(m_context, "primitive_restart_for_patches_supported", "Test PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED"));
7314 queryGroup->addChild(new PatchVerticesStateCase(m_context, "patch_vertices", "Test PATCH_VERTICES"));
7317 queryGroup->addChild(new TessControlOutputVerticesCase (m_context, "tess_control_output_vertices", "Test TESS_CONTROL_OUTPUT_VERTICES"));
7318 queryGroup->addChild(new TessGenModeQueryCase (m_context, "tess_gen_mode", "Test TESS_GEN_MODE"));
7319 queryGroup->addChild(new TessGenSpacingQueryCase (m_context, "tess_gen_spacing", "Test TESS_GEN_SPACING"));
7320 queryGroup->addChild(new TessGenVertexOrderQueryCase (m_context, "tess_gen_vertex_order", "Test TESS_GEN_VERTEX_ORDER"));
7321 queryGroup->addChild(new TessGenPointModeQueryCase (m_context, "tess_gen_point_mode", "Test TESS_GEN_POINT_MODE"));
7324 queryGroup->addChild(new ReferencedByTessellationQueryCase (m_context, "referenced_by_tess_control_shader", "Test REFERENCED_BY_TESS_CONTROL_SHADER", true));
7325 queryGroup->addChild(new ReferencedByTessellationQueryCase (m_context, "referenced_by_tess_evaluation_shader", "Test REFERENCED_BY_TESS_EVALUATION_SHADER", false));
7326 queryGroup->addChild(new IsPerPatchQueryCase (m_context, "is_per_patch", "Test IS_PER_PATCH"));
7330 TestCaseGroup* const tessCoordGroup = new TestCaseGroup(m_context, "tesscoord", "Get tessellation coordinates with transform feedback and validate them");
7331 addChild(tessCoordGroup);
7333 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7335 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7337 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7338 tessCoordGroup->addChild(new TessCoordCase(m_context,
7339 (string() + getTessPrimitiveTypeShaderName(primitiveType) + "_" + getSpacingModeShaderName((SpacingMode)spacingI)).c_str(), "",
7340 primitiveType, (SpacingMode)spacingI));
7345 TestCaseGroup* const windingGroup = new TestCaseGroup(m_context, "winding", "Test the cw and ccw input layout qualifiers");
7346 addChild(windingGroup);
7348 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7350 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7351 if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
7354 for (int windingI = 0; windingI < WINDING_LAST; windingI++)
7356 const Winding winding = (Winding)windingI;
7357 windingGroup->addChild(new WindingCase(m_context, (string() + getTessPrimitiveTypeShaderName(primitiveType) + "_" + getWindingShaderName(winding)).c_str(), "", primitiveType, winding));
7363 TestCaseGroup* const shaderInputOutputGroup = new TestCaseGroup(m_context, "shader_input_output", "Test tessellation control and evaluation shader inputs and outputs");
7364 addChild(shaderInputOutputGroup);
7371 } patchVertexCountCases[] =
7377 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(patchVertexCountCases); caseNdx++)
7379 const int inSize = patchVertexCountCases[caseNdx].inPatchSize;
7380 const int outSize = patchVertexCountCases[caseNdx].outPatchSize;
7382 const string caseName = "patch_vertices_" + de::toString(inSize) + "_in_" + de::toString(outSize) + "_out";
7384 shaderInputOutputGroup->addChild(new PatchVertexCountCase(m_context, caseName.c_str(), "Test input and output patch vertex counts", inSize, outSize,
7385 ("data/tessellation/" + caseName + "_ref.png").c_str()));
7389 for (int caseTypeI = 0; caseTypeI < PerPatchDataCase::CASETYPE_LAST; caseTypeI++)
7391 const PerPatchDataCase::CaseType caseType = (PerPatchDataCase::CaseType)caseTypeI;
7392 const char* const caseName = PerPatchDataCase::getCaseTypeName(caseType);
7394 shaderInputOutputGroup->addChild(new PerPatchDataCase(m_context, caseName, PerPatchDataCase::getCaseTypeDescription(caseType), caseType,
7395 PerPatchDataCase::caseTypeUsesRefImageFromFile(caseType) ? (string() + "data/tessellation/" + caseName + "_ref.png").c_str() : DE_NULL));
7398 for (int caseTypeI = 0; caseTypeI < GLPositionCase::CASETYPE_LAST; caseTypeI++)
7400 const GLPositionCase::CaseType caseType = (GLPositionCase::CaseType)caseTypeI;
7401 const char* const caseName = GLPositionCase::getCaseTypeName(caseType);
7403 shaderInputOutputGroup->addChild(new GLPositionCase(m_context, caseName, "", caseType, "data/tessellation/gl_position_ref.png"));
7406 shaderInputOutputGroup->addChild(new BarrierCase(m_context, "barrier", "Basic barrier usage", "data/tessellation/barrier_ref.png"));
7410 TestCaseGroup* const miscDrawGroup = new TestCaseGroup(m_context, "misc_draw", "Miscellaneous draw-result-verifying cases");
7411 addChild(miscDrawGroup);
7413 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7415 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7416 if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
7419 const char* const primTypeName = getTessPrimitiveTypeShaderName(primitiveType);
7421 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7423 const string caseName = string() + "fill_cover_" + primTypeName + "_" + getSpacingModeShaderName((SpacingMode)spacingI);
7425 miscDrawGroup->addChild(new BasicTriangleFillCoverCase(m_context,
7426 caseName.c_str(), "Check that there are no obvious gaps in the triangle-filled area of a tessellated shape",
7427 primitiveType, (SpacingMode)spacingI,
7428 ("data/tessellation/" + caseName + "_ref").c_str()));
7432 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7434 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7435 if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
7438 const char* const primTypeName = getTessPrimitiveTypeShaderName(primitiveType);
7440 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7442 const string caseName = string() + "fill_overlap_" + primTypeName + "_" + getSpacingModeShaderName((SpacingMode)spacingI);
7444 miscDrawGroup->addChild(new BasicTriangleFillNonOverlapCase(m_context,
7445 caseName.c_str(), "Check that there are no obvious triangle overlaps in the triangle-filled area of a tessellated shape",
7446 primitiveType, (SpacingMode)spacingI,
7447 ("data/tessellation/" + caseName + "_ref").c_str()));
7451 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7453 const string caseName = string() + "isolines_" + getSpacingModeShaderName((SpacingMode)spacingI);
7455 miscDrawGroup->addChild(new IsolinesRenderCase(m_context,
7456 caseName.c_str(), "Basic isolines render test",
7457 (SpacingMode)spacingI,
7458 ("data/tessellation/" + caseName + "_ref").c_str()));
7463 TestCaseGroup* const commonEdgeGroup = new TestCaseGroup(m_context, "common_edge", "Draw multiple adjacent shapes and check that no cracks appear between them");
7464 addChild(commonEdgeGroup);
7466 for (int caseTypeI = 0; caseTypeI < CommonEdgeCase::CASETYPE_LAST; caseTypeI++)
7468 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7470 const CommonEdgeCase::CaseType caseType = (CommonEdgeCase::CaseType)caseTypeI;
7471 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7472 if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
7475 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7477 const SpacingMode spacing = (SpacingMode)spacingI;
7478 const string caseName = (string() + getTessPrimitiveTypeShaderName(primitiveType)
7479 + "_" + getSpacingModeShaderName(spacing)
7480 + (caseType == CommonEdgeCase::CASETYPE_BASIC ? ""
7481 : caseType == CommonEdgeCase::CASETYPE_PRECISE ? "_precise"
7484 commonEdgeGroup->addChild(new CommonEdgeCase(m_context, caseName.c_str(), "", primitiveType, spacing, caseType));
7491 TestCaseGroup* const fractionalSpacingModeGroup = new TestCaseGroup(m_context, "fractional_spacing", "Test fractional spacing modes");
7492 addChild(fractionalSpacingModeGroup);
7494 fractionalSpacingModeGroup->addChild(new FractionalSpacingModeCase(m_context, "odd", "", SPACINGMODE_FRACTIONAL_ODD));
7495 fractionalSpacingModeGroup->addChild(new FractionalSpacingModeCase(m_context, "even", "", SPACINGMODE_FRACTIONAL_EVEN));
7499 TestCaseGroup* const primitiveDiscardGroup = new TestCaseGroup(m_context, "primitive_discard", "Test primitive discard with relevant outer tessellation level <= 0.0");
7500 addChild(primitiveDiscardGroup);
7502 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7504 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7506 for (int windingI = 0; windingI < WINDING_LAST; windingI++)
7508 for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++)
7510 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7511 const SpacingMode spacing = (SpacingMode)spacingI;
7512 const Winding winding = (Winding)windingI;
7513 const bool usePointMode = usePointModeI != 0;
7515 primitiveDiscardGroup->addChild(new PrimitiveDiscardCase(m_context, (string() + getTessPrimitiveTypeShaderName(primitiveType)
7516 + "_" + getSpacingModeShaderName(spacing)
7517 + "_" + getWindingShaderName(winding)
7518 + (usePointMode ? "_point_mode" : "")).c_str(), "",
7519 primitiveType, spacing, winding, usePointMode));
7527 TestCaseGroup* const invarianceGroup = new TestCaseGroup(m_context, "invariance", "Test tessellation invariance rules");
7529 TestCaseGroup* const invariantPrimitiveSetGroup = new TestCaseGroup(m_context, "primitive_set", "Test invariance rule #1");
7530 TestCaseGroup* const invariantOuterEdgeGroup = new TestCaseGroup(m_context, "outer_edge_division", "Test invariance rule #2");
7531 TestCaseGroup* const symmetricOuterEdgeGroup = new TestCaseGroup(m_context, "outer_edge_symmetry", "Test invariance rule #3");
7532 TestCaseGroup* const outerEdgeVertexSetIndexIndependenceGroup = new TestCaseGroup(m_context, "outer_edge_index_independence", "Test invariance rule #4");
7533 TestCaseGroup* const invariantTriangleSetGroup = new TestCaseGroup(m_context, "triangle_set", "Test invariance rule #5");
7534 TestCaseGroup* const invariantInnerTriangleSetGroup = new TestCaseGroup(m_context, "inner_triangle_set", "Test invariance rule #6");
7535 TestCaseGroup* const invariantOuterTriangleSetGroup = new TestCaseGroup(m_context, "outer_triangle_set", "Test invariance rule #7");
7536 TestCaseGroup* const tessCoordComponentRangeGroup = new TestCaseGroup(m_context, "tess_coord_component_range", "Test invariance rule #8, first part");
7537 TestCaseGroup* const oneMinusTessCoordComponentGroup = new TestCaseGroup(m_context, "one_minus_tess_coord_component", "Test invariance rule #8, second part");
7539 addChild(invarianceGroup);
7540 invarianceGroup->addChild(invariantPrimitiveSetGroup);
7541 invarianceGroup->addChild(invariantOuterEdgeGroup);
7542 invarianceGroup->addChild(symmetricOuterEdgeGroup);
7543 invarianceGroup->addChild(outerEdgeVertexSetIndexIndependenceGroup);
7544 invarianceGroup->addChild(invariantTriangleSetGroup);
7545 invarianceGroup->addChild(invariantInnerTriangleSetGroup);
7546 invarianceGroup->addChild(invariantOuterTriangleSetGroup);
7547 invarianceGroup->addChild(tessCoordComponentRangeGroup);
7548 invarianceGroup->addChild(oneMinusTessCoordComponentGroup);
7550 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7552 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7553 const string primName = getTessPrimitiveTypeShaderName(primitiveType);
7554 const bool triOrQuad = primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS;
7556 for (int spacingI = 0; spacingI < SPACINGMODE_LAST; spacingI++)
7558 const SpacingMode spacing = (SpacingMode)spacingI;
7559 const string primSpacName = primName + "_" + getSpacingModeShaderName(spacing);
7563 invariantOuterEdgeGroup->addChild (new InvariantOuterEdgeCase (m_context, primSpacName.c_str(), "", primitiveType, spacing));
7564 invariantTriangleSetGroup->addChild (new InvariantTriangleSetCase (m_context, primSpacName.c_str(), "", primitiveType, spacing));
7565 invariantInnerTriangleSetGroup->addChild(new InvariantInnerTriangleSetCase (m_context, primSpacName.c_str(), "", primitiveType, spacing));
7566 invariantOuterTriangleSetGroup->addChild(new InvariantOuterTriangleSetCase (m_context, primSpacName.c_str(), "", primitiveType, spacing));
7569 for (int windingI = 0; windingI < WINDING_LAST; windingI++)
7571 const Winding winding = (Winding)windingI;
7572 const string primSpacWindName = primSpacName + "_" + getWindingShaderName(winding);
7574 for (int usePointModeI = 0; usePointModeI <= 1; usePointModeI++)
7576 const bool usePointMode = usePointModeI != 0;
7577 const string primSpacWindPointName = primSpacWindName + (usePointMode ? "_point_mode" : "");
7579 invariantPrimitiveSetGroup->addChild (new InvariantPrimitiveSetCase (m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode));
7580 symmetricOuterEdgeGroup->addChild (new SymmetricOuterEdgeCase (m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode));
7581 tessCoordComponentRangeGroup->addChild (new TessCoordComponentRangeCase (m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode));
7582 oneMinusTessCoordComponentGroup->addChild (new OneMinusTessCoordComponentCase (m_context, primSpacWindPointName.c_str(), "", primitiveType, spacing, winding, usePointMode));
7585 outerEdgeVertexSetIndexIndependenceGroup->addChild(new OuterEdgeVertexSetIndexIndependenceCase(m_context, primSpacWindPointName.c_str(), "",
7586 primitiveType, spacing, winding, usePointMode));
7597 const char* description;
7598 UserDefinedIOCase::IOType ioType;
7601 { "per_patch", "Per-patch TCS outputs", UserDefinedIOCase::IO_TYPE_PER_PATCH },
7602 { "per_patch_array", "Per-patch array TCS outputs", UserDefinedIOCase::IO_TYPE_PER_PATCH_ARRAY },
7603 { "per_patch_block", "Per-patch TCS outputs in IO block", UserDefinedIOCase::IO_TYPE_PER_PATCH_BLOCK },
7604 { "per_patch_block_array", "Per-patch TCS outputs in IO block array", UserDefinedIOCase::IO_TYPE_PER_PATCH_BLOCK_ARRAY },
7605 { "per_vertex", "Per-vertex TCS outputs", UserDefinedIOCase::IO_TYPE_PER_VERTEX },
7606 { "per_vertex_block", "Per-vertex TCS outputs in IO block", UserDefinedIOCase::IO_TYPE_PER_VERTEX_BLOCK },
7609 TestCaseGroup* const userDefinedIOGroup = new TestCaseGroup(m_context, "user_defined_io", "Test non-built-in per-patch and per-vertex inputs and outputs");
7610 addChild(userDefinedIOGroup);
7612 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(ioCases); ++ndx)
7614 TestCaseGroup* const ioTypeGroup = new TestCaseGroup(m_context, ioCases[ndx].name, ioCases[ndx].description);
7615 userDefinedIOGroup->addChild(ioTypeGroup);
7617 for (int vertexArraySizeI = 0; vertexArraySizeI < UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_LAST; vertexArraySizeI++)
7619 const UserDefinedIOCase::VertexIOArraySize vertexArraySize = (UserDefinedIOCase::VertexIOArraySize)vertexArraySizeI;
7620 TestCaseGroup* const vertexArraySizeGroup = new TestCaseGroup(m_context,
7621 vertexArraySizeI == UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_IMPLICIT
7622 ? "vertex_io_array_size_implicit"
7623 : vertexArraySizeI == UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN
7624 ? "vertex_io_array_size_shader_builtin"
7625 : vertexArraySizeI == UserDefinedIOCase::VERTEX_IO_ARRAY_SIZE_EXPLICIT_QUERY
7626 ? "vertex_io_array_size_query"
7629 ioTypeGroup->addChild(vertexArraySizeGroup);
7631 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7633 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7634 vertexArraySizeGroup->addChild(new UserDefinedIOCase(m_context, getTessPrimitiveTypeShaderName(primitiveType), "", primitiveType, ioCases[ndx].ioType, vertexArraySize, UserDefinedIOCase::TESS_CONTROL_OUT_ARRAY_SIZE_IMPLICIT,
7635 (string() + "data/tessellation/user_defined_io_" + getTessPrimitiveTypeShaderName(primitiveType) + "_ref.png").c_str()));
7638 if (ioCases[ndx].ioType == UserDefinedIOCase::IO_TYPE_PER_VERTEX
7639 || ioCases[ndx].ioType == UserDefinedIOCase::IO_TYPE_PER_VERTEX_BLOCK)
7641 for (int primitiveTypeI = 0; primitiveTypeI < TESSPRIMITIVETYPE_LAST; primitiveTypeI++)
7643 const TessPrimitiveType primitiveType = (TessPrimitiveType)primitiveTypeI;
7644 vertexArraySizeGroup->addChild(new UserDefinedIOCase(m_context, (string(getTessPrimitiveTypeShaderName(primitiveType)) + "_explicit_tcs_out_size").c_str(), "", primitiveType, ioCases[ndx].ioType, vertexArraySize, UserDefinedIOCase::TESS_CONTROL_OUT_ARRAY_SIZE_LAYOUT,
7645 (string() + "data/tessellation/user_defined_io_" + getTessPrimitiveTypeShaderName(primitiveType) + "_ref.png").c_str()));
7652 de::MovePtr<TestCaseGroup> negativeGroup (new TestCaseGroup(m_context, "negative", "Negative cases"));
7655 de::MovePtr<TestCaseGroup> es31Group (new TestCaseGroup(m_context, "es31", "GLSL ES 3.1 Negative cases"));
7656 gls::ShaderLibrary shaderLibrary (m_testCtx, m_context.getRenderContext(), m_context.getContextInfo());
7657 const std::vector<tcu::TestNode*> children = shaderLibrary.loadShaderFile("shaders/es31/tessellation_negative_user_defined_io.test");
7659 for (int i = 0; i < (int)children.size(); i++)
7660 es31Group->addChild(children[i]);
7662 negativeGroup->addChild(es31Group.release());
7666 de::MovePtr<TestCaseGroup> es32Group (new TestCaseGroup(m_context, "es32", "GLSL ES 3.2 Negative cases"));
7667 gls::ShaderLibrary shaderLibrary (m_testCtx, m_context.getRenderContext(), m_context.getContextInfo());
7668 const std::vector<tcu::TestNode*> children = shaderLibrary.loadShaderFile("shaders/es32/tessellation_negative_user_defined_io.test");
7670 for (int i = 0; i < (int)children.size(); i++)
7671 es32Group->addChild(children[i]);
7673 negativeGroup->addChild(es32Group.release());
7676 userDefinedIOGroup->addChild(negativeGroup.release());