提问者建议的方法可能行不通(因此,缺少示例代码)。金属着色器 (.metal) 只是函数的集合,它不是 MTLLibrary (.metallib) 制作的。这是从字符 (const char *) 数组(与 NSString 不同)编译金属着色器的工作代码;随后是在运行前将 .metal 文件转换为 .metallib 文件的说明。
在运行时编译金属着色器
以下示例还可用于为用户提供着色器编辑器,并允许您仅更新应用的着色器部分,而无需用户更新整个应用:
NSError* error = NULL;
const char* vshSource =
"using namespace metal;\n"
"typedef struct {\n"
" packed_float2 position;\n"
" packed_float2 texcoord;\n"
"} Vertex;\n"
"typedef struct {\n"
" float3x3 matrix;\n"
" float3 offset;\n"
"} ColorConversion;\n"
"typedef struct {\n"
" float4 position [[position]];\n"
" float2 texcoord;\n"
"} Varyings;\n"
"vertex Varyings vertexPassthrough(\n"
"device Vertex* verticies [[ buffer(0) ]],\n"
"unsigned int vid [[ vertex_id ]]\n"
") {\n"
" Varyings out;\n"
" device Vertex& v = verticies[vid];\n"
" out.position = float4(float2(v.position), 0.0, 1.0);\n"
" out.texcoord = v.texcoord;\n"
" return out;\n"
"}\n";
const char* fshSource =
"using namespace metal;\n"
"typedef struct {\n"
"packed_float2 position;\n"
"packed_float2 texcoord;\n"
"} Vertex;\n"
"typedef struct {\n"
"float3x3 matrix;\n"
"float3 offset;\n"
"} ColorConversion;\n"
"typedef struct {\n"
"float4 position [[position]];\n"
"float2 texcoord;\n"
"} Varyings;\n"
"fragment half4 fragmentColorConversion(\n"
"Varyings in [[ stage_in ]],\n"
"texture2d<float, access::sample> textureBGRA [[ texture(0) ]],\n"
"constant ColorConversion &colorConversion [[ buffer(0) ]]\n"
") {\n"
"constexpr sampler s(address::clamp_to_edge, filter::linear);\n"
"return half4(half3(textureBGRA.sample(s, in.texcoord).rgb), 1.0);\n"
"}\n";
id <MTLFunction> vertexProgram;
id <MTLLibrary> vertexLibrary = [_device newLibraryWithSource:[NSString stringWithUTF8String:vshSource] options:NULL error:&error];
if (NULL != vertexLibrary)
{
vertexProgram = [vertexLibrary newFunctionWithName:@"vertexPassthrough"];
} else {
NSLog(@"Error compiling vertex program: %@", error.description);
}
id <MTLFunction> fragmentProgram;
id <MTLLibrary> fragmentLibrary = [_device newLibraryWithSource:[NSString stringWithUTF8String:fshSource] options:NULL error:&error];
if (NULL != fragmentLibrary)
{
fragmentProgram = [fragmentLibrary newFunctionWithName:@"fragmentColorConversion"];
} else {
NSLog(@"Error compiling fragment program: %@", error.description);
}
以下内容摘自 Apple 开发者文档出版物;尽管这些信息相对初级,但在就其主题进行交流时,请将其用作您和您的受众共享的通用框架的基础。
Creating Libraries During the App Build Process
出于同样的原因,接受的答案是完全错误的;而且,它关于性能权衡的说法是有问题的。以下是关于编译 Metal 着色器和创建 Metal 库的唯一明确声明,然后是实际代码:
函数和库
本章描述了如何创建一个 MTLFunction 对象作为
参考金属着色器或计算函数以及如何组织
并使用 MTLLibrary 对象访问函数。
MTLFunction 表示着色器或计算函数
一个 MTLFunction 对象代表一个单独的函数
Metal 着色语言并在 GPU 上作为
图形或计算管道。有关金属底纹的详细信息
语言,请参阅金属着色语言指南。
在 Metal 运行时和图形之间传递数据或状态,或者
用 Metal 着色语言编写的计算函数,您分配一个
纹理、缓冲区和采样器的参数索引。参数索引
标识哪个纹理、缓冲区或采样器被引用
Metal 运行时和 Metal 着色代码。
对于渲染过程,您指定一个 MTLFunction 对象用作
MTLRenderPipelineDescriptor 对象中的顶点或片段着色器,如
在创建渲染管道状态中有详细说明。对于计算通行证,您
创建 MTLComputePipelineState 时指定一个 MTLFunction 对象
目标设备的对象,如指定计算状态中所述
和计算命令编码器的资源。
库是函数的存储库
一个 MTLLibrary 对象代表一个或多个 MTLFunction 的存储库
对象。单个 MTLFunction 对象代表一个 Metal 函数
这是用着色语言编写的。在金属底纹中
语言源代码,任何使用 Metal 函数的函数
限定符(顶点、片段或内核)可以表示为
库中的 MTLFunction 对象。没有这些之一的金属功能
函数限定符不能直接由 MTLFunction 表示
对象,尽管它可以被着色器中的另一个函数调用。
库中的 MTLFunction 对象可以通过以下任一方式创建
这些来源:
- 已编译成二进制库的金属着色语言代码
应用构建过程中的格式。
- 包含金属着色语言源代码的文本字符串,由应用在运行时编译。
在应用构建过程中编译着色器语言源文件并构建库(.metallib 文件)比在运行时编译着色器源代码获得更好的应用性能。您可以在 Xcode 中或使用命令行实用程序构建库。
使用 Xcode 构建库
任何着色器源文件位于
您的项目会自动用于生成默认库,
您可以使用 MTLDevice 的 newDefaultLibrary 方法从 Metal 框架代码访问它。
使用命令行实用程序构建库
图 8-1 显示了构成编译器的命令行实用程序
金属着色器源代码的工具链。当您包含 .metal 文件时
在你的项目中,Xcode 调用这些工具来构建一个库文件
您可以在运行时在您的应用中访问。
不使用 Xcode 将着色器源编译到库中:
- 使用 metal 工具将每个 .metal 文件编译为单个 .air 文件,该文件存储着色器语言代码的中间表示 (IR)。
- (可选)使用 metal-ar 工具将多个 .air 文件一起归档到一个 .metalar 文件中。 (metal-ar 类似于 Unix ar。)
- 使用 metallib 工具从 IR .air 文件或存档 .metalar 文件构建 Metal .metallib 库文件。
示例:使用命令行实用程序构建库文件
xcrun -sdk macosx metal MyLibrary.metal -o MyLibrary.air
xcrun -sdk macosx metallib MyLibrary.air -o MyLibrary.metallib
要在框架代码中访问生成的库,请调用 newLibraryWithFile:error: 方法:
NSError *libraryError = NULL;
NSString *libraryFile = [[NSBundle mainBundle] pathForResource:@"MyLibrary" ofType:@"metallib"];
id <MTLLibrary> myLibrary = [_device newLibraryWithFile:libraryFile error:&libraryError];
if (!myLibrary) {
NSLog(@"Library error: %@", libraryError);
}