背景
出帧目前跑在 worker 进程里:providers/render3d/sprite.py 起一个本地 HTTP 服务当 docroot,用 node + Playwright Chromium 打开 bake_stage.html,mixer.setTime 逐帧抓 WebGL 缓冲。docker-compose.yml 的 worker 用 target: runtime-render3d,node、Chromium、three 因此全部进了 worker 镜像。
2026-08-26 在部署机实测:worker 镜像 2.53 GB,backend 镜像 820 MB,其中 /ms-playwright 单独 656 MB;根分区 38G 已用 32G(84%),staging 的 worker 镜像同样 2.53 GB。
部署机没有 GPU,这条路只能走 SwiftShader 软件光栅。本机 2026-08-04 实测:12 帧 2214×1848 软件渲染 6.37s,峰值进程树 RSS 约 1.0 GB,峰值 CPU 760%,即需要 7.6 个核;部署机只有 4 核。服务器上的真实负载(18MB GLB + 4096 贴图 + 骨骼动画)至今没有实测数据。
同一台机器上 i2v 的峰值内存问题见 #690,一期已由 #695 合入。两者是同一台 4C8G 上的两笔账,本 issue 不碰 i2v 的算法。
路线图见 #712。
问题
服务端渲染的这一段,用户那一侧有空闲 GPU,服务端没有;渲染结果本身也不参与计费与判定。继续留在服务端的代价是镜像多 1.7 GB、出帧时与 i2v 抢同一台 4C8G 的 CPU 与内存,而收益只是渲染环境统一。
方案
浏览器承担驱动循环与 WebGL 采样,worker 只做 Playwright 之后的部分。
浏览器:拉 model_3d_url → __ready → __setCamYaw → __setup(clip,i,n) → __coverage → __grab
→ 生帧 PNG 直传,key 绑 task_id
worker:校验张数、空帧、PNG magic → 像素化 → _lastmile → 成色闸 → 交付帧入库
优先把 bake_stage.html 的 __* 钩子抽成前端可复用模块,不要另写一套 three 出帧。
前端必须带走的硬约束,缺一条就会把坏帧交给后处理:
- 抓 WebGL 缓冲,不对页面截图,透明底会被合成掉
mixer.setTime(t) 采样,不实时播放,同一 clip 两次要能对账到采样时刻
setPixelRatio(1)、antialias: false
- 构图一次算定,横向跨度取
max(X, Z);多朝向转相机不转模型
- 覆盖率低于
MIN_COVERAGE(默认 0.005)判失败,禁止上传全透明帧
- clip 名与
ActionSpec.action 对齐,朝向与 want 对齐,张数等于 n_frames
- 材质只认
cel|lit|clay|toon|orig,拼错即抛不静默兜底。管线那份出帧台只认三种取值、其余静默落到同一分支,拿它做过的材质对照结论不成立,这条校验是防复发
任务 SLA:关标签、切后台、移动端 WebGL 失败一律判任务失败可重试;状态机要有前端出帧中这个状态,不要表现成 worker 还在跑。生帧直传复用媒体直传的 token 与重试策略。
后处理内存:32 张 1536×2560 RGBA 同时进 PIL 约 500 MB,逐帧处理做完即丢,且不与 i2v 同进程。
三渲二不套抠图(去白会吃浅灰甲),像素化仍按 ActionSpec。
不包含
- 不改帧数对账、
_lastmile、成色闸与写回 actions 的行为
- 不改 i2v 抽帧抠图算法
- 不改三渲二的计费口径与人工确认闸
- 不做多朝向出参的 UI,那条缺一个设计决定
- 不承诺跨设备逐位一致的交付帧,见下
已知代价
交付帧随用户 GPU 与驱动变化,导出包不再有同一套精灵逐位一致的保证。本方案接受这一点,换掉应用机的 CPU 与镜像体积。若之后必须可复现,另开服务端抽检重渲,不在本 issue 范围。
验收
背景
出帧目前跑在 worker 进程里:
providers/render3d/sprite.py起一个本地 HTTP 服务当 docroot,用 node + Playwright Chromium 打开bake_stage.html,mixer.setTime逐帧抓 WebGL 缓冲。docker-compose.yml的 worker 用target: runtime-render3d,node、Chromium、three 因此全部进了 worker 镜像。2026-08-26 在部署机实测:worker 镜像 2.53 GB,backend 镜像 820 MB,其中
/ms-playwright单独 656 MB;根分区 38G 已用 32G(84%),staging 的 worker 镜像同样 2.53 GB。部署机没有 GPU,这条路只能走 SwiftShader 软件光栅。本机 2026-08-04 实测:12 帧 2214×1848 软件渲染 6.37s,峰值进程树 RSS 约 1.0 GB,峰值 CPU 760%,即需要 7.6 个核;部署机只有 4 核。服务器上的真实负载(18MB GLB + 4096 贴图 + 骨骼动画)至今没有实测数据。
同一台机器上 i2v 的峰值内存问题见 #690,一期已由 #695 合入。两者是同一台 4C8G 上的两笔账,本 issue 不碰 i2v 的算法。
路线图见 #712。
问题
服务端渲染的这一段,用户那一侧有空闲 GPU,服务端没有;渲染结果本身也不参与计费与判定。继续留在服务端的代价是镜像多 1.7 GB、出帧时与 i2v 抢同一台 4C8G 的 CPU 与内存,而收益只是渲染环境统一。
方案
浏览器承担驱动循环与 WebGL 采样,worker 只做 Playwright 之后的部分。
优先把
bake_stage.html的__*钩子抽成前端可复用模块,不要另写一套 three 出帧。前端必须带走的硬约束,缺一条就会把坏帧交给后处理:
mixer.setTime(t)采样,不实时播放,同一 clip 两次要能对账到采样时刻setPixelRatio(1)、antialias: falsemax(X, Z);多朝向转相机不转模型MIN_COVERAGE(默认 0.005)判失败,禁止上传全透明帧ActionSpec.action对齐,朝向与want对齐,张数等于n_framescel|lit|clay|toon|orig,拼错即抛不静默兜底。管线那份出帧台只认三种取值、其余静默落到同一分支,拿它做过的材质对照结论不成立,这条校验是防复发任务 SLA:关标签、切后台、移动端 WebGL 失败一律判任务失败可重试;状态机要有前端出帧中这个状态,不要表现成 worker 还在跑。生帧直传复用媒体直传的 token 与重试策略。
后处理内存:32 张 1536×2560 RGBA 同时进 PIL 约 500 MB,逐帧处理做完即丢,且不与 i2v 同进程。
三渲二不套抠图(去白会吃浅灰甲),像素化仍按
ActionSpec。不包含
_lastmile、成色闸与写回actions的行为已知代价
交付帧随用户 GPU 与驱动变化,导出包不再有同一套精灵逐位一致的保证。本方案接受这一点,换掉应用机的 CPU 与镜像体积。若之后必须可复现,另开服务端抽检重渲,不在本 issue 范围。
验收
runtime,镜像内没有/ms-playwright,出帧路径不再 importbake_driver.mjsActionSpec:前端交 N 张合法 PNG,_finish产出 N 张交付帧,成色闸照跑actionsWINDUP_RENDER3D_CLIENT_BAKE=0可回到服务端 Playwright 路径