commit efe3d8feaaab28e0dbf004ac63bac11abc73e19c
Author: leszek <leszek@koltunski.pl>
Date:   Sat Apr 15 23:58:36 2017 +0100

    Messing around with the AttachDaemon (from now on a more generic DistortedMaster).
    
    In preparation for thr SORT job that will let us sort the children Nodes into postprocessing Buckets.

diff --git a/src/main/java/org/distorted/library/Distorted.java b/src/main/java/org/distorted/library/Distorted.java
index e278435..94686ef 100644
--- a/src/main/java/org/distorted/library/Distorted.java
+++ b/src/main/java/org/distorted/library/Distorted.java
@@ -134,7 +134,7 @@ public class Distorted
     DistortedNode.onDestroy();
     EffectQueue.onDestroy();
     DistortedEffects.onDestroy();
-    DistortedAttachDaemon.onDestroy();
+    DistortedMaster.onDestroy();
     EffectMessageSender.stopSending();
 
     mInitialized = false;
diff --git a/src/main/java/org/distorted/library/DistortedAttachDaemon.java b/src/main/java/org/distorted/library/DistortedAttachDaemon.java
deleted file mode 100644
index 172f7a9..0000000
--- a/src/main/java/org/distorted/library/DistortedAttachDaemon.java
+++ /dev/null
@@ -1,137 +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;
-
-import java.util.ArrayList;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * This static class handles changing topology of a Tree in a safe way. I.e. if one wants to
- * attach() a new Node somewhere in the Tree, we cannot simply attach it mid-render (detach() is
- * even worse!). What we do instead is mark that this job will have to be done and actually do it
- * just before the next render. That's what this Daemon does.
- */
-class DistortedAttachDaemon
-  {
-  private static final int ATTACH    = 0; //
-  private static final int DETACH    = 1; // types of jobs that the Daemon can do
-  private static final int DETACHALL = 2; //
-
-  private static class Job
-    {
-    int type;
-    DistortedAttacheable attacheable;
-    DistortedNode object;
-
-    Job(int t, DistortedAttacheable a, DistortedNode o)
-      {
-      type        = t;
-      attacheable = a;
-      object      = o;
-      }
-    }
-
-  private static ArrayList<Job> mJobs = new ArrayList<>();
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private DistortedAttachDaemon()
-    {
-
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static boolean toDo()
-    {
-    int num = mJobs.size();
-    Job job;
-
-    for(int i=0; i<num; i++)
-      {
-      job = mJobs.remove(0);
-
-      switch(job.type)
-        {
-        case ATTACH   : job.attacheable.attachNow(job.object);
-                        break;
-        case DETACH   : job.attacheable.detachNow(job.object);
-                        break;
-        case DETACHALL: job.attacheable.detachAllNow()       ;
-                        break;
-        default       : android.util.Log.e("AttachDaemon", "what?");
-        }
-      }
-
-    return ( num>0 );
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void cancelAttachJobs(DistortedAttacheable a, DistortedEffects e)
-    {
-    int num = mJobs.size();
-    Job job;
-
-    for(int i=0; i<num; i++)
-      {
-      job = mJobs.get(i);
-
-      if( job.type == ATTACH && job.attacheable==a )
-        {
-        DistortedEffects effects = job.object.getEffects();
-
-        if( effects.getID() == e.getID() )
-          {
-          mJobs.remove(i);
-          break;
-          }
-        }
-      }
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void attach(DistortedAttacheable a, DistortedNode n)
-    {
-    mJobs.add(new Job(ATTACH,a,n));
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void detach(DistortedAttacheable a, DistortedNode n)
-    {
-    mJobs.add(new Job(DETACH,a,n));
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void detachAll(DistortedAttacheable a)
-    {
-    mJobs.add(new Job(DETACHALL,a,null));
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  static void onDestroy()
-    {
-    mJobs.clear();
-    }
-  }
diff --git a/src/main/java/org/distorted/library/DistortedAttacheable.java b/src/main/java/org/distorted/library/DistortedAttacheable.java
deleted file mode 100644
index 4c9bec9..0000000
--- a/src/main/java/org/distorted/library/DistortedAttacheable.java
+++ /dev/null
@@ -1,48 +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;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Package-private interface implemented by all objects to which one can attach a Tree-like structure
- * of DistortedNodes. (Currently: DistortedNode itself and DistortedOutputSurface).
- * <p>
- * All the methods below are really meant to be package-local; and this interface should really be a
- * package-local class which other classes would extend (but that's impossible because OutputSurface
- * already extends other class).
- */
-interface DistortedAttacheable
-  {
-  /**
-   * Not part of public API, do not document
-   * @y.exclude
-   */
-  void attachNow(DistortedNode node);
-  /**
-   * Not part of public API, do not document
-   * @y.exclude
-   */
-  void detachNow(DistortedNode node);
-  /**
-   * Not part of public API, do not document
-   * @y.exclude
-   */
-  void detachAllNow();
-  }
diff --git a/src/main/java/org/distorted/library/DistortedMaster.java b/src/main/java/org/distorted/library/DistortedMaster.java
new file mode 100644
index 0000000..a337ead
--- /dev/null
+++ b/src/main/java/org/distorted/library/DistortedMaster.java
@@ -0,0 +1,84 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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;
+
+import java.util.ArrayList;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * This static class handles assigning jobs to other classes. It does it once, at the beginning of
+ * each frame.
+ */
+class DistortedMaster
+  {
+  private static ArrayList<DistortedSlave> mSlaves = new ArrayList<>();
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private DistortedMaster()
+    {
+
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static boolean toDo()
+    {
+    DistortedSlave slave;
+    int num = mSlaves.size();
+
+    for(int i=0; i<num; i++)
+      {
+      slave = mSlaves.remove(0);
+      slave.doWork();
+      }
+
+    return ( num>0 );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static void newSlave(DistortedSlave s)
+    {
+    int num = mSlaves.size();
+    boolean found = false;
+    DistortedSlave tmp;
+
+    for(int i=0; i<num; i++)
+      {
+      tmp = mSlaves.get(i);
+
+      if( tmp==s )
+        {
+        found = true;
+        break;
+        }
+      }
+
+    if( !found ) mSlaves.add(s);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static void onDestroy()
+    {
+    mSlaves.clear();
+    }
+  }
diff --git a/src/main/java/org/distorted/library/DistortedNode.java b/src/main/java/org/distorted/library/DistortedNode.java
index 56e9315..eff618a 100644
--- a/src/main/java/org/distorted/library/DistortedNode.java
+++ b/src/main/java/org/distorted/library/DistortedNode.java
@@ -22,8 +22,6 @@ package org.distorted.library;
 import java.util.ArrayList;
 import java.util.HashMap;
 
-import android.opengl.GLES30;
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Class which represents a Node in a Tree of (InputSurface,Mesh,Effects) triplets.
@@ -35,8 +33,32 @@ import android.opengl.GLES30;
  * to sub-class 'NodeData'. Two identical sub-trees attached at different points of the main tree
  * will point to the same NodeData; only the first of this is rendered (mData.numRender!).
  */
-public class DistortedNode implements DistortedAttacheable
+public class DistortedNode implements DistortedSlave
   {
+  private static final int ATTACH = 0;
+  private static final int DETACH = 1;
+  private static final int DETALL = 2;
+  private static final int SORT   = 3;
+
+  private ArrayList<DistortedNode> mChildren;
+  private int[] mNumChildren;  // ==mChildren.length(), but we only create mChildren if the first one gets added
+
+  private class Job
+    {
+    int type;
+    DistortedNode node;
+    DistortedEffectsPostprocess dep;
+
+    Job(int t, DistortedNode n, DistortedEffectsPostprocess d)
+      {
+      type = t;
+      node = n;
+      dep  = d;
+      }
+    }
+
+  private ArrayList<Job> mJobs = new ArrayList<>();
+
   private static HashMap<ArrayList<Long>,NodeData> mMapNodeID = new HashMap<>();
   private static long mNextNodeID =0;
 
@@ -48,9 +70,6 @@ public class DistortedNode implements DistortedAttacheable
   private DistortedRenderState mState;
   private NodeData mData;
 
-  private ArrayList<DistortedNode> mChildren;
-  private int[] mNumChildren;  // ==mChildren.length(), but we only create mChildren if the first one gets added
-
   private class NodeData
     {
     long ID;
@@ -355,13 +374,14 @@ public class DistortedNode implements DistortedAttacheable
  * Adds a new child to the last position in the list of our Node's children.
  * <p>
  * We cannot do this mid-render - actual attachment will be done just before the next render, by the
- * DistortedAttachDeamon (by calling attachNow())
+ * DistortedMaster (by calling doWork())
  *
  * @param node The new Node to add.
  */
   public void attach(DistortedNode node)
     {
-    DistortedAttachDaemon.attach(this,node);
+    mJobs.add(new Job(ATTACH,node,null));
+    DistortedMaster.newSlave(this);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -369,7 +389,7 @@ public class DistortedNode implements DistortedAttacheable
  * Adds a new child to the last position in the list of our Node's children.
  * <p>
  * We cannot do this mid-render - actual attachment will be done just before the next render, by the
- * DistortedAttachDeamon (by calling attachNow())
+ * DistortedMaster (by calling doWork())
  *
  * @param surface InputSurface to initialize our child Node with.
  * @param effects DistortedEffects to initialize our child Node with.
@@ -379,41 +399,24 @@ public class DistortedNode implements DistortedAttacheable
   public DistortedNode attach(DistortedInputSurface surface, DistortedEffects effects, MeshObject mesh)
     {
     DistortedNode node = new DistortedNode(surface,effects,mesh);
-    DistortedAttachDaemon.attach(this,node);
+    mJobs.add(new Job(ATTACH,node,null));
+    DistortedMaster.newSlave(this);
     return node;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * This is not really part of the public API. Has to be public only because it is a part of the
- * DistortedAttacheable interface, which should really be a class that we extend here instead but
- * Java has no multiple inheritance.
- *
- * @y.exclude
- * @param node The new Node to add.
- */
-  public void attachNow(DistortedNode node)
-    {
-    if( mChildren==null ) mChildren = new ArrayList<>(2);
-
-    node.mParent = this;
-    mChildren.add(node);
-    mNumChildren[0]++;
-    adjustIsomorphism();
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Removes the first occurrence of a specified child from the list of children of our Node.
  * <p>
  * We cannot do this mid-render - actual detachment will be done just before the next render, by the
- * DistortedAttachDeamon (by calling detachNow())
+ * DistortedMaster (by calling doWork())
  *
  * @param node The Node to remove.
  */
   public void detach(DistortedNode node)
     {
-    DistortedAttachDaemon.detach(this,node);
+    mJobs.add(new Job(DETACH,node,null));
+    DistortedMaster.newSlave(this);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -424,7 +427,7 @@ public class DistortedNode implements DistortedAttacheable
  * of them having the same Effects but - for instance - different Mesh. Use with care.
  * <p>
  * We cannot do this mid-render - actual detachment will be done just before the next render, by the
- * DistortedAttachDeamon (by calling detachNow())
+ * DistortedMaster (by calling doWork())
  *
  * @param effects DistortedEffects to remove.
  */
@@ -432,16 +435,17 @@ public class DistortedNode implements DistortedAttacheable
     {
     long id = effects.getID();
     DistortedNode node;
-    boolean detached= false;
+    boolean detached = false;
 
     for(int i=0; i<mNumChildren[0]; i++)
       {
       node = mChildren.get(i);
 
-      if( node.mEffects.getID()==id )
+      if( node.getEffects().getID()==id )
         {
-        detached=true;
-        DistortedAttachDaemon.detach(this,node);
+        detached = true;
+        mJobs.add(new Job(DETACH,node,null));
+        DistortedMaster.newSlave(this);
         break;
         }
       }
@@ -449,27 +453,20 @@ public class DistortedNode implements DistortedAttacheable
     if( !detached )
       {
       // if we failed to detach any, it still might be the case that
-      // there's a job in Daemon's queue that we need to cancel.
-      DistortedAttachDaemon.cancelAttachJobs(this,effects);
-      }
-    }
+      // there's an ATTACH job that we need to cancel.
+      int num = mJobs.size();
+      Job job;
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * This is not really part of the public API. Has to be public only because it is a part of the
- * DistortedAttacheable interface, which should really be a class that we extend here instead but
- * Java has no multiple inheritance.
- *
- * @y.exclude
- * @param node The Node to remove.
- */
-  public void detachNow(DistortedNode node)
-    {
-    if( mNumChildren[0]>0 && mChildren.remove(node) )
-      {
-      node.mParent = null;
-      mNumChildren[0]--;
-      adjustIsomorphism();
+      for(int i=0; i<num; i++)
+        {
+        job = mJobs.get(i);
+
+        if( job.type==ATTACH && job.node.getEffects()==effects )
+          {
+          mJobs.remove(i);
+          break;
+          }
+        }
       }
     }
 
@@ -478,38 +475,79 @@ public class DistortedNode implements DistortedAttacheable
  * Removes all children Nodes.
  * <p>
  * We cannot do this mid-render - actual detachment will be done just before the next render, by the
- * DistortedAttachDeamon (by calling detachAllNow())
+ * DistortedMaster (by calling doWork())
  */
   public void detachAll()
     {
-    DistortedAttachDaemon.detachAll(this);
+    mJobs.add(new Job(DETALL,null,null));
+    DistortedMaster.newSlave(this);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * This is not really part of the public API. Has to be public only because it is a part of the
- * DistortedAttacheable interface, which should really be a class that we extend here instead but
+ * DistortedSlave interface, which should really be a class that we extend here instead but
  * Java has no multiple inheritance.
  *
  * @y.exclude
  */
-  public void detachAllNow()
+  public void doWork()
     {
-    if( mNumChildren[0]>0 )
+    int num = mJobs.size();
+    Job job;
+
+    int numChanges=0;
+    int numSorts  =0;
+
+    for(int i=0; i<num; i++)
       {
-      DistortedNode tmp;
+      job = mJobs.remove(0);
 
-      for(int i=mNumChildren[0]-1; i>=0; i--)
+      switch(job.type)
         {
-        tmp = mChildren.remove(i);
-        tmp.mParent = null;
+        case ATTACH: numChanges++;
+                     if( mChildren==null ) mChildren = new ArrayList<>(2);
+                     job.node.mParent = this;
+                     mChildren.add(job.node);
+                     mNumChildren[0]++;
+                     break;
+        case DETACH: numChanges++;
+                     if( mNumChildren[0]>0 && mChildren.remove(job.node) )
+                       {
+                       job.node.mParent = 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;
+                         }
+
+                       mNumChildren[0] = 0;
+                       }
+                     break;
+        case SORT  : numSorts++;
+                     // TODO: sort
+                     break;
         }
+      }
 
-      mNumChildren[0] = 0;
+    if( numChanges>0 )
+      {
       adjustIsomorphism();
+
+      if( numSorts==0 )
+        {
+        // TODO: sort
+        }
       }
     }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Sets the Postprocessing Effects we will apply to the temporary buffer this Node - and fellow siblings
diff --git a/src/main/java/org/distorted/library/DistortedOutputSurface.java b/src/main/java/org/distorted/library/DistortedOutputSurface.java
index 0114912..072059a 100644
--- a/src/main/java/org/distorted/library/DistortedOutputSurface.java
+++ b/src/main/java/org/distorted/library/DistortedOutputSurface.java
@@ -25,11 +25,30 @@ import java.util.ArrayList;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-abstract class DistortedOutputSurface extends DistortedSurface implements DistortedAttacheable
+abstract class DistortedOutputSurface extends DistortedSurface implements DistortedSlave
 {
+  private static final int ATTACH = 0;
+  private static final int DETACH = 1;
+  private static final int DETALL = 2;
+  private static final int SORT   = 3;
+
   private ArrayList<DistortedNode> mChildren;
   private int mNumChildren;   // ==mChildren.length(), but we only create mChildren if the first one gets added
 
+  private class Job
+    {
+    int type;
+    DistortedNode node;
+
+    Job(int t, DistortedNode n)
+      {
+      type = t;
+      node = n;
+      }
+    }
+
+  private ArrayList<Job> mJobs = new ArrayList<>();
+
   DistortedFramebuffer mBuffer1, mBuffer2;
 
   private long mTime;
@@ -181,7 +200,7 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
 /*
     boolean changed =
 */
-    DistortedAttachDaemon.toDo();
+    DistortedMaster.toDo();
 /*
     // debugging only
     if( changed )
@@ -374,13 +393,14 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
  * Adds a new child to the last position in the list of our Surface's children.
  * <p>
  * We cannot do this mid-render - actual attachment will be done just before the next render, by the
- * DistortedAttachDeamon (by calling attachNow())
+ * DistortedMaster (by calling doWork())
  *
  * @param node The new Node to add.
  */
   public void attach(DistortedNode node)
     {
-    DistortedAttachDaemon.attach(this,node);
+    mJobs.add(new Job(ATTACH,node));
+    DistortedMaster.newSlave(this);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -388,7 +408,7 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
  * Adds a new child to the last position in the list of our Surface's children.
  * <p>
  * We cannot do this mid-render - actual attachment will be done just before the next render, by the
- * DistortedAttachDeamon (by calling attachNow())
+ * DistortedMaster (by calling doWork())
  *
  * @param surface InputSurface to initialize our child Node with.
  * @param effects DistortedEffects to initialize our child Node with.
@@ -398,26 +418,11 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
   public DistortedNode attach(DistortedInputSurface surface, DistortedEffects effects, MeshObject mesh)
     {
     DistortedNode node = new DistortedNode(surface,effects,mesh);
-    DistortedAttachDaemon.attach(this,node);
+    mJobs.add(new Job(ATTACH,node));
+    DistortedMaster.newSlave(this);
     return node;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * This is not really part of the public API. Has to be public only because it is a part of the
- * DistortedAttacheable interface, which should really be a class that we extend here instead but
- * Java has no multiple inheritance.
- *
- * @y.exclude
- * @param node new Node to add.
- */
-  public void attachNow(DistortedNode node)
-    {
-    if( mChildren==null ) mChildren = new ArrayList<>(2);
-    mChildren.add(node);
-    mNumChildren++;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Removes the first occurrence of a specified child from the list of children of our Surface.
@@ -426,7 +431,7 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
  * of them having the same Effects but - for instance - different Mesh. Use with care.
  * <p>
  * We cannot do this mid-render - actual detachment will be done just before the next render, by the
- * DistortedAttachDeamon (by calling detachNow())
+ * DistortedMaster (by calling doWork())
  *
  * @param effects DistortedEffects to remove.
  */
@@ -443,7 +448,8 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
       if( node.getEffects().getID()==id )
         {
         detached = true;
-        DistortedAttachDaemon.detach(this,node);
+        mJobs.add(new Job(DETACH,node));
+        DistortedMaster.newSlave(this);
         break;
         }
       }
@@ -451,8 +457,20 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
     if( !detached )
       {
       // if we failed to detach any, it still might be the case that
-      // there's a job in Daemon's queue that we need to cancel.
-      DistortedAttachDaemon.cancelAttachJobs(this,effects);
+      // there's an ATTACH job that we need to cancel.
+      int num = mJobs.size();
+      Job job;
+
+      for(int i=0; i<num; i++)
+        {
+        job = mJobs.get(i);
+
+        if( job.type==ATTACH && job.node.getEffects()==effects )
+          {
+          mJobs.remove(i);
+          break;
+          }
+        }
       }
     }
 
@@ -461,30 +479,14 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
  * Removes the first occurrence of a specified child from the list of children of our Surface.
  * <p>
  * We cannot do this mid-render - actual attachment will be done just before the next render, by the
- * DistortedAttachDeamon (by calling detachNow())
+ * DistortedMaster (by calling doWork())
  *
  * @param node The Node to remove.
  */
   public void detach(DistortedNode node)
     {
-    DistortedAttachDaemon.detach(this,node);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * This is not really part of the public API. Has to be public only because it is a part of the
- * DistortedAttacheable interface, which should really be a class that we extend here instead but
- * Java has no multiple inheritance.
- *
- * @y.exclude
- * @param node The Node to remove.
- */
-  public void detachNow(DistortedNode node)
-    {
-    if( mNumChildren>0 && mChildren.remove(node) )
-      {
-      mNumChildren--;
-      }
+    mJobs.add(new Job(DETACH,node));
+    DistortedMaster.newSlave(this);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -492,27 +494,63 @@ abstract class DistortedOutputSurface extends DistortedSurface implements Distor
  * Removes all children Nodes.
  * <p>
  * We cannot do this mid-render - actual attachment will be done just before the next render, by the
- * DistortedAttachDeamon (by calling detachAllNow())
+ * DistortedMaster (by calling doWork())
  */
   public void detachAll()
     {
-    DistortedAttachDaemon.detachAll(this);
+    mJobs.add(new Job(DETALL,null));
+    DistortedMaster.newSlave(this);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * This is not really part of the public API. Has to be public only because it is a part of the
- * DistortedAttacheable interface, which should really be a class that we extend here instead but
+ * DistortedSlave interface, which should really be a class that we extend here instead but
  * Java has no multiple inheritance.
  *
  * @y.exclude
  */
-  public void detachAllNow()
+  public void doWork()
     {
-    if( mNumChildren>0 )
+    int num = mJobs.size();
+    Job job;
+
+    int numChanges=0;
+    int numSorts  =0;
+
+    for(int i=0; i<num; i++)
+      {
+      job = mJobs.remove(0);
+
+      switch(job.type)
+        {
+        case ATTACH: numChanges++;
+                     if( mChildren==null ) mChildren = new ArrayList<>(2);
+                     mChildren.add(job.node);
+                     mNumChildren++;
+                     break;
+        case DETACH: numChanges++;
+                     if( mNumChildren>0 && mChildren.remove(job.node) )
+                       {
+                       mNumChildren--;
+                       }
+                     break;
+        case DETALL: numChanges++;
+                     if( mNumChildren>0 )
+                       {
+                       mNumChildren = 0;
+                       mChildren.clear();
+                       }
+                     break;
+        case SORT  : numSorts++;
+                     // TODO: sort
+                     break;
+        }
+      }
+
+    if( numChanges>0 && numSorts==0 )
       {
-      mNumChildren = 0;
-      mChildren.clear();
+      // TODO: sort
       }
     }
 }
diff --git a/src/main/java/org/distorted/library/DistortedSlave.java b/src/main/java/org/distorted/library/DistortedSlave.java
new file mode 100644
index 0000000..1428d06
--- /dev/null
+++ b/src/main/java/org/distorted/library/DistortedSlave.java
@@ -0,0 +1,37 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// 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;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Package-private interface implemented by all objects which can be assigned work by the DistortedMaster.
+ * <p>
+ * All the methods below are really meant to be package-local; and this interface should really be a
+ * package-local class which other classes would extend (but that's impossible because OutputSurface
+ * already extends other class).
+ */
+interface DistortedSlave
+  {
+  /**
+   * Not part of public API, do not document
+   * @y.exclude
+   */
+  void doWork();
+  }
