MetalGL 0.9.0 ReadMe

Copyright (c) 2014-2015 The Brenwill Workshop Ltd. All rights reserved.

Table of Contents

MetalGL License Agreement

CAREFULLY READ THE MetalGL LICENSE AGREEMENT, FOUND IN THIS MetalGL DISTRIBUTION PACKAGE. BY INSTALLING OR OTHERWISE USING THIS MetalGL DISTRIBUTION PACKAGE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS AND CONDITIONS OF THE MetalGL LICENSE AGREEMENT. IF YOU DO NOT ACCEPT THE TERMS AND CONDITIONS OF THE MetalGL LICENSE AGREEMENT, DO NOT INSTALL OR USE THIS MetalGL DISTRIBUTION PACKAGE.

About MetalGL

MetalGL is an implementation of the OpenGL ES 2.0 API, that runs on Apple's Metal graphics framework. MetalGL provides many of the performance benefits of the Metal framework, while maintaining compliance with the proven OpenGL ES 2.0 API.

Applications built on OpenGL ES 2.0, can use MetalGL to provide additional graphic performance, without having to abandon the familiar OpenGL ES 2.0 API, or rewrite rendering logic and shaders for different platforms.

MetalGL automatically detects whether the device your application is running on supports Metal, and will use the Metal framework where available. When running on a device that does not support Metal, MetalGL will automatically downshift, and transparently make use of the native OpenGL ES 2.0 rendering engine.

Metal uses a different shading language, the Metal Shading Language (MSL), than OpenGL ES 2.0, which uses the OpenGL Shading Language (GLSL). However, fear not, as MetalGL will automatically convert your GLSL shaders to their MSL equivalents. This can be done transparently at run time, using the Runtime Shader Conversion feature of MetalGL, or at development time using the MetalGLShaderConverter tool provided with MetalGL.

Installing MetalGL in Your OpenGL ES 2.0 Application

Installation of MetalGL is straightforward and easy! To add MetalGL to your OpenGL ES 2.0 application, follow the steps in this section. If you're new to MetalGL, it is recommended that you start with a smaller project to help you understand the transition, and to reduce the possibility of needing to make modifications to your shaders to ensure their compatibility with the Metal environment.

  1. Open your application in Xcode and select your application's project in the Project Navigator panel.

  2. MetalGL intercepts and redirects your application's OpenGL API calls to functionality within MetalGL. To enable this, you need to make a few changes to the Build Settings in your Xcode project, as follows:

    1. In the Project Navigator panel, select your application's project, and open the Build Settings tab.
    2. Set the Always Search User Paths (aka ALWAYS_SEARCH_USER_PATHS) setting to YES.
    3. In the User Header Search Paths (aka USER_HEADER_SEARCH_PATHS) setting, add an entry that points to the RedirectHeaders/include folder, found in the MetalGL distribution, and mark it as recursive. Be sure that you have not included the RedirectHeaders/orig folder.
    4. Set the Enable Modules (C and Objective-C) (aka CLANG_ENABLE_MODULES) setting to NO. Modules interfere with the ability to redirect the OpenGL API references.

    (Alternately, you can make the above changes to the Xcode project targets that reference OpenGL API or EAGL header files.)

  3. Repeat the previous step for any Xcode sub-projects that also reference OpenGL API or EAGL header files.

  4. Within Xcode, add the MetalGL framework to your application as follows:

    1. In the Project Navigator panel, select your application's project, select the Build Phases tab, and open the Link Binary With Libraries list.
    2. Click the + button, and in the dialog window that opens, click the Add Other... button. Locate MetalGL.framework in the MetalGL distribution, and click the Open button.
    3. In the Link Binary With Libraries list, click the + button again, and add Metal.framework and libc++.dylib from the list of system frameworks.
    4. Because of the need to set the Enable Modules (C and Objective-C) (aka CLANG_ENABLE_MODULES) build setting to NO (see above), the Link Frameworks Automatically (aka CLANG_MODULES_AUTOLINK) build setting will no longer have any effect. If you have not already done so, in the Link Binary With Libraries list, ensure that you have added any other system frameworks that your application uses (e.g. OpenGLES, UIKit, GLKit, QuartzCore, etc.).
  5. If you are just starting out, and want to make use of Runtime Shader Conversion to transparently convert your OpenGL shaders to Metal shaders at runtime, you can add the MetalGL Shader Converter framework to your application as follows. If you already have Metal versions of your shaders, you can skip this step.

    1. In the Project Navigator panel, select your application's project, select the Build Phases tab, and open the Link Binary With Libraries list.
    2. Click the + button, and in the dialog window that opens, click the Add Other... button. Locate MetalGLShaderConverter-iOS.framework in the MetalGL distribution, and click the Open button.
    3. In the Build Settings tab, ensure that the Other Linker Flags (aka OTHER_LDFLAGS) setting includes an entry for -ObjC. Since the shader converter framework is optional and weakly-linked, this entry ensures Xcode will add the MetalGLShaderConverter-iOS.framework to the application during the link phase.
  6. When a Metal app is running from Xcode, the default Scheme settings reduce performance. To improve performance and gain the benefits of Metal, perform the following in Xcode:

    1. Open the Scheme Editor for building your main application. You can do this by selecting Edit Scheme... from the Scheme drop-down menu, or select Product -> Scheme -> Edit Scheme... from the main menu.
    2. On the Info tab, set the Build Configuration to Release, and disable the Debug executable check-box.
    3. On the Options tab, disable both the Metal API Validation and GPU Frame Capture options. For optimal performance, you may also consider disabling the other simulation and debugging options on this tab. For further information, see the Xcode Scheme Settings and Performance section of Apple's Metal Programming Guide documentation.
  7. Build and run your application.

