① Metal中不支持C++11.0的如下特性:
② C++标准库在Metal语言中也不可使用
③ Metal语言对于指针使用的限制
类型 | 描述 |
---|---|
bool | 布尔类型,取值范围true、false;true可以拓展为整数常量1,false可以拓展为整数常量0 |
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 | 表示一个空的值集合 |
int4 test = int4(0,1,2,3);
int a = test.x; // 获取的向量元素0
int b = test.y; // 获取的向量元素1
int c = test.z; // 获取的向量元素2
int d = test.w; // 获取的向量元素3
int e = test.r; // 获取的向量元素0
int f = test.g; // 获取的向量元素1
int g = test.b; // 获取的向量元素2
int h = test.a; // 获取的向量元素3
float4 c;
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);
// 可以仅对 xw / wx 修改
// 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);
// 可以仅对 xyz 进行修改
// 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; // 非法,pos是二维向量,没有z这个索引
float3 pos2;
pos2.z = 1.0f; // 合法
pos2.w = 1.0f; // 非法
// 赋值时分量不可重复,取值时分量可重复
// 非法,x出现2次
pos.xx = float2(3.0,4.0f);
pos.xy = swiz.xx;
// 向量中xyzw与rgba两组分量不能混合使用
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;
float4x4 m;
// 将第二行的所有值都设置为2.0
m[1] = float4(2.0f);
// 设置第一行/第一列为1.0f
m[0][0] = 1.0f;
// 设置第三行第四列的元素为3.0f
m[2][3] = 3.0f;
// float4类型向量的所有可能构造方式
// 1个一维向量,表示一行都是x
float4(float x);/
// 4个一维向量 --> 4维向量
float4(float x,float y,float z,float w);
// 2个二维向量 --> 4维向量
float4(float2 a,float2 b);
// 1个二维向量+2个一维向量 --> 4维向量
float4(float2 a,float b,float c);
float4(float a,float2 b,float c);
float4(float a,float b,float2 c);
// 1个三维向量+1个一维向量 --> 4维向量
float4(float3 a,float b);
float4(float a,float3 b);
// 1个四维向量 --> 4维向量
float4(float4 x);
纹理类型是一个句柄,指向一维/二维/三维纹理数据,而纹理数据对应一个纹理的某个level的mipmap的全部或者一部分;
纹理的访问权限
在一个函数中描述纹理对象的类型, access枚举值由Metal定义,定义了纹理的访问权利 enum class access {sample, read, write};,有以下3种访问权利,当没设定access时,默认的access 就是 sample;
① sample: 纹理对象可以被采样(即使用采样器去纹理中读取数据,相当于OpenGL ES的GLSL中sampler2D),采样一维这时使用 或者 不使用都可以从纹理中读取数据(即可读可写可采样);
② read:不使用采样器,一个图形渲染函数 或者 一个并行计算函数可以读取纹理对象(即仅可读);
③ write:一个图形渲染函数或者一个并行计算可以向纹理对象写入数据(即可读可写);
定义纹理类型
描述一个纹理对象/类型,有以下三种方式,分别对应一维/二维/三维,其中T代表 泛型,设定了从纹理中读取数据 或是 写入时的颜色类型,T可以是half、float、short、int等
access表示纹理访问权限,当access没设定时,默认是sample:
texture1d
texture2d
texture3d
// 类型 变量 修饰符
/*
类型
- texture2d,读取的数据类型是float,没写access,默认是sample
- texture2d,读取的数据类型是float,读取的方式是read
- texture2d,读取的数据类型是float,读取的方式是write
变量名
- imgA
- imgB
- imgC
修饰符
- [[texture(0)]] 对应纹理0
- [[texture(1)]] 对应纹理1
- [[texture(2)]] 对应纹理2
*/
void foo (texture2d<float> imgA[[texture(0)]],
texture2d<float,access::read> imgB[[texture(1)]],
texture2d<float,access::write> imgC[[texture(2)]]) {
//...
}
采样器类型决定了如何对一个纹理进行采样操作,在Metal框架中有一个对应着色器语言的采样器的对象MTLSamplerState,这个对象作为图形渲染着色器函数参数或是并行计算函数的参数传递,有以下几种状态:
/*
constexpr:修饰符(必须写)
sampler:类型
s:采样器变量名称
参数
- coord: 是否需要归一化,不需要归一化,用的是像素pixel
- address: 地址环绕方式
- filter: 过滤方式
*/
constexpr sampler s(coord::pixel, address::clamp_to_zero, filter::linear);
constexpr sampler a(coord::normalized);
constexpr sampler b(address::repeat);
Metal 着⾊器语⾔⽀持下列的函数修饰符:
注意:
// 并行计算函数(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() {
.....
}
① Metal着色器语言使用地址空间修饰符来表示一个函数变量或者参数变量被分配于哪一片内存区域,有以下4中地址空间修饰符:
② 注意:
/*
注意:
1. 所有被(kernel,vertex,fragment)所修饰的参数变量,如果其类型是指针/引用 都必须带有地址空间修饰符.
2. 被fragment修饰的片元函数, 指针/引用必须被device/constant/threadgroup
*/
// 变量/参数地址空间修饰符
void CCTestFouncitionE(device int *g_data,
threadgroup int *l_data,
constant float *c_data
) {
//...
}
// 设备地址空间: device 用来修饰指针.引用
// 修饰指针变量
device float4 *color;
struct CCStruct{
float a[3];
int b[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 samples[] = { 1.0f, 2.0f, 3.0f, 4.0f };
// 对一个常量地址空间的变量进行修改也会失败,因为它只读
sampler[4] = {3,3,3,3}; //编译失败;
// 定义为常量地址空间声明时不赋初值也会编译失败
constant float a;
kernel void CCTestFouncitionG(void) {
// 在线程空间分配空间给x,p
float x;
thread float p = &x;
}
// 定义了片元输入的结构体
struct MyFragmentOutput {
// color attachment 0 颜色附着点0
float4 clr_f [[color(0)]];
// color attachment 1 颜色附着点1
int4 clr_i [[color(1)]];
// color attachment 2 颜色附着点2
uint4 clr_ui [[color(2)]];
};
fragment MyFragmentOutput my_frag_shader( ... ) {
MyFragmentOutput f;
....
f.clr_f = ...;
....
return f;
}