Android OpenGL ES 2.0 limited to only 16 textures in memory?

Basically, when I was working on an application in Android Studio using OpenGL ES 2.0, I had a big problem, because of which I could not wrap my head, and it bothered me for about a week.

So, at any time when I load more than 16, maybe 17 textures of any size in memory, and try to display them in 2D through my emulator in Genymotion or my ASUS tablet, it either starts displaying different images than what I there was a binding to this particular index or not displayed at all. However, if I run it through the Samsung Galaxy S6, it works fine. However, if I download 16 textures or less, it works on all the devices I'm testing on, including the emulator.

This made me try a little experiment to see if images of the letters az would be displayed with each letter 16x16 png, everything in my drawable folder. When each letter is displayed, it will be 80x80 in size on the screen so that I can see them. So I tried to run "a" through "z". On the emulator, as well as on my tablet, it showed only โ€œaโ€ through โ€œoโ€, with โ€œzโ€ at the end, where โ€œpโ€ is supposed to be, and stopped right there. On my Samsung Galaxy, she actually showed an โ€œaโ€ through โ€œzโ€ and did what she expected. This made no sense why it would not load on other devices, given that I set the number of textures to 27 or even higher in my constant. Hope I explained my problem normally. And I'm sure all of them are capable of loading a lot more than 16 textures, so I have to do something with my code. Instead of showing the entire project, I will show you the relevant areas where the problem may arise. Any help is appreciated and thanks in advance. Here is my code:

Constants:

public class Constants { public static final int BYTES_PER_FLOAT = 4; public static final int POSITION_COMPONENT_COUNT = 4; public static final int NORMAL_COMPONENT_COUNT = 3; public static final int COLOR_COMPONENT_COUNT = 4; public static final int TEXTURE_COORDS_COMPONENT_COUNT = 2; public static final String A_COLOR = "a_Color"; public static final String A_POSITION = "a_Position"; public static final String A_NORMAL = "a_Normal"; public static final String A_TEXTURECOORDS = "a_TextureCoords"; public static final String U_MVMATRIX = "u_MVMatrix"; public static final String U_MVPMATRIX = "u_MVPMatrix"; public static final String U_TEXTURE_UNIT = "u_Texture_Unit"; public static final String U_LIGHTPOS = "u_LightPos"; public static final String V_COLOR = "v_Color"; public static final String V_POSITION = "v_Position"; public static final String V_NORMAL = "v_Normal"; public static float SCREEN_WIDTH; public static float SCREEN_HEIGHT; public static int NUMBER_OF_TEXTURES = 27; } 

