Compiladores de sombreador Vulkan no Android

Um app Vulkan precisa gerenciar sombreadores de forma diferente da que é usada por um app com OpenGL ES. No OpenGL ES, você fornece um sombreador como um conjunto de strings que formam o texto de origem de um programa de sombreador GLSL. Por outro lado, a API Vulkan exige que você forneça um sombreador na forma de um ponto de entrada em um módulo SPIR-V (link em inglês).

A versão 12 do NDK e versões mais recentes contém uma biblioteca de ambiente de execução para compilar GLSL em SPIR-V. A biblioteca de ambiente de execução é a mesma que a presente no projeto de código aberto Shaderc e usa o mesmo compilador de referência Glslang GLSL como back-end (links em inglês). Por padrão, a versão Shaderc do compilador considera que você está compilando para Vulkan. Depois de verificar se o código é válido para Vulkan, o compilador ativa a extensão KHR_vulkan_glsl automaticamente. A versão Shaderc do compilador também gera código SPIR-V compatível com Vulkan.

É possível optar por compilar módulos SPIR-V no seu app Vulkan durante o desenvolvimento, uma prática chamada de compilação antecipada (AOT, na sigla em inglês). Ou você pode fazer o app compilá-los a partir da origem do sombreador enviada ou gerada de forma processual quando necessário durante o tempo de execução. Essa prática é chamada de compilação em tempo de execução. O Android Studio oferece compatibilidade integrada com a criação de sombreadores Vulkan.

O restante desta página fornece mais detalhes sobre cada prática e explica como integrar a compilação de sombreador ao app Vulkan.

Compilação AOT

Há duas formas de fazer a compilação AOT de sombreador, que são descritas nas seções a seguir.

Usar o Android Studio

Ao colocar sombreadores em app/src/main/shaders/, o Android Studio os reconhece pelas extensões de arquivo e realiza as seguintes ações:

  • Compila todos os arquivos de sombreador recursivamente nesse diretório.
  • Anexar o sufixo .spv aos arquivos de sombreador SPIR-V compilados.
  • Empacotar os sombreadores SPIRV no diretório assets/shaders/ do APK.

O app carregaria os sombreadores compilados do local assets/shaders/ correspondente no momento da execução. A estrutura do arquivo de sombreador spv compilado é a mesma que a do arquivo de sombreador GLSL do app em 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);

As flags de compilação do Shaderc podem ser configuradas dentro do bloco shaders da DSL do Gradle, conforme mostrado no exemplo abaixo.

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

glslcArgs se aplica a todas as compilações de sombreador. scopedArgs se aplica somente ao compilar para esse escopo. O exemplo acima cria um argumento de escopo lights, que só se aplica a sombreadores GLSL do diretório app/src/main/shaders/lights/. Consulte o glslc (link em inglês) para ver a lista completa de sinalizações de compilação disponíveis. O Shaderc no NDK é um instantâneo desse repositório do GitHub no momento do lançamento do NDK. É possível ver as sinalizações exatas compatíveis com essa versão usando o comando glslc --help, conforme descrito na próxima seção.

Compilação de linha de comando off-line

Os sombreadores GLSL podem ser compilados para SPIR-V de forma independente do app principal, usando o compilador da linha de comando glslc. As versões 12 e mais recentes do NDK empacotam uma versão do glslc pré-compilada e ferramentas relacionadas no diretório <android-ndk-dir>/shader-tools/ para oferecer compatibilidade com o uso desse modelo.

O compilador também está disponível no projeto Shaderc. Siga as instruções dele para criar uma versão binária.

O glslc fornece um conjunto avançado de opções de linha de comando para compilação de sombreador, atendendo a diversos requisitos para um app.

A ferramenta glslc compila um arquivo de origem única para um módulo SPIR-V com um único ponto de entrada de sombreador. Por padrão, o arquivo de saída tem o mesmo nome do arquivo de origem, mas com a extensão .spv anexada.

Use as extensões de nome de arquivo para informar à ferramenta glslc qual estágio de sombreador gráfico será compilado ou se o sombreador de computação está sendo compilado. Para saber mais sobre como usar essas extensões de nome de arquivo e as opções que podem ser usadas com a ferramenta, consulte Especificação do estágio do sombreador no manual do glslc (links em inglês).

Compilação em tempo de execução

Para a compilação JIT de sombreadores no momento da execução, o NDK fornece a biblioteca libshaderc, que tem APIs C e C++.

Os aplicativos em C++ precisam usar a API C++. Recomendamos que os aplicativos em outras linguagens usem a API C, porque ela tem nível inferior e provavelmente fornecerá mais estabilidade.

O exemplo a seguir mostra como usar a 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;
}

Integrar aos seus projetos

Para integrar o compilador de sombreadores do Vulkan ao seu app, use o arquivo Android.mk do projeto ou o Gradle.

Android.mk

Siga as etapas abaixo para usar o arquivo Android.mk do projeto e integrar o compilador de sombreador.

  1. Inclua as seguintes linhas no arquivo Android.mk:
    include $(CLEAR_VARS)
         ...
    LOCAL_STATIC_LIBRARIES := shaderc
         ...
    include $(BUILD_SHARED_LIBRARY)
    
    $(call import-module, third_party/shaderc)
    
  2. Defina APP_STL como c++_static, c++_shared, gnustl_static ou gnustl_shared no arquivo Application.mk do app

Integração CMake do Gradle

  1. Em uma janela de terminal, navegue até ndk_root/sources/third_party/shaderc/.
  2. Execute o comando a seguir para criar o Shaderc do NDK. Você só precisa executar este comando uma vez em cada versão do NDK usada:
    $ ../../../ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk \
    APP_STL:=<stl_version> APP_ABI=all libshaderc_combined
    

    Esse comando coloca duas pastas em <ndk_root>/sources/third_party/shaderc/. A estrutura do diretório é a seguinte:

    include/
      shaderc/
        shaderc.h
        shaderc.hpp
    libs/
      <stl_version>/
        {all of the abis}
           libshaderc.a
    
  3. Adicione includes e libs geradas usando target_include_directories e target_link_libraries, como normalmente é feito com bibliotecas externas semelhantes (links em inglês). O tipo de STL do seu app precisa corresponder a um dos tipos de stl especificados em stl_version. O NDK recomenda o uso de c++_shared ou c++_static, embora gnustl_static e gnustl_shared também sejam compatíveis.

Fazer o download do Shaderc mais recente

O Shaderc no NDK vem da árvore de origem do Android, que é um instantâneo do repositório Shaderc ascendente (links em inglês). Se você precisar do Shaderc mais recente, consulte instrução de compilação (link em inglês) para ver detalhes. As etapas gerais são as seguintes:

  1. Faça o download do Shaderc mais recente:
    git clone https://github.com/google/shaderc.git
  2. Atualizar dependências:
    ./utils/git-sync-deps
  3. Criar o 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 seu projeto para usar seu próprio build do Shaderc no arquivo de script de compilação.