From 91ed56ef40e96629d65a28201ff9b6589e641f2a Mon Sep 17 00:00:00 2001
From: Jaime Passos <lazymyuutsu@gmail.com>
Date: Sat, 8 Aug 2020 05:16:47 -0300
Subject: [PATCH] Refactor patch loading

---
 src/console.c                    |   5 +-
 src/d_main.c                     |  37 +----
 src/dehacked.c                   |   2 +-
 src/f_finale.c                   |  25 +---
 src/hardware/hw_cache.c          | 244 ++++++++++++++-----------------
 src/hardware/hw_data.h           |  15 +-
 src/hardware/hw_draw.c           |  93 ++++++------
 src/hardware/hw_drv.h            |   2 +
 src/hardware/hw_glob.h           |  27 ++--
 src/hardware/hw_main.c           |  58 ++++----
 src/hardware/hw_main.h           |   6 +-
 src/hardware/hw_md2.c            | 203 ++++++++++++++-----------
 src/hardware/r_opengl/r_opengl.c |  15 +-
 src/hu_stuff.c                   |   3 -
 src/lua_infolib.c                |   4 +-
 src/m_menu.c                     |  43 +-----
 src/r_data.c                     |   6 +-
 src/r_defs.h                     |  22 ++-
 src/r_main.c                     |   8 -
 src/r_main.h                     |   1 -
 src/r_patch.c                    | 220 ++++++++++++++++------------
 src/r_patch.h                    |  20 ++-
 src/r_segs.c                     |   2 +-
 src/r_things.c                   |  18 +--
 src/screen.c                     |   3 -
 src/sdl/hwsym_sdl.c              |   1 +
 src/sdl/i_video.c                |   5 +-
 src/st_stuff.c                   |   3 -
 src/v_video.c                    |  10 +-
 src/w_wad.c                      |  71 +++------
 src/w_wad.h                      |   7 -
 src/win32/win_dll.c              |   2 +
 src/y_inter.c                    |  73 +--------
 src/z_zone.c                     |  27 ----
 src/z_zone.h                     |   6 -
 35 files changed, 556 insertions(+), 731 deletions(-)

diff --git a/src/console.c b/src/console.c
index aac94d473c..280dc4f223 100644
--- a/src/console.c
+++ b/src/console.c
@@ -1555,7 +1555,7 @@ static void CON_DrawBackpic(void)
 	// then fill the sides with a solid color.
 	if (x > 0)
 	{
-		column_t *column = (column_t *)((UINT8 *)(con_backpic) + LONG(con_backpic->columnofs[0]));
+		column_t *column = (column_t *)((UINT8 *)(con_backpic->columns) + (con_backpic->columnofs[0]));
 		if (!column->topdelta)
 		{
 			UINT8 *source = (UINT8 *)(column) + 3;
@@ -1648,9 +1648,6 @@ void CON_Drawer(void)
 	if (!con_started || !graphics_started)
 		return;
 
-	if (needpatchrecache)
-		HU_LoadGraphics();
-
 	if (con_recalc)
 	{
 		CON_RecalcSize();
diff --git a/src/d_main.c b/src/d_main.c
index 6bc42da14e..ee3ce699c3 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -110,8 +110,6 @@ boolean devparm = false; // started game with -devparm
 boolean singletics = false; // timedemo
 boolean lastdraw = false;
 
-static void D_CheckRendererState(void);
-
 postimg_t postimgtype = postimg_none;
 INT32 postimgparam;
 postimg_t postimgtype2 = postimg_none;
@@ -245,9 +243,7 @@ static void D_Display(void)
 	//    create plane polygons, if necessary.
 	// 3. Functions related to switching video
 	//    modes (resolution) are called.
-	// 4. Patch data is freed from memory,
-	//    and recached if necessary.
-	// 5. The frame is ready to be drawn!
+	// 4. The frame is ready to be drawn!
 
 	// stop movie if needs to change renderer
 	if (setrenderneeded && (moviemode == MM_APNG))
@@ -284,9 +280,6 @@ static void D_Display(void)
 		forcerefresh = true; // force background redraw
 	}
 
-	// Lactozilla: Renderer switching
-	D_CheckRendererState();
-
 	// draw buffered stuff to screen
 	// Used only by linux GGI version
 	I_UpdateNoBlit();
@@ -679,26 +672,6 @@ static void D_Display(void)
 		I_FinishUpdate(); // page flip or blit buffer
 		rs_swaptime = I_GetTimeMicros() - rs_swaptime;
 	}
-
-	needpatchflush = false;
-	needpatchrecache = false;
-}
-
-// Check the renderer's state
-// after a possible renderer switch.
-void D_CheckRendererState(void)
-{
-	// flush all patches from memory
-	if (needpatchflush)
-	{
-		Z_FlushCachedPatches();
-		needpatchflush = false;
-	}
-
-	// some patches have been freed,
-	// so cache them again
-	if (needpatchrecache)
-		R_ReloadHUDGraphics();
 }
 
 // =========================================================================
@@ -1433,18 +1406,10 @@ void D_SRB2Main(void)
 	if ((setrenderneeded != 0) && (setrenderneeded != rendermode))
 	{
 		CONS_Printf(M_GetText("Switching the renderer...\n"));
-		Z_PreparePatchFlush();
-
-		// set needpatchflush / needpatchrecache true for D_CheckRendererState
-		needpatchflush = true;
-		needpatchrecache = true;
 
 		// Set cv_renderer to the new render mode
 		VID_CheckRenderer();
 		SCR_ChangeRendererCVars(rendermode);
-
-		// check the renderer's state
-		D_CheckRendererState();
 	}
 
 	wipegamestate = gamestate;
diff --git a/src/dehacked.c b/src/dehacked.c
index 9d6729dc22..c801dcde95 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -1046,7 +1046,7 @@ static void readspriteinfo(MYFILE *f, INT32 num, boolean sprite2)
 
 #ifdef ROTSPRITE
 	if ((sprites != NULL) && (!sprite2))
-		R_FreeSingleRotSprite(&sprites[num]);
+		R_FreeRotSprite(&sprites[num]);
 #endif
 
 	do
diff --git a/src/f_finale.c b/src/f_finale.c
index 8d39a75337..7cc5467c29 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -1901,13 +1901,6 @@ void F_EndingDrawer(void)
 	INT32 x, y, i, j, parallaxticker;
 	patch_t *rockpat;
 
-	if (needpatchrecache)
-	{
-		F_CacheEnding();
-		if (goodending && finalecount >= INFLECTIONPOINT) // time to swap some assets
-			F_CacheGoodEnding();
-	}
-
 	if (!goodending || finalecount < INFLECTIONPOINT)
 		rockpat = W_CachePatchName("ROID0000", PU_PATCH);
 	else
@@ -2706,17 +2699,12 @@ static void F_FigureActiveTtScale(void)
 	SINT8 newttscale = max(1, min(6, vid.dupx));
 	SINT8 oldttscale = activettscale;
 
-	if (needpatchrecache)
-		ttloaded[0] = ttloaded[1] = ttloaded[2] = ttloaded[3] = ttloaded[4] = ttloaded[5] = 0;
-	else
-	{
-		if (newttscale == testttscale)
-			return;
+	if (newttscale == testttscale)
+		return;
 
-		// We have a new ttscale, so load gfx
-		if(oldttscale > 0)
-			F_UnloadAlacroixGraphics(oldttscale);
-	}
+	// We have a new ttscale, so load gfx
+	if(oldttscale > 0)
+		F_UnloadAlacroixGraphics(oldttscale);
 
 	testttscale = newttscale;
 
@@ -2750,9 +2738,6 @@ void F_TitleScreenDrawer(void)
 	if (modeattacking)
 		return; // We likely came here from retrying. Don't do a damn thing.
 
-	if (needpatchrecache && (curttmode != TTMODE_ALACROIX))
-		F_CacheTitleScreen();
-
 	// Draw that sky!
 	if (curbgcolor >= 0)
 		V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, curbgcolor);
diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c
index ed3b6afee8..a267211696 100644
--- a/src/hardware/hw_cache.c
+++ b/src/hardware/hw_cache.c
@@ -309,7 +309,7 @@ static void HWR_DrawPatchInCache(GLMipmap_t *mipmap,
 	// Draw each column to the block cache
 	for (; ncols--; block += bpp, xfrac += xfracstep)
 	{
-		patchcol = (const column_t *)((const UINT8 *)realpatch + LONG(realpatch->columnofs[xfrac>>FRACBITS]));
+		patchcol = (const column_t *)((const UINT8 *)realpatch->columns + (realpatch->columnofs[xfrac>>FRACBITS]));
 
 		HWR_DrawColumnInCache(patchcol, block, mipmap,
 								pblockheight, blockmodulo,
@@ -323,7 +323,7 @@ static void HWR_DrawPatchInCache(GLMipmap_t *mipmap,
 static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap,
 	INT32 pblockwidth, INT32 pblockheight,
 	texture_t *texture, texpatch_t *patch,
-	const patch_t *realpatch)
+	const softwarepatch_t *realpatch)
 {
 	INT32 x, x1, x2;
 	INT32 col, ncols;
@@ -394,7 +394,7 @@ static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap,
 	bpp = format2bpp(mipmap->format);
 
 	if (bpp < 1 || bpp > 4)
-		I_Error("HWR_DrawPatchInCache: no drawer defined for this bpp (%d)\n",bpp);
+		I_Error("HWR_DrawTexturePatchInCache: no drawer defined for this bpp (%d)\n",bpp);
 
 	// NOTE: should this actually be pblockwidth*bpp?
 	blockmodulo = pblockwidth*bpp;
@@ -449,7 +449,7 @@ static void HWR_GenerateTexture(INT32 texnum, GLMapTexture_t *grtex)
 	UINT8 *block;
 	texture_t *texture;
 	texpatch_t *patch;
-	patch_t *realpatch;
+	softwarepatch_t *realpatch;
 	UINT8 *pdata;
 	INT32 blockwidth, blockheight, blocksize;
 
@@ -505,7 +505,7 @@ static void HWR_GenerateTexture(INT32 texnum, GLMapTexture_t *grtex)
 		boolean dealloc = true;
 		size_t lumplength = W_LumpLengthPwad(patch->wad, patch->lump);
 		pdata = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE);
-		realpatch = (patch_t *)pdata;
+		realpatch = (softwarepatch_t *)pdata;
 
 #ifndef NO_PNG_LUMPS
 		if (R_IsLumpPNG((UINT8 *)realpatch, lumplength))
@@ -547,36 +547,20 @@ static void HWR_GenerateTexture(INT32 texnum, GLMapTexture_t *grtex)
 // patch may be NULL if grMipmap has been initialised already and makebitmap is false
 void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipmap, boolean makebitmap)
 {
-#ifndef NO_PNG_LUMPS
-	// lump is a png so convert it
-	size_t len = W_LumpLengthPwad(grPatch->wadnum, grPatch->lumpnum);
-	if ((patch != NULL) && R_IsLumpPNG((const UINT8 *)patch, len))
-		patch = R_PNGToPatch((const UINT8 *)patch, len, NULL);
-#endif
-
-	// don't do it twice (like a cache)
 	if (grMipmap->width == 0)
 	{
-		// save the original patch header so that the GLPatch can be casted
-		// into a standard patch_t struct and the existing code can get the
-		// orginal patch dimensions and offsets.
-		grPatch->width = SHORT(patch->width);
-		grPatch->height = SHORT(patch->height);
-		grPatch->leftoffset = SHORT(patch->leftoffset);
-		grPatch->topoffset = SHORT(patch->topoffset);
-
 		grMipmap->width = grMipmap->height = 1;
-		while (grMipmap->width < grPatch->width) grMipmap->width <<= 1;
-		while (grMipmap->height < grPatch->height) grMipmap->height <<= 1;
+		while (grMipmap->width < patch->width) grMipmap->width <<= 1;
+		while (grMipmap->height < patch->height) grMipmap->height <<= 1;
 
 		// no wrap around, no chroma key
 		grMipmap->flags = 0;
+
 		// setup the texture info
 		grMipmap->format = patchformat;
 
-		//grPatch->max_s = grPatch->max_t = 1.0f;
-		grPatch->max_s = (float)grPatch->width / (float)grMipmap->width;
-		grPatch->max_t = (float)grPatch->height / (float)grMipmap->height;
+		grPatch->max_s = (float)patch->width / (float)grMipmap->width;
+		grPatch->max_t = (float)patch->height / (float)grMipmap->height;
 	}
 
 	Z_Free(grMipmap->data);
@@ -588,7 +572,7 @@ void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipm
 
 		HWR_DrawPatchInCache(grMipmap,
 			grMipmap->width, grMipmap->height,
-			grPatch->width, grPatch->height,
+			patch->width, patch->height,
 			patch);
 	}
 }
@@ -608,13 +592,33 @@ void HWR_InitTextureCache(void)
 	gl_flats = NULL;
 }
 
-// Callback function for HWR_FreeTextureCache.
-static void FreeMipmapColormap(INT32 patchnum, void *patch)
+void HWR_FreeTexture(patch_t *patch)
 {
-	GLPatch_t* const pat = patch;
-	(void)patchnum; //unused
+	if (patch->hardware)
+	{
+		GLPatch_t *grPatch = patch->hardware;
+
+		HWR_FreeTextureColormaps(patch);
+
+		if (grPatch->mipmap && (rendermode == render_opengl))
+			HWD.pfnDeleteTexture(grPatch->mipmap);
+
+		Z_Free(patch->hardware);
+	}
+
+	patch->hardware = NULL;
+}
+
+// Called by HWR_FreeTextureCache.
+void HWR_FreeTextureColormaps(patch_t *patch)
+{
+	GLPatch_t *pat;
 
 	// The patch must be valid, obviously
+	if (!patch)
+		return;
+
+	pat = patch->hardware;
 	if (!pat)
 		return;
 
@@ -642,6 +646,7 @@ static void FreeMipmapColormap(INT32 patchnum, void *patch)
 		if (next->data)
 			Z_Free(next->data);
 		next->data = NULL;
+		HWD.pfnDeleteTexture(next);
 
 		// Free the old colormap mipmap from memory.
 		free(next);
@@ -663,7 +668,11 @@ void HWR_FreeMipmapCache(void)
 	// Alam: free the Z_Blocks before freeing it's users
 	// free all patch colormaps after each level: must be done after ClearMipMapCache!
 	for (i = 0; i < numwadfiles; i++)
-		M_AATreeIterate(wadfiles[i]->hwrcache, FreeMipmapColormap);
+	{
+		INT32 j = 0;
+		for (; j < wadfiles[i]->numlumps; j++)
+			HWR_FreeTextureColormaps(wadfiles[i]->patchcache[j]);
+	}
 }
 
 void HWR_FreeTextureCache(void)
@@ -733,7 +742,6 @@ GLMapTexture_t *HWR_GetTexture(INT32 tex)
 	// If hardware does not have the texture, then call pfnSetTexture to upload it
 	if (!grtex->mipmap.downloaded)
 		HWD.pfnSetTexture(&grtex->mipmap);
-
 	HWR_SetCurrentTexture(&grtex->mipmap);
 
 	// The system-memory data can be purged now.
@@ -809,14 +817,13 @@ void HWR_LiterallyGetFlat(lumpnum_t flatlumpnum)
 	if (flatlumpnum == LUMPERROR)
 		return;
 
-	grmip = HWR_GetCachedGLPatch(flatlumpnum)->mipmap;
+	grmip = ((GLPatch_t *)HWR_GetCachedGLPatch(flatlumpnum)->hardware)->mipmap;
 	if (!grmip->downloaded && !grmip->data)
 		HWR_CacheFlat(grmip, flatlumpnum);
 
 	// If hardware does not have the texture, then call pfnSetTexture to upload it
 	if (!grmip->downloaded)
 		HWD.pfnSetTexture(grmip);
-
 	HWR_SetCurrentTexture(grmip);
 
 	// The system-memory data can be purged now.
@@ -854,7 +861,6 @@ void HWR_GetLevelFlat(levelflat_t *levelflat)
 		// If hardware does not have the texture, then call pfnSetTexture to upload it
 		if (!grtex->mipmap.downloaded)
 			HWD.pfnSetTexture(&grtex->mipmap);
-
 		HWR_SetCurrentTexture(&grtex->mipmap);
 
 		// The system-memory data can be purged now.
@@ -864,89 +870,61 @@ void HWR_GetLevelFlat(levelflat_t *levelflat)
 		HWR_SetCurrentTexture(NULL);
 }
 
-//
-// HWR_LoadMappedPatch(): replace the skin color of the sprite in cache
-//                          : load it first in doom cache if not already
-//
-static void HWR_LoadMappedPatch(GLMipmap_t *grmip, GLPatch_t *gpatch)
+// --------------------+
+// HWR_LoadPatchMipmap : Generates a patch into a mipmap, usually the mipmap inside the patch itself
+// --------------------+
+static void HWR_LoadPatchMipmap(patch_t *patch, GLMipmap_t *grMipmap)
 {
-	if (!grmip->downloaded && !grmip->data)
-	{
-		patch_t *patch = gpatch->rawpatch;
-		if (!patch)
-			patch = W_CacheLumpNumPwad(gpatch->wadnum, gpatch->lumpnum, PU_STATIC);
-		HWR_MakePatch(patch, gpatch, grmip, true);
-
-		// You can't free rawpatch for some reason?
-		// (Obviously I can't, sprite rotation needs that...)
-		if (!gpatch->rawpatch)
-			Z_Free(patch);
-	}
+	GLPatch_t *grPatch = patch->hardware;
+	if (!grMipmap->downloaded && !grMipmap->data)
+		HWR_MakePatch(patch, grPatch, grMipmap, true);
 
 	// If hardware does not have the texture, then call pfnSetTexture to upload it
-	if (!grmip->downloaded)
-		HWD.pfnSetTexture(grmip);
-
-	HWR_SetCurrentTexture(grmip);
+	if (!grMipmap->downloaded)
+		HWD.pfnSetTexture(grMipmap);
+	HWR_SetCurrentTexture(grMipmap);
 
 	// The system-memory data can be purged now.
-	Z_ChangeTag(grmip->data, PU_HWRCACHE_UNLOCKED);
+	Z_ChangeTag(grMipmap->data, PU_HWRCACHE_UNLOCKED);
 }
 
 // -----------------+
 // HWR_GetPatch     : Download a patch to the hardware cache and make it ready for use
 // -----------------+
-void HWR_GetPatch(GLPatch_t *gpatch)
+void HWR_GetPatch(patch_t *patch)
 {
-	// is it in hardware cache
-	if (!gpatch->mipmap->downloaded && !gpatch->mipmap->data)
-	{
-		// load the software patch, PU_STATIC or the Z_Malloc for hardware patch will
-		// flush the software patch before the conversion! oh yeah I suffered
-		patch_t *ptr = gpatch->rawpatch;
-		if (!ptr)
-			ptr = W_CacheLumpNumPwad(gpatch->wadnum, gpatch->lumpnum, PU_STATIC);
-		HWR_MakePatch(ptr, gpatch, gpatch->mipmap, true);
-
-		// this is inefficient.. but the hardware patch in heap is purgeable so it should
-		// not fragment memory, and besides the REAL cache here is the hardware memory
-		if (!gpatch->rawpatch)
-			Z_Free(ptr);
-	}
-
-	// If hardware does not have the texture, then call pfnSetTexture to upload it
-	if (!gpatch->mipmap->downloaded)
-		HWD.pfnSetTexture(gpatch->mipmap);
-
-	HWR_SetCurrentTexture(gpatch->mipmap);
-
-	// The system-memory patch data can be purged now.
-	Z_ChangeTag(gpatch->mipmap->data, PU_HWRCACHE_UNLOCKED);
+	if (!patch->hardware)
+		Patch_CreateGL(patch);
+	HWR_LoadPatchMipmap(patch, ((GLPatch_t *)patch->hardware)->mipmap);
 }
 
-
 // -------------------+
 // HWR_GetMappedPatch : Same as HWR_GetPatch for sprite color
 // -------------------+
-void HWR_GetMappedPatch(GLPatch_t *gpatch, const UINT8 *colormap)
+void HWR_GetMappedPatch(patch_t *patch, const UINT8 *colormap)
 {
-	GLMipmap_t *grmip, *newmip;
+	GLPatch_t *grPatch;
+	GLMipmap_t *grMipmap, *newMipmap;
+
+	if (!patch->hardware)
+		Patch_CreateGL(patch);
+	grPatch = patch->hardware;
 
 	if (colormap == colormaps || colormap == NULL)
 	{
-		// Load the default (green) color in doom cache (temporary?) AND hardware cache
-		HWR_GetPatch(gpatch);
+		// Load the default (green) color in hardware cache
+		HWR_GetPatch(patch);
 		return;
 	}
 
 	// search for the mimmap
 	// skip the first (no colormap translated)
-	for (grmip = gpatch->mipmap; grmip->nextcolormap; )
+	for (grMipmap = grPatch->mipmap; grMipmap->nextcolormap; )
 	{
-		grmip = grmip->nextcolormap;
-		if (grmip->colormap == colormap)
+		grMipmap = grMipmap->nextcolormap;
+		if (grMipmap->colormap == colormap)
 		{
-			HWR_LoadMappedPatch(grmip, gpatch);
+			HWR_LoadPatchMipmap(patch, grMipmap);
 			return;
 		}
 	}
@@ -957,13 +935,13 @@ void HWR_GetMappedPatch(GLPatch_t *gpatch, const UINT8 *colormap)
 	//              (it have a liste of mipmap)
 	//    this malloc is cleared in HWR_FreeTextureCache
 	//    (...) unfortunately z_malloc fragment alot the memory :(so malloc is better
-	newmip = calloc(1, sizeof (*newmip));
-	if (newmip == NULL)
+	newMipmap = calloc(1, sizeof (*newMipmap));
+	if (newMipmap == NULL)
 		I_Error("%s: Out of memory", "HWR_GetMappedPatch");
-	grmip->nextcolormap = newmip;
+	grMipmap->nextcolormap = newMipmap;
 
-	newmip->colormap = colormap;
-	HWR_LoadMappedPatch(newmip, gpatch);
+	newMipmap->colormap = colormap;
+	HWR_LoadPatchMipmap(patch, newMipmap);
 }
 
 void HWR_UnlockCachedPatch(GLPatch_t *gpatch)
@@ -1053,79 +1031,73 @@ static void HWR_DrawPicInCache(UINT8 *block, INT32 pblockwidth, INT32 pblockheig
 // HWR_GetPic       : Download a Doom pic (raw row encoded with no 'holes')
 // Returns          :
 // -----------------+
-GLPatch_t *HWR_GetPic(lumpnum_t lumpnum)
+patch_t *HWR_GetPic(lumpnum_t lumpnum)
 {
-	GLPatch_t *grpatch = HWR_GetCachedGLPatch(lumpnum);
-	if (!grpatch->mipmap->downloaded && !grpatch->mipmap->data)
+	patch_t *patch = HWR_GetCachedGLPatch(lumpnum);
+	GLPatch_t *grPatch = (GLPatch_t *)(patch->hardware);
+
+	if (!grPatch->mipmap->downloaded && !grPatch->mipmap->data)
 	{
 		pic_t *pic;
 		UINT8 *block;
 		size_t len;
 
 		pic = W_CacheLumpNum(lumpnum, PU_CACHE);
-		grpatch->width = SHORT(pic->width);
-		grpatch->height = SHORT(pic->height);
+		patch->width = SHORT(pic->width);
+		patch->height = SHORT(pic->height);
 		len = W_LumpLength(lumpnum) - sizeof (pic_t);
 
-		grpatch->leftoffset = 0;
-		grpatch->topoffset = 0;
-
-		grpatch->mipmap->width = (UINT16)grpatch->width;
-		grpatch->mipmap->height = (UINT16)grpatch->height;
+		grPatch->mipmap->width = (UINT16)patch->width;
+		grPatch->mipmap->height = (UINT16)patch->height;
 
 		if (pic->mode == PALETTE)
-			grpatch->mipmap->format = textureformat; // can be set by driver
+			grPatch->mipmap->format = textureformat; // can be set by driver
 		else
-			grpatch->mipmap->format = picmode2GR[pic->mode];
+			grPatch->mipmap->format = picmode2GR[pic->mode];
 
-		Z_Free(grpatch->mipmap->data);
+		Z_Free(grPatch->mipmap->data);
 
 		// allocate block
-		block = MakeBlock(grpatch->mipmap);
+		block = MakeBlock(grPatch->mipmap);
 
-		if (grpatch->width  == SHORT(pic->width) &&
-			grpatch->height == SHORT(pic->height) &&
-			format2bpp(grpatch->mipmap->format) == format2bpp(picmode2GR[pic->mode]))
+		if (patch->width  == SHORT(pic->width) &&
+			patch->height == SHORT(pic->height) &&
+			format2bpp(grPatch->mipmap->format) == format2bpp(picmode2GR[pic->mode]))
 		{
 			// no conversion needed
-			M_Memcpy(grpatch->mipmap->data, pic->data,len);
+			M_Memcpy(grPatch->mipmap->data, pic->data,len);
 		}
 		else
 			HWR_DrawPicInCache(block, SHORT(pic->width), SHORT(pic->height),
-			                   SHORT(pic->width)*format2bpp(grpatch->mipmap->format),
+			                   SHORT(pic->width)*format2bpp(grPatch->mipmap->format),
 			                   pic,
-			                   format2bpp(grpatch->mipmap->format));
+			                   format2bpp(grPatch->mipmap->format));
 
 		Z_Unlock(pic);
 		Z_ChangeTag(block, PU_HWRCACHE_UNLOCKED);
 
-		grpatch->mipmap->flags = 0;
-		grpatch->max_s = grpatch->max_t = 1.0f;
+		grPatch->mipmap->flags = 0;
+		grPatch->max_s = grPatch->max_t = 1.0f;
 	}
-	HWD.pfnSetTexture(grpatch->mipmap);
-	//CONS_Debug(DBG_RENDER, "picloaded at %x as texture %d\n",grpatch->mipmap.data, grpatch->mipmap.downloaded);
+	HWD.pfnSetTexture(grPatch->mipmap);
+	//CONS_Debug(DBG_RENDER, "picloaded at %x as texture %d\n",grPatch->mipmap->data, grPatch->mipmap->downloaded);
 
-	return grpatch;
+	return patch;
 }
 
-GLPatch_t *HWR_GetCachedGLPatchPwad(UINT16 wadnum, UINT16 lumpnum)
+patch_t *HWR_GetCachedGLPatchPwad(UINT16 wadnum, UINT16 lumpnum)
 {
-	aatree_t *hwrcache = wadfiles[wadnum]->hwrcache;
-	GLPatch_t *grpatch;
-
-	if (!(grpatch = M_AATreeGet(hwrcache, lumpnum)))
+	lumpcache_t *lumpcache = wadfiles[wadnum]->patchcache;
+	if (!lumpcache[lumpnum])
 	{
-		grpatch = Z_Calloc(sizeof(GLPatch_t), PU_HWRPATCHINFO, NULL);
-		grpatch->wadnum = wadnum;
-		grpatch->lumpnum = lumpnum;
-		grpatch->mipmap = Z_Calloc(sizeof(GLMipmap_t), PU_HWRPATCHINFO, NULL);
-		M_AATreeSet(hwrcache, lumpnum, grpatch);
+		void *ptr = Z_Calloc(sizeof(patch_t), PU_PATCH, &lumpcache[lumpnum]);
+		Patch_Create(NULL, 0, ptr);
+		Patch_AllocateHardwarePatch(ptr);
 	}
-
-	return grpatch;
+	return (patch_t *)(lumpcache[lumpnum]);
 }
 
-GLPatch_t *HWR_GetCachedGLPatch(lumpnum_t lumpnum)
+patch_t *HWR_GetCachedGLPatch(lumpnum_t lumpnum)
 {
 	return HWR_GetCachedGLPatchPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum));
 }
