Page 1 of 2 12 LastLast
Results 1 to 10 of 20

Thread: Release: In-game FPS counter

  1. #1
    The discussion of infinite loops got me tinkering. The following script ended up being a bit of a test-bed for a number of techniques including 'auto-load' of an action and associated code from a single file.

    Just add this to your players initialization field:-
    <table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">s=&#91;&#93; execVM &#34;fps.sqf&#34;[/QUOTE]

    The should then find that your player has a new action called "Toggle FPS" which turns the counter on and off. I&#39;ve tested this against FRAPS and it seems to give consistent results.

    The original file without the crappy CODE reformatting and _much_ easier to read is here.

    Top tip - it looks an awful lot nicer with syntax highlighting turned on. Treating it as a &#39;C&#39; file seems to give the best results in emacs.

    If you can&#39;t see the link above, the content (with unreadably mangled format) is below...

    FILE: fps.sqf
    <table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">
    // In-game FPS &#40;Frames Per Second&#41; counter
    //
    // Version 1.0, 28 dec 06
    // Created by sbsmac
    //
    // To use this script add the following to the player initialization field&#58;-
    //
    // s=&#91;&#93; execVM &#34;fps.sqf&#34;
    //
    // Note that &#39;s&#39; is a dummy variable to keep the parser happy.
    //
    // You should then find that your action menu contains &#39;Toggle FPS&#39;.
    // Select this option to toggle fps counting on and off



    if &#40;count _this ==0 &#41; then {

    //This is the code that is run at initialisation. It sets up
    //the required variables and functions and adds the &#39;toggle&#39; action.

    fpsState = &#34;stopped&#34;; //global variable to control state of counter

    //the counter function- this runs continuously until halted
    startFps = {
    fpsState=&#34;running&#34;;
    //build in a little bit of smoothing by averaging the last n frames
    _numFrames = 10;
    _frameIndex=0;
    _frameTimes=&#91;&#93;;
    //clear out the buffer &#40;needs to be one larger than num frames&#41;
    for &#34;_i&#34; from 0 to &#40;_numFrames&#41; do {_frameTimes set &#91;_i,0&#93;;};

    //use a double loop here because of the ArmA limitation that
    //loops terminate after 10000 iterations
    while {fpsState == &#34;running&#34;} do {
    while {fpsState == &#34;running&#34;} do {
    _frameTimes set &#91;_frameIndex,time&#93;;
    _frameIndex = &#40;_frameIndex +1&#41; % &#40;_numFrames+1&#41;;
    hint format &#91;&#34;fps %1 &#40;over last %2 frames&#41;&#34;,
    round&#40;&#40;_numframes&#41;/&#40;time-&#40;_frameTimes select _frameIndex&#41;&#41;&#41;,
    _numFrames&#93;;
    //delay a little bit - this forces a wait until
    //the next frame
    Sleep 0.001;
    };
    };
    hint &#34;&#34;; //clear the hint box
    fpsState = &#34;stopped&#34;;
    };

    //function to request that the counter be stopped
    stopFps = {
    fpsState = &#34;stopping&#34;;
    };

    //having set up the functions, add an action to control them
    //the exact string passed to the script is actually irrelevant
    player addAction &#91;&#34;Toggle FPS&#34;,&#34;fps.sqf&#34;,&#91;&#34;toggle&#34;&# 93;&#93;;

    } else {

    //We&#39;ve been called from the action menu so toggle fps counting.
    //fpsState has 3 states to guard against the &#40;very narrow&#41; race
    //condition where a user can hit the toggle action very quickly
    //twice in succession. This could cause two instances of the counter
    //to run if a simple boolean state was used
    switch &#40;fpsState&#41; do {
    case &#34;running&#34; &#58; { call stopFps;};
    case &#34;stopped&#34; &#58; { call startFps;};
    case &#34;stopping&#34; &#58; {}; //do nothing
    };

    };

    [/QUOTE]

    Please, no complaints that the double while-loop should be a &#39;waitUntil&#39;

    *Edit* reformatted CODE section following Sickboy&#39;s &#39;no-tabs&#39; suggestion



    Author of PVPmissionWizard ArmA2FPSAnalyser AddonChecker and ... squint

    Tools homepage

    Crosseyed and Painless - a blog about my ArmA2 developments



  2. #2
    Very Nice script&#33;

    But isn&#39;t it lighter to have a program like fraps running, as it reads the frames from directX instead of a loop in the engine?
    ergo, it seems best to have as low as possible scripts/loops running within the engine?



    A.C.E. Advanced Combat Environment

    Dev-Heaven.net Free Project Hosting | A2 Community Issue Tracker Help BIS, Help yourself!

  3. #3
    Master Gunnery Sergeant
    Join Date
    Aug 24 2003
    Posts
    1,292
    Author of the Thread
    Quote Originally Posted by [b
    Quote[/b] ]But isn&#39;t it lighter to have a program like fraps running, as it reads the frames from directX instead of a loop in the engine?

    As I said, I developed this mainly to test out different techniques - I particularly like the fact that it all runs from a single file. Whether it&#39;s actually useful to anyone else is debatable

    To actually address your question, I&#39;ve got nothing against FRAPS but I think it&#39;s unlikely that the overhead from a small script running in the game engine will be higher than that of an external process. Of course, having a second core to run FRAPS on or discovering that the ArmA parser was extraordinarily slow might change my opinion. I doubt the effect in either case is noticeable.

    I think the more practical use of a script like this would be to is monitor fps in-game so that other scripts could dynamically adjust their own processing load. Eg, if the player has a high number of fps a ballistics simulation script could afford to use a finer step size.

  4. #4
    Quote Originally Posted by (sbsmac @ Dec. 28 2006,16:50)
    ...
    Sweet&#33; Very well thought of. I love it in 1 script aswell, especially with the functions that disable/enable it again etc, as said before, nice job&#33;

    And ur right, fraps indeed probably uses more resources than a loop ingame

    One thing though, if while loops exceed or hit 10.000 and they get terminated, don&#39;t they terminate the whole script, and not just only that while? I thought it would kill the whole script and as such ppl where using a counter < 9999.

    btw, if you wish to make it more readable in [ code ] stuff, try replacing tabs by 4-8 spaces or so, not in ur script itself of coarse but just in what you post




  5. #5
    Master Gunnery Sergeant
    Join Date
    Aug 24 2003
    Posts
    1,292
    Author of the Thread
    Quote Originally Posted by [b
    Quote[/b] ]One thing though, if while loops exceed or hit 10.000 and they get terminated, don&#39;t they terminate the whole script, and not just only that while?
    Fair question, but no, it seems that the iteration count for a loop is a property of that loop and is reset upon entry to the loop. I tested this by adding this code just before the call to Sleep.

    <table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">
    _cnt = 0;
    while {fpsState == &#34;running&#34;} do {_cnt=_cnt+1;};
    hint format &#91;&#34;cnt %1 %2&#34;,_cnt,time&#93;;
    [/QUOTE]

    Obviously this loop will hit 10000 pretty quickly and would cause the entire counter to exit if the iteration count was a property of the script. As it happens, it keeps on running fine. The hint line shows _cnt always at 10000 with the time ticking along as expected.

    BTW, you can see why it is important for loops to relinquish control and be &#39;cooperative;&#39; by considering that adding this inner loop reduces my average fps from 28 to 11 &#33;


    *Edit*
    Quote Originally Posted by [b
    Quote[/b] ]btw, if you wish to make it more readable
    Good tip - thanks.





  6. #6
    It&#39;s nice.

    Suggestions:

    Formatting suggestions: Instead of going step by step, I figured I&#39;d just do a quick reformat. You&#39;re sharp, so you&#39;ll probably pick up on what all I did pretty dang quick. These are only suggestions though, so take it however you wish.

    <table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">// In-game FPS &#40;Frames Per Second&#41; counter
    //
    // Version 1.0, 28 dec 06
    // Created by sbsmac
    //
    // To use this script add the following to the player initialization field&#58;-
    //
    // s=&#91;&#93; execVM &#34;fps.sqf&#34;
    //
    // Note that &#39;s&#39; is a dummy variable to keep the parser happy.
    //
    // You should then find that your action menu contains &#39;Toggle FPS&#39;.
    // Select this option to toggle fps counting on and off



    if &#40;count _this ==0 &#41; then
    {

    //This is the code that is run at initialisation. It sets up
    //the required variables and functions and adds the &#39;toggle&#39; action.

    fpsState = &#34;stopped&#34;; //global variable to control state of counter

    //the counter function- this runs continuously until halted
    startFps =
    {
    fpsState = &#34;running&#34;;
    //build in a little bit of smoothing by averaging the last n frames
    _numFrames = 10;
    _frameIndex = 0;
    _frameTimes = &#91;&#93;;
    //clear out the buffer &#40;needs to be one larger than num frames&#41;
    for &#34;_i&#34; from 0 to &#40;_numFrames&#41; do {_frameTimes set &#91;_i,0&#93;;};

    //use a double loop here because of the ArmA limitation that
    //loops terminate after 10000 iterations
    while {fpsState == &#34;running&#34;} do
    {
    while {fpsState == &#34;running&#34;} do
    {
    _frameTimes set &#91;_frameIndex,time&#93;;
    _frameIndex = &#40;_frameIndex +1&#41; % &#40;_numFrames+1&#41;;
    hint format
    &#91;
    &#34;fps %1 &#40;over last %2 frames&#41;&#34;,
    round&#40;&#40;_numframes&#41;/&#40;time-&#40;_frameTimes select _frameIndex&#41;&#41;&#41;,
    _numFrames
    &#93;;

    //delay a little bit - this forces a wait until
    //the next frame
    Sleep 0.001;
    };
    };
    hint &#34;&#34;; //clear the hint box
    fpsState = &#34;stopped&#34;;
    };

    //function to request that the counter be stopped
    stopFps =
    {
    fpsState = &#34;stopping&#34;;
    };

    //having set up the functions, add an action to control them
    //the exact string passed to the script is actually irrelevant
    player addAction &#91;&#34;Toggle FPS&#34;,&#34;fps.sqf&#34;,&#91;&#34;toggle&#34;&# 93;&#93;;

    }
    else
    {

    //We&#39;ve been called from the action menu so toggle fps counting.
    //fsp_state has 3 states to guard against the &#40;very narrow&#41; race
    //condition where a user can hit the toggle action very quickly
    //twice in succession. This could cause two instances of the counter
    //to run if a simple boolean state was used
    switch &#40;fpsState&#41; do
    {
    case &#34;running&#34; &#58; {call stopFps;};
    case &#34;stopped&#34; &#58; {call startFps;};
    case default &#58; {}; //do nothing
    };

    };
    [/QUOTE]

    Case structures:
    Try
    <table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">
    switch &#40;fpsState&#41; do
    {
    case &#34;running&#34; &#58; {call stopFps;};
    case &#34;stopped&#34; &#58; {call startFps;};
    case default &#58; {}; //do nothing
    };
    [/QUOTE]
    in place of
    <table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">
    switch &#40;fpsState&#41; do
    {
    case &#34;running&#34; &#58; { call stopFps;};
    case &#34;stopped&#34; &#58; { call startFps;};
    case &#34;stopping&#34; &#58; {}; //do nothing
    };
    [/QUOTE]

    The default accounts for every other contigency, other than running and stopped. I suppose it doesn&#39;t really matter in this case though, since it&#39;s do nothing anyways. It&#39;s just a form thing I guess. Generally, in programming, you&#39;ll always use the default in a case structure, even if it&#39;s only to throw an error.


    Nested loops:
    That loop will run out after 27.7777 hours. I&#39;d suggest you nest it one deeper. That&#39;ll make it 277,777.777 hours instead. Although in 99.999% of cases, that wouldn&#39;t be an issue, you may end up with an insane mission designer using it, and an even more insane player playing it.


    I&#39;m sure there&#39;s a way to optimize that loop a little too, although I&#39;d prefer to wait till I&#39;ve slept to make any suggestions there.

    Edit: something was wrong there, so I removed it.



    Everything I have released for ARMA can be found here.

  7. #7
    Master Gunnery Sergeant
    Join Date
    Aug 24 2003
    Posts
    1,292
    Author of the Thread
    Colonel, thanks for the comments. A little about me - I&#39;ve been writing code for over 20 years including embedded kernels, pre-emptive schedulers and real-time device-drivers in parallel-processing environments so you&#39;ll forgive me if I have my own opinion about how things should look

    In the switch statement I prefer explicit case checks because I prefer to know when something has gone wrong (undefined state) earlier rather than later. To be really correct the case statement should actually look as it currently does but with a default clause to throw an error (so we&#39;re agreed there) but this is only a script... ;-)

    By my calculation the counter will run for 100 million iterations (10000 x 10000). Since it runs once per frame it will run for 1 million seconds (around 13 days) even at 100fps and if you have a machine that will run ArmA at 100fps please let me know the specs - I want to get one &#33;

  8. #8
    Master Gunnery Sergeant
    Join Date
    Aug 24 2003
    Posts
    1,292
    Author of the Thread
    Quote Originally Posted by [b
    Quote[/b] ]I&#39;m sure there&#39;s a way to optimize that loop a little too
    Several actually:-

    * Using a power-of-two array size is the obvious one (masking is generally much cheaper than the modulo operation).

    * Only perform the hint if the number of fps has changed since the last hint (or else a certain time has elapsed). (Display and format operations are likely to be relatively expensive.)

    * Cache the time instead of re-reading it when recalculating fps. (Calling a function is likely more expensive than reading a variable.)

    * Change the number of frames to a power of two, allowing the division to be replaced by a logical shift right by a fixed number of bits (shifting is cheaper than division). *Edit* Didn&#39;t explain this very well. Some architectures have a cheap &#39;inverse&#39; instruction for 1/x divisions. If numFrames is a power of two you can then shift the result rather than multiply it. Unlikely to apply to ArmA though so I&#39;m reaching a bit with this one

    But... the inner loop is executed once per frame. After seeing the interpreter run 10K iterations of a simple loop in less than measurable time, it seems pointless to optimise the code at th expense of readability or correctness.

    Note there is one potential area of failure for the script. I&#39;ve made the assumption that the game engine runs each script once per frame. Therefore any delay in a script will cause it to be descheduled and to wait until the next frame. It&#39;s just about possible that the game-engine might run this script, deschedule it, then run another that runs for > 0.001 seconds. It might then check back for waiting scripts and find this one ready to run. I think it&#39;s unlikely though.










  9. #9
    Quote Originally Posted by (sbsmac @ Dec. 28 2006,12:49)
    Colonel, thanks for the comments. A little about me - I&#39;ve been writing code for over 20 years including embedded kernels, pre-emptive schedulers and real-time device-drivers in parallel-processing environments so you&#39;ll forgive me if I have my own opinion about how things should look
    Cool, I&#39;m a programmer too, and did recognize it as a standard formatting scheme. I just always suggest the scheme presented above, whenever the oppurtunity presents. I think I really just like how the braces are more symetrical in my format .

    Quote Originally Posted by (sbsmac @ Dec. 28 2006,12:49)
    In the switch statement I prefer explicit case checks because I prefer to know when something has gone wrong (undefined state) earlier rather than later. To be really correct the case statement should actually look as it currently does but with a default clause to throw an error (so we&#39;re agreed there) but this is only a script... ;-)
    You could always try the throw command, but yeah, it&#39;s not a big deal really.

    Quote Originally Posted by (sbsmac @ Dec. 28 2006,12:49)
    By my calculation the counter will run for 100 million iterations (10000 x 10000). Since it runs once per frame it will run for 1 million seconds (around 13 days) even at 100fps and if you have a machine that will run ArmA at 100fps please let me know the specs - I want to get one &#33;
    Well, you&#39;re right. Pretty much a bonehead mistake. Although I do get 300 fps in max zoom in on the map in some areas, that&#39;s still nearly 4 days straight. So yeah, it&#39;s cool too probably.

    As far as optimizing the loop, I was thinking more along the lines of something else. More towards taking advantage of the way the game executes.

    Like I said though, I&#39;m tired. I&#39;ve been up a while, and that&#39;s why I made that bone head mistake above.

  10. #10
    Master Gunnery Sergeant
    Join Date
    Aug 24 2003
    Posts
    1,292
    Author of the Thread
    Quote Originally Posted by [b
    Quote[/b] ]Like I said though, I&#39;m tired. I&#39;ve been up a while, and that&#39;s why I made that bone head mistake above.
    No sweat - we&#39;ve all been there. I&#39;ve lost count of the number of times I&#39;ve woken up in the early hours thinking &#39;damn - &#39;why the hell did I write _that_?&#39;

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •