commit f28fffc25b5c51fb28eb34762d20b3a9fe546c7d
Author: Leszek Koltunski <leszek@distoretedandroid.org>
Date:   Thu Feb 16 16:30:30 2017 +0000

    A Lot of fixes for the issues uncovered by Olimpic.
    
    Still at least 1 known issue: sometimes, when we re-add a Surface, some garbage pops up on the screen for a brief split second. Visible in Olimpic.

diff --git a/src/main/java/org/distorted/library/DistortedNode.java b/src/main/java/org/distorted/library/DistortedNode.java
index 32f4a67..00903fe 100644
--- a/src/main/java/org/distorted/library/DistortedNode.java
+++ b/src/main/java/org/distorted/library/DistortedNode.java
@@ -40,6 +40,7 @@ public class DistortedNode implements DistortedAttacheable
   private static HashMap<ArrayList<Long>,NodeData> mMapNodeID = new HashMap<>();
   private static long mNextNodeID =0;
 
+  private DistortedNode mParent;
   private MeshObject mMesh;
   private DistortedEffects mEffects;
   private DistortedInputSurface mSurface;
@@ -109,7 +110,8 @@ public class DistortedNode implements DistortedAttacheable
 
     for(i=0; i<depth; i++) tmp +="   ";
     tmp += ("NodeID="+mData.ID+" nodes pointing: "+mData.numPointingNodes+" surfaceID="+
-            mSurface.getID()+" FBO="+(mData.mFBO==null ? "null":mData.mFBO.getID()));
+            mSurface.getID()+" FBO="+(mData.mFBO==null ? "null":mData.mFBO.getID()))+
+            " parent sID="+(mParent==null ? "null": (mParent.mSurface.getID()));
 
     android.util.Log.e("NODE", tmp);
 
@@ -133,81 +135,62 @@ public class DistortedNode implements DistortedAttacheable
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// tree isomorphism algorithm
 
-  void treeIsomorphism()
+  private void adjustIsomorphism()
     {
-    for(int i=0; i<mNumChildren[0]; i++)
-      {
-      mChildren.get(i).treeIsomorphism();
-      }
-
     ArrayList<Long> newList = generateIDList();
     NodeData newData = mMapNodeID.get(newList);
 
-    if( newData==null )
+    if( newData!=null )
+      {
+      newData.numPointingNodes++;
+      }
+    else
       {
-      android.util.Log.d("NODE", "list "+newList+" not found!! node surfaceID="+mSurface.getID());
-
       newData = new NodeData(++mNextNodeID,newList);
       mMapNodeID.put(newList,newData);
       }
-    else if( newData.ID != mData.ID )
-      {
-      android.util.Log.d("NODE", "list "+newList+" found!! node surfaceID="+mSurface.getID());
 
-      newData.numPointingNodes++;
-      }
+    boolean deleteOldFBO = false;
+    boolean createNewFBO = false;
 
-    if( newData.ID != mData.ID )
+    if( --mData.numPointingNodes==0 )
       {
-      boolean fboUsed = false;
-
-      if( mNumChildren[0]>0 && newData.mFBO==null )
-        {
-        if( mData.mFBO!=null )
-          {
-          if( mData.numPointingNodes>1 )
-            {
-            android.util.Log.d("NODE", "creating1 new FBO of node surfaceID="+mSurface.getID());
-            newData.mFBO = new DistortedFramebuffer(true,DistortedSurface.TYPE_TREE,mSurface.getWidth(),mSurface.getHeight());
-            }
-          else
-            {
-            android.util.Log.d("NODE", "copying over FBO of node surfaceID="+mSurface.getID());
-            newData.mFBO = mData.mFBO;
-            fboUsed = true;
-            }
-          }
-        else
-          {
-          android.util.Log.d("NODE", "creating2 new FBO of node surfaceID="+mSurface.getID());
-          newData.mFBO = new DistortedFramebuffer(true,DistortedSurface.TYPE_TREE,mSurface.getWidth(),mSurface.getHeight());
-          }
-        }
-      if( mNumChildren[0]==0 && newData.mFBO!=null )
-        {
-        android.util.Log.d("NODE", "deleting FBO of newData node!!");
-        newData.mFBO.markForDeletion();
-        newData.mFBO = null;
-        }
-
-      if( --mData.numPointingNodes==0 )
-        {
-        android.util.Log.d("NODE", "deleting1 map key "+mData.key);
-
-        mMapNodeID.remove(mData.key);
+      mMapNodeID.remove(mData.key);
+      if( mData.mFBO!=null ) deleteOldFBO=true;
+      }
+    if( mNumChildren[0]>0 && newData.mFBO==null )
+      {
+      createNewFBO = true;
+      }
+    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() );
+      newData.mFBO = null;
+      }
 
