限制 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 过剩的主要内容,如果未能解决你的问题,请参考以下文章