2017-02-23 557 views
2

当我尝试使用Android上的VBO加载沉重的图形场景时,我遇到了由GPU生成的设备日志中的内存不足异常。OpenGL ES VBO奇怪的内存影响

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(..)后,可用内存量减少了所提供数据大小的两倍(在不同设备上尝试,结果相同)。这里是我的设置:

 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); 

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

回答

1

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

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

一旦您上传了数据,您就可以安全地删除应用程序副本;它不再需要了。

+1

那么,这是我第一次加倍检查,是的,我敢肯定,这些数据不再存储 - 当在调试中手动触发GC采集时,我可以观察到它被回收。从日志中可以看到,我的代码对内存的总体影响是我数据大小的3倍,但触发GC确实将其修剪为2次。 – user1581348

+0

还记得大多数进程会因性能原因而在用户空间中缓存页面分配。如果'availMem'只是报告内核的空闲页数,那么它就会忘记在用户空间页面池中保存的任何“空闲”页面(例如在驱动程序或VM堆管理器中)。 GC中的垃圾收集不一定会强制将所有空闲页面返回给操作系统。 – solidpixel

+1

嗯..缓存听起来仍然合理,但对于我提供的数据的性质(GL_STATIC_DRAW),我希望驱动程序一旦将其传输到GPU空间(在合理的时间内)就释放用于缓存的内存,但似乎从未发生过。为了确保它不是Java问题,我使用JNI尝试了相同的代码,获得相同的结果。有趣的是,在第三个设备中,三星Galaxy S7的行为完全不同,内存跟踪与我提供的数据量没有关系。 – user1581348