From ba6094be2e90436ce23ba1b2494f1ecb9c6962ff Mon Sep 17 00:00:00 2001
From: Magnus Norddahl <dpjudas@users.noreply.github.com>
Date: Tue, 24 Jan 2017 04:15:54 +0100
Subject: [PATCH] Detach player sprites from VisibleSprite

---
 src/swrenderer/drawers/r_draw.cpp           |   2 +-
 src/swrenderer/r_swrenderer.cpp             |   2 +-
 src/swrenderer/scene/r_translucent_pass.cpp |   3 +-
 src/swrenderer/scene/r_viewport.cpp         |   2 +-
 src/swrenderer/things/r_particle.cpp        |   7 +-
 src/swrenderer/things/r_playersprite.cpp    | 313 +++++++++++---------
 src/swrenderer/things/r_playersprite.h      |  84 ++++--
 src/swrenderer/things/r_sprite.cpp          |  12 +-
 src/swrenderer/things/r_visiblesprite.cpp   |  20 +-
 src/swrenderer/things/r_visiblesprite.h     |  15 +-
 src/swrenderer/things/r_voxel.cpp           |   8 +-
 src/swrenderer/things/r_wallsprite.cpp      |   8 +-
 12 files changed, 284 insertions(+), 192 deletions(-)

diff --git a/src/swrenderer/drawers/r_draw.cpp b/src/swrenderer/drawers/r_draw.cpp
index 46c0ec784..cf3e4edbb 100644
--- a/src/swrenderer/drawers/r_draw.cpp
+++ b/src/swrenderer/drawers/r_draw.cpp
@@ -452,7 +452,7 @@ namespace swrenderer
 		else if (style == LegacyRenderStyles[STYLE_Shaded])
 		{
 			// Shaded drawer only gets 16 levels of alpha because it saves memory.
-			if ((alpha >>= 12) == 0)
+			if ((alpha >>= 12) == 0 || basecolormap == nullptr)
 				return false;
 			colfunc = &SWPixelFormatDrawers::DrawShadedColumn;
 			drawer_needs_pal_input = true;
diff --git a/src/swrenderer/r_swrenderer.cpp b/src/swrenderer/r_swrenderer.cpp
index db0ede2e6..e8e1d2c12 100644
--- a/src/swrenderer/r_swrenderer.cpp
+++ b/src/swrenderer/r_swrenderer.cpp
@@ -217,7 +217,7 @@ void FSoftwareRenderer::DrawRemainingPlayerSprites()
 {
 	if (!r_polyrenderer)
 	{
-		RenderPlayerSprite::RenderRemainingPlayerSprites();
+		RenderPlayerSprites::Instance()->RenderRemaining();
 	}
 	else
 	{
diff --git a/src/swrenderer/scene/r_translucent_pass.cpp b/src/swrenderer/scene/r_translucent_pass.cpp
index cbacf11e1..3cf4998f1 100644
--- a/src/swrenderer/scene/r_translucent_pass.cpp
+++ b/src/swrenderer/scene/r_translucent_pass.cpp
@@ -208,6 +208,7 @@ namespace swrenderer
 			clip3d->DeleteHeights();
 			clip3d->fake3D = 0;
 		}
-		RenderPlayerSprite::RenderPlayerSprites();
+
+		RenderPlayerSprites::Instance()->Render();
 	}
 }
diff --git a/src/swrenderer/scene/r_viewport.cpp b/src/swrenderer/scene/r_viewport.cpp
index df5b37f08..c92509492 100644
--- a/src/swrenderer/scene/r_viewport.cpp
+++ b/src/swrenderer/scene/r_viewport.cpp
@@ -114,7 +114,7 @@ namespace swrenderer
 		WallTMapScale2 = IYaspectMul / CenterX;
 
 		// psprite scales
-		RenderPlayerSprite::SetupSpriteScale();
+		RenderPlayerSprites::Instance()->SetupSpriteScale();
 
 		// thing clipping
 		fillshort(screenheightarray, viewwidth, (short)viewheight);
diff --git a/src/swrenderer/things/r_particle.cpp b/src/swrenderer/things/r_particle.cpp
index ed32c2a00..775e9358e 100644
--- a/src/swrenderer/things/r_particle.cpp
+++ b/src/swrenderer/things/r_particle.cpp
@@ -198,11 +198,10 @@ namespace swrenderer
 		vis->renderflags = (short)(particle->alpha * 255.0f + 0.5f);
 		vis->FakeFlatStat = fakeside;
 		vis->floorclip = 0;
-		vis->ColormapNum = 0;
 		vis->foggy = foggy;
 
 		// Particles are slightly more visible than regular sprites.
-		vis->SetColormap(tiz * r_SpriteVisibility * 0.5, shade, map, particle->bright != 0, false, false);
+		vis->Light.SetColormap(tiz * r_SpriteVisibility * 0.5, shade, map, particle->bright != 0, false, false);
 
 		VisibleSpriteList::Instance()->Push(vis);
 	}
@@ -214,7 +213,7 @@ namespace swrenderer
 		auto vis = this;
 
 		int spacing;
-		BYTE color = vis->BaseColormap->Maps[vis->startfrac];
+		BYTE color = vis->Light.BaseColormap->Maps[vis->startfrac];
 		int yl = vis->y1;
 		int ycount = vis->y2 - yl + 1;
 		int x1 = vis->x1;
@@ -225,7 +224,7 @@ namespace swrenderer
 
 		DrawMaskedSegsBehindParticle();
 
-		uint32_t fg = LightBgra::shade_pal_index_simple(color, LightBgra::calc_light_multiplier(LIGHTSCALE(0, vis->ColormapNum << FRACBITS)));
+		uint32_t fg = LightBgra::shade_pal_index_simple(color, LightBgra::calc_light_multiplier(LIGHTSCALE(0, vis->Light.ColormapNum << FRACBITS)));
 
 		// vis->renderflags holds translucency level (0-255)
 		fixed_t fglevel = ((vis->renderflags + 1) << 8) & ~0x3ff;
