Home | History | Annotate | Download | only in common
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program Tester Core
      3  * ----------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Rasterization verifier utils.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "tcuRasterizationVerifier.hpp"
     25 #include "tcuVector.hpp"
     26 #include "tcuSurface.hpp"
     27 #include "tcuTestLog.hpp"
     28 #include "tcuTextureUtil.hpp"
     29 #include "tcuVectorUtil.hpp"
     30 #include "tcuFloat.hpp"
     31 #include "deMath.h"
     32 
     33 #include "rrRasterizer.hpp"
     34 
     35 #include <limits>
     36 
     37 namespace tcu
     38 {
     39 namespace
     40 {
     41 
     42 bool lineLineIntersect (const tcu::Vector<deInt64, 2>& line0Beg, const tcu::Vector<deInt64, 2>& line0End, const tcu::Vector<deInt64, 2>& line1Beg, const tcu::Vector<deInt64, 2>& line1End)
     43 {
     44 	typedef tcu::Vector<deInt64, 2> I64Vec2;
     45 
     46 	// Lines do not intersect if the other line's endpoints are on the same side
     47 	// otherwise, the do intersect
     48 
     49 	// Test line 0
     50 	{
     51 		const I64Vec2 line			= line0End - line0Beg;
     52 		const I64Vec2 v0			= line1Beg - line0Beg;
     53 		const I64Vec2 v1			= line1End - line0Beg;
     54 		const deInt64 crossProduct0	= (line.x() * v0.y() - line.y() * v0.x());
     55 		const deInt64 crossProduct1	= (line.x() * v1.y() - line.y() * v1.x());
     56 
     57 		// check signs
     58 		if ((crossProduct0 < 0 && crossProduct1 < 0) ||
     59 			(crossProduct0 > 0 && crossProduct1 > 0))
     60 			return false;
     61 	}
     62 
     63 	// Test line 1
     64 	{
     65 		const I64Vec2 line			= line1End - line1Beg;
     66 		const I64Vec2 v0			= line0Beg - line1Beg;
     67 		const I64Vec2 v1			= line0End - line1Beg;
     68 		const deInt64 crossProduct0	= (line.x() * v0.y() - line.y() * v0.x());
     69 		const deInt64 crossProduct1	= (line.x() * v1.y() - line.y() * v1.x());
     70 
     71 		// check signs
     72 		if ((crossProduct0 < 0 && crossProduct1 < 0) ||
     73 			(crossProduct0 > 0 && crossProduct1 > 0))
     74 			return false;
     75 	}
     76 
     77 	return true;
     78 }
     79 
     80 bool isTriangleClockwise (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::Vec4& p2)
     81 {
     82 	const tcu::Vec2	u				(p1.x() / p1.w() - p0.x() / p0.w(), p1.y() / p1.w() - p0.y() / p0.w());
     83 	const tcu::Vec2	v				(p2.x() / p2.w() - p0.x() / p0.w(), p2.y() / p2.w() - p0.y() / p0.w());
     84 	const float		crossProduct	= (u.x() * v.y() - u.y() * v.x());
     85 
     86 	return crossProduct > 0.0f;
     87 }
     88 
     89 bool compareColors (const tcu::RGBA& colorA, const tcu::RGBA& colorB, int redBits, int greenBits, int blueBits)
     90 {
     91 	const int thresholdRed		= 1 << (8 - redBits);
     92 	const int thresholdGreen	= 1 << (8 - greenBits);
     93 	const int thresholdBlue		= 1 << (8 - blueBits);
     94 
     95 	return	deAbs32(colorA.getRed()   - colorB.getRed())   <= thresholdRed   &&
     96 			deAbs32(colorA.getGreen() - colorB.getGreen()) <= thresholdGreen &&
     97 			deAbs32(colorA.getBlue()  - colorB.getBlue())  <= thresholdBlue;
     98 }
     99 
    100 bool pixelNearLineSegment (const tcu::IVec2& pixel, const tcu::Vec2& p0, const tcu::Vec2& p1)
    101 {
    102 	const tcu::Vec2 pixelCenterPosition = tcu::Vec2((float)pixel.x() + 0.5f, (float)pixel.y() + 0.5f);
    103 
    104 	// "Near" = Distance from the line to the pixel is less than 2 * pixel_max_radius. (pixel_max_radius = sqrt(2) / 2)
    105 	const float maxPixelDistance		= 1.414f;
    106 	const float maxPixelDistanceSquared	= 2.0f;
    107 
    108 	// Near the line
    109 	{
    110 		const tcu::Vec2	line			= p1                  - p0;
    111 		const tcu::Vec2	v				= pixelCenterPosition - p0;
    112 		const float		crossProduct	= (line.x() * v.y() - line.y() * v.x());
    113 
    114 		// distance to line: (line x v) / |line|
    115 		//     |(line x v) / |line|| > maxPixelDistance
    116 		// ==> (line x v)^2 / |line|^2 > maxPixelDistance^2
    117 		// ==> (line x v)^2 > maxPixelDistance^2 * |line|^2
    118 
    119 		if (crossProduct * crossProduct > maxPixelDistanceSquared * tcu::lengthSquared(line))
    120 			return false;
    121 	}
    122 
    123 	// Between the endpoints
    124 	{
    125 		// distance from line endpoint 1 to pixel is less than line length + maxPixelDistance
    126 		const float maxDistance = tcu::length(p1 - p0) + maxPixelDistance;
    127 
    128 		if (tcu::length(pixelCenterPosition - p0) > maxDistance)
    129 			return false;
    130 		if (tcu::length(pixelCenterPosition - p1) > maxDistance)
    131 			return false;
    132 	}
    133 
    134 	return true;
    135 }
    136 
    137 bool pixelOnlyOnASharedEdge (const tcu::IVec2& pixel, const TriangleSceneSpec::SceneTriangle& triangle, const tcu::IVec2& viewportSize)
    138 {
    139 	if (triangle.sharedEdge[0] || triangle.sharedEdge[1] || triangle.sharedEdge[2])
    140 	{
    141 		const tcu::Vec2 triangleNormalizedDeviceSpace[3] =
    142 		{
    143 			tcu::Vec2(triangle.positions[0].x() / triangle.positions[0].w(), triangle.positions[0].y() / triangle.positions[0].w()),
    144 			tcu::Vec2(triangle.positions[1].x() / triangle.positions[1].w(), triangle.positions[1].y() / triangle.positions[1].w()),
    145 			tcu::Vec2(triangle.positions[2].x() / triangle.positions[2].w(), triangle.positions[2].y() / triangle.positions[2].w()),
    146 		};
    147 		const tcu::Vec2 triangleScreenSpace[3] =
    148 		{
    149 			(triangleNormalizedDeviceSpace[0] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
    150 			(triangleNormalizedDeviceSpace[1] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
    151 			(triangleNormalizedDeviceSpace[2] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
    152 		};
    153 
    154 		const bool pixelOnEdge0 = pixelNearLineSegment(pixel, triangleScreenSpace[0], triangleScreenSpace[1]);
    155 		const bool pixelOnEdge1 = pixelNearLineSegment(pixel, triangleScreenSpace[1], triangleScreenSpace[2]);
    156 		const bool pixelOnEdge2 = pixelNearLineSegment(pixel, triangleScreenSpace[2], triangleScreenSpace[0]);
    157 
    158 		// If the pixel is on a multiple edges return false
    159 
    160 		if (pixelOnEdge0 && !pixelOnEdge1 && !pixelOnEdge2)
    161 			return triangle.sharedEdge[0];
    162 		if (!pixelOnEdge0 && pixelOnEdge1 && !pixelOnEdge2)
    163 			return triangle.sharedEdge[1];
    164 		if (!pixelOnEdge0 && !pixelOnEdge1 && pixelOnEdge2)
    165 			return triangle.sharedEdge[2];
    166 	}
    167 
    168 	return false;
    169 }
    170 
    171 float triangleArea (const tcu::Vec2& s0, const tcu::Vec2& s1, const tcu::Vec2& s2)
    172 {
    173 	const tcu::Vec2	u				(s1.x() - s0.x(), s1.y() - s0.y());
    174 	const tcu::Vec2	v				(s2.x() - s0.x(), s2.y() - s0.y());
    175 	const float		crossProduct	= (u.x() * v.y() - u.y() * v.x());
    176 
    177 	return crossProduct / 2.0f;
    178 }
    179 
    180 tcu::IVec4 getTriangleAABB (const TriangleSceneSpec::SceneTriangle& triangle, const tcu::IVec2& viewportSize)
    181 {
    182 	const tcu::Vec2 normalizedDeviceSpace[3] =
    183 	{
    184 		tcu::Vec2(triangle.positions[0].x() / triangle.positions[0].w(), triangle.positions[0].y() / triangle.positions[0].w()),
    185 		tcu::Vec2(triangle.positions[1].x() / triangle.positions[1].w(), triangle.positions[1].y() / triangle.positions[1].w()),
    186 		tcu::Vec2(triangle.positions[2].x() / triangle.positions[2].w(), triangle.positions[2].y() / triangle.positions[2].w()),
    187 	};
    188 	const tcu::Vec2 screenSpace[3] =
    189 	{
    190 		(normalizedDeviceSpace[0] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
    191 		(normalizedDeviceSpace[1] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
    192 		(normalizedDeviceSpace[2] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
    193 	};
    194 
    195 	tcu::IVec4 aabb;
    196 
    197 	aabb.x() = (int)deFloatFloor(de::min(de::min(screenSpace[0].x(), screenSpace[1].x()), screenSpace[2].x()));
    198 	aabb.y() = (int)deFloatFloor(de::min(de::min(screenSpace[0].y(), screenSpace[1].y()), screenSpace[2].y()));
    199 	aabb.z() = (int)deFloatCeil (de::max(de::max(screenSpace[0].x(), screenSpace[1].x()), screenSpace[2].x()));
    200 	aabb.w() = (int)deFloatCeil (de::max(de::max(screenSpace[0].y(), screenSpace[1].y()), screenSpace[2].y()));
    201 
    202 	return aabb;
    203 }
    204 
    205 float getExponentEpsilonFromULP (int valueExponent, deUint32 ulp)
    206 {
    207 	DE_ASSERT(ulp < (1u<<10));
    208 
    209 	// assume mediump precision, using ulp as ulps in a 10 bit mantissa
    210 	return tcu::Float32::construct(+1, valueExponent, (1u<<23) + (ulp << (23 - 10))).asFloat() - tcu::Float32::construct(+1, valueExponent, (1u<<23)).asFloat();
    211 }
    212 
    213 float getValueEpsilonFromULP (float value, deUint32 ulp)
    214 {
    215 	DE_ASSERT(value != std::numeric_limits<float>::infinity() && value != -std::numeric_limits<float>::infinity());
    216 
    217 	const int exponent = tcu::Float32(value).exponent();
    218 	return getExponentEpsilonFromULP(exponent, ulp);
    219 }
    220 
    221 float getMaxValueWithinError (float value, deUint32 ulp)
    222 {
    223 	if (value == std::numeric_limits<float>::infinity() || value == -std::numeric_limits<float>::infinity())
    224 		return value;
    225 
    226 	return value + getValueEpsilonFromULP(value, ulp);
    227 }
    228 
    229 float getMinValueWithinError (float value, deUint32 ulp)
    230 {
    231 	if (value == std::numeric_limits<float>::infinity() || value == -std::numeric_limits<float>::infinity())
    232 		return value;
    233 
    234 	return value - getValueEpsilonFromULP(value, ulp);
    235 }
    236 
    237 float getMinFlushToZero (float value)
    238 {
    239 	// flush to zero if that decreases the value
    240 	// assume mediump precision
    241 	if (value > 0.0f && value < tcu::Float32::construct(+1, -14, 1u<<23).asFloat())
    242 		return 0.0f;
    243 	return value;
    244 }
    245 
    246 float getMaxFlushToZero (float value)
    247 {
    248 	// flush to zero if that increases the value
    249 	// assume mediump precision
    250 	if (value < 0.0f && value > tcu::Float32::construct(-1, -14, 1u<<23).asFloat())
    251 		return 0.0f;
    252 	return value;
    253 }
    254 
    255 tcu::IVec3 convertRGB8ToNativeFormat (const tcu::RGBA& color, const RasterizationArguments& args)
    256 {
    257 	tcu::IVec3 pixelNativeColor;
    258 
    259 	for (int channelNdx = 0; channelNdx < 3; ++channelNdx)
    260 	{
    261 		const int channelBitCount	= (channelNdx == 0) ? (args.redBits) : (channelNdx == 1) ? (args.greenBits) : (args.blueBits);
    262 		const int channelPixelValue	= (channelNdx == 0) ? (color.getRed()) : (channelNdx == 1) ? (color.getGreen()) : (color.getBlue());
    263 
    264 		if (channelBitCount <= 8)
    265 			pixelNativeColor[channelNdx] = channelPixelValue >> (8 - channelBitCount);
    266 		else if (channelBitCount == 8)
    267 			pixelNativeColor[channelNdx] = channelPixelValue;
    268 		else
    269 		{
    270 			// just in case someone comes up with 8+ bits framebuffers pixel formats. But as
    271 			// we can only read in rgba8, we have to guess the trailing bits. Guessing 0.
    272 			pixelNativeColor[channelNdx] = channelPixelValue << (channelBitCount - 8);
    273 		}
    274 	}
    275 
    276 	return pixelNativeColor;
    277 }
    278 
    279 /*--------------------------------------------------------------------*//*!
    280  * Returns the maximum value of x / y, where x c [minDividend, maxDividend]
    281  * and y c [minDivisor, maxDivisor]
    282  *//*--------------------------------------------------------------------*/
    283 float maximalRangeDivision (float minDividend, float maxDividend, float minDivisor, float maxDivisor)
    284 {
    285 	DE_ASSERT(minDividend <= maxDividend);
    286 	DE_ASSERT(minDivisor <= maxDivisor);
    287 
    288 	// special cases
    289 	if (minDividend == 0.0f && maxDividend == 0.0f)
    290 		return 0.0f;
    291 	if (minDivisor <= 0.0f && maxDivisor >= 0.0f)
    292 		return std::numeric_limits<float>::infinity();
    293 
    294 	return de::max(de::max(minDividend / minDivisor, minDividend / maxDivisor), de::max(maxDividend / minDivisor, maxDividend / maxDivisor));
    295 }
    296 
    297 /*--------------------------------------------------------------------*//*!
    298  * Returns the minimum value of x / y, where x c [minDividend, maxDividend]
    299  * and y c [minDivisor, maxDivisor]
    300  *//*--------------------------------------------------------------------*/
    301 float minimalRangeDivision (float minDividend, float maxDividend, float minDivisor, float maxDivisor)
    302 {
    303 	DE_ASSERT(minDividend <= maxDividend);
    304 	DE_ASSERT(minDivisor <= maxDivisor);
    305 
    306 	// special cases
    307 	if (minDividend == 0.0f && maxDividend == 0.0f)
    308 		return 0.0f;
    309 	if (minDivisor <= 0.0f && maxDivisor >= 0.0f)
    310 		return -std::numeric_limits<float>::infinity();
    311 
    312 	return de::min(de::min(minDividend / minDivisor, minDividend / maxDivisor), de::min(maxDividend / minDivisor, maxDividend / maxDivisor));
    313 }
    314 
    315 static bool isLineXMajor (const tcu::Vec2& lineScreenSpaceP0, const tcu::Vec2& lineScreenSpaceP1)
    316 {
    317 	return de::abs(lineScreenSpaceP1.x() - lineScreenSpaceP0.x()) >= de::abs(lineScreenSpaceP1.y() - lineScreenSpaceP0.y());
    318 }
    319 
    320 static bool isPackedSSLineXMajor (const tcu::Vec4& packedLine)
    321 {
    322 	const tcu::Vec2 lineScreenSpaceP0 = packedLine.swizzle(0, 1);
    323 	const tcu::Vec2 lineScreenSpaceP1 = packedLine.swizzle(2, 3);
    324 
    325 	return isLineXMajor(lineScreenSpaceP0, lineScreenSpaceP1);
    326 }
    327 
    328 struct InterpolationRange
    329 {
    330 	tcu::Vec3 max;
    331 	tcu::Vec3 min;
    332 };
    333 
    334 struct LineInterpolationRange
    335 {
    336 	tcu::Vec2 max;
    337 	tcu::Vec2 min;
    338 };
    339 
    340 InterpolationRange calcTriangleInterpolationWeights (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::Vec4& p2, const tcu::Vec2& ndpixel)
    341 {
    342 	const int roundError		= 1;
    343 	const int barycentricError	= 3;
    344 	const int divError			= 8;
    345 
    346 	const tcu::Vec2 nd0 = p0.swizzle(0, 1) / p0.w();
    347 	const tcu::Vec2 nd1 = p1.swizzle(0, 1) / p1.w();
    348 	const tcu::Vec2 nd2 = p2.swizzle(0, 1) / p2.w();
    349 
    350 	const float ka = triangleArea(ndpixel, nd1, nd2);
    351 	const float kb = triangleArea(ndpixel, nd2, nd0);
    352 	const float kc = triangleArea(ndpixel, nd0, nd1);
    353 
    354 	const float kaMax = getMaxFlushToZero(getMaxValueWithinError(ka, barycentricError));
    355 	const float kbMax = getMaxFlushToZero(getMaxValueWithinError(kb, barycentricError));
    356 	const float kcMax = getMaxFlushToZero(getMaxValueWithinError(kc, barycentricError));
    357 	const float kaMin = getMinFlushToZero(getMinValueWithinError(ka, barycentricError));
    358 	const float kbMin = getMinFlushToZero(getMinValueWithinError(kb, barycentricError));
    359 	const float kcMin = getMinFlushToZero(getMinValueWithinError(kc, barycentricError));
    360 	DE_ASSERT(kaMin <= kaMax);
    361 	DE_ASSERT(kbMin <= kbMax);
    362 	DE_ASSERT(kcMin <= kcMax);
    363 
    364 	// calculate weights: vec3(ka / p0.w, kb / p1.w, kc / p2.w) / (ka / p0.w + kb / p1.w + kc / p2.w)
    365 	const float maxPreDivisionValues[3] =
    366 	{
    367 		getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(kaMax / p0.w()), divError)),
    368 		getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(kbMax / p1.w()), divError)),
    369 		getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(kcMax / p2.w()), divError)),
    370 	};
    371 	const float minPreDivisionValues[3] =
    372 	{
    373 		getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(kaMin / p0.w()), divError)),
    374 		getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(kbMin / p1.w()), divError)),
    375 		getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(kcMin / p2.w()), divError)),
    376 	};
    377 	DE_ASSERT(minPreDivisionValues[0] <= maxPreDivisionValues[0]);
    378 	DE_ASSERT(minPreDivisionValues[1] <= maxPreDivisionValues[1]);
    379 	DE_ASSERT(minPreDivisionValues[2] <= maxPreDivisionValues[2]);
    380 
    381 	const float maxDivisor = getMaxFlushToZero(getMaxValueWithinError(maxPreDivisionValues[0] + maxPreDivisionValues[1] + maxPreDivisionValues[2], 2*roundError));
    382 	const float minDivisor = getMinFlushToZero(getMinValueWithinError(minPreDivisionValues[0] + minPreDivisionValues[1] + minPreDivisionValues[2], 2*roundError));
    383 	DE_ASSERT(minDivisor <= maxDivisor);
    384 
    385 	InterpolationRange returnValue;
    386 
    387 	returnValue.max.x() = getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(maximalRangeDivision(minPreDivisionValues[0], maxPreDivisionValues[0], minDivisor, maxDivisor)), divError));
    388 	returnValue.max.y() = getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(maximalRangeDivision(minPreDivisionValues[1], maxPreDivisionValues[1], minDivisor, maxDivisor)), divError));
    389 	returnValue.max.z() = getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(maximalRangeDivision(minPreDivisionValues[2], maxPreDivisionValues[2], minDivisor, maxDivisor)), divError));
    390 	returnValue.min.x() = getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(minimalRangeDivision(minPreDivisionValues[0], maxPreDivisionValues[0], minDivisor, maxDivisor)), divError));
    391 	returnValue.min.y() = getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(minimalRangeDivision(minPreDivisionValues[1], maxPreDivisionValues[1], minDivisor, maxDivisor)), divError));
    392 	returnValue.min.z() = getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(minimalRangeDivision(minPreDivisionValues[2], maxPreDivisionValues[2], minDivisor, maxDivisor)), divError));
    393 
    394 	DE_ASSERT(returnValue.min.x() <= returnValue.max.x());
    395 	DE_ASSERT(returnValue.min.y() <= returnValue.max.y());
    396 	DE_ASSERT(returnValue.min.z() <= returnValue.max.z());
    397 
    398 	return returnValue;
    399 }
    400 
    401 LineInterpolationRange calcLineInterpolationWeights (const tcu::Vec2& pa, float wa, const tcu::Vec2& pb, float wb, const tcu::Vec2& pr)
    402 {
    403 	const int roundError	= 1;
    404 	const int divError		= 3;
    405 
    406 	// calc weights:
    407 	//			(1-t) / wa					t / wb
    408 	//		-------------------	,	-------------------
    409 	//		(1-t) / wa + t / wb		(1-t) / wa + t / wb
    410 
    411 	// Allow 1 ULP
    412 	const float		dividend	= tcu::dot(pr - pa, pb - pa);
    413 	const float		dividendMax	= getMaxValueWithinError(dividend, 1);
    414 	const float		dividendMin	= getMinValueWithinError(dividend, 1);
    415 	DE_ASSERT(dividendMin <= dividendMax);
    416 
    417 	// Assuming lengthSquared will not be implemented as sqrt(x)^2, allow 1 ULP
    418 	const float		divisor		= tcu::lengthSquared(pb - pa);
    419 	const float		divisorMax	= getMaxValueWithinError(divisor, 1);
    420 	const float		divisorMin	= getMinValueWithinError(divisor, 1);
    421 	DE_ASSERT(divisorMin <= divisorMax);
    422 
    423 	// Allow 3 ULP precision for division
    424 	const float		tMax		= getMaxValueWithinError(maximalRangeDivision(dividendMin, dividendMax, divisorMin, divisorMax), divError);
    425 	const float		tMin		= getMinValueWithinError(minimalRangeDivision(dividendMin, dividendMax, divisorMin, divisorMax), divError);
    426 	DE_ASSERT(tMin <= tMax);
    427 
    428 	const float		perspectiveTMax			= getMaxValueWithinError(maximalRangeDivision(tMin, tMax, wb, wb), divError);
    429 	const float		perspectiveTMin			= getMinValueWithinError(minimalRangeDivision(tMin, tMax, wb, wb), divError);
    430 	DE_ASSERT(perspectiveTMin <= perspectiveTMax);
    431 
    432 	const float		perspectiveInvTMax		= getMaxValueWithinError(maximalRangeDivision((1.0f - tMax), (1.0f - tMin), wa, wa), divError);
    433 	const float		perspectiveInvTMin		= getMinValueWithinError(minimalRangeDivision((1.0f - tMax), (1.0f - tMin), wa, wa), divError);
    434 	DE_ASSERT(perspectiveInvTMin <= perspectiveInvTMax);
    435 
    436 	const float		perspectiveDivisorMax	= getMaxValueWithinError(perspectiveTMax + perspectiveInvTMax, roundError);
    437 	const float		perspectiveDivisorMin	= getMinValueWithinError(perspectiveTMin + perspectiveInvTMin, roundError);
    438 	DE_ASSERT(perspectiveDivisorMin <= perspectiveDivisorMax);
    439 
    440 	LineInterpolationRange returnValue;
    441 	returnValue.max.x() = getMaxValueWithinError(maximalRangeDivision(perspectiveInvTMin,	perspectiveInvTMax,	perspectiveDivisorMin, perspectiveDivisorMax), divError);
    442 	returnValue.max.y() = getMaxValueWithinError(maximalRangeDivision(perspectiveTMin,		perspectiveTMax,	perspectiveDivisorMin, perspectiveDivisorMax), divError);
    443 	returnValue.min.x() = getMinValueWithinError(minimalRangeDivision(perspectiveInvTMin,	perspectiveInvTMax,	perspectiveDivisorMin, perspectiveDivisorMax), divError);
    444 	returnValue.min.y() = getMinValueWithinError(minimalRangeDivision(perspectiveTMin,		perspectiveTMax,	perspectiveDivisorMin, perspectiveDivisorMax), divError);
    445 
    446 	DE_ASSERT(returnValue.min.x() <= returnValue.max.x());
    447 	DE_ASSERT(returnValue.min.y() <= returnValue.max.y());
    448 
    449 	return returnValue;
    450 }
    451 
    452 LineInterpolationRange calcLineInterpolationWeightsAxisProjected (const tcu::Vec2& pa, float wa, const tcu::Vec2& pb, float wb, const tcu::Vec2& pr)
    453 {
    454 	const int	roundError		= 1;
    455 	const int	divError		= 3;
    456 	const bool	isXMajor		= isLineXMajor(pa, pb);
    457 	const int	majorAxisNdx	= (isXMajor) ? (0) : (1);
    458 
    459 	// calc weights:
    460 	//			(1-t) / wa					t / wb
    461 	//		-------------------	,	-------------------
    462 	//		(1-t) / wa + t / wb		(1-t) / wa + t / wb
    463 
    464 	// Use axis projected (inaccurate) method, i.e. for X-major lines:
    465 	//     (xd - xa) * (xb - xa)      xd - xa
    466 	// t = ---------------------  ==  -------
    467 	//       ( xb - xa ) ^ 2          xb - xa
    468 
    469 	// Allow 1 ULP
    470 	const float		dividend	= (pr[majorAxisNdx] - pa[majorAxisNdx]);
    471 	const float		dividendMax	= getMaxValueWithinError(dividend, 1);
    472 	const float		dividendMin	= getMinValueWithinError(dividend, 1);
    473 	DE_ASSERT(dividendMin <= dividendMax);
    474 
    475 	// Allow 1 ULP
    476 	const float		divisor		= (pb[majorAxisNdx] - pa[majorAxisNdx]);
    477 	const float		divisorMax	= getMaxValueWithinError(divisor, 1);
    478 	const float		divisorMin	= getMinValueWithinError(divisor, 1);
    479 	DE_ASSERT(divisorMin <= divisorMax);
    480 
    481 	// Allow 3 ULP precision for division
    482 	const float		tMax		= getMaxValueWithinError(maximalRangeDivision(dividendMin, dividendMax, divisorMin, divisorMax), divError);
    483 	const float		tMin		= getMinValueWithinError(minimalRangeDivision(dividendMin, dividendMax, divisorMin, divisorMax), divError);
    484 	DE_ASSERT(tMin <= tMax);
    485 
    486 	const float		perspectiveTMax			= getMaxValueWithinError(maximalRangeDivision(tMin, tMax, wb, wb), divError);
    487 	const float		perspectiveTMin			= getMinValueWithinError(minimalRangeDivision(tMin, tMax, wb, wb), divError);
    488 	DE_ASSERT(perspectiveTMin <= perspectiveTMax);
    489 
    490 	const float		perspectiveInvTMax		= getMaxValueWithinError(maximalRangeDivision((1.0f - tMax), (1.0f - tMin), wa, wa), divError);
    491 	const float		perspectiveInvTMin		= getMinValueWithinError(minimalRangeDivision((1.0f - tMax), (1.0f - tMin), wa, wa), divError);
    492 	DE_ASSERT(perspectiveInvTMin <= perspectiveInvTMax);
    493 
    494 	const float		perspectiveDivisorMax	= getMaxValueWithinError(perspectiveTMax + perspectiveInvTMax, roundError);
    495 	const float		perspectiveDivisorMin	= getMinValueWithinError(perspectiveTMin + perspectiveInvTMin, roundError);
    496 	DE_ASSERT(perspectiveDivisorMin <= perspectiveDivisorMax);
    497 
    498 	LineInterpolationRange returnValue;
    499 	returnValue.max.x() = getMaxValueWithinError(maximalRangeDivision(perspectiveInvTMin,	perspectiveInvTMax,	perspectiveDivisorMin, perspectiveDivisorMax), divError);
    500 	returnValue.max.y() = getMaxValueWithinError(maximalRangeDivision(perspectiveTMin,		perspectiveTMax,	perspectiveDivisorMin, perspectiveDivisorMax), divError);
    501 	returnValue.min.x() = getMinValueWithinError(minimalRangeDivision(perspectiveInvTMin,	perspectiveInvTMax,	perspectiveDivisorMin, perspectiveDivisorMax), divError);
    502 	returnValue.min.y() = getMinValueWithinError(minimalRangeDivision(perspectiveTMin,		perspectiveTMax,	perspectiveDivisorMin, perspectiveDivisorMax), divError);
    503 
    504 	DE_ASSERT(returnValue.min.x() <= returnValue.max.x());
    505 	DE_ASSERT(returnValue.min.y() <= returnValue.max.y());
    506 
    507 	return returnValue;
    508 }
    509 
    510 template <typename WeightEquation>
    511 LineInterpolationRange calcSingleSampleLineInterpolationRangeWithWeightEquation (const tcu::Vec2&	pa,
    512 																				 float				wa,
    513 																				 const tcu::Vec2&	pb,
    514 																				 float				wb,
    515 																				 const tcu::IVec2&	pixel,
    516 																				 int				subpixelBits,
    517 																				 WeightEquation		weightEquation)
    518 {
    519 	// allow interpolation weights anywhere in the central subpixels
    520 	const float testSquareSize = (2.0f / (float)(1UL << subpixelBits));
    521 	const float testSquarePos  = (0.5f - testSquareSize / 2);
    522 
    523 	const tcu::Vec2 corners[4] =
    524 	{
    525 		tcu::Vec2((float)pixel.x() + testSquarePos + 0.0f,				(float)pixel.y() + testSquarePos + 0.0f),
    526 		tcu::Vec2((float)pixel.x() + testSquarePos + 0.0f,				(float)pixel.y() + testSquarePos + testSquareSize),
    527 		tcu::Vec2((float)pixel.x() + testSquarePos + testSquareSize,	(float)pixel.y() + testSquarePos + testSquareSize),
    528 		tcu::Vec2((float)pixel.x() + testSquarePos + testSquareSize,	(float)pixel.y() + testSquarePos + 0.0f),
    529 	};
    530 
    531 	// calculate interpolation as a line
    532 	const LineInterpolationRange weights[4] =
    533 	{
    534 		weightEquation(pa, wa, pb, wb, corners[0]),
    535 		weightEquation(pa, wa, pb, wb, corners[1]),
    536 		weightEquation(pa, wa, pb, wb, corners[2]),
    537 		weightEquation(pa, wa, pb, wb, corners[3]),
    538 	};
    539 
    540 	const tcu::Vec2 minWeights = tcu::min(tcu::min(weights[0].min, weights[1].min), tcu::min(weights[2].min, weights[3].min));
    541 	const tcu::Vec2 maxWeights = tcu::max(tcu::max(weights[0].max, weights[1].max), tcu::max(weights[2].max, weights[3].max));
    542 
    543 	LineInterpolationRange result;
    544 	result.min = minWeights;
    545 	result.max = maxWeights;
    546 	return result;
    547 }
    548 
    549 LineInterpolationRange calcSingleSampleLineInterpolationRange (const tcu::Vec2& pa, float wa, const tcu::Vec2& pb, float wb, const tcu::IVec2& pixel, int subpixelBits)
    550 {
    551 	return calcSingleSampleLineInterpolationRangeWithWeightEquation(pa, wa, pb, wb, pixel, subpixelBits, calcLineInterpolationWeights);
    552 }
    553 
    554 LineInterpolationRange calcSingleSampleLineInterpolationRangeAxisProjected (const tcu::Vec2& pa, float wa, const tcu::Vec2& pb, float wb, const tcu::IVec2& pixel, int subpixelBits)
    555 {
    556 	return calcSingleSampleLineInterpolationRangeWithWeightEquation(pa, wa, pb, wb, pixel, subpixelBits, calcLineInterpolationWeightsAxisProjected);
    557 }
    558 
    559 struct TriangleInterpolator
    560 {
    561 	const TriangleSceneSpec& scene;
    562 
    563 	TriangleInterpolator (const TriangleSceneSpec& scene_)
    564 		: scene(scene_)
    565 	{
    566 	}
    567 
    568 	InterpolationRange interpolate (int primitiveNdx, const tcu::IVec2 pixel, const tcu::IVec2 viewportSize, bool multisample, int subpixelBits) const
    569 	{
    570 		// allow anywhere in the pixel area in multisample
    571 		// allow only in the center subpixels (4 subpixels) in singlesample
    572 		const float testSquareSize = (multisample) ? (1.0f) : (2.0f / (float)(1UL << subpixelBits));
    573 		const float testSquarePos  = (multisample) ? (0.0f) : (0.5f - testSquareSize / 2);
    574 		const tcu::Vec2 corners[4] =
    575 		{
    576 			tcu::Vec2(((float)pixel.x() + testSquarePos + 0.0f)           / (float)viewportSize.x() * 2.0f - 1.0f, ((float)pixel.y() + testSquarePos + 0.0f          ) / (float)viewportSize.y() * 2.0f - 1.0f),
    577 			tcu::Vec2(((float)pixel.x() + testSquarePos + 0.0f)           / (float)viewportSize.x() * 2.0f - 1.0f, ((float)pixel.y() + testSquarePos + testSquareSize) / (float)viewportSize.y() * 2.0f - 1.0f),
    578 			tcu::Vec2(((float)pixel.x() + testSquarePos + testSquareSize) / (float)viewportSize.x() * 2.0f - 1.0f, ((float)pixel.y() + testSquarePos + testSquareSize) / (float)viewportSize.y() * 2.0f - 1.0f),
    579 			tcu::Vec2(((float)pixel.x() + testSquarePos + testSquareSize) / (float)viewportSize.x() * 2.0f - 1.0f, ((float)pixel.y() + testSquarePos + 0.0f          ) / (float)viewportSize.y() * 2.0f - 1.0f),
    580 		};
    581 		const InterpolationRange weights[4] =
    582 		{
    583 			calcTriangleInterpolationWeights(scene.triangles[primitiveNdx].positions[0], scene.triangles[primitiveNdx].positions[1], scene.triangles[primitiveNdx].positions[2], corners[0]),
    584 			calcTriangleInterpolationWeights(scene.triangles[primitiveNdx].positions[0], scene.triangles[primitiveNdx].positions[1], scene.triangles[primitiveNdx].positions[2], corners[1]),
    585 			calcTriangleInterpolationWeights(scene.triangles[primitiveNdx].positions[0], scene.triangles[primitiveNdx].positions[1], scene.triangles[primitiveNdx].positions[2], corners[2]),
    586 			calcTriangleInterpolationWeights(scene.triangles[primitiveNdx].positions[0], scene.triangles[primitiveNdx].positions[1], scene.triangles[primitiveNdx].positions[2], corners[3]),
    587 		};
    588 
    589 		InterpolationRange result;
    590 		result.min = tcu::min(tcu::min(weights[0].min, weights[1].min), tcu::min(weights[2].min, weights[3].min));
    591 		result.max = tcu::max(tcu::max(weights[0].max, weights[1].max), tcu::max(weights[2].max, weights[3].max));
    592 		return result;
    593 	}
    594 };
    595 
    596 /*--------------------------------------------------------------------*//*!
    597  * Used only by verifyMultisampleLineGroupInterpolation to calculate
    598  * correct line interpolations for the triangulated lines.
    599  *//*--------------------------------------------------------------------*/
    600 struct MultisampleLineInterpolator
    601 {
    602 	const LineSceneSpec& scene;
    603 
    604 	MultisampleLineInterpolator (const LineSceneSpec& scene_)
    605 		: scene(scene_)
    606 	{
    607 	}
    608 
    609 	InterpolationRange interpolate (int primitiveNdx, const tcu::IVec2 pixel, const tcu::IVec2 viewportSize, bool multisample, int subpixelBits) const
    610 	{
    611 		DE_UNREF(multisample);
    612 		DE_UNREF(subpixelBits);
    613 
    614 		// in triangulation, one line emits two triangles
    615 		const int		lineNdx		= primitiveNdx / 2;
    616 
    617 		// allow interpolation weights anywhere in the pixel
    618 		const tcu::Vec2 corners[4] =
    619 		{
    620 			tcu::Vec2((float)pixel.x() + 0.0f, (float)pixel.y() + 0.0f),
    621 			tcu::Vec2((float)pixel.x() + 0.0f, (float)pixel.y() + 1.0f),
    622 			tcu::Vec2((float)pixel.x() + 1.0f, (float)pixel.y() + 1.0f),
    623 			tcu::Vec2((float)pixel.x() + 1.0f, (float)pixel.y() + 0.0f),
    624 		};
    625 
    626 		const float		wa = scene.lines[lineNdx].positions[0].w();
    627 		const float		wb = scene.lines[lineNdx].positions[1].w();
    628 		const tcu::Vec2	pa = tcu::Vec2((scene.lines[lineNdx].positions[0].x() / wa + 1.0f) * 0.5f * (float)viewportSize.x(),
    629 									   (scene.lines[lineNdx].positions[0].y() / wa + 1.0f) * 0.5f * (float)viewportSize.y());
    630 		const tcu::Vec2	pb = tcu::Vec2((scene.lines[lineNdx].positions[1].x() / wb + 1.0f) * 0.5f * (float)viewportSize.x(),
    631 									   (scene.lines[lineNdx].positions[1].y() / wb + 1.0f) * 0.5f * (float)viewportSize.y());
    632 
    633 		// calculate interpolation as a line
    634 		const LineInterpolationRange weights[4] =
    635 		{
    636 			calcLineInterpolationWeights(pa, wa, pb, wb, corners[0]),
    637 			calcLineInterpolationWeights(pa, wa, pb, wb, corners[1]),
    638 			calcLineInterpolationWeights(pa, wa, pb, wb, corners[2]),
    639 			calcLineInterpolationWeights(pa, wa, pb, wb, corners[3]),
    640 		};
    641 
    642 		const tcu::Vec2 minWeights = tcu::min(tcu::min(weights[0].min, weights[1].min), tcu::min(weights[2].min, weights[3].min));
    643 		const tcu::Vec2 maxWeights = tcu::max(tcu::max(weights[0].max, weights[1].max), tcu::max(weights[2].max, weights[3].max));
    644 
    645 		// 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
    646 		InterpolationRange result;
    647 		result.min = tcu::Vec3(minWeights.x(), 0.0f, minWeights.y());
    648 		result.max = tcu::Vec3(maxWeights.x(), 0.0f, maxWeights.y());
    649 		return result;
    650 	}
    651 };
    652 
    653 template <typename Interpolator>
    654 bool verifyTriangleGroupInterpolationWithInterpolator (const tcu::Surface& surface, const TriangleSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log, const Interpolator& interpolator)
    655 {
    656 	const tcu::RGBA		invalidPixelColor	= tcu::RGBA(255, 0, 0, 255);
    657 	const bool			multisampled		= (args.numSamples != 0);
    658 	const tcu::IVec2	viewportSize		= tcu::IVec2(surface.getWidth(), surface.getHeight());
    659 	const int			errorFloodThreshold	= 4;
    660 	int					errorCount			= 0;
    661 	int					invalidPixels		= 0;
    662 	int					subPixelBits		= args.subpixelBits;
    663 	tcu::Surface		errorMask			(surface.getWidth(), surface.getHeight());
    664 
    665 	tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
    666 
    667 	// log format
    668 
    669 	log << tcu::TestLog::Message << "Verifying rasterization result. Native format is RGB" << args.redBits << args.greenBits << args.blueBits << tcu::TestLog::EndMessage;
    670 	if (args.redBits > 8 || args.greenBits > 8 || args.blueBits > 8)
    671 		log << tcu::TestLog::Message << "Warning! More than 8 bits in a color channel, this may produce false negatives." << tcu::TestLog::EndMessage;
    672 
    673 	// subpixel bits in in a valid range?
    674 
    675 	if (subPixelBits < 0)
    676 	{
    677 		log << tcu::TestLog::Message << "Invalid subpixel count (" << subPixelBits << "), assuming 0" << tcu::TestLog::EndMessage;
    678 		subPixelBits = 0;
    679 	}
    680 	else if (subPixelBits > 16)
    681 	{
    682 		// At high subpixel bit counts we might overflow. Checking at lower bit count is ok, but is less strict
    683 		log << tcu::TestLog::Message << "Subpixel count is greater than 16 (" << subPixelBits << "). Checking results using less strict 16 bit requirements. This may produce false positives." << tcu::TestLog::EndMessage;
    684 		subPixelBits = 16;
    685 	}
    686 
    687 	// check pixels
    688 
    689 	for (int y = 0; y < surface.getHeight(); ++y)
    690 	for (int x = 0; x < surface.getWidth();  ++x)
    691 	{
    692 		const tcu::RGBA		color				= surface.getPixel(x, y);
    693 		bool				stackBottomFound	= false;
    694 		int					stackSize			= 0;
    695 		tcu::Vec4			colorStackMin;
    696 		tcu::Vec4			colorStackMax;
    697 
    698 		// Iterate triangle coverage front to back, find the stack of pontentially contributing fragments
    699 		for (int triNdx = (int)scene.triangles.size() - 1; triNdx >= 0; --triNdx)
    700 		{
    701 			const CoverageType coverage = calculateTriangleCoverage(scene.triangles[triNdx].positions[0],
    702 																	scene.triangles[triNdx].positions[1],
    703 																	scene.triangles[triNdx].positions[2],
    704 																	tcu::IVec2(x, y),
    705 																	viewportSize,
    706 																	subPixelBits,
    707 																	multisampled);
    708 
    709 			if (coverage == COVERAGE_FULL || coverage == COVERAGE_PARTIAL)
    710 			{
    711 				// potentially contributes to the result fragment's value
    712 				const InterpolationRange weights = interpolator.interpolate(triNdx, tcu::IVec2(x, y), viewportSize, multisampled, subPixelBits);
    713 
    714 				const tcu::Vec4 fragmentColorMax =	de::clamp(weights.max.x(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[0] +
    715 													de::clamp(weights.max.y(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[1] +
    716 													de::clamp(weights.max.z(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[2];
    717 				const tcu::Vec4 fragmentColorMin =	de::clamp(weights.min.x(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[0] +
    718 													de::clamp(weights.min.y(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[1] +
    719 													de::clamp(weights.min.z(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[2];
    720 
    721 				if (stackSize++ == 0)
    722 				{
    723 					// first triangle, set the values properly
    724 					colorStackMin = fragmentColorMin;
    725 					colorStackMax = fragmentColorMax;
    726 				}
    727 				else
    728 				{
    729 					// contributing triangle
    730 					colorStackMin = tcu::min(colorStackMin, fragmentColorMin);
    731 					colorStackMax = tcu::max(colorStackMax, fragmentColorMax);
    732 				}
    733 
    734 				if (coverage == COVERAGE_FULL)
    735 				{
    736 					// loop terminates, this is the bottommost fragment
    737 					stackBottomFound = true;
    738 					break;
    739 				}
    740 			}
    741 		}
    742 
    743 		// Partial coverage == background may be visible
    744 		if (stackSize != 0 && !stackBottomFound)
    745 		{
    746 			stackSize++;
    747 			colorStackMin = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
    748 		}
    749 
    750 		// Is the result image color in the valid range.
    751 		if (stackSize == 0)
    752 		{
    753 			// No coverage, allow only background (black, value=0)
    754 			const tcu::IVec3	pixelNativeColor	= convertRGB8ToNativeFormat(color, args);
    755 			const int			threshold			= 1;
    756 
    757 			if (pixelNativeColor.x() > threshold ||
    758 				pixelNativeColor.y() > threshold ||
    759 				pixelNativeColor.z() > threshold)
    760 			{
    761 				++errorCount;
    762 
    763 				// don't fill the logs with too much data
    764 				if (errorCount < errorFloodThreshold)
    765 				{
    766 					log << tcu::TestLog::Message
    767 						<< "Found an invalid pixel at (" << x << "," << y << ")\n"
    768 						<< "\tPixel color:\t\t" << color << "\n"
    769 						<< "\tExpected background color.\n"
    770 						<< tcu::TestLog::EndMessage;
    771 				}
    772 
    773 				++invalidPixels;
    774 				errorMask.setPixel(x, y, invalidPixelColor);
    775 			}
    776 		}
    777 		else
    778 		{
    779 			DE_ASSERT(stackSize);
    780 
    781 			// Each additional step in the stack may cause conversion error of 1 bit due to undefined rounding direction
    782 			const int			thresholdRed	= stackSize - 1;
    783 			const int			thresholdGreen	= stackSize - 1;
    784 			const int			thresholdBlue	= stackSize - 1;
    785 
    786 			const tcu::Vec3		valueRangeMin	= tcu::Vec3(colorStackMin.xyz());
    787 			const tcu::Vec3		valueRangeMax	= tcu::Vec3(colorStackMax.xyz());
    788 
    789 			const tcu::IVec3	formatLimit		((1 << args.redBits) - 1, (1 << args.greenBits) - 1, (1 << args.blueBits) - 1);
    790 			const tcu::Vec3		colorMinF		(de::clamp(valueRangeMin.x() * (float)formatLimit.x(), 0.0f, (float)formatLimit.x()),
    791 												 de::clamp(valueRangeMin.y() * (float)formatLimit.y(), 0.0f, (float)formatLimit.y()),
    792 												 de::clamp(valueRangeMin.z() * (float)formatLimit.z(), 0.0f, (float)formatLimit.z()));
    793 			const tcu::Vec3		colorMaxF		(de::clamp(valueRangeMax.x() * (float)formatLimit.x(), 0.0f, (float)formatLimit.x()),
    794 												 de::clamp(valueRangeMax.y() * (float)formatLimit.y(), 0.0f, (float)formatLimit.y()),
    795 												 de::clamp(valueRangeMax.z() * (float)formatLimit.z(), 0.0f, (float)formatLimit.z()));
    796 			const tcu::IVec3	colorMin		((int)deFloatFloor(colorMinF.x()),
    797 												 (int)deFloatFloor(colorMinF.y()),
    798 												 (int)deFloatFloor(colorMinF.z()));
    799 			const tcu::IVec3	colorMax		((int)deFloatCeil (colorMaxF.x()),
    800 												 (int)deFloatCeil (colorMaxF.y()),
    801 												 (int)deFloatCeil (colorMaxF.z()));
    802 
    803 			// Convert pixel color from rgba8 to the real pixel format. Usually rgba8 or 565
    804 			const tcu::IVec3 pixelNativeColor = convertRGB8ToNativeFormat(color, args);
    805 
    806 			// Validity check
    807 			if (pixelNativeColor.x() < colorMin.x() - thresholdRed   ||
    808 				pixelNativeColor.y() < colorMin.y() - thresholdGreen ||
    809 				pixelNativeColor.z() < colorMin.z() - thresholdBlue  ||
    810 				pixelNativeColor.x() > colorMax.x() + thresholdRed   ||
    811 				pixelNativeColor.y() > colorMax.y() + thresholdGreen ||
    812 				pixelNativeColor.z() > colorMax.z() + thresholdBlue)
    813 			{
    814 				++errorCount;
    815 
    816 				// don't fill the logs with too much data
    817 				if (errorCount <= errorFloodThreshold)
    818 				{
    819 					log << tcu::TestLog::Message
    820 						<< "Found an invalid pixel at (" << x << "," << y << ")\n"
    821 						<< "\tPixel color:\t\t" << color << "\n"
    822 						<< "\tNative color:\t\t" << pixelNativeColor << "\n"
    823 						<< "\tAllowed error:\t\t" << tcu::IVec3(thresholdRed, thresholdGreen, thresholdBlue) << "\n"
    824 						<< "\tReference native color min: " << tcu::clamp(colorMin - tcu::IVec3(thresholdRed, thresholdGreen, thresholdBlue), tcu::IVec3(0,0,0), formatLimit) << "\n"
    825 						<< "\tReference native color max: " << tcu::clamp(colorMax + tcu::IVec3(thresholdRed, thresholdGreen, thresholdBlue), tcu::IVec3(0,0,0), formatLimit) << "\n"
    826 						<< "\tReference native float min: " << tcu::clamp(colorMinF - tcu::IVec3(thresholdRed, thresholdGreen, thresholdBlue).cast<float>(), tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast<float>()) << "\n"
    827 						<< "\tReference native float max: " << tcu::clamp(colorMaxF + tcu::IVec3(thresholdRed, thresholdGreen, thresholdBlue).cast<float>(), tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast<float>()) << "\n"
    828 						<< "\tFmin:\t" << tcu::clamp(valueRangeMin, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f)) << "\n"
    829 						<< "\tFmax:\t" << tcu::clamp(valueRangeMax, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f)) << "\n"
    830 						<< tcu::TestLog::EndMessage;
    831 				}
    832 
    833 				++invalidPixels;
    834 				errorMask.setPixel(x, y, invalidPixelColor);
    835 			}
    836 		}
    837 	}
    838 
    839 	// don't just hide failures
    840 	if (errorCount > errorFloodThreshold)
    841 		log << tcu::TestLog::Message << "Omitted " << (errorCount-errorFloodThreshold) << " pixel error description(s)." << tcu::TestLog::EndMessage;
    842 
    843 	// report result
    844 	if (invalidPixels)
    845 	{
    846 		log << tcu::TestLog::Message << invalidPixels << " invalid pixel(s) found." << tcu::TestLog::EndMessage;
    847 		log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
    848 			<< tcu::TestLog::Image("Result", "Result",			surface)
    849 			<< tcu::TestLog::Image("ErrorMask", "ErrorMask",	errorMask)
    850 			<< tcu::TestLog::EndImageSet;
    851 
    852 		return false;
    853 	}
    854 	else
    855 	{
    856 		log << tcu::TestLog::Message << "No invalid pixels found." << tcu::TestLog::EndMessage;
    857 		log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
    858 			<< tcu::TestLog::Image("Result", "Result", surface)
    859 			<< tcu::TestLog::EndImageSet;
    860 
    861 		return true;
    862 	}
    863 }
    864 
    865 
    866 float calculateIntersectionParameter (const tcu::Vec2 line[2], float w, int componentNdx)
    867 {
    868 	DE_ASSERT(componentNdx < 2);
    869 	if (line[1][componentNdx] == line[0][componentNdx])
    870 		return -1.0f;
    871 
    872 	return (w - line[0][componentNdx]) / (line[1][componentNdx] - line[0][componentNdx]);
    873 }
    874 
    875 // Clips the given line with a ((-w, -w), (-w, w), (w, w), (w, -w)) rectangle
    876 void applyClippingBox (tcu::Vec2 line[2], float w)
    877 {
    878 	for (int side = 0; side < 4; ++side)
    879 	{
    880 		const int	sign		= ((side / 2) * -2) + 1;
    881 		const int	component	= side % 2;
    882 		const float	t			= calculateIntersectionParameter(line, w * (float)sign, component);
    883 
    884 		if ((t > 0) && (t < 1))
    885 		{
    886 			const float newCoord	= t * line[1][1 - component] + (1 - t) * line[0][1 - component];
    887 
    888 			if (line[1][component] > (w * (float)sign))
    889 			{
    890 				line[1 - side / 2][component] = w * (float)sign;
    891 				line[1 - side / 2][1 - component] = newCoord;
    892 			}
    893 			else
    894 			{
    895 				line[side / 2][component] = w * (float)sign;
    896 				line[side / 2][1 - component] = newCoord;
    897 			}
    898 		}
    899 	}
    900 }
    901 
    902 enum ClipMode
    903 {
    904 	CLIPMODE_NO_CLIPPING = 0,
    905 	CLIPMODE_USE_CLIPPING_BOX,
    906 
    907 	CLIPMODE_LAST
    908 };
    909 
    910 bool verifyMultisampleLineGroupRasterization (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log, ClipMode clipMode, VerifyTriangleGroupRasterizationLogStash* logStash = DE_NULL)
    911 {
    912 	// Multisampled line == 2 triangles
    913 
    914 	const tcu::Vec2		viewportSize	= tcu::Vec2((float)surface.getWidth(), (float)surface.getHeight());
    915 	const float			halfLineWidth	= scene.lineWidth * 0.5f;
    916 	TriangleSceneSpec	triangleScene;
    917 
    918 	triangleScene.triangles.resize(2 * scene.lines.size());
    919 	for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
    920 	{
    921 		// Transform to screen space, add pixel offsets, convert back to normalized device space, and test as triangles
    922 		tcu::Vec2 lineNormalizedDeviceSpace[2] =
    923 		{
    924 			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()),
    925 			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()),
    926 		};
    927 
    928 		if (clipMode == CLIPMODE_USE_CLIPPING_BOX)
    929 		{
    930 			applyClippingBox(lineNormalizedDeviceSpace, 1.0f);
    931 		}
    932 
    933 		const tcu::Vec2 lineScreenSpace[2] =
    934 		{
    935 			(lineNormalizedDeviceSpace[0] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * viewportSize,
    936 			(lineNormalizedDeviceSpace[1] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * viewportSize,
    937 		};
    938 
    939 		const tcu::Vec2 lineDir			= tcu::normalize(lineScreenSpace[1] - lineScreenSpace[0]);
    940 		const tcu::Vec2 lineNormalDir	= tcu::Vec2(lineDir.y(), -lineDir.x());
    941 
    942 		const tcu::Vec2 lineQuadScreenSpace[4] =
    943 		{
    944 			lineScreenSpace[0] + lineNormalDir * halfLineWidth,
    945 			lineScreenSpace[0] - lineNormalDir * halfLineWidth,
    946 			lineScreenSpace[1] - lineNormalDir * halfLineWidth,
    947 			lineScreenSpace[1] + lineNormalDir * halfLineWidth,
    948 		};
    949 		const tcu::Vec2 lineQuadNormalizedDeviceSpace[4] =
    950 		{
    951 			lineQuadScreenSpace[0] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
    952 			lineQuadScreenSpace[1] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
    953 			lineQuadScreenSpace[2] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
    954 			lineQuadScreenSpace[3] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
    955 		};
    956 
    957 		triangleScene.triangles[lineNdx*2 + 0].positions[0] = tcu::Vec4(lineQuadNormalizedDeviceSpace[0].x(), lineQuadNormalizedDeviceSpace[0].y(), 0.0f, 1.0f);	triangleScene.triangles[lineNdx*2 + 0].sharedEdge[0] = false;
    958 		triangleScene.triangles[lineNdx*2 + 0].positions[1] = tcu::Vec4(lineQuadNormalizedDeviceSpace[1].x(), lineQuadNormalizedDeviceSpace[1].y(), 0.0f, 1.0f);	triangleScene.triangles[lineNdx*2 + 0].sharedEdge[1] = false;
    959 		triangleScene.triangles[lineNdx*2 + 0].positions[2] = tcu::Vec4(lineQuadNormalizedDeviceSpace[2].x(), lineQuadNormalizedDeviceSpace[2].y(), 0.0f, 1.0f);	triangleScene.triangles[lineNdx*2 + 0].sharedEdge[2] = true;
    960 
    961 		triangleScene.triangles[lineNdx*2 + 1].positions[0] = tcu::Vec4(lineQuadNormalizedDeviceSpace[0].x(), lineQuadNormalizedDeviceSpace[0].y(), 0.0f, 1.0f);	triangleScene.triangles[lineNdx*2 + 1].sharedEdge[0] = true;
    962 		triangleScene.triangles[lineNdx*2 + 1].positions[1] = tcu::Vec4(lineQuadNormalizedDeviceSpace[2].x(), lineQuadNormalizedDeviceSpace[2].y(), 0.0f, 1.0f);	triangleScene.triangles[lineNdx*2 + 1].sharedEdge[1] = false;
    963 		triangleScene.triangles[lineNdx*2 + 1].positions[2] = tcu::Vec4(lineQuadNormalizedDeviceSpace[3].x(), lineQuadNormalizedDeviceSpace[3].y(), 0.0f, 1.0f);	triangleScene.triangles[lineNdx*2 + 1].sharedEdge[2] = false;
    964 	}
    965 
    966 	return verifyTriangleGroupRasterization(surface, triangleScene, args, log, VERIFICATIONMODE_STRICT, logStash);
    967 }
    968 
    969 bool verifyMultisampleLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
    970 {
    971 	// Multisampled line == 2 triangles
    972 
    973 	const tcu::Vec2		viewportSize	= tcu::Vec2((float)surface.getWidth(), (float)surface.getHeight());
    974 	const float			halfLineWidth	= scene.lineWidth * 0.5f;
    975 	TriangleSceneSpec	triangleScene;
    976 
    977 	triangleScene.triangles.resize(2 * scene.lines.size());
    978 	for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
    979 	{
    980 		// Transform to screen space, add pixel offsets, convert back to normalized device space, and test as triangles
    981 		const tcu::Vec2 lineNormalizedDeviceSpace[2] =
    982 		{
    983 			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()),
    984 			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()),
    985 		};
    986 		const tcu::Vec2 lineScreenSpace[2] =
    987 		{
    988 			(lineNormalizedDeviceSpace[0] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * viewportSize,
    989 			(lineNormalizedDeviceSpace[1] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * viewportSize,
    990 		};
    991 
    992 		const tcu::Vec2 lineDir			= tcu::normalize(lineScreenSpace[1] - lineScreenSpace[0]);
    993 		const tcu::Vec2 lineNormalDir	= tcu::Vec2(lineDir.y(), -lineDir.x());
    994 
    995 		const tcu::Vec2 lineQuadScreenSpace[4] =
    996 		{
    997 			lineScreenSpace[0] + lineNormalDir * halfLineWidth,
    998 			lineScreenSpace[0] - lineNormalDir * halfLineWidth,
    999 			lineScreenSpace[1] - lineNormalDir * halfLineWidth,
   1000 			lineScreenSpace[1] + lineNormalDir * halfLineWidth,
   1001 		};
   1002 		const tcu::Vec2 lineQuadNormalizedDeviceSpace[4] =
   1003 		{
   1004 			lineQuadScreenSpace[0] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
   1005 			lineQuadScreenSpace[1] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
   1006 			lineQuadScreenSpace[2] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
   1007 			lineQuadScreenSpace[3] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
   1008 		};
   1009 
   1010 		triangleScene.triangles[lineNdx*2 + 0].positions[0] = tcu::Vec4(lineQuadNormalizedDeviceSpace[0].x(), lineQuadNormalizedDeviceSpace[0].y(), 0.0f, 1.0f);
   1011 		triangleScene.triangles[lineNdx*2 + 0].positions[1] = tcu::Vec4(lineQuadNormalizedDeviceSpace[1].x(), lineQuadNormalizedDeviceSpace[1].y(), 0.0f, 1.0f);
   1012 		triangleScene.triangles[lineNdx*2 + 0].positions[2] = tcu::Vec4(lineQuadNormalizedDeviceSpace[2].x(), lineQuadNormalizedDeviceSpace[2].y(), 0.0f, 1.0f);
   1013 
   1014 		triangleScene.triangles[lineNdx*2 + 0].sharedEdge[0] = false;
   1015 		triangleScene.triangles[lineNdx*2 + 0].sharedEdge[1] = false;
   1016 		triangleScene.triangles[lineNdx*2 + 0].sharedEdge[2] = true;
   1017 
   1018 		triangleScene.triangles[lineNdx*2 + 0].colors[0] = scene.lines[lineNdx].colors[0];
   1019 		triangleScene.triangles[lineNdx*2 + 0].colors[1] = scene.lines[lineNdx].colors[0];
   1020 		triangleScene.triangles[lineNdx*2 + 0].colors[2] = scene.lines[lineNdx].colors[1];
   1021 
   1022 		triangleScene.triangles[lineNdx*2 + 1].positions[0] = tcu::Vec4(lineQuadNormalizedDeviceSpace[0].x(), lineQuadNormalizedDeviceSpace[0].y(), 0.0f, 1.0f);
   1023 		triangleScene.triangles[lineNdx*2 + 1].positions[1] = tcu::Vec4(lineQuadNormalizedDeviceSpace[2].x(), lineQuadNormalizedDeviceSpace[2].y(), 0.0f, 1.0f);
   1024 		triangleScene.triangles[lineNdx*2 + 1].positions[2] = tcu::Vec4(lineQuadNormalizedDeviceSpace[3].x(), lineQuadNormalizedDeviceSpace[3].y(), 0.0f, 1.0f);
   1025 
   1026 		triangleScene.triangles[lineNdx*2 + 1].sharedEdge[0] = true;
   1027 		triangleScene.triangles[lineNdx*2 + 1].sharedEdge[1] = false;
   1028 		triangleScene.triangles[lineNdx*2 + 1].sharedEdge[2] = false;
   1029 
   1030 		triangleScene.triangles[lineNdx*2 + 1].colors[0] = scene.lines[lineNdx].colors[0];
   1031 		triangleScene.triangles[lineNdx*2 + 1].colors[1] = scene.lines[lineNdx].colors[1];
   1032 		triangleScene.triangles[lineNdx*2 + 1].colors[2] = scene.lines[lineNdx].colors[1];
   1033 	}
   1034 
   1035 	return verifyTriangleGroupInterpolationWithInterpolator(surface, triangleScene, args, log, MultisampleLineInterpolator(scene));
   1036 }
   1037 
   1038 bool verifyMultisamplePointGroupRasterization (const tcu::Surface& surface, const PointSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
   1039 {
   1040 	// Multisampled point == 2 triangles
   1041 
   1042 	const tcu::Vec2		viewportSize	= tcu::Vec2((float)surface.getWidth(), (float)surface.getHeight());
   1043 	TriangleSceneSpec	triangleScene;
   1044 
   1045 	triangleScene.triangles.resize(2 * scene.points.size());
   1046 	for (int pointNdx = 0; pointNdx < (int)scene.points.size(); ++pointNdx)
   1047 	{
   1048 		// Transform to screen space, add pixel offsets, convert back to normalized device space, and test as triangles
   1049 		const tcu::Vec2	pointNormalizedDeviceSpace			= tcu::Vec2(scene.points[pointNdx].position.x() / scene.points[pointNdx].position.w(), scene.points[pointNdx].position.y() / scene.points[pointNdx].position.w());
   1050 		const tcu::Vec2	pointScreenSpace					= (pointNormalizedDeviceSpace + tcu::Vec2(1.0f, 1.0f)) * 0.5f * viewportSize;
   1051 		const float		offset								= scene.points[pointNdx].pointSize * 0.5f;
   1052 		const tcu::Vec2	lineQuadNormalizedDeviceSpace[4]	=
   1053 		{
   1054 			(pointScreenSpace + tcu::Vec2(-offset, -offset))/ viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
   1055 			(pointScreenSpace + tcu::Vec2(-offset,  offset))/ viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
   1056 			(pointScreenSpace + tcu::Vec2( offset,  offset))/ viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
   1057 			(pointScreenSpace + tcu::Vec2( offset, -offset))/ viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
   1058 		};
   1059 
   1060 		triangleScene.triangles[pointNdx*2 + 0].positions[0] = tcu::Vec4(lineQuadNormalizedDeviceSpace[0].x(), lineQuadNormalizedDeviceSpace[0].y(), 0.0f, 1.0f);	triangleScene.triangles[pointNdx*2 + 0].sharedEdge[0] = false;
   1061 		triangleScene.triangles[pointNdx*2 + 0].positions[1] = tcu::Vec4(lineQuadNormalizedDeviceSpace[1].x(), lineQuadNormalizedDeviceSpace[1].y(), 0.0f, 1.0f);	triangleScene.triangles[pointNdx*2 + 0].sharedEdge[1] = false;
   1062 		triangleScene.triangles[pointNdx*2 + 0].positions[2] = tcu::Vec4(lineQuadNormalizedDeviceSpace[2].x(), lineQuadNormalizedDeviceSpace[2].y(), 0.0f, 1.0f);	triangleScene.triangles[pointNdx*2 + 0].sharedEdge[2] = true;
   1063 
   1064 		triangleScene.triangles[pointNdx*2 + 1].positions[0] = tcu::Vec4(lineQuadNormalizedDeviceSpace[0].x(), lineQuadNormalizedDeviceSpace[0].y(), 0.0f, 1.0f);	triangleScene.triangles[pointNdx*2 + 1].sharedEdge[0] = true;
   1065 		triangleScene.triangles[pointNdx*2 + 1].positions[1] = tcu::Vec4(lineQuadNormalizedDeviceSpace[2].x(), lineQuadNormalizedDeviceSpace[2].y(), 0.0f, 1.0f);	triangleScene.triangles[pointNdx*2 + 1].sharedEdge[1] = false;
   1066 		triangleScene.triangles[pointNdx*2 + 1].positions[2] = tcu::Vec4(lineQuadNormalizedDeviceSpace[3].x(), lineQuadNormalizedDeviceSpace[3].y(), 0.0f, 1.0f);	triangleScene.triangles[pointNdx*2 + 1].sharedEdge[2] = false;
   1067 	}
   1068 
   1069 	return verifyTriangleGroupRasterization(surface, triangleScene, args, log);
   1070 }
   1071 
   1072 void genScreenSpaceLines (std::vector<tcu::Vec4>& screenspaceLines, const std::vector<LineSceneSpec::SceneLine>& lines, const tcu::IVec2& viewportSize)
   1073 {
   1074 	DE_ASSERT(screenspaceLines.size() == lines.size());
   1075 
   1076 	for (int lineNdx = 0; lineNdx < (int)lines.size(); ++lineNdx)
   1077 	{
   1078 		const tcu::Vec2 lineNormalizedDeviceSpace[2] =
   1079 		{
   1080 			tcu::Vec2(lines[lineNdx].positions[0].x() / lines[lineNdx].positions[0].w(), lines[lineNdx].positions[0].y() / lines[lineNdx].positions[0].w()),
   1081 			tcu::Vec2(lines[lineNdx].positions[1].x() / lines[lineNdx].positions[1].w(), lines[lineNdx].positions[1].y() / lines[lineNdx].positions[1].w()),
   1082 		};
   1083 		const tcu::Vec4 lineScreenSpace[2] =
   1084 		{
   1085 			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),
   1086 			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),
   1087 		};
   1088 
   1089 		screenspaceLines[lineNdx] = tcu::Vec4(lineScreenSpace[0].x(), lineScreenSpace[0].y(), lineScreenSpace[1].x(), lineScreenSpace[1].y());
   1090 	}
   1091 }
   1092 
   1093 bool verifySinglesampleLineGroupRasterization (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
   1094 {
   1095 	DE_ASSERT(deFloatFrac(scene.lineWidth) != 0.5f); // rounding direction is not defined, disallow undefined cases
   1096 	DE_ASSERT(scene.lines.size() < 255); // indices are stored as unsigned 8-bit ints
   1097 
   1098 	bool					allOK				= true;
   1099 	bool					overdrawInReference	= false;
   1100 	int						referenceFragments	= 0;
   1101 	int						resultFragments		= 0;
   1102 	int						lineWidth			= deFloorFloatToInt32(scene.lineWidth + 0.5f);
   1103 	bool					imageShown			= false;
   1104 	std::vector<bool>		lineIsXMajor		(scene.lines.size());
   1105 	std::vector<tcu::Vec4>	screenspaceLines(scene.lines.size());
   1106 
   1107 	// 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
   1108 	tcu::TextureLevel referenceLineMap(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8), surface.getWidth(), surface.getHeight());
   1109 	tcu::clear(referenceLineMap.getAccess(), tcu::IVec4(0, 0, 0, 0));
   1110 
   1111 	genScreenSpaceLines(screenspaceLines, scene.lines, tcu::IVec2(surface.getWidth(), surface.getHeight()));
   1112 
   1113 	for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
   1114 	{
   1115 		rr::SingleSampleLineRasterizer rasterizer(tcu::IVec4(0, 0, surface.getWidth(), surface.getHeight()));
   1116 		rasterizer.init(tcu::Vec4(screenspaceLines[lineNdx][0],
   1117 								  screenspaceLines[lineNdx][1],
   1118 								  0.0f,
   1119 								  1.0f),
   1120 						tcu::Vec4(screenspaceLines[lineNdx][2],
   1121 								  screenspaceLines[lineNdx][3],
   1122 								  0.0f,
   1123 								  1.0f),
   1124 						scene.lineWidth);
   1125 
   1126 		// calculate majority of later use
   1127 		lineIsXMajor[lineNdx] = isPackedSSLineXMajor(screenspaceLines[lineNdx]);
   1128 
   1129 		for (;;)
   1130 		{
   1131 			const int			maxPackets			= 32;
   1132 			int					numRasterized		= 0;
   1133 			rr::FragmentPacket	packets[maxPackets];
   1134 
   1135 			rasterizer.rasterize(packets, DE_NULL, maxPackets, numRasterized);
   1136 
   1137 			for (int packetNdx = 0; packetNdx < numRasterized; ++packetNdx)
   1138 			{
   1139 				for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
   1140 				{
   1141 					if ((deUint32)packets[packetNdx].coverage & (1 << fragNdx))
   1142 					{
   1143 						const tcu::IVec2 fragPos = packets[packetNdx].position + tcu::IVec2(fragNdx%2, fragNdx/2);
   1144 
   1145 						// Check for overdraw
   1146 						if (!overdrawInReference)
   1147 							overdrawInReference = referenceLineMap.getAccess().getPixelInt(fragPos.x(), fragPos.y()).x() != 0;
   1148 
   1149 						// Output pixel
   1150 						referenceLineMap.getAccess().setPixel(tcu::IVec4(lineNdx + 1, 0, 0, 0), fragPos.x(), fragPos.y());
   1151 					}
   1152 				}
   1153 			}
   1154 
   1155 			if (numRasterized != maxPackets)
   1156 				break;
   1157 		}
   1158 	}
   1159 
   1160 	// Requirement 1: The coordinates of a fragment produced by the algorithm may not deviate by more than one unit
   1161 	{
   1162 		tcu::Surface	errorMask			(surface.getWidth(), surface.getHeight());
   1163 		bool			missingFragments	= false;
   1164 
   1165 		tcu::clear(errorMask.getAccess(), tcu::IVec4(0, 255, 0, 255));
   1166 
   1167 		log << tcu::TestLog::Message << "Searching for deviating fragments." << tcu::TestLog::EndMessage;
   1168 
   1169 		for (int y = 0; y < referenceLineMap.getHeight(); ++y)
   1170 		for (int x = 0; x < referenceLineMap.getWidth(); ++x)
   1171 		{
   1172 			const bool reference	= referenceLineMap.getAccess().getPixelInt(x, y).x() != 0;
   1173 			const bool result		= compareColors(surface.getPixel(x, y), tcu::RGBA::white(), args.redBits, args.greenBits, args.blueBits);
   1174 
   1175 			if (reference)
   1176 				++referenceFragments;
   1177 			if (result)
   1178 				++resultFragments;
   1179 
   1180 			if (reference == result)
   1181 				continue;
   1182 
   1183 			// Reference fragment here, matching result fragment must be nearby
   1184 			if (reference && !result)
   1185 			{
   1186 				bool foundFragment = false;
   1187 
   1188 				if (x == 0 || y == 0 || x == referenceLineMap.getWidth() - 1 || y == referenceLineMap.getHeight() -1)
   1189 				{
   1190 					// image boundary, missing fragment could be over the image edge
   1191 					foundFragment = true;
   1192 				}
   1193 
   1194 				// find nearby fragment
   1195 				for (int dy = -1; dy < 2 && !foundFragment; ++dy)
   1196 				for (int dx = -1; dx < 2 && !foundFragment; ++dx)
   1197 				{
   1198 					if (compareColors(surface.getPixel(x+dx, y+dy), tcu::RGBA::white(), args.redBits, args.greenBits, args.blueBits))
   1199 						foundFragment = true;
   1200 				}
   1201 
   1202 				if (!foundFragment)
   1203 				{
   1204 					missingFragments = true;
   1205 					errorMask.setPixel(x, y, tcu::RGBA::red());
   1206 				}
   1207 			}
   1208 		}
   1209 
   1210 		if (missingFragments)
   1211 		{
   1212 			log << tcu::TestLog::Message << "Invalid deviation(s) found." << tcu::TestLog::EndMessage;
   1213 			log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
   1214 				<< tcu::TestLog::Image("Result", "Result",			surface)
   1215 				<< tcu::TestLog::Image("ErrorMask", "ErrorMask",	errorMask)
   1216 				<< tcu::TestLog::EndImageSet;
   1217 
   1218 			imageShown = true;
   1219 			allOK = false;
   1220 		}
   1221 		else
   1222 		{
   1223 			log << tcu::TestLog::Message << "No invalid deviations found." << tcu::TestLog::EndMessage;
   1224 		}
   1225 	}
   1226 
   1227 	// Requirement 2: The total number of fragments produced by the algorithm may differ from
   1228 	//                that produced by the diamond-exit rule by no more than one.
   1229 	{
   1230 		// Check is not valid if the primitives intersect or otherwise share same fragments
   1231 		if (!overdrawInReference)
   1232 		{
   1233 			int allowedDeviation = (int)scene.lines.size() * lineWidth; // one pixel per primitive in the major direction
   1234 
   1235 			log << tcu::TestLog::Message << "Verifying fragment counts:\n"
   1236 				<< "\tDiamond-exit rule: " << referenceFragments << " fragments.\n"
   1237 				<< "\tResult image: " << resultFragments << " fragments.\n"
   1238 				<< "\tAllowing deviation of " << allowedDeviation << " fragments.\n"
   1239 				<< tcu::TestLog::EndMessage;
   1240 
   1241 			if (deAbs32(referenceFragments - resultFragments) > allowedDeviation)
   1242 			{
   1243 				tcu::Surface reference(surface.getWidth(), surface.getHeight());
   1244 
   1245 				// show a helpful reference image
   1246 				tcu::clear(reference.getAccess(), tcu::IVec4(0, 0, 0, 255));
   1247 				for (int y = 0; y < surface.getHeight(); ++y)
   1248 				for (int x = 0; x < surface.getWidth(); ++x)
   1249 					if (referenceLineMap.getAccess().getPixelInt(x, y).x())
   1250 						reference.setPixel(x, y, tcu::RGBA::white());
   1251 
   1252 				log << tcu::TestLog::Message << "Invalid fragment count in result image." << tcu::TestLog::EndMessage;
   1253 				log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
   1254 					<< tcu::TestLog::Image("Reference", "Reference",	reference)
   1255 					<< tcu::TestLog::Image("Result", "Result",			surface)
   1256 					<< tcu::TestLog::EndImageSet;
   1257 
   1258 				allOK = false;
   1259 				imageShown = true;
   1260 			}
   1261 			else
   1262 			{
   1263 				log << tcu::TestLog::Message << "Fragment count is valid." << tcu::TestLog::EndMessage;
   1264 			}
   1265 		}
   1266 		else
   1267 		{
   1268 			log << tcu::TestLog::Message << "Overdraw in scene. Fragment count cannot be verified. Skipping fragment count checks." << tcu::TestLog::EndMessage;
   1269 		}
   1270 	}
   1271 
   1272 	// Requirement 3: Line width must be constant
   1273 	{
   1274 		bool invalidWidthFound = false;
   1275 
   1276 		log << tcu::TestLog::Message << "Verifying line widths of the x-major lines." << tcu::TestLog::EndMessage;
   1277 		for (int y = 1; y < referenceLineMap.getHeight() - 1; ++y)
   1278 		{
   1279 			bool	fullyVisibleLine		= false;
   1280 			bool	previousPixelUndefined	= false;
   1281 			int		currentLine				= 0;
   1282 			int		currentWidth			= 1;
   1283 
   1284 			for (int x = 1; x < referenceLineMap.getWidth() - 1; ++x)
   1285 			{
   1286 				const bool	result	= compareColors(surface.getPixel(x, y), tcu::RGBA::white(), args.redBits, args.greenBits, args.blueBits);
   1287 				int			lineID	= 0;
   1288 
   1289 				// Which line does this fragment belong to?
   1290 
   1291 				if (result)
   1292 				{
   1293 					bool multipleNearbyLines = false;
   1294 					bool renderAtSurfaceEdge = false;
   1295 
   1296 					renderAtSurfaceEdge = (x == 1) || (x == referenceLineMap.getWidth() - 2);
   1297 
   1298 					for (int dy = -1; dy < 2; ++dy)
   1299 					for (int dx = -1; dx < 2; ++dx)
   1300 					{
   1301 						const int nearbyID = referenceLineMap.getAccess().getPixelInt(x+dx, y+dy).x();
   1302 						if (nearbyID)
   1303 						{
   1304 							if (lineID && lineID != nearbyID)
   1305 								multipleNearbyLines = true;
   1306 							lineID = nearbyID;
   1307 						}
   1308 					}
   1309 
   1310 					if (multipleNearbyLines || renderAtSurfaceEdge)
   1311 					{
   1312 						// Another line is too close, don't try to calculate width here
   1313 						// Or the render result is outside of surface range
   1314 						previousPixelUndefined = true;
   1315 						continue;
   1316 					}
   1317 				}
   1318 
   1319 				// Only line with id of lineID is nearby
   1320 
   1321 				if (previousPixelUndefined)
   1322 				{
   1323 					// The line might have been overdrawn or not
   1324 					currentLine = lineID;
   1325 					currentWidth = 1;
   1326 					fullyVisibleLine = false;
   1327 					previousPixelUndefined = false;
   1328 				}
   1329 				else if (lineID == currentLine)
   1330 				{
   1331 					// Current line continues
   1332 					++currentWidth;
   1333 				}
   1334 				else if (lineID > currentLine)
   1335 				{
   1336 					// Another line was drawn over or the line ends
   1337 					currentLine = lineID;
   1338 					currentWidth = 1;
   1339 					fullyVisibleLine = true;
   1340 				}
   1341 				else
   1342 				{
   1343 					// The line ends
   1344 					if (fullyVisibleLine && !lineIsXMajor[currentLine-1])
   1345 					{
   1346 						// check width
   1347 						if (currentWidth != lineWidth)
   1348 						{
   1349 							log << tcu::TestLog::Message << "\tInvalid line width at (" << x - currentWidth << ", " << y << ") - (" << x - 1 << ", " << y << "). Detected width of " << currentWidth << ", expected " << lineWidth << tcu::TestLog::EndMessage;
   1350 							invalidWidthFound = true;
   1351 						}
   1352 					}
   1353 
   1354 					currentLine = lineID;
   1355 					currentWidth = 1;
   1356 					fullyVisibleLine = false;
   1357 				}
   1358 			}
   1359 		}
   1360 
   1361 		log << tcu::TestLog::Message << "Verifying line widths of the y-major lines." << tcu::TestLog::EndMessage;
   1362 		for (int x = 1; x < referenceLineMap.getWidth() - 1; ++x)
   1363 		{
   1364 			bool	fullyVisibleLine		= false;
   1365 			bool	previousPixelUndefined	= false;
   1366 			int		currentLine				= 0;
   1367 			int		currentWidth			= 1;
   1368 
   1369 			for (int y = 1; y < referenceLineMap.getHeight() - 1; ++y)
   1370 			{
   1371 				const bool	result	= compareColors(surface.getPixel(x, y), tcu::RGBA::white(), args.redBits, args.greenBits, args.blueBits);
   1372 				int			lineID	= 0;
   1373 
   1374 				// Which line does this fragment belong to?
   1375 
   1376 				if (result)
   1377 				{
   1378 					bool multipleNearbyLines = false;
   1379 					bool renderAtSurfaceEdge = false;
   1380 
   1381 					renderAtSurfaceEdge = (y == 1) || (y == referenceLineMap.getWidth() - 2);
   1382 
   1383 					for (int dy = -1; dy < 2; ++dy)
   1384 					for (int dx = -1; dx < 2; ++dx)
   1385 					{
   1386 						const int nearbyID = referenceLineMap.getAccess().getPixelInt(x+dx, y+dy).x();
   1387 						if (nearbyID)
   1388 						{
   1389 							if (lineID && lineID != nearbyID)
   1390 								multipleNearbyLines = true;
   1391 							lineID = nearbyID;
   1392 						}
   1393 					}
   1394 
   1395 					if (multipleNearbyLines || renderAtSurfaceEdge)
   1396 					{
   1397 						// Another line is too close, don't try to calculate width here
   1398 						// Or the render result is outside of surface range
   1399 						previousPixelUndefined = true;
   1400 						continue;
   1401 					}
   1402 				}
   1403 
   1404 				// Only line with id of lineID is nearby
   1405 
   1406 				if (previousPixelUndefined)
   1407 				{
   1408 					// The line might have been overdrawn or not
   1409 					currentLine = lineID;
   1410 					currentWidth = 1;
   1411 					fullyVisibleLine = false;
   1412 					previousPixelUndefined = false;
   1413 				}
   1414 				else if (lineID == currentLine)
   1415 				{
   1416 					// Current line continues
   1417 					++currentWidth;
   1418 				}
   1419 				else if (lineID > currentLine)
   1420 				{
   1421 					// Another line was drawn over or the line ends
   1422 					currentLine = lineID;
   1423 					currentWidth = 1;
   1424 					fullyVisibleLine = true;
   1425 				}
   1426 				else
   1427 				{
   1428 					// The line ends
   1429 					if (fullyVisibleLine && lineIsXMajor[currentLine-1])
   1430 					{
   1431 						// check width
   1432 						if (currentWidth != lineWidth)
   1433 						{
   1434 							log << tcu::TestLog::Message << "\tInvalid line width at (" << x << ", " << y - currentWidth << ") - (" << x  << ", " << y - 1 << "). Detected width of " << currentWidth << ", expected " << lineWidth << tcu::TestLog::EndMessage;
   1435 							invalidWidthFound = true;
   1436 						}
   1437 					}
   1438 
   1439 					currentLine = lineID;
   1440 					currentWidth = 1;
   1441 					fullyVisibleLine = false;
   1442 				}
   1443 			}
   1444 		}
   1445 
   1446 		if (invalidWidthFound)
   1447 		{
   1448 			log << tcu::TestLog::Message << "Invalid line width found, image is not valid." << tcu::TestLog::EndMessage;
   1449 			allOK = false;
   1450 		}
   1451 		else
   1452 		{
   1453 			log << tcu::TestLog::Message << "Line widths are valid." << tcu::TestLog::EndMessage;
   1454 		}
   1455 	}
   1456 
   1457 	//\todo [2013-10-24 jarkko].
   1458 	//Requirement 4. If two line segments share a common endpoint, and both segments are either
   1459 	//x-major (both left-to-right or both right-to-left) or y-major (both bottom-totop
   1460 	//or both top-to-bottom), then rasterizing both segments may not produce
   1461 	//duplicate fragments, nor may any fragments be omitted so as to interrupt
   1462 	//continuity of the connected segments.
   1463 
   1464 	if (!imageShown)
   1465 	{
   1466 		log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
   1467 			<< tcu::TestLog::Image("Result", "Result", surface)
   1468 			<< tcu::TestLog::EndImageSet;
   1469 	}
   1470 
   1471 	return allOK;
   1472 }
   1473 
   1474 struct SingleSampleNarrowLineCandidate
   1475 {
   1476 	int			lineNdx;
   1477 	tcu::IVec3	colorMin;
   1478 	tcu::IVec3	colorMax;
   1479 	tcu::Vec3	colorMinF;
   1480 	tcu::Vec3	colorMaxF;
   1481 	tcu::Vec3	valueRangeMin;
   1482 	tcu::Vec3	valueRangeMax;
   1483 };
   1484 
   1485 void setMaskMapCoverageBitForLine (int bitNdx, const tcu::Vec2& screenSpaceP0, const tcu::Vec2& screenSpaceP1, float lineWidth, tcu::PixelBufferAccess maskMap)
   1486 {
   1487 	enum
   1488 	{
   1489 		MAX_PACKETS = 32,
   1490 	};
   1491 
   1492 	rr::SingleSampleLineRasterizer	rasterizer				(tcu::IVec4(0, 0, maskMap.getWidth(), maskMap.getHeight()));
   1493 	int								numRasterized			= MAX_PACKETS;
   1494 	rr::FragmentPacket				packets[MAX_PACKETS];
   1495 
   1496 	rasterizer.init(tcu::Vec4(screenSpaceP0.x(), screenSpaceP0.y(), 0.0f, 1.0f),
   1497 					tcu::Vec4(screenSpaceP1.x(), screenSpaceP1.y(), 0.0f, 1.0f),
   1498 					lineWidth);
   1499 
   1500 	while (numRasterized == MAX_PACKETS)
   1501 	{
   1502 		rasterizer.rasterize(packets, DE_NULL, MAX_PACKETS, numRasterized);
   1503 
   1504 		for (int packetNdx = 0; packetNdx < numRasterized; ++packetNdx)
   1505 		{
   1506 			for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
   1507 			{
   1508 				if ((deUint32)packets[packetNdx].coverage & (1 << fragNdx))
   1509 				{
   1510 					const tcu::IVec2	fragPos			= packets[packetNdx].position + tcu::IVec2(fragNdx%2, fragNdx/2);
   1511 
   1512 					DE_ASSERT(deInBounds32(fragPos.x(), 0, maskMap.getWidth()));
   1513 					DE_ASSERT(deInBounds32(fragPos.y(), 0, maskMap.getHeight()));
   1514 
   1515 					const deUint32		previousMask	= maskMap.getPixelUint(fragPos.x(), fragPos.y()).x();
   1516 					const deUint32		newMask			= (previousMask) | ((deUint32)1u << bitNdx);
   1517 
   1518 					maskMap.setPixel(tcu::UVec4(newMask, 0, 0, 0), fragPos.x(), fragPos.y());
   1519 				}
   1520 			}
   1521 		}
   1522 	}
   1523 }
   1524 
   1525 void setMaskMapCoverageBitForLines (const std::vector<tcu::Vec4>& screenspaceLines, float lineWidth, tcu::PixelBufferAccess maskMap)
   1526 {
   1527 	for (int lineNdx = 0; lineNdx < (int)screenspaceLines.size(); ++lineNdx)
   1528 	{
   1529 		const tcu::Vec2 pa = screenspaceLines[lineNdx].swizzle(0, 1);
   1530 		const tcu::Vec2 pb = screenspaceLines[lineNdx].swizzle(2, 3);
   1531 
   1532 		setMaskMapCoverageBitForLine(lineNdx, pa, pb, lineWidth, maskMap);
   1533 	}
   1534 }
   1535 
   1536 // verify line interpolation assuming line pixels are interpolated independently depending only on screen space location
   1537 bool verifyLineGroupPixelIndependentInterpolation (const tcu::Surface&				surface,
   1538 												   const LineSceneSpec&				scene,
   1539 												   const RasterizationArguments&	args,
   1540 												   tcu::TestLog&					log,
   1541 												   LineInterpolationMethod			interpolationMethod)
   1542 {
   1543 	DE_ASSERT(scene.lines.size() < 8); // coverage indices are stored as bitmask in a unsigned 8-bit ints
   1544 	DE_ASSERT(interpolationMethod == LINEINTERPOLATION_STRICTLY_CORRECT || interpolationMethod == LINEINTERPOLATION_PROJECTED);
   1545 
   1546 	const tcu::RGBA			invalidPixelColor	= tcu::RGBA(255, 0, 0, 255);
   1547 	const tcu::IVec2		viewportSize		= tcu::IVec2(surface.getWidth(), surface.getHeight());
   1548 	const int				errorFloodThreshold	= 4;
   1549 	int						errorCount			= 0;
   1550 	tcu::Surface			errorMask			(surface.getWidth(), surface.getHeight());
   1551 	int						invalidPixels		= 0;
   1552 	std::vector<tcu::Vec4>	screenspaceLines	(scene.lines.size()); //!< packed (x0, y0, x1, y1)
   1553 
   1554 	// Reference renderer produces correct fragments using the diamond-exit-rule. Make 2D int array, store line coverage as a 8-bit bitfield
   1555 	// The map is used to find lines with potential coverage to a given pixel
   1556 	tcu::TextureLevel		referenceLineMap	(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8), surface.getWidth(), surface.getHeight());
   1557 
   1558 	tcu::clear(referenceLineMap.getAccess(), tcu::IVec4(0, 0, 0, 0));
   1559 	tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
   1560 
   1561 	// log format
   1562 
   1563 	log << tcu::TestLog::Message << "Verifying rasterization result. Native format is RGB" << args.redBits << args.greenBits << args.blueBits << tcu::TestLog::EndMessage;
   1564 	if (args.redBits > 8 || args.greenBits > 8 || args.blueBits > 8)
   1565 		log << tcu::TestLog::Message << "Warning! More than 8 bits in a color channel, this may produce false negatives." << tcu::TestLog::EndMessage;
   1566 
   1567 	// prepare lookup map
   1568 
   1569 	genScreenSpaceLines(screenspaceLines, scene.lines, viewportSize);
   1570 	setMaskMapCoverageBitForLines(screenspaceLines, scene.lineWidth, referenceLineMap.getAccess());
   1571 
   1572 	// Find all possible lines with coverage, check pixel color matches one of them
   1573 
   1574 	for (int y = 1; y < surface.getHeight() - 1; ++y)
   1575 	for (int x = 1; x < surface.getWidth()  - 1; ++x)
   1576 	{
   1577 		const tcu::RGBA		color					= surface.getPixel(x, y);
   1578 		const tcu::IVec3	pixelNativeColor		= convertRGB8ToNativeFormat(color, args);	// Convert pixel color from rgba8 to the real pixel format. Usually rgba8 or 565
   1579 		int					lineCoverageSet			= 0;										// !< lines that may cover this fragment
   1580 		int					lineSurroundingCoverage = 0xFFFF;									// !< lines that will cover this fragment
   1581 		bool				matchFound				= false;
   1582 		const tcu::IVec3	formatLimit				((1 << args.redBits) - 1, (1 << args.greenBits) - 1, (1 << args.blueBits) - 1);
   1583 
   1584 		std::vector<SingleSampleNarrowLineCandidate> candidates;
   1585 
   1586 		// Find lines with possible coverage
   1587 
   1588 		for (int dy = -1; dy < 2; ++dy)
   1589 		for (int dx = -1; dx < 2; ++dx)
   1590 		{
   1591 			const int coverage = referenceLineMap.getAccess().getPixelInt(x+dx, y+dy).x();
   1592 
   1593 			lineCoverageSet			|= coverage;
   1594 			lineSurroundingCoverage	&= coverage;
   1595 		}
   1596 
   1597 		// background color is possible?
   1598 		if (lineSurroundingCoverage == 0 && compareColors(color, tcu::RGBA::black(), args.redBits, args.greenBits, args.blueBits))
   1599 			continue;
   1600 
   1601 		// Check those lines
   1602 
   1603 		for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
   1604 		{
   1605 			if (((lineCoverageSet >> lineNdx) & 0x01) != 0)
   1606 			{
   1607 				const float						wa				= scene.lines[lineNdx].positions[0].w();
   1608 				const float						wb				= scene.lines[lineNdx].positions[1].w();
   1609 				const tcu::Vec2					pa				= screenspaceLines[lineNdx].swizzle(0, 1);
   1610 				const tcu::Vec2					pb				= screenspaceLines[lineNdx].swizzle(2, 3);
   1611 
   1612 				const LineInterpolationRange	range			= (interpolationMethod == LINEINTERPOLATION_STRICTLY_CORRECT)
   1613 																	? (calcSingleSampleLineInterpolationRange(pa, wa, pb, wb, tcu::IVec2(x, y), args.subpixelBits))
   1614 																	: (calcSingleSampleLineInterpolationRangeAxisProjected(pa, wa, pb, wb, tcu::IVec2(x, y), args.subpixelBits));
   1615 
   1616 				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];
   1617 				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];
   1618 
   1619 				const tcu::Vec3					colorMinF		(de::clamp(valueMin.x() * (float)formatLimit.x(), 0.0f, (float)formatLimit.x()),
   1620 																 de::clamp(valueMin.y() * (float)formatLimit.y(), 0.0f, (float)formatLimit.y()),
   1621 																 de::clamp(valueMin.z() * (float)formatLimit.z(), 0.0f, (float)formatLimit.z()));
   1622 				const tcu::Vec3					colorMaxF		(de::clamp(valueMax.x() * (float)formatLimit.x(), 0.0f, (float)formatLimit.x()),
   1623 																 de::clamp(valueMax.y() * (float)formatLimit.y(), 0.0f, (float)formatLimit.y()),
   1624 																 de::clamp(valueMax.z() * (float)formatLimit.z(), 0.0f, (float)formatLimit.z()));
   1625 				const tcu::IVec3				colorMin		((int)deFloatFloor(colorMinF.x()),
   1626 																 (int)deFloatFloor(colorMinF.y()),
   1627 																 (int)deFloatFloor(colorMinF.z()));
   1628 				const tcu::IVec3				colorMax		((int)deFloatCeil (colorMaxF.x()),
   1629 																 (int)deFloatCeil (colorMaxF.y()),
   1630 																 (int)deFloatCeil (colorMaxF.z()));
   1631 
   1632 				// Verify validity
   1633 				if (pixelNativeColor.x() < colorMin.x() ||
   1634 					pixelNativeColor.y() < colorMin.y() ||
   1635 					pixelNativeColor.z() < colorMin.z() ||
   1636 					pixelNativeColor.x() > colorMax.x() ||
   1637 					pixelNativeColor.y() > colorMax.y() ||
   1638 					pixelNativeColor.z() > colorMax.z())
   1639 				{
   1640 					if (errorCount < errorFloodThreshold)
   1641 					{
   1642 						// Store candidate information for logging
   1643 						SingleSampleNarrowLineCandidate candidate;
   1644 
   1645 						candidate.lineNdx		= lineNdx;
   1646 						candidate.colorMin		= colorMin;
   1647 						candidate.colorMax		= colorMax;
   1648 						candidate.colorMinF		= colorMinF;
   1649 						candidate.colorMaxF		= colorMaxF;
   1650 						candidate.valueRangeMin	= valueMin.swizzle(0, 1, 2);
   1651 						candidate.valueRangeMax	= valueMax.swizzle(0, 1, 2);
   1652 
   1653 						candidates.push_back(candidate);
   1654 					}
   1655 				}
   1656 				else
   1657 				{
   1658 					matchFound = true;
   1659 					break;
   1660 				}
   1661 			}
   1662 		}
   1663 
   1664 		if (matchFound)
   1665 			continue;
   1666 
   1667 		// invalid fragment
   1668 		++invalidPixels;
   1669 		errorMask.setPixel(x, y, invalidPixelColor);
   1670 
   1671 		++errorCount;
   1672 
   1673 		// don't fill the logs with too much data
   1674 		if (errorCount < errorFloodThreshold)
   1675 		{
   1676 			log << tcu::TestLog::Message
   1677 				<< "Found an invalid pixel at (" << x << "," << y << "), " << (int)candidates.size() << " candidate reference value(s) found:\n"
   1678 				<< "\tPixel color:\t\t" << color << "\n"
   1679 				<< "\tNative color:\t\t" << pixelNativeColor << "\n"
   1680 				<< tcu::TestLog::EndMessage;
   1681 
   1682 			for (int candidateNdx = 0; candidateNdx < (int)candidates.size(); ++candidateNdx)
   1683 			{
   1684 				const SingleSampleNarrowLineCandidate& candidate = candidates[candidateNdx];
   1685 
   1686 				log << tcu::TestLog::Message << "\tCandidate (line " << candidate.lineNdx << "):\n"
   1687 					<< "\t\tReference native color min: " << tcu::clamp(candidate.colorMin, tcu::IVec3(0,0,0), formatLimit) << "\n"
   1688 					<< "\t\tReference native color max: " << tcu::clamp(candidate.colorMax, tcu::IVec3(0,0,0), formatLimit) << "\n"
   1689 					<< "\t\tReference native float min: " << tcu::clamp(candidate.colorMinF, tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast<float>()) << "\n"
   1690 					<< "\t\tReference native float max: " << tcu::clamp(candidate.colorMaxF, tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast<float>()) << "\n"
   1691 					<< "\t\tFmin:\t" << tcu::clamp(candidate.valueRangeMin, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f)) << "\n"
   1692 					<< "\t\tFmax:\t" << tcu::clamp(candidate.valueRangeMax, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f)) << "\n"
   1693 					<< tcu::TestLog::EndMessage;
   1694 			}
   1695 		}
   1696 	}
   1697 
   1698 	// don't just hide failures
   1699 	if (errorCount > errorFloodThreshold)
   1700 		log << tcu::TestLog::Message << "Omitted " << (errorCount-errorFloodThreshold) << " pixel error description(s)." << tcu::TestLog::EndMessage;
   1701 
   1702 	// report result
   1703 	if (invalidPixels)
   1704 	{
   1705 		log << tcu::TestLog::Message << invalidPixels << " invalid pixel(s) found." << tcu::TestLog::EndMessage;
   1706 		log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
   1707 			<< tcu::TestLog::Image("Result", "Result",			surface)
   1708 			<< tcu::TestLog::Image("ErrorMask", "ErrorMask",	errorMask)
   1709 			<< tcu::TestLog::EndImageSet;
   1710 
   1711 		return false;
   1712 	}
   1713 	else
   1714 	{
   1715 		log << tcu::TestLog::Message << "No invalid pixels found." << tcu::TestLog::EndMessage;
   1716 		log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
   1717 			<< tcu::TestLog::Image("Result", "Result", surface)
   1718 			<< tcu::TestLog::EndImageSet;
   1719 
   1720 		return true;
   1721 	}
   1722 }
   1723 
   1724 bool verifySinglesampleNarrowLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
   1725 {
   1726 	DE_ASSERT(scene.lineWidth == 1.0f);
   1727 	return verifyLineGroupPixelIndependentInterpolation(surface, scene, args, log, LINEINTERPOLATION_STRICTLY_CORRECT);
   1728 }
   1729 
   1730 bool verifyLineGroupInterpolationWithProjectedWeights (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
   1731 {
   1732 	return verifyLineGroupPixelIndependentInterpolation(surface, scene, args, log, LINEINTERPOLATION_PROJECTED);
   1733 }
   1734 
   1735 struct SingleSampleWideLineCandidate
   1736 {
   1737 	struct InterpolationPointCandidate
   1738 	{
   1739 		tcu::IVec2	interpolationPoint;
   1740 		tcu::IVec3	colorMin;
   1741 		tcu::IVec3	colorMax;
   1742 		tcu::Vec3	colorMinF;
   1743 		tcu::Vec3	colorMaxF;
   1744 		tcu::Vec3	valueRangeMin;
   1745 		tcu::Vec3	valueRangeMax;
   1746 	};
   1747 
   1748 	int							lineNdx;
   1749 	int							numCandidates;
   1750 	InterpolationPointCandidate	interpolationCandidates[3];
   1751 };
   1752 
   1753 // return point on line at a given position on a given axis
   1754 tcu::Vec2 getLineCoordAtAxisCoord (const tcu::Vec2& pa, const tcu::Vec2& pb, bool isXAxis, float axisCoord)
   1755 {
   1756 	const int	fixedCoordNdx		= (isXAxis) ? (0) : (1);
   1757 	const int	varyingCoordNdx		= (isXAxis) ? (1) : (0);
   1758 
   1759 	const float	fixedDifference		= pb[fixedCoordNdx] - pa[fixedCoordNdx];
   1760 	const float	varyingDifference	= pb[varyingCoordNdx] - pa[varyingCoordNdx];
   1761 
   1762 	DE_ASSERT(fixedDifference != 0.0f);
   1763 
   1764 	const float	resultFixedCoord	= axisCoord;
   1765 	const float	resultVaryingCoord	= pa[varyingCoordNdx] + (axisCoord - pa[fixedCoordNdx]) * (varyingDifference / fixedDifference);
   1766 
   1767 	return (isXAxis) ? (tcu::Vec2(resultFixedCoord, resultVaryingCoord))
   1768 					 : (tcu::Vec2(resultVaryingCoord, resultFixedCoord));
   1769 }
   1770 
   1771 bool isBlack (const tcu::RGBA& c)
   1772 {
   1773 	return c.getRed() == 0 && c.getGreen() == 0 && c.getBlue() == 0;
   1774 }
   1775 
   1776 bool verifySinglesampleWideLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
   1777 {
   1778 	DE_ASSERT(deFloatFrac(scene.lineWidth) != 0.5f); // rounding direction is not defined, disallow undefined cases
   1779 	DE_ASSERT(scene.lines.size() < 8); // coverage indices are stored as bitmask in a unsigned 8-bit ints
   1780 
   1781 	enum
   1782 	{
   1783 		FLAG_ROOT_NOT_SET = (1u << 16)
   1784 	};
   1785 
   1786 	const tcu::RGBA						invalidPixelColor	= tcu::RGBA(255, 0, 0, 255);
   1787 	const tcu::IVec2					viewportSize		= tcu::IVec2(surface.getWidth(), surface.getHeight());
   1788 	const int							errorFloodThreshold	= 4;
   1789 	int									errorCount			= 0;
   1790 	tcu::Surface						errorMask			(surface.getWidth(), surface.getHeight());
   1791 	int									invalidPixels		= 0;
   1792 	std::vector<tcu::Vec4>				effectiveLines		(scene.lines.size()); //!< packed (x0, y0, x1, y1)
   1793 	std::vector<bool>					lineIsXMajor		(scene.lines.size());
   1794 
   1795 	// for each line, for every distinct major direction fragment, store root pixel location (along
   1796 	// minor direction);
   1797 	std::vector<std::vector<deUint32> >	rootPixelLocation	(scene.lines.size()); //!< packed [16b - flags] [16b - coordinate]
   1798 
   1799 	// log format
   1800 
   1801 	log << tcu::TestLog::Message << "Verifying rasterization result. Native format is RGB" << args.redBits << args.greenBits << args.blueBits << tcu::TestLog::EndMessage;
   1802 	if (args.redBits > 8 || args.greenBits > 8 || args.blueBits > 8)
   1803 		log << tcu::TestLog::Message << "Warning! More than 8 bits in a color channel, this may produce false negatives." << tcu::TestLog::EndMessage;
   1804 
   1805 	// Reference renderer produces correct fragments using the diamond-exit-rule. Make 2D int array, store line coverage as a 8-bit bitfield
   1806 	// The map is used to find lines with potential coverage to a given pixel
   1807 	tcu::TextureLevel referenceLineMap(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8), surface.getWidth(), surface.getHeight());
   1808 	tcu::clear(referenceLineMap.getAccess(), tcu::IVec4(0, 0, 0, 0));
   1809 
   1810 	tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
   1811 
   1812 	// calculate mask and effective line coordinates
   1813 	{
   1814 		std::vector<tcu::Vec4> screenspaceLines(scene.lines.size());
   1815 
   1816 		genScreenSpaceLines(screenspaceLines, scene.lines, viewportSize);
   1817 		setMaskMapCoverageBitForLines(screenspaceLines, scene.lineWidth, referenceLineMap.getAccess());
   1818 
   1819 		for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
   1820 		{
   1821 			const tcu::Vec2	lineScreenSpaceP0	= screenspaceLines[lineNdx].swizzle(0, 1);
   1822 			const tcu::Vec2	lineScreenSpaceP1	= screenspaceLines[lineNdx].swizzle(2, 3);
   1823 			const bool		isXMajor			= isPackedSSLineXMajor(screenspaceLines[lineNdx]);
   1824 
   1825 			lineIsXMajor[lineNdx] = isXMajor;
   1826 
   1827 			// wide line interpolations are calculated for a line moved in minor direction
   1828 			{
   1829 				const float		offsetLength	= (scene.lineWidth - 1.0f) / 2.0f;
   1830 				const tcu::Vec2	offsetDirection	= (isXMajor) ? (tcu::Vec2(0.0f, -1.0f)) : (tcu::Vec2(-1.0f, 0.0f));
   1831 				const tcu::Vec2	offset			= offsetDirection * offsetLength;
   1832 
   1833 				effectiveLines[lineNdx] = tcu::Vec4(lineScreenSpaceP0.x() + offset.x(),
   1834 													lineScreenSpaceP0.y() + offset.y(),
   1835 													lineScreenSpaceP1.x() + offset.x(),
   1836 													lineScreenSpaceP1.y() + offset.y());
   1837 			}
   1838 		}
   1839 	}
   1840 
   1841 	for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
   1842 	{
   1843 		// Calculate root pixel lookup table for this line. Since the implementation's fragment
   1844 		// major coordinate range might not be a subset of the correct line range (they are allowed
   1845 		// to vary by one pixel), we must extend the domain to cover whole viewport along major
   1846 		// dimension.
   1847 		//
   1848 		// Expanding line strip to (effectively) infinite line might result in exit-diamnod set
   1849 		// that is not a superset of the exit-diamond set of the line strip. In practice, this
   1850 		// won't be an issue, since the allow-one-pixel-variation rule should tolerate this even
   1851 		// if the original and extended line would resolve differently a diamond the line just
   1852 		// touches (precision lost in expansion changes enter/exit status).
   1853 
   1854 		{
   1855 			const bool						isXMajor			= lineIsXMajor[lineNdx];
   1856 			const int						majorSize			= (isXMajor) ? (surface.getWidth()) : (surface.getHeight());
   1857 			rr::LineExitDiamondGenerator	diamondGenerator;
   1858 			rr::LineExitDiamond				diamonds[32];
   1859 			int								numRasterized		= DE_LENGTH_OF_ARRAY(diamonds);
   1860 
   1861 			// Expand to effectively infinite line (endpoints are just one pixel over viewport boundaries)
   1862 			const tcu::Vec2					expandedP0		= getLineCoordAtAxisCoord(effectiveLines[lineNdx].swizzle(0, 1), effectiveLines[lineNdx].swizzle(2, 3), isXMajor, -1.0f);
   1863 			const tcu::Vec2					expandedP1		= getLineCoordAtAxisCoord(effectiveLines[lineNdx].swizzle(0, 1), effectiveLines[lineNdx].swizzle(2, 3), isXMajor, (float)majorSize + 1.0f);
   1864 
   1865 			diamondGenerator.init(tcu::Vec4(expandedP0.x(), expandedP0.y(), 0.0f, 1.0f),
   1866 								  tcu::Vec4(expandedP1.x(), expandedP1.y(), 0.0f, 1.0f));
   1867 
   1868 			rootPixelLocation[lineNdx].resize(majorSize, FLAG_ROOT_NOT_SET);
   1869 
   1870 			while (numRasterized == DE_LENGTH_OF_ARRAY(diamonds))
   1871 			{
   1872 				diamondGenerator.rasterize(diamonds, DE_LENGTH_OF_ARRAY(diamonds), numRasterized);
   1873 
   1874 				for (int packetNdx = 0; packetNdx < numRasterized; ++packetNdx)
   1875 				{
   1876 					const tcu::IVec2	fragPos			= diamonds[packetNdx].position;
   1877 					const int			majorPos		= (isXMajor) ? (fragPos.x()) : (fragPos.y());
   1878 					const int			rootPos			= (isXMajor) ? (fragPos.y()) : (fragPos.x());
   1879 					const deUint32		packed			= (deUint32)((deUint16)((deInt16)rootPos));
   1880 
   1881 					// infinite line will generate some diamonds outside the viewport
   1882 					if (deInBounds32(majorPos, 0, majorSize))
   1883 					{
   1884 						DE_ASSERT((rootPixelLocation[lineNdx][majorPos] & FLAG_ROOT_NOT_SET) != 0u);
   1885 						rootPixelLocation[lineNdx][majorPos] = packed;
   1886 					}
   1887 				}
   1888 			}
   1889 
   1890 			// Filled whole lookup table
   1891 			for (int majorPos = 0; majorPos < majorSize; ++majorPos)
   1892 				DE_ASSERT((rootPixelLocation[lineNdx][majorPos] & FLAG_ROOT_NOT_SET) == 0u);
   1893 		}
   1894 	}
   1895 
   1896 	// Find all possible lines with coverage, check pixel color matches one of them
   1897 
   1898 	for (int y = 1; y < surface.getHeight() - 1; ++y)
   1899 	for (int x = 1; x < surface.getWidth()  - 1; ++x)
   1900 	{
   1901 		const tcu::RGBA		color					= surface.getPixel(x, y);
   1902 		const tcu::IVec3	pixelNativeColor		= convertRGB8ToNativeFormat(color, args);	// Convert pixel color from rgba8 to the real pixel format. Usually rgba8 or 565
   1903 		int					lineCoverageSet			= 0;										// !< lines that may cover this fragment
   1904 		int					lineSurroundingCoverage = 0xFFFF;									// !< lines that will cover this fragment
   1905 		bool				matchFound				= false;
   1906 		const tcu::IVec3	formatLimit				((1 << args.redBits) - 1, (1 << args.greenBits) - 1, (1 << args.blueBits) - 1);
   1907 
   1908 		std::vector<SingleSampleWideLineCandidate> candidates;
   1909 
   1910 		// Find lines with possible coverage
   1911 
   1912 		for (int dy = -1; dy < 2; ++dy)
   1913 		for (int dx = -1; dx < 2; ++dx)
   1914 		{
   1915 			const int coverage = referenceLineMap.getAccess().getPixelInt(x+dx, y+dy).x();
   1916 
   1917 			lineCoverageSet			|= coverage;
   1918 			lineSurroundingCoverage	&= coverage;
   1919 		}
   1920 
   1921 		// background color is possible?
   1922 		if (lineSurroundingCoverage == 0 && compareColors(color, tcu::RGBA::black(), args.redBits, args.greenBits, args.blueBits))
   1923 			continue;
   1924 
   1925 		// Check those lines
   1926 
   1927 		for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
   1928 		{
   1929 			if (((lineCoverageSet >> lineNdx) & 0x01) != 0)
   1930 			{
   1931 				const float						wa				= scene.lines[lineNdx].positions[0].w();
   1932 				const float						wb				= scene.lines[lineNdx].positions[1].w();
   1933 				const tcu::Vec2					pa				= effectiveLines[lineNdx].swizzle(0, 1);
   1934 				const tcu::Vec2					pb				= effectiveLines[lineNdx].swizzle(2, 3);
   1935 
   1936 				// \note Wide line fragments are generated by replicating the root fragment for each
   1937 				//       fragment column (row for y-major). Calculate interpolation at the root
   1938 				//       fragment.
   1939 				const bool						isXMajor		= lineIsXMajor[lineNdx];
   1940 				const int						majorPosition	= (isXMajor) ? (x) : (y);
   1941 				const deUint32					minorInfoPacked	= rootPixelLocation[lineNdx][majorPosition];
   1942 				const int						minorPosition	= (int)((deInt16)((deUint16)(minorInfoPacked & 0xFFFFu)));
   1943 				const tcu::IVec2				idealRootPos	= (isXMajor) ? (tcu::IVec2(majorPosition, minorPosition)) : (tcu::IVec2(minorPosition, majorPosition));
   1944 				const tcu::IVec2				minorDirection	= (isXMajor) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0));
   1945 
   1946 				SingleSampleWideLineCandidate	candidate;
   1947 
   1948 				candidate.lineNdx		= lineNdx;
   1949 				candidate.numCandidates	= 0;
   1950 				DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(candidate.interpolationCandidates) == 3);
   1951 
   1952 				// Interpolation happens at the root fragment, which is then replicated in minor
   1953 				// direction. Search for implementation's root position near accurate root.
   1954 				for (int minorOffset = -1; minorOffset < 2; ++minorOffset)
   1955 				{
   1956 					const tcu::IVec2				rootPosition	= idealRootPos + minorOffset * minorDirection;
   1957 
   1958 					// A fragment can be root fragment only if it exists
   1959 					// \note root fragment can "exist" outside viewport
   1960 					// \note no pixel format theshold since in this case allowing only black is more conservative
   1961 					if (deInBounds32(rootPosition.x(), 0, surface.getWidth()) &&
   1962 						deInBounds32(rootPosition.y(), 0, surface.getHeight()) &&
   1963 						isBlack(surface.getPixel(rootPosition.x(), rootPosition.y())))
   1964 					{
   1965 						continue;
   1966 					}
   1967 
   1968 					const LineInterpolationRange	range			= calcSingleSampleLineInterpolationRange(pa, wa, pb, wb, rootPosition, args.subpixelBits);
   1969 
   1970 					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];
   1971 					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];
   1972 
   1973 					const tcu::Vec3					colorMinF		(de::clamp(valueMin.x() * (float)formatLimit.x(), 0.0f, (float)formatLimit.x()),
   1974 																	 de::clamp(valueMin.y() * (float)formatLimit.y(), 0.0f, (float)formatLimit.y()),
   1975 																	 de::clamp(valueMin.z() * (float)formatLimit.z(), 0.0f, (float)formatLimit.z()));
   1976 					const tcu::Vec3					colorMaxF		(de::clamp(valueMax.x() * (float)formatLimit.x(), 0.0f, (float)formatLimit.x()),
   1977 																	 de::clamp(valueMax.y() * (float)formatLimit.y(), 0.0f, (float)formatLimit.y()),
   1978 																	 de::clamp(valueMax.z() * (float)formatLimit.z(), 0.0f, (float)formatLimit.z()));
   1979 					const tcu::IVec3				colorMin		((int)deFloatFloor(colorMinF.x()),
   1980 																	 (int)deFloatFloor(colorMinF.y()),
   1981 																	 (int)deFloatFloor(colorMinF.z()));
   1982 					const tcu::IVec3				colorMax		((int)deFloatCeil (colorMaxF.x()),
   1983 																	 (int)deFloatCeil (colorMaxF.y()),
   1984 																	 (int)deFloatCeil (colorMaxF.z()));
   1985 
   1986 					// Verify validity
   1987 					if (pixelNativeColor.x() < colorMin.x() ||
   1988 						pixelNativeColor.y() < colorMin.y() ||
   1989 						pixelNativeColor.z() < colorMin.z() ||
   1990 						pixelNativeColor.x() > colorMax.x() ||
   1991 						pixelNativeColor.y() > colorMax.y() ||
   1992 						pixelNativeColor.z() > colorMax.z())
   1993 					{
   1994 						if (errorCount < errorFloodThreshold)
   1995 						{
   1996 							// Store candidate information for logging
   1997 							SingleSampleWideLineCandidate::InterpolationPointCandidate& interpolationCandidate = candidate.interpolationCandidates[candidate.numCandidates++];
   1998 							DE_ASSERT(candidate.numCandidates <= DE_LENGTH_OF_ARRAY(candidate.interpolationCandidates));
   1999 
   2000 							interpolationCandidate.interpolationPoint	= rootPosition;
   2001 							interpolationCandidate.colorMin				= colorMin;
   2002 							interpolationCandidate.colorMax				= colorMax;
   2003 							interpolationCandidate.colorMinF			= colorMinF;
   2004 							interpolationCandidate.colorMaxF			= colorMaxF;
   2005 							interpolationCandidate.valueRangeMin		= valueMin.swizzle(0, 1, 2);
   2006 							interpolationCandidate.valueRangeMax		= valueMax.swizzle(0, 1, 2);
   2007 						}
   2008 					}
   2009 					else
   2010 					{
   2011 						matchFound = true;
   2012 						break;
   2013 					}
   2014 				}
   2015 
   2016 				if (!matchFound)
   2017 				{
   2018 					// store info for logging
   2019 					if (errorCount < errorFloodThreshold && candidate.numCandidates > 0)
   2020 						candidates.push_back(candidate);
   2021 				}
   2022 				else
   2023 				{
   2024 					// no need to check other lines
   2025 					break;
   2026 				}
   2027 			}
   2028 		}
   2029 
   2030 		if (matchFound)
   2031 			continue;
   2032 
   2033 		// invalid fragment
   2034 		++invalidPixels;
   2035 		errorMask.setPixel(x, y, invalidPixelColor);
   2036 
   2037 		++errorCount;
   2038 
   2039 		// don't fill the logs with too much data
   2040 		if (errorCount < errorFloodThreshold)
   2041 		{
   2042 			tcu::MessageBuilder msg(&log);
   2043 
   2044 			msg	<< "Found an invalid pixel at (" << x << "," << y << "), " << (int)candidates.size() << " candidate reference value(s) found:\n"
   2045 				<< "\tPixel color:\t\t" << color << "\n"
   2046 				<< "\tNative color:\t\t" << pixelNativeColor << "\n";
   2047 
   2048 			for (int lineCandidateNdx = 0; lineCandidateNdx < (int)candidates.size(); ++lineCandidateNdx)
   2049 			{
   2050 				const SingleSampleWideLineCandidate& candidate = candidates[lineCandidateNdx];
   2051 
   2052 				msg << "\tCandidate line (line " << candidate.lineNdx << "):\n";
   2053 
   2054 				for (int interpolationCandidateNdx = 0; interpolationCandidateNdx < candidate.numCandidates; ++interpolationCandidateNdx)
   2055 				{
   2056 					const SingleSampleWideLineCandidate::InterpolationPointCandidate& interpolationCandidate = candidate.interpolationCandidates[interpolationCandidateNdx];
   2057 
   2058 					msg	<< "\t\tCandidate interpolation point (index " << interpolationCandidateNdx << "):\n"
   2059 						<< "\t\t\tRoot fragment position (non-replicated fragment): " << interpolationCandidate.interpolationPoint << ":\n"
   2060 						<< "\t\t\tReference native color min: " << tcu::clamp(interpolationCandidate.colorMin, tcu::IVec3(0,0,0), formatLimit) << "\n"
   2061 						<< "\t\t\tReference native color max: " << tcu::clamp(interpolationCandidate.colorMax, tcu::IVec3(0,0,0), formatLimit) << "\n"
   2062 						<< "\t\t\tReference native float min: " << tcu::clamp(interpolationCandidate.colorMinF, tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast<float>()) << "\n"
   2063 						<< "\t\t\tReference native float max: " << tcu::clamp(interpolationCandidate.colorMaxF, tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast<float>()) << "\n"
   2064 						<< "\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"
   2065 						<< "\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";
   2066 				}
   2067 			}
   2068 
   2069 			msg << tcu::TestLog::EndMessage;
   2070 		}
   2071 	}
   2072 
   2073 	// don't just hide failures
   2074 	if (errorCount > errorFloodThreshold)
   2075 		log << tcu::TestLog::Message << "Omitted " << (errorCount-errorFloodThreshold) << " pixel error description(s)." << tcu::TestLog::EndMessage;
   2076 
   2077 	// report result
   2078 	if (invalidPixels)
   2079 	{
   2080 		log << tcu::TestLog::Message << invalidPixels << " invalid pixel(s) found." << tcu::TestLog::EndMessage;
   2081 		log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
   2082 			<< tcu::TestLog::Image("Result", "Result",			surface)
   2083 			<< tcu::TestLog::Image("ErrorMask", "ErrorMask",	errorMask)
   2084 			<< tcu::TestLog::EndImageSet;
   2085 
   2086 		return false;
   2087 	}
   2088 	else
   2089 	{
   2090 		log << tcu::TestLog::Message << "No invalid pixels found." << tcu::TestLog::EndMessage;
   2091 		log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
   2092 			<< tcu::TestLog::Image("Result", "Result", surface)
   2093 			<< tcu::TestLog::EndImageSet;
   2094 
   2095 		return true;
   2096 	}
   2097 }
   2098 
   2099 } // anonymous
   2100 
   2101 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)
   2102 {
   2103 	typedef tcu::Vector<deInt64, 2> I64Vec2;
   2104 
   2105 	const deUint64		numSubPixels						= ((deUint64)1) << subpixelBits;
   2106 	const deUint64		pixelHitBoxSize						= (multisample) ? (numSubPixels) : (2+2);	//!< allow 4 central (2x2) for non-multisample pixels. Rounding may move edges 1 subpixel to any direction.
   2107 	const bool			order								= isTriangleClockwise(p0, p1, p2);			//!< clockwise / counter-clockwise
   2108 	const tcu::Vec4&	orderedP0							= p0;										//!< vertices of a clockwise triangle
   2109 	const tcu::Vec4&	orderedP1							= (order) ? (p1) : (p2);
   2110 	const tcu::Vec4&	orderedP2							= (order) ? (p2) : (p1);
   2111 	const tcu::Vec2		triangleNormalizedDeviceSpace[3]	=
   2112 	{
   2113 		tcu::Vec2(orderedP0.x() / orderedP0.w(), orderedP0.y() / orderedP0.w()),
   2114 		tcu::Vec2(orderedP1.x() / orderedP1.w(), orderedP1.y() / orderedP1.w()),
   2115 		tcu::Vec2(orderedP2.x() / orderedP2.w(), orderedP2.y() / orderedP2.w()),
   2116 	};
   2117 	const tcu::Vec2		triangleScreenSpace[3]				=
   2118 	{
   2119 		(triangleNormalizedDeviceSpace[0] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
   2120 		(triangleNormalizedDeviceSpace[1] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
   2121 		(triangleNormalizedDeviceSpace[2] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
   2122 	};
   2123 
   2124 	// Broad bounding box - pixel check
   2125 	{
   2126 		const float minX = de::min(de::min(triangleScreenSpace[0].x(), triangleScreenSpace[1].x()), triangleScreenSpace[2].x());
   2127 		const float minY = de::min(de::min(triangleScreenSpace[0].y(), triangleScreenSpace[1].y()), triangleScreenSpace[2].y());
   2128 		const float maxX = de::max(de::max(triangleScreenSpace[0].x(), triangleScreenSpace[1].x()), triangleScreenSpace[2].x());
   2129 		const float maxY = de::max(de::max(triangleScreenSpace[0].y(), triangleScreenSpace[1].y()), triangleScreenSpace[2].y());
   2130 
   2131 		if ((float)pixel.x() > maxX + 1 ||
   2132 			(float)pixel.y() > maxY + 1 ||
   2133 			(float)pixel.x() < minX - 1 ||
   2134 			(float)pixel.y() < minY - 1)
   2135 			return COVERAGE_NONE;
   2136 	}
   2137 
   2138 	// Broad triangle - pixel area intersection
   2139 	{
   2140 		const I64Vec2 pixelCenterPosition = I64Vec2(pixel.x(), pixel.y()) * I64Vec2(numSubPixels, numSubPixels) + I64Vec2(numSubPixels / 2, numSubPixels / 2);
   2141 		const I64Vec2 triangleSubPixelSpaceRound[3] =
   2142 		{
   2143 			I64Vec2(deRoundFloatToInt32(triangleScreenSpace[0].x() * (float)numSubPixels), deRoundFloatToInt32(triangleScreenSpace[0].y() * (float)numSubPixels)),
   2144 			I64Vec2(deRoundFloatToInt32(triangleScreenSpace[1].x() * (float)numSubPixels), deRoundFloatToInt32(triangleScreenSpace[1].y() * (float)numSubPixels)),
   2145 			I64Vec2(deRoundFloatToInt32(triangleScreenSpace[2].x() * (float)numSubPixels), deRoundFloatToInt32(triangleScreenSpace[2].y() * (float)numSubPixels)),
   2146 		};
   2147 
   2148 		// Check (using cross product) if pixel center is
   2149 		// a) too far from any edge
   2150 		// b) fully inside all edges
   2151 		bool insideAllEdges = true;
   2152 		for (int vtxNdx = 0; vtxNdx < 3; ++vtxNdx)
   2153 		{
   2154 			const int		otherVtxNdx				= (vtxNdx + 1) % 3;
   2155 			const deInt64	maxPixelDistanceSquared	= pixelHitBoxSize*pixelHitBoxSize; // Max distance from the pixel center from within the pixel is (sqrt(2) * boxWidth/2). Use 2x value for rounding tolerance
   2156 			const I64Vec2	edge					= triangleSubPixelSpaceRound[otherVtxNdx]	- triangleSubPixelSpaceRound[vtxNdx];
   2157 			const I64Vec2	v						= pixelCenterPosition						- triangleSubPixelSpaceRound[vtxNdx];
   2158 			const deInt64	crossProduct			= (edge.x() * v.y() - edge.y() * v.x());
   2159 
   2160 			// distance from edge: (edge x v) / |edge|
   2161 			//     (edge x v) / |edge| > maxPixelDistance
   2162 			// ==> (edge x v)^2 / edge^2 > maxPixelDistance^2    | edge x v > 0
   2163 			// ==> (edge x v)^2 > maxPixelDistance^2 * edge^2
   2164 			if (crossProduct < 0 && crossProduct*crossProduct > maxPixelDistanceSquared * tcu::lengthSquared(edge))
   2165 				return COVERAGE_NONE;
   2166 			if (crossProduct < 0 || crossProduct*crossProduct < maxPixelDistanceSquared * tcu::lengthSquared(edge))
   2167 				insideAllEdges = false;
   2168 		}
   2169 
   2170 		if (insideAllEdges)
   2171 			return COVERAGE_FULL;
   2172 	}
   2173 
   2174 	// Accurate intersection for edge pixels
   2175 	{
   2176 		//  In multisampling, the sample points can be anywhere in the pixel, and in single sampling only in the center.
   2177 		const I64Vec2 pixelCorners[4] =
   2178 		{
   2179 			I64Vec2((pixel.x()+0) * numSubPixels, (pixel.y()+0) * numSubPixels),
   2180 			I64Vec2((pixel.x()+1) * numSubPixels, (pixel.y()+0) * numSubPixels),
   2181 			I64Vec2((pixel.x()+1) * numSubPixels, (pixel.y()+1) * numSubPixels),
   2182 			I64Vec2((pixel.x()+0) * numSubPixels, (pixel.y()+1) * numSubPixels),
   2183 		};
   2184 		const I64Vec2 pixelCenterCorners[4] =
   2185 		{
   2186 			I64Vec2(pixel.x() * numSubPixels + numSubPixels/2 + 0, pixel.y() * numSubPixels + numSubPixels/2 + 0),
   2187 			I64Vec2(pixel.x() * numSubPixels + numSubPixels/2 + 1, pixel.y() * numSubPixels + numSubPixels/2 + 0),
   2188 			I64Vec2(pixel.x() * numSubPixels + numSubPixels/2 + 1, pixel.y() * numSubPixels + numSubPixels/2 + 1),
   2189 			I64Vec2(pixel.x() * numSubPixels + numSubPixels/2 + 0, pixel.y() * numSubPixels + numSubPixels/2 + 1),
   2190 		};
   2191 
   2192 		// both rounding directions
   2193 		const I64Vec2 triangleSubPixelSpaceFloor[3] =
   2194 		{
   2195 			I64Vec2(deFloorFloatToInt32(triangleScreenSpace[0].x() * (float)numSubPixels), deFloorFloatToInt32(triangleScreenSpace[0].y() * (float)numSubPixels)),
   2196 			I64Vec2(deFloorFloatToInt32(triangleScreenSpace[1].x() * (float)numSubPixels), deFloorFloatToInt32(triangleScreenSpace[1].y() * (float)numSubPixels)),
   2197 			I64Vec2(deFloorFloatToInt32(triangleScreenSpace[2].x() * (float)numSubPixels), deFloorFloatToInt32(triangleScreenSpace[2].y() * (float)numSubPixels)),
   2198 		};
   2199 		const I64Vec2 triangleSubPixelSpaceCeil[3] =
   2200 		{
   2201 			I64Vec2(deCeilFloatToInt32(triangleScreenSpace[0].x() * (float)numSubPixels), deCeilFloatToInt32(triangleScreenSpace[0].y() * (float)numSubPixels)),
   2202 			I64Vec2(deCeilFloatToInt32(triangleScreenSpace[1].x() * (float)numSubPixels), deCeilFloatToInt32(triangleScreenSpace[1].y() * (float)numSubPixels)),
   2203 			I64Vec2(deCeilFloatToInt32(triangleScreenSpace[2].x() * (float)numSubPixels), deCeilFloatToInt32(triangleScreenSpace[2].y() * (float)numSubPixels)),
   2204 		};
   2205 		const I64Vec2* const corners = (multisample) ? (pixelCorners) : (pixelCenterCorners);
   2206 
   2207 		// Test if any edge (with any rounding) intersects the pixel (boundary). If it does => Partial. If not => fully inside or outside
   2208 
   2209 		for (int edgeNdx = 0; edgeNdx < 3; ++edgeNdx)
   2210 		for (int startRounding = 0; startRounding < 4; ++startRounding)
   2211 		for (int endRounding = 0; endRounding < 4; ++endRounding)
   2212 		{
   2213 			const int		nextEdgeNdx	= (edgeNdx+1) % 3;
   2214 			const I64Vec2	startPos	((startRounding&0x01)	? (triangleSubPixelSpaceFloor[edgeNdx].x())		: (triangleSubPixelSpaceCeil[edgeNdx].x()),		(startRounding&0x02)	? (triangleSubPixelSpaceFloor[edgeNdx].y())		: (triangleSubPixelSpaceCeil[edgeNdx].y()));
   2215 			const I64Vec2	endPos		((endRounding&0x01)		? (triangleSubPixelSpaceFloor[nextEdgeNdx].x())	: (triangleSubPixelSpaceCeil[nextEdgeNdx].x()),	(endRounding&0x02)		? (triangleSubPixelSpaceFloor[nextEdgeNdx].y())	: (triangleSubPixelSpaceCeil[nextEdgeNdx].y()));
   2216 
   2217 			for (int pixelEdgeNdx = 0; pixelEdgeNdx < 4; ++pixelEdgeNdx)
   2218 			{
   2219 				const int pixelEdgeEnd = (pixelEdgeNdx + 1) % 4;
   2220 
   2221 				if (lineLineIntersect(startPos, endPos, corners[pixelEdgeNdx], corners[pixelEdgeEnd]))
   2222 					return COVERAGE_PARTIAL;
   2223 			}
   2224 		}
   2225 
   2226 		// fully inside or outside
   2227 		for (int edgeNdx = 0; edgeNdx < 3; ++edgeNdx)
   2228 		{
   2229 			const int		nextEdgeNdx		= (edgeNdx+1) % 3;
   2230 			const I64Vec2&	startPos		= triangleSubPixelSpaceFloor[edgeNdx];
   2231 			const I64Vec2&	endPos			= triangleSubPixelSpaceFloor[nextEdgeNdx];
   2232 			const I64Vec2	edge			= endPos - startPos;
   2233 			const I64Vec2	v				= corners[0] - endPos;
   2234 			const deInt64	crossProduct	= (edge.x() * v.y() - edge.y() * v.x());
   2235 
   2236 			// a corner of the pixel is outside => "fully inside" option is impossible
   2237 			if (crossProduct < 0)
   2238 				return COVERAGE_NONE;
   2239 		}
   2240 
   2241 		return COVERAGE_FULL;
   2242 	}
   2243 }
   2244 
   2245 static void verifyTriangleGroupRasterizationLog (const tcu::Surface& surface, tcu::TestLog& log, VerifyTriangleGroupRasterizationLogStash& logStash)
   2246 {
   2247 	// Output results
   2248 	log << tcu::TestLog::Message << "Verifying rasterization result." << tcu::TestLog::EndMessage;
   2249 
   2250 	if (!logStash.result)
   2251 	{
   2252 		log << tcu::TestLog::Message << "Invalid pixels found:\n\t"
   2253 			<< logStash.missingPixels << " missing pixels. (Marked with purple)\n\t"
   2254 			<< logStash.unexpectedPixels << " incorrectly filled pixels. (Marked with red)\n\t"
   2255 			<< "Unknown (subpixel on edge) pixels are marked with yellow."
   2256 			<< tcu::TestLog::EndMessage;
   2257 		log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
   2258 			<< tcu::TestLog::Image("Result",	"Result",		surface)
   2259 			<< tcu::TestLog::Image("ErrorMask", "ErrorMask",	logStash.errorMask)
   2260 			<< tcu::TestLog::EndImageSet;
   2261 	}
   2262 	else
   2263 	{
   2264 		log << tcu::TestLog::Message << "No invalid pixels found." << tcu::TestLog::EndMessage;
   2265 		log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
   2266 			<< tcu::TestLog::Image("Result", "Result", surface)
   2267 			<< tcu::TestLog::EndImageSet;
   2268 	}
   2269 }
   2270 
   2271 bool verifyTriangleGroupRasterization (const tcu::Surface& surface, const TriangleSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log, VerificationMode mode, VerifyTriangleGroupRasterizationLogStash* logStash)
   2272 {
   2273 	DE_ASSERT(mode < VERIFICATIONMODE_LAST);
   2274 
   2275 	const tcu::RGBA		backGroundColor				= tcu::RGBA(0, 0, 0, 255);
   2276 	const tcu::RGBA		triangleColor				= tcu::RGBA(255, 255, 255, 255);
   2277 	const tcu::RGBA		missingPixelColor			= tcu::RGBA(255, 0, 255, 255);
   2278 	const tcu::RGBA		unexpectedPixelColor		= tcu::RGBA(255, 0, 0, 255);
   2279 	const tcu::RGBA		partialPixelColor			= tcu::RGBA(255, 255, 0, 255);
   2280 	const tcu::RGBA		primitivePixelColor			= tcu::RGBA(30, 30, 30, 255);
   2281 	const int			weakVerificationThreshold	= 10;
   2282 	const bool			multisampled				= (args.numSamples != 0);
   2283 	const tcu::IVec2	viewportSize				= tcu::IVec2(surface.getWidth(), surface.getHeight());
   2284 	int					missingPixels				= 0;
   2285 	int					unexpectedPixels			= 0;
   2286 	int					subPixelBits				= args.subpixelBits;
   2287 	tcu::TextureLevel	coverageMap					(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8), surface.getWidth(), surface.getHeight());
   2288 	tcu::Surface		errorMask					(surface.getWidth(), surface.getHeight());
   2289 	bool				result						= false;
   2290 
   2291 	// subpixel bits in in a valid range?
   2292 
   2293 	if (subPixelBits < 0)
   2294 	{
   2295 		log << tcu::TestLog::Message << "Invalid subpixel count (" << subPixelBits << "), assuming 0" << tcu::TestLog::EndMessage;
   2296 		subPixelBits = 0;
   2297 	}
   2298 	else if (subPixelBits > 16)
   2299 	{
   2300 		// At high subpixel bit counts we might overflow. Checking at lower bit count is ok, but is less strict
   2301 		log << tcu::TestLog::Message << "Subpixel count is greater than 16 (" << subPixelBits << "). Checking results using less strict 16 bit requirements. This may produce false positives." << tcu::TestLog::EndMessage;
   2302 		subPixelBits = 16;
   2303 	}
   2304 
   2305 	// generate coverage map
   2306 
   2307 	tcu::clear(coverageMap.getAccess(), tcu::IVec4(COVERAGE_NONE, 0, 0, 0));
   2308 
   2309 	for (int triNdx = 0; triNdx < (int)scene.triangles.size(); ++triNdx)
   2310 	{
   2311 		const tcu::IVec4 aabb = getTriangleAABB(scene.triangles[triNdx], viewportSize);
   2312 
   2313 		for (int y = de::max(0, aabb.y()); y <= de::min(aabb.w(), coverageMap.getHeight() - 1); ++y)
   2314 		for (int x = de::max(0, aabb.x()); x <= de::min(aabb.z(), coverageMap.getWidth() - 1); ++x)
   2315 		{
   2316 			if (coverageMap.getAccess().getPixelUint(x, y).x() == COVERAGE_FULL)
   2317 				continue;
   2318 
   2319 			const CoverageType coverage = calculateTriangleCoverage(scene.triangles[triNdx].positions[0],
   2320 																	scene.triangles[triNdx].positions[1],
   2321 																	scene.triangles[triNdx].positions[2],
   2322 																	tcu::IVec2(x, y),
   2323 																	viewportSize,
   2324 																	subPixelBits,
   2325 																	multisampled);
   2326 
   2327 			if (coverage == COVERAGE_FULL)
   2328 			{
   2329 				coverageMap.getAccess().setPixel(tcu::IVec4(COVERAGE_FULL, 0, 0, 0), x, y);
   2330 			}
   2331 			else if (coverage == COVERAGE_PARTIAL)
   2332 			{
   2333 				CoverageType resultCoverage = COVERAGE_PARTIAL;
   2334 
   2335 				// Sharing an edge with another triangle?
   2336 				// There should always be such a triangle, but the pixel in the other triangle might be
   2337 				// on multiple edges, some of which are not shared. In these cases the coverage cannot be determined.
   2338 				// Assume full coverage if the pixel is only on a shared edge in shared triangle too.
   2339 				if (pixelOnlyOnASharedEdge(tcu::IVec2(x, y), scene.triangles[triNdx], viewportSize))
   2340 				{
   2341 					bool friendFound = false;
   2342 					for (int friendTriNdx = 0; friendTriNdx < (int)scene.triangles.size(); ++friendTriNdx)
   2343 					{
   2344 						if (friendTriNdx != triNdx && pixelOnlyOnASharedEdge(tcu::IVec2(x, y), scene.triangles[friendTriNdx], viewportSize))
   2345 						{
   2346 							friendFound = true;
   2347 							break;
   2348 						}
   2349 					}
   2350 
   2351 					if (friendFound)
   2352 						resultCoverage = COVERAGE_FULL;
   2353 				}
   2354 
   2355 				coverageMap.getAccess().setPixel(tcu::IVec4(resultCoverage, 0, 0, 0), x, y);
   2356 			}
   2357 		}
   2358 	}
   2359 
   2360 	// check pixels
   2361 
   2362 	tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
   2363 
   2364 	for (int y = 0; y < surface.getHeight(); ++y)
   2365 	for (int x = 0; x < surface.getWidth(); ++x)
   2366 	{
   2367 		const tcu::RGBA		color				= surface.getPixel(x, y);
   2368 		const bool			imageNoCoverage		= compareColors(color, backGroundColor, args.redBits, args.greenBits, args.blueBits);
   2369 		const bool			imageFullCoverage	= compareColors(color, triangleColor, args.redBits, args.greenBits, args.blueBits);
   2370 		CoverageType		referenceCoverage	= (CoverageType)coverageMap.getAccess().getPixelUint(x, y).x();
   2371 
   2372 		switch (referenceCoverage)
   2373 		{
   2374 			case COVERAGE_NONE:
   2375 				if (!imageNoCoverage)
   2376 				{
   2377 					// coverage where there should not be
   2378 					++unexpectedPixels;
   2379 					errorMask.setPixel(x, y, unexpectedPixelColor);
   2380 				}
   2381 				break;
   2382 
   2383 			case COVERAGE_PARTIAL:
   2384 				// anything goes
   2385 				errorMask.setPixel(x, y, partialPixelColor);
   2386 				break;
   2387 
   2388 			case COVERAGE_FULL:
   2389 				if (!imageFullCoverage)
   2390 				{
   2391 					// no coverage where there should be
   2392 					++missingPixels;
   2393 					errorMask.setPixel(x, y, missingPixelColor);
   2394 				}
   2395 				else
   2396 				{
   2397 					errorMask.setPixel(x, y, primitivePixelColor);
   2398 				}
   2399 				break;
   2400 
   2401 			default:
   2402 				DE_ASSERT(false);
   2403 		};
   2404 	}
   2405 
   2406 	if (((mode == VERIFICATIONMODE_STRICT) && (missingPixels + unexpectedPixels > 0)) ||
   2407 		((mode == VERIFICATIONMODE_WEAK)   && (missingPixels + unexpectedPixels > weakVerificationThreshold)))
   2408 	{
   2409 		result = false;
   2410 	}
   2411 	else
   2412 	{
   2413 		result = true;
   2414 	}
   2415 
   2416 	// Output or stash results
   2417 	{
   2418 		VerifyTriangleGroupRasterizationLogStash* tempLogStash = (logStash == DE_NULL) ? new VerifyTriangleGroupRasterizationLogStash : logStash;
   2419 
   2420 		tempLogStash->result			= result;
   2421 		tempLogStash->missingPixels		= missingPixels;
   2422 		tempLogStash->unexpectedPixels	= unexpectedPixels;
   2423 		tempLogStash->errorMask			= errorMask;
   2424 
   2425 		if (logStash == DE_NULL)
   2426 		{
   2427 			verifyTriangleGroupRasterizationLog(surface, log, *tempLogStash);
   2428 			delete tempLogStash;
   2429 		}
   2430 	}
   2431 
   2432 	return result;
   2433 }
   2434 
   2435 bool verifyLineGroupRasterization (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
   2436 {
   2437 	const bool multisampled = args.numSamples != 0;
   2438 
   2439 	if (multisampled)
   2440 		return verifyMultisampleLineGroupRasterization(surface, scene, args, log, CLIPMODE_NO_CLIPPING, DE_NULL);
   2441 	else
   2442 		return verifySinglesampleLineGroupRasterization(surface, scene, args, log);
   2443 }
   2444 
   2445 bool verifyClippedTriangulatedLineGroupRasterization (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
   2446 {
   2447 	return verifyMultisampleLineGroupRasterization(surface, scene, args, log, CLIPMODE_USE_CLIPPING_BOX, DE_NULL);
   2448 }
   2449 
   2450 bool verifyRelaxedLineGroupRasterization (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
   2451 {
   2452 	VerifyTriangleGroupRasterizationLogStash noClippingLogStash;
   2453 	VerifyTriangleGroupRasterizationLogStash useClippingLogStash;
   2454 
   2455 	if (verifyMultisampleLineGroupRasterization(surface, scene, args, log, CLIPMODE_USE_CLIPPING_BOX, &useClippingLogStash))
   2456 	{
   2457 		log << tcu::TestLog::Message << "Relaxed rasterization succeeded with CLIPMODE_USE_CLIPPING_BOX, details follow." << tcu::TestLog::EndMessage;
   2458 
   2459 		verifyTriangleGroupRasterizationLog(surface, log, useClippingLogStash);
   2460 
   2461 		return true;
   2462 	}
   2463 	else if (verifyMultisampleLineGroupRasterization(surface, scene, args, log, CLIPMODE_NO_CLIPPING, &noClippingLogStash))
   2464 	{
   2465 		log << tcu::TestLog::Message << "Relaxed rasterization succeeded with CLIPMODE_NO_CLIPPING, details follow." << tcu::TestLog::EndMessage;
   2466 
   2467 		verifyTriangleGroupRasterizationLog(surface, log, noClippingLogStash);
   2468 
   2469 		return true;
   2470 	}
   2471 	else
   2472 	{
   2473 		log << tcu::TestLog::Message << "Relaxed rasterization failed, details follow." << tcu::TestLog::EndMessage;
   2474 
   2475 		verifyTriangleGroupRasterizationLog(surface, log, useClippingLogStash);
   2476 		verifyTriangleGroupRasterizationLog(surface, log, noClippingLogStash);
   2477 
   2478 		return false;
   2479 	}
   2480 }
   2481 
   2482 bool verifyPointGroupRasterization (const tcu::Surface& surface, const PointSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
   2483 {
   2484 	// Splitting to triangles is a valid solution in multisampled cases and even in non-multisample cases too.
   2485 	return verifyMultisamplePointGroupRasterization(surface, scene, args, log);
   2486 }
   2487 
   2488 bool verifyTriangleGroupInterpolation (const tcu::Surface& surface, const TriangleSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
   2489 {
   2490 	return verifyTriangleGroupInterpolationWithInterpolator(surface, scene, args, log, TriangleInterpolator(scene));
   2491 }
   2492 
   2493 LineInterpolationMethod verifyLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
   2494 {
   2495 	const bool multisampled = args.numSamples != 0;
   2496 
   2497 	if (multisampled)
   2498 	{
   2499 		if (verifyMultisampleLineGroupInterpolation(surface, scene, args, log))
   2500 			return LINEINTERPOLATION_STRICTLY_CORRECT;
   2501 		return LINEINTERPOLATION_INCORRECT;
   2502 	}
   2503 	else
   2504 	{
   2505 		const bool isNarrow = (scene.lineWidth == 1.0f);
   2506 
   2507 		// accurate interpolation
   2508 		if (isNarrow)
   2509 		{
   2510 			if (verifySinglesampleNarrowLineGroupInterpolation(surface, scene, args, log))
   2511 				return LINEINTERPOLATION_STRICTLY_CORRECT;
   2512 		}
   2513 		else
   2514 		{
   2515 			if (verifySinglesampleWideLineGroupInterpolation(surface, scene, args, log))
   2516 				return LINEINTERPOLATION_STRICTLY_CORRECT;
   2517 		}
   2518 
   2519 		// check with projected (inaccurate) interpolation
   2520 		log << tcu::TestLog::Message << "Accurate verification failed, checking with projected weights (inaccurate equation)." << tcu::TestLog::EndMessage;
   2521 		if (verifyLineGroupInterpolationWithProjectedWeights(surface, scene, args, log))
   2522 			return LINEINTERPOLATION_PROJECTED;
   2523 
   2524 		return LINEINTERPOLATION_INCORRECT;
   2525 	}
   2526 }
   2527 
   2528 bool verifyTriangulatedLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
   2529 {
   2530 	return verifyMultisampleLineGroupInterpolation(surface, scene, args, log);
   2531 }
   2532 
   2533 } // tcu
   2534