From 16e9794318b9fe13126b0b19c77a54c4714262a2 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 13 Sep 2010 23:52:18 +0100 Subject: [PATCH] material-arbfp: fixes for how we track private state This fixes a number of issues relating to how we track the arbfp private state associated with CoglMaterials. At the same time it adds much more extensive code documentation to try and make it a bit more approachable. --- clutter/cogl/cogl/cogl-material-arbfp.c | 175 +++++++++++++++++++----- 1 file changed, 143 insertions(+), 32 deletions(-) diff --git a/clutter/cogl/cogl/cogl-material-arbfp.c b/clutter/cogl/cogl/cogl-material-arbfp.c index e64234816..3f2ed8062 100644 --- a/clutter/cogl/cogl/cogl-material-arbfp.c +++ b/clutter/cogl/cogl/cogl-material-arbfp.c @@ -81,9 +81,22 @@ typedef struct _UnitState typedef struct _CoglMaterialBackendARBfpPrivate { + /* The private state is either used to link to another material + * called the "arbfp-authority" or it directly tracks the + * authoritative arbfp private state for a material. */ + + /* These are used for linking to another material as the arbfp + * authority. If authoritative state is provided by a priv instance + * then the authority_cache will simply point back to the same + * material. + * + * It will be NULL when the cache is invalid and + * find_arbfp_authority() will need to be called to search for the + * authority. */ CoglMaterial *authority_cache; unsigned long authority_cache_age; + /* These are used to provide authoritative arbfp state */ CoglHandle user_program; GString *source; GLuint gl_program; @@ -243,7 +256,7 @@ find_arbfp_authority (CoglMaterial *material, CoglHandle user_program) } static void -invalidate_arbfp_authority_cache (CoglMaterial *material) +break_arbfp_authority_link (CoglMaterial *material) { if (material->backend_priv_set_mask & COGL_MATERIAL_BACKEND_ARBFP_MASK) { @@ -254,6 +267,23 @@ invalidate_arbfp_authority_cache (CoglMaterial *material) } } +static void +free_authoritative_state (CoglMaterialBackendARBfpPrivate *priv) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + priv->user_program = COGL_INVALID_HANDLE; + + if (priv->gl_program) + { + GE (glDeletePrograms (1, &priv->gl_program)); + priv->gl_program = 0; + } + + g_free (priv->unit_state); + priv->unit_state = NULL; +} + static gboolean _cogl_material_backend_arbfp_start (CoglMaterial *material, int n_layers, @@ -287,9 +317,8 @@ _cogl_material_backend_arbfp_start (CoglMaterial *material, * * The arbfp-authority is the oldest ancestor whos state will result in * the same program being generated. - */ - - /* Note: we allocate ARBfp private state for both the given material + * + * Note: we allocate ARBfp private state for both the given material * and the arbfp-authority. The former will simply cache a pointer * to the authority and the later will track the arbfp program that * we will generate. @@ -313,16 +342,38 @@ _cogl_material_backend_arbfp_start (CoglMaterial *material, /* If the given material has changed since we last cached its * arbfp-authority then invalidate our cache and then search the * material's ancestors for one with matching fragment processing - * state. */ + * state. + * + * Note: there are multiple ways a arbfp-authority link may be + * broken; 1. the authority's age changes (handled here), 2. + * a material that defers to an authority is reparented will + * immediatly break any authority link and 3. when a material + * is modified that defers to an authority it will immediatly + * break any authority link. See: + * _cogl_material_backend_arbfp_material_set_parent_notify and + * _cogl_material_backend_arbfp_material_pre_change_notify + */ if (priv->authority_cache && - priv->authority_cache_age != _cogl_material_get_age (material)) - invalidate_arbfp_authority_cache (material); + priv->authority_cache_age != + _cogl_material_get_age (priv->authority_cache)) + break_arbfp_authority_link (material); + /* If the authority cache is invalid then we have to walkt through + * the materials ancestors to try and find a suitable + * arbfp-authority... */ if (!priv->authority_cache) { priv->authority_cache = find_arbfp_authority (material, user_program); - priv->authority_cache_age = _cogl_material_get_age (material); + priv->authority_cache_age = + _cogl_material_get_age (priv->authority_cache); + + /* A priv either links to an authority or provides authoritative + * state, but never both, so if the authority_cache doesn't + * point back to the current material we need to free any + * authoritative state... */ + if (priv->authority_cache != material) + free_authoritative_state (priv); } /* Now we have our arbfp-authority fetch the ARBfp backend private @@ -334,6 +385,12 @@ _cogl_material_backend_arbfp_start (CoglMaterial *material, authority->backend_privs[COGL_MATERIAL_BACKEND_ARBFP] = g_slice_new0 (CoglMaterialBackendARBfpPrivate); authority->backend_priv_set_mask |= COGL_MATERIAL_BACKEND_ARBFP_MASK; + + /* It's implied that an authority for the current material would + * also be its own authority... */ + authority_priv = authority->backend_privs[COGL_MATERIAL_BACKEND_ARBFP]; + authority_priv->authority_cache = authority; + authority_priv->authority_cache_age = priv->authority_cache_age; } authority_priv = authority->backend_privs[COGL_MATERIAL_BACKEND_ARBFP]; @@ -1019,6 +1076,49 @@ _cogl_material_backend_arbfp_end (CoglMaterial *material, return TRUE; } +static void +dirty_fragment_state (CoglMaterial *material, + CoglMaterialBackendARBfpPrivate *priv) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + g_return_if_fail (material->backend_priv_set_mask & + COGL_MATERIAL_BACKEND_ARBFP_MASK); + + priv = material->backend_privs[COGL_MATERIAL_BACKEND_ARBFP]; + + /* If we are currently deferring to another material as + * the arbfp authority then break our link with it, otherwise + * delete any authoritative state. */ + if (priv->authority_cache != material) + break_arbfp_authority_link (material); + else + free_authoritative_state (priv); +} + +static CoglMaterialBackendARBfpPrivate * +get_arbfp_authority_priv (CoglMaterial *material) +{ + CoglMaterialBackendARBfpPrivate *priv; + CoglMaterial *authority; + + if (!(material->backend_priv_set_mask & COGL_MATERIAL_BACKEND_ARBFP_MASK)) + return NULL; + + priv = material->backend_privs[COGL_MATERIAL_BACKEND_ARBFP]; + authority = priv->authority_cache; + if (!authority) + return NULL; + + if (_cogl_material_get_age (authority) != priv->authority_cache_age) + { + break_arbfp_authority_link (material); + return NULL; + } + + return authority->backend_privs[COGL_MATERIAL_BACKEND_ARBFP]; +} + static void _cogl_material_backend_arbfp_material_pre_change_notify ( CoglMaterial *material, @@ -1033,30 +1133,22 @@ _cogl_material_backend_arbfp_material_pre_change_notify ( _COGL_GET_CONTEXT (ctx, NO_RETVAL); - if (!(material->backend_priv_set_mask & COGL_MATERIAL_BACKEND_ARBFP_MASK) || - !(change & fragment_op_changes)) + if (!(change & fragment_op_changes)) return; - priv = material->backend_privs[COGL_MATERIAL_BACKEND_ARBFP]; + priv = get_arbfp_authority_priv (material); + if (!priv) + return; - priv->user_program = COGL_INVALID_HANDLE; - - if (priv->gl_program) - { - GE (glDeletePrograms (1, &priv->gl_program)); - priv->gl_program = 0; - } - - g_free (priv->unit_state); - priv->unit_state = NULL; + dirty_fragment_state (material, priv); } static gboolean -invalidate_arbfp_authority_cache_cb (CoglMaterialNode *node, - void *user_data) +break_arbfp_authority_link_cb (CoglMaterialNode *node, + void *user_data) { CoglMaterial *material = COGL_MATERIAL (node); - invalidate_arbfp_authority_cache (material); + break_arbfp_authority_link (material); return TRUE; } @@ -1064,12 +1156,33 @@ static void _cogl_material_backend_arbfp_material_set_parent_notify ( CoglMaterial *material) { - /* Any arbfp authority cache associated with this material or - * any of its descendants will now be invalid. */ - invalidate_arbfp_authority_cache (material); - + /* There are two aspects to a material being reparented that we need + * to consider: + * + * 1) the material could be an arbfp authority so we need to make + * sure that other materials defering to it for its arbfp state + * should break their link. + * + * 2) the material could be defering to another material for its + * arbfp state but the authority is no longer an ancestor, so we + * also need to break the link. If the material that's be + * re-parented has children then they may all also be affected in + * the same way. + * + * 1 should be handle by the material-age mechanism. I.e. when + * we next come to reference the cache we will see that the + * authority material has changed so we will re-evaluate what + * other material we can defer to as the arbfp authority. + * XXX: Actually the age is of the material that owns the cache + * not of the material referenced in the cache! Double check how + * this case is handled!! + * + * 2 can be dealt with now by NULLing any authority cache associated + * with the material, and doing the same for any children. + */ + break_arbfp_authority_link (material); _cogl_material_node_foreach_child (COGL_MATERIAL_NODE (material), - invalidate_arbfp_authority_cache_cb, + break_arbfp_authority_link_cb, NULL); } @@ -1103,9 +1216,7 @@ _cogl_material_backend_arbfp_free_priv (CoglMaterial *material) CoglMaterialBackendARBfpPrivate *priv = material->backend_privs[COGL_MATERIAL_BACKEND_ARBFP]; - glDeletePrograms (1, &priv->gl_program); - g_free (priv->unit_state); - priv->unit_state = NULL; + free_authoritative_state (priv); g_slice_free (CoglMaterialBackendARBfpPrivate, priv); material->backend_priv_set_mask &= ~COGL_MATERIAL_BACKEND_ARBFP_MASK; }