commit de77a6c5cf3365b8cd4f8ce61559b0026750e384
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Sun Jan 10 00:49:12 2021 +0100

    Introduce another Uniform Block Object. Now we can have much more vertex and fragment effects - up their default number to 100.

diff --git a/src/main/java/org/distorted/library/effect/EffectType.java b/src/main/java/org/distorted/library/effect/EffectType.java
index c878231..c7b4fea 100644
--- a/src/main/java/org/distorted/library/effect/EffectType.java
+++ b/src/main/java/org/distorted/library/effect/EffectType.java
@@ -71,12 +71,12 @@ public enum EffectType
  */
   public static void reset(int[] maxtable)
     {
-    maxtable[0] =10;  // By default, there can be a maximum 10 MATRIX effects in a single
-                      // EffectQueueMatrix at any given time. This can be changed with a call
-                      // to EffectQueueMatrix.setMax(int)
-    maxtable[1] = 5;  // Max 5 VERTEX Effects
-    maxtable[2] = 5;  // Max 5 FRAGMENT Effects
-    maxtable[3] = 3;  // Max 3 POSTPROCESSING Effects
+    maxtable[0] =100;  // By default, there can be a maximum 100 MATRIX effects in a single
+                       // EffectQueueMatrix at any given time. This can be changed with a call
+                       // to EffectQueueMatrix.setMax(int)
+    maxtable[1] =100;  // Max 100 VERTEX Effects
+    maxtable[2] =100;  // Max 100 FRAGMENT Effects
+    maxtable[3] =  3;  // Max 3 POSTPROCESSING Effects
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueue.java b/src/main/java/org/distorted/library/effectqueue/EffectQueue.java
index 714dc21..dd2a679 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueue.java
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueue.java
@@ -25,6 +25,8 @@ import org.distorted.library.effect.EffectType;
 import org.distorted.library.main.DistortedLibrary;
 import org.distorted.library.main.InternalMaster;
 import org.distorted.library.main.InternalStackFrameList;
+import org.distorted.library.uniformblock.UniformBlockFloatUniforms;
+import org.distorted.library.uniformblock.UniformBlockIntUniforms;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -39,8 +41,10 @@ public abstract class EffectQueue implements InternalMaster.Slave
   {
   public static final int MAIN_VARIANTS = 4; // Number of Main program variants (ATM 4: MAIN, MAIN OIT, PREPROCESS, FULL)
 
-  static final int PROP_VERT_UBO_BINDING = 5;
-  static final int PROP_FRAG_UBO_BINDING = 6;
+  static final int VERT_INT_UBO_BINDING = 5;
+  static final int VERT_FLO_UBO_BINDING = 6;
+  static final int FRAG_INT_UBO_BINDING = 7;
+  static final int FRAG_FLO_UBO_BINDING = 8;
 
   private static final int CREATE = 0;
   private static final int ATTACH = 1;
@@ -50,11 +54,10 @@ public abstract class EffectQueue implements InternalMaster.Slave
   long mTime;
   int mNumEffects;              // 'ToBe' will be more than mNumEffects if doWork() hasn't
   private int mNumEffectsToBe;  // added them yet (or less if it hasn't removed some yet)
-  private final int mNumFloatUniforms;
   Effect[] mEffects;
 
-  float[] mFloatUniforms;
-  UniformBlockProperties mUBP;
+  UniformBlockFloatUniforms mUBF;
+  UniformBlockIntUniforms mUBI;
 
   private long mID;
   private final int mIndex;
@@ -83,15 +86,13 @@ public abstract class EffectQueue implements InternalMaster.Slave
    
   EffectQueue(int numFloatUniforms, int numIntUniforms, int index)
     {
-    mCreated          = false;
-    mTime             = 0;
-    mID               = 0;
-    mNumEffects       = 0;
-    mNumEffectsToBe   = 0;
-    mIndex            = index;
-    mNumFloatUniforms = numFloatUniforms;
-
-    mUBP  = new UniformBlockProperties(numIntUniforms);
+    mCreated        = false;
+    mTime           = 0;
+    mID             = 0;
+    mNumEffects     = 0;
+    mNumEffectsToBe = 0;
+    mIndex          = index;
+
     mJobs = new ArrayList<>();
 
     mJobs.add(new Job(CREATE,numFloatUniforms,numIntUniforms, false,null)); // create the stuff that depends on max number
@@ -105,15 +106,13 @@ public abstract class EffectQueue implements InternalMaster.Slave
     {
     if( !source.mCreated )
       {
-      mCreated          = false;
-      mTime             = 0;
-      mID               = 0;
-      mNumEffects       = 0;
-      mNumEffectsToBe   = 0;
-      mIndex            = source.mIndex;
-      mNumFloatUniforms = source.mNumFloatUniforms;
-
-      mUBP  = new UniformBlockProperties(source.mUBP);
+      mCreated        = false;
+      mTime           = 0;
+      mID             = 0;
+      mNumEffects     = 0;
+      mNumEffectsToBe = 0;
+      mIndex          = source.mIndex;
+
       mJobs = new ArrayList<>();
 
       int numJobs = source.mJobs.size();
@@ -128,23 +127,22 @@ public abstract class EffectQueue implements InternalMaster.Slave
       }
     else
       {
-      mCreated          = true;
-      mTime             = source.mTime;
-      mID               = source.mID;
-      mNumEffects       = source.mNumEffects;
-      mNumEffectsToBe   = source.mNumEffectsToBe;
-      mIndex            = source.mIndex;
-      mNumFloatUniforms = source.mNumFloatUniforms;
-
-      mUBP  = new UniformBlockProperties(source.mUBP);
+      mCreated        = true;
+      mTime           = source.mTime;
+      mID             = source.mID;
+      mNumEffects     = source.mNumEffects;
+      mNumEffectsToBe = source.mNumEffectsToBe;
+      mIndex          = source.mIndex;
+
       mJobs = new ArrayList<>();
 
       int max = InternalStackFrameList.getMax(mIndex);
 
       if( max>0 )
         {
-        mEffects       = new Effect[max];
-        mFloatUniforms = new float[max*source.mNumFloatUniforms];
+        mEffects= new Effect[max];
+        mUBI  = new UniformBlockIntUniforms(source.mUBI);
+        mUBF  = new UniformBlockFloatUniforms(source.mUBF);
 
         if( mNumEffects>=0 )
           {
@@ -255,7 +253,7 @@ public abstract class EffectQueue implements InternalMaster.Slave
       mNumEffects--;
       mEffects[pos].remQueue(this);
       System.arraycopy(mEffects, pos+1, mEffects, pos, mNumEffects-pos);
-      mUBP.remove(pos, mNumEffects);
+      mUBI.remove(pos, mNumEffects);
       mEffects[mNumEffects] = null;
       }
     }
@@ -265,12 +263,12 @@ public abstract class EffectQueue implements InternalMaster.Slave
   private void addNow(int pos, Effect effect)
     {
     mEffects[pos] = effect;
-    mUBP.addOrdinal(pos, effect.getName().ordinal() );
+    mUBI.addOrdinal(pos, effect.getName().ordinal() );
     effect.addQueue(this);
 
     if( mIndex==EffectType.VERTEX.ordinal() )
       {
-      mUBP.addAssociations(pos,effect);
+      mUBI.addAssociations(pos,effect);
       }
     }
 
@@ -413,7 +411,7 @@ public abstract class EffectQueue implements InternalMaster.Slave
       {
       if (mEffects[j].getID() == effectID)
         {
-        mUBP.addAssociations(j,mEffects[j]);
+        mUBI.addAssociations(j,mEffects[j]);
         }
       }
     }
@@ -422,7 +420,8 @@ public abstract class EffectQueue implements InternalMaster.Slave
 
   public void markForDeletion()
     {
-    mUBP.markForDeletion();
+    mUBI.markForDeletion();
+    mUBF.markForDeletion();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -442,9 +441,9 @@ public abstract class EffectQueue implements InternalMaster.Slave
         case CREATE: int max = InternalStackFrameList.getMax(mIndex);
                      if( max>0 )
                        {
-                       mEffects       = new Effect[max];
-                       mFloatUniforms = new float[max*job.num1];
-                       mUBP           = new UniformBlockProperties(job.num2);
+                       mEffects= new Effect[max];
+                       mUBF    = new UniformBlockFloatUniforms(job.num1, max);
+                       mUBI    = new UniformBlockIntUniforms  (job.num2, max);
                        }
                      mCreated = true;
 
@@ -462,7 +461,7 @@ public abstract class EffectQueue implements InternalMaster.Slave
                        else if( position<=mNumEffects )
                          {
                          System.arraycopy(mEffects, position, mEffects, position+1, mNumEffects-position);
-                         mUBP.makeHole(position, mNumEffects);
+                         mUBI.makeHole(position, mNumEffects);
                          addNow(position,job.effect);
                          mNumEffects++;
                          changed = true;
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueueFragment.java b/src/main/java/org/distorted/library/effectqueue/EffectQueueFragment.java
index 3e47803..a2204b5 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueueFragment.java
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueueFragment.java
@@ -34,9 +34,9 @@ class EffectQueueFragment extends EffectQueue
 
   private static final int INDEX = EffectType.FRAGMENT.ordinal();
 
-  private final static int[] mNumEffectsH   = new int[MAIN_VARIANTS];
-  private final static int[] mUniformsH     = new int[MAIN_VARIANTS];
-  private final static int[] mPropBlockIndex= new int[MAIN_VARIANTS];
+  private final static int[] mNumEffectsH  = new int[MAIN_VARIANTS];
+  private final static int[] mIntBlockIndex= new int[MAIN_VARIANTS];
+  private final static int[] mFloBlockIndex= new int[MAIN_VARIANTS];
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
    
@@ -56,9 +56,9 @@ class EffectQueueFragment extends EffectQueue
 
   static void uniforms(int mProgramH, int variant)
     {
-    mNumEffectsH[variant]    = GLES30.glGetUniformLocation  ( mProgramH, "fNumEffects");
-    mUniformsH[variant]      = GLES30.glGetUniformLocation  ( mProgramH, "fUniforms");
-    mPropBlockIndex[variant] = GLES30.glGetUniformBlockIndex( mProgramH, "fUniformProperties");
+    mNumEffectsH[variant]   = GLES30.glGetUniformLocation  ( mProgramH, "fNumEffects");
+    mIntBlockIndex[variant] = GLES30.glGetUniformBlockIndex( mProgramH, "fUniformProperties");
+    mFloBlockIndex[variant] = GLES30.glGetUniformBlockIndex( mProgramH, "fUniformFloats");
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -67,14 +67,21 @@ class EffectQueueFragment extends EffectQueue
     { 
     if( currTime==mTime ) return;
     if( mTime==0 ) mTime = currTime;
-    long step = (currTime-mTime);
 
-    for(int i=0; i<mNumEffects; i++)
+    if( mNumEffects>0 )
       {
-      if( mEffects[i].compute(mFloatUniforms, NUM_FLOAT_UNIFORMS*i, currTime, step) )
+      long step = (currTime-mTime);
+      float[] array = mUBF.getBackingArray();
+
+      for(int i=0; i<mNumEffects; i++)
         {
-        EffectMessageSender.newMessage(mEffects[i]);
+        if( mEffects[i].compute(array, NUM_FLOAT_UNIFORMS*i, currTime, step) )
+          {
+          EffectMessageSender.newMessage(mEffects[i]);
+          }
         }
+
+      mUBF.invalidate();
       }
 
     mTime = currTime;  
@@ -88,10 +95,11 @@ class EffectQueueFragment extends EffectQueue
 
     if( mNumEffects>0 )
       {
-      int index = mUBP.getIndex();
-      GLES30.glBindBufferBase(GLES30.GL_UNIFORM_BUFFER, PROP_FRAG_UBO_BINDING, index);
-      GLES30.glUniformBlockBinding(programH, mPropBlockIndex[variant], PROP_FRAG_UBO_BINDING);
-      GLES30.glUniform4fv( mUniformsH[variant],(NUM_FLOAT_UNIFORMS/4)*mNumEffects, mFloatUniforms, 0);
+      GLES30.glBindBufferBase(GLES30.GL_UNIFORM_BUFFER, FRAG_INT_UBO_BINDING, mUBI.getIndex());
+      GLES30.glUniformBlockBinding(programH, mIntBlockIndex[variant], FRAG_INT_UBO_BINDING);
+
+      GLES30.glBindBufferBase(GLES30.GL_UNIFORM_BUFFER, FRAG_FLO_UBO_BINDING, mUBF.getIndex());
+      GLES30.glUniformBlockBinding(programH, mFloBlockIndex[variant], FRAG_FLO_UBO_BINDING);
       }  
     }
   }
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueueMatrix.java b/src/main/java/org/distorted/library/effectqueue/EffectQueueMatrix.java
index 94ce63e..6d01a36 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueueMatrix.java
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueueMatrix.java
@@ -35,13 +35,13 @@ class EffectQueueMatrix extends EffectQueue
 
   private static final int INDEX = EffectType.MATRIX.ordinal();
 
-  private static float[] mMVPMatrix       = new float[16];
-  private static float[] mModelViewMatrixP= new float[16];
-  private static float[] mModelViewMatrixV= new float[16];
+  private static final float[] mMVPMatrix       = new float[16];
+  private static final float[] mModelViewMatrixP= new float[16];
+  private static final float[] mModelViewMatrixV= new float[16];
 
-  private static int[] mMVPMatrixH = new int[MAIN_VARIANTS];
-  private static int[] mMVMatrixPH = new int[MAIN_VARIANTS];
-  private static int[] mMVMatrixVH = new int[MAIN_VARIANTS];
+  private static final int[] mMVPMatrixH = new int[MAIN_VARIANTS];
+  private static final int[] mMVMatrixPH = new int[MAIN_VARIANTS];
+  private static final int[] mMVMatrixVH = new int[MAIN_VARIANTS];
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
    
@@ -73,10 +73,11 @@ class EffectQueueMatrix extends EffectQueue
     if( currTime==mTime ) return;
     if( mTime==0 ) mTime = currTime;
     long step = (currTime-mTime);
+    float[] array = mUBF.getBackingArray();
 
     for(int i=0; i<mNumEffects; i++)
       {
-      if( mEffects[i].compute(mFloatUniforms, NUM_FLOAT_UNIFORMS*i, currTime, step) )
+      if( mEffects[i].compute(array, NUM_FLOAT_UNIFORMS*i, currTime, step) )
         {
         EffectMessageSender.newMessage(mEffects[i]);
         }
@@ -114,10 +115,12 @@ class EffectQueueMatrix extends EffectQueue
     mModelViewMatrixV[14] = mModelViewMatrixP[14];
     mModelViewMatrixV[15] = mModelViewMatrixP[15];
 
+    float[] array = mUBF.getBackingArray();
+
     // the 'Model' part of the MV matrix
     for(int i=mNumEffects-1; i>=0; i--)
       {
-      ((MatrixEffect)mEffects[i]).apply(mModelViewMatrixP,mModelViewMatrixV,mFloatUniforms,i);
+      ((MatrixEffect)mEffects[i]).apply(mModelViewMatrixP,mModelViewMatrixV,array,i);
       }
 
     // combined Model-View-Projection matrix
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.java b/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.java
index aa60ecf..5b0d528 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.java
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.java
@@ -85,30 +85,31 @@ public class EffectQueuePostprocess extends EffectQueue
     mR = mG = mB = mA = 0.0f;
     mHalo = 0;
     int halo;
+    float[] array = mUBF.getBackingArray();
 
     for(int i=0; i<mNumEffects; i++)
       {
       // first zero out the 'alpha' because BLUR effect will not overwrite this (it is a 1D effect)
       // and if previously there was a GLOW effect here then mA would be non-zero and we don't want
       // that (see preprocess())
-      mFloatUniforms[NUM_FLOAT_UNIFORMS*i+5]=0.0f;
+      array[NUM_FLOAT_UNIFORMS*i+5]=0.0f;
 
-      if( mEffects[i].compute(mFloatUniforms, NUM_FLOAT_UNIFORMS*i, currTime, step) )
+      if( mEffects[i].compute(array, NUM_FLOAT_UNIFORMS*i, currTime, step) )
         {
         EffectMessageSender.newMessage(mEffects[i]);
         }
 
-      halo = (int)mFloatUniforms[NUM_FLOAT_UNIFORMS*i];
+      halo = (int)array[NUM_FLOAT_UNIFORMS*i];
       if( halo>mHalo ) mHalo = halo;
       }
 
     // TODO  (now only really works in case of 1 effect!)
     if( mNumEffects>0 )
       {
-      mR = mFloatUniforms[2];
-      mG = mFloatUniforms[3];
-      mB = mFloatUniforms[4];
-      mA = mFloatUniforms[5];
+      mR = array[2];
+      mG = array[3];
+      mB = array[4];
+      mA = array[5];
       }
 
     mTime = currTime;
@@ -211,12 +212,13 @@ public class EffectQueuePostprocess extends EffectQueue
   public int postprocess(DistortedFramebuffer buffer)
     {
     int numRenders = 0;
+    float[] array = mUBF.getBackingArray();
 
     GLES30.glDisable(GLES30.GL_BLEND);
 
     for(int i=0; i<mNumEffects; i++)
       {
-      numRenders += ((PostprocessEffect)mEffects[i]).apply(mFloatUniforms,NUM_FLOAT_UNIFORMS*i, buffer);
+      numRenders += ((PostprocessEffect)mEffects[i]).apply(array,NUM_FLOAT_UNIFORMS*i, buffer);
       }
 
     GLES30.glEnable(GLES30.GL_BLEND);
diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueueVertex.java b/src/main/java/org/distorted/library/effectqueue/EffectQueueVertex.java
index b915cd9..1335957 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueueVertex.java
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueueVertex.java
@@ -38,10 +38,10 @@ public class EffectQueueVertex extends EffectQueue
 
   private static final int INDEX = EffectType.VERTEX.ordinal();
 
-  private final static int[] mNumEffectsH   = new int[MAIN_VARIANTS];
-  private final static int[] mInflateH      = new int[MAIN_VARIANTS];
-  private final static int[] mUniformsH     = new int[MAIN_VARIANTS];
-  private final static int[] mPropBlockIndex= new int[MAIN_VARIANTS];
+  private final static int[] mNumEffectsH  = new int[MAIN_VARIANTS];
+  private final static int[] mInflateH     = new int[MAIN_VARIANTS];
+  private final static int[] mIntBlockIndex= new int[MAIN_VARIANTS];
+  private final static int[] mFloBlockIndex= new int[MAIN_VARIANTS];
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
    
@@ -61,10 +61,10 @@ public class EffectQueueVertex extends EffectQueue
 
   static void uniforms(int mProgramH, int variant)
     {
-    mNumEffectsH[variant]    = GLES30.glGetUniformLocation  ( mProgramH, "vNumEffects");
-    mInflateH[variant]       = GLES30.glGetUniformLocation  ( mProgramH, "u_Inflate");
-    mUniformsH[variant]      = GLES30.glGetUniformLocation  ( mProgramH, "vUniforms");
-    mPropBlockIndex[variant] = GLES30.glGetUniformBlockIndex( mProgramH, "vUniformProperties");
+    mNumEffectsH[variant]   = GLES30.glGetUniformLocation  ( mProgramH, "vNumEffects");
+    mInflateH[variant]      = GLES30.glGetUniformLocation  ( mProgramH, "u_Inflate");
+    mIntBlockIndex[variant] = GLES30.glGetUniformBlockIndex( mProgramH, "vUniformProperties");
+    mFloBlockIndex[variant] = GLES30.glGetUniformBlockIndex( mProgramH, "vUniformFloats");
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -77,14 +77,21 @@ public class EffectQueueVertex extends EffectQueue
     {
     if( currTime==mTime ) return;
     if( mTime==0 ) mTime = currTime;
-    long step = (currTime-mTime);
 
-    for(int i=0; i<mNumEffects; i++)
+    if( mNumEffects>0 )
       {
-      if( mEffects[i].compute(mFloatUniforms, NUM_FLOAT_UNIFORMS*i, currTime, step) )
+      long step = (currTime-mTime);
+      float[] array = mUBF.getBackingArray();
+
+      for(int i=0; i<mNumEffects; i++)
         {
-        EffectMessageSender.newMessage(mEffects[i]);
+        if( mEffects[i].compute(array, NUM_FLOAT_UNIFORMS*i, currTime, step) )
+          {
+          EffectMessageSender.newMessage(mEffects[i]);
+          }
         }
+
+      mUBF.invalidate();
       }
 
     mTime = currTime;
@@ -103,10 +110,11 @@ public class EffectQueueVertex extends EffectQueue
 
     if( mNumEffects>0 )
       {
-      int index = mUBP.getIndex();
-      GLES30.glBindBufferBase(GLES30.GL_UNIFORM_BUFFER, PROP_VERT_UBO_BINDING, index);
-      GLES30.glUniformBlockBinding(programH, mPropBlockIndex[variant], PROP_VERT_UBO_BINDING);
-      GLES30.glUniform4fv( mUniformsH[variant]  ,(NUM_FLOAT_UNIFORMS/4)*mNumEffects, mFloatUniforms , 0);
+      GLES30.glBindBufferBase(GLES30.GL_UNIFORM_BUFFER, VERT_INT_UBO_BINDING, mUBI.getIndex());
+      GLES30.glUniformBlockBinding(programH, mIntBlockIndex[variant], VERT_INT_UBO_BINDING);
+
+      GLES30.glBindBufferBase(GLES30.GL_UNIFORM_BUFFER, VERT_FLO_UBO_BINDING, mUBF.getIndex());
+      GLES30.glUniformBlockBinding(programH, mFloBlockIndex[variant], VERT_FLO_UBO_BINDING);
       }
     }
   }
diff --git a/src/main/java/org/distorted/library/effectqueue/UniformBlockProperties.java b/src/main/java/org/distorted/library/effectqueue/UniformBlockProperties.java
deleted file mode 100644
index 4100818..0000000
--- a/src/main/java/org/distorted/library/effectqueue/UniformBlockProperties.java
+++ /dev/null
@@ -1,133 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2021 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.effectqueue;
-
-import android.opengl.GLES30;
-
-import org.distorted.library.effect.Effect;
-import org.distorted.library.main.InternalBuffer;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class UniformBlockProperties
-  {
-  private static final int NUM_BYTES = 4*100;
-
-  private final InternalBuffer mUBO;
-  private final int[] mProperties;
-  private final int mNumIntUniforms;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  UniformBlockProperties(int numUniforms)
-    {
-    mProperties= new int[NUM_BYTES/4];
-    mNumIntUniforms = numUniforms;
-    mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER, GLES30.GL_STATIC_READ);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  UniformBlockProperties(UniformBlockProperties original)
-    {
-    int size = original.mProperties.length;
-    mProperties= new int[size];
-    System.arraycopy(original.mProperties, 0, mProperties, 0, size);
-    mNumIntUniforms = original.mNumIntUniforms;
-
-    mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER, GLES30.GL_STATIC_READ);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  int getIndex()
-    {
-    return mUBO.createImmediatelyInt( NUM_BYTES, mProperties);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void remove(int pos, int numEffects)
-    {
-    System.arraycopy(mProperties, mNumIntUniforms*(pos+1), mProperties, mNumIntUniforms*pos, mNumIntUniforms*(numEffects-pos) );
-    mUBO.invalidate();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void makeHole(int pos, int numEffects)
-    {
-    System.arraycopy(mProperties, mNumIntUniforms*pos, mProperties, mNumIntUniforms*(pos+1), mNumIntUniforms*(numEffects-pos) );
-    mUBO.invalidate();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void addOrdinal(int pos, int ordinal)
-    {
-    mProperties[mNumIntUniforms*pos] = ordinal;
-    mUBO.invalidate();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void addAssociations(int pos, Effect effect)
-    {
-    effect.writeAssociations(mProperties, mNumIntUniforms*pos+1, mNumIntUniforms*pos+3);
-    mUBO.invalidate();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void markForDeletion()
-    {
-    mUBO.markForDeletion();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void print(int num)
-    {
-    StringBuilder builder = new StringBuilder();
-
-    builder.append(mUBO.getID());
-    builder.append(':');
-
-    for(int i=0; i<6; i++)
-      {
-      builder.append(' ');
-      builder.append(mProperties[4*i  ]);
-      builder.append(' ');
-      builder.append(mProperties[4*i+1]);
-      builder.append(' ');
-      builder.append(mProperties[4*i+2]);
-      builder.append(',');
-      }
-
-    builder.append(' ');
-    builder.append('(');
-    builder.append(num);
-    builder.append(')');
-
-    String res = builder.toString();
-
-    android.util.Log.e("ubp", res);
-    }
-  }
diff --git a/src/main/java/org/distorted/library/main/InternalBuffer.java b/src/main/java/org/distorted/library/main/InternalBuffer.java
index e5fff78..0d46d71 100644
--- a/src/main/java/org/distorted/library/main/InternalBuffer.java
+++ b/src/main/java/org/distorted/library/main/InternalBuffer.java
@@ -42,9 +42,9 @@ public class InternalBuffer extends InternalObject
   private static final int RECREATE = 1;
   private static final int UPDATE   = 2;
 
-  private int mStatus;
+  private int mStatus, mSize;
   private final int[] mIndex;
-  private int mTarget, mSize, mUsage;
+  private final int mTarget, mUsage;
   private Buffer mBuffer;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/mesh/MeshBase.java b/src/main/java/org/distorted/library/mesh/MeshBase.java
index 754b263..a5b773f 100644
--- a/src/main/java/org/distorted/library/mesh/MeshBase.java
+++ b/src/main/java/org/distorted/library/mesh/MeshBase.java
@@ -29,6 +29,8 @@ import org.distorted.library.effectqueue.EffectQueue;
 import org.distorted.library.main.InternalBuffer;
 import org.distorted.library.program.DistortedProgram;
 import org.distorted.library.type.Static4D;
+import org.distorted.library.uniformblock.UniformBlockAssociation;
+import org.distorted.library.uniformblock.UniformBlockCenter;
 
 import java.io.DataOutputStream;
 import java.io.IOException;
@@ -49,7 +51,7 @@ public abstract class MeshBase
    {
    private static final int ASSOC_UBO_BINDING  = 3;
    private static final int CENTER_UBO_BINDING = 4;
-           static final int MAX_EFFECT_COMPONENTS= 100;
+   public  static final int MAX_EFFECT_COMPONENTS= 100;
 
    // sizes of attributes of an individual vertex.
    private static final int POS_DATA_SIZE= 3; // vertex coordinates: x,y,z
diff --git a/src/main/java/org/distorted/library/mesh/UniformBlockAssociation.java b/src/main/java/org/distorted/library/mesh/UniformBlockAssociation.java
deleted file mode 100644
index fd7b02a..0000000
--- a/src/main/java/org/distorted/library/mesh/UniformBlockAssociation.java
+++ /dev/null
@@ -1,124 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2020 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.mesh;
-
-import android.opengl.GLES30;
-
-import org.distorted.library.main.InternalBuffer;
-
-import static org.distorted.library.mesh.MeshBase.MAX_EFFECT_COMPONENTS;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class UniformBlockAssociation
-  {
-  private static final int BLOCK_SIZE = 16*MAX_EFFECT_COMPONENTS;
-  private static final int DEFAULT_ASSOCIATION = 0xffffffff;
-
-  private final InternalBuffer mUBO;
-  private final int[] mAssociations;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  UniformBlockAssociation()
-    {
-    mAssociations= new int[BLOCK_SIZE/4];
-
-    for(int i=0; i<MAX_EFFECT_COMPONENTS; i++)
-      {
-      mAssociations[4*i  ] = DEFAULT_ASSOCIATION;
-      mAssociations[4*i+2] = i;
-      }
-
-    mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER, GLES30.GL_STATIC_READ);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  UniformBlockAssociation(UniformBlockAssociation original)
-    {
-    int size = original.mAssociations.length;
-    mAssociations= new int[size];
-    System.arraycopy(original.mAssociations, 0, mAssociations, 0, size);
-
-    mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER, GLES30.GL_STATIC_READ);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  boolean matchesAssociation( int comp, int andAssoc, int equAssoc)
-    {
-    return (andAssoc & mAssociations[4*comp]) != 0 || (equAssoc == mAssociations[4*comp+2]);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void setEffectAssociationNow(int comp, int andAssociation, int equAssociation)
-    {
-    mAssociations[4*comp  ] = andAssociation;
-    mAssociations[4*comp+2] = equAssociation;
-
-    mUBO.invalidate();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  int getIndex()
-    {
-    return mUBO.createImmediatelyInt( BLOCK_SIZE, mAssociations);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void copy(int compTo, UniformBlockAssociation assocFrom, int compFrom)
-    {
-    mAssociations[4*compTo  ] = assocFrom.mAssociations[4*compFrom  ];
-    mAssociations[4*compTo+2] = assocFrom.mAssociations[4*compFrom+2];
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void markForDeletion()
-    {
-    mUBO.markForDeletion();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void print()
-    {
-    StringBuilder builder = new StringBuilder();
-
-    builder.append(mUBO.getID());
-    builder.append(' ');
-
-    for(int i=0; i<8; i++)
-      {
-      builder.append(mAssociations[4*i]);
-      builder.append(' ');
-      builder.append(mAssociations[4*i+2]);
-      builder.append(' ');
-      }
-
-    String res = builder.toString();
-
-    android.util.Log.e("uba", res);
-    }
-  }
diff --git a/src/main/java/org/distorted/library/mesh/UniformBlockCenter.java b/src/main/java/org/distorted/library/mesh/UniformBlockCenter.java
deleted file mode 100644
index 5b32a78..0000000
--- a/src/main/java/org/distorted/library/mesh/UniformBlockCenter.java
+++ /dev/null
@@ -1,96 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2020 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted is free software: you can redistribute it and/or modify                             //
-// it under the terms of the GNU General Public License as published by                          //
-// the Free Software Foundation, either version 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted is distributed in the hope that it will be useful,                                  //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
-// GNU General Public License for more details.                                                  //
-//                                                                                               //
-// You should have received a copy of the GNU General Public License                             //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.mesh;
-
-import android.opengl.GLES30;
-
-import org.distorted.library.main.InternalBuffer;
-
-import static org.distorted.library.mesh.MeshBase.MAX_EFFECT_COMPONENTS;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-class UniformBlockCenter
-  {
-  private static final int BLOCK_SIZE = 16*MAX_EFFECT_COMPONENTS;
-
-  private final InternalBuffer mUBO;
-  private final float[] mCenter;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  UniformBlockCenter()
-    {
-    mCenter= new float[BLOCK_SIZE/4];
-    mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER, GLES30.GL_STATIC_READ);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  UniformBlockCenter(UniformBlockCenter original)
-    {
-    int size = original.mCenter.length;
-    mCenter= new float[size];
-    System.arraycopy(original.mCenter, 0, mCenter, 0, size);
-
-    mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER, GLES30.GL_STATIC_READ);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void setEffectCenterNow(int comp, float x, float y, float z)
-    {
-    mCenter[4*comp  ] = x;
-    mCenter[4*comp+1] = y;
-    mCenter[4*comp+2] = z;
-
-    mUBO.invalidate();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  int getIndex()
-    {
-    return mUBO.createImmediatelyFloat( BLOCK_SIZE, mCenter);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void copy(int compTo, UniformBlockCenter blockFrom, int compFrom)
-    {
-    mCenter[4*compTo  ] = blockFrom.mCenter[4*compFrom  ];
-    mCenter[4*compTo+1] = blockFrom.mCenter[4*compFrom+1];
-    mCenter[4*compTo+2] = blockFrom.mCenter[4*compFrom+2];
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void markForDeletion()
-    {
-    mUBO.markForDeletion();
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  float[] getBackingArray()
-    {
-    return mCenter;
-    }
-  }
diff --git a/src/main/java/org/distorted/library/uniformblock/UniformBlockAssociation.java b/src/main/java/org/distorted/library/uniformblock/UniformBlockAssociation.java
new file mode 100644
index 0000000..507baa4
--- /dev/null
+++ b/src/main/java/org/distorted/library/uniformblock/UniformBlockAssociation.java
@@ -0,0 +1,128 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.uniformblock;
+
+import android.opengl.GLES30;
+
+import org.distorted.library.main.InternalBuffer;
+
+import static org.distorted.library.mesh.MeshBase.MAX_EFFECT_COMPONENTS;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Not part of public API, do not document
+ *
+ * @y.exclude
+ */
+public class UniformBlockAssociation
+  {
+  private static final int BLOCK_SIZE = 16*MAX_EFFECT_COMPONENTS;
+  private static final int DEFAULT_ASSOCIATION = 0xffffffff;
+
+  private final InternalBuffer mUBO;
+  private final int[] mAssociations;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public UniformBlockAssociation()
+    {
+    mAssociations= new int[BLOCK_SIZE/4];
+
+    for(int i=0; i<MAX_EFFECT_COMPONENTS; i++)
+      {
+      mAssociations[4*i  ] = DEFAULT_ASSOCIATION;
+      mAssociations[4*i+2] = i;
+      }
+
+    mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER, GLES30.GL_STATIC_READ);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public UniformBlockAssociation(UniformBlockAssociation original)
+    {
+    int size = original.mAssociations.length;
+    mAssociations= new int[size];
+    System.arraycopy(original.mAssociations, 0, mAssociations, 0, size);
+
+    mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER, GLES30.GL_STATIC_READ);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public boolean matchesAssociation( int comp, int andAssoc, int equAssoc)
+    {
+    return (andAssoc & mAssociations[4*comp]) != 0 || (equAssoc == mAssociations[4*comp+2]);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void setEffectAssociationNow(int comp, int andAssociation, int equAssociation)
+    {
+    mAssociations[4*comp  ] = andAssociation;
+    mAssociations[4*comp+2] = equAssociation;
+
+    mUBO.invalidate();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getIndex()
+    {
+    return mUBO.createImmediatelyInt( BLOCK_SIZE, mAssociations);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void copy(int compTo, UniformBlockAssociation assocFrom, int compFrom)
+    {
+    mAssociations[4*compTo  ] = assocFrom.mAssociations[4*compFrom  ];
+    mAssociations[4*compTo+2] = assocFrom.mAssociations[4*compFrom+2];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void markForDeletion()
+    {
+    mUBO.markForDeletion();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void print()
+    {
+    StringBuilder builder = new StringBuilder();
+
+    builder.append(mUBO.getID());
+    builder.append(' ');
+
+    for(int i=0; i<8; i++)
+      {
+      builder.append(mAssociations[4*i]);
+      builder.append(' ');
+      builder.append(mAssociations[4*i+2]);
+      builder.append(' ');
+      }
+
+    String res = builder.toString();
+
+    android.util.Log.e("uba", res);
+    }
+  }
diff --git a/src/main/java/org/distorted/library/uniformblock/UniformBlockCenter.java b/src/main/java/org/distorted/library/uniformblock/UniformBlockCenter.java
new file mode 100644
index 0000000..72dbba0
--- /dev/null
+++ b/src/main/java/org/distorted/library/uniformblock/UniformBlockCenter.java
@@ -0,0 +1,99 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2020 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.uniformblock;
+
+import android.opengl.GLES30;
+
+import org.distorted.library.main.InternalBuffer;
+
+import static org.distorted.library.mesh.MeshBase.MAX_EFFECT_COMPONENTS;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Not part of public API, do not document
+ *
+ * @y.exclude
+ */
+public class UniformBlockCenter
+  {
+  private static final int BLOCK_SIZE = 16*MAX_EFFECT_COMPONENTS;
+
+  private final InternalBuffer mUBO;
+  private final float[] mArray;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public UniformBlockCenter()
+    {
+    mArray= new float[BLOCK_SIZE/4];
+    mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER, GLES30.GL_STATIC_READ);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public UniformBlockCenter(UniformBlockCenter original)
+    {
+    int size = original.mArray.length;
+    mArray= new float[size];
+    System.arraycopy(original.mArray, 0, mArray, 0, size);
+    mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER, GLES30.GL_STATIC_READ);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void setEffectCenterNow(int comp, float x, float y, float z)
+    {
+    mArray[4*comp  ] = x;
+    mArray[4*comp+1] = y;
+    mArray[4*comp+2] = z;
+
+    mUBO.invalidate();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getIndex()
+    {
+    return mUBO.createImmediatelyFloat( BLOCK_SIZE, mArray);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void copy(int compTo, UniformBlockCenter blockFrom, int compFrom)
+    {
+    mArray[4*compTo  ] = blockFrom.mArray[4*compFrom  ];
+    mArray[4*compTo+1] = blockFrom.mArray[4*compFrom+1];
+    mArray[4*compTo+2] = blockFrom.mArray[4*compFrom+2];
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void markForDeletion()
+    {
+    mUBO.markForDeletion();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float[] getBackingArray()
+    {
+    return mArray;
+    }
+  }
diff --git a/src/main/java/org/distorted/library/uniformblock/UniformBlockFloatUniforms.java b/src/main/java/org/distorted/library/uniformblock/UniformBlockFloatUniforms.java
new file mode 100644
index 0000000..c6f0cc6
--- /dev/null
+++ b/src/main/java/org/distorted/library/uniformblock/UniformBlockFloatUniforms.java
@@ -0,0 +1,125 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2021 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.uniformblock;
+
+import android.opengl.GLES30;
+
+import org.distorted.library.main.InternalBuffer;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Not part of public API, do not document
+ *
+ * @y.exclude
+ */
+public class UniformBlockFloatUniforms
+  {
+  private final InternalBuffer mUBO;
+  private final float[] mArray;
+  private final int mNumUniforms, mSize;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public UniformBlockFloatUniforms(int numUniforms, int size)
+    {
+    mNumUniforms = numUniforms;
+    mSize        = size;
+    mArray= new float[mNumUniforms*mSize];
+    mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER, GLES30.GL_STATIC_READ);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public UniformBlockFloatUniforms(UniformBlockFloatUniforms original)
+    {
+    mNumUniforms = original.mNumUniforms;
+    mSize        = original.mSize;
+    mArray= new float[mNumUniforms*mSize];
+    System.arraycopy(original.mArray, 0, mArray, 0, 3*mSize);
+    mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER, GLES30.GL_STATIC_READ);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getIndex()
+    {
+    return mUBO.createImmediatelyFloat( 4*mNumUniforms*mSize, mArray);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void remove(int pos, int numEffects)
+    {
+    System.arraycopy(mArray, mNumUniforms*(pos+1), mArray, mNumUniforms*pos, mNumUniforms*(numEffects-pos) );
+    mUBO.invalidate();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void markForDeletion()
+    {
+    mUBO.markForDeletion();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void invalidate()
+    {
+    mUBO.invalidate();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public float[] getBackingArray()
+    {
+    return mArray;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void print(int num)
+    {
+    StringBuilder builder = new StringBuilder();
+
+    builder.append(mUBO.getID());
+    builder.append(':');
+
+    for(int i=0; i<6; i++)
+      {
+      builder.append(' ');
+      builder.append(mArray[4*i  ]);
+      builder.append(' ');
+      builder.append(mArray[4*i+1]);
+      builder.append(' ');
+      builder.append(mArray[4*i+2]);
+      builder.append(',');
+      }
+
+    builder.append(' ');
+    builder.append('(');
+    builder.append(num);
+    builder.append(')');
+
+    String res = builder.toString();
+
+    android.util.Log.e("ubp", res);
+    }
+  }
diff --git a/src/main/java/org/distorted/library/uniformblock/UniformBlockIntUniforms.java b/src/main/java/org/distorted/library/uniformblock/UniformBlockIntUniforms.java
new file mode 100644
index 0000000..067aa92
--- /dev/null
+++ b/src/main/java/org/distorted/library/uniformblock/UniformBlockIntUniforms.java
@@ -0,0 +1,135 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2021 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted is free software: you can redistribute it and/or modify                             //
+// it under the terms of the GNU General Public License as published by                          //
+// the Free Software Foundation, either version 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted is distributed in the hope that it will be useful,                                  //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
+// GNU General Public License for more details.                                                  //
+//                                                                                               //
+// You should have received a copy of the GNU General Public License                             //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.uniformblock;
+
+import android.opengl.GLES30;
+
+import org.distorted.library.effect.Effect;
+import org.distorted.library.main.InternalBuffer;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Not part of public API, do not document
+ *
+ * @y.exclude
+ */
+public class UniformBlockIntUniforms
+  {
+  private final InternalBuffer mUBO;
+  private final int[] mArray;
+  private final int mNumUniforms, mSize;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public UniformBlockIntUniforms(int numUniforms, int size)
+    {
+    mNumUniforms = numUniforms;
+    mSize        = size;
+    mArray= new int[mNumUniforms*mSize];
+    mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER, GLES30.GL_STATIC_READ);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public UniformBlockIntUniforms(UniformBlockIntUniforms original)
+    {
+    mNumUniforms = original.mNumUniforms;
+    mSize        = original.mSize;
+    mArray= new int[mNumUniforms*mSize];
+    System.arraycopy(original.mArray, 0, mArray, 0, mSize);
+    mUBO = new InternalBuffer(GLES30.GL_UNIFORM_BUFFER, GLES30.GL_STATIC_READ);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getIndex()
+    {
+    return mUBO.createImmediatelyInt( 4*mNumUniforms*mSize, mArray);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void remove(int pos, int numEffects)
+    {
+    System.arraycopy(mArray, mNumUniforms*(pos+1), mArray, mNumUniforms*pos, mNumUniforms*(numEffects-pos) );
+    mUBO.invalidate();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void makeHole(int pos, int numEffects)
+    {
+    System.arraycopy(mArray, mNumUniforms*pos, mArray, mNumUniforms*(pos+1), mNumUniforms*(numEffects-pos) );
+    mUBO.invalidate();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void addOrdinal(int pos, int ordinal)
+    {
+    mArray[mNumUniforms*pos] = ordinal;
+    mUBO.invalidate();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void addAssociations(int pos, Effect effect)
+    {
+    effect.writeAssociations(mArray, mNumUniforms*pos+1, mNumUniforms*pos+3);
+    mUBO.invalidate();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void markForDeletion()
+    {
+    mUBO.markForDeletion();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void print(int num)
+    {
+    StringBuilder builder = new StringBuilder();
+
+    builder.append(mUBO.getID());
+    builder.append(':');
+
+    for(int i=0; i<6; i++)
+      {
+      builder.append(' ');
+      builder.append(mArray[4*i  ]);
+      builder.append(' ');
+      builder.append(mArray[4*i+1]);
+      builder.append(' ');
+      builder.append(mArray[4*i+2]);
+      builder.append(',');
+      }
+
+    builder.append(' ');
+    builder.append('(');
+    builder.append(num);
+    builder.append(')');
+
+    String res = builder.toString();
+
+    android.util.Log.e("ubp", res);
+    }
+  }
diff --git a/src/main/res/raw/main_fragment_shader.glsl b/src/main/res/raw/main_fragment_shader.glsl
index 8c80e1f..50c7535 100644
--- a/src/main/res/raw/main_fragment_shader.glsl
+++ b/src/main/res/raw/main_fragment_shader.glsl
@@ -45,17 +45,21 @@ layout (std430,binding=1) buffer linkedlist  // first (u_Size.x*u_Size.y) uints
 #endif
 
 #if NUM_FRAGMENT>0
-uniform int fNumEffects;                // total number of fragment effects
+uniform int fNumEffects;                     // total number of fragment effects
 
 layout (std140) uniform fUniformProperties
   {
-  ivec4 fProperties[NUM_FRAGMENT];      // their properties, 4 ints:
-                                        // name of the effect, unused, unused, unused
+  ivec4 fProperties[NUM_FRAGMENT];           // their properties, 4 ints:
+                                             // name of the effect, unused, unused, unused
+  };
+
+layout (std140) uniform fUniformFloats
+  {
+  vec4 fUniforms[3*NUM_FRAGMENT];            // i-th effect is 3 consecutive vec4's: [3*i], [3*i+1], [3*i+2].
+                                             // The first vec4 is the Interpolated values,
+                                             // second vec4: first float - cache, next 3: Center, the third - the Region.
   };
 
-uniform vec4 fUniforms[3*NUM_FRAGMENT]; // i-th effect is 3 consecutive vec4's: [3*i], [3*i+1], [3*i+2].
-                                        // The first vec4 is the Interpolated values,
-                                        // second vec4: first float - cache, next 3: Center, the third - the Region.
 #endif    // NUM_FRAGMENT>0
 
 #ifdef OIT
diff --git a/src/main/res/raw/main_vertex_shader.glsl b/src/main/res/raw/main_vertex_shader.glsl
index b4f2761..4f3eeb9 100644
--- a/src/main/res/raw/main_vertex_shader.glsl
+++ b/src/main/res/raw/main_vertex_shader.glsl
@@ -56,9 +56,12 @@ layout (std140) uniform vUniformProperties
                                       // 4: effect's EQU association
   };
 
-uniform vec4 vUniforms[3*NUM_VERTEX]; // i-th effect is 3 consecutive vec4's: [3*i], [3*i+1], [3*i+2].
+layout (std140) uniform vUniformFloats
+  {
+  vec4 vUniforms[3*NUM_VERTEX];       // i-th effect is 3 consecutive vec4's: [3*i], [3*i+1], [3*i+2].
                                       // The first vec4 is the Interpolated values,
                                       // second vec4: first float - cache, next 3: Center, the third -  the Region.
+  };
 
 layout (std140) uniform componentAssociation
   {
