Refactor: Simplify undo object list creation, use C++ Vector

Replace the use of the `LIB_TAG_DOIT` flag which cwas used to
only process each object data ID once with a Set. Return the objects
or bases in a Vector. Now we only iterate over the view layers bases
once instead of three times.

Pull Request: https://projects.blender.org/blender/blender/pulls/119788
This commit is contained in:
Hans Goudey
2024-03-22 16:24:30 +01:00
committed by Hans Goudey
parent b1db1702c6
commit 3039ea02c6
9 changed files with 57 additions and 113 deletions

View File

@@ -221,15 +221,14 @@ static bool armature_undosys_step_encode(bContext *C, Main *bmain, UndoStep *us_
* outside of this list will be moved out of edit-mode when reading back undo steps. */
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer, &objects_len);
blender::Vector<Object *> objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer);
us->scene_ref.ptr = scene;
us->elems = static_cast<ArmatureUndoStep_Elem *>(
MEM_callocN(sizeof(*us->elems) * objects_len, __func__));
us->elems_len = objects_len;
MEM_callocN(sizeof(*us->elems) * objects.size(), __func__));
us->elems_len = objects.size();
for (uint i = 0; i < objects_len; i++) {
for (uint i = 0; i < objects.size(); i++) {
Object *ob = objects[i];
ArmatureUndoStep_Elem *elem = &us->elems[i];
@@ -239,7 +238,6 @@ static bool armature_undosys_step_encode(bContext *C, Main *bmain, UndoStep *us_
arm->needs_flush_to_id = 1;
us->step.data_size += elem->data.undo_size;
}
MEM_freeN(objects);
bmain->is_memfile_undo_flush_needed = true;

View File

@@ -210,15 +210,14 @@ static bool curve_undosys_step_encode(bContext *C, Main *bmain, UndoStep *us_p)
* outside of this list will be moved out of edit-mode when reading back undo steps. */
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer, &objects_len);
blender::Vector<Object *> objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer);
us->scene_ref.ptr = scene;
us->elems = static_cast<CurveUndoStep_Elem *>(
MEM_callocN(sizeof(*us->elems) * objects_len, __func__));
us->elems_len = objects_len;
MEM_callocN(sizeof(*us->elems) * objects.size(), __func__));
us->elems_len = objects.size();
for (uint i = 0; i < objects_len; i++) {
for (uint i = 0; i < objects.size(); i++) {
Object *ob = objects[i];
Curve *cu = static_cast<Curve *>(ob->data);
CurveUndoStep_Elem *elem = &us->elems[i];
@@ -228,7 +227,6 @@ static bool curve_undosys_step_encode(bContext *C, Main *bmain, UndoStep *us_p)
cu->editnurb->needs_flush_to_id = 1;
us->step.data_size += elem->data.undo_size;
}
MEM_freeN(objects);
bmain->is_memfile_undo_flush_needed = true;

View File

@@ -54,11 +54,10 @@ static bool step_encode(bContext *C, Main *bmain, UndoStep *us_p)
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_num = 0;
Object **objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer, &objects_num);
Vector<Object *> objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer);
us->scene_ref.ptr = scene;
new (&us->objects) Array<StepObject>(objects_num);
new (&us->objects) Array<StepObject>(objects.size());
threading::parallel_for(us->objects.index_range(), 8, [&](const IndexRange range) {
for (const int i : range) {
@@ -70,7 +69,6 @@ static bool step_encode(bContext *C, Main *bmain, UndoStep *us_p)
object.geometry = curves_id.geometry.wrap();
}
});
MEM_SAFE_FREE(objects);
bmain->is_memfile_undo_flush_needed = true;

View File

