Android Renderer patch - Alternate render path for vertex arrays

This is a patch for the android shader renderer to use vertex arrays instead of vertex buffer objects.

This is due to a limitation of the OpenGL ES 2.0 bindings in Android 2.2 where

glDrawElements (int mode, int count, int type, int offset) is missing.



I have been playing around with the renderer for a few days and was finally able to render more than one mesh and more than one material by using vertex arrays only, no VBO.



As i dont have commit priviledges, it would be nice if any of you could test this and possibly commit it.



I have tested on a Samsung Galaxy S under Android 2.2.1



The patch for the renderer to turn off VBO:



[patch]

Index: src/android/com/jme3/renderer/android/OGLESShaderRenderer.java

===================================================================

ā€” src/android/com/jme3/renderer/android/OGLESShaderRenderer.java (revision 6806)

+++ src/android/com/jme3/renderer/android/OGLESShaderRenderer.java (working copy)

@@ -130,6 +130,7 @@



private static final Logger logger = Logger.getLogger(OGLESShaderRenderer.class.getName());

private static final boolean VALIDATE_SHADER = false;

  • private static final boolean USE_VBO = false;



    private final ByteBuffer nameBuf = BufferUtils.createByteBuffer(250);

    private final StringBuilder stringBuf = new StringBuilder(250);

    @@ -1923,9 +1924,9 @@

    GLES20.glBindTexture(type, texId);

    textures[unit] = image;


  •         statistics.onTextureUse(tex, true);<br />
    
  •         statistics.onTextureUse(tex.getImage(), true);<br />
    

}else{

  •         statistics.onTextureUse(tex, false);<br />
    
  •         statistics.onTextureUse(tex.getImage(), false);<br />
    

}



setupTextureParams(tex);

