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.
This commit is contained in:
parent
2b5bb06bed
commit
72029e14db
@ -69,6 +69,8 @@ struct _CoglRectangleMap
|
|||||||
|
|
||||||
unsigned int n_rectangles;
|
unsigned int n_rectangles;
|
||||||
|
|
||||||
|
unsigned int space_remaining;
|
||||||
|
|
||||||
GDestroyNotify value_destroy_func;
|
GDestroyNotify value_destroy_func;
|
||||||
|
|
||||||
/* Stack used for walking the structure. This is only used during
|
/* Stack used for walking the structure. This is only used during
|
||||||
@ -83,7 +85,7 @@ struct _CoglRectangleMapNode
|
|||||||
|
|
||||||
CoglRectangleMapEntry rectangle;
|
CoglRectangleMapEntry rectangle;
|
||||||
|
|
||||||
unsigned int space_remaining;
|
unsigned int largest_gap;
|
||||||
|
|
||||||
CoglRectangleMapNode *parent;
|
CoglRectangleMapNode *parent;
|
||||||
|
|
||||||
@ -136,11 +138,12 @@ _cogl_rectangle_map_new (unsigned int width,
|
|||||||
root->rectangle.y = 0;
|
root->rectangle.y = 0;
|
||||||
root->rectangle.width = width;
|
root->rectangle.width = width;
|
||||||
root->rectangle.height = height;
|
root->rectangle.height = height;
|
||||||
root->space_remaining = width * height;
|
root->largest_gap = width * height;
|
||||||
|
|
||||||
map->root = root;
|
map->root = root;
|
||||||
map->n_rectangles = 0;
|
map->n_rectangles = 0;
|
||||||
map->value_destroy_func = value_destroy_func;
|
map->value_destroy_func = value_destroy_func;
|
||||||
|
map->space_remaining = width * height;
|
||||||
|
|
||||||
map->stack = g_array_new (FALSE, FALSE, sizeof (CoglRectangleMapStackEntry));
|
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.y = node->rectangle.y;
|
||||||
left_node->rectangle.width = left_width;
|
left_node->rectangle.width = left_width;
|
||||||
left_node->rectangle.height = node->rectangle.height;
|
left_node->rectangle.height = node->rectangle.height;
|
||||||
left_node->space_remaining = (left_node->rectangle.width *
|
left_node->largest_gap = (left_node->rectangle.width *
|
||||||
left_node->rectangle.height);
|
left_node->rectangle.height);
|
||||||
node->d.branch.left = left_node;
|
node->d.branch.left = left_node;
|
||||||
|
|
||||||
right_node = _cogl_rectangle_map_node_new ();
|
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.y = node->rectangle.y;
|
||||||
right_node->rectangle.width = node->rectangle.width - left_width;
|
right_node->rectangle.width = node->rectangle.width - left_width;
|
||||||
right_node->rectangle.height = node->rectangle.height;
|
right_node->rectangle.height = node->rectangle.height;
|
||||||
right_node->space_remaining = (right_node->rectangle.width *
|
right_node->largest_gap = (right_node->rectangle.width *
|
||||||
right_node->rectangle.height);
|
right_node->rectangle.height);
|
||||||
node->d.branch.right = right_node;
|
node->d.branch.right = right_node;
|
||||||
|
|
||||||
node->type = COGL_RECTANGLE_MAP_BRANCH;
|
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.y = node->rectangle.y;
|
||||||
top_node->rectangle.width = node->rectangle.width;
|
top_node->rectangle.width = node->rectangle.width;
|
||||||
top_node->rectangle.height = top_height;
|
top_node->rectangle.height = top_height;
|
||||||
top_node->space_remaining = (top_node->rectangle.width *
|
top_node->largest_gap = (top_node->rectangle.width *
|
||||||
top_node->rectangle.height);
|
top_node->rectangle.height);
|
||||||
node->d.branch.left = top_node;
|
node->d.branch.left = top_node;
|
||||||
|
|
||||||
bottom_node = _cogl_rectangle_map_node_new ();
|
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.y = node->rectangle.y + top_height;
|
||||||
bottom_node->rectangle.width = node->rectangle.width;
|
bottom_node->rectangle.width = node->rectangle.width;
|
||||||
bottom_node->rectangle.height = node->rectangle.height - top_height;
|
bottom_node->rectangle.height = node->rectangle.height - top_height;
|
||||||
bottom_node->space_remaining = (bottom_node->rectangle.width *
|
bottom_node->largest_gap = (bottom_node->rectangle.width *
|
||||||
bottom_node->rectangle.height);
|
bottom_node->rectangle.height);
|
||||||
node->d.branch.right = bottom_node;
|
node->d.branch.right = bottom_node;
|
||||||
|
|
||||||
node->type = COGL_RECTANGLE_MAP_BRANCH;
|
node->type = COGL_RECTANGLE_MAP_BRANCH;
|
||||||
@ -266,8 +269,8 @@ static unsigned int
|
|||||||
_cogl_rectangle_map_verify_recursive (CoglRectangleMapNode *node)
|
_cogl_rectangle_map_verify_recursive (CoglRectangleMapNode *node)
|
||||||
{
|
{
|
||||||
/* This is just used for debugging the data structure. It
|
/* This is just used for debugging the data structure. It
|
||||||
recursively walks the tree to verify that the remaining space
|
recursively walks the tree to verify that the largest gap values
|
||||||
values all add up */
|
all add up */
|
||||||
|
|
||||||
switch (node->type)
|
switch (node->type)
|
||||||
{
|
{
|
||||||
@ -276,32 +279,63 @@ _cogl_rectangle_map_verify_recursive (CoglRectangleMapNode *node)
|
|||||||
int sum =
|
int sum =
|
||||||
_cogl_rectangle_map_verify_recursive (node->d.branch.left) +
|
_cogl_rectangle_map_verify_recursive (node->d.branch.left) +
|
||||||
_cogl_rectangle_map_verify_recursive (node->d.branch.right);
|
_cogl_rectangle_map_verify_recursive (node->d.branch.right);
|
||||||
g_assert (node->space_remaining ==
|
g_assert (node->largest_gap ==
|
||||||
node->d.branch.left->space_remaining +
|
MAX (node->d.branch.left->largest_gap,
|
||||||
node->d.branch.right->space_remaining);
|
node->d.branch.right->largest_gap));
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
case COGL_RECTANGLE_MAP_EMPTY_LEAF:
|
case COGL_RECTANGLE_MAP_EMPTY_LEAF:
|
||||||
g_assert (node->space_remaining ==
|
g_assert (node->largest_gap ==
|
||||||
node->rectangle.width * node->rectangle.height);
|
node->rectangle.width * node->rectangle.height);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case COGL_RECTANGLE_MAP_FILLED_LEAF:
|
case COGL_RECTANGLE_MAP_FILLED_LEAF:
|
||||||
g_assert (node->space_remaining == 0);
|
g_assert (node->largest_gap == 0);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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
|
static void
|
||||||
_cogl_rectangle_map_verify (CoglRectangleMap *map)
|
_cogl_rectangle_map_verify (CoglRectangleMap *map)
|
||||||
{
|
{
|
||||||
unsigned int actual_n_rectangles =
|
unsigned int actual_n_rectangles =
|
||||||
_cogl_rectangle_map_verify_recursive (map->root);
|
_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_n_rectangles, ==, map->n_rectangles);
|
||||||
|
g_assert_cmpuint (actual_space_remaining, ==, map->space_remaining);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* COGL_ENABLE_DEBUG */
|
#endif /* COGL_ENABLE_DEBUG */
|
||||||
@ -344,7 +378,7 @@ _cogl_rectangle_map_add (CoglRectangleMap *map,
|
|||||||
it */
|
it */
|
||||||
if (node->rectangle.width >= width &&
|
if (node->rectangle.width >= width &&
|
||||||
node->rectangle.height >= height &&
|
node->rectangle.height >= height &&
|
||||||
node->space_remaining >= rectangle_size)
|
node->largest_gap >= rectangle_size)
|
||||||
{
|
{
|
||||||
if (node->type == COGL_RECTANGLE_MAP_EMPTY_LEAF)
|
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->type = COGL_RECTANGLE_MAP_FILLED_LEAF;
|
||||||
found_node->d.data = data;
|
found_node->d.data = data;
|
||||||
found_node->space_remaining = 0;
|
found_node->largest_gap = 0;
|
||||||
if (rectangle)
|
if (rectangle)
|
||||||
*rectangle = found_node->rectangle;
|
*rectangle = found_node->rectangle;
|
||||||
|
|
||||||
/* Walk back up the tree and reduce the amount of space
|
/* Walk back up the tree and update the stored largest gap for
|
||||||
remaining in each rectangle containing this node */
|
the node's sub tree */
|
||||||
for (node = found_node->parent; node; node = node->parent)
|
for (node = found_node->parent; node; node = node->parent)
|
||||||
{
|
{
|
||||||
/* This node is a parent so it should always be a branch */
|
/* This node is a parent so it should always be a branch */
|
||||||
g_assert (node->type == COGL_RECTANGLE_MAP_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 */
|
/* There is now an extra rectangle in the map */
|
||||||
map->n_rectangles++;
|
map->n_rectangles++;
|
||||||
|
/* and less space */
|
||||||
|
map->space_remaining -= rectangle_size;
|
||||||
|
|
||||||
#ifdef COGL_ENABLE_DEBUG
|
#ifdef COGL_ENABLE_DEBUG
|
||||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DUMP_ATLAS_IMAGE))
|
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)
|
if (map->value_destroy_func)
|
||||||
map->value_destroy_func (node->d.data);
|
map->value_destroy_func (node->d.data);
|
||||||
node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF;
|
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
|
/* Walk back up the tree combining branch nodes that have two
|
||||||
empty leaves back into a single empty leaf */
|
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.left);
|
||||||
_cogl_rectangle_map_node_free (node->d.branch.right);
|
_cogl_rectangle_map_node_free (node->d.branch.right);
|
||||||
node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF;
|
node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF;
|
||||||
node->space_remaining += rectangle_size;
|
|
||||||
|
node->largest_gap = (node->rectangle.width *
|
||||||
|
node->rectangle.height);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
@ -495,11 +534,14 @@ _cogl_rectangle_map_remove (CoglRectangleMap *map,
|
|||||||
/* Reduce the amount of space remaining in all of the parents
|
/* Reduce the amount of space remaining in all of the parents
|
||||||
further up the chain */
|
further up the chain */
|
||||||
for (; node; node = node->parent)
|
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 */
|
/* There is now one less rectangle */
|
||||||
g_assert (map->n_rectangles > 0);
|
g_assert (map->n_rectangles > 0);
|
||||||
map->n_rectangles--;
|
map->n_rectangles--;
|
||||||
|
/* and more space */
|
||||||
|
map->space_remaining += rectangle_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef COGL_ENABLE_DEBUG
|
#ifdef COGL_ENABLE_DEBUG
|
||||||
@ -528,7 +570,7 @@ _cogl_rectangle_map_get_height (CoglRectangleMap *map)
|
|||||||
unsigned int
|
unsigned int
|
||||||
_cogl_rectangle_map_get_remaining_space (CoglRectangleMap *map)
|
_cogl_rectangle_map_get_remaining_space (CoglRectangleMap *map)
|
||||||
{
|
{
|
||||||
return map->root->space_remaining;
|
return map->space_remaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int
|
unsigned int
|
||||||
|
Loading…
Reference in New Issue
Block a user