/** * Copyright 2010 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ package com.jogamp.opengl.util.glsl; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.io.StringReader; import java.net.URISyntaxException; import java.net.URLConnection; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.Arrays; import java.util.Iterator; import java.util.Set; import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GL3; import com.jogamp.opengl.GL3ES3; import com.jogamp.opengl.GL4; import com.jogamp.opengl.GLES2; import com.jogamp.opengl.GLContext; import com.jogamp.opengl.GLException; import jogamp.opengl.Debug; import com.jogamp.common.net.Uri; 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. *

* 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}. *

*

* Support for {@link GL4#GL_TESS_CONTROL_SHADER} and {@link GL4#GL_TESS_EVALUATION_SHADER} * was added since 2.2.1. *

*/ public class ShaderCode { public static final boolean DEBUG_CODE = Debug.isPropertyDefined("jogl.debug.GLSLCode", true); /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in source code: {@value} */ public static final String SUFFIX_VERTEX_SOURCE = "vp" ; /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in binary: {@value} */ public static final String SUFFIX_VERTEX_BINARY = "bvp" ; /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in source code: {@value} */ public static final String SUFFIX_GEOMETRY_SOURCE = "gp" ; /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in binary: {@value} */ public static final String SUFFIX_GEOMETRY_BINARY = "bgp" ; /** * Unique resource suffix for {@link GL3ES3#GL_COMPUTE_SHADER} in source code: {@value} * @since 2.3.2 */ public static final String SUFFIX_COMPUTE_SOURCE = "cp" ; /** * Unique resource suffix for {@link GL3ES3#GL_COMPUTE_SHADER} in binary: {@value} * @since 2.3.2 */ public static final String SUFFIX_COMPUTE_BINARY = "bcp" ; /** * Unique resource suffix for {@link GL4#GL_TESS_CONTROL_SHADER} in source code: {@value} * @since 2.2.1 */ public static final String SUFFIX_TESS_CONTROL_SOURCE = "tcp" ; /** * Unique resource suffix for {@link GL4#GL_TESS_CONTROL_SHADER} in binary: {@value} * @since 2.2.1 */ public static final String SUFFIX_TESS_CONTROL_BINARY = "btcp" ; /** * Unique resource suffix for {@link GL4#GL_TESS_EVALUATION_SHADER} in source code: {@value} * @since 2.2.1 */ public static final String SUFFIX_TESS_EVALUATION_SOURCE = "tep" ; /** * Unique resource suffix for {@link GL4#GL_TESS_EVALUATION_SHADER} in binary: {@value} * @since 2.2.1 */ public static final String SUFFIX_TESS_EVALUATION_BINARY = "btep" ; /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in source code: {@value} */ public static final String SUFFIX_FRAGMENT_SOURCE = "fp" ; /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in binary: {@value} */ public static final String SUFFIX_FRAGMENT_BINARY = "bfp" ; /** Unique relative path for binary shader resources for {@link GLES2#GL_NVIDIA_PLATFORM_BINARY_NV NVIDIA}: {@value} */ public static final String SUB_PATH_NVIDIA = "nvidia" ; /** * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}. * @param count number of shaders * @param source CharSequence array containing the shader sources, organized as source[count][strings-per-shader]. * May be either an immutable String - or mutable StringBuilder array. * * @throws IllegalArgumentException if count and source.length do not match */ public ShaderCode(final int type, final int count, final CharSequence[][] source) { if(source.length != count) { throw new IllegalArgumentException("shader number ("+count+") and sourceFiles array ("+source.length+") of different lenght."); } switch (type) { case GL2ES2.GL_VERTEX_SHADER: case GL2ES2.GL_FRAGMENT_SHADER: case GL3ES3.GL_GEOMETRY_SHADER: case GL3ES3.GL_TESS_CONTROL_SHADER: case GL3ES3.GL_TESS_EVALUATION_SHADER: case GL3ES3.GL_COMPUTE_SHADER: break; default: throw new GLException("Unknown shader type: "+type); } shaderSource = source; shaderBinaryFormat = -1; shaderBinary = null; shaderType = type; shader = Buffers.newDirectIntBuffer(count); id = getNextID(); if(DEBUG_CODE) { System.out.println("Created: "+toString()); // dumpShaderSource(System.out); // already done in readShaderSource } } /** * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}. * @param count number of shaders * @param binary binary buffer containing the shader binaries, */ public ShaderCode(final int type, final int count, final int binFormat, final Buffer binary) { switch (type) { case GL2ES2.GL_VERTEX_SHADER: case GL2ES2.GL_FRAGMENT_SHADER: case GL3ES3.GL_GEOMETRY_SHADER: case GL3ES3.GL_TESS_CONTROL_SHADER: case GL3ES3.GL_TESS_EVALUATION_SHADER: case GL3ES3.GL_COMPUTE_SHADER: break; default: throw new GLException("Unknown shader type: "+type); } shaderSource = null; shaderBinaryFormat = binFormat; shaderBinary = binary; shaderType = type; shader = Buffers.newDirectIntBuffer(count); id = getNextID(); } /** * Creates a complete {@link ShaderCode} object while reading all shader source of sourceFiles, * which location is resolved using the context 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}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}. * @param count number of shaders * @param context class used to help resolving the source location * @param sourceFiles array of source locations, organized as sourceFiles[count] -> shaderSources[count][1] * @param mutableStringBuilder if true method returns a mutable StringBuilder instance * which can be edited later on at the costs of a String conversion when passing to * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. * If false method returns an immutable String instance, * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} * at no additional costs. * * @throws IllegalArgumentException if count and sourceFiles.length do not match * @see #readShaderSource(Class, String) */ public static ShaderCode create(final GL2ES2 gl, final int type, final int count, final Class context, final String[] sourceFiles, final boolean mutableStringBuilder) { if(null != gl && !ShaderUtil.isShaderCompilerAvailable(gl)) { return null; } CharSequence[][] shaderSources = null; if(null!=sourceFiles) { // sourceFiles.length and count is validated in ctor shaderSources = new CharSequence[sourceFiles.length][1]; for(int i=0; isourceLocations * via {@link #readShaderSource(Uri, boolean)}. * * @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}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}. * @param count number of shaders * @param sourceLocations array of {@link Uri} source locations, organized as sourceFiles[count] -> shaderSources[count][1] * @param mutableStringBuilder if true method returns a mutable StringBuilder instance * which can be edited later on at the costs of a String conversion when passing to * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. * If false method returns an immutable String instance, * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} * at no additional costs. * * @throws IllegalArgumentException if count and sourceFiles.length do not match * @see #readShaderSource(Uri, boolean) * @since 2.3.2 */ public static ShaderCode create(final GL2ES2 gl, final int type, final int count, final Uri[] sourceLocations, final boolean mutableStringBuilder) { if(null != gl && !ShaderUtil.isShaderCompilerAvailable(gl)) { return null; } CharSequence[][] shaderSources = null; if(null!=sourceLocations) { // sourceFiles.length and count is validated in ctor shaderSources = new CharSequence[sourceLocations.length][1]; for(int i=0; ibinaryFile, * which location is resolved using the context class, see {@link #readShaderBinary(Class, String)}. * * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_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 sourceFiles[count] * * @see #readShaderBinary(Class, String) * @see ShaderUtil#getShaderBinaryFormats(GL) */ public static ShaderCode create(final int type, final int count, final Class context, int binFormat, final String binaryFile) { ByteBuffer shaderBinary = null; if(null!=binaryFile && 0<=binFormat) { try { shaderBinary = readShaderBinary(context, binaryFile); } catch (final IOException ioe) { throw new RuntimeException("readShaderBinary("+binaryFile+") error: ", ioe); } if(null == shaderBinary) { binFormat = -1; } } if(null==shaderBinary) { return null; } return new ShaderCode(type, count, binFormat, shaderBinary); } /** * Creates a complete {@link ShaderCode} object while reading the shader binary from {@link Uri} binLocations * via {@link #readShaderBinary(Uri)}. * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}. * @param count number of shaders * @param binFormat a valid native binary format as they can be queried by {@link ShaderUtil#getShaderBinaryFormats(GL)}. * @param binLocations {@link Uri} binary location * * @see #readShaderBinary(Uri) * @see ShaderUtil#getShaderBinaryFormats(GL) * @since 2.3.2 */ public static ShaderCode create(final int type, final int count, int binFormat, final Uri binLocation) { ByteBuffer shaderBinary = null; if(null!=binLocation && 0<=binFormat) { try { shaderBinary = readShaderBinary(binLocation); } catch (final IOException ioe) { throw new RuntimeException("readShaderBinary("+binLocation+") error: ", ioe); } if(null == shaderBinary) { binFormat = -1; } } if(null==shaderBinary) { return null; } return new ShaderCode(type, count, binFormat, shaderBinary); } /** * Returns a unique suffix for shader resources as follows: * * @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}, {@link GL3#GL_GEOMETRY_SHADER}, * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}. * * @throws GLException if type is not supported * * @see #create(GL2ES2, int, Class, String, String, String, boolean) */ public static String getFileSuffix(final boolean binary, final int type) { switch (type) { case GL2ES2.GL_VERTEX_SHADER: return binary?SUFFIX_VERTEX_BINARY:SUFFIX_VERTEX_SOURCE; case GL2ES2.GL_FRAGMENT_SHADER: return binary?SUFFIX_FRAGMENT_BINARY:SUFFIX_FRAGMENT_SOURCE; case GL3ES3.GL_GEOMETRY_SHADER: return binary?SUFFIX_GEOMETRY_BINARY:SUFFIX_GEOMETRY_SOURCE; case GL3ES3.GL_TESS_CONTROL_SHADER: return binary?SUFFIX_TESS_CONTROL_BINARY:SUFFIX_TESS_CONTROL_SOURCE; case GL3ES3.GL_TESS_EVALUATION_SHADER: return binary?SUFFIX_TESS_EVALUATION_BINARY:SUFFIX_TESS_EVALUATION_SOURCE; case GL3ES3.GL_COMPUTE_SHADER: return binary?SUFFIX_COMPUTE_BINARY:SUFFIX_COMPUTE_SOURCE; default: throw new GLException("illegal shader type: "+type); } } /** * Returns a unique relative path for binary shader resources as follows: * * * @throws GLException if binFormat is not supported * * @see #create(GL2ES2, int, Class, String, String, String, boolean) */ public static String getBinarySubPath(final int binFormat) { switch (binFormat) { case GLES2.GL_NVIDIA_PLATFORM_BINARY_NV: return SUB_PATH_NVIDIA; default: throw new GLException("unsupported binary format: "+binFormat); } } /** * 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. *

* The source and binary location names are expected w/o suffixes which are * resolved and appended using the given {@code srcSuffixOpt} and {@code binSuffixOpt} * if not {@code null}, otherwise {@link #getFileSuffix(boolean, int)} determines the suffixes. *

*

* Additionally, the binary resource is expected within a subfolder of binRoot * 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. *

* * Example: *
     *   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" }, null,
     *                                         "shader/bin", "vertex", null, true);
     *      ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, 1, this.getClass(),
     *                                         "shader", new String[] { "vertex" }, null,
     *                                         "shader/bin", "fragment", null, true);
     *      ShaderProgram sp0 = new ShaderProgram();
     *      sp0.add(gl, vp0, System.err);
     *      sp0.add(gl, fp0, System.err);
     *      st.attachShaderProgram(gl, sp0, true);
     * 
* A simplified entry point is {@link #create(GL2ES2, int, Class, String, String, String, boolean)}. * *

* The location is finally being resolved using the context class, see {@link #readShaderBinary(Class, String)}. *

* * @param gl current GL object to determine whether a shader compiler is available (if source is used), * or to determine the shader binary format (if binary is used). * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}. * @param count number of shaders * @param context class used to help resolving the source and binary location * @param srcRoot relative root path for srcBasenames optional * @param srcBasenames basenames w/o path or suffix relative to srcRoot for the shader's source code * @param srcSuffixOpt optional custom suffix for shader's source file, * if {@code null} {@link #getFileSuffix(boolean, int)} is being used. * @param binRoot relative root path for binBasenames * @param binBasename basename w/o path or suffix relative to binRoot for the shader's binary code * @param binSuffixOpt optional custom suffix for shader's binary file, * if {@code null} {@link #getFileSuffix(boolean, int)} is being used. * @param mutableStringBuilder if true method returns a mutable StringBuilder instance * which can be edited later on at the costs of a String conversion when passing to * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. * If false method returns an immutable String instance, * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} * at no additional costs. * * @throws IllegalArgumentException if count and srcBasenames.length 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) * * @since 2.3.2 */ public static ShaderCode create(final GL2ES2 gl, final int type, final int count, final Class context, final String srcRoot, final String[] srcBasenames, final String srcSuffixOpt, final String binRoot, final String binBasename, final String binSuffixOpt, final boolean mutableStringBuilder) { ShaderCode res = null; final String srcPath[]; String srcPathString = null; String binFileName = null; if( null!=srcBasenames && ShaderUtil.isShaderCompilerAvailable(gl) ) { srcPath = new String[srcBasenames.length]; final String srcSuffix = null != srcSuffixOpt ? srcSuffixOpt : getFileSuffix(false, type); if( null != srcRoot && srcRoot.length() > 0 ) { for(int i=0; i binFmts = ShaderUtil.getShaderBinaryFormats(gl); final String binSuffix = null != binSuffixOpt ? binSuffixOpt : getFileSuffix(true, type); for(final Iterator iter=binFmts.iterator(); iter.hasNext(); ) { final int bFmt = iter.next().intValue(); final String bFmtPath = getBinarySubPath(bFmt); if(null==bFmtPath) continue; binFileName = binRoot + '/' + bFmtPath + '/' + binBasename + "." + binSuffix; res = create(type, count, context, bFmt, binFileName); if(null!=res) { return res; } } } throw new GLException("No shader code found (source nor binary) for src: "+srcPathString+ ", bin: "+binFileName); } /** * Simplified variation of {@link #create(GL2ES2, int, int, Class, String, String[], String, String, String, String, boolean)}. *

* 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. *

*

* The source and binary location names are expected w/o suffixes which are * resolved and appended using {@link #getFileSuffix(boolean, int)}. *

*

* Additionally, the binary resource is expected within a subfolder of binRoot * 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. *

* * Example: *
     *   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", true);
     *      ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, 1, this.getClass(),
     *                                         "shader", new String[] { "vertex" }, "shader/bin", "fragment", true);
     *      ShaderProgram sp0 = new ShaderProgram();
     *      sp0.add(gl, vp0, System.err);
     *      sp0.add(gl, fp0, System.err);
     *      st.attachShaderProgram(gl, sp0, true);
     * 
* A simplified entry point is {@link #create(GL2ES2, int, Class, String, String, String, boolean)}. * *

* The location is finally being resolved using the context class, see {@link #readShaderBinary(Class, String)}. *

* * @param gl current GL object to determine whether a shader compiler is available (if source is used), * or to determine the shader binary format (if binary is used). * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}. * @param count number of shaders * @param context class used to help resolving the source and binary location * @param srcRoot relative root path for srcBasenames optional * @param srcBasenames basenames w/o path or suffix relative to srcRoot for the shader's source code * @param binRoot relative root path for binBasenames * @param binBasename basename w/o path or suffix relative to binRoot for the shader's binary code * @param mutableStringBuilder if true method returns a mutable StringBuilder instance * which can be edited later on at the costs of a String conversion when passing to * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. * If false method returns an immutable String instance, * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} * at no additional costs. * * @throws IllegalArgumentException if count and srcBasenames.length do not match * * @see #create(GL2ES2, int, int, Class, String, String[], String, String, String, String, boolean) * @see #readShaderSource(Class, String) * @see #getFileSuffix(boolean, int) * @see ShaderUtil#getShaderBinaryFormats(GL) * @see #getBinarySubPath(int) */ public static ShaderCode create(final GL2ES2 gl, final int type, final int count, final Class context, final String srcRoot, final String[] srcBasenames, final String binRoot, final String binBasename, final boolean mutableStringBuilder) { return create(gl, type, count, context, srcRoot, srcBasenames, null, binRoot, binBasename, null, mutableStringBuilder); } /** * Simplified variation of {@link #create(GL2ES2, int, int, Class, String, String[], String, String, String, String, boolean)}. *

* Example: *

     *   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", null, null, true);
     *      ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(),
     *                                         "shader", "shader/bin", "fragment", null, null, true);
     *      ShaderProgram sp0 = new ShaderProgram();
     *      sp0.add(gl, vp0, System.err);
     *      sp0.add(gl, fp0, System.err);
     *      st.attachShaderProgram(gl, sp0, true);
     * 
*

* * @param gl current GL object to determine whether a shader compiler is available (if source is used), * or to determine the shader binary format (if binary is used). * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}. * @param context class used to help resolving the source and binary location * @param srcRoot relative root path for basename optional * @param binRoot relative root path for basename * @param basename basename w/o path or suffix relative to srcRoot and binRoot * for the shader's source and binary code. * @param srcSuffixOpt optional custom suffix for shader's source file, * if {@code null} {@link #getFileSuffix(boolean, int)} is being used. * @param binSuffixOpt optional custom suffix for shader's binary file, * if {@code null} {@link #getFileSuffix(boolean, int)} is being used. * @param mutableStringBuilder if true method returns a mutable StringBuilder instance * which can be edited later on at the costs of a String conversion when passing to * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. * If false method returns an immutable String instance, * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} * at no additional costs. * @throws IllegalArgumentException if count is not 1 * * @see #create(GL2ES2, int, int, Class, String, String[], String, String, String, String, boolean) * @since 2.3.2 */ public static ShaderCode create(final GL2ES2 gl, final int type, final Class context, final String srcRoot, final String binRoot, final String basename, final String srcSuffixOpt, final String binSuffixOpt, final boolean mutableStringBuilder) { return create(gl, type, 1, context, srcRoot, new String[] { basename }, srcSuffixOpt, binRoot, basename, binSuffixOpt, mutableStringBuilder ); } /** * Simplified variation of {@link #create(GL2ES2, int, Class, String, String, String, String, String, boolean)}. *

* Example: *

     *   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", true);
     *      ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(),
     *                                         "shader", "shader/bin", "fragment", true);
     *      ShaderProgram sp0 = new ShaderProgram();
     *      sp0.add(gl, vp0, System.err);
     *      sp0.add(gl, fp0, System.err);
     *      st.attachShaderProgram(gl, sp0, true);
     * 
*

* * @param gl current GL object to determine whether a shader compiler is available (if source is used), * or to determine the shader binary format (if binary is used). * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER}, * {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}. * @param context class used to help resolving the source and binary location * @param srcRoot relative root path for basename optional * @param binRoot relative root path for basename * @param mutableStringBuilder if true method returns a mutable StringBuilder instance * which can be edited later on at the costs of a String conversion when passing to * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. * If false method returns an immutable String instance, * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} * at no additional costs. * @param basenames basename w/o path or suffix relative to srcRoot and binRoot * for the shader's source and binary code. * @param mutableStringBuilder if true method returns a mutable StringBuilder instance * which can be edited later on at the costs of a String conversion when passing to * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. * If false method returns an immutable String instance, * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} * at no additional costs. * @throws IllegalArgumentException if count is not 1 */ public static ShaderCode create(final GL2ES2 gl, final int type, final Class context, final String srcRoot, final String binRoot, final String basename, final boolean mutableStringBuilder) { return ShaderCode.create(gl, type, context, srcRoot, binRoot, basename, null, null, mutableStringBuilder); } /** * returns the uniq shader id as an integer */ public int id() { return id; } public int shaderType() { return shaderType; } public String shaderTypeStr() { return shaderTypeStr(shaderType); } public static String shaderTypeStr(final int type) { switch (type) { case GL2ES2.GL_VERTEX_SHADER: return "VERTEX_SHADER"; case GL2ES2.GL_FRAGMENT_SHADER: return "FRAGMENT_SHADER"; case GL3ES3.GL_GEOMETRY_SHADER: return "GEOMETRY_SHADER"; case GL3ES3.GL_TESS_CONTROL_SHADER: return "TESS_CONTROL_SHADER"; case GL3ES3.GL_TESS_EVALUATION_SHADER: return "TESS_EVALUATION_SHADER"; case GL3ES3.GL_COMPUTE_SHADER: return "COMPUTE_SHADER"; } return "UNKNOWN_SHADER"; } public int shaderBinaryFormat() { return shaderBinaryFormat; } public Buffer shaderBinary() { return shaderBinary; } public CharSequence[][] shaderSource() { return shaderSource; } public boolean isValid() { return valid; } public IntBuffer shader() { return shader; } public boolean compile(final GL2ES2 gl) { return compile(gl, null); } public boolean compile(final GL2ES2 gl, final PrintStream verboseOut) { if(isValid()) return true; // 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, shaderSource, verboseOut); } else if(null!=shaderBinary) { valid=ShaderUtil.createAndLoadShader(gl, shader, shaderType, shaderBinaryFormat, shaderBinary, verboseOut); } else { throw new GLException("no code (source or binary)"); } return valid; } public void destroy(final GL2ES2 gl) { if(isValid()) { if(null!=gl) { ShaderUtil.deleteShader(gl, shader()); } valid=false; } if(null!=shaderBinary) { shaderBinary.clear(); shaderBinary=null; } shaderSource=null; shaderBinaryFormat=-1; shaderType=-1; id=-1; } @Override public boolean equals(final Object obj) { if(this==obj) { return true; } if(obj instanceof ShaderCode) { return id()==((ShaderCode)obj).id(); } return false; } @Override public int hashCode() { return id; } @Override public String toString() { final StringBuilder buf = new StringBuilder("ShaderCode[id="+id+", type="+shaderTypeStr()+", valid="+valid+", shader: "); for(int i=0; i"); return; } final int sourceCount = shaderSource.length; final int shaderCount = (null!=shader)?shader.capacity():0; for(int i=0; i=sourceCount) { out.println(""); } else { final CharSequence[] src = shaderSource[i]; int lineno=0; for(int j=0; jdata after the line containing tag. *

* Note: The shader source to be edit must be created using a mutable StringBuilder. *

* * @param shaderIdx the shader index to be used. * @param tag search string * @param fromIndex start search tag begininig with this index * @param data the text to be inserted. Shall end with an EOL '\n' character. * @return index after the inserted data * * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of type StringBuilder */ public int insertShaderSource(final int shaderIdx, final String tag, final int fromIndex, final CharSequence data) { if(null==shaderSource) { throw new IllegalStateException("no shader source"); } final int shaderCount = (null!=shader)?shader.capacity():0; if(0>shaderIdx || shaderIdx>=shaderCount) { throw new IndexOutOfBoundsException("shaderIdx not within shader bounds [0.."+(shaderCount-1)+"]: "+shaderIdx); } final int sourceCount = shaderSource.length; 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; jeol) { eol = sb.indexOf("\r", insertIdx); // eol: covers \r 'only' } if(0oldName with newName in all shader sources. *

* In case oldName and newName are equal, no action is performed. *

*

* Note: The shader source to be edit must be created using a mutable StringBuilder. *

* * @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 StringBuilder */ public int replaceInShaderSource(final String oldName, final String newName) { if(null==shaderSource) { throw new IllegalStateException("no shader source"); } if(oldName == newName || oldName.equals(newName)) { return 0; } final int oldNameLen = oldName.length(); final int newNameLen = newName.length(); int num = 0; final int sourceCount = shaderSource.length; for(int shaderIdx = 0; shaderIdxdata at position in shader source for shader shaderIdx. *

* Note: The shader source to be edit must be created using a mutable StringBuilder. *

* * @param shaderIdx the shader index to be used. * @param position in shader source segments of shader shaderIdx, -1 will append data * @param data the text to be inserted. Shall end with an EOL '\n' character * @return index after the inserted data * * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of type StringBuilder */ public int insertShaderSource(final int shaderIdx, int position, final CharSequence data) { if(null==shaderSource) { throw new IllegalStateException("no shader source"); } final int shaderCount = (null!=shader)?shader.capacity():0; if(0>shaderIdx || shaderIdx>=shaderCount) { throw new IndexOutOfBoundsException("shaderIdx not within shader bounds [0.."+(shaderCount-1)+"]: "+shaderIdx); } final int sourceCount = shaderSource.length; 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 position && j == src.length - 1 ) { position = curEndIndex; } if(0 <= position && position <= curEndIndex ) { sb.insert(position, data); return position+data.length(); } } return position; } /** * Adds shader source located in path, * either relative to the context class or absolute as-is * at position in shader source for shader shaderIdx. *

* Final location lookup is performed via {@link ClassLoader#getResource(String)} and {@link ClassLoader#getSystemResource(String)}, * see {@link IOUtil#getResource(Class, String)}. *

*

* Note: The shader source to be edit must be created using a mutable StringBuilder. *

* * @param shaderIdx the shader index to be used. * @param position in shader source segments of shader shaderIdx, -1 will append data * @param context class used to help resolve the source location * @param path location of shader source * @return index after the inserted code. * @throws IOException * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of type StringBuilder * @see IOUtil#getResource(Class, String) */ public int insertShaderSource(final int shaderIdx, final int position, final Class context, final String path) throws IOException { final CharSequence data = readShaderSource(context, path, true); if( null != data ) { return insertShaderSource(shaderIdx, position, data); } else { return position; } } private static int readShaderSource(final Class context, final URLConnection conn, final StringBuilder result, int lineno) throws IOException { if(DEBUG_CODE) { if(0 == lineno) { result.append("// "+conn.getURL().toExternalForm()+"\n"); } else { result.append("// included @ line "+lineno+": "+conn.getURL().toExternalForm()+"\n"); } } final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); try { String line = null; while ((line = reader.readLine()) != null) { lineno++; if (line.startsWith("#include ")) { final String includeFile; { String s = line.substring(9).trim(); // Bug 1283: Remove shader include filename quotes if exists at start and end only if( s.startsWith("\"") && s.endsWith("\"")) { s = s.substring(1, s.length()-1); } includeFile = s; } URLConnection nextConn = null; // Try relative of current shader location final Uri relUri = Uri.valueOf( conn.getURL() ).getRelativeOf(new Uri.Encoded( includeFile, Uri.PATH_LEGAL )); nextConn = IOUtil.openURL(relUri.toURL(), "ShaderCode.relativeOf "); if (nextConn == null) { // Try relative of class and absolute nextConn = IOUtil.getResource(includeFile, context.getClassLoader(), context); } if (nextConn == null) { // Fail throw new FileNotFoundException("Can't find include file " + includeFile); } lineno = readShaderSource(context, nextConn, result, lineno); } else { result.append(line + "\n"); } } } catch (final URISyntaxException e) { throw new IOException(e); } finally { IOUtil.close(reader, false); } return lineno; } /** * Reads shader source located in conn. * * @param context class used to help resolve the source location, may be {@code null} * @param conn the {@link URLConnection} of the shader source * @param result {@link StringBuilder} sink for the resulting shader source code * @throws IOException */ public static void readShaderSource(final Class context, final URLConnection conn, final StringBuilder result) throws IOException { readShaderSource(context, conn, result, 0); } /** * Reads shader source located in path, * either relative to the context class or absolute as-is. *

* Final location lookup is performed via {@link ClassLoader#getResource(String)} and {@link ClassLoader#getSystemResource(String)}, * see {@link IOUtil#getResource(Class, String)}. *

* * @param context class used to help resolve the source location * @param path location of shader source * @param mutableStringBuilder if true method returns a mutable StringBuilder instance * which can be edited later on at the costs of a String conversion when passing to * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. * If false method returns an immutable String instance, * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} * at no additional costs. * @throws IOException * * @see IOUtil#getResource(Class, String) */ public static CharSequence readShaderSource(final Class context, final String path, final boolean mutableStringBuilder) throws IOException { final URLConnection conn = IOUtil.getResource(path, context.getClassLoader(), context); if (conn == null) { return null; } final StringBuilder result = new StringBuilder(); readShaderSource(context, conn, result); return mutableStringBuilder ? result : result.toString(); } /** * Reads shader source located from {@link Uri#absolute} {@link Uri} sourceLocation. * @param sourceLocation {@link Uri} location of shader source * @param mutableStringBuilder if true method returns a mutable StringBuilder instance * which can be edited later on at the costs of a String conversion when passing to * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. * If false method returns an immutable String instance, * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} * at no additional costs. * @throws IOException * @since 2.3.2 */ public static CharSequence readShaderSource(final Uri sourceLocation, final boolean mutableStringBuilder) throws IOException { final URLConnection conn = IOUtil.openURL(sourceLocation.toURL(), "ShaderCode "); if (conn == null) { return null; } final StringBuilder result = new StringBuilder(); readShaderSource(null, conn, result); return mutableStringBuilder ? result : result.toString(); } /** * Reads shader binary located in path, * either relative to the context class or absolute as-is. *

* Final location lookup is perfomed via {@link ClassLoader#getResource(String)} and {@link ClassLoader#getSystemResource(String)}, * see {@link IOUtil#getResource(Class, String)}. *

* * @param context class used to help resolve the source location * @param path location of shader binary * @throws IOException * * @see IOUtil#getResource(Class, String) */ public static ByteBuffer readShaderBinary(final Class context, final String path) throws IOException { final URLConnection conn = IOUtil.getResource(path, context.getClassLoader(), context); if (conn == null) { return null; } final BufferedInputStream bis = new BufferedInputStream( conn.getInputStream() ); try { return IOUtil.copyStream2ByteBuffer( bis ); } finally { IOUtil.close(bis, false); } } /** * Reads shader binary located from {@link Uri#absolute} {@link Uri} binLocation. * @param binLocation {@link Uri} location of shader binary * @throws IOException * @since 2.3.2 */ public static ByteBuffer readShaderBinary(final Uri binLocation) throws IOException { final URLConnection conn = IOUtil.openURL(binLocation.toURL(), "ShaderCode "); if (conn == null) { return null; } final BufferedInputStream bis = new BufferedInputStream( conn.getInputStream() ); try { return IOUtil.copyStream2ByteBuffer( bis ); } finally { IOUtil.close(bis, false); } } // 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}, * same as for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader}, i.e {@link #es3_default_precision_vp}, * due to ES 3.x requirements of using same precision for uniforms! */ public static final String es3_default_precision_fp = es3_default_precision_vp; /** 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"; /** Behavior for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */ public static final String REQUIRE = "require"; /** Behavior for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */ public static final String ENABLE = "enable"; /** Behavior for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */ public static final String DISABLE = "disable"; /** Behavior for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */ public static final String WARN = "warn"; /** * Creates a GLSL extension directive. *

* Prefer {@link #ENABLE} over {@link #REQUIRE}, since the latter will force a failure if not supported. *

* * @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(final String extensionName, final String behavior) { return "#extension " + extensionName + " : " + behavior + "\n"; } /** * Add GLSL version at the head of this shader source code. *

* Note: The shader source to be edit must be created using a mutable StringBuilder. *

* @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(final 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. *

* Note: The shader source to be edit must be created using a mutable StringBuilder. *

* @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(final GL2ES2 gl, int pos) { final String defaultPrecision; 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; case GL3ES3.GL_COMPUTE_SHADER: defaultPrecision = es3_default_precision_fp; break; default: defaultPrecision = null; break; } } else 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( requiresGL3DefaultPrecision(gl) ) { // GLSL [ 1.30 .. 1.50 [ needs at least fragement float default precision! switch ( shaderType ) { case GL2ES2.GL_VERTEX_SHADER: case GL3ES3.GL_GEOMETRY_SHADER: case GL3ES3.GL_TESS_CONTROL_SHADER: case GL3ES3.GL_TESS_EVALUATION_SHADER: defaultPrecision = gl3_default_precision_vp_gp; break; case GL2ES2.GL_FRAGMENT_SHADER: defaultPrecision = gl3_default_precision_fp; break; case GL3ES3.GL_COMPUTE_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(final 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(final GL2ES2 gl) { if( gl.isGL3() ) { final VersionNumber glslVersion = gl.getContext().getGLSLVersionNumber(); return glslVersion.compareTo(GLContext.Version1_30) >= 0 && glslVersion.compareTo(GLContext.Version1_50) < 0 ; } else { return false; } } /** * Default customization of this shader source code. *

* Note: The shader source to be edit must be created using a mutable StringBuilder. *

* @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 true 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(final GL2ES2 gl, final boolean preludeVersion, final 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. *

* Note: The shader source to be edit must be created using a mutable StringBuilder. *

* @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(final GL2ES2 gl, final boolean preludeVersion, final 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 // protected CharSequence[][] shaderSource = null; protected Buffer shaderBinary = null; protected int shaderBinaryFormat = -1; protected IntBuffer shader = null; protected int shaderType = -1; protected int id = -1; protected boolean valid=false; private static synchronized int getNextID() { return nextID++; } protected static int nextID = 1; }