PDA

View Full Version : OpenGL Setting FPS



SeanAustin
07-28-2009, 09:43 AM
I'm trying to load the changing colors of a model in a .txt file. So this file could contain 100 values and I want to be able to have a integer passed in the .txt to set the FPS that these values will be rendered.
Is this possible? I read mixed things on the topic of Fixed FPS and wanted the advice of the community since I don't know the credibility of the sources I read.

Thanks

Ilian Dinev
07-28-2009, 10:04 AM
Use "float curGameTime; " instead of "int curFrame;" .

On consoles (fixed hardware capabilities and power), you can use the "int curFrame" approach if you're certain there won't be frame-drops, and you target only a specific refresh-rate.
On a PC, some users will have highest-end gaming rigs, others will be using last decade's laptops; some will have force-disabled vsync in the driver panels; and anyway some users run in 60Hz refresh-rate, others at 75Hz, others at 100Hz, etc.

Your text-file:


// time R G B
0.0 : 0.4,0.4,0.4
5.7 : 1.0,0.3,0.4
14.3 : 1.0,0.3,0.4
...


Your frame-update code:



float g_CurGameTime;

vec3 Color;

void updateScene(float frameDuration){
foreach(ScriptEntry e){ // optimize this...
if(g_CurGameTime>=e->time &amp;&amp; e->time<(g_CurGameTime+frameDuration)){
Color = e->color;
}
}
...
g_CurGameTime+=frameDuration;
}

It could be better or useful to interpolate entries via linear or spline-interpolation. There are articles/tutes on it, keywords are: animation, script, spline, lerp, interpolation, timing, events.

SeanAustin
07-28-2009, 10:11 AM
Thanks for the quick response.

One clarifying question, do I call this function in the display function I use as a GLUT callback? Because if so, wouldn't this only change the variable for the period of time specified rather than setting the actually FPS?

I might be missing something tho...

Ilian Dinev
07-28-2009, 12:12 PM
Yes, inside the display-function, call this updateScene(). You'll have to measure how much time the previous frame took, and supply it as the "frameDuration" parameter.

Do not think in terms of "FPS" (frames per second), think in terms of "time passed". For some frames, your code might take 3ms , for others you might take 44ms.

SeanAustin
07-29-2009, 08:36 AM
Illian,

I was hoping to control the actual render rate of OpenGL. To "throttle down" the rendering, if you will.

Is this possible?

