iter=allShaderCode.iterator(); iter.hasNext(); ) {
final ShaderCode shaderCode = iter.next();
if(attachedShaderCode.remove(shaderCode)) {
ShaderUtil.detachShader(gl, shaderProgram, shaderCode.shader());
}
if(destroyShaderCode) {
shaderCode.destroy(gl);
}
}
allShaderCode.clear();
attachedShaderCode.clear();
if( 0 != shaderProgram ) {
gl.glDeleteProgram(shaderProgram);
shaderProgram=0;
}
programLinked=false;
}
//
// ShaderCode handling
//
/**
* Adds a new shader to this program.
*
* This command does not compile and attach the shader,
* use {@link #add(GL2ES2, ShaderCode)} for this purpose.
*/
public synchronized void add(final ShaderCode shaderCode) throws GLException {
allShaderCode.add(shaderCode);
}
public synchronized boolean contains(final ShaderCode shaderCode) {
return allShaderCode.contains(shaderCode);
}
/**
* Warning slow O(n) operation ..
* @param id
* @return
*/
public synchronized ShaderCode getShader(final int id) {
for(final Iterator iter=allShaderCode.iterator(); iter.hasNext(); ) {
final ShaderCode shaderCode = iter.next();
if(shaderCode.id() == id) {
return shaderCode;
}
}
return null;
}
//
// ShaderCode / Program handling
//
/**
* Creates the empty GL program object using {@link GL2ES2#glCreateProgram()},
* if not already created.
*
* @param gl
* @return true if shader program is valid, i.e. not zero
*/
public synchronized final boolean init(final GL2ES2 gl) {
if( 0 == shaderProgram ) {
shaderProgram = gl.glCreateProgram();
}
return 0 != shaderProgram;
}
/**
* Adds a new shader to a this non running program.
*
* Compiles and attaches the shader, if not done yet.
*
* @return true if the shader was successfully added, false if compilation failed.
*/
public synchronized boolean add(final GL2ES2 gl, final ShaderCode shaderCode, final PrintStream verboseOut) {
if( !init(gl) ) { return false; }
if( allShaderCode.add(shaderCode) ) {
if( !shaderCode.compile(gl, verboseOut) ) {
return false;
}
if( attachedShaderCode.add(shaderCode) ) {
ShaderUtil.attachShader(gl, shaderProgram, shaderCode.shader());
}
}
return true;
}
/**
* Replace a shader in a program and re-links the program.
*
* @param gl
* @param oldShader the to be replace Shader
* @param newShader the new ShaderCode
* @param verboseOut the optional verbose output stream
*
* @return true if all steps are valid, shader compilation, attachment and linking; otherwise false.
*
* @see ShaderState#glEnableVertexAttribArray
* @see ShaderState#glDisableVertexAttribArray
* @see ShaderState#glVertexAttribPointer
* @see ShaderState#getVertexAttribPointer
* @see ShaderState#glReleaseAllVertexAttributes
* @see ShaderState#glResetAllVertexAttributes
* @see ShaderState#glResetAllVertexAttributes
* @see ShaderState#glResetAllVertexAttributes
*/
public synchronized boolean replaceShader(final GL2ES2 gl, final ShaderCode oldShader, final ShaderCode newShader, final PrintStream verboseOut) {
if(!init(gl) || !newShader.compile(gl, verboseOut)) {
return false;
}
final boolean shaderWasInUse = inUse();
if(shaderWasInUse) {
useProgram(gl, false);
}
if(null != oldShader && allShaderCode.remove(oldShader)) {
if(attachedShaderCode.remove(oldShader)) {
ShaderUtil.detachShader(gl, shaderProgram, oldShader.shader());
}
}
add(newShader);
if(attachedShaderCode.add(newShader)) {
ShaderUtil.attachShader(gl, shaderProgram, newShader.shader());
}
gl.glLinkProgram(shaderProgram);
programLinked = ShaderUtil.isProgramLinkStatusValid(gl, shaderProgram, verboseOut);
if ( programLinked && shaderWasInUse ) {
useProgram(gl, true);
}
return programLinked;
}
/**
* Links the shader code to the program.
*
* Compiles and attaches the shader code to the program if not done by yet
*
* Within this process, all GL resources (shader and program objects) are created if necessary.
*
* @param gl
* @param verboseOut
* @return true if program was successfully linked and is valid, otherwise false
*
* @see #init(GL2ES2)
*/
public synchronized boolean link(final GL2ES2 gl, final PrintStream verboseOut) {
if( !init(gl) ) {
programLinked = false; // mark unlinked due to user attempt to [re]link
return false;
}
for(final Iterator iter=allShaderCode.iterator(); iter.hasNext(); ) {
final ShaderCode shaderCode = iter.next();
if(!shaderCode.compile(gl, verboseOut)) {
programLinked = false; // mark unlinked due to user attempt to [re]link
return false;
}
if(attachedShaderCode.add(shaderCode)) {
ShaderUtil.attachShader(gl, shaderProgram, shaderCode.shader());
}
}
// Link the program
gl.glLinkProgram(shaderProgram);
programLinked = ShaderUtil.isProgramLinkStatusValid(gl, shaderProgram, verboseOut);
return programLinked;
}
@Override
public boolean equals(final Object obj) {
if(this == obj) { return true; }
if(obj instanceof ShaderProgram) {
return id()==((ShaderProgram)obj).id();
}
return false;
}
@Override
public int hashCode() {
return id;
}
public StringBuilder toString(StringBuilder sb) {
if(null == sb) {
sb = new StringBuilder();
}
sb.append("ShaderProgram[id=").append(id);
sb.append(", linked="+programLinked+", inUse="+programInUse+", program: "+shaderProgram+", "+allShaderCode.size()+" code: ");
if( 0 < allShaderCode.size() ) {
for(final Iterator iter=allShaderCode.iterator(); iter.hasNext(); ) {
sb.append(Platform.getNewline()).append(" ").append(iter.next());
}
} else {
sb.append("none");
}
sb.append("]");
return sb;
}
@Override
public String toString() {
return toString(null).toString();
}
/**
* Performs {@link GL2ES2#glValidateProgram(int)} via {@link ShaderUtil#isProgramExecStatusValid(GL, int, PrintStream)}.
* @see ShaderUtil#isProgramExecStatusValid(GL, int, PrintStream)
**/
public synchronized boolean validateProgram(final GL2ES2 gl, final PrintStream verboseOut) {
return ShaderUtil.isProgramExecStatusValid(gl, shaderProgram, verboseOut);
}
public synchronized void useProgram(final GL2ES2 gl, boolean on) {
if(on && !programLinked) {
System.err.println("Error: ShaderProgram.useProgram(on "+on+") not linked: "+this);
throw new GLException("Program is not linked");
}
if(programInUse==on) { return; }
if( 0 == shaderProgram ) {
on = false;
}
gl.glUseProgram( on ? shaderProgram : 0 );
programInUse = on;
}
public synchronized void notifyNotInUse() {
programInUse = false;
}
public void dumpSource(final PrintStream out) {
out.println();
out.println(toString());
for(final Iterator iter=allShaderCode.iterator(); iter.hasNext(); ) {
iter.next().dumpSource(out);
}
out.println();
}
private boolean programLinked = false;
private boolean programInUse = false;
private int shaderProgram = 0; // non zero is valid!
private final HashSet allShaderCode = new HashSet();
private final HashSet attachedShaderCode = new HashSet();
private final int id;
private static synchronized int getNextID() {
return nextID++;
}
private static int nextID = 1;
}