我想用OpenGL和GLFW做一些非常简单的事情:我想从左到右滚动一个100x100的白色填充矩形,然后再返回。矩形应该每帧移动1个像素,滚动应该非常平滑。这是我的代码:
int main(void)
{
GLFWwindow* window;
int i = 0, mode = 0;
if(!glfwInit()) return -1;
window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
if(!window) {
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 640, 0, 480, -1, 1);
glDisable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glColor3f(1.0, 1.0, 1.0);
while(!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glRecti(i, 190, i + 100, 290);
glfwSwapBuffers(window);
glfwPollEvents();
if(!mode) {
i++;
if(i >= 539) mode = 1;
} else {
i--;
if(i <= 0) mode = 0;
}
}
glfwTerminate();
return 0;
}
在Mac OS X和Linux上,这段代码工作正常。滚动与垂直刷新完全同步,您看不到任何口吃或闪烁。它非常顺利。
在Windows上,事情变得更加困难。默认情况下,启用桌面合成时,glfwSwapInterval(1)
对Windows没有任何影响。 GLFW文档说这已经完成,因为在启用DWM合成的情况下启用交换间隔会导致严重的抖动。通过在定义GLFW_USE_DWM_SWAP_INTERVAL
的情况下编译GLFW,可以更改此行为。在这种情况下,上面的代码在Windows上也可以正常工作。滚动完全同步,没有抖动。我在运行XP,Vista,7和8的各种不同机器上进行了测试。
但是,必须有一个很好的理由让GLFW作者默认禁用Windows上的交换间隔,所以我认为有很多配置确实会导致严重的抖动。也许我很幸运,我的机器都没有显示出来。所以定义GLFW_USE_DWM_SWAP_INTERVAL
并不是一个我可以忍受的解决方案,因为默认情况下它必须有一个被禁用的原因,尽管GLFW团队没有提出更好的解决方案,因为它有点让我感到厌烦,因为现在看来,由于这个问题,GLFW程序并不是真正可移植的。以上面的代码为例:它将在Linux和OS X上完美同步,但在Windows上它将以闪电般的速度运行。这有点违背了GLFW在我眼中的便携性概念。
鉴于GLFW_USE_DWM_SWAP_INTERVAL
无法在Windows上使用的情况,因为GLFW团队明确警告其使用,我想知道我还应该做些什么。自然的解决方案当然是一个计时器,它可以测量时间并确保glfwSwapBuffers()
不会比监视器的垂直刷新率更频繁地调用。
然而,这也不像听起来那么简单,因为我不能使用Sleep()
,因为这太不精确了。因此,我必须使用QueryPerformanceCounter()
的轮询循环。我尝试了这种方法,它几乎可以工作,但由于轮询循环睡眠,CPU使用率当然高达100%。另一方面,当使用GLFW_USE_DWM_SWAP_INTERVAL
时,CPU使用率仅为1%。
另一种方法是设置一个定时触发的计时器,但AFAIK的精度CreateTimerQueueTimer()
不是很令人满意,并且可能不会产生完全同步的结果。
简而言之:我想问一下处理这个问题的推荐方法是什么?上面的示例代码当然仅用于说明目的。我的一般问题是,我正在寻找一种简洁的方法,使glfwSwapBuffers()
交换缓冲区与Windows上的监视器垂直刷新同步。在Linux和Mac OS X上,这已经运行良好,但在Windows上存在GLFW文档所讨论的严重抖动问题(但我在这里没有看到)。
我仍然有点困惑的是,GLFW没有为这个问题提供内置的解决方案,而且几乎让程序员无法解决这个问题。我仍然是OpenGL的新手,但从我天真的角度来看,我认为有一个功能可以将缓冲区与垂直刷新同步交换,这是一个至关重要的功能,所以它让我无法理解为什么GLFW在Windows上没有它。
所以再一次我的问题是:我如何解决glfwSwapInterval()
在Windows上无法正常工作的问题?解决这个问题的建议方法是什么?有没有比使用会占用CPU的轮询计时器更好的方法?