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