/**
* Copyright 2010 JogAmp Community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of JogAmp Community.
*/
package com.jogamp.opengl.util.glsl;
import com.jogamp.opengl.*;
import com.jogamp.common.os.Platform;
import java.util.HashSet;
import java.util.Iterator;
import java.io.PrintStream;
public class ShaderProgram {
public ShaderProgram() {
id = getNextID();
}
public boolean linked() {
return programLinked;
}
public boolean inUse() {
return programInUse;
}
/** Returns the shader program name, which is non zero if valid. */
public int program() { return shaderProgram; }
/**
* returns the uniq shader id as an integer
*/
public int id() { return id; }
/**
* Detaches all shader codes and deletes the program.
* Destroys the shader codes as well.
* Calls release(gl, true)
*
* @see #release(GL2ES2, boolean)
*/
public synchronized void destroy(final GL2ES2 gl) {
release(gl, true);
}
/**
* Detaches all shader codes and deletes the program,
* but leaves the shader code intact.
* Calls release(gl, false)
*
* @see #release(GL2ES2, boolean)
*/
public synchronized void release(final GL2ES2 gl) {
release(gl, false);
}
/**
* Detaches all shader codes and deletes the program.
* If <code>destroyShaderCode</code> is true it destroys the shader codes as well.
*/
public synchronized void release(final GL2ES2 gl, final boolean destroyShaderCode) {
if( programLinked ) {
useProgram(gl, false);
}
for(final Iterator<ShaderCode> 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;
}
}
//
// ShaderCode handling
//
/**
* Adds a new shader to this program.
*
* <p>This command does not compile and attach the shader,
* use {@link #add(GL2ES2, ShaderCode)} for this purpose.</p>
*/
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<ShaderCode> 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.
*
* <p>Compiles and attaches the shader, if not done yet.</p>
*
* @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.
*
* <p>Compiles and attaches the shader code to the program if not done by yet</p>
*
* <p>Within this process, all GL resources (shader and program objects) are created if necessary.</p>
*
* @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<ShaderCode> 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+",");
for(final Iterator<ShaderCode> iter=allShaderCode.iterator(); iter.hasNext(); ) {
sb.append(Platform.getNewline()).append(" ").append(iter.next());
}
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(!programLinked) { 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;
}
private boolean programLinked = false;
private boolean programInUse = false;
private int shaderProgram = 0; // non zero is valid!
private final HashSet<ShaderCode> allShaderCode = new HashSet<ShaderCode>();
private final HashSet<ShaderCode> attachedShaderCode = new HashSet<ShaderCode>();
private final int id;
private static synchronized int getNextID() {
return nextID++;
}
private static int nextID = 1;
}