diff --git a/src/swrenderer/things/r_playersprite.cpp b/src/swrenderer/things/r_playersprite.cpp
index a259ff1b3..69da6b947 100644
--- a/src/swrenderer/things/r_playersprite.cpp
+++ b/src/swrenderer/things/r_playersprite.cpp
@@ -65,23 +65,20 @@ EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor)
 
 namespace swrenderer
 {
-	TArray<RenderPlayerSprite::vispsp_t> RenderPlayerSprite::vispsprites;
-	unsigned int RenderPlayerSprite::vispspindex;
+	RenderPlayerSprites *RenderPlayerSprites::Instance()
+	{
+		static RenderPlayerSprites instance;
+		return &instance;
+	}
 
-	double RenderPlayerSprite::pspritexscale;
-	double RenderPlayerSprite::pspritexiscale;
-	double RenderPlayerSprite::pspriteyscale;
-
-	TArray<RenderSprite> RenderPlayerSprite::avis;
-
-	void RenderPlayerSprite::SetupSpriteScale()
+	void RenderPlayerSprites::SetupSpriteScale()
 	{
 		pspritexscale = centerxwide / 160.0;
 		pspriteyscale = pspritexscale * YaspectMul;
 		pspritexiscale = 1 / pspritexscale;
 	}
 
-	void RenderPlayerSprite::RenderPlayerSprites()
+	void RenderPlayerSprites::Render()
 	{
 		int 		i;
 		int 		lightnum;
@@ -184,7 +181,7 @@ namespace swrenderer
 
 				if ((psp->GetID() != PSP_TARGETCENTER || CrosshairImage == nullptr) && psp->GetCaller() != nullptr)
 				{
-					Render(psp, camera, bobx, boby, wx, wy, r_TicFracF, spriteshade, basecolormap);
+					RenderSprite(psp, camera, bobx, boby, wx, wy, r_TicFracF, spriteshade, basecolormap, foggy);
 				}
 
 				psp = psp->GetNext();
@@ -194,7 +191,7 @@ namespace swrenderer
 		}
 	}
 
