Commit b2345ad0 authored by Ell's avatar Ell

buffer: use empty tiles when filling buffer with 0-valued color

In gegl_buffer_set_color_from_pixel(), if the pixel data, after
conversion to the buffer format, is fully zeroed, use clones of the
global empty tile for filling whole tiles, instead of creating a
new 0-filled tile.  This allows us to share memory with the global
empty tile, and makes uncloning the affected tiles more efficient.

Note that we deliberately don't just clear the corresponding area
of the buffer in this case, and make sure that there are actual
empty tiles in the requested area, to stay on the safe side (mostly
w.r.t. user-provided tile handlers, which may create non-zero tiles
when the underlying handler returns a NULL tile).
parent b0c68ce4
......@@ -33,6 +33,7 @@
#include "gegl-buffer.h"
#include "gegl-buffer-private.h"
#include "gegl-tile-storage.h"
#include "gegl-tile-handler-empty.h"
#include "gegl-sampler.h"
#include "gegl-tile-backend.h"
#include "gegl-buffer-iterator.h"
......@@ -2880,26 +2881,40 @@ gegl_buffer_set_color_from_pixel_tile (GeglBuffer *dst,
{
GeglTile *tile;
if (! data->tile)
if (data->tile)
{
data->tile = gegl_tile_new (dst->tile_storage->tile_size);
tile = gegl_tile_dup (data->tile);
}
else
{
gint tile_size = dst->tile_storage->tile_size;
if (gegl_memeq_zero (data->pixel, data->bpp))
{
tile = gegl_tile_handler_empty_new_tile (tile_size);
}
else
{
tile = gegl_tile_new (tile_size);
gegl_tile_lock (data->tile);
gegl_tile_lock (tile);
gegl_memset_pattern (gegl_tile_get_data (data->tile),
data->pixel,
data->bpp,
dst->tile_storage->tile_size / data->bpp);
gegl_memset_pattern (gegl_tile_get_data (tile),
data->pixel,
data->bpp,
tile_size / data->bpp);
gegl_tile_unlock (data->tile);
gegl_tile_unlock (tile);
}
}
tile = gegl_tile_dup (data->tile);
gegl_tile_handler_cache_insert (dst->tile_storage->cache, tile,
tile_x, tile_y, 0);
gegl_tile_unref (tile);
if (data->tile)
gegl_tile_unref (tile);
else
data->tile = tile;
}
static void
......
......@@ -38,55 +38,6 @@ finalize (GObject *object)
G_OBJECT_CLASS (gegl_tile_handler_empty_parent_class)->finalize (object);
}
static GeglTile *
_new_empty_tile (const gint tile_size)
{
static GeglTile *common_tile = NULL;
static const gint common_empty_size = sizeof (gdouble) * 4 * 128 * 128;
GeglTile *tile;
if (tile_size > common_empty_size)
{
/* The tile size is too big to use the shared buffer */
tile = gegl_tile_new (tile_size);
memset (gegl_tile_get_data (tile), 0x00, tile_size);
tile->is_zero_tile = TRUE;
}
else
{
if (!g_atomic_pointer_get (&common_tile) &&
g_once_init_enter (&common_tile))
{
GeglTile *allocated_tile = gegl_tile_new_bare ();
guchar *allocated_buffer = gegl_malloc (common_empty_size);
memset (allocated_buffer, 0x00, common_empty_size);
allocated_tile->data = allocated_buffer;
allocated_tile->destroy_notify = NULL;
allocated_tile->size = common_empty_size;
allocated_tile->is_zero_tile = TRUE;
allocated_tile->is_global_tile = TRUE;
/* avoid counting duplicates of the empty tile towards the total
* cache size, both since this is unnecessary, and since they may
* have different sizes, which is inconsistent with the duplicate-
* tracking cache logic.
*/
(*gegl_tile_n_cached_clones (allocated_tile))++;
g_once_init_leave (&common_tile, allocated_tile);
}
tile = gegl_tile_dup (common_tile);
tile->size = tile_size;
}
return tile;
}
static GeglTile *
get_tile (GeglTileSource *gegl_tile_source,
gint x,
......@@ -105,7 +56,7 @@ get_tile (GeglTileSource *gegl_tile_source,
if (!empty->tile)
{
gint tile_size = gegl_tile_backend_get_tile_size (empty->backend);
empty->tile = _new_empty_tile (tile_size);
empty->tile = gegl_tile_handler_empty_new_tile (tile_size);
}
tile = gegl_tile_handler_dup_tile (GEGL_TILE_HANDLER (empty),
......@@ -157,3 +108,52 @@ gegl_tile_handler_empty_new (GeglTileBackend *backend)
return (void*)empty;
}
GeglTile *
gegl_tile_handler_empty_new_tile (gint tile_size)
{
static GeglTile *common_tile = NULL;
static const gint common_empty_size = sizeof (gdouble) * 4 * 128 * 128;
GeglTile *tile;
if (tile_size > common_empty_size)
{
/* The tile size is too big to use the shared buffer */
tile = gegl_tile_new (tile_size);
memset (gegl_tile_get_data (tile), 0x00, tile_size);
tile->is_zero_tile = TRUE;
}
else
{
if (!g_atomic_pointer_get (&common_tile) &&
g_once_init_enter (&common_tile))
{
GeglTile *allocated_tile = gegl_tile_new_bare ();
guchar *allocated_buffer = gegl_malloc (common_empty_size);
memset (allocated_buffer, 0x00, common_empty_size);
allocated_tile->data = allocated_buffer;
allocated_tile->destroy_notify = NULL;
allocated_tile->size = common_empty_size;
allocated_tile->is_zero_tile = TRUE;
allocated_tile->is_global_tile = TRUE;
/* avoid counting duplicates of the empty tile towards the total
* cache size, both since this is unnecessary, and since they may
* have different sizes, which is inconsistent with the duplicate-
* tracking cache logic.
*/
(*gegl_tile_n_cached_clones (allocated_tile))++;
g_once_init_leave (&common_tile, allocated_tile);
}
tile = gegl_tile_dup (common_tile);
tile->size = tile_size;
}
return tile;
}
......@@ -58,6 +58,8 @@ GType gegl_tile_handler_empty_get_type (void) G_GNUC_CONST;
GeglTileHandler * gegl_tile_handler_empty_new (GeglTileBackend *backend);
GeglTile * gegl_tile_handler_empty_new_tile (gint tile_size);
G_END_DECLS
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment