cogl-pipeline: Add two hook points for adding shader snippets

This adds two new public experimental functions for attaching
CoglSnippets to two hook points on a CoglPipeline:

void cogl_pipeline_add_vertex_hook (CoglPipeline *, CoglSnippet *)
void cogl_pipeline_add_fragment_hook (CoglPipeline *, CoglSnippet *)

The hooks are intended to be around the entire vertex or fragment
processing. That means the pre string in the snippet will be inserted
at the very top of the main function and the post function will be
inserted at the very end. The declarations get inserted in the global
scope.

The snippets are stored in two separate linked lists with a structure
containing an enum representing the hook point and a pointer to the
snippet. The lists are meant to be for hooks that affect the vertex
shader and fragment shader respectively. Although there are currently
only two hooks and the names match these two lists, the intention is
*not* that each new hook will be in a separate list. The separation of
the lists is just to make it easier to determine which shader needs to
be regenerated when a new snippet is added.

When a pipeline becomes the authority for either the vertex or
fragment snipper state, it simply copies the entire list from the
previous authority (although of course the shader snippet objects are
referenced instead of copied so it doesn't duplicate the source
strings).

Each string is inserted into its own block in the shader. This means
that each string has its own scope so it doesn't need to worry about
name collisions with variables in other snippets. However it does mean
that the pre and post strings can't share variables. It could be
possible to wrap both parts in one block and then wrap the actual
inner hook code in another block, however this would mean that any
further snippets within the outer snippet would be able to see those
variables. Perhaps something to consider would be to put each snippet
into its own function which calls another function between the pre and
post strings to do further processing.

The pipeline cache for generated programs was previously shared with
the fragment shader cache because the state that affects vertex
shaders was a subset of the state that affects fragment shaders. This
is no longer the case because there is a separate state mask for
vertex snippets so the program cache now has its own hash table.

Reviewed-by: Robert Bragg <robert@linux.intel.com>
This commit is contained in:
Neil Roberts 2011-11-17 16:52:21 +00:00 committed by Robert Bragg
parent f37738453e
commit d38ae0284b
12 changed files with 593 additions and 26 deletions

View File

@ -37,6 +37,7 @@ struct _CoglPipelineCache
{ {
GHashTable *fragment_hash; GHashTable *fragment_hash;
GHashTable *vertex_hash; GHashTable *vertex_hash;
GHashTable *combined_hash;
}; };
static unsigned int static unsigned int
@ -101,6 +102,46 @@ pipeline_vertex_equal (const void *a, const void *b)
0); 0);
} }
static unsigned int
pipeline_combined_hash (const void *data)
{
unsigned int combined_state;
unsigned int layer_combined_state;
_COGL_GET_CONTEXT (ctx, 0);
combined_state =
_cogl_pipeline_get_state_for_fragment_codegen (ctx) |
COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN;
layer_combined_state =
_cogl_pipeline_get_layer_state_for_fragment_codegen (ctx) |
COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN;
return _cogl_pipeline_hash ((CoglPipeline *)data,
combined_state, layer_combined_state,
0);
}
static gboolean
pipeline_combined_equal (const void *a, const void *b)
{
unsigned int combined_state;
unsigned int layer_combined_state;
_COGL_GET_CONTEXT (ctx, 0);
combined_state =
_cogl_pipeline_get_state_for_fragment_codegen (ctx) |
COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN;
layer_combined_state =
_cogl_pipeline_get_layer_state_for_fragment_codegen (ctx) |
COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN;
return _cogl_pipeline_equal ((CoglPipeline *)a, (CoglPipeline *)b,
combined_state, layer_combined_state,
0);
}
CoglPipelineCache * CoglPipelineCache *
cogl_pipeline_cache_new (void) cogl_pipeline_cache_new (void)
{ {
@ -114,6 +155,10 @@ cogl_pipeline_cache_new (void)
pipeline_vertex_equal, pipeline_vertex_equal,
cogl_object_unref, cogl_object_unref,
cogl_object_unref); cogl_object_unref);
cache->combined_hash = g_hash_table_new_full (pipeline_combined_hash,
pipeline_combined_equal,
cogl_object_unref,
cogl_object_unref);
return cache; return cache;
} }
@ -123,6 +168,7 @@ cogl_pipeline_cache_free (CoglPipelineCache *cache)
{ {
g_hash_table_destroy (cache->fragment_hash); g_hash_table_destroy (cache->fragment_hash);
g_hash_table_destroy (cache->vertex_hash); g_hash_table_destroy (cache->vertex_hash);
g_hash_table_destroy (cache->combined_hash);
g_free (cache); g_free (cache);
} }
@ -210,27 +256,27 @@ CoglPipeline *
_cogl_pipeline_cache_get_combined_template (CoglPipelineCache *cache, _cogl_pipeline_cache_get_combined_template (CoglPipelineCache *cache,
CoglPipeline *key_pipeline) CoglPipeline *key_pipeline)
{ {
unsigned int pipeline_state_for_fragment_codegen; CoglPipeline *template =
unsigned int pipeline_layer_state_for_fragment_codegen; g_hash_table_lookup (cache->combined_hash, key_pipeline);
_COGL_GET_CONTEXT (ctx, NULL); if (template == NULL)
{
template = cogl_pipeline_copy (key_pipeline);
pipeline_state_for_fragment_codegen = g_hash_table_insert (cache->combined_hash,
_cogl_pipeline_get_state_for_fragment_codegen (ctx); template,
pipeline_layer_state_for_fragment_codegen = cogl_object_ref (template));
_cogl_pipeline_get_layer_state_for_fragment_codegen (ctx);
/* Currently the vertex shader state is a subset of the fragment if (G_UNLIKELY (g_hash_table_size (cache->combined_hash) > 50))
shader state so we can avoid a third hash table here by just {
using the fragment shader table. This assert should catch it if static gboolean seen = FALSE;
that ever changes */ if (!seen)
g_warning ("Over 50 separate programs have been "
"generated which is very unusual, so something "
"is probably wrong!\n");
seen = TRUE;
}
}
g_assert ((pipeline_state_for_fragment_codegen | return template;
COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN) ==
pipeline_state_for_fragment_codegen);
g_assert ((pipeline_layer_state_for_fragment_codegen |
COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN) ==
pipeline_layer_state_for_fragment_codegen);
return _cogl_pipeline_cache_get_fragment_template (cache, key_pipeline);
} }

