【问题标题】:OutOfMemory Exception android OpenGLes LoadTexturesOutOfMemory 异常 android OpenGLes LoadTextures
【发布时间】:2013-05-27 14:29:44
【问题描述】:

我正在尝试加载我的纹理以便在我的游戏中使用它们。目前,我的游戏需要 84 个图像帧才能按预期工作,但是当我加载这么多图像时,它给了我 OutOfMemmory 异常。

有没有更好的方法来加载这些图像?

每个 Obs 在 HDPI 设备上都有 150x150px。

我正在运行的设备是 Galaxy SII 女巫,我相信它有 50mb 的堆内存。

这里是异常发生的地方:

    public void loadTextures(GL10 gl) {
    InputStream is[] = new InputStream[textureCount];
    Bitmap bitmap[] = new Bitmap[textureCount];

    is[0] = context.getResources().openRawResource(R.drawable.bg_01);
    is[1] = context.getResources().openRawResource(R.drawable.bg_02);
    is[2] = context.getResources().openRawResource(R.drawable.bg_03);

    is[3] = context.getResources().openRawResource(R.drawable.win_hud);
    is[4] = context.getResources().openRawResource(R.drawable.heart);

    is[5] = context.getResources().openRawResource(R.drawable.skyblue);
    is[6] = context.getResources().openRawResource(R.drawable.heart);
    is[7] = context.getResources().openRawResource(R.drawable.heart);
    is[8] = context.getResources().openRawResource(R.drawable.heart);
    is[9] = context.getResources().openRawResource(R.drawable.heart);
    is[10] = context.getResources().openRawResource(R.drawable.heart);

             //reserving numbers for more textures from 6 to 10.


    is[11] = context.getResources().openRawResource(R.drawable.walking01);
    is[12] = context.getResources().openRawResource(R.drawable.walking02);
    is[13] = context.getResources().openRawResource(R.drawable.walking03);
    is[14] = context.getResources().openRawResource(R.drawable.walking04);
    is[15] = context.getResources().openRawResource(R.drawable.walking05);

    is[16] = context.getResources().openRawResource(R.drawable.flying01);
    is[17] = context.getResources().openRawResource(R.drawable.flying02);

    is[18] = context.getResources().openRawResource(R.drawable.falling_01);
    is[19] = context.getResources().openRawResource(R.drawable.falling_02);
    is[20] = context.getResources().openRawResource(R.drawable.fallen);

    is[21] = context.getResources().openRawResource(
            R.drawable.standingup_01);
    is[22] = context.getResources().openRawResource(
            R.drawable.standingup_02);

    is[23] = context.getResources().openRawResource(R.drawable.death_01);
    is[24] = context.getResources().openRawResource(R.drawable.death_02);
    is[25] = context.getResources().openRawResource(R.drawable.death_03);
    is[26] = context.getResources().openRawResource(R.drawable.death_04);
    is[27] = context.getResources().openRawResource(R.drawable.death_05);
    is[28] = context.getResources().openRawResource(R.drawable.death_06);


    is[29] = context.getResources().openRawResource(R.drawable.p_walk_1);
    is[30] = context.getResources().openRawResource(R.drawable.p_walk_2);
    is[31] = context.getResources().openRawResource(R.drawable.p_walk_3);
    is[32] = context.getResources().openRawResource(R.drawable.p_walk_4);
    is[33] = context.getResources().openRawResource(R.drawable.p_walk_5);
    is[34] = context.getResources().openRawResource(R.drawable.p_walk_6);

    is[35] = context.getResources().openRawResource(R.drawable.p_holding01);
    is[36] = context.getResources().openRawResource(R.drawable.p_holding03);

    is[37] = context.getResources().openRawResource(R.drawable.p_opening01);
    is[38] = context.getResources().openRawResource(R.drawable.p_opening02);


    is[39] = context.getResources().openRawResource(R.drawable.p_parachute01);
    is[40] = context.getResources().openRawResource(R.drawable.p_parachute02);
    is[41] = context.getResources().openRawResource(R.drawable.p_parachute03);
    is[42] = context.getResources().openRawResource(R.drawable.p_parachute04);
    is[43] = context.getResources().openRawResource(R.drawable.p_parachute05);

    is[44] = context.getResources().openRawResource(R.drawable.p_closing01);
    is[45] = context.getResources().openRawResource(R.drawable.p_closing02);

    is[46] = context.getResources().openRawResource(R.drawable.p_cracking01);
    is[47] = context.getResources().openRawResource(R.drawable.p_cracking02);

    is[48] = context.getResources().openRawResource(R.drawable.p_fallen01);

    is[49] = context.getResources().openRawResource(R.drawable.p_standing01);
    is[50] = context.getResources().openRawResource(R.drawable.p_standing02);
    is[51] = context.getResources().openRawResource(R.drawable.p_standing3);

    is[52] = context.getResources().openRawResource(R.drawable.p_dead01);
    is[53] = context.getResources().openRawResource(R.drawable.p_dead02);
    is[54] = context.getResources().openRawResource(R.drawable.p_dead03);
    is[55] = context.getResources().openRawResource(R.drawable.p_dead04);
    is[56] = context.getResources().openRawResource(R.drawable.p_dead05);
    is[57] = context.getResources().openRawResource(R.drawable.p_dead06);
    is[58] = context.getResources().openRawResource(R.drawable.p_dead07);
    is[59] = context.getResources().openRawResource(R.drawable.p_dead08);
    is[60] = context.getResources().openRawResource(R.drawable.p_dead09);

    is[61] = context.getResources().openRawResource(R.drawable.r_walking01);
    is[62] = context.getResources().openRawResource(R.drawable.r_walking02);
    is[63] = context.getResources().openRawResource(R.drawable.r_walking03);
    is[64] = context.getResources().openRawResource(R.drawable.r_walking04);
    is[65] = context.getResources().openRawResource(R.drawable.r_walking05);
    is[66] = context.getResources().openRawResource(R.drawable.r_walking06);
    is[67] = context.getResources().openRawResource(R.drawable.r_walking07);
    is[68] = context.getResources().openRawResource(R.drawable.r_walking08);
    is[69] = context.getResources().openRawResource(R.drawable.r_falling01);
    is[70] = context.getResources().openRawResource(R.drawable.r_falling02);

    is[71] = context.getResources().openRawResource(R.drawable.r_fallen);

    is[72] = context.getResources().openRawResource(R.drawable.r_standingup01);
    is[73] = context.getResources().openRawResource(R.drawable.r_standingup02);

    is[74] = context.getResources().openRawResource(R.drawable.r_dyeing01);
    is[75] = context.getResources().openRawResource(R.drawable.r_dyeing02);
    is[76] = context.getResources().openRawResource(R.drawable.r_dyeing03);
    is[77] = context.getResources().openRawResource(R.drawable.r_dyeing04);
    is[78] = context.getResources().openRawResource(R.drawable.r_dyeing05);
    is[79] = context.getResources().openRawResource(R.drawable.r_dyeing06);
    is[80] = context.getResources().openRawResource(R.drawable.r_dyeing07);
    is[81] = context.getResources().openRawResource(R.drawable.r_dyeing08);
    is[82] = context.getResources().openRawResource(R.drawable.r_dyeing09);
    is[83] = context.getResources().openRawResource(R.drawable.r_dyeing10);




    for (int loop = 0; loop < textureCount; loop++) {

        bitmap[loop] = BitmapFactory.decodeStream(is[loop]);
        try {
            is[loop].close();
            is[loop] = null;
        } catch (IOException e) {
        }
    }
    gl.glGenTextures(textureCount, textures, 0);
    for (int loop = 0; loop < textureCount; loop++) {
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[loop]);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
                GL10.GL_LINEAR);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
                GL10.GL_LINEAR);
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, // OpenGL docs.
                GL10.GL_NICEST);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
                GL10.GL_CLAMP_TO_EDGE);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
                GL10.GL_CLAMP_TO_EDGE);

        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap[loop], 0);
        bitmap[loop].recycle();
    }
}

【问题讨论】:

  • 您是否需要同时使用所有 84 张图片,意思是所有 84 张图片同时出现在屏幕上?解决方案有点泄露它,但只是要求我们在同一页上!
  • 是的,游戏就像植物大战僵尸,根据你的等级,屏幕上会出现各种僵尸。
  • 我明白了。不幸的是,在优化方面没有太多可以做的事情。我会写一个答案,并附上一些参考资料供您阅读。他们应该帮忙!
  • 太好了,对于植物大小的僵尸对僵尸僵尸,你觉得 150px 是不是太大了还是可以?
  • 当您遇到 OOM 异常时,我的第一个猜测是它太大了。您是否为高、低、中和非常高的清晰度使用不同的图像?您不应该让 Android 负责缩放。请务必让 Android 处理要从中加载的 res/drawable!

标签: android memory opengl-es heap-memory


【解决方案1】:

由于您需要同时在屏幕上显示所有这些,因此优化是完全不可能的。我建议您牢记Displaying Bitmaps Efficiently 教程,因为它提供了一些关于如何避免可怕的java.lang.OutofMemoryError: bitmap size exceeds VM budget 错误的提示。

我想到了其他一些事情。首先,我建议不要在循环之前加载所有InputStreams。为什么不在循环中加载流?也许写下要加载到文件中的图像名称并对其进行迭代。

另外,根据我的经验,BitmapFactory.decodeFileDescriptor() 造成的麻烦更少。虽然我不确定它是否能解决问题,但在这一点上它不会受到伤害!

【讨论】:

  • 我会改变循环,看看会发生什么。
  • 请告诉我进展如何!
  • 我试过 for (int loop = 0; loop
  • 没有。您需要使用的是所有尺寸的图像。您应该阅读这份关于支持多种屏幕尺寸 (developer.android.com/guide/practices/…) 的开发人员指南,其中还介绍了如何管理不同的图像尺寸。
【解决方案2】:

把你的代码改成这样的:

public void loadTextures(GL10 gl) {
    int[] ids = new int[texturesCount];
    ids[0] = R.drawable.bg_01;
    // rest of id's

    gl.glGenTextures(textureCount, textures, 0);

    for (int loop = 0; loop < textureCount; loop++) {
        Bitmap bm = BitmapFactory.decodeResource(context.getResources(), ids[loop]);
        if (null == bm) {
            // handle error
        }
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[loop]);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
                GL10.GL_LINEAR);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
                GL10.GL_LINEAR);
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, // OpenGL docs.
                GL10.GL_NICEST);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
                GL10.GL_CLAMP_TO_EDGE);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
                GL10.GL_CLAMP_TO_EDGE);

        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bm, 0);
        bm.recycle();
    }
}

这样你就不会同时有 84 个打开的流、84 个加载的位图等等......

【讨论】:

  • 是否有效,或者您仍有OOM?你是如何测量内存消耗的?我不知道你怎么能分配 42MB,因为你所有的纹理总共大约 8MB (84 * 150 * 150 * 4)
  • 我认为 bm.recycle();将来会释放内存而不是立即释放内存,我认为即使触发手动 gc 收集也无济于事,但这并不重要,系统会在需要时使用这种方法释放内存,所以我会说这样更好原来的!将其转换为与 opengl 兼容的格式也很重要,那么它将仅使用本机 ram 中一半的空间
  • @Sponge,如果不共享位图,回收将释放内存(基本上它会释放 SkBitmap 对象中保存的 SkPixelRef),没有什么像“opengl 兼容格式”,你可能在谈论减少像素大小(到 16 位 RGB565)或纹理压缩,但纹理压缩很棘手,因为没有支持 alpha 的标准格式,对于完全不透明的纹理,可以使用 ETC1
  • @Sponge 关于回收的另一个注意事项,在 3.0+ 上确实不需要它,因为 pixelrefs 是在 VM 堆上分配的,但在旧版本上,它们是在本机堆上分配的,但计入堆限制为“外部分配” ”。虽然recycle是从finalizer中调用的,但它是异步操作,在处理大量位图时可能会导致OOM,因此手动调用recycle更安全。
【解决方案3】:

据我所知,有一种方法可以绑定纹理缓冲区,以便将其加载到 GPU RAM,这将减少您的堆使用量。我正在阅读有关此内容的信息,但不确定该怎么做。也许 glBindTexture() 做的完全一样,不知道。

如果您将所有纹理文件合并到一个文件中并在 onSurfaceCreated() 中仅加载一次也很好;

也看看这个问题: How to load textures in OpenGL ES efficiently

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 2014-04-05
    • 2015-07-28
    相关资源
    最近更新 更多