From 5f5fff7e58deae37aa15f201348f89de426f3cfc Mon Sep 17 00:00:00 2001
From: Lactozilla <jp6781615@gmail.com>
Date: Mon, 20 May 2024 21:06:31 -0300
Subject: [PATCH] Undo clipsegs optimization

---
 src/r_bsp.c  | 211 ++++++++++++++++++++++++++++++++++++---------------
 src/r_bsp.h  |   2 -
 src/r_defs.h |  11 +++
 src/r_segs.c |  31 --------
 4 files changed, 159 insertions(+), 96 deletions(-)

diff --git a/src/r_bsp.c b/src/r_bsp.c
index 62970520f7..d606d7a274 100644
--- a/src/r_bsp.c
+++ b/src/r_bsp.c
@@ -52,66 +52,164 @@ void R_ClearDrawSegs(void)
 	ds_p = drawsegs;
 }
 
-// CPhipps -
-// Instead of clipsegs, let's try using an array with one entry for each column,
-// indicating whether it's blocked by a solid wall yet or not.
-UINT8 solidcol[MAXVIDWIDTH];
+// Fix from boom.
+#define MAXSEGS (MAXVIDWIDTH/2+1)
 
-// CPhipps -
-// R_ClipWallSegment
+// newend is one past the last valid seg
+static cliprange_t *newend;
+static cliprange_t solidsegs[MAXSEGS];
+
+//
+// R_ClipSolidWallSegment
+// Does handle solid walls,
+//  e.g. single sided LineDefs (middle texture)
+//  that entirely block the view.
 //
