From 24e69bb8777b8e7718586d97bd04ec3245da3d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= Date: Thu, 16 Nov 2017 11:41:16 +0100 Subject: [PATCH] gl renderer: Implement rounded clip nodes mostly a proof of concept --- gsk/gskglrenderer.c | 55 ++++++++++++++- gsk/meson.build | 1 + gsk/resources/glsl/rounded_clip.fs.glsl | 91 +++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 gsk/resources/glsl/rounded_clip.fs.glsl diff --git a/gsk/gskglrenderer.c b/gsk/gskglrenderer.c index 8c6275e9b0..913791439b 100644 --- a/gsk/gskglrenderer.c +++ b/gsk/gskglrenderer.c @@ -44,6 +44,7 @@ dump_framebuffer (const char *filename, int w, int h) static void gsk_gl_renderer_setup_render_mode (GskGLRenderer *self); + typedef struct { int id; /* Common locations (gl_common)*/ @@ -73,6 +74,11 @@ typedef struct { int start_point_location; int end_point_location; }; + struct { + int clip_bounds_location; + int corner_widths_location; + int corner_heights_location; + }; }; } Program; @@ -88,6 +94,7 @@ enum { MODE_TEXTURE, MODE_COLOR_MATRIX, MODE_LINEAR_GRADIENT, + MODE_ROUNDED_CLIP, N_MODES }; @@ -120,6 +127,11 @@ typedef struct { graphene_point_t start_point; graphene_point_t end_point; } linear_gradient_data; + struct { + graphene_rect_t clip_bounds; + float corner_widths[4]; + float corner_heights[4]; + } rounded_clip_data; }; const char *name; @@ -204,6 +216,7 @@ struct _GskGLRenderer Program color_program; Program color_matrix_program; Program linear_gradient_program; + Program rounded_clip_program; GArray *render_items; @@ -432,6 +445,22 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self, INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, start_point_location, "uStartPoint"); INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient_program, end_point_location, "uEndPoint"); + self->rounded_clip_program.id = gsk_shader_builder_create_program (builder, + "blit.vs.glsl", + "rounded_clip.fs.glsl", + &shader_error); + if (shader_error != NULL) + { + g_propagate_prefixed_error (error, + shader_error, + "Unable to create 'rounded_clip' program: "); + goto out; + } + init_common_locations (self, builder, &self->rounded_clip_program); + INIT_PROGRAM_UNIFORM_LOCATION (rounded_clip_program, clip_bounds_location, "uClipBounds"); + INIT_PROGRAM_UNIFORM_LOCATION (rounded_clip_program, corner_widths_location, "uCornerWidths"); + INIT_PROGRAM_UNIFORM_LOCATION (rounded_clip_program, corner_heights_location, "uCornerHeights"); + res = TRUE; out: @@ -686,6 +715,21 @@ render_item (GskGLRenderer *self, } break; + case MODE_ROUNDED_CLIP: + { + g_assert (item->program == &self->rounded_clip_program); + glUniform1i (item->program->source_location, 0); + gsk_gl_driver_bind_source_texture (self->gl_driver, item->render_target); + glUniform4f (item->program->clip_bounds_location, + item->rounded_clip_data.clip_bounds.origin.x, + item->rounded_clip_data.clip_bounds.origin.y, + item->rounded_clip_data.clip_bounds.size.width, + item->rounded_clip_data.clip_bounds.size.height); + glUniform4fv (item->program->corner_widths_location, 1, item->rounded_clip_data.corner_widths); + glUniform4fv (item->program->corner_heights_location, 1, item->rounded_clip_data.corner_heights); + } + break; + default: g_assert_not_reached (); } @@ -844,14 +888,23 @@ gsk_gl_renderer_add_render_item (GskGLRenderer *self, case GSK_ROUNDED_CLIP_NODE: { GskRenderNode *child = gsk_rounded_clip_node_get_child (node); + const GskRoundedRect *clip = gsk_rounded_clip_node_peek_clip (node); graphene_matrix_t p; graphene_matrix_t identity; + int i; graphene_matrix_init_identity (&identity); init_framebuffer_for_node (self, &item, node, projection, &p); gsk_gl_renderer_add_render_item (self, &p, &identity, item.children, child, item.render_target); - item.mode = MODE_BLIT; + item.mode = MODE_ROUNDED_CLIP; + item.program = &self->rounded_clip_program; + item.rounded_clip_data.clip_bounds = clip->bounds; + for (i = 0; i < 4; i ++) + { + item.rounded_clip_data.corner_widths[i] = MAX (clip->corner[i].width, 1); + item.rounded_clip_data.corner_heights[i] = MAX (clip->corner[i].height, 1); + } } break; diff --git a/gsk/meson.build b/gsk/meson.build index 95b093b53d..18208267b7 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -9,6 +9,7 @@ gsk_private_source_shaders = [ 'resources/glsl/color_matrix.vs.glsl', 'resources/glsl/linear_gradient.fs.glsl', 'resources/glsl/linear_gradient.vs.glsl', + 'resources/glsl/rounded_clip.fs.glsl', 'resources/glsl/es2_common.fs.glsl', 'resources/glsl/es2_common.vs.glsl', 'resources/glsl/gl3_common.fs.glsl', diff --git a/gsk/resources/glsl/rounded_clip.fs.glsl b/gsk/resources/glsl/rounded_clip.fs.glsl new file mode 100644 index 0000000000..5589c12e79 --- /dev/null +++ b/gsk/resources/glsl/rounded_clip.fs.glsl @@ -0,0 +1,91 @@ +uniform vec4 uClipBounds; +uniform vec4 uCornerWidths; +uniform vec4 uCornerHeights; + +struct RoundedRect +{ + vec4 bounds; + vec4 corner_widths; + vec4 corner_heights; +}; + +float +ellipsis_dist (vec2 p, vec2 radius) +{ + vec2 p0 = p / radius; + vec2 p1 = 2.0 * p0 / radius; + + return (dot(p0, p0) - 1.0) / length (p1); +} + +float +ellipsis_coverage (vec2 point, vec2 center, vec2 radius) +{ + float d = ellipsis_dist (point - center, radius); + return clamp (0.5 - d, 0.0, 1.0); +} + +float +rounded_rect_coverage (RoundedRect r, vec2 p) +{ + if (p.x < r.bounds.x || p.y < r.bounds.y || + p.x >= r.bounds.z || p.y >= r.bounds.w) + return 0.0; + + vec2 rad_tl = vec2(r.corner_widths.x, r.corner_heights.x); + vec2 rad_tr = vec2(r.corner_widths.y, r.corner_heights.y); + vec2 rad_br = vec2(r.corner_widths.z, r.corner_heights.z); + vec2 rad_bl = vec2(r.corner_widths.w, r.corner_heights.w); + + vec2 ref_tl = r.bounds.xy + vec2( r.corner_widths.x, r.corner_heights.x); + vec2 ref_tr = r.bounds.zy + vec2(-r.corner_widths.y, r.corner_heights.y); + vec2 ref_br = r.bounds.zw + vec2(-r.corner_widths.z, -r.corner_heights.z); + vec2 ref_bl = r.bounds.xw + vec2( r.corner_widths.w, -r.corner_heights.w); + + float d_tl = ellipsis_coverage(p, ref_tl, rad_tl); + float d_tr = ellipsis_coverage(p, ref_tr, rad_tr); + float d_br = ellipsis_coverage(p, ref_br, rad_br); + float d_bl = ellipsis_coverage(p, ref_bl, rad_bl); + + vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl); + + bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y, + p.x > ref_tr.x && p.y < ref_tr.y, + p.x > ref_br.x && p.y > ref_br.y, + p.x < ref_bl.x && p.y > ref_bl.y); + + return 1.0 - dot(vec4(is_out), corner_coverages); +} + +RoundedRect +rounded_rect_shrink (RoundedRect r, vec4 amount) +{ + vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz; + vec4 new_widths = max (r.corner_widths - amount.wyyw, 0.0); + vec4 new_heights = max (r.corner_heights - amount.xxzz, 0.0); + + return RoundedRect (new_bounds, new_widths, new_heights); +} + +vec4 fragCoord() { + vec4 f = gl_FragCoord; + f.x += uViewport.x; + f.y = (uViewport.y + uViewport.w) - f.y; + return f; +} + +vec4 clip(vec4 color) { + vec4 bounds = uClipBounds; + bounds.z = bounds.x + bounds.z; + bounds.w = bounds.y + bounds.w; + + RoundedRect r = RoundedRect(bounds, uCornerWidths, uCornerHeights); + + return color * rounded_rect_coverage(r, fragCoord().xy); +} + +void main() { + vec4 diffuse = Texture(uSource, vUv); + + setOutputColor(clip(diffuse) * uAlpha); +}