Performance improvement

I tested MikuMikuDance viewer on Android.

It was very slow.

I worked on its improvement.



Test codes and binaries are here.

http://sourceforge.jp/projects/mikumikustudio/downloads/53320/AndroidBenchmark.7z/



hardware ASUS Transformer TF101


  • 1st step (AndroidSample_GoMyWay_1)

    jME3 revision: r8170

    result: 17.505 fps



    It’s very slow…


  • 2nd step (AndroidSample_GoMyWay_2)

    I read OGLESShaderRenderer source code.

    I found 2 bugs(VBO bug, Matrix4Array bug).

    I fixed them.

    http://hub.jmonkeyengine.org/groups/contribution-depot-jme3/forum/topic/android-oglesshaderrenderer-bug-patch/



    result: 22.139 fps (1.26 times from 1st step)


  • 3rd step (AndroidSample_GoMyWay_3)

    I checked the log.

    Many gc events were recorded there.

    I checked jME3 source codes and I found many memory allocation codes in rendering loop.

    I removed them.



    result: 22.895 fps (1.31 times from 1st step)


  • 4th step (AndroidSample_GoMyWay_4)

    I checked Material,Uniform,Technique.

    There are too many HashMap search.

    HashMap is fast on PC, but it’s slow on Android.

    I changed HashMap to array.



    result: 27.738 fps (1.58 times from 1st step)





    Profiler result(after 10 loops):



    1st step:

    char[] allocated 1697569.

    TreeMap$Entry allocated 804687

    http://i.imgur.com/bBi9r.jpg

    3rd step:

    char[] allocated 122793.

    TreeMap$Entry allocated 102.

    http://i.imgur.com/Zs3QG.jpg

    diff

    http://i.imgur.com/wPywB.jpg



    http://p.twimg.com/AY58fFNCAAEiB99.png:large



    [patch]

    diff -r 49767f5ec293 -r 9da2988ff52f engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java

    — a/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java Wed Sep 07 00:12:26 2011 +0900

    +++ b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java Mon Sep 26 10:45:43 2011 +0900

    @@ -81,6 +81,7 @@

    import android.opengl.GLES10;

    import android.opengl.GLES11;

    import android.opengl.GLES20;

    +import android.os.Build;



    public class OGLESShaderRenderer implements Renderer {



    @@ -405,6 +406,11 @@



    // checkGLError();


  •    if (&quot;2.2&quot;.equals(Build.VERSION.RELEASE)) {<br />
    
  •        useVBO = false;<br />
    
  •    } else {<br />
    
  •        useVBO = true;<br />
    
  •    }<br />
    

logger.log(Level.INFO, "Caps: {0}", caps);

}



@@ -843,6 +849,7 @@



uniform.clearUpdateNeeded();

FloatBuffer fb;

  •    int size;<br />
    

switch (uniform.getVarType()) {

case Float:

if (verboseLogging) {

@@ -906,35 +913,40 @@

logger.info("GLES20.glUniform1fv set FloatArray." + uniform.getName());

}

fb = (FloatBuffer) uniform.getValue();

  •            GLES20.glUniform1fv(loc, 1, fb);<br />
    
  •            size = fb.capacity();<br />
    
  •            GLES20.glUniform1fv(loc, size, fb);<br />
    

break;

case Vector2Array:

if (verboseLogging) {

logger.info("GLES20.glUniform2fv set Vector2Array." + uniform.getName());

}

fb = (FloatBuffer) uniform.getValue();

  •            GLES20.glUniform2fv(loc, 1, fb);<br />
    
  •            size = fb.capacity() / 2;<br />
    
  •            GLES20.glUniform2fv(loc, size, fb);<br />
    

break;

case Vector3Array:

if (verboseLogging) {

logger.info("GLES20.glUniform3fv set Vector3Array." + uniform.getName());

}

fb = (FloatBuffer) uniform.getValue();

  •            GLES20.glUniform3fv(loc, 1, fb);<br />
    
  •            size = fb.capacity() / 3;<br />
    
  •            GLES20.glUniform3fv(loc, size, fb);<br />
    

break;

case Vector4Array:

if (verboseLogging) {

logger.info("GLES20.glUniform4fv set Vector4Array." + uniform.getName());

}

fb = (FloatBuffer) uniform.getValue();

  •            GLES20.glUniform4fv(loc, 1, fb);<br />
    
  •            size = fb.capacity() / 4;<br />
    
  •            GLES20.glUniform4fv(loc, size, fb);<br />
    

break;

case Matrix4Array:

if (verboseLogging) {

logger.info("GLES20.glUniform4fv set Matrix4Array." + uniform.getName());

}

fb = (FloatBuffer) uniform.getValue();

  •            GLES20.glUniformMatrix4fv(loc, 1, false, fb);<br />
    
  •            size = fb.capacity() / 16;<br />
    
  •            GLES20.glUniformMatrix4fv(loc, size, false, fb);<br />
    

break;

case Int:

if (verboseLogging) {

@@ -2429,8 +2441,7 @@

convertFormat(vb.getFormat()),

vb.isNormalized(),

vb.getStride(),

  •                    vb.getData());<br />
    

-

  •                    0/*vb.getData()*/);<br />
    