You can tell whether your application is now using Metal by checking the logs for an entry similar to the following:

[mgl-info] OpenGL functionality using Metal provided by MetalGL 0.9.0 (build X). 

You can also verify that your application is now using Metal by Using Xcode to capture a GPU frame.

If you find that your application is not running Metal, the first thing you should do is verify that the device supports Metal. Metal requires iOS 8 running on a device with at least a 64-bit A7 processor. Then check that the path to the RedirectHeaders/include folder that you configured in the User Header Search Paths (aka USER_HEADER_SEARCH_PATHS) build setting is correct, and has been marked recursive. If it is not accurate, redirection of the OpenGL API calls into MetalGL functionality will not occur, and your application will continue to use the native OpenGL libraries.

Activating Your MetalGL License

MetalGL is provided under a commercial paid license. You must purchase licenses covering the MetalGL features you are using before releasing MetalGL as part of a production game or application.

During evaluation, you can run MetalGL without purchasing a license. The same MetalGL distribution can be used for both evaluation and production games and applications, and the features and performance are identical in both modes. During evaluation, you will see the MetalGL logo displayed as a watermark overlay on your OpenGL scene. Once valid licenses have been purchased and activated to cover the MetalGL features you are using, this watermark will disappear.

Licenses can be purchased for one or more MetalGL feature sets. Depending on whether you purchased a single license that covers all the features you are using, or purchased individual licenses for each features set, you will need to activate one or more licenses within MetalGL.

Each license is composed of two parts, a license ID and a license key, both of which are provided to you when you purchase the license. There are two ways to activate a license within MetalGL:

  1. The preferred method is to enter your license ID and key as compiler build settings in your development environment, and call the glActivateMetalGLLicensesMGL() function to activate them. If you have multiple licenses, covering multiple MetalGL feature sets, you can configure up to four licenses using the following pairs of build settings:

     MGL_LICENSE_ID   and MGL_LICENSE_KEY
     MGL_LICENSE_ID_1 and MGL_LICENSE_KEY_1
     MGL_LICENSE_ID_2 and MGL_LICENSE_KEY_2
     MGL_LICENSE_ID_3 and MGL_LICENSE_KEY_3
    

    Each element of each pair is a single string defined as a build setting, and should not include quote marks. For example, you might configure the following build settings:

     MGL_LICENSE_ID=john.doe@example.com
     MGL_LICENSE_KEY=NOVOT3NGHDZ6OQSCXX4VYNXGI3QLI6Z6
    

    and if you purchase an additional feature set on a separate license, you can add a second pair of build settings:

     MGL_LICENSE_ID_1=john.doe@example.com
     MGL_LICENSE_KEY_1=MZ4T1Y2LDKBJHAL73JPOEJBHELRHEQJF
    

    In addition to the license ID and key, for any license activation to take place, you must also set the following build setting to indicate that you accept the terms and conditions of the MetalGL License Agreement:

     MGL_LICENSE_ACCEPT_TERMS_AND_CONDITIONS=1
    

    You can call the glActivateMetalGLLicensesMGL() function at any time, but typically you will call it during application startup.

  2. If you are unable to use build settings to enter license information, you can call the glActivateMetalGLLicenseMGL(licenseID, licenseKey, acceptLicenseTermsAndConditions) function from within your application, passing a license ID and key directly as a pair of null-terminated strings, as well as a boolean affirmation that you accept the terms and conditions of the MetalGL License Agreement.

    You can call this function at any time, before or after the OpenGL context is created, but typically you will call this function during application startup. You can call this function multiple times to accommodate licenses purchased for multiple individual feature sets. Until a valid license is applied covering each feature set used by your application, MetalGL will operate in evaluation mode.

    Using the glActivateMetalGLLicenseMGL() function is not the preferred method for activating licenses because, in a team environment, it is more difficult to enter valid licenses for each developer from your application code. Instead, consider using the glActivateMetalGLLicensesMGL() function (discussed above), which allows you to specify the license information through compiler build settings. Using compiler build settings allows you to more easily specify the license information for each developer.