Ilian Dinev
07-29-2009, 08:44 AM
wglSwapIntervalEXT(0) - no frame-limit
wglSwapIntervalEXT(1) - vsync (i.e 60Hz, depends on current monitor mode)
wglSwapIntervalEXT(2) - vsync/2 (if vsync is at 60Hz, you'll update at 30Hz)
wglSwapIntervalEXT(3) - vsync/3 (if vsync is at 60Hz, you'll update at 20Hz)
etc....


Anything else: OS-dependent timers: Sleep, polling-loops, events, etc etc. Generally anything but polling-loops will be imprecise.

SeanAustin
07-29-2009, 12:46 PM
I call that... when? Prior to rendering anything? Any 30Hz doesn't correlate to 30FPS, right? Is there a way to tell what the Hz cooresponds to.

Thanks!

Ilian Dinev
07-29-2009, 01:14 PM
You call wglSwapIntervalEXT only once. There are specs about it on this site.
http://en.wikipedia.org/wiki/Hertz

marshats
07-29-2009, 08:00 PM
Since you are using GLUT, you can use glutGet(GLUT_ELAPSED_TIME) to measure time in a cross-platform way. One thing to be aware of when using any timer function, they are not guaranteed precise beyond 10-100 millisecond. You can get around that by averaging multiple frames over a second (10xprecision of timer).

If you just want to control the speed of the display loop you can simply put a blocking condition in the display loop. I have attached a piece of minimal code that measures FPS with averaging and a blocking condition in the display loop to throttle the speed of the display (to turn on the throttling loop change in the idle() function "#undef REGULATE_FPS" to "#define REGULATE_FPS"). When REGULATE_FPS is undef'ed then rendering loop will go as fast as hardware allows and FPS will be >>60 (assuming vsync off on my machine I measure FPS ~8000) and when REGULATE_FPS is define'ed then the FPS will be fixed to 1 frame per 35milliseconds = ~29FPS which FPS() will measure. Note that it takes approximately 1 second after startup for the FPS() to accurately begin displaying the measured frames per second in the printf call.

ps for advanced users: if you want to go non-cross platform to avoid having an imprecise clock measure, please try to avoid QueryPerformanceCounter in windows since the Performance counter value may unexpectedly leap forward (http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323&amp;). One approach I have found is to use the CPU RDSTC assembly op-code on Intel/AMD processors -- see the wiki at Time Stamp Counter (http://en.wikipedia.org/wiki/RDTSC) and for working code find cycle.h at the bottom. I like RDTSC opcode since it works in linux as well as windows. But note, in my applications I have been perfectly happy to live with averaging over multiple frames to deal with the imprecise clock functions. end of comment for advanced users.



//this shows one way to get FPS using an inprecise clock
//or to throttle the drawing rate to a fixed FPS
//g++ glut_fps_demo.cpp -lGL -lglut
#include <cstdlib>
#include <iostream>

//linux openGL headers
#include <GL/gl.h>
#include <GL/glut.h>

GLint gFramesPerSecond = 0;

void FPS(void) {
static GLint Frames = 0; // frames averaged over 1000mS
static GLuint Clock; // [milliSeconds]
static GLuint PreviousClock = 0; // [milliSeconds]
static GLuint NextClock = 0; // [milliSeconds]

++Frames;
Clock = glutGet(GLUT_ELAPSED_TIME); //has limited resolution, so average over 1000mS
if ( Clock < NextClock ) return;

gFramesPerSecond = Frames/1; // store the averaged number of frames per second

PreviousClock = Clock;
NextClock = Clock+1000; // 1000mS=1S in the future
Frames=0;
}

void idle() {
#define REGULATE_FPS
#ifdef REGULATE_FPS
static GLuint PreviousClock=glutGet(GLUT_ELAPSED_TIME);
static GLuint Clock=glutGet(GLUT_ELAPSED_TIME);
static GLfloat deltaT;

Clock = glutGet(GLUT_ELAPSED_TIME);
deltaT=Clock-PreviousClock;
if (deltaT < 35) {return;} else {PreviousClock=Clock;}
#endif

//put your specific idle code here
//... this code will run at FPS
printf(".");
//end your specific idle code here

FPS(); //only call once per frame loop
glutPostRedisplay();
}

void display() {
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);

// Set the drawing color (RGB: WHITE)
printf("FPS %d\n",gFramesPerSecond);
glColor3f(1.0,1.0,1.0);

glBegin(GL_LINE_STRIP); {
glVertex3f(0.25,0.25,0.0);
glVertex3f(0.75,0.25,0.0);
glVertex3f(0.75,0.75,0.0);
glVertex3f(0.25,0.75,0.0);
glVertex3f(0.25,0.25,0.0);
}
glEnd();

glutSwapBuffers();
}

void init() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0,1.0,0.0,1.0,-1.0,1.0);
}

void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 27: // escape key
exit(0);
break;
default:
break;
}
}

int main(int argc, char** argv) {
glutInit(&amp;argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
glutCreateWindow("FPS test");

glutIdleFunc(idle);
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);

init();

glutMainLoop();
return 0;
}

SeanAustin
08-04-2009, 09:45 AM
Thanks for the posts! Very helpful, I'll probably go with the non-vsync changing route.

marshats
08-05-2009, 07:07 PM
for those interested, an alternative to the code in post #261479 above is listed here based on glutTimerFunc() rather than a blocking condition in idle().



//this shows a second way to get FPS using an imprecise clock
//while throttling the drawing rate to a fixed FPS with glutTimerFunc
//g++ glut_fps_demo.cpp -lGL -lglut
#include <cstdlib>
#include <iostream>

//linux openGL headers
#include <GL/gl.h>
#include <GL/glut.h>

GLint gFramesPerSecond = 0;

void FPS(void) {
static GLint Frames = 0; // frames averaged over 1000mS
static GLuint Clock; // [milliSeconds]
static GLuint PreviousClock = 0; // [milliSeconds]
static GLuint NextClock = 0; // [milliSeconds]

++Frames;
Clock = glutGet(GLUT_ELAPSED_TIME); //has limited resolution, so average over 1000mS
if ( Clock < NextClock ) return;

gFramesPerSecond = Frames/1; // store the averaged number of frames per second

PreviousClock = Clock;
NextClock = Clock+1000; // 1000mS=1S in the future
Frames=0;
}

void timer(int value)
{
const int desiredFPS=120;
glutTimerFunc(1000/desiredFPS, timer, ++value);

//put your specific idle code here
//... this code will run at desiredFPS
char spinner[] = {'|','/','-','~','\\'};
printf("%c",spinner[value%sizeof(spinner)/sizeof(char)]);
//end your specific idle code here

FPS(); //only call once per frame loop to measure FPS
glutPostRedisplay();
}

void display() {
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);

