[gtk+/wip/baedert/gl: 110/111] gl renderer: try doing outset box shadows



commit 6d988f68a9f3e084ced34feb6993349913d60d25
Author: Timm Bäder <mail baedert org>
Date:   Thu Dec 21 18:09:32 2017 +0100

    gl renderer: try doing outset box shadows
    
    Still fall back in cases we can't handle that way.

 gsk/gl/gskglrenderer.c                   |  334 ++++++++++++++++++++++++++++--
 gsk/gl/gskglrenderops.c                  |    3 +-
 gsk/gl/gskglrenderopsprivate.h           |    3 -
 gsk/resources/glsl/outset_shadow.fs.glsl |   17 +-
 4 files changed, 319 insertions(+), 38 deletions(-)
---
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index 5f84f4b..fc68505 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -30,6 +30,8 @@
 #define HIGHLIGHT_FALLBACK 0
 #define DEBUG_OPS          0
 
+#define SHADOW_EXTRA_SIZE  4
+
 #if DEBUG_OPS
 #define OP_PRINT(format, ...) g_print(format, ## __VA_ARGS__)
 #else
@@ -49,7 +51,6 @@
                               glGetUniformLocation(program_ptr->id, "u_" #uniform_basename);\
               }G_STMT_END
 
-
 static void G_GNUC_UNUSED
 dump_framebuffer (const char *filename, int w, int h)
 {
@@ -150,6 +151,15 @@ color_matrix_modifies_alpha (GskRenderNode *node)
   return !graphene_vec4_equal (&id_row3, &row3);
 }
 
