3D-Grafik Extreme Jogl Performanceprobleme

Mortal_Shadow

Aktives Mitglied
Hi,

ich wollte einmal opengl mit Java ausprobieren.
Dazu hab ich mir Version 1.1.1a von der Website heruntergeladen.
Dann habe ich das Wikipediabeispiel per copy-paste übernommen.
(Ein sich drehendes Tetraeder.)
Läuft auch ganz gut, allerdings wollte ich schauen, was passiert, wenn er mehr Tetraeder zeichnen soll.
Schritt 1 war einfach das zeichnen in einem Loop 10k mal aufzurufen, was extrem lahm war.
Ich habe es mir daraufhin in einer DrawList zeichnen lassen.
Braucht dennoch noch 400-500ms pro Frame.

Source:
[Java]
package test;

//depends on jogl.jar and gluegen-rt.jar
import javax.media.opengl.GL;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.glu.GLU;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.media.opengl.GLCanvas;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import com.sun.opengl.util.Animator;

public class JOGLTetrahedron implements GLEventListener, KeyListener {
float rotateT = 0.0f;
static GLU glu = new GLU();
static GLCanvas canvas = new GLCanvas();
static Frame frame = new Frame("Jogl 3D Shape/Rotation");
static Animator animator = new Animator(canvas);
private int theTorus;

public void display(GLAutoDrawable gLDrawable) {
final GL gl = gLDrawable.getGL();
gl.glClear(GL.GL_COLOR_BUFFER_BIT);
gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
gl.glTranslatef(0.0f, 0.0f, -5.0f);
gl.glRotatef(rotateT, 1.0f, 0.0f, 0.0f);
gl.glRotatef(rotateT, 0.0f, 1.0f, 0.0f);
gl.glRotatef(rotateT, 0.0f, 0.0f, 1.0f);
gl.glRotatef(rotateT, 0.0f, 1.0f, 0.0f);
gl.glCallList(theTorus);



rotateT += 0.2f;
System.out.println(System.currentTimeMillis());
}



public void displayChanged(GLAutoDrawable gLDrawable,
boolean modeChanged, boolean deviceChanged) {
}

public void init(GLAutoDrawable gLDrawable) {
GL gl = gLDrawable.getGL();
gl.glShadeModel(GL.GL_SMOOTH);
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
gl.glClearDepth(1.0f);
gl.glEnable(GL.GL_DEPTH_TEST);
gl.glDepthFunc(GL.GL_LEQUAL);
gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT,
GL.GL_NICEST);
gLDrawable.addKeyListener(this);
theTorus = gl.glGenLists(1);
gl.glNewList(theTorus, GL.GL_COMPILE);

drawTorus(gl, 8, 25);


gl.glEndList();
}

public void reshape(GLAutoDrawable gLDrawable, int x,
int y, int width, int height) {
GL gl = gLDrawable.getGL();
if(height <= 0) {
height = 1;
}
float h = (float)width / (float)height;
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluPerspective(50.0f, h, 1.0, 1000.0);
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glLoadIdentity();
}

public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ESCAPE) {
exit();
}
}

public void keyReleased(KeyEvent e) {
}

public void keyTyped(KeyEvent e) {
}

public static void exit(){
animator.stop();
frame.dispose();
System.exit(0);
}

public static void main(String[] args) {
canvas.addGLEventListener(new JOGLTetrahedron());
frame.add(canvas);
frame.setSize(640, 480);
frame.setUndecorated(true);
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
exit();
}
});
frame.setVisible(true);
animator.start();
canvas.requestFocus();
}

