1. Metal Shading Language簡(jiǎn)介
Metal著色語(yǔ)言是用來(lái)編寫(xiě)3D圖形渲染邏輯
和并行計(jì)算核心邏輯
的一門(mén)編程語(yǔ)言徙瓶,底層使用Clang和LLVM進(jìn)行編譯處理慕淡。
Metal語(yǔ)言基于C++ 11.0 語(yǔ)言設(shè)計(jì)坝辫,我們主要用來(lái)編寫(xiě)在GPU上執(zhí)行的圖像渲染邏輯代碼
以及通用并行計(jì)算邏輯代碼
。當(dāng)我們使用Metal框架來(lái)完成APP的實(shí)現(xiàn)時(shí)則需要使用Metal編程語(yǔ)言掘譬。
2. Metal 使用常識(shí)
2.1 Metal 與 C++ 11.0
Metal Restrictions 限制 (如下的C++11.0的特性在Metal 著色語(yǔ)言中是不支持的
):
- Lambda表達(dá)式
- 遞歸函數(shù)調(diào)?
- 動(dòng)態(tài)轉(zhuǎn)換操作符
- 類(lèi)型識(shí)別
- 對(duì)象創(chuàng)建(
new
)和釋放(delloc
)操作符 - 操作符
noexcept
-
goto
跳轉(zhuǎn) - 虛函數(shù)修飾符-
- 派?類(lèi)
- 異常處理
C++的標(biāo)準(zhǔn)庫(kù)不可以在Metal 著色語(yǔ)言中使用牵囤。
2.2 Metal 語(yǔ)言中對(duì)于指針使用的限制
- Metal圖形和并行計(jì)算函數(shù)用到的入?yún)ⅲ绻侵羔槺仨毷褂玫刂房臻g修飾符 (device ,threadgroup ,constant)
- 不支持函數(shù)指針
- Metal函數(shù)名不能命名為Main函數(shù)
2.3 Metal 與OpenGL ES
- 在OpenGL ES中饶深,圖片的紋理坐標(biāo)原點(diǎn)是基于左下角的,而在Metal中逛拱,蘋(píng)果為了統(tǒng)一敌厘,將圖片紋理的坐標(biāo)原點(diǎn)設(shè)在了左上角。
- 在OpenGL ES中朽合,需要我們手動(dòng)去編譯頂點(diǎn)著色器和片元著色器俱两,而在Metal中,這一切是由蘋(píng)果系統(tǒng)為我們做的曹步。
3.0 Metal數(shù)據(jù)類(lèi)型
類(lèi)型 | 描述 |
---|---|
bool | 布爾類(lèi)型宪彩,取值范圍true,false讲婚;true可以拓展為整數(shù)常量1尿孔,false可以拓展為整數(shù)常量0 |
char | 有符號(hào)8-bit整數(shù) |
unsigned char (uchar) | 無(wú)符號(hào)8-bit整數(shù) |
short | 有符號(hào)16-bit整數(shù) |
unsigned short (ushort) | 無(wú)符號(hào)16-bit整數(shù) |
int | 有符號(hào)32-bit整數(shù) |
unsigned int (uint) | 無(wú)符號(hào)32-bit整數(shù) |
half | 一個(gè)16-bit浮點(diǎn)數(shù) |
float | 一個(gè)32-bit浮點(diǎn)數(shù) |
size-t | 64-bit無(wú)符號(hào)整數(shù),表示sizeof操作符的結(jié)果 |
ptrdiff_t | 64-bit有符號(hào)整數(shù)筹麸,表示2個(gè)指針的差 |
void | 表示一個(gè)空的值集合 |
3.1 Metal 向量和矩陣數(shù)據(jù)類(lèi)型
3.1.1 向量
- booln
- charn
- shortn
- intn
- ucharn
- ushortn
- uintn
- halfn
- floatn
向量中的n,指的是維度活合,當(dāng)n=1時(shí)直接省略,n最大是4物赶。
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;
//通過(guò)向量字母來(lái)獲取元素
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);
float2 pos;
pos.x = 1.0f; //合法
pos.z = 1.0f; //非法
float3 pos2;
pos2.z = 1.0f; //合法
pos2.w = 1.0f; //非法
//非法,x出現(xiàn)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);
//非法,使用指針來(lái)指向向量/分量
my_func(&pos5.xy);
3.1.2 矩陣
矩陣支持如下類(lèi)型:
- halfnxm
- floatnxm
nxm分別指的是矩陣的?數(shù)和列數(shù)白指,最大支持4行4列
例:
float4x4 m;
//將第二排的值設(shè)置為0
m[1] = float4(2.0f);
//設(shè)置第一行/第一列為1.0f
m[0][0] = 1.0f;
//設(shè)置第三行第四列的元素為3.0f
m[2][3] = 3.0f;
float4類(lèi)型向量的所有可能構(gòu)造方式
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類(lèi)型向量的所有可能的構(gòu)造的方式
float3(float x);
float3(float x,float y,float z);
float3(float a,float2 b);
float3(float2 a,float b);
float3(float3 x);
float2類(lèi)型向量的所有可能的構(gòu)造方式
float2(float x);
float2(float x,float y);
float2(float2 x);
4.紋理Texture類(lèi)型
紋理類(lèi)型是一個(gè)句柄,它指向一個(gè)一維/二維/三維紋理數(shù)據(jù)酵紫。
枚舉值:定義了訪(fǎng)問(wèn)權(quán)限:
enum class access (sample,read,write);
sample:紋理對(duì)象可以被采樣告嘲;
read:不使用采樣器,一個(gè)圖形渲染函數(shù)或者一個(gè)并行計(jì)算函數(shù)可以讀取紋理對(duì)象奖地;
write:一個(gè)圖形渲染函數(shù)或者一個(gè)并行計(jì)算函數(shù)可以向紋理對(duì)象寫(xiě)入數(shù)據(jù)橄唬。
texture1d<T,access a = access::sample>
texture2d<T,access a = access::sample>
texture3d<T,access a = access::sample>
T:數(shù)據(jù)類(lèi)型,設(shè)定了從紋理中讀取或者是向紋理中寫(xiě)入時(shí)的顏色類(lèi)型参歹,T可以是half仰楚,float,short泽示,int等缸血。
舉例
void foo (texture2d<float> imgA [[ texture(0) ]] ,texture2d<float, access::read> imgB [[ texture(1) ]], texture2d<float, access::write> imgC [[ texture(2) ]])
{
...
}
5. 采樣器 Samples
采樣器類(lèi)型決定了如何對(duì)一個(gè)紋理進(jìn)行采樣操作蜜氨。在Metal框架中有一個(gè)采樣器對(duì)象MTLSamplerState械筛,這個(gè)對(duì)象作為圖形渲染著色器函數(shù)參數(shù)或者是并行計(jì)算函數(shù)的傳遞參數(shù),也就是在sample中我們?cè)O(shè)置紋理的采樣方式(環(huán)繞方式捎泻、過(guò)濾方式)。在Metal程序中初始化的采樣器必須使用constexpr
修飾符修飾埋哟。
- Metal支持的采樣器狀態(tài)和默認(rèn)值
枚舉名稱(chēng)(Enum Name) | 有效值(Valid Value) | 描述 |
---|---|---|
coord | normalized | 從紋理中采樣時(shí)笆豁,紋理坐標(biāo)是否需要?dú)w一化 |
address | clamp_to_edge,clamp_to_zero,mirrored_repeat,repeat | 設(shè)置所有紋理坐標(biāo)的尋址模式 |
s_address,t_address,r_address | clamp_to_edge,clamp_to_zero,mirrored_repeat,repeat | 設(shè)置某個(gè)紋理坐標(biāo)的尋址模式 |
filter | nearest,linear | 設(shè)置紋理采樣的放大和縮小的過(guò)濾模式 |
mag_filter | nearest,linear | 設(shè)置紋理采樣的放大過(guò)濾模式 |
min_filter | nearest,linear | 設(shè)置紋理采樣的縮小過(guò)濾模式 |
mip_filter | none,nearest,linear | 設(shè)置紋理采樣的minmap過(guò)濾模式,如果是none,那么只有一個(gè)層紋理能生效 |
compare_func | none,less,less_qual,greater,greater_equal,equal,not_equal | 為使用r紋理坐標(biāo)做shaw map,設(shè)置比較測(cè)試邏輯赤赊,這個(gè)狀態(tài)值的設(shè)置只可以在Metal著色語(yǔ)言程序中完成 |
從紋理中采樣時(shí)闯狱,紋理坐標(biāo)是否需要?dú)w一化
enum class coord {noramlized,pixel };
紋理采樣過(guò)濾方式,放大/縮小過(guò)濾模式
enum class filter {nearest, linear};
設(shè)置紋理采樣縮小過(guò)濾模式
enum class min_filter {nearest, linear};
設(shè)置紋理采樣放大過(guò)濾模式
enum class mag_filter {nearest, linear};
設(shè)置s,t,r紋理坐標(biāo)的尋址模式
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 };
設(shè)置所有紋理坐標(biāo)的尋址模式
enum class address{clamp_to_zero,clamp_to_edge,repeat,mirrored_repeat };
例:
constexpr samper s(coord::pixel,address::clamp_to_zero,filter::linear);
6. 函數(shù)修飾符
- kernel 抛计,表示該函數(shù)是一個(gè)數(shù)據(jù)并行計(jì)算著色函數(shù),它可以被分配在一維/二維/三維線(xiàn)程組中去執(zhí)行哄孤。
注意:使用kernel修飾的函數(shù),氣返回值類(lèi)型必須是void類(lèi)型吹截。
kernel void test(...)
{
......
}
- vertex瘦陈,表示該函數(shù)是一個(gè)頂點(diǎn)著色函數(shù),它將為頂點(diǎn)數(shù)據(jù)流中的
每個(gè)頂點(diǎn)數(shù)據(jù)執(zhí)行一次
然后為每個(gè)頂點(diǎn)生成數(shù)據(jù)
輸出到繪制管線(xiàn)波俄。 - fragment晨逝,表示該函數(shù)是一個(gè)片元著色函數(shù),它將為片元數(shù)據(jù)流中的
每個(gè)片元
和其關(guān)聯(lián)執(zhí)行一次然后將每個(gè)片元
生成的顏色數(shù)據(jù)輸出到繪制管線(xiàn)中懦铺。
只有圖形函數(shù)才可以被vertex和fragment修飾捉貌。對(duì)于圖形修飾函數(shù),可以根據(jù)返回值判斷其是為頂點(diǎn)做計(jì)算還是為像素做計(jì)算冬念。注意:一個(gè)被函數(shù)修飾符修飾的函數(shù)不能調(diào)用另一個(gè)函數(shù)修飾符修飾的函數(shù)趁窃。
kernel void test1(...)
{
}
vertex float4 test2(...)
{
test1(...); // 這個(gè)是錯(cuò)誤的調(diào)用
}
7. 地址空間修飾符
地址空間修飾符,是Metal著色語(yǔ)言用來(lái)給參數(shù)變量或者函數(shù)變量分配內(nèi)存使用的急前。所有的著色函數(shù)(kernel,vertex,fragment)的參數(shù)棚菊,如果是指針或者是引用,都必須帶有地址空間修飾符叔汁。
- device(設(shè)備地址空間)
在設(shè)備地址空間(device)指向設(shè)備內(nèi)存池(這里指的是顯存
)分配出來(lái)的緩存對(duì)象统求,既是可讀也是可寫(xiě)的。
device float4 *color;
struct test{
float a[2];
int b[1];
}
device test *info;
注意:紋理對(duì)象總是在設(shè)備地址空間分配內(nèi)存据块,device地址空間修飾符不必出現(xiàn)在紋理類(lèi)型定義中码邻,一個(gè)紋理對(duì)象的內(nèi)容無(wú)法直接訪(fǎng)問(wèn),可使用Metal自身的內(nèi)建函數(shù)另假。
- threadgroup(線(xiàn)程組地址空間)
線(xiàn)程組地址空間用于為并行計(jì)算著色函數(shù)分配內(nèi)存變量像屋,這些變量被一個(gè)線(xiàn)程組的所有線(xiàn)程共享。
在線(xiàn)程組地址空間分配的變量不能被用于圖形繪制著色函數(shù)边篮。
在并行計(jì)算周瑟函數(shù)中己莺,在線(xiàn)程組地址空間分配的變量為一共線(xiàn)程組共同使用奏甫,生命周期和線(xiàn)程組相同。
kernel void func(threadgroup float *a[[threadgroup(0)]])
{
....
}
- constant(常量地址空間)
常量地址空間指向的緩存對(duì)象也是從設(shè)備內(nèi)存池分配存儲(chǔ)凌受,但是它是只讀的阵子。
在程序域的變量的生命周期和程序一樣,在程序中的并行計(jì)算著色函數(shù)或者圖形繪制著色函數(shù)使用胜蛉,但是constant的值是不會(huì)變的挠进。
注意:常量地址空間的指針或是引用可以作為函數(shù)的參數(shù)。向聲明為常量的變量賦值會(huì)編譯失敗誊册,聲明常量不賦值也會(huì)編譯失敗领突。
constant float samples[] = {1.0f,2.0f,3.0f,4.0f};
samples[4] = {3,2,3,2}; //編譯失敗,因?yàn)閏onstant是只讀的
constant float a; // 編譯失敗案怯,因?yàn)闆](méi)有賦值
- thread(線(xiàn)程地址空間)
thread地址空間指向每個(gè)線(xiàn)程準(zhǔn)備的地址空間君旦,這個(gè)線(xiàn)程的地址空間定義的變量在其他線(xiàn)程不可見(jiàn),在圖形繪制著色函數(shù)或者并行計(jì)算著色函數(shù)中可使用thread聲明嘲碱。
kernel void func(...)
{
float x;
thread float p = &x;
...
}
對(duì)于圖形著色函數(shù)金砍,其指針或是引用類(lèi)型的參數(shù)必須定義為device或者是constant地址空間,對(duì)于并行計(jì)算著色函數(shù)悍汛,其指針或是引用類(lèi)型的參數(shù)必須定義為device或者是constant或者是threadgroup地址空間
8. 函數(shù)參數(shù)與變量
圖形繪制或者并行計(jì)算著色器函數(shù)的輸入輸出都是通過(guò)參數(shù)傳遞捞魁,除了常量地址空間變量和程序域定義的采樣器除外。
- device buffer - 設(shè)備緩存离咐,一個(gè)指向設(shè)備地址空間的任意數(shù)據(jù)類(lèi)型的指針或者引用谱俭;
- constant buffer - 常量緩存區(qū),一個(gè)指向常量地址空間的任意數(shù)據(jù)類(lèi)型的指針或者引用宵蛀;
- texture - 紋理對(duì)象昆著;
- sampler - 采樣器對(duì)象;
- threadgroup - 在線(xiàn)程組中供各線(xiàn)程共享的緩存术陶。
注意:被著色器函數(shù)的緩存(device和constant)不能重名凑懂。
對(duì)于每個(gè)著色器函數(shù)來(lái)說(shuō),一個(gè)修飾符是必須指定的梧宫,他用來(lái)設(shè)定一個(gè)緩存接谨,紋理,采樣器的位置塘匣。
- device buffer/constant buffer → [[buffer(index)]]
- texture → [[texture(index)]]
- sampler → [[sampler(index)]]
- threadgroup → [[threadgroup(index)]]
index是一個(gè)unsigned integer類(lèi)型的值脓豪,它表示了一個(gè)緩存、紋理忌卤、采樣器的位置扫夜。
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];
}
thread_position_in_grid:用于表示當(dāng)前節(jié)點(diǎn)在多線(xiàn)程網(wǎng)格中的位置;
內(nèi)建變量屬性修飾符
- [[vertex_id]]: 頂點(diǎn)id標(biāo)識(shí)符
- [[position]]: 頂點(diǎn)信息(float4)
表示片元的窗口相對(duì)坐標(biāo)
- [[point_size]]: 點(diǎn)的大小(float)
- [[color(m)]]:顏色笤闯,m編譯前必須要確定
struct myFragmentOutput{
float4 cl_f[[color(0)]];
float4 cl_i[[color(1)]];
float4 cl_u[[color(2)]];
};
fragment myFragmentOutput shader()
{
myFragmentOutput f;
f.cl_f = ...;
...
return f;
}
- [[stage_in]]:片元著色函數(shù)使用的單個(gè)片元輸入數(shù)據(jù)是由頂點(diǎn)著色函數(shù)輸出然后經(jīng)過(guò)光柵化生成的堕阔。
頂點(diǎn)和片元著色函數(shù)都是只能有一個(gè)參數(shù)被聲明為使用“stage_in”修飾符,對(duì)于一個(gè)使用了stage_in修飾符的自定義的結(jié)構(gòu)體颗味,期結(jié)構(gòu)體內(nèi)部成員可以是整形超陆、浮點(diǎn)型標(biāo)量、浮點(diǎn)型向量脱衙。
屬性修飾符目的:
- 參數(shù)表示資源如何定位? 可以理解為端口侥猬;
- 在固定管線(xiàn)和可編程管線(xiàn)進(jìn)行內(nèi)建變量的傳遞例驹;
- 將數(shù)據(jù)沿著渲染管線(xiàn)從頂點(diǎn)函數(shù)傳遞片元函數(shù)捐韩。