attribs[loc] = vb;

}

} else {

@@ -2703,7 +2714,11 @@

} else {

indices = buffers.get(Type.Index.ordinal());

}

  •    for (Entry&lt;VertexBuffer&gt; entry : buffers) {<br />
    
  •    Entry&lt;VertexBuffer&gt; table[] = buffers.getTable();<br />
    
  •    for (Entry&lt;VertexBuffer&gt; entry : table) {<br />
    
  •        if (entry == null) {<br />
    
  •            continue;<br />
    
  •        }<br />
    

VertexBuffer vb = entry.getValue();



if (vb.getBufferType() == Type.InterleavedData

diff -r 49767f5ec293 -r 9da2988ff52f engine/src/core/com/jme3/material/MatParam.java

— a/engine/src/core/com/jme3/material/MatParam.java Wed Sep 07 00:12:26 2011 +0900

+++ b/engine/src/core/com/jme3/material/MatParam.java Mon Sep 26 10:45:43 2011 +0900

@@ -38,6 +38,8 @@

import com.jme3.export.OutputCapsule;

import com.jme3.export.Savable;

import com.jme3.math.ColorRGBA;

+import com.jme3.math.Matrix3f;

+import com.jme3.math.Matrix4f;

import com.jme3.math.Quaternion;

import com.jme3.math.Vector2f;

import com.jme3.math.Vector3f;

@@ -47,7 +49,9 @@

import com.jme3.shader.VarType;

import com.jme3.texture.Texture;

import com.jme3.texture.Texture.WrapMode;

+import com.jme3.util.BufferUtils;

import java.io.IOException;

+import java.nio.FloatBuffer;



/**

  • Describes a material parameter. This is used for both defining a name and type

    @@ -62,7 +66,7 @@

    protected String prefixedName;

    protected Object value;

    protected FixedFuncBinding ffBinding;

    -
  • protected FloatBuffer multiData = null;

    /**
  • Create a new material parameter. For internal use only.

    */

    @@ -147,12 +151,145 @@

    */

    public void setValue(Object value) {

    this.value = value;
  •        switch (type){<br />
    
  •            case Matrix3:<br />
    
  •                Matrix3f m3 = (Matrix3f) value;<br />
    
  •                if (multiData == null)<br />
    
  •                    multiData = BufferUtils.createFloatBuffer(9);<br />
    

+

  •                m3.fillFloatBuffer(multiData, true);<br />
    
  •                multiData.clear();<br />
    
  •                break;<br />
    
  •            case Matrix4:<br />
    
  •                Matrix4f m4 = (Matrix4f) value;<br />
    
  •                if (multiData == null)<br />
    
  •                    multiData = BufferUtils.createFloatBuffer(16);<br />
    

+

  •                m4.fillFloatBuffer(multiData, true);<br />
    
  •                multiData.clear();<br />
    
  •                break;<br />
    
  •            case FloatArray:<br />
    
  •                float[] fa = (float[]) value;<br />
    
  •                if (multiData == null){<br />
    
  •                    multiData = BufferUtils.createFloatBuffer(fa);<br />
    
  •                }else{<br />
    
  •                    multiData = BufferUtils.ensureLargeEnough(multiData, fa.length);<br />
    
  •                }<br />
    

+

  •                multiData.put(fa);<br />
    
  •                multiData.clear();<br />
    
  •                break;<br />
    
  •            case Vector2Array:<br />
    
  •                Vector2f[] v2a = (Vector2f[]) value;<br />
    
  •                if (multiData == null){<br />
    
  •                    multiData = BufferUtils.createFloatBuffer(v2a);<br />
    
  •                } else {<br />
    
  •                    multiData = BufferUtils.ensureLargeEnough(multiData, v2a.length * 2);<br />
    
  •                }<br />
    

+

  •                for (int i = 0; i &lt; v2a.length; i++)<br />
    
  •                    BufferUtils.setInBuffer(v2a<i>, multiData, i);<br />
    

+

  •                multiData.clear();<br />
    
  •                break;<br />
    
  •            case Vector3Array:<br />
    
  •                Vector3f[] v3a = (Vector3f[]) value;<br />
    
  •                if (multiData == null){<br />
    
  •                    multiData = BufferUtils.createFloatBuffer(v3a);<br />
    
  •                } else{<br />
    
  •                    multiData = BufferUtils.ensureLargeEnough(multiData, v3a.length * 3);<br />
    
  •                }<br />
    

+

  •                for (int i = 0; i &lt; v3a.length; i++)<br />
    
  •                    BufferUtils.setInBuffer(v3a<i>, multiData, i);<br />
    

+

  •                multiData.clear();<br />
    
  •                break;<br />
    
  •            case Vector4Array:<br />
    
  •                Quaternion[] v4a = (Quaternion[]) value;<br />
    
  •                if (multiData == null){<br />
    
  •                    multiData = BufferUtils.createFloatBuffer(v4a);<br />
    
  •                } else {<br />
    
  •                    multiData = BufferUtils.ensureLargeEnough(multiData, v4a.length * 4);<br />
    
  •                }<br />
    

+

  •                for (int i = 0; i &lt; v4a.length; i++)<br />
    
  •                    BufferUtils.setInBuffer(v4a<i>, multiData, i);<br />
    

+

  •                multiData.clear();<br />
    
  •                break;<br />
    
  •            case Matrix3Array:<br />
    
  •                Matrix3f[] m3a = (Matrix3f[]) value;<br />
    

+

  •                if (multiData == null)<br />
    
  •                    multiData = BufferUtils.createFloatBuffer(m3a.length * 9);<br />
    
  •                else{<br />
    
  •                    multiData = BufferUtils.ensureLargeEnough(multiData, m3a.length * 9);<br />
    
  •                }<br />
    

+

  •                for (int i = 0; i &lt; m3a.length; i++)<br />
    
  •                    m3a<i>.fillFloatBuffer(multiData, true);<br />
    

+

  •                multiData.clear();<br />
    
  •                break;<br />
    
  •            case Matrix4Array:<br />
    
  •                Matrix4f[] m4a = (Matrix4f[]) value;<br />
    

+

  •                if (multiData == null)<br />
    
  •                    multiData = BufferUtils.createFloatBuffer(m4a.length * 16);<br />
    
  •                else{<br />
    
  •                    multiData = BufferUtils.ensureLargeEnough(multiData, m4a.length * 16);<br />
    
  •                }<br />
    

+

  •                for (int i = 0; i &lt; m4a.length; i++)<br />
    
  •                    m4a<i>.fillFloatBuffer(multiData, true);<br />
    

+

  •                multiData.clear();<br />
    
  •                break;<br />
    
  •            // Only use check if equals optimization for primitive values<br />
    
  •            case Int:<br />
    
  •            case Float:<br />
    
  •            case Boolean:<br />
    
  •                if (this.value != null &amp;&amp; this.value.equals(value))<br />
    
  •                    return;<br />
    

+

  •                this.value = value;<br />
    
  •                multiData = null;<br />
    
  •                break;<br />
    
  •            default:<br />
    
  •                this.value = value;<br />
    
  •                multiData = null;<br />
    
  •                break;<br />
    
  •        }<br />
    

}



void apply(Renderer r, Technique technique) {

TechniqueDef techDef = technique.getDef();

if (techDef.isUsingShaders()) {

  •        technique.updateUniformParam(getPrefixedName(), getVarType(), getValue(), true);<br />
    
  •        Object value;<br />
    
  •        if (multiData != null) {<br />
    
  •            value = multiData;<br />
    
  •            multiData.clear();<br />
    
  •        } else {<br />
    
  •            value = getValue();<br />
    
  •        }<br />
    
  •        technique.updateUniformParam(getPrefixedName(), getVarType(), value, true);<br />
    
  •    }<br />
    
  •    if (ffBinding != null &amp;&amp; r instanceof GL1Renderer) {<br />
    
  •        ((GL1Renderer) r).setFixedFuncBinding(ffBinding, getValue());<br />
    
  •    }<br />
    
  • }
  • void apply(Renderer r, Technique technique, int paramIndex) {
  •    TechniqueDef techDef = technique.getDef();<br />
    
  •    if (techDef.isUsingShaders()) {<br />
    
  •        Object value;<br />
    
  •        if (multiData != null) {<br />
    
  •            value = multiData;<br />
    
  •            multiData.clear();<br />
    
  •        } else {<br />
    
  •            value = getValue();<br />
    
  •        }<br />
    
  •        technique.updateUniformParam(paramIndex, getVarType(), value, true);<br />
    

}

if (ffBinding != null && r instanceof GL1Renderer) {

((GL1Renderer) r).setFixedFuncBinding(ffBinding, getValue());

diff -r 49767f5ec293 -r 9da2988ff52f engine/src/core/com/jme3/material/MatParamTexture.java

— a/engine/src/core/com/jme3/material/MatParamTexture.java Wed Sep 07 00:12:26 2011 +0900

+++ b/engine/src/core/com/jme3/material/MatParamTexture.java Mon Sep 26 10:45:43 2011 +0900

@@ -48,7 +48,14 @@

technique.updateUniformParam(getPrefixedName(), getVarType(), getUnit(), true);

}

}

-

  • @Override
  • public void apply(Renderer r, Technique technique, int paramIndex) {
  •    TechniqueDef techDef = technique.getDef();<br />
    
  •    r.setTexture(getUnit(), getTextureValue());<br />
    
  •    if (techDef.isUsingShaders()) {<br />
    
  •        technique.updateUniformParam(paramIndex, getVarType(), getUnit(), true);<br />
    
  •    }<br />
    
  • }

    @Override

    public void write(JmeExporter ex) throws IOException {

    super.write(ex);

    diff -r 49767f5ec293 -r 9da2988ff52f engine/src/core/com/jme3/material/Material.java

    — a/engine/src/core/com/jme3/material/Material.java Wed Sep 07 00:12:26 2011 +0900

    +++ b/engine/src/core/com/jme3/material/Material.java Mon Sep 26 10:45:43 2011 +0900

    @@ -61,6 +61,7 @@

    import com.jme3.util.ListMap;

    import com.jme3.util.TempVars;

    import java.io.IOException;

    +import java.nio.FloatBuffer;

    import java.util.Collection;

    import java.util.EnumSet;

    import java.util.HashMap;

    @@ -102,6 +103,7 @@

    private ListMap<String, MatParam> paramValues = new ListMap<String, MatParam>();

    private Technique technique;

    private HashMap<String, Technique> techniques = new HashMap<String, Technique>();
  • private Technique[] techniqueArray = null;

    private int nextTexUnit = 0;

    private RenderState additionalState = null;

    private RenderState mergedRenderState = new RenderState();

    @@ -210,6 +212,7 @@

    }

    mat.technique = null;

    mat.techniques = new HashMap<String, Technique>();
  •        mat.techniqueArray = null;<br />
    

mat.paramValues = new ListMap<String, MatParam>();
for (int i = 0; i < paramValues.size(); i++) {
@@ -381,7 +384,14 @@

return newName;
}
-
+ public int getParamIndex(String name) {
+ for(int i=paramValues.size()-1;i>=0;i--) {
+ if (name.equals(paramValues.getValue(i))) {
+ return i;
+ }
+ }
+ return -1;
+ }
/**
* Pass a parameter to the material shader.
*
@@ -391,19 +401,50 @@
*/
public void setParam(String name, VarType type, Object value) {
name = checkSetParam(type, name);
+ boolean techniqueUpdateNeeded = false;
+ MatParam val = getParam(name);
+// if (technique != null) {
+// technique.notifySetParam(name, type, value);
+// }

- MatParam val = getParam(name);
- if (technique != null) {
- technique.notifySetParam(name, type, value);
- }
if (val == null) {
MatParam paramDef = def.getMaterialParam(name);
- paramValues.put(name, new MatParam(type, name, value, paramDef.getFixedFuncBinding()));
+ val = new MatParam(type, name, value, paramDef.getFixedFuncBinding());
+ val.setValue(value);
+ paramValues.put(name, val);
+ techniqueUpdateNeeded = true;
} else {
val.setValue(value);
}
+ Object value2 = val.multiData != null ? val.multiData : value;
+ if (techniqueArray == null) {
+ setTechniqueArray();
+ }
+ for(Technique tech : techniqueArray) {
+ tech.notifySetParam(name, type, value2);
+ }
+ if (techniqueUpdateNeeded) {
+ for(Technique tech : techniqueArray) {
+ tech.setNeedReload(true);
+ }
+ }
}
+ public void setParam(int paramIndex, VarType type, Object value) {
+ MatParam val = paramValues.getValue(paramIndex);
+// if (technique != null) {
+// technique.notifySetParam(name, type, value);
+// }

+ val.setValue(value);
+ Object value2 = val.multiData != null ? val.multiData : value;
+ if (techniqueArray == null) {
+ setTechniqueArray();
+ }
+ paramValues.getValue(0);
+ for(Technique tech : techniqueArray) {
+ tech.notifySetParam(paramIndex, type, value2);
+ }
+ }
/**
* Clear a parameter from this material. The parameter must exist
* @param name the name of the parameter to clear
@@ -414,9 +455,16 @@

MatParam matParam = getParam(name);
if (matParam != null) {
- paramValues.remove(name);
- if (technique != null) {
- technique.notifyClearParam(name);
+// paramValues.remove(name);
+ paramValues.put(name, null);
+// if (technique != null) {
+// technique.notifyClearParam(name);
+// }
+ if (techniqueArray == null) {
+ setTechniqueArray();
+ }
+ for(Technique tech : techniqueArray) {
+ tech.notifyClearParam(name);
}
if (matParam instanceof MatParamTexture) {
int texUnit = ((MatParamTexture) matParam).getUnit();
@@ -445,7 +493,8 @@
}

int texUnit = val.getUnit();
- paramValues.remove(name);
+// paramValues.remove(name);
+ paramValues.put(name, null);
nextTexUnit--;
for (MatParam param : paramValues.values()) {
if (param instanceof MatParamTexture) {
@@ -472,17 +521,29 @@
if (value == null) {
throw new IllegalArgumentException();
}
-
+ boolean techniqueUpdateNeeded = false;
name = checkSetParam(type, name);
MatParamTexture val = getTextureParam(name);
if (val == null) {
paramValues.put(name, new MatParamTexture(type, name, value, nextTexUnit++));
+ techniqueUpdateNeeded = true;
} else {
val.setTextureValue(value);
}

- if (technique != null) {
- technique.notifySetParam(name, type, nextTexUnit - 1);
+// if (technique != null) {
+// technique.notifySetParam(name, type, nextTexUnit - 1);
+// }
+ if (techniqueArray == null) {
+ setTechniqueArray();
+ }
+ for(Technique tech : techniqueArray) {
+ tech.notifySetParam(name, type, nextTexUnit - 1);
+ }
+ if (techniqueUpdateNeeded) {
+ for(Technique tech : techniqueArray) {
+ tech.setNeedReload(true);
+ }
}

// need to recompute sort ID
@@ -843,7 +904,7 @@
if (rendererCaps.containsAll(techDef.getRequiredCaps())) {
// use the first one that supports all the caps
tech = new Technique(this, techDef);
- techniques.put(name, tech);
+ putTechnique(name, tech);
break;
}
lastTech = techDef;
@@ -868,7 +929,7 @@
}

tech = new Technique(this, techDef);
- techniques.put(name, tech);
+ putTechnique(name, tech);
}
} else if (technique == tech) {
// attempting to switch to an already
@@ -877,7 +938,9 @@
}

technique = tech;
- tech.makeCurrent(def.getAssetManager());
+ if (technique.isNeedReload()) {
+ tech.makeCurrent(def.getAssetManager(), paramValues);
+ }

// shader was changed
sortingId = -1;
@@ -894,7 +957,7 @@
selectTechnique("Default", rm);
}
} else if (technique.isNeedReload()) {
- technique.makeCurrent(def.getAssetManager());
+ technique.makeCurrent(def.getAssetManager(), paramValues);
}
}

@@ -982,7 +1045,11 @@
}
}

-
+ boolean needReloadParams = false;
+ if (technique.isNeedReload()) {
+ technique.makeCurrent(def.getAssetManager(), paramValues);
+ needReloadParams = true;
+ }
// update camera and world matrices
// NOTE: setWorldTransform should have been called already
if (techDef.isUsingShaders()) {
@@ -992,9 +1059,11 @@
}

// setup textures and uniforms
- for (int i = 0; i < paramValues.size(); i++) {
+ for (int i = paramValues.size()-1; i >=0 ; i--) {
MatParam param = paramValues.getValue(i);
- param.apply(r, technique);
+ if (param != null) {
+ param.apply(r, technique, i);
+ }
}

Shader shader = technique.getShader();
@@ -1105,7 +1174,7 @@
// not available
for (MatParam param : def.getMaterialParams()){
if (param.getValue() != null && paramValues.get(param.getName()) == null){
- setParam(param.getName(), param.getVarType(), param.getValue());
+ setParam(param.getPrefixedName(), param.getVarType(), param.getValue());
}
}
}
@@ -1116,4 +1185,11 @@
setBoolean("SeparateTexCoord", true);
}
}
+ void putTechnique(String name, Technique tech) {
+ techniques.put(name, tech);
+ techniqueArray = null;
+ }
+ void setTechniqueArray() {
+ techniqueArray = techniques.values().toArray(new Technique[techniques.size()]);
+ }
}
diff -r 49767f5ec293 -r 9da2988ff52f engine/src/core/com/jme3/material/MaterialDef.java
--- a/engine/src/core/com/jme3/material/MaterialDef.java Wed Sep 07 00:12:26 2011 +0900
+++ b/engine/src/core/com/jme3/material/MaterialDef.java Mon Sep 26 10:45:43 2011 +0900
@@ -34,6 +34,7 @@

import com.jme3.asset.AssetManager;
import com.jme3.shader.VarType;
+import com.jme3.util.ListMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -57,7 +58,8 @@

private List<TechniqueDef> defaultTechs;
private Map<String, TechniqueDef> techniques;
- private Map<String, MatParam> matParams;
+ private ListMap<String, MatParam> matParams;
+

/**
* Serialization only. Do not use.
@@ -75,7 +77,7 @@
this.assetManager = assetManager;
this.name = name;
techniques = new HashMap<String, TechniqueDef>();
- matParams = new HashMap<String, MatParam>();
+ matParams = new ListMap<String, MatParam>();
defaultTechs = new ArrayList<TechniqueDef>();
logger.log(Level.INFO, "Loaded material definition: {0}", name);
}
@@ -191,4 +193,8 @@
return techniques.get(name);
}

+ public ListMap<String, MatParam> getMatParams() {
+ return matParams;
+ }
+
}
diff -r 49767f5ec293 -r 9da2988ff52f engine/src/core/com/jme3/material/Technique.java
--- a/engine/src/core/com/jme3/material/Technique.java Wed Sep 07 00:12:26 2011 +0900
+++ b/engine/src/core/com/jme3/material/Technique.java Mon Sep 26 10:45:43 2011 +0900
@@ -43,6 +43,7 @@
import com.jme3.shader.Uniform;

import com.jme3.shader.UniformBinding;

import com.jme3.shader.VarType;

+import com.jme3.util.ListMap;

import java.io.IOException;

import java.util.ArrayList;

import java.util.Collection;

@@ -61,6 +62,11 @@
private DefineList defines;

private Shader shader;

private boolean needReload = true;

+

+ ListMap<String, MatParam> paramValues;

+

+ Uniform[] uniformArray;

+ String[] defineNameArray;



/**

* Creates a new technique instance that implements the given

@@ -75,6 +81,12 @@
if (def.isUsingShaders()) {

this.worldBindUniforms = new ArrayList<Uniform>();

this.defines = new DefineList();

+ for (MatParam param : owner.getParams()) {

+ String defineName = def.getShaderParamDefine(param.getName());

+ if (defineName != null) {

+ defines.set(defineName, param.getVarType(), param.getValue());

+ }

+ }

}

}



@@ -126,8 +138,18 @@
defines.set(defineName, type, value);

needReload = true;

}

- if (shader != null) {

- updateUniformParam(paramName, type, value);

+// if (shader != null) {

+// updateUniformParam(paramName, type, value);

+// }

+ }

+ void notifySetParam(int paramIndex, VarType type, Object value) {

+ if (defineNameArray == null) {

+ return;

+ }

+ String defineName = defineNameArray[paramIndex];

+ if (defineName != null) {

+ defines.set(defineName, type, value);

+ needReload = true;

}

}



@@ -145,6 +167,26 @@
paramName = "m_" + paramName;

}

shader.removeUniform(paramName);

+ for(int i=uniformArray.length-1;i>=0;i--) {

+ if (paramName.equals(uniformArray.getName())) {

+ uniformArray = null;

+ }

+ }

+ }

+ }

+ void notifyClearParam(int paramIndex) {

+ if (shader == null) {

+ return;

+ }

+ String defineName = defineNameArray[paramIndex];

+ if (defineName != null) {

+ defines.remove(defineName);

+ needReload = true;

+ }

+ if (shader != null) {

+ String paramName = uniformArray[paramIndex].getName();

+ shader.removeUniform(paramName);

+ uniformArray[paramIndex] = null;

}

}



@@ -168,11 +210,37 @@
}

// u.setLastChanger(owner);

}

+ void updateUniformParam(int paramIndex, VarType type, Object value, boolean ifNotOwner) {

+ Uniform u = uniformArray[paramIndex];

+ if (u == null) {

+ u = shader.getUniform(paramValues.getValue(paramIndex).getPrefixedName());

+ uniformArray[paramIndex] = u;

+ }

+

+// if (ifNotOwner && u.getLastChanger() == owner)

+// return;

+

+ switch (type) {

+ case Texture2D: // fall intentional

+ case Texture3D:

+ case TextureArray:

+ case TextureCubeMap:

+ case Int:

+ u.setValue(VarType.Int, value);

+ break;

+ default:

+ u.setValue(type, value);

+ break;

+ }

+// u.setLastChanger(owner);

+ }



void updateUniformParam(String paramName, VarType type, Object value) {

updateUniformParam(paramName, type, value, false);

}

-

+ void updateUniformParam(int paramIndex, VarType type, Object value) {

+ updateUniformParam(paramIndex, type, value, false);

+ }

/**

* Returns true if the technique must be reloaded.

* <p>

@@ -186,32 +254,51 @@
return needReload;

}



+ public void setNeedReload(boolean needReload) {

+ this.needReload = needReload;

+ }

+

/**

* Prepares the technique for use by loading the shader and setting

* the proper defines based on material parameters.

*

* @param assetManager The asset manager to use for loading shaders.

*/

- public void makeCurrent(AssetManager assetManager) {

+ public void makeCurrent(AssetManager assetManager, ListMap<String, MatParam> paramValues) {

+ this.paramValues = paramValues;

+ defineNameArray = new String[paramValues.size()];

+ for(int i=paramValues.size()-1;i>=0;i--) {

+ defineNameArray = def.getShaderParamDefine(paramValues.getValue(i).getName());

+ }

// check if reload is needed..

if (def.isUsingShaders()) {

- DefineList newDefines = new DefineList();

- Collection<MatParam> params = owner.getParams();

- for (MatParam param : params) {

- String defineName = def.getShaderParamDefine(param.getName());

- if (defineName != null) {

- newDefines.set(defineName, param.getVarType(), param.getValue());

- }

- }

+// DefineList newDefines = new DefineList();

+// Collection<MatParam> params = owner.getParams();

+// for (MatParam param : params) {

+// String defineName = def.getShaderParamDefine(param.getName());

+// if (defineName != null) {

+// newDefines.set(defineName, param.getVarType(), param.getValue());

+// }

+// }



- if (!needReload && defines.getCompiled().equals(newDefines.getCompiled())) {

- newDefines = null;

+ if (!needReload /*&& defines.getCompiled().equals(newDefines.getCompiled())*/) {

+// newDefines = null;

// defines have not been changed..

} else {

- defines.clear();

- defines.addFrom(newDefines);

+// defines.clear();

+// defines.addFrom(newDefines);

// defines changed, recompile needed

loadShader(assetManager);

+ if (shader != null) {

+ uniformArray = new Uniform[paramValues.size()];

+// for(int i=paramValues.size()-1;i>=0;i--) {

+// uniformArray = shader.getUniform(paramValues.getValue(i).getPrefixedName());

+// }

+ defineNameArray = new String[paramValues.size()];

+ for(int i=paramValues.size()-1;i>=0;i--) {

+ defineNameArray = def.getShaderParamDefine(paramValues.getValue(i).getName());

+ }

+ }

}

}

}

diff -r 49767f5ec293 -r 9da2988ff52f engine/src/core/com/jme3/renderer/RenderManager.java
--- a/engine/src/core/com/jme3/renderer/RenderManager.java Wed Sep 07 00:12:26 2011 +0900
+++ b/engine/src/core/com/jme3/renderer/RenderManager.java Mon Sep 26 10:45:43 2011 +0900
@@ -1092,7 +1092,10 @@
}

if (processors != null) {
- for (SceneProcessor proc : processors) {
+ int procSize = processors.size();
+// for (SceneProcessor proc : processors) {
+ for(int i=0;i<procSize;i++) {
+ SceneProcessor proc = processors.get(i);
if (!proc.isInitialized()) {
proc.initialize(this, vp);
}
@@ -1117,7 +1120,10 @@
}

if (processors != null) {
- for (SceneProcessor proc : processors) {
+ int procSize = processors.size();
+// for (SceneProcessor proc : processors) {
+ for(int i=0;i<procSize;i++) {
+ SceneProcessor proc = processors.get(i);
proc.postQueue(vp.getQueue());
}
}
@@ -1125,7 +1131,10 @@
flushQueue(vp);

if (processors != null) {
- for (SceneProcessor proc : processors) {
+ int procSize = processors.size();
+// for (SceneProcessor proc : processors) {
+ for(int i=0;i<procSize;i++) {
+ SceneProcessor proc = processors.get(i);
proc.postFrame(vp.getOutputFrameBuffer());
}
}
diff -r 49767f5ec293 -r 9da2988ff52f engine/src/core/com/jme3/renderer/Statistics.java
--- a/engine/src/core/com/jme3/renderer/Statistics.java Wed Sep 07 00:12:26 2011 +0900
+++ b/engine/src/core/com/jme3/renderer/Statistics.java Mon Sep 26 10:45:43 2011 +0900
@@ -136,8 +136,8 @@
public void onShaderUse(Shader shader, boolean wasSwitched){
assert shader.id >= 1;

- if (!shadersUsed.contains(shader.id))
- shadersUsed.add(shader.id);
+// if (!shadersUsed.contains(shader.id))
+// shadersUsed.add(shader.id);

if (wasSwitched)
numShaderSwitches++;
@@ -159,8 +159,8 @@
public void onTextureUse(Image image, boolean wasSwitched){
assert image.id >= 1;

- if (!texturesUsed.contains(image.id))
- texturesUsed.add(image.id);
+// if (!texturesUsed.contains(image.id))
+// texturesUsed.add(image.id);

if (wasSwitched)
numTextureBinds ++;
@@ -176,8 +176,8 @@
if (fb != null){
assert fb.id >= 1;

- if (!fbosUsed.contains(fb.id))
- fbosUsed.add(fb.id);
+// if (!fbosUsed.contains(fb.id))
+// fbosUsed.add(fb.id);
}

if (wasSwitched)
diff -r 49767f5ec293 -r 9da2988ff52f engine/src/core/com/jme3/scene/Mesh.java
--- a/engine/src/core/com/jme3/scene/Mesh.java Wed Sep 07 00:12:26 2011 +0900
+++ b/engine/src/core/com/jme3/scene/Mesh.java Mon Sep 26 10:45:43 2011 +0900
@@ -922,10 +922,13 @@
}

public void setBuffer(VertexBuffer vb){
- if (buffers.containsKey(vb.getBufferType().ordinal()))
- throw new IllegalArgumentException("Buffer type already set: "+vb.getBufferType());
+// if (buffers.containsKey(vb.getBufferType().ordinal()))
+// throw new IllegalArgumentException("Buffer type already set: "+vb.getBufferType());

- buffers.put(vb.getBufferType().ordinal(), vb);
+ VertexBuffer vb2 = buffers.put(vb.getBufferType().ordinal(), vb);
+ if (vb2 != null) {
+ buffersList.remove(vb2);
+ }
buffersList.add(vb);
updateCounts();
}
diff -r 49767f5ec293 -r 9da2988ff52f engine/src/core/com/jme3/shader/Uniform.java
--- a/engine/src/core/com/jme3/shader/Uniform.java Wed Sep 07 00:12:26 2011 +0900
+++ b/engine/src/core/com/jme3/shader/Uniform.java Mon Sep 26 10:45:43 2011 +0900
@@ -275,119 +275,122 @@
throw new NullPointerException();

setByCurrentMaterial = true;
+ if (value instanceof FloatBuffer) {
+ multiData = (FloatBuffer)value;
+ this.value = multiData;
+ } else {
+ switch (type){
+ case Matrix3:
+ Matrix3f m3 = (Matrix3f) value;
+ if (multiData == null)
+ multiData = BufferUtils.createFloatBuffer(9);

- switch (type){
- case Matrix3:
- Matrix3f m3 = (Matrix3f) value;
- if (multiData == null)
- multiData = BufferUtils.createFloatBuffer(9);
-
- m3.fillFloatBuffer(multiData, true);
- multiData.clear();
- break;
- case Matrix4:
- Matrix4f m4 = (Matrix4f) value;
- if (multiData == null)
- multiData = BufferUtils.createFloatBuffer(16);
-
- m4.fillFloatBuffer(multiData, true);
- multiData.clear();
- break;
- case FloatArray:
- float[] fa = (float[]) value;
- if (multiData == null){
- multiData = BufferUtils.createFloatBuffer(fa);
- }else{
- multiData = BufferUtils.ensureLargeEnough(multiData, fa.length);
- }
-
- multiData.put(fa);
- multiData.clear();
- break;
- case Vector2Array:
- Vector2f[] v2a = (Vector2f[]) value;
- if (multiData == null){
- multiData = BufferUtils.createFloatBuffer(v2a);
- } else {
- multiData = BufferUtils.ensureLargeEnough(multiData, v2a.length * 2);
- }
+ m3.fillFloatBuffer(multiData, true);
+ multiData.clear();
+ break;
+ case Matrix4:
+ Matrix4f m4 = (Matrix4f) value;
+ if (multiData == null)
+ multiData = BufferUtils.createFloatBuffer(16);

- for (int i = 0; i < v2a.length; i++)
- BufferUtils.setInBuffer(v2a, multiData, i);
-
- multiData.clear();
- break;
- case Vector3Array:
- Vector3f[] v3a = (Vector3f[]) value;
- if (multiData == null){
- multiData = BufferUtils.createFloatBuffer(v3a);
- } else{
- multiData = BufferUtils.ensureLargeEnough(multiData, v3a.length * 3);
- }
-
- for (int i = 0; i < v3a.length; i++)
- BufferUtils.setInBuffer(v3a, multiData, i);
+ m4.fillFloatBuffer(multiData, true);
+ multiData.clear();
+ break;
+ case FloatArray:
+ float[] fa = (float[]) value;
+ if (multiData == null){
+ multiData = BufferUtils.createFloatBuffer(fa);
+ }else{
+ multiData = BufferUtils.ensureLargeEnough(multiData, fa.length);
+ }

- multiData.clear();
- break;
- case Vector4Array:
- Quaternion[] v4a = (Quaternion[]) value;
- if (multiData == null){
- multiData = BufferUtils.createFloatBuffer(v4a);
- } else {
- multiData = BufferUtils.ensureLargeEnough(multiData, v4a.length * 4);
- }
-
- for (int i = 0; i < v4a.length; i++)
- BufferUtils.setInBuffer(v4a, multiData, i);
+ multiData.put(fa);
+ multiData.clear();
+ break;
+ case Vector2Array:
+ Vector2f[] v2a = (Vector2f[]) value;
+ if (multiData == null){
+ multiData = BufferUtils.createFloatBuffer(v2a);
+ } else {
+ multiData = BufferUtils.ensureLargeEnough(multiData, v2a.length * 2);
+ }

- multiData.clear();
- break;
- case Matrix3Array:
- Matrix3f[] m3a = (Matrix3f[]) value;
+ for (int i = 0; i < v2a.length; i++)
+ BufferUtils.setInBuffer(v2a, multiData, i);

- if (multiData == null)
- multiData = BufferUtils.createFloatBuffer(m3a.length * 9);
- else{
- multiData = BufferUtils.ensureLargeEnough(multiData, m3a.length * 9);
- }
+ multiData.clear();
+ break;
+ case Vector3Array:
+ Vector3f[] v3a = (Vector3f[]) value;
+ if (multiData == null){
+ multiData = BufferUtils.createFloatBuffer(v3a);
+ } else{
+ multiData = BufferUtils.ensureLargeEnough(multiData, v3a.length * 3);
+ }

- for (int i = 0; i < m3a.length; i++)
- m3a.fillFloatBuffer(multiData, true);
-
- multiData.clear();
- break;
- case Matrix4Array:
- Matrix4f[] m4a = (Matrix4f[]) value;
+ for (int i = 0; i < v3a.length; i++)
+ BufferUtils.setInBuffer(v3a, multiData, i);

- if (multiData == null)
- multiData = BufferUtils.createFloatBuffer(m4a.length * 16);
- else{
- multiData = BufferUtils.ensureLargeEnough(multiData, m4a.length * 16);
- }
+ multiData.clear();
+ break;
+ case Vector4Array:
+ Quaternion[] v4a = (Quaternion[]) value;
+ if (multiData == null){
+ multiData = BufferUtils.createFloatBuffer(v4a);
+ } else {
+ multiData = BufferUtils.ensureLargeEnough(multiData, v4a.length * 4);
+ }

- for (int i = 0; i < m4a.length; i++)
- m4a.fillFloatBuffer(multiData, true);
-
- multiData.clear();
- break;
- // Only use check if equals optimization for primitive values
- case Int:
- case Float:
- case Boolean:
- if (this.value != null && this.value.equals(value))
- return;
+ for (int i = 0; i < v4a.length; i++)
+ BufferUtils.setInBuffer(v4a, multiData, i);

- this.value = value;
- break;
- default:
- this.value = value;
- break;
- }
+ multiData.clear();
+ break;
+ case Matrix3Array:
+ Matrix3f[] m3a = (Matrix3f[]) value;

- if (multiData != null)
- this.value = multiData;
-
+ if (multiData == null)
+ multiData = BufferUtils.createFloatBuffer(m3a.length * 9);
+ else{
+ multiData = BufferUtils.ensureLargeEnough(multiData, m3a.length * 9);
+ }
+
+ for (int i = 0; i < m3a.length; i++)
+ m3a.fillFloatBuffer(multiData, true);
+
+ multiData.clear();
+ break;
+ case Matrix4Array:
+ Matrix4f[] m4a = (Matrix4f[]) value;
+
+ if (multiData == null)
+ multiData = BufferUtils.createFloatBuffer(m4a.length * 16);
+ else{
+ multiData = BufferUtils.ensureLargeEnough(multiData, m4a.length * 16);
+ }
+
+ for (int i = 0; i < m4a.length; i++)
+ m4a.fillFloatBuffer(multiData, true);
+
+ multiData.clear();
+ break;
+ // Only use check if equals optimization for primitive values
+ case Int:
+ case Float:
+ case Boolean:
+ if (this.value != null && this.value.equals(value))
+ return;
+
+ this.value = value;
+ break;
+ default:
+ this.value = value;
+ break;
+ }
+
+ if (multiData != null)
+ this.value = multiData;
+ }
varType = type;
updateNeeded = true;
}
@@ -435,4 +438,12 @@
updateNeeded = true;
}

+ public FloatBuffer getMultiData() {
+ return multiData;
+ }
+
+ public void setMultiData(FloatBuffer multiData) {
+ this.multiData = multiData;
+ }
+
}
diff -r 49767f5ec293 -r 9da2988ff52f engine/src/core/com/jme3/util/IntMap.java
--- a/engine/src/core/com/jme3/util/IntMap.java Wed Sep 07 00:12:26 2011 +0900
+++ b/engine/src/core/com/jme3/util/IntMap.java Mon Sep 26 10:45:43 2011 +0900
@@ -164,7 +164,9 @@
}
return null;
}
-
+ public Entry<T>[] getTable() {
+ return table;
+ }
public T remove(int key) {
int index = key & mask;
Entry prev = table[index];
@@ -197,9 +199,11 @@
}
size = 0;
}
-
+ private IntMapIterator intMapIterator = new IntMapIterator();
public Iterator<Entry<T>> iterator() {
- return (Iterator<Entry<T>>) new IntMapIterator();
+// return (Iterator<Entry<T>>) new IntMapIterator();
+ intMapIterator.init();
+ return intMapIterator;
}

final class IntMapIterator implements Iterator<Entry<T>> {
@@ -220,6 +224,11 @@
private int el = 0;

public IntMapIterator() {
+// cur = table[0];
+ }
+ public void init() {
+ idx = 0;
+ el = 0;
cur = table[0];
}
[/patch]
3 Likes

RenderManager.java

[java]

for (SceneProcessor proc : processors) {

proc.postQueue(vp.getQueue());

}

[/java]

This code creates iterator object.I changed it:

[java]

int procSize = processors.size();

// for (SceneProcessor proc : processors) {

for(int i=0;i<procSize;i++) {

SceneProcessor proc = processors.get(i);

proc.postQueue(vp.getQueue());

}

[/java]

IntMap.java

[java]

public Iterator<Entry<T>> iterator() {

return (Iterator<Entry<T>>) new IntMapIterator();

}

[/java]

This code creates iterator object.I changed it:

[java]

private IntMapIterator intMapIterator = new IntMapIterator();

public Iterator<Entry<T>> iterator() {

// return (Iterator<Entry<T>>) new IntMapIterator();

intMapIterator.init();

return intMapIterator;

}

[/java]

Technique.java

[java]

public void makeCurrent(AssetManager assetManager) {

// check if reload is needed…

if (def.isUsingShaders()) {

DefineList newDefines = new DefineList();

Collection<MatParam> params = owner.getParams();

for (MatParam param : params) {

String defineName = def.getShaderParamDefine(param.getName());

if (defineName != null) {

newDefines.set(defineName, param.getVarType(), param.getValue());

}

}



if (!needReload && defines.getCompiled().equals(newDefines.getCompiled())) {

newDefines = null;

// defines have not been changed…

} else {

defines.clear();

defines.addFrom(newDefines);

// defines changed, recompile needed

loadShader(assetManager);

}

}

}

[/java]

This code creates 5000 TreeMap$Entry instances per one second!

I removed that and I added this in Material#setParam.

[java]

for(Technique tech : techniqueArray) {

tech.notifySetParam(paramIndex, type, value2);

}

[/java]



function notifySetParam

[java]

void notifySetParam(String paramName, VarType type, Object value) {

String defineName = def.getShaderParamDefine(paramName);

if (defineName != null) {

defines.set(defineName, type, value);

needReload = true;

}

if (shader != null) {

updateUniformParam(paramName, type, value);

}

}

[/java]

“updateUniformParam(paramName, type, value);”

This is a bug and unnecessary.

updateUniformParam needs prefixed name, but paramName is not prefixed.

It’s unnecessary because updateUniformParam called from MatParam#apply.

I simply removed that.





Uniform.java

setValue function sets multiData per all parameters, frames, materials, techniques.

It’s big waste.

I moved it to MatParam and set once when material parameter changed.

From 17.5 to 27.7, that’s impressive!

oh cool, thanks!

Technique.java

[java]

void updateUniformParam(String paramName, VarType type, Object value, boolean ifNotOwner) {

Uniform u = shader.getUniform(paramName);



// if (ifNotOwner && u.getLastChanger() == owner)

// return;



switch (type) {

case Texture2D: // fall intentional

case Texture3D:

case TextureArray:

case TextureCubeMap:

case Int:

u.setValue(VarType.Int, value);

break;

default:

u.setValue(type, value);

break;

}

// u.setLastChanger(owner);

}

[/java]



This code is slow because it uses HashMap.

I changed it to array.



[java]

void updateUniformParam(int paramIndex, VarType type, Object value, boolean ifNotOwner) {

Uniform u = uniformArray[paramIndex];

if (u == null) {

u = shader.getUniform(paramValues.getValue(paramIndex).getPrefixedName());

uniformArray[paramIndex] = u;

}



// if (ifNotOwner && u.getLastChanger() == owner)

// return;



switch (type) {

case Texture2D: // fall intentional

case Texture3D:

case TextureArray:

case TextureCubeMap:

case Int:

u.setValue(VarType.Int, value);

break;

default:

u.setValue(type, value);

break;

}

// u.setLastChanger(owner);

}

[/java]



This change improved performance 22.895 fps to 27.738 fps.

(see results of 3rd step and 4th step)