Metal學習筆記(二)-- Metal Shading Language學習

跟OpenGL ES類似映企,Metal也有自己的著色器語言诡宗,相比更接近C語言的GLSL锹雏,Metal Shading Language的語言更接近于C++要拂。而且抠璃,Metal有著和OpenGL管道相同的渲染流程,即:頂點著色器->圖元裝配->光柵化->片元著色器->提交緩沖區(qū)脱惰,不同的是搏嗡,Metal中將著色器描述為函數(shù),但本質是相同的拉一。

但是畢竟是蘋果的親兒子采盒,Xcode不僅可以提供了創(chuàng)建Metal文件的入口,還能使用Clang+LLVM對Metal文件進行編譯鏈接蔚润,這相比于要開發(fā)者手敲GLSL的OpenGL ES來說磅氨,無疑是比較優(yōu)勢的一點,畢竟僅僅是以字符串形式存在的著色器來說嫡纠,查找錯誤是一個麻煩的過程烦租。

但是Metal的語法仿佛繼承了OC語言惡心的特質,學習起來還是需要一定成本的除盏,本文將對Metal Shading Language做一個學習歸納叉橱,方便日后查看。

Metal Shading Language 基本語法

Metal Shading Language使用的是 Clang 和 LLVM 進行編譯處理的者蠕,它基于C++ 11.0 語言設計窃祝,但它也不完全等同與C++的語言,Metal不支持以下特征:

  • Lambda表達式
  • 遞歸函數(shù)調用
  • 動態(tài)轉換操作符
  • 類型識別
  • 對象創(chuàng)建new和銷毀delete操作符
  • 操作符noexcept
  • goto跳轉
  • 變量存儲修飾符register和thread_local
  • 虛函數(shù)修飾符
  • 派生類
  • 異常處理
  • C++標準庫

另外踱侣,Metal像素坐標系統(tǒng)使用的原點是左上角粪小,這與OpenGL的左下角是不同的,所以我們使用Metal渲染紋理時不再需要翻轉抡句。

而且糕再,Metal函數(shù)名不能命名為Main,這點和GLSL是不同的玉转。

Metal支持結構體和枚舉突想。

基本數(shù)據(jù)類型

Metal 有以下基本數(shù)據(jù)類型:

類型 描述
bool 布爾類型,true/false
char 有符號8-bit整數(shù)
unsigned char / uchar 無符號8-bit整數(shù)
short 有符號16-bit整數(shù)
unsigned short / ushort 無符號32-bit整數(shù)
int 有符號32-bit整數(shù)
unsigned int / uint 無符號32-bit整數(shù)
half 16-bit浮點數(shù)
float 32-bit浮點數(shù)
size_t 無符號64-bit整數(shù)
prtdiff_t 64-bit有符號整數(shù)究抓,表示兩個指針的差
void 表示一個空的值集合

與OpenGL不同的是猾担,Metal支持數(shù)字后綴表示字面量類型,如0.4f刺下,0.5h绑嘹。

指針類型

Metal Shading Language 也是支持指針類型的,同樣是使用 * 表示指針,但是在Metal Shading Language中橘茉,對指針的使用以下限制:

  • Metal圖型和并行計算函數(shù)用的的入?yún)⑷绻侵羔樆蛘邞妙愋凸ひ福仨毷褂玫刂房臻g修飾符(如device姨丈,threadgroup,constant)
  • 不支持函數(shù)指針

向量和矩陣數(shù)據(jù)類型

向量

Metal Shading Language 使用基本數(shù)據(jù)類型+維度表示向量擅腰,如float3表示每個維度類型為float的3維向量蟋恬,它的定義可以寫為:

short4 vector1 = {1,2,3,4};
float3 vector2 = float3(1.f, 2.f, 3.f);

和GLSL類似,它也可以使用.xyzw或者.rgba讀取或賦予對應的值:

short2 vector3 = vector1.xy; // vector3 = (1, 2)
float2 vector4 = vector2.rb; // vector4 = (1.f, 3.f)

向量讀取的是后實際是使用索引值趁冈,所以向量的xyzw(或rgba)可以顛倒和重復:

vector3.xy = vector3.yx; // vector3 = (2, 1)
vector4.rg = vector4.rr; // vector4 = (1.f, 1,f)

另外歼争,因為xyzw(或rgba)讀取的索引值,所以不能讀取越界的索引渗勘,也不能xyzw和rgba混用沐绒,如

vector3.zw // 錯誤,二維向量沒有zw值
vector4.xg // 錯誤旺坠,xyzw和rgba不能混用

向量支持如下類型:

  • booln
  • charn
  • shortn
  • intn
  • ucharn
  • ushortn
  • uintn
  • halfn
  • floatn

0<n≤4,表示維度

矩陣

矩陣支持如下類型:

  • halfnxm
  • floatnxm

n乔遮、m分別表示矩陣的行數(shù)和列數(shù)

它可以這么定義:

half2x2 matrix = half2x2(1.h,2.h,3.h,4.h);
matrix = {2.h,2.h,2.h,2.h};

紋理類型

紋理類型是一個句柄,它指向一個一維/二維/三維紋理數(shù)據(jù)取刃。他是這么定義的:

texture1d<T, access a = access::sample>
texture2d<T, access a = access::sample>
texture3d<T, access a = access::sample>

T表示數(shù)據(jù)類型蹋肮,設定了從紋理中讀取的或是向紋理中寫入時的顏色類型,可以是half蝉衣,float括尸,short,int等

access是一個枚舉值病毡,定義了訪問權利:

enum class access {
    sample, // 默認值濒翻, 紋理對象可以被采樣,采樣一維時使用或不使用都從紋理中讀取數(shù)據(jù)
    read, // 不使用采樣器啦膜,一個圖形渲染函數(shù)或一個并行計算函數(shù)可以讀取紋理對象
    write // 一個圖形渲染函數(shù)或者一個并行函數(shù)可以像紋理對象寫入數(shù)據(jù)
}

示例如下:

void foo(texture2d<float> imgA[[ texture(0) ]],
         texture2d<float, access::read> imgB [[ texture(1) ]],
         texture2d<float, access::write> imgC [[ texture(2) ]])

采樣器類型

采樣器類型決定了如何對一個紋理進行采樣操作有送,在Metal框架中有一個對應著著色器語言的采樣器對象MTLSamplerState,這個對象作為圖形渲染著色器函數(shù)參數(shù)或者并行計算函數(shù)的參數(shù)傳遞僧家。
需要注意的是雀摘,Metal程序中,初始化采樣器必須使用constexpr修飾符聲明八拱。

constexpr sampler s(coord::normalized)

例中括號內表示采樣器的采樣參數(shù)阵赠,是一個枚舉值,Metal中采樣器以下幾種類型:

枚舉名稱 有效值 描述
coord {normalized, pixel} 從紋理中采樣時肌稻,紋理坐標是否需要歸一化
filter {nearest, linear} 紋理采用的過濾方式清蚀,放大/縮小的過濾方式
min_filter {nearest, linear} 紋理采用的縮小過濾方式
mag_filter {nearest, linear} 紋理采用的放大過濾方式
s_address, t_address, r_address {clamp_to_zero, clamp_to_edge, repeat, mirrored_repeat} 設置s、t爹谭、r坐標的尋址模式
address {clamp_to_zero, clamp_to_edge, repeat, mirrored_repeat} 設置所有的紋理坐標尋址模式
compare_func {none, less, less_equal, greater, greater_equal, equal, not_equal} 為使用r紋理坐標做shadow map枷邪,設置比較測試邏輯,這個狀態(tài)值只可以在Metal Shading Language中完成

例:

constexpr sampler s(coord::pixel, address::clamp_to_zero, filter::linear);
constexpr sampler a(address::repeat);

修飾符

修飾符是Metal Shading Language中比較難理解的點诺凡,Metal中的修飾符有函數(shù)修飾符东揣、地址空間修飾符和傳遞修飾符3種

函數(shù)修飾符

函數(shù)修飾符位于函數(shù)之前践惑,例如:

kernel void foo(...) {
 ...
}

