cogl-rectangle-map: Use a GArray for the stack

When iterating over the rectangle map a stack is used to implement a
recursive algorithm. Previously this was slice allocating a linked
list. Now it uses a GArray which is retained with the rectangle map to
avoid frequent allocations which is a little bit faster.
This commit is contained in:
Neil Roberts 2010-08-06 17:40:52 +01:00
parent 0e2d18d700
commit e080e824ee

View File

@ -70,6 +70,11 @@ struct _CoglRectangleMap
unsigned int n_rectangles; unsigned int n_rectangles;
GDestroyNotify value_destroy_func; GDestroyNotify value_destroy_func;
/* Stack used for walking the structure. This is only used during
the lifetime of a single function call but it is kept here as an
optimisation to avoid reallocating it every time it is needed */
GArray *stack;
}; };
struct _CoglRectangleMapNode struct _CoglRectangleMapNode
@ -103,8 +108,6 @@ struct _CoglRectangleMapStackEntry
/* Index of next branch of this node to explore. Basically either 0 /* Index of next branch of this node to explore. Basically either 0
to go left or 1 to go right */ to go left or 1 to go right */
gboolean next_index; gboolean next_index;
/* Next entry in the stack */
CoglRectangleMapStackEntry *next;
}; };
static CoglRectangleMapNode * static CoglRectangleMapNode *
@ -139,32 +142,38 @@ _cogl_rectangle_map_new (unsigned int width,
map->n_rectangles = 0; map->n_rectangles = 0;
map->value_destroy_func = value_destroy_func; map->value_destroy_func = value_destroy_func;
map->stack = g_array_new (FALSE, FALSE, sizeof (CoglRectangleMapStackEntry));
return map; return map;
} }
static CoglRectangleMapStackEntry * static void
_cogl_rectangle_map_stack_push (CoglRectangleMapStackEntry *stack, _cogl_rectangle_map_stack_push (GArray *stack,
CoglRectangleMapNode *node, CoglRectangleMapNode *node,
gboolean next_index) gboolean next_index)
{ {
CoglRectangleMapStackEntry *new_entry = CoglRectangleMapStackEntry *new_entry;
g_slice_new (CoglRectangleMapStackEntry);
g_array_set_size (stack, stack->len + 1);
new_entry = &g_array_index (stack, CoglRectangleMapStackEntry,
stack->len - 1);
new_entry->node = node; new_entry->node = node;
new_entry->next_index = next_index; new_entry->next_index = next_index;
new_entry->next = stack; }
return new_entry; static void
_cogl_rectangle_map_stack_pop (GArray *stack)
{
g_array_set_size (stack, stack->len - 1);
} }
static CoglRectangleMapStackEntry * static CoglRectangleMapStackEntry *
_cogl_rectangle_map_stack_pop (CoglRectangleMapStackEntry *stack) _cogl_rectangle_map_stack_get_top (GArray *stack)
{ {
CoglRectangleMapStackEntry *next = stack->next; return &g_array_index (stack, CoglRectangleMapStackEntry,
stack->len - 1);
g_slice_free (CoglRectangleMapStackEntry, stack);
return next;
} }
static CoglRectangleMapNode * static CoglRectangleMapNode *
@ -306,7 +315,7 @@ _cogl_rectangle_map_add (CoglRectangleMap *map,
{ {
unsigned int rectangle_size = width * height; unsigned int rectangle_size = width * height;
/* Stack of nodes to search in */ /* Stack of nodes to search in */
CoglRectangleMapStackEntry *node_stack; GArray *stack = map->stack;
CoglRectangleMapNode *found_node = NULL; CoglRectangleMapNode *found_node = NULL;
/* Zero-sized rectangles break the algorithm for removing rectangles /* Zero-sized rectangles break the algorithm for removing rectangles
@ -314,15 +323,21 @@ _cogl_rectangle_map_add (CoglRectangleMap *map,
g_return_val_if_fail (width > 0 && height > 0, FALSE); g_return_val_if_fail (width > 0 && height > 0, FALSE);
/* Start with the root node */ /* Start with the root node */
node_stack = _cogl_rectangle_map_stack_push (NULL, map->root, FALSE); g_array_set_size (stack, 0);
_cogl_rectangle_map_stack_push (stack, map->root, FALSE);
/* Depth-first search for an empty node that is big enough */ /* Depth-first search for an empty node that is big enough */
while (node_stack) while (stack->len > 0)
{ {
CoglRectangleMapStackEntry *stack_top;
CoglRectangleMapNode *node;
int next_index;
/* Pop an entry off the stack */ /* Pop an entry off the stack */
CoglRectangleMapNode *node = node_stack->node; stack_top = _cogl_rectangle_map_stack_get_top (stack);
int next_index = node_stack->next_index; node = stack_top->node;
node_stack = _cogl_rectangle_map_stack_pop (node_stack); next_index = stack_top->next_index;
_cogl_rectangle_map_stack_pop (stack);
/* Regardless of the type of the node, there's no point /* Regardless of the type of the node, there's no point
descending any further if the new rectangle won't fit within descending any further if the new rectangle won't fit within
@ -341,21 +356,18 @@ _cogl_rectangle_map_add (CoglRectangleMap *map,
{ {
if (next_index) if (next_index)
/* Try the right branch */ /* Try the right branch */
node_stack = _cogl_rectangle_map_stack_push (stack,
_cogl_rectangle_map_stack_push (node_stack,
node->d.branch.right, node->d.branch.right,
0); 0);
else else
{ {
/* Make sure we remember to try the right branch once /* Make sure we remember to try the right branch once
we've finished descending the left branch */ we've finished descending the left branch */
node_stack = _cogl_rectangle_map_stack_push (stack,
_cogl_rectangle_map_stack_push (node_stack,
node, node,
1); 1);
/* Try the left branch */ /* Try the left branch */
node_stack = _cogl_rectangle_map_stack_push (stack,
_cogl_rectangle_map_stack_push (node_stack,
node->d.branch.left, node->d.branch.left,
0); 0);
} }
@ -363,10 +375,6 @@ _cogl_rectangle_map_add (CoglRectangleMap *map,
} }
} }
/* Free the stack */
while (node_stack)
node_stack = _cogl_rectangle_map_stack_pop (node_stack);
if (found_node) if (found_node)
{ {
CoglRectangleMapNode *node; CoglRectangleMapNode *node;
@ -535,36 +543,39 @@ _cogl_rectangle_map_internal_foreach (CoglRectangleMap *map,
void *data) void *data)
{ {
/* Stack of nodes to search in */ /* Stack of nodes to search in */
CoglRectangleMapStackEntry *node_stack; GArray *stack = map->stack;
/* Start with the root node */ /* Start with the root node */
node_stack = _cogl_rectangle_map_stack_push (NULL, map->root, 0); g_array_set_size (stack, 0);
_cogl_rectangle_map_stack_push (stack, map->root, 0);
/* Iterate all nodes depth-first */ /* Iterate all nodes depth-first */
while (node_stack) while (stack->len > 0)
{ {
CoglRectangleMapNode *node = node_stack->node; CoglRectangleMapStackEntry *stack_top =
_cogl_rectangle_map_stack_get_top (stack);
CoglRectangleMapNode *node = stack_top->node;
switch (node->type) switch (node->type)
{ {
case COGL_RECTANGLE_MAP_BRANCH: case COGL_RECTANGLE_MAP_BRANCH:
if (node_stack->next_index == 0) if (stack_top->next_index == 0)
{ {
/* Next time we come back to this node, go to the right */ /* Next time we come back to this node, go to the right */
node_stack->next_index = 1; stack_top->next_index = 1;
/* Explore the left branch next */ /* Explore the left branch next */
node_stack = _cogl_rectangle_map_stack_push (node_stack, _cogl_rectangle_map_stack_push (stack,
node->d.branch.left, node->d.branch.left,
0); 0);
} }
else if (node_stack->next_index == 1) else if (stack_top->next_index == 1)
{ {
/* Next time we come back to this node, stop processing it */ /* Next time we come back to this node, stop processing it */
node_stack->next_index = 2; stack_top->next_index = 2;
/* Explore the right branch next */ /* Explore the right branch next */
node_stack = _cogl_rectangle_map_stack_push (node_stack, _cogl_rectangle_map_stack_push (stack,
node->d.branch.right, node->d.branch.right,
0); 0);
} }
@ -572,20 +583,20 @@ _cogl_rectangle_map_internal_foreach (CoglRectangleMap *map,
{ {
/* We're finished with this node so we can call the callback */ /* We're finished with this node so we can call the callback */
func (node, data); func (node, data);
node_stack = _cogl_rectangle_map_stack_pop (node_stack); _cogl_rectangle_map_stack_pop (stack);
} }
break; break;
default: default:
/* Some sort of leaf node, just call the callback */ /* Some sort of leaf node, just call the callback */
func (node, data); func (node, data);
node_stack = _cogl_rectangle_map_stack_pop (node_stack); _cogl_rectangle_map_stack_pop (stack);
break; break;
} }
} }
/* The stack should now be empty */ /* The stack should now be empty */
g_assert (node_stack == NULL); g_assert (stack->len == 0);
} }
typedef struct _CoglRectangleMapForeachClosure typedef struct _CoglRectangleMapForeachClosure
@ -635,6 +646,9 @@ _cogl_rectangle_map_free (CoglRectangleMap *map)
_cogl_rectangle_map_internal_foreach (map, _cogl_rectangle_map_internal_foreach (map,
_cogl_rectangle_map_free_cb, _cogl_rectangle_map_free_cb,
map); map);
g_array_free (map->stack, TRUE);
g_free (map); g_free (map);
} }