From 62e5686fb583ad991d5811baf242d40d21952e27 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Mon, 16 Apr 2012 01:38:49 +0200 Subject: API Change ShaderCode/ShaderUtil: Enable optional mutable shader source / generalize shader source storage type to CharSequence[] Benefits: - Allows code injection and general shader source editing (before compilation) - Uses mutable StringBuilder only if editing is intended, hence reduces memory footprint and String conversion at compilation in such case. - ShaderCode.create(..) factory methods add nw attribute 'mutableStringBuilder' if true method returns a mutable StringBuilder instance which can be edited later on at the costs of a String conversion when passing to 'glShaderSource(int, int, String[], IntBuffer)'. If false method returns an immutable String instance, which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} at no additional costs. - New 'edit' methods in ShaderCode: ' - int insertShaderSource(int shaderIdx, String tag, int fromIndex, CharSequence data); - int insertShaderSource(int shaderIdx, int position, CharSequence data); --- .../com/jogamp/opengl/util/glsl/ShaderCode.java | 253 ++++++++++++++++----- .../com/jogamp/opengl/util/glsl/ShaderUtil.java | 24 +- 2 files changed, 214 insertions(+), 63 deletions(-) (limited to 'src/jogl/classes/com/jogamp') diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java index 556345ee5..e347b4171 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java @@ -28,23 +28,36 @@ package com.jogamp.opengl.util.glsl; -import com.jogamp.common.nio.Buffers; -import com.jogamp.common.util.IOUtil; - -import javax.media.opengl.*; +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.StringReader; +import java.net.URLConnection; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Set; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLES2; +import javax.media.opengl.GLException; import jogamp.opengl.Debug; -import java.util.*; -import java.nio.*; -import java.io.*; -import java.net.*; +import com.jogamp.common.nio.Buffers; +import com.jogamp.common.util.IOUtil; /** * Convenient shader code class to use and instantiate vertex or fragment programs. *

