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



commit 9d3ee283b9e41e0f4f2dd755be857d1808181969
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        |   79 +++++++++++++++++++++++++++++++++++++
 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, 181 insertions(+), 4 deletions(-)
---
diff --git a/cogl/cogl-pipeline-cache.c b/cogl/cogl-pipeline-cache.c
index 8a939f8..ed224e2 100644
--- a/cogl/cogl-pipeline-cache.c
+++ b/cogl/cogl-pipeline-cache.c
@@ -35,6 +35,7 @@
 struct _CoglPipelineCache
 {
   GHashTable *fragment_hash;
+  GHashTable *vertex_hash;
 };
 
 static unsigned int
@@ -63,6 +64,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)
 {
@@ -72,6 +99,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;
 }
@@ -80,6 +111,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);
 }
 
@@ -133,3 +165,50 @@ _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)
+{
+  /* 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_STATIC_ASSERT ((COGL_PIPELINE_STATE_AFFECTS_FRAGMENT_CODEGEN |
+                    COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN) ==
+                   COGL_PIPELINE_STATE_AFFECTS_FRAGMENT_CODEGEN);
+  G_STATIC_ASSERT ((COGL_PIPELINE_LAYER_STATE_AFFECTS_FRAGMENT_CODEGEN |
+                    COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN) ==
+                   COGL_PIPELINE_LAYER_STATE_AFFECTS_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 9f41a7f..418e921 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"
 
 #ifndef HAVE_COGL_GLES2
 
@@ -165,6 +166,7 @@ _cogl_pipeline_fragend_glsl_start (CoglPipeline *pipeline,
 {
   CoglPipelineShaderState *shader_state;
   CoglPipeline *authority;
+  CoglPipeline *template_pipeline = NULL;
   CoglProgram *user_program;
   int i;
 
@@ -208,8 +210,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 06181e4..451dd7f 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"
 
 #ifndef HAVE_COGL_GLES2
 
@@ -536,6 +537,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);
 
@@ -568,9 +570,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 2f3b6d2..9811f65 100644
--- a/cogl/cogl-pipeline-vertend-glsl.c
+++ b/cogl/cogl-pipeline-vertend-glsl.c
@@ -139,6 +139,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);
@@ -175,8 +176,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]