Компиляторы шейдеров Vulkan на Android

Приложение Vulkan должно управлять шейдерами иначе, чем приложение OpenGL ES: в OpenGL ES вы предоставляете шейдер в виде набора строк, образующих исходный текст программы шейдера GLSL. Напротив, API Vulkan требует от вас предоставления шейдера в виде точки входа в модуль SPIR-V .

Версия NDK 12 и более поздние версии включают библиотеку времени выполнения для компиляции GLSL в SPIR-V. Библиотека времени выполнения такая же, как в проекте с открытым исходным кодом Shaderc , и использует тот же эталонный компилятор Glslang GLSL в качестве внутренней части. По умолчанию версия компилятора Shaderc предполагает, что вы компилируете для Vulkan. После проверки корректности вашего кода для Vulkan компилятор автоматически включает расширение KHR_vulkan_glsl . Версия компилятора Shaderc также генерирует код SPIR-V, совместимый с Vulkan.

Вы можете скомпилировать модули SPIR-V в свое приложение Vulkan во время разработки. Эта практика называется предварительной компиляцией или AOT . Кроме того, вы можете заставить свое приложение скомпилировать их из поставляемого или процедурно созданного источника шейдеров, когда это необходимо во время выполнения. Эта практика называется компиляцией во время выполнения . В Android Studio встроена поддержка создания шейдеров Vulkan.

Остальная часть этой страницы содержит более подробную информацию о каждом методе, а затем объясняет, как интегрировать компиляцию шейдеров в ваше приложение Vulkan.

AOT-компиляция

Существует два способа добиться AOT-компиляции шейдера, описанные в следующих разделах.

Используйте Android-студию

Поместив шейдеры в app/src/main/shaders/ , Android Studio распознает шейдеры по расширениям файлов и выполнит следующие действия:

  • Рекурсивно скомпилируйте все файлы шейдеров в этом каталоге.
  • Добавьте суффикс .spv к скомпилированным файлам шейдеров SPIR-V.
  • Упакуйте SPIRV-шейдеры в каталог assets/shaders/ APK.

Приложение будет загружать скомпилированные шейдеры из соответствующих assets/shaders/ расположения во время выполнения; скомпилированная файловая структура шейдера spv такая же, как файловая структура шейдера GLSL приложения в 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 можно настроить внутри блока shaders gradle DSL, как показано в следующем примере:

классный

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

Котлин

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

glslcArgs применяется ко всем компиляциям шейдеров; scopedArgs применяется только при компиляции для этой области. В приведенном выше примере создается аргумент области lights , который будет применяться только к шейдерам GLSL в каталоге app/src/main/shaders/lights/ . Обратитесь к glslc для получения полного списка доступных флагов компиляции. Обратите внимание, что Shaderc внутри NDK — это снимок из репозитория GitHub во время выпуска NDK; вы можете получить точные поддерживаемые флаги для этой версии с помощью команды glslc --help , как описано в следующем разделе.

Автономная компиляция из командной строки

Шейдеры GLSL можно скомпилировать в SPIR-V независимо от основного приложения с помощью компилятора командной строки glslc . Версия NDK 12 и более поздние версии содержат версию предварительно созданного glslc и связанных с ним инструментов в каталоге <android-ndk-dir>/shader-tools/ для поддержки этой модели использования.

Компилятор также доступен в проекте Shaderc ; следуйте инструкциям, чтобы создать двоичную версию.

glslc предоставляет богатый набор параметров командной строки для компиляции шейдеров, отвечающих различным требованиям приложения.

Инструмент glslc компилирует файл с одним исходным кодом в модуль SPIR-V с одной точкой входа в шейдер. По умолчанию выходной файл имеет то же имя, что и исходный файл, но с добавленным расширением .spv .

Вы используете расширения имен файлов, чтобы сообщить инструменту glslc, какой этап графического шейдера нужно компилировать или компилируется ли вычислительный шейдер. Информацию о том, как использовать эти расширения имен файлов и параметры, которые можно использовать с этим инструментом, см. в разделе Спецификация этапа шейдера в руководстве glslc .

Компиляция времени выполнения

Для JIT-компиляции шейдеров во время выполнения NDK предоставляет библиотеку libshaderc, которая имеет API C и C++.

Приложения C++ должны использовать API C++. Мы рекомендуем приложениям на других языках использовать API C, поскольку C ABI находится на более низком уровне и, вероятно, обеспечит лучшую стабильность.

В следующем примере показано, как использовать 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;
}

Интегрируйте в свои проекты

Вы можете интегрировать компилятор шейдеров Vulkan в свое приложение, используя файл Android.mk проекта или Gradle.

Android.mk

Выполните следующие шаги, чтобы использовать файл Android.mk вашего проекта для интеграции компилятора шейдеров.

  1. Включите следующие строки в файл Android.mk:
    include $(CLEAR_VARS)
         ...
    LOCAL_STATIC_LIBRARIES := shaderc
         ...
    include $(BUILD_SHARED_LIBRARY)
    
    $(call import-module, third_party/shaderc)
    
  2. Установите для APP_STL одно из c++_static , c++_shared , gnustl_static или gnustl_shared в Application.mk приложения.

Интеграция Gradle с CMake

  1. В окне терминала перейдите к ndk_root/sources/third_party/shaderc/ .
  2. Запустите следующую команду, чтобы собрать Shaderc NDK. Вам нужно запустить эту команду только один раз для каждой используемой вами версии NDK:
    $ ../../../ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk \
    APP_STL:=<stl_version> APP_ABI=all libshaderc_combined
    

    Эта команда помещает две папки в <ndk_root>/sources/ Third_party/shaderc/. Структура каталогов следующая:

    include/
      shaderc/
        shaderc.h
        shaderc.hpp
    libs/
      <stl_version>/
        {all of the abis}
           libshaderc.a
    
  3. Добавьте сгенерированные включения и библиотеки, используя target_include_directories и target_link_libraries , как вы обычно делаете для подобных внешних библиотек . Тип STL вашего приложения должен соответствовать одному из типов stl , указанных в stl_version . NDK рекомендует использовать c++_shared или c++_static , хотя gnustl_static и gnustl_shared также поддерживаются.

Получите последнюю версию Shaderc

Shaderc в NDK берется из дерева исходного кода Android , которое представляет собой снимок исходного репозитория Shaderc . Если вам нужна последняя версия Shaderc, подробности см. в инструкции по сборке . Шаги высокого уровня заключаются в следующем:

  1. Загрузите последнюю версию Shaderc:
    git clone https://github.com/google/shaderc.git
  2. Обновить зависимости:
    ./utils/git-sync-deps
  3. Сборка шейдерка:
    <ndk_dir>/ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk \
        APP_STL:=c++_static APP_ABI=all libshaderc_combined -j16
    
  4. Настройте свой проект для использования собственной сборки Shaderc в файле сценария сборки.