1フレーム16.7msの内訳を数える — 入力から表示までの遅延を分解する
「遅延が大きい」という言葉は、対策の役に立ちません。どこで何ミリ秒使っているかが分からないと、削る場所を決められないからです。
60fps のゲームで 1フレームは約16.7ミリ秒。この長さを基準に、入力から表示までに挟まる処理を分解します。
→ 競技シーンと技術の話題を追うなら e-Sports TODAY
基準となる長さ
まず、フレーム時間を確認します。
const frameMs = (fps) => 1000 / fps;
frameMs(30); // 33.3
frameMs(60); // 16.67
frameMs(120); // 8.33
frameMs(240); // 4.17
60fps の 16.7ms が、遅延を測るときの単位になります。「2フレーム遅い」は約33msということです。
この単位で考えると、後述する各項目が「何フレームぶんか」で比較できるようになります。ミリ秒のまま扱うより、影響の大きさが直感的になります。
入力から表示までに挟まるもの
ボタンを押してから画面が変わるまでを、順に並べます。
| # | 区間 | 性質 |
|---|---|---|
| 1 | 入力デバイスのスキャンと転送 | デバイス依存 |
| 2 | OS / ランタイムでの入力受け取り | 実装依存 |
| 3 | 次のフレームの入力読み取りまでの待ち | 最大1フレーム |
| 4 | ゲームロジックの処理 | 実装依存 |
| 5 | 描画とバッファのスワップ | パイプライン段数に依存 |
| 6 | ディスプレイの走査と応答 | 機器依存 |
ここにオンライン対戦なら通信が加わります。
| # | 区間 | 性質 |
|---|---|---|
| 7 | 自分 → サーバ/相手 の片道 | 回線・距離依存 |
| 8 | 相手側での処理 | 相手の環境 |
| 9 | 相手 → 自分 の片道 | 回線・距離依存 |
1〜6 は自分の環境だけで閉じますが、7〜9 は相手と回線が絡みます。 この違いが対策の方針を分けます。
3番が見落とされやすい
上の表で見落とされやすいのが3番、次のフレームの入力読み取りまでの待ちです。
入力はフレーム境界でまとめて読まれます。フレームの直後に押せば、ほぼ1フレームぶん待たされます。直前に押せば、ほぼ待ちません。
// 平均待ち時間 ≒ フレーム時間の半分
const avgInputWait = (fps) => 1000 / fps / 2;
avgInputWait(60); // 8.33ms
avgInputWait(120); // 4.17ms
これは実装の不備ではなく、離散的にサンプリングしている以上避けられない待ちです。フレームレートを上げると、この待ちが機械的に半分になります。 描画品質のためではなく遅延のためにフレームレートを上げる、という判断はここから来ます。
通信は往復で数える
対戦相手の入力が自分の画面に反映されるまでは、片道ではなく往復に近い時間がかかります。
const rtt = 40; // 往復 40ms と仮定
const framesLate = rtt / frameMs(60); // 約 2.4 フレーム
往復40msは、60fps でおよそ2.4フレームぶんです。相手の動きが2フレーム以上遅れて見えているという状態を、そのまま表示すると操作感が破綻します。
ここで採られるのが、遅延を隠す仕組みです。
遅延を隠す2つの方針
入力遅延を挟む方式は、自分の入力も相手のぶんが届くまで待ってから反映します。全員が同じタイミングで同じ状態を見るので、状態のずれが起きません。代わりに、自分の操作にも常に遅れが乗ります。
予測して巻き戻す方式は、相手の入力を予測してひとまず進めます。実際の入力が届いた時点で予測が外れていれば、その時点まで巻き戻して再計算します。自分の操作は即座に反映されますが、巻き戻しの瞬間に表示が飛びます。
// 巻き戻しの概形
if (受信した入力.frame < 現在のframe) {
状態を 受信した入力.frame の時点まで戻す
その入力で再適用
現在のframe まで再シミュレーション
}
再シミュレーションを1フレームで終える必要があるため、ゲームロジックは決定的で、かつ十分に速くなければなりません。 浮動小数点の扱いや乱数の種を含めて、同じ入力から同じ結果が出ることが前提になります。
この2つの選択は、ジャンルによって向き不向きが分かれます。どちらが優れているという話ではなく、何を犠牲にするかの選択です。
削れる場所と削れない場所
分解すると、対策の優先順位が決まります。
| 区間 | 削れるか | 手段 |
|---|---|---|
| 入力デバイス | △ | ポーリングレートの高い機器 |
| フレーム待ち(3) | ○ | フレームレートを上げる |
| ゲームロジック(4) | ○ | 処理の最適化 |
| 描画パイプライン(5) | ○ | バッファ段数を減らす設定 |
| ディスプレイ(6) | △ | 応答速度・リフレッシュレート |
| 通信(7〜9) | × | 物理距離は縮まらない |
通信の物理的な下限は削れません。 サーバの配置を変えることはできても、光の速度は変わりません。だから予測と巻き戻しのような、隠す仕組みが必要になります。
逆にいえば、1〜6 は自分で削れる範囲です。ここを詰めずに回線だけを責めるのは、順序が逆になります。
測るときの注意
分解して数えるには、区間ごとの計測が要ります。
- 端から端までの実測(入力から画面変化まで)は、外部の計測手段が要る
- アプリ内の計測は 3〜5 の範囲しか見えない
- RTT の平均値だけでは足りない。 ジッタ(ばらつき)が予測の失敗率を決める
平均RTTが同じでも、安定している回線と揺れる回線では体感がまったく違います。巻き戻しの発生頻度が変わるからです。平均だけでなく分布を見る必要があります。
まとめ
- 60fps の 1フレームは約16.7ms。この単位で全部を数える
- 入力から表示までは6区間、対戦ならさらに通信の3区間が乗る
- フレーム待ちの平均はフレーム時間の半分。フレームレートを上げると機械的に減る
- 通信の物理的な下限は削れない。だから隠す仕組みが要る
- 入力遅延方式と予測・巻き戻し方式は、何を犠牲にするかの選択
- 巻き戻しには決定的なロジックが前提
- RTT は平均だけでなくジッタを見る
なお、この「16.7ミリ秒の攻防」を扱ったコラムのほか、競技を支えるハードウェア、AIはeスポーツをどう変えるか、チートとインテグリティといった技術寄りの記事が e-Sports TODAY に並んでいました。競技シーン側の文脈と合わせて読むと、どの遅延がどれだけ勝敗に効くのかの感覚がつかめます。
本ページはプロモーションが含まれています

