Air 系列应用交流/轻模|AirModel · 鸿蒙 PC 适配全记录

轻模|AirModel · 鸿蒙 PC 适配全记录

【Air 系列 · 鸿蒙 PC 适配全记录】24 / 56

■ 应用:轻模(AirModel)
■ 定位:3D 模型查看、建模与渲染
■ 上游:Blender | 路线:引擎直连 EEVEE Vulkan | 版本:1.4.0
■ 市场状态:已上架 AGC 应用页: https://appgallery.huawei.com/app/6917609560767309910
■ 官网(免费下载): https://www.shunidc.cn/

————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/

PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper

适配开源地址:(AtomGit 仓库建立后回填)

环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743

▍一、为什么要适配轻模(Blender)

Blender 是开源三维创作领域的基准软件:建模、雕刻、材质、动画、物理模拟、视频剪辑与 Cycles 渲染引擎集成在同一进程内,代码规模与依赖复杂度都在桌面开源软件的第一梯队。把它带到鸿蒙 PC,验证的是一个上限问题——如果这个量级的应用能在鸿蒙上交叉编译并跑出完整界面,那么整个桌面创作软件谱系的移植就没有原则性障碍。工程的应用名"轻模(from Blender)"直接表明了这层关系。

工程事实如下:包名 com.airmodel.app,版本 1.3.0(versionCode 1300000),deviceTypes 覆盖 tablet、2in1 与 phone,方向锁定横屏,compressNativeLibs 关闭。核心产物是 entry/libs/arm64-v8a/libblender.so(当前约 91MB,strip 后基线 94.7MB 量级),它把 Blender 主体、Python 3.13 解释器静态链入同一个共享库;随包分发的还有 libosljit.so(OSL 着色器 JIT,约 56MB)、OpenImageIO 3.1、Python 扩展模块与全套 datafiles 资源。仓库远端在 gitcode(Andy88/AirModel),OHOS 源码改动提交在嵌套仓 harmony-blender,应用工程只跟踪主仓 main 分支。

适配路线在构建脚本头注释里写得很清楚:Blender 编译为 libblender.so 共享库,导出 Blender_Init 等符号;GPU 后端只用 Vulkan(VK_OHOS_surface);窗口系统用 GHOST-OHOS 后端替代 X11/Wayland;宿主工具(makesdna/makesrna/datatoc)先在本机编译再注入交叉编译目录。这与系列移植参考手册中 AirModel 式"XComponent + 原生渲染核心"范式一致。

▍二、先划清边界:不把编译通过当成适配完成

Blender 的功能面太大,"编译通过"与"可用"之间的距离比一般应用远得多:一个能加载但滚轮方向颠倒、渲染窗口黑屏、界面全英文的 Blender,对用户没有意义。工程因此在每个阶段都维护明确的功能基线(AGENTS.md 第四节"当前功能基线"),下表是当前边界的分层描述。

层次 桌面版 Blender 轻模当前方案
应用入口 main() + WM 主循环 EntryAbility/MyAbilityStage → napi_init.cpp → Blender_Init
窗口系统 GHOST 的 X11/Wayland/Win32 后端 GHOST-OHOS(cpp/ghost/ 下 System/Window/Context/DisplayManager 四类)
GPU 后端 OpenGL/Vulkan/Metal 多后端 仅 Vulkan,VK_USE_PLATFORM_OHOS,禁用 GL 与 Metal
Python 环境 随发行版安装的 Python Python 3.13 静态链入 libblender.so,扩展模块随 HAP 分发
渲染引擎 Cycles GPU/CPU、Eevee 全引擎 Cycles CPU 设备(OIIO 3.1.13.1 + OCIO 2.5.0 + OSL JIT)
界面语言 完整国际化体系 中文链路:出厂默认 zh_HANS,.mo 经 rawfile 预置
渲染结果窗 独立 Render 窗口 2in1 系统子窗口 / 手机平板页内浮层卡片双路由
着色器 JIT 进程默认可写代码内存 tabletkit 模块声明 ALLOW_WRITABLE_CODE_MEMORY 权限

