美文网首页
Metal Shading Language

Metal Shading Language

作者: lkm_0bdc | 来源:发表于2020-08-24 00:40 被阅读0次

Metal着色语言是用来编写3D图形渲染逻辑 和并行计算核心逻辑的一门编程语言,当你使用Metal框架来完成APP的实现时则需要使用Metal编程语言
OpenGL ES 的GLSL 文件加载的是一个字符串形式,但是Metal文件不是,因为Metal语言使用Clang 和LLVM进行编译处理。
Metal基于C++11.0语言设计,我们主要用来编写在GPU上执行的图像渲染逻辑代码以及通用并行计算逻辑代码。

C++ 11.0 和 Metal语言的异同之处

  • Lambda 表达式
  • 递归函数调用
  • 动态转换操作符
  • 类型识别
  • 对象创建new 和销毁delete操作符
  • 操作符noexcept
  • goto跳动
  • 变量存储修饰符register 和 thread_local
  • 虚函数修饰符
  • 派生类
  • 异常处理
  • C++标准库在Metal 语言中也不可使用

Metal 语言中对于指针使用的限制

Metal图形和并行计算函数用到的入参数,如果是指针必须使用地址空间修饰符(device,threadgroup,constant)
1.不支持函数指针
2.函数名不能出现main

Metal 像素坐标系统:Metal中纹理/帧缓存区attachment的像素使用的坐标系统的原点是在左上角。

Metal数据类型

1.标量
2.向量
3.矩阵

标量

Metal 中标量类型如下图:


数据类型

-常用的基本类型有bool int uint
-bool布尔类型 true/false
-unsigned char 可以简写成uchar
-Unsigned short 可以简写成 ushort
-half 相当于oc中的float float相当于oc中的double
-size_t 表示内存空间,相当于oc中的sizeof

bool a = true;
char b = 5;
int  d = 15;
size_t c = 1;
ptrdiff_t f = 2;

向量

向量支持如下类型
Booln,charn,shortn,intn,intn,ucharn,ushortn,uintn,halfn,floatn
向量中的n表示纬度

bool2 A= {1,2};
float4 pos = float4(1.0,2.0,3.0,4.0);
float x = pos[0];
float y = pos[1];
float4 VB;
for(int i = 0; i < 4 ; i++)
    VB[i] = pos[i] * 2.0f;

通过向量字母来获取元素 有xyzw ,rgba

//通过向量字母来获取元素
int4 test = int4(0,1,2,3);
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.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);

访问的可以乱序/重复
1.赋值(左边)时分量不可以重复,取值(右边)可以重复
2.2.xyzw与rgba不可以混合使用
float2 pos;
pos.x = 1.0f; //合法
pos.z = 1.0f; //非法

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);

GLSL中向量不可以乱序访问,只是和Metal 中的向量相似,但不等价

矩阵

矩阵支持的类型如下:
-Halfnxm,floatnxm
-nxm分别指的是矩阵的行数和列数

普通的矩阵其本质就是一个数组

float4x4 m;
//将第二排的值设置为0
m[1] = float4(2.0f);

//设置第一行/第一列为1.0f
m[0][0] = 1.0f;

//设置第三行第四列的元素为3.0f
m[2][3] = 3.0f;

float4类型向量的构造方式

-1个float构成,表示一行都是这个值
-4个float构成
-2个float2构成
-1个float2+2个float构成(顺序可以任意组合)
-1个float2+1个float
-1个float4

//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);

float4类型向量的构造方式

-1个float构成,表示都是一个值
-3float
-1个float +1float2(顺序任意组合)
-1float3

//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类型向量的构造方式

-1个float2
-1个float+1float
-1个float2

//float2类型向量的所有可能的构造方式
float2(float x);
float2(float x,float y);
float2(float2 x);

纹理TextUres类型

纹理类型是一个句柄,它指向一个一纬/二维/三维纹理数据

纹理访问权限
在一个函数中描述纹理对象的类型
access枚举值由Metal定义,enum class access{sample,read,write},有三种访问权限,当没写access,默认access是sample

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等

//纹理texture
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>

采样器类型 Samplers

