GPU

Ringkasan

MediaPipe mendukung node kalkulator untuk komputasi dan rendering GPU, dan memungkinkan penggabungan beberapa node GPU, serta mencampurnya dengan node kalkulator berbasis CPU. Ada beberapa API GPU pada platform seluler (misalnya, OpenGL ES, Metal, dan Vulkan). MediaPipe tidak mencoba menawarkan abstraksi GPU lintas-API tunggal. Tiap node dapat ditulis menggunakan API yang berbeda, yang memungkinkannya untuk memanfaatkan fitur khusus platform bila diperlukan.

Dukungan GPU sangat penting untuk mendapatkan performa yang baik di platform seluler, terutama untuk video real-time. MediaPipe memungkinkan developer menulis kalkulator yang kompatibel dengan GPU yang mendukung penggunaan GPU untuk:

  • Pemrosesan real-time di perangkat, bukan hanya batch processing
  • Rendering dan efek video, bukan hanya analisis

Berikut adalah prinsip desain untuk dukungan GPU di MediaPipe

  • Kalkulator berbasis GPU harus dapat muncul di mana pun dalam grafik, dan belum tentu digunakan untuk rendering di layar.
  • Transfer data frame dari satu kalkulator berbasis GPU ke kalkulator lainnya harus cepat, dan tidak menimbulkan operasi penyalinan yang mahal.
  • Transfer data frame antara CPU dan GPU harus seefisien yang dimungkinkan oleh platform.
  • Karena platform yang berbeda mungkin memerlukan teknik yang berbeda untuk mendapatkan performa terbaik, API harus memungkinkan fleksibilitas dalam cara mengimplementasikan segala sesuatu di balik layar.
  • Kalkulator harus diberikan fleksibilitas maksimum dalam menggunakan GPU untuk semua atau sebagian operasinya, menggabungkannya dengan CPU jika perlu.

Dukungan OpenGL ES

MediaPipe mendukung OpenGL ES hingga versi 3.2 di Android/Linux dan hingga ES 3.0 di iOS. Selain itu, MediaPipe juga mendukung Metal di iOS.

OpenGL ES 3.1 atau yang lebih tinggi diperlukan (di sistem Android/Linux) untuk menjalankan grafik dan kalkulator inferensi machine learning.

MediaPipe memungkinkan grafik untuk menjalankan OpenGL dalam beberapa konteks GL. Misalnya, cara ini dapat sangat berguna dalam grafik yang menggabungkan jalur inferensi GPU yang lebih lambat (misalnya, pada 10 FPS) dengan jalur rendering GPU yang lebih cepat (misalnya, pada 30 FPS): karena satu konteks GL sesuai dengan satu antrean perintah berurutan, menggunakan konteks yang sama untuk kedua tugas akan mengurangi kecepatan frame rendering.

Salah satu tantangan yang dipecahkan melalui penggunaan berbagai konteks oleh MediaPipe adalah kemampuan untuk berkomunikasi dengan konteks tersebut. Contoh skenario adalah skenario dengan video input yang dikirim ke jalur rendering dan inferensi, dan rendering harus memiliki akses ke output terbaru dari inferensi.

Konteks OpenGL tidak dapat diakses oleh beberapa thread secara bersamaan. Selain itu, pengalihan konteks GL aktif di thread yang sama dapat menjadi lambat di beberapa perangkat Android. Oleh karena itu, pendekatan kami adalah memiliki satu thread khusus per konteks. Setiap thread mengeluarkan perintah GL, yang membentuk antrean perintah serial pada konteksnya, yang kemudian dieksekusi oleh GPU secara asinkron.

Masa Pakai Kalkulator GPU

Bagian ini menampilkan struktur dasar metode Proses kalkulator GPU yang berasal dari GlSimpleCalculator class dasar. Kalkulator GPU LuminanceCalculator ditampilkan sebagai contoh. Metode LuminanceCalculator::GlRender dipanggil dari GlSimpleCalculator::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();
}

Prinsip desain yang disebutkan di atas telah menghasilkan pilihan desain berikut untuk dukungan GPU MediaPipe:

  • Kami memiliki jenis data GPU, yang disebut GpuBuffer, untuk merepresentasikan data gambar, dan dioptimalkan untuk penggunaan GPU. Konten persis dari jenis data ini buram dan khusus platform.
  • API level rendah berdasarkan komposisi, ketika kalkulator apa pun yang ingin menggunakan GPU akan membuat dan memiliki instance class GlCalculatorHelper. Class ini menawarkan API yang tidak bergantung pada platform untuk mengelola konteks OpenGL, menyiapkan tekstur untuk input dan output, dll.
  • API tingkat tinggi yang didasarkan pada pembuatan subclass, dengan kalkulator sederhana yang mengimplementasikan subclass filter gambar dari GlSimpleCalculator dan hanya perlu mengganti beberapa metode virtual dengan kode OpenGL spesifiknya, sedangkan superclass menangani semua pemipaan.
  • Data yang perlu dibagikan di antara semua kalkulator berbasis GPU disediakan sebagai input eksternal yang diimplementasikan sebagai layanan grafik dan dikelola oleh class GlCalculatorHelper.
  • Kombinasi helper khusus kalkulator dan layanan grafik bersama memungkinkan kita mengelola resource GPU dengan fleksibel: kita dapat memiliki konteks terpisah untuk setiap kalkulator, berbagi konteks tunggal, berbagi kunci atau proses sinkronisasi primitif lainnya, dll. -- dan semua ini dikelola oleh helper dan disembunyikan dari kalkulator individual.

Pengonversi GpuBuffer ke ImageFrame

Kami menyediakan dua kalkulator bernama GpuBufferToImageFrameCalculator dan ImageFrameToGpuBufferCalculator. Kalkulator ini mengonversi antara ImageFrame dan GpuBuffer, yang memungkinkan konstruksi grafik yang menggabungkan kalkulator GPU dan CPU. Fitur ini didukung di iOS dan Android

Jika memungkinkan, kalkulator ini menggunakan fungsionalitas khusus platform untuk berbagi data antara CPU dan GPU tanpa menyalin.

Diagram di bawah menunjukkan aliran data di aplikasi seluler yang merekam video dari kamera, menjalankannya melalui grafik MediaPipe, dan merender output di layar secara real time. Garis putus-putus menunjukkan bagian mana yang berada dalam grafik MediaPipe yang tepat. Aplikasi ini menjalankan filter deteksi tepi Canny pada CPU menggunakan OpenCV, dan menempatkan filter tersebut di atas video asli menggunakan GPU.

Cara kalkulator GPU berinteraksi

Frame video dari kamera dimasukkan ke dalam grafik sebagai paket GpuBuffer. Aliran input diakses oleh dua kalkulator secara paralel. GpuBufferToImageFrameCalculator mengonversi buffer menjadi ImageFrame, yang kemudian dikirim melalui konverter hitam putih dan filter canny (keduanya didasarkan pada OpenCV dan berjalan di CPU), yang outputnya kemudian dikonversi menjadi GpuBuffer lagi. Kalkulator GPU multi-input, GlOverlayCalculator, mengambil input GpuBuffer yang asli dan yang keluar dari detektor tepi, lalu menempatkannya menggunakan shader. Output-nya kemudian dikirim kembali ke aplikasi menggunakan kalkulator callback, dan aplikasi merender gambar ke layar menggunakan OpenGL.