代码之家  ›  专栏  ›  技术社区  ›  DocForNoc

金属文件作为iOS框架的一部分

  •  15
  • DocForNoc  · 技术社区  · 7 年前

    我正在尝试创建一个与METAL Api(iOS)配合使用的框架。我对这个平台很陌生,我想知道如何构建与之合作的框架。金属文件(我正在构建一个静态库,而不是动态库)。他们是否应该参与。文件,还是作为框架捆绑包中的资源文件?或者还有其他方法吗?谢谢

    对于那些解决这个问题的人——我最终遵循了warrenm的1建议的选项——转换了。将文件金属化为字符串并调用 newLibraryWithSource:options:error: . 虽然它的性能不是最好的,但它只允许我发布一个框架文件,而无需导入额外的资源。这对于创建使用Metal、ARKit等着色器文件的框架的人来说可能很有用。

    3 回复  |  直到 7 年前
        1
  •  21
  •   warrenm    7 年前

    有许多方法可以为金属着色器提供静态库,但都有不同的权衡。我将在这里列举它们。

    1) 改变你的。将文件转换为静态字符串,并烘焙到静态库中。

    这可能是最糟糕的选择。其思想是将金属着色器代码预处理为字符串,这些字符串作为字符串文字包含在静态库中。然后使用 newLibraryWithSource:options:error: API(或其异步同级)将源代码转换为 MTLLibrary 并检索函数。这需要你设计一个过程来完成 .metal -到字符串转换,则会失去着色器预编译的好处,使生成的应用程序速度变慢。

    2) 发货。金属文件放在静态库旁边,并要求库用户将其添加到应用程序目标中

    总而言之,这是一个不错的选择,尽管它会给用户带来更大的负担,并暴露您的金属着色器源(如果这是一个问题的话)。静态库中的代码可以使用“默认库”( newDefaultLibrary ),因为代码将由Xcode自动编译到应用程序的 default.metallib ,作为资源嵌入到应用程序包中。

    3) 发货a。静态库旁边的metallib文件

    这是易用性、性能和安全性之间的一个很好的折衷点(因为它不暴露着色器源,只暴露其IR)。基本上,您可以在项目中创建“金属库”目标,将着色器代码放入其中。这将产生一个 .metallib 文件,您可以将其与静态库一起提供,并将用户作为资源嵌入到他们的应用程序目标中。静态库可以加载 .金属库 在运行时 newLibraryWithData:error: newLibraryWithURL:error:

        2
  •  20
  •   Jim Martin    6 年前

    作为一个希望在SceneKit/ARKit相关框架中包含金属着色器函数的人,可用的答案将我引向了错误的方向。有一种更简单的解决方案 makeDefaultLibrary(bundle: Bundle) .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通过编译在构建时创建着色器函数的默认库 .金属 依赖项。框架目标和应用目标都是如此,所以真正的问题是, 如何访问框架默认库?

    可以使用访问框架默认库 makeDefaultLibrary(bundle: Bundle) MTLDevice . 上面的示例代码显示了更多细节。

    对于带有SCNProgram的Scenekit/ARKit

    bundle库可以设置为SCNProgram的库属性,然后可以定义片段和着色器函数,就像。主要项目包括金属锉:

            //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 )
            }
    
        3
  •  0
  •   James Bush    6 年前

    提问者建议的方法不可能奏效(因此,缺乏示例代码)。金属着色器(.Metal)只是一组函数,而不是MTLLibrary(.metallib)生成的。下面是从角色编译金属着色器的工作代码( const char * )数组(与不同 NSString

    在运行时编译金属着色器

    以下示例还可用于为用户提供着色器编辑器,并允许您仅更新应用程序的着色器部分,而无需用户更新整个应用程序:

    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);
    }
    

    以下是苹果开发者文档出版物的摘录;尽管这些信息相对来说比较初级,但在交流其主题时,请将其作为您和您的观众共享的公共框架的基础。

    Creating Libraries During the App Build Process

    出于同样的原因,公认的答案是完全错误的;而且,关于性能权衡的说法也值得怀疑。以下是关于编译金属着色器和创建金属库的唯一明确声明,然后是实际代码:

    函数和库

    本章介绍如何将MTLFunction对象创建为 参考金属着色器或计算函数以及如何组织 和使用MTLLibrary对象访问函数。

    MTLFunction对象表示写入的单个函数 金属着色语言,并在GPU上作为 语言,请参见《金属着色语言指南》。

    在金属运行时和图形或 用金属着色语言编写的计算函数,指定一个 纹理、缓冲区和采样器的参数索引。参数索引 标识所引用的纹理、缓冲区或采样器

    对于渲染过程,可以指定MTLFunction对象作为 MTLRenderPipelineDescriptor对象中的顶点或片段着色器,如 详细信息请参见创建渲染管道状态。对于计算过程,您 创建MTLComputePipelineState时指定MTLFunction对象 对象,如指定计算状态中所述

    库是功能的存储库

    MTLLibrary对象表示一个或多个MTL函数的存储库 物体。单个MTLFunction对象表示一个金属函数 这是用着色语言写的。在金属着色中 语言源代码,任何使用金属函数的函数 限定符(顶点、片段或内核)可以用 库中的MTLFunction对象。没有这些元素的金属函数 函数限定符不能直接由MTLFunction表示 对象,尽管它可以由着色器中的另一个函数调用。

    库中的MTLFunction对象可以从以下任意一种创建: 这些来源:

    • 编译到二进制库中的金属着色语言代码 在应用程序构建过程中格式化。

    与在运行时编译着色器源代码相比,在应用程序构建过程中编译着色器语言源文件并构建库(.metallib文件)可以获得更好的应用程序性能。您可以在Xcode内或使用命令行实用程序构建库。

    使用Xcode构建库

    中的任何着色器源文件 您可以使用MTLDevice的newDefaultLibrary方法从金属框架代码中访问它。

    使用命令行实用程序构建库

    图8-1显示了构成编译器的命令行实用程序 金属着色器源代码的工具链。当你包括。金属锉刀 您可以在运行时在应用程序中访问。

    要在不使用Xcode的情况下将着色器源编译到库中,请执行以下操作:

    1. 使用金属工具编译每一个。金属锉成单锉。air文件,其中存储着色器语言代码的中间表示(IR)。
    2. 使用metallib工具构建金属。来自IR的metallib库文件。air文件或存档。metalar文件。

    示例:使用命令行实用程序构建库文件

    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);
    }