为什么简单的glfw程序会占用所有可用的cpu,即使程序处于空闲状态(根据进程资源管理器)?

时间:2014-09-19 16:53:40

标签: c++ glfw

我有一个非常简单的游戏循环使用GLFW如下(windows x64发布模式)

我希望程序执行得非常快,但是看起来下面计算的delta总是16.667ms,这似乎是glfw以某种方式限制了我的主循环的速度。这不是问题,因为我不关心超过60hz。但是,进程资源管理器和Windows任务管理器报告我的程序正在使用大部分cpu核心。

具体吃似乎glfwSwapBuffers()吃了很多cpu,即使我什么也没画。删除该调用会将cpu使用率降低到0.5%。

顺便说一句,我的睡眠功能几乎从未被调用,因为增量总是接近16.6毫秒。

main ()
{
    double prevTime = glfwGetTime();
    //init glfw ..
    while(!glfwWindowShouldClose(window)) 
    {
        double time0=glfwGetTime();
        double delta = time0- prevTime;

        if (delta >= g_FrameInterval)
        {
            glfwPollEvents();
            prevTime= time0;
            glfwSwapBuffers(window);
        }
        else 
        {
            Sleep(10);
        }
    }
}

2 个答案:

答案 0 :(得分:4)

glfwSwapBuffers正在等待监视器vsync。这就是您的循环以60 Hz(即监视器刷新率的频率)运行的原因。至于高CPU,操作系统可能不会让您的进程进入睡眠状态。这可能会导致错过vsync,因为它无法快速唤醒。而是将CPU置于忙循环中直到vsync。这是问题的more full explanation

答案 1 :(得分:3)

似乎您需要根据交换缓冲区何时返回来同步您的线程。做一些“虚拟”交换缓冲区调用(带有启动屏幕),每次调用后读取一个定时器以获得频率(在某些显示器上可能是120赫兹,或者如果旧的CRT显示器,60赫兹,75赫兹, 85hz,100hz,120hz,160hz,200hz)并设置初始计时器计数。

如果只是以监视器速率运行,那么您可以使用固定的Sleep()值,假设您的代码有一些最大开销(取决于最慢的目标系统)。 Windows的默认滴答速率为64hz(15.625 ms),但可以使用timeBeginPeriod(1)加速,在这种情况下,Sleep(n)在Windows 7或更高版本上大约需要n ms,但最多可达n + 1 ms Windows XP。例如,如果你的代码每帧的cpu时间少于5毫秒,那么在60hz你可以在每次交换缓冲区调用之后使用固定的Sleep(10)(或Sleep(9),如果是Windows XP),或者如果在120hz,然后睡眠(2)(或睡眠(1),如果是Windows XP)。

许多游戏使用单独的线程来运行与视频无关的固定频率的物理。这是一个这样的例子,没有任何漂移随着时间的推移(delta是基于高频时钟的原始读数)。它将与图形线程位于一个单独的线程中,并在帧更新准备就绪时发出图形线程信号(互斥,信号量,某种类型的消息传递函数)。

/* code for a thread to run at fixed frequency */
typedef unsigned long long UI64;        /* unsigned 64 bit int */
#define FREQ    400                     /* frequency */

LARGE_INTEGER liPerfTemp;               /* used for query */
UI64 uFreq = FREQ;                      /* process frequency */
UI64 uOrig;                             /* original tick */
UI64 uWait;                             /* tick rate / freq */
UI64 uRem = 0;                          /* tick rate % freq */
UI64 uPrev;                             /* previous tick based on original tick */
UI64 uDelta;                            /* current tick - previous */
UI64 u2ms;                              /* 2ms of ticks */
UI64 i;

    /* ... */ /* wait for some event to start thread */
    timeBeginPeriod(1);                 /* set period to 1ms */
    Sleep(128);                         /* wait for it to stabilize */

    u2ms = ((UI64)(liPerfFreq.QuadPart)+499) / ((UI64)500);

    QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
    uOrig = uPrev = liPerfTemp.QuadPart;

    for(i = 0; i < (uFreq*30); i++){
        /* update uWait and uRem based on uRem */
        uWait = ((UI64)(liPerfFreq.QuadPart) + uRem) / uFreq;
        uRem  = ((UI64)(liPerfFreq.QuadPart) + uRem) % uFreq;
        /* wait for uWait ticks */
        while(1){
            QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
            uDelta = (UI64)(liPerfTemp.QuadPart - uPrev);
            if(uDelta >= uWait)
                break;
            if((uWait - uDelta) > u2ms)
                Sleep(1);
        }
        if(uDelta >= (uWait*2))
            dwLateStep += 1;
        uPrev += uWait;
        /* fixed frequency code goes here */
        /*  along with some type of break when done */
    }

    timeEndPeriod(1);                   /* restore period */