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