-// Replaces the old R_Clip*WallSegment functions. It draws bits of walls in those
-// columns which aren't solid, and updates the solidcol[] array appropriately
-static void R_ClipWallSegment(INT32 first, INT32 last, boolean solid)
+static void R_ClipSolidWallSegment(INT32 first, INT32 last)
 {
-	while (first < last)
-	{
-		UINT8 *p;
+	cliprange_t *next;
+	cliprange_t *start;
 
-		if (solidcol[first])
-		{
-			p = memchr(solidcol+first, 0, last-first);
-			if (!p)
-				return; // All solid
+	// Find the first range that touches the range (adjacent pixels are touching).
+	start = solidsegs;
+	while (start->last < first - 1)
+		start++;
 
-			first = p - solidcol;
+	if (first < start->first)
+	{
+		if (last < start->first - 1)
+		{
+			// Post is entirely visible (above start), so insert a new clippost.
+			R_StoreWallRange(first, last);
+			next = newend;
+			newend++;
+			// NO MORE CRASHING!
+			if (newend - solidsegs > MAXSEGS)
+				I_Error("R_ClipSolidWallSegment: Solid Segs overflow!\n");
+
+			while (next != start)
+			{
+				*next = *(next-1);
+				next--;
+			}
+			next->first = first;
+			next->last = last;
+			return;
 		}
-		else
+
+		// There is a fragment above *start.
+		R_StoreWallRange(first, start->first - 1);
+		// Now adjust the clip size.
+		start->first = first;
+	}
+
+	// Bottom contained in start?
+	if (last <= start->last)
+		return;
+
+	next = start;
+	while (last >= (next+1)->first - 1)
+	{
+		// There is a fragment between two posts.
+		R_StoreWallRange(next->last + 1, (next+1)->first - 1);
+		next++;
+
+		if (last <= next->last)
 		{
-			p = memchr(solidcol+first, 1, last-first);
+			// Bottom is contained in next.
+			// Adjust the clip size.
+			start->last = next->last;
+			goto crunch;
+		}
+	}
 
-			int to;
-			if (!p)
-				to = last;
-			else
-				to = p - solidcol;
+	// There is a fragment after *next.
+	R_StoreWallRange(next->last + 1, last);
+	// Adjust the clip size.
+	start->last = last;
+
+	// Remove start+1 to next from the clip list, because start now covers their area.
+crunch:
+	if (next == start)
+		return; // Post just extended past the bottom of one post.
 
-			R_StoreWallRange(first, to-1);
+	while (next++ != newend)
+		*++start = *next; // Remove a post.
+
+	newend = start + 1;
+
+	// NO MORE CRASHING!
+	if (newend - solidsegs > MAXSEGS)
+		I_Error("R_ClipSolidWallSegment: Solid Segs overflow!\n");
+}
+
+//
+// R_ClipPassWallSegment
+// Clips the given range of columns, but does not include it in the clip list.
+// Does handle windows, e.g. LineDefs with upper and lower texture.
+//
+static inline void R_ClipPassWallSegment(INT32 first, INT32 last)
+{
+	cliprange_t *start;
 
-			if (solid)
-				memset(solidcol+first, 1, to-first);
+	// Find the first range that touches the range
+	//  (adjacent pixels are touching).
+	start = solidsegs;
+	while (start->last < first - 1)
+		start++;
 
-			first = to;
+	if (first < start->first)
+	{
+		if (last < start->first - 1)
+		{
+			// Post is entirely visible (above start).
+			R_StoreWallRange(first, last);
+			return;
 		}
+
+		// There is a fragment above *start.
+		R_StoreWallRange(first, start->first - 1);
+	}
+
+	// Bottom contained in start?
+	if (last <= start->last)
+		return;
+
+	while (last >= (start+1)->first - 1)
+	{
+		// There is a fragment between two posts.
+		R_StoreWallRange(start->last + 1, (start+1)->first - 1);
+		start++;
+
+		if (last <= start->last)
+			return;
 	}
+
+	// There is a fragment after *next.
+	R_StoreWallRange(start->last + 1, last);
 }
 
+//
+// R_ClearClipSegs
+//
 void R_ClearClipSegs(void)
 {
-	memset(solidcol, 0, viewwidth);
+	solidsegs[0].first = -0x7fffffff;
+	solidsegs[0].last = -1;
+	solidsegs[1].first = viewwidth;
+	solidsegs[1].last = 0x7fffffff;
+	newend = solidsegs + 2;
 }
-
 void R_PortalClearClipSegs(INT32 start, INT32 end)
 {
-	R_ClearClipSegs();
-
-	for (INT32 x = 0; x < start; x++)
-		solidcol[x] = 1;
-
-	for (INT32 x = end; x < viewwidth; x++)
-		solidcol[x] = 1;
+	solidsegs[0].first = -0x7fffffff;
+	solidsegs[0].last = start-1;
+	solidsegs[1].first = end;
+	solidsegs[1].last = 0x7fffffff;
+	newend = solidsegs + 2;
 }
 
+
 // R_DoorClosed
 //
 // This function is used to fix the automap bug which
@@ -517,11 +615,11 @@ static void R_AddLine(seg_t *line)
 		return;
 
 clippass:
-	R_ClipWallSegment(x1, x2, false);
+	R_ClipPassWallSegment(x1, x2 - 1);
 	return;
 
 clipsolid:
-	R_ClipWallSegment(x1, x2, true);
+	R_ClipSolidWallSegment(x1, x2 - 1);
 }
 
 //
@@ -554,23 +652,10 @@ static boolean R_CheckBBox(const fixed_t *bspcoord)
 	angle_t angle1, angle2;
 	INT32 sx1, sx2, boxpos;
 	const INT32* check;
+	cliprange_t *start;
 
 	// Find the corners of the box that define the edges from current viewpoint.
-	if (viewx <= bspcoord[BOXLEFT])
-		boxpos = 0;
-	else if (viewx < bspcoord[BOXRIGHT])
-		boxpos = 1;
-	else
-		boxpos = 2;
-
-	if (viewy >= bspcoord[BOXTOP])
-		boxpos |= 0;
-	else if (viewy > bspcoord[BOXBOTTOM])
-		boxpos |= 1<<2;
-	else
-		boxpos |= 2<<2;
-
-	if (boxpos == 5)
+	if ((boxpos = (viewx <= bspcoord[BOXLEFT] ? 0 : viewx < bspcoord[BOXRIGHT] ? 1 : 2) + (viewy >= bspcoord[BOXTOP] ? 0 : viewy > bspcoord[BOXBOTTOM] ? 4 : 8)) == 5)
 		return true;
 
 	check = checkcoord[boxpos];
@@ -599,14 +684,14 @@ static boolean R_CheckBBox(const fixed_t *bspcoord)
 	sx2 = viewangletox[angle2];
 
 	// Does not cross a pixel.
-	if (sx1 >= sx2)
-		return false;
+	if (sx1 >= sx2) return false;
 
-	if (!memchr(solidcol+sx1, 0, sx2-sx1))
-	{
-		// All columns it covers are already solidly covered
-		return false;
-	}
+	start = solidsegs;
+	while (start->last < sx2)
+		start++;
+
+	if (sx1 >= start->first && sx2 <= start->last)
+		return false; // The clippost contains the new span.
 
 	return true;
 }
