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