If your MetalGL license is part of a multi-user pack, you must verify the user count with your license purchaser or administrator.

Once you have activated one or more licenses to cover the MetalGL features you are using, an information message will appear in the console logs for each activated feature set:

[mgl-info] Activated MetalGL OpenGL ES 2.0 Core license for 'john.doe@example.com'.
[mgl-info] Activated MetalGL OpenGL ES 2.0 Extensions license for 'john.doe@example.com'.

and the MetalGL logo watermark will no longer be displayed on top of your OpenGL scene. If the watermark remains, ensure that you have indicated acceptance of the terms and conditions of the MetalGL License Agreement, as described above, and check the console logs to ensure that your license covers all of the MetalGL features that you are using, as described in the following sub-section.

Determining Which MetalGL Feature Sets You Are Using

Before acquiring a MetalGL license, you can tell which MetalGL features sets are being used by your application by searching the console logs for entries such as the following:

[mgl-info] You require a MetalGL license for the OpenGL ES 2.0 Core feature set.
[mgl-info] You require a MetalGL license for the OpenGL ES 2.0 Extensions feature set...

In the case of the Extensions feature set, the logged message will also include an indication of the name of the first OpenGL extension that was used by your application, and the first function that was called in using that extension. This will help you understand which MetalGL features you will need to acquire a license for.

Once you have entered one or more valid MetalGL licenses, as described above, these log messages will no longer appear for the feature sets covered by the license(s).

Device Platforms and OpenGL System Classes

There are three typical platform configuration scenarios under which your application might be running MetalGL:

  1. iOS 8 (or above) on a device with an A7 processor (or above):

    These devices use a 64-bit processor and have a GPU that supports Metal. Under this scenario, Metal is available. MetalGL will automatically intercept all OpenGL ES 2.0 calls and redirect them to the MetalGL libraries, where all OpenGL ES 2.0 functionality will be implemented using Metal.

  2. iOS 7 (or earlier) on a device with an A7 processor (or above):

    Although these devices use a 64-bit processor and have a GPU that supports Metal, the Metal framework is only available with iOS 8 or above. Under this scenario, Metal is currently unavailable, but it will be available in the future when the user upgrades to iOS 8 (or above). MetalGL will automatically intercept all OpenGL ES 2.0 calls and direct them to the native OpenGL implementation. When the user upgrades to iOS 8, MetalGL will then follow Scenario 1 above and redirect all OpenGL ES 2.0 calls to the MetalGL libraries, where all OpenGL ES 2.0 functionality will be implemented using Metal.

  3. Any iOS version on a device with an A6 processor (or earlier):

    These devices use the arm7 processor, and have a GPU that cannot support Metal. Under this scenario, Metal will never be available. When building for an arm7 architecture, all OpenGL ES 2.0 calls will be directed to the native OpenGL implementation directly, without first passing through the MetalGL framework. In this scenario, your OpenGL application runs exactly as it does without MetalGL.

You do not need to do anything to support these various scenarios. MetalGL handles them all automatically.

Modifying the Scenarios

If needed, you can programmatically modify the scenario configurations above. For example, because Scenario 3 completely bypasses MetalGL, it does not support the combined GLSL+MSL shaders described in the Metal Shaders section below, so you may want to run a device that would normally operate in Scenario 3 as if it was operating in Scenario 2 instead. Or during development, you may want to test your application under both Scenario 2 or Scenario 3 on a device that normally supports Scenario 1.

OpenGL and GLKit System Classes

In order to correctly intercept OpenGL behaviour, under Scenarios 1 and 2 above, MetalGL automatically replaces the OpenGL system classes EAGLContext, EAGLSharegroup, CAEAGLLayer and GLKView with classes that operate the same way, but implement their behaviour using Metal instead of OpenGL (on iOS 8 and above).

In most cases, this replacement is completely transparent to your application under all three scenarios above. However, in order for your application to operate as expected in Scenario 2 (on a 64-bit device with iOS 7 or earlier), you should avoid adding custom categories to either of the classes GLKView or CAEAGLLayer. Any custom categories you add to GLKView or CAEAGLLayerwill be ignored in Scenario 2. If you want to add custom functionality to GLKView, you should do so be creating and using a custom subclass, instead of adding a custom category to GLKView itself. Your custom subclass will operate correctly in all three of the scenarios above.

You can use GLKView and GLKViewController in an Xcode Storyboard, by declaring a subclass of GLKView (e.g. MyGLKitView) and using it in your Storyboard. You must declare and use a subclass of GLKit so that MetalGL can automatically replace the GLKView superclass with a version that is compatible with Metal, when the class is dynamically loaded as part of the Storyboard.

Support for GLKView is provided as a convenience for OpenGL applications that use GLKView in Storyboards, or simply as an easy way to create a view that is compatible with OpenGL rendering. Be aware that MetalGL does not yet support other GLKit features and components, such as effects or the texture loader.

Interacting with the MetalGL Runtime

You programmatically configure and interact with the MetalGL runtime through function calls, enumeration values, and capabilities, in exactly the same way you do with OpenGL. The RedirectHeaders/include/MetalGL folder contains several header files that define access to MetalGL configuration, capabilities, behaviour, and components:

Metal Shaders

Metal uses a different shader language than OpenGL. OpenGL uses the well-known OpenGL Shading Language (GLSL), whereas Metal uses the new Metal Shading Language (MSL).

MetalGL provides several options for creating and running MSL versions of your existing GLSL shaders. The following options are presented in order of increasing sophistication and difficulty:

You can mix and match these options in your application. For example, a simple approach is to use Runtime Shader Conversion for most GLSL shaders, and provide combined GLSL+MSL shader source code for the odd GLSL shader that prove problematic for runtime conversion.

Metal Shader Texture Coordinates

One key difference between MSL and GLSL is the orientation of the texture coordinate system. In OpenGL (and GLSL), texture coordinates start at the lower-left corner of the texture and increase towards the upper-right corner. In Metal (and MSL), texture coordinates start at the upper-left corner of the texture and increase towards the lower-right corner.

In effect, you can think of the Y-axis of the texture coordinates of Metal as being "flipped", or "inverted", relative to the Y-axis of OpenGL. To compensate for this, when writing an MSL shader, you should use (1.0 - y) wherever you need to specify the Y-value of a texture coordinate. As an example, in an MSL shader, you should sample a texture as follows:

half4 texColor = myTex.sample(mySampler, (float2)(v_texCoord.x, (1.0 - v_texCoord.y)));

When using Runtime Shader Conversion or the MetalGLShaderConverter Shader Converter Tool, this is handled for you automatically. However, if you need to write your own MSL shaders (or hand-tweak the automatically converted GLSL shaders), be sure to invert the Y-value when using texture coordinates in MSL.

Further considerations regarding differences between MSL and GLSL are discussed in the Troubleshooting Shader Conversion section below.

MetalGLShaderConverter Shader Converter Tool

The MetalGL distribution includes the MetalGLShaderConverter command line tool, which allows you to convert your GLSL shader source code to MSL at development time, and then supply the MSL code to MetalGL using one of the methods describe in the Metal Shaders section above.

The MetalGLShaderConverter tool uses the same conversion technology as the Runtime Shader Conversion feature of MetalGL.

The MetalGLShaderConverter tool has a number of options available from the command line:

To see a complete list of options, run the MetalGLShaderConverter tool from the command line with no arguments.

Troubleshooting Shader Conversion

The shader converter technology in MetalGL is quite robust, and most GLSL shaders can be converted to MSL without any problems. In the case where a conversion issue arises, you can address the issue as follows:

The following sub-sections describe a few rare situations where the design features of the respective shading languages make automatic shader conversion difficult, and describe what you can do to correct it.

GLSL Preprocessor Directives

