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