Metal有以下3種函數(shù)修飾符

  1. kernel:表示該函數(shù)是一個數(shù)據(jù)并行計算函數(shù),他可以分配在一維/二維/三維線程組中去執(zhí)行嘶卧。
  2. vertex:表示該函數(shù)是一個頂點著色函數(shù)尔觉,它將頂點數(shù)據(jù)劉總的每個頂點數(shù)據(jù)執(zhí)行一次然后為每個頂點生成數(shù)據(jù)輸出到繪制管線。
  3. fragment:表示該函數(shù)是一個片元著色函數(shù)脸候,它將為片元數(shù)據(jù)流中的每個片元和其關聯(lián)執(zhí)行一次然后將每個片元生成的顏色數(shù)據(jù)輸出到繪制管線中穷娱。

只有圖形著色器函數(shù)才可以被vertex和fragment修飾绑蔫,對于圖形著色函數(shù)运沦,返回值類型可以辨別出他是為頂點做計算還是為每個像素做計算,圖型函數(shù)返回值可以為void配深,但這意味著該函數(shù)不產生數(shù)據(jù)輸出到繪制管線携添,這是一個無意義的動作。

函數(shù)修飾符有以下注意點

  1. 使用kernel修飾的函數(shù)篓叶,其返回值類型必須是void類型烈掠;
  2. 一個被函數(shù)修飾符修飾的函數(shù)不能調用其他被函數(shù)修飾符修飾的函數(shù);

地址空間修飾符

地址空間修飾符用于變量或者參數(shù)缸托,位于變量或參數(shù)類型之前左敌,例如:

device float4 *color;

空間修飾符表示一個變量被分配在哪一片內存區(qū)域,所有著色器函數(shù)(vertex俐镐,fragment矫限,kernel)的參數(shù),如果是指針或者應用佩抹,都必須帶有地址空間修飾符。

空間修飾符有以下4種棍苹。

  • device:設備地址空間
  • threadgroup:線程組地址空間
  • constant:常量地址空間
  • thread:線程地址空間

對于圖形著色器函數(shù)无宿,其指針或者應用類型參數(shù)必須定位為device或constant地址空間;
對于并行計算函數(shù)枢里,其指針或者應用類型參數(shù)必須定義為device或threadgroup或constant地址空間彬碱;

1.設備地址空間

指的是設備內存池分配出來的緩存對象,它是可讀可寫的搬泥,一個緩存對象可以被聲明為一個標量忿檩、向量或者是用戶自定義結構體的指針或應用。
另外注意的是辨图,紋理對象總是在設備地址空間分配內存,device地址空間修飾符不必出現(xiàn)在紋理類型定義中,一個紋理對象的內容無法直接訪問,Metal提供讀寫紋理的內建函數(shù)姨俩。

例:

device float4 *color;
struct Foo {
    float a[3];
    int b[2];
}
device Foo *my_info;

2.線程組地址空間

指的是用于為并行計算著色函數(shù)分配內存變量的空間哼勇,這些變量被一個線程組的所有線程共享积担。
在線程組地址空間分配的變量不能用與圖形繪制著色函數(shù)湿刽。
在并行計算著色函數(shù)中渴庆,在線程組地址空間分配的變量為一個線程組使用耸弄,聲明周期和線程組相同捌显。
例:

kernel void my_func(threadgroup float *a [[threadgroup(0)]]) {
    threadgroup float x;
    
    threadgroup float b[10];
}

3.常量地址空間

指向的緩存對象也是設備內存池分配,但他是只讀的尉间,它修飾的變量必須在定義的時候初始化击罪,用來初始化的值必須是編譯時的常量媳禁,修飾的變量在程序域的生命周期和程序一樣竣稽,在程序中的并行計算著色函數(shù)或者圖形繪制著色函數(shù)調用時,它的的值都會保持不變耍缴。
值得注意的是防嗡,常量地址空間的指針或引用也可以作為函數(shù)的參數(shù),為聲明的常量賦值會產生編譯錯誤,聲明常量但是沒有賦予初值也會編譯錯誤近顷;
例如:

constant float samplers[] = {1.f, 2.f, 3.f, 3.f};

sampler[4] = {3,3,3,3}; // 編譯失敗

constant float a; // 編譯失敗

4.線程地址空間

