[Bf-blender-cvs] [0562c2f31a2] undo-experiments: Merge branch 'id-ensure-unique-memory-address' into undo-experiments

Bastien Montagne noreply at git.blender.org
Fri Feb 14 15:35:32 CET 2020


Commit: 0562c2f31a22fac9e192de47466a45de38443189
Author: Bastien Montagne
Date:   Fri Feb 14 14:25:27 2020 +0100
Branches: undo-experiments
https://developer.blender.org/rB0562c2f31a22fac9e192de47466a45de38443189

Merge branch 'id-ensure-unique-memory-address' into undo-experiments

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



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

diff --cc source/blender/blenloader/intern/readfile.c
index 7240062c2dc,4231871bbb0..02423e674bd
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@@ -9806,84 -9669,6 +9815,82 @@@ static BHead *read_userdef(BlendFileDat
  /** \name Read File (Internal)
   * \{ */
  
- static int blo_undo_merge_remap_colliding_pointers_cb(void *user_data,
-                                                       ID *UNUSED(id_self),
-                                                       ID **id_pointer,
-                                                       int UNUSED(cb_flag))
++static int blo_undo_merge_remap_colliding_pointers_cb(LibraryIDLinkCallbackData *cb_data)
 +{
-   void **old_new_oldpointers = user_data;
++  ID **id_pointer = cb_data->id_pointer;
++  void **old_new_oldpointers = cb_data->user_data;
 +  if (*id_pointer == old_new_oldpointers[0]) {
 +    *id_pointer = old_new_oldpointers[1];
 +  }
 +
 +  return IDWALK_RET_NOP;
 +}
 +
 +static void blo_undo_merge_and_fix_collisions_in_libmaps(FileData *fd)
 +{
 +  for (int i = fd->libmap->nentries; i-- > 0;) {
 +    OldNew *entry = &fd->libmap->entries[i];
 +    const void *oldp = entry->oldp;
 +
 +    OldNew *reused_entry = oldnewmap_lookup_entry(fd->libmap_undo_reused, oldp);
 +    /* If both entries are pointing to same new ID of same type (stored in `nr`), there is no
 +     * collision. */
 +    if (reused_entry != NULL && reused_entry->newp != entry->newp &&
 +        reused_entry->nr != entry->nr) {
 +      const void *orig_oldp = oldp;
 +      /* We have a pointer collision, find a new free oldp value.
 +       * Note that we can only check libmap_undo_reused here as well, in the (rather unlikely) case
 +       * we'd reach another used oldp value in a libmap item not yet processed, it will simply be
 +       * detected as used/colliding too when its turn comes. */
 +      for (oldp = (const char *)oldp + 1;
 +           oldnewmap_lookup_entry(fd->libmap_undo_reused, oldp) != NULL;
 +           oldp = (const char *)oldp + 1)
 +        ;
 +
 +      printf(
 +          "%s: found same old pointer value used by both re-used IDs and newly read-from-memfile "
 +          "IDs, remapped the laters from %p to %p\n",
 +          __func__,
 +          orig_oldp,
 +          oldp);
 +      printf("%s: Nothing to worry about, unless this leads to a crash!\n", __func__);
 +
 +      /* Now we need to remap all orig_oldp pointers in local main to the new oldp value. */
 +      /* Note that we are not using regular ID remapping API, since we only care about pointer
 +       * values here, current bmain is still totally unlinked so all the extra processing would be
 +       * useless - and lead to crash. */
 +      Main *bmain = fd->mainlist->first;
 +      ID *id;
 +      const void *old_new_oldpointers[2] = {orig_oldp, oldp};
 +      FOREACH_MAIN_ID_BEGIN (bmain, id) {
 +        if (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) {
 +          /* We only want to update values of old pointers in data read from memfile, not the one
 +           * re-used from the old bmain. */
 +          continue;
 +        }
 +        BKE_library_foreach_ID_link(bmain,
 +                                    id,
 +                                    blo_undo_merge_remap_colliding_pointers_cb,
 +                                    old_new_oldpointers,
 +                                    IDWALK_NOP);
 +      }
 +      FOREACH_MAIN_ID_END;
 +    }
 +
 +    /* Inserting into libmap_undo_reused, which will be our final, merged libmap.
 +     * We are doing this in that direction (and not from libmap_undo_reused to libmap) because this
 +     * helps us reducing the changes to reused IDs (the fewer of their pointers we have to remap to
 +     * a new address, the less likely they are to be detected as 'changed' in later undo/redo
 +     * steps). */
 +    oldnewmap_insert(fd->libmap_undo_reused, oldp, entry->newp, entry->nr);
 +  }
 +
 +  /* Finally, we put our final, merged and collision-free libmap at its rightful place. */
 +  oldnewmap_free(fd->libmap);
 +  fd->libmap = fd->libmap_undo_reused;
 +  fd->libmap_undo_reused = NULL;
 +}
 +
  BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
  {
    BHead *bhead = blo_bhead_first(fd);
diff --cc source/blender/editors/undo/memfile_undo.c
index a492d6fb323,a5f30409aa6..20fb99702b7
--- a/source/blender/editors/undo/memfile_undo.c
+++ b/source/blender/editors/undo/memfile_undo.c
@@@ -101,93 -88,9 +101,92 @@@ static bool memfile_undosys_step_encode
    return true;
  }
  
- static int memfile_undosys_step_id_reused_cb(void *user_data,
-                                              ID *id_self,
-                                              ID **id_pointer,
-                                              int UNUSED(cb_flag))
 -static void memfile_undosys_step_decode(
 -    struct bContext *C, struct Main *bmain, UndoStep *us_p, int UNUSED(dir), bool UNUSED(is_final))
++static int memfile_undosys_step_id_reused_cb(LibraryIDLinkCallbackData *cb_data)
 +{
++  ID *id_self = cb_data->id_self;
++  ID **id_pointer = cb_data->id_pointer;
 +  BLI_assert((id_self->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0);
-   Main *bmain = user_data;
++  Main *bmain = cb_data->user_data;
 +
 +  ID *id = *id_pointer;
 +  if (id != NULL && id->lib == NULL && (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) == 0) {
 +    bool do_stop_iter = true;
 +    if (GS(id_self->name) == ID_OB) {
 +      Object *ob_self = (Object *)id_self;
 +      if (ob_self->type == OB_ARMATURE) {
 +        if (ob_self->data == id) {
 +          BLI_assert(GS(id->name) == ID_AR);
 +          if (ob_self->pose != NULL) {
 +            /* We have a changed/re-read armature used by an unchanged armature object: our beloved
 +             * Bone pointers from the object's pose need their usual special treatment. */
 +            ob_self->pose->flag |= POSE_RECALC;
 +          }
 +        }
 +        else {
 +          /* Cannot stop iteration until we checked ob_self->data pointer... */
 +          do_stop_iter = false;
 +        }
 +      }
 +    }
 +
 +    /* In case an old, re-used ID is using a newly read data-block (i.e. one of its ID pointers got
 +     * updated), we have to tell the depsgraph about it. */
 +    DEG_id_tag_update_ex(bmain, id_self, ID_RECALC_COPY_ON_WRITE);
 +    return do_stop_iter ? IDWALK_RET_STOP_ITER : IDWALK_RET_NOP;
 +  }
 +
 +  return IDWALK_RET_NOP;
 +}
 +
 +static void memfile_undosys_step_decode(struct bContext *C,
 +                                        struct Main *bmain,
 +                                        UndoStep *us_p,
 +                                        int undo_direction,
 +                                        bool UNUSED(is_final))
  {
 +  BLI_assert(undo_direction != 0);
 +
 +  bool use_old_bmain_data = true;
 +
 +  if (undo_direction > 0) {
 +    /* Redo case.
 +     * The only time we should have to force a complete redo is when current step is tagged as a
 +     * redo barrier.
 +     * If previous step was not a memfile one should not matter here, current data in old bmain
 +     * should still always be valid for unchanged dtat-blocks. */
 +    if (us_p->use_old_bmain_data == false) {
 +      use_old_bmain_data = false;
 +    }
 +  }
 +  else {
 +    /* Undo case.
 +     * Here we do not care whether current step is an undo barrier, since we are comming from 'the
 +     * future' we can still re-use old data. However, if *next* undo step (i.e. the one immédiately
 +     * in the future, the one we are comming from) is a barrier, then we have to force a complete
 +     * undo.
 +     * Likewise, if next step (the one we are comming from) was a non-memfile one, there is no
 +     * guarantee that current bmain data actually reflects the status of unchanged datablocks in
 +     * memfile, since changes might have been flushed to current bmain data without triggering any
 +     * memfile step storage (typical common case e.g. when using edit modes).
 +     */
 +    UndoStep *us_next = us_p->next;
 +    if (us_next != NULL) {
 +      if (us_next->use_old_bmain_data == false) {
 +        use_old_bmain_data = false;
 +      }
 +      if (us_next->type != BKE_UNDOSYS_TYPE_MEMFILE) {
 +        use_old_bmain_data = false;
 +      }
 +    }
 +  }
 +
 +  /* Extract depsgraphs from current bmain (which may be freed during undo step reading),
 +   * and store them for re-use. */
 +  GHash *depsgraphs = NULL;
 +  if (use_old_bmain_data) {
 +    depsgraphs = BKE_scene_undo_depsgraphs_extract(bmain);
 +  }
 +
    ED_editors_exit(bmain, false);
  
    MemFileUndoStep *us = (MemFileUndoStep *)us_p;



More information about the Bf-blender-cvs mailing list