diff --git a/src/d_main.c b/src/d_main.c
index c0a8eacec4cc6c49ba521a74a03c0358aee00768..09aa2d340d57ff7599d0e6d978d7a66b11fcacb6 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -411,6 +411,7 @@ static void D_Display(void)
 
 			if (!automapactive && !dedicated && cv_renderview.value)
 			{
+				rs_rendercalltime = I_GetTimeMicros();
 				if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD)
 				{
 					topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
@@ -457,6 +458,7 @@ static void D_Display(void)
 					if (postimgtype2)
 						V_DoPostProcessor(1, postimgtype2, postimgparam2);
 				}
+				rs_rendercalltime = I_GetTimeMicros() - rs_rendercalltime;
 			}
 
 			if (lastdraw)
@@ -591,6 +593,77 @@ static void D_Display(void)
 			snprintf(s, sizeof s - 1, "SysMiss %.2f%%", lostpercent);
 			V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-10, V_YELLOWMAP, s);
 		}
+		
+		if (cv_renderstats.value)
+		{
+			char s[50];
+			int frametime = I_GetTimeMicros() - rs_prevframetime;
+			int divisor = 1;
+			rs_prevframetime = I_GetTimeMicros();
+
+			if (rs_rendercalltime > 10000) divisor = 1000;
+			
+			snprintf(s, sizeof s - 1, "ft   %d", frametime / divisor);
+			V_DrawThinString(30, 10, V_MONOSPACE | V_YELLOWMAP, s);
+			snprintf(s, sizeof s - 1, "rtot %d", rs_rendercalltime / divisor);
+			V_DrawThinString(30, 20, V_MONOSPACE | V_YELLOWMAP, s);
+			if (rendermode == render_opengl)// dont show unimplemented stats
+			{
+				snprintf(s, sizeof s - 1, "bsp  %d", rs_bsptime / divisor);
+				V_DrawThinString(30, 30, V_MONOSPACE | V_YELLOWMAP, s);
+				snprintf(s, sizeof s - 1, "nsrt %d", rs_nodesorttime / divisor);
+				V_DrawThinString(30, 40, V_MONOSPACE | V_YELLOWMAP, s);
+				snprintf(s, sizeof s - 1, "ndrw %d", rs_nodedrawtime / divisor);
+				V_DrawThinString(30, 50, V_MONOSPACE | V_YELLOWMAP, s);
+				snprintf(s, sizeof s - 1, "ssrt %d", rs_spritesorttime / divisor);
+				V_DrawThinString(30, 60, V_MONOSPACE | V_YELLOWMAP, s);
+				snprintf(s, sizeof s - 1, "sdrw %d", rs_spritedrawtime / divisor);
+				V_DrawThinString(30, 70, V_MONOSPACE | V_YELLOWMAP, s);
+				/*snprintf(s, sizeof s - 1, "post %d", rs_posttime / divisor);
+				V_DrawThinString(30, 80, V_MONOSPACE | V_YELLOWMAP, s);
+				snprintf(s, sizeof s - 1, "flip %d", rs_swaptime / divisor);
+				V_DrawThinString(30, 90, V_MONOSPACE | V_YELLOWMAP, s);
+				snprintf(s, sizeof s - 1, "test %d", rs_test / divisor);
+				V_DrawThinString(30, 100, V_MONOSPACE | V_YELLOWMAP, s);*/
+
+				snprintf(s, sizeof s - 1, "nbsp %d", rs_numbspcalls);
+				V_DrawThinString(80, 10, V_MONOSPACE | V_BLUEMAP, s);
+				snprintf(s, sizeof s - 1, "nnod %d", rs_numdrawnodes);
+				V_DrawThinString(80, 20, V_MONOSPACE | V_BLUEMAP, s);
+				snprintf(s, sizeof s - 1, "nspr %d", rs_numsprites);
+				V_DrawThinString(80, 30, V_MONOSPACE | V_BLUEMAP, s);
+				snprintf(s, sizeof s - 1, "npob %d", rs_numpolyobjects);
+				V_DrawThinString(80, 40, V_MONOSPACE | V_BLUEMAP, s);
+/*
+				if (cv_enable_batching.value)
+				{
+					snprintf(s, sizeof s - 1, "bsrt %d", rs_batchsorttime / divisor);
+					V_DrawThinString(75, 55, V_MONOSPACE | V_REDMAP, s);
+					snprintf(s, sizeof s - 1, "bdrw %d", rs_batchdrawtime / divisor);
+					V_DrawThinString(75, 65, V_MONOSPACE | V_REDMAP, s);
+
+					snprintf(s, sizeof s - 1, "npol %d", rs_numpolys);
+					V_DrawThinString(130, 10, V_MONOSPACE | V_PURPLEMAP, s);
+					snprintf(s, sizeof s - 1, "ndc  %d", rs_numcalls);
+					V_DrawThinString(130, 20, V_MONOSPACE | V_PURPLEMAP, s);
+					snprintf(s, sizeof s - 1, "nshd %d", rs_numshaders);
+					V_DrawThinString(130, 30, V_MONOSPACE | V_PURPLEMAP, s);
+					snprintf(s, sizeof s - 1, "nvrt %d", rs_numverts);
+					V_DrawThinString(130, 40, V_MONOSPACE | V_PURPLEMAP, s);
+					snprintf(s, sizeof s - 1, "ntex %d", rs_numtextures);
+					V_DrawThinString(185, 10, V_MONOSPACE | V_PURPLEMAP, s);
+					snprintf(s, sizeof s - 1, "npf  %d", rs_numpolyflags);
+					V_DrawThinString(185, 20, V_MONOSPACE | V_PURPLEMAP, s);
+					snprintf(s, sizeof s - 1, "ncol %d", rs_numcolors);
+					V_DrawThinString(185, 30, V_MONOSPACE | V_PURPLEMAP, s);
+				}*/
+			}
+/*			else
+			{
+				snprintf(s, sizeof s - 1, "flip %d", rs_swaptime / divisor);
+				V_DrawThinString(30, 30, V_MONOSPACE | V_YELLOWMAP, s);
+			}*/
+		}
 
 		I_FinishUpdate(); // page flip or blit buffer
 	}
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 6fe0d407880de5602acbf0c21335f87f4d800d3d..ed43ec9ae56d6ea192a5f539dd04f2b629e5f1a1 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -146,6 +146,25 @@ static float gr_fovlud;
 static angle_t gr_aimingangle;
 static void HWR_SetTransformAiming(FTransform *trans, player_t *player, boolean skybox);
 
+// render stats
+int rs_prevframetime = 0;
+int rs_rendercalltime = 0;
+int rs_bsptime = 0;
+int rs_nodetime = 0;
+int rs_nodesorttime = 0;
+int rs_nodedrawtime = 0;
+int rs_spritesorttime = 0;
+int rs_spritedrawtime = 0;
+
+int rs_numdrawnodes = 0;
+int rs_numbspcalls = 0;
+int rs_numsprites = 0;
+int rs_numpolyobjects = 0;
+
+//int rs_posttime = 0;
+//int rs_swaptime = 0;
+
+
 // ==========================================================================
 // Lighting
 // ==========================================================================
@@ -3484,6 +3503,9 @@ static void HWR_Subsector(size_t num)
 			po = (polyobj_t *)(po->link.next);
 		}
 
+		// for render stats
+		rs_numpolyobjects += numpolys;
+
 		// Sort polyobjects
 		R_SortPolyObjects(sub);
 
@@ -3598,6 +3620,8 @@ static void HWR_RenderBSPNode(INT32 bspnum)
 
 	// Decide which side the view point is on
 	INT32 side;
+	
+	rs_numbspcalls++;
 
 	// Found a subsector?
 	if (bspnum & NF_SUBSECTOR)
@@ -4772,6 +4796,8 @@ static void HWR_CreateDrawNodes(void)
 
 	// If true, swap the draw order.
 	boolean shift = false;
+	
+	rs_nodesorttime = I_GetTimeMicros();
 
 	for (i = 0; i < numplanes; i++, p++)
 	{
@@ -4790,6 +4816,8 @@ static void HWR_CreateDrawNodes(void)
 		sortnode[p].wall = &wallinfo[i];
 		sortindex[p] = p;
 	}
+	
+	rs_numdrawnodes = p;
 
 	// p is the number of stuff to sort
 
@@ -4892,6 +4920,10 @@ static void HWR_CreateDrawNodes(void)
 		} //i++
 	} // loop++
 
+	rs_nodesorttime = I_GetTimeMicros() - rs_nodesorttime;
+	
+	rs_nodedrawtime = I_GetTimeMicros();
+
 	// Okay! Let's draw it all! Woo!
 	HWD.pfnSetTransform(&atransform);
 	HWD.pfnSetShader(0);
@@ -4926,6 +4958,8 @@ static void HWR_CreateDrawNodes(void)
 				sortnode[sortindex[i]].wall->lightlevel, sortnode[sortindex[i]].wall->wallcolormap);
 		}
 	}
+	
+	rs_nodedrawtime = I_GetTimeMicros() - rs_nodedrawtime;
 
 	numwalls = 0;
 	numplanes = 0;
@@ -6002,6 +6036,10 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player)
 	HWD.pfnSetSpecialState(HWD_SET_SHADERS, cv_grshaders.value);
 	HWD.pfnSetShader(0);
 
+	rs_numbspcalls = 0;
+	rs_numpolyobjects = 0;
+	rs_bsptime = I_GetTimeMicros();
+
 	validcount++;
 
 	HWR_RenderBSPNode((INT32)numnodes-1);
@@ -6035,6 +6073,8 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player)
 	}
 #endif
 
+	rs_bsptime = I_GetTimeMicros() - rs_bsptime;
+
 	// Check for new console commands.
 	NetUpdate();
 
@@ -6045,14 +6085,22 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player)
 #endif
 
 	// Draw MD2 and sprites
+	rs_numsprites = gr_visspritecount;
+	rs_spritesorttime = I_GetTimeMicros();
 	HWR_SortVisSprites();
+	rs_spritesorttime = I_GetTimeMicros() - rs_spritesorttime;
+	rs_spritedrawtime = I_GetTimeMicros();
 	HWR_DrawSprites();
+	rs_spritedrawtime = I_GetTimeMicros() - rs_spritedrawtime;
 
 #ifdef NEWCORONAS
 	//Hurdler: they must be drawn before translucent planes, what about gl fog?
 	HWR_DrawCoronas();
 #endif
 
