フレーム ペーシング

Swappy という名前でも知られる Android Frame Pacing ライブラリは、Android Game SDK の一部です。このライブラリは、OpenGLVulkan のゲームが Android でスムーズなレンダリングと正確なフレーム ペーシングを行えるようにサポートします。

フレーム ペーシングとは、ゲームのロジックおよびレンダリング ループを、OS のディスプレイ サブシステムおよび基盤となるディスプレイ ハードウェアと同期させることです。Android ディスプレイ サブシステムは、テアリングなどの特定の視覚的アーティファクトを回避するように設計されています。ディスプレイ サブシステムは次の処理を行って、テアリングを回避します。

  • 過去のフレームを内部的にバッファリングする
  • 遅延しているフレーム送信を検出する
  • 遅延フレームが検出されたときに現在のフレームを表示し続ける

フレームの表示時間の不整合は、ゲームのレンダリング ループがネイティブ ディスプレイ ハードウェアがサポートするものとは異なるレートで実行されることにより発生します。基盤となるディスプレイ ハードウェアに対してゲームのレンダリング ループが遅すぎる場合に問題が発生し、これによって表示時間の不整合が引き起こされます。たとえば 30 FPS で実行されるゲームが、ネイティブに 60 FPS をサポートするデバイスでレンダリングを実行しようとすると、ゲームのレンダリング ループによって、繰り返されるフレームが 16 ミリ秒間余計に画面上に残ります。このタイプの接続解除では、33 ミリ秒、16 ミリ秒、49 ミリ秒などのフレーム時間の間に大きな不整合が生じます。極度に複雑なシーンはフレームの欠落を生じさせるため、この問題をさらに複雑にします。

Frame Pacing ライブラリでは次のタスクを実行します。

  • ゲームのフレームが短いため、スタッタリングを補正します。
    • 表示タイムスタンプを追加することで、フレームが早すぎることなく、時間通りに表示されるようにします。
    • 表示タイムスタンプの拡張機能である EGL_ANDROID_presentation_timeVK_GOOGLE_display_timing を使用します。
  • スタッタリングとレイテンシを引き起こす長いフレームに同期フェンスを使用します。
    • 待機をアプリに注入します。これにより、バック プレッシャーを増大させるのではなく、ディスプレイ パイプラインが追いつけるようにします。
    • 同期フェンス(EGL_KHR_fence_syncVkFence)を使用します。
  • デバイスが複数のリフレッシュ レートをサポートしている場合は、リフレッシュ レートを選択することでより柔軟でスムーズな表示が可能になります。
  • フレーム統計情報を使用してデバッグとプロファイリングを行うための統計情報を提供します。

ニーズに応じて異なるモードで動作するようにライブラリを構成する方法については、サポートされている動作モードをご覧ください。

OpenGL レンダラまたは Vulkan レンダラを使用して実装する方法については、以下をご覧ください。

フレーム ペーシングを適切に行うで詳細をご覧ください。

フレーム/秒スロットリング介入

フレーム/秒(FPS)スロットリング介入では、プラットフォーム側の変更のみで、ゲームのペースを適切な FPS に調整できます。デベロッパーが対応する必要はありません。

FPS スロットリング介入の実装で使用するコンポーネントは次のとおりです。

GameManagerService

GameManagerService コンポーネントは、ゲームモードとゲーム介入に関する、ユーザーごとの情報とゲームごとの情報をすべて保持します。FPS 情報は、他の介入情報(解像度のダウンスケーリング ファクタなど)とともに、各ユーザー プロファイルの <PACKAGE_NAME, Interventions> マッピングで GameManagerService に格納されます。FPS 情報へのアクセスは、ゲームモードが変更されたとき、または介入が更新されたときに行われます。UIDPACKAGE_NAME とユーザーごとに一意であり、さらに <UID, Frame Rate> ペアに変換して SurfaceFlinger に送ることができます。

SurfaceFlinger

SurfaceFlinger コンポーネントは、フレームレートがディスプレイのリフレッシュ レートの除数である限り、すでにアプリの FPS スロットリングをサポートしています。vsync が発生した場合、SurfaceFlinger は、vsync のタイムスタンプがアプリのフレームレートと同位相であるかどうかを確認することで、スロットリングされたアプリに対する vsync の有効性をチェックします。フレームレートが vsync と同位相でない場合、SurfaceFlinger はフレームレートと vsync が同位相になるまでフレームを保持します。

GameManagerService と SurfaceFlinger の間のインタラクションを下図に示します。

GameManagerService と SurfaceFlinger の間のインタラクション

図 1. GameServiceManager と SurfaceFlinger の間のインタラクション

SurfaceFinger は <UID, Frame Rate> ペアマッピングを保持して、新しいフレームレート スロットリング優先度を設定します。UID はユーザーとゲームの間で一意であるため、1 つのデバイスのユーザーごとに、同じゲームで異なるフレームレートを設定できます。ゲームのフレームレートをスロットリングするために、GameServiceManager は SurfaceFlinger を呼び出して UID のフレームレートをオーバーライドします。このメカニズムにより、SurfaceFlinger はゲームモードが変更されるか介入が更新されるたびにマッピングを更新します。それに応じて SurfaceFlinger はバッファをラッチすることで FPS の変更を処理します。

FPS スロットリングについて詳しくは、FPS スロットリングの概要をご覧ください。