GLCanvas/GLJPanel phenomenon

Hi all,

I have a weird phenomenon (at least to me :)):

I started porting a small Java2D app to use jogl. So far I only draw a grid on the screen… and I have 2 computers to run this app (WinXP machine with a nVIDIA Quadro and a OpenSuSE 10.1 with Intel 945 GM onboard graphics).

Using GLJPanel (my favourite btw…) works fine on the WinXP machine, using almost no CPU at all (as I expect it). But the linux machine is stressed with around 100% CPU (most used by the X server) :frowning:

If i switch to GLCanvas it’s just the other way round… The app runs smoothly on linux and needs too much CPU on WinXP ???

I’m using Java5 with JOGL 1.1 RC3 (a very short test with Java6 showed the same behavior on the linux machine…)

Does anyone have a clue what I’m doing wrong? Here’s my display code (using display lists didn’t boost this too much…):


GL gl = drawable.getGL();
gl.glClear(GL.GL_COLOR_BUFFER_BIT);
    
float xMargin = MARGIN / drawable.getWidth();
float yMargin = MARGIN / drawable.getHeight();
gl.glColor3f(1.0f, 1.0f, 1.0f);
gl.glBegin(GL.GL_LINE_LOOP);
  gl.glVertex2f(xMargin, yMargin);
  gl.glVertex2f(1.0f - xMargin, yMargin);
  gl.glVertex2f(1.0f - xMargin, 1.0f - yMargin);
  gl.glVertex2f(xMargin, 1.0f - yMargin);
gl.glEnd();

// draw the grid:
float[] fragmentsX = getFragmentPositions(xMargin, 1.0f - xMargin, NR_OF_FRAGMENTS_X);
float[] fragmentsY = getFragmentPositions(yMargin, 1.0f - yMargin, NR_OF_FRAGMENTS_Y);

gl.glColor3f(0.8f, 0.8f, 0.8f);
gl.glBegin(GL.GL_LINES);
  // vertical lines (means fragmentsX):
  for(float f : fragmentsX) {
    gl.glVertex2f(xMargin + f, yMargin);
    gl.glVertex2f(xMargin + f, 1.0f - yMargin);
  }
  // horizontal lines (means fragmentsY):
  for(float f : fragmentsY) {
    gl.glVertex2f(xMargin, yMargin + f);
    gl.glVertex2f(1.0f - xMargin, yMargin + f);
  }
gl.glEnd();

gl.glFinish();


Are you continually redrawing the scene with an Animator, or redrawing it only on demand (i.e., in response to repaint() requests coming from the OS when the window takes damage)? If you’re continually redrawing it, you should expect it to take more CPU than necessary in pretty much all cases. You use the FPSAnimator to throttle back how quickly the scene is redrawn.

The high CPU consumption with the GLCanvas on NVidia hardware is mystifying, though. Are you running the latest drivers? Can you post a complete test case?

I’m fetching new points to draw them on the grid. So I wanted to repaint on demand when new points are available. In the end this is quite often (around 10 to 20 times a second) but it shouldn’t cause the CPU to take 100%?
Right now I repaint the whole grid when new points arrive using

Component.repaint()

I think I’ll prepare a test case now :slight_smile:

Here’s a test case:

GLEventListener impl:


import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;

public class TestRenderer implements GLEventListener {
  
  private static final float MARGIN = 0.05f;
  private static final int NR_OF_FRAGMENTS_X = 10;
  private static final int NR_OF_FRAGMENTS_Y = 5;
  
  private float leftMargin, rightMargin, bottomMargin, topMargin;
  private float[] xPos;
  private float[] yPos;

  public void display(GLAutoDrawable drawable) {
    long startTime = System.nanoTime();
    
    GL gl = drawable.getGL();
    gl.glClear(GL.GL_COLOR_BUFFER_BIT);
    
    // border:
    gl.glColor3f(0.8f, 0.8f, 0.8f);
    gl.glBegin(GL.GL_LINE_LOOP);
      gl.glVertex2f(leftMargin, bottomMargin);
      gl.glVertex2f(rightMargin, bottomMargin);
      gl.glVertex2f(rightMargin, topMargin);
      gl.glVertex2f(leftMargin, topMargin);
    gl.glEnd();
    
    // grid:
    gl.glBegin(GL.GL_LINES);
      // vertical lines (using xPos):
      for(float f : xPos) {
        gl.glVertex2f(leftMargin + f, bottomMargin);
        gl.glVertex2f(leftMargin + f, topMargin);
      }
      // horizontal lines (using yPos):
      for(float f : yPos) {
        gl.glVertex2f(leftMargin, bottomMargin + f);
        gl.glVertex2f(rightMargin, bottomMargin + f);
      }
    gl.glEnd();
    
    gl.glFinish();
    System.out.println("TestRenderer.display(): " +
        ((System.nanoTime() - startTime) / 1000000.0f) + " ms");
  }

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

  public void init(GLAutoDrawable drawable) {
    GL gl = drawable.getGL();
    gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  }

  public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
    GL gl = drawable.getGL();
    gl.glMatrixMode(GL.GL_PROJECTION);
    gl.glLoadIdentity();
    gl.glOrtho(x, width, y, height, 0.0, 1.0);
    
    leftMargin = MARGIN * width;
    rightMargin = width - leftMargin;
    bottomMargin = MARGIN * height;
    topMargin = height - bottomMargin;
    
    xPos = getFragmentPositions(leftMargin, rightMargin, NR_OF_FRAGMENTS_X);
    yPos = getFragmentPositions(bottomMargin, topMargin, NR_OF_FRAGMENTS_Y);
  }
  
  public static float[] getFragmentPositions(float min, float max, int frags) {
    float[] res = new float[frags + 1];
    float spacing = (max - min) / frags;
    
    for(int i = 0; i < res.length; ++i) {
      res[i] = i * spacing;
    }
    return res;
  }
}

and a simple starter class (uncomment/comment to switch between the canvas and panel impl):


import java.awt.Dimension;
import java.awt.event.WindowAdapter;

import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLJPanel;
import javax.swing.JFrame;

import com.sun.opengl.util.FPSAnimator;

public class Main {
  public static void main(String[] args) {
    JFrame frame = new JFrame("Weird GL problem test case");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setPreferredSize(new Dimension(768, 512));
    
    GLCapabilities caps = new GLCapabilities();
    System.out.println(caps);
    
    GLCanvas canvas = new GLCanvas(caps);
    canvas.addGLEventListener(new TestRenderer());
    frame.add(canvas);
    final FPSAnimator anim = new FPSAnimator(canvas, 10);
    
//    GLJPanel panel = new GLJPanel(caps);
//    panel.addGLEventListener(new TestRenderer());
//    frame.add(panel);
//    final FPSAnimator anim = new FPSAnimator(panel, 10);
    
    frame.addWindowListener(new WindowAdapter() {
      public void windowClosing() {
        anim.stop();
      }
    });
    
    frame.pack();
    anim.start();
    frame.setVisible(true);
  }
}

Test results:
Now even the GLCanvas version runs perfectly on my nVIDIA based laptop! Needs more research regarding my “final” application…

On the linux machine it’s like posted yesterday. sysout tells me that GLJPanel needs around 6-8 ms to finish and the CPU utilization is around 35% (X server). GLCanvas finishes the rendering in around 1 ms using no CPU…

I also got access to another WinXP machine. It’s an “older” one, P4 2.8 GHz with an Intel 82865G onboard graphics card. This machine behaves like the linux machine (both use onboard graphics), GLJPanel results in high CPU usage.
Is there any possiblity to see if GLJPanel is hardware accelerated or uses the image copy mechanism?

Conclusion:
I guess, I will stick with the GLCanvas after taking a deeper look at the GLCanvas on my laptop. It seems to be the most “compatible” for me, especially as I have no option to rely on nVIDIA/ATI graphics cards on every computer :(.

The GLJPanel in its default mode is partly hardware accelerated and partly uses image copies. It renders into a hardware-accelerated pbuffer, reads back the frame buffer into a Java BufferedImage, and uses Java 2D to draw the BufferedImage to the screen.

With more recent graphics cards and JDK 6, specifying -Dsun.java2d.opengl=true (or True for some debugging output) will enable a fully hardware-accelerated code path for the GLJPanel, but at the moment this only works on the minority of computers.

I agree with your decision to stick with the GLCanvas.