-        if( !fboUsed && mData.mFBO!=null )
-          {
-          android.util.Log.d("NODE", "deleting FBO of node surfaceID="+mSurface.getID());
+    if( deleteOldFBO && createNewFBO )
+      {
+      newData.mFBO = mData.mFBO;  // just copy over
+      android.util.Log.d("NODE", "copying over FBOs "+mData.mFBO.getID() );
+      }
+    else if( deleteOldFBO )
+      {
+      mData.mFBO.markForDeletion();
+      android.util.Log.d("NODE", "deleting old FBO "+mData.mFBO.getID() );
+      mData.mFBO = null;
+      }
+    else if( createNewFBO )
+      {
+      newData.mFBO = new DistortedFramebuffer(true, DistortedSurface.TYPE_TREE, mSurface.getWidth(),mSurface.getHeight());
+      android.util.Log.d("NODE", "creating new FBO "+newData.mFBO.getID() );
+      }
 
-          mData.mFBO.markForDeletion();
-          mData.mFBO = null;
-          }
-        }
+    mData = newData;
 
-      mData = newData;
-      }
+    if( mParent!=null ) mParent.adjustIsomorphism();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -268,7 +251,8 @@ public class DistortedNode implements DistortedAttacheable
     mChildren      = null;
     mNumChildren   = new int[1];
     mNumChildren[0]= 0;
-   
+    mParent        = null;
+
     ArrayList<Long> list = new ArrayList<>();
     list.add(mSurface.getID());
     list.add(-mEffects.getID());
