Blame gegl/buffer/gegl-tile-handler-cache.c

Packit Service 2781ba
/* This file is part of GEGL.
Packit Service 2781ba
 *
Packit Service 2781ba
 * This library is free software; you can redistribute it and/or
Packit Service 2781ba
 * modify it under the terms of the GNU Lesser General Public
Packit Service 2781ba
 * License as published by the Free Software Foundation; either
Packit Service 2781ba
 * version 3 of the License, or (at your option) any later version.
Packit Service 2781ba
 *
Packit Service 2781ba
 * This library is distributed in the hope that it will be useful,
Packit Service 2781ba
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 2781ba
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 2781ba
 * Lesser General Public License for more details.
Packit Service 2781ba
 *
Packit Service 2781ba
 * You should have received a copy of the GNU Lesser General Public
Packit Service 2781ba
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit Service 2781ba
 *
Packit Service 2781ba
 * Copyright 2006,2007 Øyvind Kolås <pippin@gimp.org>
Packit Service 2781ba
 */
Packit Service 2781ba
Packit Service 2781ba
#include "config.h"
Packit Service 2781ba
Packit Service 2781ba
#include <glib.h>
Packit Service 2781ba
#include <glib-object.h>
Packit Service 2781ba
Packit Service 2781ba
#include "gegl.h"
Packit Service 2781ba
#include "gegl-types-internal.h"
Packit Service 2781ba
#include "gegl-config.h"
Packit Service 2781ba
#include "gegl-buffer.h"
Packit Service 2781ba
#include "gegl-buffer-private.h"
Packit Service 2781ba
#include "gegl-tile.h"
Packit Service 2781ba
#include "gegl-tile-handler-cache.h"
Packit Service 2781ba
#include "gegl-tile-storage.h"
Packit Service 2781ba
#include "gegl-debug.h"
Packit Service 2781ba
Packit Service 2781ba
#include "gegl-buffer-cl-cache.h"
Packit Service 2781ba
Packit Service 2781ba
/*
Packit Service 2781ba
#define GEGL_DEBUG_CACHE_HITS
Packit Service 2781ba
*/
Packit Service 2781ba
Packit Service 2781ba
typedef struct CacheItem
Packit Service 2781ba
{
Packit Service 2781ba
  GeglTileHandlerCache *handler; /* The specific handler that cached this item*/
Packit Service 2781ba
  GeglTile *tile;                /* The tile */
Packit Service 2781ba
Packit Service 2781ba
  gint      x;                   /* The coordinates this tile was cached for */
Packit Service 2781ba
  gint      y;
Packit Service 2781ba
  gint      z;
Packit Service 2781ba
} CacheItem;
Packit Service 2781ba
Packit Service 2781ba
Packit Service 2781ba
static void       gegl_tile_handler_cache_dispose    (GObject              *object);
Packit Service 2781ba
static gboolean   gegl_tile_handler_cache_wash       (GeglTileHandlerCache *cache);
Packit Service 2781ba
static gpointer   gegl_tile_handler_cache_command    (GeglTileSource       *tile_store,
Packit Service 2781ba
                                                      GeglTileCommand       command,
Packit Service 2781ba
                                                      gint                  x,
Packit Service 2781ba
                                                      gint                  y,
Packit Service 2781ba
                                                      gint                  z,
Packit Service 2781ba
                                                      gpointer              data);
Packit Service 2781ba
static GeglTile * gegl_tile_handler_cache_get_tile   (GeglTileHandlerCache *cache,
Packit Service 2781ba
                                                      gint                  x,
Packit Service 2781ba
                                                      gint                  y,
Packit Service 2781ba
                                                      gint                  z);
Packit Service 2781ba
static gboolean   gegl_tile_handler_cache_has_tile   (GeglTileHandlerCache *cache,
Packit Service 2781ba
                                                      gint                  x,
Packit Service 2781ba
                                                      gint                  y,
Packit Service 2781ba
                                                      gint                  z);
Packit Service 2781ba
void              gegl_tile_handler_cache_insert     (GeglTileHandlerCache *cache,
Packit Service 2781ba
                                                      GeglTile             *tile,
Packit Service 2781ba
                                                      gint                  x,
Packit Service 2781ba
                                                      gint                  y,
Packit Service 2781ba
                                                      gint                  z);
Packit Service 2781ba
static void       gegl_tile_handler_cache_void       (GeglTileHandlerCache *cache,
Packit Service 2781ba
                                                      gint                  x,
Packit Service 2781ba
                                                      gint                  y,
Packit Service 2781ba
                                                      gint                  z);
Packit Service 2781ba
static void       gegl_tile_handler_cache_invalidate (GeglTileHandlerCache *cache,
Packit Service 2781ba
                                                      gint                  x,
Packit Service 2781ba
                                                      gint                  y,
Packit Service 2781ba
                                                      gint                  z);
Packit Service 2781ba
Packit Service 2781ba
Packit Service 2781ba
static GStaticMutex mutex                 = G_STATIC_MUTEX_INIT;
Packit Service 2781ba
static GQueue      *cache_queue           = NULL;
Packit Service 2781ba
static GHashTable  *cache_ht              = NULL;
Packit Service 2781ba
static gint         cache_wash_percentage = 20;
Packit Service 2781ba
static gint         cache_total           = 0; /* approximate amount of bytes stored */
Packit Service 2781ba
#ifdef GEGL_DEBUG_CACHE_HITS
Packit Service 2781ba
static gint         cache_hits            = 0;
Packit Service 2781ba
static gint         cache_misses          = 0;
Packit Service 2781ba
#endif
Packit Service 2781ba
Packit Service 2781ba
Packit Service 2781ba
G_DEFINE_TYPE (GeglTileHandlerCache, gegl_tile_handler_cache, GEGL_TYPE_TILE_HANDLER)
Packit Service 2781ba
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
gegl_tile_handler_cache_class_init (GeglTileHandlerCacheClass *class)
Packit Service 2781ba
{
Packit Service 2781ba
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
Packit Service 2781ba
Packit Service 2781ba
  gobject_class->dispose = gegl_tile_handler_cache_dispose;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
gegl_tile_handler_cache_init (GeglTileHandlerCache *cache)
Packit Service 2781ba
{
Packit Service 2781ba
  ((GeglTileSource*)cache)->command = gegl_tile_handler_cache_command;
Packit Service 2781ba
  gegl_tile_cache_init ();
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
gegl_tile_handler_cache_reinit_buffer_tiles (gpointer itm,
Packit Service 2781ba
                                             gpointer userdata)
Packit Service 2781ba
{
Packit Service 2781ba
  CacheItem *item;
Packit Service 2781ba
  item = itm;
Packit Service 2781ba
  if (item->handler == userdata)
Packit Service 2781ba
    {
Packit Service 2781ba
      GeglTileHandlerCache *cache = userdata;
Packit Service 2781ba
      cache->free_list = g_slist_prepend (cache->free_list, item);
Packit Service 2781ba
    }
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
gegl_tile_handler_cache_reinit (GeglTileHandlerCache *cache)
Packit Service 2781ba
{
Packit Service 2781ba
  CacheItem            *item;
Packit Service 2781ba
  GSList               *iter;
Packit Service 2781ba
Packit Service 2781ba
  if (cache->tile_storage->hot_tile)
Packit Service 2781ba
    {
Packit Service 2781ba
      gegl_tile_unref (cache->tile_storage->hot_tile);
Packit Service 2781ba
      cache->tile_storage->hot_tile = NULL;
Packit Service 2781ba
    }
Packit Service 2781ba
Packit Service 2781ba
  if (!cache->count)
Packit Service 2781ba
    return;
Packit Service 2781ba
Packit Service 2781ba
  g_static_mutex_lock (&mutex);
Packit Service 2781ba
  /* only throw out items belonging to this cache instance */
Packit Service 2781ba
Packit Service 2781ba
  cache->free_list = NULL;
Packit Service 2781ba
  g_queue_foreach (cache_queue, gegl_tile_handler_cache_reinit_buffer_tiles, cache);
Packit Service 2781ba
  for (iter = cache->free_list; iter; iter = g_slist_next (iter))
Packit Service 2781ba
    {
Packit Service 2781ba
      item = iter->data;
Packit Service 2781ba
      if (item->tile)
Packit Service 2781ba
        {
Packit Service 2781ba
          cache_total -= item->tile->size;
Packit Service 2781ba
          gegl_tile_mark_as_stored (item->tile); /* to avoid saving */
Packit Service 2781ba
          gegl_tile_unref (item->tile);
Packit Service 2781ba
          cache->count--;
Packit Service 2781ba
        }
Packit Service 2781ba
      g_queue_remove (cache_queue, item);
Packit Service 2781ba
      g_hash_table_remove (cache_ht, item);
Packit Service 2781ba
      g_slice_free (CacheItem, item);
Packit Service 2781ba
    }
Packit Service 2781ba
  g_slist_free (cache->free_list);
Packit Service 2781ba
  cache->free_list = NULL;
Packit Service 2781ba
  g_static_mutex_unlock (&mutex);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
gegl_tile_handler_cache_dispose_buffer_tiles (gpointer itm,
Packit Service 2781ba
                                              gpointer userdata)
Packit Service 2781ba
{
Packit Service 2781ba
  CacheItem *item;
Packit Service 2781ba
  item = itm;
Packit Service 2781ba
  if (item->handler == userdata)
Packit Service 2781ba
    {
Packit Service 2781ba
      GeglTileHandlerCache *cache = userdata;
Packit Service 2781ba
      cache->free_list = g_slist_prepend (cache->free_list, item);
Packit Service 2781ba
    }
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
gegl_tile_handler_cache_dispose (GObject *object)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglTileHandlerCache *cache;
Packit Service 2781ba
  CacheItem            *item;
Packit Service 2781ba
  GSList               *iter;
Packit Service 2781ba
Packit Service 2781ba
  cache = GEGL_TILE_HANDLER_CACHE (object);
Packit Service 2781ba
Packit Service 2781ba
  /* only throw out items belonging to this cache instance */
Packit Service 2781ba
Packit Service 2781ba
  cache->free_list = NULL;
Packit Service 2781ba
  /* XXX: for optimization this could be delayed,. or collected among multiple
Packit Service 2781ba
   * buffer destructions, to avoid the overhead of walking the full queue for
Packit Service 2781ba
   * every tiny buffer being destroyed.
Packit Service 2781ba
   */
Packit Service 2781ba
Packit Service 2781ba
  if (cache->count)
Packit Service 2781ba
    {
Packit Service 2781ba
      g_static_mutex_lock (&mutex);
Packit Service 2781ba
      g_queue_foreach (cache_queue, gegl_tile_handler_cache_dispose_buffer_tiles, cache);
Packit Service 2781ba
      for (iter = cache->free_list; iter; iter = g_slist_next (iter))
Packit Service 2781ba
        {
Packit Service 2781ba
            item = iter->data;
Packit Service 2781ba
            if (item->tile)
Packit Service 2781ba
              {
Packit Service 2781ba
                cache_total -= item->tile->size;
Packit Service 2781ba
                gegl_tile_unref (item->tile);
Packit Service 2781ba
                cache->count--;
Packit Service 2781ba
              }
Packit Service 2781ba
            g_queue_remove (cache_queue, item);
Packit Service 2781ba
            g_hash_table_remove (cache_ht, item);
Packit Service 2781ba
            g_slice_free (CacheItem, item);
Packit Service 2781ba
        }
Packit Service 2781ba
      g_slist_free (cache->free_list);
Packit Service 2781ba
      cache->free_list = NULL;
Packit Service 2781ba
      g_static_mutex_unlock (&mutex);
Packit Service 2781ba
    }
Packit Service 2781ba
Packit Service 2781ba
  if (cache->count != 0)
Packit Service 2781ba
    {
Packit Service 2781ba
      g_warning ("cache-handler tile balance not zero: %i\n", cache->count);
Packit Service 2781ba
    }
Packit Service 2781ba
  G_OBJECT_CLASS (gegl_tile_handler_cache_parent_class)->dispose (object);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static GeglTile *
Packit Service 2781ba
gegl_tile_handler_cache_get_tile_command (GeglTileSource *tile_store,
Packit Service 2781ba
                                          gint        x,
Packit Service 2781ba
                                          gint        y,
Packit Service 2781ba
                                          gint        z)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglTileHandlerCache *cache    = GEGL_TILE_HANDLER_CACHE (tile_store);
Packit Service 2781ba
  GeglTileSource       *source = GEGL_TILE_HANDLER (tile_store)->source;
Packit Service 2781ba
  GeglTile             *tile     = NULL;
Packit Service 2781ba
Packit Service 2781ba
  if (gegl_cl_is_accelerated ())
Packit Service 2781ba
    gegl_buffer_cl_cache_flush2 (cache, NULL);
Packit Service 2781ba
Packit Service 2781ba
  tile = gegl_tile_handler_cache_get_tile (cache, x, y, z);
Packit Service 2781ba
  if (tile)
Packit Service 2781ba
    {
Packit Service 2781ba
#ifdef GEGL_DEBUG_CACHE_HITS
Packit Service 2781ba
      cache_hits++;
Packit Service 2781ba
#endif
Packit Service 2781ba
      return tile;
Packit Service 2781ba
    }
Packit Service 2781ba
#ifdef GEGL_DEBUG_CACHE_HITS
Packit Service 2781ba
  cache_misses++;
Packit Service 2781ba
#endif
Packit Service 2781ba
Packit Service 2781ba
  if (source)
Packit Service 2781ba
    tile = gegl_tile_source_get_tile (source, x, y, z);
Packit Service 2781ba
Packit Service 2781ba
  if (tile)
Packit Service 2781ba
    gegl_tile_handler_cache_insert (cache, tile, x, y, z);
Packit Service 2781ba
Packit Service 2781ba
  return tile;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static gpointer
Packit Service 2781ba
gegl_tile_handler_cache_command (GeglTileSource  *tile_store,
Packit Service 2781ba
                                 GeglTileCommand  command,
Packit Service 2781ba
                                 gint             x,
Packit Service 2781ba
                                 gint             y,
Packit Service 2781ba
                                 gint             z,
Packit Service 2781ba
                                 gpointer         data)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglTileHandler      *handler = GEGL_TILE_HANDLER (tile_store);
Packit Service 2781ba
  GeglTileHandlerCache *cache   = GEGL_TILE_HANDLER_CACHE (handler);
Packit Service 2781ba
Packit Service 2781ba
  switch (command)
Packit Service 2781ba
    {
Packit Service 2781ba
      case GEGL_TILE_FLUSH:
Packit Service 2781ba
        {
Packit Service 2781ba
          GList     *link;
Packit Service 2781ba
Packit Service 2781ba
          if (gegl_cl_is_accelerated ())
Packit Service 2781ba
            gegl_buffer_cl_cache_flush2 (GEGL_TILE_HANDLER_CACHE (tile_store), NULL);
Packit Service 2781ba
Packit Service 2781ba
          if (cache->count)
Packit Service 2781ba
            {
Packit Service 2781ba
              for (link = g_queue_peek_head_link (cache_queue); link; link = link->next)
Packit Service 2781ba
                {
Packit Service 2781ba
                  CacheItem *item = link->data;
Packit Service 2781ba
                  GeglTile  *tile = item->tile;
Packit Service 2781ba
Packit Service 2781ba
                  if (tile != NULL &&
Packit Service 2781ba
                      item->handler == cache)
Packit Service 2781ba
                    {
Packit Service 2781ba
                      gegl_tile_store (tile);
Packit Service 2781ba
                    }
Packit Service 2781ba
                }
Packit Service 2781ba
            }
Packit Service 2781ba
        }
Packit Service 2781ba
        break;
Packit Service 2781ba
      case GEGL_TILE_GET:
Packit Service 2781ba
        /* XXX: we should perhaps store a NIL result, and place the empty
Packit Service 2781ba
         * generator after the cache, this would have to be possible to disable
Packit Service 2781ba
         * to work in sync operation with backend.
Packit Service 2781ba
         */
Packit Service 2781ba
        return gegl_tile_handler_cache_get_tile_command (tile_store, x, y, z);
Packit Service 2781ba
      case GEGL_TILE_IS_CACHED:
Packit Service 2781ba
        return GINT_TO_POINTER(gegl_tile_handler_cache_has_tile (cache, x, y, z));
Packit Service 2781ba
      case GEGL_TILE_EXIST:
Packit Service 2781ba
        {
Packit Service 2781ba
          gboolean exist = gegl_tile_handler_cache_has_tile (cache, x, y, z);
Packit Service 2781ba
          if (exist)
Packit Service 2781ba
            return (gpointer)TRUE;
Packit Service 2781ba
        }
Packit Service 2781ba
        break;
Packit Service 2781ba
      case GEGL_TILE_IDLE:
Packit Service 2781ba
        {
Packit Service 2781ba
          gboolean action = gegl_tile_handler_cache_wash (cache);
Packit Service 2781ba
          if (action)
Packit Service 2781ba
            return GINT_TO_POINTER(action);
Packit Service 2781ba
          /* with no action, we chain up to lower levels */
Packit Service 2781ba
          break;
Packit Service 2781ba
        }
Packit Service 2781ba
      case GEGL_TILE_REFETCH:
Packit Service 2781ba
        gegl_tile_handler_cache_invalidate (cache, x, y, z);
Packit Service 2781ba
        break;
Packit Service 2781ba
      case GEGL_TILE_VOID:
Packit Service 2781ba
        gegl_tile_handler_cache_void (cache, x, y, z);
Packit Service 2781ba
        break;
Packit Service 2781ba
      case GEGL_TILE_REINIT:
Packit Service 2781ba
        gegl_tile_handler_cache_reinit (cache);
Packit Service 2781ba
        break;
Packit Service 2781ba
      default:
Packit Service 2781ba
        break;
Packit Service 2781ba
    }
Packit Service 2781ba
Packit Service 2781ba
  return gegl_tile_handler_source_command (handler, command, x, y, z, data);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
/* write the least recently used dirty tile to disk if it
Packit Service 2781ba
 * is in the wash_percentage (20%) least recently used tiles,
Packit Service 2781ba
 * calling this function in an idle handler distributes the
Packit Service 2781ba
 * tile flushing overhead over time.
Packit Service 2781ba
 */
Packit Service 2781ba
gboolean
Packit Service 2781ba
gegl_tile_handler_cache_wash (GeglTileHandlerCache *cache)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglTile  *last_dirty = NULL;
Packit Service 2781ba
  guint      count      = 0;
Packit Service 2781ba
  gint       length     = g_queue_get_length (cache_queue);
Packit Service 2781ba
  gint       wash_tiles = cache_wash_percentage * length / 100;
Packit Service 2781ba
  GList     *link;
Packit Service 2781ba
Packit Service 2781ba
  for (link = g_queue_peek_head_link (cache_queue); link; link = link->next)
Packit Service 2781ba
    {
Packit Service 2781ba
      CacheItem *item = link->data;
Packit Service 2781ba
      GeglTile  *tile = item->tile;
Packit Service 2781ba
Packit Service 2781ba
      count++;
Packit Service 2781ba
      if (!gegl_tile_is_stored (tile))
Packit Service 2781ba
        if (count > length - wash_tiles)
Packit Service 2781ba
          last_dirty = tile;
Packit Service 2781ba
    }
Packit Service 2781ba
Packit Service 2781ba
  if (last_dirty != NULL)
Packit Service 2781ba
    {
Packit Service 2781ba
      gegl_tile_store (last_dirty);
Packit Service 2781ba
      return TRUE;
Packit Service 2781ba
    }
Packit Service 2781ba
  return FALSE;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
/* returns the requested Tile if it is in the cache, NULL otherwize.
Packit Service 2781ba
 */
Packit Service 2781ba
static GeglTile *
Packit Service 2781ba
gegl_tile_handler_cache_get_tile (GeglTileHandlerCache *cache,
Packit Service 2781ba
                                  gint                  x,
Packit Service 2781ba
                                  gint                  y,
Packit Service 2781ba
                                  gint                  z)
Packit Service 2781ba
{
Packit Service 2781ba
  CacheItem *result;
Packit Service 2781ba
  CacheItem pin;
Packit Service 2781ba
Packit Service 2781ba
  if (cache->count == 0)
Packit Service 2781ba
    return NULL;
Packit Service 2781ba
Packit Service 2781ba
  pin.x = x;
Packit Service 2781ba
  pin.y = y;
Packit Service 2781ba
  pin.z = z;
Packit Service 2781ba
  pin.handler = cache;
Packit Service 2781ba
Packit Service 2781ba
  g_static_mutex_lock (&mutex);
Packit Service 2781ba
  result = g_hash_table_lookup (cache_ht, &pin;;
Packit Service 2781ba
  if (result)
Packit Service 2781ba
    {
Packit Service 2781ba
      g_queue_remove (cache_queue, result);
Packit Service 2781ba
      g_queue_push_head (cache_queue, result);
Packit Service 2781ba
      g_static_mutex_unlock (&mutex);
Packit Service 2781ba
      return gegl_tile_ref (result->tile);
Packit Service 2781ba
    }
Packit Service 2781ba
  g_static_mutex_unlock (&mutex);
Packit Service 2781ba
  return NULL;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static gboolean
Packit Service 2781ba
gegl_tile_handler_cache_has_tile (GeglTileHandlerCache *cache,
Packit Service 2781ba
                                  gint                  x,
Packit Service 2781ba
                                  gint                  y,
Packit Service 2781ba
                                  gint                  z)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglTile *tile = gegl_tile_handler_cache_get_tile (cache, x, y, z);
Packit Service 2781ba
Packit Service 2781ba
  if (tile)
Packit Service 2781ba
    {
Packit Service 2781ba
      gegl_tile_unref (tile);
Packit Service 2781ba
      return TRUE;
Packit Service 2781ba
    }
Packit Service 2781ba
Packit Service 2781ba
  return FALSE;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static gboolean
Packit Service 2781ba
gegl_tile_handler_cache_trim (GeglTileHandlerCache *cache)
Packit Service 2781ba
{
Packit Service 2781ba
  CacheItem *last_writable;
Packit Service 2781ba
Packit Service 2781ba
  last_writable = g_queue_pop_tail (cache_queue);
Packit Service 2781ba
Packit Service 2781ba
  if (last_writable != NULL)
Packit Service 2781ba
    {
Packit Service 2781ba
      g_hash_table_remove (cache_ht, last_writable);
Packit Service 2781ba
      cache_total  -= last_writable->tile->size;
Packit Service 2781ba
      gegl_tile_unref (last_writable->tile);
Packit Service 2781ba
      g_slice_free (CacheItem, last_writable);
Packit Service 2781ba
      return TRUE;
Packit Service 2781ba
    }
Packit Service 2781ba
Packit Service 2781ba
  return FALSE;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
gegl_tile_handler_cache_invalidate (GeglTileHandlerCache *cache,
Packit Service 2781ba
                                    gint                  x,
Packit Service 2781ba
                                    gint                  y,
Packit Service 2781ba
                                    gint                  z)
Packit Service 2781ba
{
Packit Service 2781ba
  GList *link;
Packit Service 2781ba
Packit Service 2781ba
  g_static_mutex_lock (&mutex);
Packit Service 2781ba
  for (link = g_queue_peek_head_link (cache_queue); link; link = link->next)
Packit Service 2781ba
    {
Packit Service 2781ba
      CacheItem *item = link->data;
Packit Service 2781ba
      GeglTile  *tile = item->tile;
Packit Service 2781ba
Packit Service 2781ba
      if (tile != NULL &&
Packit Service 2781ba
          item->x == x &&
Packit Service 2781ba
          item->y == y &&
Packit Service 2781ba
          item->z == z &&
Packit Service 2781ba
          item->handler == cache)
Packit Service 2781ba
        {
Packit Service 2781ba
          cache_total  -= item->tile->size;
Packit Service 2781ba
          tile->tile_storage = NULL;
Packit Service 2781ba
          gegl_tile_mark_as_stored (tile); /* to cheat it out of being stored */
Packit Service 2781ba
          gegl_tile_unref (tile);
Packit Service 2781ba
          g_hash_table_remove (cache_ht, item);
Packit Service 2781ba
          g_slice_free (CacheItem, item);
Packit Service 2781ba
          g_queue_delete_link (cache_queue, link);
Packit Service 2781ba
          g_static_mutex_unlock (&mutex);
Packit Service 2781ba
          return;
Packit Service 2781ba
        }
Packit Service 2781ba
    }
Packit Service 2781ba
  g_static_mutex_unlock (&mutex);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
gegl_tile_handler_cache_void (GeglTileHandlerCache *cache,
Packit Service 2781ba
                              gint                  x,
Packit Service 2781ba
                              gint                  y,
Packit Service 2781ba
                              gint                  z)
Packit Service 2781ba
{
Packit Service 2781ba
  GList *link;
Packit Service 2781ba
Packit Service 2781ba
  if (!cache_queue)
Packit Service 2781ba
    return;
Packit Service 2781ba
Packit Service 2781ba
  g_static_mutex_lock (&mutex);
Packit Service 2781ba
  for (link = g_queue_peek_head_link (cache_queue); link; link = link->next)
Packit Service 2781ba
    {
Packit Service 2781ba
      CacheItem *item = link->data;
Packit Service 2781ba
      GeglTile  *tile = item->tile;
Packit Service 2781ba
Packit Service 2781ba
      if (tile != NULL &&
Packit Service 2781ba
          item->x == x &&
Packit Service 2781ba
          item->y == y &&
Packit Service 2781ba
          item->z == z &&
Packit Service 2781ba
          item->handler == cache)
Packit Service 2781ba
        {
Packit Service 2781ba
          gegl_tile_void (tile);
Packit Service 2781ba
          cache_total -= item->tile->size;
Packit Service 2781ba
          gegl_tile_unref (tile);
Packit Service 2781ba
          g_hash_table_remove (cache_ht, item);
Packit Service 2781ba
          g_slice_free (CacheItem, item);
Packit Service 2781ba
          g_queue_delete_link (cache_queue, link);
Packit Service 2781ba
          g_static_mutex_unlock (&mutex);
Packit Service 2781ba
          return;
Packit Service 2781ba
        }
Packit Service 2781ba
    }
Packit Service 2781ba
  g_static_mutex_unlock (&mutex);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
void
Packit Service 2781ba
gegl_tile_handler_cache_insert (GeglTileHandlerCache *cache,
Packit Service 2781ba
                                GeglTile             *tile,
Packit Service 2781ba
                                gint                  x,
Packit Service 2781ba
                                gint                  y,
Packit Service 2781ba
                                gint                  z)
Packit Service 2781ba
{
Packit Service 2781ba
  CacheItem *item = g_slice_new (CacheItem);
Packit Service 2781ba
Packit Service 2781ba
  item->handler = cache;
Packit Service 2781ba
  item->tile    = gegl_tile_ref (tile);
Packit Service 2781ba
  item->x       = x;
Packit Service 2781ba
  item->y       = y;
Packit Service 2781ba
  item->z       = z;
Packit Service 2781ba
Packit Service 2781ba
  g_static_mutex_lock (&mutex);
Packit Service 2781ba
  cache_total  += item->tile->size;
Packit Service 2781ba
  g_queue_push_head (cache_queue, item);
Packit Service 2781ba
Packit Service 2781ba
  cache->count ++;
Packit Service 2781ba
Packit Service 2781ba
  g_hash_table_insert (cache_ht, item, item);
Packit Service 2781ba
Packit Service 2781ba
  while (cache_total > gegl_config()->cache_size)
Packit Service 2781ba
    {
Packit Service 2781ba
#ifdef GEGL_DEBUG_CACHE_HITS
Packit Service 2781ba
      GEGL_NOTE(GEGL_DEBUG_CACHE, "cache_total:%i > cache_size:%i", cache_total, gegl_config()->cache_size);
Packit Service 2781ba
      GEGL_NOTE(GEGL_DEBUG_CACHE, "%f%% hit:%i miss:%i  %i]", cache_hits*100.0/(cache_hits+cache_misses), cache_hits, cache_misses, g_queue_get_length (cache_queue));
Packit Service 2781ba
#endif
Packit Service 2781ba
      gegl_tile_handler_cache_trim (cache);
Packit Service 2781ba
    }
Packit Service 2781ba
  g_static_mutex_unlock (&mutex);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
GeglTileHandlerCache *
Packit Service 2781ba
gegl_tile_handler_cache_new (void)
Packit Service 2781ba
{
Packit Service 2781ba
  return g_object_new (GEGL_TYPE_TILE_HANDLER_CACHE, NULL);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
Packit Service 2781ba
static guint
Packit Service 2781ba
gegl_tile_handler_cache_hashfunc (gconstpointer key)
Packit Service 2781ba
{
Packit Service 2781ba
  const CacheItem *e = key;
Packit Service 2781ba
  guint           hash;
Packit Service 2781ba
  gint            i;
Packit Service 2781ba
  gint            srcA = e->x;
Packit Service 2781ba
  gint            srcB = e->y;
Packit Service 2781ba
  gint            srcC = e->z;
Packit Service 2781ba
Packit Service 2781ba
  /* interleave the 10 least significant bits of all coordinates,
Packit Service 2781ba
   * this gives us Z-order / morton order of the space and should
Packit Service 2781ba
   * work well as a hash
Packit Service 2781ba
   */
Packit Service 2781ba
  hash = 0;
Packit Service 2781ba
  for (i = 9; i >= 0; i--)
Packit Service 2781ba
    {
Packit Service 2781ba
#define ADD_BIT(bit)    do { hash |= (((bit) != 0) ? 1 : 0); hash <<= 1; } while (0)
Packit Service 2781ba
      ADD_BIT (srcA & (1 << i));
Packit Service 2781ba
      ADD_BIT (srcB & (1 << i));
Packit Service 2781ba
      ADD_BIT (srcC & (1 << i));
Packit Service 2781ba
#undef ADD_BIT
Packit Service 2781ba
    }
Packit Service 2781ba
  return hash ^ GPOINTER_TO_INT (e->handler);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static gboolean
Packit Service 2781ba
gegl_tile_handler_cache_equalfunc (gconstpointer a,
Packit Service 2781ba
                                   gconstpointer b)
Packit Service 2781ba
{
Packit Service 2781ba
  const CacheItem *ea = a;
Packit Service 2781ba
  const CacheItem *eb = b;
Packit Service 2781ba
Packit Service 2781ba
  if (ea->x == eb->x &&
Packit Service 2781ba
      ea->y == eb->y &&
Packit Service 2781ba
      ea->z == eb->z &&
Packit Service 2781ba
      ea->handler == eb->handler)
Packit Service 2781ba
    return TRUE;
Packit Service 2781ba
  return FALSE;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
void
Packit Service 2781ba
gegl_tile_cache_init (void)
Packit Service 2781ba
{
Packit Service 2781ba
  if (cache_queue == NULL)
Packit Service 2781ba
    cache_queue = g_queue_new ();
Packit Service 2781ba
  if (cache_ht == NULL)
Packit Service 2781ba
    cache_ht = g_hash_table_new (gegl_tile_handler_cache_hashfunc, gegl_tile_handler_cache_equalfunc);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
void
Packit Service 2781ba
gegl_tile_cache_destroy (void)
Packit Service 2781ba
{
Packit Service 2781ba
  if (cache_queue)
Packit Service 2781ba
    g_queue_free (cache_queue);
Packit Service 2781ba
  if (cache_ht)
Packit Service 2781ba
    g_hash_table_destroy (cache_ht);
Packit Service 2781ba
  cache_queue = NULL;
Packit Service 2781ba
  cache_ht = NULL;
Packit Service 2781ba
}