Metal之Shading Language Specification(着色语言规范)
Metal簡述
- Metal著色器語言是用來編寫 3D圖形渲染邏輯、并行Metal計算核心邏輯 的一門編程語言,當(dāng)你使用Metal框架來完成APP的實現(xiàn)時則需要使用Metal編程語言。
- Metal語言使用Clang 和LLVM進(jìn)行編譯處理,編譯器對于在GPU上的代碼執(zhí)行效率有更好的控制。
- Metal基于C++ 11.0語言設(shè)計的,在C++基礎(chǔ)上多了一些擴(kuò)展和限制,主要用來編寫在GPU上執(zhí)行的圖像渲染邏輯代碼以及通用并行計算邏輯代碼。
- Metal 像素坐標(biāo)系統(tǒng):Metal中紋理 或者 幀緩存區(qū)attachment的像素使用的坐標(biāo)系統(tǒng)的原點是左上角。
- Metal 與 C++ 11.0:Metal 這?語?是基于C++ 11.0標(biāo)準(zhǔn)設(shè)計的, 它在C++基礎(chǔ)是?多了?些拓展和限制。
Metal Restrictions 限制
① Metal中不支持C++11.0的如下特性:
- Lambda表達(dá)式
- 遞歸函數(shù)調(diào)用
- 動態(tài)轉(zhuǎn)換操作符
- 類型識別
- 對象創(chuàng)建new和銷毀delete操作符
- 操作符noexcept
- go跳轉(zhuǎn)
- 變量存儲修飾符 register 和thread_local
- 虛函數(shù)修飾符
- 派生類
- 異常處理
② C++標(biāo)準(zhǔn)庫在Metal語言中也不可使用
③ Metal語言對于指針使用的限制
- Metal圖形和并行計算函數(shù)用到的入?yún)?#xff08;比如指針 / 引用),如果是指針 / 引用必須使用地址空間修飾符(比如device、threadgroup、constant)
- 不支持函數(shù)指針
- 函數(shù)名不能出現(xiàn)main
Metal數(shù)據(jù)類型
一、Metal支持的標(biāo)量類型
| bool | 布爾類型,取值范圍true、false;true可以拓展為整數(shù)常量1,false可以拓展為整數(shù)常量0 |
| char | 有符號8-bit整數(shù) |
| unsigned char uchar | 無符號8-bit整數(shù) |
| short | 有符號16-bit整數(shù) |
| unsigned short ushort | 無符號16-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ù),表示sizeof操作符的結(jié)果 |
| ptrdiff_t | 64-bit有符號整數(shù),表示2個指針的差 |
| void | 表示一個空的值集合 |
- 常用的主要有 bool、int、uint 、half
- undigned 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
二、Metal支持的向量類型
① 支持類型
- booln、charn、shortn、intn、ucharn、ushortn、uintn、halfn、floatn,其中 n 表示向量的維度,最多不超過4維向量;
- 在OpenGL ES的GLSL語言中,例如2.0f,在著色器中書寫時,是不能加f,寫成2.0,而在Metal中則可以寫成2.0f,其中f可以是大寫,也可以是小寫;
② 訪問規(guī)則
- 通過向量字母獲取元素: 向量中的向量字母僅有2種,分別為xyzw、rgba;
- 多個分量同時訪問
- 多分量訪問可以亂序/重復(fù)
賦值時分量不可重復(fù),取值時分量可重復(fù)
右邊取值和左邊賦值都合法
xyzw與rgba不能混合使用
GLSL中向量不能亂序訪問
三、Metal支持的矩陣類型
① 支持類型
- halfnxm、floatnxm,其中 nxm表示矩陣的行數(shù)和列數(shù),最多4行4列,其中half、float相當(dāng)于OC中的float、double;
- 普通的矩陣其本質(zhì)就是一個數(shù)組;
② 構(gòu)造方式
- float4 類型向量的構(gòu)造方式
1個float構(gòu)成,表示一行都是這個值
4個float構(gòu)成
2個float2構(gòu)成
1個float2+2個float構(gòu)成(順序可以任意組合)
1個float2+1個float
1個float4
Metal其他類型
一、紋理類型
-
紋理類型是一個句柄,指向一維/二維/三維紋理數(shù)據(jù),而紋理數(shù)據(jù)對應(yīng)一個紋理的某個level的mipmap的全部或者一部分;
-
紋理的訪問權(quán)限
在一個函數(shù)中描述紋理對象的類型, access枚舉值由Metal定義,定義了紋理的訪問權(quán)利 enum class access {sample, read, write};,有以下3種訪問權(quán)利,當(dāng)沒設(shè)定access時,默認(rèn)的access 就是 sample;
① sample: 紋理對象可以被采樣(即使用采樣器去紋理中讀取數(shù)據(jù),相當(dāng)于OpenGL ES的GLSL中sampler2D),采樣一維這時使用 或者 不使用都可以從紋理中讀取數(shù)據(jù)(即可讀可寫可采樣);
② read:不使用采樣器,一個圖形渲染函數(shù) 或者 一個并行計算函數(shù)可以讀取紋理對象(即僅可讀);
③ write:一個圖形渲染函數(shù)或者一個并行計算可以向紋理對象寫入數(shù)據(jù)(即可讀可寫); -
定義紋理類型
描述一個紋理對象/類型,有以下三種方式,分別對應(yīng)一維/二維/三維,其中T代表 泛型,設(shè)定了從紋理中讀取數(shù)據(jù) 或是 寫入時的顏色類型,T可以是half、float、short、int等
access表示紋理訪問權(quán)限,當(dāng)access沒設(shè)定時,默認(rèn)是sample:
texture1d<T, access a = access::sample>
texture2d<T, access a = access::sample>
texture3d<T, access a = access::sample>
二、采樣器類型 Samplers
采樣器類型決定了如何對一個紋理進(jìn)行采樣操作,在Metal框架中有一個對應(yīng)著色器語言的采樣器的對象MTLSamplerState,這個對象作為圖形渲染著色器函數(shù)參數(shù)或是并行計算函數(shù)的參數(shù)傳遞,有以下幾種狀態(tài):
- coord:從紋理中采樣時,紋理坐標(biāo)是否需要歸一化; 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)(對應(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 }; - 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 };
- 注意:
openGL ES中紋理坐標(biāo)對應(yīng)的是stq,Metal中紋理坐標(biāo)對應(yīng)是str;
在Metal程序中初始化的采樣器必須使用constexpr修飾符聲明;
函數(shù)修飾符
Metal 著?器語??持下列的函數(shù)修飾符:
- kernel:表示該函數(shù)是一個數(shù)據(jù)并行計算著色函數(shù),它可以被分配在一維/二維/三維線程組中去執(zhí)行,表示函數(shù)要并行計算,其返回值類型必須是void類型,是一個高并發(fā)函數(shù);
- vertex:表示該函數(shù)是一個頂點著色函數(shù),它將為頂點數(shù)據(jù)流中的每個頂點數(shù)據(jù)執(zhí)行一次,然后為每個頂點生成數(shù)據(jù)輸出到繪制管線;
- fragment:表示該函數(shù)是一個片元著色函數(shù),它將為片元數(shù)據(jù)流中的每個片元 和其相關(guān)聯(lián)的數(shù)據(jù)執(zhí)行一次,然后將每個片元生成的顏色數(shù)據(jù)輸出到繪制管線中;
注意:
- 使用kernel修飾的函數(shù),其返回值類型必須是void類型
- 一個被函數(shù)修飾符修飾的函數(shù)不能在調(diào)用其他也被函數(shù)修飾符修飾的函數(shù),這樣會導(dǎo)致編譯失敗,即Kernel、vertex、fragment修飾的函數(shù)不能相互調(diào)用,也不能同修飾符函數(shù)相互調(diào)用,但是可以調(diào)用普通函數(shù);
- 被函數(shù)修飾符修飾過的函數(shù),只允許在客戶端對其進(jìn)行操作. 不允許被普通的函數(shù)調(diào)用;
- Metal中并不是所有函數(shù)都需要上述3個修飾符修飾,是可以在Metal中定義普通函數(shù)的,即不帶任何修飾符的函數(shù);
- 只有圖形著色函數(shù)才可以被vertex和fragment修飾,對于圖形著色函數(shù),通過返回值類型可以辨認(rèn)出是為頂點計算還是像素計算,其返回值也可以是void,意味著不產(chǎn)生數(shù)據(jù)輸出到繪制管線,是一個無意義的動作;
變量、參數(shù)的地址空間修飾符
一、概念
① Metal著色器語言使用地址空間修飾符來表示一個函數(shù)變量或者參數(shù)變量被分配于哪一片內(nèi)存區(qū)域,有以下4中地址空間修飾符:
- device: 設(shè)備地址空間
- threadgroup: 線程組地址空間
- constant 常量地址空間
- thread 線程地址空間
② 注意:
- 所有的著色函數(shù)(vertex、fragment、kernel)的參數(shù),如果是指針/引用,都必須帶有地址空間修飾符號;
- 對于圖形著色器函數(shù)(即vertex/fragment修飾的函數(shù)),其指針/引用類型的參數(shù)必須定義為 device、constant地址空間;
- 對于并行計算函數(shù)(即kernel修飾的函數(shù)),其指針/引用類型的參數(shù)必須定義為 device、threadgroup、constant;
- 并不是所有的變量都需要修飾符,也可以定義普通變量(即無修飾符的變量);
二、device:設(shè)備地址空間修飾符
- 設(shè)備地址空間指向設(shè)備內(nèi)存池分配出來的緩存對象(設(shè)備指顯存,即GPU),即GPU空間分配的緩存對象,它是可讀可寫的,一個緩存對象可以被聲明成一個標(biāo)量、向量或是用戶自定義結(jié)構(gòu)體的指針/引用;
- device放在變量類型之前;
- 除了可以修飾 圖形著色器函數(shù) / 并行計算函數(shù)參數(shù),還可以修飾指針變量 和 結(jié)構(gòu)體指針變量:
- 紋理對象總是在設(shè)備地址空間分配內(nèi)存,即紋理對象默認(rèn)在GPU分配內(nèi)存;
- device地址空間修飾符不必出現(xiàn)在紋理類型定義中;
- 一個紋理對象的內(nèi)容無法直接訪問,Metal提供讀寫紋理的內(nèi)建函數(shù),通過內(nèi)建函數(shù)訪問紋理對象;
三、threadgroup:線程組地址空間修飾符
- 線程組地址空間用于為并行計算著色器函數(shù)分配內(nèi)存變量,這些變量被一個線程組的所有線程共享,在線程組地址空間分配的變量不能用于圖形繪制著色函數(shù)(即頂點著色函數(shù) / 片元著色函數(shù)),即在圖形繪制著色函數(shù)中不能使用線程組;
- 在并行計算著色函數(shù)中,在線程組地址空間分配的變量為一個線程組使用,生命周期和線程組相同;
四、constant:常量地址空間修飾符
- 常量地址空間指向的緩存對象也是從設(shè)備內(nèi)存池分配存儲,僅可讀;
- 在程序域的變量必須定義在常量地址空間并且聲明時初始化,用來初始化的值必須是編譯時的常量;
- 在程序域的變量的生命周期和程序一樣,在程序中的并行計算著色函數(shù)或者圖形繪制著色函數(shù)調(diào)用,但是constant的值會保持不變;
- 常量地址空間的指針/引用可以作為函數(shù)的參數(shù),向聲明為常量的變量賦值會產(chǎn)生編譯錯誤;
- 聲明常量但是沒有賦予初值也會產(chǎn)生編譯錯誤;
五、thread:線程地址空間修飾符
- 線程地址空間指向每個線程準(zhǔn)備的地址空間,也是在GPU中,該線程的地址空間定義的變量在其他線程不可見(即變量不共享);
- 在圖形繪制著色函數(shù) 或者 并行計算著色函數(shù)中聲明的變量,在線程地址空間分配存儲;
內(nèi)建變量修飾符
- [[vertex_id]] :頂點id標(biāo)識符,并不由開發(fā)者傳遞;
- [[position]]: 在頂點著色函數(shù)中,表示當(dāng)前的頂點信息,類型是float4; 還可以表示描述了片元的窗口的相對坐標(biāo)(x,y,z,1/w),即該像素點在屏幕上的位置信息;
- [[point_size]] :點的大小,類型是float;
- [[color(m)]] :顏色,m在編譯前就必須確定;
- [[stage_in]] :片元著色函數(shù)使用的單個片元輸入數(shù)據(jù)是由頂點著色函數(shù)輸出然后經(jīng)過光柵化生成的(即由頂點著色函數(shù)之后的顏色傳遞到片元著色函數(shù)),類似于GLSL中的varying傳遞紋理/顏色;
頂點和片元著色器函數(shù)都只能有一個參數(shù)被聲明為使用stage_in修飾符(即有且僅有一個);
對于一個使用了stage_in修飾符的自定義結(jié)構(gòu)體,其成員可以為一個整型/浮點類型標(biāo)量,或是整型/浮點類型向量。
總結(jié)
以上是生活随笔為你收集整理的Metal之Shading Language Specification(着色语言规范)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Metal之基本简介及常用组件说明
- 下一篇: OpenGL ES之GLSL常用内建函数