commit f4d6114ca8a68f90237e3b6a9fb7f8c14df98704
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Sun Jun 24 00:18:36 2018 +0100

    I spoke too soon, the ARM Mali flashing is of course not fixed yet. The previous commit fixed Triblur, but the bug is still reproducible elsewhere (only in the 'postprocessed' apps though).
    
    This commit introduces a circular queue in case of the postprocessing FBOs - with little success though.

diff --git a/src/main/java/org/distorted/library/effect/PostprocessEffect.java b/src/main/java/org/distorted/library/effect/PostprocessEffect.java
index 28ebd7c..c98f664 100644
--- a/src/main/java/org/distorted/library/effect/PostprocessEffect.java
+++ b/src/main/java/org/distorted/library/effect/PostprocessEffect.java
@@ -139,7 +139,7 @@ public abstract class PostprocessEffect extends Effect implements DistortedMaste
  *
  * @y.exclude
  */
-  public abstract int apply(float[] uniforms, int index, DistortedOutputSurface[] buffers);
+  public abstract int apply(float[] uniforms, int index, DistortedOutputSurface[] buffers, int fbo);
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
diff --git a/src/main/java/org/distorted/library/effect/PostprocessEffectBlur.java b/src/main/java/org/distorted/library/effect/PostprocessEffectBlur.java
index 08cec17..1b8b870 100644
--- a/src/main/java/org/distorted/library/effect/PostprocessEffectBlur.java
+++ b/src/main/java/org/distorted/library/effect/PostprocessEffectBlur.java
@@ -140,7 +140,7 @@ public class PostprocessEffectBlur extends PostprocessEffect
  *
  * @y.exclude
  */
