diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d35e774e934d5b624b4d2bcc5c19b5bc24b6abf6..89308f13fe525edeabd4cb24bfaeb7f8b84eb2a0 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -501,6 +501,7 @@ if(${SRB2_CONFIG_HWRENDER})
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_cache.c
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_clip.c
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_draw.c
+		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_gpu.c
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_light.c
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_main.c
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_md2.c
@@ -516,7 +517,7 @@ if(${SRB2_CONFIG_HWRENDER})
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_data.h
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_defs.h
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_dll.h
-		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_drv.h
+		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_gpu.h
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_glob.h
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_light.h
 		${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_main.h
diff --git a/src/Makefile b/src/Makefile
index 0c1626fc955c5465f9ae23b62875bf9395c73e25..09e37121258f5ff28dd9b6baebd2c41304870e25 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -269,7 +269,7 @@ ifdef NOHW
 else
 	OPTS+=-DHWRENDER
 	OBJS+=$(OBJDIR)/hw_bsp.o $(OBJDIR)/hw_draw.o $(OBJDIR)/hw_light.o \
-		 $(OBJDIR)/hw_main.o $(OBJDIR)/hw_clip.o $(OBJDIR)/hw_md2.o $(OBJDIR)/hw_cache.o \
+		 $(OBJDIR)/hw_main.o $(OBJDIR)/hw_gpu.o $(OBJDIR)/hw_clip.o $(OBJDIR)/hw_md2.o $(OBJDIR)/hw_cache.o \
 		 $(OBJDIR)/hw_md2load.o $(OBJDIR)/hw_md3load.o $(OBJDIR)/hw_model.o $(OBJDIR)/u_list.o $(OBJDIR)/hw_batching.o
 endif
 
@@ -707,7 +707,7 @@ else
 ifdef SDL
 ifdef MINGW
 $(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \
- doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \
+ doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_gpu.h screen.h \
  command.h hardware/hw_data.h hardware/hw_defs.h \
  hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \
  hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \
@@ -717,7 +717,7 @@ $(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h
 	$(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@
 else
 $(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \
- doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \
+ doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_gpu.h screen.h \
  command.h hardware/hw_data.h hardware/hw_defs.h \
  hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \
  hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \
@@ -808,7 +808,7 @@ ifdef MINGW
 ifndef SDL
 ifndef NOHW
 $(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \
- doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \
+ doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_gpu.h screen.h \
  command.h hardware/hw_data.h hardware/hw_defs.h \
  hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \
  hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \
@@ -818,7 +818,7 @@ $(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h
 	$(CC) $(CFLAGS) $(WFLAGS) -D_WINDOWS -mwindows -c $< -o $@
 
 $(OBJDIR)/ogl_win.o: hardware/r_opengl/ogl_win.c hardware/r_opengl/r_opengl.h \
- doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \
+ doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_gpu.h screen.h \
  command.h hardware/hw_data.h hardware/hw_defs.h \
  hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \
  hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \
diff --git a/src/hardware/hw_batching.c b/src/hardware/hw_batching.c
index d572145bf98c91c7e1a0daa4fdb9b65753cfeb80..a9c7f98dae6844d173fad639fe3ea48c2e9f20db 100644
--- a/src/hardware/hw_batching.c
+++ b/src/hardware/hw_batching.c
@@ -69,7 +69,7 @@ void HWR_SetCurrentTexture(HWRTexture_t *texture)
     }
     else
     {
-        HWD.pfnSetTexture(texture);
+        GPU->SetTexture(texture);
     }
 }
 
@@ -124,8 +124,8 @@ void HWR_ProcessPolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumP
 	else
 	{
         if (shader)
-            HWD.pfnSetShader(shader);
-        HWD.pfnDrawPolygon(pSurf, pOutVerts, iNumPts, PolyFlags);
+            GPU->SetShader(shader);
+        GPU->DrawPolygon(pSurf, pOutVerts, iNumPts, PolyFlags);
     }
 }
 
@@ -274,13 +274,13 @@ void HWR_RenderBatches(void)
 
 	if (cv_glshaders.value && gl_shadersavailable)
 	{
-		HWD.pfnSetShader(currentShader);
+		GPU->SetShader(currentShader);
 	}
 
 	if (currentPolyFlags & PF_NoTexture)
 		currentTexture = NULL;
     else
-	    HWD.pfnSetTexture(currentTexture);
+	    GPU->SetTexture(currentTexture);
 
 	while (1)// note: remember handling notexture polyflag as having texture number 0 (also in comparePolygons)
 	{
@@ -395,7 +395,7 @@ void HWR_RenderBatches(void)
 		if (changeState || stopFlag)
 		{
 			// execute draw call
-            HWD.pfnDrawIndexedTriangles(&currentSurfaceInfo, finalVertexArray, finalIndexWritePos, currentPolyFlags, finalVertexIndexArray);
+			GPU->DrawIndexedTriangles(&currentSurfaceInfo, finalVertexArray, finalIndexWritePos, currentPolyFlags, finalVertexIndexArray);
 			// update stats
 			ps_hw_numcalls++;
 			ps_hw_numverts += finalIndexWritePos;
@@ -411,7 +411,7 @@ void HWR_RenderBatches(void)
 		// change state according to change bools and next vars, update current vars and reset bools
 		if (changeShader)
 		{
-			HWD.pfnSetShader(nextShader);
+			GPU->SetShader(nextShader);
 			currentShader = nextShader;
 			changeShader = false;
 
@@ -420,7 +420,7 @@ void HWR_RenderBatches(void)
 		if (changeTexture)
 		{
 			// texture should be already ready for use from calls to SetTexture during batch collection
-		    HWD.pfnSetTexture(nextTexture);
+			GPU->SetTexture(nextTexture);
 			currentTexture = nextTexture;
 			changeTexture = false;
 
diff --git a/src/hardware/hw_batching.h b/src/hardware/hw_batching.h
index 60a93995a134b59ed7b1e4673405f2e38ab975d2..e933fc16406c769eba3a35807d9acf6606173e01 100644
--- a/src/hardware/hw_batching.h
+++ b/src/hardware/hw_batching.h
@@ -14,7 +14,7 @@
 
 #include "hw_defs.h"
 #include "hw_data.h"
-#include "hw_drv.h"
+#include "hw_gpu.h"
 
 typedef struct
 {
diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c
index 85ccc48f0ecaa339c3e6786f6ee6ba2d5e4f1d91..8138f5bef3d4c7340f47992787feed9a9cee1ba1 100644
--- a/src/hardware/hw_cache.c
+++ b/src/hardware/hw_cache.c
@@ -14,7 +14,7 @@
 
 #ifdef HWRENDER
 #include "hw_glob.h"
-#include "hw_drv.h"
+#include "hw_gpu.h"
 #include "hw_batching.h"
 
 #include "../doomstat.h"    //gamemode
@@ -599,7 +599,7 @@ void HWR_FreeTexture(patch_t *patch)
 		if (grPatch->texture)
 		{
 			if (vid.glstate == VID_GL_LIBRARY_LOADED)
-				HWD.pfnDeleteTexture(grPatch->texture);
+				GPU->DeleteTexture(grPatch->texture);
 			if (grPatch->texture->data)
 				Z_Free(grPatch->texture->data);
 			Z_Free(grPatch->texture);
@@ -650,7 +650,7 @@ void HWR_FreeTextureColormaps(patch_t *patch)
 		next->data = NULL;
 
 		if (vid.glstate == VID_GL_LIBRARY_LOADED)
-			HWD.pfnDeleteTexture(next);
+			GPU->DeleteTexture(next);
 
 		// Free the old colormap texture from memory.
 		free(next);
@@ -684,7 +684,7 @@ static void FreeTextureCache(boolean freeall)
 
 void HWR_ClearAllTextures(void)
 {
-	HWD.pfnClearTextureCache(); // free references to the textures
+	GPU->ClearTextureCache(); // free references to the textures
 	FreeTextureCache(true);
 }
 
@@ -703,7 +703,7 @@ void HWR_InitMapTextures(void)
 
 static void FreeMapTexture(GLMapTexture_t *tex)
 {
-	HWD.pfnDeleteTexture(&tex->texture);
+	GPU->DeleteTexture(&tex->texture);
 	if (tex->texture.data)
 		Z_Free(tex->texture.data);
 	tex->texture.data = NULL;
@@ -748,7 +748,7 @@ void HWR_LoadMapTextures(size_t pnumtextures)
 
 void HWR_SetPalette(RGBA_t *palette)
 {
-	HWD.pfnSetPalette(palette);
+	GPU->SetPalette(palette);
 
 	// hardware driver will flush there own cache if cache is non paletized
 	// now flush data texture cache so 32 bit texture are recomputed
@@ -780,7 +780,7 @@ GLMapTexture_t *HWR_GetTexture(INT32 tex)
 
 	// If hardware does not have the texture, then call pfnSetTexture to upload it
 	if (!grtex->texture.downloaded)
-		HWD.pfnSetTexture(&grtex->texture);
+		GPU->SetTexture(&grtex->texture);
 	HWR_SetCurrentTexture(&grtex->texture);
 
 	// The system-memory data can be purged now.
@@ -868,7 +868,7 @@ void HWR_LiterallyGetFlat(lumpnum_t flatlumpnum)
 
 	// If hardware does not have the texture, then call pfnSetTexture to upload it
 	if (!hwrtex->downloaded)
-		HWD.pfnSetTexture(hwrtex);
+		GPU->SetTexture(hwrtex);
 	HWR_SetCurrentTexture(hwrtex);
 
 	// The system-memory data can be purged now.
@@ -905,7 +905,7 @@ void HWR_GetLevelFlat(levelflat_t *levelflat)
 
 		// If hardware does not have the texture, then call pfnSetTexture to upload it
 		if (!grtex->texture.downloaded)
-			HWD.pfnSetTexture(&grtex->texture);
+			GPU->SetTexture(&grtex->texture);
 		HWR_SetCurrentTexture(&grtex->texture);
 
 		// The system-memory data can be purged now.
@@ -955,7 +955,7 @@ void HWR_GetLevelFlat(levelflat_t *levelflat)
 		}
 
 		// Tell the hardware driver to bind the current texture to the flat's texture
-		HWD.pfnSetTexture(texture);
+		GPU->SetTexture(texture);
 	}
 #endif
 	else // set no texture
@@ -973,7 +973,7 @@ static void HWR_LoadPatchTexture(patch_t *patch, HWRTexture_t *hwrTexture)
 
 	// If hardware does not have the texture, then call pfnSetTexture to upload it
 	if (!hwrTexture->downloaded)
-		HWD.pfnSetTexture(hwrTexture);
+		GPU->SetTexture(hwrTexture);
 	HWR_SetCurrentTexture(hwrTexture);
 
 	// The system-memory data can be purged now.
@@ -1167,7 +1167,7 @@ patch_t *HWR_GetPic(lumpnum_t lumpnum)
 		grPatch->texture->flags = 0;
 		grPatch->max_s = grPatch->max_t = 1.0f;
 	}
-	HWD.pfnSetTexture(grPatch->texture);
+	GPU->SetTexture(grPatch->texture);
 	//CONS_Debug(DBG_RENDER, "picloaded at %x as texture %d\n",grPatch->texture->data, grPatch->texture->downloaded);
 
 	return patch;
@@ -1283,7 +1283,7 @@ void HWR_GetFadeMask(lumpnum_t fademasklumpnum)
 	if (!hwrtex->downloaded && !hwrtex->data)
 		HWR_CacheFadeMask(hwrtex, fademasklumpnum);
 
-	HWD.pfnSetTexture(hwrtex);
+	GPU->SetTexture(hwrtex);
 
 	// The system-memory data can be purged now.
 	Z_ChangeTag(hwrtex->data, PU_HWRCACHE_UNLOCKED);
diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h
index fad978dfe0f9e70d5626584668988d02f3d63481..981d15d1532543c078d0c8b91d1b3c63eada6fb6 100644
--- a/src/hardware/hw_defs.h
+++ b/src/hardware/hw_defs.h
@@ -31,6 +31,8 @@
 #define GPU_DEFAULTMIX 0x00000000
 #define GPU_DEFAULTFOG 0xFF000000
 
+#define SCREENVERTS 10
+
 // RGBA Color components with float type ranging [ 0 ... 1 ]
 struct FRGBAFloat
 {
diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c
index 51b1e5631f368b7126c92889b49bafc5477e98e3..0a0184adf77b2fad90248ef02d896f76bcb64b66 100644
--- a/src/hardware/hw_draw.c
+++ b/src/hardware/hw_draw.c
@@ -19,7 +19,7 @@
 #ifdef HWRENDER
 #include "hw_main.h"
 #include "hw_glob.h"
-#include "hw_drv.h"
+#include "hw_gpu.h"
 
 #include "../m_misc.h" //FIL_WriteFile()
 #include "../r_draw.h" //viewborderlump
@@ -125,7 +125,7 @@ void HWR_DrawPatch(patch_t *gpatch, INT32 x, INT32 y, INT32 option)
 		flags |= PF_ForceWrapY;
 
 	// clip it since it is used for bunny scroll in doom I
-	HWD.pfnDrawPolygon(NULL, v, 4, flags);
+	GPU->DrawPolygon(NULL, v, 4, flags);
 }
 
 void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 option, const UINT8 *colormap)
@@ -376,10 +376,10 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p
 		else if (alphalevel == 15) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency];
 		else Surf.PolyColor.s.alpha = softwaretranstogl[10-alphalevel];
 		flags |= PF_Modulated;
-		HWD.pfnDrawPolygon(&Surf, v, 4, flags);
+		GPU->DrawPolygon(&Surf, v, 4, flags);
 	}
 	else
-		HWD.pfnDrawPolygon(NULL, v, 4, flags);
+		GPU->DrawPolygon(NULL, v, 4, flags);
 }
 
 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)
@@ -533,10 +533,10 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
 		else if (alphalevel == 15) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency];
 		else Surf.PolyColor.s.alpha = softwaretranstogl[10-alphalevel];
 		flags |= PF_Modulated;
-		HWD.pfnDrawPolygon(&Surf, v, 4, flags);
+		GPU->DrawPolygon(&Surf, v, 4, flags);
 	}
 	else
-		HWD.pfnDrawPolygon(NULL, v, 4, flags);
+		GPU->DrawPolygon(NULL, v, 4, flags);
 }
 
 void HWR_DrawPic(INT32 x, INT32 y, lumpnum_t lumpnum)
@@ -571,7 +571,7 @@ void HWR_DrawPic(INT32 x, INT32 y, lumpnum_t lumpnum)
 	// But then, the question is: why not 0 instead of PF_Masked ?
 	// or maybe PF_Environment ??? (like what I said above)
 	// BP: PF_Environment don't change anything ! and 0 is undifined
-	HWD.pfnDrawPolygon(NULL, v, 4, PF_Translucent | PF_NoDepthTest);
+	GPU->DrawPolygon(NULL, v, 4, PF_Translucent | PF_NoDepthTest);
 }
 
 // ==========================================================================
@@ -647,7 +647,7 @@ void HWR_DrawFlatFill (INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatlumpnum
 	// BTW, I see we put 0 for PFs, and If I'm right, that
 	// means we take the previous PFs as default
 	// how can we be sure they are ok?
-	HWD.pfnDrawPolygon(NULL, v, 4, PF_NoDepthTest); //PF_Translucent);
+	GPU->DrawPolygon(NULL, v, 4, PF_NoDepthTest); //PF_Translucent);
 }
 
 
@@ -684,7 +684,7 @@ void HWR_FadeScreenMenuBack(UINT16 color, UINT8 strength)
 		Surf.PolyColor.rgba = V_GetColor(color).rgba;
 		Surf.PolyColor.s.alpha = softwaretranstogl[strength];
 	}
-	HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
+	GPU->DrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
 }
 
 // -----------------+
@@ -857,7 +857,7 @@ void HWR_DrawFadeFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color, UINT16 ac
 		Surf.PolyColor.rgba = V_GetColor(actualcolor).rgba;
 		Surf.PolyColor.s.alpha = softwaretranstogl[strength];
 	}
-	HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
+	GPU->DrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
 }
 
 // Draw the console background with translucency support
@@ -884,7 +884,7 @@ void HWR_DrawConsoleBack(UINT32 color, INT32 height)
 	Surf.PolyColor.rgba = UINT2RGBA(color);
 	Surf.PolyColor.s.alpha = 0x80;
 
-	HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
+	GPU->DrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
 }
 
 // Very similar to HWR_DrawConsoleBack, except we draw from the middle(-ish) of the screen to the bottom.
@@ -914,7 +914,7 @@ void HWR_DrawTutorialBack(UINT32 color, INT32 boxheight)
 	Surf.PolyColor.rgba = UINT2RGBA(color);
 	Surf.PolyColor.s.alpha = (color == 0 ? 0xC0 : 0x80); // make black darker, like software
 
-	HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
+	GPU->DrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
 }
 
 
@@ -1068,7 +1068,7 @@ void HWR_drawAMline(const fline_t *fl, INT32 color)
 	v2.x = ((float)fl->b.x-(vid.width/2.0f))*(2.0f/vid.width);
 	v2.y = ((float)fl->b.y-(vid.height/2.0f))*(2.0f/vid.height);
 
-	HWD.pfnDraw2DLine(&v1, &v2, color_rgba);
+	GPU->Draw2DLine(&v1, &v2, color_rgba);
 }
 
 // -------------------+
@@ -1234,7 +1234,7 @@ void HWR_DrawConsoleFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color, UINT32
 	Surf.PolyColor.rgba = UINT2RGBA(actualcolor);
 	Surf.PolyColor.s.alpha = 0x80;
 
-	HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
+	GPU->DrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
 }
 
 // -----------------+
@@ -1338,7 +1338,7 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color)
 			clearColour.green = (float)rgbaColour.s.green / 255;
 			clearColour.blue = (float)rgbaColour.s.blue / 255;
 			clearColour.alpha = 1;
-			HWD.pfnClearBuffer(true, false, &clearColour);
+			GPU->ClearBuffer(true, false, &clearColour);
 			return;
 		}
 
@@ -1411,7 +1411,7 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color)
 
 	Surf.PolyColor = V_GetColor(color);
 
-	HWD.pfnDrawPolygon(&Surf, v, 4,
+	GPU->DrawPolygon(&Surf, v, 4,
 		PF_Modulated|PF_NoTexture|PF_NoDepthTest);
 }
 
@@ -1493,7 +1493,7 @@ UINT8 *HWR_GetScreenshot(void)
 	if (!buf)
 		return NULL;
 	// returns 24bit 888 RGB
-	HWD.pfnReadRect(0, 0, vid.width, vid.height, vid.width * 3, (void *)buf);
+	GPU->ReadRect(0, 0, vid.width, vid.height, vid.width * 3, (void *)buf);
 	return buf;
 }
 
@@ -1509,7 +1509,7 @@ boolean HWR_Screenshot(const char *pathname)
 	}
 
 	// returns 24bit 888 RGB
-	HWD.pfnReadRect(0, 0, vid.width, vid.height, vid.width * 3, (void *)buf);
+	GPU->ReadRect(0, 0, vid.width, vid.height, vid.width * 3, (void *)buf);
 
 #ifdef USE_PNG
 	ret = M_SavePNG(pathname, buf, vid.width, vid.height, NULL);
diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h
deleted file mode 100644
index 608712e34d584b25e3ca881d22448f10a1563578..0000000000000000000000000000000000000000
--- a/src/hardware/hw_drv.h
+++ /dev/null
@@ -1,140 +0,0 @@
-// SONIC ROBO BLAST 2
-//-----------------------------------------------------------------------------
-// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2020 by Sonic Team Junior.
-//
-// This program is free software distributed under the
-// terms of the GNU General Public License, version 2.
-// See the 'LICENSE' file for more details.
-//-----------------------------------------------------------------------------
-/// \file hw_drv.h
-/// \brief imports/exports for the 3D hardware low-level interface API
-
-#ifndef __HWR_DRV_H__
-#define __HWR_DRV_H__
-
-// this must be here 19991024 by Kin
-#include "../screen.h"
-#include "hw_data.h"
-#include "hw_defs.h"
-#include "hw_md2.h"
-
-#include "hw_dll.h"
-
-// ==========================================================================
-//                                                       STANDARD DLL EXPORTS
-// ==========================================================================
-
-EXPORT boolean HWRAPI(Init) (void);
-#ifndef HAVE_SDL
-EXPORT void HWRAPI(Shutdown) (void);
-#endif
-#ifdef _WINDOWS
-EXPORT void HWRAPI(GetModeList) (vmode_t **pvidmodes, INT32 *numvidmodes);
-#endif
-EXPORT void HWRAPI(SetPalette) (RGBA_t *ppal);
-EXPORT void HWRAPI(FinishUpdate) (INT32 waitvbl);
-EXPORT void HWRAPI(Draw2DLine) (F2DCoord *v1, F2DCoord *v2, RGBA_t Color);
-EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumPts, UINT32 PolyFlags);
-EXPORT void HWRAPI(DrawIndexedTriangles) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumPts, UINT32 PolyFlags, UINT32 *IndexArray);
-EXPORT void HWRAPI(RenderSkyDome) (FSkyDome *sky);
-EXPORT void HWRAPI(SetBlend) (UINT32 PolyFlags);
-EXPORT void HWRAPI(ClearBuffer) (boolean ColorMask, boolean DepthMask, FRGBAFloat *ClearColor);
-EXPORT void HWRAPI(SetTexture) (HWRTexture_t *TexInfo);
-EXPORT void HWRAPI(UpdateTexture) (HWRTexture_t *TexInfo);
-EXPORT void HWRAPI(DeleteTexture) (HWRTexture_t *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(ClearTextureCache) (void);
-
-//Hurdler: added for backward compatibility
-EXPORT void HWRAPI(SetState) (INT32 State, INT32 Value);
-
-//Hurdler: added for new development
-EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface);
-EXPORT void HWRAPI(CreateModelVBOs) (model_t *model);
-EXPORT void HWRAPI(SetTransform) (FTransform *ptransform);
-EXPORT INT32 HWRAPI(GetTextureUsed) (void);
-
-EXPORT void HWRAPI(FlushScreenTextures) (void);
-EXPORT void HWRAPI(StartScreenWipe) (void);
-EXPORT void HWRAPI(EndScreenWipe) (void);
-EXPORT void HWRAPI(DoScreenWipe) (void);
-EXPORT void HWRAPI(DrawIntermissionBG) (void);
-EXPORT void HWRAPI(MakeScreenTexture) (void);
-EXPORT void HWRAPI(MakeScreenFinalTexture) (void);
-EXPORT void HWRAPI(DrawScreenFinalTexture) (int width, int height);
-
-#define SCREENVERTS 10
-EXPORT void HWRAPI(PostImgRedraw) (float points[SCREENVERTS][SCREENVERTS][2]);
-
-// jimita
-EXPORT boolean HWRAPI(CompileShaders) (void);
-EXPORT void HWRAPI(CleanShaders) (void);
-EXPORT void HWRAPI(SetShader) (int type);
-EXPORT void HWRAPI(UnSetShader) (void);
-
-EXPORT void HWRAPI(SetShaderInfo) (INT32 info, INT32 value);
-EXPORT void HWRAPI(LoadCustomShader) (int number, char *code, size_t size, boolean isfragment);
-
-// ==========================================================================
-//                                      HWR DRIVER OBJECT, FOR CLIENT PROGRAM
-// ==========================================================================
-
-#if !defined (_CREATE_DLL_)
-
-struct hwdriver_s
-{
-	Init                pfnInit;
-	SetPalette          pfnSetPalette;
-	FinishUpdate        pfnFinishUpdate;
-	Draw2DLine          pfnDraw2DLine;
-	DrawPolygon         pfnDrawPolygon;
-	DrawIndexedTriangles    pfnDrawIndexedTriangles;
-	RenderSkyDome       pfnRenderSkyDome;
-	SetBlend            pfnSetBlend;
-	ClearBuffer         pfnClearBuffer;
-	SetTexture          pfnSetTexture;
-	UpdateTexture       pfnUpdateTexture;
-	DeleteTexture       pfnDeleteTexture;
-	ReadRect            pfnReadRect;
-	GClipRect           pfnGClipRect;
-	ClearTextureCache   pfnClearTextureCache;
-	SetState            pfnSetState;
-	DrawModel           pfnDrawModel;
-	CreateModelVBOs     pfnCreateModelVBOs;
-	SetTransform        pfnSetTransform;
-	GetTextureUsed      pfnGetTextureUsed;
-#ifdef _WINDOWS
-	GetModeList         pfnGetModeList;
-#endif
-#ifndef HAVE_SDL
-	Shutdown            pfnShutdown;
-#endif
-	PostImgRedraw       pfnPostImgRedraw;
-	FlushScreenTextures pfnFlushScreenTextures;
-	StartScreenWipe     pfnStartScreenWipe;
-	EndScreenWipe       pfnEndScreenWipe;
-	DoScreenWipe        pfnDoScreenWipe;
-	DrawIntermissionBG  pfnDrawIntermissionBG;
-	MakeScreenTexture   pfnMakeScreenTexture;
-	MakeScreenFinalTexture  pfnMakeScreenFinalTexture;
-	DrawScreenFinalTexture  pfnDrawScreenFinalTexture;
-
-	CompileShaders      pfnCompileShaders;
-	CleanShaders        pfnCleanShaders;
-	SetShader           pfnSetShader;
-	UnSetShader         pfnUnSetShader;
-
-	SetShaderInfo       pfnSetShaderInfo;
-	LoadCustomShader    pfnLoadCustomShader;
-};
-
-extern struct hwdriver_s hwdriver;
-
-#define HWD hwdriver
-
-#endif //not defined _CREATE_DLL_
-
-#endif //__HWR_DRV_H__
-
diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c
index e05fe4ceca39216a8ef2f1067b284ce7dcd9d614..b8c27ef54d4253a18294ebd714bd14213f5296f3 100644
--- a/src/hardware/hw_light.c
+++ b/src/hardware/hw_light.c
@@ -15,7 +15,7 @@
 
 #ifdef HWRENDER
 #include "hw_light.h"
-#include "hw_drv.h"
+#include "hw_gpu.h"
 #include "../i_video.h"
 #include "../z_zone.h"
 #include "../m_random.h"
@@ -889,7 +889,7 @@ void HWR_WallLighting(FOutVector *wlVerts)
 		if (dynlights->mo[j]->state->nextstate == S_NULL)
 			Surf.PolyColor.s.alpha = (UINT8)(((float)dynlights->mo[j]->tics/(float)dynlights->mo[j]->state->tics)*Surf.PolyColor.s.alpha);
 
-		HWD.pfnDrawPolygon (&Surf, wlVerts, 4, LIGHTMAPFLAGS);
+		GPU->DrawPolygon (&Surf, wlVerts, 4, LIGHTMAPFLAGS);
 
 	} // end for (j = 0; j < dynlights->nb; j++)
 }
@@ -958,7 +958,7 @@ void HWR_PlaneLighting(FOutVector *clVerts, int nrClipVerts)
 		if ((dynlights->mo[j]->state->nextstate == S_NULL))
 			Surf.PolyColor.s.alpha = (unsigned char)(((float)dynlights->mo[j]->tics/(float)dynlights->mo[j]->state->tics)*Surf.PolyColor.s.alpha);
 
-		HWD.pfnDrawPolygon (&Surf, clVerts, nrClipVerts, LIGHTMAPFLAGS);
+		GPU->DrawPolygon (&Surf, clVerts, nrClipVerts, LIGHTMAPFLAGS);
 
 	} // end for (j = 0; j < dynlights->nb; j++)
 }
@@ -1055,7 +1055,7 @@ void HWR_DoCoronasLighting(FOutVector *outVerts, gl_vissprite_t *spr)
 
 		HWR_GetPic(coronalumpnum);  /// \todo use different coronas
 
-		HWD.pfnDrawPolygon (&Surf, light, 4, PF_Modulated | PF_AdditiveSource | PF_Corona | PF_NoDepthTest);
+		GPU->DrawPolygon (&Surf, light, 4, PF_Modulated | PF_AdditiveSource | PF_Corona | PF_NoDepthTest);
 	}
 }
 #endif
@@ -1143,7 +1143,7 @@ void HWR_DrawCoronas(void)
 		light[3].y = cy+size*1.33f;
 		light[3].s = 0.0f;   light[3].t = 1.0f;
 
-		HWD.pfnDrawPolygon (&Surf, light, 4, PF_Modulated | PF_AdditiveSource | PF_NoDepthTest | PF_Corona);
+		GPU->DrawPolygon (&Surf, light, 4, PF_Modulated | PF_AdditiveSource | PF_NoDepthTest | PF_Corona);
 	}
 }
 #endif
@@ -1252,7 +1252,7 @@ static void HWR_SetLight(void)
 		lightmappatch.texture->height = 128;
 		lightmappatch.texture->flags = 0; //TF_WRAPXY; // DEBUG: view the overdraw !
 	}
-	HWD.pfnSetTexture(lightmappatch.texture);
+	GPU->SetTexture(lightmappatch.texture);
 
 	// The system-memory data can be purged now.
 	Z_ChangeTag(lightmappatch.texture->data, PU_HWRCACHE_UNLOCKED);
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index e11a37a5c1e0fd9f072605855742a846af8f1952..5395f918aad5f2120e93b846a9e488eabf9aaf40 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -17,7 +17,7 @@
 #ifdef HWRENDER
 #include "hw_glob.h"
 #include "hw_light.h"
