I have an Android app which uses a bit of OpenGL running on a Galaxy Nexus with ICS. After turning on hardware acceleration and using my OpenGL activity some of those textures are stolen by the system and now in my listviews and other UI elements. Its as if my GL pointers obtained via GLES20.glGenTextures are not actually fresh pointers but rather overwriting ones used by the window renderer.
In any case there should be some sort of firewall or sandbox between the OS screen drawing system and my app, no?
Turning off hardwareAcceleration entirely displays fine, but the UI is choppy (and buttery smooth on 2.3 and lower either way). Turning it off/on activity by activity doesn't help either.
Screenshot (before/after)
Normally a repeating bitmap drawable, now an image (from camera in this case) I loaded into OpenGL in a different activity - http://a.yfrog.com/img532/9245/t81k.png
Gen/Load texture
int[] image = new int[1];
GLES20.glGenTextures(1, image, 0);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_LINEAR);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0);
Basic draw
GLES20.glViewport(0, 0, width, height);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
GLES20.glUseProgram(shader.program);
GLES20.glUniform1i(GLES20.glGetUniformLocation(shader.program, "imageTexture"), 0);
GLES20.glVertexAttribPointer(Attributes.VERTEX, 2, GLES20.GL_FLOAT, true, 0, squareVertices);
GLES20.glEnableVertexAttribArray(Attributes.VERTEX);
GLES20.glVertexAttribPointer(Attributes.TEXTUREPOSITON, 2, GLES20.GL_FLOAT, true, 0, textureVertices);
GLES20.glEnableVertexAttribArray(Attributes.TEXTUREPOSITON);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, image[0]);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
Turns out I was calling GL commands on the UI thread at certain times. This interfered with the apps hardware accelerated display as suggested by Romain Guy at this post.
Related
Alright I made an application that uses a FBO to render to a texture and later uses this texture to display it on a quad.
That's the code I am using to initialize the FBO and the texture:
IntBuffer intBuffer = IntBuffer.allocate(1);
GLES20.glGenFramebuffers(1, intBuffer);
fbo = intBuffer.get(0);
GLES20.glGenTextures(1, intBuffer);
tex = intBuffer.get(0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbo);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, tex, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
(1. code in onDrawFrame())
The problem is that I don't know where to put the code, so at first I put the code in the onDrawFrame() method. When I do that the texturing works fine and I get the result that I want. But since I am creating a new FBO and texture every frame, after some time The application starts to lag and at the end the emulator crashes.
(2. code in onSurfaceChanged())
After noticing that behaviour I decided to put the code in the onSurfaceChanged() method. This time the application didn't crash but the entire texture is black. I tried to clear the texture or draw a white quad over it but nothing worked. And when I try to get an error from GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) or GLES20.glGetError() it says that everything is fine.
I also tried to put the code in the onDrawFrame() method surrounded with a if-clause so that it's only executed once. The result is also the black texture.
(3. code in onDrawFrame() and onSurfaceChanged())
The last thing I tried was to put the code in both methods. I wanted to reduce the code in the onDrawFrame() method until I find the codeline that makes the texture work fine. But when I started the application the quad flickered, changing from the black texture to the right texture.
There is no problem with the texture displaying since it works with the one method. I also tried to add a depth-buffer to the code but the result is the same. So since I don't need it in my application I commented it out.
Is there anyone who can say what's going on? If someone has a application that works but doesn't know what's the problem with mine it would be nice if that person could upload the project because I didn't find a tutorial that has a android project up for download.
Alright thanks to Rabbid76 for giving me a checklist to find my problem. I tried to use the fbo as a texture. This is the right binding: GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex). In the tutorial I have read the FBO was also bound. I still don't know why it works when I initialize the FBO in the onDrawFrame() method and use it then as texture.
I am mapping a 360 degree photospheric image on a sphere with camera at centre of sphere. When we build the application using native Android SDK and OpenGL ES 2.0, we see jaggy edges like the one shown in below image. The jags are visible on the arms of sofa, edges of floor etc. when I see this view using cardboard device
On the other hand, the same image ( 4096X2048 resolution) is rendered perfectly in unity3d application for Android.
The magnification and minification filter for the texture that we are using is GL_LINEAR.
Source Code used for setting filtering paramenters for texture and generating Texture and mipmap:
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mSphereTextureIds[0]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
// Load the bitmap into the bound texture.
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
Code for Multisample Antialiasing ( 4X MSAA)
cardboardView.setEGLConfigChooser(new MyConfigChooser());
cardboardView.setRenderer(renderer);
I have also turned on 4x Multisample anti aliasing. But even after doing this, there are jags inside texture. I used highp precision for both float and int in both of my shaders.
class MyConfigChooser implements CardboardView.EGLConfigChooser {
#Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int attribs[] = {
EGL10.EGL_LEVEL, 0,
EGL10.EGL_RENDERABLE_TYPE, 4, // EGL_OPENGL_ES2_BIT
EGL10.EGL_COLOR_BUFFER_TYPE, EGL10.EGL_RGB_BUFFER,
EGL10.EGL_RED_SIZE, 8,
EGL10.EGL_GREEN_SIZE, 8,
EGL10.EGL_BLUE_SIZE, 8,
EGL10.EGL_DEPTH_SIZE, 16,
EGL10.EGL_SAMPLE_BUFFERS, 1,
EGL10.EGL_SAMPLES, 2, // This is for 4x MSAA.
EGL10.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] configCounts = new int[1];
egl.eglChooseConfig(display, attribs, configs, 1, configCounts);
if (configCounts[0] == 0) {
// Failed! Error handling.
Log.d("test","MSAA Failed............");
return null;
} else {
return configs[0];
}
}
Is there some post processing that Unity does on the textures for filtering during magnification that gives it a better look without aliasing effect ?
Can we do anisotropic filtering to smoothen the texture on magnification without blurring on Android ? If yes can we get a clue on how to do that ?
Is 4X the maximum MSAA that is currently supported on Android ? (I am using Nexus 5).
Thanks in advance
Apurv Nigam
Can we do anisotropic filtering to smoothen the texture on
magnification without blurring on Android?
Not in general - it's "really expensive" so not much mobile hardware supports it.
If yes can we get a clue on how to do that?
There is no support for it in "official" OpenGL ES; check the vendor extensions to see if one exists. For desktop GL it is supported via the GL_EXT_texture_filter_anisotropic extension - I've not seen a mobile GPU supporting it.
Is 4X the maximum MSAA that is currently supported on Android? (I am
using Nexus 5).
Depends on vendor - some devices support 8x (Mali-T760), some older devices don't support it at all (Tegra-1/2/3) - so YMMV.
I found the answer to my problem. I figured out that the image was being scaled down by Universal Image Loader library being used by me. So the bitmap that was being given to texImage2D call was already scaled down causing a pixelated texture. So I decoded my image using BitmapFactory using isScaled=false option.
I just heard from a user who says that my (Android OpenGL ES 2.0) app (a game) won't run on his HTC 1X+ handset. All he gets is the music and the banner ad at the top of the screen and nothing else.
Unfortunately I don't have an HTC 1X+ to test this on.
Some notes:
Although my textures are not power of 2, I'm only using GLES20.GL_CLAMP_TO_EDGE
From what I've read, the HTC 1X+ has a max Texture Size of 2048 x 2048 and it gets it's resources from the XHDPI folder (annoyingly), even so, I have only 1 texure that exceeds that size, all other objects displayed on my app's opening page use textures much smaller than this max amount, so something should be displayed.
I'm not using texture compression of any kind
My app runs quite happily on the 15 (aprox) other devices I, and others have tested it on - just the 1x (so far) is giving problems.
Can anyone point out some common issues with OpenGL ES 2.0 that could be causing these textures not to be rendered? Are there any quirks with certain Android versions or devices?
I haven't yet posted any code simply because the app works on most devices, and I'm not sure which parts of the code would be helpful, but if any code is required, please just ask.
Edit - including texture loading code
public static int LoadTexture(GLSurfaceView view, Bitmap imgTex){
//Array for texture
int textures[] = new int[1];
try {
//texture name
GLES20.glGenTextures(1, textures, 0);
//Bind textures
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
//Set parameters for texture
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
//Apply texture
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, imgTex, 0);
//clamp texture
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);
} catch (Exception e){
}
//Increase texture count
textureCount++;
//Return texture
return textures[0];
}
Have you checked the sampler properties?
The settings look correct, but why not also specify the GL_TEXTURE_WRAP_S setting? Use GLES20.glTexParameteri for integer values.
What type of bitmap are you using?
Try to force the internal format to GL_RGBA, GL_BGR, GL_RGB
Do you properly unbind or bind to a different texture?
Other texture settings may be causing havoc in other parts of the code..
Do you specify the correct texture unit in your shader?
Print out the shader's sampler attribute position so you know this is correct, and make sure to bind the texture to it explicitly during rendering.
I'm using RenderBuffer and OpenGL instead of Canvas and Bitmap on Android Java. However, drawing to a texture takes time. I referred to this question and tried speed improvement (Reduces the call of glEnableVertexAttribArray, glClearColor, glBindFramebuffer), but it's ineffective. Is this limitations of GPU and Android OS?
*This is an extract of the program.
GLES20.glAttachShader(program, vertex);
GLES20.glAttachShader(program, fragment);
GLES20.glLinkProgram(program);
GLES20.glUseProgram(program);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[i]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, s, s,
0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbs[i]);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER,
GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, textures[i], 0);
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, renders[i]);
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, s, s);
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER,
GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, renders[i]);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbs[i]);
GLES20.glClearColor(fr, fg, fb, fa);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, old, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbs[pos]);
GLES20.glViewport(0, 0, ww, hh);
:
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
:
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, old[0]);
GLES20.glViewport(0, 0, scw, sch);
If, as I understand, you use all the code you pasted each time you need to draw something, it's not a surprise that your program is running slow.
OpenGL (wheter ES or not) is based on a "state machine" model: the OpenGL context retains the changes you make to it (using the various glXXX commands), and modify the way OpenGL will react to future commands (most notably to the ones you use for drawing).
This means that when you issue the command:
GLES20.glClearColor(fr, fg, fb, fa);
what you are really doing is set the
GL_COLOR_CLEAR_VALUE
variable inside the state machine to [fr, fg, fb, fa]. From that moment on, GL_COLOR_CLEAR_VALUE is already set to [fr, fg, fb, fa], and every extra call to glClearColor with the same arguments will change nothing in the machine.
OpenGL ES 2.0 glClearColor reference page
Why am I going into this? Because OpenGL state changes are performance critical (and some are more than others): changing the state of the context has a performance hit that often goes beyond a simple variable assignment, as it may require OpenGL to "talk" to the GPU, discard caches and whatnot. It's a necessary evil when done in a setup stage, but frequent changes might have a huge performance hit when done every frame.
Therefore the first thing you should do is to minimize the sheer number of glXXX command per second in your program by creating an initialization method and a drawframe method.
The standard approach is to use the GLSurfaceView.Renderer class
Android GLSurfaceView.Renderer class reference
cram all initialization commands into
public abstract void onSurfaceCreated (GL10 gl, EGLConfig config)
and
public abstract void onSurfaceChanged (GL10 gl, int width, int height)
and put in the method:
public abstract void onDrawFrame (GL10 gl)
the least possible amount of OpenGL commands (just what you need to actually draw stuff)
Leaving the theory aside and going into the specific, here is a list of optimizations I would suggest you to consider:
if you are using a single program, leave the whole:
GLES20.glAttachShader(program, vertex);
GLES20.glAttachShader(program, fragment);
GLES20.glLinkProgram(program);
GLES20.glUseProgram(program);
block in the setup section (and even if you don't, just switch between programs with glUseProgram: you don't have to link it over and over again!). glLinkProgram is likely to have a big performance hit!
texture initialization goes in the setup (on top of the "state change" thing you are actually moving the same very data from CPU memory to GPU memory over and over again, each frame)
framebuffer initialization (with all the renderbuffer creation and binding) goes in setup as well. Besides, why do you need multiple framebuffers anyway? :)
glViewport goes in the onSurfaceChanged method
it seems to me that you are using different framebuffers to draw different objects: I'm not sure of what you are trying to accomplish, but I'd suggest you to change strategy: draw to the whole screen and on the same buffer, unless you have a very good reason to do otherwise.
Last but not least, here is a good book on the topic:
OpenGL ES 2.0 Programming Guide
it's both a good introduction to OpenGL ES 2.0 and a nice introduction to OpenGL in general (I don't get a cut on their sales ;-) )
Hope it helps
I am programming an Android 2d game using opengl es 2.0. After I draw my sprites to the backbuffer I draw lights to a FBO and try to blend it to the back buffer again.
When I draw the FBO to the framebuffer, even trasparent without any color, the framerates drops from 60 to 30 on a Samsung Galaxy w (it has an adreno 205 as gpu). I searched everywhere and tried everything, even if I draw a single sprite on the scene and blend a trasparent FBO texture to the screen the framerate drops. I tried other games with lighting effects on that phone and they run fine, almost every game is fine on that phone, I believe they use the framebuffer as well.
On the Galaxy SII (mali 400 gpu) runs fine, I am quite new to opengl so I believe I am making a mistake somewhere, I share my code.
// Create a framebuffer and renderbuffer
GLES20.glGenFramebuffers(1, fb, offset);
GLES20.glGenRenderbuffers(1, depthRb, offset);
// Create a texture to hold the frame buffer
GLES20.glGenTextures(1, renderTex, offset);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTex[offset]);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA,
screenWidth, screenHeight, 0,
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
null);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_LINEAR);
//bind renderbuffer
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, depthRb[offset]);
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16,
screenWidth, screenHeight);
// bind the framebuffer
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fb[offset]);
// specify texture as color attachment
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, renderTex[offset], 0);
// specify depth_renderbufer as depth attachment
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT,
GLES20.GL_RENDERBUFFER, depthRb[0]);
// Check FBO status.
int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
if ( status == GLES20.GL_FRAMEBUFFER_COMPLETE )
{
Log.d("GLGame framebuffer creation", "Framebuffer complete");
}
// set default framebuffer
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
I do this once on surface creation. Not sure if is correct. I keep the texture and framebuffer ids to switch to them when I need.
My drawing code:
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
ShaderProgram program = glgame.getProgram();
//put vertices in the floatbuffer
mTriangleVertices.put(vertices, 0, len);
mTriangleVertices.flip();
GLES20.glVertexAttribPointer(program.POSITION_LOCATION, 2, GLES20.GL_FLOAT, false,
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
//preparing parameter for texture position
mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
GLES20.glEnableVertexAttribArray(program.POSITION_LOCATION);
//preparing parameter for texture coords
GLES20.glVertexAttribPointer(program.TEXTURECOORD_LOCATION, 2, GLES20.GL_FLOAT,
false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES,
mTriangleVertices);
//set projection matrix
GLES20.glEnableVertexAttribArray(program.TEXTURECOORD_LOCATION);
GLES20.glUniformMatrix4fv(program.MATRIX_LOCATION, 1, false, matrix, 0);
//draw triangle with indices to form a rectangle
GLES20.glDrawElements(GLES20.GL_TRIANGLES, numSprites * 6, GLES20.GL_UNSIGNED_SHORT,
indicesbuf);
//clear buffers
mTriangleVertices.clear();
mVertexColors.clear();
Everything is rendered on screen correctly, but the performance are ruined just when I draw the FBO texture.
Thank you very much for your help. I worked very hard on this and didn't find a solution.
According to qualcomm docs, you need to glclear after every glbindframebuffer, this is a problem related to tiled architecture, if you are switching framebuffers, data need to get copied from fastmem to normal memory to save current framebuffer and from slowmem to fast mem to get contents of newly binded frame, in case you are clearing just after glbind no data is copied from slowmem to fastmem and you are saving time, but you need to redesign your render pipeline often, so it will avoid reading data back and forth between slow and fast memory, so try to do glclear after each bind and it should help, you can also use adreno profiler to get additional information about problematic calls, but i doubt it will help with adreno200 i am trying to get two buffers for blur and i am ending with 10fps, bindframebuffer call can take up to 20msec if its not cleared, if it is it should end at 2ms.