I am currently finalizing my Swift Student Challenge submission, and Metal shaders are an essential part of my app. However, during submission, I noticed a note explaining: "Note: Xcode app playgrounds are run in Simulator", which is not possible for my app, as it also requires the camera of a physical device to function. So, I am currently transferring my app from Xcode into Swift Playgrounds, which I presume will run on physical devices.
However, I noticed that Swift Playgrounds do not yet support Metal shaders directly, so I am now pre-compiling my shaders to load them at runtime instead. Note that all the code below was run either in the terminal or in Xcode.
I have already compiled my Metal shaders with:
xcrun -sdk iphoneos metal -o Shaders.ir -c Shaders.metal
xcrun -sdk iphoneos metallib Shaders.ir -o Shaders.metallib
Which seems to have run without any problems.
When I run:
let shaderPath = Bundle.main.path(forResource: "Shaders", ofType: "metallib")
let shaderURL = URL(fileURLWithPath: shaderPath!)
let shaderData = try! Data(contentsOf: shaderURL)
do {
let device = MTLCreateSystemDefaultDevice()!
let library = try shaderData.withUnsafeBytes { bytes -> MTLLibrary? in
let dispatchData = DispatchData(bytes: bytes)
return try device.makeLibrary(data: dispatchData as __DispatchData)
}
print(library!.functionNames)
} catch {
print(error.localizedDescription)
}
My Metal shader functions are printed correctly in the console. However, based on my research, it seems like a MTLLibrary
cannot be converted into a SwiftUI ShaderLibrary
.
That is why I am now looking at these two initializers:
ShaderLibrary(url: URL)
ShaderLibrary(data: Data)
Which state: Creates a new Metal shader library from the contents of url/data, which must be the contents of precompiled Metal library. Functions compiled from the returned library will only be cached as long as the returned library exists., which I believe should work for my use case.
However, the problem arises when I run this code:
let shaderPath = Bundle.main.path(forResource: "Shaders", ofType: "metallib")
let shaderURL = URL(fileURLWithPath: shaderPath!)
let library = ShaderLibrary(url: shaderURL)
My app consistently seems to crash on the ShaderLibrary
initialization, rendering the app unusable. Why does ShaderLibrary(url: shaderURL)
cause a crash, even though my .metallib file is valid? Are there additional requirements for loading a ShaderLibrary that I may have missed?
Thank you!
Here's a test SwiftUI View similar to yours, but without the Dynamic Member enum
I encountered the same crash, even though I verified that the passthrough shader existed. However, I decided to test it in Swift Playgrounds instead of Xcode, and surprisingly, it worked! I’m not sure why the shader fails to load in Xcode but runs fine in Swift Playgrounds.
After confirming that it worked in Playgrounds, I reopened the project in Xcode, and the bug reappeared. This makes me fairly certain that the issue stems from differences between Xcode and Swift Playgrounds. Hopefully, this helps others who run into the same problem!