calling repaint of GLCanvas->Performance proble

Hi all,
got a big problem here!

In my GLEventListener I react on MouseWheelEvents with zoom in/out my 3d scene. Parallel a thread calls the repaint method of the GLCanvas frequently (10 times per second).
Heres the problem:
If I only display my 3d scene my CPU load is about 70% and the more I zoom into the scence it increases up to 100%!!!
I discovered that if don’t call the repaint method, but using an animator instead, the CPU load is about 5% . Here the problem is that I just see the zooming if resize the window ( containing the scene)!

Can somebody please tell me what’s wrong with using repaint?

Here’s the code of the run method of the updateThread:

public void run()
{
while (true)
{
try
{
//if (this.isInterrupted()) break;
Thread.sleep(100);
display.getContainer3D().repaint();
}
catch( java.lang.InterruptedException e)
{
//this.interrupt();
}
}
}

If you are running animator, there is no need for repaint or display for the canvas. If your project is rendering huge number of objects as in a molecular viewer, animator turns to killer, sucking up all cpu.What is better depends on type of your project, by the way repaint or display are tricky issues since jogl is not thread safe.

Here’s the code of my animator: (Got it from Nehe-lessons)

/*

  • Copyright © 2003 Sun Microsystems, Inc. All Rights Reserved.
  • Redistribution and use in source and binary forms, with or without
  • modification, are permitted provided that the following conditions are
  • met:
    • Redistribution of source code must retain the above copyright
  • notice, this list of conditions and the following disclaimer.
    • Redistribution in binary form must reproduce the above copyright
  • notice, this list of conditions and the following disclaimer in the
  • documentation and/or other materials provided with the distribution.
  • Neither the name of Sun Microsystems, Inc. or the names of
  • contributors may be used to endorse or promote products derived from
  • this software without specific prior written permission.
  • This software is provided “AS IS,” without a warranty of any kind. ALL
  • EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
  • INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
  • PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
  • MIDROSYSTEMS, INC. (“SUN”) AND ITS LICENSORS SHALL NOT BE LIABLE FOR
  • ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  • DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
  • ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
  • DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
  • DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
  • ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
  • SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
  • You acknowledge that this software is not designed or intended for use
  • in the design, construction, operation or maintenance of any nuclear
  • facility.
  • Sun gratefully acknowledges that this software was originally authored
  • and developed by Kenneth Bradley Russell and Christopher John Kline.
    */

package net.eads.sysde.display.framework.viewer;

import net.java.games.jogl.Animator;
import net.java.games.jogl.GLDrawable;
import net.java.games.jogl.GLException;

import java.util.Timer;
import java.util.TimerTask;

/**

  • An Animator can be attached to a GLDrawable to drive its

  • display() method in a loop. For efficiency, it sets up the
  • rendering thread for the drawable to be its own internal thread,
  • so it can not be combined with manual repaints of the
  • surface.
  • The Animator currently contains a workaround for a bug in

  • NVidia’s drivers (80174). The current semantics are that once an
  • Animator is created with a given GLDrawable as a target, repaints
  • will likely be suspended for that GLDrawable until the Animator is
  • started. This prevents multithreaded access to the context (which
  • can be problematic) when the application’s intent is for
  • single-threaded access within the Animator. It is not guaranteed
  • that repaints will be prevented during this time and applications
  • should not rely on this behavior for correctness.
    */

public class FPSAnimator extends Animator {
private GLDrawable drawable;
private RenderRunnable runnable = new RenderRunnable();
private Thread thread;
private boolean shouldStop;
private long delay;
private Timer renderTimer;
private ExceptionHandler exceptionHandler;

public FPSAnimator(GLDrawable drawable, int fps) {
    this(drawable, fps, null);
}

/** Creates a new Animator for a particular drawable. */
public FPSAnimator(GLDrawable drawable, int fps, ExceptionHandler exceptionHandler) {
    super(drawable);
    this.exceptionHandler = exceptionHandler;
    this.drawable = drawable;
    this.delay = 1000 / fps;
}

/** Starts this animator. */
public synchronized void start() {
    if (thread != null) {
        throw new GLException("Already started");
    }
    thread = new Thread(runnable);
    thread.start();

    renderTimer = new Timer();
    renderTimer.schedule(new TimerTask() {
        public void run() {
            runnable.nextFrame();
        }
    }, 0, delay);
}

/** Stops this animator, blocking until the animation thread has
 finished. */
public synchronized void stop() {
    shouldStop = true;
    while (shouldStop && thread != null) {
        try {
            wait();
        } catch (InterruptedException e) {
        }
        renderTimer.cancel();
    }
}

public boolean isFrameRateLimitEnabled() {
    return runnable.isFrameRateLimitEnabled();
}

public void setFrameRateLimitEnabled(boolean frameRateLimit) {
    runnable.setFrameRateLimitEnabled(frameRateLimit);
}

private class RenderRunnable implements Runnable {
    private boolean frameRateLimitEnabled = true;

