Adds an internal weak material mechanism
authorRobert Bragg <robert@linux.intel.com>
Thu, 27 May 2010 19:04:49 +0000 (20:04 +0100)
committerRobert Bragg <robert@linux.intel.com>
Tue, 15 Jun 2010 14:26:28 +0000 (15:26 +0100)
This adds a _cogl_material_weak_copy() function that can be used to
create materials that don't count as strong dependants on their parents.
This means the parent can be modified without worrying about how it will
affect weak materials. The material age of the parent can potentially be
queried to determine if a weak material might need to be re-created.

clutter/cogl/cogl/cogl-material-private.h
clutter/cogl/cogl/cogl-material.c

index 796ea55..a16efd6 100644 (file)
@@ -523,6 +523,12 @@ struct _CoglMaterial
 
   /* bitfields */
 
+  /* Weak materials don't count as dependants on their parents which
+   * means that the parent material can be modified without
+   * considering how the modifications may affect the weak material.
+   */
+  unsigned int          is_weak:1;
+
   /* Determines if material->big_state is valid */
   unsigned int          has_big_state:1;
 
index 2c10edd..94a8473 100644 (file)
@@ -351,6 +351,7 @@ _cogl_material_init_default_material (void)
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
+  material->is_weak = FALSE;
   material->journal_ref_count = 0;
   material->parent = NULL;
   material->backend = COGL_MATERIAL_BACKEND_UNDEFINED;
@@ -422,6 +423,56 @@ _cogl_material_init_default_material (void)
   ctx->default_material = _cogl_material_handle_new (material);
 }
 
+static void
+_cogl_material_unparent (CoglMaterial *material)
+{
+  CoglMaterial *parent = material->parent;
+
+  if (parent == NULL)
+    return;
+
+  g_return_if_fail (parent->has_children);
+
+  if (parent->first_child == material)
+    {
+      if (parent->children)
+        {
+          parent->first_child = parent->children->data;
+          parent->children =
+            g_list_delete_link (parent->children, parent->children);
+        }
+      else
+        parent->has_children = FALSE;
+    }
+  else
+    parent->children = g_list_remove (parent->children, material);
+
+  cogl_handle_unref (parent);
+
+  material->parent = NULL;
+}
+
+static void
+_cogl_material_set_parent (CoglMaterial *material, CoglMaterial *parent)
+{
+  cogl_handle_ref (parent);
+
+  if (material->parent)
+    _cogl_material_unparent (material);
+
+  material->parent = parent;
+  if (G_UNLIKELY (parent->has_children))
+    parent->children = g_list_prepend (parent->children, material);
+  else
+    {
+      parent->has_children = TRUE;
+      parent->first_child = material;
+      parent->children = NULL;
+    }
+
+  material->parent = parent;
+}
+
 /* XXX: Always have an eye out for opportunities to lower the cost of
  * cogl_material_copy. */
 CoglHandle
@@ -434,17 +485,12 @@ cogl_material_copy (CoglHandle handle)
 
   material->_parent = src->_parent;
 
+  material->is_weak = FALSE;
+
   material->journal_ref_count = 0;
 
-  material->parent = cogl_handle_ref (src);
-  if (src->has_children)
-    src->children = g_list_prepend (src->children, material);
-  else
-    {
-      src->has_children = TRUE;
-      src->first_child = material;
-      src->children = NULL;
-    }
+  material->parent = NULL;
+  _cogl_material_set_parent (material, src);
 
   material->has_children = FALSE;
 
@@ -476,6 +522,26 @@ cogl_material_copy (CoglHandle handle)
   return _cogl_material_handle_new (material);
 }
 
