Trình biên dịch đổ bóng Vulkan trên Android

Ứng dụng Vulkan phải quản lý chương trình đổ bóng khác với cách quản lý của ứng dụng OpenGL ES: Trong OpenGL ES, bạn cung cấp chương trình đổ bóng dưới dạng tập hợp chuỗi tạo thành văn bản nguồn của chương trình đổ bóng GLSL. Ngược lại, Vulkan API yêu cầu bạn cung cấp chương trình đổ bóng ở dạng điểm truy cập trong mô-đun SPIR-V.

Bản phát hành NDK 12 trở lên bao gồm một thư viện thời gian chạy để biên dịch GLSL vào SPIR-V. Thư viện thời gian chạy này giống với thư viện trong dự án nguồn mở Shaderc và sử dụng cùng một trình biên dịch tham chiếu Glslang GLSL làm phần phụ trợ. Theo mặc định, phiên bản Shaderc của trình biên dịch giả định bạn đang biên dịch cho Vulkan. Sau khi kiểm tra xem mã có hợp lệ đối với Vulkan hay không, trình biên dịch sẽ tự động bật tiện ích KHR_vulkan_glsl. Phiên bản Shaderc của trình biên dịch cũng tạo mã SPIR-V tuân thủ Vulkan.

Bạn có thể chọn biên dịch mô-đun SPIR-V vào ứng dụng Vulkan của mình trong quá trình phát triển, một biện pháp có tên là biên dịch ahead-of-time hoặc AOT. Hoặc, bạn có thể yêu cầu ứng dụng biên dịch các mô đun này từ nguồn chương trình đổ bóng được tạo theo thủ tục hoặc được chuyển khi cần trong thời gian chạy. Biện pháp này được gọi là biên dịch trong thời gian chạy. Android Studio đã tích hợp tính năng hỗ trợ xây dựng chương trình đổ bóng Vulkan.

Phần còn lại của trang này cung cấp thêm thông tin chi tiết về từng biện pháp, sau đó giải thích cách tích hợp hoạt động biên dịch chương trình đổ bóng vào ứng dụng Vulkan.

Biên dịch AOT

Có hai cách thực hiện việc biên dịch chương trình đổ bóng bằng biện pháp AOT, được mô tả trong các phần sau.

Sử dụng Android Studio

Khi đặt chương trình đổ bóng vào app/src/main/shaders/, Android Studio nhận dạng chương trình đổ bóng bằng đuôi tệp và sẽ hoàn thành các thao tác sau:

  • Biên dịch đệ quy tất cả các tệp trong chương trình đổ bóng trong thư mục đó.
  • Đưa hậu tố .spv vào các tệp trong chương trình đổ bóng SPIR-V được biên dịch.
  • Đóng gói SPIRV-shaders vào thư mục assets/shaders/ của APK.

Ứng dụng sẽ tải chương trình đổ bóng được biên dịch từ vị trí assets/shaders/ tương ứng trong thời gian chạy; cấu trúc tệp chương trình đổ bóng spv được biên dịch giống với cấu trúc tệp chương trình đổ bóng GLSL của ứng dụng trong 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);

Bạn có thể định cấu hình các cờ biên dịch của Shaderc bên trong khối shaders DSL của Gradle, như trong ví dụ sau:

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 áp dụng cho tất cả hoạt động biên dịch chương trình đổ bóng; scopedArgs chỉ áp dụng khi biên dịch cho phạm vi đó. Ví dụ ở trên sẽ tạo một đối số phạm vi lights. Đối số phạm vi này sẽ chỉ áp dụng cho chương trình đổ bóng GLSL trong thư mục app/src/main/shaders/lights/. Tham khảo glslc để biết danh sách đầy đủ các cờ biên dịch có sẵn. Lưu ý: Shaderc bên trong NDK là phần tổng quan nhanh từ kho lưu trữ github đó tại thời điểm phát hành NDK; bạn có thể nhận chính xác các cờ được hỗ trợ cho phiên bản đó bằng lệnh glslc --help, như được mô tả trong phần tiếp theo.

Biên dịch dòng lệnh ngoại tuyến

Bạn có thể biên dịch Chương trình đổ bóng GLSL tới SPIR-V độc lập với ứng dụng chính bằng cách sử dụng trình biên dịch dòng lệnh glslc. Bản phát hành NDK 12 trở lên gói một phiên bản glslc tạo sẵn và các công cụ liên quan trong thư mục <android-ndk-dir>/shader-tools/ để hỗ trợ mô hình sử dụng này.

