diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 1cfffca541d048fece90d1b1970b5b2f62ab69bc..a08e8e24d84fbaa9493337dff0efb192fccbddbf 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -544,6 +544,9 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool
 		PolyFlags |= PF_ColorMapped;
 	}
 
+	if (!cv_renderfloors.value)
+		return;
+
 	HWR_ProcessPolygon(&Surf, planeVerts, nrPlaneVerts, PolyFlags, shader, false);
 
 	if (subsector)
@@ -772,6 +775,9 @@ static void HWR_ProjectWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, FBITFIEL
 {
 	INT32 shader = SHADER_NONE;
 
+	if (!cv_renderwalls.value)
+		return;
+
 	HWR_Lighting(pSurf, lightlevel, wallcolormap);
 
 	if (HWR_UseShader())
@@ -845,6 +851,9 @@ static void HWR_SplitWall(sector_t *sector, FOutVector *wallVerts, INT32 texnum,
 	FUINT lightnum = HWR_CalcWallLight(sector->lightlevel, v1x, v1y, v2x, v2y);
 	extracolormap_t *colormap = NULL;
 
+	if (!cv_renderwalls.value)
+		return;
+
 	realtop = top = wallVerts[3].y;
 	realbot = bot = wallVerts[0].y;
 	diff = top - bot;
@@ -5073,6 +5082,9 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	// uncapped/interpolation
 	interpmobjstate_t interp = {0};
 
+	if (!cv_renderthings.value)
+		return;
+
 	if (!thing)
 		return;
 
@@ -6759,6 +6771,9 @@ void HWR_AddTransparentWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, INT32 te
 {
 	static size_t allocedwalls = 0;
 
+	if (!cv_renderwalls.value)
+		return;
+
 	// Force realloc if buffer has been freed
 	if (!wallinfo)
 		allocedwalls = 0;
@@ -6787,6 +6802,9 @@ void HWR_RenderWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, FBITFIELD blend,
 
 	INT32 shader = SHADER_NONE;
 
+	if (!cv_renderwalls.value)
+		return;
+
 	// Lighting is done here instead so that fog isn't drawn incorrectly on transparent walls after sorting
 	HWR_Lighting(pSurf, lightlevel, wallcolormap);
 
diff --git a/src/netcode/d_netcmd.c b/src/netcode/d_netcmd.c
index 66a30637f301942b0b78dc762b1f9c8f9a15916f..6bdcaa650f3b703082aa9fd4255b7d1aed7dc15e 100644
--- a/src/netcode/d_netcmd.c
+++ b/src/netcode/d_netcmd.c
@@ -893,6 +893,9 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_renderhitboxinterpolation);
 	CV_RegisterVar(&cv_renderhitboxgldepth);
 	CV_RegisterVar(&cv_renderhitbox);
+	CV_RegisterVar(&cv_renderwalls);
+	CV_RegisterVar(&cv_renderfloors);
+	CV_RegisterVar(&cv_renderthings);
 	CV_RegisterVar(&cv_renderer);
 	CV_RegisterVar(&cv_scr_depth);
 	CV_RegisterVar(&cv_scr_width);
diff --git a/src/r_plane.c b/src/r_plane.c
index 4f9ce9ec84bdf7443a16b811504a30df674d78f7..2fb2eb33e40b1d5838ae8b7ac96f3b1bc06c3c53 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -881,6 +881,9 @@ void R_DrawSinglePlane(visplane_t *pl)
 	if (!(pl->minx <= pl->maxx))
 		return;
 
+	if (!cv_renderfloors.value)
+		return;
+
 	// sky flat
 	if (pl->picnum == skyflatnum)
 	{
diff --git a/src/r_segs.c b/src/r_segs.c
index 453debeb6ef69840329270624f70dddae4603f49..2e3c55366315c7cf5fdb3f530adf5c27c84039df 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -117,6 +117,9 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 	INT32 range;
 	unsigned lengthcol;
 
+	if (!cv_renderwalls.value)
+		return;
+
 	// Calculate light table.
 	// Use different light tables
 	//   for horizontal / vertical / diagonal. Diagonal?
@@ -505,6 +508,9 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 
 	void (*colfunc_2s) (column_t *, unsigned);
 
+	if (!cv_renderwalls.value)
+		return;
+
 	// Calculate light table.
 	// Use different light tables
 	//   for horizontal / vertical / diagonal. Diagonal?
@@ -1247,29 +1253,32 @@ static void R_RenderSegLoop (void)
 			// single sided line
 			if (yl <= yh && yh >= 0 && yl < viewheight)
 			{
-				fixed_t offset = texturecolumn + rw_offsetx;
+				if (cv_renderwalls.value)
+				{
+					fixed_t offset = texturecolumn + rw_offsetx;
 
-				dc_yl = yl;
-				dc_yh = yh;
-				dc_texturemid = rw_midtexturemid;
-				dc_iscale = FixedMul(0xffffffffu / (unsigned)rw_scale, rw_midtexturescaley);
-				dc_source = R_GetColumn(midtexture, offset >> FRACBITS)->pixels;
-				dc_texheight = textureheight[midtexture]>>FRACBITS;
+					dc_yl = yl;
+					dc_yh = yh;
+					dc_texturemid = rw_midtexturemid;
+					dc_iscale = FixedMul(0xffffffffu / (unsigned)rw_scale, rw_midtexturescaley);
+					dc_source = R_GetColumn(midtexture, offset >> FRACBITS)->pixels;
+					dc_texheight = textureheight[midtexture]>>FRACBITS;
 
-				//profile stuff ---------------------------------------------------------
+					//profile stuff ---------------------------------------------------------
 #ifdef TIMING
-				ProfZeroTimer();
+					ProfZeroTimer();
 #endif
-				colfunc();
+					colfunc();
 #ifdef TIMING
-				RDMSR(0x10,&mycount);
-				mytotal += mycount;      //64bit add
+					RDMSR(0x10,&mycount);
+					mytotal += mycount;      //64bit add
 
-				if (nombre--==0)
-					I_Error("R_DrawColumn CPU Spy reports: 0x%d %d\n", *((INT32 *)&mytotal+1),
-						(INT32)mytotal);
+					if (nombre--==0)
+						I_Error("R_DrawColumn CPU Spy reports: 0x%d %d\n", *((INT32 *)&mytotal+1),
+							(INT32)mytotal);
 #endif
-				//profile stuff ---------------------------------------------------------
+					//profile stuff ---------------------------------------------------------
+				}
 
 				// dont draw anything more for this column, since
 				// a midtexture blocks the view
@@ -1313,18 +1322,21 @@ static void R_RenderSegLoop (void)
 					}
 					else if (mid >= 0) // safe to draw top texture
 					{
-						fixed_t offset = rw_offset_top;
-						if (rw_toptexturescalex < 0)
-							offset = -offset;
-						offset = toptexturecolumn + offset;
-
-						dc_yl = yl;
-						dc_yh = mid;
-						dc_texturemid = rw_toptexturemid;
-						dc_iscale = FixedMul(0xffffffffu / (unsigned)rw_scale, rw_toptexturescaley);
-						dc_source = R_GetColumn(toptexture, offset >> FRACBITS)->pixels;
-						dc_texheight = textureheight[toptexture]>>FRACBITS;
-						colfunc();
+						if (cv_renderwalls.value)
+						{
+							fixed_t offset = rw_offset_top;
+							if (rw_toptexturescalex < 0)
+								offset = -offset;
+							offset = toptexturecolumn + offset;
+
+							dc_yl = yl;
+							dc_yh = mid;
+							dc_texturemid = rw_toptexturemid;
+							dc_iscale = FixedMul(0xffffffffu / (unsigned)rw_scale, rw_toptexturescaley);
+							dc_source = R_GetColumn(toptexture, offset >> FRACBITS)->pixels;
+							dc_texheight = textureheight[toptexture]>>FRACBITS;
+							colfunc();
+						}
 						ceilingclip[rw_x] = (INT16)mid;
 					}
 					else if (!rw_ceilingmarked) // entirely off top of screen
@@ -1361,18 +1373,21 @@ static void R_RenderSegLoop (void)
 					}
 					else if (mid < viewheight) // safe to draw bottom texture
 					{
-						fixed_t offset = rw_offset_bottom;
-						if (rw_bottomtexturescalex < 0)
-							offset = -offset;
-						offset = bottomtexturecolumn + offset;
-
-						dc_yl = mid;
-						dc_yh = yh;
-						dc_texturemid = rw_bottomtexturemid;
-						dc_iscale = FixedMul(0xffffffffu / (unsigned)rw_scale, rw_bottomtexturescaley);
-						dc_source = R_GetColumn(bottomtexture, offset >> FRACBITS)->pixels;
-						dc_texheight = textureheight[bottomtexture]>>FRACBITS;
-						colfunc();
+						if (cv_renderwalls.value)
+						{
+							fixed_t offset = rw_offset_bottom;
+							if (rw_bottomtexturescalex < 0)
+								offset = -offset;
+							offset = bottomtexturecolumn + offset;
+
+							dc_yl = mid;
+							dc_yh = yh;
+							dc_texturemid = rw_bottomtexturemid;
+							dc_iscale = FixedMul(0xffffffffu / (unsigned)rw_scale, rw_bottomtexturescaley);
+							dc_source = R_GetColumn(bottomtexture, offset >> FRACBITS)->pixels;
+							dc_texheight = textureheight[bottomtexture]>>FRACBITS;
+							colfunc();
+						}
 						floorclip[rw_x] = (INT16)mid;
 					}
 					else if (!rw_floormarked)  // entirely off bottom of screen
diff --git a/src/r_things.c b/src/r_things.c
index d6ef72b9d29980134757df4547ffd03837379957..1ab666a02d49c2b14d7eb5912073c568c096ab6f 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1528,6 +1528,9 @@ static void R_ProjectSprite(mobj_t *thing)
 	// uncapped/interpolation
 	interpmobjstate_t interp = {0};
 
+	if (!cv_renderthings.value)
+		return;
+
 	// do interpolation
 	if (R_UsingFrameInterpolation() && !paused)
 	{
diff --git a/src/screen.c b/src/screen.c
index 76f546402ef38e0a4882221945ff602de73aadf9..92a8a9e2ae5b75734fea2b9347eae8700a2fb9ba 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -71,6 +71,9 @@ consvar_t cv_scr_height_w = CVAR_INIT ("scr_height_w", "400", CV_SAVE, CV_Unsign
 consvar_t cv_scr_depth = CVAR_INIT ("scr_depth", "16 bits", CV_SAVE, scr_depth_cons_t, NULL);
 
 consvar_t cv_renderview = CVAR_INIT ("renderview", "On", 0, CV_OnOff, NULL);
+consvar_t cv_renderwalls = CVAR_INIT ("renderwalls", "On", CV_NOTINNET|CV_CHEAT, CV_OnOff, NULL);
+consvar_t cv_renderfloors = CVAR_INIT ("renderfloors", "On", CV_NOTINNET|CV_CHEAT, CV_OnOff, NULL);
+consvar_t cv_renderthings = CVAR_INIT ("renderthings", "On", CV_NOTINNET|CV_CHEAT, CV_OnOff, NULL);
 
 CV_PossibleValue_t cv_renderer_t[] = {
 	{1, "Software"},
diff --git a/src/screen.h b/src/screen.h
index 64d92b9d3ef99cf3c4e066ddc5cc9e57edd94afd..1f5ff147d8c0195f58c3f8d32171e76cf8108aca 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -187,6 +187,7 @@ extern INT32 scr_bpp;
 extern UINT8 *scr_borderpatch; // patch used to fill the view borders
 
 extern consvar_t cv_scr_width, cv_scr_height, cv_scr_width_w, cv_scr_height_w, cv_scr_depth, cv_fullscreen;
+extern consvar_t cv_renderwalls, cv_renderfloors, cv_renderthings;
 extern consvar_t cv_renderview, cv_renderer;
 extern consvar_t cv_renderhitbox, cv_renderhitboxinterpolation, cv_renderhitboxgldepth;
 // wait for page flipping to end or not