Limiting fps in glut

5.3k views Asked by At

When I try to limit fps using _ftime the framerate delay (aka. sleeping time) increases.

After a minute the frames drop to almost zero.

Is there a solution for this problem?

I'm trying to make a delay in this style:

void init(int unused){

    Initilize();
    glutDisplayFunc (render);
    glutReshapeFunc (reshape);
    glutKeyboardFunc (keyboard);
    glutMouseFunc (mouse);
    glutIdleFunc (timer);
    glutMainLoop();
    timer(); //Get into the loop
}

void timer(void)
{
    _ftime(&lasttime); //get the time
    glutPostRedisplay(); //Redraw all the elements and check for input
    long timetowait;
    _ftime(&curtime); //get the after time
    timetowait = ( (int) 1000/60 - ((1000 * (curtime.time - lasttime.time)) + curtime.millitm - lasttime.millitm)); //caculate the time to wait
    timetowait = max(timetowait, 0); //Don't want to have a negative sleep ;)
    glutTimerFunc(timetowait , timer, 0); //And now sleep
}

If this is a compatibility issue here are the specs:

Windows 7 x86 on a x64 bit laptop (don't blame me about this)

and using Dev C++ with glut extension (or in other words .DevPak file)

The whole source code:

#include <GL/glut.h>
#include <GL/glu.h>
#include <stdio.h>
#include <string.h>

#ifdef __unix__
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#define OS_Windows 0
#elif defined(_WIN32) || defined(WIN32) 
#include <io.h>
#include <windows.h>
#include <sys/timeb.h>
#include <sys/types.h>
#include <time.h>
#define OS_Windows 1
#endif




//Default variables

char defaultcontrolconfig[5] = { 0x18, 0x19, 0x1A, 0x1B, 0x20 }; //UP,DOWN,LEFT,RIGHT,SPACE(JUMP)
unsigned short defaultx = 500,defaulty = 500,defaultmode = 0; //X RES,Y RES, FULLSCREENMODE (0 NO ,1 YES)

/**
    By default it should be 
    UP - move on negative z axis
    DOWN - move on positive z axis
    LEFT - move left relative to the camera (mostly negative x axis)
    RIGHT - move right relative to the camera (mostly positive x axis)
    SPACE - jump (fxp. decreasing the Z value)
**/

unsigned char controls[5],plsaply;
unsigned short gamescreen; 
FILE *configuration;
unsigned short displayx,displayy,displaymode,loop,clicked;
int winIDMain,frames;
double mainboxx = -1 ,mainboxy;
int timetowait;
#ifdef __unix__
       struct timespec curtime,lasttime;
#endif
#ifdef OS_Windows
       struct _timeb curtime, lasttime;
#endif



void keyboard(unsigned char c, int x, int y) {

    if (gamescreen == 0){
            if (c == 27) {
            exit(0);
            }
    }else if(gamescreen == 1){
        if(c == 27) {
            gamescreen = 0;
        }
    }else if(gamescreen == 2){
        switch(c){
            case 27:
                gamescreen = 1;
                break;
        }
    }
}


void mouse(int button, int state, int x, int y) {
int perpartpixels;
if(clicked == 1){   
    loop = 1;
    clicked = 0;
}
if(loop > 0){ //To pervent gliched clicking
    loop--;
}else{
if(gamescreen == 0) {
    perpartpixels = glutGet(GLUT_WINDOW_HEIGHT) / 40; //Aproxx 5% of the resolution 
    if (button == GLUT_LEFT_BUTTON){
        printf("X:%d Y:%d\n",x,y);
        if((y <= perpartpixels * 4 && y >= perpartpixels * 4 - 15) && (x >= 0 && x <= 81)){
            printf("Play\n");
            clicked = 1;
        }else if((y <= perpartpixels * 5 && y >= perpartpixels * 5 - 15) && (x >= 0 && x <= 72)){
            gamescreen = 1;
            printf("Game Screen changed\n");
            clicked = 1;
        }else if((y <= perpartpixels * 6 && y >= perpartpixels * 6 - 15) && (x >= 0 && x <= 36)){
            exit(0);
        }
    }
}else if(gamescreen == 1){
    perpartpixels = glutGet(GLUT_WINDOW_HEIGHT) / 40; //Aproxx 5% of the resolution 
    if (button == GLUT_LEFT_BUTTON){
        printf("X:%d Y:%d\n",x,y);
        if((y >= perpartpixels * 4 - 15 && y <= perpartpixels * 4) && (x >= 0 && x <= 72)){
            gamescreen = 2;
            printf("Game screen changed\n");
            clicked = 1;
        }else if((y >= perpartpixels * 5 - 15 && y <= perpartpixels * 5) && (x >= 0 && x <= 162)){
            configuration = fopen("configuration/controls.conf", "wb");
            fwrite(defaultcontrolconfig,1,5,configuration);
            fclose(configuration);
            configuration = fopen("configuration/display.conf", "wb");
            fprintf(configuration,"%hd\n%hd\n%hd",defaultx,defaulty,defaultmode);
            fclose(configuration);
            printf("Settings restored to default\n");
            plsaply = 1;
            clicked = 1;
        }else if((y >= perpartpixels * 6 - 15  && y <= perpartpixels * 6) && (x >= 0 && x <= 36)){
            gamescreen = 0;
            printf("Game screen changed\n");
            clicked = 1;
        }
    }
}else if(gamescreen == 2){
    perpartpixels = glutGet(GLUT_WINDOW_HEIGHT) / 40; //Aproxx 5% of the resolution
    if(button == GLUT_LEFT_BUTTON){
        if((y >= perpartpixels * 38 && y <= perpartpixels * 40) && (x >= 0 && x <= 36)){
            gamescreen = 1;
            printf("Game screen changed\n");
            clicked = 1;    
        }
    }
}
}
}

void render(void) {
        glClear(GL_COLOR_BUFFER_BIT);
    glLoadIdentity();
    frames++;
    char str[50];

    if(gamescreen == 0) {
        if(mainboxx >= 2.0){
            mainboxx = -1;
        }else{
            mainboxx+= 0.01;
        }
        if(mainboxx >= -0.4 && mainboxx <= -0.3){
            mainboxy += 0.03;
        }else if(mainboxx >= -0.3 && mainboxx <= -0.2){
            mainboxy += 0.01;
        }else if(mainboxx >= -0.2 && mainboxx <= 0.2){
            mainboxy -= 0.01;
        }else if(mainboxx >= 0.2){
            mainboxy = 0;
        }

        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glLoadIdentity;
        glColor3f(0.1,0.1,0.1);
        glTranslatef(-mainboxx, 0 , -1);
        glBegin(GL_QUADS);
            glVertex3f((mainboxx - 0.05),(mainboxy - 0.05),0);
            glVertex3f((mainboxx - 0.05),(mainboxy + 0.05),0);
            glVertex3f((mainboxx + 0.05),(mainboxy + 0.05),0);
            glVertex3f((mainboxx + 0.05),(mainboxy - 0.05),0);

            glVertex3f( -2, -0.05, 0);
            glVertex3f( -0.2, -0.05, 0);
            glVertex3f( -0.2, -1, 0);
            glVertex3f( -2, -1, 0);

            glVertex3f( 0.2, -0.05, 0);
            glVertex3f( 2.91, -0.05, 0);
            glVertex3f( 2.91, -1, 0);
            glVertex3f( 0.2, -1, 0);
        glEnd();
        glPopMatrix();
        sprintf(str, "%d", frames);
        char mainmenustring[9] = "Main Menu";
        glColor3f(1, 1, 1);
        glRasterPos2f(-1, 0.9);
        int mainmenulen, mainmenui;
        mainmenulen = 9;
        for (mainmenui = 0; mainmenui < mainmenulen; mainmenui++) {
            glutBitmapCharacter(GLUT_BITMAP_9_BY_15, mainmenustring[mainmenui]);
        }
        char startstring[5] = "Start";
        glColor3f(1, 1, 1);
        glRasterPos2f(-1, 0.8);
        int startlen, starti;
        startlen = 5;
        for (starti = 0; starti < startlen; starti++) {
            glutBitmapCharacter(GLUT_BITMAP_9_BY_15, startstring[starti]);
        }
        char settingsstring[8] = "Settings";
        glColor3f(1, 1, 1);
        glRasterPos2f(-1, 0.75);
        int settingslen, settingsi;
        settingslen = 8;
        for (settingsi = 0; settingsi < settingslen; settingsi++) {
            glutBitmapCharacter(GLUT_BITMAP_9_BY_15, settingsstring[settingsi]);
        }
        char exitstring[4] = "Exit";
        glColor3f(1, 1, 1);
        glRasterPos2f(-1, 0.7);
        int exitlen, exiti;
        exitlen = 4;
        for (exiti = 0; exiti < exitlen; exiti++) {
            glutBitmapCharacter(GLUT_BITMAP_9_BY_15, exitstring[exiti]);
        }
        glColor3f(1, 1, 1);
        glRasterPos2f(-1, -1);
        int flen, fi;
        flen = (int)strlen(str);
        for (fi = 0; fi < flen; fi++) {
            glutBitmapCharacter(GLUT_BITMAP_9_BY_15, str[fi]);
        }
    }else if (gamescreen == 1) {
        if(plsaply){
            sprintf(str, "%d Please restart to apply changes", frames);
        }else{
            sprintf(str, "%d", frames);
        }
        char settingstring[8] = "Settings";
        glColor3f(1, 1, 1);
        glRasterPos2f(-1, 0.9);
        int settinglen, settingi;
        settinglen = 8;
        for (settingi = 0; settingi < settinglen; settingi++) {
            glutBitmapCharacter(GLUT_BITMAP_9_BY_15, settingstring[settingi]);
        }
        char constring[8] = "Controls";
        glColor3f(1, 1, 1);
        glRasterPos2f(-1, 0.8);
        int conlen, coni;
        conlen = 8;
        for (coni = 0; coni < conlen; coni++) {
            glutBitmapCharacter(GLUT_BITMAP_9_BY_15, constring[coni]);
        }
        char rtoodstring[18] = "Restore to Default";
        glColor3f(1, 1, 1);
        glRasterPos2f(-1, 0.75);
        int rtoodlen, rtoodi;
        rtoodlen = 18;
        for (rtoodi = 0; rtoodi < rtoodlen; rtoodi++) {
            glutBitmapCharacter(GLUT_BITMAP_9_BY_15, rtoodstring[rtoodi]);
        }
        char bckstring[4] = "Back";
        glColor3f(1, 1, 1);
        glRasterPos2f(-1, 0.7);
        int bcklen, bcki;
        bcklen = 4;
        for (bcki = 0; bcki < bcklen; bcki++) {
            glutBitmapCharacter(GLUT_BITMAP_9_BY_15, bckstring[bcki]);
        }
        glColor3f(1, 1, 1);
        glRasterPos2f(-1, -1);
        int flen, fi;
        flen = (int)strlen(str);
        for (fi = 0; fi < flen; fi++) {
            glutBitmapCharacter(GLUT_BITMAP_9_BY_15, str[fi]);
        }
    }else if (gamescreen == 2){
        char bckbtnstring[4] = "Back";
        glColor3f(1, 1, 1);
        glRasterPos2f(-1, -0.9);
        int bckbtnlen, bckbtni;
        bckbtnlen = 4;
        for (bckbtni = 0; bckbtni < bckbtnlen; bckbtni++) {
            glutBitmapCharacter(GLUT_BITMAP_9_BY_15, bckbtnstring[bckbtni]);
        }

    }
    glutSwapBuffers();
}

void reshape (int w, int h)
{
    if(displaymode == 1){
        glutFullScreen();
        displayx = w;
        displayy = h;
    }
    glViewport (0, 0, w, h);
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
}

void timer(void);
void init(int unused);
void Initilize();

void init(int unused){

    Initilize();
    glutDisplayFunc (render);
    glutReshapeFunc (reshape);
    glutKeyboardFunc (keyboard);
    glutMouseFunc (mouse);
    glutIdleFunc (timer);
    glutMainLoop();
    timer();
}

void timer(void)
{
    _ftime(&lasttime);
    glutPostRedisplay();
    long timetowait;
    _ftime(&curtime);
    timetowait = ( (int) 1000/60 - ((1000 * (curtime.time - lasttime.time)) + curtime.millitm - lasttime.millitm));
    timetowait = max(timetowait, 0);
    glutTimerFunc(timetowait , timer, 0);
}

// Load config functions
void loadconfiguration(void) {
//Check if config folder is present otherwise create it 
    #ifdef __unix__
    int result = mkdir("configuration", 0777);
    #endif
    #ifdef OS_Windows
    int result = _mkdir("configuration");
    #endif
    if(result == -1){
        printf("Ignore creating folder:\nError -1 Directory already exists\n");
    }else if(result != 0){
        printf("Error: %d while creating configuration folder\n", result);
    }
    //Check if control configuration is present otherwise create it
    #ifdef __unix__
    if (access("configuration/controls.conf",F_OK)!= -1)
    {
        printf ("Found controls configuration file\n");
        configuration = fopen("configuration/controls.conf", "rb");
    }
    else
    {
        configuration = fopen("configuration/controls.conf", "wb");
        fwrite(defaultcontrolconfig,1,5,configuration);
    }
    #endif
    #ifdef OS_Windows
    if (INVALID_FILE_ATTRIBUTES == GetFileAttributes("configuration/controls.conf") && GetLastError()==ERROR_FILE_NOT_FOUND)
    {
        configuration = fopen("configuration/controls.conf", "wb");
        fwrite(defaultcontrolconfig,1,5,configuration);
    }
    else
    {
        printf ("Found controls configuration file\n");
        configuration = fopen("configuration/controls.conf", "rb");
    }
    #endif
    fread(controls,1,5,configuration);
    printf("Finished loading controls configuration\n");
    fclose(configuration);

    //Check if display configuration is present otherwise create it
    #ifdef __unix__
    if (access("configuration/display.conf",F_OK)!= -1)
    {
        printf ("Found display configuration file\n");
        configuration = fopen("configuration/display.conf", "rb");
    }
    else
    {
        configuration = fopen("configuration/display.conf", "wb");
        fprintf(configuration,"%hd\n%hd\n%hd",defaultx,defaulty,defaultmode);
    }
    #endif
    #ifdef OS_Windows
    if (INVALID_FILE_ATTRIBUTES == GetFileAttributes("configuration/display.conf") && GetLastError()==ERROR_FILE_NOT_FOUND)
    {
        configuration = fopen("configuration/display.conf", "wb");
        fprintf(configuration,"%hd\n%hd\n%hd",defaultx,defaulty,defaultmode);
    }
    else
    {
        printf ("Found display configuration file\n");
        configuration = fopen("configuration/display.conf", "rb");
    }
    #endif
    rewind(configuration);
    fscanf(configuration,"%hd\n%hd\n%hd",&displayx,&displayy,&displaymode);
    printf("Finished loading display configuration\n");
    fclose(configuration);


    printf("Finished loading configurations\n");
}

void Initilize() {
    glClearColor(0, 0, 0, 0.1);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
}

int main() {
    loadconfiguration();
    char *myargv [1];
    int myargc=1;
    myargv [0]=strdup ("./file");
    glutInit(&myargc, myargv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
        glutInitWindowPosition(100, 100);
        glutInitWindowSize(displayx, displayy);
    printf("Making a window\n");
    winIDMain = glutCreateWindow("GL Game");
    init(0);
}

Also I'am trying to get it as much as possible to be cross platform. Is it because of my code or the glut engine itself.

1

There are 1 answers

1
Guillaume Gris On BEST ANSWER

From glut documentation :

glutPostRedisplay marks the current window as needing to be redisplayed.

So this method does not performs the display but tells glut to do it. This means that this method should be quite fast. So timetowait is about the length of a frame.

Then, from glut documentation :

glutTimerFunc registers a timer callback to be triggered in a specified number of milliseconds.

and

Multiple timer callbacks at same or differing times may be registered simultaneously.

So I think you end up registering a lot of timer() calls that actually prevent GLUT from doing any display. Each time glut calls the idle Func, it creates a new chain of recursive timer() calls.


To solve your problem :

Call timer() only once when initializing. And change your function to

void timer(int) {
    glutPostRedisplay();
    glutTimerFunc(1000/60, timer, 0);
}

This should ask for a refresh every 60th of a seccond.