【免费下载链接】OpenDLSS-NR
A Vulkan reimplementation of NVIDIA's DLSS 5 Neural Rendering network, bit-exact against the original.
OpenDLSS-NR 是 NVIDIA DLSS 5 Neural Rendering(神经渲染)网络的 Vulkan 重新实现,与官方原版 bit-exact(逐位一致)。本文将带你绘制这张由 71 个 Block 组成的网络地图:6 级 U-net 分辨率金字塔、Swin 位移窗口注意力机制,以及位于底部的 ViT 全局注意力瓶颈,并解释它为什么是"生成式渲染器"而不是超分辨率放大。
🧩 先认识 OpenDLSS-NR:它到底在做什么?
一句话:DLSS-NR 不是超分,它是"重渲染"。网络接收引擎已经画好的一帧(一个低动态范围 proxy 代理图 + 3 通道高斯噪声 + 上一帧重投影的历史 + 5 个条件标量),输出同样是原始分辨率,但细节由注入的噪声"生成"出来,同时按 style 设置调整色调、结构与皮肤质感。
这个仓库的几个关键事实:
- 与 DLSS-NR 310.8.0 相同的 71-block Swin / ViT 网络,在 tensor core 上跑 FP8(E4M3 激活 + FP16 累加),权重共 141 MiB
- 中间结果也一致:所有 75 个 block 边界逐字节匹配原版,而不是只有最终图片对得上
- 每条跨越 kernel 边界的值都是 E4M3 字节或 IEEE half——网络的"身份"就是这一连串舍入序列,因此 bit-exact 意味着匹配舍入顺序,而非误差上界
- 完整的文档入口在 docs/README.md,网络图全貌在 docs/network.md
🗺️ 网络总览:71 个 Block 的分布地图
整个网络是一个带位移窗口的 Transformer U-net,底部放一个全局 ViT。71 个 Block 按"下采样—瓶颈—上采样"对称排布,通道数随深度翻倍再折回:
| 阶段 | Block 编号 | 通道数 | 分辨率级别 | 作用 |
|---|---|---|---|---|
| 全分辨率入口 | 0 | 32 | 原始 field | 输入适配 + 第一个窗口块 |
| 编码器 L0 | 1–4 | 32 | 1/2 | 窗口注意力 |
| 编码器 L1 | 5–8 | 64 | 1/4 | 窗口注意力 |
| 编码器 L2 | 9–14 | 128 | 1/8 | 窗口注意力 |
| 编码器 L3 | 15–22 | 256 | 1/16 | 窗口注意力 |
| 编码器 L4 | 23–30 | 512 | 1/32 | 窗口注意力 |
| ViT 瓶颈 | 31–38 | 1024 | 1/64 | 全局注意力 |
| 解码器 L4→L0 | 39–69 | 512→32 | 逐层上采样 | 与编码器 skip 连接 |
| 全分辨率出口 | 70 | 32 | 原始 field | 输出 head |
每个编码器 stage 与同级的解码器 stage 之间通过 skip connection 相连;下采样是 2x2 池化 + 通道翻倍 GEMM,上采样是通道减半 GEMM + 最近邻 2x 放大,再叠加 skip 的缩放。
分辨率金字塔是怎么算出来的
网络要求"6 次整除减半"后解码器能精确落回编码器尺寸,因此输入图像会先被 pad 成一个field(补零填充场)。比如:
| 有效尺寸 | 填充后 field | level 0 → level 5 |
|---|---|---|
| 512x512 | 576x512 | 288x256 → 12x8 |
| 1920x1080 | 1920x1152 | 960x576 → 32x20 |
这个填充不只是内存对齐——它决定了哪些 token 存在,是算术的一部分。规则细节见 docs/network.md 的 "The padded field" 一节。
🔬 解剖一个 Block:FFN → QKV → 窗口注意力 → 投影
每个 Block 的数据流(按"发布"顺序):
state x (E4M3) ├─ FFN(x) ─────────────┐ ├─ x * ffnScale ──→ (+)→ y ──→ 发布为 E4M3 │ │ └─ Proj(WindowAttention(QKV(y))) ─┐ y * attnScale ───────────────→ (+)→ out要点只有三个:
- 两次缩放 skip:
ffnScale和attnScale是逐通道学习的 f16 向量,skip 不是事后相加,而是作为首个矩阵乘的累加操作数"种进"累加器——因为舍入位置不同,结果也不同 - FP8 GEMM 是 prologue 而非 epilogue:正因 skip 要先进累加器,残差处理必须放在乘法链开头
- 一个微妙不对称:32 通道的 Block 用 FFN 的原始 f16结果做注意力 skip,更宽的 Block 用其E4M3 发布值——两种都被原生实现固定了
FFN 的形态随通道数变化,这也是理解权重布局的关键:
| 通道数 C | FFN 结构 |
|---|---|
| 32 | 稠密32→128→32,第一层后 SiLU |
| 64 / 128 / 256 | C/32条并行"专家"路径,各C→128→32,拼回 C 后接一个C→C的 W3 层(无路由,不是 MoE,是分组瓶颈 FFN) |
| 512 | 8 条 64 宽分支(64→256→64,仅 256 宽中间层有 SiLU)+512→512收缩层 |
| 1024(ViT) | 稠密1024→4096→1024 |
🪟 Swin 位移窗口:4 个相位,而不是通常的 2 个
窗口注意力的基本单元是8x8 token 的窗口。为了让窗口"看到"边界外的信息,Swin 风格的做法是移动窗口网格。这个网络用了4 个相位而非通常交替的 2 个——把对角偏移拆成 x、y 两个轴:
| phase | 窗口原点偏移 |
|---|---|
| 0 | (0, 0) |
| 1 | (-4, -4) |
| 2 | (-4, 0) |
| 3 | (0, -4) |
两条容易忽略的规则:
- 相位按 Block 推进,且解码器接着编码器的计数继续。例如 L2 有 6 个编码器块(相位 0,1,2,3,0,1),它的 6 个解码器块就从相位 2 开始。全分辨率级别上:block 0 用相位 0,block 70 用相位 1
- 越界窗口不裁剪:负原点或伸出边缘的窗口,其域外 token 是零向量,key/value 贡献为零,但它们的
prior + 0分数仍进入 softmax 分母——这不是近似,把它们 mask 掉会得到不同的结果
注意力本身用的是缩放余弦注意力:q、k 先做 f16 余弦归一化再乘逐头学习的 f32 温度,logit 变为有界余弦相似度——这正是 FP16 累加器加 3 位尾数权重能跑通的原因,也解释了为什么 softmax 前没有 max 减除。指数函数不用超越函数,而是 half 位操作技巧:把仿射值 clamp 后左移位域指数位,一步得到exp(s)(差一个 softmax 会除掉的比例常数)。
🧠 ViT 全局注意力:真正的瓶颈
Block 31–38 是 1024 通道的 8 个 ViT Block,工作在 level 5(1/64 分辨率)上,对全部 token 做全局注意力,而窗口块只看 8x8。它和窗口块有 4 处"猜不到"的差异:
| 差异点 | 窗口块 | ViT 块 |
|---|---|---|
| attention prior | 每头学习的 64x64 偏置 | 无 |
| Q 缩放 | q * norm * learned | q * norm * sqrt(32) * learned,三次独立的 half 乘法 |
| 平方和归一化 | fma(v, v, f16(high²))半精度路径 | 低位平方用 f32,高位平方先舍入到 half 再求和 |
| softmax | 权重先归一化再乘 P V | 权重不归一化:倒数事后乘到 value 累加器上,且分母要扣掉 padding 行的exp(0) |
它的指数位技巧常数也不同(有效 clamp 范围约 [-3, +3],窗口是 [-6, +6])。token 数 pad 到 64 的倍数,padding 行有零 K/V 并拿到同样的exp(0)权重,由上述分母修正移除。
由于 level 5 尺寸很小(1080p 下仅 32x20),全局注意力的"token 数"不多,但1024 通道的稠密1024→4096→1024FFN是整张网络最重的部分,加上全 token 打分矩阵,构成了性能瓶颈——这也是 PTX 路径为它专门做了 split-K 与流式变体的原因(见 docs/execution.md)。
📡 输入 16 通道 → 输出 4 通道
输入(16 个 f32 通道,按序):3 通道高斯噪声(Box-Muller,用padding 后坐标哈希)→ 常数 1 → proxy 颜色(居中(c-0.5)/8)→ 重投影的历史输出 → style id/128 → 局部色调 → 结构/皮肤/自动 mask 三元组 → 0。有效矩形之外的采样是镜像而非 clamp,但噪声哈希仍用 padded 坐标。
输出(head):block 70 的原始 f16 输出经32→4f16 GEMM 得到:
- 通道 0–2:RGB 残差,
neural = clamp(proxy + rgb/4, 0, 1) - 通道 3:时间混合 logit,
weight = clamp(sigmoid(logit) * blendScale, 0, 1),与重投影历史做 lerp
这个 4 通道的 head 是网络"生成"与"记忆"之间的逐像素仲裁者——遮挡等复杂情况就交给它自己的混合权重处理。完整帧管线(proxy、运动向量、历史重建、合成)在 docs/frame.md。
📂 进一步阅读:关键文件清单
| 内容 | 路径 |
|---|---|
| 设计总纲(表示、不变量、"无推导"清单) | docs/README.md |
| 网络全图(block 结构、窗口相位、field 规则) | docs/network.md |
| bit-exact 契约(舍入、softmax、E4M3 发布) | docs/numerics.md |
| 模型目录与权重布局(153 条张量记录) | docs/weights.md |
| Vulkan 执行、kernel 集合、无 barrier 链式调度 | docs/execution.md |
| 帧管线:从渲染图到像素 | docs/frame.md |
| 网络图录制(71 个 Block 的组装) | src/nr_graph.cpp |
| 模型加载与权重重排布 | src/nr_model.cpp |
| CPU 参考实现(bit-exact 的算术移植) | src/reference.cpp |
| GLSL 参考 kernel 集 | shaders/ |
| PTX 快速路径生成器(Swin 算术只写一次) | scripts/ptx/ |
| Filament 实时 demo(含时间历史回路) | demo/ |
✅ 小结
OpenDLSS-NR 的价值在于把一张"黑盒网络"彻底摊开:71 个 Block、6 级 U-net、4 相位 Swin 位移窗口、8 个 ViT 全局瓶颈块——每一处结构、每一个常数、每一种舍入顺序都被文档化为"被原生实现指定的语义"或"本实现的优化选择"。配合parity工具的逐字节比对,这是理解 DLSS 5 神经渲染最完整的公开解剖图。
【免费下载链接】OpenDLSS-NR
A Vulkan reimplementation of NVIDIA's DLSS 5 Neural Rendering network, bit-exact against the original.
相关推荐
突破算力瓶颈:Swin Transformer窗口注意力机制详解
突破算力瓶颈:Swin Transformer窗口注意力机制详解 你是否还在为Transformer模型的高计算复杂度而困扰?当处理高分辨率图像时,传统Tran
人工智能深度学习计算机视觉预训练swin_s3_small_224.ms_in1k背后原理:Swin Transformer移位窗口注意力机制图解
swin_s3_small_224.ms_in1k背后原理:Swin Transformer移位窗口注意力机制图解 一句话认识这个模型 🎯 swin_s3_s
突破图像上色瓶颈:DeOldify中U-Net架构与ResNet骨干网络的革命性设计
突破图像上色瓶颈:DeOldify中U Net架构与ResNet骨干网络的革命性设计 你是否好奇老照片上色工具如何精准还原历史色彩?为何有些算法会产生模糊边缘或
人工智能深度学习计算机视觉媒体生成图像处理
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考