C++ 为什么简单的glfw程序会在progam空闲时吃掉所有可用的cpu(根据process explorer)?

C++ 为什么简单的glfw程序会在progam空闲时吃掉所有可用的cpu(根据process explorer)?,c++,glfw,C++,Glfw,我有一个使用GLFW的非常简单的游戏循环,如下所示(WindowsX64发布模式) 我希望程序执行得非常快,但是下面计算的delta似乎总是16.667ms,这似乎是glfw在某种程度上限制了我主循环的速度。这不是一个问题,因为我不在乎得到超过60赫兹。然而,process explorer和windows任务管理器报告说,我的程序正在使用大部分cpu核心 具体来说,eat似乎glfwSwapBuffers()消耗了大量cpu,尽管我什么也没画。删除该调用会将cpu使用率降低到0.5% 顺便说一

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

我希望程序执行得非常快,但是下面计算的delta似乎总是16.667ms,这似乎是glfw在某种程度上限制了我主循环的速度。这不是一个问题,因为我不在乎得到超过60赫兹。然而,process explorer和windows任务管理器报告说,我的程序正在使用大部分cpu核心

具体来说,eat似乎glfwSwapBuffers()消耗了大量cpu,尽管我什么也没画。删除该调用会将cpu使用率降低到0.5%

顺便说一句,我的Sleep函数几乎从未被调用,因为delta总是接近16.6ms

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);
        }
    }
}

glfwSwapBuffers
正在等待监视器vsync。这就是为什么循环以60 Hz的频率运行(这是监视器刷新率的频率)。至于高CPU,操作系统可能不会让进程进入睡眠状态。这可能会导致它错过vsync,因为它无法足够快地唤醒。相反,CPU被置于一个繁忙的循环中,直到vsync停止。下面是问题的一个例子。

您似乎需要根据交换缓冲区返回的时间同步线程。进行两次“虚拟”交换缓冲区调用(带有启动屏幕),每次调用后读取计时器以获得频率(某些监视器上可能为120 hz,或者如果是旧的CRT监视器,则为60hz、75hz、85hz、100hz、120hz、160hz、200hz),并设置初始计时器计数

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

许多游戏使用单独的物理线程,该线程以与视频无关的固定频率运行。这是一个没有随时间漂移的示例(增量基于高频时钟的原始读数)。它将位于与图形线程不同的线程中,并在帧更新准备就绪时向图形线程发送信号(互斥、信号量、某种类型的消息传递功能)

/*线程以固定频率运行的代码*/
typedef无符号长UI64;/*无符号64位整数*/
#定义频率400/*频率*/
大整数liPerfTemp;/*用于查询*/
UI64 uFreq=频率;/*过程频率*/
UI64 uOrig;/*原始勾号*/
UI64 uWait;/*滴答率/频率*/
UI64 uRem=0;/*勾选率%freq*/
UI64 uPrev;/*基于原始勾号的上一个勾号*/
UI64 uDelta;/*当前勾号-上一个*/
UI64 u2ms;/*2毫秒的蜱虫*/
UI64Ⅰ;
/* ... */ /* 等待某个事件启动线程*/
timeBeginPeriod(1);/*将周期设置为1ms*/
睡眠(128);/*等它稳定下来*/
u2ms=((UI64)(liperfreq.QuadPart)+499/(UI64)500);
QueryPerformanceCounter((PLARGE_整数)和liPerfTemp);
uOrig=uPrev=LIPERFTEM.QuadPart;
对于(i=0;i<(uFreq*30);i++){
/*基于uRem更新uWait和uRem*/
uWait=((UI64)(liperfreq.QuadPart)+uRem)/uFreq;
uRem=((UI64)(Liperfreq.QuadPart)+uRem)%uFreq;
/*等待uWait滴答声*/
而(1){
QueryPerformanceCounter((PLARGE_整数)和liPerfTemp);
uDelta=(UI64)(LIPERFTEM.QuadPart-uPrev);
如果(uDelta>=uWait)
打破
如果((uWait-uDelta)>u2ms)
睡眠(1);
}
如果(uDelta>=(uWait*2))
dLateStep+=1;
uPrev+=uWait;
/*固定频率代码在这里*/
/*以及完成时的某种类型的中断*/
}
timeEndPeriod(1);/*恢复期*/

您可能应该添加
Windows
标签,因为您的问题是针对Windows的。啊,很有趣。这是有道理的。发布后我才发现glfwSwapBuffers似乎被阻塞,设置glfwSwapInterval(0);停止此操作,然后显著降低windows报告的cpu使用率。我想禁用此功能并使用睡眠的缺点将是流泪。
/* 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 */