+/* XXX: we should give this more thought before making anything like
+ * this API public! */
+CoglHandle
+_cogl_material_weak_copy (CoglHandle handle)
+{
+  CoglMaterial *material = COGL_MATERIAL (handle);
+  CoglHandle copy;
+  CoglMaterial *copy_material;
+
+  /* If we make a public API we might want want to allow weak copies
+   * of weak material? */
+  g_return_val_if_fail (!material->is_weak, COGL_INVALID_HANDLE);
+
+  copy = cogl_material_copy (handle);
+  copy_material = COGL_MATERIAL (copy);
+  copy_material->is_weak = TRUE;
+
+  return copy;
+}
+
 CoglHandle
 cogl_material_new (void)
 {
@@ -489,33 +555,6 @@ cogl_material_new (void)
 }
 
 static void
-_cogl_material_unparent (CoglMaterial *material)
-{
-  CoglMaterial *parent = material->parent;
-
-  if (parent == NULL)
-    return;
-
-  g_return_if_fail (parent->has_children);
-
-  if (parent->first_child == material)
-    {
-      if (parent->children)
-        {
-          parent->first_child = parent->children->data;
-          parent->children =
-            g_list_delete_link (parent->children, parent->children);
-        }
-      else
-        parent->has_children = FALSE;
-    }
-  else
-    parent->children = g_list_remove (parent->children, material);
-
-  cogl_handle_unref (parent);
-}
-
-static void
 _cogl_material_backend_free_priv (CoglMaterial *material)
 {
   if (material->backend != COGL_MATERIAL_BACKEND_UNDEFINED &&
@@ -1053,10 +1092,39 @@ _cogl_material_foreach_child (CoglMaterial *material,
 }
 
 static gboolean
-change_parent_cb (CoglMaterial *child,
-                  void *user_data)
+check_if_strong_cb (CoglMaterial *material, void *user_data)
 {
-  child->parent = user_data;
+  gboolean *has_strong_child = user_data;
+
+  if (!material->is_weak)
+    {
+      *has_strong_child = TRUE;
+      return FALSE;
+    }
+  return TRUE;
+}
+
+static gboolean
+has_strong_children (CoglMaterial *material)
+{
+  gboolean has_strong_child = FALSE;
+  _cogl_material_foreach_child (material,
+                                check_if_strong_cb,
+                                &has_strong_child);
+  return has_strong_child;
+}
+
+static gboolean
+reparent_strong_children_cb (CoglMaterial *material,
+                             void *user_data)
+{
+  CoglMaterial *parent = user_data;
+
+  if (material->is_weak)
+    return TRUE;
+
+  _cogl_material_set_parent (material, parent);
+
   return TRUE;
 }
 
@@ -1125,7 +1193,7 @@ _cogl_material_pre_change_notify (CoglMaterial     *material,
    * for unless we create another material to take its place first and
    * make sure descendants reference this new material instead.
    */
-  if (material->has_children)
+  if (has_strong_children (material))
     {
       CoglMaterial *new_authority;
       COGL_STATIC_COUNTER (material_copy_on_write_counter,
@@ -1155,14 +1223,13 @@ _cogl_material_pre_change_notify (CoglMaterial     *material,
       _cogl_material_copy_differences (new_authority, material,
                                        material->differences);
 
-      /* Reparent the children of material to be children of
+      /* Reparent the strong children of material to be children of
        * new_authority instead... */
-      new_authority->has_children = TRUE;
-      new_authority->first_child = material->first_child;
-      new_authority->children = material->children;
-      material->has_children = FALSE;
-      _cogl_material_foreach_child (new_authority,
-                                    change_parent_cb,
+      new_authority->has_children = FALSE;
+      new_authority->first_child = NULL;
+      new_authority->children = NULL;
+      _cogl_material_foreach_child (material,
+                                    reparent_strong_children_cb,
                                     new_authority);
 
       /* If the new_authority has any
@@ -1184,9 +1251,9 @@ _cogl_material_pre_change_notify (CoglMaterial     *material,
         }
     }
 
-  /* At this point we know we have a material with no dependants so we
-   * are now free to modify the material. */
-  g_assert (!material->has_children);
+  /* At this point we know we have a material with no strong
+   * dependants (though we may have some weak children) so we are now
+   * free to modify the material. */
 
   material->age++;