Adds an internal weak material mechanism

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.
This commit is contained in:
Robert Bragg 2010-05-27 20:04:49 +01:00
parent 9689ec5aad
commit 92e8fbfdc6
2 changed files with 123 additions and 50 deletions

View File

@ -523,6 +523,12 @@ struct _CoglMaterial
/* bitfields */ /* 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 */ /* Determines if material->big_state is valid */
unsigned int has_big_state:1; unsigned int has_big_state:1;

View File

@ -351,6 +351,7 @@ _cogl_material_init_default_material (void)
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
material->is_weak = FALSE;
material->journal_ref_count = 0; material->journal_ref_count = 0;
material->parent = NULL; material->parent = NULL;
material->backend = COGL_MATERIAL_BACKEND_UNDEFINED; material->backend = COGL_MATERIAL_BACKEND_UNDEFINED;
@ -422,6 +423,56 @@ _cogl_material_init_default_material (void)
ctx->default_material = _cogl_material_handle_new (material); 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 /* XXX: Always have an eye out for opportunities to lower the cost of
* cogl_material_copy. */ * cogl_material_copy. */
CoglHandle CoglHandle
@ -434,17 +485,12 @@ cogl_material_copy (CoglHandle handle)
material->_parent = src->_parent; material->_parent = src->_parent;
material->is_weak = FALSE;
material->journal_ref_count = 0; material->journal_ref_count = 0;
material->parent = cogl_handle_ref (src); material->parent = NULL;
if (src->has_children) _cogl_material_set_parent (material, src);
src->children = g_list_prepend (src->children, material);
else
{
src->has_children = TRUE;
src->first_child = material;
src->children = NULL;
}
material->has_children = FALSE; material->has_children = FALSE;
@ -476,6 +522,26 @@ cogl_material_copy (CoglHandle handle)
return _cogl_material_handle_new (material); 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 CoglHandle
cogl_material_new (void) cogl_material_new (void)
{ {
@ -488,33 +554,6 @@ cogl_material_new (void)
return new; return new;
} }
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 static void
_cogl_material_backend_free_priv (CoglMaterial *material) _cogl_material_backend_free_priv (CoglMaterial *material)
{ {
@ -1053,10 +1092,39 @@ _cogl_material_foreach_child (CoglMaterial *material,
} }
static gboolean static gboolean
change_parent_cb (CoglMaterial *child, check_if_strong_cb (CoglMaterial *material, void *user_data)
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; 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 * for unless we create another material to take its place first and
* make sure descendants reference this new material instead. * make sure descendants reference this new material instead.
*/ */
if (material->has_children) if (has_strong_children (material))
{ {
CoglMaterial *new_authority; CoglMaterial *new_authority;
COGL_STATIC_COUNTER (material_copy_on_write_counter, 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, _cogl_material_copy_differences (new_authority, material,
material->differences); 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 instead... */
new_authority->has_children = TRUE; new_authority->has_children = FALSE;
new_authority->first_child = material->first_child; new_authority->first_child = NULL;
new_authority->children = material->children; new_authority->children = NULL;
material->has_children = FALSE; _cogl_material_foreach_child (material,
_cogl_material_foreach_child (new_authority, reparent_strong_children_cb,
change_parent_cb,
new_authority); new_authority);
/* If the new_authority has any /* 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 /* At this point we know we have a material with no strong
* are now free to modify the material. */ * dependants (though we may have some weak children) so we are now
g_assert (!material->has_children); * free to modify the material. */
material->age++; material->age++;