    public boolean isFrameRateLimitEnabled() {
        return frameRateLimitEnabled;
    }

    public void setFrameRateLimitEnabled(boolean frameRateLimit) {
        frameRateLimitEnabled = frameRateLimit;
        nextFrameSync();
    }

    public void nextFrame() {
        if (frameRateLimitEnabled)
            nextFrameSync();
    }

    private synchronized void nextFrameSync() {
        notify();
    }

    public void run() {
        boolean noException = false;
        try {
            // Try to get OpenGL context optimization since we know we
            // will be rendering this one drawable continually from
            // this thread; make the context current once instead of
            // making it current and freeing it each frame.
            drawable.setRenderingThread(Thread.currentThread());

            // Since setRenderingThread is currently advisory (because
            // of the poor JAWT implementation in the Motif AWT, which
            // performs excessive locking) we also prevent repaint(),
            // which is called from the AWT thread, from having an
            // effect for better multithreading behavior. This call is
            // not strictly necessary, but if end users write their
            // own animation loops which update multiple drawables per
            // tick then it may be necessary to enforce the order of
            // updates.
            drawable.setNoAutoRedrawMode(true);

            while (!shouldStop) {
                noException = false;
                drawable.display();
                if (frameRateLimitEnabled) {
                    synchronized (this) {
                        if (frameRateLimitEnabled) {
                            try {
                                wait();
                            } catch (InterruptedException e) {
                            }
                        }
                    }
                }
                noException = true;
            }
        } catch (Exception e) {
            if (exceptionHandler != null)
                exceptionHandler.handleException(e);
        } finally {
            shouldStop = false;
            drawable.setNoAutoRedrawMode(false);
            try {
                // The surface is already unlocked and rendering
                // thread is already null if an exception occurred
                // during display(), so don't disable the rendering
                // thread again.
                if (noException) {
                    drawable.setRenderingThread(null);
                }
            } finally {
                synchronized (FPSAnimator.this) {
                    thread = null;
                    FPSAnimator.this.notify();
                }
            }
        }
    }
}

}

If I use this animator I do not see any changes in the scence if I use the mousewheel! Why ? If I us the updatethread it works(zooming), but then the performanceproblem occurs!

Maybe the question is : “What is your code for zooming”,?

The fps animator should work, whatever the changes are. At least I think so.

Lesson 5 modified for rotation(left) and zooming(right)based on mouse events. Animator is commented out.

/*

  • Lesson05.java
  • Created on July 15, 2003, 11:30 AM
    */

import java.awt.;
import java.awt.event.
;

import net.java.games.jogl.;
import net.java.games.jogl.util.
;

public class Lesson05 {

/** Program’s main entry point

  • @param args command line arguments.
    */
    public static void main(String[] args) {
    Frame frame = new Frame(“Lesson 5: 3D Shapes”);
    GLCanvas canvas = GLDrawableFactory.getFactory().createGLCanvas(new
    GLCapabilities());
    canvas.setGL(new DebugGL(canvas.getGL()));
    L_5_Renderer r = new L_5_Renderer();
    canvas.addGLEventListener®;
    canvas.addKeyListener®;
    canvas.addMouseMotionListener®;
    canvas.addMouseListener®;
frame.add(canvas);
frame.setSize(640, 480);
//animator = new Animator(canvas);
frame.addWindowListener(new WindowAdapter() {
  public void windowClosing(WindowEvent e) {
    //animator.stop();
    System.exit(0);
  }
});
frame.show();
//animator.start();
canvas.requestFocus();

}
}

