[cogl/wip/neil/pipeline-uniforms: 14/15] cogl-pipeline: Add support for setting uniform values



commit 930568e88bd03cac63d620ce8a2491aa2936d6c9
Author: Neil Roberts <neil linux intel com>
Date:   Thu Nov 3 17:20:43 2011 +0000

    cogl-pipeline: Add support for setting uniform values
    
    This adds the following new public experimental functions to set
    uniform values on a CoglPipeline:
    
    void
    cogl_pipeline_set_uniform_1f (CoglPipeline *pipeline,
                                  int uniform_location,
                                  float value);
    void
    cogl_pipeline_set_uniform_1i (CoglPipeline *pipeline,
                                  int uniform_location,
                                  int value);
    void
    cogl_pipeline_set_uniform_float (CoglPipeline *pipeline,
                                     int uniform_location,
                                     int n_components,
                                     int count,
                                     const float *value);
    void
    cogl_pipeline_set_uniform_int (CoglPipeline *pipeline,
                                   int uniform_location,
                                   int n_components,
                                   int count,
                                   const int *value);
    void
    cogl_pipeline_set_uniform_matrix (CoglPipeline *pipeline,
                                      int uniform_location,
                                      int dimensions,
                                      int count,
                                      gboolean transpose,
                                      const float *value);
    
    These are similar to the old functions used to set uniforms on a
    CoglProgram. To get a value to pass in as the uniform_location there
    is also:
    
    int
    cogl_pipeline_get_uniform_location (CoglPipeline *pipeline,
                                        const char *uniform_name);
    
    Conceptually the uniform locations are tied to the pipeline so that
    whenever setting a value for a new pipeline the application is
    expected to call this function. However in practice the uniform
    locations are global to the CoglContext. The names are stored in a
    linked list where the position in the list is the uniform location.
    
    The global indices are used so that each pipeline can store a mask of
    which uniforms it overrides. That way it is quicker to detect which
    uniforms are different from the last pipeline that used the same
    CoglProgramState so it can avoid flushing uniforms that haven't
    changed. Currently the values are not actually compared which means
    that it will only avoid flushing a uniform if there is a common
    ancestor that sets the value (or if the same pipeline is being flushed
    again - in which case the pipeline and its common ancestor are the
    same thing).
    
    The uniform values are stored in the big state of the pipeline as a
    sparse linked list. A bitmask stores which values have been overridden
    and only overridden values are stored in the linked list.

 cogl/cogl-context-private.h                        |    8 +
 cogl/cogl-context.c                                |    7 +
 cogl/cogl-pipeline-private.h                       |   38 ++-
 cogl/cogl-pipeline-progend-glsl.c                  |  178 ++++++++++
 cogl/cogl-pipeline-state-private.h                 |   13 +
 cogl/cogl-pipeline-state.c                         |  340 +++++++++++++++++++-
 cogl/cogl-pipeline-state.h                         |  160 +++++++++
 cogl/cogl-pipeline.c                               |   73 ++++-
 cogl/cogl-pipeline.h                               |   27 ++
 .../cogl-2.0-experimental-sections.txt             |    7 +
 10 files changed, 847 insertions(+), 4 deletions(-)
---
diff --git a/cogl/cogl-context-private.h b/cogl/cogl-context-private.h
index df0aa4a..8125e7b 100644
--- a/cogl/cogl-context-private.h
+++ b/cogl/cogl-context-private.h
@@ -258,6 +258,14 @@ struct _CoglContext
     [COGL_FLAGS_N_LONGS_FOR_SIZE (COGL_WINSYS_FEATURE_N_FEATURES)];
   void *winsys;
 
+  /* List of names of uniforms. These are used like quarks to give a
+     unique number to each uniform name except that we ensure that
+     they increase sequentially so that we can use the id as an index
+     into a bitfield representing the uniforms that a pipeline
+     overrides from its parent */
+  GSList *uniform_names;
+  int n_uniform_names;
+
   /* This defines a list of function pointers that Cogl uses from
      either GL or GLES. All functions are accessed indirectly through
      these pointers rather than linking to them directly */
diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c
index da41a82..6e95b2d 100644
--- a/cogl/cogl-context.c
+++ b/cogl/cogl-context.c
@@ -221,6 +221,9 @@ cogl_context_new (CoglDisplay *display,
       g_assert_not_reached ();
     }
 
+  context->uniform_names = NULL;
+  context->n_uniform_names = 0;
+
   /* Initialise the driver specific state */
   _cogl_init_feature_overrides (context);
 
@@ -484,8 +487,12 @@ _cogl_context_free (CoglContext *context)
 
   cogl_pipeline_cache_free (context->pipeline_cache);
 
+
   _cogl_destroy_texture_units ();
 
+  g_slist_foreach (context->uniform_names, (GFunc) g_free, NULL);
+  g_slist_free (context->uniform_names);
+
   g_byte_array_free (context->buffer_map_fallback_array, TRUE);
 
   cogl_object_unref (context->display);
diff --git a/cogl/cogl-pipeline-private.h b/cogl/cogl-pipeline-private.h
index a7dd8d4..9a2d590 100644
--- a/cogl/cogl-pipeline-private.h
+++ b/cogl/cogl-pipeline-private.h
@@ -167,6 +167,7 @@ typedef enum
   COGL_PIPELINE_STATE_POINT_SIZE_INDEX,
   COGL_PIPELINE_STATE_LOGIC_OPS_INDEX,
   COGL_PIPELINE_STATE_CULL_FACE_INDEX,
+  COGL_PIPELINE_STATE_UNIFORMS_INDEX,
 
   /* non-sparse */
   COGL_PIPELINE_STATE_REAL_BLEND_ENABLE_INDEX,
