[Bf-blender-cvs] [0aaff9a07d3] master: WM: optimize adding notifier duplication check

Campbell Barton noreply at git.blender.org
Thu Aug 18 07:55:56 CEST 2022


Commit: 0aaff9a07d3bdf8588cef15d502aeb4fdab22e5e
Author: Campbell Barton
Date:   Thu Aug 18 15:40:49 2022 +1000
Branches: master
https://developer.blender.org/rB0aaff9a07d3bdf8588cef15d502aeb4fdab22e5e

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.

===================================================================

M	source/blender/makesdna/DNA_windowmanager_types.h
M	source/blender/windowmanager/intern/wm.c
M	source/blender/windowmanager/intern/wm_event_system.cc

===================================================================

diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h
index 116ea4821cb..2586e13da39 100644
--- a/source/blender/makesdna/DNA_windowmanager_types.h
+++ b/source/blender/makesdna/DNA_windowmanager_types.h
@@ -151,6 +151,11 @@ typedef struct wmWindowManager {
 
   /** Refresh/redraw #wmNotifier structs. */
   ListBase notifier_queue;
+  /**
+   * For duplicate detection.
+   * \note keep in sync with `notifier_queue` adding/removing elements must also update this set.
+   */
+  struct GSet *notifier_queue_set;
 
   /** Information and error reports. */
   struct ReportList reports;
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 <stddef.h>
 #include <string.h>
 
+#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<const wmNotifier *>(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<const wmNotifier *>(a);
+  const wmNotifier *note_b = static_cast<const wmNotifier *>(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<wmNotifier>(__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, &note_test, &note_p)) {
+    return;
+  }
+  wmNotifier *note = MEM_new<wmNotifier>(__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<wmNotifier *>(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);



More information about the Bf-blender-cvs mailing list