OpenGLException: Cannot use Buffers when Element Array Buffer Object is enabled

Hello everybody,



I have an issue with the VBO rendering. My scene draws some TriMesh with VBO enabled for the index buffer and also some lines with no VBO at all. Here is a simple example showing what I try to do:




protected final void simpleInitGame() {
   
      Quad test = new Quad("VBOObject", 20.0f, 20.0f);
      VBOInfo vbi = new VBOInfo(true);
      vbi.setVBOIndexEnabled(true);
      test.setVBOInfo(vbi);
      rootNode.attachChild(test);
      
      FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(2 * 3);
      vertexBuffer.put(0);
      vertexBuffer.put(0);
      vertexBuffer.put(0);
      vertexBuffer.put(10);
      vertexBuffer.put(10);
      vertexBuffer.put(10);

      Line l = new Line("NoVBO", vertexBuffer, null, null, null);
      l.generateIndices();
      //Quad l = new Quad("NoVBOWithQuad", 30.0f, 20.0f);
      l.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
      l.updateRenderState();
      l.updateGeometricState(0.f, true);
      rootNode.attachChild(l);
   }



If I execute this code, I have the following exception:


org.lwjgl.opengl.OpenGLException: Cannot use Buffers when Element Array Buffer Object is enabled
   at org.lwjgl.opengl.GLChecks.ensureElementVBOdisabled(GLChecks.java:96)
   at org.lwjgl.opengl.GL11.glDrawElements(GL11.java:1074)
   at com.jme.renderer.lwjgl.LWJGLRenderer.draw(LWJGLRenderer.java:783)
   at com.jme.scene.Line.draw(Line.java:413)
   at com.jme.renderer.RenderQueue.renderTransparentBucket(RenderQueue.java:277)
   at com.jme.renderer.RenderQueue.renderBuckets(RenderQueue.java:238)
   at com.jme.renderer.Renderer.renderQueue(Renderer.java:379)
   at com.jme.renderer.lwjgl.LWJGLRenderer.displayBackBuffer(LWJGLRenderer.java:495)
   at com.jme.app.BaseGame.start(BaseGame.java:87)




  • This exception appears only when a line is drawn after a Trimesh with VBO enabled on index buffer

  • There is a exception even if I enable the VBO on the Line

  • There is no exception if I replace the line by a Quad or a TriMesh

  • There is no exception if I don't enable VBO on index buffer for the Quad



In the predrawGeometry() method, there is the following code:


if (g instanceof TriMesh) {
        if ((supportsVBO && vbo != null && vbo.getVBOIndexID() > 0)) { // use VBO
            indicesVBO = true;
            rendRecord.setBoundElementVBO(vbo.getVBOIndexID());
        } else if (supportsVBO) {
            rendRecord.setBoundElementVBO(0);
        }
}



Maybe the same thing for a line is necessary???  :?


if (g instanceof TriMesh || g instanceof Line) {
        if ((supportsVBO && vbo != null && vbo.getVBOIndexID() > 0)) { // use VBO
            indicesVBO = true;
            rendRecord.setBoundElementVBO(vbo.getVBOIndexID());
        } else if (supportsVBO) {
            rendRecord.setBoundElementVBO(0);
        }
}



Could someone help me?  :)

/** Helper method to ensure that element array buffer objects are disabled. If they are enabled, we'll throw an OpenGLException */
   static void ensureElementVBOdisabled() {
      if ((GLContext.getCapabilities().OpenGL15 && !checkBufferObject(GL15.GL_ELEMENT_ARRAY_BUFFER_BINDING, false) ||
               (GLContext.getCapabilities().GL_ARB_vertex_buffer_object && !checkBufferObject(ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, false))))
         throw new OpenGLException("Cannot use Buffers when Element Array Buffer Object is enabled");
   }



I think that maybe your fix works, did you test it?

Yes it works  :)

But I have a doubt about the patch to write :