@@ -214,6 +215,8 @@ typedef enum _CoglPipelineState
     1L<<COGL_PIPELINE_STATE_LOGIC_OPS_INDEX,
   COGL_PIPELINE_STATE_CULL_FACE =
     1L<<COGL_PIPELINE_STATE_CULL_FACE_INDEX,
+  COGL_PIPELINE_STATE_UNIFORMS =
+    1L<<COGL_PIPELINE_STATE_UNIFORMS_INDEX,
 
   COGL_PIPELINE_STATE_REAL_BLEND_ENABLE =
     1L<<COGL_PIPELINE_STATE_REAL_BLEND_ENABLE_INDEX,
@@ -249,7 +252,8 @@ typedef enum _CoglPipelineState
    COGL_PIPELINE_STATE_FOG | \
    COGL_PIPELINE_STATE_POINT_SIZE | \
    COGL_PIPELINE_STATE_LOGIC_OPS | \
-   COGL_PIPELINE_STATE_CULL_FACE)
+   COGL_PIPELINE_STATE_CULL_FACE | \
+   COGL_PIPELINE_STATE_UNIFORMS)
 
 #define COGL_PIPELINE_STATE_MULTI_PROPERTY \
   (COGL_PIPELINE_STATE_LAYERS | \
@@ -258,7 +262,8 @@ typedef enum _CoglPipelineState
    COGL_PIPELINE_STATE_DEPTH | \
    COGL_PIPELINE_STATE_FOG | \
    COGL_PIPELINE_STATE_LOGIC_OPS | \
-   COGL_PIPELINE_STATE_CULL_FACE)
+   COGL_PIPELINE_STATE_CULL_FACE | \
+   COGL_PIPELINE_STATE_UNIFORMS)
 
 #define COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN \
   (COGL_PIPELINE_STATE_LAYERS | \
@@ -348,6 +353,34 @@ typedef struct
   CoglWinding front_winding;
 } CoglPipelineCullFaceState;
 
+typedef struct _CoglPipelineUniformOverride CoglPipelineUniformOverride;
+
+COGL_SLIST_HEAD (CoglPipelineUniformOverrideList,
+                 CoglPipelineUniformOverride);
+
+struct _CoglPipelineUniformOverride
+{
+  COGL_SLIST_ENTRY (CoglPipelineUniformOverride) list_node;
+
+  /* We don't need to store the location of the uniform here because
+     it is implicit from the order in the list */
+
+  /* One of these overrides can effectively remove a uniform by
+     setting the boxed value type to none. In that case no attempt
+     will be made to upload the value */
+
+  CoglBoxedValue value;
+};
+
+typedef struct
+{
+  CoglBitmask override_mask;
+  /* Uniforms that have been modified since this pipeline was last
+     flushed */
+  CoglBitmask changed_mask;
+  CoglPipelineUniformOverrideList override_list;
+} CoglPipelineUniformsState;
+
 typedef struct
 {
   CoglPipelineLightingState lighting_state;
@@ -359,6 +392,7 @@ typedef struct
   float point_size;
   CoglPipelineLogicOpsState logic_ops_state;
   CoglPipelineCullFaceState cull_face_state;
+  CoglPipelineUniformsState uniforms_state;
 } CoglPipelineBigState;
 
 typedef enum
diff --git a/cogl/cogl-pipeline-progend-glsl.c b/cogl/cogl-pipeline-progend-glsl.c
index 0d83ed6..87ecc68 100644
--- a/cogl/cogl-pipeline-progend-glsl.c
+++ b/cogl/cogl-pipeline-progend-glsl.c
@@ -29,6 +29,8 @@
 #include "config.h"
 #endif
 
+#include <string.h>
+
 #include "cogl-util.h"
 #include "cogl-context-private.h"
 #include "cogl-pipeline-private.h"
@@ -44,6 +46,7 @@
 #include "cogl-pipeline-fragend-glsl-private.h"
 #include "cogl-pipeline-vertend-glsl-private.h"
 #include "cogl-pipeline-cache.h"
+#include "cogl-pipeline-state-private.h"
 
 #ifdef HAVE_COGL_GLES2
 
@@ -140,6 +143,11 @@ typedef struct
    * so know if we need to update all of the uniforms */
   CoglPipeline *last_used_for_pipeline;
 
+  /* Array of GL uniform locations indexed by Cogl's uniform
+     location. We are careful only to allocated this array if a custom
+     uniform is actually set */
+  GArray *uniform_locations;
+
   UnitState *unit_state;
 } CoglPipelineProgramState;
 
@@ -151,6 +159,8 @@ get_program_state (CoglPipeline *pipeline)
   return cogl_object_get_user_data (COGL_OBJECT (pipeline), &program_state_key);
 }
 
+#define UNIFORM_LOCATION_UNKNOWN -2
+
 #ifdef HAVE_COGL_GLES2
 
 #define ATTRIBUTE_LOCATION_UNKNOWN -2
@@ -309,6 +319,7 @@ program_state_new (int n_layers)
   program_state->program = 0;
   program_state->n_tex_coord_attribs = 0;
   program_state->unit_state = g_new (UnitState, n_layers);
+  program_state->uniform_locations = NULL;
 #ifdef HAVE_COGL_GLES2
   program_state->tex_coord_attribute_locations = NULL;
   program_state->flushed_modelview_stack = NULL;
@@ -349,6 +360,9 @@ destroy_program_state (void *user_data,
 
       g_free (program_state->unit_state);
 
+      if (program_state->uniform_locations)
+        g_array_free (program_state->uniform_locations, TRUE);
+
       g_slice_free (CoglPipelineProgramState, program_state);
     }
 }
@@ -539,6 +553,165 @@ update_builtin_uniforms (CoglPipeline *pipeline,
 
 #endif /* HAVE_COGL_GLES2 */
 
+typedef struct
+{
+  CoglPipelineProgramState *program_state;
+  unsigned long *uniform_differences;
+  int n_differences;
+  CoglPipelineUniformsState *uniforms_state;
+  CoglContext *ctx;
+  CoglPipelineUniformOverride *override;
+} FlushUniformsClosure;
+
+static gboolean
+flush_uniform_cb (int uniform_num, void *user_data)
+{
+  FlushUniformsClosure *data = user_data;
+
+  if (COGL_FLAGS_GET (data->uniform_differences, uniform_num))
+    {
+      GArray *uniform_locations;
+      GLint uniform_location;
+
+      if (data->program_state->uniform_locations == NULL)
+        data->program_state->uniform_locations =
+          g_array_new (FALSE, FALSE, sizeof (GLint));
+
+      uniform_locations = data->program_state->uniform_locations;
+
+      if (uniform_locations->len <= uniform_num)
+        {
+          unsigned int old_len = uniform_locations->len;
+
+          g_array_set_size (uniform_locations, uniform_num + 1);
+
+          while (old_len <= uniform_num)
+            {
+              g_array_index (uniform_locations, GLint, old_len) =
+                UNIFORM_LOCATION_UNKNOWN;
+              old_len++;
+            }
+        }
+
+      uniform_location = g_array_index (uniform_locations, GLint, uniform_num);
+
+      if (uniform_location == UNIFORM_LOCATION_UNKNOWN)
+        {
+          const char *uniform_name =
+            g_slist_nth (data->ctx->uniform_names, uniform_num)->data;
+
+          uniform_location =
+            data->ctx->glGetUniformLocation (data->program_state->program,
+                                             uniform_name);
+          g_array_index (uniform_locations, GLint, uniform_num) =
+            uniform_location;
+        }
+
+      if (uniform_location != -1)
+        _cogl_boxed_value_set_uniform (data->ctx,
+                                       uniform_location,
+                                       &data->override->value);
+
+      data->n_differences--;
+      COGL_FLAGS_SET (data->uniform_differences, uniform_num, FALSE);
+    }
+
+  data->override = COGL_SLIST_NEXT (data->override, list_node);
+
+  return data->n_differences > 0;
+}
+
+static void
+_cogl_pipeline_progend_glsl_flush_uniforms (CoglPipeline *pipeline,
+                                            CoglPipelineProgramState *
+                                                                  program_state,
+                                            GLuint gl_program,
+                                            gboolean program_changed)
+{
+  FlushUniformsClosure data;
+  int n_uniform_longs;
+
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  if (pipeline->differences & COGL_PIPELINE_STATE_UNIFORMS)
+    data.uniforms_state = &pipeline->big_state->uniforms_state;
+  else
+    data.uniforms_state = NULL;
+
+  data.program_state = program_state;
+  data.ctx = ctx;
+
+  n_uniform_longs = COGL_FLAGS_N_LONGS_FOR_SIZE (ctx->n_uniform_names);
+
+  data.uniform_differences = g_newa (unsigned long, n_uniform_longs);
+
+  /* Try to find a common ancestor for the values that were already
+     flushed on the pipeline that this program state was last used for
+     so we can avoid flushing those */
+
+  if (program_changed || program_state->last_used_for_pipeline == NULL)
+    {
+      if (program_changed)
+        {
+          /* The program has changed so all of the uniform locations
+             are invalid */
+          if (program_state->uniform_locations)
+            g_array_set_size (program_state->uniform_locations, 0);
+        }
+
+      /* We need to flush everything so mark all of the uniforms as
+         dirty */
+      memset (data.uniform_differences, 0xff,
+              n_uniform_longs * sizeof (unsigned long));
+      data.n_differences = G_MAXINT;
+    }
+  else if (program_state->last_used_for_pipeline)
+    {
+      int i;
+
+      memset (data.uniform_differences, 0,
+              n_uniform_longs * sizeof (unsigned long));
+      _cogl_pipeline_compare_uniform_differences
+        (data.uniform_differences,
+         program_state->last_used_for_pipeline,
+         pipeline);
+
+      /* We need to be sure to flush any uniforms that have changed
+         since the last flush */
+      if (data.uniforms_state)
+        _cogl_bitmask_set_flags (&data.uniforms_state->changed_mask,
+                                 data.uniform_differences);
+
+      /* Count the number of differences. This is so we can stop early
+         when we've flushed all of them */
+      data.n_differences = 0;
+
+      for (i = 0; i < n_uniform_longs; i++)
+        data.n_differences +=
+          _cogl_util_popcountl (data.uniform_differences[i]);
+    }
+
+  while (pipeline && data.n_differences > 0)
+    {
+      if (pipeline->differences & COGL_PIPELINE_STATE_UNIFORMS)
+        {
+          const CoglPipelineUniformsState *uniforms_state =
+            &pipeline->big_state->uniforms_state;
+
+          data.override = COGL_SLIST_FIRST (&uniforms_state->override_list);
+
+          _cogl_bitmask_foreach (&uniforms_state->override_mask,
+                                 flush_uniform_cb,
+                                 &data);
+        }
+
+      pipeline = _cogl_pipeline_get_parent (pipeline);
+    }
+
+  if (data.uniforms_state)
+    _cogl_bitmask_clear_all (&data.uniforms_state->changed_mask);
+}
+
 static void
 _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline,
                                  unsigned long pipelines_difference,
@@ -730,6 +903,11 @@ _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline,
     }
 #endif
 