+static inline void
+gsk_rounded_rect_shrink_to_minimum (GskRoundedRect *self)
+{
+  self->bounds.size.width = ceilf (MAX (MAX (self->corner[0].width, self->corner[1].width),
+                                        MAX (self->corner[2].width, self->corner[3].width)) * 2);
+  self->bounds.size.height = ceilf (MAX (MAX (self->corner[0].height, self->corner[1].height),
+                                         MAX (self->corner[2].height, self->corner[3].height)) * 2);
+}
+
 static void gsk_gl_renderer_setup_render_mode (GskGLRenderer   *self);
 static void add_offscreen_ops                 (GskGLRenderer   *self,
                                                RenderOpBuilder *builder,
@@ -861,30 +871,314 @@ render_outset_shadow_node (GskGLRenderer       *self,
                            RenderOpBuilder     *builder,
                            const GskQuadVertex *vertex_data)
 {
+  const GskRoundedRect *outline = gsk_outset_shadow_node_peek_outline (node);
+  GskRoundedRect offset_outline;
+  const float blur_radius = gsk_outset_shadow_node_get_blur_radius (node);
+  const float spread = gsk_outset_shadow_node_get_spread (node);
+  const float dx = gsk_outset_shadow_node_get_dx (node);
+  const float dy = gsk_outset_shadow_node_get_dy (node);
+  const float min_x = outline->bounds.origin.x - spread - blur_radius;
+  const float min_y = outline->bounds.origin.y - spread - blur_radius;
+  const float max_x = min_x + outline->bounds.size.width  + (spread+blur_radius) * 2;
+  const float max_y = min_y + outline->bounds.size.height + (spread +blur_radius) * 2;
+  float texture_width, texture_height;
+  int i;
   RenderOp op;
+  graphene_matrix_t identity;
+  graphene_matrix_t prev_projection;
+  graphene_matrix_t prev_modelview;
+  graphene_rect_t prev_viewport;
+  graphene_matrix_t item_proj;
+  GskRoundedRect prev_clip, blit_clip;
+  int prev_render_target;
+  int texture_id, render_target;
+  int blurred_texture_id, blurred_render_target;
+
+  /* offset_outline is the minimal outline we need to draw the given drop shadow,
+   * enlarged by the spread and offset by the blur radius. */
+  offset_outline = *outline;
+  gsk_rounded_rect_shrink_to_minimum (&offset_outline);
+  gsk_rounded_rect_shrink (&offset_outline, -spread, -spread, -spread, -spread);
+  offset_outline.bounds.size.width += SHADOW_EXTRA_SIZE;
+  offset_outline.bounds.size.height += SHADOW_EXTRA_SIZE;
+  offset_outline.bounds.origin.x = blur_radius;
+  offset_outline.bounds.origin.y = blur_radius;
+
+  texture_width = offset_outline.bounds.size.width   + (blur_radius * 2);
+  texture_height = offset_outline.bounds.size.height + (blur_radius * 2);
+
+  /* TODO: Find out if this happens too often.
+   *       Is the condition used here really correct? */
+  for (i = 0; i < 4; i ++)
+    {
+      if (offset_outline.corner[i].width > spread ||
+          offset_outline.corner[i].height > spread)
+        {
+          /*g_warning ("'irregular' box shadow");*/
+          render_fallback_node (self, node, builder, vertex_data);
+          return;
+        }
+    }
+
+  texture_id = gsk_gl_driver_create_texture (self->gl_driver, texture_width, texture_height);
+  gsk_gl_driver_bind_source_texture (self->gl_driver, texture_id);
+  gsk_gl_driver_init_texture_empty (self->gl_driver, texture_id);
+  render_target = gsk_gl_driver_create_render_target (self->gl_driver, texture_id, FALSE, FALSE);
+
+
+  graphene_matrix_init_ortho (&item_proj,
+                              0, texture_width, 0, texture_height,
+                              ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE);
+  graphene_matrix_scale (&item_proj, 1, -1, 1);
+  graphene_matrix_init_identity (&identity);
 
-  /* TODO: Implement blurred outset shadows as well */
-  if (gsk_outset_shadow_node_get_blur_radius (node) > 0)
+  prev_render_target = ops_set_render_target (builder, render_target);
+  op.op = OP_CLEAR;
+  ops_add (builder, &op);
+  prev_projection = ops_set_projection (builder, &item_proj);
+  prev_modelview = ops_set_modelview (builder, &identity);
+  prev_viewport = ops_set_viewport (builder, &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height));
+
+  /* Draw outline */
+  ops_set_program (builder, &self->color_program);
+  prev_clip = ops_set_clip (builder, &offset_outline);
+  ops_set_color (builder, gsk_outset_shadow_node_peek_color (node));
+  ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
+    { { 0,                            }, { 0, 1 }, },
+    { { 0,             texture_height }, { 0, 0 }, },
+    { { texture_width,                }, { 1, 1 }, },
+
+    { { texture_width, texture_height }, { 1, 0 }, },
+    { { 0,             texture_height }, { 0, 0 }, },
+    { { texture_width,                }, { 1, 1 }, },
+  });
+
+  blurred_texture_id = gsk_gl_driver_create_texture (self->gl_driver, texture_width, texture_height);
+  gsk_gl_driver_bind_source_texture (self->gl_driver, blurred_texture_id);
+  gsk_gl_driver_init_texture_empty (self->gl_driver, blurred_texture_id);
+  blurred_render_target = gsk_gl_driver_create_render_target (self->gl_driver, blurred_texture_id, TRUE, 
TRUE);
+
+  ops_set_render_target (builder, blurred_render_target);
+  op.op = OP_CLEAR;
+  ops_add (builder, &op);
+
+  gsk_rounded_rect_init_from_rect (&blit_clip,
+                                   &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height), 0.0f);
+
+  if (blur_radius > 0)
     {
-      render_fallback_node (self, node, builder, vertex_data);
-      return;
+      ops_set_program (builder, &self->blur_program);
+      op.op = OP_CHANGE_BLUR;
+      op.blur.size.width = texture_width;
+      op.blur.size.height = texture_height;
+      op.blur.radius = blur_radius;
+      ops_add (builder, &op);
     }
