[gtk: 1/9] gskglrenderer: Optimize linear-gradient shader
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk: 1/9] gskglrenderer: Optimize linear-gradient shader
- Date: Thu, 28 Jan 2021 01:43:59 +0000 (UTC)
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]