[gtk: 1/9] gskglrenderer: Optimize linear-gradient shader




commit bbf68c0d9dd2899c6ccffc0ca88df8e8c8d1c26c
Author: Fabio Lagalla <lagfabio amazon com>
Date:   Tue Jan 26 12:11:26 2021 +0100

    gskglrenderer: Optimize linear-gradient shader

 gsk/gl/gskglrenderer.c                  |   8 +--
 gsk/gl/gskglrenderopsprivate.h          |   3 +-
 gsk/resources/glsl/linear_gradient.glsl | 116 +++++++++++++++++++-------------
 gsk/resources/glsl/preamble.glsl        |  10 +++
 4 files changed, 83 insertions(+), 54 deletions(-)
---
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index c1ef336eed..b083eac5a7 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -3041,8 +3041,9 @@ apply_linear_gradient_op (const Program          *program,
                   op->n_color_stops.value * 5,
                   (float *)op->color_stops.value);
 
-  glUniform2f (program->linear_gradient.start_point_location, op->start_point[0], op->start_point[1]);
-  glUniform2f (program->linear_gradient.end_point_location, op->end_point[0], op->end_point[1]);
+  glUniform4f (program->linear_gradient.points_location,
+               op->start_point[0], op->start_point[1],
+               op->end_point[0] - op->start_point[0], op->end_point[1] - op->start_point[1]);
 }
 
 static inline void
@@ -3368,8 +3369,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer  *self,
   /* linear gradient */
   INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, color_stops);
   INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, num_color_stops);
-  INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, start_point);
-  INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, end_point);
+  INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, points);
 
   /* radial gradient */
   INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, color_stops);
diff --git a/gsk/gl/gskglrenderopsprivate.h b/gsk/gl/gskglrenderopsprivate.h
index 24544b0d44..85f62f0451 100644
--- a/gsk/gl/gskglrenderopsprivate.h
+++ b/gsk/gl/gskglrenderopsprivate.h
@@ -118,8 +118,7 @@ struct _Program
     struct {
       int num_color_stops_location;
       int color_stops_location;
-      int start_point_location;
-      int end_point_location;
+      int points_location;
     } linear_gradient;
     struct {
       int num_color_stops_location;
diff --git a/gsk/resources/glsl/linear_gradient.glsl b/gsk/resources/glsl/linear_gradient.glsl
index 588d9ec744..aa90b846e0 100644
--- a/gsk/resources/glsl/linear_gradient.glsl
+++ b/gsk/resources/glsl/linear_gradient.glsl
@@ -1,35 +1,41 @@
 // VERTEX_SHADER
-uniform vec2 u_start_point;
-uniform vec2 u_end_point;
-uniform float u_color_stops[6 * 5];
-uniform int u_num_color_stops;
+uniform vec4 u_points;
 
-_OUT_ vec2 startPoint;
-_OUT_ vec2 endPoint;
-_OUT_ float maxDist;
-_OUT_ vec2 gradient;
-_OUT_ float gradientLength;
-_OUT_ vec4 color_stops[6];
-_OUT_ float color_offsets[6];
+_NOPERSPECTIVE_ _OUT_ vec4 info;
 
 void main() {
-  gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
-
-  startPoint = (u_modelview * vec4(u_start_point, 0, 1)).xy;
-  endPoint   = (u_modelview * vec4(u_end_point,   0, 1)).xy;
-  maxDist    = length(endPoint - startPoint);
-
-  // Gradient direction
-  gradient = endPoint - startPoint;
-  gradientLength = length(gradient);
-
-  for (int i = 0; i < u_num_color_stops; i ++) {
-    color_offsets[i] = u_color_stops[(i * 5) + 0];
-    color_stops[i] = gsk_premultiply(vec4(u_color_stops[(i * 5) + 1],
-                                          u_color_stops[(i * 5) + 2],
-                                          u_color_stops[(i * 5) + 3],
-                                          u_color_stops[(i * 5) + 4]));
-  }
+  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:
@@ -39,32 +45,46 @@ uniform int u_num_color_stops;
 uniform highp int u_num_color_stops; // Why? Because it works like this.
 #endif
 
-_IN_ vec2 startPoint;
-_IN_ vec2 endPoint;
-_IN_ float maxDist;
-_IN_ vec2 gradient;
-_IN_ float gradientLength;
-_IN_ vec4 color_stops[6];
-_IN_ float color_offsets[6];
+uniform float u_color_stops[6 * 5];
+
+_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() {
-  // Position relative to startPoint
-  vec2 pos = gsk_get_frag_coord() - startPoint;
+  float offset = dot(info.xy, info.zw);
+
+  if (offset < get_offset(0)) {
+    gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
+    return;
+  }
 
-  // Current pixel, projected onto the line between the start point and the end point
-  // The projection will be relative to the start point!
-  vec2 proj = (dot(gradient, pos) / (gradientLength * gradientLength)) * gradient;
+  int n = u_num_color_stops - 1;
+  for (int i = 0; i < n; i++) {
+    float curr_offset = get_offset(i);
+    float next_offset = get_offset(i + 1);
 
-  // Offset of the current pixel
-  float offset = length(proj) / maxDist;
+    if (offset >= curr_offset && offset < next_offset) {
+      float f = (offset - curr_offset) / (next_offset - curr_offset);
+      vec4 curr_color = gsk_premultiply(get_color(i));
+      vec4 next_color = gsk_premultiply(get_color(i + 1));
+      vec4 color = mix(curr_color, next_color, f);
 
-  vec4 color = color_stops[0];
-  for (int i = 1; i < u_num_color_stops; i ++) {
-    if (offset >= color_offsets[i - 1])  {
-      float o = (offset - color_offsets[i - 1]) / (color_offsets[i] - color_offsets[i - 1]);
-      color = mix(color_stops[i - 1], color_stops[i], clamp(o, 0.0, 1.0));
+      gskSetOutputColor(color * u_alpha);
+      return;
     }
   }
 
-  gskSetOutputColor(color * u_alpha);
+  gskSetOutputColor(gsk_scaled_premultiply(get_color(n), u_alpha));
 }
diff --git a/gsk/resources/glsl/preamble.glsl b/gsk/resources/glsl/preamble.glsl
index 47cc21b004..8ca6469f6d 100644
--- a/gsk/resources/glsl/preamble.glsl
+++ b/gsk/resources/glsl/preamble.glsl
@@ -5,10 +5,12 @@ precision highp float;
 #if defined(GSK_GLES) || defined(GSK_LEGACY)
 #define _OUT_ varying
 #define _IN_ varying
+#define _NOPERSPECTIVE_
 #define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
 #else
 #define _OUT_ out
 #define _IN_ in
+#define _NOPERSPECTIVE_ noperspective
 #define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
 #endif
 
@@ -39,3 +41,11 @@ gsk_create_rect(vec4[3] data)
 vec4 gsk_premultiply(vec4 c) {
   return vec4(c.rgb * c.a, c.a);
 }
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+  // Fast version of gsk_premultiply(c) * s
+  // 4 muls instead of 7
+  float a = s * c.a;
+
+  return vec4(c.rgb * a, a);
+}


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