From 248a29bf061159c2b6e00b848b5f6dfae279f157 Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@users.noreply.github.com>
Date: Sun, 1 Apr 2018 22:26:57 +0200
Subject: [PATCH] - resorted portal render data.

Compiles but does not work...
---
 src/g_levellocals.h                      |   2 +
 src/gl/data/gl_data.h                    |  23 -----
 src/gl/data/gl_portaldata.cpp            | 123 +++++++++++++----------
 src/gl/data/gl_setup.cpp                 |  50 ---------
 src/gl/dynlights/gl_dynlight1.cpp        |   1 -
 src/gl/gl_functions.h                    |   2 -
 src/gl/renderer/gl_postprocess.cpp       |   1 -
 src/gl/renderer/gl_renderbuffers.cpp     |   1 -
 src/gl/renderer/gl_renderer.cpp          |   2 -
 src/gl/scene/gl_bsp.cpp                  |   6 +-
 src/gl/scene/gl_portal.cpp               |  20 +++-
 src/gl/scene/gl_portal.h                 |  12 +--
 src/gl/scene/gl_renderhacks.cpp          |  12 +--
 src/gl/scene/gl_scene.cpp                |  10 +-
 src/gl/scene/gl_scenedrawer.h            |   2 +-
 src/gl/scene/gl_sky.cpp                  |  11 +-
 src/gl/scene/gl_sprite.cpp               |   3 +-
 src/gl/scene/gl_spritelight.cpp          |   1 -
 src/gl/scene/gl_vertex.cpp               |   1 -
 src/gl/scene/gl_wall.h                   |   8 +-
 src/gl/scene/gl_walls.cpp                |   6 +-
 src/gl/scene/gl_walls_draw.cpp           |   1 -
 src/gl/shaders/gl_ambientshader.cpp      |   1 -
 src/gl/shaders/gl_bloomshader.cpp        |   1 -
 src/gl/shaders/gl_blurshader.cpp         |   1 -
 src/gl/shaders/gl_colormapshader.cpp     |   1 -
 src/gl/shaders/gl_lensshader.cpp         |   1 -
 src/gl/shaders/gl_postprocessshader.cpp  |   1 -
 src/gl/shaders/gl_present3dRowshader.cpp |   1 -
 src/gl/shaders/gl_presentshader.cpp      |   1 -
 src/gl/shaders/gl_shaderprogram.cpp      |   1 -
 src/gl/shaders/gl_shadowmapshader.cpp    |   1 -
 src/gl/shaders/gl_tonemapshader.cpp      |   1 -
 src/gl/system/gl_framebuffer.cpp         |   1 -
 src/p_saveg.cpp                          |   1 +
 src/p_setup.cpp                          |  27 ++++-
 src/portal.cpp                           |  21 ++++
 src/portal.h                             |  31 ++++++
 src/posix/sdl/sdlglvideo.cpp             |   1 -
 src/r_defs.h                             |   8 +-
 40 files changed, 204 insertions(+), 195 deletions(-)

diff --git a/src/g_levellocals.h b/src/g_levellocals.h
index 3fcea30f9..53fea8f97 100644
--- a/src/g_levellocals.h
+++ b/src/g_levellocals.h
@@ -89,6 +89,8 @@ struct FLevelLocals
 	FDisplacementTable Displacements;
 	FPortalBlockmap PortalBlockmap;
 	TArray<FLinePortal*> linkedPortals;	// only the linked portals, this is used to speed up looking for them in P_CollectConnectedGroups.
+	TArray<FSectorPortalGroup *> portalGroups;	
+	TArray<FLinePortalSpan> linePortalSpans;
 
 	TArray<zone_t>	Zones;
 
diff --git a/src/gl/data/gl_data.h b/src/gl/data/gl_data.h
index 75f248b07..bd68bf591 100644
--- a/src/gl/data/gl_data.h
+++ b/src/gl/data/gl_data.h
@@ -20,29 +20,6 @@ inline	int getExtraLight()
 
 struct GLSectorStackPortal;
 
-struct FPortal
-{
-	DVector2 mDisplacement;
-	int plane;
-	GLSectorStackPortal *glportal;	// for quick access to the render data. This is only valid during BSP traversal!
-
-	GLSectorStackPortal *GetRenderState();
-};
-
-struct FGLLinePortal
-{
-	// defines the complete span of this portal, if this is of type PORTT_LINKED.
-	vertex_t	*v1 = nullptr, *v2 = nullptr;	// vertices, from v1 to v2
-	TArray<FLinePortal *> lines;
-	int validcount = 0;
-};
-
-extern TArray<FPortal *> glSectorPortals;
-extern TArray<FGLLinePortal*> linePortalToGL;
-
 extern TArray<uint8_t> currentmapsection;
 
-void gl_InitPortals();
-void gl_BuildPortalCoverage(FPortalCoverage *coverage, subsector_t *subsector, const DVector2 &displacement);
-
 #endif
diff --git a/src/gl/data/gl_portaldata.cpp b/src/gl/data/gl_portaldata.cpp
index eeff9082c..d4af49ad5 100644
--- a/src/gl/data/gl_portaldata.cpp
+++ b/src/gl/data/gl_portaldata.cpp
@@ -48,7 +48,12 @@
 #include "gl/dynlights/gl_dynlight.h"
 #include "gl/dynlights/gl_glow.h"
 #include "gl/utility/gl_clock.h"
-#include "gl/gl_functions.h"
+
+//==========================================================================
+//
+// Helper types for portal grouping
+//
+//==========================================================================
 
 struct FPortalID
 {
@@ -69,25 +74,8 @@ struct FPortalSector
 };
 
 typedef TArray<FPortalSector> FPortalSectors;
-
 typedef TMap<FPortalID, FPortalSectors> FPortalMap;
 
-TArray<FPortal *> glSectorPortals;
-TArray<FGLLinePortal*> linePortalToGL;
-TArray<FGLLinePortal> glLinePortals;
-
-//==========================================================================
-//
-//
-//
-//==========================================================================
-
-GLSectorStackPortal *FPortal::GetRenderState()
-{
-	if (glportal == NULL) glportal = new GLSectorStackPortal(this);
-	return glportal;
-}
-
 //==========================================================================
 //
 // this is left as fixed_t because the nodes also are, it makes no sense
@@ -119,7 +107,6 @@ struct FCoverageBuilder
 	//==========================================================================
 	//
 	//
-	//
 	//==========================================================================
 
 	FCoverageBuilder(subsector_t *sub)
@@ -305,10 +292,16 @@ struct FCoverageBuilder
 //==========================================================================
 //
 // Calculate portal coverage for a single subsector
+// This data is used by the clipper to free up the ranges covered by a portal.
+//
+// This also gets called by the render hack code because ZDoom was really lax
+// with its stacked sector things and allowed partial tagging of affected sectors
+// Any such sector will only be found during rendering and must create its
+// coverage info then.
 //
 //==========================================================================
 
-void gl_BuildPortalCoverage(FPortalCoverage *coverage, subsector_t *subsector, const DVector2 &displacement)
+void BuildPortalCoverage(FPortalCoverage *coverage, subsector_t *subsector, const DVector2 &displacement)
 {
 	TArray<FCoverageVertex> shape;
 	double centerx=0, centery=0;
@@ -332,7 +325,7 @@ void gl_BuildPortalCoverage(FPortalCoverage *coverage, subsector_t *subsector, c
 
 //==========================================================================
 //
-// portal initialization
+//
 //
 //==========================================================================
 
@@ -355,15 +348,20 @@ static void CollectPortalSectors(FPortalMap &collection)
 	}
 }
 
-void gl_InitPortals()
+//==========================================================================
+//
+// group sector portals by displacement
+// The renderer can handle such a group in one go to avoid multiple 
+// BSP traversals
+//
+//==========================================================================
+
+static void GroupSectorPortals()
 {
 	FPortalMap collection;
 
-	if (level.nodes.Size() == 0) return;
-
-	
 	CollectPortalSectors(collection);
-	glSectorPortals.Clear();
+	level.portalGroups.Clear();
 
 	FPortalMap::Iterator it(collection);
 	FPortalMap::Pair *pair;
@@ -371,31 +369,31 @@ void gl_InitPortals()
 	int planeflags = 0;
 	while (it.NextPair(pair))
 	{
-		for(unsigned i=0;i<pair->Value.Size(); i++)
+		for (unsigned i = 0; i < pair->Value.Size(); i++)
 		{
 			if (pair->Value[i].mPlane == sector_t::floor) planeflags |= 1;
 			else if (pair->Value[i].mPlane == sector_t::ceiling) planeflags |= 2;
 		}
-		for (int i=1;i<=2;i<<=1)
+		for (int i = 1; i <= 2; i <<= 1)
 		{
-			// add separate glSectorPortals for floor and ceiling.
+			// add separate portals for floor and ceiling.
 			if (planeflags & i)
 			{
-				FPortal *portal = new FPortal;
+				FSectorPortalGroup *portal = new FSectorPortalGroup;
 				portal->mDisplacement = pair->Key.mDisplacement;
-				portal->plane = (i==1? sector_t::floor : sector_t::ceiling);	/**/
+				portal->plane = (i == 1 ? sector_t::floor : sector_t::ceiling);	/**/
 				portal->glportal = NULL;
-				glSectorPortals.Push(portal);
-				for(unsigned j=0;j<pair->Value.Size(); j++)
+				level.portalGroups.Push(portal);
+				for (unsigned j = 0; j < pair->Value.Size(); j++)
 				{
 					sector_t *sec = pair->Value[j].mSub;
 					int plane = pair->Value[j].mPlane;
 					if (portal->plane == plane)
 					{
-						for(int k=0;k<sec->subsectorcount; k++)
+						for (int k = 0; k < sec->subsectorcount; k++)
 						{
 							subsector_t *sub = sec->subsectors[k];
-							gl_BuildPortalCoverage(&sub->portalcoverage[plane], sub, pair->Key.mDisplacement);
+							BuildPortalCoverage(&sub->portalcoverage[plane], sub, pair->Key.mDisplacement);
 						}
 						sec->portals[plane] = portal;
 					}
@@ -403,10 +401,18 @@ void gl_InitPortals()
 			}
 		}
 	}
+}
 
-	// Now group the line glSectorPortals (each group must be a continuous set of colinear linedefs with no gaps)
-	glLinePortals.Clear();
-	linePortalToGL.Clear();
+//==========================================================================
+//
+// Group the line portals 
+// Each group must be a continuous set of colinear linedefs with no gaps
+//
+//==========================================================================
+
+static void GroupLinePortals()
+{
+	level.linePortalSpans.Clear();
 	TArray<int> tempindex;
 
 	tempindex.Reserve(level.linePortals.Size());
@@ -419,13 +425,13 @@ void gl_InitPortals()
 
 		if (tempindex[i] == -1)
 		{
-			tempindex[i] = glLinePortals.Size();
+			tempindex[i] = level.linePortalSpans.Size();
 			line_t *pSrcLine = level.linePortals[i].mOrigin;
 			line_t *pLine = level.linePortals[i].mDestination;
-			FGLLinePortal &glport = glLinePortals[glLinePortals.Reserve(1)];
+			FLinePortalSpan &glport = level.linePortalSpans[level.linePortalSpans.Reserve(1)];
 			glport.lines.Push(&level.linePortals[i]);
 
-			// We cannot do this grouping for non-linked glSectorPortals because they can be changed at run time.
+			// We cannot do this grouping for non-linked portals because they can be changed at run time.
 			if (level.linePortals[i].mType == PORTT_LINKED && pLine != nullptr)
 			{
 				glport.v1 = pLine->v1;
@@ -440,7 +446,7 @@ void gl_InitPortals()
 						{
 							line_t *pSrcLine2 = level.linePortals[j].mOrigin;
 							line_t *pLine2 = level.linePortals[j].mDestination;
-							// angular precision is intentionally reduced to 32 bit BAM to account for precision problems (otherwise many not perfectly horizontal or vertical glSectorPortals aren't found here.)
+							// angular precision is intentionally reduced to 32 bit BAM to account for precision problems (otherwise many not perfectly horizontal or vertical portals aren't found here.)
 							unsigned srcang = pSrcLine->Delta().Angle().BAMs();
 							unsigned dstang = pLine->Delta().Angle().BAMs();
 							if ((pSrcLine->v2 == pSrcLine2->v1 && pLine->v1 == pLine2->v2) ||
@@ -465,30 +471,37 @@ void gl_InitPortals()
 			}
 		}
 	}
-	linePortalToGL.Resize(level.linePortals.Size());
+
+	// Final assignment can only be done when all allocations are finished. Otherwise the array may be moved.
 	for (unsigned i = 0; i < level.linePortals.Size(); i++)
 	{
-		linePortalToGL[i] = &glLinePortals[tempindex[i]];
-		/*
-		Printf("portal at line %d translates to GL portal %d, range = %f,%f to %f,%f\n",
-			int(level.linePortals[i].mOrigin - lines), tempindex[i], linePortalToGL[i]->v1->fixX() / 65536., linePortalToGL[i]->v1->fixY() / 65536., linePortalToGL[i]->v2->fixX() / 65536., linePortalToGL[i]->v2->fixY() / 65536.);
-		*/
+		level.linePortals[i].mGroup = &level.linePortalSpans[tempindex[i]];
 	}
+
+}
+
+void InitPortalGroups()
+{
+	if (level.nodes.Size() == 0) return;
+
+	GroupSectorPortals();
+	GroupLinePortals();
 }
 
 CCMD(dumpportals)
 {
-	for(unsigned i=0;i<glSectorPortals.Size(); i++)
+	for(unsigned i=0;i<level.portalGroups.Size(); i++)
 	{
-		double xdisp = glSectorPortals[i]->mDisplacement.X;
-		double ydisp = glSectorPortals[i]->mDisplacement.Y;
-		Printf(PRINT_LOG, "Portal #%d, %s, displacement = (%f,%f)\n", i, glSectorPortals[i]->plane==0? "floor":"ceiling",
+		auto p = level.portalGroups[i];
+		double xdisp = p->mDisplacement.X;
+		double ydisp = p->mDisplacement.Y;
+		Printf(PRINT_LOG, "Portal #%d, %s, displacement = (%f,%f)\n", i, p->plane==0? "floor":"ceiling",
 			xdisp, ydisp);
 		Printf(PRINT_LOG, "Coverage:\n");
 		for(auto &sub : level.subsectors)
 		{
-			FPortal *port = sub.render_sector->GetGLPortal(glSectorPortals[i]->plane);
-			if (port == glSectorPortals[i])
+			auto port = sub.render_sector->GetPortalGroup(p->plane);
+			if (port == p)
 			{
 				Printf(PRINT_LOG, "\tSubsector %d (%d):\n\t\t", sub.Index(), sub.render_sector->sectornum);
 				for(unsigned k = 0;k< sub.numlines; k++)
@@ -496,7 +509,7 @@ CCMD(dumpportals)
 					Printf(PRINT_LOG, "(%.3f,%.3f), ",	sub.firstline[k].v1->fX() + xdisp, sub.firstline[k].v1->fY() + ydisp);
 				}
 				Printf(PRINT_LOG, "\n\t\tCovered by subsectors:\n");
-				FPortalCoverage *cov = &sub.portalcoverage[glSectorPortals[i]->plane];
+				FPortalCoverage *cov = &sub.portalcoverage[p->plane];
 				for(int l = 0;l< cov->sscount; l++)
 				{
 					subsector_t *csub = &level.subsectors[cov->subsectors[l]];
diff --git a/src/gl/data/gl_setup.cpp b/src/gl/data/gl_setup.cpp
index 6c1299126..ebb671c63 100644
--- a/src/gl/data/gl_setup.cpp
+++ b/src/gl/data/gl_setup.cpp
@@ -48,7 +48,6 @@
 #include "gl/dynlights/gl_dynlight.h"
 #include "gl/dynlights/gl_glow.h"
 #include "gl/utility/gl_clock.h"
-#include "gl/gl_functions.h"
 
 //==========================================================================
 //
@@ -568,8 +567,6 @@ void gl_PreprocessLevel()
 	}
 	delete[] checkmap;
 
-	gl_InitPortals();
-
 	if (GLRenderer != NULL) 
 	{
 		GLRenderer->SetupLevel();
@@ -582,53 +579,6 @@ void gl_PreprocessLevel()
 
 
 
-//==========================================================================
-//
-// Cleans up all the GL data for the last level
-//
-//==========================================================================
-
-void gl_CleanLevelData()
-{
-	// Dynamic lights must be destroyed before the sector information here is deleted.
-	TThinkerIterator<ADynamicLight> it(STAT_DLIGHT);
-	AActor * mo=it.Next();
-	while (mo)
-	{
-		AActor * next = it.Next();
-		mo->Destroy();
-		mo=next;
-	}
-
-	if (level.sides.Size() > 0 && level.sides[0].segs)
-	{
-		delete [] level.sides[0].segs;
-		level.sides[0].segs = NULL;
-	}
-	if (level.sectors.Size() > 0 && level.sectors[0].subsectors) 
-	{
-		delete [] level.sectors[0].subsectors;
-		level.sectors[0].subsectors = nullptr;
-	}
-	for (auto &sub : level.subsectors)
-	{
-		for(int j=0;j<2;j++)
-		{
-			if (sub.portalcoverage[j].subsectors != NULL)
-			{
-				delete [] sub.portalcoverage[j].subsectors;
-				sub.portalcoverage[j].subsectors = NULL;
-			}
-		}
-	}
-	for(unsigned i=0;i<glSectorPortals.Size(); i++)
-	{
-		delete glSectorPortals[i];
-	}
-	glSectorPortals.Clear();
-}
-
-
 //==========================================================================
 //
 //
diff --git a/src/gl/dynlights/gl_dynlight1.cpp b/src/gl/dynlights/gl_dynlight1.cpp
index 42f1c27cf..dbdbc05d9 100644
--- a/src/gl/dynlights/gl_dynlight1.cpp
+++ b/src/gl/dynlights/gl_dynlight1.cpp
@@ -29,7 +29,6 @@
 #include "c_dispatch.h"
 #include "p_local.h"
 #include "vectors.h"
-#include "gl/gl_functions.h"
 #include "g_level.h"
 #include "actorinlines.h"
 #include "a_dynlight.h"
diff --git a/src/gl/gl_functions.h b/src/gl/gl_functions.h
index 620533f1e..fe47af98a 100644
--- a/src/gl/gl_functions.h
+++ b/src/gl/gl_functions.h
@@ -5,7 +5,5 @@
 
 class AActor;
 
-void gl_PreprocessLevel();
-void gl_CleanLevelData();
 
 #endif
diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp
index b36da7f22..a068cc498 100644
--- a/src/gl/renderer/gl_postprocess.cpp
+++ b/src/gl/renderer/gl_postprocess.cpp
@@ -42,7 +42,6 @@
 #include "r_utility.h"
 #include "p_local.h"
 #include "colormatcher.h"
-#include "gl/gl_functions.h"
 #include "gl/system/gl_interface.h"
 #include "gl/system/gl_framebuffer.h"
 #include "gl/system/gl_cvars.h"
diff --git a/src/gl/renderer/gl_renderbuffers.cpp b/src/gl/renderer/gl_renderbuffers.cpp
index d60045f3e..acc2d5c0d 100644
--- a/src/gl/renderer/gl_renderbuffers.cpp
+++ b/src/gl/renderer/gl_renderbuffers.cpp
@@ -28,7 +28,6 @@
 #include "gl/system/gl_system.h"
 #include "m_swap.h"
 #include "v_video.h"
-#include "gl/gl_functions.h"
 #include "vectors.h"
 #include "gl/system/gl_interface.h"
 #include "gl/system/gl_framebuffer.h"
diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp
index 972ffecba..5b737f125 100644
--- a/src/gl/renderer/gl_renderer.cpp
+++ b/src/gl/renderer/gl_renderer.cpp
@@ -33,8 +33,6 @@
 #include "m_png.h"
 #include "m_crc32.h"
 #include "w_wad.h"
-//#include "gl/gl_intern.h"
-#include "gl/gl_functions.h"
 #include "vectors.h"
 #include "doomstat.h"
 
diff --git a/src/gl/scene/gl_bsp.cpp b/src/gl/scene/gl_bsp.cpp
index 8f44b4fb7..1c8ecd4ad 100644
--- a/src/gl/scene/gl_bsp.cpp
+++ b/src/gl/scene/gl_bsp.cpp
@@ -520,16 +520,16 @@ void GLSceneDrawer::DoSubsector(subsector_t * sub)
 					(sub->numlines > 2) ? SSRF_PROCESSED|SSRF_RENDERALL : SSRF_PROCESSED;
 				if (sub->hacked & 1) gl_drawinfo->AddHackedSubsector(sub);
 
-				FPortal *portal;
+				FSectorPortalGroup *portal;
 
-				portal = fakesector->GetGLPortal(sector_t::ceiling);
+				portal = fakesector->GetPortalGroup(sector_t::ceiling);
 				if (portal != NULL)
 				{
 					GLSectorStackPortal *glportal = portal->GetRenderState();
 					glportal->AddSubsector(sub);
 				}
 
-				portal = fakesector->GetGLPortal(sector_t::floor);
+				portal = fakesector->GetPortalGroup(sector_t::floor);
 				if (portal != NULL)
 				{
 					GLSectorStackPortal *glportal = portal->GetRenderState();
diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp
index d2d0a8b6e..e8a4b73cf 100644
--- a/src/gl/scene/gl_portal.cpp
+++ b/src/gl/scene/gl_portal.cpp
@@ -87,7 +87,7 @@ bool	 GLPortal::inskybox;
 UniqueList<GLSkyInfo> UniqueSkies;
 UniqueList<GLHorizonInfo> UniqueHorizons;
 UniqueList<secplane_t> UniquePlaneMirrors;
-UniqueList<FGLLinePortal> UniqueLineToLines;
+UniqueList<FLinePortalSpan> UniqueLineToLines;
 
 //==========================================================================
 //
@@ -673,6 +673,22 @@ void GLSkyboxPortal::DrawContents()
 //
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
+
+
+//==========================================================================
+//
+// Fixme: This needs abstraction.
+//
+//==========================================================================
+
+GLSectorStackPortal *FSectorPortalGroup::GetRenderState()
+{
+	if (glportal == NULL) glportal = new GLSectorStackPortal(this);
+	return glportal;
+}
+
+
+
 GLSectorStackPortal::~GLSectorStackPortal()
 {
 	if (origin != NULL && origin->glportal == this)
@@ -730,7 +746,7 @@ void GLSectorStackPortal::SetupCoverage()
 //-----------------------------------------------------------------------------
 void GLSectorStackPortal::DrawContents()
 {
-	FPortal *portal = origin;
+	FSectorPortalGroup *portal = origin;
 
 	r_viewpoint.Pos += origin->mDisplacement;
 	r_viewpoint.ActorPos += origin->mDisplacement;
diff --git a/src/gl/scene/gl_portal.h b/src/gl/scene/gl_portal.h
index 6b6f96668..6c3f024f8 100644
--- a/src/gl/scene/gl_portal.h
+++ b/src/gl/scene/gl_portal.h
@@ -74,7 +74,7 @@ struct GLSkyInfo
 extern UniqueList<GLSkyInfo> UniqueSkies;
 extern UniqueList<GLHorizonInfo> UniqueHorizons;
 extern UniqueList<secplane_t> UniquePlaneMirrors;
-extern UniqueList<FGLLinePortal> UniqueLineToLines;
+extern UniqueList<FLinePortalSpan> UniqueLineToLines;
 struct GLEEHorizonPortal;
 class GLSceneDrawer;
 
@@ -198,7 +198,7 @@ struct GLLinePortal : public GLPortal
 		CalcDelta();
 	}
 
-	GLLinePortal(FGLLinePortal *line)
+	GLLinePortal(FLinePortalSpan *line)
 	{
 		if (line->lines[0]->mType != PORTT_LINKED || line->v1 == nullptr)
 		{
@@ -258,7 +258,7 @@ public:
 
 struct GLLineToLinePortal : public GLLinePortal
 {
-	FGLLinePortal *glport;
+	FLinePortalSpan *glport;
 protected:
 	virtual void DrawContents();
 	virtual void * GetSource() const { return glport; }
@@ -268,7 +268,7 @@ protected:
 
 public:
 	
-	GLLineToLinePortal(FGLLinePortal *ll)
+	GLLineToLinePortal(FLinePortalSpan *ll)
 		: GLLinePortal(ll)
 	{
 		glport = ll;
@@ -331,11 +331,11 @@ protected:
 	virtual void * GetSource() const { return origin; }
 	virtual bool IsSky() { return true; }	// although this isn't a real sky it can be handled as one.
 	virtual const char *GetName();
-	FPortal *origin;
+	FSectorPortalGroup *origin;
 
 public:
 	
-	GLSectorStackPortal(FPortal *pt) 
+	GLSectorStackPortal(FSectorPortalGroup *pt) 
 	{
 		origin=pt;
 	}
diff --git a/src/gl/scene/gl_renderhacks.cpp b/src/gl/scene/gl_renderhacks.cpp
index f121a2605..45cc254e4 100644
--- a/src/gl/scene/gl_renderhacks.cpp
+++ b/src/gl/scene/gl_renderhacks.cpp
@@ -1034,7 +1034,7 @@ void FDrawInfo::CollectSectorStacksCeiling(subsector_t * sub, sector_t * anchor)
 	sub->validcount=validcount;
 
 	// Has a sector stack or skybox itself!
-	if (sub->render_sector->GetGLPortal(sector_t::ceiling) != nullptr) return;
+	if (sub->render_sector->GetPortalGroup(sector_t::ceiling) != nullptr) return;
 
 	// Don't bother processing unrendered subsectors
 	if (sub->numlines>2 && !(ss_renderflags[sub->Index()]&SSRF_PROCESSED)) return;
@@ -1078,7 +1078,7 @@ void FDrawInfo::CollectSectorStacksFloor(subsector_t * sub, sector_t * anchor)
 	sub->validcount=validcount;
 
 	// Has a sector stack or skybox itself!
-	if (sub->render_sector->GetGLPortal(sector_t::floor) != nullptr) return;
+	if (sub->render_sector->GetPortalGroup(sector_t::floor) != nullptr) return;
 
 	// Don't bother processing unrendered subsectors
 	if (sub->numlines>2 && !(ss_renderflags[sub->Index()]&SSRF_PROCESSED)) return;
@@ -1125,7 +1125,7 @@ void FDrawInfo::ProcessSectorStacks()
 	for (i=0;i<CeilingStacks.Size (); i++)
 	{
 		sector_t *sec = gl_FakeFlat(CeilingStacks[i], &fake, mDrawer->in_area, false);
-		FPortal *portal = sec->GetGLPortal(sector_t::ceiling);
+		auto portal = sec->GetPortalGroup(sector_t::ceiling);
 		if (portal != NULL) for(int k=0;k<sec->subsectorcount;k++)
 		{
 			subsector_t * sub = sec->subsectors[k];
@@ -1149,7 +1149,7 @@ void FDrawInfo::ProcessSectorStacks()
 
 					if (sub->portalcoverage[sector_t::ceiling].subsectors == NULL)
 					{
-						gl_BuildPortalCoverage(&sub->portalcoverage[sector_t::ceiling],	sub, portal->mDisplacement);
+						BuildPortalCoverage(&sub->portalcoverage[sector_t::ceiling],	sub, portal->mDisplacement);
 					}
 
 					portal->GetRenderState()->AddSubsector(sub);
@@ -1169,7 +1169,7 @@ void FDrawInfo::ProcessSectorStacks()
 	for (i=0;i<FloorStacks.Size (); i++)
 	{
 		sector_t *sec = gl_FakeFlat(FloorStacks[i], &fake, mDrawer->in_area, false);
-		FPortal *portal = sec->GetGLPortal(sector_t::floor);
+		auto portal = sec->GetPortalGroup(sector_t::floor);
 		if (portal != NULL) for(int k=0;k<sec->subsectorcount;k++)
 		{
 			subsector_t * sub = sec->subsectors[k];
@@ -1194,7 +1194,7 @@ void FDrawInfo::ProcessSectorStacks()
 
 					if (sub->portalcoverage[sector_t::floor].subsectors == NULL)
 					{
-						gl_BuildPortalCoverage(&sub->portalcoverage[sector_t::floor], sub, portal->mDisplacement);
+						BuildPortalCoverage(&sub->portalcoverage[sector_t::floor], sub, portal->mDisplacement);
 					}
 
 					GLSectorStackPortal *glportal = portal->GetRenderState();
diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp
index 82d0f196e..e7810d03e 100644
--- a/src/gl/scene/gl_scene.cpp
+++ b/src/gl/scene/gl_scene.cpp
@@ -42,7 +42,6 @@
 #include "po_man.h"
 #include "r_utility.h"
 #include "p_local.h"
-#include "gl/gl_functions.h"
 #include "serializer.h"
 #include "g_levellocals.h"
 #include "events.h"
@@ -266,7 +265,7 @@ void GLSceneDrawer::CreateScene()
 	ProcessAll.Clock();
 
 	// clip the scene and fill the drawlists
-	for(unsigned i=0;i<glSectorPortals.Size(); i++) glSectorPortals[i]->glportal = NULL;
+	for(auto p : level.portalGroups) p->glportal = nullptr;
 	GLRenderer->gl_spriteindex=0;
 	Bsp.Clock();
 	GLRenderer->mVBO->Map();
@@ -462,7 +461,7 @@ void GLSceneDrawer::RenderTranslucent()
 //-----------------------------------------------------------------------------
 //
 // gl_drawscene - this function renders the scene from the current
-// viewpoint, including mirrors and skyboxes and other glSectorPortals
+// viewpoint, including mirrors and skyboxes and other portals
 // It is assumed that the GLPortal::EndFrame returns with the 
 // stencil, z-buffer and the projection matrix intact!
 //
@@ -513,7 +512,7 @@ void GLSceneDrawer::DrawScene(int drawmode)
 		gl_RenderState.ApplyMatrices();
 	}
 
-	// Handle all glSectorPortals after rendering the opaque objects but before
+	// Handle all portals after rendering the opaque objects but before
 	// doing all translucent stuff
 	recursion++;
 	GLPortal::EndFrame();
@@ -1041,7 +1040,6 @@ void FGLInterface::EndSerialize(FSerializer &arc)
 	if (arc.isReading())
 	{
 		// The portal data needs to be recreated after reading a savegame.
-		gl_InitPortals();
 	}
 }
 
@@ -1152,6 +1150,7 @@ void FGLInterface::RenderTextureView (FCanvasTexture *tex, AActor *Viewpoint, do
 // 
 //
 //===========================================================================
+void gl_PreprocessLevel();
 
 void FGLInterface::PreprocessLevel() 
 {
@@ -1160,7 +1159,6 @@ void FGLInterface::PreprocessLevel()
 
 void FGLInterface::CleanLevelData() 
 {
-	gl_CleanLevelData();
 }
 
 uint32_t FGLInterface::GetCaps()
diff --git a/src/gl/scene/gl_scenedrawer.h b/src/gl/scene/gl_scenedrawer.h
index 9ca7132f1..4da0adae1 100644
--- a/src/gl/scene/gl_scenedrawer.h
+++ b/src/gl/scene/gl_scenedrawer.h
@@ -60,7 +60,7 @@ public:
 	void DrawBlend(sector_t * viewsector);
 	void EndDrawScene(sector_t * viewsector);
 	void DrawEndScene2D(sector_t * viewsector);
-	void RenderActorsInPortal(FGLLinePortal *glport);
+	void RenderActorsInPortal(FLinePortalSpan *glport);
 
 	void CheckViewArea(vertex_t *v1, vertex_t *v2, sector_t *frontsector, sector_t *backsector);
 
diff --git a/src/gl/scene/gl_sky.cpp b/src/gl/scene/gl_sky.cpp
index b5f0a0a34..f9defd1f9 100644
--- a/src/gl/scene/gl_sky.cpp
+++ b/src/gl/scene/gl_sky.cpp
@@ -29,7 +29,6 @@
 #include "doomdata.h"
 #include "portal.h"
 #include "g_levellocals.h"
-#include "gl/gl_functions.h"
 
 #include "gl/data/gl_data.h"
 #include "gl/renderer/gl_lightdata.h"
@@ -135,7 +134,7 @@ void GLWall::SkyPlane(sector_t *sector, int plane, bool allowreflect)
 		case PORTS_PORTAL:
 		case PORTS_LINKEDPORTAL:
 		{
-			FPortal *glport = sector->GetGLPortal(plane);
+			FSectorPortalGroup *glport = sector->GetPortalGroup(plane);
 			if (glport != NULL)
 			{
 				if (sector->PortalBlocksView(plane)) return;
@@ -298,8 +297,8 @@ void GLWall::SkyTop(seg_t * seg,sector_t * fs,sector_t * bs,vertex_t * v1,vertex
 			int type = fs->GetPortalType(sector_t::ceiling);
 			if (type == PORTS_STACKEDSECTORTHING || type == PORTS_PORTAL || type == PORTS_LINKEDPORTAL)
 			{
-				FPortal *pfront = fs->GetGLPortal(sector_t::ceiling);
-				FPortal *pback = bs->GetGLPortal(sector_t::ceiling);
+				auto pfront = fs->GetPortalGroup(sector_t::ceiling);
+				auto pback = bs->GetPortalGroup(sector_t::ceiling);
 				if (pfront == NULL || fs->PortalBlocksView(sector_t::ceiling)) return;
 				if (pfront == pback && !bs->PortalBlocksView(sector_t::ceiling)) return;
 			}
@@ -377,8 +376,8 @@ void GLWall::SkyBottom(seg_t * seg,sector_t * fs,sector_t * bs,vertex_t * v1,ver
 			int type = fs->GetPortalType(sector_t::floor);
 			if (type == PORTS_STACKEDSECTORTHING || type == PORTS_PORTAL || type == PORTS_LINKEDPORTAL)
 			{
-				FPortal *pfront = fs->GetGLPortal(sector_t::floor);
-				FPortal *pback = bs->GetGLPortal(sector_t::floor);
+				auto pfront = fs->GetPortalGroup(sector_t::floor);
+				auto pback = bs->GetPortalGroup(sector_t::floor);
 				if (pfront == NULL || fs->PortalBlocksView(sector_t::floor)) return;
 				if (pfront == pback && !bs->PortalBlocksView(sector_t::floor)) return;
 			}
diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp
index 55b998120..860db2193 100644
--- a/src/gl/scene/gl_sprite.cpp
+++ b/src/gl/scene/gl_sprite.cpp
@@ -30,7 +30,6 @@
 #include "p_effect.h"
 #include "g_level.h"
 #include "doomstat.h"
-#include "gl/gl_functions.h"
 #include "r_defs.h"
 #include "r_sky.h"
 #include "r_utility.h"
@@ -1284,7 +1283,7 @@ void GLSprite::ProcessParticle (particle_t *particle, sector_t *sector)//, int s
 //
 //==========================================================================
 
-void GLSceneDrawer::RenderActorsInPortal(FGLLinePortal *glport)
+void GLSceneDrawer::RenderActorsInPortal(FLinePortalSpan *glport)
 {
 	TMap<AActor*, bool> processcheck;
 	if (glport->validcount == validcount) return;	// only process once per frame
diff --git a/src/gl/scene/gl_spritelight.cpp b/src/gl/scene/gl_spritelight.cpp
index bab72ed03..115ec6347 100644
--- a/src/gl/scene/gl_spritelight.cpp
+++ b/src/gl/scene/gl_spritelight.cpp
@@ -30,7 +30,6 @@
 #include "p_local.h"
 #include "p_effect.h"
 #include "vectors.h"
-#include "gl/gl_functions.h"
 #include "g_level.h"
 #include "g_levellocals.h"
 #include "actorinlines.h"
diff --git a/src/gl/scene/gl_vertex.cpp b/src/gl/scene/gl_vertex.cpp
index 9d364e886..07f5303fd 100644
--- a/src/gl/scene/gl_vertex.cpp
+++ b/src/gl/scene/gl_vertex.cpp
@@ -23,7 +23,6 @@
 
 
 #include "gl/system/gl_system.h"
-#include "gl/gl_functions.h"
 
 #include "gl/renderer/gl_renderer.h"
 #include "gl/renderer/gl_lightdata.h"
diff --git a/src/gl/scene/gl_wall.h b/src/gl/scene/gl_wall.h
index 5d041a55f..7151b1809 100644
--- a/src/gl/scene/gl_wall.h
+++ b/src/gl/scene/gl_wall.h
@@ -20,9 +20,9 @@ class FMaterial;
 struct GLDrawList;
 struct GLSkyInfo;
 struct FTexCoordInfo;
-struct FPortal;
+struct FSectorPortalGroup;
 struct FFlatVertex;
-struct FGLLinePortal;
+struct FLinePortalSpan;
 class GLSceneDrawer;
 
 enum
@@ -171,9 +171,9 @@ public:
 		FSectorPortal *secportal;	// sector portal (formerly skybox)
 		GLSkyInfo * sky;			// for normal sky
 		GLHorizonInfo * horizon;	// for horizon information
-		FPortal * portal;			// stacked sector portals
+		FSectorPortalGroup * portal;			// stacked sector portals
 		secplane_t * planemirror;	// for plane mirrors
-		FGLLinePortal *lineportal;	// line-to-line portals
+		FLinePortalSpan *lineportal;	// line-to-line portals
 	};
 
 
diff --git a/src/gl/scene/gl_walls.cpp b/src/gl/scene/gl_walls.cpp
index f7c62bca1..56e9e004f 100644
--- a/src/gl/scene/gl_walls.cpp
+++ b/src/gl/scene/gl_walls.cpp
@@ -179,7 +179,7 @@ void GLWall::PutPortal(int ptype)
 			line_t *otherside = lineportal->lines[0]->mDestination;
 			if (otherside != NULL && otherside->portalindex < level.linePortals.Size())
 			{
-				mDrawer->RenderActorsInPortal(linePortalToGL[otherside->portalindex]);
+				mDrawer->RenderActorsInPortal(otherside->getPortal()->mGroup);
 			}
 			portal = new GLLineToLinePortal(lineportal);
 		}
@@ -1562,7 +1562,7 @@ void GLWall::Process(seg_t *seg, sector_t * frontsector, sector_t * backsector)
 
 		if (seg->linedef->isVisualPortal())
 		{
-			lineportal = linePortalToGL[seg->linedef->portalindex];
+			lineportal = seg->linedef->getPortal()->mGroup;
 			ztop[0] = zceil[0];
 			ztop[1] = zceil[1];
 			zbottom[0] = zfloor[0];
@@ -1669,7 +1669,7 @@ void GLWall::Process(seg_t *seg, sector_t * frontsector, sector_t * backsector)
 
 		if (isportal)
 		{
-			lineportal = linePortalToGL[seg->linedef->portalindex];
+			lineportal = seg->linedef->getPortal()->mGroup;
 			ztop[0] = bch1;
 			ztop[1] = bch2;
 			zbottom[0] = bfh1;
diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp
index 6bb0e7d2f..3cbaa32d4 100644
--- a/src/gl/scene/gl_walls_draw.cpp
+++ b/src/gl/scene/gl_walls_draw.cpp
@@ -27,7 +27,6 @@
 #include "g_levellocals.h"
 #include "actor.h"
 #include "actorinlines.h"
-#include "gl/gl_functions.h"
 
 #include "gl/system/gl_interface.h"
 #include "gl/system/gl_cvars.h"
diff --git a/src/gl/shaders/gl_ambientshader.cpp b/src/gl/shaders/gl_ambientshader.cpp
index 3d0af2dab..e433ebd00 100644
--- a/src/gl/shaders/gl_ambientshader.cpp
+++ b/src/gl/shaders/gl_ambientshader.cpp
@@ -23,7 +23,6 @@
 #include "gl/system/gl_system.h"
 #include "m_swap.h"
 #include "v_video.h"
-#include "gl/gl_functions.h"
 #include "vectors.h"
 #include "gl/system/gl_interface.h"
 #include "gl/system/gl_framebuffer.h"
diff --git a/src/gl/shaders/gl_bloomshader.cpp b/src/gl/shaders/gl_bloomshader.cpp
index bc7b80b01..9dbc4ff6c 100644
--- a/src/gl/shaders/gl_bloomshader.cpp
+++ b/src/gl/shaders/gl_bloomshader.cpp
@@ -28,7 +28,6 @@
 #include "gl/system/gl_system.h"
 #include "m_swap.h"
 #include "v_video.h"
-#include "gl/gl_functions.h"
 #include "vectors.h"
 #include "gl/system/gl_interface.h"
 #include "gl/system/gl_framebuffer.h"
diff --git a/src/gl/shaders/gl_blurshader.cpp b/src/gl/shaders/gl_blurshader.cpp
index 56b637f6d..71cf6ffa3 100644
--- a/src/gl/shaders/gl_blurshader.cpp
+++ b/src/gl/shaders/gl_blurshader.cpp
@@ -28,7 +28,6 @@
 #include "gl/system/gl_system.h"
 #include "m_swap.h"
 #include "v_video.h"
-#include "gl/gl_functions.h"
 #include "vectors.h"
 #include "gl/system/gl_interface.h"
 #include "gl/system/gl_framebuffer.h"
diff --git a/src/gl/shaders/gl_colormapshader.cpp b/src/gl/shaders/gl_colormapshader.cpp
index e29b27013..2036599e9 100644
--- a/src/gl/shaders/gl_colormapshader.cpp
+++ b/src/gl/shaders/gl_colormapshader.cpp
@@ -28,7 +28,6 @@
 #include "gl/system/gl_system.h"
 #include "m_swap.h"
 #include "v_video.h"
-#include "gl/gl_functions.h"
 #include "vectors.h"
 #include "gl/system/gl_interface.h"
 #include "gl/system/gl_framebuffer.h"
diff --git a/src/gl/shaders/gl_lensshader.cpp b/src/gl/shaders/gl_lensshader.cpp
index c5ec679f3..a92b2dece 100644
--- a/src/gl/shaders/gl_lensshader.cpp
+++ b/src/gl/shaders/gl_lensshader.cpp
@@ -28,7 +28,6 @@
 #include "gl/system/gl_system.h"
 #include "m_swap.h"
 #include "v_video.h"
-#include "gl/gl_functions.h"
 #include "vectors.h"
 #include "gl/system/gl_interface.h"
 #include "gl/system/gl_framebuffer.h"
diff --git a/src/gl/shaders/gl_postprocessshader.cpp b/src/gl/shaders/gl_postprocessshader.cpp
index 2d96da90c..459e35b9f 100644
--- a/src/gl/shaders/gl_postprocessshader.cpp
+++ b/src/gl/shaders/gl_postprocessshader.cpp
@@ -23,7 +23,6 @@
 #include "gl/system/gl_system.h"
 #include "m_swap.h"
 #include "v_video.h"
-#include "gl/gl_functions.h"
 #include "vectors.h"
 #include "w_wad.h"
 #include "gl/system/gl_interface.h"
diff --git a/src/gl/shaders/gl_present3dRowshader.cpp b/src/gl/shaders/gl_present3dRowshader.cpp
index 94dd7dc79..0b689f5e8 100644
--- a/src/gl/shaders/gl_present3dRowshader.cpp
+++ b/src/gl/shaders/gl_present3dRowshader.cpp
@@ -30,7 +30,6 @@
 #include "gl/system/gl_system.h"
 #include "m_swap.h"
 #include "v_video.h"
-#include "gl/gl_functions.h"
 #include "vectors.h"
 #include "gl/system/gl_interface.h"
 #include "gl/system/gl_framebuffer.h"
diff --git a/src/gl/shaders/gl_presentshader.cpp b/src/gl/shaders/gl_presentshader.cpp
index 06056f539..b3044b7bc 100644
--- a/src/gl/shaders/gl_presentshader.cpp
+++ b/src/gl/shaders/gl_presentshader.cpp
@@ -28,7 +28,6 @@
 #include "gl/system/gl_system.h"
 #include "m_swap.h"
 #include "v_video.h"
-#include "gl/gl_functions.h"
 #include "vectors.h"
 #include "gl/system/gl_interface.h"
 #include "gl/system/gl_framebuffer.h"
diff --git a/src/gl/shaders/gl_shaderprogram.cpp b/src/gl/shaders/gl_shaderprogram.cpp
index dfb46c544..5db66840d 100644
--- a/src/gl/shaders/gl_shaderprogram.cpp
+++ b/src/gl/shaders/gl_shaderprogram.cpp
@@ -28,7 +28,6 @@
 #include "gl/system/gl_system.h"
 #include "m_swap.h"
 #include "v_video.h"
-#include "gl/gl_functions.h"
 #include "vectors.h"
 #include "gl/system/gl_interface.h"
 #include "gl/system/gl_cvars.h"
diff --git a/src/gl/shaders/gl_shadowmapshader.cpp b/src/gl/shaders/gl_shadowmapshader.cpp
index 8b81d386a..41921ef87 100644
--- a/src/gl/shaders/gl_shadowmapshader.cpp
+++ b/src/gl/shaders/gl_shadowmapshader.cpp
@@ -24,7 +24,6 @@
 #include "files.h"
 #include "m_swap.h"
 #include "v_video.h"
-#include "gl/gl_functions.h"
 #include "vectors.h"
 #include "gl/system/gl_interface.h"
 #include "gl/system/gl_framebuffer.h"
diff --git a/src/gl/shaders/gl_tonemapshader.cpp b/src/gl/shaders/gl_tonemapshader.cpp
index 5045618be..eede7e915 100644
--- a/src/gl/shaders/gl_tonemapshader.cpp
+++ b/src/gl/shaders/gl_tonemapshader.cpp
@@ -28,7 +28,6 @@
 #include "gl/system/gl_system.h"
 #include "m_swap.h"
 #include "v_video.h"
-#include "gl/gl_functions.h"
 #include "vectors.h"
 #include "gl/system/gl_interface.h"
 #include "gl/system/gl_framebuffer.h"
diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp
index be21e49d9..76db15c85 100644
--- a/src/gl/system/gl_framebuffer.cpp
+++ b/src/gl/system/gl_framebuffer.cpp
@@ -45,7 +45,6 @@
 #include "gl/data/gl_data.h"
 #include "gl/textures/gl_hwtexture.h"
 #include "gl/utility/gl_clock.h"
-#include "gl/gl_functions.h"
 #include "gl/data/gl_vertexbuffer.h"
 #include "gl_debug.h"
 #include "r_videoscale.h"
diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp
index ea37c1e19..b3856cfe0 100644
--- a/src/p_saveg.cpp
+++ b/src/p_saveg.cpp
@@ -1024,6 +1024,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload)
 		}
 	}
 	AActor::RecreateAllAttachedLights();
+	InitPortalGroups();
 	Renderer->EndSerialize(arc);
 
 }
diff --git a/src/p_setup.cpp b/src/p_setup.cpp
index 0320aca07..fe8400a88 100644
--- a/src/p_setup.cpp
+++ b/src/p_setup.cpp
@@ -111,6 +111,7 @@
 #include "p_saveg.h"
 #include "g_levellocals.h"
 #include "c_dispatch.h"
+#include "a_dynlight.h"
 #ifndef NO_EDATA
 #include "edata.h"
 #endif
@@ -3499,14 +3500,24 @@ static void P_PrecacheLevel()
 
 extern polyblock_t **PolyBlockMap;
 
-//===========================================================================
+
+//==========================================================================
 //
 //
 //
-//===========================================================================
+//==========================================================================
 
 void P_FreeLevelData ()
 {
+	TThinkerIterator<ADynamicLight> it(STAT_DLIGHT);
+	auto mo = it.Next();
+	while (mo)
+	{
+		auto next = it.Next();
+		mo->Destroy();
+		mo = next;
+	}
+
 	// [ZZ] delete per-map event handlers
 	E_Shutdown(true);
 	MapThingsConverted.Clear();
@@ -3531,14 +3542,26 @@ void P_FreeLevelData ()
 		level.killed_monsters = level.found_items = level.found_secrets =
 		wminfo.maxfrags = 0;
 		
+	// delete allocated data in the level arrays.
 	if (level.sectors.Size() > 0)
 	{
 		delete[] level.sectors[0].e;
+		if (level.sectors[0].subsectors)
+		{
+			delete[] level.sectors[0].subsectors;
+			level.sectors[0].subsectors = nullptr;
+		}
 	}
 	for (auto &sub : level.subsectors)
 	{
 		if (sub.BSP != nullptr) delete sub.BSP;
 	}
+	if (level.sides.Size() > 0 && level.sides[0].segs)
+	{
+		delete[] level.sides[0].segs;
+		level.sides[0].segs = nullptr;
+	}
+
 
 	FBehavior::StaticUnloadModules ();
 	level.segs.Clear();
diff --git a/src/portal.cpp b/src/portal.cpp
index 2565d5d58..2268f8e8e 100644
--- a/src/portal.cpp
+++ b/src/portal.cpp
@@ -507,6 +507,8 @@ void P_ClearPortals()
 	level.linePortals.Clear();
 	level.linkedPortals.Clear();
 	level.sectorPortals.Resize(2);
+	level.PortalBlockmap.Clear();
+
 	// The first entry must always be the default skybox. This is what every sector gets by default.
 	memset(&level.sectorPortals[0], 0, sizeof(level.sectorPortals[0]));
 	level.sectorPortals[0].mType = PORTS_SKYVIEWPOINT;
@@ -515,6 +517,25 @@ void P_ClearPortals()
 	memset(&level.sectorPortals[1], 0, sizeof(level.sectorPortals[0]));
 	level.sectorPortals[1].mType = PORTS_SKYVIEWPOINT;
 	level.sectorPortals[1].mFlags = PORTSF_SKYFLATONLY;
+
+	// also clear the render data
+	for (auto &sub : level.subsectors)
+	{
+		for (int j = 0; j<2; j++)
+		{
+			if (sub.portalcoverage[j].subsectors != nullptr)
+			{
+				delete[] sub.portalcoverage[j].subsectors;
+				sub.portalcoverage[j].subsectors = nullptr;
+			}
+		}
+	}
+	for (unsigned i = 0; i<level.portalGroups.Size(); i++)
+	{
+		delete level.portalGroups[i];
+	}
+	level.portalGroups.Clear();
+	level.linePortalSpans.Clear();
 }
 
 //============================================================================
diff --git a/src/portal.h b/src/portal.h
index a805d52fb..f194c3886 100644
--- a/src/portal.h
+++ b/src/portal.h
@@ -173,6 +173,8 @@ enum
 // All information about a line-to-line portal (all types)
 //
 //============================================================================
+struct FLinePortalSpan;
+struct vertex_t;
 
 struct FLinePortal
 {
@@ -187,8 +189,18 @@ struct FLinePortal
 	double mSinRot;
 	double mCosRot;
 	portnode_t *lineportal_thinglist;
+	FLinePortalSpan *mGroup;
 };
 
+struct FLinePortalSpan
+{
+	// defines the complete span of connected colinear line portals, if they are of type PORTT_LINKED.
+	vertex_t	*v1 = nullptr, *v2 = nullptr;	// vertices, from v1 to v2
+	TArray<FLinePortal *> lines;
+	int validcount = 0;
+};
+
+
 //============================================================================
 //
 // All information about a sector plane portal
@@ -230,6 +242,24 @@ struct FSectorPortal
 	}
 };
 
+//============================================================================
+//
+// This groups all sector portals with identical offset.
+//
+//============================================================================
+
+struct GLSectorStackPortal;
+struct FSectorPortalGroup
+{
+	DVector2 mDisplacement;
+	int plane;
+	GLSectorStackPortal *glportal;	// for quick access to the render data. This is only valid during BSP traversal!
+
+	GLSectorStackPortal *GetRenderState();
+};
+
+
+
 //============================================================================
 //
 // Functions
@@ -255,6 +285,7 @@ void P_TranslatePortalVXVY(line_t* src, double &velx, double &vely);
 void P_TranslatePortalAngle(line_t* src, DAngle& angle);
 void P_TranslatePortalZ(line_t* src, double& vz);
 DVector2 P_GetOffsetPosition(double x, double y, double dx, double dy);
+void InitPortalGroups();
 
 
 #endif
\ No newline at end of file
diff --git a/src/posix/sdl/sdlglvideo.cpp b/src/posix/sdl/sdlglvideo.cpp
index c5074f4d2..4fbf1005f 100644
--- a/src/posix/sdl/sdlglvideo.cpp
+++ b/src/posix/sdl/sdlglvideo.cpp
@@ -49,7 +49,6 @@
 #include "sdlglvideo.h"
 #include "gl/system/gl_system.h"
 #include "r_defs.h"
-#include "gl/gl_functions.h"
 
 #include "gl/renderer/gl_renderer.h"
 #include "gl/system/gl_framebuffer.h"
diff --git a/src/r_defs.h b/src/r_defs.h
index 76a0e1e44..7f17e8466 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -45,7 +45,7 @@
 struct FLightNode;
 struct FGLSection;
 class FSerializer;
-struct FPortal;
+struct FSectorPortalGroup;
 struct FSectorPortal;
 struct FLinePortal;
 struct seg_t;
@@ -1075,7 +1075,7 @@ public:
 	int							subsectorcount;		// list of subsectors
 	double						transdoorheight;	// for transparent door hacks
 	subsector_t **				subsectors;
-	FPortal *					portals[2];			// floor and ceiling portals
+	FSectorPortalGroup *					portals[2];			// floor and ceiling portals
 
 	enum
 	{
@@ -1089,7 +1089,7 @@ public:
 
 	float GetReflect(int pos) { return gl_plane_reflection_i? reflect[pos] : 0; }
 	bool VBOHeightcheck(int pos) const { return vboheight[pos] == GetPlaneTexZ(pos); }
-	FPortal *GetGLPortal(int plane) { return portals[plane]; }
+	FSectorPortalGroup *GetPortalGroup(int plane) { return portals[plane]; }
 
 	enum
 	{
@@ -1418,6 +1418,8 @@ struct FPortalCoverage
 	int			sscount;
 };
 
+void BuildPortalCoverage(FPortalCoverage *coverage, subsector_t *subsector, const DVector2 &displacement);
+
 struct subsector_t
 {
 	sector_t	*sector;