-#include "hw_drv.h"
+#include "hw_gpu.h"
 #include "hw_batching.h"
 
 #include "../i_video.h" // for rendermode == render_glide
@@ -49,11 +49,6 @@
 #define HWPRECIP
 //#define POLYSKY
 
-// ==========================================================================
-// the hardware driver object
-// ==========================================================================
-struct hwdriver_s hwdriver;
-
 // ==========================================================================
 //                                                                     PROTOS
 // ==========================================================================
@@ -696,7 +691,7 @@ static void HWR_RenderSkyPlane(extrasubsector_t *xsub, fixed_t fixedheight)
 		v3d->z = pv->y;
 	}
 
-	HWD.pfnDrawPolygon(NULL, planeVerts, nrPlaneVerts, PF_Invisible|PF_NoTexture|PF_Occlude);
+	GPU->DrawPolygon(NULL, planeVerts, nrPlaneVerts, PF_Invisible|PF_NoTexture|PF_Occlude);
 }
 #endif //polysky
 
@@ -4714,8 +4709,8 @@ static void HWR_CreateDrawNodes(void)
 	ps_hw_nodedrawtime = I_GetPreciseTime();
 
 	// Okay! Let's draw it all! Woo!
-	HWD.pfnSetTransform(&atransform);
-	HWD.pfnSetShader(SHADER_DEFAULT);
+	GPU->SetTransform(&atransform);
+	GPU->SetShader(SHADER_DEFAULT);
 
 	for (i = 0; i < p; i++)
 	{
@@ -4768,7 +4763,7 @@ static void HWR_DrawSprites(void)
 {
 	UINT32 i;
 	boolean skipshadow = false; // skip shadow if it was drawn already for a linkdraw sprite encountered earlier in the list
-	HWD.pfnSetState(GPU_STATE_MODEL_LIGHTING, cv_glmodellighting.value);
+	GPU->SetState(GPU_STATE_MODEL_LIGHTING, cv_glmodellighting.value);
 	for (i = 0; i < gl_visspritecount; i++)
 	{
 		gl_vissprite_t *spr = gl_vsprorder[i];
@@ -4826,7 +4821,7 @@ static void HWR_DrawSprites(void)
 			}
 		}
 	}
-	HWD.pfnSetState(GPU_STATE_MODEL_LIGHTING, 0);
+	GPU->SetState(GPU_STATE_MODEL_LIGHTING, 0);
 
 	// At the end of sprite drawing, draw shapes of linkdraw sprites to z-buffer, so they
 	// don't get drawn over by transparent surfaces.
@@ -4836,7 +4831,7 @@ static void HWR_DrawSprites(void)
 	// (Other states probably don't matter. Here I left them same as in LinkDrawHackFinish)
 	// Without this workaround the rest of the draw calls in this frame (including UI, screen texture)
 	// can get drawn using an incorrect glBlendFunc, resulting in a occasional black screen.
-	HWD.pfnSetBlend(PF_Translucent|PF_Occlude|PF_Masked);
+	GPU->SetBlend(PF_Translucent|PF_Occlude|PF_Masked);
 }
 
 // --------------------------------------------------------------------------
@@ -5564,7 +5559,7 @@ void HWR_BuildSkyDome(void)
 
 static void HWR_DrawSkyBackground(player_t *player)
 {
-	HWD.pfnSetBlend(PF_Translucent|PF_NoDepthTest|PF_Modulated);
+	GPU->SetBlend(PF_Translucent|PF_NoDepthTest|PF_Modulated);
 
 	if (cv_glskydome.value)
 	{
@@ -5610,9 +5605,9 @@ static void HWR_DrawSkyBackground(player_t *player)
 			HWR_BuildSkyDome();
 		}
 
-		HWD.pfnSetShader(SHADER_SKY); // sky shader
-		HWD.pfnSetTransform(&dometransform);
-		HWD.pfnRenderSkyDome(&SkyDome);
+		GPU->SetShader(SHADER_SKY); // sky shader
+		GPU->SetTransform(&dometransform);
+		GPU->DrawSkyDome(&SkyDome);
 	}
 	else
 	{
@@ -5693,11 +5688,11 @@ static void HWR_DrawSkyBackground(player_t *player)
 			v[0].t = v[1].t -= ((float) angle / angleturn);
 		}
 
-		HWD.pfnUnSetShader();
-		HWD.pfnDrawPolygon(NULL, v, 4, 0);
+		GPU->UnSetShader();
+		GPU->DrawPolygon(NULL, v, 4, 0);
 	}
 
-	HWD.pfnSetShader(SHADER_DEFAULT);
+	GPU->SetShader(SHADER_DEFAULT);
 }
 
 
@@ -5713,16 +5708,16 @@ static inline void HWR_ClearView(void)
 
 	/// \bug faB - enable depth mask, disable color mask
 
-	HWD.pfnGClipRect((INT32)gl_viewwindowx,
+	GPU->GClipRect((INT32)gl_viewwindowx,
 	                 (INT32)gl_viewwindowy,
 	                 (INT32)(gl_viewwindowx + gl_viewwidth),
 	                 (INT32)(gl_viewwindowy + gl_viewheight),
 	                 ZCLIP_PLANE);
-	HWD.pfnClearBuffer(false, true, 0);
+	GPU->ClearBuffer(false, true, 0);
 
 	//disable clip window - set to full size
 	// rem by Hurdler
-	// HWD.pfnGClipRect(0, 0, vid.width, vid.height);
+	// GPU->GClipRect(0, 0, vid.width, vid.height);
 }
 
 
@@ -5757,7 +5752,7 @@ void HWR_SetViewSize(void)
 	gl_pspritexscale = gl_viewwidth / BASEVIDWIDTH;
 	gl_pspriteyscale = ((vid.height*gl_pspritexscale*BASEVIDWIDTH)/BASEVIDHEIGHT)/vid.width;
 
-	HWD.pfnFlushScreenTextures();
+	GPU->FlushScreenTextures();
 }
 
 // Set view aiming, for the sky dome, the skybox,
@@ -5792,8 +5787,8 @@ static void HWR_SetShaderState(void)
 	if (!cv_glallowshaders.value)
 		state = (cv_glshaders.value == GPU_SHADEROPTION_ON ? GPU_SHADEROPTION_NOCUSTOM : cv_glshaders.value);
 
-	HWD.pfnSetState(GPU_STATE_SHADERS, (INT32)state);
-	HWD.pfnSetShader(SHADER_DEFAULT);
+	GPU->SetState(GPU_STATE_SHADERS, (INT32)state);
+	GPU->SetShader(SHADER_DEFAULT);
 }
 
 // ==========================================================================
@@ -5911,7 +5906,7 @@ void HWR_RenderSkyboxView(INT32 viewnumber, player_t *player)
 
 	//04/01/2000: Hurdler: added for T&L
 	//                     Actually it only works on Walls and Planes
-	HWD.pfnSetTransform(&atransform);
+	GPU->SetTransform(&atransform);
 
 	// Reset the shader state.
 	HWR_SetShaderState();
@@ -5978,15 +5973,15 @@ void HWR_RenderSkyboxView(INT32 viewnumber, player_t *player)
 		HWR_CreateDrawNodes();
 	}
 
-	HWD.pfnSetTransform(NULL);
-	HWD.pfnUnSetShader();
+	GPU->SetTransform(NULL);
+	GPU->UnSetShader();
 
 	// Check for new console commands.
 	NetUpdate();
 
 	// added by Hurdler for correct splitscreen
 	// moved here by hurdler so it works with the new near clipping plane
-	HWD.pfnGClipRect(0, 0, vid.width, vid.height, NZCLIP_PLANE);
+	GPU->GClipRect(0, 0, vid.width, vid.height, NZCLIP_PLANE);
 }
 
 // ==========================================================================
@@ -6012,10 +6007,10 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player)
 	ClearColor.alpha = 1.0f;
 
 	if (cv_glshaders.value)
-		HWD.pfnSetShaderInfo(GPU_SHADERINFO_LEVELTIME, (INT32)leveltime); // The water surface shader needs the leveltime.
+		GPU->SetShaderInfo(GPU_SHADERINFO_LEVELTIME, (INT32)leveltime); // The water surface shader needs the leveltime.
 
 	if (viewnumber == 0) // Only do it if it's the first screen being rendered
-		HWD.pfnClearBuffer(true, false, &ClearColor); // Clear the Color Buffer, stops HOMs. Also seems to fix the skybox issue on Intel GPUs.
+		GPU->ClearBuffer(true, false, &ClearColor); // Clear the Color Buffer, stops HOMs. Also seems to fix the skybox issue on Intel GPUs.
 
 	ps_hw_skyboxtime = I_GetPreciseTime();
 	if (skybox && drawsky) // If there's a skybox and we should be drawing the sky, draw the skybox
@@ -6125,7 +6120,7 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player)
 
 	//04/01/2000: Hurdler: added for T&L
 	//                     Actually it only works on Walls and Planes
-	HWD.pfnSetTransform(&atransform);
+	GPU->SetTransform(&atransform);
 
 	// Reset the shader state.
 	HWR_SetShaderState();
@@ -6206,8 +6201,8 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player)
 		HWR_CreateDrawNodes();
 	}
 
-	HWD.pfnSetTransform(NULL);
-	HWD.pfnUnSetShader();
+	GPU->SetTransform(NULL);
+	GPU->UnSetShader();
 
 	HWR_DoPostProcessor(player);
 
@@ -6216,7 +6211,7 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player)
 
 	// added by Hurdler for correct splitscreen
 	// moved here by hurdler so it works with the new near clipping plane
-	HWD.pfnGClipRect(0, 0, vid.width, vid.height, NZCLIP_PLANE);
+	GPU->GClipRect(0, 0, vid.width, vid.height, NZCLIP_PLANE);
 }
 
 void HWR_LoadLevel(void)
@@ -6286,13 +6281,13 @@ consvar_t cv_glbatching = CVAR_INIT ("gr_batching", "On", 0, CV_OnOff, NULL);
 static void CV_glfiltermode_OnChange(void)
 {
 	if (rendermode == render_opengl)
-		HWD.pfnSetState(GPU_STATE_TEXTUREFILTERMODE, cv_glfiltermode.value);
+		GPU->SetState(GPU_STATE_TEXTUREFILTERMODE, cv_glfiltermode.value);
 }
 
 static void CV_glanisotropic_OnChange(void)
 {
 	if (rendermode == render_opengl)
-		HWD.pfnSetState(GPU_STATE_TEXTUREANISOTROPICMODE, cv_glanisotropicmode.value);
+		GPU->SetState(GPU_STATE_TEXTUREANISOTROPICMODE, cv_glanisotropicmode.value);
 }
 
 //added by Hurdler: console varibale that are saved
@@ -6372,8 +6367,8 @@ void HWR_Switch(void)
 		HWR_AddSessionCommands();
 
 	// Set special states from CVARs
-	HWD.pfnSetState(GPU_STATE_TEXTUREFILTERMODE, cv_glfiltermode.value);
-	HWD.pfnSetState(GPU_STATE_TEXTUREANISOTROPICMODE, cv_glanisotropicmode.value);
+	GPU->SetState(GPU_STATE_TEXTUREFILTERMODE, cv_glfiltermode.value);
+	GPU->SetState(GPU_STATE_TEXTUREANISOTROPICMODE, cv_glanisotropicmode.value);
 
 	// Load textures
 	if (!gl_maptexturesloaded)
@@ -6393,7 +6388,7 @@ void HWR_Shutdown(void)
 	HWR_FreeExtraSubsectors();
 	HWR_FreePolyPool();
 	HWR_FreeMapTextures();
-	HWD.pfnFlushScreenTextures();
+	GPU->FlushScreenTextures();
 }
 
 void transform(float *cx, float *cy, float *cz)
@@ -6473,14 +6468,14 @@ void HWR_RenderWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, UINT32 blend, bo
 
 INT32 HWR_GetTextureUsed(void)
 {
-	return HWD.pfnGetTextureUsed();
+	return GPU->GetTextureUsed();
 }
 
 void HWR_DoPostProcessor(player_t *player)
 {
 	postimg_t *type;
 
-	HWD.pfnUnSetShader();
+	GPU->UnSetShader();
 
 	if (splitscreen && player == &players[secondarydisplayplayer])
 		type = &postimgtype2;
@@ -6509,12 +6504,12 @@ void HWR_DoPostProcessor(player_t *player)
 
 		Surf.PolyColor.s.alpha = 0xc0; // match software mode
 
-		HWD.pfnDrawPolygon(&Surf, v, 4, PF_Modulated|PF_AdditiveSource|PF_NoTexture|PF_NoDepthTest);
+		GPU->DrawPolygon(&Surf, v, 4, PF_Modulated|PF_AdditiveSource|PF_NoTexture|PF_NoDepthTest);
 	}
 
 	// Capture the screen for intermission and screen waving
 	if(gamestate != GS_INTERMISSION)
-		HWD.pfnMakeScreenTexture();
+		GPU->MakeScreenTexture();
 
 	if (splitscreen) // Not supported in splitscreen - someone want to add support?
 		return;
@@ -6553,13 +6548,13 @@ void HWR_DoPostProcessor(player_t *player)
 				v[x][y][1] = (y/((float)(SCREENVERTS-1.0f)/9.0f))-4.5f;
 			}
 		}
-		HWD.pfnPostImgRedraw(v);
+		GPU->PostImgRedraw(v);
 		if (!(paused || P_AutoPause()))
 			disStart += 1;
 
 		// Capture the screen again for screen waving on the intermission
 		if(gamestate != GS_INTERMISSION)
-			HWD.pfnMakeScreenTexture();
+			GPU->MakeScreenTexture();
 	}
 	// Flipping of the screen isn't done here anymore
 }
@@ -6567,18 +6562,18 @@ void HWR_DoPostProcessor(player_t *player)
 void HWR_StartScreenWipe(void)
 {
 	//CONS_Debug(DBG_RENDER, "In HWR_StartScreenWipe()\n");
-	HWD.pfnStartScreenWipe();
+	GPU->StartScreenWipe();
 }
 
 void HWR_EndScreenWipe(void)
 {
 	//CONS_Debug(DBG_RENDER, "In HWR_EndScreenWipe()\n");
-	HWD.pfnEndScreenWipe();
+	GPU->EndScreenWipe();
 }
 
 void HWR_DrawIntermissionBG(void)
 {
-	HWD.pfnDrawIntermissionBG();
+	GPU->DrawIntermissionBG();
 }
 
 //
@@ -6623,7 +6618,7 @@ void HWR_DoWipe(UINT8 wipenum, UINT8 scrnnum)
 		return;
 
 	HWR_GetFadeMask(wipelumpnum);
-	HWD.pfnDoScreenWipe();
+	GPU->DoScreenWipe();
 }
 
 void HWR_DoTintedWipe(UINT8 wipenum, UINT8 scrnnum)
@@ -6634,12 +6629,12 @@ void HWR_DoTintedWipe(UINT8 wipenum, UINT8 scrnnum)
 
 void HWR_MakeScreenFinalTexture(void)
 {
-    HWD.pfnMakeScreenFinalTexture();
+    GPU->MakeScreenFinalTexture();
 }
 
 void HWR_DrawScreenFinalTexture(int width, int height)
 {
-    HWD.pfnDrawScreenFinalTexture(width, height);
+    GPU->DrawScreenFinalTexture(width, height);
 }
 
 // jimita 18032019
@@ -6658,7 +6653,7 @@ static inline UINT16 HWR_FindShaderDefs(UINT16 wadnum)
 
 boolean HWR_CompileShaders(void)
 {
-	return HWD.pfnCompileShaders();
+	return GPU->CompileShaders();
 }
 
 FShaderReferenceArray shaderxlat[] =
@@ -6786,7 +6781,7 @@ skip_lump:
 					shader_source = Z_Malloc(shader_size, PU_STATIC, NULL);
 					W_ReadLumpPwad(wadnum, shader_lumpnum, shader_source);
 
-					HWD.pfnLoadCustomShader(shaderxlat[i].id, shader_source, shader_size, (shadertype == 2));
+					GPU->LoadCustomShader(shaderxlat[i].id, shader_source, shader_size, (shadertype == 2));
 
 					Z_Free(shader_source);
 					Z_Free(shader_lumpname);
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 64b20c7163474a39ff4dcb9f9116e76f3c3d65d8..a021f079d55a5e0eb421ef948d07e1247c0c3dbf 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -25,7 +25,7 @@
 #include "../fastcmp.h"
 
 #ifdef HWRENDER
-#include "hw_drv.h"
+#include "hw_gpu.h"
 #include "hw_light.h"
 #include "hw_md2.h"
 #include "../d_main.h"
@@ -418,7 +418,7 @@ static void md2_loadTexture(md2_t *model)
 			image++;
 		}
 	}
-	HWD.pfnSetTexture(grPatch->texture);
+	GPU->SetTexture(grPatch->texture);
 }
 
 // -----------------+
@@ -472,7 +472,7 @@ static void md2_loadBlendTexture(md2_t *model)
 		grPatch->texture->width = (UINT16)w;
 		grPatch->texture->height = (UINT16)h;
 	}
-	HWD.pfnSetTexture(grPatch->texture); // We do need to do this so that it can be cleared and knows to recreate it when necessary
+	GPU->SetTexture(grPatch->texture); // We do need to do this so that it can be cleared and knows to recreate it when necessary
 
 	Z_Free(filename);
 }
@@ -1089,7 +1089,7 @@ static void HWR_GetBlendedTexture(patch_t *patch, patch_t *blendpatch, INT32 ski
 	if (blendpatch == NULL || colormap == colormaps || colormap == NULL)
 	{
 		// Don't do any blending
-		HWD.pfnSetTexture(grPatch->texture);
+		GPU->SetTexture(grPatch->texture);
 		return;
 	}
 
@@ -1097,7 +1097,7 @@ static void HWR_GetBlendedTexture(patch_t *patch, patch_t *blendpatch, INT32 ski
 		&& (patch->width != blendpatch->width || patch->height != blendpatch->height))
 	{
 		// Blend image exists, but it's bad.
-		HWD.pfnSetTexture(grPatch->texture);
+		GPU->SetTexture(grPatch->texture);
 		return;
 	}
 
@@ -1110,7 +1110,7 @@ static void HWR_GetBlendedTexture(patch_t *patch, patch_t *blendpatch, INT32 ski
 		{
 			if (hwrTexture->downloaded && hwrTexture->data)
 			{
-				HWD.pfnSetTexture(hwrTexture); // found the colormap, set it to the correct texture
+				GPU->SetTexture(hwrTexture); // found the colormap, set it to the correct texture
 				Z_ChangeTag(hwrTexture->data, PU_HWRMODELTEXTURE_UNLOCKED);
 				return;
 			}
@@ -1127,7 +1127,7 @@ static void HWR_GetBlendedTexture(patch_t *patch, patch_t *blendpatch, INT32 ski
 
 	HWR_CreateBlendedTexture(patch, blendpatch, newTexture, skinnum, color);
 
-	HWD.pfnSetTexture(newTexture);
+	GPU->SetTexture(newTexture);
 	Z_ChangeTag(newTexture->data, PU_HWRMODELTEXTURE_UNLOCKED);
 }
 
@@ -1407,7 +1407,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 				// note down the max_s and max_t that end up in the VBO
 				md2->model->vbo_max_s = md2->model->max_s;
 				md2->model->vbo_max_t = md2->model->max_t;
-				HWD.pfnCreateModelVBOs(md2->model);
+				GPU->CreateModelVBOs(md2->model);
 			}
 			else
 			{
@@ -1417,7 +1417,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 			}
 		}
 
-		//HWD.pfnSetBlend(blend); // This seems to actually break translucency?
+		//GPU->SetBlend(blend); // This seems to actually break translucency?
 		finalscale = md2->scale;
 		//Hurdler: arf, I don't like that implementation at all... too much crappy
 
@@ -1624,8 +1624,8 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 		p.mirror = atransform.mirror; // from Kart
 #endif
 
-		HWD.pfnSetShader(SHADER_MODEL);	// model shader
-		HWD.pfnDrawModel(md2->model, frame, durs, tics, nextFrame, &p, finalscale, flip, hflip, &Surf);
+		GPU->SetShader(SHADER_MODEL);	// model shader
+		GPU->DrawModel(md2->model, frame, durs, tics, nextFrame, &p, finalscale, flip, hflip, &Surf);
 	}
 
 	return true;
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index 096fb482e482df3ca6421c1f80feaca3ddfce0c8..4275d7daee22ce7359a7724533464ee304a27429 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -873,7 +873,7 @@ void SetupGLFunc4(void)
 }
 
 // jimita
-EXPORT boolean HWRAPI(CompileShaders) (void)
+static boolean CompileShaders(void)
 {
 #ifdef GL_SHADERS
 	GLint i;
@@ -937,7 +937,7 @@ EXPORT boolean HWRAPI(CompileShaders) (void)
 // Those are given to the uniforms.
 //
 
-EXPORT void HWRAPI(SetShaderInfo) (INT32 info, INT32 value)
+static void SetShaderInfo(INT32 info, INT32 value)
 {
 #ifdef GL_SHADERS
 	switch (info)
@@ -957,7 +957,7 @@ EXPORT void HWRAPI(SetShaderInfo) (INT32 info, INT32 value)
 //
 // Custom shader loading
 //
-EXPORT void HWRAPI(LoadCustomShader) (int number, char *code, size_t size, boolean isfragment)
+static void LoadCustomShader(int number, char *code, size_t size, boolean isfragment)
 {
 #ifdef GL_SHADERS
 	FShaderSource *shader;
@@ -992,8 +992,7 @@ EXPORT void HWRAPI(LoadCustomShader) (int number, char *code, size_t size, boole
 	(void)fragment;
 #endif
 }
-
-EXPORT void HWRAPI(SetShader) (int type)
+static void SetShader(int type)
 {
 #ifdef GL_SHADERS
 	if (ShadersAllowed != GPU_SHADEROPTION_OFF)
@@ -1035,8 +1034,7 @@ EXPORT void HWRAPI(SetShader) (int type)
 #endif
 	ShadersEnabled = GL_FALSE;
 }
-
-EXPORT void HWRAPI(UnSetShader) (void)
+static void UnSetShader(void)
 {
 #ifdef GL_SHADERS
 	ShaderState.current = NULL;
@@ -1049,8 +1047,7 @@ EXPORT void HWRAPI(UnSetShader) (void)
 
 	ShadersEnabled = GL_FALSE;
 }
-
-EXPORT void HWRAPI(CleanShaders) (void)
+static void CleanShaders(void)
 {
 	INT32 i;
 
@@ -1257,7 +1254,7 @@ void SetStates(void)
 // -----------------+
 // DeleteTexture    : Deletes a texture from the GPU and frees its data
 // -----------------+
-EXPORT void HWRAPI(DeleteTexture) (HWRTexture_t *pTexInfo)
+static void DeleteTexture(HWRTexture_t *pTexInfo)
 {
 	FTextureInfo *head = TexCacheHead;
 
@@ -1348,16 +1345,16 @@ INT32 isExtAvailable(const char *extension, const GLubyte *start)
 // Init             : Initialise the OpenGL interface API
 // Returns          :
 // -----------------+
-EXPORT boolean HWRAPI(Init) (void)
+static boolean Init(void)
 {
-	return LoadGL();
+	return SetupGLfunc();
 }
 
 
 // -----------------+
 // ClearTextureCache: Flush OpenGL textures from memory
 // -----------------+
-EXPORT void HWRAPI(ClearTextureCache) (void)
+static void ClearTextureCache(void)
 {
 	// GL_DBG_Printf ("HWR_Flush(exe)\n");
 	Flush();
@@ -1369,7 +1366,7 @@ EXPORT void HWRAPI(ClearTextureCache) (void)
 //                  : store pixels as 16bit 565 RGB
 // Returns          : 16bit 565 RGB pixel array stored in dst_data
 // -----------------+
-EXPORT void HWRAPI(ReadRect) (INT32 x, INT32 y, INT32 width, INT32 height,
+static void ReadRect(INT32 x, INT32 y, INT32 width, INT32 height,
                                 INT32 dst_stride, UINT16 * dst_data)
 {
 	INT32 i;
@@ -1419,7 +1416,7 @@ EXPORT void HWRAPI(ReadRect) (INT32 x, INT32 y, INT32 width, INT32 height,
 // -----------------+
 // GClipRect        : Defines the 2D hardware clipping window
 // -----------------+
-EXPORT void HWRAPI(GClipRect) (INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float nearclip)
+static void GClipRect(INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float nearclip)
 {
 	// GL_DBG_Printf ("GClipRect(%d, %d, %d, %d)\n", minx, miny, maxx, maxy);
 
@@ -1438,10 +1435,25 @@ EXPORT void HWRAPI(GClipRect) (INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, f
 }
 
 
+// -----------------+
+// SetPalette       : Changes the current texture palette
+// -----------------+
+static void SetPalette(RGBA_t *palette)
+{
+	size_t palsize = (sizeof(RGBA_t) * 256);
+	// on a palette change, you have to reload all of the textures
+	if (memcmp(&GPUTexturePalette, palette, palsize))
+	{
+		memcpy(&GPUTexturePalette, palette, palsize);
+		Flush();
+	}
+}
+
+
 // -----------------+
 // ClearBuffer      : Clear the color/alpha/depth buffer(s)
 // -----------------+
-EXPORT void HWRAPI(ClearBuffer) (boolean ColorMask, boolean DepthMask, FRGBAFloat *ClearColor)
+static void ClearBuffer(boolean ColorMask, boolean DepthMask, FRGBAFloat *ClearColor)
 {
 	// GL_DBG_Printf ("ClearBuffer(%d)\n", alpha);
 	GLbitfield ClearMask = 0;
@@ -1471,7 +1483,7 @@ EXPORT void HWRAPI(ClearBuffer) (boolean ColorMask, boolean DepthMask, FRGBAFloa
 // -----------------+
 // HWRAPI Draw2DLine: Render a 2D line
 // -----------------+
-EXPORT void HWRAPI(Draw2DLine) (F2DCoord * v1,
+static void Draw2DLine(F2DCoord * v1,
                                    F2DCoord * v2,
                                    RGBA_t Color)
 {
@@ -1601,8 +1613,7 @@ static void SetBlendMode(UINT32 flags)
 			break;
 	}
 }
-
-EXPORT void HWRAPI(SetBlend) (UINT32 PolyFlags)
+static void SetBlend(UINT32 PolyFlags)
 {
 	UINT32 Xor;
 	Xor = CurrentPolyFlags^PolyFlags;
@@ -1728,7 +1739,7 @@ static void UploadTexture(HWRTexture_t *pTexInfo, const GLvoid *pTextureBuffer,
 // -----------------+
 // UpdateTexture    : Updates the texture data.
 // -----------------+
-EXPORT void HWRAPI(UpdateTexture) (HWRTexture_t *pTexInfo)
+static void UpdateTexture(HWRTexture_t *pTexInfo)
 {
 	boolean update = true;
 	static RGBA_t textureBuffer[2048 * 2048];
@@ -1870,7 +1881,7 @@ EXPORT void HWRAPI(UpdateTexture) (HWRTexture_t *pTexInfo)
 // -----------------+
 // SetTexture       : The texture becomes the current source
 // -----------------+
-EXPORT void HWRAPI(SetTexture) (HWRTexture_t *pTexInfo)
+static void SetTexture(HWRTexture_t *pTexInfo)
 {
 	if (!pTexInfo)
 	{
@@ -2196,7 +2207,7 @@ static void PreparePolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 Po
 // -----------------+
 // DrawPolygon      : Render a polygon, set the texture, set render mode
 // -----------------+
-EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumPts, UINT32 PolyFlags)
+static void DrawPolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumPts, UINT32 PolyFlags)
 {
 	PreparePolygon(pSurf, pOutVerts, PolyFlags);
 
@@ -2213,8 +2224,7 @@ EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, UIN
 	if (PolyFlags & PF_ForceWrapY)
 		Clamp2D(GL_TEXTURE_WRAP_T);
 }
-
-EXPORT void HWRAPI(DrawIndexedTriangles) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumPts, UINT32 PolyFlags, UINT32 *IndexArray)
+static void DrawIndexedTriangles(FSurfaceInfo *pSurf, FOutVector *pOutVerts, UINT32 iNumPts, UINT32 PolyFlags, UINT32 *IndexArray)
 {
 	PreparePolygon(pSurf, pOutVerts, PolyFlags);
 
@@ -2231,8 +2241,7 @@ static const boolean gl_ext_arb_vertex_buffer_object = true;
 #define sky_vbo_x (gl_ext_arb_vertex_buffer_object ? &NULL_VBO_VERTEX->x : &sky->data[0].x)
 #define sky_vbo_u (gl_ext_arb_vertex_buffer_object ? &NULL_VBO_VERTEX->u : &sky->data[0].u)
 #define sky_vbo_r (gl_ext_arb_vertex_buffer_object ? &NULL_VBO_VERTEX->r : &sky->data[0].r)
-
-EXPORT void HWRAPI(RenderSkyDome) (FSkyDome *sky)
+static void DrawSkyDome(FSkyDome *sky)
 {
 	int i, j;
 
@@ -2319,7 +2328,7 @@ EXPORT void HWRAPI(RenderSkyDome) (FSkyDome *sky)
 // ==========================================================================
 //
 // ==========================================================================
-EXPORT void HWRAPI(SetState) (INT32 State, INT32 Value)
+static void SetState(INT32 State, INT32 Value)
 {
 	switch (State)
 	{
@@ -2550,8 +2559,7 @@ static void CreateModelVBOTiny(mesh_t *mesh, tinyframe_t *frame)
 	// since this is called mid-frame
 	pglBindBuffer(GL_ARRAY_BUFFER, 0);
 }
-
-EXPORT void HWRAPI(CreateModelVBOs) (model_t *model)
+static void CreateModelVBOs(model_t *model)
 {
 	int i;
 	for (i = 0; i < model->numMeshes; i++)
@@ -2892,7 +2900,7 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32
 // -----------------+
 // HWRAPI DrawModel : Draw a model
 // -----------------+
-EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface)
+static void DrawModel(model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface)
 {
 	DrawModelEx(model, frameIndex, duration, tics, nextFrameIndex, pos, scale, flipped, hflipped, Surface);
 }
@@ -2900,7 +2908,7 @@ EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, INT32 duration,
 // -----------------+
 // SetTransform     :
 // -----------------+
-EXPORT void HWRAPI(SetTransform) (FTransform *stransform)
+static void SetTransform(FTransform *stransform)
 {
 	static boolean special_splitscreen;
 	boolean shearing = false;
@@ -2965,8 +2973,7 @@ EXPORT void HWRAPI(SetTransform) (FTransform *stransform)
 	pglGetFloatv(GL_MODELVIEW_MATRIX, ModelMatrix); // added for new coronas' code (without depth buffer)
 
 }
-
-EXPORT INT32  HWRAPI(GetTextureUsed) (void)
+static INT32 GetTextureUsed(void)
 {
 	FTextureInfo *tmp = TexCacheHead;
 	INT32 res = 0;
@@ -2989,8 +2996,7 @@ EXPORT INT32  HWRAPI(GetTextureUsed) (void)
 
 	return res;
 }
-
-EXPORT void HWRAPI(PostImgRedraw) (float points[SCREENVERTS][SCREENVERTS][2])
+static void PostImgRedraw(float points[SCREENVERTS][SCREENVERTS][2])
 {
 	INT32 x, y;
 	float float_x, float_y, float_nextx, float_nexty;
@@ -3081,7 +3087,7 @@ EXPORT void HWRAPI(PostImgRedraw) (float points[SCREENVERTS][SCREENVERTS][2])
 
 // Sryder:	This needs to be called whenever the screen changes resolution in order to reset the screen textures to use
 //			a new size
-EXPORT void HWRAPI(FlushScreenTextures) (void)
+static void FlushScreenTextures(void)
 {
 	pglDeleteTextures(1, &ScreenTexture);
 	pglDeleteTextures(1, &FinalScreenTexture);
@@ -3123,19 +3129,19 @@ static void GenerateScreenTexture(GLuint *name)
 }
 
 // Create screen to fade from
-EXPORT void HWRAPI(StartScreenWipe) (void)
+static void StartScreenWipe(void)
 {
 	GenerateScreenTexture(&WipeStartTexture);
 }
 
 // Create screen to fade to
-EXPORT void HWRAPI(EndScreenWipe)(void)
+static void EndScreenWipe(void)
 {
 	GenerateScreenTexture(&WipeEndTexture);
 }
 
 // Draw the last scene under the intermission
-EXPORT void HWRAPI(DrawIntermissionBG)(void)
+static void DrawIntermissionBG(void)
 {
 	float xfix, yfix;
 	INT32 texsize = 2048;
@@ -3183,7 +3189,7 @@ EXPORT void HWRAPI(DrawIntermissionBG)(void)
 }
 
 // Do screen fades!
-EXPORT void HWRAPI(DoScreenWipe)(void)
+static void DoScreenWipe(void)
 {
 	INT32 texsize = 2048;
 	float xfix, yfix;
@@ -3274,17 +3280,17 @@ EXPORT void HWRAPI(DoScreenWipe)(void)
 }
 
 // Create a texture from the screen.
-EXPORT void HWRAPI(MakeScreenTexture) (void)
+static void MakeScreenTexture(void)
 {
 	GenerateScreenTexture(&ScreenTexture);
 }
 
-EXPORT void HWRAPI(MakeScreenFinalTexture) (void)
+static void MakeScreenFinalTexture(void)
 {
 	GenerateScreenTexture(&FinalScreenTexture);
 }
 
-EXPORT void HWRAPI(DrawScreenFinalTexture)(int width, int height)
+static void DrawScreenFinalTexture(int width, int height)
 {
 	float xfix, yfix;
 	float origaspect, newaspect;
@@ -3356,4 +3362,53 @@ EXPORT void HWRAPI(DrawScreenFinalTexture)(int width, int height)
 	CurrentTexture = FinalScreenTexture;
 }
 
+struct GPURenderingAPI GLInterfaceAPI = {
+	Init,
+	NULL,
+
+	SetState,
+	SetTransform,
+	SetBlend,
+	SetPalette,
+	ClearBuffer,
+
+	DrawPolygon,
+	DrawIndexedTriangles,
+	Draw2DLine,
+	DrawModel,
+	DrawSkyDome,
+
+	SetTexture,
+	UpdateTexture,
+	DeleteTexture,
+
+	ClearTextureCache,
+	GetTextureUsed,
+
+	CreateModelVBOs,
+
+	ReadRect,
+	GClipRect,
+
+	MakeScreenTexture,
+	MakeScreenFinalTexture,
+	FlushScreenTextures,
+
+	StartScreenWipe,
+	EndScreenWipe,
+	DoScreenWipe,
+	DrawIntermissionBG,
+	DrawScreenFinalTexture,
+
+	PostImgRedraw,
+
+	CompileShaders,
+	CleanShaders,
+	SetShader,
+	UnSetShader,
+
+	SetShaderInfo,
+	LoadCustomShader,
+};
+
 #endif //HWRENDER
diff --git a/src/hardware/r_opengl/r_opengl.h b/src/hardware/r_opengl/r_opengl.h
index 68a276db5dd055b1643a702b34b3199cdbe0eb9f..5ce8670afe58abe086b1f71ca28053ef9825e461 100644
--- a/src/hardware/r_opengl/r_opengl.h
+++ b/src/hardware/r_opengl/r_opengl.h
@@ -45,7 +45,7 @@
 
 #define  _CREATE_DLL_  // necessary for Unix AND Windows
 #include "../../doomdef.h"
-#include "../hw_drv.h"
+#include "../hw_gpu.h"
 
 // ==========================================================================
 //                                                                DEFINITIONS
@@ -74,7 +74,6 @@ extern FILE             *gllogstream;
 //                                                                     PROTOS
 // ==========================================================================
 
-boolean LoadGL(void);
 void *GetGLFunc(const char *proc);
 boolean SetupGLfunc(void);
 void SetupGLFunc4(void);
@@ -128,4 +127,6 @@ extern GLint            GPUScreenHeight;
 extern GLbyte           GPUScreenDepth;
 extern GLint            GPUMaximumAnisotropy;
 
+extern struct GPURenderingAPI GLInterfaceAPI;
+
 #endif
diff --git a/src/r_things.c b/src/r_things.c
index 08337392742fe3775f3faa2d1f0a073937736fc6..370fc70a458235a0a67232b803be4e38b79f45b1 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -39,7 +39,7 @@
 #include "hardware/hw_md2.h"
 #include "hardware/hw_glob.h"
 #include "hardware/hw_light.h"
-#include "hardware/hw_drv.h"
+#include "hardware/hw_gpu.h"
 #endif
 
 #define MINZ (FRACUNIT*4)
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj
index d46a4af2b0d89ea1dc93b0573e4ef1d6a32665b4..8e863da42dbbe74b0a49488257b737191a66aca8 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj
+++ b/src/sdl/Srb2SDL-vc10.vcxproj
@@ -228,7 +228,7 @@
     <ClInclude Include="..\hardware\hw_data.h" />
     <ClInclude Include="..\hardware\hw_defs.h" />
     <ClInclude Include="..\hardware\hw_dll.h" />
-    <ClInclude Include="..\hardware\hw_drv.h" />
+    <ClInclude Include="..\hardware\hw_gpu.h" />
     <ClInclude Include="..\hardware\hw_glob.h" />
     <ClInclude Include="..\hardware\hw_light.h" />
     <ClInclude Include="..\hardware\hw_main.h" />
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters
index adae2f446dbde8267e375bb794eacc50bed9c663..973ed7a75c89b35fea9ae64311d3aa36c9df6fad 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj.filters
+++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters
@@ -234,7 +234,7 @@
     <ClInclude Include="..\hardware\hw_dll.h">
       <Filter>Hw_Hardware</Filter>
     </ClInclude>
-    <ClInclude Include="..\hardware\hw_drv.h">
+    <ClInclude Include="..\hardware\hw_gpu.h">
       <Filter>Hw_Hardware</Filter>
     </ClInclude>
     <ClInclude Include="..\hardware\hw_glob.h">
diff --git a/src/sdl/Srb2SDL-vc9.vcproj b/src/sdl/Srb2SDL-vc9.vcproj
index 95f035267026215279b91d1d309b08be34e6e29e..969fdff4e2e66ede9b7c223ab82bfbf9e711ac3d 100644
--- a/src/sdl/Srb2SDL-vc9.vcproj
+++ b/src/sdl/Srb2SDL-vc9.vcproj
@@ -2539,7 +2539,7 @@
 				</FileConfiguration>
 			</File>
 			<File
-				RelativePath="..\hardware\hw_drv.h"
+				RelativePath="..\hardware\hw_gpu.h"
 				>
 			</File>
 			<File
diff --git a/src/sdl/hwsym_sdl.c b/src/sdl/hwsym_sdl.c
index 5ca424e8a07bb8512054daedfaf95da771539be2..7dcf91823be97d3001030cb7abbc004fda7fcb90 100644
--- a/src/sdl/hwsym_sdl.c
+++ b/src/sdl/hwsym_sdl.c
@@ -43,14 +43,6 @@
 
 #define  _CREATE_DLL_  // necessary for Unix AND Windows
 
-#ifdef HWRENDER
-#include "../hardware/hw_drv.h"
-#include "ogl_sdl.h"
-#ifdef STATIC_OPENGL
-#include "../hardware/r_opengl/r_opengl.h"
-#endif
-#endif
-
 #ifdef HW3SOUND
 #include "../hardware/hw3dsdrv.h"
 #endif
@@ -73,50 +65,6 @@
 void *hwSym(const char *funcName,void *handle)
 {
 	void *funcPointer = NULL;
-#ifdef HWRENDER
-	if (0 == strcmp("SetPalette", funcName))
-		funcPointer = &OglSdlSetPalette;
-
-	GETFUNC(Init);
-	GETFUNC(Draw2DLine);
-	GETFUNC(DrawPolygon);
-	GETFUNC(DrawIndexedTriangles);
-	GETFUNC(RenderSkyDome);
-	GETFUNC(SetBlend);
-	GETFUNC(ClearBuffer);
-	GETFUNC(SetTexture);
-	GETFUNC(UpdateTexture);
-	GETFUNC(DeleteTexture);
-	GETFUNC(ReadRect);
-	GETFUNC(GClipRect);
-	GETFUNC(ClearTextureCache);
-	GETFUNC(SetState);
-	GETFUNC(GetTextureUsed);
-	GETFUNC(DrawModel);
-	GETFUNC(CreateModelVBOs);
-	GETFUNC(SetTransform);
-	GETFUNC(PostImgRedraw);
-	GETFUNC(FlushScreenTextures);
-	GETFUNC(StartScreenWipe);
-	GETFUNC(EndScreenWipe);
-	GETFUNC(DoScreenWipe);
-	GETFUNC(DrawIntermissionBG);
-	GETFUNC(MakeScreenTexture);
-	GETFUNC(MakeScreenFinalTexture);
-	GETFUNC(DrawScreenFinalTexture);
-
-	GETFUNC(CompileShaders);
-	GETFUNC(CleanShaders);
-	GETFUNC(SetShader);
-	GETFUNC(UnSetShader);
-
-	GETFUNC(SetShaderInfo);
-	GETFUNC(LoadCustomShader);
-
-#else //HWRENDER
-	if (0 == strcmp("FinishUpdate", funcName))
-		return funcPointer; //&FinishUpdate;
-#endif //!HWRENDER
 #ifdef STATIC3DS
 	GETFUNC(Startup);
 	GETFUNC(AddSfx);
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index f7ed27e32a91990f3d3ba3977af85e60463f873d..2eb7c7496ab85491b42bfd4caa36c442e3f45bfa 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -77,7 +77,7 @@
 #include "sdlmain.h"
 #ifdef HWRENDER
 #include "../hardware/hw_main.h"
-#include "../hardware/hw_drv.h"
+#include "../hardware/hw_gpu.h"
 // For dynamic referencing of HW rendering functions
 #include "hwsym_sdl.h"
 #include "ogl_sdl.h"
@@ -1851,51 +1851,18 @@ void VID_StartupOpenGL(void)
 	if (!glstartup)
 	{
 		CONS_Printf("VID_StartupOpenGL()...\n");
-		HWD.pfnInit             = hwSym("Init",NULL);
-		HWD.pfnFinishUpdate     = NULL;
-		HWD.pfnDraw2DLine       = hwSym("Draw2DLine",NULL);
-		HWD.pfnDrawPolygon      = hwSym("DrawPolygon",NULL);
-		HWD.pfnDrawIndexedTriangles = hwSym("DrawIndexedTriangles",NULL);
-		HWD.pfnRenderSkyDome    = hwSym("RenderSkyDome",NULL);
-		HWD.pfnSetBlend         = hwSym("SetBlend",NULL);
-		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.pfnClearTextureCache= hwSym("ClearTextureCache",NULL);
-		HWD.pfnSetState         = hwSym("SetState",NULL);
-		HWD.pfnSetPalette       = hwSym("SetPalette",NULL);
-		HWD.pfnGetTextureUsed   = hwSym("GetTextureUsed",NULL);
-		HWD.pfnDrawModel        = hwSym("DrawModel",NULL);
-		HWD.pfnCreateModelVBOs  = hwSym("CreateModelVBOs",NULL);
-		HWD.pfnSetTransform     = hwSym("SetTransform",NULL);
-		HWD.pfnPostImgRedraw    = hwSym("PostImgRedraw",NULL);
-		HWD.pfnFlushScreenTextures=hwSym("FlushScreenTextures",NULL);
-		HWD.pfnStartScreenWipe  = hwSym("StartScreenWipe",NULL);
-		HWD.pfnEndScreenWipe    = hwSym("EndScreenWipe",NULL);
-		HWD.pfnDoScreenWipe     = hwSym("DoScreenWipe",NULL);
-		HWD.pfnDrawIntermissionBG=hwSym("DrawIntermissionBG",NULL);
-		HWD.pfnMakeScreenTexture= hwSym("MakeScreenTexture",NULL);
-		HWD.pfnMakeScreenFinalTexture=hwSym("MakeScreenFinalTexture",NULL);
-		HWD.pfnDrawScreenFinalTexture=hwSym("DrawScreenFinalTexture",NULL);
-
-		HWD.pfnCompileShaders   = hwSym("CompileShaders",NULL);
-		HWD.pfnCleanShaders     = hwSym("CleanShaders",NULL);
-		HWD.pfnSetShader        = hwSym("SetShader",NULL);
-		HWD.pfnUnSetShader      = hwSym("UnSetShader",NULL);
-
-		HWD.pfnSetShaderInfo    = hwSym("SetShaderInfo",NULL);
-		HWD.pfnLoadCustomShader = hwSym("LoadCustomShader",NULL);
-
-		vid.glstate = HWD.pfnInit() ? VID_GL_LIBRARY_LOADED : VID_GL_LIBRARY_ERROR; // let load the OpenGL library
-
-		if (vid.glstate == VID_GL_LIBRARY_ERROR)
+
+		GPUInterface_Load(&GPU);
+
+		if (VID_LoadGPUAPI() && GPU->Init())
+			vid.glstate = VID_GL_LIBRARY_LOADED;
+		else
 		{
+			vid.glstate = VID_GL_LIBRARY_ERROR;
 			rendermode = render_soft;
 			setrenderneeded = 0;
 		}
+
 		glstartup = true;
 	}
 #endif
diff --git a/src/sdl/ogl_sdl.c b/src/sdl/ogl_sdl.c
index 6ef12cbedc4196babd52220cb2f4e36886d60a08..2c4ee341497019dee544b981e0b037b2527176e6 100644
--- a/src/sdl/ogl_sdl.c
+++ b/src/sdl/ogl_sdl.c
@@ -84,7 +84,7 @@ void *GetGLFunc(const char *proc)
 	return SDL_GL_GetProcAddress(proc);
 }
 
-boolean LoadGL(void)
+boolean VID_LoadGPUAPI(void)
 {
 #ifndef STATIC_OPENGL
 	const char *OGLLibname = NULL;
@@ -108,7 +108,7 @@ boolean LoadGL(void)
 	GLULibname = "GLU32.DLL";
 #elif defined (__MACH__)
 	GLULibname = "/System/Library/Frameworks/OpenGL.framework/Libraries/libGLU.dylib";
-#elif defined (macintos)
+#elif defined (macintosh)
 	GLULibname = "OpenGLLibrary";
 #elif defined (__unix__)
 	GLULibname = "libGLU.so.1";
@@ -125,7 +125,7 @@ boolean LoadGL(void)
 	{
 		GLUhandle = hwOpen(GLULibname);
 		if (GLUhandle)
-			return SetupGLfunc();
+			return true;
 		else
 		{
 			CONS_Alert(CONS_ERROR, "Could not load GLU Library: %s\n", GLULibname);
@@ -138,8 +138,9 @@ boolean LoadGL(void)
 		CONS_Alert(CONS_ERROR, "Could not load GLU Library\n");
 		CONS_Alert(CONS_ERROR, "If you know what is the GLU library's name, use -GLUlib\n");
 	}
+
+	return true;
 #endif
-	return SetupGLfunc();
 }
 
 /**	\brief	The OglSdlSurface function
@@ -230,16 +231,5 @@ void OglSdlFinishUpdate(boolean waitvbl)
 	HWR_DrawScreenFinalTexture(realwidth, realheight);
 }
 
-EXPORT void HWRAPI(OglSdlSetPalette) (RGBA_t *palette)
-{
-	size_t palsize = (sizeof(RGBA_t) * 256);
-	// on a palette change, you have to reload all of the textures
-	if (memcmp(&GPUTexturePalette, palette, palsize))
-	{
-		memcpy(&GPUTexturePalette, palette, palsize);
-		Flush();
-	}
-}
-
 #endif //HWRENDER
 #endif //SDL
diff --git a/src/sdl/ogl_sdl.h b/src/sdl/ogl_sdl.h
index 748e30bae06036c2785cb249e92f6798dc67f4f1..68187e819e1d9866574d9c9a7b109496b392f8cc 100644
--- a/src/sdl/ogl_sdl.h
+++ b/src/sdl/ogl_sdl.h
@@ -14,7 +14,7 @@
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 // GNU General Public License for more details.
 //-----------------------------------------------------------------------------
-/// \file
+/// \file ogl_sdl.h
 /// \brief SDL specific part of the OpenGL API for SRB2
 
 #include "../v_video.h"
@@ -22,14 +22,9 @@
 extern void *GLUhandle;
 
 boolean OglSdlSurface(INT32 w, INT32 h);
-
 void OglSdlFinishUpdate(boolean vidwait);
 
 extern SDL_Renderer *renderer;
 extern SDL_GLContext sdlglcontext;
 extern Uint16      realwidth;
 extern Uint16      realheight;
-
-#ifdef _CREATE_DLL_
-EXPORT void HWRAPI( OglSdlSetPalette ) (RGBA_t *palette);
-#endif