-	void RenderPlayerSprite::Render(DPSprite *pspr, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac, int spriteshade, FDynamicColormap *basecolormap)
+	void RenderPlayerSprites::RenderSprite(DPSprite *pspr, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac, int spriteshade, FDynamicColormap *basecolormap, bool foggy)
 	{
 		double 				tx;
 		int 				x1;
@@ -208,9 +205,6 @@ namespace swrenderer
 		bool				noaccel;
 		double				alpha = owner->Alpha;
 
-		if (avis.Size() < vispspindex + 1)
-			avis.Reserve(avis.Size() - vispspindex + 1);
-
 		// decide which patch to use
 		if ((unsigned)pspr->GetSprite() >= (unsigned)sprites.Size())
 		{
@@ -272,12 +266,11 @@ namespace swrenderer
 			return;
 
 		// store information in a vissprite
-		RenderSprite *vis = &avis[vispspindex];
+		NoAccelPlayerSprite vis;
 
-		vis->renderflags = owner->renderflags;
-		vis->floorclip = 0;
+		vis.renderflags = owner->renderflags;
 
-		vis->texturemid = (BASEYCENTER - sy) * tex->Scale.Y + tex->TopOffset;
+		vis.texturemid = (BASEYCENTER - sy) * tex->Scale.Y + tex->TopOffset;
 
 		if (camera->player && (RenderTarget != screen ||
 			viewheight == RenderTarget->GetHeight() ||
@@ -288,41 +281,39 @@ namespace swrenderer
 			{
 				if (RenderTarget != screen || viewheight == RenderTarget->GetHeight())
 				{
-					vis->texturemid -= weapon->YAdjust;
+					vis.texturemid -= weapon->YAdjust;
 				}
 				else
 				{
-					vis->texturemid -= StatusBar->GetDisplacement() * weapon->YAdjust;
+					vis.texturemid -= StatusBar->GetDisplacement() * weapon->YAdjust;
 				}
 			}
 		}
 		if (pspr->GetID() < PSP_TARGETCENTER)
 		{ // Move the weapon down for 1280x1024.
-			vis->texturemid -= AspectPspriteOffset(WidescreenRatio);
+			vis.texturemid -= AspectPspriteOffset(WidescreenRatio);
 		}
-		vis->x1 = x1 < 0 ? 0 : x1;
-		vis->x2 = x2 >= viewwidth ? viewwidth : x2;
-		vis->xscale = FLOAT2FIXED(pspritexscale / tex->Scale.X);
-		vis->yscale = float(pspriteyscale / tex->Scale.Y);
-		vis->Translation = 0;		// [RH] Use default colors
-		vis->pic = tex;
-		vis->ColormapNum = 0;
+		vis.x1 = x1 < 0 ? 0 : x1;
+		vis.x2 = x2 >= viewwidth ? viewwidth : x2;
+		vis.xscale = FLOAT2FIXED(pspritexscale / tex->Scale.X);
+		vis.yscale = float(pspriteyscale / tex->Scale.Y);
+		vis.pic = tex;
 
 		// If flip is used, provided that it's not already flipped (that would just invert itself)
 		// (It's an XOR...)
 		if (!(flip) != !(pspr->Flags & PSPF_FLIP))
 		{
-			vis->xiscale = -FLOAT2FIXED(pspritexiscale * tex->Scale.X);
-			vis->startfrac = (tex->GetWidth() << FRACBITS) - 1;
+			vis.xiscale = -FLOAT2FIXED(pspritexiscale * tex->Scale.X);
+			vis.startfrac = (tex->GetWidth() << FRACBITS) - 1;
 		}
 		else
 		{
-			vis->xiscale = FLOAT2FIXED(pspritexiscale * tex->Scale.X);
-			vis->startfrac = 0;
+			vis.xiscale = FLOAT2FIXED(pspritexiscale * tex->Scale.X);
+			vis.startfrac = 0;
 		}
 
-		if (vis->x1 > x1)
-			vis->startfrac += vis->xiscale*(vis->x1 - x1);
+		if (vis.x1 > x1)
+			vis.startfrac += vis.xiscale*(vis.x1 - x1);
 
 		noaccel = false;
 		FDynamicColormap *colormap_to_use = nullptr;
@@ -336,42 +327,42 @@ namespace swrenderer
 
 				if (pspr->Flags & PSPF_FORCESTYLE)
 				{
-					vis->RenderStyle = LegacyRenderStyles[rs];
+					vis.RenderStyle = LegacyRenderStyles[rs];
 				}
 				else if (owner->RenderStyle == LegacyRenderStyles[STYLE_Fuzzy])
 				{
-					vis->RenderStyle = LegacyRenderStyles[STYLE_Fuzzy];
+					vis.RenderStyle = LegacyRenderStyles[STYLE_Fuzzy];
 				}
 				else if (owner->RenderStyle == LegacyRenderStyles[STYLE_OptFuzzy])
 				{
-					vis->RenderStyle = LegacyRenderStyles[STYLE_OptFuzzy];
-					vis->RenderStyle.CheckFuzz();
+					vis.RenderStyle = LegacyRenderStyles[STYLE_OptFuzzy];
+					vis.RenderStyle.CheckFuzz();
 				}
 				else if (owner->RenderStyle == LegacyRenderStyles[STYLE_Subtract])
 				{
-					vis->RenderStyle = LegacyRenderStyles[STYLE_Subtract];
+					vis.RenderStyle = LegacyRenderStyles[STYLE_Subtract];
 				}
 				else
 				{
-					vis->RenderStyle = LegacyRenderStyles[rs];
+					vis.RenderStyle = LegacyRenderStyles[rs];
 				}
 			}
 			else
 			{
-				vis->RenderStyle = owner->RenderStyle;
+				vis.RenderStyle = owner->RenderStyle;
 			}
 
 			// Set the alpha based on if using the overlay's own or not. Also adjust
 			// and override the alpha if not forced.
 			if (pspr->Flags & PSPF_ALPHA)
 			{
-				if (vis->RenderStyle == LegacyRenderStyles[STYLE_Fuzzy])
+				if (vis.RenderStyle == LegacyRenderStyles[STYLE_Fuzzy])
 				{
 					alpha = owner->Alpha;
 				}
-				else if (vis->RenderStyle == LegacyRenderStyles[STYLE_OptFuzzy])
+				else if (vis.RenderStyle == LegacyRenderStyles[STYLE_OptFuzzy])
 				{
-					FRenderStyle style = vis->RenderStyle;
+					FRenderStyle style = vis.RenderStyle;
 					style.CheckFuzz();
 					switch (style.BlendOp)
 					{
@@ -385,15 +376,15 @@ namespace swrenderer
 					}
 
 				}
-				else if (vis->RenderStyle == LegacyRenderStyles[STYLE_Subtract])
+				else if (vis.RenderStyle == LegacyRenderStyles[STYLE_Subtract])
 				{
 					alpha = owner->Alpha;
 				}
-				else if (vis->RenderStyle == LegacyRenderStyles[STYLE_Add] ||
-					vis->RenderStyle == LegacyRenderStyles[STYLE_Translucent] ||
-					vis->RenderStyle == LegacyRenderStyles[STYLE_TranslucentStencil] ||
-					vis->RenderStyle == LegacyRenderStyles[STYLE_AddStencil] ||
-					vis->RenderStyle == LegacyRenderStyles[STYLE_AddShaded])
+				else if (vis.RenderStyle == LegacyRenderStyles[STYLE_Add] ||
+					vis.RenderStyle == LegacyRenderStyles[STYLE_Translucent] ||
+					vis.RenderStyle == LegacyRenderStyles[STYLE_TranslucentStencil] ||
+					vis.RenderStyle == LegacyRenderStyles[STYLE_AddStencil] ||
+					vis.RenderStyle == LegacyRenderStyles[STYLE_AddShaded])
 				{
 					alpha = owner->Alpha * pspr->alpha;
 				}
@@ -406,10 +397,10 @@ namespace swrenderer
 			// Should normal renderstyle come out on top at the end and we desire alpha,
 			// switch it to translucent. Normal never applies any sort of alpha.
 			if ((pspr->Flags & PSPF_ALPHA) &&
-				vis->RenderStyle == LegacyRenderStyles[STYLE_Normal] &&
-				vis->Alpha < 1.0)
+				vis.RenderStyle == LegacyRenderStyles[STYLE_Normal] &&
+				vis.Alpha < 1.0)
 			{
-				vis->RenderStyle = LegacyRenderStyles[STYLE_Translucent];
+				vis.RenderStyle = LegacyRenderStyles[STYLE_Translucent];
 				alpha = owner->Alpha * pspr->alpha;
 			}
 
@@ -418,22 +409,22 @@ namespace swrenderer
 			if (pspr->Flags & PSPF_FORCEALPHA)
 			{
 				//Due to lack of != operators...
-				if (vis->RenderStyle == LegacyRenderStyles[STYLE_Fuzzy] ||
-					vis->RenderStyle == LegacyRenderStyles[STYLE_SoulTrans] ||
-					vis->RenderStyle == LegacyRenderStyles[STYLE_Stencil])
+				if (vis.RenderStyle == LegacyRenderStyles[STYLE_Fuzzy] ||
+					vis.RenderStyle == LegacyRenderStyles[STYLE_SoulTrans] ||
+					vis.RenderStyle == LegacyRenderStyles[STYLE_Stencil])
 				{
 				}
 				else
 				{
 					alpha = pspr->alpha;
-					vis->RenderStyle.Flags |= STYLEF_ForceAlpha;
+					vis.RenderStyle.Flags |= STYLEF_ForceAlpha;
 				}
 			}
-			vis->Alpha = clamp<float>(float(alpha), 0.f, 1.f);
+			vis.Alpha = clamp<float>(float(alpha), 0.f, 1.f);
 
 			// Due to how some of the effects are handled, going to 0 or less causes some
 			// weirdness to display. There's no point rendering it anyway if it's 0.
-			if (vis->Alpha <= 0.)
+			if (vis.Alpha <= 0.)
 				return;
 
 			//-----------------------------------------------------------------------------
@@ -441,60 +432,60 @@ namespace swrenderer
 			// The software renderer cannot invert the source without inverting the overlay
 			// too. That means if the source is inverted, we need to do the reverse of what
 			// the invert overlay flag says to do.
-			bool invertcolormap = (vis->RenderStyle.Flags & STYLEF_InvertOverlay) != 0;
+			bool invertcolormap = (vis.RenderStyle.Flags & STYLEF_InvertOverlay) != 0;
 
-			if (vis->RenderStyle.Flags & STYLEF_InvertSource)
+			if (vis.RenderStyle.Flags & STYLEF_InvertSource)
 			{
 				invertcolormap = !invertcolormap;
 			}
 
-			bool fullbright = !vis->foggy && pspr->GetState()->GetFullbright();
-			bool fadeToBlack = (vis->RenderStyle.Flags & STYLEF_FadeToBlack) != 0;
+			bool fullbright = !foggy && pspr->GetState()->GetFullbright();
+			bool fadeToBlack = (vis.RenderStyle.Flags & STYLEF_FadeToBlack) != 0;
 
-			vis->SetColormap(0, spriteshade, basecolormap, fullbright, invertcolormap, fadeToBlack);
+			vis.Light.SetColormap(0, spriteshade, basecolormap, fullbright, invertcolormap, fadeToBlack);
 
-			colormap_to_use = (FDynamicColormap*)vis->BaseColormap;
+			colormap_to_use = (FDynamicColormap*)vis.Light.BaseColormap;
 
 			if (camera->Inventory != nullptr)
 			{
 				visstyle_t visstyle;
-				visstyle.Alpha = vis->Alpha;
+				visstyle.Alpha = vis.Alpha;
 				visstyle.RenderStyle = STYLE_Count;
 				visstyle.Invert = false;
 				
 				camera->Inventory->AlterWeaponSprite(&visstyle);
 				
-				vis->Alpha = visstyle.Alpha;
+				vis.Alpha = visstyle.Alpha;
 
 				if (visstyle.RenderStyle != STYLE_Count)
 				{
-					vis->RenderStyle = visstyle.RenderStyle;
+					vis.RenderStyle = visstyle.RenderStyle;
 				}
 
 				if (visstyle.Invert)
 				{
-					vis->BaseColormap = &SpecialColormaps[INVERSECOLORMAP];
-					vis->ColormapNum = 0;
+					vis.Light.BaseColormap = &SpecialColormaps[INVERSECOLORMAP];
+					vis.Light.ColormapNum = 0;
 					noaccel = true;
 				}
 			}
 			// If we're drawing with a special colormap, but shaders for them are disabled, do
 			// not accelerate.
-			if (!r_shadercolormaps && (vis->BaseColormap >= &SpecialColormaps[0] &&
-				vis->BaseColormap <= &SpecialColormaps.Last()))
+			if (!r_shadercolormaps && (vis.Light.BaseColormap >= &SpecialColormaps[0] &&
+				vis.Light.BaseColormap <= &SpecialColormaps.Last()))
 			{
 				noaccel = true;
 			}
 			// If drawing with a BOOM colormap, disable acceleration.
-			if (vis->BaseColormap == &NormalLight && NormalLight.Maps != realcolormaps.Maps)
+			if (vis.Light.BaseColormap == &NormalLight && NormalLight.Maps != realcolormaps.Maps)
 			{
 				noaccel = true;
 			}
 			// If the main colormap has fixed lights, and this sprite is being drawn with that
 			// colormap, disable acceleration so that the lights can remain fixed.
 			if (!noaccel && realfixedcolormap == nullptr &&
-				NormalLightHasFixedLights && vis->BaseColormap == &NormalLight &&
-				vis->pic->UseBasePalette())
+				NormalLightHasFixedLights && vis.Light.BaseColormap == &NormalLight &&
+				vis.pic->UseBasePalette())
 			{
 				noaccel = true;
 			}
@@ -503,89 +494,145 @@ namespace swrenderer
 		{
 			colormap_to_use = basecolormap;
 
-			vis->BaseColormap = basecolormap;
-			vis->ColormapNum = 0;
+			vis.Light.BaseColormap = basecolormap;
+			vis.Light.ColormapNum = 0;
 		}
 
 		// Check for hardware-assisted 2D. If it's available, and this sprite is not
 		// fuzzy, don't draw it until after the switch to 2D mode.
 		if (!noaccel && RenderTarget == screen && (DFrameBuffer *)screen->Accel2D)
 		{
-			FRenderStyle style = vis->RenderStyle;
+			FRenderStyle style = vis.RenderStyle;
 			style.CheckFuzz();
 			if (style.BlendOp != STYLEOP_Fuzz)
 			{
-				if (vispsprites.Size() < vispspindex + 1)
-					vispsprites.Reserve(vispsprites.Size() - vispspindex + 1);
+				HWAccelPlayerSprite accelSprite;
 
-				vispsprites[vispspindex].vis = vis;
-				vispsprites[vispspindex].basecolormap = colormap_to_use;
-				vispsprites[vispspindex].x1 = x1;
-				vispspindex++;
+				accelSprite.pic = vis.pic;
+				accelSprite.texturemid = vis.texturemid;
+				accelSprite.yscale = vis.yscale;
+				accelSprite.xscale = vis.xscale;
+
+				accelSprite.Alpha = vis.Alpha;
+				accelSprite.RenderStyle = vis.RenderStyle;
+				accelSprite.Translation = vis.Translation;
+				accelSprite.FillColor = vis.FillColor;
+
+				accelSprite.basecolormap = colormap_to_use;
+				accelSprite.x1 = x1;
+				accelSprite.flip = vis.xiscale < 0;
+
+				if (vis.Light.BaseColormap >= &SpecialColormaps[0] &&
+					vis.Light.BaseColormap < &SpecialColormaps[SpecialColormaps.Size()])
+				{
+					accelSprite.special = static_cast<FSpecialColormap*>(vis.Light.BaseColormap);
+				}
+				else if (colormap_to_use->Color == PalEntry(255, 255, 255) &&
+					colormap_to_use->Desaturate == 0)
+				{
+					accelSprite.overlay = colormap_to_use->Fade;
+					accelSprite.overlay.a = BYTE(vis.Light.ColormapNum * 255 / NUMCOLORMAPS);
+				}
+				else
+				{
+					accelSprite.usecolormapstyle = true;
+					accelSprite.colormapstyle.Color = colormap_to_use->Color;
+					accelSprite.colormapstyle.Fade = colormap_to_use->Fade;
+					accelSprite.colormapstyle.Desaturate = colormap_to_use->Desaturate;
+					accelSprite.colormapstyle.FadeLevel = vis.Light.ColormapNum / float(NUMCOLORMAPS);
+				}
+
+				AcceleratedSprites.Push(accelSprite);
 				return;
 			}
 		}
 
-		// clip to screen bounds
-		short *mfloorclip = screenheightarray;
-		short *mceilingclip = zeroarray;
-
-		vis->Render(mfloorclip, mceilingclip, 0, 0);
+		vis.Render();
 	}
 
