Metal Shader Language語法規範

Metal 介紹

  • Metal基於C++ 11.0 語⾔設計,是⼀個⽤來編寫3D圖形渲染邏輯並⾏計算核⼼邏輯的編程語⾔,編寫Metal框架的APP須要使⽤Metal着⾊語⾔程序.
  • Metal着⾊器語⾔使⽤Clang和LLVM,編譯器對於在GPU上的代碼執⾏效率有更好的控制.

假如當須要利用GPU的高計算能力時,咱們也可使用Metal來幫助咱們,由於它不只僅能夠作圖形渲染,也擁有高併發計算的能力.編程

好比在AVFoundation作人臉識別、二維碼識別、音視頻的編解碼等工做中,須要對每一幀的數據進行計算,這種狀況下的計算量是很是大的,蘋果就利用硬件加速器(指的就是GPU)來進行高併發計算。數組


Metal和C++11.0 異同

1. C++ 11.0 特性在Metal 語⾔中不⽀持之處

  • Lambda 表達式;
  • 遞歸函數調⽤ (Metal優點在於計算而非邏輯)
  • 動態轉換操做符
  • 類型識別
  • 對象建立new 和銷燬delete 操做符
  • 操做符 noexcept
  • goto 跳轉
  • 變量存儲修飾符register 和 thread_local
  • 虛函數修飾符
  • 派⽣類
  • 異常處理
  • C++ 標準庫在Metal語⾔中也不可以使⽤

2.Metal 語⾔中對於指針使⽤的限制

  • Metal圖形和並⾏計算函數⽤到的⼊參數,若是是指針必須使⽤地址空間修飾符 (device,threadgroup,constant)
  • 不⽀持函數指針
  • 函數名不能出現main

3.Metal 像素座標系統

  • Metal 中紋理/幀緩存區attachment的像素使⽤的座標系統的原點是左上⻆

Metal數據類型

1.標量類型

Metal ⽀持後綴表示字⾯量類型, 例如 0.5F, 0.5f; 0.5h, 0.5H. 這在GLSL中是不被容許的.緩存

bool a = true;
char b = 1;
int  c = 15;
size_t d = 1;
複製代碼

2.向量類型

向量支持以下類型:markdown

n指的是維度併發

  • booln
  • charn
  • shortn
  • intn
  • ucharn
  • ushortn
  • uintn
  • halfn
  • floatn
語法規則:
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’是不能夠混用的

複製代碼

3.矩陣

nxm分別指的是矩陣的行數和列數,最大支持4行4列 例:4x4框架

  • halfnxm
  • floatnxm
語法規則
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};高併發

  • sample:紋理對象能夠被採樣. 採樣⼀維(sample2D/sample3D:採樣二維/三維紋理數據),這是使⽤或不使⽤採樣器從紋理中讀取數據;
  • 不使⽤採樣器, ⼀個圖形渲染函數或者⼀個並⾏計算函數能夠讀取紋理對象;
  • ⼀個圖形渲染函數或者⼀個並⾏計算函數能夠向紋理對象寫⼊數據;
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:表示該函數是⼀個數據並⾏計算着⾊函數. 它能夠被分配在⼀維/⼆維/三維線程組中去執⾏.(當某段代碼須要高效計算時,可使用kernel進行修飾)
  • vertex:表示該函數是⼀個頂點着⾊函數, 它將爲頂點數據流中的每一個頂點數據執⾏⼀次,而後爲每一個頂點⽣成數據輸出到繪製管線,(能夠理解爲在GLSL種,頂點着色源碼main函數中,返回的gl_Position);
  • fragment: 表示該函數是⼀個⽚元着⾊函數, 它將爲⽚元數據流中的每一個⽚元和其關聯執⾏⼀次,而後將每一個⽚元⽣成的顏⾊數據輸出到繪製管線中(能夠理解爲在GLSL中,片元着色源碼main函數中,返回的gl_FragColor)

注意:

  • 使⽤kernel 修飾的函數. 其返回值類型必須是void類型;
  • 在函數修飾符修飾過的函數中,不能夠調用被函數修飾符修飾的函數,不然編譯失敗
kernel void test1(...){...}

vertex float4 test2(...)
{
	test1(...); //編譯失敗
}

