函數和庫
本章介紹如何創建一個MTLFunction對象作為Metal著色器或計算函數的參考,以及如何組織和訪問具有MTLLibrary對象的函數。
MTLFunction表示著色器或計算功能
一個MTLFunction對象表示一個單獨的功能,它以Metal著色語言編寫,并在GPU上作為圖形或計算流水線的一部分執行。有關Metal著色語言的詳細信息,請參閱“ Metal著色語言指南”。
要在Metal運行時間和以Metal著色語言編寫的圖形或計算函數之間傳遞數據或狀態,請為紋理,緩沖區和采樣器分配參數索引。參數索引標識由Metal運行時和Metal著色代碼引用的紋理,緩沖區或采樣器。
對于渲染遍,您可以在MTLFunction對象中指定一個用作頂點或片段著色器的MTLRenderPipelineDescriptor對象,如創建渲染管道狀態中所述。對于計算過程,您可以MTLFunction在MTLComputePipelineState為目標設備創建對象時指定對象,如“ 為計算命令編碼器指定計算狀態和資源”所述。
一個Library就是一個函數庫
一個MTLLibrary對象表示的一個或多個儲存庫MTLFunction對象。單個MTLFunction對象表示使用著色語言編寫的一個Metal函數。在Metal著色語言源代碼,使用一個Metal函數限定符任何功能(vertex,fragment,或kernel)可以通過一個代表MTLFunction在一個庫中的對象。沒有這些函數限定符之一的Metal函數不能由MTLFunction對象直接表示,盡管它可以由著色器中的另一個函數調用。
MTLFunction可以從以下任一來源創建庫中的對象:
Metal著色語言代碼,在應用程序構建過程中被編譯成二進制庫格式。
包含由運行時應用程序編譯的Metal著色語言源代碼的文本字符串。
從編譯代碼創建庫
為了獲得最佳性能,請在您的應用程序的Xcode構建過程中將Metal著色語言源代碼編譯成庫文件,從而避免在應用程序運行時編譯函數源的費用。要從MTLLibrary庫二進制創建對象,請調用以下方法之一MTLDevice:
newDefaultLibrary 檢索為主包構建的庫,其中包含應用程序的Xcode項目中的所有著色器和計算函數。
newLibraryWithFile:error:將路徑傳遞給庫文件,并返回一個MTLLibrary包含存儲在該庫文件中的所有函數的對象。
newLibraryWithData:error:使用二進制blob包含庫中函數的代碼并返回一個MTLLibrary對象。
有關在構建過程中編譯Metal著色語言源代碼的更多信息,請參閱
在應用程序構建過程中創建庫。
從源代碼創建庫
要從MTLLibrary可能包含多個函數的Metal著色語言源代碼字符串創建一個,請調用以下方法之一MTLDevice。這些方法在創建庫時編譯源代碼。要指定要使用的編譯器選項,請設置MTLCompileOptions對象中的屬性。
newLibraryWithSource:options:error:從輸入字符串同步編譯源代碼以創建MTLFunction對象,然后返回MTLLibrary包含它們的對象。
newLibraryWithSource:options:completionHandler:從輸入字符串異步編譯源代碼以創建MTLFunction對象,然后返回MTLLibrary包含它們的對象。completionHandler是在對象創建完成時調用的代碼塊。
從Library獲取功能
返回具有請求名稱的對象的newFunctionWithName:方法。如果在庫中找不到使用Metal著色語言功能限定符的函數的名稱,則返回。MTLLibraryMTLFunctionnewFunctionWithName:nil
清單4-1使用通過其完整路徑名定位庫文件的newLibraryWithFile:error:方法,MTLDevice并使用其內容創建MTLLibrary具有一個或多個MTLFunction對象的對象。加載文件時出現任何錯誤error。然后創建一個表示在源代碼中調用的函數的對象的newFunctionWithName:方法。返回的函數對象現在可以在應用程序中使用。MTLLibraryMTLFunctionmy_funcmyFunc
清單4-1 從庫訪問函數
NSError *errors;
id <MTLLibrary> library = [device newLibraryWithFile:@"myarchive.metallib"
error:&errors];
id <MTLFunction> myFunc = [library newFunctionWithName:@"my_func"];
在運行時確定功能詳細信息
由于MTLFunction對象的實際內容由MTLFunction創建對象之前可以編譯的圖形著色器或計算函數定義,因此其源代碼可能無法直接用于該應用程序。您可以MTLFunction在運行時查詢以下屬性:
name一個帶有該函數名稱的字符串。
functionType,它指示函數是否被聲明為頂點,片段或計算函數。
vertexAttributes,一組MTLVertexAttribute對象,描述如何將頂點屬性數據組織在內存中,以及如何映射到頂點函數參數。有關更多詳細信息,請參閱數據組織的頂點描述符。
MTLFunction不提供對函數參數的訪問。在創建流水線狀態期間,可以獲得反映物體(MTLRenderPipelineReflection或MTLComputePipelineReflection根據命令編碼器的類型)來顯示著色器或計算函數參數的細節。有關創建管道狀態和反射對象的詳細信息,請參閱創建渲染管道狀態或創建計算管道狀態。如果不使用反射數據,請避免使用。
反射對象包含MTLArgument命令編碼器支持的每種類型功能的對象數組。因為MTLComputeCommandEncoder,屬性中MTLComputePipelineReflection有一個對應于其計算函數的參數的MTLArgument對象數組arguments。為MTLRenderCommandEncoder,MTLRenderPipelineReflection具有兩個屬性,vertexArguments并且fragmentArguments,這是分別對應于頂點函數參數和片段函數參數,陣列。
不是函數的所有參數都存在于反射對象中。反射對象僅包含具有關聯資源的參數,但不包含使用[[ stage_in ]]限定符或內置[[ vertex_id ]]或[[ attribute_id ]]限定符聲明的參數。
清單4-2顯示了如何獲取反射對象(在本示例中MTLComputePipelineReflection),然后遍歷MTLArgument其arguments屬性中的對象。
清單4-2 通過函數參數進行迭代
MTLComputePipelineReflection* reflection;
id <MTLComputePipelineState> computePS = [device
newComputePipelineStateWithFunction:func
options:MTLPipelineOptionArgumentInfo
reflection:&reflection error:&error];
for (MTLArgument *arg in reflection.arguments) {
// process each MTLArgument
}
該MTLArgument特性揭示參數傳遞給著色語言功能的細節。
- 該name屬性僅僅是參數的名稱。
- active 是一個布爾值,指示參數是否可以被忽略。
- index是在其對應的參數表中的基于零的位置。例如,for [[ buffer(2) ]],index是2。
- access描述任何訪問限制,例如讀取或寫入訪問限定符。
- type由著色語言限定符指示的,例如,[[ buffer(n) ]],[[ texture(n) ]],[[ sampler(n) ]],或[[ threadgroup(n) ]]。
type確定哪些其他MTLArgument屬性是相關的。
如果type是MTLArgumentTypeTexture,則該textureType屬性指示整體紋理類型(如texture1d_array,texture2d_ms以及texturecube在著色語言類型),并且textureDataType屬性指示組件的數據類型(如half,float,int,或uint)。
如果type是MTLArgumentTypeThreadgroupMemory,則threadgroupMemoryAlignment和threadgroupMemoryDataSize性質有關。
如果type是MTLArgumentTypeBuffer,則bufferAlignment,bufferDataSize,bufferDataType,和bufferStructType屬性相關。
如果緩沖區參數是一個結構(即,bufferDataType是MTLDataTypeStruct),則bufferStructType屬性包含MTLStructType表示該結構,和bufferDataSize包含該結構的大小,以字節為單位。如果緩沖區參數是數組(或指向數組的指針),則bufferDataType表示元素的數據類型,并bufferDataSize包含一個數組元素的大小(以字節為單位)。
清單4-3向下鉆取一個MTLStructType對象,以檢查結構體成員的細節,每個由MTLStructMember對象表示。結構體成員可以是簡單類型,數組或嵌套結構體。如果該成員是一個嵌套的結構體,那么調用structType方法MTLStructMember來獲取一個MTLStructType代表結構體的對象,然后遞歸深入分析它。如果該成員是一個數組,請使用該arrayType方法MTLStructMember獲取MTLArrayType表示該數組的方法。然后檢查它的elementType屬性MTLArrayType。如果elementType是MTLDataTypeStruct,請調用該elementStructType方法來獲取結構,并繼續深入其成員。如果elementType是MTLDataTypeArray,請調用elementArrayType方法獲取子陣列并進一步分析。
清單4-3 處理結構參數
MTLStructType *structObj = [arg.bufferStructType];
for (MTLStructMember *member in structObj.members) {
// process each MTLStructMember
if (member.dataType == MTLDataTypeStruct) {
MTLStructType *nestedStruct = member.structType;
// recursively drill down into the nested struct
}
else if (member.dataType == MTLDataTypeArray) {
MTLStructType *memberArray = member.arrayType;
// examine the elementType and drill down, if necessary
}
else {
// member is neither struct nor array
// analyze it; no need to drill down further
}
}