commit 040cd18cdc30d3b578ce4c6276e78cd75c1db517
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Wed Jun 20 00:04:49 2018 +0100

    Port all the 'non-controversial' changes from order-independent-transparency branch.

diff --git a/src/main/java/org/distorted/library/effect/PostprocessEffectBlur.java b/src/main/java/org/distorted/library/effect/PostprocessEffectBlur.java
index 2038c63..08cec17 100644
--- a/src/main/java/org/distorted/library/effect/PostprocessEffectBlur.java
+++ b/src/main/java/org/distorted/library/effect/PostprocessEffectBlur.java
@@ -144,8 +144,15 @@ public class PostprocessEffectBlur extends PostprocessEffect
     {
     if( mProgram1 ==null)
       {
-      mProgram1 = mPrograms.get(mIndex1);
-      mProgram2 = mPrograms.get(mIndex2);
+      try
+        {
+        mProgram1 = mPrograms.get(mIndex1);
+        mProgram2 = mPrograms.get(mIndex2);
+        }
+      catch(Exception ex)
+        {
+        return 0;
+        }
       }
 
     DistortedRenderState.useStencilMark();
diff --git a/src/main/java/org/distorted/library/effect/PostprocessEffectGlow.java b/src/main/java/org/distorted/library/effect/PostprocessEffectGlow.java
index 8d20066..e0cd858 100644
--- a/src/main/java/org/distorted/library/effect/PostprocessEffectGlow.java
+++ b/src/main/java/org/distorted/library/effect/PostprocessEffectGlow.java
@@ -148,8 +148,15 @@ public class PostprocessEffectGlow extends PostprocessEffect
     {
     if( mProgram1 ==null)
       {
-      mProgram1 = mPrograms.get(mIndex1);
-      mProgram2 = mPrograms.get(mIndex2);
+      try
+        {
+        mProgram1 = mPrograms.get(mIndex1);
+        mProgram2 = mPrograms.get(mIndex2);
+        }
+      catch(Exception ex)
+        {
+        return 0;
+        }
       }
 
     DistortedRenderState.useStencilMark();
diff --git a/src/main/java/org/distorted/library/main/Distorted.java b/src/main/java/org/distorted/library/main/Distorted.java
index 70be620..86f75e4 100644
--- a/src/main/java/org/distorted/library/main/Distorted.java
+++ b/src/main/java/org/distorted/library/main/Distorted.java
@@ -28,7 +28,6 @@ import org.distorted.library.effect.Effect;
 import org.distorted.library.effect.FragmentEffect;
 import org.distorted.library.effect.PostprocessEffect;
 import org.distorted.library.effect.VertexEffect;
-import org.distorted.library.program.*;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
@@ -106,21 +105,15 @@ public class Distorted
  * Needs to be called from a thread holding the OpenGL context.
  *   
  * @param context Context of the App using the library - used to open up Resources and read Shader code.
- * @throws FragmentCompilationException Fragment Shader failed to compile
- * @throws VertexCompilationException   Vertex Shader failed to compile
- * @throws VertexUniformsException      Too many uniforms in the Vertex Shader
- * @throws FragmentUniformsException    Too many uniforms in the Fragment Shader
- * @throws LinkingException             Shader failed to link
  */
   public static void onCreate(final Context context)
-  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
     {
     final ActivityManager activityManager     = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
     final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
     android.util.Log.e("DISTORTED", "Using OpenGL ES "+configurationInfo.getGlEsVersion());
 
-    GLSL = ( (configurationInfo.reqGlEsVersion>>16)>=3 ? 300 : 100 );
-    GLSL_VERSION= (GLSL==100 ? "#version 100\n" : "#version 300 es\n");
+    GLSL = ( (configurationInfo.reqGlEsVersion>>16)>=3 ? 310 : 100 );
+    GLSL_VERSION= (GLSL==100 ? "#version 100\n" : "#version 310 es\n");
 
     EffectMessageSender.startSending();
 
@@ -133,7 +126,8 @@ public class Distorted
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
- * Call this so that the Library can release its internal data structures.
+ * Call this so that the Library can mark OpenGL objects that would need to be recreated when we
+ * get resumed.
  * Must be called from Activity.onPause().
  */
   public static void onPause()
@@ -162,13 +156,4 @@ public class Distorted
 
     mInitialized = false;
     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return 2 or 3 depending if we have OpenGL Es 2.0 or 3.x context created.
- */
-  public static int getGlVersion()
-    {
-    return GLSL == 300 ? 3:2;
-    }
   }
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/main/DistortedEffects.java b/src/main/java/org/distorted/library/main/DistortedEffects.java
index 63a928d..3154b63 100644
--- a/src/main/java/org/distorted/library/main/DistortedEffects.java
+++ b/src/main/java/org/distorted/library/main/DistortedEffects.java
@@ -31,11 +31,6 @@ import org.distorted.library.effect.FragmentEffect;
 import org.distorted.library.effect.VertexEffect;
 import org.distorted.library.message.EffectListener;
 import org.distorted.library.program.DistortedProgram;
-import org.distorted.library.program.FragmentCompilationException;
-import org.distorted.library.program.FragmentUniformsException;
-import org.distorted.library.program.LinkingException;
-import org.distorted.library.program.VertexCompilationException;
-import org.distorted.library.program.VertexUniformsException;
 
 import java.io.InputStream;
 import java.nio.ByteBuffer;
@@ -92,7 +87,6 @@ public class DistortedEffects
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   static void createProgram(Resources resources)
-  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
     {
     // MAIN PROGRAM ////////////////////////////////////
     final InputStream mainVertStream = resources.openRawResource(R.raw.main_vertex_shader);
@@ -113,8 +107,16 @@ public class DistortedEffects
 
     String[] feedback = { "v_Position", "v_endPosition" };
 
-    mMainProgram = new DistortedProgram( mainVertStream, mainFragStream, mainVertHeader, mainFragHeader,
-                                         enabledEffectV, enabledEffectF, Distorted.GLSL, feedback);
+    try
+      {
+      mMainProgram = new DistortedProgram(mainVertStream, mainFragStream, mainVertHeader, mainFragHeader,
+                                          enabledEffectV, enabledEffectF, Distorted.GLSL, feedback);
+      }
+    catch(Exception e)
+      {
+      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile MAIN program: "+e.getMessage());
+      throw new RuntimeException(e.getMessage());
+      }
 
     int mainProgramH = mMainProgram.getProgramHandle();
     EffectQueueFragment.getUniforms(mainProgramH);
@@ -135,7 +137,7 @@ public class DistortedEffects
       }
     catch(Exception e)
       {
-      Log.e("EFFECTS", "exception trying to compile BLIT program: "+e.getMessage());
+      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile BLIT program: "+e.getMessage());
       throw new RuntimeException(e.getMessage());
       }
 
@@ -153,7 +155,7 @@ public class DistortedEffects
       }
     catch(Exception e)
       {
-      Log.e("EFFECTS", "exception trying to compile BLIT DEPTH program: "+e.getMessage());
+      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile BLIT DEPTH program: "+e.getMessage());
       throw new RuntimeException(e.getMessage());
       }
 
@@ -173,7 +175,7 @@ public class DistortedEffects
       }
     catch(Exception e)
       {
-      Log.e("EFFECTS", "exception trying to compile NORMAL program: "+e.getMessage());
+      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile NORMAL program: "+e.getMessage());
       throw new RuntimeException(e.getMessage());
       }
 
@@ -284,23 +286,11 @@ public class DistortedEffects
     mMainProgram.useProgram();
     GLES31.glUniform1i(mMainTextureH, 0);
 
-    if( Distorted.GLSL >= 300 )
-      {
-      GLES31.glBindBuffer(GLES31.GL_ARRAY_BUFFER, mesh.mAttVBO[0]);
-      GLES31.glVertexAttribPointer(mMainProgram.mAttribute[0], MeshObject.POS_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET0);
-      GLES31.glVertexAttribPointer(mMainProgram.mAttribute[1], MeshObject.NOR_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET1);
-      GLES31.glVertexAttribPointer(mMainProgram.mAttribute[2], MeshObject.TEX_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET2);
-      GLES31.glBindBuffer(GLES31.GL_ARRAY_BUFFER, 0);
-      }
-    else
-      {
-      mesh.mVertAttribs.position(0);
-      GLES31.glVertexAttribPointer(mMainProgram.mAttribute[0], MeshObject.POS_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, mesh.mVertAttribs);
-      mesh.mVertAttribs.position(MeshObject.POS_DATA_SIZE);
-      GLES31.glVertexAttribPointer(mMainProgram.mAttribute[1], MeshObject.NOR_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, mesh.mVertAttribs);
-      mesh.mVertAttribs.position(MeshObject.POS_DATA_SIZE+MeshObject.NOR_DATA_SIZE);
-      GLES31.glVertexAttribPointer(mMainProgram.mAttribute[2], MeshObject.TEX_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, mesh.mVertAttribs);
-      }
+    GLES31.glBindBuffer(GLES31.GL_ARRAY_BUFFER, mesh.mAttVBO[0]);
+    GLES31.glVertexAttribPointer(mMainProgram.mAttribute[0], MeshObject.POS_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET0);
+    GLES31.glVertexAttribPointer(mMainProgram.mAttribute[1], MeshObject.NOR_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET1);
+    GLES31.glVertexAttribPointer(mMainProgram.mAttribute[2], MeshObject.TEX_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET2);
+    GLES31.glBindBuffer(GLES31.GL_ARRAY_BUFFER, 0);
 
     mM.send(surface,halfW,halfH,halfZ,marginInPixels);
     mV.send();
