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