利用 openGL 的多线程

Taking advantage of multithreading with openGL

本文关键字:多线程 openGL 利用      更新时间:2023-10-16

在基于 openGL 渲染器的游戏上下文中交谈:

假设有两个线程:

  1. 更新游戏对象的游戏逻辑和物理等

  2. 根据游戏对象
  3. 中的数据对每个游戏对象进行 openGL 绘制调用(线程 1 不断更新)

除非在游戏

的当前状态下,每个游戏对象都有两个副本,否则您必须在线程 2 进行绘制调用时暂停线程 1,否则游戏对象将在对该对象的绘制调用过程中更新! 这是不可取的!

但是停止线程 1 以安全地从线程 2 进行绘制调用会扼杀多线程/并发的全部目的

除了使用数百或数千或同步对象/围栏以便可以利用多核架构来提高性能之外,还有更好的方法吗?

我知道我仍然可以使用多线程为尚未成为当前游戏状态一部分的对象加载纹理和编译着色器,但是如何为活动/可见对象执行此操作而不会与绘制和更新发生冲突?

通常的方法是,模拟线程在完成游戏步骤后将状态提交到中间缓冲区中,然后向渲染器线程发出信号。由于 OpenGL 异步执行,渲染线程应该相当快地完成,从而释放下一个状态的中间缓冲区。

无论如何,您都不应该直接从游戏状态进行渲染,因为渲染器需要执行其工作的内容和模拟生成的内容并不总是相同的。因此,无论如何,可能需要进行一些映射。

这是你问的一个很普遍的问题。如果你问10个不同的人,你可能会得到10个不同的答案。过去,我实现了类似的东西,这就是我所做的(经过一系列优化周期)。

在后台线程上运行的模型更新循环应如下所示:

while(true)
{
  updateAllModels()
}

正如您所说,当 GL 线程启动时,这将导致问题,因为它很可能基于渲染一半的模型呈现视图,这可能会导致 UI 故障在最佳情况下。

处理此问题的直接方法是同步更新:

while (true)
{
  synchronized(...)
  {
    updateAllModels();
  }
}

此处同步的对象与用于同步绘制方法的对象相同。

现在我们有一个改进的方法,它不会在 UI 中引起故障,但整体渲染可能会受到非常严重的性能影响,因为所有渲染都需要等到所有模型更新完成,反之亦然 - 模型更新需要等到所有绘图完成。

现在,让我们思考一下 - 我们真正需要同步什么?

在我的应用程序(太空游戏)中,在更新模型时,我需要计算矢量、检查碰撞并更新所有对象的位置、旋转、比例等。

在所有这些事情中,视图唯一关心的是位置、旋转、比例和其他一些小注意事项,UI 需要了解这些注意事项才能正确呈现游戏世界。渲染过程不关心游戏对象的向量、AI 代码、碰撞测试等。考虑到这一点,我将更新代码更改为如下所示:

while (true)
{
  synchronized(...)
  {
     updateVisibleChanges(); // sets all visible changes - positions, rotations, etc
  }
  updateInvisibleChanges(); // alters vectors, AI calculations, collision tests, etc  
}

与以前一样,我们正在同步更新和绘制方法,但这一次,关键部分比以前小得多。本质上,在 updateVisibleChanges 方法中应该设置的唯一内容是与应呈现的对象的位置、旋转、缩放等相关的内容。所有其他计算(通常是最详尽的计算)都是在之后执行的,并且不会停止渲染的发生。

这种方法的另一个好处 - 当您执行不可见的更改时,您可以确保所有对象都处于它们需要的位置(这对于准确的碰撞测试非常有用)。例如,在最后一个方法之前的方法中,对象 A 移动,然后对象 A 测试与尚未移动的对象 B 的碰撞。如果对象 B 在对象 A 测试碰撞之前移动,可能会有不同的结果。

当然,我展示的最后一个示例并不完美 - 您仍然需要挂起渲染方法和/或 updateVisible 方法以避免冲突,但我担心这始终是一个问题,关键是最大限度地减少您在任一线程敏感方法中所做的工作量。

希望这对:)有所帮助