/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.1 Module * ------------------------------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Tessellation Tests. *//*--------------------------------------------------------------------*/ #include "es31fTessellationTests.hpp" #include "glsTextureTestUtil.hpp" #include "glsShaderLibrary.hpp" #include "glsStateQueryUtil.hpp" #include "gluShaderProgram.hpp" #include "gluRenderContext.hpp" #include "gluPixelTransfer.hpp" #include "gluDrawUtil.hpp" #include "gluObjectWrapper.hpp" #include "gluStrUtil.hpp" #include "gluContextInfo.hpp" #include "gluVarType.hpp" #include "gluVarTypeUtil.hpp" #include "gluCallLogWrapper.hpp" #include "tcuTestLog.hpp" #include "tcuRenderTarget.hpp" #include "tcuStringTemplate.hpp" #include "tcuSurface.hpp" #include "tcuTextureUtil.hpp" #include "tcuVectorUtil.hpp" #include "tcuImageIO.hpp" #include "tcuResource.hpp" #include "tcuImageCompare.hpp" #include "deRandom.hpp" #include "deStringUtil.hpp" #include "deSharedPtr.hpp" #include "deUniquePtr.hpp" #include "deString.h" #include "deMath.h" #include "glwEnums.hpp" #include "glwDefs.hpp" #include "glwFunctions.hpp" #include #include #include #include #include #include using glu::ShaderProgram; using glu::RenderContext; using tcu::RenderTarget; using tcu::TestLog; using tcu::Vec2; using tcu::Vec3; using tcu::Vec4; using de::Random; using de::SharedPtr; using std::vector; using std::string; using namespace glw; // For GL types. namespace deqp { using gls::TextureTestUtil::RandomViewport; namespace gles31 { namespace Functional { using namespace gls::StateQueryUtil; enum { MINIMUM_MAX_TESS_GEN_LEVEL = 64 //!< GL-defined minimum for GL_MAX_TESS_GEN_LEVEL. }; static inline bool vec3XLessThan (const Vec3& a, const Vec3& b) { return a.x() < b.x(); } template static string elemsStr (const IterT& begin, const IterT& end, int wrapLengthParam = 0, int numIndentationSpaces = 0) { const string baseIndentation = string(numIndentationSpaces, ' '); const string deepIndentation = baseIndentation + string(4, ' '); const int wrapLength = wrapLengthParam > 0 ? wrapLengthParam : std::numeric_limits::max(); const int length = (int)std::distance(begin, end); string result; if (length > wrapLength) result += "(amount: " + de::toString(length) + ") "; result += string() + "{" + (length > wrapLength ? "\n"+deepIndentation : " "); { int index = 0; for (IterT it = begin; it != end; ++it) { if (it != begin) result += string() + ", " + (index % wrapLength == 0 ? "\n"+deepIndentation : ""); result += de::toString(*it); index++; } result += length > wrapLength ? "\n"+baseIndentation : " "; } result += "}"; return result; } template static string containerStr (const ContainerT& c, int wrapLengthParam = 0, int numIndentationSpaces = 0) { return elemsStr(c.begin(), c.end(), wrapLengthParam, numIndentationSpaces); } template static string arrayStr (const T (&arr)[N], int wrapLengthParam = 0, int numIndentationSpaces = 0) { return elemsStr(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr), wrapLengthParam, numIndentationSpaces); } template static T arrayMax (const T (&arr)[N]) { return *std::max_element(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr)); } template static vector members (const vector& objs, MembT T::* membP) { vector result(objs.size()); for (int i = 0; i < (int)objs.size(); i++) result[i] = objs[i].*membP; return result; } template static vector arrayToVector (const T (&arr)[N]) { return vector(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr)); } template static inline bool contains (const ContainerT& c, const T& key) { return c.find(key) != c.end(); } template static inline tcu::Vector singleTrueMask (int index) { DE_ASSERT(de::inBounds(index, 0, Size)); tcu::Vector result; result[index] = true; return result; } static int intPow (int base, int exp) { DE_ASSERT(exp >= 0); if (exp == 0) return 1; else { const int sub = intPow(base, exp/2); if (exp % 2 == 0) return sub*sub; else return sub*sub*base; } } tcu::Surface getPixels (const glu::RenderContext& rCtx, int x, int y, int width, int height) { tcu::Surface result(width, height); glu::readPixels(rCtx, x, y, result.getAccess()); return result; } tcu::Surface getPixels (const glu::RenderContext& rCtx, const RandomViewport& vp) { return getPixels(rCtx, vp.x, vp.y, vp.width, vp.height); } static inline void checkRenderTargetSize (const RenderTarget& renderTarget, int minSize) { if (renderTarget.getWidth() < minSize || renderTarget.getHeight() < minSize) throw tcu::NotSupportedError("Render target width and height must be at least " + de::toString(minSize)); } tcu::TextureLevel getPNG (const tcu::Archive& archive, const string& filename) { tcu::TextureLevel result; tcu::ImageIO::loadPNG(result, archive, filename.c_str()); return result; } static int numBasicSubobjects (const glu::VarType& type) { if (type.isBasicType()) return 1; else if (type.isArrayType()) return type.getArraySize()*numBasicSubobjects(type.getElementType()); else if (type.isStructType()) { const glu::StructType& structType = *type.getStructPtr(); int result = 0; for (int i = 0; i < structType.getNumMembers(); i++) result += numBasicSubobjects(structType.getMember(i).getType()); return result; } else { DE_ASSERT(false); return -1; } } static inline int numVerticesPerPrimitive (deUint32 primitiveTypeGL) { switch (primitiveTypeGL) { case GL_POINTS: return 1; case GL_TRIANGLES: return 3; case GL_LINES: return 2; default: DE_ASSERT(false); return -1; } } static inline void setViewport (const glw::Functions& gl, const RandomViewport& vp) { gl.viewport(vp.x, vp.y, vp.width, vp.height); } static inline deUint32 getQueryResult (const glw::Functions& gl, deUint32 queryObject) { deUint32 result = (deUint32)-1; gl.getQueryObjectuiv(queryObject, GL_QUERY_RESULT, &result); TCU_CHECK(result != (deUint32)-1); return result; } template static void readDataMapped (const glw::Functions& gl, deUint32 bufferTarget, int numElems, T* dst) { const int numBytes = numElems*(int)sizeof(T); const T* const mappedData = (const T*)gl.mapBufferRange(bufferTarget, 0, numBytes, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), (string() + "glMapBufferRange(" + glu::getBufferTargetName((int)bufferTarget) + ", 0, " + de::toString(numBytes) + ", GL_MAP_READ_BIT)").c_str()); TCU_CHECK(mappedData != DE_NULL); for (int i = 0; i < numElems; i++) dst[i] = mappedData[i]; gl.unmapBuffer(bufferTarget); } template static vector readDataMapped (const glw::Functions& gl, deUint32 bufferTarget, int numElems) { vector result(numElems); readDataMapped(gl, bufferTarget, numElems, &result[0]); return result; } namespace { template struct ConstantUnaryPredicate { bool operator() (const ArgT&) const { return res; } }; //! Helper for handling simple, one-varying transform feedbacks. template class TransformFeedbackHandler { public: struct Result { int numPrimitives; vector varying; Result (void) : numPrimitives(-1) {} Result (int n, const vector& v) : numPrimitives(n), varying(v) {} }; TransformFeedbackHandler (const glu::RenderContext& renderCtx, int maxNumVertices); Result renderAndGetPrimitives (deUint32 programGL, deUint32 tfPrimTypeGL, int numBindings, const glu::VertexArrayBinding* bindings, int numVertices) const; private: const glu::RenderContext& m_renderCtx; const glu::TransformFeedback m_tf; const glu::Buffer m_tfBuffer; const glu::Query m_tfPrimQuery; }; template TransformFeedbackHandler::TransformFeedbackHandler (const glu::RenderContext& renderCtx, int maxNumVertices) : m_renderCtx (renderCtx) , m_tf (renderCtx) , m_tfBuffer (renderCtx) , m_tfPrimQuery (renderCtx) { const glw::Functions& gl = m_renderCtx.getFunctions(); // \note Room for 1 extra triangle, to detect if GL returns too many primitives. const int bufferSize = (maxNumVertices + 3) * (int)sizeof(AttribType); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, *m_tfBuffer); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bufferSize, DE_NULL, GL_DYNAMIC_READ); } template typename TransformFeedbackHandler::Result TransformFeedbackHandler::renderAndGetPrimitives (deUint32 programGL, deUint32 tfPrimTypeGL, int numBindings, const glu::VertexArrayBinding* bindings, int numVertices) const { DE_ASSERT(tfPrimTypeGL == GL_POINTS || tfPrimTypeGL == GL_LINES || tfPrimTypeGL == GL_TRIANGLES); const glw::Functions& gl = m_renderCtx.getFunctions(); gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, *m_tf); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, *m_tfBuffer); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, *m_tfBuffer); gl.beginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, *m_tfPrimQuery); gl.beginTransformFeedback(tfPrimTypeGL); glu::draw(m_renderCtx, programGL, numBindings, bindings, glu::pr::Patches(numVertices)); GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed"); gl.endTransformFeedback(); gl.endQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); { const int numPrimsWritten = (int)getQueryResult(gl, *m_tfPrimQuery); return Result(numPrimsWritten, readDataMapped(gl, GL_TRANSFORM_FEEDBACK_BUFFER, numPrimsWritten * numVerticesPerPrimitive(tfPrimTypeGL))); } } template class SizeLessThan { public: bool operator() (const T& a, const T& b) const { return a.size() < b.size(); } }; //! Predicate functor for comparing structs by their members. template class MemberPred { public: MemberPred (MembT T::* membP) : m_membP(membP), m_pred(Pred()) {} bool operator() (const T& a, const T& b) const { return m_pred(a.*m_membP, b.*m_membP); } private: MembT T::* m_membP; Pred m_pred; }; //! Convenience wrapper for MemberPred, because class template arguments aren't deduced based on constructor arguments. template