commit 1942537e5f56b3230ff0087596824d1ce79084eb
Author: Leszek Koltunski <leszek@distoretedandroid.org>
Date:   Wed Dec 14 16:52:37 2016 +0000

    Major restructuring with DistortedTexture. One now is able to create Textures anywhere, even from a thread which does not hold the OpenGL context. Same for DistortedFramebuffers.

diff --git a/src/main/java/org/distorted/library/Distorted.java b/src/main/java/org/distorted/library/Distorted.java
index 09caf82..7d28080 100644
--- a/src/main/java/org/distorted/library/Distorted.java
+++ b/src/main/java/org/distorted/library/Distorted.java
@@ -351,7 +351,6 @@ public class Distorted
     GLES20.glEnableVertexAttribArray(mNormalH);
     GLES20.glEnableVertexAttribArray(mTextureCoordH);
    
-    DistortedTexture.reset();
     DistortedObjectTree.reset();
     EffectMessageSender.startSending();
     }
@@ -380,6 +379,7 @@ public class Distorted
     DistortedFramebuffer.release();
     DistortedObjectTree.release();
     EffectQueue.release();
+    DistortedEffectQueues.release();
     EffectMessageSender.stopSending();
    
     mInitialized = false;
diff --git a/src/main/java/org/distorted/library/DistortedEffectQueues.java b/src/main/java/org/distorted/library/DistortedEffectQueues.java
index d4904e0..4552d50 100644
--- a/src/main/java/org/distorted/library/DistortedEffectQueues.java
+++ b/src/main/java/org/distorted/library/DistortedEffectQueues.java
@@ -88,8 +88,6 @@ public class DistortedEffectQueues
    
   void drawPriv(long currTime, DistortedTexture tex, GridObject grid, DistortedFramebuffer df)
     {
-    DistortedFramebuffer.deleteAllMarked();
-
     GLES20.glViewport(0, 0, df.mWidth, df.mHeight);
 
     float halfZ = tex.mHalfX*grid.zFactor;
@@ -132,6 +130,13 @@ public class DistortedEffectQueues
     mF = null;
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static void release()
+    {
+    mNextID = 0;
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -172,9 +177,12 @@ public class DistortedEffectQueues
  */
   public void draw(long currTime, DistortedTexture tex, GridObject grid)
     {
+    tex.createTexture();
     GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
     GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex.mTextureDataH[0]);
     drawPriv(currTime, tex, grid, Distorted.mFramebuffer);
+    DistortedFramebuffer.deleteAllMarked();
+    DistortedTexture.deleteAllMarked();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -186,9 +194,12 @@ public class DistortedEffectQueues
  */
   public void draw(long currTime, DistortedTexture tex, GridObject grid, DistortedFramebuffer df)
     {
+    tex.createTexture();
     df.setAsOutput();
     GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex.mTextureDataH[0]);
     drawPriv(currTime, tex, grid, df);
+    DistortedFramebuffer.deleteAllMarked();
+    DistortedTexture.deleteAllMarked();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/DistortedFramebuffer.java b/src/main/java/org/distorted/library/DistortedFramebuffer.java
index fa64605..9e7fe60 100644
--- a/src/main/java/org/distorted/library/DistortedFramebuffer.java
+++ b/src/main/java/org/distorted/library/DistortedFramebuffer.java
@@ -48,8 +48,7 @@ public class DistortedFramebuffer
   private static boolean mListMarked = false;
   private static LinkedList<DistortedFramebuffer> mList = new LinkedList<>();
 
-  private float mX, mY;
-  private float mFOV;
+  private float mX, mY, mFOV;
 
   private int[] texIds = new int[1];
   private int[] fboIds = new int[1];
@@ -153,7 +152,8 @@ public class DistortedFramebuffer
 
   void reset()
     {
-    texIds[0] = TEXTURE_NOT_CREATED_YET;
+    if( texIds[0]!=TEXTURE_DONT_CREATE)
+      texIds[0] = TEXTURE_NOT_CREATED_YET;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -198,6 +198,7 @@ public class DistortedFramebuffer
  * @param width Width of the COLOR attachment.
  * @param height Height of the COLOR attachment.
  */
+  @SuppressWarnings("unused")
   public DistortedFramebuffer(int width, int height)
     {
     mProjectionMatrix = new float[16];
diff --git a/src/main/java/org/distorted/library/DistortedObjectTree.java b/src/main/java/org/distorted/library/DistortedObjectTree.java
index 2d589e2..290bead 100644
--- a/src/main/java/org/distorted/library/DistortedObjectTree.java
+++ b/src/main/java/org/distorted/library/DistortedObjectTree.java
@@ -86,6 +86,8 @@ public class DistortedObjectTree
   
   private void drawRecursive(long currTime, DistortedFramebuffer df)
     {
+    mTexture.createTexture();
+
     if( mNumChildren[0]<=0 )
       {
       GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexture.mTextureDataH[0]);
@@ -147,7 +149,7 @@ public class DistortedObjectTree
     {
     ArrayList<Long> ret = new ArrayList<>();
      
-    ret.add( mNumChildren[0]>0 ? mTexture.getBitmapID() : mTexture.getID() );
+    ret.add( mTexture.getID() );
     DistortedObjectTree node;
    
     for(int i=0; i<mNumChildren[0]; i++)
@@ -167,7 +169,7 @@ public class DistortedObjectTree
 
     if( otherNodesPoint ) mData.numPointingNodes--;
     else                  mMapNodeID.remove(oldList);
-   
+
     NodeData newData = mMapNodeID.get(newList);
     
     if( newData==null )
@@ -224,16 +226,16 @@ public class DistortedObjectTree
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // Debug - print all the Node IDs
-/*
+
   void debug(int depth)
     {
     String tmp="";
     int i;
 
     for(i=0; i<depth; i++) tmp +="   ";
-    tmp += (""+mData.ID);
+    tmp += (mData.ID+" (nodes: "+mData.numPointingNodes+")");
 
-    android.util.Log.e("node", tmp);
+    android.util.Log.e("NODE", tmp);
 
     for(i=0; i<mNumChildren[0]; i++)
       mChildren.get(i).debug(depth+1);
@@ -253,7 +255,7 @@ public class DistortedObjectTree
       android.util.Log.e("NODE", "key="+key+" NodeID: "+tmp.ID);
       }
     }
-*/
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -275,8 +277,8 @@ public class DistortedObjectTree
     mNumChildren[0] = 0;
    
     ArrayList<Long> list = new ArrayList<>();
-    list.add(queues.getID());   // TODO: this should depend on both texture and queues! Maybe even on grid as well
-      
+    list.add(mTexture.getID());
+
     mData = mMapNodeID.get(list);
    
     if( mData!=null )
@@ -286,7 +288,7 @@ public class DistortedObjectTree
     else
       {
       mData = new NodeData(++mNextNodeID);   
-      mMapNodeID.put(list, mData);  
+      mMapNodeID.put(list, mData);
       }
     }
 
@@ -469,6 +471,8 @@ public class DistortedObjectTree
     GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
     markRecursive();
     drawRecursive(currTime,Distorted.mFramebuffer);
+    DistortedFramebuffer.deleteAllMarked();
+    DistortedTexture.deleteAllMarked();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -483,6 +487,8 @@ public class DistortedObjectTree
     df.setAsOutput();
     markRecursive();
     drawRecursive(currTime,df);
+    DistortedFramebuffer.deleteAllMarked();
+    DistortedTexture.deleteAllMarked();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/DistortedTexture.java b/src/main/java/org/distorted/library/DistortedTexture.java
index 39194ab..cfac734 100644
--- a/src/main/java/org/distorted/library/DistortedTexture.java
+++ b/src/main/java/org/distorted/library/DistortedTexture.java
@@ -24,21 +24,20 @@ import android.graphics.Matrix;
 import android.opengl.GLES20;
 import android.opengl.GLUtils;
 
-import java.util.Arrays;
-import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 public class DistortedTexture
   {
-  private static long mNextID =0;
-  private static HashMap<Long,DistortedTexture> mTextures = new HashMap<>();
+  private static boolean mListMarked = false;
+  private static LinkedList<DistortedTexture> mList = new LinkedList<>();
 
   private int mSizeX, mSizeY;  // in screen space
   float mHalfX, mHalfY;        // halves of the above
-
   private long mID;
-  private long mBitmapID=0;
+  private boolean mMarked;
 
   private Bitmap[] mBmp= null; //
   int[] mTextureDataH;         // have to be shared among all the cloned Objects
@@ -63,13 +62,14 @@ public class DistortedTexture
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// this will be called on startup and every time OpenGL context has been lost
-// also call this from the constructor if the OpenGL context has been created already.
+// must be called from a thread holding OpenGL Context
 
-  private void resetTexture()
+  void createTexture()
     {
-    if( mTextureDataH!=null )
+    if( mBmp[0]!=null && mTextureDataH!=null )
       {
+      //android.util.Log.e("Texture", "creating "+mID);
+
       if( mTextureDataH[0]==0 ) GLES20.glGenTextures(1, mTextureDataH, 0);
 
       GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataH[0]);
@@ -78,58 +78,67 @@ public class DistortedTexture
       GLES20.glTexParameteri ( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE );
       GLES20.glTexParameteri ( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE );
 
-      if( mBmp!=null && mBmp[0]!=null)
-        {
-        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, flipBitmap(mBmp[0]), 0);
-        mBmp[0] = null;
-        }
+      GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, flipBitmap(mBmp[0]), 0);
+      mBmp[0] = null;
       }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// must be called from a thread holding OpenGL Context
 
-  private void releasePriv()
+  private void deleteTexture()
     {
-    mBmp          = null;
-    mTextureDataH = null;
-    }
+    if( mTextureDataH!=null && mTextureDataH[0]>0 )
+      {
+      //android.util.Log.e("Texture", "deleting "+mID);
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
+      GLES20.glDeleteTextures(1, mTextureDataH, 0);
 
-  long getBitmapID()
-    {
-    if( mBmp!=null && mBitmapID==0 ) mBitmapID = Arrays.hashCode(mBmp);
-    return mBitmapID;
+      mTextureDataH[0] = 0;
+      mBitmapSet[0] = false;
+      }
+
+    mMarked = false;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   long getID()
-      {
-      return mID;
-      }
+    {
+    return mID;
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  static synchronized void reset()
+  static synchronized void release()
     {
-    for(long id: mTextures.keySet())
-      {
-      mTextures.get(id).resetTexture();
-      }
+    mListMarked = false;
+    mList.clear();
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// must be called form a thread holding OpenGL Context
 
-  static synchronized void release()
+  static synchronized void deleteAllMarked()
     {
-    for(long id: mTextures.keySet())
+    if( mListMarked )
       {
-      mTextures.get(id).releasePriv();
-      }
+      DistortedTexture tmp;
+      Iterator<DistortedTexture> iterator = mList.iterator();
+
+      while(iterator.hasNext())
+        {
+        tmp = iterator.next();
+
+        if( tmp.mMarked )
+          {
+          tmp.deleteTexture();
+          iterator.remove();
+          }
+        }
 
-    mTextures.clear();
-    mNextID = 0;
+      mListMarked = false;
+      }
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -140,9 +149,6 @@ public class DistortedTexture
  */
   public DistortedTexture(int width, int height)
     {
-    mID = mNextID++;
-    mTextures.put(mID,this);
-
     mSizeX= width ; mHalfX = mSizeX/2.0f;
     mSizeY= height; mHalfY = mSizeY/2.0f;
 
@@ -152,8 +158,10 @@ public class DistortedTexture
     mBmp[0]         = null;
     mBitmapSet      = new boolean[1];
     mBitmapSet[0]   = false;
+    mID             = 0;
+    mMarked         = false;
 
-    if( Distorted.isInitialized() ) resetTexture();
+    mList.add(this);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -170,9 +178,6 @@ public class DistortedTexture
 
   public DistortedTexture(DistortedTexture dt, int flags)
     {
-    mID = mNextID++;
-    mTextures.put(mID,this);
-
     mSizeX= dt.mSizeX ; mHalfX = mSizeX/2.0f;
     mSizeY= dt.mSizeY ; mHalfY = mSizeY/2.0f;
 
@@ -181,6 +186,7 @@ public class DistortedTexture
       mTextureDataH = dt.mTextureDataH;
       mBmp          = dt.mBmp;
       mBitmapSet    = dt.mBitmapSet;
+      mID           = dt.getID();
       }
     else
       {
@@ -190,24 +196,29 @@ public class DistortedTexture
       mBitmapSet[0]   = false;
       mBmp            = new Bitmap[1];
       mBmp[0]         = null;
-
-      if( Distorted.isInitialized() ) resetTexture();
+      mID             = 0;
       }
+
+    mMarked = false;
+
+    mList.add(this);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
- * Releases all resources.
+ * Mark the underlying OpenGL object for deletion. Actual deletion will take place on the next render.
  */
-  public synchronized void delete()
+  public void markForDeletion()
     {
-    releasePriv();
-    mTextures.remove(this);
+    //android.util.Log.e("Texture", "marking for deletion "+mID);
+
+    mListMarked = true;
+    mMarked     = true;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
- * Sets the underlying android.graphics.Bitmap object and uploads it to the GPU.
+ * Sets the underlying android.graphics.Bitmap object.
  * <p>
  * You can only recycle() the passed Bitmap once the OpenGL context gets created (i.e. after call
  * to onSurfaceCreated) because only after this point can the Library upload it to the GPU!
@@ -218,17 +229,10 @@ public class DistortedTexture
   public void setTexture(Bitmap bmp)
     {
     mBitmapSet[0] = true;
+    mBmp[0]       = bmp;
+    mID           = bmp.hashCode();
 
-    if( Distorted.isInitialized() )
-      {
-      GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
-      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataH[0]);
-      GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, flipBitmap(bmp), 0);
-      }
-    else
-      {
-      mBmp[0] = bmp;
-      }
+    //android.util.Log.e("Texture", "setting new bitmap "+mID);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -238,9 +242,9 @@ public class DistortedTexture
  * @return height of the object, in pixels.
  */
   public int getWidth()
-     {
-     return mSizeX;
-     }
+    {
+    return mSizeX;
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
@@ -249,9 +253,9 @@ public class DistortedTexture
  * @return width of the Object, in pixels.
  */
   public int getHeight()
-      {
-      return mSizeY;
-      }
+    {
+    return mSizeY;
+    }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
@@ -264,7 +268,7 @@ public class DistortedTexture
  * @return depth of the Object, in pixels.
  */
   public int getDepth(GridObject grid)
-      {
-      return grid==null ? 0 : (int)(mSizeX*grid.zFactor);
-      }
+    {
+    return grid==null ? 0 : (int)(mSizeX*grid.zFactor);
+    }
   }
diff --git a/src/main/java/org/distorted/library/GridObject.java b/src/main/java/org/distorted/library/GridObject.java
index 4561e62..3c72773 100644
--- a/src/main/java/org/distorted/library/GridObject.java
+++ b/src/main/java/org/distorted/library/GridObject.java
@@ -35,7 +35,7 @@ public abstract class GridObject
    protected int dataLength;
    protected FloatBuffer mGridPositions,mGridNormals,mGridTexture;
 
-   final float zFactor; // strange workaround the fact that we need to somehow store the 'depth'
+   final float zFactor; // strange workaround for the fact that we need to somehow store the 'depth'
                         // of the Grid. Used in DistortedEffectQueues. See DistortedTexture.getDepth().
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
