【问题标题】:Weird CPU usage in OpenGL programOpenGL程序中奇怪的CPU使用率
【发布时间】:2011-08-01 00:42:11
【问题描述】:

在我自己构建的 MFC 程序中,我遇到了一些奇怪的 CPU 使用问题。

我加载了大约 360k 点的点云,一切正常(我使用 VBO 缓冲区,这是我理解的方法吗?)。我可以随意移动它,并且没有注意到任何不利影响(CPU 使用率非常低,GPU 完成了所有工作)。但是在某些角度和缩放值上,我看到我的一个处理器上的 CPU 峰值!然后我可以稍微改变角度或缩放,它会再次下降到 0 左右。大窗口比小窗口更有可能发生这种情况。

我测量了程序的 FPS,它一直保持在 65,但是当 CPU 达到峰值时,它通常会下降大约 10 个单位到 55。我还测量了 SwapBuffers 所花费的时间,在正常运行期间它大约是 0-1 毫秒。一旦 CPU 达到峰值,它就会上升到大约 20 毫秒,所以很明显,在那个函数中计算突然变得非常困难(我猜是 GPU 吗?)。这东西不在 DrawScene 函数中(这是一个在糟糕的实现中会消耗 CPU 的函数),所以我有点茫然。

我知道这不是由于可见点的数量,因为这很容易发生在数据的一个子部分上,就像在整个云上一样。我试图移动它,看看它是否与深度缓冲区、剪辑或类似的有关,但似乎完全随机的是什么角度会产生问题。不过,它似乎确实有些可重复;将模型移动到曾经滞后的位置,再次移动时会滞后。

我是 OpenGL 的新手,所以我犯了一些完全明显的错误并非不可能。

这是渲染循环的样子(它在 MFC 应用程序中通过 1 毫秒周期的计时器事件运行):

    // Clear color and depth buffer bits
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Draw OpenGL scene
    OGLDrawScene();

    unsigned int time1 = timeGetTime();

    // Swap buffers
    SwapBuffers(hdc);

    // Calculate execution time for SwapBuffers
    m_time = timeGetTime() - time1;

    // Calculate FPS
    ++m_cnt;
    if (timeGetTime() - m_lastTime > 1000)
    {
        m_fps = m_cnt;
        m_cnt = 0;
        m_lastTime = timeGetTime();
    }

【问题讨论】:

  • 我在您的问题中看不到太多实际代码。你能多提供一点吗?只是为了澄清一下:您是否每秒调用一千次 DrawScene 和 SwapBuffers?这是一个巨大的数字。
  • 如果您参考 1000 值,它只是确定何时经过 1 秒(1000 毫秒)并更新 fps 值。它每秒循环大约 65 次,但我无法修改,因为它取决于 opengl。至于代码,我真的不知道该显示什么,代码很多......
  • 我指的是您的陈述“它是通过具有 1 ms 周期的计时器事件在 MFC 应用程序中运行的”。因此我想问一下这是否真的经常被调用以及你的计时器的分辨率是多少。
  • 好吧,我现在明白你的意思了。我确实在一个周期为 1 毫秒的计时器上运行它,但即便如此循环每秒只运行 65 次......这有点奇怪,特别是考虑到整个循环的执行时间是正常情况下大约 0-1 毫秒:-/
  • 我们的想法是尽可能地精简代码以识别问题。如果您移除部件并且问题不再存在,这(在大多数情况下)是问题所在的明确提示。另一方面,如果您只剩下几行代码并且问题仍然存在,那么分析就会简单得多(对我们而言,对您而言也是如此)。

标签: c++ opengl mfc cpu-usage


【解决方案1】:

我注意到(至少不久前),ATI 驱动程序倾向于过于激进地旋转等待,而 NVidia 驱动程序则倾向于中断驱动。从技术上讲,旋转等待更快,假设您没有更好的事情可做。不幸的是,今天你可能在另一个线程上有更好的事情要做。

我认为 OP 的显示驱动程序可能确实在等待旋转。

【讨论】:

    【解决方案2】:

    好的,所以我想我已经弄清楚这是怎么发生的了。

    首先,WM_TIMER 消息的生成频率似乎最多不超过每 15 毫秒,即使在使用 timeBeginPeriod(1) 时,至少在我的计算机上也是如此。这导致了我看到的标准 65 fps。

    一旦场景的渲染时间超过 15 毫秒,SwapBuffers 就会成为限制因素。 SwapBuffers 似乎忙于等待,这导致发生这种情况时一个内核上的 CPU 使用率为 100%。这不是在某些摄像机角度发生的事情,而是根据当时屏幕上显示的点数而流畅地改变 fps 的结果。每当渲染碰巧达到超过 15 毫秒的时间并开始在 SwapBuffers 处等待时,它似乎就会出现峰值。

    类似地,有没有人知道“glReadyToSwap”之类的函数?指示缓冲区是否准备好交换的函数?这样我可以选择另一种更高分辨率的渲染方法(例如 1 ms),然后每个 ms 检查缓冲区是否准备好交换,如果它们不只是等待另一个 ms,以免忙等待。

    【讨论】:

    • 不,SwapBuffers 没有忙于等待,Windows 只是在那里显示错误的数字。添加睡眠(0);在 SwapBuffers 之后,报告的 CPU 使用率应该会下降。
    • WM_TIMER 如果您在绘制动画之后不是一个好主意。相反,只需尽可能快地在循环中调用渲染函数并测量处理完整帧所花费的时间。为此使用高分辨率计时器! SwapBuffers 不是(只是)等待渲染完成,它更有可能等待显卡执行垂直同步。如果您禁用 V-Sync(在驱动程序设置中),SwapBuffers 将不会等待,您可以观察到撕裂伪影。
    • @datenwolf:不,它确实会阻塞,添加任何睡眠功能都没有区别。我可以肯定地说,在卡准备好实际交换缓冲区之前,CPU 会卡在 SwapBuffers 上。是的,WM_TIMER 不是一个好主意,但是我仍然想要一个替代 SwapBuffers 忙碌等待的替代方案。我知道这不是由于垂直同步,因为如果我的屏幕是 60Hz,它也应该达到 65 fps。相反,只要卡片的渲染时间比 WM_TIMER 的循环周期长,它就会命中。
    • 是的,当然是 SwapBuffers 块,这就是它的全部想法。创建第二个线程以在渲染线程之外执行操作。一旦 SwapBuffers 阻塞,其他线程将获得所有 CPU 时间。如果 Sleep(0) 没有减少报告的 CPU 时间,请使用 Sleep(1)。无论如何,不​​要尝试实施一些荒谬的时序方案。只需创建一个额外的线程。 CPU也没有卡在SwapBuffers上,只是调用SwapBuffers的线程阻塞直到它返回。
    猜你喜欢
    • 2017-10-03
    • 2023-03-17
    • 2016-01-07
    • 2020-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多