边界中值得展开的是 Python 与 JIT 这两行:Cycles 引擎的插件体系依赖 Python,OSL 着色器编译依赖 LLVM JIT,两者在鸿蒙沙箱里都不是默认能力——Python 要静态链入并用版本脚本控制符号导出,JIT 要跨模块的权限声明与运行期探测。这两条链路的打通在第四节展开。

▍三、鸿蒙版的整体架构

架构分为构建期与运行期两幅图景。构建期,build_blender_ohos.sh(约 7000 行)驱动宿主工具编译、CMake 交叉配置(加载 ohos_preset_minimal.cmake 预设)、ninja 编译与 strip 精简;运行期,ArkTS 壳只做窗口、输入与文件管理,napi_init.cpp(约 1500 行)承担 XComponent 回调与 Blender 主循环之间的全部转发。

text
MyAbilityStage / EntryAbility(Stage 模型,横屏)
├── rawfile 资源递归部署(blender datafiles/scripts/python_home)
├── 主窗口判断:FLOATING(2in1)→ SubWindowPage 系统子窗口
│ 沉浸全屏(手机/平板)→ Index.ets 页内浮层卡片
├── DPI 自适应:物理对角英寸 → setDpiHint → GHOST getDPIHint
└── Index.ets / blenderRoot(持焦根容器,page 级 onKeyEvent)
└── XComponent(禁 focusable)→ libentry.so(napi_init.cpp)
├── 表面生命周期 / 触摸 / 滚轮(OnAnyAxis)/ 键盘回调转发
├── JIT 能力探测:mmap+mprotect 写入 arm64 机器码实证执行
└── libblender.so
├── GHOST-OHOS 窗口系统(Vulkan surface,IDENTITY preTransform)
├── Blender 主循环 + WM_window_dpi_set_userdef
├── Cycles CPU + OIIO/OCIO + OSL JIT(libosljit.so)
├── Python 3.13(静态链入,cycles 插件经 rawfile 部署)
└── 中文链路:language=13(zh_HANS) + .mo 预置


仓库主要目录如下:

text
AirModel/
├── AGENTS.md # 权威状态与操作基线(布局/构建链/禁止事项/功能基线)
├── tabletkit/ # feature 模块:声明 ALLOW_WRITABLE_CODE_MEMORY
├── scripts/ # Cycles/OSL/LLVM/OIIO 交叉编译脚本
├── entry/
│ ├── libs/arm64-v8a/ # HAP 打包源:libblender.so、libosljit.so、Python 扩展
│ └── src/main/
│ ├── ets/ # EntryAbility、Index、SubWindowPage、FileManager
│ ├── cpp/
│ │ ├── napi_init.cpp # NAPI 薄壳:回调转发、JIT 探测、resize 去抖
│ │ ├── build_blender_ohos.sh # 交叉编译总控脚本
│ │ ├── blender_patches/ # ohos_preset_minimal.cmake、导出版本脚本
│ │ ├── ghost/ # GHOST-OHOS 头文件(System/Window/Context)
│ │ ├── blender_build/ # 交叉编译构建目录
│ │ ├── blender_build_host/ # 宿主 makesdna/makesrna(Mach-O arm64)
│ │ └── ohos_deps/ # OIIO/OCIO/epoxy 等交叉编译产物
│ └── resources/rawfile/blender/ # datafiles、scripts、cycles 插件
└── blender_source -> Upstreams/blender_source # Blender 源码(嵌套仓 harmony-blender)


▍四、核心适配过程

### 1. GHOST-OHOS:给 Blender 一个鸿蒙窗口系统

Blender 的平台无关性建立在 GHOST 窗口抽象层上,适配的核心就是给它补一个 OHOS 后端。entry/src/main/cpp/ghost/ 下是 GHOST_SystemOHOS.hh、GHOST_WindowOHOS.hh、GHOST_ContextOHOS.hh、GHOST_DisplayManagerOHOS.hh 四类实现,CMake 预设同时关掉 X11、Wayland 与 SDL,GPU 侧只留 Vulkan 并定义 VK_USE_PLATFORM_OHOS=1。这里有两个标志性修复:其一,GHOST_ContextVK.cc 强制 IDENTITY preTransform——transformHint 非零的竖屏原生面板设备上,跟随 hint 会造成平板竖屏显示异常,此修复已列为基线、回退即复发;其二,Vulkan 句柄获取从脚本 patch 升级为源码化的 GHOST_GetVulkanHandlesOHOS() 接口,根仓库的 patch_ghost_get_vulkan_handles.py 随之退役——硬编码偏移地址的 patch 方式会在源码漂移时改错位置,源码化后这类风险归零。

### 2. 宿主工具与交叉编译流水

Blender 的构建流程里有一段必须在宿主机上执行的部分:makesdna/makesrna 生成数据结构与 RNA 绑定代码。这些工具必须编译成 Mach-O arm64 的宿主可执行文件(产物在 blender_build_host/,注入 blender_build/bin/),并且有一个必须遵守的同步规则:任何影响 RNA 生成的 WITH_* 开关(NDOF、ALEMBIC、BULLET、OPENJPEG、WEBP、FFMPEG 等)变更后,宿主目录必须同步重配重编,否则生成的 rna_*_gen.cc 与目标侧宏不一致,报 undeclared identifier。交叉配置由 blender_patches/ohos_preset_minimal.cmake 以 -C 方式加载,所有 OHOS 平台选项集中管理;编译产物经 llvm-strip 与版本脚本精简到 100MB 以下(gitcode 钩子拒收超限仓库)。

### 3. Cycles 渲染栈:OIIO、OCIO 与颜色管理

2026 年 9 月 14 日起 Cycles 以 CPU 设备启用,配套 WITH_OPENIMAGEIO 与 WITH_TBB。渲染依赖的 OIIO 3.1.13.1 与 OCIO 2.5.0 由 scripts/build_oiio_ocio_ohos.sh 交叉编译为静态库放入 ohos_deps——脚本头注释记录了全部踩坑点,包括 OIIO 3.x 把 OCIO 作为硬依赖、其 BUILD_LOCAL 不转发工具链,fmt、Robinmap、OCIO 都得自备。颜色管理配置(datafiles/colormanagement/)随 rawfile 分发;Cycles 插件 Python 源自 intern/cycles/blender/addon/,改动需在 rawfile 的 addons_core 副本双份同步。旧配置文件没有 cycles 启用记录,升级用户靠 wm_files.cc 的 wm_init_userdef() 一次性迁移并以标记文件防止重复执行。九项能力(真 OCIO、Alembic、Bullet、Audaspace、流体、OpenJPEG、WebP、Cineon、Pugixml)随后接入,接线点在 dependency_targets.cmake 尾部的 OHOS 段。

### 4. OSL JIT 与 tabletkit 权限模块

