编写3D图形渲染逻辑
和并⾏计算核⼼逻辑
的编程语⾔,编写Metal框架的APP须要使⽤Metal着⾊语⾔程序.假如当须要利用GPU的高计算能力时,咱们也可使用Metal来帮助咱们,由于它不只仅能够作图形渲染,也拥有高并发计算的能力.编程
好比在AVFoundation作人脸识别、二维码识别、音视频的编解码等工做中,须要对每一帧的数据进行计算,这种状况下的计算量是很是大的,苹果就利用硬件加速器(指的就是GPU)来进行高并发计算。数组
Metal ⽀持后缀表示字⾯量类型, 例如 0.5F, 0.5f; 0.5h, 0.5H. 这在GLSL中是不被容许的.缓存
bool a = true;
char b = 1;
int c = 15;
size_t d = 1;
复制代码
向量支持以下类型:markdown
n指的是维度并发
语法规则:
bool2 a = [1, 2]; //布尔类型的二维向量,也能够理解为布尔数组使用
float4 pos = float4(1.0f,2.0f,3.0f,4.0f);
// 获取pos中数据的方式
float x = pos[0]; //index下标
// 向量份量(字母)来获取元素有两种: 'xyzw' | 'rgba',这是索引对应的
int4 t = int4(0,1,2,3);
int a = t.x; // a = 0
int b = t.r; // b = 0
// 多个份量访问
float4 t = float4(0,1,2,3);
t.xyzw = float4(3,4,5,6); // 覆盖从新赋值
t.z = 7;
t.xy = float2(8,9);
// 多份量乱序/重复访问 -- GLSL是不容许乱序的
float4 t = float(0,1,2,3);
float4 tt = t.zywx;
float4 ttt = t.xxyy;
t.xw = float2(4,5);
t.wx = float2(6,7);
t.ww = float2(6,7); // !!! 重复赋值是不容许的,
float2 t = float2(1,2);
t.x = 3;
t.z = 4; // 这是不合法的,float2 => z 是数组越界了
// 不能够混用
float t = float4(0,1,2,3);
t.x = 4;
t.xg = float2(5,6); // 不合法,‘xyzw’和‘rgba’是不能够混用的
复制代码
nxm分别指的是矩阵的行数和列数,最大支持4行4列 例:4x4框架
语法规则
float4x4 m;
m[1] = float4(2.0f); // 指定第二行都为2.0f
m[1] = float4(1,2,3,4); // 指定第二行不一样的数据
m[0][1] = 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);
复制代码
纹理类型是⼀个句柄, 它指向⼀个⼀维/⼆维/三维纹理数据. 在⼀个函数中描述纹理对象的类型;函数
enum class access {sample ,read ,write};高并发
texture1d<T, access a = access::sample>
texture2d<T, access a = access::sample>
texture3d<T, access a = access::sample>
T:设定从纹理中读取或是向纹理中写⼊时的颜⾊类型. T能够是half, float, short, int 等
复制代码
代码示例:
void 函数名称 (texture2d<float> imgA [[ texture(0) ]] ,
texture2d<float, access::read> imgB [[ texture(1) ]],
texture2d<float, access::write> imgC [[ texture(2) ]])
{
...
}
复制代码
修饰符如下详细说明.ui
在GLSL中能够对纹理的环绕方式、过滤方式等进行设置,一样在Metal中,可使用采样器类型,来对一个纹理进行采样操做.在Metal框架中,有一个对应着色器语言的采样器的对象MTLSampleState
,做为图形渲染着色器函数参数,或是并行计算函数的参数传递spa
enum class min_filter { nearest, linear }; 设置纹理采样的缩⼩过滤方式;
enum class mag_filter { nearest, linear }; 设置纹理采样的放⼤过滤方式;
设置纹理s,t,r坐标的寻址模式; 环绕方式
enum class s_address { clamp_to_zero, clamp_to_edge, repeat, mirrored_repeat };
enum class t_address { clamp_to_zero, clamp_to_edge, repeat, mirrored_repeat };
enum class r_address { clamp_to_zero, clamp_to_edge, repeat, mirrored_repeat };
复制代码
注意:在Metal 程序中初始化的采样器必须使⽤
constexpr
修饰符声明
代码示例:
constexpr sampler s(coord::pixel, address::clamp_to_zero, filter::linear);
constexpr sampler a(coord::normalized);
constexpr sampler b(address::repeat);
constexpr sampler s(address::clamp_to_zero, filter::linear)
复制代码
Metal函数修饰符有3种
数据并⾏计算着⾊函数
. 它能够被分配在⼀维/⼆维/三维线程组中去执⾏.(当某段代码须要高效计算时,可使用kernel进行修饰)顶点着⾊函数
, 它将为顶点数据流中的每一个顶点数据执⾏⼀次,而后为每一个顶点⽣成数据输出到绘制管线,(能够理解为在GLSL种,顶点着色源码main函数中,返回的gl_Position);⽚元着⾊函数
, 它将为⽚元数据流中的每一个⽚元和其关联执⾏⼀次,而后将每一个⽚元⽣成的颜⾊数据输出到绘制管线中(能够理解为在GLSL中,片元着色源码main函数中,返回的gl_FragColor)注意:
void类型
;不能够调用被函数修饰符修饰的函数
,不然编译失败kernel void test1(...){...}
vertex float4 test2(...)
{
test1(...); //编译失败
}
void test3(...)
{
test1(...); //编译经过
}
复制代码
Metal着色器语言使用地址空间修饰符
来表示一个函数变量或参数变量,被分配在哪一片内存区域
.
注意:
被device修饰的参数,会指向设备内存池分配出来的缓存对象,即显存.它是可读写的,并且读取速度快.
device float4 *color;
struct foo{
float2[3];
int b[2];
};
device foo *info;
复制代码
纹理对象是默认分配在设备内存空间; ⼀个纹理对象的内容⽆法直接访问. Metal 提供读写纹理的内建函数;
线程组地址空间⽤于为并⾏计算着⾊函数
分配内存变量. 这些变量被⼀个线程组的全部线程共享
. 在线程组地址空间分配的变量不能被⽤于图形绘制着⾊函数顶点着⾊函数, ⽚元着⾊函数
kernel void funcName(threadgroup float *name [[ threadgroup(0) ]])
{
// A float allocated in threadgroup address space
threadgroup float x;
// An array of 10 floats allocated in threadgroup address space
threadgroup float b[10];
}
复制代码
只读的
常量
.注意:
声明为常量的变量赋值会产⽣编译错误,声明常量可是没有赋初值也会编译错误.
constant float samples[] = {1,2,3};
samples[] = {3,3,3}; // 编译错误
constant float a; // 编译错误
复制代码
能够理解Swift中let,OC中const
thread地址空间指向每一个线程准备的地址空间, 这个线程的地址空间定义的变量在其余线程不可⻅
, 在图形绘制着⾊函数或者并⾏计算着⾊函数中,均可以声明的变量thread地址空间分配;
但愿变量能够在单独的线程中进行高效的计算,但又不想被共享时,多用于并发运算中,而不是图形处理
kernel void func(...)
{
float x; // 普通变量
thread float b; //分配在线程地址空间
}
复制代码
设备地址空间
的任意数据类型的指针或者引用常量地址空间
的任意数据类型的指针或引⽤被着色器函数的缓存(device 和 constant) 不能重名
对于每一个着⾊器函数来讲, ⼀个修饰符是必须指定的. 他⽤来设定⼀个缓存,纹理, 采样器的位置(location:如GLSL中的glGetAttribLocation
)
[[buffer (index)]]
[[texture (index)]]
[[sampler (index)]]
[[threadgroup (index)]]
index是⼀个unsigned integer类型的值,它表示了⼀个缓存、纹理、采样器参数的位置(在函数参数索引表中的位置)。 从语法上讲,属性修饰符的声明位置应该位于参数变量名以后
//代码案例:并⾏计算着⾊函数add_vectors,把两个设备地址空间中的缓存inA和inB相 加,而后把结果写⼊到缓存out
kernel void add_vectors(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];
}
复制代码
当结构体中定义多个颜色,须要作区分时
struct myColors{
float4 clr_f [[ color(0) ]]; // color attachment 1 附着点1
int4 clr_i [[ color(1) ]]; // color attachment 2 附着点2
uint clr_ui [[ color(2) ]];
}
复制代码