EGL- Understanding eglChooseConfig, then ignoring it

A few months ago I posted a talk on initializing OpenGL-ES using the EGL API. Well now I’m going to walk you through how to actually get the configuration you want. Nearly all of the OpenGL-ES code I’ve seen (including the Android SDK samples) provide boilerplate so that you just ask for the configs that meet your requirements, and just take the config that is first in the list. Usually this is the exact WRONG configuration you want. It will certainly work, but it’s usually the “most capable” configuration, where as you just want the “best for my needs” configuration.

What you are actually choosing is the format of the “Surface” (EGL terminology for the Render Target (aka destination buffer) where the output will be rendered). If you are coming from Windows this is the “pixel format descriptor”. On Mac it’s the “pixel format object”. You look at what you are rendering and make a decision – for example, I need to render to a 32bit/32bit/32bit RGB color buffer, with a 16-bit depth buffer, and a 32-bit stencil buffer.

So let’s review the code that you typically see in an OpenGL-ES app to get select a Surface.

.
    // Get Display Type
    EGLDisplay eglDisplay = eglGetDisplay( EGL_DEFAULT_DISPLAY );
    eglInitialize( eglDisplay, NULL, NULL);

    // typical high-quality attrib list
    EGLint defaultAttribList[] = {
	// 32 bit color
	EGL_RED_SIZE, 8,
	EGL_GREEN_SIZE, 8,
	EGL_BLUE_SIZE, 8,
	// at least 24 bit depth
	EGL_DEPTH_SIZE, 24,
	EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
	// want opengl-es 2.x conformant CONTEXT
	EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 
	EGL_NONE
    };

    EGLint numConfigs;
    EGLConfig config;

    // DO THIS AT YOUR OWN PERIL!
    eglChooseConfig(eglDisplay, defaultAttribList,
                   &config, 1, &numConfigs)
.

Since we ask for just one config, we get one config. You can only choose one config.  What you really need to do is two steps;

  1. Make the call to eglChooseConfig as before, but pass in a null pointer for the configs parameter. This will return to total available configs that match the description in the numConfigs parameter.
  2. Allocate an array of EGLConfig’s big enough and make the same call, this time passing in the new array pointer and the new size. You will then have an array of all possible configurations available.

This will return you all the the configs that EGL thinks MEET OR BEAT your specified criteria. Note the OR BEAT.  The eglChooseConbfig spec clearly states;

When more than one EGL frame buffer configuration matches the specified attributes, a list of matching configurations is returned. The list is sorted according to the following precedence rules, which are applied in ascending order (i.e., configurations that are considered equal by a lower numbered rule are sorted by the higher numbered rule):

Special: by EGL_CONFIG_CAVEAT, where the precedence is EGL_NONE, EGL_SLOW_CONFIG, and EGL_NON_CONFORMANT_CONFIG.

Special: by EGL_COLOR_BUFFER_TYPE, where the precedence is EGL_RGB_BUFFER, EGL_LUMINANCE_BUFFER.

Special: by larger total number of color bits (for an RGB color buffer, this is the sum of EGL_RED_SIZE, EGL_GREEN_SIZE, EGL_BLUE_SIZE, and EGL_ALPHA_SIZE; for a luminance color buffer, the sum of EGL_LUMINANCE_SIZE and EGL_ALPHA_SIZE). If the requested number of bits in attrib_list is 0 or EGL_DONT_CARE for a particular color component, then the number of bits for that component is not considered.

This sort rule places configs with deeper color buffers before configs with shallower color buffers, which may be counter-intuitive.

Smaller EGL_BUFFER_SIZE.
Smaller EGL_SAMPLE_BUFFERS.
Smaller EGL_SAMPLES.
Smaller EGL_DEPTH_SIZE.
Smaller EGL_STENCIL_SIZE.
Smaller EGL_ALPHA_MASK_SIZE.
Special: EGL_NATIVE_VISUAL_TYPE (the actual sort order is implementation-defined, depending on the meaning of native visual types).
Smaller EGL_CONFIG_ID (this is always the last sorting rule, and guarantees a unique ordering).

EGLConfigs are not sorted with respect to the attributes; EGL_BIND_TO_TEXTURE_RGB, EGL_BIND_TO_TEXTURE_RGBA, EGL_CONFORMANT, EGL_LEVEL, EGL_NATIVE_RENDERABLE, EGL_MAX_SWAP_INTERVAL, EGL_MIN_SWAP_INTERVAL, EGL_RENDERABLE_TYPE, EGL_SURFACE_TYPE, EGL_TRANSPARENT_TYPE, EGL_TRANSPARENT_RED_VALUE, EGL_TRANSPARENT_GREEN_VALUE, and EGL_TRANSPARENT_BLUE_VALUE.

The emphasis is mine – but what it means is that it will prefer 1) A larger color buffer format than you specify, 2) you might want to choose a depth buffer that a different size depending on the hardware (some have odd sizes that might work better – i.e. 24-bit native might be better than 16-bit) and 3) there are attribs that it won’t sort on at all that you have no control over.

