Compilateurs de nuanceurs Vulkan sur Android

Une application Vulkan doit gérer les nuanceurs différemment d'une application OpenGL ES : dans OpenGL ES, vous fournissez un nuanceur en tant qu'ensemble de chaînes formant le texte source d'un nuanceur GLSL. En revanche, avec l'API Vulkan, vous devez fournir un nuanceur sous forme de point d'entrée dans un module SPIR-V.

Le NDK version 12 ou ultérieure inclut une bibliothèque d'exécution permettant de compiler le GLSL en SPIR-V. Celle-ci est identique à celle du projet Open Source Shaderc et utilise le même compilateur de référence Glslang GLSL que son backend. Par défaut, la version de Shaderc du compilateur suppose que la compilation est destinée à Vulkan. Après avoir vérifié si votre code est valide pour Vulkan, le compilateur active automatiquement l'extension KHR_vulkan_glsl. La version de Shaderc du compilateur génère également du code SPIR-V conforme à Vulkan.

Vous pouvez choisir de compiler des modules SPIR-V dans votre application Vulkan pendant le développement. Il s'agit d'une pratique appelée compilation AOT (ahead of time) ou compilation anticipée. Vous pouvez également demander à votre application de les compiler à partir d'une source de nuanceur livrée ou générée de manière procédurale, si nécessaire. Cette pratique est appelée compilation d'exécution. Android Studio permet de créer des nuanceurs Vulkan.

Le reste de cette page explique plus en détail chaque pratique, puis décrit comment intégrer la compilation du nuanceur à une application Vulkan.

Compilation anticipée (ou "compilation AOT")

Deux approches, décrites dans les sections suivantes, permettent d'obtenir une compilation AOT du nuanceur.

Utiliser Android Studio

En ajoutant des nuanceurs dans app/src/main/shaders/, Android Studio les reconnaît par leur extension de fichier et effectue les actions suivantes :

  • Il compile tous les fichiers de nuanceurs de manière récursive dans ce répertoire.
  • Il ajoute le suffixe .spv aux fichiers de nuanceurs SPIR-V compilés.
  • Il compresse les nuanceurs SPIRV dans le répertoire assets/shaders/ du fichier APK.

L'application charge les nuanceurs compilés à partir de l'emplacement assets/shaders/ correspondant au moment de l'exécution. La structure des fichiers de nuanceurs spv compilés est identique à celle des fichiers GLSL de l'application sous 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);

Les indicateurs de compilation Shaderc peuvent être configurés dans le bloc shaders de Gradle DSL, comme illustré dans l'exemple suivant :

Groovy

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

Les arguments glslcArgs s'appliquent à toutes les compilations de nuanceurs, tandis que les arguments scopedArgs ne s'appliquent que lors de la compilation pour ce champ d'application. L'exemple ci-dessus crée un argument de champ d'application lights, qui ne s'applique qu'aux nuanceurs GLSL du répertoire app/src/main/shaders/lights/. Reportez-vous à glslc pour obtenir la liste complète des indicateurs de compilation disponibles. Notez que Shaderc dans le NDK est un instantané tiré de ce dépôt GitHub au moment de la publication du NDK. Vous pouvez obtenir les indicateurs compatibles exacts pour cette version à l'aide de la commande glslc --help, comme décrit dans la section suivante.

Compilation de ligne de commande hors connexion

Les nuanceurs GLSL peuvent être compilés en SPIR-V indépendamment de l'application principale à l'aide du compilateur de ligne de commande glslc. Pour permettre ce modèle d'utilisation, la version 12 et les versions ultérieures du NDK contiennent une version de glslc prédéfinie et les outils associés dans le répertoire <android-ndk-dir>/shader-tools/.

Le compilateur est également disponible dans le projet Shaderc. Suivez les instructions qui s'y trouvent pour créer une version binaire.

Afin de répondre aux différentes exigences d'une application, glslc fournit un ensemble complet d'options de ligne de commande pour la compilation des nuanceurs.

L'outil glslc compile un fichier source unique en un module SPIR-V avec un seul point d'entrée de nuanceur. Par défaut, le fichier de sortie porte le même nom que le fichier source, mais avec l'extension .spv en plus.

Les extensions de nom de fichier indiquent à l'outil glslc l'étape du nuanceur de graphiques à compiler ou permettent de déterminer si un nuanceur de calcul est en cours de compilation. Pour plus d'informations sur l'utilisation de ces extensions de nom de fichier et sur les options disponibles avec l'outil, consultez Shader stage specification (Spécification des étapes du nuanceur) dans le manuel glslc.

Compilation d'exécution

Pour la compilation JIT des nuanceurs lors de l'exécution, le NDK fournit la bibliothèque libshaderc, qui contient à la fois les API C et C++.

Les applications C++ doivent utiliser l'API C++. Nous vous recommandons d'utiliser l'API C dans les autres langages de programmation, car l'ABI C est de niveau inférieur et offre donc souvent une meilleure stabilité.

L'exemple suivant montre comment utiliser l'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;
}

Intégrer le compilateur dans vos projets

Vous pouvez intégrer le compilateur de nuanceurs Vulkan dans votre application à l'aide du fichier Android.mk du projet ou de Gradle.

Android.mk

Pour intégrer le compilateur de nuanceurs, utilisez le fichier Android.mk de votre projet :

  1. Incluez les lignes suivantes dans le fichier Android.mk :
    include $(CLEAR_VARS)
         ...
    LOCAL_STATIC_LIBRARIES := shaderc
         ...
    include $(BUILD_SHARED_LIBRARY)
    
    $(call import-module, third_party/shaderc)
    
  2. Définissez APP_STL sur c++_static, c++_shared, gnustl_static ou gnustl_shared dans le fichier Application.mk de l'application.

Intégration CMake de Gradle

  1. Dans une fenêtre de terminal, accédez à ndk_root/sources/third_party/shaderc/.
  2. Exécutez la commande suivante pour créer la version de Shaderc du NDK. Vous n'avez besoin d'exécuter cette commande qu'une seule fois sur chaque version de NDK que vous utilisez :
    $ ../../../ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk \
    APP_STL:=<stl_version> APP_ABI=all libshaderc_combined
    

    Cette commande place deux dossiers dans <ndk_root>/sources/third_party/shaderc/. La structure du répertoire est la suivante :

    include/
      shaderc/
        shaderc.h
        shaderc.hpp
    libs/
      <stl_version>/
        {all of the abis}
           libshaderc.a
    
  3. Ajoutez les inclusions et les bibliothèques générées avec target_include_directories et target_link_libraries, comme vous le faites habituellement pour des bibliothèques externes similaires. Le type STL de votre application doit correspondre à l'un des types stl spécifiés dans stl_version. Le NDK recommande d'utiliser c++_shared ou c++_static, bien que gnustl_static et gnustl_shared soient également compatibles.

Obtenir la dernière version de Shaderc

Shaderc dans le NDK provient de l'arborescence source Android, qui est un instantané du dépôt Shaderc en amont. Si vous avez besoin de la dernière version de Shaderc, consultez les instructions de compilation pour en savoir plus. Voici les grandes étapes à suivre :

  1. Téléchargez la dernière version de Shaderc :
    git clone https://github.com/google/shaderc.git
  2. Mettez à jour les dépendances :
    ./utils/git-sync-deps
  3. Compilez Shaderc :
    <ndk_dir>/ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk \
        APP_STL:=c++_static APP_ABI=all libshaderc_combined -j16
    
  4. Configurez votre projet pour qu'il utilise votre propre compilation Shaderc dans le fichier de script de compilation.