openframeworks / openFrameworks

openFrameworks is a community-developed cross platform toolkit for creative coding in C++.
http://openframeworks.cc
Other
9.94k stars 2.55k forks source link

FPS timing with chrono #7867

Closed dimitre closed 5 months ago

dimitre commented 9 months ago

This PR has the intent of modernize OF framerate managing. The idea is using only std::chrono for two reasons : let std::chrono manage all time divisions in the best way possible, and unifying code across platforms. it was tested in macOS and reduced FPS drift compared to ofTimer implementation

dimitre commented 9 months ago

cc @artificiel

dimitre commented 8 months ago

I've made a proof of concept here and I'm attaching one image and the code. the code itself is standalone it can be run on OF master it changes from OF to this new mode each 4 seconds to compare the drift it can be run with different framerates, vertical sync on and off to test. tests run better on macOS with this new setFps. there is a new getFps in there too, stabilize quicker, but is not as filtered as the OF default one

Screenshot 2024-01-22 at 21 52 04

fpsTest.zip

cc @ofTheo @artificiel

ofTheo commented 8 months ago

@dimitre - Awesome! Might be good to test on Windows and Linux before merging.

dimitre commented 8 months ago

Great! I'll be testing in the next days and report back here.

dimitre commented 5 months ago

@artificiel can you please test this PR with your tests mentioned here?

I think this PR can be a slight improvement to actual FPS. and here is also an alternative way of counting FPS

#include <chrono>
using namespace std::chrono;
using namespace std::chrono_literals;

struct fpsCounter {
public:
    int nAverages = 20;
    using space = std::chrono::duration<long double, std::nano>;
    time_point<steady_clock> lastTick;
    steady_clock::duration onesec = 1s;
    std::vector <space> intervals;
    space interval;
    space average;
    bool firstTick = true;
    int cursor = 0;

    void tick() {
        if (firstTick) {
            firstTick = false;
            lastTick = steady_clock::now();
            return;
        }

        interval = steady_clock::now() - lastTick;
        lastTick = steady_clock::now();
        if (intervals.size() < nAverages) {
            intervals.emplace_back(interval);
        } else {
            intervals[cursor] = interval;
            cursor = (cursor+1)%nAverages;
        }
    }

    double getFps()  {
        average = std::reduce(intervals.begin(), intervals.end());
        return (double)intervals.size() * onesec / average;
    }

    float get() {
        average = std::reduce(intervals.begin(), intervals.end());
        return (float)intervals.size() * onesec / average;
    }
};
dimitre commented 5 months ago

finally a decent way of testing fps drift, measuring with ofGetFrameRate, and with chrono. you can toggle (pressing key "t") between OF classic FPS control and new one to see differences in drift. ofw/apps/devApps/fps Screenshot 2024-05-08 at 17 02 29

dimitre commented 5 months ago

now alternative fps counter has a subtle low pass filter (average of 4 last results) so both modes can be compared better. it is a great improvement in macOS. I'll be testing on Ubuntu soon Screenshot 2024-05-09 at 00 36 51

dimitre commented 5 months ago

Now finally tested in Windows, Linux and macOS, Tests show up to 25x more precision, from 10 milliframes drift to 0.3

The idea is using sleep_until and wake processor early (about 35ms early), so processor has time to go from low energy (sleep) to high energy and calculate the remaining time in a while loop.