/*------------------------------------------------------------------------- * 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 de::Random; using de::SharedPtr; using glu::RenderContext; using glu::ShaderProgram; using tcu::RenderTarget; using tcu::TestLog; using tcu::Vec2; using tcu::Vec3; using tcu::Vec4; using std::string; using std::vector; 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(uint32_t 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 uint32_t getQueryResult(const glw::Functions &gl, uint32_t queryObject) { uint32_t result = (uint32_t)-1; gl.getQueryObjectuiv(queryObject, GL_QUERY_RESULT, &result); TCU_CHECK(result != (uint32_t)-1); return result; } template static void readDataMapped(const glw::Functions &gl, uint32_t 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, uint32_t 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(uint32_t programGL, uint32_t 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( uint32_t programGL, uint32_t 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