使用垂直同步(OpenGL)时,CPU利用率达到100%

10

这是一个非常简单的测试程序。当禁用垂直同步时,此程序以100FPS运行,并几乎不使用CPU。当我启用垂直同步时,我获得60FPS和25%(4核系统上1个核心的100%)的CPU利用率。这是使用Nvidia GPU。在网上搜索后,有人建议在Nvidia控制面板内禁用“多线程优化”。这确实降低了CPU利用率,但仅降至10%。此外,如果我在SwapBuffers之后删除对sleep的调用,则即使禁用了多线程优化,CPU利用率也会再次达到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);
}
2个回答

8

虽然我并不确定这是正确的答案,但希望我能对此有所启示。

我也有一张nVidia GPU,并且注意到了同样的问题。我猜测驱动程序本质上是在忙等待:

while(NotTimeToSwapYet()){}

(或者看起来很高级的驱动程序版本)。

使用process hackernvoglv32.dll线程中采样一些堆栈跟踪,99%的情况下,排在列表顶部的是

KeAcquireSpinLockAtDpcLevel()

通常这是从像

KiCheckForKernelApcDelivery()EngUnlockDirectDrawSurface()

这样的地方传递下来的。

我对Windows驱动程序设计并不熟悉,无法做出确切结论,但这当然也没有告诉我我的判断是错误的。

看起来你也没有做任何明显错误的事情。在非专有Windows应用程序中进行交换时序通常会非常痛苦:需要大量的试错和不同系统之间的变化。据我所知,没有一种“正确”的方法可以始终有效(请有人告诉我我错了!)。

过去,我一直依靠垂直同步来保持CPU使用率低(即使它使事情变得有点不太灵敏),但现在似乎不再是这种情况了。我最近从DirectX转到OpenGL,所以我不能告诉你这是nVidia驱动程序的最近变化,还是他们只是在对待DX和OpenGL方面与垂直同步不同。


1
不是非常明确,但绝对有帮助。我找到了这篇文章(http://forum.openscenegraph.org/viewtopic.php?t=3653#18283),其中有人从驱动程序开发人员那里得到了一些反馈。根据他们的回复,驱动程序会让步。为了测试这个问题,我加载了我选择的3D渲染套件,并在运行OpenGL程序时将我的四个CPU核心全部占满。它的CPU利用率从25%降至0%。看起来虽然它占用了一个核心,但它并没有真正地独占它。 - Chris_F
@Chis_F 谢谢,这很好知道(也有些令人鼓舞)。但是我对高CPU使用率的主要担忧在于它会阻止笔记本进入低功耗模式;我不确定“人为”的使用情况是否会有所不同。也许移动驱动程序设置不同--我没有一个来测试。 - Profram Files
那确实是一个问题。就功耗而言,让步并不会有任何影响,但也许他们移动GPU的驱动程序操作方式不同。 - Chris_F
我注意到当Blender处于空闲状态时,即使我强制开启垂直同步,它几乎不使用CPU。据我所知,它使用OpenGL。我想知道他们是如何管理的,做了什么不同的事情。 - Chris_F
Blender似乎只在有变化时才渲染帧。 我用Fraps测试过了。 我尽可能地做类似的事情并解决了问题。在3D编辑器中很好,但在制作游戏或演示时不是一个选项。 我正在使用winpai,如果我使用GetMessage而不是PeekMessage,则应用程序将在每个帧之前等待用户输入。 因此,仅当用户移动鼠标或发生其他事件时才会渲染帧。 - LovelyHanibal
提供所提及链接的WebArchive版本:https://web.archive.org/web/20120710044621/http://forum.openscenegraph.org/viewtopic.php?t=3653#18283 - Perl99

1

交换缓冲区后,调用DwmFlush();,就不会再使用100%的CPU了!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接