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)31 inline deInt64 toSubpixelCoord (float v)
32 {
33 return (deInt64)(v * (1<<RASTERIZER_SUBPIXEL_BITS) + (v < 0.f ? -0.5f : 0.5f));
34 }
35
toSubpixelCoord(deInt32 v)36 inline deInt64 toSubpixelCoord (deInt32 v)
37 {
38 return v << RASTERIZER_SUBPIXEL_BITS;
39 }
40
ceilSubpixelToPixelCoord(deInt64 coord,bool fillEdge)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
floorSubpixelToPixelCoord(deInt64 coord,bool fillEdge)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
initEdgeCCW(EdgeFunction & edge,const HorizontalFill horizontalFill,const VerticalFill verticalFill,const deInt64 x0,const deInt64 y0,const deInt64 x1,const deInt64 y1)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
reverseEdge(EdgeFunction & edge)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
evaluateEdge(const EdgeFunction & edge,const deInt64 x,const deInt64 y)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
isInsideCCW(const EdgeFunction & edge,const deInt64 edgeVal)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
SubpixelLineSegmentrr::LineRasterUtil::SubpixelLineSegment102 SubpixelLineSegment (const tcu::Vector<deInt64,2>& v0, const tcu::Vector<deInt64,2>& v1)
103 : m_v0(v0)
104 , m_v1(v1)
105 {
106 }
107
directionrr::LineRasterUtil::SubpixelLineSegment108 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
toSubpixelVector(const tcu::Vec2 & v)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
toSubpixelVector(const tcu::IVec2 & v)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)
isTheCenterOfTheFragment(const tcu::Vector<deInt64,2> & a)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
inViewport(const tcu::IVec2 & p,const tcu::IVec4 & viewport)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
vertexOnLeftSideOfLine(const tcu::Vector<deInt64,2> & p,const SubpixelLineSegment & l)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
vertexOnRightSideOfLine(const tcu::Vector<deInt64,2> & p,const SubpixelLineSegment & l)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
vertexOnLine(const tcu::Vector<deInt64,2> & p,const SubpixelLineSegment & l)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
vertexOnLineSegment(const tcu::Vector<deInt64,2> & p,const SubpixelLineSegment & l)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
getVertexSide(const tcu::Vector<deInt64,2> & v,const SubpixelLineSegment & l)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)
lineInCornerAngleRange(const SubpixelLineSegment & line,const tcu::Vector<deInt64,2> & cornerExitNormal)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)
lineInCornerOutsideAngleRange(const SubpixelLineSegment & line,const tcu::Vector<deInt64,2> & cornerExitNormal)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
doesLineSegmentExitDiamond(const SubpixelLineSegment & line,const tcu::Vector<deInt64,2> & diamondCenter)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
TriangleRasterizer(const tcu::IVec4 & viewport,const int numSamples,const RasterizationState & state)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 *//*--------------------------------------------------------------------*/
init(const tcu::Vec4 & v0,const tcu::Vec4 & v1,const tcu::Vec4 & v2)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
rasterizeSingleSample(FragmentPacket * const fragmentPackets,float * const depthValues,const int maxFragmentPackets,int & numPacketsRasterized)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>
rasterizeMultiSample(FragmentPacket * const fragmentPackets,float * const depthValues,const int maxFragmentPackets,int & numPacketsRasterized)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
rasterize(FragmentPacket * const fragmentPackets,float * const depthValues,const int maxFragmentPackets,int & numPacketsRasterized)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
SingleSampleLineRasterizer(const tcu::IVec4 & viewport)815 SingleSampleLineRasterizer::SingleSampleLineRasterizer (const tcu::IVec4& viewport)
816 : m_viewport (viewport)
817 , m_curRowFragment (0)
818 , m_lineWidth (0.0f)
819 {
820 }
821
~SingleSampleLineRasterizer()822 SingleSampleLineRasterizer::~SingleSampleLineRasterizer ()
823 {
824 }
825
init(const tcu::Vec4 & v0,const tcu::Vec4 & v1,float lineWidth)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
rasterize(FragmentPacket * const fragmentPackets,float * const depthValues,const int maxFragmentPackets,int & numPacketsRasterized)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
MultiSampleLineRasterizer(const int numSamples,const tcu::IVec4 & viewport)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
~MultiSampleLineRasterizer()980 MultiSampleLineRasterizer::~MultiSampleLineRasterizer ()
981 {
982 }
983
init(const tcu::Vec4 & v0,const tcu::Vec4 & v1,float lineWidth)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
rasterize(FragmentPacket * const fragmentPackets,float * const depthValues,const int maxFragmentPackets,int & numPacketsRasterized)1004 void MultiSampleLineRasterizer::rasterize (FragmentPacket* const fragmentPackets, float* const depthValues, const int maxFragmentPackets, int& numPacketsRasterized)
1005 {
1006 DE_ASSERT(maxFragmentPackets > 0);
1007
1008 m_triangleRasterizer0.rasterize(fragmentPackets, depthValues, maxFragmentPackets, numPacketsRasterized);
1009
1010 // Remove 3rd barycentric value and rebalance. Lines do not have non-zero barycentric at index 2
1011 for (int packNdx = 0; packNdx < numPacketsRasterized; ++packNdx)
1012 for (int fragNdx = 0; fragNdx < 4; fragNdx++)
1013 {
1014 float removedValue = fragmentPackets[packNdx].barycentric[2][fragNdx];
1015 fragmentPackets[packNdx].barycentric[2][fragNdx] = 0.0f;
1016 fragmentPackets[packNdx].barycentric[1][fragNdx] += removedValue;
1017 }
1018
1019 // rasterizer 0 filled the whole buffer?
1020 if (numPacketsRasterized == maxFragmentPackets)
1021 return;
1022
1023 {
1024 FragmentPacket* const nextFragmentPackets = fragmentPackets + numPacketsRasterized;
1025 float* nextDepthValues = (depthValues) ? (depthValues+4*numPacketsRasterized*m_numSamples) : (DE_NULL);
1026 int numPacketsRasterized2 = 0;
1027
1028 m_triangleRasterizer1.rasterize(nextFragmentPackets, nextDepthValues, maxFragmentPackets - numPacketsRasterized, numPacketsRasterized2);
1029
1030 numPacketsRasterized += numPacketsRasterized2;
1031
1032 // Fix swapped barycentrics in the second triangle
1033 for (int packNdx = 0; packNdx < numPacketsRasterized2; ++packNdx)
1034 for (int fragNdx = 0; fragNdx < 4; fragNdx++)
1035 {
1036 float removedValue = nextFragmentPackets[packNdx].barycentric[2][fragNdx];
1037 nextFragmentPackets[packNdx].barycentric[2][fragNdx] = 0.0f;
1038 nextFragmentPackets[packNdx].barycentric[1][fragNdx] += removedValue;
1039
1040 // edge has reversed direction
1041 std::swap(nextFragmentPackets[packNdx].barycentric[0][fragNdx], nextFragmentPackets[packNdx].barycentric[1][fragNdx]);
1042 }
1043 }
1044 }
1045
1046 } // rr
1047