From b791aa0301d2df088836c7d0f123c815ed46400e Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 19 May 2023 05:35:18 +0200 Subject: [PATCH] vulkan: Clip using scissors If we have a rectangular clip without transforms, we can use scissoring. This works particularly well because it allows intersecting rounded rectangles with regular rectangles in all cases: Use the scissor rect for the rectangle and the normal clipping code for the rounded rectangle. --- gsk/vulkan/gskvulkanclip.c | 2 +- gsk/vulkan/gskvulkanclipprivate.h | 2 + gsk/vulkan/gskvulkanrenderpass.c | 84 +++++++++++++++++++++++++++++-- 3 files changed, 82 insertions(+), 6 deletions(-) diff --git a/gsk/vulkan/gskvulkanclip.c b/gsk/vulkan/gskvulkanclip.c index dcc42c5183..0d255d8148 100644 --- a/gsk/vulkan/gskvulkanclip.c +++ b/gsk/vulkan/gskvulkanclip.c @@ -21,7 +21,7 @@ gsk_vulkan_clip_init_rect (GskVulkanClip *clip, gsk_rounded_rect_init_from_rect (&clip->rect, rect, 0); } -static void +void gsk_vulkan_clip_init_copy (GskVulkanClip *self, const GskVulkanClip *src) { diff --git a/gsk/vulkan/gskvulkanclipprivate.h b/gsk/vulkan/gskvulkanclipprivate.h index 303cbf0cb8..7f7cba2e8a 100644 --- a/gsk/vulkan/gskvulkanclipprivate.h +++ b/gsk/vulkan/gskvulkanclipprivate.h @@ -36,6 +36,8 @@ struct _GskVulkanClip void gsk_vulkan_clip_init_empty (GskVulkanClip *clip, const graphene_rect_t *rect); +void gsk_vulkan_clip_init_copy (GskVulkanClip *self, + const GskVulkanClip *src); void gsk_vulkan_clip_init_rect (GskVulkanClip *clip, const graphene_rect_t *rect); diff --git a/gsk/vulkan/gskvulkanrenderpass.c b/gsk/vulkan/gskvulkanrenderpass.c index e4e7c1ea0e..674ae1b14b 100644 --- a/gsk/vulkan/gskvulkanrenderpass.c +++ b/gsk/vulkan/gskvulkanrenderpass.c @@ -766,6 +766,45 @@ gsk_vulkan_render_pass_add_color_matrix_node (GskVulkanRenderPass *self, return TRUE; } +static gboolean +clip_can_be_scissored (const graphene_rect_t *rect, + const graphene_vec2_t *scale, + GskTransform *modelview, + cairo_rectangle_int_t *int_rect) +{ + graphene_rect_t transformed_rect; + float scale_x = graphene_vec2_get_x (scale); + float scale_y = graphene_vec2_get_y (scale); + + switch (gsk_transform_get_category (modelview)) + { + case GSK_TRANSFORM_CATEGORY_UNKNOWN: + case GSK_TRANSFORM_CATEGORY_ANY: + case GSK_TRANSFORM_CATEGORY_3D: + case GSK_TRANSFORM_CATEGORY_2D: + return FALSE; + + case GSK_TRANSFORM_CATEGORY_2D_AFFINE: + case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE: + gsk_transform_transform_bounds (modelview, rect, &transformed_rect); + rect = &transformed_rect; + break; + + case GSK_TRANSFORM_CATEGORY_IDENTITY: + default: + break; + } + int_rect->x = rect->origin.x * scale_x; + int_rect->y = rect->origin.y * scale_y; + int_rect->width = rect->size.width * scale_x; + int_rect->height = rect->size.height * scale_y; + + return int_rect->x == rect->origin.x * scale_x + && int_rect->y == rect->origin.y * scale_y + && int_rect->width == rect->size.width * scale_x + && int_rect->height == rect->size.height * scale_y; +} + static inline gboolean gsk_vulkan_render_pass_add_clip_node (GskVulkanRenderPass *self, GskVulkanRender *render, @@ -774,28 +813,63 @@ gsk_vulkan_render_pass_add_clip_node (GskVulkanRenderPass *self, { GskVulkanParseState new_state; graphene_rect_t clip; + gboolean do_push_constants, do_scissor; graphene_rect_offset_r (gsk_clip_node_get_clip (node), state->offset.x, state->offset.y, &clip); - if (!gsk_vulkan_clip_intersect_rect (&new_state.clip, &state->clip, &clip)) - FALLBACK ("Failed to find intersection between clip of type %u and rectangle", state->clip.type); + /* Check if we can use scissoring for the clip */ + if (clip_can_be_scissored (&clip, &state->scale, state->modelview, &new_state.scissor)) + { + if (!gdk_rectangle_intersect (&new_state.scissor, &state->scissor, &new_state.scissor)) + return TRUE; + + if (gsk_vulkan_clip_intersect_rect (&new_state.clip, &state->clip, &clip)) + { + if (new_state.clip.type == GSK_VULKAN_CLIP_RECT) + new_state.clip.type = GSK_VULKAN_CLIP_NONE; + + do_push_constants = TRUE; + } + else + { + gsk_vulkan_clip_init_copy (&new_state.clip, &state->clip); + do_push_constants = FALSE; + } + + do_scissor = TRUE; + } + else + { + if (!gsk_vulkan_clip_intersect_rect (&new_state.clip, &state->clip, &clip)) + FALLBACK ("Failed to find intersection between clip of type %u and rectangle", state->clip.type); + + new_state.scissor = state->scissor; + + do_push_constants = TRUE; + do_scissor = FALSE; + } if (new_state.clip.type == GSK_VULKAN_CLIP_ALL_CLIPPED) return TRUE; - new_state.scissor = state->scissor; new_state.offset = state->offset; graphene_vec2_init_from_vec2 (&new_state.scale, &state->scale); new_state.modelview = state->modelview; graphene_matrix_init_from_matrix (&new_state.projection, &state->projection); - gsk_vulkan_render_pass_append_push_constants (self, node, &new_state); + if (do_scissor) + gsk_vulkan_render_pass_append_scissor (self, node, &new_state); + if (do_push_constants) + gsk_vulkan_render_pass_append_push_constants (self, node, &new_state); gsk_vulkan_render_pass_add_node (self, render, &new_state, gsk_clip_node_get_child (node)); - gsk_vulkan_render_pass_append_push_constants (self, node, state); + if (do_push_constants) + gsk_vulkan_render_pass_append_push_constants (self, node, state); + if (do_scissor) + gsk_vulkan_render_pass_append_scissor (self, node, state); return TRUE; } -- 2.30.2