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>
This commit is contained in:
Neil Roberts 2011-11-03 17:20:43 +00:00
parent 4e760d51f1
commit 4553ca0695
10 changed files with 880 additions and 4 deletions

View File

@ -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 */

View File

@ -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);

View File

@ -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

View File

@ -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,

View File

@ -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 */

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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++;
}

View File

@ -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

View File

@ -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
<SUBSECTION Private>
cogl_blend_string_error_get_type
cogl_blend_string_error_quark