mirror of
https://github.com/brl/mutter.git
synced 2024-12-26 04:42:14 +00:00
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:
parent
f37738453e
commit
d38ae0284b
@ -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;
|
||||
CoglPipeline *template =
|
||||
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 =
|
||||
_cogl_pipeline_get_state_for_fragment_codegen (ctx);
|
||||
pipeline_layer_state_for_fragment_codegen =
|
||||
_cogl_pipeline_get_layer_state_for_fragment_codegen (ctx);
|
||||
g_hash_table_insert (cache->combined_hash,
|
||||
template,
|
||||
cogl_object_ref (template));
|
||||
|
||||
/* 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 */
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return _cogl_pipeline_cache_get_fragment_template (cache, key_pipeline);
|
||||
return template;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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) );
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
@ -139,6 +153,14 @@ void
|
||||
_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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) );
|
||||
|
||||
|
@ -444,6 +444,22 @@ destroy_weak_children_cb (CoglNode *node,
|
||||
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
|
||||
_cogl_pipeline_free (CoglPipeline *pipeline)
|
||||
{
|
||||
@ -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);
|
||||
@ -830,6 +852,30 @@ _cogl_pipeline_set_vertend (CoglPipeline *pipeline, int 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
|
||||
_cogl_pipeline_copy_differences (CoglPipeline *dest,
|
||||
CoglPipeline *src,
|
||||
@ -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;
|
||||
|
@ -29,6 +29,7 @@
|
||||
#define __COGL_PIPELINE_H__
|
||||
|
||||
#include <cogl/cogl-types.h>
|
||||
#include <cogl/cogl-snippet.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user