BufferQueue と Gralloc

BufferQueue クラスは、グラフィカル データのバッファを生成するコンポーネント(プロデューサー)と、ディスプレイまたは追加処理のデータを受け入れるコンポーネント(消費者)を接続します。グラフィカル データのバッファをシステム内で移動させるほとんどの場合で BufferQueue が必要です。

Gralloc のメモリ アロケータはバッファの割り当てを実行し、2 つのベンダー固有の HIDL インターフェースで実装されます(hardware/interfaces/graphics/allocator/hardware/interfaces/graphics/mapper/ をご覧ください)。allocate() 関数は、推定される引数(幅、高さ、ピクセル形式)と一連の使用フラグを取得します。

BufferQueue のプロデューサーと消費者

消費者は、BufferQueue のデータ構造を作成して所有し、プロデューサーとは異なるプロセスに存在する可能性があります。プロデューサーがバッファを必要とする場合、dequeueBuffer() を呼び出して BufferQueue からフリーのバッファをリクエストし、バッファの幅、高さ、ピクセル形式、使用フラグを指定します。次に、プロデューサーはバッファを入力し、queueBuffer() を呼び出してそのバッファをキューに返します。続いて、消費者は acquireBuffer() でバッファを取得し、バッファのコンテンツを利用します。消費者が完了すると、releaseBuffer() を呼び出してキューにバッファを返します。同期フレームワークは、バッファが Android のグラフィックス パイプラインをどのように移動するかを管理します。

保持できるバッファの最大数などの BufferQueue の特性は、プロデューサーと消費者によって共同で決定されます。ただし、BufferQueue はバッファを必要に応じて割り当てます。特性が変わらない限り、バッファは保持されます。たとえば、プロデューサーが異なるサイズのバッファを要求した場合、古いバッファは解放され、新しいバッファがオンデマンドで割り当てられます。

多くのデータを移動させることは非効率的であるため、BufferQueue がバッファの内容をコピーすることはありません。代わりに、ハンドルが常にバッファを渡します。

Systrace を使用した BufferQueue のトラッキング

グラフィックのバッファの動きを把握するには、短時間のデバイス アクティビティを記録する Systrace というツールを使用します。システムレベルのグラフィックのコードは、関連するアプリのフレームワークのコードのほとんどがそうであるように、十分にインストゥルメント化されています。

Systrace を使用するには、gfx タグ、view タグ、sched タグを有効化します。BufferQueue オブジェクトはトレースに表示されます。例として、Grafika の Play 動画(SurfaceView)の実行中にトレースを取得する場合、SurfaceView とラベル付けされた行が、任意の時点でキューされたバッファの数を示します。

この値はアプリがアクティブな間に増加し、MediaCodec デコーダによるフレームのレンダリングをトリガーします。この値は、SurfaceFlinger が動作し、バッファを消費している間に減少します。30 fps で動画を表示する場合、60 fps までのディスプレイはソースに対応できるため、キューの値は 0 から 1 の範囲で変化します。SurfaceFlinger は 1 秒あたり 60 回ではなく、実行する作業がある場合にのみ起動します。画面が更新されていない場合には、システムは作業の回避を試み、VSYNC を無効にします。

Grafika の Play 動画(TextureView)に切り替えて、新しいトレースを取得すると、com.android.grafika または com.android.grafika.PlayMovieActivity とラベル付けされた行が表示されます。これはメインの UI レイヤで、もう 1 つの BufferQueue です。TextureView は別レイヤではなく UI レイヤにレンダリングされるため、すべての動画主導の更新はここに表示されます。

Gralloc

Gralloc のアロケータ HAL hardware/libhardware/include/hardware/gralloc.h は、使用フラグを通してバッファの割り当てを実行します。使用フラグには、次のような属性が含まれます。

  • メモリがソフトウェア(CPU)からアクセスされる頻度
  • メモリがハードウェア(GPU)からアクセスされる頻度
  • メモリが OpenGL ES(GLES)テクスチャとして使用されるかどうか
  • メモリが動画エンコーダで使用されるかどうか

たとえば、プロデューサーのバッファ形式により RGBA_8888 ピクセルが指定されており、そのバッファがソフトウェアからアクセスされる(つまり、アプリが CPU のピクセルにタッチする)ことをプロデューサーが示している場合、Gralloc は、R-G-B-A の順に 1 ピクセルあたり 4 バイトのバッファを作成します。代わりに、プロデューサーがそのバッファにはハードウェアから GLES テクスチャとしてのみアクセスされるように指定した場合、Gralloc は、BGRA の順序付け、非線形のスウィズルのレイアウト、代替色の形式など、GLES ドライバの意図するさまざまな処理を行うことができます。その推奨される形式の使用をハードウェアに許可することで、パフォーマンスの向上につながります。

プラットフォームによっては、一部の値を組み合わせることができません。たとえば、動画エンコーダのフラグに YUV ピクセルが必要になる可能性があるため、ソフトウェアのアクセスの追加と RGBA_8888 の指定が失敗します。

Gralloc から返されたハンドルは、Binder を介してプロセス間で渡すことができます。

保護されたバッファ

Gralloc の使用フラグ GRALLOC_USAGE_PROTECTED により、グラフィックス バッファはハードウェアで保護されたパスを介してのみ表示されるようになります。これらのオーバーレイ プレーンは、DRM コンテンツを表示する唯一の方法です(DRM で保護されたバッファには SurfaceFlinger や OpenGL ES ドライバからはアクセスできません)。

DRM で保護された動画は、オーバーレイ プレーンでのみ表示できます。保護されたコンテンツをサポートする動画プレーヤーは、SurfaceView で実装する必要があります。保護されていないハードウェア上で動作するソフトウェアは、バッファの読み取りや書き込みを行えません。ハードウェアで保護されたパスは Hardware Composer のオーバーレイに表示されている必要があります。つまり、保護された動画は、Hardware Composer が OpenGL ES コンポジションに切り替わるとディスプレイから表示されなくなります。

保護されたコンテンツの詳細については、DRM をご覧ください。