void test3(...)
{
	test1(...); //編譯經過
}
複製代碼

變量或參數的地址空間修飾符

Metal着色器語言使用地址空間修飾符來表示一個函數變量或參數變量,被分配在哪一片內存區域.

  • device
  • threadgrounp線程組
  • constant
  • thread

注意:

  • 對於圖形着色器函數(vertex、fragment修飾的函數),當參數涉及指針變量或者引用類型時,必須使用device或constant的地址空間修飾符
  • 對於並行計算着色函數(kernel修飾的函數),當參數涉及指針變量或引用類型時,必須使用device或treadgrounp或這constant的地址空間修飾符

1.Device Address Space設備地址空間

被device修飾的參數,會指向設備內存池分配出來的緩存對象,即顯存.它是可讀寫的,並且讀取速度快.

device float4 *color; 

struct foo{
	float2[3];
    int b[2];
};
device foo *info;
複製代碼

紋理對象是默認分配在設備內存空間; ⼀個紋理對象的內容⽆法直接訪問. Metal 提供讀寫紋理的內建函數;

2.threadgroup Address Space線程組地址空間

線程組地址空間⽤於爲並⾏計算着⾊函數分配內存變量. 這些變量被⼀個線程組的全部線程共享. 在線程組地址空間分配的變量不能被⽤於圖形繪製着⾊函數頂點着⾊函數, ⽚元着⾊函數

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];
}
複製代碼

3.constant Address Space常量地址空間

  • 常量地址空間指向的緩存對象也是從設備內存池分配存儲, 可是它是只讀的
  • 在程序域的變量必須定義在常量地址空間而且聲明的時候初始化; ⽤來初始化的值必須是編譯時的常量.

注意:

聲明爲常量的變量賦值會產⽣編譯錯誤,聲明常量可是沒有賦初值也會編譯錯誤.

constant float samples[] = {1,2,3};
samples[] = {3,3,3}; // 編譯錯誤

constant float a; 	 // 編譯錯誤
複製代碼

能夠理解Swift中let,OC中const

4.thread Address Space線程地址空間

thread地址空間指向每一個線程準備的地址空間, 這個線程的地址空間定義的變量在其餘線程不可⻅, 在圖形繪製着⾊函數或者並⾏計算着⾊函數中,均可以聲明的變量thread地址空間分配;

但願變量能夠在單獨的線程中進行高效的計算,但又不想被共享時,多用於併發運算中,而不是圖形處理

kernel void func(...)
{
	float x; // 普通變量
    thread float b; //分配在線程地址空間
}
複製代碼

函數參數與變量的傳遞修飾

  • device buffer設備緩存區: 一個指向設備地址空間的任意數據類型的指針或者引用
  • constant buffer常量緩存區: ⼀個指向常量地址空間的任意數據類型的指針或引⽤
  • texture: 紋理對象
  • sampler: 採樣器對象
  • threadGrounp: 在線程組中供各線程共享的緩存

被着色器函數的緩存(device 和 constant) 不能重名

對於每一個着⾊器函數來講, ⼀個修飾符是必須指定的. 他⽤來設定⼀個緩存,紋理, 採樣器的位置(location:如GLSL中的glGetAttribLocation)

  • device buffers/ constant buffer [[buffer (index)]]
  • texture [[texture (index)]]
  • sampler [[sampler (index)]]
  • threadgroup buffer [[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];
 }
複製代碼


內建變量屬性修飾符

  • [[vertex_id]]頂點id標識符
  • [[position]] 在頂點函數中:表示頂點信息(float4) / 在片元函數中:表示像素點(片元)在屏幕窗⼝的相對位置(x, y, z, 1/w)
  • [[point_size]] 點的⼤⼩(float)
  • [[color(m)]] 顏⾊, m編譯前得肯定;
當結構體中定義多個顏色,須要作區分時
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) ]];
}
複製代碼
  • [[stage_in]] ⽚元着⾊函數使⽤的單個⽚元輸⼊數據,是由頂點着⾊函數輸出而後通過光柵化⽣成的.在頂點/片元函數中只能有一個參數被[[stage_in]]修飾,它能夠時整型\浮點標量、整型\浮點向量、結構體.
相關文章
相關標籤/搜索