diff options
author | Georges Basile Stavracas Neto <georges.stavracas@gmail.com> | 2023-03-31 14:00:33 -0300 |
---|---|---|
committer | Georges Basile Stavracas Neto <georges.stavracas@gmail.com> | 2023-03-31 14:00:33 -0300 |
commit | b1a8c0e686a092012013b31314a49964a0c258bc (patch) | |
tree | 8f3d9c400d95623fde7c3f2baf0acf3b055519cd | |
parent | bd29bed7856f1f69c2120678c91071ddd936c223 (diff) | |
download | gtk+-b1a8c0e686a092012013b31314a49964a0c258bc.tar.gz |
gsk/vulkan/renderpass: Factor out node implementations
This is mostly a cosmetic change, and the goal is twofold:
1. Make it easier to spot unimplemented render node types; and
2. Prepare for a small rework
The implementation for each node now lives in specific functions,
like the GL renderer; unlike the GL renderer, however, we use a
node type vtable to map GskRenderNodeType → implementation. Render
node without an implementation map to NULL, and use the fallback
implementation. Render nodes that fail any check and return FALSE
also use fallback implementation.
-rw-r--r-- | gsk/vulkan/gskvulkanrenderpass.c | 968 |
1 files changed, 621 insertions, 347 deletions
diff --git a/gsk/vulkan/gskvulkanrenderpass.c b/gsk/vulkan/gskvulkanrenderpass.c index f5b1f989e5..04b9e3d8d9 100644 --- a/gsk/vulkan/gskvulkanrenderpass.c +++ b/gsk/vulkan/gskvulkanrenderpass.c @@ -236,410 +236,684 @@ gsk_vulkan_render_pass_free (GskVulkanRenderPass *self) #define FALLBACK(...) G_STMT_START { \ GSK_RENDERER_DEBUG (gsk_vulkan_render_get_renderer (render), FALLBACK, __VA_ARGS__); \ - goto fallback; \ + return FALSE; \ }G_STMT_END static void gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self, GskVulkanRender *render, const GskVulkanPushConstants *constants, - GskRenderNode *node) + GskRenderNode *node); + +static inline gboolean +gsk_vulkan_render_pass_add_fallback_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) { GskVulkanOp op = { .render.node = node }; - GskVulkanPipelineType pipeline_type; - switch (gsk_render_node_get_node_type (node)) + switch (constants->clip.type) { - case GSK_NOT_A_RENDER_NODE: - g_assert_not_reached (); - return; - case GSK_GL_SHADER_NODE: - case GSK_SHADOW_NODE: - case GSK_RADIAL_GRADIENT_NODE: - case GSK_REPEATING_RADIAL_GRADIENT_NODE: - case GSK_CONIC_GRADIENT_NODE: - default: - FALLBACK ("Unsupported node '%s'", g_type_name_from_instance ((GTypeInstance *) node)); + 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, &constants->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, &constants->clip.rect); + break; + case GSK_VULKAN_CLIP_ALL_CLIPPED: + default: + g_assert_not_reached (); + return FALSE; + } - case GSK_REPEAT_NODE: - if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) - pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE; - else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) - pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP; - else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) - pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP_ROUNDED; - else - FALLBACK ("Repeat nodes can't deal with clip type %u", constants->clip.type); - op.type = GSK_VULKAN_OP_REPEAT; - op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); - g_array_append_val (self->render_ops, op); - return; + op.render.pipeline = gsk_vulkan_render_get_pipeline (render, GSK_VULKAN_PIPELINE_TEXTURE); + g_array_append_val (self->render_ops, op); - case GSK_BLEND_NODE: - if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) - pipeline_type = GSK_VULKAN_PIPELINE_BLEND_MODE; - else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) - pipeline_type = GSK_VULKAN_PIPELINE_BLEND_MODE_CLIP; - else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) - pipeline_type = GSK_VULKAN_PIPELINE_BLEND_MODE_CLIP_ROUNDED; - else - FALLBACK ("Blend nodes can't deal with clip type %u", constants->clip.type); - op.type = GSK_VULKAN_OP_BLEND_MODE; - op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); - g_array_append_val (self->render_ops, op); - return; + return TRUE; +} - case GSK_CROSS_FADE_NODE: - if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) - pipeline_type = GSK_VULKAN_PIPELINE_CROSS_FADE; - else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) - pipeline_type = GSK_VULKAN_PIPELINE_CROSS_FADE_CLIP; - else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) - pipeline_type = GSK_VULKAN_PIPELINE_CROSS_FADE_CLIP_ROUNDED; - else - FALLBACK ("Cross fade nodes can't deal with clip type %u", constants->clip.type); - op.type = GSK_VULKAN_OP_CROSS_FADE; - op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); - g_array_append_val (self->render_ops, op); - return; +static inline gboolean +gsk_vulkan_render_pass_implode (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + g_assert_not_reached (); + return TRUE; +} - case GSK_INSET_SHADOW_NODE: - if (gsk_inset_shadow_node_get_blur_radius (node) > 0) - FALLBACK ("Blur support not implemented for inset shadows"); - else if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) - pipeline_type = GSK_VULKAN_PIPELINE_INSET_SHADOW; - else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) - pipeline_type = GSK_VULKAN_PIPELINE_INSET_SHADOW_CLIP; - else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) - pipeline_type = GSK_VULKAN_PIPELINE_INSET_SHADOW_CLIP_ROUNDED; - else - FALLBACK ("Inset shadow nodes can't deal with clip type %u", constants->clip.type); - op.type = GSK_VULKAN_OP_INSET_SHADOW; - op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); - g_array_append_val (self->render_ops, op); - return; +static inline gboolean +gsk_vulkan_render_pass_add_container_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + for (guint 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)); - case GSK_OUTSET_SHADOW_NODE: - if (gsk_outset_shadow_node_get_blur_radius (node) > 0) - FALLBACK ("Blur support not implemented for outset shadows"); - else if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) - pipeline_type = GSK_VULKAN_PIPELINE_OUTSET_SHADOW; - else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) - pipeline_type = GSK_VULKAN_PIPELINE_OUTSET_SHADOW_CLIP; - else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) - pipeline_type = GSK_VULKAN_PIPELINE_OUTSET_SHADOW_CLIP_ROUNDED; - else - FALLBACK ("Outset shadow nodes can't deal with clip type %u", constants->clip.type); - op.type = GSK_VULKAN_OP_OUTSET_SHADOW; - op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); - g_array_append_val (self->render_ops, op); - return; + return TRUE; +} - case GSK_CAIRO_NODE: - if (gsk_cairo_node_get_surface (node) == NULL) - return; - /* We're using recording surfaces, so drawing them to an image - * surface and uploading them is the right thing. - * But that's exactly what the fallback code does. - */ - goto fallback; +static inline gboolean +gsk_vulkan_render_pass_add_cairo_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + /* We're using recording surfaces, so drawing them to an image + * surface and uploading them is the right thing. + * But that's exactly what the fallback code does. + */ + if (gsk_cairo_node_get_surface (node) != NULL) + return gsk_vulkan_render_pass_add_fallback_node (self, render, constants, node); + + return TRUE; +} - case GSK_TEXT_NODE: - { - const PangoFont *font = gsk_text_node_get_font (node); - const PangoGlyphInfo *glyphs = gsk_text_node_get_glyphs (node, NULL); - guint num_glyphs = gsk_text_node_get_num_glyphs (node); - gboolean has_color_glyphs = gsk_text_node_has_color_glyphs (node); - int i; - guint count; - guint texture_index; - int x_position; - GskVulkanRenderer *renderer = GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)); - - if (has_color_glyphs) - { - if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) - pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT; - else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) - pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP; - else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) - pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP_ROUNDED; - else - FALLBACK ("Text nodes can't deal with clip type %u", constants->clip.type); - op.type = GSK_VULKAN_OP_COLOR_TEXT; - } - else - { - if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) - pipeline_type = GSK_VULKAN_PIPELINE_TEXT; - else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) - pipeline_type = GSK_VULKAN_PIPELINE_TEXT_CLIP; - else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) - pipeline_type = GSK_VULKAN_PIPELINE_TEXT_CLIP_ROUNDED; - else - FALLBACK ("Text nodes can't deal with clip type %u", constants->clip.type); - op.type = GSK_VULKAN_OP_TEXT; - } - op.text.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); +static inline gboolean +gsk_vulkan_render_pass_add_color_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + GskVulkanOp op = { + .render.node = node + }; + GskVulkanPipelineType pipeline_type; - op.text.start_glyph = 0; - op.text.texture_index = G_MAXUINT; - op.text.scale = self->scale_factor; + if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) + pipeline_type = GSK_VULKAN_PIPELINE_COLOR; + else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) + pipeline_type = GSK_VULKAN_PIPELINE_COLOR_CLIP; + else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) + pipeline_type = GSK_VULKAN_PIPELINE_COLOR_CLIP_ROUNDED; + else + FALLBACK ("Color nodes can't deal with clip type %u", constants->clip.type); - x_position = 0; - for (i = 0, count = 0; i < num_glyphs; i++) - { - const PangoGlyphInfo *gi = &glyphs[i]; - - texture_index = gsk_vulkan_renderer_cache_glyph (renderer, - (PangoFont *)font, - gi->glyph, - x_position + gi->geometry.x_offset, - gi->geometry.y_offset, - op.text.scale); - if (op.text.texture_index == G_MAXUINT) - op.text.texture_index = texture_index; - if (texture_index != op.text.texture_index) - { - op.text.num_glyphs = count; - - g_array_append_val (self->render_ops, op); - - count = 1; - op.text.start_glyph = i; - op.text.texture_index = texture_index; - } - else - count++; - - x_position += gi->geometry.width; - } + op.type = GSK_VULKAN_OP_COLOR; + op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); + g_array_append_val (self->render_ops, op); - if (op.text.texture_index != G_MAXUINT && count != 0) - { - op.text.num_glyphs = count; - g_array_append_val (self->render_ops, op); - } + return TRUE; +} - return; - } +static inline gboolean +gsk_vulkan_render_pass_add_repeating_linear_gradient_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + GskVulkanPipelineType pipeline_type; + GskVulkanOp op = { + .render.node = node + }; - case GSK_TEXTURE_NODE: - if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) - pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE; - else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) - pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP; - else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) - pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP_ROUNDED; - else - FALLBACK ("Texture nodes can't deal with clip type %u", constants->clip.type); - op.type = GSK_VULKAN_OP_TEXTURE; - op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); - g_array_append_val (self->render_ops, op); - return; + if (gsk_linear_gradient_node_get_n_color_stops (node) > GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_MAX_COLOR_STOPS) + FALLBACK ("Linear gradient with %zu color stops, hardcoded limit is %u", + gsk_linear_gradient_node_get_n_color_stops (node), + GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_MAX_COLOR_STOPS); + + if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) + pipeline_type = GSK_VULKAN_PIPELINE_LINEAR_GRADIENT; + else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) + pipeline_type = GSK_VULKAN_PIPELINE_LINEAR_GRADIENT_CLIP; + else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) + pipeline_type = GSK_VULKAN_PIPELINE_LINEAR_GRADIENT_CLIP_ROUNDED; + else + FALLBACK ("Linear gradient nodes can't deal with clip type %u", constants->clip.type); - case GSK_TEXTURE_SCALE_NODE: - goto fallback; + op.type = GSK_VULKAN_OP_LINEAR_GRADIENT; + op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); + g_array_append_val (self->render_ops, op); - case GSK_COLOR_NODE: - if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) - pipeline_type = GSK_VULKAN_PIPELINE_COLOR; - else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) - pipeline_type = GSK_VULKAN_PIPELINE_COLOR_CLIP; - else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) - pipeline_type = GSK_VULKAN_PIPELINE_COLOR_CLIP_ROUNDED; - else - FALLBACK ("Color nodes can't deal with clip type %u", constants->clip.type); - op.type = GSK_VULKAN_OP_COLOR; - op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); - g_array_append_val (self->render_ops, op); - return; - - case GSK_LINEAR_GRADIENT_NODE: - case GSK_REPEATING_LINEAR_GRADIENT_NODE: - if (gsk_linear_gradient_node_get_n_color_stops (node) > GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_MAX_COLOR_STOPS) - FALLBACK ("Linear gradient with %zu color stops, hardcoded limit is %u", - gsk_linear_gradient_node_get_n_color_stops (node), - GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_MAX_COLOR_STOPS); - if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) - pipeline_type = GSK_VULKAN_PIPELINE_LINEAR_GRADIENT; - else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) - pipeline_type = GSK_VULKAN_PIPELINE_LINEAR_GRADIENT_CLIP; - else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) - pipeline_type = GSK_VULKAN_PIPELINE_LINEAR_GRADIENT_CLIP_ROUNDED; - else - FALLBACK ("Linear gradient nodes can't deal with clip type %u", constants->clip.type); - op.type = GSK_VULKAN_OP_LINEAR_GRADIENT; - op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); - g_array_append_val (self->render_ops, op); - return; + return TRUE; +} - case GSK_OPACITY_NODE: - if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) - pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX; - else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) - pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP; - else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) - pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP_ROUNDED; - else - FALLBACK ("Opacity nodes can't deal with clip type %u", constants->clip.type); - op.type = GSK_VULKAN_OP_OPACITY; - op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); - g_array_append_val (self->render_ops, op); - return; +static inline gboolean +gsk_vulkan_render_pass_add_border_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + GskVulkanPipelineType pipeline_type; + GskVulkanOp op = { + .render.node = node + }; - case GSK_BLUR_NODE: - if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) - pipeline_type = GSK_VULKAN_PIPELINE_BLUR; - else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) - pipeline_type = GSK_VULKAN_PIPELINE_BLUR_CLIP; - else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) - pipeline_type = GSK_VULKAN_PIPELINE_BLUR_CLIP_ROUNDED; - else - FALLBACK ("Blur nodes can't deal with clip type %u", constants->clip.type); - op.type = GSK_VULKAN_OP_BLUR; - op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); - g_array_append_val (self->render_ops, op); - return; + if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) + pipeline_type = GSK_VULKAN_PIPELINE_BORDER; + else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) + pipeline_type = GSK_VULKAN_PIPELINE_BORDER_CLIP; + else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) + pipeline_type = GSK_VULKAN_PIPELINE_BORDER_CLIP_ROUNDED; + else + FALLBACK ("Border nodes can't deal with clip type %u", constants->clip.type); + + op.type = GSK_VULKAN_OP_BORDER; + op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); + g_array_append_val (self->render_ops, op); + + return TRUE; +} + +static inline gboolean +gsk_vulkan_render_pass_add_texture_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + GskVulkanPipelineType pipeline_type; + GskVulkanOp op = { + .render.node = node + }; + + if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) + pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE; + else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) + pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP; + else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) + pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP_ROUNDED; + else + FALLBACK ("Texture nodes can't deal with clip type %u", constants->clip.type); + + op.type = GSK_VULKAN_OP_TEXTURE; + op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); + g_array_append_val (self->render_ops, op); + + return TRUE; +} + +static inline gboolean +gsk_vulkan_render_pass_add_inset_shadow_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + GskVulkanPipelineType pipeline_type; + GskVulkanOp op = { + .render.node = node + }; + + if (gsk_inset_shadow_node_get_blur_radius (node) > 0) + FALLBACK ("Blur support not implemented for inset shadows"); + else if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) + pipeline_type = GSK_VULKAN_PIPELINE_INSET_SHADOW; + else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) + pipeline_type = GSK_VULKAN_PIPELINE_INSET_SHADOW_CLIP; + else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) + pipeline_type = GSK_VULKAN_PIPELINE_INSET_SHADOW_CLIP_ROUNDED; + else + FALLBACK ("Inset shadow nodes can't deal with clip type %u", constants->clip.type); + + op.type = GSK_VULKAN_OP_INSET_SHADOW; + op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); + g_array_append_val (self->render_ops, op); + + return TRUE; +} + +static inline gboolean +gsk_vulkan_render_pass_add_outset_shadow_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + GskVulkanPipelineType pipeline_type; + GskVulkanOp op = { + .render.node = node + }; + + if (gsk_outset_shadow_node_get_blur_radius (node) > 0) + FALLBACK ("Blur support not implemented for outset shadows"); + else if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) + pipeline_type = GSK_VULKAN_PIPELINE_OUTSET_SHADOW; + else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) + pipeline_type = GSK_VULKAN_PIPELINE_OUTSET_SHADOW_CLIP; + else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) + pipeline_type = GSK_VULKAN_PIPELINE_OUTSET_SHADOW_CLIP_ROUNDED; + else + FALLBACK ("Outset shadow nodes can't deal with clip type %u", constants->clip.type); + + op.type = GSK_VULKAN_OP_OUTSET_SHADOW; + op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); + g_array_append_val (self->render_ops, op); + + return TRUE; +} + +static inline gboolean +gsk_vulkan_render_pass_add_transform_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + GskVulkanOp op = { + .render.node = node + }; + graphene_matrix_t transform, mv; + GskRenderNode *child; + +#if 0 + if (!gsk_vulkan_clip_contains_rect (clip, &node->bounds)) + FALLBACK ("Transform nodes can't deal with clip type %u\n", clip->type); +#endif + + child = gsk_transform_node_get_child (node); + gsk_transform_to_matrix (gsk_transform_node_get_transform (node), &transform); + graphene_matrix_init_from_matrix (&mv, &self->mv); + graphene_matrix_multiply (&transform, &mv, &self->mv); + if (!gsk_vulkan_push_constants_transform (&op.constants.constants, constants, gsk_transform_node_get_transform (node), &child->bounds)) + FALLBACK ("Transform nodes can't deal with clip type %u", constants->clip.type); + op.type = GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS; + g_array_append_val (self->render_ops, op); + + gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, child); + gsk_vulkan_push_constants_init_copy (&op.constants.constants, constants); + graphene_matrix_init_from_matrix (&self->mv, &mv); + g_array_append_val (self->render_ops, op); + + return TRUE; +} + +static inline gboolean +gsk_vulkan_render_pass_add_opacity_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + GskVulkanPipelineType pipeline_type; + GskVulkanOp op = { + .render.node = node + }; + + if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) + pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX; + else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) + pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP; + else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) + pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP_ROUNDED; + else + FALLBACK ("Opacity nodes can't deal with clip type %u", constants->clip.type); + + op.type = GSK_VULKAN_OP_OPACITY; + op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); + g_array_append_val (self->render_ops, op); + + return TRUE; +} + +static inline gboolean +gsk_vulkan_render_pass_add_color_matrix_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + GskVulkanPipelineType pipeline_type; + GskVulkanOp op = { + .render.node = node + }; + + if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) + pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX; + else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) + pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP; + else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) + pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP_ROUNDED; + else + FALLBACK ("Color matrix nodes can't deal with clip type %u", constants->clip.type); + + op.type = GSK_VULKAN_OP_COLOR_MATRIX; + op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); + g_array_append_val (self->render_ops, op); + + return TRUE; +} + +static inline gboolean +gsk_vulkan_render_pass_add_clip_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + GskVulkanOp op = { + .render.node = node + }; + + if (!gsk_vulkan_push_constants_intersect_rect (&op.constants.constants, constants, gsk_clip_node_get_clip (node))) + FALLBACK ("Failed to find intersection between clip of type %u and rectangle", constants->clip.type); + + if (op.constants.constants.clip.type == GSK_VULKAN_CLIP_ALL_CLIPPED) + return TRUE; + + op.type = GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS; + g_array_append_val (self->render_ops, op); + + gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, gsk_clip_node_get_child (node)); + + gsk_vulkan_push_constants_init_copy (&op.constants.constants, constants); + g_array_append_val (self->render_ops, op); + + return TRUE; +} + +static inline gboolean +gsk_vulkan_render_pass_add_rounded_clip_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + GskVulkanOp op = { + .render.node = node + }; + + if (!gsk_vulkan_push_constants_intersect_rounded (&op.constants.constants, + constants, + gsk_rounded_clip_node_get_clip (node))) + FALLBACK ("Failed to find intersection between clip of type %u and rounded rectangle", constants->clip.type); + + if (op.constants.constants.clip.type == GSK_VULKAN_CLIP_ALL_CLIPPED) + return TRUE; + + op.type = GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS; + g_array_append_val (self->render_ops, op); + + gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, gsk_rounded_clip_node_get_child (node)); + + gsk_vulkan_push_constants_init_copy (&op.constants.constants, constants); + g_array_append_val (self->render_ops, op); + + return TRUE; +} + +static inline gboolean +gsk_vulkan_render_pass_add_repeat_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + GskVulkanPipelineType pipeline_type; + GskVulkanOp op = { + .render.node = node + }; + + if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) + pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE; + else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) + pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP; + else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) + pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP_ROUNDED; + else + FALLBACK ("Repeat nodes can't deal with clip type %u", constants->clip.type); + + op.type = GSK_VULKAN_OP_REPEAT; + op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); + g_array_append_val (self->render_ops, op); + + return TRUE; +} + +static inline gboolean +gsk_vulkan_render_pass_add_blend_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + GskVulkanPipelineType pipeline_type; + GskVulkanOp op = { + .render.node = node + }; + + if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) + pipeline_type = GSK_VULKAN_PIPELINE_BLEND_MODE; + else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) + pipeline_type = GSK_VULKAN_PIPELINE_BLEND_MODE_CLIP; + else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) + pipeline_type = GSK_VULKAN_PIPELINE_BLEND_MODE_CLIP_ROUNDED; + else + FALLBACK ("Blend nodes can't deal with clip type %u", constants->clip.type); + + op.type = GSK_VULKAN_OP_BLEND_MODE; + op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); + g_array_append_val (self->render_ops, op); + + return TRUE; +} + +static inline gboolean +gsk_vulkan_render_pass_add_cross_fade_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + GskVulkanOp op = { + .render.node = node + }; + GskVulkanPipelineType pipeline_type; + + if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) + pipeline_type = GSK_VULKAN_PIPELINE_CROSS_FADE; + else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) + pipeline_type = GSK_VULKAN_PIPELINE_CROSS_FADE_CLIP; + else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) + pipeline_type = GSK_VULKAN_PIPELINE_CROSS_FADE_CLIP_ROUNDED; + else + FALLBACK ("Cross fade nodes can't deal with clip type %u", constants->clip.type); - case GSK_MASK_NODE: - goto fallback; + op.type = GSK_VULKAN_OP_CROSS_FADE; + op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); + g_array_append_val (self->render_ops, op); - case GSK_COLOR_MATRIX_NODE: + return TRUE; +} + +static inline gboolean +gsk_vulkan_render_pass_add_text_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + GskVulkanOp op = { + .render.node = node + }; + GskVulkanPipelineType pipeline_type; + const PangoGlyphInfo *glyphs; + GskVulkanRenderer *renderer; + const PangoFont *font; + guint texture_index; + guint num_glyphs; + guint count; + int x_position; + int i; + + renderer = GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)); + num_glyphs = gsk_text_node_get_num_glyphs (node); + glyphs = gsk_text_node_get_glyphs (node, NULL); + font = gsk_text_node_get_font (node); + + if (gsk_text_node_has_color_glyphs (node)) + { if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) - pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX; + pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT; else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) - pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP; + pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP; else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) - pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP_ROUNDED; + pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP_ROUNDED; else - FALLBACK ("Color matrix nodes can't deal with clip type %u", constants->clip.type); - op.type = GSK_VULKAN_OP_COLOR_MATRIX; - op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); - g_array_append_val (self->render_ops, op); - return; - - case GSK_BORDER_NODE: + FALLBACK ("Text nodes can't deal with clip type %u", constants->clip.type); + op.type = GSK_VULKAN_OP_COLOR_TEXT; + } + else + { if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) - pipeline_type = GSK_VULKAN_PIPELINE_BORDER; + pipeline_type = GSK_VULKAN_PIPELINE_TEXT; else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) - pipeline_type = GSK_VULKAN_PIPELINE_BORDER_CLIP; + pipeline_type = GSK_VULKAN_PIPELINE_TEXT_CLIP; else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) - pipeline_type = GSK_VULKAN_PIPELINE_BORDER_CLIP_ROUNDED; + pipeline_type = GSK_VULKAN_PIPELINE_TEXT_CLIP_ROUNDED; else - FALLBACK ("Border nodes can't deal with clip type %u", constants->clip.type); - op.type = GSK_VULKAN_OP_BORDER; - op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); - g_array_append_val (self->render_ops, op); - return; + FALLBACK ("Text nodes can't deal with clip type %u", constants->clip.type); + op.type = GSK_VULKAN_OP_TEXT; + } + op.text.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); - case GSK_CONTAINER_NODE: - { - guint i; + op.text.start_glyph = 0; + op.text.texture_index = G_MAXUINT; + op.text.scale = self->scale_factor; - 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)); - } - } - return; + x_position = 0; + for (i = 0, count = 0; i < num_glyphs; i++) + { + const PangoGlyphInfo *gi = &glyphs[i]; + + texture_index = gsk_vulkan_renderer_cache_glyph (renderer, + (PangoFont *)font, + gi->glyph, + x_position + gi->geometry.x_offset, + gi->geometry.y_offset, + op.text.scale); + if (op.text.texture_index == G_MAXUINT) + op.text.texture_index = texture_index; + if (texture_index != op.text.texture_index) + { + op.text.num_glyphs = count; - case GSK_DEBUG_NODE: - gsk_vulkan_render_pass_add_node (self, render, constants, gsk_debug_node_get_child (node)); - return; + g_array_append_val (self->render_ops, op); - case GSK_TRANSFORM_NODE: - { - graphene_matrix_t transform, mv; - GskRenderNode *child; + count = 1; + op.text.start_glyph = i; + op.text.texture_index = texture_index; + } + else + count++; -#if 0 - if (!gsk_vulkan_clip_contains_rect (clip, &node->bounds)) - FALLBACK ("Transform nodes can't deal with clip type %u\n", clip->type); -#endif + x_position += gi->geometry.width; + } - child = gsk_transform_node_get_child (node); - gsk_transform_to_matrix (gsk_transform_node_get_transform (node), &transform); - graphene_matrix_init_from_matrix (&mv, &self->mv); - graphene_matrix_multiply (&transform, &mv, &self->mv); - if (!gsk_vulkan_push_constants_transform (&op.constants.constants, constants, gsk_transform_node_get_transform (node), &child->bounds)) - FALLBACK ("Transform nodes can't deal with clip type %u", constants->clip.type); - op.type = GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS; - g_array_append_val (self->render_ops, op); - - gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, child); - gsk_vulkan_push_constants_init_copy (&op.constants.constants, constants); - graphene_matrix_init_from_matrix (&self->mv, &mv); - g_array_append_val (self->render_ops, op); - } - return; + if (op.text.texture_index != G_MAXUINT && count != 0) + { + op.text.num_glyphs = count; + g_array_append_val (self->render_ops, op); + } - case GSK_CLIP_NODE: - { - if (!gsk_vulkan_push_constants_intersect_rect (&op.constants.constants, constants, gsk_clip_node_get_clip (node))) - FALLBACK ("Failed to find intersection between clip of type %u and rectangle", constants->clip.type); - if (op.constants.constants.clip.type == GSK_VULKAN_CLIP_ALL_CLIPPED) - return; + return TRUE; +} - op.type = GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS; - g_array_append_val (self->render_ops, op); +static inline gboolean +gsk_vulkan_render_pass_add_blur_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + GskVulkanPipelineType pipeline_type; + GskVulkanOp op = { + .render.node = node + }; - gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, gsk_clip_node_get_child (node)); + if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) + pipeline_type = GSK_VULKAN_PIPELINE_BLUR; + else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) + pipeline_type = GSK_VULKAN_PIPELINE_BLUR_CLIP; + else if (constants->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR) + pipeline_type = GSK_VULKAN_PIPELINE_BLUR_CLIP_ROUNDED; + else + FALLBACK ("Blur nodes can't deal with clip type %u", constants->clip.type); - gsk_vulkan_push_constants_init_copy (&op.constants.constants, constants); - g_array_append_val (self->render_ops, op); - } - return; + op.type = GSK_VULKAN_OP_BLUR; + op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type); + g_array_append_val (self->render_ops, op); - case GSK_ROUNDED_CLIP_NODE: - { - if (!gsk_vulkan_push_constants_intersect_rounded (&op.constants.constants, - constants, - gsk_rounded_clip_node_get_clip (node))) - FALLBACK ("Failed to find intersection between clip of type %u and rounded rectangle", constants->clip.type); - if (op.constants.constants.clip.type == GSK_VULKAN_CLIP_ALL_CLIPPED) - return; + return TRUE; +} - op.type = GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS; - g_array_append_val (self->render_ops, op); +static inline gboolean +gsk_vulkan_render_pass_add_debug_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + gsk_vulkan_render_pass_add_node (self, render, constants, gsk_debug_node_get_child (node)); + return TRUE; +} - gsk_vulkan_render_pass_add_node (self, render, &op.constants.constants, gsk_rounded_clip_node_get_child (node)); +#undef FALLBACK - gsk_vulkan_push_constants_init_copy (&op.constants.constants, constants); - g_array_append_val (self->render_ops, op); - } - return; - } +typedef gboolean (*GskVulkanRenderPassNodeFunc) (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node); + +#define N_RENDER_NODES (GSK_MASK_NODE + 1) + +/* TODO: implement remaining nodes */ +static const GskVulkanRenderPassNodeFunc nodes_vtable[N_RENDER_NODES] = { + [GSK_NOT_A_RENDER_NODE] = gsk_vulkan_render_pass_implode, + [GSK_CONTAINER_NODE] = gsk_vulkan_render_pass_add_container_node, + [GSK_CAIRO_NODE] = gsk_vulkan_render_pass_add_cairo_node, + [GSK_COLOR_NODE] = gsk_vulkan_render_pass_add_color_node, + [GSK_LINEAR_GRADIENT_NODE] = gsk_vulkan_render_pass_add_repeating_linear_gradient_node, + [GSK_REPEATING_LINEAR_GRADIENT_NODE] = gsk_vulkan_render_pass_add_repeating_linear_gradient_node, + [GSK_RADIAL_GRADIENT_NODE] = NULL, + [GSK_REPEATING_RADIAL_GRADIENT_NODE] = NULL, + [GSK_CONIC_GRADIENT_NODE] = NULL, + [GSK_BORDER_NODE] = gsk_vulkan_render_pass_add_border_node, + [GSK_TEXTURE_NODE] = gsk_vulkan_render_pass_add_texture_node, + [GSK_INSET_SHADOW_NODE] = gsk_vulkan_render_pass_add_inset_shadow_node, + [GSK_OUTSET_SHADOW_NODE] = gsk_vulkan_render_pass_add_outset_shadow_node, + [GSK_TRANSFORM_NODE] = gsk_vulkan_render_pass_add_transform_node, + [GSK_OPACITY_NODE] = gsk_vulkan_render_pass_add_opacity_node, + [GSK_COLOR_MATRIX_NODE] = gsk_vulkan_render_pass_add_color_matrix_node, + [GSK_REPEAT_NODE] = gsk_vulkan_render_pass_add_repeat_node, + [GSK_CLIP_NODE] = gsk_vulkan_render_pass_add_clip_node, + [GSK_ROUNDED_CLIP_NODE] = gsk_vulkan_render_pass_add_rounded_clip_node, + [GSK_SHADOW_NODE] = NULL, + [GSK_BLEND_NODE] = gsk_vulkan_render_pass_add_blend_node, + [GSK_CROSS_FADE_NODE] = gsk_vulkan_render_pass_add_cross_fade_node, + [GSK_TEXT_NODE] = gsk_vulkan_render_pass_add_text_node, + [GSK_BLUR_NODE] = gsk_vulkan_render_pass_add_blur_node, + [GSK_DEBUG_NODE] = gsk_vulkan_render_pass_add_debug_node, + [GSK_GL_SHADER_NODE] = NULL, + [GSK_TEXTURE_SCALE_NODE] = NULL, + [GSK_MASK_NODE] = NULL, +}; - g_assert_not_reached (); - return; +static void +gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self, + GskVulkanRender *render, + const GskVulkanPushConstants *constants, + GskRenderNode *node) +{ + GskVulkanRenderPassNodeFunc node_func; + GskRenderNodeType node_type; + gboolean fallback = FALSE; -fallback: - switch (constants->clip.type) + node_type = gsk_render_node_get_node_type (node); + node_func = nodes_vtable[node_type]; + + if (node_func) { - 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, &constants->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, &constants->clip.rect); - break; - case GSK_VULKAN_CLIP_ALL_CLIPPED: - default: - g_assert_not_reached (); - return; + if (!node_func (self, render, constants, node)) + fallback = TRUE; } - op.render.pipeline = gsk_vulkan_render_get_pipeline (render, GSK_VULKAN_PIPELINE_TEXTURE); - g_array_append_val (self->render_ops, op); + else + { + GSK_RENDERER_DEBUG (gsk_vulkan_render_get_renderer (render), + FALLBACK, "Unsupported node '%s'", + g_type_name_from_instance ((GTypeInstance *) node)); + fallback = TRUE; + } + + if (fallback) + gsk_vulkan_render_pass_add_fallback_node (self, render, constants, node); } -#undef FALLBACK void gsk_vulkan_render_pass_add (GskVulkanRenderPass *self, |