private void drawTorus(GL gl, int numc, int numt){



gl.glBegin(GL.GL_TRIANGLES);
for(int i = 0; i < 10000; i++){
// Front
gl.glColor3f(0.0f, 1.0f, 1.0f);
gl.glVertex3f(0.0f, 1.0f, 0.0f);
gl.glColor3f(0.0f, 0.0f, 1.0f);
gl.glVertex3f(-1.0f, -1.0f, 1.0f);
gl.glColor3f(0.0f, 0.0f, 0.0f);
gl.glVertex3f(1.0f, -1.0f, 1.0f);

// Right Side Facing Front
gl.glColor3f(0.0f, 1.0f, 1.0f);
gl.glVertex3f(0.0f, 1.0f, 0.0f);
gl.glColor3f(0.0f, 0.0f, 1.0f);
gl.glVertex3f(1.0f, -1.0f, 1.0f);
gl.glColor3f(0.0f, 0.0f, 0.0f);
gl.glVertex3f(0.0f, -1.0f, -1.0f);

// Left Side Facing Front
gl.glColor3f(0.0f, 1.0f, 1.0f);
gl.glVertex3f(0.0f, 1.0f, 0.0f);
gl.glColor3f(0.0f, 0.0f, 1.0f);
gl.glVertex3f(0.0f, -1.0f, -1.0f);
gl.glColor3f(0.0f, 0.0f, 0.0f);
gl.glVertex3f(-1.0f, -1.0f, 1.0f);

// Bottom
gl.glColor3f(0.0f, 0.0f, 0.0f);
gl.glVertex3f(-1.0f, -1.0f, 1.0f);
gl.glColor3f(0.1f, 0.1f, 0.1f);
gl.glVertex3f(1.0f, -1.0f, 1.0f);
gl.glColor3f(0.2f, 0.2f, 0.2f);
gl.glVertex3f(0.0f, -1.0f, -1.0f);
}
gl.glEnd();

}
}
[/Java]

Nun meine Frage: Was läuft da genau schief?
Meine GraKa (Geforce 9600) sollte da doch deutlich mehr packen.
 

Empire Phoenix

Top Contributor
Objectcount verscuhe die anzahl der object auf max 3k zu begrenzen, sonst werden die renderaufrüfe zum engpass. (verwende lieber etwas anspruchsvollere objecte was die polygonzahl angeht)
 

Mortal_Shadow

Aktives Mitglied
Die Frage ist: Wie genau reduziere ich den Objektcount?
Insgesamt bin ich ja bei 40k Dreiecken brauchen 400 ms, wobei noch keine Texturen verwendet werden.
Für eine gute Zeichenzeit nehmen wir mal 10ms (Auch bei mehr siehts noch flüssig aus, aber wenn man andere Rechnungen der CPU usw bedenkt müsste das ja auf 60fps kommen.)
Das heißt ich kann bis zu 1k Dreiecke zeichnen. (Oder auch max 16 normale Sphären)

Wenn ich die als VBO zeichne, ist es dann besser für den Objectcount?
Ich dachte eigentlich, dass die DrawList schon ausreichen müsste, da sie sich ja die zeichenoperationen merkt.

Und wie viel Dreiecke/Polygone werde ich so in etwa erwarten können?
 

Mortal_Shadow

Aktives Mitglied
Nun, ich meine das Problem erkannt zu haben:
Ich habe etwas rumprobiert und bin über
GLCapabilities capabilities = new GLCapabilities();
capabilities.setHardwareAccelerated(false/true);
gestoßen.
Egal welches ich benutze, die Rendergeschwindigkeit ist gleich (!!).
Auch hat meine CPU eine Vollauslastung.

Dennoch gibt er mir munter an, dass capabilities.getHardwareAccelerated() true sei.
Auch erkennt er meine Grafikkarte und gibt deren Version korrekt aus.

Was mache ich?
 
G

Guest2

Gast
Moin,

ich gehe schon davon aus, dass das bei Dir hartwarenbeschleunigt läuft, sonst wäre das vermutlich noch langsamer. Die DisplayLists sind zwar schneller, trotzdem veraltet. VBO stimmt als Stichwort schon.

Ein Problem ist aber auch, das bei Dir jeweils 10000 Dreiecke exakt aufeinander liegen. Normalerwiese kann die Grafikkarte viele Dreiecke/Fragmente frühzeitig verwerfen. Das dürfte in Deinem Beispiel aber nur wesentlich ineffizienter funktionieren. Baue Lieber eine Szene, wie Du sie später wirklich brauchst, und schätze dann, wie viele Dreiecke du wirklich nutzen kannst. NVIDIA wirbt zwar mit Traumzahlen (imho irgendwo um die 150 Millionen Dreiecke pro Sekunde, bei der GF9600) in einer realistischen Szene ist das aber nicht erreichbar.

Nichtsdestotrotz, hier mal Dein Beispiel, so wie ich es schreiben würde:

Java:
package test;

import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;

import com.sun.opengl.util.Animator;
import com.sun.opengl.util.BufferUtil;


public class Simple implements GLEventListener {