* A documented example of how to use this code is available - * {@link #create(GL2ES2, int, int, Class, String, String, String) here} and + * {@link #create(GL2ES2, int, Class, String, String, String, boolean) here} and * {@link #create(GL2ES2, int, int, Class, String, String[], String, String) here}. *

*/ @@ -70,11 +83,12 @@ public class ShaderCode { /** * @param type either {@link GL2ES2#GL_VERTEX_SHADER} or {@link GL2ES2#GL_FRAGMENT_SHADER} * @param count number of shaders - * @param source string array containing the shader sources, organized as source[count][strings-per-shader] + * @param source CharSequence array containing the shader sources, organized as source[count][strings-per-shader]. + * May be either an immutable String - or mutable StringBuilder array. * * @throws IllegalArgumentException if count and source.length do not match */ - public ShaderCode(int type, int count, String[][] source) { + public ShaderCode(int type, int count, CharSequence[][] source) { if(source.length != count) { throw new IllegalArgumentException("shader number ("+count+") and sourceFiles array ("+source.length+") of different lenght."); } @@ -128,22 +142,28 @@ public class ShaderCode { * @param count number of shaders * @param context class used to help resolving the source location * @param sourceFiles array of source locations, organized as sourceFiles[count] + * @param mutableStringBuilder if true method returns a mutable StringBuilder instance + * which can be edited later on at the costs of a String conversion when passing to + * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. + * If false method returns an immutable String instance, + * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} + * at no additional costs. * * @throws IllegalArgumentException if count and sourceFiles.length do not match * @see #readShaderSource(Class, String) */ - public static ShaderCode create(GL2ES2 gl, int type, int count, Class context, String[] sourceFiles) { + public static ShaderCode create(GL2ES2 gl, int type, int count, Class context, String[] sourceFiles, boolean mutableStringBuilder) { if(null != gl && !ShaderUtil.isShaderCompilerAvailable(gl)) { return null; } - String[][] shaderSources = null; + CharSequence[][] shaderSources = null; if(null!=sourceFiles) { // sourceFiles.length and count is validated in ctor - shaderSources = new String[sourceFiles.length][1]; + shaderSources = new CharSequence[sourceFiles.length][1]; for(int i=0; itype is not supported * - * @see #create(GL2ES2, int, int, Class, String, String, String) + * @see #create(GL2ES2, int, Class, String, String, String, boolean) */ public static String getFileSuffix(boolean binary, int type) { switch (type) { @@ -225,7 +245,7 @@ public class ShaderCode { * * @throws GLException if binFormat is not supported * - * @see #create(GL2ES2, int, int, Class, String, String, String) + * @see #create(GL2ES2, int, Class, String, String, String, boolean) */ public static String getBinarySubPath(int binFormat) { switch (binFormat) { @@ -282,7 +302,7 @@ public class ShaderCode { * sp0.add(gl, fp0, System.err); * st.attachShaderProgram(gl, sp0, true); * - * A simplified entry point is {@link #create(GL2ES2, int, int, Class, String, String, String)}. + * A simplified entry point is {@link #create(GL2ES2, int, Class, String, String, String, boolean)}. * *

* The location is finally being resolved using the context class, see {@link #readShaderBinary(Class, String)}. @@ -297,6 +317,12 @@ public class ShaderCode { * @param srcBasenames basenames w/o path or suffix relative to srcRoot for the shader's source code * @param binRoot relative root path for binBasenames * @param binBasename basename w/o path or suffix relative to binRoot for the shader's binary code + * @param mutableStringBuilder if true method returns a mutable StringBuilder instance + * which can be edited later on at the costs of a String conversion when passing to + * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. + * If false method returns an immutable String instance, + * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} + * at no additional costs. * * @throws IllegalArgumentException if count and srcBasenames.length do not match * @@ -308,7 +334,8 @@ public class ShaderCode { * @see #getBinarySubPath(int) */ public static ShaderCode create(GL2ES2 gl, int type, int count, Class context, - String srcRoot, String[] srcBasenames, String binRoot, String binBasename) { + String srcRoot, String[] srcBasenames, String binRoot, String binBasename, + boolean mutableStringBuilder) { ShaderCode res = null; final String srcPath[]; String srcPathString = null; @@ -320,7 +347,7 @@ public class ShaderCode { for(int i=0; isource is used), * or to determine the shader binary format (if binary is used). * @param type either {@link GL2ES2#GL_VERTEX_SHADER} or {@link GL2ES2#GL_FRAGMENT_SHADER} - * @param count number of shaders, must be one * @param context class used to help resolving the source and binary location * @param srcRoot relative root path for basename * @param binRoot relative root path for basename + * @param mutableStringBuilder TODO * @param basenames basename w/o path or suffix relative to srcRoot and binRoot * for the shader's source and binary code. - * + * @param mutableStringBuilder if true method returns a mutable StringBuilder instance + * which can be edited later on at the costs of a String conversion when passing to + * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. + * If false method returns an immutable String instance, + * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} + * at no additional costs. * @throws IllegalArgumentException if count is not 1 * * @see #create(GL2ES2, int, int, Class, String, String[], String, String) */ - public static ShaderCode create(GL2ES2 gl, int type, int count, Class context, - String srcRoot, String binRoot, String basename) { - return create(gl, type, count, context, srcRoot, new String[] { basename }, binRoot, basename ); + public static ShaderCode create(GL2ES2 gl, int type, Class context, + String srcRoot, String binRoot, String basename, boolean mutableStringBuilder) { + return create(gl, type, 1, context, srcRoot, new String[] { basename }, binRoot, basename, mutableStringBuilder ); } /** @@ -418,9 +450,9 @@ public class ShaderCode { return "UNKNOWN_SHADER"; } - public int shaderBinaryFormat() { return shaderBinaryFormat; } - public Buffer shaderBinary() { return shaderBinary; } - public String[][] shaderSource() { return shaderSource; } + public int shaderBinaryFormat() { return shaderBinaryFormat; } + public Buffer shaderBinary() { return shaderBinary; } + public CharSequence[][] shaderSource() { return shaderSource; } public boolean isValid() { return valid; } @@ -434,6 +466,9 @@ public class ShaderCode { // Create & Compile the vertex/fragment shader objects if(null!=shaderSource) { + if(DEBUG_CODE) { + dumpShaderSource(System.err); + } valid=ShaderUtil.createAndCompileShader(gl, shader, shaderType, shaderSource, verboseOut); } else if(null!=shaderBinary) { @@ -473,7 +508,7 @@ public class ShaderCode { return id; } public String toString() { - StringBuffer buf = new StringBuffer("ShaderCode[id="+id+", type="+shaderTypeStr()+", valid="+valid+", shader: "); + StringBuilder buf = new StringBuilder("ShaderCode[id="+id+", type="+shaderTypeStr()+", valid="+valid+", shader: "); for(int i=0; i"); return; } - int sourceNum = (null!=shaderSource)?shaderSource.length:0; - int shaderNum = (null!=shader)?shader.capacity():0; - for(int i=0; i=sourceNum) { + if(i>=sourceCount) { out.println(""); } else { - String[] src = shaderSource[i]; + CharSequence[] src = shaderSource[i]; + int lineno=0; + for(int j=0; jdata after the line containing tag. + *

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

+ * + * @param shaderIdx the shader index to be used. + * @param tag search string + * @param fromIndex start search tag begininig with this index + * @param data the text to be inserted. Shall end with an EOL '\n' character. + * @return index after the inserted data + */ + public int insertShaderSource(int shaderIdx, String tag, int fromIndex, CharSequence data) { + if(null==shaderSource) { + throw new IllegalStateException("no shader source"); + } + final int shaderCount = (null!=shader)?shader.capacity():0; + if(0>shaderIdx || shaderIdx>=shaderCount) { + throw new IndexOutOfBoundsException("shaderIdx not within shader bounds [0.."+(shaderCount-1)+"]: "+shaderIdx); + } + final int sourceCount = (null!=shaderSource)?shaderSource.length:0; + if(shaderIdx>=sourceCount) { + throw new IndexOutOfBoundsException("shaderIdx not within source bounds [0.."+(sourceCount-1)+"]: "+shaderIdx); + } + final CharSequence[] src = shaderSource[shaderIdx]; + int curEndIndex = 0; + for(int j=0; jeol) { + eol = sb.indexOf("\r", insertIdx); // eol: covers \r 'only' + } + if(0 context, URLConnection conn, StringBuffer result, int lineno) throws IOException { + /** + * Adds data at offset in shader source for shader shaderIdx. + *

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

+ * + * @param shaderIdx the shader index to be used. + * @param position in shader source segments of shader shaderIdx + * @param data the text to be inserted. Shall end with an EOL '\n' character. + * @return index after the inserted data + */ + public int insertShaderSource(int shaderIdx, int position, CharSequence data) { + if(null==shaderSource) { + throw new IllegalStateException("no shader source"); + } + final int shaderCount = (null!=shader)?shader.capacity():0; + if(0>shaderIdx || shaderIdx>=shaderCount) { + throw new IndexOutOfBoundsException("shaderIdx not within shader bounds [0.."+(shaderCount-1)+"]: "+shaderIdx); + } + final int sourceCount = (null!=shaderSource)?shaderSource.length:0; + if(shaderIdx>=sourceCount) { + throw new IndexOutOfBoundsException("shaderIdx not within source bounds [0.."+(sourceCount-1)+"]: "+shaderIdx); + } + final CharSequence[] src = shaderSource[shaderIdx]; + int curEndIndex = 0; + for(int j=0; j context, URLConnection conn, StringBuilder result, int lineno) throws IOException { if(DEBUG_CODE) { - System.err.printf("%3d: // %s\n", lineno, conn.getURL()); + if(0 == lineno) { + result.append("// "+conn.getURL().toExternalForm()+"\n"); + } else { + result.append("// included @ line "+lineno+": "+conn.getURL().toExternalForm()+"\n"); + } } final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); try { String line = null; while ((line = reader.readLine()) != null) { lineno++; - if(DEBUG_CODE) { - System.err.printf("%3d: %s\n", lineno, line); - } if (line.startsWith("#include ")) { String includeFile = line.substring(9).trim(); URLConnection nextConn = null; @@ -554,40 +691,38 @@ public class ShaderCode { * @param result * @throws IOException */ - public static void readShaderSource(Class context, URLConnection conn, StringBuffer result) throws IOException { - if(DEBUG_CODE) { - System.err.println(); - System.err.println("// -----------------------------------------------------------"); - } + public static void readShaderSource(Class context, URLConnection conn, StringBuilder result) throws IOException { readShaderSource(context, conn, result, 0); - if(DEBUG_CODE) { - System.err.println("// -----------------------------------------------------------"); - System.err.println(); - } } /** * Reads shader source located in path, * either relative to the context class or absolute as-is. *

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

* * @param context class used to help resolve the source location * @param path location of shader source + * @param mutableStringBuilder if true method returns a mutable StringBuilder instance + * which can be edited later on at the costs of a String conversion when passing to + * {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)}. + * If false method returns an immutable String instance, + * which can be passed to {@link GL2ES2#glShaderSource(int, int, String[], IntBuffer)} + * at no additional costs. * @throws IOException * * @see IOUtil#getResource(Class, String) */ - public static String readShaderSource(Class context, String path) throws IOException { + public static CharSequence readShaderSource(Class context, String path, boolean mutableStringBuilder) throws IOException { URLConnection conn = IOUtil.getResource(context, path); if (conn == null) { return null; } - StringBuffer result = new StringBuffer(); + StringBuilder result = new StringBuilder(); readShaderSource(context, conn, result); - return result.toString(); + return mutableStringBuilder ? result : result.toString(); } /** @@ -621,7 +756,7 @@ public class ShaderCode { // Internals only below this point // - protected String[][] shaderSource = null; + protected CharSequence[][] shaderSource = null; protected Buffer shaderBinary = null; protected int shaderBinaryFormat = -1; protected IntBuffer shader = null; diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderUtil.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderUtil.java index d8aa1aa7f..40c052498 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderUtil.java @@ -200,7 +200,7 @@ public class ShaderUtil { return info.shaderCompilerAvailable.booleanValue(); } - public static void shaderSource(GL _gl, int shader, java.lang.String[] source) + public static void shaderSource(GL _gl, int shader, CharSequence[] source) { final GL2ES2 gl = _gl.getGL2ES2(); if(!isShaderCompilerAvailable(_gl)) { @@ -215,11 +215,27 @@ public class ShaderUtil { IntBuffer lengths = Buffers.newDirectIntBuffer(count); for(int i=0; i=0; i--) { + final CharSequence csq = source[i]; + if(csq instanceof String) { + // if ShaderCode.create(.. mutableStringBuffer == false ) + tmp[i] = (String) csq; + } else { + // if ShaderCode.create(.. mutableStringBuffer == true ) + tmp[i] = source[i].toString(); + } + } + gl.glShaderSource(shader, count, tmp, lengths); } - gl.glShaderSource(shader, count, source, lengths); } - public static void shaderSource(GL _gl, IntBuffer shaders, java.lang.String[][] sources) + public static void shaderSource(GL _gl, IntBuffer shaders, CharSequence[][] sources) { int sourceNum = (null!=sources)?sources.length:0; int shaderNum = (null!=shaders)?shaders.remaining():0; @@ -312,7 +328,7 @@ public class ShaderUtil { } public static boolean createAndCompileShader(GL _gl, IntBuffer shader, int shaderType, - java.lang.String[][] sources, + CharSequence[][] sources, PrintStream verboseOut) { final GL2ES2 gl = _gl.getGL2ES2(); -- cgit v1.2.3