ハイライト
macOS Monterey は Adaptive-Sync モニターのサポートを導入、メタル ゲームは合格可能
presentAfterMinimumDurationアダプティブフレームレートを有効にします。 iPad ProのProMotionディスプレイで動作しますCADisplayLinkのtargetTimestampフレームレートが変化してもアニメーションを滑らかに保ちます。
主要内容
ほとんどの場合、ゲームは 120Hz モニター上で安定した 120fps で実行されます。ただし、シーンの複雑さが急増し、特定のフレームのレンダリングに 9 ミリ秒かかることがありました。固定リフレッシュ レート モニターでは、このフレームは次の 16.6 ミリ秒のリフレッシュ サイクルまで遅延し、ユーザーは明らかな遅延を経験します。 Adaptive-Sync モニタでは、このフレーム遅延はわずか 1ms で、ほとんど感知できません。
それが可変リフレッシュレートモニターの価値です。しかし、これを有効に活用するには、従来のフレーム レート制御の考え方を変える必要があります。
詳細
Adaptive-Sync モニターが macOS で動作する仕組み
(01:27)
固定リフレッシュ レート モニター (60Hz または 120Hz) は 16.6ms または 8.3ms ごとにリフレッシュされます。 GPU が新しいフレームの準備ができていない場合は、前のフレームが繰り返し表示されます。
Adaptive-Sync モニターの動作は異なります。各フレームが画面上に留まる時間は一定の間隔であり、固定値ではありません。たとえば、40 ~ 120 Hz の Adaptive-Sync モニターは、フレームごとに 8 ミリ秒から 25 ミリ秒の間画面上に留まります。
これはつまり:
- フレームが 9 ミリ秒で完了した場合、9 ミリ秒の直後に表示されるため、16.6 ミリ秒まで待つ必要はありません
- スタッタリングは 8 ミリ秒から 1 ミリ秒に短縮され、ユーザーはほとんど知覚できなくなりました。
(02:44)
キーポイント:
- Adaptive-Sync モニターの更新間隔は動的です
- フレームが完了した直後に表示されるため、次の固定リフレッシュ ポイントを待つ必要はありません。
- 最大滞留時間を超えると、表示を更新する必要があり、一時的に使用できなくなります。
Adaptive-Sync 最佳实践
(04:18)
固定リフレッシュ レート モニターで、GPU の作業がリフレッシュ間隔を超えて継続する場合、Apple はフレーム レートを次の係数で下げることを推奨します (例: 60fps から 30fps)。
Adaptive-Sync モニターでは、このアドバイスは変わります。アプリケーションが確実に達成できる最高の均一フレーム レートでフレームをレンダリングする必要があります。
// 检测 Adaptive-Sync 是否可用
let screen = window.screen
let isAdaptiveSync = screen.minimumRefreshInterval != screen.maximumRefreshInterval
let isFullScreen = window.styleMask.contains(.fullScreen)
if isAdaptiveSync && isFullScreen {
// Adaptive-Sync フレーム レート制御を有効にする
}
(05:51)
キーポイント:
minimumRefreshIntervalそしてmaximumRefreshIntervalNSScreenの新しいプロパティです- 两个值不相等表示 Adaptive-Sync 模式
- ウィンドウが全画面表示かどうかも確認する必要があります
- 只在全屏模式下才能使用 Adaptive-Sync 调度
presentAfterMinimumDuration を使用してフレーム レートを制御する
(06:51)
最も簡単な方法は使用することですpresentAfterMinimumDuration固定フレームレート:
let desiredInterval: CFTimeInterval = 1.0 / 78.0 // 目標 78Hz
// レンダリングループ内
drawable.present(afterMinimumDuration: desiredInterval)
(07:11)
キーポイント:
presentAfterMinimumDuration指定された間隔後にフレームがレンダリングされるようにする- ユーザーが調整可能な FPS スライダーの実装に適しています
- フレームレートは安定していますが、最適ではない可能性があります
より高度なアプローチは、フレーム レートを動的に計算することです。
var averageGPUTime: CFTimeInterval = 1.0 / 120.0 // 初始乐观估计
// 第一帧:用乐观估计
let firstDrawable = getDrawable()
let commandBuffer = encodeWork()
commandBuffer.addCompletedHandler { _ in
let gpuTime = CACurrentMediaTime() - frameStartTime
// 滚动平均
averageGPUTime = 0.9 * averageGPUTime + 0.1 * gpuTime
}
commandBuffer.commit()
firstDrawable.present(afterMinimumDuration: averageGPUTime)
(08:44)
キーポイント:
- 使用
CommandBuffer完成したハンドラーは GPU 時間を測定します - ローリング平均によりフレーム レートの変化が滑らかになります
- 同じコード セットにより、パフォーマンスが異なる Mac 上のフレーム レートを自動的に調整します。
- 弱い Mac では 48 Hz、強い Mac では 78 Hz まで動作する場合があります。
ProMotion 和 CADisplayLink 最佳实践
(10:22)
iPad Pro の ProMotion ディスプレイは最大 120Hz をサポートしますが、次の状況では使用できない場合があります。
- ユーザーが低電力モードをオンにしました (iPadOS 15 の新機能)
- ユーザーはアクセシビリティで制限フレーム レートを設定します (60 Hz に制限)
- デバイスが過熱し、システムは 120Hz に制限されています
CADisplayLinkは、カスタム描画を実行するための推奨ツールです。 vsync ごとに起動しますが、これは通常よりも優れていますNSTimerより正確に。
// CADisplayLink のセットアップ
displayLink = CADisplayLink(target: self, selector: #selector(draw))
displayLink.preferredFramesPerSecond = 40
displayLink.add(to: .current, forMode: .common)
// 在回调中
@objc func draw() {
let interval = displayLink.targetTimestamp - displayLink.timestamp
// 間隔は実際に利用可能なレンダリング時間です
}
(15:37)
キーポイント:
preferredFramesPerSecond単なる提案です。実際のフレーム レートはシステムによって決定されます。- 60Hz モニターで 40Hz を要求すると、システムは自動的に 30Hz に調整します。
durationプロパティは現在の実際のフレーム間隔を反映します- 用
targetTimestamp而不是timestamp准备动画
タイムスタンプの代わりに targetTimestamp を使用する理由
(17:46)
フレームレートが 120Hz から 60Hz に低下した場合、timestampアニメーションの進行状況をサンプリングすると、ジャンプが発生します。
// タイムスタンプを使用: フレーム レートが切り替わるとアニメーションの進行状況がジャンプします。
// 帧1 (120Hz): timestamp=0, progress=0
// 帧2 (120Hz): timestamp=8ms, progress=0.05
// フレーム 3 (60Hz): タイムスタンプ = 16 ミリ秒、進行状況 = 0.10 <- ここにジャンプしてください。
用 targetTimestamp 则平滑:
// targetTimestamp を使用: フレーム レートを切り替えるときにアニメーションを滑らかにします
// 帧1 (120Hz): targetTimestamp=8ms, progress=0.05
// 帧2 (120Hz): targetTimestamp=16ms, progress=0.10
// フレーム 3 (60Hz): targetTimestamp=32ms、progress=0.20 <- スムーズな移行
(19:26)
キーポイント:
timestampコールバックが呼び出される時刻ですtargetTimestamp次のフレームが合成される時間です- 使用
targetTimestampサンプリング アニメーション、フレーム レートが変化してもジャンプはありません - コードの変更は非常に簡単です。
timestampと置き換えますtargetTimestamp
不在のコールバックの処理
(20:16)
優先度の高いスレッドまたはビジーな実行ループにより、CADisplayLinkコールバックが遅延またはスキップされます。
@objc func draw() {
let currentTime = CACurrentMediaTime()
let timeRemaining = displayLink.targetTimestamp - currentTime
if timeRemaining < minimumWorkTime {
// 時間が足りません。作業負荷を減らすか、このフレームをスキップしてください
return
}
// 前のフレームの targetTimestamp を使用して正しい時間増分を計算します。
let delta = displayLink.targetTimestamp - previousTargetTimestamp
previousTargetTimestamp = displayLink.targetTimestamp
// デルタを使用してアニメーションの状態を更新する
updateAnimation(delta: delta)
}
(21:48)
キーポイント:
- 使用
CACurrentMediaTime()残り時間を確認する - 予約する
previousTargetTimestamp正しい時間増分を計算する - コールバックがスキップされると、デルタは自動的にフレーム間隔の 2 倍になります
- 动画不会减速或加速
重要ポイント
-
メタル ゲーム用の Adaptive-Sync サポートを追加。検出
minimumRefreshInterval != maximumRefreshIntervalまた、ウィンドウが全画面の場合は、ローリング平均 GPU 時間を使用してフレーム レートを動的に制御します。入口API:NSScreen.minimumRefreshInterval+MTLDrawable.present(afterMinimumDuration:)。 -
CADisplayLink のすべてのタイムスタンプを targetTimestamp に置き換えます。これは、フレーム レートを切り替えるときにアニメーションのジャンプを回避する唯一の正しい方法です。入口API:
CADisplayLink.targetTimestamp。 -
カスタム アニメーション用のフレーム レート低下ロジックを追加。いつ
timeRemaining < minimumWorkTimeワークロードを軽減するかフレームをスキップして、フレームが失われないようにします。入口API:CACurrentMediaTime()+displayLink.targetTimestamp。 -
iPad アプリで ProMotion が利用できるかどうかを検出します。使用
CADisplayLink.duration実際のフレーム間隔を取得します。使用しません。UIScreen.maximumFramesPerSecond(常に 120 が返されます)。入口API:CADisplayLink.duration。 -
ユーザーが調整可能な FPS キャップをゲームに追加します。使用
presentAfterMinimumDurationフレーム レートを固定し、GPU の電力消費と発熱を削減します。入口API:MTLDrawable.present(afterMinimumDuration:)。
関連セッション
- Optimize high-end games for Apple GPUs— Apple GPU TBDR アーキテクチャ ゲームの最適化
- Enhance your app with Metal ray tracing— メタル レイ トレーシング API
- Create image processing apps powered by Apple silicon— Apple Silicon画像処理の最適化
- Discover Metal debugging, profiling, and asset creation tools — Xcode 13 の Metal デバッグとパフォーマンス分析ツール
コメント
GitHub Issues · utterances