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