本质上,GPU中的内存(Buffer或Texture)本身只是一堆原始的数据字节,为了让Shader知道如何“看待”和“操作”这些数据,我们需要给这块内存套上一个“视图(View)”或者说操作的快捷方式,SRV和UAV就是两种最常用的视图。

SRV (Shader Resource View - 着色器资源视图)

SRV 是提供给 Shader 的只读(Read-Only)数据接口。

  • HLSL中的对应类型:
    • Texture2DTexture3DStructuredBuffer<T>Buffer<T>ByteAddressBuffer
  • 特性
    • 带缓存:因为是只读的,GPU可以非常激进地对其进行缓存,读取效率极高,非常适合常规的纹理采样操作。
    • 通用性:几乎所有的Shader阶段(Vertex, Pixel, Compute, Geometry等)都可以绑定和读取SRV。

示例

C++端 声明部分

CPP
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 对象详见

CPP
/* 纹理 */
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端

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支持 InterlockedAddInterlockedMin 等原子操作,确保数据写入不会产生竞争冲突(Race Condition)。
  • 缺乏传统缓存优势:由于专为无序访问设计且数据随时在变,UAV的读取效率通常不如SRV,因此不应该把UAV当做普通的纹理去频繁采样。

示例

C++端 声明部分

CPP
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

CPP
/* 纹理 */
FRDGTextureRef RDGTexture = ...
ShaderParams->OutTexture = GraphBuilder.CreateUAV(RDGTexture);

/* 结构体数据 */
FRDGBufferRef RDGMyData = ...
PassParameters->OutData = GraphBuilder.CreateUAV(RDGMyData);
点击展开查看更多

HLSL端

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 屏障。

CPP
FRDGTextureRef RDGTexture = ...

// Pass A: UAV 写入
PassAParams->OutTexture = GraphBuilder.CreateUAV(RDGTexture);
XXXShaderUtils::AddPass(...);

// Pass B: SRV 读取
ShaderBParams->MyTexture = PAssBuilder.CreateSRV(FRDGTextureSRVDesc(RDGTexture));
XXXShaderUtils::AddPass(...);
点击展开查看更多

版权声明

作者: Cheyne Xie

链接: https://chaim.eu.org/posts/9075c9cc/

许可证: CC BY-NC-SA 4.0

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Please attribute the source, use non-commercially, and maintain the same license.

开始搜索

输入关键词搜索文章内容

↑↓
ESC
⌘K 快捷键