Check your context if glCreateShader returns 0 and GL_INVALID_OPERATION

I passed half a day hunting weird bugs on Android’s Native Activity.

The one I’ll talk about here is if glCreateShader suddenly returns 0 and glError sends you back 0x502 (GL_INVALID_OPERATION or 1282 in decimal).

I had a lot of trouble finding a working solution for this problem, so here it is, for the record.

The solution that worked for me

Pass a GLEnum array[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE } as the last parameter of your eglCreateContext call, in order to be sure that EGL will set up an OpenGL ES 2.0 context.

Defining { ..., EGL_CONFORMANT, EGL_OPENGL_ES2_BIT, ... } in your configuration attributes list is NOT enough. I guess that a conformant OpenGL ES 2.0 configuration can still be used for OpenGL ES 1.0.

Example

    const EGLint attribs[] = {
      EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
      EGL_BLUE_SIZE, 8,
      EGL_GREEN_SIZE, 8,
      EGL_RED_SIZE, 8,
      EGL_CONFORMANT, EGL_OPENGL_ES2_BIT,
      EGL_NONE
    };

    const EGLint GiveMeGLES2[] = {
      EGL_CONTEXT_CLIENT_VERSION, 2,
      EGL_NONE
    };

    /* ... */

    eglChooseConfig(display, attribs, &config, 1, &numConfigs);

    eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);

    ANativeWindow_setBuffersGeometry(window, 0, 0, format);

    surface = eglCreateWindowSurface(display, config, window, NULL);
    context = eglCreateContext(display, config, NULL, GiveMeGLES2);

Passing EGL_CONTEXT_CLIENT_VERSION as the third parameter of eglQueryContext can tell you which OpenGL ES version your context was prepared for.

    GLuint v = 0;
    eglQueryContext(display, context, EGL_CONTEXT_CLIENT_VERSION, &v);

    __android_log_print(ANDROID_LOG_ERROR, LOGTAG, "Context prepared for OpenGL ES : %d", v);

Red herrings

Shader calls should be within a GL thread that is onSurfaceChanged(), onSurfaceCreated() or onDrawFrame(). (from StackOverflow)

This was not an issue. However, thanks to the BioniC team who was kind enough to implement the gettid system call.

You will need the following headers to get gettid working :

#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>

Then, for example, this logs the current thread id :

	__android_log_print(ANDROID_LOG_ERROR, "native-insanity", "Thread ID : %d", gettid());

You can use this to log the id of the threads calling your different procedures, for comparison.

glCreateShader will return GL_INVALID_OPERATION if called between glBegin and glEnd. (from old OpenGL manuals)

Non-issue with OpenGL ES > 2.x since those calls are unavailable.

Note that the current Khronos manuals do not even mention this error.

It really took me a LOT of time to understand what went wrong with this, given the lack of documentation on this issue. I’d really appreciate if some website compiled all the potential issues you can have with OpenGL with potential solutions and hints.

Stride argument in OpenGL ES 2.0

I’m putting this information here, as it took me way more time than it should to understand how the stride argument works in glVertexAttribPointer.

This argument is extremely important if you want to pack data in the same order as they are accessed by the CPU/GPU.

When reading the manual, I thought that stride was the number of bytes the OpenGL implementation would skip after reading size elements from the provided array.

However, it tends to work like this. glVertexAttribPointer :

  • starts reading data from the provided address (or from the current GL_ARRAY_BUFFER if any is bound),
  • read size elements from the address,
  • passes the values to the corresponding GLSL attribute,
  • jump stride bytes from the address it started reading from,
  • repeats this procedure count times, where count is the third argument passed to glDrawArrays.

So, for example, let’s take a float array stored at memory’s address 0x20000, containing the following 15 elements :

GLfloat arr[] = { 
  /* 0x20000 */ -1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
  /* 0x20014 */ -1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
  /* 0x20028 */  0.0f, 1.0f, 1.0f, 1.0f, 1.0f 
};

If you use glVertexAttribArray like this :

glVertexAttribArray(your_glsl_attrib_index, 3, GL_FLOAT, GL_FALSE, 20, arr);

And then use glDrawArrays, the OpenGL implementation will do something akin to this :

  • Copy the address arr (0x20000).
  • Start reading {-1.0f, 1.0f, 1.0f} from the copied address (referred as copy_arr here) and pass these values to the GLSL attribute identified by your_glsl_attrib_index.
  • Do something like copy_arr += stride. At this point, copy_arr == 0x20014.

Then, on the second iteration, it will read {-1.0f, 0.0f, 1.0f} from the new copy_arr address, redo copy_arr += stride and continue like this for each iteration.

Here’s a concise diagram resuming this.

Schema showing how the OpenGL Stride argument works