Shader conversion (via either Runtime Shader Conversion or the MetalGLShaderConverter tool) resolves all GLSL preprocessor directives (eg- #define and #ifdef...#endif) prior to conversion to MSL. This can sometimes be problematic, particularly for offline shader conversion, if you depend on the directives being dynamically set prior to compilation.

Using Texture Arrays in Shaders

GLSL permits uniforms of type sampler2D and samplerCube to be declared and used as arrays. For example, the following could be used to declare a two-element array of texture samplers in GLSL:

uniform sampler2D myTextures[2];

On the other hand, MSL requires that all textures (and their associated samplers) be declared individually, as part of the Metal shader function argument list. For example, the following could be used to declare the same two texture samplers in MSL:

fragment FragOutStruct myFragmentShader(FragInStruct fragIn [[stage_in]],
    texture2d<half> myTextureA [[texture(1)]], sampler mySampler0 [[sampler(1)]]
    texture2d<half> myTextureB [[texture(2)]], sampler mySampler1 [[sampler(2)]])

A potential problem arises therefore, if the OpenGL application using the shaders expects to set the value of the texture sampler uniform as an array using a single call to the OpenGL function glUniform1iv.

With MetalGL, if your OpenGL application uses an array of texture samplers, you have a couple of ways around this issue:

Sampling Depth Textures in Shaders

Most of the time, your textures contain visible content. However, there are certain post-processing situations where you might want to sample from a texture that contains depth information. Whereas GLSL does not distinguish between sampling visible or depth texture content, MSL requires you to declare a different type of texture when using depth content.

During normal shader conversion, a GLSL texture sampler declared as follows:

uniform sampler2D myTexture;

will typically be automatically converted to this texture2d MSL shader declaration:

fragment FragOutStruct myFragmentShader(FragInStruct fragIn [[stage_in]],
    texture2d<half> myTexture [[texture(1)]], sampler mySampler [[sampler(1)]])

However, under MSL, when sampling a depth texture, the equivalent texture declaration must be depth2d:

fragment FragOutStruct myFragmentShader(FragInStruct fragIn [[stage_in]],
    depth2d<float> myTexture [[texture(1)]], sampler mySampler [[sampler(1)]])

Since automatic shader conversion has no way of knowing that you intend to put depth content into the texture, you will need to manually edit the resulting MSL shader source code to change the texture declaration to indicate depth content. Because you must modify your MSL code, you cannot make use of Runtime Shader Conversion, but you can use one of the other techniques listed above, to load the MSL code into your application when it is running Metal.

Performance Considerations

This section discusses how MetalGL helps improve your game or application performance, and what results you should expect to see.

Call latency

Metal has a much thinner API layer than OpenGL, providing lower latency for each draw call. In addition, Metal enforces a strict organization to how you construct the graphics context state used by each draw call, effectively forcing you to pre-configure your graphics context states. This is in contrast to the wildly free-form state construction available in OpenGL, which offers flexibility but, like any dynamic system, is less efficient than a fixed alternative.

Taken together, when compared with OpenGL, the CPU load for each draw call is much less with Metal, meaning that you can make many more draw calls within a given frame time. The result is the ability to render much more content per frame.

MetalGL provides a comprehensive, lightweight, and highly-optimized bridge between the free-form state construction of OpenGL and the strict state management of Metal, while retaining much of the call latency improvements available through Metal. With MetalGL you can perform over three times the number of OpenGL draw calls, compared with native OpenGL ES 2.0, effectively tripling the amount of content you can draw in each frame.

Keep in mind that MetalGL can only help you reduce the time spent making OpenGL calls. It cannot help you reduce the time your app spends outside the OpenGL calls. An effective performance improvement strategy should involve an effort to streamline your application logic so that OpenGL calls are the blocking factor to further improvements, at which point MetalGL will help you take your OpenGL game or application performance to the next level.

Shader Loading Time

Metal supports pre-compiled shaders, which can improve shader loading and set-up performance, allowing you to reduce your scene loading time. See the Metal Shaders and MetalGLShaderConverter Shader Converter Tool sections above for more information about how to use the MetalGLShaderConverter Shader Converter Tool to create and load pre-compiled Metal shaders into MetalGL.

Xcode Configuration

When a Metal app is running from Xcode, the default Scheme settings reduce performance. Be sure to follow the instructions for configuring your application's Scheme within Xcode, found in the in the installation section above.

Known MetalGL Limitations

The following is a list of known limitations in this version of MetalGL:


MetalGL uses technology from the open-source GLSL optimizer and Mesa projects to perform GLSL to MSL shader conversion during Runtime Shader Conversion and in the MetalGLShaderConverter tool.

MetalGL uses technology from the open-source PVRTCCompressor project to help manage compressed PVRTC textures.