采色器类型

采取类型决定了如何对一个纹理进行采样操作,在Metal框架中有一个对应着色器语言的采样器的对象。
MTLSamplers 这个对象作为图形渲染着色器函数参数或是并行计算函数的参数传递

1. coord:从纹理中采样时,纹理坐标是否需要归一化
   enum class coord { normalized, pixel };
2. filter:纹理采样过滤方式,放大/缩小过滤方式
 enum class filter { nearest, linear };
3. min_filter:设置纹理采样的缩小过滤方式
enum class min_filter { nearest, linear };
4. mag_filter:设置纹理采样的放大过滤方式
enum class mag_filter { nearest, linear };
5.  s_address、t_address、r_address:设置纹理s、t、r坐标(对应纹理坐标的x、y、z)的寻址方式
 s坐标:enum class s_address { clamp_to_zero, clamp_to_edge, repeat, mirrored_repeat };
t坐标:enum class t_address { clamp_to_zero, clamp_to_edge, repeat, mirrored_repeat };
r坐标:enum class r_address { clamp_to_zero, clamp_to_edge, repeat, mirrored_repeat };
6. address:设置所有纹理坐标的寻址方式
enum class address { clamp_to_zero, clamp_to_edge, repeat, mirrored_repeat };
7. mip_filter:设置纹理采样的mipMap过滤模式, 如果是none,那么只有一层纹理生效;
enum class mip_filter { none, nearest, linear };

注意:在Metal 程序中初始化的采样器必须使用constexpr修饰符声明

函数修饰符

Metal 着色器语言支持下列的函数修饰符:
-kernel表示该函数是一个数据并行计算着色函数,它将被分配在一个一维/二维/三维的线程组网络中执行。
-vertex表示该函数是一个顶点着色函数,它将为顶点数据流中的每个顶点数据执行一次,然后为每个顶点生成数据输出到绘制管线中去。
-fragment表示该函数是一个片元着色函数,它将片元数据流中的每个片元和其相关联的数据执行一次然后为每个片元成数据输出到绘制管线中去。

1.使用kernel修饰的函数,其返回值类型必须是void类型:
2.只有图形着色函数才可以被vertex 和 fragment修饰,对于图形着色函数,返回值类型可以辨认出它是为顶点做计算还是为每像素做计算,图形着色函数的返回值可以为void,但是这也就意味着该函数不产生数据输出到绘制管线,这是一个无意义的动作。
3.一个被函数修饰符修饰的函数不能再调用其他也被函数修饰符修饰的函数,这样会导致编译失败(kernel,vertex,fragmen修饰的函数不能相互调用,相同修饰符修饰的函数也不能相互调用但可以调用普通函数 )
4.Metal中并不是所有函数都需要上述3个修饰符修饰,是可以在Metal中定义普通函数的,即不带任何修饰符的函数。

kernel void hello2(...){
 }
 vertex float4 hello1(...){ 
//编译失败
hello2(...); //错误调用
};

变量和参数的地址空间修饰符

Metal 着色器语言使用地址空间修饰符 来表示一个函数变量或者参数变量 被分配与那一片内存区域,所有的着色器(vertex,fragment,kernel)的参数,如果是指针或是引用,都必须带有地址空间修饰符号。
-device
-threadgrounp
-constant
-thread
对于图形着色器函数,其指针或是引用类型的参数必须定义为device或是constant地址空间。
对于并行计算着色函数,其指针或是引用类型的参数必须定义为device或是threadgrounp或是constant地址空间

Device Address Space(设备地址空间)

在设备地址空间(Device)指向设备内存池分配出来的缓存对象,它是可读也是可写。
一个缓存对象以被声明成一个标量,向量或是用户自定义结构体的指针或是引用。

Device float4 *color;
Struct foo{
float a[3];
Int b[2];
};
Device Foo *my_info;

纹理对象总是在设备地址分配内存,device地址空间修饰符不必出现在纹理类型定义中,一个纹理对象的内容无法直接访问。Metal提供读写纹理的内建函数。

threadgrounp Address Space 线程组地址空间

线程组地址空间用于为并行计算函数分配内存变量,这些变量被一个线程组的所有线程共享,在线程组地址空间分配的变量不能被用于图形绘制着色函数【顶点着色函数,片元着色函数】

在并行计算着色函数中,在线程组地址空间分配的变量为一个线程组使用,声明周期和线程组相同

/*
 1. threadgroup 被并行计算计算分配内存变量, 这些变量被一个线程组的所有线程共享. 在线程组分配变量不能被用于图像绘制.
 2. thread 指向每个线程准备的地址空间. 在其他线程是不可见切不可用的
 */
kernel void CCTestFouncitionF(threadgroup float *a){
 //在线程组地址空间分配一个浮点类型变量x
    threadgroup float x;
     //在线程组地址空间分配一个10个浮点类型数的数组y;
    threadgroup float y[10];
    }

constant Adress Space

常量地址空间指向的缓存对像也是从设备内存池分配存储,但是它是只读的。
在程序域的变量必须定义在常量地址空间并且声明的时候初始化,用来初始化的值必须是编译时的常量。
在程序域的变量的生命周期和程序一样,在程序中的并行计算着色函数或者图形绘制着色函数调用,但是constant的值会保持不变。

注意:常量地址空间的执政会是引用作为函数的参数,向声明为常量的变量赋值会产生编译错误。

constant float samples[] = {1.0f,2.0f,3.0f,4.0f};
//对一个常量地址空间的变量进行修改也会失败,因为它只读的
sampler[4] = {3,3,3,3};//编译失败
//定义为常量地址空间声明时不赋初值也会编译失败
constant float a;

thread Address Space 线程地址空间

thread 地址空间指向每个线程准备的地址空间,这个线程的地址空间定义的变量在其他线程不可见,在图形绘制着色函数或者并行计算着色函数中声明的变量thread地址空间分配

kernel void my_func(...)
{
float x;
thread float p = &x;
...
}

函数参数与变量

图形绘制或者并行计算着色函数的输入输出都是通过参数传递,除了常量地址空间变量和程序域定义的采样器以外。
-device buffer:设备缓存,一个指向设备地址空间的任意数据类型的指针或者引用
-constant buffer :常量缓存区,一个指向常量地址空间的任意数据类型的指针或引用
-texture:纹理对象
-sampler :采样器对象
-threadGrounp:在线程组中供各线程共享的缓存

注意:被着色器函数的缓存(device和constant)不能重名
Attribute Qualifiers to Locate Buffers,Textures,and Samplers 用于寻址,纹理,采样器的属性修饰符

对于每个着色器函数来说,一个修饰符是必须指定的。它用来设定一个缓存,纹理,采样器的位置

-device buffers /constant buffer -->[[buffer(index)]]
-texture -->[[texture(index)]]
-sampler -->[[sampler(index)]]
-threadgrounp -->[[threadgrounp(index)]]

index是一个unsigned integer类型的值,它表示了一个缓存、纹理、采样器参数的位置(在函数参数索引表中的位置)。从语法上讲,属性修饰符的声明位置应该位于参数变量名之后

例子中展示了一个简单的并行计算着色函数add_vectors,它把两个设备地址空间中的缓存inA和inB相加,然后把结果写入到缓存out。属性修饰符“(buffer(index))”为着色函数参数设定了缓存的位置。

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];
}

thread_position_in_grid:用于表示当前节点在多线程网络中的位置

内建变量属性修饰符

-[[vertex_id]] 顶点id标识符
-[[position]]顶点信息(float4)/口述了片元的窗口相对坐标(x,y,z,1/w)
-[[point_size]]点的大小(float)
-[[color(m)]] 颜色,m编译前的确定

struct MyFragmentOutput{
//color attachment 0
float4 car_f [[color(0)]];//color attchment 1
int4 car_i [[color(1)]];//color attachment 2
uint4 car_ui [[color(2)]];
};
fragment MyFragmentOutput my_frag_shader(...)
{
MyFragmentOutput f;
...
f.clr_f = ...;
...
return f;
}

相关文章

网友评论

      本文标题:Metal Shading Language

      本文链接:https://www.haomeiwen.com/subject/jghgjktx.html