Search code examples
ioscobjective-cmetal

Sharing code between C/C++/Objective-C and Metal


I know it's possible to declare custom structs in Obj-C header file, and reuse this one declaration in both Metal shaders as well as in Obj-C/Swift host code. Now I was thinking about reusing the small helper function in both Obj-C/Swift and my Metal shaders. After many attempts I was able to do that, but with many limitations and strange compiler quirks.

This is the C-function declared and defined in Objective-C bridging header:

#include <simd/simd.h>

extern inline simd_ushort2 test(simd_float2 pos) {
    const float H = 16.0;
    return (simd_ushort2){
        (ushort)(pos.x/H),
        (ushort)(pos.y/H)
    };
}

This function works in both Swift and Metal shader. But it doesn't work in Metal if I define it in implementation file instead of the header. So it seems like Metal can only operate with C-functions defined in the header? Also for some reason this doesn't work without extern keyword, which to my understanding doesn't do anything for functions defined in header.

Then if I want to do some more math in this function, like using ceilf, for example...

#include <simd/simd.h>
#include <math.h>

extern inline simd_ushort2 test(simd_float2 pos) {
    const float H = 16.0;
    return (simd_ushort2){
        (ushort)ceilf(pos.x/H),
        (ushort)ceilf(pos.y/H)
    };
}

...it doesn't compile in Metal, spawning countless errors related to importing <math.h> like 'double' is not supported in Metal

So it seems to me the scale of incompatibility between Metal and even the plain C reaches the scale of even the most basic math operations. Am I right that trying to share implementation between Metal and host-code is bad idea, and it's better to limit sharing to struct declarations and some global constants if needed?


Solution

  • You can't include standard library headers in the Metal shader files. Section 1.4.4 of Metal Shading Language Specification says

    Do not use the C++ standard library in Metal code. Instead, Metal has its own standard library, as discussed in section 5 of this document.

    That doesn't stop you from including it in the Objective-C code. What you should do is include the right header conditionally based on where the header is included or for which "platform" the source file is compiled.

    So, in your case it would look like this:

    #ifdef __METAL_VERSION__
    #include <metal_stdlib>
    using namespace metal;
    #else
    #include <math.h>
    #endif
    
    #include <simd/simd.h>
    
    extern inline simd_ushort2 test(simd_float2 pos) {
        const float H = 16.0;
        return (simd_ushort2){
            (ushort)ceilf(pos.x/H),
            (ushort)ceilf(pos.y/H)
        };
    }