summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeil Roberts <neil@linux.intel.com>2011-06-30 18:34:05 +0100
committerNeil Roberts <neil@linux.intel.com>2011-07-13 12:30:07 +0100
commitf3b90d1717173568261070cdb693efdbf18ea551 (patch)
tree04131fd98a8b4b59cd18044c76236da3d52a9bed
parent461bff18671e582efacae6a24e5a508e81cb4af9 (diff)
downloadcogl-f3b90d1717173568261070cdb693efdbf18ea551.tar.gz
cogl-pipeline: Use the pipeline cache for the GLSL backends
The CoglPipelineCache is now extended to store templates for state affecting vertex shaders and combined programs. The GLSL fragend, vertend and progend now uses this to get cached shaders and a program. When a new pipeline is created it will now get hashed three times if the GLSL backends are in use (once for the fragend, once for the vertend and once for the progend). Ideally we should add some way for the progend to check its cache before the fragends and vertends are checked so that it can bypass them entirely if it can find a cached combined program.
-rw-r--r--cogl/cogl-pipeline-cache.c90
-rw-r--r--cogl/cogl-pipeline-cache.h27
-rw-r--r--cogl/cogl-pipeline-fragend-glsl.c26
-rw-r--r--cogl/cogl-pipeline-progend-glsl.c28
-rw-r--r--cogl/cogl-pipeline-vertend-glsl.c25
5 files changed, 192 insertions, 4 deletions
diff --git a/cogl/cogl-pipeline-cache.c b/cogl/cogl-pipeline-cache.c
index f5e32a77..c2bca818 100644
--- a/cogl/cogl-pipeline-cache.c
+++ b/cogl/cogl-pipeline-cache.c
@@ -36,6 +36,7 @@
struct _CoglPipelineCache
{
GHashTable *fragment_hash;
+ GHashTable *vertex_hash;
};
static unsigned int
@@ -74,6 +75,32 @@ pipeline_fragment_equal (const void *a, const void *b)
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 gboolean
+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);
+}
+
CoglPipelineCache *
cogl_pipeline_cache_new (void)
{
@@ -83,6 +110,10 @@ cogl_pipeline_cache_new (void)
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);
return cache;
}
@@ -91,6 +122,7 @@ void
cogl_pipeline_cache_free (CoglPipelineCache *cache)
{
g_hash_table_destroy (cache->fragment_hash);
+ g_hash_table_destroy (cache->vertex_hash);
g_free (cache);
}
@@ -144,3 +176,61 @@ _cogl_pipeline_cache_get_fragment_template (CoglPipelineCache *cache,
return template;
}
+
+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 gboolean 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;
+}
+
+CoglPipeline *
+_cogl_pipeline_cache_get_combined_template (CoglPipelineCache *cache,
+ CoglPipeline *key_pipeline)
+{
+ unsigned int pipeline_state_for_fragment_codegen;
+ unsigned int pipeline_layer_state_for_fragment_codegen;
+
+ _COGL_GET_CONTEXT (ctx, NULL);
+
+ pipeline_state_for_fragment_codegen =
+ _cogl_pipeline_get_state_for_fragment_codegen (ctx);
+ pipeline_layer_state_for_fragment_codegen =
+ _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx);
+
+ /* Currently the vertex shader state is a subset of the fragment
+ shader state so we can avoid a third hash table here by just
+ using the fragment shader table. This assert should catch it if
+ that ever changes */
+
+ g_assert ((pipeline_state_for_fragment_codegen |
+ COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN) ==
+ pipeline_state_for_fragment_codegen);
+ g_assert ((pipeline_layer_state_for_fragment_codegen |
+ COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN) ==
+ pipeline_layer_state_for_fragment_codegen);
+
+ return _cogl_pipeline_cache_get_fragment_template (cache, key_pipeline);
+}
diff --git a/cogl/cogl-pipeline-cache.h b/cogl/cogl-pipeline-cache.h
index 57f80ae8..c9a5d6a8 100644
--- a/cogl/cogl-pipeline-cache.h
+++ b/cogl/cogl-pipeline-cache.h
@@ -47,4 +47,31 @@ CoglPipeline *
_cogl_pipeline_cache_get_fragment_template (CoglPipelineCache *cache,
CoglPipeline *key_pipeline);
+/*
+ * Gets a pipeline from the cache that has the same state as
+ * @key_pipeline for the state in
+ * COGL_PIPELINE_STATE_AFFECTS_VERTEX_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_vertex_template (CoglPipelineCache *cache,
+ CoglPipeline *key_pipeline);
+
+/*
+ * Gets a pipeline from the cache that has the same state as
+ * @key_pipeline for the combination of the state state in
+ * COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN and
+ * 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_combined_template (CoglPipelineCache *cache,
+ CoglPipeline *key_pipeline);
+
#endif /* __COGL_PIPELINE_CACHE_H__ */
diff --git a/cogl/cogl-pipeline-fragend-glsl.c b/cogl/cogl-pipeline-fragend-glsl.c
index 35e43e9e..90258601 100644
--- a/cogl/cogl-pipeline-fragend-glsl.c
+++ b/cogl/cogl-pipeline-fragend-glsl.c
@@ -44,6 +44,7 @@
#include "cogl-handle.h"
#include "cogl-shader-private.h"
#include "cogl-program-private.h"
+#include "cogl-pipeline-cache.h"
#include <glib.h>
@@ -154,6 +155,7 @@ _cogl_pipeline_fragend_glsl_start (CoglPipeline *pipeline,
{
CoglPipelineShaderState *shader_state;
CoglPipeline *authority;
+ CoglPipeline *template_pipeline = NULL;
CoglProgram *user_program;
int i;
@@ -197,8 +199,30 @@ _cogl_pipeline_fragend_glsl_start (CoglPipeline *pipeline,
*/
if (shader_state == NULL)
{
- shader_state = shader_state_new (n_layers);
+ /* Check if there is already a similar cached pipeline whose
+ shader state we can share */
+ if (G_LIKELY (!(COGL_DEBUG_ENABLED
+ (COGL_DEBUG_DISABLE_PROGRAM_CACHES))))
+ {
+ template_pipeline =
+ _cogl_pipeline_cache_get_fragment_template (ctx->pipeline_cache,
+ authority);
+
+ shader_state = get_shader_state (template_pipeline);
+ }
+
+ if (shader_state)
+ shader_state->ref_count++;
+ else
+ shader_state = shader_state_new (n_layers);
+
set_shader_state (authority, shader_state);
+
+ if (template_pipeline)
+ {
+ shader_state->ref_count++;
+ set_shader_state (template_pipeline, shader_state);
+ }
}
/* If the pipeline isn't actually its own glsl-authority
diff --git a/cogl/cogl-pipeline-progend-glsl.c b/cogl/cogl-pipeline-progend-glsl.c
index 9eb1d7cb..4a6fa78e 100644
--- a/cogl/cogl-pipeline-progend-glsl.c
+++ b/cogl/cogl-pipeline-progend-glsl.c
@@ -41,6 +41,7 @@
#include "cogl-program-private.h"
#include "cogl-pipeline-fragend-glsl-private.h"
#include "cogl-pipeline-vertend-glsl-private.h"
+#include "cogl-pipeline-cache.h"
#ifdef HAVE_COGL_GLES2
@@ -538,6 +539,7 @@ _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline,
gboolean program_changed = FALSE;
UpdateUniformsState state;
CoglProgram *user_program;
+ CoglPipeline *template_pipeline = NULL;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
@@ -570,9 +572,31 @@ _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline,
if (program_state == NULL)
{
- program_state
- = program_state_new (cogl_pipeline_get_n_layers (pipeline));
+ /* Check if there is already a similar cached pipeline whose
+ program state we can share */
+ if (G_LIKELY (!(COGL_DEBUG_ENABLED
+ (COGL_DEBUG_DISABLE_PROGRAM_CACHES))))
+ {
+ template_pipeline =
+ _cogl_pipeline_cache_get_combined_template (ctx->pipeline_cache,
+ authority);
+
+ program_state = get_program_state (template_pipeline);
+ }
+
+ if (program_state)
+ program_state->ref_count++;
+ else
+ program_state
+ = program_state_new (cogl_pipeline_get_n_layers (authority));
+
set_program_state (authority, program_state);
+
+ if (template_pipeline)
+ {
+ program_state->ref_count++;
+ set_program_state (template_pipeline, program_state);
+ }
}
if (authority != pipeline)
diff --git a/cogl/cogl-pipeline-vertend-glsl.c b/cogl/cogl-pipeline-vertend-glsl.c
index 74fc9a8d..b8a4535b 100644
--- a/cogl/cogl-pipeline-vertend-glsl.c
+++ b/cogl/cogl-pipeline-vertend-glsl.c
@@ -128,6 +128,7 @@ _cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline,
unsigned long pipelines_difference)
{
CoglPipelineShaderState *shader_state;
+ CoglPipeline *template_pipeline = NULL;
CoglProgram *user_program;
_COGL_GET_CONTEXT (ctx, FALSE);
@@ -164,8 +165,30 @@ _cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline,
if (shader_state == NULL)
{
- shader_state = shader_state_new ();
+ /* Check if there is already a similar cached pipeline whose
+ shader state we can share */
+ if (G_LIKELY (!(COGL_DEBUG_ENABLED
+ (COGL_DEBUG_DISABLE_PROGRAM_CACHES))))
+ {
+ template_pipeline =
+ _cogl_pipeline_cache_get_vertex_template (ctx->pipeline_cache,
+ authority);
+
+ shader_state = get_shader_state (template_pipeline);
+ }
+
+ if (shader_state)
+ shader_state->ref_count++;
+ else
+ shader_state = shader_state_new ();
+
set_shader_state (authority, shader_state);
+
+ if (template_pipeline)
+ {
+ shader_state->ref_count++;
+ set_shader_state (template_pipeline, shader_state);
+ }
}
if (authority != pipeline)