Kompilatory cieniowania Vulkan na Androidzie

Aplikacja Vulkan musi zarządzać cieniami inaczej niż aplikacja OpenGL ES: w przypadku OpenGL ES udostępniasz program do cieniowania jako zestaw ciągów tekstowych tworzących tekst źródłowy programu do cieniowania OpenGL. Z kolei interfejs Vulkan API wymaga udostępnienia programu do cieniowania w postaci punktu wejścia w module SPIR-V.

NDK w wersji 12 i nowszych zawiera bibliotekę środowiska wykonawczego, które umożliwia kompilowanie GLSL do SPIR-V. Biblioteka środowiska wykonawczego jest taka sama jak w projekcie open source Shaderc, a jako backend korzysta ten sam kompilator odwołań Glslang GLSL. Domyślnie wersja Shaderc kompilatora zakłada, że kompilujesz dla języka Vulkan. Po sprawdzeniu, czy Twój kod jest zgodny z platformą Vulkan, kompilator automatycznie włącza rozszerzenie KHR_vulkan_glsl. Wersja kompilatora Shaderc generuje też kod SPIR-V zgodny z Vulkanem.

W trakcie programowania możesz kompilować moduły SPIR-V w aplikację Vulkan (metodę nazywaną wcześniej) lub kompilację AOT. Możesz też w razie potrzeby skompilować je w czasie działania z przesłanego lub wygenerowanego proceduralnie źródła programu do cieniowania. Jest to tzw. kompilowanie w czasie działania. Android Studio ma zintegrowaną obsługę cieniowania Vulkan.

Na tej stronie znajdziesz więcej informacji o każdej z nich, a potem dowiesz się, jak zintegrować kompilację programów do cieniowania z aplikacją Vulkan.

Kompilacja AOT

Istnieją 2 sposoby na uzyskanie kompilacji AOT dla cieniowania opisane w kolejnych sekcjach.

Użyj Android Studio

Po wprowadzeniu cieniowania w app/src/main/shaders/ Android Studio rozpoznaje je na podstawie ich rozszerzeń plików i wykona te działania:

  • Skompiluj rekurencyjnie wszystkie pliki cieniowania w tym katalogu.
  • Dołącz sufiks .spv do skompilowanych plików cieniowania SPIR-V.
  • Zapakuj shader SPIRV do katalogu assets/shaders/ pakietu APK.

Aplikacja wczytuje skompilowane programy do cieniowania z odpowiedniej lokalizacji w języku assets/shaders/ w czasie działania. Skompilowana struktura pliku cieniowania spv jest taka sama jak struktura pliku cieniowania GLSL aplikacji w 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);

Flagi kompilacji Shaderc można skonfigurować wewnątrz bloku DSL shaders Gradle, jak pokazano w tym przykładzie:

Odlotowy

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

Kotlin

android {
  defaultConfig {
    shaders {
        glslcArgs += listOf("-c", "-g")
        glslcScopedArgs("lights", "-DLIGHT1=1", "-DLIGHT2=0")
    }
  }
}

Wartości glslcArgs mają zastosowanie do wszystkich kompilacji cieniowania, a scopedArgs tylko podczas kompilacji dla tego zakresu. Powyższy przykład tworzy argument zakresu lights, który będzie stosowany tylko do cieniowania GSL w katalogu app/src/main/shaders/lights/. Pełną listę dostępnych flag kompilacji znajdziesz w dokumentacji glslc. Zwróć uwagę, że Shaderc w NDK to zrzut z repozytorium github w momencie wydania NDK. Dokładne obsługiwane flagi dla danej wersji możesz uzyskać, używając polecenia glslc --help, jak opisano w następnej sekcji.

Kompilacja wiersza poleceń offline

Programy Shadery GLSL można kompilować do interfejsu SPIR-V niezależnie od głównej aplikacji za pomocą kompilatora wiersza poleceń glslc. NDK w wersji 12 i nowszych zawiera wersję gotowego pliku glslc i powiązanych narzędzi w katalogu <android-ndk-dir>/shader-tools/ na potrzeby obsługi tego modelu użycia.

