Compiler Shader Vulkan di Android

Aplikasi Vulkan harus mengelola shader dengan cara yang berbeda dengan aplikasi OpenGL ES: Di OpenGL ES, Anda menyediakan shader sebagai sekumpulan string yang membentuk teks sumber untuk suatu program shader GLSL. Sebaliknya, Vulkan API mengharuskan Anda menyediakan shader dalam bentuk titik masuk dalam modul SPIR-V.

NDK Rilis 12 dan yang lebih baru menyertakan library waktu proses untuk mengompilasi GLSL ke dalam SPIR-V. Library waktu proses ini sama dengan yang ada di project open source Shaderc, dan menggunakan compiler referensi Glslang GLSL yang sama dengan backend-nya. Secara default, versi Shaderc compiler menganggap Anda mengompilasi untuk Vulkan. Setelah memastikan validitas kode Anda untuk Vulkan, compiler otomatis mengaktifkan ekstensi KHR_vulkan_glsl. Versi Shaderc compiler juga menghasilkan kode SPIR-V yang sesuai dengan Vulkan.

Anda dapat memilih untuk mengompilasi modul SPIR-V ke dalam aplikasi Vulkan selama tahap pengembangan, melalui praktik yang disebut kompilasi ahead-of-time, atau AOT. Atau, Anda dapat meminta aplikasi agar mengompilasi modul dari sumber shader yang dikirim atau dihasilkan sesuai prosedur saat diperlukan selama waktu proses. Praktik ini disebut kompilasi waktu proses. Android Studio memiliki dukungan terintegrasi untuk membuat shader Vulkan.

Bagian selanjutnya di halaman ini menjelaskan lebih lanjut setiap praktik di atas, lalu menjelaskan cara mengintegrasikan kompilasi shader ke dalam aplikasi Vulkan.

Kompilasi AOT

Ada dua cara untuk melakukan kompilasi AOT shader, yang dijelaskan di bagian berikut.

Menggunakan Android Studio

Dengan menempatkan shader ke dalam app/src/main/shaders/, Android Studio akan mengenali shader berdasarkan ekstensi filenya, dan akan menyelesaikan tindakan berikut:

  • Mengompilasi semua file shader secara rekursif pada direktori itu.
  • Menambahkan akhiran .spv ke file shader SPIR-V yang telah dikompilasi.
  • Memaketkan shader SPIRV ke dalam direktori assets/shaders/ APK.

Aplikasi akan memuat shader yang dikompilasi dari lokasi assets/shaders/ yang sesuai pada waktu proses; struktur file shader spv yang dikompilasi sama dengan struktur file shader GLSL aplikasi di app/src/main/shaders/:

AAsset* file = AAssetManager_open(assetManager,
                     "shaders/tri.vert.spv", AASSET_MODE_BUFFER);
size_t fileLength = AAsset_getLength(file);
char* fileContent = new char[fileLength];
AAsset_read(file, fileContent, fileLength);

Flag kompilasi Shaderc dapat dikonfigurasi dalam blok shaders DSL gradle, seperti yang ditunjukkan dalam contoh berikut:

android {
  defaultConfig {
    shaders {
      glslcArgs.addAll(['-c', '-g'])
      scopedArgs.create('lights') {
        glslcArgs.addAll(['-DLIGHT1=1', '-DLIGHT2=0'])
      }
    }
  }
}

glslcArgs berlaku untuk semua kompilasi shader; scopedArgs hanya berlaku saat mengompilasi untuk cakupan tersebut. Contoh di atas membuat argumen cakupan lights, yang hanya akan berlaku untuk shader GLSL dalam direktori app/src/main/shaders/lights/. Lihat glslc untuk daftar lengkap flag kompilasi yang tersedia. Harap diingat bahwa Shaderc di dalam NDK adalah ringkasan dari repositori github pada saat NDK dirilis; Anda dapat memperoleh flag yang didukung secara persis untuk versi tersebut dengan perintah glslc --help, seperti dijelaskan pada bagian selanjutnya.

Kompilasi Command Line Offline

Shader GLSL dapat dikompilasi ke SPIR-V tanpa bergantung pada aplikasi utama menggunakan compiler command line glslc. NDK rilis 12 dan yang lebih baru mengemas versi glslc yang sudah di-build dan alat yang terkait dalam direktori <android-ndk-dir>/shader-tools/ untuk mendukung model penggunaan ini.

Compiler ini juga tersedia dari project Shaderc; ikuti petunjuk di sana untuk mem-build versi biner.

glslc menyediakan rangkaian lengkap opsi command line untuk kompilasi shader guna memenuhi berbagai persyaratan aplikasi.

