From 0aaff9a07d3bdf8588cef15d502aeb4fdab22e5e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 18 Aug 2022 15:40:49 +1000 Subject: WM: optimize adding notifier duplication check Use a GSet to check for duplicate notifiers, for certain Python scripts checking for duplicate notifiers added considerable overhead. This is an alternative to D15129 with fewer chances to existing logic. --- source/blender/windowmanager/intern/wm.c | 6 ++ .../windowmanager/intern/wm_event_system.cc | 65 +++++++++++++++------- 2 files changed, 52 insertions(+), 19 deletions(-) (limited to 'source/blender/windowmanager') diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 0d74bc259f4..9b3a0d39dfa 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -15,6 +15,7 @@ #include #include +#include "BLI_ghash.h" #include "BLI_sys_types.h" #include "DNA_windowmanager_types.h" @@ -193,6 +194,7 @@ static void window_manager_blend_read_data(BlendDataReader *reader, ID *id) BLI_listbase_clear(&wm->operators); BLI_listbase_clear(&wm->paintcursors); BLI_listbase_clear(&wm->notifier_queue); + wm->notifier_queue_set = NULL; BKE_reports_init(&wm->reports, RPT_STORE); BLI_listbase_clear(&wm->keyconfigs); @@ -580,6 +582,10 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm) } BLI_freelistN(&wm->notifier_queue); + if (wm->notifier_queue_set) { + BLI_gset_free(wm->notifier_queue_set, NULL); + wm->notifier_queue_set = NULL; + } if (wm->message_bus != NULL) { WM_msgbus_destroy(wm->message_bus); diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index bff1edc40b3..6ed1cb03a77 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -27,6 +27,7 @@ #include "BLI_blenlib.h" #include "BLI_dynstr.h" +#include "BLI_ghash.h" #include "BLI_math.h" #include "BLI_timer.h" #include "BLI_utildefines.h" @@ -252,36 +253,56 @@ void wm_event_init_from_window(wmWindow *win, wmEvent *event) /** \name Notifiers & Listeners * \{ */ -static bool wm_test_duplicate_notifier(const wmWindowManager *wm, uint type, void *reference) +/** + * Hash for #wmWindowManager.notifier_queue_set, ignores `window`. + */ +static uint note_hash_for_queue_fn(const void *ptr) { - LISTBASE_FOREACH (wmNotifier *, note, &wm->notifier_queue) { - if ((note->category | note->data | note->subtype | note->action) == type && - note->reference == reference) { - return true; - } - } + const wmNotifier *note = static_cast(ptr); + return (BLI_ghashutil_ptrhash(note->reference) ^ + (note->category | note->data | note->subtype | note->action)); +} - return false; +/** + * Comparison for #wmWindowManager.notifier_queue_set + * + * \note This is not an exact equality function as the `window` is ignored. + */ +static bool note_cmp_for_queue_fn(const void *a, const void *b) +{ + const wmNotifier *note_a = static_cast(a); + const wmNotifier *note_b = static_cast(b); + return !(((note_a->category | note_a->data | note_a->subtype | note_a->action) == + (note_b->category | note_b->data | note_b->subtype | note_b->action)) && + (note_a->reference == note_b->reference)); } void WM_event_add_notifier_ex(wmWindowManager *wm, const wmWindow *win, uint type, void *reference) { - if (wm_test_duplicate_notifier(wm, type, reference)) { - return; - } + wmNotifier note_test = {nullptr}; - wmNotifier *note = MEM_cnew(__func__); + note_test.window = win; - BLI_addtail(&wm->notifier_queue, note); + note_test.category = type & NOTE_CATEGORY; + note_test.data = type & NOTE_DATA; + note_test.subtype = type & NOTE_SUBTYPE; + note_test.action = type & NOTE_ACTION; - note->window = win; + note_test.reference = reference; - note->category = type & NOTE_CATEGORY; - note->data = type & NOTE_DATA; - note->subtype = type & NOTE_SUBTYPE; - note->action = type & NOTE_ACTION; + if (wm->notifier_queue_set == nullptr) { + wm->notifier_queue_set = BLI_gset_new_ex( + note_hash_for_queue_fn, note_cmp_for_queue_fn, __func__, 1024); + } - note->reference = reference; + void **note_p; + if (BLI_gset_ensure_p_ex(wm->notifier_queue_set, ¬e_test, ¬e_p)) { + return; + } + wmNotifier *note = MEM_new(__func__); + *note = note_test; + *note_p = note; + BLI_addtail(&wm->notifier_queue, note); } /* XXX: in future, which notifiers to send to other windows? */ @@ -306,6 +327,9 @@ void WM_main_remove_notifier_reference(const void *reference) if (wm) { LISTBASE_FOREACH_MUTABLE (wmNotifier *, note, &wm->notifier_queue) { if (note->reference == reference) { + const bool removed = BLI_gset_remove(wm->notifier_queue_set, note, nullptr); + BLI_assert(removed); + UNUSED_VARS_NDEBUG(removed); /* Don't remove because this causes problems for #wm_event_do_notifiers * which may be looping on the data (deleting screens). */ wm_notifier_clear(note); @@ -554,6 +578,9 @@ void wm_event_do_notifiers(bContext *C) /* The notifiers are sent without context, to keep it clean. */ wmNotifier *note; while ((note = static_cast(BLI_pophead(&wm->notifier_queue)))) { + const bool removed = BLI_gset_remove(wm->notifier_queue_set, note, nullptr); + BLI_assert(removed); + UNUSED_VARS_NDEBUG(removed); LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { Scene *scene = WM_window_get_active_scene(win); bScreen *screen = WM_window_get_active_screen(win); -- cgit v1.2.3