Vulkan-Shader-Compiler unter Android

Bei einer Vulkan-App muss die Verwaltung von Shadern anders sein als bei einer OpenGL ES-App: In OpenGL ES stellen Sie einen Shader als eine Reihe von Strings bereit, die den Quelltext eines GLSL-Shader-Programms bilden. Im Gegensatz dazu müssen Sie für die Vulkan API einen Shader in Form eines Einstiegspunkts in einem SPIR-V-Modul bereitstellen.

NDK Release 12 und höher enthält eine Laufzeitbibliothek zum Kompilieren von GLSL in SPIR-V. Die Laufzeitbibliothek ist mit der im Open-Source-Projekt Shaderc identisch und verwendet denselben Glslang GLSL-Referenzcompiler wie sein Backend. Standardmäßig geht die Shaderc-Version des Compilers davon aus, dass Sie für Vulkan kompilieren. Nachdem geprüft wurde, ob Ihr Code für Vulkan gültig ist, aktiviert der Compiler automatisch die Erweiterung KHR_vulkan_glsl. Die Shaderc-Version des Compilers generiert auch Vulkan-konformen SPIR-V-Code.

Sie können während der Entwicklung SPIR-V-Module in Ihrer Vulkan-App kompilieren. Diese Methode wird als Ahead-of-Time-Kompilierung oder AOT bezeichnet. Alternativ können Sie Ihre Anwendung bei Bedarf aus der ausgelieferten oder prozedural generierten Shader-Quelle kompilieren lassen. Diese Vorgehensweise wird als Laufzeitkompilierung bezeichnet. Android Studio unterstützt die Erstellung von Vulkan-Shadern.

Auf dieser Seite finden Sie weitere Informationen zu den einzelnen Übungen. Außerdem erfahren Sie, wie Sie die Shader-Kompilierung in Ihre Vulkan-App einbinden.

AOT-Kompilierung

Es gibt zwei Möglichkeiten, eine Shader-AOT-Kompilierung zu erreichen, die in den folgenden Abschnitten beschrieben werden.

Android Studio verwenden

Wenn Sie Shader in app/src/main/shaders/ einfügen, erkennt Android Studio Shader anhand ihrer Dateierweiterungen und führt die folgenden Aktionen aus:

  • Kompilieren Sie alle Shader-Dateien rekursiv in diesem Verzeichnis.
  • Hängen Sie das .spv-Suffix an die kompilierten SPIR-V-Shader-Dateien an.
  • Packen Sie SPIRV-shaders im Verzeichnis assets/shaders/ des APK.

Die Anwendung würde die kompilierten Shader aus dem entsprechenden assets/shaders/-Speicherort zur Laufzeit laden. Die kompilierte spv-Shader-Dateistruktur entspricht der GLSL-Shader-Dateistruktur der Anwendung unter 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);

Shaderc-Kompilierungs-Flags können im Gradle-DSL-Block shaders konfiguriert werden, wie im folgenden Beispiel gezeigt:

Groovig

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")
    }
  }
}

glslcArgs gelten für alle Shader-Kompilierungen; scopedArgs gilt nur beim Kompilieren für diesen Bereich. Im obigen Beispiel wird das Bereichsargument lights erstellt, das nur auf GLSL-Shader im Verzeichnis app/src/main/shaders/lights/ angewendet wird. Eine vollständige Liste der verfügbaren Kompilierungs-Flags finden Sie unter glslc. Beachten Sie, dass Shaderc innerhalb von NDK ein Snapshot von diesem GitHub-Repository zum Zeitpunkt des NDK-Release ist. Die exakten unterstützten Flags für diese Version können Sie mit dem Befehl glslc --help abrufen, wie im nächsten Abschnitt beschrieben.

Offline-Kompilierung der Befehlszeile

GLSL-Shader können unabhängig von der Hauptanwendung mit dem Befehlszeilen-Compiler glslc zu SPIR-V kompiliert werden. NDK Release 12 und höher enthält eine Version des vordefinierten glslc und zugehöriger Tools im Verzeichnis <android-ndk-dir>/shader-tools/, um dieses Nutzungsmodell zu unterstützen.

