diff --git a/src/android/i_video.c b/src/android/i_video.c
index 2d0151f5e27cd906e4856109fd8529772c704955..44e1cbac050bea57a929efcb3c2596193db812ae 100644
--- a/src/android/i_video.c
+++ b/src/android/i_video.c
@@ -51,6 +51,11 @@ INT32 VID_SetMode(INT32 modenum)
   return 0;
 }
 
+void VID_CheckRenderer(void)
+{
+	// ..............
+}
+
 const char *VID_GetModeName(INT32 modenum)
 {
   return "A320x240";
diff --git a/src/console.c b/src/console.c
index 91baf25fcfe4fbfd6c2240258a8eccda23bebd8a..826153ff00245a82caa99c7d0918d316feb5acd5 100644
--- a/src/console.c
+++ b/src/console.c
@@ -1269,7 +1269,7 @@ void CONS_Printf(const char *fmt, ...)
 	if (con_startup)
 	{
 #if (defined (_WINDOWS)) || (defined (__OS2__) && !defined (HAVE_SDL))
-		patch_t *con_backpic = W_CachePatchName("CONSBACK", PU_CACHE);
+		patch_t *con_backpic = W_CachePatchName("CONSBACK", PU_PATCH);
 
 		// Jimita: CON_DrawBackpic just called V_DrawScaledPatch
 		V_DrawScaledPatch(0, 0, 0, con_backpic);
@@ -1526,7 +1526,7 @@ static void CON_DrawConsole(void)
 	// draw console background
 	if (cons_backpic.value || con_forcepic)
 	{
-		patch_t *con_backpic = W_CachePatchName("CONSBACK", PU_CACHE);
+		patch_t *con_backpic = W_CachePatchName("CONSBACK", PU_PATCH);
 
 		// Jimita: CON_DrawBackpic just called V_DrawScaledPatch
 		V_DrawScaledPatch(0, 0, 0, con_backpic);
@@ -1583,6 +1583,12 @@ void CON_Drawer(void)
 	if (!con_started || !graphics_started)
 		return;
 
+	if (needpatchrecache)
+	{
+		W_FlushCachedPatches();
+		HU_LoadGraphics();
+	}
+
 	if (con_recalc)
 		CON_RecalcSize();
 
diff --git a/src/d_main.c b/src/d_main.c
index 3344732e511629a6a5d5a45b146ee9949ef9c9c0..06489ac7c91dd8aaa700f7896be01ecc71bcf0e6 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -227,6 +227,7 @@ gamestate_t wipegamestate = GS_LEVEL;
 
 static void D_Display(void)
 {
+	INT32 setrenderstillneeded = setrenderneeded;
 	boolean forcerefresh = false;
 	static boolean wipe = false;
 	INT32 wipedefindex = 0;
@@ -237,15 +238,19 @@ static void D_Display(void)
 	if (nodrawers)
 		return; // for comparative timing/profiling
 
+	// stop movie if needs to change renderer
+	if (setrenderneeded && (moviemode != MM_OFF))
+		M_StopMovie();
+
 	// check for change of screen size (video mode)
-	if (setmodeneeded && !wipe)
+	if ((setmodeneeded || setrenderneeded) && !wipe)
 		SCR_SetMode(); // change video mode
 
-	if (vid.recalc)
+	if (vid.recalc || setrenderstillneeded)
 		SCR_Recalc(); // NOTE! setsizeneeded is set by SCR_Recalc()
 
 	// change the view size if needed
-	if (setsizeneeded)
+	if (setsizeneeded || setrenderstillneeded)
 	{
 		R_ExecuteSetViewSize();
 		forcerefresh = true; // force background redraw
@@ -426,7 +431,7 @@ static void D_Display(void)
 			py = 4;
 		else
 			py = viewwindowy + 4;
-		patch = W_CachePatchName("M_PAUSE", PU_CACHE);
+		patch = W_CachePatchName("M_PAUSE", PU_PATCH);
 		V_DrawScaledPatch(viewwindowx + (BASEVIDWIDTH - SHORT(patch->width))/2, py, 0, patch);
 	}
 
@@ -489,6 +494,12 @@ static void D_Display(void)
 
 		I_FinishUpdate(); // page flip or blit buffer
 	}
+
+	if (needpatchrecache)
+		R_ReloadHUDGraphics();
+
+	needpatchflush = false;
+	needpatchrecache = false;
 }
 
 // =========================================================================
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index cc9127af018ca6f6863cd124efe1e9f9026d54cf..1e9915e1de7538d02ffd56b63337280ef343f651 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -771,6 +771,7 @@ void D_RegisterClientCommands(void)
 	// screen.c
 	CV_RegisterVar(&cv_fullscreen);
 	CV_RegisterVar(&cv_renderview);
+	CV_RegisterVar(&cv_renderer);
 	CV_RegisterVar(&cv_scr_depth);
 	CV_RegisterVar(&cv_scr_width);
 	CV_RegisterVar(&cv_scr_height);
diff --git a/src/djgppdos/vid_vesa.c b/src/djgppdos/vid_vesa.c
index ec7b8b886343d0d8fe9c8d9e66eff3c1aeaf1793..c8ce7dae52e645372455b76cbad40ffd03c53a0e 100644
--- a/src/djgppdos/vid_vesa.c
+++ b/src/djgppdos/vid_vesa.c
@@ -378,6 +378,11 @@ INT32 VID_SetMode (INT32 modenum)  //, UINT8 *palette)
 	return 1;
 }
 
+void VID_CheckRenderer(void)
+{
+	// ..............
+}
+
 
 
 // converts a segm:offs 32bit pair to a 32bit flat ptr
diff --git a/src/dummy/i_video.c b/src/dummy/i_video.c
index e167e833fb152d6fcc5daa7a490cede554944d3b..b8f40bed382d02844bbb8b6087fdb20b783ed17b 100644
--- a/src/dummy/i_video.c
+++ b/src/dummy/i_video.c
@@ -39,6 +39,11 @@ INT32 VID_SetMode(INT32 modenum)
 	return 0;
 }
 
+void VID_CheckRenderer(void)
+{
+	// ..............
+}
+
 const char *VID_GetModeName(INT32 modenum)
 {
 	(void)modenum;
diff --git a/src/f_finale.c b/src/f_finale.c
index 63221fc23bc6237d0ab71522aef575297736264f..58d57948b764eb83d39f2f4e0543892e5207bc31 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -466,16 +466,16 @@ static void F_IntroDrawScene(void)
 	// DRAW A FULL PIC INSTEAD OF FLAT!
 	if (intro_scenenum == 0);
 	else if (intro_scenenum == 1)
-		background = W_CachePatchName("INTRO1", PU_CACHE);
+		background = W_CachePatchName("INTRO1", PU_PATCH);
 	else if (intro_scenenum == 2)
 	{
-		background = W_CachePatchName("INTRO2", PU_CACHE);
+		background = W_CachePatchName("INTRO2", PU_PATCH);
 		highres = true;
 	}
 	else if (intro_scenenum == 3)
-		background = W_CachePatchName("INTRO3", PU_CACHE);
+		background = W_CachePatchName("INTRO3", PU_PATCH);
 	else if (intro_scenenum == 4)
-		background = W_CachePatchName("INTRO4", PU_CACHE);
+		background = W_CachePatchName("INTRO4", PU_PATCH);
 	else if (intro_scenenum == 5)
 	{
 		if (intro_curtime >= 5*TICRATE)
@@ -734,7 +734,7 @@ static void F_IntroDrawScene(void)
 		if (roidtics >= 0)
 		{
 			V_DrawScaledPatch(roidtics, 24, 0,
-				(patch = W_CachePatchName(va("ROID00%.2d", intro_curtime%35), PU_CACHE)));
+				(patch = W_CachePatchName(va("ROID00%.2d", intro_curtime%35), PU_PATCH)));
 			W_UnlockCachedPatch(patch);
 		}
 	}
@@ -1359,7 +1359,6 @@ void F_GameEvaluationDrawer(void)
 
 			if (ALL7EMERALDS(emeralds))
 				++timesBeatenWithEmeralds;
-
 			if (ultimatemode)
 				++timesBeatenUltimate;
 
@@ -1447,10 +1446,28 @@ void F_GameEndTicker(void)
 		D_StartTitle();
 }
 
-
 // ==============
 //  TITLE SCREEN
 // ==============
