vulkan: Try really hard to use clear
authorBenjamin Otte <otte@redhat.com>
Wed, 12 Jul 2023 09:04:25 +0000 (11:04 +0200)
committerBenjamin Otte <otte@redhat.com>
Sun, 16 Jul 2023 11:16:41 +0000 (13:16 +0200)
Using clear avoids the shader engine (see last commit), so if we can get
pixels out of it, we should.

So we detect the overlap with the rounded corners of the clip region and
emit shaders for those, but then use Clear() for the rest.

With this in place, widget-factory on my integrated Intel TigerLake gets
a 60% performance boost.

gsk/gskroundedrect.c
gsk/gskroundedrectprivate.h
gsk/vulkan/gskvulkanrenderpass.c

index 76efe4d9aefbef1e0ebb3404907d65d0ebb88d42..7c1a9cc6ec61bac06701121af94e37c070dc8bd0 100644 (file)
@@ -945,3 +945,44 @@ gsk_rounded_rect_to_string (const GskRoundedRect *self)
                           self->corner[3].width,
                           self->corner[3].height);
 }
+
+/*
+ * gsk_rounded_rect_get_largest_cover:
+ * @self: the rounded rect to intersect with
+ * @rect: the rectangle to intersect
+ * @result: (out caller-allocates): The resulting rectangle
+ *
+ * Computes the largest rectangle that is fully covered by both
+ * the given rect and the rounded rect.  
+ * In particular, this function respects corners, so
+ *   gsk_rounded_rect_get_largest_cover(self, &self->bounds, &rect)
+ * can be used to compute a decomposition for a rounded rect itself.
+ **/
+void
+gsk_rounded_rect_get_largest_cover (const GskRoundedRect  *self,
+                                    const graphene_rect_t *rect,
+                                    graphene_rect_t       *result)
+{
+  graphene_rect_t wide, high;
+  double start, end;
+
+  wide = self->bounds;
+  start = MAX(self->corner[GSK_CORNER_TOP_LEFT].height, self->corner[GSK_CORNER_TOP_RIGHT].height);
+  end = MAX(self->corner[GSK_CORNER_BOTTOM_LEFT].height, self->corner[GSK_CORNER_BOTTOM_RIGHT].height);
+  wide.size.height -= MIN (wide.size.height, start + end);
+  wide.origin.y += start;
+  graphene_rect_intersection (&wide, rect, &wide);
+
+  high = self->bounds;
+  start = MAX(self->corner[GSK_CORNER_TOP_LEFT].width, self->corner[GSK_CORNER_BOTTOM_LEFT].width);
+  end = MAX(self->corner[GSK_CORNER_TOP_RIGHT].width, self->corner[GSK_CORNER_BOTTOM_RIGHT].width);
+  high.size.width -= MIN (high.size.width, start + end);
+  high.origin.x += start;
+  graphene_rect_intersection (&high, rect, &high);
+
+  if (wide.size.width * wide.size.height > high.size.width * high.size.height)
+    *result = wide;
+  else
+    *result = high;
+}
+
index 64c3469df94fe7f84af645f74ff79266784056cf..964e0edf8455f19105062676bbe2f0ce7bd995e9 100644 (file)
@@ -53,6 +53,10 @@ gboolean                 gsk_rounded_rect_equal                 (gconstpointer
                                                                  gconstpointer             rect2) G_GNUC_PURE;
 char *                   gsk_rounded_rect_to_string             (const GskRoundedRect     *self) G_GNUC_MALLOC;
 
+void                     gsk_rounded_rect_get_largest_cover     (const GskRoundedRect     *self,
+                                                                 const graphene_rect_t    *rect,
+                                                                 graphene_rect_t          *result);
+
 typedef enum {
   GSK_INTERSECTION_EMPTY,
   GSK_INTERSECTION_NONEMPTY,
index 4d331ddfc1fea746123abe6ec0638548b2f3731a..51d849c1c6a3eb1d45befb27b875d2516d16dc05 100644 (file)
@@ -368,21 +368,70 @@ gsk_vulkan_render_pass_add_color_node (GskVulkanRenderPass       *self,
         return TRUE;
 
       /* we have handled the bounds, now do the corners */
-      if (state->clip.type != GSK_VULKAN_CLIP_ROUNDED ||
-          gsk_vulkan_clip_contains_rect (&state->clip, 
-                                         graphene_point_zero (),
-                                         &GRAPHENE_RECT_INIT (
-                                           int_clipped.x / graphene_vec2_get_x (&state->scale),
-                                           int_clipped.y / graphene_vec2_get_y (&state->scale),
-                                           int_clipped.width / graphene_vec2_get_x (&state->scale),
-                                           int_clipped.height / graphene_vec2_get_y (&state->scale)
-                                         )))
+      if (state->clip.type == GSK_VULKAN_CLIP_ROUNDED)
         {
-          gsk_vulkan_clear_op (render,
-                               &int_clipped,
-                               color);
-          return TRUE;
+          graphene_rect_t cover;
+          const char *clip_type;
+          float scale_x = graphene_vec2_get_x (&state->scale);
+          float scale_y = graphene_vec2_get_y (&state->scale);
+          clipped = GRAPHENE_RECT_INIT (int_clipped.x / scale_x, int_clipped.y / scale_y,
+                                        int_clipped.width / scale_x, int_clipped.height / scale_y);
+          clip_type = gsk_vulkan_clip_get_clip_type (&state->clip, graphene_point_zero(), &clipped);
+          if (clip_type[0] != '\0')
+            {
+              gsk_rounded_rect_get_largest_cover (&state->clip.rect, &clipped, &cover);
+              int_clipped.x = ceil (cover.origin.x * scale_x);
+              int_clipped.y = ceil (cover.origin.y * scale_y);
+              int_clipped.width = floor ((cover.origin.x + cover.size.width) * scale_x) - int_clipped.x;
+              int_clipped.height = floor ((cover.origin.y + cover.size.height) * scale_y) - int_clipped.y;
+              if (int_clipped.width == 0 || int_clipped.height == 0)
+                {
+                  gsk_vulkan_color_op (render,
+                                       clip_type,
+                                       &clipped,
+                                       graphene_point_zero (),
+                                       color);
+                  return TRUE;
+                }
+              cover = GRAPHENE_RECT_INIT (int_clipped.x / scale_x, int_clipped.y / scale_y,
+                                          int_clipped.width / scale_x, int_clipped.height / scale_y);
+              if (clipped.origin.x != cover.origin.x)
+                gsk_vulkan_color_op (render,
+                                     clip_type,
+                                     &GRAPHENE_RECT_INIT (clipped.origin.x, clipped.origin.y, cover.origin.x - clipped.origin.x, clipped.size.height),
+                                     graphene_point_zero (),
+                                     color);
+              if (clipped.origin.y != cover.origin.y)
+                gsk_vulkan_color_op (render,
+                                     clip_type,
+                                     &GRAPHENE_RECT_INIT (clipped.origin.x, clipped.origin.y, clipped.size.width, cover.origin.y - clipped.origin.y),
+                                     graphene_point_zero (),
+                                     color);
+              if (clipped.origin.x + clipped.size.width != cover.origin.x + cover.size.width)
+                gsk_vulkan_color_op (render,
+                                     clip_type,
+                                     &GRAPHENE_RECT_INIT (cover.origin.x + cover.size.width,
+                                                          clipped.origin.y,
+                                                          clipped.origin.x + clipped.size.width - cover.origin.x - cover.size.width,
+                                                          clipped.size.height),
+                                     graphene_point_zero (),
+                                     color);
+              if (clipped.origin.y + clipped.size.height != cover.origin.y + cover.size.height)
+                gsk_vulkan_color_op (render,
+                                     clip_type,
+                                     &GRAPHENE_RECT_INIT (clipped.origin.x,
+                                                          cover.origin.y + cover.size.height,
+                                                          clipped.size.width,
+                                                          clipped.origin.y + clipped.size.height - cover.origin.y - cover.size.height),
+                                     graphene_point_zero (),
+                                     color);
+            }
         }
+
+      gsk_vulkan_clear_op (render,
+                           &int_clipped,
+                           color);
+      return TRUE;
     }
 
   gsk_vulkan_color_op (render,