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