Vertex attribute index out of boundary

OpenGL ES 3.1 here; an Android app.

I am trying to debug a problem. The problem is: after doing a specific sequence of actions, my main object completely disappears from the screen. I know precisely what sequence of actions triggers the bug, I can thus make it happen at will.

I’ve tried several different phones with different GPUs - PowerVR, Mali, Adreno as well as Android emulators - and the problem is exactly the same everywhere. When the bug happens, the phones do not show any errors on Android debug command line; Android emulator though, exactly at the time when the bug happens, starts spitting an endless sequence of the folllowing:

E/emuglGLESv2_enc: a vertex attribute index out of boundary is detected. Skipping corresponding vertex attribute. buf=0x0

In my main rendering loop I do

int err = GLES31.glGetError();

if( err!=GLES31.GL_NO_ERROR )
android.util.Log.e(“D”, "OpenGL error "+err);

and I can’t see this debug anywhere, thus it seems like there are no OpenGL errors.

Would you have any advice on how to debug this? ‘vertex attribute index out of boundary’ ? What does that exactly mean?

I have 4 different vertex shaders. The first and second shaders both define the same list of 4 attributes:

in vec3 a_Position;
in vec3 a_Normal;
in vec2 a_TexCoordinate;
in float a_Component;

whereas the third and fourth vertex shaders both define a single attribute:

in vec2 a_Position;

As you can see I don’t specify a ‘location=N’, I assign indexes with

GLES31.glBindAttribLocation(programHandle, 0, “a_Position”);
GLES31.glBindAttribLocation(programHandle, 1, “a_Normal”);
GLES31.glBindAttribLocation(programHandle, 2, “a_TexCoordinate”);
GLES31.glBindAttribLocation(programHandle, 3, “a_Component”);

before linking the (1st and 2nd) shaders, then I use VBOs and send the attributes to the shaders with

GLES31.glBindBuffer(GLES31.GL_ARRAY_BUFFER, vbo1Index);
GLES31.glVertexAttribPointer(program.mAttribute[0], POS_DATA_SIZE, GLES31.GL_FLOAT, false, VERT1_SIZE, OFFSET_POS);
GLES31.glVertexAttribPointer(program.mAttribute[1], NOR_DATA_SIZE, GLES31.GL_FLOAT, false, VERT1_SIZE, OFFSET_NOR);
GLES31.glBindBuffer(GLES31.GL_ARRAY_BUFFER, vbo2Index );
GLES31.glVertexAttribPointer(program.mAttribute[2], TEX_DATA_SIZE, GLES31.GL_FLOAT, false, VERT2_SIZE, OFFSET_TEX);
GLES31.glVertexAttribPointer(program.mAttribute[3], COM_DATA_SIZE, GLES31.GL_FLOAT, false, VERT2_SIZE, OFFSET_COM);
GLES31.glBindBuffer(GLES31.GL_ARRAY_BUFFER, 0);

(as you can see there are just two VBOs: one contains the ‘Position’ and ‘Normal’ attributes, another - ‘TexCoordinate’ and ‘Component’ )

You haven’t posted your glEnableVertexAttribArray calls, but that would be the first place I’d check.

It means that one of the indices in the GL_ELEMENT_ARRAY_BUFFER used by glDrawElements exceeds the number of elements in the vertex attribute arrays.

OpenGL itself doesn’t track the size of vertex arrays. With client-side vertex arrays it doesn’t know the size, just their start address; it relies upon the client only passing valid indices. With VBOs, the implementation can at least determine that the offset within the buffer is beyond the size of the buffer. Thus the implementation can probably detect this, but there’s no specified error condition so it won’t be reported via glGetError.

The most likely reasons are writing invalid indices to the element array or binding the wrong VBO prior to the glVertexAttribPointer call. It’s also possible that you’re passing an incorrect stride or offset (or an invalid attribute size in the case where the stride parameter is zero) to that call, resulting in the conversion from index to offset being incorrect.

Or as mhagain suggests, you’re enabling (or rather neglecting to disable) an attribute array which is too small for the indices being used. The implementation will likely attempt to fetch attribute data from all enabled arrays regardless of whether the shader program uses those attributes. If you’re switching between multiple sets of attribute arrays by repeatedly modifying a single VAO rather than switching between VAOs, this is a risk.

Thanks for the replies!

I’ve been debugging some more, I’ve checked which VBOs get bound, the glEnableVertexAttribArray calls, the strides and offsets - everything seems to be correct. I made sure the number of vertices sent to the glDrawArrays() call multiplied by the number of bytes taken by all attribs of a single vertex is equal to the size of the bound attrib buffer - and those two numbers are indeed equal.

But the codebase is large (100k LOC) and complex, I might have overlooked something.

