OpenGL ES 2.0: Performance drop using bindbuffer frequently - android

I have a performance problem in my opengl 2.0 game. Framerate was good until I made a change in the game. Its some sort of outbreak game with 64 shapes (bricks). What I now want is when the ball hits a brick its not immediately removed - it changes status and that includes changing the texture or more correctly - the uv-coord of the atlas. I have a textureatlas and what I do is just to call GLES20.bindBuffer() for every texture in the loop, instead of calling the outside the loop. Earlier I had the same uv-coord for all shapes but now I change it depending on the bricks status and thats why I need to use binding inside the loop
private void drawShape() {
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboDataListLevelSprites.get(iName).getBuff_id_vertices());
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 0, 0);
//used this snipped before when using the same image (uv-coords) for all bricks
/*GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboDataListLevelSprites.get(iName).getBuff_id_uvs());
GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
GLES20.glVertexAttribPointer(mTextureCoordinateHandle, 2, GLES20.GL_FLOAT, false, 0, 0);
*/
for (Iterator<BrickProperties> it = arrayListBricks.iterator(); it.hasNext(); ) {
BrickProperties bp = it.next();
//now bindbuffer inside loop just too switch uv-coords of the atlas when its time to use another image
int buffIndexVal = bp.get_status_diff();
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, BrickProperties.get_buff_id_uvs()[buffIndexVal]);
GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
GLES20.glVertexAttribPointer(mTextureCoordinateHandle, 2, GLES20.GL_FLOAT, false, 0, 0);
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, bp.getTranslateData()[0], bp.getTranslateData()[1], bp.getTranslateData()[2]);
if (bp.get_status() == 0) {
it.remove();
}
render();
}
}
private void render() {
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, mMVPMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6);
}
I understand the reason to the performance drop is all the bindbuffer calls to the GPU but how could I possibly get around this problem?

It is one thing that you are binding a buffer for every object but then there is another thing you are using 2 buffers to draw a single object. You also have redundant call to unbind the buffer at the start of your render method, simply remove that.
In most cases (may be all cases) you want interleaved vertex data for increased performance. So use
{
position,
textureCoordinates
}
in a single buffer.
I see in your case you have 2 states of the same object where the second one will change vertex coordinates but not position coordinates. It might make sense to share the position data between the two if the buffer is relatively large (which I assume is not). Anyway for such sharing I would suggest you rather use your buffer structure as
{
position,
textureCoordinates,
secondaryTextureCoordinates
}
then use a separate buffer or even to put the secondary texture coordinates to another part of the same buffer.
So if the vertex buffers are relatively small then I suggest you to use "atlas" procedure. For your case that would mean creating twice the size of the buffer and put all the coordinates (having position duplicated) and put this vertex data so that there is one part after another.
I assume you can easily do that for your current drawing and effectively reduce the number of bound buffers to 0 per draw call (you only ned to bind it at some initialization). Now you will have the second part where you will set the attribute pointers for each of the drawn element just so that you may control which texture coordinates are used. This will again present redundant calls which may be avoided in your case:
Since the data structure is consistent in your buffer there is really no reason to set the pointers more the once. Simply set them once to the beginning when buffer is bound and then use the offset in draw call to control what part of the buffer is actually used GLES20.glDrawArrays(GLES20.GL_TRIANGLES, offset, 6).
If done properly your draw method should looks something like:
for (Iterator<BrickProperties> it = arrayListBricks.iterator(); it.hasNext(); ) {
BrickProperties bp = it.next();
Matrix.setIdentityM(mModelMatrix, 0);
Matrix.translateM(mModelMatrix, 0, bp.getTranslateData()[0], bp.getTranslateData()[1], bp.getTranslateData()[2]);
if (bp.get_status() == 0) {
it.remove();
}
Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, mMVPMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, bp.vertexOffset, 6);
}
This removes all the bindings and preserves only matrix operations and draw calls. If there are other drawings in the pipeline you need the buffer binding before the loop otherwise you may put it as a part of the initialization. In both cases the calls should be reduced significantly.
To add a note here it is a common practice to have another object that tracks the openGL states to avoid redundant calls. Instead of calling GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferID.. you would rather call something like contextObject.bindVertexBuffer(bufferID) which would check if the bufferID is the same as in previous call. And if it is then no actual binding would be done. If you create and use such system then it makes little difference on where you call the buffer binding and rest of the object setup since redundant calls will have no effect. Still this procedure alone will not make your situation optimal so you still need both.

