A platform agnostic version could be to use the standard <chrono>
durations and time points.
Example:
#include <chrono>
#include <thread>
int main() {
int FPS = 250;
auto time_between_frames = std::chrono::microseconds(std::chrono::seconds(1)) / FPS;
auto target_tp = std::chrono::steady_clock::now();
while(true) {
target_tp += time_between_frames; // calculate target point in time
std::this_thread::sleep_until(target_tp); // sleep until that time point
// do stuff
}
}
As long as do stuff is done in a shorter time than time_between_frames
on average this should keep a pretty stable average of 250 FPS.
A low CPU using, but almost busy waiting, version could be to sleep slightly shorter than needed in a loop, hoping that the thread will get scheduled in time.
You can combine it with a threshold value to stop sleeping in the busy loop when you get really close to the target time_point
.
std::chrono::microseconds threshold(10); // some low value (trim 1)
std::chrono::time_point<std::chrono::steady_clock> now;
decltype(target_tp - now) sleep_time;
while(true) {
target_tp += time_between_frames; // calculate target point in time
// do stuff
// an almost busy waiting loop
while((now = std::chrono::steady_clock::now()) < target_tp) {
sleep_time = target_tp - now;
if(sleep_time > threshold) {
// sleep 2/3 of the time left (trim 2)
std::this_thread::sleep_for(2 * sleep_time / 3);
}
}
}
The parameters (trim 1 and trim 2) probably need to be calibrated by the program itself at start-up or re-calibrated while running to provide the lowest CPU usage while providing an acceptable FPS stability.
Removing one trim parameter to make calibration simpler would leave this:
std::chrono::microseconds threshold(20); // some low value
while(true) {
target_tp += time_between_frames; // calculate target point in time
// do stuff
// sleep short ...
std::this_thread::sleep_until(target_tp - threshold);
// busy wait
while(std::chrono::steady_clock::now() < target_tp) {}
}
I'm guessing that the threshold
would need to be slightly larger and that the CPU usage would be slightly higher in this version - but it would need to be thoroughly tested to say for sure.
On my machine, all three versions puts < 1% load on the processor they run and provide a pretty stable 250 FPS even when do stuff takes 94% of time_between_frames
on average with an extreme spread (a random sleep between 0
and 17 * time_between_frames / 9
, uniformly distributed).
Sleep
. It is not a "scheduling problem", as you call it, it's a user's understanding problem. – Fugere