Now I noticed that on one of the real phones, when the bug happens, I get ‘OpenGL error 1285’ (out of memory) - rather than the ‘vertex attribute index out of boundary’ which I get on the emulator. At the moment I am trying to figure out which call exactly gives the ‘out of memory’ error.

EDIT: the OUT_OF_MEMORY error comes from the

glDrawArrays(GLES31.GL_TRIANGLE_STRIP, 0, mNumVertices );

call. The online reference glDrawArrays - OpenGL ES 3.1 Reference Pages does not even list this as one of the errors possible to be raised by this call?


EDIT2: Say, I have two shaders, one declares two vertex attribs, another - one.
I use the first shader, I enable the two vertex attrib arrays it requires with two calls to ‘glEnableVertexAttribArray’, I draw something.

Now I want to use the second shader. At this point, do I have to

  1. make a call to glUseProgram, and make a single call to ‘glEnableVertexAttribArray’ to enable the single attrib array declared by the shader
  2. first make two calls to ‘glDisableVertexAttribArray’ to disable the two arrays which were used by the previous shader, then call glUseProgram, and then a single call to ‘glEnableVertexAttribArray’

Or maybe something else altogether?

Currently I do option 1. Actually now that I think of it, it seems that I constantly struggle with this kind of questions - i.e. questions about the scope of certain OpenGL actions. Is disabling/enabling the attrib arrays ‘global’ or ‘per shader’ ? Those issues are very poorly explained in the docs.

I don’t use VAOs.

GL_OUT_OF_MEMORY can be generated by anything, so it isn’t listed explicitly.


  1. Make one call to glDisableVertexAttribArray to disable the array which isn’t being used.

But if you’re using ES 3, you should use VAOs to store attribute array state. Then it’s just glBindVertexArray, glUseProgram, glDrawArrays`.

That would explain the problem. The second array isn’t disabled even when it isn’t used. It must still contain enough data for both calls.

Hmm… I’ve switched to ‘option 2.’ i.e.

first make two calls to ‘glDisableVertexAttribArray’ to disable the two arrays which were used by the previous shader, then call glUseProgram, and then a single call to ‘glEnableVertexAttribArray’

and that doesn’t help one bit - still exactly the same problem.

I’ll try your ‘option 3’ now, but - isn’t option 2 equivalent to your ‘option 3’ ?

Have you tried running this under RenderDoc? It should allow you to verify the vertex attribute state at the problematic draw call and hopefully from that you can determine where an unexpected attribute is enabled/wrong buffer bound.

Thanks for all the suggestions, also for the RenderDoc - I realy need to take a deep look at it!

The issue is solved; in the end, after many shots in the dark have missed the target, I resorted to the good old ‘smallest piece of code that still reproduces the problem’ tedious method. Starting with the 100k lines of code I eventually arrived at a single line - and then it turned out there is one convoluted way in which one of the VBOs does not get recreated after a new Android Activity gets fired (which means we have just lost the OpenGL context and we need to recreate such objects).

In the process I also discovered two additional issues

  1. the issue GClements pointed out - that I wasn’t properly glDisabling the no more needed VertexAttribArrays between using different programs - corrected
  2. it also turned out that the VBOs, which get written once by the CPU and get read on the GPU, were being sent to the GPU with a call to glBufferData(… , … , …, GL_STATIC_READ) - and now I understand that’s incorrect, according to the docs it should be GL_STATIC_DRAW. Corrected again.

The thing is though - despite those two problems, I am pretty sure the app has been running just fine just about everywhere and I have evidence to back up that claim.

My app has been downloaded more than 850k times and there are currently more than 80k ‘active users’ i.e. people who have it installed and have fired it up at least once within the last 7 days.

At one point, I’ve had many users reporting heavy artifacts on Adreno 30X devices - and after many attempts at a ‘fix’ - which fixed rendering on a single version of the Qualcomm driver on Adreno 306 but created new problems on Adreno 308 - in order to make sure that the final version finally works everywhere , I had the app, on startup, report the chipset+driver version, and the number of levels that have been passed, to my central server.

The thinking goes - if there is someone out there who, on a certain chipset+driver combination, has passed at least one level, than that’s a pretty good proof that the 3D part of the app gets rendered correctly there.

So now I have amassed a big DB of just about every chipset+driver combination out there, not only Mali, Adreno & PowerVR but also an occasional Intel, Vivante, MESA, Google ShiftShader software renderers. 1623 such combinations to be exact, and on 1578 of them there is at least one user who has passed a level. The remaning 45 combinations are all some very unusual driver versions with just a few users each, so I am pretty sure those are just cases of users quickly taking a look at my app and deleting it right away rather than genuine problems with rendering.

Bottom line: despite the fact that I wasn’t disabling the VertexAttribArrays and was using GL_STATIC_READ, I am pretty sure the app was rendering correct image on just about every mobile platform out there.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.