Copyright (c) 2014-2015 The Brenwill Workshop Ltd. All rights reserved.
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.
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.
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.
Open your application in Xcode and select your application's project in the Project Navigator panel.
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:
ALWAYS_SEARCH_USER_PATHS) setting to YES.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.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.)
Repeat the previous step for any Xcode sub-projects that also reference OpenGL API or EAGL header files.
Within Xcode, add the MetalGL framework to your application as follows:
MetalGL.framework in the MetalGL distribution, and click the
Open button.Metal.framework and libc++.dylib from the list of system frameworks.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.).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.
MetalGLShaderConverter-iOS.framework in the MetalGL distribution,
and click the Open button.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.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:
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.
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:
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.
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.
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).
There are three typical platform configuration scenarios under which your application might be running MetalGL:
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.
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.
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.
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.
On a device configured for Scenario 3, setting the value of the MGL_SUPPORT_OPENGL_ON_METAL
compiler build setting to 1 in all projects and sub-projects that you configured to use MetalGL
during installation will cause MetalGL to operate as if it was in Scenario 2.
MetalGL will automatically intercept all OpenGL ES 2.0 calls and direct them to the native
OpenGL implementation. The MGL_SUPPORT_OPENGL_ON_METAL build setting is defined and documented
in the RedirectHeaders/include/MetalGL/mglEnv.h file.
There are several situations where it makes sense to intercept all OpenGL calls that are directed to the native OpenGL implementation, including:
To gain support for combined GLSL+MSL shaders as described in the Metal Shaders section below.
To gracefully handle those OpenGL enumerations that are used by MetalGL but not
OpenGL, in functions such as glEnable() and glDisable(). These enumerations are defined
in the file RedirectHeaders/include/MetalGL/mglEnv.h.
To use Metal in an application that needs to run in 32-bit mode on a 64-bit processor.
If there is some reason why you cannot distribute a binary containing a 64-bit component,
then as long as the device has a 64-bit processor and is running iOS 8 or above, Metal can
still be used, because the processor supports it, even though the binary was compiled for the
32-bit arm7 processor.
Conversely, setting the value of the MGL_SUPPORT_OPENGL_ON_METAL compiler build setting to 0 in
all projects and sub-projects that you configured to use MetalGL during installation
will cause MetalGL to operate as if it was in Scenario 3. All OpenGL ES 2.0 calls will
be directed to the native OpenGL implementation directly, without first passing through the
MetalGL framework. You might do this during application development to test Scenario 3 on
a device that normally supports Scenario 1 or Scenario 2.
On a device configured for Scenario 1, calling glDisable(GL_OPENGL_USE_METAL_MGL), before your
application has made any OpenGL calls, or created an OpenGL context, will cause MetalGL to
operate as if it was in Scenario 2. MetalGL will automatically intercept all OpenGL ES 2.0
calls and direct them to the native OpenGL implementation. You must call
glDisable(GL_OPENGL_USE_METAL_MGL) before your application has made any OpenGL calls or created
an OpenGL context, for example, during application startup.
Not surprisingly, you cannot programmatically change a device that normally operates in Scenario 2 or Scenario 3 to have it operate in Scenario 1, because Scenario 1 requires that both the device and iOS support Metal.
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.
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:
mglext.h - This is your primary interface for configuring the MetalGL environment.
This file contains standard OpenGL enumerations, capabilities, and gl...MGL() extension
functions to configure and access MetalGL capabilities and behaviour in exactly the same
way you interact with OpenGL. In particular, you can use the capability enumerations defined
in this file with the standard OpenGL glEnable() and glDisable() functions to enable or
disable runtime and debugging capabilities within MetalGL. Be sure to familiarize yourself
with the functionality provided by the enumerations, capabilities, and functions in this file.
mglMetalState.h - Contains functions for accessing some of the Metal components used
by MetalGL. By accessing these Metal components, you can augment the OpenGL behaviour
with certain Metal behaviour directly, opening up the option of creating a hybrid
OpenGL-Metal application.
mglDataTypes.h - Contains helpful functions for converting between OpenGL and Metal data types.
mglGLKDataTypes.h - Contains helpful functions for converting between GLKit and Metal data types.
mglEnv.h - Contains foundational environmental configuration, including the version of
MetalGL, and build directives that automatically tell your application how to interact
with MetalGL in various platform scenarios. You generally don't use this
file directly; it is imported by the automatic redirection headers.
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 use the Runtime Shader Conversion feature of MetalGL to automatically and
transparently convert your GLSL shaders to MSL at runtime, by simply loading your GLSL
shaders as you always have, using the standard OpenGL glShaderSource() function. When
running on Metal, MetalGL will detect that the GLSL shaders need to be converted to
MSL, and will convert them automatically. When running on a device that does not support
Metal, MetalGL will simply pass the GLSL source code to the native OpenGL engine.
To make use of this feature, you must have added the MetalGLShaderConverter-iOS.framework
to your application as described in the installation section above.
You can create shader source files that contain both GLSL source code and MSL
source code, and load them using the standard OpenGL glShaderSource() function.
MetalGL will separate the two versions of the shader source code, and automatically
use either the MSL version or the GLSL version, depending on whether the device
supports Metal or not.
This option can be quite useful if you encounter an error when using Runtime Shader Conversion to automatically convert a specific GLSL shader as described in the previous option. You can convert the GLSL source code for that shader to MSL offline, and include both versions when loading that shader.
This option does not require the MetalGLShaderConverter-iOS.framework be added
to your application.
This option does require that MetalGL is intercepting OpenGL calls, as it does when operating in either Scenario 1 or Scenario 2, as described in the Device Platforms and OpenGL System Classes section above.
To identify which section of your combined shader source contains GLSL or MSL,
you can use the following pragma directives within your combined shader source code:
#pragma MetalGL language GLSL
...
#pragma MetalGL language METAL // or #pragma MetalGL language MSL
...
#pragma MetalGL language ALL
...
In each case, any code following one of these directives will be taken to be either GLSL
or MSL source code, or in the case of the ALL directive, will be taken to be usable by
both GLSL and MSL source code. You can flip back and forth between GLSL and MSL code
within your shader by including any number of these directives. The pragma directives
MSL and METAL are synonymous.
Source code following the ALL directive will be accepted as both GLSL and MSL code.
The ALL directive can be useful for demarcating statements like the #ifdef conditional
compilation statement, where you want the #ifdef statement to be included in both the
GLSL and MSL versions of the source code. For example, you might have a complex shader that
includes something like the following:
#pragma MetalGL language ALL
#ifdef USE_LIGHTING
#pragma MetalGL language GLSL
...some GLSL lighting code...
#pragma MetalGL language MSL
...some MSL lighting code...
#pragma MetalGL language ALL
#endif
You can use the standard OpenGL glShaderSource() function to provide your own MSL
shader source code when you know your application is using Metal. To do so, call
glShaderSourceLanguageMGL(GL_SHADER_SOURCE_LANG_METAL) to indicate that source code you submit
to the glShaderSource() function contains only MSL source code. Alternately, you can identify
the shader source as MSL using the pragma directive at the top of your MSL source code:
#pragma MetalGL language METAL
...
This option does not require the MetalGLShaderConverter-iOS.framework be added
to your application.
This option does require that your application is aware of whether Metal is being used,
and is therefore aware of whether it should submit GLSL or MSL shader source code.
You can call glIsEnabled(GL_OPENGL_USE_METAL_MGL) to determine if Metal is being used.
As with other Metal applications, you can create your own MSL shader source code as
.metal files, and compile it offline using Xcode. Using a pre-compiled shader provides
for much faster shader creation at run time. You can use one of two mechanisms to associate
the pre-compiled Metal shader functions with a particular shader:
You can use the glShaderFunctionMGL() function to identify the name of the Metal
shader function to load into a particular shader. This option requires you to modify
your application code to call the glShaderFunctionMGL() function either after,
or (even better) instead of, calling the glShaderSource() and glCompileShader()
functions.
Although it is safe to call the glShaderFunctionMGL() function in addition to calling
the glShaderSource() and glCompileShader() functions, to avoid wasting effort possibly
converting and compiling shader code you won't use, you should either avoid calling the
glShaderSource() and glCompileShader() functions, or cause the source code to be
ignored by calling glShaderSourceLanguageMGL(GL_SHADER_SOURCE_LANG_IGNORE) at least
once prior to any glShaderSource() function calls.
This option does not require the MetalGLShaderConverter-iOS.framework be added
to your application.
You can also use the standard glShaderSource() function to identify the name of the
Metal shader function to load into a particular shader. In this case, the strings that
you submit to the glShaderSource() function do not contain shader source code,
but simply contain the name of a pre-compiled Metal shader function:
glShaderSourceLanguageMGL(GL_SHADER_SOURCE_LANG_METAL_FUNC);
...
...
const GLchar* shSrc = "// This source code just contains the name of \n"
"// a Metal function precompiled by Xcode \n"
"myPrecompiledVertexFunction \n";
glShaderSource(shaderID, 1, &shSrc, NULL);
glCompileShader(shaderID);
In this example, the shader source contains only the name of a Metal shader function
(myPrecompiledVertexFunction) that has been pre-compiled by Xcode. You may also
include optional comments and whitespace. As illustrated in this example, you must call
glShaderSourceLanguageMGL(GL_SHADER_SOURCE_LANG_METAL_FUNC) at least once sometime before
submitting Metal function names this way, so that MetalGL knows that shader source code
you submit with subsequent glShaderSource calls will contain just a Metal function name.
You might choose to use glShaderSource() to submit the names of pre-compiled Metal
functions in those cases where you don't have access to call the glShaderFunctionMGL()
function, but you do have the ability to set the content of the shader source code strings.
Typically this might happen if you are using a third-party library to manage your OpenGL
environment.
This option does not require the MetalGLShaderConverter-iOS.framework be added
to your application.
This option does require that your application is aware of whether Metal is being used,
and is therefore aware of whether to submit only Metal function names as shader source code.
You can call glIsEnabled(GL_OPENGL_USE_METAL_MGL) to determine if Metal is being used.
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.
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.
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:
The tool can be used to convert a single GLSL file, or an entire directory tree of shader files.
The resulting MSL code can be saved to its own file, or can be appended to the GLSL code to create a combined GLSL+MSL source code file, as described in the Metal Shaders section above.
The tool can parse a combined GLSL+MSL shader file, and re-convert the GLSL component.
The name of the resulting MSL shader function can be supplied or auto-generated.
The MSL source code can be output to .metal files, to be compiled by Xcode into a
default Metal shader library, whose functions can be accessed from within your application
using the glShaderFunctionMGL() function.
To see a complete list of options, run the MetalGLShaderConverter tool from the command
line with no arguments.
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:
Errors encountered during Runtime Shader Conversion are appended to the
information log of the shader. You can retrieve this log using the standard
OpenGL glGetShaderInfoLog() function.
To help understand conversion issues during Runtime Shader Conversion, you can enable the logging of the GLSL and MSL shader source code during conversion as follows:
glEnable(GL_LOG_SHADER_CONVERSION_SOURCE_CODE_MGL);
You can call this function any time after the OpenGL context has been created, and prior to any shader conversion activity.
For minor issues, you may be able to adjust your GLSL code so that it behaves the same under OpenGL, but is easier to automatically convert to MSL.
For more significant issues, you can use the MetalGLShaderConverter tool to convert the
shaders at development time, adjust the MSL code manually so that it compiles correctly,
and create a combined GLSL+MSL shader code that can be loaded without changes to
your application code.
You can use the MetalGLShaderConverter tool to convert the shaders to .metal files
at development time, fix any issues with the MSL code, and compile the MSL shaders
into a default Metal shader library, whose functions can be accessed from within your
application using the glShaderFunctionMGL() function.
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.
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.
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:
You can modify your application, and your GLSL fragment shader code, to use individual texture samplers, as follows:
uniform sampler2D myTextureA;
uniform sampler2D myTextureB;
and pass the value of each sampler uniform in a separate call to glUniform1i or glUniform1iv.
This option mimics the calling form of the MSL shaders, and allows you to write your shaders
once in GLSL and then seamlessly convert them to MSL using either Runtime Shader
Conversion or the MetalGLShaderConverter Shader Converter Tool.
If you are not able to (or simply don't want to) modify the way your OpenGL application
passes an array of texture samplers to the glUniform1iv function, MetalGL allows you
to modify your MSL code instead, to declare the individual MSL textures so that
MetalGL will treat them as elements in an array, and will reference those individual
texture arguments when your OpenGL application calls the glUniform1iv function with
an array of texture samplers.
To allow this, in your MSL code, declare each of your texture arguments with a special suffix that indicates the index of the element in the texture sampler array:
fragment FragOutStruct myFragmentShader(FragInStruct fragIn [[stage_in]],
texture2d<half> myTextures_0_ [[texture(1)]], sampler mySampler0 [[sampler(1)]]
texture2d<half> myTextures_1_ [[texture(2)]], sampler mySampler1 [[sampler(2)]])
Notice the suffix in the form _N_ attached to the names of each of the texture2d arguments
(and notice also that you do not need to do this for the associated sampler arguments).
This special suffix indicates to MetalGL that these two texture2d arguments should be
treated as a single GLSL uniform declared as an array of type texture2D, of length 2,
and named myTextures. This last point, the uniform name, is important to keep in mind when
requesting the location of the uniform using the glGetUniformLocation function.
The numbers you assign in the suffixes must be consecutive and start at zero. And notice
from the above example that the numbers do not have to agree with the Metal texture
indexes you assign (eg- [[texture(2)]]). This means that you can combine a texture
array with other discrete texture declarations in your MSL code.
To reference these variables within your MSL code, use the names as they are declared
in the MSL function declaration (myTextures_0_, myTextures_1_).
Since this option requires you to 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.
The MetalGLShaderConverter Shader Converter Tool can help you
create the MSL code in the form needed by this option. Create a copy of your GLSL shader,
and modify the declaration of the myTextures uniform from:
uniform sampler2D myTextures[2];
to:
uniform sampler2D myTextures_0_;
uniform sampler2D myTextures_1_;
and change all references to the array elements from:
...texture2D(myTextures[1], texCoords)...
to:
...texture2D(myTextures_1_, texCoords)...
When you run the MetalGLShaderConverter tool, it will populate the Metal shader function
arguments as described above for use as array elements. Finally, remember that, when running
under OpenGL, do not use the GLSL code that you modified here for this special conversion.
You should use the original unmodified GLSL code to allow it to continue to accept the
texture samplers as an array.
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.
This section discusses how MetalGL helps improve your game or application performance, and what results you should expect to see.
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.
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.
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.
The following is a list of known limitations in this version of MetalGL:
Multi-sampling has not yet been implemented.
Shader conversion (via either Runtime Shader Conversion or the MetalGLShaderConverter
tool) fails to convert GLSL shaders that use multiple textures passed as an array, or to
distinguish textures that sample depth content. See the section above for
assistance with handling texture arrays and depth content in shaders.
MetalGL supports GLKView and GLKController, but does not provide support for other
GLKit components such as effects and the texture loader.
Vertex attributes of type GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, or GL_UNSIGNED_SHORT
that have only a single element per vertex cannot be the last component of a vertex, unless
there is room at the end of the vertex structure for a duplicate element of that type.
This is because Metal does not support those vertex attribute types as single elements,
and the vertex content must be read as a two-element format. For example, a vertex attribute
that is defined as a single GL_SHORT will be read as two GL_SHORT values, and the value
of the second element will be ignored in the shader. This does not cause any problems if
that attribute is positioned in the middle of the vertex structure. However, if that
attribute is positioned at the end of the vertex structure, it is possible that reading
the attribute will cause the vertex stride to be breached, and an error will occur.
Try to arrange that any single-element vertex attributes of these types are not positioned
at the end of your vertex content, or pad your vertex stride accordingly (for example, with
4-byte alignment).
If you need to run MetalGL on a 64-bit device with iOS 7 or earlier, you should avoid
adding custom categories to either of the system classes GLKView or CAEAGLLayer. See the
Device Platforms and OpenGL System Classes section above for more information.
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.