-  public int apply(float[] uniforms, int index, DistortedOutputSurface[] buffers)
+  public int apply(float[] uniforms, int index, DistortedOutputSurface[] buffers, int fbo)
     {
     if( mProgram1 ==null)
       {
@@ -159,7 +159,7 @@ public class PostprocessEffectBlur extends PostprocessEffect
 
     DistortedFramebuffer buffer = (DistortedFramebuffer)buffers[mQualityLevel];
 
-    buffer.setAsOutput();
+    buffer.setAsOutputFBO(fbo);
 
     float w= buffer.getWidth();
     float h= buffer.getHeight();
@@ -182,8 +182,8 @@ public class PostprocessEffectBlur extends PostprocessEffect
     for(int i=0; i<=radius; i++) mOffsets[i] = offsetsCache[offset+i]*offsetCorrW;
 
     mProgram1.useProgram();
-    buffer.bindForOutput(1);
-    buffer.setAsInput(0);
+    buffer.bindForOutput(2*fbo+1);
+    buffer.setAsInput(2*fbo);
 
     GLES31.glColorMask(true,true,true,true);
     GLES31.glClearColor(1.0f,1.0f,1.0f,0.0f);
@@ -203,8 +203,8 @@ public class PostprocessEffectBlur extends PostprocessEffect
     for(int i=0; i<=radius; i++) mOffsets[i] = offsetsCache[offset+i]*offsetCorrH;
 
     mProgram2.useProgram();
-    buffer.bindForOutput(0);
-    buffer.setAsInput(1);
+    buffer.bindForOutput(2*fbo);
+    buffer.setAsInput(2*fbo+1);
 
     GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT);
 
diff --git a/src/main/java/org/distorted/library/effect/PostprocessEffectGlow.java b/src/main/java/org/distorted/library/effect/PostprocessEffectGlow.java
index e0cd858..e2235e3 100644
--- a/src/main/java/org/distorted/library/effect/PostprocessEffectGlow.java
+++ b/src/main/java/org/distorted/library/effect/PostprocessEffectGlow.java
@@ -144,7 +144,7 @@ public class PostprocessEffectGlow extends PostprocessEffect
  *
  * @y.exclude
  */
-  public int apply(float[] uniforms, int index, DistortedOutputSurface[] buffers)
+  public int apply(float[] uniforms, int index, DistortedOutputSurface[] buffers, int fbo)
     {
     if( mProgram1 ==null)
       {
@@ -180,15 +180,15 @@ public class PostprocessEffectGlow extends PostprocessEffect
     int offset = radius + radius*radius/4;
     radius = (radius+1)/2;
 
-    outBuffer.setAsOutput();
+    outBuffer.setAsOutputFBO(fbo);
     GLES31.glViewport(0, 0, (int)w, (int)h);
 
     // horizontal glow
     for(int i=0; i<=radius; i++) mOffsets[i] = offsetsCache[offset+i]*offsetCorrW;
 
     mProgram1.useProgram();
-    outBuffer.bindForOutput(1);
-    inBuffer.setAsInput(0);
+    outBuffer.bindForOutput(2*fbo+1);
+    inBuffer.setAsInput(2*fbo);
 
     GLES31.glColorMask(true,true,true,true);
     GLES31.glClearColor(1.0f,1.0f,1.0f,0.0f);
@@ -209,8 +209,8 @@ public class PostprocessEffectGlow extends PostprocessEffect
     for(int i=0; i<=radius; i++) mOffsets[i] = offsetsCache[offset+i]*offsetCorrH;
 
     mProgram2.useProgram();
-    outBuffer.bindForOutput(0);
-    outBuffer.setAsInput(1);
+    outBuffer.bindForOutput(2*fbo);
+    outBuffer.setAsInput(2*fbo+1);
 
     GLES31.glUniform1f ( mProgram2.mUniform[0] , n );
     GLES31.glUniform2f ( mProgram2.mUniform[1] , corrW, corrH );
diff --git a/src/main/java/org/distorted/library/main/Distorted.java b/src/main/java/org/distorted/library/main/Distorted.java
index 86f75e4..89442da 100644
--- a/src/main/java/org/distorted/library/main/Distorted.java
+++ b/src/main/java/org/distorted/library/main/Distorted.java
@@ -77,6 +77,13 @@ public class Distorted
    */
   public static final int CLONE_CHILDREN= 0x20;
 
+  /**
+   * Work around bugs in ARM Mali driver by, instead to a single FBO, rendering to a circular queue
+   * of FBO_QUEUE_SIZE FBOs. (otherwise we sometimes get a 'full pipeline flush' and the end result
+   * might be missing part of the Objects)
+   */
+  public static final int FBO_QUEUE_SIZE = 4;
+
   private static boolean mInitialized=false;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/main/DistortedFramebuffer.java b/src/main/java/org/distorted/library/main/DistortedFramebuffer.java
index ec6be14..d29e25a 100644
--- a/src/main/java/org/distorted/library/main/DistortedFramebuffer.java
+++ b/src/main/java/org/distorted/library/main/DistortedFramebuffer.java
@@ -276,7 +276,7 @@ public class DistortedFramebuffer extends DistortedOutputSurface implements Dist
  */
   public boolean setAsInput(int texture)
     {
-    if( texture>=0 && texture<mNumColors && mColorH[texture]>0 )
+    if( texture>=0 && texture<mNumFBOs*mNumColors && mColorH[texture]>0 )
       {
       GLES31.glActiveTexture(GLES31.GL_TEXTURE0);
       GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, mColorH[texture]);
@@ -290,7 +290,7 @@ public class DistortedFramebuffer extends DistortedOutputSurface implements Dist
 
   public void bindForOutput(int texture)
     {
-    if( texture>=0 && texture<mNumColors && mColorH[texture]>0 )
+    if( texture>=0 && texture<mNumFBOs*mNumColors && mColorH[texture]>0 )
       {
       GLES31.glFramebufferTexture2D(GLES31.GL_FRAMEBUFFER, GLES31.GL_COLOR_ATTACHMENT0, GLES31.GL_TEXTURE_2D, mColorH[texture], 0);
       }
diff --git a/src/main/java/org/distorted/library/main/DistortedNode.java b/src/main/java/org/distorted/library/main/DistortedNode.java
index 39e9fc8..b1c80ea 100644
--- a/src/main/java/org/distorted/library/main/DistortedNode.java
+++ b/src/main/java/org/distorted/library/main/DistortedNode.java
@@ -264,7 +264,6 @@ public class DistortedNode implements DistortedMaster.Slave
 
     if( input.setAsInput() )
       {
-      surface.setAsOutput();
       DistortedRenderState.setUpStencilMark();
       mEffects.drawPriv(mSurface.getWidth() /2.0f, mSurface.getHeight()/2.0f, mMesh, surface, currTime, queue.getHalo()*surface.mMipmap);
       DistortedRenderState.unsetUpStencilMark();
diff --git a/src/main/java/org/distorted/library/main/DistortedOutputSurface.java b/src/main/java/org/distorted/library/main/DistortedOutputSurface.java
index c2c8a63..9ab09de 100644
--- a/src/main/java/org/distorted/library/main/DistortedOutputSurface.java
+++ b/src/main/java/org/distorted/library/main/DistortedOutputSurface.java
@@ -180,7 +180,7 @@ public abstract class DistortedOutputSurface extends DistortedSurface implements
 
     for(int j=0; j<EffectQuality.LENGTH; j++)
       {
-      mBuffer[j] = new DistortedFramebuffer(2,BOTH_DEPTH_STENCIL,TYPE_SYST, (int)(width*mipmap), (int)(height*mipmap) );
+      mBuffer[j] = new DistortedFramebuffer(Distorted.FBO_QUEUE_SIZE,2,BOTH_DEPTH_STENCIL,TYPE_SYST, (int)(width*mipmap), (int)(height*mipmap) );
       mBuffer[j].mMipmap = mipmap;
       mBuffer[j].mNear   = near;  // copy mNear as well (for blitting- see PostprocessEffect.apply() )
       mBuffer[j].glClearColor(1.0f,1.0f,1.0f,0.0f);
@@ -199,12 +199,17 @@ public abstract class DistortedOutputSurface extends DistortedSurface implements
 
     for(int j=0; j<EffectQuality.LENGTH; j++)
       {
-      mBuffer[j].setAsOutput();
-      GLES31.glFramebufferTexture2D(GLES31.GL_FRAMEBUFFER, GLES31.GL_COLOR_ATTACHMENT0, GLES31.GL_TEXTURE_2D, mBuffer[j].mColorH[1], 0);
-      GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT|GLES31.GL_DEPTH_BUFFER_BIT|GLES31.GL_STENCIL_BUFFER_BIT);
-      GLES31.glFramebufferTexture2D(GLES31.GL_FRAMEBUFFER, GLES31.GL_COLOR_ATTACHMENT0, GLES31.GL_TEXTURE_2D, mBuffer[j].mColorH[0], 0);
-      GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT);
+      for(int k=0; k<Distorted.FBO_QUEUE_SIZE; k++)
+        {
+        GLES31.glBindFramebuffer(GLES31.GL_FRAMEBUFFER, mBuffer[j].mFBOH[k]);
+        GLES31.glFramebufferTexture2D(GLES31.GL_FRAMEBUFFER, GLES31.GL_COLOR_ATTACHMENT0, GLES31.GL_TEXTURE_2D, mBuffer[j].mColorH[2*k+1], 0);
+        GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT | GLES31.GL_DEPTH_BUFFER_BIT | GLES31.GL_STENCIL_BUFFER_BIT);
+        GLES31.glFramebufferTexture2D(GLES31.GL_FRAMEBUFFER, GLES31.GL_COLOR_ATTACHMENT0, GLES31.GL_TEXTURE_2D, mBuffer[j].mColorH[2*k  ], 0);
+        GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT);
+        }
       }
+
+    GLES31.glBindFramebuffer(GLES31.GL_FRAMEBUFFER, 0);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -267,11 +272,11 @@ public abstract class DistortedOutputSurface extends DistortedSurface implements
   private int blitWithDepth(long currTime, DistortedOutputSurface buffer,int fbo)
     {
     GLES31.glViewport(0, 0, mWidth, mHeight);
-    setAsOutput(currTime,fbo);
+    setAsOutputFBO(currTime,fbo);
     GLES31.glActiveTexture(GLES31.GL_TEXTURE0);
-    GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, buffer.mColorH[0]);
+    GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, buffer.mColorH[2*fbo]);
     GLES31.glActiveTexture(GLES31.GL_TEXTURE1);
-    GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, buffer.mDepthStencilH[0]);
+    GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, buffer.mDepthStencilH[fbo]);
 
     GLES31.glDisable(GLES31.GL_STENCIL_TEST);
     GLES31.glStencilMask(0x00);
