編寫3D圖形渲染邏輯
和並⾏計算核⼼邏輯
的編程語⾔,編寫Metal框架的APP須要使⽤Metal着⾊語⾔程序.假如當須要利用GPU的高計算能力時,咱們也可使用Metal來幫助咱們,由於它不只僅能夠作圖形渲染,也擁有高併發計算的能力.編程
好比在AVFoundation作人臉識別、二維碼識別、音視頻的編解碼等工做中,須要對每一幀的數據進行計算,這種狀況下的計算量是很是大的,蘋果就利用硬件加速器(指的就是GPU)來進行高併發計算。數組
Metal ⽀持後綴表示字⾯量類型, 例如 0.5F, 0.5f; 0.5h, 0.5H. 這在GLSL中是不被容許的.緩存
bool a = true;
char b = 1;
int c = 15;
size_t d = 1;
複製代碼
向量支持以下類型:markdown
n指的是維度併發
語法規則:
bool2 a = [1, 2]; //布爾類型的二維向量,也能夠理解爲布爾數組使用
float4 pos = float4(1.0f,2.0f,3.0f,4.0f);
// 獲取pos中數據的方式
float x = pos[0]; //index下標
// 向量份量(字母)來獲取元素有兩種: 'xyzw' | 'rgba',這是索引對應的
int4 t = int4(0,1,2,3);
int a = t.x; // a = 0
int b = t.r; // b = 0
// 多個份量訪問
float4 t = float4(0,1,2,3);
t.xyzw = float4(3,4,5,6); // 覆蓋從新賦值
t.z = 7;
t.xy = float2(8,9);
// 多份量亂序/重複訪問 -- GLSL是不容許亂序的
float4 t = float(0,1,2,3);
float4 tt = t.zywx;
float4 ttt = t.xxyy;
t.xw = float2(4,5);
t.wx = float2(6,7);
t.ww = float2(6,7); // !!! 重複賦值是不容許的,
float2 t = float2(1,2);
t.x = 3;
t.z = 4; // 這是不合法的,float2 => z 是數組越界了
// 不能夠混用
float t = float4(0,1,2,3);
t.x = 4;
t.xg = float2(5,6); // 不合法,‘xyzw’和‘rgba’是不能夠混用的
複製代碼
nxm分別指的是矩陣的行數和列數,最大支持4行4列 例:4x4框架
語法規則
float4x4 m;
m[1] = float4(2.0f); // 指定第二行都爲2.0f
m[1] = float4(1,2,3,4); // 指定第二行不一樣的數據
m[0][1] = 3.0f;
//float4類型向量的全部可能構造方式
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類型向量的全部可能的構造的方式
float3(float x);
float3(float x,float y,float z);
float3(float a,float2 b);
float3(float2 a,float b);
float3(float3 x);
//float2類型向量的全部可能的構造方式
float2(float x);
float2(float x,float y);
float2(float2 x);
複製代碼
紋理類型是⼀個句柄, 它指向⼀個⼀維/⼆維/三維紋理數據. 在⼀個函數中描述紋理對象的類型;函數
enum class access {sample ,read ,write};高併發
texture1d<T, access a = access::sample>
texture2d<T, access a = access::sample>
texture3d<T, access a = access::sample>
T:設定從紋理中讀取或是向紋理中寫⼊時的顏⾊類型. T能夠是half, float, short, int 等
複製代碼
代碼示例:
void 函數名稱 (texture2d<float> imgA [[ texture(0) ]] ,
texture2d<float, access::read> imgB [[ texture(1) ]],
texture2d<float, access::write> imgC [[ texture(2) ]])
{
...
}
複製代碼
修飾符如下詳細說明.ui
在GLSL中能夠對紋理的環繞方式、過濾方式等進行設置,一樣在Metal中,可使用採樣器類型,來對一個紋理進行採樣操做.在Metal框架中,有一個對應着色器語言的採樣器的對象MTLSampleState
,做爲圖形渲染着色器函數參數,或是並行計算函數的參數傳遞spa
enum class min_filter { nearest, linear }; 設置紋理採樣的縮⼩過濾方式;
enum class mag_filter { nearest, linear }; 設置紋理採樣的放⼤過濾方式;
設置紋理s,t,r座標的尋址模式; 環繞方式
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 };
複製代碼
注意:在Metal 程序中初始化的採樣器必須使⽤
constexpr
修飾符聲明
代碼示例:
constexpr sampler s(coord::pixel, address::clamp_to_zero, filter::linear);
constexpr sampler a(coord::normalized);
constexpr sampler b(address::repeat);
constexpr sampler s(address::clamp_to_zero, filter::linear)
複製代碼
Metal函數修飾符有3種
數據並⾏計算着⾊函數
. 它能夠被分配在⼀維/⼆維/三維線程組中去執⾏.(當某段代碼須要高效計算時,可使用kernel進行修飾)頂點着⾊函數
, 它將爲頂點數據流中的每一個頂點數據執⾏⼀次,而後爲每一個頂點⽣成數據輸出到繪製管線,(能夠理解爲在GLSL種,頂點着色源碼main函數中,返回的gl_Position);⽚元着⾊函數
, 它將爲⽚元數據流中的每一個⽚元和其關聯執⾏⼀次,而後將每一個⽚元⽣成的顏⾊數據輸出到繪製管線中(能夠理解爲在GLSL中,片元着色源碼main函數中,返回的gl_FragColor)注意:
void類型
;不能夠調用被函數修飾符修飾的函數
,不然編譯失敗kernel void test1(...){...}
vertex float4 test2(...)
{
test1(...); //編譯失敗
}
void test3(...)
{
test1(...); //編譯經過
}
複製代碼
Metal着色器語言使用地址空間修飾符
來表示一個函數變量或參數變量,被分配在哪一片內存區域
.
注意:
被device修飾的參數,會指向設備內存池分配出來的緩存對象,即顯存.它是可讀寫的,並且讀取速度快.
device float4 *color;
struct foo{
float2[3];
int b[2];
};
device foo *info;
複製代碼
紋理對象是默認分配在設備內存空間; ⼀個紋理對象的內容⽆法直接訪問. Metal 提供讀寫紋理的內建函數;
線程組地址空間⽤於爲並⾏計算着⾊函數
分配內存變量. 這些變量被⼀個線程組的全部線程共享
. 在線程組地址空間分配的變量不能被⽤於圖形繪製着⾊函數頂點着⾊函數, ⽚元着⾊函數
kernel void funcName(threadgroup float *name [[ threadgroup(0) ]])
{
// A float allocated in threadgroup address space
threadgroup float x;
// An array of 10 floats allocated in threadgroup address space
threadgroup float b[10];
}
複製代碼
只讀的
常量
.注意:
聲明爲常量的變量賦值會產⽣編譯錯誤,聲明常量可是沒有賦初值也會編譯錯誤.
constant float samples[] = {1,2,3};
samples[] = {3,3,3}; // 編譯錯誤
constant float a; // 編譯錯誤
複製代碼
能夠理解Swift中let,OC中const
thread地址空間指向每一個線程準備的地址空間, 這個線程的地址空間定義的變量在其餘線程不可⻅
, 在圖形繪製着⾊函數或者並⾏計算着⾊函數中,均可以聲明的變量thread地址空間分配;
但願變量能夠在單獨的線程中進行高效的計算,但又不想被共享時,多用於併發運算中,而不是圖形處理
kernel void func(...)
{
float x; // 普通變量
thread float b; //分配在線程地址空間
}
複製代碼
設備地址空間
的任意數據類型的指針或者引用常量地址空間
的任意數據類型的指針或引⽤被着色器函數的緩存(device 和 constant) 不能重名
對於每一個着⾊器函數來講, ⼀個修飾符是必須指定的. 他⽤來設定⼀個緩存,紋理, 採樣器的位置(location:如GLSL中的glGetAttribLocation
)
[[buffer (index)]]
[[texture (index)]]
[[sampler (index)]]
[[threadgroup (index)]]
index是⼀個unsigned integer類型的值,它表示了⼀個緩存、紋理、採樣器參數的位置(在函數參數索引表中的位置)。 從語法上講,屬性修飾符的聲明位置應該位於參數變量名以後
//代碼案例:並⾏計算着⾊函數add_vectors,把兩個設備地址空間中的緩存inA和inB相 加,而後把結果寫⼊到緩存out
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];
}
複製代碼
當結構體中定義多個顏色,須要作區分時
struct myColors{
float4 clr_f [[ color(0) ]]; // color attachment 1 附着點1
int4 clr_i [[ color(1) ]]; // color attachment 2 附着點2
uint clr_ui [[ color(2) ]];
}
複製代碼