Solved

Constant frame rate using threads and win32

Posted on 2004-09-22
7
1,223 Views
Last Modified: 2008-01-09
HI, my goal is to have a separate rendeirng thread be able to render and a constant frame rate of say, 30fps.  
Info: Single CPU, win32 App, openGL, C++.  
I have one rendering thread who's sole purpose in life is:  to be created, initialized, wait to start, render at a constant frame rate, wait for a signal to stop rendering, then exit.  Let's also assume the code is thread safe :)

So, at every 33.3 ms, the thread will execute some rendering functions, then "go to sleep" and do nothing until the next 33.3ms has ellapsed.  

Q1: What are good coding techniques in general to accomplish something like this.?

Q2:  Please comment on the following 2 methods:
Method A:
do
{
      switch (myWin->threadSync->waitEvent(T))
      {
      case WAIT_TIMEOUT:
            //Sampling Interval, so render to screen
            render();
            //check the accuracy
            samplingTimes.push_back(renderingTimer->GetTime()*1000);
            renderingTimer->Reset();
            break;
      case WAIT_OBJECT_0:
            //signal from main to exit rendering loop
            threadMain.setThreadExit(TRUE);
            break;
      default:
            break;
      }
}while(!threadMain.getThreadExit());

Method B:
while(!threadMain.getThreadExit())
{
      if (renderingTimer->GetTime()*1000 >= T)
      {
            //check the accuracy
            samplingTimes.push_back(renderingTimer->GetTime()*1000);
            renderingTimer->Reset();
            //Sampling Interval, so render graphics
            render();
      }      
}

The renderingTimer uses the QueryPerformanceCounter, so should be pretty accurate(I think?).  In method B, another thread is needed to set threadMain.threadExit to TRUE to stop the loop, where as in Method A, another thread must use the SetEvent() to stop the thread.

Q3:
In Method A, the sampling time is based on when there is a timeout in the WaitForSingleObject().  How accurate is this?  

Q4:
I'd like to know more about thread priorities and process priority classes.  I did a small test to see the effects of thread priorities using the above 2 methods.  Console App, main thread, and one single thread for rendering using the default settings.  
The main() has the thread render for 5 seconds.  And I checked how accurate the sampling times were.  Below are the results:
THREAD_PRIORITY_TIME_CRITICAL:
Method A error: 6.0687
Method B error: 0.001158

THREAD_PRIORITY_HIGHEST:
Method A error: 6.0205
Method B error: 0.00086314

THREAD_PRIORITY_ABOVE_NORMAL:
Method A error: 5.9933
Method B error: 0.0010459

THREAD_PRIORITY_NORMAL:
Method A error: 5.7194
Method B error: 131200.0168



the error is the sum of the squared errors of each sampling time.  The key change is from THREAD_PRIORITY_ABOVE_NORMAL to THREAD_PRIORITY_NORMAL.  Can you please comment on the results.  Note I did not change the default process priority.  

Q5:
At the end of the day, I need an accurate(as accurate as QueryPerformanceCounter) constant frame rate.  What is the solution?

Thanks for your help




0
Comment
Question by:minstrelz
7 Comments
 
LVL 9

Expert Comment

by:jhshukla
Comment Utility
i would suggest using a double buffer. i haven't done any openGL programming as of yet so i could be partially wrong. here is the pseudo code:

use double buffer
while ( ! signal to die ){
  render in the "ghost" buffer;
  wait for 33.3 ms signal;
  swap buffers;
}

an expert with more OpenGL experience should be able to provide more help.

jaydutt
0
 
LVL 39

Accepted Solution

by:
itsmeandnobodyelse earned 250 total points
Comment Utility
Q1:

As Windows isn't a real-time system, it isn't guaranteed that your thread gets scheduled at 33 fps frequency. That means, that it highly depends on your CPU (e. g. hyperthreading or not) and the traffic on your machine. I would guess that any major file access by any other application will spoil your frame rate.

The next issue is the time your rendering will take. Use GetPerformanceCounter and GetPerformanceFrequency calls to find that out. I would say, if this time isn't less than 10 ms, there is no chance to ahieve 33fps.

Q2:
In both methods the GetTime()*1000 isn't accurate (i assume GetTime() gives seconds). You have to use GetPerformanceCounter to have the accuracy needed. Method2 has no wait/sleep function, so it would catch all CPU time it could get. You have to include Sleep(1); to your loop. However, the timing isn't accurate (about 10ms), so i would prefer method  A (i assume you are using a waitable timer), though i don't know whether it's more accurate than Sleep.

Q3: see Q2

Q4:
>>> the error is the sum of the squared errors of each sampling time.

I'm sorry, i don't know what you are measuring here. I would call GetSystemTime(), timeb() function or QueryPerformanceCounter to measure your real frequency (you have to calculate averages aof about 1000 runs).

Q5:
Hope, i could help to find it.

Regards, Alex


0
 
LVL 22

Expert Comment

by:grg99
Comment Utility
First, I'd see how much CPU time the rendering thread uses per frame.  It had better be considerably less than 1/33 sec!

The exact timing is mostly irrelevant.    Your screen is getting hardware refreshed at whatever rate you've set in the Display control panel (60 to 80 Hz are typical rates).    Most display libraries sync themselves to this rate whenever you call for a full-screen update.  If they didnt you'd see all kinds of tearing and jiggling.   so "33 fps" ends up being "whatever sub-multiple of the screen refresh rate is greater and 33fps.  For example, if your refresh rate is 60 Hz, the screen will get a fresh image every OTHER scan, effectively 30fps.   If you shoot for 30fps, you run the danger of missing the exact scan rate by a little bit and effectively getting 60/3 or 20fps.





0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 

Author Comment

by:minstrelz
Comment Utility
response to itsmeandnobodyelse:
I dont know what hyperthreading is, so can you please explain its effect on the question.  You mention that it could depedn a lot on the traffic on the machine.  So, what about setting process priority to THREAD_PRIORITY_TIME_CRITICAL?  When would be a good/bad time to do something like this?
Also, from your response, the renderingTimer does use QueryPerformanceCounter to get the sampling times.  Lastly, does anyone know whether the Sleep() is more/less accurate than WaitForSingleObject( ) with some timeout?
thanks
0
 

Author Comment

by:minstrelz
Comment Utility
correction to above: I mean setting the process priority to REALTIME_PRIORITY_CLASS
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
hyperthreading is the capability of some INTEL CPU's (newer Pentium IV, Itanium) to work similar to a double-processor unit. So, the threads of an application may work quasi-parallell and are scheduled more often than with a single CPU.

REALTIME_PRIORITY_CLASS

Any priority given doesn't make the system to a realtime system. However, if you can guarantee that there is only one thread that has this kind of priority you should have a chance that the average times of scheduling are near to your goal.

Regards, Alex
0
 

Author Comment

by:minstrelz
Comment Utility
does anyone know whether the Sleep() is more/less/as accurate than WaitForSingleObject( ) with some timeout?  why or why not.  
0

Featured Post

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

772 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now