結論
多くの場合、requestAnimationFrameのFPSはディスプレイのリフレッシュレートに依存します。
もしも、ディスプレイに依存せずにFPSの最大値を設定したい場合、
const fps = 60; let lastRenderTime = 0; function render(now) { const delta = now - lastRenderTime; if (1000 / fps < delta) { lastRenderTime = now; // 描画の処理 } requestAnimationFrame(render); }
とかで、フレームで行う処理を間引くなどが必要になります。
ことの発端
WebGLのWebコンテンツを制作していた際、
M1 MaxのMacBook Pro 👉 10秒に一回ぐらいフレームが飛ぶ
Core i7のMacBook Pro 👉 安定して動作する
という事象に悩んでいました。
なんでパフォーマンスが高いであろうM1 MaxがCore i7よりも安定しないのか。
そんなにGPUが貧弱なのだろうか。外部ディスプレイの枚数に制限が掛かってるし。
などと、M1の性能を疑っていたのですが、
調査の結果、requestAnimationFrameが、
M1 MaxのMacBook Pro 👉 120FPS
Core i7のMacBook Pro 👉 60FPS
を目指して動作していることが判明し、requestAnimationFrameのFPSはディスプレイのリフレッシュレートに依存するということに気づいた次第です。
これまで、60FPSを目指して動作するものと思い込んでいましたが、それは60FPSのディスプレイで動かしていたからだったようです。
Macは設定からディスプレイのリフレッシュレートを設定できますが、M1 MaxのMacBook Proは初期値がProMotionになっています。
ProMotionのままだとrequestAnimationFrameは120FPSを目指して動作するのですが、60ヘルツに変更すると60FPSを目指して動作するようになります。
ちなみに、リフレッシュレート240Hzのゲーミングモニタを拡張ディスプレイとして繋いだ時の挙動は、
M1 MaxのMacBook Pro + ゲーミングモニタ
- MacBookのディスプレイ上で動かした場合 👉 120FPS
- ゲーミングモニタ上で動かした場合 👉 240FPS
Core i7のMacBook Pro + ゲーミングモニタ
- MacBookのディスプレイ上で動かした場合 👉 60FPS
- ゲーミングモニタ上で動かした場合 👉 60FPS
となりました。
また、M1 MaxのMacBook Pro(120Hz) + ゲーミングモニタ(240Hz)の構成でミラーリングを行った場合は120FPSで動作したので、ミラーリング時はリフレッシュレートが低い方に合わせて動作する模様です。
ことの発端で作成していたWebGLのアプリケーションは、1フレームの中で実行する処理が重すぎて、120FPSに耐えきれなかったことが原因でフレームが飛んでいたようだったので、冒頭で記載した60を超えるFPSで実行されないようなロジックを追加したところ、安定して動作するようになりました。