unityでいってみよう!

unityがチョットワカル位の人のブログ

【2021年版】リフレッシュレートとフレームレートの関係性を確認してみよう!

はじめに

Unity2018以降のリフレッシュレートとフレームレートの関係性を簡単にまとめてみました。 尚、この情報はモバイル(Android,iOS)向けに限定したいます。PCや他のプラットフォームではまた事情が違うことに注意して下さい。

※現在手持ちのMacが故障中の為、Androidバイスでのみ検証を行っております。その為、iOSに関しては検証を飛ばしていることにご注意下さい。

Unity2018

[iOS]Screen.currentResolution.refreshRateの修正

Unity2017迄はiOSの Promotion technology(refresh rate 120Hz)を有効にしていた場合でも、Screen.currentResolution.refreshRateの値は60固定でしたが、Unity2018から正しいリフレッシュレートを返すように修正されました。

※Promotion technologyを有効にした場合、実際に120[Hz]で動作していたが、Screen.currentResolution.refreshRateの値は60[Hz]を返していたということになります。

Unity2019

[Android]Screen.currentResolution.refreshRateの修正

Unity2018迄はアプリケーション起動時にのみ取得している為、アプリケーション起動後にリフレッシュレートが変更されてもこの値が変化していませんでしたが、Unity2019からは、アプリケーション起動中にリフレッシュレートが変更された場合でもScreen.currentResolution.refreshRateは現在値を返すように修正されました。

[Android]Time.deltaTimeの返す値に補正が入った

Application.targetFrameRateに-1もしくは、リフレッシュレートを整数で割った値のいずれかを設定しない場合、指定したターゲットフレームレートより低い値で動作します。これはモバイル端末の画面のフリックが必ずVSYNCで行われるです。フレームレートとリフレッシュレートの同期が一致しない場合、たとえゲームループがリフレッシュレートよりも早く動作しても、画面の切り替えの部分で詰まりが生じる為です。より詳細な説明は下記の通りです。

https://developer.android.com/games/sdk/frame-pacing#submit_frames_as_quickly_as_the_rendering_api_allows

The display pipeline contains a queue of frames, typically of size 2, which fills up if the game is trying to present frames too quickly. With no more room in the queue, the game loop (or at least the rendering thread) is blocked by an OpenGL or Vulkan call. The game is then forced to wait for the display hardware to show a frame, and this back-pressure synchronizes the two components. This situation is known as buffer-stuffing or queue-stuffing. The renderer process doesn't realize what's going on, so framerate inconsistency gets worse. If the game samples input before the frame, input latency gets worse.

例えば、リフレッシュレートが90Hzで動作している端末においてApplication.targetFrameRate = 60;とすると、フレームレートが低下します。フレームレートは一般的にTime.deltaTimeから求めますが、ほぼ空のSceneを計測した場合、Unity2018までは、0.0166666...と固定の値を返していましたが、Unity2019からはこの値が一定の周期で(0.01~33.4[ms]の間で)ばらつくようになりました。 下記のグラフはQualitySettings.vSyncCount = 0,Application.targetFrameRate=60で動作するアプリケーションをリフレッシュレートを90[Hz]に設定したPixel4XLで実行した結果です。Time.deltaTimeの値を毎フレーム取得した結果が黄色のラインなのですが、周期的にばらつきがおきているが、平均値は16.6[ms]になっていることが確認できます。

Time.deltaTimeの周期的なバラつき

[Android] Optimized Frame Pacingの実装

https://unity.com/sites/default/files/styles/810_scale_width/public/2019-07/unity-platforms-android-frame-pacing.jpg?itok=Ao5Bg6E-

Frame Pacingの機能の一つとして、マルチリフレッシュレートと呼ばれる機能があります。 これは、Pixel4のようにデバイスのディスプレイが複数のリフレッシュレートに対応している場合、アプリケーションのターゲットフレームレートに合わせて、リフレッシュレートを変更してくれます。

例えば、リフレッシュレートが90Hzの端末でApplication.targetFrameRate = 60;とするとフレームレートが低下すると記載しましたが、Optimized Frame Pacingを有効にするとリフレッシュレートが60Hzに変更される為、フレームレートが低下することはありません。 但し、上げ方向に変化することはなく、OSの設定が60Hzの場合にApplication.targetFrameRate = 45;とした場合でもリフレッシュレートは60Hzのままとなります。

バイスのリフレッシュレートとマルチリフレッシュレートで対応するフレームレートは下記の通りです。

  • On 60 Hz devices: 60 FPS / 30 FPS / 20FPS
  • On 60 Hz + 90 Hz devices: 90 FPS / 60 FPS / 45 FPS / 30 FPS
  • On 60 Hz + 90 Hz + 120 Hz devices: 120 FPS / 90 FPS / 60 FPS / 45 FPS / 40 FPS / 30 FPS

https://developer.android.com/games/sdk/frame-pacing?hl=ja#multiple_refresh_rates

Unity2020の場合

Androidバイスでの検証結果としてUnity2019と比較して、変化は見られませんでしたが、こちらの動画によるとUnity2020からのTime.deltaTimeの精度について話されているのでモバイル端末にも実は変化があるのかもしれません。

youtu.be

Unity2021の場合

[Android ]QualitySettings.vSyncCountの仕様変更

AndroidプラットフォームにおいてもiOSプラットフォームと同様にQualitySettings.vSyncCountの値を無視するようになりました。 ※QualitySettings.vSyncCount = 0として扱われます。

これはiOSプラットフォームもそうですが、VSYNC待ちをしないではないという事に注意して下さい。 リフレッシュレートがターゲットフレームレートで割り切れる場合、これまで同様のVSYNC待ちを行います。

その為、Application.targetFrameRateにリフレッシュレートを整数で割った値以外を設定すると、FPSが不安定になります。

以上です。

関連記事

unityletsgo.hatenablog.com

unityletsgo.hatenablog.com