WebGL: INVALID_OPERATION: drawArraysInstanced: no buffer is bound to enabled attribute

In a recent change to my project, two different instanced geometries share four instanced attributes. Sometimes, when one of them is removed from the scene, I get this log output and both geometries are not rendered. It’s almost certainly something that I’m doing that is incorrect, but I can’t work out what.

It’s quite a big project, so there is a lot of contextual information. I have contexts where it does and doesn’t happen 100% of the time, but the differences between them are not obvious, and haven’t given me any clue to start understanding the bug. It feels like an order of operations / race condition kind of error.

So, my question is: where can I look to gain more understanding about this error? Where are attributes bound and unbound in Three? Finding where to start digging is hard.

Thanks in advance.

Are you referring here on buffer attributes? Please notice that geometries have to be managed as a single entity. When you change the attribute configuration, you should dispose the geometry object and create a new one. It’s also not possible to dispose single buffer attributes, only the entire geometry.

Yes, I have two InstancedBufferGeometry instances that have 5 and 7 InstancedBufferAttributes. 4 of those InstancedBufferAttributes are shared. I don’t have a problem with the constructing of or disposing of geometries; the problem is that in some contexts when I remove and dispose one of the geometries, the other geometry is no longer rendered, and WebGL: INVALID_OPERATION: drawArraysInstanced: no buffer is bound to enabled attribute is console-logged. However, in other contexts, there’s no problem. I am not trying to dispose attributes, I understand how to recreate the geometry when attribute data changes.

I’m specifically asking about how to gain a better understanding about that error, which appears to surface because the same InstancedBufferAttribute instances were being used for two geometries, then one of them is removed. Some indicators about where that happens in the codebase would be really helpful, because right now it’s too big a haystack to search in.

Do you mind sharing a live example that demonstrates the issue?

I can’t do it, sorry. The project is closed-source and the problem doesn’t reproduce in any pared down case I’ve been able to produce.

I’m not asking “can you fix this?” I’m asking “where should I look?”.

Well, a live example would help me to figure out a recommendation where to look^^.

I feel like we are talking right past each other here. Thank you for your attention.

For anyone else with the same issue. I’ve fixed it by creating a shared Float32Array data that are passed that into different InstancedBufferAttributes.

When geometry is diposed, under the hood onGeometryDispose is called which iterates over attributes and removes them:

    for (const name in buffergeometry.attributes) {
      attributes.remove(buffergeometry.attributes[name]);
    }

    function remove(attribute) {
      ...
      const data = buffers.get(attribute);
      if (data) {
        gl.deleteBuffer(data.buffer);
        buffers.delete(attribute);
      }
    }

There’s no logic in there to handle sharing of attributes, so it working was order-of-operations luck.

1 Like