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 ≥ 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. */ 1254 public static final String gl3_default_precision_vp_gp = "\nprecision highp float;\nprecision highp int;\n"; 1255 /** 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. */ 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