简体   繁体   English

Android OpenGL ES 2.0仅限于内存中的16个纹理?

[英]Android OpenGL ES 2.0 Only Limited To 16 Textures In Memory?

Basically while I was working on an app in Android Studio using OpenGL ES 2.0, I ran into a big problem that I can't wrap my head around, and it's been bugging me for about a week. 基本上当我使用OpenGL ES 2.0在Android Studio中开发应用程序时,遇到了一个我无法解决的大问题,而且它已经困扰了我一周左右。

So, anytime I load more than 16, maybe 17 textures of any size in memory, and attempt to display them in 2D through either my emulator in Genymotion or my ASUS tablet, it either begins to display different images than what I was binding at that particular index, or not display at all. 因此,任何时候我在内存中加载超过16个,可能是17个任意大小的纹理,并试图通过我在Genymotion中的模拟器或我的华硕平板电脑在2D中显示它们,它或者开始显示的不同于我绑定的图像特定索引,或根本不显示。 Yet if I run it through my Samsung Galaxy S6, it runs fine. 然而,如果我通过我的三星Galaxy S6运行它,它运行正常。 However if I load 16 textures or less, it works all around in all devices I test it on, including the emulator. 但是,如果我加载16个或更少的纹理,它可以在我测试它的所有设备上运行,包括模拟器。

This made me attempt a little experiment to see if it will show letter images az with each letter being 16x16 png, all within my drawable folder. 这让我尝试了一个小实验,看看它是否会显示字母图像az,每个字母为16x16 png,都在我的drawable文件夹中。 When each letter get's displayed, it will be 80x80 in size on screen so I can see them. 显示每个字母时,屏幕上的尺寸为80x80,这样我就能看到它们。 So I tried to have it run "a" through "z". 所以我试着通过“z”运行“a”。 On the emulator, as well as my tablet, it only showed "a" through "o", with a "z" at the end where "p" is suppose to be, and stopped right there. 在模拟器上,以及我的平板电脑,它只显示“a”到“o”,在“p”结尾处有一个“z”,并在那里停止。 On my Samsung Galaxy, it actually showed "a" through "z" and did what it was suppose to. 在我的三星Galaxy上,它实际上通过“z”显示了“a”并完成了它的设想。 This didn't make any sense as to why it would not load right on other devices considering that I set the number of textures at 27 or even higher in my constant. 考虑到我在常量中将纹理数设置为27或甚至更高,这对于为什么它不会在其他设备上加载没有任何意义。 I hope I explained my problem ok. 我希望我能解释我的问题。 And I'm pretty sure they are all capable of loading a lot more than 16 textures so I must be doing something wrong with my code. 而且我很确定他们都能够加载超过16个纹理,所以我必须对我的代码做错。 Rather than show the entire project, I'm gonna show you the relevant areas where the problem may lie. 我没有展示整个项目,而是向您展示问题所在的相关领域。 Any help is appreciated and thanks in advance. 任何帮助表示赞赏,并提前致谢。 Here's 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: 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 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: 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: 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: 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);
}
}

The total number of texture objects is not normally limited. 纹理对象的总数通常不受限制。 At least not within any reasonably range, theoretically you will run of ids that can be represented by a GLuint at some point. 至少不在任何合理的范围内,理论上你会运行可以在某个时刻由GLuint表示的id。 But you will run out of memory long before that happens. 但是在这种情况发生之前很久你就会耗尽内存。 So the only practical limit is normally given by the amount of memory used for the texture data. 因此,唯一的实际限制通常由用于纹理数据的内存量给出。

However, the number of texture units is very much limited. 然而,纹理单元的数量非常有限。 And from a quick look at your code, that's what you run into. 通过快速查看代码,这就是您遇到的问题。 From your texture loading code: 从您的纹理加载代码:

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

What you're trying to do is keep all textures bound, using a different texture unit for each. 你要做的是保持所有纹理绑定,每个纹理单元使用不同的纹理单元。 Then when you draw, you select which texture unit the shader samples from: 然后在绘制时,选择着色器采样的纹理单元:

glUniform1i(uTextureUnit, index);

This is a perfectly valid approach... until you run out of texture units. 这是一种非常有效的方法......直到你的纹理单元耗尽。 Which is exactly what happens. 这恰恰是发生了什么。

The maximum number of texture units is implementation dependent, and can be queried with: 纹理单元的最大数量取决于实现,可以通过以下方式查询:

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

The minimum for this value is 8. So unless you check the value, and find more, you can only rely on having 8 texture units. 此值的最小值为8.因此,除非您检查值并找到更多值,否则您只能依赖8个纹理单位。

If you need more than 8 textures, and want your code to run reliably across devices, your somewhat unconventional approach of keeping all textures bound will not work. 如果您需要超过8个纹理,并希望您的代码可以跨设备可靠地运行,那么保持所有纹理绑定的某种非传统方法将无法工作。

The easiest approach is that you do what most people do: Bind the texture you want to use before drawing. 最简单的方法是你做大多数人做的事情:在绘图之前绑定你想要使用的纹理。 For this, you can always use texture unit 0. So you can remove all calls to glActiveTexture() , and simply place a bind call in the Draw_Polygon() method, instead of the glUniform1i() call: 为此,您始终可以使用纹理单元0.因此,您可以删除对glActiveTexture()所有调用,只需在Draw_Polygon()方法中调用bind调用,而不是glUniform1i()调用:

glBindTexture(GL_TEXTURE_2D, texture[index]);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM