Rolaand-Jayz/FSR-4.1.0-Upscaling-RE

GitHub: Rolaand-Jayz/FSR-4.1.0-Upscaling-RE

从第一性原理出发完整逆向并逐字节重建了 AMD FSR 4.1.0 的神经网络上采样流水线,填补了其内部架构与权重数据的公开分析空白。

Stars: 0 | Forks: 0

# FSR 4.1.0 — 逆向工程 我们从第一性原理出发,按位重建了一个 DLL——并与原版做到了逐字节匹配。接着,我们从原始的 x86-64 反汇编代码中,解码了主 provider DLL 的调度流水线。 AMD 的 FidelityFX Super Resolution 4.1.0 以编译好的 Windows DLL 形式发布,其中包含了一个神经网络图像放大器。该网络的权重是不透明的二进制数据块 (blob)。其流水线架构处于未公开状态。着色器调度序列则隐藏在层层 API 抽象的背后。除了 AMD 内部,从未有人公开发布过关于这个东西实际*到底在干什么*的完整结构分析——更不用说通过从零开始重建来证明他们理解它了。 我们做到了。然后我们证明了这一点: 1. **数据 DLL** — 从重建的 C 语言源代码重新编译,重新嵌入了提取出的权重数据,并在链接后对 PE 头进行了补丁操作,最终生成的二进制文件其 MD5 与原版完全一致: ``` cb1aa61c71c33b25549ed59c1551d661 fsr_data_final.dll (our rebuild) cb1aa61c71c33b25549ed59c1551d661 original (AMD's binary) ``` 2. **Provider DLL** — 反汇编了 Ghidra 无法反编译的 PSO 创建函数 (`FUN_180025990`)。解码了跳转表、标志索引表和 Pass 描述符表。将所有 30 个唯一的着色器数据块(通过 MD5 哈希验证)映射到了它们对应的 Pass 索引。从 LLVM IR 中提取了完整的资源绑定布局。 本代码仓库是该工作的完整记录:包括分析过程、走过的死胡同、方法论、提取出的神经网络权重、完整的流水线规格说明,以及用于独立验证一切的工具。 ## 简短版本 | 发现 | 详情 | 证据 | |:---|:---|:---| | **编目并分类了 602 个 DXBC 着色器数据块** | 对每个嵌入的 compute shader 进行了完整枚举 | ✅ 已验证 — 自动化提取 + 人工审计 | | **将 30 个唯一的着色器数据块映射到了 30 个 Pass** | MD5 已验证 — 所有 Pass 中零重复 | ✅ 已验证 — 二进制哈希比对 | | **提取了 6 个权重数据块,每个 131,072 字节** | 5 个完全相同 (质量/平衡/性能/超高性能/原生),1 个唯一 (DRS) | ✅ 已验证 — MD5 哈希比对 | | **权重格式已完全解码** | 7,208B FP16 偏置 → 122,880B FP8 (uint8) 权重 → 888B 额外 FP16 → 96B 填充 | ✅ 已验证 — 格式已解析,值已验证,所有数据块中的偏移量匹配 | | **流水线:27 个主 Pass + 3 个条件 Pass** | 27 轮主循环 + RCAS + SPD AutoExposure + Debug View | ✅ 已验证 — 调度函数中硬编码的循环次数 + Ghidra 反编译 | | **所有 Pass 均使用 (32,1,1) 线程组** | Wavefront 宽度的 1D 调度 | ✅ 已验证 — LLVM IR `!dx.numthreads` 元数据 | | **完整的资源绑定映射** | 识别出 9 个具有语义含义的寄存器空间 | ✅ 已验证 — 对所有 30 个 LLVM IR 数据块进行了 createHandle 分析 | | **Constant buffer 布局已解码** | 每个 Pass 5-6 个寄存器 × 4 个浮点数 (80-96 字节);奇/偶对模式 | ✅ 已验证 — 来自 LLVM IR 的 cbufferLoadLegacy 寄存器索引 | | **架构已重构** | 4.0.2 的 Pre/Body/Post 被替换为 Encoder/Orchestration/Body/Decoder 流水线 | ⚠️ 仅限静态 — 由着色器分类 + 偏移映射推断得出 | | **权重加载方式已更改** | 4.0.2 将权重嵌入着色器中;4.1.0 使用带有动态加载的 InitializerBuffer | ✅ 已验证 — 与基于 MIT 许可证的 4.0.2 源代码进行结构对比 | | **逐字节一致的 DLL 重建** | 重建 C 源代码 → MinGW 交叉编译 → PE 链接后补丁 → MD5 匹配 | ✅ 已验证 — `cb1aa61c71c33b25549ed59c1551d661` | | **PSO 创建函数已解码** | 通过原始 x86-64 反汇编解码 FUN_180025990:跳转表 + 标志索引表 + 30 条目的描述符表 | ✅ 已验证 — objdump 反汇编 + PE 区段解析 | ### 证据标签 - ✅ **已验证** — 通过多种独立方法确认(哈希匹配、二进制分析、交叉引用) - ⚠️ **仅限静态** — 由反汇编/反编译推断得出;未通过运行时插桩确认 ## Main DLL:流水线调度分析 主 provider DLL (`amd_fidelityfx_upscaler_dx12.dll`, 15.6MB) 是系统的另一半。`fsr_data.dll` 负责存储权重,而 provider DLL 则负责运行神经网络。我们通过 Ghidra 反编译和原始的 x86-64 反汇编追踪了完整的调度流水线。 ### 调度架构 核心调度函数 (`FUN_18000d5b0`) 运行了一个硬编码的 27 次迭代循环: ``` // Decompiled from Ghidra lVar24 = 0x1b; // 27 do { dispatch_per_pass(context, pass_state, threadsX, threadsY); lVar24--; } while (lVar24 != 0); ``` 在主循环之后,会执行三个条件 Pass: - **RCAS** (索引 27/0x1b) — 锐化 - **SPD AutoExposure** (索引 28/0x1c) — 自动曝光计算 - **Debug View** (索引 29/0x1d) — 调试可视化 ### PSO 创建:FUN_180025990 Ghidra 未能反编译此函数(共反编译了 344 个函数;其中不包含此函数)。我们从原始二进制文件中对其进行了反汇编,并解码了: | 结构 | RVA | 详情 | |:---|:---|:---| | 跳转表 | `0x2acac` | 30 个调度到每个 Pass 代码的条目 | | 标志索引表 | `0x1bfc50` | 64 个将配置标志映射到描述符索引 (0-29) 的条目 | | Pass 描述符表 | `0x115cf0` | 30 个条目 × 296 字节 (`imul rcx, rax, 0x128`) | 每个描述符条目包含:着色器数据块大小 (DWORD)、DXBC 着色器指针 (QWORD),接着是每组 40 字节的 7 个绑定组。 ### 30 个唯一的着色器数据块 所有 30 个 Pass 着色器都是唯一的,已通过 MD5 哈希验证: | Pass | 名称 | 大小 | MD5 | |------|------|------|-----| | 0 | pass_0 | 19,820 | fa813b1501c7 | | 1 | pass_1 | 19,884 | 98e3a4b72437 | | 2 | pass_2 | 21,648 | d99b11628f3b | | 3 | pass_3 | 21,584 | 76fdac8ca3c1 | | 4 | pass_4 | 23,336 | abb9cec4c47d | | 5 | pass_5 | 26,276 | cb3ad940c94a | | 6 | pass_6 | 23,404 | 37998fc070d8 | | 7 | pass_7 | 26,208 | 6859bda414df | | 8 | pass_8 | 21,740 | a52c653d1f5c | | 9 | pass_9 | 21,676 | 564efec2abd2 | | 10 | pass_10 | 24,856 | 4a85f5300bf4 | | 11 | pass_11 | 24,788 | 0114b73c945c | | 12 | pass_12 | 21,984 | 33e2e5c24e0d | | 13 | pass_13 | 21,920 | c287b4976479 | | 14 | pass_14 | 18,500 | 8b335e65c091 | | 15 | pass_15 | 18,436 | 3829ef86bf37 | | 16 | pass_16 | 20,456 | 3fbb8b9bd3ad | | 17 | pass_17 | 20,388 | e144590c77dd | | 18 | pass_18 | 20,552 | 7756f6dd0508 | | 19 | pass_19 | 20,480 | aeaf1b44d0b2 | | 20 | pass_20 | 26,276 | 4d61c10addc6 | | 21 | pass_21 | 26,208 | c12ca5dfa20c | | 22 | pass_22 | 23,404 | c6930a995aab | | 23 | pass_23 | 19,884 | 5943b34bd00e | | 24 | pass_24 | 19,820 | a00a487f3be2 | | 25 | pass_25 | 23,336 | 7e95f96376a7 | | 26 | pass_26 | 21,648 | f1079dce7c99 | | 27 | rcas | 21,584 | 5c6397e93889 | | 28 | spd_autoexposure | 21,740 | 1f4a33f858b4 | | 29 | debug_view | 21,676 | 4bf4d0a6886c | **唯一:30/30,重复:0** ### 资源绑定映射 每个 Pass 都使用相同的 UAV 和 CBV 布局。SRV 的数量因 Pass 类型而异 (5-8)。 **通用资源(所有 30 个 Pass):** | 类型 | 寄存器 | 空间 | 用途 | |------|----------|-------|---------| | SRV | 0 | 18 | 神经网络权重 (来自 fsr_data.dll) | | SRV | 1 | 0 | 输入颜色 | | SRV | 2 | 1 | 运动矢量 / 重投影 | | SRV | 4 | 3 | 历史 / 时间 | | SRV | 5 | 4 | 曝光 / 亮度 Mipmap | | UAV | 0 | 11 | 暂存缓冲区 (取决于分辨率) | | UAV | 1 | 0 | 输出颜色 | | UAV | 2 | 3 | 临时 / 循环输出 | | CBV | 0 | 1 | Constant buffer (80-96 字节) | | Sampler | 0 | 0 | 点采样钳制 | **条件资源(特定 Pass):** | 类型 | 寄存器 | 空间 | 存在于 | |------|----------|-------|------------| | SRV | 3 | 2 | 亮度/遮罩 (除 Pass 10-15 外的所有 Pass) | | SRV | 6 | 6 | 额外特征图 (Pass 8, 9, 18, 19, 28, 29) | | SRV | top | 17 | 中间数据 (具有 7+ 个 SRV 的 Pass) | ### Constant Buffer 布局 每个 Pass 从单个 CBV (寄存器 0,空间 1) 中读取 5 或 6 个寄存器。每个寄存器是 4 × float32 = 16 字节。 | 寄存器 | 偶数 Pass ("pre") | 奇数 Pass ("post") | |-----|---------------------|---------------------| | 0 | 调度维度 / 逆分辨率 | 调度维度 / 逆分辨率 | | 1 | 抖动 / 子像素偏移 | 抖动 / 子像素偏移 | | 2 | 缩放 / 变换参数 | 缩放 / 变换参数 | | 3 | — (未访问) | 额外参数 | | 4 | 额外配置 | 额外配置 | | 5 | 额外配置 | 额外配置 | - 偶数 Pass:5 个寄存器 × 16B = **80 字节** - 奇数 Pass:6 个寄存器 × 16B = **96 字节** ### Pass 配对结构 27 个主 Pass 构成了 13 pre/post 对 + 1 个独立 Pass: | 对 | Pre Pass | Post Pass | SRV | |------|----------|-----------|------| | 0 | pass_0 (偶数) | pass_1 (奇数) | 6 | | 1 | pass_2 | pass_3 | 7 | | 2 | pass_4 | pass_5 | 6 | | 3 | pass_6 | pass_7 | 6 | | 4 | pass_8 | pass_9 | 8 | | 5 | pass_10 | pass_11 | 5 | | 6 | pass_12 | pass_13 | 5 | | 7 | pass_14 | pass_15 | 5 | | 8 | pass_16 | pass_17 | 6 | | 9 | pass_18 | pass_19 | 7 | | 10 | pass_20 | pass_21 | 6 | | 11 | pass_22 | pass_23 | 6 | | 12 | pass_24 | pass_25 | 6 | | 独立 | pass_26 | — | 7 | 这种奇/偶拆分对应于 AMD 的命名惯例:`pass0` + `pass0_post`、`pass1` + `pass1_post` 等。 ### Pass 对称性 相同的数据块大小表明存在自编码器对称性(编码器/解码器镜像): - pass_0 ↔ pass_24 (各 19,820 字节) - pass_1 ↔ pass_23 (各 19,884 字节) - pass_4 ↔ pass_25 (各 23,336 字节) - pass_5 ↔ pass_20 (各 26,276 字节) - pass_6 ↔ pass_22 (各 23,404 字节) - pass_7 ↔ pass_21 (各 26,208 字节) 完整分析:[`reports/main_dll_analysis.md`](reports/main_dll_analysis.md)。机器可读规格说明:[`spec/pipeline_spec.json`](spec/pipeline_spec.json)。 ## 运行时捕获:三次尝试,没有银弹 运行时捕获——即通过实时观察放大器的执行来确认我们的静态分析——是我们投入了大量工程精力的一个主要目标。我们构建并部署了三种不同的方法,在运行于 Proton/VKD3D 的 CachyOS 上通过《FF7 重生》进行测试。这三种方法都遇到了阻碍。 这不是一个记录失败的章节。它记录了产生实用工具的真实工程工作,尽管主要目标(在运行时确认 27 次 Pass 调度序列)并未实现。这些工具是存在的,它们可以编译,并且就在这个代码仓库中——如果你能解决 Proton 的分层问题,它们就能正常工作。 ### 尝试 1:FFX 代理 DLL (`ffx_capture_proxy.c`) **方法:** 用一个拦截 5 个导出的 FFX API 函数(`ffxConfigure`、`ffxCreateContext`、`ffxDispatch`、`ffxDestroyContext`、`ffxQuery`)的代理,替换 AMD 的 `amd_fidelityfx_upscaler_dx12.dll`。在 API 层面记录参数和原始描述符字节。 **构建方式:** 在 Linux 上使用 MinGW 交叉编译 → `ffx_proxy.dll` **状态:** ⚠️ 已编写并编译。未部署用于实时捕获(需要重命名原始 DLL 并将代理放入游戏目录中,我们在分析阶段并未尝试此操作)。该代理捕获 API 层面的参数——调度描述符、质量预设、上下文创建——但不捕获实际的 GPU 资源绑定。它可以确认发生了*多少次*调度,但无法确认每次调度访问了*什么数据*。 ### 尝试 2:Vulkan LD_PRELOAD 垫片 (`fsr4_capture.c`) **方法:** 一个轻量级的 `.so`,通过 `LD_PRELOAD` 钩取 `vkCmdDispatch`、`vkCmdBindDescriptorSets` 和 `vkCmdBindPipeline`。没有帧捕获,也没有 RenderDoc 的开销——只有一个调度日志。专为 FSR4 的 27 次以上的 compute pass 设计,因为完整捕获会导致游戏卡死。 **构建方式:** 在 CachyOS 上执行 `gcc -shared -fPIC -O2 -o fsr4_capture.so fsr4_capture.c -ldl` **部署路径:** `/mnt/workdrive/fsr-re/runtime-capture/fsr4_capture.so` **启动选项:** ``` LD_PRELOAD=/mnt/workdrive/fsr-re/runtime-capture/fsr4_capture.so PROTON_FSR4_UPGRADE=1 DXIL_SPIRV_CONFIG=wmma_rdna3_workaround WINEDLLOVERRIDES=version=n,b %command% ``` **结果:** ❌ 垫片已加载(通过 `dispatch_log.txt` 中的 `[INIT] FSR4 capture shim loaded` 确认),但未产生任何调度数据。`LD_PRELOAD` 钩子传播到了 Proton 的 wine 进程中,但 `vkCmdDispatch` 拦截从未触发。可能的原因:VKD3D-Proton 的 Vulkan 调度所经过的代码路径并未路由通过标准的 `vkCmdDispatch` 符号,或者该钩子的 `dlsym` 解析在 Proton 加载器内部失败了。 **经验教训:** `LD_PRELOAD` 确实能够留存进入 Proton 的 wine 进程(初始化消息证明了这一点),但通过 VKD3D 转换来钩取 Vulkan 调用是不可靠的。该钩子能在宿主进程中触发,但不一定能在 wine 的 Vulkan 包装器内触发。 ### 尝试 3:RenderDoc 完整捕获 **方法:** 通过隐式层进行标准的 RenderDoc Vulkan 捕获。需要 `ENABLE_VULKAN_RENDERDOC_CAPTURE=1`(注意不是 `ENABLE_VULKAN=1`——那是一个消耗了一次调试会话的死胡同)。 **启动选项:** ``` ENABLE_VULKAN_RENDERDOC_CAPTURE=1 RENDERDOC_CAPTUREFILE=/mnt/workdrive/fsr-re/runtime-capture/fsr4_ff7r VKD3D_DEBUG=trace VKD3D_SHADER_DUMP_PATH=/mnt/workdrive/fsr-re/runtime-capture/vkd3d-shaders %command% ``` **结果:** ❌ RenderDoc 通过 VKD3D 钩取了每一次 Vulkan 调用。FSR4 每帧会调度 27 次以上的 compute pass。捕获文件变得极其巨大,游戏随之卡死,并最终在 120 秒的超时后崩溃。完整的 RenderDoc 捕获与 FSR4 的调度密度不兼容。 **经验教训:** FSR4 的神经网络推理对于全帧捕获工具而言负担太重了。轻量级的方法(垫片、日志)是唯一可行的途径。 ### 附带发现:VKD3D 着色器转储 在 RenderDoc 和垫片尝试期间设置 `VKD3D_SHADER_DUMP_PATH` 生成了着色器转储——但它们是**辅助后处理着色器**(类似 RCAS 的锐化、深度/运动自适应 Pass),而不是 FSR4 神经网络的核心。核心模型着色器通过 VKD3D 不转储的不同路径进行编译,或者它们是直接嵌入在 DLL 中的预编译 SPIR-V。 ### 诊断工具 我们编写了一个完整的诊断脚本 (`scripts/capture/diagnose_capture.sh`),它会检查所有的先决条件:RenderDoc 库、Vulkan 层注册、垫片编译情况、代理 DLL 状态、FF7R DLL 检测以及 OptiScaler 的存在情况。它还会为这三种方法打印出修正后的捕获说明。 ### 这对项目意味着什么 运行时捕获的缺失是客观存在的。我们的流水线分析现在来自于: - ✅ 对 C++ 调度函数的 Ghidra 反编译(已确认 27 次迭代循环) - ✅ 对 PSO 创建函数的原始 x86-64 反汇编(Ghidra 无法将其反编译) - ✅ 通过 MD5 哈希映射到 Pass 索引的 30 个唯一的着色器数据块 - ✅ 从 LLVM IR 提取的完整资源绑定布局 - ✅ 从 cbufferLoadLegacy 模式提取的 Constant buffer 寄存器映射 - ⚠️ 运行时调度序列未被确认(Proton 阻挡了所有捕获方法) 逐字节一致的 DLL 重建证明了我们的**数据提取**是正确的。二进制分析证明了我们的**流水线结构**是正确的。唯一未被确认的部分在于,实际的运行时执行是否与静态分析相符——而 Proton 使得这一点无法验证。 ## 我们没有做的工作 诚实的文档意味着展示存在的空白: 0. **没有进行帧生成分析。** FSR 4.1.0 同时发布了时间放大器和帧生成功能。本次 RE(逆向工程)仅涵盖**放大器**。帧生成是一个独立的流水线,拥有自己的着色器、自己的调度逻辑以及自己的资源管理。我们没有对其进行分析。这个范围是刻意设定的:放大器是机器学习组件,理解它是我们的目标。 1. **没有运行时验证。** 这款游戏需要 Proton 才能运行。Proton 的 VKD3D 转换层吸收了所有的钩子、垫片和捕获工具。我们无法通过观看代码执行来确认静态分析。这是目前最大的可信度空白。我们构建并部署了三种独立的捕获方法——均被 Proton 的 Vulkan 转换层所阻挡。**需要拥有原生 Windows + D3D12 访问权限的人进行运行时验证,以填补这一空白。** 请参阅下方的[验证状态](#validation-status--call-for-collaborators)。 2. **没有对 provider DLL 进行逐字节一致的重建。** 15.6MB 的 provider DLL 包含编译后的 C++ 代码、链接库以及 DXBC 着色器容器。要实现逐字节复现,需要 AMD 精确的构建环境、编译器版本和着色器编译器。我们转而通过结构分析证明了我们的理解。 3. **CBV 字段语义部分未知。** 我们知道访问了哪些寄存器以及提取了哪些组件 (x/y/z/w)。我们不知道每一个浮点值的确切语义含义。 4. **Root signature 的二进制格式未完全解码。** 描述符条目中的绑定组是 FFX 内部结构,而不是原始的 D3D12_ROOT_PARAMETER 结构体。我们转而解码了着色器级别的资源绑定。 5. **逐指令的推理操作未完全解码。** 我们解码了激活函数 (ReLU,通过 DXIL IR 中的 FMax)、FP8 权重解码机制(连贯的原子缓冲区 I/O)、Pass 复杂度分级以及时间状态流(历史缓冲区反馈)。我们**并没有**在单条指令级别解码每一次矩阵乘法或卷积。其高层架构已被理解;每个 Pass 内部的逐像素算术运算是由 IR 模式推断出来的,而不是逐条指令追踪出来的。 ``` fsr-re/ ├── README.md You are here. Narrative + findings. ├── LEGAL.md RE methodology, AMD history, legal positioning. ├── LICENSE MIT License. ├── VALIDATION_STATUS.md Honest assessment of what is proven vs inferred. ├── verification-report.json Machine-readable verification results. ├── .gitignore │ ├── docs/ Technical documentation. │ ├── IMPLEMENTATION_GUIDE.md Complete implementation guide for the neural upscaler. │ ├── activation-lut-analysis.md FP8 decode and activation function analysis. │ ├── adversarial-review-2.md Adversarial review — challenges assumptions. │ ├── architecture.md Network topology, layer details, channel flow. │ ├── extra-params-analysis.md Analysis of the 444 extra FP16 values. │ ├── methodology.md Full methodology narrative — including dead ends. │ ├── offset-mapping.md Complete tensor offset table. │ ├── pipeline-dispatch.md Provider DLL dispatch analysis. │ ├── shader-internals.md Neural architecture + FP8 decode analysis. │ ├── static-analysis.md Ghidra decompilation findings. │ └── weight-extraction.md How weights were found and extracted. │ ├── spec/ Machine-readable specifications. │ ├── tensor-map.json Complete tensor offset table (78 tensors). │ ├── blob-format.json Binary layout specification. │ ├── pipeline_spec.json Full 30-pass pipeline spec. │ └── shader_analysis.json 28-pass shader analysis. │ ├── reports/ Analysis reports and data. │ ├── 00-final-re-report.md Complete RE report — the authoritative document. │ ├── main_dll_analysis.md Provider DLL dispatch analysis. │ ├── architecture-map-v410.md Architecture mapping. │ ├── provider-diff-report.md Provider layer diff (4.0.2 vs 4.1.0). │ ├── ml2code-runtime-diff.md Operator comparison. │ ├── tensor-verification-report.md Offset-map verification status. │ ├── pass-catalog.json Full pass catalog (machine-readable). │ └── v410_independent_offsets.json Independent offset map. │ ├── rebuild/ Bit-identical DLL reconstruction. │ ├── README.md Build instructions, verification proof. │ ├── fsr_data.c Reconstructed C source from disassembly. │ ├── fsr_data.def PE export definitions. │ ├── pe_patcher.py Post-link PE patcher. │ ├── pe_patcher_v2.py Section-size-aware PE patcher. │ ├── build.sh Full build + verify script. │ ├── fsr_data_prepatch.dll Before PE patching (893,019 bytes). │ └── fsr_data_final.dll After patching — bit-identical (893,388 bytes). │ ├── extracted/ Weight blobs — the neural network data. │ ├── v410_initializers/ 6 blobs × 131,072 bytes (4.1.0 weights). │ ├── v402_initializers/ 7 blobs (4.0.2 weights for comparison). │ ├── fp8_initializers/ Raw FP8 initializer blobs. │ └── fp8_weights/ Per-tensor extracted weights (v402 + v410). │ ├── scripts/ Analysis and verification tools. │ ├── dll_analysis.py DLL structure enumeration. │ ├── extract_blobs.py Weight blob extraction from PE sections. │ ├── weight_encoding.py FP8/FP16 encoding analysis. │ ├── fp8_extract.py FP8 weight extraction and validation. │ ├── parse_weights.py Weight blob parsing and display. │ ├── weight_compare.py Cross-blob comparison and hashing. │ ├── parse_offsets.py Tensor offset extraction. │ ├── layer_diff.py Layer-by-layer diff between blobs. │ ├── parse_v410_dxil.py DXIL shader parsing for 4.1.0. │ ├── trace_cbuffer.py Constant buffer layout tracing. │ ├── verify.py Verification suite. │ ├── verify_tensor_offsets.py Tensor offset validation. │ ├── disasm_all_dxil.py Mass DXBC → DXIL disassembly. │ └── capture/ Runtime capture scripts. │ ├── capture-tools/ Runtime capture tooling. │ ├── analyze_capture.py Post-capture analysis pipeline. │ ├── extract_dispatches.py Dispatch extraction from RenderDoc captures. │ └── capture-guide.md Capture method documentation. │ ├── runtime-capture/ Capture artifacts from runtime attempts. │ ├── dispatch_log.txt Vulkan LD_PRELOAD shim log. │ └── fsr4_capture.so Compiled dispatch shim. │ ├── tools/ Capture tools (written, deployed, not fully successful). │ ├── README.md Setup and usage guide. │ ├── ffx_capture_proxy.c FFX API capture proxy. │ ├── ffx_d3d12_capture.c D3D12 command capture. │ ├── fsr4_capture.c Vulkan dispatch shim for Proton/Linux. │ └── setup_capture.sh Build + inject script. │ └── Not in public repo (excluded via .gitignore — see LEGAL.md): ├── build/ DXBC blobs + LLVM IR (1187 .ll files). Derived │ from proprietary shaders. ├── ghidra-decompile/ 344 decompiled C functions. Proprietary data. └── ghidra-project/ Ghidra project database. Proprietary data. ``` ## 验证状态与招募合作者 本项目在**静态分析层面已完成**——关于架构、权重、Pass 结构和资源绑定的每一项声明,都有本代码仓库中的二进制分析证据作为支撑。逐字节一致的 DLL 重建证明了数据提取是正确的。 但是**静态分析并不等同于运行时证明**。27 次 Pass 的调度序列、Constant Buffer 的值以及执行期间实际的 GPU 资源绑定,从未被实时观察过。我们尝试了三种捕获方法(FFX 代理 DLL、Vulkan LD_PRELOAD 垫片、RenderDoc 完整捕获)——均在 Linux 上被 Proton 的 VKD3D 转换层所阻挡。 ### 需要做什么 在**原生 Windows 环境下结合 D3D12** 进行运行时验证将填补这一空白。具体包括: 1. **捕获 27 次 Pass 的调度序列** — 在原生 Windows(无 Proton 层)上使用 PIX、RenderDoc 或 D3D12 捕获工具确认循环次数和 Pass 顺序。 2. **验证 Constant Buffer 内容** — 确认每个 Pass 的 CBV 值与静态分析相符。 3. **确认权重缓冲区绑定** — 验证 InitializerBuffer 是否按预测进行了绑定。 这并不是一项庞大的工程——对于拥有合适硬件和访问权限的人来说,只需几个小时的捕获工作。但这需要一台配备 AMD GPU 的 Windows 机器,以及一款运行 FSR 4.1.0 的游戏。 ### 我们需要的人 **少量贡献者**(2-3 人),需具备: - 原生 Windows + AMD RDNA 3/4 系统 - 一款运行 FSR 4.1.0 的游戏(《FF7 重生》,或任何使用独立放大器 DLL 的游戏) - 熟悉在 D3D12 下使用 RenderDoc 或 PIX - 愿意分享捕获数据(不需要源代码——只需要调度日志和 cbuffer 转储文件) ## FSR 4.0.2 源代码参考 我们在对 FSR 4.1.0 进行逆向工程时,使用了基于 MIT 许可证的 **FSR 4.0.2 源代码**作为结构参考。AMD 在 [GPUOpen](https://gpuopen.com/) 上以 MIT 许可证发布了此代码——它明确允许用于任何用途,包括分析和互操作性研究。 4.0.2 的源代码对于以下方面至关重要: - 理解张量 schema(从 HLSL 源代码映射出的 78 个张量) - 识别神经网络架构(编码器 → 瓶颈 → 解码器) - 验证权重数据块布局和 FP8 量化格式 - 确立 4.1.0 所继承的 Provider 层契约 **代码仓库:** [fsr4-sdk-402-source]() — AMD FidelityFX SDK 2.0.0, FSR 4.0.2 (ML-Upscaler),基于 MIT 许可证。 ## 法律声明与免责声明 本项目遵循公认的逆向工程原则运行: - **FSR 4.0.2** 由 AMD 在 [GPUOpen](https://gpuopen.com/fidelityfx-superresolution/) 上以 MIT 许可证发布。我们将其用作结构参考,这是 MIT 许可证明确允许的。 - **FSR 4.1.0** 的分析是通过对分发的二进制文件进行静态分析(Ghidra 反编译、DXIL 反汇编、PE 检查、原始 x86-64 反汇编)来执行的。没有违反任何许可协议。没有接受任何 EULA。该二进制文件是以“现状”进行分析的,即处于传输过程和网络传输中。 - **提取出的权重**是 AMD 训练流水线生成的数值参数。此处复现它们仅出于研究和互操作性的目的。 - **AMD 自身的创立史就是逆向工程。** AMD 花费了五年时间对 Intel 的 386 处理器进行逆向工程。他们在法庭上胜诉。他们从未对其产品的逆向工程发布过 DMCA 下架通知。他们将其整个 GPU 驱动程序栈开源。他们以 MIT 许可证发布 FSR。我们将 AMD 自身的创立原则应用到了他们的最新产品上。 有关完整的法律分析、AMD 的历史以及诚实的风险评估,请参阅 [`LEGAL.md`](LEGAL.md)。 本项目基于 **MIT License** 发布——与 AMD 为 FSR 4.0.2 选择的许可证相同。我们相信知识应该是自由的。显然,AMD 曾经也赞同这一点。 ## 着色器内部原理:神经网络架构 对所有 602 个 DXBC 数据块的完整反汇编揭示了 **27 个唯一的 FSR4 compute shader**(命名为 `fsr4_model_v07_fp8_no_scale_*`)。其余的 575 个数据块是非 FSR 的实用着色器和参数化变体。 ### 架构:fsr4_model_v07_fp8_no_scale 其名称确认了: - **模型版本 7** — AMD 的内部迭代编号 - **FP8 权重,无按张量的缩放 (scale)** — 主要采用固定的共享指数量化;4.1.0 的初始化器布局仍然包含了一小块额外的比例因子区域 - 所有质量预设都共享**相同的神经网络架构**,仅权重偏移量有所不同 ### 权重数据块分析 所有 5 个标准预设(质量、平衡、性能、超高性能、原生)都是**逐字节一致**的(MD5 `6ccdb68fc828e0bef93fa32fd144c4f6`)。质量选择纯粹是一个调度/分辨率参数——它并不是不同的网络。 DRS(动态分辨率缩放)数据块是一个**完全重新训练过的网络**: - 96.1% 的字节与标准版不同(MD5 `8e5c042e0c14cca83d56ed13df5f02dd`) - 没有匹配的 4KB 数据块 - 相同的架构(128KB,相同的 FP8 值分布) - 应用于不同训练参数的相同量化方案 ### FP8 解码:通过原子操作实现的 LUT 着色器通过 ScratchBuffer 中的 **256 条目的查找表 (LUT)** 解码 FP8 权重,利用 `atomicCompareExchange` 作为无副作用的表读取: 1. 从权重 SRV(空间 0,寄存器 18)读取 FP8 字节 2. 在 UAV(空间 1,寄存器 0)中使用固定偏移量的 LUT 查找 3. 条目之间的步长为 256 字节 (0x100) —— 每个 FP8 值对应一个条目 4. 每个字节通过 LUT 解码为 8 个 FP16 值 5. 完全在整数寄存器中进行累加 这避免了分支,并按惯例将 GPU 原子操作用作 LUT 读取。 ### 层级架构 | 等级 | Pass | 角色 | IR 行数 | LUT 操作 | 内核 | |------|--------|------|----------|---------|--------| | Input | prepass | 特征提取 + 双线性采样 | 2,267 | 206 | N/A | | Small | pass1, pass2, pass12 | 3x3 卷积 | ~4,900 | 1,989 | 3x3 | | Medium | pass4, pass5, pass10 | 4x4 卷积 | ~8,000 | 3,296 | 4x4 | | Large | pass7, pass8 | 5x4 卷积 (最深) | ~20,750 | 9,088 | 5x4 | | Special | pass3, pass6, pass9, pass11 | 独特角色 | varies | varies | varies | | Output | postpass | ML + 传统合成 | 2,675 | 1,580 | mixed | | Scatter | pass*_post (x13) | 仅数据重排 | ~125 | 0 | N/A | **Post pass 非常简单** —— 包含 2-5 次 `rawBufferStore` 调用,没有任何 ML 计算。它们将累加的结果从暂存缓冲区分散存储到输出平面中。 ### 数据流 ``` Input Color + Motion + Depth | v Prepass (bilinear sampling -> 3 feature planes) | v Pass1 (3x3 conv) -> Scatter -> Pass2 (3x3 conv) -> Scatter -> ... | (progressively deeper kernels) Pass7/8 (5x4 conv, deepest layers) -> Scatter | v Pass12 (3x3 conv) -> Scatter | v Postpass (ML composite + conventional math -> 7 output planes) ``` 该架构具有**包含对称 Pass 布局的瓶颈结构**:特征通过逐渐加深的层进行处理,然后再被重建。 ### CBV 寄存器语义 | 寄存器 | 类型 | 用法 | |----------|------|-------| | 0 | f32/i32 | 调度维度 (宽、高) | | 1 | f32 | 抖动偏移 XY | | 2 | f32/i32 | 权重索引步长 / 输出缩放比 | | 4 | i32 | 配置标志 (字段 3) | | 5 | f32/i32 | 缓冲区步长 (字段 0) + 混合/曝光 (字段 1) | | 7 | i32 | 扩展调度参数 (专用 Pass) | ### 仍未知的内容 - **确切的激活函数** — 纯整数累加掩盖了 LUT 是否对激活函数进行了编码 - **时间状态流** — 第 N-1 帧如何反馈给第 N 帧 - **跳跃连接** — Pass 的对称性暗示了它们的存在,但尚未确认 - **注意力机制** — 未发现 softmax/QKV 模式;很可能是纯卷积网络 完整细节:[docs/shader-internals.md](docs/shader-internals.md)。机器可读分析:[spec/shader_analysis.json](spec/shader_analysis.json)。 *由 Rolaand Jayz 构建 — “暗影图书馆员”。* *如果这项工作帮助你理解了某些事物,请将其传递下去。知识在分享时会产生复利。*
标签:AI超分辨率, DLL分析, Gophish, Wayback Machine, 二进制重建, 云资产清单, 反汇编, 计算机图形学, 逆向工程