class L_5_Renderer
implements GLEventListener,
KeyListener, MouseMotionListener, MouseListener {

GL gl;
GLU glu;
private float rquadX = 0.0f;
private float rtriX = 0.0f;
private float rquadY = 0.0f;
private float rtriY = 0.0f;
private float rquadZ = 0.0f;
private float transZ =-6.0f;
private int buttonType=-1;

public void display(GLDrawable gLDrawable) {
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();

gl.glTranslatef(0.0f, 0.0f, transZ);

gl.glRotatef(rquadX, 0.0f, 1.0f, 0.0f);
gl.glRotatef(rquadY, 1.0f, 0.0f, 0.0f);
gl.glRotatef(rquadZ, 0.0f, 0.0f, 1.0f);
//Rotate end…

gl.glBegin(GL.GL_QUADS); // Draw A Quad
gl.glColor3f(0.0f, 1.0f, 0.0f); // Set The Color To Green
gl.glVertex3f(1.0f, 1.0f, -1.0f); // Top Right Of The Quad (Top)
gl.glVertex3f( -1.0f, 1.0f, -1.0f); // Top Left Of The Quad (Top)
gl.glVertex3f( -1.0f, 1.0f, 1.0f); // Bottom Left Of The Quad (Top)
gl.glVertex3f(1.0f, 1.0f, 1.0f); // Bottom Right Of The Quad (Top)

gl.glColor3f(1.0f, 0.5f, 0.0f); // Set The Color To Orange
gl.glVertex3f(1.0f, -1.0f, 1.0f); // Top Right Of The Quad (Bottom)
gl.glVertex3f( -1.0f, -1.0f, 1.0f); // Top Left Of The Quad (Bottom)
gl.glVertex3f( -1.0f, -1.0f, -1.0f); // Bottom Left Of The Quad (Bottom)
gl.glVertex3f(1.0f, -1.0f, -1.0f); // Bottom Right Of The Quad (Bottom)

gl.glColor3f(1.0f, 0.0f, 0.0f); // Set The Color To Red
gl.glVertex3f(1.0f, 1.0f, 1.0f); // Top Right Of The Quad (Front)
gl.glVertex3f( -1.0f, 1.0f, 1.0f); // Top Left Of The Quad (Front)
gl.glVertex3f( -1.0f, -1.0f, 1.0f); // Bottom Left Of The Quad (Front)
gl.glVertex3f(1.0f, -1.0f, 1.0f); // Bottom Right Of The Quad (Front)

gl.glColor3f(1.0f, 1.0f, 0.0f); // Set The Color To Yellow
gl.glVertex3f(1.0f, -1.0f, -1.0f); // Bottom Left Of The Quad (Back)
gl.glVertex3f( -1.0f, -1.0f, -1.0f); // Bottom Right Of The Quad (Back)
gl.glVertex3f( -1.0f, 1.0f, -1.0f); // Top Right Of The Quad (Back)
gl.glVertex3f(1.0f, 1.0f, -1.0f); // Top Left Of The Quad (Back)

gl.glColor3f(0.0f, 0.0f, 1.0f); // Set The Color To Blue
gl.glVertex3f( -1.0f, 1.0f, 1.0f); // Top Right Of The Quad (Left)
gl.glVertex3f( -1.0f, 1.0f, -1.0f); // Top Left Of The Quad (Left)
gl.glVertex3f( -1.0f, -1.0f, -1.0f); // Bottom Left Of The Quad (Left)
gl.glVertex3f( -1.0f, -1.0f, 1.0f); // Bottom Right Of The Quad (Left)

gl.glColor3f(1.0f, 0.0f, 1.0f); // Set The Color To Violet
gl.glVertex3f(1.0f, 1.0f, -1.0f); // Top Right Of The Quad (Right)
gl.glVertex3f(1.0f, 1.0f, 1.0f); // Top Left Of The Quad (Right)
gl.glVertex3f(1.0f, -1.0f, 1.0f); // Bottom Left Of The Quad (Right)
gl.glVertex3f(1.0f, -1.0f, -1.0f); // Bottom Right Of The Quad (Right)
gl.glEnd(); // Done Drawing The Quad
gl.glFlush();

}

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

public void init(GLDrawable gLDrawable) {

gl = gLDrawable.getGL();
glu = gLDrawable.getGLU();

gl.glShadeModel(GL.GL_SMOOTH); // Enable Smooth Shading
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
gl.glClearDepth(1.0f); // Depth Buffer Setup
gl.glEnable(GL.GL_DEPTH_TEST); // Enables Depth Testing
gl.glDepthFunc(GL.GL_LEQUAL); // The Type Of Depth Testing To Do
gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST); // Really Nice Perspective Calculations
}

public void reshape(GLDrawable gLDrawable, int x, int y, int width,
int height) {
if (height <= 0) { // avoid a divide by zero error!
height = 1;
}
final float h = (float) width / (float) height;
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluPerspective(45.0f, h, 1.0, 20.0);
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glLoadIdentity();
}

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

public void keyReleased(KeyEvent e) {}

public void keyTyped(KeyEvent e) {}

private int prevMouseX, prevMouseY;
private boolean mouseRButtonDown = false;

public void mouseDragged(MouseEvent e) {
int x = e.getX();
int y = e.getY();

Dimension size = e.getComponent().getSize();
GLCanvas canvas = (GLCanvas) e.getSource();
canvas.repaint();
System.out.println("buttonType " + buttonType);

float thetaX = 360.0f * (float) (x - prevMouseX) / (float) size.width;
float thetaY = 360.0f * (float) ( -prevMouseY + y) / (float) size.height;

if(buttonType==1)
{
rquadX += thetaX;
rtriX += thetaX;
rquadY += thetaY;
rtriY += thetaY;
}
if(buttonType==3)
{
transZ +=(float) ( -prevMouseY + y)/100;
}

prevMouseX = x;
prevMouseY = y;
}

public void mouseMoved(MouseEvent e) {
}

// Methods required for the implementation of MouseListener
public void mouseEntered(MouseEvent e) {}

public void mouseExited(MouseEvent e) {}

public void mousePressed(MouseEvent e) {
buttonType=e.getButton();
prevMouseX = e.getX();
prevMouseY = e.getY();
if ( (e.getModifiers() & e.BUTTON3_MASK) != 0) {
mouseRButtonDown = true;
}
}

public void mouseReleased(MouseEvent e) {
if ( (e.getModifiers() & e.BUTTON3_MASK) != 0) {
mouseRButtonDown = false;
}
}

public void mouseClicked(MouseEvent e) {}

}

I think the reason why the scene isn’t repainted if I use the animator is that I don’t store the scene in a frame but in a Jpanel which itself is stored in an internal frame! Is that possible! The reason why I did it that why is that I thought that only with JPanels the hardware acceleration is used entirely… Is there a differrence between the components regarding the harware accelaration ???

When I used to use JPanel (lightweight components) to render my JOGL/GL4Java renderers, I received no hardware acceleration! Using a java.awt.Frame I was able to take advantage of the 3D graphic card and render at some 100 fps with no problem at all. The Frame class is heavyweight and thus should be able to provide hardware accelerated graphics.

What happens if you comment out these two lines?

canvas.repaint();
System.out.println("buttonType " + buttonType);

The canvas is automatically being repainted all the time, so these extra calls will have no effect but to slow things down.

The Jogl docs mention it somewhere, but for various reasons rendering to a lightweight GLJCanvas will never revceive hardware acceleration. You’ll have to use a heavyweight component if you want decent speeds.

Nothing stopping you putting that heavyweight GLCanvas on a lightweight component like a JPanel though, as long as you’re careful with the depth.

Sorry, but what do you mean with “as long as you’re careful with the depth” ???

You can’t place any light weight components on top of the heavy weight GLCanvas. Well, you can, you just won’t ever see them. =)

Hi, just didn’t fix the problem :-[

At the moment my scene is stored in a JInternalFrame and I have a FPSAnimator running. But the problem is still there.

Here’s an extract of the file created by the profiler during running my programm.

CPU SAMPLES BEGIN (total = 1373) Mon Jun 21 10:39:46 2004
rank self accum count trace method
1 95.99% 95.99% 1318 18 sun.awt.windows.WToolkit.eventLoop

TRACE 18:
sun.awt.windows.WToolkit.eventLoop(WToolkit.java:Native method)
sun.awt.windows.WToolkit.run(WToolkit.java:262)
java.lang.Thread.run(Thread.java:534)

What could be the reason for this. Could it be a mistake which I made using jogl or could it be that the mistake sticks in the application surrounding jogl?