SeedVR2-ncnn:SeedVR2 的 ncnn Vulkan C++ 实现与低显存实验 #7004
HGinkgo
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
作者:HGinkgo
更新日期:2026 年 9 月 14 日
当前版本:
v0.1.2项目仓库:HGinkgo/SeedVR2-ncnn
一、任务要求与完成情况
128x128、128x256和256x256目标v0.1.2的实验选项保留v0.1.2256x256为已验证边界项目不提供文生图、文生视频或通用视频生成工作流,当前定位是图片和视频增强。
这次移植最麻烦的地方并不是把 checkpoint 文件转换成 ncnn 格式,而是要把官方实现中的张量布局、窗口顺序和阶段间生命周期完整地搬到一个没有 Python 状态的 C++ runtime 里。SeedVR2-3B 的 DiT 有 32 个 block,hidden width 为 2560,AWA 又会根据当前视频形状生成边界窗口;只要 patch 顺序、文本 token 的重复方式或一个 reshape 轴写错,最终 RGB 仍然可以生成一张“看起来正常”的图片,但数值已经和参考路径分叉。因此项目把模型导出、算子测试、graph bridge 和最终图片比较分成了几层,而不是只用一张效果图判断移植是否成功。
二、模型架构
SeedVR2-3B 的 ncnn 推理图按阶段拆分,整体流程如下:
DiT 配置
33 / 1625602560 / 512020,每头128维3210(1, 2, 2)mmrope3d,rotation width128DiT 的 32 个 block 使用相同的 hidden/text/embedding 接口。偶数 block 使用未平移的 Adaptive Window Attention,奇数 block 使用 shifted attention。对于
t=1,h=45,w=80的基础空间形状,3×3 空间分区会产生 9 个边界可裁剪窗口;窗口顺序、文本 token 重复方式和 reverse permutation 都在导出和 runtime 中显式保留。VAE 配置
3 / 3168x4x0.9152VAE graph 中的三维时序 padding、空间 reshape 和 DepthToSpace 由项目导出脚本进行固定形状重写,并通过 C++ bridge 将 latent 布局转换为 DiT 使用的
(T*H*W) x 64patch 矩阵。当前 ncnn 版本没有完整的 Vulkan
Convolution3D路径,因此 VAE graph 在这些层上会使用 ncnn 可用的 CPU fallback;VAE 的输入输出、latent 形状和 DiT 之间的 GPU bridge 仍然按固定 contract 验证。这也是当前项目把“Vulkan 端到端可运行”和“所有算子纯 Vulkan”区分开的原因。模型文件组织
原始官方 checkpoint 的 DiT 部分约有 3.39B 参数,模型权重不提交到 Git 仓库,而是作为独立模型包分发。模型包包含 manifest 和完整 SHA-256 校验,运行时会在初始化阶段检查 graph、记录数和文件完整性。
三、C++ Runtime 与工程实现
导出阶段使用 TorchScript/PNNX 生成基础 graph,再由项目脚本处理 ncnn 不直接表达的边界。运行时不再依赖 Python,而是由 C++ 负责输入预处理、graph 选择、latent/patch 布局转换、conditioning 载入、采样循环以及输出写入。
VAE、DiT 和 decode 采用阶段化生命周期。VAE encode 完成后释放 encode graph,DiT 阶段复用同一个
ImageInferenceSession,decode 阶段再加载 decode graph。session 内部共享 Vulkan pipeline cache,连续处理多张图片时可以避免重复创建相同 pipeline。默认 resident DiT 路径会在 DiT 阶段开始时加载 32 个 block。为验证低显存策略,实验分支增加了 streamed residency:当前 block 加载完成后立即执行,执行结束后释放其 Net 和权重 allocator,再继续下一个 block。该策略不改变模型文件格式,也不改变 block 输入输出接口。
BF16 storage 则保持模型包中的 FP32 权重不变,只在 Vulkan runtime 中切换 storage representation。MMRoPE rotation 数据仍以 FP32 保存,DiT 输入输出边界也保留 FP32 caller contract。
四、性能与资源收益
4.1 固定形状产品路径
发布线的低分辨率目标为
128x128、128x256和256x256。256x256图像与连续 36 帧256x256RGB AVI 均已在 RTX 3090 上完成端到端验证,没有出现 Vulkan allocation failure。视频基线为 36 帧、12 fps、RGB24 AVI,输出文件可逐帧读取。同一进程内复用 session 和 pipeline cache 可以明显减少后续调用的初始化开销;这些优化只改变资源生命周期,不改变 graph 或模型权重,因此默认 FP32 输出保持与固定基线一致。
4.2 Resident 与 streamed DiT(独立实验)
下面是同一 RTX 3090、同一 FP32 模型包、同一输入和同一推理参数下的 256×256 单图比较。两次运行都使用
--steps 1,profile 包含从输入读取到输出写入的完整路径。10.0 s59.3 s1638 MiB36.0 s64.5 s1609 MiBstreamed 模式的速度代价来自 32 个 block 的重复加载;当前 RSS 只下降约 29 MiB,不能据此宣称 device-local Vulkan 显存已经按同样幅度下降。现有 profile 记录的是进程 RSS 和阶段时间,后续还需要补充 allocator 级峰值统计,才能精确描述 Vulkan heap 的收益。该实现位于独立 worktree,未进入
main的v0.1.2发布包。4.3 BF16 storage(独立实验)
BF16 storage 不需要第二套模型文件。固定
256x256acceptance 输入的结果如下:cbfdea0a27e7995887353764bc530b2ce9b01d1c94802863f28228186ba7c68bfd909aafc8c7ae9adb2b361846ae349794b671a1e77d1a8157cebbec61809ab1BF16 相对 FP32 的误差为:
因此 BF16 storage 是一个面向显存/带宽的低精度实验选项,不是 FP32 的无损替代。它位于独立 worktree,未进入
main的v0.1.2发布包;需要严格复现发布基线时,应使用默认 FP32 路径。五、正确性验证
验证采用固定模型包、固定输入、固定目标尺寸、固定 GPU 和固定推理参数,并同时检查运行状态、输出文件和数值差异。
5.1 端到端图像与视频
resident 与 streamed 的 256×256 stock 推理均满足:
两次输出 SHA-256 完全一致:
文件逐字节比较结果为 identical。这个结果关闭了 streamed block 生命周期对 FP32 stock 输出的回归风险,但不等于已经关闭更大分辨率的显存和质量风险。
固定 36 帧视频基线的输出契约为
256x256、12 fps、36 个可读取帧,输出为 RGB24 AVI;运行返回seedvr2-video-inference: ok,GPU memory 在结束后回到空闲状态。5.2 VAE、DiT 和算子级验证
固定
128x128VAE 测试得到 latent shape1x16x16x16,重建 shape 为3x1x128x128。独立 DiT block 测试检查了视频和文本残差的布局、有限值和自定义 AWA 算子;VAE→DiT→VAE bridge 测试完成了 latent patch 化、MMRoPE、AWA、输出投影、latent unpatch 和解码。这些测试将 CPU reference、ncnn graph 输出和 Vulkan 输出分阶段比较,避免只依赖最终 RGB 图像来判断问题位置。公开仓库保留快速 runtime contract;大型模型导出、GPU 数值对比和长时间性能测试保留在本地验收记录中。
5.3 query-chunk attention 的边界
独立的
SeedVR2QueryChunkAttention实验已经在长度 257、query block 128 的 Vulkan operator-level 测试中与 stock MultiHeadAttention 对齐:但完整 256×256 图像质量门仍未通过,当前 stock/custom 比较为:
因此 query-chunk 仍保留在独立实验 worktree 中,不进入默认产品路径。operator-level 一致只能证明局部算子行为接近,不能替代完整 VAE/DiT 图像质量验证。
六、构建与复现
仓库源码不包含模型权重。可以从 ModelScope 模型页面 下载模型包:
完整复现时建议先确认设备和 Vulkan ICD,而不是直接运行程序:
vulkaninfo --summary应该显示NVIDIA GeForce RTX 3090和driverName = NVIDIA。如果只看到 Mesa llvmpipe,后续的时间、显存和图像结果都不能作为本项目的 NVIDIA 验收结论。一次完整验收至少应记录以下信息:模型包 manifest 校验结果、GPU 名称和驱动版本、命令行参数、进程退出码、
seedvr2-image-inference: ok、vkAllocateMemory failed出现次数、profile 阶段时间、输出 SHA-256,以及与参考输出的 MAE、最大绝对误差和不同 RGB 字节数。对于 resident/streamed 或 FP32/BF16 这种 A/B 比较,两个运行必须使用同一输入、同一模型包和同一 GPU,不能把不同导出版本的结果混在一起。CPU 构建只需要 CMake 和 C++17 编译器:
cmake -S . -B build \ -DSEEDVR2_ENABLE_VULKAN=OFF \ -DSEEDVR2_BUILD_TESTS=ON \ -DCMAKE_BUILD_TYPE=Release cmake --build build --parallel ctest --test-dir build --output-on-failureVulkan 构建需要 Vulkan SDK、Vulkan-capable GPU 和驱动:
cmake -S . -B build-vulkan \ -DSEEDVR2_ENABLE_VULKAN=ON \ -DSEEDVR2_BUILD_TESTS=ON \ -DCMAKE_BUILD_TYPE=Release cmake --build build-vulkan --parallel ctest --test-dir build-vulkan --output-on-failure单图运行示例:
启用实验性的 streamed DiT:
如果只想检查模型包而不初始化 Vulkan,可以使用:
输出文件可以用下面的方式做最基本的复核:
cmp只适用于要求逐字节一致的 FP32 A/B。BF16 或多步采样应同时报告数值误差,不应仅凭 SHA 不同就判断运行失败。默认采样为单步 endpoint 路径。
--steps N可以显式启用实验性的多步 Euler 采样;多步采样会改变结果和运行时间,不属于默认单步质量承诺。--vae-tile-size也属于实验选项,VAE 全局 attention 尚未证明可以在任意空间分块下保持等价。七、效果展示
以下图片来自仓库当前发布线的 RTX 3090 Vulkan CLI。左侧为官方 SeedVR2 示例视频中的一帧,经裁剪并降采样为
64x64输入;右侧为本项目在固定256x256路径生成的输出。同一输入和输出的并排动画预览:
官方视频样例
输入采用官方 SeedVR_VideoDemos 数据集 的
1_1_lq.mp4,原视频为640x480、121 帧。经 SeedVR2-ncnn 推理后,得到256x256、121 帧的 RGB24 视频,处理完整且输出可正常播放;结果 SHA-256 为5b16698d7bbafdc00aa4ee87134ea82dcc8976dde59d78ff5db21054c89ae8ac。官方视频输出预览:
完整输出文件:
1_1_lq.mp4的 ncnn 输出 AVI发布前需将本地生成的 GIF 和 AVI 上传到仓库
assets/目录;上面的链接即为 Discussion 使用的 GitHub 路径。示例图片和官方输入视频来自 SeedVR2 官方示例空间 及其公开数据集;发布仓库只提交轻量的单帧输入、输出和对比 GIF,不把完整视频素材打进 Git 历史。示例素材的版权和使用条件以原作者及数据集说明为准。
八、当前边界与后续计划
当前发布线已经验证到
256x256,但没有把更高分辨率写成质量承诺。高分辨率路径需要同时考虑 VAE global attention、DiT token 数量、模型权重驻留和 pipeline 缓冲,不能只把 CLI 上限改成720x1280就视为完成。若后续开放更高面积,运行时会沿用现有 Vulkan 分配错误路径;显存不足时直接退出并报告vkAllocateMemory failed,不做自动降级或伪造成功。本地曾有独立的
720x1280pack1 FP32 low-level harness 完成过 VAE→DiT→VAE 全链路,记录的 wall-clock 约为 326 秒、最大 RSS 约 7.79 GiB,Vulkan heap budget 约 23676 MiB;但这不是当前产品 CLI 的验收记录。另一次使用不同动态 graph/package 的资源边界测试在接近 24 GiB 时出现:两类记录说明高分辨率结果对 graph、allocator 和 harness 口径敏感。当前准确结论是:720p 在本项目中尚未作为产品功能支持或验证,不能根据旧 harness 的成功或失败单独发布 720p 承诺。
后续是否扩大输入尺寸,取决于独立的高分辨率 graph 和真实 RTX 3090 端到端验收;当前不把旧 harness 的成功或失败包装成发布能力。query-chunk attention 仍需先完成完整图像质量门,再考虑用于降低高分辨率的 attention 峰值。
九、参考链接
模型权重、官方示例素材和第三方依赖遵循各自许可证。本项目不主张拥有示例素材的版权,也不代表官方模型作者或 ncnn 项目背书。
All reactions