+  _cogl_pipeline_progend_glsl_flush_uniforms (pipeline,
+                                              program_state,
+                                              gl_program,
+                                              program_changed);
+
   if (user_program)
     _cogl_program_flush_uniforms (user_program,
                                   gl_program,
diff --git a/cogl/cogl-pipeline-state-private.h b/cogl/cogl-pipeline-state-private.h
index dd00f00..57eff5b 100644
--- a/cogl/cogl-pipeline-state-private.h
+++ b/cogl/cogl-pipeline-state-private.h
@@ -87,6 +87,10 @@ gboolean
 _cogl_pipeline_cull_face_state_equal (CoglPipeline *authority0,
                                       CoglPipeline *authority1);
 
+gboolean
+_cogl_pipeline_uniforms_state_equal (CoglPipeline *authority0,
+                                     CoglPipeline *authority1);
+
 void
 _cogl_pipeline_hash_color_state (CoglPipeline *authority,
                                  CoglPipelineHashState *state);
@@ -139,4 +143,13 @@ void
 _cogl_pipeline_hash_cull_face_state (CoglPipeline *authority,
                                      CoglPipelineHashState *state);
 
+void
+_cogl_pipeline_hash_uniforms_state (CoglPipeline *authority,
+                                    CoglPipelineHashState *state);
+
+void
+_cogl_pipeline_compare_uniform_differences (unsigned long *differences,
+                                            CoglPipeline *pipeline0,
+                                            CoglPipeline *pipeline1);
+
 #endif /* __COGL_PIPELINE_STATE_PRIVATE_H */
diff --git a/cogl/cogl-pipeline-state.c b/cogl/cogl-pipeline-state.c
index 78102d8..9ac09a4 100644
--- a/cogl/cogl-pipeline-state.c
+++ b/cogl/cogl-pipeline-state.c
@@ -34,7 +34,7 @@
 #include "cogl-blend-string.h"
 #include "cogl-util.h"
 #include "cogl-depth-state-private.h"
-#include "cogl-pipeline-private.h"
+#include "cogl-pipeline-state-private.h"
 
 #include "string.h"
 
@@ -238,6 +238,100 @@ _cogl_pipeline_user_shader_equal (CoglPipeline *authority0,
           authority1->big_state->user_program);
 }
 
+typedef struct
+{
+  const CoglBoxedValue **values;
+  const CoglPipelineUniformOverride *override_values;
+} GetUniformsClosure;
+
+static gboolean
+get_uniforms_cb (int uniform_num, void *user_data)
+{
+  GetUniformsClosure *data = user_data;
+
+  if (data->values[uniform_num] == NULL)
+    data->values[uniform_num] = &data->override_values->value;
+
+  data->override_values = COGL_SLIST_NEXT (data->override_values, list_node);
+
+  return TRUE;
+}
+
+static void
+_cogl_pipeline_get_all_uniform_values (CoglPipeline *pipeline,
+                                       const CoglBoxedValue **values)
+{
+  GetUniformsClosure data;
+
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  memset (values, 0,
+          sizeof (const CoglBoxedValue *) * ctx->n_uniform_names);
+
+  data.values = values;
+
+  do
+    {
+      const CoglPipelineUniformsState *uniforms_state =
+        &pipeline->big_state->uniforms_state;
+
+      data.override_values = COGL_SLIST_FIRST (&uniforms_state->override_list);
+
+      if ((pipeline->differences & COGL_PIPELINE_STATE_UNIFORMS))
+        _cogl_bitmask_foreach (&uniforms_state->override_mask,
+                               get_uniforms_cb,
+                               &data);
+
+      pipeline = _cogl_pipeline_get_parent (pipeline);
+    }
+  while (pipeline);
+}
+
+gboolean
+_cogl_pipeline_uniforms_state_equal (CoglPipeline *authority0,
+                                     CoglPipeline *authority1)
+{
+  unsigned long *differences;
+  const CoglBoxedValue **values0, **values1;
+  int n_longs;
+  int i;
+
+  _COGL_GET_CONTEXT (ctx, FALSE);
+
+  if (authority0 == authority1)
+    return TRUE;
+
+  values0 = g_alloca (sizeof (const CoglBoxedValue *) * ctx->n_uniform_names);
+  values1 = g_alloca (sizeof (const CoglBoxedValue *) * ctx->n_uniform_names);
+
+  n_longs = COGL_FLAGS_N_LONGS_FOR_SIZE (ctx->n_uniform_names);
+  differences = g_alloca (n_longs * sizeof (unsigned long));
+  memset (differences, 0, sizeof (unsigned long) * n_longs);
+  _cogl_pipeline_compare_uniform_differences (differences,
+                                              authority0,
+                                              authority1);
+
+  _cogl_pipeline_get_all_uniform_values (authority0, values0);
+  _cogl_pipeline_get_all_uniform_values (authority1, values1);
+
+  COGL_FLAGS_FOREACH_START (differences, n_longs, i)
+    {
+      const CoglBoxedValue *value0 = values0[i];
+      const CoglBoxedValue *value1 = values1[i];
+
+      if (value0 == NULL || value0->type == COGL_BOXED_NONE)
+        {
+          if (value1 != NULL && value1->type != COGL_BOXED_NONE)
+            return FALSE;
+        }
+      else if (!_cogl_boxed_value_equal (value0, value1))
+        return FALSE;
+    }
+  COGL_FLAGS_FOREACH_END;
+
+  return TRUE;
+}
+
 void
 cogl_pipeline_get_color (CoglPipeline *pipeline,
                          CoglColor    *color)
@@ -1262,6 +1356,163 @@ cogl_pipeline_set_point_size (CoglPipeline *pipeline,
                                    _cogl_pipeline_point_size_equal);
 }
 
