限制过剩的 fps

Limiting fps in glut

当我尝试使用 _ftime 限制 fps 时,帧速率延迟(又名休眠时间)增加。

一分钟后,帧数几乎降为零。

这个问题有解决办法吗?

我正在尝试以这种方式延迟:

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
}

如果这是兼容性问题,请查看规格:

Windows x64 位笔记本电脑上的 7 x86(不要为此责怪我)

并使用带有过剩扩展名的 Dev C++(或者换句话说,.DevPak 文件)

整个源代码:

#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);
}

我也在努力让它尽可能地跨平台。 是我的代码问题还是引擎过剩问题

来自 glut documentation :

glutPostRedisplay marks the current window as needing to be redisplayed.

所以这个方法不执行显示而是告诉glut来执行。这意味着该方法应该非常快。所以timetowait大约是一帧的长度。

然后,从glut documentation

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

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

所以我认为您最终注册了很多实际上阻止 GLUT 进行任何显示的 timer() 调用。每次 glut 调用空闲 Func 时,它都会创建一个新的递归链 timer() 调用。


解决您的问题:

初始化时只调用一次timer()。并将您的功能更改为

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

这应该要求每 60 秒刷新一次。