如何减少 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 毫秒左右,所以我认为已经足够了
灵感来自
我正在使用 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 毫秒左右,所以我认为已经足够了
灵感来自