@@ -299,7 +283,8 @@ public class DistortedNode implements DistortedAttacheable
   public DistortedNode(DistortedNode node, int flags)
     {
     mEffects= new DistortedEffects(node.mEffects,flags);
-    mMesh = node.mMesh;
+    mMesh   = node.mMesh;
+    mParent = null;
 
     if( (flags & Distorted.CLONE_SURFACE) != 0 )
       {
@@ -397,8 +382,10 @@ public class DistortedNode implements DistortedAttacheable
     {
     if( mChildren==null ) mChildren = new ArrayList<>(2);
 
+    node.mParent = this;
     mChildren.add(node);
     mNumChildren[0]++;
+    adjustIsomorphism();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -456,19 +443,9 @@ public class DistortedNode implements DistortedAttacheable
     {
     if( mNumChildren[0]>0 && mChildren.remove(node) )
       {
+      node.mParent = null;
       mNumChildren[0]--;
-
-      if( mNumChildren[0]==0 && mData.mFBO!=null )
-        {
-        if( --mData.numPointingNodes==0 )
-          {
-          mData.mFBO.markForDeletion();
-          android.util.Log.d("NODE", "deleting2 map key "+mData.key);
-
-          mMapNodeID.remove(mData.key);
-          }
-        mData.mFBO = null;
-        }
+      adjustIsomorphism();
       }
     }
 
@@ -494,20 +471,16 @@ public class DistortedNode implements DistortedAttacheable
     {
     if( mNumChildren[0]>0 )
       {
-      mNumChildren[0] = 0;
-      mChildren.clear();
+      DistortedNode tmp;
 
-      if( mData.mFBO!=null )
+      for(int i=mNumChildren[0]-1; i>=0; i--)
         {
-        if( --mData.numPointingNodes==0 )
-          {
-          mData.mFBO.markForDeletion();
-          android.util.Log.d("NODE", "deleting3 map key "+mData.key);
-
-          mMapNodeID.remove(mData.key);
-          }
-        mData.mFBO = null;
+        tmp = mChildren.remove(i);
+        tmp.mParent = null;
         }
+
+      mNumChildren[0] = 0;
+      adjustIsomorphism();
       }
     }
 
diff --git a/src/main/java/org/distorted/library/DistortedOutputSurface.java b/src/main/java/org/distorted/library/DistortedOutputSurface.java
index 16a659a..1600845 100644
--- a/src/main/java/org/distorted/library/DistortedOutputSurface.java
+++ b/src/main/java/org/distorted/library/DistortedOutputSurface.java
@@ -116,12 +116,11 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
     // change tree topology (attach and detach children)
     boolean changed = DistortedAttachDaemon.toDo();
 
-    // if some changes have been made, we need to rebuilt our tree-isomorphism data structures.
+    // debugging only
     if( changed )
       {
       for(int i=0; i<mNumChildren; i++)
         {
-        mChildren.get(i).treeIsomorphism();
         mChildren.get(i).debug(0);
         }
 
@@ -135,6 +134,7 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
     // to be created immediately, before the calls to drawRecursive()
     toDo();
 
+    // debugging only
     if( changed )
       {
       DistortedSurface.debugLists();
@@ -209,7 +209,7 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
 
       if( mColorCreated==CREATED )
         {
-        moveToToDo();
+        markForCreation();
         recreate();
         }
       }
@@ -227,12 +227,12 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
     if( enable && mDepthCreated==DONT_CREATE )
       {
       mDepthCreated = NOT_CREATED_YET;
-      moveToToDo();
+      markForCreation();
       }
     if( !enable && mDepthCreated!=DONT_CREATE )
       {
       mDepthCreated = DONT_CREATE;
-      moveToToDo();
+      markForCreation();
       }
     }
 
diff --git a/src/main/java/org/distorted/library/DistortedSurface.java b/src/main/java/org/distorted/library/DistortedSurface.java
index 62de8fe..ed927aa 100644
--- a/src/main/java/org/distorted/library/DistortedSurface.java
+++ b/src/main/java/org/distorted/library/DistortedSurface.java
@@ -19,6 +19,7 @@
 
 package org.distorted.library;
 
+import java.util.HashMap;
 import java.util.LinkedList;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -40,14 +41,28 @@ abstract class DistortedSurface
   static final int TYPE_TREE = 1;
   static final int TYPE_SYST = 2;
 
+  private static final int JOB_CREATE = 0;
+  private static final int JOB_DELETE = 1;
+
+  private class Job
+    {
+    DistortedSurface surface;
+    int action;
+
+    Job(DistortedSurface s, int a)
+      {
+      surface = s;
+      action  = a;
+      }
+    }
+
   private static boolean mToDo = false;
   private static LinkedList<DistortedSurface> mDoneList = new LinkedList<>();
-  private static LinkedList<DistortedSurface> mToDoList = new LinkedList<>();
+  private static HashMap<Long,Job> mToDoList = new HashMap<>();
   private static long mNextClientID = 0;
   private static long mNextSystemID = 0;
 
   private long mID;
-  private boolean mMarked;
   private int mType;
   int mColorCreated;
   int[] mColorH = new int[1];
@@ -66,26 +81,28 @@ abstract class DistortedSurface
     {
     if( mToDo )
       {
+      Job job;
       DistortedSurface surface;
 
-      int num = mToDoList.size();
-
-      for(int i=0; i<num; i++)
+      for(Long key: mToDoList.keySet())
         {
-        surface = mToDoList.removeFirst();
+        job = mToDoList.get(key);
+        surface = job.surface;
 
-        if(surface.mMarked)
-          {
-          surface.delete();
-          surface.mMarked = false;
-          }
-        else
+        android.util.Log.d("SURFACE", "  ---> need to "+(job.action==JOB_CREATE ? "create":"delete")+" surfaceID="+surface.getID() );
+
+        if( job.action==JOB_CREATE )
           {
           surface.create();
           mDoneList.add(surface);
           }
+        else if( job.action==JOB_DELETE )
+          {
+          surface.delete();
+          }
         }
 
+      mToDoList.clear();
       mToDo = false;
       }
     }
@@ -94,91 +111,88 @@ abstract class DistortedSurface
 
   static synchronized void onDestroy()
     {
+    Job job;
     DistortedSurface surface;
 
-    int num = mDoneList.size();
-
-    for(int i=0; i<num; i++)
+    for(Long key: mToDoList.keySet())
       {
-      surface = mDoneList.removeFirst();
+      job = mToDoList.get(key);
 
-      if( surface.mType==TYPE_SYST )
+      if( job.surface.mType==TYPE_SYST )
         {
-        mToDoList.add(surface);
-        surface.recreate();
+        mDoneList.add(job.surface);
         }
       }
 
-    num = mToDoList.size();
+    mToDoList.clear();
+
+    int num = mDoneList.size();
 
     for(int i=0; i<num; i++)
       {
-      surface = mToDoList.get(i);
+      surface = mDoneList.removeFirst();
 
-      if( surface.mType!=TYPE_SYST )
+      if( surface.mType==TYPE_SYST )
         {
-        mDoneList.remove(i);
-        i--;
-        num--;
+        mToDoList.put(surface.getID(), surface.new Job(surface,JOB_CREATE) );
+        surface.recreate();
         }
       }
 
-    mToDo = (num>0);
+    mToDo = true;
     mNextClientID = 0;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  synchronized void moveToToDo()
-    {
-    if ( mDoneList.remove(this) )
-      {
-      mToDoList.add(this);
-      mToDo = true;
-      }
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   @SuppressWarnings("unused")
   static void debugLists()
     {
     android.util.Log.e("Surface", "Done list:");
-    debugList(mDoneList);
+
+    DistortedSurface surface;
+    int num = mDoneList.size();
+
+    for(int i=0; i<num; i++)
+      {
+      surface = mDoneList.get(i);
+      surface.print(i, "");
+      }
+
     android.util.Log.e("Surface", "ToDo list:");
-    debugList(mToDoList);
+
+    Job job;
+    int i=0;
+
+    for(Long key: mToDoList.keySet())
+      {
+      job = mToDoList.get(key);
+      job.surface.print(i++, job.action==JOB_CREATE ? " create":" delete");
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private static void debugList(LinkedList<DistortedSurface> list)
+  private void print(int i, String extra)
     {
-    DistortedSurface surface;
     String str;
 
-    int num = list.size();
+    if( this instanceof DistortedFramebuffer ) str = (i+": Framebuffer ");
+    else if( this instanceof DistortedTexture) str = (i+": Texture     ");
+    else if( this instanceof DistortedScreen ) str = (i+": Screen      ");
+    else                                       str = (i+": UNKNOWN     ");
 
-    for(int i=0; i<num; i++)
-      {
-      surface = list.get(i);
-
-      if( surface instanceof DistortedFramebuffer ) str = (i+": Framebuffer ");
-      else if( surface instanceof DistortedTexture) str = (i+": Texture     ");
-      else if( surface instanceof DistortedScreen ) str = (i+": Screen      ");
-      else                                          str = (i+": UNKNOWN     ");
-
-      str += ("("+surface.getWidth()+","+surface.getHeight()+") surfaceID:"+surface.getID());
+    str += ("("+getWidth()+","+getHeight()+") surfaceID:"+getID());
 
-      switch(surface.mType)
-        {
-        case TYPE_SYST: str+=" SYSTEM"; break;
-        case TYPE_USER: str+=" USER"  ; break;
-        case TYPE_TREE: str+=" TREE"  ; break;
-        default       : str+=" ERROR??";
-        }
-
-      android.util.Log.e("Surface", str);
+    switch(mType)
+      {
+      case TYPE_SYST: str+=" SYSTEM"; break;
+      case TYPE_USER: str+=" USER"  ; break;
+      case TYPE_TREE: str+=" TREE"  ; break;
+      default       : str+=" ERROR??";
       }
+
+    android.util.Log.e("Surface", str+extra);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -189,32 +203,36 @@ abstract class DistortedSurface
     mSizeY        = height;
     mColorCreated = create;
     mColorH[0]    = 0;
-    mMarked       = false;
     mID           = type==TYPE_SYST ? --mNextSystemID : ++mNextClientID;
     mType         = type;
 
     if( create!=DONT_CREATE )
       {
-      mToDoList.add(this);
+      mToDoList.put(mID, new Job(this,JOB_CREATE) );
       mToDo = true;
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  synchronized void markForCreation()
+    {
+    mDoneList.remove(this);
+    mToDoList.put(mID, new Job(this,JOB_CREATE) );
+    mToDo = true;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Mark the underlying OpenGL object for deletion. Actual deletion will take place on the next render.
  */
-  public void markForDeletion()
+  synchronized public void markForDeletion()
     {
-    if( !mMarked )
-      {
-      mToDo   = true;
-      mMarked = true;
-      mDoneList.remove(this);
-      mToDoList.add(this);
-      }
+    mDoneList.remove(this);
+    mToDoList.put(mID, new Job(this,JOB_DELETE) );
+    mToDo = true;
     }
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/DistortedTexture.java b/src/main/java/org/distorted/library/DistortedTexture.java
index 6ea8572..e28c295 100644
--- a/src/main/java/org/distorted/library/DistortedTexture.java
+++ b/src/main/java/org/distorted/library/DistortedTexture.java
@@ -162,6 +162,6 @@ public class DistortedTexture extends DistortedSurface implements DistortedInput
   public void setTexture(Bitmap bmp)
     {
     mBmp= bmp;
-    moveToToDo();
+    markForCreation();
     }
   }