+static void F_CacheTitleScreen(void)
+{
+	ttbanner = W_CachePatchName("TTBANNER", PU_LEVEL);
+	ttwing = W_CachePatchName("TTWING", PU_LEVEL);
+	ttsonic = W_CachePatchName("TTSONIC", PU_LEVEL);
+	ttswave1 = W_CachePatchName("TTSWAVE1", PU_LEVEL);
+	ttswave2 = W_CachePatchName("TTSWAVE2", PU_LEVEL);
+	ttswip1 = W_CachePatchName("TTSWIP1", PU_LEVEL);
+	ttsprep1 = W_CachePatchName("TTSPREP1", PU_LEVEL);
+	ttsprep2 = W_CachePatchName("TTSPREP2", PU_LEVEL);
+	ttspop1 = W_CachePatchName("TTSPOP1", PU_LEVEL);
+	ttspop2 = W_CachePatchName("TTSPOP2", PU_LEVEL);
+	ttspop3 = W_CachePatchName("TTSPOP3", PU_LEVEL);
+	ttspop4 = W_CachePatchName("TTSPOP4", PU_LEVEL);
+	ttspop5 = W_CachePatchName("TTSPOP5", PU_LEVEL);
+	ttspop6 = W_CachePatchName("TTSPOP6", PU_LEVEL);
+	ttspop7 = W_CachePatchName("TTSPOP7", PU_LEVEL);
+}
+
 void F_StartTitleScreen(void)
 {
 	if (gamestate != GS_TITLESCREEN && gamestate != GS_WAITINGPLAYERS)
@@ -1469,21 +1486,7 @@ void F_StartTitleScreen(void)
 	demoDelayLeft = demoDelayTime;
 	demoIdleLeft = demoIdleTime;
 
-	ttbanner = W_CachePatchName("TTBANNER", PU_LEVEL);
-	ttwing = W_CachePatchName("TTWING", PU_LEVEL);
-	ttsonic = W_CachePatchName("TTSONIC", PU_LEVEL);
-	ttswave1 = W_CachePatchName("TTSWAVE1", PU_LEVEL);
-	ttswave2 = W_CachePatchName("TTSWAVE2", PU_LEVEL);
-	ttswip1 = W_CachePatchName("TTSWIP1", PU_LEVEL);
-	ttsprep1 = W_CachePatchName("TTSPREP1", PU_LEVEL);
-	ttsprep2 = W_CachePatchName("TTSPREP2", PU_LEVEL);
-	ttspop1 = W_CachePatchName("TTSPOP1", PU_LEVEL);
-	ttspop2 = W_CachePatchName("TTSPOP2", PU_LEVEL);
-	ttspop3 = W_CachePatchName("TTSPOP3", PU_LEVEL);
-	ttspop4 = W_CachePatchName("TTSPOP4", PU_LEVEL);
-	ttspop5 = W_CachePatchName("TTSPOP5", PU_LEVEL);
-	ttspop6 = W_CachePatchName("TTSPOP6", PU_LEVEL);
-	ttspop7 = W_CachePatchName("TTSPOP7", PU_LEVEL);
+	F_CacheTitleScreen();
 }
 
 // (no longer) De-Demo'd Title Screen
@@ -1492,6 +1495,9 @@ void F_TitleScreenDrawer(void)
 	if (modeattacking)
 		return; // We likely came here from retrying. Don't do a damn thing.
 
+	if (needpatchrecache)
+		F_CacheTitleScreen();
+
 	// Draw that sky!
 	F_SkyScroll(titlescrollspeed);
 
diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c
index 78fc31afccceb1397b08bc40bc3a17e247ff0d89..3857ad5d7264f5dabe527f0e5f6b7c92b67c4add 100644
--- a/src/hardware/hw_cache.c
+++ b/src/hardware/hw_cache.c
@@ -549,16 +549,14 @@ void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipm
 //             CACHING HANDLING
 // =================================================
 
-static size_t gr_numtextures;
+static size_t gr_numtextures = 0;
 static GLTexture_t *gr_textures; // for ALL Doom textures
 
 void HWR_InitTextureCache(void)
 {
-	gr_numtextures = 0;
 	gr_textures = NULL;
 }
 
-
 // Callback function for HWR_FreeTextureCache.
 static void FreeMipmapColormap(INT32 patchnum, void *patch)
 {
@@ -587,15 +585,17 @@ void HWR_FreeTextureCache(void)
 	// Alam: free the Z_Blocks before freeing it's users
 
 	// free all skin after each level: must be done after pfnClearMipMapCache!
-	for (i = 0; i < numwadfiles; i++)
-		M_AATreeIterate(wadfiles[i]->hwrcache, FreeMipmapColormap);
+	// temp fix, idk why this crashes
+	// is it because the colormaps were already freed anyway?
+	if (!needpatchrecache)
+		for (i = 0; i < numwadfiles; i++)
+			M_AATreeIterate(wadfiles[i]->hwrcache, FreeMipmapColormap);
 
 	// now the heap don't have any 'user' pointing to our
 	// texturecache info, we can free it
 	if (gr_textures)
 		free(gr_textures);
 	gr_textures = NULL;
-	gr_numtextures = 0;
 }
 
 void HWR_PrepLevelCache(size_t pnumtextures)
@@ -642,8 +642,11 @@ GLTexture_t *HWR_GetTexture(INT32 tex)
 	GLTexture_t *grtex;
 #ifdef PARANOIA
 	if ((unsigned)tex >= gr_numtextures)
-		I_Error(" HWR_GetTexture: tex >= numtextures\n");
+		I_Error("HWR_GetTexture: tex >= numtextures\n");
 #endif
+	if (needpatchrecache && (!gr_textures))
+		HWR_PrepLevelCache(gr_numtextures);
+
 	grtex = &gr_textures[tex];
 
 	if (!grtex->mipmap.grInfo.data && !grtex->mipmap.downloaded)
