函数与库
本章节介绍如何创建一个作为 Metal 着色器或计算函数的引用的 MTLFunction 对象,以及如何利用 MTLLibrary 对象组织和访问一个函数。
代表着色器函数或计算函数的 MTLFunction
一个 MTLFunction 对象代表一个由 Metal 着色器语言编写的函数,它能够作为图形或计算管线的一部分在 GPU 上执行。
你可以声明一个纹理、缓冲或采样器的参数索引,来实现在 Metal 运行时和用Metal 着色器语言编写的图形、计算函数之间传递数据或状态。参数索引唯一标识一个被 Metal 运行时和 Metal 着色代码共享的纹理、缓冲或采样器。
创建一个渲染管道状态 介绍了如何在一个渲染通道内,为 MTLRenderPipelineDescriptor 对象确定一个被用于顶点或片段着色器的 MTLFunction 对象。为计算缓冲编码器确定计算状态和资源 介绍了如何在一个计算通道内,为指定设备创建 MTLComputePipelineState 时确定一个 MTLFunction 对象。
函数库
一个 MTLLibrary 对象代表了一个或多个 MTLFunction 对象的集合库。单独一个 MTLFunction 对象代表了一个用 Metal 着色器语言编写的 Metal 函数,任何使用了 Metal 函数标识符(vertex, fragment, 或 kernel)的函数都可以被引用为函数库中的一个 MTLFunction 对象。没有被这些函数标识符标识的函数则不能直接被 MTLFunction 对象引用,但是可以在着色器中被间接调用。
可以通过以下方法创建一个函数库中的 MTLFunction 对象。
- 在 app 构建阶段,将 Metal 着色器语言编写的代码编译到二进制库格式中
- 在 app 运行时,编译一段包含 Metal 着色器语言源代码的字符串
通过编译后的代码创建函数库
为了最佳性能,最好在 Xcode 编译构建 app 过程中就将你的 Metal 着色器源代码编译到一个二进制文件中,这样能避免在运行时执行编译函数源码的性能损耗。调用以下 MTLDevice 方法之一即可从一个二进制库创建一个 MTLLibrary 对象。
-
newDefaultLibrary
从主 bundle 中检索包含一个 app 的 XCode 项目中所有着色器和计算函数的函数库 -
newLibraryWithFile:error:
接受一个函数库文件的路径,返回包含那个文件中所有存储的方法的 MTLLibrary 对象 -
newLibraryWithData:error:
接受一个包含函数代码的二进制数据集合,返回一个 MTLLibrary 对象
MTLFunction 不提供对函数参数的访问。可以在流水线状态的创建过程中,生成一个可访问着色器或计算函数参数细节的反射对象(可能为 MTLRenderPipelineReflection 或 MTLComputePipelineReflection,取决于命令编码器类型)。更多创建流水线状态和反射对象的信息请查阅 创建渲染流水线状态 或 创建计算流水线状态。如果不使用反射对象中的数据,要避免持有它。
反射对象中包含一个数组,数组中包含所有当前命令编码器支持的函数的 MTLArgument 对象。对于 MTLComputeCommandEncoder,MTLComputePipelineReflection 的 arguments 属性包含一个 MTLArgument 数组,对应其计算函数的参数。对于 MTLRenderCommandEncoder,MTLRenderPipelineReflection 有两个属性,vertexArguments 和 fragmentArguments,分别对应于顶点着色器和片段着色器函数的参数。
反射对象不会包含所有的函数参数,仅包含具有相关资源的参数,不包含用 [[ stage_in ]] 修饰符或内建 [[ vertex_id ]] or [[ attribute_id ]] 修饰符声明的参数。
Listing 4-2 向你展示了如何获取一个反射对象(示例中用到了 MTLComputePipelineReflection 对象)并枚举它的 arguments 属性,获取其中的 MTLArgument 对象。
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 是一个从 0 开始的坐标值,表明它在对应参数表中的位置,如 [[ 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 就与之关联
如果 buffer 参数是一个结构体(此时 bufferDataType 为 MTLDataTypeStruct),则 bufferStructType 属性就会包含一个 MTLStructType 属性来表示这个结构体,bufferDataSize 代表了结构体的字节大小。如果 buffer 参数是一个数组(或者指向数组的指针),则 bufferDataType 属性就会表示数组中元素的类型,而 bufferDataSize 表示一个数组元素的字节大小。
Listing 4-3 深入一个 MTLStructType 对象来验证结构体成员变量,每一个都被 MTLStructMember 所表示。一个结构体成员变量可能是基本类型、数组或者一个嵌套结构体。如果这个成员变量是嵌套结构体,则调用此 MTLStructMember 的 structType 来获取一个 MTLStructType 变量以表示此嵌套结构体,然后继续递归深入解析它。如果成员变量是一个数组,则调用此 MTLStructMember 的 arrayType 来获取一个表示此数组的 MTLArrayType 变量,然后检查 MTLArrayType 的 elementType 变量以确定其内部元素的类型。如果 elementType 是 MTLDataTypeStruct,调用 elementStructType 以持有此结构体,然后继续深入它的成员变量。如果 elementType 是 MTLDataTypeArray,则调用 elementArrayType 来持有此子数组,并继续解析它。
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
}
}
网友评论