如何减少 OpenGL CPU 使用率 and/or 如何正确使用 OpenGL

How to reduce OpenGL CPU usage and/or how to use OpenGL properly

我正在使用 OpenGL 构建 Micromouse simulation application,我有一种预感,我没有正确地做事。特别是,我怀疑我让我的(主要是静态的)图形以接近恒定的帧速率 (60 FPS) 刷新的方式。我的做法是:

1) 启动计时器
2) 绘制我的形状和文字(大约一千个):

glBegin(GL_POLYGON);
for (Cartesian vertex : polygon.getVertices()) {
    std::pair<float, float> coordinates = getOpenGlCoordinates(vertex);
    glVertex2f(coordinates.first, coordinates.second);
}   
glEnd();

glPushMatrix();
glScalef(scaleX, scaleY, 0);
glTranslatef(coordinates.first * 1.0/scaleX, coordinates.second * 1.0/scaleY, 0);
for (int i = 0; i < text.size(); i += 1) {
    glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, text.at(i));
}
glPopMatrix();

3) 调用

glFlush();

4) 停止计时器
5) 休眠 (1/FPS - 持续时间) 秒
6) 调用

glutPostRedisplay();

"problem" 是上面的方法确实占用了我的 CPU - 该过程使用了 96-100% 之类的东西。我知道使用大量 CPU 本质上没有任何问题,但我觉得我不应该一直使用那么多

更重要的是 大多数 图形不会在帧与帧之间发生变化。它实际上只是一个移动到(并覆盖)一些静态形状的多边形。有什么方法可以告诉 OpenGL 只重绘自上一帧以来发生变化的内容(希望它会减少 glxxx 调用的次数,我认为这是 "problem" 的来源)?或者,更好的是,我让图形刷新的方法是否正确?

首先,OpenGL 最大的 CPU 猪是 即时模式 … 而您正在使用它(glBegin、glEnd)。 IM 的问题是,每个单独的顶点都需要进行一对完整的 OpenGL 调用;并且由于 OpenGL 使用线程本地状态,这意味着每个 OpenGL 调用都必须通过某种间接方式。所以第一步就是摆脱它。

下一个问题是如何为显示计时。如果用户输入和显示之间的低延迟不是您的最终目标,标准方法将设置 window 进行双缓冲,启用 V-Sync,将 swap interval 设置为 1 并进行缓冲区交换(glutSwapBuffers) 一旦帧被渲染。阻塞的确切时间和位置取决于实现(不幸的是),但是只要您的渲染器能够跟上(即渲染帧花费更少的时间),您或多或少可以保证准确地达到屏幕刷新频率一个屏幕刷新间隔)。

glutPostRedisplay 只是为主循环设置一个标志,以便在没有其他事件未决时调用显示函数,因此通过它重新绘制帧的时间不是很准确。

最后但同样重要的是,您可能会被 Windows 计算 CPU 时间(在驱动程序上下文中花费的时间,包括阻塞、等待 V-Sync)的方式简单地嘲笑到消耗的 CPU 时间,而它实际上是可中断的睡眠。 不管你怎么写,你已经在你的代码中做了一个睡眠,这将排除这种情况,因为获得更合理的会计的首选方法是在之前或之后添加一个 Sleep(1)缓冲区交换。

我发现让渲染线程进入睡眠状态有助于将 cpu 使用率从(我的情况)26% 降低到 8% 左右

#include <chrono>
#include <thread>

void render_loop(){
  ...
  auto const start_time = std::chrono::steady_clock::now();
  auto const wait_time = std::chrono::milliseconds{ 17 };
  auto next_time = start_time + wait_time;
  while(true){
    ...
    // execute once after thread wakes up every 17ms which is theoretically 60 frames per 
    // second
    auto then = std::chrono::high_resolution_clock::now();
    std::this_thread::sleep_until(next_time);

    ...rendering jobs

    auto elasped_time = 
    std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::high_resolution_clock::now() - then);
    std::cout << "ms: " << elasped_time.count() << '\n';
    next_time += wait_time;
  }
}

我考虑过在线程休眠时尝试测量帧速率,但我的用例没有任何理由尝试这样做。结果平均在 16 毫秒左右,所以我认为已经足够了

灵感来自