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
319struct InterpolationRange
320{
321	tcu::Vec3 max;
322	tcu::Vec3 min;
323};
324
325struct LineInterpolationRange
326{
327	tcu::Vec2 max;
328	tcu::Vec2 min;
329};
330
331InterpolationRange calcTriangleInterpolationWeights (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::Vec4& p2, const tcu::Vec2& ndpixel)
332{
333	const int roundError		= 1;
334	const int barycentricError	= 3;
335	const int divError			= 8;
336
337	const tcu::Vec2 nd0 = p0.swizzle(0, 1) / p0.w();
338	const tcu::Vec2 nd1 = p1.swizzle(0, 1) / p1.w();
339	const tcu::Vec2 nd2 = p2.swizzle(0, 1) / p2.w();
340
341	const float ka = triangleArea(ndpixel, nd1, nd2);
342	const float kb = triangleArea(ndpixel, nd2, nd0);
343	const float kc = triangleArea(ndpixel, nd0, nd1);
344
345	const float kaMax = getMaxFlushToZero(getMaxValueWithinError(ka, barycentricError));
346	const float kbMax = getMaxFlushToZero(getMaxValueWithinError(kb, barycentricError));
347	const float kcMax = getMaxFlushToZero(getMaxValueWithinError(kc, barycentricError));
348	const float kaMin = getMinFlushToZero(getMinValueWithinError(ka, barycentricError));
349	const float kbMin = getMinFlushToZero(getMinValueWithinError(kb, barycentricError));
350	const float kcMin = getMinFlushToZero(getMinValueWithinError(kc, barycentricError));
351	DE_ASSERT(kaMin <= kaMax);
352	DE_ASSERT(kbMin <= kbMax);
353	DE_ASSERT(kcMin <= kcMax);
354
355	// calculate weights: vec3(ka / p0.w, kb / p1.w, kc / p2.w) / (ka / p0.w + kb / p1.w + kc / p2.w)
356	const float maxPreDivisionValues[3] =
357	{
358		getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(kaMax / p0.w()), divError)),
359		getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(kbMax / p1.w()), divError)),
360		getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(kcMax / p2.w()), divError)),
361	};
362	const float minPreDivisionValues[3] =
363	{
364		getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(kaMin / p0.w()), divError)),
365		getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(kbMin / p1.w()), divError)),
366		getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(kcMin / p2.w()), divError)),
367	};
368	DE_ASSERT(minPreDivisionValues[0] <= maxPreDivisionValues[0]);
369	DE_ASSERT(minPreDivisionValues[1] <= maxPreDivisionValues[1]);
370	DE_ASSERT(minPreDivisionValues[2] <= maxPreDivisionValues[2]);
371
372	const float maxDivisor = getMaxFlushToZero(getMaxValueWithinError(maxPreDivisionValues[0] + maxPreDivisionValues[1] + maxPreDivisionValues[2], 2*roundError));
373	const float minDivisor = getMinFlushToZero(getMinValueWithinError(minPreDivisionValues[0] + minPreDivisionValues[1] + minPreDivisionValues[2], 2*roundError));
374	DE_ASSERT(minDivisor <= maxDivisor);
375
376	InterpolationRange returnValue;
377
378	returnValue.max.x() = getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(maximalRangeDivision(minPreDivisionValues[0], maxPreDivisionValues[0], minDivisor, maxDivisor)), divError));
379	returnValue.max.y() = getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(maximalRangeDivision(minPreDivisionValues[1], maxPreDivisionValues[1], minDivisor, maxDivisor)), divError));
380	returnValue.max.z() = getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(maximalRangeDivision(minPreDivisionValues[2], maxPreDivisionValues[2], minDivisor, maxDivisor)), divError));
381	returnValue.min.x() = getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(minimalRangeDivision(minPreDivisionValues[0], maxPreDivisionValues[0], minDivisor, maxDivisor)), divError));
382	returnValue.min.y() = getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(minimalRangeDivision(minPreDivisionValues[1], maxPreDivisionValues[1], minDivisor, maxDivisor)), divError));
383	returnValue.min.z() = getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(minimalRangeDivision(minPreDivisionValues[2], maxPreDivisionValues[2], minDivisor, maxDivisor)), divError));
384
385	DE_ASSERT(returnValue.min.x() <= returnValue.max.x());
386	DE_ASSERT(returnValue.min.y() <= returnValue.max.y());
387	DE_ASSERT(returnValue.min.z() <= returnValue.max.z());
388
389	return returnValue;
390}
391
392LineInterpolationRange calcSingleSampleLineInterpolationWeights (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::Vec2& ndpoint)
393{
394	const int divError = 3;
395
396	const tcu::Vec2 nd0 = p0.swizzle(0, 1) / p0.w();
397	const tcu::Vec2 nd1 = p1.swizzle(0, 1) / p1.w();
398
399	// project p to the line along the minor direction
400
401	const bool		xMajor		= (de::abs(nd0.x() - nd1.x()) >= de::abs(nd0.y() - nd1.y()));
402	const tcu::Vec2	minorDir	= (xMajor) ? (tcu::Vec2(0.0f, 1.0f)) : (tcu::Vec2(1.0f, 0.0f));
403	const tcu::Vec2	lineDir		(nd1 - nd0);
404	const tcu::Vec2	d			(ndpoint - nd0);
405
406	// calculate factors: vec2((1-t) / p0.w, t / p1.w) / ((1-t) / p0.w + t / p1.w)
407
408	const float		tFactorMax				= getMaxValueWithinError(-(1.0f / (minorDir.x()*lineDir.y() - lineDir.x()*minorDir.y())), divError);
409	const float		tFactorMin				= getMinValueWithinError(-(1.0f / (minorDir.x()*lineDir.y() - lineDir.x()*minorDir.y())), divError);
410	DE_ASSERT(tFactorMin <= tFactorMax);
411
412	const float		tResult1				= tFactorMax * (minorDir.y()*d.x() - minorDir.x()*d.y());
413	const float		tResult2				= tFactorMin * (minorDir.y()*d.x() - minorDir.x()*d.y());
414	const float		tMax					= de::max(tResult1, tResult2);
415	const float		tMin					= de::min(tResult1, tResult2);
416	DE_ASSERT(tMin <= tMax);
417
418	const float		perspectiveTMax			= getMaxValueWithinError(maximalRangeDivision(tMin, tMax, p1.w(), p1.w()), divError);
419	const float		perspectiveTMin			= getMinValueWithinError(minimalRangeDivision(tMin, tMax, p1.w(), p1.w()), divError);
420	DE_ASSERT(perspectiveTMin <= perspectiveTMax);
421
422	const float		perspectiveInvTMax		= getMaxValueWithinError(maximalRangeDivision((1.0f - tMax), (1.0f - tMin), p0.w(), p0.w()), divError);
423	const float		perspectiveInvTMin		= getMinValueWithinError(minimalRangeDivision((1.0f - tMax), (1.0f - tMin), p0.w(), p0.w()), divError);
424	DE_ASSERT(perspectiveInvTMin <= perspectiveInvTMax);
425
426	const float		perspectiveDivisorMax	= perspectiveTMax + perspectiveInvTMax;
427	const float		perspectiveDivisorMin	= perspectiveTMin + perspectiveInvTMin;
428	DE_ASSERT(perspectiveDivisorMin <= perspectiveDivisorMax);
429
430	LineInterpolationRange returnValue;
431	returnValue.max.x() = getMaxValueWithinError(maximalRangeDivision(perspectiveInvTMin,	perspectiveInvTMax,	perspectiveDivisorMin, perspectiveDivisorMax), divError);
432	returnValue.max.y() = getMaxValueWithinError(maximalRangeDivision(perspectiveTMin,		perspectiveTMax,	perspectiveDivisorMin, perspectiveDivisorMax), divError);
433	returnValue.min.x() = getMinValueWithinError(minimalRangeDivision(perspectiveInvTMin,	perspectiveInvTMax,	perspectiveDivisorMin, perspectiveDivisorMax), divError);
434	returnValue.min.y() = getMinValueWithinError(minimalRangeDivision(perspectiveTMin,		perspectiveTMax,	perspectiveDivisorMin, perspectiveDivisorMax), divError);
435
436	DE_ASSERT(returnValue.min.x() <= returnValue.max.x());
437	DE_ASSERT(returnValue.min.y() <= returnValue.max.y());
438
439	return returnValue;
440}
441
442LineInterpolationRange calcMultiSampleLineInterpolationWeights (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::Vec2& ndpoint)
443{
444	const int divError = 3;
445
446	// calc weights: vec2((1-t) / p0.w, t / p1.w) / ((1-t) / p0.w + t / p1.w)
447
448	// highp vertex shader
449	const tcu::Vec2 nd0 = p0.swizzle(0, 1) / p0.w();
450	const tcu::Vec2 nd1 = p1.swizzle(0, 1) / p1.w();
451
452	// Allow 1 ULP
453	const float		dividend	= tcu::dot(ndpoint - nd0, nd1 - nd0);
454	const float		dividendMax	= getMaxValueWithinError(dividend, 1);
455	const float		dividendMin	= getMaxValueWithinError(dividend, 1);
456	DE_ASSERT(dividendMin <= dividendMax);
457
458	// Assuming lengthSquared will not be implemented as sqrt(x)^2, allow 1 ULP
459	const float		divisor		= tcu::lengthSquared(nd1 - nd0);
460	const float		divisorMax	= getMaxValueWithinError(divisor, 1);
461	const float		divisorMin	= getMaxValueWithinError(divisor, 1);
462	DE_ASSERT(divisorMin <= divisorMax);
463
464	// Allow 3 ULP precision for division
465	const float		tMax		= getMaxValueWithinError(maximalRangeDivision(dividendMin, dividendMax, divisorMin, divisorMax), divError);
466	const float		tMin		= getMinValueWithinError(minimalRangeDivision(dividendMin, dividendMax, divisorMin, divisorMax), divError);
467	DE_ASSERT(tMin <= tMax);
468
469	const float		perspectiveTMax			= getMaxValueWithinError(maximalRangeDivision(tMin, tMax, p1.w(), p1.w()), divError);
470	const float		perspectiveTMin			= getMinValueWithinError(minimalRangeDivision(tMin, tMax, p1.w(), p1.w()), divError);
471	DE_ASSERT(perspectiveTMin <= perspectiveTMax);
472
473	const float		perspectiveInvTMax		= getMaxValueWithinError(maximalRangeDivision((1.0f - tMax), (1.0f - tMin), p0.w(), p0.w()), divError);
474	const float		perspectiveInvTMin		= getMinValueWithinError(minimalRangeDivision((1.0f - tMax), (1.0f - tMin), p0.w(), p0.w()), divError);
475	DE_ASSERT(perspectiveInvTMin <= perspectiveInvTMax);
476
477	const float		perspectiveDivisorMax	= perspectiveTMax + perspectiveInvTMax;
478	const float		perspectiveDivisorMin	= perspectiveTMin + perspectiveInvTMin;
479	DE_ASSERT(perspectiveDivisorMin <= perspectiveDivisorMax);
480
481	LineInterpolationRange returnValue;
482	returnValue.max.x() = getMaxValueWithinError(maximalRangeDivision(perspectiveInvTMin,	perspectiveInvTMax,	perspectiveDivisorMin, perspectiveDivisorMax), divError);
483	returnValue.max.y() = getMaxValueWithinError(maximalRangeDivision(perspectiveTMin,		perspectiveTMax,	perspectiveDivisorMin, perspectiveDivisorMax), divError);
484	returnValue.min.x() = getMinValueWithinError(minimalRangeDivision(perspectiveInvTMin,	perspectiveInvTMax,	perspectiveDivisorMin, perspectiveDivisorMax), divError);
485	returnValue.min.y() = getMinValueWithinError(minimalRangeDivision(perspectiveTMin,		perspectiveTMax,	perspectiveDivisorMin, perspectiveDivisorMax), divError);
486
487	DE_ASSERT(returnValue.min.x() <= returnValue.max.x());
488	DE_ASSERT(returnValue.min.y() <= returnValue.max.y());
489
490	return returnValue;
491}
492
493LineInterpolationRange calcSingleSampleLineInterpolationRange (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::IVec2& pixel, const tcu::IVec2& viewportSize, int subpixelBits)
494{
495	// allow interpolation weights anywhere in the central subpixels
496	const float testSquareSize = (2.0f / (1UL << subpixelBits));
497	const float testSquarePos  = (0.5f - testSquareSize / 2);
498	const tcu::Vec2 corners[4] =
499	{
500		tcu::Vec2((pixel.x() + testSquarePos + 0.0f)           / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + testSquarePos + 0.0f          ) / viewportSize.y() * 2.0f - 1.0f),
501		tcu::Vec2((pixel.x() + testSquarePos + 0.0f)           / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + testSquarePos + testSquareSize) / viewportSize.y() * 2.0f - 1.0f),
502		tcu::Vec2((pixel.x() + testSquarePos + testSquareSize) / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + testSquarePos + testSquareSize) / viewportSize.y() * 2.0f - 1.0f),
503		tcu::Vec2((pixel.x() + testSquarePos + testSquareSize) / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + testSquarePos + 0.0f          ) / viewportSize.y() * 2.0f - 1.0f),
504	};
505
506	// calculate interpolation as a line
507	const LineInterpolationRange weights[4] =
508	{
509		calcSingleSampleLineInterpolationWeights(p0, p1, corners[0]),
510		calcSingleSampleLineInterpolationWeights(p0, p1, corners[1]),
511		calcSingleSampleLineInterpolationWeights(p0, p1, corners[2]),
512		calcSingleSampleLineInterpolationWeights(p0, p1, corners[3]),
513	};
514
515	const tcu::Vec2 minWeights = tcu::min(tcu::min(weights[0].min, weights[1].min), tcu::min(weights[2].min, weights[3].min));
516	const tcu::Vec2 maxWeights = tcu::max(tcu::max(weights[0].max, weights[1].max), tcu::max(weights[2].max, weights[3].max));
517
518	// 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
519	LineInterpolationRange result;
520	result.min = minWeights;
521	result.max = maxWeights;
522	return result;
523}
524
525struct TriangleInterpolator
526{
527	const TriangleSceneSpec& scene;
528
529	TriangleInterpolator (const TriangleSceneSpec& scene_)
530		: scene(scene_)
531	{
532	}
533
534	InterpolationRange interpolate (int primitiveNdx, const tcu::IVec2 pixel, const tcu::IVec2 viewportSize, bool multisample, int subpixelBits) const
535	{
536		// allow anywhere in the pixel area in multisample
537		// allow only in the center subpixels (4 subpixels) in singlesample
538		const float testSquareSize = (multisample) ? (1.0f) : (2.0f / (1UL << subpixelBits));
539		const float testSquarePos  = (multisample) ? (0.0f) : (0.5f - testSquareSize / 2);
540		const tcu::Vec2 corners[4] =
541		{
542			tcu::Vec2((pixel.x() + testSquarePos + 0.0f)           / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + testSquarePos + 0.0f          ) / viewportSize.y() * 2.0f - 1.0f),
543			tcu::Vec2((pixel.x() + testSquarePos + 0.0f)           / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + testSquarePos + testSquareSize) / viewportSize.y() * 2.0f - 1.0f),
544			tcu::Vec2((pixel.x() + testSquarePos + testSquareSize) / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + testSquarePos + testSquareSize) / viewportSize.y() * 2.0f - 1.0f),
545			tcu::Vec2((pixel.x() + testSquarePos + testSquareSize) / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + testSquarePos + 0.0f          ) / viewportSize.y() * 2.0f - 1.0f),
546		};
547		const InterpolationRange weights[4] =
548		{
549			calcTriangleInterpolationWeights(scene.triangles[primitiveNdx].positions[0], scene.triangles[primitiveNdx].positions[1], scene.triangles[primitiveNdx].positions[2], corners[0]),
550			calcTriangleInterpolationWeights(scene.triangles[primitiveNdx].positions[0], scene.triangles[primitiveNdx].positions[1], scene.triangles[primitiveNdx].positions[2], corners[1]),
551			calcTriangleInterpolationWeights(scene.triangles[primitiveNdx].positions[0], scene.triangles[primitiveNdx].positions[1], scene.triangles[primitiveNdx].positions[2], corners[2]),
552			calcTriangleInterpolationWeights(scene.triangles[primitiveNdx].positions[0], scene.triangles[primitiveNdx].positions[1], scene.triangles[primitiveNdx].positions[2], corners[3]),
553		};
554
555		InterpolationRange result;
556		result.min = tcu::min(tcu::min(weights[0].min, weights[1].min), tcu::min(weights[2].min, weights[3].min));
557		result.max = tcu::max(tcu::max(weights[0].max, weights[1].max), tcu::max(weights[2].max, weights[3].max));
558		return result;
559	}
560};
561
562/*--------------------------------------------------------------------*//*!
563 * Used only by verifyMultisampleLineGroupInterpolation to calculate
564 * correct line interpolations for the triangulated lines.
565 *//*--------------------------------------------------------------------*/
566struct MultisampleLineInterpolator
567{
568	const LineSceneSpec& scene;
569
570	MultisampleLineInterpolator (const LineSceneSpec& scene_)
571		: scene(scene_)
572	{
573	}
574
575	InterpolationRange interpolate (int primitiveNdx, const tcu::IVec2 pixel, const tcu::IVec2 viewportSize, bool multisample, int subpixelBits) const
576	{
577		DE_ASSERT(multisample);
578		DE_UNREF(multisample);
579		DE_UNREF(subpixelBits);
580
581		// in triangulation, one line emits two triangles
582		const int		lineNdx		= primitiveNdx / 2;
583
584		// allow interpolation weights anywhere in the pixel
585		const tcu::Vec2 corners[4] =
586		{
587			tcu::Vec2((pixel.x() + 0.0f) / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + 0.0f) / viewportSize.y() * 2.0f - 1.0f),
588			tcu::Vec2((pixel.x() + 0.0f) / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + 1.0f) / viewportSize.y() * 2.0f - 1.0f),
589			tcu::Vec2((pixel.x() + 1.0f) / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + 1.0f) / viewportSize.y() * 2.0f - 1.0f),
590			tcu::Vec2((pixel.x() + 1.0f) / viewportSize.x() * 2.0f - 1.0f, (pixel.y() + 0.0f) / viewportSize.y() * 2.0f - 1.0f),
591		};
592
593		// calculate interpolation as a line
594		const LineInterpolationRange weights[4] =
595		{
596			calcMultiSampleLineInterpolationWeights(scene.lines[lineNdx].positions[0], scene.lines[lineNdx].positions[1], corners[0]),
597			calcMultiSampleLineInterpolationWeights(scene.lines[lineNdx].positions[0], scene.lines[lineNdx].positions[1], corners[1]),
598			calcMultiSampleLineInterpolationWeights(scene.lines[lineNdx].positions[0], scene.lines[lineNdx].positions[1], corners[2]),
599			calcMultiSampleLineInterpolationWeights(scene.lines[lineNdx].positions[0], scene.lines[lineNdx].positions[1], corners[3]),
600		};
601
602		const tcu::Vec2 minWeights = tcu::min(tcu::min(weights[0].min, weights[1].min), tcu::min(weights[2].min, weights[3].min));
603		const tcu::Vec2 maxWeights = tcu::max(tcu::max(weights[0].max, weights[1].max), tcu::max(weights[2].max, weights[3].max));
604
605		// 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
606		InterpolationRange result;
607		result.min = tcu::Vec3(minWeights.x(), 0.0f, minWeights.y());
608		result.max = tcu::Vec3(maxWeights.x(), 0.0f, maxWeights.y());
609		return result;
610	}
611};
612
613template <typename Interpolator>
614bool verifyTriangleGroupInterpolationWithInterpolator (const tcu::Surface& surface, const TriangleSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log, const Interpolator& interpolator)
615{
616	const tcu::RGBA		invalidPixelColor	= tcu::RGBA(255, 0, 0, 255);
617	const bool			multisampled		= (args.numSamples != 0);
618	const tcu::IVec2	viewportSize		= tcu::IVec2(surface.getWidth(), surface.getHeight());
619	const int			errorFloodThreshold	= 4;
620	int					errorCount			= 0;
621	int					invalidPixels		= 0;
622	int					subPixelBits		= args.subpixelBits;
623	tcu::Surface		errorMask			(surface.getWidth(), surface.getHeight());
624
625	tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
626
627	// log format
628
629	log << tcu::TestLog::Message << "Verifying rasterization result. Native format is RGB" << args.redBits << args.greenBits << args.blueBits << tcu::TestLog::EndMessage;
630	if (args.redBits > 8 || args.greenBits > 8 || args.blueBits > 8)
631		log << tcu::TestLog::Message << "Warning! More than 8 bits in a color channel, this may produce false negatives." << tcu::TestLog::EndMessage;
632
633	// subpixel bits in in a valid range?
634
635	if (subPixelBits < 0)
636	{
637		log << tcu::TestLog::Message << "Invalid subpixel count (" << subPixelBits << "), assuming 0" << tcu::TestLog::EndMessage;
638		subPixelBits = 0;
639	}
640	else if (subPixelBits > 16)
641	{
642		// At high subpixel bit counts we might overflow. Checking at lower bit count is ok, but is less strict
643		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;
644		subPixelBits = 16;
645	}
646
647	// check pixels
648
649	for (int y = 0; y < surface.getHeight(); ++y)
650	for (int x = 0; x < surface.getWidth();  ++x)
651	{
652		const tcu::RGBA		color				= surface.getPixel(x, y);
653		bool				stackBottomFound	= false;
654		int					stackSize			= 0;
655		tcu::Vec4			colorStackMin;
656		tcu::Vec4			colorStackMax;
657
658		// Iterate triangle coverage front to back, find the stack of pontentially contributing fragments
659		for (int triNdx = (int)scene.triangles.size() - 1; triNdx >= 0; --triNdx)
660		{
661			const CoverageType coverage = calculateTriangleCoverage(scene.triangles[triNdx].positions[0],
662																	scene.triangles[triNdx].positions[1],
663																	scene.triangles[triNdx].positions[2],
664																	tcu::IVec2(x, y),
665																	viewportSize,
666																	subPixelBits,
667																	multisampled);
668
669			if (coverage == COVERAGE_FULL || coverage == COVERAGE_PARTIAL)
670			{
671				// potentially contributes to the result fragment's value
672				const InterpolationRange weights = interpolator.interpolate(triNdx, tcu::IVec2(x, y), viewportSize, multisampled, subPixelBits);
673
674				const tcu::Vec4 fragmentColorMax =	de::clamp(weights.max.x(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[0] +
675													de::clamp(weights.max.y(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[1] +
676													de::clamp(weights.max.z(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[2];
677				const tcu::Vec4 fragmentColorMin =	de::clamp(weights.min.x(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[0] +
678													de::clamp(weights.min.y(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[1] +
679													de::clamp(weights.min.z(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[2];
680
681				if (stackSize++ == 0)
682				{
683					// first triangle, set the values properly
684					colorStackMin = fragmentColorMin;
685					colorStackMax = fragmentColorMax;
686				}
687				else
688				{
689					// contributing triangle
690					colorStackMin = tcu::min(colorStackMin, fragmentColorMin);
691					colorStackMax = tcu::max(colorStackMax, fragmentColorMax);
692				}
693
694				if (coverage == COVERAGE_FULL)
695				{
696					// loop terminates, this is the bottommost fragment
697					stackBottomFound = true;
698					break;
699				}
700			}
701		}
702
703		// Partial coverage == background may be visible
704		if (stackSize != 0 && !stackBottomFound)
705		{
706			stackSize++;
707			colorStackMin = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
708		}
709
710		// Is the result image color in the valid range.
711		if (stackSize == 0)
712		{
713			// No coverage, allow only background (black, value=0)
714			const tcu::IVec3	pixelNativeColor	= convertRGB8ToNativeFormat(color, args);
715			const int			threshold			= 1;
716
717			if (pixelNativeColor.x() > threshold ||
718				pixelNativeColor.y() > threshold ||
719				pixelNativeColor.z() > threshold)
720			{
721				++errorCount;
722
723				// don't fill the logs with too much data
724				if (errorCount < errorFloodThreshold)
725				{
726					log << tcu::TestLog::Message
727						<< "Found an invalid pixel at (" << x << "," << y << ")\n"
728						<< "\tPixel color:\t\t" << color << "\n"
729						<< "\tExpected background color.\n"
730						<< tcu::TestLog::EndMessage;
731				}
732
733				++invalidPixels;
734				errorMask.setPixel(x, y, invalidPixelColor);
735			}
736		}
737		else
738		{
739			DE_ASSERT(stackSize);
740
741			// Each additional step in the stack may cause conversion error of 1 bit due to undefined rounding direction
742			const int			thresholdRed	= stackSize - 1;
743			const int			thresholdGreen	= stackSize - 1;
744			const int			thresholdBlue	= stackSize - 1;
745
746			const tcu::Vec3		valueRangeMin	= tcu::Vec3(colorStackMin.xyz());
747			const tcu::Vec3		valueRangeMax	= tcu::Vec3(colorStackMax.xyz());
748
749			const tcu::IVec3	formatLimit		((1 << args.redBits) - 1, (1 << args.greenBits) - 1, (1 << args.blueBits) - 1);
750			const tcu::Vec3		colorMinF		(de::clamp(valueRangeMin.x() * formatLimit.x(), 0.0f, (float)formatLimit.x()),
751												 de::clamp(valueRangeMin.y() * formatLimit.y(), 0.0f, (float)formatLimit.y()),
752												 de::clamp(valueRangeMin.z() * formatLimit.z(), 0.0f, (float)formatLimit.z()));
753			const tcu::Vec3		colorMaxF		(de::clamp(valueRangeMax.x() * formatLimit.x(), 0.0f, (float)formatLimit.x()),
754												 de::clamp(valueRangeMax.y() * formatLimit.y(), 0.0f, (float)formatLimit.y()),
755												 de::clamp(valueRangeMax.z() * formatLimit.z(), 0.0f, (float)formatLimit.z()));
756			const tcu::IVec3	colorMin		((int)deFloatFloor(colorMinF.x()),
757												 (int)deFloatFloor(colorMinF.y()),
758												 (int)deFloatFloor(colorMinF.z()));
759			const tcu::IVec3	colorMax		((int)deFloatCeil (colorMaxF.x()),
760												 (int)deFloatCeil (colorMaxF.y()),
761												 (int)deFloatCeil (colorMaxF.z()));
762
763			// Convert pixel color from rgba8 to the real pixel format. Usually rgba8 or 565
764			const tcu::IVec3 pixelNativeColor = convertRGB8ToNativeFormat(color, args);
765
766			// Validity check
767			if (pixelNativeColor.x() < colorMin.x() - thresholdRed   ||
768				pixelNativeColor.y() < colorMin.y() - thresholdGreen ||
769				pixelNativeColor.z() < colorMin.z() - thresholdBlue  ||
770				pixelNativeColor.x() > colorMax.x() + thresholdRed   ||
771				pixelNativeColor.y() > colorMax.y() + thresholdGreen ||
772				pixelNativeColor.z() > colorMax.z() + thresholdBlue)
773			{
774				++errorCount;
775
776				// don't fill the logs with too much data
777				if (errorCount <= errorFloodThreshold)
778				{
779					log << tcu::TestLog::Message
780						<< "Found an invalid pixel at (" << x << "," << y << ")\n"
781						<< "\tPixel color:\t\t" << color << "\n"
782						<< "\tNative color:\t\t" << pixelNativeColor << "\n"
783						<< "\tAllowed error:\t\t" << tcu::IVec3(thresholdRed, thresholdGreen, thresholdBlue) << "\n"
784						<< "\tReference native color min: " << tcu::clamp(colorMin - tcu::IVec3(thresholdRed, thresholdGreen, thresholdBlue), tcu::IVec3(0,0,0), formatLimit) << "\n"
785						<< "\tReference native color max: " << tcu::clamp(colorMax + tcu::IVec3(thresholdRed, thresholdGreen, thresholdBlue), tcu::IVec3(0,0,0), formatLimit) << "\n"
786						<< "\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"
787						<< "\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"
788						<< "\tFmin:\t" << tcu::clamp(valueRangeMin, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f)) << "\n"
789						<< "\tFmax:\t" << tcu::clamp(valueRangeMax, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f)) << "\n"
790						<< tcu::TestLog::EndMessage;
791				}
792
793				++invalidPixels;
794				errorMask.setPixel(x, y, invalidPixelColor);
795			}
796		}
797	}
798
799	// don't just hide failures
800	if (errorCount > errorFloodThreshold)
801		log << tcu::TestLog::Message << "Omitted " << (errorCount-errorFloodThreshold) << " pixel error description(s)." << tcu::TestLog::EndMessage;
802
803	// report result
804	if (invalidPixels)
805	{
806		log << tcu::TestLog::Message << invalidPixels << " invalid pixel(s) found." << tcu::TestLog::EndMessage;
807		log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
808			<< tcu::TestLog::Image("Result", "Result",			surface)
809			<< tcu::TestLog::Image("ErrorMask", "ErrorMask",	errorMask)
810			<< tcu::TestLog::EndImageSet;
811
812		return false;
813	}
814	else
815	{
816		log << tcu::TestLog::Message << "No invalid pixels found." << tcu::TestLog::EndMessage;
817		log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
818			<< tcu::TestLog::Image("Result", "Result", surface)
819			<< tcu::TestLog::EndImageSet;
820
821		return true;
822	}
823}
824
825bool verifyMultisampleLineGroupRasterization (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
826{
827	// Multisampled line == 2 triangles
828
829	const tcu::Vec2		viewportSize	= tcu::Vec2((float)surface.getWidth(), (float)surface.getHeight());
830	const float			halfLineWidth	= scene.lineWidth * 0.5f;
831	TriangleSceneSpec	triangleScene;
832
833	triangleScene.triangles.resize(2 * scene.lines.size());
834	for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
835	{
836		// Transform to screen space, add pixel offsets, convert back to normalized device space, and test as triangles
837		const tcu::Vec2 lineNormalizedDeviceSpace[2] =
838		{
839			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()),
840			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()),
841		};
842		const tcu::Vec2 lineScreenSpace[2] =
843		{
844			(lineNormalizedDeviceSpace[0] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * viewportSize,
845			(lineNormalizedDeviceSpace[1] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * viewportSize,
846		};
847
848		const tcu::Vec2 lineDir			= tcu::normalize(lineScreenSpace[1] - lineScreenSpace[0]);
849		const tcu::Vec2 lineNormalDir	= tcu::Vec2(lineDir.y(), -lineDir.x());
850
851		const tcu::Vec2 lineQuadScreenSpace[4] =
852		{
853			lineScreenSpace[0] + lineNormalDir * halfLineWidth,
854			lineScreenSpace[0] - lineNormalDir * halfLineWidth,
855			lineScreenSpace[1] - lineNormalDir * halfLineWidth,
856			lineScreenSpace[1] + lineNormalDir * halfLineWidth,
857		};
858		const tcu::Vec2 lineQuadNormalizedDeviceSpace[4] =
859		{
860			lineQuadScreenSpace[0] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
861			lineQuadScreenSpace[1] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
862			lineQuadScreenSpace[2] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
863			lineQuadScreenSpace[3] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
864		};
865
866		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;
867		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;
868		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;
869
870		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;
871		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;
872		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;
873	}
874
875	return verifyTriangleGroupRasterization(surface, triangleScene, args, log);
876}
877
878bool verifyMultisampleLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
879{
880	// Multisampled line == 2 triangles
881
882	const tcu::Vec2		viewportSize	= tcu::Vec2((float)surface.getWidth(), (float)surface.getHeight());
883	const float			halfLineWidth	= scene.lineWidth * 0.5f;
884	TriangleSceneSpec	triangleScene;
885
886	triangleScene.triangles.resize(2 * scene.lines.size());
887	for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
888	{
889		// Transform to screen space, add pixel offsets, convert back to normalized device space, and test as triangles
890		const tcu::Vec2 lineNormalizedDeviceSpace[2] =
891		{
892			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()),
893			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()),
894		};
895		const tcu::Vec2 lineScreenSpace[2] =
896		{
897			(lineNormalizedDeviceSpace[0] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * viewportSize,
898			(lineNormalizedDeviceSpace[1] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * viewportSize,
899		};
900
901		const tcu::Vec2 lineDir			= tcu::normalize(lineScreenSpace[1] - lineScreenSpace[0]);
902		const tcu::Vec2 lineNormalDir	= tcu::Vec2(lineDir.y(), -lineDir.x());
903
904		const tcu::Vec2 lineQuadScreenSpace[4] =
905		{
906			lineScreenSpace[0] + lineNormalDir * halfLineWidth,
907			lineScreenSpace[0] - lineNormalDir * halfLineWidth,
908			lineScreenSpace[1] - lineNormalDir * halfLineWidth,
909			lineScreenSpace[1] + lineNormalDir * halfLineWidth,
910		};
911		const tcu::Vec2 lineQuadNormalizedDeviceSpace[4] =
912		{
913			lineQuadScreenSpace[0] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
914			lineQuadScreenSpace[1] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
915			lineQuadScreenSpace[2] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
916			lineQuadScreenSpace[3] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
917		};
918
919		triangleScene.triangles[lineNdx*2 + 0].positions[0] = tcu::Vec4(lineQuadNormalizedDeviceSpace[0].x(), lineQuadNormalizedDeviceSpace[0].y(), 0.0f, 1.0f);
920		triangleScene.triangles[lineNdx*2 + 0].positions[1] = tcu::Vec4(lineQuadNormalizedDeviceSpace[1].x(), lineQuadNormalizedDeviceSpace[1].y(), 0.0f, 1.0f);
921		triangleScene.triangles[lineNdx*2 + 0].positions[2] = tcu::Vec4(lineQuadNormalizedDeviceSpace[2].x(), lineQuadNormalizedDeviceSpace[2].y(), 0.0f, 1.0f);
922
923		triangleScene.triangles[lineNdx*2 + 0].sharedEdge[0] = false;
924		triangleScene.triangles[lineNdx*2 + 0].sharedEdge[1] = false;
925		triangleScene.triangles[lineNdx*2 + 0].sharedEdge[2] = true;
926
927		triangleScene.triangles[lineNdx*2 + 0].colors[0] = scene.lines[lineNdx].colors[0];
928		triangleScene.triangles[lineNdx*2 + 0].colors[1] = scene.lines[lineNdx].colors[0];
929		triangleScene.triangles[lineNdx*2 + 0].colors[2] = scene.lines[lineNdx].colors[1];
930
931		triangleScene.triangles[lineNdx*2 + 1].positions[0] = tcu::Vec4(lineQuadNormalizedDeviceSpace[0].x(), lineQuadNormalizedDeviceSpace[0].y(), 0.0f, 1.0f);
932		triangleScene.triangles[lineNdx*2 + 1].positions[1] = tcu::Vec4(lineQuadNormalizedDeviceSpace[2].x(), lineQuadNormalizedDeviceSpace[2].y(), 0.0f, 1.0f);
933		triangleScene.triangles[lineNdx*2 + 1].positions[2] = tcu::Vec4(lineQuadNormalizedDeviceSpace[3].x(), lineQuadNormalizedDeviceSpace[3].y(), 0.0f, 1.0f);
934
935		triangleScene.triangles[lineNdx*2 + 1].sharedEdge[0] = true;
936		triangleScene.triangles[lineNdx*2 + 1].sharedEdge[1] = false;
937		triangleScene.triangles[lineNdx*2 + 1].sharedEdge[2] = false;
938
939		triangleScene.triangles[lineNdx*2 + 1].colors[0] = scene.lines[lineNdx].colors[0];
940		triangleScene.triangles[lineNdx*2 + 1].colors[1] = scene.lines[lineNdx].colors[1];
941		triangleScene.triangles[lineNdx*2 + 1].colors[2] = scene.lines[lineNdx].colors[1];
942	}
943
944	return verifyTriangleGroupInterpolationWithInterpolator(surface, triangleScene, args, log, MultisampleLineInterpolator(scene));
945}
946
947bool verifyMultisamplePointGroupRasterization (const tcu::Surface& surface, const PointSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
948{
949	// Multisampled point == 2 triangles
950
951	const tcu::Vec2		viewportSize	= tcu::Vec2((float)surface.getWidth(), (float)surface.getHeight());
952	TriangleSceneSpec	triangleScene;
953
954	triangleScene.triangles.resize(2 * scene.points.size());
955	for (int pointNdx = 0; pointNdx < (int)scene.points.size(); ++pointNdx)
956	{
957		// Transform to screen space, add pixel offsets, convert back to normalized device space, and test as triangles
958		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());
959		const tcu::Vec2	pointScreenSpace					= (pointNormalizedDeviceSpace + tcu::Vec2(1.0f, 1.0f)) * 0.5f * viewportSize;
960		const float		offset								= scene.points[pointNdx].pointSize * 0.5f;
961		const tcu::Vec2	lineQuadNormalizedDeviceSpace[4]	=
962		{
963			(pointScreenSpace + tcu::Vec2(-offset, -offset))/ viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
964			(pointScreenSpace + tcu::Vec2(-offset,  offset))/ viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
965			(pointScreenSpace + tcu::Vec2( offset,  offset))/ viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
966			(pointScreenSpace + tcu::Vec2( offset, -offset))/ viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
967		};
968
969		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;
970		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;
971		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;
972
973		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;
974		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;
975		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;
976	}
977
978	return verifyTriangleGroupRasterization(surface, triangleScene, args, log);
979}
980
981bool verifySinglesampleLineGroupRasterization (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
982{
983	DE_ASSERT(deFloatFrac(scene.lineWidth) != 0.5f); // rounding direction is not defined, disallow undefined cases
984	DE_ASSERT(scene.lines.size() < 255); // indices are stored as unsigned 8-bit ints
985
986	bool				allOK				= true;
987	bool				overdrawInReference	= false;
988	int					referenceFragments	= 0;
989	int					resultFragments		= 0;
990	int					lineWidth			= deFloorFloatToInt32(scene.lineWidth + 0.5f);
991	bool				imageShown			= false;
992	std::vector<bool>	lineIsXMajor		(scene.lines.size());
993
994	// 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
995	tcu::TextureLevel referenceLineMap(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8), surface.getWidth(), surface.getHeight());
996	tcu::clear(referenceLineMap.getAccess(), tcu::IVec4(0, 0, 0, 0));
997
998	for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
999	{
1000		rr::SingleSampleLineRasterizer rasterizer(tcu::IVec4(0, 0, surface.getWidth(), surface.getHeight()));
1001
1002		const tcu::Vec2 lineNormalizedDeviceSpace[2] =
1003		{
1004			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()),
1005			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()),
1006		};
1007		const tcu::Vec4 lineScreenSpace[2] =
1008		{
1009			tcu::Vec4((lineNormalizedDeviceSpace[0].x() + 1.0f) * 0.5f * (float)surface.getWidth(), (lineNormalizedDeviceSpace[0].y() + 1.0f) * 0.5f * (float)surface.getHeight(), 0.0f, 1.0f),
1010			tcu::Vec4((lineNormalizedDeviceSpace[1].x() + 1.0f) * 0.5f * (float)surface.getWidth(), (lineNormalizedDeviceSpace[1].y() + 1.0f) * 0.5f * (float)surface.getHeight(), 0.0f, 1.0f),
1011		};
1012
1013		rasterizer.init(lineScreenSpace[0], lineScreenSpace[1], scene.lineWidth);
1014
1015		// calculate majority of later use
1016		lineIsXMajor[lineNdx] = de::abs(lineScreenSpace[1].x() - lineScreenSpace[0].x()) >= de::abs(lineScreenSpace[1].y() - lineScreenSpace[0].y());
1017
1018		for (;;)
1019		{
1020			const int			maxPackets			= 32;
1021			int					numRasterized		= 0;
1022			rr::FragmentPacket	packets[maxPackets];
1023
1024			rasterizer.rasterize(packets, DE_NULL, maxPackets, numRasterized);
1025
1026			for (int packetNdx = 0; packetNdx < numRasterized; ++packetNdx)
1027			{
1028				for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
1029				{
1030					if ((deUint32)packets[packetNdx].coverage & (1 << fragNdx))
1031					{
1032						const tcu::IVec2 fragPos = packets[packetNdx].position + tcu::IVec2(fragNdx%2, fragNdx/2);
1033
1034						// Check for overdraw
1035						if (!overdrawInReference)
1036							overdrawInReference = referenceLineMap.getAccess().getPixelInt(fragPos.x(), fragPos.y()).x() != 0;
1037
1038						// Output pixel
1039						referenceLineMap.getAccess().setPixel(tcu::IVec4(lineNdx + 1, 0, 0, 0), fragPos.x(), fragPos.y());
1040					}
1041				}
1042			}
1043
1044			if (numRasterized != maxPackets)
1045				break;
1046		}
1047	}
1048
1049	// Requirement 1: The coordinates of a fragment produced by the algorithm may not deviate by more than one unit
1050	{
1051		tcu::Surface	errorMask			(surface.getWidth(), surface.getHeight());
1052		bool			missingFragments	= false;
1053
1054		tcu::clear(errorMask.getAccess(), tcu::IVec4(0, 255, 0, 255));
1055
1056		log << tcu::TestLog::Message << "Searching for deviating fragments." << tcu::TestLog::EndMessage;
1057
1058		for (int y = 0; y < referenceLineMap.getHeight(); ++y)
1059		for (int x = 0; x < referenceLineMap.getWidth(); ++x)
1060		{
1061			const bool reference	= referenceLineMap.getAccess().getPixelInt(x, y).x() != 0;
1062			const bool result		= compareColors(surface.getPixel(x, y), tcu::RGBA::white, args.redBits, args.greenBits, args.blueBits);
1063
1064			if (reference)
1065				++referenceFragments;
1066			if (result)
1067				++resultFragments;
1068
1069			if (reference == result)
1070				continue;
1071
1072			// Reference fragment here, matching result fragment must be nearby
1073			if (reference && !result)
1074			{
1075				bool foundFragment = false;
1076
1077				if (x == 0 || y == 0 || x == referenceLineMap.getWidth() - 1 || y == referenceLineMap.getHeight() -1)
1078				{
1079					// image boundary, missing fragment could be over the image edge
1080					foundFragment = true;
1081				}
1082
1083				// find nearby fragment
1084				for (int dy = -1; dy < 2 && !foundFragment; ++dy)
1085				for (int dx = -1; dx < 2 && !foundFragment; ++dx)
1086				{
1087					if (compareColors(surface.getPixel(x+dx, y+dy), tcu::RGBA::white, args.redBits, args.greenBits, args.blueBits))
1088						foundFragment = true;
1089				}
1090
1091				if (!foundFragment)
1092				{
1093					missingFragments = true;
1094					errorMask.setPixel(x, y, tcu::RGBA::red);
1095				}
1096			}
1097		}
1098
1099		if (missingFragments)
1100		{
1101			log << tcu::TestLog::Message << "Invalid deviation(s) found." << tcu::TestLog::EndMessage;
1102			log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
1103				<< tcu::TestLog::Image("Result", "Result",			surface)
1104				<< tcu::TestLog::Image("ErrorMask", "ErrorMask",	errorMask)
1105				<< tcu::TestLog::EndImageSet;
1106
1107			imageShown = true;
1108			allOK = false;
1109		}
1110		else
1111		{
1112			log << tcu::TestLog::Message << "No invalid deviations found." << tcu::TestLog::EndMessage;
1113		}
1114	}
1115
1116	// Requirement 2: The total number of fragments produced by the algorithm may differ from
1117	//                that produced by the diamond-exit rule by no more than one.
1118	{
1119		// Check is not valid if the primitives intersect or otherwise share same fragments
1120		if (!overdrawInReference)
1121		{
1122			int allowedDeviation = (int)scene.lines.size() * lineWidth; // one pixel per primitive in the major direction
1123
1124			log << tcu::TestLog::Message << "Verifying fragment counts:\n"
1125				<< "\tDiamond-exit rule: " << referenceFragments << " fragments.\n"
1126				<< "\tResult image: " << resultFragments << " fragments.\n"
1127				<< "\tAllowing deviation of " << allowedDeviation << " fragments.\n"
1128				<< tcu::TestLog::EndMessage;
1129
1130			if (deAbs32(referenceFragments - resultFragments) > allowedDeviation)
1131			{
1132				tcu::Surface reference(surface.getWidth(), surface.getHeight());
1133
1134				// show a helpful reference image
1135				tcu::clear(reference.getAccess(), tcu::IVec4(0, 0, 0, 255));
1136				for (int y = 0; y < surface.getHeight(); ++y)
1137				for (int x = 0; x < surface.getWidth(); ++x)
1138					if (referenceLineMap.getAccess().getPixelInt(x, y).x())
1139						reference.setPixel(x, y, tcu::RGBA::white);
1140
1141				log << tcu::TestLog::Message << "Invalid fragment count in result image." << tcu::TestLog::EndMessage;
1142				log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
1143					<< tcu::TestLog::Image("Reference", "Reference",	reference)
1144					<< tcu::TestLog::Image("Result", "Result",			surface)
1145					<< tcu::TestLog::EndImageSet;
1146
1147				allOK = false;
1148				imageShown = true;
1149			}
1150			else
1151			{
1152				log << tcu::TestLog::Message << "Fragment count is valid." << tcu::TestLog::EndMessage;
1153			}
1154		}
1155		else
1156		{
1157			log << tcu::TestLog::Message << "Overdraw in scene. Fragment count cannot be verified. Skipping fragment count checks." << tcu::TestLog::EndMessage;
1158		}
1159	}
1160
1161	// Requirement 3: Line width must be constant
1162	{
1163		bool invalidWidthFound = false;
1164
1165		log << tcu::TestLog::Message << "Verifying line widths of the x-major lines." << tcu::TestLog::EndMessage;
1166		for (int y = 1; y < referenceLineMap.getHeight() - 1; ++y)
1167		{
1168			bool	fullyVisibleLine		= false;
1169			bool	previousPixelUndefined	= false;
1170			int		currentLine				= 0;
1171			int		currentWidth			= 1;
1172
1173			for (int x = 1; x < referenceLineMap.getWidth() - 1; ++x)
1174			{
1175				const bool	result	= compareColors(surface.getPixel(x, y), tcu::RGBA::white, args.redBits, args.greenBits, args.blueBits);
1176				int			lineID	= 0;
1177
1178				// Which line does this fragment belong to?
1179
1180				if (result)
1181				{
1182					bool multipleNearbyLines = false;
1183
1184					for (int dy = -1; dy < 2; ++dy)
1185					for (int dx = -1; dx < 2; ++dx)
1186					{
1187						const int nearbyID = referenceLineMap.getAccess().getPixelInt(x+dx, y+dy).x();
1188						if (nearbyID)
1189						{
1190							if (lineID && lineID != nearbyID)
1191								multipleNearbyLines = true;
1192							lineID = nearbyID;
1193						}
1194					}
1195
1196					if (multipleNearbyLines)
1197					{
1198						// Another line is too close, don't try to calculate width here
1199						previousPixelUndefined = true;
1200						continue;
1201					}
1202				}
1203
1204				// Only line with id of lineID is nearby
1205
1206				if (previousPixelUndefined)
1207				{
1208					// The line might have been overdrawn or not
1209					currentLine = lineID;
1210					currentWidth = 1;
1211					fullyVisibleLine = false;
1212					previousPixelUndefined = false;
1213				}
1214				else if (lineID == currentLine)
1215				{
1216					// Current line continues
1217					++currentWidth;
1218				}
1219				else if (lineID > currentLine)
1220				{
1221					// Another line was drawn over or the line ends
1222					currentLine = lineID;
1223					currentWidth = 1;
1224					fullyVisibleLine = true;
1225				}
1226				else
1227				{
1228					// The line ends
1229					if (fullyVisibleLine && !lineIsXMajor[currentLine-1])
1230					{
1231						// check width
1232						if (currentWidth != lineWidth)
1233						{
1234							log << tcu::TestLog::Message << "\tInvalid line width at (" << x - currentWidth << ", " << y << ") - (" << x - 1 << ", " << y << "). Detected width of " << currentWidth << ", expected " << lineWidth << tcu::TestLog::EndMessage;
1235							invalidWidthFound = true;
1236						}
1237					}
1238
1239					currentLine = lineID;
1240					currentWidth = 1;
1241					fullyVisibleLine = false;
1242				}
1243			}
1244		}
1245
1246		log << tcu::TestLog::Message << "Verifying line widths of the y-major lines." << tcu::TestLog::EndMessage;
1247		for (int x = 1; x < referenceLineMap.getWidth() - 1; ++x)
1248		{
1249			bool	fullyVisibleLine		= false;
1250			bool	previousPixelUndefined	= false;
1251			int		currentLine				= 0;
1252			int		currentWidth			= 1;
1253
1254			for (int y = 1; y < referenceLineMap.getHeight() - 1; ++y)
1255			{
1256				const bool	result	= compareColors(surface.getPixel(x, y), tcu::RGBA::white, args.redBits, args.greenBits, args.blueBits);
1257				int			lineID	= 0;
1258
1259				// Which line does this fragment belong to?
1260
1261				if (result)
1262				{
1263					bool multipleNearbyLines = false;
1264
1265					for (int dy = -1; dy < 2; ++dy)
1266					for (int dx = -1; dx < 2; ++dx)
1267					{
1268						const int nearbyID = referenceLineMap.getAccess().getPixelInt(x+dx, y+dy).x();
1269						if (nearbyID)
1270						{
1271							if (lineID && lineID != nearbyID)
1272								multipleNearbyLines = true;
1273							lineID = nearbyID;
1274						}
1275					}
1276
1277					if (multipleNearbyLines)
1278					{
1279						// Another line is too close, don't try to calculate width here
1280						previousPixelUndefined = true;
1281						continue;
1282					}
1283				}
1284
1285				// Only line with id of lineID is nearby
1286
1287				if (previousPixelUndefined)
1288				{
1289					// The line might have been overdrawn or not
1290					currentLine = lineID;
1291					currentWidth = 1;
1292					fullyVisibleLine = false;
1293					previousPixelUndefined = false;
1294				}
1295				else if (lineID == currentLine)
1296				{
1297					// Current line continues
1298					++currentWidth;
1299				}
1300				else if (lineID > currentLine)
1301				{
1302					// Another line was drawn over or the line ends
1303					currentLine = lineID;
1304					currentWidth = 1;
1305					fullyVisibleLine = true;
1306				}
1307				else
1308				{
1309					// The line ends
1310					if (fullyVisibleLine && lineIsXMajor[currentLine-1])
1311					{
1312						// check width
1313						if (currentWidth != lineWidth)
1314						{
1315							log << tcu::TestLog::Message << "\tInvalid line width at (" << x << ", " << y - currentWidth << ") - (" << x  << ", " << y - 1 << "). Detected width of " << currentWidth << ", expected " << lineWidth << tcu::TestLog::EndMessage;
1316							invalidWidthFound = true;
1317						}
1318					}
1319
1320					currentLine = lineID;
1321					currentWidth = 1;
1322					fullyVisibleLine = false;
1323				}
1324			}
1325		}
1326
1327		if (invalidWidthFound)
1328		{
1329			log << tcu::TestLog::Message << "Invalid line width found, image is not valid." << tcu::TestLog::EndMessage;
1330			allOK = false;
1331		}
1332		else
1333		{
1334			log << tcu::TestLog::Message << "Line widths are valid." << tcu::TestLog::EndMessage;
1335		}
1336	}
1337
1338	//\todo [2013-10-24 jarkko].
1339	//Requirement 4. If two line segments share a common endpoint, and both segments are either
1340	//x-major (both left-to-right or both right-to-left) or y-major (both bottom-totop
1341	//or both top-to-bottom), then rasterizing both segments may not produce
1342	//duplicate fragments, nor may any fragments be omitted so as to interrupt
1343	//continuity of the connected segments.
1344
1345	if (!imageShown)
1346	{
1347		log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
1348			<< tcu::TestLog::Image("Result", "Result", surface)
1349			<< tcu::TestLog::EndImageSet;
1350	}
1351
1352	return allOK;
1353}
1354
1355struct SingleSampleLineCoverageCandidate
1356{
1357	int			lineNdx;
1358	tcu::IVec3	colorMin;
1359	tcu::IVec3	colorMax;
1360	tcu::Vec3	colorMinF;
1361	tcu::Vec3	colorMaxF;
1362	tcu::Vec3	valueRangeMin;
1363	tcu::Vec3	valueRangeMax;
1364};
1365
1366bool verifySinglesampleLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
1367{
1368	DE_ASSERT(deFloatFrac(scene.lineWidth) != 0.5f); // rounding direction is not defined, disallow undefined cases
1369	DE_ASSERT(scene.lines.size() < 8); // coverage indices are stored as bitmask in a unsigned 8-bit ints
1370
1371	const tcu::RGBA		invalidPixelColor	= tcu::RGBA(255, 0, 0, 255);
1372	const tcu::IVec2	viewportSize		= tcu::IVec2(surface.getWidth(), surface.getHeight());
1373	const int			errorFloodThreshold	= 4;
1374	int					errorCount			= 0;
1375	tcu::Surface		errorMask			(surface.getWidth(), surface.getHeight());
1376	int					invalidPixels		= 0;
1377
1378	// log format
1379
1380	log << tcu::TestLog::Message << "Verifying rasterization result. Native format is RGB" << args.redBits << args.greenBits << args.blueBits << tcu::TestLog::EndMessage;
1381	if (args.redBits > 8 || args.greenBits > 8 || args.blueBits > 8)
1382		log << tcu::TestLog::Message << "Warning! More than 8 bits in a color channel, this may produce false negatives." << tcu::TestLog::EndMessage;
1383
1384	// Reference renderer produces correct fragments using the diamond-exit-rule. Make 2D int array, store line coverage as a 8-bit bitfield
1385	// The map is used to find lines with potential coverage to a given pixel
1386	tcu::TextureLevel referenceLineMap(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8), surface.getWidth(), surface.getHeight());
1387	tcu::clear(referenceLineMap.getAccess(), tcu::IVec4(0, 0, 0, 0));
1388
1389	tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
1390
1391	for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
1392	{
1393		rr::SingleSampleLineRasterizer rasterizer(tcu::IVec4(0, 0, surface.getWidth(), surface.getHeight()));
1394
1395		const tcu::Vec2 lineNormalizedDeviceSpace[2] =
1396		{
1397			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()),
1398			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()),
1399		};
1400		const tcu::Vec4 lineScreenSpace[2] =
1401		{
1402			tcu::Vec4((lineNormalizedDeviceSpace[0].x() + 1.0f) * 0.5f * (float)surface.getWidth(), (lineNormalizedDeviceSpace[0].y() + 1.0f) * 0.5f * (float)surface.getHeight(), 0.0f, 1.0f),
1403			tcu::Vec4((lineNormalizedDeviceSpace[1].x() + 1.0f) * 0.5f * (float)surface.getWidth(), (lineNormalizedDeviceSpace[1].y() + 1.0f) * 0.5f * (float)surface.getHeight(), 0.0f, 1.0f),
1404		};
1405
1406		rasterizer.init(lineScreenSpace[0], lineScreenSpace[1], scene.lineWidth);
1407
1408		// Calculate correct line coverage
1409		for (;;)
1410		{
1411			const int			maxPackets			= 32;
1412			int					numRasterized		= 0;
1413			rr::FragmentPacket	packets[maxPackets];
1414
1415			rasterizer.rasterize(packets, DE_NULL, maxPackets, numRasterized);
1416
1417			for (int packetNdx = 0; packetNdx < numRasterized; ++packetNdx)
1418			{
1419				for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
1420				{
1421					if ((deUint32)packets[packetNdx].coverage & (1 << fragNdx))
1422					{
1423						const tcu::IVec2	fragPos			= packets[packetNdx].position + tcu::IVec2(fragNdx%2, fragNdx/2);
1424						const int			previousMask	= referenceLineMap.getAccess().getPixelInt(fragPos.x(), fragPos.y()).x();
1425						const int			newMask			= (previousMask) | (1UL << lineNdx);
1426
1427						referenceLineMap.getAccess().setPixel(tcu::IVec4(newMask, 0, 0, 0), fragPos.x(), fragPos.y());
1428					}
1429				}
1430			}
1431
1432			if (numRasterized != maxPackets)
1433				break;
1434		}
1435	}
1436
1437	// Find all possible lines with coverage, check pixel color matches one of them
1438
1439	for (int y = 1; y < surface.getHeight() - 1; ++y)
1440	for (int x = 1; x < surface.getWidth()  - 1; ++x)
1441	{
1442		const tcu::RGBA		color					= surface.getPixel(x, y);
1443		const tcu::IVec3	pixelNativeColor		= convertRGB8ToNativeFormat(color, args);	// Convert pixel color from rgba8 to the real pixel format. Usually rgba8 or 565
1444		int					lineCoverageSet			= 0;										// !< lines that may cover this fragment
1445		int					lineSurroundingCoverage = 0xFFFF;									// !< lines that will cover this fragment
1446		bool				matchFound				= false;
1447		const tcu::IVec3	formatLimit				((1 << args.redBits) - 1, (1 << args.greenBits) - 1, (1 << args.blueBits) - 1);
1448
1449		std::vector<SingleSampleLineCoverageCandidate> candidates;
1450
1451		// Find lines with possible coverage
1452
1453		for (int dy = -1; dy < 2; ++dy)
1454		for (int dx = -1; dx < 2; ++dx)
1455		{
1456			const int coverage = referenceLineMap.getAccess().getPixelInt(x+dx, y+dy).x();
1457
1458			lineCoverageSet			|= coverage;
1459			lineSurroundingCoverage	&= coverage;
1460		}
1461
1462		// background color is possible?
1463		if (lineSurroundingCoverage == 0 && compareColors(color, tcu::RGBA::black, args.redBits, args.greenBits, args.blueBits))
1464			continue;
1465
1466		// Check those lines
1467
1468		for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
1469		{
1470			if (((lineCoverageSet >> lineNdx) & 0x01) != 0)
1471			{
1472				const LineInterpolationRange range = calcSingleSampleLineInterpolationRange(scene.lines[lineNdx].positions[0],
1473																							scene.lines[lineNdx].positions[1],
1474																							tcu::IVec2(x, y),
1475																							viewportSize,
1476																							args.subpixelBits);
1477
1478				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];
1479				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];
1480
1481				const tcu::Vec3		colorMinF		(de::clamp(valueMin.x() * formatLimit.x(), 0.0f, (float)formatLimit.x()),
1482													 de::clamp(valueMin.y() * formatLimit.y(), 0.0f, (float)formatLimit.y()),
1483													 de::clamp(valueMin.z() * formatLimit.z(), 0.0f, (float)formatLimit.z()));
1484				const tcu::Vec3		colorMaxF		(de::clamp(valueMax.x() * formatLimit.x(), 0.0f, (float)formatLimit.x()),
1485													 de::clamp(valueMax.y() * formatLimit.y(), 0.0f, (float)formatLimit.y()),
1486													 de::clamp(valueMax.z() * formatLimit.z(), 0.0f, (float)formatLimit.z()));
1487				const tcu::IVec3	colorMin		((int)deFloatFloor(colorMinF.x()),
1488													 (int)deFloatFloor(colorMinF.y()),
1489													 (int)deFloatFloor(colorMinF.z()));
1490				const tcu::IVec3	colorMax		((int)deFloatCeil (colorMaxF.x()),
1491													 (int)deFloatCeil (colorMaxF.y()),
1492													 (int)deFloatCeil (colorMaxF.z()));
1493
1494				// Verify validity
1495				if (pixelNativeColor.x() < colorMin.x() ||
1496					pixelNativeColor.y() < colorMin.y() ||
1497					pixelNativeColor.z() < colorMin.z() ||
1498					pixelNativeColor.x() > colorMax.x() ||
1499					pixelNativeColor.y() > colorMax.y() ||
1500					pixelNativeColor.z() > colorMax.z())
1501				{
1502					if (errorCount < errorFloodThreshold)
1503					{
1504						// Store candidate information for logging
1505						SingleSampleLineCoverageCandidate candidate;
1506
1507						candidate.lineNdx		= lineNdx;
1508						candidate.colorMin		= colorMin;
1509						candidate.colorMax		= colorMax;
1510						candidate.colorMinF		= colorMinF;
1511						candidate.colorMaxF		= colorMaxF;
1512						candidate.valueRangeMin	= valueMin.swizzle(0, 1, 2);
1513						candidate.valueRangeMax	= valueMax.swizzle(0, 1, 2);
1514
1515						candidates.push_back(candidate);
1516					}
1517				}
1518				else
1519				{
1520					matchFound = true;
1521					break;
1522				}
1523			}
1524		}
1525
1526		if (matchFound)
1527			continue;
1528
1529		// invalid fragment
1530		++invalidPixels;
1531		errorMask.setPixel(x, y, invalidPixelColor);
1532
1533		++errorCount;
1534
1535		// don't fill the logs with too much data
1536		if (errorCount < errorFloodThreshold)
1537		{
1538			log << tcu::TestLog::Message
1539				<< "Found an invalid pixel at (" << x << "," << y << "), " << (int)candidates.size() << " candidate reference value(s) found:\n"
1540				<< "\tPixel color:\t\t" << color << "\n"
1541				<< "\tNative color:\t\t" << pixelNativeColor << "\n"
1542				<< tcu::TestLog::EndMessage;
1543
1544			for (int candidateNdx = 0; candidateNdx < (int)candidates.size(); ++candidateNdx)
1545			{
1546				const SingleSampleLineCoverageCandidate& candidate = candidates[candidateNdx];
1547
1548				log << tcu::TestLog::Message << "\tCandidate (line " << candidate.lineNdx << "):\n"
1549					<< "\t\tReference native color min: " << tcu::clamp(candidate.colorMin, tcu::IVec3(0,0,0), formatLimit) << "\n"
1550					<< "\t\tReference native color max: " << tcu::clamp(candidate.colorMax, tcu::IVec3(0,0,0), formatLimit) << "\n"
1551					<< "\t\tReference native float min: " << tcu::clamp(candidate.colorMinF, tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast<float>()) << "\n"
1552					<< "\t\tReference native float max: " << tcu::clamp(candidate.colorMaxF, tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast<float>()) << "\n"
1553					<< "\t\tFmin:\t" << tcu::clamp(candidate.valueRangeMin, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f)) << "\n"
1554					<< "\t\tFmax:\t" << tcu::clamp(candidate.valueRangeMax, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f)) << "\n"
1555					<< tcu::TestLog::EndMessage;
1556			}
1557		}
1558	}
1559
1560	// don't just hide failures
1561	if (errorCount > errorFloodThreshold)
1562		log << tcu::TestLog::Message << "Omitted " << (errorCount-errorFloodThreshold) << " pixel error description(s)." << tcu::TestLog::EndMessage;
1563
1564	// report result
1565	if (invalidPixels)
1566	{
1567		log << tcu::TestLog::Message << invalidPixels << " invalid pixel(s) found." << tcu::TestLog::EndMessage;
1568		log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
1569			<< tcu::TestLog::Image("Result", "Result",			surface)
1570			<< tcu::TestLog::Image("ErrorMask", "ErrorMask",	errorMask)
1571			<< tcu::TestLog::EndImageSet;
1572
1573		return false;
1574	}
1575	else
1576	{
1577		log << tcu::TestLog::Message << "No invalid pixels found." << tcu::TestLog::EndMessage;
1578		log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
1579			<< tcu::TestLog::Image("Result", "Result", surface)
1580			<< tcu::TestLog::EndImageSet;
1581
1582		return true;
1583	}
1584}
1585
1586} // anonymous
1587
1588CoverageType 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)
1589{
1590	typedef tcu::Vector<deInt64, 2> I64Vec2;
1591
1592	const deUint64		numSubPixels						= ((deUint64)1) << subpixelBits;
1593	const deUint64		pixelHitBoxSize						= (multisample) ? (numSubPixels) : (2+2);	//!< allow 4 central (2x2) for non-multisample pixels. Rounding may move edges 1 subpixel to any direction.
1594	const bool			order								= isTriangleClockwise(p0, p1, p2);			//!< clockwise / counter-clockwise
1595	const tcu::Vec4&	orderedP0							= p0;										//!< vertices of a clockwise triangle
1596	const tcu::Vec4&	orderedP1							= (order) ? (p1) : (p2);
1597	const tcu::Vec4&	orderedP2							= (order) ? (p2) : (p1);
1598	const tcu::Vec2		triangleNormalizedDeviceSpace[3]	=
1599	{
1600		tcu::Vec2(orderedP0.x() / orderedP0.w(), orderedP0.y() / orderedP0.w()),
1601		tcu::Vec2(orderedP1.x() / orderedP1.w(), orderedP1.y() / orderedP1.w()),
1602		tcu::Vec2(orderedP2.x() / orderedP2.w(), orderedP2.y() / orderedP2.w()),
1603	};
1604	const tcu::Vec2		triangleScreenSpace[3]				=
1605	{
1606		(triangleNormalizedDeviceSpace[0] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
1607		(triangleNormalizedDeviceSpace[1] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
1608		(triangleNormalizedDeviceSpace[2] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
1609	};
1610
1611	// Broad bounding box - pixel check
1612	{
1613		const float minX = de::min(de::min(triangleScreenSpace[0].x(), triangleScreenSpace[1].x()), triangleScreenSpace[2].x());
1614		const float minY = de::min(de::min(triangleScreenSpace[0].y(), triangleScreenSpace[1].y()), triangleScreenSpace[2].y());
1615		const float maxX = de::max(de::max(triangleScreenSpace[0].x(), triangleScreenSpace[1].x()), triangleScreenSpace[2].x());
1616		const float maxY = de::max(de::max(triangleScreenSpace[0].y(), triangleScreenSpace[1].y()), triangleScreenSpace[2].y());
1617
1618		if ((float)pixel.x() > maxX + 1 ||
1619			(float)pixel.y() > maxY + 1 ||
1620			(float)pixel.x() < minX - 1 ||
1621			(float)pixel.y() < minY - 1)
1622			return COVERAGE_NONE;
1623	}
1624
1625	// Broad triangle - pixel area intersection
1626	{
1627		const I64Vec2 pixelCenterPosition = I64Vec2(pixel.x(), pixel.y()) * I64Vec2(numSubPixels, numSubPixels) + I64Vec2(numSubPixels / 2, numSubPixels / 2);
1628		const I64Vec2 triangleSubPixelSpaceRound[3] =
1629		{
1630			I64Vec2(deRoundFloatToInt32(triangleScreenSpace[0].x()*numSubPixels), deRoundFloatToInt32(triangleScreenSpace[0].y()*numSubPixels)),
1631			I64Vec2(deRoundFloatToInt32(triangleScreenSpace[1].x()*numSubPixels), deRoundFloatToInt32(triangleScreenSpace[1].y()*numSubPixels)),
1632			I64Vec2(deRoundFloatToInt32(triangleScreenSpace[2].x()*numSubPixels), deRoundFloatToInt32(triangleScreenSpace[2].y()*numSubPixels)),
1633		};
1634
1635		// Check (using cross product) if pixel center is
1636		// a) too far from any edge
1637		// b) fully inside all edges
1638		bool insideAllEdges = true;
1639		for (int vtxNdx = 0; vtxNdx < 3; ++vtxNdx)
1640		{
1641			const int		otherVtxNdx				= (vtxNdx + 1) % 3;
1642			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
1643			const I64Vec2	edge					= triangleSubPixelSpaceRound[otherVtxNdx]	- triangleSubPixelSpaceRound[vtxNdx];
1644			const I64Vec2	v						= pixelCenterPosition						- triangleSubPixelSpaceRound[vtxNdx];
1645			const deInt64	crossProduct			= (edge.x() * v.y() - edge.y() * v.x());
1646
1647			// distance from edge: (edge x v) / |edge|
1648			//     (edge x v) / |edge| > maxPixelDistance
1649			// ==> (edge x v)^2 / edge^2 > maxPixelDistance^2    | edge x v > 0
1650			// ==> (edge x v)^2 > maxPixelDistance^2 * edge^2
1651			if (crossProduct < 0 && crossProduct*crossProduct > maxPixelDistanceSquared * tcu::lengthSquared(edge))
1652				return COVERAGE_NONE;
1653			if (crossProduct < 0 || crossProduct*crossProduct < maxPixelDistanceSquared * tcu::lengthSquared(edge))
1654				insideAllEdges = false;
1655		}
1656
1657		if (insideAllEdges)
1658			return COVERAGE_FULL;
1659	}
1660
1661	// Accurate intersection for edge pixels
1662	{
1663		//  In multisampling, the sample points can be anywhere in the pixel, and in single sampling only in the center.
1664		const I64Vec2 pixelCorners[4] =
1665		{
1666			I64Vec2((pixel.x()+0) * numSubPixels, (pixel.y()+0) * numSubPixels),
1667			I64Vec2((pixel.x()+1) * numSubPixels, (pixel.y()+0) * numSubPixels),
1668			I64Vec2((pixel.x()+1) * numSubPixels, (pixel.y()+1) * numSubPixels),
1669			I64Vec2((pixel.x()+0) * numSubPixels, (pixel.y()+1) * numSubPixels),
1670		};
1671		const I64Vec2 pixelCenterCorners[4] =
1672		{
1673			I64Vec2(pixel.x() * numSubPixels + numSubPixels/2 + 0, pixel.y() * numSubPixels + numSubPixels/2 + 0),
1674			I64Vec2(pixel.x() * numSubPixels + numSubPixels/2 + 1, pixel.y() * numSubPixels + numSubPixels/2 + 0),
1675			I64Vec2(pixel.x() * numSubPixels + numSubPixels/2 + 1, pixel.y() * numSubPixels + numSubPixels/2 + 1),
1676			I64Vec2(pixel.x() * numSubPixels + numSubPixels/2 + 0, pixel.y() * numSubPixels + numSubPixels/2 + 1),
1677		};
1678
1679		// both rounding directions
1680		const I64Vec2 triangleSubPixelSpaceFloor[3] =
1681		{
1682			I64Vec2(deFloorFloatToInt32(triangleScreenSpace[0].x()*numSubPixels), deFloorFloatToInt32(triangleScreenSpace[0].y()*numSubPixels)),
1683			I64Vec2(deFloorFloatToInt32(triangleScreenSpace[1].x()*numSubPixels), deFloorFloatToInt32(triangleScreenSpace[1].y()*numSubPixels)),
1684			I64Vec2(deFloorFloatToInt32(triangleScreenSpace[2].x()*numSubPixels), deFloorFloatToInt32(triangleScreenSpace[2].y()*numSubPixels)),
1685		};
1686		const I64Vec2 triangleSubPixelSpaceCeil[3] =
1687		{
1688			I64Vec2(deCeilFloatToInt32(triangleScreenSpace[0].x()*numSubPixels), deCeilFloatToInt32(triangleScreenSpace[0].y()*numSubPixels)),
1689			I64Vec2(deCeilFloatToInt32(triangleScreenSpace[1].x()*numSubPixels), deCeilFloatToInt32(triangleScreenSpace[1].y()*numSubPixels)),
1690			I64Vec2(deCeilFloatToInt32(triangleScreenSpace[2].x()*numSubPixels), deCeilFloatToInt32(triangleScreenSpace[2].y()*numSubPixels)),
1691		};
1692		const I64Vec2* const corners = (multisample) ? (pixelCorners) : (pixelCenterCorners);
1693
1694		// Test if any edge (with any rounding) intersects the pixel (boundary). If it does => Partial. If not => fully inside or outside
1695
1696		for (int edgeNdx = 0; edgeNdx < 3; ++edgeNdx)
1697		for (int startRounding = 0; startRounding < 4; ++startRounding)
1698		for (int endRounding = 0; endRounding < 4; ++endRounding)
1699		{
1700			const int		nextEdgeNdx	= (edgeNdx+1) % 3;
1701			const I64Vec2	startPos	((startRounding&0x01)	? (triangleSubPixelSpaceFloor[edgeNdx].x())		: (triangleSubPixelSpaceCeil[edgeNdx].x()),		(startRounding&0x02)	? (triangleSubPixelSpaceFloor[edgeNdx].y())		: (triangleSubPixelSpaceCeil[edgeNdx].y()));
1702			const I64Vec2	endPos		((endRounding&0x01)		? (triangleSubPixelSpaceFloor[nextEdgeNdx].x())	: (triangleSubPixelSpaceCeil[nextEdgeNdx].x()),	(endRounding&0x02)		? (triangleSubPixelSpaceFloor[nextEdgeNdx].y())	: (triangleSubPixelSpaceCeil[nextEdgeNdx].y()));
1703			const I64Vec2	edge		= endPos - startPos;
1704
1705			for (int pixelEdgeNdx = 0; pixelEdgeNdx < 4; ++pixelEdgeNdx)
1706			{
1707				const int pixelEdgeEnd = (pixelEdgeNdx + 1) % 4;
1708
1709				if (lineLineIntersect(startPos, endPos, corners[pixelEdgeNdx], corners[pixelEdgeEnd]))
1710					return COVERAGE_PARTIAL;
1711			}
1712		}
1713
1714		// fully inside or outside
1715		for (int edgeNdx = 0; edgeNdx < 3; ++edgeNdx)
1716		{
1717			const int		nextEdgeNdx		= (edgeNdx+1) % 3;
1718			const I64Vec2&	startPos		= triangleSubPixelSpaceFloor[edgeNdx];
1719			const I64Vec2&	endPos			= triangleSubPixelSpaceFloor[nextEdgeNdx];
1720			const I64Vec2	edge			= endPos - startPos;
1721			const I64Vec2	v				= corners[0] - endPos;
1722			const deInt64	crossProduct	= (edge.x() * v.y() - edge.y() * v.x());
1723
1724			// a corner of the pixel is outside => "fully inside" option is impossible
1725			if (crossProduct < 0)
1726				return COVERAGE_NONE;
1727		}
1728
1729		return COVERAGE_FULL;
1730	}
1731}
1732
1733bool verifyTriangleGroupRasterization (const tcu::Surface& surface, const TriangleSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log, VerificationMode mode)
1734{
1735	DE_ASSERT(mode < VERIFICATIONMODE_LAST);
1736
1737	const tcu::RGBA		backGroundColor				= tcu::RGBA(0, 0, 0, 255);
1738	const tcu::RGBA		triangleColor				= tcu::RGBA(255, 255, 255, 255);
1739	const tcu::RGBA		missingPixelColor			= tcu::RGBA(255, 0, 255, 255);
1740	const tcu::RGBA		unexpectedPixelColor		= tcu::RGBA(255, 0, 0, 255);
1741	const tcu::RGBA		partialPixelColor			= tcu::RGBA(255, 255, 0, 255);
1742	const tcu::RGBA		primitivePixelColor			= tcu::RGBA(30, 30, 30, 255);
1743	const int			weakVerificationThreshold	= 10;
1744	const bool			multisampled				= (args.numSamples != 0);
1745	const tcu::IVec2	viewportSize				= tcu::IVec2(surface.getWidth(), surface.getHeight());
1746	int					missingPixels				= 0;
1747	int					unexpectedPixels			= 0;
1748	int					subPixelBits				= args.subpixelBits;
1749	tcu::TextureLevel	coverageMap					(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8), surface.getWidth(), surface.getHeight());
1750	tcu::Surface		errorMask					(surface.getWidth(), surface.getHeight());
1751
1752	// subpixel bits in in a valid range?
1753
1754	if (subPixelBits < 0)
1755	{
1756		log << tcu::TestLog::Message << "Invalid subpixel count (" << subPixelBits << "), assuming 0" << tcu::TestLog::EndMessage;
1757		subPixelBits = 0;
1758	}
1759	else if (subPixelBits > 16)
1760	{
1761		// At high subpixel bit counts we might overflow. Checking at lower bit count is ok, but is less strict
1762		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;
1763		subPixelBits = 16;
1764	}
1765
1766	// generate coverage map
1767
1768	tcu::clear(coverageMap.getAccess(), tcu::IVec4(COVERAGE_NONE, 0, 0, 0));
1769
1770	for (int triNdx = 0; triNdx < (int)scene.triangles.size(); ++triNdx)
1771	{
1772		const tcu::IVec4 aabb = getTriangleAABB(scene.triangles[triNdx], viewportSize);
1773
1774		for (int y = de::max(0, aabb.y()); y <= de::min(aabb.w(), coverageMap.getHeight() - 1); ++y)
1775		for (int x = de::max(0, aabb.x()); x <= de::min(aabb.z(), coverageMap.getWidth() - 1); ++x)
1776		{
1777			if (coverageMap.getAccess().getPixelUint(x, y).x() == COVERAGE_FULL)
1778				continue;
1779
1780			const CoverageType coverage = calculateTriangleCoverage(scene.triangles[triNdx].positions[0],
1781																	scene.triangles[triNdx].positions[1],
1782																	scene.triangles[triNdx].positions[2],
1783																	tcu::IVec2(x, y),
1784																	viewportSize,
1785																	subPixelBits,
1786																	multisampled);
1787
1788			if (coverage == COVERAGE_FULL)
1789			{
1790				coverageMap.getAccess().setPixel(tcu::IVec4(COVERAGE_FULL, 0, 0, 0), x, y);
1791			}
1792			else if (coverage == COVERAGE_PARTIAL)
1793			{
1794				CoverageType resultCoverage = COVERAGE_PARTIAL;
1795
1796				// Sharing an edge with another triangle?
1797				// There should always be such a triangle, but the pixel in the other triangle might be
1798				// on multiple edges, some of which are not shared. In these cases the coverage cannot be determined.
1799				// Assume full coverage if the pixel is only on a shared edge in shared triangle too.
1800				if (pixelOnlyOnASharedEdge(tcu::IVec2(x, y), scene.triangles[triNdx], viewportSize))
1801				{
1802					bool friendFound = false;
1803					for (int friendTriNdx = 0; friendTriNdx < (int)scene.triangles.size(); ++friendTriNdx)
1804					{
1805						if (friendTriNdx != triNdx && pixelOnlyOnASharedEdge(tcu::IVec2(x, y), scene.triangles[friendTriNdx], viewportSize))
1806						{
1807							friendFound = true;
1808							break;
1809						}
1810					}
1811
1812					if (friendFound)
1813						resultCoverage = COVERAGE_FULL;
1814				}
1815
1816				coverageMap.getAccess().setPixel(tcu::IVec4(resultCoverage, 0, 0, 0), x, y);
1817			}
1818		}
1819	}
1820
1821	// check pixels
1822
1823	tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
1824
1825	for (int y = 0; y < surface.getHeight(); ++y)
1826	for (int x = 0; x < surface.getWidth(); ++x)
1827	{
1828		const tcu::RGBA		color				= surface.getPixel(x, y);
1829		const bool			imageNoCoverage		= compareColors(color, backGroundColor, args.redBits, args.greenBits, args.blueBits);
1830		const bool			imageFullCoverage	= compareColors(color, triangleColor, args.redBits, args.greenBits, args.blueBits);
1831		CoverageType		referenceCoverage	= (CoverageType)coverageMap.getAccess().getPixelUint(x, y).x();
1832
1833		switch (referenceCoverage)
1834		{
1835			case COVERAGE_NONE:
1836				if (!imageNoCoverage)
1837				{
1838					// coverage where there should not be
1839					++unexpectedPixels;
1840					errorMask.setPixel(x, y, unexpectedPixelColor);
1841				}
1842				break;
1843
1844			case COVERAGE_PARTIAL:
1845				// anything goes
1846				errorMask.setPixel(x, y, partialPixelColor);
1847				break;
1848
1849			case COVERAGE_FULL:
1850				if (!imageFullCoverage)
1851				{
1852					// no coverage where there should be
1853					++missingPixels;
1854					errorMask.setPixel(x, y, missingPixelColor);
1855				}
1856				else
1857				{
1858					errorMask.setPixel(x, y, primitivePixelColor);
1859				}
1860				break;
1861
1862			default:
1863				DE_ASSERT(false);
1864		};
1865	}
1866
1867	// Output results
1868	log << tcu::TestLog::Message << "Verifying rasterization result." << tcu::TestLog::EndMessage;
1869
1870	if (((mode == VERIFICATIONMODE_STRICT) && (missingPixels + unexpectedPixels > 0)) ||
1871		((mode == VERIFICATIONMODE_WEAK)   && (missingPixels + unexpectedPixels > weakVerificationThreshold)))
1872	{
1873		log << tcu::TestLog::Message << "Invalid pixels found:\n\t"
1874			<< missingPixels << " missing pixels. (Marked with purple)\n\t"
1875			<< unexpectedPixels << " incorrectly filled pixels. (Marked with red)\n\t"
1876			<< "Unknown (subpixel on edge) pixels are marked with yellow."
1877			<< tcu::TestLog::EndMessage;
1878		log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
1879			<< tcu::TestLog::Image("Result",	"Result",		surface)
1880			<< tcu::TestLog::Image("ErrorMask", "ErrorMask",	errorMask)
1881			<< tcu::TestLog::EndImageSet;
1882
1883		return false;
1884	}
1885	else
1886	{
1887		log << tcu::TestLog::Message << "No invalid pixels found." << tcu::TestLog::EndMessage;
1888		log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
1889			<< tcu::TestLog::Image("Result", "Result", surface)
1890			<< tcu::TestLog::EndImageSet;
1891
1892		return true;
1893	}
1894}
1895
1896bool verifyLineGroupRasterization (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
1897{
1898	const bool multisampled = args.numSamples != 0;
1899
1900	if (multisampled)
1901		return verifyMultisampleLineGroupRasterization(surface, scene, args, log);
1902	else
1903		return verifySinglesampleLineGroupRasterization(surface, scene, args, log);
1904}
1905
1906bool verifyPointGroupRasterization (const tcu::Surface& surface, const PointSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
1907{
1908	// Splitting to triangles is a valid solution in multisampled cases and even in non-multisample cases too.
1909	return verifyMultisamplePointGroupRasterization(surface, scene, args, log);
1910}
1911
1912bool verifyTriangleGroupInterpolation (const tcu::Surface& surface, const TriangleSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
1913{
1914	return verifyTriangleGroupInterpolationWithInterpolator(surface, scene, args, log, TriangleInterpolator(scene));
1915}
1916
1917bool verifyLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
1918{
1919	const bool multisampled = args.numSamples != 0;
1920
1921	if (multisampled)
1922		return verifyMultisampleLineGroupInterpolation(surface, scene, args, log);
1923	else
1924		return verifySinglesampleLineGroupInterpolation(surface, scene, args, log);
1925}
1926
1927} // StateQueryUtil
1928} // gls
1929} // deqp
1930