Sculpt: Move some mesh geometry storage out of per-node undo data
This storage of the entire mesh for topology-changing operations doesn't need to be stored in every single undo node. Move it to the data for the undo step instead. This decreases the size of undo nodes from 3880 to 1816 bytes. That saves about 4MB of memory on a single stroke affecting most of a 6 million vertex mesh. I didn't change anything BMesh related here because it's trickier to get right and not quite as encapsulated. Moving all BMesh undo data out of `undo::Node` would be a good step though, because only one undo node is used anyway.
This commit is contained in:
@@ -193,16 +193,6 @@ struct Node {
|
||||
BMLogEntry *bm_entry;
|
||||
bool applied;
|
||||
|
||||
/* Geometry modification operations.
|
||||
*
|
||||
* Original geometry is stored before some modification is run and is used to restore state of
|
||||
* the object when undoing the operation
|
||||
*
|
||||
* Modified geometry is stored after the modification and is used to redo the modification. */
|
||||
bool geometry_clear_pbvh;
|
||||
undo::NodeGeometry geometry_original;
|
||||
undo::NodeGeometry geometry_modified;
|
||||
|
||||
/* Geometry at the bmesh enter moment. */
|
||||
undo::NodeGeometry geometry_bmesh_enter;
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "iostream"
|
||||
|
||||
#include "DNA_key_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
@@ -140,6 +141,21 @@ struct StepData {
|
||||
float3 pivot_pos;
|
||||
float4 pivot_rot;
|
||||
|
||||
/* Geometry modification operations.
|
||||
*
|
||||
* Original geometry is stored before some modification is run and is used to restore state of
|
||||
* the object when undoing the operation
|
||||
*
|
||||
* Modified geometry is stored after the modification and is used to redo the modification. */
|
||||
bool geometry_clear_pbvh;
|
||||
undo::NodeGeometry geometry_original;
|
||||
undo::NodeGeometry geometry_modified;
|
||||
|
||||
/* Geometry at the bmesh enter moment. */
|
||||
undo::NodeGeometry geometry_bmesh_enter;
|
||||
|
||||
bool applied;
|
||||
|
||||
std::mutex nodes_mutex;
|
||||
|
||||
/**
|
||||
@@ -859,19 +875,19 @@ static void geometry_free_data(NodeGeometry *geometry)
|
||||
&geometry->face_offsets_sharing_info);
|
||||
}
|
||||
|
||||
static void restore_geometry(Node &unode, Object &object)
|
||||
static void restore_geometry(StepData &step_data, Object &object)
|
||||
{
|
||||
if (unode.geometry_clear_pbvh) {
|
||||
if (step_data.geometry_clear_pbvh) {
|
||||
SCULPT_pbvh_clear(object);
|
||||
}
|
||||
|
||||
if (unode.applied) {
|
||||
restore_geometry_data(&unode.geometry_modified, object);
|
||||
unode.applied = false;
|
||||
if (step_data.applied) {
|
||||
restore_geometry_data(&step_data.geometry_modified, object);
|
||||
step_data.applied = false;
|
||||
}
|
||||
else {
|
||||
restore_geometry_data(&unode.geometry_original, object);
|
||||
unode.applied = true;
|
||||
restore_geometry_data(&step_data.geometry_original, object);
|
||||
step_data.applied = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -983,74 +999,77 @@ static void restore_list(bContext *C, Depsgraph *depsgraph, StepData &step_data)
|
||||
Vector<bool> modified_verts_color;
|
||||
Vector<bool> modified_faces_face_set;
|
||||
Vector<bool> modified_grids;
|
||||
for (std::unique_ptr<Node> &unode : step_data.nodes) {
|
||||
/* Check if undo data matches current data well enough to continue. */
|
||||
if (unode->mesh_verts_num) {
|
||||
if (ss.totvert != unode->mesh_verts_num) {
|
||||
continue;
|
||||
if (step_data.type == Type::Geometry) {
|
||||
restore_geometry(step_data, object);
|
||||
changed_all_geometry = true;
|
||||
BKE_sculpt_update_object_for_edit(depsgraph, &object, false);
|
||||
}
|
||||
else {
|
||||
for (std::unique_ptr<Node> &unode : step_data.nodes) {
|
||||
/* Check if undo data matches current data well enough to continue. */
|
||||
if (unode->mesh_verts_num) {
|
||||
if (ss.totvert != unode->mesh_verts_num) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (unode->mesh_grids_num && subdiv_ccg != nullptr) {
|
||||
if ((subdiv_ccg->grids.size() != unode->mesh_grids_num) ||
|
||||
(subdiv_ccg->grid_size != unode->grid_size))
|
||||
{
|
||||
continue;
|
||||
else if (unode->mesh_grids_num && subdiv_ccg != nullptr) {
|
||||
if ((subdiv_ccg->grids.size() != unode->mesh_grids_num) ||
|
||||
(subdiv_ccg->grid_size != unode->grid_size))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
use_multires_undo = true;
|
||||
}
|
||||
|
||||
use_multires_undo = true;
|
||||
}
|
||||
|
||||
switch (step_data.type) {
|
||||
case Type::None:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
case Type::Position:
|
||||
modified_verts_position.resize(ss.totvert, false);
|
||||
if (restore_coords(C, object, depsgraph, step_data, *unode, modified_verts_position)) {
|
||||
changed_position = true;
|
||||
}
|
||||
break;
|
||||
case Type::HideVert:
|
||||
modified_verts_hide.resize(ss.totvert, false);
|
||||
if (restore_hidden(object, *unode, modified_verts_hide)) {
|
||||
changed_hide_vert = true;
|
||||
}
|
||||
break;
|
||||
case Type::HideFace:
|
||||
modified_faces_hide.resize(ss.totfaces, false);
|
||||
if (restore_hidden_face(object, *unode, modified_faces_hide)) {
|
||||
changed_hide_face = true;
|
||||
}
|
||||
break;
|
||||
case Type::Mask:
|
||||
modified_verts_mask.resize(ss.totvert, false);
|
||||
if (restore_mask(object, *unode, modified_verts_mask)) {
|
||||
changed_mask = true;
|
||||
}
|
||||
break;
|
||||
case Type::FaceSet:
|
||||
modified_faces_face_set.resize(ss.totfaces, false);
|
||||
if (restore_face_sets(object, *unode, modified_faces_face_set)) {
|
||||
changed_face_sets = true;
|
||||
}
|
||||
break;
|
||||
case Type::Color:
|
||||
modified_verts_color.resize(ss.totvert, false);
|
||||
if (restore_color(object, *unode, modified_verts_color)) {
|
||||
changed_color = true;
|
||||
}
|
||||
break;
|
||||
case Type::Geometry:
|
||||
restore_geometry(*unode, object);
|
||||
changed_all_geometry = true;
|
||||
BKE_sculpt_update_object_for_edit(depsgraph, &object, false);
|
||||
break;
|
||||
|
||||
case Type::DyntopoBegin:
|
||||
case Type::DyntopoEnd:
|
||||
case Type::DyntopoSymmetrize:
|
||||
BLI_assert_msg(0, "Dynamic topology should've already been handled");
|
||||
break;
|
||||
switch (step_data.type) {
|
||||
case Type::None:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
case Type::Position:
|
||||
modified_verts_position.resize(ss.totvert, false);
|
||||
if (restore_coords(C, object, depsgraph, step_data, *unode, modified_verts_position)) {
|
||||
changed_position = true;
|
||||
}
|
||||
break;
|
||||
case Type::HideVert:
|
||||
modified_verts_hide.resize(ss.totvert, false);
|
||||
if (restore_hidden(object, *unode, modified_verts_hide)) {
|
||||
changed_hide_vert = true;
|
||||
}
|
||||
break;
|
||||
case Type::HideFace:
|
||||
modified_faces_hide.resize(ss.totfaces, false);
|
||||
if (restore_hidden_face(object, *unode, modified_faces_hide)) {
|
||||
changed_hide_face = true;
|
||||
}
|
||||
break;
|
||||
case Type::Mask:
|
||||
modified_verts_mask.resize(ss.totvert, false);
|
||||
if (restore_mask(object, *unode, modified_verts_mask)) {
|
||||
changed_mask = true;
|
||||
}
|
||||
break;
|
||||
case Type::FaceSet:
|
||||
modified_faces_face_set.resize(ss.totfaces, false);
|
||||
if (restore_face_sets(object, *unode, modified_faces_face_set)) {
|
||||
changed_face_sets = true;
|
||||
}
|
||||
break;
|
||||
case Type::Color:
|
||||
modified_verts_color.resize(ss.totvert, false);
|
||||
if (restore_color(object, *unode, modified_verts_color)) {
|
||||
changed_color = true;
|
||||
}
|
||||
break;
|
||||
case Type::Geometry:
|
||||
case Type::DyntopoBegin:
|
||||
case Type::DyntopoEnd:
|
||||
case Type::DyntopoSymmetrize:
|
||||
/* Handled elsewhere. */
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1141,9 +1160,9 @@ static void restore_list(bContext *C, Depsgraph *depsgraph, StepData &step_data)
|
||||
|
||||
static void free_step_data(StepData &step_data)
|
||||
{
|
||||
geometry_free_data(&step_data.geometry_original);
|
||||
geometry_free_data(&step_data.geometry_modified);
|
||||
for (std::unique_ptr<Node> &unode : step_data.nodes) {
|
||||
geometry_free_data(&unode->geometry_original);
|
||||
geometry_free_data(&unode->geometry_modified);
|
||||
geometry_free_data(&unode->geometry_bmesh_enter);
|
||||
if (unode->bm_entry) {
|
||||
BM_log_entry_drop(unode->bm_entry);
|
||||
@@ -1316,43 +1335,28 @@ static void store_color(const Object &object, Node &unode)
|
||||
}
|
||||
}
|
||||
|
||||
static NodeGeometry *geometry_get(Node &unode)
|
||||
static NodeGeometry *geometry_get(StepData &step_data)
|
||||
{
|
||||
if (!unode.geometry_original.is_initialized) {
|
||||
return &unode.geometry_original;
|
||||
if (!step_data.geometry_original.is_initialized) {
|
||||
return &step_data.geometry_original;
|
||||
}
|
||||
|
||||
BLI_assert(!unode.geometry_modified.is_initialized);
|
||||
BLI_assert(!step_data.geometry_modified.is_initialized);
|
||||
|
||||
return &unode.geometry_modified;
|
||||
return &step_data.geometry_modified;
|
||||
}
|
||||
|
||||
static Node *geometry_push(const Object &object)
|
||||
static void geometry_push(const Object &object)
|
||||
{
|
||||
StepData *step_data = get_step_data();
|
||||
|
||||
Node *unode = nullptr;
|
||||
for (std::unique_ptr<Node> &iter_unode : step_data->nodes) {
|
||||
if (step_data->type == Type::Geometry) {
|
||||
unode = iter_unode.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
step_data->type = Type::Geometry;
|
||||
|
||||
if (!unode) {
|
||||
step_data->nodes.append(std::make_unique<Node>());
|
||||
step_data->applied = false;
|
||||
step_data->geometry_clear_pbvh = true;
|
||||
|
||||
unode = step_data->nodes.last().get();
|
||||
step_data->type = Type::Geometry;
|
||||
}
|
||||
|
||||
unode->applied = false;
|
||||
unode->geometry_clear_pbvh = true;
|
||||
|
||||
NodeGeometry *geometry = geometry_get(*unode);
|
||||
NodeGeometry *geometry = geometry_get(*step_data);
|
||||
store_geometry_data(geometry, object);
|
||||
|
||||
return unode;
|
||||
}
|
||||
|
||||
static void store_face_sets(const Mesh &mesh, Node &unode)
|
||||
@@ -1743,6 +1747,8 @@ void push_end_ex(Object &ob, const bool use_nested_undo)
|
||||
unode->normal = {};
|
||||
}
|
||||
|
||||
std::cout << step_data->nodes.size() << '\n';
|
||||
|
||||
step_data->undo_size = threading::parallel_reduce(
|
||||
step_data->nodes.index_range(),
|
||||
16,
|
||||
@@ -2088,8 +2094,8 @@ void push_multires_mesh_begin(bContext *C, const char *str)
|
||||
|
||||
push_begin_ex(*object, str);
|
||||
|
||||
Node *geometry_unode = geometry_push(*object);
|
||||
geometry_unode->geometry_clear_pbvh = false;
|
||||
geometry_push(*object);
|
||||
get_step_data()->geometry_clear_pbvh = false;
|
||||
|
||||
push_all_grids(object);
|
||||
}
|
||||
@@ -2103,8 +2109,8 @@ void push_multires_mesh_end(bContext *C, const char *str)
|
||||
|
||||
Object *object = CTX_data_active_object(C);
|
||||
|
||||
Node *geometry_unode = geometry_push(*object);
|
||||
geometry_unode->geometry_clear_pbvh = false;
|
||||
geometry_push(*object);
|
||||
get_step_data()->geometry_clear_pbvh = false;
|
||||
|
||||
push_end(*object);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user