使用vsync(OpenGL)时CPU利用率为100%

100% CPU utilization when using vsync (OpenGL)

本文关键字:CPU 利用率 100% vsync OpenGL 使用      更新时间:2023-10-16

这里有一个非常简单的测试程序。当vsync被禁用时,这个程序以100FPS的速度运行,几乎占用了0%的CPU。当我启用vsync时,我可以获得60FPS和25%(在4核系统中为一核的100%)的CPU利用率。这是使用Nvidia GPU。通过在线搜索,我得到了在英伟达控制面板内禁用"多线程优化"的建议。这确实降低了CPU利用率,但仅降低到10%。此外,如果我在SwapBuffers之后删除对睡眠的调用,即使禁用了多线程优化,我也能再次获得25%的利用率。有人能解释一下吗?我做错什么了吗?英伟达的OpenGL实现是否存在无可救药的缺陷?

#include <GLFW/glfw3.h>
#include <thread>
#include <cstdlib>
#include <cstdio>
int main(int argc, char *argv[])
{
if(!glfwInit())
exit(EXIT_FAILURE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Vsync Test", nullptr, nullptr);
if(!window)
{
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwMakeContextCurrent(window);
#ifdef USE_VSYNC
glfwSwapInterval(1);
#else
glfwSwapInterval(0);
#endif
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
double lastTime = glfwGetTime();
double nbFrames = 0;
while(!glfwWindowShouldClose(window))
{
double currentTime = glfwGetTime();
nbFrames++;
if (currentTime - lastTime >= 1.0)
{
char cbuffer[50];
snprintf(cbuffer, sizeof(cbuffer), "OpenGL Vsync Test [%.1f fps, %.3f ms]", nbFrames, 1000.0 / nbFrames);
glfwSetWindowTitle(window, cbuffer);
nbFrames = 0;
lastTime++;
}
glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers(window);
glfwPollEvents();
//limit to 100FPS for when vsync is disabled
std::chrono::milliseconds dura(10);
std::this_thread::sleep_for(dura);
}
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}

我不太愿意给出这个答案,因为我真的不知道"答案",但希望我能为它提供一些线索。

我也有一个nVidia GPU,我也注意到了同样的事情。我的猜测是,驱动程序本质上是旋转等待:

while(NotTimeToSwapYet()){}

(或者不管它的高级驱动程序版本是什么样子)。

使用进程黑客从nvoglv32.dll的线程中采样一些堆栈跟踪,大约99%的时间处于列表顶部的是

KeAcquireSpinLockAtDpcLevel()

它通常在之类的东西的下游

KiCheckForKernelApcDelivery()EngUnlockDirectDrawSurface()

我对Windows驱动程序编程还不够精通,不能称之为结论性的,但它也不能告诉我我错了。

看起来你也没有做错什么。根据我的经验,在非独占的Windows应用程序中交换时间真的很痛苦:涉及到很多尝试和错误,不同系统之间也有很多可变性。据我所知,没有一种"正确"的方法可以一直有效(请有人告诉我我错了!)。

在过去,我一直能够依靠vsync来保持低CPU使用率(即使它确实降低了响应速度),但现在似乎不再是这样了。我最近才从DirectX切换到OpenGL,所以我无法告诉你这是nVidia驱动程序最近的变化,还是他们只是在vsync方面区别对待DX和OpenGL。

交换缓冲区后,调用DwmFlush();,它将不再使用100%的cpu!