[cogl/wip/neil/snippets: 8/27] cogl-pipeline: Add two hook points for adding shader snippets
- From: Neil Roberts <nroberts src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [cogl/wip/neil/snippets: 8/27] cogl-pipeline: Add two hook points for adding shader snippets
- Date: Tue, 6 Dec 2011 17:11:32 +0000 (UTC)
commit ce1799f9b96c67574af1c9b1de68d92000fd3b36
Author: Neil Roberts <neil linux intel com>
Date: Thu Nov 17 16:52:21 2011 +0000
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.
cogl/cogl-pipeline-cache.c | 84 +++++++++++++----
cogl/cogl-pipeline-fragend-arbfp.c | 5 +
cogl/cogl-pipeline-fragend-fixed.c | 5 +
cogl/cogl-pipeline-fragend-glsl.c | 61 ++++++++++++
cogl/cogl-pipeline-private.h | 52 ++++++++++-
cogl/cogl-pipeline-state-private.h | 22 +++++
cogl/cogl-pipeline-state.c | 179 ++++++++++++++++++++++++++++++++++++
cogl/cogl-pipeline-state.h | 58 ++++++++++++
cogl/cogl-pipeline-vertend-fixed.c | 5 +
cogl/cogl-pipeline-vertend-glsl.c | 62 ++++++++++++-
cogl/cogl-pipeline.c | 85 +++++++++++++++++-
cogl/cogl-pipeline.h | 1 +
12 files changed, 593 insertions(+), 26 deletions(-)
---
diff --git a/cogl/cogl-pipeline-cache.c b/cogl/cogl-pipeline-cache.c
index b5d99da..5df18ce 100644
--- a/cogl/cogl-pipeline-cache.c
+++ b/cogl/cogl-pipeline-cache.c
@@ -37,6 +37,7 @@ struct _CoglPipelineCache
{
GHashTable *fragment_hash;
GHashTable *vertex_hash;
+ GHashTable *combined_hash;
};
static unsigned int
@@ -101,6 +102,46 @@ pipeline_vertex_equal (const void *a, const void *b)
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 *
cogl_pipeline_cache_new (void)
{
@@ -114,6 +155,10 @@ cogl_pipeline_cache_new (void)
pipeline_vertex_equal,
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;
}
@@ -123,6 +168,7 @@ cogl_pipeline_cache_free (CoglPipelineCache *cache)
{
g_hash_table_destroy (cache->fragment_hash);
g_hash_table_destroy (cache->vertex_hash);
+ g_hash_table_destroy (cache->combined_hash);
g_free (cache);
}
@@ -210,27 +256,27 @@ CoglPipeline *
_cogl_pipeline_cache_get_combined_template (CoglPipelineCache *cache,
CoglPipeline *key_pipeline)
{
- unsigned int pipeline_state_for_fragment_codegen;
- unsigned int pipeline_layer_state_for_fragment_codegen;
-
- _COGL_GET_CONTEXT (ctx, NULL);
+ CoglPipeline *template =
+ g_hash_table_lookup (cache->combined_hash, key_pipeline);
- pipeline_state_for_fragment_codegen =
- _cogl_pipeline_get_state_for_fragment_codegen (ctx);
- pipeline_layer_state_for_fragment_codegen =
- _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx);
+ if (template == NULL)
+ {
+ template = cogl_pipeline_copy (key_pipeline);
- /* Currently the vertex shader state is a subset of the fragment
- shader state so we can avoid a third hash table here by just
- using the fragment shader table. This assert should catch it if
- that ever changes */
+ g_hash_table_insert (cache->combined_hash,
+ template,
+ cogl_object_ref (template));
- g_assert ((pipeline_state_for_fragment_codegen |
- 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);
+ if (G_UNLIKELY (g_hash_table_size (cache->combined_hash) > 50))
+ {
+ static gboolean seen = FALSE;
+ if (!seen)
+ g_warning ("Over 50 separate programs have been "
+ "generated which is very unusual, so something "
+ "is probably wrong!\n");
+ seen = TRUE;
+ }
+ }
- return _cogl_pipeline_cache_get_fragment_template (cache, key_pipeline);
+ return template;
}
diff --git a/cogl/cogl-pipeline-fragend-arbfp.c b/cogl/cogl-pipeline-fragend-arbfp.c
index 10bd1bd..b08899b 100644
--- a/cogl/cogl-pipeline-fragend-arbfp.c
+++ b/cogl/cogl-pipeline-fragend-arbfp.c
@@ -32,6 +32,7 @@
#include "cogl-debug.h"
#include "cogl-context-private.h"
#include "cogl-pipeline-private.h"
+#include "cogl-pipeline-state-private.h"
#include "cogl-pipeline-layer-private.h"
#ifdef COGL_PIPELINE_FRAGEND_ARBFP
@@ -178,6 +179,10 @@ _cogl_pipeline_fragend_arbfp_start (CoglPipeline *pipeline,
if (_cogl_pipeline_get_fog_enabled (pipeline))
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);
if (user_program != COGL_INVALID_HANDLE)
{
diff --git a/cogl/cogl-pipeline-fragend-fixed.c b/cogl/cogl-pipeline-fragend-fixed.c
index e6adfca..64b84b8 100644
--- a/cogl/cogl-pipeline-fragend-fixed.c
+++ b/cogl/cogl-pipeline-fragend-fixed.c
@@ -31,6 +31,7 @@
#include "cogl-context-private.h"
#include "cogl-pipeline-private.h"
+#include "cogl-pipeline-state-private.h"
#include "cogl-pipeline-opengl-private.h"
#ifdef COGL_PIPELINE_FRAGEND_FIXED
@@ -101,6 +102,10 @@ _cogl_pipeline_fragend_fixed_start (CoglPipeline *pipeline,
if (ctx->driver == COGL_DRIVER_GLES2)
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
appropriate backend for that language should handle it. We can
still use the fixed fragment backend if the program only contains
diff --git a/cogl/cogl-pipeline-fragend-glsl.c b/cogl/cogl-pipeline-fragend-glsl.c
index 488070b..d445190 100644
--- a/cogl/cogl-pipeline-fragend-glsl.c
+++ b/cogl/cogl-pipeline-fragend-glsl.c
@@ -181,6 +181,16 @@ _cogl_pipeline_fragend_glsl_get_shader (CoglPipeline *pipeline)
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
_cogl_pipeline_fragend_glsl_start (CoglPipeline *pipeline,
int n_layers,
@@ -190,6 +200,7 @@ _cogl_pipeline_fragend_glsl_start (CoglPipeline *pipeline,
CoglPipelineShaderState *shader_state;
CoglPipeline *authority;
CoglPipeline *template_pipeline = NULL;
+ CoglPipelineSnippet *snippet;
CoglProgram *user_program;
int i;
@@ -319,6 +330,37 @@ _cogl_pipeline_fragend_glsl_start (CoglPipeline *pipeline,
"main ()\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++)
{
shader_state->unit_state[i].sampled = FALSE;
@@ -885,6 +927,7 @@ _cogl_pipeline_fragend_glsl_end (CoglPipeline *pipeline,
GLint lengths[2];
GLint compile_status;
GLuint shader;
+ CoglPipelineSnippet *snippet;
COGL_STATIC_COUNTER (fragend_glsl_compile_counter,
"glsl fragment compile counter",
@@ -922,6 +965,24 @@ _cogl_pipeline_fragend_glsl_end (CoglPipeline *pipeline,
add_alpha_test_snippet (pipeline, shader_state);
#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");
GE_RET( shader, ctx, glCreateShader (GL_FRAGMENT_SHADER) );
diff --git a/cogl/cogl-pipeline-private.h b/cogl/cogl-pipeline-private.h
index a6100a8..e518ecf 100644
--- a/cogl/cogl-pipeline-private.h
+++ b/cogl/cogl-pipeline-private.h
@@ -168,6 +168,8 @@ typedef enum
COGL_PIPELINE_STATE_LOGIC_OPS_INDEX,
COGL_PIPELINE_STATE_CULL_FACE_INDEX,
COGL_PIPELINE_STATE_UNIFORMS_INDEX,
+ COGL_PIPELINE_STATE_VERTEX_SNIPPETS_INDEX,
+ COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS_INDEX,
/* non-sparse */
COGL_PIPELINE_STATE_REAL_BLEND_ENABLE_INDEX,
@@ -217,6 +219,10 @@ typedef enum _CoglPipelineState
1L<<COGL_PIPELINE_STATE_CULL_FACE_INDEX,
COGL_PIPELINE_STATE_UNIFORMS =
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 =
1L<<COGL_PIPELINE_STATE_REAL_BLEND_ENABLE_INDEX,
@@ -240,7 +246,9 @@ typedef enum _CoglPipelineState
COGL_PIPELINE_STATE_LAYERS | \
COGL_PIPELINE_STATE_LIGHTING | \
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 \
(COGL_PIPELINE_STATE_LIGHTING | \
@@ -253,7 +261,9 @@ typedef enum _CoglPipelineState
COGL_PIPELINE_STATE_POINT_SIZE | \
COGL_PIPELINE_STATE_LOGIC_OPS | \
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 \
(COGL_PIPELINE_STATE_LAYERS | \
@@ -263,11 +273,14 @@ typedef enum _CoglPipelineState
COGL_PIPELINE_STATE_FOG | \
COGL_PIPELINE_STATE_LOGIC_OPS | \
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 \
(COGL_PIPELINE_STATE_LAYERS | \
- COGL_PIPELINE_STATE_USER_SHADER)
+ COGL_PIPELINE_STATE_USER_SHADER | \
+ COGL_PIPELINE_STATE_VERTEX_SNIPPETS)
typedef enum
{
@@ -353,6 +366,35 @@ typedef struct
CoglBitmask changed_mask;
} 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
{
CoglPipelineLightingState lighting_state;
@@ -365,6 +407,8 @@ typedef struct
CoglPipelineLogicOpsState logic_ops_state;
CoglPipelineCullFaceState cull_face_state;
CoglPipelineUniformsState uniforms_state;
+ CoglPipelineSnippetList vertex_snippets;
+ CoglPipelineSnippetList fragment_snippets;
} CoglPipelineBigState;
typedef enum
diff --git a/cogl/cogl-pipeline-state-private.h b/cogl/cogl-pipeline-state-private.h
index ef7665a..9a38e11 100644
--- a/cogl/cogl-pipeline-state-private.h
+++ b/cogl/cogl-pipeline-state-private.h
@@ -31,6 +31,12 @@
CoglPipeline *
_cogl_pipeline_get_user_program (CoglPipeline *pipeline);
+gboolean
+_cogl_pipeline_has_vertex_snippets (CoglPipeline *pipeline);
+
+gboolean
+_cogl_pipeline_has_fragment_snippets (CoglPipeline *pipeline);
+
void
_cogl_pipeline_set_fog_state (CoglPipeline *pipeline,
const CoglPipelineFogState *fog_state);
@@ -83,6 +89,14 @@ gboolean
_cogl_pipeline_uniforms_state_equal (CoglPipeline *authority0,
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
_cogl_pipeline_hash_color_state (CoglPipeline *authority,
CoglPipelineHashState *state);
@@ -140,6 +154,14 @@ _cogl_pipeline_hash_uniforms_state (CoglPipeline *authority,
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
_cogl_pipeline_compare_uniform_differences (unsigned long *differences,
CoglPipeline *pipeline0,
CoglPipeline *pipeline1);
diff --git a/cogl/cogl-pipeline-state.c b/cogl/cogl-pipeline-state.c
index 88bf153..56cdb16 100644
--- a/cogl/cogl-pipeline-state.c
+++ b/cogl/cogl-pipeline-state.c
@@ -35,6 +35,7 @@
#include "cogl-util.h"
#include "cogl-depth-state-private.h"
#include "cogl-pipeline-state-private.h"
+#include "cogl-snippet-private.h"
#include "string.h"
@@ -339,6 +340,41 @@ _cogl_pipeline_uniforms_state_equal (CoglPipeline *authority0,
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
cogl_pipeline_get_color (CoglPipeline *pipeline,
CoglColor *color)
@@ -1548,6 +1584,114 @@ cogl_pipeline_set_uniform_matrix (CoglPipeline *pipeline,
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
_cogl_pipeline_hash_color_state (CoglPipeline *authority,
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);
+}
diff --git a/cogl/cogl-pipeline-state.h b/cogl/cogl-pipeline-state.h
index b69966c..d2c0ada 100644
--- a/cogl/cogl-pipeline-state.h
+++ b/cogl/cogl-pipeline-state.h
@@ -937,6 +937,64 @@ cogl_pipeline_set_uniform_matrix (CoglPipeline *pipeline,
gboolean transpose,
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 */
G_END_DECLS
diff --git a/cogl/cogl-pipeline-vertend-fixed.c b/cogl/cogl-pipeline-vertend-fixed.c
index 5850677..3b19016 100644
--- a/cogl/cogl-pipeline-vertend-fixed.c
+++ b/cogl/cogl-pipeline-vertend-fixed.c
@@ -31,6 +31,7 @@
#include "cogl-context-private.h"
#include "cogl-pipeline-private.h"
+#include "cogl-pipeline-state-private.h"
#include "cogl-pipeline-opengl-private.h"
#ifdef COGL_PIPELINE_VERTEND_FIXED
@@ -59,6 +60,10 @@ _cogl_pipeline_vertend_fixed_start (CoglPipeline *pipeline,
if (ctx->driver == COGL_DRIVER_GLES2)
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
appropriate backend for that language should handle it. We can
still use the fixed vertex backend if the program only contains
diff --git a/cogl/cogl-pipeline-vertend-glsl.c b/cogl/cogl-pipeline-vertend-glsl.c
index dcd6ef5..a6cadee 100644
--- a/cogl/cogl-pipeline-vertend-glsl.c
+++ b/cogl/cogl-pipeline-vertend-glsl.c
@@ -128,6 +128,16 @@ _cogl_pipeline_vertend_glsl_get_shader (CoglPipeline *pipeline)
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
_cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline,
int n_layers,
@@ -136,6 +146,7 @@ _cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline,
{
CoglPipelineShaderState *shader_state;
CoglPipeline *template_pipeline = NULL;
+ CoglPipelineSnippet *snippet;
CoglProgram *user_program;
_COGL_GET_CONTEXT (ctx, FALSE);
@@ -254,6 +265,37 @@ _cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline,
"main ()\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)
/* There is no builtin uniform for the pointsize on GLES2 so we need
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 compile_status;
GLuint shader;
+ CoglPipelineSnippet *snippet;
COGL_STATIC_COUNTER (vertend_glsl_compile_counter,
"glsl vertex compile counter",
@@ -363,7 +406,24 @@ _cogl_pipeline_vertend_glsl_end (CoglPipeline *pipeline,
"cogl_modelview_projection_matrix * "
"cogl_position_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) );
diff --git a/cogl/cogl-pipeline.c b/cogl/cogl-pipeline.c
index 6b84b3f..e47a5de 100644
--- a/cogl/cogl-pipeline.c
+++ b/cogl/cogl-pipeline.c
@@ -445,6 +445,22 @@ destroy_weak_children_cb (CoglNode *node,
}
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
_cogl_pipeline_free (CoglPipeline *pipeline)
{
if (!pipeline->is_weak)
@@ -488,6 +504,12 @@ _cogl_pipeline_free (CoglPipeline *pipeline)
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);
recursively_free_layer_caches (pipeline);
@@ -831,6 +853,30 @@ _cogl_pipeline_set_vertend (CoglPipeline *pipeline, int 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
_cogl_pipeline_copy_differences (CoglPipeline *dest,
CoglPipeline *src,
unsigned long differences)
@@ -973,6 +1019,14 @@ _cogl_pipeline_copy_differences (CoglPipeline *dest,
_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
* _copy_differences is typically used to initialize pipeline state
* 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;
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))
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)
{
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;
state_hash_functions[COGL_PIPELINE_STATE_UNIFORMS_INDEX] =
_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! */
- g_assert (COGL_PIPELINE_STATE_SPARSE_COUNT == 14);
+ g_assert (COGL_PIPELINE_STATE_SPARSE_COUNT == 16);
}
unsigned int
@@ -2860,7 +2940,8 @@ CoglPipelineState
_cogl_pipeline_get_state_for_fragment_codegen (CoglContext *context)
{
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)
state |= COGL_PIPELINE_STATE_ALPHA_FUNC;
diff --git a/cogl/cogl-pipeline.h b/cogl/cogl-pipeline.h
index 11b71a0..b66f18d 100644
--- a/cogl/cogl-pipeline.h
+++ b/cogl/cogl-pipeline.h
@@ -29,6 +29,7 @@
#define __COGL_PIPELINE_H__
#include <cogl/cogl-types.h>
+#include <cogl/cogl-snippet.h>
G_BEGIN_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]