【发布时间】:2022-03-03 22:34:02
【问题描述】:
我正在使用 Python 中的 SDL2 和 OpenGL(pysdl2、PyOpenGL)编写一个实时交互式图形应用程序。应用程序不断产生帧,这些帧可能会响应键盘/鼠标输入或根据时间而变化。
我的主事件循环,从网络上一些我无法再次找到的晦涩来源复制而来,看起来(简化)如下:
event = sdl2.SDL_Event()
running = True
while running:
# process events
while sdl2.SDL_PollEvent(ctypes.byref(event)) != 0:
if event.type == sdl2.SDL_QUIT:
running = False
# render frame
<some OpenGL commands>
sdl2.SDL_GL_SwapWindow(window)
sdl2.SDL_Delay(10)
据我了解,10 毫秒的延迟旨在给 CPU 一些喘息的空间,事实上,当我移除它时,CPU 使用率会翻倍。
我不明白为什么它是必要的。图形显示是双缓冲的,缓冲交换与垂直回扫同步(60 Hz = 16 ms)。天真地认为如果<some OpenGL commands> 花费少于 16 毫秒,那么SDL_GL_SwapWindow 无论如何都会引入延迟,所以SDL_Delay 是不必要的。如果他们使用更多,那么程序将难以跟上显示帧速率,并且引入延迟会造成伤害。
现在从response to another question 中告诉我的内容来看,在执行SDL_GL_SwapWindow 时不会发生缓冲区交换和回溯同步,但这只会将“同步和交换”指令放入 OpenGL队列,并且在之前的所有内容都完成后执行该指令。 <some OpenGL commands> 也是如此。但是这个指令队列是有限的,因此在某些时候我的程序不是等待回溯,而是等待指令队列中有空间。最终效果应该是相同的:如果我的事件循环的一次执行平均需要少于 16 毫秒,那么程序将平均延迟足够长的时间以使其每次循环执行 16 毫秒。再说一遍,为什么需要显式延迟?
第二个问题:考虑到延迟可能会影响帧率,有没有更好的方法让 CPU 休息?
【问题讨论】:
-
可能是初始代码旨在将帧速率限制为垂直同步的一半,我们并不知道(并且不同的显示器具有不同的刷新率)。如果你想要完整的显示器刷新率并且你有 vsync - 它不是必需的。如果 vsync 被禁用(或实施不当),事情会变得更加复杂,但简单的常数值延迟不是这里的解决方案(更不用说延迟不是很精确)。我没有得到第二个问题,这是两个相互矛盾的观点。
-
@keltar,感谢您对迟到的回复感到抱歉。第二个问题与之前的观察有关:“据我了解,10 毫秒的延迟是为了给 CPU 一些喘息的空间,确实,当我删除它时,CPU 使用率会翻倍。 "
-
该程序不受 CPU 限制,CPU 只是每帧发送一些 OpenGL 命令。帧速率由 vsync 控制(至 60 Hz)。所以,无论如何,CPU应该主要是等待。尽管如此,拥有
SDL_Delay会减少报告的 CPU 使用率(在这种情况下不降低帧速率,显然 CPU 每帧需要少于 6 毫秒)。我不明白,但似乎需要一些“给 CPU 一些喘息空间”的东西? -
也许
SDL_Delay有暗示操作系统调度程序的副作用? -
如果你确定你仍然有 60fps(30 对解释来说太容易了),那么我会开始使用系统范围的分析器,比如 Linux 上的 perf/oprofile 或 Windows 上的 ETW,看看时间在哪里被烧毁.也许 dirver 认为下一个 vsync 的时间相对较快,并诉诸自旋锁而不是操作系统调用延迟/重新安排。 SDL_Delay 不是自旋锁,因此它不使用 CPU,但要求操作系统“暂停我的进程/线程至少在那段时间”——对于某些任务来说太不精确了。
标签: python opengl sdl-2 pyopengl pysdl2