- Introduced `onnx_moge2.py` for MoGe-2 ONNX inference with NumPy post-processing. - Added `onnx_ben2.py` for BEN2 ONNX inference with Pillow and NumPy preprocessing. - Created `onnx_da3.py` for Depth Anything 3 inference with ONNX Runtime and NumPy. - Implemented shared ONNX Runtime session helpers in `onnx_runtime.py`. - Developed unit tests for server functionalities in `test_server.py`, including model caching and job management. - Enhanced job server to handle frame extraction without requiring models.
11 KiB
Job Server
运行架构
SnapForm 在独立的 Python 3.12 Environment 中运行 HTTP Job Server,Blender 只通过标准库 HTTP Client 提交和轮询任务,不把计算依赖载入 Blender Python。Server 只监听 127.0.0.1,由 BlendJob ServerController 保存随机端口与 Instance ID;专属本地进程协议不使用额外 Token 鉴权。
每个 Blender 实例启动并持有一个 SnapForm 专属 Server,不发现或复用其它 Blender、其它插件启动的本地 Server。多个 Blender 可以对应多个进程,但模型、Environment 与任务产物仍可共享同一个 Storage Root。
Environment、模型和任务结果默认保存在 ~/.snapform:
~/.snapform/
├── .venv/
├── environment.json
├── models/
├── jobs/
├── manifest.json
├── environment.log
├── environment-status.json
└── server.log
不同 Blender 版本可共享此目录。也可以在 Extension Preferences 的 Storage Root 中修改位置。连接端口与 Instance ID 只保存在启动它们的 Blender 进程内存中。
通用 HTTP 层位于 blendjob module。JobServer 包装 FastAPI 并提供 Job、FIFO Queue 与 Resource API,JobClient 是无第三方依赖的 Blender 侧访问器。SnapForm 的 server/__init__.py 直接导出 server 实例、集中注册全部公开 Job,并把 ModelManager 注册为延迟创建的 model_manager Resource。完整接入方式见 BlendJob 开发者指南。
SnapForm 根 package 不再有 client.py、environment.py、storage.py 或 models.py。这些文件会制造并不存在的业务边界:
- Storage Root、
.venv、Manifest、安装状态与日志路径由BlenderJobRuntime唯一管理; InstallEnvironment由runtime.operator_classes()提供,Python 与 packages 直接声明在传给 Runtime 的environment字典中;- 模型 catalog、磁盘目录、下载和 ONNX Session 都属于 Server,分别位于
model_catalog.py、model_download.py与model_manager.py; - Blender UI 只读取
model_managerResource Snapshot,不导入模型 catalog,也不检查模型文件。
blendjob/client.py 仍然存在,但它只是 BlendJob 内部的无依赖 HTTP transport,不是 SnapForm 业务抽象,也不向 Operator 暴露 JobRequest。
SnapForm 的业务 Operator 直接提交参数字典,Job Type 一一对应:
download_model→download-modelremove_background→remove-backgroundextract_frames→extract-framesgenerate_da3_depth→generate-da3-depthgenerate_moge2_depth→generate-moge2-depthgenerate_da3_surface→generate-da3-surfacegenerate_moge2_surface→generate-moge2-surfacedebug_da3→debug-da3debug_moge2→debug-moge2debug_ben2→debug-ben2
DA3 与 MoGe-2 的生产任务不再合并成带 Backend 参数的通用请求。每个 Operator 的 request(context) 直接构造自己的有效参数,Debug 任务也与生产任务完全分开。
Server 端没有 execute_*、*_result 或 CLI worker 中转。全部 @server.job(...) 都集中在 server/__init__.py:简单任务直接实现,复杂任务调用按领域拆分的 media.py、background.py、moge2.py 或 da3.py;共享的纯几何计算位于 geometry.py。模型下载由 ModelManager 单向调用 model_download.py,不存在循环 import。
ONNX-only Environment
扩展不内置 uv 可执行文件或 AI 运行库。Install Environment 首先下载固定版本的 uv,校验 SHA-256 后解压到共享 Storage Root,再用它创建环境并安装:
- ONNX Runtime 1.24.4;Windows 使用
onnxruntime-directml - NumPy 2.4.2、Pillow 12.1.1、PyAV 17.0.0、trimesh 4.11.2
- FastAPI 0.139.2、Uvicorn 0.51.0、huggingface-hub 1.4.1
运行环境不安装 PyTorch、TorchVision、xFormers,也不安装 DA3、MoGe、BEN2 或 utils3d 的 Python 源码包。模型推理和 SnapForm 所需的相机、点云、网格后处理均由 ONNX Runtime、NumPy 与内部实现完成。
Windows 当前发行环境提供 DirectML 与 CPU;Auto 优先使用 DmlExecutionProvider。显式指定不可用设备时会安全回退到 CPU。其他平台安装标准 ONNX Runtime,并按可用 provider 选择 CoreML、OpenVINO 或 CPU。
environment.json 保存规范化后的声明,manifest.json 保存该声明与 BlendJob 基础 Server packages 的 Hash。Python、packages、平台 packages 或 BlendJob Environment Schema 改变后需要重新执行 Install Environment,不再维护手工 Manifest Revision。
模型
所有正式功能都使用 ONNX:
- MoGe-2 ViT-S、ViT-B、ViT-L Normal
- DA3 Small、DA3 Base
- BEN2 Base
首次安装在 post_install(runtime) 中自动下载 MoGe-2 ViT-S Normal 与 FP16 BEN2 Base。它们和 Models panel 的后续下载一样,都是 download-model Job,与推理共用队列、HTTP 进度和取消协议。模型按声明的目录名保存在 ~/.snapform/models/,并保留仓库内部结构。声明了文件清单与校验值的模型先走 Hugging Face resolve 直链流式下载,失败后尝试 https://models.omoolab.xyz/<repository-name>/<relative-path>;两者都失败时当前 Job 直接失败,不记录或猜测后续下载源。建连成功后不限制模型文件读取时长。R2 文件会校验大小与 SHA-256。未声明清单的仓库仍使用 huggingface_hub 快照下载。可用 HF_ENDPOINT 指定 Hugging Face 镜像。
model_manager Resource 对外统一表示模型系统,持有模型 catalog、磁盘就绪状态和内存 Session。Blender 不拼接模型目录,也不直接检查权重文件。ModelSessionCache 保留 BEN2,并在 DA3 与 MoGe-2 之间只保留最近使用的一种 Depth Session。GET /resources/model_manager 返回 catalog、下载状态和当前加载状态;POST /resources/model_manager/clear 只允许在没有 Active 或 Queued Job 时执行。
DA3 ONNX 使用模型的 camera decoder 恢复外参与内参;旧版 Use Ray Pose 参数只为任务协议兼容保留,不参与 ONNX 推理。MoGe-2 的 metric depth、normal、mask、normalized intrinsics,以及 BEN2 的 Alpha,均在 NumPy 后处理中恢复。
安装网络与镜像
安装器直接开始工作,不做阻塞式网络预检。packages 由 uv 安装,可用 UV_INDEX_URL 显式指定镜像。Windows 系统代理会注入子进程;同时支持 HTTP_PROXY、HTTPS_PROXY 与 ALL_PROXY。
uv 默认从 Astral 的 GitHub Release 下载,失败后回退到 https://mirror.omoolab.xyz/uv/<version>/。两个来源使用相同的连续字节进度并共用固定 SHA-256 校验。下载压缩包只作临时文件,提取完成后删除。模型下载不经过 GitHub。
首次安装依次显示 Environment、MoGe-2 下载与 BEN2 下载,每项分别使用 0–100%。Environment 由 BlendJob 依次完成 uv 下载、Python Environment 创建与 packages 安装,uv 不再占用独立一轮进度。两个模型下载由 SnapForm 的 post_install(runtime) 通过 runtime.request() 顺序发起;切换模型时进度归零,但不显示 2/2 等阶段编号。
生命周期
扩展启用后不启动 Server;第一次执行 Job 或点击 Start 时才为当前 Blender 启动专属实例。正常关闭或禁用扩展时直接停止自己启动的进程;Server 也监视 Blender 父进程 PID,父进程异常退出后会自行停止。
AI、帧提取和 Models panel 下载的 Job 进度保存在 Server 内存中。Handler 只使用 context.progress(value, message) 发布进度,终态由 JobServer 内部管理;SnapForm 不再维护独立的 server/progress.py 或 Job 状态 JSON。Blender 每 0.25 秒轮询 GET /jobs/{job_id},任务目录不写 status.json。Operator 直接提交参数字典;Job 被接受时由 JobServer 自动生成 ID 并创建 jobs/<opaque-job-id>/,提交响应同时返回 job_id 与 directory。路径通过 JobContext.directory 传给 Handler,SnapForm Server 强制将输出写入该目录。
job_runtime.py 创建唯一的 runtime = BlenderJobRuntime(...),直接接收 server 实例并导出 runtime.JobOperatorBase。Runtime 独占 Storage Root、Environment 路径、安装进程、Server 控制和 Status Bar。Operator 未覆盖 request(context) 时会提交全部 Property;覆盖后返回值纯替换默认参数。可选的 response(context, result) 在 Blender 主线程直接完成结果导入。SnapForm 不再提供 Client 请求层、Job Mixin、Status Bar 或 Build 中转 Operator。
Environment 安装是 Server 启动前唯一的 Bootstrap 流程,使用 Storage Root 下共享的 environment-status.json 和 environment.log 回传进度,不创建 Job 目录。Environment 成功后执行普通的 post_install(runtime);默认模型下载此时已经是正式 Server Job,会创建自己的 Job 目录,并使用与其它任务相同的进度和取消协议。
Server 使用标准库单 Worker FIFO Queue,全局同时只执行一个 Job,包括模型下载。Server 忙碌时新 Job 进入 queued,不再返回 409;排队任务可以在执行前取消。取消运行中任务不会重启 Server;流式模型下载会在分块检查点结束,ONNX Runtime 当前调用则在返回后的最近检查点结束。JobServer 最多保留 100 个已完成 Job 的内存状态,后续提交时清理更早记录。
启动与崩溃信息写入 ~/.snapform/server.log。
已知限制
DA3 ONNX 的注意力是朴素实现(MatMul → Softmax → MatMul),峰值显存与 (帧数 × 每帧 patch 数)² 成正比。迁移到 ONNX 前的 PyTorch 版使用 scaled_dot_product_attention(XFORMERS_DISABLED=1),按块计算注意力分数、不落地完整注意力矩阵,显存近似线性,因此视频能处理几十上百帧;ONNX 导出把 SDPA 回退成朴素注意力,多帧在中高分辨率下会显存爆炸(约 12 帧 @ 1008 即需 ~90 GB)。
Depth Plane 与 Surface 的视频、序列帧转换会复用同一个 ONNX Session 逐帧独立推理,再把结果合并到现有序列输出,因此峰值显存只取决于单帧分辨率。逐帧结果不保留 DA3 的跨视图尺度与相机位姿一致性,适用于当前逐帧动画结构。Debug 多帧输出仍使用联合推理,以保留多视图关系。
联合推理时,onnx_da3.infer 会在执行前按 views × patches 预估注意力张量,超过 4 GiB 直接报错并提示减少帧数或 Resolution Scale,避免 onnxruntime 报出难以定位的分配失败或 UnicodeDecodeError(DirectML 在中文 Windows 下报错信息为 GBK 编码,被 UTF-8 解码掩盖)。
待解决方向:
- 重新导出 DA3 ONNX,使用 ORT 的
Attention/MultiHeadAttention贡献算子,或注入内存高效的注意力算子; - 若未来需要统一坐标系的视频重建,可按小窗口分批推理,再对齐相机位姿与深度尺度。