98.性能优化-GPU-着色器优化-ComputeShader-数据类型
98.1 知识点
Compute Shader 中的数据类型概览
Compute Shader 的数据类型和普通 HLSL(Unity Shader)非常接近,但由于不走渲染管线,重点在于通用计算与内存访问,类型体系上有针对数据并行和存储的扩展。主要分为:
- 标量类型
- 向量类型
- 矩阵类型
- 复合类型
- 纹理类型
- 常量缓冲区
以下只强调常用类型。
标量类型
| 类型 | 大小 |
|---|---|
bool |
1 byte |
int |
4 byte |
uint |
4 byte |
float |
4 byte |
double |
8 byte |
注意: 虽支持 half,但因对齐、平台支持及与 C# 数据交互等问题,不建议在 Compute Shader 中使用。
// 基本标量类型
bool b; // 布尔类型(1 byte)
int i; // 32 位有符号整数(4 byte)
uint ui; // 32 位无符号整数(4 byte)
float f; // 32 位浮点数(4 byte)
// double d; // 64 位双精度(部分平台支持,8 byte)
// half h; // 16 位半精度,不建议使用
向量类型
- 浮点:
float2/float3/float4(常用于坐标、RGBA 颜色) - 整数:
int2/int3/int4,uint2/uint3/uint4 - 布尔:
bool2/bool3/bool4
// 向量类型(常用于坐标、颜色等)
float2 f2; // 二维浮点向量
float3 f3; // 三维浮点向量(常用于空间坐标)
float4 f4; // 四维浮点向量(常用于 RGBA 颜色)
int2 i2; // 二维整数向量
int3 i3; // 三维整数向量
int4 i4; // 四维整数向量
uint2 u2; // 二维无符号整数向量
bool2 b2; // 二维布尔向量
矩阵类型
float2x2/float3x3/float4x4(常用于变换矩阵、MVP 等)int3x3等整数矩阵也可用
// 矩阵类型(常用于变换矩阵)
float4x4 f44; // 4x4 浮点矩阵(常用于模型-视图-投影矩阵)
float2x2 f22; // 2x2 浮点矩阵
float3x3 f33; // 3x3 浮点矩阵
int3x3 i33; // 3x3 整数矩阵
复合类型
结构体
struct 结构体名
{
// 各类型成员,需注意内存对齐
};
用于组织相关数据(如位置、速度、生命周期等)。
// 自定义结构体(用于组织相关数据)
struct Test
{
float3 position; // 位置坐标(12 字节)
float3 v; // 速度向量(12 字节)
float lifeTime; // 生命周期(4 字节)
// 结构体总大小:28 字节(需注意内存对齐)
};
缓冲区
StructuredBuffer<T>:只读结构体数组RWStructuredBuffer<T>:可读写结构体数组ByteAddressBuffer:按字节寻址的只读缓冲区RWByteAddressBuffer:可读写字节地址缓冲区AppendStructuredBuffer<T>:仅支持Append()追加,适合输出元素个数不固定的场景(队列式)ConsumeStructuredBuffer<T>:仅支持Consume()取出,动态消耗元素(队列式)
// 缓冲区类型示例(T 可为结构体或基础类型)
StructuredBuffer<Test> buffer; // 只读结构化缓冲区
// RWStructuredBuffer<Test> buffer2; // 可读写结构化缓冲区
ByteAddressBuffer buffer3; // 按字节寻址的只读缓冲区
// RWByteAddressBuffer buffer4; // 可读写字节地址缓冲区
AppendStructuredBuffer<float> buffer5; // 仅支持 Append() 追加
// ConsumeStructuredBuffer<float> buffer6; // 仅支持 Consume() 取出
纹理类型
- 只读:
Texture1D/Texture2D/Texture3D,TextureCube,Texture2DMS(多重采样) - 可读写:
RWTexture2D/RWTexture3D,RWTexture2DArray
常用示例:RWTexture2D<float4> 表示可读写二维纹理,存储 float4,多用于输出图像。
// 纹理类型示例
RWTexture2D<float4> Result; // 可读写二维纹理(存储 float4,通常用于输出图像)
// Texture2D<float4> tex2d; // 只读二维纹理
// RWTexture3D<float4> rw3d; // 可读写三维纹理
// TextureCube cubeMap; // 立方体贴图
// Texture2DMS<float4> ms; // 多重采样纹理
常量缓冲区
用 cbuffer 把一组常量打包传给 Compute Shader,按块上传更高效,驱动可批量上传并放入缓存。
cbuffer 自定义常量区名 : register(b[寄存器索引])
{
float deltaTime;
float intensity;
int count;
};
注意: register(b[索引]) 可省略,由 Unity 自动分配。若手写,索引需为 b0~b13(共 14 个常量缓冲区)。编写时应尽量少用 cbuffer,并留意各平台最大常量缓冲区数量。
98.2 知识点代码
Lesson98_性能优化_GPU_着色器优化_ComputeShader_数据类型.cs
using UnityEngine;
public class Lesson98_性能优化_GPU_着色器优化_ComputeShader_数据类型 : MonoBehaviour
{
#region 知识点一 Compute Shader 中的数据类型
// Compute Shader 的数据类型和普通 HLSL(即 Unity Shader)非常接近
// 但由于它不走渲染管线,它的重点在于 通用计算与内存访问
// 因此在类型体系上有一些专门针对 数据并行 和 存储 的扩展
// 主要分为:1.标量类型 2.向量类型 3.矩阵类型 4.复合类型 5.纹理类型 6.常量缓冲区
// 注意:这里只强调常用的类型
#endregion
#region 知识点二 标量类型
// bool 1byte
// int 4byte
// uint 4byte
// float 4byte
// double 8byte
// 注意:虽然支持 half,但是由于对齐问题、平台支持问题以及和 C# 数据交互问题,不建议在 Compute Shader 中使用
#endregion
#region 知识点三 向量类型
// float2/float3/float4
// int2/int3/int4
// uint2/uint3/uint4
// bool2/bool3/bool4
#endregion
#region 知识点四 矩阵类型
// float2x2 / float3x3 / float4x4
#endregion
#region 知识点五 复合类型
// 1. 结构体:struct 结构体名 { 各类型数据 }
// 2. 缓冲区:
// StructuredBuffer<T> 只读结构体数组
// RWStructuredBuffer<T> 可读写结构体数组
// ByteAddressBuffer 低级字节访问,只读
// RWByteAddressBuffer 可读写字节访问
// AppendStructuredBuffer<T> 动态追加元素(队列式),适合一开始不知道输出元素个数的场景,只能通过 Append() 添加
// ConsumeStructuredBuffer<T> 动态消耗元素(队列式),只能通过 Consume() 移除
#endregion
#region 知识点六 纹理类型
// Texture1D, Texture2D, Texture3D 只读普通采样纹理
// RWTexture2D, RWTexture3D 可读写纹理
// TextureCube 立方体贴图
// Texture2DMS 多重采样纹理,用于抗锯齿
// RWTexture2DArray 可读写的纹理数组
#endregion
#region 知识点七 常量缓冲区
// cbuffer 自定义常量区名(常用 Params): register(b[寄存器索引])
// { float deltaTime; float intensity; int count; };
// 用于将一组常量打包在一起传给 Compute Shader,按块上传数据更高效
// 注意:register(b[寄存器索引]) 可省略,省略后 Unity 会自动分配
// 若要手写,必须是 b0 到 b13,共 14 个常量缓冲区
// 编写时应尽量使用较少的 cbuffer,并注意每个平台的最大常量缓冲区数量
#endregion
}
Lesson98_ComputeShader_DataType.compute
#pragma kernel CSMain
#pragma kernel CSMain2
// Compute Shader 支持的变量类型示例
// 基本标量类型
bool b; // 布尔类型
int i; // 32 位有符号整数
uint ui; // 32 位无符号整数
float f; // 32 位浮点数
// double d; // 64 位双精度浮点数(部分平台支持)
// half h; // 16 位半精度浮点数
// 数组类型
float fff[8]; // 包含 8 个 float 元素的数组
// 向量类型(常用于坐标、颜色等)
float2 f2; // 二维浮点向量
float3 f3; // 三维浮点向量(常用于空间坐标)
float4 f4; // 四维浮点向量(常用于 RGBA 颜色)
int2 i2; // 二维整数向量
int3 i3; // 三维整数向量
int4 i4; // 四维整数向量
// 矩阵类型(常用于变换矩阵)
float4x4 f44; // 4x4 浮点矩阵(常用于模型-视图-投影矩阵)
float2x2 f22; // 2x2 浮点矩阵
float3x3 f33; // 3x3 浮点矩阵
int3x3 i33; // 3x3 整数矩阵
// 自定义结构体(用于组织相关数据)
struct Test
{
float3 position; // 位置坐标(12 字节)
float3 v; // 速度向量(12 字节)
float lifeTime; // 生命周期(4 字节)
// 结构体总大小:28 字节(需注意内存对齐)
};
// Compute Shader 缓冲区类型示例
StructuredBuffer<Test> buffer; // 只读结构化缓冲区(存储 Test 结构体数组)
// RWStructuredBuffer<Test> buffer2; // 可读写结构化缓冲区(注释状态)
ByteAddressBuffer buffer3; // 字节地址缓冲区(按字节寻址的只读缓冲区)
// RWByteAddressBuffer buffer4; // 可读写字节地址缓冲区(注释状态)
AppendStructuredBuffer<float> buffer5; // 追加结构化缓冲区(仅支持追加操作)
// ConsumeStructuredBuffer<float> buffer6; // 消费结构化缓冲区(支持原子取出操作)
// 纹理缓冲区类型
RWTexture2D<float4> Result; // 可读写二维纹理(存储 float4,通常用于输出图像)
// Texture2D<float4> Result2; // 只读二维纹理(注释状态)
// 常量缓冲区(存储每次 Dispatch 调用中保持不变的参数,同一 Dispatch 内所有线程共享)
cbuffer Params
{
float deltaTime; // 帧时间间隔(用于与时间相关的计算)
float intensity; // 强度系数(可用于调节效果强度)
int count; // 元素数量(可用于控制处理的数据量)
}
// 线程组配置:指定每个线程组包含的线程数量
// [numthreads(X, Y, Z)] 定义一个线程组包含 X*Y*Z 个线程
[numthreads(8,8,1)]
void CSMain(uint3 id : SV_DispatchThreadID, // 全局线程 ID(在整个 Dispatch 调用中唯一)
uint3 id2 : SV_GroupID, // 线程组 ID(标识当前线程组的位置)
uint3 id3 : SV_GroupThreadID) // 线程组内线程 ID(在线程组内唯一)
{
// 类型转换示例:将 4x4 矩阵转换为 2x2 矩阵(截取左上角 2x2 部分)
f22 = (float2x2)f44;
// 核心计算逻辑:根据线程 ID 生成棋盘格图案
// R 通道:id.x 和 id.y 的按位与结果
// G 通道:id.x 的低 4 位映射到 [0,1] 范围
// B 通道:id.y 的低 4 位映射到 [0,1] 范围
// A 通道:固定为 0.0(完全透明)
Result[id.xy] = float4(id.x & id.y, (id.x & 15) / 15.0, (id.y & 15) / 15.0, 0.0);
}
// 第二个计算着色器内核函数
// 使用 1x1x1 线程组配置,适合执行单次原子操作或全局控制逻辑
[numthreads(1,1,1)]
void CSMain2(uint3 id : SV_DispatchThreadID) // 仅需全局线程 ID
{
// 此处可编写第二个内核的核心逻辑
// 例如:初始化缓冲区、执行全局同步操作等
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com