extern cvar_t cl_minfps_fade;
extern cvar_t cl_minfps_qualitymax;
extern cvar_t cl_minfps_qualitymin;
-extern cvar_t cl_minfps_qualitypower;
-extern cvar_t cl_minfps_qualityscale;
-extern cvar_t r_viewscale_fpsscaling;
-static double cl_updatescreen_rendertime = 0;
+extern cvar_t cl_minfps_qualitymultiply;
+extern cvar_t cl_minfps_qualityhysteresis;
+extern cvar_t cl_minfps_qualitystepmax;
static double cl_updatescreen_quality = 1;
void CL_UpdateScreen(void)
{
vec3_t vieworigin;
- double rendertime1;
- double drawscreenstart;
+ static double drawscreenstart = 0.0;
+ double drawscreendelta;
float conwidth, conheight;
- float f;
r_viewport_t viewport;
+ if(drawscreenstart)
+ {
+ drawscreendelta = Sys_DirtyTime() - drawscreenstart;
+ if (cl_minfps.value > 0 && !cls.timedemo && (!cls.capturevideo.active || cls.capturevideo.realtime) && drawscreendelta >= 0 && drawscreendelta < 60)
+ {
+ // quality adjustment according to render time
+ double actualframetime;
+ double targetframetime;
+ double adjust;
+ double f;
+ double h;
+ r_refdef.lastdrawscreentime += (drawscreendelta - r_refdef.lastdrawscreentime) * cl_minfps_fade.value;
+ actualframetime = r_refdef.lastdrawscreentime;
+ targetframetime = (1.0 / cl_minfps.value);
+
+ // assume render time scales linearily / cl_minfps_qualitymultiply.value with quality...
+ f = cl_updatescreen_quality / actualframetime * cl_minfps_qualitymultiply.value;
+ // we scale hysteresis by quality
+ h = cl_updatescreen_quality * cl_minfps_qualityhysteresis.value;
+
+ targetframetime -= h / f; // this makes sure resulting fps is > minfps despite hysteresis
+
+ adjust = (targetframetime - actualframetime) * f;
+ if(adjust > h)
+ adjust -= h;
+ else if(adjust > -h)
+ adjust = 0;
+ else
+ adjust += h;
+
+ // adjust > 0 if:
+ // (targetframetime - actualframetime) * f > h
+ // (targetframetime - actualframetime) * (cl_updatescreen_quality / actualframetime * qualitymultiply) > h
+ // (1.0 / minfps - h / (quality / actualframetime * qualitymultiply) - actualframetime) * (quality / actualframetime * qualitymultiply) > h
+ // (1.0 / minfps - actualframetime) * (quality / actualframetime * qualitymultiply) > 2 * h
+ // actualframetime < quality qualitymultiply / (minfps quality qualitymultiply + 2 minfps h)
+ // actualframetime < 1.0 / minfps * (quality qualitymultiply) / (quality qualitymultiply + 2 h)
+ // actualframetime < 1.0 / minfps / (1 + 2 h / (quality qualitymultiply))
+ // actualframetime < 1.0 / minfps / (1 + 2 qualityhysteresis / qualitymultiply)
+
+ // adjust < 0 if:
+ // (targetframetime - actualframetime) * f < -h
+ // (targetframetime - actualframetime) * (cl_updatescreen_quality / actualframetime * qualitymultiply) < -h
+ // (1.0 / minfps - h / (quality / actualframetime * qualitymultiply) - actualframetime) * (quality / actualframetime * qualitymultiply) < -h
+ // (1.0 / minfps - actualframetime) * (quality / actualframetime * qualitymultiply) < 0
+ // actualframetime > 1.0 / minfps
+
+ /*
+ Con_Printf("adjust UP if fps > %f, adjust DOWN if fps < %f\n",
+ cl_minfps.value * (1.0 + 2.0 * cl_minfps_qualityhysteresis.value / cl_minfps_qualitymultiply.value),
+ cl_minfps.value);
+ */
+
+ adjust = bound(-cl_minfps_qualitystepmax.value, adjust, cl_minfps_qualitystepmax.value);
+ cl_updatescreen_quality += adjust;
+ cl_updatescreen_quality = bound(max(0.01, cl_minfps_qualitymin.value), cl_updatescreen_quality, cl_minfps_qualitymax.value);
+ }
+ else
+ {
+ cl_updatescreen_quality = 1;
+ r_refdef.lastdrawscreentime = 0;
+ }
+ }
+
+ drawscreenstart = Sys_DirtyTime();
+
Sbar_ShowFPS_Update();
if (!scr_initialized || !con_initialized || !scr_refresh.integer)
return;
}
- rendertime1 = Sys_DirtyTime();
-
conwidth = bound(160, vid_conwidth.value, 32768);
conheight = bound(90, vid_conheight.value, 24576);
if (vid_conwidth.value != conwidth)
R_ClearScreen(false);
r_refdef.view.clear = false;
r_refdef.view.isoverlay = false;
- f = pow((float)cl_updatescreen_quality, cl_minfps_qualitypower.value) * cl_minfps_qualityscale.value;
- r_refdef.view.quality = bound(cl_minfps_qualitymin.value, f, cl_minfps_qualitymax.value);
+
+ // calculate r_refdef.view.quality
+ r_refdef.view.quality = cl_updatescreen_quality;
#ifndef USE_GLES2
if (qglPolygonStipple)
}
#endif
- if (r_viewscale_fpsscaling.integer)
- GL_Finish();
- drawscreenstart = Sys_DirtyTime();
#ifndef USE_GLES2
if (R_Stereo_Active())
{
else
#endif
SCR_DrawScreen();
- if (r_viewscale_fpsscaling.integer)
- GL_Finish();
- r_refdef.lastdrawscreentime = Sys_DirtyTime() - drawscreenstart;
SCR_CaptureVideo();
if (qglFlush)
qglFlush(); // FIXME: should we really be using qglFlush here?
- // quality adjustment according to render time
- cl_updatescreen_rendertime += ((Sys_DirtyTime() - rendertime1) - cl_updatescreen_rendertime) * bound(0, cl_minfps_fade.value, 1);
- if (cl_minfps.value > 0 && cl_updatescreen_rendertime > 0 && !cls.timedemo && (!cls.capturevideo.active || !cls.capturevideo.realtime))
- cl_updatescreen_quality = 1 / (cl_updatescreen_rendertime * cl_minfps.value);
- else
- cl_updatescreen_quality = 1;
-
if (!vid_activewindow)
VID_SetMouse(false, false, false);
else if (key_consoleactive)
cvar_t host_speeds = {0, "host_speeds","0", "reports how much time is used in server/graphics/sound"};
cvar_t host_maxwait = {0, "host_maxwait","1000", "maximum sleep time requested from the operating system in millisecond. Larger sleeps will be done using multiple host_maxwait length sleeps. Lowering this value will increase CPU load, but may help working around problems with accuracy of sleep times."};
cvar_t cl_minfps = {CVAR_SAVE, "cl_minfps", "40", "minimum fps target - while the rendering performance is below this, it will drift toward lower quality"};
-cvar_t cl_minfps_fade = {CVAR_SAVE, "cl_minfps_fade", "0.2", "how fast the quality adapts to varying framerate"};
+cvar_t cl_minfps_fade = {CVAR_SAVE, "cl_minfps_fade", "1", "how fast the quality adapts to varying framerate"};
cvar_t cl_minfps_qualitymax = {CVAR_SAVE, "cl_minfps_qualitymax", "1", "highest allowed drawdistance multiplier"};
cvar_t cl_minfps_qualitymin = {CVAR_SAVE, "cl_minfps_qualitymin", "0.25", "lowest allowed drawdistance multiplier"};
-cvar_t cl_minfps_qualitypower = {CVAR_SAVE, "cl_minfps_qualitypower", "4", "raises quality value to a power of itself, higher values make quality drop more sharply in relation to framerate"};
-cvar_t cl_minfps_qualityscale = {CVAR_SAVE, "cl_minfps_qualityscale", "0.5", "multiplier for quality"};
+cvar_t cl_minfps_qualitymultiply = {CVAR_SAVE, "cl_minfps_qualitymultiply", "0.2", "multiplier for quality changes in quality change per second render time (1 assumes linearity of quality and render time)"};
+cvar_t cl_minfps_qualityhysteresis = {CVAR_SAVE, "cl_minfps_qualityhysteresis", "0.025", "reduce all quality changes by this to reduce flickering"};
+cvar_t cl_minfps_qualitystepmax = {CVAR_SAVE, "cl_minfps_qualitystepmax", "0.1", "maximum quality change in a single frame"};
cvar_t cl_maxfps = {CVAR_SAVE, "cl_maxfps", "0", "maximum fps cap, 0 = unlimited, if game is running faster than this it will wait before running another frame (useful to make cpu time available to other programs)"};
cvar_t cl_maxfps_alwayssleep = {0, "cl_maxfps_alwayssleep","1", "gives up some processing time to other applications each frame, value in milliseconds, disabled if cl_maxfps is 0"};
cvar_t cl_maxidlefps = {CVAR_SAVE, "cl_maxidlefps", "20", "maximum fps cap when the game is not the active window (makes cpu time available to other programs"};
Cvar_RegisterVariable (&cl_minfps_fade);
Cvar_RegisterVariable (&cl_minfps_qualitymax);
Cvar_RegisterVariable (&cl_minfps_qualitymin);
- Cvar_RegisterVariable (&cl_minfps_qualitypower);
- Cvar_RegisterVariable (&cl_minfps_qualityscale);
+ Cvar_RegisterVariable (&cl_minfps_qualitystepmax);
+ Cvar_RegisterVariable (&cl_minfps_qualityhysteresis);
+ Cvar_RegisterVariable (&cl_minfps_qualitymultiply);
Cvar_RegisterVariable (&cl_maxfps);
Cvar_RegisterVariable (&cl_maxfps_alwayssleep);
Cvar_RegisterVariable (&cl_maxidlefps);