CADisplayLink 无法在模拟器中实现恒定帧率
CADisplayLink is unable to achieve a constant frame rate in simulator
我正在尝试使用 CADisplayLink
来实现游戏循环的恒定帧速率。在模拟器中测试时,我无法获得稳定的时间范围。我应该考虑采用不同的方法吗?
或者仅仅是因为模拟器无法正确模拟硬件?
根据Apple,这是计算 FPS 的方法:
let actualFramesPerSecond = 1 / (displaylink.targetTimestamp - displaylink.timestamp)
在我为测试而设置的普通 iOS 应用程序中,它打印了一个恒定的帧速率:
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999875668439
但是,当我使用实际经过的时间计算 FPS 时,它给出了其他信息:
FPS: 64.35942918520792
FPS: 58.30848150362142
FPS: 57.640194044003465
FPS: 64.47022656706324
FPS: 59.392580005664115
FPS: 60.282043174566674
此外,当使用代码渲染一些精灵时,它在模拟器中看起来不稳定。上面的代码片段怎么可能与现实相差这么多?
我主要担心的是:为什么用 Apple 方式计算 FPS 时帧率是恒定的?他们的文档是否可能只包含一个小故障?此外,当有负载时, 'actual' 值是预期的,但对于一个什么都不做的空循环?
这是我用来执行游戏循环的代码:
private var previousTimeInSeconds: Double = 0
private lazy var displayLink: CADisplayLink = {
let displayLink = CADisplayLink(target: self,
selector: #selector(displayLoop))
return displayLink;
}()
private func startLoop() {
previousTimeInSeconds = Date().timeIntervalSince1970
displayLink.add(to: .current, forMode: .common)
}
@objc private func displayLoop() {
let currentTimeInSeconds = Date().timeIntervalSince1970
let elapsedTimeInSeconds = currentTimeInSeconds - previousTimeInSeconds
previousTimeInSeconds = currentTimeInSeconds
//let actualFramesPerSecond = 1 / (displayLink.targetTimestamp - displayLink.timestamp) // is showing constant 59.xxx FPS
let actualFramesPerSecond = 1 / elapsedTimeInSeconds
print("FPS: \(actualFramesPerSecond)") // varies from 50.xxx to 70.xxx FPS
/*
FPS: 64.35942918520792
FPS: 58.30848150362142
FPS: 57.640194044003465
FPS: 64.47022656706324
FPS: 59.392580005664115
FPS: 60.282043174566674
*/
}
我想我找到了答案。不要使用 Date().timeIntervalSince1970
来计算每秒的单个帧速率。好像不够准确。
而是使用 displayLink.timestamp
,这是与显示的最后一帧关联的时间值。将上面代码中的 Date()
用法替换为 timestamp
,您将获得预期的 FPS 值:
FPS: 59.99999875668439
FPS: 59.99999896623196
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 29.999999430729087
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999896623196
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999896623196
如您所见,它基本上保持在 59 FPS,偶尔会有一些下降,这完全是预料之中的,因为无法保证以恒定速率调用 CADisplayLink
。但是在 'labor situations' 下(几乎)没有执行代码,这是您可以期望的帧速率。
如果要计算实际帧速率时间,我从 Apple 文档 ("let actualFramesPerSecond = 1 / (displayLink.targetTimestamp - displayLink.timestamp)") 中获取的公式有点无用。相反,它表明 targetTimestamp
可用于实现稳定的游戏循环(因为结果值确实是一个常数)。我希望文档在这方面能更清楚。
我正在尝试使用 CADisplayLink
来实现游戏循环的恒定帧速率。在模拟器中测试时,我无法获得稳定的时间范围。我应该考虑采用不同的方法吗?
或者仅仅是因为模拟器无法正确模拟硬件?
根据Apple,这是计算 FPS 的方法:
let actualFramesPerSecond = 1 / (displaylink.targetTimestamp - displaylink.timestamp)
在我为测试而设置的普通 iOS 应用程序中,它打印了一个恒定的帧速率:
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999875668439
但是,当我使用实际经过的时间计算 FPS 时,它给出了其他信息:
FPS: 64.35942918520792
FPS: 58.30848150362142
FPS: 57.640194044003465
FPS: 64.47022656706324
FPS: 59.392580005664115
FPS: 60.282043174566674
此外,当使用代码渲染一些精灵时,它在模拟器中看起来不稳定。上面的代码片段怎么可能与现实相差这么多?
我主要担心的是:为什么用 Apple 方式计算 FPS 时帧率是恒定的?他们的文档是否可能只包含一个小故障?此外,当有负载时, 'actual' 值是预期的,但对于一个什么都不做的空循环?
这是我用来执行游戏循环的代码:
private var previousTimeInSeconds: Double = 0
private lazy var displayLink: CADisplayLink = {
let displayLink = CADisplayLink(target: self,
selector: #selector(displayLoop))
return displayLink;
}()
private func startLoop() {
previousTimeInSeconds = Date().timeIntervalSince1970
displayLink.add(to: .current, forMode: .common)
}
@objc private func displayLoop() {
let currentTimeInSeconds = Date().timeIntervalSince1970
let elapsedTimeInSeconds = currentTimeInSeconds - previousTimeInSeconds
previousTimeInSeconds = currentTimeInSeconds
//let actualFramesPerSecond = 1 / (displayLink.targetTimestamp - displayLink.timestamp) // is showing constant 59.xxx FPS
let actualFramesPerSecond = 1 / elapsedTimeInSeconds
print("FPS: \(actualFramesPerSecond)") // varies from 50.xxx to 70.xxx FPS
/*
FPS: 64.35942918520792
FPS: 58.30848150362142
FPS: 57.640194044003465
FPS: 64.47022656706324
FPS: 59.392580005664115
FPS: 60.282043174566674
*/
}
我想我找到了答案。不要使用 Date().timeIntervalSince1970
来计算每秒的单个帧速率。好像不够准确。
而是使用 displayLink.timestamp
,这是与显示的最后一帧关联的时间值。将上面代码中的 Date()
用法替换为 timestamp
,您将获得预期的 FPS 值:
FPS: 59.99999875668439
FPS: 59.99999896623196
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 29.999999430729087
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999896623196
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999875668439
FPS: 59.99999896623196
如您所见,它基本上保持在 59 FPS,偶尔会有一些下降,这完全是预料之中的,因为无法保证以恒定速率调用 CADisplayLink
。但是在 'labor situations' 下(几乎)没有执行代码,这是您可以期望的帧速率。
如果要计算实际帧速率时间,我从 Apple 文档 ("let actualFramesPerSecond = 1 / (displayLink.targetTimestamp - displayLink.timestamp)") 中获取的公式有点无用。相反,它表明 targetTimestamp
可用于实现稳定的游戏循环(因为结果值确实是一个常数)。我希望文档在这方面能更清楚。