In prepVBO we have:


        if (g instanceof TriMesh) {

            if (vbo.isVBOIndexEnabled() && vbo.getVBOIndexID() <= 0) {
                TriMesh tb = (TriMesh) g;
                if (tb.getIndexBuffer() != null) {
                    Object vboid;
                    if ((vboid = vboMap.get(tb.getIndexBuffer())) != null) {
                        vbo.setVBOIndexID(((Integer) vboid).intValue());
                    } else {
                        tb.getIndexBuffer().rewind();
                        int vboID = rendRecord.makeVBOId();
                        vbo.setVBOIndexID(vboID);
                        vboMap.put(tb.getIndexBuffer(), vboID);

                        rendRecord.invalidateVBO(); // make sure we set it...
                        rendRecord.setBoundElementVBO(vbo.getVBOIndexID());
                        ARBBufferObject
                                .glBufferDataARB(
                                        ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB,
                                        tb.getIndexBuffer(),
                                        ARBBufferObject.GL_STATIC_DRAW_ARB);

                    }
                }
            }
        }



But nothing for Lines and QuadMesh (Lines and QuadMesh could use VBO for index buffer... ) so the code should be:



        IntBuffer indexBuffer = null;
        if (g instanceof TriMesh) {
           TriMesh tb = (TriMesh) g;
           indexBuffer = tb.getIndexBuffer();
        } else if (g instanceof Line) {
           Line l = (Line) g;
           indexBuffer = l.getIndexBuffer();
        } else if (g instanceof QuadMesh) {
           QuadMesh qm = (QuadMesh) g;
           indexBuffer = qm.getIndexBuffer();
        }
       
        if (indexBuffer != null) {
            if (vbo.isVBOIndexEnabled() && vbo.getVBOIndexID() <= 0) {              
                Object vboid;
                if ((vboid = vboMap.get(indexBuffer)) != null) {
                    vbo.setVBOIndexID(((Integer) vboid).intValue());
                } else {
                   indexBuffer.rewind();
                    int vboID = rendRecord.makeVBOId();
                    vbo.setVBOIndexID(vboID);
                    vboMap.put(indexBuffer, vboID);

                    rendRecord.invalidateVBO(); // make sure we set it...
                    rendRecord.setBoundElementVBO(vbo.getVBOIndexID());
                    ARBBufferObject
                            .glBufferDataARB(
                                    ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB,
                                    indexBuffer,
                                    ARBBufferObject.GL_STATIC_DRAW_ARB);

                }
            }
        }




(To avoid the use of a lot of "instanceof" we could imagine to create a IndexedGeometry class... but that's a lot of work and a lot of test...  :D )


In predrawGeometry() , we also need to add some instanceof :


 if (g instanceof TriMesh || g instanceof Line || g instanceof QuadMesh) {
            if ((supportsVBO && vbo != null && vbo.getVBOIndexID() > 0)) { // use VBO
                indicesVBO = true;
                rendRecord.setBoundElementVBO(vbo.getVBOIndexID());
            } else if (supportsVBO) {
                rendRecord.setBoundElementVBO(0);
            }
 }



I think the same fix is necessary for JOGL...


Ok. Put this patch into the section "Contribution Depot" and if it is accepted for LWJGL, I will fix it in JOGL side but there are a lot of "instanceof"  :x

Probably a dumb question (only looked at this briefly) but could you use 'instanceof Geometry'?

basixs said:

Probably a dumb question (only looked at this briefly) but could you use 'instanceof Geometry'?

Sadly no. But perhaps the proper solution would include moving the index buffer up to geometry. We should check if there are any subclasses that *don't* have an index buffer.

Oups... the Point class has also an index buffer, so with this solution we should add an "instanceof Point"
Arggg the "instanceof" invades the Renderer!!  }:-@ Help! :-o

Moving the indexBuffer in Geometry is probably the best solution  :). The class VBOInfo is already in Geometry and contains some methods relatives to the index buffer so it will be homogeneous.

The bad point: Curve class inherit from Geometry: it doesn't need an index buffer and it's render doesn't use VBO at all...

blobby said:

The bad point: Curve class inherit from Geometry: it doesn't need an index buffer and it's render doesn't use VBO at all...

Maybe we could use a dummy index buffer but Curve already uses a vertex buffer:
 

 public Curve(String name, Vector3f[] controlPoints) {
        super(name);
        if (null == controlPoints) {
            throw new JmeException("Control Points may not be null.");
        }

        if (controlPoints.length < 2) {
            throw new JmeException("There must be at least two control points.");
        }

        setVertexBuffer(BufferUtils.createFloatBuffer(controlPoints));
        steps = 25;
    }


Maybe we could add a getIndexBuffer() method in Geometry: this method could return null when no index buffer is specified like getNormalBuffer(), getBinormalBuffer(), getTextureCoords() etc...

What do you think about that?