@@ -750,20 +750,128 @@ CLIENT SIDE DEMO PLAYBACK
750
750
=======================================================================
751
751
*/
752
752
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
+
753
859
/*
754
860
=================
755
861
CL_DemoCompleted
756
862
=================
757
863
*/
758
864
static void CL_DemoCompleted ( void ) {
759
865
if ( com_timedemo -> integer ) {
760
- int time ;
866
+ CL_TimedemoResults () ;
761
867
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" );
766
871
}
872
+
873
+ cls .benchmarking = qfalse ;
874
+ cls .resetTimedemoCvar = qfalse ;
767
875
}
768
876
769
877
// fretn
@@ -3316,6 +3424,11 @@ void CL_Frame( int msec, int realMsec ) {
3316
3424
float fps ;
3317
3425
float frameDuration ;
3318
3426
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
+ }
3319
3432
3320
3433
CL_TrackCvarChanges ( qfalse );
3321
3434
@@ -3468,6 +3581,12 @@ void CL_Frame( int msec, int realMsec ) {
3468
3581
cls .discordInit = qfalse ;
3469
3582
}
3470
3583
#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
+ }
3471
3590
}
3472
3591
3473
3592
@@ -4392,6 +4511,7 @@ static void CL_InitGLimp_Cvars( void )
4392
4511
4393
4512
static const cmdListItem_t cl_cmds [] = {
4394
4513
{ "addFavorite" , CL_AddFavorite_f , NULL },
4514
+ { "benchmark" , CL_StartBenchmark_f , CL_CompleteDemoName },
4395
4515
{ "cache_endgather" , CL_Cache_EndGather_f , NULL },
4396
4516
{ "cache_mapchange" , CL_Cache_MapChange_f , NULL },
4397
4517
{ "cache_setindex" , CL_Cache_SetIndex_f , NULL },
0 commit comments