You could bind buffer and draw all the objects with the same UV-s at once. Instead of iterating through each brick, iterate through all objects that use the same UV-s.
Also, try batching the objects so that you draw them all at once. Using Index Buffer Objects may help in this.

Related

how to assign one texture to another efficiently in OpenGL ES for Android

I want to copy texture1 to texture2. The background is that I generate 15 empty texture id and bind them to GLES11Ext.GL_TEXTURE_EXTERNAL_OES, the following is my code:
int[] textures = new int[15];
GLES20.glGenTextures(15, textures, 0);
GlUtil.checkGlError("glGenTextures");
for(int i=0;i<15;i++)
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[i]);
then I always transfer camera preview to textures[0], I want copy the texture from textures[0] to textures[1] in order to keep the frame content of timestamp 1, and copy the texture from textures[0] to textures[2] in order to keep the frame content of timestamp 2... it looks like buffer some texture data in GPU and render some of that in the future. So I want to know is there anyway to do this? And can I just use textures[2]=textures[0] to copy texture data?
There isn't a very direct way to copy texture data in ES 2.0. The easiest way is probably using glCopyTexImage2D(). To use this, you have to create an FBO, and attach the source texture to it. Say if srcTexId is the id of the source texture, and dstTexId the id of the destination texture:
GLuint fboId = 0;
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, srcTexId, 0);
glBindTexture(GL_TEXTURE_2D, dstTexId);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, width, height, 0);
That being said, from your description, I don't believe that this is really what you should be doing, for the following reasons:
I don't think copying texture data as shown above will work for the external textures you are using.
Copying texture data will always be expensive, and sounds completely unnecessary to solve your problem.
It sounds like you want to keep the 15 most recent camera images. To do this, you can simply track which of your 15 textures contains the most recent image, and treat the list of the 15 texture ids as a circular buffer.
Say initially you create your texture ids:
int[] textures = new int[15];
GLES20.glGenTextures(15, textures, 0);
int newestIdx = 0;
Then every time you receive a new frame, you write it to the next entry in your list of texture ids, wrapping around at 15:
newestIdx = (newestIdx + 1) % 15;
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[newestIdx]);
// Capture new frame into currently bound texture.
Then, every time you want to use the ith frame, with 0 referring to the most recent, 1 to the frame before that, etc, you bind it with:
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[(newestIdx + i) % 15]);
So the textures never get copied. You just keep track of which texture contains which frame, and access them accordingly.

Android OpenGL ES 2.0: Sphere Texture Mapping

I have created 2 sphere's, and I am currently trying to texture them. However the texture coordinates seem to be a bit off.
The Texture (Just supposed to be a window or some kind of break):
The result of the texture being applied to both spheres (You can ignore the black lines not around the square, that's just the camera - yes it's an AR app):
Now I exported the Sphere using a perl script I found that converts obj files to a list of vertices and texture coords OBJ2OPENGL. And the obj file has been converted with 2160 Vertices.
When I render the sphere:
GLES20.glVertexAttribPointer(vertexHandle, 3, GLES20.GL_FLOAT,
false, 0, ufo_sphere.getVertices());
GLES20.glVertexAttribPointer(normalHandle, 3, GLES20.GL_FLOAT,
false, 0, ufo_sphere.getNormals());
GLES20.glVertexAttribPointer(textureCoordHandle, 3,
GLES20.GL_FLOAT, false, 0, ufo_sphere.getTexCoords());
GLES20.glEnableVertexAttribArray(vertexHandle);
GLES20.glEnableVertexAttribArray(normalHandle);
GLES20.glEnableVertexAttribArray(textureCoordHandle);
// activate texture 0, bind it, and pass to shader
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,
mTextures.get(textureIndex).mTextureID[0]);
GLES20.glUniform1i(texSampler2DHandle, 0);
// pass the model view matrix to the shader
GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false,
modelViewProjection, 0);
//GLES20.glDrawElements(GLES20.GL_TRIANGLES, sphere.getNumObjectVertex(), GLES20.GL_UNSIGNED_SHORT, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, sphere.getNumObjectVertex()); // I have tried TRIANGLES and TRIANGLE_FAN as well
// disable the enabled arrays
GLES20.glDisableVertexAttribArray(vertexHandle);
GLES20.glDisableVertexAttribArray(normalHandle);
GLES20.glDisableVertexAttribArray(textureCoordHandle);
I can see at least one thing which seems to be wrong...
GLES20.glVertexAttribPointer(textureCoordHandle, 3,
GLES20.GL_FLOAT, false, 0, ufo_sphere.getTexCoords());
Texture coordinates typically have a size of 2 (u, v). So you could try this instead:
GLES20.glVertexAttribPointer(textureCoordHandle, 2,
GLES20.GL_FLOAT, false, 0, ufo_sphere.getTexCoords());
Also, in the link you provided they use the GL_TRIANGLES format instead of GL_TRIANGLE_STRIP so you should switch back to that.

