diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index f50a27ad2..7a9e0eaf1 100644
--- a/cogl/Makefile.am
+++ b/cogl/Makefile.am
@@ -256,6 +256,8 @@ cogl_sources_c = \
$(srcdir)/cogl-pipeline-vertend-fixed-private.h \
$(srcdir)/cogl-pipeline-progend-glsl.c \
$(srcdir)/cogl-pipeline-progend-glsl-private.h \
+ $(srcdir)/cogl-pipeline-cache.h \
+ $(srcdir)/cogl-pipeline-cache.c \
$(srcdir)/cogl-material-compat.c \
$(srcdir)/cogl-program.c \
$(srcdir)/cogl-program-private.h \
diff --git a/cogl/cogl-context-private.h b/cogl/cogl-context-private.h
index 8ffcba19d..b4adebd6b 100644
--- a/cogl/cogl-context-private.h
+++ b/cogl/cogl-context-private.h
@@ -42,6 +42,7 @@
#include "cogl-bitmask.h"
#include "cogl-atlas.h"
#include "cogl-texture-driver.h"
+#include "cogl-pipeline-cache.h"
typedef struct
{
@@ -106,9 +107,7 @@ struct _CoglContext
int legacy_state_set;
-#ifdef HAVE_COGL_GL
- GHashTable *arbfp_cache;
-#endif
+ CoglPipelineCache *pipeline_cache;
/* Textures */
CoglHandle default_gl_texture_2d_tex;
diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c
index 757bbcdf8..4ab37e25b 100644
--- a/cogl/cogl-context.c
+++ b/cogl/cogl-context.c
@@ -288,10 +288,7 @@ cogl_context_new (CoglDisplay *display,
context->legacy_depth_test_enabled = FALSE;
-#ifdef HAVE_COGL_GL
- _context->arbfp_cache = g_hash_table_new (_cogl_pipeline_fragend_arbfp_hash,
- _cogl_pipeline_fragend_arbfp_equal);
-#endif
+ context->pipeline_cache = cogl_pipeline_cache_new ();
for (i = 0; i < COGL_BUFFER_BIND_TARGET_COUNT; i++)
context->current_buffer[i] = NULL;
@@ -468,9 +465,7 @@ _cogl_context_free (CoglContext *context)
cogl_object_unref (_context->flushed_projection_stack);
#endif
-#ifdef HAVE_COGL_GL
- g_hash_table_unref (context->arbfp_cache);
-#endif
+ cogl_pipeline_cache_free (context->pipeline_cache);
g_byte_array_free (context->buffer_map_fallback_array, TRUE);
diff --git a/cogl/cogl-pipeline-cache.c b/cogl/cogl-pipeline-cache.c
new file mode 100644
index 000000000..f5e32a77d
--- /dev/null
+++ b/cogl/cogl-pipeline-cache.c
@@ -0,0 +1,146 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2011 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
+ * .
+ *
+ *
+ * Authors:
+ * Neil Roberts
+ * Robert Bragg
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-pipeline-private.h"
+#include "cogl-pipeline-cache.h"
+#include "cogl-context-private.h"
+
+struct _CoglPipelineCache
+{
+ GHashTable *fragment_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 gboolean
+pipeline_fragment_equal (const void *a, const void *b)
+{
+ 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_equal ((CoglPipeline *)a, (CoglPipeline *)b,
+ fragment_state, layer_fragment_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);
+
+ return cache;
+}
+
+void
+cogl_pipeline_cache_free (CoglPipelineCache *cache)
+{
+ g_hash_table_destroy (cache->fragment_hash);
+ g_free (cache);
+}
+
+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 gboolean 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;
+}
diff --git a/cogl/cogl-pipeline-cache.h b/cogl/cogl-pipeline-cache.h
new file mode 100644
index 000000000..57f80ae8e
--- /dev/null
+++ b/cogl/cogl-pipeline-cache.h
@@ -0,0 +1,50 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2011 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 .
+ *
+ *
+ */
+
+#ifndef __COGL_PIPELINE_CACHE_H__
+#define __COGL_PIPELINE_CACHE_H__
+
+#include "cogl-pipeline.h"
+
+typedef struct _CoglPipelineCache CoglPipelineCache;
+
+CoglPipelineCache *
+cogl_pipeline_cache_new (void);
+
+void
+cogl_pipeline_cache_free (CoglPipelineCache *cache);
+
+/*
+ * Gets a pipeline from the cache that has the same state as
+ * @key_pipeline for the state in
+ * COGL_PIPELINE_STATE_AFFECTS_FRAGMENT_CODEGEN. If there is no
+ * matching pipline already then a copy of key_pipeline is stored in
+ * the cache 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_cache_get_fragment_template (CoglPipelineCache *cache,
+ CoglPipeline *key_pipeline);
+
+#endif /* __COGL_PIPELINE_CACHE_H__ */
diff --git a/cogl/cogl-pipeline-fragend-arbfp-private.h b/cogl/cogl-pipeline-fragend-arbfp-private.h
index 168bbe976..06c625ce2 100644
--- a/cogl/cogl-pipeline-fragend-arbfp-private.h
+++ b/cogl/cogl-pipeline-fragend-arbfp-private.h
@@ -32,12 +32,5 @@
extern const CoglPipelineFragend _cogl_pipeline_arbfp_fragend;
-unsigned int
-_cogl_pipeline_fragend_arbfp_hash (const void *pipeline);
-
-gboolean
-_cogl_pipeline_fragend_arbfp_equal (const void *pipeline0,
- const void *pipeline1);
-
#endif /* __COGL_PIPELINE_ARBFP_PRIVATE_H */
diff --git a/cogl/cogl-pipeline-fragend-arbfp.c b/cogl/cogl-pipeline-fragend-arbfp.c
index da0907ff2..aed5d9698 100644
--- a/cogl/cogl-pipeline-fragend-arbfp.c
+++ b/cogl/cogl-pipeline-fragend-arbfp.c
@@ -69,9 +69,6 @@ typedef struct
{
int ref_count;
- /* XXX: only valid during codegen */
- CoglPipeline *arbfp_authority;
-
CoglHandle user_program;
/* XXX: only valid during codegen */
GString *source;
@@ -155,6 +152,7 @@ _cogl_pipeline_fragend_arbfp_start (CoglPipeline *pipeline,
{
CoglPipelineShaderState *shader_state;
CoglPipeline *authority;
+ CoglPipeline *template_pipeline;
CoglHandle user_program;
_COGL_GET_CONTEXT (ctx, FALSE);
@@ -184,8 +182,7 @@ _cogl_pipeline_fragend_arbfp_start (CoglPipeline *pipeline,
return FALSE;
}
- /* Now lookup our ARBfp backend private state (allocating if
- * necessary) */
+ /* Now lookup our ARBfp backend private state */
shader_state = get_shader_state (pipeline);
/* If we have a valid shader_state then we are all set and don't
@@ -219,113 +216,73 @@ _cogl_pipeline_fragend_arbfp_start (CoglPipeline *pipeline,
/* If we haven't yet found an existing program then before we resort to
* generating a new arbfp program we see if we can find a suitable
- * program in the arbfp_cache. */
+ * program in the pipeline_cache. */
if (G_LIKELY (!(COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_PROGRAM_CACHES))))
{
- shader_state = g_hash_table_lookup (ctx->arbfp_cache, authority);
- if (shader_state)
- {
- shader_state->ref_count++;
- set_shader_state (pipeline, shader_state);
+ template_pipeline =
+ _cogl_pipeline_cache_get_fragment_template (ctx->pipeline_cache,
+ authority);
- /* Since we have already resolved the arbfp-authority at this point
- * we might as well also associate any program we find from the cache
- * with the authority too... */
- if (authority != pipeline)
+ shader_state = get_shader_state (template_pipeline);
+
+ if (shader_state)
+ shader_state->ref_count++;
+ }
+
+ /* If we still haven't got a shader state then we'll have to create
+ a new one */
+ if (shader_state == NULL)
+ {
+ shader_state = shader_state_new (n_layers);
+
+ shader_state->user_program = user_program;
+ if (user_program == COGL_INVALID_HANDLE)
+ {
+ int i;
+
+ /* We reuse a single grow-only GString for code-gen */
+ g_string_set_size (ctx->codegen_source_buffer, 0);
+ shader_state->source = ctx->codegen_source_buffer;
+ g_string_append (shader_state->source,
+ "!!ARBfp1.0\n"
+ "TEMP output;\n"
+ "TEMP tmp0, tmp1, tmp2, tmp3, tmp4;\n"
+ "PARAM half = {.5, .5, .5, .5};\n"
+ "PARAM one = {1, 1, 1, 1};\n"
+ "PARAM two = {2, 2, 2, 2};\n"
+ "PARAM minus_one = {-1, -1, -1, -1};\n");
+
+ for (i = 0; i < n_layers; i++)
{
- shader_state->ref_count++;
- set_shader_state (authority, shader_state);
+ shader_state->unit_state[i].sampled = FALSE;
+ shader_state->unit_state[i].dirty_combine_constant = FALSE;
}
- return TRUE;
+ shader_state->next_constant_id = 0;
}
}
- /* If we still haven't found an existing program then start
- * generating code for a new program...
- */
-
- shader_state = shader_state_new (n_layers);
set_shader_state (pipeline, shader_state);
- /* Since we have already resolved the arbfp-authority at this point we might
- * as well also associate any program we generate with the authority too...
- */
+ /* Since we have already resolved the arbfp-authority at this point
+ * we might as well also associate any program we find from the cache
+ * with the authority too... */
if (authority != pipeline)
{
shader_state->ref_count++;
set_shader_state (authority, shader_state);
}
- shader_state->user_program = user_program;
- if (user_program == COGL_INVALID_HANDLE)
+ /* If we found a template then we'll attach it to that too so that
+ next time a similar pipeline is used it can use the same state */
+ if (template_pipeline)
{
- int i;
-
- /* We reuse a single grow-only GString for code-gen */
- g_string_set_size (ctx->codegen_source_buffer, 0);
- shader_state->source = ctx->codegen_source_buffer;
- g_string_append (shader_state->source,
- "!!ARBfp1.0\n"
- "TEMP output;\n"
- "TEMP tmp0, tmp1, tmp2, tmp3, tmp4;\n"
- "PARAM half = {.5, .5, .5, .5};\n"
- "PARAM one = {1, 1, 1, 1};\n"
- "PARAM two = {2, 2, 2, 2};\n"
- "PARAM minus_one = {-1, -1, -1, -1};\n");
-
- /* At the end of code-gen we'll add the program to a cache and
- * we'll use the authority pipeline as the basis for key into
- * that cache... */
- shader_state->arbfp_authority = authority;
-
- for (i = 0; i < n_layers; i++)
- {
- shader_state->unit_state[i].sampled = FALSE;
- shader_state->unit_state[i].dirty_combine_constant = FALSE;
- }
- shader_state->next_constant_id = 0;
+ shader_state->ref_count++;
+ set_shader_state (template_pipeline, shader_state);
}
return TRUE;
}
-unsigned int
-_cogl_pipeline_fragend_arbfp_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);
-}
-
-gboolean
-_cogl_pipeline_fragend_arbfp_equal (const void *a, const void *b)
-{
- 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_equal ((CoglPipeline *)a, (CoglPipeline *)b,
- fragment_state, layer_fragment_state,
- 0);
-}
-
static const char *
gl_target_to_arbfp_string (GLenum gl_target)
{
@@ -889,51 +846,6 @@ _cogl_pipeline_fragend_arbfp_end (CoglPipeline *pipeline,
}
shader_state->source = NULL;
-
- if (G_LIKELY (!(COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_PROGRAM_CACHES))))
- {
- CoglPipeline *key;
-
- /* 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
- * _cogl_pipeline_fragend_arbfp_backend_start 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 arbfp program and references small dummy
- * textures instead of potentially large user textures. */
- key = cogl_pipeline_copy (shader_state->arbfp_authority);
- shader_state->ref_count++;
- g_hash_table_insert (ctx->arbfp_cache, key, shader_state);
- if (G_UNLIKELY (g_hash_table_size (ctx->arbfp_cache) > 50))
- {
- static gboolean seen = FALSE;
- if (!seen)
- g_warning ("Over 50 separate ARBfp programs have been "
- "generated which is very unusual, so something "
- "is probably wrong!\n");
- seen = TRUE;
- }
- }
-
- /* The authority is only valid during codegen since the program
- * state may have a longer lifetime than the original authority
- * it is created for. */
- shader_state->arbfp_authority = NULL;
}
if (shader_state->user_program != COGL_INVALID_HANDLE)