When we last left our intrepid window, it looked like this:
And it clocked in at around 9000 frames per second by eating all the CPU time it could get its hands on. This is not ideal. We could call SDL_Delay() with a value of 1ms every frame to cap it at 1000 FPS, and that drops CPU utilization down to 0% for me. This, while better than attempting to use your computer as a space heater, is still not a good solution. It is extremely rigid and can’t adapt to differing loads (and is extremely wasteful).
What we want is something that will limit us to 60 FPS (See !!Science!! #2 for details on why) no matter how heavy or light a singular frame’s workload is, as well as providing a FPS counter. SDL_gfx comes with a frame limiting function (no fps counter though), and I’m not overly fond of the implementation, so we’re going to build our own variation.
This was my original implementation. Mostly straightforward, SDL_GetTicks() returns the number of milliseconds that have passed since the program was run. In going over it for this tutorial though, I found it somewhat lacking.
The FPS counter should be accurate enough computationally (not visually, where it becomes inaccurate with frame time spikes), but there does exist an edge case where a frame time spike on the last frame of a given second could potentially cause ‘seconds’ to last longer than an actual second by the time of the last frame if it was greater than the time allotted. I’m not overly worried about this part.
This interpretation of the frame limiter is a little more forgiving with frame time spikes for frame-based movement (which admittedly we shouldn’t be doing) than SDL_gfx would be. Instead of starting over with a maximum frame time delay after a longer than limit frame, it will attempt to catch up to existing frame targets. This does mean bad spikes could be hidden underneath computational power. The counter will say 60 fps, but visually you won’t be getting that due to monitor refresh rates. I’m not sure if I want to change that or not, you get the exact same visual frames either way, the only things that changes is the counter accuracy. I’m thinking I’d rather know that I could do 60 fps if it wasn’t for the spike and have the spike brought to my attention than know exactly how many visual frames the spike cost me.
So the trick becomes providing enough information that we can see any problems without turning this into an extremely complicated mess. I’m also kind of torn on the update function. Returning the data from the limit function is probably a better thing to do, as it allows you to parse and display it elsewhere, or maybe just return a bool stating whether there’s new data and add in a get_stats() function.
So I went and made a few changes. There’s a new struct that holds the data for the last frame (Which includes min frame processing time, max frame processing time, and average frame processing time) accessible anywhere. You might want to roll it into the object, with associated functions to pull the data instead, that’s your call. The new stats should help illustrate if you are having spiking problems. I’ve also removed the update functionality and stuck it in the main loop. The new limit function returns true every second, and you can use that to do specific update functionality in program specific code instead of changing the framework coding for each program.
And that rounds out another section of our framework.