Android openGL attempt to increase FPS by using the same vertices for multiple meshes

I am developing a game where similar objects are rendered on the screen. The problem is that every time a new object is added, more vertices needs to be handled, so FPS decrease.
Since I am a beginner with openGl, I have some general ideas about improving performance, I just don't know what openGl allows me to do.
Since most of my objects are identical(same number of vertices, same colors, same uvs and same normals), but only the position and rotation of the objects are different, is it possible to send the vertices for similar objects only once, on each frame render , and then call drawElements for all objects and modify only the objects positions?
Is it possible to go even further, and upload those similar vertices, only once when the scene is 1st created, and on each frame render just call drawElements for each object?
Currently, I am sending the vertices for each object like this:
.......
gl.glNormalPointer(GL10.GL_FLOAT, 0, normals);
gl.glColorPointer(4, GL10.GL_UNSIGNED_BYTE, 0, colors);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertices);
gl.glPushMatrix();
gl.glTranslatef(position);
gl.glRotatef(rotation);
gl.glScalef(scale);
gl.glDrawElements(renderType,nr,GL10.GL_UNSIGNED_SHORT, faces);
gl.glPopMatrix();
........
I am thinking I should do someting like this:
.......
gl.glNormalPointer(GL10.GL_FLOAT, 0, normals);
gl.glColorPointer(4, GL10.GL_UNSIGNED_BYTE, 0, colors);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertices);
for(eachObject)
{
gl.glPushMatrix();
gl.glTranslatef(position);
gl.glRotatef(rotation);
gl.glScalef(scale);
gl.glDrawElements(renderType,nr,GL10.GL_UNSIGNED_SHORT, faces);
gl.glPopMatrix();
........
}
Yes
Yes (even better)
Your second code example looks exactly like what you should be doing.
There's no reason you'd need to upload the vertices more than once, doing it on initialization should be sufficient. After that just set the pointers any time you want to render. You can call glDrawElements as many times as you want with different translations/rotations.

Using VBOs/IBOs in OpenGL ES 2.0 on Android

I am trying to create a simple test program on android (API 10) using OpenGL ES 2.0 to draw a simple rectangle. I can get this to work with float buffers referencing the vertices directly, but i would rather do it with VBOs/IBOs. I have looked for countless hours trying to find a simple explanation (tutorial), but have yet to come across one. My code compiles and runs just fine, but nothing is showing up on the screen other than the clear color.
Here are some code chunks to help explain how I have it set up right now.
Part of onSurfaceChanged():
int[] buffers = new int[2];
GLES20.glGenBuffers(2, buffers, 0);
rectVerts = buffers[0];
rectInds = buffers[1];
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, rectVerts);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, (rectBuffer.limit()*4), rectBuffer, GLES20.GL_STATIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, rectInds);
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, (rectIndices.limit()*4), rectIndices, GLES20.GL_STATIC_DRAW);
Part of onDrawFrame():
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, rectVerts);
GLES20.glEnableVertexAttribArray(0);
GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false, 0, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, rectInds);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_INT, 0);
I don't see anything immediately wrong, but here's some ideas you can touch on.
1) 'Compiling and running fine' is a useless metric for an opengl program. Errors are reported through actively calling glGetError and checking compile and link status of the shaders with glGet(Shader|Program)iv. Do you check for errors anywhere?
2) You shouldn't be assuming that 0 is the correct index for vertices. It may work now but will likely break later if you change your shader. Get the correct index with glGetAttribLocation.
3) You're binding the verts buffer onDraw, but I don't see anything about the indices buffer. Is that always bound?
4) You could also try drawing your VBO with glDrawArrays to get the index buffer out of the equation, just to help debugging to see which part is wrong.
Otherwise what you have looks correct as far as I can tell in that small snippet. Maybe something else outside of it is going wrong.

