I am having problems with texture corruption on Android emulator (it runs fine on most android devices).
The picture above is a reference rendering produced by emulator running Android 4.1 Jelly Bean, everything looks as it should.
The second picture is captured in emulator running Android 1.6. Note the corruption of some of the disabled toolbar buttons (they are rendered with color 1f,1f,1f,0.5f)
The third picture is captured in the same emulator. The difference is that now score is rendered in the upper-right corner. Score is a bitmap font, it's texture is an alpha mask. Everything rendered after the score looses it's texture. Note that the previous screenshot also contained bitmap font rendered the same way (but using different texture).
A similar problem was present on one of the Samsung devices (I don't remember the model). When the floor texture was rendered, everything rendered after that lost texture. The problem did not manifest itself when I either a) did not bind the texture b) did bind the texture, but drew no triangles using it c) recreated the png asset from scratch.
Opengl settings:
gl.glDisable(GL10.GL_LIGHTING);
gl.glDisable(GL10.GL_CULL_FACE);
gl.glDisable(GL10.GL_DEPTH_TEST);
gl.glDisable(GL10.GL_DITHER);
gl.glDepthMask(false);
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glBlendFunc(GL10.GL_ONE,GL10.GL_ONE_MINUS_SRC_ALPHA);
gl.glShadeModel(GL10.GL_FLAT);
gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE);
How textures are loaded:
public void doGLLoading(Engine renderer) {
GL10 gl=renderer.getGl();
int[] ids=new int[1];
gl.glGenTextures(1, ids,0);
id=ids[0];
gl.glBindTexture(GL10.GL_TEXTURE_2D, id);
Log.d("SpriteDraw", String.format("Texture %s has format %s",getPath(),bitmap.getConfig().toString()));
buildMipmap(gl, bitmap);
gl.glTexParameterf(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_MIN_FILTER, minFilter);
gl.glTexParameterf(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_MAG_FILTER, magFilter);
gl.glTexParameterf(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_WRAP_S, textureWrapS);
gl.glTexParameterf(GL10.GL_TEXTURE_2D,GL10.GL_TEXTURE_WRAP_T, textureWrapT);
}
private void buildMipmap(GL10 gl, Bitmap bitmap) {
int level = 0;
int height = bitmap.getHeight();
int width = bitmap.getWidth();
while (height >= 1 || width >= 1) {
// First of all, generate the texture from our bitmap and set it to
// the according level
//TextureUtils.texImage2D(gl, GL10.GL_TEXTURE_2D, level, -1, bitmap, -1, 0);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0);
if (height == 1 || width == 1) {
break;
}
// Increase the mipmap level
level++;
height /= 2;
width /= 2;
Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height,
true);
// Clean up
bitmap.recycle();
bitmap = bitmap2;
}
}
Notes: the font is rendered using gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); and GL10.glDrawArrays. The corruption affects not only the 1.6 emulator, but also the 2.x series of android, althought it is not as prominent (the alpha masks are still rendered incorrectly). All assets are correctly loaded as power of two bitmaps.
I suggest using 32bit textures, not grayscale, not "alphamasks" (what's that?)
Check the size of your texture, it should not exceed maximum size ( glGetInteger( GL_MAX_TEXTURE_SIZE ). Also make shure your textures are power of 2. Yes, you mentioned before they are, but if they are in scalable assets (drawable_x_dpi folders), they will be scaled by android. To avoid scaling, put them to "raw" folder.
Just for test, try to disable all filtering, including mipmaps - set GL_TEXTURE_WRAP_S, and GL_TEXTURE_WRAP_T to GL_NEAREST
Related
Within my Android App, which is an OpenGL ES 2.0 game, I've included 4 sets of graphic resources like so
ldpi
mdpi
hdpi
xhdpi
Now, within my xhdpi folder, the largest asset I have is 2560 x 1838 as I'm targeting large screens here (for example the Google Nexus 10 tablet which gets its resources from the XHDPI folder). Up until now, everything has worked on the devices on which I've tested (Google Nexus 10, Samsung Galaxy S3, S4 & S5, Nexus 4 & 5 etc etc....)
I've recently heard from a user who has encountered problems running this on an HTC One X+ handset (I believe there are other reasons for this besides the one I'm talking about here so I have a separate question open for the other issues).
The thing is, according to: this, the maximum texture size for the this phone is 2048x2048, but then according to this this phone gets its resources from the XHDPI folder.
So, it won't display the textures from this atlas (it's an atlas of backgrounds containing 6 separate backgrounds of 1280*612.
Now I have two options that I am aware of to fix this:
Reduce the size of the backgrounds. Ruled out as this would compromise quality on larger-screen devices (like the nexus 10)
Split into 2 atlases would rather not do this as would like to keep all backgrounds in 1 atlas to optimise loading speed (and just to keep things tidy)
Are there any other options? Am I able to provide, within a sub folder of xhdpi another folder that fits within the 1X+'s limits? And if so, how do I get the device to grab resources from there?
I'm doing this blind as I don't have an 1X+ on which to test this, but from what I've read, I believe having assets larger than 2048 x 2048 is going to cause problems on this device.
This is my code for applying textures:
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);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE);
} catch (Exception e){
}
//Increase texture count
textureCount++;
//Return texture
return textures[0];
}
If you don't know if a texture load will work, you can do all the same operations, except use GL_PROXY_TEXTURE_2D (or 1D, 3D, etc.) instead of GL_TEXTURE_2D to check to see if the load will work for a given texture size and parameters. OpenGL attempts to perform the load, and and it will set all texture state to 0 if it doesn't work or there is another problem in a texture parameter. Then if the texture load fails (in your case due to the image being too big), have your program load a smaller scaled texture.
EDIT: I use iOS and my gl.h doesn't include GL_PROXY_TEXTURE_* and I can't find any reference in the OpenGL ES3 specification, so I'm not sure this will work for you with OpenGL.
Alternatively, get your max texture size (per dimension, e.g. width, height or depth) and load a suitable image sized using:
glGetIntegerv( GL_MAX_TEXTURE_SIZE, &size );
Afterward, check your image to ensure it worked.
GLuint name;
int width = 10000; // really bad width
int height = 512;
GLint size;
glGenTextures( 1, &name );
glBindTexture( GL_TEXTURE_2D, name );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, 0);
GLenum error = glGetError();
if( error ) {
printf("Failed!");
}
else {
printf("Succeeded!");
}
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.
Honestly, I don't like to ask things this way, but I have no clue about this one!
Have you seen this before??
It can be seen that the image is scrambled following some defined pattern. This happens only in some (low end) devices, with Non Power of two images (FBO). It works well on other devices.
What I do, is to load an Android Bitmap to a FBO (works OK, as it shows ok on the screen). I do some editing (I paste a sticker, which in the image seems to be in the right place), and finally save the FBO into a Bitmap again. It works ok for a 512x512 FBO (the FBO has the image size), but no for that one (507x800).
Any Ideas??? I don't post code because I have no clue, please tell me and I'll add it.
This is the GL call to retrieve info from FBO
public Buffer toPixelBuffer(){
final int w = this.getWidth(); //colorTexture width
final int h = this.getHeight();
final ByteBuffer pixels = BufferUtils.newByteBuffer(w*h * 4);
Gdx.gl.glPixelStorei(GL10.GL_PACK_ALIGNMENT, 1);
Gdx.gl.glReadPixels(0,0, w, h, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE, pixels);
pixels.clear();
return pixels;
}
I also don't have a buggy device with me to test right now :(
Thank you!
I had the exact same problem. I experienced this on Galaxy Ace, Galaxy Y, and some other devices.
After lot of testing I did found out that it wasn't even required POT textures, so keeping the texture size with a 64 pixel increment made the trick. So lets say if I have a 122x53 texture, I need to convert it to 128x64. An so on.
Next is the function I use to get a valid texture dimension. Call it for both Width and Height.
/**
* Some GPUs such as the "VideoCore IV HW" on the Samsung Galaxy Ace
* require texture (FBO) sizes to be in '64' increments (WTF!!!!)
*
* #param dimension
* Base dimension to calculate
* #return Resolved 64 dimension
*/
public static int calculate64Dimension(final int dimension)
{
return (((dimension - 1) >> 6) << 6) + 64;
}
Due to some unfathomable reason, the textures won't load.
Here's the method
public static void loadGLTexture(GL10 gl, Context context){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inDensity = 240;// needed so that the image will be 512x512
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.glasstexture, options);
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Log.i("GridLoginSquare.loadGLTexture 96", "Bitmap:{w:" + width + " h:" + height + "}");
gl.glGenTextures(1, textures, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
// create nearest filtered texture
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
// Use Android GLUtils to specify a two-dimensional texture image from our bitmap
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); //error is on this line
Log.e("GridLoginSquare.loadGLTexture 102", " ERR "+gl.glGetError());
// Clean up
bitmap.recycle();
}
The size of my texture image is 405 x 512
The line Log.e("GridLoginSquare.loadGLTexture 102", " ERR "+gl.glGetError()); gives
me a 1281 error which (after investigating others who had similar problems) means
INVALID_VALUE
The above code is a part of the overall GridLoginSquare class which is here
NOTE This problem had already persisted before BitmapFactory.Options density was included to scale the image. Similarly the texture uv was divided by 405 and 512 when this was included in the gist code.
I have had this problem when I did not put my textures in the drawable-nodpi folder. Try moving your textures into that folder.
Also, OpenGL prefers your textures to have dimensions that are powers of two. I am not sure if this would cause the textures to not appear at all, but you may as well try making your texture images 512x512 or 256x256 just to see.
I had exactly the same error at textImage2D with glGetError returning 0, and WindyB's solution of using images whose width & length are powers of two... DID WORK form me! Thank you very much! :)
This error took place only in Android 2 (exactly Android 2.3.3). In Android 4 it always worked fine (I like to test my Apps in different Android versions).
By the way, I had no need of creating folder 'drawable-nodpi', I have it all stacked in just 'drawable'
In case it helps: At http://developer.android.com/guide/topics/graphics/opengl.html#manifest under 'Declaring OpenGL Requirements' it says that some texture compression formats are not compatible with some devices.. Maybe your problem has something to do with this?
I have my textures on drawable-nodpi folder (which we have to create) and was getting the same error 1281 after call this function: GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0);
And it was being caused because my textures wasn't at power of 2, so after I fix it (leave it at power of 2), it works fine, only leaving a texture bleeding (when a tiny undesired part of the texture appears on the borders), but this is solved adjusting the values of the uvs coordinates.
I'm creating my own mipmaps when creating textures, and as soon as I enable mipmaps via GL_LINEAR_MIPMAP_NEAREST (or anything MIPMAP), the textures are just a very dark, blurry mess. If I switch to GL_LINEAR, they're fine.
Here's how I'm creating the texture:
glGenTextures(1, &m_TextureId);
glBindTexture( GL_TEXTURE_2D, id );
int level = 0;
jobject mippedBitmap = srcBitmap;
while (width >= 2 && height >= 2) {
jniEnv->CallStaticVoidMethod(s_GLUtilsClass, s_texImage2DMethodID,
GL_TEXTURE_2D, level, mippedBitmap, 0);
width >>= 1;
height >>= 1;
level++;
mippedBitmap = createScaledBitmap(jniEnv, srcBitmap, width, height, true);
}
I've omitted all Bitmap.recycle()/NewGlobalRef() calls for brevity. createScaledBitmap is obviously a JNI call to Bitmap.createScaledBitmap().
I also tried the other version of texImage2D that takes the format and type of the bitmap. I've verified that it's always RGBA.
EDIT: To elaborate on the textures - they're really almost black. I tried eraseColor() on the mips with bright colors, and they're still extremely dark.
glGenerateMipmap will do this task faster and much more convenient for you.
The code you're showing will not generate the lower mip levels (1x1 will be missing, e.g.), so your texture will be incomplete. That should make the rendering as if the texturing was not present at all. It's unclear from your description if this is what you're observing.
In all cases, you should provide the mipmaps all the way down to the 1x1 (and for non square textures, this requires some changes in the computation of the new texture size to keep passing 1, as in 4x1 -> 2x1 -> 1x1)
It may be the case that Bitmap.createScaledBitmap does not correct the gamma. Have a look at this thread for code to create gamma-corrected mipmaps