Metal簡述
- Metal著色器語言是用來編寫
3D圖形渲染邏輯、并行Metal計(jì)算核心邏輯的一門編程語言,當(dāng)你使用Metal框架來完成APP的實(shí)現(xiàn)時(shí)則需要使用Metal編程語言。 - Metal語言
使用Clang 和LLVM進(jìn)行編譯處理,編譯器對(duì)于在GPU上的代碼執(zhí)行效率有更好的控制 - Metal基于C++ 11.0語言設(shè)計(jì)的,在C++基礎(chǔ)上多了一些擴(kuò)展和限制,主要用來編寫在GPU上執(zhí)行的圖像渲染邏輯代碼以及通用并行計(jì)算邏輯代碼
- Metal 像素坐標(biāo)系統(tǒng):Metal中
紋理 或者 幀緩存區(qū)attachment的像素使用的坐標(biāo)系統(tǒng)的原點(diǎn)是左上角
Metal語言的限制
- Metal中不支持C++11.0的如下特性
- Lambda表達(dá)式
- 遞歸函數(shù)調(diào)用
- 動(dòng)態(tài)轉(zhuǎn)換操作符
- 類型識(shí)別
- 對(duì)象創(chuàng)建new和銷毀delete操作符
- 操作符noexcept
- go跳轉(zhuǎn)
- 變量存儲(chǔ)修飾符 register 和thread_local
- 虛函數(shù)修飾符
- 派生類
- 異常處理
- C++標(biāo)準(zhǔn)庫在Metal語言中也不可使用
- Metal語言對(duì)于指針使用的限制
- Metal圖形和并行計(jì)算函數(shù)用到的入?yún)ⅲū热?code>指針 / 引用),如果是
指針 / 引用必須使用地址空間修飾符(比如device、threadgroup、constant) - 不支持函數(shù)指針
- 函數(shù)名不能出現(xiàn)main
- Metal圖形和并行計(jì)算函數(shù)用到的入?yún)ⅲū热?code>指針 / 引用),如果是
Metal 基本數(shù)據(jù)類型
基本數(shù)據(jù)類型主要有 :
- 標(biāo)量
- 向量
- 矩陣
標(biāo)量
Metal中的標(biāo)量類型如下圖所示

