【问题标题】:Taking advantage of multithreading with openGL利用 openGL 的多线程
【发布时间】:2015-01-04 09:06:47
【问题描述】:

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

假设有两个线程:

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

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

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

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

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

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

【问题讨论】:

    标签: android c++ multithreading opengl-es


    【解决方案1】:

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

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

    【讨论】:

    • 中间缓冲区?这听起来像是所有可见游戏对象的临时副本,不是吗?这直接使峰值内存需求量翻倍
    • @Allahjane:仅复制控制绘图所需的数据。根据参与者数据结构的复杂性,这可以节省大量时间。例如,复制整个 AI 状态(如果你的 NPC 有 AI)是没有任何意义的。
    • 如果游戏逻辑发现它必须改变对象的纹理怎么办
    【解决方案2】:

    这是一个很笼统的问题。如果你问 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 的碰撞。有可能在对象 A 测试碰撞之前移动了对象 B,会有不同的结果。

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

    希望这会有所帮助:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-14
      • 1970-01-01
      相关资源
      最近更新 更多