@@ -290,10 +295,10 @@ public abstract class DistortedOutputSurface extends DistortedSurface implements
     GLES31.glClearDepthf(1.0f);
     GLES31.glClearStencil(0);
 
-    buffer.setAsOutput();
-    GLES31.glFramebufferTexture2D(GLES31.GL_FRAMEBUFFER, GLES31.GL_COLOR_ATTACHMENT0, GLES31.GL_TEXTURE_2D, buffer.mColorH[1], 0);
+    buffer.setAsOutputFBO(fbo);
+    GLES31.glFramebufferTexture2D(GLES31.GL_FRAMEBUFFER, GLES31.GL_COLOR_ATTACHMENT0, GLES31.GL_TEXTURE_2D, buffer.mColorH[2*fbo+1], 0);
     GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT|GLES31.GL_DEPTH_BUFFER_BIT|GLES31.GL_STENCIL_BUFFER_BIT);
-    GLES31.glFramebufferTexture2D(GLES31.GL_FRAMEBUFFER, GLES31.GL_COLOR_ATTACHMENT0, GLES31.GL_TEXTURE_2D, buffer.mColorH[0], 0);
+    GLES31.glFramebufferTexture2D(GLES31.GL_FRAMEBUFFER, GLES31.GL_COLOR_ATTACHMENT0, GLES31.GL_TEXTURE_2D, buffer.mColorH[2*fbo  ], 0);
     GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT);
 
     return 1;
