package javax.media.opengl.glsl; import javax.media.opengl.*; import java.util.HashMap; import java.util.Iterator; import java.nio.*; import java.io.PrintStream; public class ShaderProgram { public ShaderProgram() { id = getNextID(); } public boolean linked() { return programLinked; } public boolean inUse() { return programInUse; } public int program() { return shaderProgram; } /** * returns the uniq shader id as an integer * @see #key() */ public int id() { return id.intValue(); } /** * returns the uniq shader id as an Integer * * @see #id() */ public Integer key() { return id; } /** * @see #glReleaseAllVertexAttributes */ public synchronized void release(GL2ES2 gl) { release(gl, false); } /** * @see #glReleaseAllVertexAttributes */ public synchronized void release(GL2ES2 gl, boolean releaseShaderToo) { glUseProgram(gl, false); for(Iterator iter=shaderMap.values().iterator(); iter.hasNext(); ) { ShaderCode shaderCode = (ShaderCode) iter.next(); gl.glDetachShader(shaderProgram, shaderCode.shader()); if(releaseShaderToo) { shaderCode.release(gl); } } shaderMap.clear(); gl.glDeleteProgram(shaderProgram); shaderProgram=-1; } // // ShaderCode handling // /** * Adds a new shader to a this non running program. * * @return false if the program is in use, or the shader already exist, * otherwise true. */ public synchronized boolean add(ShaderCode shaderCode) { if(shaderMap.containsKey(shaderCode.key())) return false; shaderMap.put(shaderCode.key(), shaderCode); return true; } public synchronized ShaderCode getShader(int id) { return (ShaderCode) shaderMap.get(new Integer(id)); } // // Program handling // /** * Replace a shader in a 'running' program. * Refetches all previously bin/get attribute names * and resets all attribute data as well * * @see getAttribLocation * @param gl * @param oldShaderID the to be replace Shader * @param newShader the new ShaderCode * @param verboseOut the optional verbose outputstream * @throws GLException is the program is not linked * * @see #glRefetchAttribLocations * @see #glResetAllVertexAttributes * @see #glReplaceShader */ public synchronized boolean glReplaceShader(GL2ES2 gl, int oldShaderID, ShaderCode newShader, PrintStream verboseOut) { if(!programLinked) throw new GLException("Program is not linked"); boolean shaderWasInUse = programInUse; glUseProgram(gl, false); if(!newShader.compile(gl, verboseOut)) { return false; } if(oldShaderID>=0) { ShaderCode oldShader = (ShaderCode) shaderMap.remove(new Integer(oldShaderID)); if(null!=oldShader) { gl.glDetachShader(shaderProgram, oldShader.shader()); } } add(newShader); gl.glAttachShader(shaderProgram, newShader.shader()); gl.glLinkProgram(shaderProgram); if ( ! gl.glIsProgramValid(shaderProgram, System.err) ) { return false; } if(shaderWasInUse) { glUseProgram(gl, true); } return true; } public synchronized boolean link(GL2ES2 gl, PrintStream verboseOut) { if(programLinked) throw new GLException("Program is already linked"); if(0>shaderProgram) { shaderProgram = gl.glCreateProgram(); } for(Iterator iter=shaderMap.values().iterator(); iter.hasNext(); ) { ShaderCode shaderCode = (ShaderCode) iter.next(); if(!shaderCode.compile(gl, verboseOut)) { return false; } gl.glAttachShader(shaderProgram, shaderCode.shader()); } // Link the program gl.glLinkProgram(shaderProgram); programLinked = gl.glIsProgramValid(shaderProgram, System.err); return programLinked; } public boolean equals(Object obj) { if(this==obj) return true; if(obj instanceof ShaderCode) { return id()==((ShaderCode)obj).id(); } return false; } public int hashCode() { return id.intValue(); } public String toString() { StringBuffer buf = new StringBuffer(); buf.append("ShaderProgram[id="+id); buf.append(", linked="+programLinked+", inUse="+programInUse+", program: "+shaderProgram+", ["); for(Iterator iter=shaderMap.values().iterator(); iter.hasNext(); ) { buf.append((ShaderCode) iter.next()); buf.append(" "); } buf.append("]"); return buf.toString(); } protected synchronized void glUseProgram(GL2ES2 gl, boolean on) { if(!programLinked) throw new GLException("Program is not linked"); if(programInUse==on) return; gl.glUseProgram(on?shaderProgram:0); programInUse = on; //Throwable tX = new Throwable("Info: ShaderProgram.glUseProgram: "+on); //tX.printStackTrace(); } protected boolean programLinked = false; protected boolean programInUse = false; protected int shaderProgram=-1; protected HashMap shaderMap = new HashMap(); protected Integer id = null; private static synchronized Integer getNextID() { return new Integer(nextID++); } protected static int nextID = 1; }