OpenGL ES VBO 奇怪的内存影响

Posted

技术标签:

【中文标题】OpenGL ES VBO 奇怪的内存影响【英文标题】:OpenGL ES VBO strange memory impact 【发布时间】:2017-02-23 17:57:52 【问题描述】:

尝试在 android 上使用 VBO 加载繁重的图形场景时,我在 GPU 生成的设备日志中遇到内存不足异常。

20:53:48.640 app W/Adreno-GSL: <sharedmem_gpumem_alloc_id:2255>: sharedmem_gpumem_alloc: mmap failed errno 12 Out of memory
20:53:48.642 app E/Adreno-GSL: <gsl_memory_alloc_pure:1971>: GSL MEM ERROR: kgsl_sharedmem_alloc ioctl failed.

我尝试提供的原始二进制数据大小小于可用 RAM 量的一半。在做了一些研究之后,我发现在每次glBufferData(..) 调用后,可用内存量减少了所提供数据大小的 2 倍(在不同设备上尝试,结果相同)。这是我的设置:

        logMem("before buff creation");

        ByteBuffer dummyBuff = ByteBuffer.allocateDirect(1024 * 1024 * 16).order(ByteOrder.nativeOrder());

        byte[] some = new byte[1024];
        for (int i = 0; i < dummyBuff.capacity(); i+= some.length) 
            dummyBuff.put(some);
        

        dummyBuff.rewind();

        logMem("buff data created");

        int[] bufferHandles = new int[1];
        GLES20.glGenBuffers(1, bufferHandles, 0);
        int bufferHandle = bufferHandles[0];

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferHandle);
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, dummyBuff.capacity(), dummyBuff, GLES20.GL_STATIC_DRAW);

        logMem("buff data supplied");

记录剩余的内存量

    manager = (ActivityManager) getSystemService( Activity.ACTIVITY_SERVICE );
    info = new ActivityManager.MemoryInfo();

    ...
    manager.getMemoryInfo(info);
    Log.v("mem", tag + ", mem " + info.availMem/1024/1024);

这是我在日志中得到的内容

20:13:34.243 V/mem:  before buff creation, mem 1381
20:13:34.466 V/mem:  buff data created, mem 1366
20:13:34.500 V/mem:  buff data supplied, mem 1334 

我也试过组合

        GLES20.glBufferData(.., dummyBuf.capacity(), null, ..);
        GLES20.glBufferSubData(.., 0, dummyBuf.capacity(), dummyBuf);

在这种情况下,在第一行执行后,我损失了 1 倍的缓冲区大小,正如预期的那样,但在第二次中,另一个 1 倍的内存缓冲区大小消失了。 我在使用不同 GPU(Adreno、Mali)的 2 个不同设备上进行了尝试,并且得到了相同的行为。所以,我的问题是:我错过了什么,还是这是预期的行为?在向 VBO 提供数据时,有什么方法可以减少这种 RAM 影响?

【问题讨论】:

【参考方案1】:

你还记得释放你分配的缓冲区的副本吗?

在 OpenGL ES 中,所有 GPU 服务器端资源(纹理、缓冲区、程序等)都被复制到驱动程序堆栈拥有的内存中。驱动程序不能只保留指向应用程序分配的缓冲区的指针。

上传数据后,您可以安全地删除应用程序端的副本;已经不需要了。

【讨论】:

嗯,这是我仔细检查的第一件事,是的,我确定这些数据不再存储 - 在调试中手动触发 GC 收集时,我可以观察到它被回收。从日志中可以看出,我的代码对内存的总影响是我的数据大小的 3 倍,但触发 GC 确实将其减少到 2 倍。 请记住,出于性能原因,大多数进程会将页面分配缓存在用户空间中。如果availMem 只是报告来自内核的空闲页面计数,则它不会注意到任何保存在用户空间页面池中的“空闲”页面(例如,在驱动程序或 VM 堆管理器中); GC 中的垃圾回收不一定会强制将所有空闲页面返回给操作系统。 嗯..缓存听起来很合理,但是,对于我提供的数据的性质(GL_STATIC_DRAW),我希望驱动程序在传输到 GPU 空间后立即释放用于缓存的内存(在合理的时间内),但它似乎永远不会发生。只是为了确保这不是 java 问题,我用 JNI 尝试了相同的代码,获得了相同的结果。有趣的是,虽然在第三款设备中,三星 Galaxy S7 的行为完全不同,但内存轨道与我提供的数据量的相关性为零。

以上是关于OpenGL ES VBO 奇怪的内存影响的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL ES:一个 VBO - 几个精灵 - 每个单独翻译

安卓 OpenGL ES 2.0 VBO

OpenGL ES 2.0 VBO 问题

OpenGL ES 2.0 vbo 白屏

在 OpenGL ES 2 中更新 VBO 的顶点

OpenGL ES:一个 VBO 中的多个网格