2017-02-23 2 views
2

Ich bin in out of memory Ausnahmen in Geräteprotokoll von GPU generiert beim Versuch, schwere Grafik-Szene mit VBO auf Android laden geladen.OpenGL ES VBO seltsame Speicher Auswirkungen

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. 

Die Rohbinärdatengröße, die ich liefern möchte, ist weniger als die Hälfte der verfügbaren RAM-Menge. Nach einigen Recherchen fand ich, dass nach jedem glBufferData(..) Anruf die Menge an freiem Speicher 2 Mal die Größe der gelieferten Daten abnimmt (auf verschiedenen Geräten versucht, dasselbe Ergebnis). Hier ist mein Setup:

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

Speichermenge links mit

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

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

hier angemeldet ist, was ich im Protokoll bin immer

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 

Ich habe auch versucht, eine Kombination aus

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

In diesem Fall Nach der Ausführung der ersten Zeile erhielt ich wie erwartet einen Verlust der Puffergröße von 1x, aber mit der zweiten war ein weiterer Pufferspeicher von 1 x Größe verschwunden. Ich versuchte es auf 2 verschiedenen Geräten mit verschiedenen GPUs (Adreno, Mali) und bekam das gleiche Verhalten. Meine Frage ist also: Fehle ich etwas oder ist das ein erwartetes Verhalten? Gibt es eine Möglichkeit, diesen RAM-Einfluss zu verringern, wenn Daten an VBO geliefert werden?

Antwort

1

Erinnern Sie sich daran, die Kopie des zugewiesenen Puffers freizugeben?

In OpenGL ES werden alle serverseitigen GPU-Ressourcen (Texturen, Puffer, Programme usw.) in den Speicher des Treiberstapels kopiert. Treiber können nicht nur Zeiger auf die von der Anwendung zugewiesenen Puffer halten.

Sobald Sie die Daten hochgeladen haben, können Sie die anwendungsseitige Kopie sicher löschen; es wird nicht mehr benötigt.

+1

Nun, das war die erste Sache, die ich doppelt überprüft habe, und ja, ich bin sicher, dass diese Daten nicht mehr gespeichert werden - wenn GC Collection im Debug manuell auslösen, konnte ich beobachten, dass es zurückgewonnen wird. Wie Sie aus dem Protokoll entnehmen können, hat sich der Gesamteinfluss auf den Speicher für meinen Code auf das Dreifache der Größe meiner Daten ausgewirkt, aber durch die Auslösung von GC wurde der Wert auf 2 reduziert. – user1581348

+0

Denken Sie daran, dass die meisten Prozesse Seitenzuordnungen im Benutzerbereich aus Gründen der Leistung zwischenspeichern. Wenn "availMem" nur die Anzahl der freien Seiten vom Kernel meldet, ist es nicht an "freie" Seiten gebunden, die in User-Space-Seitenpools (z. B. im Treiber oder im VM-Heap-Manager) gespeichert sind. Die Garbage Collection im GC erzwingt nicht zwangsweise die Rückgabe aller freien Seiten an das Betriebssystem. – solidpixel

+1

Hmm..caching klingt vernünftig, trotzdem würde ich für die Art der Daten, die ich bereitstelle (GL_STATIC_DRAW), erwarten, dass der Treiber den für das Zwischenspeichern verwendeten Speicher freigibt, sobald er in den GPU-Bereich übertragen wird (in einer vernünftigen Zeit). aber es scheint, als ob es nie passiert. Nur um sicher zu sein, dass es kein Java-Problem ist, habe ich den gleichen Code mit JNI ausprobiert und dasselbe Ergebnis erzielt. Interessanterweise hatte das Samsung Galaxy S7 in einem dritten Gerät ein völlig anderes Verhalten, die Speicherspur hatte jedoch keine Corellation mit der Menge an Daten, die ich lieferte. – user1581348