Calculate wide line raster area properly.
[platform/upstream/VK-GL-CTS.git] / framework / referencerenderer / rrRasterizer.cpp
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