+typedef struct
+{
+  int location;
+  CoglPipelineUniformOverride *previous_override;
+  CoglPipelineUniformOverride *found_override;
+  CoglPipelineUniformOverride *it;
+} FindUniformOverrideClosure;
+
+static gboolean
+find_uniform_override_cb (int it_location,
+                          void *user_data)
+{
+  FindUniformOverrideClosure *data = user_data;
+
+  if (it_location < data->location)
+    {
+      data->previous_override = data->it;
+      data->it = COGL_SLIST_NEXT (data->it, list_node);
+
+      return TRUE;
+    }
+  else
+    {
+      if (it_location == data->location)
+        data->found_override = data->it;
+
+      return FALSE;
+    }
+}
+
+static CoglBoxedValue *
+_cogl_pipeline_override_uniform (CoglPipeline *pipeline,
+                                 int location)
+{
+  CoglPipelineState state = COGL_PIPELINE_STATE_UNIFORMS;
+  CoglPipelineUniformsState *uniforms_state;
+  FindUniformOverrideClosure find_data;
+  CoglPipelineUniformOverride *override;
+
+  _COGL_GET_CONTEXT (ctx, NULL);
+
+  g_return_val_if_fail (cogl_is_pipeline (pipeline), NULL);
+  g_return_val_if_fail (location >= 0, NULL);
+  g_return_val_if_fail (location < ctx->n_uniform_names, NULL);
+
+  /* - Flush journal primitives referencing the current state.
+   * - Make sure the pipeline has no dependants so it may be modified.
+   * - If the pipeline isn't currently an authority for the state being
+   *   changed, then initialize that state from the current authority.
+   */
+  _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE);
+
+  uniforms_state = &pipeline->big_state->uniforms_state;
+
+  find_data.previous_override = NULL;
+  find_data.found_override = NULL;
+  find_data.it = COGL_SLIST_FIRST (&uniforms_state->override_list);
+  find_data.location = location;
+
+  _cogl_bitmask_foreach (&uniforms_state->override_mask,
+                         find_uniform_override_cb,
+                         &find_data);
+
+  _cogl_bitmask_set (&uniforms_state->changed_mask, location, TRUE);
+
+  /* If this pipeline already has an override for this value then we
+     can just use it directly */
+  if (find_data.found_override)
+    return &find_data.found_override->value;
+
+  /* We need to add a new override */
+  override = g_slice_new (CoglPipelineUniformOverride);
+  _cogl_boxed_value_init (&override->value);
+
+  if (find_data.previous_override)
+    COGL_SLIST_INSERT_AFTER (find_data.previous_override, override, list_node);
+  else
+    COGL_SLIST_INSERT_HEAD (&uniforms_state->override_list,
+                            override,
+                            list_node);
+
+  _cogl_bitmask_set (&uniforms_state->override_mask, location, TRUE);
+
+  return &override->value;
+}
+
+void
+cogl_pipeline_set_uniform_1f (CoglPipeline *pipeline,
+                              int uniform_location,
+                              float value)
+{
+  CoglBoxedValue *boxed_value;
+
+  boxed_value = _cogl_pipeline_override_uniform (pipeline, uniform_location);
+
+  _cogl_boxed_value_set_1f (boxed_value, value);
+}
+
+void
+cogl_pipeline_set_uniform_1i (CoglPipeline *pipeline,
+                              int uniform_location,
+                              int value)
+{
+  CoglBoxedValue *boxed_value;
+
+  boxed_value = _cogl_pipeline_override_uniform (pipeline, uniform_location);
+
+  _cogl_boxed_value_set_1i (boxed_value, value);
+}
+
+void
+cogl_pipeline_set_uniform_float (CoglPipeline *pipeline,
+                                 int uniform_location,
+                                 int n_components,
+                                 int count,
+                                 const float *value)
+{
+  CoglBoxedValue *boxed_value;
+
+  boxed_value = _cogl_pipeline_override_uniform (pipeline, uniform_location);
+
+  _cogl_boxed_value_set_float (boxed_value, n_components, count, value);
+}
+
+void
+cogl_pipeline_set_uniform_int (CoglPipeline *pipeline,
+                               int uniform_location,
+                               int n_components,
+                               int count,
+                               const int *value)
+{
+  CoglBoxedValue *boxed_value;
+
+  boxed_value = _cogl_pipeline_override_uniform (pipeline, uniform_location);
+
+  _cogl_boxed_value_set_int (boxed_value, n_components, count, value);
+}
+
+void
+cogl_pipeline_set_uniform_matrix (CoglPipeline *pipeline,
+                                  int uniform_location,
+                                  int dimensions,
+                                  int count,
+                                  gboolean transpose,
+                                  const float *value)
+{
+  CoglBoxedValue *boxed_value;
+
+  boxed_value = _cogl_pipeline_override_uniform (pipeline, uniform_location);
+
+  _cogl_boxed_value_set_matrix (boxed_value,
+                                dimensions,
+                                count,
+                                transpose,
+                                value);
+}
+
 void
 _cogl_pipeline_hash_color_state (CoglPipeline *authority,
                                  CoglPipelineHashState *state)
@@ -1455,3 +1706,90 @@ _cogl_pipeline_hash_cull_face_state (CoglPipeline *authority,
                                      cull_face_state,
                                      sizeof (CoglPipelineCullFaceState));
 }
+
+void
+_cogl_pipeline_hash_uniforms_state (CoglPipeline *authority,
+                                    CoglPipelineHashState *state)
+{
+  /* This isn't used anywhere yet because the uniform state doesn't
+     affect program generation. It's quite a hassle to implement so
+     let's just leave it until something actually needs it */
+  g_warn_if_reached ();
+}
+
+void
+_cogl_pipeline_compare_uniform_differences (unsigned long *differences,
+                                            CoglPipeline *pipeline0,
+                                            CoglPipeline *pipeline1)
+{
+  GSList *head0 = NULL;
+  GSList *head1 = NULL;
+  CoglPipeline *node0;
+  CoglPipeline *node1;
+  int len0 = 0;
+  int len1 = 0;
+  int count;
+  GSList *common_ancestor0;
+  GSList *common_ancestor1;
+
+  /* This algorithm is copied from
+     _cogl_pipeline_compare_differences(). It might be nice to share
+     the code more */
+
+  for (node0 = pipeline0; node0; node0 = _cogl_pipeline_get_parent (node0))
+    {
+      GSList *link = alloca (sizeof (GSList));
+      link->next = head0;
+      link->data = node0;
+      head0 = link;
+      len0++;
+    }
+  for (node1 = pipeline1; node1; node1 = _cogl_pipeline_get_parent (node1))
+    {
+      GSList *link = alloca (sizeof (GSList));
+      link->next = head1;
+      link->data = node1;
+      head1 = link;
+      len1++;
+    }
+
+  /* NB: There's no point looking at the head entries since we know both
+   * pipelines must have the same default pipeline as their root node. */
+  common_ancestor0 = head0;
+  common_ancestor1 = head1;
+  head0 = head0->next;
+  head1 = head1->next;
+  count = MIN (len0, len1) - 1;
+  while (count--)
+    {
+      if (head0->data != head1->data)
+        break;
+      common_ancestor0 = head0;
+      common_ancestor1 = head1;
+      head0 = head0->next;
+      head1 = head1->next;
+    }
+
+  for (head0 = common_ancestor0->next; head0; head0 = head0->next)
+    {
+      node0 = head0->data;
+      if ((node0->differences & COGL_PIPELINE_STATE_UNIFORMS))
+        {
+          const CoglPipelineUniformsState *uniforms_state =
+            &node0->big_state->uniforms_state;
+          _cogl_bitmask_set_flags (&uniforms_state->override_mask,
+                                   differences);
+        }
+    }
+  for (head1 = common_ancestor1->next; head1; head1 = head1->next)
+    {
+      node1 = head1->data;
+      if ((node1->differences & COGL_PIPELINE_STATE_UNIFORMS))
+        {
+          const CoglPipelineUniformsState *uniforms_state =
+            &node1->big_state->uniforms_state;
+          _cogl_bitmask_set_flags (&uniforms_state->override_mask,
+                                   differences);
+        }
+    }
+}
diff --git a/cogl/cogl-pipeline-state.h b/cogl/cogl-pipeline-state.h
index 7015b00..15e59f4 100644
--- a/cogl/cogl-pipeline-state.h
+++ b/cogl/cogl-pipeline-state.h
@@ -686,6 +686,166 @@ void
 cogl_pipeline_get_depth_state (CoglPipeline *pipeline,
                                CoglDepthState *state_out);
 
