CADisplayLink не может обеспечить постоянную частоту кадров в симуляторе.

Я пытаюсь использовать 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

Кроме того, при использовании кода для рендеринга некоторых спрайтов он выглядит нестабильным в симуляторе. Как возможно, что приведенный выше фрагмент кода так сильно расходится с реальностью?

Меня больше всего беспокоит: почему при расчете FPS в Apple используется постоянная частота кадров? Возможно ли, что их документация просто содержит небольшой сбой? Кроме того, «фактические» значения как бы ожидаются, когда есть некоторая нагрузка, но для пустого цикла, который ничего не делает?

Это код, который я использую для выполнения своего игрового цикла:

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
     */
}

person Datasun    schedule 14.09.2019    source источник
comment
Я бы не беспокоился о частоте кадров в симуляторе. Вопрос в том, какой частоты кадров вы достигаете на разумно настроенных устройствах. И убедитесь, что тестируете релизную сборку, а не отладочную.   -  person Rob    schedule 14.09.2019
comment
CADisplayLink не обещает постоянной частоты кадров. Он обещает частоту кадров, связанную с дисплеем. Даже на iOS, хотя это может быть довольно последовательным, это не обещано, и вы, конечно, не можете на это полагаться. Вы должны делать анимацию на основе фактического времени, не предполагая, что скорость всегда постоянна.   -  person Rob Napier    schedule 14.09.2019


Ответы (1)


Думаю, я нашел ответ. Не используйте 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 вызывается с постоянной скоростью. Но в «рабочих ситуациях» с (почти) отсутствием кода для выполнения это частота кадров, которую вы могли бы ожидать.

Формула, которую я взял из документации Apple («let factualFramesPerSecond = 1 / (displayLink.targetTimestamp — displayLink.timestamp)»), бесполезна, если вы хотите рассчитать реальную частоту кадров. Вместо этого он показывает, что targetTimestamp можно использовать для достижения стабильного игрового цикла (поскольку результирующее значение действительно является константой). Я хотел бы, чтобы документация была более ясной в этом.

person Datasun    schedule 15.09.2019