简体   繁体   中英

Swift include metal shader into library [Using swift package manager]

I've always worked with Xcode projects but now I'm starting a project that I want in the future run on other platforms, so I'm using Swift Package Manager to generate the project, but I'm facing an issue, my library need to include a metal shader file (also a openGL shader file), but I'm clueless on how to accomplish this.

My project has 2 parts, the library that holds the graphic stuff and the executable that is my actual application, so I want to import my graphic library into my application, but the issue is that the metal shader file is not included in the Xcode project also seems that isn't compiled/included in the bundled files for the library so I can load at runtime and use it when needed.

Also if you think I'm doing something completely wrong just point me.

Regards

In Xcode 12 (swift-tools-version:5.3) a .metal file can be used from within a Swift Package by using the MTLDevice.makeDefaultLibrary(bundle:) method.

The Swift Package should have a structure similar to:

-Sources
    -MetalShaders
        -MetalShaders.metal
        -MetalShaders.swift
-Tests
    -MetalShadersTests
        -MetalShadersTests.swift

The file MetalShaders.metal should contain the Metal source code, and the file MetalShaders.swift should contain the necessary setup code. One example for initial content of MetalShaders.swift is the following:

// A metal device for access to the GPU.
public var metalDevice: MTLDevice!

// A metal library.
public var packageMetalLibrary: MTLLibrary!

// Function to perform the initial setup for Metal processing on the GPU
public func setupMetal() {
    // Create metal device for the default GPU:
    metalDevice = MTLCreateSystemDefaultDevice()
    
    // Create the library of metal functions
    // Note: Need to use makeDefaultLibrary(bundle:) as the "normal"
    //       call makeDefaultLibrary() returns nil.
    packageMetalLibrary = try? metalDevice.makeDefaultLibrary(bundle: Bundle.module)

    // List the available Metal shader functions
    print(packageMetalLibrary.functionNames)

    //
    // ... add additional setup code here ...
    // 

}

With this approach, the packageMetalLibrary variable can then be accessed from the Xcode project that has a dependency on the Swift Package by importing the target from the swift package the same way that other frameworks or packages are imported.

import MetalShaders

It may be possible to use this approach with earlier versions of Xcode as the method MTLDevice.makeDefaultLibrary(bundle:) is available since iOS 10 / macOS 10.12, but the Bundle.module extension would need to be implemented and it is not clear if this will work.

Put your Shaders.metal file to Metal directory. Then add recources to your target in Package.swift

.target(
   name: "TargetName",
   dependencies: [
     .product(name: "AnotherModule", package: "AnotherPackage")
   ],
   resources: [
     .copy("Metal/")
   ]
)

Now you can access to your library:

let url = Bundle.module.url(forResource: "Shaders", withExtension: "metal", subdirectory: "Metal")!
let source = try String(contentsOf: url)
let library = try device.makeLibrary(source: source, options: nil)

Or even better (but only for a single shader file):

.target(
   name: "TargetName",
   dependencies: [
     .product(name: "AnotherModule", package: "AnotherPackage")
   ],
   resources: [
     .process("Metal/Shaders.metal")
   ]
)

Now you can access to your library:

let library = try device.makeDefaultLibrary(bundle: Bundle.module)
let source = """

    #include <metal_stdlib>
    using namespace metal;

    struct Vertex {...
"""
let library = try device.makeLibrary(source: source options: nil)
print(library.functionNames)

Metal's class allow to build shader library from String at runtime. https://developer.apple.com/documentation/metal/libraries Hoping it helps.

Seems that right now isn't possible to do this. I'll update this when find a workaround or Apple fix this issue.

https://bugs.swift.org/browse/SR-2866

Edit: a temporary solution that I found was create an bash script that edit the workspace settings to change the build path and then compile/copy the metallib file to the executable path (didn't tried yet to see how to work distributing the library through SPM or something like that, right now only works with the code in the same folder (ie Sources/MyEngine <- (this folder has the metal shader), Sources/MyGame).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM