Help on Geometry Shaders using jogl

Hi, I am a new to shaders in general, especially geometry shaders. I have a 8800GTX card.
When I tried to run a simple geometry shader program (obtained from a tutorial online), it doesnt seem to be working when I use jogl, while the same thing works when written in C.

in the GLEventListener’s init() method, i initialize the geometry shader as follows


g = gl.glCreateShader(GL.GL_GEOMETRY_SHADER_EXT);
String [] gs = new String [1];
gs[0] = readShaderFile("glsl/toon.geom");

int [] gb = new int [] {gs[0].length()};
gl.glShaderSource(g,1,gs,gb,0);
gl.glCompileShader(g);

p = gl.glCreateProgram();
gl.glAttachShader(p,g);
		
gl.glProgramParameteriEXT(g,GL.GL_GEOMETRY_INPUT_TYPE_EXT,GL.GL_LINES);
gl.glProgramParameteriEXT(g,GL.GL_GEOMETRY_OUTPUT_TYPE_EXT,GL.GL_LINE_STRIP);

int [] temp = new int[2];
gl.glGetIntegerv(GL.GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT,temp,0);
gl.glProgramParameteriEXT(g,GL.GL_GEOMETRY_VERTICES_OUT_EXT,temp[0]);

gl.glLinkProgram(p);
gl.glUseProgram(p);

and my geometry shader code is as follows. (I only draw one line in the display() method , and i want two lines in the output)


#version 120 
#extension GL_EXT_geometry_shader4 : enable

void main(void)
{
	//increment variable
	int i;

	//Pass-thru!
	for(i=0; i< gl_VerticesIn; i++){
		gl_Position = gl_PositionIn[i];
		EmitVertex();
	}
	EndPrimitive();

	//New piece of geometry!  We just swizzle the x and y terms
	for(i=0; i< gl_VerticesIn; i++){
		gl_Position = gl_PositionIn[i];
		gl_Position.xy = gl_Position.yx;
		EmitVertex();
	}
	EndPrimitive();
}

Any idea where I might be making a mistake. any help would be appreciated.

I have two things to say:

  1. You should check for errors in both your shaders and your programs using code similar to

In my Shader.java :


    private void checkCompileError(GL gl) {
        IntBuffer status = BufferUtil.newIntBuffer(1);
        gl.glGetShaderiv(id, GL.GL_COMPILE_STATUS, status);

        if (status.get() == GL.GL_FALSE) {
            getInfoLog(gl);
        } else {
            System.out.println("Successfully compiled shader " + id);
        }
    }

    private void getInfoLog(GL gl) {
        IntBuffer infoLogLength = BufferUtil.newIntBuffer(1);
        gl.glGetShaderiv(id, GL.GL_INFO_LOG_LENGTH, infoLogLength);

        ByteBuffer infoLog = BufferUtil.newByteBuffer(infoLogLength.get(0));
        gl.glGetShaderInfoLog(id, infoLogLength.get(0), null, infoLog);

        String infoLogString =
                Charset.forName("US-ASCII").decode(infoLog).toString();
        throw new Error("Shader compile error\n" + infoLogString);
    }

In my Program.java:


    private void checkLinkAndValidationErrors(GL gl) {
        IntBuffer status = BufferUtil.newIntBuffer(1);
        gl.glGetProgramiv(id, GL.GL_LINK_STATUS, status);

        if (status.get() == GL.GL_FALSE) {
            getInfoLog(gl);
        } else {
            status.rewind();
            gl.glValidateProgram(id);
            gl.glGetProgramiv(id, GL.GL_VALIDATE_STATUS, status);
            if (status.get() == GL.GL_FALSE) {
                getInfoLog(gl);
            } else {
                System.out.println("Successfully linked program " + id);
            }
        }
    }

    private void getInfoLog(GL gl) {
        IntBuffer infoLogLength = BufferUtil.newIntBuffer(1);
        gl.glGetProgramiv(id, GL.GL_INFO_LOG_LENGTH, infoLogLength);

        ByteBuffer infoLog = BufferUtil.newByteBuffer(infoLogLength.get(0));
        gl.glGetProgramInfoLog(id, infoLogLength.get(0), null, infoLog);

        String infoLogString =
                Charset.forName("US-ASCII").decode(infoLog).toString();
        throw new Error("Program compile error\n" + infoLogString);
    }

  1. It looks as if you are trying to send the geometry shader’s source code as one String. Although it doesn’t matter whether or not you start a new line for the code, it does matter for pre-processor statements and comments.

IE it will understand

	for(i=0; i< gl_VerticesIn; i++){gl_Position = gl_PositionIn[i];EmitVertex();}

but it won’t understand

#version 120 #extension GL_EXT_geometry_shader4 : enablevoid main(void){	//increment variableint i;

Your readShaderFile should return an array of Strings each containing one line of the source code.

EDIT : Acctually you may only need one String if each line is separated with \n. You should experiment with this.

thanks for the suggestions, but the geometry shader still doesn’t work :frowning:

As for your questions,

  1. the shader code compiled and linked succesfully.
  2. The shader program is in a single string with a “\n” after each line.

I didn’t mention it in my previously, but its only the geometry shader that is not working. The vertex and fragment shaders work fine.
(Actually when using the geometry shader, there is no output on the screen, not even the original line that i draw).

EDIT: When I attach only the geometry shader ( and not the vertex and fragment shader) there is a link error, but when all three shaders are attached then, the shader program successfully links. Also, the infostring is emty, so I can’t figure out what the error is.

I am attaching my entire code, may be something is wrong with it.


package hope.it.works;

import java.awt.BorderLayout;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.BufferedReader;
import java.io.FileReader;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.charset.Charset;
import java.text.DecimalFormat;
import java.text.NumberFormat;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.swing.JFrame;

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

public class Shader extends JFrame implements GLEventListener, WindowListener {

	private static final long serialVersionUID = 1L;
	boolean first = true;
	int list;
	int no;
	double[] x, y, z, a;

	public Shader() {
		super("Test Shader");

		setLayout(new BorderLayout());
		addWindowListener(this);

		setSize(850, 850);
		setVisible(true);

		setupJOGL();
		String s = "0.00";
		noFormat = new DecimalFormat(s);
	}

	private void setupJOGL() {
		GLCapabilities caps = new GLCapabilities();
		caps.setDoubleBuffered(true);
		caps.setHardwareAccelerated(true);
		GLCanvas canvas = new GLCanvas(caps);
		canvas.addGLEventListener(this);

		add(canvas, BorderLayout.CENTER);

		Animator anim = new Animator(canvas);
		anim.start();
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new Shader().setVisible(true);
	}

	public void display(GLAutoDrawable drawable) {
		GL gl = drawable.getGL();

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

		gl.glColor3f(1, 0, 0);
		gl.glBegin(GL.GL_LINES);
		gl.glVertex2d(0, -d);
		gl.glVertex2d(0, d);
		gl.glEnd();
		updateFPS();
	}

	public void displayChanged(GLAutoDrawable arg0, boolean arg1, boolean arg2) {
		// TODO Auto-generated method stub

	}

	public void init(GLAutoDrawable drawable) {
		GL gl = drawable.getGL();

		gl.glClearColor(0, 0, 0, 0);
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		gl.glOrtho(0, 1, 0, 1, -1, 1);

		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glLoadIdentity();

		setupShaders(gl);
	}

	int p;
	int v;
	int f;
	int g;

	private void setupShaders(GL gl) {

		v = gl.glCreateShader(GL.GL_VERTEX_SHADER);
		f = gl.glCreateShader(GL.GL_FRAGMENT_SHADER);
		g = gl.glCreateShader(GL.GL_GEOMETRY_SHADER_EXT);

		String[] vs = new String[1];
		vs[0] = readShaderFile("glsl/toon.vert");
		String[] gs = new String[1];
		gs[0] = readShaderFile("glsl/toon.geom");
		String[] fs = new String[1];
		fs[0] = readShaderFile("glsl/toon.frag");

		int[] vb = new int[] { vs[0].length() };
		gl.glShaderSource(v, 1, vs, vb, 0);

		int[] fb = new int[] { fs[0].length() };
		gl.glShaderSource(f, 1, fs, fb, 0);

		int[] gb = new int[] { gs[0].length() };
		gl.glShaderSource(g, 1, gs, gb, 0);

		gl.glCompileShader(v);
		checkCompileError(gl, v);

		gl.glCompileShader(f);
		checkCompileError(gl, f);

		gl.glCompileShader(g);
		checkCompileError(gl, g);

		p = gl.glCreateProgram();

		gl.glAttachShader(p, f);
		gl.glAttachShader(p, v);
		gl.glAttachShader(p, g);

		gl
				.glProgramParameteriEXT(g, GL.GL_GEOMETRY_INPUT_TYPE_EXT,
						GL.GL_LINES);
		gl.glProgramParameteriEXT(g, GL.GL_GEOMETRY_OUTPUT_TYPE_EXT,
				GL.GL_LINE_STRIP);

		int[] temp = new int[2];
		gl.glGetIntegerv(GL.GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT, temp, 0);
		gl.glProgramParameteriEXT(g, GL.GL_GEOMETRY_VERTICES_OUT_EXT, temp[0]);

		gl.glLinkProgram(p);
		checkLinkAndValidationErrors(gl, p);
		gl.glUseProgram(p);
	}

	private void checkCompileError(GL gl, int id) {
		IntBuffer status = BufferUtil.newIntBuffer(1);
		gl.glGetShaderiv(id, GL.GL_COMPILE_STATUS, status);

		if (status.get() == GL.GL_FALSE) {
			getInfoLog(gl, id);
		} else {
			System.out.println("Successfully compiled shader " + id);
		}
	}

	private void getInfoLog(GL gl, int id) {
		IntBuffer infoLogLength = BufferUtil.newIntBuffer(1);
		gl.glGetShaderiv(id, GL.GL_INFO_LOG_LENGTH, infoLogLength);

		ByteBuffer infoLog = BufferUtil.newByteBuffer(infoLogLength.get(0));
		gl.glGetShaderInfoLog(id, infoLogLength.get(0), null, infoLog);

		String infoLogString = Charset.forName("US-ASCII").decode(infoLog)
				.toString();
		throw new Error("Shader compile error\n" + infoLogString);
	}

	private void checkLinkAndValidationErrors(GL gl, int id) {
		IntBuffer status = BufferUtil.newIntBuffer(1);
		gl.glGetProgramiv(id, GL.GL_LINK_STATUS, status);

		if (status.get() == GL.GL_FALSE) {
			getInfoLog(gl, id);
		} else {
			status.rewind();
			gl.glValidateProgram(id);
			gl.glGetProgramiv(id, GL.GL_VALIDATE_STATUS, status);
			if (status.get() == GL.GL_FALSE) {
				getInfoLog(gl, id);
			} else {
				System.out.println("Successfully linked program " + id);
			}
		}
	}

