From 9c71f276d1fcc87b69b413847fd1da34b30d0932 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Fri, 4 Apr 2014 02:30:00 +0200
Subject: Bug 801: Cleanup shader-program location/data update ; Add
 COLORTEXTURE + TextureSequence to Region (Demo: TextureButton)

Cleanup shader-program location/data update

- GLUniformData:
  - Allow lazy data setup, as used for
    RenderState.ProgramLocal, see below

- RenderState
  - Separate data (pmv, weight, colorStatic) from
    program-local uniforms -> add class ProgramLocal.
    Reduces uniform location lookups, since
    ProgramLocal is bound to Region impl.

  - ProgramLocal.update(..) needs to write uniform data always,
    since data is being used in multiple programs!

  - No 'dirty' tracking possible, removed - see above.

- RegionRenderer
  - Fix shader-selection: 2-pass programs differ from 1-pass!
  - No shader-setup at init

+++

Add COLORTEXTURE + TextureSequence to Region
  - Create color-texture coords in vertex-shader via region's bounding box (pass-1)
  - Use color-texture unit in pass-1 if enabled (own shader program)
  - Use TextureSequence in Region impl. providing all required data (unit + texture-name)
  - Demo: TextureButton (a UIShape)
---
 .../classes/com/jogamp/graph/curve/Region.java     |  25 ++-
 .../com/jogamp/graph/curve/opengl/GLRegion.java    |  25 ++-
 .../jogamp/graph/curve/opengl/RegionRenderer.java  |  29 ++-
 .../com/jogamp/graph/curve/opengl/RenderState.java | 201 +++++++++++++--------
 .../jogamp/graph/curve/opengl/TextRegionUtil.java  |   4 +-
 5 files changed, 172 insertions(+), 112 deletions(-)

(limited to 'src/jogl/classes/com/jogamp/graph/curve')

diff --git a/src/jogl/classes/com/jogamp/graph/curve/Region.java b/src/jogl/classes/com/jogamp/graph/curve/Region.java
index 7edd2f572..230fa324d 100644
--- a/src/jogl/classes/com/jogamp/graph/curve/Region.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/Region.java
@@ -37,6 +37,7 @@ import com.jogamp.graph.geom.Triangle;
 import com.jogamp.graph.geom.Vertex;
 import com.jogamp.opengl.math.geom.AABBox;
 import com.jogamp.opengl.math.geom.Frustum;
+import com.jogamp.opengl.util.texture.TextureSequence;
 
 /**
  * Abstract Outline shape representation define the method an OutlineShape(s)
@@ -102,7 +103,7 @@ public abstract class Region {
     /** Default maximum {@link #getQuality() quality}, {@value}. */
     public static final int MAX_QUALITY  = 1;
 
-    public static final int TWO_PASS_DEFAULT_TEXTURE_UNIT = 0;
+    public static final int DEFAULT_TWO_PASS_TEXTURE_UNIT = 0;
 
     private final int renderModes;
     private int quality;
@@ -175,7 +176,7 @@ public abstract class Region {
     protected abstract void pushIndex(int idx);
 
     /**
-     * Return bit-field of render modes, see {@link #create(int)}.
+     * Return bit-field of render modes, see {@link #create(int, TextureSequence)}.
      */
     public final int getRenderModes() { return renderModes; }
 
@@ -369,19 +370,33 @@ public abstract class Region {
         return box;
     }
 
-    /** Check if this region is dirty. A region is marked dirty when new
-     * Vertices, Triangles, and or Lines are added after a call to update()
+    /**
+     * Check if this region is dirty. A region is marked dirty when new
+     * Vertices, Triangles, and or Lines are added after a call to update().
+     * <p>
+     * A region is also dirty if other render attributes or parameters are changed!
+     * </p>
      *
      * @return true if region is Dirty, false otherwise
      *
-     * @see update(GL2ES2) */
+     * @see update(GL2ES2)
+     */
     public final boolean isDirty() {
         return dirty;
     }
 
+    /**
+     * See {@link #isDirty()}.
+     */
     protected final void setDirty(boolean v) {
         dirty = v;
     }
+    /**
+     * See {@link #isDirty()}.
+     */
+    public final void markDirty() {
+        dirty = true;
+    }
 
     public String toString() {
         return "Region["+getRenderModeString(this.renderModes)+", q "+quality+", dirty "+dirty+", vertices "+numVertices+", box "+box+"]";
diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java
index 9505f2a9d..3c6045a1f 100644
--- a/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java
@@ -35,6 +35,7 @@ import jogamp.graph.curve.opengl.VBORegion2PVBAAES2;
 import jogamp.graph.curve.opengl.VBORegionSPES2;
 
 import com.jogamp.opengl.util.PMVMatrix;
+import com.jogamp.opengl.util.texture.TextureSequence;
 import com.jogamp.graph.curve.Region;
 
 /** A GLRegion is the OGL binding of one or more OutlineShapes
@@ -53,23 +54,31 @@ public abstract class GLRegion extends Region {
      * Create a GLRegion using the passed render mode
      *
      * <p> In case {@link Region#VBAA_RENDERING_BIT} is being requested the default texture unit
-     * {@link Region#TWO_PASS_DEFAULT_TEXTURE_UNIT} is being used.</p>
-     *
-     * @param rs the RenderState to be used
+     * {@link Region#DEFAULT_TWO_PASS_TEXTURE_UNIT} is being used.</p>
      * @param renderModes bit-field of modes, e.g. {@link Region#VARWEIGHT_RENDERING_BIT}, {@link Region#VBAA_RENDERING_BIT}
+     * @param colorTexSeq TODO
+     * @param rs the RenderState to be used
      */
-    public static GLRegion create(int renderModes) {
+    public static GLRegion create(int renderModes, final TextureSequence colorTexSeq) {
+        if( null != colorTexSeq ) {
+            renderModes |= Region.COLORTEXTURE_RENDERING_BIT;
+        } else if( Region.hasColorTexture(renderModes) ) {
+            throw new IllegalArgumentException("COLORTEXTURE_RENDERING_BIT set but null TextureSequence");
+        }
         if( isVBAA(renderModes) ) {
-            return new VBORegion2PVBAAES2(renderModes, Region.TWO_PASS_DEFAULT_TEXTURE_UNIT);
+            return new VBORegion2PVBAAES2(renderModes, colorTexSeq, Region.DEFAULT_TWO_PASS_TEXTURE_UNIT);
         } else if( isMSAA(renderModes) ) {
-            return new VBORegion2PMSAAES2(renderModes, Region.TWO_PASS_DEFAULT_TEXTURE_UNIT);
+            return new VBORegion2PMSAAES2(renderModes, colorTexSeq, Region.DEFAULT_TWO_PASS_TEXTURE_UNIT);
         } else {
-            return new VBORegionSPES2(renderModes);
+            return new VBORegionSPES2(renderModes, colorTexSeq);
         }
     }
 
-    protected GLRegion(int renderModes) {
+    protected final TextureSequence colorTexSeq;
+
+    protected GLRegion(int renderModes, TextureSequence colorTexSeq) {
         super(renderModes);
+        this.colorTexSeq = colorTexSeq;
     }
 
     /**
diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java
index ade6098e1..8724baff8 100644
--- a/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java
@@ -139,9 +139,6 @@ public class RegionRenderer {
     public final int getHeight() { return vp_height; }
 
     public final PMVMatrix getMatrix() { return rs.getMatrix(); }
-    public final PMVMatrix getMatrixMutable() { return rs.getMatrixMutable(); }
-    public final void setMatrixDirty() { rs.setMatrixDirty(); }
-    public final boolean isMatrixDirty() { return rs.isMatrixDirty(); }
 
     //////////////////////////////////////
 
@@ -190,12 +187,6 @@ public class RegionRenderer {
         if( null != enableCallback ) {
             enableCallback.run(gl, this);
         }
-
-        useShaderProgram(gl, renderModes, true, 0, 0);
-        initialized = rs.update(gl, true, renderModes, true);
-        if(!initialized) {
-            throw new GLException("Shader initialization failed");
-        }
     }
 
     public final void destroy(GL2ES2 gl) {
@@ -247,14 +238,13 @@ public class RegionRenderer {
     public final void reshapeNotify(int width, int height) {
         this.vp_width = width;
         this.vp_height = height;
-        rs.setMatrixDirty();
     }
 
     public final void reshapePerspective(float angle, int width, int height, float near, float far) {
         this.vp_width = width;
         this.vp_height = height;
         final float ratio = (float)width/(float)height;
-        final PMVMatrix p = rs.getMatrixMutable();
+        final PMVMatrix p = rs.getMatrix();
         p.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
         p.glLoadIdentity();
         p.gluPerspective(angle, ratio, near, far);
@@ -263,7 +253,7 @@ public class RegionRenderer {
     public final void reshapeOrtho(int width, int height, float near, float far) {
         this.vp_width = width;
         this.vp_height = height;
-        final PMVMatrix p = rs.getMatrixMutable();
+        final PMVMatrix p = rs.getMatrix();
         p.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
         p.glLoadIdentity();
         p.glOrthof(0, width, 0, height, near, far);
@@ -368,6 +358,7 @@ public class RegionRenderer {
     private final IntObjectHashMap shaderPrograms = new IntObjectHashMap();
 
     private static final int HIGH_MASK = Region.COLORCHANNEL_RENDERING_BIT | Region.COLORTEXTURE_RENDERING_BIT;
+    private static final int TWO_PASS_BIT = 1 <<  31;
 
     /**
      * @param gl
@@ -382,7 +373,8 @@ public class RegionRenderer {
                                           final boolean pass1, final int quality, final int sampleCount) {
         final ShaderModeSelector1 sel1 = pass1 ? ShaderModeSelector1.selectPass1(renderModes) :
                                                  ShaderModeSelector1.selectPass2(renderModes, quality, sampleCount);
-        final int shaderKey = sel1.ordinal() | ( HIGH_MASK & renderModes );
+        final boolean isTwoPass = Region.isTwoPass( renderModes );
+        final int shaderKey = sel1.ordinal() | ( HIGH_MASK & renderModes ) | ( isTwoPass ? TWO_PASS_BIT : 0 );
 
         /**
         if(DEBUG) {
@@ -395,14 +387,16 @@ public class RegionRenderer {
             final boolean spChanged = getRenderState().setShaderProgram(gl, sp);
             if(DEBUG) {
                 if( spChanged ) {
-                    System.err.printf("RegionRendererImpl01.useShaderProgram.X1: GOT renderModes %s, sel1 %s, key 0x%X (changed)%n", Region.getRenderModeString(renderModes), sel1, shaderKey);
+                    System.err.printf("RegionRendererImpl01.useShaderProgram.X1: GOT renderModes %s, sel1 %s, key 0x%X -> sp %d / %d (changed)%n", Region.getRenderModeString(renderModes), sel1, shaderKey, sp.program(), sp.id());
+                } else {
+                    System.err.printf("RegionRendererImpl01.useShaderProgram.X1: GOT renderModes %s, sel1 %s, key 0x%X -> sp %d / %d (keep)%n", Region.getRenderModeString(renderModes), sel1, shaderKey, sp.program(), sp.id());
                 }
             }
             return spChanged;
         }
         final String versionedBaseName = getVersionedShaderName();
         final String vertexShaderName;
-        if( Region.isTwoPass( renderModes ) ) {
+        if( isTwoPass ) {
             vertexShaderName = versionedBaseName+"-pass"+(pass1?1:2);
         } else {
             vertexShaderName = versionedBaseName+"-single";
@@ -453,6 +447,7 @@ public class RegionRenderer {
         if( !sp.init(gl) ) {
             throw new GLException("RegionRenderer: Couldn't init program: "+sp);
         }
+
         if( !sp.link(gl, System.err) ) {
             throw new GLException("could not link program: "+sp);
         }
@@ -460,8 +455,8 @@ public class RegionRenderer {
 
         shaderPrograms.put(shaderKey, sp);
         if(DEBUG) {
-            System.err.printf("RegionRendererImpl01.useShaderProgram.X1: PUT renderModes %s, sel1 %s, key 0x%X -> SP %s (changed)%n",
-                    Region.getRenderModeString(renderModes), sel1, shaderKey, sp);
+            System.err.printf("RegionRendererImpl01.useShaderProgram.X1: PUT renderModes %s, sel1 %s, key 0x%X -> sp %d / %d (changed)%n",
+                    Region.getRenderModeString(renderModes), sel1, shaderKey, sp.program(), sp.id());
         }
         return true;
     }
diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java
index c7083a41b..818526cd7 100644
--- a/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java
@@ -31,6 +31,7 @@ import java.nio.FloatBuffer;
 
 import javax.media.opengl.GL;
 import javax.media.opengl.GL2ES2;
+import javax.media.opengl.GLException;
 import javax.media.opengl.GLUniformData;
 
 import jogamp.graph.curve.opengl.shader.UniformNames;
@@ -78,25 +79,109 @@ public class RenderState {
 
     private final Vertex.Factory<? extends Vertex> vertexFactory;
     private final PMVMatrix pmvMatrix;
-    private final GLUniformData gcu_PMVMatrix01;
-    private final GLUniformData gcu_Weight;
-    private final GLUniformData gcu_ColorStatic;
-    private boolean gcu_PMVMatrix01_dirty = true;
-    private boolean gcu_Weight_dirty = true;
-    private boolean gcu_ColorStatic_dirty = true;
+    private final float[] weight;
+    private final FloatBuffer weightBuffer;
+    private final float[] colorStatic;
+    private final FloatBuffer colorStaticBuffer;
     private ShaderProgram sp;
     private int hintBitfield;
 
+    private final int id;
+    private static synchronized int getNextID() {
+        return nextID++;
+    }
+    private static int nextID = 1;
+
+    /**
+     * Representation of {@link RenderState} data for one {@link ShaderProgram}
+     * as {@link GLUniformData}.
+     * <p>
+     * FIXME: Utilize 'ARB_Uniform_Buffer_Object' where available!
+     * </p>
+     */
+    public static class ProgramLocal {
+        public final GLUniformData gcu_PMVMatrix01;
+        public final GLUniformData gcu_Weight;
+        public final GLUniformData gcu_ColorStatic;
+        private int rsId = -1;
+
+        public ProgramLocal() {
+            gcu_PMVMatrix01 = GLUniformData.creatEmptyMatrix(UniformNames.gcu_PMVMatrix01, 4, 4);
+            gcu_Weight = GLUniformData.creatEmptyVector(UniformNames.gcu_Weight, 1);
+            gcu_ColorStatic = GLUniformData.creatEmptyVector(UniformNames.gcu_ColorStatic, 4);
+        }
+
+        public final int getRenderStateId() { return rsId; }
+
+        /**
+         * <p>
+         * Since {@link RenderState} data is being used in multiple
+         * {@link ShaderProgram}s the data must always be written.
+         * </p>
+         * @param gl
+         * @param updateLocation
+         * @param renderModes
+         * @param throwOnError TODO
+         * @return true if no error occurred, i.e. all locations found, otherwise false.
+         */
+        public final boolean update(final GL2ES2 gl, final RenderState rs, final boolean updateLocation, final int renderModes, final boolean pass1, final boolean throwOnError) {
+            if( rs.id() != rsId ) {
+                gcu_PMVMatrix01.setData(rs.pmvMatrix.glGetPMvMatrixf());
+                gcu_Weight.setData(rs.weightBuffer);
+                gcu_ColorStatic.setData(rs.colorStaticBuffer);
+                rsId = rs.id();
+            }
+            boolean res = true;
+            if( null != rs.sp && rs.sp.inUse() ) {
+                if( !Region.isTwoPass(renderModes) || !pass1 ) {
+                    final boolean r0 = rs.updateUniformDataLoc(gl, updateLocation, true, gcu_PMVMatrix01, throwOnError);
+                    res = res && r0;
+                }
+                if( pass1 ) {
+                    if( Region.hasVariableWeight( renderModes ) ) {
+                        final boolean r0 = rs.updateUniformDataLoc(gl, updateLocation, true, gcu_Weight, throwOnError);
+                        res = res && r0;
+                    }
+                    {
+                        final boolean r0 = rs.updateUniformDataLoc(gl, updateLocation, true, gcu_ColorStatic, throwOnError);
+                        res = res && r0;
+                    }
+                }
+            }
+            return res;
+        }
+
+        public StringBuilder toString(StringBuilder sb, boolean alsoUnlocated) {
+            if(null==sb) {
+                sb = new StringBuilder();
+            }
+            sb.append("ProgramLocal[rsID ").append(rsId).append(Platform.NEWLINE);
+            // pmvMatrix.toString(sb, "%.2f");
+            sb.append(gcu_PMVMatrix01).append(", ").append(Platform.NEWLINE);
+            sb.append(gcu_ColorStatic).append(", ");
+            sb.append(gcu_Weight).append("]");
+            return sb;
+        }
+
+        @Override
+        public String toString() {
+            return toString(null, false).toString();
+        }
+    }
+
     protected RenderState(Vertex.Factory<? extends Vertex> vertexFactory, PMVMatrix pmvMatrix) {
+        this.id = getNextID();
         this.sp = null;
         this.vertexFactory = vertexFactory;
         this.pmvMatrix = null != pmvMatrix ? pmvMatrix : new PMVMatrix();
-        this.gcu_PMVMatrix01 = new GLUniformData(UniformNames.gcu_PMVMatrix01, 4, 4, this.pmvMatrix.glGetPMvMatrixf());
-        this.gcu_Weight = new GLUniformData(UniformNames.gcu_Weight, 1.0f);
-        this.gcu_ColorStatic = new GLUniformData(UniformNames.gcu_ColorStatic, 4, FloatBuffer.allocate(4));
+        this.weight = new float[1];
+        this.weightBuffer = FloatBuffer.wrap(weight);
+        this.colorStatic = new float[4];
+        this.colorStaticBuffer = FloatBuffer.wrap(colorStatic);
         this.hintBitfield = 0;
     }
 
+    public final int id() { return id; }
     public final ShaderProgram getShaderProgram() { return sp; }
     public final boolean isShaderProgramInUse() { return null != sp ? sp.inUse() : false; }
 
@@ -124,86 +209,46 @@ public class RenderState {
     public final Vertex.Factory<? extends Vertex> getVertexFactory() { return vertexFactory; }
 
     public final PMVMatrix getMatrix() { return pmvMatrix; }
-    public final PMVMatrix getMatrixMutable() {
-        gcu_PMVMatrix01_dirty = true;
-        return pmvMatrix;
-    }
-    public final GLUniformData getMatrixUniform() { return gcu_PMVMatrix01; }
-    public final void setMatrixDirty() { gcu_PMVMatrix01_dirty = true; }
-    public final boolean isMatrixDirty() { return gcu_PMVMatrix01_dirty;}
 
     public static boolean isWeightValid(float v) {
         return 0.0f <= v && v <= 1.9f ;
     }
-    public final float getWeight() { return gcu_Weight.floatValue(); }
+    public final float getWeight() { return weight[0]; }
     public final void setWeight(float v) {
         if( !isWeightValid(v) ) {
              throw new IllegalArgumentException("Weight out of range");
         }
-        gcu_Weight_dirty = true;
-        gcu_Weight.setData(v);
+        weight[0] = v;
     }
 
 
     public final float[] getColorStatic(float[] rgbaColor) {
-        FloatBuffer fb = (FloatBuffer) gcu_ColorStatic.getBuffer();
-        rgbaColor[0] = fb.get(0);
-        rgbaColor[1] = fb.get(1);
-        rgbaColor[2] = fb.get(2);
-        rgbaColor[3] = fb.get(3);
+        System.arraycopy(colorStatic, 0, rgbaColor, 0, 4);
         return rgbaColor;
     }
     public final void setColorStatic(float r, float g, float b, float a){
-        final FloatBuffer fb = (FloatBuffer) gcu_ColorStatic.getBuffer();
-        fb.put(0, r);
-        fb.put(1, g);
-        fb.put(2, b);
-        fb.put(3, a);
-        gcu_ColorStatic_dirty = true;
+        colorStatic[0] = r;
+        colorStatic[1] = g;
+        colorStatic[2] = b;
+        colorStatic[3] = a;
     }
 
 
-    /**
-     *
-     * @param gl
-     * @param updateLocation
-     * @param renderModes
-     * @return true if no error occurred, i.e. all locations found, otherwise false.
-     */
-    public final boolean update(GL2ES2 gl, final boolean updateLocation, final int renderModes, final boolean pass1) {
-        boolean res = true;
-        if( null != sp && sp.inUse() ) {
-            if( ( !Region.isTwoPass(renderModes) || !pass1 ) && ( gcu_PMVMatrix01_dirty || updateLocation ) ) {
-                final boolean r0 = updateUniformDataLoc(gl, updateLocation, gcu_PMVMatrix01_dirty, gcu_PMVMatrix01);
-                res = res && r0;
-                gcu_PMVMatrix01_dirty = !r0;
-            }
-            if( pass1 ) {
-                if( Region.hasVariableWeight( renderModes ) && ( gcu_Weight_dirty || updateLocation ) ) {
-                    final boolean r0 = updateUniformDataLoc(gl, updateLocation, gcu_Weight_dirty, gcu_Weight);
-                    res = res && r0;
-                    gcu_Weight_dirty = !r0;
-                }
-                if( gcu_ColorStatic_dirty || updateLocation )  {
-                    final boolean r0 = updateUniformDataLoc(gl, updateLocation, gcu_ColorStatic_dirty, gcu_ColorStatic);
-                    res = res && r0;
-                    gcu_ColorStatic_dirty = false;
-                }
-            }
-        }
-        return res;
-    }
-
     /**
      *
      * @param gl
      * @param updateLocation
      * @param data
+     * @param throwOnError TODO
      * @return true if no error occured, i.e. all locations found, otherwise false.
      */
-    public final boolean updateUniformLoc(final GL2ES2 gl, final boolean updateLocation, final GLUniformData data) {
+    public final boolean updateUniformLoc(final GL2ES2 gl, final boolean updateLocation, final GLUniformData data, final boolean throwOnError) {
         if( updateLocation || 0 > data.getLocation() ) {
-            return 0 <= data.setLocation(gl, sp.program());
+            final boolean ok = 0 <= data.setLocation(gl, sp.program());
+            if( throwOnError && !ok ) {
+                throw new GLException("Could not locate "+data);
+            }
+            return ok;
         } else {
             return true;
         }
@@ -215,12 +260,16 @@ public class RenderState {
      * @param updateLocation
      * @param updateData TODO
      * @param data
+     * @param throwOnError TODO
      * @return true if no error occured, i.e. all locations found, otherwise false.
      */
-    public final boolean updateUniformDataLoc(final GL2ES2 gl, boolean updateLocation, boolean updateData, final GLUniformData data) {
+    public final boolean updateUniformDataLoc(final GL2ES2 gl, boolean updateLocation, boolean updateData, final GLUniformData data, final boolean throwOnError) {
         updateLocation = updateLocation || 0 > data.getLocation();
         if( updateLocation ) {
             updateData = 0 <= data.setLocation(gl, sp.program());
+            if( throwOnError && !updateData ) {
+                throw new GLException("Could not locate "+data);
+            }
         }
         if( updateData ){
             gl.glUniform(data);
@@ -233,11 +282,16 @@ public class RenderState {
     /**
      * @param gl
      * @param data
+     * @param throwOnError TODO
      * @return true if no error occured, i.e. all locations found, otherwise false.
      */
-    public final boolean updateAttributeLoc(final GL2ES2 gl, final boolean updateLocation, final GLArrayDataServer data) {
+    public final boolean updateAttributeLoc(final GL2ES2 gl, final boolean updateLocation, final GLArrayDataServer data, boolean throwOnError) {
         if( updateLocation || 0 > data.getLocation() ) {
-            return 0 <= data.setLocation(gl, sp.program());
+            final boolean ok = 0 <= data.setLocation(gl, sp.program());
+            if( throwOnError && !ok ) {
+                throw new GLException("Could not locate "+data);
+            }
+            return ok;
         } else {
             return true;
         }
@@ -274,21 +328,8 @@ public class RenderState {
         return false;
     }
 
-    public StringBuilder toString(StringBuilder sb, boolean alsoUnlocated) {
-        if(null==sb) {
-            sb = new StringBuilder();
-        }
-        sb.append("RenderState[").append(sp).append(Platform.NEWLINE);
-        // pmvMatrix.toString(sb, "%.2f");
-        sb.append(", dirty[pmv "+gcu_PMVMatrix01_dirty+", color "+gcu_ColorStatic_dirty+", weight "+gcu_Weight_dirty+"], ").append(Platform.NEWLINE);
-        sb.append(gcu_PMVMatrix01).append(", ").append(Platform.NEWLINE);
-        sb.append(gcu_ColorStatic).append(", ");
-        sb.append(gcu_Weight).append("]");
-        return sb;
-    }
-
     @Override
     public String toString() {
-        return toString(null, false).toString();
+        return "RenderState["+sp+"]";
     }
 }
diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java
index f944843e9..aadbecc91 100644
--- a/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java
@@ -178,7 +178,7 @@ public class TextRegionUtil {
         final int special = 0;
         GLRegion region = getCachedRegion(font, str, pixelSize, special);
         if(null == region) {
-            region = GLRegion.create(renderModes);
+            region = GLRegion.create(renderModes, null);
             addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str, rgbaColor, tempT1, tempT2);
             addCachedRegion(gl, font, str, pixelSize, special, region);
         }
@@ -213,7 +213,7 @@ public class TextRegionUtil {
         if(!renderer.isInitialized()){
             throw new GLException("TextRendererImpl01: not initialized!");
         }
-        final GLRegion region = GLRegion.create(renderModes);
+        final GLRegion region = GLRegion.create(renderModes, null);
         addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str, rgbaColor, temp1, temp2);
         region.draw(gl, renderer, sampleCount);
         region.destroy(gl);
-- 
cgit v1.2.3