commit c43abe6c0fa852ab0dcf3ad7a441ac189a476a54
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Tue Apr 30 10:42:30 2019 +0100

    Fixes for memory leak problems uncovered by the 'Rubik' app. (mainly: new method DistortedNode.markForDeletion)

diff --git a/src/main/java/org/distorted/library/main/Distorted.java b/src/main/java/org/distorted/library/main/Distorted.java
index 9192506..1be70e8 100644
--- a/src/main/java/org/distorted/library/main/Distorted.java
+++ b/src/main/java/org/distorted/library/main/Distorted.java
@@ -26,9 +26,7 @@ import android.content.res.Resources;
 import android.opengl.GLES31;
 
 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.message.EffectMessageSender;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -108,7 +106,7 @@ public class Distorted
 // ARM Mali driver r12 has problems when we keep swapping many FBOs (fixed in r22)
 // PowerVR GE8100 compiler fails to compile OIT programs.
 
-  static void detectBuggyDrivers()
+  private static void detectBuggyDrivers()
     {
     String vendor  = GLES31.glGetString(GLES31.GL_VENDOR);
     String version = GLES31.glGetString(GLES31.GL_VERSION);
@@ -149,7 +147,7 @@ public class Distorted
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
- * When OpenGL context gets created, you need to call this method so that the library can initialise its internal data structures.
+ * When OpenGL context gets created, call this method so that the library can initialise its internal data structures.
  * I.e. best called from GLSurfaceView.onCreate().
  * <p>
  * Needs to be called from a thread holding the OpenGL context.
diff --git a/src/main/java/org/distorted/library/main/DistortedEffects.java b/src/main/java/org/distorted/library/main/DistortedEffects.java
index f66454c..de2efc6 100644
--- a/src/main/java/org/distorted/library/main/DistortedEffects.java
+++ b/src/main/java/org/distorted/library/main/DistortedEffects.java
@@ -415,12 +415,16 @@ public class DistortedEffects
 
   void newNode(DistortedNode node)
     {
-    mM.newNode(node);
-    mF.newNode(node);
-    mV.newNode(node);
     mP.newNode(node);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void removeNode(DistortedNode node)
+    {
+    mP.removeNode(node);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private void displayNormals(MeshBase mesh)
diff --git a/src/main/java/org/distorted/library/main/DistortedNode.java b/src/main/java/org/distorted/library/main/DistortedNode.java
index 9987941..14d1abc 100644
--- a/src/main/java/org/distorted/library/main/DistortedNode.java
+++ b/src/main/java/org/distorted/library/main/DistortedNode.java
@@ -101,6 +101,24 @@ public class DistortedNode implements DistortedMaster.Slave
     mMapNodeID.clear();
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public void markForDeletion()
+    {
+    if( --mData.numPointingNodes==0 )
+      {
+      mMapNodeID.remove(mData.key);
+
+      if( mData.mFBO!=null )
+        {
+        mData.mFBO.markForDeletion();
+        mData.mFBO = null;
+        }
+      }
+
+    mEffects.removeNode(this);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   private ArrayList<Long> generateIDList()
@@ -656,53 +674,55 @@ public class DistortedNode implements DistortedMaster.Slave
   public void doWork()
     {
     int num = mJobs.size();
-    Job job;
-
-    int numChanges=0;
 
-    for(int i=0; i<num; i++)
+    if( num>0 )
       {
-      job = mJobs.remove(0);
+      Job job;
+      int numChanges=0;
 
-      switch(job.type)
+      for(int i=0; i<num; i++)
         {
-        case ATTACH: numChanges++;
-                     if( mChildren==null ) mChildren = new ArrayList<>(2);
-                     job.node.mParent = this;
-                     job.node.mSurfaceParent = null;
-                     DistortedMaster.addSortingByBuckets(mChildren,job.node);
-                     mNumChildren[0]++;
-                     break;
-        case DETACH: numChanges++;
-                     if( mNumChildren[0]>0 && mChildren.remove(job.node) )
-                       {
-                       job.node.mParent = null;
+        job = mJobs.remove(0);
+
+        switch(job.type)
+          {
+          case ATTACH: numChanges++;
+                       if( mChildren==null ) mChildren = new ArrayList<>(2);
+                       job.node.mParent = this;
                        job.node.mSurfaceParent = null;
-                       mNumChildren[0]--;
-                       }
-                     break;
-        case DETALL: numChanges++;
-                     if( mNumChildren[0]>0 )
-                       {
-                       DistortedNode tmp;
-
-                       for(int j=mNumChildren[0]-1; j>=0; j--)
+                       DistortedMaster.addSortingByBuckets(mChildren,job.node);
+                       mNumChildren[0]++;
+                       break;
+          case DETACH: numChanges++;
+                       if( mNumChildren[0]>0 && mChildren.remove(job.node) )
                          {
-                         tmp = mChildren.remove(j);
-                         tmp.mParent = null;
-                         tmp.mSurfaceParent = null;
+                         job.node.mParent = null;
+                         job.node.mSurfaceParent = null;
+                         mNumChildren[0]--;
                          }
+                       break;
+          case DETALL: numChanges++;
+                       if( mNumChildren[0]>0 )
+                         {
+                         DistortedNode tmp;
+
+                         for(int j=mNumChildren[0]-1; j>=0; j--)
+                           {
+                           tmp = mChildren.remove(j);
+                           tmp.mParent = null;
+                           tmp.mSurfaceParent = null;
+                           }
 
-                       mNumChildren[0] = 0;
-                       }
-                     break;
-        case SORT  : mChildren.remove(job.node);
-                     DistortedMaster.addSortingByBuckets(mChildren,job.node);
-                     break;
+                         mNumChildren[0] = 0;
+                         }
+                       break;
+          case SORT  : mChildren.remove(job.node);
+                       DistortedMaster.addSortingByBuckets(mChildren,job.node);
+                       break;
+          }
         }
+      if( numChanges>0 ) adjustIsomorphism();
       }
-
-    if( numChanges>0 ) adjustIsomorphism();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/main/DistortedObject.java b/src/main/java/org/distorted/library/main/DistortedObject.java
index 57ceaba..5fe7dff 100644
--- a/src/main/java/org/distorted/library/main/DistortedObject.java
+++ b/src/main/java/org/distorted/library/main/DistortedObject.java
@@ -171,12 +171,8 @@ abstract class DistortedObject
     {
     android.util.Log.e("Object", "Done list:");
 
-    DistortedObject object;
-    int num = mDoneList.size();
-
-    for(int i=0; i<num; i++)
+    for(DistortedObject object : mDoneList)
       {
-      object = mDoneList.get(i);
       object.print("");
       }
 
diff --git a/src/main/java/org/distorted/library/main/DistortedOutputSurface.java b/src/main/java/org/distorted/library/main/DistortedOutputSurface.java
index e614fba..6164313 100644
--- a/src/main/java/org/distorted/library/main/DistortedOutputSurface.java
+++ b/src/main/java/org/distorted/library/main/DistortedOutputSurface.java
@@ -1041,41 +1041,45 @@ public void setOrderIndependentTransparency(boolean oit, float initialSize)
   public void doWork()
     {
     int num = mJobs.size();
-    Job job;
 
-    for(int i=0; i<num; i++)
+    if( num>0 )
       {
-      job = mJobs.remove(0);
+      Job job;
 
-      switch(job.type)
+      for(int i=0; i<num; i++)
         {
-        case ATTACH: if( mChildren==null ) mChildren = new ArrayList<>(2);
-                     job.node.setSurfaceParent(this);
-                     DistortedMaster.addSortingByBuckets(mChildren,job.node);
-                     mNumChildren++;
-                     break;
-        case DETACH: if( mNumChildren>0 && mChildren.remove(job.node) )
-                       {
-                       job.node.setSurfaceParent(null);
-                       mNumChildren--;
-                       }
-                     break;
-        case DETALL: if( mNumChildren>0 )
-                       {
-                       DistortedNode tmp;
-
-                       for(int j=mNumChildren-1; j>=0; j--)
+        job = mJobs.remove(0);
+
+        switch(job.type)
+          {
+          case ATTACH: if( mChildren==null ) mChildren = new ArrayList<>(2);
+                       job.node.setSurfaceParent(this);
+                       DistortedMaster.addSortingByBuckets(mChildren,job.node);
+                       mNumChildren++;
+                       break;
+          case DETACH: if( mNumChildren>0 && mChildren.remove(job.node) )
                          {
-                         tmp = mChildren.remove(j);
-                         tmp.setSurfaceParent(null);
+                         job.node.setSurfaceParent(null);
+                         mNumChildren--;
                          }
+                       break;
+          case DETALL: if( mNumChildren>0 )
+                         {
+                         DistortedNode tmp;
+
+                         for(int j=mNumChildren-1; j>=0; j--)
+                           {
+                           tmp = mChildren.remove(j);
+                           tmp.setSurfaceParent(null);
+                           }
 
-                       mNumChildren = 0;
-                       }
-                     break;
-        case SORT  : mChildren.remove(job.node);
-                     DistortedMaster.addSortingByBuckets(mChildren,job.node);
-                     break;
+                         mNumChildren = 0;
+                         }
+                       break;
+          case SORT  : mChildren.remove(job.node);
+                       DistortedMaster.addSortingByBuckets(mChildren,job.node);
+                       break;
+          }
         }
       }
     }
diff --git a/src/main/java/org/distorted/library/main/EffectQueue.java b/src/main/java/org/distorted/library/main/EffectQueue.java
index 768ebd9..93b32bd 100644
--- a/src/main/java/org/distorted/library/main/EffectQueue.java
+++ b/src/main/java/org/distorted/library/main/EffectQueue.java
@@ -148,6 +148,13 @@ abstract class EffectQueue implements DistortedMaster.Slave
     mNodes.add(node);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  void removeNode(DistortedNode node)
+    {
+    mNodes.remove(node);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   @SuppressWarnings("unused")