Alat glslc mengompilasi file sumber tunggal ke modul SPIR-V dengan titik masuk shader tunggal. Secara default, file output memiliki nama yang sama dengan file sumber, tetapi dengan tambahan ekstensi .spv.

Ekstensi nama file digunakan untuk memberi tahu alat glslc tahap shader grafis mana yang akan dikompilasi, atau apakah shader komputasi sedang dikompilasi. Untuk informasi tentang cara menggunakan ekstensi nama file ini, dan opsi yang dapat Anda gunakan dengan alat tersebut, lihat Spesifikasi tahap Shader dalam panduan glslc.

Kompilasi Waktu Proses

Untuk kompilasi JIT shader selama waktu proses, NDK menyediakan library libshaderc, yang memiliki C dan C++ API.

Aplikasi C++ harus menggunakan C++ API. Kami merekomendasikan agar aplikasi dalam bahasa lain menggunakan C API, karena C ABI adalah level yang lebih rendah, dan cenderung memberikan stabilitas yang lebih baik.

Contoh berikut menunjukkan cara menggunakan C++ API:

#include <iostream>
#include <string>
#include <vector>
#include <shaderc/shaderc.hpp>

std::vector<uint32_t> compile_file(const std::string& name,
                                   shaderc_shader_kind kind,
                                   const std::string& data) {
  shaderc::Compiler compiler;
  shaderc::CompileOptions options;

  // Like -DMY_DEFINE=1
  options.AddMacroDefinition("MY_DEFINE", "1");

  shaderc::SpvCompilationResult module = compiler.CompileGlslToSpv(
      data.c_str(), data.size(), kind, name.c_str(), options);

  if (module.GetCompilationStatus() !=
      shaderc_compilation_status_success) {
    std::cerr << module.GetErrorMessage();
  }

  std::vector<uint32_t> result(module.cbegin(), module.cend());
  return result;
}

Mengintegrasikan ke dalam Project Anda

Compiler shader Vulkan dapat diintegrasikan ke dalam aplikasi menggunakan file atau Gradle Android.mk project.

Android.mk

Lakukan langkah-langkah berikut ini untuk menggunakan file Android.mk project Anda guna mengintegrasikan compiler shader.

  1. Sertakan baris berikut ke file Android.mk Anda:
    include $(CLEAR_VARS)
         ...
    LOCAL_STATIC_LIBRARIES := shaderc
         ...
    include $(BUILD_SHARED_LIBRARY)
    
    $(call import-module, third_party/shaderc)
    
  2. Tetapkan APP_STL ke salah satu dari c++_static, c++_shared, gnustl_static, atau gnustl_shared dalam Application.mk aplikasi

Integrasi CMake Gradle

  1. Di jendela terminal, buka ndk_root/sources/third_party/shaderc/.
  2. Jalankan perintah berikut untuk membuat Shaderc NDK. Perintah ini hanya perlu dijalankan sekali pada setiap versi NDK yang Anda gunakan:
    $ ../../../ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk \
    APP_STL:=<stl_version> APP_ABI=all libshaderc_combined
    

    Perintah ini menempatkan dua folder di <ndk_root>/sources/third_party/shaderc/. Struktur direktorinya adalah sebagai berikut:

    include/
      shaderc/
        shaderc.h
        shaderc.hpp
    libs/
      <stl_version>/
        {all of the abis}
           libshaderc.a
    
  3. Tambahkan include dan lib yang dihasilkan menggunakan target_include_directories dan target_link_libraries, seperti yang biasa Anda lakukan untuk library eksternal yang serupa. Jenis STL aplikasi Anda harus cocok dengan salah satu dari jenis stl yang ditentukan dalam stl_version. NDK merekomendasikan penggunaan c++_shared atau c++_static, meskipun gnustl_static dan gnustl_shared juga didukung.

Mendapatkan Shaderc Terbaru

Shaderc di NDK berasal dari Android Source tree, yang merupakan ringkasan dari repositori Shaderc upstream. Jika memerlukan Shaderc terbaru, baca petunjuk proses build untuk detailnya. Langkah-langkah tingkat tingginya adalah sebagai berikut:

  1. Download Shaderc terbaru:
    git clone https://github.com/google/shaderc.git
  2. Dependensi update:
    ./utils/git-sync-dep
  3. Shaderc build:
    <ndk_dir>/ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk \
        APP_STL:=c++_static APP_ABI=all libshaderc_combined -j16
    
  4. Konfigurasikan project agar menggunakan build Shaderc milik Anda sendiri dalam file skrip build Anda.