/* This file is part of GEGL. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with GEGL; if not, see . * * Copyright 2006,2007 Øyvind Kolås */ #include "config.h" #include #include #include "gegl-buffer-backend.h" #include "gegl-tile-backend.h" #include "gegl-tile-backend-ram.h" static void dbg_alloc (int size); static void dbg_dealloc (int size); typedef struct _RamEntry RamEntry; struct _RamEntry { gint x; gint y; gint z; guchar *offset; }; static inline void ram_entry_read (GeglTileBackendRam *ram, RamEntry *entry, guchar *dest) { gint tile_size = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (ram)); memcpy (dest, entry->offset, tile_size); } static inline void ram_entry_write (GeglTileBackendRam *ram, RamEntry *entry, guchar *source) { gint tile_size = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (ram)); memcpy (entry->offset, source, tile_size); } static inline RamEntry * ram_entry_new (GeglTileBackendRam *ram) { RamEntry *self = g_slice_new (RamEntry); gint tile_size = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (ram)); self->offset = g_malloc (tile_size); dbg_alloc (tile_size); return self; } static inline void ram_entry_destroy (RamEntry *entry, GeglTileBackendRam *ram) { gint tile_size = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (ram)); g_free (entry->offset); g_hash_table_remove (ram->entries, entry); dbg_dealloc (tile_size); g_slice_free (RamEntry, entry); } G_DEFINE_TYPE (GeglTileBackendRam, gegl_tile_backend_ram, GEGL_TYPE_TILE_BACKEND) static GObjectClass * parent_class = NULL; static gint allocs = 0; static gint ram_size = 0; static gint peak_allocs = 0; static gint peak_ram_size = 0; void gegl_tile_backend_ram_stats (void) { g_warning ("leaked: %i chunks (%f mb) peak: %i (%i bytes %fmb))", allocs, ram_size / 1024 / 1024.0, peak_allocs, peak_ram_size, peak_ram_size / 1024 / 1024.0); } static void dbg_alloc (gint size) { allocs++; ram_size += size; if (allocs > peak_allocs) peak_allocs = allocs; if (ram_size > peak_ram_size) peak_ram_size = ram_size; } static void dbg_dealloc (gint size) { allocs--; ram_size -= size; } static inline RamEntry * lookup_entry (GeglTileBackendRam *self, gint x, gint y, gint z) { RamEntry key; key.x = x; key.y = y; key.z = z; key.offset = 0; return g_hash_table_lookup (self->entries, &key); } /* this is the only place that actually should * instantiate tiles, when the cache is large enough * that should make sure we don't hit this function * too often. */ static GeglTile * get_tile (GeglTileSource *tile_store, gint x, gint y, gint z) { GeglTileBackendRam *tile_backend_ram = GEGL_TILE_BACKEND_RAM (tile_store); GeglTileBackend *backend = GEGL_TILE_BACKEND (tile_store); GeglTile *tile = NULL; gint tile_size = gegl_tile_backend_get_tile_size (backend); { RamEntry *entry = lookup_entry (tile_backend_ram, x, y, z); if (!entry) return NULL; tile = gegl_tile_new (tile_size); ram_entry_read (tile_backend_ram, entry, gegl_tile_get_data (tile)); } return tile; } static gboolean set_tile (GeglTileSource *store, GeglTile *tile, gint x, gint y, gint z) { GeglTileBackend *backend = GEGL_TILE_BACKEND (store); GeglTileBackendRam *tile_backend_ram = GEGL_TILE_BACKEND_RAM (backend); RamEntry *entry = lookup_entry (tile_backend_ram, x, y, z); if (entry == NULL) { entry = ram_entry_new (tile_backend_ram); entry->x = x; entry->y = y; entry->z = z; g_hash_table_insert (tile_backend_ram->entries, entry, entry); } ram_entry_write (tile_backend_ram, entry, gegl_tile_get_data (tile)); gegl_tile_mark_as_stored (tile); return TRUE; } static gboolean void_tile (GeglTileSource *store, GeglTile *tile, gint x, gint y, gint z) { GeglTileBackend *backend = GEGL_TILE_BACKEND (store); GeglTileBackendRam *tile_backend_ram = GEGL_TILE_BACKEND_RAM (backend); RamEntry *entry = lookup_entry (tile_backend_ram, x, y, z); if (entry != NULL) { ram_entry_destroy (entry, tile_backend_ram); } return TRUE; } static gboolean exist_tile (GeglTileSource *store, GeglTile *tile, gint x, gint y, gint z) { GeglTileBackend *backend = GEGL_TILE_BACKEND (store); GeglTileBackendRam *tile_backend_ram = GEGL_TILE_BACKEND_RAM (backend); RamEntry *entry = lookup_entry (tile_backend_ram, x, y, z); return entry != NULL; } enum { PROP_0 }; static gpointer gegl_tile_backend_ram_command (GeglTileSource *tile_store, GeglTileCommand command, gint x, gint y, gint z, gpointer data) { switch (command) { case GEGL_TILE_GET: return get_tile (tile_store, x, y, z); case GEGL_TILE_SET: set_tile (tile_store, data, x, y, z); return NULL; case GEGL_TILE_IDLE: return NULL; case GEGL_TILE_VOID: void_tile (tile_store, data, x, y, z); return NULL; case GEGL_TILE_EXIST: return GINT_TO_POINTER(exist_tile (tile_store, data, x, y, z)); default: g_assert (command < GEGL_TILE_LAST_COMMAND && command >= 0); } return FALSE; } static void set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void finalize (GObject *object) { GeglTileBackendRam *self = (GeglTileBackendRam *) object; g_hash_table_unref (self->entries); (*G_OBJECT_CLASS (parent_class)->finalize)(object); } static guint hashfunc (gconstpointer key) { const RamEntry *e = key; guint hash; gint i; gint srcA = e->x; gint srcB = e->y; gint srcC = e->z; /* interleave the 10 least significant bits of all coordinates, * this gives us Z-order / morton order of the space and should * work well as a hash */ hash = 0; for (i = 9; i >= 0; i--) { #define ADD_BIT(bit) do { hash |= (((bit) != 0) ? 1 : 0); hash <<= 1; \ } \ while (0) ADD_BIT (srcA & (1 << i)); ADD_BIT (srcB & (1 << i)); ADD_BIT (srcC & (1 << i)); #undef ADD_BIT } return hash; } static gboolean equalfunc (gconstpointer a, gconstpointer b) { const RamEntry *ea = a; const RamEntry *eb = b; if (ea->x == eb->x && ea->y == eb->y && ea->z == eb->z) return TRUE; return FALSE; } static GObject * gegl_tile_backend_ram_constructor (GType type, guint n_params, GObjectConstructParam *params) { GObject *object; GeglTileBackendRam *ram; object = G_OBJECT_CLASS (parent_class)->constructor (type, n_params, params); ram = GEGL_TILE_BACKEND_RAM (object); ram->entries = g_hash_table_new (hashfunc, equalfunc); return object; } static void gegl_tile_backend_ram_class_init (GeglTileBackendRamClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); gobject_class->get_property = get_property; gobject_class->set_property = set_property; gobject_class->constructor = gegl_tile_backend_ram_constructor; gobject_class->finalize = finalize; } static void gegl_tile_backend_ram_init (GeglTileBackendRam *self) { ((GeglTileSource*)self)->command = gegl_tile_backend_ram_command; self->entries = NULL; }