openframeworks / openFrameworks

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

Sleep() and ofSetFrameRate accuracy #1873

Open kamend opened 11 years ago

kamend commented 11 years ago

Hi guys, Here is something strange I am fighting with the last few days. I have a PointGrey Flea3 cam, which could run at 120 fps. I am trying to run a basic application under Windows 7, using their FlyCapture SDK, although the problem I am having is also present, if I run just the basic empty example application.

Basically I am doing this, at the setup() of my application, I would set:

ofSetFrameRate(120); ofSetVerticalSync(false);

and what I would get is Openframeworks running at no more then ~64 fps. The really weird thing is that sometimes after a while, the application will start running at my desired rate, but if I restart, I am back at the ~64 fps limit.

So, I dug deeper into the matter and I noticed the Sleep() method inside the ofAppGlutWindow idle_cb function. When I set the frame rate to 120 fps, the idle_cb function correctly calculates that it has too sleep for 8ms and waitMillis is indeed 8ms, but for some reason the Sleep function could not get lower then 16 ms sleep, which I measured using a suggested on the forums more accurate timer - QueryPerformanceCounter. So, I guess the problem here is the resolution of the Sleep method and the most weird thing is that sometimes it would work, sometimes it would not. I tested this on my MacBook Pro and on my Desktop machine, so I guess it's not hardware related.

I did another test too, setting ofSetFrameRate(0), bypassing the Sleep function. But then I get a resolution problem with ofGetElapsedTimef(), I still could not get a difference between the frames lower then 16ms. So I did another test with QueryPerformanceCounter and measured it against the ofGetElapsedTimef() inside the "update()" function and what I noticed is that when I set the FlyCaptureSDK to 120 fps, the application was indeed running in 120 frames per second, but OF was showing ~64fps. Basically the difference between frames using QueryPerformanceCounter was 8ms, but the difference measured with ofGetElapsedTimef was 16ms. Once again I should mention, that sometimes after like 10 minutes, I would get the resolution and the application showing correct frame rate, but when I restart it will be back at the ~64 fps limit.

Does anybody actually have this problem, is this a known issue or am I doing something completely wrong here?

Here is the QueryPerformanceCode, I am usuing for measurement:

class Timer {
public:
    double PCFreq;
    unsigned long long CounterStart;

    void StartTimer() {
         LARGE_INTEGER li;
         if(!QueryPerformanceFrequency(&li))
            cout << "QueryPerformanceFrequency failed!\n";

         PCFreq = double(li.QuadPart)/1000.0;

         QueryPerformanceCounter(&li);
         CounterStart = li.QuadPart;
    }

    double Timer::GetElapsedTime()
    {
        LARGE_INTEGER li;
        QueryPerformanceCounter(&li);
        return double(li.QuadPart-CounterStart)/PCFreq;
    }

};

Keep up the good work, Kamen

kamend commented 11 years ago

Here is actually how the Cinder guys, has dealt with replacing the Sleep() method, I guess they had the same issue.

https://github.com/cinder/Cinder/commit/6352526d12e67156c9b78c7b86a52bd6a8e4c08c

openframeworks commented 11 years ago

Kamen,

can you try altering this line in ofConstants.h

// #define WIN32_HIGH_RES_TIMING

// note: this is experimental! // uncomment to turn this on (only for windows machines) // if you want to try setting the timer to be high resolution // this could make camera grabbing and other low level // operations quicker, but you must quit the app normally, // ie, using "esc", rather than killing the process or closing // the console window in order to set the timer resolution back // to normal (since the high res timer might give the OS // problems) // info: http://www.geisswerks.com/ryan/FAQS/timing.html

try uncommenting the #define WIN32_HIGH_RES_TIMING and rebuilding OF -- it alters some code in ofAppRunner.cpp that should allow sleep to be more precise.

I'm curious if that helps?

kamend commented 11 years ago

Hi Zach, It does work and the resolution of Sleep() is higher. May be a Notice to use the HIGH_RES_TIMING, when someone sets ofSetFrameRate to higher then 60 fps will be nice, or a comment in the code why would someone need to do this, will be really helpful, if somebody has the same issue.

Is there any particulare reason, why Openframeworks does not use QueryPerformanceCounter for getting the ellapsed time, its more accurate, even without setting the timeBeginPeriod()/timeEndPeriod().

Thanks, Kamen

kamend commented 11 years ago

Also, in order fo timeBeginPeriod() to work, I needed to include or I get an undefined error.