summaryrefslogtreecommitdiff
path: root/cogl/cogl-pipeline-hash-table.c
diff options
context:
space:
mode:
Diffstat (limited to 'cogl/cogl-pipeline-hash-table.c')
-rw-r--r--cogl/cogl-pipeline-hash-table.c157
1 files changed, 157 insertions, 0 deletions
diff --git a/cogl/cogl-pipeline-hash-table.c b/cogl/cogl-pipeline-hash-table.c
new file mode 100644
index 00000000..d3769f3c
--- /dev/null
+++ b/cogl/cogl-pipeline-hash-table.c
@@ -0,0 +1,157 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2013 Intel Corporation.
+ *
+ * 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/>.
+ *
+ *
+ * Authors:
+ * Neil Roberts <neil@linux.intel.com>
+ * Robert Bragg <robert@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-context-private.h"
+#include "cogl-pipeline-private.h"
+#include "cogl-pipeline-hash-table.h"
+
+typedef struct
+{
+ /* The template pipeline */
+ CoglPipeline *pipeline;
+
+ /* GHashTable annoyingly doesn't let us pass a user data pointer to
+ * the hash and equal functions so to work around it we have to
+ * store the pointer in every hash table entry. We will use this
+ * entry as both the key and the value */
+ CoglPipelineHashTable *hash;
+} CoglPipelineHashTableEntry;
+
+static void
+value_destroy_cb (void *value)
+{
+ CoglPipelineHashTableEntry *entry = value;
+
+ cogl_object_unref (entry->pipeline);
+
+ g_slice_free (CoglPipelineHashTableEntry, entry);
+}
+
+static unsigned int
+entry_hash (const void *data)
+{
+ const CoglPipelineHashTableEntry *entry = data;
+ const CoglPipelineHashTable *hash = entry->hash;
+
+ return _cogl_pipeline_hash (entry->pipeline,
+ hash->main_state,
+ hash->layer_state,
+ 0);
+}
+
+static CoglBool
+entry_equal (const void *a,
+ const void *b)
+{
+ const CoglPipelineHashTableEntry *entry_a = a;
+ const CoglPipelineHashTableEntry *entry_b = b;
+ const CoglPipelineHashTable *hash = entry_a->hash;
+
+ return _cogl_pipeline_equal (entry_a->pipeline,
+ entry_b->pipeline,
+ hash->main_state,
+ hash->layer_state,
+ 0);
+}
+
+void
+_cogl_pipeline_hash_table_init (CoglPipelineHashTable *hash,
+ unsigned int main_state,
+ unsigned int layer_state,
+ const char *debug_string)
+{
+ hash->n_unique_pipelines = 0;
+ hash->debug_string = debug_string;
+ hash->main_state = main_state;
+ hash->layer_state = layer_state;
+ hash->table = g_hash_table_new_full (entry_hash,
+ entry_equal,
+ NULL, /* key destroy */
+ value_destroy_cb);
+}
+
+void
+_cogl_pipeline_hash_table_destroy (CoglPipelineHashTable *hash)
+{
+ g_hash_table_destroy (hash->table);
+}
+
+CoglPipeline *
+_cogl_pipeline_hash_table_get (CoglPipelineHashTable *hash,
+ CoglPipeline *key_pipeline)
+{
+ CoglPipelineHashTableEntry dummy_entry;
+ CoglPipelineHashTableEntry *entry;
+ unsigned int copy_state;
+
+ dummy_entry.pipeline = key_pipeline;
+ dummy_entry.hash = hash;
+
+ entry = g_hash_table_lookup (hash->table, &dummy_entry);
+
+ if (entry)
+ return entry->pipeline;
+
+ if (hash->n_unique_pipelines == 50)
+ g_warning ("Over 50 separate %s have been generated which is very "
+ "unusual, so something is probably wrong!\n",
+ hash->debug_string);
+
+ /* XXX: I wish there was a way to insert into a GHashTable with a
+ * pre-calculated hash value since there is a cost to calculating
+ * the hash of a CoglPipeline and in this case we know we have
+ * already called _cogl_pipeline_hash during the lookup so we could
+ * pass the value through to here to avoid hashing it again.
+ */
+
+ /* XXX: Any keys referenced by the hash table need to remain valid
+ * all the while that there are corresponding values, so for now we
+ * simply make a copy of the current authority pipeline.
+ *
+ * FIXME: A problem with this is that our key into the cache may
+ * hold references to some arbitrary user textures which will now be
+ * kept alive indefinitly which is a shame. A better solution will
+ * be to derive a special "key pipeline" from the authority which
+ * derives from the base Cogl pipeline (to avoid affecting the
+ * lifetime of any other pipelines) and only takes a copy of the
+ * state that relates to the fragment shader and references small
+ * dummy textures instead of potentially large user textures.
+ */
+ entry = g_slice_new (CoglPipelineHashTableEntry);
+ entry->pipeline = cogl_pipeline_copy (key_pipeline);
+ entry->hash = hash;
+
+ g_hash_table_insert (hash->table, entry, entry);
+
+ hash->n_unique_pipelines++;
+
+ return entry->pipeline;
+}