[gtk+/wip/ebassi/gsk-renderer] gsk: Add GskResourceCache



commit f273107acf21fe3232121151ec0b5be6cfb7b785
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Wed Aug 31 11:47:43 2016 +0100

    gsk: Add GskResourceCache
    
    GskResourceCache is a base class for caching graphical resources, like
    surfaces, images, and glyphs.

 gsk/Makefile.am               |    3 +
 gsk/gsk.h                     |    1 +
 gsk/gskresourcecache.c        |  427 +++++++++++++++++++++++++++++++++++++++++
 gsk/gskresourcecache.h        |   73 +++++++
 gsk/gskresourcecacheprivate.h |   26 +++
 5 files changed, 530 insertions(+), 0 deletions(-)
---
diff --git a/gsk/Makefile.am b/gsk/Makefile.am
index 7fdfb3b..1ffd41a 100644
--- a/gsk/Makefile.am
+++ b/gsk/Makefile.am
@@ -32,6 +32,7 @@ gsk_public_source_h = \
        gskrenderer.h \
        gskrendernode.h \
        gskrendernodeiter.h \
+       gskresourcecache.h \
        gsktypes.h
 gsk_private_source_h = \
        gskcairorendererprivate.h \
@@ -43,6 +44,7 @@ gsk_private_source_h = \
        gskprofilerprivate.h \
        gskrendererprivate.h \
        gskrendernodeprivate.h \
+       gskresourcecacheprivate.h \
        gskshaderbuilderprivate.h
 gsk_private_source_c = \
        gskprivate.c
@@ -62,6 +64,7 @@ gsk_source_c = \
        gskrenderer.c \
        gskrendernode.c \
        gskrendernodeiter.c \
+       gskresourcecache.c \
        gskshaderbuilder.c
 
 all_sources = \
diff --git a/gsk/gsk.h b/gsk/gsk.h
index 01c4569..c068b50 100644
--- a/gsk/gsk.h
+++ b/gsk/gsk.h
@@ -24,6 +24,7 @@
 #include <gsk/gskrenderer.h>
 #include <gsk/gskrendernode.h>
 #include <gsk/gskrendernodeiter.h>
+#include <gsk/gskresourcecache.h>
 
 #include <gsk/gsktypes.h>
 #include <gsk/gskenumtypes.h>
diff --git a/gsk/gskresourcecache.c b/gsk/gskresourcecache.c
new file mode 100644
index 0000000..274474b
--- /dev/null
+++ b/gsk/gskresourcecache.c
@@ -0,0 +1,427 @@
+#include "config.h"
+
+#include "gskresourcecacheprivate.h"
+
+typedef struct {
+  GHashTable *resources;
+
+  GHashFunc key_hash;
+  GEqualFunc key_equal;
+  GDestroyNotify key_notify;
+
+  GType value_type;
+
+  /* Interned, do not free */
+  const char *name;
+} GskResourceCachePrivate;
+
+typedef struct {
+  GType value_type;
+  gpointer value;
+  gint64 last_access_time;
+  gint64 age;
+} ResourceCacheItem;
+
+enum {
+  PROP_NAME = 1,
+
+  N_PROPERTIES
+};
+
+static GParamSpec *gsk_resource_cache_properties[N_PROPERTIES];
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GskResourceCache, gsk_resource_cache, G_TYPE_OBJECT)
+
+static void
+gsk_resource_cache_dispose (GObject *gobject)
+{
+  GskResourceCache *self = GSK_RESOURCE_CACHE (gobject);
+  GskResourceCachePrivate *priv = gsk_resource_cache_get_instance_private (self);
+
+  g_clear_pointer (&priv->resources, g_hash_table_unref);
+
+  G_OBJECT_CLASS (gsk_resource_cache_parent_class)->dispose (gobject);
+}
+
+static void
+gsk_resource_cache_set_property (GObject      *gobject,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  GskResourceCache *self = GSK_RESOURCE_CACHE (gobject);
+  GskResourceCachePrivate *priv = gsk_resource_cache_get_instance_private (self);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      if (g_value_get_string (value) != NULL)
+        priv->name = g_intern_static_string (g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+gsk_resource_cache_get_property (GObject    *gobject,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  GskResourceCache *self = GSK_RESOURCE_CACHE (gobject);
+  GskResourceCachePrivate *priv = gsk_resource_cache_get_instance_private (self);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      g_value_set_string (value, priv->name);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+    }
+}
+
+static void
+gsk_resource_cache_class_init (GskResourceCacheClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->set_property = gsk_resource_cache_set_property;
+  gobject_class->get_property = gsk_resource_cache_get_property;
+  gobject_class->dispose = gsk_resource_cache_dispose;
+
+  gsk_resource_cache_properties[PROP_NAME] =
+    g_param_spec_string ("name", "Name", "The (short) name of the resource cache",
+                         NULL,
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_READWRITE |
+                         G_PARAM_STATIC_STRINGS);
+}
+
+static void
+gsk_resource_cache_init (GskResourceCache *self)
+{
+}
+
+/**
+ * gsk_resource_cache_set_hash_func:
+ * @cache: a #GskResourceCache
+ * @key_hash: a hashing function for the key
+ *
+ * Sets the hashing function for the keys inside the resource @cache.
+ *
+ * Since: 3.22
+ */
+void
+gsk_resource_cache_set_hash_func (GskResourceCache *cache,
+                                  GHashFunc         key_hash)
+{
+  GskResourceCachePrivate *priv = gsk_resource_cache_get_instance_private (cache);
+
+  g_return_if_fail (GSK_IS_RESOURCE_CACHE (cache));
+  g_return_if_fail (priv->resources == NULL);
+
+  priv->key_hash = key_hash;
+}
+
+/**
+ * gsk_resource_cache_set_equal_func:
+ * @cache: a #GskResourceCache
+ * @key_equal: a function for checking two keys for equality
+ *
+ * Sets the comparison function for two keys in the resource @cache.
+ *
+ * Since: 3.22
+ */
+void
+gsk_resource_cache_set_equal_func (GskResourceCache *cache,
+                                   GEqualFunc        key_equal)
+{
+  GskResourceCachePrivate *priv = gsk_resource_cache_get_instance_private (cache);
+
+  g_return_if_fail (GSK_IS_RESOURCE_CACHE (cache));
+  g_return_if_fail (priv->resources == NULL);
+
+  priv->key_equal = key_equal;
+}
+
+/**
+ * gsk_resource_cache_set_free_func:
+ * @cache: a #GskResourceCache
+ * @key_notify: a function to be called when removing a key
+ *
+ * Sets the function to be called when removing a key from the resource @cache.
+ *
+ * Since: 3.22
+ */
+void
+gsk_resource_cache_set_free_func (GskResourceCache *cache,
+                                  GDestroyNotify    key_notify)
+{
+  GskResourceCachePrivate *priv = gsk_resource_cache_get_instance_private (cache);
+
+  g_return_if_fail (GSK_IS_RESOURCE_CACHE (cache));
+  g_return_if_fail (priv->resources == NULL);
+
+  priv->key_notify = key_notify;
+}
+
+/**
+ * gsk_resource_cache_set_value_type:
+ * @cache: a #GskResourceCache
+ * @value_type: the type of the data to be stored inside the cache
+ *
+ * Sets the #GType of the values inside the resource @cache.
+ *
+ * Only pointer-sized types can be stored inside the cache, i.e. the only
+ * admissible types for @value_type are:
+ *
+ *  - %G_TYPE_POINTER (no memory management)
+ *  - %G_TYPE_BOXED (the cache will copy the data)
+ *  - %G_TYPE_OBJECT (the cache will acquire a reference to the data)
+ *
+ * Since: 3.22
+ */
+void
+gsk_resource_cache_set_value_type (GskResourceCache *cache,
+                                   GType             value_type)
+{
+  GskResourceCachePrivate *priv = gsk_resource_cache_get_instance_private (cache);
+
+  g_return_if_fail (GSK_IS_RESOURCE_CACHE (cache));
+  g_return_if_fail (priv->resources == NULL);
+  g_return_if_fail (value_type != G_TYPE_INVALID);
+
+  if (G_TYPE_FUNDAMENTAL (value_type) != G_TYPE_POINTER ||
+      G_TYPE_FUNDAMENTAL (value_type) != G_TYPE_BOXED ||
+      G_TYPE_FUNDAMENTAL (value_type) != G_TYPE_OBJECT)
+    {
+      g_critical ("Unsupported resource type '%s'", g_type_name (value_type));
+      return;
+    }
+
+  priv->value_type = value_type;
+}
+
+/**
+ * gsk_resource_cache_set_name:
+ * @cache: a #GskResourceCache
+ * @name: a name for the @cache
+ *
+ * Sets a name for the resource @cache, for debugging purposes.
+ *
+ * Since: 3.22
+ */
+void
+gsk_resource_cache_set_name (GskResourceCache *cache,
+                             const char       *name)
+{
+  GskResourceCachePrivate *priv = gsk_resource_cache_get_instance_private (cache);
+
+  g_return_if_fail (GSK_IS_RESOURCE_CACHE (cache));
+
+  priv->name = g_intern_string (name);
+}
+
+static ResourceCacheItem *
+resource_cache_item_new (GType    value_type,
+                         gpointer value)
+{
+  ResourceCacheItem *item;
+
+  if (value_type == G_TYPE_INVALID)
+    {
+      g_critical ("Invalid resource type; did you forget to call "
+                  "gsk_resource_cache_set_value_type()?");
+      return NULL;
+    }
+
+  item = g_slice_new0 (ResourceCacheItem);
+  item->value_type = value_type;
+
+  switch (G_TYPE_FUNDAMENTAL (item->value_type))
+    {
+    case G_TYPE_POINTER:
+      item->value = value;
+      break;
+
+    case G_TYPE_BOXED:
+      if (value != NULL)
+        item->value = g_boxed_copy (item->value_type, value);
+      break;
+
+    case G_TYPE_OBJECT:
+      if (value != NULL)
+        item->value = g_object_ref (value);
+      break;
+
+    default:
+      g_critical ("Unsupported resource type '%s'", g_type_name (value_type));
+      g_slice_free (ResourceCacheItem, item);
+      return NULL;
+    }
+
+  item->age = 1;
+
+  return item;
+}
+
+static gpointer
+resource_cache_item_get_value (ResourceCacheItem *item)
+{
+  item->last_access_time = g_get_monotonic_time ();
+
+  return item->value;
+}
+
+static void
+resource_cache_item_free (gpointer data)
+{
+  ResourceCacheItem *item = data;
+
+  if (data == NULL)
+    return;
+
+  switch (G_TYPE_FUNDAMENTAL (item->value_type))
+    {
+    case G_TYPE_POINTER:
+      break;
+
+    case G_TYPE_BOXED:
+      if (item->value != NULL)
+        g_boxed_free (item->value_type, item->value);
+      break;
+
+    case G_TYPE_OBJECT:
+      if (item->value != NULL)
+        g_object_unref (item->value);
+      break;
+
+    default:
+      g_critical ("Unsupported resource type '%s'", g_type_name (item->value_type));
+    }
+
+  g_slice_free (ResourceCacheItem, item);
+}
+
+void
+gsk_resource_cache_add_item (GskResourceCache *cache,
+                             gpointer          key,
+                             gpointer          value)
+{
+  GskResourceCachePrivate *priv = gsk_resource_cache_get_instance_private (cache);
+  ResourceCacheItem *item;
+
+  g_return_if_fail (GSK_IS_RESOURCE_CACHE (cache));
+
+  if (priv->resources == NULL)
+    {
+      priv->resources = g_hash_table_new_full (priv->key_hash, priv->key_equal,
+                                               priv->key_notify,
+                                               resource_cache_item_free);
+    }
+
+  item = g_hash_table_lookup (priv->resources, key);
+  if (item != NULL)
+    {
+      item->age += 1;
+      return;
+    }
+
+  item = resource_cache_item_new (priv->value_type, value);
+  if (item != NULL)
+    g_hash_table_insert (priv->resources, key, item);
+}
+
+gboolean
+gsk_resource_cache_has_item (GskResourceCache *cache,
+                             gpointer          key)
+{
+  GskResourceCachePrivate *priv = gsk_resource_cache_get_instance_private (cache);
+
+  g_return_val_if_fail (GSK_IS_RESOURCE_CACHE (cache), FALSE);
+
+  if (priv->resources == NULL)
+    return FALSE;
+
+  return g_hash_table_lookup (priv->resources, key) != NULL;
+}
+
+gpointer
+gsk_resource_cache_get_item (GskResourceCache *cache,
+                             gpointer          key)
+{
+  GskResourceCachePrivate *priv = gsk_resource_cache_get_instance_private (cache);
+  ResourceCacheItem *item;
+
+  g_return_val_if_fail (GSK_IS_RESOURCE_CACHE (cache), FALSE);
+
+  if (priv->resources == NULL)
+    return NULL;
+
+  item = g_hash_table_lookup (priv->resources, key);
+  if (item == NULL)
+    return NULL;
+
+  return resource_cache_item_get_value (item);
+}
+
+gboolean
+gsk_resource_cache_invalidate_item (GskResourceCache *cache,
+                                    gpointer          key)
+{
+  GskResourceCachePrivate *priv = gsk_resource_cache_get_instance_private (cache);
+  ResourceCacheItem *item;
+
+  g_return_val_if_fail (GSK_IS_RESOURCE_CACHE (cache), FALSE);
+
+  if (priv->resources == NULL)
+    return FALSE;
+
+  item = g_hash_table_lookup (priv->resources, key);
+  if (item == NULL)
+    return FALSE;
+
+  item->age -= 1;
+  if (item->age < 0)
+    {
+      g_critical ("Too many invalidations for item of type '%s'", g_type_name (priv->value_type));
+      item->age = 0;
+    }
+
+  return TRUE;
+}
+
+int
+gsk_resource_cache_collect_items (GskResourceCache *cache)
+{
+  GskResourceCachePrivate *priv = gsk_resource_cache_get_instance_private (cache);
+  GHashTableIter iter;
+  gpointer item_p;
+  int res = 0;
+
+  g_return_val_if_fail (GSK_IS_RESOURCE_CACHE (cache), 0);
+
+  if (priv->resources == NULL)
+    return 0;
+
+  g_hash_table_iter_init (&iter, priv->resources);
+  while (g_hash_table_iter_next (&iter, NULL, &item_p))
+    {
+      ResourceCacheItem *item = item_p;
+
+      if (item->age == 0)
+        {
+          g_hash_table_iter_remove (&iter);
+          res += 1;
+          continue;
+        }
+
+      item->age -= 1;
+    }
+
+  return res;
+}
diff --git a/gsk/gskresourcecache.h b/gsk/gskresourcecache.h
new file mode 100644
index 0000000..fbdb780
--- /dev/null
+++ b/gsk/gskresourcecache.h
@@ -0,0 +1,73 @@
+/* GSK - The GTK Scene Kit
+ *
+ * Copyright 2016  Endless
+ *
+ * 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 2 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GSK_RESOURCE_CACHE_H__
+#define __GSK_RESOURCE_CACHE_H__
+
+#if !defined (__GSK_H_INSIDE__) && !defined (GSK_COMPILATION)
+#error "Only <gsk/gsk.h> can be included directly."
+#endif
+
+#include <gsk/gsktypes.h>
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_RESOURCE_CACHE (gsk_resource_cache_get_type())
+
+#define GSK_RESOURCE_CACHE(obj)         (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_RESOURCE_CACHE, 
GskResourceCache))
+#define GSK_IS_RESOURCE_CACHE(obj)      (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_RESOURCE_CACHE))
+
+typedef struct _GskResourceCache        GskResourceCache;
+typedef struct _GskResourceCacheClass   GskResourceCacheClass;
+
+GDK_AVAILABLE_IN_3_22
+GType gsk_resource_cache_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_22
+void            gsk_resource_cache_set_hash_func        (GskResourceCache *cache,
+                                                         GHashFunc         key_hash);
+GDK_AVAILABLE_IN_3_22
+void            gsk_resource_cache_set_equal_func       (GskResourceCache *cache,
+                                                         GEqualFunc        key_equal);
+GDK_AVAILABLE_IN_3_22
+void            gsk_resource_cache_set_free_func        (GskResourceCache *cache,
+                                                         GDestroyNotify    key_notify);
+GDK_AVAILABLE_IN_3_22
+void            gsk_resource_cache_set_value_type       (GskResourceCache *cache,
+                                                         GType             value_type);
+GDK_AVAILABLE_IN_3_22
+void            gsk_resource_cache_set_name             (GskResourceCache *cache,
+                                                         const char       *name);
+
+GDK_AVAILABLE_IN_3_22
+void            gsk_resource_cache_add_item             (GskResourceCache *cache,
+                                                         gpointer          key,
+                                                         gpointer          value);
+GDK_AVAILABLE_IN_3_22
+gboolean        gsk_resource_cache_has_item             (GskResourceCache *cache,
+                                                         gpointer          key);
+GDK_AVAILABLE_IN_3_22
+gpointer        gsk_resource_cache_get_item             (GskResourceCache *cache,
+                                                         gpointer          key);
+GDK_AVAILABLE_IN_3_22
+gboolean        gsk_resource_cache_invalidate_item      (GskResourceCache *cache,
+                                                         gpointer          key);
+
+G_END_DECLS
+
+#endif /* __GSK_RESOURCE_CACHE_H__ */
diff --git a/gsk/gskresourcecacheprivate.h b/gsk/gskresourcecacheprivate.h
new file mode 100644
index 0000000..6daa909
--- /dev/null
+++ b/gsk/gskresourcecacheprivate.h
@@ -0,0 +1,26 @@
+#ifndef __GSK_RESOURCE_CACHE_PRIVATE_H__
+#define __GSK_RESOURCE_CACHE_PRIVATE_H__
+
+#include "gskresourcecache.h"
+
+G_BEGIN_DECLS
+
+#define GSK_RESOURCE_CACHE_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_RESOURCE_CACHE, 
GskResourceCacheClass))
+#define GSK_IS_RESOURCE_CACHE_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_RESOURCE_CACHE))
+#define GSK_RESOURCE_CACHE_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_RESOURCE_CACHE, 
GskResourceCacheClass))
+
+struct _GskResourceCache
+{
+  GObject parent_instance;
+};
+
+struct _GskResourceCacheClass
+{
+  GObjectClass parent_class;
+};
+
+int             gsk_resource_cache_collect_items        (GskResourceCache *cache);
+
+G_END_DECLS
+
+#endif /* __GSK_RESOURCE_CACHE_PRIVATE_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]