本质上,GPU中的内存(Buffer或Texture)本身只是一堆原始的数据字节,为了让Shader知道如何“看待”和“操作”这些数据,我们需要给这块内存套上一个“视图(View)”或者说操作的快捷方式,SRV和UAV就是两种最常用的视图。
SRV (Shader Resource View - 着色器资源视图)
SRV 是提供给 Shader 的只读(Read-Only)数据接口。
- HLSL中的对应类型:
Texture2D、Texture3D、StructuredBuffer<T>、Buffer<T>、ByteAddressBuffer
- 特性:
- 带缓存:因为是只读的,GPU可以非常激进地对其进行缓存,读取效率极高,非常适合常规的纹理采样操作。
- 通用性:几乎所有的Shader阶段(Vertex, Pixel, Compute, Geometry等)都可以绑定和读取SRV。
示例
C++端 声明部分
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
// 纹理
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, MyTexture)
// 结构体数据
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FMyData>, MyData)
END_SHADER_PARAMETER_STRUCT()创建SRV(RDG 对象详见
)/* 纹理 */
FRDGTextureRef RDGTexture = ...
// 全视图
ShaderParams->MyTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc(RDGTexture));
// 特定Mipmap
ShaderParams->MyTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(RDGTexture, 2));
// 映射像素格式
ShaderParams->MyTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateWithPixelFormat(RDGTexture, EPixelFormat::PF_A16B16G16R16));
// 将 Texture2DArray 的某一切片映射为 Texture2D
ShaderParams->MyTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForSlice(RDGTextureArray, 0));
/* 结构体数据 */
FRDGBufferRef RDGMyData = ...
// 全视图
ShaderParams->MyData = GraphBuilder.CreateSRV(RDGMyData);
// 切片
ShaderParams->MyData = GraphBuilder.CreateSRV(FRDGBufferSRVDesc(RDGMyData, 5, 10));HLSL端
// 纹理
Texture2D<float4> MyTexture;
// 结构体数据
struct FMyData { ... };
StructuredBuffer<FMyData> MyData;UAV (Unordered Access View - 无序访问视图)
UAV 是提供给 Shader 的读/写(Read/Write)数据接口。 HLSL中的对应类型:
RWTexture2D<T>、RWStructuredBuffer<T>、AppendStructuredBuffer<T>、ConsumeStructuredBuffer<T>特性:- 支持原子操作:因为多个线程可能会同时写入同一个内存地址(比如统计场景中的某种粒子数量),UAV支持
InterlockedAdd、InterlockedMin等原子操作,确保数据写入不会产生竞争冲突(Race Condition)。 - 缺乏传统缓存优势:由于专为无序访问设计且数据随时在变,UAV的读取效率通常不如SRV,因此不应该把UAV当做普通的纹理去频繁采样。
示例
C++端 声明部分
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
// 纹理
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D<float4>, OutTexture)
// 结构体数据
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer<FMyData>, OutData)
END_SHADER_PARAMETER_STRUCT()创建UAV
/* 纹理 */
FRDGTextureRef RDGTexture = ...
ShaderParams->OutTexture = GraphBuilder.CreateUAV(RDGTexture);
/* 结构体数据 */
FRDGBufferRef RDGMyData = ...
PassParameters->OutData = GraphBuilder.CreateUAV(RDGMyData);HLSL端
// 纹理
Texture2D<float4> OutTexture;
// 结构体数据
struct FMyData { ... };
StructuredBuffer<FMyData> OutData;
...
OutTexture[ThreadId.xy] = Value;
OutData[x] = Value;UAV 转 SRV
在 RDG 系统中,UAV 转 SRV 最神奇的地方在于:你不需要写任何一行的“状态转换(Transition)”或“内存屏障(Barrier)”代码。 你只需要告诉 RDG:“Pass A 用这个 Buffer 当 UAV”,“Pass B 用这个同一个 Buffer 当 SRV”,RDG 在最终编译图的时候,会自动分析出数据依赖,并在底层替你插入最完美的 GPU 屏障。
FRDGTextureRef RDGTexture = ...
// Pass A: UAV 写入
PassAParams->OutTexture = GraphBuilder.CreateUAV(RDGTexture);
XXXShaderUtils::AddPass(...);
// Pass B: SRV 读取
ShaderBParams->MyTexture = PAssBuilder.CreateSRV(FRDGTextureSRVDesc(RDGTexture));
XXXShaderUtils::AddPass(...);