vulkan: Try really hard to use clear

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.
This commit is contained in:
Benjamin Otte 2023-07-12 11:04:25 +02:00
parent bb2cd7225e
commit ce042f7ba1
3 changed files with 107 additions and 13 deletions

View 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;
}

View 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,

View 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,