Improving Performance?

I’m currently working on a project in Java using OpenGL via JOGL. The basic premise is that it is a Railroad simulator, which will need to display a large amount of lines 1px thick of varying colours (the track), a background to these that is 7px thick and dark gray (ballast), and display moving blocks on these (trains).

Currently, I’m using GL_LINES to draw the lines and their backgrounds on screen. The data is stored within an array, and track colours are determined at runtime by checking if certain bits are set in a number.

However, currently performance is quite low - down to 15fps at times. An older project which this is based on reached 50fps at similar points without trouble.

Any possible way to either draw this more efficiently than the code below, or to up the frame rate some other way?

I’ve included the majority of the draw code.

“bf” is my BitFinder class - it will return “true” or “false” depending on if a certain bit is set in an integer.

“tpa” is TrackPieceArray - a list of all the bits of track read from file. As well as x and y info, this contains information about labels and so forth.


public void display(GLAutoDrawable gLDrawable) {
        
        final GL gl = gLDrawable.getGL();
        gl.glClearColor(0.25f, 0.25f, 0.25f, 0.0f);
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
        gl.glLoadIdentity();
        
        gl.glTranslatef(mapXPos, mapYPos, 0.0f);
        
        
        drawTrackBallast(gl);
        drawTrackLines(gl);
        
        gl.glEnd();
        date = new Date();
        endTime = date.getTime();
        
        gl.glTranslatef(10.0f, 10.0f, 0.0f);
        long l = 1000l / (endTime - startTime);
        String s = Long.toString(l);
        //glut.glutBitmapString(GLUT.BITMAP_TIMES_ROMAN_24, s);
        System.out.println(s);
        
        date = new Date();
        startTime = date.getTime();
        
        gl.glTranslatef(-10.0f, -10.0f, 0.0f);
        
        gl.glFlush();
    }
    
    //Draws the 5px ballast
    public void drawTrackBallast(GL gl)
    {
        //Grey, 7px
        gl.glColor3f(0.5f, 0.5f, 0.5f);
        gl.glLineWidth(7.0f);
        gl.glBegin(GL.GL_LINES);
        
        for (int i = 0; i < tpa.length; i++){
            if(!bf.checkBit(tpa[i].trackType, 0) && sbc.isInView(tpa[i], mapXPos, mapYPos, mapWidth, mapHeight))
            {    
                gl.glVertex3f(tpa[i].x1, tpa[i].y1, 0.0f);
                gl.glVertex3f(tpa[i].x2, tpa[i].y2, 0.0f);
            }
        }
        
        //Done drawing lines
        gl.glEnd();
    }

//Draws the 1 px track lines.   
    public void drawTrackLines(GL gl)
    {
        //1px Wide
        gl.glLineWidth(1.0f);
        gl.glBegin(GL.GL_LINES);
        
        for (int i = 0; i < tpa.length; i++){
            if(!bf.checkBit(tpa[i].trackType, 0) && sbc.isInView(tpa[i], mapXPos, mapYPos, mapWidth, mapHeight))
            {            
                //Plain track, will be overridden by any of the following
                gl.glColor3f(0.0f, 0.0f, 0.0f);
                
                //Mainline Track
                if (bf.checkBit(tpa[i].trackType, 1))
                {
                    gl.glColor3f(0.5f, 0.25f, 0.0f);
                }
                
                //Load + Unload
                if (bf.checkBit(tpa[i].trackType, 2) && bf.checkBit(tpa[i].trackType, 6))
                {
                    gl.glColor3f(1.0f, 0.5f, 0.0f);
                }
                //Load
                else if (bf.checkBit(tpa[i].trackType, 6))
                {
                    gl.glColor3f(0.5f, 0.0f, 0.5f);
                }
                //Unload
                else if (bf.checkBit(tpa[i].trackType, 2))
                {
                    gl.glColor3f(0.0f, 0.5f, 0.0f);
                }
                
                gl.glVertex3f(tpa[i].x1, tpa[i].y1, 0.0f);
                gl.glVertex3f(tpa[i].x2, tpa[i].y2, 0.0f);
            }
        }
        
        //Done drawing lines
        gl.glEnd();
    }

Two things off the top of my head…

  1. Put your Vertex Data into Arrays. Take a look at glDrawArrays and also VBOs.

  2. Try GL_LINE_STRIPS which should (in theory) almost halve your vertex requirement.

I started using Arrays of Vertex Data - that did squeeze out a few more FPS.

GL_LINE_STRIPS is a bit harder to implement, as one vertex may be connected to 1, 2 or 3 lines, and I’d have to do a fair bit of parsing and juggling to draw the whole thing in one strip. In fact, there’s no guarantee all the lines will be connected anyway.

I was playing around earlier trying to determine the exact source of the slowness, and discovered it was actually mostly due to the code which determined whether or not to display a line if it was in screen, and if so, what colour to use.

I took this out and created a method to create an array of every line at the start - and bam, 60fps! And that’s only because I’m using an FPSAnimator to limit it to this.

Moral of the story: It cost more time to choose which lines to ignore as they’re outside view than it does to draw them anyway. That was a surprising one.

Why was that bit of code in there? I started with Java’s Graphics2D, and that was so slow to draw, that bit of code did help. OpenGL, it seems, is even more efficient than I thought!

The question is, how exactly you determine whether a line is visible. If you are doing this for every line independently, than you are wasting A LOT of cycles. That means your CPU will be so busy, it has no time to issue draw commands to the GPU and the GPU will actually sit idle, waiting for something to do.

Instead you should use something like a quadtree or so, which allows you to determine which cells are visible. All lines that are inside visible cells will then be drawn, without additional checks. Culling objects (polygons, entire meshes, lines, whetever …) using such structures (BSP-Trees, QuadTrees, Octrees, KdTrees, …) is very efficient and nearly always worth the effort. For the future you might want to do that. But VBOs are really the most important optimization for now.

Jan.