Der Compiler ist auch im Projekt Shaderc verfügbar. Folgen Sie der dortigen Anleitung, um eine Binärversion zu erstellen.

glslc bietet eine Vielzahl von Befehlszeilenoptionen für die Shader-Kompilierung, um verschiedene Anforderungen an eine Anwendung zu erfüllen.

Das glslc-Tool kompiliert eine einzelne Quelldatei in ein SPIR-V-Modul mit einem einzelnen Shader-Einstiegspunkt. Standardmäßig hat die Ausgabedatei denselben Namen wie die Quelldatei, aber die Erweiterung .spv.

Mithilfe von Dateinamenerweiterungen teilen Sie dem glslc-Tool mit, welche Grafik-Shader-Phase kompiliert werden soll oder ob ein Compute-Shader kompiliert wird. Informationen zur Verwendung dieser Dateinamenerweiterungen und zu den Optionen, die Sie mit dem Tool verwenden können, finden Sie unter Spezifikation für Shader-Phasen im Handbuch zu glslc.

Laufzeitkompilierung

Für die JIT-Kompilierung von Shadern während der Laufzeit stellt der NDK die libshaderc-Bibliothek bereit, die sowohl C- als auch C++ APIs enthält.

C++-Anwendungen sollten die C++ API verwenden. Wir empfehlen, für Anwendungen in anderen Sprachen die C API zu verwenden, da das C ABI auf einem niedrigeren Niveau liegt und wahrscheinlich eine bessere Stabilität bietet.

Das folgende Beispiel zeigt, wie die C++ API verwendet wird:

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

Integration in Ihre Projekte

Sie können den Vulkan-Shader-Compiler mithilfe der Android.mk-Datei des Projekts oder mit Gradle in Ihre App einbinden.

Android.Mk

Führen Sie die folgenden Schritte aus, um den Shader-Compiler mithilfe der Datei Android.mk Ihres Projekts zu integrieren.

  1. Fügen Sie die folgenden Zeilen in die Datei „Android.mk“ ein:
    include $(CLEAR_VARS)
         ...
    LOCAL_STATIC_LIBRARIES := shaderc
         ...
    include $(BUILD_SHARED_LIBRARY)
    
    $(call import-module, third_party/shaderc)
    
  2. Legen Sie APP_STL in „Application.mk“ der Anwendung auf c++_static, c++_shared, gnustl_static oder gnustl_shared fest.

CMake-Integration von Gradle

  1. Rufen Sie in einem Terminalfenster ndk_root/sources/third_party/shaderc/ auf.
  2. Führen Sie den folgenden Befehl aus, um den Shaderc von NDK zu erstellen. Sie müssen diesen Befehl nur einmal für jede verwendete NDK-Version ausführen:
    $ ../../../ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk \
    APP_STL:=<stl_version> APP_ABI=all libshaderc_combined
    

    Dieser Befehl platziert zwei Ordner in <ndk_root>/sources/third_party/shaderc/. Die Verzeichnisstruktur sieht so aus:

    include/
      shaderc/
        shaderc.h
        shaderc.hpp
    libs/
      <stl_version>/
        {all of the abis}
           libshaderc.a
    
  3. Fügen Sie die generierten Includes und Bibliotheken mit target_include_directories und target_link_libraries hinzu, wie Sie das normalerweise für ähnliche externe Bibliotheken tun. Der STL-Typ Ihrer Anwendung muss einem der stl-Typen entsprechen, die in stl_version angegeben sind. Der NDK empfiehlt die Verwendung von c++_shared oder c++_static, obwohl gnustl_static und gnustl_shared ebenfalls unterstützt werden.

Die neueste Version von Shaderc

Shaderc in NDK stammt aus dem Android Source Tree, einem Snapshot des vorgelagerten Shaderc-Repositorys. Wenn Sie den neuesten Shaderc benötigen, lesen Sie die Build-Anleitung. Die übergeordneten Schritte sind folgende:

  1. Laden Sie den aktuellen Shaderc herunter:
    git clone https://github.com/google/shaderc.git
  2. Abhängigkeiten aktualisieren:
    ./utils/git-sync-deps
  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. Konfigurieren Sie Ihr Projekt so, dass Ihr eigener Shaderc-Build in Ihrer Build-Skriptdatei verwendet wird.