@@ -331,7 +336,7 @@ public abstract class DistortedOutputSurface extends DistortedSurface implements
 
       if( currBucket==0 )
         {
-        setAsOutput(time,fbo);
+        setAsOutputFBO(time,fbo);
         numRenders += child1.draw(time,this);
         }
       else
@@ -349,13 +354,14 @@ public abstract class DistortedOutputSurface extends DistortedSurface implements
             for(int j=bucketChange; j<i; j++)
               {
               child2 = children.get(j);
+              mBuffer[internalQuality].setAsOutputFBO(fbo);
               numRenders += child2.markStencilAndDepth(time,mBuffer[internalQuality],lastQueue);
               }
 
-            numRenders += lastQueue.postprocess(mBuffer);
+            numRenders += lastQueue.postprocess(mBuffer,fbo);
             numRenders += blitWithDepth(time, mBuffer[quality],fbo);
 
-            mBuffer[quality].setAsOutputAndClear(time);
+            mBuffer[quality].setAsOutputAndClear(time,fbo);
             }
 
           internalQuality = currQueue.getInternalQuality();
@@ -363,7 +369,7 @@ public abstract class DistortedOutputSurface extends DistortedSurface implements
           bucketChange    = i;
           }
 
-        mBuffer[quality].setAsOutput(time);
+        mBuffer[quality].setAsOutputFBO(time,fbo);
         child1.drawNoBlend(time,mBuffer[quality]);
 
         if( i==numChildren-1 )
@@ -371,10 +377,11 @@ public abstract class DistortedOutputSurface extends DistortedSurface implements
           for(int j=bucketChange; j<numChildren; j++)
             {
             child2 = children.get(j);
+            mBuffer[internalQuality].setAsOutputFBO(fbo);
             numRenders += child2.markStencilAndDepth(time,mBuffer[internalQuality],currQueue);
             }
 
-          numRenders += currQueue.postprocess(mBuffer);
+          numRenders += currQueue.postprocess(mBuffer,fbo);
           numRenders += blitWithDepth(time, mBuffer[quality],fbo);
           }
         } // end postprocessed child case
@@ -422,12 +429,11 @@ public abstract class DistortedOutputSurface extends DistortedSurface implements
  * Useful for drawing to the postprocessing buffer, which must sometimes be cleared multiple times
  * per frame.
  */
