1.介绍
Metal基于C++ 11.0标准设计的着色器语言,在C++的基础上进行了一些拓展和限制.
Metal 的限制
- Lambda表达式
- 地柜函数调用
- 动态转换操作符
- 类型识别
- 创建对象:
new
和释放对象 dealloc
操作
- 操作符
noexcept
-
goto
跳转符
- 虚函数修饰符
- 派生类
- 异常处理
- C++的标准款不可以在Metal着色语言中使用
- Metal语言有对指针的使用限制
- Metal图形和并行计算函数用到的入参如果是指针,使用地址空间修饰符(device , threadgroup , constant)
- 不支持函数指针
- Metal 函数名不能命名为Main
2.基础类型
类型 |
解释 |
bool |
布尔类型 |
char |
有符号8-bit 整数 |
unsigned char uchar |
无符号8-bit 整数 |
short |
有符号16-bit 整数 |
unsigned short ushort |
无符号16-bit 整数 |
int |
有符号32-bit 整数 |
unsigned int uint |
无符号32-bit 整数 |
half |
16-bit 浮点数 |
float |
32-bit 浮点数 |
size-t |
64-bit 无符号整数,表示sizeof操作符结果; |
ptrdiff_t |
64-bit 有符号整数,表示2个指针的查 |
void |
表示一个空的集合 |
支持的向量类型 |
解释 |
booln |
bool型向量n是一个数字,表示多少个坐标组成的向量 |
charn |
char型向量n是一个数字,表示多少个坐标组成的向量 |
shortn |
short型向量n是一个数字,表示多少个坐标组成的向量 |
intn |
int型向量n是一个数字,表示多少个坐标组成的向量 |
ucharn |
uchar型向量n是一个数字,表示多少个坐标组成的向量 |
ushortn |
ushort型向量n是一个数字,表示多少个坐标组成的向量 |
uintn |
uint型向量n是一个数字,表示多少个坐标组成的向量 |
halfn |
half型向量n是一个数字,表示多少个坐标组成的向量 |
floatn |
float型向量n是一个数字,表示多少个坐标组成的向量 |
矩阵的描述方式 |
解释 矩阵只支持两种类型 |
halfxm |
xm分别指的是矩阵的行数和列数 |
floatxm |
xm分别指的是矩阵的行数和列数 |
//基本数据类型
bool a = true;
char b = 5;
int d = 15;
size_t c = 1;
ptrdiff_t f = 2;
//向量
bool2 A= [1,0];
float4 pos = float4(1.0f,2.0f,3.0f,4.0f);
float x = pos[0];
float y = pos[1];
//利用 pos 向量的规律变化得到一个新的向量VB
float4 VB;
for(int i = 0; i < 4 ; i++)
VB[i] = pos[i] * 2.0f;
//通过向量字母来获取元素
int4 test = int4(0,1,2,3,4);
int a = test.x;
int b = test.y;
int c = test.z;
int d = test.w;
int e = test.r;
int f = test.g;
int g = test.b;
int h = test.a;
//多维向量的赋值
float4 c; //c是一个四维向量 x,y,z,w坐标
c.xyzw = float4(1.0f,2.0f,3.0f,4.0f);
c.z = 1.0f; //单独为某一个方向坐标赋值
c.xy = float2(3.0f,4.0f);
c.xyz = float3(3.0f,4.0f,5.0f);
float4 pos = float4(1.0f,2.0f,3.0f,4.0f);
//分量语法,允许分量乱序 或者 分量重复出现
float4 swiz = pos.wxyz; //swiz = (4.0,1.0,2.0,3.0);
float4 dup = pos.xxyy; //dup = (1.0f,1.0f,2.0f,2.0f)
//pos = (5.0f,2.0,3.0,6.0)
pos.xw = float2(5.0f,6.0f);
//pos = (8.0f,2.0f,3.0f,7.0f)
pos.wx = float2(7.0f,8.0f);
//pos = (3.0f,5.0f,9.0f,7.0f);
pos.xyz = float3(3.0f,5.0f,9.0f);
float2 pos;
pos.x = 1.0f; //合法
pos.z = 1.0f; //非法 - 超范围 二维向量没有z坐标
float3 pos2;
pos2.z = 1.0f; //合法
pos2.w = 1.0f; //非法
//非法,x出现2次
pos.xx = float2(3.0,4.0f);
//不合法-使用混合限定符
pos.xy = float4(1.0f,2.0,3.0,4.0);
float4 pos4 = float4(1.0f,2.0f,3.0f,4.0f);
pos4.x = 1.0f;
pos4.y = 2.0f;
//非法,.rgba与.xyzw 混合使用
pos4.xg = float2(2.0f,3.0f);
////非法,.rgba与.xyzw 混合使用
float3 coord = pos4.ryz;
float4 pos5 = float4(1.0f,2.0f,3.0f,4.0f);
//非法,使用指针来指向向量/分量
my_func(&pos5.xy);
float4x4 m;
//将第二排的值设置为0
m[1] = float4(2.0f);
//设置第一行/第一列为1.0f
m[0][0] = 1.0f;
//设置第三行第四列的元素为3.0f
m[2][3] = 3.0f;
//float4类型向量的所有可能构造方式
float4(float x);
float4(float x,float y,float z,float w);
float4(float2 a,float2 b);
float4(float2 a,float b,float c);
float4(float a,float2 b,float c);
float4(float a,float b,float2 c);
float4(float3 a,float b);
float4(float a,float3 b);
float4(float4 x);
//float3类型向量的所有可能的构造的方式
float3(float x);
float3(float x,float y,float z);
float3(float a,float2 b);
float3(float2 a,float b);
float3(float3 x);
//float2类型向量的所有可能的构造方式
float2(float x);
float2(float x,float y);
float2(float2 x);
//多个向量构造器的使用
float x = 1.0f,y = 2.0f,z = 3.0f,w = 4.0f;
float4 a = float4(0.0f);
float4 b = float4(x,y,z,w);
float2 c = float2(5.0f,6.0f);
float2 a = float2(x,y);
float2 b = float2(z,w);
float4 x = float4(a.xy,b.xy);
//定义一个缓存
//Metal 所谓的缓存,就是一块用指针指向的区域 (显存)
//在客户端代码中,使用指针指向的一块代码区域(内存)
//2个修饰符 : device (设备空间,其实就是GPU) , constant (设备空间只读)
//作用:当做函数的参数来传递
device float4 *device_buffer ;
//结构体
struct my_user_data{
float4 a;
float b;
int2 c;
};
my_user_data *a;
constant my_user_data *user_data; // 只读
//纹理
//纹理数据就是一个句柄,它指向一个 一维/二维/三维 纹理数据\
enum class access {sample,read,write};
texture1d<T,access a = access::sample>
texture1d_array<T,access a = access::sample>
texture2d<T,access a = access::sample>
texture2d_array<T,access a = access::sample>
texture3d<T,access a = access::sample>
texturecube<T,access a = access::sample>
texture2d_ms<T,access a = access::read>
//T表示类型: int , float
//带有深度格式的纹理必须被声明为下面纹理数据类型中的一个
enum class depth_forma {depth_float};
depth2d<T,access a = depth_format::depth_float>
depth2d_array<T,access a = access::sample,depth_format d = depth_format::depth_float>
depthcube<T,access a = access::sample,depth_format d = depth_format::depth_float>
depth2d_ms<T,access a = access::read,depth_format d = depth_format::depth_float>
//使用方式:
void foo (texture2d<float> imgA[[texture(0)]],
texture2d<float,access::read> imgB[[texture(1)]],
texture2d<float,access::write> imgC[[texture(2)]])
{
//...
}
//函数修饰符.
/*
3个函数修饰符:
1. kernel : 并行计算函数
2. vertex : 顶点函数
3. fragment : 片元函数
*/
//1.并行计算函数(kernel)
kernel void CCTestKernelFunctionA(int a,int b)
{
/*
注意:
1. 使用kernel 修饰的函数返回值必须是void 类型
2. 一个被函数修饰符修饰过的函数,不允许在调用其他的被函数修饰过的函数. 非法
3. 被函数修饰符修饰过的函数,只允许在客户端对齐进行操作. 不允许被普通的函数调用.
*/
//不可以的!
//一个被函数修饰符修饰过的函数,不允许在调用其他的被函数修饰过的函数. 非法
CCTestKernelFunctionB(1,2);//非法
CCTestVertexFunctionB(1,2);//非法
//可以! 你可以调用普通函数.而且在Metal 不仅仅只有这3种被修饰过的函数.普通函数也可以存在
CCTest();
}
kernel void CCTestKernelFunctionB(int a,int b)
{
}
//顶点函数
vertex int CCTestVertexFunctionB(int a,int b){
}
//片元函数
fragment int CCTestVertexFunctionB(int a,int b){
}
void CCTest()
{
}
//变量/函数参数地址空间修饰符
/*
1.device
2.threadgroup
3.constant
4.thread
*/
/*
注意:
1. 所有被(kernel,vertex,fragment)所修饰的参数变量,如果其类型是指针/引用 都必须带有地址空间修饰符.
2. 被fragment修饰的片元函数, 指针/引用必须被device/constant/threadgroup
*/
//变量/参数地址空间修饰符
void CCTestFouncitionE(device int *g_data,
threadgroup int *l_data,
constant float *c_data
)
{
//...
}
// 设备地址空间: device 用来修饰指针.引用
//1.修饰指针变量
device float4 *color;
struct CCStruct{
float a[3];
int b[2];
};
//2.修饰结构体类的指针变量
device CCStruct *my_CS;
/*
1. threadgroup 被并行计算计算分配内存变量, 这些变量被一个线程组的所有线程共享. 在线程组分配变量不能被用于图像绘制.
2. thread 指向每个线程准备的地址空间. 在其他线程是不可见切不可用的
*/
kernel void CCTestFouncitionF(threadgroup float *a)
{
//在线程组地址空间分配一个浮点类型变量x
threadgroup float x;
//在线程组地址空间分配一个10个浮点类型数的数组y;
threadgroup float y[10];
}
constant float sampler[] = {1.0f,2.0f,3.0f,4.0f};
kernel void CCTestFouncitionG(void)
{
//在线程空间分配空间给x,p
float x;
thread float p = &x;
}
//常量地址修饰空间
//constant 显存,但是它是只读.
//属性修饰符
/*
1. device buffer(设备缓存)
2. constant buffer(常量缓存)
3. texture Object(纹理对象)
4. sampler Object(采样器对象)
5. 线程组 threadgroup
属性修饰符目的:
1. 参数表示资源如何定位? 可以理解为端口
2. 在固定管线和可编程管线进行内建变量的传递
3. 将数据沿着渲染管线从顶点函数传递片元函数.
在代码中如何表现:
1.已知条件:device buffer(设备缓存)/constant buffer(常量缓存)
代码表现:[[buffer(index)]]
解读:不变的buffer ,index 可以由开发者来指定.
2.已知条件:texture Object(纹理对象)
代码表现: [[texture(index)]]
解读:不变的texture ,index 可以由开发者来指定.
3.已知条件:sampler Object(采样器对象)
代码表示: [[sampler(index)]]
解读:不变的sampler ,index 可以由开发者来指定.
4.已知条件:threadgroup Object(线程组对象)
代码表示: [[threadgroup(index)]]
解读:不变的threadgroup ,index 可以由开发者来指定.
*/
//并行计算着色器函数add_vectros ,实现2个设备地址空间中的缓存A与缓存B相加.然后将结果写入到缓存out.
//属性修饰符"(buffer(index))" 为着色函数参数设定了缓存的位置
kernel void add_vectros(
const device float4 *inA [[buffer(0)]],
const device float4 *inB [[buffer(1)]],
device float4 *out [[buffer(2)]]
uint id[[thread_position_in_grid]])
{
out[id] = inA[id] + inB[id];
}
网友评论