cogl: Add pipeline hooks to user program pipelines

Instead of just adding the cogl header boilerplate, we'll redirect the
main function with #define and #undef to cogl_main(). The real main
calls the hooks-generated cogl_hooks() which chains up to the users
main() which is now called cogl_main().

    <boilerplate>
    #define main cogl_main
    <user shader>
    <hook functions>
    void
    cogl_hooks () {
      cogl_main ();
      <hook code>
    }
    #undef main
    void main () { cogl_hooks(); }

This allows the user shader to continue using shaders which seem like
they define the main function and output to the framebuffer but also
gives the CoglPipeline a chance to add hooks.

This in particular makes our ClutterColorState transform hooks work on
user programs which are used by ClutterShaderEffects.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/7804
Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/7805
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3662
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4037>
This commit is contained in:
Sebastian Wick
2024-09-18 19:37:26 +02:00
committed by Marge Bot
parent 62e5706ed5
commit 5e98ee5dbe

View File

@ -699,24 +699,75 @@ _cogl_pipeline_progend_glsl_start (CoglPipeline *pipeline)
return TRUE;
}
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 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
needs_recompile (CoglShader *shader,
CoglPipeline *pipeline,
CoglPipeline *prev)
{
/* XXX: currently the only things that will affect the
* boilerplate for user shaders, apart from driver features,
* are the pipeline layer-indices, texture-unit-indices and
* snippets
*/
if (pipeline == prev)
return FALSE;
if (!_cogl_pipeline_layer_and_unit_numbers_equal (prev, pipeline))
return TRUE;
switch (shader->type)
{
case COGL_SHADER_TYPE_VERTEX:
if (!_cogl_pipeline_vertex_snippets_state_equal (prev, pipeline))
return TRUE;
break;
case COGL_SHADER_TYPE_FRAGMENT:
if (!_cogl_pipeline_fragment_snippets_state_equal (prev, pipeline))
return TRUE;
break;
default:
g_assert_not_reached ();
break;
}
return FALSE;
}
static void
_cogl_shader_compile_real (CoglShader *shader,
CoglPipeline *pipeline)
{
g_autoptr(GString) hooks_source = NULL;
CoglPipelineSnippetData snippet_data;
const char *shader_sources[4];
GLenum gl_type;
GLint status;
CoglContext *ctx = pipeline->context;
if (shader->gl_handle)
{
CoglPipeline *prev = shader->compilation_pipeline;
/* XXX: currently the only things that will affect the
* boilerplate for user shaders, apart from driver features,
* are the pipeline layer-indices and texture-unit-indices
*/
if (pipeline == prev ||
_cogl_pipeline_layer_and_unit_numbers_equal (prev, pipeline))
if (!needs_recompile (shader, pipeline, shader->compilation_pipeline))
return;
GE (ctx, glDeleteShader (shader->gl_handle));
@ -729,28 +780,47 @@ _cogl_shader_compile_real (CoglShader *shader,
}
}
hooks_source = g_string_new ("");
memset (&snippet_data, 0, sizeof (snippet_data));
snippet_data.chain_function = "cogl_main";
snippet_data.final_name = "cogl_hooks";
snippet_data.source_buf = hooks_source;
switch (shader->type)
{
case COGL_SHADER_TYPE_VERTEX:
gl_type = GL_VERTEX_SHADER;
snippet_data.snippets = get_vertex_snippets (pipeline);
snippet_data.hook = COGL_SNIPPET_HOOK_VERTEX;
snippet_data.function_prefix = "cogl_vertex_hook";
break;
case COGL_SHADER_TYPE_FRAGMENT:
gl_type = GL_FRAGMENT_SHADER;
snippet_data.snippets = get_fragment_snippets (pipeline);
snippet_data.hook = COGL_SNIPPET_HOOK_FRAGMENT;
snippet_data.function_prefix = "cogl_fragment_hook";
break;
default:
g_assert_not_reached ();
break;
}
_cogl_pipeline_snippet_generate_code (&snippet_data);
shader_sources[0] = "#define main cogl_main\n";
shader_sources[1] = shader->source;
shader_sources[2] = hooks_source->str;
shader_sources[3] = "#undef main\n"
"void main () { cogl_hooks(); }\n";
shader->gl_handle = ctx->glCreateShader (gl_type);
_cogl_glsl_shader_set_source_with_boilerplate (ctx,
shader->gl_handle,
gl_type,
pipeline,
1,
(const char **)
&shader->source,
G_N_ELEMENTS (shader_sources),
shader_sources,
NULL);
GE (ctx, glCompileShader (shader->gl_handle));