From 72029e14db0c0b0f82c20cf6760ab96a81c76e46 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Wed, 11 Aug 2010 15:08:00 +0100 Subject: [PATCH] cogl-rectangle-map: Record largest gap rather than the remaining space Previously each node in the rectangle map tree would store the total remaining space in all of its children to use as an optimization when adding nodes. With this it could skip an entire branch of the tree if it knew there could never be enough space for the new node in the branch. This modifies that slightly to instead store the largest single gap. This allows it to skip a branch earlier because often there would be a lot of small gaps which would add up to enough a space for the new rectangle, but the space can't be used unless it is in a single node. The rectangle map still needs to keep track of the total remaining space for the whole map for the debugging output so this has been added back in to the CoglRectangleMap struct. There is a separate debugging function to verify this value. --- clutter/cogl/cogl/cogl-rectangle-map.c | 94 +++++++++++++++++++------- 1 file changed, 68 insertions(+), 26 deletions(-) diff --git a/clutter/cogl/cogl/cogl-rectangle-map.c b/clutter/cogl/cogl/cogl-rectangle-map.c index c5cb6ca20..fe9189ff7 100644 --- a/clutter/cogl/cogl/cogl-rectangle-map.c +++ b/clutter/cogl/cogl/cogl-rectangle-map.c @@ -69,6 +69,8 @@ struct _CoglRectangleMap unsigned int n_rectangles; + unsigned int space_remaining; + GDestroyNotify value_destroy_func; /* Stack used for walking the structure. This is only used during @@ -83,7 +85,7 @@ struct _CoglRectangleMapNode CoglRectangleMapEntry rectangle; - unsigned int space_remaining; + unsigned int largest_gap; CoglRectangleMapNode *parent; @@ -136,11 +138,12 @@ _cogl_rectangle_map_new (unsigned int width, root->rectangle.y = 0; root->rectangle.width = width; root->rectangle.height = height; - root->space_remaining = width * height; + root->largest_gap = width * height; map->root = root; map->n_rectangles = 0; map->value_destroy_func = value_destroy_func; + map->space_remaining = width * height; map->stack = g_array_new (FALSE, FALSE, sizeof (CoglRectangleMapStackEntry)); @@ -198,8 +201,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); + left_node->largest_gap = (left_node->rectangle.width * + left_node->rectangle.height); node->d.branch.left = left_node; right_node = _cogl_rectangle_map_node_new (); @@ -209,8 +212,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); + right_node->largest_gap = (right_node->rectangle.width * + right_node->rectangle.height); node->d.branch.right = right_node; node->type = COGL_RECTANGLE_MAP_BRANCH; @@ -240,8 +243,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); + top_node->largest_gap = (top_node->rectangle.width * + top_node->rectangle.height); node->d.branch.left = top_node; bottom_node = _cogl_rectangle_map_node_new (); @@ -251,8 +254,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); + bottom_node->largest_gap = (bottom_node->rectangle.width * + bottom_node->rectangle.height); node->d.branch.right = bottom_node; node->type = COGL_RECTANGLE_MAP_BRANCH; @@ -266,8 +269,8 @@ 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 */ + recursively walks the tree to verify that the largest gap values + all add up */ switch (node->type) { @@ -276,32 +279,63 @@ _cogl_rectangle_map_verify_recursive (CoglRectangleMapNode *node) 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); + g_assert (node->largest_gap == + MAX (node->d.branch.left->largest_gap, + node->d.branch.right->largest_gap)); return sum; } case COGL_RECTANGLE_MAP_EMPTY_LEAF: - g_assert (node->space_remaining == + g_assert (node->largest_gap == node->rectangle.width * node->rectangle.height); return 0; case COGL_RECTANGLE_MAP_FILLED_LEAF: - g_assert (node->space_remaining == 0); + g_assert (node->largest_gap == 0); return 1; } return 0; } +static unsigned int +_cogl_rectangle_map_get_space_remaining_recursive (CoglRectangleMapNode *node) +{ + /* This is just used for debugging the data structure. It + recursively walks the tree to verify that the remaining space + value adds up */ + + switch (node->type) + { + case COGL_RECTANGLE_MAP_BRANCH: + { + CoglRectangleMapNode *l = node->d.branch.left; + CoglRectangleMapNode *r = node->d.branch.right; + + return (_cogl_rectangle_map_get_space_remaining_recursive (l) + + _cogl_rectangle_map_get_space_remaining_recursive (r)); + } + + case COGL_RECTANGLE_MAP_EMPTY_LEAF: + return node->rectangle.width * node->rectangle.height; + + case COGL_RECTANGLE_MAP_FILLED_LEAF: + return 0; + } + + return 0; +} + static void _cogl_rectangle_map_verify (CoglRectangleMap *map) { unsigned int actual_n_rectangles = _cogl_rectangle_map_verify_recursive (map->root); + unsigned int actual_space_remaining = + _cogl_rectangle_map_get_space_remaining_recursive (map->root); g_assert_cmpuint (actual_n_rectangles, ==, map->n_rectangles); + g_assert_cmpuint (actual_space_remaining, ==, map->space_remaining); } #endif /* COGL_ENABLE_DEBUG */ @@ -344,7 +378,7 @@ _cogl_rectangle_map_add (CoglRectangleMap *map, it */ if (node->rectangle.width >= width && node->rectangle.height >= height && - node->space_remaining >= rectangle_size) + node->largest_gap >= rectangle_size) { if (node->type == COGL_RECTANGLE_MAP_EMPTY_LEAF) { @@ -399,22 +433,25 @@ _cogl_rectangle_map_add (CoglRectangleMap *map, found_node->type = COGL_RECTANGLE_MAP_FILLED_LEAF; found_node->d.data = data; - found_node->space_remaining = 0; + found_node->largest_gap = 0; if (rectangle) *rectangle = found_node->rectangle; - /* Walk back up the tree and reduce the amount of space - remaining in each rectangle containing this node */ + /* Walk back up the tree and update the stored largest gap for + the node's sub tree */ 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; + node->largest_gap = MAX (node->d.branch.left->largest_gap, + node->d.branch.right->largest_gap); } /* There is now an extra rectangle in the map */ map->n_rectangles++; + /* and less space */ + map->space_remaining -= rectangle_size; #ifdef COGL_ENABLE_DEBUG if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DUMP_ATLAS_IMAGE)) @@ -471,7 +508,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; + node->largest_gap = rectangle_size; /* Walk back up the tree combining branch nodes that have two empty leaves back into a single empty leaf */ @@ -486,7 +523,9 @@ _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; + + node->largest_gap = (node->rectangle.width * + node->rectangle.height); } else break; @@ -495,11 +534,14 @@ _cogl_rectangle_map_remove (CoglRectangleMap *map, /* 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; + node->largest_gap = MAX (node->d.branch.left->largest_gap, + node->d.branch.right->largest_gap); /* There is now one less rectangle */ g_assert (map->n_rectangles > 0); map->n_rectangles--; + /* and more space */ + map->space_remaining += rectangle_size; } #ifdef COGL_ENABLE_DEBUG @@ -528,7 +570,7 @@ _cogl_rectangle_map_get_height (CoglRectangleMap *map) unsigned int _cogl_rectangle_map_get_remaining_space (CoglRectangleMap *map) { - return map->root->space_remaining; + return map->space_remaining; } unsigned int