From 4d697283aeaaff0973120dc71aa8f3ae9337898c Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 18 Sep 2020 18:03:30 +0200 Subject: [PATCH] Support GLShaderNode in backends For vulkan/broadway this just means to ignore it, but for the gl backend we support (with up to 4 texture inputs, which is similar to what shadertoy does, so should be widely supported). --- gsk/broadway/gskbroadwayrenderer.c | 2 + gsk/gl/gskglrenderer.c | 382 ++++++++++++++++++++++++++--- gsk/gl/gskglrendererprivate.h | 14 ++ gsk/gl/gskglrenderops.c | 28 +++ gsk/gl/gskglrenderopsprivate.h | 18 +- gsk/gl/gskglshaderbuilder.c | 8 +- gsk/gl/gskglshaderbuilderprivate.h | 2 + gsk/gl/opbuffer.c | 2 + gsk/gl/opbuffer.h | 15 ++ gsk/meson.build | 1 + gsk/resources/glsl/custom.glsl | 21 ++ gsk/vulkan/gskvulkanrenderpass.c | 1 + 12 files changed, 451 insertions(+), 43 deletions(-) create mode 100644 gsk/gl/gskglrendererprivate.h create mode 100644 gsk/resources/glsl/custom.glsl diff --git a/gsk/broadway/gskbroadwayrenderer.c b/gsk/broadway/gskbroadwayrenderer.c index e18b03f826..bb6de84846 100644 --- a/gsk/broadway/gskbroadwayrenderer.c +++ b/gsk/broadway/gskbroadwayrenderer.c @@ -258,6 +258,7 @@ collect_reused_child_nodes (GskRenderer *renderer, case GSK_LINEAR_GRADIENT_NODE: /* Fallbacks (=> leaf for now */ + case GSK_GL_SHADER_NODE: case GSK_COLOR_MATRIX_NODE: case GSK_TEXT_NODE: case GSK_REPEATING_LINEAR_GRADIENT_NODE: @@ -847,6 +848,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer, case GSK_BLEND_NODE: case GSK_CROSS_FADE_NODE: case GSK_BLUR_NODE: + case GSK_GL_SHADER_NODE: default: break; /* Fallback */ } diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index a465bcfd79..6efe62bca7 100644 --- a/gsk/gl/gskglrenderer.c +++ b/gsk/gl/gskglrenderer.c @@ -1,6 +1,6 @@ #include "config.h" -#include "gskglrenderer.h" +#include "gskglrendererprivate.h" #include "gskdebugprivate.h" #include "gskenums.h" @@ -19,6 +19,7 @@ #include "gskglnodesampleprivate.h" #include "gsktransform.h" #include "glutilsprivate.h" +#include "gskglshaderprivate.h" #include "gskprivate.h" @@ -64,6 +65,11 @@ glGetUniformLocation(program_ptr->id, "u_" #uniform_basename);\ }G_STMT_END +static Program *gsk_gl_renderer_lookup_custom_program (GskGLRenderer *self, + GskGLShader *shader); +static Program *gsk_gl_renderer_create_custom_program (GskGLRenderer *self, + GskGLShader *shader); + typedef enum { FORCE_OFFSCREEN = 1 << 0, @@ -130,6 +136,12 @@ print_render_node_tree (GskRenderNode *root, int level) print_render_node_tree (gsk_shadow_node_get_child (root), level + 1); break; + case GSK_GL_SHADER_NODE: + g_print ("%*s GL Shader\n", level * INDENT, " "); + for (i = 0; i < gsk_gl_shader_node_get_n_children (root); i++) + print_render_node_tree (gsk_gl_shader_node_get_child (root, i), level + 1); + break; + case GSK_TEXTURE_NODE: g_print ("%*s Texture %p\n", level * INDENT, " ", gsk_texture_node_get_texture (root)); break; @@ -495,6 +507,40 @@ struct _GskGLRendererClass G_DEFINE_TYPE (GskGLRenderer, gsk_gl_renderer, GSK_TYPE_RENDERER) +static void +init_shader_builder (GskGLRenderer *self, + GskGLShaderBuilder *shader_builder) +{ +#ifdef G_ENABLE_DEBUG + if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), SHADERS)) + shader_builder->debugging = TRUE; +#endif + + if (gdk_gl_context_get_use_es (self->gl_context)) + { + gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GLES); + shader_builder->gles = TRUE; + } + else if (gdk_gl_context_is_legacy (self->gl_context)) + { + int maj, min; + + gdk_gl_context_get_version (self->gl_context, &maj, &min); + + if (maj == 3) + gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GL3_LEGACY); + else + gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GL2_LEGACY); + + shader_builder->legacy = TRUE; + } + else + { + gsk_gl_shader_builder_set_glsl_version (shader_builder, SHADER_VERSION_GL3); + shader_builder->gl3 = TRUE; + } +} + static void G_GNUC_UNUSED add_rect_ops (RenderOpBuilder *builder, const graphene_rect_t *r) @@ -1011,6 +1057,178 @@ render_texture_node (GskGLRenderer *self, } } +static Program * +compile_glshader (GskGLRenderer *self, + GskGLShader *shader, + GError **error) +{ + GskGLShaderBuilder shader_builder; + const char *shader_source; + gsize shader_source_len; + int n_uniforms; + const GskGLUniform *uniforms; + GBytes *bytes; + int n_required_textures = gsk_gl_shader_get_n_textures (shader); + int program_id; + Program *program; + + bytes = gsk_gl_shader_get_source (shader); + shader_source = g_bytes_get_data (bytes, &shader_source_len); + uniforms = gsk_gl_shader_get_uniforms (shader, &n_uniforms); + + if (n_uniforms > G_N_ELEMENTS (program->glshader.args_locations)) + { + g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_UNSUPPORTED_FORMAT, + "GLShaderNode supports max %d custom uniforms", (int)G_N_ELEMENTS (program->glshader.args_locations)); + return NULL; + } + + if (n_required_textures > G_N_ELEMENTS (program->glshader.texture_locations)) + { + g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_UNSUPPORTED_FORMAT, + "GLShaderNode supports max %d texture sources", (int)(G_N_ELEMENTS (program->glshader.texture_locations))); + return NULL; + } + + gsk_gl_shader_builder_init (&shader_builder, + "/org/gtk/libgsk/glsl/preamble.glsl", + "/org/gtk/libgsk/glsl/preamble.vs.glsl", + "/org/gtk/libgsk/glsl/preamble.fs.glsl"); + + init_shader_builder (self, &shader_builder); + program_id = gsk_gl_shader_builder_create_program (&shader_builder, + "/org/gtk/libgsk/glsl/custom.glsl", + shader_source, shader_source_len, + error); + gsk_gl_shader_builder_finish (&shader_builder); + + if (program_id < 0) + return NULL; + + program = gsk_gl_renderer_create_custom_program (self, shader); + + program->id = program_id; + INIT_COMMON_UNIFORM_LOCATION (program, alpha); + INIT_COMMON_UNIFORM_LOCATION (program, clip_rect); + INIT_COMMON_UNIFORM_LOCATION (program, viewport); + INIT_COMMON_UNIFORM_LOCATION (program, projection); + INIT_COMMON_UNIFORM_LOCATION (program, modelview); + program->glshader.size_location = glGetUniformLocation(program->id, "u_size"); + program->glshader.texture_locations[0] = glGetUniformLocation(program->id, "u_texture1"); + program->glshader.texture_locations[1] = glGetUniformLocation(program->id, "u_texture2"); + program->glshader.texture_locations[2] = glGetUniformLocation(program->id, "u_texture3"); + program->glshader.texture_locations[3] = glGetUniformLocation(program->id, "u_texture4"); + + /* We use u_textue1 for the texture 0 in the glshaders, so alias it here so we can use the regular setters */ + program->source_location = program->glshader.texture_locations[0]; + + for (int i = 0; i < G_N_ELEMENTS (program->glshader.args_locations); i++) + { + if (i < n_uniforms) + { + program->glshader.args_locations[i] = glGetUniformLocation(program->id, uniforms[i].name); + /* This isn't necessary a hard error, you might declare uniforms that are not actually + always used, for instance if you have an "API" in uniforms for multiple shaders. */ + if (program->glshader.args_locations[i] == -1) + g_debug ("Declared uniform `%s` not found in GskGLShader", uniforms[i].name); + } + else + program->glshader.args_locations[i] = -1; + } + + return program; +} + +gboolean +gsk_gl_render_try_compile_gl_shader (GskGLRenderer *self, + GskGLShader *shader, + GError **error) +{ + Program *program; + + gdk_gl_context_make_current (self->gl_context); + + /* Maybe we tried to compile it already? */ + program = gsk_gl_renderer_lookup_custom_program (self, shader); + if (program != NULL) + { + if (program->id > 0) + return TRUE; + else + { + g_propagate_error (error, g_error_copy (program->glshader.compile_error)); + return FALSE; + } + } + + program = compile_glshader (self, shader, error); + return program != NULL; +} + +static inline void +render_gl_shader_node (GskGLRenderer *self, + GskRenderNode *node, + RenderOpBuilder *builder) +{ + GskGLShader *shader = gsk_gl_shader_node_get_shader (node); + Program *program = gsk_gl_renderer_lookup_custom_program (self, shader); + int n_children = gsk_gl_shader_node_get_n_children (node); + + if (program == NULL) + { + GError *error = NULL; + + program = compile_glshader (self, shader, &error); + if (program == NULL) + { + /* We create the program anyway (in a failed state), so that any compiler warnings or other are only reported once */ + program = gsk_gl_renderer_create_custom_program (self, shader); + program->id = -1; + program->glshader.compile_error = error; + + g_warning ("Failed to compile gl shader: %s", error->message); + } + } + + if (program->id >= 0 && n_children <= G_N_ELEMENTS (program->glshader.texture_locations)) + { + GBytes *args; + TextureRegion regions[4]; + gboolean is_offscreen[4]; + for (guint i = 0; i < n_children; i++) + { + GskRenderNode *child = gsk_gl_shader_node_get_child (node, i); + if (!add_offscreen_ops (self, builder, + &node->bounds, + child, + ®ions[i], &is_offscreen[i], + FORCE_OFFSCREEN | RESET_CLIP | RESET_OPACITY)) + return; + } + + args = gsk_gl_shader_node_get_args (node); + ops_set_program (builder, program); + + ops_set_gl_shader_args (builder, shader, node->bounds.size.width, node->bounds.size.height, g_bytes_get_data (args, NULL)); + for (guint i = 0; i < n_children; i++) + { + if (i == 0) + ops_set_texture (builder, regions[i].texture_id); + else + ops_set_extra_texture (builder, regions[i].texture_id, i); + } + + load_offscreen_vertex_data (ops_draw (builder, NULL), node, builder); + } + else + { + static GdkRGBA pink = { 255 / 255., 105 / 255., 180 / 255., 1.0 }; + ops_set_program (builder, &self->programs->color_program); + ops_set_color (builder, &pink); + load_vertex_data (ops_draw (builder, NULL), node, builder); + } +} + /* Returns TRUE is applying transform to bounds * yields an axis-aligned rectangle */ @@ -2694,6 +2912,17 @@ apply_source_texture_op (const Program *program, glBindTexture (GL_TEXTURE_2D, op->texture_id); } +static inline void +apply_source_extra_texture_op (const Program *program, + const OpExtraTexture *op) +{ + g_assert(op->texture_id != 0); + OP_PRINT (" -> New extra texture %d: %d", op->idx, op->texture_id); + glUniform1i (program->glshader.texture_locations[op->idx], op->idx); + glActiveTexture (GL_TEXTURE0 + op->idx); + glBindTexture (GL_TEXTURE_2D, op->texture_id); +} + static inline void apply_color_matrix_op (const Program *program, const OpColorMatrix *op) @@ -2838,6 +3067,51 @@ apply_border_op (const Program *program, glUniform4fv (program->border.outline_rect_location, 3, (float *)&op->outline.bounds); } +static inline void +apply_gl_shader_args_op (const Program *program, + const OpGLShader *op) +{ + int n_uniforms, i; + const GskGLUniform *uniforms; + + OP_PRINT (" -> GL Shader Args"); + + glUniform2fv (program->glshader.size_location, 1, op->size); + + uniforms = gsk_gl_shader_get_uniforms (op->shader, &n_uniforms); + for (i = 0; i < n_uniforms; i++) + { + const GskGLUniform *u = &uniforms[i]; + const guchar *data = op->uniform_data + u->offset; + + switch (u->type) + { + default: + case GSK_GL_UNIFORM_TYPE_NONE: + break; + case GSK_GL_UNIFORM_TYPE_FLOAT: + glUniform1fv (program->glshader.args_locations[i], 1, (const float *)data); + break; + case GSK_GL_UNIFORM_TYPE_INT: + glUniform1iv (program->glshader.args_locations[i], 1, (const gint32 *)data); + break; + case GSK_GL_UNIFORM_TYPE_UINT: + case GSK_GL_UNIFORM_TYPE_BOOL: + glUniform1uiv (program->glshader.args_locations[i], 1, (const guint32 *) data); + break; + case GSK_GL_UNIFORM_TYPE_VEC2: + glUniform2fv (program->glshader.args_locations[i], 1, (const float *)data); + break; + case GSK_GL_UNIFORM_TYPE_VEC3: + glUniform3fv (program->glshader.args_locations[i], 1, (const float *)data); + break; + case GSK_GL_UNIFORM_TYPE_VEC4: + glUniform4fv (program->glshader.args_locations[i], 1, (const float *)data); + break; + } + } +} + static inline void apply_border_width_op (const Program *program, const OpBorder *op) @@ -2909,6 +3183,32 @@ gsk_gl_renderer_dispose (GObject *gobject) G_OBJECT_CLASS (gsk_gl_renderer_parent_class)->dispose (gobject); } +static void +program_init (Program *program) +{ + program->index = -1; + program->state.opacity = 1.0f; +} + +static void +program_finalize (Program *program) +{ + if (program->id > 0) + glDeleteProgram (program->id); + if (program->index == -1 && + program->glshader.compile_error != NULL) + g_error_free (program->glshader.compile_error); + + gsk_transform_unref (program->state.modelview); +} + +static void +program_free (Program *program) +{ + program_finalize (program); + g_free (program); +} + static GskGLRendererPrograms * gsk_gl_renderer_programs_new (void) { @@ -2918,9 +3218,11 @@ gsk_gl_renderer_programs_new (void) programs = g_new0 (GskGLRendererPrograms, 1); programs->ref_count = 1; for (i = 0; i < GL_N_PROGRAMS; i ++) - { - programs->programs[i].state.opacity = 1.0f; - } + program_init (&programs->programs[i]); + + /* We use direct hash for performance, not string hash on the source, because we assume each caller + * reuses a single GskGLShader for all uses and different callers will use different source content. */ + programs->custom_programs = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify)g_object_unref, (GDestroyNotify)program_free); return programs; } @@ -2941,15 +3243,33 @@ gsk_gl_renderer_programs_unref (GskGLRendererPrograms *programs) if (programs->ref_count == 0) { for (i = 0; i < GL_N_PROGRAMS; i ++) - { - if (programs->programs[i].id > 0) - glDeleteProgram (programs->programs[i].id); - gsk_transform_unref (programs->programs[i].state.modelview); - } + program_finalize (&programs->programs[i]); + + g_hash_table_destroy (programs->custom_programs); g_free (programs); } } +static Program * +gsk_gl_renderer_lookup_custom_program (GskGLRenderer *self, + GskGLShader *shader) +{ + return g_hash_table_lookup (self->programs->custom_programs, shader); +} + +static Program * +gsk_gl_renderer_create_custom_program (GskGLRenderer *self, + GskGLShader *shader) +{ + Program *program = g_new0 (Program, 1); + + program_init (program); + + g_hash_table_insert (self->programs->custom_programs, g_object_ref (shader), program); + + return program; +} + static GskGLRendererPrograms * gsk_gl_renderer_create_programs (GskGLRenderer *self, GError **error) @@ -2984,35 +3304,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, g_assert (G_N_ELEMENTS (program_definitions) == GL_N_PROGRAMS); -#ifdef G_ENABLE_DEBUG - if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), SHADERS)) - shader_builder.debugging = TRUE; -#endif - - if (gdk_gl_context_get_use_es (self->gl_context)) - { - - gsk_gl_shader_builder_set_glsl_version (&shader_builder, SHADER_VERSION_GLES); - shader_builder.gles = TRUE; - } - else if (gdk_gl_context_is_legacy (self->gl_context)) - { - int maj, min; - - gdk_gl_context_get_version (self->gl_context, &maj, &min); - - if (maj == 3) - gsk_gl_shader_builder_set_glsl_version (&shader_builder, SHADER_VERSION_GL3_LEGACY); - else - gsk_gl_shader_builder_set_glsl_version (&shader_builder, SHADER_VERSION_GL2_LEGACY); - - shader_builder.legacy = TRUE; - } - else - { - gsk_gl_shader_builder_set_glsl_version (&shader_builder, SHADER_VERSION_GL3); - shader_builder.gl3 = TRUE; - } + init_shader_builder (self, &shader_builder); programs = gsk_gl_renderer_programs_new (); @@ -3023,7 +3315,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, prog->index = i; prog->id = gsk_gl_shader_builder_create_program (&shader_builder, program_definitions[i].resource_path, - error); + NULL, 0, error); if (prog->id < 0) { g_clear_pointer (&programs, gsk_gl_renderer_programs_unref); @@ -3468,6 +3760,10 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self, render_repeat_node (self, node, builder); break; + case GSK_GL_SHADER_NODE: + render_gl_shader_node (self, node, builder); + break; + case GSK_REPEATING_LINEAR_GRADIENT_NODE: case GSK_REPEATING_RADIAL_GRADIENT_NODE: case GSK_CAIRO_NODE: @@ -3750,6 +4046,10 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self) apply_source_texture_op (program, ptr); break; + case OP_CHANGE_EXTRA_SOURCE_TEXTURE: + apply_source_extra_texture_op (program, ptr); + break; + case OP_CHANGE_CROSS_FADE: g_assert (program == &self->programs->cross_fade_program); apply_cross_fade_op (program, ptr); @@ -3796,6 +4096,10 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self) apply_repeat_op (program, ptr); break; + case OP_CHANGE_GL_SHADER_ARGS: + apply_gl_shader_args_op (program, ptr); + break; + case OP_DRAW: { const OpDraw *op = ptr; diff --git a/gsk/gl/gskglrendererprivate.h b/gsk/gl/gskglrendererprivate.h new file mode 100644 index 0000000000..917aecb2e8 --- /dev/null +++ b/gsk/gl/gskglrendererprivate.h @@ -0,0 +1,14 @@ +#ifndef __GSK_GL_RENDERER_PRIVATE_H__ +#define __GSK_GL_RENDERER_PRIVATE_H__ + +#include "gskglrenderer.h" + +G_BEGIN_DECLS + +gboolean gsk_gl_render_try_compile_gl_shader (GskGLRenderer *self, + GskGLShader *shader, + GError **error); + +G_END_DECLS + +#endif /* __GSK_GL_RENDERER_PRIVATE_H__ */ diff --git a/gsk/gl/gskglrenderops.c b/gsk/gl/gskglrenderops.c index 9f5b6718d1..1fc9d02810 100644 --- a/gsk/gl/gskglrenderops.c +++ b/gsk/gl/gskglrenderops.c @@ -558,6 +558,18 @@ ops_set_texture (RenderOpBuilder *builder, builder->current_texture = texture_id; } +void +ops_set_extra_texture (RenderOpBuilder *builder, + int texture_id, + int idx) +{ + OpExtraTexture *op; + + op = ops_begin (builder, OP_CHANGE_EXTRA_SOURCE_TEXTURE); + op->texture_id = texture_id; + op->idx = idx; +} + int ops_set_render_target (RenderOpBuilder *builder, int render_target_id) @@ -621,6 +633,22 @@ ops_set_color (RenderOpBuilder *builder, op->rgba = color; } +void +ops_set_gl_shader_args (RenderOpBuilder *builder, + GskGLShader *shader, + float width, + float height, + const guchar *uniform_data) +{ + OpGLShader *op; + + op = ops_begin (builder, OP_CHANGE_GL_SHADER_ARGS); + op->shader = shader; + op->size[0] = width; + op->size[1] = height; + op->uniform_data = uniform_data; +} + void ops_set_color_matrix (RenderOpBuilder *builder, const graphene_matrix_t *matrix, diff --git a/gsk/gl/gskglrenderopsprivate.h b/gsk/gl/gskglrenderopsprivate.h index 728fbfbffe..38b5756a9c 100644 --- a/gsk/gl/gskglrenderopsprivate.h +++ b/gsk/gl/gskglrenderopsprivate.h @@ -84,7 +84,7 @@ typedef struct struct _Program { - int index; /* Into the renderer's program array */ + int index; /* Into the renderer's program array -1 for custom */ int id; /* Common locations (gl_common)*/ @@ -159,6 +159,12 @@ struct _Program int child_bounds_location; int texture_rect_location; } repeat; + struct { + int size_location; + int args_locations[8]; + int texture_locations[4]; + GError *compile_error; + } glshader; }; ProgramState state; }; @@ -184,7 +190,7 @@ typedef struct { Program unblurred_outset_shadow_program; }; }; - ProgramState state[GL_N_PROGRAMS]; + GHashTable *custom_programs; /* GskGLShader -> Program* */ } GskGLRendererPrograms; typedef struct @@ -256,6 +262,9 @@ graphene_rect_t ops_set_viewport (RenderOpBuilder *builder, void ops_set_texture (RenderOpBuilder *builder, int texture_id); +void ops_set_extra_texture (RenderOpBuilder *builder, + int texture_id, + int idx); int ops_set_render_target (RenderOpBuilder *builder, int render_target_id); @@ -282,6 +291,11 @@ void ops_set_inset_shadow (RenderOpBuilder *self, const GdkRGBA *color, float dx, float dy); +void ops_set_gl_shader_args (RenderOpBuilder *builder, + GskGLShader *shader, + float width, + float height, + const guchar *uniform_data); void ops_set_unblurred_outset_shadow (RenderOpBuilder *self, const GskRoundedRect outline, float spread, diff --git a/gsk/gl/gskglshaderbuilder.c b/gsk/gl/gskglshaderbuilder.c index 86e2cdd407..f860a68d95 100644 --- a/gsk/gl/gskglshaderbuilder.c +++ b/gsk/gl/gskglshaderbuilder.c @@ -129,6 +129,8 @@ print_shader_info (const char *prefix, int gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self, const char *resource_path, + const char *extra_fragment_snippet, + gsize extra_fragment_length, GError **error) { @@ -191,7 +193,7 @@ gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self, print_shader_info ("Vertex shader", vertex_id, resource_path); fragment_id = glCreateShader (GL_FRAGMENT_SHADER); - glShaderSource (fragment_id, 8, + glShaderSource (fragment_id, 9, (const char *[]) { version_buffer, self->debugging ? "#define GSK_DEBUG 1\n" : "", @@ -200,7 +202,8 @@ gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self, self->gles ? "#define GSK_GLES 1\n" : "", g_bytes_get_data (self->preamble, NULL), g_bytes_get_data (self->fs_preamble, NULL), - fragment_shader_start + fragment_shader_start, + extra_fragment_snippet ? extra_fragment_snippet : "" }, (int[]) { -1, @@ -211,6 +214,7 @@ gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self, -1, -1, -1, + extra_fragment_length, }); glCompileShader (fragment_id); diff --git a/gsk/gl/gskglshaderbuilderprivate.h b/gsk/gl/gskglshaderbuilderprivate.h index ec9cd66845..870df050d0 100644 --- a/gsk/gl/gskglshaderbuilderprivate.h +++ b/gsk/gl/gskglshaderbuilderprivate.h @@ -33,6 +33,8 @@ void gsk_gl_shader_builder_set_glsl_version (GskGLShaderBuilder *self, int gsk_gl_shader_builder_create_program (GskGLShaderBuilder *self, const char *resource_path, + const char *extra_fragment_snippet, + gsize extra_fragment_length, GError **error); G_END_DECLS diff --git a/gsk/gl/opbuffer.c b/gsk/gl/opbuffer.c index f212e96eec..8a7f8c9b1f 100644 --- a/gsk/gl/opbuffer.c +++ b/gsk/gl/opbuffer.c @@ -31,6 +31,8 @@ static guint op_sizes[OP_LAST] = { sizeof (OpDebugGroup), 0, sizeof (OpBlend), + sizeof (OpGLShader), + sizeof (OpExtraTexture), }; void diff --git a/gsk/gl/opbuffer.h b/gsk/gl/opbuffer.h index dcdedc6c50..a2236dd7a1 100644 --- a/gsk/gl/opbuffer.h +++ b/gsk/gl/opbuffer.h @@ -39,6 +39,8 @@ typedef enum OP_PUSH_DEBUG_GROUP = 25, OP_POP_DEBUG_GROUP = 26, OP_CHANGE_BLEND = 27, + OP_CHANGE_GL_SHADER_ARGS = 28, + OP_CHANGE_EXTRA_SOURCE_TEXTURE = 29, OP_LAST } OpKind; @@ -124,6 +126,12 @@ typedef struct int texture_id; } OpTexture; +typedef struct +{ + int texture_id; + int idx; +} OpExtraTexture; + typedef struct { gsize vao_offset; @@ -198,6 +206,13 @@ typedef struct float texture_rect[4]; } OpRepeat; +typedef struct +{ + float size[2]; + GskGLShader *shader; + const guchar *uniform_data; +} OpGLShader; + void op_buffer_init (OpBuffer *buffer); void op_buffer_destroy (OpBuffer *buffer); void op_buffer_clear (OpBuffer *buffer); diff --git a/gsk/meson.build b/gsk/meson.build index b52d379eb9..e71ac50c78 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -16,6 +16,7 @@ gsk_private_gl_shaders = [ 'resources/glsl/cross_fade.glsl', 'resources/glsl/blend.glsl', 'resources/glsl/repeat.glsl', + 'resources/glsl/custom.glsl', ] gsk_public_sources = files([ diff --git a/gsk/resources/glsl/custom.glsl b/gsk/resources/glsl/custom.glsl new file mode 100644 index 0000000000..d2aed97fc8 --- /dev/null +++ b/gsk/resources/glsl/custom.glsl @@ -0,0 +1,21 @@ +// VERTEX_SHADER: +void main() { + gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); + vUv = vec2(aUv.x, aUv.y); +} + +// FRAGMENT_SHADER: +// The shader supplies: +void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv); + +uniform vec2 u_size; +uniform sampler2D u_source2; +uniform sampler2D u_source3; +uniform sampler2D u_source4; + +void main() { + vec4 fragColor; + vec2 fragCoord = vec2(vUv.x * u_size.x, (1.0-vUv.y) * u_size.y); + mainImage(fragColor, fragCoord, u_size, vUv); + gskSetOutputColor(fragColor); +} diff --git a/gsk/vulkan/gskvulkanrenderpass.c b/gsk/vulkan/gskvulkanrenderpass.c index 2ea547881c..be8dd2795a 100644 --- a/gsk/vulkan/gskvulkanrenderpass.c +++ b/gsk/vulkan/gskvulkanrenderpass.c @@ -256,6 +256,7 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self, case GSK_NOT_A_RENDER_NODE: g_assert_not_reached (); return; + case GSK_GL_SHADER_NODE: case GSK_SHADOW_NODE: case GSK_RADIAL_GRADIENT_NODE: case GSK_REPEATING_RADIAL_GRADIENT_NODE: