UPL9-17-c——数据输入规则
UPL9-17-c——数据输入规则
本章代码关键字
1 | computeShader.SetFloat() // 设置float类型基本数据 |
CPU侧(C# 侧)的数据输入规则
我们上一节学习了 Compute Shader 中的各种数据类型
而这些类型的数据都需要从 CPU 侧(C# 中)进行输入之后,才能在 GPU 中利用这些数据进行计算
因此,这里主要阐述 C# 侧如何向 Compute Shader 中输入数据
标量数据输入
-
float1
computeShader.SetFloat("name", value)
-
int、uint
uint 没有专门的输入 API,因此直接使用SetInt()方法1
computeShader.SetInt("name", value)
-
bool1
computeShader.SetBool("name", true)
注意:
- 虽然上节课我们提到 ComputeShader 中支持
double 和half
但是它们仅在少数 GPU 上可用,因此 C# 侧也没有提供专门的 API 进行数据输入
因此建议不要在 ComputeShader 中使用它们- 所有
Set 相关方法都需要在Dispatch之前调用- 所有变量名需要和
ComputeShader内声明一致
假设要输入以下 ComputeShader 变量:
1 | bool b; |
在 C# 中:
1 | public ComputeShader computeShader; // 声明 computeShader 变量,可以通过 Inspector 窗口关联,也可以动态加载 |
向量数据输入
向量数据类型都是通过 SetVector 进行输入,有几个元素就传几个元素数值进去即可,
但是,如果是非 4 维的向量,建议使用 SetFloats 、SetInts(存疑,需要进一步验证)
-
float2需要传入
Vector4类型向量,Compute Shader 内部会自动截取前两个分量1
computeShader.SetVector("name", new Vector4(x, y, 0, 0))
建议:
1
computeShader.SetFloats("name", ....)
-
float3需要传入
Vector4类型向量,Compute Shader 内部会自动截取前三个分量1
computeShader.SetVector("name", new Vector4(x, y, z, 0))
建议:
1
computeShader.SetFloats("name", ....)
-
float41
computeShader.SetVector("name", new Vector4(x, y, z, w))
假设要输入以下 ComputeShader 变量:
1 | float2 f2; |
在 C# 中:
1 | public ComputeShader computeShader; // 声明 computeShader 变量,可以通过 Inspector 窗口关联,也可以动态加载 |
对于 Vector2 和 Vector3 向量以及 int 类型向量,使用 SetVector 方法传递由于会将值放在 Vector4 内,
因此 Vector4 内部分分量会被抛弃,在部分平台上有风险,因此,对于非 Vector4 数据的传递,建议使用 SetFloats 和 SetInts 方法
1 | public ComputeShader computeShader; // 声明 computeShader 变量,可以通过 Inspector 窗口关联,也可以动态加载 |
对于数组类型变量,也可以使用 SetFloats 和 SetInts 方法传递
1 | float fArray[10]; |
在 C# 中:
1 | public ComputeShader computeShader; // 声明 computeShader 变量,可以通过 Inspector 窗口关联,也可以动态加载 |
矩阵数据输入
-
float4x41
computeShader.SetMatrix("name", Matrix4x4)
如果想要传小于 的矩阵,还是使用 SetMatrix() 传 Matrix4x4 类型矩阵,然后在 Compute Shader 中进行截取转换
1 | // 常量参数 |
假设要输入以下 ComputeShader 变量:
1 | float4x4 f44; |
在 C# 中:
1 | public ComputeShader computeShader; // 声明 computeShader 变量,可以通过 Inspector 窗口关联,也可以动态加载 |
复合类型输入
Compute Shader 的结构体和缓冲区数据需要通过 ComputeBuffer 传入
这个 Buffer 相当于 CPU 和 GPU 之间的共享显存区域
GPU 端(HLSL)和 CPU 端(C#)都要定义完全相同的结构布局
因此必须满足以下三个关键点:
- 结构体字段顺序必须一致
- 字段类型大小必须匹配
- 长度必须是结构体总字节数
Compute Shader 中涉及到 Buffer 相关的数据,都可以通过以下方式在 C# 侧输入数据
C# 侧输入步骤:
-
声明对应的数组或
List,存储对应数据 -
实例化
ComputeBuffer 对象:new ComputeBuffer(对象数量, 单个对象所占字节数, 缓冲区类型),不同的缓冲区使用ComputeBufferType参数-
StructuredBuffer<T> /RWStructuredBuffer<T> 使用ComputeBufferType.Default(默认值,可以不传) -
ByteAddressBuffer /RWByteAddressBuffer 使用ComputeBufferType.Raw -
AppendStructuredBuffer<T> /ConsumeStructuredBuffer<T> 使用ComputeBufferType.Append
-
-
利用
ComputeBuffer 对象的SetData API,将 C# 数据输入 -
利用
ComputeShader 的SetBuffer 方法,输入ComputeBuffer数据
注意:
ComputeBuffer 的步长参数(stride)必须等于 GPU 端结构体(或者基本类型)实际字节数特殊缓冲类型
AppendStructuredBuffer<T> /ConsumeStructuredBuffer<T>在 C# 端需要使用
new ComputeBuffer(count, stride, ComputeBufferType.Append),其中count 代表缓冲区最多有几个元素
其中AppendStructuredBuffer 在使用前调用buffer.SetCounterValue(0),而ConsumeStructuredBuffer需要输入数据
传递结构体时,在 C# 需要定义相同的结构体,传递时必须要使用缓冲区,最常用的就是 StructuredBuffer<T> / RWStructuredBuffer<T>
注意:新版本中有一个
GraphicsBuffer 类,它的用法和ComputeBuffer类似,它可以和 SRP 等管线更好地配合
假设要输入以下 ComputeShader 变量(Test 结构体通过 StructuredBuffer<T> 传递):
1 | struct Test |
在 C# 中:
-
传递结构体 / 多个结构体时,使用
StructuredBuffer<T> /RWStructuredBuffer<T>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26public ComputeShader computeShader; // 声明 computeShader 变量,可以通过 Inspector 窗口关联,也可以动态加载
// 一个 Test 结构体大小为 28 byte
struct Test
{
Vector3 position; // 12 byte
Vector3 velocity; // 12 byte
float lifetime; // 4 byte
}
void Start()
{
int index = computeShader.FindKernel("CSMain"); // 获取 computeShader 中名为 CSMain 的函数索引
// StructuredBuffer / RWStructuredBuffer
// 假设要传递3个结构体数据,结构体大小为 28 byte,通过 StructuredBuffer 传递
var list = new List<Test>();
list.Add(new Test());
list.Add(new Test());
list.Add(new Test());
var computeBuffer = new ComputeBuffer(list.Count, 28);
computeBuffer.SetData(list);
computeShader.SetBuffer(index, "buffer", computeBuffer);
computeShader.Dispatch(index, 64, 64, 0); // 启动 CSMain 的计算
} -
传递多个基本类型时,可以使用
ByteAddressBuffer /RWByteAddressBuffer1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public ComputeShader computeShader; // 声明 computeShader 变量,可以通过 Inspector 窗口关联,也可以动态加载
void Start()
{
int index = computeShader.FindKernel("CSMain"); // 获取 computeShader 中名为 CSMain 的函数索引
// ByteAddressBuffer / RWByteAddressBuffer
// 假设要传递64个int数据,通过 ByteAddressBuffer 传递
var list2 = new List<int>();
for (int i = 0; i < 64; i++)
{
list2.Add(i);
}
var computeBuffer2 = new ComputeBuffer(64, 4, ComputeBufferType.Raw);
computeBuffer2.SetData(list2);
computeShader.SetBuffer(index2, "buffer3", computeBuffer2);
computeShader.Dispatch(index, 64, 64, 0); // 启动 CSMain 的计算
} -
传递只能加或者只能减的
AppendStructuredBuffer<T> /ConsumeStructuredBuffer<T>的缓冲区时1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public ComputeShader computeShader; // 声明 computeShader 变量,可以通过 Inspector 窗口关联,也可以动态加载
void Start()
{
int index = computeShader.FindKernel("CSMain"); // 获取 computeShader 中名为 CSMain 的函数索引
// AppendStructuredBuffer / ConsumeStructuredBuffer
// 假设 AppendStructuredBuffer 最多能加 10 个 float(4byte) 数据,ConsumeStructuredBuffer 包含 10 个float(4byte)数据
var computeBuffer3 = new ComputeBuffer(10, 4, ComputeBufferType.Append);
var computeBuffer4 = new ComputeBuffer(10, 4, ComputeBufferType.Append);
// 如果是只能加的 AppendStructuredBuffer,需要设置为0,
computeBuffer3.SetCounterValue(0); // 告诉ComputeShader,AppendStructuredBuffer内无数据
computeShader.SetBuffer(index2, "buffer5", computeBuffer3);
// 如果是只能减的 ConsumeStructuredBuffer,需要设置其数据
var list3 = new List<float>();
for (int i = 0; i < 10; i++)
{
list2.Add(i);
}
computeBuffer4.SetData(list3);
computeShader.SetBuffer(index2, "buffer6", computeBuffer4);
computeShader.Dispatch(index, 64, 64, 0); // 启动 CSMain 的计算
}
纹理类型输入
1 | computeShader.SetTexture(入口函数索引, 变量名, 纹理对象) |
其中输入的纹理对象类型:
-
普通
Texture2D:只能读,不能写,适合作为输入,ComputeShader 使用Texture2D<T>接收 -
RenderTexture:可随机写,适合作为输出,ComputeShader 使用RWTexture2D<T>接收注意:
如果需要传入
RenderTexture 对象,需要将其enableRandomWrite 属性设置为true,代表可随机写入,一般是用于写入的纹理资源
RWTexture2D /RWTexture3D 绑定的纹理必须是enableRandomWrite = true 的RenderTexture
假设要输入以下 ComputeShader 变量:
1 | Texture2D<float4> tex; |
在 C# 中:
1 | public ComputeShader computeShader; // 声明 computeShader 变量,可以通过 Inspector 窗口关联,也可以动态加载 |
常量缓冲区输入
只需要匹配 cbuffer 中声明的变量名直接使用 SetXXX() 方法进行设置即可,和 C# 设置 Compute Shader 基本类型变量一样
假设要输入以下 ComputeShader 常量缓冲区:
1 | cbuffer Params |
在 C# 中:
1 | public ComputeShader computeShader; // 声明 computeShader 变量,可以通过 Inspector 窗口关联,也可以动态加载 |
