diff options
-rw-r--r-- | cogl/Makefile.am | 2 | ||||
-rw-r--r-- | cogl/cogl-pipeline-cache.c | 241 | ||||
-rw-r--r-- | cogl/cogl-pipeline-hash-table.c | 157 | ||||
-rw-r--r-- | cogl/cogl-pipeline-hash-table.h | 69 |
4 files changed, 263 insertions, 206 deletions
diff --git a/cogl/Makefile.am b/cogl/Makefile.am index 33214abb..5dee0988 100644 --- a/cogl/Makefile.am +++ b/cogl/Makefile.am @@ -349,6 +349,8 @@ cogl_sources_c = \ $(srcdir)/cogl-pipeline-snippet.c \ $(srcdir)/cogl-pipeline-cache.h \ $(srcdir)/cogl-pipeline-cache.c \ + $(srcdir)/cogl-pipeline-hash-table.h \ + $(srcdir)/cogl-pipeline-hash-table.c \ $(srcdir)/cogl-material-compat.c \ $(srcdir)/cogl-program.c \ $(srcdir)/cogl-program-private.h \ diff --git a/cogl/cogl-pipeline-cache.c b/cogl/cogl-pipeline-cache.c index fab3614f..df4c4338 100644 --- a/cogl/cogl-pipeline-cache.c +++ b/cogl/cogl-pipeline-cache.c @@ -3,7 +3,7 @@ * * An object oriented GL/GLES Abstraction/Utility Layer * - * Copyright (C) 2011 Intel Corporation. + * Copyright (C) 2011, 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 @@ -32,133 +32,47 @@ #include "cogl-context-private.h" #include "cogl-pipeline-private.h" #include "cogl-pipeline-cache.h" +#include "cogl-pipeline-hash-table.h" struct _CoglPipelineCache { - GHashTable *fragment_hash; - GHashTable *vertex_hash; - GHashTable *combined_hash; + CoglPipelineHashTable fragment_hash; + CoglPipelineHashTable vertex_hash; + CoglPipelineHashTable combined_hash; }; -static unsigned int -pipeline_fragment_hash (const void *data) -{ - unsigned int fragment_state; - unsigned int layer_fragment_state; - - _COGL_GET_CONTEXT (ctx, 0); - - fragment_state = - _cogl_pipeline_get_state_for_fragment_codegen (ctx); - layer_fragment_state = - _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx); - - return _cogl_pipeline_hash ((CoglPipeline *)data, - fragment_state, layer_fragment_state, - 0); -} - -static CoglBool -pipeline_fragment_equal (const void *a, const void *b) +CoglPipelineCache * +_cogl_pipeline_cache_new (void) { + CoglPipelineCache *cache = g_new (CoglPipelineCache, 1); + unsigned long vertex_state; + unsigned long layer_vertex_state; unsigned int fragment_state; unsigned int layer_fragment_state; _COGL_GET_CONTEXT (ctx, 0); + vertex_state = + COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN; + layer_vertex_state = + COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN; fragment_state = _cogl_pipeline_get_state_for_fragment_codegen (ctx); layer_fragment_state = _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx); - return _cogl_pipeline_equal ((CoglPipeline *)a, (CoglPipeline *)b, - fragment_state, layer_fragment_state, - 0); -} - -static unsigned int -pipeline_vertex_hash (const void *data) -{ - unsigned long vertex_state = - COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN; - unsigned long layer_vertex_state = - COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN; - - return _cogl_pipeline_hash ((CoglPipeline *)data, - vertex_state, layer_vertex_state, - 0); -} - -static CoglBool -pipeline_vertex_equal (const void *a, const void *b) -{ - unsigned long vertex_state = - COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN; - unsigned long layer_vertex_state = - COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN; - - return _cogl_pipeline_equal ((CoglPipeline *)a, (CoglPipeline *)b, - vertex_state, layer_vertex_state, - 0); -} - -static unsigned int -pipeline_combined_hash (const void *data) -{ - unsigned int combined_state; - unsigned int layer_combined_state; - - _COGL_GET_CONTEXT (ctx, 0); - - combined_state = - _cogl_pipeline_get_state_for_fragment_codegen (ctx) | - COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN; - layer_combined_state = - _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx) | - COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN; - - return _cogl_pipeline_hash ((CoglPipeline *)data, - combined_state, layer_combined_state, - 0); -} - -static CoglBool -pipeline_combined_equal (const void *a, const void *b) -{ - unsigned int combined_state; - unsigned int layer_combined_state; - - _COGL_GET_CONTEXT (ctx, 0); - - combined_state = - _cogl_pipeline_get_state_for_fragment_codegen (ctx) | - COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN; - layer_combined_state = - _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx) | - COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN; - - return _cogl_pipeline_equal ((CoglPipeline *)a, (CoglPipeline *)b, - combined_state, layer_combined_state, - 0); -} - -CoglPipelineCache * -_cogl_pipeline_cache_new (void) -{ - CoglPipelineCache *cache = g_new (CoglPipelineCache, 1); - - cache->fragment_hash = g_hash_table_new_full (pipeline_fragment_hash, - pipeline_fragment_equal, - cogl_object_unref, - cogl_object_unref); - cache->vertex_hash = g_hash_table_new_full (pipeline_vertex_hash, - pipeline_vertex_equal, - cogl_object_unref, - cogl_object_unref); - cache->combined_hash = g_hash_table_new_full (pipeline_combined_hash, - pipeline_combined_equal, - cogl_object_unref, - cogl_object_unref); + _cogl_pipeline_hash_table_init (&cache->vertex_hash, + vertex_state, + layer_vertex_state, + "vertex shaders"); + _cogl_pipeline_hash_table_init (&cache->fragment_hash, + fragment_state, + layer_fragment_state, + "fragment shaders"); + _cogl_pipeline_hash_table_init (&cache->combined_hash, + vertex_state | fragment_state, + layer_vertex_state | layer_fragment_state, + "programs"); return cache; } @@ -166,9 +80,9 @@ _cogl_pipeline_cache_new (void) void _cogl_pipeline_cache_free (CoglPipelineCache *cache) { - g_hash_table_destroy (cache->fragment_hash); - g_hash_table_destroy (cache->vertex_hash); - g_hash_table_destroy (cache->combined_hash); + _cogl_pipeline_hash_table_destroy (&cache->fragment_hash); + _cogl_pipeline_hash_table_destroy (&cache->vertex_hash); + _cogl_pipeline_hash_table_destroy (&cache->combined_hash); g_free (cache); } @@ -176,107 +90,22 @@ CoglPipeline * _cogl_pipeline_cache_get_fragment_template (CoglPipelineCache *cache, CoglPipeline *key_pipeline) { - CoglPipeline *template = - g_hash_table_lookup (cache->fragment_hash, key_pipeline); - - if (template == NULL) - { - /* 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. */ - template = cogl_pipeline_copy (key_pipeline); - - g_hash_table_insert (cache->fragment_hash, - template, - cogl_object_ref (template)); - - if (G_UNLIKELY (g_hash_table_size (cache->fragment_hash) > 50)) - { - static CoglBool seen = FALSE; - if (!seen) - g_warning ("Over 50 separate fragment shaders have been " - "generated which is very unusual, so something " - "is probably wrong!\n"); - seen = TRUE; - } - } - - return template; + return _cogl_pipeline_hash_table_get (&cache->fragment_hash, + key_pipeline); } CoglPipeline * _cogl_pipeline_cache_get_vertex_template (CoglPipelineCache *cache, CoglPipeline *key_pipeline) { - CoglPipeline *template = - g_hash_table_lookup (cache->vertex_hash, key_pipeline); - - if (template == NULL) - { - template = cogl_pipeline_copy (key_pipeline); - - g_hash_table_insert (cache->vertex_hash, - template, - cogl_object_ref (template)); - - if (G_UNLIKELY (g_hash_table_size (cache->vertex_hash) > 50)) - { - static CoglBool seen = FALSE; - if (!seen) - g_warning ("Over 50 separate vertex shaders have been " - "generated which is very unusual, so something " - "is probably wrong!\n"); - seen = TRUE; - } - } - - return template; + return _cogl_pipeline_hash_table_get (&cache->vertex_hash, + key_pipeline); } CoglPipeline * _cogl_pipeline_cache_get_combined_template (CoglPipelineCache *cache, CoglPipeline *key_pipeline) { - CoglPipeline *template = - g_hash_table_lookup (cache->combined_hash, key_pipeline); - - if (template == NULL) - { - template = cogl_pipeline_copy (key_pipeline); - - g_hash_table_insert (cache->combined_hash, - template, - cogl_object_ref (template)); - - if (G_UNLIKELY (g_hash_table_size (cache->combined_hash) > 50)) - { - static CoglBool seen = FALSE; - if (!seen) - g_warning ("Over 50 separate programs have been " - "generated which is very unusual, so something " - "is probably wrong!\n"); - seen = TRUE; - } - } - - return template; + return _cogl_pipeline_hash_table_get (&cache->combined_hash, + key_pipeline); } 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; +} diff --git a/cogl/cogl-pipeline-hash-table.h b/cogl/cogl-pipeline-hash-table.h new file mode 100644 index 00000000..1b0a0d93 --- /dev/null +++ b/cogl/cogl-pipeline-hash-table.h @@ -0,0 +1,69 @@ +/* + * 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/>. + * + * + */ + +#ifndef __COGL_PIPELINE_HASH_H__ +#define __COGL_PIPELINE_HASH_H__ + +#include "cogl-pipeline.h" + +typedef struct +{ + /* Total number of pipelines that were ever added to the hash. This + * is not decremented when a pipeline is removed. It is only used to + * generate a warning if an unusually high number of pipelines are + * generated */ + int n_unique_pipelines; + + /* String that will be used to describe the usage of this hash table + * in the debug warning when too many pipelines are generated. This + * must be a static string because it won't be copied or freed */ + const char *debug_string; + + unsigned int main_state; + unsigned int layer_state; + + GHashTable *table; +} CoglPipelineHashTable; + +void +_cogl_pipeline_hash_table_init (CoglPipelineHashTable *hash, + unsigned int main_state, + unsigned int layer_state, + const char *debug_string); + +void +_cogl_pipeline_hash_table_destroy (CoglPipelineHashTable *hash); + +/* + * Gets a pipeline from the hash that has the same state as + * @key_pipeline according to the limited state bits passed to + * _cogl_pipeline_hash_table_init(). If there is no matching pipelines + * already then a copy of key_pipeline is stored in the hash so that + * it will be used next time the function is called with a similar + * pipeline. In that case the copy itself will be returned + */ +CoglPipeline * +_cogl_pipeline_hash_table_get (CoglPipelineHashTable *hash, + CoglPipeline *key_pipeline); + +#endif /* __COGL_PIPELINE_HASH_H__ */ |