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