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 di platform seluler (misalnya, OpenGL ES, Metal, dan Vulkan). MediaPipe tidak berupaya menawarkan abstraksi GPU lintas API tunggal. Setiap node dapat ditulis menggunakan API yang berbeda, sehingga dapat memanfaatkan fitur khusus platform saat diperlukan.

Dukungan GPU sangat penting untuk performa yang baik pada 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 saja dalam grafik, dan tidak harus digunakan untuk rendering di layar.
  • Transfer data frame dari satu kalkulator berbasis GPU ke kalkulator lainnya seharusnya cepat, dan tidak menimbulkan operasi penyalinan yang mahal.
  • Transfer data frame antara CPU dan GPU harus seefisien yang dimungkinkan platform.
  • Karena platform yang berbeda mungkin memerlukan teknik yang berbeda untuk performa terbaik, API harus memungkinkan fleksibilitas dalam cara implementasi berbagai hal di balik layar.
  • Kalkulator harus diizinkan untuk mencapai fleksibilitas maksimum dalam menggunakan GPU untuk semua atau sebagian dari operasinya, yang 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.

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

MediaPipe memungkinkan grafik menjalankan OpenGL dalam berbagai konteks GL. Misalnya, bisa sangat berguna dalam grafik yang menggabungkan jalur inferensi GPU yang lebih lambat (misalnya, di 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 jenis tugas akan mengurangi kecepatan frame rendering.

Salah satu tantangan dalam penggunaan berbagai konteks oleh MediaPipe adalah kemampuan untuk berkomunikasi dengan mereka. Contoh skenario adalah dengan video input yang dikirim ke jalur rendering maupun inferensi, dan rendering harus memiliki akses ke {i>output <i}terbaru dari inferensi.

Konteks OpenGL tidak dapat diakses oleh beberapa thread sekaligus. Selain itu, mengalihkan konteks GL aktif pada thread yang sama dapat memperlambat beberapa perangkat Android. Oleh karena itu, pendekatan kita adalah memiliki satu thread khusus dengan ukuran unit maksimum 1 MB per konteks. Setiap thread mengeluarkan perintah GL, membangun antrean perintah serial sesuai konteksnya, yang kemudian dieksekusi oleh GPU secara asinkron.

Masa Pakai Kalkulator GPU

Bagian ini menyajikan struktur dasar metode Proses dari GPU kalkulator yang berasal dari class dasar GlSimpleCalculator. 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-prinsip desain yang disebutkan di atas telah menghasilkan desain berikut pilihan dukungan GPU MediaPipe:

  • Kami memiliki jenis data GPU, yang disebut GpuBuffer, untuk mewakili data gambar, yang dioptimalkan untuk penggunaan GPU. Konten persis jenis data ini buram dan spesifik per platform.
  • API tingkat rendah berdasarkan komposisi, tempat kalkulator apa pun yang ingin menggunakan GPU 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 berdasarkan subclass, dengan kalkulator sederhana yang menerapkan subclass filter gambar dari GlSimpleCalculator dan hanya perlu mengganti beberapa metode virtual dengan kode OpenGL spesifiknya, sementara superclass menangani semua proses lanjutan.
  • 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 kami mengelola sumber daya GPU secara fleksibel: kami dapat memiliki konteks terpisah per kalkulator, berbagi satu konteks, berbagi kunci atau primitif sinkronisasi lainnya, dll. -- dan semua ini dikelola oleh helper dan tersembunyi dari masing-masing kalkulator.

GpuBuffer ke Pengonversi ImageFrame

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

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

Diagram di bawah menunjukkan aliran data dalam 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 sudah benar berada dalam grafik MediaPipe. Aplikasi ini menjalankan filter deteksi tepi Canny pada CPU menggunakan OpenCV, dan menempatkan filter di atas video asli menggunakan GPU.

Cara kalkulator GPU berinteraksi

Frame video dari kamera dimasukkan ke dalam grafik sebagai paket GpuBuffer. Tujuan aliran input diakses oleh dua kalkulator secara paralel. GpuBufferToImageFrameCalculator mengonversi buffer menjadi ImageFrame, yang kemudian dikirim melalui pengonversi {i>grayscale<i} dan filter bagus (keduanya berbasis pada OpenCV dan berjalan di CPU), yang {i>outputnya<i} yang kemudian dikonversi menjadi GpuBuffer lagi. Kalkulator GPU multi-input, GlOverlayCalculator, memerlukan memasukkan GpuBuffer asli dan yang keluar dari detektor tepi, dan menempatkan overlay menggunakan shader. Output-nya kemudian dikirim kembali ke aplikasi menggunakan kalkulator callback, dan aplikasi akan merender gambar ke layar menggunakan OpenGL.