View File

@ -32,6 +32,7 @@
#include "cogl-debug.h" #include "cogl-debug.h"
#include "cogl-context-private.h" #include "cogl-context-private.h"
#include "cogl-pipeline-private.h" #include "cogl-pipeline-private.h"
#include "cogl-pipeline-state-private.h"
#include "cogl-pipeline-layer-private.h" #include "cogl-pipeline-layer-private.h"
#ifdef COGL_PIPELINE_FRAGEND_ARBFP #ifdef COGL_PIPELINE_FRAGEND_ARBFP
@ -178,6 +179,10 @@ _cogl_pipeline_fragend_arbfp_start (CoglPipeline *pipeline,
if (_cogl_pipeline_get_fog_enabled (pipeline)) if (_cogl_pipeline_get_fog_enabled (pipeline))
return FALSE; return FALSE;
/* Fragment snippets are only supported in the GLSL fragend */
if (_cogl_pipeline_has_fragment_snippets (pipeline))
return FALSE;
user_program = cogl_pipeline_get_user_program (pipeline); user_program = cogl_pipeline_get_user_program (pipeline);
if (user_program != COGL_INVALID_HANDLE) if (user_program != COGL_INVALID_HANDLE)
{ {

View File

@ -31,6 +31,7 @@
#include "cogl-context-private.h" #include "cogl-context-private.h"
#include "cogl-pipeline-private.h" #include "cogl-pipeline-private.h"
#include "cogl-pipeline-state-private.h"
#include "cogl-pipeline-opengl-private.h" #include "cogl-pipeline-opengl-private.h"
#ifdef COGL_PIPELINE_FRAGEND_FIXED #ifdef COGL_PIPELINE_FRAGEND_FIXED
@ -101,6 +102,10 @@ _cogl_pipeline_fragend_fixed_start (CoglPipeline *pipeline,
if (ctx->driver == COGL_DRIVER_GLES2) if (ctx->driver == COGL_DRIVER_GLES2)
return FALSE; return FALSE;
/* Fragment snippets are only supported in the GLSL fragend */
if (_cogl_pipeline_has_fragment_snippets (pipeline))
return FALSE;
/* If there is a user program with a fragment shader then the /* If there is a user program with a fragment shader then the
appropriate backend for that language should handle it. We can appropriate backend for that language should handle it. We can
still use the fixed fragment backend if the program only contains still use the fixed fragment backend if the program only contains

View File

@ -181,6 +181,16 @@ _cogl_pipeline_fragend_glsl_get_shader (CoglPipeline *pipeline)
return 0; return 0;
} }
static CoglPipelineSnippetList *
get_fragment_snippets (CoglPipeline *pipeline)
{
pipeline =
_cogl_pipeline_get_authority (pipeline,
COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS);
return &pipeline->big_state->fragment_snippets;
}
static gboolean static gboolean
_cogl_pipeline_fragend_glsl_start (CoglPipeline *pipeline, _cogl_pipeline_fragend_glsl_start (CoglPipeline *pipeline,
int n_layers, int n_layers,
@ -190,6 +200,7 @@ _cogl_pipeline_fragend_glsl_start (CoglPipeline *pipeline,
CoglPipelineShaderState *shader_state; CoglPipelineShaderState *shader_state;
CoglPipeline *authority; CoglPipeline *authority;
CoglPipeline *template_pipeline = NULL; CoglPipeline *template_pipeline = NULL;
CoglPipelineSnippet *snippet;
CoglProgram *user_program; CoglProgram *user_program;
int i; int i;
@ -319,6 +330,37 @@ _cogl_pipeline_fragend_glsl_start (CoglPipeline *pipeline,
"main ()\n" "main ()\n"
"{\n"); "{\n");
COGL_LIST_FOREACH (snippet, get_fragment_snippets (pipeline), list_node)
{
const char *declarations =
cogl_snippet_get_declarations (snippet->snippet);
/* Add all of the declarations for fragment snippets */
if (declarations)
{
g_string_append (shader_state->header, declarations);
g_string_append_c (shader_state->header, '\n');
}
/* Add all of the pre-hooks for fragment processing */
if (snippet->hook == COGL_PIPELINE_SNIPPET_HOOK_FRAGMENT)
{
const char *pre =
cogl_snippet_get_pre (snippet->snippet);
if (pre)
{
g_string_append (shader_state->source, " {\n");
g_string_append (shader_state->source, pre);
g_string_append (shader_state->source, " }\n");
}
}
}
/* Enclose the generated fragment processing in a block so that any
variables declared in it won't be in the scope of the snippets */
g_string_append (shader_state->source, " {\n");
for (i = 0; i < n_layers; i++) for (i = 0; i < n_layers; i++)
{ {
shader_state->unit_state[i].sampled = FALSE; shader_state->unit_state[i].sampled = FALSE;
@ -885,6 +927,7 @@ _cogl_pipeline_fragend_glsl_end (CoglPipeline *pipeline,
GLint lengths[2]; GLint lengths[2];
GLint compile_status; GLint compile_status;
GLuint shader; GLuint shader;
CoglPipelineSnippet *snippet;
COGL_STATIC_COUNTER (fragend_glsl_compile_counter, COGL_STATIC_COUNTER (fragend_glsl_compile_counter,
"glsl fragment compile counter", "glsl fragment compile counter",
@ -922,6 +965,24 @@ _cogl_pipeline_fragend_glsl_end (CoglPipeline *pipeline,
add_alpha_test_snippet (pipeline, shader_state); add_alpha_test_snippet (pipeline, shader_state);
#endif #endif
/* Close the block surrounding the generated fragment processing */
g_string_append (shader_state->source, " }\n");
/* Add all of the post-hooks for fragment processing */
COGL_LIST_FOREACH (snippet, get_fragment_snippets (pipeline), list_node)
if (snippet->hook == COGL_PIPELINE_SNIPPET_HOOK_FRAGMENT)
{
const char *post =
cogl_snippet_get_post (snippet->snippet);
if (post)
{
g_string_append (shader_state->source, " {\n");
g_string_append (shader_state->source, post);
g_string_append (shader_state->source, " }\n");
}
}
g_string_append (shader_state->source, "}\n"); g_string_append (shader_state->source, "}\n");
GE_RET( shader, ctx, glCreateShader (GL_FRAGMENT_SHADER) ); GE_RET( shader, ctx, glCreateShader (GL_FRAGMENT_SHADER) );

View File

@ -168,6 +168,8 @@ typedef enum
COGL_PIPELINE_STATE_LOGIC_OPS_INDEX, COGL_PIPELINE_STATE_LOGIC_OPS_INDEX,
COGL_PIPELINE_STATE_CULL_FACE_INDEX, COGL_PIPELINE_STATE_CULL_FACE_INDEX,
COGL_PIPELINE_STATE_UNIFORMS_INDEX, COGL_PIPELINE_STATE_UNIFORMS_INDEX,
COGL_PIPELINE_STATE_VERTEX_SNIPPETS_INDEX,
COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS_INDEX,
/* non-sparse */ /* non-sparse */
COGL_PIPELINE_STATE_REAL_BLEND_ENABLE_INDEX, COGL_PIPELINE_STATE_REAL_BLEND_ENABLE_INDEX,
@ -217,6 +219,10 @@ typedef enum _CoglPipelineState
1L<<COGL_PIPELINE_STATE_CULL_FACE_INDEX, 1L<<COGL_PIPELINE_STATE_CULL_FACE_INDEX,
COGL_PIPELINE_STATE_UNIFORMS = COGL_PIPELINE_STATE_UNIFORMS =
1L<<COGL_PIPELINE_STATE_UNIFORMS_INDEX, 1L<<COGL_PIPELINE_STATE_UNIFORMS_INDEX,
COGL_PIPELINE_STATE_VERTEX_SNIPPETS =
1L<<COGL_PIPELINE_STATE_VERTEX_SNIPPETS_INDEX,
COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS =
1L<<COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS_INDEX,
COGL_PIPELINE_STATE_REAL_BLEND_ENABLE = COGL_PIPELINE_STATE_REAL_BLEND_ENABLE =
1L<<COGL_PIPELINE_STATE_REAL_BLEND_ENABLE_INDEX, 1L<<COGL_PIPELINE_STATE_REAL_BLEND_ENABLE_INDEX,
@ -240,7 +246,9 @@ typedef enum _CoglPipelineState
COGL_PIPELINE_STATE_LAYERS | \ COGL_PIPELINE_STATE_LAYERS | \
COGL_PIPELINE_STATE_LIGHTING | \ COGL_PIPELINE_STATE_LIGHTING | \
COGL_PIPELINE_STATE_BLEND | \ COGL_PIPELINE_STATE_BLEND | \
COGL_PIPELINE_STATE_USER_SHADER) COGL_PIPELINE_STATE_USER_SHADER | \
COGL_PIPELINE_STATE_VERTEX_SNIPPETS | \
COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS)
#define COGL_PIPELINE_STATE_NEEDS_BIG_STATE \ #define COGL_PIPELINE_STATE_NEEDS_BIG_STATE \
(COGL_PIPELINE_STATE_LIGHTING | \ (COGL_PIPELINE_STATE_LIGHTING | \
@ -253,7 +261,9 @@ typedef enum _CoglPipelineState
COGL_PIPELINE_STATE_POINT_SIZE | \ COGL_PIPELINE_STATE_POINT_SIZE | \
COGL_PIPELINE_STATE_LOGIC_OPS | \ COGL_PIPELINE_STATE_LOGIC_OPS | \
COGL_PIPELINE_STATE_CULL_FACE | \ COGL_PIPELINE_STATE_CULL_FACE | \
COGL_PIPELINE_STATE_UNIFORMS) COGL_PIPELINE_STATE_UNIFORMS | \
COGL_PIPELINE_STATE_VERTEX_SNIPPETS | \
COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS)
#define COGL_PIPELINE_STATE_MULTI_PROPERTY \ #define COGL_PIPELINE_STATE_MULTI_PROPERTY \
(COGL_PIPELINE_STATE_LAYERS | \ (COGL_PIPELINE_STATE_LAYERS | \
@ -263,11 +273,14 @@ typedef enum _CoglPipelineState
COGL_PIPELINE_STATE_FOG | \ COGL_PIPELINE_STATE_FOG | \
COGL_PIPELINE_STATE_LOGIC_OPS | \ COGL_PIPELINE_STATE_LOGIC_OPS | \
COGL_PIPELINE_STATE_CULL_FACE | \ COGL_PIPELINE_STATE_CULL_FACE | \
COGL_PIPELINE_STATE_UNIFORMS) COGL_PIPELINE_STATE_UNIFORMS | \
COGL_PIPELINE_STATE_VERTEX_SNIPPETS | \
COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS)
#define COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN \ #define COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN \
(COGL_PIPELINE_STATE_LAYERS | \ (COGL_PIPELINE_STATE_LAYERS | \
COGL_PIPELINE_STATE_USER_SHADER) COGL_PIPELINE_STATE_USER_SHADER | \
COGL_PIPELINE_STATE_VERTEX_SNIPPETS)
typedef enum typedef enum
{ {
@ -353,6 +366,35 @@ typedef struct
CoglBitmask changed_mask; CoglBitmask changed_mask;
} CoglPipelineUniformsState; } CoglPipelineUniformsState;
/* Enumeration of all the hook points that a snippet can be attached
to within a pipeline. Note that although there are currently only
two points that directly correspond to the two state flags, the
idea isn't that each new enum here will mean a state flag. The
state flags are just intended to mark the split between hooks that
affect the fragment shader and hooks that affect the vertex
shader. For example, if we add a hook to wrap around the processing
for a particular layer then that hook would be part of the fragment
snippets state. */
typedef enum
{
COGL_PIPELINE_SNIPPET_HOOK_VERTEX,
COGL_PIPELINE_SNIPPET_HOOK_FRAGMENT
} CoglPipelineSnippetHook;
typedef struct _CoglPipelineSnippet CoglPipelineSnippet;
COGL_LIST_HEAD (CoglPipelineSnippetList, CoglPipelineSnippet);
struct _CoglPipelineSnippet
{
COGL_LIST_ENTRY (CoglPipelineSnippet) list_node;
/* Hook where this snippet is attached */
CoglPipelineSnippetHook hook;
CoglSnippet *snippet;
};
typedef struct typedef struct
{ {
CoglPipelineLightingState lighting_state; CoglPipelineLightingState lighting_state;
@ -365,6 +407,8 @@ typedef struct
CoglPipelineLogicOpsState logic_ops_state; CoglPipelineLogicOpsState logic_ops_state;
CoglPipelineCullFaceState cull_face_state; CoglPipelineCullFaceState cull_face_state;
CoglPipelineUniformsState uniforms_state; CoglPipelineUniformsState uniforms_state;
CoglPipelineSnippetList vertex_snippets;
CoglPipelineSnippetList fragment_snippets;
} CoglPipelineBigState; } CoglPipelineBigState;
typedef enum typedef enum

View File

@ -31,6 +31,12 @@
CoglPipeline * CoglPipeline *
_cogl_pipeline_get_user_program (CoglPipeline *pipeline); _cogl_pipeline_get_user_program (CoglPipeline *pipeline);
gboolean
_cogl_pipeline_has_vertex_snippets (CoglPipeline *pipeline);
gboolean
_cogl_pipeline_has_fragment_snippets (CoglPipeline *pipeline);
void void
_cogl_pipeline_set_fog_state (CoglPipeline *pipeline, _cogl_pipeline_set_fog_state (CoglPipeline *pipeline,
const CoglPipelineFogState *fog_state); const CoglPipelineFogState *fog_state);
@ -83,6 +89,14 @@ gboolean
_cogl_pipeline_uniforms_state_equal (CoglPipeline *authority0, _cogl_pipeline_uniforms_state_equal (CoglPipeline *authority0,
CoglPipeline *authority1); CoglPipeline *authority1);
gboolean
_cogl_pipeline_vertex_snippets_state_equal (CoglPipeline *authority0,
CoglPipeline *authority1);
gboolean
_cogl_pipeline_fragment_snippets_state_equal (CoglPipeline *authority0,
CoglPipeline *authority1);
void void
_cogl_pipeline_hash_color_state (CoglPipeline *authority, _cogl_pipeline_hash_color_state (CoglPipeline *authority,
CoglPipelineHashState *state); CoglPipelineHashState *state);
@ -139,6 +153,14 @@ void
_cogl_pipeline_hash_uniforms_state (CoglPipeline *authority, _cogl_pipeline_hash_uniforms_state (CoglPipeline *authority,
CoglPipelineHashState *state); CoglPipelineHashState *state);
void
_cogl_pipeline_hash_vertex_snippets_state (CoglPipeline *authority,
CoglPipelineHashState *state);
void
_cogl_pipeline_hash_fragment_snippets_state (CoglPipeline *authority,
CoglPipelineHashState *state);
void void
_cogl_pipeline_compare_uniform_differences (unsigned long *differences, _cogl_pipeline_compare_uniform_differences (unsigned long *differences,
CoglPipeline *pipeline0, CoglPipeline *pipeline0,

View File

@ -35,6 +35,7 @@
#include "cogl-util.h" #include "cogl-util.h"
#include "cogl-depth-state-private.h" #include "cogl-depth-state-private.h"
#include "cogl-pipeline-state-private.h" #include "cogl-pipeline-state-private.h"
#include "cogl-snippet-private.h"
#include "string.h" #include "string.h"
@ -339,6 +340,41 @@ _cogl_pipeline_uniforms_state_equal (CoglPipeline *authority0,
return TRUE; return TRUE;
} }
static gboolean
_cogl_pipeline_snippet_list_equal (CoglPipelineSnippetList *list0,
CoglPipelineSnippetList *list1)
{
CoglPipelineSnippet *l0, *l1;
for (l0 = COGL_LIST_FIRST (list0), l1 = COGL_LIST_FIRST (list1);
l0 && l1;
l0 = COGL_LIST_NEXT (l0, list_node), l1 = COGL_LIST_NEXT (l1, list_node))
if (l0->hook != l1->hook || l0->snippet != l1->snippet)
return FALSE;
return l0 == NULL && l1 == NULL;
}
gboolean
_cogl_pipeline_vertex_snippets_state_equal (CoglPipeline *authority0,
CoglPipeline *authority1)
{
return _cogl_pipeline_snippet_list_equal (&authority0->big_state->
vertex_snippets,
&authority1->big_state->
vertex_snippets);
}
gboolean
_cogl_pipeline_fragment_snippets_state_equal (CoglPipeline *authority0,
CoglPipeline *authority1)
{
return _cogl_pipeline_snippet_list_equal (&authority0->big_state->
fragment_snippets,
&authority1->big_state->
fragment_snippets);
}
void void
cogl_pipeline_get_color (CoglPipeline *pipeline, cogl_pipeline_get_color (CoglPipeline *pipeline,
CoglColor *color) CoglColor *color)
@ -1548,6 +1584,114 @@ cogl_pipeline_set_uniform_matrix (CoglPipeline *pipeline,
value); value);
} }
static void
_cogl_pipeline_snippet_list_add (CoglPipelineSnippetList *list,
CoglPipelineSnippetHook hook,
CoglSnippet *snippet)
{
CoglPipelineSnippet *pipeline_snippet = g_slice_new (CoglPipelineSnippet);
pipeline_snippet->hook = hook;
pipeline_snippet->snippet = cogl_object_ref (snippet);
_cogl_snippet_make_immutable (pipeline_snippet->snippet);
if (COGL_LIST_EMPTY (list))
COGL_LIST_INSERT_HEAD (list, pipeline_snippet, list_node);
else
{
CoglPipelineSnippet *tail;
for (tail = COGL_LIST_FIRST (list);
COGL_LIST_NEXT (tail, list_node);
tail = COGL_LIST_NEXT (tail, list_node));
COGL_LIST_INSERT_AFTER (tail, pipeline_snippet, list_node);
}
}
static void
_cogl_pipeline_add_vertex_snippet (CoglPipeline *pipeline,
CoglPipelineSnippetHook hook,
CoglSnippet *snippet)
{
CoglPipelineState state = COGL_PIPELINE_STATE_VERTEX_SNIPPETS;
g_return_if_fail (cogl_is_pipeline (pipeline));
g_return_if_fail (cogl_is_snippet (snippet));
/* - 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);
_cogl_pipeline_snippet_list_add (&pipeline->big_state->vertex_snippets,
hook,
snippet);
}
void
cogl_pipeline_add_vertex_hook (CoglPipeline *pipeline,
CoglSnippet *snippet)
{
_cogl_pipeline_add_vertex_snippet (pipeline,
COGL_PIPELINE_SNIPPET_HOOK_VERTEX,
snippet);
}
static void
_cogl_pipeline_add_fragment_snippet (CoglPipeline *pipeline,
CoglPipelineSnippetHook hook,
CoglSnippet *snippet)
{
CoglPipelineState state = COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS;
g_return_if_fail (cogl_is_pipeline (pipeline));
g_return_if_fail (cogl_is_snippet (snippet));
/* - 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);
_cogl_pipeline_snippet_list_add (&pipeline->big_state->fragment_snippets,
hook,
snippet);
}
void
cogl_pipeline_add_fragment_hook (CoglPipeline *pipeline,
CoglSnippet *snippet)
{
_cogl_pipeline_add_fragment_snippet (pipeline,
COGL_PIPELINE_SNIPPET_HOOK_FRAGMENT,
snippet);
}
gboolean
_cogl_pipeline_has_vertex_snippets (CoglPipeline *pipeline)
{
CoglPipeline *authority =
_cogl_pipeline_get_authority (pipeline,
COGL_PIPELINE_STATE_VERTEX_SNIPPETS);
return !COGL_LIST_EMPTY (&authority->big_state->vertex_snippets);
}
gboolean
_cogl_pipeline_has_fragment_snippets (CoglPipeline *pipeline)
{
CoglPipeline *authority =
_cogl_pipeline_get_authority (pipeline,
COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS);
return !COGL_LIST_EMPTY (&authority->big_state->fragment_snippets);
}
void void
_cogl_pipeline_hash_color_state (CoglPipeline *authority, _cogl_pipeline_hash_color_state (CoglPipeline *authority,
CoglPipelineHashState *state) CoglPipelineHashState *state)
@ -1828,3 +1972,38 @@ _cogl_pipeline_compare_uniform_differences (unsigned long *differences,
} }
} }
} }
static void
_cogl_pipeline_snippet_list_hash (CoglPipelineSnippetList *list,
CoglPipelineHashState *state)
{
CoglPipelineSnippet *l;
COGL_LIST_FOREACH (l, list, list_node)
{
state->hash =
_cogl_util_one_at_a_time_hash (state->hash,
&l->hook,
sizeof (CoglPipelineSnippetHook));
state->hash =
_cogl_util_one_at_a_time_hash (state->hash,
&l->snippet,
sizeof (CoglSnippet *));
}
}
void
_cogl_pipeline_hash_vertex_snippets_state (CoglPipeline *authority,
CoglPipelineHashState *state)
{
_cogl_pipeline_snippet_list_hash (&authority->big_state->vertex_snippets,
state);
}
void
_cogl_pipeline_hash_fragment_snippets_state (CoglPipeline *authority,
CoglPipelineHashState *state)
{
_cogl_pipeline_snippet_list_hash (&authority->big_state->fragment_snippets,
state);
}

View File

@ -937,6 +937,64 @@ cogl_pipeline_set_uniform_matrix (CoglPipeline *pipeline,
gboolean transpose, gboolean transpose,
const float *value); const float *value);
/**
* cogl_pipeline_add_vertex_hook:
* @pipeline: A #CoglPipeline
* @snippet: The #CoglSnippet to add to the vertex processing hook
*
* Adds a shader snippet that will hook on to the vertex processing
* stage of @pipeline. This gives a chance for the application to
* modify the vertex attributes generated by the shader. Typically the
* snippet will modify cogl_color_out or cogl_position_out builtins.
*
* The declarations string in @snippet will be inserted in the
* global scope of the shader. Use this to declare any uniforms,
* attributes or functions that the snippet requires.
*
* The pre string in @snippet will be inserted at the top of the
* main() function before any vertex processing is done.
*
* The post string in @snippet will be inserted after all of the
* standard vertex processing is done. This can be used to modify the
* outputs.
*
* Since: 1.10
* Stability: Unstable
*/
void
cogl_pipeline_add_vertex_hook (CoglPipeline *pipeline,
CoglSnippet *snippet);
/**
* cogl_pipeline_add_fragment_hook:
* @pipeline: A #CoglPipeline
* @snippet: The #CoglSnippet to add to the fragment processing hook
*
* Adds a shader snippet that will hook on to the fragment processing
* stage of @pipeline. This gives a chance for the application to
* modify the fragment color generated by the shader. Typically the
* snippet will modify cogl_color_out.
*
* The declarations string in @snippet will be inserted in the
* global scope of the shader. Use this to declare any uniforms,
* attributes or functions that the snippet requires.
*
* The pre string in @snippet will be inserted at the top of the
* main() function before any fragment processing is done.
*
* The post string in @snippet will be inserted after all of the
* standard fragment processing is done. At this point the generated
* value for the rest of the pipeline state will already be in
* cogl_color_out so the application can modify the result by altering
* this variable.
*
* Since: 1.10
* Stability: Unstable
*/
void
cogl_pipeline_add_fragment_hook (CoglPipeline *pipeline,
CoglSnippet *snippet);
#endif /* COGL_ENABLE_EXPERIMENTAL_API */ #endif /* COGL_ENABLE_EXPERIMENTAL_API */
G_END_DECLS G_END_DECLS

View File

@ -31,6 +31,7 @@
#include "cogl-context-private.h" #include "cogl-context-private.h"
#include "cogl-pipeline-private.h" #include "cogl-pipeline-private.h"
#include "cogl-pipeline-state-private.h"
#include "cogl-pipeline-opengl-private.h" #include "cogl-pipeline-opengl-private.h"
#ifdef COGL_PIPELINE_VERTEND_FIXED #ifdef COGL_PIPELINE_VERTEND_FIXED
@ -59,6 +60,10 @@ _cogl_pipeline_vertend_fixed_start (CoglPipeline *pipeline,
if (ctx->driver == COGL_DRIVER_GLES2) if (ctx->driver == COGL_DRIVER_GLES2)
return FALSE; return FALSE;
/* Vertex snippets are only supported in the GLSL fragend */
if (_cogl_pipeline_has_vertex_snippets (pipeline))
return FALSE;
/* If there is a user program with a vertex shader then the /* If there is a user program with a vertex shader then the
appropriate backend for that language should handle it. We can appropriate backend for that language should handle it. We can
still use the fixed vertex backend if the program only contains still use the fixed vertex backend if the program only contains

View File

@ -128,6 +128,16 @@ _cogl_pipeline_vertend_glsl_get_shader (CoglPipeline *pipeline)
return 0; return 0;
} }
static CoglPipelineSnippetList *
get_vertex_snippets (CoglPipeline *pipeline)
{
pipeline =
_cogl_pipeline_get_authority (pipeline,
COGL_PIPELINE_STATE_VERTEX_SNIPPETS);
return &pipeline->big_state->vertex_snippets;
}
static gboolean static gboolean
_cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline, _cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline,
int n_layers, int n_layers,
@ -136,6 +146,7 @@ _cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline,
{ {
CoglPipelineShaderState *shader_state; CoglPipelineShaderState *shader_state;
CoglPipeline *template_pipeline = NULL; CoglPipeline *template_pipeline = NULL;
CoglPipelineSnippet *snippet;
CoglProgram *user_program; CoglProgram *user_program;
_COGL_GET_CONTEXT (ctx, FALSE); _COGL_GET_CONTEXT (ctx, FALSE);
@ -254,6 +265,37 @@ _cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline,
"main ()\n" "main ()\n"
"{\n"); "{\n");
COGL_LIST_FOREACH (snippet, get_vertex_snippets (pipeline), list_node)
{
const char *declarations =
cogl_snippet_get_declarations (snippet->snippet);
/* Add all of the declarations for vertex snippets */
if (declarations)
{
g_string_append (shader_state->header, declarations);
g_string_append_c (shader_state->header, '\n');
}
/* Add all of the pre-hooks for vertex processing */
if (snippet->hook == COGL_PIPELINE_SNIPPET_HOOK_VERTEX)
{
const char *pre =
cogl_snippet_get_pre (snippet->snippet);
if (pre)
{
g_string_append (shader_state->source, " {\n");
g_string_append (shader_state->source, pre);
g_string_append (shader_state->source, " }\n");
}
}
}
/* Enclose the generated vertex processing in a block so that any
variables declared in it won't be in the scope of the snippets */
g_string_append (shader_state->source, " {\n");
if (ctx->driver == COGL_DRIVER_GLES2) if (ctx->driver == COGL_DRIVER_GLES2)
/* There is no builtin uniform for the pointsize on GLES2 so we need /* There is no builtin uniform for the pointsize on GLES2 so we need
to copy it from the custom uniform in the vertex shader */ to copy it from the custom uniform in the vertex shader */
@ -350,6 +392,7 @@ _cogl_pipeline_vertend_glsl_end (CoglPipeline *pipeline,
GLint lengths[2]; GLint lengths[2];
GLint compile_status; GLint compile_status;
GLuint shader; GLuint shader;
CoglPipelineSnippet *snippet;
COGL_STATIC_COUNTER (vertend_glsl_compile_counter, COGL_STATIC_COUNTER (vertend_glsl_compile_counter,
"glsl vertex compile counter", "glsl vertex compile counter",
@ -363,7 +406,24 @@ _cogl_pipeline_vertend_glsl_end (CoglPipeline *pipeline,
"cogl_modelview_projection_matrix * " "cogl_modelview_projection_matrix * "
"cogl_position_in;\n" "cogl_position_in;\n"
" cogl_color_out = cogl_color_in;\n" " cogl_color_out = cogl_color_in;\n"
"}\n"); " }\n");
/* Add all of the post-hooks for vertex processing */
COGL_LIST_FOREACH (snippet, get_vertex_snippets (pipeline), list_node)
if (snippet->hook == COGL_PIPELINE_SNIPPET_HOOK_VERTEX)
{
const char *post =
cogl_snippet_get_post (snippet->snippet);
if (post)
{
g_string_append (shader_state->source, " {\n");
g_string_append (shader_state->source, post);
g_string_append (shader_state->source, " }\n");
}
}
g_string_append (shader_state->source, "}\n");
GE_RET( shader, ctx, glCreateShader (GL_VERTEX_SHADER) ); GE_RET( shader, ctx, glCreateShader (GL_VERTEX_SHADER) );

View File

@ -444,6 +444,22 @@ destroy_weak_children_cb (CoglNode *node,
return TRUE; return TRUE;
} }
static void
_cogl_pipeline_snippet_free (CoglPipelineSnippet *pipeline_snippet)
{
cogl_object_unref (pipeline_snippet->snippet);
g_slice_free (CoglPipelineSnippet, pipeline_snippet);
}
static void
_cogl_pipeline_snippet_list_free (CoglPipelineSnippetList *list)
{
CoglPipelineSnippet *pipeline_snippet, *tmp;
COGL_LIST_FOREACH_SAFE (pipeline_snippet, list, list_node, tmp)
_cogl_pipeline_snippet_free (pipeline_snippet);
}
static void static void
_cogl_pipeline_free (CoglPipeline *pipeline) _cogl_pipeline_free (CoglPipeline *pipeline)
{ {
@ -488,6 +504,12 @@ _cogl_pipeline_free (CoglPipeline *pipeline)
g_list_free (pipeline->layer_differences); g_list_free (pipeline->layer_differences);
} }
if (pipeline->differences & COGL_PIPELINE_STATE_VERTEX_SNIPPETS)
_cogl_pipeline_snippet_list_free (&pipeline->big_state->vertex_snippets);
if (pipeline->differences & COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS)
_cogl_pipeline_snippet_list_free (&pipeline->big_state->fragment_snippets);
g_list_free (pipeline->deprecated_get_layers_list); g_list_free (pipeline->deprecated_get_layers_list);
recursively_free_layer_caches (pipeline); recursively_free_layer_caches (pipeline);
@ -830,6 +852,30 @@ _cogl_pipeline_set_vertend (CoglPipeline *pipeline, int vertend)
pipeline->vertend = vertend; pipeline->vertend = vertend;
} }
static void
_cogl_pipeline_snippet_list_copy (CoglPipelineSnippetList *dst,
const CoglPipelineSnippetList *src)
{
CoglPipelineSnippet *tail = NULL;
const CoglPipelineSnippet *l;
COGL_LIST_INIT (dst);
COGL_LIST_FOREACH (l, src, list_node)
{
CoglPipelineSnippet *copy = g_slice_dup (CoglPipelineSnippet, l);
cogl_object_ref (copy->snippet);
if (tail)
COGL_LIST_INSERT_AFTER (tail, copy, list_node);
else
COGL_LIST_INSERT_HEAD (dst, copy, list_node);
tail = copy;
}
}
static void static void
_cogl_pipeline_copy_differences (CoglPipeline *dest, _cogl_pipeline_copy_differences (CoglPipeline *dest,
CoglPipeline *src, CoglPipeline *src,
@ -973,6 +1019,14 @@ _cogl_pipeline_copy_differences (CoglPipeline *dest,
_cogl_bitmask_init (&big_state->uniforms_state.changed_mask); _cogl_bitmask_init (&big_state->uniforms_state.changed_mask);
} }
if (differences & COGL_PIPELINE_STATE_VERTEX_SNIPPETS)
_cogl_pipeline_snippet_list_copy (&big_state->vertex_snippets,
&src->big_state->vertex_snippets);
if (differences & COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS)
_cogl_pipeline_snippet_list_copy (&big_state->fragment_snippets,
&src->big_state->fragment_snippets);
/* XXX: we shouldn't bother doing this in most cases since /* XXX: we shouldn't bother doing this in most cases since
* _copy_differences is typically used to initialize pipeline state * _copy_differences is typically used to initialize pipeline state
* by copying it from the current authority, so it's not actually * by copying it from the current authority, so it's not actually
@ -1066,6 +1120,16 @@ _cogl_pipeline_init_multi_property_sparse_state (CoglPipeline *pipeline,
uniforms_state->override_values = NULL; uniforms_state->override_values = NULL;
break; break;
} }
case COGL_PIPELINE_STATE_VERTEX_SNIPPETS:
_cogl_pipeline_snippet_list_copy (&pipeline->big_state->vertex_snippets,
&authority->big_state->vertex_snippets);
break;
case COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS:
_cogl_pipeline_snippet_list_copy (&pipeline->big_state->fragment_snippets,
&authority->big_state->
fragment_snippets);
break;
} }
} }
@ -2260,6 +2324,18 @@ _cogl_pipeline_equal (CoglPipeline *pipeline0,
_cogl_pipeline_uniforms_state_equal)) _cogl_pipeline_uniforms_state_equal))
goto done; goto done;
if (!simple_property_equal (authorities0, authorities1,
pipelines_difference,
COGL_PIPELINE_STATE_VERTEX_SNIPPETS_INDEX,
_cogl_pipeline_vertex_snippets_state_equal))
goto done;
if (!simple_property_equal (authorities0, authorities1,
pipelines_difference,
COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS_INDEX,
_cogl_pipeline_fragment_snippets_state_equal))
goto done;
if (pipelines_difference & COGL_PIPELINE_STATE_LAYERS) if (pipelines_difference & COGL_PIPELINE_STATE_LAYERS)
{ {
CoglPipelineStateIndex state_index = COGL_PIPELINE_STATE_LAYERS_INDEX; CoglPipelineStateIndex state_index = COGL_PIPELINE_STATE_LAYERS_INDEX;
@ -2669,9 +2745,13 @@ _cogl_pipeline_init_state_hash_functions (void)
_cogl_pipeline_hash_logic_ops_state; _cogl_pipeline_hash_logic_ops_state;
state_hash_functions[COGL_PIPELINE_STATE_UNIFORMS_INDEX] = state_hash_functions[COGL_PIPELINE_STATE_UNIFORMS_INDEX] =
_cogl_pipeline_hash_uniforms_state; _cogl_pipeline_hash_uniforms_state;
state_hash_functions[COGL_PIPELINE_STATE_VERTEX_SNIPPETS_INDEX] =
_cogl_pipeline_hash_vertex_snippets_state;
state_hash_functions[COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS_INDEX] =
_cogl_pipeline_hash_fragment_snippets_state;
/* So we get a big error if we forget to update this code! */ /* So we get a big error if we forget to update this code! */
g_assert (COGL_PIPELINE_STATE_SPARSE_COUNT == 14); g_assert (COGL_PIPELINE_STATE_SPARSE_COUNT == 16);
} }
unsigned int unsigned int
@ -2860,7 +2940,8 @@ CoglPipelineState
_cogl_pipeline_get_state_for_fragment_codegen (CoglContext *context) _cogl_pipeline_get_state_for_fragment_codegen (CoglContext *context)
{ {
CoglPipelineState state = (COGL_PIPELINE_STATE_LAYERS | CoglPipelineState state = (COGL_PIPELINE_STATE_LAYERS |
COGL_PIPELINE_STATE_USER_SHADER); COGL_PIPELINE_STATE_USER_SHADER |
COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS);
if (context->driver == COGL_DRIVER_GLES2) if (context->driver == COGL_DRIVER_GLES2)
state |= COGL_PIPELINE_STATE_ALPHA_FUNC; state |= COGL_PIPELINE_STATE_ALPHA_FUNC;

View File

@ -29,6 +29,7 @@
#define __COGL_PIPELINE_H__ #define __COGL_PIPELINE_H__
#include <cogl/cogl-types.h> #include <cogl/cogl-types.h>
#include <cogl/cogl-snippet.h>
G_BEGIN_DECLS G_BEGIN_DECLS