I am trying to create a framework that works with METAL Api (iOS). I am pretty new to this platform and I would like to know how to build the framework to work with .metal files
As someone looking to include metal shader functions in a SceneKit / ARKit related framework, the available answers led me in the wrong direction. There is a much simpler solution that uses makeDefaultLibrary(bundle: Bundle) (iOS 10+) to access the functions included in a framework's .metal
dependencies. Adding here for people in a similar position.
TL;DR, Access a Framework's MTLLibrary like this:
//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 creates a default library of shader functions at build time by compiling .metal
dependencies. This is true of both framework targets and app targets, so the real question is, how do I access my framework’s default library?
It’s possible to access a framework’s default library using the using the makeDefaultLibrary(bundle: Bundle)
method on MTLDevice
. The sample code above shows more detail.
For Scenekit/ARKit with SCNProgram
The bundle library can be set as an SCNProgram’s library property, and then fragment and shader functions can be defined just as if the .metal file was included in the main project:
//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 )
}