FPS, frame MS and frame stepping helper

BlitzMax Forums/BlitzMax Programming/FPS, frame MS and frame stepping helper

Hezkore(Posted 2016) [#1]
NOTICE: This will NOT help with the speed or smoothness of your game.
It will however tell you how smooth it goes.

Here's my little FPS and frame MS helper I use in random projects.
It's a quick way to get an FPS counter into your project and also find out how long it takes to render each frame.
It also comes with a cheap way of doing frame stepping. (pausing time and moving one frame ahead)
And also a sort of history graph for both FPS and frame MS.

So instead of using something like Fraps or Dxtory, you can just import this.

You'll find it over at https://bitbucket.org/Hezkore/tfps
Along with an example.

Feel free to use it or improve it.
If you do improve it somehow, please share it. :)




dw817(Posted 2016) [#2]
Hi Hezkore:

* Errm ... BlitzMAX has a unique way of handling how fast something goes. They use a command called CreateTimer()

I wrote code to demonstrate this earlier HERE:

Updated: 04/07/16
http://www.blitzbasic.com/Community/post.php?topic=105731&post=1293684


Hezkore(Posted 2016) [#3]
This has nothing to do with handling time or handling how fast things go.
It's about telling you how efficient your game is running.

I can for example tell you that your rain example runs at roughly 59 frames per second, and that it takes 17-18 milliseconds to render each frame.
I can also pause time and slowly move through each frame and actually see the rain drops being created on the screen thanks to the frame stepping.

Btw, you shouldn't rely on CreateTimer() and your link is broken.


Derron(Posted 2016) [#4]
18ms ...is what it should take as dw is using "Delay 18" in the code.


bye
Ron


Hezkore(Posted 2016) [#5]
16ms is what he's using, which would result in about a 62,5 FPS.
But since delay is not very exact and only does a "minimum"of 16ms delay, he ends up with a slightly higher delay and lands at about 59 FPS.
Which is why the delay function isn't always the best thing to use.




Derron(Posted 2016) [#6]
Ok, then delaying 16 - rest is the drawing/flipping.

So the reason for having a lower amount of fps is the speed of your machine. If "draw + flip" happens in <1ms, the delay is nearly the only "time taking" command.
The slower your computer/gpu, the less accurate "Delay 16" will behave to the expectation.


I am using a custom delta timer which tries to delay some time too (cpu load ) but only if frame rate capping is active.

-> instead of delaying a big pile of milliseconds at once, I only delay for 1 millisecond and skip doing things if there isn't enough time gone. So instead of "loop update delay16" I have an "loop update delay1? | loop skipUpdate delay1? | loop skipUpdate delay1? | ..."-approach.


bye
Ron


Hezkore(Posted 2016) [#7]
Yeah most of the rest is the drawing.
But even with no flipping or drawing, delay won't be exact as it only does a minimum of the time you've specified.
It kinda depends on your background processes, those are given some time to work while a delay is active.
So it's not until those background processes say it's okay to continue that the delay ends. (kinda)
It usually isn't a much longer delay, but it's not very exact and should probably not be used for important timing stuff (like FPS) in a proper project.


Derron(Posted 2016) [#8]
I still do not get what your code exactly does:

- there is an "Update()" which I am supposed to call ... to call when?
- - call it during the RenderWorld-call? No ... as it handles Keyboard-actions
- - call it during the UpdateWorld-call? No ... as it measures render times...

Think it should be split into things done at a logical-update (updateWorld) and things done during rendering.


For now this is not true:
It's a quick way to get an FPS counter into your projects and also find out how long it takes to render each frames.


It measures how long the whole "update+render"-thingie takes. If you now use that Update in your "RenderWorld"-function only, you are making it inaccurate as it uses KeyHit/FlushKeys (something "logical") within a function only doing "draw this here and there".


If you extract that Key-handling into a custom function ("HandleInput()") you could call it in your update-function and this way keep the normal "Update()" function clean.
This eg. allows for a 200hz update rate (and its input handling) compared to a low 30Hz render rate.


Do not get me wrong: it works, but imho all of our projects and sourcecodes provide some bits to optimize here and there :-)


bye
Ron


Hezkore(Posted 2016) [#9]
The only time it actually uses key inputs is when it's taking control of the application. (via frame stepping)
Which I'll admit isn't the best at its current stage heh.
But since the game is paused anyways, it doesn't really matter as you're the once advancing frames and not the game anymore.

Throw TFPS.Update() in right before your flip and just call Print TFPS.FPS() anywhere and you're done.
Or use Print TFPS.FrameMS() if you wan't some more exact data on how long it takes to render your frames.

It's really just a simple counter for how many frames you've got per second.
But it's good for spotting slowdowns in your game.
Handling the speed of your game is something different.


Hezkore(Posted 2016) [#10]
Here's en example showing it all.
If you drag the window around, you'll see the lag spikes quite clearly.

You don't have to use any of the frame stepping stuff or history graphs.
You could (as I mentioned above) just call TFPS.Update() before you flip the buffers, then at any point call TFPS.FPS() to see how smooth the game is going.

<Code removed, see first post>


dw817(Posted 2016) [#11]
Sorry 'bout that. Fixed the link.

And yes, DELAY is quick and dirty. Methods involving FPS counters and CreateTimer() are preferable for high accuracy in timing animation effects.


Derron(Posted 2016) [#12]
Ok, found something to optimize ;-)

you are iterating over the array and moving one after another to the previous spot (x[i] = x[i+1]). The optimization is to use a currentPosition / arrayCursor ... this cursor is increased/decreased and wraps at 0 or max (aka "x = (x + 1) mod array.length").

While the 10-sized array might not benefit from it ("mod" is rather expensive) that much, the 128-sized array will do.


Sorry for "nitpicking" but I wouldn't let you go with something useful ;-)



bye
Ron


Hezkore(Posted 2016) [#13]
Yeah thanks Derron! I actually had it like that at first. :)
But I didn't really notice much of an impact on speed and I didn't like when it looped back to the start (was a bit confusing to watch) so I chose this method instead.
If we could somehow still make the right side of the history graph always be the newest frame I'd do your method.

The code is now hosted over at Bitbucket.
It'll make sure that the code is always up to date and not lost here on the forums.

I've also updated it so that you no longer have to call TFPS.Update()
Oh and it's now a module heh.
Import the module and just call TFPS.FPS() to see your current FPS.


Derron(Posted 2016) [#14]
@right side of the historygraph
Just use the "currentPosition" (or how you call) it counter to find out the starting index.

Instead of
for local bla:int = EachIn array

you then do
local arrayIndex:int = currentPosition
for local offset:int = 0 until array.length
arrayIndex = (currentPosition + offset) mod array.length
...

The "newest" is always before (or after) the "currentPosition" value...


bye
Ron


Hezkore(Posted 2016) [#15]
Ah yes, thank you.
I'll try to get that in some time! :)


BlitzSupport(Posted 2016) [#16]
Nice demo! I find FPS-related stuff gets really complicated once you get into it, so much so that I can't really deal with it, but that demo visualises the frame spikes really well, even from clicking on other windows and the like... looks very smooth to me, too.