/* * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistribution of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * */ package com.jogamp.opengl.util.glsl; import java.io.PrintStream; import java.nio.*; import java.util.*; import com.jogamp.opengl.*; import com.jogamp.common.nio.Buffers; import com.jogamp.opengl.GLExtensions; public class ShaderUtil { public static String getShaderInfoLog(final GL _gl, final int shaderObj) { final GL2ES2 gl = _gl.getGL2ES2(); final int[] infoLogLength=new int[1]; gl.glGetShaderiv(shaderObj, GL2ES2.GL_INFO_LOG_LENGTH, infoLogLength, 0); if(infoLogLength[0]==0) { return "(no info log)"; } final int[] charsWritten=new int[1]; final byte[] infoLogBytes = new byte[infoLogLength[0]]; gl.glGetShaderInfoLog(shaderObj, infoLogLength[0], charsWritten, 0, infoLogBytes, 0); return new String(infoLogBytes, 0, charsWritten[0]); } public static String getProgramInfoLog(final GL _gl, final int programObj) { final GL2ES2 gl = _gl.getGL2ES2(); final int[] infoLogLength=new int[1]; gl.glGetProgramiv(programObj, GL2ES2.GL_INFO_LOG_LENGTH, infoLogLength, 0); if(infoLogLength[0]==0) { return "(no info log)"; } final int[] charsWritten=new int[1]; final byte[] infoLogBytes = new byte[infoLogLength[0]]; gl.glGetProgramInfoLog(programObj, infoLogLength[0], charsWritten, 0, infoLogBytes, 0); return new String(infoLogBytes, 0, charsWritten[0]); } public static boolean isShaderStatusValid(final GL _gl, final int shaderObj, final int name, final PrintStream verboseOut) { final GL2ES2 gl = _gl.getGL2ES2(); final int[] ires = new int[1]; gl.glGetShaderiv(shaderObj, name, ires, 0); final boolean res = ires[0]==1; if(!res && null!=verboseOut) { verboseOut.println("Shader status invalid: "+ getShaderInfoLog(gl, shaderObj)); } return res; } public static boolean isShaderStatusValid(final GL _gl, final IntBuffer shaders, final int name, final PrintStream verboseOut) { boolean res = true; for (int i = shaders.position(); i < shaders.limit(); i++) { res = isShaderStatusValid(_gl, shaders.get(i), name, verboseOut) && res; } return res; } public static boolean isProgramStatusValid(final GL _gl, final int programObj, final int name) { final GL2ES2 gl = _gl.getGL2ES2(); final int[] ires = new int[1]; gl.glGetProgramiv(programObj, name, ires, 0); return ires[0]==1; } public static boolean isProgramLinkStatusValid(final GL _gl, final int programObj, final PrintStream verboseOut) { final GL2ES2 gl = _gl.getGL2ES2(); if(!gl.glIsProgram(programObj)) { if(null!=verboseOut) { verboseOut.println("Program name invalid: "+programObj); } return false; } if(!isProgramStatusValid(gl, programObj, GL2ES2.GL_LINK_STATUS)) { if(null!=verboseOut) { verboseOut.println("Program link failed: "+programObj+"\n\t"+ getProgramInfoLog(gl, programObj)); } return false; } return true; } /** * Performs {@link GL2ES2#glValidateProgram(int)} * <p> * One shall only call this method while debugging and only if all required * resources by the shader are set. * </p> * <p> * Note: It is possible that a working shader program will fail validation. * This has been experienced on NVidia APX2500 and Tegra2. * </p> * @see GL2ES2#glValidateProgram(int) **/ public static boolean isProgramExecStatusValid(final GL _gl, final int programObj, final PrintStream verboseOut) { final GL2ES2 gl = _gl.getGL2ES2(); gl.glValidateProgram(programObj); if(!isProgramStatusValid(gl, programObj, GL2ES2.GL_VALIDATE_STATUS)) { if(null!=verboseOut) { verboseOut.println("Program validation failed: "+programObj+"\n\t"+ getProgramInfoLog(gl, programObj)); } return false; } return true; } public static void createShader(final GL _gl, final int type, final IntBuffer shaders) { final GL2ES2 gl = _gl.getGL2ES2(); for (int i = shaders.position(); i < shaders.limit(); i++) { shaders.put(i, gl.glCreateShader(type)); } } /** * If supported, queries the natively supported shader binary formats using * {@link GL2ES2#GL_NUM_SHADER_BINARY_FORMATS} and {@link GL2ES2#GL_SHADER_BINARY_FORMATS} * via {@link GL2ES2#glGetIntegerv(int, int[], int)}. */ public static Set<Integer> getShaderBinaryFormats(final GL _gl) { final GL2ES2 gl = _gl.getGL2ES2(); final ProfileInformation info = getProfileInformation(gl); if(null == info.shaderBinaryFormats) { info.shaderBinaryFormats = new HashSet<Integer>(); if (gl.isGLES2Compatible()) { try { final int[] param = new int[1]; gl.glGetIntegerv(GL2ES2.GL_NUM_SHADER_BINARY_FORMATS, param, 0); final int err = gl.glGetError(); final int numFormats = GL.GL_NO_ERROR == err ? param[0] : 0; if(numFormats>0) { final int[] formats = new int[numFormats]; gl.glGetIntegerv(GL2ES2.GL_SHADER_BINARY_FORMATS, formats, 0); for(int i=0; i<numFormats; i++) { info.shaderBinaryFormats.add(Integer.valueOf(formats[i])); } } } catch (final GLException gle) { System.err.println("Caught exception on thread "+Thread.currentThread().getName()); gle.printStackTrace(); } } } return info.shaderBinaryFormats; } /** Returns true if a hader compiler is available, otherwise false. */ public static boolean isShaderCompilerAvailable(final GL _gl) { final GL2ES2 gl = _gl.getGL2ES2(); final ProfileInformation info = getProfileInformation(gl); if(null==info.shaderCompilerAvailable) { if(gl.isGLES2()) { boolean queryOK = false; try { final byte[] param = new byte[1]; gl.glGetBooleanv(GL2ES2.GL_SHADER_COMPILER, param, 0); final int err = gl.glGetError(); boolean v = GL.GL_NO_ERROR == err && param[0]!=(byte)0x00; if(!v) { final Set<Integer> bfs = getShaderBinaryFormats(gl); if(bfs.size()==0) { // no supported binary formats, hence a compiler must be available! v = true; } } info.shaderCompilerAvailable = Boolean.valueOf(v); queryOK = true; } catch (final GLException gle) { System.err.println("Caught exception on thread "+Thread.currentThread().getName()); gle.printStackTrace(); } if(!queryOK) { info.shaderCompilerAvailable = Boolean.valueOf(true); } } else if( gl.isGL2ES2() ) { info.shaderCompilerAvailable = new Boolean(true); } else { throw new GLException("Invalid OpenGL profile"); } } return info.shaderCompilerAvailable.booleanValue(); } /** Returns true if GeometryShader is supported, i.e. whether GLContext is ≥ 3.2 or ARB_geometry_shader4 extension is available. */ public static boolean isGeometryShaderSupported(final GL _gl) { final GLContext ctx = _gl.getContext(); return ctx.getGLVersionNumber().compareTo(GLContext.Version3_2) >= 0 || ctx.isExtensionAvailable(GLExtensions.ARB_geometry_shader4); } public static void shaderSource(final GL _gl, final int shader, final CharSequence[] source) { final GL2ES2 gl = _gl.getGL2ES2(); if(!isShaderCompilerAvailable(_gl)) { throw new GLException("No compiler is available"); } final int count = (null!=source)?source.length:0; if(count==0) { throw new GLException("No sources specified"); } final IntBuffer lengths = Buffers.newDirectIntBuffer(count); for(int i=0; i<count; i++) { lengths.put(i, source[i].length()); } if(source instanceof String[]) { // rare case .. gl.glShaderSource(shader, count, (String[])source, lengths); } else { final String[] tmp = new String[source.length]; for(int i = source.length - 1; i>=0; i--) { final CharSequence csq = source[i]; if(csq instanceof String) { // if ShaderCode.create(.. mutableStringBuilder == false ) tmp[i] = (String) csq; } else { // if ShaderCode.create(.. mutableStringBuilder == true ) tmp[i] = source[i].toString(); } } gl.glShaderSource(shader, count, tmp, lengths); } } public static void shaderSource(final GL _gl, final IntBuffer shaders, final CharSequence[][] sources) { final int sourceNum = (null!=sources)?sources.length:0; final int shaderNum = (null!=shaders)?shaders.remaining():0; if(shaderNum<=0 || sourceNum<=0 || shaderNum!=sourceNum) { throw new GLException("Invalid number of shaders and/or sources: shaders="+ shaderNum+", sources="+sourceNum); } for(int i=0; i<sourceNum; i++) { shaderSource(_gl, shaders.get(shaders.position() + i), sources[i]); } } public static void shaderBinary(final GL _gl, final IntBuffer shaders, final int binFormat, final java.nio.Buffer bin) { final GL2ES2 gl = _gl.getGL2ES2(); if(getShaderBinaryFormats(gl).size()<=0) { throw new GLException("No binary formats are supported"); } final int shaderNum = shaders.remaining(); if(shaderNum<=0) { throw new GLException("No shaders specified"); } if(null==bin) { throw new GLException("Null shader binary"); } final int binLength = bin.remaining(); if(0>=binLength) { throw new GLException("Empty shader binary (remaining == 0)"); } gl.glShaderBinary(shaderNum, shaders, binFormat, bin, binLength); } public static void compileShader(final GL _gl, final IntBuffer shaders) { final GL2ES2 gl = _gl.getGL2ES2(); for (int i = shaders.position(); i < shaders.limit(); i++) { gl.glCompileShader(shaders.get(i)); } } public static void attachShader(final GL _gl, final int program, final IntBuffer shaders) { final GL2ES2 gl = _gl.getGL2ES2(); for (int i = shaders.position(); i < shaders.limit(); i++) { gl.glAttachShader(program, shaders.get(i)); } } public static void detachShader(final GL _gl, final int program, final IntBuffer shaders) { final GL2ES2 gl = _gl.getGL2ES2(); for (int i = shaders.position(); i < shaders.limit(); i++) { gl.glDetachShader(program, shaders.get(i)); } } public static void deleteShader(final GL _gl, final IntBuffer shaders) { final GL2ES2 gl = _gl.getGL2ES2(); for (int i = shaders.position(); i < shaders.limit(); i++) { gl.glDeleteShader(shaders.get(i)); } } public static boolean createAndLoadShader(final GL _gl, final IntBuffer shader, final int shaderType, final int binFormat, final java.nio.Buffer bin, final PrintStream verboseOut) { final GL2ES2 gl = _gl.getGL2ES2(); int err = gl.glGetError(); // flush previous errors .. if(err!=GL.GL_NO_ERROR && null!=verboseOut) { verboseOut.println("createAndLoadShader: Pre GL Error: 0x"+Integer.toHexString(err)); } createShader(gl, shaderType, shader); err = gl.glGetError(); if(err!=GL.GL_NO_ERROR) { throw new GLException("createAndLoadShader: CreateShader failed, GL Error: 0x"+Integer.toHexString(err)); } shaderBinary(gl, shader, binFormat, bin); err = gl.glGetError(); if(err!=GL.GL_NO_ERROR && null!=verboseOut) { verboseOut.println("createAndLoadShader: ShaderBinary failed, GL Error: 0x"+Integer.toHexString(err)); } return err == GL.GL_NO_ERROR; } public static boolean createAndCompileShader(final GL _gl, final IntBuffer shader, final int shaderType, final CharSequence[][] sources, final PrintStream verboseOut) { final GL2ES2 gl = _gl.getGL2ES2(); int err = gl.glGetError(); // flush previous errors .. if(err!=GL.GL_NO_ERROR && null!=verboseOut) { verboseOut.println("createAndCompileShader: Pre GL Error: 0x"+Integer.toHexString(err)); } createShader(gl, shaderType, shader); err = gl.glGetError(); if(err!=GL.GL_NO_ERROR) { throw new GLException("createAndCompileShader: CreateShader failed, GL Error: 0x"+Integer.toHexString(err)); } shaderSource(gl, shader, sources); err = gl.glGetError(); if(err!=GL.GL_NO_ERROR) { throw new GLException("createAndCompileShader: ShaderSource failed, GL Error: 0x"+Integer.toHexString(err)); } compileShader(gl, shader); err = gl.glGetError(); if(err!=GL.GL_NO_ERROR && null!=verboseOut) { verboseOut.println("createAndCompileShader: CompileShader failed, GL Error: 0x"+Integer.toHexString(err)); } return isShaderStatusValid(gl, shader, GL2ES2.GL_COMPILE_STATUS, verboseOut) && err == GL.GL_NO_ERROR; } private static final String implObjectKey = "com.jogamp.opengl.util.glsl.ShaderUtil" ; private static class ProfileInformation { Boolean shaderCompilerAvailable = null; Set<Integer> shaderBinaryFormats = null; } private static ProfileInformation getProfileInformation(final GL gl) { final GLContext context = gl.getContext(); context.validateCurrent(); ProfileInformation data = (ProfileInformation) context.getAttachedObject(implObjectKey); if (data == null) { data = new ProfileInformation(); context.attachObject(implObjectKey, data); } return data; } }