summaryrefslogtreecommitdiff
path: root/cogl
diff options
context:
space:
mode:
authorNeil Roberts <neil@linux.intel.com>2011-11-03 17:20:43 +0000
committerNeil Roberts <neil@linux.intel.com>2011-11-16 16:32:11 +0000
commit4553ca0695a08a08a32925d3621d4269b6c459c7 (patch)
tree1ae861c7b16eaf3a4cf797fad76ab183b8d588a1 /cogl
parent4e760d51f108dfcd2ca8033799d441edfb82f041 (diff)
downloadcogl-4553ca0695a08a08a32925d3621d4269b6c459c7.tar.gz
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. Reviewed-by: Robert Bragg <robert@linux.intel.com>
Diffstat (limited to 'cogl')
-rw-r--r--cogl/cogl-context-private.h8
-rw-r--r--cogl/cogl-context.c7
-rw-r--r--cogl/cogl-pipeline-private.h38
-rw-r--r--cogl/cogl-pipeline-progend-glsl.c179
-rw-r--r--cogl/cogl-pipeline-state-private.h13
-rw-r--r--cogl/cogl-pipeline-state.c340
-rw-r--r--cogl/cogl-pipeline-state.h160
-rw-r--r--cogl/cogl-pipeline.c105
-rw-r--r--cogl/cogl-pipeline.h27
9 files changed, 873 insertions, 4 deletions
diff --git a/cogl/cogl-context-private.h b/cogl/cogl-context-private.h
index df0aa4a7..8125e7bb 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 b8296ece..58fa44d6 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);
@@ -478,8 +481,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 5b07e5ef..4c2dd2b8 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 | \
@@ -334,6 +339,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;
@@ -345,6 +378,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 0d83ed60..891be79d 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,166 @@ update_builtin_uniforms (CoglPipeline *pipeline,
#endif /* HAVE_COGL_GLES2 */
+typedef struct
+{
+ CoglPipelineProgramState *program_state;
+ unsigned long *uniform_differences;
+ int n_differences;
+ 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)
+{
+ CoglPipelineUniformsState *uniforms_state;
+ FlushUniformsClosure data;
+ int n_uniform_longs;
+
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ if (pipeline->differences & COGL_PIPELINE_STATE_UNIFORMS)
+ uniforms_state = &pipeline->big_state->uniforms_state;
+ else
+ 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 (uniforms_state)
+ _cogl_bitmask_set_flags (&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 *parent_uniforms_state =
+ &pipeline->big_state->uniforms_state;
+
+ data.override =
+ COGL_SLIST_FIRST (&parent_uniforms_state->override_list);
+
+ _cogl_bitmask_foreach (&parent_uniforms_state->override_mask,
+ flush_uniform_cb,
+ &data);
+ }
+
+ pipeline = _cogl_pipeline_get_parent (pipeline);
+ }
+
+ if (uniforms_state)
+ _cogl_bitmask_clear_all (&uniforms_state->changed_mask);
+}
+
static void
_cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline,
unsigned long pipelines_difference,
@@ -730,6 +904,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 d93771bc..ef7665ad 100644
--- a/cogl/cogl-pipeline-state-private.h
+++ b/cogl/cogl-pipeline-state-private.h
@@ -79,6 +79,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);
@@ -131,4 +135,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 db662f4f..e9cf5c7c 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)
@@ -1304,6 +1398,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)
@@ -1497,3 +1748,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 5dfd21a1..cd6a22b7 100644
--- a/cogl/cogl-pipeline-state.h
+++ b/cogl/cogl-pipeline-state.h
@@ -777,6 +777,166 @@ cogl_pipeline_set_front_face_winding (CoglPipeline *pipeline,
CoglWinding
cogl_pipeline_get_front_face_winding (CoglPipeline *pipeline);
+#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 907f692c..a2af6214 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);
@@ -927,6 +951,38 @@ _cogl_pipeline_copy_differences (CoglPipeline *dest,
sizeof (CoglPipelineCullFaceState));
}
+ if (differences & COGL_PIPELINE_STATE_UNIFORMS)
+ {
+ CoglPipelineUniformOverride *prev = NULL;
+ CoglPipelineUniformOverride *override;
+
+ COGL_SLIST_INIT (&big_state->uniforms_state.override_list);
+
+ COGL_SLIST_FOREACH (override,
+ &src->big_state->uniforms_state.override_list,
+ list_node)
+ {
+ CoglPipelineUniformOverride *new_override =
+ g_slice_new (CoglPipelineUniformOverride);
+ _cogl_boxed_value_copy (&new_override->value,
+ &override->value);
+ if (prev)
+ COGL_SLIST_INSERT_AFTER (prev, new_override, list_node);
+ else
+ COGL_SLIST_INSERT_HEAD (&big_state->uniforms_state.override_list,
+ new_override,
+ list_node);
+
+ prev = new_override;
+ }
+
+ _cogl_bitmask_init (&big_state->uniforms_state.override_mask);
+ _cogl_bitmask_set_bits (&big_state->uniforms_state.override_mask,
+ &src->big_state->uniforms_state.override_mask);
+
+ _cogl_bitmask_init (&big_state->uniforms_state.changed_mask);
+ }
+
/* XXX: we shouldn't bother doing this in most cases since
* _copy_differences is typically used to initialize pipeline state
* by copying it from the current authority, so it's not actually
@@ -1011,6 +1067,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 +2263,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;
@@ -2606,9 +2676,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
@@ -2804,3 +2876,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 d93c6f8e..11b71a00 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