@@ -1218,7 +1190,7 @@ static void HWR_CacheFadeMask(GLMipmap_t *grMipmap, lumpnum_t fademasklumpnum)
 
 void HWR_GetFadeMask(lumpnum_t fademasklumpnum)
 {
-	GLMipmap_t *grmip = HWR_GetCachedGLPatch(fademasklumpnum)->mipmap;
+	GLMipmap_t *grmip = ((GLPatch_t *)HWR_GetCachedGLPatch(fademasklumpnum)->hardware)->mipmap;
 	if (!grmip->downloaded && !grmip->data)
 		HWR_CacheFadeMask(grmip, fademasklumpnum);
 
diff --git a/src/hardware/hw_data.h b/src/hardware/hw_data.h
index e5477d7292..6a872d2587 100644
--- a/src/hardware/hw_data.h
+++ b/src/hardware/hw_data.h
@@ -73,23 +73,10 @@ struct GLMapTexture_s
 typedef struct GLMapTexture_s GLMapTexture_t;
 
 
-// a cached patch as converted to hardware format, holding the original patch_t
-// header so that the existing code can retrieve ->width, ->height as usual
-// This is returned by W_CachePatchNum()/W_CachePatchName(), when rendermode
-// is 'render_opengl'. Else it returns the normal patch_t data.
-
+// a cached patch as converted to hardware format
 struct GLPatch_s
 {
-	// the 4 first fields come right away from the original patch_t
-	INT16               width;
-	INT16               height;
-	INT16               leftoffset;     // pixels to the left of origin
-	INT16               topoffset;      // pixels below the origin
-	//
 	float               max_s,max_t;
-	UINT16              wadnum;      // the software patch lump num for when the hardware patch
-	UINT16              lumpnum;     // was flushed, and we need to re-create it
-	void                *rawpatch;   // :^)
 	GLMipmap_t          *mipmap;
 } ATTRPACK;
 typedef struct GLPatch_s GLPatch_t;
diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c
index f5a984d5d3..a4e1df4963 100644
--- a/src/hardware/hw_draw.c
+++ b/src/hardware/hw_draw.c
@@ -68,10 +68,11 @@ static UINT8 softwaretranstogl_lo[11] = {  0, 12, 24, 36, 48, 60, 71, 83, 95,111
 // Notes            : x,y : positions relative to the original Doom resolution
 //                  : textes(console+score) + menus + status bar
 // -----------------+
-void HWR_DrawPatch(GLPatch_t *gpatch, INT32 x, INT32 y, INT32 option)
+void HWR_DrawPatch(patch_t *gpatch, INT32 x, INT32 y, INT32 option)
 {
 	FOutVector v[4];
 	FBITFIELD flags;
+	GLPatch_t *hwrPatch;
 
 //  3--2
 //  | /|
@@ -84,6 +85,7 @@ void HWR_DrawPatch(GLPatch_t *gpatch, INT32 x, INT32 y, INT32 option)
 
 	// make patch ready in hardware cache
 	HWR_GetPatch(gpatch);
+	hwrPatch = ((GLPatch_t *)gpatch->hardware);
 
 	switch (option & V_SCALEPATCHMASK)
 	{
@@ -103,17 +105,17 @@ void HWR_DrawPatch(GLPatch_t *gpatch, INT32 x, INT32 y, INT32 option)
 	if (option & V_NOSCALESTART)
 		sdupx = sdupy = 2.0f;
 
-	v[0].x = v[3].x = (x*sdupx-SHORT(gpatch->leftoffset)*pdupx)/vid.width - 1;
-	v[2].x = v[1].x = (x*sdupx+(SHORT(gpatch->width)-SHORT(gpatch->leftoffset))*pdupx)/vid.width - 1;
-	v[0].y = v[1].y = 1-(y*sdupy-SHORT(gpatch->topoffset)*pdupy)/vid.height;
-	v[2].y = v[3].y = 1-(y*sdupy+(SHORT(gpatch->height)-SHORT(gpatch->topoffset))*pdupy)/vid.height;
+	v[0].x = v[3].x = (x*sdupx-(gpatch->leftoffset)*pdupx)/vid.width - 1;
+	v[2].x = v[1].x = (x*sdupx+(gpatch->width-gpatch->leftoffset)*pdupx)/vid.width - 1;
+	v[0].y = v[1].y = 1-(y*sdupy-(gpatch->topoffset)*pdupy)/vid.height;
+	v[2].y = v[3].y = 1-(y*sdupy+(gpatch->height-gpatch->topoffset)*pdupy)/vid.height;
 
 	v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
 
 	v[0].s = v[3].s = 0.0f;
-	v[2].s = v[1].s = gpatch->max_s;
+	v[2].s = v[1].s = hwrPatch->max_s;
 	v[0].t = v[1].t = 0.0f;
-	v[2].t = v[3].t = gpatch->max_t;
+	v[2].t = v[3].t = hwrPatch->max_t;
 
 	flags = PF_Translucent|PF_NoDepthTest;
 
@@ -126,13 +128,14 @@ void HWR_DrawPatch(GLPatch_t *gpatch, INT32 x, INT32 y, INT32 option)
 	HWD.pfnDrawPolygon(NULL, v, 4, flags);
 }
 
-void HWR_DrawStretchyFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 option, const UINT8 *colormap)
+void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 option, const UINT8 *colormap)
 {
 	FOutVector v[4];
 	FBITFIELD flags;
 	float cx = FIXED_TO_FLOAT(x);
 	float cy = FIXED_TO_FLOAT(y);
 	UINT8 alphalevel = ((option & V_ALPHAMASK) >> V_ALPHASHIFT);
+	GLPatch_t *hwrPatch;
 
 //  3--2
 //  | /|
@@ -151,6 +154,8 @@ void HWR_DrawStretchyFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t
 	else
 		HWR_GetMappedPatch(gpatch, colormap);
 
+	hwrPatch = ((GLPatch_t *)gpatch->hardware);
+
 	dupx = (float)vid.dupx;
 	dupy = (float)vid.dupy;
 
@@ -181,13 +186,13 @@ void HWR_DrawStretchyFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t
 
 		// left offset
 		if (option & V_FLIP)
-			offsetx = (float)(SHORT(gpatch->width) - SHORT(gpatch->leftoffset)) * fscalew;
+			offsetx = (float)(gpatch->width - gpatch->leftoffset) * fscalew;
 		else
-			offsetx = (float)SHORT(gpatch->leftoffset) * fscalew;
+			offsetx = (float)(gpatch->leftoffset) * fscalew;
 
 		// top offset
 		// TODO: make some kind of vertical version of V_FLIP, maybe by deprecating V_OFFSET in future?!?
-		offsety = (float)SHORT(gpatch->topoffset) * fscaleh;
+		offsety = (float)(gpatch->topoffset) * fscaleh;
 
 		if ((option & (V_NOSCALESTART|V_OFFSET)) == (V_NOSCALESTART|V_OFFSET)) // Multiply by dupx/dupy for crosshairs
 		{
@@ -277,17 +282,14 @@ void HWR_DrawStretchyFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t
 			// if it's meant to cover the whole screen, black out the rest (ONLY IF TOP LEFT ISN'T TRANSPARENT)
 			// cx and cy are possibly *slightly* off from float maths
 			// This is done before here compared to software because we directly alter cx and cy to centre
-			if (cx >= -0.1f && cx <= 0.1f && SHORT(gpatch->width) == BASEVIDWIDTH && cy >= -0.1f && cy <= 0.1f && SHORT(gpatch->height) == BASEVIDHEIGHT)
+			if (cx >= -0.1f && cx <= 0.1f && (gpatch->width) == BASEVIDWIDTH && cy >= -0.1f && cy <= 0.1f && SHORT(gpatch->height) == BASEVIDHEIGHT)
 			{
-				// Need to temporarily cache the real patch to get the colour of the top left pixel
-				patch_t *realpatch = W_CacheSoftwarePatchNumPwad(gpatch->wadnum, gpatch->lumpnum, PU_STATIC);
-				const column_t *column = (const column_t *)((const UINT8 *)(realpatch) + LONG((realpatch)->columnofs[0]));
+				const column_t *column = (const column_t *)((const UINT8 *)(gpatch->columns) + (gpatch->columnofs[0]));
 				if (!column->topdelta)
 				{
 					const UINT8 *source = (const UINT8 *)(column) + 3;
 					HWR_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0]));
 				}
-				Z_Free(realpatch);
 			}
 			// centre screen
 			if (fabsf((float)vid.width - (float)BASEVIDWIDTH * dupx) > 1.0E-36f)
@@ -317,13 +319,13 @@ void HWR_DrawStretchyFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t
 
 	if (pscale != FRACUNIT || (splitscreen && option & V_PERPLAYER))
 	{
-		fwidth = (float)SHORT(gpatch->width) * fscalew * dupx;
-		fheight = (float)SHORT(gpatch->height) * fscaleh * dupy;
+		fwidth = (float)(gpatch->width) * fscalew * dupx;
+		fheight = (float)(gpatch->height) * fscaleh * dupy;
 	}
 	else
 	{
-		fwidth = (float)SHORT(gpatch->width) * dupx;
-		fheight = (float)SHORT(gpatch->height) * dupy;
+		fwidth = (float)(gpatch->width) * dupx;
+		fheight = (float)(gpatch->height) * dupy;
 	}
 
 	// positions of the cx, cy, are between 0 and vid.width/vid.height now, we need them to be between -1 and 1
@@ -345,17 +347,17 @@ void HWR_DrawStretchyFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t
 
 	if (option & V_FLIP)
 	{
-		v[0].s = v[3].s = gpatch->max_s;
+		v[0].s = v[3].s = hwrPatch->max_s;
 		v[2].s = v[1].s = 0.0f;
 	}
 	else
 	{
 		v[0].s = v[3].s = 0.0f;
-		v[2].s = v[1].s = gpatch->max_s;
+		v[2].s = v[1].s = hwrPatch->max_s;
 	}
 
 	v[0].t = v[1].t = 0.0f;
-	v[2].t = v[3].t = gpatch->max_t;
+	v[2].t = v[3].t = hwrPatch->max_t;
 
 	flags = PF_Translucent|PF_NoDepthTest;
 
@@ -380,13 +382,14 @@ void HWR_DrawStretchyFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t
 		HWD.pfnDrawPolygon(NULL, v, 4, flags);
 }
 
-void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, INT32 option, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h)
+void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, INT32 option, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h)
 {
 	FOutVector v[4];
 	FBITFIELD flags;
 	float cx = FIXED_TO_FLOAT(x);
 	float cy = FIXED_TO_FLOAT(y);
 	UINT8 alphalevel = ((option & V_ALPHAMASK) >> V_ALPHASHIFT);
+	GLPatch_t *hwrPatch;
 
 //  3--2
 //  | /|
@@ -399,6 +402,7 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal
 
 	// make patch ready in hardware cache
 	HWR_GetPatch(gpatch);
+	hwrPatch = ((GLPatch_t *)gpatch->hardware);
 
 	dupx = (float)vid.dupx;
 	dupy = (float)vid.dupy;
@@ -438,15 +442,12 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal
 			// This is done before here compared to software because we directly alter cx and cy to centre
 			if (cx >= -0.1f && cx <= 0.1f && SHORT(gpatch->width) == BASEVIDWIDTH && cy >= -0.1f && cy <= 0.1f && SHORT(gpatch->height) == BASEVIDHEIGHT)
 			{
-				// Need to temporarily cache the real patch to get the colour of the top left pixel
-				patch_t *realpatch = W_CacheSoftwarePatchNumPwad(gpatch->wadnum, gpatch->lumpnum, PU_STATIC);
-				const column_t *column = (const column_t *)((const UINT8 *)(realpatch) + LONG((realpatch)->columnofs[0]));
+				const column_t *column = (const column_t *)((const UINT8 *)(gpatch->columns) + (gpatch->columnofs[0]));
 				if (!column->topdelta)
 				{
 					const UINT8 *source = (const UINT8 *)(column) + 3;
 					HWR_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0]));
 				}
-				Z_Free(realpatch);
 			}
 			// centre screen
 			if (fabsf((float)vid.width - (float)BASEVIDWIDTH * dupx) > 1.0E-36f)
@@ -469,11 +470,11 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal
 	fwidth = w;
 	fheight = h;
 
-	if (fwidth > SHORT(gpatch->width))
-		fwidth = SHORT(gpatch->width);
+	if (fwidth > gpatch->width)
+		fwidth = gpatch->width;
 
-	if (fheight > SHORT(gpatch->height))
-		fheight = SHORT(gpatch->height);
+	if (fheight > gpatch->height)
+		fheight = gpatch->height;
 
 	if (pscale != FRACUNIT)
 	{
@@ -503,17 +504,17 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal
 
 	v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
 
-	v[0].s = v[3].s = ((sx  )/(float)SHORT(gpatch->width) )*gpatch->max_s;
-	if (sx + w > SHORT(gpatch->width))
-		v[2].s = v[1].s = gpatch->max_s;
+	v[0].s = v[3].s = ((sx)/(float)(gpatch->width))*hwrPatch->max_s;
+	if (sx + w > gpatch->width)
+		v[2].s = v[1].s = hwrPatch->max_s;
 	else
-		v[2].s = v[1].s = ((sx+w)/(float)SHORT(gpatch->width) )*gpatch->max_s;
+		v[2].s = v[1].s = ((sx+w)/(float)(gpatch->width))*hwrPatch->max_s;
 
-	v[0].t = v[1].t = ((sy  )/(float)SHORT(gpatch->height))*gpatch->max_t;
-	if (sy + h > SHORT(gpatch->height))
-		v[2].t = v[3].t = gpatch->max_t;
+	v[0].t = v[1].t = ((sy)/(float)(gpatch->height))*hwrPatch->max_t;
+	if (sy + h > gpatch->height)
+		v[2].t = v[3].t = hwrPatch->max_t;
 	else
-		v[2].t = v[3].t = ((sy+h)/(float)SHORT(gpatch->height))*gpatch->max_t;
+		v[2].t = v[3].t = ((sy+h)/(float)(gpatch->height))*hwrPatch->max_t;
 
 	flags = PF_Translucent|PF_NoDepthTest;
 
@@ -541,7 +542,7 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal
 void HWR_DrawPic(INT32 x, INT32 y, lumpnum_t lumpnum)
 {
 	FOutVector      v[4];
-	const GLPatch_t    *patch;
+	const patch_t    *patch;
 
 	// make pic ready in hardware cache
 	patch = HWR_GetPic(lumpnum);
@@ -558,10 +559,10 @@ void HWR_DrawPic(INT32 x, INT32 y, lumpnum_t lumpnum)
 
 	v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
 
-	v[0].s = v[3].s =  0;
-	v[2].s = v[1].s =  patch->max_s;
-	v[0].t = v[1].t =  0;
-	v[2].t = v[3].t =  patch->max_t;
+	v[0].s = v[3].s = 0;
+	v[2].s = v[1].s = ((GLPatch_t *)patch->hardware)->max_s;
+	v[0].t = v[1].t = 0;
+	v[2].t = v[3].t = ((GLPatch_t *)patch->hardware)->max_t;
 
 
 	//Hurdler: Boris, the same comment as above... but maybe for pics
@@ -934,7 +935,7 @@ void HWR_DrawViewBorder(INT32 clearlines)
 	INT32 top, side;
 	INT32 baseviewwidth, baseviewheight;
 	INT32 basewindowx, basewindowy;
-	GLPatch_t *patch;
+	patch_t *patch;
 
 //    if (gl_viewwidth == vid.width)
 //        return;
diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h
index 6f039cc3a0..cc13549095 100644
--- a/src/hardware/hw_drv.h
+++ b/src/hardware/hw_drv.h
@@ -42,6 +42,7 @@ EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags);
 EXPORT void HWRAPI(ClearBuffer) (FBOOLEAN ColorMask, FBOOLEAN DepthMask, FRGBAFloat *ClearColor);
 EXPORT void HWRAPI(SetTexture) (FTextureInfo *TexInfo);
 EXPORT void HWRAPI(UpdateTexture) (FTextureInfo *TexInfo);
+EXPORT void HWRAPI(DeleteTexture) (FTextureInfo *TexInfo);
 EXPORT void HWRAPI(ReadRect) (INT32 x, INT32 y, INT32 width, INT32 height, INT32 dst_stride, UINT16 *dst_data);
 EXPORT void HWRAPI(GClipRect) (INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float nearclip);
 EXPORT void HWRAPI(ClearMipMapCache) (void);
@@ -96,6 +97,7 @@ struct hwdriver_s
 	ClearBuffer         pfnClearBuffer;
 	SetTexture          pfnSetTexture;
 	UpdateTexture       pfnUpdateTexture;
+	DeleteTexture       pfnDeleteTexture;
 	ReadRect            pfnReadRect;
 	GClipRect           pfnGClipRect;
 	ClearMipMapCache    pfnClearMipMapCache;
diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h
index 6ede8448bf..94c553535c 100644
--- a/src/hardware/hw_glob.h
+++ b/src/hardware/hw_glob.h
@@ -64,8 +64,7 @@ typedef struct gl_vissprite_s
 	float x1, x2;
 	float tz, ty;
 	float tracertz; // for MF2_LINKDRAW sprites, this contains tracer's tz for use in sorting
-	//lumpnum_t patchlumpnum;
-	GLPatch_t *gpatch;
+	patch_t *gpatch;
 	boolean flip;
 	UINT8 translucency;       //alpha level 0-255
 	mobj_t *mobj; // NOTE: This is a precipmobj_t if precip is true !!! Watch out.
@@ -86,25 +85,33 @@ extern size_t addsubsector;
 void HWR_InitPolyPool(void);
 void HWR_FreePolyPool(void);
 
+void HWR_FreeExtraSubsectors(void);
+
 // --------
 // hw_cache.c
 // --------
 void HWR_InitTextureCache(void);
 void HWR_FreeTextureCache(void);
 void HWR_FreeMipmapCache(void);
-void HWR_FreeExtraSubsectors(void);
 
+patch_t *HWR_GetCachedGLPatchPwad(UINT16 wad, UINT16 lump);
+patch_t *HWR_GetCachedGLPatch(lumpnum_t lumpnum);
+
+void HWR_GetPatch(patch_t *patch);
+void HWR_GetMappedPatch(patch_t *patch, const UINT8 *colormap);
+void HWR_GetFadeMask(lumpnum_t fademasklumpnum);
+patch_t *HWR_GetPic(lumpnum_t lumpnum);
+
+GLMapTexture_t *HWR_GetTexture(INT32 tex);
 void HWR_GetLevelFlat(levelflat_t *levelflat);
 void HWR_LiterallyGetFlat(lumpnum_t flatlumpnum);
-GLMapTexture_t *HWR_GetTexture(INT32 tex);
-void HWR_GetPatch(GLPatch_t *gpatch);
-void HWR_GetMappedPatch(GLPatch_t *gpatch, const UINT8 *colormap);
+
+void HWR_FreeTexture(patch_t *patch);
+void HWR_FreeTextureColormaps(patch_t *patch);
 void HWR_UnlockCachedPatch(GLPatch_t *gpatch);
-GLPatch_t *HWR_GetPic(lumpnum_t lumpnum);
+
 void HWR_SetPalette(RGBA_t *palette);
-GLPatch_t *HWR_GetCachedGLPatchPwad(UINT16 wad, UINT16 lump);
-GLPatch_t *HWR_GetCachedGLPatch(lumpnum_t lumpnum);
-void HWR_GetFadeMask(lumpnum_t fademasklumpnum);
+
 
 // --------
 // hw_draw.c
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index ddc935f0da..7ba7f911c0 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -706,7 +706,7 @@ static void HWR_DrawSegsSplats(FSurfaceInfo * pSurf)
 {
 	FOutVector wallVerts[4];
 	wallsplat_t *splat;
-	GLPatch_t *gpatch;
+	patch_t *gpatch;
 	fixed_t i;
 	// seg bbox
 	fixed_t segbbox[4];
@@ -3549,7 +3549,7 @@ static boolean HWR_DoCulling(line_t *cullheight, line_t *viewcullheight, float v
 
 static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale)
 {
-	GLPatch_t *gpatch;
+	patch_t *gpatch;
 	FOutVector shadowVerts[4];
 	FSurfaceInfo sSurf;
 	float fscale; float fx; float fy; float offset;
@@ -3575,12 +3575,12 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale)
 	if (alpha >= 255) return;
 	alpha = 255 - alpha;
 
-	gpatch = (GLPatch_t *)W_CachePatchName("DSHADOW", PU_CACHE);
-	if (!(gpatch && gpatch->mipmap->format)) return;
+	gpatch = (patch_t *)W_CachePatchName("DSHADOW", PU_CACHE);
+	if (!(gpatch && ((GLPatch_t *)gpatch->hardware)->mipmap->format)) return;
 	HWR_GetPatch(gpatch);
 
 	scalemul = FixedMul(FRACUNIT - floordiff/640, scale);
-	scalemul = FixedMul(scalemul, (thing->radius*2) / SHORT(gpatch->height));
+	scalemul = FixedMul(scalemul, (thing->radius*2) / gpatch->height);
 
 	fscale = FIXED_TO_FLOAT(scalemul);
 	fx = FIXED_TO_FLOAT(thing->x);
@@ -3592,9 +3592,9 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale)
 	//  0--1
 
 	if (thing && fabsf(fscale - 1.0f) > 1.0E-36f)
-		offset = (SHORT(gpatch->height)/2) * fscale;
+		offset = ((gpatch->height)/2) * fscale;
 	else
-		offset = (float)(SHORT(gpatch->height)/2);
+		offset = (float)((gpatch->height)/2);
 
 	shadowVerts[2].x = shadowVerts[3].x = fx + offset;
 	shadowVerts[1].x = shadowVerts[0].x = fx - offset;
@@ -3624,10 +3624,10 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale)
 	}
 
 	shadowVerts[0].s = shadowVerts[3].s = 0;