Matrix Palette, Drawing Issue when ModelView Matrix

Using OpenGL 1.1 and the Matrix Palette extension. The issue I'm having, is that not every model being loaded needs to be animated, so I don't think that I need to enable those client states nor provide weights or weight index arrays. For example, I'm trying something like this during my drawing code...
glMatrixMode(GL_MATRIX_PALETTE_OES);
glBindBuffer(GL_ARRAY_BUFFER, dataBuffers[0]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dataBuffers[1]);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_WEIGHT_ARRAY_OES);
glEnableClientState(GL_MATRIX_INDEX_ARRAY_OES);
//Code to modify the palettes... works fine...
for(i = 0; i < mech.boneCount; ++i){
glCurrentPaletteMatrixOES(i);
glLoadPaletteFromModelViewMatrixOES();
GenerateBoneMatrixPalette(bones, i);
}
glVertexPointer(3, GL_FLOAT, sizeof(VertexData), (char*)(NULL + 0));
glNormalPointer(GL_FLOAT, sizeof(VertexData), (char*)(NULL + 12));
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(VertexData), (char*)(NULL + 24));
glWeightPointerOES(1, GL_FLOAT, sizeof(VertexData), (char*)(NULL + 28));
glMatrixIndexPointerOES(1, GL_UNSIGNED_BYTE, sizeof(VertexData), (char*)(NULL + 32));
glDrawElements(GL_TRIANGLES, mech.indexsize, GL_UNSIGNED_SHORT, (char*)(NULL + 0));
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_WEIGHT_ARRAY_OES);
glDisableClientState(GL_MATRIX_INDEX_ARRAY_OES);
glBindBuffer(GL_ARRAY_BUFFER, dataBuffers[2]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dataBuffers[3]);
glMatrixMode(GL_MODELVIEW);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
//pardon the hard 28 value here, it's the correct offset for this test
glVertexPointer(3, GL_FLOAT, 28, (char*)(NULL + 0));
glNormalPointer(GL_FLOAT, 28, (char*)(NULL + 12));
glColorPointer(4, GL_UNSIGNED_BYTE, 28, (char*)(NULL + 24));
glDrawElements(GL_TRIANGLES, indexsize, GL_UNSIGNED_SHORT, (char*)(NULL + 0));
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
But it is not working. The first section displays correctly but the second does not display at all. If I add an, what feels to be, unnecessary weight and weight index element to the second section modifying the stride as needed and not setting the matrix to the modelview... then it displays what I expect.
The test that feels the strangest, is if I do not enable the Matrix Palette extension at all and only draw the second part, it works just fine. However, just enabling the Matrix Palette extension causes the second section to not work at all, making it seem that I can not draw simply while the mode is set to the ModelView Matrix... though this seems somewhat unusual as the ModelView still absolutely is affected by transformations.
So... is it possible to switch to and draw while the ModelView is the current matrix while using this extension? Or must I make use/reuse a single palette to make it work.
I haven't found an OES matrix palette extension in the extension registry, but an ARB extension and suppose it works similar. In this extension, you have to enable GL_MATRIX_PALETTE and or GL_VERTEX_BLEND (with glEnable) to use matrix palette skinning and disable it to not use it.
But the glMatrixMode does't have anything to do with enabling or disabling it. It just selects the matrix to which matrix modification functions apply (like glLoadIdentity, glTranslate, ...).
EDIT: After googling this extension (I have no ES experience) I found, that you definitely have to enable GL_MATRIX_PALETTE_OES to use it (via glEnable) and then disable it again for your second part to not use it. As I've written above, glMatrixMode doesn't do what you thought it to.

Categories

Resources