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
37namespace tcu
38{
39namespace
40{
41
42bool 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
80bool 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
89bool 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
100bool 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
137bool 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
171float 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
180tcu::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
205float 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
213float 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
221float 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
229float 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
237float 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
246float 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
255tcu::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 *//*--------------------------------------------------------------------*/
283float 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 *//*--------------------------------------------------------------------*/
301float 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
315static 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
320static 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
328struct InterpolationRange
329{
330	tcu::Vec3 max;
331	tcu::Vec3 min;
332};
333
334struct LineInterpolationRange
335{
336	tcu::Vec2 max;
337	tcu::Vec2 min;
338};
339
340InterpolationRange 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
401LineInterpolationRange 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
452LineInterpolationRange 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
510template <typename WeightEquation>
511LineInterpolationRange 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
549LineInterpolationRange 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
554LineInterpolationRange 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
559struct 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 *//*--------------------------------------------------------------------*/
600struct 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
653template <typename Interpolator>
654bool 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
866float 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
876void 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
902enum ClipMode
903{
904	CLIPMODE_NO_CLIPPING = 0,
905	CLIPMODE_USE_CLIPPING_BOX,
906
907	CLIPMODE_LAST
908};
909
910bool 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
969bool 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
1038bool 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
1072void 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
1093bool 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
1474struct 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
1485void 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
1525void 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
1537bool 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
1724bool 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
1730bool 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
1735struct 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
1754tcu::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
1771bool isBlack (const tcu::RGBA& c)
1772{
1773	return c.getRed() == 0 && c.getGreen() == 0 && c.getBlue() == 0;
1774}
1775
1776bool 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
2101CoverageType 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
2245static 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
2271bool 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
2435bool 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
2445bool 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
2450bool 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
2482bool 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
2488bool 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
2493LineInterpolationMethod 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
2528bool 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