[gtk/matthiasc/glshader-node: 3/9] Support GLShaderNode in backends




commit 21e0ad05615574c5bee6065f3928898bb7de3cf7
Author: Alexander Larsson <alexl redhat com>
Date:   Fri Sep 18 18:03:30 2020 +0200

    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 |  10 ++
 gsk/gl/gskglrenderer.c             | 337 ++++++++++++++++++++++++++++++++-----
 gsk/gl/gskglrenderops.c            |  28 +++
 gsk/gl/gskglrenderopsprivate.h     |  15 +-
 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   |   4 +
 11 files changed, 402 insertions(+), 41 deletions(-)
---
diff --git a/gsk/broadway/gskbroadwayrenderer.c b/gsk/broadway/gskbroadwayrenderer.c
index e18b03f826..1dd8e8164d 100644
--- a/gsk/broadway/gskbroadwayrenderer.c
+++ b/gsk/broadway/gskbroadwayrenderer.c
@@ -272,6 +272,11 @@ collect_reused_child_nodes (GskRenderer *renderer,
 
       /* Bin nodes */
 
+    case GSK_GL_SHADER_NODE:
+      collect_reused_node (renderer,
+                           gsk_gl_shader_node_get_fallback_child (node));
+      break;
+
     case GSK_SHADOW_NODE:
       collect_reused_node (renderer,
                            gsk_shadow_node_get_child (node));
@@ -792,6 +797,11 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
         }
       return;
 
+    case GSK_GL_SHADER_NODE:
+      gsk_broadway_renderer_add_node (renderer,
+                                      gsk_gl_shader_node_get_fallback_child (node), offset_x, offset_y, 
clip_bounds);
+      return;
+
       /* Generic nodes */
 
     case GSK_CONTAINER_NODE:
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index bc1831e347..b31d7c3cea 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -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,13 @@ 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, " ");
+        print_render_node_tree (gsk_gl_shader_node_get_fallback_child (root), level + 1);
+        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 +508,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)
@@ -1006,6 +1053,138 @@ render_texture_node (GskGLRenderer       *self,
     }
 }
 
+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);
+  GskRenderNode *fallback = gsk_gl_shader_node_get_fallback_child (node);
+  int n_children = gsk_gl_shader_node_get_n_children (node);
+
+  if (program == NULL)
+    {
+      GskGLShaderBuilder shader_builder;
+      const char *shader_source;
+      gsize shader_source_len;
+      GError *error = NULL;
+      int n_uniforms;
+      const GskGLUniform *uniforms;
+      GBytes *bytes;
+      int n_required_textures = gsk_gl_shader_get_n_required_textures (shader);
+
+      /* We always create the program, so that any compiler warnings or other is only reported once */
+      program = gsk_gl_renderer_create_custom_program (self, shader);
+
+      bytes = gsk_gl_shader_get_bytes (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));
+        }
+      else 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)));
+        }
+      else
+        {
+          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)
+            {
+              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);
+                      if (program->glshader.args_locations[i] == -1)
+                        g_warning ("Expected uniform `%s` not found in shader", uniforms[i].name);
+                    }
+                  else
+                    program->glshader.args_locations[i] = -1;
+                }
+            }
+        }
+
+      if (program->id <= 0)
+        {
+          g_warning ("Failed to compile gl shader: %s\n", error->message);
+          g_error_free (error);
+        }
+    }
+
+  if (program->id >= 0 && n_children <= G_N_ELEMENTS (program->glshader.texture_locations))
+    {
+      GBytes *uniform_data;
+      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,
+                                  &regions[i], &is_offscreen[i],
+                                  FORCE_OFFSCREEN | RESET_CLIP | RESET_OPACITY))
+            {
+              if (fallback)
+                gsk_gl_renderer_add_render_ops (self, fallback, builder);
+              return;
+            }
+        }
+
+      uniform_data = gsk_gl_shader_node_get_uniform_data (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 (uniform_data, 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
+    {
+      if (fallback)
+        gsk_gl_renderer_add_render_ops (self, fallback, builder);
+    }
+}
+
 /* Returns TRUE is applying transform to bounds
  * yields an axis-aligned rectangle
  */
@@ -2671,6 +2850,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)
@@ -2815,6 +3005,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_GLUNIFORM_TYPE_NONE:
+          break;
+        case GSK_GLUNIFORM_TYPE_FLOAT:
+          glUniform1fv (program->glshader.args_locations[i], 1, (const float *)data);
+          break;
+        case GSK_GLUNIFORM_TYPE_INT:
+          glUniform1iv (program->glshader.args_locations[i], 1, (const gint32 *)data);
+          break;
+        case GSK_GLUNIFORM_TYPE_UINT:
+        case GSK_GLUNIFORM_TYPE_BOOL:
+          glUniform1uiv (program->glshader.args_locations[i], 1, (const guint32 *) data);
+          break;
+        case GSK_GLUNIFORM_TYPE_VEC2:
+          glUniform2fv (program->glshader.args_locations[i], 1, (const float *)data);
+          break;
+        case GSK_GLUNIFORM_TYPE_VEC3:
+          glUniform3fv (program->glshader.args_locations[i], 1, (const float *)data);
+          break;
+        case GSK_GLUNIFORM_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)
@@ -2886,6 +3121,28 @@ 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);
+  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)
 {
@@ -2895,9 +3152,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;
 }
@@ -2918,15 +3177,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)
@@ -2961,35 +3238,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 ();
 
@@ -3000,7 +3249,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);
@@ -3445,6 +3694,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:
@@ -3727,6 +3980,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);
@@ -3773,6 +4030,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/gskglrenderops.c b/gsk/gl/gskglrenderops.c
index d8ec3b9ec6..d8f1f527b9 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 f2dff2862d..2f87117110 100644
--- a/gsk/gl/gskglrenderopsprivate.h
+++ b/gsk/gl/gskglrenderopsprivate.h
@@ -159,6 +159,11 @@ struct _Program
       int child_bounds_location;
       int texture_rect_location;
     } repeat;
+    struct {
+      int size_location;
+      int args_locations[8];
+      int texture_locations[4];
+    } glshader;
   };
 
   ProgramState state;
@@ -185,7 +190,7 @@ typedef struct {
       Program unblurred_outset_shadow_program;
     };
   };
-  ProgramState state[GL_N_PROGRAMS];
+  GHashTable *custom_programs; /* GskGLShader -> Program* */
 } GskGLRendererPrograms;
 
 typedef struct
@@ -257,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);
@@ -283,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 443048865e..8c9542da29 100644
--- a/gsk/gl/gskglshaderbuilder.c
+++ b/gsk/gl/gskglshaderbuilder.c
@@ -96,6 +96,8 @@ check_shader_error (int     shader_id,
 int
 gsk_gl_shader_builder_create_program (GskGLShaderBuilder  *self,
                                       const char          *resource_path,
+                                      const char          *extra_fragment_snippet,
+                                      gsize                extra_fragment_length,
                                       GError             **error)
 {
 
@@ -156,7 +158,7 @@ gsk_gl_shader_builder_create_program (GskGLShaderBuilder  *self,
     }
 
   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" : "",
@@ -165,7 +167,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,
@@ -176,6 +179,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..e694c9a7ab
--- /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(gsk_premultiply(fragColor));
+}
diff --git a/gsk/vulkan/gskvulkanrenderpass.c b/gsk/vulkan/gskvulkanrenderpass.c
index 2ea547881c..ad729958ca 100644
--- a/gsk/vulkan/gskvulkanrenderpass.c
+++ b/gsk/vulkan/gskvulkanrenderpass.c
@@ -539,6 +539,10 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass           *self,
       }
       return;
 
+    case GSK_GL_SHADER_NODE:
+     gsk_vulkan_render_pass_add_node (self, render, constants, gsk_gl_shader_node_get_fallback_child (node));
+      return;
+
     case GSK_DEBUG_NODE:
       gsk_vulkan_render_pass_add_node (self, render, constants, gsk_debug_node_get_child (node));
       return;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]