Texture.java:

 import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.GLUtils; import static android.opengl.GLES20.*; public class Texture { public static int[] texture; public static void Load(Context context, int resourceId, int index) { //glGenTextures(Constants.NUMBER_OF_TEXTURES, texture, starting_index); //int n: specifies the number of texture names to be generated //int[] textures: specifies an array in which the generated texture names are stored //int offset: the starting index of your array! Bitmap bitmap; final BitmapFactory.Options options = new BitmapFactory.Options(); options.inScaled = false; // loading texture bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options); // ...and bind it to our array glActiveTexture(GL_TEXTURE0 + index); glBindTexture(GL_TEXTURE_2D, texture[index]); // create nearest filtered texture glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Use Android GLUtils to specify a two-dimensional texture image from our bitmap GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0); // Clean up bitmap.recycle(); } public static void Delete(int[] texture, int starting_index) { try { glDeleteTextures(1, texture, starting_index); } catch(Exception e) { return; } } } 

Quad.java

 import static android.opengl.GLES20.*; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; public class Quad { public float vertices[]; public float colors[]; public float texture_coords[]; public FloatBuffer vertexBuffer; public FloatBuffer textureBuffer; public FloatBuffer colorBuffer; public Quad(float x1, float y1, float z1, float w1, float x2, float y2, float z2, float w2, float x3, float y3, float z3, float w3, float x4, float y4, float z4, float w4, float red, float green, float blue, float alpha, float u1, float v1, float u2, float v2) { vertices = new float[]{x1, y1, z1, w1, x2, y2, z2, w2, x3, y3, z3, w3, x4, y4, z4, w4}; colors = new float[]{red, green, blue, alpha, red, green, blue, alpha, red, green, blue, alpha, red, green, blue, alpha}; ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(vertices.length * Constants.BYTES_PER_FLOAT); vertexByteBuffer.order(ByteOrder.nativeOrder()); vertexBuffer = vertexByteBuffer.asFloatBuffer(); vertexBuffer.put(vertices); vertexBuffer.position(0); ByteBuffer colorByteBuffer = ByteBuffer.allocateDirect(colors.length * Constants.BYTES_PER_FLOAT); colorByteBuffer.order(ByteOrder.nativeOrder()); colorBuffer = colorByteBuffer.asFloatBuffer(); colorBuffer.put(colors); colorBuffer.position(0); texture_coords = new float[]{u1, v1, u2, v1, u1, v2, u2, v2}; ByteBuffer textureByteBuffer = ByteBuffer.allocateDirect(texture_coords.length * Constants.BYTES_PER_FLOAT); textureByteBuffer.order(ByteOrder.nativeOrder()); textureBuffer = textureByteBuffer.asFloatBuffer(); textureBuffer.put(texture_coords); textureBuffer.position(0); } public void Draw_Polygon(int index, int program, float[] modelview_projection_matrix, float[] modelview_matrix) { int aPositionHandle = glGetAttribLocation(program, Constants.A_POSITION); glEnableVertexAttribArray(aPositionHandle); glVertexAttribPointer(aPositionHandle, Constants.POSITION_COMPONENT_COUNT, GL_FLOAT, false, 0, vertexBuffer); int aColorHandle = glGetAttribLocation(program, Constants.A_COLOR); glEnableVertexAttribArray(aColorHandle); glVertexAttribPointer(aColorHandle, Constants.COLOR_COMPONENT_COUNT, GL_FLOAT, false, 0, colorBuffer); int aTextureCoordsHandle = glGetAttribLocation(program, Constants.A_TEXTURECOORDS); glEnableVertexAttribArray(aTextureCoordsHandle); glVertexAttribPointer(aTextureCoordsHandle, Constants.TEXTURE_COORDS_COMPONENT_COUNT, GL_FLOAT, false, 0, textureBuffer); int uModelViewProjectionMatrixHandle = glGetUniformLocation(program, Constants.U_MVPMATRIX); glUniformMatrix4fv(uModelViewProjectionMatrixHandle, 1, false, modelview_projection_matrix, 0); int uModelViewMatrixHandle = glGetUniformLocation(program, Constants.U_MVMATRIX); glUniformMatrix4fv(uModelViewMatrixHandle, 1, false, modelview_matrix, 0); int uTextureUnit = glGetUniformLocation(program, Constants.U_TEXTURE_UNIT); glUniform1i(uTextureUnit, index); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDisableVertexAttribArray(aPositionHandle); glDisableVertexAttribArray(aColorHandle); glDisableVertexAttribArray(aTextureCoordsHandle); } } 

Text.java:

 import android.opengl.Matrix; import static android.opengl.GLES20.*; public class Text { public Quad[] poly = new Quad[1]; public String string; public int char_width; public int char_height; void Draw() { int texture_num = 0; int x_pos = 0; int y_pos = 0; for (int i = 0; i < string.length(); i++) { char character = string.charAt(i); switch(character) { case ' ': { texture_num = 0; break; } case 'a': { texture_num = 1; break; } case 'b': { texture_num = 2; break; } case 'c': { texture_num = 3; break; } case 'd': { texture_num = 4; break; } case 'e': { texture_num = 5; break; } case 'f': { texture_num = 6; break; } case 'g': { texture_num = 7; break; } case 'h': { texture_num = 8; break; } case 'i': { texture_num = 9; break; } case 'j': { texture_num = 10; break; } case 'k': { texture_num = 11; break; } case 'l': { texture_num = 12; break; } case 'm': { texture_num = 13; break; } case 'n': { texture_num = 14; break; } case 'o': { texture_num = 15; break; } case 'p': { texture_num = 16; break; } case 'q': { texture_num = 17; break; } case 'r': { texture_num = 18; break; } case 's': { texture_num = 19; break; } case 't': { texture_num = 20; break; } case 'u': { texture_num = 21; break; } case 'v': { texture_num = 22; break; } case 'w': { texture_num = 23; break; } case 'x': { texture_num = 24; break; } case 'y': { texture_num = 25; break; } case 'z': { texture_num = 26; break; } } Matrix.setIdentityM(OpenGL.model_matrix, 0); Matrix.translateM(OpenGL.model_matrix, 0, OpenGL.model_matrix, 0, 0.0f, 0.0f, 0.0f); Matrix.multiplyMM(OpenGL.matrix_ortho_projection_and_view, 0, OpenGL.matrix_ortho_projection, 0, OpenGL.model_matrix, 0); glUseProgram(Shader.textured_colored_shader_program); poly[0] = new Quad(x_pos + 0.0f, y_pos + 0.0f, 0.0f, 1.0f, x_pos + char_width, y_pos + 0.0f, 0.0f, 1.0f, x_pos + 0.0f, y_pos + char_height, 0.0f, 1.0f, x_pos + char_width, y_pos + char_height, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f); poly[0].Draw_Polygon(texture_num, Shader.textured_colored_shader_program, OpenGL.matrix_ortho_projection_and_view, OpenGL.model_matrix); x_pos += char_width; if (x_pos >= Constants.SCREEN_WIDTH) { x_pos = 0; y_pos += char_height; } } } } 

OpenGL.java:

 import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.content.Context; import android.opengl.GLSurfaceView.Renderer; import static android.opengl.GLES20.*; import android.util.Log; import android.opengl.Matrix; public class OpenGL implements Renderer { public static Context context; public static final float[] matrix_ortho_projection = new float[16]; public static float[] model_matrix = new float[16]; private final float[] matrix_view = new float[16]; public static final float[] matrix_ortho_projection_and_view = new float[16]; public int get_width, get_height; public static boolean texture_loading_enabled = true; Text text = new Text(); public OpenGL(Context context) { this.context = context; } public static void Load_Textures() { switch(State.game_state) { case State.LOGO: { Texture.Delete(Texture.texture, 0); glFlush(); Texture.texture = new int[Constants.NUMBER_OF_TEXTURES]; glGenTextures(Constants.NUMBER_OF_TEXTURES, Texture.texture, 0); Texture.Load(context, R.drawable.c64_space, 0); Texture.Load(context, R.drawable.c64_a, 1); Texture.Load(context, R.drawable.c64_b, 2); Texture.Load(context, R.drawable.c64_c, 3); Texture.Load(context, R.drawable.c64_d, 4); Texture.Load(context, R.drawable.c64_e, 5); Texture.Load(context, R.drawable.c64_f, 6); Texture.Load(context, R.drawable.c64_g, 7); Texture.Load(context, R.drawable.c64_h, 8); Texture.Load(context, R.drawable.c64_i, 9); Texture.Load(context, R.drawable.c64_j, 10); Texture.Load(context, R.drawable.c64_k, 11); Texture.Load(context, R.drawable.c64_l, 12); Texture.Load(context, R.drawable.c64_m, 13); Texture.Load(context, R.drawable.c64_n, 14); Texture.Load(context, R.drawable.c64_o, 15); Texture.Load(context, R.drawable.c64_p, 16); Texture.Load(context, R.drawable.c64_q, 17); Texture.Load(context, R.drawable.c64_r, 18); Texture.Load(context, R.drawable.c64_s, 19); Texture.Load(context, R.drawable.c64_t, 20); Texture.Load(context, R.drawable.c64_u, 21); Texture.Load(context, R.drawable.c64_v, 22); Texture.Load(context, R.drawable.c64_w, 23); Texture.Load(context, R.drawable.c64_x, 24); Texture.Load(context, R.drawable.c64_y, 25); Texture.Load(context, R.drawable.c64_z, 26); break; } case State.TITLE: { break; } case State.GAME: { break; } } texture_loading_enabled = false; } private void Controls() { switch(State.game_state) { case State.LOGO: { break; } case State.TITLE: { break; } case State.GAME: { break; } } } @Override public void onDrawFrame(GL10 glUnused) { Controls(); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); switch(State.game_state) { case State.LOGO: { text.Draw(); break; } case State.TITLE: { break; } case State.GAME: { break; } } } @Override public void onSurfaceChanged(GL10 glUnused, int width, int height) { // Set the OpenGL viewport to the same size as the surface. Log.d("TAG", "onSurfaceChanged()"); get_width = width; get_height = height; glViewport(0, 0, width, height); for(int i = 0; i < 16; i++) { matrix_ortho_projection[i] = 0.0f; matrix_view[i] = 0.0f; model_matrix[i] = 0.0f; matrix_ortho_projection_and_view[i] = 0.0f; } Matrix.orthoM(matrix_ortho_projection, 0, 0.0f, (float) get_width, (float) get_height, 0.0f, 0.0f, 1.0f); } @Override public void onSurfaceCreated(GL10 glUnused, EGLConfig config) { Log.d("TAG", "onSurfaceCreated()"); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); Shader.Create_Texture_Colored_Shader(context); Shader.Create_Colored_Shader(context); text.char_width = 80; text.char_height = 80; text.string = "abcdefghijklmnopqrstuvwxyz"; switch(State.game_state) { case State.LOGO: { if (texture_loading_enabled == true) Load_Textures(); break; } case State.TITLE: { break; } case State.GAME: { break; } } } } 

Shader.java:

 import android.content.Context; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import static android.opengl.GLES20.*; public class Shader { public static int textured_colored_shader_program; public static int colored_shader_program; public static String readTextFileFromRawResource(final Context context, final int resourceId) { final InputStream inputStream = context.getResources().openRawResource(resourceId); final InputStreamReader inputStreamReader = new InputStreamReader(inputStream); final BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String nextLine; final StringBuilder body = new StringBuilder(); try { while ((nextLine = bufferedReader.readLine()) != null) { body.append(nextLine); body.append('\n'); } } catch (IOException e) { return null; } return body.toString(); } public static int loadShader(int type, String shaderCode) { int shader = glCreateShader(type); glShaderSource(shader, shaderCode); glCompileShader(shader); return shader; } public static void Create_Texture_Colored_Shader(Context context) { int vertexShader = loadShader(GL_VERTEX_SHADER, readTextFileFromRawResource(context, R.raw.vertex_shader)); int fragmentShader = loadShader(GL_FRAGMENT_SHADER, readTextFileFromRawResource(context, R.raw.fragment_shader)); textured_colored_shader_program = glCreateProgram(); glAttachShader(textured_colored_shader_program, vertexShader); glAttachShader(textured_colored_shader_program, fragmentShader); glLinkProgram(textured_colored_shader_program); } public static void Create_Colored_Shader(Context context) { int vertexShader = loadShader(GL_VERTEX_SHADER, readTextFileFromRawResource(context, R.raw.vertex_shader_no_texture)); int fragmentShader = loadShader(GL_FRAGMENT_SHADER, readTextFileFromRawResource(context, R.raw.fragment_shader_no_texture)); colored_shader_program = glCreateProgram(); glAttachShader(colored_shader_program, vertexShader); glAttachShader(colored_shader_program, fragmentShader); glLinkProgram(colored_shader_program); } } 
+5
source share
1 answer

The total number of object textures is usually not limited. At least in any reasonable range, theoretically you will run identifiers, which at some point might be represented by GLuint . But you do not have enough memory long before this happens. Thus, the only practical limit is usually determined by the amount of memory used for texture data.

However, the number of texture units is very limited. And take a quick look at your code, what are you facing. From the texture loading code:

 glActiveTexture(GL_TEXTURE0 + index); glBindTexture(GL_TEXTURE_2D, texture[index]); 

What you are trying to do is bind all the textures using a different texture for each texture. Then, when you draw, you choose which texture units form the shader samples:

 glUniform1i(uTextureUnit, index); 

This is the right approach ... until you finish the texture units. What exactly is going on.

The maximum number of texture units depends on the implementation and can be requested using:

 GLint maxUnits = 0; glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxUnits); 

The minimum value for this value is 8. Therefore, if you do not check the value and find more, you can only rely on the presence of 8 texture units.

If you need more than 8 textures and want your code to work reliably on different devices, your somewhat unconventional approach to saving all related textures will not work.

The simplest approach is that you do what most people do: snap the texture you want to use before drawing. You can always use texture block 0 for this. Thus, you can delete all calls to glActiveTexture() and simply put the binding call into the Draw_Polygon() method instead of calling glUniform1i() :

 glBindTexture(GL_TEXTURE_2D, texture[index]); 
+6
source

Source: https://habr.com/ru/post/1242686/


All Articles