@@ -1313,7 +1398,7 @@ void R_RenderPortalHorizonLine(sector_t *sector)
 	firstseg = NULL;
 	curline = &segs[0];
 
-	R_ClipWallSegment(portalclipstart, portalclipend, true);
+	R_ClipSolidWallSegment(portalclipstart, portalclipend);
 
 	curline = NULL;
 }
diff --git a/src/r_bsp.h b/src/r_bsp.h
index d08769755c..44ddd0b1bf 100644
--- a/src/r_bsp.h
+++ b/src/r_bsp.h
@@ -38,8 +38,6 @@ extern boolean horizonline;
 
 extern INT32 doorclosed;
 
-extern UINT8 solidcol[MAXVIDWIDTH];
-
 // BSP?
 void R_ClearClipSegs(void);
 void R_PortalClearClipSegs(INT32 start, INT32 end);
diff --git a/src/r_defs.h b/src/r_defs.h
index 227a98d12f..da4dd2d70e 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -31,6 +31,17 @@
 
 #include "taglist.h"
 
+//
+// ClipWallSegment
+// Clips the given range of columns
+// and includes it in the new clip list.
+//
+typedef struct
+{
+	INT32 first;
+	INT32 last;
+} cliprange_t;
+
 // Silhouette, needed for clipping segs (mainly) and sprites representing things.
 #define SIL_NONE   0
 #define SIL_BOTTOM 1
diff --git a/src/r_segs.c b/src/r_segs.c
index dbbbf126b1..285ef658ef 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -994,8 +994,6 @@ static boolean R_FFloorCanClip(visffloor_t *pfloor)
 	return (cv_ffloorclip.value && !R_IsFFloorTranslucent(pfloor) && !pfloor->polyobj);
 }
 
-static boolean didsolidcol; // True if at least one column was marked solid
-
 //
 // R_RenderSegLoop
 // Draws zero, one, or two textures (and possibly a masked
@@ -1437,12 +1435,6 @@ static void R_RenderSegLoop (void)
 				floorclip[rw_x] = bottomclip;
 		}
 
-		if ((markceiling || markfloor) && (floorclip[rw_x] <= ceilingclip[rw_x] + 1))
-		{
-			solidcol[rw_x] = 1;
-			didsolidcol = true;
-		}
-
 		if (maskedtexturecol)
 			maskedtexturecol[rw_x] = texturecolumn + rw_offsetx;
 
@@ -1565,12 +1557,6 @@ static void R_MarkSegBounds(void)
 		if (markfloor) // no bottom wall
 			floorclip[rw_x] = bottomclip;
 
-		if (floorclip[rw_x] <= ceilingclip[rw_x] + 1)
-		{
-			solidcol[rw_x] = 1;
-			didsolidcol = true;
-		}
-
 		rw_scale += rw_scalestep;
 		topfrac += topstep;
 		bottomfrac += bottomstep;
@@ -2954,8 +2940,6 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 		}
 	}
 
-	didsolidcol = false;
-
 	if (!segtextured && !numffloors && !numbackffloors)
 	{
 		if (markfloor || markceiling)
@@ -2985,21 +2969,6 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 	else
 		ds_p->portalpass = 0;
 
-	// cph - if a column was made solid by this wall, we _must_ save full clipping info
-	if (backsector && didsolidcol)
-	{
-		if (!(ds_p->silhouette & SIL_BOTTOM))
-		{
-			ds_p->silhouette |= SIL_BOTTOM;
-			ds_p->bsilheight = backsector->f_slope ? INT32_MAX : backsector->floorheight;
-		}
-		if (!(ds_p->silhouette & SIL_TOP))
-		{
-			ds_p->silhouette |= SIL_TOP;
-			ds_p->tsilheight = backsector->c_slope ? INT32_MIN : backsector->ceilingheight;
-		}
-	}
-
 	// save sprite clipping info
 	if (maskedtexture || (ds_p->silhouette & (SIL_TOP | SIL_BOTTOM)))
 	{
-- 
GitLab