+#define cogl_pipeline_set_uniform_1f \
+        cogl_pipeline_set_uniform_1f_EXP
+/**
+ * cogl_pipeline_set_uniform_1f:
+ * @pipeline: A #CoglPipeline object
+ * @uniform_location: The uniform's location identifier
+ * @value: The new value for the uniform
+ *
+ * Sets a new value for the uniform at @uniform_location. If this
+ * pipeline has a user program attached and is later used as a source
+ * for drawing, the given value will be assigned to the uniform which
+ * can be accessed from the shader's source. The value for
+ * @uniform_location should be retrieved from the string name of the
+ * uniform by calling cogl_pipeline_get_uniform_location().
+ *
+ * This function should be used to set uniforms that are of type
+ * float. It can also be used to set a single member of a float array
+ * uniform.
+ *
+ * Since: 2.0
+ * Stability: Unstable
+ */
+void
+cogl_pipeline_set_uniform_1f (CoglPipeline *pipeline,
+                              int uniform_location,
+                              float value);
+
+#define cogl_pipeline_set_uniform_1i \
+        cogl_pipeline_set_uniform_1i_EXP
+/**
+ * cogl_pipeline_set_uniform_1i:
+ * @pipeline: A #CoglPipeline object
+ * @uniform_location: The uniform's location identifier
+ * @value: The new value for the uniform
+ *
+ * Sets a new value for the uniform at @uniform_location. If this
+ * pipeline has a user program attached and is later used as a source
+ * for drawing, the given value will be assigned to the uniform which
+ * can be accessed from the shader's source. The value for
+ * @uniform_location should be retrieved from the string name of the
+ * uniform by calling cogl_pipeline_get_uniform_location().
+ *
+ * This function should be used to set uniforms that are of type
+ * int. It can also be used to set a single member of a int array
+ * uniform or a sampler uniform.
+ *
+ * Since: 2.0
+ * Stability: Unstable
+ */
+void
+cogl_pipeline_set_uniform_1i (CoglPipeline *pipeline,
+                              int uniform_location,
+                              int value);
+
+#define cogl_pipeline_set_uniform_float \
+        cogl_pipeline_set_uniform_float_EXP
+/**
+ * cogl_pipeline_set_uniform_float:
+ * @pipeline: A #CoglPipeline object
+ * @uniform_location: The uniform's location identifier
+ * @n_components: The number of components in the corresponding uniform's type
+ * @count: The number of values to set
+ * @value: Pointer to the new values to set
+ *
+ * Sets new values for the uniform at @uniform_location. If this
+ * pipeline has a user program attached and is later used as a source
+ * for drawing, the given values will be assigned to the uniform which
+ * can be accessed from the shader's source. The value for
+ * @uniform_location should be retrieved from the string name of the
+ * uniform by calling cogl_pipeline_get_uniform_location().
+ *
+ * This function can be used to set any floating point type uniform,
+ * including float arrays and float vectors. For example, to set a
+ * single vec4 uniform you would use 4 for @n_components and 1 for
+ * @count. To set an array of 8 float values, you could use 1 for
+ * @n_components and 8 for @count.
+ *
+ * Since: 2.0
+ * Stability: Unstable
+ */
+void
+cogl_pipeline_set_uniform_float (CoglPipeline *pipeline,
+                                 int uniform_location,
+                                 int n_components,
+                                 int count,
+                                 const float *value);
+
+#define cogl_pipeline_set_uniform_int \
+        cogl_pipeline_set_uniform_int_EXP
+/**
+ * cogl_pipeline_set_uniform_int:
+ * @pipeline: A #CoglPipeline object
+ * @uniform_location: The uniform's location identifier
+ * @n_components: The number of components in the corresponding uniform's type
+ * @count: The number of values to set
+ * @value: Pointer to the new values to set
+ *
+ * Sets new values for the uniform at @uniform_location. If this
+ * pipeline has a user program attached and is later used as a source
+ * for drawing, the given values will be assigned to the uniform which
+ * can be accessed from the shader's source. The value for
+ * @uniform_location should be retrieved from the string name of the
+ * uniform by calling cogl_pipeline_get_uniform_location().
+ *
+ * This function can be used to set any integer type uniform,
+ * including int arrays and int vectors. For example, to set a single
+ * ivec4 uniform you would use 4 for @n_components and 1 for
+ * @count. To set an array of 8 int values, you could use 1 for
+ * @n_components and 8 for @count.
+ *
+ * Since: 2.0
+ * Stability: Unstable
+ */
+void
+cogl_pipeline_set_uniform_int (CoglPipeline *pipeline,
+                               int uniform_location,
+                               int n_components,
+                               int count,
+                               const int *value);
+
+#define cogl_pipeline_set_uniform_matrix \
+        cogl_pipeline_set_uniform_matrix_EXP
+/**
+ * cogl_pipeline_set_uniform_matrix:
+ * @pipeline: A #CoglPipeline object
+ * @uniform_location: The uniform's location identifier
+ * @dimensions: The size of the matrix
+ * @count: The number of values to set
+ * @transpose: Whether to transpose the matrix
+ * @value: Pointer to the new values to set
+ *
+ * Sets new values for the uniform at @uniform_location. If this
+ * pipeline has a user program attached and is later used as a source
+ * for drawing, the given values will be assigned to the uniform which
+ * can be accessed from the shader's source. The value for
+ * @uniform_location should be retrieved from the string name of the
+ * uniform by calling cogl_pipeline_get_uniform_location().
+ *
+ * This function can be used to set any matrix type uniform, including
+ * matrix arrays. For example, to set a single mat4 uniform you would
+ * use 4 for @dimensions and 1 for @count. To set an array of 8
+ * mat3 values, you could use 3 for @dimensions and 8 for @count.
+ *
+ * If @transpose is %FALSE then the matrix is expected to be in
+ * column-major order or if it is %TRUE then the matrix is in
+ * row-major order. You can pass a #CoglMatrix by calling by passing
+ * the result of cogl_matrix_get_array() in @value and setting
+ * @transpose to %FALSE.
+ *
+ * Since: 2.0
+ * Stability: Unstable
+ */
+void
+cogl_pipeline_set_uniform_matrix (CoglPipeline *pipeline,
+                                  int uniform_location,
+                                  int dimensions,
+                                  int count,
+                                  gboolean transpose,
+                                  const float *value);
+
 #endif /* COGL_ENABLE_EXPERIMENTAL_API */
 
 G_END_DECLS
diff --git a/cogl/cogl-pipeline.c b/cogl/cogl-pipeline.c
index 60e96eb..1e10a71 100644
--- a/cogl/cogl-pipeline.c
+++ b/cogl/cogl-pipeline.c
@@ -111,6 +111,7 @@ _cogl_pipeline_init_default_pipeline (void)
   CoglDepthState *depth_state = &big_state->depth_state;
   CoglPipelineLogicOpsState *logic_ops_state = &big_state->logic_ops_state;
   CoglPipelineCullFaceState *cull_face_state = &big_state->cull_face_state;
+  CoglPipelineUniformsState *uniforms_state = &big_state->uniforms_state;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
@@ -221,6 +222,10 @@ _cogl_pipeline_init_default_pipeline (void)
   cull_face_state->mode = COGL_PIPELINE_CULL_FACE_MODE_NONE;
   cull_face_state->front_winding = COGL_WINDING_COUNTER_CLOCKWISE;
 
+  _cogl_bitmask_init (&uniforms_state->override_mask);
+  _cogl_bitmask_init (&uniforms_state->changed_mask);
+  COGL_SLIST_INIT (&uniforms_state->override_list);
+
   ctx->default_pipeline = _cogl_pipeline_object_new (pipeline);
 }
 
@@ -458,6 +463,25 @@ _cogl_pipeline_free (CoglPipeline *pipeline)
       pipeline->big_state->user_program)
     cogl_handle_unref (pipeline->big_state->user_program);
 
+  if (pipeline->differences & COGL_PIPELINE_STATE_UNIFORMS)
+    {
+      CoglPipelineUniformsState *uniforms_state
+        = &pipeline->big_state->uniforms_state;
+      CoglPipelineUniformOverride *override, *tmp;
+
+      COGL_SLIST_FOREACH_SAFE (override,
+                               &uniforms_state->override_list,
+                               list_node,
+                               tmp)
+        {
+          _cogl_boxed_value_destroy (&override->value);
+          g_slice_free (CoglPipelineUniformOverride, override);
+        }
+
+      _cogl_bitmask_destroy (&uniforms_state->override_mask);
+      _cogl_bitmask_destroy (&uniforms_state->changed_mask);
+    }
+
   if (pipeline->differences & COGL_PIPELINE_STATE_NEEDS_BIG_STATE)
     g_slice_free (CoglPipelineBigState, pipeline->big_state);
 