- 常用的主要有 bool、int、uint 、half
- unsigned char可以簡寫為 uchar
- unsigned short 可以簡寫為 ushort
- unsigned int 可以簡寫為 uint
- 其中
half相當(dāng)于OC中的float,float相當(dāng)于OC中的double -
size_t用來表示內(nèi)存空間, 相當(dāng)于 OC中sizeof
bool a = true;
char b = 5;
int d = 15;
//用于表示內(nèi)存空間
size_t c = 1;
ptrdiff_t f = 2;
向量
向量支持如下類型
- booln、charn、shortn、intn、ucharn、ushortn、uintn、halfn、floatn,其中 n 表示向量的維度,最多不超過4維向量
//直接賦值初始化
bool2 A= {1,2};
//通過內(nèi)建函數(shù)float4初始化
float4 pos = float4(1.0,2.0,3.0,4.0);
//通過下標(biāo)從向量中獲取某個(gè)值
float x = pos[0];
float y = pos[1];
//通過for循環(huán)對(duì)一個(gè)向量進(jìn)行運(yùn)算
float4 VB;
for(int i = 0; i < 4 ; i++)
{
VB[i] = pos[i] * 2.0f;
}
在OpenGL ES的GLSL語言中,例如
2.0f,在著色器中書寫時(shí),是不能加f,寫成2.0,而在Metal中則可以寫成2.0f,其中f可以是大寫,也可以是小寫
向量的訪問規(guī)則
- 通過向量字母獲取元素: 向量中的向量字母僅有2種,分別為
xyzw、rgba
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
- 多個(gè)分量同時(shí)訪問
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);
- 多分量訪問可以亂序/重復(fù)
-
賦值時(shí)分量不可重復(fù),取值時(shí)分量可重復(fù) -
右邊是取值和左邊賦值都合法 -
xyzw與rgba不能混合使用
-
float4 pos = float4(1.0f,2.0f,3.0f,4.0f);
//向量分量逆序訪問
float4 swiz = pos.wxyz; //swiz = (4.0,1.0,2.0,3.0);
//向量分量重復(fù)訪問
float4 dup = pos.xxyy; //dup = (1.0f,1.0f,2.0f,2.0f);
//可以僅對(duì) 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);
//可以僅對(duì) xyz 進(jìn)行修改
//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這個(gè)索引
float3 pos2;
pos2.z = 1.0f; //合法
pos2.w = 1.0f; //非法
// 賦值 時(shí) 分量不可重復(fù),取值 時(shí) 分量可重復(fù)
//非法,x出現(xiàn)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;
GLSL中向量不能亂序訪問,只是和Metal中的向量相似,并不是等價(jià)。
矩陣
矩陣支持如下類型
-
halfnxm、floatnxm,其中nxm表示矩陣的行數(shù)和列數(shù),最多4行4列,其中half、float相當(dāng)于OC中的float、double - 普通的矩陣其
本質(zhì)就是一個(gè)數(shù)組
float4x4 m;
//將第二行的所有值都設(shè)置為2.0
m[1] = float4(2.0f);
//設(shè)置第一行/第一列為1.0f
m[0][0] = 1.0f;
//設(shè)置第三行第四列的元素為3.0f
m[2][3] = 3.0f;
- float4 類型向量的構(gòu)造方式
- 1個(gè)float構(gòu)成,表示一行都是這個(gè)值
- 4個(gè)float構(gòu)成
- 2個(gè)float2構(gòu)成
- 1個(gè)float2+2個(gè)float構(gòu)成(順序可以任意組合)
- 1個(gè)float2+1個(gè)float
- 1個(gè)float4
//float4類型向量的所有可能構(gòu)造方式
//1個(gè)一維向量,表示一行都是x
float4(float x);/
//4個(gè)一維向量 --> 4維向量
float4(float x,float y,float z,float w);
//2個(gè)二維向量 --> 4維向量
float4(float2 a,float2 b);
//1個(gè)二維向量+2個(gè)一維向量 --> 4維向量
float4(float2 a,float b,float c);
float4(float a,float2 b,float c);
float4(float a,float b,float2 c);
//1個(gè)三維向量+1個(gè)一維向量 --> 4維向量
float4(float3 a,float b);
float4(float a,float3 b);
//1個(gè)四維向量 --> 4維向量
float4(float4 x);
- float3 類型向量的構(gòu)造方式
- 1個(gè)float構(gòu)成,表示一行都是這個(gè)值
- 3個(gè)float
- 1個(gè)float+1個(gè)float2(順序可以任意組合)
- 1個(gè)float2
//float3類型向量的所有可能的構(gòu)造的方式
//1個(gè)一維向量
float3(float x);
//3個(gè)一維向量
float3(float x,float y,float z);
//1個(gè)一維向量 + 1個(gè)二維向量
float3(float a,float2 b);
//1個(gè)二維向量 + 1個(gè)一維向量
float3(float2 a,float b);
//1個(gè)三維向量
float3(float3 x);
- float2 類型向量的構(gòu)造方式
- 1個(gè)float構(gòu)成,表示一行都是這個(gè)值
- 2個(gè)float
- 1個(gè)float2
//float2類型向量的所有可能的構(gòu)造方式
//1個(gè)一維向量
float2(float x);
//2個(gè)一維向量
float2(float x,float y);
//1個(gè)二維向量
float2(float2 x);
Metal 其他類型
主要有以下兩種
- 紋理
- 采樣器
紋理類型
紋理類型是一個(gè)句柄,指向一維/二維/三維紋理數(shù)據(jù),而紋理數(shù)據(jù)對(duì)應(yīng)一個(gè)紋理的某個(gè)level的mipmap的全部或者一部分
紋理的訪問權(quán)限
在一個(gè)函數(shù)中描述紋理對(duì)象的類型
access枚舉值由Metal定義,定義了紋理的訪問權(quán)利 enum class access {sample, read, write};,有以下3種訪問權(quán)利,當(dāng)沒寫access時(shí),默認(rèn)的access 就是 sample
-
sample: 紋理對(duì)象可以被采樣(即使用采樣器去紋理中讀取數(shù)據(jù),相當(dāng)于OpenGL ES的GLSL中sampler2D),采樣一維這時(shí)使用 或者 不使用都可以從紋理中讀取數(shù)據(jù)(即可讀可寫可采樣) -
read:不使用采樣器,一個(gè)圖形渲染函數(shù)或者一個(gè)并行計(jì)算函數(shù)可以讀取紋理對(duì)象(即僅可讀) -
write:一個(gè)圖形渲染函數(shù) 或者 一個(gè)并行計(jì)算可以向紋理對(duì)象寫入數(shù)據(jù)(即可讀可寫)
定義紋理類型
描述一個(gè)紋理對(duì)象/類型,有以下三種方式,分別對(duì)應(yīng)一維/二維/三維,
- 其中
T代表泛型,設(shè)定了從紋理中讀取數(shù)據(jù) 或是 寫入時(shí)的顏色類型,T可以是half、float、short、int等 -
access表示紋理訪問權(quán)限,當(dāng)access沒寫時(shí),默認(rèn)是sampletexture1d<T, access a = access::sample>texture2d<T, access a = access::sample>texture3d<T, access a = access::sample>
//類型 變量 修飾符
/*
類型
- texture2d<float>,讀取的數(shù)據(jù)類型是float,沒寫access,默認(rèn)是sample
- texture2d<float,access::read>,讀取的數(shù)據(jù)類型是float,讀取的方式是read
- texture2d<float,access::write>,讀取的數(shù)據(jù)類型是float,讀取的方式是write
變量名
- imgA
- imgB
- imgC
修飾符
- [[texture(0)]] 對(duì)應(yīng)紋理0
- [[texture(1)]] 對(duì)應(yīng)紋理1
- [[texture(2)]] 對(duì)應(yīng)紋理2
*/
void foo (texture2d<float> imgA[[texture(0)]],
texture2d<float,access::read> imgB[[texture(1)]],
texture2d<float,access::write> imgC[[texture(2)]])
{
//...
}
采樣器類型 Samplers
采樣器類型決定了如何對(duì)一個(gè)紋理進(jìn)行采樣操作,在Metal框架中有一個(gè)對(duì)應(yīng)著色器語言的采樣器的對(duì)象MTLSamplerState,這個(gè)對(duì)象作為圖形渲染著色器函數(shù)參數(shù) 或是 并行計(jì)算函數(shù)的參數(shù)傳遞,有以下幾種狀態(tài):
-
coord:從紋理中采樣時(shí),紋理坐標(biāo)是否需要?dú)w一化enum class coord { normalized, pixel };
-
filter:紋理采樣過濾方式,放大/縮小過濾方式enum class filter { nearest, linear };
-
min_filter:設(shè)置紋理采樣的縮小過濾方式enum class min_filter { nearest, linear };
-
mag_filter:設(shè)置紋理采樣的放大過濾方式enum class mag_filter { nearest, linear };
-
s_address、t_address、r_address:設(shè)置紋理s、t、r坐標(biāo)(對(duì)應(yīng)紋理坐標(biāo)的x、y、z)的尋址方式- s坐標(biāo):
enum class s_address { clamp_to_zero, clamp_to_edge, repeat, mirrored_repeat }; - t坐標(biāo):
enum class t_address { clamp_to_zero, clamp_to_edge, repeat, mirrored_repeat }; - r坐標(biāo):
enum class r_address { clamp_to_zero, clamp_to_edge, repeat, mirrored_repeat };
- s坐標(biāo):
-
address:設(shè)置所有紋理坐標(biāo)的尋址方式enum class address { clamp_to_zero, clamp_to_edge, repeat, mirrored_repeat };
-
mip_filter:設(shè)置紋理采樣的mipMap過濾模式, 如果是none,那么只有一層紋理生效;enum class mip_filter { none, nearest, linear };
采樣器所有狀態(tài)如圖所示