-	void RenderPlayerSprite::RenderRemainingPlayerSprites()
+	void RenderPlayerSprites::RenderRemaining()
 	{
-		for (unsigned int i = 0; i < vispspindex; i++)
+		for (const HWAccelPlayerSprite &sprite : AcceleratedSprites)
 		{
-			RenderSprite *vis = vispsprites[i].vis;
-			FDynamicColormap *colormap = vispsprites[i].basecolormap;
-			bool flip = vis->xiscale < 0;
-			FSpecialColormap *special = NULL;
-			PalEntry overlay = 0;
-			FColormapStyle colormapstyle;
-			bool usecolormapstyle = false;
-
-			if (vis->BaseColormap >= &SpecialColormaps[0] &&
-				vis->BaseColormap < &SpecialColormaps[SpecialColormaps.Size()])
-			{
-				special = static_cast<FSpecialColormap*>(vis->BaseColormap);
-			}
-			else if (colormap->Color == PalEntry(255, 255, 255) &&
-				colormap->Desaturate == 0)
-			{
-				overlay = colormap->Fade;
-				overlay.a = BYTE(vis->ColormapNum * 255 / NUMCOLORMAPS);
-			}
-			else
-			{
-				usecolormapstyle = true;
-				colormapstyle.Color = colormap->Color;
-				colormapstyle.Fade = colormap->Fade;
-				colormapstyle.Desaturate = colormap->Desaturate;
-				colormapstyle.FadeLevel = vis->ColormapNum / float(NUMCOLORMAPS);
-			}
-			screen->DrawTexture(vis->pic,
-				viewwindowx + vispsprites[i].x1,
-				viewwindowy + viewheight / 2 - vis->texturemid * vis->yscale - 0.5,
-				DTA_DestWidthF, FIXED2DBL(vis->pic->GetWidth() * vis->xscale),
-				DTA_DestHeightF, vis->pic->GetHeight() * vis->yscale,
-				DTA_Translation, TranslationToTable(vis->Translation),
-				DTA_FlipX, flip,
+			screen->DrawTexture(sprite.pic,
+				viewwindowx + sprite.x1,
+				viewwindowy + viewheight / 2 - sprite.texturemid * sprite.yscale - 0.5,
+				DTA_DestWidthF, FIXED2DBL(sprite.pic->GetWidth() * sprite.xscale),
+				DTA_DestHeightF, sprite.pic->GetHeight() * sprite.yscale,
+				DTA_Translation, TranslationToTable(sprite.Translation),
+				DTA_FlipX, sprite.flip,
 				DTA_TopOffset, 0,
 				DTA_LeftOffset, 0,
 				DTA_ClipLeft, viewwindowx,
 				DTA_ClipTop, viewwindowy,
 				DTA_ClipRight, viewwindowx + viewwidth,
 				DTA_ClipBottom, viewwindowy + viewheight,
-				DTA_AlphaF, vis->Alpha,
-				DTA_RenderStyle, vis->RenderStyle,
-				DTA_FillColor, vis->FillColor,
-				DTA_SpecialColormap, special,
-				DTA_ColorOverlay, overlay.d,
-				DTA_ColormapStyle, usecolormapstyle ? &colormapstyle : NULL,
+				DTA_AlphaF, sprite.Alpha,
+				DTA_RenderStyle, sprite.RenderStyle,
+				DTA_FillColor, sprite.FillColor,
+				DTA_SpecialColormap, sprite.special,
+				DTA_ColorOverlay, sprite.overlay.d,
+				DTA_ColormapStyle, sprite.usecolormapstyle ? &sprite.colormapstyle : nullptr,
 				TAG_DONE);
 		}
 
-		vispspindex = 0;
+		AcceleratedSprites.Clear();
+	}
+
+	/////////////////////////////////////////////////////////////////////////
+
+	void NoAccelPlayerSprite::Render()
+	{
+		if (xscale == 0 || fabs(yscale) < (1.0f / 32000.0f))
+		{ // scaled to 0; can't see
+			return;
+		}
+
+		R_SetColorMapLight(Light.BaseColormap, 0, Light.ColormapNum << FRACBITS);
+
+		FDynamicColormap *basecolormap = static_cast<FDynamicColormap*>(Light.BaseColormap);
+
+		bool visible = R_SetPatchStyle(RenderStyle, Alpha, Translation, FillColor, basecolormap);
+
+		if (RenderStyle == LegacyRenderStyles[STYLE_Shaded])
+		{ // For shaded sprites, R_SetPatchStyle sets a dc_colormap to an alpha table, but
+		  // it is the brightest one. We need to get back to the proper light level for
+		  // this sprite.
+			R_SetColorMapLight(drawerargs::dc_fcolormap, 0, Light.ColormapNum << FRACBITS);
+		}
+
+		if (!visible)
+			return;
+
+		double spryscale = yscale;
+		bool sprflipvert = false;
+		fixed_t iscale = FLOAT2FIXED(1 / yscale);
+
+		double sprtopscreen;
+		if (renderflags & RF_YFLIP)
+		{
+			sprflipvert = true;
+			spryscale = -spryscale;
+			iscale = -iscale;
+			sprtopscreen = CenterY + (texturemid - pic->GetHeight()) * spryscale;
+		}
+		else
+		{
+			sprflipvert = false;
+			sprtopscreen = CenterY - texturemid * spryscale;
+		}
+
+		// clip to screen bounds
+		short *mfloorclip = screenheightarray;
+		short *mceilingclip = zeroarray;
+
+		fixed_t frac = startfrac;
+		for (int x = x1; x < x2; x++)
+		{
+			R_DrawMaskedColumn(x, iscale, pic, frac, spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip, false);
+			frac += xiscale;
+		}
+
+		NetUpdate();
 	}
 }
