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