    //x1, y1, z1, r1, g1, b1, x2, y2, z2, r2, g2, b2, ...
    private static float[] tetrahedron = {
        0.0f,  1.0f,  0.0f, 0.0f, 1.0f, 1.0f, -1.0f, -1.0f,  1.0f, 0.0f, 0.0f, 1.0f,  1.0f, -1.0f,  1.0f, 0.0f, 0.0f, 0.0f,
        0.0f,  1.0f,  0.0f, 0.0f, 1.0f, 1.0f,  1.0f, -1.0f,  1.0f, 0.0f, 0.0f, 1.0f,  0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
        0.0f,  1.0f,  0.0f, 0.0f, 1.0f, 1.0f,  0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, -1.0f, -1.0f,  1.0f, 0.0f, 0.0f, 0.0f,
        0.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,  1.0f, -1.0f,  1.0f, 0.1f, 0.1f, 0.1f, -1.0f, -1.0f,  1.0f, 0.2f, 0.2f, 0.2f};


    private GL             gl          = null;
    private GLU            glu         = null;

    private FloatBuffer    vertexArray = null;
    private int            vboHandle   = 0;
    private float          rot         = 0;

    private long           time;


    @Override
    public void init(final GLAutoDrawable drawable) {

        final int doubler = 10000;

        vertexArray = BufferUtil.newFloatBuffer(tetrahedron.length * doubler);

        for(int i = 0; i < doubler; i++)
            vertexArray.put(tetrahedron, 0, tetrahedron.length);

        vertexArray.rewind();

        gl = drawable.getGL();
        glu = new GLU();

        // VSync disabled for time measurement
        gl.setSwapInterval(0);  

        gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

        gl.glEnable(GL.GL_DEPTH_TEST);
        gl.glEnable(GL.GL_CULL_FACE);
        gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL.GL_COLOR_ARRAY);

        final IntBuffer result = BufferUtil.newIntBuffer(1);
        gl.glGenBuffers(1, result);
        vboHandle = result.get(0);

        gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vboHandle);
        gl.glBufferData(GL.GL_ARRAY_BUFFER, vertexArray.capacity() * 4, vertexArray, GL.GL_STATIC_DRAW);
        gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);

    }


    @Override
    public void display(final GLAutoDrawable drawable) {

        rot += 1;

        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
        gl.glLoadIdentity();

        gl.glTranslatef(0.0f, 0.0f, -5.0f);
        gl.glRotatef(rot, 0.0f, 1.0f, 0.0f);
        gl.glRotatef(rot, 1.0f, 0.0f, 0.0f);

        gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vboHandle);
        gl.glVertexPointer(3, GL.GL_FLOAT, 24, 0);
        gl.glColorPointer(3, GL.GL_FLOAT, 24, 12);
        gl.glDrawArrays(GL.GL_TRIANGLES, 0, vertexArray.capacity() / 6);
        gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);

        System.out.println(-(time - (time = System.nanoTime())) / 1000000);

    }


    @Override
    public void reshape(final GLAutoDrawable drawable, final int x, final int y, final int width, int height) {

        if (height <= 0)
            height = 1;

        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
        glu.gluPerspective(45.0f, (float) width / (float) height, 1.0, 20.0);
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();

    }


    @Override
    public void displayChanged(final GLAutoDrawable drawable, final boolean modeChanged, final boolean deviceChanged) {

    }


    public static void main(final String[] args) {

        final Simple simple = new Simple();
        final Frame frame = new Frame();
        final GLCanvas canvas = new GLCanvas();
        final Animator animator = new Animator(canvas);

        canvas.addGLEventListener(simple);
        frame.add(canvas);
        frame.setSize(500, 500);
        frame.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(final WindowEvent e) {

                new Thread(new Runnable() {
                    public void run() {

                        animator.stop();
                        System.exit(0);

                    }

                }).start();
            }
        });

        frame.setVisible(true);
        animator.start();
    }

}

Gruß,
Fancy
 

Mortal_Shadow

Aktives Mitglied
Vielen Dank für das ausführliche Codebeispiel.
Performancegewinn kommt hierbei hauptsächlich durch das Backfaceculling.
Ich habe es zum Vergleich in meinem alten Test aktiviert.
67ms pro Frame.
In deinem Test mit VBO:
57ms pro Frame.
Also auch so etwas schneller.

Und einen weiteren Flaschenhals habe ich entdeckt:
Es kommt extrem auf die Dreiecksgröße an (eigentlich ja logisch).
Beide Beispiele liefen im Fullscreen ("Simple" wurde angepasst) mit recht großem Tetraeder.
Macht man es kleiner gibt es einen enormen Performance-boost.

Ich denke das mir all das zusammen ausreicht für das was ich brauche.

Vielen Dank.
 

Ähnliche Java Themen

Neue Themen


Oben