指向每個線程準備的地址空間域醇,這個線程地址空間定義的變量在其他線程不可見酪呻〗赘裕可以在圖形繪制函數(shù)或者并行計算函數(shù)中使用闷尿。
例:

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

傳遞修飾符

對于圖形繪制或并行著色器函數(shù)來說,輸入/輸出都需要通過參數(shù)傳遞(除了常量地址空間變量和程序域中定義的采樣器以外)女坑,參數(shù)可以以下之一:

  • device buffer - 設備緩存填具,一個指向設備地址空間的任意數(shù)據(jù)類型的指針或者引用
  • constant buffer - 常量緩存區(qū),一個指向常量地址空間的任意數(shù)據(jù)類型的引用
  • texture - 紋理對象
  • sampler - 采樣器對象
  • threadgroup - 在線程組中供各線程共享的緩存

注意的是堂飞,著色器的緩存(device 和 constant)不能重名灌旧。

對于每個著色器來說,一個變量傳遞修飾符是必須指定的绰筛,它用來設置一個緩沖枢泰、紋理、采樣器的位置铝噩。
傳遞修飾符位于參數(shù)變量之后衡蚂,用[[]]表示,并在()內指定它的位置。
通常我們有以下幾種類型可以從外部傳入著色器:

  • [[buffer(index)]] // device buffer / constant buffer
  • [[texture(index)]] // texture
  • [[sampler(index)]] // sampler
  • [[threadgroup(index)]] // threadgroup

index是一個unsigned integer類型的值毛甲,它表示一個緩存年叮、紋理、采樣器參數(shù)的位置玻募。

參考代碼如下:

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: 用于表示當前節(jié)點在多線程網絡中的位置,不需要用戶傳入

內建變量屬性修飾符

除了以上的傳遞修飾符只损,Metal還提供了內建的傳遞修飾符。常見的有以下幾種:

  • [[vertex_id]] 頂點id表示符七咧,即當前是第幾個頂點
  • [[position]] 頂點信息跃惫,表述片元的窗口相對坐標(x, y, z, 1/w)
  • [[point_size]] 點的大小
  • [[color(m)]] 顏色,m編譯前得確定,表示傳入顏色附著點m
  • [[stage_in]] 表示由頂點著色函數(shù)輸出經過光柵化生成傳入片元函數(shù)的數(shù)據(jù)艾栋,一個頂點和片元函數(shù)都只能有一個參數(shù)被聲明為stage_in
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末爆存,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蝗砾,更是在濱河造成了極大的恐慌先较,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件悼粮,死亡現(xiàn)場離奇詭異闲勺,居然都是意外死亡,警方通過查閱死者的電腦和手機矮锈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門霉翔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人苞笨,你說我怎么就攤上這事∽涌簦” “怎么了瀑凝?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長臭杰。 經常有香客問我粤咪,道長,這世上最難降的妖魔是什么渴杆? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任寥枝,我火速辦了婚禮,結果婚禮上磁奖,老公的妹妹穿的比我還像新娘囊拜。我一直安慰自己,他們只是感情好比搭,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布冠跷。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蜜托。 梳的紋絲不亂的頭發(fā)上抄囚,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音橄务,去河邊找鬼幔托。 笑死,一個胖子當著我的面吹牛蜂挪,可吹牛的內容都是我干的柑司。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼锅劝,長吁一口氣:“原來是場噩夢啊……” “哼攒驰!你這毒婦竟也來了?” 一聲冷哼從身側響起故爵,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤玻粪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后诬垂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體劲室,經...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年结窘,在試婚紗的時候發(fā)現(xiàn)自己被綠了很洋。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡隧枫,死狀恐怖喉磁,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情官脓,我是刑警寧澤协怒,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站卑笨,受9級特大地震影響孕暇,放射性物質發(fā)生泄漏。R本人自食惡果不足惜赤兴,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一妖滔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧桶良,春花似錦座舍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鉴竭。三九已至,卻和暖如春岸浑,著一層夾襖步出監(jiān)牢的瞬間搏存,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工矢洲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留璧眠,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓读虏,卻偏偏與公主長得像责静,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子盖桥,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354