-	shadowVerts[2].s = shadowVerts[1].s = gpatch->max_s;
+	shadowVerts[2].s = shadowVerts[1].s = ((GLPatch_t *)gpatch->hardware)->max_s;
 
 	shadowVerts[3].t = shadowVerts[2].t = 0;
-	shadowVerts[0].t = shadowVerts[1].t = gpatch->max_t;
+	shadowVerts[0].t = shadowVerts[1].t = ((GLPatch_t *)gpatch->hardware)->max_t;
 
 	if (thing->subsector->sector->numlights)
 	{
@@ -3687,7 +3687,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr)
 	float this_scale = 1.0f;
 	FOutVector wallVerts[4];
 	FOutVector baseWallVerts[4]; // This is what the verts should end up as
-	GLPatch_t *gpatch;
+	patch_t *gpatch;
 	FSurfaceInfo Surf;
 	const boolean hires = (spr->mobj && spr->mobj->skin && ((skin_t *)spr->mobj->skin)->flags & SF_HIRES);
 	extracolormap_t *colormap;
@@ -3715,7 +3715,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr)
 	if (hires)
 		this_scale = this_scale * FIXED_TO_FLOAT(((skin_t *)spr->mobj->skin)->highresscale);
 
-	gpatch = spr->gpatch; //W_CachePatchNum(spr->patchlumpnum, PU_CACHE);
+	gpatch = spr->gpatch;
 
 	// cache the patch in the graphics card memory
 	//12/12/99: Hurdler: same comment as above (for md2)
@@ -3740,25 +3740,25 @@ static void HWR_SplitSprite(gl_vissprite_t *spr)
 
 	if (spr->flip)
 	{
-		baseWallVerts[0].s = baseWallVerts[3].s = gpatch->max_s;
+		baseWallVerts[0].s = baseWallVerts[3].s = ((GLPatch_t *)gpatch->hardware)->max_s;
 		baseWallVerts[2].s = baseWallVerts[1].s = 0;
 	}
 	else
 	{
 		baseWallVerts[0].s = baseWallVerts[3].s = 0;
-		baseWallVerts[2].s = baseWallVerts[1].s = gpatch->max_s;
+		baseWallVerts[2].s = baseWallVerts[1].s = ((GLPatch_t *)gpatch->hardware)->max_s;
 	}
 
 	// flip the texture coords (look familiar?)
 	if (spr->vflip)
 	{
-		baseWallVerts[3].t = baseWallVerts[2].t = gpatch->max_t;
+		baseWallVerts[3].t = baseWallVerts[2].t = ((GLPatch_t *)gpatch->hardware)->max_t;
 		baseWallVerts[0].t = baseWallVerts[1].t = 0;
 	}
 	else
 	{
 		baseWallVerts[3].t = baseWallVerts[2].t = 0;
-		baseWallVerts[0].t = baseWallVerts[1].t = gpatch->max_t;
+		baseWallVerts[0].t = baseWallVerts[1].t = ((GLPatch_t *)gpatch->hardware)->max_t;
 	}
 
 	// if it has a dispoffset, push it a little towards the camera
@@ -3963,7 +3963,7 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
 {
 	float this_scale = 1.0f;
 	FOutVector wallVerts[4];
-	GLPatch_t *gpatch; // sprite patch converted to hardware
+	patch_t *gpatch; // sprite patch converted to hardware
 	FSurfaceInfo Surf;
 	const boolean hires = (spr->mobj && spr->mobj->skin && ((skin_t *)spr->mobj->skin)->flags & SF_HIRES);
 	//const boolean papersprite = (spr->mobj && (spr->mobj->frame & FF_PAPERSPRITE));
@@ -3990,7 +3990,7 @@ static void HWR_DrawSprite(gl_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 = spr->gpatch; //W_CachePatchNum(spr->patchlumpnum, PU_CACHE);
+	gpatch = spr->gpatch;
 
 #ifdef ALAM_LIGHTING
 	if (!(spr->mobj->flags2 & MF2_DEBRIS) && (spr->mobj->sprite != SPR_PLAY ||
@@ -4021,21 +4021,21 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
 
 	if (spr->flip)
 	{
-		wallVerts[0].s = wallVerts[3].s = gpatch->max_s;
+		wallVerts[0].s = wallVerts[3].s = ((GLPatch_t *)gpatch->hardware)->max_s;
 		wallVerts[2].s = wallVerts[1].s = 0;
 	}else{
 		wallVerts[0].s = wallVerts[3].s = 0;
-		wallVerts[2].s = wallVerts[1].s = gpatch->max_s;
+		wallVerts[2].s = wallVerts[1].s = ((GLPatch_t *)gpatch->hardware)->max_s;
 	}
 
 	// flip the texture coords (look familiar?)
 	if (spr->vflip)
 	{
-		wallVerts[3].t = wallVerts[2].t = gpatch->max_t;
+		wallVerts[3].t = wallVerts[2].t = ((GLPatch_t *)gpatch->hardware)->max_t;
 		wallVerts[0].t = wallVerts[1].t = 0;
 	}else{
 		wallVerts[3].t = wallVerts[2].t = 0;
-		wallVerts[0].t = wallVerts[1].t = gpatch->max_t;
+		wallVerts[0].t = wallVerts[1].t = ((GLPatch_t *)gpatch->hardware)->max_t;
 	}
 
 	// cache the patch in the graphics card memory
@@ -4121,7 +4121,7 @@ static inline void HWR_DrawPrecipitationSprite(gl_vissprite_t *spr)
 {
 	FBITFIELD blend = 0;
 	FOutVector wallVerts[4];
-	GLPatch_t *gpatch; // sprite patch converted to hardware
+	patch_t *gpatch; // sprite patch converted to hardware
 	FSurfaceInfo Surf;
 
 	if (!spr->mobj)
@@ -4131,7 +4131,7 @@ static inline void HWR_DrawPrecipitationSprite(gl_vissprite_t *spr)
 		return;
 
 	// cache sprite graphics
-	gpatch = spr->gpatch; //W_CachePatchNum(spr->patchlumpnum, PU_CACHE);
+	gpatch = spr->gpatch;
 
 	// create the sprite billboard
 	//
@@ -4153,10 +4153,10 @@ static inline void HWR_DrawPrecipitationSprite(gl_vissprite_t *spr)
 	HWR_RotateSpritePolyToAim(spr, wallVerts, true);
 
 	wallVerts[0].s = wallVerts[3].s = 0;
-	wallVerts[2].s = wallVerts[1].s = gpatch->max_s;
+	wallVerts[2].s = wallVerts[1].s = ((GLPatch_t *)gpatch->hardware)->max_s;
 
 	wallVerts[3].t = wallVerts[2].t = 0;
-	wallVerts[0].t = wallVerts[1].t = gpatch->max_t;
+	wallVerts[0].t = wallVerts[1].t = ((GLPatch_t *)gpatch->hardware)->max_t;
 
 	// cache the patch in the graphics card memory
 	//12/12/99: Hurdler: same comment as above (for md2)
@@ -5014,13 +5014,12 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	vis->tz = tz; // Keep tz for the simple sprite sorting that happens
 	vis->tracertz = tracertz;
 	vis->dispoffset = dispoffset; // Monster Iestyn: 23/11/15: HARDWARE SUPPORT AT LAST
-	//vis->patchlumpnum = sprframe->lumppat[rot];
 #ifdef ROTSPRITE
 	if (rotsprite)
-		vis->gpatch = (GLPatch_t *)rotsprite;
+		vis->gpatch = (patch_t *)rotsprite;
 	else
 #endif
-		vis->gpatch = (GLPatch_t *)W_CachePatchNum(sprframe->lumppat[rot], PU_CACHE);
+		vis->gpatch = (patch_t *)W_CachePatchNum(sprframe->lumppat[rot], PU_CACHE);
 	vis->flip = flip;
 	vis->mobj = thing;
 	vis->z1 = z1;
@@ -5155,8 +5154,7 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
 	vis->z2 = z2;
 	vis->tz = tz;
 	vis->dispoffset = 0; // Monster Iestyn: 23/11/15: HARDWARE SUPPORT AT LAST
-	//vis->patchlumpnum = sprframe->lumppat[rot];
-	vis->gpatch = (GLPatch_t *)W_CachePatchNum(sprframe->lumppat[rot], PU_CACHE);
+	vis->gpatch = (patch_t *)W_CachePatchNum(sprframe->lumppat[rot], PU_CACHE);
 	vis->flip = flip;
 	vis->mobj = (mobj_t *)thing;
 
diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h
index ddb3696b6f..3bcef05de3 100644
--- a/src/hardware/hw_main.h
+++ b/src/hardware/hw_main.h
@@ -35,9 +35,9 @@ void HWR_DrawViewBorder(INT32 clearlines);
 void HWR_DrawFlatFill(INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatlumpnum);
 void HWR_InitTextureMapping(void);
 void HWR_SetViewSize(void);
-void HWR_DrawPatch(GLPatch_t *gpatch, INT32 x, INT32 y, INT32 option);
-void HWR_DrawStretchyFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 option, const UINT8 *colormap);
-void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t scale, INT32 option, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h);
+void HWR_DrawPatch(patch_t *gpatch, INT32 x, INT32 y, INT32 option);
+void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 option, const UINT8 *colormap);
+void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t scale, INT32 option, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h);
 void HWR_MakePatch(const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipmap, boolean makebitmap);
 void HWR_CreatePlanePolygons(INT32 bspnum);
 void HWR_CreateStaticLightmaps(INT32 bspnum);
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index fa5156758e..caef0a02d5 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -348,48 +348,53 @@ static GLTextureFormat_t PCX_Load(const char *filename, int *w, int *h,
 // -----------------+
 static void md2_loadTexture(md2_t *model)
 {
-	GLPatch_t *grpatch;
+	patch_t *patch;
+	GLPatch_t *grPatch = NULL;
 	const char *filename = model->filename;
 
 	if (model->grpatch)
 	{
-		grpatch = model->grpatch;
-		Z_Free(grpatch->mipmap->data);
+		patch = model->grpatch;
+		grPatch = (GLPatch_t *)(patch->hardware);
+		if (grPatch)
+			Z_Free(grPatch->mipmap->data);
 	}
 	else
-	{
-		grpatch = Z_Calloc(sizeof *grpatch, PU_HWRPATCHINFO,
-		                   &(model->grpatch));
-		grpatch->mipmap = Z_Calloc(sizeof (GLMipmap_t), PU_HWRPATCHINFO, NULL);
-	}
+		model->grpatch = patch = Patch_Create(NULL, 0, NULL);
+
+	if (!patch->hardware)
+		Patch_AllocateHardwarePatch(patch);
+
+	if (grPatch == NULL)
+		grPatch = (GLPatch_t *)(patch->hardware);
 
-	if (!grpatch->mipmap->downloaded && !grpatch->mipmap->data)
+	if (!grPatch->mipmap->downloaded && !grPatch->mipmap->data)
 	{
 		int w = 0, h = 0;
 		UINT32 size;
 		RGBA_t *image;
 
 #ifdef HAVE_PNG
-		grpatch->mipmap->format = PNG_Load(filename, &w, &h, grpatch);
-		if (grpatch->mipmap->format == 0)
+		grPatch->mipmap->format = PNG_Load(filename, &w, &h, grPatch);
+		if (grPatch->mipmap->format == 0)
 #endif
-		grpatch->mipmap->format = PCX_Load(filename, &w, &h, grpatch);
-		if (grpatch->mipmap->format == 0)
+		grPatch->mipmap->format = PCX_Load(filename, &w, &h, grPatch);
+		if (grPatch->mipmap->format == 0)
 		{
 			model->notexturefile = true; // mark it so its not searched for again repeatedly
 			return;
 		}
 
-		grpatch->mipmap->downloaded = 0;
-		grpatch->mipmap->flags = 0;
+		grPatch->mipmap->downloaded = 0;
+		grPatch->mipmap->flags = 0;
 
-		grpatch->width = (INT16)w;
-		grpatch->height = (INT16)h;
-		grpatch->mipmap->width = (UINT16)w;
-		grpatch->mipmap->height = (UINT16)h;
+		patch->width = (INT16)w;
+		patch->height = (INT16)h;
+		grPatch->mipmap->width = (UINT16)w;
+		grPatch->mipmap->height = (UINT16)h;
 
 		// Lactozilla: Apply colour cube
-		image = grpatch->mipmap->data;
+		image = grPatch->mipmap->data;
 		size = w*h;
 		while (size--)
 		{
@@ -397,7 +402,7 @@ static void md2_loadTexture(md2_t *model)
 			image++;
 		}
 	}
-	HWD.pfnSetTexture(grpatch->mipmap);
+	HWD.pfnSetTexture(grPatch->mipmap);
 }
 
 // -----------------+
@@ -405,48 +410,53 @@ static void md2_loadTexture(md2_t *model)
 // -----------------+
 static void md2_loadBlendTexture(md2_t *model)
 {
-	GLPatch_t *grpatch;
+	patch_t *patch;
+	GLPatch_t *grPatch = NULL;
 	char *filename = Z_Malloc(strlen(model->filename)+7, PU_STATIC, NULL);
-	strcpy(filename, model->filename);
 
+	strcpy(filename, model->filename);
 	FIL_ForceExtension(filename, "_blend.png");
 
 	if (model->blendgrpatch)
 	{
-		grpatch = model->blendgrpatch;
-		Z_Free(grpatch->mipmap->data);
+		patch = model->blendgrpatch;
+		grPatch = (GLPatch_t *)(patch->hardware);
+		if (grPatch)
+			Z_Free(grPatch->mipmap->data);
 	}
 	else
-	{
-		grpatch = Z_Calloc(sizeof *grpatch, PU_HWRPATCHINFO,
-		                   &(model->blendgrpatch));
-		grpatch->mipmap = Z_Calloc(sizeof (GLMipmap_t), PU_HWRPATCHINFO, NULL);
-	}
+		model->blendgrpatch = patch = Patch_Create(NULL, 0, NULL);
+
+	if (!patch->hardware)
+		Patch_AllocateHardwarePatch(patch);
+
+	if (grPatch == NULL)
+		grPatch = (GLPatch_t *)(patch->hardware);
 
-	if (!grpatch->mipmap->downloaded && !grpatch->mipmap->data)
+	if (!grPatch->mipmap->downloaded && !grPatch->mipmap->data)
 	{
 		int w = 0, h = 0;
 #ifdef HAVE_PNG
-		grpatch->mipmap->format = PNG_Load(filename, &w, &h, grpatch);
-		if (grpatch->mipmap->format == 0)
+		grPatch->mipmap->format = PNG_Load(filename, &w, &h, grPatch);
+		if (grPatch->mipmap->format == 0)
 #endif
-		grpatch->mipmap->format = PCX_Load(filename, &w, &h, grpatch);
-		if (grpatch->mipmap->format == 0)
+		grPatch->mipmap->format = PCX_Load(filename, &w, &h, grPatch);
+		if (grPatch->mipmap->format == 0)
 		{
 			model->noblendfile = true; // mark it so its not searched for again repeatedly
 			Z_Free(filename);
 			return;
 		}
 
-		grpatch->mipmap->downloaded = 0;
-		grpatch->mipmap->flags = 0;
+		grPatch->mipmap->downloaded = 0;
+		grPatch->mipmap->flags = 0;
 
-		grpatch->width = (INT16)w;
-		grpatch->height = (INT16)h;
-		grpatch->mipmap->width = (UINT16)w;
-		grpatch->mipmap->height = (UINT16)h;
+		patch->width = (INT16)w;
+		patch->height = (INT16)h;
+		grPatch->mipmap->width = (UINT16)w;
+		grPatch->mipmap->height = (UINT16)h;
 	}
-	HWD.pfnSetTexture(grpatch->mipmap); // We do need to do this so that it can be cleared and knows to recreate it when necessary
+	HWD.pfnSetTexture(grPatch->mipmap); // We do need to do this so that it can be cleared and knows to recreate it when necessary
 
 	Z_Free(filename);
 }
@@ -664,8 +674,10 @@ spritemodelfound:
 #define SETBRIGHTNESS(brightness,r,g,b) \
 	brightness = (UINT8)(((1063*(UINT16)(r))/5000) + ((3576*(UINT16)(g))/5000) + ((361*(UINT16)(b))/5000))
 
-static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, GLMipmap_t *grmip, INT32 skinnum, skincolornum_t color)
+static void HWR_CreateBlendedTexture(patch_t *gpatch, patch_t *blendgpatch, GLMipmap_t *grMipmap, INT32 skinnum, skincolornum_t color)
 {
+	GLPatch_t *hwrPatch = gpatch->hardware;
+	GLPatch_t *hwrBlendPatch = blendgpatch->hardware;
 	UINT16 w = gpatch->width, h = gpatch->height;
 	UINT32 size = w*h;
 	RGBA_t *image, *blendimage, *cur, blendcolor;
@@ -678,28 +690,29 @@ static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch,
 	memset(translation, 0, sizeof(translation));
 	memset(cutoff, 0, sizeof(cutoff));
 
-	if (grmip->width == 0)
+	if (grMipmap->width == 0)
 	{
-		grmip->width = gpatch->width;
-		grmip->height = gpatch->height;
+		grMipmap->width = gpatch->width;
+		grMipmap->height = gpatch->height;
 
 		// no wrap around, no chroma key
-		grmip->flags = 0;
+		grMipmap->flags = 0;
+
 		// setup the texture info
-		grmip->format = GL_TEXFMT_RGBA;
+		grMipmap->format = GL_TEXFMT_RGBA;
 	}
 
-	if (grmip->data)
+	if (grMipmap->data)
 	{
-		Z_Free(grmip->data);
-		grmip->data = NULL;
+		Z_Free(grMipmap->data);
+		grMipmap->data = NULL;
 	}
 
-	cur = Z_Malloc(size*4, PU_HWRMODELTEXTURE, &grmip->data);
+	cur = Z_Malloc(size*4, PU_HWRMODELTEXTURE, &grMipmap->data);
 	memset(cur, 0x00, size*4);
 
-	image = gpatch->mipmap->data;
-	blendimage = blendgpatch->mipmap->data;
+	image = hwrPatch->mipmap->data;
+	blendimage = hwrBlendPatch->mipmap->data;
 
 	// TC_METALSONIC includes an actual skincolor translation, on top of its flashing.
 	if (skinnum == TC_METALSONIC)
@@ -1038,37 +1051,39 @@ skippixel:
 
 #undef SETBRIGHTNESS
 
-static void HWR_GetBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, INT32 skinnum, const UINT8 *colormap, skincolornum_t color)
+static void HWR_GetBlendedTexture(patch_t *patch, patch_t *blendpatch, INT32 skinnum, const UINT8 *colormap, skincolornum_t color)
 {
 	// mostly copied from HWR_GetMappedPatch, hence the similarities and comment
-	GLMipmap_t *grmip, *newmip;
+	GLPatch_t *grPatch = patch->hardware;
+	GLPatch_t *grBlendPatch = NULL;
+	GLMipmap_t *grMipmap, *newMipmap;
 
-	if (colormap == colormaps || colormap == NULL)
+	if (blendpatch == NULL || colormap == colormaps || colormap == NULL)
 	{
 		// Don't do any blending
-		HWD.pfnSetTexture(gpatch->mipmap);
+		HWD.pfnSetTexture(grPatch->mipmap);
 		return;
 	}
 
-	if ((blendgpatch && blendgpatch->mipmap->format)
-		&& (gpatch->width != blendgpatch->width || gpatch->height != blendgpatch->height))
+	if ((blendpatch && (grBlendPatch = blendpatch->hardware) && grBlendPatch->mipmap->format)
+		&& (patch->width != blendpatch->width || patch->height != blendpatch->height))
 	{
 		// Blend image exists, but it's bad.
-		HWD.pfnSetTexture(gpatch->mipmap);
+		HWD.pfnSetTexture(grPatch->mipmap);
 		return;
 	}
 
 	// search for the mipmap
 	// skip the first (no colormap translated)
-	for (grmip = gpatch->mipmap; grmip->nextcolormap; )
+	for (grMipmap = grPatch->mipmap; grMipmap->nextcolormap; )
 	{
-		grmip = grmip->nextcolormap;
-		if (grmip->colormap == colormap)
+		grMipmap = grMipmap->nextcolormap;
+		if (grMipmap->colormap == colormap)
 		{
-			if (grmip->downloaded && grmip->data)
+			if (grMipmap->downloaded && grMipmap->data)
 			{
-				HWD.pfnSetTexture(grmip); // found the colormap, set it to the correct texture
-				Z_ChangeTag(grmip->data, PU_HWRMODELTEXTURE_UNLOCKED);
+				HWD.pfnSetTexture(grMipmap); // found the colormap, set it to the correct texture
+				Z_ChangeTag(grMipmap->data, PU_HWRMODELTEXTURE_UNLOCKED);
 				return;
 			}
 		}
@@ -1081,16 +1096,16 @@ static void HWR_GetBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, INT
 	//              (it have a liste of mipmap)
 	//    this malloc is cleared in HWR_FreeTextureCache
 	//    (...) unfortunately z_malloc fragment alot the memory :(so malloc is better
-	newmip = calloc(1, sizeof (*newmip));
-	if (newmip == NULL)
+	newMipmap = calloc(1, sizeof (*newMipmap));
+	if (newMipmap == NULL)
 		I_Error("%s: Out of memory", "HWR_GetBlendedTexture");
-	grmip->nextcolormap = newmip;
-	newmip->colormap = colormap;
+	grMipmap->nextcolormap = newMipmap;
+	newMipmap->colormap = colormap;
 
-	HWR_CreateBlendedTexture(gpatch, blendgpatch, newmip, skinnum, color);
+	HWR_CreateBlendedTexture(patch, blendpatch, newMipmap, skinnum, color);
 
-	HWD.pfnSetTexture(newmip);
-	Z_ChangeTag(newmip->data, PU_HWRMODELTEXTURE_UNLOCKED);
+	HWD.pfnSetTexture(newMipmap);
+	Z_ChangeTag(newMipmap->data, PU_HWRMODELTEXTURE_UNLOCKED);
 }
 
 #define NORMALFOG 0x00000000
@@ -1177,9 +1192,11 @@ static UINT8 HWR_GetModelSprite2(md2_t *md2, skin_t *skin, UINT8 spr2, player_t
 	return spr2;
 }
 
-static void adjustTextureCoords(model_t *model, GLPatch_t *gpatch)
+static void adjustTextureCoords(model_t *model, patch_t *patch)
 {
 	int i;
+	GLPatch_t *gpatch = ((GLPatch_t *)patch->hardware);
+
 	for (i = 0; i < model->numMeshes; i++)
 	{
 		int j;
@@ -1264,7 +1281,8 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 
 	// Look at HWR_ProjectSprite for more
 	{
-		GLPatch_t *gpatch;
+		patch_t *gpatch, *blendgpatch;
+		GLPatch_t *hwrPatch = NULL, *hwrBlendPatch = NULL;
 		INT32 durs = spr->mobj->state->tics;
 		INT32 tics = spr->mobj->tics;
 		//mdlframe_t *next = NULL;
@@ -1309,14 +1327,26 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 		// texture loading before model init, so it knows if sprite graphics are used, which
 		// means that texture coordinates have to be adjusted
 		gpatch = md2->grpatch;
-		if (!gpatch || ((!gpatch->mipmap->format || !gpatch->mipmap->downloaded) && !md2->notexturefile))
+		if (gpatch)
+			hwrPatch = ((GLPatch_t *)gpatch->hardware);
+
+		if (!gpatch || !hwrPatch
+		|| ((!hwrPatch->mipmap->format || !hwrPatch->mipmap->downloaded) && !md2->notexturefile))
 			md2_loadTexture(md2);
-		gpatch = md2->grpatch; // Load it again, because it isn't being loaded into gpatch after md2_loadtexture...
 
-		if ((gpatch && gpatch->mipmap->format) // don't load the blend texture if the base texture isn't available
-			&& (!md2->blendgrpatch
-			|| ((!((GLPatch_t *)md2->blendgrpatch)->mipmap->format || !((GLPatch_t *)md2->blendgrpatch)->mipmap->downloaded)
-			&& !md2->noblendfile)))
+		// Load it again, because it isn't being loaded into gpatch after md2_loadtexture...
+		gpatch = md2->grpatch;
+		if (gpatch)
+			hwrPatch = ((GLPatch_t *)gpatch->hardware);
+
+		// Load blend texture
+		blendgpatch = md2->blendgrpatch;
+		if (blendgpatch)
+			hwrBlendPatch = ((GLPatch_t *)blendgpatch->hardware);
+
+		if ((gpatch && hwrPatch && hwrPatch->mipmap->format) // don't load the blend texture if the base texture isn't available
+			&& (!blendgpatch || !hwrBlendPatch
+			|| ((!hwrBlendPatch->mipmap->format || !hwrBlendPatch->mipmap->downloaded) && !md2->noblendfile)))
 			md2_loadBlendTexture(md2);
 
 		if (md2->error)
@@ -1332,7 +1362,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 				md2_printModelInfo(md2->model);
 				// if model uses sprite patch as texture, then
 				// adjust texture coordinates to take power of two textures into account
-				if (!gpatch || !gpatch->mipmap->format)
+				if (!gpatch || !hwrPatch->mipmap->format)
 					adjustTextureCoords(md2->model, spr->gpatch);
 				HWD.pfnCreateModelVBOs(md2->model);
 			}
@@ -1352,7 +1382,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 		finalscale = md2->scale;
 		//Hurdler: arf, I don't like that implementation at all... too much crappy
 
-		if (gpatch && gpatch->mipmap->format) // else if meant that if a texture couldn't be loaded, it would just end up using something else's texture
+		if (gpatch && hwrPatch && hwrPatch->mipmap->format) // else if meant that if a texture couldn't be loaded, it would just end up using something else's texture
 		{
 			INT32 skinnum = TC_DEFAULT;
 
@@ -1385,13 +1415,12 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 			}
 
 			// Translation or skin number found
-			HWR_GetBlendedTexture(gpatch, (GLPatch_t *)md2->blendgrpatch, skinnum, spr->colormap, (skincolornum_t)spr->mobj->color);
+			HWR_GetBlendedTexture(gpatch, blendgpatch, skinnum, spr->colormap, (skincolornum_t)spr->mobj->color);
 		}
 		else
 		{
 			// Sprite
-			gpatch = spr->gpatch; //W_CachePatchNum(spr->patchlumpnum, PU_CACHE);
-			HWR_GetMappedPatch(gpatch, spr->colormap);
+			HWR_GetMappedPatch(spr->gpatch, spr->colormap);
 		}
 
 		if (spr->mobj->frame & FF_ANIMATE)
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index 461966224d..fbe65bc171 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -1317,6 +1317,17 @@ void SetStates(void)
 }
 
 
+// -----------------+
+// DeleteTexture    : Deletes a texture from the GPU and frees its data
+// -----------------+
+EXPORT void HWRAPI(DeleteTexture) (FTextureInfo *pTexInfo)
+{
+	if (pTexInfo->downloaded)
+		pglDeleteTextures(1, (GLuint *)&pTexInfo->downloaded);
+	pTexInfo->downloaded = 0;
+}
+
+
 // -----------------+
 // Flush            : flush OpenGL textures
 //                  : Clear list of downloaded mipmaps
@@ -1327,9 +1338,7 @@ void Flush(void)
 
 	while (gl_cachehead)
 	{
-		if (gl_cachehead->downloaded)
-			pglDeleteTextures(1, (GLuint *)&gl_cachehead->downloaded);
-		gl_cachehead->downloaded = 0;
+		DeleteTexture(gl_cachehead);
 		gl_cachehead = gl_cachehead->nextmipmap;
 	}
 	gl_cachetail = gl_cachehead = NULL; //Hurdler: well, gl_cachehead is already NULL
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index b611925330..ff9f274fcd 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -2038,9 +2038,6 @@ 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/lua_infolib.c b/src/lua_infolib.c
index 830d97625d..712983cacc 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -379,7 +379,7 @@ static int lib_setSpriteInfo(lua_State *L)
 			return luaL_error(L, "spriteinfo[] index %d out of range (1 - %d)", i, NUMSPRITES-1);
 #ifdef ROTSPRITE
 		if (sprites != NULL)
-			R_FreeSingleRotSprite(&sprites[i]);
+			R_FreeRotSprite(&sprites[i]);
 #endif
 		info = &spriteinfo[i]; // get the spriteinfo to assign to.
 	}
@@ -464,7 +464,7 @@ static int spriteinfo_set(lua_State *L)
 
 #ifdef ROTSPRITE
 	if (sprites != NULL)
-		R_FreeSingleRotSprite(&sprites[sprinfo-spriteinfo]);
+		R_FreeRotSprite(&sprites[sprinfo-spriteinfo]);
 #endif
 
 	if (fastcmp(field, "pivot"))
diff --git a/src/m_menu.c b/src/m_menu.c
index 59d297e1a2..3d6b1c0fae 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -1490,7 +1490,7 @@ static menuitem_t OP_SoundOptionsMenu[] =
 
 	{IT_STRING | IT_CVAR,  NULL,  "MIDI Music", &cv_gamemidimusic, 36},
 	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "MIDI Music Volume", &cv_midimusicvolume, 41},
-	
+
 	{IT_STRING | IT_CVAR,  NULL,  "Music Preference", &cv_musicpref, 51},
 
 	{IT_HEADER, NULL, "Miscellaneous", NULL, 61},
@@ -5593,9 +5593,6 @@ static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, bo
 	if (map <= 0)
 		return;
 
-	if (needpatchrecache)
-		M_CacheLevelPlatter();
-
 	//  A 564x100 image of the level as entry MAPxxW
 	if (!(levelselect.rows[row].mapavailable[col]))
 	{
@@ -5627,9 +5624,6 @@ static void M_DrawLevelPlatterMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolea
 	if (map <= 0)
 		return;
 
-	if (needpatchrecache)
-		M_CacheLevelPlatter();
-
 	//  A 160x100 image of the level as entry MAPxxP
 	if (!(levelselect.rows[row].mapavailable[col]))
 	{
@@ -6452,10 +6446,6 @@ static void M_DrawAddons(void)
 		return;
 	}
 
-	// Lactozilla: Load addons menu patches.
-	if (needpatchrecache)
-		M_LoadAddonsPatches();
-
 	if (Playing())
 		V_DrawCenteredString(BASEVIDWIDTH/2, 5, warningflags, "Adding files mid-game may cause problems.");
 	else
@@ -7610,9 +7600,6 @@ static void M_DrawSoundTest(void)
 	fixed_t hscale = FRACUNIT/2, vscale = FRACUNIT/2, bounce = 0;
 	UINT8 frame[4] = {0, 0, -1, SKINCOLOR_RUBY};
 
-	if (needpatchrecache)
-		M_CacheSoundTest();
-
 	// let's handle the ticker first. ideally we'd tick this somewhere else, BUT...
 	if (curplaying)
 	{
@@ -8260,9 +8247,6 @@ static void M_DrawLoadGameData(void)
 	if (vid.width != BASEVIDWIDTH*vid.dupx)
 		hsep = (hsep*vid.width)/(BASEVIDWIDTH*vid.dupx);
 
-	if (needpatchrecache)
-		M_CacheLoadGameData();
-
 	for (i = -2; i <= 2; i++)
 	{
 		savetodraw = (saveSlotSelected + i + numsaves)%numsaves;
@@ -8966,7 +8950,6 @@ void M_ForceSaveSlotSelected(INT32 sslot)
 // CHARACTER SELECT
 // ================
 
-// lactozilla: sometimes the renderer changes and these patches don't exist anymore
 static void M_CacheCharacterSelectEntry(INT32 i, INT32 skinnum)
 {
 	if (!(description[i].picname[0]))
@@ -8987,22 +8970,6 @@ static void M_CacheCharacterSelectEntry(INT32 i, INT32 skinnum)
 		description[i].namepic = W_CachePatchName(description[i].nametag, PU_PATCH);
 }
 
-static void M_CacheCharacterSelect(void)
-{
-	INT32 i, skinnum;
-
-	for (i = 0; i < MAXSKINS; i++)
-	{
-		if (!description[i].used)
-			continue;
-
-		// Already set in M_SetupChoosePlayer
-		skinnum = description[i].skinnum[0];
-		if ((skinnum != -1) && (R_SkinUsable(-1, skinnum)))
-			M_CacheCharacterSelectEntry(i, skinnum);
-	}
-}
-
 static UINT8 M_SetupChoosePlayerDirect(INT32 choice)
 {
 	INT32 skinnum;
@@ -9209,10 +9176,6 @@ static void M_DrawSetupChoosePlayerMenu(void)
 	INT32 x, y;
 	INT32 w = (vid.width/vid.dupx);
 
-	// lactozilla: the renderer changed so recache patches
-	if (needpatchrecache)
-		M_CacheCharacterSelect();
-
 	if (abs(char_scroll) > FRACUNIT)
 		char_scroll -= (char_scroll>>2);
 	else // close enough.
@@ -10584,10 +10547,6 @@ void M_DrawMarathon(void)
 	angle_t fa;
 	INT32 dupz = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy), xspan = (vid.width/dupz), yspan = (vid.height/dupz), diffx = (xspan - BASEVIDWIDTH)/2, diffy = (yspan - BASEVIDHEIGHT)/2, maxy = BASEVIDHEIGHT + diffy;
 
-	// lactozilla: the renderer changed so recache patches
-	if (needpatchrecache)
-		M_CacheCharacterSelect();
-
 	curbgxspeed = 0;
 	curbgyspeed = 18;
 
diff --git a/src/r_data.c b/src/r_data.c
index befb73c20a..4b2ff74841 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -482,7 +482,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
 	UINT8 *blocktex;
 	texture_t *texture;
 	texpatch_t *patch;
-	patch_t *realpatch;
+	softwarepatch_t *realpatch;
 	UINT8 *pdata;
 	int x, x1, x2, i, width, height;
 	size_t blocksize;
@@ -512,7 +512,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
 		lumpnum = patch->lump;
 		lumplength = W_LumpLengthPwad(wadnum, lumpnum);
 		pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
-		realpatch = (patch_t *)pdata;
+		realpatch = (softwarepatch_t *)pdata;
 
 #ifndef NO_PNG_LUMPS
 		if (R_IsLumpPNG((UINT8 *)realpatch, lumplength))
@@ -608,7 +608,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
 		lumpnum = patch->lump;
 		pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
 		lumplength = W_LumpLengthPwad(wadnum, lumpnum);
-		realpatch = (patch_t *)pdata;
+		realpatch = (softwarepatch_t *)pdata;
 		dealloc = true;
 
 #ifndef NO_PNG_LUMPS
diff --git a/src/r_defs.h b/src/r_defs.h
index 132106bc88..10fc99ccda 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -652,16 +652,26 @@ typedef enum
 	RGBA32          = 4,  // 32 bit rgba
 } pic_mode_t;
 
-#if defined(_MSC_VER)
-#pragma pack(1)
-#endif
-
 // Patches.
 // A patch holds one or more columns.
 // Patches are used for sprites and all masked pictures, and we compose
 // textures from the TEXTURES list of patches.
 //
-// WARNING: this structure is cloned in GLPatch_t
+typedef struct
+{
+	INT16 width, height;
+	INT16 leftoffset, topoffset;
+
+	INT32 *columnofs; // Column offsets. This is relative to patch->columns
+	UINT8 *columns; // Software column data
+
+	void *hardware; // OpenGL patch, allocated whenever necessary
+} patch_t;
+
+#if defined(_MSC_VER)
+#pragma pack(1)
+#endif
+
 typedef struct
 {
 	INT16 width;          // bounding box size
@@ -670,7 +680,7 @@ typedef struct
 	INT16 topoffset;      // pixels below the origin
 	INT32 columnofs[8];     // only [width] used
 	// the [0] is &columnofs[width]
-} ATTRPACK patch_t;
+} ATTRPACK softwarepatch_t;
 
 #ifdef _MSC_VER
 #pragma warning(disable :  4200)
diff --git a/src/r_main.c b/src/r_main.c
index 4f79dd8db4..b70e6f25f5 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -1572,14 +1572,6 @@ void R_InitHardwareMode(void)
 }
 #endif
 
-void R_ReloadHUDGraphics(void)
-{
-	CONS_Debug(DBG_RENDER, "R_ReloadHUDGraphics()...\n");
-	ST_LoadGraphics();
-	HU_LoadGraphics();
-	ST_ReloadSkinFaceGraphics();
-}
-
 // =========================================================================
 //                    ENGINE COMMANDS & VARS
 // =========================================================================
diff --git a/src/r_main.h b/src/r_main.h
index 729ec6973d..cc098462d8 100644
--- a/src/r_main.h
+++ b/src/r_main.h
@@ -115,7 +115,6 @@ void R_Init(void);
 #ifdef HWRENDER
 void R_InitHardwareMode(void);
 #endif
-void R_ReloadHUDGraphics(void);
 
 void R_CheckViewMorph(void);
 void R_ApplyViewMorph(void);
diff --git a/src/r_patch.c b/src/r_patch.c
index 8980eda582..783c1fbec0 100644
--- a/src/r_patch.c
+++ b/src/r_patch.c
@@ -50,6 +50,98 @@
 
 static unsigned char imgbuf[1<<26];
 
+//
+// Creates a patch.
+// Assumes a PU_PATCH zone memory tag and no user, but can always be set later
+//
+
+patch_t *Patch_Create(softwarepatch_t *source, size_t srcsize, void *dest)
+{
+	patch_t *patch = (dest == NULL) ? Z_Calloc(sizeof(patch_t), PU_PATCH, NULL) : (patch_t *)(dest);
+
+	if (source)
+	{
+		INT32 col, colsize;
+		size_t size = sizeof(INT32) * source->width;
+		size_t offs = (sizeof(INT16) * 4) + size;
+
+		patch->width      = source->width;
+		patch->height     = source->height;
+		patch->leftoffset = source->leftoffset;
+		patch->topoffset  = source->topoffset;
+		patch->columnofs  = Z_Calloc(size, PU_PATCH, NULL);
+
+		for (col = 0; col < source->width; col++)
+		{
+			// This makes the column offsets relative to the column data itself,
+			// instead of the entire patch data
+			patch->columnofs[col] = LONG(source->columnofs[col]) - offs;
+		}
+
+		if (!srcsize)
+			I_Error("R_CreatePatch: no source size!");
+
+		colsize = (INT32)(srcsize) - (INT32)offs;
+		if (colsize <= 0)
+			I_Error("R_CreatePatch: no column data!");
+
+		patch->columns = Z_Calloc(colsize, PU_PATCH, NULL);
+		M_Memcpy(patch->columns, ((UINT8 *)source + LONG(source->columnofs[0])), colsize);
+	}
+
+	if (patch->hardware)
+		I_Error("wtf?\n");
+
+	return patch;
+}
+
+//
+// Frees a patch from memory.
+//
+
+void Patch_Free(patch_t *patch)
+{
+#ifdef HWRENDER
+	if (patch->hardware)
+		HWR_FreeTexture(patch);
+#endif
+
+	if (patch->columnofs)
+		Z_Free(patch->columnofs);
+	if (patch->columns)
+		Z_Free(patch->columns);
+
+	Z_Free(patch);
+}
+
+#ifdef HWRENDER
+//
+// Allocates a hardware patch.
+//
+
+void *Patch_AllocateHardwarePatch(patch_t *patch)
+{
+	if (!patch->hardware)
+	{
+		GLPatch_t *grPatch = Z_Calloc(sizeof(GLPatch_t), PU_HWRPATCHINFO, &patch->hardware);
+		grPatch->mipmap = Z_Calloc(sizeof(GLMipmap_t), PU_HWRPATCHINFO, &grPatch->mipmap);
+	}
+	return (void *)(patch->hardware);
+}
+
+//
+// Creates a hardware patch.
+//
+
+void *Patch_CreateGL(patch_t *patch)
+{
+	GLPatch_t *grPatch = (GLPatch_t *)Patch_AllocateHardwarePatch(patch);
+	if (!grPatch->mipmap->data) // Run HWR_MakePatch in all cases, to recalculate some things
+		HWR_MakePatch(patch, grPatch, grPatch->mipmap, false);
+	return grPatch;
+}
+#endif // HWRENDER
+
 //
 // R_CheckIfPatch
 //
@@ -177,7 +269,7 @@ void R_PatchToFlat(patch_t *patch, UINT8 *flat)
 	for (col = 0; col < SHORT(patch->width); col++, desttop++)
 	{
 		INT32 topdelta, prevdelta = -1;
-		column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[col]));
+		column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[col]));
 
 		while (column->topdelta != 0xff)
 		{
@@ -220,7 +312,7 @@ void R_PatchToMaskedFlat(patch_t *patch, UINT16 *raw, boolean flip)
 	for (col = 0; col < SHORT(patch->width); col++, desttop++)
 	{
 		INT32 topdelta, prevdelta = -1;
-		column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[flip ? (patch->width-1-col) : col]));
+		column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[flip ? (patch->width-1-col) : col]));
 		while (column->topdelta != 0xff)
 		{
 			topdelta = column->topdelta;
@@ -244,7 +336,7 @@ void R_PatchToMaskedFlat(patch_t *patch, UINT16 *raw, boolean flip)
 //
 // Convert a flat to a patch.
 //
-patch_t *R_FlatToPatch(UINT8 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize, boolean transparency)
+softwarepatch_t *R_FlatToPatch(UINT8 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize, boolean transparency)
 {
 	UINT32 x, y;
 	UINT8 *img;
@@ -350,7 +442,7 @@ patch_t *R_FlatToPatch(UINT8 *raw, UINT16 width, UINT16 height, UINT16 leftoffse
 
 	if (destsize != NULL)
 		*destsize = size;
-	return (patch_t *)img;
+	return (softwarepatch_t *)img;
 }
 
 //
@@ -359,7 +451,7 @@ patch_t *R_FlatToPatch(UINT8 *raw, UINT16 width, UINT16 height, UINT16 leftoffse
 // Convert a masked flat to a patch.
 // Explanation of "masked" flats in R_PatchToMaskedFlat.
 //
-patch_t *R_MaskedFlatToPatch(UINT16 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize)
+softwarepatch_t *R_MaskedFlatToPatch(UINT16 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize)
 {
 	UINT32 x, y;
 	UINT8 *img;
@@ -464,7 +556,7 @@ patch_t *R_MaskedFlatToPatch(UINT16 *raw, UINT16 width, UINT16 height, UINT16 le
 
 	if (destsize != NULL)
 		*destsize = size;
-	return (patch_t *)img;
+	return (softwarepatch_t *)img;
 }
 
 //
@@ -736,7 +828,7 @@ UINT8 *R_PNGToFlat(UINT16 *width, UINT16 *height, UINT8 *png, size_t size)
 //
 // Convert a PNG to a patch.
 //
-patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize)
+softwarepatch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize)
 {
 	UINT16 width, height;
 	INT16 topoffset = 0, leftoffset = 0;
@@ -981,7 +1073,7 @@ static void R_ParseSpriteInfo(boolean spr2)
 
 #ifdef ROTSPRITE
 	if ((sprites != NULL) && (!spr2))
-		R_FreeSingleRotSprite(&sprites[sprnum]);
+		R_FreeRotSprite(&sprites[sprnum]);
 #endif
 
 	// Left Curly Brace
@@ -1143,10 +1235,10 @@ static UINT16 GetPatchPixel(patch_t *patch, INT32 x, INT32 y, boolean flip)
 	column_t *column;
 	UINT8 *source;
 
-	if (x >= 0 && x < SHORT(patch->width))
+	if (x >= 0 && x < patch->width)
 	{
 		INT32 topdelta, prevdelta = -1;
-		column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[flip ? (patch->width-1-x) : x]));
+		column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[flip ? (patch->width-1-x) : x]));
 		while (column->topdelta != 0xff)
 		{
 			topdelta = column->topdelta;
@@ -1194,8 +1286,8 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp
 {
 	UINT32 i;
 	INT32 angle;
-	patch_t *patch;
-	patch_t *newpatch;
+	patch_t *patch, *newpatch;
+	softwarepatch_t *swpatch;
 	UINT16 *rawdst;
 	size_t size;
 	INT32 bflip = (flip != 0x00);
@@ -1212,28 +1304,14 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp
 		INT32 width, height, leftoffset;
 		fixed_t ca, sa;
 		lumpnum_t lump = sprframe->lumppat[rot];
-#ifndef NO_PNG_LUMPS
-		size_t lumplength;
-#endif
 
 		if (lump == LUMPERROR)
 			return;
 
-		patch = (patch_t *)W_CacheLumpNum(lump, PU_STATIC);
-#ifndef NO_PNG_LUMPS
-		lumplength = W_LumpLength(lump);
-
-		if (R_IsLumpPNG((UINT8 *)patch, lumplength))
-			patch = R_PNGToPatch((UINT8 *)patch, lumplength, NULL);
-		else
-#endif
-		// Because there's something wrong with SPR_DFLM, I guess
-		if (!R_CheckIfPatch(lump))
-			return;
-
-		width = SHORT(patch->width);
-		height = SHORT(patch->height);
-		leftoffset = SHORT(patch->leftoffset);
+		patch = (patch_t *)W_CachePatchNum(lump, PU_STATIC);
+		width = patch->width;
+		height = patch->height;
+		leftoffset = patch->leftoffset;
 
 		// rotation pivot
 		px = SPRITE_XCENTER;
@@ -1345,37 +1423,33 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp
 			}
 
 			// make patch
-			newpatch = R_MaskedFlatToPatch(rawdst, newwidth, newheight, 0, 0, &size);
+			swpatch = R_MaskedFlatToPatch(rawdst, newwidth, newheight, 0, 0, &size);
 			{
-				newpatch->leftoffset = (newpatch->width / 2) + (leftoffset - px);
-				newpatch->topoffset = (newpatch->height / 2) + (SHORT(patch->topoffset) - py);
+				swpatch->leftoffset = (swpatch->width / 2) + (leftoffset - px);
+				swpatch->topoffset = (swpatch->height / 2) + (patch->topoffset - py);
 			}
 
 			//BP: we cannot use special tric in hardware mode because feet in ground caused by z-buffer
 			if (rendermode != render_none) // not for psprite
-				newpatch->topoffset += FEETADJUST>>FRACBITS;
+				swpatch->topoffset += FEETADJUST>>FRACBITS;
 
 			// P_PrecacheLevel
 			if (devparm) spritememory += size;
 
 			// convert everything to little-endian, for big-endian support
-			newpatch->width = SHORT(newpatch->width);
-			newpatch->height = SHORT(newpatch->height);
-			newpatch->leftoffset = SHORT(newpatch->leftoffset);
-			newpatch->topoffset = SHORT(newpatch->topoffset);
+			swpatch->width = SHORT(swpatch->width);
+			swpatch->height = SHORT(swpatch->height);
+			swpatch->leftoffset = SHORT(swpatch->leftoffset);
+			swpatch->topoffset = SHORT(swpatch->topoffset);
+
+			newpatch = Patch_Create(swpatch, size, NULL);
 
 #ifdef HWRENDER
 			if (rendermode == render_opengl)
-			{
-				GLPatch_t *grPatch = Z_Calloc(sizeof(GLPatch_t), PU_HWRPATCHINFO, NULL);
-				grPatch->mipmap = Z_Calloc(sizeof(GLMipmap_t), PU_HWRPATCHINFO, NULL);
-				grPatch->rawpatch = newpatch;
-				sprframe->rotsprite.patch[rot][angle] = (patch_t *)grPatch;
-				HWR_MakePatch(newpatch, grPatch, grPatch->mipmap, false);
-			}
-			else
-#endif // HWRENDER
-				sprframe->rotsprite.patch[rot][angle] = newpatch;
+				Patch_CreateGL(newpatch);
+#endif
+
+			sprframe->rotsprite.patch[rot][angle] = newpatch;
 
 			// free rotated image data
 			Z_Free(rawdst);
@@ -1386,6 +1460,7 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp
 
 		// free image data
 		Z_Free(patch);
+		Z_Free(swpatch);
 	}
 #undef SPRITE_XCENTER
 #undef SPRITE_YCENTER
