[gtk/ngl-tweaks: 3/3] ngl: Add a shader for short linear gradients




commit b9d21f1f1a68ad87e5801afed8c5e7405c5975c1
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Mar 9 22:29:22 2021 -0500

    ngl: Add a shader for short linear gradients
    
    Almost all our linear gradients have 2 stops.
    Add a simple shader for that case.

 gsk/meson.build                           |  1 +
 gsk/ngl/gsknglprograms.defs               |  6 +++
 gsk/ngl/gsknglrenderjob.c                 | 35 ++++++++++++-
 gsk/resources/glsl/linear_gradient_2.glsl | 84 +++++++++++++++++++++++++++++++
 4 files changed, 125 insertions(+), 1 deletion(-)
---
diff --git a/gsk/meson.build b/gsk/meson.build
index dd1ac34ff9..1d579ab5ae 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -7,6 +7,7 @@ gsk_private_gl_shaders = [
   'resources/glsl/coloring.glsl',
   'resources/glsl/color.glsl',
   'resources/glsl/linear_gradient.glsl',
+  'resources/glsl/linear_gradient_2.glsl',
   'resources/glsl/radial_gradient.glsl',
   'resources/glsl/conic_gradient.glsl',
   'resources/glsl/color_matrix.glsl',
diff --git a/gsk/ngl/gsknglprograms.defs b/gsk/ngl/gsknglprograms.defs
index b0b797f0e9..cfd5580037 100644
--- a/gsk/ngl/gsknglprograms.defs
+++ b/gsk/ngl/gsknglprograms.defs
@@ -57,6 +57,12 @@ GSK_NGL_DEFINE_PROGRAM (linear_gradient,
                         GSK_NGL_ADD_UNIFORM (3, LINEAR_GRADIENT_POINTS, u_points)
                         GSK_NGL_ADD_UNIFORM (4, LINEAR_GRADIENT_REPEAT, u_repeat))
 
+GSK_NGL_DEFINE_PROGRAM (linear_gradient_2,
+                        "/org/gtk/libgsk/glsl/linear_gradient_2.glsl",
+                        GSK_NGL_ADD_UNIFORM (1, LINEAR_GRADIENT_2_COLOR_STOPS, u_color_stops)
+                        GSK_NGL_ADD_UNIFORM (2, LINEAR_GRADIENT_2_POINTS, u_points)
+                        GSK_NGL_ADD_UNIFORM (3, LINEAR_GRADIENT_2_REPEAT, u_repeat))
+
 GSK_NGL_DEFINE_PROGRAM (outset_shadow,
                         "/org/gtk/libgsk/glsl/outset_shadow.glsl",
                         GSK_NGL_ADD_UNIFORM (1, OUTSET_SHADOW_COLOR, u_color)
diff --git a/gsk/ngl/gsknglrenderjob.c b/gsk/ngl/gsknglrenderjob.c
index 794086c4e3..b34692b3cb 100644
--- a/gsk/ngl/gsknglrenderjob.c
+++ b/gsk/ngl/gsknglrenderjob.c
@@ -1493,6 +1493,37 @@ gsk_ngl_render_job_visit_linear_gradient_node (GskNglRenderJob     *job,
   gsk_ngl_render_job_end_draw (job);
 }
 
+static inline void
+gsk_ngl_render_job_visit_linear_gradient_2_node (GskNglRenderJob     *job,
+                                                 const GskRenderNode *node)
+{
+  const GskColorStop *stops = gsk_linear_gradient_node_get_color_stops (node, NULL);
+  const graphene_point_t *start = gsk_linear_gradient_node_get_start (node);
+  const graphene_point_t *end = gsk_linear_gradient_node_get_end (node);
+  int n_color_stops = gsk_linear_gradient_node_get_n_color_stops (node);
+  gboolean repeat = gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE;
+  float x1 = job->offset_x + start->x;
+  float x2 = job->offset_x + end->x;
+  float y1 = job->offset_y + start->y;
+  float y2 = job->offset_y + end->y;
+
+  g_assert (n_color_stops == 2);
+
+  gsk_ngl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, linear_gradient_2));
+  gsk_ngl_program_set_uniform1fv (job->current_program,
+                                  UNIFORM_LINEAR_GRADIENT_2_COLOR_STOPS, 0,
+                                  n_color_stops * 5,
+                                  (const float *)stops);
+  gsk_ngl_program_set_uniform4f (job->current_program,
+                                 UNIFORM_LINEAR_GRADIENT_2_POINTS, 0,
+                                 x1, y1, x2 - x1, y2 - y1);
+  gsk_ngl_program_set_uniform1i (job->current_program,
+                                 UNIFORM_LINEAR_GRADIENT_2_REPEAT, 0,
+                                 repeat);
+  gsk_ngl_render_job_draw_rect (job, &node->bounds);
+  gsk_ngl_render_job_end_draw (job);
+}
+
 static inline void
 gsk_ngl_render_job_visit_conic_gradient_node (GskNglRenderJob     *job,
                                               const GskRenderNode *node)
