diff options
Diffstat (limited to 'src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java')
-rw-r--r-- | src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java | 524 |
1 files changed, 282 insertions, 242 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java index 62082aacd..f60cb6088 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java @@ -35,66 +35,40 @@ import java.util.Iterator; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; import javax.media.opengl.GLArrayData; -import javax.media.opengl.GLContext; import javax.media.opengl.GLException; import javax.media.opengl.GLUniformData; import jogamp.opengl.Debug; import com.jogamp.common.os.Platform; -import com.jogamp.common.util.IntObjectHashMap; import com.jogamp.opengl.util.GLArrayDataEditable; +/** + * ShaderState allows to sharing data between shader programs, + * while updating the attribute and uniform locations when switching. + * <p> + * This allows seamless switching of programs using <i>almost</i> same data + * but performing different artifacts. + * </p> + * <p> + * A {@link #useProgram(GL2ES2, boolean) used} ShaderState is attached to the current GL context + * and can be retrieved via {@link #getShaderState(GL)}. + * </p> + */ public class ShaderState { - public static final boolean DEBUG = Debug.isPropertyDefined("jogl.debug.GLSLState", true); - private static final String currentStateKey = "jogamp.opengl.glsl.ShaderState" ; - - public ShaderState() { - } - - public boolean verbose() { return verbose; } + public static final boolean DEBUG; - public void setVerbose(boolean v) { verbose=v; } - - /** - * Fetches the current shader state from this thread (TLS) current GLContext - * - * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean) - * @see com.jogamp.opengl.util.glsl.ShaderState#getShaderState(GL) - * @see com.jogamp.opengl.util.glsl.ShaderState#setShaderState(GL) - * @see com.jogamp.opengl.util.glsl.ShaderState#getCurrentShaderState() - */ - public static ShaderState getCurrentShaderState() { - return getShaderState(GLContext.getCurrentGL()); + static { + Debug.initSingleton(); + DEBUG = Debug.isPropertyDefined("jogl.debug.GLSLState", true); } - /** - * Gets the shader state attached to the GL object's GLContext - * - * @param gl the GL object referencing the GLContext - * - * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean) - * @see com.jogamp.opengl.util.glsl.ShaderState#getShaderState(GL) - * @see com.jogamp.opengl.util.glsl.ShaderState#setShaderState(GL) - * @see com.jogamp.opengl.util.glsl.ShaderState#getCurrentShaderState() - */ - public static ShaderState getShaderState(GL gl) { - return (ShaderState) gl.getContext().getAttachedObject(currentStateKey); + public ShaderState() { } - /** - * Attaches the shader state to the GL object's GLContext - * - * @param gl the GL object referencing the GLContext - * - * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean) - * @see com.jogamp.opengl.util.glsl.ShaderState#getShaderState(GL) - * @see com.jogamp.opengl.util.glsl.ShaderState#setShaderState(GL) - * @see com.jogamp.opengl.util.glsl.ShaderState#getCurrentShaderState() - */ - public final ShaderState setShaderState(GL gl) { - return (ShaderState) gl.getContext().attachObject(currentStateKey, this); - } + public boolean verbose() { return verbose; } + + public void setVerbose(boolean v) { verbose = DEBUG || v; } /** * Returns the attached user object for the given name to this ShaderState. @@ -106,7 +80,7 @@ public class ShaderState { /** * Attach user object for the given name to this ShaderState. * Returns the previously set object or null. - * + * * @return the previous mapped object or null if none */ public final Object attachObject(String name, Object obj) { @@ -115,54 +89,30 @@ public class ShaderState { /** * @param name name of the mapped object to detach - * + * * @return the previous mapped object or null if none */ public final Object detachObject(String name) { return attachedObjectsByString.remove(name); - } - - /** - * Returns the attached user object for the given name to this ShaderState. - */ - public final Object getAttachedObject(int name) { - return attachedObjectsByInt.get(name); - } - - /** - * Attach user object for the given name to this ShaderState. - * Returns the previously set object or null. - */ - public final Object attachObject(int name, Object obj) { - return attachedObjectsByInt.put(name, obj); } - public final Object detachObject(int name) { - return attachedObjectsByInt.remove(name); - } - /** * Turns the shader program on or off.<br> - * Puts this ShaderState to to the thread local storage (TLS), - * if <code>on</code> is <code>true</code>. * * @throws GLException if no program is attached * * @see com.jogamp.opengl.util.glsl.ShaderState#useProgram(GL2ES2, boolean) - * @see com.jogamp.opengl.util.glsl.ShaderState#getShaderState(GL) - * @see com.jogamp.opengl.util.glsl.ShaderState#getCurrentShaderState() */ public synchronized void useProgram(GL2ES2 gl, boolean on) throws GLException { - if(null==shaderProgram) { throw new GLException("No program is attached"); } + if(null==shaderProgram) { throw new GLException("No program is attached"); } if(on) { - setShaderState(gl); if(shaderProgram.linked()) { shaderProgram.useProgram(gl, true); if(resetAllShaderData) { resetAllAttributes(gl); resetAllUniforms(gl); } - } else { + } else { if(resetAllShaderData) { setAllAttributes(gl); } @@ -174,7 +124,7 @@ public class ShaderState { resetAllUniforms(gl); } } - resetAllShaderData = false; + resetAllShaderData = false; } else { shaderProgram.useProgram(gl, false); } @@ -191,34 +141,38 @@ public class ShaderState { /** * Attach or switch a shader program * - * <p>Attaching a shader program the first time, + * <p>Attaching a shader program the first time, * as well as switching to another program on the fly, * while managing all attribute and uniform data.</p> - * + * * <p>[Re]sets all data and use program in case of a program switch.</p> - * + * * <p>Use program, {@link #useProgram(GL2ES2, boolean)}, * if <code>enable</code> is <code>true</code>.</p> - * + * + * @return true if shader program was attached, otherwise false (already attached) + * * @throws GLException if program was not linked and linking fails */ - public synchronized void attachShaderProgram(GL2ES2 gl, ShaderProgram prog, boolean enable) throws GLException { - if(DEBUG) { + public synchronized boolean attachShaderProgram(GL2ES2 gl, ShaderProgram prog, boolean enable) throws GLException { + if(verbose) { int curId = (null!=shaderProgram)?shaderProgram.id():-1; int newId = (null!=prog)?prog.id():-1; - System.err.println("Info: attachShaderProgram: "+curId+" -> "+newId+" (enable: "+enable+")\n\t"+shaderProgram+"\n\t"+prog); - if(verbose) { - Throwable tX = new Throwable("Info: attachShaderProgram: Trace"); - tX.printStackTrace(); + System.err.println("ShaderState: attachShaderProgram: "+curId+" -> "+newId+" (enable: "+enable+")\n\t"+shaderProgram+"\n\t"+prog); + if(DEBUG) { + Thread.dumpStack(); } } if(null!=shaderProgram) { if(shaderProgram.equals(prog)) { - // nothing to do .. - if(DEBUG) { - System.err.println("Info: attachShaderProgram: NOP: equal id: "+shaderProgram.id()); + if(enable) { + useProgram(gl, true); + } + // nothing else to do .. + if(verbose) { + System.err.println("ShaderState: attachShaderProgram: No switch, equal id: "+shaderProgram.id()+", enabling "+enable); } - return; + return false; } if(shaderProgram.inUse()) { if(null != prog && enable) { @@ -236,7 +190,7 @@ public class ShaderState { shaderProgram = prog; if(null!=shaderProgram) { - // [re]set all data and use program if switching program, + // [re]set all data and use program if switching program, // or use program if program is linked if(resetAllShaderData || enable) { useProgram(gl, true); // may reset all data @@ -248,6 +202,7 @@ public class ShaderState { if(DEBUG) { System.err.println("Info: attachShaderProgram: END"); } + return true; } public ShaderProgram shaderProgram() { return shaderProgram; } @@ -261,8 +216,7 @@ public class ShaderState { */ public synchronized void destroy(GL2ES2 gl) { release(gl, true, true, true); - attachedObjectsByString.clear(); - attachedObjectsByInt.clear(); + attachedObjectsByString.clear(); } /** @@ -282,13 +236,13 @@ public class ShaderState { * @see ShaderProgram#release(GL2ES2, boolean) */ public synchronized void release(GL2ES2 gl, boolean destroyBoundAttributes, boolean destroyShaderProgram, boolean destroyShaderCode) { - if(null!=shaderProgram) { + if(null!=shaderProgram && shaderProgram.linked() ) { shaderProgram.useProgram(gl, false); } if(destroyBoundAttributes) { for(Iterator<GLArrayData> iter = managedAttributes.iterator(); iter.hasNext(); ) { iter.next().destroy(gl); - } + } } releaseAllAttributes(gl); releaseAllUniforms(gl); @@ -304,7 +258,7 @@ public class ShaderState { /** * Gets the cached location of a shader attribute. * - * @return -1 if there is no such attribute available, + * @return -1 if there is no such attribute available, * otherwise >= 0 * * @see #bindAttribLocation(GL2ES2, int, String) @@ -316,7 +270,7 @@ public class ShaderState { Integer idx = activeAttribLocationMap.get(name); return (null!=idx)?idx.intValue():-1; } - + /** * Get the previous cached vertex attribute data. * @@ -335,23 +289,30 @@ public class ShaderState { public GLArrayData getAttribute(String name) { return activeAttribDataMap.get(name); } - + + public boolean isActiveAttribute(GLArrayData attribute) { + return attribute == activeAttribDataMap.get(attribute.getName()); + } + /** * Binds or unbinds the {@link GLArrayData} lifecycle to this ShaderState. - * + * * <p>If an attribute location is cached (ie {@link #bindAttribLocation(GL2ES2, int, String)}) * it is promoted to the {@link GLArrayData} instance.</p> - * - * <p>The attribute will be destroyed with {@link #destroy(GL2ES2)} + * + * <p>The attribute will be destroyed with {@link #destroy(GL2ES2)} * and it's location will be reset when switching shader with {@link #attachShaderProgram(GL2ES2, ShaderProgram)}.</p> - * + * * <p>The data will not be transfered to the GPU, use {@link #vertexAttribPointer(GL2ES2, GLArrayData)} additionally.</p> - * + * + * <p>The data will also be {@link GLArrayData#associate(Object, boolean) associated} with this ShaderState.</p> + * * @param attribute the {@link GLArrayData} which lifecycle shall be managed * @param own true if <i>owning</i> shall be performs, false if <i>disowning</i>. - * + * * @see #bindAttribLocation(GL2ES2, int, String) * @see #getAttribute(String) + * @see GLArrayData#associate(Object, boolean) */ public void ownAttribute(GLArrayData attribute, boolean own) { if(own) { @@ -363,12 +324,13 @@ public class ShaderState { } else { managedAttributes.remove(attribute); } + attribute.associate(this, own); } - + public boolean ownsAttribute(GLArrayData attribute) { return managedAttributes.contains(attribute); } - + /** * Binds a shader attribute to a location. * Multiple names can be bound to one location. @@ -377,14 +339,14 @@ public class ShaderState { * * @throws GLException if no program is attached * @throws GLException if the program is already linked - * + * * @see javax.media.opengl.GL2ES2#glBindAttribLocation(int, int, String) * @see #getAttribLocation(GL2ES2, String) * @see #getCachedAttribLocation(String) */ public void bindAttribLocation(GL2ES2 gl, int location, String name) { if(null==shaderProgram) throw new GLException("No program is attached"); - if(shaderProgram.linked()) throw new GLException("Program is already linked"); + if(shaderProgram.linked()) throw new GLException("Program is already linked"); final Integer loc = new Integer(location); activeAttribLocationMap.put(name, loc); gl.glBindAttribLocation(shaderProgram.program(), location, name); @@ -399,25 +361,28 @@ public class ShaderState { * * @throws GLException if no program is attached * @throws GLException if the program is already linked - * + * * @see javax.media.opengl.GL2ES2#glBindAttribLocation(int, int, String) * @see #getAttribLocation(GL2ES2, String) * @see #getCachedAttribLocation(String) * @see #getAttribute(String) */ public void bindAttribLocation(GL2ES2 gl, int location, GLArrayData data) { - bindAttribLocation(gl, location, data.getName()); - data.setLocation(location); + if(null==shaderProgram) throw new GLException("No program is attached"); + if(shaderProgram.linked()) throw new GLException("Program is already linked"); + final String name = data.getName(); + activeAttribLocationMap.put(name, new Integer(location)); + data.setLocation(gl, shaderProgram.program(), location); activeAttribDataMap.put(data.getName(), data); } /** - * Gets the location of a shader attribute.<br> + * Gets the location of a shader attribute with given <code>name</code>.<br> * Uses either the cached value {@link #getCachedAttribLocation(String)} if valid, * or the GLSL queried via {@link GL2ES2#glGetAttribLocation(int, String)}.<br> * The location will be cached. * - * @return -1 if there is no such attribute available, + * @return -1 if there is no such attribute available, * otherwise >= 0 * @throws GLException if no program is attached * @throws GLException if the program is not linked and no location was cached. @@ -434,14 +399,15 @@ public class ShaderState { if(!shaderProgram.linked()) throw new GLException("Program is not linked"); location = gl.glGetAttribLocation(shaderProgram.program(), name); if(0<=location) { - Integer idx = new Integer(location); - activeAttribLocationMap.put(name, idx); + activeAttribLocationMap.put(name, new Integer(location)); if(DEBUG) { - System.err.println("Info: glGetAttribLocation: "+name+", loc: "+location); + System.err.println("ShaderState: glGetAttribLocation: "+name+", loc: "+location); } } else if(verbose) { - Throwable tX = new Throwable("Info: glGetAttribLocation failed, no location for: "+name+", loc: "+location); - tX.printStackTrace(); + System.err.println("ShaderState: glGetAttribLocation failed, no location for: "+name+", loc: "+location); + if(DEBUG) { + Thread.dumpStack(); + } } } return location; @@ -449,14 +415,14 @@ public class ShaderState { /** * Validates and returns the location of a shader attribute.<br> - * Uses either the cached value {@link #getCachedAttribLocation(String)} if valid, + * Uses either the cached value {@link #getCachedAttribLocation(String)} if valid, * or the GLSL queried via {@link GL2ES2#glGetAttribLocation(int, String)}.<br> - * The location will be cached and set in the + * The location will be cached and set in the * {@link GLArrayData} object. * - * @return -1 if there is no such attribute available, + * @return -1 if there is no such attribute available, * otherwise >= 0 - * + * * @throws GLException if no program is attached * @throws GLException if the program is not linked and no location was cached. * @@ -467,12 +433,31 @@ public class ShaderState { * @see #getAttribute(String) */ public int getAttribLocation(GL2ES2 gl, GLArrayData data) { - int location = getAttribLocation(gl, data.getName()); - data.setLocation(location); + if(null==shaderProgram) throw new GLException("No program is attached"); + final String name = data.getName(); + int location = getCachedAttribLocation(name); + if(0<=location) { + data.setLocation(location); + } else { + if(!shaderProgram.linked()) throw new GLException("Program is not linked"); + location = data.setLocation(gl, shaderProgram.program()); + if(0<=location) { + Integer idx = new Integer(location); + activeAttribLocationMap.put(name, idx); + if(DEBUG) { + System.err.println("ShaderState: glGetAttribLocation: "+name+", loc: "+location); + } + } else if(verbose) { + System.err.println("ShaderState: glGetAttribLocation failed, no location for: "+name+", loc: "+location); + if(DEBUG) { + Thread.dumpStack(); + } + } + } activeAttribDataMap.put(data.getName(), data); return location; } - + // // Enabled Vertex Arrays and its data // @@ -484,38 +469,40 @@ public class ShaderState { final Boolean v = activedAttribEnabledMap.get(name); return null != v && v.booleanValue(); } - + /** * @return true if the {@link GLArrayData} attribute is enable */ public final boolean isVertexAttribArrayEnabled(GLArrayData data) { return isVertexAttribArrayEnabled(data.getName()); } - + private boolean enableVertexAttribArray(GL2ES2 gl, String name, int location) { activedAttribEnabledMap.put(name, Boolean.TRUE); if(0>location) { location = getAttribLocation(gl, name); if(0>location) { if(verbose) { - Throwable tX = new Throwable("Info: glEnableVertexAttribArray failed, no index for: "+name); - tX.printStackTrace(); + System.err.println("ShaderState: glEnableVertexAttribArray failed, no index for: "+name); + if(DEBUG) { + Thread.dumpStack(); + } } return false; } } if(DEBUG) { - System.err.println("Info: glEnableVertexAttribArray: "+name+", loc: "+location); + System.err.println("ShaderState: glEnableVertexAttribArray: "+name+", loc: "+location); } gl.glEnableVertexAttribArray(location); return true; } - + /** * Enables a vertex attribute array. - * + * * This method retrieves the the location via {@link #getAttribLocation(GL2ES2, GLArrayData)} - * hence {@link #enableVertexAttribArray(GL2ES2, GLArrayData)} shall be preferred. + * hence {@link #enableVertexAttribArray(GL2ES2, GLArrayData)} shall be preferred. * * Even if the attribute is not found in the current shader, * it is marked enabled in this state. @@ -523,7 +510,7 @@ public class ShaderState { * @return false, if the name is not found, otherwise true * * @throws GLException if the program is not linked and no location was cached. - * + * * @see #glEnableVertexAttribArray * @see #glDisableVertexAttribArray * @see #glVertexAttribPointer @@ -532,7 +519,7 @@ public class ShaderState { public boolean enableVertexAttribArray(GL2ES2 gl, String name) { return enableVertexAttribArray(gl, name, -1); } - + /** * Enables a vertex attribute array, usually invoked by {@link GLArrayDataEditable#enableBuffer(GL, boolean)}. @@ -541,7 +528,7 @@ public class ShaderState { * and is the preferred alternative to {@link #enableVertexAttribArray(GL2ES2, String)}. * If data location is unset it will be retrieved via {@link #getAttribLocation(GL2ES2, GLArrayData)} set * and cached in this state. - * + * * Even if the attribute is not found in the current shader, * it is marked enabled in this state. * @@ -560,36 +547,38 @@ public class ShaderState { getAttribLocation(gl, data); } else { // ensure data is the current bound one - activeAttribDataMap.put(data.getName(), data); + activeAttribDataMap.put(data.getName(), data); } return enableVertexAttribArray(gl, data.getName(), data.getLocation()); } - + private boolean disableVertexAttribArray(GL2ES2 gl, String name, int location) { activedAttribEnabledMap.put(name, Boolean.FALSE); if(0>location) { location = getAttribLocation(gl, name); if(0>location) { if(verbose) { - Throwable tX = new Throwable("Info: glDisableVertexAttribArray failed, no index for: "+name); - tX.printStackTrace(); + System.err.println("ShaderState: glDisableVertexAttribArray failed, no index for: "+name); + if(DEBUG) { + Thread.dumpStack(); + } } return false; } } if(DEBUG) { - System.err.println("Info: glDisableVertexAttribArray: "+name); + System.err.println("ShaderState: glDisableVertexAttribArray: "+name); } gl.glDisableVertexAttribArray(location); return true; } - + /** * Disables a vertex attribute array * * This method retrieves the the location via {@link #getAttribLocation(GL2ES2, GLArrayData)} * hence {@link #disableVertexAttribArray(GL2ES2, GLArrayData)} shall be preferred. - * + * * Even if the attribute is not found in the current shader, * it is removed from this state enabled list. * @@ -614,7 +603,7 @@ public class ShaderState { * and is the preferred alternative to {@link #disableVertexAttribArray(GL2ES2, String)}. * If data location is unset it will be retrieved via {@link #getAttribLocation(GL2ES2, GLArrayData)} set * and cached in this state. - * + * * Even if the attribute is not found in the current shader, * it is removed from this state enabled list. * @@ -634,19 +623,20 @@ public class ShaderState { } return disableVertexAttribArray(gl, data.getName(), data.getLocation()); } - + /** - * Set the {@link GLArrayData} vertex attribute data. - * - * This method uses the {@link GLArrayData}'s location if set. - * If data location is unset it will be retrieved via {@link #getAttribLocation(GL2ES2, GLArrayData)}, set - * and cached in this state. - * + * Set the {@link GLArrayData} vertex attribute data, if it's location is valid, i.e. ≥ 0. + * <p> + * This method uses the {@link GLArrayData}'s location if valid, i.e. ≥ 0.<br/> + * If data's location is invalid, it will be retrieved via {@link #getAttribLocation(GL2ES2, GLArrayData)}, + * set and cached in this state. + * </p> + * * @return false, if the location could not be determined, otherwise true * * @throws GLException if no program is attached * @throws GLException if the program is not linked and no location was cached. - * + * * @see #glEnableVertexAttribArray * @see #glDisableVertexAttribArray * @see #glVertexAttribPointer @@ -656,11 +646,11 @@ public class ShaderState { int location = data.getLocation(); if(0 > location) { location = getAttribLocation(gl, data); - } + } if(0 <= location) { // only pass the data, if the attribute exists in the current shader if(DEBUG) { - System.err.println("Info: glVertexAttribPointer: "+data); + System.err.println("ShaderState: glVertexAttribPointer: "+data); } gl.glVertexAttribPointer(data); return true; @@ -693,16 +683,16 @@ public class ShaderState { activeAttribDataMap.clear(); activedAttribEnabledMap.clear(); activeAttribLocationMap.clear(); - managedAttributes.clear(); + managedAttributes.clear(); } - + /** * Disables all vertex attribute arrays. * * Their enabled stated will be removed from this state only * if 'removeFromState' is true. * - * This method purpose is more for debugging. + * This method purpose is more for debugging. * * @see #glEnableVertexAttribArray * @see #glDisableVertexAttribArray @@ -727,34 +717,45 @@ public class ShaderState { } private final void relocateAttribute(GL2ES2 gl, GLArrayData attribute) { - // get new location .. + // get new location .. note: 'activeAttribLocationMap' is cleared before final String name = attribute.getName(); - final int loc = getAttribLocation(gl, name); - attribute.setLocation(loc); - + final int loc = attribute.setLocation(gl, shaderProgram.program()); if(0<=loc) { + activeAttribLocationMap.put(name, new Integer(loc)); + if(DEBUG) { + System.err.println("ShaderState: relocateAttribute: "+name+", loc: "+loc); + } if(isVertexAttribArrayEnabled(name)) { // enable attrib, VBO and pass location/data gl.glEnableVertexAttribArray(loc); } - + if( attribute.isVBO() ) { gl.glBindBuffer(GL.GL_ARRAY_BUFFER, attribute.getVBOName()); - } - - gl.glVertexAttribPointer(attribute); + gl.glVertexAttribPointer(attribute); + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); + } else { + gl.glVertexAttribPointer(attribute); + } } } - + /** * Reset all previously enabled mapped vertex attribute data. - * - * <p>Attribute data is bound to the GL state</p> - * <p>Attribute location is bound to the program</p> - * - * <p>However, since binding an attribute to a location via {@link #bindAttribLocation(GL2ES2, int, GLArrayData)} - * <i>must</i> happen before linking <b>and</b> we try to promote the attributes to the new program, - * we have to gather the probably new location etc.</p> + * + * <p> + * Attribute data is bound to the GL state, i.e. VBO data itself will not be updated. + * </p> + * + * <p> + * Attribute location and it's data assignment is bound to the program, + * hence both are updated. + * </p> + * + * <p> + * Note: Such update could only be prevented, + * if tracking am attribute/program dirty flag. + * </p> * * @throws GLException is the program is not linked * @@ -763,9 +764,9 @@ public class ShaderState { private final void resetAllAttributes(GL2ES2 gl) { if(!shaderProgram.linked()) throw new GLException("Program is not linked"); activeAttribLocationMap.clear(); - - for(Iterator<GLArrayData> iter = managedAttributes.iterator(); iter.hasNext(); ) { - iter.next().setLocation(-1); + + for(int i=0; i<managedAttributes.size(); i++) { + ((GLArrayData)managedAttributes.get(i)).setLocation(-1); } for(Iterator<GLArrayData> iter = activeAttribDataMap.values().iterator(); iter.hasNext(); ) { relocateAttribute(gl, iter.next()); @@ -778,21 +779,23 @@ public class ShaderState { final int loc = attribute.getLocation(); if(0<=loc) { - this.bindAttribLocation(gl, loc, name); - + bindAttribLocation(gl, loc, name); + if(isVertexAttribArrayEnabled(name)) { // enable attrib, VBO and pass location/data gl.glEnableVertexAttribArray(loc); } - + if( attribute.isVBO() ) { gl.glBindBuffer(GL.GL_ARRAY_BUFFER, attribute.getVBOName()); - } - - gl.glVertexAttribPointer(attribute); + gl.glVertexAttribPointer(attribute); + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); + } else { + gl.glVertexAttribPointer(attribute); + } } } - + /** * preserves the attribute location .. (program not linked) */ @@ -809,7 +812,7 @@ public class ShaderState { /** * Gets the cached location of the shader uniform. * - * @return -1 if there is no such uniform available, + * @return -1 if there is no such uniform available, * otherwise >= 0 */ public final int getCachedUniformLocation(String name) { @@ -819,38 +822,38 @@ public class ShaderState { /** * Bind the {@link GLUniform} lifecycle to this ShaderState. - * + * * <p>If a uniform location is cached it is promoted to the {@link GLUniformData} instance.</p> - * - * <p>The attribute will be destroyed with {@link #destroy(GL2ES2)} + * + * <p>The attribute will be destroyed with {@link #destroy(GL2ES2)} * and it's location will be reset when switching shader with {@link #attachShaderProgram(GL2ES2, ShaderProgram)}.</p> - * + * * <p>The data will not be transfered to the GPU, use {@link #uniform(GL2ES2, GLUniformData)} additionally.</p> - * + * * @param uniform the {@link GLUniformData} which lifecycle shall be managed - * + * * @see #getUniform(String) */ public void ownUniform(GLUniformData uniform) { final int location = getCachedUniformLocation(uniform.getName()); if(0<=location) { uniform.setLocation(location); - } + } activeUniformDataMap.put(uniform.getName(), uniform); - managedUniforms.add(uniform); + managedUniforms.add(uniform); } - + public boolean ownsUniform(GLUniformData uniform) { return managedUniforms.contains(uniform); } - + /** - * Gets the location of a shader uniform.<br> + * Gets the location of a shader uniform with given <code>name</code>.<br> * Uses either the cached value {@link #getCachedUniformLocation(String)} if valid, * or the GLSL queried via {@link GL2ES2#glGetUniformLocation(int, String)}.<br> * The location will be cached. * <p> - * The current shader program ({@link #attachShaderProgram(GL2ES2, ShaderProgram)}) + * The current shader program ({@link #attachShaderProgram(GL2ES2, ShaderProgram)}) * must be in use ({@link #useProgram(GL2ES2, boolean) }) !</p> * * @return -1 if there is no such attribute available, @@ -867,13 +870,16 @@ public class ShaderState { if(!shaderProgram.inUse()) throw new GLException("Program is not in use"); int location = getCachedUniformLocation(name); if(0>location) { + if(!shaderProgram.linked()) throw new GLException("Program is not linked"); location = gl.glGetUniformLocation(shaderProgram.program(), name); if(0<=location) { Integer idx = new Integer(location); activeUniformLocationMap.put(name, idx); } else if(verbose) { - Throwable tX = new Throwable("Info: glUniform failed, no location for: "+name+", index: "+location); - tX.printStackTrace(); + System.err.println("ShaderState: glUniform failed, no location for: "+name+", index: "+location); + if(DEBUG) { + Thread.dumpStack(); + } } } return location; @@ -883,10 +889,10 @@ public class ShaderState { * Validates and returns the location of a shader uniform.<br> * Uses either the cached value {@link #getCachedUniformLocation(String)} if valid, * or the GLSL queried via {@link GL2ES2#glGetUniformLocation(int, String)}.<br> - * The location will be cached and set in the + * The location will be cached and set in the * {@link GLUniformData} object. * <p> - * The current shader program ({@link #attachShaderProgram(GL2ES2, ShaderProgram)}) + * The current shader program ({@link #attachShaderProgram(GL2ES2, ShaderProgram)}) * must be in use ({@link #useProgram(GL2ES2, boolean) }) !</p> * * @return -1 if there is no such attribute available, @@ -900,26 +906,36 @@ public class ShaderState { * @see ShaderProgram#glReplaceShader */ public int getUniformLocation(GL2ES2 gl, GLUniformData data) { - int location = getUniformLocation(gl, data.getName()); - data.setLocation(location); - activeUniformDataMap.put(data.getName(), data); + if(!shaderProgram.inUse()) throw new GLException("Program is not in use"); + final String name = data.getName(); + int location = getCachedUniformLocation(name); + if(0<=location) { + data.setLocation(location); + } else { + if(!shaderProgram.linked()) throw new GLException("Program is not linked"); + location = data.setLocation(gl, shaderProgram.program()); + if(0<=location) { + activeUniformLocationMap.put(name, new Integer(location)); + } else if(verbose) { + System.err.println("ShaderState: glUniform failed, no location for: "+name+", index: "+location); + if(DEBUG) { + Thread.dumpStack(); + } + } + } + activeUniformDataMap.put(name, data); return location; } - + /** - * Set the uniform data. - * - * Even if the uniform is not found in the current shader, - * it is stored in this state. - * - * @param data the GLUniforms's name must match the uniform one, - * it's index will be set with the uniforms's location, - * if found. - * - * - * @return false, if the name is not found, otherwise true + * Set the uniform data, if it's location is valid, i.e. ≥ 0. + * <p> + * This method uses the {@link GLUniformData}'s location if valid, i.e. ≥ 0.<br/> + * If data's location is invalid, it will be retrieved via {@link #getUniformLocation(GL2ES2, GLUniformData)}, + * set and cached in this state. + * </p> * - * @throws GLException if the program is not in use + * @return false, if the location could not be determined, otherwise true * * @see #glGetUniformLocation * @see javax.media.opengl.GL2ES2#glGetUniformLocation @@ -936,11 +952,12 @@ public class ShaderState { if(0<=location) { // only pass the data, if the uniform exists in the current shader if(DEBUG) { - System.err.println("Info: glUniform: "+data); + System.err.println("ShaderState: glUniform: "+data); } gl.glUniform(data); + return true; } - return true; + return false; } /** @@ -961,37 +978,49 @@ public class ShaderState { activeUniformLocationMap.clear(); managedUniforms.clear(); } - + /** * Reset all previously mapped uniform data - * + * <p> * Uniform data and location is bound to the program, - * hence both are updated here + * hence both are updated. + * </p> + * <p> + * Note: Such update could only be prevented, + * if tracking a uniform/program dirty flag. + * </p> * * @throws GLException is the program is not in use - * + * * @see #attachShaderProgram(GL2ES2, ShaderProgram) */ private final void resetAllUniforms(GL2ES2 gl) { - if(!shaderProgram.inUse()) throw new GLException("Program is not in use"); + if(!shaderProgram.inUse()) throw new GLException("Program is not in use"); activeUniformLocationMap.clear(); for(Iterator<GLUniformData> iter = managedUniforms.iterator(); iter.hasNext(); ) { iter.next().setLocation(-1); - } + } for(Iterator<GLUniformData> iter = activeUniformDataMap.values().iterator(); iter.hasNext(); ) { - final GLUniformData uniform = iter.next(); - uniform.setLocation(-1); - uniform(gl, uniform); + final GLUniformData data = iter.next(); + final int loc = data.setLocation(gl, shaderProgram.program()); + if( 0 <= loc ) { + // only pass the data, if the uniform exists in the current shader + activeUniformLocationMap.put(data.getName(), new Integer(loc)); + if(DEBUG) { + System.err.println("ShaderState: resetAllUniforms: "+data); + } + gl.glUniform(data); + } } } - public StringBuilder toString(StringBuilder sb) { + public StringBuilder toString(StringBuilder sb, boolean alsoUnlocated) { if(null==sb) { sb = new StringBuilder(); } - + sb.append("ShaderState[ "); - + sb.append(Platform.getNewline()).append(" "); if(null != shaderProgram) { shaderProgram.toString(sb); @@ -1008,43 +1037,54 @@ public class ShaderState { } sb.append(Platform.getNewline()).append(" ],").append(" activeAttributes ["); for(Iterator<GLArrayData> iter = activeAttribDataMap.values().iterator(); iter.hasNext(); ) { - sb.append(Platform.getNewline()).append(" ").append(iter.next()); + final GLArrayData ad = iter.next(); + if( alsoUnlocated || 0 <= ad.getLocation() ) { + sb.append(Platform.getNewline()).append(" ").append(ad); + } } sb.append(Platform.getNewline()).append(" ],").append(" managedAttributes ["); for(Iterator<GLArrayData> iter = managedAttributes.iterator(); iter.hasNext(); ) { - sb.append(Platform.getNewline()).append(" ").append(iter.next()); + final GLArrayData ad = iter.next(); + if( alsoUnlocated || 0 <= ad.getLocation() ) { + sb.append(Platform.getNewline()).append(" ").append(ad); + } } sb.append(Platform.getNewline()).append(" ],").append(" activeUniforms ["); for(Iterator<GLUniformData> iter=activeUniformDataMap.values().iterator(); iter.hasNext(); ) { - sb.append(Platform.getNewline()).append(" ").append(iter.next()); + final GLUniformData ud = iter.next(); + if( alsoUnlocated || 0 <= ud.getLocation() ) { + sb.append(Platform.getNewline()).append(" ").append(ud); + } } sb.append(Platform.getNewline()).append(" ],").append(" managedUniforms ["); for(Iterator<GLUniformData> iter = managedUniforms.iterator(); iter.hasNext(); ) { - sb.append(Platform.getNewline()).append(" ").append(iter.next()); + final GLUniformData ud = iter.next(); + if( alsoUnlocated || 0 <= ud.getLocation() ) { + sb.append(Platform.getNewline()).append(" ").append(ud); + } } sb.append(Platform.getNewline()).append(" ]").append(Platform.getNewline()).append("]"); return sb; } - + @Override public String toString() { - return toString(null).toString(); + return toString(null, DEBUG).toString(); } - - private boolean verbose = DEBUG ? true : false; + + private boolean verbose = DEBUG; private ShaderProgram shaderProgram=null; - + private HashMap<String, Boolean> activedAttribEnabledMap = new HashMap<String, Boolean>(); private HashMap<String, Integer> activeAttribLocationMap = new HashMap<String, Integer>(); private HashMap<String, GLArrayData> activeAttribDataMap = new HashMap<String, GLArrayData>(); private ArrayList<GLArrayData> managedAttributes = new ArrayList<GLArrayData>(); - + private HashMap<String, Integer> activeUniformLocationMap = new HashMap<String, Integer>(); private HashMap<String, GLUniformData> activeUniformDataMap = new HashMap<String, GLUniformData>(); private ArrayList<GLUniformData> managedUniforms = new ArrayList<GLUniformData>(); - - private HashMap<String, Object> attachedObjectsByString = new HashMap<String, Object>(); - private IntObjectHashMap attachedObjectsByInt = new IntObjectHashMap(); + + private HashMap<String, Object> attachedObjectsByString = new HashMap<String, Object>(); private boolean resetAllShaderData = false; } |