From 87ff90fb03216737df70ff83246664b7fba2663e Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Mon, 22 Aug 2011 16:38:45 +0200
Subject: Fix regression of commit 6c346d98f04e2355210960fe9ffde47432f04d62,
 where VBO/attribute binding wasn't updated (VBO data written, shader
 change/switch attribute on same location) ; Optimized interleaved GLSL VBO
 binding, hence split up GLArrayHandler syncData/enableState

---
 .../com/jogamp/opengl/util/GLArrayDataClient.java  |   8 +-
 .../com/jogamp/opengl/util/GLArrayHandler.java     |  14 ++-
 .../com/jogamp/opengl/util/glsl/ShaderState.java   | 103 ++++++++++++++-------
 .../jogamp/opengl/util/GLDataArrayHandler.java     |   9 +-
 .../jogamp/opengl/util/GLFixedArrayHandler.java    |  54 +++++------
 .../opengl/util/GLFixedArrayHandlerFlat.java       |  40 ++++----
 .../util/GLFixedArrayHandlerInterleaved.java       |  18 ++--
 .../jogamp/opengl/util/glsl/GLSLArrayHandler.java  |  38 ++++++--
 .../opengl/util/glsl/GLSLArrayHandlerFlat.java     |  14 ++-
 .../util/glsl/GLSLArrayHandlerInterleaved.java     |  46 +++++++--
 .../test/junit/jogl/glsl/GLSLMiscHelper.java       |   2 +-
 11 files changed, 230 insertions(+), 116 deletions(-)

(limited to 'src')

diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataClient.java b/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataClient.java
index 13c61766e..3600081bc 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataClient.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataClient.java
@@ -162,7 +162,13 @@ public class GLArrayDataClient extends GLArrayDataWrapper implements GLArrayData
             // init/generate VBO name if not done yet
             init_vbo(gl);
         }
-        glArrayHandler.enableBuffer(gl, enable);
+        if(enable) {
+            glArrayHandler.syncData(gl, true);
+            glArrayHandler.enableState(gl, true);
+        } else {
+            glArrayHandler.enableState(gl, false);
+            glArrayHandler.syncData(gl, false);
+        }
         bufferEnabled = enable;
     }
   }
diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLArrayHandler.java b/src/jogl/classes/com/jogamp/opengl/util/GLArrayHandler.java
index ee80c4299..b30e220bd 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/GLArrayHandler.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/GLArrayHandler.java
@@ -11,13 +11,21 @@ import javax.media.opengl.*;
 public interface GLArrayHandler {
 
   /**
-   * Implementation shall ensure the buffers data is synchronized to the GPU
-   * and the array state is enabled.
+   * Implementation shall associate the data with the array
+   * and synchronize the data with the GPU.
+   * 
+   * @param gl current GL object
+   * @param enable true if array data shall be valid, otherwise false.
+   */
+  public void syncData(GL gl, boolean enable);
+  
+  /**
+   * Implementation shall enable or disable the array state.
    * 
    * @param gl current GL object
    * @param enable true if array shall be enabled, otherwise false.
    */
-  public void enableBuffer(GL gl, boolean enable);
+  public void enableState(GL gl, boolean enable);
   
   /**
    * Supporting interleaved arrays, where sub handlers may handle 
diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java
index e8a547057..a2a012e08 100644
--- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java
+++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java
@@ -303,7 +303,7 @@ public class ShaderState {
      * @see #getAttribLocation(GL2ES2, String)
      * @see GL2ES2#glGetAttribLocation(int, String)
      */
-    public int getAttribLocation(String name) {
+    public int getCachedAttribLocation(String name) {
         Integer idx = (Integer) activeAttribLocationMap.get(name);
         return (null!=idx)?idx.intValue():-1;
     }
@@ -346,7 +346,7 @@ public class ShaderState {
      */
     public void ownAttribute(GLArrayData attribute, boolean own) {
         if(own) {
-            final int location = getAttribLocation(attribute.getName());
+            final int location = getCachedAttribLocation(attribute.getName());
             if(0<=location) {
                 attribute.setLocation(location);
             }
@@ -363,7 +363,7 @@ public class ShaderState {
     /**
      * Binds a shader attribute to a location.
      * Multiple names can be bound to one location.
-     * The value will be cached and can be retrieved via {@link #getAttribLocation(String)}
+     * The value will be cached and can be retrieved via {@link #getCachedAttribLocation(String)}
      * before or after linking.
      *
      * @throws GLException if no program is attached
@@ -371,7 +371,7 @@ public class ShaderState {
      * 
      * @see javax.media.opengl.GL2ES2#glBindAttribLocation(int, int, String)
      * @see #getAttribLocation(GL2ES2, String)
-     * @see #getAttribLocation(String)
+     * @see #getCachedAttribLocation(String)
      */
     public void bindAttribLocation(GL2ES2 gl, int location, String name) {
         if(null==shaderProgram) throw new GLException("No program is attached");
@@ -384,7 +384,7 @@ public class ShaderState {
     /**
      * Binds a shader {@link GLArrayData} attribute to a location.
      * Multiple names can be bound to one location.
-     * The value will be cached and can be retrieved via {@link #getAttribLocation(String)}
+     * The value will be cached and can be retrieved via {@link #getCachedAttribLocation(String)}
      * and {@link #getAttribute(String)}before or after linking.
      * The {@link GLArrayData}'s location will be set as well.
      *
@@ -393,7 +393,7 @@ public class ShaderState {
      * 
      * @see javax.media.opengl.GL2ES2#glBindAttribLocation(int, int, String)
      * @see #getAttribLocation(GL2ES2, String)
-     * @see #getAttribLocation(String)
+     * @see #getCachedAttribLocation(String)
      * @see #getAttribute(String)
      */
     public void bindAttribLocation(GL2ES2 gl, int location, GLArrayData data) {
@@ -403,24 +403,24 @@ public class ShaderState {
     }
 
     /**
-     * Gets the location of a shader attribute,
-     * either the cached value {@link #getAttribLocation(String)} if valid or
-     * the retrieved one {@link GL2ES2#glGetAttribLocation(int, String)}.
-     * In the latter case the value will be cached.
+     * Gets the location of a shader attribute.<br>
+     * Uses either the cached value {@link #getCachedAttribLocation(String)} if valid,
+     * or the GLSL queried via {@link GL2ES2#glGetAttribLocation(int, String)}.<br>
+     * The location will be cached.
      *
      * @return -1 if there is no such attribute available, 
      *         otherwise >= 0
      * @throws GLException if no program is attached
      * @throws GLException if the program is not linked and no location was cached.
      *
-     * @see #getAttribLocation(String)
+     * @see #getCachedAttribLocation(String)
      * @see #bindAttribLocation(GL2ES2, int, GLArrayData)
      * @see #bindAttribLocation(GL2ES2, int, String)
      * @see GL2ES2#glGetAttribLocation(int, String)
      */
     public int getAttribLocation(GL2ES2 gl, String name) {
         if(null==shaderProgram) throw new GLException("No program is attached");
-        int location = getAttribLocation(name);
+        int location = getCachedAttribLocation(name);
         if(0>location) {
             if(!shaderProgram.linked()) throw new GLException("Program is not linked");
             location = gl.glGetAttribLocation(shaderProgram.program(), name);
@@ -439,11 +439,11 @@ public class ShaderState {
     }
 
     /**
-     * Gets the location of a shader attribute,
-     * either the cached value {@link #getAttribLocation(String)} if valid or
-     * the retrieved one {@link GL2ES2#glGetAttribLocation(int, String)}.
-     * In the latter case the value will be cached.
-     * The {@link GLArrayData}'s location will be set as well.
+     * Validates and returns the location of a shader attribute.<br>
+     * Uses either the cached value {@link #getCachedAttribLocation(String)} if valid, 
+     * or the GLSL queried via {@link GL2ES2#glGetAttribLocation(int, String)}.<br>
+     * The location will be cached and set in the  
+     * {@link GLArrayData} object.
      *
      * @return -1 if there is no such attribute available, 
      *         otherwise >= 0
@@ -451,7 +451,7 @@ public class ShaderState {
      * @throws GLException if no program is attached
      * @throws GLException if the program is not linked and no location was cached.
      *
-     * @see #getAttribLocation(String)
+     * @see #getCachedAttribLocation(String)
      * @see #bindAttribLocation(GL2ES2, int, GLArrayData)
      * @see #bindAttribLocation(GL2ES2, int, String)
      * @see GL2ES2#glGetAttribLocation(int, String)
@@ -646,11 +646,7 @@ public class ShaderState {
         int location = data.getLocation();
         if(0 > location) {
             location = getAttribLocation(gl, data);
-        } /* else { 
-            done via enable ..
-            // ensure data is the current bound one
-            activeAttribDataMap.put(data.getName(), data);             
-        } */
+        } 
         if(0 <= location) {
             // only pass the data, if the attribute exists in the current shader
             if(DEBUG) {
@@ -804,6 +800,17 @@ public class ShaderState {
     // Shader Uniform handling
     //
 
+    /**
+     * Gets the cached location of the shader uniform.
+     *
+     * @return -1 if there is no such uniform available, 
+     *         otherwise >= 0
+     */
+    public final int getCachedUniformLocation(String name) {
+        Integer idx = (Integer) activeUniformLocationMap.get(name);
+        return (null!=idx)?idx.intValue():-1;
+    }
+
     /**
      * Bind the {@link GLUniform} lifecycle to this ShaderState.
      *  
@@ -819,7 +826,7 @@ public class ShaderState {
      * @see #getUniform(String)
      */
     public void ownUniform(GLUniformData uniform) {
-        final int location = getUniformLocation(uniform.getName());
+        final int location = getCachedUniformLocation(uniform.getName());
         if(0<=location) {
             uniform.setLocation(location);
         }        
@@ -832,8 +839,13 @@ public class ShaderState {
     }
     
     /**
-     * Gets the index of a shader uniform.
-     * This must be done when the program is in use !
+     * Gets the location of a shader uniform.<br>
+     * Uses either the cached value {@link #getCachedUniformLocation(String)} if valid,
+     * or the GLSL queried via {@link GL2ES2#glGetUniformLocation(int, String)}.<br>
+     * The location will be cached.
+     * <p>
+     * The current shader program ({@link #attachShaderProgram(GL2ES2, ShaderProgram)}) 
+     * must be in use ({@link #useProgram(GL2ES2, boolean) }) !</p>
      *
      * @return -1 if there is no such attribute available,
      *         otherwise >= 0
@@ -845,9 +857,9 @@ public class ShaderState {
      * @see #getUniformLocation
      * @see ShaderProgram#glReplaceShader
      */
-    protected final int getUniformLocation(GL2ES2 gl, String name) {
+    public final int getUniformLocation(GL2ES2 gl, String name) {
         if(!shaderProgram.inUse()) throw new GLException("Program is not in use");
-        int location = getUniformLocation(name);
+        int location = getCachedUniformLocation(name);
         if(0>location) {
             location = gl.glGetUniformLocation(shaderProgram.program(), name);
             if(0<=location) {
@@ -861,11 +873,33 @@ public class ShaderState {
         return location;
     }
 
-    protected final int getUniformLocation(String name) {
-        Integer idx = (Integer) activeUniformLocationMap.get(name);
-        return (null!=idx)?idx.intValue():-1;
-    }
+    /**
+     * Validates and returns the location of a shader uniform.<br>
+     * Uses either the cached value {@link #getCachedUniformLocation(String)} if valid,
+     * or the GLSL queried via {@link GL2ES2#glGetUniformLocation(int, String)}.<br>
+     * The location will be cached and set in the  
+     * {@link GLUniformData} object.
+     * <p>
+     * The current shader program ({@link #attachShaderProgram(GL2ES2, ShaderProgram)}) 
+     * must be in use ({@link #useProgram(GL2ES2, boolean) }) !</p>
+     *
+     * @return -1 if there is no such attribute available,
+     *         otherwise >= 0
 
+     * @throws GLException is the program is not linked
+     *
+     * @see #glGetUniformLocation
+     * @see javax.media.opengl.GL2ES2#glGetUniformLocation
+     * @see #getUniformLocation
+     * @see ShaderProgram#glReplaceShader
+     */
+    public int getUniformLocation(GL2ES2 gl, GLUniformData data) {
+        int location = getUniformLocation(gl, data.getName());
+        data.setLocation(location);
+        activeUniformDataMap.put(data.getName(), data);        
+        return location;
+    }
+    
     /**
      * Set the uniform data.
      *
@@ -883,6 +917,7 @@ public class ShaderState {
      *
      * @see #glGetUniformLocation
      * @see javax.media.opengl.GL2ES2#glGetUniformLocation
+     * @see javax.media.opengl.GL2ES2#glUniform
      * @see #getUniformLocation
      * @see ShaderProgram#glReplaceShader
      */
@@ -890,10 +925,8 @@ public class ShaderState {
         if(!shaderProgram.inUse()) throw new GLException("Program is not in use");
         int location = data.getLocation();
         if(0>location) {
-            location = getUniformLocation(gl, data.getName());
-            data.setLocation(location);
+            location = getUniformLocation(gl, data);
         }
-        activeUniformDataMap.put(data.getName(), data);
         if(0<=location) {
             // only pass the data, if the uniform exists in the current shader
             if(DEBUG) {
diff --git a/src/jogl/classes/jogamp/opengl/util/GLDataArrayHandler.java b/src/jogl/classes/jogamp/opengl/util/GLDataArrayHandler.java
index 718b63822..259b8ae23 100644
--- a/src/jogl/classes/jogamp/opengl/util/GLDataArrayHandler.java
+++ b/src/jogl/classes/jogamp/opengl/util/GLDataArrayHandler.java
@@ -29,6 +29,7 @@
 package jogamp.opengl.util;
 
 import javax.media.opengl.*;
+
 import com.jogamp.opengl.util.*;
 
 import java.nio.*;
@@ -52,7 +53,7 @@ public class GLDataArrayHandler implements GLArrayHandler {
       throw new UnsupportedOperationException();
   }
   
-  public final void enableBuffer(GL gl, boolean enable) {
+  public final void syncData(GL gl, boolean enable) {
     if(enable) {
         Buffer buffer = ad.getBuffer();
 
@@ -67,7 +68,11 @@ public class GLDataArrayHandler implements GLArrayHandler {
         }
     } else {
         gl.glBindBuffer(ad.getVBOTarget(), 0);
-    }
+    }      
+  }
+  
+  public final void enableState(GL gl, boolean enable) { 
+      // no array association
   }
 }
 
diff --git a/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandler.java b/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandler.java
index e365f0f4b..2cce72ff4 100644
--- a/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandler.java
+++ b/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandler.java
@@ -51,30 +51,9 @@ public class GLFixedArrayHandler implements GLArrayHandler {
       throw new UnsupportedOperationException();
   }
   
-  private final void passArrayPointer(GLPointerFunc gl) {
-    switch(ad.getIndex()) {
-        case GLPointerFunc.GL_VERTEX_ARRAY:
-            gl.glVertexPointer(ad);
-            break;
-        case GLPointerFunc.GL_NORMAL_ARRAY:
-            gl.glNormalPointer(ad);
-            break;
-        case GLPointerFunc.GL_COLOR_ARRAY:
-            gl.glColorPointer(ad);
-            break;
-        case GLPointerFunc.GL_TEXTURE_COORD_ARRAY:
-            gl.glTexCoordPointer(ad);
-            break;
-        default:
-            throw new GLException("invalid glArrayIndex: "+ad.getIndex()+":\n\t"+ad); 
-    }
-  }
-
-  public final void enableBuffer(GL gl, boolean enable) {
-    GLPointerFunc glp = gl.getGL2ES1();
+  public final void syncData(GL gl, boolean enable) {
     if(enable) {
-        Buffer buffer = ad.getBuffer();
-
+        final Buffer buffer = ad.getBuffer();
         if(ad.isVBO()) {
             // always bind and refresh the VBO mgr, 
             // in case more than one gl*Pointer objects are in use
@@ -86,13 +65,34 @@ public class GLFixedArrayHandler implements GLArrayHandler {
                 ad.setVBOWritten(true);
             }
         }
-        passArrayPointer(glp);
+        final GLPointerFunc glp = gl.getGL2ES1();
+        switch(ad.getIndex()) {
+            case GLPointerFunc.GL_VERTEX_ARRAY:
+                glp.glVertexPointer(ad);
+                break;
+            case GLPointerFunc.GL_NORMAL_ARRAY:
+                glp.glNormalPointer(ad);
+                break;
+            case GLPointerFunc.GL_COLOR_ARRAY:
+                glp.glColorPointer(ad);
+                break;
+            case GLPointerFunc.GL_TEXTURE_COORD_ARRAY:
+                glp.glTexCoordPointer(ad);
+                break;
+            default:
+                throw new GLException("invalid glArrayIndex: "+ad.getIndex()+":\n\t"+ad); 
+        }
+    } else if(ad.isVBO()) {
+        gl.glBindBuffer(ad.getVBOTarget(), 0);
+    }
+  }
+  
+  public final void enableState(GL gl, boolean enable) {
+    final GLPointerFunc glp = gl.getGL2ES1();
+    if(enable) {
         glp.glEnableClientState(ad.getIndex());        
     } else {
         glp.glDisableClientState(ad.getIndex());
-        if(ad.isVBO()) {
-            gl.glBindBuffer(ad.getVBOTarget(), 0);
-        }
     }
   }
 }
diff --git a/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandlerFlat.java b/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandlerFlat.java
index e1cf5d572..4dda9c6a1 100644
--- a/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandlerFlat.java
+++ b/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandlerFlat.java
@@ -50,29 +50,31 @@ public class GLFixedArrayHandlerFlat implements GLArrayHandler {
       throw new UnsupportedOperationException();
   }
   
-  private final void passArrayPointer(GLPointerFunc gl) {
-    switch(ad.getIndex()) {
-        case GLPointerFunc.GL_VERTEX_ARRAY:
-            gl.glVertexPointer(ad);
-            break;
-        case GLPointerFunc.GL_NORMAL_ARRAY:
-            gl.glNormalPointer(ad);
-            break;
-        case GLPointerFunc.GL_COLOR_ARRAY:
-            gl.glColorPointer(ad);
-            break;
-        case GLPointerFunc.GL_TEXTURE_COORD_ARRAY:
-            gl.glTexCoordPointer(ad);
-            break;
-        default:
-            throw new GLException("invalid glArrayIndex: "+ad.getIndex()+":\n\t"+ad); 
+  public final void syncData(GL gl, boolean enable) {
+    if(enable) {
+        final GLPointerFunc glp = gl.getGL2ES1();
+        switch(ad.getIndex()) {
+            case GLPointerFunc.GL_VERTEX_ARRAY:
+                glp.glVertexPointer(ad);
+                break;
+            case GLPointerFunc.GL_NORMAL_ARRAY:
+                glp.glNormalPointer(ad);
+                break;
+            case GLPointerFunc.GL_COLOR_ARRAY:
+                glp.glColorPointer(ad);
+                break;
+            case GLPointerFunc.GL_TEXTURE_COORD_ARRAY:
+                glp.glTexCoordPointer(ad);
+                break;
+            default:
+                throw new GLException("invalid glArrayIndex: "+ad.getIndex()+":\n\t"+ad); 
+        }
     }
   }
 
-  public final void enableBuffer(GL gl, boolean enable) {
-    GLPointerFunc glp = gl.getGL2ES1();
+  public final void enableState(GL gl, boolean enable) {
+    final GLPointerFunc glp = gl.getGL2ES1();
     if(enable) {
-        passArrayPointer(glp);
         glp.glEnableClientState(ad.getIndex());        
     } else {
         glp.glDisableClientState(ad.getIndex());
diff --git a/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandlerInterleaved.java b/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandlerInterleaved.java
index 838032646..4bac20217 100644
--- a/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandlerInterleaved.java
+++ b/src/jogl/classes/jogamp/opengl/util/GLFixedArrayHandlerInterleaved.java
@@ -54,15 +54,15 @@ public class GLFixedArrayHandlerInterleaved implements GLArrayHandler {
       subArrays.add(handler);
   }
 
-  private final void enableSubBuffer(GL gl, boolean enable) {
+  private final void syncSubData(GL gl, boolean enable) {
       for(int i=0; i<subArrays.size(); i++) {
-          subArrays.get(i).enableBuffer(gl, enable);
+          subArrays.get(i).syncData(gl, enable);
       }      
   }
   
-  public final void enableBuffer(GL gl, boolean enable) {
+  public final void syncData(GL gl, boolean enable) {
     if(enable) {
-        Buffer buffer = ad.getBuffer();
+        final Buffer buffer = ad.getBuffer();
 
         if(ad.isVBO()) {
             // always bind and refresh the VBO mgr, 
@@ -75,13 +75,19 @@ public class GLFixedArrayHandlerInterleaved implements GLArrayHandler {
                 ad.setVBOWritten(true);
             }
         }
-        enableSubBuffer(gl, true);
+        syncSubData(gl, true);
     } else {
-        enableSubBuffer(gl, false);
+        syncSubData(gl, false);
         if(ad.isVBO()) {
             gl.glBindBuffer(ad.getVBOTarget(), 0);
         }
     }
   }
+  
+  public final void enableState(GL gl, boolean enable) {
+    for(int i=0; i<subArrays.size(); i++) {
+        subArrays.get(i).enableState(gl, enable);
+    }      
+  }
 }
 
diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandler.java b/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandler.java
index b4b7b5ace..d2fc52d5c 100644
--- a/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandler.java
+++ b/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandler.java
@@ -32,7 +32,7 @@ import java.nio.Buffer;
 
 import javax.media.opengl.GL;
 import javax.media.opengl.GL2ES2;
-import javax.media.opengl.GLException;
+
 import com.jogamp.opengl.util.GLArrayDataEditable;
 import com.jogamp.opengl.util.GLArrayHandler;
 import com.jogamp.opengl.util.glsl.ShaderState;
@@ -54,12 +54,25 @@ public class GLSLArrayHandler implements GLArrayHandler {
       throw new UnsupportedOperationException();
   }
   
-  public final void enableBuffer(GL gl, boolean enable) {
-    GL2ES2 glsl = gl.getGL2ES2();
+  public final void syncData(GL gl, boolean enable) {
+    final GL2ES2 glsl = gl.getGL2ES2();
 
     if(enable) {
-        Buffer buffer = ad.getBuffer();
-
+        final Buffer buffer = ad.getBuffer();
+        /*
+         * This would be the non optimized code path:
+         * 
+        if(ad.isVBO()) {
+            glsl.glBindBuffer(ad.getVBOTarget(), ad.getVBOName());
+            if(!ad.isVBOWritten()) {
+                if(null!=buffer) {
+                    glsl.glBufferData(ad.getVBOTarget(), ad.getSizeInBytes(), buffer, ad.getVBOUsage());
+                }
+                ad.setVBOWritten(true);
+            }
+        }
+        st.vertexAttribPointer(glsl, ad);
+        */
         if(ad.isVBO()) {
             // bind and refresh the VBO / vertex-attr only if necessary
             if(!ad.isVBOWritten()) {
@@ -69,8 +82,9 @@ public class GLSLArrayHandler implements GLArrayHandler {
                 }
                 ad.setVBOWritten(true);
                 st.vertexAttribPointer(glsl, ad);
-            } else if(ad.getLocation() >= 0) {
+            } else if(st.getAttribLocation(glsl, ad) >= 0) {
                 // didn't experience a performance hit on this query ..
+                // (using ShaderState's location query above to validate the location)
                 final int[] qi = new int[1];
                 glsl.glGetVertexAttribiv(ad.getLocation(), GL2ES2.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, qi, 0);
                 if(ad.getVBOName() != qi[0]) {
@@ -81,12 +95,18 @@ public class GLSLArrayHandler implements GLArrayHandler {
         } else if(null!=buffer) {
             st.vertexAttribPointer(glsl, ad);
         }
+    } else if(ad.isVBO()) {
+        glsl.glBindBuffer(ad.getVBOTarget(), 0);
+    }      
+  }
+  
+  public final void enableState(GL gl, boolean enable) {
+    final GL2ES2 glsl = gl.getGL2ES2();
+
+    if(enable) {
         st.enableVertexAttribArray(glsl, ad);
     } else {
         st.disableVertexAttribArray(glsl, ad);
-        if(ad.isVBO()) {
-            glsl.glBindBuffer(ad.getVBOTarget(), 0);
-        }
     }
   }
 
diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandlerFlat.java b/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandlerFlat.java
index 38379877f..5c4aa718c 100644
--- a/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandlerFlat.java
+++ b/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandlerFlat.java
@@ -51,16 +51,20 @@ public class GLSLArrayHandlerFlat implements GLArrayHandler {
       throw new UnsupportedOperationException();
   }
   
-  public final void enableBuffer(GL gl, boolean enable) {
-    GL2ES2 glsl = gl.getGL2ES2();
+  public final void syncData(GL gl, boolean enable) {
+    if(enable) {
+        st.vertexAttribPointer(gl.getGL2ES2(), ad);
+    }
+  }
+
+  public final void enableState(GL gl, boolean enable) {
+    final GL2ES2 glsl = gl.getGL2ES2();
 
     if(enable) {
-        st.vertexAttribPointer(glsl, ad);
         st.enableVertexAttribArray(glsl, ad);
     } else {
         st.disableVertexAttribArray(glsl, ad);
     }
-  }
-
+  }  
 }
 
diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandlerInterleaved.java b/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandlerInterleaved.java
index ba5814a09..c662c13d2 100644
--- a/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandlerInterleaved.java
+++ b/src/jogl/classes/jogamp/opengl/util/glsl/GLSLArrayHandlerInterleaved.java
@@ -60,21 +60,22 @@ public class GLSLArrayHandlerInterleaved implements GLArrayHandler {
       subArrays.add(handler);
   }
 
-  private final void enableSubBuffer(GL gl, boolean enable) {
+  private final void syncSubData(GL gl, boolean enable) {
       for(int i=0; i<subArrays.size(); i++) {
-          subArrays.get(i).enableBuffer(gl, enable);
+          subArrays.get(i).syncData(gl, enable);
       }      
   }
   
-  public final void enableBuffer(GL gl, boolean enable) {
+  public final void syncData(GL gl, boolean enable) {
     GL2ES2 glsl = gl.getGL2ES2();
 
     if(enable) {
         Buffer buffer = ad.getBuffer();
 
+        /*
+         * This would be the non optimized code path:
+         * 
         if(ad.isVBO()) {
-            // always bind and refresh the VBO mgr, 
-            // in case more than one attributes are in use
             glsl.glBindBuffer(ad.getVBOTarget(), ad.getVBOName());
             if(!ad.isVBOWritten()) {
                 if(null!=buffer) {
@@ -83,13 +84,42 @@ public class GLSLArrayHandlerInterleaved implements GLArrayHandler {
                 ad.setVBOWritten(true);
             }
         }
-        enableSubBuffer(gl, true);
+        syncSubData(gl, true);
+        */
+        if(ad.isVBO()) {
+            // bind and refresh the VBO / vertex-attr only if necessary
+            if(!ad.isVBOWritten()) {
+                glsl.glBindBuffer(ad.getVBOTarget(), ad.getVBOName());
+                if(null!=buffer) {
+                    glsl.glBufferData(ad.getVBOTarget(), ad.getSizeInBytes(), buffer, ad.getVBOUsage());
+                }
+                ad.setVBOWritten(true);
+                syncSubData(gl, true);
+            } else if(st.getAttribLocation(glsl, ad) >= 0) {
+                // didn't experience a performance hit on this query ..
+                // (using ShaderState's location query above to validate the location)
+                final int[] qi = new int[1];
+                glsl.glGetVertexAttribiv(ad.getLocation(), GL2ES2.GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, qi, 0);
+                if(ad.getVBOName() != qi[0]) {
+                    glsl.glBindBuffer(ad.getVBOTarget(), ad.getVBOName());
+                    syncSubData(gl, true);
+                }
+            }
+        } else if(null!=buffer) {
+            syncSubData(gl, true);
+        }
     } else {
-        enableSubBuffer(gl, false);
+        syncSubData(gl, false);
         if(ad.isVBO()) {
             glsl.glBindBuffer(ad.getVBOTarget(), 0);
         }
-    }
+    }      
+  }
+  
+  public final void enableState(GL gl, boolean enable) {
+    for(int i=0; i<subArrays.size(); i++) {
+        subArrays.get(i).enableState(gl, enable);
+    }      
   }
 
 }
diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/glsl/GLSLMiscHelper.java b/src/test/com/jogamp/opengl/test/junit/jogl/glsl/GLSLMiscHelper.java
index 504691fbe..7b30dedcc 100644
--- a/src/test/com/jogamp/opengl/test/junit/jogl/glsl/GLSLMiscHelper.java
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/glsl/GLSLMiscHelper.java
@@ -45,7 +45,7 @@ public class GLSLMiscHelper {
         if(null != st) {            
             Assert.assertEquals(data, st.getAttribute(data.getName()));            
             if(st.shaderProgram().linked()) {
-                Assert.assertEquals(data.getLocation(), st.getAttribLocation(data.getName()));
+                Assert.assertEquals(data.getLocation(), st.getCachedAttribLocation(data.getName()));
                 Assert.assertEquals(data.getLocation(), st.getAttribLocation(gl, data));
                 Assert.assertEquals(data.getLocation(), st.getAttribLocation(gl, data.getName()));
                 Assert.assertEquals(data.getLocation(), gl.glGetAttribLocation(st.shaderProgram().program(), data.getName()));                
-- 
cgit v1.2.3