Repository: google/filament
File: filament/backend/src/metal/MetalShaderCompiler.mm (Objective-C++)

Code before:
    return mMode == Mode::ASYNCHRONOUS;
}

/* static */ MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::compileProgram(
        const Program& program, id<MTLDevice> device) {
    std::array<id<MTLFunction>, Program::SHADER_TYPE_COUNT> functions = { nil };
    const auto& sources = program.getShadersSource();
    for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
        const auto& source = sources[i];
        // It's okay for some shaders to be empty, they shouldn't be used in any draw calls.
        if (source.empty()) {
            continue;
        }

        NSError* error = nil;
        id<MTLLibrary> library = nil;
        switch (program.getShaderLanguage()) {
            case ShaderLanguage::MSL: {
                MTLCompileOptions* options = [MTLCompileOptions new];
                options.fastMathEnabled = YES;
                // The docs for MTLCompileOptions say that Metal uses the most
                // recent language version if it is not specified, but this is
                // not always true: it is possible for the environment to be set
                // up in a way where an older version is used by default. So we
                // explicitly set the minimum version we require.
                options.languageVersion = MTLLanguageVersion2_3;

                assert_invariant(source[source.size() - 1] == '\0');
                // the shader string is null terminated and the length includes the null character
                NSString* objcSource = [[NSString alloc] initWithBytes:source.data()
                                                                length:source.size() - 1
                                                              encoding:NSUTF8StringEncoding];
                library = [device newLibraryWithSource:objcSource options:options error:&error];
                break;
            }
            case ShaderLanguage::METAL_LIBRARY: {
                dispatch_data_t data = dispatch_data_create(source.data(), source.size(),
                        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
                        DISPATCH_DATA_DESTRUCTOR_DEFAULT);
                library = [device newLibraryWithData:data error:&error];
                break;
            }
            case ShaderLanguage::ESSL1:
            case ShaderLanguage::ESSL3:
            case ShaderLanguage::SPIRV:
            case ShaderLanguage::WGSL:
            case ShaderLanguage::UNSPECIFIED:
                break;
        }

        if (library == nil) {

Review comment (suggestion):
I think it's good to be explicit with the language version here, but this should match what we pass to spirv-cross while compiling materials:

https://github.com/google/filament/blob/ad27d48fd309e1e8940e1a23a95ec992f07c481c/libs/filamat/src/GLSLPostProcessor.cpp#L359-L369

I think it's safe to go with MSL 2.0 on mobile, and MSL 2.3 on desktop. You can use a `#if defined(FILAMENT_IOS)` check here.

Code after:
    return mMode == Mode::ASYNCHRONOUS;
}

/* static */ MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::compileProgram(
        const Program& program, id<MTLDevice> device) {
    std::array<id<MTLFunction>, Program::SHADER_TYPE_COUNT> functions = { nil };
    const auto& sources = program.getShadersSource();
    for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
        const auto& source = sources[i];
        // It's okay for some shaders to be empty, they shouldn't be used in any draw calls.
        if (source.empty()) {
            continue;
        }

        NSError* error = nil;
        id<MTLLibrary> library = nil;
        switch (program.getShaderLanguage()) {
            case ShaderLanguage::MSL: {
                MTLCompileOptions* options = [MTLCompileOptions new];
                options.fastMathEnabled = YES;
                // The docs for MTLCompileOptions say that Metal uses the most
                // recent language version if it is not specified, but this is
                // not always true: it is possible for the environment to be set
                // up in a way where an older version is used by default. So we
                // explicitly set the minimum version we require.
                #if defined(FILAMENT_IOS)
                    options.languageVersion = MTLLanguageVersion2_0;
                #else
                    if (@available(macOS 11.0, *)) {
                        options.languageVersion = MTLLanguageVersion2_3;
                    } else {
                        options.languageVersion = MTLLanguageVersion2_2;
                    }
                #endif

                assert_invariant(source[source.size() - 1] == '\0');
                // the shader string is null terminated and the length includes the null character
                NSString* objcSource = [[NSString alloc] initWithBytes:source.data()
                                                                length:source.size() - 1
                                                              encoding:NSUTF8StringEncoding];
                library = [device newLibraryWithSource:objcSource options:options error:&error];
                break;
            }
            case ShaderLanguage::METAL_LIBRARY: {
                dispatch_data_t data = dispatch_data_create(source.data(), source.size(),
                        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
                        DISPATCH_DATA_DESTRUCTOR_DEFAULT);
                library = [device newLibraryWithData:data error:&error];
                break;
            }
            case ShaderLanguage::ESSL1: