PDA

View Full Version : I dont understand what glutPostRedisplay() does



Bluebomber357
09-26-2009, 11:23 AM
I look at the man pages, and it doesn't say why we need it. It is just too cryptic, can anybody elaborate for me? Possibly give an example of what could wrong if it is not used when needed?

marshats
09-26-2009, 05:29 PM
lets say you have an animation by calling



glutDisplayFunc(display); // opengl drawing goes here
glutTimerFunc(30, drive, -1); // physics, movement equations here
glutMainLoop();


At the end of your physics update function you call glutPostRedisplay()


void drive (int data) {
glutTimerFunc(30, drive, -1); // call drive() again in 30 milliseconds
// callback function moves the car.
.... move x, y, z, etc
glutPostRedisplay();
}


glutPostRedisplay() essentially sets a flag so that on the next iteration of the mainloop, your registered display() function is called. If you don't tell the mainloop it needs to draw the next frame with a glutPostRedisplay (ie imagine commenting out glutPostRedisplay in drive(data) above) then your animation will look as if it is stuck ie no animation even though the x,y,z position of the "car" will be changing!

with glutpostredisplay your mainloop will unroll something like:



drive (-1);
display();
drive (-1);
display();
drive (-1);
display();
...


but without it the main loop will unroll something like:


drive (-1);
drive (-1);
drive (-1);
...

Bluebomber357
09-30-2009, 06:54 PM
Do I have to use glutPostRedisplay() for animation, I just made this: http://bluebomber128.wordpress.com/2009/09/26/opengl-pong-project/

It is an openGL simple pong game, and the animation is perfectly smooth and I don't ever call glutPostRedisplay(). I am not sure if I am doing something to compensate for it, but I can't think of anything. It just seems like my display function is called by GLUT for every frame displayed by my monitor. Anybody have any ideas why I am getting away with not using glutPostRedisplay()?

marshats
09-30-2009, 08:13 PM
It is common practice to put only gl drawing commands in the "display" callback and separately put the physics/cpu position/rotation/etc updates in the separate "idle" or "timer" function. This then necesitates the use of glutPostRedisplay(). This has some benefits like giving your "keyboard" callback a chance to respond if you are fully utilizing the CPU and the GPU. I think of it as "load balancing" optimization that helps performance and responsiveness of the application. As you begin to work more with openGL you will see programming patterns where it naturally lends itself into a "physics" part and "drawing" part. The separation naturally follows this pattern and makes it easier to deal with large complicated moving scenes with thousands of elements.

Nothing says you have to do this separation -- Hence, you do not _have_ to use glutPostRedisplay() for animation. There is nothing inherently wrong with this approach of mixing your physics and drawing in one callback. However, I would recommend against using it to help your self out later on.

I looked at your link and didn't see any source code showing what you are doing. I suspect you are putting all your drawing commands in the same callback ( "idle" or "timer") as where you update either your objects positions.

Bluebomber357
09-30-2009, 08:35 PM
Yeah I used the same function for idle and display.

I believe I've seen glutPostRedisplay for the glut reshape function, does that make sense?

Isn't it not a good idea to use the glut timer function because you can't guarantee that it will call the callback function consistently at the right time?

marshats
09-30-2009, 08:56 PM
>Yeah I used the same function for idle and display.

That's ok. You may want to try going with the approach of splitting the display from the physics though if you find your code getting overly complicated or things like the keyboard becoming non-responsive.

> I believe I've seen glutPostRedisplay for the glut reshape function, does that make sense?

You can call glutPostRedisplay() as many times as you like, it just sets a flag so that when the mainloop is ready it will call the real "display" callback once per mainloop iteration. It doesn't realy make sense to put a glutPostRedisplay in reshape because display will be called automatically when the window is reshaped anyhow.

>Isn't it not a good idea to use the glut timer function because you can't guarantee that it will call the callback function consistently at the right time?

Well if you are right on the edge of having enough time to draw your scene + do the physics then the timer function will probably be inconsistent like you say. But with today's hardware it is amazing how much you can draw and compute. I like the timer because it sleeps during the dead time when you are not drawing + computing positions. I have had very complicated scenes with 100's of objects moving using the timer approach -- while watching the CPU usage at <10%!!! Also, as the GPU's get faster the code still runs at the same speed -- it no objects moving at different speeds on differernt GPUs. For these reasons, I usually start with using the simpler timer callback approach and if I find my scene/physics starts to get complicated and time-consuming enough I go back to the idle callback+measuring time approach for more precise control.

Attached are two "Helloworld codes" that I use as starting points whenever I start a new glut project. The first using the timer approach and the second using the idle approach.



//this shows one way to get FPS using an inprecise clock
//or to throttle the drawing rate to a fixed FPS with glutTimerFunc
//g++ glut_fps_demo_alternative.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;
}