Kompilator jest również dostępny w projekcie Shaderc. Aby utworzyć wersję binarną, postępuj zgodnie z podanymi tam instrukcjami.

glslc udostępnia bogaty zestaw opcji wiersza poleceń na potrzeby kompilacji programu do cieniowania, które pozwalają spełnić różne wymagania aplikacji.

Narzędzie glslc skompiluje plik z jednym kodem źródłowym i utworzy moduł SPIR-V z pojedynczym punktem wejścia do cieniowania. Domyślnie plik wyjściowy ma taką samą nazwę jak plik źródłowy, ale z dołączonym rozszerzeniem .spv.

Rozszerzenia plików informują narzędzie glslc o tym, który program do cieniowania grafiki ma skompilować oraz o tym, czy ten program jest kompilowany. Informacje o tym, jak korzystać z tych rozszerzeń nazw plików, a także o opcjach, których możesz używać z narzędziem, znajdziesz w specyfikacji etapu Shader w instrukcji glslc.

Kompilacja środowiska wykonawczego

Do kompilacji programów do cieniowania przy użyciu metody JIT w czasie działania NDK dostarcza bibliotekę libshaderc, która zawiera interfejsy API zarówno w języku C, jak i C++.

Aplikacje w C++ powinny używać interfejsu API C++. Zalecamy, aby aplikacje w innych językach korzystały z interfejsu C API, ponieważ interfejs C ABI jest niższy i prawdopodobnie zapewni większą stabilność.

Z przykładu poniżej dowiesz się, jak korzystać z interfejsu API C++:

#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;
}

Integracja ze swoimi projektami

Możesz zintegrować kompilator do cieniowania Vulkan ze swoją aplikacją za pomocą pliku Android.mk projektu lub Gradle.

Android.mk

Wykonaj poniższe czynności, aby użyć pliku Android.mk swojego projektu do integracji kompilatora do cieniowania.

  1. W pliku Android.mk umieść te wiersze:
    include $(CLEAR_VARS)
         ...
    LOCAL_STATIC_LIBRARIES := shaderc
         ...
    include $(BUILD_SHARED_LIBRARY)
    
    $(call import-module, third_party/shaderc)
    
  2. Ustaw APP_STL na jedną z wartości c++_static, c++_shared, gnustl_static lub gnustl_shared w pliku Application.mk aplikacji

Integracja CMake dla Gradle

  1. W oknie terminala przejdź do ndk_root/sources/third_party/shaderc/.
  2. Uruchom to polecenie, by utworzyć Shaderc dla NDK. To polecenie musisz uruchomić tylko raz w każdej wersji NDK:
    $ ../../../ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk \
    APP_STL:=<stl_version> APP_ABI=all libshaderc_combined
    

    To polecenie umieszcza 2 foldery w katalogu <ndk_root>/sources/third_party/shaderc/. Struktura katalogów jest taka:

    include/
      shaderc/
        shaderc.h
        shaderc.hpp
    libs/
      <stl_version>/
        {all of the abis}
           libshaderc.a
    
  3. Dodaj wygenerowane rozszerzenia i biblioteki za pomocą target_include_directories i target_link_libraries, tak jak w przypadku podobnych bibliotek zewnętrznych. Typ STL aplikacji musi pasować do jednego z typów stl określonych w polu stl_version. NDK zaleca użycie c++_shared lub c++_static, ale obsługiwane są też gnustl_static i gnustl_shared.

Pobierz najnowszą wersję Shaderca

Shaderc w NDK pochodzi z drzewa źródłowego Androida, które jest zrzutem przedstawnego repozytorium Shaderc. Jeśli potrzebujesz najnowszej wersji Shaderc, zapoznaj się ze szczegółami kompilacji. Ogólnie rzecz biorąc:

  1. Pobierz najnowszą wersję Shaderca:
    git clone https://github.com/google/shaderc.git
  2. Zaktualizuj zależności:
    ./utils/git-sync-deps
  3. Kompilacja Shaderc:
    <ndk_dir>/ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk \
        APP_STL:=c++_static APP_ABI=all libshaderc_combined -j16
    
  4. Skonfiguruj projekt tak, aby w pliku skryptu kompilacji korzystał z własnej kompilacji Shaderc.