diff --git a/src/swrenderer/things/r_playersprite.h b/src/swrenderer/things/r_playersprite.h
index 75a4e7ba6..b12bbd8d3 100644
--- a/src/swrenderer/things/r_playersprite.h
+++ b/src/swrenderer/things/r_playersprite.h
@@ -14,41 +14,83 @@
 #pragma once
 
 #include "r_visiblesprite.h"
-#include "r_sprite.h"
+#include "r_data/colormaps.h"
 
 class DPSprite;
 
 namespace swrenderer
 {
-	class RenderPlayerSprite
+	class NoAccelPlayerSprite
 	{
 	public:
-		static void SetupSpriteScale();
+		short x1 = 0;
+		short x2 = 0;
 
-		static void RenderPlayerSprites();
-		static void RenderRemainingPlayerSprites();
+		double texturemid = 0.0;
+
+		fixed_t xscale = 0;
+		float yscale = 0.0f;
+
+		FTexture *pic = nullptr;
+
+		fixed_t xiscale = 0;
+		fixed_t startfrac = 0;
+
+		float Alpha = 0.0f;
+		FRenderStyle RenderStyle;
+		uint32_t Translation = 0;
+		uint32_t FillColor = 0;
+
+		ColormapLight Light;
+
+		short renderflags = 0;
+
+		void Render();
+	};
+
+	class HWAccelPlayerSprite
+	{
+	public:
+		FTexture *pic = nullptr;
+		double texturemid = 0.0;
+		float yscale = 0.0f;
+		fixed_t xscale = 0;
+
+		float Alpha = 0.0f;
+		FRenderStyle RenderStyle;
+		uint32_t Translation = 0;
+		uint32_t FillColor = 0;
+
+		FDynamicColormap *basecolormap = nullptr;
+		int x1 = 0;
+
+		bool flip = false;
+		FSpecialColormap *special = nullptr;
+		PalEntry overlay = 0;
+		FColormapStyle colormapstyle;
+		bool usecolormapstyle = false;
+	};
+
+	class RenderPlayerSprites
+	{
+	public:
+		static RenderPlayerSprites *Instance();
+
+		void SetupSpriteScale();
+
+		void Render();
+		void RenderRemaining();
 
 	private:
-		static void Render(DPSprite *pspr, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac, int spriteshade, FDynamicColormap *basecolormap);
+		void RenderSprite(DPSprite *pspr, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac, int spriteshade, FDynamicColormap *basecolormap, bool foggy);
 
 		enum { BASEXCENTER = 160 };
 		enum { BASEYCENTER = 100 };
 
-		// Used to store a psprite's drawing information if it needs to be drawn later.
-		struct vispsp_t
-		{
-			RenderSprite *vis;
-			FDynamicColormap *basecolormap;
-			int	 x1;
-		};
+		TArray<HWAccelPlayerSprite> AcceleratedSprites;
 
-		static TArray<vispsp_t> vispsprites;
-		static unsigned int vispspindex;
-
-		static double pspritexscale;
-		static double pspritexiscale;
-		static double pspriteyscale;
-
-		static TArray<RenderSprite> avis;
+		double pspritexscale = 0.0;
+		double pspritexiscale = 0.0;
+		double pspriteyscale = 0.0;
 	};
 }
