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