OpenGL ruckelt schon bei 1000 Dreiecken...

  • Antworten:11
Daniel W.
  • Forum-Beiträge: 17

04.09.2012, 21:23:31 via Website

Hi!
Auf meinen Sony Ericsson Ray ruckelt meine Anwendung schon bei 1000...
Ich lasse sie nur durch gl.glRotatef rotieren.
Ist das normal?
Was kann ich dagegen tun?

Antworten
André
  • Forum-Beiträge: 77

04.09.2012, 22:46:27 via Website

1000 Dreiecke sind nicht viel, allerdings kenne ich auch das Gerät nicht. Es kann natürlich wie immer viele Gründe geben. Um einfach mal ins Blaue zu raten, du könntest mal schauen was der Speicherverbrauch sagt. Eine gute Prüfgröße ist z.B. das Intervall, in dem der GC ausgeführt wird während deine Anwendung läuft. Wenn der mehrmals pro Minute anspringt, kann das durchaus ein Grund für das Ruckeln sein.
Ansonsten wäre die Frage, wie du die 1000 Dreiecke zeichnest. Wenn du dafür 4000 OGL Aufrufe tätigst, ist das wahrscheinlich der Grund.
Das einfachste wäre wohl, wenn du mal den Code deiner Zeichenroutine posten würdest.

Antworten
Daniel W.
  • Forum-Beiträge: 17

05.09.2012, 11:47:39 via Website

Danke für die Antwort!
Mein Handy sollte es eigentlich schon schaffen. (Die meißten 3D Anwendungen laufen flüssig...)
Mir ist das ganze erst aufgefallen, weil ich eine Klasse geschrieben habe, mit der ich .OBJ Dateien importieren kann.
Also hab ich es nochmal getestet, indem ich in einer for-Schleife 1000-mal ein Dreieck zeichnen lasse.
Hier meine Dreiecksklasse:
1public class Triangle
2{
3 private FloatBuffer mVertices;
4 private GL10 gl;
5
6 public Triangle(GL10 gl,
7 float X1,float Y1,float Z1,
8 float X2,float Y2,float Z2,
9 float X3,float Y3,float Z3)
10 {
11 this.gl = gl;
12 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(3*3*4);
13 byteBuffer.order(ByteOrder.nativeOrder());
14 mVertices = byteBuffer.asFloatBuffer();
15
16 mVertices.put(new float[] {
17 X1,Y1,Z1,
18 X2,Y2,Z2,
19 X3,Y3,Z3
20 });
21 mVertices.flip();
22 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
23 }
24
25 public void draw()
26 {
27 gl.glColor4f(0, 0, 1, 1);
28 gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertices);
29 gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
30 }
31}

Wie gesagt, die draw()-Methode wird im onDrawFrame() mit einer for-Schleife 1000 mal aufgerufen.

Wahrscheinlich ist es der GC-Intervall, weil LogCat mir permanent GC_FOR_MALLOC.... und GC_CONCURRENT auspuckt.
Hier der vollständige Text: "GC_FOR_MALLOC freed 1786, 62% free 3193K/8263K, external 1685K/213 K, paused 14ms"

Bei GC_CONCURRENT steht das gleiche außer bei paused "1ms+2ms".

Ich nehme an, dass das ein zu niedriger Wert ist (Das rattert nur so runter, wird also wahrscheinlich jedes mal beim Rendern neu aufgerufen.)
Was kann man dagegen machen?

Antworten
André
  • Forum-Beiträge: 77

05.09.2012, 13:07:11 via Website

Keinen Speicher allozieren. Du solltest beim Start des Programmes alle Objekte / Dreiecke erzeugen, und dann während es läuft nichts weiter. Damit dürfte es dann so gut wie keine GC_ Einträge im Logcat mehr geben. In welchem Zeitabstand kommen die Einträge zur Zeit?

Um herauszufinden, wo du viel Speicher allozierst, kannst du den DDMS Profiler in Eclipse benutzen (zumindest für den Emulator), der ist dafür super. Ansonsten könntest du auch deine onDrawFrame() Methode posten.

Antworten
Daniel W.
  • Forum-Beiträge: 17

05.09.2012, 14:05:31 via Website

Hier meine onDrawFrame():
1@Override
2 public void onDrawFrame(GL10 gl)
3 {
4 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
5 for (int i = 0;i<1000;i++)
6 triangle.draw();
7 gl.glRotatef(1, 0, 1, 0);
8 }

Auch wenn es nur ein Dreieck ist, sollte es sich so wie 1000 verhalten.
Wenn ich ein Modell importiere wird mir das bestätigt.
Wann die Einträge kommen, ist verschieden.
Von ~20 mal die Sekunde bis zu ~1mal pro Sekunde.