@@ -337,12 +337,10 @@ static bool step_encode(bContext *C, Main *bmain, UndoStep *us_p)
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_num = 0;
Object **objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer, &objects_num);
BLI_SCOPED_DEFER([&]() { MEM_SAFE_FREE(objects); })
Vector<Object *> objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer);
us->scene_ref.ptr = scene;
new (&us->objects) Array<StepObject>(objects_num);
new (&us->objects) Array<StepObject>(objects.size());
threading::parallel_for(us->objects.index_range(), 8, [&](const IndexRange range) {
for (const int64_t i : range) {

View File

@@ -10,6 +10,7 @@
#include "BLI_compiler_attrs.h"
#include "BLI_sys_types.h"
#include "BLI_vector.hh"
struct Base;
struct CLG_LogRef;
@@ -101,12 +102,10 @@ void ED_undo_object_editmode_restore_helper(Scene *scene,
uint object_array_len,
uint object_array_stride);
Object **ED_undo_editmode_objects_from_view_layer(const Scene *scene,
ViewLayer *view_layer,
uint *r_len);
Base **ED_undo_editmode_bases_from_view_layer(const Scene *scene,
ViewLayer *view_layer,
uint *r_len);
blender::Vector<Object *> ED_undo_editmode_objects_from_view_layer(const Scene *scene,
ViewLayer *view_layer);
blender::Vector<Base *> ED_undo_editmode_bases_from_view_layer(const Scene *scene,
ViewLayer *view_layer);
/**
* Ideally we won't access the stack directly,

View File

@@ -204,15 +204,14 @@ static bool lattice_undosys_step_encode(bContext *C, Main *bmain, UndoStep *us_p
* outside of this list will be moved out of edit-mode when reading back undo steps. */
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer, &objects_len);
blender::Vector<Object *> objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer);
us->scene_ref.ptr = scene;
us->elems = static_cast<LatticeUndoStep_Elem *>(
MEM_callocN(sizeof(*us->elems) * objects_len, __func__));
us->elems_len = objects_len;
MEM_callocN(sizeof(*us->elems) * objects.size(), __func__));
us->elems_len = objects.size();
for (uint i = 0; i < objects_len; i++) {
for (uint i = 0; i < objects.size(); i++) {
Object *ob = objects[i];
LatticeUndoStep_Elem *elem = &us->elems[i];
@@ -222,7 +221,6 @@ static bool lattice_undosys_step_encode(bContext *C, Main *bmain, UndoStep *us_p
lt->editlatt->needs_flush_to_id = 1;
us->step.data_size += elem->data.undo_size;
}
MEM_freeN(objects);
bmain->is_memfile_undo_flush_needed = true;

View File

@@ -928,21 +928,20 @@ static bool mesh_undosys_step_encode(bContext *C, Main *bmain, UndoStep *us_p)
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const ToolSettings *ts = scene->toolsettings;
uint objects_len = 0;
Object **objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer, &objects_len);
blender::Vector<Object *> objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer);
us->scene_ref.ptr = scene;
us->elems = static_cast<MeshUndoStep_Elem *>(
MEM_callocN(sizeof(*us->elems) * objects_len, __func__));
us->elems_len = objects_len;
MEM_callocN(sizeof(*us->elems) * objects.size(), __func__));
us->elems_len = objects.size();
UndoMesh **um_references = nullptr;
#ifdef USE_ARRAY_STORE
um_references = mesh_undostep_reference_elems_from_objects(objects, objects_len);
um_references = mesh_undostep_reference_elems_from_objects(objects.data(), objects.size());
#endif
for (uint i = 0; i < objects_len; i++) {
for (uint i = 0; i < objects.size(); i++) {
Object *ob = objects[i];
MeshUndoStep_Elem *elem = &us->elems[i];
@@ -962,7 +961,6 @@ static bool mesh_undosys_step_encode(bContext *C, Main *bmain, UndoStep *us_p)
elem->data.mesh.id.session_uid = mesh->id.session_uid;
#endif
}
MEM_freeN(objects);
if (um_references != nullptr) {
MEM_freeN(um_references);

View File

@@ -160,15 +160,14 @@ static bool mball_undosys_step_encode(bContext *C, Main *bmain, UndoStep *us_p)
* outside of this list will be moved out of edit-mode when reading back undo steps. */
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer, &objects_len);
blender::Vector<Object *> objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer);
us->scene_ref.ptr = scene;
us->elems = static_cast<MBallUndoStep_Elem *>(
MEM_callocN(sizeof(*us->elems) * objects_len, __func__));
us->elems_len = objects_len;
MEM_callocN(sizeof(*us->elems) * objects.size(), __func__));
us->elems_len = objects.size();
for (uint i = 0; i < objects_len; i++) {
for (uint i = 0; i < objects.size(); i++) {
Object *ob = objects[i];
MBallUndoStep_Elem *elem = &us->elems[i];
@@ -178,7 +177,6 @@ static bool mball_undosys_step_encode(bContext *C, Main *bmain, UndoStep *us_p)
mb->needs_flush_to_id = 1;
us->step.data_size += elem->data.undo_size;
}
MEM_freeN(objects);
bmain->is_memfile_undo_flush_needed = true;

View File

@@ -54,6 +54,9 @@
#include "UI_interface.hh"
#include "UI_resources.hh"
using blender::Set;
using blender::Vector;
/** We only need this locally. */
static CLG_LogRef LOG = {"ed.undo"};
@@ -852,12 +855,11 @@ void ED_undo_object_editmode_restore_helper(Scene *scene,
uint object_array_stride)
{
Main *bmain = G_MAIN;
uint bases_len = 0;
/* Don't request unique data because we want to de-select objects when exiting edit-mode
* for that to be done on all objects we can't skip ones that share data. */
Base **bases = ED_undo_editmode_bases_from_view_layer(scene, view_layer, &bases_len);
for (uint i = 0; i < bases_len; i++) {
((ID *)bases[i]->object->data)->tag |= LIB_TAG_DOIT;
Vector<Base *> bases = ED_undo_editmode_bases_from_view_layer(scene, view_layer);
for (Base *base : bases) {
((ID *)base->object->data)->tag |= LIB_TAG_DOIT;
}
Object **ob_p = object_array;
for (uint i = 0; i < object_array_len;
@@ -867,16 +869,15 @@ void ED_undo_object_editmode_restore_helper(Scene *scene,
ED_object_editmode_enter_ex(bmain, scene, obedit, EM_NO_CONTEXT);
((ID *)obedit->data)->tag &= ~LIB_TAG_DOIT;
}
for (uint i = 0; i < bases_len; i++) {
ID *id = static_cast<ID *>(bases[i]->object->data);
for (Base *base : bases) {
ID *id = static_cast<ID *>(base->object->data);
if (id->tag & LIB_TAG_DOIT) {
ED_object_editmode_exit_ex(bmain, scene, bases[i]->object, EM_FREEDATA);
ED_object_editmode_exit_ex(bmain, scene, base->object, EM_FREEDATA);
/* Ideally we would know the selection state it was before entering edit-mode,
* for now follow the convention of having them unselected when exiting the mode. */
ED_object_base_select(bases[i], BA_DESELECT);
ED_object_base_select(base, BA_DESELECT);
}
}
MEM_freeN(bases);
}
/** \} */
@@ -891,49 +892,17 @@ void ED_undo_object_editmode_restore_helper(Scene *scene,
* and local collections may be used.
* \{ */
static int undo_editmode_objects_from_view_layer_prepare(const Scene *scene,
ViewLayer *view_layer,
Object *obact)
{
const short object_type = obact->type;
BKE_view_layer_synced_ensure(scene, view_layer);
ListBase *object_bases = BKE_view_layer_object_bases_get(view_layer);
LISTBASE_FOREACH (Base *, base, object_bases) {
Object *ob = base->object;
if ((ob->type == object_type) && (ob->mode & OB_MODE_EDIT)) {
ID *id = static_cast<ID *>(ob->data);
id->tag &= ~LIB_TAG_DOIT;
}
}
int len = 0;
LISTBASE_FOREACH (Base *, base, object_bases) {
Object *ob = base->object;
if ((ob->type == object_type) && (ob->mode & OB_MODE_EDIT)) {
ID *id = static_cast<ID *>(ob->data);
if ((id->tag & LIB_TAG_DOIT) == 0) {
len += 1;
id->tag |= LIB_TAG_DOIT;
}
}
}
return len;
}
Object **ED_undo_editmode_objects_from_view_layer(const Scene *scene,
ViewLayer *view_layer,
uint *r_len)
Vector<Object *> ED_undo_editmode_objects_from_view_layer(const Scene *scene,
ViewLayer *view_layer)
{
BKE_view_layer_synced_ensure(scene, view_layer);
Base *baseact = BKE_view_layer_active_base_get(view_layer);
if ((baseact == nullptr) || (baseact->object->mode & OB_MODE_EDIT) == 0) {
return static_cast<Object **>(MEM_mallocN(0, __func__));
return {};
}
const int len = undo_editmode_objects_from_view_layer_prepare(
scene, view_layer, baseact->object);
Set<const ID *> object_data;
const short object_type = baseact->object->type;
int i = 0;
Object **objects = static_cast<Object **>(MEM_malloc_arrayN(len, sizeof(*objects), __func__));
Vector<Object *> objects(object_data.size());
/* Base iteration, starting with the active-base to ensure it's the first item in the array.
* Looping over the active-base twice is OK as the tag check prevents it being handled twice. */
for (Base *base = baseact,
@@ -943,33 +912,26 @@ Object **ED_undo_editmode_objects_from_view_layer(const Scene *scene,
{
Object *ob = base->object;
if ((ob->type == object_type) && (ob->mode & OB_MODE_EDIT)) {
ID *id = static_cast<ID *>(ob->data);
if (id->tag & LIB_TAG_DOIT) {
objects[i++] = ob;
id->tag &= ~LIB_TAG_DOIT;
if (object_data.add(static_cast<const ID *>(ob->data))) {
objects.append(ob);
}
}
}
BLI_assert(i == len);
BLI_assert(object_data.is_empty());
BLI_assert(objects[0] == baseact->object);
*r_len = len;
return objects;
}
Base **ED_undo_editmode_bases_from_view_layer(const Scene *scene,
ViewLayer *view_layer,
uint *r_len)
Vector<Base *> ED_undo_editmode_bases_from_view_layer(const Scene *scene, ViewLayer *view_layer)
{
BKE_view_layer_synced_ensure(scene, view_layer);
Base *baseact = BKE_view_layer_active_base_get(view_layer);
if ((baseact == nullptr) || (baseact->object->mode & OB_MODE_EDIT) == 0) {
return static_cast<Base **>(MEM_mallocN(0, __func__));
return {};
}
const int len = undo_editmode_objects_from_view_layer_prepare(
scene, view_layer, baseact->object);
Set<const ID *> object_data;
const short object_type = baseact->object->type;
int i = 0;
Base **base_array = static_cast<Base **>(MEM_malloc_arrayN(len, sizeof(*base_array), __func__));
Vector<Base *> bases;
/* Base iteration, starting with the active-base to ensure it's the first item in the array.
* Looping over the active-base twice is OK as the tag check prevents it being handled twice. */
for (Base *base = BKE_view_layer_active_base_get(view_layer),
@@ -979,18 +941,15 @@ Base **ED_undo_editmode_bases_from_view_layer(const Scene *scene,
{
Object *ob = base->object;
if ((ob->type == object_type) && (ob->mode & OB_MODE_EDIT)) {
ID *id = static_cast<ID *>(ob->data);
if (id->tag & LIB_TAG_DOIT) {
base_array[i++] = base;
id->tag &= ~LIB_TAG_DOIT;
if (object_data.add(static_cast<const ID *>(ob->data))) {
bases.append(base);
}
}
}
BLI_assert(i == len);
BLI_assert(base_array[0] == baseact);
*r_len = len;
return base_array;
BLI_assert(object_data.is_empty());
BLI_assert(bases[0] == baseact);
return bases;
}
/** \} */