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

Posted

技术标签:

【中文标题】使用 vsync (OpenGL) 时 CPU 利用率为 100%【英文标题】:100% CPU utilization when using vsync (OpenGL) 【发布时间】:2014-03-22 10:18:24 【问题描述】:

这是一个非常简单的测试程序。当 vsync 被禁用时,该程序以 100FPS 运行,几乎占用了 0% 的 CPU。当我启用 vsync 时,我得到 60FPS 和 25%(100% 的一个核心在 4 核系统上)的 CPU 利用率。这是使用 Nvidia GPU。在线搜索导致我建议在 Nvidia 控制面板中禁用“多线程优化”。这确实降低了 CPU 利用率,但仅降低了 10%。此外,如果我在 SwapBuffers 之后删除睡眠调用,即使禁用了多线程优化,我也会再次获得 25% 的利用率。任何人都可以对此有所了解吗?难道我做错了什么? Nvidia 的 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);

【问题讨论】:

【参考方案1】:

我不愿给出这个答案,因为我真的不知道“答案”,但希望我能对此有所了解。

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

while(NotTimeToSwapYet())

(或任何花哨的驱动程序版本)。

使用process hacker从nvoglv32.dll的线程中采样一些堆栈跟踪,大约99%的时间在列表顶部的东西是

KeAcquireSpinLockAtDpcLevel()

通常在

之类的东西的下游

KiCheckForKernelApcDelivery()EngUnlockDirectDrawSurface()

我在 Windows 驱动程序编程方面还不够精通,无法得出结论,但这当然也不能告诉我我错了。

而且看起来你也没有做任何明显错误的事情。根据我的经验,非独占 Windows 应用程序中的交换时间非常痛苦:涉及大量的反复试验,并且不同系统之间存在很多可变性。据我所知,没有一种“正确”的方法可以一直有效(请有人告诉我我错了!)。

过去,我一直能够依靠 vsync 来保持较低的 CPU 使用率(即使它确实使事情的响应速度降低了一些),但现在似乎不再如此了。我最近从 DirectX 切换到了 OpenGL,所以我无法告诉你这是否是 nVidia 驱动程序的最新变化,或者他们是否只是在 vsync 方面对 DX 和 OpenGL 进行了不同的处理。

【讨论】:

不是很明确,但绝对有帮助。我确实找到了这篇文章 (forum.openscenegraph.org/viewtopic.php?t=3653#18283),有人从驱动程序开发人员那里得到了一些反馈。根据他们的反应,司机让步了。为了自己测试这一点,我加载了我选择的 3D 渲染套件,并在运行 OpenGL 程序的同时将我的所有四个 CPU 内核最大化。它的 CPU 利用率从 25% 下降到 0%。似乎虽然它最大化了一个核心,但它并没有实际上占用它。 @Chis_F 谢谢,很高兴知道(而且有点令人鼓舞)。但我对高 CPU 使用率的主要担忧是它会阻止笔记本电脑进入低功耗模式。我不确定“人造”的用法是否会有所不同。也许移动驱动程序的设置有所不同——我没有要测试的。 这当然是一个问题。就功耗而言,产量不会有任何影响,但也许他们的移动 GPU 的驱动程序运行方式不同。 我注意到 Blender 在空闲时几乎不使用 CPU,即使我强制打开 vsync 也是如此。据我所知,它使用 OpenGL。我想知道他们采取了哪些不同的措施来管理这一点。 Blender 似乎只有在某些情况发生变化时才会渲染帧。我用 Fraps 对其进行了测试。我会在可能的情况下做类似的事情并解决问题。在 3d 编辑器中很好,但在制作游戏或演示时不是一个选项。我正在使用 winpai,如果我使用 GetMessage 而不是 PeekMessage,那么应用程序将在每一帧之前等待用户输入。所以只有当用户移动鼠标或其他东西时才会渲染帧。【参考方案2】:

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

【讨论】:

以上是关于使用 vsync (OpenGL) 时 CPU 利用率为 100%的主要内容,如果未能解决你的问题,请参考以下文章

Android:了解 OnDrawFrame、FPS 和 VSync (OpenGL ES 2.0)

Vsync 来龙去脉

Vsync 来龙去脉

Vsync 来龙去脉

Vsync 来龙去脉

防止 OpenGL 缓冲帧