GPU

概要

MediaPipe は、GPU コンピューティングとレンダリングのための計算ノードをサポートし、複数の GPU ノードを結合したり、CPU ベースの計算ノードと混在させたりすることができます。モバイル プラットフォーム(OpenGL ES、Metal、Vulkan など)には、いくつかの GPU API があります。MediaPipe は、単一のクロス API GPU 抽象化は行いません。個々のノードはさまざまな API を使用して記述できるため、必要に応じてプラットフォーム固有の機能を利用できます。

モバイル プラットフォームで高いパフォーマンスを実現するには、GPU のサポートが不可欠です。特にリアルタイムの動画ではこの点が重要になります。MediaPipe を使用すると、デベロッパーは以下の目的で GPU の使用をサポートする GPU 対応計算ツールを作成できます。

  • バッチ処理だけでなく、デバイス上のリアルタイム処理
  • 分析だけでなく、動画のレンダリングと効果にも対応

MediaPipe での GPU サポートの設計原則は次のとおりです。

  • GPU ベースの計算ツールはグラフの任意の場所に配置でき、画面上のレンダリングに必ずしも使用する必要はありません。
  • GPU ベースの計算機から別の GPU 計算ツールへのフレームデータの転送は高速で、コピー操作のコストが高くならないようにする必要があります。
  • CPU と GPU 間のフレームデータの転送は、プラットフォームで可能な限り効率的である必要があります。
  • プラットフォームが異なれば最適なパフォーマンスを得るために必要な手法も異なるため、API はバックグラウンドで柔軟な実装を行えるようにする必要があります。
  • 計算ツールでは、そのすべてまたは一部に GPU を柔軟に使用でき、必要に応じて CPU と組み合わせることもできます。

OpenGL ES のサポート

MediaPipe は、Android/Linux でバージョン 3.2 まで、ES 3.0 までの OpenGL ES をサポートします。 利用できますまた、MediaPipe は iOS 版 Metal にも対応しています。

(Android/Linux システムの場合)を実行するには OpenGL ES 3.1 以降が必要 ML 推論計算ツールおよびグラフです

MediaPipe を使用すると、グラフを複数の GL コンテキストで OpenGL 実行できます。たとえば、 より低速な GPU 推論パス(例: 10 例: 高速の GPU レンダリング パス(例: 30 FPS): 1 つの GL コンテキストで 1 つのシーケンシャル コマンドキューに対応し、両方のコマンドに同じコンテキストを使用 レンダリングのフレームレートが下がります。

MediaPipe で複数のコンテキストを使用することで解決できる課題の一つは、 内部 IP アドレスを使用して通信できますこのシナリオの例としては、入力動画が レンダリング パスと推論パスの両方に送信されます。レンダリング プロセスでは、 最新の出力にアクセスできます。

OpenGL コンテキストに、複数のスレッドから同時にアクセスすることはできません。 さらに、同じスレッド上でアクティブな GL コンテキストを切り替えると、 一部の Android デバイスなどです。そのため Google のアプローチは、1 つの専用スレッドを 決定できます各スレッドが GL コマンドを発行し、シリアル コマンドキューを構築する 実行され、その後 GPU によって非同期に実行されます。

GPU 計算ツールのライフサイクル

このセクションでは、GPU の Process メソッドの基本構造について説明します。 基本クラス GlSimpleCalculator から派生した計算ツール。GPU 計算ツール LuminanceCalculator は例として示しています。メソッド LuminanceCalculator::GlRenderGlSimpleCalculator::Process から呼び出されます。

// Converts RGB images into luminance images, still stored in RGB format.
// See GlSimpleCalculator for inputs, outputs and input side packets.
class LuminanceCalculator : public GlSimpleCalculator {
 public:
  absl::Status GlSetup() override;
  absl::Status GlRender(const GlTexture& src,
                        const GlTexture& dst) override;
  absl::Status GlTeardown() override;

 private:
  GLuint program_ = 0;
  GLint frame_;
};
REGISTER_CALCULATOR(LuminanceCalculator);

absl::Status LuminanceCalculator::GlRender(const GlTexture& src,
                                           const GlTexture& dst) {
  static const GLfloat square_vertices[] = {
      -1.0f, -1.0f,  // bottom left
      1.0f,  -1.0f,  // bottom right
      -1.0f, 1.0f,   // top left
      1.0f,  1.0f,   // top right
  };
  static const GLfloat texture_vertices[] = {
      0.0f, 0.0f,  // bottom left
      1.0f, 0.0f,  // bottom right
      0.0f, 1.0f,  // top left
      1.0f, 1.0f,  // top right
  };

  // program
  glUseProgram(program_);
  glUniform1i(frame_, 1);

  // vertex storage
  GLuint vbo[2];
  glGenBuffers(2, vbo);
  GLuint vao;
  glGenVertexArrays(1, &vao);
  glBindVertexArray(vao);

  // vbo 0
  glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
  glBufferData(GL_ARRAY_BUFFER, 4 * 2 * sizeof(GLfloat), square_vertices,
               GL_STATIC_DRAW);
  glEnableVertexAttribArray(ATTRIB_VERTEX);
  glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, nullptr);

  // vbo 1
  glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
  glBufferData(GL_ARRAY_BUFFER, 4 * 2 * sizeof(GLfloat), texture_vertices,
               GL_STATIC_DRAW);
  glEnableVertexAttribArray(ATTRIB_TEXTURE_POSITION);
  glVertexAttribPointer(ATTRIB_TEXTURE_POSITION, 2, GL_FLOAT, 0, 0, nullptr);

  // draw
  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

  // cleanup
  glDisableVertexAttribArray(ATTRIB_VERTEX);
  glDisableVertexAttribArray(ATTRIB_TEXTURE_POSITION);
  glBindBuffer(GL_ARRAY_BUFFER, 0);
  glBindVertexArray(0);
  glDeleteVertexArrays(1, &vao);
  glDeleteBuffers(2, vbo);

  return absl::OkStatus();
}