diff --git a/src/swrenderer/things/r_sprite.cpp b/src/swrenderer/things/r_sprite.cpp
index d237348f4..1c91c75b0 100644
--- a/src/swrenderer/things/r_sprite.cpp
+++ b/src/swrenderer/things/r_sprite.cpp
@@ -201,7 +201,6 @@ namespace swrenderer
 		vis->Alpha = float(thing->Alpha);
 		vis->fakefloor = fakefloor;
 		vis->fakeceiling = fakeceiling;
-		vis->ColormapNum = 0;
 		//vis->bInMirror = renderportal->MirrorFlags & RF_XFLIP;
 		//vis->bSplitSprite = false;
 
@@ -224,7 +223,7 @@ namespace swrenderer
 		bool fullbright = !vis->foggy && ((renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT));
 		bool fadeToBlack = (vis->RenderStyle.Flags & STYLEF_FadeToBlack) != 0;
 
-		vis->SetColormap(r_SpriteVisibility / MAX(tz, MINZ), spriteshade, basecolormap, fullbright, invertcolormap, fadeToBlack);
+		vis->Light.SetColormap(r_SpriteVisibility / MAX(tz, MINZ), spriteshade, basecolormap, fullbright, invertcolormap, fadeToBlack);
 
 		VisibleSpriteList::Instance()->Push(vis);
 	}
@@ -237,7 +236,6 @@ namespace swrenderer
 		FTexture		*tex;
 		int				x2;
 		fixed_t			xiscale;
-		bool			ispsprite = (!vis->sector && vis->gpos != FVector3(0, 0, 0));
 
 		double spryscale, sprtopscreen;
 		bool sprflipvert;
@@ -248,9 +246,9 @@ namespace swrenderer
 		}
 
 		fixed_t centeryfrac = FLOAT2FIXED(CenterY);
-		R_SetColorMapLight(vis->BaseColormap, 0, vis->ColormapNum << FRACBITS);
+		R_SetColorMapLight(vis->Light.BaseColormap, 0, vis->Light.ColormapNum << FRACBITS);
 
-		FDynamicColormap *basecolormap = static_cast<FDynamicColormap*>(vis->BaseColormap);
+		FDynamicColormap *basecolormap = static_cast<FDynamicColormap*>(vis->Light.BaseColormap);
 
 		bool visible = R_SetPatchStyle(vis->RenderStyle, vis->Alpha, vis->Translation, vis->FillColor, basecolormap);
 
@@ -258,7 +256,7 @@ namespace swrenderer
 		{ // For shaded sprites, R_SetPatchStyle sets a dc_colormap to an alpha table, but
 		  // it is the brightest one. We need to get back to the proper light level for
 		  // this sprite.
-			R_SetColorMapLight(drawerargs::dc_fcolormap, 0, vis->ColormapNum << FRACBITS);
+			R_SetColorMapLight(drawerargs::dc_fcolormap, 0, vis->Light.ColormapNum << FRACBITS);
 		}
 
 		if (visible)
@@ -292,7 +290,7 @@ namespace swrenderer
 			{
 				while (x < x2)
 				{
-					if (ispsprite || !RenderTranslucentPass::ClipSpriteColumnWithPortals(x, vis))
+					if (!RenderTranslucentPass::ClipSpriteColumnWithPortals(x, vis))
 						R_DrawMaskedColumn(x, iscale, tex, frac, spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip, false);
 					x++;
 					frac += xiscale;
diff --git a/src/swrenderer/things/r_visiblesprite.cpp b/src/swrenderer/things/r_visiblesprite.cpp
index 5007cd10d..73cf909f4 100644
--- a/src/swrenderer/things/r_visiblesprite.cpp
+++ b/src/swrenderer/things/r_visiblesprite.cpp
@@ -52,8 +52,8 @@ namespace swrenderer
 		int r1, r2;
 		short topclip, botclip;
 		short *clip1, *clip2;
-		FSWColormap *colormap = spr->BaseColormap;
-		int colormapnum = spr->ColormapNum;
+		FSWColormap *colormap = spr->Light.BaseColormap;
+		int colormapnum = spr->Light.ColormapNum;
 		F3DFloor *rover;
 
 		Clip3DFloors *clip3d = Clip3DFloors::Instance();
@@ -134,7 +134,7 @@ namespace swrenderer
 
 				int spriteshade = LIGHT2SHADE(sec->lightlevel + R_ActualExtraLight(spr->foggy));
 
-				SetColormap(r_SpriteVisibility / MAX(MINZ, (double)spr->depth), spriteshade, mybasecolormap, isFullBright, invertcolormap, fadeToBlack);
+				Light.SetColormap(r_SpriteVisibility / MAX(MINZ, (double)spr->depth), spriteshade, mybasecolormap, isFullBright, invertcolormap, fadeToBlack);
 			}
 		}
 
@@ -259,8 +259,8 @@ namespace swrenderer
 
 		if (topclip >= botclip)
 		{
-			spr->BaseColormap = colormap;
-			spr->ColormapNum = colormapnum;
+			spr->Light.BaseColormap = colormap;
+			spr->Light.ColormapNum = colormapnum;
 			return;
 		}
 
@@ -386,8 +386,8 @@ namespace swrenderer
 				}
 				if (i == x2)
 				{
-					spr->BaseColormap = colormap;
-					spr->ColormapNum = colormapnum;
+					spr->Light.BaseColormap = colormap;
+					spr->Light.ColormapNum = colormapnum;
 					return;
 				}
 			}
@@ -405,11 +405,11 @@ namespace swrenderer
 			int maxvoxely = spr->gzb > hzb ? INT_MAX : xs_RoundToInt((spr->gzt - hzb) / spr->yscale);
 			spr->Render(cliptop, clipbot, minvoxely, maxvoxely);
 		}
-		spr->BaseColormap = colormap;
-		spr->ColormapNum = colormapnum;
+		spr->Light.BaseColormap = colormap;
+		spr->Light.ColormapNum = colormapnum;
 	}
 
