3D View: re-use select id buffer for circle select on cursor-motion
authorCampbell Barton <ideasman42@gmail.com>
Mon, 20 May 2019 06:29:04 +0000 (16:29 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Mon, 20 May 2019 06:34:23 +0000 (16:34 +1000)
The new selection code was redrawing everything for each update.

Use the gestures wmGenericUserData to store the cache between
executions and ensure it's freed.

source/blender/editors/space_view3d/view3d_select.c

index 414ae392399406d48ec40745093658d08da64604..b3b4910c5257b698feb5c6d8bc488075f5ae816e 100644 (file)
@@ -232,6 +232,22 @@ static void editselect_buf_cache_free(struct EditSelectBuf_Cache *esel)
   MEM_SAFE_FREE(esel->bases);
 }
 
+static void editselect_buf_cache_free_voidp(void *esel_voidp)
+{
+  editselect_buf_cache_free(esel_voidp);
+  MEM_freeN(esel_voidp);
+}
+
+static void editselect_buf_cache_init_with_generic_userdata(wmGenericUserData *wm_userdata,
+                                                            ViewContext *vc)
+{
+  struct EditSelectBuf_Cache *esel = MEM_callocN(sizeof(*esel), __func__);
+  wm_userdata->data = esel;
+  wm_userdata->free_fn = editselect_buf_cache_free_voidp;
+  wm_userdata->use_free = true;
+  editselect_buf_cache_init(esel, vc);
+}
+
 /** \} */
 
 /* -------------------------------------------------------------------- */