Or you can ignore eglChooseConfig entirely and do you own sorting and selection. In this case you’d just call eglGetConfigs to get all the number of TOTAL configurations (for THAT eglDisplay – yes turtles all the way down). And then, for each config, call eglGetConfigAttrib to query each attribute you care about. Shove them all in a list and THEN sort by desirability.  And this post is already too long, so I’ll leave that bit of code for next time.

Here’s how to query/store all the configs. oglesBufferFormat is a structure that contains the attrib I’m interested in. You need to make your own for your needs.

.
// Get number of all configs, have gotten display from EGL
if ( EGL_FALSE == eglGetConfigs(_eglDisplay, NULL, 0, &numConfigs) )
    {
    queryEGLError();
    return EGL_FALSE;
    }
DebugMsg("there are %d configurations available.\n", numConfigs);

// collect information about the configs
EGLConfig *configs = new EGLConfig[numConfigs];

if ( EGL_FALSE == eglGetConfigs(_eglDisplay,configs,numConfigs,&numConfigs) )
    {
    queryEGLError();
    delete [] configs;
    return EGL_FALSE;
    }

std::vector<oglesBufferFormat> _bufferFormats;

oglesBufferFormat newFormat;

for ( GLint c = 0 ; c < numConfigs ; ++c)
    {
    EGLConfig config = configs[c]; 
    eglGetConfigAttrib( _eglDisplay, config, EGL_ALPHA_SIZE, &(newFormat._alpha_size));
    eglGetConfigAttrib( _eglDisplay, config, EGL_BIND_TO_TEXTURE_RGB, &(newFormat._bind_to_texture_rgb));
    eglGetConfigAttrib( _eglDisplay, config, EGL_BIND_TO_TEXTURE_RGBA, &(newFormat._bind_to_texture_rgba));
    eglGetConfigAttrib( _eglDisplay, config, EGL_BLUE_SIZE, &(newFormat._blue_size));
    eglGetConfigAttrib( _eglDisplay, config, EGL_BUFFER_SIZE, &(newFormat._buffer_size));
    eglGetConfigAttrib( _eglDisplay, config, EGL_CONFIG_CAVEAT, &(newFormat._config_caveat));
    eglGetConfigAttrib( _eglDisplay, config, EGL_CONFIG_ID, &(newFormat._config_id));
    eglGetConfigAttrib( _eglDisplay, config, EGL_DEPTH_SIZE, &(newFormat._depth_size));
    eglGetConfigAttrib( _eglDisplay, config, EGL_GREEN_SIZE, &(newFormat._green_size));
    eglGetConfigAttrib( _eglDisplay, config, EGL_LEVEL, &(newFormat._level));
    eglGetConfigAttrib( _eglDisplay, config, EGL_MAX_PBUFFER_WIDTH, &(newFormat._max_pbuffer_width));
    eglGetConfigAttrib( _eglDisplay, config, EGL_MAX_PBUFFER_HEIGHT, &(newFormat._max_pbuffer_height));
    eglGetConfigAttrib( _eglDisplay, config, EGL_MAX_PBUFFER_PIXELS, &(newFormat._max_pbuffer_pixels));
    eglGetConfigAttrib( _eglDisplay, config, EGL_MAX_SWAP_INTERVAL, &(newFormat._max_swap_interval));
    eglGetConfigAttrib( _eglDisplay, config, EGL_MIN_SWAP_INTERVAL, &(newFormat._min_swap_interval));
    eglGetConfigAttrib( _eglDisplay, config, EGL_NATIVE_RENDERABLE, &(newFormat._native_renderable));
    eglGetConfigAttrib( _eglDisplay, config, EGL_NATIVE_VISUAL_ID, &(newFormat._native_renderable));
    /// etc etc etc for all those that you care about

    if ( majorVersion >= 1 && minorVersion >= 2 )
        {       
        // 1.2
        eglGetConfigAttrib( _eglDisplay, config, EGL_ALPHA_MASK_SIZE, &(newFormat._alpha_mask_size));
        eglGetConfigAttrib( _eglDisplay, config, EGL_COLOR_BUFFER_TYPE, &(newFormat._color_buffer_type));
        eglGetConfigAttrib( _eglDisplay, config, EGL_LUMINANCE_SIZE, &(newFormat._luminance_size));
        eglGetConfigAttrib( _eglDisplay, config, EGL_RENDERABLE_TYPE, &(newFormat._renderable_type));
        }

    if ( majorVersion >= 1 && minorVersion >= 3 )
        {
        // 1.3
        eglGetConfigAttrib( _eglDisplay, config, EGL_CONFORMANT, &(newFormat._conformant));
        }
        _bufferFormats.push_back(newFormat);
    }
.
This entry was posted in OpenGL. Bookmark the permalink.