Mittels android.os.Debug.getNativeHeapAllocatedsize()/1024 hab ich mir den Speicherverbrauch angesehen.
Außerdem habe ich in der Triangle Klasse den VertexPointer und die Color4f aus der .draw() rausgenommen und sie in den Konstruktor gepackt.
Es wird ein Speicherverbrauch von 5242/5243 KB angezeigt.

Antworten
André
  • Forum-Beiträge: 77

05.09.2012, 14:49:42 via Website

Daniel W.
Wann die Einträge kommen, ist verschieden.
Von ~20 mal die Sekunde bis zu ~1mal pro Sekunde.

Das ist auf jeden Fall das Problem. Wenn der GC mehrmals pro Sekunde anspringt, ist da etwas ziemlich im argen. In dem Code den du gepostet hast sehe ich da kein Problem, wird noch mehr ausgeführt? Hat deine App noch andere Threads?
Ansonsten nutz mal den Profiler, der sagt dir genau wo du so häufig Speicher allozierst.
Edit: 5 MB für eine App mit einem Dreieck? Irgendwo musst du noch deutlich mehr machen als hier gezeigt.

— geändert am 05.09.2012, 14:50:54

Antworten
Daniel W.
  • Forum-Beiträge: 17

05.09.2012, 15:11:03 via Website

Jetzt, wo ich die VertexPointer und die Color4f Methode in den Konstruktor geschoben habe, wird die GC nur noch alle 2 Sekunden aufgerufen.
Ich hab jetzt ein neues Projekt angelegt und alles nochmal neu gemacht.
Es wird wieder ein Speicher von 5265 angezeigt.
Ich kenn mich nicht den Profiler nicht aus, habs mir nur mal grob durchgelesen.
Kannst du kurz erklären was ich machen soll?
Meine Anwendung hat keine anderen Threads. Nur der Sourcecode, den ich gepostet habe, und die Initialisierung des Dreiecks.

Antworten
André
  • Forum-Beiträge: 77

05.09.2012, 15:30:48 via Website

Der Profiler ist hier gut beschrieben: http://developer.android.com/tools/debugging/ddms.html
Wie viele Frames hast du denn jetzt mit dieser Verbesserung?

Dein Beispiel für das Zeichnen von 1000 Dreiecken ist allerdings auch etwas ungünstig, weil du dadurch so viele OpenGL Aufrufe tätigst. Ich vermute, dass das teure hier die zentausenden OpenGL Aufrufe pro Sekunde sind (pro Aufruf ein JNI Kontextwechsel).
In einer echten Anwendung würde man das viel besser machen, da hast du zwar mehr Dreiecke, aber nicht viele Aufrufe. Üblicherweise werden für jedes Objekt (nicht Dreieck oder Fläche) nur wenige Aufrufe getätigt. Ein Objekt wird als Mesh repräsentiert, und das Zeichnen eines Meshes (bestehend aus beliebig vielen Dreiecken) geht in 2-5 OpenGL Aufrufen.
Du könntest also mal probieren, wie die Performance aussieht, wenn du deine 1000 Dreiecke in einem Objekt repräsentierst und dann nur einen glVertexPointer und glDrawElements Aufruf zum Zeichnen brauchst.

— geändert am 05.09.2012, 15:32:00

Antworten
Daniel W.
  • Forum-Beiträge: 17

05.09.2012, 20:20:41 via Website

Hab das ganze nun ohne Dreiecksklasse getan.
Ich hab in der onDrawFrame() nur gl.glDrawArrays() in die Schleife gepackt, und es ruckelt immer noch...

Antworten
André
  • Forum-Beiträge: 77

05.09.2012, 22:45:50 via Website

Ja, damit hast du auch nichts gewonnen. Die Idee ist, alle 1000 Dreiecke mit einem Aufruf von glDrawArrays zu zeichnen. Vielleicht liegt es aber auch gar nicht daran. Was hat das Profiling ergeben?

Antworten
Daniel W.
  • Forum-Beiträge: 17

06.09.2012, 00:08:24 via Website

Warte, soll das heißen ich soll mit einem gl.glDrawArrays() Aufruf möglichst ein ganzes mesh zeichnen?
Kann mir das schwer vorstellen...

Antworten
André
  • Forum-Beiträge: 77

06.09.2012, 10:01:47 via Website

Warum sollte man ein Mesh mit mehreren Befehlen zeichnen? Wenn du ein Objekt aus einer Datei lädtst, hast du eh alle Daten zusammen (Vertex Buffer, Index Buffer, Texture Buffer etc.) und kannst sie mit einem einzigen Befehl ausgeben. Das aufzutrennen macht keinen Sinn.
Aber wie schon gesagt, man sollte auch 1000 (kleine) Meshes ausgeben können. Wenn du wissen willst, wo in deinem Fall das Problem liegt, müsstest du schon noch etwas mehr dafür tun.

Antworten