@@ -775,9 +791,9 @@ static void do_lasso_select_mesh__doSelectFace(void *userData,
 }
 
 static bool do_lasso_select_mesh(ViewContext *vc,
+                                 wmGenericUserData *wm_userdata,
                                  const int mcords[][2],
                                  short moves,
-                                 struct EditSelectBuf_Cache *esel,
                                  const eSelectOp sel_op)
 {
   LassoSelectUserData data;
@@ -805,10 +821,11 @@ static bool do_lasso_select_mesh(ViewContext *vc,
 
   const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
 
+  struct EditSelectBuf_Cache *esel = wm_userdata->data;
   if (use_zbuf) {
-    /* Lazy initialize. */
-    if (esel->sel_id_ctx == NULL) {
-      editselect_buf_cache_init(esel, vc);
+    if (wm_userdata->data == NULL) {
+      editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc);
+      esel = wm_userdata->data;
       const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
       esel->select_bitmap = ED_select_buffer_bitmap_from_poly(buffer_len, mcords, moves, &rect);
     }
@@ -1091,11 +1108,11 @@ static void do_lasso_select_meshobject__doSelectVert(void *userData,
   }
 }
 static bool do_lasso_select_paintvert(ViewContext *vc,
+                                      wmGenericUserData *wm_userdata,
                                       const int mcords[][2],
                                       short moves,
                                       const eSelectOp sel_op)
 {
-  struct EditSelectBuf_Cache esel = {NULL};
   const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
   Object *ob = vc->obact;
   Mesh *me = ob->data;
@@ -1113,15 +1130,20 @@ static bool do_lasso_select_paintvert(ViewContext *vc,
 
   BLI_lasso_boundbox(&rect, mcords, moves);
 
+  struct EditSelectBuf_Cache *esel = wm_userdata->data;
   if (use_zbuf) {
-    editselect_buf_cache_init(&esel, vc);
-
-    const uint buffer_len = EDBM_select_id_context_elem_len(esel.sel_id_ctx);
-    esel.select_bitmap = ED_select_buffer_bitmap_from_poly(buffer_len, mcords, moves, &rect);
+    if (wm_userdata->data == NULL) {
+      editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc);
+      esel = wm_userdata->data;
+      const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
+      esel->select_bitmap = ED_select_buffer_bitmap_from_poly(buffer_len, mcords, moves, &rect);
+    }
   }
 
   if (use_zbuf) {
-    changed |= edbm_backbuf_check_and_select_verts_obmode(me, &esel, sel_op);
+    if (esel->select_bitmap != NULL) {
+      changed |= edbm_backbuf_check_and_select_verts_obmode(me, esel, sel_op);
+    }
   }
   else {
     LassoSelectUserData data;
@@ -1136,8 +1158,6 @@ static bool do_lasso_select_paintvert(ViewContext *vc,
     changed |= data.is_changed;
   }
 
-  editselect_buf_cache_free(&esel);
-
   if (changed) {
     if (SEL_OP_CAN_DESELECT(sel_op)) {
       BKE_mesh_mselect_validate(me);
@@ -1149,6 +1169,7 @@ static bool do_lasso_select_paintvert(ViewContext *vc,
   return changed;
 }
 static bool do_lasso_select_paintface(ViewContext *vc,
+                                      wmGenericUserData *wm_userdata,
                                       const int mcords[][2],
                                       short moves,
                                       const eSelectOp sel_op)
@@ -1169,13 +1190,16 @@ static bool do_lasso_select_paintface(ViewContext *vc,
 
   BLI_lasso_boundbox(&rect, mcords, moves);
 
-  {
-    struct EditSelectBuf_Cache esel = {NULL};
-    editselect_buf_cache_init(&esel, vc);
-    const uint buffer_len = EDBM_select_id_context_elem_len(esel.sel_id_ctx);
-    esel.select_bitmap = ED_select_buffer_bitmap_from_poly(buffer_len, mcords, moves, &rect);
-    changed |= edbm_backbuf_check_and_select_faces_obmode(me, &esel, sel_op);
-    editselect_buf_cache_free(&esel);
+  struct EditSelectBuf_Cache *esel = wm_userdata->data;
+  if (esel == NULL) {
+    editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc);
+    esel = wm_userdata->data;
+    const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
+    esel->select_bitmap = ED_select_buffer_bitmap_from_poly(buffer_len, mcords, moves, &rect);
+  }
+
+  if (esel->select_bitmap) {
+    changed |= edbm_backbuf_check_and_select_faces_obmode(me, esel, sel_op);
   }
 
   if (changed) {
@@ -1224,12 +1248,15 @@ static bool view3d_lasso_select(
   Object *ob = CTX_data_active_object(C);
   bool changed_multi = false;
 
+  wmGenericUserData wm_userdata_buf = {0};
+  wmGenericUserData *wm_userdata = &wm_userdata_buf;
+
   if (vc->obedit == NULL) { /* Object Mode */
     if (BKE_paint_select_face_test(ob)) {
-      changed_multi |= do_lasso_select_paintface(vc, mcords, moves, sel_op);
+      changed_multi |= do_lasso_select_paintface(vc, wm_userdata, mcords, moves, sel_op);
     }
     else if (BKE_paint_select_vert_test(ob)) {
-      changed_multi |= do_lasso_select_paintvert(vc, mcords, moves, sel_op);
+      changed_multi |= do_lasso_select_paintvert(vc, wm_userdata, mcords, moves, sel_op);
     }
     else if (ob &&
              (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))) {
@@ -1246,17 +1273,13 @@ static bool view3d_lasso_select(
     }
   }
   else { /* Edit Mode */
-
-    /* TODO(campbell): cache selection buffer between executions. */
-    struct EditSelectBuf_Cache esel = {NULL};
-
     FOREACH_OBJECT_IN_MODE_BEGIN (vc->view_layer, vc->v3d, ob->type, ob->mode, ob_iter) {
       ED_view3d_viewcontext_init_object(vc, ob_iter);
       bool changed = false;
 
       switch (vc->obedit->type) {
         case OB_MESH:
-          changed = do_lasso_select_mesh(vc, mcords, moves, &esel, sel_op);
+          changed = do_lasso_select_mesh(vc, wm_userdata, mcords, moves, sel_op);
           break;
         case OB_CURVE:
         case OB_SURF:
@@ -1283,9 +1306,10 @@ static bool view3d_lasso_select(
       }
     }
     FOREACH_OBJECT_IN_MODE_END;
-
-    editselect_buf_cache_free(&esel);
   }
+
+  WM_generic_user_data_free(wm_userdata);
+
   return changed_multi;
 }
 
@@ -2467,7 +2491,10 @@ static void do_paintvert_box_select__doSelectVert(void *userData,
     data->is_changed = true;
   }
 }
-static bool do_paintvert_box_select(ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
+static bool do_paintvert_box_select(ViewContext *vc,
+                                    wmGenericUserData *wm_userdata,
+                                    const rcti *rect,
+                                    const eSelectOp sel_op)
 {
   const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
 
@@ -2487,12 +2514,16 @@ static bool do_paintvert_box_select(ViewContext *vc, const rcti *rect, const eSe
     /* pass */
   }
   else if (use_zbuf) {
-    struct EditSelectBuf_Cache esel = {NULL};
-    editselect_buf_cache_init(&esel, vc);
-    const uint buffer_len = EDBM_select_id_context_elem_len(esel.sel_id_ctx);
-    esel.select_bitmap = ED_select_buffer_bitmap_from_rect(buffer_len, rect);
-    changed |= edbm_backbuf_check_and_select_verts_obmode(me, &esel, sel_op);
-    editselect_buf_cache_free(&esel);
+    struct EditSelectBuf_Cache *esel = wm_userdata->data;
+    if (wm_userdata->data == NULL) {
+      editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc);
+      esel = wm_userdata->data;
+      const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
+      esel->select_bitmap = ED_select_buffer_bitmap_from_rect(buffer_len, rect);
+    }
+    if (esel->select_bitmap != NULL) {
+      changed |= edbm_backbuf_check_and_select_verts_obmode(me, esel, sel_op);
+    }
   }
   else {
     BoxSelectUserData data;
@@ -2516,7 +2547,10 @@ static bool do_paintvert_box_select(ViewContext *vc, const rcti *rect, const eSe
   return changed;
 }
 
-static bool do_paintface_box_select(ViewContext *vc, const rcti *rect, int sel_op)
+static bool do_paintface_box_select(ViewContext *vc,
+                                    wmGenericUserData *wm_userdata,
+                                    const rcti *rect,
+                                    int sel_op)
 {
   Object *ob = vc->obact;
   Mesh *me;
@@ -2535,12 +2569,16 @@ static bool do_paintface_box_select(ViewContext *vc, const rcti *rect, int sel_o
     /* pass */
   }
   else {
-    struct EditSelectBuf_Cache esel = {NULL};
-    editselect_buf_cache_init(&esel, vc);
-    const uint buffer_len = EDBM_select_id_context_elem_len(esel.sel_id_ctx);
-    esel.select_bitmap = ED_select_buffer_bitmap_from_rect(buffer_len, rect);
-    changed |= edbm_backbuf_check_and_select_faces_obmode(me, &esel, sel_op);
-    editselect_buf_cache_free(&esel);
+    struct EditSelectBuf_Cache *esel = wm_userdata->data;
+    if (wm_userdata->data == NULL) {
+      editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc);
+      esel = wm_userdata->data;
+      const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
+      esel->select_bitmap = ED_select_buffer_bitmap_from_rect(buffer_len, rect);
+    }
+    if (esel->select_bitmap != NULL) {
+      changed |= edbm_backbuf_check_and_select_faces_obmode(me, esel, sel_op);
+    }
   }
 
   if (changed) {
@@ -2705,8 +2743,8 @@ static void do_mesh_box_select__doSelectFace(void *userData,
   }
 }
 static bool do_mesh_box_select(ViewContext *vc,
+                               wmGenericUserData *wm_userdata,
                                const rcti *rect,
-                               struct EditSelectBuf_Cache *esel,
                                const eSelectOp sel_op)
 {
   BoxSelectUserData data;
@@ -2726,12 +2764,13 @@ static bool do_mesh_box_select(ViewContext *vc,
 
   GPU_matrix_set(vc->rv3d->viewmat);
 
-  const int use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
+  const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
 
+  struct EditSelectBuf_Cache *esel = wm_userdata->data;
   if (use_zbuf) {
-    /* Lazy initialize. */
-    if (esel->sel_id_ctx == NULL) {
-      editselect_buf_cache_init(esel, vc);
+    if (wm_userdata->data == NULL) {
+      editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc);
+      esel = wm_userdata->data;
       const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
       esel->select_bitmap = ED_select_buffer_bitmap_from_rect(buffer_len, rect);
     }
@@ -3091,6 +3130,9 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
   rcti rect;
   bool changed_multi = false;
 
+  wmGenericUserData wm_userdata_buf = {0};
+  wmGenericUserData *wm_userdata = &wm_userdata_buf;
+
   view3d_operator_needs_opengl(C);
   BKE_object_update_select_id(CTX_data_main(C));
 
@@ -3101,9 +3143,6 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
   WM_operator_properties_border_to_rcti(op, &rect);
 
   if (vc.obedit) {
-
-    struct EditSelectBuf_Cache esel = {NULL};
-
     FOREACH_OBJECT_IN_MODE_BEGIN (
         vc.view_layer, vc.v3d, vc.obedit->type, vc.obedit->mode, ob_iter) {
       ED_view3d_viewcontext_init_object(&vc, ob_iter);
@@ -3112,7 +3151,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
       switch (vc.obedit->type) {
         case OB_MESH:
           vc.em = BKE_editmesh_from_object(vc.obedit);
-          changed = do_mesh_box_select(&vc, &rect, &esel, sel_op);
+          changed = do_mesh_box_select(&vc, wm_userdata, &rect, sel_op);
           if (changed) {
             DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT);
             WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
@@ -3154,8 +3193,6 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
       changed_multi |= changed;
     }
     FOREACH_OBJECT_IN_MODE_END;
-
-    editselect_buf_cache_free(&esel);
   }
   else { /* no editmode, unified for bones and objects */
     if (vc.obact && vc.obact->mode & OB_MODE_SCULPT) {
@@ -3164,10 +3201,10 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
           C, &vc, &rect, sel_op == SEL_OP_ADD ? true : false);
     }
     else if (vc.obact && BKE_paint_select_face_test(vc.obact)) {
-      changed_multi = do_paintface_box_select(&vc, &rect, sel_op);
+      changed_multi = do_paintface_box_select(&vc, wm_userdata, &rect, sel_op);
     }
     else if (vc.obact && BKE_paint_select_vert_test(vc.obact)) {
-      changed_multi = do_paintvert_box_select(&vc, &rect, sel_op);
+      changed_multi = do_paintvert_box_select(&vc, wm_userdata, &rect, sel_op);
     }
     else if (vc.obact && vc.obact->mode & OB_MODE_PARTICLE_EDIT) {
       changed_multi = PE_box_select(C, &rect, sel_op);
@@ -3180,6 +3217,8 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
     }
   }
 
+  WM_generic_user_data_free(wm_userdata);
+
   if (changed_multi) {
     return OPERATOR_FINISHED;
   }
@@ -3286,7 +3325,7 @@ static void mesh_circle_doSelectFace(void *userData,
 }
 
 static bool mesh_circle_select(ViewContext *vc,
-                               struct EditSelectBuf_Cache *esel,
+                               wmGenericUserData *wm_userdata,
                                eSelectOp sel_op,
                                const int mval[2],
                                float rad)
@@ -3308,22 +3347,26 @@ static bool mesh_circle_select(ViewContext *vc,
 
   view3d_userdata_circleselect_init(&data, vc, select, mval, rad);
 
-  const int use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
+  const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
 
   if (use_zbuf) {
-    /* Lazy initialize. */
-    if (esel->sel_id_ctx == NULL) {
-      editselect_buf_cache_init(esel, vc);
-      const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
-      esel->select_bitmap = ED_select_buffer_bitmap_from_circle(
-          buffer_len, mval, (int)(rad + 1.0f));
+    if (wm_userdata->data == NULL) {
+      editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc);
     }
   }
+  struct EditSelectBuf_Cache *esel = wm_userdata->data;
+
+  if (use_zbuf) {
+    const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
+    esel->select_bitmap = ED_select_buffer_bitmap_from_circle(buffer_len, mval, (int)(rad + 1.0f));
+  }
 
   if (ts->selectmode & SCE_SELECT_VERTEX) {
     if (use_zbuf) {
-      changed |= edbm_backbuf_check_and_select_verts(
-          esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
+      if (esel->select_bitmap != NULL) {
+        changed |= edbm_backbuf_check_and_select_verts(
+            esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
+      }
     }
     else {
       mesh_foreachScreenVert(vc, mesh_circle_doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
@@ -3332,8 +3375,10 @@ static bool mesh_circle_select(ViewContext *vc,
 
   if (ts->selectmode & SCE_SELECT_EDGE) {
     if (use_zbuf) {
-      changed |= edbm_backbuf_check_and_select_edges(
-          esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
+      if (esel->select_bitmap != NULL) {
+        changed |= edbm_backbuf_check_and_select_edges(
+            esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
+      }
     }
     else {
       mesh_foreachScreenEdge(vc, mesh_circle_doSelectEdge, &data, V3D_PROJ_TEST_CLIP_NEAR);
@@ -3342,14 +3387,23 @@ static bool mesh_circle_select(ViewContext *vc,
 
   if (ts->selectmode & SCE_SELECT_FACE) {
     if (use_zbuf) {
-      changed |= edbm_backbuf_check_and_select_faces(
-          esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
+      if (esel->select_bitmap != NULL) {
+        changed |= edbm_backbuf_check_and_select_faces(
+            esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
+      }
     }
     else {
       mesh_foreachScreenFace(vc, mesh_circle_doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
     }
   }
 
+  if (use_zbuf) {
+    if (esel->select_bitmap != NULL) {
+      MEM_freeN(esel->select_bitmap);
+      esel->select_bitmap = NULL;
+    }
+  }
+
   changed |= data.is_changed;
 
   if (changed) {
@@ -3359,6 +3413,7 @@ static bool mesh_circle_select(ViewContext *vc,
 }
 
 static bool paint_facesel_circle_select(ViewContext *vc,
+                                        wmGenericUserData *wm_userdata,
                                         const eSelectOp sel_op,
                                         const int mval[2],
                                         float rad)
@@ -3373,14 +3428,19 @@ static bool paint_facesel_circle_select(ViewContext *vc,
     changed |= paintface_deselect_all_visible(vc->C, ob, SEL_DESELECT, false);
   }
 
+  if (wm_userdata->data == NULL) {
+    editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc);
+  }
+
   {
-    /* TODO(campbell): cache selection buffer between executions. */
-    struct EditSelectBuf_Cache esel = {NULL};
-    editselect_buf_cache_init(&esel, vc);
-    const uint buffer_len = EDBM_select_id_context_elem_len(esel.sel_id_ctx);
-    esel.select_bitmap = ED_select_buffer_bitmap_from_circle(buffer_len, mval, (int)(rad + 1.0f));
-    changed |= edbm_backbuf_check_and_select_faces_obmode(me, &esel, sel_op);
-    editselect_buf_cache_free(&esel);
+    struct EditSelectBuf_Cache *esel = wm_userdata->data;
+    const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
+    esel->select_bitmap = ED_select_buffer_bitmap_from_circle(buffer_len, mval, (int)(rad + 1.0f));
+    if (esel->select_bitmap != NULL) {
+      changed |= edbm_backbuf_check_and_select_faces_obmode(me, esel, sel_op);
+      MEM_freeN(esel->select_bitmap);
+      esel->select_bitmap = NULL;
+    }
   }
 
   if (changed) {
@@ -3402,12 +3462,12 @@ static void paint_vertsel_circle_select_doSelectVert(void *userData,
   }
 }
 static bool paint_vertsel_circle_select(ViewContext *vc,
+                                        wmGenericUserData *wm_userdata,
                                         const eSelectOp sel_op,
                                         const int mval[2],
                                         float rad)
 {
   BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB));
-  struct EditSelectBuf_Cache esel = {NULL};
   const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
   Object *ob = vc->obact;
   Mesh *me = ob->data;
@@ -3422,15 +3482,20 @@ static bool paint_vertsel_circle_select(ViewContext *vc,
   const bool select = (sel_op != SEL_OP_SUB);
 
   if (use_zbuf) {
-    /* TODO(campbell): cache selection buffer between executions. */
-    editselect_buf_cache_init(&esel, vc);
-
-    const uint buffer_len = EDBM_select_id_context_elem_len(esel.sel_id_ctx);
-    esel.select_bitmap = ED_select_buffer_bitmap_from_circle(buffer_len, mval, (int)(rad + 1.0f));
+    if (wm_userdata->data == NULL) {
+      editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc);
+    }
   }
 
   if (use_zbuf) {
-    changed |= edbm_backbuf_check_and_select_verts_obmode(me, &esel, sel_op);
+    struct EditSelectBuf_Cache *esel = wm_userdata->data;
+    const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
+    esel->select_bitmap = ED_select_buffer_bitmap_from_circle(buffer_len, mval, (int)(rad + 1.0f));
+    if (esel->select_bitmap != NULL) {
+      changed |= edbm_backbuf_check_and_select_verts_obmode(me, esel, sel_op);
+      MEM_freeN(esel->select_bitmap);
+      esel->select_bitmap = NULL;
+    }
   }
   else {
     CircleSelectUserData data;
@@ -3443,8 +3508,6 @@ static bool paint_vertsel_circle_select(ViewContext *vc,
     changed |= data.is_changed;
   }
 
-  editselect_buf_cache_free(&esel);
-
   if (changed) {
     if (sel_op == SEL_OP_SUB) {
       BKE_mesh_mselect_validate(me);
@@ -3779,28 +3842,40 @@ static bool mball_circle_select(ViewContext *vc,
 /** Callbacks for circle selection in Editmode */
 
 static bool obedit_circle_select(ViewContext *vc,
-                                 struct EditSelectBuf_Cache *esel,
+                                 wmGenericUserData *wm_userdata,
                                  const eSelectOp sel_op,
                                  const int mval[2],
                                  float rad)
 {
+  bool changed = false;
   BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB));
   switch (vc->obedit->type) {
     case OB_MESH:
-      return mesh_circle_select(vc, esel, sel_op, mval, rad);
+      changed = mesh_circle_select(vc, wm_userdata, sel_op, mval, rad);
+      break;
     case OB_CURVE:
     case OB_SURF:
-      return nurbscurve_circle_select(vc, sel_op, mval, rad);
+      changed = nurbscurve_circle_select(vc, sel_op, mval, rad);
+      break;
     case OB_LATTICE:
-      return lattice_circle_select(vc, sel_op, mval, rad);
+      changed = lattice_circle_select(vc, sel_op, mval, rad);
+      break;
     case OB_ARMATURE:
-      return armature_circle_select(vc, sel_op, mval, rad);
+      changed = armature_circle_select(vc, sel_op, mval, rad);
+      break;
     case OB_MBALL:
-      return mball_circle_select(vc, sel_op, mval, rad);
+      changed = mball_circle_select(vc, sel_op, mval, rad);
+      break;
     default:
       BLI_assert(0);
-      return false;
+      break;
   }
+
+  if (changed) {
+    DEG_id_tag_update(vc->obact->data, ID_RECALC_SELECT);
+    WM_main_add_notifier(NC_GEOM | ND_SELECT, vc->obact->data);
+  }
+  return changed;
 }
 
 static bool object_circle_select(ViewContext *vc,
@@ -3849,8 +3924,13 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
   const int radius = RNA_int_get(op->ptr, "radius");
   const int mval[2] = {RNA_int_get(op->ptr, "x"), RNA_int_get(op->ptr, "y")};
 
+  /* Allow each selection type to allocate their own data thats used between executions. */
+  wmGesture *gesture = op->customdata; /* NULL when non-modal. */
+  wmGenericUserData wm_userdata_buf = {0};
+  wmGenericUserData *wm_userdata = gesture ? &gesture->user_data : &wm_userdata_buf;
+
   const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"),
-                                              WM_gesture_is_modal_first(op->customdata));
+                                              WM_gesture_is_modal_first(gesture));
 
   ED_view3d_viewcontext_init(C, &vc);
 
@@ -3859,9 +3939,9 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
 
   if (obedit || BKE_paint_select_elem_test(obact) || (obact && (obact->mode & OB_MODE_POSE))) {
     view3d_operator_needs_opengl(C);
-    BKE_object_update_select_id(CTX_data_main(C));
-
-    struct EditSelectBuf_Cache esel = {NULL};
+    if (obedit == NULL) {
+      BKE_object_update_select_id(CTX_data_main(C));
+    }
 
     FOREACH_OBJECT_IN_MODE_BEGIN (vc.view_layer, vc.v3d, obact->type, obact->mode, ob_iter) {
       ED_view3d_viewcontext_init_object(&vc, ob_iter);
@@ -3870,16 +3950,13 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
       obedit = vc.obedit;
 
       if (obedit) {
-        if (obedit_circle_select(&vc, &esel, sel_op, mval, (float)radius)) {
-          DEG_id_tag_update(obact->data, ID_RECALC_SELECT);
-          WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data);
-        }
+        obedit_circle_select(&vc, wm_userdata, sel_op, mval, (float)radius);
       }
       else if (BKE_paint_select_face_test(obact)) {
-        paint_facesel_circle_select(&vc, sel_op, mval, (float)radius);
+        paint_facesel_circle_select(&vc, wm_userdata, sel_op, mval, (float)radius);
       }
       else if (BKE_paint_select_vert_test(obact)) {
-        paint_vertsel_circle_select(&vc, sel_op, mval, (float)radius);
+        paint_vertsel_circle_select(&vc, wm_userdata, sel_op, mval, (float)radius);
       }
       else if (obact->mode & OB_MODE_POSE) {
         pose_circle_select(&vc, sel_op, mval, (float)radius);
@@ -3889,8 +3966,6 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
       }
     }
     FOREACH_OBJECT_IN_MODE_END;
-
-    editselect_buf_cache_free(&esel);
   }
   else if (obact && (obact->mode & OB_MODE_PARTICLE_EDIT)) {
     if (PE_circle_select(C, sel_op, mval, (float)radius)) {
@@ -3908,6 +3983,11 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
     }
   }
 
+  /* Otherwise this is freed by the gesture. */
+  if (wm_userdata == &wm_userdata_buf) {
+    WM_generic_user_data_free(wm_userdata);
+  }
+
   return OPERATOR_FINISHED;
 }