1

我在 OpenGL ES 中创建了一个运行循环,它由 CADisplayLink 以 60fps 调用。AFAIK CADisplayLink 在后台线程上调用它的目标。

我有大约 100 个状态变量供运行循环使用。

问题:从主线程,我想改变在运行循环中使用的状态变量来绘制一些东西。只有在所有状态变量都设置为其目标值之后,才必须绘制框架。

恐怕在某些时候,当我更改状态变量时,我还没有完成所有更改(在主线程上的同一运行循环迭代中的一个大方法中),例如几何形状的位置,有多个- 线程相关的崩溃或问题,CADisplayLink 将在我更新状态变量的方法中间启动,然后绘制垃圾或崩溃。

显然,当我只使用同步或原子属性时,它无济于事,因为它仍然不是事务性的。我想我需要交易。

我天真的方法是这样的:

运行循环读取的实例变量:

BOOL updatingState;

如果updateState 为YES,run loop 方法将跳过绘图。

然后在开始更改状态之前,我将其设置为 YES。当一切都改变了,我把它设置回NO。

当然,现在的问题是:如果 - 当我改变这个时 - 运行循环方法正在读取值怎么办?

游戏引擎如何处理这个问题?他们有什么样的锁定机制,以便在绘制下一帧之前完成状态变量的更改?

4

3 回答 3

2

您可能会发现读取-复制-更新策略很有用。一种可能的实现是每个对象实际上包含两个渲染参数副本,并且使用一个原子标志来告诉渲染线程使用哪个。您将需要在渲染器中使用读取内存屏障以确保在读取任何参数之前读取标志,并在更新程序线程中使用写入内存屏障以确保在翻转标志之前写入所有参数更新.

于 2012-05-17T15:57:16.383 回答
1

完成此操作的常用方法是在绘制完成之前,在每次运行循环迭代中发生所有状态更新。也就是说,运行循环的示意图如下所示:

updateState();
draw();

使用此模型,绘图仅在达到一致状态后发生。

为此,您需要有一个模型,其中在每个 updateState() 上轮询诸如按键之类的事件,而不是异步发生,并在每次迭代中进行时间测量,以告诉您自上一帧以来经过了多少时间。

不过,我无法帮助您如何在 iOS 编程的具体案例中实现这一点,因为我对此一无所知。但我希望我能给你指出正确的方向。

于 2012-05-17T15:54:02.347 回答
1

我认为这是并发中的一个常见问题,因此有几种方法可以做到:

  1. 使用不可变状态类来保存状态变量。
  2. 使用锁定机制(如果不能使用不可变类)来保护状态变量。
  3. 有多个可以修改的状态,但只有一个是“活动的”。这将允许您重用状态,并将减少复制和内存分配。

此外,考虑这种情况:

线程 1. 开始画一些东西。
线程 1. 读取状态 01 参数的 1/2(第一个状态)。
线程 2。用状态 02(第二个状态)交换状态 01。
线程 1. 读取状态 02 的另外 1/2,但它与状态 01 参数不同。

所以最好的选择是不允许在绘图期间更新状态,所以选项 3 可能是最好的方法,因为您只需选择最新的状态并绘制它。假设您有两种状态:drawingStatenonDrawingState。在您的绘图函数中,您将始终使用drawingStateto 绘制,而其他线程修改nonDrawingState. 完成绘图后,您可以交换状态并使用最新的状态修改继续绘图。

于 2012-05-17T15:54:51.813 回答