iOS上的vsync:它是如何工作的

Kay*_*Kay 5 graphics opengl-es ios

我正在试图弄清楚vsync如何在iOS上运行CADisplayLink.我想在这里回答的问题是:

  • 如果我错过CPU端的框架,iOS上会发生什么?

我设置了一些测试:

  • CADisplayLink在iPhone上使用间隔为1 = ~60Hz 的计时器
  • 使用[CADisplayLink timestamp]和两者分析计时器调用之间的时间mach_absolute_time()

测试1:基础测试:在这里,我只跟踪了10000个样本:

timestamp-----
max: 16.762583
min: 16.627625
mean: 16.675080
variance: 0.000333

mach----------
max: 22.606042
min: 10.815958
mean: 16.675694
variance: 0.602793

diff----------
max: 5.941417
min: 0.000750
mean: -0.000615
variance: 0.607476
Run Code Online (Sandbox Code Playgroud)

测试2:由于CPU等待时间短而导致帧丢失:这里,在帧X上,我只是坐在自旋锁中进行Y次迭代.相对于第X帧的记录帧时间:

Y = 200000
-1: 16.72   17.85   (-1.13)
0:  16.66   15.37   (1.29)
1:  33.32   43.74   (-10.42) (*)
2:  16.74   4.34    (12.40) (*)
3:  16.61   16.70   (-0.10)
4:  16.71   16.65   (0.06)  

Y = 100000
-1: 16.66   16.50   (0.16)
0:  16.69   16.67   (0.02)
1:  16.63   22.21   (-5.58) (*)
2:  16.68   9.91    (6.77) (*)
3:  16.70   16.04   (0.67)
4:  16.69   16.70   (-0.01)
Run Code Online (Sandbox Code Playgroud)

测试3:与测试2相同,但使用OS调用睡眠t秒

t = 0.03
-1: 16.67   16.54   (0.14)
0:  16.68   16.91   (-0.23)
1:  33.35   34.78   (-1.43) (*)
2:  16.68   14.93   (1.74) (*)
3:  16.68   16.87   (-0.20)
4:  16.67   16.48   (0.19)

t = 0.02
-1: 16.64   16.54   (0.09)
0:  16.72   16.91   (-0.19)
1:  16.66   20.79   (-4.13) (*)
2:  16.69   12.60   (4.08) (*)
3:  16.67   18.24   (-1.57)
4:  16.68   15.09   (1.59)

t = 3.0
-1: 16.68   16.74   (-0.07)
0:  16.63   16.58   (0.05)
1:  266.84  3004.04 (-2737.20) (*)
2:  2751.35 14.82   (2736.54) (*)
3:  16.66   16.80   (-0.14)
4:  16.68   15.86   (0.81)
Run Code Online (Sandbox Code Playgroud)

观察:

  • 与Apple文档不同,在CPU帧丢失的CADisplayLink情况下等待vsync.
  • 然而,对于小错过,它看起来就像Apple 希望我们相信的那样[CADisplayLink timestamp]
  • 我最好的解释是这样的:

  1. CADisplayLink 只有当图形驱动程序的命令缓冲区为空,或GPU的命令缓冲区为空(并因此从图形驱动程序请求更多命令或取消阻止)时,才会发生调用.
  2. 在iPhone上的GPU已经启用垂直同步-这拖延,并处理更多的GPU命令之前等待物理屏幕刷新.
  3. 如果我们的GPU和CPU帧始终低于16.6ms,我们将生成更多GPU命令然后GPU可以消耗
  4. 因此,当GPU停止时,它会停止从驱动程序中拉出命令,一旦我们填满驱动程序命令缓冲区,我们就会CADisplayLink停止调用.
  5. 在vsync之后,GPU能够处理更多GPU命令,因此也能够从驱动程序中提取命令.当驱动程序命令队列中有足够的空间时,驱动程序会通知iOS它需要更多命令,然后我们得到一个CADisplayLink调用.
  6. (我不知道这是否会驱动iOS上的整个事件循环,或者事件循环是否始终在运行,并且只是轮询驱动程序迭代以查看它是否可以调用 CADisplayLink
  7. 因此,我们CADisplayLink大约每16.6ms调用一次 - 一个管道的下游结果,在远端的vsync 等待.
  8. 但是,如果我们错过CPU侧的帧,即在驱动程序请求后产生命令稍长于16.6ms,则可以.仍然有一两帧(需要测量)仍在管道中的命令.只要我们不让管道耗尽,我们就可以了:我们仍然可以让GPU渲染1个新帧,每16.6ms,正确使用vsync
  9. 这就是为什么[CADisplayLink timestamp]与"实际"时间不同的原因mach_absolute_time().

例如,Test2:i = 100000:

  1. 我们运行了一堆帧,很容易在16.6ms以下打包管道
  2. 我们花了很长时间在一架X(22.21ms)
  3. 然而,GPU落后1帧:帧X-2在屏幕上,当vsync发生时,它不需要帧X的命令,帧X-1仍然存在于管道中
  4. 当下一个vsync发生时,GPU 确实需要帧X的命令,他们就在那里.
  5. 在框架X上花了一段时间后,我们能够在快速启动框架X + 1之后填充管道

因此,对于用户来说,没有丢帧.

我离这里有多远?我希望自己是一名计算机工程师.