Compiler Shader Vulkan pada 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 pada modul SPIR-V.

NDK Rilis 12 dan yang lebih baru menyertakan library runtime untuk mengompilasi GLSL ke dalam SPIR-V. Library runtime 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 membuat kompilasi 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. Cara lainnya, Anda dapat meminta aplikasi agar mengompilasi modul dari sumber shader yang dikirim atau dihasilkan sesuai prosedur saat dibutuhkan selama runtime. Praktek ini disebut kompilasi runtime. 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 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.
  • Mengemas SPIRV-shader ke dalam direktori assets/shaders/ APK.

Aplikasi akan memuat shader yang telah dikompilasi dari lokasi assets/shaders/ yang terkait saat runtime; struktur file shader spv yang dikompilasi sama dengan struktur file shader GLSL aplikasi pada 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);
    

Tanda kompilasi Shaderc dapat dikonfigurasi di dalam blok shaders DSL gradle, seperti 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 membuat kompilasi untuk lingkup itu. Contoh di atas membuat argumen lingkup lights, yang hanya akan berlaku untuk shader GLSL pada direktori app/src/main/shaders/lights/. Lihat glslc untuk daftar lengkap tanda kompilasi yang tersedia. Harap diingat bahwa Shaderc di dalam NDK adalah cuplikan dari repo github pada saat NDK dirilis; Anda dapat memperoleh tanda yang didukung secara persis untuk versi tersebut dengan perintah glslc --help, seperti dijelaskan pada bagian selanjutnya. Tutorial Vulkan segitiga menggunakan fitur ini.

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 pre-built dan fitur terkait di dalam direktori <android-ndk-dir>/shader-tools/ untuk mendukung model penggunaan ini.

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

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

Fitur 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 fitur glslc tahap shader grafis mana yang akan dikompilasi, atau apakah shader komputasi sedang dikompilasi. Untuk informasi cara menggunakan ekstensi nama file ini, dan opsi yang dapat Anda gunakan dengan fitur ini, lihat Spesifikasi tahap Shader dalam petunjuk glslc.

Kompilasi Runtime

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

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

Contoh berikut menampilkan 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 Grader Android.mk project.

Android.mk

Jalankan langkah berikut untuk menggunakan file Android.mk project guna mengintegrasikan compiler shader.

  1. Sertakan baris berikut ke dalam file Android.mk:
        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 di 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 baris includes dan libs yang dihasilkan menggunakan target_include_directories dan target_link_libraries, seperti yang biasanya Anda lakukan untuk library eksternal serupa. Jenis STL aplikasi Anda harus cocok dengan salah satu jenis stl yang ditentukan dalam stl_version. NDK merekomendasikan penggunaan c++_shared
  4. atau c++_static , walaupun gnustl_static dan gnustl_shared juga didukung.

Getting The Latest Shaderc

Shaderc in NDK comes from Android Source tree, which is a snapshot of the upstream Shaderc repo. If you need the latest Shaderc, refer to build instruction for details. The high-level steps are as follows:

  1. Download the latest Shaderc:
    git clone https://github.com/google/shaderc.git
  2. Update dependencies:
    ./utils/git-sync-dep
  3. Build Shaderc:
    <ndk_dir>/ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk \
            APP_STL:=c++_static APP_ABI=all libshaderc_combined -j16
        
  4. Configure your project to use your own Shaderc build in your build script file.