The approach suggested by the questioner may not work (hence the lack of sample code). A metal shader (.metal) is just a set of functions; it does not create MTLLibrary (.metallib). Here is the working code that compiles the Metal shader from an array of characters ( const char * ) (does not match NSString ); it is followed by instructions to convert the .metal file to a .metallib file before execution.
Compiling a metal shader at run time
The following example can also be used to provide users with a shader editor, and it allows you to update only part of the shader of your application, without requiring the user to update the entire application:
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); }
The following are excerpts from the Apple Developer Documentation; although the information is relatively elementary, use it as the basis for a common structure that you and your audience will share when communicating about your subject.
Creating libraries during application build
The accepted answer is categorically incorrect for the same reasons; and it argues that performance trade-offs are dubious. Here is the only definite statement that can be made about compiling Metal shaders and creating Metal libraries, and then real code:
Functions and Libraries
This chapter describes how to create an MTLFunction object as a reference to a Shader or Metal computational function, and how to organize and access functions using an MTLLibrary object.
MTLFunction Represents a shader or computational function.
The MTLFunction object is the only function that is written in the Metal shading language and runs on the GPU as part of a graphics or computing pipeline. For more information about the language of metallic shading, see the Guide to the language of metallic shading.
To transfer data or state between the Metal runtime and a graphic or computational function written in the Metal shading language, you assign an argument index to textures, buffers, and samplers. The argument index determines which texture, buffer, or sampler is referenced by both the Metal execution code and the Metal shader code.
To render, you specify the MTLFunction object to use as a vertex or fragment shader in the MTLRenderPipelineDescriptor object, as described in detail in the section Creating the state of the rendering pipeline. To pass the calculations, you specify the MTLFunction object when creating the MTLComputePipelineState object for the target device, as described in the "Specify the calculation status and resources for the calculation command encoder" section.
A library is a repository of functions
An MTLLibrary object represents the repository of one or more MTLFunction objects. One MTLFunction object represents one Metal function, which was written in a shading language. In the source code of the Metal shading language, any function that uses the Metal function specifier (vertex, fragment, or kernel) can be represented by an MTLFunction object in the library. A Metal function without one of these function qualifiers cannot be directly represented by an MTLFunction object, although it can be called by another function in a shader.
MTLFunction objects in the library can be created from any of the following sources:
- The metal shading language code that was compiled into the binary library during the build process of the application.
- A text string containing the source code for the Metal shader language, which is compiled by the application at run time.
Compiling the source files in the shader language and creating the library (.metallib file) during the build process of the application provides better application performance than compiling the shader source code at run time. You can create the library in Xcode or using command line utilities.
Using Xcode to create a library
Any source shader files that are in your project are automatically used to create a default library, which can be accessed from the Metal platform code using the newDefaultLibrary MTLDevice method.
Using command line utilities to create a library
Figure 8-1 shows the command line utilities that form the compiler toolchain for the metal shader source code. When you include .metal files in your project, Xcode calls these tools to create a library file that you can access in your application at runtime.
To compile the shader source code into a library without using Xcode:
- Use the metal tool to compile each .metal file into a single .air file, which stores the intermediate representation (IR) of the shader language code.
- If desired, you can use the metal-ar tool to archive multiple .air files together into a single .metalar file. (metal-ar is similar to Unix ar.)
- Use the metallib tool to create the Metal.metallib library file from IR.air files or from .metalar archive files.
Example: creating a library file using command line utilities
xcrun -sdk macosx metal MyLibrary.metal -o MyLibrary.air xcrun -sdk macosx metallib MyLibrary.air -o MyLibrary.metallib
To access the resulting library in the platform code, call the 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); }