From: Jarkko Pöyry Date: Fri, 20 Feb 2015 03:40:03 +0000 (-0800) Subject: Allow correct and projected interpolation equations in single-sampled line interpolat... X-Git-Tag: upstream/0.1.0~1908^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2306a3bf8214ab2c51d8023ca1ccd979fb8d31bb;p=platform%2Fupstream%2FVK-GL-CTS.git Allow correct and projected interpolation equations in single-sampled line interpolation test. - Use proper interpolation equation for verification. Do not approximate distance to pixel center by projecting it along major direction. (i.e. just selecting major compontent). - Detect projected interpolation in interpolation tests and issue a QualityWarning for such implementations. - Calculate line interpolation weights in screen space, not in clip space. Bug: 19410338 Change-Id: Ie27aa9cf121d1c92c86cda19a7b8c97e132fcb66 --- diff --git a/framework/referencerenderer/rrRasterizer.cpp b/framework/referencerenderer/rrRasterizer.cpp index 53d3ecc..df5b412 100644 --- a/framework/referencerenderer/rrRasterizer.cpp +++ b/framework/referencerenderer/rrRasterizer.cpp @@ -865,7 +865,7 @@ SingleSampleLineRasterizer::SingleSampleLineRasterizer (const tcu::IVec4& viewpo { } -SingleSampleLineRasterizer::~SingleSampleLineRasterizer () +SingleSampleLineRasterizer::~SingleSampleLineRasterizer (void) { } @@ -925,16 +925,18 @@ void SingleSampleLineRasterizer::rasterize (FragmentPacket* const fragmentPacket { DE_ASSERT(maxFragmentPackets > 0); - const deInt64 halfPixel = 1ll << (RASTERIZER_SUBPIXEL_BITS-1); - const deInt32 lineWidth = (m_lineWidth > 1.0f) ? (deInt32)floor(m_lineWidth + 0.5f) : 1; - const bool isXMajor = de::abs((m_v1 - m_v0).x()) >= de::abs((m_v1 - m_v0).y()); - const tcu::IVec2 minorDirection = (isXMajor ? tcu::IVec2(0, 1) : tcu::IVec2(1, 0)); - const tcu::Vector widthOffset = (isXMajor ? tcu::Vector(0, -1) : tcu::Vector(-1, 0)) * (toSubpixelCoord(lineWidth - 1) / 2); - const tcu::Vector pa = LineRasterUtil::toSubpixelVector(m_v0.xy()) + widthOffset; - const tcu::Vector pb = LineRasterUtil::toSubpixelVector(m_v1.xy()) + widthOffset; - const LineRasterUtil::SubpixelLineSegment line = LineRasterUtil::SubpixelLineSegment(pa, pb); + const deInt64 halfPixel = 1ll << (RASTERIZER_SUBPIXEL_BITS-1); + const deInt32 lineWidth = (m_lineWidth > 1.0f) ? deFloorFloatToInt32(m_lineWidth + 0.5f) : 1; + const bool isXMajor = de::abs((m_v1 - m_v0).x()) >= de::abs((m_v1 - m_v0).y()); + const tcu::IVec2 minorDirection = (isXMajor) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0)); + const int minViewportLimit = (isXMajor) ? (m_viewport.y()) : (m_viewport.x()); + const int maxViewportLimit = (isXMajor) ? (m_viewport.y() + m_viewport.w()) : (m_viewport.x() + m_viewport.z()); + const tcu::Vector widthOffset = -minorDirection.cast() * (toSubpixelCoord(lineWidth - 1) / 2); + const tcu::Vector pa = LineRasterUtil::toSubpixelVector(m_v0.xy()) + widthOffset; + const tcu::Vector pb = LineRasterUtil::toSubpixelVector(m_v1.xy()) + widthOffset; + const LineRasterUtil::SubpixelLineSegment line = LineRasterUtil::SubpixelLineSegment(pa, pb); - int packetNdx = 0; + int packetNdx = 0; while (m_curPos.y() <= m_bboxMax.y() && packetNdx < maxFragmentPackets) { @@ -943,21 +945,19 @@ void SingleSampleLineRasterizer::rasterize (FragmentPacket* const fragmentPacket // Should current fragment be drawn? == does the segment exit this diamond? if (LineRasterUtil::doesLineSegmentExitDiamond(line, diamondPosition)) { - const tcu::Vector pr = diamondPosition; - const float t = tcu::dot((pr - pa).asFloat(), (pb - pa).asFloat()) / tcu::lengthSquared(pb.asFloat() - pa.asFloat()); + const tcu::Vector pr = diamondPosition; + const float t = tcu::dot((pr - pa).asFloat(), (pb - pa).asFloat()) / tcu::lengthSquared(pb.asFloat() - pa.asFloat()); // Rasterize on only fragments that are would end up in the viewport (i.e. visible) - const int minViewportLimit = (isXMajor) ? (m_viewport.y()) : (m_viewport.x()); - const int maxViewportLimit = (isXMajor) ? (m_viewport.y() + m_viewport.w()) : (m_viewport.x() + m_viewport.z()); - const int fragmentLocation = (isXMajor) ? (m_curPos.y()) : (m_curPos.x()); - + const int fragmentLocation = (isXMajor) ? (m_curPos.y()) : (m_curPos.x()); const int rowFragBegin = de::max(0, minViewportLimit - fragmentLocation); const int rowFragEnd = de::min(maxViewportLimit - fragmentLocation, lineWidth); // Wide lines require multiple fragments. for (; rowFragBegin + m_curRowFragment < rowFragEnd; m_curRowFragment++) { - const tcu::IVec2 fragmentPos = m_curPos + minorDirection * (rowFragBegin + m_curRowFragment); + const int replicationId = rowFragBegin + m_curRowFragment; + const tcu::IVec2 fragmentPos = m_curPos + minorDirection * replicationId; // We only rasterize visible area DE_ASSERT(LineRasterUtil::inViewport(fragmentPos, m_viewport)); @@ -1088,4 +1088,70 @@ void MultiSampleLineRasterizer::rasterize (FragmentPacket* const fragmentPackets } } +LineExitDiamondGenerator::LineExitDiamondGenerator (void) +{ +} + +LineExitDiamondGenerator::~LineExitDiamondGenerator (void) +{ +} + +void LineExitDiamondGenerator::init (const tcu::Vec4& v0, const tcu::Vec4& v1) +{ + const deInt64 x0 = toSubpixelCoord(v0.x()); + const deInt64 y0 = toSubpixelCoord(v0.y()); + const deInt64 x1 = toSubpixelCoord(v1.x()); + const deInt64 y1 = toSubpixelCoord(v1.y()); + + // line endpoints might be perturbed, add some margin + const deInt64 xMin = de::min(x0, x1) - toSubpixelCoord(1); + const deInt64 xMax = de::max(x0, x1) + toSubpixelCoord(1); + const deInt64 yMin = de::min(y0, y1) - toSubpixelCoord(1); + const deInt64 yMax = de::max(y0, y1) + toSubpixelCoord(1); + + m_bboxMin.x() = floorSubpixelToPixelCoord(xMin, true); + m_bboxMin.y() = floorSubpixelToPixelCoord(yMin, true); + m_bboxMax.x() = ceilSubpixelToPixelCoord (xMax, true); + m_bboxMax.y() = ceilSubpixelToPixelCoord (yMax, true); + + m_v0 = v0; + m_v1 = v1; + + m_curPos = m_bboxMin; +} + +void LineExitDiamondGenerator::rasterize (LineExitDiamond* const lineDiamonds, const int maxDiamonds, int& numWritten) +{ + DE_ASSERT(maxDiamonds > 0); + + const deInt64 halfPixel = 1ll << (RASTERIZER_SUBPIXEL_BITS-1); + const tcu::Vector pa = LineRasterUtil::toSubpixelVector(m_v0.xy()); + const tcu::Vector pb = LineRasterUtil::toSubpixelVector(m_v1.xy()); + const LineRasterUtil::SubpixelLineSegment line = LineRasterUtil::SubpixelLineSegment(pa, pb); + + int diamondNdx = 0; + + while (m_curPos.y() <= m_bboxMax.y() && diamondNdx < maxDiamonds) + { + const tcu::Vector diamondPosition = LineRasterUtil::toSubpixelVector(m_curPos) + tcu::Vector(halfPixel,halfPixel); + + if (LineRasterUtil::doesLineSegmentExitDiamond(line, diamondPosition)) + { + LineExitDiamond& packet = lineDiamonds[diamondNdx]; + packet.position = m_curPos; + ++diamondNdx; + } + + ++m_curPos.x(); + if (m_curPos.x() > m_bboxMax.x()) + { + ++m_curPos.y(); + m_curPos.x() = m_bboxMin.x(); + } + } + + DE_ASSERT(diamondNdx <= maxDiamonds); + numWritten = diamondNdx; +} + } // rr diff --git a/framework/referencerenderer/rrRasterizer.hpp b/framework/referencerenderer/rrRasterizer.hpp index fabd7af..f1ccd98 100644 --- a/framework/referencerenderer/rrRasterizer.hpp +++ b/framework/referencerenderer/rrRasterizer.hpp @@ -161,10 +161,11 @@ private: tcu::IVec2 m_curPos; //!< Current rasterization position. }; + /*--------------------------------------------------------------------*//*! * \brief Single sample line rasterizer * - * Triangle rasterizer implements following features: + * Line rasterizer implements following features: * - Rasterization using fixed-point coordinates * - Depth interpolation * - Perspective-correct interpolation @@ -177,7 +178,7 @@ class SingleSampleLineRasterizer { public: SingleSampleLineRasterizer (const tcu::IVec4& viewport); - ~SingleSampleLineRasterizer (); + ~SingleSampleLineRasterizer (void); void init (const tcu::Vec4& v0, const tcu::Vec4& v1, float lineWidth); @@ -205,7 +206,7 @@ private: /*--------------------------------------------------------------------*//*! * \brief Multisampled line rasterizer * - * Triangle rasterizer implements following features: + * Line rasterizer implements following features: * - Rasterization using fixed-point coordinates * - Depth interpolation * - Perspective-correct interpolation @@ -237,6 +238,49 @@ private: TriangleRasterizer m_triangleRasterizer1; }; + +/*--------------------------------------------------------------------*//*! + * \brief Pixel diamond + * + * Structure representing a diamond a line exits. + *//*--------------------------------------------------------------------*/ +struct LineExitDiamond +{ + tcu::IVec2 position; +}; + +/*--------------------------------------------------------------------*//*! + * \brief Line exit diamond generator + * + * For a given line, generates list of diamonds the line exits using the + * line-exit rules of the line rasterization. Does not do scissoring. + * + * \note Not used by rr, but provided to prevent test cases requiring + * accurate diamonds from abusing SingleSampleLineRasterizer. + *//*--------------------------------------------------------------------*/ +class LineExitDiamondGenerator +{ +public: + LineExitDiamondGenerator (void); + ~LineExitDiamondGenerator (void); + + void init (const tcu::Vec4& v0, const tcu::Vec4& v1); + + // only available after init() + void rasterize (LineExitDiamond* const lineDiamonds, const int maxDiamonds, int& numWritten); + +private: + LineExitDiamondGenerator (const LineExitDiamondGenerator&); // not allowed + LineExitDiamondGenerator& operator= (const LineExitDiamondGenerator&); // not allowed + + // Per-line rasterization state. + tcu::Vec4 m_v0; + tcu::Vec4 m_v1; + tcu::IVec2 m_bboxMin; //!< Bounding box min (inclusive). + tcu::IVec2 m_bboxMax; //!< Bounding box max (inclusive). + tcu::IVec2 m_curPos; //!< Current rasterization position. +}; + } // rr #endif // _RRRASTERIZER_HPP diff --git a/modules/gles2/functional/es2fRasterizationTests.cpp b/modules/gles2/functional/es2fRasterizationTests.cpp index c78aad1..5d46b05 100644 --- a/modules/gles2/functional/es2fRasterizationTests.cpp +++ b/modules/gles2/functional/es2fRasterizationTests.cpp @@ -27,6 +27,7 @@ #include "tcuRenderTarget.hpp" #include "tcuVectorUtil.hpp" #include "tcuStringTemplate.hpp" +#include "tcuResultCollector.hpp" #include "gluShaderProgram.hpp" #include "gluRenderContext.hpp" #include "gluPixelTransfer.hpp" @@ -1583,20 +1584,20 @@ void TriangleInterpolationTest::extractTriangles (std::vector& outVertices, std::vector& outColors) const; - void extractLines (std::vector& outLines, const std::vector& vertices, const std::vector& colors) const; + void generateVertices (int iteration, std::vector& outVertices, std::vector& outColors) const; + void extractLines (std::vector& outLines, const std::vector& vertices, const std::vector& colors) const; - const glw::GLenum m_primitive; - const bool m_projective; - const int m_iterationCount; + const glw::GLenum m_primitive; + const bool m_projective; + const int m_iterationCount; - int m_iteration; - bool m_allIterationsPassed; + int m_iteration; + tcu::ResultCollector m_result; }; LineInterpolationTest::LineInterpolationTest (Context& ctx, const char* name, const char* desc, glw::GLenum primitive, int flags, float lineWidth) @@ -1605,7 +1606,6 @@ LineInterpolationTest::LineInterpolationTest (Context& ctx, const char* name, co , m_projective ((flags & INTERPOLATIONFLAGS_PROJECTED) != 0) , m_iterationCount (3) , m_iteration (0) - , m_allIterationsPassed (true) { m_lineWidth = lineWidth; } @@ -1642,6 +1642,7 @@ LineInterpolationTest::IterateResult LineInterpolationTest::iterate (void) { RasterizationArguments args; LineSceneSpec scene; + LineInterpolationMethod iterationResult; args.numSamples = m_numSamples; args.subpixelBits = m_subpixelBits; @@ -1652,18 +1653,38 @@ LineInterpolationTest::IterateResult LineInterpolationTest::iterate (void) scene.lines.swap(lines); scene.lineWidth = m_lineWidth; - if (!verifyLineGroupInterpolation(resultImage, scene, args, m_testCtx.getLog())) - m_allIterationsPassed = false; + iterationResult = verifyLineGroupInterpolation(resultImage, scene, args, m_testCtx.getLog()); + switch (iterationResult) + { + case LINEINTERPOLATION_STRICTLY_CORRECT: + // line interpolation matches the specification + m_result.addResult(QP_TEST_RESULT_PASS, "Pass"); + break; + + case LINEINTERPOLATION_PROJECTED: + // line interpolation weights are otherwise correct, but they are projected onto major axis + m_testCtx.getLog() << tcu::TestLog::Message + << "Interpolation was calculated using coordinates projected onto major axis. " + "This method does not produce the same values as the non-projecting method defined in the specification." + << tcu::TestLog::EndMessage; + m_result.addResult(QP_TEST_RESULT_QUALITY_WARNING, "Interpolation was calculated using projected coordinateds"); + break; + + case LINEINTERPOLATION_INCORRECT: + // line interpolation is incorrect + m_result.addResult(QP_TEST_RESULT_FAIL, "Found invalid pixel values"); + break; + + default: + DE_ASSERT(false); + break; + } } // result if (++m_iteration == m_iterationCount) { - if (m_allIterationsPassed) - m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); - else - m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Found invalid pixel values"); - + m_result.setTestContextResult(m_testCtx); return STOP; } else diff --git a/modules/gles3/functional/es3fRasterizationTests.cpp b/modules/gles3/functional/es3fRasterizationTests.cpp index d32f455..fa3f6b9 100644 --- a/modules/gles3/functional/es3fRasterizationTests.cpp +++ b/modules/gles3/functional/es3fRasterizationTests.cpp @@ -28,6 +28,7 @@ #include "tcuVectorUtil.hpp" #include "tcuStringTemplate.hpp" #include "tcuTextureUtil.hpp" +#include "tcuResultCollector.hpp" #include "gluShaderProgram.hpp" #include "gluRenderContext.hpp" #include "gluPixelTransfer.hpp" @@ -1905,7 +1906,7 @@ private: const PrimitiveWideness m_primitiveWideness; int m_iteration; - bool m_allIterationsPassed; + tcu::ResultCollector m_result; float m_maxLineWidth; std::vector m_lineWidths; }; @@ -1917,7 +1918,6 @@ LineInterpolationTest::LineInterpolationTest (Context& ctx, const char* name, co , m_iterationCount (3) , m_primitiveWideness (wideness) , m_iteration (0) - , m_allIterationsPassed (true) , m_maxLineWidth (1.0f) { m_flatshade = ((flags & INTERPOLATIONFLAGS_FLATSHADE) != 0); @@ -1992,6 +1992,7 @@ LineInterpolationTest::IterateResult LineInterpolationTest::iterate (void) { RasterizationArguments args; LineSceneSpec scene; + LineInterpolationMethod iterationResult; args.numSamples = m_numSamples; args.subpixelBits = m_subpixelBits; @@ -2002,8 +2003,32 @@ LineInterpolationTest::IterateResult LineInterpolationTest::iterate (void) scene.lines.swap(lines); scene.lineWidth = getLineWidth(); - if (!verifyLineGroupInterpolation(resultImage, scene, args, m_testCtx.getLog())) - m_allIterationsPassed = false; + iterationResult = verifyLineGroupInterpolation(resultImage, scene, args, m_testCtx.getLog()); + switch (iterationResult) + { + case LINEINTERPOLATION_STRICTLY_CORRECT: + // line interpolation matches the specification + m_result.addResult(QP_TEST_RESULT_PASS, "Pass"); + break; + + case LINEINTERPOLATION_PROJECTED: + // line interpolation weights are otherwise correct, but they are projected onto major axis + m_testCtx.getLog() << tcu::TestLog::Message + << "Interpolation was calculated using coordinates projected onto major axis. " + "This method does not produce the same values as the non-projecting method defined in the specification." + << tcu::TestLog::EndMessage; + m_result.addResult(QP_TEST_RESULT_QUALITY_WARNING, "Interpolation was calculated using projected coordinateds"); + break; + + case LINEINTERPOLATION_INCORRECT: + // line interpolation is incorrect + m_result.addResult(QP_TEST_RESULT_FAIL, "Found invalid pixel values"); + break; + + default: + DE_ASSERT(false); + break; + } } } else @@ -2012,11 +2037,7 @@ LineInterpolationTest::IterateResult LineInterpolationTest::iterate (void) // result if (++m_iteration == m_iterationCount) { - if (m_allIterationsPassed) - m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); - else - m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Found invalid pixel values"); - + m_result.setTestContextResult(m_testCtx); return STOP; } else diff --git a/modules/glshared/glsRasterizationTestUtil.cpp b/modules/glshared/glsRasterizationTestUtil.cpp index f479d5e..d50a8c8 100644 --- a/modules/glshared/glsRasterizationTestUtil.cpp +++ b/modules/glshared/glsRasterizationTestUtil.cpp @@ -316,6 +316,19 @@ float minimalRangeDivision (float minDividend, float maxDividend, float minDivis return de::min(de::min(minDividend / minDivisor, minDividend / maxDivisor), de::min(maxDividend / minDivisor, maxDividend / maxDivisor)); } +static bool isLineXMajor (const tcu::Vec2& lineScreenSpaceP0, const tcu::Vec2& lineScreenSpaceP1) +{ + return de::abs(lineScreenSpaceP1.x() - lineScreenSpaceP0.x()) >= de::abs(lineScreenSpaceP1.y() - lineScreenSpaceP0.y()); +} + +static bool isPackedSSLineXMajor (const tcu::Vec4& packedLine) +{ + const tcu::Vec2 lineScreenSpaceP0 = packedLine.swizzle(0, 1); + const tcu::Vec2 lineScreenSpaceP1 = packedLine.swizzle(2, 3); + + return isLineXMajor(lineScreenSpaceP0, lineScreenSpaceP1); +} + struct InterpolationRange { tcu::Vec3 max; @@ -389,42 +402,43 @@ InterpolationRange calcTriangleInterpolationWeights (const tcu::Vec4& p0, const return returnValue; } -LineInterpolationRange calcSingleSampleLineInterpolationWeights (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::Vec2& ndpoint) +LineInterpolationRange calcLineInterpolationWeights (const tcu::Vec2& pa, float wa, const tcu::Vec2& pb, float wb, const tcu::Vec2& pr) { - const int divError = 3; - - const tcu::Vec2 nd0 = p0.swizzle(0, 1) / p0.w(); - const tcu::Vec2 nd1 = p1.swizzle(0, 1) / p1.w(); - - // project p to the line along the minor direction + const int roundError = 1; + const int divError = 3; - const bool xMajor = (de::abs(nd0.x() - nd1.x()) >= de::abs(nd0.y() - nd1.y())); - const tcu::Vec2 minorDir = (xMajor) ? (tcu::Vec2(0.0f, 1.0f)) : (tcu::Vec2(1.0f, 0.0f)); - const tcu::Vec2 lineDir (nd1 - nd0); - const tcu::Vec2 d (ndpoint - nd0); + // calc weights: + // (1-t) / wa t / wb + // ------------------- , ------------------- + // (1-t) / wa + t / wb (1-t) / wa + t / wb - // calculate factors: vec2((1-t) / p0.w, t / p1.w) / ((1-t) / p0.w + t / p1.w) + // Allow 1 ULP + const float dividend = tcu::dot(pr - pa, pb - pa); + const float dividendMax = getMaxValueWithinError(dividend, 1); + const float dividendMin = getMinValueWithinError(dividend, 1); + DE_ASSERT(dividendMin <= dividendMax); - const float tFactorMax = getMaxValueWithinError(-(1.0f / (minorDir.x()*lineDir.y() - lineDir.x()*minorDir.y())), divError); - const float tFactorMin = getMinValueWithinError(-(1.0f / (minorDir.x()*lineDir.y() - lineDir.x()*minorDir.y())), divError); - DE_ASSERT(tFactorMin <= tFactorMax); + // Assuming lengthSquared will not be implemented as sqrt(x)^2, allow 1 ULP + const float divisor = tcu::lengthSquared(pb - pa); + const float divisorMax = getMaxValueWithinError(divisor, 1); + const float divisorMin = getMinValueWithinError(divisor, 1); + DE_ASSERT(divisorMin <= divisorMax); - const float tResult1 = tFactorMax * (minorDir.y()*d.x() - minorDir.x()*d.y()); - const float tResult2 = tFactorMin * (minorDir.y()*d.x() - minorDir.x()*d.y()); - const float tMax = de::max(tResult1, tResult2); - const float tMin = de::min(tResult1, tResult2); + // Allow 3 ULP precision for division + const float tMax = getMaxValueWithinError(maximalRangeDivision(dividendMin, dividendMax, divisorMin, divisorMax), divError); + const float tMin = getMinValueWithinError(minimalRangeDivision(dividendMin, dividendMax, divisorMin, divisorMax), divError); DE_ASSERT(tMin <= tMax); - const float perspectiveTMax = getMaxValueWithinError(maximalRangeDivision(tMin, tMax, p1.w(), p1.w()), divError); - const float perspectiveTMin = getMinValueWithinError(minimalRangeDivision(tMin, tMax, p1.w(), p1.w()), divError); + const float perspectiveTMax = getMaxValueWithinError(maximalRangeDivision(tMin, tMax, wb, wb), divError); + const float perspectiveTMin = getMinValueWithinError(minimalRangeDivision(tMin, tMax, wb, wb), divError); DE_ASSERT(perspectiveTMin <= perspectiveTMax); - const float perspectiveInvTMax = getMaxValueWithinError(maximalRangeDivision((1.0f - tMax), (1.0f - tMin), p0.w(), p0.w()), divError); - const float perspectiveInvTMin = getMinValueWithinError(minimalRangeDivision((1.0f - tMax), (1.0f - tMin), p0.w(), p0.w()), divError); + const float perspectiveInvTMax = getMaxValueWithinError(maximalRangeDivision((1.0f - tMax), (1.0f - tMin), wa, wa), divError); + const float perspectiveInvTMin = getMinValueWithinError(minimalRangeDivision((1.0f - tMax), (1.0f - tMin), wa, wa), divError); DE_ASSERT(perspectiveInvTMin <= perspectiveInvTMax); - const float perspectiveDivisorMax = perspectiveTMax + perspectiveInvTMax; - const float perspectiveDivisorMin = perspectiveTMin + perspectiveInvTMin; + const float perspectiveDivisorMax = getMaxValueWithinError(perspectiveTMax + perspectiveInvTMax, roundError); + const float perspectiveDivisorMin = getMinValueWithinError(perspectiveTMin + perspectiveInvTMin, roundError); DE_ASSERT(perspectiveDivisorMin <= perspectiveDivisorMax); LineInterpolationRange returnValue; @@ -439,26 +453,33 @@ LineInterpolationRange calcSingleSampleLineInterpolationWeights (const tcu::Vec4 return returnValue; } -LineInterpolationRange calcMultiSampleLineInterpolationWeights (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::Vec2& ndpoint) +LineInterpolationRange calcLineInterpolationWeightsAxisProjected (const tcu::Vec2& pa, float wa, const tcu::Vec2& pb, float wb, const tcu::Vec2& pr) { - const int divError = 3; + const int roundError = 1; + const int divError = 3; + const bool isXMajor = isLineXMajor(pa, pb); + const int majorAxisNdx = (isXMajor) ? (0) : (1); - // calc weights: vec2((1-t) / p0.w, t / p1.w) / ((1-t) / p0.w + t / p1.w) + // calc weights: + // (1-t) / wa t / wb + // ------------------- , ------------------- + // (1-t) / wa + t / wb (1-t) / wa + t / wb - // highp vertex shader - const tcu::Vec2 nd0 = p0.swizzle(0, 1) / p0.w(); - const tcu::Vec2 nd1 = p1.swizzle(0, 1) / p1.w(); + // Use axis projected (inaccurate) method, i.e. for X-major lines: + // (xd - xa) * (xb - xa) xd - xa + // t = --------------------- == ------- + // ( xb - xa ) ^ 2 xb - xa // Allow 1 ULP - const float dividend = tcu::dot(ndpoint - nd0, nd1 - nd0); + const float dividend = (pr[majorAxisNdx] - pa[majorAxisNdx]); const float dividendMax = getMaxValueWithinError(dividend, 1); - const float dividendMin = getMaxValueWithinError(dividend, 1); + const float dividendMin = getMinValueWithinError(dividend, 1); DE_ASSERT(dividendMin <= dividendMax); - // Assuming lengthSquared will not be implemented as sqrt(x)^2, allow 1 ULP - const float divisor = tcu::lengthSquared(nd1 - nd0); + // Allow 1 ULP + const float divisor = (pb[majorAxisNdx] - pa[majorAxisNdx]); const float divisorMax = getMaxValueWithinError(divisor, 1); - const float divisorMin = getMaxValueWithinError(divisor, 1); + const float divisorMin = getMinValueWithinError(divisor, 1); DE_ASSERT(divisorMin <= divisorMax); // Allow 3 ULP precision for division @@ -466,16 +487,16 @@ LineInterpolationRange calcMultiSampleLineInterpolationWeights (const tcu::Vec4& const float tMin = getMinValueWithinError(minimalRangeDivision(dividendMin, dividendMax, divisorMin, divisorMax), divError); DE_ASSERT(tMin <= tMax); - const float perspectiveTMax = getMaxValueWithinError(maximalRangeDivision(tMin, tMax, p1.w(), p1.w()), divError); - const float perspectiveTMin = getMinValueWithinError(minimalRangeDivision(tMin, tMax, p1.w(), p1.w()), divError); + const float perspectiveTMax = getMaxValueWithinError(maximalRangeDivision(tMin, tMax, wb, wb), divError); + const float perspectiveTMin = getMinValueWithinError(minimalRangeDivision(tMin, tMax, wb, wb), divError); DE_ASSERT(perspectiveTMin <= perspectiveTMax); - const float perspectiveInvTMax = getMaxValueWithinError(maximalRangeDivision((1.0f - tMax), (1.0f - tMin), p0.w(), p0.w()), divError); - const float perspectiveInvTMin = getMinValueWithinError(minimalRangeDivision((1.0f - tMax), (1.0f - tMin), p0.w(), p0.w()), divError); + const float perspectiveInvTMax = getMaxValueWithinError(maximalRangeDivision((1.0f - tMax), (1.0f - tMin), wa, wa), divError); + const float perspectiveInvTMin = getMinValueWithinError(minimalRangeDivision((1.0f - tMax), (1.0f - tMin), wa, wa), divError); DE_ASSERT(perspectiveInvTMin <= perspectiveInvTMax); - const float perspectiveDivisorMax = perspectiveTMax + perspectiveInvTMax; - const float perspectiveDivisorMin = perspectiveTMin + perspectiveInvTMin; + const float perspectiveDivisorMax = getMaxValueWithinError(perspectiveTMax + perspectiveInvTMax, roundError); + const float perspectiveDivisorMin = getMinValueWithinError(perspectiveTMin + perspectiveInvTMin, roundError); DE_ASSERT(perspectiveDivisorMin <= perspectiveDivisorMax); LineInterpolationRange returnValue; @@ -490,38 +511,55 @@ LineInterpolationRange calcMultiSampleLineInterpolationWeights (const tcu::Vec4& return returnValue; } -LineInterpolationRange calcSingleSampleLineInterpolationRange (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::IVec2& pixel, const tcu::IVec2& viewportSize, int subpixelBits) +template +LineInterpolationRange calcSingleSampleLineInterpolationRangeWithWeightEquation (const tcu::Vec2& pa, + float wa, + const tcu::Vec2& pb, + float wb, + const tcu::IVec2& pixel, + int subpixelBits, + WeightEquation weightEquation) { // allow interpolation weights anywhere in the central subpixels const float testSquareSize = (2.0f / (1UL << subpixelBits)); const float testSquarePos = (0.5f - testSquareSize / 2); + const tcu::Vec2 corners[4] = { - tcu::Vec2((pixel.x() + testSquarePos + 0.0f) / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + testSquarePos + 0.0f ) / viewportSize.y() * 2.0f - 1.0f), - tcu::Vec2((pixel.x() + testSquarePos + 0.0f) / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + testSquarePos + testSquareSize) / viewportSize.y() * 2.0f - 1.0f), - tcu::Vec2((pixel.x() + testSquarePos + testSquareSize) / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + testSquarePos + testSquareSize) / viewportSize.y() * 2.0f - 1.0f), - tcu::Vec2((pixel.x() + testSquarePos + testSquareSize) / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + testSquarePos + 0.0f ) / viewportSize.y() * 2.0f - 1.0f), + tcu::Vec2(pixel.x() + testSquarePos + 0.0f, pixel.y() + testSquarePos + 0.0f), + tcu::Vec2(pixel.x() + testSquarePos + 0.0f, pixel.y() + testSquarePos + testSquareSize), + tcu::Vec2(pixel.x() + testSquarePos + testSquareSize, pixel.y() + testSquarePos + testSquareSize), + tcu::Vec2(pixel.x() + testSquarePos + testSquareSize, pixel.y() + testSquarePos + 0.0f), }; // calculate interpolation as a line const LineInterpolationRange weights[4] = { - calcSingleSampleLineInterpolationWeights(p0, p1, corners[0]), - calcSingleSampleLineInterpolationWeights(p0, p1, corners[1]), - calcSingleSampleLineInterpolationWeights(p0, p1, corners[2]), - calcSingleSampleLineInterpolationWeights(p0, p1, corners[3]), + weightEquation(pa, wa, pb, wb, corners[0]), + weightEquation(pa, wa, pb, wb, corners[1]), + weightEquation(pa, wa, pb, wb, corners[2]), + weightEquation(pa, wa, pb, wb, corners[3]), }; const tcu::Vec2 minWeights = tcu::min(tcu::min(weights[0].min, weights[1].min), tcu::min(weights[2].min, weights[3].min)); const tcu::Vec2 maxWeights = tcu::max(tcu::max(weights[0].max, weights[1].max), tcu::max(weights[2].max, weights[3].max)); - // convert to three-component form. For all triangles, the vertex 0 is always emitted by the line starting point, and vertex 2 by the ending point LineInterpolationRange result; result.min = minWeights; result.max = maxWeights; return result; } +LineInterpolationRange calcSingleSampleLineInterpolationRange (const tcu::Vec2& pa, float wa, const tcu::Vec2& pb, float wb, const tcu::IVec2& pixel, int subpixelBits) +{ + return calcSingleSampleLineInterpolationRangeWithWeightEquation(pa, wa, pb, wb, pixel, subpixelBits, calcLineInterpolationWeights); +} + +LineInterpolationRange calcSingleSampleLineInterpolationRangeAxisProjected (const tcu::Vec2& pa, float wa, const tcu::Vec2& pb, float wb, const tcu::IVec2& pixel, int subpixelBits) +{ + return calcSingleSampleLineInterpolationRangeWithWeightEquation(pa, wa, pb, wb, pixel, subpixelBits, calcLineInterpolationWeightsAxisProjected); +} + struct TriangleInterpolator { const TriangleSceneSpec& scene; @@ -584,19 +622,26 @@ struct MultisampleLineInterpolator // allow interpolation weights anywhere in the pixel const tcu::Vec2 corners[4] = { - tcu::Vec2((pixel.x() + 0.0f) / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + 0.0f) / viewportSize.y() * 2.0f - 1.0f), - tcu::Vec2((pixel.x() + 0.0f) / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + 1.0f) / viewportSize.y() * 2.0f - 1.0f), - tcu::Vec2((pixel.x() + 1.0f) / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + 1.0f) / viewportSize.y() * 2.0f - 1.0f), - tcu::Vec2((pixel.x() + 1.0f) / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + 0.0f) / viewportSize.y() * 2.0f - 1.0f), + tcu::Vec2(pixel.x() + 0.0f, pixel.y() + 0.0f), + tcu::Vec2(pixel.x() + 0.0f, pixel.y() + 1.0f), + tcu::Vec2(pixel.x() + 1.0f, pixel.y() + 1.0f), + tcu::Vec2(pixel.x() + 1.0f, pixel.y() + 0.0f), }; + const float wa = scene.lines[lineNdx].positions[0].w(); + const float wb = scene.lines[lineNdx].positions[1].w(); + const tcu::Vec2 pa = tcu::Vec2((scene.lines[lineNdx].positions[0].x() / wa + 1.0f) * 0.5f * viewportSize.x(), + (scene.lines[lineNdx].positions[0].y() / wa + 1.0f) * 0.5f * viewportSize.y()); + const tcu::Vec2 pb = tcu::Vec2((scene.lines[lineNdx].positions[1].x() / wb + 1.0f) * 0.5f * viewportSize.x(), + (scene.lines[lineNdx].positions[1].y() / wb + 1.0f) * 0.5f * viewportSize.y()); + // calculate interpolation as a line const LineInterpolationRange weights[4] = { - calcMultiSampleLineInterpolationWeights(scene.lines[lineNdx].positions[0], scene.lines[lineNdx].positions[1], corners[0]), - calcMultiSampleLineInterpolationWeights(scene.lines[lineNdx].positions[0], scene.lines[lineNdx].positions[1], corners[1]), - calcMultiSampleLineInterpolationWeights(scene.lines[lineNdx].positions[0], scene.lines[lineNdx].positions[1], corners[2]), - calcMultiSampleLineInterpolationWeights(scene.lines[lineNdx].positions[0], scene.lines[lineNdx].positions[1], corners[3]), + calcLineInterpolationWeights(pa, wa, pb, wb, corners[0]), + calcLineInterpolationWeights(pa, wa, pb, wb, corners[1]), + calcLineInterpolationWeights(pa, wa, pb, wb, corners[2]), + calcLineInterpolationWeights(pa, wa, pb, wb, corners[3]), }; const tcu::Vec2 minWeights = tcu::min(tcu::min(weights[0].min, weights[1].min), tcu::min(weights[2].min, weights[3].min)); @@ -978,42 +1023,62 @@ bool verifyMultisamplePointGroupRasterization (const tcu::Surface& surface, cons return verifyTriangleGroupRasterization(surface, triangleScene, args, log); } +void genScreenSpaceLines (std::vector& screenspaceLines, const std::vector& lines, const tcu::IVec2& viewportSize) +{ + DE_ASSERT(screenspaceLines.size() == lines.size()); + + for (int lineNdx = 0; lineNdx < (int)lines.size(); ++lineNdx) + { + const tcu::Vec2 lineNormalizedDeviceSpace[2] = + { + tcu::Vec2(lines[lineNdx].positions[0].x() / lines[lineNdx].positions[0].w(), lines[lineNdx].positions[0].y() / lines[lineNdx].positions[0].w()), + tcu::Vec2(lines[lineNdx].positions[1].x() / lines[lineNdx].positions[1].w(), lines[lineNdx].positions[1].y() / lines[lineNdx].positions[1].w()), + }; + const tcu::Vec4 lineScreenSpace[2] = + { + tcu::Vec4((lineNormalizedDeviceSpace[0].x() + 1.0f) * 0.5f * (float)viewportSize.x(), (lineNormalizedDeviceSpace[0].y() + 1.0f) * 0.5f * (float)viewportSize.y(), 0.0f, 1.0f), + tcu::Vec4((lineNormalizedDeviceSpace[1].x() + 1.0f) * 0.5f * (float)viewportSize.x(), (lineNormalizedDeviceSpace[1].y() + 1.0f) * 0.5f * (float)viewportSize.y(), 0.0f, 1.0f), + }; + + screenspaceLines[lineNdx] = tcu::Vec4(lineScreenSpace[0].x(), lineScreenSpace[0].y(), lineScreenSpace[1].x(), lineScreenSpace[1].y()); + } +} + bool verifySinglesampleLineGroupRasterization (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log) { DE_ASSERT(deFloatFrac(scene.lineWidth) != 0.5f); // rounding direction is not defined, disallow undefined cases DE_ASSERT(scene.lines.size() < 255); // indices are stored as unsigned 8-bit ints - bool allOK = true; - bool overdrawInReference = false; - int referenceFragments = 0; - int resultFragments = 0; - int lineWidth = deFloorFloatToInt32(scene.lineWidth + 0.5f); - bool imageShown = false; - std::vector lineIsXMajor (scene.lines.size()); + bool allOK = true; + bool overdrawInReference = false; + int referenceFragments = 0; + int resultFragments = 0; + int lineWidth = deFloorFloatToInt32(scene.lineWidth + 0.5f); + bool imageShown = false; + std::vector lineIsXMajor (scene.lines.size()); + std::vector screenspaceLines(scene.lines.size()); // Reference renderer produces correct fragments using the diamond-rule. Make 2D int array, each cell contains the highest index (first index = 1) of the overlapping lines or 0 if no line intersects the pixel tcu::TextureLevel referenceLineMap(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8), surface.getWidth(), surface.getHeight()); tcu::clear(referenceLineMap.getAccess(), tcu::IVec4(0, 0, 0, 0)); + genScreenSpaceLines(screenspaceLines, scene.lines, tcu::IVec2(surface.getWidth(), surface.getHeight())); + for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx) { rr::SingleSampleLineRasterizer rasterizer(tcu::IVec4(0, 0, surface.getWidth(), surface.getHeight())); - - const tcu::Vec2 lineNormalizedDeviceSpace[2] = - { - tcu::Vec2(scene.lines[lineNdx].positions[0].x() / scene.lines[lineNdx].positions[0].w(), scene.lines[lineNdx].positions[0].y() / scene.lines[lineNdx].positions[0].w()), - tcu::Vec2(scene.lines[lineNdx].positions[1].x() / scene.lines[lineNdx].positions[1].w(), scene.lines[lineNdx].positions[1].y() / scene.lines[lineNdx].positions[1].w()), - }; - const tcu::Vec4 lineScreenSpace[2] = - { - tcu::Vec4((lineNormalizedDeviceSpace[0].x() + 1.0f) * 0.5f * (float)surface.getWidth(), (lineNormalizedDeviceSpace[0].y() + 1.0f) * 0.5f * (float)surface.getHeight(), 0.0f, 1.0f), - tcu::Vec4((lineNormalizedDeviceSpace[1].x() + 1.0f) * 0.5f * (float)surface.getWidth(), (lineNormalizedDeviceSpace[1].y() + 1.0f) * 0.5f * (float)surface.getHeight(), 0.0f, 1.0f), - }; - - rasterizer.init(lineScreenSpace[0], lineScreenSpace[1], scene.lineWidth); + rasterizer.init(tcu::Vec4(screenspaceLines[lineNdx][0], + screenspaceLines[lineNdx][1], + 0.0f, + 1.0f), + tcu::Vec4(screenspaceLines[lineNdx][2], + screenspaceLines[lineNdx][3], + 0.0f, + 1.0f), + scene.lineWidth); // calculate majority of later use - lineIsXMajor[lineNdx] = de::abs(lineScreenSpace[1].x() - lineScreenSpace[0].x()) >= de::abs(lineScreenSpace[1].y() - lineScreenSpace[0].y()); + lineIsXMajor[lineNdx] = isPackedSSLineXMajor(screenspaceLines[lineNdx]); for (;;) { @@ -1352,7 +1417,7 @@ bool verifySinglesampleLineGroupRasterization (const tcu::Surface& surface, cons return allOK; } -struct SingleSampleLineCoverageCandidate +struct SingleSampleNarrowLineCandidate { int lineNdx; tcu::IVec3 colorMin; @@ -1363,76 +1428,92 @@ struct SingleSampleLineCoverageCandidate tcu::Vec3 valueRangeMax; }; -bool verifySinglesampleLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log) +void setMaskMapCoverageBitForLine (int bitNdx, const tcu::Vec2& screenSpaceP0, const tcu::Vec2& screenSpaceP1, float lineWidth, tcu::PixelBufferAccess maskMap) { - DE_ASSERT(deFloatFrac(scene.lineWidth) != 0.5f); // rounding direction is not defined, disallow undefined cases - DE_ASSERT(scene.lines.size() < 8); // coverage indices are stored as bitmask in a unsigned 8-bit ints + enum + { + MAX_PACKETS = 32, + }; - const tcu::RGBA invalidPixelColor = tcu::RGBA(255, 0, 0, 255); - const tcu::IVec2 viewportSize = tcu::IVec2(surface.getWidth(), surface.getHeight()); - const int errorFloodThreshold = 4; - int errorCount = 0; - tcu::Surface errorMask (surface.getWidth(), surface.getHeight()); - int invalidPixels = 0; + rr::SingleSampleLineRasterizer rasterizer (tcu::IVec4(0, 0, maskMap.getWidth(), maskMap.getHeight())); + int numRasterized = MAX_PACKETS; + rr::FragmentPacket packets[MAX_PACKETS]; - // log format + rasterizer.init(tcu::Vec4(screenSpaceP0.x(), screenSpaceP0.y(), 0.0f, 1.0f), + tcu::Vec4(screenSpaceP1.x(), screenSpaceP1.y(), 0.0f, 1.0f), + lineWidth); - log << tcu::TestLog::Message << "Verifying rasterization result. Native format is RGB" << args.redBits << args.greenBits << args.blueBits << tcu::TestLog::EndMessage; - if (args.redBits > 8 || args.greenBits > 8 || args.blueBits > 8) - log << tcu::TestLog::Message << "Warning! More than 8 bits in a color channel, this may produce false negatives." << tcu::TestLog::EndMessage; + while (numRasterized == MAX_PACKETS) + { + rasterizer.rasterize(packets, DE_NULL, MAX_PACKETS, numRasterized); - // Reference renderer produces correct fragments using the diamond-exit-rule. Make 2D int array, store line coverage as a 8-bit bitfield - // The map is used to find lines with potential coverage to a given pixel - tcu::TextureLevel referenceLineMap(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8), surface.getWidth(), surface.getHeight()); - tcu::clear(referenceLineMap.getAccess(), tcu::IVec4(0, 0, 0, 0)); + for (int packetNdx = 0; packetNdx < numRasterized; ++packetNdx) + { + for (int fragNdx = 0; fragNdx < 4; ++fragNdx) + { + if ((deUint32)packets[packetNdx].coverage & (1 << fragNdx)) + { + const tcu::IVec2 fragPos = packets[packetNdx].position + tcu::IVec2(fragNdx%2, fragNdx/2); - tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f)); + DE_ASSERT(deInBounds32(fragPos.x(), 0, maskMap.getWidth())); + DE_ASSERT(deInBounds32(fragPos.y(), 0, maskMap.getHeight())); - for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx) + const int previousMask = maskMap.getPixelInt(fragPos.x(), fragPos.y()).x(); + const int newMask = (previousMask) | (1UL << bitNdx); + + maskMap.setPixel(tcu::IVec4(newMask, 0, 0, 0), fragPos.x(), fragPos.y()); + } + } + } + } +} + +void setMaskMapCoverageBitForLines (const std::vector& screenspaceLines, float lineWidth, tcu::PixelBufferAccess maskMap) +{ + for (int lineNdx = 0; lineNdx < (int)screenspaceLines.size(); ++lineNdx) { - rr::SingleSampleLineRasterizer rasterizer(tcu::IVec4(0, 0, surface.getWidth(), surface.getHeight())); + const tcu::Vec2 pa = screenspaceLines[lineNdx].swizzle(0, 1); + const tcu::Vec2 pb = screenspaceLines[lineNdx].swizzle(2, 3); - const tcu::Vec2 lineNormalizedDeviceSpace[2] = - { - tcu::Vec2(scene.lines[lineNdx].positions[0].x() / scene.lines[lineNdx].positions[0].w(), scene.lines[lineNdx].positions[0].y() / scene.lines[lineNdx].positions[0].w()), - tcu::Vec2(scene.lines[lineNdx].positions[1].x() / scene.lines[lineNdx].positions[1].w(), scene.lines[lineNdx].positions[1].y() / scene.lines[lineNdx].positions[1].w()), - }; - const tcu::Vec4 lineScreenSpace[2] = - { - tcu::Vec4((lineNormalizedDeviceSpace[0].x() + 1.0f) * 0.5f * (float)surface.getWidth(), (lineNormalizedDeviceSpace[0].y() + 1.0f) * 0.5f * (float)surface.getHeight(), 0.0f, 1.0f), - tcu::Vec4((lineNormalizedDeviceSpace[1].x() + 1.0f) * 0.5f * (float)surface.getWidth(), (lineNormalizedDeviceSpace[1].y() + 1.0f) * 0.5f * (float)surface.getHeight(), 0.0f, 1.0f), - }; + setMaskMapCoverageBitForLine(lineNdx, pa, pb, lineWidth, maskMap); + } +} - rasterizer.init(lineScreenSpace[0], lineScreenSpace[1], scene.lineWidth); +// verify line interpolation assuming line pixels are interpolated independently depending only on screen space location +bool verifyLineGroupPixelIndependentInterpolation (const tcu::Surface& surface, + const LineSceneSpec& scene, + const RasterizationArguments& args, + tcu::TestLog& log, + LineInterpolationMethod interpolationMethod) +{ + DE_ASSERT(scene.lines.size() < 8); // coverage indices are stored as bitmask in a unsigned 8-bit ints + DE_ASSERT(interpolationMethod == LINEINTERPOLATION_STRICTLY_CORRECT || interpolationMethod == LINEINTERPOLATION_PROJECTED); - // Calculate correct line coverage - for (;;) - { - const int maxPackets = 32; - int numRasterized = 0; - rr::FragmentPacket packets[maxPackets]; + const tcu::RGBA invalidPixelColor = tcu::RGBA(255, 0, 0, 255); + const tcu::IVec2 viewportSize = tcu::IVec2(surface.getWidth(), surface.getHeight()); + const int errorFloodThreshold = 4; + int errorCount = 0; + tcu::Surface errorMask (surface.getWidth(), surface.getHeight()); + int invalidPixels = 0; + std::vector screenspaceLines (scene.lines.size()); //!< packed (x0, y0, x1, y1) - rasterizer.rasterize(packets, DE_NULL, maxPackets, numRasterized); + // Reference renderer produces correct fragments using the diamond-exit-rule. Make 2D int array, store line coverage as a 8-bit bitfield + // The map is used to find lines with potential coverage to a given pixel + tcu::TextureLevel referenceLineMap (tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8), surface.getWidth(), surface.getHeight()); - for (int packetNdx = 0; packetNdx < numRasterized; ++packetNdx) - { - for (int fragNdx = 0; fragNdx < 4; ++fragNdx) - { - if ((deUint32)packets[packetNdx].coverage & (1 << fragNdx)) - { - const tcu::IVec2 fragPos = packets[packetNdx].position + tcu::IVec2(fragNdx%2, fragNdx/2); - const int previousMask = referenceLineMap.getAccess().getPixelInt(fragPos.x(), fragPos.y()).x(); - const int newMask = (previousMask) | (1UL << lineNdx); + tcu::clear(referenceLineMap.getAccess(), tcu::IVec4(0, 0, 0, 0)); + tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f)); - referenceLineMap.getAccess().setPixel(tcu::IVec4(newMask, 0, 0, 0), fragPos.x(), fragPos.y()); - } - } - } + // log format - if (numRasterized != maxPackets) - break; - } - } + log << tcu::TestLog::Message << "Verifying rasterization result. Native format is RGB" << args.redBits << args.greenBits << args.blueBits << tcu::TestLog::EndMessage; + if (args.redBits > 8 || args.greenBits > 8 || args.blueBits > 8) + log << tcu::TestLog::Message << "Warning! More than 8 bits in a color channel, this may produce false negatives." << tcu::TestLog::EndMessage; + + // prepare lookup map + + genScreenSpaceLines(screenspaceLines, scene.lines, viewportSize); + setMaskMapCoverageBitForLines(screenspaceLines, scene.lineWidth, referenceLineMap.getAccess()); // Find all possible lines with coverage, check pixel color matches one of them @@ -1446,7 +1527,7 @@ bool verifySinglesampleLineGroupInterpolation (const tcu::Surface& surface, cons bool matchFound = false; const tcu::IVec3 formatLimit ((1 << args.redBits) - 1, (1 << args.greenBits) - 1, (1 << args.blueBits) - 1); - std::vector candidates; + std::vector candidates; // Find lines with possible coverage @@ -1469,27 +1550,30 @@ bool verifySinglesampleLineGroupInterpolation (const tcu::Surface& surface, cons { if (((lineCoverageSet >> lineNdx) & 0x01) != 0) { - const LineInterpolationRange range = calcSingleSampleLineInterpolationRange(scene.lines[lineNdx].positions[0], - scene.lines[lineNdx].positions[1], - tcu::IVec2(x, y), - viewportSize, - args.subpixelBits); - - const tcu::Vec4 valueMin = de::clamp(range.min.x(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[0] + de::clamp(range.min.y(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[1]; - const tcu::Vec4 valueMax = de::clamp(range.max.x(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[0] + de::clamp(range.max.y(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[1]; - - const tcu::Vec3 colorMinF (de::clamp(valueMin.x() * formatLimit.x(), 0.0f, (float)formatLimit.x()), - de::clamp(valueMin.y() * formatLimit.y(), 0.0f, (float)formatLimit.y()), - de::clamp(valueMin.z() * formatLimit.z(), 0.0f, (float)formatLimit.z())); - const tcu::Vec3 colorMaxF (de::clamp(valueMax.x() * formatLimit.x(), 0.0f, (float)formatLimit.x()), - de::clamp(valueMax.y() * formatLimit.y(), 0.0f, (float)formatLimit.y()), - de::clamp(valueMax.z() * formatLimit.z(), 0.0f, (float)formatLimit.z())); - const tcu::IVec3 colorMin ((int)deFloatFloor(colorMinF.x()), - (int)deFloatFloor(colorMinF.y()), - (int)deFloatFloor(colorMinF.z())); - const tcu::IVec3 colorMax ((int)deFloatCeil (colorMaxF.x()), - (int)deFloatCeil (colorMaxF.y()), - (int)deFloatCeil (colorMaxF.z())); + const float wa = scene.lines[lineNdx].positions[0].w(); + const float wb = scene.lines[lineNdx].positions[1].w(); + const tcu::Vec2 pa = screenspaceLines[lineNdx].swizzle(0, 1); + const tcu::Vec2 pb = screenspaceLines[lineNdx].swizzle(2, 3); + + const LineInterpolationRange range = (interpolationMethod == LINEINTERPOLATION_STRICTLY_CORRECT) + ? (calcSingleSampleLineInterpolationRange(pa, wa, pb, wb, tcu::IVec2(x, y), args.subpixelBits)) + : (calcSingleSampleLineInterpolationRangeAxisProjected(pa, wa, pb, wb, tcu::IVec2(x, y), args.subpixelBits)); + + const tcu::Vec4 valueMin = de::clamp(range.min.x(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[0] + de::clamp(range.min.y(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[1]; + const tcu::Vec4 valueMax = de::clamp(range.max.x(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[0] + de::clamp(range.max.y(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[1]; + + const tcu::Vec3 colorMinF (de::clamp(valueMin.x() * formatLimit.x(), 0.0f, (float)formatLimit.x()), + de::clamp(valueMin.y() * formatLimit.y(), 0.0f, (float)formatLimit.y()), + de::clamp(valueMin.z() * formatLimit.z(), 0.0f, (float)formatLimit.z())); + const tcu::Vec3 colorMaxF (de::clamp(valueMax.x() * formatLimit.x(), 0.0f, (float)formatLimit.x()), + de::clamp(valueMax.y() * formatLimit.y(), 0.0f, (float)formatLimit.y()), + de::clamp(valueMax.z() * formatLimit.z(), 0.0f, (float)formatLimit.z())); + const tcu::IVec3 colorMin ((int)deFloatFloor(colorMinF.x()), + (int)deFloatFloor(colorMinF.y()), + (int)deFloatFloor(colorMinF.z())); + const tcu::IVec3 colorMax ((int)deFloatCeil (colorMaxF.x()), + (int)deFloatCeil (colorMaxF.y()), + (int)deFloatCeil (colorMaxF.z())); // Verify validity if (pixelNativeColor.x() < colorMin.x() || @@ -1502,7 +1586,7 @@ bool verifySinglesampleLineGroupInterpolation (const tcu::Surface& surface, cons if (errorCount < errorFloodThreshold) { // Store candidate information for logging - SingleSampleLineCoverageCandidate candidate; + SingleSampleNarrowLineCandidate candidate; candidate.lineNdx = lineNdx; candidate.colorMin = colorMin; @@ -1543,7 +1627,7 @@ bool verifySinglesampleLineGroupInterpolation (const tcu::Surface& surface, cons for (int candidateNdx = 0; candidateNdx < (int)candidates.size(); ++candidateNdx) { - const SingleSampleLineCoverageCandidate& candidate = candidates[candidateNdx]; + const SingleSampleNarrowLineCandidate& candidate = candidates[candidateNdx]; log << tcu::TestLog::Message << "\tCandidate (line " << candidate.lineNdx << "):\n" << "\t\tReference native color min: " << tcu::clamp(candidate.colorMin, tcu::IVec3(0,0,0), formatLimit) << "\n" @@ -1583,6 +1667,381 @@ bool verifySinglesampleLineGroupInterpolation (const tcu::Surface& surface, cons } } +bool verifySinglesampleNarrowLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log) +{ + DE_ASSERT(scene.lineWidth == 1.0f); + return verifyLineGroupPixelIndependentInterpolation(surface, scene, args, log, LINEINTERPOLATION_STRICTLY_CORRECT); +} + +bool verifyLineGroupInterpolationWithProjectedWeights (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log) +{ + return verifyLineGroupPixelIndependentInterpolation(surface, scene, args, log, LINEINTERPOLATION_PROJECTED); +} + +struct SingleSampleWideLineCandidate +{ + struct InterpolationPointCandidate + { + tcu::IVec2 interpolationPoint; + tcu::IVec3 colorMin; + tcu::IVec3 colorMax; + tcu::Vec3 colorMinF; + tcu::Vec3 colorMaxF; + tcu::Vec3 valueRangeMin; + tcu::Vec3 valueRangeMax; + }; + + int lineNdx; + int numCandidates; + InterpolationPointCandidate interpolationCandidates[3]; +}; + +// return point on line at a given position on a given axis +tcu::Vec2 getLineCoordAtAxisCoord (const tcu::Vec2& pa, const tcu::Vec2& pb, bool isXAxis, float axisCoord) +{ + const int fixedCoordNdx = (isXAxis) ? (0) : (1); + const int varyingCoordNdx = (isXAxis) ? (1) : (0); + + const float fixedDifference = pb[fixedCoordNdx] - pa[fixedCoordNdx]; + const float varyingDifference = pb[varyingCoordNdx] - pa[varyingCoordNdx]; + + DE_ASSERT(fixedDifference != 0.0f); + + const float resultFixedCoord = axisCoord; + const float resultVaryingCoord = pa[varyingCoordNdx] + (axisCoord - pa[fixedCoordNdx]) * (varyingDifference / fixedDifference); + + return (isXAxis) ? (tcu::Vec2(resultFixedCoord, resultVaryingCoord)) + : (tcu::Vec2(resultVaryingCoord, resultFixedCoord)); +} + +bool isBlack (const tcu::RGBA& c) +{ + return c.getRed() == 0 && c.getGreen() == 0 && c.getBlue() == 0; +} + +bool verifySinglesampleWideLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log) +{ + DE_ASSERT(deFloatFrac(scene.lineWidth) != 0.5f); // rounding direction is not defined, disallow undefined cases + DE_ASSERT(scene.lines.size() < 8); // coverage indices are stored as bitmask in a unsigned 8-bit ints + + enum + { + FLAG_ROOT_NOT_SET = (1u << 16) + }; + + const tcu::RGBA invalidPixelColor = tcu::RGBA(255, 0, 0, 255); + const tcu::IVec2 viewportSize = tcu::IVec2(surface.getWidth(), surface.getHeight()); + const int errorFloodThreshold = 4; + int errorCount = 0; + tcu::Surface errorMask (surface.getWidth(), surface.getHeight()); + int invalidPixels = 0; + std::vector effectiveLines (scene.lines.size()); //!< packed (x0, y0, x1, y1) + std::vector lineIsXMajor (scene.lines.size()); + + // for each line, for every distinct major direction fragment, store root pixel location (along + // minor direction); + std::vector > rootPixelLocation (scene.lines.size()); //!< packed [16b - flags] [16b - coordinate] + + // log format + + log << tcu::TestLog::Message << "Verifying rasterization result. Native format is RGB" << args.redBits << args.greenBits << args.blueBits << tcu::TestLog::EndMessage; + if (args.redBits > 8 || args.greenBits > 8 || args.blueBits > 8) + log << tcu::TestLog::Message << "Warning! More than 8 bits in a color channel, this may produce false negatives." << tcu::TestLog::EndMessage; + + // Reference renderer produces correct fragments using the diamond-exit-rule. Make 2D int array, store line coverage as a 8-bit bitfield + // The map is used to find lines with potential coverage to a given pixel + tcu::TextureLevel referenceLineMap(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8), surface.getWidth(), surface.getHeight()); + tcu::clear(referenceLineMap.getAccess(), tcu::IVec4(0, 0, 0, 0)); + + tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f)); + + // calculate mask and effective line coordinates + { + std::vector screenspaceLines(scene.lines.size()); + + genScreenSpaceLines(screenspaceLines, scene.lines, viewportSize); + setMaskMapCoverageBitForLines(screenspaceLines, scene.lineWidth, referenceLineMap.getAccess()); + + for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx) + { + const tcu::Vec2 lineScreenSpaceP0 = screenspaceLines[lineNdx].swizzle(0, 1); + const tcu::Vec2 lineScreenSpaceP1 = screenspaceLines[lineNdx].swizzle(2, 3); + const bool isXMajor = isPackedSSLineXMajor(screenspaceLines[lineNdx]); + + lineIsXMajor[lineNdx] = isXMajor; + + // wide line interpolations are calculated for a line moved in minor direction + { + const float offsetLength = (scene.lineWidth - 1.0f) / 2.0f; + const tcu::Vec2 offsetDirection = (isXMajor) ? (tcu::Vec2(0.0f, -1.0f)) : (tcu::Vec2(-1.0f, 0.0f)); + const tcu::Vec2 offset = offsetDirection * offsetLength; + + effectiveLines[lineNdx] = tcu::Vec4(lineScreenSpaceP0.x() + offset.x(), + lineScreenSpaceP0.y() + offset.y(), + lineScreenSpaceP1.x() + offset.x(), + lineScreenSpaceP1.y() + offset.y()); + } + } + } + + for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx) + { + // Calculate root pixel lookup table for this line. Since the implementation's fragment + // major coordinate range might not be a subset of the correct line range (they are allowed + // to vary by one pixel), we must extend the domain to cover whole viewport along major + // dimension. + // + // Expanding line strip to (effectively) infinite line might result in exit-diamnod set + // that is not a superset of the exit-diamond set of the line strip. In practice, this + // won't be an issue, since the allow-one-pixel-variation rule should tolerate this even + // if the original and extended line would resolve differently a diamond the line just + // touches (precision lost in expansion changes enter/exit status). + + { + const bool isXMajor = lineIsXMajor[lineNdx]; + const int majorSize = (isXMajor) ? (surface.getWidth()) : (surface.getHeight()); + rr::LineExitDiamondGenerator diamondGenerator; + rr::LineExitDiamond diamonds[32]; + int numRasterized = DE_LENGTH_OF_ARRAY(diamonds); + + // Expand to effectively infinite line (endpoints are just one pixel over viewport boundaries) + const tcu::Vec2 expandedP0 = getLineCoordAtAxisCoord(effectiveLines[lineNdx].swizzle(0, 1), effectiveLines[lineNdx].swizzle(2, 3), isXMajor, -1.0f); + const tcu::Vec2 expandedP1 = getLineCoordAtAxisCoord(effectiveLines[lineNdx].swizzle(0, 1), effectiveLines[lineNdx].swizzle(2, 3), isXMajor, (float)majorSize + 1.0f); + + diamondGenerator.init(tcu::Vec4(expandedP0.x(), expandedP0.y(), 0.0f, 1.0f), + tcu::Vec4(expandedP1.x(), expandedP1.y(), 0.0f, 1.0f)); + + rootPixelLocation[lineNdx].resize(majorSize, FLAG_ROOT_NOT_SET); + + while (numRasterized == DE_LENGTH_OF_ARRAY(diamonds)) + { + diamondGenerator.rasterize(diamonds, DE_LENGTH_OF_ARRAY(diamonds), numRasterized); + + for (int packetNdx = 0; packetNdx < numRasterized; ++packetNdx) + { + const tcu::IVec2 fragPos = diamonds[packetNdx].position; + const int majorPos = (isXMajor) ? (fragPos.x()) : (fragPos.y()); + const int rootPos = (isXMajor) ? (fragPos.y()) : (fragPos.x()); + const deUint32 packed = (deUint32)((deUint16)((deInt16)rootPos)); + + // infinite line will generate some diamonds outside the viewport + if (deInBounds32(majorPos, 0, majorSize)) + { + DE_ASSERT((rootPixelLocation[lineNdx][majorPos] & FLAG_ROOT_NOT_SET) != 0u); + rootPixelLocation[lineNdx][majorPos] = packed; + } + } + } + + // Filled whole lookup table + for (int majorPos = 0; majorPos < majorSize; ++majorPos) + DE_ASSERT((rootPixelLocation[lineNdx][majorPos] & FLAG_ROOT_NOT_SET) == 0u); + } + } + + // Find all possible lines with coverage, check pixel color matches one of them + + for (int y = 1; y < surface.getHeight() - 1; ++y) + for (int x = 1; x < surface.getWidth() - 1; ++x) + { + const tcu::RGBA color = surface.getPixel(x, y); + const tcu::IVec3 pixelNativeColor = convertRGB8ToNativeFormat(color, args); // Convert pixel color from rgba8 to the real pixel format. Usually rgba8 or 565 + int lineCoverageSet = 0; // !< lines that may cover this fragment + int lineSurroundingCoverage = 0xFFFF; // !< lines that will cover this fragment + bool matchFound = false; + const tcu::IVec3 formatLimit ((1 << args.redBits) - 1, (1 << args.greenBits) - 1, (1 << args.blueBits) - 1); + + std::vector candidates; + + // Find lines with possible coverage + + for (int dy = -1; dy < 2; ++dy) + for (int dx = -1; dx < 2; ++dx) + { + const int coverage = referenceLineMap.getAccess().getPixelInt(x+dx, y+dy).x(); + + lineCoverageSet |= coverage; + lineSurroundingCoverage &= coverage; + } + + // background color is possible? + if (lineSurroundingCoverage == 0 && compareColors(color, tcu::RGBA::black, args.redBits, args.greenBits, args.blueBits)) + continue; + + // Check those lines + + for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx) + { + if (((lineCoverageSet >> lineNdx) & 0x01) != 0) + { + const float wa = scene.lines[lineNdx].positions[0].w(); + const float wb = scene.lines[lineNdx].positions[1].w(); + const tcu::Vec2 pa = effectiveLines[lineNdx].swizzle(0, 1); + const tcu::Vec2 pb = effectiveLines[lineNdx].swizzle(2, 3); + + // \note Wide line fragments are generated by replicating the root fragment for each + // fragment column (row for y-major). Calculate interpolation at the root + // fragment. + const bool isXMajor = lineIsXMajor[lineNdx]; + const int majorPosition = (isXMajor) ? (x) : (y); + const deUint32 minorInfoPacked = rootPixelLocation[lineNdx][majorPosition]; + const int minorPosition = (int)((deInt16)((deUint16)(minorInfoPacked & 0xFFFFu))); + const tcu::IVec2 idealRootPos = (isXMajor) ? (tcu::IVec2(majorPosition, minorPosition)) : (tcu::IVec2(minorPosition, majorPosition)); + const tcu::IVec2 minorDirection = (isXMajor) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0)); + + SingleSampleWideLineCandidate candidate; + + candidate.lineNdx = lineNdx; + candidate.numCandidates = 0; + DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(candidate.interpolationCandidates) == 3); + + // Interpolation happens at the root fragment, which is then replicated in minor + // direction. Search for implementation's root position near accurate root. + for (int minorOffset = -1; minorOffset < 2; ++minorOffset) + { + const tcu::IVec2 rootPosition = idealRootPos + minorOffset * minorDirection; + + // A fragment can be root fragment only if it exists + // \note root fragment can "exist" outside viewport + // \note no pixel format theshold since in this case allowing only black is more conservative + if (deInBounds32(rootPosition.x(), 0, surface.getWidth()) && + deInBounds32(rootPosition.y(), 0, surface.getHeight()) && + isBlack(surface.getPixel(rootPosition.x(), rootPosition.y()))) + { + continue; + } + + const LineInterpolationRange range = calcSingleSampleLineInterpolationRange(pa, wa, pb, wb, rootPosition, args.subpixelBits); + + const tcu::Vec4 valueMin = de::clamp(range.min.x(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[0] + de::clamp(range.min.y(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[1]; + const tcu::Vec4 valueMax = de::clamp(range.max.x(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[0] + de::clamp(range.max.y(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[1]; + + const tcu::Vec3 colorMinF (de::clamp(valueMin.x() * formatLimit.x(), 0.0f, (float)formatLimit.x()), + de::clamp(valueMin.y() * formatLimit.y(), 0.0f, (float)formatLimit.y()), + de::clamp(valueMin.z() * formatLimit.z(), 0.0f, (float)formatLimit.z())); + const tcu::Vec3 colorMaxF (de::clamp(valueMax.x() * formatLimit.x(), 0.0f, (float)formatLimit.x()), + de::clamp(valueMax.y() * formatLimit.y(), 0.0f, (float)formatLimit.y()), + de::clamp(valueMax.z() * formatLimit.z(), 0.0f, (float)formatLimit.z())); + const tcu::IVec3 colorMin ((int)deFloatFloor(colorMinF.x()), + (int)deFloatFloor(colorMinF.y()), + (int)deFloatFloor(colorMinF.z())); + const tcu::IVec3 colorMax ((int)deFloatCeil (colorMaxF.x()), + (int)deFloatCeil (colorMaxF.y()), + (int)deFloatCeil (colorMaxF.z())); + + // Verify validity + if (pixelNativeColor.x() < colorMin.x() || + pixelNativeColor.y() < colorMin.y() || + pixelNativeColor.z() < colorMin.z() || + pixelNativeColor.x() > colorMax.x() || + pixelNativeColor.y() > colorMax.y() || + pixelNativeColor.z() > colorMax.z()) + { + if (errorCount < errorFloodThreshold) + { + // Store candidate information for logging + SingleSampleWideLineCandidate::InterpolationPointCandidate& interpolationCandidate = candidate.interpolationCandidates[candidate.numCandidates++]; + DE_ASSERT(candidate.numCandidates <= DE_LENGTH_OF_ARRAY(candidate.interpolationCandidates)); + + interpolationCandidate.interpolationPoint = rootPosition; + interpolationCandidate.colorMin = colorMin; + interpolationCandidate.colorMax = colorMax; + interpolationCandidate.colorMinF = colorMinF; + interpolationCandidate.colorMaxF = colorMaxF; + interpolationCandidate.valueRangeMin = valueMin.swizzle(0, 1, 2); + interpolationCandidate.valueRangeMax = valueMax.swizzle(0, 1, 2); + } + } + else + { + matchFound = true; + break; + } + } + + if (!matchFound) + { + // store info for logging + if (errorCount < errorFloodThreshold && candidate.numCandidates > 0) + candidates.push_back(candidate); + } + else + { + // no need to check other lines + break; + } + } + } + + if (matchFound) + continue; + + // invalid fragment + ++invalidPixels; + errorMask.setPixel(x, y, invalidPixelColor); + + ++errorCount; + + // don't fill the logs with too much data + if (errorCount < errorFloodThreshold) + { + tcu::MessageBuilder msg(&log); + + msg << "Found an invalid pixel at (" << x << "," << y << "), " << (int)candidates.size() << " candidate reference value(s) found:\n" + << "\tPixel color:\t\t" << color << "\n" + << "\tNative color:\t\t" << pixelNativeColor << "\n"; + + for (int lineCandidateNdx = 0; lineCandidateNdx < (int)candidates.size(); ++lineCandidateNdx) + { + const SingleSampleWideLineCandidate& candidate = candidates[lineCandidateNdx]; + + msg << "\tCandidate line (line " << candidate.lineNdx << "):\n"; + + for (int interpolationCandidateNdx = 0; interpolationCandidateNdx < candidate.numCandidates; ++interpolationCandidateNdx) + { + const SingleSampleWideLineCandidate::InterpolationPointCandidate& interpolationCandidate = candidate.interpolationCandidates[interpolationCandidateNdx]; + + msg << "\t\tCandidate interpolation point (index " << interpolationCandidateNdx << "):\n" + << "\t\t\tRoot fragment position (non-replicated fragment): " << interpolationCandidate.interpolationPoint << ":\n" + << "\t\t\tReference native color min: " << tcu::clamp(interpolationCandidate.colorMin, tcu::IVec3(0,0,0), formatLimit) << "\n" + << "\t\t\tReference native color max: " << tcu::clamp(interpolationCandidate.colorMax, tcu::IVec3(0,0,0), formatLimit) << "\n" + << "\t\t\tReference native float min: " << tcu::clamp(interpolationCandidate.colorMinF, tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast()) << "\n" + << "\t\t\tReference native float max: " << tcu::clamp(interpolationCandidate.colorMaxF, tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast()) << "\n" + << "\t\t\tFmin:\t" << tcu::clamp(interpolationCandidate.valueRangeMin, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f)) << "\n" + << "\t\t\tFmax:\t" << tcu::clamp(interpolationCandidate.valueRangeMax, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f)) << "\n"; + } + } + + msg << tcu::TestLog::EndMessage; + } + } + + // don't just hide failures + if (errorCount > errorFloodThreshold) + log << tcu::TestLog::Message << "Omitted " << (errorCount-errorFloodThreshold) << " pixel error description(s)." << tcu::TestLog::EndMessage; + + // report result + if (invalidPixels) + { + log << tcu::TestLog::Message << invalidPixels << " invalid pixel(s) found." << tcu::TestLog::EndMessage; + log << tcu::TestLog::ImageSet("Verification result", "Result of rendering") + << tcu::TestLog::Image("Result", "Result", surface) + << tcu::TestLog::Image("ErrorMask", "ErrorMask", errorMask) + << tcu::TestLog::EndImageSet; + + return false; + } + else + { + log << tcu::TestLog::Message << "No invalid pixels found." << tcu::TestLog::EndMessage; + log << tcu::TestLog::ImageSet("Verification result", "Result of rendering") + << tcu::TestLog::Image("Result", "Result", surface) + << tcu::TestLog::EndImageSet; + + return true; + } +} + } // anonymous CoverageType calculateTriangleCoverage (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::Vec4& p2, const tcu::IVec2& pixel, const tcu::IVec2& viewportSize, int subpixelBits, bool multisample) @@ -1913,14 +2372,39 @@ bool verifyTriangleGroupInterpolation (const tcu::Surface& surface, const Triang return verifyTriangleGroupInterpolationWithInterpolator(surface, scene, args, log, TriangleInterpolator(scene)); } -bool verifyLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log) +LineInterpolationMethod verifyLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log) { const bool multisampled = args.numSamples != 0; if (multisampled) - return verifyMultisampleLineGroupInterpolation(surface, scene, args, log); + { + if (verifyMultisampleLineGroupInterpolation(surface, scene, args, log)) + return LINEINTERPOLATION_STRICTLY_CORRECT; + return LINEINTERPOLATION_INCORRECT; + } else - return verifySinglesampleLineGroupInterpolation(surface, scene, args, log); + { + const bool isNarrow = (scene.lineWidth == 1.0f); + + // accurate interpolation + if (isNarrow) + { + if (verifySinglesampleNarrowLineGroupInterpolation(surface, scene, args, log)) + return LINEINTERPOLATION_STRICTLY_CORRECT; + } + else + { + if (verifySinglesampleWideLineGroupInterpolation(surface, scene, args, log)) + return LINEINTERPOLATION_STRICTLY_CORRECT; + } + + // check with projected (inaccurate) interpolation + log << tcu::TestLog::Message << "Accurate verification failed, checking with projected weights (inaccurate equation)." << tcu::TestLog::EndMessage; + if (verifyLineGroupInterpolationWithProjectedWeights(surface, scene, args, log)) + return LINEINTERPOLATION_PROJECTED; + + return LINEINTERPOLATION_INCORRECT; + } } } // StateQueryUtil diff --git a/modules/glshared/glsRasterizationTestUtil.hpp b/modules/glshared/glsRasterizationTestUtil.hpp index 7b14ae9..af86283 100644 --- a/modules/glshared/glsRasterizationTestUtil.hpp +++ b/modules/glshared/glsRasterizationTestUtil.hpp @@ -53,6 +53,13 @@ enum VerificationMode VERIFICATIONMODE_LAST }; +enum LineInterpolationMethod +{ + LINEINTERPOLATION_STRICTLY_CORRECT = 0, // !< line interpolation matches the specification + LINEINTERPOLATION_PROJECTED, // !< line interpolation weights are otherwise correct, but they are projected onto major axis + LINEINTERPOLATION_INCORRECT // !< line interpolation is incorrect +}; + struct TriangleSceneSpec { struct SceneTriangle @@ -158,9 +165,9 @@ bool verifyTriangleGroupInterpolation (const tcu::Surface& surface, const Triang * * The background is expected to be black. * - * Returns false if invalid rasterization interpolation is found. + * Returns the detected interpolation method of the input image. *//*--------------------------------------------------------------------*/ -bool verifyLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log); +LineInterpolationMethod verifyLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log); } // StateQueryUtil } // gls