| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL ES 3.1 Module |
| * ------------------------------------------------- |
| * |
| * Copyright 2015 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 Primitive bounding box tests. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "es31fPrimitiveBoundingBoxTests.hpp" |
| |
| #include "tcuTestLog.hpp" |
| #include "tcuRenderTarget.hpp" |
| #include "tcuStringTemplate.hpp" |
| #include "tcuSurface.hpp" |
| #include "tcuTextureUtil.hpp" |
| #include "tcuVectorUtil.hpp" |
| #include "gluCallLogWrapper.hpp" |
| #include "gluContextInfo.hpp" |
| #include "gluRenderContext.hpp" |
| #include "gluStrUtil.hpp" |
| #include "gluShaderProgram.hpp" |
| #include "gluObjectWrapper.hpp" |
| #include "gluPixelTransfer.hpp" |
| #include "glsStateQueryUtil.hpp" |
| #include "glwFunctions.hpp" |
| #include "glwEnums.hpp" |
| #include "deRandom.hpp" |
| #include "deUniquePtr.hpp" |
| #include "deStringUtil.hpp" |
| |
| #include <vector> |
| #include <sstream> |
| #include <algorithm> |
| |
| namespace deqp |
| { |
| namespace gles31 |
| { |
| namespace Functional |
| { |
| namespace |
| { |
| |
| namespace StateQueryUtil = ::deqp::gls::StateQueryUtil; |
| |
| struct BoundingBox |
| { |
| tcu::Vec4 min; |
| tcu::Vec4 max; |
| |
| /*--------------------------------------------------------------------*//*! |
| * Get component by index of a 8-component vector constructed by |
| * concatenating 4-component min and max vectors. |
| *//*--------------------------------------------------------------------*/ |
| float& getComponentAccess (int ndx); |
| const float& getComponentAccess (int ndx) const; |
| }; |
| |
| float& BoundingBox::getComponentAccess (int ndx) |
| { |
| DE_ASSERT(ndx >= 0 && ndx < 8); |
| if (ndx < 4) |
| return min[ndx]; |
| else |
| return max[ndx-4]; |
| } |
| |
| const float& BoundingBox::getComponentAccess (int ndx) const |
| { |
| return const_cast<BoundingBox*>(this)->getComponentAccess(ndx); |
| } |
| |
| struct ProjectedBBox |
| { |
| tcu::Vec3 min; |
| tcu::Vec3 max; |
| }; |
| |
| static ProjectedBBox projectBoundingBox (const BoundingBox& bbox) |
| { |
| const float wMin = de::max(0.0f, bbox.min.w()); // clamp to w=0 as extension requires |
| const float wMax = de::max(0.0f, bbox.max.w()); |
| ProjectedBBox retVal; |
| |
| retVal.min = tcu::min(bbox.min.swizzle(0, 1, 2) / wMin, |
| bbox.min.swizzle(0, 1, 2) / wMax); |
| retVal.max = tcu::max(bbox.max.swizzle(0, 1, 2) / wMin, |
| bbox.max.swizzle(0, 1, 2) / wMax); |
| return retVal; |
| } |
| |
| static tcu::IVec4 getViewportBoundingBoxArea (const ProjectedBBox& bbox, const tcu::IVec2& viewportSize, float size = 0.0f) |
| { |
| tcu::Vec4 vertexBox; |
| tcu::IVec4 pixelBox; |
| |
| vertexBox.x() = (bbox.min.x() * 0.5f + 0.5f) * (float)viewportSize.x(); |
| vertexBox.y() = (bbox.min.y() * 0.5f + 0.5f) * (float)viewportSize.y(); |
| vertexBox.z() = (bbox.max.x() * 0.5f + 0.5f) * (float)viewportSize.x(); |
| vertexBox.w() = (bbox.max.y() * 0.5f + 0.5f) * (float)viewportSize.y(); |
| |
| pixelBox.x() = deFloorFloatToInt32(vertexBox.x() - size/2.0f); |
| pixelBox.y() = deFloorFloatToInt32(vertexBox.y() - size/2.0f); |
| pixelBox.z() = deCeilFloatToInt32(vertexBox.z() + size/2.0f); |
| pixelBox.w() = deCeilFloatToInt32(vertexBox.w() + size/2.0f); |
| return pixelBox; |
| } |
| |
| static std::string specializeShader(Context& context, const char* code) |
| { |
| const glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(context.getRenderContext().getType()); |
| std::map<std::string, std::string> specializationMap; |
| |
| specializationMap["GLSL_VERSION_DECL"] = glu::getGLSLVersionDeclaration(glslVersion); |
| |
| if (glu::contextSupports(context.getRenderContext().getType(), glu::ApiType::es(3, 2))) |
| { |
| specializationMap["GEOMETRY_SHADER_REQUIRE"] = ""; |
| specializationMap["GEOMETRY_POINT_SIZE"] = "#extension GL_EXT_geometry_point_size : require"; |
| specializationMap["GPU_SHADER5_REQUIRE"] = ""; |
| specializationMap["TESSELLATION_SHADER_REQUIRE"] = ""; |
| specializationMap["TESSELLATION_POINT_SIZE_REQUIRE"] = "#extension GL_EXT_tessellation_point_size : require"; |
| specializationMap["PRIMITIVE_BOUNDING_BOX_REQUIRE"] = ""; |
| specializationMap["PRIM_GL_BOUNDING_BOX"] = "gl_BoundingBox"; |
| } |
| else |
| { |
| specializationMap["GEOMETRY_SHADER_REQUIRE"] = "#extension GL_EXT_geometry_shader : require"; |
| specializationMap["GEOMETRY_POINT_SIZE"] = "#extension GL_EXT_geometry_point_size : require"; |
| specializationMap["GPU_SHADER5_REQUIRE"] = "#extension GL_EXT_gpu_shader5 : require"; |
| specializationMap["TESSELLATION_SHADER_REQUIRE"] = "#extension GL_EXT_tessellation_shader : require"; |
| specializationMap["TESSELLATION_POINT_SIZE_REQUIRE"] = "#extension GL_EXT_tessellation_point_size : require"; |
| specializationMap["PRIMITIVE_BOUNDING_BOX_REQUIRE"] = "#extension GL_EXT_primitive_bounding_box : require"; |
| specializationMap["PRIM_GL_BOUNDING_BOX"] = "gl_BoundingBoxEXT"; |
| } |
| |
| return tcu::StringTemplate(code).specialize(specializationMap); |
| } |
| |
| class InitialValueCase : public TestCase |
| { |
| public: |
| InitialValueCase (Context& context, const char* name, const char* desc); |
| |
| void init (void); |
| IterateResult iterate (void); |
| }; |
| |
| InitialValueCase::InitialValueCase (Context& context, const char* name, const char* desc) |
| : TestCase(context, name, desc) |
| { |
| } |
| |
| void InitialValueCase::init (void) |
| { |
| const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)); |
| |
| if (!supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box")) |
| throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension"); |
| } |
| |
| InitialValueCase::IterateResult InitialValueCase::iterate (void) |
| { |
| StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLfloat[8]> state; |
| glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); |
| |
| gl.enableLogging(true); |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Querying GL_PRIMITIVE_BOUNDING_BOX_EXT, expecting (-1, -1, -1, 1) (1, 1, 1, 1)" |
| << tcu::TestLog::EndMessage; |
| |
| gl.glGetFloatv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "query"); |
| |
| if (!state.verifyValidity(m_testCtx)) |
| return STOP; |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Got " << tcu::formatArray(&state[0], &state[8]) |
| << tcu::TestLog::EndMessage; |
| |
| if ((state[0] != -1.0f) || (state[1] != -1.0f) || (state[2] != -1.0f) || (state[3] != 1.0f) || |
| (state[4] != 1.0f) || (state[5] != 1.0f) || (state[6] != 1.0f) || (state[7] != 1.0f)) |
| { |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Error, unexpected value" |
| << tcu::TestLog::EndMessage; |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid initial value"); |
| } |
| else |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| return STOP; |
| } |
| |
| class QueryCase : public TestCase |
| { |
| public: |
| enum QueryMethod |
| { |
| QUERY_FLOAT = 0, |
| QUERY_BOOLEAN, |
| QUERY_INT, |
| QUERY_INT64, |
| |
| QUERY_LAST |
| }; |
| |
| QueryCase (Context& context, const char* name, const char* desc, QueryMethod method); |
| |
| private: |
| void init (void); |
| IterateResult iterate (void); |
| |
| bool verifyState (glu::CallLogWrapper& gl, const BoundingBox& bbox) const; |
| |
| const QueryMethod m_method; |
| }; |
| |
| QueryCase::QueryCase (Context& context, const char* name, const char* desc, QueryMethod method) |
| : TestCase (context, name, desc) |
| , m_method (method) |
| { |
| DE_ASSERT(method < QUERY_LAST); |
| } |
| |
| void QueryCase::init (void) |
| { |
| const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)); |
| |
| if (!supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box")) |
| throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension"); |
| } |
| |
| QueryCase::IterateResult QueryCase::iterate (void) |
| { |
| static const BoundingBox fixedCases[] = |
| { |
| { tcu::Vec4( 0.0f, 0.0f, 0.0f, 0.0f), tcu::Vec4( 0.0f, 0.0f, 0.0f, 0.0f) }, |
| { tcu::Vec4(-0.0f, -0.0f, -0.0f, -0.0f), tcu::Vec4( 0.0f, 0.0f, 0.0f, -0.0f) }, |
| { tcu::Vec4( 0.0f, 0.0f, 0.0f, 0.0f), tcu::Vec4( 1.0f, 1.0f, 1.0f, -1.0f) }, |
| { tcu::Vec4( 2.0f, 2.0f, 2.0f, 2.0f), tcu::Vec4( 1.5f, 1.5f, 1.5f, 1.0f) }, |
| { tcu::Vec4( 1.0f, 1.0f, 1.0f, 1.0f), tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f) }, |
| { tcu::Vec4( 1.0f, 1.0f, 1.0f, 0.3f), tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.2f) }, |
| }; |
| |
| const int numRandomCases = 9; |
| glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); |
| de::Random rnd (0xDE3210); |
| std::vector<BoundingBox> cases; |
| |
| cases.insert(cases.begin(), DE_ARRAY_BEGIN(fixedCases), DE_ARRAY_END(fixedCases)); |
| for (int ndx = 0; ndx < numRandomCases; ++ndx) |
| { |
| BoundingBox boundingBox; |
| |
| // parameter evaluation order is not guaranteed, cannot just do "max = (rand(), rand(), ...) |
| for (int coordNdx = 0; coordNdx < 8; ++coordNdx) |
| boundingBox.getComponentAccess(coordNdx) = rnd.getFloat(-4.0f, 4.0f); |
| |
| cases.push_back(boundingBox); |
| } |
| |
| gl.enableLogging(true); |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| for (int caseNdx = 0; caseNdx < (int)cases.size(); ++caseNdx) |
| { |
| const tcu::ScopedLogSection section (m_testCtx.getLog(), "Iteration", "Iteration " + de::toString(caseNdx+1)); |
| const BoundingBox& boundingBox = cases[caseNdx]; |
| |
| gl.glPrimitiveBoundingBox(boundingBox.min.x(), boundingBox.min.y(), boundingBox.min.z(), boundingBox.min.w(), |
| boundingBox.max.x(), boundingBox.max.y(), boundingBox.max.z(), boundingBox.max.w()); |
| |
| if (!verifyState(gl, boundingBox)) |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Unexpected query result"); |
| } |
| |
| return STOP; |
| } |
| |
| bool QueryCase::verifyState (glu::CallLogWrapper& gl, const BoundingBox& bbox) const |
| { |
| switch (m_method) |
| { |
| case QUERY_FLOAT: |
| { |
| StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLfloat[8]> state; |
| bool error = false; |
| |
| gl.glGetFloatv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "query"); |
| |
| if (!state.verifyValidity(m_testCtx)) |
| return false; |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "glGetFloatv returned " << tcu::formatArray(&state[0], &state[8]) |
| << tcu::TestLog::EndMessage; |
| |
| for (int ndx = 0; ndx < 8; ++ndx) |
| if (state[ndx] != bbox.getComponentAccess(ndx)) |
| error = true; |
| |
| if (error) |
| { |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Error, unexpected value\n" |
| << "Expected [" |
| << bbox.min.x() << ", " << bbox.min.y() << ", " << bbox.min.z() << ", " << bbox.min.w() << ", " |
| << bbox.max.x() << ", " << bbox.max.y() << ", " << bbox.max.z() << ", " << bbox.max.w() << "]" |
| << tcu::TestLog::EndMessage; |
| return false; |
| } |
| return true; |
| } |
| |
| case QUERY_INT: |
| { |
| StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint[8]> state; |
| bool error = false; |
| |
| gl.glGetIntegerv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "query"); |
| |
| if (!state.verifyValidity(m_testCtx)) |
| return false; |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "glGetIntegerv returned " << tcu::formatArray(&state[0], &state[8]) |
| << tcu::TestLog::EndMessage; |
| |
| for (int ndx = 0; ndx < 8; ++ndx) |
| if (state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint>(bbox.getComponentAccess(ndx)) && |
| state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint>(bbox.getComponentAccess(ndx))) |
| error = true; |
| |
| if (error) |
| { |
| tcu::MessageBuilder builder(&m_testCtx.getLog()); |
| |
| builder << "Error, unexpected value\n" |
| << "Expected ["; |
| |
| for (int ndx = 0; ndx < 8; ++ndx) |
| { |
| const glw::GLint roundDown = StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint>(bbox.getComponentAccess(ndx)); |
| const glw::GLint roundUp = StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint>(bbox.getComponentAccess(ndx)); |
| |
| if (ndx != 0) |
| builder << ", "; |
| |
| if (roundDown == roundUp) |
| builder << roundDown; |
| else |
| builder << "{" << roundDown << ", " << roundUp << "}"; |
| } |
| |
| builder << "]" |
| << tcu::TestLog::EndMessage; |
| return false; |
| } |
| return true; |
| } |
| |
| case QUERY_INT64: |
| { |
| StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLint64[8]> state; |
| bool error = false; |
| |
| gl.glGetInteger64v(GL_PRIMITIVE_BOUNDING_BOX_EXT, state); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "query"); |
| |
| if (!state.verifyValidity(m_testCtx)) |
| return false; |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "glGetInteger64v returned " << tcu::formatArray(&state[0], &state[8]) |
| << tcu::TestLog::EndMessage; |
| |
| for (int ndx = 0; ndx < 8; ++ndx) |
| if (state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint64>(bbox.getComponentAccess(ndx)) && |
| state[ndx] != StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint64>(bbox.getComponentAccess(ndx))) |
| error = true; |
| |
| if (error) |
| { |
| tcu::MessageBuilder builder(&m_testCtx.getLog()); |
| |
| builder << "Error, unexpected value\n" |
| << "Expected ["; |
| |
| for (int ndx = 0; ndx < 8; ++ndx) |
| { |
| const glw::GLint64 roundDown = StateQueryUtil::roundGLfloatToNearestIntegerHalfDown<glw::GLint64>(bbox.getComponentAccess(ndx)); |
| const glw::GLint64 roundUp = StateQueryUtil::roundGLfloatToNearestIntegerHalfUp<glw::GLint64>(bbox.getComponentAccess(ndx)); |
| |
| if (ndx != 0) |
| builder << ", "; |
| |
| if (roundDown == roundUp) |
| builder << roundDown; |
| else |
| builder << "{" << roundDown << ", " << roundUp << "}"; |
| } |
| |
| builder << "]" |
| << tcu::TestLog::EndMessage; |
| return false; |
| } |
| return true; |
| } |
| |
| case QUERY_BOOLEAN: |
| { |
| StateQueryUtil::StateQueryMemoryWriteGuard<glw::GLboolean[8]> state; |
| bool error = false; |
| |
| gl.glGetBooleanv(GL_PRIMITIVE_BOUNDING_BOX_EXT, state); |
| GLU_EXPECT_NO_ERROR(gl.glGetError(), "query"); |
| |
| if (!state.verifyValidity(m_testCtx)) |
| return false; |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "glGetBooleanv returned [" |
| << glu::getBooleanStr(state[0]) << ", " << glu::getBooleanStr(state[1]) << ", " << glu::getBooleanStr(state[2]) << ", " << glu::getBooleanStr(state[3]) << ", " |
| << glu::getBooleanStr(state[4]) << ", " << glu::getBooleanStr(state[5]) << ", " << glu::getBooleanStr(state[6]) << ", " << glu::getBooleanStr(state[7]) << "]\n" |
| << tcu::TestLog::EndMessage; |
| |
| for (int ndx = 0; ndx < 8; ++ndx) |
| if (state[ndx] != ((bbox.getComponentAccess(ndx) != 0.0f) ? (GL_TRUE) : (GL_FALSE))) |
| error = true; |
| |
| if (error) |
| { |
| tcu::MessageBuilder builder(&m_testCtx.getLog()); |
| |
| builder << "Error, unexpected value\n" |
| << "Expected ["; |
| |
| for (int ndx = 0; ndx < 8; ++ndx) |
| { |
| if (ndx != 0) |
| builder << ", "; |
| |
| builder << ((bbox.getComponentAccess(ndx) != 0.0f) ? ("GL_TRUE") : ("GL_FALSE")); |
| } |
| |
| builder << "]" |
| << tcu::TestLog::EndMessage; |
| return false; |
| } |
| return true; |
| } |
| |
| default: |
| DE_ASSERT(false); |
| return true; |
| } |
| } |
| |
| class BBoxRenderCase : public TestCase |
| { |
| public: |
| enum |
| { |
| FLAG_RENDERTARGET_DEFAULT = 1u << 0, //!< render to default renderbuffer |
| FLAG_RENDERTARGET_FBO = 1u << 1, //!< render to framebuffer object |
| |
| FLAG_BBOXSIZE_EQUAL = 1u << 2, //!< set tight primitive bounding box |
| FLAG_BBOXSIZE_LARGER = 1u << 3, //!< set padded primitive bounding box |
| FLAG_BBOXSIZE_SMALLER = 1u << 4, //!< set too small primitive bounding box |
| |
| FLAG_TESSELLATION = 1u << 5, //!< use tessellation shader |
| FLAG_GEOMETRY = 1u << 6, //!< use geometry shader |
| |
| FLAG_SET_BBOX_STATE = 1u << 7, //!< set primitive bounding box using global state |
| FLAG_SET_BBOX_OUTPUT = 1u << 8, //!< set primitive bounding box using tessellation output |
| FLAG_PER_PRIMITIVE_BBOX = 1u << 9, //!< set primitive bounding per primitive |
| |
| FLAGBIT_USER_BIT = 10u //!< bits N and and up are reserved for subclasses |
| }; |
| |
| BBoxRenderCase (Context& context, const char* name, const char* description, int numIterations, deUint32 flags); |
| ~BBoxRenderCase (void); |
| |
| protected: |
| enum RenderTarget |
| { |
| RENDERTARGET_DEFAULT, |
| RENDERTARGET_FBO, |
| }; |
| enum BBoxSize |
| { |
| BBOXSIZE_EQUAL, |
| BBOXSIZE_LARGER, |
| BBOXSIZE_SMALLER, |
| }; |
| |
| enum |
| { |
| RENDER_TARGET_MIN_SIZE = 256, |
| FBO_SIZE = 512, |
| MIN_VIEWPORT_SIZE = 256, |
| MAX_VIEWPORT_SIZE = 512, |
| }; |
| DE_STATIC_ASSERT(MIN_VIEWPORT_SIZE <= RENDER_TARGET_MIN_SIZE); |
| |
| enum |
| { |
| VA_POS_VEC_NDX = 0, |
| VA_COL_VEC_NDX = 1, |
| VA_NUM_ATTRIB_VECS = 2, |
| }; |
| |
| enum AABBRoundDirection |
| { |
| ROUND_INWARDS = 0, |
| ROUND_OUTWARDS |
| }; |
| |
| struct IterationConfig |
| { |
| tcu::IVec2 viewportPos; |
| tcu::IVec2 viewportSize; |
| tcu::Vec2 patternPos; //!< in NDC |
| tcu::Vec2 patternSize; //!< in NDC |
| BoundingBox bbox; |
| }; |
| |
| virtual void init (void); |
| virtual void deinit (void); |
| IterateResult iterate (void); |
| |
| virtual std::string genVertexSource (void) const = 0; |
| virtual std::string genFragmentSource (void) const = 0; |
| virtual std::string genTessellationControlSource (void) const = 0; |
| virtual std::string genTessellationEvaluationSource (void) const = 0; |
| virtual std::string genGeometrySource (void) const = 0; |
| |
| virtual IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const = 0; |
| virtual void getAttributeData (std::vector<tcu::Vec4>& data) const = 0; |
| virtual void renderTestPattern (const IterationConfig& config) = 0; |
| virtual void verifyRenderResult (const IterationConfig& config) = 0; |
| |
| IterationConfig generateRandomConfig (int seed, const tcu::IVec2& renderTargetSize) const; |
| tcu::IVec4 getViewportPatternArea (const tcu::Vec2& patternPos, const tcu::Vec2& patternSize, const tcu::IVec2& viewportSize, AABBRoundDirection roundDir) const; |
| |
| void setupRender (const IterationConfig& config); |
| |
| enum ShaderFunction |
| { |
| SHADER_FUNC_MIRROR_X, |
| SHADER_FUNC_MIRROR_Y, |
| SHADER_FUNC_INSIDE_BBOX, |
| }; |
| |
| const char* genShaderFunction (ShaderFunction func) const; |
| |
| const RenderTarget m_renderTarget; |
| const BBoxSize m_bboxSize; |
| const bool m_hasTessellationStage; |
| const bool m_hasGeometryStage; |
| const bool m_useGlobalState; |
| const bool m_calcPerPrimitiveBBox; |
| const int m_numIterations; |
| |
| de::MovePtr<glu::ShaderProgram> m_program; |
| de::MovePtr<glu::Buffer> m_vbo; |
| de::MovePtr<glu::Framebuffer> m_fbo; |
| |
| private: |
| std::vector<IterationConfig> m_iterationConfigs; |
| int m_iteration; |
| }; |
| |
| BBoxRenderCase::BBoxRenderCase (Context& context, const char* name, const char* description, int numIterations, deUint32 flags) |
| : TestCase (context, name, description) |
| , m_renderTarget ((flags & FLAG_RENDERTARGET_DEFAULT) ? (RENDERTARGET_DEFAULT) : (RENDERTARGET_FBO)) |
| , m_bboxSize ((flags & FLAG_BBOXSIZE_EQUAL) ? (BBOXSIZE_EQUAL) : (flags & FLAG_BBOXSIZE_SMALLER) ? (BBOXSIZE_SMALLER) : (BBOXSIZE_LARGER)) |
| , m_hasTessellationStage ((flags & FLAG_TESSELLATION) != 0) |
| , m_hasGeometryStage ((flags & FLAG_GEOMETRY) != 0) |
| , m_useGlobalState ((flags & FLAG_SET_BBOX_STATE) != 0) |
| , m_calcPerPrimitiveBBox ((flags & FLAG_PER_PRIMITIVE_BBOX) != 0) |
| , m_numIterations (numIterations) |
| , m_iteration (0) |
| { |
| // validate flags |
| DE_ASSERT((((m_renderTarget == RENDERTARGET_DEFAULT) ? (FLAG_RENDERTARGET_DEFAULT) : (0)) | |
| ((m_renderTarget == RENDERTARGET_FBO) ? (FLAG_RENDERTARGET_FBO) : (0)) | |
| ((m_bboxSize == BBOXSIZE_EQUAL) ? (FLAG_BBOXSIZE_EQUAL) : (0)) | |
| ((m_bboxSize == BBOXSIZE_LARGER) ? (FLAG_BBOXSIZE_LARGER) : (0)) | |
| ((m_bboxSize == BBOXSIZE_SMALLER) ? (FLAG_BBOXSIZE_SMALLER) : (0)) | |
| ((m_hasTessellationStage) ? (FLAG_TESSELLATION) : (0)) | |
| ((m_hasGeometryStage) ? (FLAG_GEOMETRY) : (0)) | |
| ((m_useGlobalState) ? (FLAG_SET_BBOX_STATE) : (0)) | |
| ((!m_useGlobalState) ? (FLAG_SET_BBOX_OUTPUT) : (0)) | |
| ((m_calcPerPrimitiveBBox) ? (FLAG_PER_PRIMITIVE_BBOX) : (0))) == (flags & ((1u << FLAGBIT_USER_BIT) - 1))); |
| |
| DE_ASSERT(m_useGlobalState || m_hasTessellationStage); // using non-global state requires tessellation |
| |
| if (m_calcPerPrimitiveBBox) |
| { |
| DE_ASSERT(!m_useGlobalState); // per-primitive test requires per-primitive (non-global) state |
| DE_ASSERT(m_bboxSize == BBOXSIZE_EQUAL); // smaller is hard to verify, larger not interesting |
| } |
| } |
| |
| BBoxRenderCase::~BBoxRenderCase (void) |
| { |
| deinit(); |
| } |
| |
| void BBoxRenderCase::init (void) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| const tcu::IVec2 renderTargetSize = (m_renderTarget == RENDERTARGET_DEFAULT) ? |
| (tcu::IVec2(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())) : |
| (tcu::IVec2(FBO_SIZE, FBO_SIZE)); |
| const bool supportsES32 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)); |
| |
| // requirements |
| if (!supportsES32 && !m_context.getContextInfo().isExtensionSupported("GL_EXT_primitive_bounding_box")) |
| throw tcu::NotSupportedError("Test requires GL_EXT_primitive_bounding_box extension"); |
| if (!supportsES32 && m_hasTessellationStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader")) |
| throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader extension"); |
| if (!supportsES32 && m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")) |
| throw tcu::NotSupportedError("Test requires GL_EXT_geometry_shader extension"); |
| if (m_renderTarget == RENDERTARGET_DEFAULT && (renderTargetSize.x() < RENDER_TARGET_MIN_SIZE || renderTargetSize.y() < RENDER_TARGET_MIN_SIZE)) |
| throw tcu::NotSupportedError(std::string() + "Test requires " + de::toString<int>(RENDER_TARGET_MIN_SIZE) + "x" + de::toString<int>(RENDER_TARGET_MIN_SIZE) + " default framebuffer"); |
| |
| // log case specifics |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Setting primitive bounding box " |
| << ((m_calcPerPrimitiveBBox) ? ("to exactly cover each generated primitive") |
| : (m_bboxSize == BBOXSIZE_EQUAL) ? ("to exactly cover rendered grid") |
| : (m_bboxSize == BBOXSIZE_LARGER) ? ("to cover the grid and include some padding") |
| : (m_bboxSize == BBOXSIZE_SMALLER) ? ("to cover only a subset of the grid") |
| : (DE_NULL)) |
| << ".\n" |
| << "Rendering with vertex" |
| << ((m_hasTessellationStage) ? ("-tessellation{ctrl,eval}") : ("")) |
| << ((m_hasGeometryStage) ? ("-geometry") : ("")) |
| << "-fragment program.\n" |
| << "Set bounding box using " |
| << ((m_useGlobalState) ? ("PRIMITIVE_BOUNDING_BOX_EXT state") : ("gl_BoundingBoxEXT output")) |
| << "\n" |
| << "Verifying rendering results are valid within the bounding box." |
| << tcu::TestLog::EndMessage; |
| |
| // resources |
| |
| { |
| glu::ProgramSources sources; |
| sources << glu::VertexSource(specializeShader(m_context, genVertexSource().c_str())); |
| sources << glu::FragmentSource(specializeShader(m_context, genFragmentSource().c_str())); |
| |
| if (m_hasTessellationStage) |
| sources << glu::TessellationControlSource(specializeShader(m_context, genTessellationControlSource().c_str())) |
| << glu::TessellationEvaluationSource(specializeShader(m_context, genTessellationEvaluationSource().c_str())); |
| if (m_hasGeometryStage) |
| sources << glu::GeometrySource(specializeShader(m_context, genGeometrySource().c_str())); |
| |
| m_program = de::MovePtr<glu::ShaderProgram>(new glu::ShaderProgram(m_context.getRenderContext(), sources)); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "build program"); |
| |
| { |
| const tcu::ScopedLogSection section(m_testCtx.getLog(), "ShaderProgram", "Shader program"); |
| m_testCtx.getLog() << *m_program; |
| } |
| |
| if (!m_program->isOk()) |
| throw tcu::TestError("failed to build program"); |
| } |
| |
| if (m_renderTarget == RENDERTARGET_FBO) |
| { |
| glu::Texture colorAttachment(m_context.getRenderContext()); |
| |
| gl.bindTexture(GL_TEXTURE_2D, *colorAttachment); |
| gl.texStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, FBO_SIZE, FBO_SIZE); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "gen tex"); |
| |
| m_fbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext())); |
| gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, **m_fbo); |
| gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *colorAttachment, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "attach"); |
| |
| // unbind to prevent texture name deletion from removing it from current fbo attachments |
| gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); |
| } |
| |
| { |
| std::vector<tcu::Vec4> data; |
| |
| getAttributeData(data); |
| |
| m_vbo = de::MovePtr<glu::Buffer>(new glu::Buffer(m_context.getRenderContext())); |
| gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo); |
| gl.bufferData(GL_ARRAY_BUFFER, (int)(data.size() * sizeof(tcu::Vec4)), &data[0], GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "create vbo"); |
| } |
| |
| // Iterations |
| for (int iterationNdx = 0; iterationNdx < m_numIterations; ++iterationNdx) |
| m_iterationConfigs.push_back(generateConfig(iterationNdx, renderTargetSize)); |
| } |
| |
| void BBoxRenderCase::deinit (void) |
| { |
| m_program.clear(); |
| m_vbo.clear(); |
| m_fbo.clear(); |
| } |
| |
| BBoxRenderCase::IterateResult BBoxRenderCase::iterate (void) |
| { |
| const tcu::ScopedLogSection section (m_testCtx.getLog(), |
| std::string() + "Iteration" + de::toString((int)m_iteration), |
| std::string() + "Iteration " + de::toString((int)m_iteration+1) + "/" + de::toString((int)m_iterationConfigs.size())); |
| const IterationConfig& config = m_iterationConfigs[m_iteration]; |
| |
| // default |
| if (m_iteration == 0) |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| renderTestPattern(config); |
| verifyRenderResult(config); |
| |
| if (++m_iteration < (int)m_iterationConfigs.size()) |
| return CONTINUE; |
| |
| return STOP; |
| } |
| |
| BBoxRenderCase::IterationConfig BBoxRenderCase::generateRandomConfig (int seed, const tcu::IVec2& renderTargetSize) const |
| { |
| de::Random rnd (seed); |
| IterationConfig config; |
| |
| // viewport config |
| config.viewportSize.x() = rnd.getInt(MIN_VIEWPORT_SIZE, de::min<int>(renderTargetSize.x(), MAX_VIEWPORT_SIZE)); |
| config.viewportSize.y() = rnd.getInt(MIN_VIEWPORT_SIZE, de::min<int>(renderTargetSize.y(), MAX_VIEWPORT_SIZE)); |
| config.viewportPos.x() = rnd.getInt(0, renderTargetSize.x() - config.viewportSize.x()); |
| config.viewportPos.y() = rnd.getInt(0, renderTargetSize.y() - config.viewportSize.y()); |
| |
| // pattern location inside viewport |
| config.patternSize.x() = rnd.getFloat(0.4f, 1.4f); |
| config.patternSize.y() = rnd.getFloat(0.4f, 1.4f); |
| config.patternPos.x() = rnd.getFloat(-1.0f, 1.0f - config.patternSize.x()); |
| config.patternPos.y() = rnd.getFloat(-1.0f, 1.0f - config.patternSize.y()); |
| |
| // accurate bounding box |
| config.bbox.min = tcu::Vec4(config.patternPos.x(), config.patternPos.y(), 0.0f, 1.0f); |
| config.bbox.max = tcu::Vec4(config.patternPos.x() + config.patternSize.x(), config.patternPos.y() + config.patternSize.y(), 0.0f, 1.0f); |
| |
| if (m_bboxSize == BBOXSIZE_LARGER) |
| { |
| // increase bbox size |
| config.bbox.min.x() -= rnd.getFloat() * 0.5f; |
| config.bbox.min.y() -= rnd.getFloat() * 0.5f; |
| config.bbox.min.z() -= rnd.getFloat() * 0.5f; |
| |
| config.bbox.max.x() += rnd.getFloat() * 0.5f; |
| config.bbox.max.y() += rnd.getFloat() * 0.5f; |
| config.bbox.max.z() += rnd.getFloat() * 0.5f; |
| } |
| else if (m_bboxSize == BBOXSIZE_SMALLER) |
| { |
| // reduce bbox size |
| config.bbox.min.x() += rnd.getFloat() * 0.4f * config.patternSize.x(); |
| config.bbox.min.y() += rnd.getFloat() * 0.4f * config.patternSize.y(); |
| |
| config.bbox.max.x() -= rnd.getFloat() * 0.4f * config.patternSize.x(); |
| config.bbox.max.y() -= rnd.getFloat() * 0.4f * config.patternSize.y(); |
| } |
| |
| return config; |
| } |
| |
| tcu::IVec4 BBoxRenderCase::getViewportPatternArea (const tcu::Vec2& patternPos, const tcu::Vec2& patternSize, const tcu::IVec2& viewportSize, AABBRoundDirection roundDir) const |
| { |
| const float halfPixel = 0.5f; |
| tcu::Vec4 vertexBox; |
| tcu::IVec4 pixelBox; |
| |
| vertexBox.x() = (patternPos.x() * 0.5f + 0.5f) * (float)viewportSize.x(); |
| vertexBox.y() = (patternPos.y() * 0.5f + 0.5f) * (float)viewportSize.y(); |
| vertexBox.z() = ((patternPos.x() + patternSize.x()) * 0.5f + 0.5f) * (float)viewportSize.x(); |
| vertexBox.w() = ((patternPos.y() + patternSize.y()) * 0.5f + 0.5f) * (float)viewportSize.y(); |
| |
| if (roundDir == ROUND_INWARDS) |
| { |
| pixelBox.x() = (int)deFloatCeil(vertexBox.x()+halfPixel); |
| pixelBox.y() = (int)deFloatCeil(vertexBox.y()+halfPixel); |
| pixelBox.z() = (int)deFloatFloor(vertexBox.z()-halfPixel); |
| pixelBox.w() = (int)deFloatFloor(vertexBox.w()-halfPixel); |
| } |
| else |
| { |
| pixelBox.x() = (int)deFloatFloor(vertexBox.x()-halfPixel); |
| pixelBox.y() = (int)deFloatFloor(vertexBox.y()-halfPixel); |
| pixelBox.z() = (int)deFloatCeil(vertexBox.z()+halfPixel); |
| pixelBox.w() = (int)deFloatCeil(vertexBox.w()+halfPixel); |
| } |
| |
| return pixelBox; |
| } |
| |
| void BBoxRenderCase::setupRender (const IterationConfig& config) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| const glw::GLint posLocation = gl.getAttribLocation(m_program->getProgram(), "a_position"); |
| const glw::GLint colLocation = gl.getAttribLocation(m_program->getProgram(), "a_color"); |
| const glw::GLint posScaleLocation = gl.getUniformLocation(m_program->getProgram(), "u_posScale"); |
| |
| TCU_CHECK(posLocation != -1); |
| TCU_CHECK(colLocation != -1); |
| TCU_CHECK(posScaleLocation != -1); |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Setting viewport to (" |
| << "x: " << config.viewportPos.x() << ", " |
| << "y: " << config.viewportPos.y() << ", " |
| << "w: " << config.viewportSize.x() << ", " |
| << "h: " << config.viewportSize.y() << ")\n" |
| << "Vertex coordinates are in range:\n" |
| << "\tx: [" << config.patternPos.x() << ", " << (config.patternPos.x() + config.patternSize.x()) << "]\n" |
| << "\ty: [" << config.patternPos.y() << ", " << (config.patternPos.y() + config.patternSize.y()) << "]\n" |
| << tcu::TestLog::EndMessage; |
| |
| if (!m_calcPerPrimitiveBBox) |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Setting primitive bounding box to:\n" |
| << "\t" << config.bbox.min << "\n" |
| << "\t" << config.bbox.max << "\n" |
| << tcu::TestLog::EndMessage; |
| |
| if (m_useGlobalState) |
| gl.primitiveBoundingBox(config.bbox.min.x(), config.bbox.min.y(), config.bbox.min.z(), config.bbox.min.w(), |
| config.bbox.max.x(), config.bbox.max.y(), config.bbox.max.z(), config.bbox.max.w()); |
| else |
| // state is overriden by the tessellation output, set bbox to invisible area to imitiate dirty state left by application |
| gl.primitiveBoundingBox(-2.0f, -2.0f, 0.0f, 1.0f, |
| -1.7f, -1.7f, 0.0f, 1.0f); |
| |
| if (m_fbo) |
| gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, **m_fbo); |
| |
| gl.viewport(config.viewportPos.x(), config.viewportPos.y(), config.viewportSize.x(), config.viewportSize.y()); |
| gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); |
| gl.clear(GL_COLOR_BUFFER_BIT); |
| |
| gl.bindBuffer(GL_ARRAY_BUFFER, **m_vbo); |
| gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, (int)(VA_NUM_ATTRIB_VECS * sizeof(float[4])), glu::BufferOffsetAsPointer(4 * VA_POS_VEC_NDX * sizeof(float))); |
| gl.vertexAttribPointer(colLocation, 4, GL_FLOAT, GL_FALSE, (int)(VA_NUM_ATTRIB_VECS * sizeof(float[4])), glu::BufferOffsetAsPointer(4 * VA_COL_VEC_NDX * sizeof(float))); |
| gl.enableVertexAttribArray(posLocation); |
| gl.enableVertexAttribArray(colLocation); |
| gl.useProgram(m_program->getProgram()); |
| gl.uniform4f(posScaleLocation, config.patternPos.x(), config.patternPos.y(), config.patternSize.x(), config.patternSize.y()); |
| |
| { |
| const glw::GLint bboxMinPos = gl.getUniformLocation(m_program->getProgram(), "u_primitiveBBoxMin"); |
| const glw::GLint bboxMaxPos = gl.getUniformLocation(m_program->getProgram(), "u_primitiveBBoxMax"); |
| |
| gl.uniform4f(bboxMinPos, config.bbox.min.x(), config.bbox.min.y(), config.bbox.min.z(), config.bbox.min.w()); |
| gl.uniform4f(bboxMaxPos, config.bbox.max.x(), config.bbox.max.y(), config.bbox.max.z(), config.bbox.max.w()); |
| } |
| |
| gl.uniform2i(gl.getUniformLocation(m_program->getProgram(), "u_viewportPos"), config.viewportPos.x(), config.viewportPos.y()); |
| gl.uniform2i(gl.getUniformLocation(m_program->getProgram(), "u_viewportSize"), config.viewportSize.x(), config.viewportSize.y()); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "setup"); |
| } |
| |
| const char* BBoxRenderCase::genShaderFunction (ShaderFunction func) const |
| { |
| switch (func) |
| { |
| case SHADER_FUNC_MIRROR_X: |
| return "vec4 mirrorX(in highp vec4 p)\n" |
| "{\n" |
| " highp vec2 patternOffset = u_posScale.xy;\n" |
| " highp vec2 patternScale = u_posScale.zw;\n" |
| " highp vec2 patternCenter = patternOffset + patternScale * 0.5;\n" |
| " return vec4(2.0 * patternCenter.x - p.x, p.y, p.z, p.w);\n" |
| "}\n"; |
| |
| case SHADER_FUNC_MIRROR_Y: |
| return "vec4 mirrorY(in highp vec4 p)\n" |
| "{\n" |
| " highp vec2 patternOffset = u_posScale.xy;\n" |
| " highp vec2 patternScale = u_posScale.zw;\n" |
| " highp vec2 patternCenter = patternOffset + patternScale * 0.5;\n" |
| " return vec4(p.x, 2.0 * patternCenter.y - p.y, p.z, p.w);\n" |
| "}\n"; |
| |
| case SHADER_FUNC_INSIDE_BBOX: |
| return "uniform highp ivec2 u_viewportPos;\n" |
| "uniform highp ivec2 u_viewportSize;\n" |
| "flat in highp float v_bbox_expansionSize;\n" |
| "flat in highp vec3 v_bbox_clipMin;\n" |
| "flat in highp vec3 v_bbox_clipMax;\n" |
| "\n" |
| "bool fragmentInsideTheBBox(in highp float depth)\n" |
| "{\n" |
| " highp vec4 wc = vec4(floor((v_bbox_clipMin.x * 0.5 + 0.5) * float(u_viewportSize.x) - v_bbox_expansionSize/2.0),\n" |
| " floor((v_bbox_clipMin.y * 0.5 + 0.5) * float(u_viewportSize.y) - v_bbox_expansionSize/2.0),\n" |
| " ceil((v_bbox_clipMax.x * 0.5 + 0.5) * float(u_viewportSize.x) + v_bbox_expansionSize/2.0),\n" |
| " ceil((v_bbox_clipMax.y * 0.5 + 0.5) * float(u_viewportSize.y) + v_bbox_expansionSize/2.0));\n" |
| " if (gl_FragCoord.x < float(u_viewportPos.x) + wc.x || gl_FragCoord.x > float(u_viewportPos.x) + wc.z ||\n" |
| " gl_FragCoord.y < float(u_viewportPos.y) + wc.y || gl_FragCoord.y > float(u_viewportPos.y) + wc.w)\n" |
| " return false;\n" |
| " const highp float dEpsilon = 0.001;\n" |
| " if (depth*2.0-1.0 < v_bbox_clipMin.z - dEpsilon || depth*2.0-1.0 > v_bbox_clipMax.z + dEpsilon)\n" |
| " return false;\n" |
| " return true;\n" |
| "}\n"; |
| default: |
| DE_ASSERT(false); |
| return ""; |
| } |
| } |
| |
| class GridRenderCase : public BBoxRenderCase |
| { |
| public: |
| GridRenderCase (Context& context, const char* name, const char* description, deUint32 flags); |
| ~GridRenderCase (void); |
| |
| private: |
| void init (void); |
| |
| std::string genVertexSource (void) const; |
| std::string genFragmentSource (void) const; |
| std::string genTessellationControlSource (void) const; |
| std::string genTessellationEvaluationSource (void) const; |
| std::string genGeometrySource (void) const; |
| |
| IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const; |
| void getAttributeData (std::vector<tcu::Vec4>& data) const; |
| void renderTestPattern (const IterationConfig& config); |
| void verifyRenderResult (const IterationConfig& config); |
| |
| const int m_gridSize; |
| }; |
| |
| GridRenderCase::GridRenderCase (Context& context, const char* name, const char* description, deUint32 flags) |
| : BBoxRenderCase (context, name, description, 12, flags) |
| , m_gridSize (24) |
| { |
| } |
| |
| GridRenderCase::~GridRenderCase (void) |
| { |
| } |
| |
| void GridRenderCase::init (void) |
| { |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Rendering yellow-green grid to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n" |
| << "Grid cells are in random order, varying grid size and location for each iteration.\n" |
| << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated blue channel." |
| << tcu::TestLog::EndMessage; |
| |
| BBoxRenderCase::init(); |
| } |
| |
| std::string GridRenderCase::genVertexSource (void) const |
| { |
| std::ostringstream buf; |
| |
| buf << "${GLSL_VERSION_DECL}\n" |
| "in highp vec4 a_position;\n" |
| "in highp vec4 a_color;\n" |
| "out highp vec4 vtx_color;\n" |
| "uniform highp vec4 u_posScale;\n" |
| "\n"; |
| if (!m_hasTessellationStage) |
| { |
| DE_ASSERT(m_useGlobalState); |
| buf << "uniform highp vec4 u_primitiveBBoxMin;\n" |
| "uniform highp vec4 u_primitiveBBoxMax;\n" |
| "\n" |
| "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n" |
| "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n" |
| "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n" |
| "\n"; |
| } |
| |
| buf << "void main()\n" |
| "{\n" |
| " highp vec2 patternOffset = u_posScale.xy;\n" |
| " highp vec2 patternScale = u_posScale.zw;\n" |
| " gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n" |
| " vtx_color = a_color;\n"; |
| |
| if (!m_hasTessellationStage) |
| { |
| DE_ASSERT(m_useGlobalState); |
| buf << "\n" |
| " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = 0.0;\n" |
| " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n" |
| " min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n" |
| " vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n" |
| " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n" |
| " min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n" |
| " vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n"; |
| } |
| |
| buf<< "}\n"; |
| |
| return buf.str(); |
| } |
| |
| std::string GridRenderCase::genFragmentSource (void) const |
| { |
| const char* const colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color"); |
| std::ostringstream buf; |
| |
| buf << "${GLSL_VERSION_DECL}\n" |
| "in mediump vec4 " << colorInputName << ";\n" |
| "layout(location = 0) out mediump vec4 o_color;\n" |
| << genShaderFunction(SHADER_FUNC_INSIDE_BBOX) |
| << "\n" |
| "void main()\n" |
| "{\n" |
| " mediump vec4 baseColor = " << colorInputName << ";\n" |
| " mediump float blueChannel;\n" |
| " if (fragmentInsideTheBBox(gl_FragCoord.z))\n" |
| " blueChannel = 0.0;\n" |
| " else\n" |
| " blueChannel = 1.0;\n" |
| " o_color = vec4(baseColor.r, baseColor.g, blueChannel, baseColor.a);\n" |
| "}\n"; |
| |
| return buf.str(); |
| } |
| |
| std::string GridRenderCase::genTessellationControlSource (void) const |
| { |
| std::ostringstream buf; |
| |
| buf << "${GLSL_VERSION_DECL}\n" |
| "${TESSELLATION_SHADER_REQUIRE}\n" |
| "${PRIMITIVE_BOUNDING_BOX_REQUIRE}\n" |
| "layout(vertices=3) out;\n" |
| "\n" |
| "in highp vec4 vtx_color[];\n" |
| "out highp vec4 tess_ctrl_color[];\n" |
| "uniform highp float u_tessellationLevel;\n" |
| "uniform highp vec4 u_posScale;\n"; |
| |
| if (!m_calcPerPrimitiveBBox) |
| { |
| buf << "uniform highp vec4 u_primitiveBBoxMin;\n" |
| "uniform highp vec4 u_primitiveBBoxMax;\n"; |
| } |
| |
| buf << "patch out highp float vp_bbox_expansionSize;\n" |
| "patch out highp vec3 vp_bbox_clipMin;\n" |
| "patch out highp vec3 vp_bbox_clipMax;\n"; |
| |
| if (m_calcPerPrimitiveBBox) |
| { |
| buf << "\n"; |
| if (m_hasGeometryStage) |
| buf << genShaderFunction(SHADER_FUNC_MIRROR_X); |
| buf << genShaderFunction(SHADER_FUNC_MIRROR_Y); |
| |
| buf << "vec4 transformVec(in highp vec4 p)\n" |
| "{\n" |
| " return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n" |
| "}\n"; |
| } |
| |
| buf << "\n" |
| "void main()\n" |
| "{\n" |
| " // convert to nonsensical coordinates, just in case\n" |
| " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n" |
| " tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n" |
| "\n" |
| " gl_TessLevelOuter[0] = u_tessellationLevel;\n" |
| " gl_TessLevelOuter[1] = u_tessellationLevel;\n" |
| " gl_TessLevelOuter[2] = u_tessellationLevel;\n" |
| " gl_TessLevelInner[0] = u_tessellationLevel;\n"; |
| |
| if (m_calcPerPrimitiveBBox) |
| { |
| buf << "\n" |
| " highp vec4 bboxMin = min(min(transformVec(gl_in[0].gl_Position),\n" |
| " transformVec(gl_in[1].gl_Position)),\n" |
| " transformVec(gl_in[2].gl_Position));\n" |
| " highp vec4 bboxMax = max(max(transformVec(gl_in[0].gl_Position),\n" |
| " transformVec(gl_in[1].gl_Position)),\n" |
| " transformVec(gl_in[2].gl_Position));\n"; |
| } |
| else |
| { |
| buf << "\n" |
| " highp vec4 bboxMin = u_primitiveBBoxMin;\n" |
| " highp vec4 bboxMax = u_primitiveBBoxMax;\n"; |
| } |
| |
| if (!m_useGlobalState) |
| buf << "\n" |
| " ${PRIM_GL_BOUNDING_BOX}[0] = bboxMin;\n" |
| " ${PRIM_GL_BOUNDING_BOX}[1] = bboxMax;\n"; |
| |
| buf << " vp_bbox_expansionSize = 0.0;\n" |
| " vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n" |
| " vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n" |
| " vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n" |
| " vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n" |
| "}\n"; |
| |
| return buf.str(); |
| } |
| |
| std::string GridRenderCase::genTessellationEvaluationSource (void) const |
| { |
| std::ostringstream buf; |
| |
| buf << "${GLSL_VERSION_DECL}\n" |
| "${TESSELLATION_SHADER_REQUIRE}\n" |
| "${GPU_SHADER5_REQUIRE}\n" |
| "layout(triangles) in;\n" |
| "\n" |
| "in highp vec4 tess_ctrl_color[];\n" |
| "out highp vec4 tess_color;\n" |
| "uniform highp vec4 u_posScale;\n" |
| "patch in highp float vp_bbox_expansionSize;\n" |
| "patch in highp vec3 vp_bbox_clipMin;\n" |
| "patch in highp vec3 vp_bbox_clipMax;\n" |
| "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n" |
| "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n" |
| "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n" |
| "\n" |
| "precise gl_Position;\n" |
| "\n" |
| << genShaderFunction(SHADER_FUNC_MIRROR_Y) |
| << "void main()\n" |
| "{\n" |
| " // non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n" |
| " gl_Position = mirrorY(gl_TessCoord.x * gl_in[0].gl_Position.zwyx +\n" |
| " gl_TessCoord.y * gl_in[1].gl_Position.zwyx +\n" |
| " gl_TessCoord.z * gl_in[2].gl_Position.zwyx);\n" |
| " tess_color = tess_ctrl_color[0];\n" |
| " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = vp_bbox_expansionSize;\n" |
| " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n" |
| " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n" |
| "}\n"; |
| |
| return buf.str(); |
| } |
| |
| std::string GridRenderCase::genGeometrySource (void) const |
| { |
| const char* const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color"); |
| std::ostringstream buf; |
| |
| buf << "${GLSL_VERSION_DECL}\n" |
| "${GEOMETRY_SHADER_REQUIRE}\n" |
| "layout(triangles) in;\n" |
| "layout(max_vertices=9, triangle_strip) out;\n" |
| "\n" |
| "in highp vec4 " << colorInputName << "[3];\n" |
| "out highp vec4 geo_color;\n" |
| "uniform highp vec4 u_posScale;\n" |
| "\n" |
| "flat in highp float v_geo_bbox_expansionSize[3];\n" |
| "flat in highp vec3 v_geo_bbox_clipMin[3];\n" |
| "flat in highp vec3 v_geo_bbox_clipMax[3];\n" |
| "flat out highp vec3 v_bbox_clipMin;\n" |
| "flat out highp vec3 v_bbox_clipMax;\n" |
| "flat out highp float v_bbox_expansionSize;\n" |
| << genShaderFunction(SHADER_FUNC_MIRROR_X) |
| << "\n" |
| "void setVisualizationVaryings()\n" |
| "{\n" |
| " v_bbox_expansionSize = v_geo_bbox_expansionSize[0];\n" |
| " v_bbox_clipMin = v_geo_bbox_clipMin[0];\n" |
| " v_bbox_clipMax = v_geo_bbox_clipMax[0];\n" |
| "}\n" |
| "void main()\n" |
| "{\n" |
| " // Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n" |
| " highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n" |
| " highp vec4 p1 = mirrorX(gl_in[1].gl_Position);\n" |
| " highp vec4 p2 = mirrorX(gl_in[2].gl_Position);\n" |
| " highp vec4 pCentroid = vec4((p0.xyz + p1.xyz + p2.xyz) / 3.0, 1.0);\n" |
| " highp vec4 triangleColor = " << colorInputName << "[0];\n" |
| "\n" |
| " gl_Position = p0; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" |
| " gl_Position = p1; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" |
| " gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" |
| " EndPrimitive();\n" |
| "\n" |
| " gl_Position = p1; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" |
| " gl_Position = p2; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" |
| " gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" |
| " EndPrimitive();\n" |
| "\n" |
| " gl_Position = p2; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" |
| " gl_Position = p0; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" |
| " gl_Position = pCentroid; geo_color = triangleColor; setVisualizationVaryings(); EmitVertex();\n" |
| " EndPrimitive();\n" |
| "}\n"; |
| |
| return buf.str(); |
| } |
| |
| GridRenderCase::IterationConfig GridRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const |
| { |
| return generateRandomConfig(0xDEDEDEu * (deUint32)iteration, renderTargetSize); |
| } |
| |
| void GridRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const |
| { |
| const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f); |
| const tcu::Vec4 yellow (1.0f, 1.0f, 0.0f, 1.0f); |
| std::vector<int> cellOrder (m_gridSize * m_gridSize); |
| de::Random rnd (0xDE56789); |
| |
| // generate grid with cells in random order |
| for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) |
| cellOrder[ndx] = ndx; |
| rnd.shuffle(cellOrder.begin(), cellOrder.end()); |
| |
| data.resize(m_gridSize * m_gridSize * 6 * 2); |
| for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) |
| { |
| const int cellNdx = cellOrder[ndx]; |
| const int cellX = cellNdx % m_gridSize; |
| const int cellY = cellNdx / m_gridSize; |
| const tcu::Vec4& cellColor = ((cellX+cellY)%2 == 0) ? (green) : (yellow); |
| |
| data[(ndx * 6 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+0) / float(m_gridSize), float(cellY+0) / float(m_gridSize), 0.0f, 1.0f); |
| data[(ndx * 6 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor; |
| data[(ndx * 6 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+1) / float(m_gridSize), float(cellY+1) / float(m_gridSize), 0.0f, 1.0f); |
| data[(ndx * 6 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor; |
| data[(ndx * 6 + 2) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+0) / float(m_gridSize), float(cellY+1) / float(m_gridSize), 0.0f, 1.0f); |
| data[(ndx * 6 + 2) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor; |
| data[(ndx * 6 + 3) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+0) / float(m_gridSize), float(cellY+0) / float(m_gridSize), 0.0f, 1.0f); |
| data[(ndx * 6 + 3) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor; |
| data[(ndx * 6 + 4) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+1) / float(m_gridSize), float(cellY+0) / float(m_gridSize), 0.0f, 1.0f); |
| data[(ndx * 6 + 4) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor; |
| data[(ndx * 6 + 5) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(cellX+1) / float(m_gridSize), float(cellY+1) / float(m_gridSize), 0.0f, 1.0f); |
| data[(ndx * 6 + 5) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = cellColor; |
| } |
| } |
| |
| void GridRenderCase::renderTestPattern (const IterationConfig& config) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| setupRender(config); |
| |
| if (m_hasTessellationStage) |
| { |
| const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel"); |
| const glw::GLfloat tessLevel = 2.8f; // will be rounded up |
| |
| TCU_CHECK(tessLevelPos != -1); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage; |
| |
| gl.uniform1f(tessLevelPos, tessLevel); |
| gl.patchParameteri(GL_PATCH_VERTICES, 3); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "patch param"); |
| } |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Rendering grid." << tcu::TestLog::EndMessage; |
| |
| gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_TRIANGLES), 0, m_gridSize * m_gridSize * 6); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "draw"); |
| } |
| |
| void GridRenderCase::verifyRenderResult (const IterationConfig& config) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| const ProjectedBBox projectedBBox = projectBoundingBox(config.bbox); |
| const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(projectedBBox, config.viewportSize); |
| const tcu::IVec4 viewportGridOuterArea = getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_OUTWARDS); |
| const tcu::IVec4 viewportGridInnerArea = getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_INWARDS); |
| tcu::Surface viewportSurface (config.viewportSize.x(), config.viewportSize.y()); |
| tcu::Surface errorMask (config.viewportSize.x(), config.viewportSize.y()); |
| bool anyError = false; |
| |
| if (!m_calcPerPrimitiveBBox) |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Projected bounding box: (clip space)\n" |
| << "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n" |
| << "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n" |
| << "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n" |
| << "In viewport coordinates:\n" |
| << "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n" |
| << "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n" |
| << "Verifying render results within the bounding box.\n" |
| << tcu::TestLog::EndMessage; |
| else |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Verifying render result." |
| << tcu::TestLog::EndMessage; |
| |
| if (m_fbo) |
| gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo); |
| glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess()); |
| |
| tcu::clear(errorMask.getAccess(), tcu::IVec4(0,0,0,255)); |
| |
| for (int y = de::max(viewportBBoxArea.y(), 0); y < de::min(viewportBBoxArea.w(), config.viewportSize.y()); ++y) |
| for (int x = de::max(viewportBBoxArea.x(), 0); x < de::min(viewportBBoxArea.z(), config.viewportSize.x()); ++x) |
| { |
| const tcu::RGBA pixel = viewportSurface.getPixel(x, y); |
| const bool outsideGrid = x < viewportGridOuterArea.x() || |
| y < viewportGridOuterArea.y() || |
| x > viewportGridOuterArea.z() || |
| y > viewportGridOuterArea.w(); |
| const bool insideGrid = x > viewportGridInnerArea.x() && |
| y > viewportGridInnerArea.y() && |
| x < viewportGridInnerArea.z() && |
| y < viewportGridInnerArea.w(); |
| |
| bool error = false; |
| |
| if (outsideGrid) |
| { |
| // expect black |
| if (pixel.getRed() != 0 || pixel.getGreen() != 0 || pixel.getBlue() != 0) |
| error = true; |
| } |
| |
| else if (insideGrid) |
| { |
| // expect green, yellow or a combination of these |
| if (pixel.getGreen() != 255 || pixel.getBlue() != 0) |
| error = true; |
| } |
| else |
| { |
| // boundary, allow anything |
| } |
| |
| if (error) |
| { |
| errorMask.setPixel(x, y, tcu::RGBA::red()); |
| anyError = true; |
| } |
| } |
| |
| if (anyError) |
| { |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Image verification failed." |
| << tcu::TestLog::EndMessage |
| << tcu::TestLog::ImageSet("Images", "Image verification") |
| << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess()) |
| << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess()) |
| << tcu::TestLog::EndImageSet; |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); |
| } |
| else |
| { |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Result image ok." |
| << tcu::TestLog::EndMessage |
| << tcu::TestLog::ImageSet("Images", "Image verification") |
| << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess()) |
| << tcu::TestLog::EndImageSet; |
| } |
| } |
| |
| class LineRenderCase : public BBoxRenderCase |
| { |
| public: |
| enum |
| { |
| LINEFLAG_WIDE = 1u << FLAGBIT_USER_BIT, //!< use wide lines |
| }; |
| |
| LineRenderCase (Context& context, const char* name, const char* description, deUint32 flags); |
| ~LineRenderCase (void); |
| |
| private: |
| enum |
| { |
| GREEN_COMPONENT_NDX = 1, |
| BLUE_COMPONENT_NDX = 2, |
| |
| SCAN_ROW_COMPONENT_NDX = GREEN_COMPONENT_NDX, // \note: scans are orthogonal to the line |
| SCAN_COL_COMPONENT_NDX = BLUE_COMPONENT_NDX, |
| }; |
| |
| enum QueryDirection |
| { |
| DIRECTION_HORIZONTAL = 0, |
| DIRECTION_VERTICAL, |
| }; |
| |
| enum ScanResult |
| { |
| SCANRESULT_NUM_LINES_OK_BIT = (1 << 0), |
| SCANRESULT_LINE_WIDTH_OK_BIT = (1 << 1), |
| SCANRESULT_LINE_WIDTH_WARN_BIT = (1 << 2), |
| SCANRESULT_LINE_WIDTH_ERR_BIT = (1 << 3), |
| SCANRESULT_LINE_CONT_OK_BIT = (1 << 4), |
| SCANRESULT_LINE_CONT_ERR_BIT = (1 << 5), |
| SCANRESULT_LINE_CONT_WARN_BIT = (1 << 6), |
| }; |
| |
| void init (void); |
| |
| std::string genVertexSource (void) const; |
| std::string genFragmentSource (void) const; |
| std::string genTessellationControlSource (void) const; |
| std::string genTessellationEvaluationSource (void) const; |
| std::string genGeometrySource (void) const; |
| |
| IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const; |
| void getAttributeData (std::vector<tcu::Vec4>& data) const; |
| void renderTestPattern (const IterationConfig& config); |
| void verifyRenderResult (const IterationConfig& config); |
| |
| tcu::IVec2 getNumberOfLinesRange (int queryAreaBegin, int queryAreaEnd, float patternStart, float patternSize, int viewportArea, QueryDirection queryDir) const; |
| deUint8 scanRow (const tcu::ConstPixelBufferAccess& access, int row, int rowBegin, int rowEnd, int rowViewportBegin, int rowViewportEnd, const tcu::IVec2& numLines, int& floodCounter) const; |
| deUint8 scanColumn (const tcu::ConstPixelBufferAccess& access, int column, int columnBegin, int columnEnd, int columnViewportBegin, int columnViewportEnd, const tcu::IVec2& numLines, int& floodCounter) const; |
| bool checkAreaNumLines (const tcu::ConstPixelBufferAccess& access, const tcu::IVec4& area, int& floodCounter, int componentNdx, const tcu::IVec2& numLines) const; |
| deUint8 checkLineContinuity (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& messageLimitCounter) const; |
| tcu::IVec2 getNumMinimaMaxima (const tcu::ConstPixelBufferAccess& access, int componentNdx) const; |
| deUint8 checkLineWidths (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& floodCounter) const; |
| void printLineWidthError (const tcu::IVec2& pos, int detectedLineWidth, const tcu::IVec2& lineWidthRange, bool isHorizontal, int& floodCounter) const; |
| |
| const int m_patternSide; |
| const bool m_isWideLineCase; |
| const int m_wideLineLineWidth; |
| }; |
| |
| LineRenderCase::LineRenderCase (Context& context, const char* name, const char* description, deUint32 flags) |
| : BBoxRenderCase (context, name, description, 12, flags) |
| , m_patternSide (12) |
| , m_isWideLineCase ((flags & LINEFLAG_WIDE) != 0) |
| , m_wideLineLineWidth (5) |
| { |
| } |
| |
| LineRenderCase::~LineRenderCase (void) |
| { |
| } |
| |
| void LineRenderCase::init (void) |
| { |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Rendering line pattern to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n" |
| << "Vertical lines are green, horizontal lines blue. Using additive blending.\n" |
| << "Line segments are in random order, varying pattern size and location for each iteration.\n" |
| << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel." |
| << tcu::TestLog::EndMessage; |
| |
| if (m_isWideLineCase) |
| { |
| glw::GLfloat lineWidthRange[2] = {0.0f, 0.0f}; |
| m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_LINE_WIDTH_RANGE, lineWidthRange); |
| |
| if (lineWidthRange[1] < (float)m_wideLineLineWidth) |
| throw tcu::NotSupportedError("Test requires line width " + de::toString(m_wideLineLineWidth)); |
| } |
| |
| BBoxRenderCase::init(); |
| } |
| |
| std::string LineRenderCase::genVertexSource (void) const |
| { |
| std::ostringstream buf; |
| |
| buf << "${GLSL_VERSION_DECL}\n" |
| "in highp vec4 a_position;\n" |
| "in highp vec4 a_color;\n" |
| "out highp vec4 vtx_color;\n" |
| "uniform highp vec4 u_posScale;\n" |
| "uniform highp float u_lineWidth;\n" |
| "\n"; |
| if (!m_hasTessellationStage) |
| { |
| DE_ASSERT(m_useGlobalState); |
| buf << "uniform highp vec4 u_primitiveBBoxMin;\n" |
| "uniform highp vec4 u_primitiveBBoxMax;\n" |
| "\n" |
| "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n" |
| "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n" |
| "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n" |
| "\n"; |
| } |
| buf << "void main()\n" |
| "{\n" |
| " highp vec2 patternOffset = u_posScale.xy;\n" |
| " highp vec2 patternScale = u_posScale.zw;\n" |
| " gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n" |
| " vtx_color = a_color;\n"; |
| if (!m_hasTessellationStage) |
| { |
| DE_ASSERT(m_useGlobalState); |
| buf << "\n" |
| " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = u_lineWidth;\n" |
| " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n" |
| " min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n" |
| " vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n" |
| " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n" |
| " min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n" |
| " vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n"; |
| } |
| buf << "}\n"; |
| |
| return buf.str(); |
| } |
| |
| std::string LineRenderCase::genFragmentSource (void) const |
| { |
| const char* const colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color"); |
| std::ostringstream buf; |
| |
| buf << "${GLSL_VERSION_DECL}\n" |
| "in mediump vec4 " << colorInputName << ";\n" |
| "layout(location = 0) out mediump vec4 o_color;\n" |
| << genShaderFunction(SHADER_FUNC_INSIDE_BBOX) |
| << "\n" |
| "void main()\n" |
| "{\n" |
| " mediump vec4 baseColor = " << colorInputName << ";\n" |
| " mediump float redChannel;\n" |
| " if (fragmentInsideTheBBox(gl_FragCoord.z))\n" |
| " redChannel = 0.0;\n" |
| " else\n" |
| " redChannel = 1.0;\n" |
| " o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n" |
| "}\n"; |
| |
| return buf.str(); |
| } |
| |
| std::string LineRenderCase::genTessellationControlSource (void) const |
| { |
| std::ostringstream buf; |
| |
| buf << "${GLSL_VERSION_DECL}\n" |
| "${TESSELLATION_SHADER_REQUIRE}\n" |
| "${PRIMITIVE_BOUNDING_BOX_REQUIRE}\n" |
| "layout(vertices=2) out;" |
| "\n" |
| "in highp vec4 vtx_color[];\n" |
| "out highp vec4 tess_ctrl_color[];\n" |
| "uniform highp float u_tessellationLevel;\n" |
| "uniform highp vec4 u_posScale;\n" |
| "uniform highp float u_lineWidth;\n"; |
| |
| if (!m_calcPerPrimitiveBBox) |
| { |
| buf << "uniform highp vec4 u_primitiveBBoxMin;\n" |
| "uniform highp vec4 u_primitiveBBoxMax;\n"; |
| } |
| |
| buf << "patch out highp float vp_bbox_expansionSize;\n" |
| "patch out highp vec3 vp_bbox_clipMin;\n" |
| "patch out highp vec3 vp_bbox_clipMax;\n"; |
| |
| if (m_calcPerPrimitiveBBox) |
| { |
| buf << "\n"; |
| if (m_hasGeometryStage) |
| buf << genShaderFunction(SHADER_FUNC_MIRROR_X); |
| buf << genShaderFunction(SHADER_FUNC_MIRROR_Y); |
| |
| buf << "vec4 transformVec(in highp vec4 p)\n" |
| "{\n" |
| " return " << ((m_hasGeometryStage) ? ("mirrorX(mirrorY(p))") : ("mirrorY(p)")) << ";\n" |
| "}\n"; |
| } |
| |
| buf << "\n" |
| "void main()\n" |
| "{\n" |
| " // convert to nonsensical coordinates, just in case\n" |
| " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position.wzxy;\n" |
| " tess_ctrl_color[gl_InvocationID] = vtx_color[gl_InvocationID];\n" |
| "\n" |
| " gl_TessLevelOuter[0] = 0.8; // will be rounded up to 1\n" |
| " gl_TessLevelOuter[1] = u_tessellationLevel;\n"; |
| |
| if (m_calcPerPrimitiveBBox) |
| { |
| buf << "\n" |
| " highp vec4 bboxMin = min(transformVec(gl_in[0].gl_Position),\n" |
| " transformVec(gl_in[1].gl_Position));\n" |
| " highp vec4 bboxMax = max(transformVec(gl_in[0].gl_Position),\n" |
| " transformVec(gl_in[1].gl_Position));\n"; |
| } |
| else |
| { |
| buf << "\n" |
| " highp vec4 bboxMin = u_primitiveBBoxMin;\n" |
| " highp vec4 bboxMax = u_primitiveBBoxMax;\n"; |
| } |
| |
| if (!m_useGlobalState) |
| buf << "\n" |
| " ${PRIM_GL_BOUNDING_BOX}[0] = bboxMin;\n" |
| " ${PRIM_GL_BOUNDING_BOX}[1] = bboxMax;\n"; |
| |
| buf << " vp_bbox_expansionSize = u_lineWidth;\n" |
| " vp_bbox_clipMin = min(vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMin.w,\n" |
| " vec3(bboxMin.x, bboxMin.y, bboxMin.z) / bboxMax.w);\n" |
| " vp_bbox_clipMax = max(vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMin.w,\n" |
| " vec3(bboxMax.x, bboxMax.y, bboxMax.z) / bboxMax.w);\n" |
| "}\n"; |
| |
| return buf.str(); |
| } |
| |
| std::string LineRenderCase::genTessellationEvaluationSource (void) const |
| { |
| std::ostringstream buf; |
| |
| buf << "${GLSL_VERSION_DECL}\n" |
| "${TESSELLATION_SHADER_REQUIRE}\n" |
| "layout(isolines) in;" |
| "\n" |
| "in highp vec4 tess_ctrl_color[];\n" |
| "out highp vec4 tess_color;\n" |
| "uniform highp vec4 u_posScale;\n" |
| "\n" |
| "patch in highp float vp_bbox_expansionSize;\n" |
| "patch in highp vec3 vp_bbox_clipMin;\n" |
| "patch in highp vec3 vp_bbox_clipMax;\n" |
| "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n" |
| "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n" |
| "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n" |
| << genShaderFunction(SHADER_FUNC_MIRROR_Y) |
| << "void main()\n" |
| "{\n" |
| " // non-trivial tessellation evaluation shader, convert from nonsensical coords, flip vertically\n" |
| " gl_Position = mirrorY(mix(gl_in[0].gl_Position.zwyx, gl_in[1].gl_Position.zwyx, gl_TessCoord.x));\n" |
| " tess_color = tess_ctrl_color[0];\n" |
| " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = vp_bbox_expansionSize;\n" |
| " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin = vp_bbox_clipMin;\n" |
| " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax = vp_bbox_clipMax;\n" |
| "}\n"; |
| |
| return buf.str(); |
| } |
| |
| std::string LineRenderCase::genGeometrySource (void) const |
| { |
| const char* const colorInputName = (m_hasTessellationStage) ? ("tess_color") : ("vtx_color"); |
| std::ostringstream buf; |
| |
| buf << "${GLSL_VERSION_DECL}\n" |
| "${GEOMETRY_SHADER_REQUIRE}\n" |
| "layout(lines) in;\n" |
| "layout(max_vertices=5, line_strip) out;\n" |
| "\n" |
| "in highp vec4 " << colorInputName << "[2];\n" |
| "out highp vec4 geo_color;\n" |
| "uniform highp vec4 u_posScale;\n" |
| "\n" |
| "\n" |
| "flat in highp float v_geo_bbox_expansionSize[2];\n" |
| "flat in highp vec3 v_geo_bbox_clipMin[2];\n" |
| "flat in highp vec3 v_geo_bbox_clipMax[2];\n" |
| "flat out highp vec3 v_bbox_clipMin;\n" |
| "flat out highp vec3 v_bbox_clipMax;\n" |
| "flat out highp float v_bbox_expansionSize;\n" |
| << genShaderFunction(SHADER_FUNC_MIRROR_X) |
| << "\n" |
| "void setVisualizationVaryings()\n" |
| "{\n" |
| " v_bbox_expansionSize = v_geo_bbox_expansionSize[0];\n" |
| " v_bbox_clipMin = v_geo_bbox_clipMin[0];\n" |
| " v_bbox_clipMax = v_geo_bbox_clipMax[0];\n" |
| "}\n" |
| "void main()\n" |
| "{\n" |
| " // Non-trivial geometry shader: 1-to-3 amplification, mirror horizontally\n" |
| " highp vec4 p0 = mirrorX(gl_in[0].gl_Position);\n" |
| " highp vec4 p1 = mirrorX(gl_in[1].gl_Position);\n" |
| " highp vec4 lineColor = " << colorInputName << "[0];\n" |
| "\n" |
| " // output two separate primitives, just in case\n" |
| " gl_Position = mix(p0, p1, 0.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n" |
| " gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n" |
| " EndPrimitive();\n" |
| "\n" |
| " gl_Position = mix(p0, p1, 0.33); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n" |
| " gl_Position = mix(p0, p1, 0.67); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n" |
| " gl_Position = mix(p0, p1, 1.00); geo_color = lineColor; setVisualizationVaryings(); EmitVertex();\n" |
| " EndPrimitive();\n" |
| "}\n"; |
| |
| return buf.str(); |
| } |
| |
| LineRenderCase::IterationConfig LineRenderCase::generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const |
| { |
| const int numMaxAttempts = 128; |
| |
| // Avoid too narrow viewports, lines could merge together. Require viewport is at least 2.5x the size of the line bodies. |
| for (int attemptNdx = 0; attemptNdx < numMaxAttempts; ++attemptNdx) |
| { |
| const IterationConfig& config = generateRandomConfig((0xDEDEDEu * (deUint32)iteration) ^ (0xABAB13 * attemptNdx), renderTargetSize); |
| |
| if ((float)config.viewportSize.x() * (config.patternSize.x() * 0.5f) > 2.5f * (float)m_patternSide * (float)m_wideLineLineWidth && |
| (float)config.viewportSize.y() * (config.patternSize.y() * 0.5f) > 2.5f * (float)m_patternSide * (float)m_wideLineLineWidth) |
| { |
| return config; |
| } |
| } |
| |
| DE_ASSERT(false); |
| return IterationConfig(); |
| } |
| |
| void LineRenderCase::getAttributeData (std::vector<tcu::Vec4>& data) const |
| { |
| const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f); |
| const tcu::Vec4 blue (0.0f, 0.0f, 1.0f, 1.0f); |
| std::vector<int> cellOrder (m_patternSide * m_patternSide * 2); |
| de::Random rnd (0xDE12345); |
| |
| // generate crosshatch pattern with segments in random order |
| for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) |
| cellOrder[ndx] = ndx; |
| rnd.shuffle(cellOrder.begin(), cellOrder.end()); |
| |
| data.resize(cellOrder.size() * 4); |
| for (int ndx = 0; ndx < (int)cellOrder.size(); ++ndx) |
| { |
| const int segmentID = cellOrder[ndx]; |
| const int direction = segmentID & 0x01; |
| const int majorCoord = (segmentID >> 1) / m_patternSide; |
| const int minorCoord = (segmentID >> 1) % m_patternSide; |
| |
| if (direction) |
| { |
| data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(minorCoord) / float(m_patternSide), float(majorCoord) / float(m_patternSide), 0.0f, 1.0f); |
| data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green; |
| data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(minorCoord) / float(m_patternSide), float(majorCoord + 1) / float(m_patternSide), 0.0f, 1.0f); |
| data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = green; |
| } |
| else |
| { |
| data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(majorCoord) / float(m_patternSide), float(minorCoord) / float(m_patternSide), 0.0f, 1.0f); |
| data[(ndx * 2 + 0) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue; |
| data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_POS_VEC_NDX] = tcu::Vec4(float(majorCoord + 1) / float(m_patternSide), float(minorCoord) / float(m_patternSide), 0.0f, 1.0f); |
| data[(ndx * 2 + 1) * VA_NUM_ATTRIB_VECS + VA_COL_VEC_NDX] = blue; |
| } |
| } |
| } |
| |
| void LineRenderCase::renderTestPattern (const IterationConfig& config) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| setupRender(config); |
| |
| if (m_hasTessellationStage) |
| { |
| const glw::GLint tessLevelPos = gl.getUniformLocation(m_program->getProgram(), "u_tessellationLevel"); |
| const glw::GLfloat tessLevel = 2.8f; // will be rounded up |
| |
| TCU_CHECK(tessLevelPos != -1); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "u_tessellationLevel = " << tessLevel << tcu::TestLog::EndMessage; |
| |
| gl.uniform1f(tessLevelPos, tessLevel); |
| gl.patchParameteri(GL_PATCH_VERTICES, 2); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "patch param"); |
| } |
| |
| if (m_isWideLineCase) |
| gl.lineWidth((float)m_wideLineLineWidth); |
| |
| gl.uniform1f(gl.getUniformLocation(m_program->getProgram(), "u_lineWidth"), (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f)); |
| |
| m_testCtx.getLog() << tcu::TestLog::Message << "Rendering pattern." << tcu::TestLog::EndMessage; |
| |
| gl.enable(GL_BLEND); |
| gl.blendFunc(GL_ONE, GL_ONE); |
| gl.blendEquation(GL_FUNC_ADD); |
| |
| gl.drawArrays((m_hasTessellationStage) ? (GL_PATCHES) : (GL_LINES), 0, m_patternSide * m_patternSide * 2 * 2); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "draw"); |
| } |
| |
| void LineRenderCase::verifyRenderResult (const IterationConfig& config) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| const bool isMsaa = m_context.getRenderTarget().getNumSamples() > 1; |
| const ProjectedBBox projectedBBox = projectBoundingBox(config.bbox); |
| const float lineWidth = (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f); |
| const tcu::IVec4 viewportBBoxArea = getViewportBoundingBoxArea(projectedBBox, config.viewportSize, lineWidth); |
| const tcu::IVec4 viewportPatternArea = getViewportPatternArea(config.patternPos, config.patternSize, config.viewportSize, ROUND_INWARDS); |
| const tcu::IVec2 expectedHorizontalLines = getNumberOfLinesRange(viewportBBoxArea.y(), viewportBBoxArea.w(), config.patternPos.y(), config.patternSize.y(), config.viewportSize.y(), DIRECTION_VERTICAL); |
| const tcu::IVec2 expectedVerticalLines = getNumberOfLinesRange(viewportBBoxArea.x(), viewportBBoxArea.z(), config.patternPos.x(), config.patternSize.x(), config.viewportSize.x(), DIRECTION_HORIZONTAL); |
| const tcu::IVec4 verificationArea = tcu::IVec4(de::max(viewportBBoxArea.x(), 0), |
| de::max(viewportBBoxArea.y(), 0), |
| de::min(viewportBBoxArea.z(), config.viewportSize.x()), |
| de::min(viewportBBoxArea.w(), config.viewportSize.y())); |
| |
| tcu::Surface viewportSurface (config.viewportSize.x(), config.viewportSize.y()); |
| int messageLimitCounter = 8; |
| |
| enum ScanResultCodes |
| { |
| SCANRESULT_NUM_LINES_ERR = 0, |
| SCANRESULT_LINE_WIDTH_MSAA = 1, |
| SCANRESULT_LINE_WIDTH_WARN = 2, |
| SCANRESULT_LINE_WIDTH_ERR = 3, |
| SCANRESULT_LINE_CONT_ERR = 4, |
| SCANRESULT_LINE_CONT_WARN = 5, |
| SCANRESULT_LINE_LAST |
| }; |
| |
| int rowScanResult[SCANRESULT_LINE_LAST] = {0, 0, 0, 0, 0, 0}; |
| int columnScanResult[SCANRESULT_LINE_LAST] = {0, 0, 0, 0, 0, 0}; |
| bool anyError = false; |
| bool msaaRelaxationRequired = false; |
| bool hwIssueRelaxationRequired = false; |
| |
| if (!m_calcPerPrimitiveBBox) |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Projected bounding box: (clip space)\n" |
| << "\tx: [" << projectedBBox.min.x() << "," << projectedBBox.max.x() << "]\n" |
| << "\ty: [" << projectedBBox.min.y() << "," << projectedBBox.max.y() << "]\n" |
| << "\tz: [" << projectedBBox.min.z() << "," << projectedBBox.max.z() << "]\n" |
| << "In viewport coordinates:\n" |
| << "\tx: [" << viewportBBoxArea.x() << ", " << viewportBBoxArea.z() << "]\n" |
| << "\ty: [" << viewportBBoxArea.y() << ", " << viewportBBoxArea.w() << "]\n" |
| << "Verifying render results within the bounding box:\n" |
| << tcu::TestLog::EndMessage; |
| else |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Verifying render result:" |
| << tcu::TestLog::EndMessage; |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "\tCalculating number of horizontal and vertical lines within the bounding box, expecting:\n" |
| << "\t[" << expectedHorizontalLines.x() << ", " << expectedHorizontalLines.y() << "] horizontal lines.\n" |
| << "\t[" << expectedVerticalLines.x() << ", " << expectedVerticalLines.y() << "] vertical lines.\n" |
| << tcu::TestLog::EndMessage; |
| |
| if (m_fbo) |
| gl.bindFramebuffer(GL_READ_FRAMEBUFFER, **m_fbo); |
| glu::readPixels(m_context.getRenderContext(), config.viewportPos.x(), config.viewportPos.y(), viewportSurface.getAccess()); |
| |
| // scan rows |
| for (int y = de::max(verificationArea.y(), viewportPatternArea.y()); y < de::min(verificationArea.w(), viewportPatternArea.w()); ++y) |
| { |
| const deUint8 result = scanRow(viewportSurface.getAccess(), |
| y, |
| verificationArea.x(), |
| verificationArea.z(), |
| de::max(verificationArea.x(), viewportPatternArea.x()), |
| de::min(verificationArea.z(), viewportPatternArea.z()), |
| expectedVerticalLines, |
| messageLimitCounter); |
| |
| if ((result & SCANRESULT_NUM_LINES_OK_BIT) == 0) |
| rowScanResult[SCANRESULT_NUM_LINES_ERR]++; |
| if ((result & SCANRESULT_LINE_CONT_OK_BIT) == 0) |
| { |
| if ((result & SCANRESULT_LINE_CONT_WARN_BIT) != 0) |
| rowScanResult[SCANRESULT_LINE_CONT_WARN]++; |
| else |
| rowScanResult[SCANRESULT_LINE_CONT_ERR]++; |
| } |
| else if ((result & SCANRESULT_LINE_WIDTH_OK_BIT) == 0) |
| { |
| if (m_isWideLineCase && isMsaa) |
| { |
| // multisampled wide lines might not be supported |
| rowScanResult[SCANRESULT_LINE_WIDTH_MSAA]++; |
| } |
| else if ((result & SCANRESULT_LINE_WIDTH_ERR_BIT) == 0 && |
| (result & SCANRESULT_LINE_WIDTH_WARN_BIT) != 0) |
| { |
| rowScanResult[SCANRESULT_LINE_WIDTH_WARN]++; |
| } |
| else |
| rowScanResult[SCANRESULT_LINE_WIDTH_ERR]++; |
| } |
| } |
| |
| // scan columns |
| for (int x = de::max(verificationArea.x(), viewportPatternArea.x()); x < de::min(verificationArea.z(), viewportPatternArea.z()); ++x) |
| { |
| const deUint8 result = scanColumn(viewportSurface.getAccess(), |
| x, |
| verificationArea.y(), |
| verificationArea.w(), |
| de::min(verificationArea.y(), viewportPatternArea.y()), |
| de::min(verificationArea.w(), viewportPatternArea.w()), |
| expectedHorizontalLines, |
| messageLimitCounter); |
| |
| if ((result & SCANRESULT_NUM_LINES_OK_BIT) == 0) |
| columnScanResult[SCANRESULT_NUM_LINES_ERR]++; |
| if ((result & SCANRESULT_LINE_CONT_OK_BIT) == 0) |
| { |
| if ((result & SCANRESULT_LINE_CONT_WARN_BIT) != 0) |
| columnScanResult[SCANRESULT_LINE_CONT_WARN]++; |
| else |
| columnScanResult[SCANRESULT_LINE_CONT_ERR]++; |
| } |
| else if ((result & SCANRESULT_LINE_WIDTH_OK_BIT) == 0) |
| { |
| if (m_isWideLineCase && isMsaa) |
| { |
| // multisampled wide lines might not be supported |
| columnScanResult[SCANRESULT_LINE_WIDTH_MSAA]++; |
| } |
| else if ((result & SCANRESULT_LINE_WIDTH_ERR_BIT) == 0 && |
| (result & SCANRESULT_LINE_WIDTH_WARN_BIT) != 0) |
| { |
| columnScanResult[SCANRESULT_LINE_WIDTH_WARN]++; |
| } |
| else |
| columnScanResult[SCANRESULT_LINE_WIDTH_ERR]++; |
| } |
| } |
| |
| if (columnScanResult[SCANRESULT_LINE_WIDTH_ERR] != 0 || rowScanResult[SCANRESULT_LINE_WIDTH_ERR] != 0) |
| anyError = true; |
| else if(columnScanResult[SCANRESULT_LINE_CONT_ERR] != 0 || rowScanResult[SCANRESULT_LINE_CONT_ERR] != 0) |
| anyError = true; |
| else if (columnScanResult[SCANRESULT_LINE_WIDTH_MSAA] != 0 || rowScanResult[SCANRESULT_LINE_WIDTH_MSAA] != 0) |
| msaaRelaxationRequired = true; |
| else if (columnScanResult[SCANRESULT_LINE_WIDTH_WARN] != 0 || rowScanResult[SCANRESULT_LINE_WIDTH_WARN] != 0) |
| hwIssueRelaxationRequired = true; |
| else if (columnScanResult[SCANRESULT_NUM_LINES_ERR] != 0) |
| { |
| // found missing lines in a columnw and row line continuity check reported a warning (not an error) -> line width precision issue |
| if (rowScanResult[SCANRESULT_LINE_CONT_ERR] == 0 && rowScanResult[SCANRESULT_LINE_CONT_WARN]) |
| hwIssueRelaxationRequired = true; |
| else |
| anyError = true; |
| } |
| else if (rowScanResult[SCANRESULT_NUM_LINES_ERR] != 0) |
| { |
| // found missing lines in a row and column line continuity check reported a warning (not an error) -> line width precision issue |
| if (columnScanResult[SCANRESULT_LINE_CONT_ERR] == 0 && columnScanResult[SCANRESULT_LINE_CONT_WARN]) |
| hwIssueRelaxationRequired = true; |
| else |
| anyError = true; |
| } |
| |
| if (anyError || msaaRelaxationRequired || hwIssueRelaxationRequired) |
| { |
| if (messageLimitCounter < 0) |
| m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (-messageLimitCounter) << " row/column error descriptions." << tcu::TestLog::EndMessage; |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Image verification failed." |
| << tcu::TestLog::EndMessage |
| << tcu::TestLog::ImageSet("Images", "Image verification") |
| << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess()) |
| << tcu::TestLog::EndImageSet; |
| |
| if (anyError) |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); |
| else if (hwIssueRelaxationRequired) |
| { |
| // Line width hw issue |
| m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Line width verification failed"); |
| } |
| else |
| { |
| // MSAA wide lines are optional |
| m_testCtx.setTestResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, "Multisampled wide line verification failed"); |
| } |
| } |
| else |
| { |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Result image ok." |
| << tcu::TestLog::EndMessage |
| << tcu::TestLog::ImageSet("Images", "Image verification") |
| << tcu::TestLog::Image("Viewport", "Viewport contents", viewportSurface.getAccess()) |
| << tcu::TestLog::EndImageSet; |
| } |
| } |
| |
| tcu::IVec2 LineRenderCase::getNumberOfLinesRange (int queryAreaBegin, int queryAreaEnd, float patternStart, float patternSize, int viewportArea, QueryDirection queryDir) const |
| { |
| // pattern is not symmetric due to mirroring |
| const int patternStartNdx = (queryDir == DIRECTION_HORIZONTAL) ? ((m_hasGeometryStage) ? (1) : (0)) : ((m_hasTessellationStage) ? (1) : (0)); |
| const int patternEndNdx = patternStartNdx + m_patternSide; |
| |
| int numLinesMin = 0; |
| int numLinesMax = 0; |
| |
| for (int lineNdx = patternStartNdx; lineNdx < patternEndNdx; ++lineNdx) |
| { |
| const float linePos = (patternStart + (float(lineNdx) / float(m_patternSide)) * patternSize) * 0.5f + 0.5f; |
| const float lineWidth = (m_isWideLineCase) ? ((float)m_wideLineLineWidth) : (1.0f); |
| |
| if (linePos * (float)viewportArea > (float)queryAreaBegin + 1.0f && |
| linePos * (float)viewportArea < (float)queryAreaEnd - 1.0f) |
| { |
| // line center is within the area |
| ++numLinesMin; |
| ++numLinesMax; |
| } |
| else if (linePos * (float)viewportArea > (float)queryAreaBegin - lineWidth*0.5f - 1.0f && |
| linePos * (float)viewportArea < (float)queryAreaEnd + lineWidth*0.5f + 1.0f) |
| { |
| // line could leak into area |
| ++numLinesMax; |
| } |
| } |
| |
| return tcu::IVec2(numLinesMin, numLinesMax); |
| } |
| |
| deUint8 LineRenderCase::scanRow (const tcu::ConstPixelBufferAccess& access, int row, int rowBegin, int rowEnd, int rowViewportBegin, int rowViewportEnd, const tcu::IVec2& numLines, int& messageLimitCounter) const |
| { |
| const bool numLinesOk = checkAreaNumLines(access, tcu::IVec4(rowBegin, row, rowEnd - rowBegin, 1), messageLimitCounter, SCAN_ROW_COMPONENT_NDX, numLines); |
| const deUint8 lineWidthRes = checkLineWidths(access, tcu::IVec2(rowBegin, row), tcu::IVec2(rowEnd, row), SCAN_ROW_COMPONENT_NDX, messageLimitCounter); |
| const deUint8 lineContinuityRes = checkLineContinuity(access, tcu::IVec2(rowViewportBegin, row), tcu::IVec2(rowViewportEnd, row), SCAN_COL_COMPONENT_NDX, messageLimitCounter); |
| deUint8 result = 0; |
| |
| if (numLinesOk) |
| result |= (deUint8)SCANRESULT_NUM_LINES_OK_BIT; |
| |
| if (lineContinuityRes == 0) |
| result |= (deUint8)SCANRESULT_LINE_CONT_OK_BIT; |
| else |
| result |= lineContinuityRes; |
| |
| if (lineWidthRes == 0) |
| result |= (deUint8)SCANRESULT_LINE_WIDTH_OK_BIT; |
| else |
| result |= lineWidthRes; |
| |
| return result; |
| } |
| |
| deUint8 LineRenderCase::scanColumn (const tcu::ConstPixelBufferAccess& access, int column, int columnBegin, int columnEnd, int columnViewportBegin, int columnViewportEnd, const tcu::IVec2& numLines, int& messageLimitCounter) const |
| { |
| const bool numLinesOk = checkAreaNumLines(access, tcu::IVec4(column, columnBegin, 1, columnEnd - columnBegin), messageLimitCounter, SCAN_COL_COMPONENT_NDX, numLines); |
| const deUint8 lineWidthRes = checkLineWidths(access, tcu::IVec2(column, columnBegin), tcu::IVec2(column, columnEnd), SCAN_COL_COMPONENT_NDX, messageLimitCounter); |
| const deUint8 lineContinuityRes = checkLineContinuity(access, tcu::IVec2(column, columnViewportBegin), tcu::IVec2(column, columnViewportEnd), SCAN_ROW_COMPONENT_NDX, messageLimitCounter); |
| deUint8 result = 0; |
| |
| if (numLinesOk) |
| result |= (deUint8)SCANRESULT_NUM_LINES_OK_BIT; |
| |
| if (lineContinuityRes == 0) |
| result |= (deUint8)SCANRESULT_LINE_CONT_OK_BIT; |
| else |
| result |= lineContinuityRes; |
| |
| if (lineWidthRes == 0) |
| result |= (deUint8)SCANRESULT_LINE_WIDTH_OK_BIT; |
| else |
| result |= lineWidthRes; |
| |
| return result; |
| } |
| |
| bool LineRenderCase::checkAreaNumLines (const tcu::ConstPixelBufferAccess& access, const tcu::IVec4& area, int& messageLimitCounter, int componentNdx, const tcu::IVec2& numLines) const |
| { |
| // Num maxima == num lines |
| const tcu::ConstPixelBufferAccess subAccess = tcu::getSubregion(access, area.x(), area.y(), 0, area.z(), area.w(), 1); |
| const tcu::IVec2 numMinimaMaxima = getNumMinimaMaxima(subAccess, componentNdx); |
| const int numMaxima = numMinimaMaxima.y(); |
| |
| // In valid range |
| if (numMaxima >= numLines.x() && numMaxima <= numLines.y()) |
| return true; |
| |
| if (--messageLimitCounter < 0) |
| return false; |
| |
| if (area.z() == 1) |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "On column " << area.x() << ", y: [" << area.y() << "," << (area.y()+area.w()) << "):\n" |
| << "\tExpected [" << numLines.x() << ", " << numLines.y() << "] lines but the number of lines in the area is " << numMaxima |
| << tcu::TestLog::EndMessage; |
| else |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "On row " << area.y() << ", x: [" << area.x() << "," << (area.x()+area.z()) << "):\n" |
| << "\tExpected [" << numLines.x() << ", " << numLines.y() << "] lines but the number of lines in the area is " << numMaxima |
| << tcu::TestLog::EndMessage; |
| |
| return false; |
| } |
| |
| tcu::IVec2 LineRenderCase::getNumMinimaMaxima (const tcu::ConstPixelBufferAccess& access, int componentNdx) const |
| { |
| DE_ASSERT(access.getWidth() == 1 || access.getHeight() == 1); |
| |
| int previousValue = -1; |
| int previousSign = 0; |
| int numMinima = 0; |
| int numMaxima = 0; |
| |
| for (int y = 0; y < access.getHeight(); ++y) |
| for (int x = 0; x < access.getWidth(); ++x) |
| { |
| const int componentValue = access.getPixelInt(x, y)[componentNdx]; |
| |
| if (previousValue != -1) |
| { |
| const int sign = (componentValue > previousValue) ? (+1) : (componentValue < previousValue) ? (-1) : (0); |
| |
| // local minima/maxima in sign changes (zero signless) |
| if (sign != 0 && sign == -previousSign) |
| { |
| previousSign = sign; |
| |
| if (sign > 0) |
| ++numMinima; |
| else |
| ++numMaxima; |
| } |
| else if (sign != 0 && previousSign == 0) |
| { |
| previousSign = sign; |
| |
| // local extreme at the start boundary |
| if (sign > 0) |
| ++numMinima; |
| else |
| ++numMaxima; |
| } |
| } |
| |
| previousValue = componentValue; |
| } |
| |
| // local extreme at the end boundary |
| if (previousSign > 0) |
| ++numMaxima; |
| else if (previousSign < 0) |
| ++numMinima; |
| else |
| { |
| ++numMaxima; |
| ++numMinima; |
| } |
| |
| return tcu::IVec2(numMinima, numMaxima); |
| } |
| |
| deUint8 LineRenderCase::checkLineContinuity (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& messageLimitCounter) const |
| { |
| bool line = false; |
| const tcu::IVec2 advance = (begin.x() == end.x()) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0)); |
| int missedPixels = 0; |
| int totalPixels = 0; |
| deUint8 errorMask = 0; |
| |
| for (tcu::IVec2 cursor = begin; cursor != end; cursor += advance) |
| { |
| const bool hit = (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0); |
| |
| if (hit) |
| line = true; |
| else if (line && !hit) |
| { |
| // non-continuous line detected |
| const tcu::IVec2 advanceNeighbor = tcu::IVec2(1, 1) - advance; |
| const tcu::IVec2 cursorNeighborPos = cursor + advanceNeighbor; |
| const tcu::IVec2 cursorNeighborNeg = cursor - advanceNeighbor; |
| // hw precision issues may lead to a line being non-straight -> check neighboring pixels |
| if ((access.getPixelInt(cursorNeighborPos.x(), cursorNeighborPos.y())[componentNdx] == 0) && (access.getPixelInt(cursorNeighborNeg.x(), cursorNeighborNeg.y())[componentNdx] == 0)) |
| ++missedPixels; |
| } |
| ++totalPixels; |
| } |
| |
| if (missedPixels > 0) |
| { |
| if (--messageLimitCounter >= 0) |
| { |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Found non-continuous " << ((advance.x() == 1) ? ("horizontal") : ("vertical")) << " line near " << begin << ". " |
| << "Missed pixels: " << missedPixels |
| << tcu::TestLog::EndMessage; |
| } |
| // allow 10% missing pixels for warning |
| if (missedPixels <= deRoundFloatToInt32((float)totalPixels * 0.1f)) |
| errorMask = SCANRESULT_LINE_CONT_WARN_BIT; |
| else |
| errorMask = SCANRESULT_LINE_CONT_ERR_BIT; |
| } |
| |
| return errorMask; |
| } |
| |
| deUint8 LineRenderCase::checkLineWidths (const tcu::ConstPixelBufferAccess& access, const tcu::IVec2& begin, const tcu::IVec2& end, int componentNdx, int& messageLimitCounter) const |
| { |
| const bool multisample = m_context.getRenderTarget().getNumSamples() > 1; |
| const int lineRenderWidth = (m_isWideLineCase) ? (m_wideLineLineWidth) : 1; |
| const tcu::IVec2 lineWidthRange = (multisample) |
| ? (tcu::IVec2(lineRenderWidth, lineRenderWidth+1)) // multisampled "smooth" lines may spread to neighboring pixel |
| : (tcu::IVec2(lineRenderWidth, lineRenderWidth)); |
| const tcu::IVec2 relaxedLineWidthRange = (tcu::IVec2(lineRenderWidth-1, lineRenderWidth+1)); |
| |
| int lineWidth = 0; |
| bool bboxLimitedLine = false; |
| deUint8 errorMask = 0; |
| |
| const tcu::IVec2 advance = (begin.x() == end.x()) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0)); |
| |
| // fragments before begin? |
| if (access.getPixelInt(begin.x(), begin.y())[componentNdx] != 0) |
| { |
| bboxLimitedLine = true; |
| |
| for (tcu::IVec2 cursor = begin - advance;; cursor -= advance) |
| { |
| if (cursor.x() < 0 || cursor.y() < 0) |
| { |
| break; |
| } |
| else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0) |
| { |
| ++lineWidth; |
| } |
| else |
| break; |
| } |
| } |
| |
| for (tcu::IVec2 cursor = begin; cursor != end; cursor += advance) |
| { |
| const bool hit = (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0); |
| |
| if (hit) |
| ++lineWidth; |
| else if (lineWidth) |
| { |
| // Line is allowed to be be thinner if it borders the bbox boundary (since part of the line might have been discarded). |
| const bool incorrectLineWidth = (lineWidth < lineWidthRange.x() && !bboxLimitedLine) || (lineWidth > lineWidthRange.y()); |
| |
| if (incorrectLineWidth) |
| { |
| const bool incorrectRelaxedLineWidth = (lineWidth < relaxedLineWidthRange.x() && !bboxLimitedLine) || (lineWidth > relaxedLineWidthRange.y()); |
| |
| if (incorrectRelaxedLineWidth) |
| errorMask |= SCANRESULT_LINE_WIDTH_ERR_BIT; |
| else |
| errorMask |= SCANRESULT_LINE_WIDTH_WARN_BIT; |
| |
| printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter); |
| } |
| |
| lineWidth = 0; |
| bboxLimitedLine = false; |
| } |
| } |
| |
| // fragments after end? |
| if (lineWidth) |
| { |
| for (tcu::IVec2 cursor = end;; cursor += advance) |
| { |
| if (cursor.x() >= access.getWidth() || cursor.y() >= access.getHeight()) |
| { |
| if (lineWidth > lineWidthRange.y()) |
| { |
| if (lineWidth > relaxedLineWidthRange.y()) |
| errorMask |= SCANRESULT_LINE_WIDTH_ERR_BIT; |
| else |
| errorMask |= SCANRESULT_LINE_WIDTH_WARN_BIT; |
| |
| printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter); |
| } |
| |
| break; |
| } |
| else if (access.getPixelInt(cursor.x(), cursor.y())[componentNdx] != 0) |
| { |
| ++lineWidth; |
| } |
| else if (lineWidth) |
| { |
| // only check that line width is not larger than expected. Line width may be smaller |
| // since the scanning 'cursor' is now outside the bounding box. |
| const bool incorrectLineWidth = (lineWidth > lineWidthRange.y()); |
| |
| if (incorrectLineWidth) |
| { |
| const bool incorrectRelaxedLineWidth = (lineWidth > relaxedLineWidthRange.y()); |
| |
| if (incorrectRelaxedLineWidth) |
| errorMask |= SCANRESULT_LINE_WIDTH_ERR_BIT; |
| else |
| errorMask |= SCANRESULT_LINE_WIDTH_WARN_BIT; |
| |
| printLineWidthError(cursor, lineWidth, lineWidthRange, advance.x() == 0, messageLimitCounter); |
| } |
| |
| lineWidth = 0; |
| } |
| } |
| } |
| |
| return errorMask; |
| } |
| |
| void LineRenderCase::printLineWidthError (const tcu::IVec2& pos, int detectedLineWidth, const tcu::IVec2& lineWidthRange, bool isHorizontal, int& messageLimitCounter) const |
| { |
| if (--messageLimitCounter < 0) |
| return; |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Found incorrect line width near " << pos << ": (" << ((isHorizontal) ? ("horizontal") : ("vertical")) << " line)\n" |
| << "\tExpected line width in range [" << lineWidthRange.x() << ", " << lineWidthRange.y() << "] but found " << detectedLineWidth |
| << tcu::TestLog::EndMessage; |
| } |
| |
| class PointRenderCase : public BBoxRenderCase |
| { |
| public: |
| enum |
| { |
| POINTFLAG_WIDE = 1u << FLAGBIT_USER_BIT, //!< use wide points |
| }; |
| struct GeneratedPoint |
| { |
| tcu::Vec2 center; |
| int size; |
| bool even; |
| }; |
| |
| PointRenderCase (Context& context, const char* name, const char* description, deUint32 flags); |
| ~PointRenderCase (void); |
| |
| private: |
| enum ResultPointType |
| { |
| POINT_FULL = 0, |
| POINT_PARTIAL |
| }; |
| |
| void init (void); |
| void deinit (void); |
| |
| std::string genVertexSource (void) const; |
| std::string genFragmentSource (void) const; |
| std::string genTessellationControlSource (void) const; |
| std::string genTessellationEvaluationSource (void) const; |
| std::string genGeometrySource (void) const; |
| |
| IterationConfig generateConfig (int iteration, const tcu::IVec2& renderTargetSize) const; |
| void generateAttributeData (void); |
| void getAttributeData (std::vector<tcu::Vec4>& data) const; |
| void renderTestPattern (const IterationConfig& config); |
| void verifyRenderResult (const IterationConfig& config); |
| |
| void genReferencePointData (const IterationConfig& config, std::vector<GeneratedPoint>& data) const; |
| bool verifyNarrowPointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter); |
| bool verifyWidePointPattern (const tcu::Surface& viewport, const std::vector<GeneratedPoint>& refPoints, const ProjectedBBox& bbox, int& logFloodCounter); |
| bool verifyWidePoint (const tcu::Surface& viewport, const GeneratedPoint& refPoint, const ProjectedBBox& bbox, ResultPointType pointType, int& logFloodCounter); |
| bool verifyWidePointAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, const GeneratedPoint& refPoint, const tcu::IVec4& bbox, ResultPointType pointType, int componentNdx, int& logFloodCounter); |
| tcu::IVec2 scanPointWidthAt (const tcu::IVec2& pointPos, const tcu::Surface& viewport, int expectedPointSize, int componentNdx) const; |
| |
| const int m_numStripes; |
| const bool m_isWidePointCase; |
| std::vector<tcu::Vec4> m_attribData; |
| }; |
| |
| PointRenderCase::PointRenderCase (Context& context, const char* name, const char* description, deUint32 flags) |
| : BBoxRenderCase (context, name, description, 12, flags) |
| , m_numStripes (4) |
| , m_isWidePointCase ((flags & POINTFLAG_WIDE) != 0) |
| { |
| } |
| |
| PointRenderCase::~PointRenderCase (void) |
| { |
| } |
| |
| void PointRenderCase::init (void) |
| { |
| if (m_isWidePointCase) |
| { |
| // extensions |
| if (m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_point_size")) |
| throw tcu::NotSupportedError("Test requires GL_EXT_geometry_point_size extension"); |
| if (m_hasTessellationStage && !m_hasGeometryStage && !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_point_size")) |
| throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_point_size extension"); |
| |
| // point size range |
| { |
| glw::GLfloat pointSizeRange[2] = {0.0f, 0.0f}; |
| m_context.getRenderContext().getFunctions().getFloatv(GL_ALIASED_POINT_SIZE_RANGE, pointSizeRange); |
| |
| if (pointSizeRange[1] < 5.0f) |
| throw tcu::NotSupportedError("Test requires point size 5.0"); |
| } |
| } |
| |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Rendering point pattern to " << ((m_renderTarget == RENDERTARGET_DEFAULT) ? ("default frame buffer") : ("fbo")) << ".\n" |
| << "Half of the points are green, half blue. Using additive blending.\n" |
| << "Points are in random order, varying pattern size and location for each iteration.\n" |
| << "Marking all discardable fragments (fragments outside the bounding box) with a fully saturated red channel." |
| << tcu::TestLog::EndMessage; |
| |
| generateAttributeData(); |
| |
| BBoxRenderCase::init(); |
| } |
| |
| void PointRenderCase::deinit (void) |
| { |
| // clear data |
| m_attribData = std::vector<tcu::Vec4>(); |
| |
| // deinit parent |
| BBoxRenderCase::deinit(); |
| } |
| |
| std::string PointRenderCase::genVertexSource (void) const |
| { |
| std::ostringstream buf; |
| |
| buf << "${GLSL_VERSION_DECL}\n" |
| "in highp vec4 a_position;\n" |
| "in highp vec4 a_color;\n" |
| "out highp vec4 vtx_color;\n" |
| "uniform highp vec4 u_posScale;\n" |
| "\n"; |
| if (!m_hasTessellationStage) |
| { |
| DE_ASSERT(m_useGlobalState); |
| buf << "uniform highp vec4 u_primitiveBBoxMin;\n" |
| "uniform highp vec4 u_primitiveBBoxMax;\n" |
| "\n" |
| "flat out highp float v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize;\n" |
| "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin;\n" |
| "flat out highp vec3 v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax;\n" |
| "\n"; |
| } |
| |
| buf << "void main()\n" |
| "{\n" |
| " highp vec2 patternOffset = u_posScale.xy;\n" |
| " highp vec2 patternScale = u_posScale.zw;\n" |
| " highp float pointSize = " |
| << ((m_isWidePointCase && !m_hasTessellationStage && !m_hasGeometryStage) ? ("(a_color.g > 0.0) ? (5.0) : (3.0)") : ("1.0")) |
| << ";\n" |
| << " gl_Position = vec4(a_position.xy * patternScale + patternOffset, a_position.z, a_position.w);\n" |
| " gl_PointSize = pointSize;\n" |
| " vtx_color = a_color;\n"; |
| |
| if (!m_hasTessellationStage) |
| { |
| DE_ASSERT(m_useGlobalState); |
| buf << "\n" |
| " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_expansionSize = pointSize;\n" |
| " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMin =\n" |
| " min(vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMin.w,\n" |
| " vec3(u_primitiveBBoxMin.x, u_primitiveBBoxMin.y, u_primitiveBBoxMin.z) / u_primitiveBBoxMax.w);\n" |
| " v_" << (m_hasGeometryStage ? "geo_" : "") << "bbox_clipMax =\n" |
| " min(vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMin.w,\n" |
| " vec3(u_primitiveBBoxMax.x, u_primitiveBBoxMax.y, u_primitiveBBoxMax.z) / u_primitiveBBoxMax.w);\n"; |
| } |
| |
| buf << "}\n"; |
| return buf.str(); |
| } |
| |
| std::string PointRenderCase::genFragmentSource (void) const |
| { |
| const char* const colorInputName = (m_hasGeometryStage) ? ("geo_color") : (m_hasTessellationStage) ? ("tess_color") : ("vtx_color"); |
| std::ostringstream buf; |
| |
| buf << "${GLSL_VERSION_DECL}\n" |
| "in mediump vec4 " << colorInputName << ";\n" |
| "layout(location = 0) out mediump vec4 o_color;\n" |
| << genShaderFunction(SHADER_FUNC_INSIDE_BBOX) |
| << "\n" |
| "void main()\n" |
| "{\n" |
| " mediump vec4 baseColor = " << colorInputName << ";\n" |
| " mediump float redChannel;\n" |
| " if (fragmentInsideTheBBox(gl_FragCoord.z))\n" |
| " redChannel = 0.0;\n" |
| " else\n" |
| " redChannel = 1.0;\n" |
| " o_color = vec4(redChannel, baseColor.g, baseColor.b, baseColor.a);\n" |
| "}\n"; |
| |
| return buf.str(); |
| } |
| |
| std::string PointRenderCase::genTessellationControlSource (void) const |
| |