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 28 namespace rr 29 { 30 31 inline deInt64 toSubpixelCoord (float v) 32 { 33 return (deInt64)(v * (1<<RASTERIZER_SUBPIXEL_BITS) + (v < 0.f ? -0.5f : 0.5f)); 34 } 35 36 inline deInt64 toSubpixelCoord (deInt32 v) 37 { 38 return v << RASTERIZER_SUBPIXEL_BITS; 39 } 40 41 inline deInt32 ceilSubpixelToPixelCoord (deInt64 coord, bool fillEdge) 42 { 43 if (coord >= 0) 44 return (deInt32)((coord + ((1ll<<RASTERIZER_SUBPIXEL_BITS) - (fillEdge ? 0 : 1))) >> RASTERIZER_SUBPIXEL_BITS); 45 else 46 return (deInt32)((coord + (fillEdge ? 1 : 0)) >> RASTERIZER_SUBPIXEL_BITS); 47 } 48 49 inline deInt32 floorSubpixelToPixelCoord (deInt64 coord, bool fillEdge) 50 { 51 if (coord >= 0) 52 return (deInt32)((coord - (fillEdge ? 1 : 0)) >> RASTERIZER_SUBPIXEL_BITS); 53 else 54 return (deInt32)((coord - ((1ll<<RASTERIZER_SUBPIXEL_BITS) - (fillEdge ? 0 : 1))) >> RASTERIZER_SUBPIXEL_BITS); 55 } 56 57 static 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 76 static 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 84 static 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 89 static inline bool isInsideCCW (const EdgeFunction& edge, const deInt64 edgeVal) 90 { 91 return edge.inclusive ? (edgeVal >= 0) : (edgeVal > 0); 92 } 93 94 namespace LineRasterUtil 95 { 96 97 struct 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 114 enum LINE_SIDE 115 { 116 LINE_SIDE_INTERSECT = 0, 117 LINE_SIDE_LEFT, 118 LINE_SIDE_RIGHT 119 }; 120 121 static tcu::Vector<deInt64,2> toSubpixelVector (const tcu::Vec2& v) 122 { 123 return tcu::Vector<deInt64,2>(toSubpixelCoord(v.x()), toSubpixelCoord(v.y())); 124 } 125 126 static tcu::Vector<deInt64,2> toSubpixelVector (const tcu::IVec2& v) 127 { 128 return tcu::Vector<deInt64,2>(toSubpixelCoord(v.x()), toSubpixelCoord(v.y())); 129 } 130 131 #if defined(DE_DEBUG) 132 static bool isTheCenterOfTheFragment (const tcu::Vector<deInt64,2>& a) 133 { 134 const deUint64 pixelSize = 1ll << (RASTERIZER_SUBPIXEL_BITS); 135 const deUint64 halfPixel = 1ll << (RASTERIZER_SUBPIXEL_BITS-1); 136 return ((a.x() & (pixelSize-1)) == halfPixel && 137 (a.y() & (pixelSize-1)) == halfPixel); 138 } 139 140 static 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 150 static 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 159 static 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 168 static 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 177 static 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 193 static 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) 209 bool 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) 222 bool 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 234 bool doesLineSegmentExitDiamond (const SubpixelLineSegment& line, const tcu::Vector<deInt64,2>& diamondCenter) 235 { 236 DE_ASSERT(isTheCenterOfTheFragment(diamondCenter)); 237 238 // Diamond Center is at diamondCenter in subpixel coords 239 240 const deInt64 halfPixel = 1ll << (RASTERIZER_SUBPIXEL_BITS-1); 241 242 const struct DiamondBound 243 { 244 tcu::Vector<deInt64,2> p0; 245 tcu::Vector<deInt64,2> p1; 246 bool edgeInclusive; // would a point on the bound be inside of the region 247 } bounds[] = 248 { 249 { diamondCenter + tcu::Vector<deInt64,2>(0, -halfPixel), diamondCenter + tcu::Vector<deInt64,2>(-halfPixel, 0), false }, 250 { diamondCenter + tcu::Vector<deInt64,2>(-halfPixel, 0), diamondCenter + tcu::Vector<deInt64,2>(0, halfPixel), false }, 251 { diamondCenter + tcu::Vector<deInt64,2>(0, halfPixel), diamondCenter + tcu::Vector<deInt64,2>(halfPixel, 0), true }, 252 { diamondCenter + tcu::Vector<deInt64,2>(halfPixel, 0), diamondCenter + tcu::Vector<deInt64,2>(0, -halfPixel), true }, 253 }; 254 255 const struct DiamondCorners 256 { 257 enum CORNER_EDGE_CASE_BEHAVIOR 258 { 259 CORNER_EDGE_CASE_NONE, // if the line intersects just a corner, no entering or exiting 260 CORNER_EDGE_CASE_HIT, // if the line intersects just a corner, entering and exit 261 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) 262 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) 263 }; 264 enum CORNER_START_CASE_BEHAVIOR 265 { 266 CORNER_START_CASE_NONE, // the line starting point is outside, no exiting 267 CORNER_START_CASE_OUTSIDE, // exit, if line does not intersect the region (preturbing moves the start point inside) 268 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. 269 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. 270 }; 271 enum CORNER_END_CASE_BEHAVIOR 272 { 273 CORNER_END_CASE_NONE, // end is inside, no exiting (preturbing moves the line end inside) 274 CORNER_END_CASE_DIRECTION, // exit, if line intersected the region (preturbing moves the line end outside) 275 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) 276 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) 277 }; 278 279 tcu::Vector<deInt64,2> dp; 280 bool pointInclusive; // would a point in this corner intersect with the region 281 CORNER_EDGE_CASE_BEHAVIOR lineBehavior; // would a line segment going through this corner intersect with the region 282 CORNER_START_CASE_BEHAVIOR startBehavior; // how the corner behaves if the start point at the corner 283 CORNER_END_CASE_BEHAVIOR endBehavior; // how the corner behaves if the end point at the corner 284 285 } corners[] = 286 { 287 { 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}, 288 { tcu::Vector<deInt64,2>(-halfPixel, 0), false, DiamondCorners::CORNER_EDGE_CASE_NONE, DiamondCorners::CORNER_START_CASE_NONE, DiamondCorners::CORNER_END_CASE_DIRECTION }, 289 { 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 }, 290 { tcu::Vector<deInt64,2>(halfPixel, 0), true, DiamondCorners::CORNER_EDGE_CASE_HIT, DiamondCorners::CORNER_START_CASE_OUTSIDE, DiamondCorners::CORNER_END_CASE_NONE }, 291 }; 292 293 // Corner cases at the corners 294 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(corners); ++ndx) 295 { 296 const tcu::Vector<deInt64,2> p = diamondCenter + corners[ndx].dp; 297 const bool intersectsAtCorner = LineRasterUtil::vertexOnLineSegment(p, line); 298 299 if (!intersectsAtCorner) 300 continue; 301 302 // line segment body intersects with the corner 303 if (p != line.m_v0 && p != line.m_v1) 304 { 305 if (corners[ndx].lineBehavior == DiamondCorners::CORNER_EDGE_CASE_HIT) 306 return true; 307 308 // endpoint in (+X, -Y) (X or Y may be 0) direction <==> x*y <= 0 309 if (corners[ndx].lineBehavior == DiamondCorners::CORNER_EDGE_CASE_HIT_FIRST_QUARTER && 310 (line.direction().x() * line.direction().y()) <= 0) 311 return true; 312 313 // endpoint in (+X, +Y) (Y > 0) direction <==> x*y > 0 314 if (corners[ndx].lineBehavior == DiamondCorners::CORNER_EDGE_CASE_HIT_SECOND_QUARTER && 315 (line.direction().x() * line.direction().y()) > 0) 316 return true; 317 } 318 319 // line exits the area at the corner 320 if (lineInCornerAngleRange(line, corners[ndx].dp)) 321 { 322 const bool startIsInside = corners[ndx].pointInclusive || p != line.m_v0; 323 const bool endIsOutside = !corners[ndx].pointInclusive || p != line.m_v1; 324 325 // starting point is inside the region and end endpoint is outside 326 if (startIsInside && endIsOutside) 327 return true; 328 } 329 330 // line end is at the corner 331 if (p == line.m_v1) 332 { 333 if (corners[ndx].endBehavior == DiamondCorners::CORNER_END_CASE_DIRECTION || 334 corners[ndx].endBehavior == DiamondCorners::CORNER_END_CASE_DIRECTION_AND_FIRST_QUARTER || 335 corners[ndx].endBehavior == DiamondCorners::CORNER_END_CASE_DIRECTION_AND_SECOND_QUARTER) 336 { 337 // did the line intersect the region 338 if (lineInCornerAngleRange(line, corners[ndx].dp)) 339 return true; 340 } 341 342 // due to the perturbed endpoint, lines at this the angle will cause and enter-exit pair 343 if (corners[ndx].endBehavior == DiamondCorners::CORNER_END_CASE_DIRECTION_AND_FIRST_QUARTER && 344 line.direction().x() < 0 && 345 line.direction().y() > 0) 346 return true; 347 if (corners[ndx].endBehavior == DiamondCorners::CORNER_END_CASE_DIRECTION_AND_SECOND_QUARTER && 348 line.direction().x() > 0 && 349 line.direction().y() > 0) 350 return true; 351 } 352 353 // line start is at the corner 354 if (p == line.m_v0) 355 { 356 if (corners[ndx].startBehavior == DiamondCorners::CORNER_START_CASE_OUTSIDE) 357 { 358 // if the line is not going inside, it will exit 359 if (lineInCornerOutsideAngleRange(line, corners[ndx].dp)) 360 return true; 361 } 362 363 // exit, if line the angle between line vector and X-axis is in range (0, 45] in positive Y side. 364 if (corners[ndx].startBehavior == DiamondCorners::CORNER_START_CASE_POSITIVE_Y_45 && 365 line.direction().x() > 0 && 366 line.direction().y() > 0 && 367 line.direction().y() <= line.direction().x()) 368 return true; 369 370 // exit, if line the angle between line vector and X-axis is in range [0, 45] in negative Y side. 371 if (corners[ndx].startBehavior == DiamondCorners::CORNER_START_CASE_NEGATIVE_Y_45 && 372 line.direction().x() > 0 && 373 line.direction().y() <= 0 && 374 -line.direction().y() <= line.direction().x()) 375 return true; 376 } 377 } 378 379 // Does the line intersect boundary at the left == exits the diamond 380 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(bounds); ++ndx) 381 { 382 const bool startVertexInside = LineRasterUtil::vertexOnLeftSideOfLine (line.m_v0, LineRasterUtil::SubpixelLineSegment(bounds[ndx].p0, bounds[ndx].p1)) || 383 (bounds[ndx].edgeInclusive && LineRasterUtil::vertexOnLine (line.m_v0, LineRasterUtil::SubpixelLineSegment(bounds[ndx].p0, bounds[ndx].p1))); 384 const bool endVertexInside = LineRasterUtil::vertexOnLeftSideOfLine (line.m_v1, LineRasterUtil::SubpixelLineSegment(bounds[ndx].p0, bounds[ndx].p1)) || 385 (bounds[ndx].edgeInclusive && LineRasterUtil::vertexOnLine (line.m_v1, LineRasterUtil::SubpixelLineSegment(bounds[ndx].p0, bounds[ndx].p1))); 386 387 // start must be on inside this half space (left or at the inclusive boundary) 388 if (!startVertexInside) 389 continue; 390 391 // end must be outside of this half-space (right or at non-inclusive boundary) 392 if (endVertexInside) 393 continue; 394 395 // Does the line via v0 and v1 intersect the line segment p0-p1 396 // <==> p0 and p1 are the different sides (LEFT, RIGHT) of the v0-v1 line. 397 // Corners are not allowed, they are checked already 398 LineRasterUtil::LINE_SIDE sideP0 = LineRasterUtil::getVertexSide(bounds[ndx].p0, line); 399 LineRasterUtil::LINE_SIDE sideP1 = LineRasterUtil::getVertexSide(bounds[ndx].p1, line); 400 401 if (sideP0 != LineRasterUtil::LINE_SIDE_INTERSECT && 402 sideP1 != LineRasterUtil::LINE_SIDE_INTERSECT && 403 sideP0 != sideP1) 404 return true; 405 } 406 407 return false; 408 } 409 410 } // LineRasterUtil 411 412 TriangleRasterizer::TriangleRasterizer (const tcu::IVec4& viewport, const int numSamples, const RasterizationState& state) 413 : m_viewport (viewport) 414 , m_numSamples (numSamples) 415 , m_winding (state.winding) 416 , m_horizontalFill (state.horizontalFill) 417 , m_verticalFill (state.verticalFill) 418 , m_face (FACETYPE_LAST) 419 { 420 } 421 422 /*--------------------------------------------------------------------*//*! 423 * \brief Initialize triangle rasterization 424 * \param v0 Screen-space coordinates (x, y, z) and 1/w for vertex 0. 425 * \param v1 Screen-space coordinates (x, y, z) and 1/w for vertex 1. 426 * \param v2 Screen-space coordinates (x, y, z) and 1/w for vertex 2. 427 *//*--------------------------------------------------------------------*/ 428 void TriangleRasterizer::init (const tcu::Vec4& v0, const tcu::Vec4& v1, const tcu::Vec4& v2) 429 { 430 m_v0 = v0; 431 m_v1 = v1; 432 m_v2 = v2; 433 434 // Positions in fixed-point coordinates. 435 const deInt64 x0 = toSubpixelCoord(v0.x()); 436 const deInt64 y0 = toSubpixelCoord(v0.y()); 437 const deInt64 x1 = toSubpixelCoord(v1.x()); 438 const deInt64 y1 = toSubpixelCoord(v1.y()); 439 const deInt64 x2 = toSubpixelCoord(v2.x()); 440 const deInt64 y2 = toSubpixelCoord(v2.y()); 441 442 // Initialize edge functions. 443 if (m_winding == WINDING_CCW) 444 { 445 initEdgeCCW(m_edge01, m_horizontalFill, m_verticalFill, x0, y0, x1, y1); 446 initEdgeCCW(m_edge12, m_horizontalFill, m_verticalFill, x1, y1, x2, y2); 447 initEdgeCCW(m_edge20, m_horizontalFill, m_verticalFill, x2, y2, x0, y0); 448 } 449 else 450 { 451 // Reverse edges 452 initEdgeCCW(m_edge01, m_horizontalFill, m_verticalFill, x1, y1, x0, y0); 453 initEdgeCCW(m_edge12, m_horizontalFill, m_verticalFill, x2, y2, x1, y1); 454 initEdgeCCW(m_edge20, m_horizontalFill, m_verticalFill, x0, y0, x2, y2); 455 } 456 457 // Determine face. 458 const deInt64 s = evaluateEdge(m_edge01, x2, y2); 459 const bool positiveArea = (m_winding == WINDING_CCW) ? (s > 0) : (s < 0); 460 m_face = positiveArea ? FACETYPE_FRONT : FACETYPE_BACK; 461 462 if (!positiveArea) 463 { 464 // Reverse edges so that we can use CCW area tests & interpolation 465 reverseEdge(m_edge01); 466 reverseEdge(m_edge12); 467 reverseEdge(m_edge20); 468 } 469 470 // Bounding box 471 const deInt64 xMin = de::min(de::min(x0, x1), x2); 472 const deInt64 xMax = de::max(de::max(x0, x1), x2); 473 const deInt64 yMin = de::min(de::min(y0, y1), y2); 474 const deInt64 yMax = de::max(de::max(y0, y1), y2); 475 476 m_bboxMin.x() = floorSubpixelToPixelCoord (xMin, m_horizontalFill == FILL_LEFT); 477 m_bboxMin.y() = floorSubpixelToPixelCoord (yMin, m_verticalFill == FILL_BOTTOM); 478 m_bboxMax.x() = ceilSubpixelToPixelCoord (xMax, m_horizontalFill == FILL_RIGHT); 479 m_bboxMax.y() = ceilSubpixelToPixelCoord (yMax, m_verticalFill == FILL_TOP); 480 481 // Clamp to viewport 482 const int wX0 = m_viewport.x(); 483 const int wY0 = m_viewport.y(); 484 const int wX1 = wX0 + m_viewport.z() - 1; 485 const int wY1 = wY0 + m_viewport.w() -1; 486 487 m_bboxMin.x() = de::clamp(m_bboxMin.x(), wX0, wX1); 488 m_bboxMin.y() = de::clamp(m_bboxMin.y(), wY0, wY1); 489 m_bboxMax.x() = de::clamp(m_bboxMax.x(), wX0, wX1); 490 m_bboxMax.y() = de::clamp(m_bboxMax.y(), wY0, wY1); 491 492 m_curPos = m_bboxMin; 493 } 494 495 void TriangleRasterizer::rasterizeSingleSample (FragmentPacket* const fragmentPackets, float* const depthValues, const int maxFragmentPackets, int& numPacketsRasterized) 496 { 497 DE_ASSERT(maxFragmentPackets > 0); 498 499 const deUint64 halfPixel = 1ll << (RASTERIZER_SUBPIXEL_BITS-1); 500 int packetNdx = 0; 501 502 while (m_curPos.y() <= m_bboxMax.y() && packetNdx < maxFragmentPackets) 503 { 504 const int x0 = m_curPos.x(); 505 const int y0 = m_curPos.y(); 506 507 // Subpixel coords 508 const deInt64 sx0 = toSubpixelCoord(x0) + halfPixel; 509 const deInt64 sx1 = toSubpixelCoord(x0+1) + halfPixel; 510 const deInt64 sy0 = toSubpixelCoord(y0) + halfPixel; 511 const deInt64 sy1 = toSubpixelCoord(y0+1) + halfPixel; 512 513 const deInt64 sx[4] = { sx0, sx1, sx0, sx1 }; 514 const deInt64 sy[4] = { sy0, sy0, sy1, sy1 }; 515 516 // Viewport test 517 const bool outX1 = x0+1 == m_viewport.x()+m_viewport.z(); 518 const bool outY1 = y0+1 == m_viewport.y()+m_viewport.w(); 519 520 DE_ASSERT(x0 < m_viewport.x()+m_viewport.z()); 521 DE_ASSERT(y0 < m_viewport.y()+m_viewport.w()); 522 523 // Edge values 524 tcu::Vector<deInt64, 4> e01; 525 tcu::Vector<deInt64, 4> e12; 526 tcu::Vector<deInt64, 4> e20; 527 528 // Coverage 529 deUint64 coverage = 0; 530 531 // Evaluate edge values 532 for (int i = 0; i < 4; i++) 533 { 534 e01[i] = evaluateEdge(m_edge01, sx[i], sy[i]); 535 e12[i] = evaluateEdge(m_edge12, sx[i], sy[i]); 536 e20[i] = evaluateEdge(m_edge20, sx[i], sy[i]); 537 } 538 539 // Compute coverage mask 540 coverage = setCoverageValue(coverage, 1, 0, 0, 0, isInsideCCW(m_edge01, e01[0]) && isInsideCCW(m_edge12, e12[0]) && isInsideCCW(m_edge20, e20[0])); 541 coverage = setCoverageValue(coverage, 1, 1, 0, 0, !outX1 && isInsideCCW(m_edge01, e01[1]) && isInsideCCW(m_edge12, e12[1]) && isInsideCCW(m_edge20, e20[1])); 542 coverage = setCoverageValue(coverage, 1, 0, 1, 0, !outY1 && isInsideCCW(m_edge01, e01[2]) && isInsideCCW(m_edge12, e12[2]) && isInsideCCW(m_edge20, e20[2])); 543 coverage = setCoverageValue(coverage, 1, 1, 1, 0, !outX1 && !outY1 && isInsideCCW(m_edge01, e01[3]) && isInsideCCW(m_edge12, e12[3]) && isInsideCCW(m_edge20, e20[3])); 544 545 // Advance to next location 546 m_curPos.x() += 2; 547 if (m_curPos.x() > m_bboxMax.x()) 548 { 549 m_curPos.y() += 2; 550 m_curPos.x() = m_bboxMin.x(); 551 } 552 553 if (coverage == 0) 554 continue; // Discard. 555 556 // Floating-point edge values for barycentrics etc. 557 const tcu::Vec4 e01f = e01.asFloat(); 558 const tcu::Vec4 e12f = e12.asFloat(); 559 const tcu::Vec4 e20f = e20.asFloat(); 560 561 // Compute depth values. 562 if (depthValues) 563 { 564 const tcu::Vec4 ooSum = 1.0f / (e01f + e12f + e20f); 565 const tcu::Vec4 z0 = e12f * ooSum; 566 const tcu::Vec4 z1 = e20f * ooSum; 567 const tcu::Vec4 z2 = e01f * ooSum; 568 569 depthValues[packetNdx*4+0] = z0[0]*m_v0.z() + z1[0]*m_v1.z() + z2[0]*m_v2.z(); 570 depthValues[packetNdx*4+1] = z0[1]*m_v0.z() + z1[1]*m_v1.z() + z2[1]*m_v2.z(); 571 depthValues[packetNdx*4+2] = z0[2]*m_v0.z() + z1[2]*m_v1.z() + z2[2]*m_v2.z(); 572 depthValues[packetNdx*4+3] = z0[3]*m_v0.z() + z1[3]*m_v1.z() + z2[3]*m_v2.z(); 573 } 574 575 // Compute barycentrics and write out fragment packet 576 { 577 FragmentPacket& packet = fragmentPackets[packetNdx]; 578 579 const tcu::Vec4 b0 = e12f * m_v0.w(); 580 const tcu::Vec4 b1 = e20f * m_v1.w(); 581 const tcu::Vec4 b2 = e01f * m_v2.w(); 582 const tcu::Vec4 ooSum = 1.0f / (b0 + b1 + b2); 583 584 packet.position = tcu::IVec2(x0, y0); 585 packet.coverage = coverage; 586 packet.barycentric[0] = b0 * ooSum; 587 packet.barycentric[1] = b1 * ooSum; 588 packet.barycentric[2] = 1.0f - packet.barycentric[0] - packet.barycentric[1]; 589 590 packetNdx += 1; 591 } 592 } 593 594 DE_ASSERT(packetNdx <= maxFragmentPackets); 595 numPacketsRasterized = packetNdx; 596 } 597 598 // Sample positions - ordered as (x, y) list. 599 600 // \note Macros are used to eliminate function calls even in debug builds. 601 #define SAMPLE_POS_TO_SUBPIXEL_COORD(POS) \ 602 (deInt64)((POS) * (1<<RASTERIZER_SUBPIXEL_BITS) + 0.5f) 603 604 #define SAMPLE_POS(X, Y) \ 605 SAMPLE_POS_TO_SUBPIXEL_COORD(X), SAMPLE_POS_TO_SUBPIXEL_COORD(Y) 606 607 static const deInt64 s_samplePos2[] = 608 { 609 SAMPLE_POS(0.3f, 0.3f), 610 SAMPLE_POS(0.7f, 0.7f) 611 }; 612 613 static const deInt64 s_samplePos4[] = 614 { 615 SAMPLE_POS(0.25f, 0.25f), 616 SAMPLE_POS(0.75f, 0.25f), 617 SAMPLE_POS(0.25f, 0.75f), 618 SAMPLE_POS(0.75f, 0.75f) 619 }; 620 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_samplePos4) == 4*2); 621 622 static const deInt64 s_samplePos8[] = 623 { 624 SAMPLE_POS( 7.f/16.f, 9.f/16.f), 625 SAMPLE_POS( 9.f/16.f, 13.f/16.f), 626 SAMPLE_POS(11.f/16.f, 3.f/16.f), 627 SAMPLE_POS(13.f/16.f, 11.f/16.f), 628 SAMPLE_POS( 1.f/16.f, 7.f/16.f), 629 SAMPLE_POS( 5.f/16.f, 1.f/16.f), 630 SAMPLE_POS(15.f/16.f, 5.f/16.f), 631 SAMPLE_POS( 3.f/16.f, 15.f/16.f) 632 }; 633 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_samplePos8) == 8*2); 634 635 static const deInt64 s_samplePos16[] = 636 { 637 SAMPLE_POS(1.f/8.f, 1.f/8.f), 638 SAMPLE_POS(3.f/8.f, 1.f/8.f), 639 SAMPLE_POS(5.f/8.f, 1.f/8.f), 640 SAMPLE_POS(7.f/8.f, 1.f/8.f), 641 SAMPLE_POS(1.f/8.f, 3.f/8.f), 642 SAMPLE_POS(3.f/8.f, 3.f/8.f), 643 SAMPLE_POS(5.f/8.f, 3.f/8.f), 644 SAMPLE_POS(7.f/8.f, 3.f/8.f), 645 SAMPLE_POS(1.f/8.f, 5.f/8.f), 646 SAMPLE_POS(3.f/8.f, 5.f/8.f), 647 SAMPLE_POS(5.f/8.f, 5.f/8.f), 648 SAMPLE_POS(7.f/8.f, 5.f/8.f), 649 SAMPLE_POS(1.f/8.f, 7.f/8.f), 650 SAMPLE_POS(3.f/8.f, 7.f/8.f), 651 SAMPLE_POS(5.f/8.f, 7.f/8.f), 652 SAMPLE_POS(7.f/8.f, 7.f/8.f) 653 }; 654 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_samplePos16) == 16*2); 655 656 #undef SAMPLE_POS 657 #undef SAMPLE_POS_TO_SUBPIXEL_COORD 658 659 template<int NumSamples> 660 void TriangleRasterizer::rasterizeMultiSample (FragmentPacket* const fragmentPackets, float* const depthValues, const int maxFragmentPackets, int& numPacketsRasterized) 661 { 662 DE_ASSERT(maxFragmentPackets > 0); 663 664 const deInt64* samplePos = DE_NULL; 665 const deUint64 halfPixel = 1ll << (RASTERIZER_SUBPIXEL_BITS-1); 666 int packetNdx = 0; 667 668 switch (NumSamples) 669 { 670 case 2: samplePos = s_samplePos2; break; 671 case 4: samplePos = s_samplePos4; break; 672 case 8: samplePos = s_samplePos8; break; 673 case 16: samplePos = s_samplePos16; break; 674 default: 675 DE_ASSERT(false); 676 } 677 678 while (m_curPos.y() <= m_bboxMax.y() && packetNdx < maxFragmentPackets) 679 { 680 const int x0 = m_curPos.x(); 681 const int y0 = m_curPos.y(); 682 683 // Base subpixel coords 684 const deInt64 sx0 = toSubpixelCoord(x0); 685 const deInt64 sx1 = toSubpixelCoord(x0+1); 686 const deInt64 sy0 = toSubpixelCoord(y0); 687 const deInt64 sy1 = toSubpixelCoord(y0+1); 688 689 const deInt64 sx[4] = { sx0, sx1, sx0, sx1 }; 690 const deInt64 sy[4] = { sy0, sy0, sy1, sy1 }; 691 692 // Viewport test 693 const bool outX1 = x0+1 == m_viewport.x()+m_viewport.z(); 694 const bool outY1 = y0+1 == m_viewport.y()+m_viewport.w(); 695 696 DE_ASSERT(x0 < m_viewport.x()+m_viewport.z()); 697 DE_ASSERT(y0 < m_viewport.y()+m_viewport.w()); 698 699 // Edge values 700 tcu::Vector<deInt64, 4> e01[NumSamples]; 701 tcu::Vector<deInt64, 4> e12[NumSamples]; 702 tcu::Vector<deInt64, 4> e20[NumSamples]; 703 704 // Coverage 705 deUint64 coverage = 0; 706 707 // Evaluate edge values at sample positions 708 for (int sampleNdx = 0; sampleNdx < NumSamples; sampleNdx++) 709 { 710 const deInt64 ox = samplePos[sampleNdx*2 + 0]; 711 const deInt64 oy = samplePos[sampleNdx*2 + 1]; 712 713 for (int fragNdx = 0; fragNdx < 4; fragNdx++) 714 { 715 e01[sampleNdx][fragNdx] = evaluateEdge(m_edge01, sx[fragNdx] + ox, sy[fragNdx] + oy); 716 e12[sampleNdx][fragNdx] = evaluateEdge(m_edge12, sx[fragNdx] + ox, sy[fragNdx] + oy); 717 e20[sampleNdx][fragNdx] = evaluateEdge(m_edge20, sx[fragNdx] + ox, sy[fragNdx] + oy); 718 } 719 } 720 721 // Compute coverage mask 722 for (int sampleNdx = 0; sampleNdx < NumSamples; sampleNdx++) 723 { 724 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])); 725 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])); 726 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])); 727 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])); 728 } 729 730 // Advance to next location 731 m_curPos.x() += 2; 732 if (m_curPos.x() > m_bboxMax.x()) 733 { 734 m_curPos.y() += 2; 735 m_curPos.x() = m_bboxMin.x(); 736 } 737 738 if (coverage == 0) 739 continue; // Discard. 740 741 // Compute depth values. 742 if (depthValues) 743 { 744 for (int sampleNdx = 0; sampleNdx < NumSamples; sampleNdx++) 745 { 746 // Floating-point edge values at sample coordinates. 747 const tcu::Vec4& e01f = e01[sampleNdx].asFloat(); 748 const tcu::Vec4& e12f = e12[sampleNdx].asFloat(); 749 const tcu::Vec4& e20f = e20[sampleNdx].asFloat(); 750 751 const tcu::Vec4 ooSum = 1.0f / (e01f + e12f + e20f); 752 const tcu::Vec4 z0 = e12f * ooSum; 753 const tcu::Vec4 z1 = e20f * ooSum; 754 const tcu::Vec4 z2 = e01f * ooSum; 755 756 depthValues[(packetNdx*4+0)*NumSamples + sampleNdx] = z0[0]*m_v0.z() + z1[0]*m_v1.z() + z2[0]*m_v2.z(); 757 depthValues[(packetNdx*4+1)*NumSamples + sampleNdx] = z0[1]*m_v0.z() + z1[1]*m_v1.z() + z2[1]*m_v2.z(); 758 depthValues[(packetNdx*4+2)*NumSamples + sampleNdx] = z0[2]*m_v0.z() + z1[2]*m_v1.z() + z2[2]*m_v2.z(); 759 depthValues[(packetNdx*4+3)*NumSamples + sampleNdx] = z0[3]*m_v0.z() + z1[3]*m_v1.z() + z2[3]*m_v2.z(); 760 } 761 } 762 763 // Compute barycentrics and write out fragment packet 764 { 765 FragmentPacket& packet = fragmentPackets[packetNdx]; 766 767 // Floating-point edge values at pixel center. 768 tcu::Vec4 e01f; 769 tcu::Vec4 e12f; 770 tcu::Vec4 e20f; 771 772 for (int i = 0; i < 4; i++) 773 { 774 e01f[i] = float(evaluateEdge(m_edge01, sx[i] + halfPixel, sy[i] + halfPixel)); 775 e12f[i] = float(evaluateEdge(m_edge12, sx[i] + halfPixel, sy[i] + halfPixel)); 776 e20f[i] = float(evaluateEdge(m_edge20, sx[i] + halfPixel, sy[i] + halfPixel)); 777 } 778 779 // Barycentrics & scale. 780 const tcu::Vec4 b0 = e12f * m_v0.w(); 781 const tcu::Vec4 b1 = e20f * m_v1.w(); 782 const tcu::Vec4 b2 = e01f * m_v2.w(); 783 const tcu::Vec4 ooSum = 1.0f / (b0 + b1 + b2); 784 785 packet.position = tcu::IVec2(x0, y0); 786 packet.coverage = coverage; 787 packet.barycentric[0] = b0 * ooSum; 788 packet.barycentric[1] = b1 * ooSum; 789 packet.barycentric[2] = 1.0f - packet.barycentric[0] - packet.barycentric[1]; 790 791 packetNdx += 1; 792 } 793 } 794 795 DE_ASSERT(packetNdx <= maxFragmentPackets); 796 numPacketsRasterized = packetNdx; 797 } 798 799 void TriangleRasterizer::rasterize (FragmentPacket* const fragmentPackets, float* const depthValues, const int maxFragmentPackets, int& numPacketsRasterized) 800 { 801 DE_ASSERT(maxFragmentPackets > 0); 802 803 switch (m_numSamples) 804 { 805 case 1: rasterizeSingleSample (fragmentPackets, depthValues, maxFragmentPackets, numPacketsRasterized); break; 806 case 2: rasterizeMultiSample<2> (fragmentPackets, depthValues, maxFragmentPackets, numPacketsRasterized); break; 807 case 4: rasterizeMultiSample<4> (fragmentPackets, depthValues, maxFragmentPackets, numPacketsRasterized); break; 808 case 8: rasterizeMultiSample<8> (fragmentPackets, depthValues, maxFragmentPackets, numPacketsRasterized); break; 809 case 16: rasterizeMultiSample<16> (fragmentPackets, depthValues, maxFragmentPackets, numPacketsRasterized); break; 810 default: 811 DE_ASSERT(DE_FALSE); 812 } 813 } 814 815 SingleSampleLineRasterizer::SingleSampleLineRasterizer (const tcu::IVec4& viewport) 816 : m_viewport (viewport) 817 , m_curRowFragment (0) 818 , m_lineWidth (0.0f) 819 { 820 } 821 822 SingleSampleLineRasterizer::~SingleSampleLineRasterizer () 823 { 824 } 825 826 void SingleSampleLineRasterizer::init (const tcu::Vec4& v0, const tcu::Vec4& v1, float lineWidth) 827 { 828 const bool isXMajor = de::abs((v1 - v0).x()) >= de::abs((v1 - v0).y()); 829 830 // Bounding box \note: with wide lines, the line is actually moved as in the spec 831 const deInt32 lineWidthPixels = (lineWidth > 1.0f) ? (deInt32)floor(lineWidth + 0.5f) : 1; 832 833 const tcu::IVec2 minorDirection = (isXMajor ? tcu::IVec2(0, 1) : tcu::IVec2(1, 0)); 834 const tcu::Vector<deInt64,2> widthOffset = (isXMajor ? tcu::Vector<deInt64,2>(0, -1) : tcu::Vector<deInt64,2>(-1, 0)) * (toSubpixelCoord(lineWidthPixels - 1) / 2); 835 836 const deInt64 x0 = toSubpixelCoord(v0.x()) + widthOffset.x(); 837 const deInt64 y0 = toSubpixelCoord(v0.y()) + widthOffset.y(); 838 const deInt64 x1 = toSubpixelCoord(v1.x()) + widthOffset.x(); 839 const deInt64 y1 = toSubpixelCoord(v1.y()) + widthOffset.y(); 840 841 // line endpoints might be perturbed, add some margin 842 const deInt64 xMin = de::min(x0, x1) - toSubpixelCoord(1); 843 const deInt64 xMax = de::max(x0, x1) + toSubpixelCoord(1); 844 const deInt64 yMin = de::min(y0, y1) - toSubpixelCoord(1); 845 const deInt64 yMax = de::max(y0, y1) + toSubpixelCoord(1); 846 847 // Remove invisible area 848 849 if (isXMajor) 850 { 851 // clamp to viewport in major direction 852 m_bboxMin.x() = de::clamp(floorSubpixelToPixelCoord(xMin, true), m_viewport.x(), m_viewport.x() + m_viewport.z() - 1); 853 m_bboxMax.x() = de::clamp(ceilSubpixelToPixelCoord (xMax, true), m_viewport.x(), m_viewport.x() + m_viewport.z() - 1); 854 855 // clamp to padded viewport in minor direction (wide lines might bleed over viewport in minor direction) 856 m_bboxMin.y() = de::clamp(floorSubpixelToPixelCoord(yMin, true), m_viewport.y() - lineWidthPixels, m_viewport.y() + m_viewport.w() - 1); 857 m_bboxMax.y() = de::clamp(ceilSubpixelToPixelCoord (yMax, true), m_viewport.y() - lineWidthPixels, m_viewport.y() + m_viewport.w() - 1); 858 } 859 else 860 { 861 // clamp to viewport in major direction 862 m_bboxMin.y() = de::clamp(floorSubpixelToPixelCoord(yMin, true), m_viewport.y(), m_viewport.y() + m_viewport.w() - 1); 863 m_bboxMax.y() = de::clamp(ceilSubpixelToPixelCoord (yMax, true), m_viewport.y(), m_viewport.y() + m_viewport.w() - 1); 864 865 // clamp to padded viewport in minor direction (wide lines might bleed over viewport in minor direction) 866 m_bboxMin.x() = de::clamp(floorSubpixelToPixelCoord(xMin, true), m_viewport.x() - lineWidthPixels, m_viewport.x() + m_viewport.z() - 1); 867 m_bboxMax.x() = de::clamp(ceilSubpixelToPixelCoord (xMax, true), m_viewport.x() - lineWidthPixels, m_viewport.x() + m_viewport.z() - 1); 868 } 869 870 m_lineWidth = lineWidth; 871 872 m_v0 = v0; 873 m_v1 = v1; 874 875 m_curPos = m_bboxMin; 876 m_curRowFragment = 0; 877 } 878 879 void SingleSampleLineRasterizer::rasterize (FragmentPacket* const fragmentPackets, float* const depthValues, const int maxFragmentPackets, int& numPacketsRasterized) 880 { 881 DE_ASSERT(maxFragmentPackets > 0); 882 883 const deInt64 halfPixel = 1ll << (RASTERIZER_SUBPIXEL_BITS-1); 884 const deInt32 lineWidth = (m_lineWidth > 1.0f) ? (deInt32)floor(m_lineWidth + 0.5f) : 1; 885 const bool isXMajor = de::abs((m_v1 - m_v0).x()) >= de::abs((m_v1 - m_v0).y()); 886 const tcu::IVec2 minorDirection = (isXMajor ? tcu::IVec2(0, 1) : tcu::IVec2(1, 0)); 887 const tcu::Vector<deInt64,2> widthOffset = (isXMajor ? tcu::Vector<deInt64,2>(0, -1) : tcu::Vector<deInt64,2>(-1, 0)) * (toSubpixelCoord(lineWidth - 1) / 2); 888 const tcu::Vector<deInt64,2> pa = LineRasterUtil::toSubpixelVector(m_v0.xy()) + widthOffset; 889 const tcu::Vector<deInt64,2> pb = LineRasterUtil::toSubpixelVector(m_v1.xy()) + widthOffset; 890 const LineRasterUtil::SubpixelLineSegment line = LineRasterUtil::SubpixelLineSegment(pa, pb); 891 892 int packetNdx = 0; 893 894 while (m_curPos.y() <= m_bboxMax.y() && packetNdx < maxFragmentPackets) 895 { 896 const tcu::Vector<deInt64,2> diamondPosition = LineRasterUtil::toSubpixelVector(m_curPos) + tcu::Vector<deInt64,2>(halfPixel,halfPixel); 897 898 // Should current fragment be drawn? == does the segment exit this diamond? 899 if (LineRasterUtil::doesLineSegmentExitDiamond(line, diamondPosition)) 900 { 901 const tcu::Vector<deInt64,2> pr = diamondPosition; 902 const float t = tcu::dot((pr - pa).asFloat(), (pb - pa).asFloat()) / tcu::lengthSquared(pb.asFloat() - pa.asFloat()); 903 904 // Rasterize on only fragments that are would end up in the viewport (i.e. visible) 905 const int minViewportLimit = (isXMajor) ? (m_viewport.y()) : (m_viewport.x()); 906 const int maxViewportLimit = (isXMajor) ? (m_viewport.y() + m_viewport.w()) : (m_viewport.x() + m_viewport.z()); 907 const int fragmentLocation = (isXMajor) ? (m_curPos.y()) : (m_curPos.x()); 908 909 const int rowFragBegin = de::max(0, minViewportLimit - fragmentLocation); 910 const int rowFragEnd = de::min(maxViewportLimit - fragmentLocation, lineWidth); 911 912 // Wide lines require multiple fragments. 913 for (; rowFragBegin + m_curRowFragment < rowFragEnd; m_curRowFragment++) 914 { 915 const tcu::IVec2 fragmentPos = m_curPos + minorDirection * (rowFragBegin + m_curRowFragment); 916 917 // We only rasterize visible area 918 DE_ASSERT(LineRasterUtil::inViewport(fragmentPos, m_viewport)); 919 920 // Compute depth values. 921 if (depthValues) 922 { 923 const float za = m_v0.z(); 924 const float zb = m_v1.z(); 925 926 depthValues[packetNdx*4+0] = (1 - t) * za + t * zb; 927 depthValues[packetNdx*4+1] = 0; 928 depthValues[packetNdx*4+2] = 0; 929 depthValues[packetNdx*4+3] = 0; 930 } 931 932 { 933 // output this fragment 934 // \note In order to make consistent output with multisampled line rasterization, output "barycentric" coordinates 935 FragmentPacket& packet = fragmentPackets[packetNdx]; 936 937 const tcu::Vec4 b0 = tcu::Vec4(1 - t); 938 const tcu::Vec4 b1 = tcu::Vec4(t); 939 const tcu::Vec4 ooSum = 1.0f / (b0 + b1); 940 941 packet.position = fragmentPos; 942 packet.coverage = getCoverageBit(1, 0, 0, 0); 943 packet.barycentric[0] = b0 * ooSum; 944 packet.barycentric[1] = b1 * ooSum; 945 packet.barycentric[2] = tcu::Vec4(0.0f); 946 947 packetNdx += 1; 948 } 949 950 if (packetNdx == maxFragmentPackets) 951 { 952 m_curRowFragment++; // don't redraw this fragment again next time 953 numPacketsRasterized = packetNdx; 954 return; 955 } 956 } 957 958 m_curRowFragment = 0; 959 } 960 961 ++m_curPos.x(); 962 if (m_curPos.x() > m_bboxMax.x()) 963 { 964 ++m_curPos.y(); 965 m_curPos.x() = m_bboxMin.x(); 966 } 967 } 968 969 DE_ASSERT(packetNdx <= maxFragmentPackets); 970 numPacketsRasterized = packetNdx; 971 } 972 973 MultiSampleLineRasterizer::MultiSampleLineRasterizer (const int numSamples, const tcu::IVec4& viewport) 974 : m_numSamples (numSamples) 975 , m_triangleRasterizer0 (viewport, m_numSamples, RasterizationState()) 976 , m_triangleRasterizer1 (viewport, m_numSamples, RasterizationState()) 977 { 978 } 979 980 MultiSampleLineRasterizer::~MultiSampleLineRasterizer () 981 { 982 } 983 984 void MultiSampleLineRasterizer::init (const tcu::Vec4& v0, const tcu::Vec4& v1, float lineWidth) 985 { 986 // allow creation of single sampled rasterizer objects but do not allow using them 987 DE_ASSERT(m_numSamples > 1); 988 989 const tcu::Vec2 lineVec = tcu::Vec2(tcu::Vec4(v1).xy()) - tcu::Vec2(tcu::Vec4(v0).xy()); 990 const tcu::Vec2 normal2 = tcu::normalize(tcu::Vec2(-lineVec[1], lineVec[0])); 991 const tcu::Vec4 normal4 = tcu::Vec4(normal2.x(), normal2.y(), 0, 0); 992 const float offset = lineWidth / 2.0f; 993 994 const tcu::Vec4 p0 = v0 + normal4 * offset; 995 const tcu::Vec4 p1 = v0 - normal4 * offset; 996 const tcu::Vec4 p2 = v1 - normal4 * offset; 997 const tcu::Vec4 p3 = v1 + normal4 * offset; 998 999 // Edge 0 -> 1 is always along the line and edge 1 -> 2 is in 90 degree angle to the line 1000 m_triangleRasterizer0.init(p0, p3, p2); 1001 m_triangleRasterizer1.init(p2, p1, p0); 1002 } 1003 1004 void MultiSampleLineRasterizer::rasterize (FragmentPacket* const fragmentPackets, float* const depthValues, const int maxFragmentPackets, int& numPacketsRasterized) 1005 { 1006 DE_ASSERT(maxFragmentPackets > 0); 1007 1008 m_triangleRasterizer0.rasterize(fragmentPackets, depthValues, maxFragmentPackets, numPacketsRasterized); 1009 1010 // Remove 3rd barycentric value and rebalance. Lines do not have non-zero barycentric at index 2 1011 for (int packNdx = 0; packNdx < numPacketsRasterized; ++packNdx) 1012 for (int fragNdx = 0; fragNdx < 4; fragNdx++) 1013 { 1014 float removedValue = fragmentPackets[packNdx].barycentric[2][fragNdx]; 1015 fragmentPackets[packNdx].barycentric[2][fragNdx] = 0.0f; 1016 fragmentPackets[packNdx].barycentric[1][fragNdx] += removedValue; 1017 } 1018 1019 // rasterizer 0 filled the whole buffer? 1020 if (numPacketsRasterized == maxFragmentPackets) 1021 return; 1022 1023 { 1024 FragmentPacket* const nextFragmentPackets = fragmentPackets + numPacketsRasterized; 1025 float* nextDepthValues = (depthValues) ? (depthValues+4*numPacketsRasterized*m_numSamples) : (DE_NULL); 1026 int numPacketsRasterized2 = 0; 1027 1028 m_triangleRasterizer1.rasterize(nextFragmentPackets, nextDepthValues, maxFragmentPackets - numPacketsRasterized, numPacketsRasterized2); 1029 1030 numPacketsRasterized += numPacketsRasterized2; 1031 1032 // Fix swapped barycentrics in the second triangle 1033 for (int packNdx = 0; packNdx < numPacketsRasterized2; ++packNdx) 1034 for (int fragNdx = 0; fragNdx < 4; fragNdx++) 1035 { 1036 float removedValue = nextFragmentPackets[packNdx].barycentric[2][fragNdx]; 1037 nextFragmentPackets[packNdx].barycentric[2][fragNdx] = 0.0f; 1038 nextFragmentPackets[packNdx].barycentric[1][fragNdx] += removedValue; 1039 1040 // edge has reversed direction 1041 std::swap(nextFragmentPackets[packNdx].barycentric[0][fragNdx], nextFragmentPackets[packNdx].barycentric[1][fragNdx]); 1042 } 1043 } 1044 } 1045 1046 } // rr 1047