+	rs_numdrawnodes = 0;
+	rs_nodesorttime = 0;
+	rs_nodedrawtime = 0;
 	if (numplanes || numpolyplanes || numwalls) //Hurdler: render 3D water and transparent walls after everything
 	{
 		HWR_CreateDrawNodes();
@@ -6118,6 +6166,11 @@ consvar_t cv_granisotropicmode = {"gr_anisotropicmode", "1", CV_CALL, granisotro
 consvar_t cv_grcorrecttricks = {"gr_correcttricks", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_grsolvetjoin = {"gr_solvetjoin", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 
+// render stats
+// for now have it in here in the hw code
+// could have it somewhere else since renderstats could also be a software rendering thing
+consvar_t cv_renderstats = {"renderstats", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+
 static void CV_grfiltermode_OnChange(void)
 {
 	if (rendermode == render_opengl)
@@ -6155,6 +6208,8 @@ void HWR_AddCommands(void)
 	CV_RegisterVar(&cv_grfiltermode);
 	CV_RegisterVar(&cv_grcorrecttricks);
 	CV_RegisterVar(&cv_grsolvetjoin);
+	
+	CV_RegisterVar(&cv_renderstats);
 
 #ifndef NEWCLIP
 	CV_RegisterVar(&cv_grclipwalls);
diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h
index aedfa9cd6206face0d2f2c210093ad5e96bc2cd5..8aaa335fbe68e51430dc7b576b6ace00b882f467 100644
--- a/src/hardware/hw_main.h
+++ b/src/hardware/hw_main.h
@@ -104,4 +104,25 @@ extern float gr_viewwindowx, gr_basewindowcentery;
 extern fixed_t *hwbbox;
 extern FTransform atransform;
 
+
+// render stats console toggle
+extern consvar_t cv_renderstats;
+// render stats time counter variables
+extern int rs_prevframetime;// time when previous frame was rendered
+extern int rs_rendercalltime;
+extern int rs_bsptime;
+extern int rs_nodetime;
+extern int rs_nodesorttime;
+extern int rs_nodedrawtime;
+extern int rs_spritesorttime;
+extern int rs_spritedrawtime;
+
+//extern int rs_posttime;
+//extern int rs_swaptime;
+
+extern int rs_numdrawnodes;
+extern int rs_numbspcalls;
+extern int rs_numsprites;
+extern int rs_numpolyobjects;
+
 #endif
diff --git a/src/i_system.h b/src/i_system.h
index b38748244b9465107686f8d3ba74cebb771db418..dd0b65f6df542d228019f5c3c91d59c5bcd6728c 100644
--- a/src/i_system.h
+++ b/src/i_system.h
@@ -46,6 +46,8 @@ UINT32 I_GetFreeMem(UINT32 *total);
 */
 tic_t I_GetTime(void);
 
+int I_GetTimeMicros(void);// provides microsecond counter for render stats
+
 /**	\brief	The I_Sleep function
 
 	\return	void
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index a86af316e9e22460c734057960f39a8301fc67e9..9d63225cb263d3a8c7e170380cf18b4c0a1d51dc 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -2060,9 +2060,12 @@ static p_timeGetTime pfntimeGetTime = NULL;
 // but lower precision on Windows NT
 // ---------
 
-tic_t I_GetTime(void)
+DWORD TimeFunction(boolean microseconds)
 {
-	tic_t newtics = 0;
+	DWORD newtics = 0;
+	int multiplier = 1;
+
+	if (microseconds) multiplier = 1000;
 
 	if (!starttickcount) // high precision timer
 	{
@@ -2082,7 +2085,7 @@ tic_t I_GetTime(void)
 
 		if (frequency.LowPart && QueryPerformanceCounter(&currtime))
 		{
-			newtics = (INT32)((currtime.QuadPart - basetime.QuadPart) * NEWTICRATE
+			newtics = (INT32)((currtime.QuadPart - basetime.QuadPart) * 1000 * multiplier
 				/ frequency.QuadPart);
 		}
 		else if (pfntimeGetTime)
@@ -2090,11 +2093,11 @@ tic_t I_GetTime(void)
 			currtime.LowPart = pfntimeGetTime();
 			if (!basetime.LowPart)
 				basetime.LowPart = currtime.LowPart;
-			newtics = ((currtime.LowPart - basetime.LowPart)/(1000/NEWTICRATE));
+			newtics = currtime.LowPart - basetime.LowPart;
 		}
 	}
 	else
-		newtics = (GetTickCount() - starttickcount)/(1000/NEWTICRATE);
+		newtics = (GetTickCount() - starttickcount) * multiplier;
 
 	return newtics;
 }
@@ -2116,6 +2119,7 @@ static void I_ShutdownTimer(void)
 // I_GetTime
 // returns time in 1/TICRATE second tics
 //
+/*
 tic_t I_GetTime (void)
 {
 	static Uint64 basetime = 0;
@@ -2132,8 +2136,33 @@ tic_t I_GetTime (void)
 
 	return (tic_t)ticks;
 }
+*/
+int TimeFunction(boolean microseconds)// this cant actually do microseconds so it fakes it
+{
+	static Uint64 basetime = 0;
+		   Uint64 ticks = SDL_GetTicks();
+
+	if (!basetime)
+		basetime = ticks;
+
+	ticks -= basetime;
+	
+	return microseconds ? ticks * 1000 : ticks;
+}
 #endif
 
+tic_t I_GetTime(void)
+{
+	//return TimeFunction(false) / (1000/NEWTICRATE);
+	// how about this
+	return (TimeFunction(false) * NEWTICRATE) / 1000;
+}
+
+int I_GetTimeMicros(void)
+{
+	return TimeFunction(true);
+}
+
 //
 //I_StartupTimer
 //