Skip to content

Most visited

Recently visited

navigation

Vulkan Shader Compilers on Android

A Vulkan app must manage shaders differently from the way an OpenGL ES app does: In OpenGL ES, you provide a shader as a set of strings forming the source text of a GLSL shader program. By contrast, the Vulkan API requires you to provide a shader in the form of an entry point in a SPIR-V module.

The NDK Release 12 and later includes a runtime library for compiling GLSL into SPIR-V. The runtime library is the same as the one in the Shaderc open source project, and uses the same Glslang GLSL reference compiler as its back end. By default, the Shaderc version of the compiler assumes you are compiling for Vulkan. After checking whether your code is valid for Vulkan, the compiler automatically enables the KHR_vulkan_glsl extension. The Shaderc version of the compiler also generates Vulkan-compliant SPIR-V code.

You can choose to compile SPIR-V modules into your Vulkan app during development, a practice called ahead-of-time, or AOT, compilation. Alternatively, you can have your app compile them from shipped or procedurally generated shader source when needed during runtime. This practice is called runtime compiling. Android Studio has integrated support to build Vulkan shaders.

The rest of this page provides more detail about each practice, and then explains how to integrate shader compilation into your Vulkan app.

AOT Compilation

There are two ways to achive shader AOT compilation, described in the following sections.

Using Android Studio

Putting shaders into app/src/main/shaders/, Android Studio recognizes shaders by their file extensions, and will complete the following actions:

  • Compile all shader files recursively under that directory.
  • Append the .spv suffix to the compiled SPIR-V shader files.
  • Pack SPIRV-shaders into the APK's assets/shaders/ directory.
The application would load the compiled shaders from the corresponding assets/shaders/ location at run time; the compiled spv shader file structure is the same as the application's GLSL shader file structure under 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 compile flags could be configured inside the gradle DSL shaders block, as shown in the following example:

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

glslcArgs apply to all shader compilations; scopedArgs only apply when compiling for that scope. The above example creates a scope argument lights, which will only apply to GLSL shaders under the app/src/main/shaders/lights/ directory. Refer to glslc for the complete list of available compilation flags. Note that shaderc inside NDK is a snapshot from that github repo at the NDK release time; you can get the exact supported flags for that version with the command glslc --help, as decribed in the next section. The triangle Vulkan tutorial uses this feature.

Offline Command-Line Compilation

GLSL Shaders can be compiled to SPIR-V independent of the main application using the glslc command-line compiler. NDK release 12 and later packs a version of pre-built glslc and related tools in the <android-ndk-dir>/shader-tools/ directory to support this usage model.

The compiler is also available from the Shaderc project; follow the instructions there to build a binary version.

glslc provides a rich set of command-line options for shader compilation to meet various requirements for an application.

The glslc tool compiles a single-source file to a SPIR-V module with a single shader entry point. By default, the output file has the same name as that of the source file, but with the .spv extension appended.

You use filename extensions to tell the glslc tool which graphics shader stage to compile, or whether a compute shader is being compiled. For information on how to use these filename extensions, and options you can use with the tool, see Shader stage specification in the glslc manual.

Runtime Compilation

For JIT compilation of shaders during runtime, the NDK provides the libshaderc library, which has both C and C++ APIs.

C++ applications should use the C++ API. We recommend that apps in other languages use the C API, because the C ABI is lower level, and likely to provide better stability.

The following example shows how to use the C++ API:

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

Integrating into Your Projects

You can integrate the Vulkan shader compiler into your app using either the project's Android.mk file or Gradle.

Android.mk

Perform the following steps to use your project's Android.mk file to integrate the shader compiler.

  1. Include the following lines in your Android.mk file:
    include $(CLEAR_VARS)
         ...
    LOCAL_STATIC_LIBRARIES := shaderc
         ...
    include $(BUILD_SHARED_LIBRARY)
    
    $(call import-module, third_party/shaderc)
    
  2. Set APP_STL to one of c++_static, c++_shared, gnustl_static, or gnustl_shared.

Gradle

  1. In a terminal window, navigate to ndk_root/sources/third_party/shaderc/.
  2. Run the following command:
    $ ../../../ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk \
    APP_STL:=<stl_version> APP_ABI=all libshaderc_combined
    

    This command places two folders in <ndk_root>/sources/third_party/shaderc/. The directory structure is as follows:

    include/
      shaderc/
        shaderc.h
        shaderc.hpp
    libs/
      <stl_version>/
        {all of the abis}
           libshaderc.a
    
  3. Add includes and link lines as you normally would for external libraries.
  4. The STL that you use to build your program must match the stl specified in stl_version. Only c++_static, c++_shared, gnustl_static, and gnustl_shared are supported.

This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Follow Google Developers on WeChat

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience.
(Sep 2017 survey)