- 图形学
- 游戏开发
【免费下载链接】dlssg-to-fsr3
Adds AMD FSR 3 Frame Generation to games by replacing Nvidia DLSS Frame Generation (nvngx_dlssg).
FidelityFX Blur 1.1 是 AMD FidelityFX SDK 中一款基于 Compute Shader 的高性能单趟(single-pass)高斯模糊技术,通过线程组共享内存(LDS)缓存中间结果,将传统两趟可分离高斯模糊合并为一次 compute dispatch 完成。本文以 dependencies/FidelityFX-SDK/docs/techniques/blur.md 为主干,结合当前仓库(gh_mirrors/dl/dlssg-to-fsr3,即 dlssg-to-fsr3 项目中内嵌的 FidelityFX SDK 依赖)中的宿主 API 头文件与 GPU 着色器源码,系统讲解 FfxBlur 的完整集成流程、可自定义回调机制、内核权重/内核尺寸/浮点精度等核心参数,以及"水平模糊缓存 + 垂直模糊输出"三步算法与环形缓冲区共享内存布局。读完本文,你将能够直接把 FidelityFX Blur 集成进自己的渲染管线,或基于自定义 main 函数与回调重写属于自己的模糊计算内核。
一、技术概览与环境要求
FidelityFX Blur 是一款compute-based、高度优化、单趟完成的高斯模糊技术。它在上层 API 层面表现为一个独立的 FfxBlurContext,功能上与标准两趟可分离高斯模糊完全等价,但性能更高——这正是它与其他常规模糊实现的核心差异。
| 项目 | 要求 |
|---|---|
| HLSL | CS_6_0(Compute Shader 6.0) |
| GLSL | version 450 |
| 版本号 | FFX_BLUR_VERSION_MAJOR 1/MINOR 1/PATCH 0(见 ffx_blur.h) |
| 内部上下文数量 | FFX_BLUR_CONTEXT_COUNT = 1 |
宿主头文件 ffx_blur.h 将其描述为"在 compute shader 上实现、针对极致性能手工优化的模糊效果集合",当前包含一种效果:支持最大 21x21 内核的高斯模糊。GPU 侧入口函数为ffxBlurPass(封装于 ffx_blur_blur.h),实际算法主体为ffxBlur(定义于 ffx_blur.h)。
二、C++ SDK 集成指南(宿主侧 API)
使用 FFX SDK C++ API 集成 Blur 是最简单的方式,整体流程分为三步:初始化上下文 → 逐帧执行 Dispatch → 销毁上下文。
2.1 初始化 FfxBlurContext
// Initialize the FFX backend and blur context (do this once) size_t scratchBufferSize = ffxGetScratchMemorySize(FFX_BLUR_CONTEXT_COUNT); void* scratchBuffer = malloc(scratchBufferSize); FfxInterface backendInterface; ffxGetInterface(&backendInterface, GetDevice(), scratchBuffer, scratchBufferSize, FFX_BLUR_CONTEXT_COUNT); FfxBlurContextDescription desc = {}; desc.backendInterface = backendInterface; desc.floatPrecision = FFX_BLUR_FLOAT_PRECISION_32BIT; desc.kernelPermutations = FFX_BLUR_KERNEL_PERMUTATIONS_ALL; // mask to support some Guassian sigma kernels or ALL desc.kernelSizes = FFX_BLUR_KERNEL_SIZE_ALL; // mask to support some kernel sizes or ALL FfxBlurContext blurContext; ffxBlurContextCreate(&blurContext, &desc);关键点说明:
- scratch 缓冲:
ffxGetScratchMemorySize(FFX_BLUR_CONTEXT_COUNT)返回后端所需 scratch 内存大小,FFX_BLUR_CONTEXT_COUNT为 1(Blur 仅需一个内部效果上下文)。 - backendInterface:通过
ffxGetInterface绑定当前设备的后端实现(DX12 / Vulkan)。 - 创建/销毁:
ffxBlurContextCreate与ffxBlurContextDestroy是上下文生命周期管理的唯二入口,声明见 ffx_blur.h。
2.2 FfxBlurContextDescription 核心参数
依据 ffx_blur.h,上下文描述结构体包含四个字段:
| 字段 | 类型 | 含义 |
|---|---|---|
kernelPermutations | 位掩码 | 使能的 Gaussian sigma 内核排列(1.6 / 2.8 / 4.0) |
kernelSizes | 位掩码 | 使能的内核尺寸(3x3 … 21x21) |
floatPrecision | 枚举 | 期望的浮点精度(FP32 / FP16) |
backendInterface | 结构体 | 指向 FFX 后端实现的一组函数指针 |
其中FFX_BLUR_KERNEL_PERMUTATIONS_ALL与FFX_BLUR_KERNEL_SIZE_ALL是"全部使能"的便捷宏:前者为(1 << FFX_BLUR_KERNEL_PERMUTATION_COUNT) - 1(3 种 sigma 全开),后者为(1 << FFX_BLUR_KERNEL_SIZE_COUNT) - 1(10 种内核尺寸全开)。
2.3 逐帧执行:ffxBlurContextDispatch
// Execute blur effect (multiple times) FfxBlurDispatchDescription desc = {}; desc.commandList = ffxGetCommandList(pCmdList); desc.kernelPermutation = FFX_BLUR_KERNEL_PERMUTATION_2; // Guassian sigma (1.6, 2.8, or 4.0) desc.kernelSize = FFX_BLUR_KERNEL_SIZE_15x15; // Kernel sizes (3x3 ... 21x21) desc.input = ffxGetResource(inputResource, L"BLUR_InputSrc", FFX_RESOURCE_STATE_PIXEL_COMPUTE_READ); desc.inputAndOutputSize.width = desc.input.description.width; desc.inputAndOutputSize.height = desc.input.description.height; desc.output = ffxGetResource(outputResource, L"BLUR_Output", FFX_RESOURCE_STATE_UNORDERED_ACCESS); ffxBlurContextDispatch(&blurContext, &desc); // When done using the blur effect, clean up. ffxBlurContextDestroy(&blurContext);FfxBlurDispatchDescription的完整字段(见 ffx_blur.h):
| 字段 | 说明 |
|---|---|
commandList | 用于记录渲染命令的FfxCommandList |
kernelPermutation | 本次使用的 sigma 排列(必须是 Context 创建时使能的排列之一) |
kernelSize | 本次使用的内核尺寸(必须是 Context 创建时使能的尺寸之一) |
inputAndOutputSize | 输入输出资源的宽高(像素) |
input | 待模糊的输入资源,状态为FFX_RESOURCE_STATE_PIXEL_COMPUTE_READ |
output | 模糊结果输出资源,状态为FFX_RESOURCE_STATE_UNORDERED_ACCESS |
2.4 参数取值范围速查(枚举定义)
以下枚举均定义于 ffx_blur.h:
内核排列(sigma 值)——FfxBlurKernelPermutation:
| 枚举值 | 位 | 含义 |
|---|---|---|
FFX_BLUR_KERNEL_PERMUTATION_0 | 1<<0 | sigma = 1.6 |
FFX_BLUR_KERNEL_PERMUTATION_1 | 1<<1 | sigma = 2.8 |
FFX_BLUR_KERNEL_PERMUTATION_2 | 1<<2 | sigma = 4.0 |
内核尺寸——FfxBlurKernelSize(全部为奇数内核,从 3x3 到 21x21,共 10 档):
| 枚举值 | 位 | 枚举值 | 位 |
|---|---|---|---|
FFX_BLUR_KERNEL_SIZE_3x3 | 1<<0 | FFX_BLUR_KERNEL_SIZE_13x13 | 1<<5 |
FFX_BLUR_KERNEL_SIZE_5x5 | 1<<1 | FFX_BLUR_KERNEL_SIZE_15x15 | 1<<6 |
FFX_BLUR_KERNEL_SIZE_7x7 | 1<<2 | FFX_BLUR_KERNEL_SIZE_17x17 | 1<<7 |
FFX_BLUR_KERNEL_SIZE_9x9 | 1<<3 | FFX_BLUR_KERNEL_SIZE_19x19 | 1<<8 |
FFX_BLUR_KERNEL_SIZE_11x11 | 1<<4 | FFX_BLUR_KERNEL_SIZE_21x21 | 1<<9 |
浮点精度——FfxBlurFloatPrecision:FFX_BLUR_FLOAT_PRECISION_32BIT = 0、FFX_BLUR_FLOAT_PRECISION_16BIT = 1。FP16 变体会在 ffx_blur_callbacks_hlsl.h 中通过FFX_HALF宏选择FfxFloat16类型的 I/O 与内核权重。
三、通过回调与自定义 main 函数定制 Blur
Blur 在 ffx_blur_callbacks_hlsl.h 与 ffx_blur_callbacks_glsl.h 中提供了默认的内核权重与 I/O 函数实现,SDK 使用者可以将其替换,以对接自己的应用资源绑定。
3.1 内核权重回调的重写规则
Blur 要求1D 内核权重在编译时嵌入着色器以获得最大性能,具体要求如下:
- 函数签名中的权重类型为
FfxFloat32或FfxFloat16,由是否启用半精度决定;这两个类型定义于 ffx_core.h; - 必须提供一组归一化的高斯权重分布,以静态数组形式定义,元素按递减顺序排列(从中心权重到边缘权重)。
官方示例(5x5 高斯内核覆盖):
#if FFX_HALF #define FFX_BLUR_KERNEL_TYPE FfxFloat16 #else #define FFX_BLUR_KERNEL_TYPE FfxFloat32 #endif inline FFX_BLUR_KERNEL_TYPE GetKernelWeight(int iKernelIndex) { static FFX_BLUR_KERNEL_TYPE kernel_weights[] = { 0.257030201088974, 0.22378581991669, 0.147699079538823 }; // for a 5x5 Gaussian kernel return kernel_weights[iKernelIndex]; } FFX_BLUR_KERNEL_TYPE FfxBlurLoadKernelWeight(FfxInt32 iKernelIndex) { return GetKernelWeight(iKernelIndex); }从源码层面印证:默认实现 ffx_blur_callbacks_hlsl.h 内嵌了 3 种 sigma(1.6 / 2.8 / 4.0)x 10 种尺寸(FFX_BLUR_KERNEL_RANGE从 2 到 11)的完整权重表,并通过FFX_BLUR_OPTION_KERNEL_PERMUTATION(0/1/2)与FFX_BLUR_OPTION_KERNEL_DIMENSION两个宏在编译期选取具体数组;FFX_BLUR_KERNEL_RANGE = ((FFX_BLUR_OPTION_KERNEL_DIMENSION - 1) / 2) + 1,即"中心 + 半宽"。若未定义FFX_BLUR_OPTION_KERNEL_DIMENSION,编译会直接报错(见 ffx_blur.h),这正是"权重必须编译期确定"的设计体现。
3.2 I/O 回调的重写示例
#if FFX_HALF FfxFloat16x3 FfxBlurLoadInput(FfxInt16x2 inPxCoord) { return texColorInput[inPxCoord].rgb; } void FfxBlurStoreOutput(FfxInt32x2 outPxCoord, FfxFloat16x3 color) { texColorOutput[outPxCoord] = min16float4(color, 1); } #else FfxFloat32x3 FfxBlurLoadInput(FfxInt32x2 inPxCoord) { return texColorInput[inPxCoord].rgb; } void FfxBlurStoreOutput(FfxInt32x2 outPxCoord, FfxFloat32x3 color) { texColorOutput[outPxCoord] = float4(color, 1); } #endif注意默认实现的资源绑定约定:输入纹理为r_input_src(SRV,FFX_BLUR_BIND_SRV_INPUT_SRC),输出为rw_output(UAV,FFX_BLUR_BIND_UAV_OUTPUT),资源标识符定义见 ffx_blur_resources.h(FFX_BLUR_RESOURCE_IDENTIFIER_INPUT_SRC = 1、FFX_BLUR_RESOURCE_IDENTIFIER_OUTPUT = 2)。重写回调后即可对接自定义纹理名称与格式。
3.3 通过自定义 main 函数集成
除了 C++ 宿主 API,Blur 也支持通过自己的 computemain函数直接集成。核心要求是:必须定义FFX_BLUR_TILE_SIZE_X与FFX_BLUR_TILE_SIZE_Y宏(一般均为 8,源码默认值也如此,见 ffx_blur.h)。
HLSL 入口示例:
#include "blur/ffx_blur_callbacks_hlsl.h" #include "blur/ffx_blur_blur.h" [numthreads(FFX_BLUR_TILE_SIZE_X, FFX_BLUR_TILE_SIZE_Y, 1)] void CS( uint3 LocalThreadId : SV_GroupThreadID, uint3 WorkGroupId : SV_GroupID, uint3 DispatchThreadID : SV_DispatchThreadID) { // Run FidelityFX - Blur ffxBlurPass(int2(DispatchThreadID.xy), int2(LocalThreadId.xy), int2(WorkGroupId.xy)); }GLSL 入口示例:
#include "blur/ffx_blur_callbacks_glsl.h" #include "blur/ffx_blur_blur.h" layout (local_size_x = FFX_BLUR_TILE_SIZE_X, local_size_y = FFX_BLUR_TILE_SIZE_Y, local_size_z = 1) in; void main() { // Run FidelityFX Blur ffxBlurPass( FfxInt32x2(gl_GlobalInvocationID.xy), FfxInt32x2(gl_LocalInvocationID.xy), FfxInt32x2(gl_WorkGroupID.xy)); }其中ffxBlurPass会进一步把三个 ID 连同ImageSize()(来自常量缓冲区cbBLUR的imageSize字段)一起传给真正的算法函数ffxBlur。CPU 侧 Dispatch 时 Y 维默认使用FFX_BLUR_DISPATCH_Y = 8,即一个工作组在 Y 方向覆盖 8 个 tile 高度。
四、Under the Hood:三步算法与复杂度分析
4.1 单趟三步骤算法
Blur 采用三步算法,通过线程组共享内存存储中间模糊结果,从而在一次 compute dispatch内完成传统上需要两趟可分离滤波的工作:
- Pre-fill(预填充)阶段:从图像源填充缓存,对首批 tile 执行水平模糊;
- Loop top-down(自上而下主循环):从图像源填充缓存并做水平模糊 → 基于缓存结果做垂直模糊 → 写入目标资源;
- Loop over the last few tiles(尾部收尾循环):仅对最后几个 tile 做垂直模糊并写入目标资源。
4.2 复杂度理论
| Dispatch 时间复杂度 | Wave 执行时间复杂度 | Group Shared Memory 空间复杂度 |
|---|---|---|
O(W * H * K) | O(H * K) | O(Th * (Tw + K)) |
其中:
W:图像宽度(Image Width)H:图像高度(Image Height)K:1D 内核尺寸(1D Kernel Size)Tw:模糊 tile 宽度(Blur Tile Size X)Th:模糊 tile 高度(Blur Tile Size Y)
从源码注释(ffx_blur.h)可以看到对算法的精确定义:图像在缓存到 LDS 的同时进行水平模糊,当所有 LDS tile 填满后,在 LDS 上执行垂直模糊,结果写入 UAV 作为最终输出。水平模糊的负载量(image_load指令数)会随FFX_BLUR_OPTION_KERNEL_DIMENSION线性增长,这也是大内核(如 21x21)成本的主要来源。
4.3 工作分布(Work Distribution)
Blur 使用8x8 线程组,每个线程组负责图像中的一个 tile(threadgroup 与 tile 一一对应)。工作分布受以下#define影响:
FFX_BLUR_TILE_SIZE_X/FFX_BLUR_TILE_SIZE_Y:tile 尺寸,默认 8;FFX_BLUR_DISPATCH_Y:Y 方向 dispatch 维度,默认 8,决定每个工作组纵向覆盖的 tile 数;FFX_BLUR_OPTION_KERNEL_DIMENSION:内核维度,直接决定预填充 tile 数量NUM_PREFILL_TILES_OUTPUT_CACHE = DIV_AND_ROUND_UP(kernel, TILE_SIZE_Y)(例如 3/5/7 内核预填充 1 个 tile,9~15 预填充 2 个,17~21 预填充 3 个,见 ffx_blur.h)。
4.4 共享内存:tile 环形缓冲区
Blur 在共享内存中开辟一块区域作为tile 环形缓冲区(ring buffer)来存放中间结果:
- 与单个 tile 对应的线程组,先采样源图像并执行水平模糊,把结果写入共享内存;
- 随后该线程组从共享内存读取中间结果,执行垂直模糊,再把完全模糊后的图像写入目标纹理。
实现层面的优化细节(同样定义于 ffx_blur.h):
- 环形缓冲 tile 数:
NUM_TILES_OUTPUT_CACHE在FFX_BLUR_OPTION_KERNEL_DIMENSION > 7时为 8,否则为 4; - 快速取模:借助
FAST_MOD(x, y) = x & (y-1)用位与代替取模(要求 tile 数为 2 的幂),降低 ALU 开销; - LDS 打包:默认开启
BLUR_GROUPSHARED_MEMORY_PK_UINT,将 RG 两通道的 FP16 打包进单个 32 位无符号整数(PackF2/UnpackToF2,见 ffx_blur.h),B 通道单独使用 FP32 以避免 bank conflict;历史遗留选项BLUR_GROUPSHARED_MEMORY_SOA与BLUR_GROUPSHARED_MEMORY_HALF已被标记为 Deprecated; - 边界钳制:默认启用
clamp(BLUR_DISABLE_CLAMP = 0),图像边缘采样坐标被钳制到[0, ImageSize-1]区间,保证边界像素正确;BLUR_OPTIMIZED_CLAMP为实验性开关。
五、配套示例与调试对照(samples/blur)
仓库同时提供了配套示例文档 dependencies/FidelityFX-SDK/docs/samples/blur.md,该示例(对应 dependencies/FidelityFX-SDK/samples/blur 目录)用于演示与对比单趟高斯模糊相对标准两趟可分离模糊的性能优势,支持 Windows + DirectX 12 与 Vulkan。
示例 UI 提供的可调参数与本文上述 API 参数一一对应:
| UI 元素 | 取值 | 控制内容 |
|---|---|---|
| Algorithm | FidelityFX Blur, Single Pass Filter, Multi-pass Separable Filter, Multi-pass Separable Filter Transpose | 切换当前使用的算法(FFX Blur / 标准多趟可分离滤波对照) |
| Gaussian Kernel Sigma | 1.6, 2.8, 4.0 | 切换生成高斯内核所用的 sigma |
| Kernel Size | 3x3, 5x5, ..., 21x21 | 切换模糊内核尺寸 |
| Floating Point Math | Use FP32, Use FP16 | 切换 FP32 / FP16 实现 |
| Display the difference | On/Off | 开关对比模式 |
| Comparison mode settings | Compare Algorithm, Compare Gaussian Sigma, Compare Kernel Size, Compare FP Math | 对比模式下对四个维度分别做差 |
| Diff Factor | 1.0..10.0 | 显示差值时的放大倍数 |
对比模式的实现逻辑是:把Compare Algorithm下拉框所选算法(连同其 sigma、尺寸、数学精度设置)生成的图像,与Algorithm下拉框所选算法生成的图像相减;由于差异可能极小,Diff Factor滑杆会把差值乘以指定倍数后再显示到屏幕上。该示例是验证本文第 2 节各参数实际效果的最直接工具。
六、进一步阅读与参考资料
- GDC 2019: A Blend of GCN Optimization & Color Processing,Part I: Store Caching in Separable Filters(本篇文档共享内存环形缓存设计的理论来源,见原文档 Further Reading 一节)
- GDC 2018: Engine Optimization Hot Lap
- Rastergrid: Efficient Blur w/ Linear Sampling
仓库内的相关深入材料还包括:FidelityFX SDK 结构说明、FFX API 集成指南,以及 FidelityFX Blur 命名规范;若需在运行时动态切换内核,可参考宿主侧实现 dependencies/FidelityFX-SDK/sdk/src/components/blur/ffx_blur.cpp。
小结
FidelityFX Blur 1.1 的价值在于用一次 compute dispatch 完成传统两趟可分离高斯模糊的全部工作:宿主侧通过ffxBlurContextCreate/ffxBlurContextDispatch/ffxBlurContextDestroy三个 API 即可完整接入;GPU 侧以 8x8 tile 为单位,利用 LDS 环形缓冲区缓存水平模糊结果,配合 2 的幂 tile 数位运算取模、FP16 打包等优化手段压低开销;三种 sigma(1.6/2.8/4.0)与十档内核尺寸(3x3~21x21)通过位掩码灵活组合,FP32/FP16 双精度变体可按需选择。无论你是通过 C++ 宿主 API 快速集成,还是借助回调与自定义 main 函数深度定制,本文给出的参数表与源码路径都能帮助你定位到对应实现。
- 图形学
- 游戏开发
【免费下载链接】dlssg-to-fsr3
Adds AMD FSR 3 Frame Generation to games by replacing Nvidia DLSS Frame Generation (nvngx_dlssg).
相关推荐
FidelityFX Blur 示例详解:单次调度高斯模糊的算法原理、UI 参数与 SDK 集成实战
FidelityFX Blur 示例详解:单次调度高斯模糊的算法原理、UI 参数与 SDK 集成实战 FidelityFX Blur 示例演示了 AMD Fid
图形学游戏开发FidelityFX FSR2 时间超分辨率技术深度解析:从算法原理到集成实战(dlssg-to-fsr3 仓库)
FidelityFX FSR2 时间超分辨率技术深度解析:从算法原理到集成实战(dlssg to fsr3 仓库) FSR2(FidelityFX Super
图形学游戏开发dlssg-to-fsr3 核心依赖解读:AMD FidelityFX SDK 1.1 版本新特性、FSR 3.1 帧生成与 FSR API 全解析
dlssg to fsr3 核心依赖解读:AMD FidelityFX SDK 1.1 版本新特性、FSR 3.1 帧生成与 FSR API 全解析 本文围绕本
图形学游戏开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考