1/*------------------------------------------------------------------------- 2 * drawElements Quality Program Reference Renderer 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 Reference rasterizer 22 *//*--------------------------------------------------------------------*/ 23 24#include "rrRasterizer.hpp" 25#include "deMath.h" 26#include "tcuVectorUtil.hpp" 27 28namespace rr 29{ 30 31inline deInt64 toSubpixelCoord (float v, int bits) 32{ 33 return (deInt64)(v * (float)(1 << bits) + (v < 0.f ? -0.5f : 0.5f)); 34} 35 36inline deInt64 toSubpixelCoord (deInt32 v, int bits) 37{ 38 return v << bits; 39} 40 41inline deInt32 ceilSubpixelToPixelCoord (deInt64 coord, int bits, bool fillEdge) 42{ 43 if (coord >= 0) 44 return (deInt32)((coord + ((1ll << bits) - (fillEdge ? 0 : 1))) >> bits); 45 else 46 return (deInt32)((coord + (fillEdge ? 1 : 0)) >> bits); 47} 48 49inline deInt32 floorSubpixelToPixelCoord (deInt64 coord, int bits, bool fillEdge) 50{ 51 if (coord >= 0) 52 return (deInt32)((coord - (fillEdge ? 1 : 0)) >> bits); 53 else 54 return (deInt32)((coord - ((1ll << bits) - (fillEdge ? 0 : 1))) >> bits); 55} 56 57static inline void initEdgeCCW (EdgeFunction& edge, const HorizontalFill horizontalFill, const VerticalFill verticalFill, const deInt64 x0, const deInt64 y0, const deInt64 x1, const deInt64 y1) 58{ 59 // \note See EdgeFunction documentation for details. 60 61 const deInt64 xd = x1-x0; 62 const deInt64 yd = y1-y0; 63 bool inclusive = false; //!< Inclusive in CCW orientation. 64 65 if (yd == 0) 66 inclusive = verticalFill == FILL_BOTTOM ? xd >= 0 : xd <= 0; 67 else 68 inclusive = horizontalFill == FILL_LEFT ? yd <= 0 : yd >= 0; 69 70 edge.a = (y0 - y1); 71 edge.b = (x1 - x0); 72 edge.c = x0*y1 - y0*x1; 73 edge.inclusive = inclusive; //!< \todo [pyry] Swap for CW triangles 74} 75 76static inline void reverseEdge (EdgeFunction& edge) 77{ 78 edge.a = -edge.a; 79 edge.b = -edge.b; 80 edge.c = -edge.c; 81 edge.inclusive = !edge.inclusive; 82} 83 84static inline deInt64 evaluateEdge (const EdgeFunction& edge, const deInt64 x, const deInt64 y) 85{ 86 return edge.a*x + edge.b*y + edge.c; 87} 88 89static inline bool isInsideCCW (const EdgeFunction& edge, const deInt64 edgeVal) 90{ 91 return edge.inclusive ? (edgeVal >= 0) : (edgeVal > 0); 92} 93 94namespace LineRasterUtil 95{ 96 97struct SubpixelLineSegment 98{ 99 const tcu::Vector<deInt64,2> m_v0; 100 const tcu::Vector<deInt64,2> m_v1; 101 102 SubpixelLineSegment (const tcu::Vector<deInt64,2>& v0, const tcu::Vector<deInt64,2>& v1) 103 : m_v0(v0) 104 , m_v1(v1) 105 { 106 } 107 108 tcu::Vector<deInt64,2> direction (void) const 109 { 110 return m_v1 - m_v0; 111 } 112}; 113 114enum LINE_SIDE 115{ 116 LINE_SIDE_INTERSECT = 0, 117 LINE_SIDE_LEFT, 118 LINE_SIDE_RIGHT 119}; 120 121static tcu::Vector<deInt64,2> toSubpixelVector (const tcu::Vec2& v, int bits) 122{ 123 return tcu::Vector<deInt64,2>(toSubpixelCoord(v.x(), bits), toSubpixelCoord(v.y(), bits)); 124} 125 126static tcu::Vector<deInt64,2> toSubpixelVector (const tcu::IVec2& v, int bits) 127{ 128 return tcu::Vector<deInt64,2>(toSubpixelCoord(v.x(), bits), toSubpixelCoord(v.y(), bits)); 129} 130 131#if defined(DE_DEBUG) 132static bool isTheCenterOfTheFragment (const tcu::Vector<deInt64,2>& a, int bits) 133{ 134 const deUint64 pixelSize = 1ll << bits; 135 const deUint64 halfPixel = 1ll << (bits - 1); 136 return ((a.x() & (pixelSize-1)) == halfPixel && 137 (a.y() & (pixelSize-1)) == halfPixel); 138} 139 140static bool inViewport (const tcu::IVec2& p, const tcu::IVec4& viewport) 141{ 142 return p.x() >= viewport.x() && 143 p.y() >= viewport.y() && 144 p.x() < viewport.x() + viewport.z() && 145 p.y() < viewport.y() + viewport.w(); 146} 147#endif // DE_DEBUG 148 149// returns true if vertex is on the left side of the line 150static bool vertexOnLeftSideOfLine (const tcu::Vector<deInt64,2>& p, const SubpixelLineSegment& l) 151{ 152 const tcu::Vector<deInt64,2> u = l.direction(); 153 const tcu::Vector<deInt64,2> v = ( p - l.m_v0); 154 const deInt64 crossProduct = (u.x() * v.y() - u.y() * v.x()); 155 return crossProduct < 0; 156} 157 158// returns true if vertex is on the right side of the line 159static bool vertexOnRightSideOfLine (const tcu::Vector<deInt64,2>& p, const SubpixelLineSegment& l) 160{ 161 const tcu::Vector<deInt64,2> u = l.direction(); 162 const tcu::Vector<deInt64,2> v = ( p - l.m_v0); 163 const deInt64 crossProduct = (u.x() * v.y() - u.y() * v.x()); 164 return crossProduct > 0; 165} 166 167// returns true if vertex is on the line 168static bool vertexOnLine (const tcu::Vector<deInt64,2>& p, const SubpixelLineSegment& l) 169{ 170 const tcu::Vector<deInt64,2> u = l.direction(); 171 const tcu::Vector<deInt64,2> v = ( p - l.m_v0); 172 const deInt64 crossProduct = (u.x() * v.y() - u.y() * v.x()); 173 return crossProduct == 0; // cross product == 0 174} 175 176// returns true if vertex is on the line segment 177static bool vertexOnLineSegment (const tcu::Vector<deInt64,2>& p, const SubpixelLineSegment& l) 178{ 179 if (!vertexOnLine(p, l)) 180 return false; 181 182 const tcu::Vector<deInt64,2> v = l.direction(); 183 const tcu::Vector<deInt64,2> u1 = ( p - l.m_v0); 184 const tcu::Vector<deInt64,2> u2 = ( p - l.m_v1); 185 186 if (v.x() == 0 && v.y() == 0) 187 return false; 188 189 return tcu::dot( v, u1) >= 0 && 190 tcu::dot(-v, u2) >= 0; // dot (A->B, A->V) >= 0 and dot (B->A, B->V) >= 0 191} 192 193static LINE_SIDE getVertexSide (const tcu::Vector<deInt64,2>& v, const SubpixelLineSegment& l) 194{ 195 if (vertexOnLeftSideOfLine(v, l)) 196 return LINE_SIDE_LEFT; 197 else if (vertexOnRightSideOfLine(v, l)) 198 return LINE_SIDE_RIGHT; 199 else if (vertexOnLine(v, l)) 200 return LINE_SIDE_INTERSECT; 201 else 202 { 203 DE_ASSERT(false); 204 return LINE_SIDE_INTERSECT; 205 } 206} 207 208// returns true if angle between line and given cornerExitNormal is in range (-45, 45) 209bool lineInCornerAngleRange (const SubpixelLineSegment& line, const tcu::Vector<deInt64,2>& cornerExitNormal) 210{ 211 // v0 -> v1 has angle difference to cornerExitNormal in range (-45, 45) 212 const tcu::Vector<deInt64,2> v = line.direction(); 213 const deInt64 dotProduct = dot(v, cornerExitNormal); 214 215 // dotProduct > |v1-v0|*|cornerExitNormal|/sqrt(2) 216 if (dotProduct < 0) 217 return false; 218 return 2 * dotProduct * dotProduct > tcu::lengthSquared(v)*tcu::lengthSquared(cornerExitNormal); 219} 220 221// returns true if angle between line and given cornerExitNormal is in range (-135, 135) 222bool lineInCornerOutsideAngleRange (const SubpixelLineSegment& line, const tcu::Vector<deInt64,2>& cornerExitNormal) 223{ 224 // v0 -> v1 has angle difference to cornerExitNormal in range (-135, 135) 225 const tcu::Vector<deInt64,2> v = line.direction(); 226 const deInt64 dotProduct = dot(v, cornerExitNormal); 227 228 // dotProduct > -|v1-v0|*|cornerExitNormal|/sqrt(2) 229 if (dotProduct >= 0) 230 return true; 231 return 2 * (-dotProduct) * (-dotProduct) < tcu::lengthSquared(v)*tcu::lengthSquared(cornerExitNormal); 232} 233 234bool doesLineSegmentExitDiamond (const SubpixelLineSegment& line, const tcu::Vector<deInt64,2>& diamondCenter, int bits) 235{ 236 DE_ASSERT(isTheCenterOfTheFragment(diamondCenter, bits)); 237 238 // Diamond Center is at diamondCenter in subpixel coords 239 240 const deInt64 halfPixel = 1ll << (bits - 1); 241 242 // Reject distant diamonds early 243 { 244 const tcu::Vector<deInt64,2> u = line.direction(); 245 const tcu::Vector<deInt64,2> v = (diamondCenter - line.m_v0); 246 const deInt64 crossProduct = (u.x() * v.y() - u.y() * v.x()); 247 248 // crossProduct = |p| |l| sin(theta) 249 // distanceFromLine = |p| sin(theta) 250 // => distanceFromLine = crossProduct / |l| 251 // 252 // |distanceFromLine| > C 253 // => distanceFromLine^2 > C^2 254 // => crossProduct^2 / |l|^2 > C^2 255 // => crossProduct^2 > |l|^2 * C^2 256 257 const deInt64 floorSqrtMaxInt64 = 3037000499LL; //!< floor(sqrt(MAX_INT64)) 258 259 const deInt64 broadRejectDistance = 2 * halfPixel; 260 const deInt64 broadRejectDistanceSquared = broadRejectDistance * broadRejectDistance; 261 const bool crossProductOverflows = (crossProduct > floorSqrtMaxInt64 || crossProduct < -floorSqrtMaxInt64); 262 const deInt64 crossProductSquared = (crossProductOverflows) ? (0) : (crossProduct * crossProduct); // avoid overflow 263 const deInt64 lineLengthSquared = tcu::lengthSquared(u); 264 const bool limitValueCouldOverflow = ((64 - deClz64(lineLengthSquared)) + (64 - deClz64(broadRejectDistanceSquared))) > 63; 265 const deInt64 limitValue = (limitValueCouldOverflow) ? (0) : (lineLengthSquared * broadRejectDistanceSquared); // avoid overflow 266 267 // only cross overflows 268 if (crossProductOverflows && !limitValueCouldOverflow) 269 return false; 270 271 // both representable 272 if (!crossProductOverflows && !limitValueCouldOverflow) 273 { 274 if (crossProductSquared > limitValue) 275 return false; 276 } 277 } 278 279 const struct DiamondBound 280 { 281 tcu::Vector<deInt64,2> p0; 282 tcu::Vector<deInt64,2> p1; 283 bool edgeInclusive; // would a point on the bound be inside of the region 284 } bounds[] = 285 { 286 { diamondCenter + tcu::Vector<deInt64,2>(0, -halfPixel), diamondCenter + tcu::Vector<deInt64,2>(-halfPixel, 0), false }, 287 { diamondCenter + tcu::Vector<deInt64,2>(-halfPixel, 0), diamondCenter + tcu::Vector<deInt64,2>(0, halfPixel), false }, 288 { diamondCenter + tcu::Vector<deInt64,2>(0, halfPixel), diamondCenter + tcu::Vector<deInt64,2>(halfPixel, 0), true }, 289 { diamondCenter + tcu::Vector<deInt64,2>(halfPixel, 0), diamondCenter + tcu::Vector<deInt64,2>(0, -halfPixel), true }, 290 }; 291 292 const struct DiamondCorners 293 { 294 enum CORNER_EDGE_CASE_BEHAVIOR 295 { 296 CORNER_EDGE_CASE_NONE, // if the line intersects just a corner, no entering or exiting 297 CORNER_EDGE_CASE_HIT, // if the line intersects just a corner, entering and exit 298 CORNER_EDGE_CASE_HIT_FIRST_QUARTER, // if the line intersects just a corner and the line has either endpoint in (+X,-Y) direction (preturbing moves the line inside) 299 CORNER_EDGE_CASE_HIT_SECOND_QUARTER // if the line intersects just a corner and the line has either endpoint in (+X,+Y) direction (preturbing moves the line inside) 300 }; 301 enum CORNER_START_CASE_BEHAVIOR 302 { 303 CORNER_START_CASE_NONE, // the line starting point is outside, no exiting 304 CORNER_START_CASE_OUTSIDE, // exit, if line does not intersect the region (preturbing moves the start point inside) 305 CORNER_START_CASE_POSITIVE_Y_45, // exit, if line the angle of line vector and X-axis is in range (0, 45] in positive Y side. 306 CORNER_START_CASE_NEGATIVE_Y_45 // exit, if line the angle of line vector and X-axis is in range [0, 45] in negative Y side. 307 }; 308 enum CORNER_END_CASE_BEHAVIOR 309 { 310 CORNER_END_CASE_NONE, // end is inside, no exiting (preturbing moves the line end inside) 311 CORNER_END_CASE_DIRECTION, // exit, if line intersected the region (preturbing moves the line end outside) 312 CORNER_END_CASE_DIRECTION_AND_FIRST_QUARTER, // exit, if line intersected the region, or line originates from (+X,-Y) direction (preturbing moves the line end outside) 313 CORNER_END_CASE_DIRECTION_AND_SECOND_QUARTER // exit, if line intersected the region, or line originates from (+X,+Y) direction (preturbing moves the line end outside) 314 }; 315 316 tcu::Vector<deInt64,2> dp; 317 bool pointInclusive; // would a point in this corner intersect with the region 318 CORNER_EDGE_CASE_BEHAVIOR lineBehavior; // would a line segment going through this corner intersect with the region 319 CORNER_START_CASE_BEHAVIOR startBehavior; // how the corner behaves if the start point at the corner 320 CORNER_END_CASE_BEHAVIOR endBehavior; // how the corner behaves if the end point at the corner 321 } corners[] = 322 { 323 { tcu::Vector<deInt64,2>(0, -halfPixel), false, DiamondCorners::CORNER_EDGE_CASE_HIT_SECOND_QUARTER, DiamondCorners::CORNER_START_CASE_POSITIVE_Y_45, DiamondCorners::CORNER_END_CASE_DIRECTION_AND_SECOND_QUARTER}, 324 { tcu::Vector<deInt64,2>(-halfPixel, 0), false, DiamondCorners::CORNER_EDGE_CASE_NONE, DiamondCorners::CORNER_START_CASE_NONE, DiamondCorners::CORNER_END_CASE_DIRECTION }, 325 { tcu::Vector<deInt64,2>(0, halfPixel), false, DiamondCorners::CORNER_EDGE_CASE_HIT_FIRST_QUARTER, DiamondCorners::CORNER_START_CASE_NEGATIVE_Y_45, DiamondCorners::CORNER_END_CASE_DIRECTION_AND_FIRST_QUARTER }, 326 { tcu::Vector<deInt64,2>(halfPixel, 0), true, DiamondCorners::CORNER_EDGE_CASE_HIT, DiamondCorners::CORNER_START_CASE_OUTSIDE, DiamondCorners::CORNER_END_CASE_NONE }, 327 }; 328 329 // Corner cases at the corners 330 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(corners); ++ndx) 331 { 332 const tcu::Vector<deInt64,2> p = diamondCenter + corners[ndx].dp; 333 const bool intersectsAtCorner = LineRasterUtil::vertexOnLineSegment(p, line); 334 335 if (!intersectsAtCorner) 336 continue; 337 338 // line segment body intersects with the corner 339 if (p != line.m_v0 && p != line.m_v1) 340 { 341 if (corners[ndx].lineBehavior == DiamondCorners::CORNER_EDGE_CASE_HIT) 342 return true; 343 344 // endpoint in (+X, -Y) (X or Y may be 0) direction <==> x*y <= 0 345 if (corners[ndx].lineBehavior == DiamondCorners::CORNER_EDGE_CASE_HIT_FIRST_QUARTER && 346 (line.direction().x() * line.direction().y()) <= 0) 347 return true; 348 349 // endpoint in (+X, +Y) (Y > 0) direction <==> x*y > 0 350 if (corners[ndx].lineBehavior == DiamondCorners::CORNER_EDGE_CASE_HIT_SECOND_QUARTER && 351 (line.direction().x() * line.direction().y()) > 0) 352 return true; 353 } 354 355 // line exits the area at the corner 356 if (lineInCornerAngleRange(line, corners[ndx].dp)) 357 { 358 const bool startIsInside = corners[ndx].pointInclusive || p != line.m_v0; 359 const bool endIsOutside = !corners[ndx].pointInclusive || p != line.m_v1; 360 361 // starting point is inside the region and end endpoint is outside 362 if (startIsInside && endIsOutside) 363 return true; 364 } 365 366 // line end is at the corner 367 if (p == line.m_v1) 368 { 369 if (corners[ndx].endBehavior == DiamondCorners::CORNER_END_CASE_DIRECTION || 370 corners[ndx].endBehavior == DiamondCorners::CORNER_END_CASE_DIRECTION_AND_FIRST_QUARTER || 371 corners[ndx].endBehavior == DiamondCorners::CORNER_END_CASE_DIRECTION_AND_SECOND_QUARTER) 372 { 373 // did the line intersect the region 374 if (lineInCornerAngleRange(line, corners[ndx].dp)) 375 return true; 376 } 377 378 // due to the perturbed endpoint, lines at this the angle will cause and enter-exit pair 379 if (corners[ndx].endBehavior == DiamondCorners::CORNER_END_CASE_DIRECTION_AND_FIRST_QUARTER && 380 line.direction().x() < 0 && 381 line.direction().y() > 0) 382 return true; 383 if (corners[ndx].endBehavior == DiamondCorners::CORNER_END_CASE_DIRECTION_AND_SECOND_QUARTER && 384 line.direction().x() > 0 && 385 line.direction().y() > 0) 386 return true; 387 } 388 389 // line start is at the corner 390 if (p == line.m_v0) 391 { 392 if (corners[ndx].startBehavior == DiamondCorners::CORNER_START_CASE_OUTSIDE) 393 { 394 // if the line is not going inside, it will exit 395 if (lineInCornerOutsideAngleRange(line, corners[ndx].dp)) 396 return true; 397 } 398 399 // exit, if line the angle between line vector and X-axis is in range (0, 45] in positive Y side. 400 if (corners[ndx].startBehavior == DiamondCorners::CORNER_START_CASE_POSITIVE_Y_45 && 401 line.direction().x() > 0 && 402 line.direction().y() > 0 && 403 line.direction().y() <= line.direction().x()) 404 return true; 405 406 // exit, if line the angle between line vector and X-axis is in range [0, 45] in negative Y side. 407 if (corners[ndx].startBehavior == DiamondCorners::CORNER_START_CASE_NEGATIVE_Y_45 && 408 line.direction().x() > 0 && 409 line.direction().y() <= 0 && 410 -line.direction().y() <= line.direction().x()) 411 return true; 412 } 413 } 414 415 // Does the line intersect boundary at the left == exits the diamond 416 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(bounds); ++ndx) 417 { 418 const bool startVertexInside = LineRasterUtil::vertexOnLeftSideOfLine (line.m_v0, LineRasterUtil::SubpixelLineSegment(bounds[ndx].p0, bounds[ndx].p1)) || 419 (bounds[ndx].edgeInclusive && LineRasterUtil::vertexOnLine (line.m_v0, LineRasterUtil::SubpixelLineSegment(bounds[ndx].p0, bounds[ndx].p1))); 420 const bool endVertexInside = LineRasterUtil::vertexOnLeftSideOfLine (line.m_v1, LineRasterUtil::SubpixelLineSegment(bounds[ndx].p0, bounds[ndx].p1)) || 421 (bounds[ndx].edgeInclusive && LineRasterUtil::vertexOnLine (line.m_v1, LineRasterUtil::SubpixelLineSegment(bounds[ndx].p0, bounds[ndx].p1))); 422 423 // start must be on inside this half space (left or at the inclusive boundary) 424 if (!startVertexInside) 425 continue; 426 427 // end must be outside of this half-space (right or at non-inclusive boundary) 428 if (endVertexInside) 429 continue; 430 431 // Does the line via v0 and v1 intersect the line segment p0-p1 432 // <==> p0 and p1 are the different sides (LEFT, RIGHT) of the v0-v1 line. 433 // Corners are not allowed, they are checked already 434 LineRasterUtil::LINE_SIDE sideP0 = LineRasterUtil::getVertexSide(bounds[ndx].p0, line); 435 LineRasterUtil::LINE_SIDE sideP1 = LineRasterUtil::getVertexSide(bounds[ndx].p1, line); 436 437 if (sideP0 != LineRasterUtil::LINE_SIDE_INTERSECT && 438 sideP1 != LineRasterUtil::LINE_SIDE_INTERSECT && 439 sideP0 != sideP1) 440 return true; 441 } 442 443 return false; 444} 445 446} // LineRasterUtil 447 448TriangleRasterizer::TriangleRasterizer (const tcu::IVec4& viewport, const int numSamples, const RasterizationState& state, const int subpixelBits) 449 : m_viewport (viewport) 450 , m_numSamples (numSamples) 451 , m_winding (state.winding) 452 , m_horizontalFill (state.horizontalFill) 453 , m_verticalFill (state.verticalFill) 454 , m_subpixelBits (subpixelBits) 455 , m_face (FACETYPE_LAST) 456 , m_viewportOrientation (state.viewportOrientation) 457{ 458} 459 460/*--------------------------------------------------------------------*//*! 461 * \brief Initialize triangle rasterization 462 * \param v0 Screen-space coordinates (x, y, z) and 1/w for vertex 0. 463 * \param v1 Screen-space coordinates (x, y, z) and 1/w for vertex 1. 464 * \param v2 Screen-space coordinates (x, y, z) and 1/w for vertex 2. 465 *//*--------------------------------------------------------------------*/ 466void TriangleRasterizer::init (const tcu::Vec4& v0, const tcu::Vec4& v1, const tcu::Vec4& v2) 467{ 468 m_v0 = v0; 469 m_v1 = v1; 470 m_v2 = v2; 471 472 // Positions in fixed-point coordinates. 473 const deInt64 x0 = toSubpixelCoord(v0.x(), m_subpixelBits); 474 const deInt64 y0 = toSubpixelCoord(v0.y(), m_subpixelBits); 475 const deInt64 x1 = toSubpixelCoord(v1.x(), m_subpixelBits); 476 const deInt64 y1 = toSubpixelCoord(v1.y(), m_subpixelBits); 477 const deInt64 x2 = toSubpixelCoord(v2.x(), m_subpixelBits); 478 const deInt64 y2 = toSubpixelCoord(v2.y(), m_subpixelBits); 479 480 // Initialize edge functions. 481 if (m_winding == WINDING_CCW) 482 { 483 initEdgeCCW(m_edge01, m_horizontalFill, m_verticalFill, x0, y0, x1, y1); 484 initEdgeCCW(m_edge12, m_horizontalFill, m_verticalFill, x1, y1, x2, y2); 485 initEdgeCCW(m_edge20, m_horizontalFill, m_verticalFill, x2, y2, x0, y0); 486 } 487 else 488 { 489 // Reverse edges 490 initEdgeCCW(m_edge01, m_horizontalFill, m_verticalFill, x1, y1, x0, y0); 491 initEdgeCCW(m_edge12, m_horizontalFill, m_verticalFill, x2, y2, x1, y1); 492 initEdgeCCW(m_edge20, m_horizontalFill, m_verticalFill, x0, y0, x2, y2); 493 } 494 495 // Determine face. 496 const deInt64 s = evaluateEdge(m_edge01, x2, y2); 497 const bool positiveArea = (m_winding == WINDING_CCW) ? (s > 0) : (s < 0); 498 499 if (m_viewportOrientation == VIEWPORTORIENTATION_UPPER_LEFT) 500 m_face = positiveArea ? FACETYPE_BACK : FACETYPE_FRONT; 501 else 502 m_face = positiveArea ? FACETYPE_FRONT : FACETYPE_BACK; 503 504 if (!positiveArea) 505 { 506 // Reverse edges so that we can use CCW area tests & interpolation 507 reverseEdge(m_edge01); 508 reverseEdge(m_edge12); 509 reverseEdge(m_edge20); 510 } 511 512 // Bounding box 513 const deInt64 xMin = de::min(de::min(x0, x1), x2); 514 const deInt64 xMax = de::max(de::max(x0, x1), x2); 515 const deInt64 yMin = de::min(de::min(y0, y1), y2); 516 const deInt64 yMax = de::max(de::max(y0, y1), y2); 517 518 m_bboxMin.x() = floorSubpixelToPixelCoord (xMin, m_subpixelBits, m_horizontalFill == FILL_LEFT); 519 m_bboxMin.y() = floorSubpixelToPixelCoord (yMin, m_subpixelBits, m_verticalFill == FILL_BOTTOM); 520 m_bboxMax.x() = ceilSubpixelToPixelCoord (xMax, m_subpixelBits, m_horizontalFill == FILL_RIGHT); 521 m_bboxMax.y() = ceilSubpixelToPixelCoord (yMax, m_subpixelBits, m_verticalFill == FILL_TOP); 522 523 // Clamp to viewport 524 const int wX0 = m_viewport.x(); 525 const int wY0 = m_viewport.y(); 526 const int wX1 = wX0 + m_viewport.z() - 1; 527 const int wY1 = wY0 + m_viewport.w() -1; 528 529 m_bboxMin.x() = de::clamp(m_bboxMin.x(), wX0, wX1); 530 m_bboxMin.y() = de::clamp(m_bboxMin.y(), wY0, wY1); 531 m_bboxMax.x() = de::clamp(m_bboxMax.x(), wX0, wX1); 532 m_bboxMax.y() = de::clamp(m_bboxMax.y(), wY0, wY1); 533 534 m_curPos = m_bboxMin; 535} 536 537void TriangleRasterizer::rasterizeSingleSample (FragmentPacket* const fragmentPackets, float* const depthValues, const int maxFragmentPackets, int& numPacketsRasterized) 538{ 539 DE_ASSERT(maxFragmentPackets > 0); 540 541 const deUint64 halfPixel = 1ll << (m_subpixelBits - 1); 542 int packetNdx = 0; 543 544 // For depth interpolation; given barycentrics A, B, C = (1 - A - B) 545 // we can reformulate the usual z = z0*A + z1*B + z2*C into more 546 // stable equation z = A*(z0 - z2) + B*(z1 - z2) + z2. 547 const float za = m_v0.z()-m_v2.z(); 548 const float zb = m_v1.z()-m_v2.z(); 549 const float zc = m_v2.z(); 550 551 while (m_curPos.y() <= m_bboxMax.y() && packetNdx < maxFragmentPackets) 552 { 553 const int x0 = m_curPos.x(); 554 const int y0 = m_curPos.y(); 555 556 // Subpixel coords 557 const deInt64 sx0 = toSubpixelCoord(x0, m_subpixelBits) + halfPixel; 558 const deInt64 sx1 = toSubpixelCoord(x0+1, m_subpixelBits) + halfPixel; 559 const deInt64 sy0 = toSubpixelCoord(y0, m_subpixelBits) + halfPixel; 560 const deInt64 sy1 = toSubpixelCoord(y0+1, m_subpixelBits) + halfPixel; 561 562 const deInt64 sx[4] = { sx0, sx1, sx0, sx1 }; 563 const deInt64 sy[4] = { sy0, sy0, sy1, sy1 }; 564 565 // Viewport test 566 const bool outX1 = x0+1 == m_viewport.x()+m_viewport.z(); 567 const bool outY1 = y0+1 == m_viewport.y()+m_viewport.w(); 568 569 DE_ASSERT(x0 < m_viewport.x()+m_viewport.z()); 570 DE_ASSERT(y0 < m_viewport.y()+m_viewport.w()); 571 572 // Edge values 573 tcu::Vector<deInt64, 4> e01; 574 tcu::Vector<deInt64, 4> e12; 575 tcu::Vector<deInt64, 4> e20; 576 577 // Coverage 578 deUint64 coverage = 0; 579 580 // Evaluate edge values 581 for (int i = 0; i < 4; i++) 582 { 583 e01[i] = evaluateEdge(m_edge01, sx[i], sy[i]); 584 e12[i] = evaluateEdge(m_edge12, sx[i], sy[i]); 585 e20[i] = evaluateEdge(m_edge20, sx[i], sy[i]); 586 } 587 588 // Compute coverage mask 589 coverage = setCoverageValue(coverage, 1, 0, 0, 0, isInsideCCW(m_edge01, e01[0]) && isInsideCCW(m_edge12, e12[0]) && isInsideCCW(m_edge20, e20[0])); 590 coverage = setCoverageValue(coverage, 1, 1, 0, 0, !outX1 && isInsideCCW(m_edge01, e01[1]) && isInsideCCW(m_edge12, e12[1]) && isInsideCCW(m_edge20, e20[1])); 591 coverage = setCoverageValue(coverage, 1, 0, 1, 0, !outY1 && isInsideCCW(m_edge01, e01[2]) && isInsideCCW(m_edge12, e12[2]) && isInsideCCW(m_edge20, e20[2])); 592 coverage = setCoverageValue(coverage, 1, 1, 1, 0, !outX1 && !outY1 && isInsideCCW(m_edge01, e01[3]) && isInsideCCW(m_edge12, e12[3]) && isInsideCCW(m_edge20, e20[3])); 593 594 // Advance to next location 595 m_curPos.x() += 2; 596 if (m_curPos.x() > m_bboxMax.x()) 597 { 598 m_curPos.y() += 2; 599 m_curPos.x() = m_bboxMin.x(); 600 } 601 602 if (coverage == 0) 603 continue; // Discard. 604 605 // Floating-point edge values for barycentrics etc. 606 const tcu::Vec4 e01f = e01.asFloat(); 607 const tcu::Vec4 e12f = e12.asFloat(); 608 const tcu::Vec4 e20f = e20.asFloat(); 609 610 // Compute depth values. 611 if (depthValues) 612 { 613 const tcu::Vec4 edgeSum = e01f + e12f + e20f; 614 const tcu::Vec4 z0 = e12f / edgeSum; 615 const tcu::Vec4 z1 = e20f / edgeSum; 616 617 depthValues[packetNdx*4+0] = z0[0]*za + z1[0]*zb + zc; 618 depthValues[packetNdx*4+1] = z0[1]*za + z1[1]*zb + zc; 619 depthValues[packetNdx*4+2] = z0[2]*za + z1[2]*zb + zc; 620 depthValues[packetNdx*4+3] = z0[3]*za + z1[3]*zb + zc; 621 } 622 623 // Compute barycentrics and write out fragment packet 624 { 625 FragmentPacket& packet = fragmentPackets[packetNdx]; 626 627 const tcu::Vec4 b0 = e12f * m_v0.w(); 628 const tcu::Vec4 b1 = e20f * m_v1.w(); 629 const tcu::Vec4 b2 = e01f * m_v2.w(); 630 const tcu::Vec4 bSum = b0 + b1 + b2; 631 632 packet.position = tcu::IVec2(x0, y0); 633 packet.coverage = coverage; 634 packet.barycentric[0] = b0 / bSum; 635 packet.barycentric[1] = b1 / bSum; 636 packet.barycentric[2] = 1.0f - packet.barycentric[0] - packet.barycentric[1]; 637 638 packetNdx += 1; 639 } 640 } 641 642 DE_ASSERT(packetNdx <= maxFragmentPackets); 643 numPacketsRasterized = packetNdx; 644} 645 646// Sample positions - ordered as (x, y) list. 647static const float s_samplePts2[] = 648{ 649 0.3f, 0.3f, 650 0.7f, 0.7f 651}; 652 653static const float s_samplePts4[] = 654{ 655 0.25f, 0.25f, 656 0.75f, 0.25f, 657 0.25f, 0.75f, 658 0.75f, 0.75f 659}; 660 661static const float s_samplePts8[] = 662{ 663 7.f / 16.f, 9.f / 16.f, 664 9.f / 16.f, 13.f / 16.f, 665 11.f / 16.f, 3.f / 16.f, 666 13.f / 16.f, 11.f / 16.f, 667 1.f / 16.f, 7.f / 16.f, 668 5.f / 16.f, 1.f / 16.f, 669 15.f / 16.f, 5.f / 16.f, 670 3.f / 16.f, 15.f / 16.f 671}; 672 673static const float s_samplePts16[] = 674{ 675 1.f / 8.f, 1.f / 8.f, 676 3.f / 8.f, 1.f / 8.f, 677 5.f / 8.f, 1.f / 8.f, 678 7.f / 8.f, 1.f / 8.f, 679 1.f / 8.f, 3.f / 8.f, 680 3.f / 8.f, 3.f / 8.f, 681 5.f / 8.f, 3.f / 8.f, 682 7.f / 8.f, 3.f / 8.f, 683 1.f / 8.f, 5.f / 8.f, 684 3.f / 8.f, 5.f / 8.f, 685 5.f / 8.f, 5.f / 8.f, 686 7.f / 8.f, 5.f / 8.f, 687 1.f / 8.f, 7.f / 8.f, 688 3.f / 8.f, 7.f / 8.f, 689 5.f / 8.f, 7.f / 8.f, 690 7.f / 8.f, 7.f / 8.f 691}; 692 693template<int NumSamples> 694void TriangleRasterizer::rasterizeMultiSample (FragmentPacket* const fragmentPackets, float* const depthValues, const int maxFragmentPackets, int& numPacketsRasterized) 695{ 696 DE_ASSERT(maxFragmentPackets > 0); 697 698 // Big enough to hold maximum multisample count 699 deInt64 samplePos[DE_LENGTH_OF_ARRAY(s_samplePts16)]; 700 const float * samplePts = DE_NULL; 701 const deUint64 halfPixel = 1ll << (m_subpixelBits - 1); 702 int packetNdx = 0; 703 704 // For depth interpolation, see rasterizeSingleSample 705 const float za = m_v0.z()-m_v2.z(); 706 const float zb = m_v1.z()-m_v2.z(); 707 const float zc = m_v2.z(); 708 709 switch (NumSamples) 710 { 711 case 2: samplePts = s_samplePts2; break; 712 case 4: samplePts = s_samplePts4; break; 713 case 8: samplePts = s_samplePts8; break; 714 case 16: samplePts = s_samplePts16; break; 715 default: 716 DE_ASSERT(false); 717 } 718 719 for (int c = 0; c < NumSamples * 2; ++c) 720 samplePos[c] = toSubpixelCoord(samplePts[c], m_subpixelBits); 721 722 while (m_curPos.y() <= m_bboxMax.y() && packetNdx < maxFragmentPackets) 723 { 724 const int x0 = m_curPos.x(); 725 const int y0 = m_curPos.y(); 726 727 // Base subpixel coords 728 const deInt64 sx0 = toSubpixelCoord(x0, m_subpixelBits); 729 const deInt64 sx1 = toSubpixelCoord(x0+1, m_subpixelBits); 730 const deInt64 sy0 = toSubpixelCoord(y0, m_subpixelBits); 731 const deInt64 sy1 = toSubpixelCoord(y0+1, m_subpixelBits); 732 733 const deInt64 sx[4] = { sx0, sx1, sx0, sx1 }; 734 const deInt64 sy[4] = { sy0, sy0, sy1, sy1 }; 735 736 // Viewport test 737 const bool outX1 = x0+1 == m_viewport.x()+m_viewport.z(); 738 const bool outY1 = y0+1 == m_viewport.y()+m_viewport.w(); 739 740 DE_ASSERT(x0 < m_viewport.x()+m_viewport.z()); 741 DE_ASSERT(y0 < m_viewport.y()+m_viewport.w()); 742 743 // Edge values 744 tcu::Vector<deInt64, 4> e01[NumSamples]; 745 tcu::Vector<deInt64, 4> e12[NumSamples]; 746 tcu::Vector<deInt64, 4> e20[NumSamples]; 747 748 // Coverage 749 deUint64 coverage = 0; 750 751 // Evaluate edge values at sample positions 752 for (int sampleNdx = 0; sampleNdx < NumSamples; sampleNdx++) 753 { 754 const deInt64 ox = samplePos[sampleNdx*2 + 0]; 755 const deInt64 oy = samplePos[sampleNdx*2 + 1]; 756 757 for (int fragNdx = 0; fragNdx < 4; fragNdx++) 758 { 759 e01[sampleNdx][fragNdx] = evaluateEdge(m_edge01, sx[fragNdx] + ox, sy[fragNdx] + oy); 760 e12[sampleNdx][fragNdx] = evaluateEdge(m_edge12, sx[fragNdx] + ox, sy[fragNdx] + oy); 761 e20[sampleNdx][fragNdx] = evaluateEdge(m_edge20, sx[fragNdx] + ox, sy[fragNdx] + oy); 762 } 763 } 764 765 // Compute coverage mask 766 for (int sampleNdx = 0; sampleNdx < NumSamples; sampleNdx++) 767 { 768 coverage = setCoverageValue(coverage, NumSamples, 0, 0, sampleNdx, isInsideCCW(m_edge01, e01[sampleNdx][0]) && isInsideCCW(m_edge12, e12[sampleNdx][0]) && isInsideCCW(m_edge20, e20[sampleNdx][0])); 769 coverage = setCoverageValue(coverage, NumSamples, 1, 0, sampleNdx, !outX1 && isInsideCCW(m_edge01, e01[sampleNdx][1]) && isInsideCCW(m_edge12, e12[sampleNdx][1]) && isInsideCCW(m_edge20, e20[sampleNdx][1])); 770 coverage = setCoverageValue(coverage, NumSamples, 0, 1, sampleNdx, !outY1 && isInsideCCW(m_edge01, e01[sampleNdx][2]) && isInsideCCW(m_edge12, e12[sampleNdx][2]) && isInsideCCW(m_edge20, e20[sampleNdx][2])); 771 coverage = setCoverageValue(coverage, NumSamples, 1, 1, sampleNdx, !outX1 && !outY1 && isInsideCCW(m_edge01, e01[sampleNdx][3]) && isInsideCCW(m_edge12, e12[sampleNdx][3]) && isInsideCCW(m_edge20, e20[sampleNdx][3])); 772 } 773 774 // Advance to next location 775 m_curPos.x() += 2; 776 if (m_curPos.x() > m_bboxMax.x()) 777 { 778 m_curPos.y() += 2; 779 m_curPos.x() = m_bboxMin.x(); 780 } 781 782 if (coverage == 0) 783 continue; // Discard. 784 785 // Compute depth values. 786 if (depthValues) 787 { 788 for (int sampleNdx = 0; sampleNdx < NumSamples; sampleNdx++) 789 { 790 // Floating-point edge values at sample coordinates. 791 const tcu::Vec4& e01f = e01[sampleNdx].asFloat(); 792 const tcu::Vec4& e12f = e12[sampleNdx].asFloat(); 793 const tcu::Vec4& e20f = e20[sampleNdx].asFloat(); 794 795 const tcu::Vec4 edgeSum = e01f + e12f + e20f; 796 const tcu::Vec4 z0 = e12f / edgeSum; 797 const tcu::Vec4 z1 = e20f / edgeSum; 798 799 depthValues[(packetNdx*4+0)*NumSamples + sampleNdx] = z0[0]*za + z1[0]*zb + zc; 800 depthValues[(packetNdx*4+1)*NumSamples + sampleNdx] = z0[1]*za + z1[1]*zb + zc; 801 depthValues[(packetNdx*4+2)*NumSamples + sampleNdx] = z0[2]*za + z1[2]*zb + zc; 802 depthValues[(packetNdx*4+3)*NumSamples + sampleNdx] = z0[3]*za + z1[3]*zb + zc; 803 } 804 } 805 806 // Compute barycentrics and write out fragment packet 807 { 808 FragmentPacket& packet = fragmentPackets[packetNdx]; 809 810 // Floating-point edge values at pixel center. 811 tcu::Vec4 e01f; 812 tcu::Vec4 e12f; 813 tcu::Vec4 e20f; 814 815 for (int i = 0; i < 4; i++) 816 { 817 e01f[i] = float(evaluateEdge(m_edge01, sx[i] + halfPixel, sy[i] + halfPixel)); 818 e12f[i] = float(evaluateEdge(m_edge12, sx[i] + halfPixel, sy[i] + halfPixel)); 819 e20f[i] = float(evaluateEdge(m_edge20, sx[i] + halfPixel, sy[i] + halfPixel)); 820 } 821 822 // Barycentrics & scale. 823 const tcu::Vec4 b0 = e12f * m_v0.w(); 824 const tcu::Vec4 b1 = e20f * m_v1.w(); 825 const tcu::Vec4 b2 = e01f * m_v2.w(); 826 const tcu::Vec4 bSum = b0 + b1 + b2; 827 828 packet.position = tcu::IVec2(x0, y0); 829 packet.coverage = coverage; 830 packet.barycentric[0] = b0 / bSum; 831 packet.barycentric[1] = b1 / bSum; 832 packet.barycentric[2] = 1.0f - packet.barycentric[0] - packet.barycentric[1]; 833 834 packetNdx += 1; 835 } 836 } 837 838 DE_ASSERT(packetNdx <= maxFragmentPackets); 839 numPacketsRasterized = packetNdx; 840} 841 842void TriangleRasterizer::rasterize (FragmentPacket* const fragmentPackets, float* const depthValues, const int maxFragmentPackets, int& numPacketsRasterized) 843{ 844 DE_ASSERT(maxFragmentPackets > 0); 845 846 switch (m_numSamples) 847 { 848 case 1: rasterizeSingleSample (fragmentPackets, depthValues, maxFragmentPackets, numPacketsRasterized); break; 849 case 2: rasterizeMultiSample<2> (fragmentPackets, depthValues, maxFragmentPackets, numPacketsRasterized); break; 850 case 4: rasterizeMultiSample<4> (fragmentPackets, depthValues, maxFragmentPackets, numPacketsRasterized); break; 851 case 8: rasterizeMultiSample<8> (fragmentPackets, depthValues, maxFragmentPackets, numPacketsRasterized); break; 852 case 16: rasterizeMultiSample<16> (fragmentPackets, depthValues, maxFragmentPackets, numPacketsRasterized); break; 853 default: 854 DE_ASSERT(DE_FALSE); 855 } 856} 857 858SingleSampleLineRasterizer::SingleSampleLineRasterizer (const tcu::IVec4& viewport, const int subpixelBits) 859 : m_viewport (viewport) 860 , m_subpixelBits (subpixelBits) 861 , m_curRowFragment (0) 862 , m_lineWidth (0.0f) 863 , m_stippleCounter (0) 864{ 865} 866 867SingleSampleLineRasterizer::~SingleSampleLineRasterizer (void) 868{ 869} 870 871void SingleSampleLineRasterizer::init (const tcu::Vec4& v0, const tcu::Vec4& v1, float lineWidth, deUint32 stippleFactor, deUint16 stipplePattern) 872{ 873 const bool isXMajor = de::abs((v1 - v0).x()) >= de::abs((v1 - v0).y()); 874 875 // Bounding box \note: with wide lines, the line is actually moved as in the spec 876 const deInt32 lineWidthPixels = (lineWidth > 1.0f) ? (deInt32)floor(lineWidth + 0.5f) : 1; 877 878 const tcu::Vector<deInt64,2> widthOffset = (isXMajor ? tcu::Vector<deInt64,2>(0, -1) : tcu::Vector<deInt64,2>(-1, 0)) * (toSubpixelCoord(lineWidthPixels - 1, m_subpixelBits) / 2); 879 880 const deInt64 x0 = toSubpixelCoord(v0.x(), m_subpixelBits) + widthOffset.x(); 881 const deInt64 y0 = toSubpixelCoord(v0.y(), m_subpixelBits) + widthOffset.y(); 882 const deInt64 x1 = toSubpixelCoord(v1.x(), m_subpixelBits) + widthOffset.x(); 883 const deInt64 y1 = toSubpixelCoord(v1.y(), m_subpixelBits) + widthOffset.y(); 884 885 // line endpoints might be perturbed, add some margin 886 const deInt64 xMin = de::min(x0, x1) - toSubpixelCoord(1, m_subpixelBits); 887 const deInt64 xMax = de::max(x0, x1) + toSubpixelCoord(1, m_subpixelBits); 888 const deInt64 yMin = de::min(y0, y1) - toSubpixelCoord(1, m_subpixelBits); 889 const deInt64 yMax = de::max(y0, y1) + toSubpixelCoord(1, m_subpixelBits); 890 891 // Remove invisible area 892 893 if (isXMajor) 894 { 895 // clamp to viewport in major direction 896 m_bboxMin.x() = de::clamp(floorSubpixelToPixelCoord(xMin, m_subpixelBits, true), m_viewport.x(), m_viewport.x() + m_viewport.z() - 1); 897 m_bboxMax.x() = de::clamp(ceilSubpixelToPixelCoord (xMax, m_subpixelBits, true), m_viewport.x(), m_viewport.x() + m_viewport.z() - 1); 898 899 // clamp to padded viewport in minor direction (wide lines might bleed over viewport in minor direction) 900 m_bboxMin.y() = de::clamp(floorSubpixelToPixelCoord(yMin, m_subpixelBits, true), m_viewport.y() - lineWidthPixels, m_viewport.y() + m_viewport.w() - 1); 901 m_bboxMax.y() = de::clamp(ceilSubpixelToPixelCoord (yMax, m_subpixelBits, true), m_viewport.y() - lineWidthPixels, m_viewport.y() + m_viewport.w() - 1); 902 } 903 else 904 { 905 // clamp to viewport in major direction 906 m_bboxMin.y() = de::clamp(floorSubpixelToPixelCoord(yMin, m_subpixelBits, true), m_viewport.y(), m_viewport.y() + m_viewport.w() - 1); 907 m_bboxMax.y() = de::clamp(ceilSubpixelToPixelCoord (yMax, m_subpixelBits, true), m_viewport.y(), m_viewport.y() + m_viewport.w() - 1); 908 909 // clamp to padded viewport in minor direction (wide lines might bleed over viewport in minor direction) 910 m_bboxMin.x() = de::clamp(floorSubpixelToPixelCoord(xMin, m_subpixelBits, true), m_viewport.x() - lineWidthPixels, m_viewport.x() + m_viewport.z() - 1); 911 m_bboxMax.x() = de::clamp(ceilSubpixelToPixelCoord (xMax, m_subpixelBits, true), m_viewport.x() - lineWidthPixels, m_viewport.x() + m_viewport.z() - 1); 912 } 913 914 m_lineWidth = lineWidth; 915 916 m_v0 = v0; 917 m_v1 = v1; 918 919 // Choose direction of traversal and whether to start at bbox min or max. Direction matters 920 // for the stipple counter. 921 int xDelta = (m_v1 - m_v0).x() > 0 ? 1 : -1; 922 int yDelta = (m_v1 - m_v0).y() > 0 ? 1 : -1; 923 924 m_curPos.x() = xDelta > 0 ? m_bboxMin.x() : m_bboxMax.x(); 925 m_curPos.y() = yDelta > 0 ? m_bboxMin.y() : m_bboxMax.y(); 926 927 m_curRowFragment = 0; 928 m_stippleFactor = stippleFactor; 929 m_stipplePattern = stipplePattern; 930} 931 932void SingleSampleLineRasterizer::rasterize (FragmentPacket* const fragmentPackets, float* const depthValues, const int maxFragmentPackets, int& numPacketsRasterized) 933{ 934 DE_ASSERT(maxFragmentPackets > 0); 935 936 const deInt64 halfPixel = 1ll << (m_subpixelBits - 1); 937 const deInt32 lineWidth = (m_lineWidth > 1.0f) ? deFloorFloatToInt32(m_lineWidth + 0.5f) : 1; 938 const bool isXMajor = de::abs((m_v1 - m_v0).x()) >= de::abs((m_v1 - m_v0).y()); 939 const tcu::IVec2 minorDirection = (isXMajor) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0)); 940 const int minViewportLimit = (isXMajor) ? (m_viewport.y()) : (m_viewport.x()); 941 const int maxViewportLimit = (isXMajor) ? (m_viewport.y() + m_viewport.w()) : (m_viewport.x() + m_viewport.z()); 942 const tcu::Vector<deInt64,2> widthOffset = -minorDirection.cast<deInt64>() * (toSubpixelCoord(lineWidth - 1, m_subpixelBits) / 2); 943 const tcu::Vector<deInt64,2> pa = LineRasterUtil::toSubpixelVector(m_v0.xy(), m_subpixelBits) + widthOffset; 944 const tcu::Vector<deInt64,2> pb = LineRasterUtil::toSubpixelVector(m_v1.xy(), m_subpixelBits) + widthOffset; 945 const LineRasterUtil::SubpixelLineSegment line = LineRasterUtil::SubpixelLineSegment(pa, pb); 946 947 int packetNdx = 0; 948 int xDelta = (m_v1 - m_v0).x() > 0 ? 1 : -1; 949 int yDelta = (m_v1 - m_v0).y() > 0 ? 1 : -1; 950 951 while (m_curPos.y() <= m_bboxMax.y() && m_curPos.y() >= m_bboxMin.y() && packetNdx < maxFragmentPackets) 952 { 953 const tcu::Vector<deInt64,2> diamondPosition = LineRasterUtil::toSubpixelVector(m_curPos, m_subpixelBits) + tcu::Vector<deInt64,2>(halfPixel,halfPixel); 954 955 // Should current fragment be drawn? == does the segment exit this diamond? 956 if (LineRasterUtil::doesLineSegmentExitDiamond(line, diamondPosition, m_subpixelBits)) 957 { 958 const tcu::Vector<deInt64,2> pr = diamondPosition; 959 const float t = tcu::dot((pr - pa).asFloat(), (pb - pa).asFloat()) / tcu::lengthSquared(pb.asFloat() - pa.asFloat()); 960 961 // Rasterize on only fragments that are would end up in the viewport (i.e. visible) 962 const int fragmentLocation = (isXMajor) ? (m_curPos.y()) : (m_curPos.x()); 963 const int rowFragBegin = de::max(0, minViewportLimit - fragmentLocation); 964 const int rowFragEnd = de::min(maxViewportLimit - fragmentLocation, lineWidth); 965 966 int stippleBit = (m_stippleCounter / m_stippleFactor) % 16; 967 bool stipplePass = (m_stipplePattern & (1 << stippleBit)) != 0; 968 m_stippleCounter++; 969 970 if (stipplePass) 971 { 972 // Wide lines require multiple fragments. 973 for (; rowFragBegin + m_curRowFragment < rowFragEnd; m_curRowFragment++) 974 { 975 const int replicationId = rowFragBegin + m_curRowFragment; 976 const tcu::IVec2 fragmentPos = m_curPos + minorDirection * replicationId; 977 978 // We only rasterize visible area 979 DE_ASSERT(LineRasterUtil::inViewport(fragmentPos, m_viewport)); 980 981 // Compute depth values. 982 if (depthValues) 983 { 984 const float za = m_v0.z(); 985 const float zb = m_v1.z(); 986 987 depthValues[packetNdx*4+0] = (1 - t) * za + t * zb; 988 depthValues[packetNdx*4+1] = 0; 989 depthValues[packetNdx*4+2] = 0; 990 depthValues[packetNdx*4+3] = 0; 991 } 992 993 { 994 // output this fragment 995 // \note In order to make consistent output with multisampled line rasterization, output "barycentric" coordinates 996 FragmentPacket& packet = fragmentPackets[packetNdx]; 997 998 const tcu::Vec4 b0 = tcu::Vec4(1 - t); 999 const tcu::Vec4 b1 = tcu::Vec4(t); 1000 const tcu::Vec4 ooSum = 1.0f / (b0 + b1); 1001 1002 packet.position = fragmentPos; 1003 packet.coverage = getCoverageBit(1, 0, 0, 0); 1004 packet.barycentric[0] = b0 * ooSum; 1005 packet.barycentric[1] = b1 * ooSum; 1006 packet.barycentric[2] = tcu::Vec4(0.0f); 1007 1008 packetNdx += 1; 1009 } 1010 1011 if (packetNdx == maxFragmentPackets) 1012 { 1013 m_curRowFragment++; // don't redraw this fragment again next time 1014 m_stippleCounter--; // reuse same stipple counter next time 1015 numPacketsRasterized = packetNdx; 1016 return; 1017 } 1018 } 1019 1020 m_curRowFragment = 0; 1021 } 1022 } 1023 1024 m_curPos.x() += xDelta; 1025 if (m_curPos.x() > m_bboxMax.x() || m_curPos.x() < m_bboxMin.x()) 1026 { 1027 m_curPos.y() += yDelta; 1028 m_curPos.x() = xDelta > 0 ? m_bboxMin.x() : m_bboxMax.x(); 1029 } 1030 } 1031 1032 DE_ASSERT(packetNdx <= maxFragmentPackets); 1033 numPacketsRasterized = packetNdx; 1034} 1035 1036MultiSampleLineRasterizer::MultiSampleLineRasterizer (const int numSamples, const tcu::IVec4& viewport, const int subpixelBits) 1037 : m_numSamples (numSamples) 1038 , m_triangleRasterizer0 (viewport, m_numSamples, RasterizationState(), subpixelBits) 1039 , m_triangleRasterizer1 (viewport, m_numSamples, RasterizationState(), subpixelBits) 1040{ 1041} 1042 1043MultiSampleLineRasterizer::~MultiSampleLineRasterizer () 1044{ 1045} 1046 1047void MultiSampleLineRasterizer::init (const tcu::Vec4& v0, const tcu::Vec4& v1, float lineWidth) 1048{ 1049 // allow creation of single sampled rasterizer objects but do not allow using them 1050 DE_ASSERT(m_numSamples > 1); 1051 1052 const tcu::Vec2 lineVec = tcu::Vec2(tcu::Vec4(v1).xy()) - tcu::Vec2(tcu::Vec4(v0).xy()); 1053 const tcu::Vec2 normal2 = tcu::normalize(tcu::Vec2(-lineVec[1], lineVec[0])); 1054 const tcu::Vec4 normal4 = tcu::Vec4(normal2.x(), normal2.y(), 0, 0); 1055 const float offset = lineWidth / 2.0f; 1056 1057 const tcu::Vec4 p0 = v0 + normal4 * offset; 1058 const tcu::Vec4 p1 = v0 - normal4 * offset; 1059 const tcu::Vec4 p2 = v1 - normal4 * offset; 1060 const tcu::Vec4 p3 = v1 + normal4 * offset; 1061 1062 // Edge 0 -> 1 is always along the line and edge 1 -> 2 is in 90 degree angle to the line 1063 m_triangleRasterizer0.init(p0, p3, p2); 1064 m_triangleRasterizer1.init(p2, p1, p0); 1065} 1066 1067void MultiSampleLineRasterizer::rasterize (FragmentPacket* const fragmentPackets, float* const depthValues, const int maxFragmentPackets, int& numPacketsRasterized) 1068{ 1069 DE_ASSERT(maxFragmentPackets > 0); 1070 1071 m_triangleRasterizer0.rasterize(fragmentPackets, depthValues, maxFragmentPackets, numPacketsRasterized); 1072 1073 // Remove 3rd barycentric value and rebalance. Lines do not have non-zero barycentric at index 2 1074 for (int packNdx = 0; packNdx < numPacketsRasterized; ++packNdx) 1075 for (int fragNdx = 0; fragNdx < 4; fragNdx++) 1076 { 1077 float removedValue = fragmentPackets[packNdx].barycentric[2][fragNdx]; 1078 fragmentPackets[packNdx].barycentric[2][fragNdx] = 0.0f; 1079 fragmentPackets[packNdx].barycentric[1][fragNdx] += removedValue; 1080 } 1081 1082 // rasterizer 0 filled the whole buffer? 1083 if (numPacketsRasterized == maxFragmentPackets) 1084 return; 1085 1086 { 1087 FragmentPacket* const nextFragmentPackets = fragmentPackets + numPacketsRasterized; 1088 float* nextDepthValues = (depthValues) ? (depthValues+4*numPacketsRasterized*m_numSamples) : (DE_NULL); 1089 int numPacketsRasterized2 = 0; 1090 1091 m_triangleRasterizer1.rasterize(nextFragmentPackets, nextDepthValues, maxFragmentPackets - numPacketsRasterized, numPacketsRasterized2); 1092 1093 numPacketsRasterized += numPacketsRasterized2; 1094 1095 // Fix swapped barycentrics in the second triangle 1096 for (int packNdx = 0; packNdx < numPacketsRasterized2; ++packNdx) 1097 for (int fragNdx = 0; fragNdx < 4; fragNdx++) 1098 { 1099 float removedValue = nextFragmentPackets[packNdx].barycentric[2][fragNdx]; 1100 nextFragmentPackets[packNdx].barycentric[2][fragNdx] = 0.0f; 1101 nextFragmentPackets[packNdx].barycentric[1][fragNdx] += removedValue; 1102 1103 // edge has reversed direction 1104 std::swap(nextFragmentPackets[packNdx].barycentric[0][fragNdx], nextFragmentPackets[packNdx].barycentric[1][fragNdx]); 1105 } 1106 } 1107} 1108 1109LineExitDiamondGenerator::LineExitDiamondGenerator (const int subpixelBits) 1110 : m_subpixelBits(subpixelBits) 1111{ 1112} 1113 1114LineExitDiamondGenerator::~LineExitDiamondGenerator (void) 1115{ 1116} 1117 1118void LineExitDiamondGenerator::init (const tcu::Vec4& v0, const tcu::Vec4& v1) 1119{ 1120 const deInt64 x0 = toSubpixelCoord(v0.x(), m_subpixelBits); 1121 const deInt64 y0 = toSubpixelCoord(v0.y(), m_subpixelBits); 1122 const deInt64 x1 = toSubpixelCoord(v1.x(), m_subpixelBits); 1123 const deInt64 y1 = toSubpixelCoord(v1.y(), m_subpixelBits); 1124 1125 // line endpoints might be perturbed, add some margin 1126 const deInt64 xMin = de::min(x0, x1) - toSubpixelCoord(1, m_subpixelBits); 1127 const deInt64 xMax = de::max(x0, x1) + toSubpixelCoord(1, m_subpixelBits); 1128 const deInt64 yMin = de::min(y0, y1) - toSubpixelCoord(1, m_subpixelBits); 1129 const deInt64 yMax = de::max(y0, y1) + toSubpixelCoord(1, m_subpixelBits); 1130 1131 m_bboxMin.x() = floorSubpixelToPixelCoord(xMin, m_subpixelBits, true); 1132 m_bboxMin.y() = floorSubpixelToPixelCoord(yMin, m_subpixelBits, true); 1133 m_bboxMax.x() = ceilSubpixelToPixelCoord (xMax, m_subpixelBits, true); 1134 m_bboxMax.y() = ceilSubpixelToPixelCoord (yMax, m_subpixelBits, true); 1135 1136 m_v0 = v0; 1137 m_v1 = v1; 1138 1139 m_curPos = m_bboxMin; 1140} 1141 1142void LineExitDiamondGenerator::rasterize (LineExitDiamond* const lineDiamonds, const int maxDiamonds, int& numWritten) 1143{ 1144 DE_ASSERT(maxDiamonds > 0); 1145 1146 const deInt64 halfPixel = 1ll << (m_subpixelBits - 1); 1147 const tcu::Vector<deInt64,2> pa = LineRasterUtil::toSubpixelVector(m_v0.xy(), m_subpixelBits); 1148 const tcu::Vector<deInt64,2> pb = LineRasterUtil::toSubpixelVector(m_v1.xy(), m_subpixelBits); 1149 const LineRasterUtil::SubpixelLineSegment line = LineRasterUtil::SubpixelLineSegment(pa, pb); 1150 1151 int diamondNdx = 0; 1152 1153 while (m_curPos.y() <= m_bboxMax.y() && diamondNdx < maxDiamonds) 1154 { 1155 const tcu::Vector<deInt64,2> diamondPosition = LineRasterUtil::toSubpixelVector(m_curPos, m_subpixelBits) + tcu::Vector<deInt64,2>(halfPixel,halfPixel); 1156 1157 if (LineRasterUtil::doesLineSegmentExitDiamond(line, diamondPosition, m_subpixelBits)) 1158 { 1159 LineExitDiamond& packet = lineDiamonds[diamondNdx]; 1160 packet.position = m_curPos; 1161 ++diamondNdx; 1162 } 1163 1164 ++m_curPos.x(); 1165 if (m_curPos.x() > m_bboxMax.x()) 1166 { 1167 ++m_curPos.y(); 1168 m_curPos.x() = m_bboxMin.x(); 1169 } 1170 } 1171 1172 DE_ASSERT(diamondNdx <= maxDiamonds); 1173 numWritten = diamondNdx; 1174} 1175 1176} // rr 1177