diff options
Diffstat (limited to 'src/jogl/classes/com/jogamp/opengl/util/glsl')
8 files changed, 762 insertions, 422 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java index f6b686d7e..6a64edeb5 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java @@ -45,13 +45,16 @@ import java.util.Set; import javax.media.opengl.GL; import javax.media.opengl.GL2ES2; +import javax.media.opengl.GL3; import javax.media.opengl.GLES2; +import javax.media.opengl.GLContext; import javax.media.opengl.GLException; import jogamp.opengl.Debug; import com.jogamp.common.nio.Buffers; import com.jogamp.common.util.IOUtil; +import com.jogamp.common.util.VersionNumber; /** * Convenient shader code class to use and instantiate vertex or fragment programs. @@ -59,33 +62,38 @@ import com.jogamp.common.util.IOUtil; * A documented example of how to use this code is available * {@link #create(GL2ES2, int, Class, String, String, String, boolean) here} and * {@link #create(GL2ES2, int, int, Class, String, String[], String, String) here}. - * </p> + * </p> */ public class ShaderCode { - public static final boolean DEBUG = Debug.debug("GLSLCode"); public static final boolean DEBUG_CODE = Debug.isPropertyDefined("jogl.debug.GLSLCode", true); /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in source code: <code>vp</code> */ public static final String SUFFIX_VERTEX_SOURCE = "vp" ; - + /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in binary: <code>bvp</code> */ public static final String SUFFIX_VERTEX_BINARY = "bvp" ; - + + /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in source code: <code>gp</code> */ + public static final String SUFFIX_GEOMETRY_SOURCE = "gp" ; + + /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in binary: <code>bgp</code> */ + public static final String SUFFIX_GEOMETRY_BINARY = "bgp" ; + /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in source code: <code>fp</code> */ public static final String SUFFIX_FRAGMENT_SOURCE = "fp" ; - + /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in binary: <code>bfp</code> */ public static final String SUFFIX_FRAGMENT_BINARY = "bfp" ; - + /** Unique relative path for binary shader resources for {@link GLES2#GL_NVIDIA_PLATFORM_BINARY_NV NVIDIA}: <code>nvidia</code> */ public static final String SUB_PATH_NVIDIA = "nvidia" ; /** - * @param type either {@link GL2ES2#GL_VERTEX_SHADER} or {@link GL2ES2#GL_FRAGMENT_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} * @param count number of shaders * @param source CharSequence array containing the shader sources, organized as <code>source[count][strings-per-shader]</code>. * May be either an immutable <code>String</code> - or mutable <code>StringBuilder</code> array. - * + * * @throws IllegalArgumentException if <code>count</count> and <code>source.length</code> do not match */ public ShaderCode(int type, int count, CharSequence[][] source) { @@ -95,6 +103,7 @@ public class ShaderCode { switch (type) { case GL2ES2.GL_VERTEX_SHADER: case GL2ES2.GL_FRAGMENT_SHADER: + case GL3.GL_GEOMETRY_SHADER: break; default: throw new GLException("Unknown shader type: "+type); @@ -113,14 +122,15 @@ public class ShaderCode { } /** - * @param type either {@link GL2ES2#GL_VERTEX_SHADER} or {@link GL2ES2#GL_FRAGMENT_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} * @param count number of shaders - * @param binary binary buffer containing the shader binaries, + * @param binary binary buffer containing the shader binaries, */ public ShaderCode(int type, int count, int binFormat, Buffer binary) { switch (type) { case GL2ES2.GL_VERTEX_SHADER: case GL2ES2.GL_FRAGMENT_SHADER: + case GL3.GL_GEOMETRY_SHADER: break; default: throw new GLException("Unknown shader type: "+type); @@ -136,19 +146,19 @@ public class ShaderCode { /** * Creates a complete {@link ShaderCode} object while reading all shader source of <code>sourceFiles</code>, * which location is resolved using the <code>context</code> class, see {@link #readShaderSource(Class, String)}. - * + * * @param gl current GL object to determine whether a shader compiler is available. If null, no validation is performed. - * @param type either {@link GL2ES2#GL_VERTEX_SHADER} or {@link GL2ES2#GL_FRAGMENT_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} * @param count number of shaders * @param context class used to help resolving the source location * @param sourceFiles array of source locations, organized as <code>sourceFiles[count]</code> * @param mutableStringBuilder if <code>true</code> method returns a mutable <code>StringBuilder</code> instance - * which can be edited later on at the costs of a String conversion when passing to + * which can be edited later on at the costs of a String conversion when passing to * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. * If <code>false</code> method returns an immutable <code>String</code> instance, * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} * at no additional costs. - * + * * @throws IllegalArgumentException if <code>count</count> and <code>sourceFiles.length</code> do not match * @see #readShaderSource(Class, String) */ @@ -181,16 +191,16 @@ public class ShaderCode { /** * Creates a complete {@link ShaderCode} object while reading the shader binary of <code>binaryFile</code>, * which location is resolved using the <code>context</code> class, see {@link #readShaderBinary(Class, String)}. - * - * @param type either {@link GL2ES2#GL_VERTEX_SHADER} or {@link GL2ES2#GL_FRAGMENT_SHADER} + * + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} * @param count number of shaders * @param context class used to help resolving the source location * @param binFormat a valid native binary format as they can be queried by {@link ShaderUtil#getShaderBinaryFormats(GL)}. * @param sourceFiles array of source locations, organized as <code>sourceFiles[count]</code> - * + * * @see #readShaderBinary(Class, String) * @see ShaderUtil#getShaderBinaryFormats(GL) - */ + */ public static ShaderCode create(int type, int count, Class<?> context, int binFormat, String binaryFile) { ByteBuffer shaderBinary = null; if(null!=binaryFile && 0<=binFormat) { @@ -214,16 +224,18 @@ public class ShaderCode { * <ul> * <li>Source<ul> * <li>{@link GL2ES2#GL_VERTEX_SHADER vertex}: {@link #SUFFIX_VERTEX_SOURCE}</li> - * <li>{@link GL2ES2#GL_FRAGMENT_SHADER fragment}: {@link #SUFFIX_FRAGMENT_SOURCE}</li></ul></li> + * <li>{@link GL2ES2#GL_FRAGMENT_SHADER fragment}: {@link #SUFFIX_FRAGMENT_SOURCE}</li> + * <li>{@link GL3#GL_GEOMETRY_SHADER geometry}: {@link #SUFFIX_GEOMETRY_SOURCE}</li></ul></li> * <li>Binary<ul> * <li>{@link GL2ES2#GL_VERTEX_SHADER vertex}: {@link #SUFFIX_VERTEX_BINARY}</li> - * <li>{@link GL2ES2#GL_FRAGMENT_SHADER fragment}: {@link #SUFFIX_FRAGMENT_BINARY}</li></ul></li> - * </ul> - * @param binary true for a binary resource, false for a source resource - * @param type either {@link GL2ES2#GL_VERTEX_SHADER} or {@link GL2ES2#GL_FRAGMENT_SHADER} - * + * <li>{@link GL2ES2#GL_FRAGMENT_SHADER fragment}: {@link #SUFFIX_FRAGMENT_BINARY}</li> + * <li>{@link GL3#GL_GEOMETRY_SHADER geometry}: {@link #SUFFIX_GEOMETRY_BINARY}</li></ul></li> + * </ul> + * @param binary true for a binary resource, false for a source resource + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} + * * @throws GLException if <code>type</code> is not supported - * + * * @see #create(GL2ES2, int, Class, String, String, String, boolean) */ public static String getFileSuffix(boolean binary, int type) { @@ -232,21 +244,23 @@ public class ShaderCode { return binary?SUFFIX_VERTEX_BINARY:SUFFIX_VERTEX_SOURCE; case GL2ES2.GL_FRAGMENT_SHADER: return binary?SUFFIX_FRAGMENT_BINARY:SUFFIX_FRAGMENT_SOURCE; + case GL3.GL_GEOMETRY_SHADER: + return binary?SUFFIX_GEOMETRY_BINARY:SUFFIX_GEOMETRY_SOURCE; default: throw new GLException("illegal shader type: "+type); } } - /** + /** * Returns a unique relative path for binary shader resources as follows: * <ul> * <li>{@link GLES2#GL_NVIDIA_PLATFORM_BINARY_NV NVIDIA}: {@link #SUB_PATH_NVIDIA}</li> * </ul> - * + * * @throws GLException if <code>binFormat</code> is not supported - * + * * @see #create(GL2ES2, int, Class, String, String, String, boolean) - */ + */ public static String getBinarySubPath(int binFormat) { switch (binFormat) { case GLES2.GL_NVIDIA_PLATFORM_BINARY_NV: @@ -257,42 +271,42 @@ public class ShaderCode { } /** - * Convenient creation method for instantiating a complete {@link ShaderCode} object - * either from source code using {@link #create(GL2ES2, int, int, Class, String[])}, + * Convenient creation method for instantiating a complete {@link ShaderCode} object + * either from source code using {@link #create(GL2ES2, int, int, Class, String[])}, * or from a binary code using {@link #create(int, int, Class, int, String)}, * whatever is available first. * <p> - * The source and binary location names are expected w/o suffixes which are + * The source and binary location names are expected w/o suffixes which are * resolved and appended using {@link #getFileSuffix(boolean, int)}. * </p> * <p> * Additionally, the binary resource is expected within a subfolder of <code>binRoot</code> * which reflects the vendor specific binary format, see {@link #getBinarySubPath(int)}. * All {@link ShaderUtil#getShaderBinaryFormats(GL)} are being iterated - * using the binary subfolder, the first existing resource is being used. + * using the binary subfolder, the first existing resource is being used. * </p> - * + * * Example: * <pre> * Your std JVM layout (plain or within a JAR): - * + * * org/test/glsl/MyShaderTest.class * org/test/glsl/shader/vertex.vp * org/test/glsl/shader/fragment.fp * org/test/glsl/shader/bin/nvidia/vertex.bvp * org/test/glsl/shader/bin/nvidia/fragment.bfp - * + * * Your Android APK layout: - * + * * classes.dex * assets/org/test/glsl/shader/vertex.vp * assets/org/test/glsl/shader/fragment.fp * assets/org/test/glsl/shader/bin/nvidia/vertex.bvp * assets/org/test/glsl/shader/bin/nvidia/fragment.bfp * ... - * + * * Your invocation in org/test/glsl/MyShaderTest.java: - * + * * ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, 1, this.getClass(), * "shader", new String[] { "vertex" }, "shader/bin", "vertex"); * ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, 1, this.getClass(), @@ -303,14 +317,14 @@ public class ShaderCode { * st.attachShaderProgram(gl, sp0, true); * </pre> * A simplified entry point is {@link #create(GL2ES2, int, Class, String, String, String, boolean)}. - * + * * <p> * The location is finally being resolved using the <code>context</code> class, see {@link #readShaderBinary(Class, String)}. * </p> - * + * * @param gl current GL object to determine whether a shader compiler is available (if <code>source</code> is used), * or to determine the shader binary format (if <code>binary</code> is used). - * @param type either {@link GL2ES2#GL_VERTEX_SHADER} or {@link GL2ES2#GL_FRAGMENT_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} * @param count number of shaders * @param context class used to help resolving the source and binary location * @param srcRoot relative <i>root</i> path for <code>srcBasenames</code> @@ -318,22 +332,22 @@ public class ShaderCode { * @param binRoot relative <i>root</i> path for <code>binBasenames</code> * @param binBasename basename w/o path or suffix relative to <code>binRoot</code> for the shader's binary code * @param mutableStringBuilder if <code>true</code> method returns a mutable <code>StringBuilder</code> instance - * which can be edited later on at the costs of a String conversion when passing to + * which can be edited later on at the costs of a String conversion when passing to * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. * If <code>false</code> method returns an immutable <code>String</code> instance, * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} * at no additional costs. - * + * * @throws IllegalArgumentException if <code>count</count> and <code>srcBasenames.length</code> do not match - * + * * @see #create(GL2ES2, int, int, Class, String[]) * @see #create(int, int, Class, int, String) * @see #readShaderSource(Class, String) * @see #getFileSuffix(boolean, int) * @see ShaderUtil#getShaderBinaryFormats(GL) * @see #getBinarySubPath(int) - */ - public static ShaderCode create(GL2ES2 gl, int type, int count, Class<?> context, + */ + public static ShaderCode create(GL2ES2 gl, int type, int count, Class<?> context, String srcRoot, String[] srcBasenames, String binRoot, String binBasename, boolean mutableStringBuilder) { ShaderCode res = null; @@ -376,28 +390,28 @@ public class ShaderCode { /** * Simplified variation of {@link #create(GL2ES2, int, int, Class, String, String[], String, String)}. * <br> - * + * * Example: * <pre> * Your std JVM layout (plain or within a JAR): - * + * * org/test/glsl/MyShaderTest.class * org/test/glsl/shader/vertex.vp * org/test/glsl/shader/fragment.fp * org/test/glsl/shader/bin/nvidia/vertex.bvp * org/test/glsl/shader/bin/nvidia/fragment.bfp - * + * * Your Android APK layout: - * + * * classes.dex * assets/org/test/glsl/shader/vertex.vp * assets/org/test/glsl/shader/fragment.fp * assets/org/test/glsl/shader/bin/nvidia/vertex.bvp * assets/org/test/glsl/shader/bin/nvidia/fragment.bfp * ... - * + * * Your invocation in org/test/glsl/MyShaderTest.java: - * + * * ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, this.getClass(), * "shader", "shader/bin", "vertex"); * ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(), @@ -407,10 +421,10 @@ public class ShaderCode { * sp0.add(gl, fp0, System.err); * st.attachShaderProgram(gl, sp0, true); * </pre> - * + * * @param gl current GL object to determine whether a shader compiler is available (if <code>source</code> is used), * or to determine the shader binary format (if <code>binary</code> is used). - * @param type either {@link GL2ES2#GL_VERTEX_SHADER} or {@link GL2ES2#GL_FRAGMENT_SHADER} + * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER} or {@link GL3#GL_GEOMETRY_SHADER} * @param context class used to help resolving the source and binary location * @param srcRoot relative <i>root</i> path for <code>basename</code> * @param binRoot relative <i>root</i> path for <code>basename</code> @@ -418,20 +432,20 @@ public class ShaderCode { * @param basenames basename w/o path or suffix relative to <code>srcRoot</code> and <code>binRoot</code> * for the shader's source and binary code. * @param mutableStringBuilder if <code>true</code> method returns a mutable <code>StringBuilder</code> instance - * which can be edited later on at the costs of a String conversion when passing to + * which can be edited later on at the costs of a String conversion when passing to * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. * If <code>false</code> method returns an immutable <code>String</code> instance, * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} * at no additional costs. * @throws IllegalArgumentException if <code>count</count> is not 1 - * + * * @see #create(GL2ES2, int, int, Class, String, String[], String, String) - */ - public static ShaderCode create(GL2ES2 gl, int type, Class<?> context, + */ + public static ShaderCode create(GL2ES2 gl, int type, Class<?> context, String srcRoot, String binRoot, String basename, boolean mutableStringBuilder) { - return create(gl, type, 1, context, srcRoot, new String[] { basename }, binRoot, basename, mutableStringBuilder ); + return create(gl, type, 1, context, srcRoot, new String[] { basename }, binRoot, basename, mutableStringBuilder ); } - + /** * returns the uniq shader id as an integer */ @@ -440,12 +454,14 @@ public class ShaderCode { public int shaderType() { return shaderType; } public String shaderTypeStr() { return shaderTypeStr(shaderType); } - public static String shaderTypeStr(int type) { + public static String shaderTypeStr(int type) { switch (type) { case GL2ES2.GL_VERTEX_SHADER: return "VERTEX_SHADER"; case GL2ES2.GL_FRAGMENT_SHADER: return "FRAGMENT_SHADER"; + case GL3.GL_GEOMETRY_SHADER: + return "GEOMETRY_SHADER"; } return "UNKNOWN_SHADER"; } @@ -467,6 +483,7 @@ public class ShaderCode { // Create & Compile the vertex/fragment shader objects if(null!=shaderSource) { if(DEBUG_CODE) { + System.err.println("ShaderCode.compile:"); dumpShaderSource(System.err); } valid=ShaderUtil.createAndCompileShader(gl, shader, shaderType, @@ -497,6 +514,7 @@ public class ShaderCode { id=-1; } + @Override public boolean equals(Object obj) { if(this==obj) { return true; } if(obj instanceof ShaderCode) { @@ -504,9 +522,11 @@ public class ShaderCode { } return false; } + @Override public int hashCode() { return id; } + @Override public String toString() { StringBuilder buf = new StringBuilder("ShaderCode[id="+id+", type="+shaderTypeStr()+", valid="+valid+", shader: "); for(int i=0; i<shader.remaining(); i++) { @@ -536,7 +556,7 @@ public class ShaderCode { } else { CharSequence[] src = shaderSource[i]; int lineno=0; - + for(int j=0; j<src.length; j++) { out.printf("%4d: // Segment %d/%d: \n", lineno, j, src.length); final BufferedReader reader = new BufferedReader(new StringReader(src[j].toString())); @@ -552,19 +572,19 @@ public class ShaderCode { out.println("--------------------------------------------------------------"); } } - + /** * Adds <code>data</code> after the line containing <code>tag</code>. * <p> * Note: The shader source to be edit must be created using a mutable StringBuilder. * </p> - * + * * @param shaderIdx the shader index to be used. * @param tag search string * @param fromIndex start search <code>tag</code> begininig with this index * @param data the text to be inserted. Shall end with an EOL '\n' character. * @return index after the inserted <code>data</code> - * + * * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of type <code>StringBuilder</code> */ public int insertShaderSource(int shaderIdx, String tag, int fromIndex, CharSequence data) { @@ -578,7 +598,7 @@ public class ShaderCode { final int sourceCount = (null!=shaderSource)?shaderSource.length:0; if(shaderIdx>=sourceCount) { throw new IndexOutOfBoundsException("shaderIdx not within source bounds [0.."+(sourceCount-1)+"]: "+shaderIdx); - } + } final CharSequence[] src = shaderSource[shaderIdx]; int curEndIndex = 0; for(int j=0; j<src.length; j++) { @@ -586,8 +606,8 @@ public class ShaderCode { throw new IllegalStateException("shader source not a mutable StringBuilder, but CharSequence of type: "+src[j].getClass().getName()); } final StringBuilder sb = (StringBuilder)src[j]; - curEndIndex += sb.length(); - if(fromIndex < curEndIndex) { + curEndIndex += sb.length(); + if(fromIndex < curEndIndex) { int insertIdx = sb.indexOf(tag, fromIndex); if(0<=insertIdx) { insertIdx += tag.length(); @@ -613,15 +633,15 @@ public class ShaderCode { * Replaces <code>oldName</code> with <code>newName</code> in all shader sources. * <p> * In case <code>oldName</code> and <code>newName</code> are equal, no action is performed. - * </p> + * </p> * <p> * Note: The shader source to be edit must be created using a mutable StringBuilder. * </p> - * + * * @param oldName the to be replace string * @param newName the replacement string * @return the number of replacements - * + * * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of type <code>StringBuilder</code> */ public int replaceInShaderSource(String oldName, String newName) { @@ -658,18 +678,18 @@ public class ShaderCode { } return num; } - + /** * Adds <code>data</code> at <code>offset</code> in shader source for shader <code>shaderIdx</code>. * <p> * Note: The shader source to be edit must be created using a mutable StringBuilder. * </p> - * + * * @param shaderIdx the shader index to be used. * @param position in shader source segments of shader <code>shaderIdx</code> - * @param data the text to be inserted. Shall end with an EOL '\n' character. + * @param data the text to be inserted. Shall end with an EOL '\n' character * @return index after the inserted <code>data</code> - * + * * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of type <code>StringBuilder</code> */ public int insertShaderSource(int shaderIdx, int position, CharSequence data) { @@ -683,7 +703,7 @@ public class ShaderCode { final int sourceCount = (null!=shaderSource)?shaderSource.length:0; if(shaderIdx>=sourceCount) { throw new IndexOutOfBoundsException("shaderIdx not within source bounds [0.."+(sourceCount-1)+"]: "+shaderIdx); - } + } final CharSequence[] src = shaderSource[shaderIdx]; int curEndIndex = 0; for(int j=0; j<src.length; j++) { @@ -691,8 +711,8 @@ public class ShaderCode { throw new IllegalStateException("shader source not a mutable StringBuilder, but CharSequence of type: "+src[j].getClass().getName()); } final StringBuilder sb = (StringBuilder)src[j]; - curEndIndex += sb.length(); - if(position < curEndIndex) { + curEndIndex += sb.length(); + if(position < curEndIndex) { sb.insert(position, data); return position+data.length(); } @@ -700,6 +720,7 @@ public class ShaderCode { return -1; } + @SuppressWarnings("resource") private static int readShaderSource(Class<?> context, URLConnection conn, StringBuilder result, int lineno) throws IOException { if(DEBUG_CODE) { if(0 == lineno) { @@ -716,12 +737,12 @@ public class ShaderCode { if (line.startsWith("#include ")) { String includeFile = line.substring(9).trim(); URLConnection nextConn = null; - + // Try relative of current shader location nextConn = IOUtil.openURL(IOUtil.getRelativeOf(conn.getURL(), includeFile), "ShaderCode.relativeOf "); if (nextConn == null) { // Try relative of class and absolute - nextConn = IOUtil.getResource(context, includeFile); + nextConn = IOUtil.getResource(context, includeFile); } if (nextConn == null) { // Fail @@ -739,7 +760,7 @@ public class ShaderCode { } /** - * + * * @param context * @param conn * @param result @@ -748,7 +769,7 @@ public class ShaderCode { public static void readShaderSource(Class<?> context, URLConnection conn, StringBuilder result) throws IOException { readShaderSource(context, conn, result, 0); } - + /** * Reads shader source located in <code>path</code>, * either relative to the <code>context</code> class or absolute <i>as-is</i>. @@ -756,21 +777,21 @@ public class ShaderCode { * Final location lookup is performed via {@link ClassLoader#getResource(String)} and {@link ClassLoader#getSystemResource(String)}, * see {@link IOUtil#getResource(Class, String)}. * </p> - * + * * @param context class used to help resolve the source location * @param path location of shader source * @param mutableStringBuilder if <code>true</code> method returns a mutable <code>StringBuilder</code> instance - * which can be edited later on at the costs of a String conversion when passing to + * which can be edited later on at the costs of a String conversion when passing to * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. * If <code>false</code> method returns an immutable <code>String</code> instance, * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} * at no additional costs. - * @throws IOException - * + * @throws IOException + * * @see IOUtil#getResource(Class, String) - */ + */ public static CharSequence readShaderSource(Class<?> context, String path, boolean mutableStringBuilder) throws IOException { - URLConnection conn = IOUtil.getResource(context, path); + URLConnection conn = IOUtil.getResource(context, path); if (conn == null) { return null; } @@ -780,17 +801,17 @@ public class ShaderCode { } /** - * Reads shader binary located in <code>path</code>, + * Reads shader binary located in <code>path</code>, * either relative to the <code>context</code> class or absolute <i>as-is</i>. * <p> * Final location lookup is perfomed via {@link ClassLoader#getResource(String)} and {@link ClassLoader#getSystemResource(String)}, * see {@link IOUtil#getResource(Class, String)}. * </p> - * + * * @param context class used to help resolve the source location * @param path location of shader binary - * @throws IOException - * + * @throws IOException + * * @see IOUtil#getResource(Class, String) */ public static ByteBuffer readShaderBinary(Class<?> context, String path) throws IOException { @@ -806,6 +827,186 @@ public class ShaderCode { } } + // Shall we use: #ifdef GL_FRAGMENT_PRECISION_HIGH .. #endif for using highp in fragment shader if avail ? + /** Default precision of {@link GL#isGLES2() ES2} for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader}: {@value #es2_default_precision_vp} */ + public static final String es2_default_precision_vp = "\nprecision highp float;\nprecision highp int;\n/*precision lowp sampler2D;*/\n/*precision lowp samplerCube;*/\n"; + /** Default precision of {@link GL#isGLES2() ES2} for {@link GL2ES2#GL_FRAGMENT_SHADER fragment-shader}: {@value #es2_default_precision_fp} */ + public static final String es2_default_precision_fp = "\nprecision mediump float;\nprecision mediump int;\n/*precision lowp sampler2D;*/\n/*precision lowp samplerCube;*/\n"; + + /** Default precision of {@link GL#isGLES3() ES3} for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader}: {@value #es3_default_precision_vp} */ + public static final String es3_default_precision_vp = es2_default_precision_vp; + /** Default precision of {@link GL#isGLES3() ES3} for {@link GL2ES2#GL_FRAGMENT_SHADER fragment-shader}: {@value #es3_default_precision_fp} */ + public static final String es3_default_precision_fp = es2_default_precision_fp; + + /** Default precision of GLSL ≥ 1.30 as required until < 1.50 for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader} or {@link GL3#GL_GEOMETRY_SHADER geometry-shader}: {@value #gl3_default_precision_vp_gp}. See GLSL Spec 1.30-1.50 Section 4.5.3. */ + public static final String gl3_default_precision_vp_gp = "\nprecision highp float;\nprecision highp int;\n"; + /** Default precision of GLSL ≥ 1.30 as required until < 1.50 for {@link GL2ES2#GL_FRAGMENT_SHADER fragment-shader}: {@value #gl3_default_precision_fp}. See GLSL Spec 1.30-1.50 Section 4.5.3. */ + public static final String gl3_default_precision_fp = "\nprecision highp float;\nprecision mediump int;\n/*precision mediump sampler2D;*/\n"; + + /** <i>Behavior</i> for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */ + public static final String REQUIRE = "require"; + /** <i>Behavior</i> for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */ + public static final String ENABLE = "enable"; + /** <i>Behavior</i> for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */ + public static final String DISABLE = "disable"; + /** <i>Behavior</i> for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */ + public static final String WARN = "warn"; + + /** + * Creates a GLSL extension directive. + * <p> + * Prefer {@link #ENABLE} over {@link #REQUIRE}, since the latter will force a failure if not supported. + * </p> + * + * @param extensionName + * @param behavior shall be either {@link #REQUIRE}, {@link #ENABLE}, {@link #DISABLE} or {@link #WARN} + * @return the complete extension directive + */ + public static String createExtensionDirective(String extensionName, String behavior) { + return "#extension " + extensionName + " : " + behavior; + } + + /** + * Add GLSL version at the head of this shader source code. + * <p> + * Note: The shader source to be edit must be created using a mutable StringBuilder. + * </p> + * @param gl a GL context, which must have been made current once + * @return the index after the inserted data, maybe 0 if nothing has be inserted. + */ + public final int addGLSLVersion(GL2ES2 gl) { + return insertShaderSource(0, 0, gl.getContext().getGLSLVersionString()); + } + + /** + * Adds default precision to source code at given position if required, i.e. + * {@link #es2_default_precision_vp}, {@link #es2_default_precision_fp}, + * {@link #gl3_default_precision_vp_gp}, {@link #gl3_default_precision_fp} or none, + * depending on the {@link GLContext#getGLSLVersionNumber() GLSL version} being used. + * <p> + * Note: The shader source to be edit must be created using a mutable StringBuilder. + * </p> + * @param gl a GL context, which must have been made current once + * @param pos position within this mutable shader source. + * @return the index after the inserted data, maybe 0 if nothing has be inserted. + */ + public final int addDefaultShaderPrecision(GL2ES2 gl, int pos) { + final String defaultPrecision; + if( gl.isGLES2() ) { + switch ( shaderType ) { + case GL2ES2.GL_VERTEX_SHADER: + defaultPrecision = es2_default_precision_vp; break; + case GL2ES2.GL_FRAGMENT_SHADER: + defaultPrecision = es2_default_precision_fp; break; + default: + defaultPrecision = null; + break; + } + } else if( gl.isGLES3() ) { + switch ( shaderType ) { + case GL2ES2.GL_VERTEX_SHADER: + defaultPrecision = es3_default_precision_vp; break; + case GL2ES2.GL_FRAGMENT_SHADER: + defaultPrecision = es3_default_precision_fp; break; + default: + defaultPrecision = null; + break; + } + } else if( requiresGL3DefaultPrecision(gl) ) { + // GLSL [ 1.30 .. 1.50 [ needs at least fragement float default precision! + switch ( shaderType ) { + case GL2ES2.GL_VERTEX_SHADER: + case GL3.GL_GEOMETRY_SHADER: + defaultPrecision = gl3_default_precision_vp_gp; break; + case GL2ES2.GL_FRAGMENT_SHADER: + defaultPrecision = gl3_default_precision_fp; break; + default: + defaultPrecision = null; + break; + } + } else { + defaultPrecision = null; + } + if( null != defaultPrecision ) { + pos = insertShaderSource(0, pos, defaultPrecision); + } + return pos; + } + + /** Returns true, if GLSL version requires default precision, i.e. ES2 or GLSL [1.30 .. 1.50[. */ + public static final boolean requiresDefaultPrecision(GL2ES2 gl) { + if( gl.isGLES() ) { + return true; + } + return requiresGL3DefaultPrecision(gl); + } + + /** Returns true, if GL3 GLSL version requires default precision, i.e. GLSL [1.30 .. 1.50[. */ + public static final boolean requiresGL3DefaultPrecision(GL2ES2 gl) { + if( gl.isGL3() ) { + final VersionNumber glslVersion = gl.getContext().getGLSLVersionNumber(); + return glslVersion.compareTo(GLContext.Version130) >= 0 && glslVersion.compareTo(GLContext.Version150) < 0 ; + } else { + return false; + } + } + + /** + * Default customization of this shader source code. + * <p> + * Note: The shader source to be edit must be created using a mutable StringBuilder. + * </p> + * @param gl a GL context, which must have been made current once + * @param preludeVersion if true {@link GLContext#getGLSLVersionString()} is preluded, otherwise not. + * @param addDefaultPrecision if <code>true</code> default precision source code line(s) are added, i.e. + * {@link #es2_default_precision_vp}, {@link #es2_default_precision_fp}, + * {@link #gl3_default_precision_vp_gp}, {@link #gl3_default_precision_fp} or none, + * depending on the {@link GLContext#getGLSLVersionNumber() GLSL version} being used. + * @return the index after the inserted data, maybe 0 if nothing has be inserted. + * @see #addGLSLVersion(GL2ES2) + * @see #addDefaultShaderPrecision(GL2ES2, int) + */ + public final int defaultShaderCustomization(GL2ES2 gl, boolean preludeVersion, boolean addDefaultPrecision) { + int pos; + if( preludeVersion ) { + pos = addGLSLVersion(gl); + } else { + pos = 0; + } + if( addDefaultPrecision ) { + pos = addDefaultShaderPrecision(gl, pos); + } + return pos; + } + + /** + * Default customization of this shader source code. + * <p> + * Note: The shader source to be edit must be created using a mutable StringBuilder. + * </p> + * @param gl a GL context, which must have been made current once + * @param preludeVersion if true {@link GLContext#getGLSLVersionString()} is preluded, otherwise not. + * @param esDefaultPrecision optional default precision source code line(s) preluded if not null and if {@link GL#isGLES()}. + * You may use {@link #es2_default_precision_fp} for fragment shader and {@link #es2_default_precision_vp} for vertex shader. + * @return the index after the inserted data, maybe 0 if nothing has be inserted. + * @see #addGLSLVersion(GL2ES2) + * @see #addDefaultShaderPrecision(GL2ES2, int) + */ + public final int defaultShaderCustomization(GL2ES2 gl, boolean preludeVersion, String esDefaultPrecision) { + int pos; + if( preludeVersion ) { + pos = addGLSLVersion(gl); + } else { + pos = 0; + } + if( gl.isGLES() && null != esDefaultPrecision ) { + pos = insertShaderSource(0, pos, esDefaultPrecision); + } else { + pos = addDefaultShaderPrecision(gl, pos); + } + return pos; + } + //---------------------------------------------------------------------- // Internals only below this point // diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderProgram.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderProgram.java index 14ea7d2b8..b289b41e2 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderProgram.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderProgram.java @@ -37,7 +37,7 @@ import java.util.Iterator; import java.io.PrintStream; public class ShaderProgram { - + public ShaderProgram() { id = getNextID(); } @@ -50,6 +50,7 @@ public class ShaderProgram { return programInUse; } + /** Returns the shader program name, which is non zero if valid. */ public int program() { return shaderProgram; } /** @@ -84,7 +85,9 @@ public class ShaderProgram { * If <code>destroyShaderCode</code> is true it destroys the shader codes as well. */ public synchronized void release(GL2ES2 gl, boolean destroyShaderCode) { - useProgram(gl, false); + if( programLinked ) { + useProgram(gl, false); + } for(Iterator<ShaderCode> iter=allShaderCode.iterator(); iter.hasNext(); ) { ShaderCode shaderCode = iter.next(); if(attachedShaderCode.remove(shaderCode)) { @@ -96,9 +99,9 @@ public class ShaderProgram { } allShaderCode.clear(); attachedShaderCode.clear(); - if(0<=shaderProgram) { + if( 0 != shaderProgram ) { gl.glDeleteProgram(shaderProgram); - shaderProgram=-1; + shaderProgram=0; } } @@ -108,7 +111,7 @@ public class ShaderProgram { /** * 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> */ @@ -119,7 +122,7 @@ public class ShaderProgram { public synchronized boolean contains(ShaderCode shaderCode) { return allShaderCode.contains(shaderCode); } - + /** * Warning slow O(n) operation .. * @param id @@ -140,30 +143,33 @@ public class ShaderProgram { // /** - * Creates the empty GL program object using {@link GL2ES2#glCreateProgram()} - * + * 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 void init(GL2ES2 gl) { - if(0>shaderProgram) { + public synchronized final boolean init(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(GL2ES2 gl, ShaderCode shaderCode, PrintStream verboseOut) { - init(gl); + if( !init(gl) ) { return false; } if( allShaderCode.add(shaderCode) ) { - if(!shaderCode.compile(gl, verboseOut)) { + if( !shaderCode.compile(gl, verboseOut) ) { return false; } - if(attachedShaderCode.add(shaderCode)) { + if( attachedShaderCode.add(shaderCode) ) { ShaderUtil.attachShader(gl, shaderProgram, shaderCode.shader()); } } @@ -173,11 +179,11 @@ public class ShaderProgram { /** * Replace a shader in a program and re-links the program. * - * @param gl + * @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 @@ -190,31 +196,29 @@ public class ShaderProgram { * @see ShaderState#glResetAllVertexAttributes */ public synchronized boolean replaceShader(GL2ES2 gl, ShaderCode oldShader, ShaderCode newShader, PrintStream verboseOut) { - init(gl); - - if(!newShader.compile(gl, verboseOut)) { + if(!init(gl) || !newShader.compile(gl, verboseOut)) { return false; } - + 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, System.err); + + programLinked = ShaderUtil.isProgramLinkStatusValid(gl, shaderProgram, verboseOut); if ( programLinked && shaderWasInUse ) { useProgram(gl, true); } @@ -223,23 +227,27 @@ public class ShaderProgram { /** * 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(GL2ES2 gl, PrintStream verboseOut) { - init(gl); + if( !init(gl) ) { + programLinked = false; // mark unlinked due to user attempt to [re]link + return false; + } for(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)) { @@ -250,11 +258,12 @@ public class ShaderProgram { // Link the program gl.glLinkProgram(shaderProgram); - programLinked = ShaderUtil.isProgramLinkStatusValid(gl, shaderProgram, System.err); + programLinked = ShaderUtil.isProgramLinkStatusValid(gl, shaderProgram, verboseOut); return programLinked; } + @Override public boolean equals(Object obj) { if(this == obj) { return true; } if(obj instanceof ShaderProgram) { @@ -263,6 +272,7 @@ public class ShaderProgram { return false; } + @Override public int hashCode() { return id; } @@ -279,7 +289,8 @@ public class ShaderProgram { sb.append("]"); return sb; } - + + @Override public String toString() { return toString(null).toString(); } @@ -291,17 +302,20 @@ public class ShaderProgram { public synchronized boolean validateProgram(GL2ES2 gl, PrintStream verboseOut) { return ShaderUtil.isProgramExecStatusValid(gl, shaderProgram, verboseOut); } - + public synchronized void useProgram(GL2ES2 gl, boolean on) { if(!programLinked) { throw new GLException("Program is not linked"); } if(programInUse==on) { return; } - gl.glUseProgram(on?shaderProgram:0); + if( 0 == shaderProgram ) { + on = false; + } + gl.glUseProgram( on ? shaderProgram : 0 ); programInUse = on; } protected boolean programLinked = false; protected boolean programInUse = false; - protected int shaderProgram=-1; + protected int shaderProgram = 0; // non zero is valid! protected HashSet<ShaderCode> allShaderCode = new HashSet<ShaderCode>(); protected HashSet<ShaderCode> attachedShaderCode = new HashSet<ShaderCode>(); protected int id = -1; 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; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderUtil.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderUtil.java index 40c052498..5cd384c58 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderUtil.java @@ -1,21 +1,21 @@ /* * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * + * * - Redistribution 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. - * + * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A @@ -28,7 +28,7 @@ * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * + * */ package com.jogamp.opengl.util.glsl; @@ -40,6 +40,7 @@ import java.util.*; import javax.media.opengl.*; import com.jogamp.common.nio.Buffers; +import com.jogamp.opengl.GLExtensions; public class ShaderUtil { public static String getShaderInfoLog(GL _gl, int shaderObj) { @@ -116,11 +117,11 @@ public class ShaderUtil { } return true; } - + /** * Performs {@link GL2ES2#glValidateProgram(int)} * <p> - * One shall only call this method while debugging and only if all required + * One shall only call this method while debugging and only if all required * resources by the shader are set. * </p> * <p> @@ -149,7 +150,7 @@ public class ShaderUtil { } /** - * If supported, queries the natively supported shader binary formats using + * If supported, queries the natively supported shader binary formats using * {@link GL2ES2#GL_NUM_SHADER_BINARY_FORMATS} and {@link GL2ES2#GL_SHADER_BINARY_FORMATS} * via {@link GL2ES2#glGetIntegerv(int, int[], int)}. */ @@ -159,15 +160,21 @@ public class ShaderUtil { if(null == info.shaderBinaryFormats) { info.shaderBinaryFormats = new HashSet<Integer>(); if (gl.isGLES2Compatible()) { - final int[] param = new int[1]; - gl.glGetIntegerv(GL2ES2.GL_NUM_SHADER_BINARY_FORMATS, param, 0); - int numFormats = param[0]; - if(numFormats>0) { - int[] formats = new int[numFormats]; - gl.glGetIntegerv(GL2ES2.GL_SHADER_BINARY_FORMATS, formats, 0); - for(int i=0; i<numFormats; i++) { - info.shaderBinaryFormats.add(new Integer(formats[i])); + try { + final int[] param = new int[1]; + gl.glGetIntegerv(GL2ES2.GL_NUM_SHADER_BINARY_FORMATS, param, 0); + final int err = gl.glGetError(); + final int numFormats = GL.GL_NO_ERROR == err ? param[0] : 0; + if(numFormats>0) { + int[] formats = new int[numFormats]; + gl.glGetIntegerv(GL2ES2.GL_SHADER_BINARY_FORMATS, formats, 0); + for(int i=0; i<numFormats; i++) { + info.shaderBinaryFormats.add(new Integer(formats[i])); + } } + } catch (GLException gle) { + System.err.println("Catched Exception on thread "+Thread.currentThread().getName()); + gle.printStackTrace(); } } } @@ -180,17 +187,28 @@ public class ShaderUtil { final ProfileInformation info = getProfileInformation(gl); if(null==info.shaderCompilerAvailable) { if(gl.isGLES2()) { - final byte[] param = new byte[1]; - gl.glGetBooleanv(GL2ES2.GL_SHADER_COMPILER, param, 0); - boolean v = param[0]!=(byte)0x00; - if(!v) { - final Set<Integer> bfs = getShaderBinaryFormats(gl); - if(bfs.size()==0) { - // no supported binary formats, hence a compiler must be available! - v = true; + boolean queryOK = false; + try { + final byte[] param = new byte[1]; + gl.glGetBooleanv(GL2ES2.GL_SHADER_COMPILER, param, 0); + final int err = gl.glGetError(); + boolean v = GL.GL_NO_ERROR == err && param[0]!=(byte)0x00; + if(!v) { + final Set<Integer> bfs = getShaderBinaryFormats(gl); + if(bfs.size()==0) { + // no supported binary formats, hence a compiler must be available! + v = true; + } } + info.shaderCompilerAvailable = new Boolean(v); + queryOK = true; + } catch (GLException gle) { + System.err.println("Catched Exception on thread "+Thread.currentThread().getName()); + gle.printStackTrace(); + } + if(!queryOK) { + info.shaderCompilerAvailable = new Boolean(true); } - info.shaderCompilerAvailable = new Boolean(v); } else if( gl.isGL2ES2() ) { info.shaderCompilerAvailable = new Boolean(true); } else { @@ -200,6 +218,13 @@ public class ShaderUtil { return info.shaderCompilerAvailable.booleanValue(); } + /** Returns true if GeometryShader is supported, i.e. whether GLContext is ≥ 3.2 or ARB_geometry_shader4 extension is available. */ + public static boolean isGeometryShaderSupported(GL _gl) { + final GLContext ctx = _gl.getContext(); + return ctx.getGLVersionNumber().compareTo(GLContext.Version320) >= 0 || + ctx.isExtensionAvailable(GLExtensions.ARB_geometry_shader4); + } + public static void shaderSource(GL _gl, int shader, CharSequence[] source) { final GL2ES2 gl = _gl.getGL2ES2(); @@ -215,7 +240,7 @@ public class ShaderUtil { IntBuffer lengths = Buffers.newDirectIntBuffer(count); for(int i=0; i<count; i++) { lengths.put(i, source[i].length()); - } + } if(source instanceof String[]) { // rare case .. gl.glShaderSource(shader, count, (String[])source, lengths); @@ -224,10 +249,10 @@ public class ShaderUtil { for(int i = source.length - 1; i>=0; i--) { final CharSequence csq = source[i]; if(csq instanceof String) { - // if ShaderCode.create(.. mutableStringBuffer == false ) + // if ShaderCode.create(.. mutableStringBuilder == false ) tmp[i] = (String) csq; } else { - // if ShaderCode.create(.. mutableStringBuffer == true ) + // if ShaderCode.create(.. mutableStringBuilder == true ) tmp[i] = source[i].toString(); } } @@ -313,7 +338,7 @@ public class ShaderUtil { } createShader(gl, shaderType, shader); - err = gl.glGetError(); + err = gl.glGetError(); if(err!=GL.GL_NO_ERROR) { throw new GLException("createAndLoadShader: CreateShader failed, GL Error: 0x"+Integer.toHexString(err)); } @@ -328,7 +353,7 @@ public class ShaderUtil { } public static boolean createAndCompileShader(GL _gl, IntBuffer shader, int shaderType, - CharSequence[][] sources, + CharSequence[][] sources, PrintStream verboseOut) { final GL2ES2 gl = _gl.getGL2ES2(); @@ -338,32 +363,32 @@ public class ShaderUtil { } createShader(gl, shaderType, shader); - err = gl.glGetError(); + err = gl.glGetError(); if(err!=GL.GL_NO_ERROR) { throw new GLException("createAndCompileShader: CreateShader failed, GL Error: 0x"+Integer.toHexString(err)); } shaderSource(gl, shader, sources); - err = gl.glGetError(); + err = gl.glGetError(); if(err!=GL.GL_NO_ERROR) { throw new GLException("createAndCompileShader: ShaderSource failed, GL Error: 0x"+Integer.toHexString(err)); } compileShader(gl, shader); - err = gl.glGetError(); + err = gl.glGetError(); if(err!=GL.GL_NO_ERROR && null!=verboseOut) { verboseOut.println("createAndCompileShader: CompileShader failed, GL Error: 0x"+Integer.toHexString(err)); } return isShaderStatusValid(gl, shader, GL2ES2.GL_COMPILE_STATUS, verboseOut) && err == GL.GL_NO_ERROR; } - + private static final String implObjectKey = "com.jogamp.opengl.util.glsl.ShaderUtil" ; - + private static class ProfileInformation { Boolean shaderCompilerAvailable = null; Set<Integer> shaderBinaryFormats = null; - } + } private static ProfileInformation getProfileInformation(GL gl) { final GLContext context = gl.getContext(); diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/fixedfunc/FixedFuncUtil.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/fixedfunc/FixedFuncUtil.java index d92a7aa22..2f8884a3a 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/fixedfunc/FixedFuncUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/fixedfunc/FixedFuncUtil.java @@ -4,26 +4,45 @@ package com.jogamp.opengl.util.glsl.fixedfunc; -import javax.media.opengl.*; -import javax.media.opengl.fixedfunc.*; +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES1; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLException; +import javax.media.opengl.fixedfunc.GLPointerFuncUtil; + +import jogamp.opengl.util.glsl.fixedfunc.FixedFuncHook; +import jogamp.opengl.util.glsl.fixedfunc.FixedFuncImpl; +import jogamp.opengl.util.glsl.fixedfunc.FixedFuncPipeline; + +import com.jogamp.opengl.util.PMVMatrix; -import jogamp.opengl.util.glsl.fixedfunc.*; /** * Tool to pipeline GL2ES2 into a fixed function emulation implementing GL2ES1. */ public class FixedFuncUtil { /** + * @param gl + * @param mode one of the {@link ShaderSelectionMode}s + * @param pmvMatrix optional pass through PMVMatrix for the {@link FixedFuncHook} and {@link FixedFuncPipeline} * @return If gl is a GL2ES1 and force is false, return the type cast object, * otherwise create a fixed function emulation pipeline using the given GL2ES2 impl * and hook it to the GLContext via {@link GLContext#setGL(GL)}. * @throws GLException if the GL object is neither GL2ES1 nor GL2ES2 + * + * @see ShaderSelectionMode#AUTO + * @see ShaderSelectionMode#COLOR + * @see ShaderSelectionMode#COLOR_LIGHT_PER_VERTEX + * @see ShaderSelectionMode#COLOR_TEXTURE + * @see ShaderSelectionMode#COLOR_TEXTURE_LIGHT_PER_VERTEX */ - public static final GL2ES1 wrapFixedFuncEmul(GL gl, boolean force) { + public static final GL2ES1 wrapFixedFuncEmul(GL gl, ShaderSelectionMode mode, PMVMatrix pmvMatrix, boolean force, boolean verbose) { if(gl.isGL2ES2() && ( !gl.isGL2ES1() || force ) ) { - GL2ES2 es2 = gl.getGL2ES2(); - FixedFuncHook hook = new FixedFuncHook(es2); - FixedFuncImpl impl = new FixedFuncImpl(es2, hook); + final GL2ES2 es2 = gl.getGL2ES2(); + final FixedFuncHook hook = new FixedFuncHook(es2, mode, pmvMatrix); + hook.setVerbose(verbose); + final FixedFuncImpl impl = new FixedFuncImpl(es2, hook); gl.getContext().setGL(impl); return impl; } else if(gl.isGL2ES1()) { @@ -33,21 +52,30 @@ public class FixedFuncUtil { } /** + * @param gl + * @param mode one of the {@link ShaderSelectionMode}s + * @param pmvMatrix optional pass through PMVMatrix for the {@link FixedFuncHook} and {@link FixedFuncPipeline} * @return If gl is a GL2ES1, return the type cast object, * otherwise create a fixed function emulation pipeline using the GL2ES2 impl. * and hook it to the GLContext via {@link GLContext#setGL(GL)}. * @throws GLException if the GL object is neither GL2ES1 nor GL2ES2 + * + * @see ShaderSelectionMode#AUTO + * @see ShaderSelectionMode#COLOR + * @see ShaderSelectionMode#COLOR_LIGHT_PER_VERTEX + * @see ShaderSelectionMode#COLOR_TEXTURE + * @see ShaderSelectionMode#COLOR_TEXTURE_LIGHT_PER_VERTEX */ - public static final GL2ES1 wrapFixedFuncEmul(GL gl) { - return wrapFixedFuncEmul(gl, false); + public static final GL2ES1 wrapFixedFuncEmul(GL gl, ShaderSelectionMode mode, PMVMatrix pmvMatrix) { + return wrapFixedFuncEmul(gl, mode, null, false, false); } /** - * Mapping fixed function (client) array indices to + * Mapping fixed function (client) array indices to * GLSL array attribute names. * * Useful for uniq mapping of canonical array index names as listed. - * + * * @see #mgl_Vertex * @see javax.media.opengl.fixedfunc.GLPointerFunc#GL_VERTEX_ARRAY * @see #mgl_Normal diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/fixedfunc/ShaderSelectionMode.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/fixedfunc/ShaderSelectionMode.java new file mode 100644 index 000000000..426fb0d85 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/fixedfunc/ShaderSelectionMode.java @@ -0,0 +1,27 @@ +package com.jogamp.opengl.util.glsl.fixedfunc; + +/** + * Shader selection mode + * + * @see ShaderSelectionMode#AUTO + * @see ShaderSelectionMode#COLOR + * @see ShaderSelectionMode#COLOR_LIGHT_PER_VERTEX + * @see ShaderSelectionMode#COLOR_TEXTURE + * @see ShaderSelectionMode#COLOR_TEXTURE_LIGHT_PER_VERTEX + */ +public enum ShaderSelectionMode { + /** Auto shader selection, based upon FFP states. */ + AUTO, + /** Fixed shader selection: Simple color. */ + COLOR, + /** Fixed shader selection: Multi-Textured color. 2 texture units. */ + COLOR_TEXTURE2, + /** Fixed shader selection: Multi-Textured color. 4 texture units. */ + COLOR_TEXTURE4, + /** Fixed shader selection: Multi-Textured color. 8 texture units. */ + COLOR_TEXTURE8, + /** Fixed shader selection: Color with vertex-lighting. */ + COLOR_LIGHT_PER_VERTEX, + /** Fixed shader selection: Multi-Textured color with vertex-lighting. 8 texture units.*/ + COLOR_TEXTURE8_LIGHT_PER_VERTEX +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShader.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShader.java index a5b1c6687..44fbf1c6d 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShader.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShader.java @@ -55,8 +55,8 @@ public abstract class CompileShader { URL resourceURL = IOUtil.getResource(null, resourceName).getURL(); String dirName = dirname(resourceURL.getPath()); - outName = dirName + File.separator + "bin" + File.separator + - ShaderCode.getBinarySubPath(getBinaryFormat()) + File.separator + + outName = dirName + File.separator + "bin" + File.separator + + ShaderCode.getBinarySubPath(getBinaryFormat()) + File.separator + outName; processOneShader(resourceName, outName, type); } @@ -137,7 +137,7 @@ public abstract class CompileShader { } String dirname; if (lastSlash < 0) { - dirname = new String(); + dirname = ""; } else { dirname = path.substring(0, lastSlash + 1); } @@ -161,6 +161,7 @@ public abstract class CompileShader { new Thread(this, "Output Reader Thread").start(); } + @Override public void run() { byte[] buffer = new byte[4096]; diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShaderNVidia.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShaderNVidia.java index 8eb9ef579..215cf592b 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShaderNVidia.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/sdk/CompileShaderNVidia.java @@ -21,10 +21,12 @@ public class CompileShaderNVidia extends CompileShader { } } + @Override public int getBinaryFormat() { return GLES2.GL_NVIDIA_PLATFORM_BINARY_NV; } + @Override public File getSDKCompilerDir() { File compilerDir = new File( NVAPSDK + File.separator + "tools" + File.separator ); File compilerFile = new File( compilerDir, getVertexShaderCompiler()); @@ -39,10 +41,12 @@ public class CompileShaderNVidia extends CompileShader { return compilerDir; } + @Override public String getVertexShaderCompiler() { return "glslv.bat"; } + @Override public String getFragmentShaderCompiler() { return "glslf.bat"; } |