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