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