cogl-atlas-texture: Use a single atlas for both RGB and RGBA textures

The internal format of the atlas texture is still set to the
appropriate format so Cogl will disable blending for textures that are
intended to be RGB. This should end up ignoring the alpha channel from
the texture in the atlas. This makes the code slightly easier to
maintain and should also improve the chances of batching.
This commit is contained in:
Neil Roberts 2010-01-29 12:19:42 +00:00
parent 67fc6ead78
commit 406c203b42
3 changed files with 57 additions and 103 deletions

View File

@ -242,26 +242,20 @@ _cogl_atlas_texture_remove_from_atlas (CoglAtlasTexture *atlas_tex)
{ {
if (atlas_tex->in_atlas) if (atlas_tex->in_atlas)
{ {
CoglAtlas *atlas;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
atlas = ((atlas_tex->format & COGL_A_BIT) ? cogl_atlas_remove_rectangle (ctx->atlas, &atlas_tex->rectangle);
ctx->atlas_alpha :
ctx->atlas_no_alpha);
cogl_atlas_remove_rectangle (atlas, &atlas_tex->rectangle);
COGL_NOTE (ATLAS, "Removed rectangle sized %ix%i", COGL_NOTE (ATLAS, "Removed rectangle sized %ix%i",
atlas_tex->rectangle.width, atlas_tex->rectangle.width,
atlas_tex->rectangle.height); atlas_tex->rectangle.height);
COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste",
cogl_atlas_get_width (atlas), cogl_atlas_get_width (ctx->atlas),
cogl_atlas_get_height (atlas), cogl_atlas_get_height (ctx->atlas),
cogl_atlas_get_n_rectangles (atlas), cogl_atlas_get_n_rectangles (ctx->atlas),
cogl_atlas_get_remaining_space (atlas) * 100 / cogl_atlas_get_remaining_space (ctx->atlas) * 100 /
(cogl_atlas_get_width (atlas) * (cogl_atlas_get_width (ctx->atlas) *
cogl_atlas_get_height (atlas))); cogl_atlas_get_height (ctx->atlas)));
atlas_tex->in_atlas = FALSE; atlas_tex->in_atlas = FALSE;
} }
@ -354,7 +348,6 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex)
/* Make sure this texture is not in the atlas */ /* Make sure this texture is not in the atlas */
if (atlas_tex->in_atlas) if (atlas_tex->in_atlas)
{ {
CoglHandle atlas_texture;
CoglAtlasTextureBlitData blit_data; CoglAtlasTextureBlitData blit_data;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
@ -363,11 +356,6 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex)
cogl_handle_unref (atlas_tex->sub_texture); cogl_handle_unref (atlas_tex->sub_texture);
if ((atlas_tex->format & COGL_A_BIT))
atlas_texture = ctx->atlas_alpha_texture;
else
atlas_texture = ctx->atlas_no_alpha_texture;
/* Create a new texture at the right size, not including the /* Create a new texture at the right size, not including the
border */ border */
atlas_tex->sub_texture = atlas_tex->sub_texture =
@ -380,7 +368,7 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex)
aren't available this will end up having to copy the entire aren't available this will end up having to copy the entire
atlas texture */ atlas texture */
_cogl_atlas_texture_blit_begin (&blit_data, atlas_tex->sub_texture, _cogl_atlas_texture_blit_begin (&blit_data, atlas_tex->sub_texture,
atlas_texture); ctx->atlas_texture);
_cogl_atlas_texture_blit (&blit_data, _cogl_atlas_texture_blit (&blit_data,
atlas_tex->rectangle.x + 1, atlas_tex->rectangle.x + 1,
atlas_tex->rectangle.y + 1, atlas_tex->rectangle.y + 1,
@ -433,15 +421,10 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex,
unsigned int rowstride, unsigned int rowstride,
const guint8 *data) const guint8 *data)
{ {
CoglHandle big_texture;
_COGL_GET_CONTEXT (ctx, FALSE); _COGL_GET_CONTEXT (ctx, FALSE);
big_texture = ((atlas_tex->format & COGL_A_BIT) ?
ctx->atlas_alpha_texture : ctx->atlas_no_alpha_texture);
/* Copy the central data */ /* Copy the central data */
if (!cogl_texture_set_region (big_texture, if (!cogl_texture_set_region (ctx->atlas_texture,
src_x, src_y, src_x, src_y,
dst_x + atlas_tex->rectangle.x + 1, dst_x + atlas_tex->rectangle.x + 1,
dst_y + atlas_tex->rectangle.y + 1, dst_y + atlas_tex->rectangle.y + 1,
@ -455,7 +438,7 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex,
/* Update the left edge pixels */ /* Update the left edge pixels */
if (dst_x == 0 && if (dst_x == 0 &&
!cogl_texture_set_region (big_texture, !cogl_texture_set_region (ctx->atlas_texture,
src_x, src_y, src_x, src_y,
atlas_tex->rectangle.x, atlas_tex->rectangle.x,
dst_y + atlas_tex->rectangle.y + 1, dst_y + atlas_tex->rectangle.y + 1,
@ -466,7 +449,7 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex,
return FALSE; return FALSE;
/* Update the right edge pixels */ /* Update the right edge pixels */
if (dst_x + dst_width == atlas_tex->rectangle.width - 2 && if (dst_x + dst_width == atlas_tex->rectangle.width - 2 &&
!cogl_texture_set_region (big_texture, !cogl_texture_set_region (ctx->atlas_texture,
src_x + dst_width - 1, src_y, src_x + dst_width - 1, src_y,
atlas_tex->rectangle.x + atlas_tex->rectangle.x +
atlas_tex->rectangle.width - 1, atlas_tex->rectangle.width - 1,
@ -478,7 +461,7 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex,
return FALSE; return FALSE;
/* Update the top edge pixels */ /* Update the top edge pixels */
if (dst_y == 0 && if (dst_y == 0 &&
!cogl_texture_set_region (big_texture, !cogl_texture_set_region (ctx->atlas_texture,
src_x, src_y, src_x, src_y,
dst_x + atlas_tex->rectangle.x + 1, dst_x + atlas_tex->rectangle.x + 1,
atlas_tex->rectangle.y, atlas_tex->rectangle.y,
@ -489,7 +472,7 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex,
return FALSE; return FALSE;
/* Update the bottom edge pixels */ /* Update the bottom edge pixels */
if (dst_y + dst_height == atlas_tex->rectangle.height - 2 && if (dst_y + dst_height == atlas_tex->rectangle.height - 2 &&
!cogl_texture_set_region (big_texture, !cogl_texture_set_region (ctx->atlas_texture,
src_x, src_y + dst_height - 1, src_x, src_y + dst_height - 1,
dst_x + atlas_tex->rectangle.x + 1, dst_x + atlas_tex->rectangle.x + 1,
atlas_tex->rectangle.y + atlas_tex->rectangle.y +
@ -654,7 +637,7 @@ _cogl_atlas_texture_get_height (CoglTexture *tex)
} }
static CoglHandle static CoglHandle
_cogl_atlas_texture_create_sub_texture (CoglHandle full_texture, _cogl_atlas_texture_create_sub_texture (CoglHandle full_texture,
const CoglAtlasRectangle *rectangle) const CoglAtlasRectangle *rectangle)
{ {
/* Create a subtexture for the given rectangle not including the /* Create a subtexture for the given rectangle not including the
@ -803,10 +786,7 @@ _cogl_atlas_texture_compare_size_cb (const void *a,
} }
static gboolean static gboolean
_cogl_atlas_texture_reserve_space (CoglPixelFormat format, _cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex,
CoglAtlas **atlas_ptr,
CoglHandle *atlas_tex_ptr,
CoglAtlasTexture *new_sub_tex,
guint width, guint width,
guint height) guint height)
{ {
@ -819,30 +799,31 @@ _cogl_atlas_texture_reserve_space (CoglPixelFormat format,
_COGL_GET_CONTEXT (ctx, FALSE); _COGL_GET_CONTEXT (ctx, FALSE);
/* Check if we can fit the rectangle into the existing atlas */ /* Check if we can fit the rectangle into the existing atlas */
if (*atlas_ptr && cogl_atlas_add_rectangle (*atlas_ptr, width, height, if (ctx->atlas && cogl_atlas_add_rectangle (ctx->atlas, width, height,
new_sub_tex, new_sub_tex,
&new_sub_tex->rectangle)) &new_sub_tex->rectangle))
{ {
COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste",
cogl_atlas_get_width (*atlas_ptr), cogl_atlas_get_width (ctx->atlas),
cogl_atlas_get_height (*atlas_ptr), cogl_atlas_get_height (ctx->atlas),
cogl_atlas_get_n_rectangles (*atlas_ptr), cogl_atlas_get_n_rectangles (ctx->atlas),
cogl_atlas_get_remaining_space (*atlas_ptr) * 100 / cogl_atlas_get_remaining_space (ctx->atlas) * 100 /
(cogl_atlas_get_width (*atlas_ptr) * (cogl_atlas_get_width (ctx->atlas) *
cogl_atlas_get_height (*atlas_ptr))); cogl_atlas_get_height (ctx->atlas)));
return TRUE; return TRUE;
} }
/* We need to reorganise the atlas so we'll get an array of all the /* We need to reorganise the atlas so we'll get an array of all the
textures currently in the atlas. */ textures currently in the atlas. */
data.n_textures = 0; data.n_textures = 0;
if (*atlas_ptr == NULL) if (ctx->atlas == NULL)
data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData)); data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData));
else else
{ {
data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData) * data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData) *
(cogl_atlas_get_n_rectangles (*atlas_ptr) + 1)); (cogl_atlas_get_n_rectangles (ctx->atlas) + 1));
cogl_atlas_foreach (*atlas_ptr, _cogl_atlas_texture_get_rectangles_cb, cogl_atlas_foreach (ctx->atlas, _cogl_atlas_texture_get_rectangles_cb,
&data); &data);
} }
@ -858,15 +839,15 @@ _cogl_atlas_texture_reserve_space (CoglPixelFormat format,
_cogl_atlas_texture_compare_size_cb); _cogl_atlas_texture_compare_size_cb);
/* Try to create a new atlas that can contain all of the textures */ /* Try to create a new atlas that can contain all of the textures */
if (*atlas_ptr) if (ctx->atlas)
{ {
atlas_width = cogl_atlas_get_width (*atlas_ptr); atlas_width = cogl_atlas_get_width (ctx->atlas);
atlas_height = cogl_atlas_get_height (*atlas_ptr); atlas_height = cogl_atlas_get_height (ctx->atlas);
/* If there is enough space in the existing for the new /* If there is enough space in the existing for the new
rectangle in the existing atlas we'll start with the same rectangle in the existing atlas we'll start with the same
size, otherwise we'll immediately double it */ size, otherwise we'll immediately double it */
if (cogl_atlas_get_remaining_space (*atlas_ptr) < width * height) if (cogl_atlas_get_remaining_space (ctx->atlas) < width * height)
_cogl_atlas_texture_get_next_size (&atlas_width, &atlas_height); _cogl_atlas_texture_get_next_size (&atlas_width, &atlas_height);
} }
else else
@ -892,46 +873,46 @@ _cogl_atlas_texture_reserve_space (CoglPixelFormat format,
_cogl_texture_2d_new_with_size (cogl_atlas_get_width (new_atlas), _cogl_texture_2d_new_with_size (cogl_atlas_get_width (new_atlas),
cogl_atlas_get_height (new_atlas), cogl_atlas_get_height (new_atlas),
COGL_TEXTURE_NONE, COGL_TEXTURE_NONE,
format); COGL_PIXEL_FORMAT_RGBA_8888);
COGL_NOTE (ATLAS, COGL_NOTE (ATLAS,
"Atlas %s with size %ix%i", "Atlas %s with size %ix%i",
*atlas_ptr == NULL || ctx->atlas == NULL ||
cogl_atlas_get_width (*atlas_ptr) != cogl_atlas_get_width (ctx->atlas) !=
cogl_atlas_get_width (new_atlas) || cogl_atlas_get_width (new_atlas) ||
cogl_atlas_get_height (*atlas_ptr) != cogl_atlas_get_height (ctx->atlas) !=
cogl_atlas_get_height (new_atlas) ? cogl_atlas_get_height (new_atlas) ?
"resized" : "reorganized", "resized" : "reorganized",
cogl_atlas_get_width (new_atlas), cogl_atlas_get_width (new_atlas),
cogl_atlas_get_height (new_atlas)); cogl_atlas_get_height (new_atlas));
if (*atlas_ptr) if (ctx->atlas)
{ {
/* Move all the textures to the right position in the new /* Move all the textures to the right position in the new
texture. This will also update the texture's rectangle */ texture. This will also update the texture's rectangle */
_cogl_atlas_texture_migrate (data.n_textures, _cogl_atlas_texture_migrate (data.n_textures,
data.textures, data.textures,
*atlas_tex_ptr, ctx->atlas_texture,
new_tex, new_tex,
new_sub_tex); new_sub_tex);
cogl_atlas_free (*atlas_ptr); cogl_atlas_free (ctx->atlas);
cogl_handle_unref (*atlas_tex_ptr); cogl_handle_unref (ctx->atlas_texture);
} }
else else
/* We know there's only one texture so we can just directly /* We know there's only one texture so we can just directly
update the rectangle from its new position */ update the rectangle from its new position */
data.textures[0].texture->rectangle = data.textures[0].new_position; data.textures[0].texture->rectangle = data.textures[0].new_position;
*atlas_ptr = new_atlas; ctx->atlas = new_atlas;
*atlas_tex_ptr = new_tex; ctx->atlas_texture = new_tex;
COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste",
cogl_atlas_get_width (*atlas_ptr), cogl_atlas_get_width (ctx->atlas),
cogl_atlas_get_height (*atlas_ptr), cogl_atlas_get_height (ctx->atlas),
cogl_atlas_get_n_rectangles (*atlas_ptr), cogl_atlas_get_n_rectangles (ctx->atlas),
cogl_atlas_get_remaining_space (*atlas_ptr) * 100 / cogl_atlas_get_remaining_space (ctx->atlas) * 100 /
(cogl_atlas_get_width (*atlas_ptr) * (cogl_atlas_get_width (ctx->atlas) *
cogl_atlas_get_height (*atlas_ptr))); cogl_atlas_get_height (ctx->atlas)));
ret = TRUE; ret = TRUE;
} }
@ -949,8 +930,6 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle,
CoglAtlasTexture *atlas_tex; CoglAtlasTexture *atlas_tex;
CoglBitmap *bmp = (CoglBitmap *) bmp_handle; CoglBitmap *bmp = (CoglBitmap *) bmp_handle;
CoglTextureUploadData upload_data; CoglTextureUploadData upload_data;
CoglAtlas **atlas_ptr;
CoglHandle *atlas_tex_ptr;
_COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
@ -1000,17 +979,6 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle,
return COGL_INVALID_HANDLE; return COGL_INVALID_HANDLE;
} }
if ((internal_format & COGL_A_BIT))
{
atlas_ptr = &ctx->atlas_alpha;
atlas_tex_ptr = &ctx->atlas_alpha_texture;
}
else
{
atlas_ptr = &ctx->atlas_no_alpha;
atlas_tex_ptr = &ctx->atlas_no_alpha_texture;
}
/* We need to allocate the texture now because we need the pointer /* We need to allocate the texture now because we need the pointer
to set as the data for the rectangle in the atlas */ to set as the data for the rectangle in the atlas */
atlas_tex = g_new (CoglAtlasTexture, 1); atlas_tex = g_new (CoglAtlasTexture, 1);
@ -1020,11 +988,7 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle,
atlas_tex->rectangle.height = upload_data.bitmap.height + 2; atlas_tex->rectangle.height = upload_data.bitmap.height + 2;
/* Try to make some space in the atlas for the texture */ /* Try to make some space in the atlas for the texture */
if (!_cogl_atlas_texture_reserve_space (internal_format & if (!_cogl_atlas_texture_reserve_space (atlas_tex,
~COGL_PREMULT_BIT,
atlas_ptr,
atlas_tex_ptr,
atlas_tex,
atlas_tex->rectangle.width, atlas_tex->rectangle.width,
atlas_tex->rectangle.height)) atlas_tex->rectangle.height))
{ {
@ -1035,7 +999,7 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle,
if (!_cogl_texture_upload_data_convert (&upload_data, internal_format)) if (!_cogl_texture_upload_data_convert (&upload_data, internal_format))
{ {
cogl_atlas_remove_rectangle (*atlas_ptr, &atlas_tex->rectangle); cogl_atlas_remove_rectangle (ctx->atlas, &atlas_tex->rectangle);
g_free (atlas_tex); g_free (atlas_tex);
_cogl_texture_upload_data_free (&upload_data); _cogl_texture_upload_data_free (&upload_data);
return COGL_INVALID_HANDLE; return COGL_INVALID_HANDLE;
@ -1045,7 +1009,7 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle,
atlas_tex->format = internal_format; atlas_tex->format = internal_format;
atlas_tex->in_atlas = TRUE; atlas_tex->in_atlas = TRUE;
atlas_tex->sub_texture = atlas_tex->sub_texture =
_cogl_atlas_texture_create_sub_texture (*atlas_tex_ptr, _cogl_atlas_texture_create_sub_texture (ctx->atlas_texture,
&atlas_tex->rectangle); &atlas_tex->rectangle);
/* Defer to set_region so that we can share the code for copying the /* Defer to set_region so that we can share the code for copying the

View File

@ -147,10 +147,8 @@ cogl_create_context (void)
cogl_enable (enable_flags); cogl_enable (enable_flags);
_cogl_flush_face_winding (); _cogl_flush_face_winding ();
_context->atlas_alpha = NULL; _context->atlas = NULL;
_context->atlas_no_alpha = NULL; _context->atlas_texture = COGL_INVALID_HANDLE;
_context->atlas_alpha_texture = COGL_INVALID_HANDLE;
_context->atlas_no_alpha_texture = COGL_INVALID_HANDLE;
return TRUE; return TRUE;
} }
@ -193,14 +191,10 @@ _cogl_destroy_context ()
if (_context->default_material) if (_context->default_material)
cogl_handle_unref (_context->default_material); cogl_handle_unref (_context->default_material);
if (_context->atlas_alpha) if (_context->atlas)
cogl_atlas_free (_context->atlas_alpha); cogl_atlas_free (_context->atlas);
if (_context->atlas_no_alpha) if (_context->atlas_texture)
cogl_atlas_free (_context->atlas_no_alpha); cogl_handle_unref (_context->atlas_texture);
if (_context->atlas_alpha_texture)
cogl_handle_unref (_context->atlas_alpha_texture);
if (_context->atlas_no_alpha_texture)
cogl_handle_unref (_context->atlas_no_alpha_texture);
g_free (_context); g_free (_context);
} }

View File

@ -112,12 +112,8 @@ typedef struct
CoglHandle texture_download_material; CoglHandle texture_download_material;
/* Separate atlases for textures with and without alpha */ CoglAtlas *atlas;
CoglAtlas *atlas_alpha; CoglHandle atlas_texture;
CoglAtlas *atlas_no_alpha;
/* Textures for the two atlases */
CoglHandle atlas_alpha_texture;
CoglHandle atlas_no_alpha_texture;
CoglContextDriver drv; CoglContextDriver drv;
} CoglContext; } CoglContext;