Compiler shader Vulkan di Android

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

NDK Rilis 12 dan yang lebih baru menyertakan library runtime untuk mengompilasi GLSL ke dalam SPIR-V. Library runtime ini sama seperti yang ada di project open source Shaderc, dan menggunakan compiler referensi Glslang GLSL yang sama seperti 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 runtime. Praktik ini disebut kompilasi runtime. Android Studio memiliki dukungan terintegrasi untuk membuat shader Vulkan.

Bagian selanjutnya di halaman ini memberikan detail selengkapnya tentang 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 runtime; 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, sebagaimana 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. Perlu diketahui bahwa Shaderc di dalam NDK adalah snapshot dari repositori github pada saat NDK dirilis; Anda dapat memperoleh flag yang didukung secara persis untuk versi tersebut dengan perintah glslc --help, sebagaimana dijelaskan di 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 memaketkan versi glslc bawaan dan alat 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 kumpulan 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 seperti file sumber, tetapi dengan tambahan ekstensi .spv.

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

Kompilasi runtime

Untuk kompilasi JIT shader selama runtime, NDK menyediakan library libshaderc, yang memiliki C API 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

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

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 dilakukan untuk library eksternal 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 pohon Sumber Android, yang merupakan snapshot 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.