@@ -1011,6 +1035,14 @@ _cogl_pipeline_init_multi_property_sparse_state (CoglPipeline *pipeline,
                 sizeof (CoglPipelineCullFaceState));
         break;
       }
+    case COGL_PIPELINE_STATE_UNIFORMS:
+      {
+        CoglPipelineUniformsState *uniforms_state =
+          &pipeline->big_state->uniforms_state;
+        _cogl_bitmask_init (&uniforms_state->override_mask);
+        _cogl_bitmask_init (&uniforms_state->changed_mask);
+        COGL_SLIST_INIT (&uniforms_state->override_list);
+      }
     }
 }
 
@@ -2199,6 +2231,12 @@ _cogl_pipeline_equal (CoglPipeline *pipeline0,
                               _cogl_pipeline_user_shader_equal))
     goto done;
 
+  if (!simple_property_equal (authorities0, authorities1,
+                              pipelines_difference,
+                              COGL_PIPELINE_STATE_UNIFORMS_INDEX,
+                              _cogl_pipeline_uniforms_state_equal))
+    goto done;
+
   if (pipelines_difference & COGL_PIPELINE_STATE_LAYERS)
     {
       CoglPipelineStateIndex state_index = COGL_PIPELINE_STATE_LAYERS_INDEX;
@@ -2610,9 +2648,11 @@ _cogl_pipeline_init_state_hash_functions (void)
     _cogl_pipeline_hash_point_size_state;
   state_hash_functions[COGL_PIPELINE_STATE_LOGIC_OPS_INDEX] =
     _cogl_pipeline_hash_logic_ops_state;
+  state_hash_functions[COGL_PIPELINE_STATE_UNIFORMS_INDEX] =
+    _cogl_pipeline_hash_uniforms_state;
 
   /* So we get a big error if we forget to update this code! */
-  g_assert (COGL_PIPELINE_STATE_SPARSE_COUNT == 13);
+  g_assert (COGL_PIPELINE_STATE_SPARSE_COUNT == 14);
 }
 
 unsigned int
@@ -2808,3 +2848,34 @@ _cogl_pipeline_get_state_for_fragment_codegen (CoglContext *context)
 
   return state;
 }
+
+int
+cogl_pipeline_get_uniform_location (CoglPipeline *pipeline,
+                                    const char *uniform_name)
+{
+  GSList *l;
+  int location = 0;
+
+  _COGL_GET_CONTEXT (ctx, -1);
+
+  /* This API is designed as if the uniform locations are specific to
+     a pipeline but they are actually unique across a whole
+     CoglContext. Potentially this could just be
+     cogl_context_get_uniform_location but it seems to make sense to
+     keep the API this way so that we can change the internals if need
+     be. */
+
+  /* Look for an existing uniform with this name */
+  for (l = ctx->uniform_names; l; l = l->next)
+    {
+      if (!strcmp (uniform_name, l->data))
+        return location;
+
+      location++;
+    }
+
+  ctx->uniform_names =
+    g_slist_append (ctx->uniform_names, g_strdup (uniform_name));
+
+  return ctx->n_uniform_names++;
+}
diff --git a/cogl/cogl-pipeline.h b/cogl/cogl-pipeline.h
index d93c6f8..11b71a0 100644
--- a/cogl/cogl-pipeline.h
+++ b/cogl/cogl-pipeline.h
@@ -138,6 +138,33 @@ cogl_pipeline_foreach_layer (CoglPipeline *pipeline,
                              CoglPipelineLayerCallback callback,
                              void *user_data);
 
+#define cogl_pipeline_get_uniform_location \
+        cogl_pipeline_get_uniform_location_EXP
+/**
+ * cogl_pipeline_get_uniform_location:
+ * @pipeline: A #CoglPipeline object
+ * @uniform_name: The name of a uniform
+ *
+ * This is used to get an integer representing the uniform with the
+ * name @uniform_name. The integer can be passed to functions such as
+ * cogl_pipeline_set_uniform_1f() to set the value of a uniform.
+ *
+ * This function will always return a valid integer. Ie, unlike
+ * OpenGL, it does not return -1 if the uniform is not available in
+ * this pipeline so it can not be used to test whether uniforms are
+ * present. It is not necessary to set the program on the pipeline
+ * before calling this function.
+ *
+ * Return value: A integer representing the location of the given uniform.
+ *
+ * Since: 2.0
+ * Stability: Unstable
+ */
+int
+cogl_pipeline_get_uniform_location (CoglPipeline *pipeline,
+                                    const char *uniform_name);
+
+
 #endif /* COGL_ENABLE_EXPERIMENTAL_API */
 
 G_END_DECLS
diff --git a/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt b/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt
index 5555126..b73f353 100644
--- a/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt
+++ b/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt
@@ -593,6 +593,13 @@ cogl_pipeline_remove_layer
 cogl_pipeline_get_n_layers
 cogl_pipeline_foreach_layer
 
+cogl_pipeline_get_uniform_location
+cogl_pipeline_set_uniform_1f
+cogl_pipeline_set_uniform_1i
+cogl_pipeline_set_uniform_float
+cogl_pipeline_set_uniform_int
+cogl_pipeline_set_uniform_matrix
+
 <SUBSECTION Private>
 cogl_blend_string_error_get_type
 cogl_blend_string_error_quark



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