2017-02-23 112 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