From 05e90b1cdee42b3465c660a8cfa46fb9aaea0de0 Mon Sep 17 00:00:00 2001 From: Jehan Date: Wed, 3 Sep 2025 18:17:57 +0200 Subject: [PATCH] app: optimize the detection of filters with aux input. As reported by Liam, apparently for someone with a lot of filters, these node creations just to check the aux input may add up noticeable time on boot (it may also depend on the OS too? I didn't have any noticeable delay personally). Unfortunately we cannot know the presence of an aux input just with class introspection because they can be registered by the op at runtime. This optimization is therefore twofold: * Since we know which filters have an aux input among all the ones with hardcoded actions, we also hardcode this data. It means we only do the actual check on non-hardcoded operations (third-party filters but also GEGL operations which are not specifically listed in our code). * I only do these checks once, stored by name in a hash table, because filters_actions_setup() is actually run several times (for different menus). This should improve startup time a lot for people who experienced this delay. --- app/actions/actions.c | 20 ++++++++++ app/actions/actions.h | 3 ++ app/actions/filters-actions.c | 72 +++++++++++++++++------------------ 3 files changed, 59 insertions(+), 36 deletions(-) diff --git a/app/actions/actions.c b/app/actions/actions.c index 97ea4d0d17..7240d45ccb 100644 --- a/app/actions/actions.c +++ b/app/actions/actions.c @@ -104,6 +104,7 @@ /* global variables */ GimpActionFactory *global_action_factory = NULL; +GHashTable *aux_filter_hash_table = NULL; /* private variables */ @@ -270,6 +271,8 @@ actions_init (Gimp *gimp) action_groups[i].icon_name, action_groups[i].setup_func, action_groups[i].update_func); + + aux_filter_hash_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); } void @@ -280,6 +283,23 @@ actions_exit (Gimp *gimp) g_return_if_fail (global_action_factory->gimp == gimp); g_clear_object (&global_action_factory); + g_hash_table_unref (aux_filter_hash_table); +} + +/* XXX Temporary code to store the list of filter operations with an + * "aux" input. This won't be necessary anymore once these filters can + * be applied non-destructively too in the future. + */ +void +actions_filter_set_aux (const gchar *action_name) +{ + g_hash_table_add (aux_filter_hash_table, (gpointer) g_strdup (action_name)); +} + +gboolean +actions_filter_get_aux (const gchar *action_name) +{ + return g_hash_table_lookup (aux_filter_hash_table, action_name) != NULL; } Gimp * diff --git a/app/actions/actions.h b/app/actions/actions.h index 494a03e1bb..89f05f709b 100644 --- a/app/actions/actions.h +++ b/app/actions/actions.h @@ -24,6 +24,9 @@ extern GimpActionFactory *global_action_factory; void actions_init (Gimp *gimp); void actions_exit (Gimp *gimp); +void actions_filter_set_aux (const gchar *action_name); +gboolean actions_filter_get_aux (const gchar *action_name); + Gimp * action_data_get_gimp (gpointer data); GimpContext * action_data_get_context (gpointer data); GimpImage * action_data_get_image (gpointer data); diff --git a/app/actions/filters-actions.c b/app/actions/filters-actions.c index bcc617a52f..e0f3e8e6de 100644 --- a/app/actions/filters-actions.c +++ b/app/actions/filters-actions.c @@ -754,10 +754,10 @@ static const GimpEnumActionEntry filters_repeat_actions[] = void filters_actions_setup (GimpActionGroup *group) { + static gboolean first_setup = TRUE; GimpProcedureActionEntry *entries; gint n_entries; gint i; - GList *actions; GList *op_classes; GList *iter; GStrvBuilder *gegl_actions; @@ -783,6 +783,21 @@ filters_actions_setup (GimpActionGroup *group) filters_actions_set_tooltips (group, filters_interactive_actions, G_N_ELEMENTS (filters_interactive_actions)); + /* XXX Hardcoded list to prevent expensive node/graph creation of a + * well-known list of operations. + * This whole code will disappear when we'll support filters with aux + * input non-destructively anyway. + */ + if (first_setup) + { + actions_filter_set_aux ("filters-variable-blur"); + actions_filter_set_aux ("filters-oilify"); + actions_filter_set_aux ("filters-lens-blur"); + actions_filter_set_aux ("filters-gaussian-blur-selective"); + actions_filter_set_aux ("filters-displace"); + actions_filter_set_aux ("filters-bump-map"); + } + gegl_actions = g_strv_builder_new (); op_classes = gimp_gegl_get_op_classes (TRUE); @@ -868,6 +883,23 @@ filters_actions_setup (GimpActionGroup *group) g_free (short_label); } + /* Identify third-party filters based on operations with an + * auxiliary pad in first setup because of slowness on Windows. + * See #14781. + */ + if (first_setup) + { + GeglNode *node = gegl_node_new (); + + gegl_node_set (node, + "operation", op_class->name, + NULL); + + if (gegl_node_has_pad (node, "aux")) + actions_filter_set_aux (action_name); + + g_clear_object (&node); + } g_strv_builder_add (gegl_actions, action_name); g_free (label); @@ -882,40 +914,6 @@ filters_actions_setup (GimpActionGroup *group) g_strv_builder_unref (gegl_actions); g_list_free (op_classes); - /* Identify all filters based on operations with an auxiliary pad in - * setup because of slowness on Windows. See #14781. - */ - actions = gimp_action_group_list_actions (group); - for (iter = actions; iter; iter = iter->next) - { - GimpAction *action = iter->data; - const gchar *action_name; - - action_name = gimp_action_get_name (action); - - if (! filters_is_non_interactive (action_name) && GIMP_IS_STRING_ACTION (action)) - { - const gchar *opname; - - opname = GIMP_STRING_ACTION (action)->value; - - if (opname != NULL && g_strcmp0 (opname, "gegl:gegl") != 0 && gegl_has_operation (opname)) - { - GeglNode *node = gegl_node_new (); - - gegl_node_set (node, - "operation", opname, - NULL); - - if (gegl_node_has_pad (node, "aux")) - g_object_set_data (G_OBJECT (action), "filters-action-has-pad", GINT_TO_POINTER (TRUE)); - - g_clear_object (&node); - } - } - } - g_list_free (actions); - gimp_action_group_add_enum_actions (group, "filters-action", filters_repeat_actions, G_N_ELEMENTS (filters_repeat_actions), @@ -951,6 +949,8 @@ filters_actions_setup (GimpActionGroup *group) group, 0); filters_actions_history_changed (group->gimp, group); + + first_setup = FALSE; } void @@ -1054,7 +1054,7 @@ filters_actions_update (GimpActionGroup *group, * types of layers where filters can only be applied * non-destructively. */ - sensitive = ! GPOINTER_TO_INT (g_object_get_data (G_OBJECT (action), "filters-action-has-pad")); + sensitive = ! actions_filter_get_aux (action_name); SET_SENSITIVE (gimp_action_get_name (action), sensitive); }