diff --git a/gsk/Makefile.am b/gsk/Makefile.am index 1f2730b899..31f9eacf21 100644 --- a/gsk/Makefile.am +++ b/gsk/Makefile.am @@ -26,6 +26,7 @@ if HAVE_VULKAN gsk_private_vulkan_source_h = \ gskvulkanblendpipelineprivate.h \ gskvulkanbufferprivate.h \ + gskvulkanclipprivate.h \ gskvulkancolorpipelineprivate.h \ gskvulkancommandpoolprivate.h \ gskvulkanimageprivate.h \ @@ -39,6 +40,7 @@ gsk_private_vulkan_source_h = \ gsk_private_vulkan_source_c = \ gskvulkanblendpipeline.c \ gskvulkanbuffer.c \ + gskvulkanclip.c \ gskvulkancolorpipeline.c \ gskvulkancommandpool.c \ gskvulkanimage.c \ diff --git a/gsk/gskroundedrect.c b/gsk/gskroundedrect.c index a38b96e58c..16ad85ef45 100644 --- a/gsk/gskroundedrect.c +++ b/gsk/gskroundedrect.c @@ -277,6 +277,21 @@ gsk_rounded_rect_shrink (GskRoundedRect *self, return self; } +/* XXX: Fina a better name */ +gboolean +gsk_rounded_rect_is_circular (const GskRoundedRect *self) +{ + guint i; + + for (i = 0; i < 4; i++) + { + if (self->corner[i].width != self->corner[i].height) + return FALSE; + } + + return TRUE; +} + /** * gsk_rounded_rect_is_rectilinear: * @self: the #GskRoundedRect to check diff --git a/gsk/gskroundedrectprivate.h b/gsk/gskroundedrectprivate.h index 39b968c562..d11567ad28 100644 --- a/gsk/gskroundedrectprivate.h +++ b/gsk/gskroundedrectprivate.h @@ -7,6 +7,8 @@ G_BEGIN_DECLS +gboolean gsk_rounded_rect_is_circular (const GskRoundedRect *self); + void gsk_rounded_rect_path (const GskRoundedRect *self, cairo_t *cr); diff --git a/gsk/gskvulkanclip.c b/gsk/gskvulkanclip.c new file mode 100644 index 0000000000..38755f5c24 --- /dev/null +++ b/gsk/gskvulkanclip.c @@ -0,0 +1,151 @@ +#include "config.h" + +#include "gskvulkanclipprivate.h" + +#include "gskroundedrectprivate.h" + +void +gsk_vulkan_clip_init_empty (GskVulkanClip *clip, + const graphene_rect_t *rect) +{ + clip->type = GSK_VULKAN_CLIP_NONE; + gsk_rounded_rect_init_from_rect (&clip->rect, rect, 0); +} + +static void +gsk_vulkan_clip_init_copy (GskVulkanClip *self, + const GskVulkanClip *src) +{ + self->type = src->type; + gsk_rounded_rect_init_copy (&self->rect, &src->rect); +} + +gboolean +gsk_vulkan_clip_intersect_rect (GskVulkanClip *dest, + const GskVulkanClip *src, + const graphene_rect_t *rect) +{ + if (graphene_rect_contains_rect (rect, &src->rect.bounds)) + { + gsk_vulkan_clip_init_copy (dest, src); + return TRUE; + } + if (!graphene_rect_intersection (rect, &src->rect.bounds, NULL)) + { + dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED; + return TRUE; + } + + switch (src->type) + { + case GSK_VULKAN_CLIP_ALL_CLIPPED: + dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED; + break; + + case GSK_VULKAN_CLIP_NONE: + gsk_vulkan_clip_init_copy (dest, src); + if (graphene_rect_intersection (&dest->rect.bounds, rect, &dest->rect.bounds)) + dest->type = GSK_VULKAN_CLIP_RECT; + else + dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED; + break; + + case GSK_VULKAN_CLIP_RECT: + gsk_vulkan_clip_init_copy (dest, src); + if (!graphene_rect_intersection (&dest->rect.bounds, rect, &dest->rect.bounds)) + dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED; + break; + + case GSK_VULKAN_CLIP_ROUNDED_CIRCULAR: + case GSK_VULKAN_CLIP_ROUNDED: + if (gsk_rounded_rect_contains_rect (&src->rect, rect)) + { + dest->type = GSK_VULKAN_CLIP_RECT; + gsk_rounded_rect_init_from_rect (&dest->rect, rect, 0); + } + else + { + /* some points of rect are inside src's rounded rect, + * some are outside. */ + /* XXX: If the 2 rects don't intersect on rounded corners, + * we could actually compute a new clip here. + */ + return FALSE; + } + } + + return TRUE; +} + +gboolean +gsk_vulkan_clip_intersect_rounded_rect (GskVulkanClip *dest, + const GskVulkanClip *src, + const GskRoundedRect *rounded) +{ + if (gsk_rounded_rect_contains_rect (rounded, &src->rect.bounds)) + { + gsk_vulkan_clip_init_copy (dest, src); + return TRUE; + } + if (!graphene_rect_intersection (&rounded->bounds, &src->rect.bounds, NULL)) + { + dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED; + return TRUE; + } + + switch (src->type) + { + case GSK_VULKAN_CLIP_ALL_CLIPPED: + dest->type = GSK_VULKAN_CLIP_ALL_CLIPPED; + break; + + case GSK_VULKAN_CLIP_NONE: + dest->type = gsk_rounded_rect_is_circular (&dest->rect) ? GSK_VULKAN_CLIP_ROUNDED_CIRCULAR : GSK_VULKAN_CLIP_ROUNDED; + gsk_rounded_rect_init_copy (&dest->rect, rounded); + break; + + case GSK_VULKAN_CLIP_RECT: + if (graphene_rect_contains_rect (&src->rect.bounds, &rounded->bounds)) + { + dest->type = gsk_rounded_rect_is_circular (&dest->rect) ? GSK_VULKAN_CLIP_ROUNDED_CIRCULAR : GSK_VULKAN_CLIP_ROUNDED; + gsk_rounded_rect_init_copy (&dest->rect, rounded); + return TRUE; + } + /* some points of rect are inside src's rounded rect, + * some are outside. */ + /* XXX: If the 2 rects don't intersect on rounded corners, + * we could actually compute a new clip here. + */ + return FALSE; + + case GSK_VULKAN_CLIP_ROUNDED_CIRCULAR: + case GSK_VULKAN_CLIP_ROUNDED: + /* XXX: improve */ + return FALSE; + } + + return TRUE; +} + +gboolean +gsk_vulkan_clip_contains_rect (const GskVulkanClip *self, + const graphene_rect_t *rect) +{ + switch (self->type) + { + default: + g_assert_not_reached(); + case GSK_VULKAN_CLIP_ALL_CLIPPED: + return FALSE; + + case GSK_VULKAN_CLIP_NONE: + return TRUE; + + case GSK_VULKAN_CLIP_RECT: + return graphene_rect_contains_rect (&self->rect.bounds, rect); + + case GSK_VULKAN_CLIP_ROUNDED_CIRCULAR: + case GSK_VULKAN_CLIP_ROUNDED: + return gsk_rounded_rect_contains_rect (&self->rect, rect); + } +} diff --git a/gsk/gskvulkanclipprivate.h b/gsk/gskvulkanclipprivate.h new file mode 100644 index 0000000000..492ef4c2b1 --- /dev/null +++ b/gsk/gskvulkanclipprivate.h @@ -0,0 +1,53 @@ +#ifndef __GSK_VULKAN_CLIP_PRIVATE_H__ +#define __GSK_VULKAN_CLIP_PRIVATE_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +typedef enum { + /* The whole area is clipped, no drawing is necessary. + * This can't be handled by return values because for return + * values we return if clips could even be computed. + */ + GSK_VULKAN_CLIP_ALL_CLIPPED, + /* No clipping is necesary, but the clip rect is set + * to the actual bounds of the underlying framebuffer + */ + GSK_VULKAN_CLIP_NONE, + /* The clip is a rectangular area */ + GSK_VULKAN_CLIP_RECT, + /* The clip is a rounded rectangle, and for every corner + * corner.width == corner.height is true + */ + GSK_VULKAN_CLIP_ROUNDED_CIRCULAR, + /* The clip is a rounded rectangle */ + GSK_VULKAN_CLIP_ROUNDED +} GskVulkanClipComplexity; + +typedef struct _GskVulkanClip GskVulkanClip; + +struct _GskVulkanClip +{ + GskVulkanClipComplexity type; + GskRoundedRect rect; +}; + +void gsk_vulkan_clip_init_empty (GskVulkanClip *clip, + const graphene_rect_t *rect); + +gboolean gsk_vulkan_clip_intersect_rect (GskVulkanClip *dest, + const GskVulkanClip *src, + const graphene_rect_t *rect) G_GNUC_WARN_UNUSED_RESULT; +gboolean gsk_vulkan_clip_intersect_rounded_rect (GskVulkanClip *dest, + const GskVulkanClip *src, + const GskRoundedRect *rounded) G_GNUC_WARN_UNUSED_RESULT; + +gboolean gsk_vulkan_clip_contains_rect (const GskVulkanClip *self, + const graphene_rect_t *rect) G_GNUC_WARN_UNUSED_RESULT; + +G_END_DECLS + +#endif /* __GSK_VULKAN_CLIP_PRIVATE_H__ */ diff --git a/gsk/gskvulkanrender.c b/gsk/gskvulkanrender.c index eabdd6f6ce..e2e8167b4c 100644 --- a/gsk/gskvulkanrender.c +++ b/gsk/gskvulkanrender.c @@ -243,7 +243,14 @@ gsk_vulkan_render_add_node (GskVulkanRender *self, self->render_passes = g_slist_prepend (self->render_passes, pass); - gsk_vulkan_render_pass_add (pass, self, &self->mvp, node); + gsk_vulkan_render_pass_add (pass, + self, + &self->mvp, + &GRAPHENE_RECT_INIT ( + self->viewport.offset.x, self->viewport.offset.y, + self->viewport.extent.width, self->viewport.extent.height + ), + node); } void diff --git a/gsk/gskvulkanrenderpass.c b/gsk/gskvulkanrenderpass.c index e5a343a5e1..a7ba94d0c0 100644 --- a/gsk/gskvulkanrenderpass.c +++ b/gsk/gskvulkanrenderpass.c @@ -2,11 +2,13 @@ #include "gskvulkanrenderpassprivate.h" -#include "gskvulkanblendpipelineprivate.h" -#include "gskvulkancolorpipelineprivate.h" -#include "gskvulkanimageprivate.h" #include "gskrendernodeprivate.h" #include "gskrenderer.h" +#include "gskroundedrectprivate.h" +#include "gskvulkanblendpipelineprivate.h" +#include "gskvulkanclipprivate.h" +#include "gskvulkancolorpipelineprivate.h" +#include "gskvulkanimageprivate.h" #include "gskvulkanpushconstantsprivate.h" #include "gskvulkanrendererprivate.h" @@ -17,6 +19,8 @@ typedef struct _GskVulkanOpPushConstants GskVulkanOpPushConstants; typedef enum { /* GskVulkanOpRender */ GSK_VULKAN_OP_FALLBACK, + GSK_VULKAN_OP_FALLBACK_CLIP, + GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP, GSK_VULKAN_OP_SURFACE, GSK_VULKAN_OP_TEXTURE, GSK_VULKAN_OP_COLOR, @@ -29,6 +33,7 @@ struct _GskVulkanOpRender GskVulkanOpType type; GskRenderNode *node; /* node that's the source of this op */ GskVulkanPipeline *pipeline; /* pipeline to use */ + GskRoundedRect clip; /* clip rect (or random memory if not relevant) */ GskVulkanImage *source; /* source image to render */ gsize vertex_offset; /* offset into vertex buffer */ gsize vertex_count; /* number of vertices */ @@ -81,6 +86,7 @@ void gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self, GskVulkanRender *render, const GskVulkanPushConstants *constants, + const GskVulkanClip *clip, GskRenderNode *node) { GskVulkanOp op = { @@ -92,34 +98,35 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self, { case GSK_NOT_A_RENDER_NODE: g_assert_not_reached (); - break; - + return; default: - op.type = GSK_VULKAN_OP_FALLBACK; - op.render.pipeline = gsk_vulkan_render_get_pipeline (render, GSK_VULKAN_PIPELINE_BLIT); - g_array_append_val (self->render_ops, op); - break; + goto fallback; case GSK_CAIRO_NODE: - if (gsk_cairo_node_get_surface (node) != NULL) - { - op.type = GSK_VULKAN_OP_SURFACE; - op.render.pipeline = gsk_vulkan_render_get_pipeline (render, GSK_VULKAN_PIPELINE_BLIT); - g_array_append_val (self->render_ops, op); - } - break; + if (gsk_cairo_node_get_surface (node) == NULL) + return; + if (!gsk_vulkan_clip_contains_rect (clip, &node->bounds)) + goto fallback; + op.type = GSK_VULKAN_OP_SURFACE; + op.render.pipeline = gsk_vulkan_render_get_pipeline (render, GSK_VULKAN_PIPELINE_BLIT); + g_array_append_val (self->render_ops, op); + return; case GSK_TEXTURE_NODE: + if (!gsk_vulkan_clip_contains_rect (clip, &node->bounds)) + goto fallback; op.type = GSK_VULKAN_OP_TEXTURE; op.render.pipeline = gsk_vulkan_render_get_pipeline (render, GSK_VULKAN_PIPELINE_BLIT); g_array_append_val (self->render_ops, op); - break; + return; case GSK_COLOR_NODE: + if (!gsk_vulkan_clip_contains_rect (clip, &node->bounds)) + goto fallback; op.type = GSK_VULKAN_OP_COLOR; op.render.pipeline = gsk_vulkan_render_get_pipeline (render, GSK_VULKAN_PIPELINE_COLOR); g_array_append_val (self->render_ops, op); - break; + return; case GSK_CONTAINER_NODE: { @@ -127,13 +134,19 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self, for (i = 0; i < gsk_container_node_get_n_children (node); i++) { - gsk_vulkan_render_pass_add_node (self, render, constants, gsk_container_node_get_child (node, i)); + gsk_vulkan_render_pass_add_node (self, render, constants, clip, gsk_container_node_get_child (node, i)); } } - break; + return; + case GSK_TRANSFORM_NODE: { graphene_matrix_t transform; + GskVulkanClip new_clip; + graphene_rect_t rect; + + if (!gsk_vulkan_clip_contains_rect (clip, &node->bounds)) + goto fallback; gsk_transform_node_get_transform (node, &transform); op.type = GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS; @@ -141,28 +154,86 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self, gsk_vulkan_push_constants_multiply_mvp (&op.constants.constants, &transform); g_array_append_val (self->render_ops, op); - gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, gsk_transform_node_get_child (node)); + graphene_matrix_transform_bounds (&transform, &clip->rect.bounds, &rect); + gsk_vulkan_clip_init_empty (&new_clip, &rect); + + gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, &new_clip, gsk_transform_node_get_child (node)); gsk_vulkan_push_constants_init_copy (&op.constants.constants, constants); g_array_append_val (self->render_ops, op); } - break; + return; + case GSK_CLIP_NODE: + { + GskVulkanClip new_clip; + + if (!gsk_vulkan_clip_intersect_rect (&new_clip, clip, gsk_clip_node_peek_clip (node))) + goto fallback; + if (new_clip.type == GSK_VULKAN_CLIP_ALL_CLIPPED) + return; + + gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, &new_clip, gsk_clip_node_get_child (node)); + } + return; + + case GSK_ROUNDED_CLIP_NODE: + { + GskVulkanClip new_clip; + + if (!gsk_vulkan_clip_intersect_rounded_rect (&new_clip, clip, gsk_rounded_clip_node_peek_clip (node))) + goto fallback; + if (new_clip.type == GSK_VULKAN_CLIP_ALL_CLIPPED) + return; + + gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, &new_clip, gsk_rounded_clip_node_get_child (node)); + } + return; } + + g_assert_not_reached (); + return; + +fallback: + switch (clip->type) + { + case GSK_VULKAN_CLIP_NONE: + op.type = GSK_VULKAN_OP_FALLBACK; + break; + case GSK_VULKAN_CLIP_RECT: + op.type = GSK_VULKAN_OP_FALLBACK_CLIP; + gsk_rounded_rect_init_copy (&op.render.clip, &clip->rect); + break; + case GSK_VULKAN_CLIP_ROUNDED_CIRCULAR: + case GSK_VULKAN_CLIP_ROUNDED: + op.type = GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP; + gsk_rounded_rect_init_copy (&op.render.clip, &clip->rect); + break; + case GSK_VULKAN_CLIP_ALL_CLIPPED: + default: + g_assert_not_reached (); + return; + } + op.render.pipeline = gsk_vulkan_render_get_pipeline (render, GSK_VULKAN_PIPELINE_BLIT); + g_array_append_val (self->render_ops, op); } void gsk_vulkan_render_pass_add (GskVulkanRenderPass *self, GskVulkanRender *render, const graphene_matrix_t *mvp, + const graphene_rect_t *viewport, GskRenderNode *node) { GskVulkanOp op = { 0, }; + GskVulkanClip clip; op.type = GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS; gsk_vulkan_push_constants_init (&op.constants.constants, mvp); g_array_append_val (self->render_ops, op); - gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, node); + gsk_vulkan_clip_init_empty (&clip, viewport); + + gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, &clip, node); } static void @@ -177,12 +248,30 @@ gsk_vulkan_render_pass_upload_fallback (GskVulkanRenderPass *self, node = op->node; + /* XXX: We could intersect bounds with clip bounds here */ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, ceil (node->bounds.size.width), ceil (node->bounds.size.height)); cr = cairo_create (surface); cairo_translate (cr, -node->bounds.origin.x, -node->bounds.origin.y); + if (op->type == GSK_VULKAN_OP_FALLBACK_CLIP) + { + cairo_rectangle (cr, + op->clip.bounds.origin.x, op->clip.bounds.origin.y, + op->clip.bounds.size.width, op->clip.bounds.size.height); + cairo_clip (cr); + } + else if (op->type == GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP) + { + gsk_rounded_rect_path (&op->clip, cr); + cairo_clip (cr); + } + else + { + g_assert (op->type == GSK_VULKAN_OP_FALLBACK); + } + gsk_render_node_draw (node, cr); cairo_destroy (cr); @@ -213,6 +302,8 @@ gsk_vulkan_render_pass_upload (GskVulkanRenderPass *self, switch (op->type) { case GSK_VULKAN_OP_FALLBACK: + case GSK_VULKAN_OP_FALLBACK_CLIP: + case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP: gsk_vulkan_render_pass_upload_fallback (self, &op->render, render, uploader); break; @@ -261,6 +352,8 @@ gsk_vulkan_render_pass_count_vertex_data (GskVulkanRenderPass *self) switch (op->type) { case GSK_VULKAN_OP_FALLBACK: + case GSK_VULKAN_OP_FALLBACK_CLIP: + case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP: case GSK_VULKAN_OP_SURFACE: case GSK_VULKAN_OP_TEXTURE: op->render.vertex_count = gsk_vulkan_blend_pipeline_count_vertex_data (GSK_VULKAN_BLEND_PIPELINE (op->render.pipeline)); @@ -300,6 +393,8 @@ gsk_vulkan_render_pass_collect_vertex_data (GskVulkanRenderPass *self, switch (op->type) { case GSK_VULKAN_OP_FALLBACK: + case GSK_VULKAN_OP_FALLBACK_CLIP: + case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP: case GSK_VULKAN_OP_SURFACE: case GSK_VULKAN_OP_TEXTURE: { @@ -348,6 +443,8 @@ gsk_vulkan_render_pass_reserve_descriptor_sets (GskVulkanRenderPass *self, switch (op->type) { case GSK_VULKAN_OP_FALLBACK: + case GSK_VULKAN_OP_FALLBACK_CLIP: + case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP: case GSK_VULKAN_OP_SURFACE: case GSK_VULKAN_OP_TEXTURE: op->render.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, op->render.source); @@ -382,6 +479,8 @@ gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self, switch (op->type) { case GSK_VULKAN_OP_FALLBACK: + case GSK_VULKAN_OP_FALLBACK_CLIP: + case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP: case GSK_VULKAN_OP_SURFACE: case GSK_VULKAN_OP_TEXTURE: if (current_pipeline != op->render.pipeline) diff --git a/gsk/gskvulkanrenderpassprivate.h b/gsk/gskvulkanrenderpassprivate.h index 6c9cf36f46..c7afdb5a21 100644 --- a/gsk/gskvulkanrenderpassprivate.h +++ b/gsk/gskvulkanrenderpassprivate.h @@ -17,6 +17,7 @@ void gsk_vulkan_render_pass_free (GskVulk void gsk_vulkan_render_pass_add (GskVulkanRenderPass *self, GskVulkanRender *render, const graphene_matrix_t*mvp, + const graphene_rect_t *viewport, GskRenderNode *node); void gsk_vulkan_render_pass_upload (GskVulkanRenderPass *self,