我在OpenGL ES中创建了一个运行循环,由CADisplayLink以60fps调用。 AFAIK CADisplayLink在后台线程上调用它的目标。
我有大约100个运行循环使用的状态变量。
问题:从主线程,我想更改运行循环中使用的状态变量来绘制一些东西。只有在所有状态变量都设置为目标值后才能绘制框架。
我担心在某些时候,当我更改状态变量时,我还没有更改它们(在主线程上的相同运行循环迭代中的一个大方法),例如几何形状的位置,存在多线程相关的崩溃或问题,其中CADisplayLink将在我的方法中间启动,更新状态变量,然后绘制垃圾或崩溃。
显然,当我只使用同步或原子属性时,它无济于事,因为它仍然不是事务性的。我想我需要交易。
我天真的做法是:
运行循环读取的实例变量:
BOOL updatingState;
如果updatesState为YES,则运行循环方法将跳过绘图。
然后在开始改变状态之前,我将其设置为YES。当一切都改变了,我把它重新设置为NO。
现在当然,问题:如果 - 我正在改变这一点 - 运行循环方法正在读取值?
游戏引擎如何处理这个问题?它们具有什么样的锁定机制,因此可以在绘制下一帧之前完成状态变量的更改?
答案 0 :(得分:2)
您可能会发现有用的读取 - 复制 - 更新策略。一种可能的实现是每个对象实际上包含两个呈现参数副本,并且原子标志用于告知呈现线程使用哪个。您需要在渲染器中使用读取内存屏障,以确保在读取任何参数之前读取该标志,并在更新程序线程中写入内存屏障,以确保在翻转标志之前写入所有参数更新
答案 1 :(得分:1)
通常的方法是在绘制完成之前,所有状态更新都发生在每次运行循环迭代中。也就是说,运行循环看起来像这样:
updateState();
draw();
使用此模型,绘图仅在达到一致状态后才会发生。
为了实现这一点,你需要有一个模型,在每个updateState()上轮询事件,例如按键,而不是异步发生,以及每次迭代的时间测量,告诉你自上次以来经过了多少时间帧。
我无法帮助你在iOS编程的具体情况下如何实现这一点,因为我对此一无所知。但我希望我能指出你正确的方向。
答案 2 :(得分:1)
我认为这是并发中的常见问题,因此有几种方法可以做到:
此外,请考虑以下情况:
线程1.开始画画。
线程1.读取状态01参数的1/2(第一状态)
线程2.用状态02(第二状态)交换状态01
线程1.读取状态02的另一个1/2,但它与状态01参数不同。
因此,最好的选择是不允许在绘图期间更新状态,因此选项3可能是最好的方法,因为您只需获取最新状态并绘制它。假设您有两种状态:drawingState
和nonDrawingState
。在绘图功能中,您将始终使用drawingState
绘制,而其他线程则修改nonDrawingState
。完成绘图后,您可以交换状态并继续绘制最新的状态修改。