我有一个非常简单的游戏循环使用GLFW如下(windows x64发布模式)
我希望程序执行得非常快,但是看起来下面计算的delta总是16.667ms,这似乎是glfw以某种方式限制了我的主循环的速度。这不是问题,因为我不关心超过60hz。但是,进程资源管理器和Windows任务管理器报告我的程序正在使用大部分cpu核心。
具体吃似乎glfwSwapBuffers()吃了很多cpu,即使我什么也没画。删除该调用会将cpu使用率降低到0.5%。
顺便说一句,我的睡眠功能几乎从未被调用,因为增量总是接近16.6毫秒。
main ()
{
double prevTime = glfwGetTime();
//init glfw ..
while(!glfwWindowShouldClose(window))
{
double time0=glfwGetTime();
double delta = time0- prevTime;
if (delta >= g_FrameInterval)
{
glfwPollEvents();
prevTime= time0;
glfwSwapBuffers(window);
}
else
{
Sleep(10);
}
}
}
答案 0 :(得分:4)
glfwSwapBuffers
正在等待监视器vsync。这就是您的循环以60 Hz(即监视器刷新率的频率)运行的原因。至于高CPU,操作系统可能不会让您的进程进入睡眠状态。这可能会导致错过vsync,因为它无法快速唤醒。而是将CPU置于忙循环中直到vsync。这是问题的more full explanation。
答案 1 :(得分:3)
似乎您需要根据交换缓冲区何时返回来同步您的线程。做一些“虚拟”交换缓冲区调用(带有启动屏幕),每次调用后读取一个定时器以获得频率(在某些显示器上可能是120赫兹,或者如果旧的CRT显示器,60赫兹,75赫兹, 85hz,100hz,120hz,160hz,200hz)并设置初始计时器计数。
如果只是以监视器速率运行,那么您可以使用固定的Sleep()值,假设您的代码有一些最大开销(取决于最慢的目标系统)。 Windows的默认滴答速率为64hz(15.625 ms),但可以使用timeBeginPeriod(1)加速,在这种情况下,Sleep(n)在Windows 7或更高版本上大约需要n ms,但最多可达n + 1 ms Windows XP。例如,如果你的代码每帧的cpu时间少于5毫秒,那么在60hz你可以在每次交换缓冲区调用之后使用固定的Sleep(10)(或Sleep(9),如果是Windows XP),或者如果在120hz,然后睡眠(2)(或睡眠(1),如果是Windows XP)。
许多游戏使用单独的线程来运行与视频无关的固定频率的物理。这是一个这样的例子,没有任何漂移随着时间的推移(delta是基于高频时钟的原始读数)。它将与图形线程位于一个单独的线程中,并在帧更新准备就绪时发出图形线程信号(互斥,信号量,某种类型的消息传递函数)。
/* code for a thread to run at fixed frequency */
typedef unsigned long long UI64; /* unsigned 64 bit int */
#define FREQ 400 /* frequency */
LARGE_INTEGER liPerfTemp; /* used for query */
UI64 uFreq = FREQ; /* process frequency */
UI64 uOrig; /* original tick */
UI64 uWait; /* tick rate / freq */
UI64 uRem = 0; /* tick rate % freq */
UI64 uPrev; /* previous tick based on original tick */
UI64 uDelta; /* current tick - previous */
UI64 u2ms; /* 2ms of ticks */
UI64 i;
/* ... */ /* wait for some event to start thread */
timeBeginPeriod(1); /* set period to 1ms */
Sleep(128); /* wait for it to stabilize */
u2ms = ((UI64)(liPerfFreq.QuadPart)+499) / ((UI64)500);
QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
uOrig = uPrev = liPerfTemp.QuadPart;
for(i = 0; i < (uFreq*30); i++){
/* update uWait and uRem based on uRem */
uWait = ((UI64)(liPerfFreq.QuadPart) + uRem) / uFreq;
uRem = ((UI64)(liPerfFreq.QuadPart) + uRem) % uFreq;
/* wait for uWait ticks */
while(1){
QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
uDelta = (UI64)(liPerfTemp.QuadPart - uPrev);
if(uDelta >= uWait)
break;
if((uWait - uDelta) > u2ms)
Sleep(1);
}
if(uDelta >= (uWait*2))
dwLateStep += 1;
uPrev += uWait;
/* fixed frequency code goes here */
/* along with some type of break when done */
}
timeEndPeriod(1); /* restore period */