	private String readShaderFile(String file) {
		String s = "";
		try {
			BufferedReader r = new BufferedReader(new FileReader(file));
			String t = r.readLine();
			while (t != null) {
				s += t;
				s += "\n";
				t = r.readLine();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return s;
	}

	int w;
	int h;
	int d;

	public void reshape(GLAutoDrawable drawable, int x, int y, int width,
			int height) {
		GL gl = drawable.getGL();

		gl.glViewport(0, 0, width, height);
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();

		w = width / 2;
		h = height / 2;
		gl.glOrtho(-1 * w, w, -1 * h, h, -600, 600);
		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glLoadIdentity();
		d = Math.min(w, h);
	}

	public void windowActivated(WindowEvent e) {
		// TODO Auto-generated method stub

	}

	public void windowClosed(WindowEvent e) {
		// TODO Auto-generated method stub

	}

	public void windowClosing(WindowEvent e) {
		System.exit(0);
	}

	public void windowDeactivated(WindowEvent e) {
		// TODO Auto-generated method stub

	}

	public void windowDeiconified(WindowEvent e) {
		// TODO Auto-generated method stub

	}

	public void windowIconified(WindowEvent e) {
		// TODO Auto-generated method stub

	}

	public void windowOpened(WindowEvent e) {
		// TODO Auto-generated method stub

	}

	private double stTime;
	private double enTime;
	private double frameCt;
	private NumberFormat noFormat;

	private void updateFPS() {
		enTime = System.currentTimeMillis();
		double diff = enTime - stTime;
		frameCt++;
		if (diff > 1000) {
			double fps = frameCt * 1000 / diff;
			stTime = enTime;
			String s = noFormat.format(fps);
			this.setTitle("Name [FPS: " + s + "]");
			frameCt = 0;
		}
	}
}

The shaders im using are as follows

The vertex shader


void main()
{	
	//Transform the vertex (ModelViewProj matrix)
	gl_Position = ftransform();
}

The fragment shader


void main()
{	
	//Shade to blue!
	gl_FragColor = vec4(0.0,0.0,1.0,1.0);	
}

I had already posted the geometry shader code in my initial post.

OK Fixed.

  1. You should use the DebugGL object as follows:

    public void init(GLAutoDrawable drawable) {
        drawable.setGL(new DebugGL(drawable.getGL()));
        GL gl = drawable.getGL();

        gl.glClearColor(0, 0, 0, 0);
        ....

We checked for program errors and shader errors but forgot to check for basic OpenGL errors!
The DebugGL object will call glGetError after every gl command.

  1. This will show you that the problem is

        gl.glProgramParameteriEXT(g, GL.GL_GEOMETRY_INPUT_TYPE_EXT, GL.GL_LINES);
        gl.glProgramParameteriEXT(g, GL.GL_GEOMETRY_OUTPUT_TYPE_EXT, GL.GL_LINE_STRIP);
        ...
        gl.glProgramParameteriEXT(g, GL.GL_GEOMETRY_VERTICES_OUT_EXT, temp[0]);

You are setting program parameters and these apply to the program not the geometry shader.
IE you should have

        gl.glProgramParameteriEXT(p, GL.GL_GEOMETRY_INPUT_TYPE_EXT, GL.GL_LINES);
        ...

It now works. Thanks a lot.

EDIT: I have another basic question. When I tried using only the vertex shader and geometry shader, I found that there was no output, but using these two along with the fragment shader, it worked. Till now i was under the assumption that if no fragment shader is specified, then the opengl default will be used. But I guess i was wrong, and the shader program uses whatever shaders that were attached to it. If this is case, what do I need to do to get the default functionality vertex shader and fragment shader to work with my customized geometry shader?

According to the spec, a vertex or a fragment shader should work on its own. There are a few things that make it complicated, and not recommended, however. If you’re just writing a vertex shader, there are a lot of variables you have to give values to provide all of the information that the fixed function pipeline would already have.

In your example shaders, you don’t pass color information from the vertex shader. This means that the fragment stage for the ffp doesn’t have any color. What’s probably doing is just rendering everything as black, or the last set color.

Thanks, I set the front and back color in the vertex shader and it works now.

Now I have yet another problem ??? If I generate primitives within a loop (where the iterations of the loop is variable), then the performance of the program drastically decreases. Where as if I loop a constant no. of times (Ex. for (int i = 0;i < 10; i++) ), then there is no performance degradation. Also the constant seems to be a small number. Any one knows if there is an optimal way for generating as many output primitives as possible? The maximum no. of output vertices as specified in the glProgramParameteriEXT() method is 1024, so I have no idea why this should happen.

This is a result of the design of the hardware.

Your GPU will have dozens of shader units, probably a multiple of 16 or 32.

Now the problem is that each shader unit isn’t isolated, it is located in a block of shaders (probably 16 or 32 shaders per block). There is only 1 instructor dispatcher per block, so all shaders in that block are working in lockstep on the same instruction. When there are IF statements (or loops, that cannot be unrolled - loops with a constant can be unrolled…), and 50% of the shaders evaluate the condition to true and 50% to false, you’ll have 50% of your shaders in that block idling, waiting for the other shaders to finish those instructions that are part of the IF branch, you get the reverse effect when the ELSE branch is executed (if any).

When 10% of of the fragments enter an IF branch, 90% will be blocking. That’s what causing your dynamic-for-loops to drastically reduce performance.

Besides that, branch-prediction and conditional jump themselves are not so performant on GPUs.

I thought ATi AMD GPUs have a more efficient design that ‘lock step’ , but you might have to Google that.

Even if AMD does have an efficient design, it doesnt help much since they do not provide GLSL support for geometric shaders in their OpenGL driver.

Those goddamn sons of bitches!

Does any one know how i can debug a shader program? I heard gDebugger supports shader debugging, but dont know how to run a java application for it. I was wondering if there is any Eclipse plugin for debugging shaders ;D