【问题标题】:How to write an SDL2 / OpenGL event loop如何编写 SDL2 / OpenGL 事件循环
【发布时间】: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)。天真地认为如果&lt;some OpenGL commands&gt; 花费少于 16 毫秒,那么SDL_GL_SwapWindow 无论如何都会引入延迟,所以SDL_Delay 是不必要的。如果他们使用更多,那么程序将难以跟上显示帧速率,并且引入延迟会造成伤害。

现在从response to another question 中告诉我的内容来看,在执行SDL_GL_SwapWindow 时不会发生缓冲区交换和回溯同步,但这只会将“同步和交换”指令放入 OpenGL队列,并且在之前的所有内容都完成后执行该指令。 &lt;some OpenGL commands&gt; 也是如此。但是这个指令队列是有限的,因此在某些时候我的程序不是等待回溯,而是等待指令队列中有空间。最终效果应该是相同的:如果我的事件循环的一次执行平均需要少于 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


【解决方案1】:

你不需要使用sdl2.SDL_Delay(10),因为如果你使用事件循环,SDL_Delay 是 SDL2 框架的计时器,那么它不需要使用 SDL_Delay。

我的 Python 3.10 代码看起来像 KeyPress and Close window by mouse click 示例

event = sdl2.SDL_Event()
running = True
while running:
    while sdl2.SDL_PollEvent(ctypes.byref(event)) != 0:
        if event.type == sdl2.SDL_QUIT:
            running = False

        if event.type == sdl2.SDL_KEYDOWN:
            if event.key.keysym.sym == sdl2.SDLK_ESCAPE:
                running = False

    ... GL Functions ...

    sdl2.SDL_GL_SwapWindow(window)

请记住,事件循环应该像 C/C++ 中的其他。 Java、C# 或其他编程语言。

没有事件循环意味着 你应该使用SDL_Delay() 然后游戏窗口会自动关闭。

希望您能理解 - 我找到了 Python 示例,但它们看起来很奇怪且错误 - 它们写得像“拉丁语”。

我想提高谁像其他编程语言一样理解得更好。 PS:我会用清晰的例子在我的github上努力工作:)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-03
    • 1970-01-01
    • 2013-07-24
    • 2020-08-14
    相关资源
    最近更新 更多