98.ComputeShader数据类型

98.性能优化-GPU-着色器优化-ComputeShader-数据类型


98.1 知识点

Compute Shader 中的数据类型概览

Compute Shader 的数据类型和普通 HLSL(Unity Shader)非常接近,但由于不走渲染管线,重点在于通用计算与内存访问,类型体系上有针对数据并行存储的扩展。主要分为:

  1. 标量类型
  2. 向量类型
  3. 矩阵类型
  4. 复合类型
  5. 纹理类型
  6. 常量缓冲区

以下只强调常用类型。

标量类型

类型 大小
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 / int4uint2 / 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 / Texture3DTextureCubeTexture2DMS(多重采样)
  • 可读写:RWTexture2D / RWTexture3DRWTexture2DArray

常用示例: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 自动分配。若手写,索引需为 b0b13(共 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

×

喜欢就点赞,疼爱就打赏