@@ -709,6 +712,9 @@ void HWR_GetFlat(lumpnum_t flatlumpnum)
 {
 	GLMipmap_t *grmip;
 
+	if (needpatchflush)
+		W_FlushCachedPatches();
+
 	grmip = &HWR_GetCachedGLPatch(flatlumpnum)->mipmap;
 
 	if (!grmip->downloaded && !grmip->grInfo.data)
@@ -745,6 +751,9 @@ static void HWR_LoadMappedPatch(GLMipmap_t *grmip, GLPatch_t *gpatch)
 // -----------------+
 void HWR_GetPatch(GLPatch_t *gpatch)
 {
+	if (needpatchflush)
+		W_FlushCachedPatches();
+
 	// is it in hardware cache
 	if (!gpatch->mipmap.downloaded && !gpatch->mipmap.grInfo.data)
 	{
@@ -772,6 +781,9 @@ void HWR_GetMappedPatch(GLPatch_t *gpatch, const UINT8 *colormap)
 {
 	GLMipmap_t *grmip, *newmip;
 
+	if (needpatchflush)
+		W_FlushCachedPatches();
+
 	if (colormap == colormaps || colormap == NULL)
 	{
 		// Load the default (green) color in doom cache (temporary?) AND hardware cache
@@ -897,6 +909,9 @@ GLPatch_t *HWR_GetPic(lumpnum_t lumpnum)
 {
 	GLPatch_t *grpatch;
 
+	if (needpatchflush)
+		W_FlushCachedPatches();
+
 	grpatch = HWR_GetCachedGLPatch(lumpnum);
 
 	if (!grpatch->mipmap.downloaded && !grpatch->mipmap.grInfo.data)
@@ -1094,6 +1109,9 @@ void HWR_GetFadeMask(lumpnum_t fademasklumpnum)
 {
 	GLMipmap_t *grmip;
 
+	if (needpatchflush)
+		W_FlushCachedPatches();
+
 	grmip = &HWR_GetCachedGLPatch(fademasklumpnum)->mipmap;
 
 	if (!grmip->downloaded && !grmip->grInfo.data)
diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c
index cd2c95237aa17ef7f891643f6a4b8261d5a248f8..2dc18aee13b0436c09397e0cc286398d66588082 100644
--- a/src/hardware/hw_draw.c
+++ b/src/hardware/hw_draw.c
@@ -703,7 +703,7 @@ void HWR_DrawViewBorder(INT32 clearlines)
 	// top edge
 	if (clearlines > basewindowy - 8)
 	{
-		patch = W_CachePatchNum(viewborderlump[BRDR_T], PU_CACHE);
+		patch = W_CachePatchNum(viewborderlump[BRDR_T], PU_PATCH);
 		for (x = 0; x < baseviewwidth; x += 8)
 			HWR_DrawPatch(patch, basewindowx + x, basewindowy - 8,
 				0);
@@ -712,7 +712,7 @@ void HWR_DrawViewBorder(INT32 clearlines)
 	// bottom edge
 	if (clearlines > basewindowy + baseviewheight)
 	{
-		patch = W_CachePatchNum(viewborderlump[BRDR_B], PU_CACHE);
+		patch = W_CachePatchNum(viewborderlump[BRDR_B], PU_PATCH);
 		for (x = 0; x < baseviewwidth; x += 8)
 			HWR_DrawPatch(patch, basewindowx + x,
 				basewindowy + baseviewheight, 0);
@@ -721,7 +721,7 @@ void HWR_DrawViewBorder(INT32 clearlines)
 	// left edge
 	if (clearlines > basewindowy)
 	{
-		patch = W_CachePatchNum(viewborderlump[BRDR_L], PU_CACHE);
+		patch = W_CachePatchNum(viewborderlump[BRDR_L], PU_PATCH);
 		for (y = 0; y < baseviewheight && basewindowy + y < clearlines;
 			y += 8)
 		{
@@ -733,7 +733,7 @@ void HWR_DrawViewBorder(INT32 clearlines)
 	// right edge
 	if (clearlines > basewindowy)
 	{
-		patch = W_CachePatchNum(viewborderlump[BRDR_R], PU_CACHE);
+		patch = W_CachePatchNum(viewborderlump[BRDR_R], PU_PATCH);
 		for (y = 0; y < baseviewheight && basewindowy+y < clearlines;
 			y += 8)
 		{
@@ -745,22 +745,22 @@ void HWR_DrawViewBorder(INT32 clearlines)
 	// Draw beveled corners.
 	if (clearlines > basewindowy - 8)
 		HWR_DrawPatch(W_CachePatchNum(viewborderlump[BRDR_TL],
-				PU_CACHE),
+				PU_PATCH),
 			basewindowx - 8, basewindowy - 8, 0);
 
 	if (clearlines > basewindowy - 8)
 		HWR_DrawPatch(W_CachePatchNum(viewborderlump[BRDR_TR],
-				PU_CACHE),
+				PU_PATCH),
 			basewindowx + baseviewwidth, basewindowy - 8, 0);
 
 	if (clearlines > basewindowy+baseviewheight)
 		HWR_DrawPatch(W_CachePatchNum(viewborderlump[BRDR_BL],
-				PU_CACHE),
+				PU_PATCH),
 			basewindowx - 8, basewindowy + baseviewheight, 0);
 
 	if (clearlines > basewindowy + baseviewheight)
 		HWR_DrawPatch(W_CachePatchNum(viewborderlump[BRDR_BR],
-				PU_CACHE),
+				PU_PATCH),
 			basewindowx + baseviewwidth,
 			basewindowy + baseviewheight, 0);
 }
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 7e0b369eb8d55cdfc303388f7d393c8f2aa5ab79..399a7bfd2522f868ba89f1985cece44b3d627a43 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -872,7 +872,7 @@ static void HWR_DrawSegsSplats(FSurfaceInfo * pSurf)
 		if (!M_PointInBox(segbbox,splat->v1.x,splat->v1.y) && !M_PointInBox(segbbox,splat->v2.x,splat->v2.y))
 			continue;
 
-		gpatch = W_CachePatchNum(splat->patch, PU_CACHE);
+		gpatch = W_CachePatchNum(splat->patch, PU_PATCH);
 		HWR_GetPatch(gpatch);
 
 		wallVerts[0].x = wallVerts[3].x = FIXED_TO_FLOAT(splat->v1.x);
@@ -4311,7 +4311,7 @@ static void HWR_SplitSprite(gr_vissprite_t *spr)
 	if (hires)
 		this_scale = this_scale * FIXED_TO_FLOAT(((skin_t *)spr->mobj->skin)->highresscale);
 
-	gpatch = W_CachePatchNum(spr->patchlumpnum, PU_CACHE);
+	gpatch = W_CachePatchNum(spr->patchlumpnum, PU_PATCH);
 
 	// cache the patch in the graphics card memory
 	//12/12/99: Hurdler: same comment as above (for md2)
@@ -4664,7 +4664,7 @@ static void HWR_DrawSprite(gr_vissprite_t *spr)
 	//          sure to do it the right way. So actually, we keep normal sprite
 	//          in memory and we add the md2 model if it exists for that sprite
 
-	gpatch = W_CachePatchNum(spr->patchlumpnum, PU_CACHE);
+	gpatch = W_CachePatchNum(spr->patchlumpnum, PU_PATCH);
 
 #ifdef ALAM_LIGHTING
 	if (!(spr->mobj->flags2 & MF2_DEBRIS) && (spr->mobj->sprite != SPR_PLAY ||
@@ -4809,7 +4809,7 @@ static inline void HWR_DrawPrecipitationSprite(gr_vissprite_t *spr)
 		return;
 
 	// cache sprite graphics
-	gpatch = W_CachePatchNum(spr->patchlumpnum, PU_CACHE);
+	gpatch = W_CachePatchNum(spr->patchlumpnum, PU_PATCH);
 
 	// create the sprite billboard
 	//
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index cb33562d8b66260b9b3ee843333e25b27e93740c..353e49f8112c63c9b0fe937d3a33c8775f8668b3 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1350,7 +1350,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
 		else
 		{
 			// Sprite
-			gpatch = W_CachePatchNum(spr->patchlumpnum, PU_CACHE);
+			gpatch = W_CachePatchNum(spr->patchlumpnum, PU_PATCH);
 			HWR_GetMappedPatch(gpatch, spr->colormap);
 		}
 
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index a9a2b7504c07287ab40efae2c01d4e23ae88fc5f..2451f5c4d8bd2a8137d0f818d58d23701cca5239 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -1993,6 +1993,9 @@ static void HU_DrawDemoInfo(void)
 //
 void HU_Drawer(void)
 {
+	if (needpatchrecache)
+		R_ReloadHUDGraphics();
+
 #ifndef NONET
 	// draw chat string plus cursor
 	if (chat_on)
diff --git a/src/i_video.h b/src/i_video.h
index 4bb2c58293e3cc96309e6cf577540fcd6f63abd9..a62f3ff641b326f9d60dd589b3b226df1a43eac3 100644
--- a/src/i_video.h
+++ b/src/i_video.h
@@ -84,6 +84,7 @@ INT32 VID_GetModeForSize(INT32 w, INT32 h);
 	\return	currect video mode
 */
 INT32 VID_SetMode(INT32 modenum);
+void VID_CheckRenderer(void);
 
 /**	\brief	The VID_GetModeName function
 
diff --git a/src/m_menu.c b/src/m_menu.c
index a833ace21a56d2286af516fef7b427644a6ba9f2..f6c47077e3e87da09e80a8b7a28c4a22985aeb87 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -2872,19 +2872,19 @@ static void M_DrawThermo(INT32 x, INT32 y, consvar_t *cv)
 	centerlump[1] = W_GetNumForName("M_THERMM");
 	cursorlump = W_GetNumForName("M_THERMO");
 
-	V_DrawScaledPatch(xx, y, 0, p = W_CachePatchNum(leftlump,PU_CACHE));
+	V_DrawScaledPatch(xx, y, 0, p = W_CachePatchNum(leftlump,PU_PATCH));
 	xx += SHORT(p->width) - SHORT(p->leftoffset);
 	for (i = 0; i < 16; i++)
 	{
-		V_DrawScaledPatch(xx, y, V_WRAPX, W_CachePatchNum(centerlump[i & 1], PU_CACHE));
+		V_DrawScaledPatch(xx, y, V_WRAPX, W_CachePatchNum(centerlump[i & 1], PU_PATCH));
 		xx += 8;
 	}
-	V_DrawScaledPatch(xx, y, 0, W_CachePatchNum(rightlump, PU_CACHE));
+	V_DrawScaledPatch(xx, y, 0, W_CachePatchNum(rightlump, PU_PATCH));
 
 	xx = (cv->value - cv->PossibleValue[0].value) * (15*8) /
 		(cv->PossibleValue[1].value - cv->PossibleValue[0].value);
 
-	V_DrawScaledPatch((x + 8) + xx, y, 0, W_CachePatchNum(cursorlump, PU_CACHE));
+	V_DrawScaledPatch((x + 8) + xx, y, 0, W_CachePatchNum(cursorlump, PU_PATCH));
 }
 
 //  A smaller 'Thermo', with range given as percents (0-100)
@@ -2940,15 +2940,15 @@ void M_DrawTextBox(INT32 x, INT32 y, INT32 width, INT32 boxlines)
 	// draw left side
 	cx = x;
 	cy = y;
-	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_TL], PU_CACHE));
+	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_TL], PU_PATCH));
 	cy += boff;
-	p = W_CachePatchNum(viewborderlump[BRDR_L], PU_CACHE);
+	p = W_CachePatchNum(viewborderlump[BRDR_L], PU_PATCH);
 	for (n = 0; n < boxlines; n++)
 	{
 		V_DrawScaledPatch(cx, cy, V_WRAPY, p);
 		cy += step;
 	}
-	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_BL], PU_CACHE));
+	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_BL], PU_PATCH));
 
 	// draw middle
 	V_DrawFlatFill(x + boff, y + boff, width*step, boxlines*step, st_borderpatchnum);
@@ -2957,23 +2957,23 @@ void M_DrawTextBox(INT32 x, INT32 y, INT32 width, INT32 boxlines)
 	cy = y;
 	while (width > 0)
 	{
-		V_DrawScaledPatch(cx, cy, V_WRAPX, W_CachePatchNum(viewborderlump[BRDR_T], PU_CACHE));
-		V_DrawScaledPatch(cx, y + boff + boxlines*step, V_WRAPX, W_CachePatchNum(viewborderlump[BRDR_B], PU_CACHE));
+		V_DrawScaledPatch(cx, cy, V_WRAPX, W_CachePatchNum(viewborderlump[BRDR_T], PU_PATCH));
+		V_DrawScaledPatch(cx, y + boff + boxlines*step, V_WRAPX, W_CachePatchNum(viewborderlump[BRDR_B], PU_PATCH));
 		width--;
 		cx += step;
 	}
 
 	// draw right side
 	cy = y;
-	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_TR], PU_CACHE));
+	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_TR], PU_PATCH));
 	cy += boff;
-	p = W_CachePatchNum(viewborderlump[BRDR_R], PU_CACHE);
+	p = W_CachePatchNum(viewborderlump[BRDR_R], PU_PATCH);
 	for (n = 0; n < boxlines; n++)
 	{
 		V_DrawScaledPatch(cx, cy, V_WRAPY, p);
 		cy += step;
 	}
-	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_BR], PU_CACHE));
+	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_BR], PU_PATCH));
 */
 }
 
@@ -3914,6 +3914,27 @@ static void M_AddonsOptions(INT32 choice)
 #define LOCATIONSTRING1 "Visit \x83SRB2.ORG/MODS\x80 to get & make add-ons!"
 //#define LOCATIONSTRING2 "Visit \x88SRB2.ORG/MODS\x80 to get & make add-ons!"
 
+static void M_LoadAddonsPatches(void)
+{
+	addonsp[EXT_FOLDER] = W_CachePatchName("M_FFLDR", PU_PATCH);
+	addonsp[EXT_UP] = W_CachePatchName("M_FBACK", PU_PATCH);
+	addonsp[EXT_NORESULTS] = W_CachePatchName("M_FNOPE", PU_PATCH);
+	addonsp[EXT_TXT] = W_CachePatchName("M_FTXT", PU_PATCH);
+	addonsp[EXT_CFG] = W_CachePatchName("M_FCFG", PU_PATCH);
+	addonsp[EXT_WAD] = W_CachePatchName("M_FWAD", PU_PATCH);
+#ifdef USE_KART
+	addonsp[EXT_KART] = W_CachePatchName("M_FKART", PU_PATCH);
+#endif
+	addonsp[EXT_PK3] = W_CachePatchName("M_FPK3", PU_PATCH);
+	addonsp[EXT_SOC] = W_CachePatchName("M_FSOC", PU_PATCH);
+	addonsp[EXT_LUA] = W_CachePatchName("M_FLUA", PU_PATCH);
+	addonsp[NUM_EXT] = W_CachePatchName("M_FUNKN", PU_PATCH);
+	addonsp[NUM_EXT+1] = W_CachePatchName("M_FSEL", PU_PATCH);
+	addonsp[NUM_EXT+2] = W_CachePatchName("M_FLOAD", PU_PATCH);
+	addonsp[NUM_EXT+3] = W_CachePatchName("M_FSRCH", PU_PATCH);
+	addonsp[NUM_EXT+4] = W_CachePatchName("M_FSAVE", PU_PATCH);
+}
+
 static void M_Addons(INT32 choice)
 {
 	const char *pathname = ".";
@@ -3964,23 +3985,7 @@ static void M_Addons(INT32 choice)
 			W_UnlockCachedPatch(addonsp[i]);
 	}
 
-	addonsp[EXT_FOLDER] = W_CachePatchName("M_FFLDR", PU_STATIC);
-	addonsp[EXT_UP] = W_CachePatchName("M_FBACK", PU_STATIC);
-	addonsp[EXT_NORESULTS] = W_CachePatchName("M_FNOPE", PU_STATIC);
-	addonsp[EXT_TXT] = W_CachePatchName("M_FTXT", PU_STATIC);
-	addonsp[EXT_CFG] = W_CachePatchName("M_FCFG", PU_STATIC);
-	addonsp[EXT_WAD] = W_CachePatchName("M_FWAD", PU_STATIC);
-#ifdef USE_KART
-	addonsp[EXT_KART] = W_CachePatchName("M_FKART", PU_STATIC);
-#endif
-	addonsp[EXT_PK3] = W_CachePatchName("M_FPK3", PU_STATIC);
-	addonsp[EXT_SOC] = W_CachePatchName("M_FSOC", PU_STATIC);
-	addonsp[EXT_LUA] = W_CachePatchName("M_FLUA", PU_STATIC);
-	addonsp[NUM_EXT] = W_CachePatchName("M_FUNKN", PU_STATIC);
-	addonsp[NUM_EXT+1] = W_CachePatchName("M_FSEL", PU_STATIC);
-	addonsp[NUM_EXT+2] = W_CachePatchName("M_FLOAD", PU_STATIC);
-	addonsp[NUM_EXT+3] = W_CachePatchName("M_FSRCH", PU_STATIC);
-	addonsp[NUM_EXT+4] = W_CachePatchName("M_FSAVE", PU_STATIC);
+	M_LoadAddonsPatches();
 
 	MISC_AddonsDef.prevMenu = currentMenu;
 	M_SetupNextMenu(&MISC_AddonsDef);
@@ -4119,6 +4124,9 @@ static void M_DrawAddons(void)
 		return;
 	}
 
+	if (needpatchrecache)
+		M_LoadAddonsPatches();
+
 	if (Playing())
 		V_DrawCenteredString(BASEVIDWIDTH/2, 5, warningflags, "Adding files mid-game may cause problems.");
 	else
diff --git a/src/p_setup.c b/src/p_setup.c
index b2636c3505f0efb327c07ff92caf1b3ef8ee2086..0058f7defa587c6f94ca16c04b1bf13c2019766a 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -2967,16 +2967,8 @@ boolean P_SetupLevel(boolean skipprecip)
 	globalweather = mapheaderinfo[gamemap-1]->weather;
 
 #ifdef HWRENDER // not win32 only 19990829 by Kin
-	if (rendermode != render_soft && rendermode != render_none)
-	{
-#ifdef ALAM_LIGHTING
-		// BP: reset light between levels (we draw preview frame lights on current frame)
-		HWR_ResetLights();
-#endif
-		// Correct missing sidedefs & deep water trick
-		HWR_CorrectSWTricks();
-		HWR_CreatePlanePolygons((INT32)numnodes - 1);
-	}
+	if (rendermode == render_opengl)
+		HWR_SetupLevel();
 #endif
 
 	// oh god I hope this helps
@@ -3123,10 +3115,8 @@ boolean P_SetupLevel(boolean skipprecip)
 
 	// preload graphics
 #ifdef HWRENDER // not win32 only 19990829 by Kin
-	if (rendermode != render_soft && rendermode != render_none)
-	{
+	if (rendermode == render_opengl)
 		HWR_PrepLevelCache(numtextures);
-	}
 #endif
 
 	P_MapEnd();
@@ -3181,6 +3171,19 @@ boolean P_SetupLevel(boolean skipprecip)
 	return true;
 }
 
+#ifdef HWRENDER
+void HWR_SetupLevel(void)
+{
+#ifdef ALAM_LIGHTING
+	// BP: reset light between levels (we draw preview frame lights on current frame)
+	HWR_ResetLights();
+#endif
+	// Correct missing sidedefs & deep water trick
+	HWR_CorrectSWTricks();
+	HWR_CreatePlanePolygons((INT32)numnodes - 1);
+}
+#endif
+
 //
 // P_RunSOC
 //
diff --git a/src/p_setup.h b/src/p_setup.h
index 41c2bf1335fc40f4db383d19fc543f1dc2fbb283..622e1edbcd6a8b667d3c9f0f8dd1b686e8cfeae6 100644
--- a/src/p_setup.h
+++ b/src/p_setup.h
@@ -59,6 +59,9 @@ void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum);
 #endif
 void P_LoadThingsOnly(void);
 boolean P_SetupLevel(boolean skipprecip);
+#ifdef HWRENDER
+void HWR_SetupLevel(void);
+#endif
 boolean P_AddWadFile(const char *wadfilename);
 #ifdef DELFILE
 boolean P_DelWadFile(void);
diff --git a/src/r_data.c b/src/r_data.c
index bd12eb248012b2bbee3649a783b4799cf3a54a61..cd50272b03b92894d197520fdb7731caa49dcbee 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -95,6 +95,8 @@ size_t numspritelumps, max_spritelumps;
 
 // textures
 INT32 numtextures = 0; // total number of textures found,
+boolean needpatchflush = false;
+boolean needpatchrecache = false;
 // size of following tables
 
 texture_t **textures = NULL;
@@ -1266,7 +1268,6 @@ INT32 R_CreateColormap(char *p1, char *p2, char *p3)
 	extra_colormaps[mapnum].fog = fog;
 
 	// This code creates the colormap array used by software renderer
-	if (rendermode == render_soft)
 	{
 		double r, g, b, cbrightness;
 		int p;
@@ -1611,7 +1612,7 @@ void R_PrecacheLevel(void)
 				lump = sf->lumppat[k];
 				if (devparm)
 					spritememory += W_LumpLength(lump);
-				W_CachePatchNum(lump, PU_CACHE);
+				W_CachePatchNum(lump, PU_PATCH);
 			}
 		}
 	}
diff --git a/src/r_data.h b/src/r_data.h
index 5de51ccd4981708b77614c6af9cb699644dc10ec..e6229d6c8ccbe896c53c758fc014aa94fd503368 100644
--- a/src/r_data.h
+++ b/src/r_data.h
@@ -96,5 +96,7 @@ INT32 R_CreateColormap(char *p1, char *p2, char *p3);
 const char *R_ColormapNameForNum(INT32 num);
 
 extern INT32 numtextures;
+extern boolean needpatchflush;
+extern boolean needpatchrecache;
 
 #endif
diff --git a/src/r_main.c b/src/r_main.c
index 08b1ab2f0fb25f79dcdc297ab7e762d302fbb3e2..f12fe23fe30dc9cf5d21fa48ace36af192e4b85f 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -19,6 +19,7 @@
 #include "r_local.h"
 #include "r_splats.h" // faB(21jan): testing
 #include "r_sky.h"
+#include "hu_stuff.h"
 #include "st_stuff.h"
 #include "p_local.h"
 #include "keys.h"
@@ -28,6 +29,7 @@
 #include "d_main.h"
 #include "v_video.h"
 #include "p_spec.h" // skyboxmo
+#include "p_setup.h"
 #include "z_zone.h"
 #include "m_random.h" // quake camera shake
 
@@ -1337,6 +1339,25 @@ void R_RenderPlayerView(player_t *player)
 		skyVisible1 = skyVisible;
 }
 
+#ifdef HWRENDER
+void R_InitHardwareMode(void)
+{
+	if (gamestate == GS_LEVEL)
+	{
+		HWR_SetupLevel();
+		HWR_PrepLevelCache(numtextures);
+	}
+}
+#endif
+
+void R_ReloadHUDGraphics(void)
+{
+	W_FlushCachedPatches();
+	ST_LoadGraphics();
+	HU_LoadGraphics();
+	ST_ReloadSkinFaceGraphics();
+}
+
 // =========================================================================
 //                    ENGINE COMMANDS & VARS
 // =========================================================================
diff --git a/src/r_main.h b/src/r_main.h
index 6ae5aa221667569b4b59987b37c4002219958f0c..6b521e43a6cdff1af8eb0355c5e51c3a9d862eb7 100644
--- a/src/r_main.h
+++ b/src/r_main.h
@@ -84,6 +84,10 @@ extern consvar_t cv_tailspickup;
 
 // Called by startup code.
 void R_Init(void);
+#ifdef HWRENDER
+void R_InitHardwareMode(void);
+#endif
+void R_ReloadHUDGraphics(void);
 
 // just sets setsizeneeded true
 extern boolean setsizeneeded;
diff --git a/src/r_segs.c b/src/r_segs.c
index c82554ac865d0edac67b35a7cf212de82d48f53e..d5f651fc5e751f69ee92e8e3cc48d8a55a03f055 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -164,7 +164,7 @@ static void R_DrawWallSplats(void)
 		mfloorclip = floorclip;
 		mceilingclip = ceilingclip;
 
-		patch = W_CachePatchNum(splat->patch, PU_CACHE);
+		patch = W_CachePatchNum(splat->patch, PU_PATCH);
 
 		dc_texturemid = splat->top + (SHORT(patch->height)<<(FRACBITS-1)) - viewz;
 		if (splat->yoffset)
diff --git a/src/r_splats.c b/src/r_splats.c
index 9ab671274403f1f7dfea967271f286d8aafef1cd..e09e68aa1b69a5e6bc7914b630d7cc1134ae8559 100644
--- a/src/r_splats.c
+++ b/src/r_splats.c
@@ -147,7 +147,7 @@ void R_AddWallSplat(line_t *wallline, INT16 sectorside, const char *patchname, f
 	splat->flags = flags;
 
 	// bad.. but will be needed for drawing anyway..
-	patch = W_CachePatchNum(splat->patch, PU_CACHE);
+	patch = W_CachePatchNum(splat->patch, PU_PATCH);
 
 	// offset needed by draw code for texture mapping
 	linelength = P_SegLength((seg_t *)wallline);
diff --git a/src/screen.c b/src/screen.c
index af6aed03c0c78abef969d2a87e3cd11a27c42609..d485a6367c6c2997ec176e2ece86443f6a5bd5da 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -56,6 +56,7 @@ void (*twosmultipatchtransfunc)(void); // for cols with transparent pixels AND t
 // ------------------
 viddef_t vid;
 INT32 setmodeneeded; //video mode change needed if > 0 (the mode number to set + 1)
+INT32 setrenderneeded = 0;
 
 static CV_PossibleValue_t scr_depth_cons_t[] = {{8, "8 bits"}, {16, "16 bits"}, {24, "24 bits"}, {32, "32 bits"}, {0, NULL}};
 
@@ -71,6 +72,10 @@ consvar_t cv_scr_depth = {"scr_depth", "16 bits", CV_SAVE, scr_depth_cons_t, NUL
 #endif
 consvar_t cv_renderview = {"renderview", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 
+static void SCR_ChangeRenderer (void);
+static CV_PossibleValue_t cv_renderer_t[] = {{1, "Software"}, {2, "OpenGL"}, {0, NULL}};
+consvar_t cv_renderer = {"renderer", "Software", CV_CALL, cv_renderer_t, SCR_ChangeRenderer, 0, NULL, NULL, 0, 0, NULL};
+
 static void SCR_ChangeFullscreen (void);
 
 consvar_t cv_fullscreen = {"fullscreen", "Yes", CV_SAVE|CV_CALL, CV_YesNo, SCR_ChangeFullscreen, 0, NULL, NULL, 0, 0, NULL};
@@ -96,19 +101,8 @@ boolean R_3DNow = false;
 boolean R_MMXExt = false;
 boolean R_SSE2 = false;
 
-
-void SCR_SetMode(void)
+void SCR_SetDrawFuncs(void)
 {
-	if (dedicated)
-		return;
-
-	if (!setmodeneeded || WipeInAction)
-		return; // should never happen and don't change it during a wipe, BAD!
-
-	VID_SetMode(--setmodeneeded);
-
-	V_SetPalette(0);
-
 	//
 	//  setup the right draw routines for either 8bpp or 16bpp
 	//
@@ -166,8 +160,35 @@ void SCR_SetMode(void)
 	if (SCR_IsAspectCorrect(vid.width, vid.height))
 		CONS_Alert(CONS_WARNING, M_GetText("Resolution is not aspect-correct!\nUse a multiple of %dx%d\n"), BASEVIDWIDTH, BASEVIDHEIGHT);
 #endif*/
+
+	wallcolfunc = walldrawerfunc;
+}
+
+void SCR_SetMode(void)
+{
+	if (dedicated)
+		return;
+
+	if (!(setmodeneeded || setrenderneeded) || WipeInAction)
+		return; // should never happen and don't change it during a wipe, BAD!
+
+	if (setrenderneeded)
+	{
+		needpatchflush = true;
+		needpatchrecache = true;
+		VID_CheckRenderer();
+	}
+
+	if (setmodeneeded)
+		VID_SetMode(--setmodeneeded);
+
+	V_SetPalette(0);
+
+	SCR_SetDrawFuncs();
+
 	// set the apprpriate drawer for the sky (tall or INT16)
 	setmodeneeded = 0;
+	setrenderneeded = 0;
 }
 
 // do some initial settings for the game loading screen
@@ -384,6 +405,29 @@ void SCR_ChangeFullscreen(void)
 #endif
 }
 
+void SCR_ChangeRenderer(void)
+{
+	setrenderneeded = 0;
+
+	if (con_startup)
+	{
+		if (rendermode == render_soft)
+			CV_StealthSetValue(&cv_renderer, 1);
+		else if (rendermode == render_opengl)
+			CV_StealthSetValue(&cv_renderer, 2);
+		return;
+	}
+
+	if (cv_renderer.value == 1)
+		setrenderneeded = render_soft;
+	else if (cv_renderer.value == 2)
+		setrenderneeded = render_opengl;
+
+	// setting the same renderer twice WILL crash your game, so let's not, please
+	if (rendermode == setrenderneeded)
+		setrenderneeded = 0;
+}
+
 boolean SCR_IsAspectCorrect(INT32 width, INT32 height)
 {
 	return
diff --git a/src/screen.h b/src/screen.h
index 9ad254d3fcaca0b9dd3eded342ac4937db505e32..df05184210e1d3bfaf9deeada1b315032ca97d82 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -154,11 +154,12 @@ extern boolean R_SSE2;
 // ----------------
 extern viddef_t vid;
 extern INT32 setmodeneeded; // mode number to set if needed, or 0
+extern INT32 setrenderneeded;
 
 extern INT32 scr_bpp;
 extern UINT8 *scr_borderpatch; // patch used to fill the view borders
 
-extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_fullscreen;
+extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_fullscreen;
 // wait for page flipping to end or not
 extern consvar_t cv_vidwait;
 
@@ -167,6 +168,7 @@ extern void (*walldrawerfunc)(void);
 
 // Change video mode, only at the start of a refresh.
 void SCR_SetMode(void);
+void SCR_SetDrawFuncs(void);
 // Recalc screen size dependent stuff
 void SCR_Recalc(void);
 // Check parms once at startup
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index 77bf414ccc8265e4eeb602aee0ad8edb5ac65884..45d9e3ed95299d26b15362bdb7af08387b6feaba 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -71,6 +71,7 @@
 #include "../i_video.h"
 #include "../console.h"
 #include "../command.h"
+#include "../r_main.h"
 #include "sdlmain.h"
 #ifdef HWRENDER
 #include "../hardware/hw_main.h"
@@ -170,6 +171,7 @@ static void Impl_VideoSetupBuffer(void);
 static SDL_bool Impl_CreateWindow(SDL_bool fullscreen);
 //static void Impl_SetWindowName(const char *title);
 static void Impl_SetWindowIcon(void);
+static void I_StartupGraphicsGL(void);
 
 static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen)
 {
@@ -1059,7 +1061,6 @@ void I_FinishUpdate(void)
 		SDL_RenderCopy(renderer, texture, NULL, NULL);
 		SDL_RenderPresent(renderer);
 	}
-
 #ifdef HWRENDER
 	else if (rendermode == render_opengl)
 	{
@@ -1259,6 +1260,81 @@ void VID_PrepareModeList(void)
 #endif
 }
 
+//     SOMETIME IN
+//      THE FUTURE
+// WHEN I ACTUALLY RENDER
+//      THIS FRAME
+static int renderflags;
+static SDL_bool Impl_CreateContext(int flags)
+{
+	// Renderer-specific stuff
+#ifdef HWRENDER
+	if (rendermode == render_opengl)
+	{
+		if (!sdlglcontext)
+			sdlglcontext = SDL_GL_CreateContext(window);
+		if (sdlglcontext == NULL)
+		{
+			SDL_DestroyWindow(window);
+			I_Error("Failed to create a GL context: %s\n", SDL_GetError());
+		}
+		SDL_GL_MakeCurrent(window, sdlglcontext);
+	}
+	else
+#endif
+	if (rendermode == render_soft)
+	{
+		flags = 0; // Use this to set SDL_RENDERER_* flags now
+		if (usesdl2soft)
+			flags |= SDL_RENDERER_SOFTWARE;
+		else if (cv_vidwait.value)
+			flags |= SDL_RENDERER_PRESENTVSYNC;
+
+		if (!renderer)
+			renderer = SDL_CreateRenderer(window, -1, flags);
+		if (renderer == NULL)
+		{
+			CONS_Printf(M_GetText("Couldn't create rendering context: %s\n"), SDL_GetError());
+			return SDL_FALSE;
+		}
+		SDL_RenderSetLogicalSize(renderer, BASEVIDWIDTH, BASEVIDHEIGHT);
+	}
+	return SDL_TRUE;
+}
+
+void VID_CheckRenderer(void)
+{
+	if (setrenderneeded)
+	{
+		rendermode = setrenderneeded;
+		Impl_CreateContext(renderflags);
+		if (rendermode == render_soft)
+		{
+#ifdef HWRENDER
+			HWR_FreeTextureCache();
+#endif
+			SCR_SetDrawFuncs();
+		}
+	}
+
+	SDLSetMode(vid.width, vid.height, USE_FULLSCREEN);
+
+	if (rendermode == render_soft)
+	{
+		if (bufSurface)
+		{
+			SDL_FreeSurface(bufSurface);
+			bufSurface = NULL;
+		}
+		Impl_VideoSetupBuffer();
+	}
+	else if (rendermode == render_opengl)
+	{
+		I_StartupGraphicsGL();
+		R_InitHardwareMode();
+	}
+}
+
 INT32 VID_SetMode(INT32 modeNum)
 {
 	SDLdoUngrabMouse();
@@ -1290,20 +1366,7 @@ INT32 VID_SetMode(INT32 modeNum)
 		vid.modenum = -1;
 	}
 	//Impl_SetWindowName("SRB2 "VERSIONSTRING);
-
-	SDLSetMode(vid.width, vid.height, USE_FULLSCREEN);
-
-	if (rendermode == render_soft)
-	{
-		if (bufSurface)
-		{
-			SDL_FreeSurface(bufSurface);
-			bufSurface = NULL;
-		}
-
-		Impl_VideoSetupBuffer();
-	}
-
+	VID_CheckRenderer();
 	return SDL_TRUE;
 }
 
@@ -1338,38 +1401,8 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
 		return SDL_FALSE;
 	}
 
-	// Renderer-specific stuff
-#ifdef HWRENDER
-	if (rendermode == render_opengl)
-	{
-		sdlglcontext = SDL_GL_CreateContext(window);
-		if (sdlglcontext == NULL)
-		{
-			SDL_DestroyWindow(window);
-			I_Error("Failed to create a GL context: %s\n", SDL_GetError());
-		}
-		SDL_GL_MakeCurrent(window, sdlglcontext);
-	}
-	else
-#endif
-	if (rendermode == render_soft)
-	{
-		flags = 0; // Use this to set SDL_RENDERER_* flags now
-		if (usesdl2soft)
-			flags |= SDL_RENDERER_SOFTWARE;
-		else if (cv_vidwait.value)
-			flags |= SDL_RENDERER_PRESENTVSYNC;
-
-		renderer = SDL_CreateRenderer(window, -1, flags);
-		if (renderer == NULL)
-		{
-			CONS_Printf(M_GetText("Couldn't create rendering context: %s\n"), SDL_GetError());
-			return SDL_FALSE;
-		}
-		SDL_RenderSetLogicalSize(renderer, BASEVIDWIDTH, BASEVIDHEIGHT);
-	}
-
-	return SDL_TRUE;
+	renderflags = flags;
+	return Impl_CreateContext(flags);
 }
 
 /*
@@ -1438,6 +1471,51 @@ static void Impl_VideoSetupBuffer(void)
 	}
 }
 
+static void I_StartupGraphicsGL(void)
+{
+#ifdef HWRENDER
+	static boolean glstartup = false;
+	if (!glstartup)
+	{
+		HWD.pfnInit             = hwSym("Init",NULL);
+		HWD.pfnFinishUpdate     = NULL;
+		HWD.pfnDraw2DLine       = hwSym("Draw2DLine",NULL);
+		HWD.pfnDrawPolygon      = hwSym("DrawPolygon",NULL);
+		HWD.pfnSetBlend         = hwSym("SetBlend",NULL);
+		HWD.pfnClearBuffer      = hwSym("ClearBuffer",NULL);
+		HWD.pfnSetTexture       = hwSym("SetTexture",NULL);
+		HWD.pfnReadRect         = hwSym("ReadRect",NULL);
+		HWD.pfnGClipRect        = hwSym("GClipRect",NULL);
+		HWD.pfnClearMipMapCache = hwSym("ClearMipMapCache",NULL);
+		HWD.pfnSetSpecialState  = hwSym("SetSpecialState",NULL);
+		HWD.pfnSetPalette       = hwSym("SetPalette",NULL);
+		HWD.pfnGetTextureUsed   = hwSym("GetTextureUsed",NULL);
+		HWD.pfnDrawMD2          = hwSym("DrawMD2",NULL);
+		HWD.pfnDrawMD2i         = hwSym("DrawMD2i",NULL);
+		HWD.pfnSetTransform     = hwSym("SetTransform",NULL);
+		HWD.pfnGetRenderVersion = hwSym("GetRenderVersion",NULL);
+#ifdef SHUFFLE
+		HWD.pfnPostImgRedraw    = hwSym("PostImgRedraw",NULL);
+#endif
+		HWD.pfnFlushScreenTextures=hwSym("FlushScreenTextures",NULL);
+		HWD.pfnStartScreenWipe  = hwSym("StartScreenWipe",NULL);
+		HWD.pfnEndScreenWipe    = hwSym("EndScreenWipe",NULL);
+		HWD.pfnDoScreenWipe     = hwSym("DoScreenWipe",NULL);
+		HWD.pfnDrawIntermissionBG=hwSym("DrawIntermissionBG",NULL);
+		HWD.pfnMakeScreenTexture= hwSym("MakeScreenTexture",NULL);
+		HWD.pfnMakeScreenFinalTexture=hwSym("MakeScreenFinalTexture",NULL);
+		HWD.pfnDrawScreenFinalTexture=hwSym("DrawScreenFinalTexture",NULL);
+		// check gl renderer lib
+		if (HWD.pfnGetRenderVersion() != VERSION)
+			I_Error("%s", M_GetText("The version of the renderer doesn't match the version of the executable\nBe sure you have installed SRB2 properly.\n"));
+		if (!HWD.pfnInit(I_Error)) // let load the OpenGL library
+			rendermode = render_soft;
+		else
+			glstartup = true;
+	}
+#endif
+}
+
 void I_StartupGraphics(void)
 {
 	if (dedicated)
@@ -1478,10 +1556,11 @@ void I_StartupGraphics(void)
 		))
 			framebuffer = SDL_TRUE;
 	}
-	if (M_CheckParm("-software"))
-	{
+
+	if (M_CheckParm("-opengl"))
+		rendermode = render_opengl;
+	else if (M_CheckParm("software"))
 		rendermode = render_soft;
-	}
 
 	usesdl2soft = M_CheckParm("-softblit");
 	borderlesswindow = M_CheckParm("-borderless");
@@ -1489,45 +1568,7 @@ void I_StartupGraphics(void)
 	//SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY>>1,SDL_DEFAULT_REPEAT_INTERVAL<<2);
 	VID_Command_ModeList_f();
 #ifdef HWRENDER
-	if (M_CheckParm("-opengl") || rendermode == render_opengl)
-	{
-		rendermode = render_opengl;
-		HWD.pfnInit             = hwSym("Init",NULL);
-		HWD.pfnFinishUpdate     = NULL;
-		HWD.pfnDraw2DLine       = hwSym("Draw2DLine",NULL);
-		HWD.pfnDrawPolygon      = hwSym("DrawPolygon",NULL);
-		HWD.pfnSetBlend         = hwSym("SetBlend",NULL);
-		HWD.pfnClearBuffer      = hwSym("ClearBuffer",NULL);
-		HWD.pfnSetTexture       = hwSym("SetTexture",NULL);
-		HWD.pfnReadRect         = hwSym("ReadRect",NULL);
-		HWD.pfnGClipRect        = hwSym("GClipRect",NULL);
-		HWD.pfnClearMipMapCache = hwSym("ClearMipMapCache",NULL);
-		HWD.pfnSetSpecialState  = hwSym("SetSpecialState",NULL);
-		HWD.pfnSetPalette       = hwSym("SetPalette",NULL);
-		HWD.pfnGetTextureUsed   = hwSym("GetTextureUsed",NULL);
-		HWD.pfnDrawMD2          = hwSym("DrawMD2",NULL);
-		HWD.pfnDrawMD2i         = hwSym("DrawMD2i",NULL);
-		HWD.pfnSetTransform     = hwSym("SetTransform",NULL);
-		HWD.pfnGetRenderVersion = hwSym("GetRenderVersion",NULL);
-#ifdef SHUFFLE
-		HWD.pfnPostImgRedraw    = hwSym("PostImgRedraw",NULL);
-#endif
-		HWD.pfnFlushScreenTextures=hwSym("FlushScreenTextures",NULL);
-		HWD.pfnStartScreenWipe  = hwSym("StartScreenWipe",NULL);
-		HWD.pfnEndScreenWipe    = hwSym("EndScreenWipe",NULL);
-		HWD.pfnDoScreenWipe     = hwSym("DoScreenWipe",NULL);
-		HWD.pfnDrawIntermissionBG=hwSym("DrawIntermissionBG",NULL);
-		HWD.pfnMakeScreenTexture= hwSym("MakeScreenTexture",NULL);
-		HWD.pfnMakeScreenFinalTexture=hwSym("MakeScreenFinalTexture",NULL);
-		HWD.pfnDrawScreenFinalTexture=hwSym("DrawScreenFinalTexture",NULL);
-		// check gl renderer lib
-		if (HWD.pfnGetRenderVersion() != VERSION)
-			I_Error("%s", M_GetText("The version of the renderer doesn't match the version of the executable\nBe sure you have installed SRB2 properly.\n"));
-		if (!HWD.pfnInit(I_Error)) // let load the OpenGL library
-		{
-			rendermode = render_soft;
-		}
-	}
+	I_StartupGraphicsGL();
 #endif
 
 	// Fury: we do window initialization after GL setup to allow
diff --git a/src/st_stuff.c b/src/st_stuff.c
index 287aea134ba4e81bb5f50848696516d11990a9bb..11bb9875d2cba8f86ef2a01f951514843dbe0338 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -1909,6 +1909,9 @@ static void ST_overlayDrawer(void)
 
 void ST_Drawer(void)
 {
+	if (needpatchrecache)
+		R_ReloadHUDGraphics();
+
 #ifdef SEENAMES
 	if (cv_seenames.value && cv_allowseenames.value && displayplayer == consoleplayer && seenplayer && seenplayer->mo)
 	{
diff --git a/src/w_wad.c b/src/w_wad.c
index 7de64f9adbea7024e03500c5ca37190770de9e6d..00797b87d66b36386676a1569e388a9e08d4312c 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -41,6 +41,7 @@
 #include "dehacked.h"
 #include "d_clisrv.h"
 #include "r_defs.h"
+#include "r_data.h"
 #include "i_system.h"
 #include "md5.h"
 #include "lua_script.h"
@@ -1422,7 +1423,6 @@ void *W_CacheLumpNumPwad(UINT16 wad, UINT16 lump, INT32 tag)
 
 void *W_CacheLumpNum(lumpnum_t lumpnum, INT32 tag)
 {
-
 	return W_CacheLumpNumPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum),tag);
 }
 
@@ -1503,12 +1503,30 @@ void *W_CacheLumpName(const char *name, INT32 tag)
 // Cache a patch into heap memory, convert the patch format as necessary
 //
 
+void W_FlushCachedPatches(void)
+{
+	if (needpatchflush)
+	{
+		Z_FreeTag(PU_CACHE);
+		Z_FreeTag(PU_PATCH);
+		Z_FreeTag(PU_HUDGFX);
+		Z_FreeTag(PU_HWRPATCHINFO);
+		Z_FreeTag(PU_HWRPATCHCOLMIPMAP);
+		Z_FreeTag(PU_HWRCACHE);
+		Z_FreeTags(PU_HWRCACHE_UNLOCKED, PU_HWRPATCHINFO_UNLOCKED);
+	}
+	needpatchflush = false;
+}
+
 // Software-only compile cache the data without conversion
 #ifdef HWRENDER
 static inline void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag)
 {
 	GLPatch_t *grPatch;
 
+	if (needpatchflush)
+		W_FlushCachedPatches();
+
 	if (rendermode == render_soft || rendermode == render_none)
 		return W_CacheLumpNumPwad(wad, lump, tag);
 
diff --git a/src/w_wad.h b/src/w_wad.h
index 87566c3ee8c103d33d554199d520cee4a501c91b..84e921e755c5b1f3d6943cd683141d7764acd8ee 100644
--- a/src/w_wad.h
+++ b/src/w_wad.h
@@ -183,6 +183,7 @@ void *W_CachePatchNum(lumpnum_t lumpnum, INT32 tag); // return a patch_t
 #endif
 
 void W_UnlockCachedPatch(void *patch);
+void W_FlushCachedPatches(void);
 
 void W_VerifyFileMD5(UINT16 wadfilenum, const char *matchmd5);
 
diff --git a/src/win32/win_vid.c b/src/win32/win_vid.c
index cca7810b3b76209f117b35798fa08bceebac758b..a6d9cdfc1d506ec34ae41cadba0ce8a01c538e64 100644
--- a/src/win32/win_vid.c
+++ b/src/win32/win_vid.c
@@ -939,6 +939,11 @@ INT32 VID_SetMode(INT32 modenum)
 	return 1;
 }
 
+void VID_CheckRenderer(void)
+{
+	// ..............
+}
+
 // ========================================================================
 // Free the video buffer of the last video mode,
 // allocate a new buffer for the video mode to set.
diff --git a/src/y_inter.c b/src/y_inter.c
index ed4972d2e6ae9fd21d7627f9db4650be58ced0f7..ab0af490d3242b6328d66a774b9b020533522470 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -1051,8 +1051,8 @@ void Y_StartIntermission(void)
 			}
 
 			for (i = 0; i < 4; ++i)
-				data.coop.bonuspatches[i] = W_CachePatchName(data.coop.bonuses[i].patch, PU_STATIC);
-			data.coop.ptotal = W_CachePatchName("YB_TOTAL", PU_STATIC);
+				data.coop.bonuspatches[i] = W_CachePatchName(data.coop.bonuses[i].patch, PU_PATCH);
+			data.coop.ptotal = W_CachePatchName("YB_TOTAL", PU_PATCH);
 
 			// get act number
 			if (mapheaderinfo[prevmap]->actnum)
@@ -1062,13 +1062,13 @@ void Y_StartIntermission(void)
 				data.coop.ttlnum = W_CachePatchName("TTL01", PU_STATIC);
 
 			// get background patches
-			widebgpatch = W_CachePatchName("INTERSCW", PU_STATIC);
-			bgpatch = W_CachePatchName("INTERSCR", PU_STATIC);
+			widebgpatch = W_CachePatchName("INTERSCW", PU_PATCH);
+			bgpatch = W_CachePatchName("INTERSCR", PU_PATCH);
 
 			// grab an interscreen if appropriate
 			if (mapheaderinfo[gamemap-1]->interscreen[0] != '#')
 			{
-				interpic = W_CachePatchName(mapheaderinfo[gamemap-1]->interscreen, PU_STATIC);
+				interpic = W_CachePatchName(mapheaderinfo[gamemap-1]->interscreen, PU_PATCH);
 				useinterpic = true;
 				usebuffer = false;
 			}
@@ -1153,17 +1153,17 @@ void Y_StartIntermission(void)
 			// give out ring bonuses
 			Y_AwardSpecialStageBonus();
 
-			data.spec.bonuspatch = W_CachePatchName(data.spec.bonus.patch, PU_STATIC);
-			data.spec.pscore = W_CachePatchName("YB_SCORE", PU_STATIC);
-			data.spec.pcontinues = W_CachePatchName("YB_CONTI", PU_STATIC);
+			data.spec.bonuspatch = W_CachePatchName(data.spec.bonus.patch, PU_PATCH);
+			data.spec.pscore = W_CachePatchName("YB_SCORE", PU_PATCH);
+			data.spec.pcontinues = W_CachePatchName("YB_CONTI", PU_PATCH);
 
 			// get background tile
-			bgtile = W_CachePatchName("SPECTILE", PU_STATIC);
+			bgtile = W_CachePatchName("SPECTILE", PU_PATCH);
 
 			// grab an interscreen if appropriate
 			if (mapheaderinfo[gamemap-1]->interscreen[0] != '#')
 			{
-				interpic = W_CachePatchName(mapheaderinfo[gamemap-1]->interscreen, PU_STATIC);
+				interpic = W_CachePatchName(mapheaderinfo[gamemap-1]->interscreen, PU_PATCH);
 				useinterpic = true;
 			}
 			else
@@ -1175,14 +1175,14 @@ void Y_StartIntermission(void)
 			// get special stage specific patches
 /*			if (!stagefailed && ALL7EMERALDS(emeralds))
 			{
-				data.spec.cemerald = W_CachePatchName("GOTEMALL", PU_STATIC);
+				data.spec.cemerald = W_CachePatchName("GOTEMALL", PU_PATCH);
 				data.spec.headx = 70;
 				data.spec.nowsuper = players[consoleplayer].skin
 					? NULL : W_CachePatchName("NOWSUPER", PU_STATIC);
 			}
 			else
 			{
-				data.spec.cemerald = W_CachePatchName("CEMERALD", PU_STATIC);
+				data.spec.cemerald = W_CachePatchName("CEMERALD", PU_PATCH);
 				data.spec.headx = 48;
 				data.spec.nowsuper = NULL;
 			} */
@@ -1255,9 +1255,9 @@ void Y_StartIntermission(void)
 
 			// get RESULT header
 			data.match.result =
-				W_CachePatchName("RESULT", PU_STATIC);
+				W_CachePatchName("RESULT", PU_PATCH);
 
-			bgtile = W_CachePatchName("SRB2BACK", PU_STATIC);
+			bgtile = W_CachePatchName("SRB2BACK", PU_PATCH);
 			usetile = true;
 			useinterpic = false;
 			break;
@@ -1283,9 +1283,9 @@ void Y_StartIntermission(void)
 			data.match.levelstring[sizeof data.match.levelstring - 1] = '\0';
 
 			// get RESULT header
-			data.match.result = W_CachePatchName("RESULT", PU_STATIC);
+			data.match.result = W_CachePatchName("RESULT", PU_PATCH);
 
-			bgtile = W_CachePatchName("SRB2BACK", PU_STATIC);
+			bgtile = W_CachePatchName("SRB2BACK", PU_PATCH);
 			usetile = true;
 			useinterpic = false;
 			break;
@@ -1322,7 +1322,7 @@ void Y_StartIntermission(void)
 				data.match.blueflag = bmatcico;
 			}
 
-			bgtile = W_CachePatchName("SRB2BACK", PU_STATIC);
+			bgtile = W_CachePatchName("SRB2BACK", PU_PATCH);
 			usetile = true;
 			useinterpic = false;
 			break;
@@ -1348,7 +1348,7 @@ void Y_StartIntermission(void)
 			data.competition.levelstring[sizeof data.competition.levelstring - 1] = '\0';
 
 			// get background tile
-			bgtile = W_CachePatchName("SRB2BACK", PU_STATIC);
+			bgtile = W_CachePatchName("SRB2BACK", PU_PATCH);
 			usetile = true;
 			useinterpic = false;
 			break;
diff --git a/src/z_zone.h b/src/z_zone.h
index 1424a3782b9263cb40891589c8495bcd34e4dcfb..a6255524380ea3b3eecd9dfd127ea562c5fc01dd 100644
--- a/src/z_zone.h
+++ b/src/z_zone.h
@@ -39,6 +39,7 @@
 #define PU_SOUND               11 // static while playing
 #define PU_MUSIC               12 // static while playing
 #define PU_HUDGFX              13 // static until WAD added
+#define PU_PATCH               14 // static until renderer change
 
 #define PU_HWRPATCHINFO        21 // Hardware GLPatch_t struct for OpenGL texture cache
 #define PU_HWRPATCHCOLMIPMAP   22 // Hardware GLMipmap_t struct colromap variation of patch