diff --git a/src/main/java/org/distorted/library/main/DistortedFramebuffer.java b/src/main/java/org/distorted/library/main/DistortedFramebuffer.java
index b3a158f..ad84f4e 100644
--- a/src/main/java/org/distorted/library/main/DistortedFramebuffer.java
+++ b/src/main/java/org/distorted/library/main/DistortedFramebuffer.java
@@ -31,11 +31,6 @@ import android.opengl.GLES31;
 public class DistortedFramebuffer extends DistortedOutputSurface implements DistortedInputSurface
   {
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void prepareDebug(long time) {}
-  void renderDebug(long time)  {}
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // Must be called from a thread holding OpenGL Context
 
@@ -201,20 +196,6 @@ public class DistortedFramebuffer extends DistortedOutputSurface implements Dist
     super(width,height,NOT_CREATED_YET,numcolors,depthStencil,NOT_CREATED_YET,TYPE_USER);
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-/**
- * Create new offscreen Framebuffer with COLOR0 attachment only.
- *
- * @param width Width of the COLOR0 attachment.
- * @param height Height of the COLOR0 attachment.
- */
-  @SuppressWarnings("unused")
-  public DistortedFramebuffer(int width, int height)
-    {
-    super(width,height,NOT_CREATED_YET, 1, NO_DEPTH_NO_STENCIL,NOT_CREATED_YET,TYPE_USER);
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Bind the underlying rectangle of pixels as a OpenGL Texture.
diff --git a/src/main/java/org/distorted/library/main/DistortedMaster.java b/src/main/java/org/distorted/library/main/DistortedMaster.java
index 2305b37..38a45fa 100644
--- a/src/main/java/org/distorted/library/main/DistortedMaster.java
+++ b/src/main/java/org/distorted/library/main/DistortedMaster.java
@@ -96,16 +96,27 @@ public class DistortedMaster
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// can make this logarithmic but the typical number of children is very small anyway
+// Can make this logarithmic but the typical number of children is very small anyway.
+//
+// We want to keep same buckets next to each other, while avoiding changes in order of the children
+// (if possible!) We want to keep bucket=0 (i.e. the non-postprocessed children) at the beginning.
 
-  static void addSorted(ArrayList<DistortedNode> mChildren, DistortedNode newChild)
+  static void addSortingByBuckets(ArrayList<DistortedNode> mChildren, DistortedNode newChild)
     {
     int i,num = mChildren.size();
     long bucket = newChild.getPostprocessQueue().getID();
+    boolean sameBucket = false;
 
     for(i=0; i<num; i++)
       {
-      if( mChildren.get(i).getPostprocessQueue().getID() > bucket ) break;
+      if( mChildren.get(i).getPostprocessQueue().getID() == bucket )
+        {
+        sameBucket=true;
+        }
+      else if( sameBucket || bucket==0 )
+        {
+        break;
+        }
       }
 
     mChildren.add(i,newChild);
diff --git a/src/main/java/org/distorted/library/main/DistortedNode.java b/src/main/java/org/distorted/library/main/DistortedNode.java
index 1b300da..7f3d728 100644
--- a/src/main/java/org/distorted/library/main/DistortedNode.java
+++ b/src/main/java/org/distorted/library/main/DistortedNode.java
@@ -228,7 +228,7 @@ public class DistortedNode implements DistortedMaster.Slave
     if( mNumChildren[0]==0 && newData.mFBO!=null )
       {
       newData.mFBO.markForDeletion();
-      android.util.Log.d("NODE", "ERROR!! this NodeData cannot possibly contain a non-null FBO!! "+newData.mFBO.getID() );
+      android.util.Log.e("NODE", "ERROR!! this NodeData cannot possibly contain a non-null FBO!! "+newData.mFBO.getID() );
       newData.mFBO = null;
       }
 
@@ -283,7 +283,6 @@ public class DistortedNode implements DistortedMaster.Slave
 
     if( input.setAsInput() )
       {
-      surface.setAsOutput(currTime);
       mState.apply();
       GLES31.glDisable(GLES31.GL_BLEND);
       mEffects.drawPriv(mSurface.getWidth()/2.0f, mSurface.getHeight()/2.0f, mMesh, surface, currTime, 0);
@@ -303,7 +302,6 @@ public class DistortedNode implements DistortedMaster.Slave
 
     if( input.setAsInput() )
       {
-      surface.setAsOutput(currTime);
       mState.apply();
       mEffects.drawPriv(mSurface.getWidth()/2.0f, mSurface.getHeight()/2.0f, mMesh, surface, currTime, 0);
       return 1;
@@ -364,13 +362,13 @@ public class DistortedNode implements DistortedMaster.Slave
     if( mParent!=null )
       {
       mParent.mChildren.remove(this);
-      DistortedMaster.addSorted(mParent.mChildren,this);
+      DistortedMaster.addSortingByBuckets(mParent.mChildren,this);
       }
     else if( mSurfaceParent!=null )
       {
       ArrayList<DistortedNode> children = mSurfaceParent.getChildren();
       children.remove(this);
-      DistortedMaster.addSorted(children,this);
+      DistortedMaster.addSortingByBuckets(children,this);
       }
     }
 
@@ -651,7 +649,7 @@ public class DistortedNode implements DistortedMaster.Slave
                      if( mChildren==null ) mChildren = new ArrayList<>(2);
                      job.node.mParent = this;
                      job.node.mSurfaceParent = null;
-                     DistortedMaster.addSorted(mChildren,job.node);
+                     DistortedMaster.addSortingByBuckets(mChildren,job.node);
                      mNumChildren[0]++;
                      break;
         case DETACH: numChanges++;
@@ -678,7 +676,7 @@ public class DistortedNode implements DistortedMaster.Slave
                        }
                      break;
         case SORT  : mChildren.remove(job.node);
-                     DistortedMaster.addSorted(mChildren,job.node);
+                     DistortedMaster.addSortingByBuckets(mChildren,job.node);
                      break;
         }
       }
diff --git a/src/main/java/org/distorted/library/main/DistortedObject.java b/src/main/java/org/distorted/library/main/DistortedObject.java
index cf1d82b..e147544 100644
--- a/src/main/java/org/distorted/library/main/DistortedObject.java
+++ b/src/main/java/org/distorted/library/main/DistortedObject.java
@@ -119,11 +119,18 @@ abstract class DistortedObject
     DistortedObject object;
     int num = mDoneList.size();
 
-    for(int i=0; i<num; i++)
+    try
+      {
+      for (int i = 0; i < num; i++)
+        {
+        object = mDoneList.removeFirst();
+        mToDoMap.put(object.mID, object.new Job(object, JOB_CREATE));
+        object.recreate();
+        }
+      }
+    catch( Exception ex )
       {
-      object = mDoneList.removeFirst();
-      mToDoMap.put(object.mID, object.new Job(object,JOB_CREATE) );
-      object.recreate();
+      // something else removed an object in the meantime; ignore
       }
 
     mToDo = true;
diff --git a/src/main/java/org/distorted/library/main/DistortedObjectCounter.java b/src/main/java/org/distorted/library/main/DistortedObjectCounter.java
deleted file mode 100644
index 7276c6d..0000000
--- a/src/main/java/org/distorted/library/main/DistortedObjectCounter.java
+++ /dev/null
@@ -1,97 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 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.main;
-
-import java.util.ArrayList;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Return unique IDs of objects in a lazy way. When we create a new Object, return the lowest unused
- * integer. When an objects gets deleted, its ID then gets marked unused. We do not try to 'collapse'
- * unused holes.
- */
-class DistortedObjectCounter
-  {
-  private ArrayList<Integer> mUsed;
-  private int mFirstUnused;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  DistortedObjectCounter()
-    {
-    mUsed        = new ArrayList<>();
-    mFirstUnused = 0;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  int returnNext()
-    {
-    int size = mUsed.size();
-
-    if( mFirstUnused<size )
-      {
-      int ret = mFirstUnused;
-
-      mUsed.set(mFirstUnused,1);
-
-      int found=-1;
-
-      for(int i=mFirstUnused+1; i<size; i++ )
-        {
-        if( mUsed.get(i)==0 )
-          {
-          found = i;
-          break;
-          }
-        }
-
-      mFirstUnused = found<0 ? size : found;
-
-      return ret;
-      }
-
-    mUsed.add(1);
-    mFirstUnused++;
-
-    return size;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void release(int objID)
-    {
-    int size = mUsed.size();
-
-    if( objID<size && objID>=0 && mUsed.get(objID)==1 )
-      {
-      mUsed.set(objID,0);
-      if( objID<mFirstUnused ) mFirstUnused = objID;
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void releaseAll()
-    {
-    mUsed.clear();
-    mFirstUnused = 0;
-    }
-  }
diff --git a/src/main/java/org/distorted/library/main/DistortedOutputSurface.java b/src/main/java/org/distorted/library/main/DistortedOutputSurface.java
index 20ccb3a..9946c2d 100644
--- a/src/main/java/org/distorted/library/main/DistortedOutputSurface.java
+++ b/src/main/java/org/distorted/library/main/DistortedOutputSurface.java
@@ -34,13 +34,6 @@ import java.util.ArrayList;
  */
 public abstract class DistortedOutputSurface extends DistortedSurface implements DistortedMaster.Slave
 {
-//////////// DEBUG FLAGS /////////////////////////////////////////////
-/**
- * When rendering a Screen, show FPS in the upper-left corner?
- */
-public static final int DEBUG_FPS = 1;
-//////////// END DEBUG FLAGS /////////////////////////////////////////
-
 /**
  * Do not create DEPTH or STENCIL attachment
  */
@@ -77,7 +70,7 @@ public static final int DEBUG_FPS = 1;
   private ArrayList<Job> mJobs = new ArrayList<>();
 
   // Global buffers used for postprocessing.
-  private static DistortedOutputSurface[] mBuffer = new DistortedOutputSurface[EffectQuality.LENGTH];
+  private static DistortedOutputSurface[] mBuffer = null;
 
   private long mTime;
   private float mFOV;
@@ -95,18 +88,11 @@ public static final int DEBUG_FPS = 1;
   private int mClear;
   float mMipmap;
 
-  private int mDebugLevel;
-
   int mRealWidth;   // the Surface can be backed up with a texture that is
   int mRealHeight;  // larger than the viewport we have to it.
                     // mWidth,mHeight are the sizes of the Viewport, those -
                     // sizes of the backing up texture.
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  abstract void prepareDebug(long time);
-  abstract void renderDebug(long time);
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   DistortedOutputSurface(int width, int height, int createColor, int numcolors, int depthStencil, int fbo, int type)
@@ -128,7 +114,6 @@ public static final int DEBUG_FPS = 1;
     mDepthStencilH[0]= 0;
 
     mTime = 0;
-    mDebugLevel = 0;
 
     mClearR = 0.0f;
     mClearG = 0.0f;
@@ -184,8 +169,9 @@ public static final int DEBUG_FPS = 1;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private static void createBuffers(int width, int height, float near)
+  private static void createPostprocessingBuffers(int width, int height, float near)
     {
+    mBuffer = new DistortedOutputSurface[EffectQuality.LENGTH];
     float mipmap=1.0f;
 
     for(int j=0; j<EffectQuality.LENGTH; j++)
@@ -221,21 +207,26 @@ public static final int DEBUG_FPS = 1;
 
   static synchronized void onDestroy()
     {
-    for(int j=0; j<EffectQuality.LENGTH; j++)
+    if( mBuffer!=null )
       {
-      mBuffer[j] = null;
+      for (int i=0; i<EffectQuality.LENGTH; i++)
+        {
+        mBuffer[i] = null;
+        }
+
+      mBuffer = null;
       }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // The postprocessing buffers mBuffer[] are generally speaking too large (there's just one static
-// set of them) so before we use them for output, we need to adjust the Vieport as if they were
+// set of them) so before we use them for output, we need to adjust the Viewport as if they were
 // smaller. That takes care of outputting pixels to them. When we use them as input, we have to
 // adjust the texture coords - see the get{Width|Height}Correction functions.
 
-  private static void cloneViewport(DistortedOutputSurface from)
+  private static void clonePostprocessingViewport(DistortedOutputSurface from)
     {
-    if( mBuffer[0].mWidth != from.mWidth )
+    if( mBuffer[0].mWidth != from.mWidth || mBuffer[0].mHeight != from.mHeight )
       {
       DistortedOutputSurface surface;
 
@@ -304,6 +295,18 @@ public static final int DEBUG_FPS = 1;
     return 1;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void clear()
+    {
+    DistortedRenderState.colorDepthStencilOn();
+    GLES31.glClearColor(mClearR, mClearG, mClearB, mClearA);
+    GLES31.glClearDepthf(mClearDepth);
+    GLES31.glClearStencil(mClearStencil);
+    GLES31.glClear(mClear);
+    DistortedRenderState.colorDepthStencilRestore();
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // Render all children, one by one. If there are no postprocessing effects, just render to THIS.
 // Otherwise, render to a buffer and on each change of Postprocessing Bucket, apply the postprocessing
@@ -322,14 +325,22 @@ public static final int DEBUG_FPS = 1;
       currQueue = child1.getPostprocessQueue();
       currBucket= currQueue.getID();
 
-      if( currBucket==0 ) numRenders += child1.draw(time,this);
+      if( currBucket==0 )
+        {
+        GLES31.glBindFramebuffer(GLES31.GL_FRAMEBUFFER, mFBOH[0]);
+        numRenders += child1.draw(time,this);
+        }
       else
         {
-        if( mBuffer[0]==null ) createBuffers(mWidth,mHeight,mNear);
+        if( mBuffer==null ) createPostprocessingBuffers(mWidth,mHeight,mNear);
 
         if( lastBucket!=currBucket )
           {
-          if( lastBucket!=0 )
+          if( lastBucket==0 )
+            {
+            clonePostprocessingViewport(this);
+            }
+          else
             {
             for(int j=bucketChange; j<i; j++)
               {
@@ -346,10 +357,9 @@ public static final int DEBUG_FPS = 1;
           internalQuality = currQueue.getInternalQuality();
           quality         = currQueue.getQuality();
           bucketChange    = i;
-
-          cloneViewport(this);
           }
 
+        mBuffer[quality].setAsOutput(time);
         child1.drawNoBlend(time,mBuffer[quality]);
 
         if( i==numChildren-1 )
@@ -363,11 +373,11 @@ public static final int DEBUG_FPS = 1;
           numRenders += currQueue.postprocess(mBuffer);
           numRenders += blitWithDepth(time, mBuffer[quality]);
           }
-        }
+        } // end postprocessed child case
 
       lastQueue = currQueue;
       lastBucket= currBucket;
-      }
+      } // end main for loop
 
     return numRenders;
     }
@@ -423,20 +433,6 @@ public static final int DEBUG_FPS = 1;
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Make the library show various debugging information.
- * <p>
- * Currently only DEBUG_FPS - show FPS in the upper-left corner of every Screen - is defined.
- *
- * @param bitmask 0, or a bitmask of DEBUG_** flags to enable (currently only DEBUG_FPS defined)
- */
-  public void setDebug(int bitmask)
-    {
-    if( this instanceof DistortedScreen )
-      mDebugLevel = bitmask;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
 
 /**
  * Draws all the attached children to this OutputSurface.
@@ -448,8 +444,6 @@ public static final int DEBUG_FPS = 1;
  */
   public int render(long time)
     {
-    if( mDebugLevel!=0 ) prepareDebug(time);
-
     // change tree topology (attach and detach children)
 /*
     boolean changed1 =
@@ -494,8 +488,6 @@ public static final int DEBUG_FPS = 1;
     setAsOutput(time);
     numRenders += renderChildren(time,mNumChildren,mChildren);
 
-    if( mDebugLevel != 0 ) renderDebug(time);
-
     return numRenders;
     }
 
@@ -515,37 +507,7 @@ public static final int DEBUG_FPS = 1;
     if( mTime!=time )
       {
       mTime = time;
-      DistortedRenderState.colorDepthStencilOn();
-      GLES31.glClearColor(mClearR, mClearG, mClearB, mClearA);
-      GLES31.glClearDepthf(mClearDepth);
-      GLES31.glClearStencil(mClearStencil);
-      GLES31.glClear(mClear);
-      DistortedRenderState.colorDepthStencilRestore();
-/*
-      if( mSSBO[0]>=0 )
-        {
-        // yes, this DOES keep on working when 'value' overflows into negative territory.
-        int value = mIntBuffer.get(FRAME_DELAY*mSurfaceID+mLastIndex);
-
-        if( value-mLastValue[mLastIndex]!=mLastDiff )
-          {
-          android.util.Log.d("surface", "id " + mSurfaceID +
-                             (mType == TYPE_USER ? " USER" : (mType == TYPE_SYST ? " SYST" : " TREE")) +
-                             " viewport: (" + mWidth + "x" + mHeight + ") last frame: " + (value - mLastValue[mLastIndex])
-                             + " avg: " + (mAvgSum/RUNNING_AVERAGE)
-                            );
-          }
-
-        mLastDiff = value-mLastValue[mLastIndex];
-        mLastValue[mLastIndex] = value;
-
-        mAvgSum += (mLastDiff-mRunningAvg[mAvgIndex]);
-        mRunningAvg[mAvgIndex] = mLastDiff;
-        if( ++mAvgIndex>=RUNNING_AVERAGE ) mAvgIndex =0;
-        }
-
-      if( ++mLastIndex >= FRAME_DELAY ) mLastIndex=0;
-*/
+      clear();
       }
     }
 
@@ -683,7 +645,7 @@ public static final int DEBUG_FPS = 1;
       mNear=0.99f;
       }
 
-    if( mBuffer[0]!=null )
+    if( mBuffer!=null )
       {
       for(int j=0; j<EffectQuality.LENGTH; j++) mBuffer[j].mNear = mNear;
       }
@@ -874,7 +836,7 @@ public static final int DEBUG_FPS = 1;
         {
         case ATTACH: if( mChildren==null ) mChildren = new ArrayList<>(2);
                      job.node.setSurfaceParent(this);
-                     DistortedMaster.addSorted(mChildren,job.node);
+                     DistortedMaster.addSortingByBuckets(mChildren,job.node);
                      mNumChildren++;
                      break;
         case DETACH: if( mNumChildren>0 && mChildren.remove(job.node) )
@@ -897,7 +859,7 @@ public static final int DEBUG_FPS = 1;
                        }
                      break;
         case SORT  : mChildren.remove(job.node);
-                     DistortedMaster.addSorted(mChildren,job.node);
+                     DistortedMaster.addSortingByBuckets(mChildren,job.node);
                      break;
         }
       }
diff --git a/src/main/java/org/distorted/library/main/DistortedScreen.java b/src/main/java/org/distorted/library/main/DistortedScreen.java
index e74025c..25ce826 100644
--- a/src/main/java/org/distorted/library/main/DistortedScreen.java
+++ b/src/main/java/org/distorted/library/main/DistortedScreen.java
@@ -1,5 +1,5 @@
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
+// Copyright 2018 Leszek Koltunski                                                               //
 //                                                                                               //
 // This file is part of Distorted.                                                               //
 //                                                                                               //
@@ -34,9 +34,11 @@ import org.distorted.library.type.Static3D;
  * <p>
  * User is able to render to it just like to a DistortedFramebuffer.
  */
-public class DistortedScreen extends DistortedOutputSurface
+public class DistortedScreen extends DistortedFramebuffer
   {
   ///// DEBUGGING ONLY /////////////////////////
+  private boolean mShowFPS;
+
   private static final int NUM_FRAMES  = 100;
 
   private MeshObject fpsMesh;
@@ -46,27 +48,92 @@ public class DistortedScreen extends DistortedOutputSurface
   private Bitmap fpsBitmap;
   private Paint mPaint;
   private int fpsH, fpsW;
-  private String fpsString = "";
+  private String fpsString;
   private long lastTime=0;
   private long[] durations;
   private int currDuration;
   private static MatrixEffectMove mMoveEffect = new MatrixEffectMove( new Static3D(5,5,0) );
-  private boolean mInitialized;
+  ///// END DEBUGGING //////////////////////////
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// here we don't manage underlying OpenGL assets ourselves
-
-  void create()   {}
-  void delete()   {}
-  void recreate() {}
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Create a new Screen. Initially 1x1 in size.
+ * <p>
+ * Has to be followed by a 'resize()' to set the size.
+ */
+  public DistortedScreen()
+    {
+    super(1,1,1,BOTH_DEPTH_STENCIL);
+    mShowFPS = false;
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Draws all the attached children to this OutputSurface.
+ * <p>
+ * Must be called from a thread holding OpenGL Context.
+ *
+ * @param time Current time, in milliseconds. This will be passed to all the Effects stored in the children Nodes.
+ * @return Number of objects rendered.
+ */
+  public int render(long time)
+    {
+    if( mShowFPS )
+      {
+      if( lastTime==0 ) lastTime = time;
 
-  private void initialize()
+      currDuration++;
+      if (currDuration >= NUM_FRAMES) currDuration = 0;
+      durations[NUM_FRAMES] += ((time - lastTime) - durations[currDuration]);
+      durations[currDuration] = time - lastTime;
+
+      fpsString = "" + ((int)(10000.0f*NUM_FRAMES/durations[NUM_FRAMES]))/10.0f;
+
+      mPaint.setColor(0xffffffff);
+      fpsCanvas.drawRect(0, 0, fpsW, fpsH, mPaint);
+      mPaint.setColor(0xff000000);
+      fpsCanvas.drawText(fpsString, fpsW/2, 0.75f*fpsH, mPaint);
+      fpsTexture.setTexture(fpsBitmap);
+
+      lastTime = time;
+      }
+
+    int numrender = super.render(time);
+
+    GLES31.glBindFramebuffer(GLES31.GL_FRAMEBUFFER, 0);
+    clear();
+    setAsInput();
+    GLES31.glColorMask(true,true,true,true);
+    GLES31.glDepthMask(false);
+    GLES31.glDisable(GLES31.GL_STENCIL_TEST);
+    GLES31.glDisable(GLES31.GL_DEPTH_TEST);
+    GLES31.glDisable(GLES31.GL_BLEND);
+
+    DistortedEffects.blitPriv(this);
+
+    if( mShowFPS && fpsTexture.setAsInput())
+      {
+      fpsEffects.drawPriv(fpsW / 2.0f, fpsH / 2.0f, fpsMesh, this, time, 0);
+      }
+
+    return numrender+1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Make the library show Frames Per Second in the upper-left corner.
+ * <p>
+ */
+  public void showFPS()
     {
+    mShowFPS = true;
+
     fpsW = 120;
     fpsH =  70;
 
+    fpsString = "";
     fpsBitmap = Bitmap.createBitmap(fpsW,fpsH, Bitmap.Config.ARGB_8888);
     fpsMesh = new MeshFlat(1,1);
     fpsTexture = new DistortedTexture(fpsW,fpsH);
@@ -85,61 +152,5 @@ public class DistortedScreen extends DistortedOutputSurface
 
     for(int i=0; i<NUM_FRAMES+1; i++) durations[i]=16;  // Assume FPS will be
     durations[NUM_FRAMES] = NUM_FRAMES*16;              // close to 1000/16 ~ 60
-    mInitialized=true;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void prepareDebug(long time)
-    {
-    if( !mInitialized ) initialize();
-
-    if( lastTime==0 ) lastTime = time;
-
-    currDuration++;
-    if (currDuration >= NUM_FRAMES) currDuration = 0;
-    durations[NUM_FRAMES] += ((time - lastTime) - durations[currDuration]);
-    durations[currDuration] = time - lastTime;
-
-    fpsString = "" + ((int)(10000.0f*NUM_FRAMES/durations[NUM_FRAMES]))/10.0f;
-
-    mPaint.setColor(0xffffffff);
-    fpsCanvas.drawRect(0, 0, fpsW, fpsH, mPaint);
-    mPaint.setColor(0xff000000);
-    fpsCanvas.drawText(fpsString, fpsW/2, 0.75f*fpsH, mPaint);
-    fpsTexture.setTexture(fpsBitmap);
-
-    lastTime = time;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  void renderDebug(long time)
-    {
-    if (fpsTexture.setAsInput())
-      {
-      setAsOutput(time);
-      GLES31.glColorMask(true,true,true,true);
-      GLES31.glDepthMask(false);
-      GLES31.glDisable(GLES31.GL_STENCIL_TEST);
-      GLES31.glDisable(GLES31.GL_DEPTH_TEST);
-      fpsEffects.drawPriv(fpsW/2.0f, fpsH/2.0f, fpsMesh, this, time, 0);
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// PUBLIC API
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Create a new Screen.
- * <p>
- * Has to be followed by a 'resize()' to set the size.
- */
-  public DistortedScreen()
-    {
-    // Screen also has to be created (3rd arg 'NOT_CREATED_YET') because of the SSBO inside OutputSurface.
-    super(0,0,NOT_CREATED_YET,1,DEPTH_NO_STENCIL,0,TYPE_USER);
-
-    mInitialized = false;
     }
-  }
+  }
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/main/EffectQueuePostprocess.java b/src/main/java/org/distorted/library/main/EffectQueuePostprocess.java
index d7e9da7..d3bb295 100644
--- a/src/main/java/org/distorted/library/main/EffectQueuePostprocess.java
+++ b/src/main/java/org/distorted/library/main/EffectQueuePostprocess.java
@@ -32,7 +32,6 @@ class EffectQueuePostprocess extends EffectQueue
   private static final int NUM_UNIFORMS = PostprocessEffect.NUM_UNIFORMS;
   private static final int INDEX = EffectType.POSTPROCESS.ordinal();
 
-
   private int mHalo;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/program/DistortedProgram.java b/src/main/java/org/distorted/library/program/DistortedProgram.java
index e520403..67214b4 100644
--- a/src/main/java/org/distorted/library/program/DistortedProgram.java
+++ b/src/main/java/org/distorted/library/program/DistortedProgram.java
@@ -443,7 +443,6 @@ android.util.Log.d("Program", end.substring(0,40));
  * Create a new Shader Program from two source strings.
  * <p>
  * Needs to be called from a thread holding the OpenGL context.
- * Assumed to hold GLSL 'version 300 es' source.
  *
  * @param vertex   Vertex shader code.
  * @param fragment Fragment shader code.
diff --git a/src/main/res/raw/blit_depth_fragment_shader.glsl b/src/main/res/raw/blit_depth_fragment_shader.glsl
index 5e72e64..889ccef 100644
--- a/src/main/res/raw/blit_depth_fragment_shader.glsl
+++ b/src/main/res/raw/blit_depth_fragment_shader.glsl
@@ -17,18 +17,10 @@
 // along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                       //
 //////////////////////////////////////////////////////////////////////////////////////////////
 
-precision lowp float;
+precision highp float;
 
-#if __VERSION__ != 100
 out vec4 fragColor;           // The output color
 in vec2 v_TexCoordinate;      // Interpolated texture coordinate per fragment.
-#define TEXTURE texture
-#define FRAG_COLOR fragColor
-#else
-varying vec2 v_TexCoordinate; // Interpolated texture coordinate per fragment.
-#define TEXTURE texture2D
-#define FRAG_COLOR gl_FragColor
-#endif
 
 uniform sampler2D u_Texture;
 uniform sampler2D u_DepthTexture;
@@ -37,6 +29,6 @@ uniform sampler2D u_DepthTexture;
 
 void main()                    		
   {
-  gl_FragDepth = TEXTURE(u_DepthTexture,v_TexCoordinate).r;
-  FRAG_COLOR   = TEXTURE(u_Texture     ,v_TexCoordinate);
+  gl_FragDepth = texture(u_DepthTexture,v_TexCoordinate).r;
+  fragColor    = texture(u_Texture     ,v_TexCoordinate);
   }
\ No newline at end of file
diff --git a/src/main/res/raw/blit_depth_vertex_shader.glsl b/src/main/res/raw/blit_depth_vertex_shader.glsl
index a9d3811..4047179 100644
--- a/src/main/res/raw/blit_depth_vertex_shader.glsl
+++ b/src/main/res/raw/blit_depth_vertex_shader.glsl
@@ -17,15 +17,10 @@
 // along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                       //
 //////////////////////////////////////////////////////////////////////////////////////////////
 
-precision lowp float;
+precision highp float;
 
-#if __VERSION__ != 100
 in vec2 a_Position;           // Per-vertex position.
 out vec2 v_TexCoordinate;     //
-#else
-attribute vec2 a_Position;    // Per-vertex position.
-varying vec2 v_TexCoordinate; //
-#endif
 
 uniform float u_Depth;        // distance from the near plane to render plane, in clip coords
 uniform vec2  u_TexCorr;      // when we blit from postprocessing buffers, the buffers can be
diff --git a/src/main/res/raw/blit_fragment_shader.glsl b/src/main/res/raw/blit_fragment_shader.glsl
index 5dec392..fa99194 100644
--- a/src/main/res/raw/blit_fragment_shader.glsl
+++ b/src/main/res/raw/blit_fragment_shader.glsl
@@ -19,22 +19,13 @@
 
 precision lowp float;
 
-#if __VERSION__ != 100
 out vec4 fragColor;           // The output color
 in vec2 v_TexCoordinate;      // Interpolated texture coordinate per fragment.
-#define TEXTURE texture
-#define FRAG_COLOR fragColor
-#else
-varying vec2 v_TexCoordinate; // Interpolated texture coordinate per fragment.
-#define TEXTURE texture2D
-#define FRAG_COLOR gl_FragColor
-#endif
-
 uniform sampler2D u_Texture;  // The input texture.
 
 //////////////////////////////////////////////////////////////////////////////////////////////
 
 void main()                    		
   {
-  FRAG_COLOR = TEXTURE(u_Texture,v_TexCoordinate);
+  fragColor = texture(u_Texture,v_TexCoordinate);
   }
\ No newline at end of file
diff --git a/src/main/res/raw/blit_vertex_shader.glsl b/src/main/res/raw/blit_vertex_shader.glsl
index 7080267..88b64b1 100644
--- a/src/main/res/raw/blit_vertex_shader.glsl
+++ b/src/main/res/raw/blit_vertex_shader.glsl
@@ -19,14 +19,8 @@
 
 precision lowp float;
 
-#if __VERSION__ != 100
 in vec2 a_Position;           // Per-vertex position.
 out vec2 v_TexCoordinate;     //
-#else
-attribute vec2 a_Position;    // Per-vertex position.
-varying vec2 v_TexCoordinate; //
-#endif
-
 uniform float u_Depth;        // distance from the near plane to render plane, in clip coords
 
 //////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/res/raw/normal_fragment_shader.glsl b/src/main/res/raw/normal_fragment_shader.glsl
index b2b55b7..963f0f3 100644
--- a/src/main/res/raw/normal_fragment_shader.glsl
+++ b/src/main/res/raw/normal_fragment_shader.glsl
@@ -19,16 +19,11 @@
 
 precision lowp float;
 
-#if __VERSION__ != 100
 out vec4 fragColor;
-#define FRAG_COLOR fragColor
-#else
-#define FRAG_COLOR gl_FragColor
-#endif
 
 //////////////////////////////////////////////////////////////////////////////////////////////
 
 void main()
   {
-  FRAG_COLOR = vec4(1.0,0.0,0.0,1.0);
+  fragColor = vec4(1.0,0.0,0.0,1.0);
   }
\ No newline at end of file
diff --git a/src/main/res/raw/normal_vertex_shader.glsl b/src/main/res/raw/normal_vertex_shader.glsl
index 5e01c12..83ab55b 100644
--- a/src/main/res/raw/normal_vertex_shader.glsl
+++ b/src/main/res/raw/normal_vertex_shader.glsl
@@ -19,17 +19,12 @@
 
 precision lowp float;
 
-#if __VERSION__ != 100
 in vec3 a_Position;
-#else
-attribute vec3 a_Position;
-#endif
-
 uniform mat4 u_MVPMatrix;
 
 //////////////////////////////////////////////////////////////////////////////////////////////
 
 void main()
   {
-  gl_Position = u_MVPMatrix*vec4( a_Position, 1.0);
+  gl_Position = u_MVPMatrix * vec4(a_Position, 1.0);
   }
\ No newline at end of file
