我正在尝试创建一个适用于METAL Api(iOS)的框架。我是这个平台的新手,我想知道如何构建框架来处理.metal文件(我正在构建一个静态库,而不是动态的)。它们应该是.a文件的一部分,还是作为框架包中的资源文件?或者还有其他方法吗?感谢。
更新
对于那些解决这个问题的人 - 我最终关注了warrenm的建议选项 - 将.metal文件转换为字符串并调用newLibraryWithSource:options:error:
。
虽然它不是性能最好的,但它允许我只发送一个框架文件,而无需导入额外的资源。对于使用着色器文件创建使用Metal,ARKit等的框架的人来说,这可能是有用的。
答案 0 :(得分:12)
有很多方法可以为Metal着色器提供静态库,所有这些都有不同的权衡。我试着在这里列举一下。
1)将.metal文件转换为静态字符串,这些字符串将附加到静态库中。
这可能是最糟糕的选择。我们的想法是将您的Metal着色器代码预处理为字符串,这些字符串作为字符串文字包含在静态库中。然后,您将使用newLibraryWithSource:options:error:
API(或其异步兄弟)将源转换为MTLLibrary
并检索函数。这需要您设计一个进行.metal
到字符串转换的过程,并且您将失去着色器预编译的好处,使得生成的应用程序变慢。
2)将.metal文件与静态库一起发送,并要求库用户将它们添加到应用目标
考虑到所有因素,这是一个不错的选择,但它会给您的用户带来更多负担并暴露您的Metal着色器源(如果这是一个问题)。静态库中的代码可以使用"默认库" (newDefaultLibrary
),因为代码将由Xcode自动编译到应用程序的default.metallib
中,该应用程序作为资源嵌入应用程序包中。
3)将.metallib文件与静态库一起发送
这是易用性,性能和安全性之间的良好中间点(因为它不会暴露您的着色器源,只有它的IR)。基本上,你可以创建一个"金属图书馆"项目中的目标,您将着色器代码放入其中。这将生成一个.metallib
文件,您可以将其与静态库一起发布,并将您的用户作为资源嵌入其应用目标中。您的静态库可以在运行时使用.metallib
或newLibraryWithData:error:
API加载newLibraryWithURL:error:
。由于您的着色器将被预编译,因此创建库将更快,并且您将保留编译时诊断的好处。
答案 1 :(得分:3)
当某人希望在SceneKit / ARKit相关框架中包含金属着色器功能时,可用的答案将我引向错误的方向。有一个更简单的解决方案,它使用makeDefaultLibrary(bundle: Bundle)(iOS 10+)访问框架的.metal
依赖项中包含的功能。为位置相似的人添加此处。
TL; DR,像这样访问框架的MTLLibrary:
//Get the framework bundle by using `Bundle(for: type(of: self))` from inside any framework class.
//Then use the bundle to define an MTLLibrary.
let frameworkBundle = Bundle(for: type(of: self))
let device = MTLCreateSystemDefaultDevice()
do {
let bundleLib = try device?.makeDefaultLibrary(bundle: frameworkBundle)
print(bundleLib.functionNames) //we can access our framework's metal functions! No build tricks/workarounds.
} catch {
print("Couldn't locate default library for bundle: \(frameworkBundle)")
print( error )
}
Xcode在编译时通过编译.metal
依赖关系来创建默认的着色器函数库。框架目标和应用程序目标都是如此,所以真正的问题是,如何访问框架的默认库?
可以使用makeDefaultLibrary(bundle: Bundle)
上的MTLDevice
方法来访问框架的默认库。上面的示例代码显示了更多细节。
对于带有SCNProgram的Scenekit / ARKit
可以将包库设置为SCNProgram的库属性,然后可以定义片段和着色器函数,就像主项目中包含.metal文件一样。
//The SCNProgram that will use our framework's metal functions
var program = SCNProgram()
//Use the framework's bundle to define an MTLLibrary.
let frameworkBundle = Bundle(for: type(of: self))
let device = MTLCreateSystemDefaultDevice()
do {
let bundleLib = try device?.makeDefaultLibrary(bundle: frameworkBundle)
//set the SCNProgram's library, and define functions as usual
program.library = bundleLib
program.fragmentFunctionName = "yourCustomFrameworkFragmentFunction"
program.vertexFunctionName = "yourCustomFrameworkVertexFunction"
} catch {
print("Couldn't locate default library for bundle: \(frameworkBundle)")
print( error )
}
答案 2 :(得分:0)
提问者建议的方法不可行(因此缺少示例代码)。金属着色器(.metal)只是函数的集合,它不是MTLLibrary(.metallib)make。以下是从字符(const char *
)数组(与NSString
不同)编译Metal着色器的工作代码;接下来是在运行之前将.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 Developer Documentation出版物的摘录;虽然这些信息相对较为简陋,但在沟通其主题时,请将其作为您和您的受众共享的共同框架的基础。
Creating Libraries During the App Build Process
由于同样的原因,接受的答案是错误的;而且,它对性能权衡的主张是值得怀疑的。以下是关于编译Metal着色器和创建Metal库的唯一权威陈述,其后是实际代码:
功能和库
本章介绍如何将MTLFunction对象创建为 引用金属着色器或计算功能以及如何组织 并使用MTLLibrary对象访问函数。
MTLFunction表示着色器或计算函数
MTLFunction对象表示写入的单个函数 金属着色语言并作为GPU的一部分在GPU上执行 图形或计算管道。有关金属阴影的详细信息 语言,请参阅金属着色语言指南。
在Metal运行时和图形或之间传递数据或状态 用Metal着色语言编写的计算函数,你指定一个 纹理,缓冲区和采样器的参数索引。参数索引 标识正在引用的纹理,缓冲区或采样器 Metal运行时和Metal着色代码。
对于渲染过程,您指定一个MTLFunction对象用作 MTLRenderPipelineDescriptor对象中的顶点或片段着色器,如 详细信息创建渲染管道状态。对于计算通行证,您 在创建MTLComputePipelineState时指定MTLFunction对象 目标设备的对象,如“指定计算状态”中所述 和计算命令编码器的资源。
图书馆是职能的储存库
MTLLibrary对象表示一个或多个MTLFunction的存储库 对象。单个MTLFunction对象表示一个Metal函数 用着色语写的。在金属底纹中 语言源代码,任何使用Metal函数的函数 限定符(顶点,片段或内核)可以用a表示 库中的MTLFunction对象。没有这些的金属功能 函数限定符不能由MTLFunction直接表示 对象,虽然它可以由着色器中的另一个函数调用。
库中的MTLFunction对象可以从以下任何一个创建 这些来源:
- 编译为二进制库的金属着色语言代码 应用程序构建过程中的格式。
- 包含应用程序在运行时编译的金属着色语言源代码的文本字符串。
在应用程序构建过程中编译着色器语言源文件并构建库(.metallib文件)比在运行时编译着色器源代码实现更好的应用程序性能。您可以在Xcode中或使用命令行实用程序构建库。
使用Xcode构建库
中的任何着色器源文件 您的项目会自动用于生成默认库, 您可以使用MTLDevice的newDefaultLibrary方法从Metal框架代码访问。
使用命令行工具构建库
图8-1显示了构成编译器的命令行实用程序 金属着色器源代码的工具链。包含.metal文件时 在您的项目中,Xcode调用这些工具来构建库文件 您可以在运行时在应用中访问。
要在不使用Xcode的情况下将着色器源编译到库中:
- 使用金属工具将每个.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:method:
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);
}