2014-09-25 76 views
0

所以现在在我的OpenGL游戏引擎中,当我的渲染线程几乎没有任何关系时,它占用了我的CPU可以给它的最大值。 Windows任务管理器显示我的应用程序占用了25%的处理量(我有4个硬件线程,因此25%是一个线程可以使用的最大值)。当我根本没有启动渲染线程时,我得到0-2%(这是它自己的担心,因为它所做的全部都是运行SDL输入循环)。为什么我的渲染线程占用100%cpu?

那么,我的渲染线程究竟做了什么?这里有一些代码:

Timer timer; 

while (gVar.running) 
{ 
    timer.frequencyCap(60.0); 

    beginFrame(); 
    drawFrame(); 
    endFrame(); 
} 

让我们来看看其中的每一个。 Timer是我使用SDL_GetPerformanceCounter制作的定制计时器类。 timer.frequencyCap(60.0);旨在确保循环每秒不会运行超过60次。下面的代码为Timer::frequencyCap()

double Timer::frequencyCap(double maxFrequency) 
{ 
    double duration; 

    update(); 
    duration = _deltaTime; 
    if (duration < (1.0/maxFrequency)) 
    { 
     double dur = ((1.0/maxFrequency) - duration) * 1000000.0; 
     this_thread::sleep_for(chrono::microseconds((int64)dur)); 
     update(); 
    } 

    return duration; 
} 

void Timer::update(void) 
{ 
    if (_freq == 0) 
     return; 

    _prevTicks = _currentTicks; 
    _currentTicks = SDL_GetPerformanceCounter(); 

     // Some sanity checking here. // 
     // The only way _currentTicks can be less than _prevTicks is if we've wrapped around to 0. // 
     // So, we need some other way of calculating the difference. 
    if (_currentTicks < _prevTicks) 
    { 
     // If we take difference between UINT64_MAX and _prevTicks, then add that to _currentTicks, we get the proper difference between _currentTicks and _prevTicks. // 
     uint64 dif = UINT64_MAX - _prevTicks; 

     // The +1 here prvents an off-by-1 error. In truth, the error would be pretty much indistinguishable, but we might as well be correct. // 
     _deltaTime = (double)(_currentTicks + dif + 1)/(double)_freq; 
    } 
    else 
     _deltaTime = (double)(_currentTicks - _prevTicks)/(double)_freq; 
} 

接下来的3个功能是相当简单的(在此阶段):

void Renderer::beginFrame() 
{ 
     // Perform a resize if we need to. // 
    if (_needResize) 
    { 
     gWindow.getDrawableSize(&_width, &_height); 
     glViewport(0, 0, _width, _height); 
     _needResize = false; 
    } 

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 
} 

void Renderer::endFrame() 
{ 
    gWindow.swapBuffers(); 
} 

void Renderer::drawFrame() 
{ 
} 

渲染线程使用的std ::线程创建。我能想到的唯一解释是timer.frequencyCap以某种方式不起作用,除非我在主线程中使用完全相同的功能,并且我在0-2%空闲。

我在这里做错了什么?

+3

添加日志记录,直到您明白发生了什么。 – 2014-09-25 06:05:09

+1

强烈建议您废除自己的计时器控制并使用图形驱动程序提供的[交换间隔控制](http://www.opengl.org/wiki/Swap_Interval)。 – starrify 2014-09-25 06:23:31

+0

任何sleep_for调用保证低于100%的CPU负载。检查这个条件if(duration <(1.0/maxFrequency))' – 9dan 2014-09-25 06:30:29

回答

1

如果你有一个复杂的现场或非优化渲染

  • 命中瓶颈的地方,或者在GL代码
  • 然后帧率通常下降到约20 fps的(至少在英伟达)无论错误现场
  • 对非常复杂场景的复杂性甚至吼叫是

试试这个:

  1. 尝试测量时需要这种处理

    beginFrame(); 
    drawFrame(); 
    endFrame(); 
    
    • 在那里你会看到你的FPS上限
    • 把它比作场景复杂度/ HW能力
    • ,并决定如果它是一个错误或过于复杂的场景
    • 试图关闭一些GL的东西
    • 例如上周我发现如果我把CU LL_FACE关闭它实际上加速了我的一个非优化渲染约10-100倍,我不知道为什么直到今天(老东西GL代码)
  2. 检查GL错误

  3. 我在您的代码中看不到任何glFlush()/ glFinish()

    • 尝试使用glFinish();
  4. 如果您不能排序了这一点,你仍然可以使用肮脏的伎俩像

    • 添加Sleep(1);到您的代码
    • 这将迫使睡你的线程所以它不会使用100%的功率
    • 它睡觉的时间为1ms +调度粒度,因此也限制了目标FPS
    • 您使用this_thread::sleep_for(chrono::microseconds((int64)dur));
    • 不知道这个功能,你真的确定它是做你的想法吗?
+0

现在,'drawFrame()'完全没有做任何事情。场景并不复杂或简单,因为场景不存在。我打电话的是“glClear”和“SDL_GL_SwapWindow”。这就是为什么我很困惑。添加'glFinish()'到代码实际上使它变得更糟* - 我从25%的使用率降到45-50%! 'glFlush'没有这个问题,但也没有帮助。 – 2014-09-26 04:21:18

+0

@ HaydnV.Harach不测量CPU使用情况,而是测量代码花费的时间。只有这样你才能发现问题所在。 – Spektre 2014-09-26 07:07:24

4

如果V-Sync启用和程序荣誉的交换间隔,那么你看到你的程序占用了100%,实际上是一种假象Windows如何衡量CPU时间。这是一个长期以来已知的问题,但是,无论何时,当程序在驱动程序上下文中阻塞时(这是在OpenGL在V-Sync上阻塞时发生的情况)窗口将记录实际耗用CPU时间的程序,而实际上它只是空闲。

如果您在交换缓冲区之后添加一个Sleep(1),它将使Windows陷入更为理智的会计;在一些系统上,即使是一个Sleep(0)也能做到这一点。

无论如何,大多数情况下,100%只是一个美容问题。


在过去的几周我已经做了低延迟渲染一些详尽的研究(即减少了用户的输入,并走出显示相应的光子之间的时间),因为我很快就得到一个VR耳机。下面是关于定时SwapBuffers的发现:解决这个问题的理想方案实际上是定时帧渲染时间,并在SwapBuffers之前添加仿真睡眠,以便在V-Sync之前几秒钟唤醒。然而,说起来容易做起来难,因为OpenGL是高度异步的,显式添加同步会降低吞吐量。

+0

+1这也可以解释我在过去反击的一些GL怪异 – Spektre 2014-09-25 10:14:53

+0

@Spektre:等到你开始分析各种OpenGL相关调用的时间线。在SwapBuffers和改变OpenGL调用的第一个帧缓冲区内容之间发生的事情真的很奇怪......并且不一致。我发现,如果在* static *场景中启用了V-Sync,则流水线失速会偏离几ms。你会期望这样的测试有一些非常一致的值。显然,OS进程调度器,GPU和OpenGL驱动程序之间正在发生一些非常奇怪的事情。我还没有试验当我更改调度器参数时会发生什么。 – datenwolf 2014-09-25 10:28:20

+0

来自XP +的Windows任务调度程序很奇怪...我在慢速机器上(即使是2核心)和少量线程(〜3不要求)(与OpenGL无关)有问题,从哪一个机器访问asynchro USB传输,并定期挂断几秒到几秒。这让我对W7疯狂,它在W2K上更糟。它只发生在低端硬件(就像一些CELERON笔记本...),即使CPU足够处理它......我怀疑它有访问驱动程序层(这也适用于GL的东西)的东西。这甚至冻结一切甚至不相关... – Spektre 2014-09-25 10:53:47