From 4553ca0695a08a08a32925d3621d4269b6c459c7 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 3 Nov 2011 17:20:43 +0000 Subject: [PATCH] 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 --- cogl/cogl-context-private.h | 8 + cogl/cogl-context.c | 7 + cogl/cogl-pipeline-private.h | 38 +- cogl/cogl-pipeline-progend-glsl.c | 179 +++++++++ cogl/cogl-pipeline-state-private.h | 13 + cogl/cogl-pipeline-state.c | 340 +++++++++++++++++- cogl/cogl-pipeline-state.h | 160 +++++++++ cogl/cogl-pipeline.c | 105 +++++- cogl/cogl-pipeline.h | 27 ++ .../cogl-2.0-experimental-sections.txt | 7 + 10 files changed, 880 insertions(+), 4 deletions(-) diff --git a/cogl/cogl-context-private.h b/cogl/cogl-context-private.h index df0aa4a7c..8125e7bb7 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 b8296eceb..58fa44d61 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 5b07e5efe..4c2dd2b8c 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< + #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 d93771bc9..ef7665ad4 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 db662f4fa..e9cf5c7ce 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 5dfd21a1b..cd6a22b74 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 907f692c7..a2af62141 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 d93c6f8ec..11b71a00e 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 b0b8ae7aa..f2dd72c17 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 @@ -598,6 +598,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 + cogl_blend_string_error_get_type cogl_blend_string_error_quark