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