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