openFrameworks icon indicating copy to clipboard operation
openFrameworks copied to clipboard

FPS timing with chrono

Open dimitre opened this issue 2 years ago • 4 comments

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 avatar Jan 18 '24 16:01 dimitre

cc @artificiel

dimitre avatar Jan 18 '24 17:01 dimitre

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

dimitre avatar Jan 23 '24 00:01 dimitre

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

ofTheo avatar Feb 05 '24 16:02 ofTheo

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

dimitre avatar Feb 05 '24 19:02 dimitre

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

  • https://github.com/openframeworks/openFrameworks/issues/7760#issuecomment-1820046935

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 avatar May 06 '24 22:05 dimitre

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 avatar May 08 '24 20:05 dimitre

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 avatar May 09 '24 03:05 dimitre

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.

dimitre avatar May 11 '24 02:05 dimitre