Skip to content

Commit 794849d

Browse files
committed
Improve timdemo output + add benchmark command
Timedemo now outputs time, frames, min/max/avg fps, 99th pct min and 99.9th pct min fps Added benchmark command - wrapper command that enables timedemo and plays back specified demo
1 parent 29fc640 commit 794849d

File tree

3 files changed

+141
-13
lines changed

3 files changed

+141
-13
lines changed

src/client/cl_cgame.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,7 +1519,7 @@ static void CL_FirstSnapshot( void ) {
15191519
cl.serverTimeDelta = cl.snap.serverTime - cls.realtime;
15201520
cl.oldServerTime = cl.snap.serverTime;
15211521

1522-
clc.timeDemoBaseTime = cl.snap.serverTime;
1522+
clc.timedemo.timeDemoBaseTime = cl.snap.serverTime;
15231523

15241524
// if this is the first frame of active play,
15251525
// execute the contents of activeAction now
@@ -1695,11 +1695,11 @@ void CL_SetCGameTime( void ) {
16951695
// while a normal demo may have different time samples
16961696
// each time it is played back
16971697
if ( com_timedemo->integer ) {
1698-
if ( !clc.timeDemoStart ) {
1699-
clc.timeDemoStart = Sys_Milliseconds();
1698+
if ( !clc.timedemo.timeDemoStart ) {
1699+
clc.timedemo.timeDemoStart = Sys_Microseconds();
17001700
}
1701-
clc.timeDemoFrames++;
1702-
cl.serverTime = clc.timeDemoBaseTime + clc.timeDemoFrames * 50;
1701+
clc.timedemo.timeDemoFrames++;
1702+
cl.serverTime = clc.timedemo.timeDemoBaseTime + clc.timedemo.timeDemoFrames * 50;
17031703
}
17041704

17051705
//while ( cl.serverTime >= cl.snap.serverTime ) {

src/client/cl_main.c

Lines changed: 125 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -750,20 +750,128 @@ CLIENT SIDE DEMO PLAYBACK
750750
=======================================================================
751751
*/
752752

753+
/*
754+
====================
755+
CL_StartBenchmark_f
756+
====================
757+
*/
758+
static void CL_StartBenchmark_f(void) {
759+
if (Cmd_Argc() < 2) {
760+
Com_Printf("benchmark <demoname>\n");
761+
return;
762+
}
763+
764+
// enable timedemo if it isn't set already (and keep track if we changed the value, so we can restore it)
765+
if (!Cvar_VariableIntegerValue("timedemo")) {
766+
Cvar_Set("timedemo", "1");
767+
cls.resetTimedemoCvar = qtrue;
768+
}
769+
770+
cls.benchmarking = qtrue;
771+
Cbuf_ExecuteText(EXEC_APPEND, va("demo %s", Cmd_Argv(1)));
772+
}
773+
774+
/*
775+
=================
776+
CL_CompareFrametimes
777+
=================
778+
*/
779+
static int CL_CompareFrametimes(const void *a, const void *b) {
780+
const uint32_t arg1 = *(uint32_t *)a;
781+
const uint32_t arg2 = *(uint32_t *)b;
782+
783+
if (arg1 > arg2) {
784+
return 1;
785+
} else if (arg2 > arg1) {
786+
return -1;
787+
} else {
788+
return 0;
789+
}
790+
}
791+
792+
/*
793+
=================
794+
CL_TimedemoResults
795+
=================
796+
*/
797+
static void CL_TimedemoResults(void) {
798+
int64_t time;
799+
int i, numFrames;
800+
uint32_t sortedFrametimes[MAX_TIMEDEMO_FRAMES];
801+
int onePercentIdx, pointOnePercentIdx;
802+
float fps, minFps, maxFps;
803+
char onePercent[8], pointOnePercent[8];
804+
805+
time = Sys_Microseconds() - clc.timedemo.timeDemoStart;
806+
807+
if (time <= 0) {
808+
return;
809+
}
810+
811+
// timeFrames gets incremented before we get here, but we never have a chance to measure the frametime
812+
// since the playback ends, therefore scrap the last frame entirely as it never gets stored
813+
numFrames = clc.timedemo.timeDemoFrames - 1;
814+
815+
fps = numFrames * 1000000.0f / time;
816+
817+
Com_Memcpy(sortedFrametimes, clc.timedemo.frametime, numFrames * sizeof(uint32_t));
818+
qsort(sortedFrametimes, numFrames, sizeof(uint32_t), CL_CompareFrametimes);
819+
820+
minFps = 1000000.0f / sortedFrametimes[numFrames - 1];
821+
maxFps = 1000000.0f / sortedFrametimes[0];
822+
823+
// filter out potential 0ms anomalies for maxfps
824+
if (sortedFrametimes[0] == 0) {
825+
for (i = 0; i < numFrames; i++) {
826+
if (sortedFrametimes[i] != 0) {
827+
maxFps = 1000000.0f / sortedFrametimes[i];
828+
break;
829+
}
830+
}
831+
}
832+
833+
onePercentIdx = (int)(0.01f * numFrames);
834+
835+
// make sure we have enough total frames to display 1% lows
836+
if (onePercentIdx) {
837+
Com_sprintf(onePercent, sizeof(onePercent), "%3.2f", 1000000.0f / sortedFrametimes[numFrames - 1 - onePercentIdx]);
838+
}
839+
840+
pointOnePercentIdx = (int)(0.001f * numFrames);
841+
842+
// make sure we have enough total frames to display 0.1% lows
843+
if (pointOnePercentIdx) {
844+
Com_sprintf(pointOnePercent, sizeof(pointOnePercent), "%3.2f", 1000000.0f / sortedFrametimes[numFrames - 1 - pointOnePercentIdx]);
845+
}
846+
847+
Com_Printf("\n----- Benchmark results -----\n");
848+
Com_Printf("\n%-18s %3.2f sec\n%-18s %i\n%-18s %3.2f\n%-18s %3.2f\n%-18s %3.2f\n%-18s %s\n%-18s %s\n",
849+
"Time elapsed:", time / 1000000.0f,
850+
"Total frames:", numFrames,
851+
"Minimum fps:", minFps,
852+
"Maximum fps:", maxFps,
853+
"Average fps:", fps,
854+
"99th pct. min:", onePercentIdx ? onePercent : "--",
855+
"99.9th pct. min:", pointOnePercentIdx ? pointOnePercent : "--");
856+
Com_Printf("\n-----------------------------\n\n");
857+
}
858+
753859
/*
754860
=================
755861
CL_DemoCompleted
756862
=================
757863
*/
758864
static void CL_DemoCompleted( void ) {
759865
if ( com_timedemo->integer ) {
760-
int time;
866+
CL_TimedemoResults();
761867

762-
time = Sys_Milliseconds() - clc.timeDemoStart;
763-
if ( time > 0 ) {
764-
Com_Printf( "%i frames, %3.*f seconds: %3.1f fps\n", clc.timeDemoFrames,
765-
time > 10000 ? 1 : 2, time/1000.0, clc.timeDemoFrames*1000.0 / time );
868+
// reset timedemo cvar if 'benchmark' command forcibly enabled it
869+
if (cls.benchmarking && cls.resetTimedemoCvar) {
870+
Cvar_Set("timedemo", "0");
766871
}
872+
873+
cls.benchmarking = qfalse;
874+
cls.resetTimedemoCvar = qfalse;
767875
}
768876

769877
// fretn
@@ -3316,6 +3424,11 @@ void CL_Frame( int msec, int realMsec ) {
33163424
float fps;
33173425
float frameDuration;
33183426
aviRecordingState_t aviRecord = AVIDEMO_NONE;
3427+
int64_t frameStart = 0;
3428+
3429+
if (clc.demoplaying && com_timedemo && com_timedemo->integer) {
3430+
frameStart = Sys_Microseconds();
3431+
}
33193432

33203433
CL_TrackCvarChanges( qfalse );
33213434

@@ -3468,6 +3581,12 @@ void CL_Frame( int msec, int realMsec ) {
34683581
cls.discordInit = qfalse;
34693582
}
34703583
#endif
3584+
3585+
if (clc.demoplaying && clc.timedemo.timeDemoFrames && com_timedemo && com_timedemo->integer) {
3586+
// use circular buffer to store frametimes, the array should hold enough frames for ~3h of demo playback,
3587+
// but in case of extremely long demos, just start dropping frames from the beginning
3588+
clc.timedemo.frametime[(clc.timedemo.timeDemoFrames - 1) % MAX_TIMEDEMO_FRAMES] = Sys_Microseconds() - frameStart;
3589+
}
34713590
}
34723591

34733592

@@ -4392,6 +4511,7 @@ static void CL_InitGLimp_Cvars( void )
43924511

43934512
static const cmdListItem_t cl_cmds[] = {
43944513
{ "addFavorite", CL_AddFavorite_f, NULL },
4514+
{ "benchmark", CL_StartBenchmark_f, CL_CompleteDemoName },
43954515
{ "cache_endgather", CL_Cache_EndGather_f, NULL },
43964516
{ "cache_mapchange", CL_Cache_MapChange_f, NULL },
43974517
{ "cache_setindex", CL_Cache_SetIndex_f, NULL },

src/client/client.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,14 @@ demo through a file.
214214
=============================================================================
215215
*/
216216

217+
#define MAX_TIMEDEMO_FRAMES 262140
218+
219+
typedef struct {
220+
uint32_t frametime[MAX_TIMEDEMO_FRAMES];
221+
int timeDemoFrames; // counter of rendered frames
222+
int64_t timeDemoStart; // cls.realtime before first frame
223+
int timeDemoBaseTime; // each frame will be at this time + frameNum * 50
224+
} timedemo_t ;
217225

218226
typedef struct {
219227

@@ -290,9 +298,7 @@ typedef struct {
290298
fileHandle_t wavefile;
291299
int wavetime;
292300

293-
int timeDemoFrames; // counter of rendered frames
294-
int timeDemoStart; // cls.realtime before first frame
295-
int timeDemoBaseTime; // each frame will be at this time + frameNum * 50
301+
timedemo_t timedemo;
296302

297303
float aviVideoFrameRemainder;
298304
float aviSoundFrameRemainder;
@@ -432,6 +438,8 @@ typedef struct {
432438
float biasX;
433439
float biasY;
434440

441+
qboolean benchmarking;
442+
qboolean resetTimedemoCvar;
435443
} clientStatic_t;
436444

437445
extern int bigchar_width;

0 commit comments

Comments
 (0)