OpenGL ES 2.0 Shader Programs

The OpenGL ES 2.0 standard allows vendor implementations to support shader programs as pre-compiled binaries, source code to be compiled when the OpenGL ES 2.0 program is run, or both. At the time of writing, the Khronos Group has not defined the Application Binary Interface (ABI) for shader binaries. These are intended to be registered by hardware vendors with Khronos. This would result in updates to the header file GLES2/gl2ext.h.

Variant: ScreenPlay and non-ScreenPlay. Target audience: Device creators.

A shader is a compilation unit for one of the two programmable stages in the OpenGL ES pipeline. A shader program is a complete set of shaders that are compiled and linked together.

It is not always possible to register an extension to define the enumerated value GLenum binaryformat supplied to glShaderBinary() by client applications. In such cases the enumerations need to be defined in an SDK-specific file, which the client application must include.

To maximize portability, shader programs may be supplied in as many formats as possible. Symbian recommends that OpenGL ES 2.0 implementation vendors supply guidance to application developers to enable them to achieve this goal. For example, if OpenGL ES 2.0 support is being added to an SDK for a family of handsets, the different ABI formats that are supported should be documented. Alternatively, if source code compilation is available, this should also be documented.

The OpenGL ES 2.0 implementation vendor must give application writers details on how to pre-compile to a binary suitable for run-time loading. Alternatively, the vendor must explain whether shaders must be supplied as source code. A vendor may wish to provide support for both approaches. In such conditions, the vendor must describe the benefits of each method.

In order to allow client applications to be written in a portable fashion, vendor implementations must support the following queries:

  • glGetBooleanv(GL_SHADER_COMPILER, &shaderCompilerSupport)

  • glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &nShaderBinaryFormats).

If the GL_SHADER_COMPILER query returns GL_TRUE, the vendor implementation has a runtime compiler for the shader source code. If the GL_NUM_SHADER_BINARY_FORMATS query is greater than zero, the vendor implementation supports loading pre-compiled shader programs.

It is recommended that application developers make use of such queries in structuring their programs to take advantage of whatever is available in the platform to construct shader programs. The following code excerpt demonstrates this:

programObjectId = glCreateProgram();
GLuint fragShaderId;
GLuint vertShaderId;
fragShaderId = glCreateShader(GL_FRAGMENT_SHADER);
vertShaderId = glCreateShader(GL_VERTEX_SHADER);
    
GLboolean shaderCompilerSupport;
glGetBooleanv(GL_SHADER_COMPILER, &shaderCompilerSupport);
GLboolean shaderBinarySupport;
GLint nShaderBinaryFormats;
glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &nShaderBinaryFormats);
shaderBinarySupport = (nShaderBinaryFormats > 0) ? GL_TRUE : GL_FALSE;
    
/*
 * Support Compile?  Support Binary?  Preferred Action
 * ----------------  ---------------  ----------------
 * FALSE             FALSE            Panic (out of spec behavior)
 * FALSE             TRUE             Load binary shader if have one
 * TRUE              FALSE            Compile shader from source if have it
 * TRUE              TRUE             Compile shader (we assume it would
 *                                    be better optimized than pre-built
 *                                    binaries; experimentation required)
 */
    
if (shaderCompilerSupport == GL_TRUE)
    {
    GLint shaderCompiled;
    glShaderSource(fragShaderId, 1, (const char**)&KFragmentShaderSrc, NULL);
    glCompileShader(fragShaderId);
    glGetShaderiv(fragShaderId, GL_COMPILE_STATUS, &shaderCompiled);
    
    if (!shaderCompiled) 
        {
        User::Panic(_L("OGLES2App-Comp"), 1);
        }
    glShaderSource(vertShaderId, 1, (const char**)&KVertexShaderSrc, NULL);
    glCompileShader(vertShaderId);
    glGetShaderiv(vertShaderId, GL_COMPILE_STATUS, &shaderCompiled);
    if (!shaderCompiled) 
        {
        User::Panic(_L("OGLES2App-Comp"), 2);
        }
    }
else if (shaderBinarySupport == GL_TRUE)
    {
    // This single-platform example has uses only one binary format type.
    // A multi-platform example would use a loop to cycle through
    // each available binary format before giving up.
    glShaderBinary(1, &fragShaderId, GL_SGX_BINARY_VENDOR, KFragmentShaderBin, KFragmentShaderBinSz);

    if (glGetError())
        {
        User::Panic(_L("OGLES2App-Load"), 3);
        }
    glShaderBinary(1, &vertShaderId, GL_SGX_BINARY_VENDOR, KVertexShaderBin, KVertexShaderBinSz);

    if (glGetError())
        {
        User::Panic(_L("OGLES2App-Load"), 4);
        }
    }
else
    {
    User::Panic(_L("OGLES2App-Lib"), 5);
    }
    
glAttachShader(programObjectId, fragShaderId);
glAttachShader(programObjectId, vertShaderId);
glDeleteShader(fragShaderId);
glDeleteShader(vertShaderId);
    
// Any attribute bindings need to be done before LinkProgram
glBindAttribLocation(programObjectId, KVertexAttrIndex, "myVertex");
    
glLinkProgram(programObjectId);
GLint programLinked;
glGetProgramiv(programObjectId, GL_LINK_STATUS, &programLinked);
if (!programLinked)
    {
    User::Panic(_L("OGLES2App-Link"), 6);
    }
glUseProgram(programObjectId);

Notes

  • This program is locked to an SDK-specific family because it uses the binary format token GL_SGX_BINARY_VENDOR using an SDK-specific file, for example, sdk_gl2ext.h.

  • The program checks for compiler support and binary support, but prefers to compile rather than to use binaries.

  • The program assumes there is only one binary format (GL_SGX_BINARY_VENDOR) whereas this might need to be turned into a loop to cover a family of handsets.

  • Top-level declarations are not shown for reasons of space. These are: KVertexShaderSrc, KVertexAttrIndex, KFragmentShaderBin, KFragmentShaderBinSz, KVertexShaderBin, KVertexShaderBinSz, programObjectId.

  • Shader source code or binaries should be in a secure location, accessible privately to the application. They should not be in a public resource file due to malicious replacement and reverse engineering risks.

Security issues

Although limited in behavior, shader programs are deployed into the runtime environment of a device and therefore OpenGL ES 2.0 implementation vendors must take measures to address the security threats that are involved.

When an OpenGL ES 2.0 application provides source code for a shader program, the OpenGL ES implementation vendor must take care not to allow its data to be compromised in terms of confidentiality or integrity. The programs are confidential because they may represent the intellectual property of the shader program author. Integrity must be assured for shader source code and shader binaries to prevent malicious replacement or modification.

For more information, see OpenGL ES Security Threats and Measures.