@@ -1394,11 +1469,11 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp
 }
 
 //
-// R_FreeSingleRotSprite
+// R_FreeRotSprite
 //
 // Free sprite rotation data from memory, for a single spritedef.
 //
-void R_FreeSingleRotSprite(spritedef_t *spritedef)
+void R_FreeRotSprite(spritedef_t *spritedef)
 {
 	UINT8 frame;
 	INT32 rot, ang;
@@ -1414,30 +1489,8 @@ void R_FreeSingleRotSprite(spritedef_t *spritedef)
 				{
 					patch_t *rotsprite = sprframe->rotsprite.patch[rot][ang];
 					if (rotsprite)
-					{
-#ifdef HWRENDER
-						if (rendermode == render_opengl)
-						{
-							GLPatch_t *grPatch = (GLPatch_t *)rotsprite;
-							if (grPatch->rawpatch)
-							{
-								Z_Free(grPatch->rawpatch);
-								grPatch->rawpatch = NULL;
-							}
-							if (grPatch->mipmap)
-							{
-								if (grPatch->mipmap->data)
-								{
-									Z_Free(grPatch->mipmap->data);
-									grPatch->mipmap->data = NULL;
-								}
-								Z_Free(grPatch->mipmap);
-								grPatch->mipmap = NULL;
-							}
-						}
-#endif
-						Z_Free(rotsprite);
-					}
+						Patch_Free(rotsprite);
+					rotsprite = NULL;
 				}
 				sprframe->rotsprite.cached &= ~(1<<rot);
 			}
@@ -1449,7 +1502,7 @@ void R_FreeSingleRotSprite(spritedef_t *spritedef)
 // R_FreeSkinRotSprite
 //
 // Free sprite rotation data from memory, for a skin.
-// Calls R_FreeSingleRotSprite.
+// Calls R_FreeRotSprite.
 //
 void R_FreeSkinRotSprite(size_t skinnum)
 {
@@ -1458,23 +1511,8 @@ void R_FreeSkinRotSprite(size_t skinnum)
 	spritedef_t *skinsprites = skin->sprites;
 	for (i = 0; i < NUMPLAYERSPRITES*2; i++)
 	{
-		R_FreeSingleRotSprite(skinsprites);
+		R_FreeRotSprite(skinsprites);
 		skinsprites++;
 	}
 }
-
-//
-// R_FreeAllRotSprite
-//
-// Free ALL sprite rotation data from memory.
-//
-void R_FreeAllRotSprite(void)
-{
-	INT32 i;
-	size_t s;
-	for (s = 0; s < numsprites; s++)
-		R_FreeSingleRotSprite(&sprites[s]);
-	for (i = 0; i < numskins; ++i)
-		R_FreeSkinRotSprite(i);
-}
 #endif
diff --git a/src/r_patch.h b/src/r_patch.h
index a2db6320ca..de4981fba2 100644
--- a/src/r_patch.h
+++ b/src/r_patch.h
@@ -37,21 +37,30 @@ typedef struct
 	boolean available;
 } spriteinfo_t;
 
+// Patch functions
+patch_t *Patch_Create(softwarepatch_t *source, size_t srcsize, void *dest);
+void Patch_Free(patch_t *patch);
+
+#ifdef HWRENDER
+void *Patch_AllocateHardwarePatch(patch_t *patch);
+void *Patch_CreateGL(patch_t *patch);
+#endif
+
 // Conversions between patches / flats / textures...
 boolean R_CheckIfPatch(lumpnum_t lump);
 void R_TextureToFlat(size_t tex, UINT8 *flat);
 void R_PatchToFlat(patch_t *patch, UINT8 *flat);
 void R_PatchToMaskedFlat(patch_t *patch, UINT16 *raw, boolean flip);
-patch_t *R_FlatToPatch(UINT8 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize, boolean transparency);
-patch_t *R_MaskedFlatToPatch(UINT16 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize);
+softwarepatch_t *R_FlatToPatch(UINT8 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize, boolean transparency);
+softwarepatch_t *R_MaskedFlatToPatch(UINT16 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize);
 
-// Portable Network Graphics
+// PNGs
 boolean R_IsLumpPNG(const UINT8 *d, size_t s);
 #define W_ThrowPNGError(lumpname, wadfilename) I_Error("W_Wad: Lump \"%s\" in file \"%s\" is a .png - please convert to either Doom or Flat (raw) image format.", lumpname, wadfilename); // Fears Of LJ Sonic
 
 #ifndef NO_PNG_LUMPS
 UINT8 *R_PNGToFlat(UINT16 *width, UINT16 *height, UINT8 *png, size_t size);
-patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize);
+softwarepatch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize);
 boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size);
 #endif
 
@@ -64,11 +73,10 @@ void R_ParseSPRTINFOLump(UINT16 wadNum, UINT16 lumpNum);
 #ifdef ROTSPRITE
 INT32 R_GetRollAngle(angle_t rollangle);
 void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, spriteframe_t *sprframe, INT32 rot, UINT8 flip);
-void R_FreeSingleRotSprite(spritedef_t *spritedef);
+void R_FreeRotSprite(spritedef_t *spritedef);
 void R_FreeSkinRotSprite(size_t skinnum);
 extern fixed_t rollcosang[ROTANGLES];
 extern fixed_t rollsinang[ROTANGLES];
-void R_FreeAllRotSprite(void);
 #endif
 
 #endif // __R_PATCH__
diff --git a/src/r_segs.c b/src/r_segs.c
index d9fc758381..1778278927 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -217,7 +217,7 @@ static void R_DrawWallSplats(void)
 				continue;
 
 			// draw the texture
-			col = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[texturecolumn]));
+			col = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[texturecolumn]));
 			R_DrawSplatColumn(col);
 		}
 	} // next splat
diff --git a/src/r_things.c b/src/r_things.c
index c0795acd5a..0636a880d8 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -228,7 +228,7 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16
 	UINT8 frame;
 	UINT8 rotation;
 	lumpinfo_t *lumpinfo;
-	patch_t patch;
+	softwarepatch_t patch;
 	UINT8 numadded = 0;
 
 	memset(sprtemp,0xFF, sizeof (sprtemp));
@@ -241,7 +241,7 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16
 	if (spritedef->numframes) // (then spriteframes is not null)
 	{
 #ifdef ROTSPRITE
-		R_FreeSingleRotSprite(spritedef);
+		R_FreeRotSprite(spritedef);
 #endif
 		// copy the already defined sprite frames
 		M_Memcpy(sprtemp, spritedef->spriteframes,
@@ -277,7 +277,7 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16
 			W_ReadLumpHeaderPwad(wadnum, l, &patch, sizeof (patch_t), 0);
 #ifndef NO_PNG_LUMPS
 			{
-				patch_t *png = W_CacheLumpNumPwad(wadnum, l, PU_STATIC);
+				softwarepatch_t *png = W_CacheLumpNumPwad(wadnum, l, PU_STATIC);
 				size_t len = W_LumpLengthPwad(wadnum, l);
 				// lump is a png so convert it
 				if (R_IsLumpPNG((UINT8 *)png, len))
@@ -398,7 +398,7 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16
 		spritedef->numframes < maxframe)   // more frames are defined ?
 	{
 #ifdef ROTSPRITE
-		R_FreeSingleRotSprite(spritedef);
+		R_FreeRotSprite(spritedef);
 #endif
 		Z_Free(spritedef->spriteframes);
 		spritedef->spriteframes = NULL;
@@ -910,7 +910,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
 			sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale));
 			dc_iscale = (0xffffffffu / (unsigned)spryscale);
 
-			column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[texturecolumn]));
+			column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[texturecolumn]));
 
 			localcolfunc (column);
 		}
@@ -928,9 +928,9 @@ static void R_DrawVisSprite(vissprite_t *vis)
 			texturecolumn = frac>>FRACBITS;
 			if (texturecolumn < 0 || texturecolumn >= pwidth)
 				I_Error("R_DrawSpriteRange: bad texturecolumn at %d from end", vis->x2 - dc_x);
-			column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[texturecolumn]));
+			column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[texturecolumn]));
 #else
-			column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[frac>>FRACBITS]));
+			column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[frac>>FRACBITS]));
 #endif
 			localcolfunc (column);
 		}
@@ -995,9 +995,9 @@ static void R_DrawPrecipitationVisSprite(vissprite_t *vis)
 		if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width))
 			I_Error("R_DrawPrecipitationSpriteRange: bad texturecolumn");
 
-		column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[texturecolumn]));
+		column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[texturecolumn]));
 #else
-		column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[frac>>FRACBITS]));
+		column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[frac>>FRACBITS]));
 #endif
 		R_DrawMaskedColumn(column);
 	}
diff --git a/src/screen.c b/src/screen.c
index e7ff9e7355..f5d19cada1 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -202,9 +202,6 @@ void SCR_SetMode(void)
 	// Lactozilla: Renderer switching
 	if (setrenderneeded)
 	{
-		Z_PreparePatchFlush();
-		needpatchflush = true;
-		needpatchrecache = true;
 		VID_CheckRenderer();
 		if (!setmodeneeded)
 			VID_SetMode(vid.modenum);
diff --git a/src/sdl/hwsym_sdl.c b/src/sdl/hwsym_sdl.c
index 416c8d2f55..e1845fac8f 100644
--- a/src/sdl/hwsym_sdl.c
+++ b/src/sdl/hwsym_sdl.c
@@ -86,6 +86,7 @@ void *hwSym(const char *funcName,void *handle)
 	GETFUNC(ClearBuffer);
 	GETFUNC(SetTexture);
 	GETFUNC(UpdateTexture);
+	GETFUNC(DeleteTexture);
 	GETFUNC(ReadRect);
 	GETFUNC(GClipRect);
 	GETFUNC(ClearMipMapCache);
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index 01194a02fa..1bb48e4681 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -1660,7 +1660,7 @@ static void Impl_SetWindowName(const char *title)
 static void Impl_SetWindowIcon(void)
 {
 	if (window && icoSurface)
-		SDL_SetWindowIcon(window, icoSurface);	
+		SDL_SetWindowIcon(window, icoSurface);
 }
 
 static void Impl_VideoSetupSDLBuffer(void)
@@ -1770,7 +1770,7 @@ void I_StartupGraphics(void)
 	// Window icon
 #ifdef HAVE_IMAGE
 	icoSurface = IMG_ReadXPMFromArray(SDL_icon_xpm);
-#endif	
+#endif
 
 	// Fury: we do window initialization after GL setup to allow
 	// SDL_GL_LoadLibrary to work well on Windows
@@ -1836,6 +1836,7 @@ void VID_StartupOpenGL(void)
 		HWD.pfnClearBuffer      = hwSym("ClearBuffer",NULL);
 		HWD.pfnSetTexture       = hwSym("SetTexture",NULL);
 		HWD.pfnUpdateTexture    = hwSym("UpdateTexture",NULL);
+		HWD.pfnDeleteTexture    = hwSym("DeleteTexture",NULL);
 		HWD.pfnReadRect         = hwSym("ReadRect",NULL);
 		HWD.pfnGClipRect        = hwSym("GClipRect",NULL);
 		HWD.pfnClearMipMapCache = hwSym("ClearMipMapCache",NULL);
diff --git a/src/st_stuff.c b/src/st_stuff.c
index d5aa5fbac8..f6ba94fccd 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -2755,9 +2755,6 @@ 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/v_video.c b/src/v_video.c
index b88c4838bc..7f07852fa8 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -531,7 +531,7 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca
 	//if (rendermode != render_soft && !con_startup)		// Why?
 	if (rendermode == render_opengl)
 	{
-		HWR_DrawStretchyFixedPatch((GLPatch_t *)patch, x, y, pscale, vscale, scrn, colormap);
+		HWR_DrawStretchyFixedPatch(patch, x, y, pscale, vscale, scrn, colormap);
 		return;
 	}
 #endif
@@ -716,7 +716,7 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca
 			// if it's meant to cover the whole screen, black out the rest (ONLY IF TOP LEFT ISN'T TRANSPARENT)
 			if (x == 0 && SHORT(patch->width) == BASEVIDWIDTH && y == 0 && SHORT(patch->height) == BASEVIDHEIGHT)
 			{
-				column = (const column_t *)((const UINT8 *)(patch) + LONG(patch->columnofs[0]));
+				column = (const column_t *)((const UINT8 *)(patch->columns) + (patch->columnofs[0]));
 				if (!column->topdelta)
 				{
 					source = (const UINT8 *)(column) + 3;
@@ -784,7 +784,7 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca
 			if (x+offx >= vid.width) // don't draw off the right of the screen (WRAP PREVENTION)
 				break;
 		}
-		column = (const column_t *)((const UINT8 *)(patch) + LONG(patch->columnofs[col>>FRACBITS]));
+		column = (const column_t *)((const UINT8 *)(patch->columns) + (patch->columnofs[col>>FRACBITS]));
 
 		while (column->topdelta != 0xff)
 		{
@@ -831,7 +831,7 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_
 	//if (rendermode != render_soft && !con_startup)		// Not this again
 	if (rendermode == render_opengl)
 	{
-		HWR_DrawCroppedPatch((GLPatch_t*)patch,x,y,pscale,scrn,sx,sy,w,h);
+		HWR_DrawCroppedPatch(patch,x,y,pscale,scrn,sx,sy,w,h);
 		return;
 	}
 #endif
@@ -1007,7 +1007,7 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_
 			continue;
 		if (x >= vid.width) // don't draw off the right of the screen (WRAP PREVENTION)
 			break;
-		column = (const column_t *)((const UINT8 *)(patch) + LONG(patch->columnofs[col>>FRACBITS]));
+		column = (const column_t *)((const UINT8 *)(patch->columns) + (patch->columnofs[col>>FRACBITS]));
 
 		while (column->topdelta != 0xff)
 		{
diff --git a/src/w_wad.c b/src/w_wad.c
index e4a19f30e3..f40f4eb4a8 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -836,11 +836,6 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup)
 	Z_Calloc(numlumps * sizeof (*wadfile->lumpcache), PU_STATIC, &wadfile->lumpcache);
 	Z_Calloc(numlumps * sizeof (*wadfile->patchcache), PU_STATIC, &wadfile->patchcache);
 
-#ifdef HWRENDER
-	// allocates GLPatch info structures and store them in a tree
-	wadfile->hwrcache = M_AATreeAlloc(AATREE_ZUSER);
-#endif
-
 	//
 	// add the wadfile
 	//
@@ -1676,13 +1671,7 @@ void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag)
 	if (!lumpcache[lump])
 	{
 		size_t len = W_LumpLengthPwad(wad, lump);
-		void *ptr, *lumpdata;
-#ifndef NO_PNG_LUMPS
-		void *srcdata = NULL;
-#endif
-
-		ptr = Z_Malloc(len, tag, &lumpcache[lump]);
-		lumpdata = Z_Malloc(len, tag, NULL);
+		void *ptr, *dest, *lumpdata = Z_Malloc(len, PU_STATIC, NULL);
 
 		// read the lump in full
 		W_ReadLumpHeaderPwad(wad, lump, lumpdata, 0, 0);
@@ -1692,14 +1681,25 @@ void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag)
 		if (R_IsLumpPNG((UINT8 *)lumpdata, len))
 		{
 			size_t newlen;
-			srcdata = R_PNGToPatch((UINT8 *)lumpdata, len, &newlen);
-			ptr = Z_Realloc(ptr, newlen, tag, &lumpcache[lump]);
-			M_Memcpy(ptr, srcdata, newlen);
-			Z_Free(srcdata);
+			void *converted = R_PNGToPatch((UINT8 *)lumpdata, len, &newlen);
+			ptr = Z_Malloc(newlen, PU_STATIC, NULL);
+			M_Memcpy(ptr, converted, newlen);
+			Z_Free(converted);
+			len = newlen;
 		}
 		else // just copy it into the patch cache
 #endif
+		{
+			ptr = Z_Malloc(len, PU_STATIC, NULL);
 			M_Memcpy(ptr, lumpdata, len);
+		}
+
+		Z_Free(lumpdata);
+
+		dest = Z_Calloc(sizeof(patch_t), tag, &lumpcache[lump]);
+		Patch_Create(ptr, len, dest);
+
+		Z_Free(ptr);
 	}
 	else
 		Z_ChangeTag(lumpcache[lump], tag);
@@ -1714,45 +1714,22 @@ void *W_CacheSoftwarePatchNum(lumpnum_t lumpnum, INT32 tag)
 
 void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag)
 {
-#ifdef HWRENDER
-	GLPatch_t *grPatch;
-#endif
+	patch_t *patch;
 
 	if (!TestValidLump(wad, lump))
 		return NULL;
 
+	patch = W_CacheSoftwarePatchNumPwad(wad, lump, tag);
+
 #ifdef HWRENDER
 	// Software-only compile cache the data without conversion
 	if (rendermode == render_soft || rendermode == render_none)
 #endif
-	{
-		return W_CacheSoftwarePatchNumPwad(wad, lump, tag);
-	}
-#ifdef HWRENDER
-
-	grPatch = HWR_GetCachedGLPatchPwad(wad, lump);
-
-	if (grPatch->mipmap->data)
-	{
-		if (tag == PU_CACHE)
-			tag = PU_HWRCACHE;
-		Z_ChangeTag(grPatch->mipmap->data, tag);
-	}
-	else
-	{
-		patch_t *ptr = NULL;
+		return (void *)patch;
 
-		// Only load the patch if we haven't initialised the grPatch yet
-		if (grPatch->mipmap->width == 0)
-			ptr = W_CacheLumpNumPwad(grPatch->wadnum, grPatch->lumpnum, PU_STATIC);
-
-		// Run HWR_MakePatch in all cases, to recalculate some things
-		HWR_MakePatch(ptr, grPatch, grPatch->mipmap, false);
-		Z_Free(ptr);
-	}
-
-	// return GLPatch_t, which can be casted to (patch_t) with valid patch header info
-	return (void *)grPatch;
+#ifdef HWRENDER
+	Patch_CreateGL(patch);
+	return (void *)patch;
 #endif
 }
 
@@ -1767,7 +1744,7 @@ void W_UnlockCachedPatch(void *patch)
 	// have different lifetimes from software's.
 #ifdef HWRENDER
 	if (rendermode == render_opengl)
-		HWR_UnlockCachedPatch((GLPatch_t*)patch);
+		HWR_UnlockCachedPatch((GLPatch_t *)((patch_t *)patch)->hardware);
 	else
 #endif
 		Z_Unlock(patch);
diff --git a/src/w_wad.h b/src/w_wad.h
index fddc65529d..ee3df9cf4e 100644
--- a/src/w_wad.h
+++ b/src/w_wad.h
@@ -102,10 +102,6 @@ virtlump_t* vres_Find(const virtres_t*, const char*);
 
 #define lumpcache_t void *
 
-#ifdef HWRENDER
-#include "m_aatree.h"
-#endif
-
 // Resource type of the WAD. Yeah, I know this sounds dumb, but I'll leave it like this until I clean up the code further.
 typedef enum restype
 {
@@ -123,9 +119,6 @@ typedef struct wadfile_s
 	lumpinfo_t *lumpinfo;
 	lumpcache_t *lumpcache;
 	lumpcache_t *patchcache;
-#ifdef HWRENDER
-	aatree_t *hwrcache; // patches are cached in renderer's native format
-#endif
 	UINT16 numlumps; // this wad's number of resources
 	FILE *handle;
 	UINT32 filesize; // for network
diff --git a/src/win32/win_dll.c b/src/win32/win_dll.c
index 3f6c5e2902..54526975ef 100644
--- a/src/win32/win_dll.c
+++ b/src/win32/win_dll.c
@@ -107,6 +107,7 @@ static loadfunc_t hwdFuncTable[] = {
 	{"ClearBuffer@12",      &hwdriver.pfnClearBuffer},
 	{"SetTexture@4",        &hwdriver.pfnSetTexture},
 	{"UpdateTexture@4",     &hwdriver.pfnUpdateTexture},
+	{"DeleteTexture@4",     &hwdriver.pfnDeleteTexture},
 	{"ReadRect@24",         &hwdriver.pfnReadRect},
 	{"GClipRect@20",        &hwdriver.pfnGClipRect},
 	{"ClearMipMapCache@0",  &hwdriver.pfnClearMipMapCache},
@@ -139,6 +140,7 @@ static loadfunc_t hwdFuncTable[] = {
 	{"ClearBuffer",         &hwdriver.pfnClearBuffer},
 	{"SetTexture",          &hwdriver.pfnSetTexture},
 	{"UpdateTexture",       &hwdriver.pfnUpdateTexture},
+	{"DeleteTexture",       &hwdriver.pfnDeleteTexture},
 	{"ReadRect",            &hwdriver.pfnReadRect},
 	{"GClipRect",           &hwdriver.pfnGClipRect},
 	{"ClearMipMapCache",    &hwdriver.pfnClearMipMapCache},
diff --git a/src/y_inter.c b/src/y_inter.c
index d857d60dc0..4810ce7fa5 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -158,7 +158,6 @@ typedef struct
 
 boolean usebuffer = false;
 static boolean useinterpic;
-static boolean safetorender = true;
 static y_buffer_t *y_buffer;
 
 static INT32 intertic;
@@ -175,7 +174,6 @@ static void Y_CalculateCompetitionWinners(void);
 static void Y_CalculateTimeRaceWinners(void);
 static void Y_CalculateMatchWinners(void);
 static void Y_UnloadData(void);
-static void Y_CleanupData(void);
 
 // Stuff copy+pasted from st_stuff.c
 #define ST_DrawNumFromHud(h,n)        V_DrawTallNum(hudinfo[h].x, hudinfo[h].y, hudinfo[h].f, n)
@@ -322,19 +320,6 @@ void Y_IntermissionDrawer(void)
 	if (intertype == int_none || rendermode == render_none)
 		return;
 
-	// Lactozilla: Renderer switching
-	if (needpatchrecache)
-	{
-		Y_CleanupData();
-		safetorender = false;
-	}
-
-	if (!safetorender)
-		V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
-
-	if (!safetorender)
-		goto dontdrawbg;
-
 	if (useinterpic)
 		V_DrawScaledPatch(0, 0, 0, interpic);
 	else if (!usetile)
@@ -372,7 +357,6 @@ void Y_IntermissionDrawer(void)
 	if (!LUA_HudEnabled(hud_intermissiontally))
 		goto skiptallydrawer;
 
-dontdrawbg:
 	if (intertype == int_coop)
 	{
 		INT32 bonusy;
@@ -422,17 +406,14 @@ dontdrawbg:
 
 		bonusy = 150;
 		// Total
-		if (safetorender)
-		{
-			V_DrawScaledPatch(152, bonusy, 0, data.coop.ptotal);
-			V_DrawTallNum(BASEVIDWIDTH - 68, bonusy + 1, 0, data.coop.total);
-		}
+		V_DrawScaledPatch(152, bonusy, 0, data.coop.ptotal);
+		V_DrawTallNum(BASEVIDWIDTH - 68, bonusy + 1, 0, data.coop.total);
 		bonusy -= (3*SHORT(tallnum[0]->height)/2) + 1;
 
 		// Draw bonuses
 		for (i = 3; i >= 0; --i)
 		{
-			if (data.coop.bonuses[i].display && safetorender)
+			if (data.coop.bonuses[i].display)
 			{
 				V_DrawScaledPatch(152, bonusy, 0, data.coop.bonuspatches[i]);
 				V_DrawTallNum(BASEVIDWIDTH - 68, bonusy + 1, 0, data.coop.bonuses[i].points);
@@ -661,8 +642,7 @@ dontdrawbg:
 		char strtime[10];
 
 		// draw the header
-		if (safetorender)
-			V_DrawScaledPatch(112, 2, 0, data.match.result);
+		V_DrawScaledPatch(112, 2, 0, data.match.result);
 
 		// draw the level name
 		V_DrawCenteredString(BASEVIDWIDTH/2, 20, 0, data.match.levelstring);
@@ -1218,8 +1198,6 @@ void Y_StartIntermission(void)
 		I_Error("endtic is dirty");
 #endif
 
-	safetorender = true;
-
 	if (!multiplayer)
 	{
 		timer = 0;
@@ -2067,7 +2045,6 @@ void Y_EndIntermission(void)
 }
 
 #define UNLOAD(x) if (x) {Z_ChangeTag(x, PU_CACHE);} x = NULL;
-#define CLEANUP(x) x = NULL;
 
 //
 // Y_UnloadData
@@ -2118,45 +2095,3 @@ static void Y_UnloadData(void)
 			break;
 	}
 }
-
-static void Y_CleanupData(void)
-{
-	// unload the background patches
-	CLEANUP(bgpatch);
-	CLEANUP(bgtile);
-	CLEANUP(interpic);
-
-	switch (intertype)
-	{
-		case int_coop:
-			// unload the coop and single player patches
-			CLEANUP(data.coop.bonuspatches[3]);
-			CLEANUP(data.coop.bonuspatches[2]);
-			CLEANUP(data.coop.bonuspatches[1]);
-			CLEANUP(data.coop.bonuspatches[0]);
-			CLEANUP(data.coop.ptotal);
-			break;
-		case int_spec:
-			// unload the special stage patches
-			//CLEANUP(data.spec.cemerald);
-			//CLEANUP(data.spec.nowsuper);
-			CLEANUP(data.spec.bonuspatches[1]);
-			CLEANUP(data.spec.bonuspatches[0]);
-			CLEANUP(data.spec.pscore);
-			CLEANUP(data.spec.pcontinues);
-			break;
-		case int_match:
-		case int_race:
-			CLEANUP(data.match.result);
-			break;
-		case int_ctf:
-			CLEANUP(data.match.blueflag);
-			CLEANUP(data.match.redflag);
-			break;
-		default:
-			//without this default,
-			//int_none, int_tag, int_chaos, and int_classicrace
-			//are not handled
-			break;
-	}
-}
diff --git a/src/z_zone.c b/src/z_zone.c
index 2387a11433..7a6dfe040a 100644
--- a/src/z_zone.c
+++ b/src/z_zone.c
@@ -499,33 +499,6 @@ void Z_FreeTags(INT32 lowtag, INT32 hightag)
 // Utility functions
 // -----------------
 
-// for renderer switching
-boolean needpatchflush = false;
-boolean needpatchrecache = false;
-
-// flush all patches from memory
-void Z_FlushCachedPatches(void)
-{
-	CONS_Debug(DBG_RENDER, "Z_FlushCachedPatches()...\n");
-	Z_FreeTag(PU_PATCH);
-	Z_FreeTag(PU_HUDGFX);
-	Z_FreeTag(PU_HWRPATCHINFO);
-	Z_FreeTag(PU_HWRMODELTEXTURE);
-	Z_FreeTag(PU_HWRCACHE);
-	Z_FreeTag(PU_HWRCACHE_UNLOCKED);
-	Z_FreeTag(PU_HWRPATCHINFO_UNLOCKED);
-	Z_FreeTag(PU_HWRMODELTEXTURE_UNLOCKED);
-}
-
-// happens before a renderer switch
-void Z_PreparePatchFlush(void)
-{
-	CONS_Debug(DBG_RENDER, "Z_PreparePatchFlush()...\n");
-#ifdef ROTSPRITE
-	R_FreeAllRotSprite();
-#endif
-}
-
 // starting value of nextcleanup
 #define CLEANUPCOUNT 2000
 
diff --git a/src/z_zone.h b/src/z_zone.h
index 5cbcc6655b..c9dafc1aed 100644
--- a/src/z_zone.h
+++ b/src/z_zone.h
@@ -144,10 +144,4 @@ size_t Z_TagsUsage(INT32 lowtag, INT32 hightag);
 char *Z_StrDup(const char *in);
 #define Z_Unlock(p) (void)p // TODO: remove this now that NDS code has been removed
 
-// For renderer switching
-extern boolean needpatchflush;
-extern boolean needpatchrecache;
-void Z_FlushCachedPatches(void);
-void Z_PreparePatchFlush(void);
-
 #endif
-- 
GitLab