+  else
+    {
+      ops_set_program (builder, &self->blit_program);
+    }
+
+  ops_set_clip (builder, &blit_clip);
+  ops_set_texture (builder, texture_id);
+  ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
+    { { 0,             0              }, { 0, 0 }, },
+    { { 0,             texture_height }, { 0, 1 }, },
+    { { texture_width, 0              }, { 1, 0 }, },
+
+    { { texture_width, texture_height }, { 1, 1 }, },
+    { { 0,             texture_height }, { 0, 1 }, },
+    { { texture_width, 0              }, { 1, 0 }, },
+
+  });
+  ops_set_clip (builder, &prev_clip);
+
+  ops_set_viewport (builder, &prev_viewport);
+  ops_set_modelview (builder, &prev_modelview);
+  ops_set_projection (builder, &prev_projection);
+  ops_set_render_target (builder, prev_render_target);
 
+  ops_set_program (builder, &self->outset_shadow_program);
+  ops_set_texture (builder, blurred_texture_id);
   op.op = OP_CHANGE_OUTSET_SHADOW;
-  rgba_to_float (gsk_outset_shadow_node_peek_color (node), op.outset_shadow.color);
   rounded_rect_to_floats (self, builder,
                           gsk_outset_shadow_node_peek_outline (node),
                           op.outset_shadow.outline,
                           op.outset_shadow.corner_widths,
                           op.outset_shadow.corner_heights);
-  op.outset_shadow.radius = gsk_outset_shadow_node_get_blur_radius (node) * self->scale_factor;
-  op.outset_shadow.spread = gsk_outset_shadow_node_get_spread (node) * self->scale_factor;
-  op.outset_shadow.offset[0] = gsk_outset_shadow_node_get_dx (node) * self->scale_factor;
-  op.outset_shadow.offset[1] = -gsk_outset_shadow_node_get_dy (node) * self->scale_factor;
-
-  ops_set_program (builder, &self->outset_shadow_program);
   ops_add (builder, &op);
-  ops_draw (builder, vertex_data);
+
+  /* We use the one outset shadow op from above to draw all 8 sides/corners. */
+  {
+    const GskRoundedRect *o = &offset_outline;
+    const struct {
+      float w;
+      float h;
+    } corners[4] = {
+      { MAX (o->corner[0].width,  spread) + blur_radius,
+        MAX (o->corner[0].height, spread) + blur_radius },
+      { MAX (o->corner[1].width,  spread) + blur_radius,
+        MAX (o->corner[1].height, spread) + blur_radius },
+      { MAX (o->corner[2].width,  spread) + blur_radius,
+        MAX (o->corner[2].height, spread) + blur_radius },
+      { MAX (o->corner[3].width,  spread) + blur_radius,
+        MAX (o->corner[3].height, spread) + blur_radius }
+    };
+    float x1 = min_x + dx;
+    float x2 = x1 + corners[0].w - dx;
+    float y1 = min_y + dy;
+    float y2 = y1 + corners[0].h  - dy;
+    float tx1 = 0;
+    float tx2 = ((corners[0].w  - dx) / texture_width);
+    float ty1 = 1 - ((corners[0].h - dy) / texture_height);
+    float ty2 = 1;
+
+    /* Top left */
+    ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
+      { { x1, y1 }, { tx1, ty2 }, },
+      { { x1, y2 }, { tx1, ty1 }, },
+      { { x2, y1 }, { tx2, ty2 }, },
+
+      { { x2, y2 }, { tx2, ty1 }, },
+      { { x1, y2 }, { tx1, ty1 }, },
+      { { x2, y1 }, { tx2, ty2 }, },
+    });
+
+    /* Top right */
+    x1 = max_x - corners[1].w ;
+    x2 = x1 + corners[1].w  + dx;
+    y1 = min_y + dy;
+    y2 = y1 + corners[1].h  - dy;
+    tx1 = 1 - ((corners[1].w  + dx) / texture_width);
+    tx2 = 1;
+    ty1 = 1 - ((corners[1].h  - dy) / texture_height);
+    ty2 = 1;
+    ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
+      { { x1, y1 }, { tx1, ty2 }, },
+      { { x1, y2 }, { tx1, ty1 }, },
+      { { x2, y1 }, { tx2, ty2 }, },
+
+      { { x2, y2 }, { tx2, ty1 }, },
+      { { x1, y2 }, { tx1, ty1 }, },
+      { { x2, y1 }, { tx2, ty2 }, },
+    });
+
+    /* Bottom right */
+    x1 = max_x - corners[2].w ;
+    x2 = x1 + corners[2].w  + dx;
+    y1 = max_y - corners[2].h ;
+    y2 = y1 + corners[2].h  + dy;
+    tx1 = 1 - ((corners[2].w  + dx) / texture_width);
+    tx2 = 1;
+    ty1 = 0;
+    ty2 = (corners[2].h  + dy) / texture_height;
+    ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
+      { { x1, y1 }, { tx1, ty2 }, },
+      { { x1, y2 }, { tx1, ty1 }, },
+      { { x2, y1 }, { tx2, ty2 }, },
+
+      { { x2, y2 }, { tx2, ty1 }, },
+      { { x1, y2 }, { tx1, ty1 }, },
+      { { x2, y1 }, { tx2, ty2 }, },
+    });
+
+    /* Bottom left */
+    x1 = min_x + dx;
+    x2 = x1 + corners[3].w  - dx;
+    y1 = max_y - corners[3].h ;
+    y2 = y1 + corners[3].h + dy;
+    tx1 = 0;
+    tx2 = (corners[3].w  - dx) / texture_width;
+    ty1 = 0;
+    ty2 = (corners[3].h  + dy) / texture_height;
+    ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
+      { { x1, y1 }, { tx1, ty2 }, },
+      { { x1, y2 }, { tx1, ty1 }, },
+      { { x2, y1 }, { tx2, ty2 }, },
+
+      { { x2, y2 }, { tx2, ty1 }, },
+      { { x1, y2 }, { tx1, ty1 }, },
+      { { x2, y1 }, { tx2, ty2 }, },
+    });
+
+    /* Left side */
+    x1 = min_x + dx;
+    x2 = x1 + corners[0].w - dx;
+    y1 = min_y + corners[0].h;
+    y2 = max_y - corners[3].h;
+    tx1 = 0;
+    tx2 = (x2 - x1) / texture_width;
+    ty1 = 1 - (corners[0].h / texture_height);
+    ty2 = ty1 - (SHADOW_EXTRA_SIZE / texture_height);
+    ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
+      { { x1, y1 }, { tx1, ty2 }, },
+      { { x1, y2 }, { tx1, ty1 }, },
+      { { x2, y1 }, { tx2, ty2 }, },
+
+      { { x2, y2 }, { tx2, ty1 }, },
+      { { x1, y2 }, { tx1, ty1 }, },
+      { { x2, y1 }, { tx2, ty2 }, },
+    });
+
+    /* Right side */
+    x1 = max_x - spread - blur_radius;
+    x2 = max_x + dx;
+    y1 = min_y + corners[0].h;
+    y2 = max_y - corners[3].h;
+    tx1 = 1 - ((x2 - x1) / texture_width);
+    tx2 = 1;
+    ty1 = 1 - (corners[1].h) / texture_height;
+    ty2 = ty1 - (SHADOW_EXTRA_SIZE / texture_height);
+    ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
+      { { x1, y1 }, { tx1, ty2 }, },
+      { { x1, y2 }, { tx1, ty1 }, },
+      { { x2, y1 }, { tx2, ty2 }, },
+
+      { { x2, y2 }, { tx2, ty1 }, },
+      { { x1, y2 }, { tx1, ty1 }, },
+      { { x2, y1 }, { tx2, ty2 }, },
+    });
+
+    /* Top side */
+    x1 = min_x + corners[0].w;
+    x2 = max_x - corners[1].w;
+    y1 = min_y + dy;
+    y2 = y1 + spread + blur_radius - dy;
+    tx1 = corners[1].w / texture_width;
+    tx2 = tx1 + (SHADOW_EXTRA_SIZE / texture_width);
+    ty1 = 1;
+    ty2 = 1 - ((y2 - y1) / texture_height);
+    ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
+      { { x1, y1 }, { tx1, ty1 }, },
+      { { x1, y2 }, { tx1, ty2 }, },
+      { { x2, y1 }, { tx2, ty1 }, },
+
+      { { x2, y2 }, { tx2, ty2 }, },
+      { { x1, y2 }, { tx1, ty2 }, },
+      { { x2, y1 }, { tx2, ty1 }, },
+    });
+
+    /* Bottom side */
+    x1 = min_x + corners[0].w;
+    x2 = max_x - corners[1].w;
+    y1 = max_y - corners[3].h ;
+    y2 = y1 + corners[3].h + dy;
+    tx1 = corners[3].w / texture_width;
+    tx2 = tx1 + (SHADOW_EXTRA_SIZE / texture_width);
+    ty1 = 0;
+    ty2 = (y2 - y1) / texture_height;
+    ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
+      { { x1, y1 }, { tx1, ty2 }, },
+      { { x1, y2 }, { tx1, ty1 }, },
+      { { x2, y1 }, { tx2, ty2 }, },
+
+      { { x2, y2 }, { tx2, ty1 }, },
+      { { x1, y2 }, { tx1, ty1 }, },
+      { { x2, y1 }, { tx2, ty2 }, },
+    });
+  }
+
+  ops_set_clip (builder, &prev_clip);
 }
 
 static inline void
