xref: /aosp_15_r20/external/deqp/framework/common/tcuRasterizationVerifier.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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::__anone85a11dd0111::TriangleInterpolator628     TriangleInterpolator(const TriangleSceneSpec &scene_) : scene(scene_)
629     {
630     }
631 
interpolatetcu::__anone85a11dd0111::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::__anone85a11dd0111::MultisampleLineInterpolator679     MultisampleLineInterpolator(const LineSceneSpec &scene_) : scene(scene_)
680     {
681     }
682 
interpolatetcu::__anone85a11dd0111::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 != DE_NULL)
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(deFloatFrac(scene.lineWidth) != 0.5f); // rounding direction is not defined, disallow undefined cases
1494     DE_ASSERT(scene.lines.size() < 255);             // indices are stored as unsigned 8-bit ints
1495 
1496     bool allOK               = true;
1497     bool overdrawInReference = false;
1498     int referenceFragments   = 0;
1499     int resultFragments      = 0;
1500     int lineWidth            = deFloorFloatToInt32(scene.lineWidth + 0.5f);
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     genScreenSpaceLines(screenspaceLines, scene.lines, tcu::IVec2(surface.getWidth(), surface.getHeight()));
1510 
1511     rr::SingleSampleLineRasterizer rasterizer(tcu::IVec4(0, 0, surface.getWidth(), surface.getHeight()),
1512                                               args.subpixelBits);
1513     for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
1514     {
1515         rasterizer.init(tcu::Vec4(screenspaceLines[lineNdx][0], screenspaceLines[lineNdx][1], 0.0f, 1.0f),
1516                         tcu::Vec4(screenspaceLines[lineNdx][2], screenspaceLines[lineNdx][3], 0.0f, 1.0f),
1517                         scene.lineWidth, scene.stippleFactor, scene.stipplePattern);
1518 
1519         if (!scene.isStrip)
1520             rasterizer.resetStipple();
1521 
1522         // calculate majority of later use
1523         lineIsXMajor[lineNdx] = isPackedSSLineXMajor(screenspaceLines[lineNdx]);
1524 
1525         for (;;)
1526         {
1527             const int maxPackets = 32;
1528             int numRasterized    = 0;
1529             rr::FragmentPacket packets[maxPackets];
1530 
1531             rasterizer.rasterize(packets, DE_NULL, maxPackets, numRasterized);
1532 
1533             for (int packetNdx = 0; packetNdx < numRasterized; ++packetNdx)
1534             {
1535                 for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
1536                 {
1537                     if ((uint32_t)packets[packetNdx].coverage & (1 << fragNdx))
1538                     {
1539                         const tcu::IVec2 fragPos = packets[packetNdx].position + tcu::IVec2(fragNdx % 2, fragNdx / 2);
1540 
1541                         // Check for overdraw
1542                         if (!overdrawInReference)
1543                             overdrawInReference =
1544                                 referenceLineMap.getAccess().getPixelInt(fragPos.x(), fragPos.y()).x() != 0;
1545 
1546                         // Output pixel
1547                         referenceLineMap.getAccess().setPixel(tcu::IVec4(lineNdx + 1, 0, 0, 0), fragPos.x(),
1548                                                               fragPos.y());
1549                     }
1550                 }
1551             }
1552 
1553             if (numRasterized != maxPackets)
1554                 break;
1555         }
1556     }
1557 
1558     // Requirement 1: The coordinates of a fragment produced by the algorithm may not deviate by more than one unit
1559     {
1560         tcu::Surface errorMask(surface.getWidth(), surface.getHeight());
1561         bool missingFragments = false;
1562 
1563         tcu::clear(errorMask.getAccess(), tcu::IVec4(0, 255, 0, 255));
1564 
1565         log << tcu::TestLog::Message << "Searching for deviating fragments." << tcu::TestLog::EndMessage;
1566 
1567         for (int y = 0; y < referenceLineMap.getHeight(); ++y)
1568             for (int x = 0; x < referenceLineMap.getWidth(); ++x)
1569             {
1570                 const bool reference = referenceLineMap.getAccess().getPixelInt(x, y).x() != 0;
1571                 const bool result    = compareColors(surface.getPixel(x, y), tcu::RGBA::white(), args.redBits,
1572                                                      args.greenBits, args.blueBits);
1573 
1574                 if (reference)
1575                     ++referenceFragments;
1576                 if (result)
1577                     ++resultFragments;
1578 
1579                 if (reference == result)
1580                     continue;
1581 
1582                 // Reference fragment here, matching result fragment must be nearby
1583                 if (reference && !result)
1584                 {
1585                     bool foundFragment = false;
1586 
1587                     if (x == 0 || y == 0 || x == referenceLineMap.getWidth() - 1 ||
1588                         y == referenceLineMap.getHeight() - 1)
1589                     {
1590                         // image boundary, missing fragment could be over the image edge
1591                         foundFragment = true;
1592                     }
1593 
1594                     // find nearby fragment
1595                     for (int dy = -1; dy < 2 && !foundFragment; ++dy)
1596                         for (int dx = -1; dx < 2 && !foundFragment; ++dx)
1597                         {
1598                             if (compareColors(surface.getPixel(x + dx, y + dy), tcu::RGBA::white(), args.redBits,
1599                                               args.greenBits, args.blueBits))
1600                                 foundFragment = true;
1601                         }
1602 
1603                     if (!foundFragment)
1604                     {
1605                         missingFragments = true;
1606                         errorMask.setPixel(x, y, tcu::RGBA::red());
1607                     }
1608                 }
1609             }
1610 
1611         if (missingFragments)
1612         {
1613 
1614             allOK = false;
1615         }
1616         else
1617         {
1618             log << tcu::TestLog::Message << "No invalid deviations found." << tcu::TestLog::EndMessage;
1619         }
1620     }
1621 
1622     // Requirement 2: The total number of fragments produced by the algorithm may differ from
1623     //                that produced by the diamond-exit rule by no more than one.
1624     {
1625         // Check is not valid if the primitives intersect or otherwise share same fragments
1626         if (!overdrawInReference)
1627         {
1628             int allowedDeviation =
1629                 (int)scene.lines.size() * lineWidth; // one pixel per primitive in the major direction
1630 
1631             log << tcu::TestLog::Message << "Verifying fragment counts:\n"
1632                 << "\tDiamond-exit rule: " << referenceFragments << " fragments.\n"
1633                 << "\tResult image: " << resultFragments << " fragments.\n"
1634                 << "\tAllowing deviation of " << allowedDeviation << " fragments.\n"
1635                 << tcu::TestLog::EndMessage;
1636 
1637             if (deAbs32(referenceFragments - resultFragments) > allowedDeviation)
1638             {
1639                 tcu::Surface reference(surface.getWidth(), surface.getHeight());
1640 
1641                 // show a helpful reference image
1642                 tcu::clear(reference.getAccess(), tcu::IVec4(0, 0, 0, 255));
1643                 for (int y = 0; y < surface.getHeight(); ++y)
1644                     for (int x = 0; x < surface.getWidth(); ++x)
1645                         if (referenceLineMap.getAccess().getPixelInt(x, y).x())
1646                             reference.setPixel(x, y, tcu::RGBA::white());
1647 
1648                 log << tcu::TestLog::Message << "Invalid fragment count in result image." << tcu::TestLog::EndMessage;
1649                 log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
1650                     << tcu::TestLog::Image("Reference", "Reference", reference)
1651                     << tcu::TestLog::Image("Result", "Result", surface) << tcu::TestLog::EndImageSet;
1652 
1653                 allOK = false;
1654             }
1655             else
1656             {
1657                 log << tcu::TestLog::Message << "Fragment count is valid." << tcu::TestLog::EndMessage;
1658             }
1659         }
1660         else
1661         {
1662             log << tcu::TestLog::Message
1663                 << "Overdraw in scene. Fragment count cannot be verified. Skipping fragment count checks."
1664                 << tcu::TestLog::EndMessage;
1665         }
1666     }
1667 
1668     // Requirement 3: Line width must be constant
1669     {
1670         bool invalidWidthFound = false;
1671 
1672         log << tcu::TestLog::Message << "Verifying line widths of the x-major lines." << tcu::TestLog::EndMessage;
1673         for (int y = 1; y < referenceLineMap.getHeight() - 1; ++y)
1674         {
1675             bool fullyVisibleLine       = false;
1676             bool previousPixelUndefined = false;
1677             int currentLine             = 0;
1678             int currentWidth            = 1;
1679 
1680             for (int x = 1; x < referenceLineMap.getWidth() - 1; ++x)
1681             {
1682                 const bool result = compareColors(surface.getPixel(x, y), tcu::RGBA::white(), args.redBits,
1683                                                   args.greenBits, args.blueBits);
1684                 int lineID        = 0;
1685 
1686                 // Which line does this fragment belong to?
1687 
1688                 if (result)
1689                 {
1690                     bool multipleNearbyLines = false;
1691                     bool renderAtSurfaceEdge = false;
1692 
1693                     renderAtSurfaceEdge = (x == 1) || (x == referenceLineMap.getWidth() - 2);
1694 
1695                     for (int dy = -1; dy < 2; ++dy)
1696                         for (int dx = -1; dx < 2; ++dx)
1697                         {
1698                             const int nearbyID = referenceLineMap.getAccess().getPixelInt(x + dx, y + dy).x();
1699                             if (nearbyID)
1700                             {
1701                                 if (lineID && lineID != nearbyID)
1702                                     multipleNearbyLines = true;
1703                             }
1704                         }
1705 
1706                     if (multipleNearbyLines || renderAtSurfaceEdge)
1707                     {
1708                         // Another line is too close, don't try to calculate width here
1709                         // Or the render result is outside of surface range
1710                         previousPixelUndefined = true;
1711                         continue;
1712                     }
1713                 }
1714 
1715                 // Only line with id of lineID is nearby
1716 
1717                 if (previousPixelUndefined)
1718                 {
1719                     // The line might have been overdrawn or not
1720                     currentLine            = lineID;
1721                     currentWidth           = 1;
1722                     fullyVisibleLine       = false;
1723                     previousPixelUndefined = false;
1724                 }
1725                 else if (lineID == currentLine)
1726                 {
1727                     // Current line continues
1728                     ++currentWidth;
1729                 }
1730                 else if (lineID > currentLine)
1731                 {
1732                     // Another line was drawn over or the line ends
1733                     currentLine      = lineID;
1734                     currentWidth     = 1;
1735                     fullyVisibleLine = true;
1736                 }
1737                 else
1738                 {
1739                     // The line ends
1740                     if (fullyVisibleLine && !lineIsXMajor[currentLine - 1])
1741                     {
1742                         // check width
1743                         if (currentWidth != lineWidth)
1744                         {
1745                             log << tcu::TestLog::Message << "\tInvalid line width at (" << x - currentWidth << ", " << y
1746                                 << ") - (" << x - 1 << ", " << y << "). Detected width of " << currentWidth
1747                                 << ", expected " << lineWidth << tcu::TestLog::EndMessage;
1748                             invalidWidthFound = true;
1749                         }
1750                     }
1751 
1752                     currentLine      = lineID;
1753                     currentWidth     = 1;
1754                     fullyVisibleLine = false;
1755                 }
1756             }
1757         }
1758 
1759         log << tcu::TestLog::Message << "Verifying line widths of the y-major lines." << tcu::TestLog::EndMessage;
1760         for (int x = 1; x < referenceLineMap.getWidth() - 1; ++x)
1761         {
1762             bool fullyVisibleLine       = false;
1763             bool previousPixelUndefined = false;
1764             int currentLine             = 0;
1765             int currentWidth            = 1;
1766 
1767             for (int y = 1; y < referenceLineMap.getHeight() - 1; ++y)
1768             {
1769                 const bool result = compareColors(surface.getPixel(x, y), tcu::RGBA::white(), args.redBits,
1770                                                   args.greenBits, args.blueBits);
1771                 int lineID        = 0;
1772 
1773                 // Which line does this fragment belong to?
1774 
1775                 if (result)
1776                 {
1777                     bool multipleNearbyLines = false;
1778                     bool renderAtSurfaceEdge = false;
1779 
1780                     renderAtSurfaceEdge = (y == 1) || (y == referenceLineMap.getWidth() - 2);
1781 
1782                     for (int dy = -1; dy < 2; ++dy)
1783                         for (int dx = -1; dx < 2; ++dx)
1784                         {
1785                             const int nearbyID = referenceLineMap.getAccess().getPixelInt(x + dx, y + dy).x();
1786                             if (nearbyID)
1787                             {
1788                                 if (lineID && lineID != nearbyID)
1789                                     multipleNearbyLines = true;
1790                                 lineID = nearbyID;
1791                             }
1792                         }
1793 
1794                     if (multipleNearbyLines || renderAtSurfaceEdge)
1795                     {
1796                         // Another line is too close, don't try to calculate width here
1797                         // Or the render result is outside of surface range
1798                         previousPixelUndefined = true;
1799                         continue;
1800                     }
1801                 }
1802 
1803                 // Only line with id of lineID is nearby
1804 
1805                 if (previousPixelUndefined)
1806                 {
1807                     // The line might have been overdrawn or not
1808                     currentLine            = lineID;
1809                     currentWidth           = 1;
1810                     fullyVisibleLine       = false;
1811                     previousPixelUndefined = false;
1812                 }
1813                 else if (lineID == currentLine)
1814                 {
1815                     // Current line continues
1816                     ++currentWidth;
1817                 }
1818                 else if (lineID > currentLine)
1819                 {
1820                     // Another line was drawn over or the line ends
1821                     currentLine      = lineID;
1822                     currentWidth     = 1;
1823                     fullyVisibleLine = true;
1824                 }
1825                 else
1826                 {
1827                     // The line ends
1828                     if (fullyVisibleLine && lineIsXMajor[currentLine - 1])
1829                     {
1830                         // check width
1831                         if (currentWidth != lineWidth)
1832                         {
1833                             log << tcu::TestLog::Message << "\tInvalid line width at (" << x << ", " << y - currentWidth
1834                                 << ") - (" << x << ", " << y - 1 << "). Detected width of " << currentWidth
1835                                 << ", expected " << lineWidth << tcu::TestLog::EndMessage;
1836                             invalidWidthFound = true;
1837                         }
1838                     }
1839 
1840                     currentLine      = lineID;
1841                     currentWidth     = 1;
1842                     fullyVisibleLine = false;
1843                 }
1844             }
1845         }
1846 
1847         if (invalidWidthFound)
1848         {
1849             log << tcu::TestLog::Message << "Invalid line width found, image is not valid." << tcu::TestLog::EndMessage;
1850             allOK = false;
1851         }
1852         else
1853         {
1854             log << tcu::TestLog::Message << "Line widths are valid." << tcu::TestLog::EndMessage;
1855         }
1856     }
1857 
1858     //\todo [2013-10-24 jarkko].
1859     //Requirement 4. If two line segments share a common endpoint, and both segments are either
1860     //x-major (both left-to-right or both right-to-left) or y-major (both bottom-totop
1861     //or both top-to-bottom), then rasterizing both segments may not produce
1862     //duplicate fragments, nor may any fragments be omitted so as to interrupt
1863     //continuity of the connected segments.
1864 
1865     {
1866         tcu::Surface reference(surface.getWidth(), surface.getHeight());
1867         tcu::clear(reference.getAccess(), tcu::IVec4(0, 0, 0, 255));
1868         for (int y = 0; y < surface.getHeight(); ++y)
1869             for (int x = 0; x < surface.getWidth(); ++x)
1870                 if (referenceLineMap.getAccess().getPixelInt(x, y).x())
1871                     reference.setPixel(x, y, tcu::RGBA::white());
1872         log << tcu::TestLog::Message << "Invalid fragment count in result image." << tcu::TestLog::EndMessage;
1873         log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
1874             << tcu::TestLog::Image("Reference", "Reference", reference)
1875             << tcu::TestLog::Image("Result", "Result", surface) << tcu::TestLog::EndImageSet;
1876     }
1877 
1878     return allOK;
1879 }
1880 
1881 struct SingleSampleNarrowLineCandidate
1882 {
1883     int lineNdx;
1884     tcu::IVec3 colorMin;
1885     tcu::IVec3 colorMax;
1886     tcu::Vec3 colorMinF;
1887     tcu::Vec3 colorMaxF;
1888     tcu::Vec3 valueRangeMin;
1889     tcu::Vec3 valueRangeMax;
1890 };
1891 
setMaskMapCoverageBitForLine(int bitNdx,const tcu::Vec2 & screenSpaceP0,const tcu::Vec2 & screenSpaceP1,float lineWidth,tcu::PixelBufferAccess maskMap,const int subpixelBits)1892 void setMaskMapCoverageBitForLine(int bitNdx, const tcu::Vec2 &screenSpaceP0, const tcu::Vec2 &screenSpaceP1,
1893                                   float lineWidth, tcu::PixelBufferAccess maskMap, const int subpixelBits)
1894 {
1895     enum
1896     {
1897         MAX_PACKETS = 32,
1898     };
1899 
1900     rr::SingleSampleLineRasterizer rasterizer(tcu::IVec4(0, 0, maskMap.getWidth(), maskMap.getHeight()), subpixelBits);
1901     int numRasterized = MAX_PACKETS;
1902     rr::FragmentPacket packets[MAX_PACKETS];
1903 
1904     rasterizer.init(tcu::Vec4(screenSpaceP0.x(), screenSpaceP0.y(), 0.0f, 1.0f),
1905                     tcu::Vec4(screenSpaceP1.x(), screenSpaceP1.y(), 0.0f, 1.0f), lineWidth, 1, 0xFFFF);
1906 
1907     while (numRasterized == MAX_PACKETS)
1908     {
1909         rasterizer.rasterize(packets, DE_NULL, MAX_PACKETS, numRasterized);
1910 
1911         for (int packetNdx = 0; packetNdx < numRasterized; ++packetNdx)
1912         {
1913             for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
1914             {
1915                 if ((uint32_t)packets[packetNdx].coverage & (1 << fragNdx))
1916                 {
1917                     const tcu::IVec2 fragPos = packets[packetNdx].position + tcu::IVec2(fragNdx % 2, fragNdx / 2);
1918 
1919                     DE_ASSERT(deInBounds32(fragPos.x(), 0, maskMap.getWidth()));
1920                     DE_ASSERT(deInBounds32(fragPos.y(), 0, maskMap.getHeight()));
1921 
1922                     const uint32_t previousMask = maskMap.getPixelUint(fragPos.x(), fragPos.y()).x();
1923                     const uint32_t newMask      = (previousMask) | ((uint32_t)1u << bitNdx);
1924 
1925                     maskMap.setPixel(tcu::UVec4(newMask, 0, 0, 0), fragPos.x(), fragPos.y());
1926                 }
1927             }
1928         }
1929     }
1930 }
1931 
setMaskMapCoverageBitForLines(const std::vector<tcu::Vec4> & screenspaceLines,float lineWidth,tcu::PixelBufferAccess maskMap,const int subpixelBits)1932 void setMaskMapCoverageBitForLines(const std::vector<tcu::Vec4> &screenspaceLines, float lineWidth,
1933                                    tcu::PixelBufferAccess maskMap, const int subpixelBits)
1934 {
1935     for (int lineNdx = 0; lineNdx < (int)screenspaceLines.size(); ++lineNdx)
1936     {
1937         const tcu::Vec2 pa = screenspaceLines[lineNdx].swizzle(0, 1);
1938         const tcu::Vec2 pb = screenspaceLines[lineNdx].swizzle(2, 3);
1939 
1940         setMaskMapCoverageBitForLine(lineNdx, pa, pb, lineWidth, maskMap, subpixelBits);
1941     }
1942 }
1943 
1944 // 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)1945 bool verifyLineGroupPixelIndependentInterpolation(const tcu::Surface &surface, const LineSceneSpec &scene,
1946                                                   const RasterizationArguments &args, tcu::TestLog &log,
1947                                                   LineInterpolationMethod interpolationMethod)
1948 {
1949     DE_ASSERT(scene.lines.size() < 8); // coverage indices are stored as bitmask in a unsigned 8-bit ints
1950     DE_ASSERT(interpolationMethod == LINEINTERPOLATION_STRICTLY_CORRECT ||
1951               interpolationMethod == LINEINTERPOLATION_PROJECTED);
1952 
1953     const tcu::RGBA invalidPixelColor = tcu::RGBA(255, 0, 0, 255);
1954     const tcu::IVec2 viewportSize     = tcu::IVec2(surface.getWidth(), surface.getHeight());
1955     const int errorFloodThreshold     = 4;
1956     int errorCount                    = 0;
1957     tcu::Surface errorMask(surface.getWidth(), surface.getHeight());
1958     int invalidPixels = 0;
1959     std::vector<tcu::Vec4> screenspaceLines(scene.lines.size()); //!< packed (x0, y0, x1, y1)
1960 
1961     // Reference renderer produces correct fragments using the diamond-exit-rule. Make 2D int array, store line coverage as a 8-bit bitfield
1962     // The map is used to find lines with potential coverage to a given pixel
1963     tcu::TextureLevel referenceLineMap(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8),
1964                                        surface.getWidth(), surface.getHeight());
1965 
1966     tcu::clear(referenceLineMap.getAccess(), tcu::IVec4(0, 0, 0, 0));
1967     tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
1968 
1969     // log format
1970 
1971     log << tcu::TestLog::Message << "Verifying rasterization result. Native format is RGB" << args.redBits
1972         << args.greenBits << args.blueBits << tcu::TestLog::EndMessage;
1973     if (args.redBits > 8 || args.greenBits > 8 || args.blueBits > 8)
1974         log << tcu::TestLog::Message
1975             << "Warning! More than 8 bits in a color channel, this may produce false negatives."
1976             << tcu::TestLog::EndMessage;
1977 
1978     // prepare lookup map
1979 
1980     genScreenSpaceLines(screenspaceLines, scene.lines, viewportSize);
1981     setMaskMapCoverageBitForLines(screenspaceLines, scene.lineWidth, referenceLineMap.getAccess(), args.subpixelBits);
1982 
1983     // Find all possible lines with coverage, check pixel color matches one of them
1984 
1985     for (int y = 1; y < surface.getHeight() - 1; ++y)
1986         for (int x = 1; x < surface.getWidth() - 1; ++x)
1987         {
1988             const tcu::RGBA color             = surface.getPixel(x, y);
1989             const tcu::IVec3 pixelNativeColor = convertRGB8ToNativeFormat(
1990                 color, args); // Convert pixel color from rgba8 to the real pixel format. Usually rgba8 or 565
1991             int lineCoverageSet         = 0;      // !< lines that may cover this fragment
1992             int lineSurroundingCoverage = 0xFFFF; // !< lines that will cover this fragment
1993             bool matchFound             = false;
1994             const tcu::IVec3 formatLimit((1 << args.redBits) - 1, (1 << args.greenBits) - 1, (1 << args.blueBits) - 1);
1995 
1996             std::vector<SingleSampleNarrowLineCandidate> candidates;
1997 
1998             // Find lines with possible coverage
1999 
2000             for (int dy = -1; dy < 2; ++dy)
2001                 for (int dx = -1; dx < 2; ++dx)
2002                 {
2003                     const int coverage = referenceLineMap.getAccess().getPixelInt(x + dx, y + dy).x();
2004 
2005                     lineCoverageSet |= coverage;
2006                     lineSurroundingCoverage &= coverage;
2007                 }
2008 
2009             // background color is possible?
2010             if (lineSurroundingCoverage == 0 &&
2011                 compareColors(color, tcu::RGBA::black(), args.redBits, args.greenBits, args.blueBits))
2012                 continue;
2013 
2014             // Check those lines
2015 
2016             for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
2017             {
2018                 if (((lineCoverageSet >> lineNdx) & 0x01) != 0)
2019                 {
2020                     const float wa     = scene.lines[lineNdx].positions[0].w();
2021                     const float wb     = scene.lines[lineNdx].positions[1].w();
2022                     const tcu::Vec2 pa = screenspaceLines[lineNdx].swizzle(0, 1);
2023                     const tcu::Vec2 pb = screenspaceLines[lineNdx].swizzle(2, 3);
2024 
2025                     const LineInterpolationRange range = (interpolationMethod == LINEINTERPOLATION_STRICTLY_CORRECT) ?
2026                                                              (calcSingleSampleLineInterpolationRange(
2027                                                                  pa, wa, pb, wb, tcu::IVec2(x, y), args.subpixelBits)) :
2028                                                              (calcSingleSampleLineInterpolationRangeAxisProjected(
2029                                                                  pa, wa, pb, wb, tcu::IVec2(x, y), args.subpixelBits));
2030 
2031                     const tcu::Vec4 valueMin = de::clamp(range.min.x(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[0] +
2032                                                de::clamp(range.min.y(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[1];
2033                     const tcu::Vec4 valueMax = de::clamp(range.max.x(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[0] +
2034                                                de::clamp(range.max.y(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[1];
2035 
2036                     const tcu::Vec3 colorMinF(
2037                         de::clamp(valueMin.x() * (float)formatLimit.x(), 0.0f, (float)formatLimit.x()),
2038                         de::clamp(valueMin.y() * (float)formatLimit.y(), 0.0f, (float)formatLimit.y()),
2039                         de::clamp(valueMin.z() * (float)formatLimit.z(), 0.0f, (float)formatLimit.z()));
2040                     const tcu::Vec3 colorMaxF(
2041                         de::clamp(valueMax.x() * (float)formatLimit.x(), 0.0f, (float)formatLimit.x()),
2042                         de::clamp(valueMax.y() * (float)formatLimit.y(), 0.0f, (float)formatLimit.y()),
2043                         de::clamp(valueMax.z() * (float)formatLimit.z(), 0.0f, (float)formatLimit.z()));
2044                     const tcu::IVec3 colorMin((int)deFloatFloor(colorMinF.x()), (int)deFloatFloor(colorMinF.y()),
2045                                               (int)deFloatFloor(colorMinF.z()));
2046                     const tcu::IVec3 colorMax((int)deFloatCeil(colorMaxF.x()), (int)deFloatCeil(colorMaxF.y()),
2047                                               (int)deFloatCeil(colorMaxF.z()));
2048 
2049                     // Verify validity
2050                     if (pixelNativeColor.x() < colorMin.x() || pixelNativeColor.y() < colorMin.y() ||
2051                         pixelNativeColor.z() < colorMin.z() || pixelNativeColor.x() > colorMax.x() ||
2052                         pixelNativeColor.y() > colorMax.y() || pixelNativeColor.z() > colorMax.z())
2053                     {
2054                         if (errorCount < errorFloodThreshold)
2055                         {
2056                             // Store candidate information for logging
2057                             SingleSampleNarrowLineCandidate candidate;
2058 
2059                             candidate.lineNdx       = lineNdx;
2060                             candidate.colorMin      = colorMin;
2061                             candidate.colorMax      = colorMax;
2062                             candidate.colorMinF     = colorMinF;
2063                             candidate.colorMaxF     = colorMaxF;
2064                             candidate.valueRangeMin = valueMin.swizzle(0, 1, 2);
2065                             candidate.valueRangeMax = valueMax.swizzle(0, 1, 2);
2066 
2067                             candidates.push_back(candidate);
2068                         }
2069                     }
2070                     else
2071                     {
2072                         matchFound = true;
2073                         break;
2074                     }
2075                 }
2076             }
2077 
2078             if (matchFound)
2079                 continue;
2080 
2081             // invalid fragment
2082             ++invalidPixels;
2083             errorMask.setPixel(x, y, invalidPixelColor);
2084 
2085             ++errorCount;
2086 
2087             // don't fill the logs with too much data
2088             if (errorCount < errorFloodThreshold)
2089             {
2090                 log << tcu::TestLog::Message << "Found an invalid pixel at (" << x << "," << y << "), "
2091                     << (int)candidates.size() << " candidate reference value(s) found:\n"
2092                     << "\tPixel color:\t\t" << color << "\n"
2093                     << "\tNative color:\t\t" << pixelNativeColor << "\n"
2094                     << tcu::TestLog::EndMessage;
2095 
2096                 for (int candidateNdx = 0; candidateNdx < (int)candidates.size(); ++candidateNdx)
2097                 {
2098                     const SingleSampleNarrowLineCandidate &candidate = candidates[candidateNdx];
2099 
2100                     log << tcu::TestLog::Message << "\tCandidate (line " << candidate.lineNdx << "):\n"
2101                         << "\t\tReference native color min: "
2102                         << tcu::clamp(candidate.colorMin, tcu::IVec3(0, 0, 0), formatLimit) << "\n"
2103                         << "\t\tReference native color max: "
2104                         << tcu::clamp(candidate.colorMax, tcu::IVec3(0, 0, 0), formatLimit) << "\n"
2105                         << "\t\tReference native float min: "
2106                         << tcu::clamp(candidate.colorMinF, tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast<float>())
2107                         << "\n"
2108                         << "\t\tReference native float max: "
2109                         << tcu::clamp(candidate.colorMaxF, tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast<float>())
2110                         << "\n"
2111                         << "\t\tFmin:\t"
2112                         << tcu::clamp(candidate.valueRangeMin, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f))
2113                         << "\n"
2114                         << "\t\tFmax:\t"
2115                         << tcu::clamp(candidate.valueRangeMax, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f))
2116                         << "\n"
2117                         << tcu::TestLog::EndMessage;
2118                 }
2119             }
2120         }
2121 
2122     // don't just hide failures
2123     if (errorCount > errorFloodThreshold)
2124         log << tcu::TestLog::Message << "Omitted " << (errorCount - errorFloodThreshold)
2125             << " pixel error description(s)." << tcu::TestLog::EndMessage;
2126 
2127     // report result
2128     if (invalidPixels)
2129     {
2130         log << tcu::TestLog::Message << invalidPixels << " invalid pixel(s) found." << tcu::TestLog::EndMessage;
2131         log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
2132             << tcu::TestLog::Image("Result", "Result", surface)
2133             << tcu::TestLog::Image("ErrorMask", "ErrorMask", errorMask) << tcu::TestLog::EndImageSet;
2134 
2135         return false;
2136     }
2137     else
2138     {
2139         log << tcu::TestLog::Message << "No invalid pixels found." << tcu::TestLog::EndMessage;
2140         log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
2141             << tcu::TestLog::Image("Result", "Result", surface) << tcu::TestLog::EndImageSet;
2142 
2143         return true;
2144     }
2145 }
2146 
verifySinglesampleNarrowLineGroupInterpolation(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)2147 bool verifySinglesampleNarrowLineGroupInterpolation(const tcu::Surface &surface, const LineSceneSpec &scene,
2148                                                     const RasterizationArguments &args, tcu::TestLog &log)
2149 {
2150     DE_ASSERT(scene.lineWidth == 1.0f);
2151     return verifyLineGroupPixelIndependentInterpolation(surface, scene, args, log, LINEINTERPOLATION_STRICTLY_CORRECT);
2152 }
2153 
verifyLineGroupInterpolationWithNonProjectedWeights(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)2154 bool verifyLineGroupInterpolationWithNonProjectedWeights(const tcu::Surface &surface, const LineSceneSpec &scene,
2155                                                          const RasterizationArguments &args, tcu::TestLog &log)
2156 {
2157     return verifyLineGroupPixelIndependentInterpolation(surface, scene, args, log, LINEINTERPOLATION_STRICTLY_CORRECT);
2158 }
2159 
verifyLineGroupInterpolationWithProjectedWeights(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)2160 bool verifyLineGroupInterpolationWithProjectedWeights(const tcu::Surface &surface, const LineSceneSpec &scene,
2161                                                       const RasterizationArguments &args, tcu::TestLog &log)
2162 {
2163     return verifyLineGroupPixelIndependentInterpolation(surface, scene, args, log, LINEINTERPOLATION_PROJECTED);
2164 }
2165 
2166 struct SingleSampleWideLineCandidate
2167 {
2168     struct InterpolationPointCandidate
2169     {
2170         tcu::IVec2 interpolationPoint;
2171         tcu::IVec3 colorMin;
2172         tcu::IVec3 colorMax;
2173         tcu::Vec3 colorMinF;
2174         tcu::Vec3 colorMaxF;
2175         tcu::Vec3 valueRangeMin;
2176         tcu::Vec3 valueRangeMax;
2177     };
2178 
2179     int lineNdx;
2180     int numCandidates;
2181     InterpolationPointCandidate interpolationCandidates[3];
2182 };
2183 
2184 // 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)2185 tcu::Vec2 getLineCoordAtAxisCoord(const tcu::Vec2 &pa, const tcu::Vec2 &pb, bool isXAxis, float axisCoord)
2186 {
2187     const int fixedCoordNdx   = (isXAxis) ? (0) : (1);
2188     const int varyingCoordNdx = (isXAxis) ? (1) : (0);
2189 
2190     const float fixedDifference   = pb[fixedCoordNdx] - pa[fixedCoordNdx];
2191     const float varyingDifference = pb[varyingCoordNdx] - pa[varyingCoordNdx];
2192 
2193     DE_ASSERT(fixedDifference != 0.0f);
2194 
2195     const float resultFixedCoord = axisCoord;
2196     const float resultVaryingCoord =
2197         pa[varyingCoordNdx] + (axisCoord - pa[fixedCoordNdx]) * (varyingDifference / fixedDifference);
2198 
2199     return (isXAxis) ? (tcu::Vec2(resultFixedCoord, resultVaryingCoord)) :
2200                        (tcu::Vec2(resultVaryingCoord, resultFixedCoord));
2201 }
2202 
isBlack(const tcu::RGBA & c)2203 bool isBlack(const tcu::RGBA &c)
2204 {
2205     return c.getRed() == 0 && c.getGreen() == 0 && c.getBlue() == 0;
2206 }
2207 
verifySinglesampleWideLineGroupInterpolation(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)2208 bool verifySinglesampleWideLineGroupInterpolation(const tcu::Surface &surface, const LineSceneSpec &scene,
2209                                                   const RasterizationArguments &args, tcu::TestLog &log)
2210 {
2211     DE_ASSERT(deFloatFrac(scene.lineWidth) != 0.5f); // rounding direction is not defined, disallow undefined cases
2212     DE_ASSERT(scene.lines.size() < 8);               // coverage indices are stored as bitmask in a unsigned 8-bit ints
2213 
2214     enum
2215     {
2216         FLAG_ROOT_NOT_SET = (1u << 16)
2217     };
2218 
2219     const tcu::RGBA invalidPixelColor = tcu::RGBA(255, 0, 0, 255);
2220     const tcu::IVec2 viewportSize     = tcu::IVec2(surface.getWidth(), surface.getHeight());
2221     const int errorFloodThreshold     = 4;
2222     int errorCount                    = 0;
2223     tcu::Surface errorMask(surface.getWidth(), surface.getHeight());
2224     int invalidPixels = 0;
2225     std::vector<tcu::Vec4> effectiveLines(scene.lines.size()); //!< packed (x0, y0, x1, y1)
2226     std::vector<bool> lineIsXMajor(scene.lines.size());
2227 
2228     // for each line, for every distinct major direction fragment, store root pixel location (along
2229     // minor direction);
2230     std::vector<std::vector<uint32_t>> rootPixelLocation(
2231         scene.lines.size()); //!< packed [16b - flags] [16b - coordinate]
2232 
2233     // log format
2234 
2235     log << tcu::TestLog::Message << "Verifying rasterization result. Native format is RGB" << args.redBits
2236         << args.greenBits << args.blueBits << tcu::TestLog::EndMessage;
2237     if (args.redBits > 8 || args.greenBits > 8 || args.blueBits > 8)
2238         log << tcu::TestLog::Message
2239             << "Warning! More than 8 bits in a color channel, this may produce false negatives."
2240             << tcu::TestLog::EndMessage;
2241 
2242     // Reference renderer produces correct fragments using the diamond-exit-rule. Make 2D int array, store line coverage as a 8-bit bitfield
2243     // The map is used to find lines with potential coverage to a given pixel
2244     tcu::TextureLevel referenceLineMap(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8),
2245                                        surface.getWidth(), surface.getHeight());
2246     tcu::clear(referenceLineMap.getAccess(), tcu::IVec4(0, 0, 0, 0));
2247 
2248     tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
2249 
2250     // calculate mask and effective line coordinates
2251     {
2252         std::vector<tcu::Vec4> screenspaceLines(scene.lines.size());
2253 
2254         genScreenSpaceLines(screenspaceLines, scene.lines, viewportSize);
2255         setMaskMapCoverageBitForLines(screenspaceLines, scene.lineWidth, referenceLineMap.getAccess(),
2256                                       args.subpixelBits);
2257 
2258         for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
2259         {
2260             const tcu::Vec2 lineScreenSpaceP0 = screenspaceLines[lineNdx].swizzle(0, 1);
2261             const tcu::Vec2 lineScreenSpaceP1 = screenspaceLines[lineNdx].swizzle(2, 3);
2262             const bool isXMajor               = isPackedSSLineXMajor(screenspaceLines[lineNdx]);
2263 
2264             lineIsXMajor[lineNdx] = isXMajor;
2265 
2266             // wide line interpolations are calculated for a line moved in minor direction
2267             {
2268                 const float offsetLength        = (scene.lineWidth - 1.0f) / 2.0f;
2269                 const tcu::Vec2 offsetDirection = (isXMajor) ? (tcu::Vec2(0.0f, -1.0f)) : (tcu::Vec2(-1.0f, 0.0f));
2270                 const tcu::Vec2 offset          = offsetDirection * offsetLength;
2271 
2272                 effectiveLines[lineNdx] =
2273                     tcu::Vec4(lineScreenSpaceP0.x() + offset.x(), lineScreenSpaceP0.y() + offset.y(),
2274                               lineScreenSpaceP1.x() + offset.x(), lineScreenSpaceP1.y() + offset.y());
2275             }
2276         }
2277     }
2278 
2279     for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
2280     {
2281         // Calculate root pixel lookup table for this line. Since the implementation's fragment
2282         // major coordinate range might not be a subset of the correct line range (they are allowed
2283         // to vary by one pixel), we must extend the domain to cover whole viewport along major
2284         // dimension.
2285         //
2286         // Expanding line strip to (effectively) infinite line might result in exit-diamnod set
2287         // that is not a superset of the exit-diamond set of the line strip. In practice, this
2288         // won't be an issue, since the allow-one-pixel-variation rule should tolerate this even
2289         // if the original and extended line would resolve differently a diamond the line just
2290         // touches (precision lost in expansion changes enter/exit status).
2291 
2292         {
2293             const bool isXMajor = lineIsXMajor[lineNdx];
2294             const int majorSize = (isXMajor) ? (surface.getWidth()) : (surface.getHeight());
2295             rr::LineExitDiamondGenerator diamondGenerator(args.subpixelBits);
2296             rr::LineExitDiamond diamonds[32];
2297             int numRasterized = DE_LENGTH_OF_ARRAY(diamonds);
2298 
2299             // Expand to effectively infinite line (endpoints are just one pixel over viewport boundaries)
2300             const tcu::Vec2 expandedP0 = getLineCoordAtAxisCoord(
2301                 effectiveLines[lineNdx].swizzle(0, 1), effectiveLines[lineNdx].swizzle(2, 3), isXMajor, -1.0f);
2302             const tcu::Vec2 expandedP1 =
2303                 getLineCoordAtAxisCoord(effectiveLines[lineNdx].swizzle(0, 1), effectiveLines[lineNdx].swizzle(2, 3),
2304                                         isXMajor, (float)majorSize + 1.0f);
2305 
2306             diamondGenerator.init(tcu::Vec4(expandedP0.x(), expandedP0.y(), 0.0f, 1.0f),
2307                                   tcu::Vec4(expandedP1.x(), expandedP1.y(), 0.0f, 1.0f));
2308 
2309             rootPixelLocation[lineNdx].resize(majorSize, FLAG_ROOT_NOT_SET);
2310 
2311             while (numRasterized == DE_LENGTH_OF_ARRAY(diamonds))
2312             {
2313                 diamondGenerator.rasterize(diamonds, DE_LENGTH_OF_ARRAY(diamonds), numRasterized);
2314 
2315                 for (int packetNdx = 0; packetNdx < numRasterized; ++packetNdx)
2316                 {
2317                     const tcu::IVec2 fragPos = diamonds[packetNdx].position;
2318                     const int majorPos       = (isXMajor) ? (fragPos.x()) : (fragPos.y());
2319                     const int rootPos        = (isXMajor) ? (fragPos.y()) : (fragPos.x());
2320                     const uint32_t packed    = (uint32_t)((uint16_t)((int16_t)rootPos));
2321 
2322                     // infinite line will generate some diamonds outside the viewport
2323                     if (deInBounds32(majorPos, 0, majorSize))
2324                     {
2325                         DE_ASSERT((rootPixelLocation[lineNdx][majorPos] & FLAG_ROOT_NOT_SET) != 0u);
2326                         rootPixelLocation[lineNdx][majorPos] = packed;
2327                     }
2328                 }
2329             }
2330 
2331             // Filled whole lookup table
2332             for (int majorPos = 0; majorPos < majorSize; ++majorPos)
2333                 DE_ASSERT((rootPixelLocation[lineNdx][majorPos] & FLAG_ROOT_NOT_SET) == 0u);
2334         }
2335     }
2336 
2337     // Find all possible lines with coverage, check pixel color matches one of them
2338 
2339     for (int y = 1; y < surface.getHeight() - 1; ++y)
2340         for (int x = 1; x < surface.getWidth() - 1; ++x)
2341         {
2342             const tcu::RGBA color             = surface.getPixel(x, y);
2343             const tcu::IVec3 pixelNativeColor = convertRGB8ToNativeFormat(
2344                 color, args); // Convert pixel color from rgba8 to the real pixel format. Usually rgba8 or 565
2345             int lineCoverageSet         = 0;      // !< lines that may cover this fragment
2346             int lineSurroundingCoverage = 0xFFFF; // !< lines that will cover this fragment
2347             bool matchFound             = false;
2348             const tcu::IVec3 formatLimit((1 << args.redBits) - 1, (1 << args.greenBits) - 1, (1 << args.blueBits) - 1);
2349 
2350             std::vector<SingleSampleWideLineCandidate> candidates;
2351 
2352             // Find lines with possible coverage
2353 
2354             for (int dy = -1; dy < 2; ++dy)
2355                 for (int dx = -1; dx < 2; ++dx)
2356                 {
2357                     const int coverage = referenceLineMap.getAccess().getPixelInt(x + dx, y + dy).x();
2358 
2359                     lineCoverageSet |= coverage;
2360                     lineSurroundingCoverage &= coverage;
2361                 }
2362 
2363             // background color is possible?
2364             if (lineSurroundingCoverage == 0 &&
2365                 compareColors(color, tcu::RGBA::black(), args.redBits, args.greenBits, args.blueBits))
2366                 continue;
2367 
2368             // Check those lines
2369 
2370             for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
2371             {
2372                 if (((lineCoverageSet >> lineNdx) & 0x01) != 0)
2373                 {
2374                     const float wa     = scene.lines[lineNdx].positions[0].w();
2375                     const float wb     = scene.lines[lineNdx].positions[1].w();
2376                     const tcu::Vec2 pa = effectiveLines[lineNdx].swizzle(0, 1);
2377                     const tcu::Vec2 pb = effectiveLines[lineNdx].swizzle(2, 3);
2378 
2379                     // \note Wide line fragments are generated by replicating the root fragment for each
2380                     //       fragment column (row for y-major). Calculate interpolation at the root
2381                     //       fragment.
2382                     const bool isXMajor             = lineIsXMajor[lineNdx];
2383                     const int majorPosition         = (isXMajor) ? (x) : (y);
2384                     const uint32_t minorInfoPacked  = rootPixelLocation[lineNdx][majorPosition];
2385                     const int minorPosition         = (int)((int16_t)((uint16_t)(minorInfoPacked & 0xFFFFu)));
2386                     const tcu::IVec2 idealRootPos   = (isXMajor) ? (tcu::IVec2(majorPosition, minorPosition)) :
2387                                                                    (tcu::IVec2(minorPosition, majorPosition));
2388                     const tcu::IVec2 minorDirection = (isXMajor) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0));
2389 
2390                     SingleSampleWideLineCandidate candidate;
2391 
2392                     candidate.lineNdx       = lineNdx;
2393                     candidate.numCandidates = 0;
2394                     DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(candidate.interpolationCandidates) == 3);
2395 
2396                     // Interpolation happens at the root fragment, which is then replicated in minor
2397                     // direction. Search for implementation's root position near accurate root.
2398                     for (int minorOffset = -1; minorOffset < 2; ++minorOffset)
2399                     {
2400                         const tcu::IVec2 rootPosition = idealRootPos + minorOffset * minorDirection;
2401 
2402                         // A fragment can be root fragment only if it exists
2403                         // \note root fragment can "exist" outside viewport
2404                         // \note no pixel format theshold since in this case allowing only black is more conservative
2405                         if (deInBounds32(rootPosition.x(), 0, surface.getWidth()) &&
2406                             deInBounds32(rootPosition.y(), 0, surface.getHeight()) &&
2407                             isBlack(surface.getPixel(rootPosition.x(), rootPosition.y())))
2408                         {
2409                             continue;
2410                         }
2411 
2412                         const LineInterpolationRange range =
2413                             calcSingleSampleLineInterpolationRange(pa, wa, pb, wb, rootPosition, args.subpixelBits);
2414 
2415                         const tcu::Vec4 valueMin =
2416                             de::clamp(range.min.x(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[0] +
2417                             de::clamp(range.min.y(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[1];
2418                         const tcu::Vec4 valueMax =
2419                             de::clamp(range.max.x(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[0] +
2420                             de::clamp(range.max.y(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[1];
2421 
2422                         const tcu::Vec3 colorMinF(
2423                             de::clamp(valueMin.x() * (float)formatLimit.x(), 0.0f, (float)formatLimit.x()),
2424                             de::clamp(valueMin.y() * (float)formatLimit.y(), 0.0f, (float)formatLimit.y()),
2425                             de::clamp(valueMin.z() * (float)formatLimit.z(), 0.0f, (float)formatLimit.z()));
2426                         const tcu::Vec3 colorMaxF(
2427                             de::clamp(valueMax.x() * (float)formatLimit.x(), 0.0f, (float)formatLimit.x()),
2428                             de::clamp(valueMax.y() * (float)formatLimit.y(), 0.0f, (float)formatLimit.y()),
2429                             de::clamp(valueMax.z() * (float)formatLimit.z(), 0.0f, (float)formatLimit.z()));
2430                         const tcu::IVec3 colorMin((int)deFloatFloor(colorMinF.x()), (int)deFloatFloor(colorMinF.y()),
2431                                                   (int)deFloatFloor(colorMinF.z()));
2432                         const tcu::IVec3 colorMax((int)deFloatCeil(colorMaxF.x()), (int)deFloatCeil(colorMaxF.y()),
2433                                                   (int)deFloatCeil(colorMaxF.z()));
2434 
2435                         // Verify validity
2436                         if (pixelNativeColor.x() < colorMin.x() || pixelNativeColor.y() < colorMin.y() ||
2437                             pixelNativeColor.z() < colorMin.z() || pixelNativeColor.x() > colorMax.x() ||
2438                             pixelNativeColor.y() > colorMax.y() || pixelNativeColor.z() > colorMax.z())
2439                         {
2440                             if (errorCount < errorFloodThreshold)
2441                             {
2442                                 // Store candidate information for logging
2443                                 SingleSampleWideLineCandidate::InterpolationPointCandidate &interpolationCandidate =
2444                                     candidate.interpolationCandidates[candidate.numCandidates++];
2445                                 DE_ASSERT(candidate.numCandidates <=
2446                                           DE_LENGTH_OF_ARRAY(candidate.interpolationCandidates));
2447 
2448                                 interpolationCandidate.interpolationPoint = rootPosition;
2449                                 interpolationCandidate.colorMin           = colorMin;
2450                                 interpolationCandidate.colorMax           = colorMax;
2451                                 interpolationCandidate.colorMinF          = colorMinF;
2452                                 interpolationCandidate.colorMaxF          = colorMaxF;
2453                                 interpolationCandidate.valueRangeMin      = valueMin.swizzle(0, 1, 2);
2454                                 interpolationCandidate.valueRangeMax      = valueMax.swizzle(0, 1, 2);
2455                             }
2456                         }
2457                         else
2458                         {
2459                             matchFound = true;
2460                             break;
2461                         }
2462                     }
2463 
2464                     if (!matchFound)
2465                     {
2466                         // store info for logging
2467                         if (errorCount < errorFloodThreshold && candidate.numCandidates > 0)
2468                             candidates.push_back(candidate);
2469                     }
2470                     else
2471                     {
2472                         // no need to check other lines
2473                         break;
2474                     }
2475                 }
2476             }
2477 
2478             if (matchFound)
2479                 continue;
2480 
2481             // invalid fragment
2482             ++invalidPixels;
2483             errorMask.setPixel(x, y, invalidPixelColor);
2484 
2485             ++errorCount;
2486 
2487             // don't fill the logs with too much data
2488             if (errorCount < errorFloodThreshold)
2489             {
2490                 tcu::MessageBuilder msg(&log);
2491 
2492                 msg << "Found an invalid pixel at (" << x << "," << y << "), " << (int)candidates.size()
2493                     << " candidate reference value(s) found:\n"
2494                     << "\tPixel color:\t\t" << color << "\n"
2495                     << "\tNative color:\t\t" << pixelNativeColor << "\n";
2496 
2497                 for (int lineCandidateNdx = 0; lineCandidateNdx < (int)candidates.size(); ++lineCandidateNdx)
2498                 {
2499                     const SingleSampleWideLineCandidate &candidate = candidates[lineCandidateNdx];
2500 
2501                     msg << "\tCandidate line (line " << candidate.lineNdx << "):\n";
2502 
2503                     for (int interpolationCandidateNdx = 0; interpolationCandidateNdx < candidate.numCandidates;
2504                          ++interpolationCandidateNdx)
2505                     {
2506                         const SingleSampleWideLineCandidate::InterpolationPointCandidate &interpolationCandidate =
2507                             candidate.interpolationCandidates[interpolationCandidateNdx];
2508 
2509                         msg << "\t\tCandidate interpolation point (index " << interpolationCandidateNdx << "):\n"
2510                             << "\t\t\tRoot fragment position (non-replicated fragment): "
2511                             << interpolationCandidate.interpolationPoint << ":\n"
2512                             << "\t\t\tReference native color min: "
2513                             << tcu::clamp(interpolationCandidate.colorMin, tcu::IVec3(0, 0, 0), formatLimit) << "\n"
2514                             << "\t\t\tReference native color max: "
2515                             << tcu::clamp(interpolationCandidate.colorMax, tcu::IVec3(0, 0, 0), formatLimit) << "\n"
2516                             << "\t\t\tReference native float min: "
2517                             << tcu::clamp(interpolationCandidate.colorMinF, tcu::Vec3(0.0f, 0.0f, 0.0f),
2518                                           formatLimit.cast<float>())
2519                             << "\n"
2520                             << "\t\t\tReference native float max: "
2521                             << tcu::clamp(interpolationCandidate.colorMaxF, tcu::Vec3(0.0f, 0.0f, 0.0f),
2522                                           formatLimit.cast<float>())
2523                             << "\n"
2524                             << "\t\t\tFmin:\t"
2525                             << tcu::clamp(interpolationCandidate.valueRangeMin, tcu::Vec3(0.0f, 0.0f, 0.0f),
2526                                           tcu::Vec3(1.0f, 1.0f, 1.0f))
2527                             << "\n"
2528                             << "\t\t\tFmax:\t"
2529                             << tcu::clamp(interpolationCandidate.valueRangeMax, tcu::Vec3(0.0f, 0.0f, 0.0f),
2530                                           tcu::Vec3(1.0f, 1.0f, 1.0f))
2531                             << "\n";
2532                     }
2533                 }
2534 
2535                 msg << tcu::TestLog::EndMessage;
2536             }
2537         }
2538 
2539     // don't just hide failures
2540     if (errorCount > errorFloodThreshold)
2541         log << tcu::TestLog::Message << "Omitted " << (errorCount - errorFloodThreshold)
2542             << " pixel error description(s)." << tcu::TestLog::EndMessage;
2543 
2544     // report result
2545     if (invalidPixels)
2546     {
2547         log << tcu::TestLog::Message << invalidPixels << " invalid pixel(s) found." << tcu::TestLog::EndMessage;
2548         log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
2549             << tcu::TestLog::Image("Result", "Result", surface)
2550             << tcu::TestLog::Image("ErrorMask", "ErrorMask", errorMask) << tcu::TestLog::EndImageSet;
2551 
2552         return false;
2553     }
2554     else
2555     {
2556         log << tcu::TestLog::Message << "No invalid pixels found." << tcu::TestLog::EndMessage;
2557         log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
2558             << tcu::TestLog::Image("Result", "Result", surface) << tcu::TestLog::EndImageSet;
2559 
2560         return true;
2561     }
2562 }
2563 
2564 } // namespace
2565 
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)2566 CoverageType calculateTriangleCoverage(const tcu::Vec4 &p0, const tcu::Vec4 &p1, const tcu::Vec4 &p2,
2567                                        const tcu::IVec2 &pixel, const tcu::IVec2 &viewportSize, int subpixelBits,
2568                                        bool multisample)
2569 {
2570     using tcu::I64Vec2;
2571 
2572     const uint64_t numSubPixels = ((uint64_t)1) << subpixelBits;
2573     const uint64_t pixelHitBoxSize =
2574         (multisample) ? (numSubPixels) :
2575                         5; //!< 5 = ceil(6 * sqrt(2) / 2) to account for a 3 subpixel fuzz around pixel center
2576     const bool order           = isTriangleClockwise(p0, p1, p2); //!< clockwise / counter-clockwise
2577     const tcu::Vec4 &orderedP0 = p0;                              //!< vertices of a clockwise triangle
2578     const tcu::Vec4 &orderedP1 = (order) ? (p1) : (p2);
2579     const tcu::Vec4 &orderedP2 = (order) ? (p2) : (p1);
2580     const tcu::Vec2 triangleNormalizedDeviceSpace[3] = {
2581         tcu::Vec2(orderedP0.x() / orderedP0.w(), orderedP0.y() / orderedP0.w()),
2582         tcu::Vec2(orderedP1.x() / orderedP1.w(), orderedP1.y() / orderedP1.w()),
2583         tcu::Vec2(orderedP2.x() / orderedP2.w(), orderedP2.y() / orderedP2.w()),
2584     };
2585     const tcu::Vec2 triangleScreenSpace[3] = {
2586         (triangleNormalizedDeviceSpace[0] + tcu::Vec2(1.0f, 1.0f)) * 0.5f *
2587             tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
2588         (triangleNormalizedDeviceSpace[1] + tcu::Vec2(1.0f, 1.0f)) * 0.5f *
2589             tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
2590         (triangleNormalizedDeviceSpace[2] + tcu::Vec2(1.0f, 1.0f)) * 0.5f *
2591             tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
2592     };
2593 
2594     // Broad bounding box - pixel check
2595     {
2596         const float minX =
2597             de::min(de::min(triangleScreenSpace[0].x(), triangleScreenSpace[1].x()), triangleScreenSpace[2].x());
2598         const float minY =
2599             de::min(de::min(triangleScreenSpace[0].y(), triangleScreenSpace[1].y()), triangleScreenSpace[2].y());
2600         const float maxX =
2601             de::max(de::max(triangleScreenSpace[0].x(), triangleScreenSpace[1].x()), triangleScreenSpace[2].x());
2602         const float maxY =
2603             de::max(de::max(triangleScreenSpace[0].y(), triangleScreenSpace[1].y()), triangleScreenSpace[2].y());
2604 
2605         if ((float)pixel.x() > maxX + 1 || (float)pixel.y() > maxY + 1 || (float)pixel.x() < minX - 1 ||
2606             (float)pixel.y() < minY - 1)
2607             return COVERAGE_NONE;
2608     }
2609 
2610     // Broad triangle - pixel area intersection
2611     {
2612         const DVec2 pixelCenterPosition =
2613             DVec2((double)pixel.x(), (double)pixel.y()) * DVec2((double)numSubPixels, (double)numSubPixels) +
2614             DVec2((double)numSubPixels / 2, (double)numSubPixels / 2);
2615         const DVec2 triangleSubPixelSpace[3] = {
2616             DVec2(triangleScreenSpace[0].x() * (double)numSubPixels, triangleScreenSpace[0].y() * (double)numSubPixels),
2617             DVec2(triangleScreenSpace[1].x() * (double)numSubPixels, triangleScreenSpace[1].y() * (double)numSubPixels),
2618             DVec2(triangleScreenSpace[2].x() * (double)numSubPixels, triangleScreenSpace[2].y() * (double)numSubPixels),
2619         };
2620 
2621         // Check (using cross product) if pixel center is
2622         // a) too far from any edge
2623         // b) fully inside all edges
2624         bool insideAllEdges = true;
2625         for (int vtxNdx = 0; vtxNdx < 3; ++vtxNdx)
2626         {
2627             const int otherVtxNdx = (vtxNdx + 1) % 3;
2628             const double maxPixelDistanceSquared =
2629                 (double)(pixelHitBoxSize *
2630                          pixelHitBoxSize); // Max distance from the pixel center from within the pixel is (sqrt(2) * boxWidth/2). Use 2x value for rounding tolerance
2631             const DVec2 edge          = triangleSubPixelSpace[otherVtxNdx] - triangleSubPixelSpace[vtxNdx];
2632             const DVec2 v             = pixelCenterPosition - triangleSubPixelSpace[vtxNdx];
2633             const double crossProduct = (edge.x() * v.y() - edge.y() * v.x());
2634 
2635             // distance from edge: (edge x v) / |edge|
2636             //     (edge x v) / |edge| > maxPixelDistance
2637             // ==> (edge x v)^2 / edge^2 > maxPixelDistance^2    | edge x v > 0
2638             // ==> (edge x v)^2 > maxPixelDistance^2 * edge^2
2639             if (crossProduct < 0 && crossProduct * crossProduct > maxPixelDistanceSquared * tcu::lengthSquared(edge))
2640                 return COVERAGE_NONE;
2641             if (crossProduct < 0 || crossProduct * crossProduct < maxPixelDistanceSquared * tcu::lengthSquared(edge))
2642                 insideAllEdges = false;
2643         }
2644 
2645         if (insideAllEdges)
2646             return COVERAGE_FULL;
2647     }
2648 
2649     // Accurate intersection for edge pixels
2650     {
2651         //  In multisampling, the sample points can be anywhere in the pixel, and in single sampling only in the center.
2652         const I64Vec2 pixelCorners[4] = {
2653             I64Vec2((pixel.x() + 0) * numSubPixels, (pixel.y() + 0) * numSubPixels),
2654             I64Vec2((pixel.x() + 1) * numSubPixels, (pixel.y() + 0) * numSubPixels),
2655             I64Vec2((pixel.x() + 1) * numSubPixels, (pixel.y() + 1) * numSubPixels),
2656             I64Vec2((pixel.x() + 0) * numSubPixels, (pixel.y() + 1) * numSubPixels),
2657         };
2658 
2659         // 3 subpixel tolerance around pixel center to account for accumulated errors during various line rasterization methods
2660         const I64Vec2 pixelCenterCorners[4] = {
2661             I64Vec2(pixel.x() * numSubPixels + numSubPixels / 2 - 3, pixel.y() * numSubPixels + numSubPixels / 2 - 3),
2662             I64Vec2(pixel.x() * numSubPixels + numSubPixels / 2 + 3, pixel.y() * numSubPixels + numSubPixels / 2 - 3),
2663             I64Vec2(pixel.x() * numSubPixels + numSubPixels / 2 + 3, pixel.y() * numSubPixels + numSubPixels / 2 + 3),
2664             I64Vec2(pixel.x() * numSubPixels + numSubPixels / 2 - 3, pixel.y() * numSubPixels + numSubPixels / 2 + 3),
2665         };
2666 
2667         // both rounding directions
2668         const I64Vec2 triangleSubPixelSpaceFloor[3] = {
2669             I64Vec2(deFloorFloatToInt32(triangleScreenSpace[0].x() * (float)numSubPixels),
2670                     deFloorFloatToInt32(triangleScreenSpace[0].y() * (float)numSubPixels)),
2671             I64Vec2(deFloorFloatToInt32(triangleScreenSpace[1].x() * (float)numSubPixels),
2672                     deFloorFloatToInt32(triangleScreenSpace[1].y() * (float)numSubPixels)),
2673             I64Vec2(deFloorFloatToInt32(triangleScreenSpace[2].x() * (float)numSubPixels),
2674                     deFloorFloatToInt32(triangleScreenSpace[2].y() * (float)numSubPixels)),
2675         };
2676         const I64Vec2 triangleSubPixelSpaceCeil[3] = {
2677             I64Vec2(deCeilFloatToInt32(triangleScreenSpace[0].x() * (float)numSubPixels),
2678                     deCeilFloatToInt32(triangleScreenSpace[0].y() * (float)numSubPixels)),
2679             I64Vec2(deCeilFloatToInt32(triangleScreenSpace[1].x() * (float)numSubPixels),
2680                     deCeilFloatToInt32(triangleScreenSpace[1].y() * (float)numSubPixels)),
2681             I64Vec2(deCeilFloatToInt32(triangleScreenSpace[2].x() * (float)numSubPixels),
2682                     deCeilFloatToInt32(triangleScreenSpace[2].y() * (float)numSubPixels)),
2683         };
2684         const I64Vec2 *const corners = (multisample) ? (pixelCorners) : (pixelCenterCorners);
2685 
2686         // Test if any edge (with any rounding) intersects the pixel (boundary). If it does => Partial. If not => fully inside or outside
2687 
2688         for (int edgeNdx = 0; edgeNdx < 3; ++edgeNdx)
2689             for (int startRounding = 0; startRounding < 4; ++startRounding)
2690                 for (int endRounding = 0; endRounding < 4; ++endRounding)
2691                 {
2692                     const int nextEdgeNdx = (edgeNdx + 1) % 3;
2693                     const I64Vec2 startPos((startRounding & 0x01) ? (triangleSubPixelSpaceFloor[edgeNdx].x()) :
2694                                                                     (triangleSubPixelSpaceCeil[edgeNdx].x()),
2695                                            (startRounding & 0x02) ? (triangleSubPixelSpaceFloor[edgeNdx].y()) :
2696                                                                     (triangleSubPixelSpaceCeil[edgeNdx].y()));
2697                     const I64Vec2 endPos((endRounding & 0x01) ? (triangleSubPixelSpaceFloor[nextEdgeNdx].x()) :
2698                                                                 (triangleSubPixelSpaceCeil[nextEdgeNdx].x()),
2699                                          (endRounding & 0x02) ? (triangleSubPixelSpaceFloor[nextEdgeNdx].y()) :
2700                                                                 (triangleSubPixelSpaceCeil[nextEdgeNdx].y()));
2701 
2702                     for (int pixelEdgeNdx = 0; pixelEdgeNdx < 4; ++pixelEdgeNdx)
2703                     {
2704                         const int pixelEdgeEnd = (pixelEdgeNdx + 1) % 4;
2705 
2706                         if (lineLineIntersect(startPos, endPos, corners[pixelEdgeNdx], corners[pixelEdgeEnd]))
2707                             return COVERAGE_PARTIAL;
2708                     }
2709                 }
2710 
2711         // fully inside or outside
2712         for (int edgeNdx = 0; edgeNdx < 3; ++edgeNdx)
2713         {
2714             const int nextEdgeNdx      = (edgeNdx + 1) % 3;
2715             const I64Vec2 &startPos    = triangleSubPixelSpaceFloor[edgeNdx];
2716             const I64Vec2 &endPos      = triangleSubPixelSpaceFloor[nextEdgeNdx];
2717             const I64Vec2 edge         = endPos - startPos;
2718             const I64Vec2 v            = corners[0] - endPos;
2719             const int64_t crossProduct = (edge.x() * v.y() - edge.y() * v.x());
2720 
2721             // a corner of the pixel is outside => "fully inside" option is impossible
2722             if (crossProduct < 0)
2723                 return COVERAGE_NONE;
2724         }
2725 
2726         return COVERAGE_FULL;
2727     }
2728 }
2729 
calculateUnderestimateLineCoverage(const tcu::Vec4 & p0,const tcu::Vec4 & p1,const float lineWidth,const tcu::IVec2 & pixel,const tcu::IVec2 & viewportSize)2730 CoverageType calculateUnderestimateLineCoverage(const tcu::Vec4 &p0, const tcu::Vec4 &p1, const float lineWidth,
2731                                                 const tcu::IVec2 &pixel, const tcu::IVec2 &viewportSize)
2732 {
2733     DE_ASSERT(viewportSize.x() == viewportSize.y() && viewportSize.x() > 0);
2734     DE_ASSERT(p0.w() == 1.0f && p1.w() == 1.0f);
2735 
2736     const Vec2 p    = Vec2(p0.x(), p0.y());
2737     const Vec2 q    = Vec2(p1.x(), p1.y());
2738     const Vec2 pq   = Vec2(p1.x() - p0.x(), p1.y() - p0.y());
2739     const Vec2 pqn  = normalize(pq);
2740     const Vec2 lw   = 0.5f * lineWidth * pqn;
2741     const Vec2 n    = Vec2(lw.y(), -lw.x());
2742     const Vec2 vp   = Vec2(float(viewportSize.x()), float(viewportSize.y()));
2743     const Vec2 a    = 0.5f * (p + Vec2(1.0f, 1.0f)) * vp + n;
2744     const Vec2 b    = 0.5f * (p + Vec2(1.0f, 1.0f)) * vp - n;
2745     const Vec2 c    = 0.5f * (q + Vec2(1.0f, 1.0f)) * vp - n;
2746     const Vec2 ba   = b - a;
2747     const Vec2 bc   = b - c;
2748     const float det = ba.x() * bc.y() - ba.y() * bc.x();
2749     int within      = 0;
2750 
2751     if (det != 0.0f)
2752     {
2753         for (int cornerNdx = 0; cornerNdx < 4; ++cornerNdx)
2754         {
2755             const int pixelCornerOffsetX = ((cornerNdx & 1) ? 1 : 0);
2756             const int pixelCornerOffsetY = ((cornerNdx & 2) ? 1 : 0);
2757             const Vec2 f      = Vec2(float(pixel.x() + pixelCornerOffsetX), float(pixel.y() + pixelCornerOffsetY));
2758             const Vec2 bf     = b - f;
2759             const float alpha = (bf.x() * bc.y() - bc.x() * bf.y()) / det;
2760             const float beta  = (ba.x() * bf.y() - bf.x() * ba.y()) / det;
2761             bool cornerWithin = de::inRange(alpha, 0.0f, 1.0f) && de::inRange(beta, 0.0f, 1.0f);
2762 
2763             if (cornerWithin)
2764                 within++;
2765         }
2766     }
2767 
2768     if (within == 0)
2769         return COVERAGE_NONE;
2770     else if (within == 4)
2771         return COVERAGE_FULL;
2772     else
2773         return COVERAGE_PARTIAL;
2774 }
2775 
calculateUnderestimateTriangleCoverage(const tcu::Vec4 & p0,const tcu::Vec4 & p1,const tcu::Vec4 & p2,const tcu::IVec2 & pixel,int subpixelBits,const tcu::IVec2 & viewportSize)2776 CoverageType calculateUnderestimateTriangleCoverage(const tcu::Vec4 &p0, const tcu::Vec4 &p1, const tcu::Vec4 &p2,
2777                                                     const tcu::IVec2 &pixel, int subpixelBits,
2778                                                     const tcu::IVec2 &viewportSize)
2779 {
2780     using tcu::I64Vec2;
2781 
2782     const uint64_t numSubPixels = ((uint64_t)1) << subpixelBits;
2783     const bool order            = isTriangleClockwise(p0, p1, p2); //!< clockwise / counter-clockwise
2784     const tcu::Vec4 &orderedP0  = p0;                              //!< vertices of a clockwise triangle
2785     const tcu::Vec4 &orderedP1  = (order) ? (p1) : (p2);
2786     const tcu::Vec4 &orderedP2  = (order) ? (p2) : (p1);
2787     const tcu::Vec2 triangleNormalizedDeviceSpace[3] = {
2788         tcu::Vec2(orderedP0.x() / orderedP0.w(), orderedP0.y() / orderedP0.w()),
2789         tcu::Vec2(orderedP1.x() / orderedP1.w(), orderedP1.y() / orderedP1.w()),
2790         tcu::Vec2(orderedP2.x() / orderedP2.w(), orderedP2.y() / orderedP2.w()),
2791     };
2792     const tcu::Vec2 triangleScreenSpace[3] = {
2793         (triangleNormalizedDeviceSpace[0] + tcu::Vec2(1.0f, 1.0f)) * 0.5f *
2794             tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
2795         (triangleNormalizedDeviceSpace[1] + tcu::Vec2(1.0f, 1.0f)) * 0.5f *
2796             tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
2797         (triangleNormalizedDeviceSpace[2] + tcu::Vec2(1.0f, 1.0f)) * 0.5f *
2798             tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
2799     };
2800 
2801     // Broad bounding box - pixel check
2802     {
2803         const float minX =
2804             de::min(de::min(triangleScreenSpace[0].x(), triangleScreenSpace[1].x()), triangleScreenSpace[2].x());
2805         const float minY =
2806             de::min(de::min(triangleScreenSpace[0].y(), triangleScreenSpace[1].y()), triangleScreenSpace[2].y());
2807         const float maxX =
2808             de::max(de::max(triangleScreenSpace[0].x(), triangleScreenSpace[1].x()), triangleScreenSpace[2].x());
2809         const float maxY =
2810             de::max(de::max(triangleScreenSpace[0].y(), triangleScreenSpace[1].y()), triangleScreenSpace[2].y());
2811 
2812         if ((float)pixel.x() > maxX + 1 || (float)pixel.y() > maxY + 1 || (float)pixel.x() < minX - 1 ||
2813             (float)pixel.y() < minY - 1)
2814             return COVERAGE_NONE;
2815     }
2816 
2817     // Accurate intersection for edge pixels
2818     {
2819         //  In multisampling, the sample points can be anywhere in the pixel, and in single sampling only in the center.
2820         const I64Vec2 pixelCorners[4] = {
2821             I64Vec2((pixel.x() + 0) * numSubPixels, (pixel.y() + 0) * numSubPixels),
2822             I64Vec2((pixel.x() + 1) * numSubPixels, (pixel.y() + 0) * numSubPixels),
2823             I64Vec2((pixel.x() + 1) * numSubPixels, (pixel.y() + 1) * numSubPixels),
2824             I64Vec2((pixel.x() + 0) * numSubPixels, (pixel.y() + 1) * numSubPixels),
2825         };
2826         // both rounding directions
2827         const I64Vec2 triangleSubPixelSpaceFloor[3] = {
2828             I64Vec2(deFloorFloatToInt32(triangleScreenSpace[0].x() * (float)numSubPixels),
2829                     deFloorFloatToInt32(triangleScreenSpace[0].y() * (float)numSubPixels)),
2830             I64Vec2(deFloorFloatToInt32(triangleScreenSpace[1].x() * (float)numSubPixels),
2831                     deFloorFloatToInt32(triangleScreenSpace[1].y() * (float)numSubPixels)),
2832             I64Vec2(deFloorFloatToInt32(triangleScreenSpace[2].x() * (float)numSubPixels),
2833                     deFloorFloatToInt32(triangleScreenSpace[2].y() * (float)numSubPixels)),
2834         };
2835         const I64Vec2 triangleSubPixelSpaceCeil[3] = {
2836             I64Vec2(deCeilFloatToInt32(triangleScreenSpace[0].x() * (float)numSubPixels),
2837                     deCeilFloatToInt32(triangleScreenSpace[0].y() * (float)numSubPixels)),
2838             I64Vec2(deCeilFloatToInt32(triangleScreenSpace[1].x() * (float)numSubPixels),
2839                     deCeilFloatToInt32(triangleScreenSpace[1].y() * (float)numSubPixels)),
2840             I64Vec2(deCeilFloatToInt32(triangleScreenSpace[2].x() * (float)numSubPixels),
2841                     deCeilFloatToInt32(triangleScreenSpace[2].y() * (float)numSubPixels)),
2842         };
2843 
2844         // Test if any edge (with any rounding) intersects the pixel (boundary). If it does => Partial. If not => fully inside or outside
2845 
2846         for (int edgeNdx = 0; edgeNdx < 3; ++edgeNdx)
2847             for (int startRounding = 0; startRounding < 4; ++startRounding)
2848                 for (int endRounding = 0; endRounding < 4; ++endRounding)
2849                 {
2850                     const int nextEdgeNdx = (edgeNdx + 1) % 3;
2851                     const I64Vec2 startPos((startRounding & 0x01) ? (triangleSubPixelSpaceFloor[edgeNdx].x()) :
2852                                                                     (triangleSubPixelSpaceCeil[edgeNdx].x()),
2853                                            (startRounding & 0x02) ? (triangleSubPixelSpaceFloor[edgeNdx].y()) :
2854                                                                     (triangleSubPixelSpaceCeil[edgeNdx].y()));
2855                     const I64Vec2 endPos((endRounding & 0x01) ? (triangleSubPixelSpaceFloor[nextEdgeNdx].x()) :
2856                                                                 (triangleSubPixelSpaceCeil[nextEdgeNdx].x()),
2857                                          (endRounding & 0x02) ? (triangleSubPixelSpaceFloor[nextEdgeNdx].y()) :
2858                                                                 (triangleSubPixelSpaceCeil[nextEdgeNdx].y()));
2859 
2860                     for (int pixelEdgeNdx = 0; pixelEdgeNdx < 4; ++pixelEdgeNdx)
2861                     {
2862                         const int pixelEdgeEnd = (pixelEdgeNdx + 1) % 4;
2863 
2864                         if (lineLineIntersect(startPos, endPos, pixelCorners[pixelEdgeNdx], pixelCorners[pixelEdgeEnd]))
2865                             return COVERAGE_PARTIAL;
2866                     }
2867                 }
2868 
2869         // fully inside or outside
2870         for (int edgeNdx = 0; edgeNdx < 3; ++edgeNdx)
2871         {
2872             const int nextEdgeNdx      = (edgeNdx + 1) % 3;
2873             const I64Vec2 &startPos    = triangleSubPixelSpaceFloor[edgeNdx];
2874             const I64Vec2 &endPos      = triangleSubPixelSpaceFloor[nextEdgeNdx];
2875             const I64Vec2 edge         = endPos - startPos;
2876             const I64Vec2 v            = pixelCorners[0] - endPos;
2877             const int64_t crossProduct = (edge.x() * v.y() - edge.y() * v.x());
2878 
2879             // a corner of the pixel is outside => "fully inside" option is impossible
2880             if (crossProduct < 0)
2881                 return COVERAGE_NONE;
2882         }
2883 
2884         return COVERAGE_FULL;
2885     }
2886 }
2887 
logTriangleGroupRasterizationStash(const tcu::Surface & surface,tcu::TestLog & log,VerifyTriangleGroupRasterizationLogStash & logStash)2888 static void logTriangleGroupRasterizationStash(const tcu::Surface &surface, tcu::TestLog &log,
2889                                                VerifyTriangleGroupRasterizationLogStash &logStash)
2890 {
2891     // Output results
2892     log << tcu::TestLog::Message << "Verifying rasterization result." << tcu::TestLog::EndMessage;
2893 
2894     for (size_t msgNdx = 0; msgNdx < logStash.messages.size(); ++msgNdx)
2895         log << tcu::TestLog::Message << logStash.messages[msgNdx] << tcu::TestLog::EndMessage;
2896 
2897     if (!logStash.result)
2898     {
2899         log << tcu::TestLog::Message << "Invalid pixels found:\n\t" << logStash.missingPixels
2900             << " missing pixels. (Marked with purple)\n\t" << logStash.unexpectedPixels
2901             << " incorrectly filled pixels. (Marked with red)\n\t"
2902             << "Unknown (subpixel on edge) pixels are marked with yellow." << tcu::TestLog::EndMessage;
2903         log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
2904             << tcu::TestLog::Image("Result", "Result", surface)
2905             << tcu::TestLog::Image("ErrorMask", "ErrorMask", logStash.errorMask) << tcu::TestLog::EndImageSet;
2906     }
2907     else
2908     {
2909         log << tcu::TestLog::Message << "No invalid pixels found." << tcu::TestLog::EndMessage;
2910         log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
2911             << tcu::TestLog::Image("Result", "Result", surface) << tcu::TestLog::EndImageSet;
2912     }
2913 }
2914 
verifyTriangleGroupRasterization(const tcu::Surface & surface,const TriangleSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log,VerificationMode mode,VerifyTriangleGroupRasterizationLogStash * logStash,const bool vulkanLinesTest)2915 bool verifyTriangleGroupRasterization(const tcu::Surface &surface, const TriangleSceneSpec &scene,
2916                                       const RasterizationArguments &args, tcu::TestLog &log, VerificationMode mode,
2917                                       VerifyTriangleGroupRasterizationLogStash *logStash, const bool vulkanLinesTest)
2918 {
2919     DE_ASSERT(mode < VERIFICATIONMODE_LAST);
2920 
2921     const tcu::RGBA backGroundColor       = tcu::RGBA(0, 0, 0, 255);
2922     const tcu::RGBA triangleColor         = tcu::RGBA(255, 255, 255, 255);
2923     const tcu::RGBA missingPixelColor     = tcu::RGBA(255, 0, 255, 255);
2924     const tcu::RGBA unexpectedPixelColor  = tcu::RGBA(255, 0, 0, 255);
2925     const tcu::RGBA partialPixelColor     = tcu::RGBA(255, 255, 0, 255);
2926     const tcu::RGBA primitivePixelColor   = tcu::RGBA(30, 30, 30, 255);
2927     const int weakVerificationThreshold   = 10;
2928     const int weakerVerificationThreshold = 25;
2929     const bool multisampled               = (args.numSamples != 0);
2930     const tcu::IVec2 viewportSize         = tcu::IVec2(surface.getWidth(), surface.getHeight());
2931     int missingPixels                     = 0;
2932     int unexpectedPixels                  = 0;
2933     int subPixelBits                      = args.subpixelBits;
2934     tcu::TextureLevel coverageMap(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8),
2935                                   surface.getWidth(), surface.getHeight());
2936     tcu::Surface errorMask(surface.getWidth(), surface.getHeight());
2937     bool result = false;
2938 
2939     // subpixel bits in a valid range?
2940 
2941     if (subPixelBits < 0)
2942     {
2943         log << tcu::TestLog::Message << "Invalid subpixel count (" << subPixelBits << "), assuming 0"
2944             << tcu::TestLog::EndMessage;
2945         subPixelBits = 0;
2946     }
2947     else if (subPixelBits > 16)
2948     {
2949         // At high subpixel bit counts we might overflow. Checking at lower bit count is ok, but is less strict
2950         log << tcu::TestLog::Message << "Subpixel count is greater than 16 (" << subPixelBits
2951             << "). Checking results using less strict 16 bit requirements. This may produce false positives."
2952             << tcu::TestLog::EndMessage;
2953         subPixelBits = 16;
2954     }
2955 
2956     // generate coverage map
2957 
2958     tcu::clear(coverageMap.getAccess(), tcu::IVec4(COVERAGE_NONE, 0, 0, 0));
2959 
2960     for (int triNdx = 0; triNdx < (int)scene.triangles.size(); ++triNdx)
2961     {
2962         const tcu::IVec4 aabb = getTriangleAABB(scene.triangles[triNdx], viewportSize);
2963 
2964         for (int y = de::max(0, aabb.y()); y <= de::min(aabb.w(), coverageMap.getHeight() - 1); ++y)
2965             for (int x = de::max(0, aabb.x()); x <= de::min(aabb.z(), coverageMap.getWidth() - 1); ++x)
2966             {
2967                 if (coverageMap.getAccess().getPixelUint(x, y).x() == COVERAGE_FULL)
2968                     continue;
2969 
2970                 const CoverageType coverage = calculateTriangleCoverage(
2971                     scene.triangles[triNdx].positions[0], scene.triangles[triNdx].positions[1],
2972                     scene.triangles[triNdx].positions[2], tcu::IVec2(x, y), viewportSize, subPixelBits, multisampled);
2973 
2974                 if (coverage == COVERAGE_FULL)
2975                 {
2976                     coverageMap.getAccess().setPixel(tcu::IVec4(COVERAGE_FULL, 0, 0, 0), x, y);
2977                 }
2978                 else if (coverage == COVERAGE_PARTIAL)
2979                 {
2980                     CoverageType resultCoverage = COVERAGE_PARTIAL;
2981 
2982                     // Sharing an edge with another triangle?
2983                     // There should always be such a triangle, but the pixel in the other triangle might be
2984                     // on multiple edges, some of which are not shared. In these cases the coverage cannot be determined.
2985                     // Assume full coverage if the pixel is only on a shared edge in shared triangle too.
2986                     if (pixelOnlyOnASharedEdge(tcu::IVec2(x, y), scene.triangles[triNdx], viewportSize))
2987                     {
2988                         bool friendFound = false;
2989                         for (int friendTriNdx = 0; friendTriNdx < (int)scene.triangles.size(); ++friendTriNdx)
2990                         {
2991                             if (friendTriNdx == triNdx)
2992                                 continue;
2993 
2994                             const CoverageType friendCoverage = calculateTriangleCoverage(
2995                                 scene.triangles[friendTriNdx].positions[0], scene.triangles[friendTriNdx].positions[1],
2996                                 scene.triangles[friendTriNdx].positions[2], tcu::IVec2(x, y), viewportSize,
2997                                 subPixelBits, multisampled);
2998 
2999                             if (friendCoverage != COVERAGE_NONE &&
3000                                 pixelOnlyOnASharedEdge(tcu::IVec2(x, y), scene.triangles[friendTriNdx], viewportSize))
3001                             {
3002                                 friendFound = true;
3003                                 break;
3004                             }
3005                         }
3006 
3007                         if (friendFound)
3008                             resultCoverage = COVERAGE_FULL;
3009                     }
3010 
3011                     coverageMap.getAccess().setPixel(tcu::IVec4(resultCoverage, 0, 0, 0), x, y);
3012                 }
3013             }
3014     }
3015 
3016     // check pixels
3017 
3018     tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
3019 
3020     // Use these to quick check there is something drawn when a test expects something else than an empty picture.
3021     bool referenceEmpty = true;
3022     bool resultEmpty    = true;
3023 
3024     for (int y = 0; y < surface.getHeight(); ++y)
3025         for (int x = 0; x < surface.getWidth(); ++x)
3026         {
3027             const tcu::RGBA color = surface.getPixel(x, y);
3028             const bool imageNoCoverage =
3029                 compareColors(color, backGroundColor, args.redBits, args.greenBits, args.blueBits);
3030             const bool imageFullCoverage =
3031                 compareColors(color, triangleColor, args.redBits, args.greenBits, args.blueBits);
3032             CoverageType referenceCoverage = (CoverageType)coverageMap.getAccess().getPixelUint(x, y).x();
3033 
3034             if (!imageNoCoverage)
3035                 resultEmpty = false;
3036 
3037             switch (referenceCoverage)
3038             {
3039             case COVERAGE_NONE:
3040                 if (!imageNoCoverage)
3041                 {
3042                     // coverage where there should not be
3043                     ++unexpectedPixels;
3044                     errorMask.setPixel(x, y, unexpectedPixelColor);
3045                 }
3046                 break;
3047 
3048             case COVERAGE_PARTIAL:
3049             {
3050                 referenceEmpty     = false;
3051                 bool foundFragment = false;
3052                 if (vulkanLinesTest == true)
3053                 {
3054                     for (int dy = -1; dy < 2 && !foundFragment; ++dy)
3055                         for (int dx = -1; dx < 2 && !foundFragment; ++dx)
3056                         {
3057                             if (x + dx >= 0 && x + dx != surface.getWidth() && y + dy >= 0 &&
3058                                 y + dy != surface.getHeight() &&
3059                                 (CoverageType)coverageMap.getAccess().getPixelUint(x + dx, y + dy).x() != COVERAGE_NONE)
3060                             {
3061                                 const tcu::RGBA color2 = surface.getPixel(x + dx, y + dy);
3062                                 if (compareColors(color2, triangleColor, args.redBits, args.greenBits, args.blueBits))
3063                                     foundFragment = true;
3064                             }
3065                         }
3066                 }
3067                 // anything goes
3068                 if (foundFragment == false)
3069                 {
3070                     errorMask.setPixel(x, y, partialPixelColor);
3071                     if (vulkanLinesTest == true)
3072                         ++missingPixels;
3073                 }
3074             }
3075             break;
3076 
3077             case COVERAGE_FULL:
3078                 referenceEmpty = false;
3079                 if (!imageFullCoverage)
3080                 {
3081                     // no coverage where there should be
3082                     ++missingPixels;
3083                     errorMask.setPixel(x, y, missingPixelColor);
3084                 }
3085                 else
3086                 {
3087                     errorMask.setPixel(x, y, primitivePixelColor);
3088                 }
3089                 break;
3090 
3091             default:
3092                 DE_ASSERT(false);
3093             }
3094         }
3095 
3096     if (((mode == VERIFICATIONMODE_STRICT) && (missingPixels + unexpectedPixels > 0)) ||
3097         ((mode == VERIFICATIONMODE_WEAK) && (missingPixels + unexpectedPixels > weakVerificationThreshold)) ||
3098         ((mode == VERIFICATIONMODE_WEAKER) && (missingPixels + unexpectedPixels > weakerVerificationThreshold)) ||
3099         ((mode == VERIFICATIONMODE_SMOOTH) && (missingPixels > weakVerificationThreshold)) ||
3100         referenceEmpty != resultEmpty)
3101     {
3102         result = false;
3103     }
3104     else
3105     {
3106         result = true;
3107     }
3108 
3109     // Output or stash results
3110     {
3111         VerifyTriangleGroupRasterizationLogStash *tempLogStash =
3112             (logStash == DE_NULL) ? new VerifyTriangleGroupRasterizationLogStash : logStash;
3113 
3114         tempLogStash->result           = result;
3115         tempLogStash->missingPixels    = missingPixels;
3116         tempLogStash->unexpectedPixels = unexpectedPixels;
3117         tempLogStash->errorMask        = errorMask;
3118 
3119         if (logStash == DE_NULL)
3120         {
3121             logTriangleGroupRasterizationStash(surface, log, *tempLogStash);
3122             delete tempLogStash;
3123         }
3124     }
3125 
3126     return result;
3127 }
3128 
verifyLineGroupRasterization(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)3129 bool verifyLineGroupRasterization(const tcu::Surface &surface, const LineSceneSpec &scene,
3130                                   const RasterizationArguments &args, tcu::TestLog &log)
3131 {
3132     const bool multisampled = args.numSamples != 0;
3133 
3134     if (multisampled)
3135         return verifyMultisampleLineGroupRasterization(surface, scene, args, log, CLIPMODE_NO_CLIPPING, DE_NULL, false,
3136                                                        true);
3137     else
3138         return verifySinglesampleLineGroupRasterization(surface, scene, args, log);
3139 }
3140 
verifyClippedTriangulatedLineGroupRasterization(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)3141 bool verifyClippedTriangulatedLineGroupRasterization(const tcu::Surface &surface, const LineSceneSpec &scene,
3142                                                      const RasterizationArguments &args, tcu::TestLog &log)
3143 {
3144     return verifyMultisampleLineGroupRasterization(surface, scene, args, log, CLIPMODE_USE_CLIPPING_BOX, DE_NULL, false,
3145                                                    true);
3146 }
3147 
verifyRelaxedLineGroupRasterization(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log,const bool vulkanLinesTest,const bool strict)3148 bool verifyRelaxedLineGroupRasterization(const tcu::Surface &surface, const LineSceneSpec &scene,
3149                                          const RasterizationArguments &args, tcu::TestLog &log,
3150                                          const bool vulkanLinesTest, const bool strict)
3151 {
3152     VerifyTriangleGroupRasterizationLogStash useClippingLogStash;
3153     VerifyTriangleGroupRasterizationLogStash noClippingLogStash;
3154     VerifyTriangleGroupRasterizationLogStash useClippingForcedStrictLogStash;
3155     VerifyTriangleGroupRasterizationLogStash noClippingForcedStrictLogStash;
3156 
3157     if (verifyMultisampleLineGroupRasterization(surface, scene, args, log, CLIPMODE_USE_CLIPPING_BOX,
3158                                                 &useClippingLogStash, vulkanLinesTest, strict))
3159     {
3160         logTriangleGroupRasterizationStash(surface, log, useClippingLogStash);
3161 
3162         return true;
3163     }
3164     else if (verifyMultisampleLineGroupRasterization(surface, scene, args, log, CLIPMODE_NO_CLIPPING,
3165                                                      &noClippingLogStash, vulkanLinesTest, strict))
3166     {
3167         logTriangleGroupRasterizationStash(surface, log, noClippingLogStash);
3168 
3169         return true;
3170     }
3171     else if (strict == false &&
3172              verifyMultisampleLineGroupRasterization(surface, scene, args, log, CLIPMODE_USE_CLIPPING_BOX,
3173                                                      &useClippingForcedStrictLogStash, vulkanLinesTest, true))
3174     {
3175         logTriangleGroupRasterizationStash(surface, log, useClippingForcedStrictLogStash);
3176 
3177         return true;
3178     }
3179     else if (strict == false &&
3180              verifyMultisampleLineGroupRasterization(surface, scene, args, log, CLIPMODE_NO_CLIPPING,
3181                                                      &noClippingForcedStrictLogStash, vulkanLinesTest, true))
3182     {
3183         logTriangleGroupRasterizationStash(surface, log, noClippingForcedStrictLogStash);
3184 
3185         return true;
3186     }
3187     else if (strict == false && args.numSamples == 0 && verifyLineGroupRasterization(surface, scene, args, log))
3188     {
3189         return true;
3190     }
3191     else
3192     {
3193         log << tcu::TestLog::Message << "Relaxed rasterization failed, details follow." << tcu::TestLog::EndMessage;
3194 
3195         logTriangleGroupRasterizationStash(surface, log, useClippingLogStash);
3196         logTriangleGroupRasterizationStash(surface, log, noClippingLogStash);
3197 
3198         if (strict == false)
3199         {
3200             logTriangleGroupRasterizationStash(surface, log, useClippingForcedStrictLogStash);
3201             logTriangleGroupRasterizationStash(surface, log, noClippingForcedStrictLogStash);
3202         }
3203 
3204         return false;
3205     }
3206 }
3207 
verifyPointGroupRasterization(const tcu::Surface & surface,const PointSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)3208 bool verifyPointGroupRasterization(const tcu::Surface &surface, const PointSceneSpec &scene,
3209                                    const RasterizationArguments &args, tcu::TestLog &log)
3210 {
3211     // Splitting to triangles is a valid solution in multisampled cases and even in non-multisample cases too.
3212     return verifyMultisamplePointGroupRasterization(surface, scene, args, log);
3213 }
3214 
verifyTriangleGroupInterpolation(const tcu::Surface & surface,const TriangleSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)3215 bool verifyTriangleGroupInterpolation(const tcu::Surface &surface, const TriangleSceneSpec &scene,
3216                                       const RasterizationArguments &args, tcu::TestLog &log)
3217 {
3218     VerifyTriangleGroupInterpolationLogStash logStash;
3219     const bool result =
3220         verifyTriangleGroupInterpolationWithInterpolator(surface, scene, args, logStash, TriangleInterpolator(scene));
3221 
3222     logTriangleGroupnterpolationStash(surface, log, logStash);
3223 
3224     return result;
3225 }
3226 
verifyLineGroupInterpolation(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)3227 LineInterpolationMethod verifyLineGroupInterpolation(const tcu::Surface &surface, const LineSceneSpec &scene,
3228                                                      const RasterizationArguments &args, tcu::TestLog &log)
3229 {
3230     const bool multisampled = args.numSamples != 0;
3231 
3232     if (multisampled)
3233     {
3234         if (verifyMultisampleLineGroupInterpolation(surface, scene, args, log))
3235             return LINEINTERPOLATION_STRICTLY_CORRECT;
3236         return LINEINTERPOLATION_INCORRECT;
3237     }
3238     else
3239     {
3240         const bool isNarrow = (scene.lineWidth == 1.0f);
3241 
3242         // accurate interpolation
3243         if (isNarrow)
3244         {
3245             if (verifySinglesampleNarrowLineGroupInterpolation(surface, scene, args, log))
3246                 return LINEINTERPOLATION_STRICTLY_CORRECT;
3247         }
3248         else
3249         {
3250             if (verifySinglesampleWideLineGroupInterpolation(surface, scene, args, log))
3251                 return LINEINTERPOLATION_STRICTLY_CORRECT;
3252 
3253             if (scene.allowNonProjectedInterpolation &&
3254                 verifyLineGroupInterpolationWithNonProjectedWeights(surface, scene, args, log))
3255                 return LINEINTERPOLATION_STRICTLY_CORRECT;
3256         }
3257 
3258         // check with projected (inaccurate) interpolation
3259         log << tcu::TestLog::Message
3260             << "Accurate verification failed, checking with projected weights (inaccurate equation)."
3261             << tcu::TestLog::EndMessage;
3262         if (verifyLineGroupInterpolationWithProjectedWeights(surface, scene, args, log))
3263             return LINEINTERPOLATION_PROJECTED;
3264 
3265         return LINEINTERPOLATION_INCORRECT;
3266     }
3267 }
3268 
verifyTriangulatedLineGroupInterpolation(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log,const bool strictMode,const bool allowBresenhamForNonStrictLines)3269 bool verifyTriangulatedLineGroupInterpolation(const tcu::Surface &surface, const LineSceneSpec &scene,
3270                                               const RasterizationArguments &args, tcu::TestLog &log,
3271                                               const bool strictMode, const bool allowBresenhamForNonStrictLines)
3272 {
3273     return verifyMultisampleLineGroupInterpolation(surface, scene, args, log, strictMode,
3274                                                    allowBresenhamForNonStrictLines);
3275 }
3276 
3277 } // namespace tcu
3278