博客 / 詳情

返回

【NDK / JNI】Sceneform-EQR 集成 Filament JNI 源碼:關鍵點與逐步操作記錄

摘要:
在 Sceneform-EQR 項目中,原有對 Filament 的使用方式僅依賴官方預編譯產物,Native 層不可控且 SO 體積較大。
為提升工程可維護性並優化產物體積,本文記錄了在不修改 Filament Java API 的前提下,引入 Filament JNI 源碼、合併 filament-android / filament-utils-android / gltfio-android 三個模塊,並統一 JNI_OnLoad 的完整實踐過程。

Sceneform-EQR 集成 Filament JNI 源碼的工程優化實踐

結論:從 Java-only 依賴到 Native 一體化編譯,壓縮 SO 體積 35%+
so庫從4.3M壓縮至2.8M

倉庫地址:https://github.com/eqgis/Sceneform-EQR/tree/dev-withFilamentCpp

一、項目優化背景

1. Sceneform-EQR 與 Filament 的現狀

Sceneform-EQR 本質上是一個基於 Filament 的 Android 3D / AR 渲染框架
在現有工程結構中:

  • Java 層:直接依賴 Filament 官方提供的 Java API
  • Native 層:使用官方預編譯好的 *.so / *.a 產物
  • Filament 模塊拆分

    • filament-android
    • filament-utils-android
    • gltfio-android

這種方式雖然接入成本低,但在實際工程中逐漸暴露出幾個問題。


2. 原有方案的痛點

(1)SO 體積偏大

Filament 官方 Android 產物以模塊級 so形式存在:

  • 多個 so 文件
  • 每個 so 內部存在一定符號冗餘
  • 無法針對 Sceneform-EQR 的真實使用場景裁剪
最終 APK / AAR 體積不友好

(2)Native 層不可控
  • 無法修改 JNI 註冊邏輯
  • 無法裁剪不需要的 Native 功能
  • 排查 Native Crash 時,只能“對着黑盒猜”

(3)後續深度定製受限

隨着 Sceneform-EQR 後續規劃:

  • 自定義渲染管線
  • 深度接入 glTF / IBL / 視頻紋理
  • 可能修改 Filament JNI 行為

故:必須依賴 Native 源碼


3. 優化目標

綜合考慮,最終確定本次優化目標:

  1. 引入 Filament JNI 源碼
  2. 將 filament / utils / gltfio 三個模塊合併為一個 so
  3. 統一 JNI_OnLoad,消除多 so 衝突
  4. 在不修改 Java API 的前提下,完成底層重構
  5. 降低 SO 體積,提升工程可維護性

二、準備階段

1. 準備 Filament 源碼

從官方倉庫獲取 Filament 源碼:


2. 獲取 Android Native 編譯產物

有兩種方式:

  • 方式一:自行編譯 Filament

參考filament源碼工程的build.md,按步驟操作即可獲取

  • 方式二:直接使用官方 Release

當前推薦方式二,直接下載官方 Release 中的 android-native 產物:

https://github.com/google/filament/releases

解壓後結構如下:

  • include/ → Filament 頭文件
  • lib/arm64-v8a/*.a → 靜態庫


三、代碼合併過程

1. Java 層代碼合併

(1)覆蓋 Filament Java API

當前 Sceneform-EQR 未修改 Filament Java 源碼,因此可以直接覆蓋:

com.google.android.filament.android.*

⚠️ 後續 Sceneform-EQR 將會對 Java API 有定製,需通過 Git 進行差異合併。

(2)合併三個 Android 模塊的 Java 源碼

將以下模塊中的 src/main/java 內容合併至 Sceneform-EQR:

  • filament-android
  • filament-utils-android
  • gltfio-android


2. JNI 源碼合併

(1)拷貝 filament-android JNI 源碼
filament-1.67.1/android/filament-android/src/main/cpp

拷貝至:

Sceneform-eqr/cpp/filament-android


(2)同樣處理 gltfio / filament-utils



3. 拷貝 common 代碼

Filament JNI 依賴 common 層工具類:


拷貝至工程中


4. 拷貝 libs 與 third_party

從 Filament 源碼中拷貝 必要的依賴庫

實踐建議: 只拷貝編譯所需庫,避免 third_party 全量引入

四、解決 JNI_OnLoad 衝突(核心問題)

1. 問題本質

官方 Filament:

  • filament-android.so
  • filament-utils.so
  • gltfio.so

!!!每個模塊都有自己的 JNI_OnLoad!!!

而現在:

  • 需要 合併為一個 so
  • 只能存在一個 JNI_OnLoad

2. 解決思路

  • 各模塊:

    • 移除 JNI_OnLoad
    • 提供 registerXXX() 方法
  • 核心 so:

    • 統一 JNI_OnLoad
    • 手動調用各模塊註冊函數

3. 修改 Filament.cpp

extern "C"
JNIEXPORT jint registerFilament(JavaVM* vm, void* reserved) {
    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    ::filament::VirtualMachineEnv::JNI_OnLoad(vm);
    return JNI_VERSION_1_6;
}

4. 修改 Utils.cpp(filament-utils)

extern "C"
JNIEXPORT jint registerUtils(JavaVM* vm, void*) {
    ...
    env->RegisterNatives(...)
    return JNI_VERSION_1_6;
}

5. 重寫統一 JNI_OnLoad

extern "C"
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
    registerFilament(vm, nullptr);
    registerUtils(vm, nullptr);
    return JNI_VERSION_1_6;
}

五、Native 編譯配置

1. 引入 Filament 靜態庫

將 android-native 產物引入工程:



2. CMakeLists 配置

  • filament-android
    跳轉至git查看:
    https://github.com/eqgis/Sceneform-EQR/blob/dev-withFilamentCpp/Eq-Renderer/Android/eq-renderer/src/main/cpp/filament-android/CMakeLists.txt
  • gltfio-android
    跳轉至git查看:
    https://github.com/eqgis/Sceneform-EQR/blob/dev-withFilamentCpp/Eq-Renderer/Android/eq-renderer/src/main/cpp/gltfio-android/CMakeLists.txt
  • filament-utils-android
    跳轉至git查看:
    https://github.com/eqgis/Sceneform-EQR/blob/dev-withFilamentCpp/Eq-Renderer/Android/eq-renderer/src/main/cpp/filament-utils/CMakeLists.txt

!!!關鍵點:!!!

  • 使用 STATIC IMPORTED
  • 顯式指定 .a 路徑
  • 嚴格控制 target_link_libraries 順序

3. 主模塊 whole-archive

target_link_libraries(eqr-core
    -Wl,--whole-archive
    filament-jni
    filament-utils-jni
    gltfio-jni
    -Wl,--no-whole-archive
)
否則部分 JNI 符號會被裁剪

4. 頭文件缺失問題

Filament 1.67.1 中:

  • backend/private 未包含在 android-native

解決方案:

從 Filament 源碼工程補拷頭文件

補充説明:當前需採用ndk 27以上版本

六、編譯結果與優化收益

1. 最終 AAR 產物


2. SO 體積對比

方案 SO 大小
原多 so 方案 4.3 MB
合併 JNI 源碼後 2.8 MB
  • 合併編譯前
  • 合併編譯後
體積減少約 35%

七、小結

本次優化帶來的收益

  • SO 體積顯著下降
  • Native 層完全可控
  • JNI 註冊邏輯清晰統一
  • 為 Sceneform-EQR 後續深度定製打下基礎
缺點:Sceneform-EQR升級filament的工作量增大,故以此文備忘~

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.