所以我正在阅读这篇文章,其中包含“SDL中多线程编程的提示和建议” - https://vilimpoc.org/research/portmonitorg/sdl-tips-and-tricks.html
它谈到SDL_PollEvent效率低下,因为它会导致CPU使用率过高,因此建议使用SDL_WaitEvent。
它显示了两个循环的示例,但我看不出它如何在游戏循环中起作用。是否只有不需要不断更新的东西才能使用SDL_WaitEvent,即如果你有游戏运行,你会在每一帧都执行游戏逻辑。
我能想到的唯一可以使用的是像绘画程序这样的程序,其中只有用户输入需要操作。
我认为我应该继续使用SDL_PollEvent进行通用游戏编程吗?
答案 0 :(得分:9)
如果您的游戏仅更新/重新绘制用户输入,那么您可以使用SDL_WaitEvent。但是,即使没有用户输入,大多数游戏都会进行动画/物理。所以我认为SDL_PollEvent对于大多数游戏来说都是最好的。
SDL_WaitEvent可能有用的一种情况是,如果您在一个线程中拥有它,而在另一个线程上拥有动画/逻辑。这样即使SDL_WaitEvent等待很长时间,您的游戏也会继续绘画/更新。 (编辑:这可能实际上不起作用。请参阅Henrik下面的评论)
对于使用100%CPU的SDL_PollEvent,如文章所示,当您检测到游戏的运行速度超过所需的每秒帧数时,可以通过在循环中添加睡眠来缓解这种情况。
答案 1 :(得分:6)
如果您的输入中不需要子帧精度,并且您的游戏一直是动画,那么SDL_PollEvent是合适的。
子帧精度对于例如重要。玩家可能想要非常小的移动增量的游戏 - 如果你使用keydown的经典懒惰方法来表示“velocity = 1”而keyup意味着“velocity = 0”,则快速点击和释放键具有不可预测的行为然后你只能每帧更新一次位置。如果您的点击恰好与帧渲染重叠,那么您将获得一帧持续时间的移动,如果它没有得到任何移动,那么您真正想要的是一个小于长度的移动量基于事件发生的时间戳的帧的结构。
不幸的是,SDL的事件不包括来自操作系统的实际事件时间戳,只包括PumpEvents调用的时间戳,而且WaitEvent以10ms的间隔有效地轮询,因此即使在单独的线程中运行WaitEvent,也是最精确的ll get是10ms(如果你在相同的轮询周期中得到一个keydown和keyup那么你可能会大约更小一些,然后它是〜5ms)。
因此,如果你真的想要对输入进行精确计时,你实际上可能需要使用较小的SDL_Delay编写自己的SDL_WaitEventTimeout版本,并在主游戏循环的单独线程中运行 。
进一步遗憾的是,SDL_PumpEvents必须在初始化视频子系统的线程上运行(每https://wiki.libsdl.org/SDL_PumpEvents),因此在另一个线程上运行输入循环以获得子帧时序的整个想法由SDL限定框架。
总之,对于带动画的SDL应用程序,没有理由使用除SDL_PollEvents之外的任何东西。对于子帧速率输入精度你可以做的最好的事情是,如果你有时间在帧之间刻录,你可以选择在那段时间内精确,但是你会得到奇怪的渲染持续时间窗口每个框架,您的输入失去精度,所以最终会出现不同的不一致。
答案 2 :(得分:5)
通常,您应该使用SDL_WaitEvent而不是SDL_PollEvent将CPU释放到操作系统以处理其他任务,例如处理用户输入。这将向用户显示对用户输入的缓慢反应,因为这可能导致他们输入命令和应用程序处理事件之间的延迟。通过使用SDL_WaitEvent,操作系统可以更快地将事件发布到您的应用程序,从而提高感知性能。
作为附带好处,电池供电系统(如笔记本电脑和便携式设备)的用户应该看到电池使用量略少,因为操作系统有机会降低整体CPU使用率,因为您的游戏100%没有使用它 - 它只会在事件发生时才使用它。
答案 3 :(得分:0)
您实际上可以在主线程中初始化SDL和窗口,然后再创建2个线程用于更新(随时间推移更新游戏状态和变量)和渲染(相应渲染表面)。 然后,在完成所有操作之后,在主线程中使用SDL_WaitEvent来管理SDL_Event。这样,您可以确保在与sdl_init相同的线程中管理事件。
我长期以来一直使用这种方法来使我的游戏在Windows和Linux上运行,并且能够如上所述成功同时运行3个线程。
我必须使用互斥锁来确保可以通过暂停渲染线程来在更新线程中变换/更改纹理/表面,并且该锁每60帧调用一次,因此该锁不会造成重大性能问题。
此模型最适合创建事件驱动的游戏,运行时游戏或两者。