限制 fps 过剩

Posted

技术标签:

【中文标题】限制 fps 过剩【英文标题】:Limiting fps in glut 【发布时间】:2015-06-25 18:57:31 【问题描述】:

当我尝试使用 _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

如果这是兼容性问题,请参考以下规格:

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

并使用带有 glut 扩展名的 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);

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

【问题讨论】:

附带说明,我建议使用 GLFW 而不是 glut。它更现代、功能更全面、更易于使用。 【参考方案1】:

来自glut documentation:

glutPostRedisplay 将当前窗口标记为需要重新显示。

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

然后,来自glut documentation:

glutTimerFunc 注册一个定时器回调,在指定的毫秒数内触发。

可以同时注册多个相同或不同时间的计时器回调。

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


解决您的问题:

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

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

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

【讨论】:

那么我怎么能不调用大量的 glutTimerFunc 来做到这一点

以上是关于限制 fps 过剩的主要内容,如果未能解决你的问题,请参考以下文章

Qt QML:限制最大FPS

XNA 4.0 C#限制FPS

如何限制 UVC 小工具的 FPS?

限制 Sprite 套件帧率

在 Three.js 中限制帧速率以提高性能,requestAnimationFrame?

如何为游戏循环制作计时器?