1、openGL ES中紋理坐標(biāo)對(duì)應(yīng)的是
stq,Metal中紋理坐標(biāo)對(duì)應(yīng)是str
2、在Metal程序中初始化的采樣器必須使用constexpr修飾符聲明
/*
constexpr:修飾符(必須寫)
sampler:類型
s:采樣器變量名稱
參數(shù)
- coord: 是否需要?dú)w一化,不需要?dú)w一化,用的是像素pixel
- address: 地址環(huán)繞方式
- filter: 過濾方式
*/
constexpr sampler s(coord::pixel, address::clamp_to_zero, filter::linear);
constexpr sampler a(coord::normalized);
constexpr sampler b(address::repeat);
函數(shù)修飾符
Metal有以下3中函數(shù)修飾符,放在函數(shù)的最前面,即位于函數(shù)返回值的前面
-
kernel:表示該函數(shù)是一個(gè)數(shù)據(jù)并行計(jì)算著色函數(shù),它可以被分配在一維/二維/三維線程組中去執(zhí)行,表示函數(shù)要并行計(jì)算,其返回值類型必須是void類型,是一個(gè)高并發(fā)函數(shù) -
vertex:表示該函數(shù)是一個(gè)頂點(diǎn)著色函數(shù),它將為頂點(diǎn)數(shù)據(jù)流中的每個(gè)頂點(diǎn)數(shù)據(jù)執(zhí)行一次,然后為每個(gè)頂點(diǎn)生成數(shù)據(jù)輸出到繪制管線 -
fragment:表示該函數(shù)是一個(gè)片元著色函數(shù),它將為片元數(shù)據(jù)流中的每個(gè)片元 和其相關(guān)聯(lián)的數(shù)據(jù)執(zhí)行一次,然后將每個(gè)片元生成的顏色數(shù)據(jù)輸出到繪制管線中
1、使用kernel修飾的函數(shù),其返回值類型必須是void類型
2、一個(gè)被函數(shù)修飾符修飾的函數(shù)不能在調(diào)用其他也被函數(shù)修飾符修飾的函數(shù),這樣會(huì)導(dǎo)致編譯失敗,即Kernel、vertex、fragment修飾的函數(shù)不能相互調(diào)用,也不能同修飾符函數(shù)相互調(diào)用。但是可以調(diào)用普通函數(shù)
3、被函數(shù)修飾符修飾過的函數(shù),只允許在客戶端對(duì)其進(jìn)行操作.不允許被普通的函數(shù)調(diào)用
4、Metal中并不是所有函數(shù)都需要上述3個(gè)修飾符修飾,是可以在Metal中定義普通函數(shù)的,即不帶任何修飾符的函數(shù)
5、只有圖形著色函數(shù)才可以被vertex和fragment修飾,對(duì)于圖形著色函數(shù),通過返回值類型可以辨認(rèn)出是為頂點(diǎn)計(jì)算還是像素計(jì)算,其返回值也可以是void,意味著不產(chǎn)生數(shù)據(jù)輸出到繪制管線,是一個(gè)無意義的動(dòng)作
//并行計(jì)算函數(shù)(kernel)
kernel void CCTestKernelFunctionA(int a,int b)
{
/*
注意:
1\. 使用kernel 修飾的函數(shù)返回值必須是void 類型
2\. 一個(gè)被函數(shù)修飾符修飾過的函數(shù),不允許在調(diào)用其他的被函數(shù)修飾過的函數(shù). 非法
3\. 被函數(shù)修飾符修飾過的函數(shù),只允許在客戶端對(duì)其進(jìn)行操作. 不允許被普通的函數(shù)調(diào)用.
*/
//不可以的!
//一個(gè)被函數(shù)修飾符修飾過的函數(shù),不允許在調(diào)用其他的被函數(shù)修飾過的函數(shù). 非法
CCTestKernelFunctionB(1,2);//非法,錯(cuò)誤調(diào)用?。?!
CCTestVertexFunctionB(1,2);//非法,錯(cuò)誤調(diào)用!??!
//可以! 你可以調(diào)用普通函數(shù).而且在Metal 不僅僅只有這3種被修飾過的函數(shù).普通函數(shù)也可以存在
CCTest();
}
//并行計(jì)算函數(shù)
kernel void CCTestKernelFunctionB(int a,int b)
{
.....
}
//頂點(diǎn)函數(shù)
vertex int CCTestVertexFunctionB(int a,int b)
{
.....
}
//片元函數(shù)
fragment int CCTestVertexFunctionB(int a,int b)
{
.....
}
//普通函數(shù)
void CCTest()
{
.....
}
變量、參數(shù)的地址空間修飾符
Metal著色器語言使用地址空間修飾符來表示一個(gè) 函數(shù)變量 或者 參數(shù)變量 被分配于哪一片內(nèi)存區(qū)域,有以下4中地址空間修飾符
-
device: 設(shè)備地址空間 -
threadgroup: 線程組地址空間 -
constant常量地址空間 -
thread線程地址空間
1、所有的著色函數(shù)(vertex、fragment、kernel)的參數(shù),如果是指針/引用,都
必須帶有地址空間修飾符號(hào)
2、對(duì)于圖形著色器函數(shù)(即vertex/fragment修飾的函數(shù)),其指針/引用類型的參數(shù)必須定義為device、constant地址空間
3、對(duì)于并行計(jì)算函數(shù)(即kernel修飾的函數(shù)),其指針/引用類型的參數(shù)必須定義為device、threadgroup、constant
4、并不是所有的變量都需要修飾符,也可以定義普通變量(即無修飾符的變量)
/*
注意:
1\. 所有被(kernel,vertex,fragment)所修飾的參數(shù)變量,如果其類型是指針/引用 都必須帶有地址空間修飾符.
2\. 被fragment修飾的片元函數(shù), 指針/引用必須被device/constant/threadgroup
*/
//變量/參數(shù)地址空間修飾符
void CCTestFouncitionE(device int *g_data,
threadgroup int *l_data,
constant float *c_data
)
{
//...
}
device:設(shè)備地址空間修飾符
- 設(shè)備地址空間指向
設(shè)備內(nèi)存池分配出來的緩存對(duì)象(設(shè)備指顯存,即GPU),即GPU空間分配的緩存對(duì)象,它是可讀可寫的,一個(gè)緩存對(duì)象可以被聲明成一個(gè)標(biāo)量、向量或是用戶自定義結(jié)構(gòu)體的指針/引用 - device放在變量類型之前
除了可以修飾 圖形著色器函數(shù) / 并行計(jì)算函數(shù)參數(shù),還可以修飾指針變量 和 結(jié)構(gòu)體指針變量
// 設(shè)備地址空間: device 用來修飾指針.引用
//1.修飾指針變量
device float4 *color;
struct CCStruct{
float a[3];
int b[2];
};
//2.修飾結(jié)構(gòu)體類的指針變量
device CCStruct *my_CS;
1、紋理對(duì)象總是在
設(shè)備地址空間分配內(nèi)存,即紋理對(duì)象默認(rèn)在GPU分配內(nèi)存
2、device地址空間修飾符不必出現(xiàn)在紋理類型定義中
3、一個(gè)紋理對(duì)象的內(nèi)容無法直接訪問,Metal提供讀寫紋理的內(nèi)建函數(shù),通過內(nèi)建函數(shù)訪問紋理對(duì)象
threadgroup:線程組地址空間修飾符
- 線程組地址空間用于為
并行計(jì)算著色器函數(shù)分配內(nèi)存變量,這些變量被一個(gè)線程組的所有線程共享,在線程組地址空間分配的變量不能用于圖形繪制著色函數(shù)(即頂點(diǎn)著色函數(shù) / 片元著色函數(shù)),即在圖形繪制著色函數(shù)中不能使用線程組 - 在并行計(jì)算著色函數(shù)中,在線程組地址空間分配的變量為一個(gè)線程組使用,生命周期和線程組相同
/*
1\. threadgroup 被并行計(jì)算計(jì)算分配內(nèi)存變量, 這些變量被一個(gè)線程組的所有線程共享. 在線程組分配變量不能被用于圖像繪制.
2\. thread 指向每個(gè)線程準(zhǔn)備的地址空間. 在其他線程是不可見切不可用的
*/
kernel void CCTestFouncitionF(threadgroup float *a)
{
//在線程組地址空間分配一個(gè)浮點(diǎn)類型變量x
threadgroup float x;
//在線程組地址空間分配一個(gè)10個(gè)浮點(diǎn)類型數(shù)的數(shù)組y;
threadgroup float y[10];
}
constant:常量地址空間修飾符
- 常量地址空間指向的緩存對(duì)象也是從設(shè)備內(nèi)存池分配存儲(chǔ),
僅可讀 - 在程序域的變量必須定義在常量地址空間并且聲明時(shí)初始化,用來初始化的值必須是編譯時(shí)的常量
- 在程序域的變量的生命周期和程序一樣,在程序中的并行計(jì)算著色函數(shù) 或者 圖形繪制著色函數(shù)調(diào)用,但是constant的值會(huì)保持不變
常量地址空間的指針/引用可以作為函數(shù)的參數(shù),向聲明為
常量的變量賦值會(huì)產(chǎn)生編譯錯(cuò)誤
2、聲明常量但是沒有賦予初值也會(huì)產(chǎn)生編譯錯(cuò)誤
constant float samples[] = { 1.0f, 2.0f, 3.0f, 4.0f };
//對(duì)一個(gè)常量地址空間的變量進(jìn)行修改也會(huì)失敗,因?yàn)樗蛔x的
sampler[4] = {3,3,3,3}; //編譯失敗;
//定義為常量地址空間聲明時(shí)不賦初值也會(huì)編譯失敗
constant float a;
thread:線程地址空間修飾符
- 線程地址空間指向每個(gè)線程準(zhǔn)備的地址空間,也是在GPU中,該線程的地址空間定義的變量
在其他線程不可見(即變量不共享) - 在圖形繪制著色函數(shù) 或者 并行計(jì)算著色函數(shù)中聲明的變量,
在線程地址空間分配存儲(chǔ)
kernel void CCTestFouncitionG(void)
{
//在線程空間分配空間給x,p
float x;
thread float p = &x;
}
函數(shù)參數(shù)與變量的傳遞修飾符,即屬性修飾符
圖形繪制 或者 并行計(jì)算著色器函數(shù)的輸入輸出都是通過參數(shù)傳遞,除了常量地址空間變量和程序域定義的采樣器之外, 其他參數(shù)修飾的可以是如下之一,有以下5種屬性修飾符:
-
device buffer設(shè)備緩存:一個(gè)指向設(shè)備地址空間的任意數(shù)據(jù)類型的指針/引用 -
constant buffer常量緩存:一個(gè)指向常量地址空間的任意數(shù)據(jù)類型的指針/引用 -
texture紋理對(duì)象 -
sampler采樣器對(duì)象 -
threadGroup在線程組中供線程共享的緩存
為什么需要屬性修飾符?
- 參數(shù)表示資源的定位,可以理解為端口,相當(dāng)于OpenGl ES中的
location - 在固定管線和可編程管線進(jìn)行內(nèi)建變量的傳遞
- 將數(shù)據(jù)沿著渲染管線從頂點(diǎn)函數(shù)傳遞到片元函數(shù)
傳遞修飾符在代碼中的體現(xiàn)
對(duì)于每個(gè)著色函數(shù)來說,一個(gè)修飾符是必須指定的,它用來設(shè)置一個(gè)緩存、紋理、采樣器的位置,傳遞修飾符對(duì)應(yīng)的寫法如下:
-
device buffer--->[[buffer(index)]] -
constant buffer--->[[buffer(index)]] -
texture--->[[texture(index)]] -
sampler--->[[sampler(index)]] -
threadGroup--->[[threadGroup(index)]]
在代碼中的表現(xiàn)如下:
在代碼中如何表現(xiàn):
1.已知條件:device buffer(設(shè)備緩存)/constant buffer(常量緩存)
代碼表現(xiàn):[[buffer(index)]]
解讀:不變的buffer ,index 可以由開發(fā)者來指定.
2.已知條件:texture Object(紋理對(duì)象)
代碼表現(xiàn): [[texture(index)]]
解讀:不變的texture ,index 可以由開發(fā)者來指定.
3.已知條件:sampler Object(采樣器對(duì)象)
代碼表示: [[sampler(index)]]
解讀:不變的sampler ,index 可以由開發(fā)者來指定.
4.已知條件:threadgroup Object(線程組對(duì)象)
代碼表示: [[threadgroup(index)]]
解讀:不變的threadgroup ,index 可以由開發(fā)者來指定.
1、index是一個(gè)unsigned interger類型的值,表示了一個(gè)
緩存、紋理、采樣器參數(shù)的位置(即在函數(shù)參數(shù)索引表中的位置,相當(dāng)于OpenGl ES中的location)
2、從語法上來說,屬性修飾符的聲明位置應(yīng)該位于參數(shù)變量名之后
//并行計(jì)算著色器函數(shù)add_vectros ,實(shí)現(xiàn)2個(gè)設(shè)備地址空間中的緩存A與緩存B相加.然后將結(jié)果寫入到緩存out.
//屬性修飾符"(buffer(index))" 為著色函數(shù)參數(shù)設(shè)定了緩存的位置
//thread_position_in_grid:用于表示當(dāng)前節(jié)點(diǎn)在多線程網(wǎng)格中的位置,并不需要開發(fā)者傳遞,是Metal自帶的。
/*
kernel:并行計(jì)算函數(shù)修飾符
void:函數(shù)返回值類型
add_vectros:函數(shù)名
const device float4 *inA [[buffer(0)]]:定義了一個(gè)float4類型的指針,指向一個(gè)4維向量空間,放在設(shè)備內(nèi)存空間(即顯存GPU中)
- const device:只決定放在哪里
- inA:變量名
- [[buffer(0)]] 對(duì)應(yīng) buffer中0這個(gè)id
*/
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];
}
//著色函數(shù)的多個(gè)參數(shù)使用不同類型的屬性修飾符的情況
//紋理讀取的方式的sampler,即采樣器,[[sampler(0)]]表示采樣器的緩存id
kernel void my_kernel(device float4 *p [[buffer(0)]],
texture2d<float> img [[texture(0)]],
sampler sam [[sampler(0)]])
{
//.....
}
常見的內(nèi)建變量修飾符
-
[[vertex_id]]:頂點(diǎn)id標(biāo)識(shí)符,并不由開發(fā)者傳遞 -
[[position]]- 在
頂點(diǎn)著色函數(shù)中,表示當(dāng)前的頂點(diǎn)信息,類型是float4、 - 還可以表示
描述了片元的窗口的相對(duì)坐標(biāo)(x,y,z,1/w),即該像素點(diǎn)在屏幕上的位置信息
- 在
-
[[point_size]]:點(diǎn)的大小,類型是float -
[[color(m)]]:顏色,m在編譯前就必須確定
//定義了片元輸入的結(jié)構(gòu)體,
struct MyFragmentOutput {
// color attachment 0 顏色附著點(diǎn)0
float4 clr_f [[color(0)]];
// color attachment 1 顏色附著點(diǎn)1
int4 clr_i [[color(1)]];
// color attachment 2 顏色附著點(diǎn)2
uint4 clr_ui [[color(2)]];
};
fragment MyFragmentOutput my_frag_shader( ... )
{
MyFragmentOutput f;
....
f.clr_f = ...;
....
return f;
}
-
[[stage_in]]:片元著色函數(shù)使用的單個(gè)片元輸入數(shù)據(jù)是由頂點(diǎn)著色函數(shù)輸出然后經(jīng)過光柵化生成的(即由頂點(diǎn)著色函數(shù)之后的顏色傳遞到片元著色函數(shù)),類似于GLSL中的varying傳遞紋理/顏色- 頂點(diǎn)和片元著色器函數(shù)都
只能有一個(gè)參數(shù)被聲明為使用stage_in修飾符(即有且僅有一個(gè)) - 對(duì)于一個(gè)使用了stage_in修飾符的自定義結(jié)構(gòu)體,其成員可以為一個(gè)整型/浮點(diǎn)類型標(biāo)量,或是整型/浮點(diǎn)類型向量
- 頂點(diǎn)和片元著色器函數(shù)都