OSL 着色器的编译依赖 LLVM JIT,而 JIT 需要可写且可执行的代码内存——这在鸿蒙上是一项需要显式声明的内核权限。工程的做法是拆出一个名为 tabletkit 的 feature 模块,在它的 module.json5 里声明 ohos.permission.kernel.ALLOW_WRITABLE_CODE_MEMORY,由签名 profile 的 ACL 授予;主 HAP 不携带该声明。运行期做双重实证:napi_init.cpp 的 AirModelProbeJitCapability() 在进程最早时机 mmap 一页匿名内存、写入一段 arm64 机器码(movz w0,#0x5A; ret)、mprotect 加执行位后真实调用,返回 0x5A 才算权限生效;另有 RunJitSmoke 冒烟入口,dlopen libjitsmoke.so 让 LLJIT 编译 sum(40,2) 并断言返回 42。探测失败的场景(tabletkit 未安装、手机侧无此权限)走 BLENDER_OSL_DISABLE=1 静默降级。OSL 本体由 scripts/build_osl_ohos.sh 交叉编译,LLVM 20.1.8 工具链由 build_llvm_ohos.sh 产出。

### 5. 输入链路与子窗口路由

输入侧的处理都在 napi_init.cpp 与 ArkTS 层:XComponent 原生键回调加 page 级 onKeyEvent(根容器 blenderRoot 持焦),修饰键本地按位跟踪;出厂启用两键鼠标仿真(Alt+左键等于中键),这是没有三键鼠标的触屏环境的必要补齐。滚轮分支对 OHOS 轴值做了方向取反(自然滚动语义)并给 0.5 步进系数。窗口 resize 做了 150ms 最后者胜的去抖(KickDeferredResize),配合 GHOST_SystemOHOS::postSurfaceResizedForId 队列合并——直通 Blender resize 会逐帧重建 swapchain 造成卡顿。渲染结果窗采用统一双路由:主窗 FLOATING(2in1)走系统 subwindow,沉浸全屏(手机/平板)走 Index.ets 页内浮层卡片,XComponent id 统一为 blender_sub_<N>,native 侧零感知。DPI 自适应由 Index.ets 按物理对角英寸计算 scale,经 setDpiHint 注入到 GHOST_WindowOHOS::getDPIHint()(原先此处硬编码 160),2in1 维持基线 160,其余设备钳制在 1.0 到 1.6 之间。

▍五、适配中遇到的几个难点

### 难点一:RNA 代码生成与目标侧宏的错位

现象是关闭 NDOF 后编译报 undeclared identifier,出错位置在自动生成的 rna_userdef_gen.cc。根因是宿主 makesrna 与目标侧的 WITH_* 宏不一致:宿主工具还是按旧配置生成的绑定代码,引用了目标侧已关闭的 ndof 回调。解法是把同步重编固化为流程纪律——任何影响 RNA 生成的开关变更,宿主目录必须增量重配重编,AGENTS.md 把这条写进构建链路第 1 步并记录了 2026-09-16 的事故。交叉编译里"宿主阶段"与"目标阶段"的配置一致性,是这个项目最容易被忽视的暗礁。

### 难点二:触摸事件被焦点系统拦截

现象是建模区触控失效。根因是 XComponent 声明了 focusable,触摸事件被 ArkUI 焦点系统优先消费,到不了原生层。解法是禁止给 XComponent 加 focusable(错误码 59037564 已作为禁止事项写入 AGENTS.md),键盘输入改由 page 级 onKeyEvent 承接。这个案例决定了后续所有输入改动都围绕"焦点留在 ArkTS 层、事件按位转发"的既有路径展开,而不是回退到组件级焦点。

### 难点三:swapchain 逐帧重建造成的 resize 卡顿

现象是窗口化与全屏切换时明显卡顿。根因是 SurfaceChanged 直通 Blender resize,每次尺寸变化都触发 swapchain 重建,窗口拖拽期间逐帧执行。解法是去抖加合并:napi 侧 150ms 最后者胜的延迟触发,surface 销毁即失效;GHOST 侧 postSurfaceResizedForId 合并队列。遗留问题同样有记录:切换过程中 surface 若被系统回收,仍会走整轮 Blender_Exit 到 Init 的秒级冻结,后续方案是保留上下文只重建 swapchain。AGENTS.md 同时注明"不要恢复直通",把修复的边界钉死。

### 难点四:符号导出失控把 .so 推过 100MB

现象是 libblender.so 体积涨到 108MB 以上,超过 gitcode 仓库钩子的 100MB 上限。根因是版本脚本缺失时动态符号表全量导出——Blender 加 Python 的符号面极大。解法是 blender_patches/libblender_exports.map 白名单只导出 Blender_* 与 Py*,配合 gc-sections、icf 与 gnu hash style 固化在平台 cmake 里;验收命令是 nm -D 校验 Blender_* 恰好 21 个入口、Py* 不少于 1400 个,strip 后基线 94.7MB。符号面控制同时服务了体积与加载性能,这步缺失时其余优化都不起作用。

### 难点五:双 HAP 部署与权限承接

现象是渲染时 OSL JIT 静默失效,排查发现 tabletkit 从未部署过——devecocli build 默认只构建 entry 模块。根因是 ALLOW_WRITABLE_CODE_MEMORY 声明在 tabletkit 模块里,漏装它 JIT 权限就无从谈起。解法是把流程固化:构建需 devecocli build --modules tabletkit 单独补建,安装必须先装 entry HAP 再装 tabletkit HAP,两步缺一不可。同类签名问题也归入此列:调试签名换设备会报 9568423(UDID 不在 profile 白名单),release 证书本地直装报 9568322,各有对应的签名材料分工(调试 p7b 与发布 Release.p7b,后者含 ACL 但不可本地装)。

▍六、编译、安装与启动

本工程的构建链路比常规 HAP 复杂,AGENTS.md 给出的完整顺序如下(详细参数固化在脚本内,勿跳步):

bash

cd entry/src/main/cpp/blender_build_host && cmake --build . --target makesdna makesrna --parallel 10




ninja -C entry/src/main/cpp/blender_build blender -j10




export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk/default
devecocli build
devecocli build --modules tabletkit


安装与启动必须双 HAP(tabletkit 漏装则 JIT 权限缺失):

bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc install -r tabletkit/build/default/outputs/default/tabletkit-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airmodel.app


装机后的验证流程同样成文:strings entry/libs/arm64-v8a/libblender.so | grep "force IDENTITY preTransform" 应至少命中一处;启动若报 10106102 screen is locked,先 power-shell wakeup 加 uinput 上滑解锁;再以 snapshot_display 截图确认横屏、全简体中文、视口/大纲/属性面板完整。每次验证记录写入 ai_log/ 留档。

▍七、当前功能边界

当前版本已覆盖的能力,逐条对应 AGENTS.md 功能基线:

- 横屏完整 Blender 界面:视口、大纲、属性面板,全简体中文(zh_HANS 默认,.mo 预置);
- 触摸与键盘输入全链路:两键鼠标仿真、滚轮方向修正与步进系数、修饰键跟踪;
- Cycles CPU 渲染引擎:OIIO 3.1.13.1 + OCIO 2.5.0 真颜色管理 + OSL JIT(权限探测门控);
- 九项能力接入:Alembic、Bullet、Audaspace、流体、OpenJPEG、WebP、Cineon、Pugixml 与真 OCIO;
- 渲染结果窗双路由:2in1 系统子窗口、手机平板页内浮层卡片(含渲染进度 HUD 与挖孔避让);
- DPI 自适应:按设备物理对角英寸注入,2in1 基线 160 零回归;
- 多设备验证基线:MatePad Mini、MateBook Pro、Pura X 已在签名白名单内运行。

尚未覆盖的能力,依据 AGENTS.md 的缺库清单与遗留记录:OpenSubdiv、ceres(libmv 跟踪)、GMP、FFTW3、Haru、Potrace、Manifold、OpenVDB、FFmpeg 九项依赖待交叉编译接入;NDOF 已关(平板无硬件);Audaspace 为空设备,VSE 音频静音;resize 中 surface 被回收时仍会整轮重启(秒级冻结),上下文保留方案待做。因此,当前版本更准确的定位是"Blender 鸿蒙 PC 建模与渲染基础版":从建模、材质到 Cycles 出图的创作主链路已在鸿蒙上成立,物理仿真完整度与视频剪辑音频还是下一步的活。

▍八、总结

轻模的意义不在"又一个应用上了鸿蒙",而在它把移植工程的上限标定出来:7000 行构建脚本、版本脚本控制符号面、宿主与目标两侧的生成代码同步、跨模块的 JIT 权限设计、双 HAP 部署纪律——这些手段组合起来,让一个千万行级、依赖 Python 与 LLVM 的桌面软件在 arm64-v8a 的鸿蒙设备上跑出了可交互的三维创作环境。工程同时示范了大项目如何做协约管理:AGENTS.md 作为权威基线记录每个决策的来历与禁止事项,ai_log 按日留档,嵌套仓隔离上游改动,任何一条都可回溯。

后续工作的重心按缺库清单与遗留项推进:优先补 OpenSubdiv 与 ceres(雕刻与运动跟踪两块能力板),完成 resize 的上下文保留方案消除秒级冻结,再把渲染结果窗与 DPI 适配在新机型上回归。每一步的验收方式不变:可构建的 .so 与 HAP、真机截图确认、ai_log 留痕,让"能用"始终有据可查。
楼主 8 天前
想参与讨论?登录 或 30 秒注册。