From 9a62c069aa4d748c94ea9d364738dad26466671b Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 6 Aug 2010 16:13:20 +0100 Subject: [PATCH] cogl-rectangle-map: Store the remaining space per node Previously the remaining space was managed as part of the CoglRectangleMap struct. Now it is stored per node so that at any point in the hierarchy we can quickly determine how much space is remaining in all of the node's children. That way when adding a rectangle we can miss out entire branches more quickly if we know that there is no way the new rectangle would fit in that branch. This also adds a function to recursively verify the cached state in the nodes such as the remaining space and the number of rectangles. This function is only called when the dump-atlas-image debug flag is set because it is potentially quite slow. --- cogl/cogl-rectangle-map.c | 108 +++++++++++++++++++++++++++++++++----- 1 file changed, 96 insertions(+), 12 deletions(-) diff --git a/cogl/cogl-rectangle-map.c b/cogl/cogl-rectangle-map.c index 77f1d066c..a0970d5f2 100644 --- a/cogl/cogl-rectangle-map.c +++ b/cogl/cogl-rectangle-map.c @@ -67,7 +67,6 @@ struct _CoglRectangleMap { CoglRectangleMapNode *root; - unsigned int space_remaining; unsigned int n_rectangles; GDestroyNotify value_destroy_func; @@ -79,6 +78,8 @@ struct _CoglRectangleMapNode CoglRectangleMapEntry rectangle; + unsigned int space_remaining; + CoglRectangleMapNode *parent; union @@ -132,9 +133,9 @@ _cogl_rectangle_map_new (unsigned int width, root->rectangle.y = 0; root->rectangle.width = width; root->rectangle.height = height; + root->space_remaining = width * height; map->root = root; - map->space_remaining = width * height; map->n_rectangles = 0; map->value_destroy_func = value_destroy_func; @@ -188,6 +189,8 @@ _cogl_rectangle_map_node_split_horizontally (CoglRectangleMapNode *node, left_node->rectangle.y = node->rectangle.y; left_node->rectangle.width = left_width; left_node->rectangle.height = node->rectangle.height; + left_node->space_remaining = (left_node->rectangle.width * + left_node->rectangle.height); node->d.branch.left = left_node; right_node = _cogl_rectangle_map_node_new (); @@ -197,6 +200,8 @@ _cogl_rectangle_map_node_split_horizontally (CoglRectangleMapNode *node, right_node->rectangle.y = node->rectangle.y; right_node->rectangle.width = node->rectangle.width - left_width; right_node->rectangle.height = node->rectangle.height; + right_node->space_remaining = (right_node->rectangle.width * + right_node->rectangle.height); node->d.branch.right = right_node; node->type = COGL_RECTANGLE_MAP_BRANCH; @@ -226,6 +231,8 @@ _cogl_rectangle_map_node_split_vertically (CoglRectangleMapNode *node, top_node->rectangle.y = node->rectangle.y; top_node->rectangle.width = node->rectangle.width; top_node->rectangle.height = top_height; + top_node->space_remaining = (top_node->rectangle.width * + top_node->rectangle.height); node->d.branch.left = top_node; bottom_node = _cogl_rectangle_map_node_new (); @@ -235,6 +242,8 @@ _cogl_rectangle_map_node_split_vertically (CoglRectangleMapNode *node, bottom_node->rectangle.y = node->rectangle.y + top_height; bottom_node->rectangle.width = node->rectangle.width; bottom_node->rectangle.height = node->rectangle.height - top_height; + bottom_node->space_remaining = (bottom_node->rectangle.width * + bottom_node->rectangle.height); node->d.branch.right = bottom_node; node->type = COGL_RECTANGLE_MAP_BRANCH; @@ -242,6 +251,52 @@ _cogl_rectangle_map_node_split_vertically (CoglRectangleMapNode *node, return top_node; } +#ifdef COGL_ENABLE_DEBUG + +static unsigned int +_cogl_rectangle_map_verify_recursive (CoglRectangleMapNode *node) +{ + /* This is just used for debugging the data structure. It + recursively walks the tree to verify that the remaining space + values all add up */ + + switch (node->type) + { + case COGL_RECTANGLE_MAP_BRANCH: + { + int sum = + _cogl_rectangle_map_verify_recursive (node->d.branch.left) + + _cogl_rectangle_map_verify_recursive (node->d.branch.right); + g_assert (node->space_remaining == + node->d.branch.left->space_remaining + + node->d.branch.right->space_remaining); + return sum; + } + + case COGL_RECTANGLE_MAP_EMPTY_LEAF: + g_assert (node->space_remaining == + node->rectangle.width * node->rectangle.height); + return 0; + + case COGL_RECTANGLE_MAP_FILLED_LEAF: + g_assert (node->space_remaining == 0); + return 1; + } + + return 0; +} + +static void +_cogl_rectangle_map_verify (CoglRectangleMap *map) +{ + unsigned int actual_n_rectangles = + _cogl_rectangle_map_verify_recursive (map->root); + + g_assert_cmpuint (actual_n_rectangles, ==, map->n_rectangles); +} + +#endif /* COGL_ENABLE_DEBUG */ + gboolean _cogl_rectangle_map_add (CoglRectangleMap *map, unsigned int width, @@ -249,6 +304,7 @@ _cogl_rectangle_map_add (CoglRectangleMap *map, void *data, CoglRectangleMapEntry *rectangle) { + unsigned int rectangle_size = width * height; /* Stack of nodes to search in */ CoglRectangleMapStackEntry *node_stack; CoglRectangleMapNode *found_node = NULL; @@ -272,7 +328,8 @@ _cogl_rectangle_map_add (CoglRectangleMap *map, descending any further if the new rectangle won't fit within it */ if (node->rectangle.width >= width && - node->rectangle.height >= height) + node->rectangle.height >= height && + node->space_remaining >= rectangle_size) { if (node->type == COGL_RECTANGLE_MAP_EMPTY_LEAF) { @@ -312,6 +369,8 @@ _cogl_rectangle_map_add (CoglRectangleMap *map, if (found_node) { + CoglRectangleMapNode *node; + /* Split according to whichever axis will leave us with the largest space */ if (found_node->rectangle.width - width > @@ -332,18 +391,31 @@ _cogl_rectangle_map_add (CoglRectangleMap *map, found_node->type = COGL_RECTANGLE_MAP_FILLED_LEAF; found_node->d.data = data; + found_node->space_remaining = 0; if (rectangle) *rectangle = found_node->rectangle; - /* Record how much empty space is remaining after this rectangle - is added */ - g_assert (width * height <= map->space_remaining); - map->space_remaining -= width * height; + /* Walk back up the tree and reduce the amount of space + remaining in each rectangle containing this node */ + for (node = found_node->parent; node; node = node->parent) + { + /* This node is a parent so it should always be a branch */ + g_assert (node->type == COGL_RECTANGLE_MAP_BRANCH); + + node->space_remaining -= rectangle_size; + } + + /* There is now an extra rectangle in the map */ map->n_rectangles++; #ifdef COGL_ENABLE_DEBUG if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DUMP_ATLAS_IMAGE)) - _cogl_rectangle_map_dump_image (map); + { + _cogl_rectangle_map_dump_image (map); + /* Dumping the rectangle map is really slow so we might as well + verify the space remaining here as it is also quite slow */ + _cogl_rectangle_map_verify (map); + } #endif return TRUE; @@ -357,6 +429,7 @@ _cogl_rectangle_map_remove (CoglRectangleMap *map, const CoglRectangleMapEntry *rectangle) { CoglRectangleMapNode *node = map->root; + unsigned int rectangle_size = rectangle->width * rectangle->height; /* We can do a binary-chop down the search tree to find the rectangle */ while (node->type == COGL_RECTANGLE_MAP_BRANCH) @@ -390,6 +463,7 @@ _cogl_rectangle_map_remove (CoglRectangleMap *map, if (map->value_destroy_func) map->value_destroy_func (node->d.data); node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF; + node->space_remaining += rectangle_size; /* Walk back up the tree combining branch nodes that have two empty leaves back into a single empty leaf */ @@ -404,20 +478,30 @@ _cogl_rectangle_map_remove (CoglRectangleMap *map, _cogl_rectangle_map_node_free (node->d.branch.left); _cogl_rectangle_map_node_free (node->d.branch.right); node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF; + node->space_remaining += rectangle_size; } else break; } - /* There is now more free space and one less rectangle */ - map->space_remaining += rectangle->width * rectangle->height; + /* Reduce the amount of space remaining in all of the parents + further up the chain */ + for (; node; node = node->parent) + node->space_remaining += rectangle_size; + + /* There is now one less rectangle */ g_assert (map->n_rectangles > 0); map->n_rectangles--; } #ifdef COGL_ENABLE_DEBUG if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DUMP_ATLAS_IMAGE)) - _cogl_rectangle_map_dump_image (map); + { + _cogl_rectangle_map_dump_image (map); + /* Dumping the rectangle map is really slow so we might as well + verify the space remaining here as it is also quite slow */ + _cogl_rectangle_map_verify (map); + } #endif } @@ -436,7 +520,7 @@ _cogl_rectangle_map_get_height (CoglRectangleMap *map) unsigned int _cogl_rectangle_map_get_remaining_space (CoglRectangleMap *map) { - return map->space_remaining; + return map->root->space_remaining; } unsigned int