1 /**
2  * Copyright 2010 JogAmp Community. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are
5  * permitted provided that the following conditions are met:
6  *
7  *    1. Redistributions of source code must retain the above copyright notice, this list of
8  *       conditions and the following disclaimer.
9  *
10  *    2. Redistributions in binary form must reproduce the above copyright notice, this list
11  *       of conditions and the following disclaimer in the documentation and/or other materials
12  *       provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  * The views and conclusions contained in the software and documentation are those of the
25  * authors and should not be interpreted as representing official policies, either expressed
26  * or implied, of JogAmp Community.
27  */
28 
29 package com.jogamp.opengl.util.glsl;
30 
31 import java.io.BufferedInputStream;
32 import java.io.BufferedReader;
33 import java.io.FileNotFoundException;
34 import java.io.IOException;
35 import java.io.InputStreamReader;
36 import java.io.PrintStream;
37 import java.io.StringReader;
38 import java.net.URISyntaxException;
39 import java.net.URLConnection;
40 import java.nio.Buffer;
41 import java.nio.ByteBuffer;
42 import java.nio.IntBuffer;
43 import java.util.Arrays;
44 import java.util.Iterator;
45 import java.util.Set;
46 
47 import com.jogamp.opengl.GL;
48 import com.jogamp.opengl.GL2ES2;
49 import com.jogamp.opengl.GL3;
50 import com.jogamp.opengl.GL3ES3;
51 import com.jogamp.opengl.GL4;
52 import com.jogamp.opengl.GLES2;
53 import com.jogamp.opengl.GLContext;
54 import com.jogamp.opengl.GLException;
55 
56 import jogamp.opengl.Debug;
57 
58 import com.jogamp.common.net.Uri;
59 import com.jogamp.common.nio.Buffers;
60 import com.jogamp.common.util.IOUtil;
61 import com.jogamp.common.util.VersionNumber;
62 
63 /**
64  * Convenient shader code class to use and instantiate vertex or fragment programs.
65  * <p>
66  * A documented example of how to use this code is available
67  * {@link #create(GL2ES2, int, Class, String, String, String, boolean) here} and
68  * {@link #create(GL2ES2, int, int, Class, String, String[], String, String) here}.
69  * </p>
70  * <p>
71  * Support for {@link GL4#GL_TESS_CONTROL_SHADER} and {@link GL4#GL_TESS_EVALUATION_SHADER}
72  * was added since 2.2.1.
73  * </p>
74  */
75 public class ShaderCode {
76     public static final boolean DEBUG_CODE = Debug.isPropertyDefined("jogl.debug.GLSLCode", true);
77 
78     /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in source code: <code>{@value}</code> */
79     public static final String SUFFIX_VERTEX_SOURCE   =  "vp" ;
80 
81     /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in binary: <code>{@value}</code> */
82     public static final String SUFFIX_VERTEX_BINARY   = "bvp" ;
83 
84     /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in source code: <code>{@value}</code> */
85     public static final String SUFFIX_GEOMETRY_SOURCE =  "gp" ;
86 
87     /** Unique resource suffix for {@link GL3#GL_GEOMETRY_SHADER} in binary: <code>{@value}</code> */
88     public static final String SUFFIX_GEOMETRY_BINARY = "bgp" ;
89 
90     /**
91      * Unique resource suffix for {@link GL3ES3#GL_COMPUTE_SHADER} in source code: <code>{@value}</code>
92      * @since 2.3.2
93      */
94     public static final String SUFFIX_COMPUTE_SOURCE =  "cp" ;
95 
96     /**
97      * Unique resource suffix for {@link GL3ES3#GL_COMPUTE_SHADER} in binary: <code>{@value}</code>
98      * @since 2.3.2
99      */
100     public static final String SUFFIX_COMPUTE_BINARY = "bcp" ;
101 
102     /**
103      * Unique resource suffix for {@link GL4#GL_TESS_CONTROL_SHADER} in source code: <code>{@value}</code>
104      * @since 2.2.1
105      */
106     public static final String SUFFIX_TESS_CONTROL_SOURCE =  "tcp" ;
107 
108     /**
109      * Unique resource suffix for {@link GL4#GL_TESS_CONTROL_SHADER} in binary: <code>{@value}</code>
110      * @since 2.2.1
111      */
112     public static final String SUFFIX_TESS_CONTROL_BINARY = "btcp" ;
113 
114     /**
115      * Unique resource suffix for {@link GL4#GL_TESS_EVALUATION_SHADER} in source code: <code>{@value}</code>
116      * @since 2.2.1
117      */
118     public static final String SUFFIX_TESS_EVALUATION_SOURCE =  "tep" ;
119 
120     /**
121      * Unique resource suffix for {@link GL4#GL_TESS_EVALUATION_SHADER} in binary: <code>{@value}</code>
122      * @since 2.2.1
123      */
124     public static final String SUFFIX_TESS_EVALUATION_BINARY = "btep" ;
125 
126     /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in source code: <code>{@value}</code> */
127     public static final String SUFFIX_FRAGMENT_SOURCE =  "fp" ;
128 
129     /** Unique resource suffix for {@link GL2ES2#GL_FRAGMENT_SHADER} in binary: <code>{@value}</code> */
130     public static final String SUFFIX_FRAGMENT_BINARY = "bfp" ;
131 
132     /** Unique relative path for binary shader resources for {@link GLES2#GL_NVIDIA_PLATFORM_BINARY_NV NVIDIA}: <code>{@value}</code> */
133     public static final String SUB_PATH_NVIDIA = "nvidia" ;
134 
135     /**
136      * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
137      *                    {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
138      * @param count number of shaders
139      * @param source CharSequence array containing the shader sources, organized as <code>source[count][strings-per-shader]</code>.
140      *               May be either an immutable <code>String</code> - or mutable <code>StringBuilder</code> array.
141      *
142      * @throws IllegalArgumentException if <code>count</code> and <code>source.length</code> do not match
143      */
ShaderCode(final int type, final int count, final CharSequence[][] source)144     public ShaderCode(final int type, final int count, final CharSequence[][] source) {
145         if(source.length != count) {
146             throw new IllegalArgumentException("shader number ("+count+") and sourceFiles array ("+source.length+") of different lenght.");
147         }
148         switch (type) {
149             case GL2ES2.GL_VERTEX_SHADER:
150             case GL2ES2.GL_FRAGMENT_SHADER:
151             case GL3.GL_GEOMETRY_SHADER:
152             case GL3.GL_TESS_CONTROL_SHADER:
153             case GL3.GL_TESS_EVALUATION_SHADER:
154             case GL3ES3.GL_COMPUTE_SHADER:
155                 break;
156             default:
157                 throw new GLException("Unknown shader type: "+type);
158         }
159         shaderSource = source;
160         shaderBinaryFormat = -1;
161         shaderBinary = null;
162         shaderType   = type;
163         shader = Buffers.newDirectIntBuffer(count);
164         id = getNextID();
165 
166         if(DEBUG_CODE) {
167             System.out.println("Created: "+toString());
168             // dumpShaderSource(System.out); // already done in readShaderSource
169         }
170     }
171 
172     /**
173      * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
174      *                    {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
175      * @param count number of shaders
176      * @param binary binary buffer containing the shader binaries,
177      */
ShaderCode(final int type, final int count, final int binFormat, final Buffer binary)178     public ShaderCode(final int type, final int count, final int binFormat, final Buffer binary) {
179         switch (type) {
180             case GL2ES2.GL_VERTEX_SHADER:
181             case GL2ES2.GL_FRAGMENT_SHADER:
182             case GL3.GL_GEOMETRY_SHADER:
183             case GL3.GL_TESS_CONTROL_SHADER:
184             case GL3.GL_TESS_EVALUATION_SHADER:
185             case GL3ES3.GL_COMPUTE_SHADER:
186                 break;
187             default:
188                 throw new GLException("Unknown shader type: "+type);
189         }
190         shaderSource = null;
191         shaderBinaryFormat = binFormat;
192         shaderBinary = binary;
193         shaderType   = type;
194         shader = Buffers.newDirectIntBuffer(count);
195         id = getNextID();
196     }
197 
198     /**
199      * Creates a complete {@link ShaderCode} object while reading all shader source of <code>sourceFiles</code>,
200      * which location is resolved using the <code>context</code> class, see {@link #readShaderSource(Class, String)}.
201      *
202      * @param gl current GL object to determine whether a shader compiler is available. If null, no validation is performed.
203      * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
204      *                    {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
205      * @param count number of shaders
206      * @param context class used to help resolving the source location
207      * @param sourceFiles array of source locations, organized as <code>sourceFiles[count]</code> -> <code>shaderSources[count][1]</code>
208      * @param mutableStringBuilder if <code>true</code> method returns a mutable <code>StringBuilder</code> instance
209      *                        which can be edited later on at the costs of a String conversion when passing to
210      *                        {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
211      *                        If <code>false</code> method returns an immutable <code>String</code> instance,
212      *                        which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
213      *                        at no additional costs.
214      *
215      * @throws IllegalArgumentException if <code>count</code> and <code>sourceFiles.length</code> do not match
216      * @see #readShaderSource(Class, String)
217      */
create(final GL2ES2 gl, final int type, final int count, final Class<?> context, final String[] sourceFiles, final boolean mutableStringBuilder)218     public static ShaderCode create(final GL2ES2 gl, final int type, final int count, final Class<?> context,
219                                     final String[] sourceFiles, final boolean mutableStringBuilder) {
220         if(null != gl && !ShaderUtil.isShaderCompilerAvailable(gl)) {
221             return null;
222         }
223 
224         CharSequence[][] shaderSources = null;
225         if(null!=sourceFiles) {
226             // sourceFiles.length and count is validated in ctor
227             shaderSources = new CharSequence[sourceFiles.length][1];
228             for(int i=0; i<sourceFiles.length; i++) {
229                 try {
230                     shaderSources[i][0] = readShaderSource(context, sourceFiles[i], mutableStringBuilder);
231                 } catch (final IOException ioe) {
232                     throw new RuntimeException("readShaderSource("+sourceFiles[i]+") error: ", ioe);
233                 }
234                 if(null == shaderSources[i][0]) {
235                     shaderSources = null;
236                 }
237             }
238         }
239         if(null==shaderSources) {
240             return null;
241         }
242         return new ShaderCode(type, count, shaderSources);
243     }
244 
245     /**
246      * Creates a complete {@link ShaderCode} object while reading all shader sources from {@link Uri} <code>sourceLocations</code>
247      * via {@link #readShaderSource(Uri, boolean)}.
248      *
249      * @param gl current GL object to determine whether a shader compiler is available. If null, no validation is performed.
250      * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
251      *                    {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
252      * @param count number of shaders
253      * @param sourceLocations array of {@link Uri} source locations, organized as <code>sourceFiles[count]</code> -> <code>shaderSources[count][1]</code>
254      * @param mutableStringBuilder if <code>true</code> method returns a mutable <code>StringBuilder</code> instance
255      *                        which can be edited later on at the costs of a String conversion when passing to
256      *                        {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
257      *                        If <code>false</code> method returns an immutable <code>String</code> instance,
258      *                        which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
259      *                        at no additional costs.
260      *
261      * @throws IllegalArgumentException if <code>count</code> and <code>sourceFiles.length</code> do not match
262      * @see #readShaderSource(Uri, boolean)
263      * @since 2.3.2
264      */
create(final GL2ES2 gl, final int type, final int count, final Uri[] sourceLocations, final boolean mutableStringBuilder)265     public static ShaderCode create(final GL2ES2 gl, final int type, final int count,
266                                     final Uri[] sourceLocations, final boolean mutableStringBuilder) {
267         if(null != gl && !ShaderUtil.isShaderCompilerAvailable(gl)) {
268             return null;
269         }
270 
271         CharSequence[][] shaderSources = null;
272         if(null!=sourceLocations) {
273             // sourceFiles.length and count is validated in ctor
274             shaderSources = new CharSequence[sourceLocations.length][1];
275             for(int i=0; i<sourceLocations.length; i++) {
276                 try {
277                     shaderSources[i][0] = readShaderSource(sourceLocations[i], mutableStringBuilder);
278                 } catch (final IOException ioe) {
279                     throw new RuntimeException("readShaderSource("+sourceLocations[i]+") error: ", ioe);
280                 }
281                 if(null == shaderSources[i][0]) {
282                     shaderSources = null;
283                 }
284             }
285         }
286         if(null==shaderSources) {
287             return null;
288         }
289         return new ShaderCode(type, count, shaderSources);
290     }
291 
292     /**
293      * Creates a complete {@link ShaderCode} object while reading the shader binary of <code>binaryFile</code>,
294      * which location is resolved using the <code>context</code> class, see {@link #readShaderBinary(Class, String)}.
295      *
296      * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
297      *                    {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
298      * @param count number of shaders
299      * @param context class used to help resolving the source location
300      * @param binFormat a valid native binary format as they can be queried by {@link ShaderUtil#getShaderBinaryFormats(GL)}.
301      * @param sourceFiles array of source locations, organized as <code>sourceFiles[count]</code>
302      *
303      * @see #readShaderBinary(Class, String)
304      * @see ShaderUtil#getShaderBinaryFormats(GL)
305      */
create(final int type, final int count, final Class<?> context, int binFormat, final String binaryFile)306     public static ShaderCode create(final int type, final int count, final Class<?> context, int binFormat, final String binaryFile) {
307         ByteBuffer shaderBinary = null;
308         if(null!=binaryFile && 0<=binFormat) {
309             try {
310                 shaderBinary = readShaderBinary(context, binaryFile);
311             } catch (final IOException ioe) {
312                 throw new RuntimeException("readShaderBinary("+binaryFile+") error: ", ioe);
313             }
314             if(null == shaderBinary) {
315                 binFormat = -1;
316             }
317         }
318         if(null==shaderBinary) {
319             return null;
320         }
321         return new ShaderCode(type, count, binFormat, shaderBinary);
322     }
323 
324     /**
325      * Creates a complete {@link ShaderCode} object while reading the shader binary from {@link Uri} <code>binLocations</code>
326      * via {@link #readShaderBinary(Uri)}.
327      * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
328      *                    {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
329      * @param count number of shaders
330      * @param binFormat a valid native binary format as they can be queried by {@link ShaderUtil#getShaderBinaryFormats(GL)}.
331      * @param binLocations {@link Uri} binary location
332      *
333      * @see #readShaderBinary(Uri)
334      * @see ShaderUtil#getShaderBinaryFormats(GL)
335      * @since 2.3.2
336      */
create(final int type, final int count, int binFormat, final Uri binLocation)337     public static ShaderCode create(final int type, final int count, int binFormat, final Uri binLocation) {
338         ByteBuffer shaderBinary = null;
339         if(null!=binLocation && 0<=binFormat) {
340             try {
341                 shaderBinary = readShaderBinary(binLocation);
342             } catch (final IOException ioe) {
343                 throw new RuntimeException("readShaderBinary("+binLocation+") error: ", ioe);
344             }
345             if(null == shaderBinary) {
346                 binFormat = -1;
347             }
348         }
349         if(null==shaderBinary) {
350             return null;
351         }
352         return new ShaderCode(type, count, binFormat, shaderBinary);
353     }
354 
355     /**
356      * Returns a unique suffix for shader resources as follows:
357      * <ul>
358      *   <li>Source<ul>
359      *     <li>{@link GL2ES2#GL_VERTEX_SHADER vertex}: {@link #SUFFIX_VERTEX_SOURCE}</li>
360      *     <li>{@link GL2ES2#GL_FRAGMENT_SHADER fragment}: {@link #SUFFIX_FRAGMENT_SOURCE}</li>
361      *     <li>{@link GL3#GL_GEOMETRY_SHADER geometry}: {@link #SUFFIX_GEOMETRY_SOURCE}</li>
362      *     <li>{@link GL4#GL_TESS_CONTROL_SHADER tess-ctrl}: {@link #SUFFIX_TESS_CONTROL_SOURCE}</li>
363      *     <li>{@link GL4#GL_TESS_EVALUATION_SHADER tess-eval}: {@link #SUFFIX_TESS_EVALUATION_SOURCE}</li>
364      *     <li>{@link GL3ES3#GL_COMPUTE_SHADER}: {@link #SUFFIX_COMPUTE_SOURCE}</li>
365      *     </ul></li>
366      *   <li>Binary<ul>
367      *     <li>{@link GL2ES2#GL_VERTEX_SHADER vertex}: {@link #SUFFIX_VERTEX_BINARY}</li>
368      *     <li>{@link GL2ES2#GL_FRAGMENT_SHADER fragment}: {@link #SUFFIX_FRAGMENT_BINARY}</li>
369      *     <li>{@link GL3#GL_GEOMETRY_SHADER geometry}: {@link #SUFFIX_GEOMETRY_BINARY}</li>
370      *     <li>{@link GL4#GL_TESS_CONTROL_SHADER tess-ctrl}: {@link #SUFFIX_TESS_CONTROL_BINARY}</li>
371      *     <li>{@link GL4#GL_TESS_EVALUATION_SHADER tess-eval}: {@link #SUFFIX_TESS_EVALUATION_BINARY}</li>
372      *     <li>{@link GL3ES3#GL_COMPUTE_SHADER}: {@link #SUFFIX_COMPUTE_BINARY}</li>
373      *     </ul></li>
374      * </ul>
375      * @param binary true for a binary resource, false for a source resource
376      * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
377      *                    {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
378      *
379      * @throws GLException if <code>type</code> is not supported
380      *
381      * @see #create(GL2ES2, int, Class, String, String, String, boolean)
382      */
getFileSuffix(final boolean binary, final int type)383     public static String getFileSuffix(final boolean binary, final int type) {
384         switch (type) {
385             case GL2ES2.GL_VERTEX_SHADER:
386                 return binary?SUFFIX_VERTEX_BINARY:SUFFIX_VERTEX_SOURCE;
387             case GL2ES2.GL_FRAGMENT_SHADER:
388                 return binary?SUFFIX_FRAGMENT_BINARY:SUFFIX_FRAGMENT_SOURCE;
389             case GL3.GL_GEOMETRY_SHADER:
390                 return binary?SUFFIX_GEOMETRY_BINARY:SUFFIX_GEOMETRY_SOURCE;
391             case GL3.GL_TESS_CONTROL_SHADER:
392                 return binary?SUFFIX_TESS_CONTROL_BINARY:SUFFIX_TESS_CONTROL_SOURCE;
393             case GL3.GL_TESS_EVALUATION_SHADER:
394                 return binary?SUFFIX_TESS_EVALUATION_BINARY:SUFFIX_TESS_EVALUATION_SOURCE;
395             case GL3ES3.GL_COMPUTE_SHADER:
396                 return binary?SUFFIX_COMPUTE_BINARY:SUFFIX_COMPUTE_SOURCE;
397             default:
398                 throw new GLException("illegal shader type: "+type);
399         }
400     }
401 
402     /**
403      * Returns a unique relative path for binary shader resources as follows:
404      * <ul>
405      *   <li>{@link GLES2#GL_NVIDIA_PLATFORM_BINARY_NV NVIDIA}: {@link #SUB_PATH_NVIDIA}</li>
406      * </ul>
407      *
408      * @throws GLException if <code>binFormat</code> is not supported
409      *
410      * @see #create(GL2ES2, int, Class, String, String, String, boolean)
411      */
getBinarySubPath(final int binFormat)412     public static String getBinarySubPath(final int binFormat) {
413         switch (binFormat) {
414             case GLES2.GL_NVIDIA_PLATFORM_BINARY_NV:
415                 return SUB_PATH_NVIDIA;
416             default:
417                 throw new GLException("unsupported binary format: "+binFormat);
418         }
419     }
420 
421     /**
422      * Convenient creation method for instantiating a complete {@link ShaderCode} object
423      * either from source code using {@link #create(GL2ES2, int, int, Class, String[])},
424      * or from a binary code using {@link #create(int, int, Class, int, String)},
425      * whatever is available first.
426      * <p>
427      * The source and binary location names are expected w/o suffixes which are
428      * resolved and appended using the given {@code srcSuffixOpt} and {@code binSuffixOpt}
429      * if not {@code null}, otherwise {@link #getFileSuffix(boolean, int)} determines the suffixes.
430      * </p>
431      * <p>
432      * Additionally, the binary resource is expected within a subfolder of <code>binRoot</code>
433      * which reflects the vendor specific binary format, see {@link #getBinarySubPath(int)}.
434      * All {@link ShaderUtil#getShaderBinaryFormats(GL)} are being iterated
435      * using the binary subfolder, the first existing resource is being used.
436      * </p>
437      *
438      * Example:
439      * <pre>
440      *   Your std JVM layout (plain or within a JAR):
441      *
442      *      org/test/glsl/MyShaderTest.class
443      *      org/test/glsl/shader/vertex.vp
444      *      org/test/glsl/shader/fragment.fp
445      *      org/test/glsl/shader/bin/nvidia/vertex.bvp
446      *      org/test/glsl/shader/bin/nvidia/fragment.bfp
447      *
448      *   Your Android APK layout:
449      *
450      *      classes.dex
451      *      assets/org/test/glsl/shader/vertex.vp
452      *      assets/org/test/glsl/shader/fragment.fp
453      *      assets/org/test/glsl/shader/bin/nvidia/vertex.bvp
454      *      assets/org/test/glsl/shader/bin/nvidia/fragment.bfp
455      *      ...
456      *
457      *   Your invocation in org/test/glsl/MyShaderTest.java:
458      *
459      *      ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, 1, this.getClass(),
460      *                                         "shader", new String[] { "vertex" }, null,
461      *                                         "shader/bin", "vertex", null, true);
462      *      ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, 1, this.getClass(),
463      *                                         "shader", new String[] { "vertex" }, null,
464      *                                         "shader/bin", "fragment", null, true);
465      *      ShaderProgram sp0 = new ShaderProgram();
466      *      sp0.add(gl, vp0, System.err);
467      *      sp0.add(gl, fp0, System.err);
468      *      st.attachShaderProgram(gl, sp0, true);
469      * </pre>
470      * A simplified entry point is {@link #create(GL2ES2, int, Class, String, String, String, boolean)}.
471      *
472      * <p>
473      * The location is finally being resolved using the <code>context</code> class, see {@link #readShaderBinary(Class, String)}.
474      * </p>
475      *
476      * @param gl current GL object to determine whether a shader compiler is available (if <code>source</code> is used),
477      *           or to determine the shader binary format (if <code>binary</code> is used).
478      * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
479      *                    {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
480      * @param count number of shaders
481      * @param context class used to help resolving the source and binary location
482      * @param srcRoot relative <i>root</i> path for <code>srcBasenames</code> optional
483      * @param srcBasenames basenames w/o path or suffix relative to <code>srcRoot</code> for the shader's source code
484      * @param srcSuffixOpt optional custom suffix for shader's source file,
485      *                     if {@code null} {@link #getFileSuffix(boolean, int)} is being used.
486      * @param binRoot relative <i>root</i> path for <code>binBasenames</code>
487      * @param binBasename basename w/o path or suffix relative to <code>binRoot</code> for the shader's binary code
488      * @param binSuffixOpt optional custom suffix for shader's binary file,
489      *                     if {@code null} {@link #getFileSuffix(boolean, int)} is being used.
490      * @param mutableStringBuilder if <code>true</code> method returns a mutable <code>StringBuilder</code> instance
491      *                        which can be edited later on at the costs of a String conversion when passing to
492      *                        {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
493      *                        If <code>false</code> method returns an immutable <code>String</code> instance,
494      *                        which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
495      *                        at no additional costs.
496      *
497      * @throws IllegalArgumentException if <code>count</code> and <code>srcBasenames.length</code> do not match
498      *
499      * @see #create(GL2ES2, int, int, Class, String[])
500      * @see #create(int, int, Class, int, String)
501      * @see #readShaderSource(Class, String)
502      * @see #getFileSuffix(boolean, int)
503      * @see ShaderUtil#getShaderBinaryFormats(GL)
504      * @see #getBinarySubPath(int)
505      *
506      * @since 2.3.2
507      */
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)508     public static ShaderCode create(final GL2ES2 gl, final int type, final int count, final Class<?> context,
509                                     final String srcRoot, final String[] srcBasenames, final String srcSuffixOpt,
510                                     final String binRoot, final String binBasename, final String binSuffixOpt,
511                                     final boolean mutableStringBuilder) {
512         ShaderCode res = null;
513         final String srcPath[];
514         String srcPathString = null;
515         String binFileName = null;
516 
517         if( null!=srcBasenames && ShaderUtil.isShaderCompilerAvailable(gl) ) {
518             srcPath = new String[srcBasenames.length];
519             final String srcSuffix = null != srcSuffixOpt ? srcSuffixOpt : getFileSuffix(false, type);
520             if( null != srcRoot && srcRoot.length() > 0 ) {
521                 for(int i=0; i<srcPath.length; i++) {
522                     srcPath[i] = srcRoot + '/' + srcBasenames[i] + "." + srcSuffix;
523                 }
524             } else {
525                 for(int i=0; i<srcPath.length; i++) {
526                     srcPath[i] = srcBasenames[i] + "." + srcSuffix;
527                 }
528             }
529             res = create(gl, type, count, context, srcPath, mutableStringBuilder);
530             if(null!=res) {
531                 return res;
532             }
533             srcPathString = Arrays.toString(srcPath);
534         } else {
535             srcPath = null;
536         }
537         if( null!=binBasename ) {
538             final Set<Integer> binFmts = ShaderUtil.getShaderBinaryFormats(gl);
539             final String binSuffix = null != binSuffixOpt ? binSuffixOpt : getFileSuffix(true, type);
540             for(final Iterator<Integer> iter=binFmts.iterator(); iter.hasNext(); ) {
541                 final int bFmt = iter.next().intValue();
542                 final String bFmtPath = getBinarySubPath(bFmt);
543                 if(null==bFmtPath) continue;
544                 binFileName = binRoot + '/' + bFmtPath + '/' + binBasename + "." + binSuffix;
545                 res = create(type, count, context, bFmt, binFileName);
546                 if(null!=res) {
547                     return res;
548                 }
549             }
550         }
551         throw new GLException("No shader code found (source nor binary) for src: "+srcPathString+
552                               ", bin: "+binFileName);
553     }
554 
555     /**
556      * Simplified variation of {@link #create(GL2ES2, int, int, Class, String, String[], String, String, String, String, boolean)}.
557      * <p>
558      * Convenient creation method for instantiating a complete {@link ShaderCode} object
559      * either from source code using {@link #create(GL2ES2, int, int, Class, String[])},
560      * or from a binary code using {@link #create(int, int, Class, int, String)},
561      * whatever is available first.
562      * </p>
563      * <p>
564      * The source and binary location names are expected w/o suffixes which are
565      * resolved and appended using {@link #getFileSuffix(boolean, int)}.
566      * </p>
567      * <p>
568      * Additionally, the binary resource is expected within a subfolder of <code>binRoot</code>
569      * which reflects the vendor specific binary format, see {@link #getBinarySubPath(int)}.
570      * All {@link ShaderUtil#getShaderBinaryFormats(GL)} are being iterated
571      * using the binary subfolder, the first existing resource is being used.
572      * </p>
573      *
574      * Example:
575      * <pre>
576      *   Your std JVM layout (plain or within a JAR):
577      *
578      *      org/test/glsl/MyShaderTest.class
579      *      org/test/glsl/shader/vertex.vp
580      *      org/test/glsl/shader/fragment.fp
581      *      org/test/glsl/shader/bin/nvidia/vertex.bvp
582      *      org/test/glsl/shader/bin/nvidia/fragment.bfp
583      *
584      *   Your Android APK layout:
585      *
586      *      classes.dex
587      *      assets/org/test/glsl/shader/vertex.vp
588      *      assets/org/test/glsl/shader/fragment.fp
589      *      assets/org/test/glsl/shader/bin/nvidia/vertex.bvp
590      *      assets/org/test/glsl/shader/bin/nvidia/fragment.bfp
591      *      ...
592      *
593      *   Your invocation in org/test/glsl/MyShaderTest.java:
594      *
595      *      ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, 1, this.getClass(),
596      *                                         "shader", new String[] { "vertex" }, "shader/bin", "vertex", true);
597      *      ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, 1, this.getClass(),
598      *                                         "shader", new String[] { "vertex" }, "shader/bin", "fragment", true);
599      *      ShaderProgram sp0 = new ShaderProgram();
600      *      sp0.add(gl, vp0, System.err);
601      *      sp0.add(gl, fp0, System.err);
602      *      st.attachShaderProgram(gl, sp0, true);
603      * </pre>
604      * A simplified entry point is {@link #create(GL2ES2, int, Class, String, String, String, boolean)}.
605      *
606      * <p>
607      * The location is finally being resolved using the <code>context</code> class, see {@link #readShaderBinary(Class, String)}.
608      * </p>
609      *
610      * @param gl current GL object to determine whether a shader compiler is available (if <code>source</code> is used),
611      *           or to determine the shader binary format (if <code>binary</code> is used).
612      * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
613      *                    {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
614      * @param count number of shaders
615      * @param context class used to help resolving the source and binary location
616      * @param srcRoot relative <i>root</i> path for <code>srcBasenames</code> optional
617      * @param srcBasenames basenames w/o path or suffix relative to <code>srcRoot</code> for the shader's source code
618      * @param binRoot relative <i>root</i> path for <code>binBasenames</code>
619      * @param binBasename basename w/o path or suffix relative to <code>binRoot</code> for the shader's binary code
620      * @param mutableStringBuilder if <code>true</code> method returns a mutable <code>StringBuilder</code> instance
621      *                        which can be edited later on at the costs of a String conversion when passing to
622      *                        {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
623      *                        If <code>false</code> method returns an immutable <code>String</code> instance,
624      *                        which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
625      *                        at no additional costs.
626      *
627      * @throws IllegalArgumentException if <code>count</code> and <code>srcBasenames.length</code> do not match
628      *
629      * @see #create(GL2ES2, int, int, Class, String, String[], String, String, String, String, boolean)
630      * @see #readShaderSource(Class, String)
631      * @see #getFileSuffix(boolean, int)
632      * @see ShaderUtil#getShaderBinaryFormats(GL)
633      * @see #getBinarySubPath(int)
634      */
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)635     public static ShaderCode create(final GL2ES2 gl, final int type, final int count, final Class<?> context,
636                                     final String srcRoot, final String[] srcBasenames,
637                                     final String binRoot, final String binBasename,
638                                     final boolean mutableStringBuilder) {
639             return create(gl, type, count, context, srcRoot, srcBasenames, null,
640                           binRoot, binBasename, null, mutableStringBuilder);
641     }
642 
643     /**
644      * Simplified variation of {@link #create(GL2ES2, int, int, Class, String, String[], String, String, String, String, boolean)}.
645      * <p>
646      * Example:
647      * <pre>
648      *   Your std JVM layout (plain or within a JAR):
649      *
650      *      org/test/glsl/MyShaderTest.class
651      *      org/test/glsl/shader/vertex.vp
652      *      org/test/glsl/shader/fragment.fp
653      *      org/test/glsl/shader/bin/nvidia/vertex.bvp
654      *      org/test/glsl/shader/bin/nvidia/fragment.bfp
655      *
656      *   Your Android APK layout:
657      *
658      *      classes.dex
659      *      assets/org/test/glsl/shader/vertex.vp
660      *      assets/org/test/glsl/shader/fragment.fp
661      *      assets/org/test/glsl/shader/bin/nvidia/vertex.bvp
662      *      assets/org/test/glsl/shader/bin/nvidia/fragment.bfp
663      *      ...
664      *
665      *   Your invocation in org/test/glsl/MyShaderTest.java:
666      *
667      *      ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, this.getClass(),
668      *                                         "shader", "shader/bin", "vertex", null, null, true);
669      *      ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(),
670      *                                         "shader", "shader/bin", "fragment", null, null, true);
671      *      ShaderProgram sp0 = new ShaderProgram();
672      *      sp0.add(gl, vp0, System.err);
673      *      sp0.add(gl, fp0, System.err);
674      *      st.attachShaderProgram(gl, sp0, true);
675      * </pre>
676      * </p>
677      *
678      * @param gl current GL object to determine whether a shader compiler is available (if <code>source</code> is used),
679      *           or to determine the shader binary format (if <code>binary</code> is used).
680      * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
681      *                    {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
682      * @param context class used to help resolving the source and binary location
683      * @param srcRoot relative <i>root</i> path for <code>basename</code> optional
684      * @param binRoot relative <i>root</i> path for <code>basename</code>
685      * @param basename basename w/o path or suffix relative to <code>srcRoot</code> and <code>binRoot</code>
686      *                 for the shader's source and binary code.
687      * @param srcSuffixOpt optional custom suffix for shader's source file,
688      *                     if {@code null} {@link #getFileSuffix(boolean, int)} is being used.
689      * @param binSuffixOpt optional custom suffix for shader's binary file,
690      *                     if {@code null} {@link #getFileSuffix(boolean, int)} is being used.
691      * @param mutableStringBuilder if <code>true</code> method returns a mutable <code>StringBuilder</code> instance
692      *                        which can be edited later on at the costs of a String conversion when passing to
693      *                        {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
694      *                        If <code>false</code> method returns an immutable <code>String</code> instance,
695      *                        which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
696      *                        at no additional costs.
697      * @throws IllegalArgumentException if <code>count</code> is not 1
698      *
699      * @see #create(GL2ES2, int, int, Class, String, String[], String, String, String, String, boolean)
700      * @since 2.3.2
701      */
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)702     public static ShaderCode create(final GL2ES2 gl, final int type, final Class<?> context,
703                                     final String srcRoot, final String binRoot,
704                                     final String basename, final String srcSuffixOpt, final String binSuffixOpt,
705                                     final boolean mutableStringBuilder) {
706         return create(gl, type, 1, context, srcRoot, new String[] { basename }, srcSuffixOpt,
707                                             binRoot, basename, binSuffixOpt, mutableStringBuilder );
708     }
709 
710     /**
711      * Simplified variation of {@link #create(GL2ES2, int, Class, String, String, String, String, String, boolean)}.
712      * <p>
713      * Example:
714      * <pre>
715      *   Your std JVM layout (plain or within a JAR):
716      *
717      *      org/test/glsl/MyShaderTest.class
718      *      org/test/glsl/shader/vertex.vp
719      *      org/test/glsl/shader/fragment.fp
720      *      org/test/glsl/shader/bin/nvidia/vertex.bvp
721      *      org/test/glsl/shader/bin/nvidia/fragment.bfp
722      *
723      *   Your Android APK layout:
724      *
725      *      classes.dex
726      *      assets/org/test/glsl/shader/vertex.vp
727      *      assets/org/test/glsl/shader/fragment.fp
728      *      assets/org/test/glsl/shader/bin/nvidia/vertex.bvp
729      *      assets/org/test/glsl/shader/bin/nvidia/fragment.bfp
730      *      ...
731      *
732      *   Your invocation in org/test/glsl/MyShaderTest.java:
733      *
734      *      ShaderCode vp0 = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, this.getClass(),
735      *                                         "shader", "shader/bin", "vertex", true);
736      *      ShaderCode fp0 = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(),
737      *                                         "shader", "shader/bin", "fragment", true);
738      *      ShaderProgram sp0 = new ShaderProgram();
739      *      sp0.add(gl, vp0, System.err);
740      *      sp0.add(gl, fp0, System.err);
741      *      st.attachShaderProgram(gl, sp0, true);
742      * </pre>
743      * </p>
744      *
745      * @param gl current GL object to determine whether a shader compiler is available (if <code>source</code> is used),
746      *           or to determine the shader binary format (if <code>binary</code> is used).
747      * @param type either {@link GL2ES2#GL_VERTEX_SHADER}, {@link GL2ES2#GL_FRAGMENT_SHADER}, {@link GL3#GL_GEOMETRY_SHADER},
748      *                    {@link GL4#GL_TESS_CONTROL_SHADER}, {@link GL4#GL_TESS_EVALUATION_SHADER} or {@link GL3ES3#GL_COMPUTE_SHADER}.
749      * @param context class used to help resolving the source and binary location
750      * @param srcRoot relative <i>root</i> path for <code>basename</code> optional
751      * @param binRoot relative <i>root</i> path for <code>basename</code>
752      * @param mutableStringBuilder if <code>true</code> method returns a mutable <code>StringBuilder</code> instance
753      *                        which can be edited later on at the costs of a String conversion when passing to
754      *                        {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
755      *                        If <code>false</code> method returns an immutable <code>String</code> instance,
756      *                        which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
757      *                        at no additional costs.
758      * @param basenames basename w/o path or suffix relative to <code>srcRoot</code> and <code>binRoot</code>
759      *                  for the shader's source and binary code.
760      * @param mutableStringBuilder if <code>true</code> method returns a mutable <code>StringBuilder</code> instance
761      *                        which can be edited later on at the costs of a String conversion when passing to
762      *                        {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
763      *                        If <code>false</code> method returns an immutable <code>String</code> instance,
764      *                        which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
765      *                        at no additional costs.
766      * @throws IllegalArgumentException if <code>count</code> is not 1
767      */
create(final GL2ES2 gl, final int type, final Class<?> context, final String srcRoot, final String binRoot, final String basename, final boolean mutableStringBuilder)768     public static ShaderCode create(final GL2ES2 gl, final int type, final Class<?> context,
769                                     final String srcRoot, final String binRoot, final String basename,
770                                     final boolean mutableStringBuilder) {
771         return ShaderCode.create(gl, type, context, srcRoot, binRoot, basename, null, null, mutableStringBuilder);
772     }
773 
774     /**
775      * returns the uniq shader id as an integer
776      */
id()777     public int id() { return id; }
778 
shaderType()779     public int        shaderType() { return shaderType; }
shaderTypeStr()780     public String     shaderTypeStr() { return shaderTypeStr(shaderType); }
781 
shaderTypeStr(final int type)782     public static String shaderTypeStr(final int type) {
783         switch (type) {
784             case GL2ES2.GL_VERTEX_SHADER:
785                 return "VERTEX_SHADER";
786             case GL2ES2.GL_FRAGMENT_SHADER:
787                 return "FRAGMENT_SHADER";
788             case GL3.GL_GEOMETRY_SHADER:
789                 return "GEOMETRY_SHADER";
790             case GL3.GL_TESS_CONTROL_SHADER:
791                 return "TESS_CONTROL_SHADER";
792             case GL3.GL_TESS_EVALUATION_SHADER:
793                 return "TESS_EVALUATION_SHADER";
794             case GL3ES3.GL_COMPUTE_SHADER:
795             	return "COMPUTE_SHADER";
796         }
797         return "UNKNOWN_SHADER";
798     }
799 
shaderBinaryFormat()800     public int shaderBinaryFormat() { return shaderBinaryFormat; }
shaderBinary()801     public Buffer shaderBinary() { return shaderBinary; }
shaderSource()802     public CharSequence[][] shaderSource() { return shaderSource; }
803 
isValid()804     public boolean    isValid() { return valid; }
805 
shader()806     public IntBuffer  shader() { return shader; }
807 
compile(final GL2ES2 gl)808     public boolean compile(final GL2ES2 gl) {
809         return compile(gl, null);
810     }
compile(final GL2ES2 gl, final PrintStream verboseOut)811     public boolean compile(final GL2ES2 gl, final PrintStream verboseOut) {
812         if(isValid()) return true;
813 
814         // Create & Compile the vertex/fragment shader objects
815         if(null!=shaderSource) {
816             if(DEBUG_CODE) {
817                 System.err.println("ShaderCode.compile:");
818                 dumpShaderSource(System.err);
819             }
820             valid=ShaderUtil.createAndCompileShader(gl, shader, shaderType,
821                                                     shaderSource, verboseOut);
822         } else if(null!=shaderBinary) {
823             valid=ShaderUtil.createAndLoadShader(gl, shader, shaderType,
824                                                  shaderBinaryFormat, shaderBinary, verboseOut);
825         } else {
826             throw new GLException("no code (source or binary)");
827         }
828         return valid;
829     }
830 
destroy(final GL2ES2 gl)831     public void destroy(final GL2ES2 gl) {
832         if(isValid()) {
833             if(null!=gl) {
834                 ShaderUtil.deleteShader(gl, shader());
835             }
836             valid=false;
837         }
838         if(null!=shaderBinary) {
839             shaderBinary.clear();
840             shaderBinary=null;
841         }
842         shaderSource=null;
843         shaderBinaryFormat=-1;
844         shaderType=-1;
845         id=-1;
846     }
847 
848     @Override
equals(final Object obj)849     public boolean equals(final Object obj) {
850         if(this==obj) { return true; }
851         if(obj instanceof ShaderCode) {
852             return id()==((ShaderCode)obj).id();
853         }
854         return false;
855     }
856     @Override
hashCode()857     public int hashCode() {
858         return id;
859     }
860     @Override
toString()861     public String toString() {
862         final StringBuilder buf = new StringBuilder("ShaderCode[id="+id+", type="+shaderTypeStr()+", valid="+valid+", shader: ");
863         for(int i=0; i<shader.remaining(); i++) {
864             buf.append(" "+shader.get(i));
865         }
866         if(null!=shaderSource) {
867             buf.append(", source]");
868         } else if(null!=shaderBinary) {
869             buf.append(", binary "+shaderBinary+"]");
870         }
871         return buf.toString();
872     }
873 
dumpShaderSource(final PrintStream out)874     public void dumpShaderSource(final PrintStream out) {
875         if(null==shaderSource) {
876             out.println("<no shader source>");
877             return;
878         }
879         final int sourceCount = shaderSource.length;
880         final int shaderCount = (null!=shader)?shader.capacity():0;
881         for(int i=0; i<shaderCount; i++) {
882             out.println("");
883             out.println("Shader #"+i+"/"+shaderCount+" name "+shader.get(i));
884             out.println("--------------------------------------------------------------");
885             if(i>=sourceCount) {
886                 out.println("<no shader source>");
887             } else {
888                 final CharSequence[] src = shaderSource[i];
889                 int lineno=0;
890 
891                 for(int j=0; j<src.length; j++) {
892                     out.printf("%4d: // Segment %d/%d: \n", lineno, j, src.length);
893                     final BufferedReader reader = new BufferedReader(new StringReader(src[j].toString()));
894                     String line = null;
895                     try {
896                         while ((line = reader.readLine()) != null) {
897                             lineno++;
898                             out.printf("%4d: %s\n", lineno, line);
899                         }
900                     } catch (final IOException e) { /* impossible .. StringReader */ }
901                 }
902             }
903             out.println("--------------------------------------------------------------");
904         }
905     }
906 
907     /**
908      * Adds <code>data</code> after the line containing <code>tag</code>.
909      * <p>
910      * Note: The shader source to be edit must be created using a mutable StringBuilder.
911      * </p>
912      *
913      * @param shaderIdx the shader index to be used.
914      * @param tag search string
915      * @param fromIndex start search <code>tag</code> begininig with this index
916      * @param data the text to be inserted. Shall end with an EOL '\n' character.
917      * @return index after the inserted <code>data</code>
918      *
919      * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of type <code>StringBuilder</code>
920      */
insertShaderSource(final int shaderIdx, final String tag, final int fromIndex, final CharSequence data)921     public int insertShaderSource(final int shaderIdx, final String tag, final int fromIndex, final CharSequence data) {
922         if(null==shaderSource) {
923             throw new IllegalStateException("no shader source");
924         }
925         final int shaderCount = (null!=shader)?shader.capacity():0;
926         if(0>shaderIdx || shaderIdx>=shaderCount) {
927             throw new IndexOutOfBoundsException("shaderIdx not within shader bounds [0.."+(shaderCount-1)+"]: "+shaderIdx);
928         }
929         final int sourceCount = shaderSource.length;
930         if(shaderIdx>=sourceCount) {
931             throw new IndexOutOfBoundsException("shaderIdx not within source bounds [0.."+(sourceCount-1)+"]: "+shaderIdx);
932         }
933         final CharSequence[] src = shaderSource[shaderIdx];
934         int curEndIndex = 0;
935         for(int j=0; j<src.length; j++) {
936             if( !(src[j] instanceof StringBuilder) ) {
937                 throw new IllegalStateException("shader source not a mutable StringBuilder, but CharSequence of type: "+src[j].getClass().getName());
938             }
939             final StringBuilder sb = (StringBuilder)src[j];
940             curEndIndex += sb.length();
941             if(fromIndex < curEndIndex) {
942                 int insertIdx = sb.indexOf(tag, fromIndex);
943                 if(0<=insertIdx) {
944                     insertIdx += tag.length();
945                     int eol = sb.indexOf("\n", insertIdx); // eol: covers \n and \r\n
946                     if(0>eol) {
947                         eol = sb.indexOf("\r", insertIdx); // eol: covers \r 'only'
948                     }
949                     if(0<eol) {
950                         insertIdx = eol+1;  // eol found
951                     } else {
952                         sb.insert(insertIdx, "\n"); // add eol
953                         insertIdx++;
954                     }
955                     sb.insert(insertIdx, data);
956                     return insertIdx+data.length();
957                 }
958             }
959         }
960         return -1;
961     }
962 
963     /**
964      * Replaces <code>oldName</code> with <code>newName</code> in all shader sources.
965      * <p>
966      * In case <code>oldName</code> and <code>newName</code> are equal, no action is performed.
967      * </p>
968      * <p>
969      * Note: The shader source to be edit must be created using a mutable StringBuilder.
970      * </p>
971      *
972      * @param oldName the to be replace string
973      * @param newName the replacement string
974      * @return the number of replacements
975      *
976      * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of type <code>StringBuilder</code>
977      */
replaceInShaderSource(final String oldName, final String newName)978     public int replaceInShaderSource(final String oldName, final String newName) {
979         if(null==shaderSource) {
980             throw new IllegalStateException("no shader source");
981         }
982         if(oldName == newName || oldName.equals(newName)) {
983             return 0;
984         }
985         final int oldNameLen = oldName.length();
986         final int newNameLen = newName.length();
987         int num = 0;
988         final int sourceCount = shaderSource.length;
989         for(int shaderIdx = 0; shaderIdx<sourceCount; shaderIdx++) {
990             final CharSequence[] src = shaderSource[shaderIdx];
991             for(int j=0; j<src.length; j++) {
992                 if( !(src[j] instanceof StringBuilder) ) {
993                     throw new IllegalStateException("shader source not a mutable StringBuilder, but CharSequence of type: "+src[j].getClass().getName());
994                 }
995                 final StringBuilder sb = (StringBuilder)src[j];
996                 int curPos = 0;
997                 while(curPos<sb.length()-oldNameLen+1) {
998                     final int startIdx = sb.indexOf(oldName, curPos);
999                     if(0<=startIdx) {
1000                         final int endIdx = startIdx + oldNameLen;
1001                         sb.replace(startIdx, endIdx, newName);
1002                         curPos = startIdx + newNameLen;
1003                         num++;
1004                     } else {
1005                         curPos = sb.length();
1006                     }
1007                 }
1008             }
1009         }
1010         return num;
1011     }
1012 
1013     /**
1014      * Adds <code>data</code> at <code>position</code> in shader source for shader <code>shaderIdx</code>.
1015      * <p>
1016      * Note: The shader source to be edit must be created using a mutable StringBuilder.
1017      * </p>
1018      *
1019      * @param shaderIdx the shader index to be used.
1020      * @param position in shader source segments of shader <code>shaderIdx</code>, -1 will append data
1021      * @param data the text to be inserted. Shall end with an EOL '\n' character
1022      * @return index after the inserted <code>data</code>
1023      *
1024      * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of type <code>StringBuilder</code>
1025      */
insertShaderSource(final int shaderIdx, int position, final CharSequence data)1026     public int insertShaderSource(final int shaderIdx, int position, final CharSequence data) {
1027         if(null==shaderSource) {
1028             throw new IllegalStateException("no shader source");
1029         }
1030         final int shaderCount = (null!=shader)?shader.capacity():0;
1031         if(0>shaderIdx || shaderIdx>=shaderCount) {
1032             throw new IndexOutOfBoundsException("shaderIdx not within shader bounds [0.."+(shaderCount-1)+"]: "+shaderIdx);
1033         }
1034         final int sourceCount = shaderSource.length;
1035         if(shaderIdx>=sourceCount) {
1036             throw new IndexOutOfBoundsException("shaderIdx not within source bounds [0.."+(sourceCount-1)+"]: "+shaderIdx);
1037         }
1038         final CharSequence[] src = shaderSource[shaderIdx];
1039         int curEndIndex = 0;
1040         for(int j=0; j<src.length; j++) {
1041             if( !(src[j] instanceof StringBuilder) ) {
1042                 throw new IllegalStateException("shader source not a mutable StringBuilder, but CharSequence of type: "+src[j].getClass().getName());
1043             }
1044             final StringBuilder sb = (StringBuilder)src[j];
1045             curEndIndex += sb.length();
1046             if( 0 > position && j == src.length - 1 ) {
1047                 position = curEndIndex;
1048             }
1049             if(0 <= position && position <= curEndIndex ) {
1050                 sb.insert(position, data);
1051                 return position+data.length();
1052             }
1053         }
1054         return position;
1055     }
1056 
1057     /**
1058      * Adds shader source located in <code>path</code>,
1059      * either relative to the <code>context</code> class or absolute <i>as-is</i>
1060      * at <code>position</code> in shader source for shader <code>shaderIdx</code>.
1061      * <p>
1062      * Final location lookup is performed via {@link ClassLoader#getResource(String)} and {@link ClassLoader#getSystemResource(String)},
1063      * see {@link IOUtil#getResource(Class, String)}.
1064      * </p>
1065      * <p>
1066      * Note: The shader source to be edit must be created using a mutable StringBuilder.
1067      * </p>
1068      *
1069      * @param shaderIdx the shader index to be used.
1070      * @param position in shader source segments of shader <code>shaderIdx</code>, -1 will append data
1071      * @param context class used to help resolve the source location
1072      * @param path location of shader source
1073      * @return index after the inserted code.
1074      * @throws IOException
1075      * @throws IllegalStateException if the shader source's CharSequence is immutable, i.e. not of type <code>StringBuilder</code>
1076      * @see IOUtil#getResource(Class, String)
1077      */
insertShaderSource(final int shaderIdx, final int position, final Class<?> context, final String path)1078     public int insertShaderSource(final int shaderIdx, final int position, final Class<?> context, final String path) throws IOException {
1079         final CharSequence data = readShaderSource(context, path, true);
1080         if( null != data ) {
1081             return insertShaderSource(shaderIdx, position, data);
1082         } else {
1083             return position;
1084         }
1085     }
1086 
readShaderSource(final Class<?> context, final URLConnection conn, final StringBuilder result, int lineno)1087     private static int readShaderSource(final Class<?> context, final URLConnection conn, final StringBuilder result, int lineno) throws IOException  {
1088         if(DEBUG_CODE) {
1089             if(0 == lineno) {
1090                 result.append("// "+conn.getURL().toExternalForm()+"\n");
1091             } else {
1092                 result.append("// included @ line "+lineno+": "+conn.getURL().toExternalForm()+"\n");
1093             }
1094         }
1095         final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
1096         try {
1097             String line = null;
1098             while ((line = reader.readLine()) != null) {
1099                 lineno++;
1100                 if (line.startsWith("#include ")) {
1101                     final String includeFile = line.substring(9).trim();
1102                     URLConnection nextConn = null;
1103 
1104                     // Try relative of current shader location
1105                     final Uri relUri = Uri.valueOf( conn.getURL() ).getRelativeOf(new Uri.Encoded( includeFile, Uri.PATH_LEGAL ));
1106                     nextConn = IOUtil.openURL(relUri.toURL(), "ShaderCode.relativeOf ");
1107                     if (nextConn == null) {
1108                         // Try relative of class and absolute
1109                         nextConn = IOUtil.getResource(includeFile, context.getClassLoader(), context);
1110                     }
1111                     if (nextConn == null) {
1112                         // Fail
1113                         throw new FileNotFoundException("Can't find include file " + includeFile);
1114                     }
1115                     lineno = readShaderSource(context, nextConn, result, lineno);
1116                 } else {
1117                     result.append(line + "\n");
1118                 }
1119             }
1120         } catch (final URISyntaxException e) {
1121             throw new IOException(e);
1122         } finally {
1123             IOUtil.close(reader, false);
1124         }
1125         return lineno;
1126     }
1127 
1128     /**
1129      * Reads shader source located in <code>conn</code>.
1130      *
1131      * @param context class used to help resolve the source location, may be {@code null}
1132      * @param conn the {@link URLConnection} of the shader source
1133      * @param result {@link StringBuilder} sink for the resulting shader source code
1134      * @throws IOException
1135      */
readShaderSource(final Class<?> context, final URLConnection conn, final StringBuilder result)1136     public static void readShaderSource(final Class<?> context, final URLConnection conn, final StringBuilder result) throws IOException {
1137         readShaderSource(context, conn, result, 0);
1138     }
1139 
1140     /**
1141      * Reads shader source located in <code>path</code>,
1142      * either relative to the <code>context</code> class or absolute <i>as-is</i>.
1143      * <p>
1144      * Final location lookup is performed via {@link ClassLoader#getResource(String)} and {@link ClassLoader#getSystemResource(String)},
1145      * see {@link IOUtil#getResource(Class, String)}.
1146      * </p>
1147      *
1148      * @param context class used to help resolve the source location
1149      * @param path location of shader source
1150      * @param mutableStringBuilder if <code>true</code> method returns a mutable <code>StringBuilder</code> instance
1151      *                        which can be edited later on at the costs of a String conversion when passing to
1152      *                        {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
1153      *                        If <code>false</code> method returns an immutable <code>String</code> instance,
1154      *                        which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
1155      *                        at no additional costs.
1156      * @throws IOException
1157      *
1158      * @see IOUtil#getResource(Class, String)
1159      */
readShaderSource(final Class<?> context, final String path, final boolean mutableStringBuilder)1160     public static CharSequence readShaderSource(final Class<?> context, final String path, final boolean mutableStringBuilder) throws IOException {
1161         final URLConnection conn = IOUtil.getResource(path, context.getClassLoader(), context);
1162         if (conn == null) {
1163             return null;
1164         }
1165         final StringBuilder result = new StringBuilder();
1166         readShaderSource(context, conn, result);
1167         return mutableStringBuilder ? result : result.toString();
1168     }
1169 
1170     /**
1171      * Reads shader source located from {@link Uri#absolute} {@link Uri} <code>sourceLocation</code>.
1172      * @param sourceLocation {@link Uri} location of shader source
1173      * @param mutableStringBuilder if <code>true</code> method returns a mutable <code>StringBuilder</code> instance
1174      *                        which can be edited later on at the costs of a String conversion when passing to
1175      *                        {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}.
1176      *                        If <code>false</code> method returns an immutable <code>String</code> instance,
1177      *                        which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}
1178      *                        at no additional costs.
1179      * @throws IOException
1180      * @since 2.3.2
1181      */
readShaderSource(final Uri sourceLocation, final boolean mutableStringBuilder)1182     public static CharSequence readShaderSource(final Uri sourceLocation, final boolean mutableStringBuilder) throws IOException {
1183         final URLConnection conn = IOUtil.openURL(sourceLocation.toURL(), "ShaderCode ");
1184         if (conn == null) {
1185             return null;
1186         }
1187         final StringBuilder result = new StringBuilder();
1188         readShaderSource(null, conn, result);
1189         return mutableStringBuilder ? result : result.toString();
1190     }
1191 
1192     /**
1193      * Reads shader binary located in <code>path</code>,
1194      * either relative to the <code>context</code> class or absolute <i>as-is</i>.
1195      * <p>
1196      * Final location lookup is perfomed via {@link ClassLoader#getResource(String)} and {@link ClassLoader#getSystemResource(String)},
1197      * see {@link IOUtil#getResource(Class, String)}.
1198      * </p>
1199      *
1200      * @param context class used to help resolve the source location
1201      * @param path location of shader binary
1202      * @throws IOException
1203      *
1204      * @see IOUtil#getResource(Class, String)
1205      */
readShaderBinary(final Class<?> context, final String path)1206     public static ByteBuffer readShaderBinary(final Class<?> context, final String path) throws IOException {
1207         final URLConnection conn = IOUtil.getResource(path, context.getClassLoader(), context);
1208         if (conn == null) {
1209             return null;
1210         }
1211         final BufferedInputStream bis = new BufferedInputStream( conn.getInputStream() );
1212         try {
1213             return IOUtil.copyStream2ByteBuffer( bis );
1214         } finally {
1215             IOUtil.close(bis, false);
1216         }
1217     }
1218 
1219     /**
1220      * Reads shader binary located from {@link Uri#absolute} {@link Uri} <code>binLocation</code>.
1221      * @param binLocation {@link Uri} location of shader binary
1222      * @throws IOException
1223      * @since 2.3.2
1224      */
readShaderBinary(final Uri binLocation)1225     public static ByteBuffer readShaderBinary(final Uri binLocation) throws IOException {
1226         final URLConnection conn = IOUtil.openURL(binLocation.toURL(), "ShaderCode ");
1227         if (conn == null) {
1228             return null;
1229         }
1230         final BufferedInputStream bis = new BufferedInputStream( conn.getInputStream() );
1231         try {
1232             return IOUtil.copyStream2ByteBuffer( bis );
1233         } finally {
1234             IOUtil.close(bis, false);
1235         }
1236     }
1237 
1238     // Shall we use: #ifdef GL_FRAGMENT_PRECISION_HIGH .. #endif for using highp in fragment shader if avail ?
1239     /** Default precision of {@link GL#isGLES2() ES2} for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader}: {@value #es2_default_precision_vp} */
1240     public static final String es2_default_precision_vp = "\nprecision highp float;\nprecision highp int;\n/*precision lowp sampler2D;*/\n/*precision lowp samplerCube;*/\n";
1241     /** Default precision of {@link GL#isGLES2() ES2} for {@link GL2ES2#GL_FRAGMENT_SHADER fragment-shader}: {@value #es2_default_precision_fp} */
1242     public static final String es2_default_precision_fp = "\nprecision mediump float;\nprecision mediump int;\n/*precision lowp sampler2D;*/\n/*precision lowp samplerCube;*/\n";
1243 
1244     /** Default precision of {@link GL#isGLES3() ES3} for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader}: {@value #es3_default_precision_vp} */
1245     public static final String es3_default_precision_vp = es2_default_precision_vp;
1246     /**
1247      * Default precision of {@link GL#isGLES3() ES3} for {@link GL2ES2#GL_FRAGMENT_SHADER fragment-shader}: {@value #es3_default_precision_fp},
1248      * same as for {@link GL2ES2#GL_VERTEX_SHADER vertex-shader}, i.e {@link #es3_default_precision_vp},
1249      * due to ES 3.x requirements of using same precision for uniforms!
1250      */
1251     public static final String es3_default_precision_fp = es3_default_precision_vp;
1252 
1253     /** Default precision of GLSL &ge; 1.30 as required until &lt; 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. */
1254     public static final String gl3_default_precision_vp_gp = "\nprecision highp float;\nprecision highp int;\n";
1255     /** Default precision of GLSL &ge; 1.30 as required until &lt; 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. */
1256     public static final String gl3_default_precision_fp = "\nprecision highp float;\nprecision mediump int;\n/*precision mediump sampler2D;*/\n";
1257 
1258     /** <i>Behavior</i> for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */
1259     public static final String REQUIRE = "require";
1260     /** <i>Behavior</i> for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */
1261     public static final String ENABLE = "enable";
1262     /** <i>Behavior</i> for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */
1263     public static final String DISABLE = "disable";
1264     /** <i>Behavior</i> for GLSL extension directive, see {@link #createExtensionDirective(String, String)}, value {@value}. */
1265     public static final String WARN = "warn";
1266 
1267     /**
1268      * Creates a GLSL extension directive.
1269      * <p>
1270      * Prefer {@link #ENABLE} over {@link #REQUIRE}, since the latter will force a failure if not supported.
1271      * </p>
1272      *
1273      * @param extensionName
1274      * @param behavior shall be either {@link #REQUIRE}, {@link #ENABLE}, {@link #DISABLE} or {@link #WARN}
1275      * @return the complete extension directive
1276      */
createExtensionDirective(final String extensionName, final String behavior)1277     public static String createExtensionDirective(final String extensionName, final String behavior) {
1278         return "#extension " + extensionName + " : " + behavior + "\n";
1279     }
1280 
1281     /**
1282      * Add GLSL version at the head of this shader source code.
1283      * <p>
1284      * Note: The shader source to be edit must be created using a mutable StringBuilder.
1285      * </p>
1286      * @param gl a GL context, which must have been made current once
1287      * @return the index after the inserted data, maybe 0 if nothing has be inserted.
1288      */
addGLSLVersion(final GL2ES2 gl)1289     public final int addGLSLVersion(final GL2ES2 gl) {
1290         return insertShaderSource(0, 0, gl.getContext().getGLSLVersionString());
1291     }
1292 
1293     /**
1294      * Adds default precision to source code at given position if required, i.e.
1295      * {@link #es2_default_precision_vp}, {@link #es2_default_precision_fp},
1296      * {@link #gl3_default_precision_vp_gp}, {@link #gl3_default_precision_fp} or none,
1297      * depending on the {@link GLContext#getGLSLVersionNumber() GLSL version} being used.
1298      * <p>
1299      * Note: The shader source to be edit must be created using a mutable StringBuilder.
1300      * </p>
1301      * @param gl a GL context, which must have been made current once
1302      * @param pos position within this mutable shader source.
1303      * @return the index after the inserted data, maybe 0 if nothing has be inserted.
1304      */
addDefaultShaderPrecision(final GL2ES2 gl, int pos)1305     public final int addDefaultShaderPrecision(final GL2ES2 gl, int pos) {
1306         final String defaultPrecision;
1307         if( gl.isGLES3() ) {
1308             switch ( shaderType ) {
1309                 case GL2ES2.GL_VERTEX_SHADER:
1310                     defaultPrecision = es3_default_precision_vp; break;
1311                 case GL2ES2.GL_FRAGMENT_SHADER:
1312                     defaultPrecision = es3_default_precision_fp; break;
1313                 case GL3ES3.GL_COMPUTE_SHADER:
1314                 	defaultPrecision = es3_default_precision_fp; break;
1315                 default:
1316                     defaultPrecision = null;
1317                     break;
1318             }
1319         } else if( gl.isGLES2() ) {
1320             switch ( shaderType ) {
1321                 case GL2ES2.GL_VERTEX_SHADER:
1322                     defaultPrecision = es2_default_precision_vp; break;
1323                 case GL2ES2.GL_FRAGMENT_SHADER:
1324                     defaultPrecision = es2_default_precision_fp; break;
1325                 default:
1326                     defaultPrecision = null;
1327                     break;
1328             }
1329         } else if( requiresGL3DefaultPrecision(gl) ) {
1330             // GLSL [ 1.30 .. 1.50 [ needs at least fragement float default precision!
1331             switch ( shaderType ) {
1332                 case GL2ES2.GL_VERTEX_SHADER:
1333                 case GL3.GL_GEOMETRY_SHADER:
1334                 case GL3.GL_TESS_CONTROL_SHADER:
1335                 case GL3.GL_TESS_EVALUATION_SHADER:
1336                     defaultPrecision = gl3_default_precision_vp_gp; break;
1337                 case GL2ES2.GL_FRAGMENT_SHADER:
1338                     defaultPrecision = gl3_default_precision_fp; break;
1339                 case GL3ES3.GL_COMPUTE_SHADER:
1340                 	defaultPrecision = gl3_default_precision_fp; break;
1341                 default:
1342                     defaultPrecision = null;
1343                     break;
1344             }
1345         } else {
1346             defaultPrecision = null;
1347         }
1348         if( null != defaultPrecision ) {
1349             pos = insertShaderSource(0, pos, defaultPrecision);
1350         }
1351         return pos;
1352     }
1353 
1354     /** Returns true, if GLSL version requires default precision, i.e. ES2 or GLSL [1.30 .. 1.50[. */
requiresDefaultPrecision(final GL2ES2 gl)1355     public static final boolean requiresDefaultPrecision(final GL2ES2 gl) {
1356         if( gl.isGLES() ) {
1357             return true;
1358         }
1359         return requiresGL3DefaultPrecision(gl);
1360     }
1361 
1362     /** Returns true, if GL3 GLSL version requires default precision, i.e. GLSL [1.30 .. 1.50[. */
requiresGL3DefaultPrecision(final GL2ES2 gl)1363     public static final boolean requiresGL3DefaultPrecision(final GL2ES2 gl) {
1364         if( gl.isGL3() ) {
1365             final VersionNumber glslVersion = gl.getContext().getGLSLVersionNumber();
1366             return glslVersion.compareTo(GLContext.Version1_30) >= 0 && glslVersion.compareTo(GLContext.Version1_50) < 0 ;
1367         } else {
1368             return false;
1369         }
1370     }
1371 
1372     /**
1373      * Default customization of this shader source code.
1374      * <p>
1375      * Note: The shader source to be edit must be created using a mutable StringBuilder.
1376      * </p>
1377      * @param gl a GL context, which must have been made current once
1378      * @param preludeVersion if true {@link GLContext#getGLSLVersionString()} is preluded, otherwise not.
1379      * @param addDefaultPrecision if <code>true</code> default precision source code line(s) are added, i.e.
1380      *                            {@link #es2_default_precision_vp}, {@link #es2_default_precision_fp},
1381      *                            {@link #gl3_default_precision_vp_gp}, {@link #gl3_default_precision_fp} or none,
1382      *                            depending on the {@link GLContext#getGLSLVersionNumber() GLSL version} being used.
1383      * @return the index after the inserted data, maybe 0 if nothing has be inserted.
1384      * @see #addGLSLVersion(GL2ES2)
1385      * @see #addDefaultShaderPrecision(GL2ES2, int)
1386      */
defaultShaderCustomization(final GL2ES2 gl, final boolean preludeVersion, final boolean addDefaultPrecision)1387     public final int defaultShaderCustomization(final GL2ES2 gl, final boolean preludeVersion, final boolean addDefaultPrecision) {
1388         int pos;
1389         if( preludeVersion ) {
1390             pos = addGLSLVersion(gl);
1391         } else {
1392             pos = 0;
1393         }
1394         if( addDefaultPrecision ) {
1395             pos = addDefaultShaderPrecision(gl, pos);
1396         }
1397         return pos;
1398     }
1399 
1400     /**
1401      * Default customization of this shader source code.
1402      * <p>
1403      * Note: The shader source to be edit must be created using a mutable StringBuilder.
1404      * </p>
1405      * @param gl a GL context, which must have been made current once
1406      * @param preludeVersion if true {@link GLContext#getGLSLVersionString()} is preluded, otherwise not.
1407      * @param esDefaultPrecision optional default precision source code line(s) preluded if not null and if {@link GL#isGLES()}.
1408      *        You may use {@link #es2_default_precision_fp} for fragment shader and {@link #es2_default_precision_vp} for vertex shader.
1409      * @return the index after the inserted data, maybe 0 if nothing has be inserted.
1410      * @see #addGLSLVersion(GL2ES2)
1411      * @see #addDefaultShaderPrecision(GL2ES2, int)
1412      */
defaultShaderCustomization(final GL2ES2 gl, final boolean preludeVersion, final String esDefaultPrecision)1413     public final int defaultShaderCustomization(final GL2ES2 gl, final boolean preludeVersion, final String esDefaultPrecision) {
1414         int pos;
1415         if( preludeVersion ) {
1416             pos = addGLSLVersion(gl);
1417         } else {
1418             pos = 0;
1419         }
1420         if( gl.isGLES() && null != esDefaultPrecision ) {
1421             pos = insertShaderSource(0, pos, esDefaultPrecision);
1422         } else {
1423             pos = addDefaultShaderPrecision(gl, pos);
1424         }
1425         return pos;
1426     }
1427 
1428     //----------------------------------------------------------------------
1429     // Internals only below this point
1430     //
1431 
1432     protected CharSequence[][] shaderSource = null;
1433     protected Buffer     shaderBinary = null;
1434     protected int        shaderBinaryFormat = -1;
1435     protected IntBuffer  shader = null;
1436     protected int        shaderType = -1;
1437     protected int        id = -1;
1438 
1439     protected boolean valid=false;
1440 
getNextID()1441     private static synchronized int getNextID() {
1442         return nextID++;
1443     }
1444     protected static int nextID = 1;
1445 }
1446 
1447