[cogl/wip/glsl-cache: 3/3] cogl-pipeline: Use the pipeline cache for the GLSL backends



commit 52b4df8dfc19725799921db065d4b34edcf7065e
Author: Neil Roberts <neil linux intel com>
Date:   Thu Jun 30 18:34:05 2011 +0100

    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.

 cogl/cogl-pipeline-cache.c        |   90 +++++++++++++++++++++++++++++++++++++
 cogl/cogl-pipeline-cache.h        |   27 +++++++++++
 cogl/cogl-pipeline-fragend-glsl.c |   26 ++++++++++-
 cogl/cogl-pipeline-progend-glsl.c |   28 +++++++++++-
 cogl/cogl-pipeline-vertend-glsl.c |   25 ++++++++++-
 5 files changed, 192 insertions(+), 4 deletions(-)
---
diff --git a/cogl/cogl-pipeline-cache.c b/cogl/cogl-pipeline-cache.c
index f5e32a7..c2bca81 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 57f80ae..c9a5d6a 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 35e43e9..9025860 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 9eb1d7c..4a6fa78 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 74fc9a8..b8a4535 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)



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