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 奇怪的内存影响的主要内容,如果未能解决你的问题,请参考以下文章