Trình biên dịch cũng được cung cấp từ dự án Shaderc; hãy làm theo hướng dẫn tại đó để tạo phiên bản nhị phân.

glslc cung cấp một bộ tuỳ chọn dòng lệnh phong phú cho việc biên dịch chương trình đổ bóng để đáp ứng nhiều yêu cầu đối với một ứng dụng.

Công cụ glslc biên dịch một tệp đơn nguồn thành mô-đun SPIR-V với một điểm nhập duy nhất cho chương trình đổ bóng. Theo mặc định, tệp đầu ra có cùng tên với tệp nguồn, nhưng có hậu tố là đuôi .spv.

Bạn sử dụng đuôi của tên tệp để cho công cụ glslc biết giai đoạn của chương trình đổ bóng đồ hoạ cần biên dịch, hoặc biết việc chương trình đổ bóng điện toán có đang được biên dịch hay không. Để biết thông tin về cách sử dụng các đuôi tên tệp này và tuỳ chọn mà bạn có thể sử dụng bằng công cụ này, hãy xem phần Thông số kỹ thuật về giai đoạn của chương trình đổ bóng trong hướng dẫn sử dụng glslc.

Biên dịch trong thời gian chạy

Đối với quá trình biên dịch JIT của chương trình đổ bóng trong thời gian chạy, NDK cung cấp thư viện libshaderc, trong đó có cả API C và C ++.

Các ứng dụng C++ phải sử dụng API C++. Các ứng dụng bằng ngôn ngữ khác nên sử dụng API C, vì ABI C có cấp độ thấp hơn và có thể đem lại sự ổn định cao hơn.

Ví dụ sau đây minh hoạ cách sử dụng 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;
}

Tích hợp vào dự án của bạn

Bạn có thể tích hợp trình biên dịch chương trình đổ bóng Vulkan vào ứng dụng bằng cách sử dụng tệp Android.mk của dự án hoặc Gradle.

Android.mk

Hãy thực hiện các bước sau để sử dụng tệp Android.mk của dự án nhằm tích hợp trình biên dịch chương trình đổ bóng.

  1. Đưa các dòng sau vào tệp Android.mk:
    include $(CLEAR_VARS)
         ...
    LOCAL_STATIC_LIBRARIES := shaderc
         ...
    include $(BUILD_SHARED_LIBRARY)
    
    $(call import-module, third_party/shaderc)
    
  2. Đặt APP_STL thành một trong các giá trị: c++_static, c++_shared, gnustl_static hoặc gnustl_shared trong Application.mk của ứng dụng

Khả năng tích hợp CMake của Gradle

  1. Trong cửa sổ dòng lệnh, hãy chuyển đến ndk_root/sources/third_party/shaderc/.
  2. Chạy lệnh sau để tạo shaderc của NDK. Bạn chỉ cần chạy lệnh này một lần trên mỗi phiên bản NDK mà bạn sử dụng:
    $ ../../../ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk \
    APP_STL:=<stl_version> APP_ABI=all libshaderc_combined
    

    Lệnh này đặt hai thư mục trong <ndk_root>/sources/third_party/shaderc/. Cấu trúc thư mục như sau:

    include/
      shaderc/
        shaderc.h
        shaderc.hpp
    libs/
      <stl_version>/
        {all of the abis}
           libshaderc.a
    
  3. Thêm phần includes và libs được tạo bằng target_include_directoriestarget_link_libraries như bạn thường làm với thư viện bên ngoài tương tự. Loại STL của ứng dụng phải khớp với một trong các loại stl đã chỉ định trong stl_version. NDK khuyên bạn nên sử dụng c++_shared hoặc c++_static, mặc dù gnustl_staticgnustl_shared cũng được hỗ trợ.

Tải Shaderc mới nhất

Shaderc trong NDK đến từ cây Nguồn Android, là phần tổng quan nhanh về kho lưu trữ Shaderc ngược dòng (upstream). Nếu bạn cần Shaderc mới nhất, hãy tham khảo hướng dẫn tạo để biết thông tin chi tiết. Sau đây là các bước cấp cao:

  1. Tải Shaderc mới nhất xuống:
    git clone https://github.com/google/shaderc.git
  2. Cập nhật phần phụ thuộc:
    ./utils/git-sync-deps
  3. Tạ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. Định cấu hình cho dự án sử dụng bản dựng Shaderc riêng trong tệp tập lệnh bản dựng.