-
-  private void setAsOutputAndClear(long time)
+  private void setAsOutputAndClear(long time,int fbo)
     {
-    GLES31.glBindFramebuffer(GLES31.GL_FRAMEBUFFER, mFBOH[0]);
+    GLES31.glBindFramebuffer(GLES31.GL_FRAMEBUFFER, mFBOH[fbo]);
 
-    mTime[0] = time;    // have to do this otherwise on the next setAsOutput() we would clear
+    mTime[fbo] = time;    // have to do this otherwise on the next setAsOutput() we would clear
     DistortedRenderState.colorDepthStencilOn();
     GLES31.glClearColor(mClearR, mClearG, mClearB, mClearA);
     GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT);
@@ -436,7 +442,7 @@ public abstract class DistortedOutputSurface extends DistortedSurface implements
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void setAsOutput(long time, int fbo)
+  private void setAsOutputFBO(long time, int fbo)
     {
     if( fbo>=0 && fbo<mNumFBOs )
       {
@@ -450,7 +456,25 @@ public abstract class DistortedOutputSurface extends DistortedSurface implements
       }
     else
       {
-      android.util.Log.e("surface", "error in setAsOutput, fbo="+fbo);
+      android.util.Log.e("surface", "error in setAsOutput1, fbo="+fbo);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Not part of the Public API.
+ *
+ * @y.exclude
+ */
+  public void setAsOutputFBO(int fbo)
+    {
+    if( fbo>=0 && fbo<mNumFBOs )
+      {
+      GLES31.glBindFramebuffer(GLES31.GL_FRAMEBUFFER, mFBOH[fbo]);
+      }
+    else
+      {
+      android.util.Log.e("surface", "error in setAsOutput2, fbo="+fbo);
       }
     }
 
diff --git a/src/main/java/org/distorted/library/main/DistortedScreen.java b/src/main/java/org/distorted/library/main/DistortedScreen.java
index f302dc5..08d209d 100644
--- a/src/main/java/org/distorted/library/main/DistortedScreen.java
+++ b/src/main/java/org/distorted/library/main/DistortedScreen.java
@@ -56,7 +56,6 @@ public class DistortedScreen extends DistortedFramebuffer
   ///// END DEBUGGING //////////////////////////
 
   private int mCurrFBO;
-  private static final int NUM_FBO = 3;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
@@ -68,7 +67,7 @@ public class DistortedScreen extends DistortedFramebuffer
  */
   public DistortedScreen()
     {
-    super(NUM_FBO,1,BOTH_DEPTH_STENCIL, TYPE_SYST, 1,1);
+    super(Distorted.FBO_QUEUE_SIZE,1,BOTH_DEPTH_STENCIL, TYPE_SYST, 1,1);
     mShowFPS = false;
     mCurrFBO = 0;
     }
@@ -122,7 +121,7 @@ public class DistortedScreen extends DistortedFramebuffer
       }
 
     mCurrFBO++;
-    if( mCurrFBO>=NUM_FBO ) mCurrFBO=0;
+    if( mCurrFBO>=Distorted.FBO_QUEUE_SIZE ) mCurrFBO=0;
 
     return numrender+1;
     }
diff --git a/src/main/java/org/distorted/library/main/EffectQueuePostprocess.java b/src/main/java/org/distorted/library/main/EffectQueuePostprocess.java
index d3bb295..89d6934 100644
--- a/src/main/java/org/distorted/library/main/EffectQueuePostprocess.java
+++ b/src/main/java/org/distorted/library/main/EffectQueuePostprocess.java
@@ -100,7 +100,7 @@ class EffectQueuePostprocess extends EffectQueue
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  int postprocess(DistortedOutputSurface[] buffers)
+  int postprocess(DistortedOutputSurface[] buffers, int fbo)
     {
     int numRenders = 0;
 
@@ -108,7 +108,7 @@ class EffectQueuePostprocess extends EffectQueue
 
     for(int i=0; i<mNumEffects; i++)
       {
-      numRenders += ((PostprocessEffect)mEffects[i]).apply(mUniforms,NUM_UNIFORMS*i, buffers);
+      numRenders += ((PostprocessEffect)mEffects[i]).apply(mUniforms,NUM_UNIFORMS*i, buffers, fbo);
       }
 
     GLES31.glEnable(GLES31.GL_BLEND);
