100% CPU utilization when using vsync (OpenGL)
Asked Answered
M

2

10

Here is a very simple test program. When vsync is disabled this program runs at 100FPS and uses up virtually 0% of the CPU. When I enable vsync, I get 60FPS and 25% (100% of one core on a 4 core system) CPU utilization. This is using a Nvidia GPU. Searching online lead me to the suggestion to disable "multithreaded optimization" inside of Nvidia control panel. This does decrease the CPU utilization, but only to 10%. Furthermore, if I remove the call to sleep after SwapBuffers, I get 25% utilization again even with multithreaded optimization disabled. Can anyone shed some light on this? Am I doing something wrong? Is Nvidia's OpenGL implementation just hopelessly flawed?

#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);
}
Microanalysis answered 21/2, 2014 at 4:30 Comment(0)
C
8

I hesitate to give this as an answer, as I don't really know the "answer," but hopefully I can shed some light towards it.

I have an nVidia GPU as well and I've noticed the same thing. My guess is that the driver is essentially spin-waiting:

while(NotTimeToSwapYet()){}

(or whatever the fancy driver version of that looks like).

Using process hacker to sample some stack traces from nvoglv32.dll's thread, The thing that's at the top of the list about 99% of the time is

KeAcquireSpinLockAtDpcLevel()

which is usually downstream from things like

KiCheckForKernelApcDelivery() and EngUnlockDirectDrawSurface()

I'm not well versed enough in Windows driver programming to call this conclusive, but it certainly doesn't tell me I'm wrong either.

And it doesn't look like you're doing anything obviously wrong either. It's been my experience that swap timing in non-exclusive Windows applications is just really painful: there's a lot of trial and error involved, and a lot of variability between different systems. As far as I know there is no "right" way to do it that will work well all the time (please, someone tell me I'm wrong!).

In the past, I've been able to rely on vsync to keep CPU usage low (even if it did make things a bit less responsive), but that doesn't seem to be the case any longer. I switched from DirectX to OpenGL relatively recently, so I couldn't tell you if this is a recent change in nVidia's driver, or whether they just treat DX and OpenGL differently with respect to vsync.

Coenobite answered 21/2, 2014 at 8:23 Comment(6)
Not quite definitive, but definitely helpful. I did find this post (forum.openscenegraph.org/viewtopic.php?t=3653#18283) where someone got some feedback from the driver developers. According to their response, the driver yields. To test this for myself I loaded up my 3D rendering suite of choice and proceeded to max out all four of my CPU cores while I had the OpenGL program running. Its CPU utilization went from 25% to 0%. It seems while it maxes out a core, it doesn't actually hog it.Microanalysis
@Chis_F Thanks, that's good to know (and somewhat encouraging). But my main concern with the high CPU usage is that it discourages laptops from entering low power mode; I'm not sure if the usage being "artificial" makes a difference there. Maybe mobile drivers are set up differently -- I don't have one to test.Coenobite
That's certainly a concern. Yielding would make no difference as far as power consumption goes, but maybe the drivers for their mobile GPUs operate differently.Microanalysis
I've noticed that Blender uses almost no CPU when idle, even if I force vsync on. As far as I know it uses OpenGL. I wonder what they are doing differently to manage that.Microanalysis
Blender seem to be rendering frames only when something have change. I tested it with Fraps. I do something similar when it's possible and it solve the problem. It's fine in 3d editor, but it's not an option when you are making game or demo. I'm using winpai and if I use GetMessage instead of PeekMessage then application will wait for user input before every frame. So frames are rendered only when user move mose, or something.Epicurean
WebArchive version of the mentioned link: web.archive.org/web/20120710044621/http://…Minervamines
E
2

after swap buffers, call DwmFlush(); and it will no longer use 100% cpu!

Else answered 14/2, 2022 at 9:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.