上記の設計原則に基づき、次のような設計が実現しました。 MediaPipe GPU サポートの選択肢は次のとおりです。

  • 画像データを表すための GpuBuffer という GPU データ型があり、GPU の使用向けに最適化されています。このデータ型の正確な内容は不透明であり、プラットフォーム固有です。
  • コンポジションに基づく低レベル API。GPU を使用するすべての計算ツールで、GlCalculatorHelper クラスのインスタンスを作成して所有します。このクラスは、OpenGL コンテキストの管理、入出力のテクスチャの設定などを行うためのプラットフォームに依存しない API を提供します。
  • サブクラス化に基づく高レベル API。GlSimpleCalculator から画像フィルタのサブクラスを実装する単純な計算ツールは、特定の OpenGL コードでいくつかの仮想メソッドをオーバーライドするだけでよく、すべてのプラミングはスーパークラスが処理します。
  • すべての GPU ベースの計算ツールで共有する必要があるデータは、グラフサービスとして実装され、GlCalculatorHelper クラスによって管理される外部入力として提供されます。
  • 計算ツール固有のヘルパーと共有グラフ サービスを組み合わせることで、GPU リソースの管理を柔軟に行うことができます。たとえば、計算ツールごとに個別のコンテキストを用意したり、単一のコンテキストを共有したり、ロックやその他の同期プリミティブを共有したりできます。これらはすべてヘルパーによって管理され、個々の計算ツールからは隠されます。

GpuBuffer から ImageFrame へのコンバータ

GpuBufferToImageFrameCalculatorImageFrameToGpuBufferCalculator という 2 つの計算ツールが用意されています。これらの計算ツールは ImageFrameGpuBuffer の間で変換するため、GPU と CPU の計算ツールを組み合わせたグラフを構築できます。iOS と Android の両方でサポートされています。

可能な場合、これらの計算ツールはプラットフォーム固有の機能を使用して、コピーすることなく CPU と GPU の間でデータを共有します。

以下の図は、カメラから動画をキャプチャして MediaPipe グラフで実行し、出力をリアルタイムで画面にレンダリングするモバイルアプリのデータフローを示しています。破線は、MediaPipe グラフ内の適切な部分を示します。このアプリケーションは、OpenCV を使用して CPU で Canny エッジ検出フィルタを実行し、GPU を使用して元の動画の上にオーバーレイします。

GPU 計算ツールの相互作用

カメラからの動画フレームは、GpuBuffer パケットとしてグラフにフィードされます。「 2 つの計算機が並列でアクセスします。 GpuBufferToImageFrameCalculator はバッファを ImageFrame に変換します。 その後、グレースケール コンバータと Canny フィルタ(どちらも 実行され、OpenCV 上で実行されるなど)、その出力は また GpuBuffer です。多入力 GPU 計算ツール GlOverlayCalculator は、 元の GpuBuffer とエッジ検出器からの出力の両方を入力する。 シェーダーを使用してオーバーレイします。出力はバックエンドに送られて コールバック計算機能を使用して、アプリケーションで画像を 画面に表示されます