@@ -3695,7 +3726,9 @@ gsk_ngl_render_job_visit_node (GskNglRenderJob     *job,
 
     case GSK_LINEAR_GRADIENT_NODE:
     case GSK_REPEATING_LINEAR_GRADIENT_NODE:
-      if (gsk_linear_gradient_node_get_n_color_stops (node) < MAX_GRADIENT_STOPS)
+      if (gsk_linear_gradient_node_get_n_color_stops (node) == 2)
+        gsk_ngl_render_job_visit_linear_gradient_2_node (job, node);
+      else if (gsk_linear_gradient_node_get_n_color_stops (node) < MAX_GRADIENT_STOPS)
         gsk_ngl_render_job_visit_linear_gradient_node (job, node);
       else
         gsk_ngl_render_job_visit_as_fallback (job, node);
diff --git a/gsk/resources/glsl/linear_gradient_2.glsl b/gsk/resources/glsl/linear_gradient_2.glsl
new file mode 100644
index 0000000000..b4cc0fb7b2
--- /dev/null
+++ b/gsk/resources/glsl/linear_gradient_2.glsl
@@ -0,0 +1,84 @@
+// VERTEX_SHADER
+uniform vec4 u_points;
+
+_NOPERSPECTIVE_ _OUT_ vec4 info;
+
+void main() {
+  gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
+
+  vec2 mv0 = u_modelview[0].xy;
+  vec2 mv1 = u_modelview[1].xy;
+  vec2 offset = aPosition - u_points.xy;
+  vec2 coord = vec2(dot(mv0, offset),
+                    dot(mv1, offset));
+
+  // Original equation:
+  // VS | maxDist = length(end - start);
+  // VS | gradient = end - start;
+  // VS | gradientLength = length(gradient);
+  // FS | pos = frag_coord - start
+  // FS | proj = (dot(gradient, pos) / (gradientLength * gradientLength)) * gradient
+  // FS | offset = length(proj) / maxDist
+
+  // Simplified formula derivation:
+  // 1. Notice that maxDist = gradientLength:
+  // offset = length(proj) / gradientLength
+  // 2. Let gnorm = gradient / gradientLength, then:
+  // proj = (dot(gnorm * gradientLength, pos) / (gradientLength * gradientLength)) * (gnorm * 
gradientLength) =
+  //      = dot(gnorm, pos) * gnorm
+  // 3. Since gnorm is unit length then:
+  // length(proj) = length(dot(gnorm, pos) * gnorm) = dot(gnorm, pos)
+  // 4. We can avoid the FS division by passing a scaled pos from the VS:
+  // offset = dot(gnorm, pos) / gradientLength = dot(gnorm, pos / gradientLength)
+  // 5. 1.0 / length(gradient) is inversesqrt(dot(gradient, gradient)) in GLSL
+  vec2 gradient = vec2(dot(mv0, u_points.zw),
+                       dot(mv1, u_points.zw));
+  float rcp_gradient_length = inversesqrt(dot(gradient, gradient));
+
+  info = rcp_gradient_length * vec4(coord, gradient);
+}
+
+// FRAGMENT_SHADER:
+uniform float u_color_stops[2 * 5];
+uniform bool u_repeat;
+
+_NOPERSPECTIVE_ _IN_ vec4 info;
+
+float get_offset(int index) {
+  return u_color_stops[5 * index];
+}
+
+vec4 get_color(int index) {
+  int base = 5 * index + 1;
+
+  return vec4(u_color_stops[base],
+              u_color_stops[base + 1],
+              u_color_stops[base + 2],
+              u_color_stops[base + 3]);
+}
+
+void main() {
+  float offset = dot(info.xy, info.zw);
+
+  if (u_repeat) {
+    offset = fract(offset);
+  }
+
+  float offset0 = get_offset(0);
+  float offset1 = get_offset(1);
+
+  if (offset < offset0) {
+    gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
+  }
+  else if (offset < offset1) {
+    float f = (offset - offset0) / (offset1 - offset0);
+    vec4 curr_color = gsk_premultiply(get_color(0));
+    vec4 next_color = gsk_premultiply(get_color(1));
+    vec4 color = mix(curr_color, next_color, f);
+    gskSetOutputColor(color * u_alpha);
+    return;
+  }
+  else {
+    gskSetOutputColor(gsk_scaled_premultiply(get_color(1), u_alpha));
+  }
+}


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