为什么我的渲染线程占用100%的cpu?

时间:2014-09-25 06:01:34

标签: c++ multithreading performance opengl sdl

现在,在我的OpenGL游戏引擎中,当我的渲染线程完全无关时,它会占用我的CPU可以提供的最大值。 Windows任务管理器显示我的应用程序占用了25%的处理(我有4个硬件线程,因此25%是一个线程可以采取的最大值)。当我完全没有启动渲染线程时,我得到0-2%(由于它所做的一切正在运行SDL输入循环,所以它自己很担心)。

那么,我的渲染线程到底在做什么?这是一些代码:

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 :: thread创建的。我能想到的唯一解释是timer.frequencyCap不知何故不起作用,除了我在主线程中使用完全相同的函数而且我在0-2%时闲置。

我在这里做错了什么?

2 个答案:

答案 0 :(得分:4)

如果启用了V-Sync并且您的程序遵循交换间隔,那么您看到程序占用100%实际上是Windows测量CPU时间的工件。这是一个众所周知的问题,但是任何时候你的程序在驱动程序上下文中阻塞(这是在V-Sync上的OpenGL阻塞时会发生这种情况)窗口会为实际消耗CPU时间的程序计算这个问题,而实际上它只是闲置。

如果在交换缓冲区之后立即添加Sleep(1),它将欺骗Windows进入更合理的会计;在某些系统上,即使是Sleep(0),也可以做到这一点。

无论如何,100%只是一个美容问题,大部分时间都是。


在过去的几周里,我对低延迟渲染进行了一些详尽的研究(即最小化用户输入和相应光子从显示器出来之间的时间),因为我很快就会得到一个VR耳机。这就是我发现的关于时序SwapBuffers的问题:该问题的理智解决方案实际上是对帧渲染时间进行计时并在SwapBuffers之前添加一个人工睡眠,以便在V-Sync之前仅唤醒几毫秒。然而,这说起来容易做起来难,因为OpenGL是高度异步的,显式添加同步会降低吞吐量。

答案 1 :(得分:1)

如果您有复杂场景或非优化渲染

  • 在某处遇到瓶颈或在gl代码中出错
  • 然后帧率通常会下降到大约20 fps(至少在NVidia上),无论场景的复杂程度如何
  • 对于非常复杂的场景甚至贬低

试试这个:

  1. 尝试测量处理时间

    beginFrame();
    drawFrame();
    endFrame();
    
    • 你会看到你的fps限制
    • 将其与场景复杂度/硬件能力进行比较
    • 并确定它是一个错误还是太复杂的场景
    • 尝试关闭一些GL内容
    • 例如,上周我发现,如果我关闭CULL_FACE,它实际上加速了我的非优化渲染中的一个大约10-100次,直到今天我都不明白为什么(在旧的GL代码上)
  2. 检查GL错误

  3. 我的代码中没有看到任何glFlush()/ glFinish()

    • 尝试使用glFinish();
    • 进行测量
  4. 如果你无法解决这个问题,你仍然可以使用像

    这样的肮脏技巧
    • Sleep(1);添加到您的代码
    • 它将强制让你的线程睡眠,因此它永远不会使用100%的力量
    • 它睡眠的时间是1ms +调度程序粒度,因此它也限制了目标fps
    • 您使用this_thread::sleep_for(chrono::microseconds((int64)dur));
    • 不知道这个功能你真的确定它符合你的想法吗?