@@ -2510,35 +2511,53 @@

}

}


  • private void renderMeshVertexArray(Mesh mesh, int lod, int count){
  • /**
  • * renderMeshVertexArray renders a mesh using vertex arrays<br />
    
  • * @param mesh<br />
    
  • * @param lod<br />
    
  • * @param count<br />
    
  • */<br />
    
  • private void renderMeshVertexArray(Mesh mesh, int lod, int count)
  • {
  •    if (verboseLogging)<br />
    
  •        logger.info(&quot;renderMeshVertexArray&quot;);<br />
    

- if (verboseLogging)
- logger.info("renderMeshVertexArray");
+ IntMap<VertexBuffer> buffers = mesh.getBuffers();
+ for (Entry<VertexBuffer> entry : buffers){
+ VertexBuffer vb = entry.getValue();

- if (mesh.getId() == -1){
- updateVertexArray(mesh);
- }
+ if (vb.getBufferType() == Type.InterleavedData
+ || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
+ || vb.getBufferType() == Type.Index)
+ continue;

- if (context.boundVertexArray != mesh.getId()){
- // ARBVertexArrayObject.glBindVertexArray(mesh.getId());
- // GLES20.glBindVertexArray(mesh.getId());
- context.boundVertexArray = mesh.getId();
+ if (vb.getStride() == 0){
+ // not interleaved
+ setVertexAttrib_Array(vb);
+ }else{
+ // interleaved
+ VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData);
+ setVertexAttrib_Array(vb, interleavedData);
+ }
}

- IntMap<VertexBuffer> buffers = mesh.getBuffers();
VertexBuffer indices = null;
- if (mesh.getNumLodLevels() > 0){
+ if (mesh.getNumLodLevels() > 0)
+ {
indices = mesh.getLodLevel(lod);
- }else{
+ }
+ else
+ {
indices = buffers.get(Type.Index.ordinal());
}
- if (indices != null){
- drawTriangleList(indices, mesh, count);
- }else{
-// throw new UnsupportedOperationException("Cannot render without index buffer");
-
- if (verboseLogging)
- logger.info("GLES20.glDrawArrays(" + mesh.getMode() + ", " + 0 + ", " + mesh.getVertexCount() + ")");
+ if (indices != null)
+ {
+ drawTriangleList_Array(indices, mesh, count);
+ }
+ else
+ {
+ if (verboseLogging)
+ logger.info("GLES20.glDrawArrays(" + mesh.getMode() + ", " + 0 + ", " + mesh.getVertexCount() + ")");

GLES20.glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
}
@@ -2613,7 +2632,10 @@
// if (GLContext.getCapabilities().GL_ARB_vertex_array_object){
// renderMeshVertexArray(mesh, lod, count);
// }else{
+ if (USE_VBO)
renderMeshDefault(mesh, lod, count);
+ else
+ renderMeshVertexArray(mesh, lod, count);
// }
}

@@ -2630,4 +2652,176 @@
return true;
}

+ /**
+ * drawTriangleList_Array uses Vertex Array
+ * @param indexBuf
+ * @param mesh
+ * @param count
+ */
+ public void drawTriangleList_Array(VertexBuffer indexBuf, Mesh mesh, int count)
+ {
+ if (verboseLogging)
+ logger.info("drawTriangleList_Array(Count = " + count + ")");
+
+ if (indexBuf.getBufferType() != VertexBuffer.Type.Index)
+ throw new IllegalArgumentException("Only index buffers are allowed as triangle lists.");
+
+ boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing);
+ if (useInstancing)
+ {
+ throw new IllegalArgumentException("Caps.MeshInstancing is not supported.");
+ }
+
+ int vertCount = mesh.getVertexCount();
+ Buffer indexData = indexBuf.getData();
+ indexData.clear();
+
+ if (mesh.getMode() == Mode.Hybrid)
+ {
+ int[] modeStart = mesh.getModeStart();
+ int[] elementLengths = mesh.getElementLengths();
+
+ int elMode = convertElementMode(Mode.Triangles);
+ int fmt = convertFormat(indexBuf.getFormat());
+ int elSize = indexBuf.getFormat().getComponentSize();
+ int listStart = modeStart[0];
+ int stripStart = modeStart[1];
+ int fanStart = modeStart[2];
+ int curOffset = 0;
+ for (int i = 0; i < elementLengths.length; i++)
+ {
+ if (i == stripStart)
+ {
+ elMode = convertElementMode(Mode.TriangleStrip);
+ }
+ else if (i == fanStart)
+ {
+ elMode = convertElementMode(Mode.TriangleStrip);
+ }
+ int elementLength = elementLengths;
+
+ indexBuf.getData().position(curOffset);
+ if (verboseLogging)
+ logger.info("glDrawElements(): " + elementLength + ", " + curOffset);
+
+ GLES20.glDrawElements(elMode, elementLength, fmt, indexBuf.getData());
+
+ curOffset += elementLength * elSize;
+ }
+ }
+ else //if (mesh.getMode() == Mode.Hybrid)
+ {
+ if (verboseLogging)
+ logger.info("glDrawElements(), indexBuf.capacity (" + indexBuf.getData().capacity() + "), vertCount (" + vertCount + ")");
+
+ GLES20.glDrawElements(
+ convertElementMode(mesh.getMode()),
+ indexBuf.getData().capacity(),
+ convertFormat(indexBuf.getFormat()),
+ indexBuf.getData()
+ );
+ }
+ }
+
+ /**
+ * setVertexAttrib_Array uses Vertex Array
+ * @param vb
+ * @param idb
+ */
+ public void setVertexAttrib_Array(VertexBuffer vb, VertexBuffer idb)
+ {
+ if (verboseLogging)
+ logger.info("setVertexAttrib_Array(" + vb + ", " + idb + ")");
+
+ if (vb.getBufferType() == VertexBuffer.Type.Index)
+ throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib");
+
+ // Get shader
+ int programId = context.boundShaderProgram;
+ if (programId > 0)
+ {
+ VertexBuffer[] attribs = context.boundAttribs;
+
+ Attribute attrib = boundShader.getAttribute(vb.getBufferType().name());
+ int loc = attrib.getLocation();
+ if (loc == -1)
+ {
+ //throw new IllegalArgumentException("Location is invalid for attrib: [" + vb.getBufferType().name() + "]");
+ if (verboseLogging)
+ logger.warning("attribute is invalid in shader: [" + vb.getBufferType().name() + "]");
+ return;
+ }
+ else if (loc == -2)
+ {
+ String attributeName = "in" + vb.getBufferType().name();
+
+ if (verboseLogging)
+ logger.info("GLES20.glGetAttribLocation(" + programId + ", " + attributeName + ")");
+
+ loc = GLES20.glGetAttribLocation(programId, attributeName);
+ if (loc < 0)
+ {
+ attrib.setLocation(-1);
+ if (verboseLogging)
+ logger.warning("attribute is invalid in shader: [" + vb.getBufferType().name() + "]");
+ return; // not available in shader.
+ }
+ else
+ {
+ attrib.setLocation(loc);
+ }
+
+ } // if (loc == -2)
+
+ if ((attribs[loc] != vb) || vb.isUpdateNeeded())
+ {
+ // NOTE: Use data from interleaved buffer if specified
+ VertexBuffer avb = idb != null ? idb : vb;
+ avb.getData().clear();
+ avb.getData().position(vb.getOffset());
+
+ if (verboseLogging)
+ logger.info("GLES20.glVertexAttribPointer(" +
+ "location=" + loc + ", " +
+ "numComponents=" + vb.getNumComponents() + ", " +
+ "format=" + vb.getFormat() + ", " +
+ "isNormalized=" + vb.isNormalized() + ", " +
+ "stride=" + vb.getStride() + ", " +
+ "data.capacity=" + avb.getData().capacity() + ")"
+ );
+
+
+ // Upload attribute data
+ GLES20.glVertexAttribPointer(loc,
+ vb.getNumComponents(),
+ convertFormat(vb.getFormat()),
+ vb.isNormalized(),
+ vb.getStride(),
+ avb.getData());
+ checkGLError();
+
+ GLES20.glEnableVertexAttribArray(loc);
+
+ attribs[loc] = vb;
+ } // if (attribs[loc] != vb)
+ }
+ else
+ {
+ throw new IllegalStateException("Cannot render mesh without shader bound");
+ }
+ }
+
+ /**
+ * setVertexAttrib_Array uses Vertex Array
+ * @param vb
+ */
+ public void setVertexAttrib_Array(VertexBuffer vb)
+ {
+ setVertexAttrib_Array(vb, null);
+ }
+
+ public void setAlphaToCoverage(boolean value)
+ {
+ // TODO Auto-generated method stub
+ }
}
[/patch]
1 Like

Nice job !

It will greatly help going forward on Android development.

Iā€™m away for all this week, but I will check it on Samsung Galaxy Tab next week.