// Set the drawing color (RGB: WHITE)
printf("FPS %d\r",gFramesPerSecond); fflush(stdout);

glColor3f(1.0,1.0,1.0);

glBegin(GL_LINE_STRIP); {
glVertex3f(0.25,0.25,0.0);
glVertex3f(0.75,0.25,0.0);
glVertex3f(0.75,0.75,0.0);
glVertex3f(0.25,0.75,0.0);
glVertex3f(0.25,0.25,0.0);
}
glEnd();

glutSwapBuffers();
}

void init() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0,1.0,0.0,1.0,-1.0,1.0);
}

void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 27: // escape key
exit(0);
break;
default:
break;
}
}

int main(int argc, char** argv) {
glutInit(&amp;argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
glutCreateWindow("FPS test");

glutTimerFunc(0,timer,0);
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);

init();

glutMainLoop();
return 0;
}

javier_macross
09-18-2009, 04:01 PM
Hello, it is very impressive. Congratulations. But I have a problem, when I use glTranslatef*() or glRotatef*() I have a poor smooth movement. What happens?

It seems as if it made small jumps. :-(

There is some way to have a smooth movement independent of fps?

for your help, thank you very much.

marshats
10-20-2009, 11:37 AM
Assuming you are calling glTranslate/glRotate from within the display() function, you have to scale your inputs to the time between frames ie dt = 1/gFramesPerSecond to make the code FPS independent.

For instance, if you want a full rotation of 360 degrees every 8 seconds then you would do something like


GLfloat dt = (gFramesPerSecond>0 ? 1.0/gFramesPerSecond : 1.0);
static GLfloat angle=0.0;
angle += 360./8.*dt
glRotatef(angle,...);

marshats
10-21-2009, 05:47 PM
Here's a follow up on the previous post... working bare code for animation using glutTimerFunc and regulating the FPS to a set value "desiredFPS".



//this shows a second way to get FPS using an imprecise clock
//while throttling the drawing rate to a fixed FPS with glutTimerFunc
//g++ glut_fps_demo.cpp -lGL -lglut
#include <cstdlib>
#include <iostream>

//linux openGL headers
#include <GL/gl.h>
#include <GL/glut.h>

GLint gFramesPerSecond = 0;
GLfloat gAngle = 0.0;

void FPS(void) {
static GLint Frames = 0; // frames averaged over 1000mS
static GLuint Clock; // [milliSeconds]
static GLuint NextClock = 0; // [milliSeconds]

++Frames;
Clock = glutGet(GLUT_ELAPSED_TIME); //has limited resolution, so average over 1000mS
if ( Clock < NextClock ) return;

gFramesPerSecond = Frames/1; // store the averaged number of frames per second

NextClock = Clock+1000; // 1000mS=1S in the future
Frames=0;
}

void timer(int value)
{
const int desiredFPS=120;
glutTimerFunc(1000/desiredFPS, timer, ++value);
GLfloat dt = (gFramesPerSecond>0 ? 1.0/gFramesPerSecond : 1.0);

//put your specific idle code here
//... this code will run at desiredFPS
gAngle += dt*360./8.; //rotate 360 degrees every 8 seconds
//end your specific idle code here

FPS(); //only call once per frame loop to measure FPS
glutPostRedisplay(); // initiate display() call at desiredFPS rate
}

void display() {
// Will be called at FPS rate, ok to use global values now to rener scene
glClear(GL_COLOR_BUFFER_BIT);

//quick and dirty way to display FPS value
printf("FPS %d\r",gFramesPerSecond); fflush(stdout);

glColor3f(1.0,1.0,1.0);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(gAngle,0.,0.,1.);
glScalef(0.25,0.25,0.25);

glBegin(GL_LINE_STRIP); {
glVertex3f(-1.0, -1.0, 0.0);
glVertex3f( 1.0, -1.0, 0.0);
glVertex3f( 1.0, 1.0, 0.0);
glVertex3f(-1.0, 1.0, 0.0);
glVertex3f(-1.0, -1.0, 0.0);
}
glEnd();

glutSwapBuffers();
}

void init() {
glClearColor(0.0, 0.0, 0.0, 0.0);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1.0,1.0,-1.0,1.0,-1.0,1.0);
}

void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 27: // escape key
exit(0);
break;
default:
break;
}
}

int main(int argc, char** argv) {
glutInit(&amp;argc, argv);
glutInitDisplayMode(GLUT_DOUBLE);
glutCreateWindow("FPS test /w glutTimerFunc");

glutTimerFunc(0,timer,0);
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);

init();

glutMainLoop();
return 0;
}