From 55d75c5abcb194d28efb5e53af25d486705a81a6 Mon Sep 17 00:00:00 2001 From: "Sha..rd" Date: Tue, 10 Jul 2012 03:53:53 +0000 Subject: [PATCH] * Shader now stores shader language in the ShaderSources instead of the Shader itself (everything makes a lot more sense now). ^ Make sure your shader languages are appropriate for each shader type (vert / frag) in your J3MS! * Shaders no longer have the "usable" member and the renderers don't use it either (its useless) git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9545 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../renderer/android/OGLESShaderRenderer.java | 134 +++----- .../Common/MatDefs/Post/BloomFinal.j3md | 7 +- .../com/jme3/asset/DesktopAssetManager.java | 48 +-- .../src/core/com/jme3/material/Technique.java | 13 +- .../core/com/jme3/material/TechniqueDef.java | 68 +++- engine/src/core/com/jme3/shader/Shader.java | 297 ++++++++---------- .../src/core/com/jme3/shader/ShaderKey.java | 27 +- .../core/com/jme3/shader/ShaderVariable.java | 19 +- .../jme3/renderer/lwjgl/LwjglRenderer.java | 134 ++------ 9 files changed, 304 insertions(+), 443 deletions(-) diff --git a/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java index 081def689..443923b2d 100644 --- a/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java +++ b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java @@ -590,6 +590,18 @@ public class OGLESShaderRenderer implements Renderer { } } + protected void bindProgram(Shader shader) { + int shaderId = shader.getId(); + if (context.boundShaderProgram != shaderId) { + GLES20.glUseProgram(shaderId); + statistics.onShaderUse(shader, true); + boundShader = shader; + context.boundShaderProgram = shaderId; + } else { + statistics.onShaderUse(shader, false); + } + } + protected void updateUniform(Shader shader, Uniform uniform) { int shaderId = shader.getId(); @@ -698,7 +710,6 @@ public class OGLESShaderRenderer implements Renderer { protected void updateShaderUniforms(Shader shader) { ListMap uniforms = shader.getUniformMap(); -// for (Uniform uniform : shader.getUniforms()){ for (int i = 0; i < uniforms.size(); i++) { Uniform uniform = uniforms.getValue(i); if (uniform.isUpdateNeeded()) { @@ -709,7 +720,6 @@ public class OGLESShaderRenderer implements Renderer { protected void resetUniformLocations(Shader shader) { ListMap uniforms = shader.getUniformMap(); -// for (Uniform uniform : shader.getUniforms()){ for (int i = 0; i < uniforms.size(); i++) { Uniform uniform = uniforms.getValue(i); uniform.reset(); // e.g check location again @@ -736,27 +746,28 @@ public class OGLESShaderRenderer implements Renderer { } } - public void updateShaderSourceData(ShaderSource source, String language) { + public void updateShaderSourceData(ShaderSource source) { int id = source.getId(); if (id == -1) { - // create id + // Create id id = GLES20.glCreateShader(convertShaderType(source.getType())); if (id <= 0) { throw new RendererException("Invalid ID received when trying to create shader."); } source.setId(id); } + + if (!source.getLanguage().equals("GLSL100")) { + throw new RendererException("This shader cannot run in OpenGL ES. " + + "Only GLSL 1.0 shaders are supported."); + } // upload shader source // merge the defines and source code - byte[] versionData = new byte[]{};//"#version 140\n".getBytes(); -// versionData = "#define INSTANCING 1\n".getBytes(); byte[] definesCodeData = source.getDefines().getBytes(); byte[] sourceCodeData = source.getSource().getBytes(); - ByteBuffer codeBuf = BufferUtils.createByteBuffer(versionData.length - + definesCodeData.length - + sourceCodeData.length); - codeBuf.put(versionData); + ByteBuffer codeBuf = BufferUtils.createByteBuffer(definesCodeData.length + + sourceCodeData.length); codeBuf.put(definesCodeData); codeBuf.put(sourceCodeData); codeBuf.flip(); @@ -791,10 +802,11 @@ public class OGLESShaderRenderer implements Renderer { if (compiledOK) { if (infoLog != null) { - logger.log(Level.INFO, "compile success: " + source.getName() + ", " + infoLog); + logger.log(Level.INFO, "compile success: {0}, {1}", new Object[]{source.getName(), infoLog}); } else { - logger.log(Level.FINE, "compile success: " + source.getName()); + logger.log(Level.FINE, "compile success: {0}", source.getName()); } + source.clearUpdateNeeded(); } else { logger.log(Level.WARNING, "Bad compile of:\n{0}", new Object[]{ShaderDebug.formatShaderSource(source.getDefines(), source.getSource(),stringBuf.toString())}); @@ -804,18 +816,6 @@ public class OGLESShaderRenderer implements Renderer { throw new RendererException("compile error in:" + source + " error: "); } } - - source.clearUpdateNeeded(); - // only usable if compiled - source.setUsable(compiledOK); - if (!compiledOK) { - // make sure to dispose id cause all program's - // shaders will be cleared later. - GLES20.glDeleteShader(id); - } else { - // register for cleanup since the ID is usable - objManager.registerForCleanup(source); - } } public void updateShaderData(Shader shader) { @@ -835,15 +835,7 @@ public class OGLESShaderRenderer implements Renderer { for (ShaderSource source : shader.getSources()) { if (source.isUpdateNeeded()) { - updateShaderSourceData(source, shader.getLanguage()); - // shader has been compiled here - } - - if (!source.isUsable()) { - // it's useless.. just forget about everything.. - shader.setUsable(false); - shader.clearUpdateNeeded(); - return; + updateShaderSourceData(source); } GLES20.glAttachShader(id, source.getId()); } @@ -871,41 +863,27 @@ public class OGLESShaderRenderer implements Renderer { } else { logger.fine("shader link success"); } - } else { - if (infoLog != null) { - throw new RendererException("Shader link failure, shader:" + shader + " info:" + infoLog); - } else { - throw new RendererException("Shader link failure, shader:" + shader + " info: "); - } - } - - shader.clearUpdateNeeded(); - if (!linkOK) { - // failure.. forget about everything - shader.resetSources(); - shader.setUsable(false); - deleteShader(shader); - } else { - shader.setUsable(true); + shader.clearUpdateNeeded(); if (needRegister) { + // Register shader for clean up if it was created in this method. objManager.registerForCleanup(shader); statistics.onNewShader(); } else { // OpenGL spec: uniform locations may change after re-link resetUniformLocations(shader); } + } else { + if (infoLog != null) { + throw new RendererException("Shader link failure, shader:" + shader + " info:" + infoLog); + } else { + throw new RendererException("Shader link failure, shader:" + shader + " info: "); + } } } - + public void setShader(Shader shader) { if (shader == null) { - if (context.boundShaderProgram > 0) { - GLES20.glUseProgram(0); - - statistics.onShaderUse(null, true); - context.boundShaderProgram = 0; - boundShader = null; - } + throw new IllegalArgumentException("Shader cannot be null"); } else { if (shader.isUpdateNeeded()) { updateShaderData(shader); @@ -913,37 +891,11 @@ public class OGLESShaderRenderer implements Renderer { // NOTE: might want to check if any of the // sources need an update? - if (!shader.isUsable()) { - logger.warning("shader is not usable."); - return; - } assert shader.getId() > 0; updateShaderUniforms(shader); - if (context.boundShaderProgram != shader.getId()) { - if (VALIDATE_SHADER) { - // check if shader can be used - // with current state - GLES20.glValidateProgram(shader.getId()); - GLES20.glGetProgramiv(shader.getId(), GLES20.GL_VALIDATE_STATUS, intBuf1); - - boolean validateOK = intBuf1.get(0) == GLES20.GL_TRUE; - if (validateOK) { - logger.fine("shader validate success"); - } else { - logger.warning("shader validate failure"); - } - } - - GLES20.glUseProgram(shader.getId()); - - statistics.onShaderUse(shader, true); - context.boundShaderProgram = shader.getId(); - boundShader = shader; - } else { - statistics.onShaderUse(shader, false); - } + bindProgram(shader); } } @@ -952,9 +904,8 @@ public class OGLESShaderRenderer implements Renderer { logger.warning("Shader source is not uploaded to GPU, cannot delete."); return; } - source.setUsable(false); + source.clearUpdateNeeded(); - GLES20.glDeleteShader(source.getId()); source.resetObject(); } @@ -964,20 +915,17 @@ public class OGLESShaderRenderer implements Renderer { logger.warning("Shader is not uploaded to GPU, cannot delete."); return; } + for (ShaderSource source : shader.getSources()) { if (source.getId() != -1) { GLES20.glDetachShader(shader.getId(), source.getId()); - // the next part is done by the GLObjectManager automatically -// glDeleteShader(source.getId()); + deleteShaderSource(source); } } - // kill all references so sources can be collected - // if needed. - shader.resetSources(); - + GLES20.glDeleteProgram(shader.getId()); - statistics.onDeleteShader(); + shader.resetObject(); } /*********************************************************************\ diff --git a/engine/src/core-effects/Common/MatDefs/Post/BloomFinal.j3md b/engine/src/core-effects/Common/MatDefs/Post/BloomFinal.j3md index 68f8c287d..036c665ee 100644 --- a/engine/src/core-effects/Common/MatDefs/Post/BloomFinal.j3md +++ b/engine/src/core-effects/Common/MatDefs/Post/BloomFinal.j3md @@ -8,7 +8,7 @@ MaterialDef Bloom Final { } Technique { - VertexShader GLSL100: Common/MatDefs/Post/Post15.vert + VertexShader GLSL150: Common/MatDefs/Post/Post15.vert FragmentShader GLSL150: Common/MatDefs/Post/bloomFinal15.frag WorldParameters { @@ -28,9 +28,4 @@ MaterialDef Bloom Final { WorldViewProjectionMatrix } } - - - - Technique FixedFunc { - } } \ No newline at end of file diff --git a/engine/src/core/com/jme3/asset/DesktopAssetManager.java b/engine/src/core/com/jme3/asset/DesktopAssetManager.java index 83944f3d0..d969cbe74 100644 --- a/engine/src/core/com/jme3/asset/DesktopAssetManager.java +++ b/engine/src/core/com/jme3/asset/DesktopAssetManager.java @@ -336,11 +336,6 @@ public class DesktopAssetManager implements AssetManager { return loadAsset(new AssetKey(name)); } - /** - * Loads a texture. - * - * @return the texture - */ public Texture loadTexture(TextureKey key){ return (Texture) loadAsset(key); } @@ -365,18 +360,16 @@ public class DesktopAssetManager implements AssetManager { return loadAudio(new AudioKey(name, false)); } - /** - * Loads a bitmap font with the given name. - * - * @param name - * @return the loaded {@link BitmapFont} - */ public BitmapFont loadFont(String name){ return (BitmapFont) loadAsset(new AssetKey(name)); } - public InputStream loadGLSLLibrary(AssetKey key){ - return (InputStream) loadAsset(key); + public Spatial loadModel(ModelKey key){ + return (Spatial) loadAsset(key); + } + + public Spatial loadModel(String name){ + return loadModel(new ModelKey(name)); } /** @@ -389,34 +382,21 @@ public class DesktopAssetManager implements AssetManager { // cache abuse in method // that doesn't use loaders/locators AssetCache cache = handler.getCache(SimpleAssetCache.class); - Shader s = (Shader) cache.getFromCache(key); - if (s == null){ + Shader shader = (Shader) cache.getFromCache(key); + if (shader == null){ String vertName = key.getVertName(); String fragName = key.getFragName(); String vertSource = (String) loadAsset(new AssetKey(vertName)); String fragSource = (String) loadAsset(new AssetKey(fragName)); - s = new Shader(key.getLanguage()); - s.addSource(Shader.ShaderType.Vertex, vertName, vertSource, key.getDefines().getCompiled()); - s.addSource(Shader.ShaderType.Fragment, fragName, fragSource, key.getDefines().getCompiled()); + shader = new Shader(); + shader.initialize(); + shader.addSource(Shader.ShaderType.Vertex, vertName, vertSource, key.getDefines().getCompiled(), key.getVertexShaderLanguage()); + shader.addSource(Shader.ShaderType.Fragment, fragName, fragSource, key.getDefines().getCompiled(), key.getFragmentShaderLanguage()); - cache.addToCache(key, s); + cache.addToCache(key, shader); } - return s; - } - - public Spatial loadModel(ModelKey key){ - return (Spatial) loadAsset(key); - } - - /** - * Load a model. - * - * @param name - * @return the loaded model - */ - public Spatial loadModel(String name){ - return loadModel(new ModelKey(name)); + return shader; } } diff --git a/engine/src/core/com/jme3/material/Technique.java b/engine/src/core/com/jme3/material/Technique.java index 44f473295..c833fd3c9 100644 --- a/engine/src/core/com/jme3/material/Technique.java +++ b/engine/src/core/com/jme3/material/Technique.java @@ -32,9 +32,7 @@ package com.jme3.material; import com.jme3.asset.AssetManager; -import com.jme3.export.*; import com.jme3.shader.*; -import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -43,7 +41,7 @@ import java.util.logging.Logger; /** * Represents a technique instance. */ -public class Technique implements Savable { +public class Technique /* implements Savable */ { private static final Logger logger = Logger.getLogger(Technique.class.getName()); private TechniqueDef def; @@ -133,6 +131,10 @@ public class Technique implements Savable { } void updateUniformParam(String paramName, VarType type, Object value) { + if (paramName == null) { + throw new IllegalArgumentException(); + } + Uniform u = shader.getUniform(paramName); switch (type) { case TextureBuffer: @@ -208,7 +210,8 @@ public class Technique implements Savable { ShaderKey key = new ShaderKey(def.getVertexShaderName(), def.getFragmentShaderName(), allDefines, - def.getShaderLanguage()); + def.getVertexShaderLanguage(), + def.getFragmentShaderLanguage()); shader = manager.loadShader(key); // register the world bound uniforms @@ -225,6 +228,7 @@ public class Technique implements Savable { needReload = false; } + /* public void write(JmeExporter ex) throws IOException { OutputCapsule oc = ex.getCapsule(this); oc.write(def, "def", null); @@ -240,4 +244,5 @@ public class Technique implements Savable { defines = (DefineList) ic.readSavable("defines", null); shader = (Shader) ic.readSavable("shader", null); } + */ } diff --git a/engine/src/core/com/jme3/material/TechniqueDef.java b/engine/src/core/com/jme3/material/TechniqueDef.java index aaeb34004..ee94a8c9d 100644 --- a/engine/src/core/com/jme3/material/TechniqueDef.java +++ b/engine/src/core/com/jme3/material/TechniqueDef.java @@ -51,6 +51,11 @@ import java.util.List; */ public class TechniqueDef implements Savable { + /** + * Version #1: Separate shader language for each shader source. + */ + public static final int SAVABLE_VERSION = 1; + /** * Describes light rendering mode. */ @@ -101,7 +106,9 @@ public class TechniqueDef implements Savable { private String vertName; private String fragName; - private String shaderLang; + private String vertLanguage; + private String fragLanguage; + private DefineList presetDefines; private boolean usesShaders; @@ -227,13 +234,16 @@ public class TechniqueDef implements Savable { * @param fragmentShader The name of the fragment shader * @param shaderLanguage The shader language */ - public void setShaderFile(String vertexShader, String fragmentShader, String shaderLanguage){ + public void setShaderFile(String vertexShader, String fragmentShader, String vertLanguage, String fragLanguage){ this.vertName = vertexShader; this.fragName = fragmentShader; - this.shaderLang = shaderLanguage; + this.vertLanguage = vertLanguage; + this.fragLanguage = fragLanguage; - Caps langCap = Caps.valueOf(shaderLanguage); - requiredCaps.add(langCap); + Caps vertCap = Caps.valueOf(vertLanguage); + requiredCaps.add(vertCap); + Caps fragCap = Caps.valueOf(fragLanguage); + requiredCaps.add(fragCap); usesShaders = true; } @@ -247,9 +257,9 @@ public class TechniqueDef implements Savable { * @see #addShaderParamDefine(java.lang.String, java.lang.String) */ public String getShaderParamDefine(String paramName){ - if (defineParams == null) + if (defineParams == null) { return null; - + } return defineParams.get(paramName); } @@ -266,9 +276,9 @@ public class TechniqueDef implements Savable { * @param defineName The name of the define parameter, e.g. USE_LIGHTING */ public void addShaderParamDefine(String paramName, String defineName){ - if (defineParams == null) + if (defineParams == null) { defineParams = new HashMap(); - + } defineParams.put(paramName, defineName); } @@ -297,9 +307,9 @@ public class TechniqueDef implements Savable { * @param value The value of the define */ public void addShaderPresetDefine(String defineName, VarType type, Object value){ - if (presetDefines == null) + if (presetDefines == null) { presetDefines = new DefineList(); - + } presetDefines.set(defineName, type, value); } @@ -325,14 +335,27 @@ public class TechniqueDef implements Savable { } /** - * Returns the shader language of the shaders used in this technique. - * - * @return the shader language of the shaders used in this technique. + * @deprecated Use {@link #getVertexShaderLanguage() } instead. */ + @Deprecated public String getShaderLanguage() { - return shaderLang; + return vertLanguage; } + /** + * Returns the language of the fragment shader used in this technique. + */ + public String getFragmentShaderLanguage() { + return fragLanguage; + } + + /** + * Returns the language of the vertex shader used in this technique. + */ + public String getVertexShaderLanguage() { + return vertLanguage; + } + /** * Adds a new world parameter by the given name. * @@ -368,12 +391,14 @@ public class TechniqueDef implements Savable { oc.write(name, "name", null); oc.write(vertName, "vertName", null); oc.write(fragName, "fragName", null); - oc.write(shaderLang, "shaderLang", null); + oc.write(vertLanguage, "vertLanguage", null); + oc.write(vertLanguage, "fragLanguage", null); oc.write(presetDefines, "presetDefines", null); oc.write(lightMode, "lightMode", LightMode.Disable); oc.write(shadowMode, "shadowMode", ShadowMode.Disable); oc.write(renderState, "renderState", null); oc.write(usesShaders, "usesShaders", false); + // TODO: Finish this when Map export is available // oc.write(defineParams, "defineParams", null); // TODO: Finish this when List export is available @@ -385,12 +410,21 @@ public class TechniqueDef implements Savable { name = ic.readString("name", null); vertName = ic.readString("vertName", null); fragName = ic.readString("fragName", null); - shaderLang = ic.readString("shaderLang", null); presetDefines = (DefineList) ic.readSavable("presetDefines", null); lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable); shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable); renderState = (RenderState) ic.readSavable("renderState", null); usesShaders = ic.readBoolean("usesShaders", false); + + if (ic.getSavableVersion(TechniqueDef.class) == 0) { + // Old version + vertLanguage = ic.readString("shaderLang", null); + fragLanguage = vertLanguage; + } else { + // New version + vertLanguage = ic.readString("vertLanguage", null); + fragLanguage = ic.readString("fragLanguage", null);; + } } } diff --git a/engine/src/core/com/jme3/shader/Shader.java b/engine/src/core/com/jme3/shader/Shader.java index 265eed6a0..90c5b6c46 100644 --- a/engine/src/core/com/jme3/shader/Shader.java +++ b/engine/src/core/com/jme3/shader/Shader.java @@ -32,37 +32,33 @@ package com.jme3.shader; -import com.jme3.export.*; import com.jme3.renderer.Renderer; import com.jme3.scene.VertexBuffer; import com.jme3.util.IntMap; import com.jme3.util.IntMap.Entry; import com.jme3.util.ListMap; import com.jme3.util.NativeObject; -import java.io.IOException; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; -public final class Shader extends NativeObject implements Savable { - - private String language; +public final class Shader extends NativeObject { /** - * True if the shader is fully compiled & linked. - * (e.g no GL error will be invoked if used). + * + * @deprecated shader language now specified per shader source. See + * {@link ShaderSource#setLanguage(String) */ - private boolean usable = false; - + @Deprecated + private String language; + /** - * A list of all shaders currently attached. + * A list of all shader sources currently attached. */ - private ArrayList shaderList; + private ArrayList shaderSourceList; /** * Maps uniform name to the uniform variable. */ -// private HashMap uniforms; private ListMap uniforms; /** @@ -94,50 +90,32 @@ public final class Shader extends NativeObject implements Savable { * Shader source describes a shader object in OpenGL. Each shader source * is assigned a certain pipeline which it controls (described by it's type). */ - public static class ShaderSource extends NativeObject implements Savable { + public static class ShaderSource extends NativeObject { - ShaderType shaderType; - - boolean usable = false; - String name = null; - String source = null; - String defines = null; + ShaderType sourceType; + String language; + String name; + String source; + String defines; public ShaderSource(ShaderType type){ super(ShaderSource.class); - this.shaderType = type; - if (type == null) + this.sourceType = type; + if (type == null) { throw new NullPointerException("The shader type must be specified"); + } } protected ShaderSource(ShaderSource ss){ super(ShaderSource.class, ss.id); - this.shaderType = ss.shaderType; - usable = false; - name = ss.name; - // forget source & defines + // No data needs to be copied. + // (This is a destructable clone) } public ShaderSource(){ super(ShaderSource.class); } - public void write(JmeExporter ex) throws IOException{ - OutputCapsule oc = ex.getCapsule(this); - oc.write(shaderType, "shaderType", null); - oc.write(name, "name", null); - oc.write(source, "source", null); - oc.write(defines, "defines", null); - } - - public void read(JmeImporter im) throws IOException{ - InputCapsule ic = im.getCapsule(this); - shaderType = ic.readEnum("shaderType", ShaderType.class, null); - name = ic.readString("name", null); - source = ic.readString("source", null); - defines = ic.readString("defines", null); - } - public void setName(String name){ this.name = name; } @@ -147,21 +125,33 @@ public final class Shader extends NativeObject implements Savable { } public ShaderType getType() { - return shaderType; + return sourceType; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + if (language == null) { + throw new NullPointerException("Shader language cannot be null"); + } + this.language = language; + setUpdateNeeded(); } public void setSource(String source){ - if (source == null) + if (source == null) { throw new NullPointerException("Shader source cannot be null"); - + } this.source = source; setUpdateNeeded(); } public void setDefines(String defines){ - if (defines == null) + if (defines == null) { throw new NullPointerException("Shader defines cannot be null"); - + } this.defines = defines; setUpdateNeeded(); } @@ -174,14 +164,6 @@ public final class Shader extends NativeObject implements Savable { return defines; } - public boolean isUsable(){ - return usable; - } - - public void setUsable(boolean usable){ - this.usable = usable; - } - @Override public String toString(){ String nameTxt = ""; @@ -192,12 +174,11 @@ public final class Shader extends NativeObject implements Savable { return getClass().getSimpleName() + "["+nameTxt+"type=" - + shaderType.name()+"]"; + + sourceType.name()+", language=" + language + "]"; } public void resetObject(){ id = -1; - usable = false; setUpdateNeeded(); } @@ -211,110 +192,103 @@ public final class Shader extends NativeObject implements Savable { } /** - * Create an empty shader. + * @deprecated Shader sources are now associated with the shader + * language. */ + @Deprecated public Shader(String language){ super(Shader.class); this.language = language; - shaderList = new ArrayList(); -// uniforms = new HashMap(); + initialize(); + } + + /** + * Initializes the shader for use, must be called after the + * constructor without arguments is used. + */ + public void initialize() { + shaderSourceList = new ArrayList(); uniforms = new ListMap(); attribs = new IntMap(); } - + /** - * Do not use this constructor. Serialization purposes only. + * Creates a new shader, {@link #initialize() } must be called + * after this constructor for the shader to be usable. */ public Shader(){ super(Shader.class); } + /** + * Do not use this constructor. Used for destructable clones only. + */ protected Shader(Shader s){ super(Shader.class, s.id); - shaderList = new ArrayList(); - //uniforms = new ListMap(); - //attribs = new IntMap(); - // NOTE: Because ShaderSources are registered separately with - // the GLObjectManager - for (ShaderSource source : s.shaderList){ - shaderList.add( (ShaderSource)source.createDestructableClone() ); + // Shader sources cannot be shared, therefore they must + // be destroyed together with the parent shader. + shaderSourceList = new ArrayList(); + for (ShaderSource source : s.shaderSourceList){ + shaderSourceList.add( (ShaderSource)source.createDestructableClone() ); } } - public void write(JmeExporter ex) throws IOException{ - OutputCapsule oc = ex.getCapsule(this); - oc.write(language, "language", null); - oc.writeSavableArrayList(shaderList, "shaderList", null); - oc.writeIntSavableMap(attribs, "attribs", null); - oc.writeStringSavableMap(uniforms, "uniforms", null); - } - - public void read(JmeImporter im) throws IOException{ - InputCapsule ic = im.getCapsule(this); - language = ic.readString("language", null); - shaderList = ic.readSavableArrayList("shaderList", null); - attribs = (IntMap) ic.readIntSavableMap("attribs", null); - - HashMap uniMap = (HashMap) ic.readStringSavableMap("uniforms", null); - uniforms = new ListMap(uniMap); - } - /** - * Creates a deep clone of the shader, where the sources are available - * but have not been compiled yet. Does not copy the uniforms or attribs. - * @return + * @deprecated Use the method that takes a language argument instead. + * {@link #addSource(com.jme3.shader.Shader.ShaderType, java.lang.String, java.lang.String, java.lang.String, java.lang.String) } */ -// public Shader createDeepClone(String defines){ -// Shader newShader = new Shader(language); -// for (ShaderSource source : shaderList){ -// if (!source.getDefines().equals(defines)){ -// // need to clone the shadersource so -// // the correct defines can be placed -// ShaderSource newSource = new ShaderSource(source.getType()); -// newSource.setSource(source.getSource()); -// newSource.setDefines(defines); -// newShader.addSource(newSource); -// }else{ -// // no need to clone source, also saves -// // having to compile the shadersource -// newShader.addSource(source); -// } -// } -// return newShader; -// } - + @Deprecated + public void addSource(ShaderType type, String name, String source, String defines) { + addSource(type, name, source, defines, this.language); + } + /** - * Adds source code to a certain pipeline. - * - * @param type The pipeline to control - * @param source The shader source code (in GLSL). + * @deprecated Use the method that takes a language argument instead. + * {@link #addSource(com.jme3.shader.Shader.ShaderType, java.lang.String, java.lang.String, java.lang.String, java.lang.String) } */ - public void addSource(ShaderType type, String name, String source, String defines){ - ShaderSource shader = new ShaderSource(type); - shader.setSource(source); - shader.setName(name); - if (defines != null) - shader.setDefines(defines); - - shaderList.add(shader); - setUpdateNeeded(); - } - + @Deprecated public void addSource(ShaderType type, String source, String defines){ addSource(type, null, source, defines); } + /** + * @deprecated Use the method that takes a language argument instead. + * {@link #addSource(com.jme3.shader.Shader.ShaderType, java.lang.String, java.lang.String, java.lang.String, java.lang.String) } + */ + @Deprecated public void addSource(ShaderType type, String source){ addSource(type, source, null); } /** - * Adds an existing shader source to this shader. - * @param source + * @deprecated Shader sources may not be shared. + * {@link #addSource(com.jme3.shader.Shader.ShaderType, java.lang.String, java.lang.String, java.lang.String, java.lang.String) } */ + @Deprecated private void addSource(ShaderSource source){ - shaderList.add(source); + shaderSourceList.add(source); + setUpdateNeeded(); + } + + /** + * Adds source code to a certain pipeline. + * + * @param type The pipeline to control + * @param source The shader source code (in GLSL). + * @param defines Preprocessor defines (placed at the beginning of the shader) + * @param language The shader source language, currently accepted is GLSL### + * where ### is the version, e.g. GLSL100 = GLSL 1.0, GLSL330 = GLSL 3.3, etc. + */ + public void addSource(ShaderType type, String name, String source, String defines, String language){ + ShaderSource shaderSource = new ShaderSource(type); + shaderSource.setSource(source); + shaderSource.setName(name); + shaderSource.setLanguage(language); + if (defines != null) { + shaderSource.setDefines(defines); + } + shaderSourceList.add(shaderSource); setUpdateNeeded(); } @@ -343,69 +317,73 @@ public final class Shader extends NativeObject implements Savable { return attrib; } -// public Collection getUniforms(){ -// return uniforms.values(); -// } - public ListMap getUniformMap(){ return uniforms; } -// public Collection getAttributes() { -// return attribs. -// } - public Collection getSources(){ - return shaderList; + return shaderSourceList; } + /** + * @deprecated Shaders no longer have a language variable, + * use {@link ShaderSource#getLanguage() } instead. + */ + @Deprecated public String getLanguage(){ return language; } @Override - public String toString(){ - return getClass().getSimpleName() + "[language="+language - + ", numSources="+shaderList.size() - + ", numUniforms="+uniforms.size() - + ", shaderSources="+getSources()+"]"; + public String toString() { + return getClass().getSimpleName() + + "[numSources=" + shaderSourceList.size() + + ", numUniforms=" + uniforms.size() + + ", shaderSources=" + getSources() + "]"; } /** - * Clears all sources. Assuming that they have already been detached and - * removed on the GL side. + * @deprecated This method is not needed since deleting + * a shader causes the sources to delete as well, thus its not required + * for them to be GC'd to be removed from GL. */ + @Deprecated public void resetSources(){ - shaderList.clear(); + shaderSourceList.clear(); } /** - * Returns true if this program and all it's shaders have been compiled, - * linked and validated successfuly. + * @deprecated Unusable shaders cause the renderer to crash, + * therefore this field no longer serves any purpose. */ + @Deprecated public boolean isUsable(){ - return usable; + return true; } /** - * Sets if the program can be used. Should only be called by the Renderer. - * @param usable + * @deprecated Unusable shaders cause the renderer to crash, + * therefore this field no longer serves any purpose. */ + @Deprecated public void setUsable(boolean usable){ - this.usable = usable; } /** * Usually called when the shader itself changes or during any - * time when the var locations need to be refreshed. + * time when the variable locations need to be refreshed. */ - public void resetLocations(){ - // NOTE: Shader sources will be reset seperately from the shader itself. - for (Uniform uniform : uniforms.values()){ - uniform.reset(); // fixes issue with re-initialization + public void resetLocations() { + if (uniforms != null) { + // NOTE: Shader sources will be reset seperately from the shader itself. + for (Uniform uniform : uniforms.values()) { + uniform.reset(); // fixes issue with re-initialization + } } - for (Entry entry : attribs){ - entry.getValue().location = -2; + if (attribs != null) { + for (Entry entry : attribs) { + entry.getValue().location = ShaderVariable.LOC_UNKNOWN; + } } } @@ -422,12 +400,9 @@ public final class Shader extends NativeObject implements Savable { @Override public void resetObject() { this.id = -1; - this.usable = false; - - for (ShaderSource source : shaderList){ + for (ShaderSource source : shaderSourceList){ source.resetObject(); } - setUpdateNeeded(); } diff --git a/engine/src/core/com/jme3/shader/ShaderKey.java b/engine/src/core/com/jme3/shader/ShaderKey.java index a4c18ebfb..c2057712b 100644 --- a/engine/src/core/com/jme3/shader/ShaderKey.java +++ b/engine/src/core/com/jme3/shader/ShaderKey.java @@ -43,16 +43,18 @@ public class ShaderKey extends AssetKey { protected String fragName; protected DefineList defines; - protected String language; + protected String vertLanguage; + protected String fragLanguage; public ShaderKey(){ } - public ShaderKey(String vertName, String fragName, DefineList defines, String lang){ + public ShaderKey(String vertName, String fragName, DefineList defines, String vertLanguage, String fragLanguage){ super(vertName); this.fragName = fragName; this.defines = defines; - this.language = lang; + this.vertLanguage = vertLanguage; + this.fragLanguage = fragLanguage; } @Override @@ -71,7 +73,6 @@ public class ShaderKey extends AssetKey { final ShaderKey other = (ShaderKey) obj; if (name.equals(other.name) && fragName.equals(other.fragName)){ -// return true; if (defines != null && other.defines != null) return defines.getCompiled().equals(other.defines.getCompiled()); else if (defines != null || other.defines != null) @@ -103,8 +104,20 @@ public class ShaderKey extends AssetKey { return fragName; } + /** + * @deprecated Use {@link #getVertexShaderLanguage() } instead. + */ + @Deprecated public String getLanguage() { - return language; + return vertLanguage; + } + + public String getVertexShaderLanguage() { + return vertLanguage; + } + + public String getFragmentShaderLanguage() { + return fragLanguage; } @Override @@ -112,7 +125,7 @@ public class ShaderKey extends AssetKey { super.write(ex); OutputCapsule oc = ex.getCapsule(this); oc.write(fragName, "fragment_name", null); - oc.write(language, "language", null); + oc.write(vertLanguage, "language", null); } @Override @@ -120,7 +133,7 @@ public class ShaderKey extends AssetKey { super.read(im); InputCapsule ic = im.getCapsule(this); fragName = ic.readString("fragment_name", null); - language = ic.readString("language", null); + vertLanguage = ic.readString("language", null); } } diff --git a/engine/src/core/com/jme3/shader/ShaderVariable.java b/engine/src/core/com/jme3/shader/ShaderVariable.java index 7dbd2e251..1c547b9d1 100644 --- a/engine/src/core/com/jme3/shader/ShaderVariable.java +++ b/engine/src/core/com/jme3/shader/ShaderVariable.java @@ -32,15 +32,15 @@ package com.jme3.shader; -import com.jme3.export.*; -import java.io.IOException; - -public class ShaderVariable implements Savable { +public class ShaderVariable { + public static final int LOC_UNKNOWN = -2, + LOC_NOT_DEFINED = -1; + // if -2, location not known // if -1, not defined in shader // if >= 0, uniform defined and available. - protected int location = -2; + protected int location = LOC_UNKNOWN; /** * Name of the uniform as was declared in the shader. @@ -54,15 +54,6 @@ public class ShaderVariable implements Savable { */ protected boolean updateNeeded = true;; - public void write(JmeExporter ex) throws IOException{ - OutputCapsule oc = ex.getCapsule(this); - oc.write(name, "name", null); - } - - public void read(JmeImporter im) throws IOException{ - InputCapsule ic = im.getCapsule(this); - name = ic.readString("name", null); - } public void setLocation(int location){ this.location = location; diff --git a/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java b/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java index a1ce9e8c0..46b5753e5 100644 --- a/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java +++ b/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java @@ -53,7 +53,10 @@ import com.jme3.texture.FrameBuffer.RenderBuffer; import com.jme3.texture.Image; import com.jme3.texture.Texture; import com.jme3.texture.Texture.WrapAxis; -import com.jme3.util.*; +import com.jme3.util.BufferUtils; +import com.jme3.util.ListMap; +import com.jme3.util.NativeObjectManager; +import com.jme3.util.SafeArrayList; import java.nio.*; import java.util.EnumSet; import java.util.List; @@ -61,7 +64,6 @@ import java.util.logging.Level; import java.util.logging.Logger; import jme3tools.converters.MipMapGenerator; import jme3tools.shader.ShaderDebug; -import org.lwjgl.opengl.*; import static org.lwjgl.opengl.ARBTextureMultisample.*; import static org.lwjgl.opengl.EXTFramebufferBlit.*; import static org.lwjgl.opengl.EXTFramebufferMultisample.*; @@ -72,6 +74,7 @@ import static org.lwjgl.opengl.GL13.*; import static org.lwjgl.opengl.GL14.*; import static org.lwjgl.opengl.GL15.*; import static org.lwjgl.opengl.GL20.*; +import org.lwjgl.opengl.*; //import static org.lwjgl.opengl.ARBDrawInstanced.*; public class LwjglRenderer implements Renderer { @@ -889,7 +892,6 @@ public class LwjglRenderer implements Renderer { protected void updateShaderUniforms(Shader shader) { ListMap uniforms = shader.getUniformMap(); -// for (Uniform uniform : shader.getUniforms()){ for (int i = 0; i < uniforms.size(); i++) { Uniform uniform = uniforms.getValue(i); if (uniform.isUpdateNeeded()) { @@ -900,7 +902,6 @@ public class LwjglRenderer implements Renderer { protected void resetUniformLocations(Shader shader) { ListMap uniforms = shader.getUniformMap(); -// for (Uniform uniform : shader.getUniforms()){ for (int i = 0; i < uniforms.size(); i++) { Uniform uniform = uniforms.getValue(i); uniform.reset(); // e.g check location again @@ -927,10 +928,10 @@ public class LwjglRenderer implements Renderer { } } - public void updateShaderSourceData(ShaderSource source, String language) { + public void updateShaderSourceData(ShaderSource source) { int id = source.getId(); if (id == -1) { - // create id + // Create id id = glCreateShader(convertShaderType(source.getType())); if (id <= 0) { throw new RendererException("Invalid ID received when trying to create shader."); @@ -941,9 +942,9 @@ public class LwjglRenderer implements Renderer { throw new RendererException("Cannot recompile shader source"); } - // upload shader source - // merge the defines and source code - + // Upload shader source. + // Merge the defines and source code. + String language = source.getLanguage(); stringBuf.setLength(0); if (language.startsWith("GLSL")) { int version = Integer.parseInt(language.substring(4)); @@ -999,6 +1000,7 @@ public class LwjglRenderer implements Renderer { } else { logger.log(Level.FINE, "{0} compile success", source.getName()); } + source.clearUpdateNeeded(); } else { logger.log(Level.WARNING, "Bad compile of:\n{0}", new Object[]{ShaderDebug.formatShaderSource(source.getDefines(), source.getSource(), stringBuf.toString())}); @@ -1008,21 +1010,6 @@ public class LwjglRenderer implements Renderer { throw new RendererException("compile error in:" + source + " error: "); } } - - source.clearUpdateNeeded(); - // only usable if compiled - source.setUsable(compiledOK); - if (!compiledOK) { - // make sure to dispose id cause all program's - // shaders will be cleared later. - glDeleteShader(id); - } else { - // register for cleanup since the ID is usable - // NOTE: From now on cleanup is handled - // by the parent shader object so no need - // to register. - //objManager.registerForCleanup(source); - } } public void updateShaderData(Shader shader) { @@ -1041,15 +1028,7 @@ public class LwjglRenderer implements Renderer { for (ShaderSource source : shader.getSources()) { if (source.isUpdateNeeded()) { - updateShaderSourceData(source, shader.getLanguage()); - // shader has been compiled here - } - - if (!source.isUsable()) { - // it's useless.. just forget about everything.. - shader.setUsable(false); - shader.clearUpdateNeeded(); - return; + updateShaderSourceData(source); } glAttachShader(id, source.getId()); } @@ -1057,13 +1036,16 @@ public class LwjglRenderer implements Renderer { if (caps.contains(Caps.OpenGL30)) { // Check if GLSL version is 1.5 for shader GL30.glBindFragDataLocation(id, 0, "outFragColor"); + // For MRT for(int i = 0 ; i < maxMRTFBOAttachs ; i++) { GL30.glBindFragDataLocation(id, i, "outFragData[" + i + "]"); } } - // link shaders to program + // Link shaders to program glLinkProgram(id); + + // Check link status glGetProgram(id, GL_LINK_STATUS, intBuf1); boolean linkOK = intBuf1.get(0) == GL_TRUE; String infoLog = null; @@ -1089,53 +1071,35 @@ public class LwjglRenderer implements Renderer { } else { logger.fine("shader link success"); } - } else { - if (infoLog != null) { - throw new RendererException("Shader link failure, shader:" + shader + " info:" + infoLog); - } else { - throw new RendererException("Shader link failure, shader:" + shader + " info: "); - } - } - - shader.clearUpdateNeeded(); - if (!linkOK) { - // failure.. forget about everything - shader.resetSources(); - shader.setUsable(false); - deleteShader(shader); - } else { - shader.setUsable(true); + shader.clearUpdateNeeded(); if (needRegister) { + // Register shader for clean up if it was created in this method. objManager.registerForCleanup(shader); statistics.onNewShader(); } else { // OpenGL spec: uniform locations may change after re-link resetUniformLocations(shader); } + } else { + if (infoLog != null) { + throw new RendererException("Shader link failure, shader:" + shader + " info:" + infoLog); + } else { + throw new RendererException("Shader link failure, shader:" + shader + " info: "); + } } } public void setShader(Shader shader) { if (shader == null) { - throw new IllegalArgumentException("shader cannot be null"); -// if (context.boundShaderProgram > 0) { -// glUseProgram(0); -// statistics.onShaderUse(null, true); -// context.boundShaderProgram = 0; -// boundShader = null; -// } + throw new IllegalArgumentException("Shader cannot be null"); } else { if (shader.isUpdateNeeded()) { updateShaderData(shader); } - + // NOTE: might want to check if any of the // sources need an update? - if (!shader.isUsable()) { - return; - } - assert shader.getId() > 0; updateShaderUniforms(shader); @@ -1148,7 +1112,6 @@ public class LwjglRenderer implements Renderer { logger.warning("Shader source is not uploaded to GPU, cannot delete."); return; } - source.setUsable(false); source.clearUpdateNeeded(); glDeleteShader(source.getId()); source.resetObject(); @@ -1167,12 +1130,9 @@ public class LwjglRenderer implements Renderer { } } - // kill all references so sources can be collected - // if needed. - shader.resetSources(); glDeleteProgram(shader.getId()); - statistics.onDeleteShader(); + shader.resetObject(); } /*********************************************************************\ @@ -2116,46 +2076,6 @@ public class LwjglRenderer implements Renderer { throw new UnsupportedOperationException("Unknown buffer format."); } } -// }else{ -// if (created || vb.hasDataSizeChanged()){ -// glBufferData(target, vb.getData().capacity() * vb.getFormat().getComponentSize(), usage); -// } -// -// ByteBuffer buf = glMapBuffer(target, -// GL_WRITE_ONLY, -// vb.getMappedData()); -// -// if (buf != vb.getMappedData()){ -// buf = buf.order(ByteOrder.nativeOrder()); -// vb.setMappedData(buf); -// } -// -// buf.clear(); -// -// switch (vb.getFormat()){ -// case Byte: -// case UnsignedByte: -// buf.put( (ByteBuffer) vb.getData() ); -// break; -// case Short: -// case UnsignedShort: -// buf.asShortBuffer().put( (ShortBuffer) vb.getData() ); -// break; -// case Int: -// case UnsignedInt: -// buf.asIntBuffer().put( (IntBuffer) vb.getData() ); -// break; -// case Float: -// buf.asFloatBuffer().put( (FloatBuffer) vb.getData() ); -// break; -// case Double: -// break; -// default: -// throw new RuntimeException("Unknown buffer format."); -// } -// -// glUnmapBuffer(target); -// } vb.clearUpdateNeeded(); }