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 で動画を表示する場合、キューの値は 0 から 1 までの範囲で変化します。これは、最大で 60 fps の表示速度までであればソースに対応できるためです。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 は、GLES ドライバの意図するあらゆる処理(BGRA の順序付け、非線形スウィズルド レイアウト、代替色形式など)を行えます。ハードウェアにそのハードウェアの推奨フォーマットの使用を許可すると、パフォーマンスを向上できます。

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

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

保護されたバッファ

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

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

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