@@ -1207,9 +1501,6 @@ apply_outset_shadow_op (const Program  *program,
                         const RenderOp *op)
 {
   OP_PRINT (" -> outset shadow");
-  glUniform4fv (program->outset_shadow.color_location, 1, op->outset_shadow.color);
-  glUniform2fv (program->outset_shadow.offset_location, 1, op->outset_shadow.offset);
-  glUniform1f (program->outset_shadow.spread_location, op->outset_shadow.spread);
   glUniform4fv (program->outset_shadow.outline_location, 1, op->outset_shadow.outline);
   glUniform4fv (program->outset_shadow.corner_widths_location, 1, op->outset_shadow.corner_widths);
   glUniform4fv (program->outset_shadow.corner_heights_location, 1, op->outset_shadow.corner_heights);
@@ -1458,9 +1749,6 @@ gsk_gl_renderer_create_programs (GskGLRenderer  *self,
   INIT_PROGRAM_UNIFORM_LOCATION (inset_shadow, corner_heights);
 
   /* outset shadow */
-  INIT_PROGRAM_UNIFORM_LOCATION (outset_shadow, color);
-  INIT_PROGRAM_UNIFORM_LOCATION (outset_shadow, spread);
-  INIT_PROGRAM_UNIFORM_LOCATION (outset_shadow, offset);
   INIT_PROGRAM_UNIFORM_LOCATION (outset_shadow, outline);
   INIT_PROGRAM_UNIFORM_LOCATION (outset_shadow, corner_widths);
   INIT_PROGRAM_UNIFORM_LOCATION (outset_shadow, corner_heights);
@@ -1936,6 +2224,8 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self,
         continue;
 
       if (op->op != OP_CHANGE_PROGRAM &&
+          op->op != OP_CHANGE_RENDER_TARGET &&
+          op->op != OP_CLEAR &&
           program == NULL)
         continue;
 
@@ -2004,15 +2294,15 @@ gsk_gl_renderer_render_ops (GskGLRenderer *self,
           apply_linear_gradient_op (program, op);
           break;
 
-       case OP_CHANGE_BLUR:
+        case OP_CHANGE_BLUR:
           apply_blur_op (program, op);
           break;
 
-       case OP_CHANGE_INSET_SHADOW:
+        case OP_CHANGE_INSET_SHADOW:
           apply_inset_shadow_op (program, op);
           break;
 
-       case OP_CHANGE_OUTSET_SHADOW:
+        case OP_CHANGE_OUTSET_SHADOW:
           apply_outset_shadow_op (program, op);
           break;
 
diff --git a/gsk/gl/gskglrenderops.c b/gsk/gl/gskglrenderops.c
index 9e2f719..0520ff7 100644
--- a/gsk/gl/gskglrenderops.c
+++ b/gsk/gl/gskglrenderops.c
@@ -117,7 +117,8 @@ ops_set_modelview (RenderOpBuilder         *builder,
   graphene_matrix_t prev_mv;
   RenderOp *last_op;
 
-  if (memcmp (&builder->program_state[builder->current_program->index].modelview, modelview,
+  if (builder->current_program &&
+      memcmp (&builder->program_state[builder->current_program->index].modelview, modelview,
               sizeof (graphene_matrix_t)) == 0)
     return *modelview;
 
diff --git a/gsk/gl/gskglrenderopsprivate.h b/gsk/gl/gskglrenderopsprivate.h
index 5081a0c..ff93d67 100644
--- a/gsk/gl/gskglrenderopsprivate.h
+++ b/gsk/gl/gskglrenderopsprivate.h
@@ -89,9 +89,6 @@ typedef struct
       int corner_heights_location;
     } inset_shadow;
     struct {
-      int color_location;
-      int spread_location;
-      int offset_location;
       int outline_location;
       int corner_widths_location;
       int corner_heights_location;
diff --git a/gsk/resources/glsl/outset_shadow.fs.glsl b/gsk/resources/glsl/outset_shadow.fs.glsl
index c927172..997891c 100644
--- a/gsk/resources/glsl/outset_shadow.fs.glsl
+++ b/gsk/resources/glsl/outset_shadow.fs.glsl
@@ -3,9 +3,8 @@ uniform float u_blur_radius;
 uniform vec4 u_color;
 uniform vec2 u_offset;
 uniform vec4 u_outline;
-uniform vec4 u_corner_widths;
-uniform vec4 u_corner_heights;
-
+uniform vec4 u_corner_widths = vec4(0, 0, 0, 0);
+uniform vec4 u_corner_heights = vec4(0, 0, 0, 0);
 
 void main() {
   vec4 f = gl_FragCoord;
@@ -13,15 +12,9 @@ void main() {
   f.x += u_viewport.x;
   f.y = (u_viewport.y + u_viewport.w) - f.y;
 
-  RoundedRect outline = RoundedRect(vec4(u_outline.xy, u_outline.xy + u_outline.zw),
-                                    u_corner_widths, u_corner_heights);
-  RoundedRect outside = rounded_rect_shrink(outline, vec4(- u_spread));
-
+  RoundedRect outline = RoundedRect(vec4(u_outline.xy, u_outline.xy + u_outline.zw), u_corner_widths, 
u_corner_heights);
 
-  vec2 offset = vec2(u_offset.x, - u_offset.y);
-  vec4 color = vec4(u_color.rgb * u_color.a, u_color.a);
-  color = color * clamp (rounded_rect_coverage (outside, f.xy - offset) -
-                         rounded_rect_coverage (outline, f.xy),
-                         0.0, 1.0);
+  vec4 color = Texture(u_source, vUv);
+  color *= (1 - clamp(rounded_rect_coverage (outline, f.xy), 0, 1));
   setOutputColor(color);
 }


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