-	void VisibleSprite::SetColormap(double visibility, int shade, FDynamicColormap *basecolormap, bool fullbright, bool invertColormap, bool fadeToBlack)
+	void ColormapLight::SetColormap(double visibility, int shade, FDynamicColormap *basecolormap, bool fullbright, bool invertColormap, bool fadeToBlack)
 	{
 		if (fadeToBlack)
 		{
diff --git a/src/swrenderer/things/r_visiblesprite.h b/src/swrenderer/things/r_visiblesprite.h
index bfe8a84e0..edc83667e 100644
--- a/src/swrenderer/things/r_visiblesprite.h
+++ b/src/swrenderer/things/r_visiblesprite.h
@@ -23,6 +23,15 @@ struct FSWColormap;
 
 namespace swrenderer
 {
+	class ColormapLight
+	{
+	public:
+		int ColormapNum = 0;
+		FSWColormap *BaseColormap = nullptr;
+
+		void SetColormap(double visibility, int shade, FDynamicColormap *basecolormap, bool fullbright, bool invertColormap, bool fadeToBlack);
+	};
+
 	class VisibleSprite
 	{
 	public:
@@ -38,8 +47,6 @@ namespace swrenderer
 		float SortDist() const { return idepth; }
 
 	protected:
-		void SetColormap(double visibility, int shade, FDynamicColormap *basecolormap, bool fullbright, bool invertColormap, bool fadeToBlack);
-
 		virtual bool IsParticle() const { return false; }
 		virtual bool IsVoxel() const { return false; }
 		virtual bool IsWallSprite() const { return false; }
@@ -65,9 +72,7 @@ namespace swrenderer
 		FVector3 gpos = { 0.0f, 0.0f, 0.0f }; // origin in world coordinates
 		sector_t *sector = nullptr; // sector this sprite is in
 
-		// Light shared calculation?
-		int ColormapNum = 0;	// Which colormap is rendered
-		FSWColormap *BaseColormap = nullptr;	// Base colormap used together with ColormapNum
+		ColormapLight Light;
 		float Alpha = 0.0f;
 		FRenderStyle RenderStyle;
 		bool foggy = false;
diff --git a/src/swrenderer/things/r_voxel.cpp b/src/swrenderer/things/r_voxel.cpp
index 7706da69b..a4b44e008 100644
--- a/src/swrenderer/things/r_voxel.cpp
+++ b/src/swrenderer/things/r_voxel.cpp
@@ -149,7 +149,7 @@ namespace swrenderer
 		vis->Alpha = float(thing->Alpha);
 		vis->fakefloor = fakefloor;
 		vis->fakeceiling = fakeceiling;
-		vis->ColormapNum = 0;
+		vis->Light.ColormapNum = 0;
 		//vis->bInMirror = renderportal->MirrorFlags & RF_XFLIP;
 		//vis->bSplitSprite = false;
 
@@ -175,7 +175,7 @@ namespace swrenderer
 		bool fullbright = !vis->foggy && ((renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT));
 		bool fadeToBlack = (vis->RenderStyle.Flags & STYLEF_FadeToBlack) != 0;
 
-		vis->SetColormap(r_SpriteVisibility / MAX(tz, MINZ), spriteshade, basecolormap, fullbright, invertcolormap, fadeToBlack);
+		vis->Light.SetColormap(r_SpriteVisibility / MAX(tz, MINZ), spriteshade, basecolormap, fullbright, invertcolormap, fadeToBlack);
 
 		VisibleSpriteList::Instance()->Push(vis);
 		RenderTranslucentPass::DrewAVoxel = true;
@@ -185,9 +185,9 @@ namespace swrenderer
 	{
 		auto sprite = this;
 
-		FDynamicColormap *basecolormap = static_cast<FDynamicColormap*>(sprite->BaseColormap);
+		FDynamicColormap *basecolormap = static_cast<FDynamicColormap*>(sprite->Light.BaseColormap);
 
-		R_SetColorMapLight(sprite->BaseColormap, 0, sprite->ColormapNum << FRACBITS);
+		R_SetColorMapLight(sprite->Light.BaseColormap, 0, sprite->Light.ColormapNum << FRACBITS);
 
 		bool visible = R_SetPatchStyle(sprite->RenderStyle, sprite->Alpha, sprite->Translation, sprite->FillColor, basecolormap);
 		if (!visible)
diff --git a/src/swrenderer/things/r_wallsprite.cpp b/src/swrenderer/things/r_wallsprite.cpp
index 44477c1e1..76691e07d 100644
--- a/src/swrenderer/things/r_wallsprite.cpp
+++ b/src/swrenderer/things/r_wallsprite.cpp
@@ -132,7 +132,7 @@ namespace swrenderer
 		vis->wallc = wallc;
 		vis->foggy = foggy;
 
-		vis->SetColormap(r_SpriteVisibility / MAX(tz, MINZ), spriteshade, basecolormap, false, false, false);
+		vis->Light.SetColormap(r_SpriteVisibility / MAX(tz, MINZ), spriteshade, basecolormap, false, false, false);
 
 		VisibleSpriteList::Instance()->Push(vis);
 	}
@@ -165,7 +165,7 @@ namespace swrenderer
 		}
 		// Prepare lighting
 		bool calclighting = false;
-		FSWColormap *usecolormap = spr->BaseColormap;
+		FSWColormap *usecolormap = spr->Light.BaseColormap;
 		bool rereadcolormap = true;
 
 		// Decals that are added to the scene must fade to black.
@@ -206,14 +206,14 @@ namespace swrenderer
 
 		int x = x1;
 
-		FDynamicColormap *basecolormap = static_cast<FDynamicColormap*>(spr->BaseColormap);
+		FDynamicColormap *basecolormap = static_cast<FDynamicColormap*>(spr->Light.BaseColormap);
 
 		bool visible = R_SetPatchStyle(spr->RenderStyle, spr->Alpha, spr->Translation, spr->FillColor, basecolormap);
 
 		// R_SetPatchStyle can modify basecolormap.
 		if (rereadcolormap)
 		{
-			usecolormap = spr->BaseColormap;
+			usecolormap = spr->Light.BaseColormap;
 		}
 
 		if (!visible)