//this shows one way to get FPS using an inprecise clock
//see idle() and change
// 1. to go as fast as hardware: #undef REGULATE_FPS
// 2. to throttle to 1/35ms=29FPS: #define REGULATE_FPS
#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

printf(".");
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;
}

Bluebomber357
10-01-2009, 07:34 AM
Thanks for the example code. I still have questions though. Since my pong project didn't use a timer, does that mean that my display was just getting called as fast as my computer could handle? If I disable my Vsync will it run way too fast or something?

Doesn't using a timer cap the frame-rate? If I play Quake 3, I can run it as 300fps, how do they get the game to play at the same rate? If I had an old computer and it ran at 20fps, do they skip draws but still run the same amount of physics calls?

ZbuffeR
10-01-2009, 08:04 AM
Modern games often run the game and physics engine at a fixed rate (such as 100Hz), separately from rendering engine. This is the only way to provide reliable behaviour between heterogenous computers. Then for display, interpolate between 2 snapshots of the game simulation.
BTW original Quake 1 to Quake 3 do have slight different behaviour depending on framerate, contrary to Quake Live.

marshats
10-01-2009, 10:25 AM
> Since my pong project didn't use a timer, does that mean that my display was just getting called as fast as my computer could handle? If I disable my Vsync will it run way too fast or something?

Yes, your display was being limited by VSYNC (60FPS most likely due to most monitors having a 60Hz update rate). If you disable vsync I would expect your code to run too fast to be playable. In fact the example code #2 above was my attempt at measuring the effects of VSYNC. When I turned of regulation with "#undef REGULATE_FPS", I would measure 1000FPS when VSYNC was set to off in my driver but 60FPS when VSYNC was enable with my driver.

>Doesn't using a timer cap the frame-rate?

Yes, to the precision of the timer itself. I have found that it is perfectly good for 60Hz to match my monitor. This is true if my draw+physics can all be done in less than 1/60Hz=16ms with time to spare. This is not always the case especially in modern games. ZbuffeR is alluding to this case where you need to do more physics calls than display calls. In fact that is the best way to do things if you are going for the optimal performance with very complicated physics and drawing.

When you say you run Quake at 300FPS, that doesn't mean you are drawing the scene 300 times per second to the screen -- the monitor can only draw at 60 frames per second but the physics is being updated at 300FPS. In fact it doesn't make sense to send your scene for display any faster than 60 times per second (or whatever your monitor rate is).

However, physics engines that compute positions and forces etc, have a numerical stability issue. They can't take arbitrarily large time differences and remain stable. To be stable they like to run with small time steps -- usually the smaller the better. Hence, the separation of physics from display. For instance update physics more often at 120Hz,8ms and update display at 60Hz,16ms.

But in your particular pong game -- since you are VSYNC limited you are probably computing opneGL scene and position updates much faster than 16ms. So you can easily get away with using the timer callback to keep the speed of play independent of GPU/CPU speed with an update of 1000/60FPS=16ms as



const int desiredFPS=60;
glutTimerFunc(1000/desiredFPS, timer, ++value);


But again, if you are trying to get setup for future, you may just want to learn how to separate the physics from the display, measure time accurately, and based on measured time call glutPostRedisplay at a sub-rate of your physics update rate.

Bluebomber357
10-01-2009, 05:52 PM
Thank you guys for all the answers. Just one question left, if I have my Vsync off, then display is called as fast as possible by the CPU right? So how would a timer that dictates that glutPostRedisplay() be called every 16ms control the amount of display calls? Wouldn't it just be called every 16ms along with the constant calls that would happen with Vsync off.

I am sorry if I am bit dense by asking this quesiton, I am now confused as to why my display function gets called without using glutPostRedisplay() at all...

marshats
10-01-2009, 06:38 PM
> if I have my Vsync off, then display is called as fast as possible by the CPU right?

Not quite, your "display" callback is NOT called as fast as possible by your CPU just because VSYNC is off. When the "display" is called is still determined by your program. If you put your drawing in "idle" callback (not recommended) then it will draw as fast as possible since idle is called every iteration of the mainloop. But if you use instead of the "idle" callback, the timer with 16ms then timer will be called every 16ms and if at the end of it you call glutPostRedisplay() you will effectively tell the mainloop to call "display" ASAP before starting next loop iteration ie every 16ms. If you don't call glutPostRedisplay() then you will not see anything on the screen animating!

All this assumes that you have separated the physics from the display;
glutTimerFunc(0,physics,0); //put non-opengl calls in physics()
glutDisplayFunc(display); // put opengl calls in display()
and have not defined a glutIdleFunc(idle); -- ie don't define an idle callback when using timer callback, thats confusing

Bluebomber357
10-02-2009, 09:05 AM
bump

ZbuffeR
10-02-2009, 12:17 PM
bump ? what is your question again ? I though marshats already answered nicely.

Bluebomber357
10-02-2009, 12:49 PM
wow I missed his post, pardon me, thanks guys for all the answers :D